linux驱动 — 常用API

Posted 超凡东皇

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了linux驱动 — 常用API相关的知识,希望对你有一定的参考价值。

一、设备树—API

①of_find_compatible_node 函数

②of_get_named_gpio 函数

③irq_of_parse_and_map 函数

二、GPIO—API

①gpio_request 函数

②gpio_free 函数

③gpio_direction_input 函数

④gpio_direction_output 函数

⑤gpio_is_valid 函数

⑥gpio_get_value 函数

⑦gpio_set_value 函数

三、中断—API

①request_irq 函数

②free_irq 函数

③enable_irq()与disable_irq()函数

四、Pinctrl—API 

①devm_pinctrl_get 函数

②devm_pinctrl_put 函数

③pinctrl_lookup_state 函数

④pinctrl_select_state函数


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 标号。

valueGPIO 默认输出值。

返回值:0,设置成功;负值,设置失败。

⑤gpio_is_valid 函数

检测gpio端口是否合法,函数原型如下:

int gpio_is_valid(int number); 

函数参数和返回值含义如下:

numbergpio端口号

返回值:无效反为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的主要内容,如果未能解决你的问题,请参考以下文章

# Java 常用代码片段

linux驱动 — regalator使用

linux驱动 — regalator使用

Express实战 - 应用案例- realworld-API - 路由设计 - mongoose - 数据验证 - 密码加密 - 登录接口 - 身份认证 - token - 增删改查API(代码片段

Linux/安卓驱动入门视频

Linux驱动实践:你知道字符设备驱动程序的两种写法吗?