Beruflich Dokumente
Kultur Dokumente
Barbeau
Outline:
1. Introduction
2. User-mode drivers
o Access to I/O map devices
o Access to memory map devices
o Finding devices
o Data link access with Linux PF_PACKET
3. Kernel-mode drivers
o Block/char device
o Polling/interrupt mode
o Major/minor number
o Registration of a device driver
o Interrupt handling
1. Introduction
2. User-mode drivers
The x86 architecture has two memory maps: one for memory and one for
I/O.
Access to the I/O map under Linux: root user, system calls ioperm(2) and
iopl(2).
April 1, 2002 1
Advanced Linux Programming M. Barbeau
iopl(): opens the entire I/O map (note: I/O mapped PCI devices are located
above 0x03ff.)
System calls: inb(), inw(), and inl(), for byte, word and long reading; outb(),
outw(), and outl(), for byte, word, long writing. (see sys/io.h)
Call mmap() with the file descriptor, offset and size (in the file). An address
mapped to the physical address space is returned. Address can be cast to a
data type and used like a normal pointer.
Example:
more /proc/bus/pci/device
Vendor ID: 10d9 (managed by PCI SIG), Device ID: 0531 (vender's
choice), also printed on the card.
April 1, 2002 2
Advanced Linux Programming M. Barbeau
This section contains code extracted from a user level Wireless (Ethernet)
LAN driver. Only the data link access is addressed. The complete example
available at:
http://www.scs.carleton.ca/~barbeau/Courses/SETP/index.html
Data:
// wireless interface configuration
struct Ifconfig
{
int sockid; // socket descriptor
int ifindex; // index of the interface
WLANAddr hwaddr; // MAC address
int mtu; // maximum transmission unit
};
// constructor
WLANProtocol::WLANProtocol(char * device, Outcome & retval)
{
// undefined the socket descriptor
ifconfig.sockid = -1;
April 1, 2002 3
Advanced Linux Programming M. Barbeau
retval = init();
}
((WLANProtocol *)arg)->Receive();
pthread_exit(NULL); // this statement should never be executed
}
Outcome WLANProtocol::init()
{
// (1) create device level socket
// - PF_PACKET : low level packet interface
// - SOCK_RAW : raw packets including link level header
// - ETH_P_ALL : all frames will be received
if ((ifconfig.sockid = socket(PF_PACKET, SOCK_RAW,
htons(ETH_P_ALL))) == -1)
{
aLog.log("WLANProtocol, cannot open socket: %s",
strerror(errno));
return NOK;
}
April 1, 2002 4
Advanced Linux Programming M. Barbeau
ifconfig.mtu = ifr.ifr_mtu;
aLog.log("WLANProtocol, MTU: %d", ifconfig.mtu);
return OK;
}
April 1, 2002 5
Advanced Linux Programming M. Barbeau
{
Message * msg; // received message
Outcome result;
unsigned char * buff; // pointer to received data
unsigned int i; // frame length
struct sockaddr_ll from; // source address of the message
socklen_t fromlen = sizeof(struct sockaddr_ll);
int error;
// (1) initialisation
#define THREADPERMSG
#ifdef THREADPERMSG
// (1.1) thread per message
aLog.log("WLANProtocol, thread per message model");
April 1, 2002 6
Advanced Linux Programming M. Barbeau
// create a thread
// the thread is reponsible for deleting "aDemuxArg" and "msg"
pthread_t msg_tid; // identifier of the thread
error = pthread_create(
&msg_tid, &attr, demux<DemuxArgClass, ProtocolUI>,
(void *)aDemuxArg);
if (error)
{
aLog.log("WLANProtocol, failed to create thread: %s", error);
delete msg;
delete aDemuxArg;
}
#else
// (8.2) single thread model
xDemux(NULL, msg);
#endif
April 1, 2002 7
Advanced Linux Programming M. Barbeau
}
}
// destructor
WLANProtocol::~WLANProtocol()
{
aLog.log("WLANProtocol, shutdown");
// cancel the receive thread
int error = pthread_cancel(threadID);
if (error != 0)
{
aLog.log("WLANProtocol, failed to cancel thread");
}
Sending a frame:
// sends a frame
Outcome WLANSession::xPush(Message *msg)
{
assert(msg);
if (ifconfig->sockid == -1)
{
aLog.log("WLANSession::xPush(), sockid is NULL");
return NOK;
}
April 1, 2002 8
Advanced Linux Programming M. Barbeau
if (sentlen == -1 )
{
aLog.log("WLANSession::xPush(), sendto failed");
return NOK;
}
return OK;
}
3. Kernel-mode drivers
Block device
A block device is something that can host a file system such as a disk. A
block device can only be accessed as multiples of a block, where a block is
usually one kilobyte of data.
Buffering is used.
Character device
A character device is one that can be accessed like a file, and a char driver is
in charge of implementing this behavior. This driver implements the open,
close, read and write system calls. The console and parallel ports are
examples of char devices.
No buffering.
April 1, 2002 9
Advanced Linux Programming M. Barbeau
Interrupt mode
A device driver, an IRQ, interrupt service routine (ISR), and a device bottom
half are involved.
The device driver starts the device and performs the I/O, an IRQ is issued,
ISR is executed, bottom half is marked for execution or a function is put in
the task queue.
The ret_from_sys_call code block is executed (runs after any system call),
the process state is switched to TASK_RUNNING, and scheduler is
invoked.
Role of the ISR: completion of the I/O (i.e. check the device status, data
transfer from device to primary memory) and marks bottom half for later
execution (with the mark_bh() kernel function).
April 1, 2002 10
Advanced Linux Programming M. Barbeau
Interrupt mode
Major/minor number
Major number: standard identity of a device driver (e.g. 2 for floppy, 3 for
IDE hard disk, 6 for a parallel interface, see file /usr/include/linux/major.h).
April 1, 2002 11
Advanced Linux Programming M. Barbeau
Interrupt handling
The request_irq() kernel function associates an ISR with an IRQ. Fails if an
ISR is already associated to the IRQ.
Demultiplexing of IRQs: several devices on the same IRQ, a master ISR de-
multiplexes the IRQ to several ISRs (hardware is polled to determine the
cause of the IRQ and the ISR is selected).
APIs to character device drivers and block device drivers are slightly
different.
April 1, 2002 12
Advanced Linux Programming M. Barbeau
Look the same as an interface to the file system! A device driver may
support only a subset of the operations.
int hd_init(void)
{
/* registration of the interface with the kernel */
if (register_blkdev(MAJOR_NR, "hd", &hd_fops)
{
printk("hd: unable to get major %d for harddisk\n",
MAJOR_NR);
return -1;
}
blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
read_ahead[MAJOR_NR] = 8; /*8 sectors (4kB) read-ahead*/
hd_gendisk.next = gendisk_head;
gendisk_head = &hd_gendisk;
timer_table[HD_TIMER].fn = hd_times_out;
return 0;
}
April 1, 2002 13
Advanced Linux Programming M. Barbeau
Implementation of operations:
The hd_open() function: called when a user-space process calls open() for
device /dev/hdi.
return 0;
}
Further Reading
April 1, 2002 14