Skip to content
This repository has been archived by the owner on Mar 14, 2024. It is now read-only.

Commit

Permalink
Address @jpmedley feedback
Browse files Browse the repository at this point in the history
  • Loading branch information
beaufortfrancois committed Oct 21, 2020
1 parent 5b33c6a commit 0df7bd7
Showing 1 changed file with 75 additions and 76 deletions.
151 changes: 75 additions & 76 deletions src/site/content/en/blog/usb/index.md
Expand Up @@ -19,8 +19,8 @@ feedback:
- api
---

If I said plain and simple "USB", there is a good chance that you will
immediately think of keyboards, mice, audio, video and storage devices. You're
If I said plainly and simply "USB", there is a good chance that you will
immediately think of keyboards, mice, audio, video, and storage devices. You're
right but you'll find other kinds of Universal Serial Bus (USB) devices out
there.

Expand All @@ -34,39 +34,40 @@ devices.
But most importantly this will **make USB safer and easier to use by bringing
it to the Web**.

Let's see what you could expect with the WebUSB API:
Let's see the behavior you could expect with the WebUSB API:

1. Buy a USB device.
2. Plug it into your computer.
3. A notification appears right away, with the right website to go to for this
device.
4. Simply click on it. Website is there and ready to use!
5. Click to connect and a USB device chooser shows up in Chrome, where you can
2. Plug it into your computer. A notification appears right away, with the right
website to go to for this device.
3. Click the notification. The website is there and ready to use!
4. Click to connect and a USB device chooser shows up in Chrome where you can
pick your device.
6. Tada!

Tada!

What would this procedure be like without the WebUSB API?

- Read a box, label, or search on line and possibly end up on the wrong website.
- Have to install a native application.
- Is it supported on my operating system? Make sure you download the "right"
thing.
- Scary OS prompts popup and warn you about installing drivers/applications from
the Internet.
- Malfunctioning code harms the whole computer. The Web is built to [contain
malfunctioning websites].
- Only use the USB device once? On the Web, the website is gone once you closed
tab. On a computer the code sticks around.
1. Install a platform-specific application.
2. If it's even supported on my operating system, verify that I've downloaded
the right thing.
3. Install the thing. If you're lucky, you'll get no scary OS prompts or popups
warning you about installing drivers/applications from the internet. If
you're unlucky, the installed drivers or applications malfunction and harm
your computer. (Remember, the web is built to [contain malfunctioning
websites]).
4. If you only use the feature once, the code stays on your computer until you
think to remove it. (On the Web, the space for unused is eventually
reclaimed.)

## Before we start
## Before I start

This article assumes you have some basic knowledge of how USB works. If not, I
recommend reading [USB in a NutShell]. For background information about USB,
check out the [official USB specifications].

The [WebUSB API] is available in Chrome 61.

### Available for Origin Trials
### Available for origin trials

In order to get as much feedback as possible from developers using the WebUSB
API in the field, we've previously added this feature in Chrome 54 and Chrome
Expand All @@ -78,32 +79,30 @@ The latest trial has successfully ended in September 2017.

### HTTPS only

Because this experimental API is a powerful new feature added to the Web, it is
made available only to [secure contexts]. This means you'll need to build with
TLS in mind.
Because of this feature's power, it only works on [secure contexts]. This means
you'll need to build with [TLS] in mind.

### User gesture required

As a security feature, getting access to connected USB devices with
`navigator.usb.requestDevice` must be called via a user gesture
like a touch or mouse click.
As a security precaution, `navigator.usb.requestDevice()` may only
be called through a user gesture such as a touch or mouse click.

### Feature Policy

A feature policy is a mechanism that allows developers to selectively enable
and disable various browser features and APIs. It can be defined via a HTTP
A [feature policy] is a mechanism that allows developers to selectively enable
and disable various browser features and APIs. It can be defined via an HTTP
header and/or an iframe "allow" attribute.

You can define a feature that controls whether the usb attribute is exposed on
the Navigator object, or in other words if you allow WebUSB.
You can define a feature policy that controls whether the usb attribute is
exposed on the Navigator object, or in other words if you allow WebUSB.

Below is an example of a header policy where WebUSB is not allowed:

```http
Feature-Policy: fullscreen "*"; usb "none"; payment "self" https://payment.example.com
```

Below is another example of a different container policy where USB is allowed:
Below is another example of a container policy where USB is allowed:

