You are on page 1of 54

Chapter

4
Writing Character Drivers

Tornado Device Driver Workshop

Copyright Wind River Systems

Wind River Systems

4-1

I/O system interface to a character driver. Driver initialization. Character device driver entry points. Supporting select( ) in a driver.

Writing Character Drivers

4.1

Introduction Writing Character Drivers Development Support Routines Supporting Select

Tornado Device Driver Workshop

Copyright Wind River Systems

Wind River Systems

4-2

Writing an I/O system vs. non-I/O system driver. Block vs. character device drivers. I/O system interface to driver.

Standard I/O System Interface


UNIX compatible Routines:
q q

open (lename, ags, mode) creat (lename, ags) read (fd, &buf, nBytes) write (fd, &buf, nBytes) ioctl (fd, command, arg) close (fd) remove (lename)

Tornado Device Driver Workshop

Copyright Wind River Systems

Wind River Systems

4-3

A Note on delete( )
The delete entry point is accessed by an application program calling remove( ). The delete entry point is intended to remove les; it is not used by most drivers. Two types of drivers use it:
q

File systems, e.g. dosFsLib uses it to remove les on a DOS le system Network drivers, e.g. netDrv uses it to request that a remote le be removed

The delete entry is not intended to be used to remove drivers or devices.

Tornado Device Driver Workshop

Copyright Wind River Systems

Wind River Systems

4-4

I/O System Overview

Application

open()

creat()

close()

read()

write()

ioctl()

ioLib

Driver A

Driver B

Driver C

Tornado Device Driver Workshop

Copyright Wind River Systems

Wind River Systems

4-5

The I/O system provides an application some degree of device independence.

When to Write A Driver With A Standard Interface


Flexible input/output to multiple devices with the same interface is needed. Stream devices require support. Use of I/O redirection. is desired An interface familiar to application programmers is desired. Support for select( ) is desired.

Tornado Device Driver Workshop

Copyright Wind River Systems

Wind River Systems

4-6

The VxWorks shell calls creat( ), read( ) or write( ), and close( ) when performing redirection. I/O redirection allows tasks to specify input or output sources at run time. In VxWorks, see ioGlobalStdSet( ) and ioTaskStdSet( ) for details.

When NOT to Write A Driver With A Standard Interface


Device does not lend itself to I/O system model (not stream oriented). Speed is very critical.

Tornado Device Driver Workshop

Copyright Wind River Systems

Wind River Systems

4-7

While there is little overhead in the VxWorks I/O system, there is some additional code which must be executed.

Drivers Using The Standard Interface


Block Drivers
q q

File system-based Random access and sequential Transfer data in multi-byte blocks Examples: oppy or hard disks, ramdisk Any other device Examples: serial or graphical input device such as mouse or graphics tablet

Non-block Drivers
q q

Tornado Device Driver Workshop

Copyright Wind River Systems

Wind River Systems

4-8

Block Devices
Block devices are designed to support a le system. VxWorks-supported le systems:
q q

MS-DOS Raw Tape

Tornado Device Driver Workshop

Copyright Wind River Systems

Wind River Systems

4-9

I/O System Overview


Devices Non-Block Drivers I/O System open creat read write File Systems ioctl close DOS delete -----Raw Application A B

Block Drivers

C D

Tornado Device Driver Workshop

Copyright Wind River Systems

Wind River Systems

4-10

For block devices, the I/O system calls le system routines which in turn make driver calls. For non-block devices, the I/O system directly calls driver routines.

Device Reference
lename
device descriptor

fd

deviceId

Application

I/O System

Device Driver

Application references device by le name and le descriptor Device driver references device by device descriptor and device ID
Tornado Device Driver Workshop Copyright Wind River Systems Wind River Systems

4-11

Device drivers typically do not know (or care) about the le name (device name) or le descriptor. The I/O system communicates with the driver via device descriptors and device IDs (dened by the driver).

Driver Table
creat delete open close read write ioctl

0 1 2 3 Driver Number
Use iosDrvShow( ) to display the driver table.
myOpen NULL myOpen myClose myRead myWrite myIoctl

Tornado Device Driver Workshop

Copyright Wind River Systems

Wind River Systems

4-12

Used by I/O system to access driver entry points Fixed in size, but congurable by the NUM_DRIVERS denition in congAll.h. Commonly contains the same routine for both the creat( ) and open( ) entry points. Normally contains a null entry point for the delete( ) routine .

