linux 字符设备驱动的学习

1、使用cat/prco/devices  可以查看当前已经被使用掉的设备号,选择未使用的,linux 支持动静态分配的设备号,此时采用的静态分配

驱动代码:这也是驱动代码的基本流程 ,这是一个虚拟的 chrdevbase 设备,再次使用的register_chrdev和unregister_chrdev 进行注册和销毁,但是这老版本的函数 

#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>

#define DEVICE_NUM 200 // cat /proc/devices”可以查看当前已经被使用掉的设备号,选择未使用的
#define CHRDEVBASE_NAME "mydevtest"
static char readbuf[100];
static char writebuf[100];
static char kerneldata[] = {"kernel data!"};
// static struct file_operations test_fops; // 定义了一个 file_operations 结构体变量 test_fops

static int chrdev_open(struct inode *inode, struct file *filp)
{
    printk(" dev open\r\n");
    return 0;
}
static ssize_t chrdev_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{
    int ret = 0;
    memcpy(readbuf, kerneldata, sizeof(kerneldata));
    ret = copy_to_user(buf, readbuf, cnt);
    // printk(" %s readbuf %s", buf, readbuf);
    if (ret == 0)
    {
        printk("kernel data send ok!\r\n");
    }
    else
    {
        printk("kernel data send fail\r\n");
    }
    return 0;
}
static ssize_t chrdev_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
    int ret = 0;
    ret = copy_from_user(writebuf, buf, cnt);
    if (ret == 0)
    {
        printk("kernel recevdata:%s\r\n", writebuf);
    }
    else
    {
        printk(" kernel data recv fail\r\n");
    }
    printk(" kernel data recv %d \r\n", ret);
    return 0;
}

static int chrdev_release(struct inode *inode, struct file *filp)
{
    // printk("chrdev release \r\n"); //开启log打印有问题
    // printk_ratelimit();
    return 0;
}
static struct file_operations test_fops = {
    .owner = THIS_MODULE,
    .open = chrdev_open,
    .read = chrdev_read,
    .write = chrdev_write,
    .release = chrdev_release,

};

static int __init chrdev_init(void) //__init”来修饰
{
    int ret = 0;
    ret = register_chrdev(DEVICE_NUM, CHRDEVBASE_NAME, &test_fops);
    if (ret < 0)
    {
        printk(" register fail\r\n");
    }
    printk("chrdevbase init!\r\n");
    return 0;
}

static void __exit chrdev_exit(void) //__exit”来修饰
{
    unregister_chrdev(DEVICE_NUM, CHRDEVBASE_NAME);
    printk("dev exit\r\n");
}

module_init(chrdev_init);
module_exit(chrdev_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("answer");

应用层代码:

#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"
/***************************************************************
Copyright © ALIENTEK Co., Ltd. 1998-2029. All rights reserved.
文件名		: chrdevbaseApp.c
作者	  	: 左忠凯
版本	   	: V1.0
描述	   	: chrdevbase驱测试APP。
其他	   	: 使用方法:./chrdevbase /dev/chrdevbase <1>|<2>
			 argv[2] 1:读文件
			 argv[2] 2:写文件
论坛 	   	: www.openedv.com
日志	   	: 初版V1.0 2019/1/30 左忠凯创建
***************************************************************/

static char usrdata[] = {"usr data!"};

/*
 * @description		: main主程序
 * @param - argc 	: argv数组元素个数
 * @param - argv 	: 具体参数
 * @return 			: 0 成功;其他 失败
 */
int main(int argc, char *argv[])
{
	int fd, retvalue;
	char *filename;
	char readbuf[100], writebuf[100];

	if (argc != 3)
	{
		printf("Error Usage!\r\n");
		return -1;
	}

	filename = argv[1];

	/* 打开驱动文件 */
	fd = open(filename, O_RDWR);
	if (fd < 0)
	{
		printf("Can't open file %s\r\n", filename);
		return -1;
	}

	if (atoi(argv[2]) == 1)
	{ /* 从驱动文件读取数据 */
		retvalue = read(fd, readbuf, 50);
		if (retvalue < 0)
		{
			printf("read file %s failed!\r\n", filename);
		}
		else
		{
			/*  读取成功,打印出读取成功的数据 */
			printf("read data:%s\r\n", readbuf);
		}
	}

	if (atoi(argv[2]) == 2)
	{
		/* 向设备驱动写数据 */
		memcpy(writebuf, usrdata, sizeof(usrdata));
		retvalue = write(fd, writebuf, 50);
		if (retvalue < 0)
		{
			printf("write file %s failed!\r\n", filename);
		}
	}

	/* 关闭设备 */
	retvalue = close(fd);
	if (retvalue < 0)
	{
		printf("Can't close file %s\r\n", filename);
		return -1;
	}

	return 0;
}

makefile

KERNELDIR := /home/answer/linux/alientek_linux/linux-imx-rel_imx_4.1.15//换成自己的环境
CURRENT_PATH := $(shell pwd)
obj-m := my_self.o

build: kernel_modules

kernel_modules:
	$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules
clean:
	$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean

下载到开发板,

demod

modprobe  mydevtest.ko

//加载驱动

创建设备在dev目录下: mknod /dev/mydevtest c 200 0

然后 执行 ./chrdevtesr /dev/mydevtest 1

./chrdevtesr /dev/mydevtest 2

卸载驱动 rmmod  mydevtest.ko

dev目录下的 直接rm  删除节点

 1、对于任何外设的驱动,最终都要配置相应的硬件寄存器,,编写linux 驱动,一定符合驱动框架

1. 首先 物理地址怎么映射到虚拟空间,需要MMU映射(MMU内存管理单元)

mmu:

1、管理虚拟空间到物理空间的映射

2、储存器的访问权限,内存保护

注意:多个虚拟空间可以映射同一地址

Linux 内核启动的时候会初始化 MMU,如果没有开启 MMU 的话直接向 0X020E0068 这个寄存器地址写入数据就可以配置 GPIO1_IO03 的复用功能。现在开启了 MMU,并且设置了内存映射,因此就不能直接向 0X020E0068 这个地址写入数据了。我们必须得到 0X020E0068 这个物理地址在 Linux 系统里面对应的虚拟地址,这里就涉及到了物理内存和虚拟内存之间的转换,需要用到两个函数: ioremap 和 iounmap  在arch/arm/include/asm/io.h文件中

对于 I.MX6ULL 来说一个寄存器是 4 字节(32 位)的,因此映射的内存长度为 4

使用 ioremap 函数将寄存器的物理地址映射到虚拟地址以后,我们就可以直接通过指针访问这些地址,但是 Linux 内核不建议这么做,而是推荐使用一组操作函数来对映射后的内存进行读写操作

映射后对于IO的读写操作函数

1、在此使用linux 新的API函数获取主设备和子设备的id

register_chrdev 

alloc_chrdev_region

register_chrdev_region

 2、注册方法

cdev 结构体在 include/linux/cdev.h 文件,先定义该结构体变量


3. 初始化cdev设备 对上述的箭头处的变量

void cdev_init(struct cdev *cdev, const struct file_operations *fops)

4.初始化完成后,向linux添加字符设备(cdev 结构体变量

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值