Writing PCMCIA/CF Device Drivers

Technologies like Wi-Fi, GPRS, and miniature storage devices are ubiquitous today as PCMCIA or CF cards. The Linux kernel supports PCMCIA devices on a variety of architectures.
Much of today’s popular technologies, such as memory cards, modems, wireless and wired Ethernet, General Packet Radio Service (GPRS), and the Global Positioning System (GPS) are widely available in the form factor of PCMCIA (an acronym for Personal Computer Memory Card International Association) or Compact Flash (CF) cards. Most laptops and many embedded devices support PCMCIA or CF interfaces, thus instantly enabling them to make use of a wide variety of technologies.
PCMCIA is a 16-bit data transfer interface specification that uses the 68-pin connector originally used by memory cards. The specification has grown to include support for higher speeds in the form of 32-bit CardBus cards. (The term PC Card is used when referring to either PCMCIA or CardBus devices.) CF cards are smaller, but compatible with PCMCIA, and are frequently used in handheld devices like PDAs and digital cameras. CF cards come in two flavors: Type 1 cards and the thicker Type II cards. CF cards have only 50 pins, but can be slipped into your laptop’s PCMCIA slot using a passive CF-to-PCMCIA adapter.
In this column, let’s explore how the Linux PCMCIA layer works and examine the support present in the kernel for PCMCIA/CF host adapters and client devices. Next month’s column will use the concepts learned here while implementing simple drivers for some popular networking and storage technologies.

The Kernel PCMCIA/CF Layer

Linux PCMCIA/CF support is available on Intel-based laptops as well as on architectures like ARM, MIPS, and PowerPC. The PCMCIA subsystem consists of device drivers for PCMCIA host controllers, client drivers for different cards, a daemon that aids hot plugging, user mode utilities, and a Card Services module that interacts with all the above.
Figure One illustrates the interaction between the modules that constitute the PCMCIA subsystem.
FIGURE ONE: Interaction between the modules that constitute the PCMCIA subsystem



As shown in Figure One, the Linux PCMCIA layer consists of:
*A Card Services kernel module that provides services to host controller drivers and PCMCIA card drivers (also called clients). Card Services provides an infrastructure that makes driver implementations simpler and adds a level of indirection that renders client drivers independent of host controllers.
*Host controller device drivers that implement low level routines for talking with the PCMCIA host controller. Each device slot that the host controller supports is called a socket.
*XX_cs, the client device driver that responds to socket events like card insertion and ejection. This also configures the generic device driver (XX.o) with resources like IRQs, I/O base addresses, and memory windows.
*XX.o, the generic device driver, which is not PCMCIA-specific. If your device is a PCMCIA disk, XX.o is the IDE device driver, XX-dependent layers are the filesystem layers, while XX-applications are programs that access files on the PCMCIA disk.
*The Card Manager daemon (cardmgr) provides support for hot plugging. PCMCIA/CF cards have two memory spaces: attribute memory and common memory. Attribute memory holds configuration registers and descriptor information like the Card Information Structure (CIS). While Card Services passes information from the card’s CIS up to cardmgr, cardmgr passes resource allocation policies (defined in /etc/pcmcia/config.opts) down to Card Services.
*A Driver Services module (ds.o) that’s responsible for communication between the kernel PCMCIA layer and the cardmgr daemon.
*Utilities such as cardctl that can be used to control the state of PCMCIA sockets and select between different configuration schemes.

The PCMCIA Host Controller

The PCMCIA host controller bridges the card with the system bus. It maps card memory to host I/O and memory windows, and routes interrupts generated by the card to a free processor interrupt line.
While laptops generally have a PCMCIA controller chip connected to the PCI bus, PCMCIA support is built-in to the microcontroller in many embedded devices. Figure Two relates the kernel PCMCIA modules with the typical architecture of a PCMCIA-enabled laptop.
FIGURE TWO: The kernel PCMCIA modules and the typical architecture of a PCMCIA-enabled laptop



Card Services

Card Services is the main component of the kernel PCMCIA core. It offers a set of services to client drivers and host controller drivers. Some of these services include routines to manipulate the card’s CIS space (GetFirstTuple(), GetNextTuple(), ParseTuple()), routines to reserve and release IRQ and I/O window information (RequestIRQ(), ReleaseIRQ(), RequestIO(), ReleaseIO()) and routines to actually configure PCMCIA sockets (RequestConfiguration()).
Card Services notifies the client driver’s event handler when the host controller reports events like card insertion, removal, and low battery. Code snippets in Listings Four and Five use some of the functionalities offered by Card Services.

