OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 /** | 5 /** |
6 * @fileoverview Implements a low-level gnubby driver based on chrome.hid. | 6 * @fileoverview Implements a low-level gnubby driver based on chrome.hid. |
7 */ | 7 */ |
8 'use strict'; | 8 'use strict'; |
9 | 9 |
10 /** | 10 /** |
11 * Low level gnubby 'driver'. One per physical USB device. | 11 * Low level gnubby 'driver'. One per physical USB device. |
12 * @param {Gnubbies} gnubbies The gnubbies instances this device is enumerated | 12 * @param {Gnubbies} gnubbies The gnubbies instances this device is enumerated |
13 * in. | 13 * in. |
14 * @param {!chrome.hid.HidConnectInfo} dev The connection to the device. | 14 * @param {!chrome.hid.HidConnectInfo} dev The connection to the device. |
15 * @param {number} id The device's id. | 15 * @param {number} id The device's id. |
16 * @constructor | 16 * @constructor |
17 * @implements {GnubbyDevice} | 17 * @implements {GnubbyDevice} |
18 */ | 18 */ |
19 function HidGnubbyDevice(gnubbies, dev, id) { | 19 function HidGnubbyDevice(gnubbies, dev, id) { |
20 /** @private {Gnubbies} */ | 20 /** @private {Gnubbies} */ |
21 this.gnubbies_ = gnubbies; | 21 this.gnubbies_ = gnubbies; |
22 this.dev = dev; | 22 this.dev = dev; |
23 this.id = id; | 23 this.id = id; |
24 this.txqueue = []; | 24 this.txqueue = []; |
25 this.clients = []; | 25 this.clients = []; |
26 this.lockCID = 0; // channel ID of client holding a lock, if != 0. | 26 this.lockCID = 0; // channel ID of client holding a lock, if != 0. |
27 this.lockMillis = 0; // current lock period. | 27 this.lockMillis = 0; // current lock period. |
28 this.lockTID = null; // timer id of lock timeout. | 28 this.lockTID = null; // timer id of lock timeout. |
29 this.closing = false; // device to be closed by receive loop. | 29 this.closing = false; // device to be closed by receive loop. |
30 this.updating = false; // device firmware is in final stage of updating. | 30 this.updating = false; // device firmware is in final stage of updating. |
31 } | 31 } |
32 | 32 |
33 /** | 33 /** |
34 * Namespace for the HidGnubbyDevice implementation. | 34 * Namespace for the HidGnubbyDevice implementation. |
35 * @const | 35 * @const |
36 */ | 36 */ |
37 HidGnubbyDevice.NAMESPACE = 'hid'; | 37 HidGnubbyDevice.NAMESPACE = 'hid'; |
38 | 38 |
39 /** Destroys this low-level device instance. */ | 39 /** Destroys this low-level device instance. */ |
40 HidGnubbyDevice.prototype.destroy = function() { | 40 HidGnubbyDevice.prototype.destroy = function() { |
41 if (!this.dev) return; // Already dead. | 41 if (!this.dev) |
| 42 return; // Already dead. |
42 | 43 |
43 function closeLowLevelDevice(dev) { | 44 function closeLowLevelDevice(dev) { |
44 chrome.hid.disconnect(dev.connectionId, function() { | 45 chrome.hid.disconnect(dev.connectionId, function() { |
45 if (chrome.runtime.lastError) { | 46 if (chrome.runtime.lastError) { |
46 console.warn(UTIL_fmt('Device ' + dev.connectionId + | 47 console.warn(UTIL_fmt( |
47 ' couldn\'t be disconnected:')); | 48 'Device ' + dev.connectionId + ' couldn\'t be disconnected:')); |
48 console.warn(UTIL_fmt(chrome.runtime.lastError.message)); | 49 console.warn(UTIL_fmt(chrome.runtime.lastError.message)); |
49 return; | 50 return; |
50 } | 51 } |
51 console.log(UTIL_fmt('Device ' + dev.connectionId + ' closed')); | 52 console.log(UTIL_fmt('Device ' + dev.connectionId + ' closed')); |
52 }); | 53 }); |
53 } | 54 } |
54 | 55 |
55 this.gnubbies_.removeOpenDevice( | 56 this.gnubbies_.removeOpenDevice( |
56 {namespace: HidGnubbyDevice.NAMESPACE, device: this.id}); | 57 {namespace: HidGnubbyDevice.NAMESPACE, device: this.id}); |
57 this.closing = true; | 58 this.closing = true; |
58 | 59 |
59 console.log(UTIL_fmt('HidGnubbyDevice.destroy()')); | 60 console.log(UTIL_fmt('HidGnubbyDevice.destroy()')); |
60 | 61 |
61 // Synthesize a close error frame to alert all clients, | 62 // Synthesize a close error frame to alert all clients, |
62 // some of which might be in read state. | 63 // some of which might be in read state. |
63 // | 64 // |
64 // Use magic CID 0 to address all. | 65 // Use magic CID 0 to address all. |
65 this.publishFrame_(new Uint8Array([ | 66 this.publishFrame_(new Uint8Array([ |
66 0, 0, 0, 0, // broadcast CID | 67 0, 0, 0, 0, // broadcast CID |
67 GnubbyDevice.CMD_ERROR, | 68 GnubbyDevice.CMD_ERROR, 0, 1, // length |
68 0, 1, // length | 69 GnubbyDevice.GONE |
69 GnubbyDevice.GONE]).buffer); | 70 ]).buffer); |
70 | 71 |
71 // Set all clients to closed status and remove them. | 72 // Set all clients to closed status and remove them. |
72 while (this.clients.length != 0) { | 73 while (this.clients.length != 0) { |
73 var client = this.clients.shift(); | 74 var client = this.clients.shift(); |
74 if (client) client.closed = true; | 75 if (client) |
| 76 client.closed = true; |
75 } | 77 } |
76 | 78 |
77 if (this.lockTID) { | 79 if (this.lockTID) { |
78 window.clearTimeout(this.lockTID); | 80 window.clearTimeout(this.lockTID); |
79 this.lockTID = null; | 81 this.lockTID = null; |
80 } | 82 } |
81 | 83 |
82 var dev = this.dev; | 84 var dev = this.dev; |
83 this.dev = null; | 85 this.dev = null; |
84 var reallyCloseDevice = closeLowLevelDevice.bind(null, dev); | 86 var reallyCloseDevice = closeLowLevelDevice.bind(null, dev); |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
116 | 118 |
117 var remaining = []; | 119 var remaining = []; |
118 var changes = false; | 120 var changes = false; |
119 for (var i = 0; i < old.length; ++i) { | 121 for (var i = 0; i < old.length; ++i) { |
120 var client = old[i]; | 122 var client = old[i]; |
121 if (client.receivedFrame(f)) { | 123 if (client.receivedFrame(f)) { |
122 // Client still alive; keep on list. | 124 // Client still alive; keep on list. |
123 remaining.push(client); | 125 remaining.push(client); |
124 } else { | 126 } else { |
125 changes = true; | 127 changes = true; |
126 console.log(UTIL_fmt( | 128 console.log(UTIL_fmt('[' + Gnubby.hexCid(client.cid) + '] left?')); |
127 '[' + Gnubby.hexCid(client.cid) + '] left?')); | |
128 } | 129 } |
129 } | 130 } |
130 if (changes) this.clients = remaining; | 131 if (changes) |
| 132 this.clients = remaining; |
131 }; | 133 }; |
132 | 134 |
133 /** | 135 /** |
134 * Register a client for this gnubby. | 136 * Register a client for this gnubby. |
135 * @param {*} who The client. | 137 * @param {*} who The client. |
136 */ | 138 */ |
137 HidGnubbyDevice.prototype.registerClient = function(who) { | 139 HidGnubbyDevice.prototype.registerClient = function(who) { |
138 for (var i = 0; i < this.clients.length; ++i) { | 140 for (var i = 0; i < this.clients.length; ++i) { |
139 if (this.clients[i] === who) return; // Already registered. | 141 if (this.clients[i] === who) |
| 142 return; // Already registered. |
140 } | 143 } |
141 this.clients.push(who); | 144 this.clients.push(who); |
142 if (this.clients.length == 1) { | 145 if (this.clients.length == 1) { |
143 // First client? Kick off read loop. | 146 // First client? Kick off read loop. |
144 this.readLoop_(); | 147 this.readLoop_(); |
145 } | 148 } |
146 }; | 149 }; |
147 | 150 |
148 /** | 151 /** |
149 * De-register a client. | 152 * De-register a client. |
150 * @param {*} who The client. | 153 * @param {*} who The client. |
151 * @return {number} The number of remaining listeners for this device, or -1 | 154 * @return {number} The number of remaining listeners for this device, or -1 |
152 * Returns number of remaining listeners for this device. | 155 * Returns number of remaining listeners for this device. |
153 * if this had no clients to start with. | 156 * if this had no clients to start with. |
154 */ | 157 */ |
155 HidGnubbyDevice.prototype.deregisterClient = function(who) { | 158 HidGnubbyDevice.prototype.deregisterClient = function(who) { |
156 var current = this.clients; | 159 var current = this.clients; |
157 if (current.length == 0) return -1; | 160 if (current.length == 0) |
| 161 return -1; |
158 this.clients = []; | 162 this.clients = []; |
159 for (var i = 0; i < current.length; ++i) { | 163 for (var i = 0; i < current.length; ++i) { |
160 var client = current[i]; | 164 var client = current[i]; |
161 if (client !== who) this.clients.push(client); | 165 if (client !== who) |
| 166 this.clients.push(client); |
162 } | 167 } |
163 return this.clients.length; | 168 return this.clients.length; |
164 }; | 169 }; |
165 | 170 |
166 /** | 171 /** |
167 * @param {*} who The client. | 172 * @param {*} who The client. |
168 * @return {boolean} Whether this device has who as a client. | 173 * @return {boolean} Whether this device has who as a client. |
169 */ | 174 */ |
170 HidGnubbyDevice.prototype.hasClient = function(who) { | 175 HidGnubbyDevice.prototype.hasClient = function(who) { |
171 if (this.clients.length == 0) return false; | 176 if (this.clients.length == 0) |
| 177 return false; |
172 for (var i = 0; i < this.clients.length; ++i) { | 178 for (var i = 0; i < this.clients.length; ++i) { |
173 if (who === this.clients[i]) | 179 if (who === this.clients[i]) |
174 return true; | 180 return true; |
175 } | 181 } |
176 return false; | 182 return false; |
177 }; | 183 }; |
178 | 184 |
179 /** | 185 /** |
180 * Reads all incoming frames and notifies clients of their receipt. | 186 * Reads all incoming frames and notifies clients of their receipt. |
181 * @private | 187 * @private |
182 */ | 188 */ |
183 HidGnubbyDevice.prototype.readLoop_ = function() { | 189 HidGnubbyDevice.prototype.readLoop_ = function() { |
184 //console.log(UTIL_fmt('entering readLoop')); | 190 // console.log(UTIL_fmt('entering readLoop')); |
185 if (!this.dev) return; | 191 if (!this.dev) |
| 192 return; |
186 | 193 |
187 if (this.closing) { | 194 if (this.closing) { |
188 this.destroy(); | 195 this.destroy(); |
189 return; | 196 return; |
190 } | 197 } |
191 | 198 |
192 // No interested listeners, yet we hit readLoop(). | 199 // No interested listeners, yet we hit readLoop(). |
193 // Must be clean-up. We do this here to make sure no transfer is pending. | 200 // Must be clean-up. We do this here to make sure no transfer is pending. |
194 if (!this.clients.length) { | 201 if (!this.clients.length) { |
195 this.closing = true; | 202 this.closing = true; |
196 this.destroy(); | 203 this.destroy(); |
197 return; | 204 return; |
198 } | 205 } |
199 | 206 |
200 // firmwareUpdate() sets this.updating when writing the last block before | 207 // firmwareUpdate() sets this.updating when writing the last block before |
201 // the signature. We process that reply with the already pending | 208 // the signature. We process that reply with the already pending |
202 // read transfer but we do not want to start another read transfer for the | 209 // read transfer but we do not want to start another read transfer for the |
203 // signature block, since that request will have no reply. | 210 // signature block, since that request will have no reply. |
204 // Instead we will see the device drop and re-appear on the bus. | 211 // Instead we will see the device drop and re-appear on the bus. |
205 // Current libusb on some platforms gets unhappy when transfer are pending | 212 // Current libusb on some platforms gets unhappy when transfer are pending |
206 // when that happens. | 213 // when that happens. |
207 // TODO: revisit once Chrome stabilizes its behavior. | 214 // TODO: revisit once Chrome stabilizes its behavior. |
208 if (this.updating) { | 215 if (this.updating) { |
209 console.log(UTIL_fmt('device updating. Ending readLoop()')); | 216 console.log(UTIL_fmt('device updating. Ending readLoop()')); |
210 return; | 217 return; |
211 } | 218 } |
212 | 219 |
213 var self = this; | 220 var self = this; |
214 chrome.hid.receive( | 221 chrome.hid.receive(this.dev.connectionId, function(report_id, data) { |
215 this.dev.connectionId, | 222 if (chrome.runtime.lastError || !data) { |
216 function(report_id, data) { | 223 console.log(UTIL_fmt('receive got lastError:')); |
217 if (chrome.runtime.lastError || !data) { | 224 console.log(UTIL_fmt(chrome.runtime.lastError.message)); |
218 console.log(UTIL_fmt('receive got lastError:')); | 225 window.setTimeout(function() { |
219 console.log(UTIL_fmt(chrome.runtime.lastError.message)); | 226 self.destroy(); |
220 window.setTimeout(function() { self.destroy(); }, 0); | 227 }, 0); |
221 return; | 228 return; |
222 } | 229 } |
223 var u8 = new Uint8Array(data); | 230 var u8 = new Uint8Array(data); |
224 console.log(UTIL_fmt('<' + UTIL_BytesToHex(u8))); | 231 console.log(UTIL_fmt('<' + UTIL_BytesToHex(u8))); |
225 | 232 |
226 self.publishFrame_(data); | 233 self.publishFrame_(data); |
227 | 234 |
228 // Read more. | 235 // Read more. |
229 window.setTimeout(function() { self.readLoop_(); }, 0); | 236 window.setTimeout(function() { |
230 } | 237 self.readLoop_(); |
231 ); | 238 }, 0); |
| 239 }); |
232 }; | 240 }; |
233 | 241 |
234 /** | 242 /** |
235 * Check whether channel is locked for this request or not. | 243 * Check whether channel is locked for this request or not. |
236 * @param {number} cid Channel id | 244 * @param {number} cid Channel id |
237 * @param {number} cmd Request command | 245 * @param {number} cmd Request command |
238 * @return {boolean} true if not locked for this request. | 246 * @return {boolean} true if not locked for this request. |
239 * @private | 247 * @private |
240 */ | 248 */ |
241 HidGnubbyDevice.prototype.checkLock_ = function(cid, cmd) { | 249 HidGnubbyDevice.prototype.checkLock_ = function(cid, cmd) { |
242 if (this.lockCID) { | 250 if (this.lockCID) { |
243 // We have an active lock. | 251 // We have an active lock. |
244 if (this.lockCID != cid) { | 252 if (this.lockCID != cid) { |
245 // Some other channel has active lock. | 253 // Some other channel has active lock. |
246 | 254 |
247 if (cmd != GnubbyDevice.CMD_SYNC && | 255 if (cmd != GnubbyDevice.CMD_SYNC && cmd != GnubbyDevice.CMD_INIT) { |
248 cmd != GnubbyDevice.CMD_INIT) { | |
249 // Anything but SYNC|INIT gets an immediate busy. | 256 // Anything but SYNC|INIT gets an immediate busy. |
250 var busy = new Uint8Array( | 257 var busy = new Uint8Array([ |
251 [(cid >> 24) & 255, | 258 (cid >> 24) & 255, (cid >> 16) & 255, (cid >> 8) & 255, cid & 255, |
252 (cid >> 16) & 255, | 259 GnubbyDevice.CMD_ERROR, 0, 1, // length |
253 (cid >> 8) & 255, | 260 GnubbyDevice.BUSY |
254 cid & 255, | 261 ]); |
255 GnubbyDevice.CMD_ERROR, | |
256 0, 1, // length | |
257 GnubbyDevice.BUSY]); | |
258 // Log the synthetic busy too. | 262 // Log the synthetic busy too. |
259 console.log(UTIL_fmt('<' + UTIL_BytesToHex(busy))); | 263 console.log(UTIL_fmt('<' + UTIL_BytesToHex(busy))); |
260 this.publishFrame_(busy.buffer); | 264 this.publishFrame_(busy.buffer); |
261 return false; | 265 return false; |
262 } | 266 } |
263 | 267 |
264 // SYNC|INIT gets to go to the device to flush OS tx/rx queues. | 268 // SYNC|INIT gets to go to the device to flush OS tx/rx queues. |
265 // The usb firmware is to alway respond to SYNC/INIT, | 269 // The usb firmware is to alway respond to SYNC/INIT, |
266 // regardless of lock status. | 270 // regardless of lock status. |
267 } | 271 } |
(...skipping 24 matching lines...) Expand all Loading... |
292 this.lockMillis = nseconds * 1000 + 100; | 296 this.lockMillis = nseconds * 1000 + 100; |
293 } else { | 297 } else { |
294 // Releasing lock voluntarily. | 298 // Releasing lock voluntarily. |
295 this.lockCID = 0; | 299 this.lockCID = 0; |
296 } | 300 } |
297 } | 301 } |
298 | 302 |
299 // (re)set the lock timeout if we still hold it. | 303 // (re)set the lock timeout if we still hold it. |
300 if (this.lockCID) { | 304 if (this.lockCID) { |
301 var self = this; | 305 var self = this; |
302 this.lockTID = window.setTimeout( | 306 this.lockTID = window.setTimeout(function() { |
303 function() { | 307 console.warn( |
304 console.warn(UTIL_fmt( | 308 UTIL_fmt('lock for CID ' + Gnubby.hexCid(cid) + ' expired!')); |
305 'lock for CID ' + Gnubby.hexCid(cid) + ' expired!')); | 309 self.lockTID = null; |
306 self.lockTID = null; | 310 self.lockCID = 0; |
307 self.lockCID = 0; | 311 }, this.lockMillis); |
308 }, | |
309 this.lockMillis); | |
310 } | 312 } |
311 } | 313 } |
312 }; | 314 }; |
313 | 315 |
314 /** | 316 /** |
315 * Queue command to be sent. | 317 * Queue command to be sent. |
316 * If queue was empty, initiate the write. | 318 * If queue was empty, initiate the write. |
317 * @param {number} cid The client's channel ID. | 319 * @param {number} cid The client's channel ID. |
318 * @param {number} cmd The command to send. | 320 * @param {number} cmd The command to send. |
319 * @param {ArrayBuffer|Uint8Array} data Command arguments | 321 * @param {ArrayBuffer|Uint8Array} data Command arguments |
320 */ | 322 */ |
321 HidGnubbyDevice.prototype.queueCommand = function(cid, cmd, data) { | 323 HidGnubbyDevice.prototype.queueCommand = function(cid, cmd, data) { |
322 if (!this.dev) return; | 324 if (!this.dev) |
323 if (!this.checkLock_(cid, cmd)) return; | 325 return; |
| 326 if (!this.checkLock_(cid, cmd)) |
| 327 return; |
324 | 328 |
325 var u8 = new Uint8Array(data); | 329 var u8 = new Uint8Array(data); |
326 var f = new Uint8Array(64); | 330 var f = new Uint8Array(64); |
327 | 331 |
328 HidGnubbyDevice.setCid_(f, cid); | 332 HidGnubbyDevice.setCid_(f, cid); |
329 f[4] = cmd; | 333 f[4] = cmd; |
330 f[5] = (u8.length >> 8); | 334 f[5] = (u8.length >> 8); |
331 f[6] = (u8.length & 255); | 335 f[6] = (u8.length & 255); |
332 | 336 |
333 var lockArg = (u8.length > 0) ? u8[0] : 0; | 337 var lockArg = (u8.length > 0) ? u8[0] : 0; |
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
370 * @param {ArrayBuffer} frame Data frame | 374 * @param {ArrayBuffer} frame Data frame |
371 * @param {number} cid The client's channel ID. | 375 * @param {number} cid The client's channel ID. |
372 * @param {number} cmd The command to send. | 376 * @param {number} cmd The command to send. |
373 * @param {number} arg Command argument | 377 * @param {number} arg Command argument |
374 * @private | 378 * @private |
375 */ | 379 */ |
376 HidGnubbyDevice.prototype.queueFrame_ = function(frame, cid, cmd, arg) { | 380 HidGnubbyDevice.prototype.queueFrame_ = function(frame, cid, cmd, arg) { |
377 this.updateLock_(cid, cmd, arg); | 381 this.updateLock_(cid, cmd, arg); |
378 var wasEmpty = (this.txqueue.length == 0); | 382 var wasEmpty = (this.txqueue.length == 0); |
379 this.txqueue.push(frame); | 383 this.txqueue.push(frame); |
380 if (wasEmpty) this.writePump_(); | 384 if (wasEmpty) |
| 385 this.writePump_(); |
381 }; | 386 }; |
382 | 387 |
383 /** | 388 /** |
384 * Stuff queued frames from txqueue[] to device, one by one. | 389 * Stuff queued frames from txqueue[] to device, one by one. |
385 * @private | 390 * @private |
386 */ | 391 */ |
387 HidGnubbyDevice.prototype.writePump_ = function() { | 392 HidGnubbyDevice.prototype.writePump_ = function() { |
388 if (!this.dev) return; // Ignore. | 393 if (!this.dev) |
| 394 return; // Ignore. |
389 | 395 |
390 if (this.txqueue.length == 0) return; // Done with current queue. | 396 if (this.txqueue.length == 0) |
| 397 return; // Done with current queue. |
391 | 398 |
392 var frame = this.txqueue[0]; | 399 var frame = this.txqueue[0]; |
393 | 400 |
394 var self = this; | 401 var self = this; |
395 function transferComplete() { | 402 function transferComplete() { |
396 if (chrome.runtime.lastError) { | 403 if (chrome.runtime.lastError) { |
397 console.log(UTIL_fmt('send got lastError:')); | 404 console.log(UTIL_fmt('send got lastError:')); |
398 console.log(UTIL_fmt(chrome.runtime.lastError.message)); | 405 console.log(UTIL_fmt(chrome.runtime.lastError.message)); |
399 window.setTimeout(function() { self.destroy(); }, 0); | 406 window.setTimeout(function() { |
| 407 self.destroy(); |
| 408 }, 0); |
400 return; | 409 return; |
401 } | 410 } |
402 self.txqueue.shift(); // drop sent frame from queue. | 411 self.txqueue.shift(); // drop sent frame from queue. |
403 if (self.txqueue.length != 0) { | 412 if (self.txqueue.length != 0) { |
404 window.setTimeout(function() { self.writePump_(); }, 0); | 413 window.setTimeout(function() { |
| 414 self.writePump_(); |
| 415 }, 0); |
405 } | 416 } |
406 } | 417 } |
407 | 418 |
408 var u8 = new Uint8Array(frame); | 419 var u8 = new Uint8Array(frame); |
409 | 420 |
410 // See whether this requires scrubbing before logging. | 421 // See whether this requires scrubbing before logging. |
411 var alternateLog = Gnubby.hasOwnProperty('redactRequestLog') && | 422 var alternateLog = Gnubby.hasOwnProperty('redactRequestLog') && |
412 Gnubby['redactRequestLog'](u8); | 423 Gnubby['redactRequestLog'](u8); |
413 if (alternateLog) { | 424 if (alternateLog) { |
414 console.log(UTIL_fmt('>' + alternateLog)); | 425 console.log(UTIL_fmt('>' + alternateLog)); |
415 } else { | 426 } else { |
416 console.log(UTIL_fmt('>' + UTIL_BytesToHex(u8))); | 427 console.log(UTIL_fmt('>' + UTIL_BytesToHex(u8))); |
417 } | 428 } |
418 | 429 |
419 var u8f = new Uint8Array(64); | 430 var u8f = new Uint8Array(64); |
420 for (var i = 0; i < u8.length; ++i) { | 431 for (var i = 0; i < u8.length; ++i) { |
421 u8f[i] = u8[i]; | 432 u8f[i] = u8[i]; |
422 } | 433 } |
423 | 434 |
424 chrome.hid.send( | 435 chrome.hid.send( |
425 this.dev.connectionId, | 436 this.dev.connectionId, |
426 0, // report Id. Must be 0 for our use. | 437 0, // report Id. Must be 0 for our use. |
427 u8f.buffer, | 438 u8f.buffer, transferComplete); |
428 transferComplete | |
429 ); | |
430 }; | 439 }; |
431 | 440 |
432 /** | 441 /** |
433 * List of legacy HID devices that do not support the F1D0 usage page as | 442 * List of legacy HID devices that do not support the F1D0 usage page as |
434 * mandated by the spec, but still need to be supported. | 443 * mandated by the spec, but still need to be supported. |
435 * TODO: remove when these devices no longer need to be supported. | 444 * TODO: remove when these devices no longer need to be supported. |
436 * @const | 445 * @const |
437 */ | 446 */ |
438 HidGnubbyDevice.HID_VID_PIDS = [ | 447 HidGnubbyDevice.HID_VID_PIDS = [ |
439 {'vendorId': 4176, 'productId': 512} // Google-specific Yubico HID | 448 {'vendorId': 4176, 'productId': 512} // Google-specific Yubico HID |
440 ]; | 449 ]; |
441 | 450 |
442 /** | 451 /** |
443 * @param {function(Array)} cb Enumeration callback | 452 * @param {function(Array)} cb Enumeration callback |
444 * @param {GnubbyEnumerationTypes=} opt_type Which type of enumeration to do. | 453 * @param {GnubbyEnumerationTypes=} opt_type Which type of enumeration to do. |
445 */ | 454 */ |
446 HidGnubbyDevice.enumerate = function(cb, opt_type) { | 455 HidGnubbyDevice.enumerate = function(cb, opt_type) { |
447 /** | 456 /** |
448 * One pass using getDevices, and one for each of the hardcoded vid/pids. | 457 * One pass using getDevices, and one for each of the hardcoded vid/pids. |
449 * @const | 458 * @const |
(...skipping 28 matching lines...) Expand all Loading... |
478 } | 487 } |
479 } | 488 } |
480 | 489 |
481 // Pass 1: usagePage-based enumeration, for FIDO U2F devices. If non-FIDO | 490 // Pass 1: usagePage-based enumeration, for FIDO U2F devices. If non-FIDO |
482 // devices are asked for, "implement" this pass by providing it the empty | 491 // devices are asked for, "implement" this pass by providing it the empty |
483 // list. (enumerated requires that it's called once per pass.) | 492 // list. (enumerated requires that it's called once per pass.) |
484 var f1d0Filter = {usagePage: 0xf1d0}; | 493 var f1d0Filter = {usagePage: 0xf1d0}; |
485 if (opt_type == GnubbyEnumerationTypes.VID_PID) { | 494 if (opt_type == GnubbyEnumerationTypes.VID_PID) { |
486 enumerated(f1d0Filter, []); | 495 enumerated(f1d0Filter, []); |
487 } else { | 496 } else { |
488 chrome.hid.getDevices({filters: [f1d0Filter]}, | 497 chrome.hid.getDevices( |
489 enumerated.bind(null, f1d0Filter)); | 498 {filters: [f1d0Filter]}, enumerated.bind(null, f1d0Filter)); |
490 } | 499 } |
491 // Pass 2: vid/pid-based enumeration, for legacy devices. If FIDO devices | 500 // Pass 2: vid/pid-based enumeration, for legacy devices. If FIDO devices |
492 // are asked for, "implement" this pass by providing it the empty list. | 501 // are asked for, "implement" this pass by providing it the empty list. |
493 if (opt_type == GnubbyEnumerationTypes.FIDO_U2F) { | 502 if (opt_type == GnubbyEnumerationTypes.FIDO_U2F) { |
494 enumerated(false, []); | 503 enumerated(false, []); |
495 } else { | 504 } else { |
496 for (var i = 0; i < HidGnubbyDevice.HID_VID_PIDS.length; i++) { | 505 for (var i = 0; i < HidGnubbyDevice.HID_VID_PIDS.length; i++) { |
497 var vidPid = HidGnubbyDevice.HID_VID_PIDS[i]; | 506 var vidPid = HidGnubbyDevice.HID_VID_PIDS[i]; |
498 chrome.hid.getDevices({filters: [vidPid]}, enumerated.bind(null, vidPid)); | 507 chrome.hid.getDevices({filters: [vidPid]}, enumerated.bind(null, vidPid)); |
499 } | 508 } |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
545 */ | 554 */ |
546 HidGnubbyDevice.register = function(gnubbies) { | 555 HidGnubbyDevice.register = function(gnubbies) { |
547 var HID_GNUBBY_IMPL = { | 556 var HID_GNUBBY_IMPL = { |
548 isSharedAccess: true, | 557 isSharedAccess: true, |
549 enumerate: HidGnubbyDevice.enumerate, | 558 enumerate: HidGnubbyDevice.enumerate, |
550 deviceToDeviceId: HidGnubbyDevice.deviceToDeviceId, | 559 deviceToDeviceId: HidGnubbyDevice.deviceToDeviceId, |
551 open: HidGnubbyDevice.open | 560 open: HidGnubbyDevice.open |
552 }; | 561 }; |
553 gnubbies.registerNamespace(HidGnubbyDevice.NAMESPACE, HID_GNUBBY_IMPL); | 562 gnubbies.registerNamespace(HidGnubbyDevice.NAMESPACE, HID_GNUBBY_IMPL); |
554 }; | 563 }; |
OLD | NEW |