Device List
/pipe/abc 2 /pipe/xyz 2 /tyCo/0 1 serial driver data sacto: 4

pipe driver data

pipe driver data

netDrv driver data

Use devs( ) to see list

Tornado Device Driver Workshop

Copyright Wind River Systems

Wind River Systems

4-13

Used by I/O system to match a device name to a driver for open( ), creat( ) and delete( ) the 3 routines that identify a le by lename (not le descriptor) Doubly linked list of structures One structure per device Number of entries on list is dynamic Multiple device list structures may access the same driver (i.e. a driver may support multiple devices). The matching above is done by comparing le name; the access is by drvNum. Only accessed by I/O system during an open(), creat() and remove().

The Device Descriptor Structure


DL_NODE

Driver independent DEV_HDR

device name driver # Index into Driver Table device specic data

Driver dependent

Every device has a device descriptor linked to the device list. The rst member of the structure is a DEV_HDR. The driver-dependent portion contains device specic information.
Tornado Device Driver Workshop Copyright Wind River Systems Wind River Systems

4-14

The rst member of the device descriptor struct is a DEV_HDR:


typedef struct { DL_NODE node; short drvNum; char * name; } DEV_HDR;

Typical driver declaration:


#include "iosLib.h" ... typedef struct { DEV_HDR devHdr; /* driver specific information follows */ . . . } XX_DEV;

File Descriptor Table


Driver # 3 4 5 6 7 ...
Index into Driver Table Driver dependent value

Device ID

Use iosFdShow( ) to see table.


Tornado Device Driver Workshop Copyright Wind River Systems

Wind River Systems

4-15

Used by I/O system to identify driver after a device or le is opened. A le descriptor obtained by a successful open( ) or creat( ) is an index into this table. Fixed in size, but congurable by the NUM_FILES deninition in congAll.h. The above description is simplied. Dened in iosLib.c (not the header le!) as a FD_ENTRY, it also contains the lename, an inuse ag, and a pointer to the DEV_HDR.

Writing Character Drivers

Introduction 4.2 Writing Character Drivers Development Support Routines Supporting Select

Tornado Device Driver Workshop

Copyright Wind River Systems

Wind River Systems

4-16

Installing a driver Driver entry points Removing a driver

Opening A Device
fd = open (/myDevice, O_READ, 0)
Device List Driver Table
creat 0 1 2 myOpen NULL myOpen myClose myRead myWrite myIoctl delete open close read write ioctl

myOpen (pDevHdr, NULL, O_READ, 0)

Tornado Device Driver Workshop

Copyright Wind River Systems

Wind River Systems

4-17

Searches through device list for longest string match of name / myDevice. Lets say nds a match for /myDevice... Using the driver number stored in the DEV_HDR of the matching device descriptor, the I/O system indexes the driver table to call the appropriate driver xxOpen routine. The pDevHdr is a pointer to the matching device descriptor structure. Driver xxOpen( ) routine, i.e. myOpen( ), returns a device identier. The sequence above applies as well for creat( ).

Returning from Drivers xxOpen( )


fd = open (/myDevice, O_READ, 0)
le descriptor

driver number

deviceId

File Descriptor Table

deviceId = myOpen (pDevHdr, ... )


Tornado Device Driver Workshop Copyright Wind River Systems Wind River Systems

4-18

On successful return from drivers xxOpen:


q

I/O system adds driver number and device ID to the le descriptor table: Device ID returned from drivers xxOpen (usually it is pDevHdr, the address of the device descriptor)

Thereafter, fd identies the device. The le descriptor is in index into the le descriptor table (hence, alleviating the need for searches).

Reading from A Device


read (fd, &buf, nBytes)
File Descriptor Table Driver Table
creat 0 1 2 myOpen NULL myOpen myClose myRead myWrite myIoctl delete open close read write ioctl

myRead (devId, &buf, nBytes)


Tornado Device Driver Workshop Copyright Wind River Systems Wind River Systems

4-19

he fd, returned from open( ), is used as an index into the File Descriptor Table. The driver number from the File Descriptor Table is used as an index into the Driver Table, allowing invocation of the drivers xxRead( ) routine. The devId is the device ID (stored in the File Descriptor Table) dened by the return value of the drivers xxOpen( ).

