Next Previous Contents

9. The Socket Driver Layer

In the Linux PCMCIA model, the ``Socket Services'' layer is a private API intended only for the use of Card Services. The API is based loosely on the PCMCIA Socket Services specification, but is oriented towards support for the common x86 laptop host controller types.

9.1 Card Services entry points for socket drivers

Card Services provides special entry points for registering and unregistering socket drivers:

typedef int (*ss_entry_t)(u_int sock, u_int cmd, void *arg);
extern int register_ss_entry(int nsock, ss_entry_t entry);
extern void unregister_ss_entry(ss_entry_t entry);

The socket driver invokes register_ss_entry with nsock indicating how many sockets are owned by this driver, and entry pointing to the function that will provide socket services for these sockets. The unregister_ss_entry routine can be safely invoked whenever Card Services does not have any callback functions registered for sockets owned by this driver.

9.2 Services provided by the socket driver

Socket Services calls have the following form:

#include "pcmcia/ss.h"

int (*ss_entry)(u_int sock, int service, void *arg);

Non-zero return codes indicate that a request failed.

SS_InquireSocket

int (*ss_entry)(u_int sock, SS_InquireSocket, socket_cap_t *cap);
The socket_cap_t data structure is given by:
typedef struct socket_cap_t {
        u_int           features;
        u_int           irq_mask;
        u_int           map_size;
        u_char          pci_irq;
        u_char          cardbus;
        struct bus_operations *bus;
} socket_cap_t;

The SS_InquireSocket service is used to retrieve socket capabilities. The irq_mask field is a bit mask indicating which ISA interrupts can be configured for IO cards. The map_size field gives the address granularity of memory windows. The pci_irq field, if not zero, is the PCI interrupt number assigned to this socket. It is independent of irq_mask, and can actually be used in any situation where exactly one interrupt is associated with a specific socket. For CardBus bridges, the cardbus field should be non-zero, and gives the PCI bus number of the CardBus side of the bridge.

For sockets that do not directly map cards into the host IO and memory space, the bus field is a pointer to a table of entry points for IO primitives for this socket.

The following flags may be specified in features:

SS_CAP_PAGE_REGS

Indicates that this socket supports full 32-bit addressing for 16-bit PC Card memory windows.

SS_CAP_VIRTUAL_BUS

Indicates that 16-bit card memory and IO accesses must be performed using the bus operations table, rather than using native bus operations.

SS_CAP_MEM_ALIGN

Indicates that memory windows must be aligned by the window size.

SS_CAP_STATIC_MAP

Indicates that memory windows are statically mapped at fixed locations in the host address space, and cannot be repositioned.

SS_CAP_PCCARD

Indicates that this socket supports 16-bit PC cards.

SS_CAP_CARDBUS

Indicates that this socket supports 32-bit CardBus cards.

SS_RegisterCallback

int (*ss_entry)(u_int sock, SS_RegisterCallback, ss_callback_t *call);
The ss_callback_t data structure is given by:
typedef struct ss_callback_t {
        void            (*handler)(void *info, u_int events);
        void            *info;
} ss_callback_t;

The SS_RegisterCallback service sets up a callback function to be invoked when the socket driver receives card status change events. To unregister a callback, this function is called with a handler value of NULL. Only one callback function can be registered per socket.

The handler will be called with the value of info that was passed to SS_RegisterCallback for this socket, and with a bit map of events in the events parameter. The following events are defined:

SS_DETECT

A card detect change (insertion or removal) has been detected.

SS_READY

A memory card's ready signal has changed state.

SS_BATDEAD

A memory card has raised the battery-dead signal.

SS_BATWARN

A memory card has raised the battery-low signal.

SS_STSCHG

An IO card has raised the status change signal.

SS_GetStatus

int (*ss_entry)(u_int sock, SS_GetStatus, u_int *status);

The SS_GetStatus service returns the current status of this socket. The status parameter will be constructed out of the following flags:

SS_WRPROT

The card is write-protected.

SS_BATDEAD

A memory card has raised the battery-dead signal.

SS_BATWARN

A memory card has raised the battery-low signal.

SS_READY

A memory card has raised its ready signal.

SS_DETECT

