linux2.6版本中描述设备的结构体,【链表Linux面试题】面试问题:1.2 Lin… - 看准网...

在目前的内核版本中,存在三种流行的字符设备编程模型:杂项设备驱动模型,早期经典标准字符设备驱动模型, Linux 2.6 标准字符设备驱动模型。

Linux 系统借鉴了面向对象的思想来管理设备驱动 ,每一类设备都都会有定义一个特定的结构体来描述它,这个结构体包含了设备的基本信息,以及操作设备方法(函数指针),所以,编写程序实际上就是实现核心结构,

然后把这结构注册到内核中。

所以,学习一种设备驱动,第一步就是要学习该设备对应数据结构,弄清楚成员是什么意思,什么作用,以及是否一定需要实现成员。

1.2.1 杂项设备的核心数据结构

1. 结构体:

头文件路径:

include\Linux\miscdevice.h

struct miscdevice {

int minor; //次设备号

const char *name; //设备名,/dev/下的设备文件名

const struct file_operations *fops; //文件操作方法集合指针

//以下是内核使用,用户不需要关注

struct list_head list;//用于挂接到杂项设备链表misc_list上。

struct device *parent;//指向父设备

struct device *this_device;//在创建设备节点时指向函数device_create()返回的设备结构

const char *nodename;

umode_t mode;

};

上面结构中核心部分有三个成员:minor, name, fops这三个成员必须实现。

fops 成员对应的结构定义: struct file_operations。

它定义了一系列的操作设备的函数指针 ,用户根据自己需要实现所需要功能的指针成员。

include\Linux\fs.h

struct file_operations {

struct module *owner;

loff_t (*llseek) (struct file *, loff_t, int); //移动光标,对应 Linux 系统应用编程接口的 lseek 函数

ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);

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

.....................

unsigned int (*poll) (struct file *, struct poll_table_struct *);

long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);

.....................

int (*mmap) (struct file *, struct vm_area_struct *);

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

.....................

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

.....................

};

常用成员说明:

llseek: 移动文件指针,对应 Linux 系统应用编程接口的 lseek 函数。

read: 从设备文件中读取数据,对应 Linux 系统应用编程接口的 read 函数。

write: 向设备文件写入数据,对应 Linux 系统应用编程接口的 write 函数。

poll: 轮询函数,对应 Linux 系统应用编程接口的 poll 函数或 select 函数

unlocked_ioctl: i/o 控制,对应 Linux 系统应用编程接口的 ioctl 函数

mmap:内存映射, 对应 Linux 系统应用编程接口的 mmap 函数

open:打开设备,操作设备的第一步,对应系统应用编程接口的 open 函数

release:关闭设备,操作设备的最后一步,对应系统应用编程接口的 close 函数

实际应用中只会实现一部分成员,不可能全部实现,也没有必要实现全部成员。

1.2.2 杂项设备的设备号

主设备号: 固定是 10

次设备号: 0~255. (写 255 时候内核会动态分配一个能用的次设备号)

每个杂项设备驱动次设备不能相同。 当你不能确定哪一个次设备号是可用的情况下需要使用 255,这样内核可以帮你找一个可以使用的号。

(如果杂项设备的设备号被置为MISC_DYNAMIC_MINOR,则表明该设备的设备号要从新自动分配。)

1.2.3 杂项设备特征

安装杂项设备会自动在/dev 目录生成设备文件。

早期经典标准字符设备驱动模型和 Linux2.6 标准字符设备驱动不会自动生成设备文件。

调用一次 misc_register 注册函数,只会占用一个次设备号。

1.2.4 杂项设备注册/注销函数

实现了 struct miscdevice 结构必须的成员后,还需要使用内核专用函数向系统注册,这样才可以完成一个驱动 的编程。

1. 注册函数

int misc_register(struct miscdevice * misc);

功能:向内核注册一个杂项字符设备

参数:

misc 已经实现好 min,name, fops 三个成员的 struct miscdevice 结构变量的地址

返回: 0:注册成功;

<0 :失败,返回失败错误码

2. 注销函数

这个函数功能的 misc_register 函数相反。

int misc_deregister(struct miscdevice *misc);

功能:注销一个已经注册到内核中杂项字符设备

参数: misc 已经注册的 struct miscdevice 结构变量的地址

返回: 0:注册成功;

<0 :失败,返回失败错误码

1.2.5 杂项设备驱动模型的代码模板

1.2.5.1 编写驱动程序的前奏,基础理论

1. 驱动程序是以内核模块形式编写,所以要编写驱动,先编写使用一个模块文件做为起点。

2. 每类设备都有一个核心结构,必须定义核心结构的变量

3. 编写一类设备驱动程序就是实现其核心结构必须的成员

4. 把实现好的核心结构变量向内核注册---在模块加载函数中注册

5. 在模块卸载函数中把核心结构注销

1.2.5.2 编写杂项设备驱动代码模板

驱动代码模板:

#include

#include

//包含必须的头文件

#include

#include

//以下是文件操作方法的具体实现代码

static int xxx_open(struct inode *pinode, struct file *pfile )

{

printk(KERN_EMERG"line:%d, %s is call\n", __LINE__, __FUNCTION__);

return 0;

}

static ssize_t xxx_read(struct file *pfile,

char __user *buf, size_t count, loff_t *poff)

