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
                          eg. timer TCR0 on XScale coprocessor CP6
                     1 => main system bus
                          eg. DMA controller on OMAP3
                     2 => peripheral bus
                          eg. on chip UART on iMx6
                     3 => expansion bus
                          eg. UART inside the southbridge over PCI
                     4 => serial bus
                          eg. audio codec fed by a southbridge over PCI
   Bits 27-24: Bus sub-type (see Hdr:HALDevice for definitions)
   Bits 23-16: Bus number
   Bits 15-8:  Card number (PCI, expansion card etc) / chip select number
   Bits 7-0:   Unit number

The bus type fields are broadly ordered by access cost. Therefore peripherals directly attached to the ARM core are first, then on-chip buses typically at the same clock speed as the caches, then low bandwidth on chip peripheral buses, off chip expansion via circuit board tracks, and lastly the slowest/narrowest serial bus class.

Each step down the heirarchy typically involves a bridge component to translate the bus transactions. The processor bus, for example, has no translation as it is accessed using native ARM instructions.

Note that the field is describing the location of the device being described, which may itself emit another bus type. For example an APB connected IIC controller is on the peripheral bus (not the serial bus).

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.


Workspace
=========

As noted in the overview, a device may be implemented either in the HAL
or supplemented after the kernel starts through RISC OS modules.

The HAL uses the ATPCS assigned static base (sb/r9) to access position
independent data, while traditional modules use the workspace pointer (wp/r12).
Assembler modules use r12 directly, for C modules CMHG arranges this to keep
track of position independent data as r12 may be corrupted across function
calls as the ATPCS uses it as scratch register 'ip'.

When designing device specific extensions to the basic device descriptor, do
not create a dependence on the data pointer that would preclude implementing
the device in the HAL or a RISC OS module.

Each device specific extension takes a
  struct device *
as its first argument. This is the structure which is defined as part of the
client API. You are free to keep other unrelated private data or state in
addition to this structure, typically this would be implemented as:

a) For a HAL based device, the device structure is typically allocated from
   the global HAL workspace, so it is possible to compute the base of that
   workspace (and hence recover sb) by subtraction.
   Alternatively, keep a larger version of the structure internally, and
   keep the original sb in a private area off the end or the one seen by
   the client.
   In both cases the original sb was known at the point the HAL device was
   registered.
b) For a RISC OS module based device, keep private data in global variables
   associated with your module. CMHG will dereference these automatically
   by deriving offsets from the R12 value passed to the CMHG veneers.


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
