Next Previous Contents

8. Anatomy of a Card Services Client Driver

Each release of the Linux Card Services package comes with a well-commented ``dummy'' client driver that should be used as a starting point for writing a new driver. Look for it in clients/dummy_cs.c. This is not just a piece of sample code: it is written to function as a sort of generic card enabler. If bound to an IO card, it will read the card's CIS and configure the card appropriately, assuming that the card's CIS is complete and accurate.

8.1 Module initialization and cleanup

All loadable modules must supply init_module() and cleanup_module() functions, which are invoked by the module support code when the module is installed and removed. A client driver's init function should register the driver with Driver Services, via the register_pccard_driver() call. The cleanup function should use unregister_pccard_driver() to unregister with Driver Services. Depending on the driver, the cleanup function may also need to free any device structures that still exist at shutdown time.

8.2 The *_attach() and *_detach() functions

The *_attach() entry point is responsible for creating an ``instance'' of the driver, setting up any data structures needed to manage one card. The *_attach() function should allocate and initialize a dev_link_t structure, and call RegisterClient to establish a link with Card Services. It returns a pointer to the new dev_link_t structure, or NULL if the new instance could not be created.

The *_detach() entry point deletes a driver instance created by a previous call to *_attach. It also breaks the link with Card Services, using DeregisterClient.

The *_attach() entry point is called by Driver Services when a card has been successfully identified and mapped to a matching driver by a DS_BIND_REQUEST ioctl(). The *_detach() entry point is called in response to a DS_UNBIND_REQUEST ioctl() call.

8.3 The *_config() and *_release() functions

The *_config() function is called to prepare a card for IO. Most drivers read some configuration details from the card itsef, but most have at least some built-in knowledge of how the device should be set up. For example, the serial card driver reads a card's CFTABLE_ENTRY tuples to determine appropriate IO port base addresses and corresponding configuration indices, but the driver ignores the interrupt information in the CIS. The *_config function will parse relevant parts of a card's CIS, then make calls to RequestIO, RequestIRQ, and/or RequestWindow, then call RequestConfiguration.

When a card is successfully configured, the *_config() routine should fill in the dev_name, major, and minor fields in the dev_link_t structure. These fields will be returned to user programs by Driver Services in response to a DS_GET_DEVICE_INFO ioctl().

The *_release() function should release any resource allocated by a previous call to *_config(), and blank out the device's dev_name field.

The *_config() and *_release functions are normally called in response to card status change events or from timer interrupts. Thus, they cannot sleep, and should not call other kernel functions that might block.

8.4 The client event handler

The *_event() entry point is called from Card Services to notify a driver of card status change events.

8.5 Locking and synchronization issues

A configured socket should only be released when all associated devices are closed. Releasing a socket allows its system resources to be allocated for use by another device. If the released resources are reallocated while IO to the original device is still in progress, the original driver may interfere with use of the new device.

A driver instance should only be freed after its corresponding socket configuration has been released. Card Services requires that a client explicitly release any allocated resources before a call to DeregisterClient will succeed.

All loadable modules have a ``use count'' that is used by the system to determine when it is safe to unload a module. The convention in client drivers is to increment the use count when a device is opened, and to decrement the count when a device is closed. So, a driver can be unloaded whenever all associated devices are closed. in particular, a driver can be unloaded even if it is still bound to a socket, and the module cleanup code needs to be able to appropriately free any such resources that are still allocated. This should always be safe, because if the driver has a use count of zero, all devices are closed, which means all active sockets can be released, and all device instances can be detached.

If a driver's *_release() function is called while a device is still open, it should set the DEV_STALE_CONFIG flag in the device state, to signal that the device should be released when the driver's close() function is called. If *_detach() is called for a configured device, the DEV_STALE_LINK flag should be set to signal that the instance should be detached when the *_release() function is called.

8.6 Using existing Linux drivers to access PC Card devices

Many of the current client drivers use existing Linux driver code to perform device IO operations. The Card Services client module handles card configuration and responds to card status change events, but delegates device IO to a compatible driver for a conventional ISA bus card. In some cases, a conventional driver can be used without modification. However, to fully support PC Card features like hot swapping and power management, there needs to be some communication between the PC Card client code and the device IO code.

Most Linux drivers expect to probe for devices at boot time, and are not designed to handle adding and removing devices. One side-effect of the move towards driver modularization is that it is usually easier to adapt a modularized driver to handle removable devices.

It is important that a device driver be able to recover from having a device disappear at an inappropriate time. At best, the driver should check for device presence before attempting any IO operation or before handling an IO interrupt. Loops that check device status should have timeouts so they will eventually exit if a device never responds.

The dummy_cs driver may be useful for loading legacy drivers for compatible PC Card devices. After binding dummy_cs to a card, the legacy driver module may be able to detect and communicate with the device as if it were not a PC Card. This arrangement will generally not support clean hot swapping or power management functions, however it may be useful as a basis for later developing a more full-featured client driver.

Next Previous Contents