第12课第2.2节 字符设备驱动程序之LED驱动程序_测试改进

0.基础知识

内核使用三种 数据结构 表示打开的文件:
1.进程级的文件描述符表
   每一个文件描述符会与一个打开文件相对应,同时,不同的文件描述符也会指向同一个文件。相同的文件可以被不    同的进程打开也可以在同一个进程中被多次打开。系统为每一个进程维护了一个文件描述符表,该表的值都是从0    开始的,所以在不同的进程中你会看到相同的文件描述符,这种情况下相同文件描述符有可能指向同一个文件,也    有可能指向不同的文件。具体情况要具体分析,要理解具体其概况如何,需要查看由内核维护的3个数据结构。
2.打开文件表(file结构体)
  内核对所有打开的文件的文件维护有一个打开文件表(file结构体),并将file结构体中各条目称为打开文件句柄 。   一个file结构体句柄存储了与一个打开文件相关的全部信息,如下所示:
  
    当前文件偏移量(调用read()和write()时更新,或使用lseek()直接修改)
    打开文件时所使用的状态标识(即,open()的flags参数)
    文件访问模式(如调用open()时所设置的只读模式、只写模式或读写模式)
    与信号驱动相关的设置
    对该文件i-node对象的引用
    文件类型(例如:常规文件、套接字或FIFO)和访问权限
    一个指针,指向该文件所持有的锁列表
    文件的各种属性,包括文件大小以及与不同类型操作相关的时间戳
3.文件系统的i-node表(inode结构体)

    下图展示了文件描述符、打开的文件表以及i-node之间的关系,图中,两个进程拥有诸多打开的文件描述符。

一.几个shell命令

cat /proc/devices  显示已经存在的  组设备号+驱动名字
ismod  名字.ko     安装驱动
rmmod 名字.ko    卸载驱动
lsmod                 显示有的驱动
在/sys/class/下面有以驱动为名的文件夹,进入这个文件夹ls可以看到这个驱动包含的设备节点名

二.几个驱动常用函数讲解(先阅读0基础知识部分)

1.static int first_drv_open(struct inode *inode, struct file *file)
  static int s3c24xx_leds_open(struct inode *inode, struct file *file)
  第一个参数:打开文件表里面的inode 结构体的指针
  第二个参数:打开文件表指针(file 结构体指针)
