Dostęp do urządzeń USB w internecie

Interfejs WebUSB API zwiększa bezpieczeństwo i łatwość korzystania z USB dzięki umieszczeniu go w internecie.

François Beaufort
François Beaufort

Jeśli powiedziałem przejrzyście i po prostu „USB”, jest duża szansa, że pomyśl o klawiaturach, myszach, urządzeniach audio, wideo i urządzeniach pamięci masowej. To już Do wyboru są jednak inne rodzaje urządzeń tam.

Te niestandaryzowane urządzenia USB wymagają dostawców sprzętu do zapisu danych związanych z określoną platformą sterowniki i pakiety SDK, aby umożliwić korzystanie z nich (deweloperowi). Ten kod związany z konkretną platformą uniemożliwiał dotychczas używanie tych urządzeń przez internet. A to jeden z powodów, dla których stworzyliśmy interfejs WebUSB API: pozwalają na udostępnianie w internecie usług urządzeń USB. Dzięki temu interfejsowi API sprzęt producenci będą mogli tworzyć pakiety SDK JavaScript działające na różnych platformach, urządzenia.

Jednak przede wszystkim zwiększy to bezpieczeństwo i wygodę korzystania z USB dzięki w internecie.

Zobaczmy, jak działa interfejs WebUSB API:

  1. Kup urządzenie USB.
  2. Podłącz go do komputera. Powiadomienie pojawi się od razu, po prawej stronie strona, która się wyświetla na tym urządzeniu.
  3. Kliknij powiadomienie. Strona internetowa istnieje i jest gotowa do użycia.
  4. Kliknij, aby połączyć, a w Chrome pojawi się moduł wyboru urządzeń USB, wybierz swoje urządzenie.

Tadam!

Jak wygląda ta procedura bez interfejsu WebUSB API?

  1. zainstalować aplikację dla konkretnej platformy,
  2. Jeśli mój system operacyjny obsługuje tę funkcję, sprawdź, czy została pobrana jest właściwa.
  3. Zainstaluj to. Przy odrobinie szczęścia nie będziesz otrzymywać żadnych przerażających promptów i wyskakujących okienek w systemie operacyjnym ostrzeżenie dotyczące instalowania sterowników/aplikacji z internetu. Jeśli jeśli masz pecha, zainstalowane sterowniki lub aplikacje działają nieprawidłowo, Twój komputer. (Pamiętaj, że internet został zbudowany tak, aby zawierać błędy stronach internetowych).
  4. Jeśli użyjesz funkcji tylko raz, kod pozostanie na komputerze do momentu o jego usunięciu. (W internecie miejsce na nieużywane reclaimed.)

Zanim zacznę

W tym artykule zakładamy, że masz już podstawową wiedzę o działaniu USB. Jeśli nie, zalecamy przeczytanie artykułu USB in a NutShell. Podstawowe informacje na temat USB: znajdziesz w oficjalnej specyfikacji urządzenia USB.

Interfejs WebUSB API jest dostępny w Chrome 61.

Dostępne w przypadku testowania origin

Aby uzyskać jak najwięcej opinii od programistów korzystających z WebUSB API, ale tę funkcję dodaliśmy wcześniej w Chrome 54 i Chrome. 57 jako pierwotną wersję próbną.

Ostatni okres próbny zakończył się we wrześniu 2017 r.

Prywatność i bezpieczeństwo

Tylko HTTPS

Ze względu na zaawansowane możliwości działa ona tylko w przypadku bezpiecznych kontekstów. Oznacza to, że pamiętaj o TLS.

Wymagany gest użytkownika

Ze względów bezpieczeństwa navigator.usb.requestDevice() może może być wywoływana gestem użytkownika, na przykład dotknięciem lub kliknięciem.

Zasady dotyczące uprawnień

Zasady dotyczące uprawnień to mechanizm, który umożliwia programistom selektywne włączanie oraz wyłączyć różne funkcje i interfejsy API przeglądarki. Można ją zdefiniować za pomocą protokołu HTTP nagłówek i/lub element iframe „allow” .

Możesz zdefiniować zasadę uprawnień, która określa, czy atrybut usb ma być widoczne na obiekcie Navigator, lub innymi słowy po zezwoleniu na WebUSB.

