|
|
First, the udi_cmos code defines the channel operations for the channel between the primary region and the bus bridge. This channel and its operations vectors were defined previously under ``Bus bridge metalanguage entry points''.
static void cmos_parent_channel_event(udi_channel_event_cb_t *channel_event_cb) { udi_bus_bind_cb_t *bus_bind_cb; switch (channel_event_cb->event) { case UDI_CHANNEL_BOUND:bus_bind_cb = UDI_MCB(channel_event_cb-> params.parent_bound.bind_cb, udi_bus_bind_cb_t);
/* Keep a link back to the channel event CB for the ack. */ UDI_GCB(bus_bind_cb)->initiator_context = channel_event_cb;
/* Bind to the parent bus bridge driver. */ udi_bus_bind_req(bus_bind_cb); break; default: udi_channel_event_complete(channel_event_cb, UDI_OK); } }
The cmos_parent_channel_event operation responds to an event at the other end of the bus bridge channel; the bus bridge mapper generates a call to the cmos_parent_channel_event entry point, with an appropriately filled udi_channel_event_cb_t structure defining the event.
If the channel_event_cb->event
element is UDI_CHANNEL_BOUND,
The routine first converts the channel_event_cb->params.bound.bind_cb
structure to the type required for the udi_bus_bind_req call later in
the routine.
It does this using the UDI_MCB macro call defined in
Control Block Management
in the Core Specification.
A pointer to the channel_event_cb is saved so it can be accessed by the cmos_bus_bind_ack operation. Then, a binding back to the bus bridge is requested using udi_bus_bind_req and the converted bus_bind_cb control block (see above). See udi_bus_bind_req(3udi), and udi_bus_bind_cb_t(3udi).
Otherwise, if channel_event_cb->event
is not
UDI_CHANNEL_BOUND, then
a call to udi_channel_event_complete indicates to the bridge
mapper that the event was processed successfully (see
Inter-module Communication
in the Core Specification for the definition of
udi_channel_event_complete and udi_channel_event_cb_t).
The bridge mapper responds to the udi_bus_bind_req call with a call to the cmos_bus_bind_ack channel operation, listed below.
static void cmos_bus_bind_ack_1(udi_cb_t *, udi_pio_handle_t);static void cmos_bus_bind_ack( udi_bus_bind_cb_t *bus_bind_cb, udi_dma_constraints_t constraints, udi_ubit8_t preferred_endianness, udi_status_t status) { cmos_region_data_t *rdata = bus_bind_cb->gcb.context; udi_channel_event_cb_t *channel_event_cb = bus_bind_cb->gcb.initiator_context;
/* * Don't need to do anything with preferred_endianness, since * our device doesn't do DMA. Even if it did, * preferred_endianness is only used with bi-endianness devices * that can change endianness. */ udi_dma_constraints_free(constraints);
/* * Save the bus bind control block for unbinding later. */ rdata->bus_bind_cb = bus_bind_cb;
if (status != UDI_OK) { udi_channel_event_complete( channel_event_cb, UDI_STAT_CANNOT_BIND); return; }
/* * Now we have access to our hardware. Set up the PIO mappings * we'll need later. */ udi_pio_map(cmos_bus_bind_ack_1, UDI_GCB(bus_bind_cb), CMOS_REGSET, CMOS_BASE, CMOS_LENGTH, cmos_trans_read, sizeof cmos_trans_read / sizeof(udi_pio_trans_t), UDI_PIO_NEVERSWAP|UDI_PIO_STRICTORDER, CMOS_PACE, 0); }
static void cmos_bus_bind_ack_2(udi_cb_t *, udi_pio_handle_t);
static void cmos_bus_bind_ack_1( udi_cb_t *gcb, udi_pio_handle_t new_pio_handle) { cmos_region_data_t *rdata = gcb->context;
/* Save the PIO handle for later use. */ rdata->trans_read = new_pio_handle;
udi_pio_map(cmos_bus_bind_ack_2, gcb, CMOS_REGSET, CMOS_BASE, CMOS_LENGTH, cmos_trans_write, sizeof cmos_trans_write / sizeof(udi_pio_trans_t), UDI_PIO_NEVERSWAP|UDI_PIO_STRICTORDER, CMOS_PACE, 0); }
static void cmos_bus_bind_ack_2( udi_cb_t *gcb, udi_pio_handle_t new_pio_handle) { cmos_region_data_t *rdata = gcb->context; udi_channel_event_cb_t *channel_event_cb = gcb->initiator_context;
/* Save the PIO handle for later use. */ rdata->trans_write = new_pio_handle;
#if DO_INTERRUPTS /* Attach interrupts here... */ #endif
/* Let the MA know we've completed binding. */ udi_channel_event_complete(channel_event_cb, UDI_OK); }
The cmos_bus_bind_ack channel operation is called by the bridge mapper (in response to a udi_bus_bind_req from the driver) to send the status of the bind request back to the driver. It is called with The cmos_bus_bind_ack routine needs to take appropriate actions based on the status given by the mapper.
The bridge mapper calls cmos_bus_bind_ack with the same bus_bind_cb control block passed to it via cmos_bus_bind_req from the driver, a DMA constraints vector, an indication of the most effective endianness to use for the bus, and the status of the previously requested bind operation.
Since this driver does not do any DMA, the DMA constraints are freed via a call to udi_dma_constrins_free, and the preferred_endianness argument is simply ignored.
The bus_bind_cb control block passed to the driver is first used to
update the beginning of the region data structure (rdata)
to the context
element passed as part of
bus_bind_cb to the driver.
This is the context from the driver's earlier call to
cmos_bus_bind_req, and was passed back to the driver as part of
the cmos_bus_bind_ack.
The cmos_udi driver's region data structure cmos_region_data_t was defined earlier in the driver code; see ``Region data definitions''.
Similarly, the context of the channel when the event was generated
by the bridge mapper, the initiator_context
, is saved at the
beginning of a channel_event_cb structure.
This will be used later in the event the bind was unsuccessful.
The entire bus_bind_cb control block is then saved in the rdata region data structure for later use when unbinding the channel.
Next, the status
argument passed in the call to
cmos_bus_bind_ack is checked, and if the status is
anythingother than UDI_OK, then a call to
udi_channel_event_complete acknowledges the failureof the bind to the
bridge mapper, using the channel_event_cb control block
containing the initiator_context
of the channel event.
See
udi_channel_event_complete(3udi).
If status
is UDI_OK, then the channel to the bus bridge is
bound and we can access the CMOS RAM device.
The udi_pio_map routine is called to map the device memory
and registers for access by the driver.
Two PIO handles must be allocated: one for reading the device and
one for writing to it.
Since udi_pio_map is an asynchronous service call to the environment, and may not return immediately, a ``callback'' function is provided with the call. When the environment allocates the PIO handle, it returns it to the driver by invoking the callback routine.
In the udi_cmos driver, the PIO handle for the write operation is requested in the code for the cmos_bus_bind_ack routine. The cmos_bus_bind_ack_1 routine is specified as the callback function; when the environment allocates the handle, it calls cmos_bus_bind_ack_1 to return it to the driver. The cmos_bus_bind_ack_1 routine saves the context of the call and the write transaction's PIO handle, and then requests a handle for the read transaction using a callback function of cmos_bus_bind_ack_2.
The cmos_bus_bind_ack_2 saves the PIO handle for the write operation in the region data structure. Finally, cmos_bus_bind_ack_2 issues a udi_channel_event_complete call to tell the environment that the bind operation is completed.
The two udi_pio_map calls use similar arguments (in order of appearance):
callback
regset_idx
base_offset
length
trans_list
list_length
trans_list
.
pio_attributes
pace
pace
microseconds before
another access occurs to the same device register set (via any handle).
(The UDI_PIO_STRICTORDER attribute is required for non-zero pace
values.)
We also need a channel operation entry point for the environment to use to respond to a udi_bus_unbind_req from the driver, so the bridge mapper can send the status of the unbind request back to the driver.
The bridge mapper calls cmos_bus_bind_ack with the same bus_bind_cb control block passed to it via cmos_bus_unbind_req from the driver.
static void cmos_bus_unbind_ack(udi_bus_bind_cb_t *bus_bind_cb) { udi_mgmt_cb_t *cb = bus_bind_cb->gcb.initiator_context; cmos_region_data_t *rdata = UDI_GCB(bus_bind_cb)->context;udi_pio_unmap(rdata->trans_read); udi_pio_unmap(rdata->trans_write);
udi_cb_free(UDI_GCB(bus_bind_cb));
udi_devmgmt_ack(cb, 0, UDI_OK); }
This routine first saves the context of the operation in a udi_mgmt_cb_t(3udi) control block for later use in the acknowlegdement operation, and gets the region data from the control block passed to it. It then frees the region data memory blocks (using udi_pio_unmap(3udi)) and the control block (using udi_cb_free(3udi)). The udi_devmgmt_ack(3udi) call acknowledges the completion of the unbind operation on the driver side.