<iohw.h>

<iohw.h>

[added with TR18015 and TR18037]


ioindex_t · ioreg

ioand · ioandbuf · ioandbufl · ioandl · iogroup_acquire · iogroup_map · iogroup_release · ioor · ioorbuf · ioorbufl · ioorl · iord · iordbuf · iordbufl · iordl · iowr · iowrbuf · iowrbufl · iowrl · ioxor · ioxorbuf · ioxorbufl · ioxorl


Include the added header <iohw.h> so that you can write low-level I/O hardware drivers in C that are easier to port to different architectures.

Note that the use of this header does not require the additions to the C language mandated by TR18037, which include fixed-point arithmetic and named address spaces.

    /* TYPES */
typedef i-type ioindex_t;
typedef i-type ioreg;

    /* FUNCTIONS (all masked by macros) */
unsigned int iord(ioreg dev);
unsigned long iordl(ioreg dev);
unsigned int iordbuf(ioreg dev, ioindex_t idx);
unsigned long iordbufl(ioreg dev, ioindex_t idx);

void iowr(ioreg dev, unsigned int val);
void iowrl(ioreg dev, unsigned int val);
void iowrbuf(ioreg dev, ioindex_t idx, unsigned int val);
void iowrbufl(ioreg dev, ioindex_t idx, unsigned int val);

void ioor(ioreg dev, unsigned int val);
void ioorl(ioreg dev, unsigned int val);
void ioorbuf(ioreg dev, ioindex_t idx, unsigned int val);
void ioorbufl(ioreg dev, ioindex_t idx, unsigned int val);

void ioand(ioreg dev, unsigned int val);
void ioandl(ioreg dev, unsigned int val);
void ioandbuf(ioreg dev, ioindex_t idx, unsigned int val);
void ioandbufl(ioreg dev, ioindex_t idx, unsigned int val);

void ioxor(ioreg dev, unsigned int val);
void ioxorl(ioreg dev, unsigned int val);
void ioxorbuf(ioreg dev, ioindex_t idx, unsigned int val);
void ioxorbufl(ioreg dev, ioindex_t idx, unsigned int val);

void iogroup_acquire(int group);
void iogroup_release(int group);
void iogroup_map(int group, int direct);

The header <iohw.h> defines two types and a number of functions, all of which are typically masked as macros. You should view this header as a prototype for defining the atomic operations needed to express a low-level I/O hardware driver (thus the root name iohw) that is intended to be reasonably portable C. The facilities in this header are structured around a few basic concepts:

  • The type ioreg describes the space of all I/O addresses. These can be port addresses, for processors with port I/O instructions, or memory addresses, for processors with memory-mapped I/O hardware. In a simpler implementation, the actual argument corresponding to the parameter name dev can also be used to construct the name of a function to call.
  • The type ioindex_t describes an integer type that can be used to index into a hardware buffer, an array of I/O addresses.
  • An argument named group describes the space of all hardware groups, which might be meaningful on an architecture that supports switching among groups of similar I/O addresses by changing a base address dynamically. In a simpler implementation, the actual argument corresponding to the parameter name dev can also be used to construct the name of a function to call.

The function names are thus suggestive of specific I/O operations, though they have no required semantics:

  • iord reads a port and returns as the value of the function.
  • iowr writes val to a port.
  • ioor ORs val into a port (bitwise inclusive OR).
  • ioand ANDs val into a port (bitwise AND).
  • ioxor XORs val into a port (bitwise exclusive OR).

Moreover:

  • The suffix buf performs the operation with the element idx of a buffer.
  • The suffix l (lowercase L) takes the type of the port as unsigned long instead of unsigned int.

Similarly, functions whose name begins with iogroup_ operate on hardware groups:

  • The suffix acquire establishes group as the active hardware group.
  • The suffix release disestablished group as the active hardware group.
  • The suffix map maps the dynamic group into the actual hardware group direct.

In this implementation, all functions are masked by macros that follow the pattern:

#define _IOHW_CAT(x, y)    x##_##y   /* expand arguments and paste */
#define iordbuf(dev, idx)  _IOHW_CAT(dev, brd)(idx)

Thus, the first argument (after macro expansion) is pasted onto a suitable suffix to produce the name of the actual function to call. So you can write code such as:

#define KBD         kbd   /* root name of keyboard functions */
#define KBD_STATUS  0     /* first of two adjacent ports */
#define KBD_DATA    1     /* second of two adjacent ports */
#define KBD_DONE    0x80  /* DONE status bit */

extern unsigned int kbd_brd(ioindex_t idx);  /* actual driver */

unsigned int getkbd()
    {   /* read keyboard when ready */
    while ((iordbuf(KBD, KBD_STATUS) & KBD_DONE) == 0)
        ;   /* wait until character is present */
    return (iordbuf(KBD, KBD_DATA));  /* read character and clear DONE */
    }

All actual driver calls will be to the function (or macro) kbd_brd.


See also the Table of Contents and the Index.

Copyright © 1992-2006 by P.J. Plauger. All rights reserved.

Last modified: 2013-12-21



Got questions about leaving a comment? Get answers from our Disqus FAQ.

comments powered by Disqus