先介紹下我的環境,用的ubuntu發行版,kernel版本是3.8.0,《Linux設備驅動程序》一書是針對2.6.0的kernel,在這個例子中會看到一些差異。這個例子要實現的目標是,編譯裝載該module後,通過udev動態的生成設備節點star0,讀取設備節點cat /dev/star0可以打印出星號,星號的數量可以由模塊參數動態的指定。以下為源代碼:
star.h
#ifndef STAR_H_H_H
#define STAR_H_H_H
#define AUTHOR "Tao Yang"
#define DESCRIPTION "A CHAR DEVICE DIRVERS SAMPLE USING UDEV"
#define VERSION "0.1"
#endif
star.c
//module_init module_exit
#include
#include
//module_param
#include
//printk container_of
#include
//dev_t MAJOR MINOR MKDEV
#include
//file_operations file register/unregister_chrdev_region alloc_chrdev_region register/unregister_chrdev(old)
#include
//cdev cdev_init/add/del
#include
//copy_from_user copy_to_user
#include
#include
#include "star.h"
//define device number
#define STAR_MAJOR 0
#define STAR_MINOR 0
#define STAR_DEVS 1
#define DEVICE_NAME "star0"
#define CLASS_NAME "star"
static int howmany=5;
module_param(howmany,int,S_IRUGO);
//module info
MODULE_AUTHOR(AUTHOR);
MODULE_DESCRIPTION(DESCRIPTION);
MODULE_VERSION(VERSION);
MODULE_LICENSE("GPL");
//device variables
static struct class* star_class=NULL;
static struct device* star_sysdevice=NULL;
int star_major=STAR_MAJOR;
int star_minor=STAR_MINOR;
int star_nr_devs=STAR_DEVS;
//device struct
static struct star_dev {
char *data;
struct cdev cdev;
};
static struct star_dev star_device;
static ssize_t star_read(struct file * filp,char * buf,size_t count,loff_t *ppos)
{
int i;
char star_str[10000];
struct star_dev *dev=filp->private_data;
for (i=0;i
star_str[i]='*';
}
star_str[howmany]='\n';
int len=strlen(star_str);
if(count
return -EINVAL;
if(*ppos!=0)
return 0;
if(copy_to_user(buf,star_str,len))
return -EINVAL;
*ppos=len;
return len;
}
int star_open(struct inode *inode,struct file *filp)
{
struct star_dev *dev;
dev=container_of(inode->i_cdev,struct star_dev,cdev);
filp->private_data=dev;
//......
return 0;
}
//file operations
static const struct file_operations star_fops = {
.owner = THIS_MODULE,
.read = star_read,
.open = star_open,
};
static void star_setup_cdev(struct star_dev *dev,int index)
{
int err,devno=MKDEV(star_major,star_minor+index);
printk(KERN_ALERT "setup cdev...\n");
cdev_init(&dev->cdev,&star_fops);
dev->cdev.owner=THIS_MODULE;
dev->cdev.ops=&star_fops;
err=cdev_add(&dev->cdev,devno,1);
if(err)
printk(KERN_ALERT "Error %d adding star%d",err,index);
}
static int __init star_init(void)
{
int ret;
dev_t dev;
printk(KERN_ALERT "hello tom!\n");
if(star_major){
dev=MKDEV(star_major,star_minor);
ret=register_chrdev_region(dev,star_nr_devs,DEVICE_NAME);
printk(KERN_ALERT "static!\n");
}else{
ret=alloc_chrdev_region(&dev,star_minor,star_nr_devs,DEVICE_NAME);
star_major=MAJOR(dev);
printk(KERN_ALERT "dynamic!\n");
printk(KERN_ALERT "Device Major is %d!\n",star_major);
}
if(ret<0){
printk(KERN_ALERT "star:can't get major %d\n",star_major);
return ret;
}
printk(KERN_ALERT "set up cdev!");
star_setup_cdev(&star_device,0);
star_class=class_create(THIS_MODULE,CLASS_NAME);
if(IS_ERR(star_class)){
printk(KERN_ALERT "failed to register device class '%s'\n",CLASS_NAME);
}
//with a class ,the easiest way to instantiate a device is to call device_create()
star_sysdevice=device_create(star_class,NULL,MKDEV(star_major,0),NULL,DEVICE_NAME);
return 0;
}
static void __exit star_exit(void)
{
device_destroy(star_class,MKDEV(star_major,star_minor));
class_unregister(star_class);
class_destroy(star_class);
cdev_del(&star_device.cdev);
printk(KERN_ALERT "goodbye...\n");
}
module_init(star_init);
module_exit(star_exit);
Makefile
ifneq ($(KERNELRELEASE),)
obj-m:=star.o
else
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
PWD :=$(shell pwd)
default:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
endif
編譯源代碼:
root@ubuntu:~/embedded_linux/ldd3/practice# make
make -C /lib/modules/3.8.0-29-generic/build M=/home/tom/embedded_linux/ldd3/practice modules
make[1]: Entering directory `/usr/src/linux-headers-3.8.0-29-generic'
CC [M] /home/tom/embedded_linux/ldd3/practice/star.o
/home/tom/embedded_linux/ldd3/practice/star.c:58:1: warning: useless storage class specifier in empty declaration [enabled by default]
/home/tom/embedded_linux/ldd3/practice/star.c: In function ‘star_read’:
/home/tom/embedded_linux/ldd3/practice/star.c:72:5: warning: ISO C90 forbids mixed declarations and code [-Wdeclaration-after-statement]
/home/tom/embedded_linux/ldd3/practice/star.c:66:22: warning: unused variable ‘dev’ [-Wunused-variable]
/home/tom/embedded_linux/ldd3/practice/star.c:81:1: warning: the frame size of 10032 bytes is larger than 1024 bytes [-Wframe-larger-than=]
Building modules, stage 2.
MODPOST 1 modules
CC /home/tom/embedded_linux/ldd3/practice/star.mod.o
LD [M] /home/tom/embedded_linux/ldd3/practice/star.ko
make[1]: Leaving directory `/usr/src/linux-headers-3.8.0-29-generic'
root@ubuntu:~/embedded_linux/ldd3/practice#
安裝驅動模塊:
root@ubuntu:~/embedded_linux/ldd3/practice# insmod star.ko
root@ubuntu:~/embedded_linux/ldd3/practice# dmesg -c
[ 3946.696548] hello tom!
[ 3946.699892] dynamic!
[ 3946.699894] Device Major is 249!
[ 3946.699895] set up cdev!