Linux字符驱动

1.Linux设备驱动分类

按管理的设备硬件来分:字符设备,块设备,网络设备
1)字符设备
    按字节流访问,能够顺序访问,也能够指定位置访问,如:按键 串口 终端 触摸屏 LCD等。
2)块设备
    在Unix系统下,块设备按一定的数据块进行访问,数据块为512bytes 1K等。
    在Linux下,块设备既可以按数据块的方式访问,也可以安字节流访问,    他和字符设备的区别在于linux系统中描述块设备和字符设备的数据结构和操作方法是不一样的。
3)网络设备
    网卡,网络设备一般要结合tcp/ip协议栈来实现。

2.字符设备驱动

驱动程序的作用
    1)管理对应的硬件。
    2)给用户提供访问硬件的操作的方法(接口)。

3.应用程序如何访问硬件

 硬件设备在linux系统下,会以设备文件的形式存在,设备文件(字符和块)在/dev,那么应用程序要访问硬件其实就是访问对应的设备文件

4.应用程序如何访问设备文件

通过调用系统调用来实现对其的访问,访问设备文件和访问普通文件的方式是一样的
open read write ioctl mmap close......

5.应用程序如何通过设备文件在非常多的驱动中找到对应的硬件驱动

设备文件本身包含一些属性:
1)设备文件是字符设备文件(c)还是块设备文件(b)
2)设备文件还包括主设备号和次设备号这两个重要的属性
3)应用程序就是通过主设备号找到对应的驱动程序
4)一个驱动程序只有一个主设备号

6.次设备号的作用

次设备号用于找到具体要访问的设备个体

设备号:主设备号和次设备号
数据类型:

    #include <linux/fs.h>
    dev_t(unsigned int);//32byte
    高12位:主设备号
    低20位:次设备号
设备号操作宏
    MAJOR:获取设备号的主设备号;
    MINOR:获取设备号的次设备号;
    MKDEV:通过主设备号和次设备号生成设备号。

设备号是属于系统资源,如果要实现驱动和设备号的绑定,首先必须向内核申请设备号资源,只有完成申请之后,才能和驱动程序绑定。

7.如何向内核申请设备号

静态分配和动态分配
7.1 静态分配
    1)查看哪些主设备号已经被使用
    cat /proc/devices

Character devices:字符设备
  1 mem
  5 /dev/tty
  5 /dev/console
  5 /dev/ptmx
 10 misc
 13 input
 21 sg

Block devices:块设备
  1 ramdisk
259 blkext
  7 loop
  8 sd
 31 mtdblock
 65 sd

    2)然后根据你的设备个数分配次设备号,一般次设备号都从0开始
    dev_t dev = MKDEV(主设备号,次设备号);

    3)调用register_chrdev_region;向内核申请
    注:主设备号不能为0

7.2 动态分配
    调用alloc_chrdev_region 直接向内核申请

    释放设备号
    unregister_chrdev_region


8.linux字符设备驱动四个重要的数据结构

1)struct file
作用:描述文件打开以后的状态属性
生命周期:从open打开成功内核创建,到close关闭内核销毁
重要的成员
    const struct file_operations    *f_op;

    unsigned int    f_flags;       //文件的操作属性
    loff_t            f_pos;            //文件的操作位置

2) struct file_operations
定义文件的操作函数集合,用户空间调用对应的系统调用时就会调用file结构体中file_operations成员中对应的函数

3) struct inode
作用:描述一个文件的物理结构
生命周期:文件存在,内核创建,文件销毁,内核销毁对应的inode
重要的成员
    dev_t            i_rdev;              //存放设备号
    struct cdev        *i_cdev;         //指向一个字符设备

4) struct cdev
表示一个字符设备驱动
重要的数据成员:
    const struct *ops;                  //字符设备驱动的操作函数集合
    dev_t dev;                             //设备号

一个文件只有一个inode,可以有多个file

9. struct file如何找到cdev中的file_operations

1)应用程序调用open,最终调用sys_open
2)sys_open创建file结构体,描述文件的打开信息
3)通过主设备号找到对应的cdev
4)将cdev中的file_operations成员赋值给file中的file_operations成员
5)sys_open最后调用cdev中的file_operations中的open函数
6)以后用户空间对设备文件的所有操作访问的都是cdev中的file_operations成员的操作方法,也就是对硬件的操作.

read  --> sys_read  --> file->f_op->read  --> cdev->ops->read
write --> sys_write --> file->f_op->write --> cdev->ops->write

