一:字符设备驱动

  在linux下面,应用层看到的一切皆为文件(名字)所有的设备都是文件,都可以调用open,read,write来操作,而在内核中每个中每个设备有唯一的对应一个设备号;

    APP   (名字)            
    OS      (设备号)
    HW

  下面我们写一个简单的字符设备驱动,再应用层我们打开一个设备,看看它是怎么来调用内核中的函数的;

  首先我们使用命令mknod 创建一个名为wangcai的设备名,在应用曾打开它;

  mknod /dev/设备文件名  c   主设备号   次设备号

  命令 mknod wangcai c 9  0

  在注册字符设备是我们需要用到这几个struct cdev,原型如下:

 struct cdev
{
struct kobject kobj; // 内嵌的kobject对象,描述设备引用计数
struct module *owner; // 所属模块,一般赋值为THIS_MODULE
struct file_operations *ops; // 文件操作结构体
struct list_head list;
dev_t dev; // 设备号
unsigned int count;
};

  cdev结构体的dev_t定义了设备号,32位。高12位为主设备号,低20位为次设备号。

  (1)应用层打开设备

 #include <stdio.h>
#include <fcntl.h> int main()
{
int fd = ;
fd = open("wangcai", O_RDWR);
if(fd < ) {
perror("open error");
return ;
} return ;
}

  (2)在内核中册了设备文件wangcai和方法ops

  

 #include <linux/init.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/fs.h> MODULE_LICENSE("GPL");