Writing to A Device
write (fd, &buf, nBytes)
File Descriptor Table Driver Table
creat 0 1 2 myOpen NULL myOpen myClose myRead myWrite myIoctl delete open close read write ioctl

myWrite (devId, &buf, nBytes)


Tornado Device Driver Workshop Copyright Wind River Systems Wind River Systems

4-20

The mechanism for write (as well as ioctl and close) is identical to read.

I/O System Overview


Startup Code Application

open()

creat()

close()

read()

write()

ioctl()

ioLib

iosDrvInstall() iosDevAdd()

iosLib

xxDrv()

xxDevCreate()

xxOpen()

xxClose()

xxRead()

xxWrite()

xxIoctl()

xxDrv

Tornado Device Driver Workshop

Copyright Wind River Systems

Wind River Systems

4-21

The xxDrv( ) and xxDevCreate( ) driver entry points are not accessed through ioLib. These routines install the driver and devices into the I/O system. The xxDrv( ) and xxDevCreate( ) routines are normally called during system startup (often from the usrRoot task). The xx in xxDrv denotes the driver handler name, typically the same as the name prexing any global variable (including function names), e.g., xxDrv( ), xxOpen( ), xxDrvNum.

Device Driver Functions


xxDrv( )
q q

Installs driver in driver table -- iosDrvInstall( ) Performs any driver initialization Called once and once only Adds device to the system device list -- iosDevAdd( ) Performs any device initialization Called once for each device These are optional

xxDevCreate( )
q q

Any of the 6 driver entry points


q

Tornado Device Driver Workshop

Copyright Wind River Systems

Wind River Systems

4-22

Driver initialization may include allocating memory and initialization data structures to default values. Device initialization may include actually writing to the device.

Initializing A Driver
STATUS xxDrv (args...)
xx is the driver handler, e.g. pipeDrv or tyCoDrv. The arguments are driver dependent. Calls iosDrvInstall( ) to add driver to I/O system driver table. Should be called once only. Often called from user initialization code.

Tornado Device Driver Workshop

Copyright Wind River Systems

Wind River Systems

4-23

Adding Driver into Driver Table


iosDrvInstall (xxCreat, xxDelete, xxOpen, xxClose, xxRead, xxWrite, xxIoctl)
All routines are optional use NULL if routine not provided. Returns ERROR or driver number on success. Example:
fooDrvNum = iosDrvInstall (fooOpen, NULL, fooOpen, fooClose, fooRead, fooWrite, fooIoctl)

Tornado Device Driver Workshop

Copyright Wind River Systems

Wind River Systems

4-24

Remember this should only be called once for driver. Driver number should be stored in global variable for future reference.

Example xxDrv( ) Routine


1 LOCAL int fooDrvNum; 2 3 STATUS fooDrv (void) 4 { 5 /* If driver already installed, just return */ 6 if (fooDrvNum > 0) 7 return (OK); 8 9 /* driver initialization, if any, here */ 10 11 /* add driver to driver table */ 12 if ((fooDrvNum = iosDrvInstall (fooOpen, NULL, 13 fooOpen, fooClose, fooRead, fooWrite, 14 fooIoctl)) == ERROR) 15 return (ERROR); 16 return (OK); 17 }
Tornado Device Driver Workshop Copyright Wind River Systems

Wind River Systems

4-25

As shown above, installing a driver open( ) routine in the creat( ) entry point is a common practice.

Creating an Instance of Device


STATUS xxDevCreate (devName, args...)
Args are driver dependent. Adds the specied device name to the system device list by calling iosDevAdd( ). Performs any device specic initialization. Typically, allocates memory for device descriptor structure. If the driver is not installed (xxDrvNum < 1), sets errno to S_ioLib_NO_DRIVER and returns ERROR.

Tornado Device Driver Workshop

Copyright Wind River Systems

Wind River Systems

4-26

The args are used for device-specic information such as board address, channel number or other identier.

Adding to the Device List


STATUS iosDevAdd (pDevHdr, devName, drvNum)
pDevHdr devName drvNum Pointer to DEV_HDR Name of device Driver number returned from iosDrvInstall( )

Adds the device descriptor to the device list. Initializes the DEV_HDR with the device name and driver number.

Tornado Device Driver Workshop

Copyright Wind River Systems

Wind River Systems

4-27

Fails if devName already exists in the device list. This routine mallocs memory to hold devName. It initializes the DEV_HDR with the drvNum and devName and adds it to the linked list.

