Networking and Storage with PC Cards

Thanks to plug-and-play devices like PC cards, you can backup gigabytes of data onto a stamp-sized device or enable your laptop with diverse networking technologies.

Last month’s column looked at the Personal Computer Memory Card International Association (PCMCIA) core support present in the Linux kernel and discussed how devices having PCMCIA or Compact Flash (CF) interfaces could use services provided by the kernel to instantly enable themselves with diverse technologies.

PCMCIA is a 16-bit data transfer interface specification that has evolved to include support for higher speeds in the form of 32-bit CardBus cards. Since CardBus is closer to the Peripheral Component Interconnect (PCI) bus, recent versions of Linux have moved hot-plug support for CardBus devices from the PCMCIA layer to the PCI layer. The term PC Card is used to refer to either PCMCIA or CardBus cards.

This month, let’s continue and look at some popular PCMCIA/CF cards and see how they interact with the PCMCIA core and the rest of the kernel. Let’s also implement a simple driver for a multifunction CardBus card.

PCMCIA Networking over Serial Transports

Many networking technologies use a serial transport mechanism to communicate with host devices. serial_cs, the generic serial card services driver allows the rest of the operating system to see the PCMCIA/CF card as a serial device. The first unused serial device, /dev/ttySX, gets allotted to the card. serial_cs thus emulates a serial port over cards that feature technologies like General Packet Radio Service (GPRS), Global Systems for Mobile communications (GSM), or Global Positioning System (GPS). It also allows Bluetooth PCMCIA/CF cards that use a serial transport to transfer Host Control Interface (HCI) packets to Bluetooth protocol layers.

Figure One illustrates how kernel modules implementing different networking technologies interact with the serial_cs driver to communicate with the respective cards.

FIGURE ONE: How networking kernel modules interact with the serial_cs driver

The Point-to-Point Protocol (PPP) allows networking protocols such as TCP/IP to run over a serial link. In the context of PCMCIA cards in Figure One, PPP can get TCP/IP applications running over GPRS, and data over GSM and Bluetooth RFCOMM. The PPP daemon, pppd, can be attached over the virtual serial port emulated by serial_cs as:

#pppd ttySX call connection-script

connection-script contains command sequences exchanged between pppd and the service provider to establish a link. The connection script depends on the particular card that is being used. A GPRS card would need a context string to be sent as part of the connection script, while a GSM card might need an exchange of passwords. Note that the PPP kernel modules (ppp_generic, ppp_async, and slhc) have to be loaded for pppd to work.

PCMCIA Storage Devices

PCMCIA/CF storage cards come in different flavors:

*Miniature Integrated Drive Electronics(IDE)disk drives or Microdrives. These are tiny versions of mechanical hard drives that use magnetic media. The data transfer rates are typically higher than solid state memory devices, but IDE drives have spin-up and seek latencies before data can be transferred. The IDE Card Services driver ide_cs, in combination with the legacy IDE drivers, can be used to communicate with such memory cards.

*Solid state memory cards that have no moving parts, but emulate IDE. Such cards usually use flash memory, which is transparent to the operating system because of the IDE emulation. Since these drives are also IDE-based, the same IDE Card Services driver (ide_cs) can be used.

*Memory cards that use flash memory, but without IDE emulation. The memory_cs card services driver provides block and character interfaces over such cards. The block interface can be used to put a file system onto card memory, while the character interface can be used to access raw data. The memory_cs driver can also be used to read the attribute memory space of any PCMCIA card. memory_cb is the CardBus version of this driver.

Wireless Local Area Network (802.11 WLAN) Cards

Since there are many popular WLAN PC cards out there, let’s briefly look at WLAN support on Linux. A WLAN network is an alternative to a wired LAN and is generally used within a single building. The 802.11b WLAN standard supports a maximum speed of 11 Mbps, while the 802.11a and the 802.11g standards support maximum speeds of 54 Mbps.

Linux support for WLAN broadly consists of a WLAN API implementation and WLAN device drivers. Two Linux projects define a generic WLAN API and provide tools to let user-space applications configure parameters and access information from WLAN device drivers.

*The Wireless Extensions project provides a common user space interface for different wireless cards. The project’s tools include iwconfig to configure parameters like Wired Equivalent Privacy (WEP) keys and Service Set Identifiers (SSIDs) in the WLAN driver. WEP provides a degree of encryption, while SSIDs identify individual WLANs.

