[设备驱动] 最简单的内核设备驱动--字符驱动
概要:
x86平台上(linux-2.6.34.14;Linux debian 3.2.0-3-686-pae)编写一个256字节的字符驱动程序.在/dev下生成设备节点,供用户访问.
一.目录结构
[root:simple_cdev] tree
.
├── demo.c
├── demo.h
├── example
│ ├── main.cpp
│ └── Makefile
└── Makefile
1 directory, 5 files
二.demo内核驱动模块源码和编译:
2.1 字符驱动demo.c源码:
[root:simple_cdev] cat demo.c /// @file demo.c /// @brief /// @author Easton Woo /// 0.01 /// @date 2013-09-02 #include <linux/module.h> #include <linux/cdev.h> #include <linux/fs.h> #include <linux/slab.h> #include <linux/uaccess.h> #include <linux/types.h> #include <linux/ioctl.h> #include "demo.h" #include <linux/device.h> int DEMO_open(struct inode *innode, struct file *filep); int DEMO_close(struct inode *innode, struct file *filp); ssize_t DEMO_read(struct file *filp, char __user *buf, size_t count, loff_t* p_pos); ssize_t DEMO_write(struct file *filp, const char __user *buf, size_t count, loff_t* p_pos); loff_t DEMO_seek(struct file *filp, loff_t offset, int where); long DEMO_ioctl(struct file * filp, unsigned int cmd, unsigned long arg); int DEMO_init_module(void); void DEMO_cleanup_module(void); struct DEMO_dev { struct cdev cdev; }; struct file_operations DEMO_fops = { .owner = THIS_MODULE, .open = DEMO_open, .release = DEMO_close, .read = DEMO_read, .write = DEMO_write, .llseek = DEMO_seek, .unlocked_ioctl = DEMO_ioctl, //.ioctl 成员变量在我的这个内核版本中已经不能用了。 }; struct DEMO_dev* DEMO_devices; static struct class* class_demo; static unsigned char demo_inc = 0; static u8 demoBuffer[256]; #define DEMO_MAJOR 224 #define DEMO_MINNOR 0 //////////////////////设备操作////////////////////////////// int DEMO_open(struct inode *innode, struct file *filep) { struct DEMO_dev * dev = NULL; if( demo_inc > 0 ) { return -ERESTARTSYS; } demo_inc++; dev = container_of(innode->i_cdev,struct DEMO_dev,cdev); filep->private_data = dev; printk(KERN_WARNING "DEMO: device is open!\n"); return 0; } int DEMO_close(struct inode *innode, struct file *filp) { if( demo_inc > 0 ) { demo_inc--; } printk(KERN_WARNING "DEMO: device is close!\n"); return 0; } ssize_t DEMO_read(struct file *filp, char __user *buf, size_t count, loff_t* p_pos) { loff_t pos = *p_pos; int iReadCount = 0; if(pos > 256 - 1) { return -1; } if(256 - pos < count) { count = 256 -pos; } iReadCount = copy_to_user(buf, demoBuffer+pos,count); if(iReadCount == 0) { printk(KERN_INFO "DEMO: read %d data successful\n",count ); } else { printk(KERN_INFO "DEMO: read %d data fail\n",count ); } count = iReadCount; return count; } ssize_t DEMO_write(struct file *filp, const char __user *buf, size_t count, loff_t* p_pos) { loff_t pos = *p_pos; int iWriteCount = 0; if(pos > 256 - 1) { return -1; } if(256 - pos < count) { count = 256 -pos; } iWriteCount = copy_from_user(demoBuffer+pos,buf,count); if(iWriteCount == 0) { printk(KERN_INFO "DEMO: write %d data successful\n",count ); } else { printk(KERN_INFO "DEMO: write %d data fail\n",count ); } count = iWriteCount; return 0; } loff_t DEMO_seek(struct file *filp, loff_t offset, int where) { loff_t pos = filp->f_pos; switch(where) { case 0: pos = offset; break; case 1: pos += offset; break; case 2: pos = 256-1 - offset; break; } if(pos > 256 -1 || pos < 0) { return -1; } printk(KERN_INFO "DEMO: seek move %llu is OK!\n",offset); return filp->f_pos = pos; } long DEMO_ioctl(struct file * filp, unsigned int cmd, unsigned long arg) { switch(cmd) { case DEMO_IO_INFO: printk(KERN_INFO "DEMO: run commod \"DEMO_IO_INFO\" successful !\n"); break; case DEMO_IO_OK: printk(KERN_INFO "DEMO: run commod \"DEMO_IO_OK\" successful !\n"); break; default: printk(KERN_INFO "DEMO: error commod %d!\n",cmd); return -1; break; } return 0; } //////////////////////注册销毁////////////////////////////// int DEMO_init_module(void) { int result; dev_t dev = 0; dev = MKDEV(DEMO_MAJOR,DEMO_MINNOR); result = register_chrdev_region(dev, 1, "DEMO"); if(result < 0) { printk(KERN_WARNING "DEMO: can't get major %d\n",DEMO_MAJOR); return result; } DEMO_devices = kmalloc(sizeof(struct DEMO_dev), GFP_KERNEL); if(!DEMO_devices) { result = -ENOMEM; goto fail; } memset(DEMO_devices, 0, sizeof(struct DEMO_dev)); cdev_init(&DEMO_devices->cdev, &DEMO_fops); DEMO_devices->cdev.owner = THIS_MODULE; DEMO_devices->cdev.ops = &DEMO_fops; result = cdev_add(&DEMO_devices->cdev, dev, 1); if (result) { printk(KERN_WARNING "DEMO: Error %d adding DEMO\n", result); goto fail; } class_demo = class_create(THIS_MODULE, "demo_cdev");//在/sys目录下建立了一个demo_cdev的目录 if (IS_ERR(class_demo)) { printk(KERN_INFO "DEMO: create class error\n"); return -1; } device_create(class_demo,NULL,dev,NULL,"demo_inode" "%d", MINOR(dev)); memset(demoBuffer,0,sizeof(demoBuffer)); printk(KERN_WARNING "DEMO:[test] add to kerner!\n"); return 0; fail: DEMO_cleanup_module(); return result; } void DEMO_cleanup_module(void) { dev_t devno = MKDEV(DEMO_MAJOR, DEMO_MINNOR); unregister_chrdev_region(devno, 1); if (DEMO_devices) { cdev_del(&DEMO_devices->cdev); kfree(DEMO_devices); device_destroy(class_demo, devno);//在2.6早期的版本应该使用class_device_destroy,早于2.6.27? 2.6.29? 忘记了。 class_destroy(class_demo); } printk(KERN_WARNING "DEMO:[test] remove in kerner!\n"); } module_init(DEMO_init_module); module_exit(DEMO_cleanup_module); MODULE_LICENSE("GPL"); //解决 Error: could not insert module demo.ko: Unknown symbol in module
2.2 编写MakeFile编译:
[root:simple_cdev] cat Makefile AR = ar # ARCH = i386 CC = gcc # 解决方法:在Makefile中将"CFLAGS"替换成"EXTRA_CFLAGS"。 EXTRA_CFLAGS += -Wall # $(MAKE) ifneq ($(KERNELRELEASE),) obj-m := demo.o else KERNELDIR ?= /lib/modules/$(shell uname -r)/build PWD := $(shell pwd) all: $(MAKE) -C $(KERNELDIR) M=$(PWD) modules clean: @rm -rf *.o *.ko *.mod.c *.order *.symvers endif
2.3 编译出demo.ko内核驱动模块:
[root:simple_cdev] ls
demo.c demo.h example/ Makefile
[root:simple_cdev] make
make -C /lib/modules/3.2.0-3-686-pae/build M=/work/my_test/driver/simple_cdev modules
make[1]: Entering directory `/usr/src/linux-headers-3.2.0-3-686-pae'
CC [M] /work/my_test/driver/simple_cdev/demo.o
Building modules, stage 2.
MODPOST 1 modules
CC /work/my_test/driver/simple_cdev/demo.mod.o
LD [M] /work/my_test/driver/simple_cdev/demo.ko
make[1]: Leaving directory `/usr/src/linux-headers-3.2.0-3-686-pae'
[root:simple_cdev] ls
demo.c demo.h demo.ko demo.mod.c demo.mod.o demo.o example/ Makefile modules.order Module.symvers
[root:simple_cdev]
三.测试代码和编译:
3.1 测试代码 main.cpp 和Makfile
[user:example] cat main.cpp /// @file main.cpp /// @brief // /// @author Easton Woo /// 0.01 /// @date 2013-09-02 #include <stdio.h> #include <unistd.h> #include <string.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <sys/ioctl.h> #include "../demo.h" int main() { int fd = -1; fd = open("/dev/demo_inode0",O_RDWR); if(fd < 0) { printf("open /dev/demo_inode0 is error!\n"); return -1; } write(fd, "hello", sizeof("hello")); lseek(fd, 0, SEEK_SET); char buf[256]; memset(buf,0,sizeof(buf)); read(fd, buf, sizeof(buf)); printf("%s\n",buf); ioctl(fd, DEMO_IO_INFO, 0); // ioctl(fd, DEMO_IO_OK, 0); ioctl(fd, 3, 0); close(fd); fd = -1; return 0; } [user:example]
[user:example] cat Makefile
all: g++ -o test_demo_app.i386.elf main.cpp .PHONY: clean clean: rm *.elf
[root:simple_cdev] cd example/
[root:example] ls
main.cpp Makefile
[root:example] make
g++ -o test_demo_app.i386.elf main.cpp
[root:example] ls
main.cpp Makefile test_demo_app.i386.elf*
[root:example]
四.驱动加载内核,测试结果:
[root:simple_cdev] ls
demo.c demo.h demo.ko demo.mod.c demo.mod.o demo.o example/ Makefile modules.order Module.symvers
[root:simple_cdev] ls /dev/demo_inode0
ls: 无法访问/dev/demo_inode0: 没有那个文件或目录
[root:simple_cdev] dmesg --clear
[root:simple_cdev]
[root:simple_cdev] dmesg
[root:simple_cdev] insmod demo.ko
[root:simple_cdev] lsmod | grep 'demo'
demo 12592 0
[root:simple_cdev] ls /dev/demo_inode0
/dev/demo_inode0
[root:simple_cdev] dmesg
[170724.473397] DEMO:[test] add to kerner!
[root:simple_cdev] ./example/test_demo_app.i386.elf
hello
[root:simple_cdev] dmesg
[170724.473397] DEMO:[test] add to kerner!
[170780.200620] DEMO: device is open!
[170780.200687] DEMO: write 6 data successful
[170780.200716] DEMO: seek move 0 is OK!
[170780.200783] DEMO: read 256 data successful
[170780.201758] DEMO: run commod "DEMO_IO_INFO" successful !
[170780.201768] DEMO: error commod 3!
[170780.201779] DEMO: device is close!
[root:simple_cdev] rmmod demo
[root:simple_cdev] dmesg
[170724.473397] DEMO:[test] add to kerner!
[170780.200620] DEMO: device is open!
[170780.200687] DEMO: write 6 data successful
[170780.200716] DEMO: seek move 0 is OK!
[170780.200783] DEMO: read 256 data successful
[170780.201758] DEMO: run commod "DEMO_IO_INFO" successful !
[170780.201768] DEMO: error commod 3!
[170780.201779] DEMO: device is close!
[170816.292639] DEMO:[test] remove in kerner!
[root:simple_cdev] lsmod | grep 'demo'
[root:simple_cdev]