MODULE_AUTHOR("bunfly");
int file_open(struct inode *no, struct file *fp); struct cdev wangcai;//设备
struct file_operations fops;//方法
dev_t devno; int bunfly_init()
{
fops.open = file_open;//调用open
//wangcai.ops = &fops;
cdev_init(&wangcai, &fops);//初始化cdev成员,建立cdev和file_operations之间的连接
//wangcai.dev = (9, 0);
devno = MKDEV(, );//字符设备号注册
//insert_list(&wangcai);
cdev_add(&wangcai, devno, );// 向系统添加一个dev,完成字符设备的注册,常用于模块加载函数中 } int bunfly_exit()
{
printk("this is bunfly_exit\n");
} module_init(bunfly_init);
module_exit(bunfly_exit); int file_open(struct inode *no, struct file *fp)
{
printk("this is file_open\n"); return ;
}

  下面代码是实现字符设备的读写操作:

  (1)应用层

  write:

#include <stdio.h>
#include <fcntl.h>
#include <string.h> int main(int argc, char *argv[])
{
if(argc < ) {
printf("using %s <dev> <msg>\n", argv[]);
return ;
} int fd = ;
int ret = ; fd = open(argv[], O_RDWR);
if(fd < ) {
perror("open error"); return ;
} ret = write(fd, argv[], strlen(argv[]));
if(ret < ) {
perror("write error");
return ;
}
close(fd); return ;
}

  read:

  

 #include <stdio.h>
#include <fcntl.h>
#include <string.h> int main(int argc, char *argv[])
{
if(argc != ) {
printf("using %s <dev>\n", argv[]);
return ;
} int fd = ;
int ret = ;
unsigned char data[] = {}; fd = open(argv[], O_RDWR);
if(fd < ) {
perror("open error"); return ;
} ret = read(fd, data, );
if(ret < ) {
perror("write error");
return ;
} printf("data is: %s\n", data); close(fd); return ;
}

  

  (2)内核  

 #include <linux/init.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/fs.h> MODULE_LICENSE("GPL");
MODULE_AUTHOR("bunfly"); int file_open(struct inode *no, struct file *fp);
ssize_t file_read(struct file *fp, char *buff, size_t size, loff_t * loff);
ssize_t file_write(struct file *fp, const char *buff, size_t size, loff_t *loff); struct cdev wangcai;//设备
struct file_operations fops;//方法
dev_t devno;
unsigned char data[] = {}; int bunfly_init()
{
fops.open = file_open;
fops.read = file_read;
fops.write = file_write; //wangcai.ops = &fops;
cdev_init(&wangcai, &fops);//初始化cdev成员,建立cdev和file_operations之间的连接
//wangcai.dev = (9, 0);
devno = MKDEV(, );//字符设备号注册
//insert_list(&wangcai);
cdev_add(&wangcai, devno, );// 向系统添加一个dev,完成字符设备的注册,常用于模块加载函数中 } int bunfly_exit()
{
cdev_del(&wangcai); /*注销设备*/
unregister_chrdev_region(MKDEV(, ), );
printk("this is bunfly_exit\n");
} module_init(bunfly_init);
module_exit(bunfly_exit); int file_open(struct inode *no, struct file *fp)
{
return ;
}
ssize_t file_read(struct file *fp, char *buff, size_t size, loff_t *loff)
{
strcpy(buff, data);
return size;
} ssize_t file_write(struct file *fp, const char *buff, size_t size, loff_t *loff)
{
memset(data, , );
strcpy(data, buff);
return size;
}
59

  下面代码是通过ioctl()函数来控制灯亮灯灭:

  (1)应用层

  

#include <stdio.h>
#include <string.h>
#include <fcntl.h> int main(int argc, char *argv[])
{
if(argc != ) {
printf("using %s <devname> 1:0\n", argv[]);
return ;
} int fd = ;
fd = open(argv[], O_RDWR);
if(fd < ) {
perror("open");
return ;
} ioctl(fd, atoi(argv[]));
close(fd);
return ;
}

  (2)内核

  

 #include <linux/init.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/io.h>
#include <linux/gpio.h> MODULE_LICENSE("GPL");
MODULE_AUTHOR("bunfly"); long my_ioctl(struct file *fp, unsigned int id, unsigned long fd);
int file_open(struct inode *no, struct file *fp); struct cdev wangcai;
struct file_operations fops;
dev_t devno;
unsigned long gpio_virt;
unsigned long *gpm4con, *gpm4dat; int bunfly_init()
{
fops.open = file_open;
fops.unlocked_ioctl = my_ioctl; //wangcai.ops = &fops;
cdev_init(&wangcai, &fops);//初始化cdev成员,建立cdev和file_operations之间的连接
//wangcai.dev = (9, 0);
devno = MKDEV(, );//字符设备号注册
//insert_list(&wangcai);
cdev_add(&wangcai, devno, );// 向系统添加一个dev,完成字符设备的注册,常用于模块加载函数中 gpio_virt = ioremap(0x11000000, SZ_4K);//led物理地址到虚拟地址映射
gpm4con = gpio_virt + 0x02e0;
gpm4dat = gpio_virt + 0x02e4; return ;
} int bunfly_exit()
{
cdev_del(&wangcai); /*注销设备*/
unregister_chrdev_region(MKDEV(, ), );
printk("this is bunfly_exit\n"); return ;
} module_init(bunfly_init);
module_exit(bunfly_exit); int file_open(struct inode *no, struct file *fp)
{
return ;
} long my_ioctl(struct file *fp, unsigned int id, unsigned long fd)
{
if(id == ) {
*gpm4con = 0x1111;
*gpm4dat = 0x0;
}
if(id == ) {
*gpm4con = 0x1111;
*gpm4dat = 0xf;
}
}

    通过上面的代码我们已经了解了字符设备驱动的原理,在linux下应用层看到的设备都只 是一个名字,应用层打开一个设备最终会调到内核中的file_operations方法来进行读写操作,如果我们只创建一个的设备的时候,我们可以对他正 常的读写,那如果当我们有两个设备时,我们是否还能正常的进行读写操作,明显是存在问题的,就是第二次的数据会将第一次的数据覆盖,因为我们只有一个数据存储的data;那么解决这个问题的方法就是封装;

  下面是具体代码:

  

 #include <linux/init.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/fs.h> MODULE_LICENSE("GPL");
MODULE_AUTHOR("bunfly"); int file_open(struct inode *no, struct file *fp);
ssize_t file_read(struct file *fp, char *buff, size_t size, loff_t *loff);
ssize_t file_write(struct file *fp, const char *buff, size_t size, loff_t *loff); struct file_operations fops;//方法 /*封装*/
struct bunfly_cdev {
dev_t devno;//设备号
struct cdev cdev;
unsigned char data[];
}; int bunfly_init()
{
fops.open = file_open;
fops.read = file_read;
fops.write = file_write; struct bunfly_cdev wangcai;
cdev_init(&wangcai.cdev, &fops);//初始化cdev成员,建立cdev和file_operations之间的连接
wangcai.devno = MKDEV(, );//注册设备号
cdev_add(&wangcai.cdev, wangcai.devno, );// 向系统添加一个dev,完成字符设备的注册,常用于模块加载函数中 struct bunfly_cdev tugou;
cdev_init(&tugou.cdev, &fops);
tugou.devno = MKDEV(, );
cdev_add(&tugou.cdev, tugou.devno, ); return ;
} int bunfly_exit()
{
printk("this is bunfly_exit\n"); return ;
} module_init(bunfly_init);
module_exit(bunfly_exit); int file_open(struct inode *no, struct file *fp)
{
struct cdev *addr = no->i_cdev;//找到struct cdev dev 在struct bunfly_cdev中的地址
struct bunfly_cdev *this = container_of(addr, struct bunfly_cdev, cdev);
fp->private_data = this; //父类在子类中的地址 //子类类型 return ;
}
ssize_t file_read(struct file *fp, char *buff, size_t size, loff_t *loff)
{
struct bunfly_cdev *this = fp->private_data;
strcpy(buff,this-> data); return size;
} ssize_t file_write(struct file *fp, const char *buff, size_t size, loff_t *loff)
{
struct bunfly_cdev *this = fp->private_data;
memset(this->data, , );
strcpy(this->data, buff); return size;
}

  代码中有看到了container_of,再次强调掌握,还需要注意的是:

  (1)每一个设备文件仅有inode结构体 ;

  (2)每打开一次文件就创建一个file 结构体;

  

linux设备驱动之字符设备驱动模型(1)的更多相关文章

  1. 【转】深入浅出:Linux设备驱动之字符设备驱动

    深入浅出:Linux设备驱动之字符设备驱动 一.linux系统将设备分为3类:字符设备.块设备.网络设备.使用驱动程序: 字符设备:是指只能一个字节一个字节读写的设备,不能随机读取设备内存中的某一数据 ...

  2. 手把手教Linux驱动3-之字符设备架构详解,有这篇就够了

    一.Linux设备分类 Linux系统为了管理方便,将设备分成三种基本类型: 字符设备 块设备 网络设备 字符设备: 字符(char)设备是个能够像字节流(类似文件)一样被访问的设备,由字符设备驱动程 ...

  3. Linux驱动设计——字符设备驱动(一)

    Linux字符设别驱动结构 cdev结构体 struct cdev { struct kobject kobj; struct module *owner; const struct file_ope ...

  4. Linux 设备驱动之字符设备

    参考转载博客:http://blog.chinaunix.net/uid-26833883-id-4369060.html https://www.cnblogs.com/xiaojiang1025/ ...

  5. linux中c表示字符设备文件符号

    linux中c表示字符设备文件,b表示块设备文件,l表示符号链接文件,r表示可读权限,w表示可写权限.linux文件属性解读:文件类型:-:普通文件 (f)d:目录文件b:块设备文件 (block)c ...

  6. linux设备驱动之字符设备驱动模型(2)

    在上一篇中我们已经了解了字符设备驱动的原理,也了解了应用层调用内核函数的机制,但是我们每次操作设备,都必须首先通过mknod命令创建一个设备文件名,比如说我们要打开u盘,硬盘等这些设备,难道我们还要自 ...

  7. 深入浅出:Linux设备驱动之字符设备驱

    一.linux系统将设备分为3类:字符设备.块设备.网络设备.使用驱动程序: 字符设备:是指只能一个字节一个字节读写的设备,不能随机读取设备内存中的某一数据,读取数据需要按照先后数据.字符设备是面向流 ...

  8. 【Linux驱动】字符设备驱动

    一.linux系统将设备分为3类:字符设备.块设备.网络设备.使用驱动程序: 1.字符设备:是指只能一个字节一个字节读写的设备,不能随机读取设备内存中的某一数据,读取数据需要按照先后数据.字符设备是面 ...

  9. 蜕变成蝶~Linux设备驱动之字符设备驱动

    一.linux系统将设备分为3类:字符设备.块设备.网络设备.使用驱动程序: 字符设备:是指只能一个字节一个字节读写的设备,不能随机读取设备内存中的某一数据,读取数据需要按照先后数据.字符设备是面向流 ...

随机推荐

  1. umask函数的用法 - 如何进行权限位的设置

    下面程序创建了两个文件,创建foo文件时,umask值为0,创建第二个时,umask值禁止所有组和其他用户的访问权限. 测试结果: 测试结果可以看出更改进程的文件模式掩码并不影响其父进程(常常是she ...

  2. 安卓TV开发(八) 移动智能终端多媒体爬虫技术 获取加载网页视频源

    转载请标明出处:http://blog.csdn.net/sk719887916/article/details/40049137,作者:skay 从上一篇学习中,学习了多媒体技术中的怎么去用josu ...

  3. Linux变量键盘读取、数组与声明: read, array, declare

    [root@www ~]# read [-pt] variable 选项与参数: -p :后面可以接提示字符! -t :后面可以接等待的『秒数!』这个比较有趣-不会一直等待使用者啦! 范例一:让用户由 ...

  4. 使用Android Studio手把手教你将应用打包+代码混淆

    最近几天用Google的Design库写了个App,使用Android Studio将app打包时遇到的几个瓶颈,所以把详细步骤写入下来. AS中怎么获取应用签名 这和eclipse不同,eclips ...

  5. 内核调试神器SystemTap — 更多功能与原理(三)

    a linux trace/probe tool. 官网:https://sourceware.org/systemtap/ 用户空间 SystemTap探测用户空间程序需要utrace的支持,3.5 ...

  6. 记——加快gradle 构建速度的经验

    Gradle作为一个新的构建系统,无疑在灵活,扩展,跨平台等各方面都表现得非常优秀,然而,它也有一点备受吐槽,就是速度慢.以下为本人使用gradle过程中,几次加快gradle构建速度的经验之谈. 本 ...

  7. IOS原声二维码条形码扫描实现

    本文讲述如何用系统自带的东东实现二维码扫描的功能:点击当前页面的某个按钮,创建扫描VIEW.细心的小伙伴可以发现 title被改变了,返回按钮被隐藏了.这个代码自己写就行了,与本文关系不大...绿色的 ...

  8. 第15章-输入/输出 --- 理解Java的IO流

    (一)理解Java的IO流 JAVA的IO流是实现输入/输出的基础,它可以方便地实现数据的输入/输出操作,在Java中把不同的输入/输出(键盘.文件.网络连接等)抽象表述为"流"( ...

  9. AS3编程规范

    整理了一些AS3的编程规范,有些规则对于大部分语言都是适用的,有什么问题请提出来,我会持续改进这份规范,谢谢!   [参考资料] 1.http://opensource.adobe.com/wiki/ ...

  10. 接口文档神器之apidoc

    //@desn:apidoc linux环境  windows环境使用 //@desn:码字不宜,转载请注明出处 //@author:张慧源  <turing_zhy@163.com> / ...