Cross-platform Node.js library for USB device communication with both legacy and WebUSB-compatible APIs.
—
Interface and endpoint management provides methods to claim USB interfaces and manage data endpoints for bulk and interrupt transfers. This includes interface claiming/releasing, alternate setting management, and endpoint access.
Access and manage USB interfaces on a device.
/**
* Return the interface with the specified interface number
* The device must be open to use this method
* @param addr - Interface number (default: 0)
* @returns Interface object
*/
interface(addr: number): Interface;
/**
* USB Interface class for device communication
*/
interface Interface {
/** Integer interface number */
interfaceNumber: number;
/** Integer alternate setting number */
altSetting: number;
/** Object with fields from the interface descriptor */
descriptor: InterfaceDescriptor;
/** List of endpoints on this interface: InEndpoint and OutEndpoint objects */
endpoints: Endpoint[];
}Usage Examples:
import { findByIds } from 'usb';
const device = findByIds(0x1234, 0x5678);
if (device) {
device.open();
// Get the first interface (interface 0)
const interface0 = device.interface(0);
console.log(`Interface Number: ${interface0.interfaceNumber}`);
console.log(`Alternate Setting: ${interface0.altSetting}`);
console.log(`Interface Class: ${interface0.descriptor.bInterfaceClass}`);
console.log(`Number of Endpoints: ${interface0.endpoints.length}`);
// Access interface descriptor properties
const desc = interface0.descriptor;
console.log('Interface Descriptor:');
console.log(` Class: ${desc.bInterfaceClass}`);
console.log(` Subclass: ${desc.bInterfaceSubClass}`);
console.log(` Protocol: ${desc.bInterfaceProtocol}`);
console.log(` Endpoints: ${desc.bNumEndpoints}`);
device.close();
}Claim interfaces for exclusive access and release them when done.
/**
* Claims the interface. This method must be called before using any endpoints of this interface.
* The device must be open to use this method.
*/
claim(): void;
/**
* Releases the interface and resets the alternate setting. Calls callback when complete.
* It is an error to release an interface with pending transfers.
* @param callback - Completion callback
*/
release(callback?: (error?: LibUSBException) => void): void;
/**
* Releases the interface with optional endpoint stream control
* @param closeEndpoints - If true, any active endpoint streams are stopped
* @param callback - Completion callback
*/
release(closeEndpoints?: boolean, callback?: (error?: LibUSBException) => void): void;
/**
* Async version of release
*/
releaseAsync(): Promise<void>;Usage Examples:
import { findByIds } from 'usb';
const device = findByIds(0x1234, 0x5678);
if (device) {
device.open();
const interface0 = device.interface(0);
try {
// Claim the interface for exclusive access
interface0.claim();
console.log('Interface claimed successfully');
// Now you can use the interface endpoints
console.log(`Interface has ${interface0.endpoints.length} endpoints`);
// Use endpoints here...
// Release the interface when done (callback version)
interface0.release((error) => {
if (error) {
console.error('Failed to release interface:', error.message);
} else {
console.log('Interface released successfully');
}
device.close();
});
} catch (error) {
console.error('Failed to claim interface:', error);
device.close();
}
}
// Using async/await pattern
async function useInterfaceAsync() {
const device = findByIds(0x1234, 0x5678);
if (!device) return;
device.open();
const interface0 = device.interface(0);
try {
interface0.claim();
console.log('Interface claimed');
// Use interface...
// Release with async
await interface0.releaseAsync();
console.log('Interface released');
} catch (error) {
console.error('Interface error:', error);
} finally {
device.close();
}
}
// Release with endpoint stream closure
const deviceWithStreams = findByIds(0x5678, 0x1234);
if (deviceWithStreams) {
deviceWithStreams.open();
const interface0 = deviceWithStreams.interface(0);
interface0.claim();
// ... start some endpoint streams/polling ...
// Release and stop all endpoint streams
interface0.release(true, (error) => {
if (!error) {
console.log('Interface released and all endpoint streams stopped');
}
deviceWithStreams.close();
});
}Manage alternate interface settings.
/**
* Sets the alternate setting. It updates the interface.endpoints array
* to reflect the endpoints found in the alternate setting.
* The device must be open to use this method.
* @param altSetting - Alternate setting number
* @param callback - Completion callback
*/
setAltSetting(altSetting: number, callback?: (error?: LibUSBException) => void): void;
/**
* Async version of setAltSetting
* @param alternateSetting - Alternate setting number
*/
setAltSettingAsync(alternateSetting: number): Promise<void>;Usage Examples:
import { findByIds } from 'usb';
const device = findByIds(0x1234, 0x5678);
if (device) {
device.open();
const interface0 = device.interface(0);
interface0.claim();
console.log(`Current alternate setting: ${interface0.altSetting}`);
console.log(`Current endpoints: ${interface0.endpoints.length}`);
// Set alternate setting 1
interface0.setAltSetting(1, (error) => {
if (error) {
console.error('Failed to set alternate setting:', error.message);
return;
}
console.log(`New alternate setting: ${interface0.altSetting}`);
console.log(`New endpoints: ${interface0.endpoints.length}`);
// The endpoints array has been updated to reflect the new setting
interface0.endpoints.forEach((endpoint, index) => {
console.log(` Endpoint ${index}: 0x${endpoint.address.toString(16)}, ${endpoint.direction}`);
});
interface0.release(() => device.close());
});
}
// Using async/await
async function setAltSettingAsync() {
const device = findByIds(0x1234, 0x5678);
if (!device) return;
device.open();
const interface0 = device.interface(0);
try {
interface0.claim();
// Set alternate setting using async method
await interface0.setAltSettingAsync(2);
console.log('Alternate setting 2 activated');
await interface0.releaseAsync();
} catch (error) {
console.error('Error setting alternate setting:', error);
} finally {
device.close();
}
}Manage kernel driver attachment/detachment for interfaces.
/**
* Returns false if a kernel driver is not active; true if active.
* The device must be open to use this method.
*/
isKernelDriverActive(): boolean;
/**
* Detaches the kernel driver from the interface.
* The device must be open to use this method.
*/
detachKernelDriver(): void;
/**
* Re-attaches the kernel driver for the interface.
* The device must be open to use this method.
*/
attachKernelDriver(): void;Usage Examples:
import { findByIds } from 'usb';
const device = findByIds(0x1234, 0x5678);
if (device) {
device.open();
const interface0 = device.interface(0);
// Check if kernel driver is active
if (interface0.isKernelDriverActive()) {
console.log('Kernel driver is active, need to detach');
try {
// Detach kernel driver
interface0.detachKernelDriver();
console.log('Kernel driver detached');
// Now we can claim the interface
interface0.claim();
console.log('Interface claimed after detaching kernel driver');
// Use interface...
// Release interface
interface0.release((error) => {
if (!error) {
// Reattach kernel driver
try {
interface0.attachKernelDriver();
console.log('Kernel driver reattached');
} catch (attachError) {
console.warn('Could not reattach kernel driver:', attachError);
}
}
device.close();
});
} catch (error) {
console.error('Failed to detach kernel driver:', error);
device.close();
}
} else {
console.log('No kernel driver active');
interface0.claim();
// ... use interface ...
interface0.release(() => device.close());
}
}
// Safe kernel driver management pattern
function safeInterfaceAccess(device: Device, interfaceNumber: number) {
const interface0 = device.interface(interfaceNumber);
let kernelDriverWasActive = false;
try {
// Check and detach kernel driver if needed
if (interface0.isKernelDriverActive()) {
kernelDriverWasActive = true;
interface0.detachKernelDriver();
console.log('Kernel driver detached');
}
// Claim interface
interface0.claim();
console.log('Interface claimed');
return {
interface: interface0,
cleanup: (callback?: () => void) => {
interface0.release((error) => {
if (!error && kernelDriverWasActive) {
try {
interface0.attachKernelDriver();
console.log('Kernel driver reattached');
} catch (attachError) {
console.warn('Could not reattach kernel driver');
}
}
if (callback) callback();
});
}
};
} catch (error) {
console.error('Failed to access interface:', error);
throw error;
}
}Access and manage endpoints on interfaces.
/**
* Return the InEndpoint or OutEndpoint with the specified address.
* The device must be open to use this method.
* @param addr - Endpoint address
* @returns Endpoint object or undefined if not found
*/
endpoint(addr: number): Endpoint | undefined;
/**
* Base endpoint interface
*/
interface Endpoint {
/** Endpoint address */
address: number;
/** Endpoint direction: "in" or "out" */
direction: 'in' | 'out';
/** Endpoint type: BULK, INTERRUPT, etc. */
transferType: number;
/** Sets the timeout in milliseconds for transfers on this endpoint */
timeout: number;
/** Object with fields from the endpoint descriptor */
descriptor: EndpointDescriptor;
}Usage Examples:
import { findByIds, LIBUSB_TRANSFER_TYPE_BULK, LIBUSB_TRANSFER_TYPE_INTERRUPT } from 'usb';
const device = findByIds(0x1234, 0x5678);
if (device) {
device.open();
const interface0 = device.interface(0);
interface0.claim();
// List all endpoints
console.log('Available endpoints:');
interface0.endpoints.forEach((endpoint, index) => {
console.log(` Endpoint ${index}:`);
console.log(` Address: 0x${endpoint.address.toString(16)}`);
console.log(` Direction: ${endpoint.direction}`);
console.log(` Type: ${endpoint.transferType === LIBUSB_TRANSFER_TYPE_BULK ? 'BULK' :
endpoint.transferType === LIBUSB_TRANSFER_TYPE_INTERRUPT ? 'INTERRUPT' : 'OTHER'}`);
console.log(` Max Packet Size: ${endpoint.descriptor.wMaxPacketSize}`);
});
// Find specific endpoint by address
const endpoint1 = interface0.endpoint(0x81); // IN endpoint 1
if (endpoint1) {
console.log(`Found IN endpoint: 0x${endpoint1.address.toString(16)}`);
console.log(`Direction: ${endpoint1.direction}`);
// Configure endpoint timeout
endpoint1.timeout = 5000; // 5 second timeout
console.log(`Set endpoint timeout to ${endpoint1.timeout}ms`);
}
// Find OUT endpoint
const outEndpoint = interface0.endpoint(0x02); // OUT endpoint 2
if (outEndpoint) {
console.log(`Found OUT endpoint: 0x${outEndpoint.address.toString(16)}`);
console.log(`Max packet size: ${outEndpoint.descriptor.wMaxPacketSize}`);
}
// Find endpoints by direction
const inEndpoints = interface0.endpoints.filter(ep => ep.direction === 'in');
const outEndpoints = interface0.endpoints.filter(ep => ep.direction === 'out');
console.log(`Found ${inEndpoints.length} IN endpoints and ${outEndpoints.length} OUT endpoints`);
interface0.release(() => device.close());
}
// Helper function to find endpoints by type
function findEndpointsByType(interface0: Interface, transferType: number) {
return interface0.endpoints.filter(endpoint => endpoint.transferType === transferType);
}
// Usage
const device2 = findByIds(0x5678, 0x1234);
if (device2) {
device2.open();
const interface0 = device2.interface(0);
interface0.claim();
// Find all bulk endpoints
const bulkEndpoints = findEndpointsByType(interface0, LIBUSB_TRANSFER_TYPE_BULK);
console.log(`Found ${bulkEndpoints.length} bulk endpoints`);
// Find all interrupt endpoints
const interruptEndpoints = findEndpointsByType(interface0, LIBUSB_TRANSFER_TYPE_INTERRUPT);
console.log(`Found ${interruptEndpoints.length} interrupt endpoints`);
interface0.release(() => device2.close());
}Install with Tessl CLI
npx tessl i tessl/npm-usb