{

printk(KERN_EMERG"line:%d, %s is call\n", __LINE__, __FUNCTION__);

return count;

}

static ssize_t xxx_write(struct file *pfile,

const char __user *buf, size_t count, loff_t *poff)

{

printk(KERN_EMERG"line:%d, %s is call\n", __LINE__, __FUNCTION__);

return count;

}

static loff_t xxx_llseek(struct file *pfile, loff_t off, int whence)

{

printk(KERN_EMERG"line:%d, %s is call\n", __LINE__, __FUNCTION__);

return off;

}

static int xxx_release (struct inode *pinode, struct file *pfile)

{

printk(KERN_EMERG"line:%d, %s is call\n", __LINE__, __FUNCTION__);

return 0;

}

static long xxx_unlocked_ioctl (struct file *pfile,

unsigned int cmd, unsigned long args)

{

printk(KERN_EMERG"line:%d, %s is call\n", __LINE__, __FUNCTION__);

return 0;

}

//文件操作方法集合指针

static const struct file_operations mymisc_fops = {

.open = xxx_open,

.read = xxx_read,

.write = xxx_write,

.llseek = xxx_llseek,

.release = xxx_release,

.unlocked_ioctl = xxx_unlocked_ioctl,

};

//定义核心结构

static struct miscdevice misc = {

.minor = 255,

.name = "mymisc", ///dev目录下的设备名

.fops = &mymisc_fops,

};

static int __init mymisdevice_init(void)

{

int ret;

//注册核心结构

ret = misc_register(&misc);

if(ret < 0) {

printk(KERN_EMERG"misc_register error\n");

return ret;

}

printk(KERN_EMERG"misc_register ok\n");

return 0;

}

static void __exit mymisdevice_exit(void)

{

int ret;

//注销核心结构

ret = misc_deregister(&misc);

if(ret < 0) {

printk(KERN_EMERG"misc_deregister error\n");

return ;

}

printk(KERN_EMERG"misc_deregister ok\n");

}

module_init(mymisdevice_init);

module_exit(mymisdevice_exit);

MODULE_LICENSE("GPL");

应用程序测试驱动:

#include

#include

#include

#include

#include //lseek

#include //ioctl

int fd; //存放文件描述符号

char save_buf[1024]={0}; //存放数据使用

int main(void)

{

int ret;

fd = open("/dev/mymisc",O_RDWR); //以读写方式进行打开

if(fd < 0){

printf("open error\r\n");

return -1;

}

printf("fd=%d\r\n",fd); //成功时候输出文件描述符

//读操作

ret = read(fd, save_buf,1024);

if(ret < 0){

printf("read error\r\n");

return -1;

}

//写操作

write(fd, "console out test\r\n", sizeof("console out test\r\n"));

//移动文件指针操作

lseek(fd,0,SEEK_SET);

//i/o控制操作

ioctl(fd,0,0);

//关闭文件

close(fd);

return 0;

}

Makefile

# Makefile 2.6

#hello是模块名,也是对应的 c 文件名。

obj-m += misc_device_module.o

# KDIR内核源码路径,根据自己需要设置

# X86源码路径统一是/lib/modules/$(shell uname -r)/build

#如果要编译 ARM 的模块,则修改成 ARM 的内核源码路径

#KDIR :=/lib/modules/$(shell uname -r)/build

KDIR := /root/work/linux-3.5_xyd_modversion/

all:

@make -C $(KDIR) M=$(PWD) modules

@rm -f *.o *.mod.o *.mod.c *.symvers *.markers *.unsigned *.order *~ *.bak

arm-linux-gcc app.c -o app

cp app *.ko /root/work/rootfs/home

clean:

make -C $(KDIR) M=$(PWD) modules clean

rm -f *.ko *.o *.mod.o *.mod.c *.symvers *.markers *.unsigned *.order *~ *.bak

开发板测试结果:

[root@book home]# ls /dev/mymisc

ls: /dev/mymisc: No such file or directory

安装后生成了设备文件,通过这个文件可以找到这份驱动程序的文件操作方法

[root@book home]# insmod misc_device_module.ko

[ 65.010000] misc_register ok

[root@book home]# ls /dev/mymisc

/dev/mymisc

卸载后,设备文件自动删除:

[root@book home]# rmmod misc_device_module

[ 91.145000] misc_deregister ok

[root@book home]# ls /dev/mymisc

ls: /dev/mymisc: No such file or directory

查看杂项设备的主次设备号: 主是 10,次 47---内核自动分配的(因为代码写了 255)

[root@book home]# insmod misc_device_module.ko

[ 98.970000] misc_register ok

[root@ChenZhiFa home]# ls /dev/mymisc -l

crw-rw---- 1 root root 10, 47 Sep 9 2016 /dev/mymisc

编写用户程序,通过用户编程 API 接口调用驱动程序的文件操作方法:

[root@book home]# lsmod

Tainted: G

misc_device_module 2079 0 - Live 0xbf004000 (O)

[root@book home]# ls

app misc_device_module.ko

[root@ChenZhiFa home]# ./app

[ 732.400000] line:13, xxx_open is call

[ 732.400000] line:21, xxx_read is call

[ 732.400000] line:28, xxx_write is call

[ 732.400000] line:35, xxx_llseek is call

[ 732.400000] line:52, xxx_unlocked_ioctl is call

[ 732.400000] line:43, xxx_release is call

fd=3

[root@book home]#

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值