Poniżej znajduje się przykład zasady nagłówka, w której niedozwolony jest protokół WebUSB:

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

Poniżej znajdziesz inny przykład zasad dotyczących kontenerów, w których dozwolone jest użycie USB:

<iframe allowpaymentrequest allow="usb; fullscreen"></iframe>

Zacznij kodować

Interfejs WebUSB API w dużym stopniu opiera się na obietnicach JavaScriptu. Jeśli nie znasz usługi, zapoznaj się z tym świetnym samouczkiem. Jeszcze jedno, () => {} to po prostu funkcje strzałek ECMAScript 2015.

Uzyskaj dostęp do urządzeń USB

Możesz poprosić użytkownika o wybranie jednego podłączonego urządzenia USB za pomocą navigator.usb.requestDevice() lub zadzwoń pod numer navigator.usb.getDevices(), aby otrzymać Lista wszystkich podłączonych urządzeń USB, do których strona ma dostęp.

Funkcja navigator.usb.requestDevice() przyjmuje wymagany obiekt JavaScript która definiuje filters. Te filtry pozwalają dopasować dowolne urządzenie USB do danego dostawcy (vendorId) i opcjonalnie (productId) produktu. Klucze classCode, protocolCode, serialNumber i subclassCode mogą która również powinna być tu zdefiniowana.

Zrzut ekranu przedstawiający prompt użytkownika urządzenia USB w Chrome
Potwierdzenie użytkownika urządzenia USB

Oto jak uzyskać dostęp do połączonego urządzenia Arduino skonfigurowanego aby zezwolić na źródło.

navigator.usb.requestDevice({ filters: [{ vendorId: 0x2341 }] })
.then(device => {
  console.log(device.productName);      // "Arduino Micro"
  console.log(device.manufacturerName); // "Arduino LLC"
})
.catch(error => { console.error(error); });

Zanim spytasz, nie wymyśliłem magicznej liczby szesnastkowej 0x2341 numer. Po prostu wyszukiwałem słowo „Arduino” na tej liście identyfikatorów USB.

USB device zwróconego w ramach powyższej obietnicy ma pewne podstawowe, ale ważne informacje o urządzeniu, takie jak obsługiwana wersja USB, maksymalnego rozmiaru pakietu, dostawcy i identyfikatora produktu, liczby możliwych konfiguracji urządzeń. Zasadniczo zawiera on wszystkie pola deskryptor USB urządzenia.

// Get all connected USB devices the website has been granted access to.
navigator.usb.getDevices().then(devices => {
  devices.forEach(device => {
    console.log(device.productName);      // "Arduino Micro"
    console.log(device.manufacturerName); // "Arduino LLC"
  });
})

Przy okazji: jeśli urządzenie USB informuje o obsłudze WebUSB, zdefiniuje adres URL strony docelowej, a Chrome będzie wyświetlać trwałe powiadomienie, gdy Podłączono urządzenie USB. Kliknięcie go spowoduje otwarcie strony docelowej.

Zrzut ekranu pokazujący powiadomienie WebUSB w Chrome
Powiadomienie WebUSB.

Porozmawiaj z płytą USB Arduino

Teraz zobaczmy, jak łatwo można się komunikować przy użyciu interfejsu zgodnego z WebUSB. Płytka Arduino przez port USB. Sprawdź instrukcje na stronie Na stronie https://github.com/webusb/arduino włącz WebUSB swoje szkice.

Bez obaw. W dalszej części artykułu omówię wszystkie metody korzystania z urządzenia WebUSB związane z urządzeniami. ten artykuł.

let device;

navigator.usb.requestDevice({ filters: [{ vendorId: 0x2341 }] })
.then(selectedDevice => {
    device = selectedDevice;
    return device.open(); // Begin a session.
  })
.then(() => device.selectConfiguration(1)) // Select configuration #1 for the device.
.then(() => device.claimInterface(2)) // Request exclusive control over interface #2.
.then(() => device.controlTransferOut({
    requestType: 'class',
    recipient: 'interface',
    request: 0x22,
    value: 0x01,
    index: 0x02})) // Ready to receive data
