Skip to content

Host drivers

Reusable host-side class drivers - each a Driver subclass opened with Driver.open(vid, pid, transport=...) - plus filesystem helpers used by the mass-storage host.

HID driver

hid

HID (class 0x03) - generic host driver. Auto-binds to HID devices by class code.

Discovers the HID interface, its interrupt IN/OUT endpoints, and the Report descriptor length from the configuration descriptor, then exposes the full HID 1.11 request set: report-descriptor fetch, interrupt IN/OUT reports, GET/SET_REPORT over EP0, and GET/SET_IDLE + GET/SET_PROTOCOL.

HIDDriver

HIDDriver(handle)

Bases: Driver

Source code in usbip/host.py
def __init__(self, handle: Handle):
    self.handle = handle

matches class-attribute instance-attribute

matches = {'bInterfaceClass': 3}

interface property

interface

report_descriptor

report_descriptor(length=None)
Source code in usbip/classes/host/hid.py
def report_descriptor(self, length=None) -> bytes:
    info = self._discover()
    count = length or info["report_desc_len"] or 256
    return self.handle.control(0x81, 0x06, 0x2200, info["iface"], count)

report_map

report_map()

Parse the device's Report descriptor into {(report_type, report_id): HIDReport}. Fetched and cached on first use.

Source code in usbip/classes/host/hid.py
def report_map(self):
    """Parse the device's Report descriptor into {(report_type, report_id):
    HIDReport}. Fetched and cached on first use."""
    if getattr(self, "_report_map", None) is None:
        self._report_map, self._numbered = parse_report_descriptor(self.report_descriptor())
    return self._report_map

input_report_size

input_report_size(report_id=0)

Inferred wire size (bytes) of an Input report - its payload plus the leading report-ID byte when the device uses numbered reports. None if the report descriptor doesn't declare it.

Source code in usbip/classes/host/hid.py
def input_report_size(self, report_id=0):
    """Inferred wire size (bytes) of an Input report - its payload plus the
    leading report-ID byte when the device uses numbered reports. None if the
    report descriptor doesn't declare it."""
    rep = self.report_map().get((REPORT_INPUT, report_id))
    if rep is None:
        return None
    prefix = 1 if self._numbered else 0
    return rep.size + prefix

read_report

read_report(length=None)
Source code in usbip/classes/host/hid.py
def read_report(self, length=None) -> bytes:
    info = self._discover()
    if length is None:  # infer the Input report size
        length = self.input_report_size() or info["in_mps"] or 8
    return self.handle.interrupt_in(info["in_ep"], length)

reports

reports(length=None)
Source code in usbip/classes/host/hid.py
def reports(self, length=None):
    while True:
        yield self.read_report(length)

output_report

output_report(data, report_id=0)
Source code in usbip/classes/host/hid.py
def output_report(self, data, report_id=0):
    info = self._discover()
    if info["out_ep"] is not None:
        return self.handle.interrupt_out(info["out_ep"], bytes(data))
    return self.set_report(REPORT_OUTPUT, report_id, data)

get_report

get_report(report_type=REPORT_INPUT, report_id=0, length=None)
Source code in usbip/classes/host/hid.py
def get_report(self, report_type=REPORT_INPUT, report_id=0, length=None) -> bytes:
    info = self._discover()
    count = length or info["in_mps"] or 64
    return self.handle.control(0xA1, GET_REPORT, (report_type << 8) | report_id, info["iface"], count)

set_report

set_report(report_type, report_id, data)
Source code in usbip/classes/host/hid.py
def set_report(self, report_type, report_id, data):
    info = self._discover()
    return self.handle.control(
        0x21, SET_REPORT, (report_type << 8) | report_id, info["iface"], bytes(data)
    )

get_idle

get_idle(report_id=0)
Source code in usbip/classes/host/hid.py
def get_idle(self, report_id=0) -> int:
    info = self._discover()
    return self.handle.control(0xA1, GET_IDLE, report_id, info["iface"], 1)[0]

set_idle

set_idle(duration=0, report_id=0)
Source code in usbip/classes/host/hid.py
def set_idle(self, duration=0, report_id=0):
    info = self._discover()
    return self.handle.control(0x21, SET_IDLE, (duration << 8) | report_id, info["iface"], b"")

get_protocol

get_protocol()
Source code in usbip/classes/host/hid.py
def get_protocol(self) -> int:
    info = self._discover()
    return self.handle.control(0xA1, GET_PROTOCOL, 0, info["iface"], 1)[0]