Example Device Creation


1 LOCAL int fooDrvNum; /* initialized in fooDrv() */ ... STATUS fooDevCreate (char * devName) { FOO_DEV * pFooDev; /* Check if driver is installed */ if (fooDrvNum < 1) { errno = S_ioLib_NO_DRIVER; return (ERROR); }

2 3 4 5 6 7 8 9 10 11 12

/* Allocate and zero device structure */ if ((pFooDev = (FOO_DEV *) malloc (sizeof (FOO_DEV))) = = NULL) 13 return (ERROR); 14 bzero (pFooDev, sizeof (FOO_DEV));
Tornado Device Driver Workshop Copyright Wind River Systems Wind River Systems

4-28

xxDevCreate( ) may take optional device-specic arguments. If you dynamically allocate the device descriptor structure (FOO_DEV), remember to zero out its contents.

Example (contd)
15 */ 16 */ 17 18 19 20 21 22 23 24 /* Add device to the device list */ if (iosDevAdd (&pFooDev->devHdr, devName, fooDrvNum) == ERROR) { free ((char *) pFooDev); return (ERROR); } return (OK); } /* Perform any device-specific initialization here /* Init device specific portion of FOO_DEV here

Tornado Device Driver Workshop

Copyright Wind River Systems

Wind River Systems

4-29

Driver Open Routine


int xxOpen (pDevHdr, name, ags, mode)
pDevHdr name ags mode Pointer to device descriptor Pointer to device name remainder Flags from open( ): O_RDONLY, etc. Permissions from open( ) not normally used in character driver

Returns ERROR or device identier, usually pDevHdr. Initializes the specic channel, if appropriate. Optionally, fails multiple attempts to open the same device.
Tornado Device Driver Workshop Copyright Wind River Systems Wind River Systems

4-30

Name is a pointer to the characters remaining after the device name specied in the xxDevCreate( ). For example, if the device was create with
xxDevCreate ("/myDev",...)

and opened with


fd = open ("/myDev/4", 0)

then name would be a pointer to /4. For most drivers, this will be a null pointer. If return value from xxOpen( ) is ERROR, then the open has failed and nothing is added to the le descriptor table.
The le descriptor table holds a pointer to the DEV_HDR, pointer to lename, an inuse ag, and a value. The value holds the drivers return from xxOpen(). This value is the rst argument passed to read, write, ioctl, and close. The inuse ag is a BOOL to indicate whether this is a valid entry, like zeroing the inode number in a UNIX directory.

Return Value from xxOpen()


ERROR to cause application open to fail. Pointer to the device descriptor, when information is maintained only by device. A driver-specic identier, typically a pointer to a private driver structure.

Tornado Device Driver Workshop

Copyright Wind River Systems

Wind River Systems

4-31

To conrm that the return value from xxOpen( ) is being saved in le descriptor table, use iosFdValue( ). If a driver specic structure is chosen, there are two choices: 1. malloc memory as needed, this is good in that it does not limit the number of opens, bad in that if many open/closes are made, memory will become fragmented. 2. Declare a xed sized array of structures, this is good because it avoids the memory fragmentation problem, bad in that if the number of opens are limited and if structure is large or array large then memory is wasted. This approach is also slightly faster.

Example
1 typedef struct
2 3 4 5 6 7 8 9 10 11 12 13 14 { MY_DEV * pMyDev; int offset; } MY_DEV_PER_FD: ... int myOpen (DEV_HDR * pDevHdr, char * name, int flags) ... if ((pMyDevPerFd = (MY_DEV_PER_FD *) malloc (sizeof (MY_DEV_PER_FD)) == NULL) return (ERROR); pMyDevPerFd->pMyDev = (MY_DEV *) pDevHdr; pMyDevPerFd->offset = 0; return ((int) pMyDevPerFd); }

Tornado Device Driver Workshop

Copyright Wind River Systems

Wind River Systems

4-32

Consequences of using the code above:


q q

Must supply a myClose( ) to free memory allocated in myOpen( ). To maintain current device offset, driver would have to modify offset on each myRead( ), myWrite( ), and myIoctl( ), if an FIOSEEK command is implemented. Remember that the rst parameter to each I/O call will now be a pointer to your MY_DEV_PER_FD structure and not to MY_DEV:
int myRead (int deviceId, char * buf, int nBytes) { MY_DEV_PER_FD *pMyDevPerFd = (MY_DEV_PER_FD *) deviceId; MY_DEV * pMyDev = pMyDevPerFd->pMyDev; ...

Read/Write Routine
int xxWrite (deviceId, pBuf, nBytes) int xxRead (deviceId, pBuf, nBytes)
deviceId pBuf nBytes Value returned from driver xxOpen( ) Pointer to buffer to read or write The number of bytes to read or write

Tornado Device Driver Workshop

Copyright Wind River Systems

Wind River Systems

4-33

These routines should return:


q q

the number of bytes successfully read or written, or ERROR on failure, or zero to indicate end-of-le or end-of-device (if meaningful)

Ioctl Routine
int xxIoctl (deviceId, cmd, arg)
deviceId cmd arg Value returned from driver xxOpen( ) Action to perform Optional cmd-specic int, often used as a pointer

Return value is cmd specic, except that ERROR is reserved to indicate failure. Unknown commands should be failed and errno set to S_ioLib_UNKNOWN_REQUEST.
Tornado Device Driver Workshop Copyright Wind River Systems

Wind River Systems

4-34

The cmds are usually dened in a header le used by the driver and the application program. The I/O system itself handles the FIOGETNAME command which copies the lename associated with le descriptor to arg. Include ioLib.h.

Ioctl Arguments
Arg may be dened as:
q q

an integer value an address to input data an address to output data not used

When arg is used as an address, it is often an address of a structure.

Tornado Device Driver Workshop

Copyright Wind River Systems

Wind River Systems

4-35

Ioctl Example
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 LOCAL FOO_STATE fooState; LOCAL int fooSpeed; LOCAL SEM_ID fooSem; ... int fooIoctl (int fooDevId, int cmd, int arg) { int status; switch (cmd) { case FOO_SPEED_SET: fooSpeed = arg; status = OK; break; case FOO_SPEED_GET: if (arg) *(int *)arg = fooSpeed; status = fooSpeed; break;
Copyright Wind River Systems Wind River Systems

Tornado Device Driver Workshop

4-36

Example demonstrates
q q

Passing an int (FOO_SPEED_SET) to a driver Returning an int (FOO_SPEED_GET) from a driver Passing the contents of a structure (FOO_STATE_SET) to a driver Returning the contents of a structure (FOO_STATE_GET) from a driver

Line 16-17: For demonstration purposes, these lines show how fooSpeed may be either returned or stored at the address of arg. Normally, a driver passes fooSpeed back one way or the other (not both ways as shown). This assumes that fooSpeed can not have a value of -1 (ERROR). The test (line 16) for non-zero arg is precautionary.

Ioctl Example (contd)


20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 case FOO_STATE_SET: semTake (fooSem, WAIT_FOREVER); fooState = * (FOO_STATE *) arg; semGive (fooSem); status = OK; break; case FOO_STATE_GET: semTake (fooSem, WAIT_FOREVER); * (FOO_STATE *) arg = fooState; semGive (fooSem); status = OK; break; default: errno = S_ioLib_UNKNOWN_REQUEST; status = ERROR; } return (status); }
Copyright Wind River Systems

Tornado Device Driver Workshop

Wind River Systems

4-37

In this example, the use of arg is cmd dependent:


FOO_SPEED_SET FOO_SPEED_GET FOO_STATE_SET FOO_STATE_GET

int Pointer to an int Pointer to a structure Pointer to a structure

Since assignment of structures is non-atomic, the FOO_STATE_SET and FOO_STATE_GET commands are protected by a mutex semaphore.

The xxClose Routine


STATUS xxClose (devId)
If driver is managing a private structure referenced by devId, perform any reset action if necessary. Free memory allocated in your xxOpen( ), if any. Most drivers do not have a close routine.

Tornado Device Driver Workshop

Copyright Wind River Systems

Wind River Systems

4-38

Writing Character Drivers


Introduction Writing Character Drivers 4.3 Development Support Routines Supporting Select

Tornado Device Driver Workshop

Copyright Wind River Systems

Wind River Systems

4-39

Routines that may be useful for developing and debugging character drivers.

Deleting a Device
void iosDevDelete (pDevHdr)
Removes the device descriptor addressed by pDevHdr from the device list. Inverse of iosDevAdd( ). Prevents any new opens on the device. Any le descriptors already opened remain unaffected (as long as the device descriptor itself is not freed or modied).

Tornado Device Driver Workshop

Copyright Wind River Systems

Wind River Systems

4-40

Removing a Driver
iosDrvRemove (drvNum, forceClose)
Removes driver from driver table and deletes any associated devices from the device list. If forceClose is TRUE, closes all open les associated with driver. If forceClose is FALSE, returns ERROR if any les are open.

Tornado Device Driver Workshop

Copyright Wind River Systems

Wind River Systems

4-41

If forceClose is TRUE and driver has an xxClose( ) routine, routine will be called for each open le. This routine does not free memory allocated for a device list structure.

Getting A Device Descriptor


DEV_HDR * iosDevFind (name, pNameTail)
name pNameTail remainder Returns a pointer to the DEV_HDR associated with name or, if not found, to the default device, or NULL if no default device dened. Routine initializes pNameTail with a pointer to the part of name which was not matched with the device name. Sometimes useful in debugging or removing a device. Device name Pointer to string of name

Tornado Device Driver Workshop

Copyright Wind River Systems

Wind River Systems

4-42

If pNameTail points to a null character, the match was exact. If pNameTail points to name, there was no match.

Example Remove Device Routine


1 #include "vxWorks.h" 2 #include "iosLib.h" 3 4 STATUS rmDev (char * devName) 5 { 6 DEV_HDR *pDevHdr; 7 char * pNameTail; 8 9 pDevHdr = iosDevFind (devName, &pNameTail); 10 11 if (pDevHdr == NULL || *pNameTail != \0) 12 return (ERROR); 13 14 iosDevDelete (pDevHdr); 15 return (OK); 16 }

Tornado Device Driver Workshop

Copyright Wind River Systems

Wind River Systems

4-43

Important limitations in above routine:


q q

Does not free the device descriptor If the device descriptors are dynamically allocated in the xxDevCreate( ) routine, using this routine alone would create a memory leak Does not close les there may still be open le descriptors referencing this device It would be safe to free the device descriptor only after all referencing le descriptors were closed The device descriptor itself may reference resources which would not be freed, e.g. semaphores, message queues, or private buffers

File Descriptor to Device ID Conversion


int iosFdValue (fd)
Returns the Device ID (return value from drivers xxOpen) for the device associated with the le descriptor fd. Returns ERROR if fd is invalid.

Tornado Device Driver Workshop

Copyright Wind River Systems

Wind River Systems

4-44

Writing Character Drivers


Introduction Writing Character Drivers Development Support Routines 4.4 Supporting Select

Tornado Device Driver Workshop

Copyright Wind River Systems

Wind River Systems

4-45

Select initialization Supporting selects ioctl commands Notifying select when device has data to read or is ready to write

Supporting select( )
The select( ) call permits tasks to pend on multiple le descriptors. Task can block until data is available, or device can be written to. Allows for pending with a timeout. Supporting select( ) is optional. Appropriate only for I/O system character drivers.

Tornado Device Driver Workshop

Copyright Wind River Systems

Wind River Systems

4-46

Select() Tools
Select uses a struct fd_set which may be viewed as an array of bits:
... 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0

Each bit represents a le descriptor of interest. Macros to help manipulate a struct fd_set: FD_SET (fd, &fdset) FD_CLR (fd, &fdset) FD_ZERO (&fdset) FD_ISSET (fd, &fdset) Sets the fd bit in fdset Clears the fd bit in fdset Clears all bits in fdset Tests if the fd bit is set in fdset

Tornado Device Driver Workshop

Copyright Wind River Systems

Wind River Systems

4-47

Using select( )
int select (width, pReadFds, pWriteFds, pExceptFds, pTimeOut)
width pReadFds pWriteFds pExceptFds Number of bits to examine in the struct fd_sets below Pointer to a struct fd_set which holds the read le descriptors of interest Pointer to a struct fd_set which holds the write le descriptors of interest Pointer to a struct fd_set which holds the exception le descriptors of interest not implemented Pointer to a struct timeval, NULL implies wait forever
Copyright Wind River Systems Wind River Systems

pTimeOut
Tornado Device Driver Workshop

4-48

The fd_set holds a bit per le descriptor, i.e. le descriptor 0 is bit 0, le descriptor 1 is bit 1 and so forth. Returns number of fds that are ready, zero on time out, or ERROR (e.g. if driver does not support select). On return, select has modied the struct fd_set so that only those bits corresponding to the le descriptors which meet the requested condition are set. The selectLib is UNIX BSD 4.3 compatible. VxWorks naming conventions are violated to maintain compatibility.

Select Example
1 struct fd_set readFds;
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 int fds[NUM_FDS], width = 0; ... FD_ZERO (&readFds); for (i=0; i<NUM_FDS; i++) { FD_SET (fds[i], &readFds); width = (fds[i] > width) ? fds[i] : width; } width++; if (select(width, &readFds,NULL,NULL,NULL) == ERROR) return (ERROR); for (i=0; i<NUM_FDS; i++) { if (FD_ISSET (fds[i], &readFds)) { /* do something now that fds[i] is ready */ } }
Copyright Wind River Systems Wind River Systems

