CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-usb

Cross-platform Node.js library for USB device communication with both legacy and WebUSB-compatible APIs.

Pending
Overview
Eval results
Files

constants-errors.mddocs/

Constants and Error Handling

Constants and error handling provide USB constants, error codes, and exception handling for robust USB applications. This includes USB class codes, transfer types, error constants, and comprehensive error handling patterns.

Capabilities

LibUSB Exception Handling

Handle errors and exceptions from libusb operations.

/**
 * LibUSB Exception class for USB operation errors
 */
interface LibUSBException extends Error {
  /** LibUSB error number/code */
  errno: number;
  
  /** Error message */
  message: string;
  
  /** Error name */
  name: string;
}

Usage Examples:

import { findByIds, LibUSBException } from 'usb';

function handleUSBErrors() {
  const device = findByIds(0x1234, 0x5678);
  if (!device) return;
  
  try {
    device.open();
    
    // Attempt control transfer that might fail
    device.controlTransfer(0x80, 0x06, 0x0100, 0x0000, 18, (error, data) => {
      if (error) {
        console.error('Control transfer failed:');
        console.error(`  Error: ${error.message}`);
        console.error(`  LibUSB Error Code: ${error.errno}`);
        console.error(`  Error Name: ${error.name}`);
        
        // Handle specific error types
        handleSpecificError(error);
      } else {
        console.log('Control transfer successful');
      }
    });
    
  } catch (error) {
    console.error('Device operation failed:', error);
    
    if (error instanceof LibUSBException) {
      console.error(`LibUSB Error Code: ${error.errno}`);
      handleSpecificError(error);
    }
  } finally {
    try {
      device.close();
    } catch (closeError) {
      console.warn('Failed to close device:', closeError);
    }
  }
}

function handleSpecificError(error: LibUSBException) {
  switch (error.errno) {
    case LIBUSB_ERROR_TIMEOUT:
      console.error('Operation timed out - device may be unresponsive');
      break;
    case LIBUSB_ERROR_NO_DEVICE:
      console.error('Device was disconnected');
      break;
    case LIBUSB_ERROR_ACCESS:
      console.error('Access denied - check permissions or kernel drivers');
      break;
    case LIBUSB_ERROR_BUSY:
      console.error('Resource is busy - device may be in use by another process');
      break;
    case LIBUSB_ERROR_NOT_SUPPORTED:
      console.error('Operation not supported by this device or platform');
      break;
    case LIBUSB_ERROR_PIPE:
      console.error('Pipe error - endpoint may be stalled');
      break;
    default:
      console.error(`Unhandled LibUSB error: ${error.errno}`);
  }
}

USB Error Constants

Standard libusb error codes for error handling.

/**
 * LibUSB Error Constants
 */

/** Input/output error */
const LIBUSB_ERROR_IO: number;

/** Invalid parameter */
const LIBUSB_ERROR_INVALID_PARAM: number;

/** Access denied (insufficient permissions) */
const LIBUSB_ERROR_ACCESS: number;

/** No such device (it may have been disconnected) */
const LIBUSB_ERROR_NO_DEVICE: number;

/** Entity not found */
const LIBUSB_ERROR_NOT_FOUND: number;

/** Resource busy */
const LIBUSB_ERROR_BUSY: number;

/** Operation timed out */
const LIBUSB_ERROR_TIMEOUT: number;

/** Overflow */  
const LIBUSB_ERROR_OVERFLOW: number;

/** Pipe error */
const LIBUSB_ERROR_PIPE: number;

/** System call interrupted (perhaps due to signal) */
const LIBUSB_ERROR_INTERRUPTED: number;

/** Insufficient memory */
const LIBUSB_ERROR_NO_MEM: number;

/** Operation not supported or unimplemented on this platform */
const LIBUSB_ERROR_NOT_SUPPORTED: number;

/** Other error */
const LIBUSB_ERROR_OTHER: number;

Usage Examples:

