04.驱动学习复盘(原始架构)

1.在单片机和裸机中我们操作硬件,

//操作寄存器
unsigned int *p = 0x12345678;
*p = 0x87654321;

2.在Linux上使能了MMU,不能直接操作物理地址,需要把物理地址转换成虚拟地址。

3.内核提供了相关的函数:

ioremap:把物理地址转换成虚拟地址

iounmap:释放掉ioremap映射的地址

static inline void __iomem *ioremap(phys_addr_t offset, unsigned long size)
参数:映射物理地址的起始地址,要映射多大的内存空间
返回值:成功返回虚拟地址的首地址,失败返回NULL

static inline void iounmap(void __iomem *addr)
参数:要取消映射的虚拟地址的首地址

注意:物理地址只能被映射一次,多次映射会失败

4.cat /proc/iomem 查看哪些物理地址被映射过了。

5.一个相对完整的驱动实践编写,以蜂鸣器为例,要先根据硬件原理图和数据手册确定相关的寄存器地址。还是直接上代码了。

app:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

int main(int argc, char *argv[])
{
	int fd;

	char buf[64] = {0};

	fd = open("/dev/beep_drv",O_RDWR);

	if(fd < 0)
	{
		perror("open error\n");
		return fd;
	}
	
	//接收来自命令行的数据
	//atoi将字符串转换成整形
	buf[0] = atoi(argv[1]);

	write(fd,buf,sizeof(buf));

	close(fd);

	return 0;
}

驱动:

#include <linux/init.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/io.h>

//物理地址
#define PWM 0xC0018000
//映射完的虚拟地址首地址
unsigned int *vir_pwm_dr;

int beep_open(struct inode * inode, struct file * file)
{
	printk("beep_open\n");

	return 0;
}

int beep_release (struct inode *inode, struct file *file)
{
	printk("beep_release\n");

	return 0;
}

ssize_t beep_read (struct file *file, char __user *ubuf, size_t size, loff_t *loff_t)
{
	char kbuf[64] = "hahaha";
	
	if(copy_to_user(ubuf, kbuf, strlen(kbuf)) != 0)
	{
		printk("copy_to_user error\n");
		return -1;
	}

	return 0;
}

ssize_t beep_write (struct file *file, const char __user *ubuf, size_t size, loff_t *loff_t)
{
	char kbuf[64] = {0};
	
	if(copy_from_user(kbuf, ubuf, size) != 0)
	{
		printk("copy_from_user error\n");
		return -1;
	}

	printk("beep_write\n");

	if(kbuf[0] == 1)
		//左移一位
		*vir_pwm_dr |= (1<<1);
	else if(kbuf[0] == 0)
		*vir_pwm_dr &= ~(1<<1);
		
	return 0;
}

struct file_operations beep_fops = {
	.owner = THIS_MODULE,
	.open = beep_open,
	.release = beep_release,
	.read = beep_read,
	.write = beep_write
};
	
struct miscdevice beep_dev = {

	.minor = MISC_DYNAMIC_MINOR,
	.name = "beep_drv",
	.fops = &beep_fops
};

static int __init beep_init(void)
{
	int ret;

	ret = misc_register(&beep_dev);

	if(ret < 0)
	{
		printk("beep register is error\n");
		return -1;
	}
	
	printk("beep_init\n");

	//映射虚拟地址(物理地址,要映射的大小)
	vir_pwm_dr = ioremap(PWM, 4);

	if(vir_pwm_dr == NULL)
	{
		printk("PWM ioremap error\n");
		//Linux标准错误
		return -EBUSY;
	}

	printk("PWM ioremap ok\n");

	return 0;
}

static void __exit beep_exit(void)
{
	misc_deregister(&beep_dev);

	//取消映射(映射完的虚拟地址的首地址)
	iounmap(vir_pwm_dr);
	
	printk("beep_exit\n");
}

module_init(beep_init);
module_exit(beep_exit);

MODULE_LICENSE("GPL");

6.驱动传参,例如安装驱动时:insmod beep.ko a=1

驱动传参的作用:

(1)设置驱动的相关参数,比如设置缓冲区的大小

(2)设置安全校验,防止我们写的驱动被别人盗用

7.驱动传参使用的函数,

(1)传递普通的参数,比如 char, int 类型的

//参数:参数名,类型,参数读写权限
module_param(name,type,perm);

(2)传递数组

//参数:参数名、类型、实际传入的个数、参数读写权限
module_param_array(name, type, nump, perm);

8.关于驱动传参的驱动代码:

//包含宏定义的头文件
#include <linux/init.h>
//包含初始化加载模块的头文件
#include <linux/module.h>

static int a;
static int b[5];
//实际传入的个数
static int count;

//int、char类型参数传递
//参数名、类型、读写权限
module_param(a, int, S_IRUSR);

//传递数组
//参数名、类型、实际传入的个数、参数读写权限
module_param_array(b, int, &count, S_IRUSR);

//装载卸载函数入口
static int __init hello_init(void)
{	
	int i;
	for(i=0;i<count;i++)
	{
		printk("b[%d] = %d \n",i,b[i]);
	}	
	printk("count = %d\n",count);
	printk("a = %d\n",a);
	return 0;
}

static void __exit hello_exit(void)
{
	printk("a = %d\n",a);
	printk("hello_exit\n");
}

//驱动模块装载卸载声明
module_init(hello_init);
module_exit(hello_exit);

//开源许可证GPL声明
MODULE_LICENSE("GPL");



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值