Tornado Device Driver Workshop

4-49

When select( ) returns, the struct fd_set has been modied so that only the bits corresponding to the le descriptors that are ready are set. If a read, write, or exception argument is NULL, that argument is ignored. Must include selectLib.h.

Select Overview
xxDevCreate( ) selWakeupListInit( )

selNodeAdd( )

Application

selWakeup( ) xxIoctl (...,FIOSELECT,...) select( ) xxIoctl (...,FIOUNSELECT,...) selNodeDelete( )

selWakeupAll (...,SELWRITE)

ready to write

selWakeupAll (...,SELREAD)

data to read

selectLib
Tornado Device Driver Workshop Copyright Wind River Systems

Driver
4-50

Wind River Systems

Select Initialization
Driver must declare a SEL_WAKEUP_LIST structure, typically in the device descriptor structure. xxDevCreate( ) must call:
selWakeupListInit (pWakeupList) SEL_WAKEUP_LIST * pWakeupList;

When device becomes available for writes, call:


selWakeupAll (pWakeupList, SELWRITE);

When device obtains data for reads, call:


selWakeupAll (pWakeupList, SELREAD);

Tornado Device Driver Workshop

Copyright Wind River Systems

Wind River Systems

4-51

The SEL_WAKEUP_LIST structure is usually declared as part of the device descriptor structure:
typedef struct { DEV_HDR devHdr; ... SEL_WAKEUP_LIST selWakeupList; ... } FOO_DEV; ... STATUS fooDevCreate (...) { ... pFooDev = (FOO_DEV *) malloc (sizeof (FOO_DEV)); selWakeupListInit (&pFooDev->selWakeupList); ... }

