红联Linux门户
Linux帮助

Linux字符设备驱动例子

发布时间:2016-05-24 11:27:55来源:linux网站作者:满小茂

编写好驱动,通过挂载的方法将驱动程序挂载到内核里面,大致步骤如下:


一: 1>建立以.c为后缀的c语言程序文件 (里面包含了设备名及设备号等)
2>建立Makefile文件(作用是通过make来产生设备文件*.ko文件,里面可以建立自己的平台所需的设备文件如:arm等).make 产生相应的设备文件


二: 要在/dev下建立相应的设备结点(设备名),用insomd *.ko命令将相应的驱动设备文件挂载到内核中.


三:编写测试文件(.c文件)用来测试内核是否已近成功挂载到内核.(编写好相应的测试文件后,用gcc –o Filename Filename.c(测试文件名) 来产生相应的可执行文件).


四:如果设备驱动挂载成功,当执行测试文件(./Filename)时会产生相应的结果.


五:可能用到的相关命令:
1. lsmod:列出内核已经载入模块的专题.
输出:
Module(模块名)size(大小)used by (被..使用)
2. demop:分析可加载模块的依赖性,生成modules.dep文件和映射文件
3. uname –r   显示内核版本(在编写Makefile时使用到)
4. modprobe : linux 内核添加和删除模块(相关参数请查看man帮助文档)
5. modinfo:显示内核模块的信息.
6. insmod: 向linux内核中加载一个模块,用法:insmod  [filename] [module options…]
7. rmmod: 删除内核中的模块, 用法: rmmod [-f,w,s,v] [modulename]
8. dmesg: 显示内核缓冲区,内核的各种信息,内核启动时的信息会写入到/var/log/下.


六.例子
第一步:增加头文件和宏定义

#include <linux/fs.h>
#include <linux/types.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/module>
#include <linux/kernel>


第二步:添加与字符设备定义及注册有关的数据成员

//定义设备名称
#define DEVICE_NAME "test"  //设备名
#define BUF_SIZE 1024
static char tmpbuf[BUF_SIZE];

//定义主次设备号
static unsigned int TestMajor=0;   //主
static unsigned int TestMinor=0;   //次

static struct cdev *test_cdev;
static dev_t dev;


第三步:增加open/release函数

static int test_chardev_open(struct inode *inode,struct file *file)
{
printk("open major=%d, minor=%d\n", imajor(inode),
iminor(inode));
return 0;
}

static int test_chardev_release(struct inode *inode,struct file *file)
{
printk("close major=%d,minor=%d\n",imajor(inode),
iminor(inode));
return 0;
}


第四步:增加read函数

static ssize_t test_chardev_read(struct file *file,char __user *buf,
size_t const count,loff_t *offset)
{
if(count < BUF_SIZE)
{
if(copy_to_user(buf,tmpbuf,count))
{
printk("copy to user fail \n");
return -EFAULT;
}
}else{
printk("read size must be less than %d\n", BUF_SIZE);
return -EINVAL;
}
*offset += count;
return count;
}


第五步:增加write函数

static ssize_t test_chardev_write(struct file *file, const char __user  *buf,size_t const count,loff_t *offset)
{
if(count < BUF_SIZE)
{
if(copy_from_user(tmpbuf,buf,count))
{
printk("copy from user fail \n");
return -EFAULT;
}
}else{
printk("size must be less than %d\n", BUF_SIZE);
return -EINVAL;
}    
*offset += count;
return count;
}


第六步:添加增加file_operations 成员

static struct file_operations chardev_fops={
.owner = THIS_MODULE,
.read = test_chardev_read,
.write = test_chardev_write,
.open = test_chardev_open,
.release = test_chardev_release,
};


第七步:在模块的入口添加设备的设备号获取及设备注册

static int __init chrdev_init(void)
{
int result;
if(TestMajor)
{
dev=MKDEV(TestMajor,TestMinor);//创建设备编号
result=register_chrdev_region(dev,1,DEVICE_NAME);
} else {
result=alloc_chrdev_region(&dev,TestMinor,1,DEVICE_NAME);
TestMajor=MAJOR(dev);
}
if(result<0)
{
printk(KERN_WARNING"LED: cannot get major %d \n",TestMajor);
return result;
}
test_cdev=cdev_alloc();
cdev_init(test_cdev,&chardev_fops);
//test_cdev->ops=&chardev_fops;
test_cdev->owner=THIS_MODULE;
result=cdev_add(test_cdev,dev,1);
if(result)
printk("<1>Error %d while register led device!\n",result);
return 0;
}


第八步:在模块的出口函数增加设备设备号释放及设备注销函数

unregister_chrdev_region(MKDEV(TestMajor,TestMinor),1);
cdev_del(test_cdev);


第九步:编译并加载该模块


第十步:根据设备号的设置,在文件系统中建立对应的设备节点

# mknod /dev/test  c  XXX(主设备号)  XX(次设备号)


完整例子
驱动文件

#include <linux/init.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/uaccess.h>

#define DEVICENAME  "ccccc"

unsigned int major=221;
unsigned int minor=0;
struct cdev *abc;
dev_t dev;
static char bufrh[1024]="read success!";

static int aaaaa_open(struct inode *inodep, struct file *filep)
{
printk("read success!\n");
return 0;
}

int aaaaa_release(struct inode *inodep, struct file *filep)
{
return 0;
}
static ssize_t aaaaa_read (struct file *filep, char __user *buf, size_t  count, loff_t *offset)
{
if(copy_to_user(buf, bufrh, 1))
{
printk("copy_to_user fail!\n");
}
return 0;
}

ssize_t aaaaa_write (struct file *filep, const char __user *buf,  size_t count, loff_t *offse)
{
printk("write!\n");
return 0;
}

static const struct  file_operations  fops = {
.owner = THIS_MODULE,
.open = aaaaa_open,
.release = aaaaa_release,
.read = aaaaa_read,
.write = aaaaa_write,

};

static int __init aaaaa_init(void)
{
int a;
dev=MKDEV(major, minor);
a=register_chrdev_region(dev, 1, DEVICENAME);
abc=cdev_alloc();
abc->owner=THIS_MODULE;
cdev_init(abc, &fops);
cdev_add(abc, dev, 1);
return 0;
}

static void __exit  aaaaa_cleanup(void)
{
cdev_del(abc);
unregister_chrdev_region(dev, 1);
}

module_init(aaaaa_init);
module_exit(aaaaa_cleanup);
MODULE_LICENSE("GPL ");

Makefile文件

obj-m += firstqd.o(相应设备文件名)

KERDIR = /usr/src/linux-headers-2.6.32-24-generic   #x86平台
PWD=$(shell pwd)

modules:
$(MAKE) -C $(KERDIR) M=$(PWD)  modules

pc:
gcc -o fristqd firstqd.c
arm:
arm-linux-gcc -o fristqd firstqd.c

clean:
rm -rf *.o *~core *.depend *.cmd *.ko *.mod.c *.tmp_versions


用户空间测试代码

#include <stdio.h>
#include <sys/types.h>
#include <fcntl.h>

char buf[1024];
char bufw[1024]="write success";
int main()
{
int fd,m,n;
fd=open("/dev/aaa",O_RDWR);
if (fd)
{
m=read(fd,buf,100);
printf("read kernel:%s\n",buf);

n=write(fd,bufw,10);
}
//printf("ni hao");
return  0;
}


本文永久更新地址:http://www.linuxdiyf.com/linux/20905.html