今天开始学习字符设备驱动。学习这个分为两个部分,字符设备的使用和字符设备的编写
第一部分:字符设备的使用
1.使用字符设备,先编写Makefile
object -m := memdev.o
KDIR := /需要编写到内核的内核路径
all:
make -C $(KDIR) M=$(PWD) modules CROSS_COMPILE=arm-linux- ARCH=arm
2.make生成memdev.ko
安装字符设备
insmod memdev.ko
然后查看设备号
创建字符设备文件
mknod /dev/文件名 c 主设备号 次设备号
3.应用层程序,可以调用open函数直接打开创建的字符设备设备文件,并且可以用其他函数进行操作。
第二部分:字符设备的编写
1.编写内核模块:
#include<linux/module.h>
#include<linux/init.h>
int memdev_init()
{
return 0;
}
void memdev_exit()
{
}
module_init(memdev_init);
module_exit(memdev_exit);
2.驱动初始化
在模块的初始化函数中。
(1)分配cdev。
静态分配:struct cdev mdev
动态分配:mdev = cdev_alloc()
(2)初始化cdev,用cdev_init
这个函数的原型可以在Linux内核中直接复制
static const struct file_operations mem_fops =
{
.llseek = mem_llseek,
.read = mem_read,
.write = mem_write,
.open = mem_open,
.release = mem_release,
};
cdev_init(&cdev, &mem_fops);
(3)注册cdev,用cdev_add
这里面用到了设备号
设备号的分配有两种:
静态分配:自己直接定义一个设备号,然后用register_chrdev_region( 0, 2, "memdev");申请设备号
动态分配:alloc_chrdev_region(&devno, 0, 2, "memdev");
alloc_chrdev_region(&devno, 0, 2, "memdev");
cdev_add(&cdev, devno, 2);
3.实现设配操作
函数原型在内核中可以直接找到。
先将五个函数的原型进行修改成5个函数。
然后对每一个进行编写。
**************************************************************************************************************\
打开函数的实现:
int mem_open(struct inode *inode, struct file *filp)
{
}
(1)先打开硬件(这次是虚拟的所以省略)
(2)提取次设备号,次设备号在inode中
int num=MINOR(inode->i_rdev)
(3)把设备的寄存器的基地址保存在struct file的private_data这个成员中,这里用数组来表示寄存器
int dev1_regs[5];
int dev2_regs[5];
if(num == 0)
file->private_data = dev1_regs;
if(num == 1)
file->private_data = dev1_regs;
return 0;
**************************************************************************************
关闭函数的实现:
因为是虚拟的所以只需要返回就可以了。
/*文件释放函数*/
int mem_release(struct inode *inode, struct file *filp)
{
return 0;
}
******************************************************************************************
读函数的实现:
(1)从struct file中取出寄存器的基地址。
int *register_addr = filp->private_data; /*获取设备的寄存器基地址*/
(2)通过copy_form_user,将从设备中读到的数据返回给应用程序
copy_to_user(buf,register_addr+ (*ppos/*偏移*/),size/*大小*/)
(3)收尾
将读写指针修改为刚才读到的位置
file->f_pos += size;
*********************************************************************
写函数
写和读类似
(1)从struct file中取出寄存器的基地址。
int *register_addr = filp->private_data; /*获取设备的寄存器基地址*/
(2)通过copy_from_user,将从设备中读到的数据返回给应用程序
copy_from_user(register_addr + p, buf, count))
(3)收尾
将读写指针修改为刚才读到的位置
file->f_pos += size;
*************************************************************************
偏移函数:
switch(whence) {
case SEEK_SET:
newpos = offset;
break;
case SEEK_CUR:
newpos = filp->f_pos + offset;
break;
case SEEK_END:
newpos = 5*sizeof(int)-1 + offset;
break;
default:
return -EINVAL;
}
if ((newpos<0) || (newpos>5*sizeof(int)))
return -EINVAL;
filp->f_pos = newpos;