A card is present.

SS_POWERON

Power has been applied to the socket.

SS_STSCHG

An IO card has raised the status change signal.

SS_CARDBUS

The socket contains a CardBus card (as opposed to a 16-bit PC Card).

SS_3VCARD

The card must be operated at no more than 3.3V.

SS_XVCARD

The card must be operated at no more than X.XV (not yet defined).

SS_GetSocket, SS_SetSocket

int (*ss_entry)(u_int sock, SS_GetSocket, socket_state_t *);
int (*ss_entry)(u_int sock, SS_SetSocket, socket_state_t *);
The socket_state_t data structure is given by:
typedef struct socket_state_t {
        u_int           flags;
        u_int           csc_mask;
        u_char          Vcc, Vpp;
        u_char          io_irq;
} socket_state_t;

The csc_mask field indicates which event types should generate card status change interrupts. The following event types can be monitored:

SS_DETECT

Card detect changes (insertion or removal).

SS_READY

Memory card ready/busy changes.

SS_BATDEAD

Memory card battery-dead changes.

SS_BATWARN

Memory card battery-low changes.

SS_STSCHG

IO card status changes.

The Vcc and Vpp parameters are in units of 0.1 volts. If non-zero, io_irq specifies an interrupt number to be assigned to the card, in IO mode. The following fields are defined in flags:

SS_PWR_AUTO

Indicates that the socket should automatically power up sockets at card insertion time, if supported.

SS_IOCARD

Indicates that the socket should be configured for ``memory and IO'' interface mode, as opposed to simple memory card mode.

SS_RESET

Indicates that the card's hardware reset signal should be raised.

SS_SPKR_ENA

Indicates that speaker output should be enabled for this socket.

SS_OUTPUT_ENA

Indicates that data signals to the card should be activated.

SS_GetIOMap, SS_SetIOMap

int (*ss_entry)(u_int sock, SS_GetIOMap, pccard_io_map *);
int (*ss_entry)(u_int sock, SS_SetIOMap, pccard_io_map *);
The pccard_io_map data structure is given by:
typedef struct pccard_io_map {
        u_char          map;
        u_char          flags;
        u_short         speed;
        u_short         start, stop;
} pccard_io_map;

The SS_GetIOMap and SS_SetIOMap entries are used to configure IO space windows. IO windows are assumed to not support address translation. The Linux Card Services layer assumes that each socket has at least two independently configurable IO port windows.

The map field specifies which IO map should be accessed. The speed field is the map access speed in nanoseconds. The start and stop fields give the lower and upper addresses for the IO map. The flags field is composed of the following:

MAP_ACTIVE

Specifies that the address map should be enabled.

MAP_16BIT

Specifies that the map should be configured for 16-bit accesses (as opposed to 8-bit).

MAP_AUTOSZ

Specifies that the map should be configured to auto-size bus accesses in response to the card's IOCS16 signal.

MAP_0WS

Requests zero wait states, as opposed to standard ISA bus timing.

MAP_WRPROT

Specifies that the map should be write protected.

MAP_USE_WAIT

Specifies that access timing should respect the card's WAIT signal.

MAP_PREFETCH

Specifies that this map may be configured for prefetching.

SS_GetMemMap, SS_SetMemMap

int (*ss_entry)(u_int sock, SS_GetMemMap, pccard_mem_map *);
int (*ss_entry)(u_int sock, SS_SetMemMap, pccard_mem_map *);
The pccard_mem_map data structure is given by:
typedef struct pccard_mem_map {
        u_char          map;
        u_char          flags;
        u_short         speed;
        u_long          sys_start, sys_stop;
        u_int           card_start;
} pccard_mem_map;

The map field specifies the map number. The speed field specifies an access speed in nanoseconds. The sys_start and sys_stop fields give the starting and ending addresses for the window in the host's physical address space. The card_start value specifies the card address to be mapped to sys_start. The Linux Card Services layer assumes that each socket has at least four independently configurable memory windows.

MAP_ACTIVE

Specifies that the address map should be enabled.

MAP_16BIT

Specifies that the map should be configured for 16-bit accesses (as opposed to 8-bit).

MAP_AUTOSZ