import { 
  findByIds, 
  LIBUSB_ERROR_TIMEOUT,
  LIBUSB_ERROR_NO_DEVICE,
  LIBUSB_ERROR_ACCESS,
  LIBUSB_ERROR_BUSY,
  LIBUSB_ERROR_NOT_SUPPORTED,
  LIBUSB_ERROR_PIPE
} from 'usb';

async function robustDeviceOperation() {
  const device = findByIds(0x1234, 0x5678);
  if (!device) {
    throw new Error('Device not found');
  }
  
  let attempts = 0;
  const maxAttempts = 3;
  
  while (attempts < maxAttempts) {
    try {
      device.open();
      
      // Perform operation with timeout handling
      const data = await performTransferWithRetry(device);
      console.log('Operation successful:', data);
      
      device.close();
      return data;
      
    } catch (error) {
      attempts++;
      console.error(`Attempt ${attempts} failed:`, error.message);
      
      if (error.errno) {
        switch (error.errno) {
          case LIBUSB_ERROR_TIMEOUT:
            console.log('Timeout - retrying with longer timeout');
            if (attempts < maxAttempts) {
              await sleep(1000 * attempts); // Progressive delay
              continue;
            }
            break;
            
          case LIBUSB_ERROR_NO_DEVICE:
            console.error('Device disconnected - cannot retry');
            throw error;
            
          case LIBUSB_ERROR_ACCESS:
            console.error('Access denied - check permissions');
            throw error;
            
          case LIBUSB_ERROR_BUSY:
            console.log('Device busy - waiting before retry');
            if (attempts < maxAttempts) {
              await sleep(2000);
              continue;
            }
            break;
            
          case LIBUSB_ERROR_PIPE:
            console.log('Pipe error - attempting to clear halt');
            try {
              // Clear halt and retry
              await clearEndpointHalt(device);
              continue;
            } catch (clearError) {
              console.error('Failed to clear halt:', clearError);
            }
            break;
            
          default:
            console.error('Unrecoverable error');
            throw error;
        }
      }
      
      if (attempts >= maxAttempts) {
        throw new Error(`Operation failed after ${maxAttempts} attempts`);
      }
    }
  }
}

function sleep(ms: number): Promise<void> {
  return new Promise(resolve => setTimeout(resolve, ms));
}

USB Class Constants

USB device and interface class codes.

/**
 * USB Class Constants
 */

/** Each interface specifies its own class information */
const LIBUSB_CLASS_PER_INTERFACE: number;

/** Audio class */
const LIBUSB_CLASS_AUDIO: number;

/** Communications class */
const LIBUSB_CLASS_COMM: number;

/** Human Interface Device class */
const LIBUSB_CLASS_HID: number;

/** Printer class */
const LIBUSB_CLASS_PRINTER: number;

/** Image class */
const LIBUSB_CLASS_PTP: number;

/** Mass storage class */
const LIBUSB_CLASS_MASS_STORAGE: number;

/** Hub class */
const LIBUSB_CLASS_HUB: number;

/** Data class */
const LIBUSB_CLASS_DATA: number;

/** Wireless class */
const LIBUSB_CLASS_WIRELESS: number;

/** Application class */
const LIBUSB_CLASS_APPLICATION: number;

/** Class is vendor-specific */
const LIBUSB_CLASS_VENDOR_SPEC: number;

Usage Examples:

import { 
  getDeviceList,
  LIBUSB_CLASS_HID,
  LIBUSB_CLASS_MASS_STORAGE,
  LIBUSB_CLASS_AUDIO,
  LIBUSB_CLASS_HUB,
  LIBUSB_CLASS_VENDOR_SPEC
} from 'usb';