The Client Device Driver (XX_cs)

The client device driver looks at the card’s CIS space and configures the card depending on the information that it reads. It also contains a handler that’s invoked by Card Services when the host controller driver detects a card status event.
In Listing One, the client driver registers attach and detach entry points with Card Services. The XX_attach() routine gets invoked when a request is received from the user mode cardmgr daemon to bind the client driver to a socket. (The data flow between the various PCMCIA components will get clearer when you trace the code path later on.)
Listing One: Attaching a client driver (2.4.x kernels)

/* Invoked during module initialization */

static int __init init_XX_cs (void)
{

/* “XX_cs” is the name of the client. This needs to match the string
specified in the PCMCIA configuration file (/etc/pcmcia/config).
Note that the actual location of configuration files depends on
the Linux distribution that you use */

register_pccard_driver (“XX_cs”, &XX_attach, &XX_detach);
}

The registration syntax changed between the 2.4 and 2.6 kernels. Listing Two shows the corresponding routine used in 2.6.
Listing Two: Attaching a client (2.6 Kernels)

static struct pcmcia_driver XX_cs_driver = {
.owner = THIS_MODULE,
.drv = {
.name = “XX_cs”,
},
.attach = XX_attach,
.detach = XX_detach,
};

static int __init init_XX_cs(void)
{
return pcmcia_register_driver (&XX_cs_driver);
}

XX_attach() allocates a data structure for the corresponding device instance, populates it with information such as the address of the event handler routine, and registers the device with Card Services. This is shown in Listing Three.
Listing Three: Registering the event handler with Card Services

static dev_link_t *XX_attach (void)
{
dev_link_t *link;

/* Populate the ’link’ structure here with attributes
* related to IRQ, I/O windows and other link configuration
* details like the voltage level used by the card. See
* include/pcmcia/ds.h for the structure definition.
*/

client_reg.dev_info = “XX_cs”; /* Name */
client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE;

/* Request notification when the following events occur */
client_reg.EventMask =
CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET |
CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME; /* Power Mgmt Events */

client_reg.event_handler = &XX_event; /* See Listing Four */
client_reg.Version = 0×0210; /* Version */

/* Assign the link data structure that was populated */
client_reg.event_callback_args.client_data = link;

/* Register with Card Services */
ret = CardServices (RegisterClient, &link->handle, &client_reg);
}

Again, the function for registering a client has changed in the 2.6 kernels. The corresponding 2.6 function is:
pcmcia_register_client (&link->handle, &client_reg);
The event handler responds to changes in the card status. It contains code to handle events like hotplug and power management requests. Listing Four contains the event handler.
Listing Four: The Event Handler

static int XX_event (event_t event, int priority,
event_callback_args_t *args)
{
/* … */

switch (event) {
case CS_EVENT_CARD_INSERTION: /* card was inserted */
link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
XX_config (link); /* See Listing Five */
break;

case CS_EVENT_PM_SUSPEND: /* power management event */
link->state |= DEV_SUSPEND;
CardServices (ReleaseConfiguration, link->handle);
break;

case CS_EVENT_PM_RESUME: /* power management event */
link->state &= ~DEV_SUSPEND;

/* … */

case CS_EVENT_CARD_RESET:
CardServices (RequestConfiguration, link->handle, &link->conf);
break;
}

return 0;
}

Listing Five shows the routine that configures the generic device driver, XX.o, with resource information like I/O and memory window addresses. After this step, data flow to and from the PCMCIA card passes through the XX.o driver and is transparent to the rest of the layers. Any interrupts generated by the PCMCIA card in response to data reception or transmit completion are handled by an interrupt handler that is part of XX.o.
Listing Five: Configuring the generic device driver (XX.o)

/* This will make the XX device available to the system */

