linux设备驱动模型 实例,linux设备驱动模型一字符设备 驱动实例

前面分析了驱动中的一些基本结构,下面结合实例分析一下驱动的编写及实现

下面这个例子来处网上:

#include //模块所需的大量符号和函数定义

#include //指定初始化和清楚函数

#include //文件系统相关的函数和头文件

#include //cdev结构的头文件

#include //在内核和用户空间中移动数据的函数

#include //kfree、kmalloc分配头文件

MODULE_LICENSE("GPL"); //指定代码使用的许可证

//文件操作函数的声明

int globalvar_open(struct inode *, struct file *);

int globalvar_release(struct inode *, struct file *);

ssize_t globalvar_read(struct file *, char *, size_t, loff_t *);

ssize_t globalvar_write(struct file *, const char *, size_t, loff_t *);

int dev_major = 50; //指定主设备号

int dev_minor = 0; //指定次设备号首先是头文件的包含,注意要包含slab.h头文件,否则会找不到kfree等函数。然后定义了主次设备号,这个在创建设备的时候会用到。

struct file_operations globalvar_fops= //将文件操作与分配的设备号相连

{

owner: THIS_MODULE, //指向拥有该模块结构的指针

open: globalvar_open,

release: globalvar_release,

read: globalvar_read,

write: globalvar_write,

};

struct globalvar_dev //用来表示我们定义设备的结构

{

int global_var; //这个变量代表要操作的设备

struct cdev cdev; //内核中表示字符设备的结构

};

struct globalvar_dev *my_dev; //设备结构的指针

定义了一个file_operations结构变量,中间主要是对一些函数指针的赋值,当我们调用相应的系统函数时就会找到这里对应的函数。

接着定义了一个结构体中,中间包含一个cdev结构,这个结构是字符设备中的一个重要的结构

//打开设备文件系统调用对应的操作

int globalvar_open(struct inode *inode, struct file *filp)

{

struct globalvar_dev *dev;

//根据inode结构的cdev字段,获得整个设备结构的指针

dev=container_of(inode->i_cdev, struct globalvar_dev, cdev);

//将file结构中的private_data字段指向已分配的设备结构

filp->private_data=dev;

return 0;

}

//关闭设备文件系统调用对应的操作

int globalvar_release(struct inode *inode, struct file *filp)

{

return 0;

}

//读设备文件系统调用对应的操作

ssize_t globalvar_read(struct file *filp, char *buf, size_t len, loff_t *off)

{

//获取指向已分配数据的指针

struct globalvar_dev *dev=filp->private_data;

//将设备变量值复制到用户空间

if(copy_to_user(buf, &dev->global_var, sizeof(int)))

{

return -EFAULT;

}

return sizeof(int); //返回读取数据的大小

}

//写设备文件系统调用对应的操作

ssize_t globalvar_write(struct file *filp, const char *buf, size_t len, loff_t *off)

{

//获取指向已分配数据的指针

struct globalvar_dev *dev=filp->private_data;

//从用户空间复制数据到内核中的设备变量

if(copy_from_user(&dev->global_var, buf, sizeof(int)))

{

return -EFAULT;

}

return sizeof(int); //返回写数据的大小

}定义了几个系统调用相应的函数,需要注意的是在open的时候将上面定义的结构保存在了file结构的private_data中,这样再以后的操作的时候 只要从这里面取出来就可以了,如在read的时候将dev中的值拷贝给调用者

static void __exit globalvar_exit(void) //退出模块时的操作

{

dev_t devno=MKDEV(dev_major, dev_minor); //dev_t是用来表示设备编号的结构

cdev_del(&my_dev->cdev); //从系统中移除一个字符设备

kfree(my_dev); //释放自定义的设备结构

unregister_chrdev_region(devno, 1); //注销已注册的驱动程序

printk("globalvar unregister success\n");

}

static int __init globalvar_init(void) //初始化模块的操作