```html
<iframe allowpaymentrequest allow="usb; fullscreen"></iframe>
Expand All @@ -113,20 +112,19 @@ Below is another example of a different container policy where USB is allowed:

The WebUSB API relies heavily on JavaScript [Promises]. If you're not familiar
with them, check out this great [Promises tutorial]. One more thing, `() => {}`
are simply ECMAScript 2015 [Arrow functions] -- they have a shorter syntax
compared to function expressions and lexically bind the `this` value.
are simply ECMAScript 2015 [Arrow functions].

### Get access to USB devices

You can either prompt the user to select a single connected USB device using
`navigator.usb.requestDevice` or call `navigator.usb.getDevices` to get a list
of all connected USB devices the origin has access to.
`navigator.usb.requestDevice()` or call `navigator.usb.getDevices()` to get a
list of all connected USB devices the origin has access to.

The `navigator.usb.requestDevice` function takes a mandatory JavaScript object
The `navigator.usb.requestDevice()` function takes a mandatory JavaScript object
that defines `filters`. These filters are used to match any USB device with the
given vendor (`vendorId`) and optionally product (`productId`) identifiers. The
`classCode`, `protocolCode`, `serialNumber`, and `subclassCode` keys can also be
defined there as well.
given vendor (`vendorId`) and, optionally, product (`productId`) identifiers.
The `classCode`, `protocolCode`, `serialNumber`, and `subclassCode` keys can
also be defined there as well.

<figure class="w-figure">
<img src="./usb-device-chooser.png" class="w-screenshot" alt="Screenshot of the USB device user prompt in Chrome">
Expand All @@ -142,29 +140,29 @@ navigator.usb.requestDevice({ filters: [{ vendorId: 0x2341 }] })
console.log(device.productName); // "Arduino Micro"
console.log(device.manufacturerName); // "Arduino LLC"
})
.catch(error => { console.log(error); });
.catch(error => { console.error(error); });
```

Before you ask, I didn't magically come up with this `0x2341` hexadecimal
number. I simply searched for the word "Arduino" in this [List of USB ID's].

The USB `device` returned in the fulfilled promise above has some basic, yet
important information about the device such as the supported USB version,
maximum packet size, vendor and product IDs, the number of possible
configurations the device can have - basically all fields contained in the
maximum packet size, vendor, and product IDs, the number of possible
configurations the device can have. Basically it contains all fields in the
[device USB Descriptor].

For info, if a USB device announces its [support for WebUSB], as well as
By the way, if a USB device announces its [support for WebUSB], as well as
defining a landing page URL, Chrome will show a persistent notification when the
USB device is plugged in. Clicking on this notification will open the landing page.
USB device is plugged in. Clicking this notification will open the landing page.

<figure class="w-figure">
<img src="./web-usb-notification.png" class="w-screenshot" alt="Screenshot of the WebUSB notification in Chrome">
<figcaption class="w-figcaption">WebUSB notification.</figcaption>
</figure>

From there, you can simply call `navigator.usb.getDevices` and get access to
your Arduino device as shown below.
From there, you can simply call `navigator.usb.getDevices()` and access your
Arduino device as shown below.

```js
navigator.usb.getDevices().then(devices => {
Expand Down Expand Up @@ -205,12 +203,12 @@ navigator.usb.requestDevice({ filters: [{ vendorId: 0x2341 }] })
const decoder = new TextDecoder();
console.log('Received: ' + decoder.decode(result.data));
})
.catch(error => { console.log(error); });
.catch(error => { console.error(error); });
```

Please keep in mind that the WebUSB library we are using here is just
implementing one example protocol (based on the standard USB serial protocol)
and that manufacturers can create any set and types of endpoints they wish.
Please keep in mind that the WebUSB library I'm using here is just implementing
one example protocol (based on the standard USB serial protocol) and that
manufacturers can create any set and types of endpoints they wish.
Control transfers are especially nice for small configuration commands as
they get bus priority and have a well defined structure.

Expand Down Expand Up @@ -244,39 +242,39 @@ basically two things:
URL].
- It exposes a WebUSB Serial API that you may use to override the default one.

Let's look at the JavaScript code again. Once we get the `device` picked by the
user, `device.open` simply runs all platform-specific steps to start a session
with the USB device. Then, we have to select an available USB Configuration
with `device.selectConfiguration`. Remember that a Configuration specifies how
the device is powered, its maximum power consumption and its number of
interfaces. Talking about interfaces, we also need to request exclusive access
with `device.claimInterface` since data can only be transferred to an interface
or associated endpoints when the interface is claimed. Finally calling
`device.controlTransferOut` is needed to set up the Arduino device with the
Look at the JavaScript code again. Once I get the `device` picked by the user,
`device.open()` runs all platform-specific steps to start a session with the USB
device. Then, I have to select an available USB Configuration with
`device.selectConfiguration()`. Remember that a configuration specifies how the
device is powered, its maximum power consumption and its number of interfaces.
Speaking of interfaces, I also need to request exclusive access with
`device.claimInterface()` since data can only be transferred to an interface or
associated endpoints when the interface is claimed. Finally calling
`device.controlTransferOut()` is needed to set up the Arduino device with the
appropriate commands to communicate through the WebUSB Serial API.