function categorizeDevicesByClass() {
  const devices = getDeviceList();
  const devicesByClass: { [key: string]: Device[] } = {};
  
  devices.forEach(device => {
    const deviceClass = device.deviceDescriptor.bDeviceClass;
    const className = getClassNameFromCode(deviceClass);
    
    if (!devicesByClass[className]) {
      devicesByClass[className] = [];
    }
    
    devicesByClass[className].push(device);
  });
  
  // Display results
  Object.entries(devicesByClass).forEach(([className, devices]) => {
    console.log(`${className} devices: ${devices.length}`);
    devices.forEach(device => {
      console.log(`  VID:PID = ${device.deviceDescriptor.idVendor.toString(16)}:${device.deviceDescriptor.idProduct.toString(16)}`);
    });
  });
  
  return devicesByClass;
}

function getClassNameFromCode(classCode: number): string {
  switch (classCode) {
    case LIBUSB_CLASS_AUDIO:
      return 'Audio';
    case LIBUSB_CLASS_COMM:
      return 'Communications';
    case LIBUSB_CLASS_HID:
      return 'HID';
    case LIBUSB_CLASS_PRINTER:
      return 'Printer';
    case LIBUSB_CLASS_PTP:
      return 'Image/PTP';
    case LIBUSB_CLASS_MASS_STORAGE:
      return 'Mass Storage';
    case LIBUSB_CLASS_HUB:
      return 'Hub';
    case LIBUSB_CLASS_DATA:
      return 'Data';
    case LIBUSB_CLASS_WIRELESS:
      return 'Wireless';
    case LIBUSB_CLASS_APPLICATION:
      return 'Application';
    case LIBUSB_CLASS_VENDOR_SPEC:
      return 'Vendor Specific';
    case LIBUSB_CLASS_PER_INTERFACE:
      return 'Per Interface';
    default:
      return 'Unknown';
  }
}

// Find specific device types
function findHIDDevices() {
  const devices = getDeviceList();
  return devices.filter(device => 
    device.deviceDescriptor.bDeviceClass === LIBUSB_CLASS_HID
  );
}

function findMassStorageDevices() {
  const devices = getDeviceList();
  return devices.filter(device =>
    device.deviceDescriptor.bDeviceClass === LIBUSB_CLASS_MASS_STORAGE
  );
}

Transfer Type Constants

USB transfer type constants for endpoint configuration.

/**
 * Transfer Type Constants
 */

/** Control endpoint */
const LIBUSB_TRANSFER_TYPE_CONTROL: number;

/** Isochronous endpoint */
const LIBUSB_TRANSFER_TYPE_ISOCHRONOUS: number;

/** Bulk endpoint */
const LIBUSB_TRANSFER_TYPE_BULK: number;

/** Interrupt endpoint */
const LIBUSB_TRANSFER_TYPE_INTERRUPT: number;

Usage Examples:

import { 
  findByIds,
  LIBUSB_TRANSFER_TYPE_CONTROL,
  LIBUSB_TRANSFER_TYPE_BULK,
  LIBUSB_TRANSFER_TYPE_INTERRUPT,
  LIBUSB_TRANSFER_TYPE_ISOCHRONOUS
} from 'usb';

function analyzeEndpointTypes() {
  const device = findByIds(0x1234, 0x5678);
  if (!device) return;
  
  device.open();
  
  try {
    const interface0 = device.interface(0);
    interface0.claim();
    
    console.log('Endpoint Analysis:');
    interface0.endpoints.forEach((endpoint, index) => {
      const transferType = endpoint.transferType;
      const typeName = getTransferTypeName(transferType);
      
      console.log(`  Endpoint ${index} (0x${endpoint.address.toString(16)}):`);
      console.log(`    Transfer Type: ${typeName}`);
      console.log(`    Direction: ${endpoint.direction}`);
      console.log(`    Max Packet Size: ${endpoint.descriptor.wMaxPacketSize}`);
      
      // Provide recommendations based on transfer type
      switch (transferType) {
        case LIBUSB_TRANSFER_TYPE_BULK:
          console.log(`    Recommended for: Large data transfers, file transfers`);
          console.log(`    Characteristics: Reliable, error correction, no guaranteed timing`);
          break;
          
        case LIBUSB_TRANSFER_TYPE_INTERRUPT:
          console.log(`    Recommended for: Small, periodic data, HID reports`);
          console.log(`    Characteristics: Guaranteed timing, error correction`);
          console.log(`    Polling interval: ${endpoint.descriptor.bInterval}ms`);
          break;
          
        case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS:
          console.log(`    Recommended for: Audio/video streaming`);
          console.log(`    Characteristics: Guaranteed timing, no error correction`);
          break;
          
        case LIBUSB_TRANSFER_TYPE_CONTROL:
          console.log(`    Recommended for: Device configuration, commands`);
          console.log(`    Characteristics: Reliable, structured format`);
          break;
      }
    });
    
    interface0.release(() => {});
  } finally {
    device.close();
  }
}

