# gizhw:device

the devices the machine has, as the program opens and drives them. Discovery reads sysfs (each class folder of /sys/class: its devices, their node, bus, ids and model); sensors and RAPL energy are readings; the NVIDIA GPU boards are read and controlled through NVML (the driver's management library). A Handle is one open device node: ioctl, mmap, read, write, poll and close through libc, with arguments in native memory. Every class module (npu, v4l2, evdev, bus, tpm, block, rdma, codec) drives its device through a Handle and calls libc through libcCall only here. The CPU's facts (features, caches, NUMA nodes) are gizlang:machine's.

```gizlang
import * as Device from "gizhw:device";
```

## Types

### `Device`

```gizlang
type Device = { name: string; kind: string; path: string; node: string; bus: string; vendor: i64; product: i64; model: string; driver: string; }
```

A device of one class: `name` is its entry in the class folder (`renderD128`, `accel0`, `tpm0`), `kind` the class
(`drm`, `accel`, `tpm`), `path` its sysfs directory, resolved. `node` is its /dev file ("" when it has none), `bus`
the bus it sits on (`pci`, `usb`, `platform`, "" when unknown), `vendor` and `product` its ids (0 when none), `model`
its model text, and `driver` the driver bound to it ("" when none).

- `name`: `string`
- `kind`: `string`
- `path`: `string`
- `node`: `string`
- `bus`: `string`
- `vendor`: `i64`
- `product`: `i64`
- `model`: `string`
- `driver`: `string`

### `Sensor`

```gizlang
type Sensor = { device: string; channel: string; value: i64; }
```

A reading of one hwmon channel: `device` is the hwmon's name (`k10temp`, `nvme`) and `channel` its file
(`temp1_input`). `value` is in the channel's unit: milli-degrees C (temp), rpm (fan), microwatts (power),
millivolts (in) or milliamps (curr).

- `device`: `string`
- `channel`: `string`
- `value`: `i64`

### `Energy`

```gizlang
type Energy = { domain: string; microjoules: i64; }
```

An energy counter of a powercap domain: `domain` is its name (`package-0`) and `microjoules` its count. The count
wraps at the domain's max_energy_range_uj, so a power is the difference of two reads over their time.

- `domain`: `string`
- `microjoules`: `i64`

### `Telemetry`

```gizlang
type Telemetry = { sensors: Vec<Sensor>; energy: Vec<Energy>; gpus: Vec<GpuReading>; }
```

The readings of one moment: the hwmon sensors, the RAPL energy counters and each NVIDIA board's state (none when the
driver's management library is not there).

- `sensors`: `Vec<M7.Sensor>`
- `energy`: `Vec<M7.Energy>`
- `gpus`: `Vec<M7.GpuReading>`

### `Handle`

```gizlang
type Handle
```

An open device node or socket: `fd` is its descriptor, and the addresses of the libc functions it calls.

- `fd`: `i64`

### `GpuBoards`

```gizlang
type GpuBoards
```

The NVIDIA GPUs of the machine (gpuBoards()).

- `count`: `i64`

### `GpuReading`

```gizlang
type GpuReading = { index: i64; name: string; temperature: i64; utilization: i64; memoryUtilization: i64; memoryUsed: i64; memoryTotal: i64; power: f64; powerLimit: f64; powerMin: f64; powerMax: f64; fans: Vec<i64>; smClock: i64; memoryClock: i64; reasons: u64; replays: i64; }
```

One GPU's state: temperature in degrees C, utilization in percent, memory in bytes, power in watts, clocks in MHz.
`fans`: each fan's speed in percent. `reasons`: NVML's clocks event reasons (bits: 4 software power cap, 32 software
thermal, 64 hardware thermal, 128 hardware power brake). `replays`: PCIe replays since the driver loaded.

- `index`: `i64`
- `name`: `string`
- `temperature`: `i64`
- `utilization`: `i64`
- `memoryUtilization`: `i64`
- `memoryUsed`: `i64`
- `memoryTotal`: `i64`
- `power`: `f64`
- `powerLimit`: `f64`
- `powerMin`: `f64`
- `powerMax`: `f64`
- `fans`: `Vec<i64>`
- `smClock`: `i64`
- `memoryClock`: `i64`
- `reasons`: `u64`
- `replays`: `i64`

### `Xid`

```gizlang
type Xid = { index: i64; xid: i64; }
```

An Xid error a GPU reported (a driver or hardware fault; 79: the GPU fell off the bus).

- `index`: `i64`
- `xid`: `i64`

## Functions

### `platform`

```gizlang
function platform(sys: System): string
```

The kernel this program runs on, in lower case ("linux", "darwin"; "unknown" when it does not say): a class module
(camera, audio, input) picks its backend by it.

Effects: fail, io.

### `noBackend`

```gizlang
function noBackend(sys: System, kind: string): string
```

The error of a class module on a platform that has no backend for it: the class and the platform, named.

Effects: fail, io.

### `devices`

```gizlang
function devices(sys: System, kind: string): Vec<Device> throws IoError
```

The devices of class `kind`, a folder of /sys/class (such as "accel", "drm", "tpm", "nvme", "video4linux" or
"sound"), in the order the kernel lists them. A class that the kernel does not have gives none.

Effects: fail, io.

### `sensors`

```gizlang
function sensors(sys: System): Vec<Sensor> throws IoError
```

Every reading of a temperature, fan, power, voltage or current channel that hwmon reports, by hwmon device in the
order the kernel lists them. A channel that cannot be read is skipped.

Effects: fail, io.

### `energy`

```gizlang
function energy(sys: System): Vec<Energy> throws IoError
```

The energy counter of every powercap domain that has one (RAPL, through the powercap interface), in the order the
kernel lists them.

Effects: fail, io.

### `telemetry`

```gizlang
function telemetry(sys: System): Telemetry throws IoError
```

Every reading now. A board that NVML cannot read is left out.

Effects: fail, io.

### `open`

```gizlang
function open(sys: System, path: string, writable: bool): Handle throws IoError
```

Opens device node `path` (a /dev file) read-write when `writable`, read-only otherwise.

Effects: fail, io.

### `openNode`

```gizlang
function openNode(sys: System, path: string, writable: bool): Handle throws string
```

Opens `path` like open, with the failure as text, for a class module that returns string errors.

Effects: fail, io.

### `wrap`

```gizlang
function wrap(sys: System, fd: i64): Handle
```

A handle on the descriptor `fd` that is already open (a socket, or one a library gave).

Effects: fail, io.

### `libcCall`

```gizlang
function libcCall(sys: System, name: string, args: Vec<u64>): u64
```

Calls the C library's function `name` with integer arguments (a socket, a name lookup) and gives its raw result.
Only this module calls libc by name; a class module uses a Handle or this call.

Effects: fail, io.

### `textAt`

```gizlang
function textAt(data: bytes, at: i64, length: i64): string
```

The text of a fixed-size C string field `data[at, at + length)` of a struct, up to its NUL, trimmed.

Effects: fail.

### `cstring`

```gizlang
function cstring(os: Native, text: string): u64
```

A NUL-terminated copy of `text` in native memory, for a C string argument (free it with the native interface).

Effects: fail, io.

### `cstringAt`

```gizlang
function cstringAt(os: Native, address: u64): string
```

The text of the NUL-terminated string at `address` (at most 256 bytes): a C string the library gave.

Effects: fail, io.

### `gpuBoards`

```gizlang
function gpuBoards(sys: System): GpuBoards throws string
```

NVML, set up. An error says when the driver has no NVML or it does not start. Xid errors are watched on each GPU
that allows it (GpuBoards.xids).

Effects: fail, io.

## Methods

### `Device.attribute`

```gizlang
function Device.attribute(self: Device, sys: System, name: string): string throws IoError
```

The text of attribute `name` of this device (a file of its sysfs folder, such as `brightness`, `online` or
`cur_brightness`), trimmed.

Effects: fail, io.

### `Device.setAttribute`

```gizlang
function Device.setAttribute(self: Device, sys: System, name: string, value: string): i64 throws IoError
```

Writes `value` to attribute `name` of this device: the process needs write authority for that folder. Gives the
number of bytes written.

Effects: fail, io.

### `Handle.text`

```gizlang
function Handle.text(self: Handle, at: u64): string
```

The text at `address`, a NUL-terminated C string the library gave.

Effects: fail, io.

### `Handle.alloc`

```gizlang
function Handle.alloc(self: Handle, size: i64): u64
```

Allocates `size` bytes of native memory (free it with free).

Effects: fail, io.

### `Handle.free`

```gizlang
function Handle.free(self: Handle, at: u64): void
```

Frees native memory from alloc.

Effects: fail, io.

### `Handle.poke`

```gizlang
function Handle.poke(self: Handle, at: u64, data: bytes): void
```

Copies `data` into native memory at `at`.

Effects: fail, io.

### `Handle.peek`

```gizlang
function Handle.peek(self: Handle, at: u64, length: i64): bytes
```

The `length` bytes of native memory at `at`.

Effects: fail, io.

### `Handle.peekInto`

```gizlang
function Handle.peekInto(self: Handle, at: u64, into: mut bytes): void
```

The `into.length` bytes of native memory at `at`, copied into `into` with no allocation.

Effects: fail.

### `Handle.flush`

```gizlang
function Handle.flush(self: Handle, at: u64, length: i64): void
```

Writes the CPU cache lines of `length` bytes of a mapping at `at` back to memory and drops them (Native.flush).

Effects: fail, io.

### `Handle.ioctl`

```gizlang
function Handle.ioctl(self: Handle, request: u64, argument: u64): i64 throws string
```

ioctl `request` with `argument`, a native address (or 0): a negative result is a failure. Gives the result.

Effects: fail, io.

### `Handle.exchange`

```gizlang
function Handle.exchange(self: Handle, request: u64, data: bytes): bytes throws string
```

ioctl `request` with `data` as the argument struct: the struct is copied to native memory, the kernel fills it, and
the struct after the call is given.

Effects: fail, io.

### `Handle.readBytes`

```gizlang
function Handle.readBytes(self: Handle, length: i64): bytes throws string
```

Reads up to `length` bytes from the descriptor, waiting for them; gives what was read.

Effects: fail, io.

### `Handle.writeBytes`

```gizlang
function Handle.writeBytes(self: Handle, data: bytes): i64 throws string
```

Writes `data` to the descriptor. Gives the number of bytes written.

Effects: fail, io.

### `Handle.poll`

```gizlang
function Handle.poll(self: Handle, events: i64, timeout: i64): i64 throws string
```

Waits up to `timeout` ms for the descriptor to be ready for `events` (POLLIN 1, POLLOUT 4). Gives the events that
are ready: 0 at a timeout.

Effects: fail, io.

### `Handle.map`

```gizlang
function Handle.map(self: Handle, length: i64, offset: i64, alignment: i64 = 1): u64 throws string
```

Maps `length` bytes of the device at `offset`, shared, readable and writable, at an address that is a multiple of
`alignment` (a power of two; 1 for any). Gives the address.

Effects: fail, io.

### `Handle.unmap`

```gizlang
function Handle.unmap(self: Handle, at: u64, length: i64): void
```

Unmaps `length` bytes from `at` (from map).

Effects: fail, io.

### `Handle.close`

```gizlang
function Handle.close(self: Handle): void
```

Closes the descriptor.

Effects: fail, io.

### `GpuBoards.state`

```gizlang
function GpuBoards.state(self: GpuBoards, i: i64): GpuReading throws string
```

GPU `i`'s state now.

Effects: fail, io.

### `GpuBoards.setPowerLimit`

```gizlang
function GpuBoards.setPowerLimit(self: GpuBoards, i: i64, watts: f64): void throws string
```

Sets GPU `i`'s power limit to `watts` (within GpuReading.powerMin to powerMax; root).

Effects: fail, io.

### `GpuBoards.setFans`

```gizlang
function GpuBoards.setFans(self: GpuBoards, i: i64, percent: i64): void throws string
```

Sets every fan of GPU `i` to `percent` (root).

Effects: fail, io.

### `GpuBoards.defaultFans`

```gizlang
function GpuBoards.defaultFans(self: GpuBoards, i: i64): void throws string
```

Gives every fan of GPU `i` back to the GPU's own (VBIOS) control (root).

Effects: fail, io.

### `GpuBoards.xids`

```gizlang
function GpuBoards.xids(self: GpuBoards): Vec<Xid>
```

The Xid errors reported since the last call, without waiting.

Effects: fail, io.

### `GpuBoards.close`

```gizlang
function GpuBoards.close(self: mut GpuBoards): void
```

Stops NVML.

Effects: fail, io.

