开发板文件系统操作指令
| 指令 | 功能 | 备注 |
|---|---|---|
| insmod+xxx.ko | 向开发板内核添加驱动文件 | |
| rmmod+xxx.ko | 从开发板内核中移除对应的驱动文件 | |
| ls /sys/bus/xxxbus/devices/ | 查看xxx总线中的设备文件 | 需要添加总线和设备文件 |
| ls /sys/bus/xxxbus/drivers/ | 查看xxx总线中的驱动文件 | 需要添加总线和驱动文件 |
| ls /dev/xxx | 查看添加的设备文件 | 就是device_create()第5个参数 |
| ls /proc | 查看设备号 | |
| ./xxx | 执行xxx可执行文件 | 需要添加可执行文件 |
| ls /proc/device-tree/ | 查看添加到设备树上的节点 | 要在设备树文件中添加节点并重新编译,拷贝编译后的设备树文件到tftpdir目录,重启开发板,利用tftp方式下载到开发板中。 |
| ls /proc/interrupts/ | 查看申请的中断 | 就是request_irq()的第4个参数 |
ubunt重要文件路径
注:该路径为个人路径
| 路径 | 描述 | 备注 |
|---|---|---|
| /home/ubuntu/code/kernel/linux-3.14 | Linux-3.14内核路径 | 包含众多内核文件 |
| /home/ubuntu/tftpdir | tftp服务路径 | 包含tftp服务上传下载文件 |
| /home/ubuntu/nfsdir/rootfs | nfs服务路径 | 包含nfs服务共享的文件(根文件系统) |
| /home/ubuntu/ARM_Tools/gcc-4.6.4/bin/arm-none-linux-gnueabi-gcc | gcc交叉编译工具路径 | |
| /home/ubuntu/code/kernel/linux-3.14/arch/arm/boot/dts | 设备树文件 | |
驱动文件框架
/* 头文件 */
#include <linux/module.h>
/* 函数入口实现 */
static int __init chr_dev_init(void)
{
printk("-----%s-----\n",__FUNCTION__);
return 0;
}
static void __exit chr_dev_init(void)
{
printk("-----%s-----\n",__FUNCTION__);
}
/* 模块入口声明 */
module_init(chr_dev_init);
module_exit(chr_dev_init);
MODULE_LICENSE("GPL");
Makefile脚本
KERNEL_DIR=/home/ubuntu/code/kernel/linux-3.14
CUR_DIR=$(shell pwd)
MODULE_NAME=module
APP_NAME=led_app
ROOTFS_DIR=/home/ubuntu/nfsdir/rootfs/drv_module
ifeq ($(KERNELRELEASE), )
all:
make -C $(KERNEL_DIR) M=$(CUR_DIR) modules
/home/ubuntu/ARM_Tools/gcc-4.6.4/bin/arm-none-linux-gnueabi-gcc $(APP_NAME).c -o $(APP_NAME)
clean:
make -C $(KERNEL_DIR) M=$(CUR_DIR) clean
install:
cp *.ko $(ROOTFS_DIR)
cp $(APP_NAME).o $(ROOTFS_DIR)
else
obj-m += $(MODULE_NAME).o
endif
应用层实现底层硬件的控制原理
首先应用程序想要实现底层硬件的控制,必须要通过内核去调用设备驱动来控制底层硬件,所以说直接控制硬件的是内核中的设备驱动。那么应用程序又是如何让设备驱动去控制硬件的呢?首先需要在内核中添加应用程序想要控制的硬件的设备驱动,让内核知道有这个驱动设备,有了想要的设备驱动,然后让应用程序关联这个设备驱动,实现应用程序 调用文件IO,设备驱动执行对应的接口函数。我们可以在这个接口函数中,编写程序实现对硬件寄存器的控制(地址的控制),最终实现硬件的控制。
这里有几个知识点
问题1、如何在内核中添加应用层想要的设备驱动?
答:通过创建字符设备文件(设备节点)来实现。这个字符设备文件其实就是一个设备信息结构体,
其中存放了我们想要的设备驱动信息,其中包含了,字符设备的信息内容、我们想要创建的设备驱动的设备号、以及给我们想要创建的设备驱动取得名字等信息。然而这些作为该结构体成员的设备驱动的具体参数值(内容),需要我们去创建出来,然后利用一个返回值为该结构体类型的指针函数struct device *device_create(),将这些参数值(内容)传到这个结构体中。这样才算创建完了一个字符设备文件,此时的字符设备文件中相当于被初始化了,将传入的参数值(内容)作为对应成员的值(内容)。
这里又有个问题:
问题1.1怎么创建该结构体(字符设备文件)的具体参数值(内容)呢?
在回答这个问题前,我们先要知道,这个结构体(设备文件)需要我们创建哪些具体的参数值(内容):创建的设备文件的信息内容、父类对象、设备号、私有数据、设备文件名。好,明白了要创建什么,现在就要知道,怎么创建
-
参数1的具体值(内容)-------设备文件的信息内容
通过 class_create()函数创建 struct class * class_create(owner,name); 例如:struct class * cls_led = class_create(THIS_MODULE,"led cls"); 参数1: owner:拥有者,一般THIS_MODULE 参数2: name:字符串,描述信息 返回值: struct class * 信息结构体 -
参数2的具体值(内容)-------创建父类对象
这个参数一般为NULL -
参数3的具体值(内容)--------设备号
在创建设备号之前需要明白: 设备号:32bit = 主设备号(高12bit) + 次设备号(低20bit) 主设备号:表示同一类设备 次设备号:表示同一类设备中的不同设备方式1: 其实以下两步操作可以通过一个封装好的函数来实现 -------MKDEV(主设备号,次设备号),返回值就是设备号方式2: 我们只需要设置主设备号,次设备号自动分配 /* 向内核申请注册设备号 */ int register_chrdev( unsigned int major, const char *name, const struct file_operations *fops) 例如:ret = register_chrdev(250,"led",&fops); 进一步计算的到设备号: dev_t devt = 250<<20 | 0; 参数1: unsigned int major:主设备号 无符号整数 参数2: const char *name:描述一个设备驱动信息,自定义,通俗来说就是设备名叫什么 字符串首地址 参数3: const struct file_operations *fops:文件操作对象 结构体地址(指针) 解释:文件操作对象,就是一个结构体的实例化对象的地址,该结构体中的成员都是函数指针,这个结构体可以实现应用层程序和存储驱动间的函数关联,置于这个关联有什么用,下面会详细讲,这里大概就说一下,就是应用层调用什么文件IO接口,底层设备驱动就执行该结构体实例化的对象中赋值的函数接口。 返回值: 正确返回0,失败返回负数 -
参数4的具体值(内容)-------私有数据
私有数据,一般填NULL -
参数5的具体值(内容)------设备文件名
一个字符串
问题1.2、得到了这个设备节点的成员的具体的值了(设备节点是一个结构体),怎么赋值给这个设备节点(结构体)呢?
当我们得到结构体(设备节点)所需要的成员的具体值(内容)后,就可以利用下面这个函数将值赋值给(设备节点)结构体中对应的成员。
struct device *device_create( struct class *class, //参数1
struct device *parent, //参数2
dev_t devt, //参数3
void *drvdata, //参数4
const char *fmt, //参数5
...)----创建设备节点
例如:struct device * led_dev = device_create(cls_led,NULL,devt,NULL,"led%d",3);
问题2、有了字符设备驱动文件,怎么关联应用程序和设备驱动呢?
首先我们要知道,什么是关联,所谓关联就是:在驱动中实现文件io接口功能,当应用程序调用文件io时,驱动程序也调用对应的文件io接口函数
在内核中设计好的结构体 struct file_operations 每一个成员变量都代表绑定一个系统调用(文件io)函数,只要对结构体中的成员赋值,就代表值绑定上一个文件io函数
struct file_operations {
struct module *owner;
loff_t (*llseek) (struct file *, loff_t, int);
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
int (*iterate) (struct file *, struct dir_context *);
unsigned int (*poll) (struct file *, struct poll_table_struct *);
long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
int (*mmap) (struct file *, struct vm_area_struct *);
int (*open) (struct inode *, struct file *);
int (*flush) (struct file *, fl_owner_t id);
int (*release) (struct inode *, struct file *);
int (*fsync) (struct file *, loff_t, loff_t, int datasync);
int (*aio_fsync) (struct kiocb *, int datasync);
int (*fasync) (int, struct file *, int);
int (*lock) (struct file *, int, struct file_lock *);
ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
int (*check_flags)(int);
int (*flock) (struct file *, int, struct file_lock *);
ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
int (*setlease)(struct file *, long, struct file_lock **);
long (*fallocate)(struct file *file, int mode, loff_t offset,
loff_t len);
int (*show_fdinfo)(struct seq_file *m, struct file *f);
};//函数指针的集合,
每个函数指针赋值为函数地址,就代表当应用程序调用对应的文件io函数时,驱动就执行函数指针赋值的对应函数
例如:
//驱动需要对应执行的IO函数实现,注意,这些IO函数是根据应用程序的需要进行关联和编写的,如果应用程序中没有用到相关的IO函数,就不需要关联和编写
int led_open (struct inode * inode, struct file * file)
{
printk("led_open\n");
return 0;
}
int led_close (struct inode * inode, struct file * file

本文详细介绍了Linux驱动的学习,包括开发板文件系统操作指令、Ubuntu的重要文件路径和驱动文件框架。讲解了如何在内核中添加设备驱动,如何关联应用程序和设备驱动,以及中断处理、虚拟内存地址映射、文件阻塞IO模型和异步收发数据等内容。同时,文章提供了实践练习,帮助读者深入理解驱动开发。
最低0.47元/天 解锁文章
2119

被折叠的 条评论
为什么被折叠?



