DOC HOME SITE MAP MAN PAGES GNU INFO SEARCH PRINT BOOK
 
UDI driver coding basics

udi_cmos child channel operations

The udi_cmos driver acts as a GIO provider, meaning that it waits for requests to read from or write to CMOS RAM from the client side of the GIO communication channel. All the child channel operations defined here were declared to the environment earlier in the code; see ``GIO metalanguage entry points''.

First, we provide the routines to bind and unbind a channel, and respond to general channel events.

   static void
   cmos_gio_bind_req(udi_gio_bind_cb_t *gio_bind_cb)
   {
   	udi_gio_bind_ack(gio_bind_cb, CMOS_DEVSIZE, 0, UDI_OK);
   }
   

static void cmos_gio_unbind_req(udi_gio_bind_cb_t *gio_bind_cb) { udi_gio_unbind_ack(gio_bind_cb); }

static void cmos_child_channel_event(udi_channel_event_cb_t *channel_event_cb) { udi_channel_event_complete(channel_event_cb, UDI_OK); }

The above routines are the driver-side counterparts of the udi_gio_bind_req(3udi) udi_gio_unbind_req(3udi) and udi_channel_event_ind(3udi) environment-side channel operations; cmos_gio_bind_req, for example, is executed in response to a udi_gio_bind_req call from the UDI environment. The routines used by the udi_cmos driver, it should be noted, are quite simple; complex drivers may need to do more (such as saving state information or freeing control blocks) in response to channel bind, unbind, and event indication operations from the UDI environment.

The cmos_gio_bind_req call responds to the environment with a gio_bind_cb control block (used to hold the same structure passed to the driver via udi_gio_bind_req), two arguments indicating the device size, and a status indicator. See udi_gio_bind_ack(3udi).

cmos_gio_unbind_req similarly acknowledges an unbind request by returning udi_gio_unbind_ack(3udi) with an unmodified control block.

The general event-response routine, cmos_child_channel_event, simply acknowledges receipt of an event notification and passes UDI_OK back to the environment.

Next, we define the operation to respond to a transfer request from the UDI environment, which is the way a CMOS read or write is invoked.

   static void cmos_do_read(udi_gio_xfer_cb_t *, udi_ubit8_t);
   static void cmos_do_write(udi_gio_xfer_cb_t *, udi_ubit8_t);
   

static void cmos_gio_xfer_req(udi_gio_xfer_cb_t *gio_xfer_cb) { udi_gio_rw_params_t *rw_params = gio_xfer_cb->tr_params;

/* * We can ignore the timeout parameter since all of our * operations are synchronous. */

switch (gio_xfer_cb->op) { case UDI_GIO_OP_READ: udi_assert(rw_params->offset_hi == 0 && rw_params->offset_lo <= CMOS_DEVSIZE && gio_xfer_cb->data_buf->buf_size <= CMOS_DEVSIZE - rw_params->offset_lo); cmos_do_read(gio_xfer_cb, rw_params->offset_lo); break; case UDI_GIO_OP_WRITE: udi_assert(rw_params->offset_hi == 0 && rw_params->offset_lo <= CMOS_DEVSIZE && gio_xfer_cb->data_buf->buf_size <= CMOS_DEVSIZE - rw_params->offset_lo); cmos_do_write(gio_xfer_cb, rw_params->offset_lo); break; default: udi_gio_xfer_nak(gio_xfer_cb, UDI_STAT_NOT_UNDERSTOOD); } }

This routine is the driver-side counterpart to the ,LR udi_gio_xfer_req 3udi environment-side channel operation. Essentially, once the udi_cmos driver is loaded and bound, it waits for a udi_gio_xfer_req call from the environment.

The control block type used here is a standard UDI type (see udi_gio_xfer_cb_t(3udi)), and was declared previously in the control block initialization vector of the driver code (see ``Control block indexes and structures''). The gio_xfer_cb structure used by cmos_gio_xfer_req is filled in by the UDI environment prior to invocation of udi_gio_xfer_req.

First, cmos_gio_xfer_req sets a udi_gio_rw_params_t(3udi) structure to the contents of the tr_params element of the gio_xfer_cb structure (set by the environment). The format of the tr_params element was set indirectly by the driver code's cmos_gio_bind_req call (see above), by specifying a non-zero device_size_lo to udi_gio_bind_ack(3udi). See udi_gio_rw_params_t(3udi).

Next, cmos_gio_xfer_req issues a call to the appropriate subroutine (cmos_do_read or cmos_do_write), based on the value of gio_xfer_cb->op passed to it from the environment. The driver only expects two generic operation types (UDI_GIO_OP_READ and UDI_GIO_OP_WRITE) from the environment (see udi_gio_op_t 3udi ).