.then(() => device.transferIn(5, 64)) // Waiting for 64 bytes of data from endpoint #5.
.then(result => {
  const decoder = new TextDecoder();
  console.log('Received: ' + decoder.decode(result.data));
})
.catch(error => { console.error(error); });

Pamiętaj, że biblioteka WebUSB, której używam, to tylko implementacja przykładowego protokołu (opartego na standardowym protokole szeregowym USB) oraz producenci mogą tworzyć dowolne zestawy i typy punktów końcowych; Transfery ustawień są szczególnie przydatne w przypadku małych poleceń konfiguracyjnych, zyskują priorytet autobusowy i mają dobrze zdefiniowany strukturę.

A oto szkic, który przesłano na płytkę Arduino.

// Third-party WebUSB Arduino library
#include <WebUSB.h>

WebUSB WebUSBSerial(1 /* https:// */, "webusb.github.io/arduino/demos");

#define Serial WebUSBSerial

void setup() {
  Serial.begin(9600);
  while (!Serial) {
    ; // Wait for serial port to connect.
  }
  Serial.write("WebUSB FTW!");
  Serial.flush();
}

void loop() {
  // Nothing here for now.
}

Używana w przykładowym powyżej bibliotece WebUSB Arduino innej firmy zasadniczo 2 rzeczy:

  • Urządzenie działa jak urządzenie WebUSB, umożliwiając Chrome odczytanie adresu URL strony docelowej.
  • Udostępnia interfejs WebUSB Serial API, którego możesz użyć do zastąpienia domyślnego.

Spójrz jeszcze raz na kod JavaScript. Gdy już dostanę wybrany przez użytkownika element (device), device.open() uruchamia wszystkie kroki potrzebne na danej platformie, aby rozpocząć sesję przy użyciu USB urządzenia. Następnie muszę wybrać dostępną konfigurację USB – device.selectConfiguration() Pamiętaj, że konfiguracja określa sposób zasilane urządzenie, maksymalne zużycie energii i liczbę interfejsów. A jeśli już mowa o interfejsach, to muszę też poprosić o dostęp na wyłączność za pomocą device.claimInterface(), ponieważ dane można przenieść tylko do interfejsu lub powiązanych punktów końcowych po zgłoszeniu własności interfejsu. Ostatnie połączenie Aplikacja device.controlTransferOut() jest niezbędna do skonfigurowania urządzenia Arduino z odpowiednie polecenia do komunikowania się przez interfejs WebUSB Serial API.

Następnie device.transferIn() wykonuje przelew zbiorczy na który informuje, że host jest gotowy do przyjmowania danych zbiorczych. Następnie funkcja obiekt typu result zawiera obiekt DataView data, który wymaga odpowiedniej analizy.

Jeśli masz doświadczenie w korzystaniu z USB, wszystko to powinno wyglądać znajomo.

Chcę więcej

Interfejs WebUSB API umożliwia interakcję ze wszystkimi typami punktów końcowych/transferu USB:

  • Transfery za pomocą funkcji CONTROL – używane do wysyłania i odbierania konfiguracji lub poleceń do urządzenia USB są obsługiwane za pomocą controlTransferIn(setup, length) i controlTransferOut(setup, data).
  • transfery INTERRUPT, używane przez krótki czas, są obsługiwane za pomocą tych samych metod, co transfery BULK z transferIn(endpointNumber, length) i transferOut(endpointNumber, data).
  • Przesyłanie ISOCHRONOUS, używane w przypadku strumieni danych, takich jak wideo i dźwięk, jest obsługiwane za pomocą isochronousTransferIn(endpointNumber, packetLengths) i isochronousTransferOut(endpointNumber, data, packetLengths)
  • transfery BULK służące do przesyłania dużej ilości danych, które nie są wrażliwe czasowo w niezawodny sposób i są obsługiwane za pomocą transferIn(endpointNumber, length) oraz transferOut(endpointNumber, data)

Możesz też obejrzeć projekt WebLight Mike'a Tsao, to wzorcowy przykład konstrukcji urządzenia LED sterowanego przez USB, które zostało zaprojektowane dla interfejsu WebUSB API (bez użycia Arduino). Znajdziesz tam informacje o sprzęcie, oprogramowaniu i oprogramowania układowego.

Unieważnianie dostępu do urządzenia USB