2.static ssize_t first_drv_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
  static ssize_t s3c24xx_leds_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
  第一个参数:打开文件表指针(file结构体指针)
  第二个参数:用户空间buff地址
  第三个参数:接收数据的长度
  第四个参数:
  static ssize_t first_drv_read(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
  static ssize_t s3c24xx_leds_read(struct file *file, const char __user *buf, size_t count, loff_t * ppos) 
  第一个参数:打开文件表指针(file结构体指针)
  第二个参数:用户空间buff地址
  第三个参数:传送数据的长度
  第四个参数:
3. unsigned long  copy_from_user(void *to, const void __user *from, unsigned long n)
  从用户空间拷贝数据到内核空间
 第一个参数:内核空间地址
  第二个参数:用户空间地址
  第三个参数:需要拷贝数据的长度
4.unsigned long copy_to_user(void *to, const void __user *from, unsigned long n)
  
  从内核空间拷贝数据到用户空间
  第一个参数:用户空间地址
  第二个参数:内核空间地址
  第三个参数:需要拷贝数据的长度
5.int register_chardev (unsigned int major, const char *name, struct file_operations *fops) 
  功能:注册设备 
  返回:组设备号
  第一个参数:组设备号,写0则自动分配组设备号(返回值也是这个)
  第二个参数:name 设备名称
  第三个参数: fops file_operations 结构体变量地址(指针)。
6.unregister_chrdev(unsigned int major,const char *name); 
  功能:卸载设备
  第一个参数:组设备号
  第二个参数:设备名称
7.struct class *class_create(struct module *owner, const char *name)
  

  内核中定义了struct class结构体,一个struct class 结构体类型变量对应一个类,内核同时提供了class_create()函数,可以用它来创   建一个类,这个类存放于sysfs下面,一旦创建了这个类,再调用device_create()函数在/dev目录下创建相应的设备节点。这样,加载模块     的时候,用户空间中的udev会自动响应device_create()函数,去/sysfs下寻找对应的类而创建设备节点。

  说明:

  在2.6较早的内核版本中,device_cerate()函数名称是calss_device_create()


  第一个参数:  结构体struct module在内核中代表一个内核模块,通过insmod(实际执行init_module系统调用)把自己编写的内核模块插入内核  时,模块便与一个 struct module结构体相关联,并成为内核的一部分。

 我们插入一个内核模块,一般会使用工具insmod,该工具实际上调用了系统调用init_module,在该系统调用函数中,首先调用    load_module,把用户空间传入的整个内核模块文件创建成一个内核模块,返回一个struct module结构体。内核中便以这个结构体代表这个内核模块。

 第二个参数:name是模块的名字,一般会拿模块文件的文件名作为模块名。它是这个模块的一个标识。


8.    struct class_device *class_device_create(struct class *cls, struct class_device *parent dev_t devt,  struct device *device,  const char *fmt, ...)
9.class_device_unregister
10.class_destroy
11.static inline void __iomem *ioremap(unsigned long offset, unsigned long size)
    将物理地址转换为虚拟地址,返回虚拟地址
    第一个参数:物理地址
    第二个参数:需要空间的大小
12.unregister_chrdev(major, "first_drv"); // 卸载设备

三.LED驱动程序实现(驱动代码)

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/arch/regs-gpio.h>
#include <asm/hardware.h>

/*建立major组设备号对应的设备节点*/
static struct class *firstdrv_class;//一个类
static struct class_device	*firstdrv_class_dev;//在上面类下建立设备

volatile unsigned long *gpfcon = NULL;
volatile unsigned long *gpfdat = NULL;


static int first_drv_open(struct inode *inode, struct file *file)
{
	//printk("first_drv_open\n");
	/* 配置GPF4,5,6为输出 */
	*gpfcon &= ~((0x3<<(4*2)) | (0x3<<(5*2)) | (0x3<<(6*2)));
	*gpfcon |= ((0x1<<(4*2)) | (0x1<<(5*2)) | (0x1<<(6*2)));
	return 0;
}

static ssize_t first_drv_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
{
	int val;

	//printk("first_drv_write\n");

	copy_from_user(&val, buf, count); //	copy_to_user();

	if (val == 1)
	{
		// 点灯
		*gpfdat &= ~((1<<4) | (1<<5) | (1<<6));
	}
	else
	{
		// 灭灯
		*gpfdat |= (1<<4) | (1<<5) | (1<<6);
	}
	
	return 0;
}

static struct file_operations first_drv_fops = {
    .owner  =   THIS_MODULE,    /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
    .open   =   first_drv_open,     
    .write  =	first_drv_write,	   
};


int major;//组设备号
/*
major作用
在VFS(虚拟文件系统)中实际是用一个数组来管理文件,而这个数组就是以组设备号来所谓索引
在注册驱动的时候register_chrdev函数会按照major作为索引在VFS对应位置填入file_operation
*/

static int first_drv_init(void)
{
	major = register_chrdev(0, "first_drv", &first_drv_fops); // 注册, 告诉内核.写0是系统自动分配组设备号(找空缺的组设备号)
    

	/*建立major组设备号对应的设备节点*/
	firstdrv_class = class_create(THIS_MODULE, "firstdrv");//创建一个类,<span style="font-family: Arial, Helvetica, sans-serif;">THIS_MODULE,</span><span style="font-family: Arial, Helvetica, sans-serif;">这是一个宏.</span><span style="font-family: Arial, Helvetica, sans-serif;">推向编译模块时自动创建的__this_module变量 </span>


	firstdrv_class_dev = class_device_create(firstdrv_class, NULL, MKDEV(major, 0), NULL, "xyz"); /* /dev/xyz 在上面创建的类下创建设备*/

	gpfcon = (volatile unsigned long *)ioremap(0x56000050, 16);
	gpfdat = gpfcon + 1;

	return 0;
}

static void first_drv_exit(void)
{
	unregister_chrdev(major, "first_drv"); // 卸载

	class_device_unregister(firstdrv_class_dev);
	class_destroy(firstdrv_class);
	iounmap(gpfcon);
}

module_init(first_drv_init);
module_exit(first_drv_exit);


MODULE_LICENSE("GPL");


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值