function getTransferTypeName(transferType: number): string {
  switch (transferType) {
    case LIBUSB_TRANSFER_TYPE_CONTROL:
      return 'Control';
    case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS:
      return 'Isochronous';
    case LIBUSB_TRANSFER_TYPE_BULK:
      return 'Bulk';
    case LIBUSB_TRANSFER_TYPE_INTERRUPT:
      return 'Interrupt';
    default:
      return 'Unknown';
  }
}

Transfer Status Constants

USB transfer completion status codes.

/**
 * Transfer Status Constants
 */

/** Transfer completed without error */
const LIBUSB_TRANSFER_COMPLETED: number;

/** Transfer failed */
const LIBUSB_TRANSFER_ERROR: number;

/** Transfer timed out */
const LIBUSB_TRANSFER_TIMED_OUT: number;

/** Transfer was cancelled */
const LIBUSB_TRANSFER_CANCELLED: number;

/** Halt condition detected (endpoint stalled) or control request not supported */
const LIBUSB_TRANSFER_STALL: number;

/** Device was disconnected */
const LIBUSB_TRANSFER_NO_DEVICE: number;

/** Device sent more data than requested */
const LIBUSB_TRANSFER_OVERFLOW: number;

Usage Examples:

import { 
  findByIds,
  LIBUSB_TRANSFER_COMPLETED,
  LIBUSB_TRANSFER_ERROR,
  LIBUSB_TRANSFER_TIMED_OUT,
  LIBUSB_TRANSFER_CANCELLED,
  LIBUSB_TRANSFER_STALL,
  LIBUSB_TRANSFER_NO_DEVICE,
  LIBUSB_TRANSFER_OVERFLOW
} from 'usb';

function handleTransferStatus() {
  const device = findByIds(0x1234, 0x5678);
  if (!device) return;
  
  device.open();
  
  try {
    const interface0 = device.interface(0);
    interface0.claim();
    
    const inEndpoint = interface0.endpoints.find(ep => ep.direction === 'in');
    if (inEndpoint) {
      // Create transfer with detailed status handling
      const transfer = inEndpoint.makeTransfer(5000, (error, buffer, actualLength) => {
        if (error) {
          console.error('Transfer failed:', error.message);
          console.error('LibUSB Error Code:', error.errno);
          
          // Handle transfer-specific errors
          switch (error.errno) {
            case LIBUSB_TRANSFER_TIMED_OUT:
              console.error('Transfer timed out - device may be slow or unresponsive');
              break;
              
            case LIBUSB_TRANSFER_STALL:
              console.error('Endpoint stalled - may need to clear halt');
              // Clear halt and retry
              inEndpoint.clearHalt((clearError) => {
                if (!clearError) {
                  console.log('Halt cleared, can retry transfer');
                }
              });
              break;
              
            case LIBUSB_TRANSFER_NO_DEVICE:
              console.error('Device disconnected during transfer');
              break;
              
            case LIBUSB_TRANSFER_OVERFLOW:
              console.error('Device sent more data than expected');
              break;
              
            case LIBUSB_TRANSFER_CANCELLED:
              console.log('Transfer was cancelled');
              break;
              
            default:
              console.error('Unknown transfer error');
          }
        } else {
          console.log(`Transfer completed successfully: ${actualLength} bytes`);
          
          if (actualLength < buffer.length) {
            console.log('Short transfer - received less data than buffer size');
          }
          
          console.log('Data:', buffer.slice(0, actualLength).toString('hex'));
        }
      });
      
      // Submit transfer
      const buffer = Buffer.alloc(64);
      transfer.submit(buffer);
      
      // Cancel transfer after 2 seconds for demonstration
      setTimeout(() => {
        const cancelled = transfer.cancel();
        if (cancelled) {
          console.log('Transfer cancellation requested');
        }
      }, 2000);
    }
    
    interface0.release(() => {});
  } finally {
    device.close();
  }
}

