|
|
|
|
 |
Extended BlockDriver specification
|
 |
 |
Version 2.31 - updated 4 September 2023
|
The first 384 bytes of the driver has a fixed format. It allows an
application to check its features, even before actually installing
and using it. It also provides the main entry point through which
the functions of the blockdriver should be called.
The driver code should start at offset 0x0180 (384) and may have
any length. The parent application should check its size before
loading it.
|
In version 2.00 of the BlockDriver speicification, a relocatable
module (RM) has been introduced to make writing a BlockDriver a
lot easier. Although the use of the BlockDriver module is not
mandatory and you are free to write your own routines, it is
highly recommended to register with the module as a bare minimum,
and keep it informated about any statistics. It allows the monitoring
application !Serial to give a relibably view of the status of
your serial ports.
If your serial port driver is properly implemented, it should be
accessible through DeviceFS, which means that the BlockDriver Module
can call most of the functions on your behalf. This means that the
actual BlockDriver should be no more than a simple venier
to the calls of the BlockDriver Module. In addition, the BlockDriver
Module provides some useful calls to check the current hardware
and find the correct device names under which your interface
is known by the OS.
➤ BlockDriver Module specification
|
 |
Main entry point
0x0000 (128 bytes)
|
 |
 |
This is the main entry point for calling the functions supplied
by the BlockDriver. The entry point can be called in usermode or in
supervisor mode. Registers r0-r3 are used to pass parameters
to the routines. The routines may corrupt r0-r3, but should preserve
r4-r12 and used r14 for the return address.
Entry
|
The code to despatch the function should be placed in the first 0x80 bytes of
the driver code. In assembler, it might look like this:
.entry
STMFD R13!,R14
CMP R0,#((table_end - table_start)/4)
ADDCC PC,PC,R0,LSL#2
LDMFD R13!,PC
.table_start
B func_0
B func_1
...
.table_end
|
 |
Driver flags
0x00C4 (4 bytes)
|
 |
 |
32-bit value of which the top byte specifies the highest
port number (pp) available. As the ports are internally numbered from
zero onwards, the higest port number is the number_of_ports-1.
The remaining 24 bits are used for the driver flags.
The flag bits have the following meaning:
|
0
|
|
More than one port available
|
1
|
|
Supports split baudrates
|
2
|
|
Has more than one byte FIFO
|
3
|
|
Can use Driver_ControlLines to generate a break
|
4
|
|
Requires polling (function 19)
|
5
|
|
Won't empty (is a shared buffer)
|
6
|
|
Supports block get/put operations
|
7
|
|
Prefer not to overlap serial IO and disk IO
|
8
|
|
Supports inquiry initialise
|
9
|
|
Supports return of RISC OS TX and RX buffers (function 20 and 21)
|
10
|
|
Device is removable (e.g. USB)
|
11
|
|
Supports device selection based on Location (USB)
|
12
|
|
Supports device selection based on Serial Number (USB, where supported)
|
13-15
|
|
reserved for future expansion
|
16
|
|
Code is 32-bit compliant (bit 16 set)
|
17
|
|
Code is NOT 26-bit compatible (bit 17 set)
|
18
|
|
Allows flags in the top 24 bits of the port number in r1 on entry to 'Driver_Initialise'
|
19
|
|
Code is ARMv7 compliant
|
20-23
|
|
reserved
|
24-31
|
|
Highest port number supported 1 (set to 255 (0xFF) when no ports are available)
|
 |
-
The number of ports available is the highest port number (0-254) + 1.
If no ports are available, the highest port number is set to 255.
This can be the case with the SerialUSB driver, if no serial adapters
are plugged into the USB subsystem.
|
 |
Driver ID
0x00C8 (4 bytes)
|
 |
 |
Unique 32-bit value that identifies the driver, allocated by us.
The lowest byte is always zero, so that it can be used to specify
the port on a particular interface, as part of the
Device_Claim call. For example, if you want to claim port 5
on an II Dual Serial expansion card (0x00000100), you would use
0x00000105. The primary internal port of the computer
uses driver 0 and port 0.
Currently allocated drivers numbers (bits 8-31):
|
0
|
|
Internal serial port(s), i.e. Internal, InternalPC, Internal32 and InternalDirect
|
1
|
|
II Dual Serial podule
|
2
|
|
Serial Port dual serial podule
|
3
|
|
Internal serial (with PC-style cable wiring)
|
4
|
|
Brainsoft Multipod
|
5
|
|
Acorn telnet interface
|
6
|
|
Serial Port dual serial podule (with PC-style cable wiring)
|
7
|
|
SerialUSB 1
|
25
|
|
ModemCard
|
50
|
|
MultiPort serial card
|
100
|
|
Remote econet driver
|
128
|
|
Virtual pipe (end A)
|
129
|
|
Virtual pipe (end B)
|
255
|
|
Dummy
|
-
SerialUSB requires Colin Granville's SerialUSB module,
which is present in the current BlockDrivers distribution.
|
 |
