@ginden/blinkstick-v2
Version:
Improved Blickstick API for Node.js
119 lines • 4.6 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.LibUsbTransport = void 0;
const usb_transport_1 = require("./usb-transport");
const node_util_1 = require("node:util");
const tsafe_1 = require("tsafe");
const utils_1 = require("../utils");
function isDeviceOpen(device) {
return Boolean(device.interfaces);
}
/**
* @summary UsbTransport implementation using the 'usb' (libusb) library.
* @category Adapters
*/
class LibUsbTransport extends usb_transport_1.UsbTransport {
constructor(device, minimalDeviceInfo) {
super();
this.device = device;
this.minimalDeviceInfo = minimalDeviceInfo;
this.wIndex = 0;
}
/**
* Creates a new instance of LibUsbTransport from a USB device.
*/
static async buildFromDevice(device) {
const minimimalDeviceInfo = await LibUsbTransport.calculateMinimalDeviceInfo(device);
const iface = device.interfaces?.[0];
(0, tsafe_1.assert)(iface, 'Device must have at least one interface');
if (iface.isKernelDriverActive()) {
iface.detachKernelDriver();
}
iface.claim();
return new LibUsbTransport(device, minimimalDeviceInfo);
}
/**
* Calculates minimal device information from a USB device.
* This includes vendorId, productId, release, manufacturer, product, and serialNumber.
*/
static async calculateMinimalDeviceInfo(device) {
if (isDeviceOpen(device)) {
const desc = device.deviceDescriptor;
const [manufacturer, product, serialNumber] = (await Promise.all([
(0, node_util_1.promisify)((cb) => device.getStringDescriptor(desc.iManufacturer, cb))(),
(0, node_util_1.promisify)((cb) => device.getStringDescriptor(desc.iProduct, cb))(),
(0, node_util_1.promisify)((cb) => device.getStringDescriptor(desc.iSerialNumber, cb))(),
]));
return {
vendorId: desc.idVendor,
productId: desc.idProduct,
release: desc.bcdDevice,
manufacturer,
product,
serialNumber,
};
}
else {
try {
device.open();
return await LibUsbTransport.calculateMinimalDeviceInfo(device);
}
finally {
device.close();
}
}
}
async sendFeatureReport(data) {
const buffer = (0, utils_1.asBuffer)(data);
const reportId = buffer[0]; // HID reports start with reportId
const bmRequestType = 0x20;
const wValue = (3 << 8) | reportId; // 3 = Feature report type
const ret = await new Promise((resolve, reject) => {
this.device.controlTransfer(bmRequestType, 0x09, // bRequest: SET_REPORT
wValue, // wValue: (3=Feature) << 8 | reportId
this.wIndex, // wIndex: interface number
buffer, (err, b) => (err ? reject(err) : resolve(b)));
});
if (typeof ret === 'number') {
return ret; // If the return type is a number, return it directly
}
return ret.length;
}
async getFeatureReport(reportId, reportLength) {
const ret = await new Promise((resolve, reject) => {
this.device.controlTransfer(0xa1, // bmRequestType: Device to host | Class | Interface
0x01, // bRequest: GET_REPORT
(3 << 8) | reportId, // wValue: (3=Feature) << 8 | reportId
this.wIndex, // wIndex: interface number
reportLength, (err, data) => (err ? reject(err) : resolve(data)));
});
if (!Buffer.isBuffer(ret)) {
throw Object.assign(new Error('Expected a Buffer from getFeatureReport'), {
returnValue: ret,
});
}
return ret;
}
getDeviceInfo() {
return this.minimalDeviceInfo;
}
async close() {
return new Promise((resolve, reject) => {
try {
this.device.interfaces?.forEach((iface) => {
if (iface.isKernelDriverActive()) {
iface.detachKernelDriver();
}
iface.release(true, () => { }); // release w/ callback to avoid warning
});
this.device.close();
resolve();
}
catch (err) {
reject(err);
}
});
}
}
exports.LibUsbTransport = LibUsbTransport;
//# sourceMappingURL=lib-usb-transport.js.map