set_protocol

set_protocol(protocol)
Source code in usbip/classes/host/hid.py
def set_protocol(self, protocol):
    info = self._discover()
    return self.handle.control(0x21, SET_PROTOCOL, protocol, info["iface"], b"")

HIDReport dataclass

HIDReport(report_type, report_id, fields)

All fields belonging to one report, keyed by (report_type, report_id).

report_type instance-attribute

report_type

report_id instance-attribute

report_id

fields instance-attribute

fields

bits property

bits

size property

size

Payload size in bytes (excludes any leading report-ID byte).

HIDField dataclass

HIDField(report_type, report_id, usage_page, usages, usage_min, usage_max, report_size, report_count, flags, logical_min, logical_max)

One Input/Output/Feature field group declared by a Report descriptor.

report_type instance-attribute

report_type

report_id instance-attribute

report_id

usage_page instance-attribute

usage_page

usages instance-attribute

usages

usage_min instance-attribute

usage_min

usage_max instance-attribute

usage_max

report_size instance-attribute

report_size

report_count instance-attribute

report_count

flags instance-attribute

flags

logical_min instance-attribute

logical_min

logical_max instance-attribute

logical_max

bits property

bits

constant property

constant

CDC-ACM driver

cdc_acm

CDC-ACM - host driver. Auto-binds to the CDC Data interface (class 0x0A).

CDCDriver

CDCDriver(handle)

Bases: Driver

Source code in usbip/host.py
def __init__(self, handle: Handle):
    self.handle = handle

matches class-attribute instance-attribute

matches = {'bInterfaceClass': 10}

write

write(data)
Source code in usbip/classes/host/cdc_acm.py
def write(self, data: bytes):
    return self.handle.bulk_out(0x01, data)

read

read(length=64)
Source code in usbip/classes/host/cdc_acm.py
def read(self, length=64) -> bytes:
    return self.handle.bulk_in(0x81, length)

Mass-storage driver

msc

Mass Storage - host driver: Bulk-Only Transport (CBW/CSW) over two bulk EPs.