Speed table
0x0100 (128 bytes)
|
 |
 |
Table with supported baudrates. One word per speed, terminated with a NULL word.
Note that some drivers will only build this table after a
pre-initialisation call. Check bit 8 (supports inquiry initialise)
of the driver flags to see if this is the case.
If the flag is set,
you should call Driver_Initialise with
r1 = -1, before reading the baudrate table.
|
The following function codes can be called from the parent application,
through the main entry point at address 0x0000:
|
-
Added in version 2.00. Check bit 9 of the
driver flags before calling this entry.
-
Added in version 2.10.
-
Added in version 2.30.
|
Send a byte to the serial port. Returns with r0 = 0
if the byte was successfully inserted into the TX buffer,
or -1 if it was not.
Entry
|
r0
|
|
0
|
r1
|
|
Port number
|
r2
|
|
Byte to send
|
r0
|
|
Result code (0 = ok, -1 = error)
|
Get a byte from the serial port. Returns with the byte in r0
when successful, or -1 if it was not.
Entry
|
r0
|
|
Byte (or -1 if no byte available)
|
Send a block of data to the serial port. The call returns with
the number of bytes that have been transferred successfully.
There is no guarantee that all bytes are sent in a single call.
You should call this entry with updated parameters repeatedly,
until all bytes are sent.
Entry
|
r0
|
|
2
|
r1
|
|
Port number
|
r2
|
|
Pointer to block
|
r3
|
|
Number of bytes to send
|
r0
|
|
Number of bytes transferred
|
Get a block of data from the serial port and put them
into the input buffer. There is no guarantee that the
number of bytes requested have actually been transferred,
so you should inspect the value returned in r0 on exit.
Entry
|
r0
|
|
3
|
r1
|
|
Port number
|
r2
|
|
Pointer to buffer
|
r3
|
|
Maximum number of bytes to read (size of buffer)
|
r0
|
|
Number of bytes transferred
|
Check how much space is left in the output buffer.
Entry
|
r0
|
|
Number of bytes free in TX buffer
|
Check how much space is left in the input buffer.
Entry
|
r0
|
|
Number of bytes free in RX buffer
|
Clear the output buffer.
Entry
|
Clear the input buffer.
Entry
|
Set the state of the modem control lines of the RS232 port.
Entry
|
r0
|
|
8
|
r1
|
|
Port number
|
r2
|
|
New settings or -1 to read
|
0
|
|
DTR
|
1
|
|
RTS
|
2
|
|
TX data active (when set), i.e. break state
|
 |
Driver_ReadControlLines
9
|
 |
 |
