-
Notifications
You must be signed in to change notification settings - Fork 2
/
usb.js
129 lines (110 loc) · 2.68 KB
/
usb.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
const { Device, FlashInterface } = require('./device');
const { delay } = require('./util');
const usb = require('particle-usb');
const fs = require('fs');
// Flashing an NCP firmware can take a few minutes
const FLASH_TIMEOUT = 4 * 60 * 1000;
async function openUsbDeviceById(id, { timeout = 3000 } = {}) {
const t2 = Date.now() + timeout;
for (;;) {
try {
const dev = await usb.openDeviceById(id);
await delay(500); // Just in case
return dev;
} catch (err) {
// Ignore error
}
const t = t2 - Date.now();
if (t <= 0) {
throw new Error('Unable to open USB device');
}
await delay(Math.min(t, 250 + Math.floor(Math.random() * 250)));
}
}
class UsbDevice extends Device {
constructor({ id, platformId, device, log }) {
super({ id, platformId, log });
this._dev = device;
}
async open(options) {
if (this._dev) {
throw new Error('Device is already open');
}
this._dev = await openUsbDeviceById(this.id, options);
}
async close() {
if (this._dev) {
await this._dev.close();
this._dev = null;
}
}
async reset() {
if (!this._dev) {
throw new Error('Device is not open');
}
await this._dev.reset();
}
async prepareToFlash() {
if (!this._dev) {
throw new Error('Device is not open');
}
try {
// Make sure the device is not trying to connect to the cloud
this._log.debug('Entering listening mode');
await this._dev.enterListeningMode();
await delay(1000); // Just in case
} catch (err) {
this._log.warn(err.message);
}
}
async flashModule(module) {
if (!this._dev) {
throw new Error('Device is not open');
}
const data = fs.readFileSync(module.file);
await this._dev.updateFirmware(data, { timeout: FLASH_TIMEOUT });
return { resetPending: true };
}
async writeToFlash(/* file, storage, address */) {
throw new Error('Not supported');
}
canFlashModule(/* module */) {
return true;
}
canWriteToFlash(/* storage */) {
return false;
}
}
class UsbFlashInterface extends FlashInterface {
constructor({ log }) {
super({ log });
}
async init() {
}
async shutdown() {
}
async listDevices() {
const devs = [];
const usbDevs = await usb.getDevices();
for (const usbDev of usbDevs) {
try {
await usbDev.open();
const id = usbDev.id;
devs.push(new UsbDevice({ id, platformId: usbDev.platformId, log: this._log }));
} catch (err) {
// Ignore error
} finally {
await usbDev.close();
}
}
return devs;
}
async openDeviceById(id, options) {
const usbDev = await openUsbDeviceById(id, options);
return new UsbDevice({ id: usbDev.id, platformId: usbDev.platformId, device: usbDev, log: this._log });
}
}
module.exports = {
openUsbDeviceById,
UsbFlashInterface
};