Overview ======== The HAL introduces the new concept of a "device". A device is a logical representation of a component of hardware. Each active devices is uniquely identified by a constant pointer to a device descriptor. The descriptor is a structure which contains information about the device and a series of entry points to perform usually simple operations on the device. Devices can be provided by the Bootloader, or by RISC OS modules. Devices provided outside the Bootloader are, in principle, hot swappable, although it is up to device drivers using it whether they can support this. Throughout this document, device descriptors are described in terms of C, although the scheme maps naturally to assembler or C++. All device calls use the base ATPCS calling standard (R0-R3 arguments/return values, R4-R11 preserved, R12 corrupted), to permit straightforward use from C or assembler. From C: XXXDevice->Activate(XXXDevice); A simple call to a activate a device from assembler might look like: LDR R0, XXXDevice MOV LR, PC LDR PC, [R0, #DevEntry_Activate] ; R0-R3,R12 corrupted If an assembler device driver module is using a lot of device calls, it might be preferable to move the workspace pointer from the traditional R12 to R11. The device descriptor ===================== The device descriptor starts with a fixed format header, as described below. Following this header are more function pointers providing device-specific calls. struct device { uint16_t type; uint16_t id; uint32_t location; uint32_t version; const char *description; void *address; uint32_t reserved[3]; bool (*Activate)(struct device *); void (*Deactivate)(struct device *); void (*Reset)(struct device *); int32_t (*Sleep)(struct device *, int32_t state); int32_t devicenumber; bool (*TestIRQ)(struct device *); uint32_t reserved[2]; }; struct serial { struct device dev; uint8_t (*ReadByte)(struct serial *); void (*WriteByte)(struct serial *, uint8_t c); // private data } Hence, the first device specific function pointer is offset 32 bytes from the device pointer. Type ---- The type word describes what the device is. Bits 15-8: Top level type (eg video, sound, system peripheral, comms port) Defined values are: 1 = video 2 = sound 3 = system peripheral 4 = comms port Bits 7-0: Second level type (eg VDU display, 16-bit PCM sound output, interrupt controller, UART). Allocated independently within each top level type. This half-word, together with the version number, indicate which device specific calls are available. ID -- 16-bit product code - a unique identifier for this particular device. Location -------- The location describes the location of the device in terms of the bus architecture of the computer. Again, it is grouped by bytes. Bits 31-28: Bus type 0 => processor (0 = core, 1 = coprocessor) 1 => main system bus (0 = AHB, 1 = ASB, 2 = PXBus) 2 => peripheral bus (0 = APB) 3 => expansion bus (0 = Acorn Expansion Card, 1 = ISA, 2 = PCI) 4 => serial bus (0 = AC-Link) Bits 27-24: Bus sub-type (see above) Bits 23-16: Bus number Bits 15-8: Card number (PCI, expansion card etc) / chip select number Bits 7-0: Unit number Version ------- The version describes the version of the device API implemented. It consists of a major version number (bits 31-16) and a minor version number (bits 15-0). A change in major version number indicates an incompatible change of API. An increase in the minor version number indicates backwards-compatible extensions to the API (eg extra functions). Description ----------- A textual description of the device. This should be English, human-readable and Basic Latin (characters &20-&7E). Descriptors along the lines of those output by *Podules are expected. For example: National Semiconductor 16C550 UART Philips ISP1160 USB host controller Acorn IOMD21 PS/2 keyboard Acorn VIDC20 Intel SA-1110 DMA controller Address ------- This field may either be 0, or may be a pointer to the base address of the memory-mapped registers of the device in question. Drivers should not normally use this field to directly poke the device. If they do, they must be absolutely certain that there is no other way to achieve the effect, and that the device type word is known to them. What exactly the address points to depends on the exact device in question. Activate -------- A device driver must call the Activate entry point before using a device. A success/failure indication is returned: 1 indicates successful activation, 0 indicates unsuccessful. Devices may ignore activate/deactivate calls, count them, or may alternatively provide full locking to allow only one activation. Typically this would be called by in a driver's module initialisation routine. Alternatively, it might be called just before a DeviceFS device is opened for access. Deactivate ---------- A device driver must call the Deactivate entry point when it has finished using a device. Reset ----- The Kernel will call the Reset entry point of every device on the system before performing a software reset (eg OS_Reset or Ctrl-Break), after it has issued Service_PreReset. All devices must enter a quiescent state. Sleep ----- This call reads or sets a device's power-down state. If the second parameter is -1, then the current state is returned; otherwise the second parameter must be a value in the range 0-255 giving sleepiness (0 = full power, 255 = off) and the old sleepiness is returned. Note that the value returned does not have to match the last value programmed: for example, if a device cannot power down, it will always return 0. DeviceNumber ------------ If this is -1, then the device has no associated interrupt. Otherwise, bits 0-30 give the device number and bit 31 flags that the device vector is shared, ie this is the R0 that should be passed to OS_ClaimDeviceVector. If bit 31 is set then the TestIRQ routine must be used to determine whether the vector should be claimed or passed on. TestIRQ ------- Returns 0 if the device is not interrupting, or 1 if the device is interrupting. When DeviceNumber is -1, this must be a null pointer. Creation and removal of devices =============================== Devices are declared by calling the HAL->OS call OS_AddDevice or SWI OS_Hardware 2. SWI OS_Hardware 2 (SWI &7A) --------------------------- On entry: R0 -> device descriptor R8 = 2 On exit: All registers preserved void OS_AddDevice(uint32_t flags, struct device *d); void HAL_InitDevices(uint32_t flags); Declare a new device to the system. OS_AddDevice must not be called until HAL_InitDevices is called. Devices are removed by calling OS_Hardware 3. There is no HAL->OS equivalent. SWI OS_Hardware 3 (SWI &7A) --------------------------- On entry: R0 -> device descriptor R8 = 3 On exit: All registers preserved The Kernel tracks all present devices, issuing service calls as devices come and go, and providing a call to enumerate devices of a particular type. SWI OS_Hardware 4 (SWI &7A) --------------------------- On entry: R0 bits 0-15 = type to match bits 16-31 = maximum major version number to match R1 = 0 to start an enumeration, else preserved from last call R8 = 4 On exit: R1 = -1 if there are no (more) devices of this type R2 -> device descriptor (undefined if R1 = -1) Other registers preserved Service_Hardware (Service Call &D9) ------------------------------------ On entry: R0 bits 0-7 = sub-reason code, bits 8-31 flags (undefined, ignore) R1 = reason code (&D9) R2 -> device On exit: Depends on sub-reason code Sub-reason code 0: Device added On exit: All registers must be preserved Sub-reason code 1: Device being removed On exit: R1 = 0 => we object to device being removed R0 -> error block other registers must be preserved else all registers must be preserved