dc/hbj11: init with flasher

This brings in the flashing infrastructure for HBJ11 (bluepill&web).

Change-Id: I480855689f849c24712d58a0ccbce6e91c34f8bd
diff --git a/dc/hbj11/flasher/web/i2c.js b/dc/hbj11/flasher/web/i2c.js
new file mode 100644
index 0000000..6aa4ed7
--- /dev/null
+++ b/dc/hbj11/flasher/web/i2c.js
@@ -0,0 +1,159 @@
+/**
+ * Low-level interface to programmer's I2C USB class.
+ *
+ * See //dc/hbj11/flasher/bluepill/src/i2c.rs for more information.
+ */
+
+const ControlInRequest = Object.freeze({
+  'GetStatus': 1,
+});
+
+const ControlOutRequest = Object.freeze({
+  'SetLED': 1,
+  'ReadI2C': 2,
+  'ReadBuffer': 3,
+  'WriteI2C': 4,
+  'SetWritePointer': 5,
+});
+
+export const Status = Object.freeze({
+  'Idle': 0,
+  'InvalidArgument': 1,
+  'Ack': 2,
+  'Nack': 3,
+  'BusError': 4,
+});
+
+export const StatusFromU8 = function(u8) {
+  for (const label of Object.keys(Status)) {
+    let val = Status[label];
+    if (val === u8) {
+      return label;
+    }
+  }
+};
+
+/**
+ * Low-level interface to programmer's I2C USB class. Thinly wraps available
+ * USB transfers.
+ */
+export class USBI2CClassInterface {
+  /**
+   * @param {USBDevice} usb - The WebUSB device that backs this USB class.
+   */
+  constructor(device) {
+    this.usb = device;
+    this.BUFFER_SIZE = 1024;
+    this.PACKET_SIZE = 64;
+  }
+
+  /**
+   * Open this programmer via WebUSB and finds all required endpoints.
+   */
+  async open() {
+    await this.usb.open();
+    await this.usb.selectConfiguration(1);
+    await this.usb.claimInterface(0);
+
+    let eps = this.usb.configuration.interfaces[0].alternate.endpoints;
+    this.bulk_out = null;
+    this.bulk_in = null;
+    for (const ep of eps) {
+        if (ep.direction == "out" && ep.type == "bulk") {
+            this.bulk_out = ep;
+        }
+        if (ep.direction == "in" && ep.type == "bulk") {
+            this.bulk_in = ep;
+        }
+    }
+    if (this.bulk_out === null) {
+      throw new Error("Could not find bulk out endpoint");
+    }
+    if (this.bulk_in === null) {
+      throw new Error("Could not find bulk in endpoint");
+    }
+  }
+
+  /**
+   * Performs a USB Control OUT request to the I2C class.
+   * @param {number} request - Request number for transfer (0-255).
+   * @param {number} value - Value for transfer (0-65535).
+   * @returns {Promise<USBOutTransferResult>} The underlying WebUSB transfer result.
+   */
+  async controlOut(request, value) {
+    return await this.usb.controlTransferOut({
+      requestType: "vendor",
+      recipient: "interface",
+      request: request,
+      value: value,
+      index: 0,
+    });
+  }
+
+  /**
+   * Read status from programmer.
+   * @returns {Status} The status of the programmer.
+   */
+  async getStatus() {
+    let res = await this.usb.controlTransferIn({
+      requestType: "vendor",
+      recipient: "interface",
+      request: ControlInRequest.GetStatus,
+      value: 0,
+      index: 0
+    }, 1);
+    if (res.data.byteLength < 1) {
+      throw new Error('returned data too short')
+    }
+    return res.data.getInt8(0);
+  }
+
+  /**
+   * Sends SetLED control OUT request.
+   */
+  async setLED(on) {
+    return await this.controlOut(ControlOutRequest.SetLED, on ? 1 : 0);
+  }
+
+  /**
+   * Sends ReadI2C control OUT request.
+   */
+  async readI2C(addr, length) {
+    return await this.controlOut(ControlOutRequest.ReadI2C, (length << 8) | addr);
+  }
+
+  /**
+   * Sends ReadBuffer control OUT request.
+   */
+  async readBuffer(addr, length) {
+    return await this.controlOut(ControlOutRequest.ReadBuffer, (length << 8) | addr);
+  }
+
+  /**
+   * Sends WriteI2C control OUT request.
+   */
+  async writeI2C(addr, length) {
+    return await this.controlOut(ControlOutRequest.WriteI2C, (length << 8) | addr);
+  }
+
+  /**
+   * Sends SetWritePointer control OUT request.
+   */
+  async setWritePointer(addr) {
+    return await this.controlOut(ControlOutRequest.SetWritePointer, addr);
+  }
+
+  /**
+   * Requets bulk IN data.
+   */
+  async bulkIn(length) {
+      return await this.usb.transferIn(this.bulk_in.endpointNumber, length);
+  }
+
+  /**
+   * Sends bulk OUT data.
+   */
+  async bulkOut(data) {
+      return await this.usb.transferOut(this.bulk_out.endpointNumber, data);
+  }
+}