static int XX_config ()
{

if (CardServices (GetConfigurationInfo, handle, &config) == CS_SUCCESS) {
/* If the card is already configured, configure XX.o with
the I/O port and IRQ information */
return (setup_XX (base_address, irq));
}

/* Read configuration tuples from the card’s CIS space, and
accordingly configure IRQs and base addresses */
tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY; /* Specify Desired Tuple */
CardServices (GetTupleData, handle, tuple);
CardServices (ParseTuple, handle, tuple, parse);

/* … */

/* See include/pcmcia/ds.h for the definition of
the ink stucture */

/* Reserve IO port window */
CardServices (RequestIO, link->handle, &link->io);
/* Reserve IRQ */
CardServices (RequestIRQ, link->handle, &link->irq);
/* Actually configure the socket */
CardServices (RequestConfiguration, link->handle, &link->conf);

/* … */

/* Call setup_XX, which is part of the XX.o driver
with resource information */
setup_XX (link->io.BasePort1, link->irq.AssignedIRQ);

/* … */
}

From this point, the XX.o driver is ready to handle the data flow to and from the card.

Driver Services

Driver Services is the kernel piece responsible for communicating with the cardmgr daemon. It’s essentially a character device driver whose major number is obtained by cardmgr from the process filesystem entry, /proc/devices.
cardmgr monitors PCMCIA sockets for card events by polling Driver Services. It sends I/O control commands to Driver Services to exchange information between kernel space and user space.

Tying the Pieces Together

As you saw in Figure One, the PCMCIA layer consists of various components and the data flow path between the components can sometimes get complicated. Let’s trace the code path from the time you insert a PCMCIA card until an application starts transferring data to the card.
1.During startup, the cardmgr daemon starts polling on the PCMCIA socket. This translates to an invocation of the poll entry point that is part of Driver Services (ds.o).
2.When you insert the card, the interrupt handler that is part of the host controller device driver detects the event. It schedules its interrupt bottom handler, which in turn invokes the event handler that is part of Card Services.
3.Card Services parses the event (using parse_events()) and dispatches it to the registered client device driver (using send_events()). The event also gets notified to the Driver Services handler (ds_event) that was registered with Card Services during initialization.
4.When the Driver Services event handler gets notified that the card has been inserted, it wakes up the cardmgr daemon that’s polling it for data.
5.cardmgr sends a series of I/O control (ioctl()) commands to Driver Services and obtains information about the inserted card, such as the make, model, and manufacturer ID of your device.
6.cardmgr examines its configuration file (/etc/pcmcia/config) and determines and loads the device drivers that need to be bound to this card (XX_cs.o and XX.o in Figure One).
7.When the XX_cs module gets loaded, it registers itself with Card Services as shown in Listing One.
8.cardmgr now issues a bind request to Driver Services, which binds the XX_cs.o module with the corresponding PCMCIA socket. This results in the invocation of the XX_attach entry point that was registered by the client driver in Listing Three.
9.The XX_attach entry point invokes XX_config that configures the generic device driver (XX.o) as shown in Listing Five. This configures XX.o with resources like memory windows.
10.cardmgr issues another ioctl() command to Driver Services and deciphers device class specific details. In the case of a networking card, this includes the network interface name to which the card has been bound. cardmgr uses this information to start necessary application services.
From this point, data transfer is done by XX-applications via driver entry points that are part of the generic XX.o driver. The form factor of the device (for example, whether it is a PCMCIA disk or an actual IDE disk) is transparent to the applications as well as to the XX.o driver.

Looking at the Sources

In the Linux source tree, the drivers/pcmcia/ subdirectory contains the sources for Card Services, Driver Services, and PCMCIA host adapter device drivers. Client device drivers can be found under the particular device class subdirectory. So, drivers for PCMCIA networking drivers can be found under drivers/net/pcmcia/. PCMCIA-related structures are defined in header files present in the include/pcmcia/ directory. Note that some of the client driver locations have changed in the 2.6 tree.
The PCMCIA project web site, http://pcmcia-cs.sourceforge.net, contains sample client driver (like dummy_cs.c), sources for the user mode Card Manager daemon and a useful programming guide that documents the features offered by kernel Card Services.

Sreekrishnan Venkateswaran has been working for IBM India since 1996. His recent Linux projects include putting Linux onto a wristwatch, a cell phone, and a pacemaker programmer. You can reach Krishnan at class="emailaddress">krishhna@gmail.com.

Fatal error: Call to undefined function aa_author_bios() in /opt/apache/dms/b2b/linux-mag.com/site/www/htdocs/wp-content/themes/linuxmag/single.php on line 62