Any other operation type code casues a udi_gio_xfer_nak(3udi) to be sent indicating the operation was not understood (see Common Status Codes for a list of status codes and their meanings).

In both the read and write cases, the driver code provides an example of using udi_assert(3udi) to check the consistency of the data being passed to the read or write operation. The udi_assert calls shown check to see that the offset_hi and offset_lo parameters being passed match what was set previously in the driver's call to udi_gio_bind_ack(3udi) (see cmos_gio_bind_ack, defined above). If this assertion returns ``false'', then the UDI environment is expected to abort the execution of the driver instance.

Now, we define the read routine:

   static void cmos_do_read_2(udi_cb_t *, udi_buf_t *, udi_status_t, udi_ubit16_t);
   

static void cmos_do_read(udi_gio_xfer_cb_t *gio_xfer_cb, udi_ubit8_t addr) { cmos_gio_xfer_scratch_t *gio_xfer_scratch = gio_xfer_cb->gcb.scratch; cmos_region_data_t *rdata = UDI_GCB(gio_xfer_cb)->context;

/* * Store address into first byte of scratch space, * so the trans list can get at it. */ gio_xfer_scratch->addr = addr; gio_xfer_scratch->count = gio_xfer_cb->data_buf->buf_size;

udi_pio_trans(cmos_do_read_2, UDI_GCB(gio_xfer_cb), rdata->trans_read, 0, gio_xfer_cb->data_buf, NULL); }

Invoked when the environment executes a udi_gio_xfer_req operation with gio_xfer_cb->op set to UDI_GIO_OP_READ (see above), cmos_do_read sets up a gio_xfer_scratch structure and ititializes it to the scratch structure in the gio_xfer_cb control block passed from the UDI environment (via cmos_gio_xfer_req). The cmos_gio_xfer_scratch_t type was declared earlier in the code (see ``Scratch space, offsets, and requirements'').

Region data (rdata) is set to the context found in the control block passed by cmos_do_read.

cmos_do_read then sets the addr and count elements in the scratch space to the address passed from cmos_gio_xfer_req (rw_params->offset_lo) and the buffer size passed from the UDI environment. The declarations from ``Scratch space, offsets, and requirements'' and ``Programmed I/O (PIO)'' let the environment know to use this scratch space in the PIO transactions executed by the udi_pio_trans operation that follows.

Finally, udi_pio_trans(3udi) is called to do the read from CMOS RAM. cmos_do_read_2 is the callback routine (explained below) that is executed by the environment once it's done servicing the call to udi_pio_trans. The gio_xfer_cb control block originating from the environment's call to udi_gio_xfer_req is passed via a call to UDI_GCB(3udi) to convert it to the generic control block udi_cb_t(3udi) type required. The PIO handle is passed via the region data structure (rdata->trans_read; see ``udi_cmos region data structure''). The trans_read PIO transaction handle causes the list of operations specified in the cmos_trans_read[] array (see ``Programmed I/O (PIO)'') to be performed on the device. The 0 value that follows the PIO handle indicates to start the PIO transation list from the beginning. gio_xfer_cb->data_buf is the data buffer originally passed to the driver from the UDI environment, to be used by UDI_PIO_BUF transactions in the cmos_trans_read[] array. (The final NULL argument indicates that there are no UDI_PIO_MEM transactions in the PIO transaction list.)

Once udi_pio_trans finishes the transaction list, it executes the callback function, cmos_do_read_2. (The format of the callback function is defined on udi_pio_trans(3udi).)

   static void
   cmos_do_read_2(
   	udi_cb_t *gcb,
   	udi_buf_t *new_buf,
   	udi_status_t status,
   	udi_ubit16_t result)
   {
   	udi_gio_xfer_cb_t *gio_xfer_cb =
   				UDI_MCB(gcb, udi_gio_xfer_cb_t);
   

/* * The pio trans trans may (and probably will) have handed us a * new buffer. Use that buffer in the udi_gio_xfer_ack. */ gio_xfer_cb->data_buf = new_buf;

udi_gio_xfer_ack(gio_xfer_cb); }

