Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 32 additions & 2 deletions lib/usb.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const VENDOR_DEVICE_LIST = [
{ vid: 0x413c, pid: 0x8143 }, // Broadcom BCM20702A0
{ vid: 0x0a12, pid: 0x0001 }, // CSR
{ vid: 0x0b05, pid: 0x17cb }, // ASUS BT400
{ vid: 0x0b05, pid: 0x190e, resetCompleteFallback: true }, // ASUS USB-BT500 (Realtek RTL8761B)
{ vid: 0x8087, pid: 0x07da }, // Intel 6235
{ vid: 0x8087, pid: 0x07dc }, // Intel 7260
{ vid: 0x8087, pid: 0x0a2a }, // Intel 7265
Expand All @@ -32,6 +33,7 @@ const VENDOR_DEVICE_LIST = [

function BluetoothHciSocket () {
this._isUp = false;
this._resetCompleteFallback = false;

this._hciEventEndpointBuffer = Buffer.alloc(0);
this._aclDataInEndpointBuffer = Buffer.alloc(0);
Expand Down Expand Up @@ -74,6 +76,8 @@ BluetoothHciSocket.prototype.bindUser = function (devId, params) {
throw new Error('No compatible USB Bluetooth 4.0 device found!');
}

this._resetCompleteFallback = this._needsResetCompleteFallback(this._usbDevice);

this._usbDevice.open();

this._usbDeviceInterface = this._usbDevice.interfaces[0];
Expand Down Expand Up @@ -155,6 +159,20 @@ BluetoothHciSocket.prototype._findUsbDevice = function (devId, usbParams) {
}
};

BluetoothHciSocket.prototype._needsResetCompleteFallback = function (usbDevice) {
const deviceDescriptor = usbDevice && usbDevice.deviceDescriptor;

if (!deviceDescriptor) {
return false;
}

return VENDOR_DEVICE_LIST.some(device => {
return device.resetCompleteFallback === true &&
deviceDescriptor.idVendor === device.vid &&
deviceDescriptor.idProduct === device.pid;
});
};

BluetoothHciSocket.prototype.getDeviceList = function () {
return getDeviceList()
.filter(dev => {
Expand Down Expand Up @@ -275,8 +293,20 @@ BluetoothHciSocket.prototype.onHciEventEndpointData = function (data) {
]));
}

if (this._mode === 'raw' && buf.length === 6 && (buf.toString('hex') === '0e0401030c00' || buf.toString('hex') === '0e0402030c00')) {
debug('reset complete');
const isStandardResetComplete = buf.length === 6 &&
(buf.toString('hex') === '0e0401030c00' || buf.toString('hex') === '0e0402030c00');
const isFallbackResetComplete = !this._isUp &&
this._resetCompleteFallback &&
buf.length >= 6 &&
buf.readUInt8(0) === 0x0e &&
buf.readUInt8(5) === 0x00;

if (this._mode === 'raw' && (isStandardResetComplete || isFallbackResetComplete)) {
if (isFallbackResetComplete && !isStandardResetComplete) {
debug('reset complete (ASUS USB-BT500 fallback)');
} else {
debug('reset complete');
}
this._isUp = true;
this.emit('state', this._isUp);
}
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@
"semantic-release": "semantic-release",
"pretest": "npm run rebuild",
"rebuild": "node-gyp rebuild",
"test": "jshint lib/*.js && node test.js"
"test": "jshint lib/*.js && node test.js && mocha \"test/**/*.test.js\""
},
"jshintConfig": {
"esversion": 6
Expand Down
63 changes: 63 additions & 0 deletions test/usb.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
const assert = require('assert');

const BluetoothHciSocket = require('../lib/usb');

function createRawSocket (resetCompleteFallback) {
const socket = new BluetoothHciSocket();
const states = [];

socket._mode = 'raw';
socket._resetCompleteFallback = resetCompleteFallback;
socket.on('state', state => states.push(state));

return { socket, states };
}

describe('USB reset completion', function () {
it('enables the fallback only for the ASUS BT500', function () {
const socket = new BluetoothHciSocket();

assert.strictEqual(socket._needsResetCompleteFallback({
deviceDescriptor: { idVendor: 0x0b05, idProduct: 0x190e }
}), true);
assert.strictEqual(socket._needsResetCompleteFallback({
deviceDescriptor: { idVendor: 0x0b05, idProduct: 0x17cb }
}), false);
});

it('accepts the standard reset response for every adapter', function () {
const { socket, states } = createRawSocket(false);

socket.onHciEventEndpointData(Buffer.from('0e0401030c00', 'hex'));

assert.strictEqual(socket.isDevUp(), true);
assert.deepStrictEqual(states, [true]);
});

it('accepts a successful non-standard Command Complete for the ASUS BT500', function () {
const { socket, states } = createRawSocket(true);

socket.onHciEventEndpointData(Buffer.from('0e0401011000', 'hex'));

assert.strictEqual(socket.isDevUp(), true);
assert.deepStrictEqual(states, [true]);
});

it('ignores a non-reset Command Complete for other adapters', function () {
const { socket, states } = createRawSocket(false);

socket.onHciEventEndpointData(Buffer.from('0e0401011000', 'hex'));

assert.strictEqual(socket.isDevUp(), false);
assert.deepStrictEqual(states, []);
});

it('ignores a failed Command Complete for the ASUS BT500', function () {
const { socket, states } = createRawSocket(true);

socket.onHciEventEndpointData(Buffer.from('0e0401011001', 'hex'));

assert.strictEqual(socket.isDevUp(), false);
assert.deepStrictEqual(states, []);
});
});
Loading