linux驱动 — 常用API
Posted 超凡东皇
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了linux驱动 — 常用API相关的知识,希望对你有一定的参考价值。
inux驱动开发,首先从使用驱动的API开始,先会使用API,然后才能更深入的分析,本篇所列的都是驱动开发中非常常用的API,之所以从设备树API开始,是本人觉得驱动开发的源头在设备树,并且大部分驱动都跟设备树相关。
一、设备树—API
①of_find_compatible_node 函数
of_find_compatible_node 函数根据 device_type 和 compatible 这两个属性查找指定的节点, 函数原型如下:
struct device_node *of_find_compatible_node(struct device_node,*from, const char *type,
const char *compatible)
函数参数和返回值含义如下:
from:开始查找的节点,如果为 NULL 表示从根节点开始查找整个设备树。
type:要查找的节点对应的 type 字符串,也就是 device_type 属性值,可以为 NULL,表示忽略掉 device_type 属性。
compatible:要查找的节点所对应的 compatible 属性列表。
返回值:找到的节点,如果为 NULL 表示查找失败
②of_get_named_gpio 函数
此函数获取 GPIO 编号,因为 Linux 内核中关于 GPIO 的 API 函数都要使用 GPIO 编号,此函数会将设备树中类似<&gpio5 7 GPIO_ACTIVE_LOW>的属性信息转换为对应的 GPIO 编号,此函数在驱动中使用很频繁!函数原型如下:
int of_get_named_gpio(struct device_node *np, const char *propname, int index)
函数参数和返回值含义如下:
np:设备节点。
propname:包含要获取 GPIO 信息的属性名。
③irq_of_parse_and_map 函数
编写驱动的时候需要用到中断号,我们用到中断号,中断信息已经写到了设备树里面,因
此可以通过 irq_of_parse_and_map 函数从 interupts 属性中提取到对应的设备号,函数原型如下: unsigned int irq_of_parse_and_map(struct device_node *dev, int index)
函数参数和返回值含义如下:
dev:设备节点。
index:索引号,interrupts 属性可能包含多条中断信息,通过 index 指定要获取的信息。
返回值:中断号。
示例
//设备树节点
&misc {
compatible = "misc,test";
interrupt-parent = <&pio>;
interrupts = <8 IRQ_TYPE_EDGE_FALLING>;
test-gpio = <&pio 30 0>;
};
//API使用示例
//获取指定节点,获取不到时返回NULL
struct device_node *nd = of_find_compatible_node(NULL, NULL, "misc,test");
//获取GPIO编号,获取不到时返回负值
int test_gpio = of_get_named_gpio(nd, "test-gpio", 0);
//获取中断号,获取不到时返回负值
static unsigned int tp_irq = irq_of_parse_and_map(nd, 0);
...
二、GPIO—API
①gpio_request 函数
gpio_request 函数用于申请一个 GPIO 管脚,在使用一个 GPIO 之前一定要使用 gpio_request 进行申请
函数原型如下:
int gpio_request(unsigned gpio, const char *label)
函数参数和返回值含义如下:
gpio:要申请的 gpio 标号,使用 of_get_named_gpio 函数从设备树获取指定 GPIO 属性信息,此函数会返回这个 GPIO 的标号。
label:给 gpio 设置个名字。
返回值:0,申请成功;其他值,申请失败。
②gpio_free 函数
如果不使用某个 GPIO 了,那么就可以调用 gpio_free 函数进行释放。函数原型如下:
void gpio_free(unsigned gpio)
函数参数和返回值含义如下:
gpio:要释放的 gpio 标号。
返回值:无。
③gpio_direction_input 函数
此函数用于设置某个 GPIO 为输入,函数原型如下所示:
int gpio_direction_input(unsigned gpio)
函数参数和返回值含义如下:
gpio:要设置为输入的 GPIO 标号。
返回值:0,设置成功;负值,设置失败。
④gpio_direction_output 函数
此函数用于设置某个 GPIO 为输出,并且设置默认输出值,函数原型如下:
int gpio_direction_output(unsigned gpio, int value)
函数参数和返回值含义如下:
gpio:要设置为输出的 GPIO 标号。
value:GPIO 默认输出值。
返回值:0,设置成功;负值,设置失败。
⑤gpio_is_valid 函数
检测gpio端口是否合法,函数原型如下:
int gpio_is_valid(int number);
函数参数和返回值含义如下:
number:gpio端口号
返回值:无效反为0
⑥gpio_get_value 函数
此函数用于获取某个 GPIO 的值(0 或 1),此函数是个宏,定义所示:
#define gpio_get_value __gpio_get_value
int __gpio_get_value(unsigned gpio)
函数参数和返回值含义如下:
gpio:要获取的 GPIO 标号。
返回值:非负值,得到的 GPIO 值;负值,获取失败。
⑦gpio_set_value 函数
此函数用于设置某个 GPIO 的值,此函数是个宏,定义如下
#define gpio_set_value __gpio_set_value
void __gpio_set_value(unsigned gpio, int value)
函数参数和返回值含义如下:
gpio:要设置的 GPIO 标号。
value:要设置的值。
返回值:无
示例
//设备树节点
&misc {
compatible = "misc,test";
interrupt-parent = <&pio>;
interrupts = <8 IRQ_TYPE_EDGE_FALLING>;
test-gpio = <&pio 30 0>;
};
//API使用示例
//获取指定节点,获取不到时返回NULL
struct device_node *nd = of_find_compatible_node(NULL, NULL, "misc,test");
//获取GPIO编号,获取不到时返回负值
int test_gpio = of_get_named_gpio(nd, "test-gpio", 0);
/
/
//设置gpio为输出,同时设置输出寄存器为0,即低电平
int val = gpio_direction_output(test_gpio, 0);
//获取gpio状态,高低电平(0或1)
int status = gpio_get_value(test_gpio);
//设置gpio输出寄存器为1,即高电平
gpio_set_value(test_gpio, 1);
//读写操作可能导致睡眠,在中断中就需要使用带cansleep的函数
//gpio_get_value_cansleep(test_gpio);
//gpio_set_value_cansleep(test_gpio, 1);
//设置gpio为输入
gpio_direction_input(test_gpio);
...
三、中断—API
①request_irq 函数
在 Linux 内核中要想使用某个中断是需要申请的,request_irq 函数用于申请中断,request_irq 函数可能会导致睡眠,因此不能在中断上下文或者其他禁止睡眠的代码段中使用 request_irq 函数。request_irq 函数会激活(使能)中断,所以不需要我们手动去使能中断
request_irq 函数原型如下:
int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, const char *name,
void *dev) 函数参数和返回值含义如下:
irq:要申请中断的中断号。
handler:中断处理函数,当中断发生以后就会执行此中断处理函数。
flags:中断标志,可以在文件 include/linux/interrupt.h 里面查看所有的中断标志
name:中断名字,设置以后可以在/proc/interrupts 文件中看到对应的中断名字。
dev:如果将 flags 设置为 IRQF_SHARED 的话,dev 用来区分不同的中断,一般情况下将dev 设置为设备结构体,dev 会传递给中断处理函数 irq_handler_t 的第二个参数。
返回值:0 中断申请成功,其他负值 中断申请失败,如果返回-EBUSY 的话表示中断已经
被申请了。
②free_irq 函数
void free_irq(unsigned int irq, void *dev_id)
关于该函数参数的一些说明如下:
- irq参数是已经申请的硬件中断号;
- dev_id参数和request_irq()函数的dev参数对应,一般为设备的设备结构体或者 NULL。
③enable_irq()与disable_irq()函数
在Linux设备驱动中断编程中,如果想要使能或者屏蔽中断的话,可以使用enable_irq()和disable_irq()内核函数接口。
使能中断IRQ,可以使用enable_irq()函数接口,该函数的定义如下:
void enable_irq(unsigned int irq) { unsigned long flags; struct irq_desc *desc = irq_get_desc_buslock(irq, &flags, IRQ_GET_DESC_CHECK_GLOBAL); if (!desc) return; if (WARN(!desc->irq_data.chip, \\ KERN_ERR"enable_irq before setup/request_irq: irq %u\\n", irq)) goto out; __enable_irq(desc); out: irq_put_desc_busunlock(desc, flags); }
屏蔽中断IRQ,可以使用disable_irq()和disable_irq_nosync()函数,这两个函数的定义如下:
void disable_irq(unsigned int irq) { if (!__disable_irq_nosync(irq)) synchronize_irq(irq); } void disable_irq_nosync(unsigned int irq) { __disable_irq_nosync(irq); }
两个函数的形参都是irq,表示要屏蔽中断的硬件中断号,这两个函数的区别在于,disable_irq()函数会等待目前的中断处理完成,而disable_irq_nosync()函数则不会等待。
示例:
//设备树节点
&misc {
compatible = "misc,test";
interrupt-parent = <&pio>;
interrupts = <8 IRQ_TYPE_EDGE_FALLING>;
test-gpio = <&pio 30 0>;
};
//API使用示例
//获取指定节点,获取不到时返回NULL
struct device_node *nd = of_find_compatible_node(NULL, NULL, "misc,test");
//获取GPIO编号,获取不到时返回负值
int test_gpio = of_get_named_gpio(nd, "test-gpio", 0);
//获取中断号,获取不到时返回负值
static unsigned int tp_irq = irq_of_parse_and_map(nd, 0);
//申请一个 GPIO ,使用前需要向系统申请一下,别人就用不能用了
gpio_request(test_gpio, "test_gpio");
//申请中断,同时注册test_interrupt_handler为回调函数
int ret = request_irq(tp_irq, (irq_handler_t) test_interrupt_handler, IRQF_TRIGGER_FALLING, "test-eint", NULL);
...
四、Pinctrl—API
①devm_pinctrl_get 函数
struct pinctrl * devm_pinctrl_get(struct device *dev);
函数功能:根据设备获取pin操作句柄,所有的pin操作必须基于此pinctrl句柄。与pinctrl_get接口功能完全一样,只是devm_pinctrl_get会将申请的pinctrl句柄做记账,绑定到设备句柄信息中。设备驱动申请pin资源,推荐优先使用devm_pinctrl_get接口。
返回值 pinctrl句柄
参数 dev:使用pin的设备,pinctrl子系统会通过设备名与pin配置信息匹配。
②devm_pinctrl_put 函数
函数原型 void devm_pinctrl_put(struct pinctrl *p);
函数功能 释放pinctrl句柄,必须与devm_pinctrl_get配对使用。
返回值 无
参数 p:pinctrl句柄
③pinctrl_lookup_state 函数
函数原型 struct pinctrl_state * pinctrl_lookup_state(struct pinctrl *p, const char *name);
函数功能 查找pin句柄指定状态下的状态句柄。
返回值 状态句柄
参数 p:pinctrl句柄
name:状态名称,A33平台上只有default一种状态
④pinctrl_select_state函数
函数原型 int pinctrl_select_state(struct pinctrl *p, struct pinctrl_state *s);
函数功能 设置pin句柄的状态到硬件
返回值 0表示成功,其它表示失败
参数 p:pinctrl句柄
S:状态句柄
示例
//设备树节点
default: tp_test_pins_1: eint@8 {
pins_cmd_dat {
pins = <MT8163_PIN_30_EINT8__FUNC_GPIO30>;
/*配置成输入下拉*/
slew-rate = <0>;
bias-pull-down = <00>;
/*//配置成输入pull disable
slew-rate = <0>;
bias-disable;
//配置成输出high
slew-rate = <1>;
bias-disable;
output-high;
//配置成输出low
slew-rate = <1>;
bias-disable;
output-low;*/
};
};
tp_test_pins_1: eint@8 {
pins_cmd_dat {
pins = <MT8163_PIN_30_EINT8__FUNC_GPIO30>;
slew-rate = <1>;
bias-disable;
output-high;
};
};
&misc {
compatible = "misc,test";
interrupt-parent = <&pio>;
interrupts = <8 IRQ_TYPE_EDGE_FALLING>;
eint-debounce = <256>;
pinctrl-names = "default","tp_test_1";
pinctrl-0 = <&default>;
pinctrl-1 = <&tp_test_pins_1>;
test1-gpio = <&pio 30 0>;
test2-gpio = <&pio 31 0>;
status = "okay";
};
//获得设备对应的pin control state holder
static struct pinctrl *pinctrl_test = devm_pinctrl_get(&dev);
//查找pin control state
struct pinctrl_state *tp_test_1 = pinctrl_lookup_state(pinctrl_test, "default");
struct pinctrl_state *tp_test_2 = pinctrl_lookup_state(pinctrl_test, "tp_test_1");
//设置gpio功能
pinctrl_select_state(pinctrl_test, tp_test_1 );
pinctrl_select_state(pinctrl_test, tp_test_2 );
注:Pinctrl模块兼容GPIO的功能,如果pin是作为GPIO input/output,仍然可以使用gpiolib中的标准接口,但是如果要使用GPIO的复用功能,则需要使用pinctrl接口。
在驱动代码中我们经常会见到一些以devm开头的函数,这一类的函数都是和设备资源管理(Managed Device Resource)相关的,驱动中提供这些函数主要是为了方便对于申请的资源进行释放,比如:irq、regulator、gpio等等。在驱动进行初始化的时候如果失败,那么通常会goto到某个地方释放资源,这样的标签多了之后会让代码看起来不简洁,devm就是为来处理这种情况。
以上是关于linux驱动 — 常用API的主要内容,如果未能解决你的问题,请参考以下文章
Express实战 - 应用案例- realworld-API - 路由设计 - mongoose - 数据验证 - 密码加密 - 登录接口 - 身份认证 - token - 增删改查API(代码片段