Source code cleanup + README update

Change-Id: I4874f60bbd6ccf5c7878b44152ceabf0095f621c
diff --git a/README.md b/README.md
index fd1c20f..01a0eb1 100644
--- a/README.md
+++ b/README.md
@@ -1,9 +1,10 @@
-# WebCoral
+# WebCoral demo
 
-This is a project to demonstrate how to access
-[Coral USB Accelerator](https://coral.ai/products/accelerator/) from
-Chrome browser using [WebUSB](https://wicg.github.io/webusb/).
-The demo works on Linux and macOS.
+This is a project to demonstrate how to use the
+[Coral USB Accelerator](https://coral.ai/products/accelerator/) from the Chrome
+browser with [WebUSB](https://wicg.github.io/webusb/).
+
+Currently, this demo is verified to work only on Linux and macOS.
 
 ## Web Server Setup
 
@@ -17,14 +18,14 @@
 cd webcoral
 ```
 
-Download `.tflite` model files to `site/models`:
+Download `.tflite` model files to `site/models/`:
 ```
 make download
 ```
 
-Build WASM files inside Docker container:
+Build WASM files inside a Docker container:
 ```
-DOCKER_SHELL_COMMAND="make wasm" make docker-shell
+DOCKER_SHELL_COMMAND="make COMPILATION_MODE=opt wasm" make docker-shell
 ```
 
 Run local web server using python:
@@ -36,9 +37,11 @@
 
 ## System Setup
 
-On **Linux** you need to install device rules to make Coral USB device
+On **macOS**, you don't need to install anything else.
+
+On **Linux**, you need to install device rules to make the Coral USB Accelerator
 visible in Chrome. You probably already have them installed if you are using
-Coral products. Just in case this repo has a bash script right for that:
+Coral products. If not, this repo has a bash script to install the device rules:
 ```
 scripts/linux_device_rules.sh install
 ```
@@ -47,22 +50,20 @@
 scripts/linux_device_rules.sh uninstall
 ```
 
-On **macOS** you don't need to install anything.
-
 ## Device Setup
 
-Coral USB accelerator doesn't have preinstalled firmware. If you run `lsusb`
-command right after plugging USB device in, you'll see
+To use the USB Accelerator from the web browser, you need to update the USB
+Accelerator's firmware as follows. The firmware is usually automatically flashed
+by [libedgetpu](https://github.com/google-coral/libedgetpu) library when using
+C++ or Python programs, but that’s not the case from the browser.
+
+If you run `lsusb` command right after plugging USB device in, you'll see:
 ```
 Bus 001 Device 008: ID 1a6e:089a Global Unichip Corp.
 ```
 
 This means firmware is not flashed yet. It is possible to flash firmware
-directly from Chrome or from command line. Either way after flashing you'll see:
-```
-Bus 001 Device 009: ID 18d1:9302 Google Inc.
-```
-which means that device is ready to use.
+directly from Chrome or using command line.
 
 To flash firmware from command line:
 ```
@@ -74,10 +75,18 @@
 There is an [issue](https://crbug.com/1189418) on macOS. It is already fixed and
 the fix will be available in Chrome 91.
 
+Either way, you should now verify it's flashed by again running `lsusb` and you
+should see:
+```
+Bus 001 Device 009: ID 18d1:9302 Google Inc.
+```
+which means that device is ready to use.
+
+
 ## Demo
 
-Make sure the USB device was properly flashed. Choose the model you like to run
-and press `Initialize Interpreter` button. TPU model will require to select USB
-device in the dialog. CPU model will be ready immediately. At this moment you
-can run inference on any local image file by pressing `Choose Image File`
-button.
+Open Chrome at http://localhost:8000/. Choose the model you want to run and
+press the **Initialize Interpreter** button. Selecting an Edge TPU model then
+requires you to select the USB Accelerator in the dialog; selecting a CPU model
+will be ready immediately. At this moment, you can run inference on any local
+image file by pressing the **Choose Image File** button.
diff --git a/site/index.html b/site/index.html
index 0e381b2..ac3f674 100644
--- a/site/index.html
+++ b/site/index.html
@@ -1,93 +1,85 @@
 <!DOCTYPE html>
 <html>
   <head>
-    <meta charset="UTF-8">
-    <title>Coral USB Accelerator + WebUSB</title>
-    <script src="dfu.js"></script>
-    <script src="dfuse.js"></script>
-    <script src="models.js"></script>
-    <script src="tflite.js"></script>
-    <script src="interpreter.js"></script>
+    <meta charset='UTF-8'>
+    <title>Coral USB Accelerator Demo</title>
+    <script src='dfu.js'></script>
+    <script src='dfuse.js'></script>
+    <script src='models.js'></script>
+    <script src='tflite.js'></script>
+    <script src='interpreter.js'></script>
   </head>
   <body>
     <h1>Coral USB Accelerator Demo</h1>
-    <button id="button-firmware">1. Flash Device Firmware</button>&nbsp;<span id="firmware-status"></span><br/><br/>
-    <button id="button-init">2. Initialize Interpreter</button>
-    <select id="model">
-      <option value="mobilenet_cpu">MobileNet V1 (CPU)</option>
-      <option value="mobilenet_tpu">MobileNet V1 (TPU)</option>
-      <option value="ssd_mobilenet_coco_cpu">SSD MobileNet V2 - Coco (CPU)</option>
-      <option value="ssd_mobilenet_coco_tpu">SSD MobileNet V2 - Coco (TPU)</option>
-      <option value="ssd_mobilenet_face_cpu">SSD MobileNet V2 - Face (CPU)</option>
-      <option value="ssd_mobilenet_face_tpu">SSD MobileNet V2 - Face (TPU)</option>
-    </select><br/><br/>
-    <button id="button-image" onclick="document.getElementById('file').click();" disabled>3. Choose Image File</button><br/><br/>
-    <input type="file" id="file" style="display:none;"/><br/>
-    <h2 id="result"></h2>
-    <canvas id="canvas"></canvas>
+    <button id='button-firmware'>1. Flash USB Firmware</button>&nbsp;<span id='firmware-status'></span>
+    <br/><br/>
+    <button id='button-init'>2. Initialize Interpreter</button>
+    <select id='model'>
+      <option value='mobilenet_tpu'>MobileNet V1 (TPU)</option>
+      <option value='mobilenet_cpu'>MobileNet V1 (CPU)</option>
+      <option value='ssd_mobilenet_coco_tpu'>SSD MobileNet V2 - Coco (TPU)</option>
+      <option value='ssd_mobilenet_coco_cpu'>SSD MobileNet V2 - Coco (CPU)</option>
+      <option value='ssd_mobilenet_face_tpu'>SSD MobileNet V2 - Face (TPU)</option>
+      <option value='ssd_mobilenet_face_cpu'>SSD MobileNet V2 - Face (CPU)</option>
+    </select>
+    <br/><br/>
+    <button id='button-image' onclick='document.getElementById("file").click();' disabled>3. Choose Image File</button>
+    <input type='file' id='file' style='display:none;'/>
+    <br/><br/>
+    <h2 id='result'></h2>
+    <canvas id='canvas'></canvas>
     <br/><br/>
 
-    <!--button id="test">Inference Test</button><br/><br/-->
     <script>
-      async function loadLocalImage(file) {
-        return new Promise(resolve => {
-          var reader = new FileReader();
-          reader.onload = function() {
-            var img = new Image();
-            img.onload = function() {
-              resolve(img);
-            };
-            img.src = reader.result;
-          };
-          reader.readAsDataURL(file);
-        });
-      }
-
+      // DFU functions are based on the code from
+      // https://github.com/devanlai/webdfu/blob/gh-pages/dfu-util/dfu-util.js
       async function getDFUDescriptorProperties(device) {
         let data = await device.readConfigurationDescriptor(0);
         let configDesc = dfu.parseConfigurationDescriptor(data);
         let funcDesc = null;
         if (configDesc.bConfigurationValue == device.settings.configuration.configurationValue) {
-            for (let desc of configDesc.descriptors) {
-                if (desc.bDescriptorType == 0x21 && desc.hasOwnProperty("bcdDFUVersion")) {
-                    funcDesc = desc;
-                    break;
-                }
+          for (let desc of configDesc.descriptors) {
+            if (desc.bDescriptorType == 0x21 && desc.hasOwnProperty('bcdDFUVersion')) {
+              funcDesc = desc;
+              break;
             }
+          }
         }
 
         if (!funcDesc) return {};
 
         return {
-            WillDetach:            ((funcDesc.bmAttributes & 0x08) != 0),
-            ManifestationTolerant: ((funcDesc.bmAttributes & 0x04) != 0),
-            CanUpload:             ((funcDesc.bmAttributes & 0x02) != 0),
-            CanDnload:             ((funcDesc.bmAttributes & 0x01) != 0),
-            TransferSize:          funcDesc.wTransferSize,
-            DetachTimeOut:         funcDesc.wDetachTimeOut,
-            DFUVersion:            funcDesc.bcdDFUVersion
+          WillDetach:            ((funcDesc.bmAttributes & 0x08) != 0),
+          ManifestationTolerant: ((funcDesc.bmAttributes & 0x04) != 0),
+          CanUpload:             ((funcDesc.bmAttributes & 0x02) != 0),
+          CanDnload:             ((funcDesc.bmAttributes & 0x01) != 0),
+          TransferSize:          funcDesc.wTransferSize,
+          DetachTimeOut:         funcDesc.wDetachTimeOut,
+          DFUVersion:            funcDesc.bcdDFUVersion
         };
       }
 
       async function connect(device) {
-          await device.open();
+        await device.open();
 
-          let desc = await getDFUDescriptorProperties(device);
-          if (desc && Object.keys(desc).length > 0)
-            device.properties = desc;
+        let desc = await getDFUDescriptorProperties(device);
+        if (desc && Object.keys(desc).length > 0)
+          device.properties = desc;
 
-          device.logDebug = console.log;
-          device.logInfo = console.log;
-          device.logWarning = console.log;
-          device.logError = console.log;
-          device.logProgress = console.log;
+        device.logDebug = console.log;
+        device.logInfo = console.log;
+        device.logWarning = console.log;
+        device.logError = console.log;
+        device.logProgress = console.log;
 
-          return device;
+        return device;
       }
 
       async function openDevice() {
         try {
-          let device = await navigator.usb.requestDevice({ 'filters': [{'vendorId': 0x1a6e, 'productId': 0x089a}] });
+          let device = await navigator.usb.requestDevice({
+            'filters': [{'vendorId': 0x1a6e, 'productId': 0x089a}]
+          });
           let interfaces = dfu.findDeviceDfuInterfaces(device);
           if (interfaces.length != 1) return null;
           return await connect(new dfu.Device(device, interfaces[0]));
@@ -96,19 +88,31 @@
         }
       }
 
+      async function loadLocalImage(file) {
+        return new Promise(resolve => {
+          var reader = new FileReader();
+          reader.onload = () => {
+            var img = new Image();
+            img.onload = () => { resolve(img); };
+            img.src = reader.result;
+          };
+          reader.readAsDataURL(file);
+        });
+      }
+
       async function loadFile(url) {
         return new Promise(resolve => {
           let req = new XMLHttpRequest();
           req.open('GET', url, true);
           req.responseType = 'arraybuffer';
-          req.onload = function (event) { resolve(req.response); };
+          req.onload = event => { resolve(req.response); };
           req.send(null);
         });
       }
 
       document.addEventListener('DOMContentLoaded', event => {
-        let downloadButton = document.querySelector("#button-firmware");
-        downloadButton.addEventListener('click', async function(event) {
+        let downloadButton = document.querySelector('#button-firmware');
+        downloadButton.addEventListener('click', async event => {
           let firmwareFile = await loadFile('/firmware.bin');
           let device = await openDevice();
           if (device) {
@@ -118,7 +122,7 @@
           }
 
           if (device && firmwareFile) {
-            document.getElementById("firmware-status").textContent = "Flashing...";
+            document.getElementById('firmware-status').textContent = 'Flashing...';
 
             try {
               let status = await device.getStatus();
@@ -126,7 +130,7 @@
                 await device.clearStatus();
               }
             } catch (error) {
-              device.logWarning("Failed to clear status");
+              device.logWarning('Failed to clear status');
             }
 
             try {
@@ -134,40 +138,32 @@
               await device.do_download(device.properties.TransferSize, firmwareFile, manifestationTolerant);
               if (!manifestationTolerant)
                 await device.waitDisconnected(5000);
-              document.getElementById("firmware-status").textContent = "Ready :)";
+              document.getElementById('firmware-status').textContent = 'Ready :)';
             } catch (error) {
-              document.getElementById("firmware-status").textContent = "Failed :(";
+              document.getElementById('firmware-status').textContent = 'Failed :(';
             }
           }
         });
       });
 
       Module['onRuntimeInitialized'] = () => {
-        let inference_test = Module.cwrap("inference_test",  null, [], { async: true });
-        let testButton = document.querySelector("#test");
-        if (testButton) {
-          testButton.addEventListener('click', async () => {
-            inference_test();
-          });
-        }
-
         let interpreter;
         let model;
 
-        document.querySelector("#button-init").addEventListener('click', async () => {
+        document.querySelector('#button-init').addEventListener('click', async () => {
           model = TFLITE_MODELS[document.getElementById('model').value];
           console.log(model);
           interpreter = new tflite.Interpreter();
           if (await interpreter.createFromBuffer(await loadFile(model.url))) {
-            document.getElementById("button-firmware").disabled = true;
-            document.getElementById("button-init").disabled = true;
-            document.getElementById("model").disabled = true;
-            document.getElementById("button-image").disabled = false;
+            document.getElementById('button-firmware').disabled = true;
+            document.getElementById('button-init').disabled = true;
+            document.getElementById('model').disabled = true;
+            document.getElementById('button-image').disabled = false;
           }
         });
 
-        document.querySelector("#file").addEventListener('change', async () => {
-          const input = document.getElementById("file");
+        document.querySelector('#file').addEventListener('change', async () => {
+          const input = document.getElementById('file');
           const file = input.files[0];
           if (!file) return;
 
@@ -175,7 +171,7 @@
           console.log('SHAPE:', width, 'x', height);
 
           const img = await loadLocalImage(file);
-          const c = document.getElementById("canvas");
+          const c = document.getElementById('canvas');
           c.width = width;
           c.height = height;
           const ctx = c.getContext('2d');
@@ -198,7 +194,7 @@
 
           const imageData = ctx.getImageData(0, 0, width, height);
           tflite.setRgbaInput(interpreter, imageData.data);
-          document.getElementById("result").textContent = "Recognizing...";
+          document.getElementById('result').textContent = 'Recognizing...';
           const inferenceStart = Date.now();
           await interpreter.invoke();
           const inferenceTime = Date.now() - inferenceStart;
@@ -207,14 +203,11 @@
           switch (model.type) {
             case 'classification':
               const maxIndex = tflite.getClassificationOutput(interpreter);
-              console.log('Class:', maxIndex);
               label = model.labels[maxIndex];
               break;
             case 'detection':
               const objects = tflite.getDetectionOutput(interpreter, threshold=0.5);
-              console.log('Detected objects:', objects);
               for (obj of objects) {
-                console.log(obj);
                 const x = obj.bbox.xmin * width;
                 const y = obj.bbox.ymin * height;
                 const w = obj.bbox.xmax * width - x;
@@ -225,7 +218,7 @@
               label = `${objects.length} ${objects.length == 1 ? 'object' : 'objects'}`;
               break;
           }
-          document.getElementById("result").textContent = `${label}: ${inferenceTime} ms`;
+          document.getElementById('result').textContent = `${label}: ${inferenceTime} ms`;
         });
       };
       Module['print'] = txt => console.log(txt);
diff --git a/site/tflite.js b/site/tflite.js
index 1547317..5bc2bb7 100644
--- a/site/tflite.js
+++ b/site/tflite.js
@@ -102,10 +102,10 @@
 
     this.id = nextId++;
 
-    Module['invokeDone'] = function(id) {
+    Module['invokeDone'] = function(id, result) {
       const key = callbackKey(id);
       const callback = Module[key];
-      if (callback) callback();
+      if (callback) callback(result);
       delete Module[key];
     };
   }
@@ -181,8 +181,10 @@
 
   tflite.Interpreter.prototype.invoke = function() {
     const self = this;
-    return new Promise(resolve => {
-      Module[callbackKey(self.id)] = resolve;
+    return new Promise((resolve, reject) => {
+      Module[callbackKey(self.id)] = result => {
+        (result ? resolve : reject)();
+      };
       self.interpreter_invoke_async(self.interpreter, self.id);
     });
   }
diff --git a/tflite/interpreter.cc b/tflite/interpreter.cc
index fbda4d4..a227dfc 100644
--- a/tflite/interpreter.cc
+++ b/tflite/interpreter.cc
@@ -11,12 +11,12 @@
 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 // See the License for the specific language governing permissions and
 // limitations under the License.
-
 #include <cstring>
 #include <fstream>
 #include <iostream>
 #include <memory>
 #include <thread>
+#include <utility>
 
 #include <emscripten.h>
 
@@ -24,10 +24,8 @@
 
 #include "tensorflow/lite/model_builder.h"
 #include "tensorflow/lite/interpreter.h"
-//#include "tensorflow/lite/mutable_op_resolver.h"
 #include "tensorflow/lite/interpreter_builder.h"
 #include "tensorflow/lite/kernels/register.h"  // BuiltinOpResolver
-//#include "tensorflow/lite/model.h"
 
 #include "tflite/queue.h"
 
@@ -36,6 +34,9 @@
 constexpr char kEdgeTpuCustomOp[] = "edgetpu-custom-op";
 constexpr int kExit = -1;
 
+using DelegatePtr = std::unique_ptr<TfLiteDelegate,
+                                    decltype(&edgetpu_free_delegate)>;
+
 bool HasCustomOp(const tflite::FlatBufferModel& model, const char* name) {
   const auto* opcodes = model->operator_codes();
   if (!opcodes) return false;
@@ -52,10 +53,11 @@
  public:
   Interpreter(): thread_([this]() {
     while (true) {
-      if (auto cmd = queue_.Pop(250)) {
+      if (auto cmd = queue_.Pop(250/*ms*/)) {
         if (cmd.value() == kExit) break;
-        Invoke();
-        MAIN_THREAD_ASYNC_EM_ASM({Module['invokeDone']($0);}, cmd.value());
+        auto result = Invoke();
+        MAIN_THREAD_ASYNC_EM_ASM({Module['invokeDone']($0, $1);},
+                                 cmd.value(), result);
       }
     }
   }) {}
@@ -76,7 +78,8 @@
   }
 
   bool Init(const char* model_buffer, size_t model_buffer_size, int verbosity) {
-    auto model = tflite::FlatBufferModel::BuildFromBuffer(model_buffer, model_buffer_size);
+    auto model = tflite::FlatBufferModel::BuildFromBuffer(model_buffer,
+                                                          model_buffer_size);
     if (!model) {
       std::cerr << "[ERROR] Cannot load model" << std::endl;
       return false;
@@ -88,9 +91,6 @@
     // Model
     model_ = std::move(model);
 
-    //tflite::MutableOpResolver resolver;
-    //resolver.AddCustom("TFLite_Detection_PostProcess",
-    //                   tflite::ops::custom::Register_DETECTION_POSTPROCESS());
     tflite::ops::builtin::BuiltinOpResolver resolver;
     if (tflite::InterpreterBuilder(*model_, resolver)(&interpreter_) != kTfLiteOk) {
       std::cerr << "[ERROR] Cannot create interpreter" << std::endl;
@@ -110,10 +110,10 @@
 
       auto& device = devices.get()[0];
       edgetpu_option option = {"Usb.AlwaysDfu", "False"};
-      auto* delegate = edgetpu_create_delegate(device.type, device.path, &option, 1);
-
-      if (interpreter_->ModifyGraphWithDelegate(
-        std::unique_ptr<TfLiteDelegate, decltype(&edgetpu_free_delegate)>(delegate, edgetpu_free_delegate)) != kTfLiteOk) {
+      DelegatePtr delegate(edgetpu_create_delegate(device.type, device.path,
+                                                   &option, 1),
+                           edgetpu_free_delegate);
+      if (interpreter_->ModifyGraphWithDelegate(std::move(delegate)) != kTfLiteOk) {
         std::cerr << "[ERROR] Cannot apply EdgeTPU delegate" << std::endl;
         return false;
       }
@@ -162,10 +162,12 @@
   }
 
  public:
-  void Invoke() {
+  bool Invoke() {
     if (interpreter_->Invoke() != kTfLiteOk) {
-      std::cout << "[ERROR] Cannot invoke interpreter" << std::endl;
+      std::cerr << "[ERROR] Cannot invoke interpreter" << std::endl;
+      return false;
     }
+    return true;
   }
 
   void InvokeAsync(size_t id) {
@@ -181,73 +183,11 @@
 
 }  // namespace
 
-int main(int argc, char* argv[]) {
-  std::cout << "main()" << std::endl;
-  return 0;
-}
-
 extern "C" {
 
-//  --linkopt=--preload-file=$(MAKEFILE_DIR)/mobilenet_v1_1.0_224_quant_edgetpu.tflite@model.tflite
-//  --linkopt=--preload-file=$(MAKEFILE_DIR)/cat.rgb@cat.rgb
-EMSCRIPTEN_KEEPALIVE void inference_test() {
-  // auto* interpreter = new Interpreter();
-  // interpreter->Init("model.tflite", 10);
-
-  // std::ifstream file("cat.rgb", std::ios::binary);
-  // if (!file) {
-  //   std::cout << "[ERROR] Cannot open file" << std::endl;
-  //   return;
-  // }
-
-  // if (!file.read(reinterpret_cast<char*>(interpreter->Input()),
-  //                interpreter->InputSize())) {
-  //   std::cout << "[ERROR] Cannot read file" << std::endl;
-  //   return;
-  // }
-
-  // std::thread th([interpreter]() {
-  //   auto index = interpreter->Invoke();
-  //   std::cout << "====> Result: " << index << std::endl;
-  //   delete interpreter;
-  // });
-  // th.detach();
-}
-
-// EMSCRIPTEN_KEEPALIVE void update_firmware() {
-//   std::thread th([]() {
-//     platforms::darwinn::api::Driver::Options options;
-
-//     platforms::darwinn::api::Device device;
-//     device.chip = platforms::darwinn::api::Chip::kBeagle;
-//     device.type = platforms::darwinn::api::Device::Type::USB;
-//     device.path = "default";
-
-//     auto* factory = platforms::darwinn::api::DriverFactory::GetOrCreate();
-//     std::cout << factory << std::endl;
-//     auto driver = factory->CreateDriver(device);
-//     if (!driver.ok()) {
-//        std::cout << "[ERROR] Cannot create USB driver" << std::endl;
-//        MAIN_THREAD_ASYNC_EM_ASM({Module['firmwareDone']($0);}, 0);
-//        return;
-//     }
-
-//     auto* usb_driver = static_cast<platforms::darwinn::driver::UsbDriver*>(driver->get());
-//     auto status = usb_driver->FlashUsbDevice();
-//     if (!status.ok()) {
-//       std::cout << "[ERROR] Cannot flash USB device" << std::endl;
-//       MAIN_THREAD_ASYNC_EM_ASM({Module['firmwareDone']($0);}, 0);
-//       return;
-//     }
-
-//     std::cout << "USB device is ready!" << std::endl;
-//     MAIN_THREAD_ASYNC_EM_ASM({Module['firmwareDone']($0);}, 1);
-//     return;
-//   });
-//   th.detach();
-// }
-
-EMSCRIPTEN_KEEPALIVE void* interpreter_create(const char* model_buffer, size_t model_buffer_size, int verbosity) {
+EMSCRIPTEN_KEEPALIVE
+void* interpreter_create(const char* model_buffer, size_t model_buffer_size,
+                         int verbosity) {
   auto* interpreter = new Interpreter();
   if (!interpreter->Init(model_buffer, model_buffer_size, verbosity)) {
     delete interpreter;
@@ -256,45 +196,55 @@
   return interpreter;
 }
 
-EMSCRIPTEN_KEEPALIVE void interpreter_destroy(void* p) {
+EMSCRIPTEN_KEEPALIVE
+void interpreter_destroy(void* p) {
   delete reinterpret_cast<Interpreter*>(p);
 }
 
 // Inputs
-EMSCRIPTEN_KEEPALIVE size_t interpreter_num_inputs(void* interpreter) {
+EMSCRIPTEN_KEEPALIVE
+size_t interpreter_num_inputs(void* interpreter) {
   return reinterpret_cast<Interpreter*>(interpreter)->NumInputs();
 }
 
-EMSCRIPTEN_KEEPALIVE void* interpreter_input_buffer(void* interpreter, size_t tensor_index) {
+EMSCRIPTEN_KEEPALIVE
+void* interpreter_input_buffer(void* interpreter, size_t tensor_index) {
   return reinterpret_cast<Interpreter*>(interpreter)->InputBuffer(tensor_index);
 }
 
-EMSCRIPTEN_KEEPALIVE size_t interpreter_num_input_dims(void *interpreter, size_t tensor_index) {
+EMSCRIPTEN_KEEPALIVE
+size_t interpreter_num_input_dims(void *interpreter, size_t tensor_index) {
   return reinterpret_cast<Interpreter*>(interpreter)->NumInputDims(tensor_index);
 }
 
-EMSCRIPTEN_KEEPALIVE size_t interpreter_input_dim(void *interpreter, size_t tensor_index, size_t dim) {
+EMSCRIPTEN_KEEPALIVE
+size_t interpreter_input_dim(void *interpreter, size_t tensor_index, size_t dim) {
   return reinterpret_cast<Interpreter*>(interpreter)->InputDim(tensor_index, dim);
 }
 
 // Outputs
-EMSCRIPTEN_KEEPALIVE size_t interpreter_num_outputs(void* interpreter) {
+EMSCRIPTEN_KEEPALIVE
+size_t interpreter_num_outputs(void* interpreter) {
   return reinterpret_cast<Interpreter*>(interpreter)->NumOutputs();
 }
 
-EMSCRIPTEN_KEEPALIVE const void* interpreter_output_buffer(void* interpreter, size_t tensor_index) {
+EMSCRIPTEN_KEEPALIVE
+const void* interpreter_output_buffer(void* interpreter, size_t tensor_index) {
   return reinterpret_cast<Interpreter*>(interpreter)->OutputBuffer(tensor_index);
 }
 
-EMSCRIPTEN_KEEPALIVE size_t interpreter_num_output_dims(void *interpreter, size_t tensor_index) {
+EMSCRIPTEN_KEEPALIVE
+size_t interpreter_num_output_dims(void *interpreter, size_t tensor_index) {
   return reinterpret_cast<Interpreter*>(interpreter)->NumOutputDims(tensor_index);
 }
 
-EMSCRIPTEN_KEEPALIVE size_t interpreter_output_dim(void *interpreter, size_t tensor_index, size_t dim) {
+EMSCRIPTEN_KEEPALIVE
+size_t interpreter_output_dim(void *interpreter, size_t tensor_index, size_t dim) {
   return reinterpret_cast<Interpreter*>(interpreter)->OutputDim(tensor_index, dim);
 }
 
-EMSCRIPTEN_KEEPALIVE void interpreter_invoke_async(void *interpreter, size_t id) {
+EMSCRIPTEN_KEEPALIVE
+void interpreter_invoke_async(void *interpreter, size_t id) {
   return reinterpret_cast<Interpreter*>(interpreter)->InvokeAsync(id);
 }
 
diff --git a/tflite/libusb.cc b/tflite/libusb.cc
index 18ac6f6..0445998 100644
--- a/tflite/libusb.cc
+++ b/tflite/libusb.cc
@@ -11,7 +11,6 @@
 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 // See the License for the specific language governing permissions and
 // limitations under the License.
-
 #include <iostream>
 
 #include <emscripten.h>
@@ -25,8 +24,7 @@
 #define LIBUSB_NANO 0
 #define LIBUSB_RC ""
 
-//#define LIBUSB_ENABLE_LOG
-
+// #define LIBUSB_ENABLE_LOG
 #ifdef LIBUSB_ENABLE_LOG
 #define LIBUSB_LOG(...)           \
   do {                            \
@@ -41,7 +39,6 @@
   uint8_t bus_number;
   uint8_t port_number;
   struct libusb_context *ctx;
-
   struct libusb_device_descriptor descriptor;
 };
 
@@ -54,7 +51,7 @@
   struct libusb_device *dev;
 };
 
-static const struct libusb_version libusb_version_internal = {
+static const struct libusb_version kVersion = {
   LIBUSB_MAJOR,
   LIBUSB_MINOR,
   LIBUSB_MICRO,
@@ -66,12 +63,9 @@
 static int js_request_device(struct libusb_device *dev) {
   return MAIN_THREAD_EM_ASM_INT({
     return Asyncify.handleAsync(async () => {
-      //console.log('js_request_device');
-
       // Bus 001 Device 005: ID 1a6e:089a Global Unichip Corp.
       // Bus 002 Device 007: ID 18d1:9302 Google Inc.
       let options = {'filters': [{'vendorId': 0x18d1, 'productId': 0x9302}]};
-
       let devices = await navigator.usb.getDevices();
       if (!devices.length) {
         try {
@@ -82,13 +76,11 @@
         }
       }
 
-      //console.log('js_request_device, device list:', devices);
-
       if (devices.length === 1) {
         let d = devices[0];
         _fill_device($0,
                    /*bcdUSB=*/(d.usbVersionMajor << 8) | d.usbVersionMinor,
-                   /*bDeviceClass*/d.deviceClass,
+                   /*bDeviceClass=*/d.deviceClass,
                    /*bDeviceSubClass=*/d.deviceSubClass,
                    /*bDeviceProtocol=*/d.deviceProtocol,
                    /*idVendor=*/d.vendorId,
@@ -103,12 +95,11 @@
   }, dev);
 }
 
-static int js_control_transfer(uint8_t bmRequestType, uint8_t bRequest, uint16_t wValue,
-                         uint16_t wIndex, uint8_t *data, uint16_t wLength, unsigned int timeout) {
+static int js_control_transfer(uint8_t bmRequestType, uint8_t bRequest,
+                               uint16_t wValue, uint16_t wIndex, uint8_t *data,
+                               uint16_t wLength, unsigned int timeout) {
   return MAIN_THREAD_EM_ASM_INT({
     return Asyncify.handleAsync(async () => {
-      //console.log('js_control_transfer');
-
       let bmRequestType = $0;
       let bRequest = $1;
       let wValue = $2;
@@ -128,8 +119,8 @@
       let dir_in = (bmRequestType & 0x80) == 0x80;
       if (dir_in) {
         let result = await this.libusb_device.controlTransferIn(setup, wLength);
-        if (result.status != "ok") {
-          out("JS[js_control_transfer]: <ERROR> controlTransferIn response: " + result.status);
+        if (result.status != 'ok') {
+          console.error('controlTransferIn', result);
           return 0;
         }
 
@@ -139,11 +130,11 @@
       } else {
         let buffer = new Uint8Array(wLength);
         for (let i = 0; i < wLength; ++i)
-          buffer[i] = getValue(data + i, "i8");
+          buffer[i] = getValue(data + i, 'i8');
 
         let result = await this.libusb_device.controlTransferOut(setup, buffer);
-        if(result.status != "ok") {
-          out("JS[js_control_transfer]: <ERROR> controlTransferOut response: " + result.status);
+        if (result.status != 'ok') {
+          console.error('controlTransferOut', result);
           return 0;
         }
         return result.bytesWritten;
@@ -152,22 +143,34 @@
   }, bmRequestType, bRequest, wValue, wIndex, data, wLength, timeout);
 }
 
-static void print_device_descriptor(const struct libusb_device_descriptor* d) {
-  std::cout << "Descriptor" << std::endl;
-  std::cout << "  bLength: "                 << static_cast<int>(d->bLength)            << std::endl;
-  std::cout << "  bDescriptorType: "         << static_cast<int>(d->bDescriptorType)    << std::endl;
-  std::cout << "  bcdUSB: 0x"    << std::hex << static_cast<int>(d->bcdUSB) << std::dec << std::endl;
-  std::cout << "  bDeviceClass: "            << static_cast<int>(d->bDeviceClass)       << std::endl;
-  std::cout << "  bDeviceSubClass: "         << static_cast<int>(d->bDeviceSubClass)    << std::endl;
-  std::cout << "  bDeviceProtocol: "         << static_cast<int>(d->bDeviceProtocol)    << std::endl;
-  std::cout << "  bMaxPacketSize0: "         << static_cast<int>(d->bMaxPacketSize0)    << std::endl;
-  std::cout << "  idVendor: 0x"  << std::hex << static_cast<int>(d->idVendor)  << std::dec << std::endl;
-  std::cout << "  idProduct: 0x" << std::hex << static_cast<int>(d->idProduct) << std::dec << std::endl;
-  std::cout << "  bcdDevice: 0x" << std::hex << static_cast<int>(d->bcdDevice) << std::dec << std::endl;
-  std::cout << "  iManufacturer: "           << static_cast<int>(d->iManufacturer)      << std::endl;
-  std::cout << "  iProduct: "                << static_cast<int>(d->iProduct)           << std::endl;
-  std::cout << "  iSerialNumber: "           << static_cast<int>(d->iSerialNumber)      << std::endl;
-  std::cout << "  bNumConfigurations: "      << static_cast<int>(d->bNumConfigurations) << std::endl;
+static void print(const char* line) { std::cout << line << std::endl; }
+
+static void print(const char* line, int value, bool hex=false) {
+  if (hex)
+    std::cout << line << std::hex << value << std::dec << std::endl;
+  else
+    std::cout << line << value << std::endl;
+}
+
+static void print_device(const struct libusb_device* dev) {
+  print("USB Device");
+  print("  Bus: ",  dev->bus_number);
+  print("  Port: ", dev->port_number);
+  print("  Descriptor");
+  print("    bLength: ",            dev->descriptor.bLength);
+  print("    bDescriptorType: ",    dev->descriptor.bDescriptorType);
+  print("    bcdUSB: 0x",           dev->descriptor.bcdUSB, /*hex=*/true);
+  print("    bDeviceClass: ",       dev->descriptor.bDeviceClass);
+  print("    bDeviceSubClass: ",    dev->descriptor.bDeviceSubClass);
+  print("    bDeviceProtocol: ",    dev->descriptor.bDeviceProtocol);
+  print("    bMaxPacketSize0: ",    dev->descriptor.bMaxPacketSize0);
+  print("    idVendor: 0x",         dev->descriptor.idVendor, /*hex=*/true);
+  print("    idProduct: 0x",        dev->descriptor.idProduct, /*hex=*/true);
+  print("    bcdDevice: 0x",        dev->descriptor.bcdDevice, /*hex=*/true);
+  print("    iManufacturer: ",      dev->descriptor.iManufacturer);
+  print("    iProduct: ",           dev->descriptor.iProduct);
+  print("    iSerialNumber: ",      dev->descriptor.iSerialNumber);
+  print("    bNumConfigurations: ", dev->descriptor.bNumConfigurations);
 }
 
 extern "C" {
@@ -188,12 +191,12 @@
 }
 
 void LIBUSB_CALL libusb_set_debug(libusb_context *ctx, int level) {
-  LIBUSB_LOG("<NOT IMPLEMENTED> libusb_set_debug");
+  LIBUSB_LOG("libusb_set_debug [NOT IMPLEMENTED]");
 }
 
 const struct libusb_version* LIBUSB_CALL libusb_get_version() {
   LIBUSB_LOG("libusb_get_version");
-  return &libusb_version_internal;
+  return &kVersion;
 }
 
 struct libusb_transfer* LIBUSB_CALL libusb_alloc_transfer(int iso_packets) {
@@ -202,7 +205,7 @@
   size_t size = sizeof(struct libusb_transfer) +
                 sizeof(struct libusb_iso_packet_descriptor) * iso_packets;
 
-  return reinterpret_cast<libusb_transfer*>(calloc(1, size));;
+  return reinterpret_cast<libusb_transfer*>(calloc(1, size));
 }
 
 int LIBUSB_CALL libusb_submit_transfer(struct libusb_transfer *transfer) {
@@ -216,29 +219,25 @@
     case LIBUSB_TRANSFER_TYPE_INTERRUPT:
       if (dir_in) {
         MAIN_THREAD_ASYNC_EM_ASM({
-          //console.log('js_submit_bulk_in_transfer, endpoint: ', $0);
-
           this.libusb_device.transferIn($0, $2).then(function(result) {
             var data = new Uint8Array(result.data.buffer);
             writeArrayToMemory(data, $1);
             _set_transfer_completed($3, data.length);
           }).catch(function(error) {
-            console.error('js_submit_bulk_in_transfer:', error);
+            console.error('transferIn', error);
             _set_transfer_error($3);
           });
         }, endpoint, transfer->buffer, transfer->length, transfer);
       } else {
         MAIN_THREAD_ASYNC_EM_ASM({
-          //console.log('js_submit_bulk_out_transfer, endpoint: ', $0);
-
           var data = new Uint8Array($2);
           for(let i = 0; i < $2; ++i)
-            data[i] = getValue($1 + i, "i8");
+            data[i] = getValue($1 + i, 'i8');
 
           this.libusb_device.transferOut($0, data).then(function(result) {
             _set_transfer_completed($3, result.bytesWritten);
           }).catch(function(error) {
-            console.error('js_submit_bulk_out_transfer:', error);
+            console.error('transferOut', error);
             _set_transfer_error($3);
           });
         }, endpoint, transfer->buffer, transfer->length, transfer);
@@ -252,7 +251,7 @@
 }
 
 int LIBUSB_CALL libusb_cancel_transfer(struct libusb_transfer *transfer) {
-  LIBUSB_LOG("<NOT IMPLEMENTED> libusb_cancel_transfer");
+  LIBUSB_LOG("libusb_cancel_transfer [NOT IMPLEMENTED]");
   return 0;
 }
 
@@ -280,11 +279,11 @@
 }
 
 int LIBUSB_CALL libusb_handle_events(libusb_context *ctx) {
-  //std::cout << "libusb_handle_events" << std::endl;
+  LIBUSB_LOG("libusb_handle_events");
+
   while (true) {
-    if (auto item = ctx->completed_transfers.Pop(25)) {
+    if (auto item = ctx->completed_transfers.Pop(25/*ms*/)) {
       auto* transfer = item.value();
-      //std::cout << "==  Running callback ==" << std::endl;
       transfer->callback(transfer);
     } else {
       break;
@@ -299,51 +298,50 @@
   return MAIN_THREAD_EM_ASM_INT({
     return Asyncify.handleAsync(async () => {
       try {
-        //console.log('js_reset_device');
         await this.libusb_device.reset();
         return 0;  // LIBUSB_SUCCESS
       } catch (error) {
-        console.error('js_reset_device:', error);
-        //return -1;  // LIBUSB_ERROR_IO
-        return 0;  // TODO(dkovalev)
+        console.error('reset', error);
+        // TODO: return -1;  // LIBUSB_ERROR_IO
+        return 0;  // LIBUSB_SUCCESS
       }
     });
   });
 }
 
 int LIBUSB_CALL libusb_control_transfer(libusb_device_handle *dev_handle,
-  uint8_t request_type, uint8_t bRequest, uint16_t wValue, uint16_t wIndex,
-  unsigned char *data, uint16_t wLength, unsigned int timeout) {
+    uint8_t request_type, uint8_t bRequest, uint16_t wValue, uint16_t wIndex,
+    unsigned char *data, uint16_t wLength, unsigned int timeout) {
   LIBUSB_LOG("libusb_control_transfer");
-  return js_control_transfer(request_type, bRequest, wValue, wIndex, data, wLength, timeout);
+  return js_control_transfer(request_type, bRequest, wValue, wIndex, data,
+                             wLength, timeout);
 }
 
 int LIBUSB_CALL libusb_bulk_transfer(libusb_device_handle *dev_handle,
-  unsigned char endpoint, unsigned char *data, int length,
-  int *actual_length, unsigned int timeout) {
-  LIBUSB_LOG("<NOT IMPLEMENTED> libusb_bulk_transfer");
+    unsigned char endpoint, unsigned char *data, int length,
+    int *actual_length, unsigned int timeout) {
+  LIBUSB_LOG("libusb_bulk_transfer [NOT IMPLEMENTED]");
   return 0;
 }
 
 int LIBUSB_CALL libusb_interrupt_transfer(libusb_device_handle *dev_handle,
-  unsigned char endpoint, unsigned char *data, int length,
-  int *actual_length, unsigned int timeout) {
-  LIBUSB_LOG("<NOT IMPLEMENTED> libusb_interrupt_transfer");
+    unsigned char endpoint, unsigned char *data, int length,
+    int *actual_length, unsigned int timeout) {
+  LIBUSB_LOG("libusb_interrupt_transfer [NOT IMPLEMENTED]");
   return 0;
 }
 
 int LIBUSB_CALL libusb_open(libusb_device *dev, libusb_device_handle **handle) {
   LIBUSB_LOG("libusb_open");
-  // dev->descriptor.idVendor, dev->descriptor.idProduct
-
+  // TODO: check dev->descriptor.idVendor and dev->descriptor.idProduct
   MAIN_THREAD_EM_ASM_INT({
     return Asyncify.handleAsync(async () => {
-      //console.log('js_open_device');
       await this.libusb_device.open();
       try {
-        await this.libusb_device.reset(); // TODO(dkovalev)
+        // TODO: Avoid resetting on open.
+        await this.libusb_device.reset();
       } catch (error) {
-        console.error('this.libusb_device.reset()', error);
+        console.error('reset', error);
       }
       return 1;
     });
@@ -362,7 +360,6 @@
 
   MAIN_THREAD_EM_ASM_INT({
     Asyncify.handleAsync(async () => {
-      //console.log('js_close_device');
       return await this.libusb_device.close();
     });
   });
@@ -382,11 +379,11 @@
   dev->ctx = ctx;
 
   if (js_request_device(dev)) {
-    print_device_descriptor(&dev->descriptor);
-    *list = new libusb_device*[2]{dev, nullptr};
+    print_device(dev);
+    *list = new libusb_device*[]{dev, nullptr};
     return 1;
   } else {
-    *list = new libusb_device*[1]{nullptr};
+    *list = new libusb_device*[]{nullptr};
     return 0;
   }
 }
@@ -412,7 +409,7 @@
 
 int LIBUSB_CALL libusb_get_device_speed(libusb_device *dev) {
   LIBUSB_LOG("libusb_get_device_speed");
-  return LIBUSB_SPEED_SUPER;  // TODO(dkovalev)
+  return LIBUSB_SPEED_SUPER;  // TODO: Implement.
 }
 
 uint8_t LIBUSB_CALL libusb_get_bus_number(libusb_device *dev) {
@@ -420,37 +417,38 @@
   return dev->bus_number;
 }
 
-int LIBUSB_CALL libusb_set_configuration(libusb_device_handle *dev, int configuration) {
-  LIBUSB_LOG("<NOT IMPLEMENTED> libusb_set_configuration");
+int LIBUSB_CALL libusb_set_configuration(libusb_device_handle *dev,
+                                         int configuration) {
+  LIBUSB_LOG("libusb_set_configuration [NOT IMPLEMENTED]");
   return 0;
 }
 
-int LIBUSB_CALL libusb_claim_interface(libusb_device_handle *dev, int interface_number) {
-  LIBUSB_LOG("libusb_claim_interface: %d", interface_number);
+int LIBUSB_CALL libusb_claim_interface(libusb_device_handle *dev,
+                                       int interface_number) {
+  LIBUSB_LOG("libusb_claim_interface: interface_number=%d", interface_number);
   return MAIN_THREAD_EM_ASM_INT({
     return Asyncify.handleAsync(async () => {
       try {
-        //console.log('js_claim_interface: ', $0);
         await this.libusb_device.claimInterface($0);
         return 0;  // LIBUSB_SUCCESS
       } catch (error) {
-        console.error('js_claim_interface:', error);
+        console.error('claimInterface:', error);
         return -1;  // LIBUSB_ERROR_IO
       }
     });
   }, interface_number);
 }
 
-int LIBUSB_CALL libusb_release_interface(libusb_device_handle *dev, int interface_number) {
-  LIBUSB_LOG("libusb_release_interface: %d", interface_number);
+int LIBUSB_CALL libusb_release_interface(libusb_device_handle *dev,
+                                         int interface_number) {
+  LIBUSB_LOG("libusb_release_interface: interface_number=%d", interface_number);
   return MAIN_THREAD_EM_ASM_INT({
     return Asyncify.handleAsync(async () => {
       try {
-        //console.log('js_release_interface: ', $0, this.libusb_device);
         await this.libusb_device.releaseInterface($0);
         return 0;  // LIBUSB_SUCCESS
       } catch (error) {
-        console.error('js_release_interface:', error);
+        console.error('releaseInterface:', error);
         return -1;  // LIBUSB_ERROR_IO
       }
     });
@@ -458,8 +456,9 @@
 }
 
 
-EMSCRIPTEN_KEEPALIVE void set_transfer_error(struct libusb_transfer* transfer) {
-  LIBUSB_LOG("==> set_tansfer_error [%p]", transfer);
+EMSCRIPTEN_KEEPALIVE
+void set_transfer_error(struct libusb_transfer* transfer) {
+  LIBUSB_LOG("set_tansfer_error: transfer=%p", transfer);
   libusb_context* ctx = transfer->dev_handle->dev->ctx;
 
   transfer->status = LIBUSB_TRANSFER_CANCELLED;
@@ -468,8 +467,10 @@
   ctx->completed_transfers.Push(transfer);
 }
 
-EMSCRIPTEN_KEEPALIVE void set_transfer_completed(struct libusb_transfer* transfer, int actual_length) {
-  LIBUSB_LOG("==> set_tansfer_completed [%p]: %d", transfer, actual_length);
+EMSCRIPTEN_KEEPALIVE
+void set_transfer_completed(struct libusb_transfer* transfer, int actual_length) {
+  LIBUSB_LOG("set_tansfer_completed: transfer=%p, actual_length=%d",
+             transfer, actual_length);
   libusb_context* ctx = transfer->dev_handle->dev->ctx;
 
   transfer->status = LIBUSB_TRANSFER_COMPLETED;
@@ -478,7 +479,8 @@
   ctx->completed_transfers.Push(transfer);
 }
 
-EMSCRIPTEN_KEEPALIVE void fill_device(struct libusb_device* dev,
+EMSCRIPTEN_KEEPALIVE
+void fill_device(struct libusb_device* dev,
     uint16_t bcdUSB,
     uint8_t bDeviceClass,
     uint8_t bDeviceSubClass,
diff --git a/tflite/queue.h b/tflite/queue.h
index 18d9cce..cc08f1f 100644
--- a/tflite/queue.h
+++ b/tflite/queue.h
@@ -11,9 +11,8 @@
 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 // See the License for the specific language governing permissions and
 // limitations under the License.
-
-#ifndef INTERPRETER_QUEUE_H_
-#define INTERPRETER_QUEUE_H_
+#ifndef TFLITE_QUEUE_H_
+#define TFLITE_QUEUE_H_
 
 #include <chrono>
 #include <condition_variable>
@@ -26,7 +25,7 @@
  public:
   Queue() {}
 
-  void Push(T t){
+  void Push(T t) {
     {
       std::lock_guard<std::mutex> lock(m_);
       q_.push(t);
@@ -36,8 +35,9 @@
 
   std::optional<T> Pop(int timeout_ms) {
     std::unique_lock<std::mutex> lock(m_);
-    if (!cv_.wait_for(lock, std::chrono::milliseconds(timeout_ms),
-                  [this]{ return !q_.empty(); }))
+    if (!cv_.wait_for(lock,
+                      std::chrono::milliseconds(timeout_ms),
+                      [this]{ return !q_.empty(); }))
       return std::nullopt;
 
     auto t = q_.front();
@@ -51,4 +51,4 @@
   std::queue<T> q_;
 };
 
-#endif  // INTERPRETER_QUEUE_H_
+#endif  // TFLITE_QUEUE_H_
diff --git a/third_party/libedgetpu b/third_party/libedgetpu
index 999ad40..e68e773 160000
--- a/third_party/libedgetpu
+++ b/third_party/libedgetpu
@@ -1 +1 @@
-Subproject commit 999ad407bbaac56d53fc03a2c1d9c281cde970ef
+Subproject commit e68e773ba65b14fc68e6ff01c58f0dc761682125