Strona może wyczyścić uprawnienia dostępu do urządzenia USB, którego już nie potrzebuje wywołując funkcję forget() w instancji USBDevice. Na przykład w przypadku adresu edukacyjna aplikacja internetowa używana na współdzielonym komputerze z wieloma urządzeniami, duża zbyt duża liczba pozwoleń wywołanych przez użytkowników wpływa negatywnie na wrażenia użytkowników.

// Voluntarily revoke access to this USB device.
await device.forget();

Funkcja forget() jest dostępna w Chrome 101 i nowszych wersjach, więc sprawdź, czy jest ona obsługiwane przez:

if ("usb" in navigator && "forget" in USBDevice.prototype) {
  // forget() is supported.
}

Ograniczenia rozmiaru transferu

Niektóre systemy operacyjne nakładają ograniczenia na ilość danych, które mogą być przesyłane oczekujące transakcje USB. Dzielenie danych na mniejsze transakcje tylko przesłanie kilku plików jednocześnie pozwala uniknąć tych ograniczeń. Zmniejsza też ilość używanej pamięci i umożliwia aplikacji zgłaszanie postępów Zakończono przenoszenie danych.

Wielokrotne transfery przesyłane do punktu końcowego są zawsze wykonywane w odpowiedniej kolejności, można zwiększyć przepustowość, przesyłając wiele fragmentów w kolejce, aby uniknąć opóźnienia między transferami USB. Po każdym pełnym przesłaniu fragmentu powiadom swój kod, że powinien dostarczać więcej danych zgodnie z opisem w pomocy pomocniczej poniżej.

const BULK_TRANSFER_SIZE = 16 * 1024; // 16KB
const MAX_NUMBER_TRANSFERS = 3;

async function sendRawPayload(device, endpointNumber, data) {
  let i = 0;
  let pendingTransfers = [];
  let remainingBytes = data.byteLength;
  while (remainingBytes > 0) {
    const chunk = data.subarray(
      i * BULK_TRANSFER_SIZE,
      (i + 1) * BULK_TRANSFER_SIZE
    );
    // If we've reached max number of transfers, let's wait.
    if (pendingTransfers.length == MAX_NUMBER_TRANSFERS) {
      await pendingTransfers.shift();
    }
    // Submit transfers that will be executed in order.
    pendingTransfers.push(device.transferOut(endpointNumber, chunk));
    remainingBytes -= chunk.byteLength;
    i++;
  }
  // And wait for last remaining transfers to complete.
  await Promise.all(pendingTransfers);
}

Wskazówki

Debugowanie USB w Chrome jest łatwiejsze dzięki wewnętrznej stronie about://device-log który umożliwia przeglądanie wszystkich zdarzeń związanych z urządzeniem USB w jednym miejscu.

Zrzut ekranu strony dziennika urządzenia służącej do debugowania WebUSB w Chrome
Strona dziennika urządzenia w Chrome służąca do debugowania interfejsu WebUSB API.

Przydatne są również strony wewnętrzne about://usb-internals, na których możesz: symuluje podłączanie i odłączanie wirtualnych urządzeń WebUSB. Przydaje się to do testowania interfejsu bez użycia fizycznego sprzętu.

Zrzut ekranu przedstawiający stronę wewnętrzną służącą do debugowania WebUSB w Chrome
Wewnętrzna strona w Chrome służąca do debugowania interfejsu WebUSB API.

W większości systemów Linux urządzenia USB są mapowane z uprawnieniami tylko do odczytu przez wartość domyślną. Aby zezwolić Chrome na otwarcie urządzenia USB, musisz dodać nowy udev . Utwórz plik pod adresem /etc/udev/rules.d/50-yourdevicename.rules z ta treść:

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

gdzie [yourdevicevendor] to 2341, jeśli Twoje urządzenie to na przykład Arduino. Możesz też dodać ATTR{idProduct}, aby zwiększyć szczegółowość reguły. Upewnij się, że atrybuty user jest członkiem grupy plugdev. Następnie po prostu ponownie podłącz urządzenie.

Zasoby

Wyślij tweeta na adres @ChromiumDev, używając hashtagu #WebUSB. i daj nam znać, gdzie i jak go używasz.

Podziękowania

Dziękujemy Joe Medley za przeczytanie tego artykułu.