The following is meant to be a set of notes for writing a device driver and is not intended to be a tutorial or handbook. I recommend the following sources as better references for device driver writing: Claus Schroeter's character device driver white paper, his PCI programming white paper, and his driver development kit whitepaper (ftp:ftp.llp.fu-berlin.de/pub/linux/LINUX-LAB/whitepapers), Micheal Johnson's device driver whitepaper, Micheal Battersby's notes on device driver writing, David Rusling's book The Linux Kernel, and the Linux Kernel Hacker's Guide. Of course, the best reference of all is the source code for a device driver similar to the one you're trying to write.
I will concentrate on modular device drivers rather than those which are compiled into the kernel.
mknod /dev/filename b MAJOR MINOR
struct file_operations {
int (*lseek) (struct inode *, struct file *, off_t, int);
int (*read) (struct inode *, struct file *, char *, int);
int (*write) (struct inode *, struct file *, const char *, int);
int (*readdir) (struct inode *, struct file *, void *, filldir_t);
int (*select) (struct inode *, struct file *, int, select_table *);
int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
int (*mmap) (struct inode *, struct file *, struct vm_area_struct *);
int (*open) (struct inode *, struct file *);
void (*release) (struct inode *, struct file *);
int (*fsync) (struct inode *, struct file *);
int (*fasync) (struct inode *, struct file *, int);
int (*check_media_change) (kdev_t dev);
int (*revalidate) (kdev_t dev);
};
lseek(fd,position,whence), where whence is either
0 (file start), 1 (current position), or 2 (file end).
inode->i_rdev is the device number of the open device; use
MAJOR(inode->i_rdev) and MINOR(inode->i_rdev) to get
the major and minor numbers
file->private_data can be used to store driver specific data
#include <linux/malloc.h> void *kmalloc(int size, int priority); void kfree_s(void *obj, int size); void kfree(void *obj);
#include <linux/mm.h> void *vmalloc(long size); void vfree(void *obj);the priority is either GFP_KERNEL for normal cases, or GFP_ATOMIC for allocating memory inside interrupts
#include <linux/mm.h> void * vremap(unsigned long offset, unsigned long size);
where offset is the location in physical memory. Note that the address returned is different from the hardware address.
#include <linux/mm.h>
int remap_page_range(unsigned long from, unsigned long to,
unsigned long size, pgprot_t prot);
where from is a pointer to the region to be mapped, to is the
physical memory location, and prot is the set of memory protection flags
applied to the region (i.e., PROT_READ, PROT_WRITE, etc.)
#include <linux/mm.h> int verify_area(int type, void * area, unsigned long size);where type is either VERIFY_READ if the kernel is to read from the buffer, or VERIFY_WRITE if the kernel is to write to the buffer.
#include <asm/segment.h> inline unsigned char get_fs_byte(const char * addr); inline unsigned char get_fs_word(const unsigned short * addr); inline unsigned char get_fs_long(const unsigned long * addr); inline void put_fs_byte(char val, char * addr); inline void put_fs_word(short val, short * addr); inline void put_fs_long(long val, long * addr); inline void memcpy_fromfs(void * to, const void * from, unsigned long n); inline void memcpy_tofs (void * to, const void * from, unsigned long n);
#include <asm/io.h> char inb(int port); short inw(int port); void outb(char val, int port); void outw(short val, int port);
Alternative versions of these macros, identified by a ``_p'' suffix, perform a short (~ 1 usec) pause after the I/O access. This is sometimes necessary because the CPU can execute I/O instructions faster than the device/bus can keep up.
/proc/pci.
pcibios_present().
int pcibios_find_device (unsigned short vendor, unsigned short dev_id,
unsigned short index, unsigned char *bus,
unsigned char *dev_fn);
#include <linux/bios32.h>
int pcibios_read_config_byte (unsigned char bus, unsigned char dev_fn,
unsigned char where, unsigned char *val);
int pcibios_read_config_word (unsigned char bus, unsigned char dev_fn,
unsigned char where, unsigned short *val);
int pcibios_read_config_dword (unsigned char bus, unsigned char dev_fn,
unsigned char where, unsigned int *val);
#include <linux/bios32.h>
int pcibios_write_config_byte (unsigned char bus, unsigned char dev_fn,
unsigned char where, unsigned char val);
int pcibios_write_config_word (unsigned char bus, unsigned char dev_fn,
unsigned char where, unsigned short val);
int pcibios_write_config_dword (unsigned char bus, unsigned char dev_fn,
unsigned char where, unsigned int val);
PCI_BASE_ADDRESS_MEM_MASK or PCI_BASE_ADDRESS_IO_MASK.
#include <linux/module.h> #include <linux/version.h>
MOD_INC_USE_COUNT on each open().
MOD_DEC_USE_COUNT on each release().
insmod foo.o
rmmod foo.o
lsmod
-Wall -O2 -DMODULE -D__KERNEL__
This document was generated using the LaTeX2HTML translator Version 97.1 (release) (July 13th, 1997)
Copyright © 1993, 1994, 1995, 1996, 1997, Nikos Drakos, Computer Based Learning Unit, University of Leeds.
The command line arguments were:
latex2html -split 0 -no_math -no_images -no_navigation ddrive.tex.
The translation was initiated by Nick Maliszewskyj on 8/27/1997