{

int ret, err;

dev_t devno=MKDEV(dev_major, dev_minor);

//动态分配设备号,次设备号已经指定

ret=alloc_chrdev_region(&devno, dev_minor, 1, "globalvar");

//保存动态分配的主设备号

dev_major=MAJOR(devno);

printk("dev_major = %d.\n", dev_major);

//根据期望值分配设备号

//ret=register_chrdev_region(devno, 1, "globalvar");

if(ret<0)

{

printk("globalvar register failure\n");

globalvar_exit(); //如果注册设备号失败就退出系统

return ret;

}

else

{

printk("globalvar register success\n");

}

//为设备在内核空间分配空间

my_dev=kmalloc(sizeof(struct globalvar_dev), GFP_KERNEL);

if(!my_dev)

{

ret=-ENOMEM; //如果分配失败返回错误信息

printk("create device failed\n");

}

else //如果分配成功就可以完成设备的初始化

{

my_dev->global_var=0; //设备变量初始化为0

cdev_init(&my_dev->cdev, &globalvar_fops); //初始化设备中的cdev结构

my_dev->cdev.owner=THIS_MODULE; //初始化cdev中的所有者字段

err=cdev_add(&my_dev->cdev, devno, 1); //向内核添加这个cdev结构的信息

if(err<0)

printk("add device failure\n"); //如果添加失败打印错误消息

}

return ret;

}

module_init(globalvar_init); //模块被装载时调用globalvar_init

module_exit(globalvar_exit); //模块被卸载时调用globalvar_exit接下来的就是模块的初始化即退出了,初始化的时候有两种方法分配主设备号,一种是调用alloc_chrdev_region动态分配,这个时候主设备号是随机的,后面创建设备需要注意匹配,否则调用open的时候会失败,另外一种就是调用 register_chrdev_region分配固定的主次设备号,这个时候主次设备号就是文件前面定义的 50 和0

撯接着给我们定义的结构分配空间赋值,调用cdev_init初始化cdev并将前面定义的file_operations结构赋值给它,最后调用cdev_add向内核添加这个cdev结构。

makefile比较简单

EXTRA_CFLAGS := -g

obj-m += globalvar.o

default:

make -C /lib/modules/$(shell uname -r)/build/ SUBDIRS=$(shell pwd) modules

编译后使用insmod globalvar.ko加载模块

可以使用cat /proc/devices 或dmesg或lsmod查看下模块是否加载成功

加载成功后可以用mknod创建一个设备文件

root@leaves-desktop:/home/leaves/Test/cha# rmmod globalvar.ko

root@leaves-desktop:/home/leaves/Test/cha# insmod globalvar.ko

root@leaves-desktop:/home/leaves/Test/cha# rm -f /dev/globalvar

root@leaves-desktop:/home/leaves/Test/cha# mknod /dev/globalvar c 250 0

root@leaves-desktop:/home/leaves# ^C

root@leaves-desktop:/home/leaves# cat /proc/kmsg

<4>[82981.675828] dev_major = 250.

<4>[82981.675918] globalvar register success这里使用的是动态分配主设备号这里可以看到分配的主设备号是250,所以创建调用的时候使用mknod /dev/globalvar c 250 0,如果是固定分配,对应这里文件定义的主调和号是50,这里创建设备应该使用mknod /dev/globalvar c 50 0

设备创建发好后,我们需要一个程序来测试 一下:

#include

#include

#include

#include

main()

{

int fd, num;

fd=open("/dev/globalvar", O_RDWR, S_IRUSR|S_IWUSR); //可读写方式打开设备文件

if(fd!=-1)

{

read(fd, &num, sizeof(int)); //读取设备变量

printf("The globalvar is %d\n", num);

printf("Please input the num written to globalvar\n");

scanf("%d", &num);

write(fd, &num, sizeof(int)); //写设备变量

read(fd, &num, sizeof(int)); //再次读取刚才写的值

printf("The globalvar is %d\n", num);

close(fd); //关闭设备文件

}

else

{

perror("ERROR:");

printf("Device open failure\n");

}

}这里只是简单的打开设备读写数据

如果创建的设备没有问题,是应该可以和设备文件交互的

root@leaves-desktop:/home/leaves/Test/cha# ./a.out

The globalvar is 0

Please input the num written to globalvar 这样,一个简单的字符设备的驱动就ok了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值