Endpoint Direction Constants

Endpoint direction constants for transfer operations.

/**
 * Endpoint Direction Constants
 */

/** IN: device-to-host */
const LIBUSB_ENDPOINT_IN: number;

/** OUT: host-to-device */
const LIBUSB_ENDPOINT_OUT: number;

Usage Examples:

import { 
  findByIds,
  LIBUSB_ENDPOINT_IN,
  LIBUSB_ENDPOINT_OUT
} from 'usb';

function demonstrateEndpointDirections() {
  const device = findByIds(0x1234, 0x5678);
  if (!device) return;
  
  device.open();
  
  try {
    const interface0 = device.interface(0);
    interface0.claim();
    
    // 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`);
    
    // Demonstrate control transfer with direction
    const bmRequestTypeIn = LIBUSB_ENDPOINT_IN | 0x00 | 0x80; // Standard, device, IN
    device.controlTransfer(bmRequestTypeIn, 0x06, 0x0100, 0x0000, 18, (error, data) => {
      if (!error && data) {
        console.log('IN control transfer successful');
      }
    });
    
    const bmRequestTypeOut = LIBUSB_ENDPOINT_OUT | 0x00 | 0x00; // Standard, device, OUT
    const outData = Buffer.from([0x01, 0x02, 0x03]);
    device.controlTransfer(bmRequestTypeOut, 0x09, 0x0001, 0x0000, outData, (error, bytesWritten) => {
      if (!error) {
        console.log('OUT control transfer successful');
      }
    });
    
    interface0.release(() => {});
  } finally {
    device.close();
  }
}

// Helper function to determine endpoint direction from address
function getEndpointDirection(endpointAddress: number): 'in' | 'out' {
  return (endpointAddress & LIBUSB_ENDPOINT_IN) ? 'in' : 'out';
}

// Create endpoint address with direction
function createEndpointAddress(endpointNumber: number, direction: 'in' | 'out'): number {
  const directionBit = direction === 'in' ? LIBUSB_ENDPOINT_IN : LIBUSB_ENDPOINT_OUT;
  return (endpointNumber & 0x7F) | directionBit;
}

Request Type Constants

USB request type constants for control transfers.

/**
 * Request Type Constants
 */

/** Standard request */
const LIBUSB_REQUEST_TYPE_STANDARD: number;

/** Class-specific request */
const LIBUSB_REQUEST_TYPE_CLASS: number;

/** Vendor-specific request */
const LIBUSB_REQUEST_TYPE_VENDOR: number;

/** Reserved request type */
const LIBUSB_REQUEST_TYPE_RESERVED: number;

/**
 * Request Recipient Constants
 */

/** Device recipient */
const LIBUSB_RECIPIENT_DEVICE: number;

/** Interface recipient */
const LIBUSB_RECIPIENT_INTERFACE: number;

/** Endpoint recipient */
const LIBUSB_RECIPIENT_ENDPOINT: number;

/** Other recipient */
const LIBUSB_RECIPIENT_OTHER: number;

Usage Examples:

import { 
  findByIds,
  LIBUSB_ENDPOINT_IN,
  LIBUSB_ENDPOINT_OUT,
  LIBUSB_REQUEST_TYPE_STANDARD,
  LIBUSB_REQUEST_TYPE_CLASS,
  LIBUSB_REQUEST_TYPE_VENDOR,
  LIBUSB_RECIPIENT_DEVICE,
  LIBUSB_RECIPIENT_INTERFACE,
  LIBUSB_RECIPIENT_ENDPOINT
} from 'usb';

