关于驱动的一次操作流程

1.驱动是什么?

        驱动是一个内核调用设备资源的载体,用户通过系统调用进入内核,然后内核操作驱动动作来操作外设寄存器地址。

2.一个cpu的linux框架

          内核态:操作系统运行的状态。

         用户态:用户编写的应用运行状态。

3.两者之间的沟通:

         系统调用。

4.linux下点灯,

        

...
fd=open("/sys/class/leds/heartbeat/trigger",O_RDWR);
if(fd)
{
  write(fd,1);
}
...

在cpp中加这段代码,会发现心跳led点亮。那么这段代码是怎么执行的呢?

1),open,write是c库给我们的系统调用函数,它会执行下面几个步骤:

         装载open的调用号;一般是操作系统提供的,可以搜一下。open的调用号为5

         触发系统调用中断:int 0x80

        存储当前进程的所有执行数据,一般是程序地址,堆栈。

        执行系统调用中断,进入内核态。

        判断调用号是否可用,执行内核open函数

        open函数的参数有驱动文件指针参数,根据驱动文件指针,执行驱动文件中的open函数。

       驱动文件中的open函数会操作物理地址映射的虚拟地址,进而操作外设寄存器。

      操作完成后,退出内核,加载原先进程的堆栈。

2)write也是一样,也会通过系统调用陷入内核,然后操作芯片物理外设,点亮led。

3)那么驱动的编写流程是什么

         了解芯片的物理外设地址。

         通过ioremap函数将需要操作物理地址映射到虚拟地址。

        通过提供的函数分配或者申请设备的主次设备号,主次设备号是设备树的标志,使得内核管理更加方便。

       动作函数定义,比如open函数,write函数

       文件操作结构体初始化

       模块初始化

       模块卸载方式

       证书权限,这个可以写个人名字和一些备注。

驱动文件;

    


#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/delay.h>
#include <linux/poll.h>
#include <linux/mutex.h>
#include <linux/wait.h>
#include <linux/uaccess.h>
#include <asm/io.h>
#include <linux/device.h>

static int major;
static struct class *led_class;

/* registers */
// RCC_PLL4CR地址:0x50000000 + 0x894
static volatile unsigned int *RCC_PLL4CR;

// RCC_MP_AHB4ENSETR 地址:0x50000000 + 0xA28
static volatile unsigned int *RCC_MP_AHB4ENSETR;

// GPIOA_MODER 地址:0x50002000 + 0x00
static volatile unsigned int *GPIOA_MODER;

// GPIOA_BSRR 地址: 0x50002000 + 0x18
static volatile unsigned int *GPIOA_BSRR;

static ssize_t led_write(struct file *filp, const char __user *buf,
			 size_t count, loff_t *ppos)
{
	char val;
	/* copy_from_user : get data from app */
	copy_from_user(&val, buf, 1);

	/* to set gpio register: out 1/0 */
	if (val)
	{
		/* set gpa10 to let led on */
		*GPIOA_BSRR = (1<<26);
	}
	else
	{

		/* set gpa10 to let led off */
		*GPIOA_BSRR = (1<<10);
	}
	return 1;
}

static int led_open(struct inode *inode, struct file *filp)
{
	/* enalbe PLL4, it is clock source for all gpio */
	*RCC_PLL4CR |= (1<<0);
	while ((*RCC_PLL4CR & (1<<1)) == 0);
	
	/* enable gpioA */
	*RCC_MP_AHB4ENSETR |= (1<<0);
	
	/*
	 * configure gpa10 as gpio
	 * configure gpio as output 
	 * 01:General purpose output mode
	 */
	*GPIOA_MODER &= ~(3<<20);
	*GPIOA_MODER |= (1<<20);

	return 0;
}

static struct file_operations led_fops = {
	.owner		= THIS_MODULE,
	.write		= led_write,
	.open		= led_open,
};

/* 入口函数 */
static int __init led_init(void)
{
	printk("%s %d\n", __FUNCTION__, __LINE__);
	
	major = register_chrdev(0, "100ask_led", &led_fops);

	/* ioremap(base_phy, size); */
	// RCC_PLL4CR地址:0x50000000 + 0x894
	RCC_PLL4CR = ioremap(0x50000000 + 0x894, 4);
	
	// RCC_MP_AHB4ENSETR 地址:0x50000000 + 0xA28
	RCC_MP_AHB4ENSETR = ioremap(0x50000000 + 0xA28, 4);
	
	// GPIOA_MODER 地址:0x50002000 + 0x00
	GPIOA_MODER = ioremap(0x50002000 + 0x00, 4);
	
	// GPIOA_BSRR 地址: 0x50002000 + 0x18
	GPIOA_BSRR = ioremap(0x50002000 + 0x18, 4);

	led_class = class_create(THIS_MODULE, "myled");
	device_create(led_class, NULL, MKDEV(major, 0), NULL, "myled"); /* /dev/myled */
	
	return 0;
}

static void __exit led_exit(void)
{
	iounmap(RCC_PLL4CR);
	iounmap(RCC_MP_AHB4ENSETR);
	iounmap(GPIOA_MODER);
	iounmap(GPIOA_BSRR);
	
	device_destroy(led_class, MKDEV(major, 0));
	class_destroy(led_class);
	
	unregister_chrdev(major, "100ask_led");
}

module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");

4)编译驱动

      可以试着写一下makefile,但是基本上有现成模板稍微更改就行。

5)加载驱动

    insmod,动态加载。***.ko

   静态是直接编译进内核。

6)卸载驱动

    rmmode。   ***.ko

5.其实每一步都是无数人花费很长时间逐渐搭建的框架,每一步都值得深入学习,这是我目前所了解的用户如何调用系统资源的流程,会不断更新,不断学习。

6.总结:其实这些东西也没有想像中的可怕,一点一点的搞,将所有的疑惑通过资料查找解决,基本上就没什么问题了。

7.参考:http://t.csdn.cn/QGNuy    (这是驱动介绍的,非常详细)

             http://t.csdn.cn/TqBxe      (这是系统调用的,非常详细)

            

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值