10.如何将cdev添加到内核

1)分配初始化 struct file_operations
struct file_operations cdd_fops = {
    .open = ...
    .read = ....
    .write = ...
    .....
};

2)分配初始化struct cdev
struct cdev cdd_cdev;
cdev_init(&cdd_cdev,&cdd_fops);
//cdd_cdev.ops = cdd_fops;

3)将cdev添加到内核
cdev_add(&cdd_cdev,设备号,设备个数);
结果就是讲cdd_cdev添加到内核的cdev数组中,下标是以设备号为索引

一旦完成cdev的添加,内核中就有了一个真实的字符设备驱动

11.设备文件的创建

静态创建:
    mknod cdd c 251 0
动态创建:
    创建设备类:class_create
    创建设备文件:device_create


12.实现一个字符设备驱动的步骤

1)申请设备号(静态/动态)
2)注册cdev到内核
3)创建设备类
4)创建设备文件

13.实例

#include <linux/init.h>
#include <linux/module.h>

#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>

MODULE_LICENSE("GPL v2");

#define CDD_MAJOR 100
#define CDD_MINOR 0
#define CDD_COUNT 1

dev_t dev;
u32 cdd_minor = 0;
//实例化cdev
struct cdev cdd_cdev;

struct class *dev_class = NULL;
struct device *dev_device = NULL;

int cdd_open(struct inode *inode, struct file *filp)
{
	printk("enter cdd_open!\n");
	return 0;
}

ssize_t cdd_read(struct file *filp, char __user *buf, size_t count, loff_t *offset)
{
	printk("enter cdd_read!\n");
	return 0;
}

ssize_t cdd_write(struct file *filp, const char __user *buf, size_t count, loff_t *offset)
{
	printk("enter cdd_write!\n");
	return 0;
}

int cdd_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long data)
{
	printk("enter cdd_ioctl!\n");
	return 0;
}

int cdd_release(struct inode *inode, struct file *filp)
{
	printk("enter cdd_release!\n");
	return 0;
}

struct file_operations cdd_fops = 
{
	.owner = THIS_MODULE,
	.open = cdd_open,
	.read = cdd_read,
	.write = cdd_write,
	.ioctl = cdd_ioctl,
	.release = cdd_release,
};

int __init cdd_init(void)
{
	int ret = 0;
	if(cdd_minor)//静态分配
	{
		dev = MKDEV(CDD_MAJOR,CDD_MINOR);

		//向内核申请
		ret = register_chrdev_region(dev, CDD_COUNT, "cdd_demo");
	}
	else//动态分配
	{
		ret = alloc_chrdev_region(&dev, cdd_minor, CDD_COUNT, "cdd_demo");
	}
	
	if(ret<0)
	{
		printk("register_chrdev failed!\n");
		goto faiure_register_chrdev;
	}

	//初始化cdev
	cdev_init(&cdd_cdev, &cdd_fops);
	//向内核注册cdev
	ret = cdev_add(&cdd_cdev, dev, CDD_COUNT);
	if(ret<0)
	{
		printk("cdev_add failed!\n");
		goto failure_cdev_add;
	}

	/*动态创建设备文件*/
	// 1.创建设备类
	//会在/sys/class目录下创建"cdd_class"为名的文件夹
	dev_class = class_create(THIS_MODULE, "cdd_class");
	if(IS_ERR(dev_class))
	{
		ret = PTR_ERR(dev_class);
		goto failure_class_create;
	}
	// 2.创建设备文件
	//会在/dev目录下创建对应的设备文件
	dev_device = device_create(dev_class, NULL, dev, NULL, "cdd");
	if(IS_ERR(dev_device))
	{
		ret = PTR_ERR(dev_device);
		goto failure_device_create;
	}
		
	return 0;

	failure_device_create:
		class_destroy(dev_class);
	failure_class_create:
		cdev_del(&cdd_cdev);
	failure_cdev_add:
		unregister_chrdev_region(dev, CDD_COUNT);
	faiure_register_chrdev:
		return ret;
}

void __exit cdd_exit(void)
{
	//注销设备
	device_destroy(dev_class, dev);
	//注销设备类
	class_destroy(dev_class);
	//注销cdev
	cdev_del(&cdd_cdev);
	//注销设备号
	unregister_chrdev_region(dev, CDD_COUNT);
}

module_init(cdd_init);
module_exit(cdd_exit);

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值