Read the state of the modem control lines of the RS232 port.
Entry
|
Returns a bitmask of errors seen since last call to this entry.
The bitmask is reset, each time this entry is called.
Entry
|
0
|
|
Overrun error
|
1
|
|
Parity error
|
2
|
|
Framing error
|
3
|
|
Break received
|
Send a so-called break. Note that this call blocks
until the break has been sent. Although some serial ports
might be able to multitask when sending a break, the internal
serial port can't. Check the driver flags
to see of you can use a multitasking break before using this call.
The break time (r2) should be specified in centiseconds.
Entry
|
r0
|
|
11
|
r1
|
|
Port number
|
r2
|
|
Break time [cs]
|
This call is similar to Driver_GetByte, but
leaves the byte in the buffer.
Entry
|
r0
|
|
Byte (or -1 if no byte available)
|
This call allows the speed of the serial port's transmitter
to be set to a particular baudrate. Note that most serial
ports do not support a split baudrate (i.e. a differentb
audrate for RX and TX). Inspect the driver flags to see
if it does. If split baudrates are not supported, setting
either the TX or the RX baudrate, will set both.
Note that the returned speed might be (slightly) different
from the requested speed, as some interfaces return the
actual calculated speed of the device.
Entry
|
r0
|
|
13
|
r1
|
|
Port number
|
r2
|
|
Speed (or -1 to read)
|
This call allows the speed of the serial port's receiver
to be set to a particular baudrate. Note that most serial
ports do not support a split baudrate (i.e. a differentb
audrate for RX and TX). Inspect the driver flags to see
if it does. If split baudrates are not supported, setting
either the TX or the RX baudrate, will set both.
Note that the returned speed might be (slightly) different
from the requested speed, as some interfaces return the
actual calculated speed of the device.
Entry
|
r0
|
|
14
|
r1
|
|
Port number
|
r2
|
|
Speed (or -1 to read)
|
This call sets or reads the bit format of a single data word,
also known as the word format, consisting of the number
of start bits, the number of data bits and the number of stop bits.
It also defines any parity error checking.
Entry
|
r0
|
|
15
|
r1
|
|
Port number
|
r2
|
|
Data format (or -1 to read)
|
0-1
|
|
Data length (0 = 8 bits, 1 = 7 bits, 2 = 6 bits, 3 = 5 bits)
|
2
|
|
Stop bits (0 = 1 stop bit, 1 = 2 stop bits)
|
3
|
|
Use parity (0 = no parity, 1 = parity on)
|
4
|
|
Even parity (0 = odd parity, 1 = even parity)
|
Define what type of handshaking is used. Also known as flow control.
Note that not all serial ports support all types of handshaking. In most
cases, the computer's primary serial port is fully implemented and has all
modem control lines. Additional ports may be less complete. Also note that
DTR/DSR handshaking is not supported by most modern ports.
Entry
|
0
|
|
No flow control
|
1
|
|
RTS/CTS
|
2
|
|
XON/XOFF
|
3
|
|
DTR/DSR 1
|
-
Traditionally, this value was used to set both (i.e. hardware
and software handshaking), In version 2.00 of the BlockDriver
specification this has been changed to DTR/DSR hardware
handshaking, which is supported by the OS.
Note that most hardware does not support this type of
handshaking though.
|
This call serves two purposes. It MUST be called at least once
with a value of -1 in r1, before calling any of the other functions,
to allow the driver to adjust itself to the available hardware,
count ports, enumerate baudrates, cache port names, etc. When called this way, the driver will not
touch any hardware. Bit 8 of the
driver flags will
tell you whether this function is supported.
Do not read the baudrate tables or any of the other driver flags,
until after this call.
The second purpose of this call is to initialise a driver and claim
the port for the host application. This is done by specifying the
port number in r1. Once claimed, the port can generally not be
used by another application, until Driver_CloseDown is called.
Entry
|
r0
|
|
17
|
r1
|
|
Port number and flags 1 (or -1 for pre-initialise call)
|
r2
|
|
Pointer to parameter string (optional)
|
r3
|
|
Magic word (&BD02BD02) to enable r2
|
Port number and flags 1 (r1)
|
0-7
|
|
Port number
|
8
|
|
Device is shared (i.e. may have multiple users)
|
9
|
|
Fake device 2
|
10
|
|
Statistics
|
11
|
|
NoBuffer
|
12
|
|
Removable
|
13
|
|
Location
|
14
|
|
Serno
|
15-31
|
|
Reserved for future expansion
|
r0
|
|
0 when successful (or pointer to error string if not)
|
-
Before specifying flags in this register, you should check the
driver flags to see if this feature is supported.
-
A device should register itself as a Fake device, if it does
not provide an interface through DeviceFS. This means that the
BlockDriver module can not handle the device on your behalf.
In such cases you should full implement your own driver, and
call the module only to keep it informed of any status changes.
|
This entry should be called when the application no longer
needs the serial port. It is typically called when closing
the application, or before claiming another port. Once the
port has been released, it will be available to other applications.
Entry
|
This return code is new in version 2.31 of the BlockDriver specification.
It is used to pass an error code back to the calling application. A
value of '0' means that the call was issued successfully.
Older BlockDrivers will return a value of '18' (the same as r0 on entry),
which should be interpreted by your code as 'no error'.
|
This entry should be called regularly for the driver to perform
polling tasks. This was needed in the past, for example, by the
Econet drivers to check internal buffers and poll any Econet
Protocol tasks. This entry should typically be called from the
main polling loop of the parent task, and may be as infrequent
as three to four time a second. If the driver needs more frequent
polling, it should install a module and use callbacks.
Entry
|
This call can be used to read the handle of the current TX buffer.
Note that this call has first been specified in version 2.00 of the
BlockDriver specification. Check bit 9 of the
driver flags before calling this entry.
Entry
|
r0
|
|
Handle (or -1 when not available)
|
This call can be used to read the handle of the current RX buffer.
Note that this call has first been specified in version 2.00 of the
BlockDriver specification. Check bit 9 of the
driver flags before calling this entry.
Entry
|
r0
|
|
Handle (or -1 when not available)
|
Read information about the device
Entry
|
r0
|
|
22
|
r1
|
|
Port number
|
r2
|
|
Reason code
|
0
|
|
Get physical location (e.g. when using USB)
|
1
|
|
Read device Serial Number (when available)
|
2
|
|
Get DeviceFS name for this interface (when available)
|
3
|
|
Get port description (string)
|
|
|
Any links shown in red are currently unavailable.
© X-Ample Technology BV. Created: Wednesday 26 April 2017. Last changed: Wednesday, 27 September 2023 - 15:53 CET.
|
 |
|
|
|
|