目录
1、在Linux中什么是驱动?
让一个硬件(文件)正常工作的代码。
在Linux系统中一切皆文件!!!
操作硬件文件接口函数:打开---open;关闭---close;读---read;写---write
上层open会调用内核层的open
上层close会调用内核层的close
2、Linux驱动的设备文件特点
Linux下设备文件分为三大类:
字符设备文件:几乎所有的设备文件都是字符设备文件
块设备文件:存储设备---DRAM SD卡 Emmc
网络设备文件:WiFi...
crw-rw---- 1 root video 81, 0 6月 14 09:56 /dev/video0
*Linux下设备文件一定都有设备号!
81,0 就是摄像头设备的设备号!
就好比ID号
* 81 是主设备号
主设备号代表一类设备
* 0 是次设备号
次设备号代表这类设备不同设备
* 81 和 0 虽说你看到的是两个号
但是在Linux系统下他俩会合并成
一个号码--uint32_t 号码
81 占据高12 位
0 占据低 20 位
0000 0101 0001 0000 0000 0000 0000 0000
这个就是真实的在系统中的设备号!
3、Linux下杂项驱动特点
杂项设备驱动的特点:
* Linux下最简单的驱动开发的方法
* Linux下杂项设备主设备号固定10
* Linux下杂项设备次设备号0-255
* 但是系统已经占据了一部分杂项设备
* 你不知道系统占用了哪个次设备号
一般为了防止冲突
所以一般使用杂项设备的时候
我们往往指定次设备号为 255
255 代表让系统给你分配一个可以用的
* 杂项会自动生成一个设备文件
他也是唯一自动生成设备文件的
驱动开发的方法
驱动开发有三种方法:
杂项
经典
Linux2.6
4、Linux下杂项驱动的API
杂项的关键字: misc
头文件 : <linux/miscdevice.h>
函数的功能:往内核注册一个杂项设备
函数头文件:<linux/miscdevice.h>
<linux/fs.h>
函数的原型:
int misc_register(
struct miscdevice * misc
)
函数的参数:
misc:
就是该杂项注册的核心结构体
该结构体描述你要注册什么设备!
描述一个设备:
生成一个什么名字设备文件
描述文件的打开的内核函数
描述文件的关闭的内核函数
.....................
设备的设备号
函数返回值:
成功返回 0
失败返回 -1
struct miscdevice {
int minor;
const char *name;
const struct file_operations *fops;
struct list_head list;
struct device *parent;
struct device *this_device;
const char *nodename;
umode_t mode;
};
仅需要填充前三个成员变量即可完成注册
minor:
major:主设备号
minor:次设备号
255 代表让系统帮你分配一个
name:
你要生成一个设备文件
你要告诉你生成的设备文件
名字叫什么
假如说我填的是 ccc
他就会在 /dev/ccc 文件
fops:
文件的接口!
内核的文件接口!
内核层也有一套 文件接口!
open close read write
用户层去调用 open的时候
他会间接调用内核层的open
close read write 也是同理
+++++++++++++++++++++++++++++++++++++++
struct file_operations {
struct module *owner;
........
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
ssize_t (*write) (struct file *,
int (*open) (struct inode *, struct file *);
int (*release) (struct inode *, struct file *);
...........
};
owner:
固定填写 THIS_MODULE
open:
内核层的open的函数指针
release:
内核层的close的函数指针
read:
内核层的read的函数指针
write :
内核层的write的函数指针
+++++++++++++++++++++++++++++++++++++++
函数的功能:取消注册一个杂项设备
自动的删除设备文件
函数头文件:同上
函数的原型:
int misc_deregister(
struct miscdevice * misc
)
函数的参数:
misc:
你注册的那个结构体往里填入即可!
函数返回值:
成功返回 0
失败返回 -1
5、Linux下杂项驱动的开发示例
#include "linux/module.h"
#include "linux/kernel.h"
#include "linux/miscdevice.h"
#include "linux/fs.h"
#include "linux/gpio.h"
uint32_t * base = NULL;
#define GPD0_CON * (base)
#define GPD0_DAT *(base + 1)
//加载函数
struct miscdevice misc;
struct file_operations ops;
//上层打开/dev/BIGLED 文件就会跳到内核层
//调用这个 myBIGLED_open
int myBIGLED_open(struct inode * inode, struct file *file)
{
printk("我是内核层的开灯函数!\r\n");
/*
在此处打开LED灯
如果没有GPIO子系统
那么你首先想到的方法是什么
*/
GPD0_DAT|=(0x01<<0);
return 0;
}
//当上层关闭 /dev/BIGLED文件
//就会调用内核层的 myBIGLED_close 函数
int myBIGLED_close(struct inode * inode, struct file *file)
{
printk("我是内核层的关灯函数!\r\n");
/*
关闭LED灯
*/
GPD0_DAT&=~(0x01<<0);
return 0;
}
/*
GPD0_0 引脚
GPD0_CON 0x1140 00A0
GPD0_DAT 寄存器 0x1140 00A4
*/
static int __init led_init(void)
{
ops.owner = THIS_MODULE;
ops.open = myBIGLED_open;
ops.release = myBIGLED_close;
misc.minor = 255;//系统分配次设备号
misc.name = "BIGLED";//在/dev/BIGLED 设备文件
misc.fops =&ops;
int ret = misc_register(&misc);
if(ret < 0)
{
printk("注册失败\r\n");
return -EINVAL;//加载失败
}
base = ioremap(0x114000A0,8);
GPD0_CON &=~(0x0F<<0);//清零
GPD0_CON |=(0x01<<0);//输出
return 0;
}
//卸载函数
static void __exit led_exit(void)
{
misc_deregister(&misc);
//取消注册设备
}
module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");
6、Linux下的内存映射
函数的功能:在内核中映射真实的物理地址
举个例子:真实物理地址是 0x11111
通过此函数他会给你返回一个映射后的地址
0xbbbb
那么我去访问或者操作0xbbbb 就相当于
操作 0x11111
函数头文件:#include <linux/io.h>
函数的原型:
void * ioremap(cookie, size)
函数的参数:
cookie:
真实的物理地址
size:
你要映射的物理地址的大小
只要是32位的CPU
他的寄存器一般都是四字节对齐!
每个寄存器大小都是四个字节
函数返回值:
成功返回映射后的 地址
失败返回 NULL
7、Linux下的GPIO子系统
gpio_request(unsigned gpio, const char * label)
申请注册使用一个IO口
gpio: 要申请使用哪个IO口
label: 要给这个IO口起个标签名--无所谓
gpio_free(unsigned gpio)
释放一个GPIO口
gpio:要释放哪个IO口
gpio_direction_output(unsigned gpio, int value)
要把gpio口设置为输出工作模式!
有个输出的默认电平---value
1 -- 高电平
0 -- 低电平
gpio_direction_input(unsigned gpio)
要把gpio口设置为输入工作模式!
gpio_set_value(unsigned gpio,int value);
使用此函数的前提是 GPIO口已经是 输出工作模式了
int value 是 1 就代表高电平 0 -- 低电平
gpio_get_value(unsigned gpio)
获取这个IO口的状态
返回值 int 返回0代表当前低电平
返回1代表当前高电平
针对开发板--exynos4412CPU
它的GPIO口全部定义在
arch/arm/mach-exynos/include/mach
此文件夹是开发板的专属文件夹
有个头文件 gpio.h
此文件定义着开发板的所有的GPIO口的定义
举个例子:
想用 GPM2_3 引脚
EXYNOS4X12_GPM2(3);
想用 GPD0_0
EXYNOS4_GPD0(0);