This function simply passes on the control block passed to it (gcb, which contains the initiator's context), after converting it to the appropriate type required by udi_gio_xfer_ack(3udi) and setting the data_buf element to point to new_buf.

Note that any service call or channel operation that uses a callback must return a new buffer pointer in the corresponding callback, as does cmos_do_read_2, since the original buffer passed to the call (in this case, udi_pio_trans) may have been reallocated by the environment. See udi_buf_t(3udi).

The udi_gio_xfer_ack call requires that the data_buf element of the control block passed to it must either be the same as in the original request (in this case, the udi_gio_xfer_req from the environment side of the GIO channel), or a direct ``descendant'' of the original buffer (i.e. a buffer resulting from a chain of one or more service calls that replace the original buffer with a modified version), or NULL. The data_buf element is set to new_buf to meet this requirement.

The status and result parameters are ignored in the callback, which assumes success of the read operation.

The write operation is defined similarly to the read operation, as shown below:

   static void
   cmos_do_write_1(udi_cb_t *, udi_buf_t *, udi_status_t, udi_ubit16_t);
   

static void cmos_do_write(udi_gio_xfer_cb_t *gio_xfer_cb, udi_ubit8_t addr) { cmos_region_data_t *rdata = UDI_GCB(gio_xfer_cb)->context; cmos_gio_xfer_scratch_t *gio_xfer_scratch = gio_xfer_cb->gcb.scratch;

/* * The first CMOS_RDONLY_SZ bytes of this device are not * allowed to be written through this driver. Fail any attempt * to write to these bytes. */ if (addr < CMOS_RDONLY_SZ) { udi_gio_xfer_nak(gio_xfer_cb, UDI_STAT_MISTAKEN_IDENTITY); return; }

/* * Store address into first byte of scratch space, * so the trans list can get at it. The data to write * will be accessed directly from the buffer. */ gio_xfer_scratch->addr = addr; gio_xfer_scratch->count = gio_xfer_cb->data_buf->buf_size;

udi_pio_trans(cmos_do_write_1, UDI_GCB(gio_xfer_cb), rdata->trans_write, 0, gio_xfer_cb->data_buf, NULL); }

Just as the cmos_do_read routine did, cmos_do_write first sets a region data structure to the context passed to it, and prepares a scratch structure by setting it to the contents of the scratch area passed to it (see ``Scratch space, offsets, and requirements'').

The addr argument is checked to make sure it addresses the writeable portion of the device as earlier defined (see ``Programmed I/O (PIO)''). If it addresses the read-only portion of the device, a udi_gio_xfer_nak(3udi) is returned using the gio_xfer_cb control block and a status code of UDI_STAT_MISTAKEN_IDENTITY (see Common Status Codes in the UDI Core Specification for a list of status codes and their meanings).

The scratch data structure's elements are then set to the addr argument and the buf_size element from the gio_xfer_cb argument. As with the read operation, these are used in the PIO transactions executed as part of the udi_pio_trans that follows.

udi_pio_trans(3udi) is called to do the write to CMOS RAM. cmos_do_write_1 is the callback routine (explained below) that is executed by the environment once it's done servicing the call to udi_pio_trans. The gio_xfer_cb control block originating from the environment's call to udi_gio_xfer_req is passed via a call to UDI_GCB(3udi) to convert it to the generic control block udi_cb_t(3udi) type required. The PIO handle is passed via the region data structure (rdata->trans_write; see ``udi_cmos region data structure''). The trans_write PIO transaction handle causes the list of operations specified in the cmos_trans_write[] array (see ``Programmed I/O (PIO)'') to be performed on the device. The 0 value that follows the PIO handle indicates to start the PIO transation list from the beginning. gio_xfer_cb->data_buf is the data buffer originally passed to the driver from the UDI environment, to be used by UDI_PIO_BUF transactions in the cmos_trans_write[] array. Presumably, it contains the data to write to the device. (The final NULL argument indicates that there are no UDI_PIO_MEM transactions in the PIO transaction list.)

Once udi_pio_trans finishes the transaction list, it executes the callback function, cmos_do_write_1. (The format of the callback function is defined on udi_pio_trans(3udi).)

   static void
   cmos_do_write_1(
   	udi_cb_t *gcb,
   	udi_buf_t *new_buf,
   	udi_status_t status,
   	udi_ubit16_t result)
   {
   	udi_gio_xfer_cb_t *gio_xfer_cb =
   				UDI_MCB(gcb, udi_gio_xfer_cb_t);
   

/* We're done with the buffer. Free it. */ udi_buf_free(gio_xfer_cb->data_buf); gio_xfer_cb->data_buf = NULL;

udi_gio_xfer_ack(gio_xfer_cb); }

This function passes on the control block passed to it (gcb, which contains the initiator's context), after converting it to the appropriate type required by udi_gio_xfer_ack(3udi) and freeing the data_buf element in the control block.

The udi_gio_xfer_ack call requires that the data_buf element of the control block passed to it must either be the same as in the original request (in this case, the udi_gio_xfer_req from the environment side of the GIO channel), a direct ``descendant'' of the original buffer (i.e. a buffer resulting from a chain of one or more service calls that replace the original buffer with a modified version), or NULL. The data_buf element is set to NULL to meet this requirement.

The status and result parameters are ignored in the callback, which assumes success of the write operation.


Next topic: udi_cmos Management Agent channel operations
Previous topic: udi_cmos parent channel operations

© 2005 The SCO Group, Inc. All rights reserved.
OpenServer 6 and UnixWare (SVR5) HDK - 19 June 2005