From there, `device.transferIn` performs a bulk transfer onto the
From there, `device.transferIn()` performs a bulk transfer onto the
device to inform it that the host is ready to receive bulk data. Then, the
promise is fulfilled with a `result` object containing a [DataView] `data` that
has to be parsed appropriately.

For those who are familiar with USB, all of this should look pretty familiar.
If you're familiar with USB, all of this should look pretty familiar.

### I want more

The WebUSB API lets you interact with the all USB transfer/endpoint types:

- CONTROL transfers, used to send or receive configuration or command
parameters to a USB device are handled with `controlTransferIn(setup,
parameters to a USB device, are handled with `controlTransferIn(setup,
length)` and `controlTransferOut(setup, data)`.
- INTERRUPT transfers, used for a small amount of time sensitive data are
- INTERRUPT transfers, used for a small amount of time sensitive data, are
handled with the same methods as BULK transfers with
`transferIn(endpointNumber, length)` and `transferOut(endpointNumber, data)`.
- ISOCHRONOUS transfers, used for streams of data like video and sound are
- ISOCHRONOUS transfers, used for streams of data like video and sound, are
handled with `isochronousTransferIn(endpointNumber, packetLengths)` and
`isochronousTransferOut(endpointNumber, data, packetLengths)`.
- BULK transfers, used to transfer a large amount of non-time-sensitive data in
a reliable way are handled with `transferIn(endpointNumber, length)` and
a reliable way, are handled with `transferIn(endpointNumber, length)` and
`transferOut(endpointNumber, data)`.

You may also want to have a look at Mike Tsao's [WebLight project] which
Expand All @@ -295,8 +293,8 @@ where you can see all USB device related events in one single place.
</figure>

The internal page `chrome://usb-internals` also comes in handy and allows you
to simulate connection connection and disconnection of virtual WebUSB devices.
This is be useful for doing UI testing without the need for real hardware.
to simulate connection and disconnection of virtual WebUSB devices.
This is be useful for doing UI testing without for real hardware.

<figure class="w-figure">
<img src="./web-usb-internals-page.png" class="w-screenshot" alt="Screenshot of the internal page to debug WebUSB in Chrome">
Expand All @@ -314,14 +312,7 @@ SUBSYSTEM=="usb", ATTR{idVendor}=="[yourdevicevendor]", MODE="0664", GROUP="plug

where `[yourdevicevendor]` is `2341` if your device is an Arduino for instance.
`ATTR{idProduct}` can also be added for a more specific rule. Make sure your
`user` is a member of the `plugdev` group. Then, just reconnect your device.

## What's next

A second iteration of the WebUSB API will look at [Shared Worker] and [Service
Worker] support. Imagine for instance a security key website using the WebUSB
API that would install a service worker to act as a middle man to authenticate
users.
`user` is a [member] of the `plugdev` group. Then, just reconnect your device.

{% Aside %}
Microsoft OS 2.0 Descriptors used by the Arduino examples only work on Windows
Expand All @@ -343,13 +334,19 @@ of an INF file.

Please share your WebUSB demos with the [#webusb] hashtag.

## Acknowledgements

Thanks to [Joe Medley] for reviewing this article.

<!-- lint disable definition-case -->
[contain malfunctioning websites]: https://www.youtube.com/watch?v=29e0CtgXZSI
[USB in a NutShell]: http://www.beyondlogic.org/usbnutshell
[official USB specifications]: https://www.usb.org/
[WebUSB API]: https://wicg.github.io/webusb/
[origin trial]: https://github.com/GoogleChrome/OriginTrials/blob/gh-pages/developer-guide.md
[secure contexts]: https://w3c.github.io/webappsec/specs/powerfulfeatures/#intro
[TLS]: https://en.wikipedia.org/wiki/Transport_Layer_Security
[feature policy]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Feature_Policy
[Promises]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise
[Promises tutorial]: /promises
[Arrow functions]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Functions/Arrow_functions
Expand All @@ -363,7 +360,9 @@ Please share your WebUSB demos with the [#webusb] hashtag.
[DataView]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView
[WebLight project]: https://github.com/sowbug/weblight
[udev rule]: https://www.freedesktop.org/software/systemd/man/udev.html
[member]: https://wiki.debian.org/SystemGroups
[Shared Worker]: https://developer.mozilla.org/en-US/docs/Web/API/SharedWorker
[Service Worker]: https://jakearchibald.github.io/isserviceworkerready/resources.html
[#webusb]: https://twitter.com/search?q=webusb
[Joe Medley]: https://github.com/jpmedley
<!-- lint enable definition-case -->

0 comments on commit 0df7bd7

Please sign in to comment.