今天整理了一下字符设备驱动的模板,工程分开3个文件.
main.c 驱动加载初始化工作。loadmod.c 提供安装和卸载驱动的函数。
fileops.c 文件操作函数。
Makefile
#KERNELDIR = /home/fontlose/board/tx2416/kernelsom2416 KERNELDIR = /usr/src/kernels/2.6.35.13-92.fc14.i686/ PWD := $(shell pwd) #CC = arm-linux-gcc moddev-objs=main.o fileops.o loadmod.o obj-m+=moddev.o modules: $(MAKE) -C $(KERNELDIR) M=$(PWD) modules clean: rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions .PHONY: modules clean
main.c
#include <linux/module.h> #include <linux/kernel.h> #include <linux/device.h> #include <linux/init.h> #include "loadmod.h" #include "fileops.h" MODULE_AUTHOR("my name"); MODULE_LICENSE("Dual BSD/GPL"); #define MAJORNUM 0 #define MINJORNUM 60 struct file_operations ops= { .owner=THIS_MODULE, .open=fileops_open, .write=fileops_write, .read=fileops_read, .release=fileops_release }; static int template_init(void) { if ( install_chardev(MAJORNUM,MINJORNUM,4,"moduledev","modclass",&ops)) return -1; if(fileops_init()) { uninstall_chardev(); return -1; } return 0; } static void template_exit(void) { uninstall_chardev(); fileops_destroy(); printk(KERN_ALERT "goodbye\n"); } module_init(template_init); module_exit(template_exit);
loadmod.h
#ifndef __LOADMOD_H__ #define __LOADMOD_H__ #include <linux/fs.h> extern unsigned char install_chardev(int major_i,int minor_i,unsigned char count,char*devname,char*classname,struct file_operations *ops); extern int uninstall_chardev(void); #endif
#include <linux/module.h> #include <linux/errno.h> #include <linux/kernel.h> #include <linux/device.h> #include <linux/kdev_t.h> #include <linux/err.h> #include <linux/cdev.h> #include <linux/init.h> #include <linux/fs.h> #include <linux/string.h> static struct cdev cdev_install; // static struct class *class_install; // static int major_install,minor_install; // static unsigned char count_install; // /** * install_chardev 字符设备注册函数 * @major_i 主设备号 * @minor_i 次设备号 * @count 设备个数 * @devname 设备名+注册的设备名自动在/dev下生成devname+次设备号的文件 * @classname 设备类名 * @ops 文件操作数据结构 */ unsigned char install_chardev(int major_i,int minor_i,unsigned char count,char*devname,char*classname,struct file_operations *ops) { //step 1:设备号的注册 int i; dev_t dev; if(major_i==0) //动态申请一个主设备号 { int res=alloc_chrdev_region(&dev,minor_i,count,devname); if(res){ printk(KERN_ALERT "alloc chrdev region fail!\n"); return -1; } major_i=MAJOR(dev); } else { int res; dev=MKDEV(major_i,minor_i); res=register_chrdev_region(dev,count,devname); if(res){ printk(KERN_ALERT "register chrdev region fail!\n"); return -1; } } major_install=major_i; minor_install=minor_i; count_install=count; //step 2:注册设备 cdev_install.count=0; if(ops!=0) { cdev_init(&cdev_install,ops); cdev_install.owner=THIS_MODULE; cdev_install.ops =ops; if( cdev_add(&cdev_install,dev,count_install) ) { unregister_chrdev_region(dev,count_install); printk(KERN_ALERT "cdev add fail!\n"); return -1; } } //step 3:创建设备节点 注意在linux 2.6 //低版本使用class_device_create和class_device_destroy,高版本使用device_create和device_destroy class_install=class_create(THIS_MODULE,classname); if(IS_ERR(class_install)) { if(cdev_install.count>0) cdev_del(&cdev_install); unregister_chrdev_region(dev,count_install); printk(KERN_ALERT "class creat fail!\n"); return -1; } for(i=0;i<count;i++) { device_create(class_install,NULL,MKDEV(major_install,minor_install+i),NULL,"%s%d",devname,minor_install+i); } printk(KERN_ALERT "device initial success major:%d minor:%d!\n",major_install,minor_install); return 0; } /** * uninstall_chardev 卸载字符设备 */ int uninstall_chardev(void) { int i; if(cdev_install.count >0) cdev_del(&cdev_install); unregister_chrdev_region(MKDEV(major_install,minor_install),count_install); for(i=0;i<count_install;i++) { device_destroy(class_install,MKDEV(major_install,minor_install+i)); } class_destroy(class_install); return 0; }
fileops.h
#ifndef __FILEOPS_H__ #define __FILEOPS_H__ #include <linux/fs.h> extern int fileops_init(void); extern void fileops_destroy(void); extern int fileops_open(struct inode *inode, struct file *filp); extern int fileops_release(struct inode *inode,struct file *filp); extern ssize_t fileops_read(struct file *filp, char __user *buff, size_t count, loff_t *offp); extern ssize_t fileops_write(struct file *filp, const char __user *buff, size_t count, loff_t *offp); extern int fileops_ioctl(struct inode *, struct file *, unsigned int, unsigned long); #endif
fileops.c
#include <linux/init.h> #include <linux/kernel.h> #include <linux/kdev_t.h> #include <linux/err.h> #include <linux/cdev.h> #include <linux/fs.h> #include "fileops.h" #include <asm/uaccess.h> /** * 初始化 在加载时候调用 */ int fileops_init(void) { return 0; } /** *释放函数 在模块卸载时候调用 */ void fileops_destroy(void) { } /** * 文件打开 驱动初始化为后续操作做准备,如果不实现默认打开成功 * 初始化设备,如有需要则初始化filp->private_data * * @inode 文件的索引节点,它用来存放档案及目录的基本信息,可取inode结构内 * 的次编号来确定具体哪个设备 * @filp */ int fileops_open(struct inode *inode, struct file *filp) { printk(KERN_ALERT "fileops_open\n"); return 0; } /** * 文件关闭 释放open申请的内存 关闭设备 */ int fileops_release(struct inode *inode,struct file *filp) { printk(KERN_ALERT "fileops_release\n"); return 0; } /** *读设备 *@filp 一般操作private_data数据 *@buff 用户空间的缓存,用于存放读取的数据 *@count 用户想要读取的数据大小 *@offp 用户正在存取的文件位置 *返回实际读出大小 负数表示出错 */ ssize_t fileops_read(struct file *filp, char __user *buff, size_t count, loff_t *offp) { /*每次读完更新offp*/ printk(KERN_ALERT "fileops_read\n"); return 0; } /** *写设备 *@filp 一般操作private_data数据 *@buff 用户空间的缓存,用于存放要写的数据 *@count 用户想要写入的数据大小 *@offp 用户正在存取的文件位置 *返回实际写入大小 负数表示出错 */ ssize_t fileops_write(struct file *filp, const char __user *buff, size_t count, loff_t *offp) { /*这里系统调用会一直写 直到写够count,如返回小于count的数,会再次调用此方法直到返回错误或写够count*/ printk(KERN_ALERT "fileops_write\n"); return count; } /** *fileops_ioctl ioctl 系统调用提供了发出设备特定命令的方法 *@inode *@filp *@cmd *@arg */ int fileops_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) { printk(KERN_ALERT "fileops_ioctl\n"); return 0; }
测试结果
[root@localhost module]# make make -C /usr/src/kernels/2.6.35.13-92.fc14.i686/ M=/home/fontlose/board/tx2416/rootfs/usr/ldds/module modules make[1]: Entering directory `/usr/src/kernels/2.6.35.13-92.fc14.i686' CC [M] /home/fontlose/board/tx2416/rootfs/usr/ldds/module/main.o CC [M] /home/fontlose/board/tx2416/rootfs/usr/ldds/module/fileops.o CC [M] /home/fontlose/board/tx2416/rootfs/usr/ldds/module/loadmod.o LD [M] /home/fontlose/board/tx2416/rootfs/usr/ldds/module/moddev.o Building modules, stage 2. MODPOST 1 modules CC /home/fontlose/board/tx2416/rootfs/usr/ldds/module/moddev.mod.o LD [M] /home/fontlose/board/tx2416/rootfs/usr/ldds/module/moddev.ko make[1]: Leaving directory `/usr/src/kernels/2.6.35.13-92.fc14.i686' [root@localhost module]# dmesg|tail -2 [31864.045477] device initial success major:247 minor:60! [31872.877461] goodbye [root@localhost module]# ls /dev/moduledev6* /dev/moduledev60 /dev/moduledev61 /dev/moduledev62 /dev/moduledev63 [root@localhost module]# ls /sys/class/modclass/ moduledev60 moduledev61 moduledev62 moduledev63 [root@localhost module]# echo 1234 > /dev/moduledev60 [root@localhost module]# dmesg|tail -3 [32022.480159] fileops_open [32022.480171] fileops_write [32022.480174] fileops_release