Specifies that the map should be configured to auto-size bus accesses in response to the card's IOCS16 signal.

MAP_0WS

Requests zero wait states, as opposed to standard ISA bus timing.

MAP_WRPROT

Specifies that the map should be write protected.

MAP_ATTRIB

Specifies that the map should be for attribute (as opposed to common) memory.

MAP_USE_WAIT

Specifies that access timing should respect the card's WAIT signal.

SS_GetBridge, SS_SetBridge

int (*ss_entry)(u_int sock, SS_GetBridge, cb_bridge_map *);
int (*ss_entry)(u_int sock, SS_SetBridge, cb_bridge_map *);
The cb_bridge_map data structure is given by:
typedef struct cb_bridge_map {
        u_char          map;
        u_char          flags;
        u_int           start, stop;
} cb_bridge_map;

The SS_GetBridge and SS_SetBridge entry points are used for configuring bridge address windows for CardBus devices. They are similar to the 16-bit IO and memory map services. It is assumed that each CardBus socket has at least two IO and two memory bridge windows. The flags field is composed of:

MAP_ACTIVE

Specifies that the address map should be enabled.

MAP_PREFETCH

Specifies that this map can be configured for prefetching.

MAP_IOSPACE

Specifies that this map should be for IO space (as opposed to memory space).

SS_ProcSetup

int (*ss_entry)(u_int sock, SS_ProcSetup, struct proc_dir_entry *base);

Card Services uses this entry point to give the socket driver a procfs directory handle under which it may create status files for a specific socket. It is the socket driver's responsbility to delete any proc entries before it is unloaded.

9.3 Supporting unusual socket architectures

The Socket Services interface is oriented towards socket controllers that allow PCMCIA cards to be configured to mimic native system devices with the same functionality. The ExCA standard specifies that socket controllers should provide two IO and five memory windows per socket, which can be independently configured and positioned in the host address space and mapped to arbitrary segments of card address space. Some controllers and architectures do not provide this level of functionality. In these situations, Socket Services can effectively virtualize the socket interface for client drivers.

On the client side (including internal Card Services uses), to use the virtualized socket interface, code must first specify:

#include "pcmcia/bus_ops.h"

All IO operations then need to be replaced with new bus-neutral forms. The following functions need to be virtualized:

The bus-neutral functions have a prefix of ``bus_'', with a new first argument, the bus operations table pointer returned by SS_InquireSocket. For example, inb(port) should be replaced with bus_inb(bus, port).

All the IO primitives are defined as macros that call entry points in the bus operations table. There is not a one-to-one mapping from IO primitives to bus operation entry points.

The bus operations table is defined as:

typedef struct bus_operations {
        void    *priv;
        u32     (*b_in)(void *bus, u32 port, s32 sz);
        void    (*b_ins)(void *bus, u32 port, void *buf,
                         u32 count, s32 sz);
        void    (*b_out)(void *bus, u32 val, u32 port, s32 sz);
        void    (*b_outs)(void *bus, u32 port, void *buf,
                          u32 count, s32 sz);
        void    *(*b_ioremap)(void *bus, u_long ofs, u_long sz);
        void    (*b_iounmap)(void *bus, void *addr);
        u32     (*b_read)(void *bus, void *addr, s32 sz);
        void    (*b_write)(void *bus, u32 val, void *addr, s32 sz);
        void    (*b_copy_from)(void *bus, void *d, void *s, u32 count);
        void    (*b_copy_to)(void *bus, void *d, void *s, u32 count);
        int     (*b_request_irq)(void *bus, u_int irq,
                                 void (*handler)(int, void *,
                                                 struct pt_regs *),
                                 u_long flags, const char *device,
                                 void *dev_id);
        void    (*b_free_irq)(void *bus, u_int irq, void *dev_id);
} bus_operations;

The priv field can be used for any purpose by the socket driver, for instance, to indicate which of several sockets is being addressed. The b_in, b_out, b_read, and b_write entry points each support byte, word, and dword operations, either byte-swapped or unswapped. The sz parameter is 0, 1, or 2 for byte, word, or dword accesses; -1 and -2 select word and dword unswapped accesses.


Next Previous Contents