*The linux-wlan project supports another set of tools to communicate with WLAN drivers. These utilities, unlike the ones based on Wireless Extensions, use a Simple Network Management Protocol (SNMP) syntax that mirrors the IEEE 802.11 specification.

Many available WLAN PC cards are either based on the Intersil Prism chipset or the Hermes chipset. Linux drivers that support popular WLAN cards include the Orinoco WLAN driver and the linux-wlan-ng driver. The former, part of the kernel sources, supports both Hermes-based cards and Intersil Prism-based cards. The orinoco_cs driver provides the Card Services support. The linux-wlan-ng driver supports a variety of cards based on the Prism chipset. The driver supports the linux-wlan API and partially supports Wireless Extensions.

Certain WLAN PC Cards support a mode that allow them to act as access points and this requires additional driver support. Some WLAN cards require different driver versions to support PCMCIA and CF flavors, since the CF cards lack on-card firmware and depend on the driver for firmware download.

Multifunction CardBus Cards

Even though CardBus and PCMCIA cards use 68-pin connectors, CardBus devices support 32 data lines compared to the 16 in PCMCIA cards. This is possible by multiplexing address and data lines as done in the PCI bus. CardBus is close to the PCI bus and the kernel handles it via the PCI layer. Please refer to the March 2000″ Gearheads” column at http://www.linux-mag.com/2000-03/gear_01.html for an in depth look at the Linux PCI layer. A multifunction CardBus card is a combination device that can simultaneously perform multiple functions.

Letís take the example of an Ethernet-Modem multifunction CardBus card and see how it can be used for networking on an LAN, as well as for establishing a dialup connection to an Internet service provider. You will essentially need one device driver per supported function. Assuming that you already have generic serial and Ethernet drivers that support the corresponding chipsets used on the card, letís tinker with those drivers and get them to work over the CardBus interface.

Listing One and Listing Two describe the data structures that have to be registered with the PCI layer for the Ethernet/modem card. Since the Ethernet and modem functions have different function IDs in PCI terminology, they need separate probe() entry points and PCI id_table s.

LISTING ONE: Driver data structures for the network function