function demonstrateControlTransferTypes() {
  const device = findByIds(0x1234, 0x5678);
  if (!device) return;
  
  device.open();
  
  try {
    // Standard device request - Get device descriptor
    const standardRequest = LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_STANDARD | LIBUSB_RECIPIENT_DEVICE;
    device.controlTransfer(standardRequest, 0x06, 0x0100, 0x0000, 18, (error, data) => {
      if (!error && data) {
        console.log('Standard device request successful');
        console.log('Device descriptor received:', data.length, 'bytes');
      }
    });
    
    // Vendor-specific request
    const vendorRequest = LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE;
    device.controlTransfer(vendorRequest, 0x01, 0x0000, 0x0000, 64, (error, data) => {
      if (!error && data) {
        console.log('Vendor-specific request successful');
      } else if (error) {
        console.log('Vendor request not supported (expected for most devices)');
      }
    });
    
    // Class-specific interface request (example for HID)
    const classRequest = LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE;
    device.controlTransfer(classRequest, 0x01, 0x0000, 0x0000, 8, (error, data) => {
      if (!error && data) {
        console.log('Class-specific interface request successful');
      } else if (error) {
        console.log('Class request not applicable for this device');
      }
    });
    
    // Endpoint-specific request - Clear halt
    const endpointRequest = LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_STANDARD | LIBUSB_RECIPIENT_ENDPOINT;
    device.controlTransfer(endpointRequest, 0x01, 0x0000, 0x0081, 0, (error) => {
      if (!error) {
        console.log('Clear halt request successful');
      } else {
        console.log('Clear halt not needed or failed');
      }
    });
    
  } finally {
    device.close();
  }
}

// Helper function to build request type byte
function buildRequestType(direction: 'in' | 'out', type: 'standard' | 'class' | 'vendor', recipient: 'device' | 'interface' | 'endpoint' | 'other'): number {
  let requestType = 0;
  
  // Direction
  if (direction === 'in') {
    requestType |= LIBUSB_ENDPOINT_IN;
  }
  
  // Type
  switch (type) {
    case 'standard':
      requestType |= LIBUSB_REQUEST_TYPE_STANDARD;
      break;
    case 'class':
      requestType |= LIBUSB_REQUEST_TYPE_CLASS;
      break;
    case 'vendor':
      requestType |= LIBUSB_REQUEST_TYPE_VENDOR;
      break;
  }
  
  // Recipient
  switch (recipient) {
    case 'device':
      requestType |= LIBUSB_RECIPIENT_DEVICE;
      break;
    case 'interface':
      requestType |= LIBUSB_RECIPIENT_INTERFACE;
      break;
    case 'endpoint':
      requestType |= LIBUSB_RECIPIENT_ENDPOINT;
      break;
    case 'other':
      requestType |= LIBUSB_RECIPIENT_OTHER;
      break;
  }
  
  return requestType;
}

// Usage example
function sendVendorCommand() {
  const device = findByIds(0x1234, 0x5678);
  if (!device) return;
  
  device.open();
  
  try {
    // Build vendor-specific device OUT request
    const requestType = buildRequestType('out', 'vendor', 'device');
    const commandData = Buffer.from([0x12, 0x34, 0x56, 0x78]);
    
    device.controlTransfer(requestType, 0x42, 0x1234, 0x0000, commandData, (error, bytesWritten) => {
      if (!error) {
        console.log(`Vendor command sent: ${bytesWritten} bytes`);
      } else {
        console.error('Vendor command failed:', error.message);
      }
    });
    
  } finally {
    device.close();
  }
}

Install with Tessl CLI

npx tessl i tessl/npm-usb

docs

constants-errors.md

data-transfers.md

descriptors.md

device-communication.md

device-management.md

event-handling.md

index.md

interfaces-endpoints.md

webusb-api.md

tile.json