Noel Schenk
Noel Schenk

Reputation: 726

WebUSB API send raw data to serial USB device

Can't define data to send to my serial device (connected via USB).

How I get the device:

function callTransfer(temp1) {
    temp1.controlTransferOut({
        requestType: 'standard',
        recipient: 'device',
        request: 0x07,
        value: 0x08,
        index: 0x04
    })
        .then(() => {
            console.log('sent req'); device.transferIn(1, 32)
        }) // Waiting for 32 bytes of data from endpoint #1.
        .then(result => {
            console.log(result);
        })
        .catch(error => {
            console.log(error);
        });
}

navigator.usb.requestDevice({
    filters: [{}]
}).then((selectedDevice) => {
    device = selectedDevice;
    return device.open()
        .then(() => device.reset())
        .then(() => device.selectConfiguration(1))
        .then(() => device.claimInterface(device.configuration.interfaces[0].interfaceNumber))
        .then(() => {
            callTransfer(device);
        })
});

After that I can go into wireshark and read the values:

recipient = usb.bmRequestType (but not freely definable it is an enum - link see below)
request = usb.setup.bRequest
value = usb.setup.wValue
index = usb.setup.wIndex

But for my device I also need to set usb.LanguageId, usb.DescriptorIndex and more. Also the recipient is not part of those 4 enums

Is there any way to send raw data or set more properties?

Know there is the transferOut function but when I tried it, it just dumped it at the end, which does not work for my issue.

Tried to change the recipient or request parameter but then it doesn't work or it's worse also adding more parameters to the controlTransferOut object didn't seem to change anything.

Resources:

  1. WICG
  2. MDN web docs
  3. Google Developers Web Updates
  4. WebUSB API Arduino example code
  5. Google Developers Building a Device for WebUSB

Upvotes: 1

Views: 3737

Answers (2)

Noel Schenk
Noel Schenk

Reputation: 726

So this is what I came up with (huge thanks to @Reilly Grant)

Load device

let y;
navigator.usb.requestDevice({
    filters: [{}]
}).then((selectedDevice) => {
    device = selectedDevice;
    return device.open()
        .then(() => device.reset())
        .then(() => device.selectConfiguration(1))
        .then(() => device.claimInterface(device.configuration.interfaces[0].interfaceNumber))
        .then(() => {
            y = device;
        })
});

Request device name (just an example)

y.controlTransferIn({
        requestType: 'standard',
        recipient: 'device',
        request: 0x06,
        value: 0x0302,
        index: 0x409
    }, 255)
        .then(result => {
            let decoder = new TextDecoder()
            console.log(decoder.decode(result.data));
            console.log('sent req');
        }).catch(error => {
            console.log(error);
        });

What helped me were these resources:

Read them really carefully. It takes a moment but it's all worth it!

Also if you don't know you can add multiple hex values like this: 0x2341 where 23 ist the first couple and 41 the second.

If you have problems unloading/unbinding the device checkout this other post: Unbinding USB Interface to use Chrome Web USB API


Following a breakdown of USBControlTransferParameters (the object to controlTransferIn)

To know what to send each parameter checkout "Standard Device Requests". In my example I wanted to get the name of the device. So I checked the bRequest (info in wireshark) which said "GET_DESCRIPTOR" you will find 0x06 next to it which is the request parameter (again checkout the "Standard Device Requests" Table). Also you can get the requestType and recipient from wireshark (you will find it under bmRequestType but don't get fooled those numbers don't work so just check what requestType and recipient it shows and fill them in as string. The value is tricky cause for this request you have to add 2 hex values (the index & descriptor type | do it in this order the documentation from beyondlogic is I think wrong here). In wireshark it's DescriptorIndex and bDescriptorType. So at the end you got the index, I think you can just leave it empty meaning 0x00 but it's to define the language so I added it (in wireshark it's LanguageId). Then 255 is the length I got again from wireshark but it's supposed to be how long the return is.


Set permissions if using linux

It will send an error message like: Access denied

Checkout this page all the way down at the end: Access USB Devices on the Web

SUBSYSTEM=="usb", ATTR{idVendor}=="[yourdevicevendor]", MODE="0664", GROUP="plugdev"

Upvotes: 0

Reilly Grant
Reilly Grant

Reputation: 6083

The request you are sending looks like a SET_DESCRIPTOR request. Since you mention a language ID I assume you are trying to set a string descriptor. When setting a string descriptor then the language ID and descriptor index parameters should be set in the "index" parameter and the lower byte of the "value" parameter respectively. The upper byte of "value" parameter should be set to 0x03 to indicate that you are setting a string descriptor. The "data" parameter should be an ArrayBuffer containing the descriptor you want to send to the device.

Source: https://www.beyondlogic.org/usbnutshell/usb6.shtml

The bmRequestType field is set based on the "recipient" and "requestType" parameters as well as whether you called controlTransferIn() or controlTransferOut(). This gives you complete control over this field.

Source: https://wicg.github.io/webusb/#control-transfer

It may help to explain what you are trying to accomplish. I am not aware of a USB serial device which accepts SET_DESCRIPTOR requests. Documentation for the device you are connecting to is helpful when answering this type of question.

Upvotes: 1

Related Questions