Contained Within
Find More Documentation
Featured Support Resources
| Download this book in PDF
PC Card Event Management
6
- This chapter discusses event handling in the PCMCIA framework and provides code examples for several events.
PC Card Event Management Overview
- PCMCIA event management provides the mechanism that informs PC Card drivers of hardware and software status changes. PCMCIA events are generated by physical state changes or by changes in the software framework. Software events occur directly as a result of changes in the software state of the PCMCIA framework or indirectly due to a change in hardware state of the PCMCIA framework or underlying host system.
- For example, inserting a PC Card into a socket causes Card Services to deliver a card insertion event to the PC Card driver. A battery low event informs the driver when the battery is beginning to fail. PCMCIA events also notify drivers of the completion of an asynchronous task; for example, a card ready event informs the driver that the card has completed its internal initialization and is ready for operation.
- To respond to events, the PC Card client must supply an event handler routine and specify the events that it is interested in. Once the event handler is registered with Card Services, the event handler drives the completion of the card configuration process as well as driver responses to other events.
Event Types
- Card Services defines a set of event types that the PC Card driver uses to register interest in certain events. A driver registers interest in events by specifying an event bit mask in the driver registration call csx_RegisterClient(9F). The event mask can be modified using the csx_RequestSocketMask(9F) function or the csx_GetEventMask(9F) and csx_SetEventMask(9F) functions.
-
Table 6-1 lists the possible event types.
-
Table 6-1
| Event Types | Description |
| CS_EVENT_BATTERY_LOW | The PC Card battery charge is weak and needs to be replaced. |
| CS_EVENT_BATTERY_DEAD | The PC Card battery is no longer providing operational voltage. |
| CS_EVENT_CARD_INSERTION | A PC Card has been inserted in a socket. |
| CS_EVENT_CARD_READY | A PC Card's READY line has changed from the busy to ready state. |
| CS_EVENT_CARD_REMOVAL | A PC Card has been removed from a socket. |
| CS_EVENT_CARD_RESET | A hardware reset has occurred. |
| CS_EVENT_CARD_LOCK | A mechanical latch has been manipulated, preventing the removal of the PC Card from the socket. |
| CS_EVENT_CARD_UNLOCK | A mechanical latch has been manipulated, enabling the removal of the PC Card from the socket. |
| CS_EVENT_CLIENT_INFO | A request that the client return its client information data. |
| CS_EVENT_EJECTION_REQUEST | A request that the PC Card be ejected from a socket using a motor-driven mechanism. |
| CS_EVENT_EJECTION_COMPLETE | A motor has ejected a PC Card from a socket. |
| CS_EVENT_ERASE_COMPLETE | A queued erase request that is processed in the background is complete. |
-
Table 6-1 (Continued)
| Event Types | Description |
| CS_EVENT_INSERTION_REQUEST | A request that a PC Card be inserted into a socket using a motor-driven mechanism. |
| CS_EVENT_INSERTION_COMPLETE | A motor has inserted a PC Card in a socket. |
| CS_EVENT_RESET_REQUEST | A request for a physical reset by a client. |
| CS_EVENT_REGISTRATION_COMPLETE | Client registration has completed. |
| CS_EVENT_RESET_COMPLETE | A reset request that is processed in the background has been completed. |
| CS_EVENT_RESET_PHYSICAL | A reset is about to occur. |
| CS_EVENT_WRITE_PROTECT | The write-protect status of the PC Card in the indicated socket has changed. |
-
Note - In all cases, Card Services delivers an event to each driver instance associated with a function on a multiple-function PC Card.
Event Priorities
- Events are dispatched to the PC Card driver as either CS_EVENT_PRI_HIGH for high-priority events or CS_EVENT_PRI_LOW for low-priority events. Some events can be delivered at both high priority and low priority. Card Services specifies the priority of each event to the driver's event handler in an argument to the driver event handler. The driver uses the priority flag to determine which behavior to follow.
- High-priority events are delivered above lock level (above the level of the system scheduler). The driver must use its high-level event mutex initialized with the iblk_cookie returned by csx_RegisterClient(9F) to protect such events.
- Low-priority events are delivered below lock level (below the level of the system scheduler), and--unless the cookie has been returned by a call to ddi_add_softintr(9F)--the driver must use its low-level event mutex initialized with a NULL interrupt cookie to protect these events.
Event Handler Entry Point
- Each instance of a PC Card driver must register an event handler to manage events associated with its PC Card. The driver also specifies the events that it is interested in. When Card Services notices that an event has occurred, it notifies interested clients. Event notification takes place through the driver's csx_event_handler(9E) entry point.
- The driver event handler is registered using the csx_RegisterClient(9F) function. This typically occurs at driver initialization in the attach(9E) function. The csx_RegisterClient(9F) client_req_t structure includes fields for the event handler and event mask. For more information on driver registration, see "Driver Registration With Card Services" on page 39.
- Once csx_RegisterClient(9F) returns to the driver, the PC Card driver event handler must be capable of receiving events. Sometimes a card insertion event can occur without a PC Card actually being inserted. This is called an artificial insertion event. An artificial insertion event is generated by Card Services and not by the action of a card being inserted. Card Services delievers a CS_EVENT_REGISTRATION_COMPLETE event when registration is complete and all artificial events have been generated.
csx_event_handler(9E)
- Card Services calls the driver event handler with the event type, the event priority, and optional client data. The event handler entry point is defined as:
-
-
int32_t event_handler(event_t event,int32_t priority,
event_callback_args_t *args);
- The client data is specified in the driver registration call using the event_callback_args.client_data field in the csx_RegisterClient(9F) client_req_t structure. Typically, this argument is the driver instance's soft-state pointer.
-
Code Example 6-1 shows a PC Card driver event handler that handles a basic set of events, including card ready, card insertion, and card removal events. The driver ignores events that it has not registered interest in and returns a status code to Card Services.
-
Code Example 6-1 PC Card Event Handler
-
-
static int32_t
xx_event_handler(event_t event, int32_t priority,
event_callback_args_t *eca)
{
xx_unit_t *xx = eca->client_data;
client_info_t *ci = &eca->client_info;
int32_t ret = CS_UNSUPPORTED_EVENT;
switch (event) {
case CS_EVENT_REGISTRATION_COMPLETE:
ret = CS_SUCCESS;
break;
case CS_EVENT_CARD_READY:
if (priority & CS_EVENT_PRI_LOW) {
mutex_enter(&xx->event_mutex);
ret = xx_card_ready(xx);
mutex_exit(&xx->event_mutex);
}
break;
case CS_EVENT_CARD_INSERTION:
if (priority & CS_EVENT_PRI_LOW) {
mutex_enter(&xx->event_mutex);
ret = xx_card_insertion(xx);
mutex_exit(&xx->event_mutex);
}
break;
case CS_EVENT_CARD_REMOVAL:
case CS_EVENT_CARD_REMOVAL_LOWP:
if (priority & CS_EVENT_PRI_HIGH) {
mutex_enter(&xx->event_hi_mutex);
cbt->card_state &= ~XX_CARD_INSERTED;
mutex_exit(&xx->event_hi_mutex);
rval = CS_SUCCESS;
} else {
mutex_enter(&xx->event_mutex);
ret = xx_card_removal(xx);
mutex_exit(&xx->event_mutex);
}
break;
case CS_EVENT_CLIENT_INFO:
mutex_enter(&xx->event_mutex);
if (GET_CLIENT_INFO_SUBSVC(ci->Attributes) ==
CS_CLIENT_INFO_SUBSVC_CS) {
/*
-
-
* Note that CardServices will prepare the
* DriverName field on the driver's behalf.
*/
ci->Revision = XX_CI_REVISION;
ci->CSLevel = CS_VERSION;
ci->RevDate = XX_REV_DATE;
strcpy(ci->ClientName, XX_CLIENT_DESCRIPTION);
strcpy(ci->VendorName, XX_VENDOR_DESCRIPTION);
ci->Attributes |= CS_CLIENT_INFO_VALID;
ret = CS_SUCCESS;
}
mutex_exit(&xx->event_mutex);
break;
}
return (ret);
}
Event Handling Examples
- Typically, PC Card drivers are primarily concerned with card insertion, card ready, and card removal events. A driver may also want to set up a timeout routine to handle the delay that may occur between card insertion and card readiness. The following sections provide information and sample code for these events.
Card Insertion
- When a PC Card is inserted, Card Services delivers a CS_EVENT_CARD_INSERTION event to the driver's event handler. Note that PC Card drivers only receive card insertion events for their specific cards and are not informed about other PC Cards in the system.
- In Code Example 6-2, the driver determines whether the card is ready for further processing. If the card is ready, the driver calls the card ready routine, and card configuration continues. If the card is not ready, the driver sets a timeout and waits for the card to become ready.
-
Code Example 6-2 Card Insertion Routine
-
-
static int
xx_card_insertion(xx_state_t *xx)
{
int ret = CS_OUT_OF_RESOURCE;
-
-
get_status_t get_status;
ASSERT(mutex_owned(&xx->event_mutex));
xx->card_event &= ~(XX_CARD_READY | XX_CARD_ERROR);
xx->card_event |= XX_CARD_INSERTED;
ret = csx_GetStatus(xx->handle, &get_status);
if (get_status.CardState & CS_EVENT_CARD_READY)
(void) xx_card_ready(xx);
if ((xx->card_event &
(XX_CARD_READY | XX_CARD_ERROR)) == 0) {
/* If the card isn't ready yet, we set up a timeout
* handler to trap cards that never do give us a
* Card Ready event,and then return to wait for either
* the Card Ready event or the timeout to expire.
*/
xx->ready_timeout_id =
timeout(xx_card_ready_timeout, (caddr_t)xx,
(long)(drv_usectohz(1000000) *
XX_READY_TIMEOUT));
}
return (CS_SUCCESS);
}
-
Note - PC Card drivers do not need to parse CIS to determine if their card is inserted. However, some cards may need to parse CIS in order to determine configuration changes. For example, in a system where a fully-relocatable modem is inserted and later replaced by a different modem card, the framework will consider both cards the same, but the new card will require a different configuration.
Card Ready
- When a PC Card becomes ready after insertion, Card Services delivers a CS_EVENT_CARD_READY event to the driver's event handler. This callback indicates that the driver may now access and initialize the card, as shown in Code Example 6-3. For information on card configuration, see Chapter 7, "PC Card Configuration."
-
Code Example 6-3 Card Ready Routine
-
-
static int
xx_card_ready(xx_state_t *xx)
{
int ret;
modify_config_t modify_config;
ASSERT(mutex_owned(&xx->event_mutex));
/* Remove any pending card ready timer */
if (xx->ready_timeout_id) {
int id = xx->ready_timeout_id;
xx->ready_timeout_id = 0;
mutex_exit(&xx->event_mutex);
untimeout(id);
mutex_enter(&xx->event_mutex);
}
/*
* If the card is just now becoming ready,
* perform basic card configuration and initialization
*/
if ((xx->card_event & XX_CARD_READY) == 0) {
if (xx_card_configuration(xx) == 0) {
xx->card_event |= XX_CARD_ERROR;
if (xx->card_event & XX_CARD_WAIT_READY) {
xx->card_event &= ~XX_CARD_WAIT_READY;
ASSERT(mutex_owned(&xx->event_mutex));
cv_broadcast(&xx->readywait_cv);
}
return (CS_SUCCESS);
}
}
return (CS_SUCCESS);
}
Card-Ready Timeout
- The PC Card Standard acknowledges that a PC Card may take a few seconds to become ready after insertion. Card Services delivers a
- CS_EVENT_CARD_INSERTION event when the card is inserted, but the card may not be ready to use at that time. When the card becomes ready to use, Card Services delivers a CS_EVENT_CARD_READY event.
- For best results, the client driver should provide a timeout routine to handle the case when the card is not ready when inserted. This routine will enable the driver to detect a card that has not become ready within a reasonable period of time. When the timeout occurs, the driver can issue a signal on the readywait_cv to wake up the attach(9E) function. This will prevent attach(9E) from hanging forever. Code Example 6-4 shows a card-ready timeout routine.
-
Code Example 6-4 Card-Ready Timeout Routine
-
-
static void
xx_card_ready_timeout(xx_state_t *xx)
{
mutex_enter(&xx->event_mutex);
if (xx->ready_timeout_id) {
xx->ready_timeout_id = 0;
xx->card_event |= XX_CARD_READY | XX_CARD_ERROR;
}
if (xx->card_event & XX_CARD_WAIT_READY) {
xx->card_event &= ~XX_CARD_WAIT_READY;
cv_broadcast(&xx->readywait_cv);
}
mutex_exit(&xx->event_mutex);
}
Card Removal
- When a PC Card is removed from a PCMCIA socket, Card Services delivers a CS_EVENT_CARD_REMOVAL event to the PC Card driver's event handler for the instance of that driver associated with that card. Card removal events are delivered to the driver twice, first as a high-priority card removal event, then as a low-priority card removal event. These event priorities always occur in a predetermined sequence, with high-priority events occuring first and low-priority events later.
- The purpose of the high-priority card removal event is to immediately notify the driver that the card has been removed. The high-priority card removal event allows the driver to set a bit in the driver's soft state so that when the card is removed the driver can break out of any loops it may be processing, and can stop attempting to service interrupts for the card.
- The purpose of the low-priority card removal event is to release all I/O and IRQ resources acquired from the time the card was inserted. These resources can be released using the functions csx_ReleaseConfiguration(9F), csx_ReleaseIO(9F), csx_ReleaseIRQ(9F), and csx_ReleaseWindow(9F).
High-Priority Card Removal
- High-priority card removal events are delivered with priority CS_EVENT_PRI_HIGH and are always delivered before the corresponding low-priority card removal event. High-priority card removal events are delivered to the driver above lock level, and the event handler must acquire the high-priority event mutex to manage this event.
-
Code Example 6-5 shows an event handler dealing with a high-priority card removal event.
-
Code Example 6-5 Event Handler for High-Priority Event
-
-
xx_event_handler(event_t event, int priority,
event_callback_args_t *eca)
{
int rval;
xx_unit_t *xx = eca->client_data;
switch (event) {
...
case CS_EVENT_CARD_REMOVAL:
if (priority & CS_EVENT_PRI_HIGH) {
mutex_enter(&xx->event_hi_mutex);
cbt->card_state &= ~XX_CARD_INSERTED;
mutex_exit(&xx->event_hi_mutex);
rval = CS_SUCCESS;
} else {
rval = xx_card_removal(xx);
}
break;
...
return (rval);
}
Low-Priority Card Removal
- After the high-priority card removal event has been delivered, Card Services schedules and delivers a low-priority card removal event of priority CS_EVENT_PRI_LOW. The driver should use its low-priority event mutex to manage this event.
- When a card removal event is delivered, a driver that uses interrupts must first acquire the I/O mutex in the event handler to cause the driver to spin until any pending I/O interrupts have completed. Clearing the card inserted or ready state during the earlier high-priority Card Removal event permits the driver to ensure that any interrupt triggered after that event can return without initiating any activity on the device or even acquiring the I/O mutexes. This is important since the device may be sharing the interrupt line with other devices, and the driver should not stall or interfere with those devices in any way.
-
Code Example 6-6 shows a typical low-priority card removal event.
-
Code Example 6-6 Event Handler for Low-Priority Event
-
-
static int
xx_card_removal(xx_state_t *xx)
{
int ret;
sockevent_t sockevent;
remove_device_node_t remove_device_node;
io_req_t io_req;
modify_config_t modify_config;
release_config_t release_config;
irq_req_t irq_req;
ASSERT(mutex_owned(&xx->event_mutex));
/* Remove any pending card ready timer */
if (xx->ready_timeout_id) {
int id = xx->ready_timeout_id;
xx->ready_timeout_id = 0;
mutex_exit(&xx->event_mutex);
untimeout(id);
mutex_enter(&xx->event_mutex);
}
/* Did the card fail initialization? */
-
-
if (xx->card_event & XX_CARD_ERROR) {
xx->card_event &= ~(XX_CARD_READY |
XX_CARD_WAIT_READY | XX_CARD_INSERTED);
xx->card_event &= ~XX_CARD_BUSY;
return (CS_SUCCESS);
}
/*
* First, grab the I/O mutex. This will cause the driver
* to spin until any pending I/O interrupts have completed.
*/
xx->card_event &= ~XX_CARD_INSERTED;
mutex_enter(&xx->irq_mutex);
xx->card_event &= ~(XX_CARD_READY | XX_CARD_ERROR |
XX_CARD_WAIT_READY);
/*
* Now that we are sure the interrupt handlers won't
* attempt to initiate any activity, we can continue
* freeing this card's I/O resources.
*/
mutex_exit(&xx->irq_mutex);
modify_config.Attributes = 0;
modify_config.Vpp1 = 0;
modify_config.Vpp2 = 0;
modify_config.Attributes = (CONF_VPP1_CHANGE_VALID |
CONF_VPP1_CHANGE_VALID | CONF_VPP2_CHANGE_VALID);
ret = csx_ModifyConfiguration(xx->handle, &modify_config);
ret = csx_ReleaseConfiguration(xx->handle, &release_config);
ret = csx_ReleaseIRQ(xx->handle, &irq_req);
mutex_destroy(&xx->irq_mutex);
ret = csx_ReleaseIO(xx->handle, &io_req);
remove_device_node.Action = REMOVAL_ALL_DEVICE_NODES;
remove_device_node.NumDevNodes = 0;
ret = csx_RemoveDeviceNode(xx->handle, &remove_device_node);
-
-
xx->card_event &= ~XX_CARD_BUSY;
return (CS_SUCCESS);
}
|
|