Bulk endpoint addresses are discovered from the configuration descriptor (real devices don't all use 0x01/0x81), so this works against any MSC device.

MSCDriver

MSCDriver(handle)

Bases: Driver

Source code in usbip/classes/host/msc.py
def __init__(self, handle):
    super().__init__(handle)
    self._tag = 0
    self.ep_in, self.ep_out = 0x81, 0x01  # sensible defaults
    self._discover_endpoints()

matches class-attribute instance-attribute

matches = {'bInterfaceClass': 8}

max_lun

max_lun()
Source code in usbip/classes/host/msc.py
def max_lun(self) -> int:
    return self.handle.control(0xA1, GET_MAX_LUN, 0, 0, 1)[0]

command_in

command_in(cdb, dlen, lun=0)
Source code in usbip/classes/host/msc.py
def command_in(self, cdb, dlen, lun=0):
    self._cbw(cdb, dlen, True, lun)
    data = (
        self._data_phase(lambda: self.handle.bulk_in(self.ep_in, dlen), self.ep_in)
        if dlen
        else b""
    )
    return data, self._csw()

command_out

command_out(cdb, data=b'', lun=0)
Source code in usbip/classes/host/msc.py
def command_out(self, cdb, data=b"", lun=0):
    self._cbw(cdb, len(data), False, lun)
    if data:
        self._data_phase(lambda: self.handle.bulk_out(self.ep_out, data), self.ep_out)
    return self._csw()

inquiry

inquiry(lun=0)
Source code in usbip/classes/host/msc.py
def inquiry(self, lun=0) -> bytes:
    data, _ = self.command_in([OP_INQUIRY, 0, 0, 0, 36, 0], 36, lun)
    return data

read_capacity

read_capacity(lun=0)
Source code in usbip/classes/host/msc.py
def read_capacity(self, lun=0):
    data, _ = self.command_in([OP_READ_CAPACITY_10] + [0] * 9, 8, lun)
    last = int.from_bytes(data[0:4], "big")
    block_size = int.from_bytes(data[4:8], "big")
    return last + 1, block_size

read_blocks

read_blocks(lba, count, block_size, lun=0)
Source code in usbip/classes/host/msc.py
def read_blocks(self, lba, count, block_size, lun=0) -> bytes:
    cdb = self._cdb10(OP_READ_10, lba, count)
    data, status = self.command_in(cdb, count * block_size, lun)
    if status:
        raise OSError(f"READ(10) failed, status {status}")
    return data

write_blocks

write_blocks(lba, data, block_size, lun=0)
Source code in usbip/classes/host/msc.py
def write_blocks(self, lba, data, block_size, lun=0) -> int:
    count = len(data) // block_size
    cdb = self._cdb10(OP_WRITE_10, lba, count)
    status = self.command_out(cdb, data, lun)
    if status:
        raise OSError(f"WRITE(10) failed, status {status}")
    return count

MTP host

mtp

MTP / PTP - host driver: the bulk container protocol (command -> data -> response) over two bulk endpoints, plus convenience operations.

Small but complete enough to drive the device-side classes/device/mtp.py in tests and to script real MTP devices: open a session, enumerate objects, download /upload files, read object properties. Endpoint addresses are discovered from the configuration descriptor. Mirrors what libmtp does on the wire.

MtpHost

MtpHost(handle)

Bases: Driver

Source code in usbip/classes/host/mtp.py
def __init__(self, handle):
    super().__init__(handle)
    self.ep_in, self.ep_out, self.ep_intr = 0x81, 0x01, 0x82
    self._txid = 0
    self.session = 0
    self._discover_endpoints()

matches class-attribute instance-attribute

matches = {'bInterfaceClass': 6}

session instance-attribute

session = 0

transaction

transaction(op, params=(), data_out=None)

Run command [-> data] -> response. Returns (resp_code, resp_params, data_in).

Source code in usbip/classes/host/mtp.py
def transaction(self, op, params=(), data_out=None):
    """Run command [-> data] -> response. Returns (resp_code, resp_params, data_in)."""
    self._txid += 1
    txid = self._txid
    body = b"".join(struct.pack("<I", param) for param in params)
    self.handle.bulk_out(
        self.ep_out, struct.pack("<IHHI", 12 + len(body), CT_COMMAND, op, txid) + body
    )
    if data_out is not None:
        self.handle.bulk_out(
            self.ep_out, struct.pack("<IHHI", 12 + len(data_out), CT_DATA, op, txid) + data_out
        )
    data_in = None
    while True:
        ctype, code, _, payload = self._recv_container()
        if ctype == CT_DATA:
            data_in = payload
        elif ctype == CT_RESPONSE:
            rparams = [
                int.from_bytes(payload[i : i + 4], "little") for i in range(0, len(payload), 4)
            ]
            return code, rparams, data_in

open_session

open_session(sid=1)
Source code in usbip/classes/host/mtp.py
def open_session(self, sid=1):
    self._ok(OP_OPEN_SESSION, [sid])
    self.session = sid

close_session

close_session()
Source code in usbip/classes/host/mtp.py
def close_session(self):
    self._ok(OP_CLOSE_SESSION)
    self.session = 0

device_info

device_info()
Source code in usbip/classes/host/mtp.py
def device_info(self):
    _, data = self._ok(OP_GET_DEVICE_INFO)
    return _parse_device_info(data)

storage_ids

storage_ids()
Source code in usbip/classes/host/mtp.py
def storage_ids(self):
    _, data = self._ok(OP_GET_STORAGE_IDS)
    return _u32_array(data)

storage_info

storage_info(storage_id)
Source code in usbip/classes/host/mtp.py
def storage_info(self, storage_id):
    _, data = self._ok(OP_GET_STORAGE_INFO, [storage_id])
    return data

num_objects

num_objects(parent=ROOT, storage=ALL, fmt=0)
Source code in usbip/classes/host/mtp.py
def num_objects(self, parent=ROOT, storage=ALL, fmt=0):
    rparams, _ = self._ok(OP_GET_NUM_OBJECTS, [storage, fmt, parent])
    return rparams[0] if rparams else 0

object_handles

object_handles(parent=ROOT, storage=ALL, fmt=0)
Source code in usbip/classes/host/mtp.py
def object_handles(self, parent=ROOT, storage=ALL, fmt=0):
    _, data = self._ok(OP_GET_OBJECT_HANDLES, [storage, fmt, parent])
    return _u32_array(data)

object_info

object_info(handle)
Source code in usbip/classes/host/mtp.py
def object_info(self, handle):
    _, data = self._ok(OP_GET_OBJECT_INFO, [handle])
    return parse_object_info(data)

get_object

get_object(handle)
Source code in usbip/classes/host/mtp.py
def get_object(self, handle):
    _, data = self._ok(OP_GET_OBJECT, [handle])
    return data

get_partial_object

get_partial_object(handle, offset, count)
Source code in usbip/classes/host/mtp.py
def get_partial_object(self, handle, offset, count):
    _rparams, data = self._ok(OP_GET_PARTIAL_OBJECT, [handle, offset, count])
    return data

send_object_info

send_object_info(parent, name, size, fmt=None, storage=65537)
Source code in usbip/classes/host/mtp.py
def send_object_info(self, parent, name, size, fmt=None, storage=0x00010001):
    is_dir = fmt == FMT_ASSOCIATION
    fmt = FMT_ASSOCIATION if is_dir else (fmt or FMT_UNDEFINED)
    info = build_object_info(storage, fmt, size, parent, name, is_dir)
    rparams, _ = self._ok(OP_SEND_OBJECT_INFO, [storage, parent], data_out=info)
    return rparams[2] if len(rparams) >= 3 else 0  # the reserved ObjectHandle

send_object

send_object(data)
Source code in usbip/classes/host/mtp.py
def send_object(self, data):
    self._ok(OP_SEND_OBJECT, [], data_out=data)

create_file

create_file(parent, name, data, storage=65537)
Source code in usbip/classes/host/mtp.py
def create_file(self, parent, name, data, storage=0x00010001):
    handle = self.send_object_info(parent, name, len(data), storage=storage)
    self.send_object(data)
    return handle

make_dir

make_dir(parent, name, storage=65537)
Source code in usbip/classes/host/mtp.py
def make_dir(self, parent, name, storage=0x00010001):
    return self.send_object_info(parent, name, 0, fmt=FMT_ASSOCIATION, storage=storage)

delete_object

delete_object(handle)
Source code in usbip/classes/host/mtp.py
def delete_object(self, handle):
    self._ok(OP_DELETE_OBJECT, [handle, 0])

set_object_prop_value

set_object_prop_value(handle, prop, value)
Source code in usbip/classes/host/mtp.py
def set_object_prop_value(self, handle, prop, value):
    self._ok(OP_SET_OBJECT_PROP_VALUE, [handle, prop], data_out=value)

rename

rename(handle, new_name)
Source code in usbip/classes/host/mtp.py
def rename(self, handle, new_name):
    self.set_object_prop_value(handle, OPC_FILENAME, ptp_str(new_name))

move_object

move_object(handle, parent, storage=65537)
Source code in usbip/classes/host/mtp.py
def move_object(self, handle, parent, storage=0x00010001):
    self._ok(OP_MOVE_OBJECT, [handle, storage, parent])

copy_object

copy_object(handle, parent, storage=65537)
Source code in usbip/classes/host/mtp.py
def copy_object(self, handle, parent, storage=0x00010001):
    rparams, _ = self._ok(OP_COPY_OBJECT, [handle, storage, parent])
    return rparams[0] if rparams else 0

get_partial_object_64

get_partial_object_64(handle, offset, count)
Source code in usbip/classes/host/mtp.py
def get_partial_object_64(self, handle, offset, count):
    _, data = self._ok(
        OP_GET_PARTIAL_OBJECT_64, [handle, offset & 0xFFFFFFFF, offset >> 32, count]
    )
    return data

begin_edit

begin_edit(handle)
Source code in usbip/classes/host/mtp.py
def begin_edit(self, handle):
    self._ok(OP_BEGIN_EDIT_OBJECT, [handle])

end_edit

end_edit(handle)
Source code in usbip/classes/host/mtp.py
def end_edit(self, handle):
    self._ok(OP_END_EDIT_OBJECT, [handle])

truncate_object

truncate_object(handle, size)
Source code in usbip/classes/host/mtp.py
def truncate_object(self, handle, size):
    self._ok(OP_TRUNCATE_OBJECT, [handle, size & 0xFFFFFFFF, size >> 32])

send_partial_object

send_partial_object(handle, offset, data)
Source code in usbip/classes/host/mtp.py
def send_partial_object(self, handle, offset, data):
    rparams, _ = self._ok(
        OP_SEND_PARTIAL_OBJECT,
        [handle, offset & 0xFFFFFFFF, offset >> 32, len(data)],
        data_out=data,
    )
    return rparams[0] if rparams else 0

object_prop_value

object_prop_value(handle, prop)
Source code in usbip/classes/host/mtp.py
def object_prop_value(self, handle, prop):
    _, data = self._ok(OP_GET_OBJECT_PROP_VALUE, [handle, prop])
    return data

device_prop_value

device_prop_value(prop)
Source code in usbip/classes/host/mtp.py
def device_prop_value(self, prop):
    _, data = self._ok(OP_GET_DEVICE_PROP_VALUE, [prop])
    return data

object_prop_list

object_prop_list(handle=ALL, fmt=0, prop=ALL)
Source code in usbip/classes/host/mtp.py
def object_prop_list(self, handle=ALL, fmt=0, prop=ALL):
    _, data = self._ok(OP_GET_OBJECT_PROP_LIST, [handle, fmt, prop, 0, 0])
    return _parse_prop_list(data)

MtpError

MtpError(code)

Bases: IOError

Source code in usbip/classes/host/mtp.py
def __init__(self, code):
    super().__init__(f"MTP response 0x{code:04X}")
    self.code = code

code instance-attribute

code = code

Bluetooth driver

bluetooth

Bluetooth (class 0xE0) - generic host driver for the USBIP BT dongle.

Discovers the HCI/ACL endpoints from the configuration descriptor and exposes the USB Bluetooth transport: send HCI commands on EP0, read HCI events from the interrupt IN, and exchange ACL over the bulk pair. This is what the loopback and cross-language tests drive; a real Bluetooth host stack (BlueZ via the kernel btusb driver) talks to the dongle the same way over the wire.

BluetoothDriver

BluetoothDriver(handle)

Bases: Driver

Source code in usbip/host.py
def __init__(self, handle: Handle):
    self.handle = handle

matches class-attribute instance-attribute

matches = {'bInterfaceClass': BT_CLASS}

send_command

send_command(cmd)

Send a raw HCI command packet (opcode-LE + plen + params) on EP0.

Source code in usbip/classes/host/bluetooth.py
def send_command(self, cmd) -> int:
    """Send a raw HCI command packet (opcode-LE + plen + params) on EP0."""
    return self.handle.control(0x20, 0x00, 0x0000, 0x0000, bytes(cmd))

command

command(opcode, params=b'')

Build and send an HCI command from (opcode, parameters).

Source code in usbip/classes/host/bluetooth.py
def command(self, opcode, params=b"") -> int:
    """Build and send an HCI command from (opcode, parameters)."""
    return self.send_command(struct.pack("<HB", opcode, len(params)) + bytes(params))

recv_event

recv_event(length=257)
Source code in usbip/classes/host/bluetooth.py
def recv_event(self, length=257) -> bytes:
    data = self._discover()
    return self.handle.interrupt_in(data["event_in"], length)

reset

reset()

HCI Reset (0x0C03); returns the Command Complete event.

Source code in usbip/classes/host/bluetooth.py
def reset(self) -> bytes:
    """HCI Reset (0x0C03); returns the Command Complete event."""
    self.command(OP_RESET)
    return self.recv_event()

send_acl

send_acl(pdu)
Source code in usbip/classes/host/bluetooth.py
def send_acl(self, pdu) -> int:
    data = self._discover()
    return self.handle.bulk_out(data["acl_out"], bytes(pdu))

recv_acl

recv_acl(length=1024)
Source code in usbip/classes/host/bluetooth.py
def recv_acl(self, length=1024) -> bytes:
    data = self._discover()
    return self.handle.bulk_in(data["acl_in"], length)

Filesystem helpers (extras)

Utilities the mass-storage / MTP hosts build on: a block-device view, a FAT filesystem reader, and an ISO-9660 reader.

Block device

blockdev

Byte-addressable read view over a block device (e.g. an MSC host driver).

The filesystem readers (fatfs, isofs) work in byte offsets; this adapts that to the LBA/block reads exposed by MSCDriver. view(base) returns a sub-view shifted by a byte offset (used for an MBR partition start).

BlockDevice

BlockDevice(read_blocks, block_size, num_blocks=0, base=0)
Source code in usbip/classes/host/extras/blockdev.py
def __init__(self, read_blocks, block_size, num_blocks=0, base=0):
    self._read_blocks = read_blocks  # callable(lba, count) -> bytes
    self.block_size = block_size
    self.num_blocks = num_blocks
    self.base = base  # byte offset of this view

block_size instance-attribute

block_size = block_size

num_blocks instance-attribute

num_blocks = num_blocks

base instance-attribute

base = base

read

read(offset, length)
Source code in usbip/classes/host/extras/blockdev.py
def read(self, offset, length):
    if length <= 0:
        return b""
    offset += self.base
    first = offset // self.block_size
    last = (offset + length - 1) // self.block_size
    chunk = self._read_blocks(first, last - first + 1)
    start = offset - first * self.block_size
    return chunk[start : start + length]

view

view(base)
Source code in usbip/classes/host/extras/blockdev.py
def view(self, base):
    return BlockDevice(self._read_blocks, self.block_size, self.num_blocks, self.base + base)

from_msc classmethod

from_msc(msc)
Source code in usbip/classes/host/extras/blockdev.py
@classmethod
def from_msc(cls, msc):
    num_blocks, block_size = msc.read_capacity()
    return cls(
        lambda lba, count: msc.read_blocks(lba, count, block_size), block_size, num_blocks
    )

FAT filesystem

fatfs

Read-only FAT12/16/32 reader over a BlockDevice (MBR-aware, VFAT LFN-aware).

Parses the boot sector / BPB, follows cluster chains through the FAT, and reads directory entries including long file names. Lookups are case-insensitive. On an MBR-partitioned disk it locates the first FAT partition automatically.

DirEntry module-attribute

DirEntry = namedtuple('DirEntry', 'name is_dir size first_cluster')

FatFs

FatFs(dev)
Source code in usbip/classes/host/extras/fatfs.py
def __init__(self, dev):
    sec0 = dev.read(0, 512)
    base = 0
    is_bpb = sec0[0x36:0x3A] == b"FAT1" or sec0[0x52:0x57] == b"FAT32"
    if not is_bpb and sec0[510:512] == b"\x55\xaa":
        base = self._first_fat_partition(sec0)
    self.dev = dev.view(base) if base else dev
    self._fat_cache_sec = -1
    self._fat_cache = b""
    self._parse_bpb()

dev instance-attribute

dev = dev.view(base) if base else dev

listdir

listdir(path='/')
Source code in usbip/classes/host/extras/fatfs.py
def listdir(self, path="/"):
    entry = self._find(path) if path.strip("/") else None
    if entry is not None and not entry.is_dir:
        raise NotADirectoryError(path)
    return [child.name for child in self._dir_entries(entry)]

stat

stat(path)
Source code in usbip/classes/host/extras/fatfs.py
def stat(self, path):
    return self._find(path)

read_file

read_file(path)
Source code in usbip/classes/host/extras/fatfs.py
def read_file(self, path):
    entry = self._find(path)
    if entry is None or entry.is_dir:
        raise IsADirectoryError(path)
    data = bytearray()
    for cluster in self._chain(entry.first_cluster):
        data += self._read_cluster(cluster)
        if len(data) >= entry.size:
            break
    return bytes(data[: entry.size])

ISO-9660 filesystem

isofs

Read-only ISO 9660 reader over a BlockDevice (2048-byte logical sectors).

Parses the Primary Volume Descriptor (sector 16), then walks directory records. Strips the ;1 version suffix and matches names case-insensitively. Joliet (the supplementary UCS-2 descriptor) is not parsed - primary names only.

SECTOR module-attribute

SECTOR = 2048

IsoFs

IsoFs(dev)
Source code in usbip/classes/host/extras/isofs.py
def __init__(self, dev):
    self.dev = dev
    pvd = dev.read(16 * SECTOR, SECTOR)
    if pvd[0] != 1 or pvd[1:6] != b"CD001":
        raise ValueError("not an ISO 9660 volume")
    self.root = self._record(pvd, 156)  # root directory record lives in the PVD

dev instance-attribute

dev = dev

root instance-attribute

root = self._record(pvd, 156)

listdir

listdir(path='/')
Source code in usbip/classes/host/extras/isofs.py
def listdir(self, path="/"):
    rec = self._find(path)
    if not rec["is_dir"]:
        raise NotADirectoryError(path)
    return [name for name, _ in self._read_dir(rec)]

read_file

read_file(path)
Source code in usbip/classes/host/extras/isofs.py
def read_file(self, path):
    rec = self._find(path)
    if rec["is_dir"]:
        raise IsADirectoryError(path)
    return self.dev.read(rec["extent"] * SECTOR, rec["size"])