今天整理了一下字符设备驱动的模板,工程分开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
loadmod.c
#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