The selWakeupAll( ) routine may be called at interrupt time.

Select Commands in ioctl


Command FIOSELECT
selNodeAdd (pSelWakeupList, (SEL_WAKEUP_NODE *) arg); if ((selWakeupType ((SEL_WAKEUP_NODE *) arg) == SELREAD) && thereIsDataToRead) selWakeup ((SEL_WAKEUP_NODE *) arg); if ((selWakeupType ((SEL_WAKEUP_NODE *) arg) == SELWRITE) && deviceCanBeWrittenTo) selWakeup ((SEL_WAKEUP_NODE *) arg);

Command FIOUNSELECT
selNodeDelete (pSelWakeupList, (SEL_WAKEUP_NODE *) arg);

Tornado Device Driver Workshop

Copyright Wind River Systems

Wind River Systems

4-52

In FIOSELECT code, thereIsDataToRead and deviceCanBeWrittenTo must be replaced with appropriate device-dependent code. Example:
int fooIoctl (int deviceId, int cmd, int arg) { FOO_DEV * pFooDev = (FOO_DEV *) deviceId; switch (cmd) { case FIOSELECT: selNodeAdd (&pFooDev->selWakeupList, (SEL_WAKEUP_NODE *) arg); ... } }

Summary
xxDrv( ) called once per driver
q q

call iosDrvInstall( ) return STATUS allocate and initialize a device descriptor structure call iosDevAdd( ) return STATUS

xxDevCreate( ) called once per device


q q

Tornado Device Driver Workshop

Copyright Wind River Systems

Wind River Systems

4-53

Summary
xxOpen( )
q q

initialize any per-open structure return device ID or ERROR on failure reset any per open structure free any memory allocated during xxOpen( )

xxClose( )
q q

xxRead( ) / xxWrite( ) xxIoctl( )


q q

process known commands fail unknown commands

Tornado Device Driver Workshop

Copyright Wind River Systems

Wind River Systems

4-54