/*
* The set of PCI IDs that the network driver is interested in.
* Only a single entry in our case
*/
struct pci_device_id network_driver_pci_table[] __devinitdata = {
{
{
PCI_VENDOR_ID, /* Interface chip manufacturer ID */
PCI_DEVICE_ID_NETWORK, /* Device ID for the Network Function */
PCI_ANY_ID, /* Card manu/subvendor ID */
PCI_ANY_ID, /* Subdevice ID */
0, 0, /* Class, Classmask */
network_driver_private_data /* Correlates configuration info if
the driver supports multiple PCI cards */
}, {0},
}

struct pci_driver network_pci_driver = {
name: “network_driver_name”, /* Name */
probe: network_driver_probe, /* Probe */
remove: __devexit_p (network_driver_remove), /* Release */
id_table: network_driver_pci_table, /* See Above */
};

/* Ethernet Driver Init */
static int __init network_driver_init(void)
{
/* … */
pci_module_init (&network_pci_driver);
/* … */
}

LISTING TWO: Driver data structures for the modem function

struct pci_driver serial_pci_driver = {
name: “modem_driver_name”, /* Name */
probe: modem_driver_probe, /* Probe */
remove: __devexit_p(modem_driver_remove), /* Release */
id_table: modem_driver_pci_table,
};

/* Serial Driver init */
static int __init serial_driver_init(void)
{
/* … */
pci_module_init (&serial_pci_driver);
/* … */
}

The __devexit_p() macro discards the supplied function at compile time if you haven’t configured hot-plug support and if the driver is not a dynamically loadable module. Look at include/linux/pci.h for the definition of struct pci_driver.

The probe() entry points are invoked by the Linux PCI layer when a hotplug event is detected, if the registered IDs in the driver’s PCI ID table match with the ones read from the card.

Listing Three shows the probe() entry point for the Ethernet function. This enables the PCI device, discovers resource information like I/O address and IRQs, allocates and populates a networking data structure associated with the device, and registers it with the kernel networking layer.

LISTING THREE: PCI driver network function probe routine

static int __devinit network_driver_probe
(struct pci_dev *pdev, const struct pci_device_id *id)
{
struct net_device * net_dev; /* See include/linux/netdevice.h */

/* Ask low level PCI code to enable I/O and
memory regions for this device. Look up the IRQ
for the device that the PCI layer allotted when
it scanned the bus */
pci_enable_device (pdev);

/* Use this device in Bus mastering mode, since
the network function is capable for DMA. Look at
Documentation/DMA-mapping.txt for a discussion
on DMA on PCI */
pci_set_master (pdev);

/* Allocate an Ethernet device and fill in
generic values in the net_dev structure.
prv_data is the private driver data structure
that contains buffers, locks and so on */
net_dev = alloc_etherdev (sizeof(*prv_data));

/* Get I/O address for this PCI region */
ioaddr = pci_resource_start (pdev, 0);

/* Fill in resource information obtained from
the PCI layer */
net_dev->base_addr = ioaddr;
net_dev->irq = pdev->irq;

/* Populate net_dev with your network device
driver entry points */
/* … */
/* Register the driver with the network layer.
This will allot an unused ethX interface */
register_netdev (net_dev);
/* … */
}

Listing Four does similar work for the modem function, but the device is registered with the kernel serial layer instead. In the listings, pci_dev is the PCI data structure associated with the device, while pci_device_id is the device’s entry in the PCI ID table.

LISTING FOUR: PCI driver modem function probe routine

static int __devinit modem_driver_probe
(struct pci_dev *pdev, const struct pci_device_id *id)
{
struct serial_struct serial_req; /* See include/linux/serial.h */

/* Ask low level PCI code to enable I/O and
memory regions for this PCI device */
pci_enable_device (pdev);

/* Get the PCI irq and i/o address to be used
and populate the serial_req structure with these
resources. See linux/pci.h for helper functions
*/
/* … */
/* Register this information with the serial
layer * and get an unused ttySX port allotted */
register_serial (&serial_req);
/* … */
}

To get a clearer idea, let’s trace the code path from the time you insert the Ethernet/modem CardBus card until you are allotted a serial port (/dev/ttySX) and a network interface (ethX).

1.For each supported CardBus function, the respective driver initialization routine registers a probe() entry point with the PCI layer as shown in the first two listings.

2.Depending on the kernel version you use, either the PCMCIA layer or the PCI hotplug layer detects the insertion of the and asks the PCI layer to handle the event.

3.For each of the two CardBus functions, the PCI layer matches the vendor and device IDs with those registered by PCI drivers. It thereís a match, the corresponding driver probe() entry points are called. This results in the invocation of serial_driver_probe() and network_driver_probe(), as described in Listing Three and Listing Four.

4.The probe() entry points configure the serial and Ethernet drivers with resource information, which result in the allocation of a serial port (ttySX) and a networking interface (ethX). From this point on, application data flows over these interfaces.

Debugging

To effectively debug PCMCIA/CF client drivers, you need to watch the debug messages emitted by the kernel PCMCIA core. To do that, define PCMCIA_DEBUG in source files in the drivers/pcmcia/ directory. Verbosity levels can be controlled by setting a value either for PCMCIA_DEBUG or for the pc_debug module insertion parameter.

Information about PC Card client drivers can be found in the process file system entry, /proc/bus/pccard. Take a look at /proc/pci to know more about your PCMCIA host controller if your system uses a PCI-to-PCMCIA bridge. proc/bus/pci/devices contains entries for CardBus devices. /proc/interrupts lists interrupts used on the system, including those used by the PCMCIA layer.

The lspci command can be used to glean information about all PCI buses and PCI devices in the system. If you’re using a multifunction CardBus card on your laptop, the output will contain entries for each supported CardBus function.

Looking at the Sources

For an example multifunction CardBus driver offering both modem and Ethernet functionalities, look at drivers/net/pcmcia/xircom_tulip_cb.c for 2.4 kernels or drivers/net/tulip/xircom_tulip_cb.c for 2.6 kernels.

Wireless PC Card drivers can be found in the drivers/net/wireless/ subdirectory. drivers/ide/legacy/ide-cs.c is the Card Services driver used by PC Card memory devices that emulate IDE. See drivers/char/pcmcia/serial_cs.c (2.4 kernels) or drivers/serial/serial_cs.c (2.6 kernels) for the Card Services driver used by PCMCIA cards such as GPRS, GPS, or modems, which communicate using a serial transport.

Sreekrishnan Venkateswaran has been working for IBM India since 1996. His recent Linux projects include putting Linux onto a wristwatch, an MP3 player, and a pacemaker. You can reach Krishnan at 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