Implementación del controlador de plataforma del árbol de dispositivos del controlador en el sistema Linux

Un controlador simple todos los días, con el tiempo, se familiarizará cada vez más con los controladores de Linux y será cada vez más fácil aprender a escribir programas de controladores. Hoy procederemos con el controlador de dispositivo de plataforma debajo del árbol de dispositivos.

En el artículo anterior, explicamos el método tradicional de escritura de controladores y dispositivos de plataforma que no utiliza un árbol de dispositivos. El último kernel de Linux ya admite el árbol de dispositivos, por lo que es particularmente importante cómo escribir el controlador de plataforma en el árbol de dispositivos. En este capítulo, aprenderemos cómo escribir el controlador de plataforma en el árbol de dispositivos.

1. Introducción al controlador de plataforma debajo del árbol de dispositivos

El marco del controlador de la plataforma se divide en bus, dispositivo y controlador. No es necesario que nosotros, los programadores de controladores, administremos el bus. Esto lo proporciona el kernel de Linux. Al escribir el controlador, solo debemos centrarnos en la implementación específica del dispositivo y controlador.

En el kernel de Linux sin árbol de dispositivos, debemos escribir y registrar platform_device y platform_driver respectivamente, que representan el dispositivo y el controlador respectivamente.

Cuando usamos el árbol de dispositivos, la descripción del dispositivo se coloca en el árbol de dispositivos, por lo que no necesitamos escribir platform_device, solo necesitamos implementar platform_driver.

Al escribir un controlador de plataforma basado en el árbol de dispositivos, debemos prestar atención a los siguientes puntos:

  • 1. Cree un nodo de dispositivo en el árbol de dispositivos.

No hay duda de que primero debe crear un nodo de dispositivo en el árbol de dispositivos para describir la información del dispositivo. La clave es establecer el valor del atributo compatible, porque el autobús de la plataforma debe coincidir con el conductor a través del valor del atributo compatible del nodo del dispositivo! Es importante recordar esto.

  • 2. Preste atención a los atributos de compatibilidad al escribir controladores de plataforma.

Al utilizar el árbol de dispositivos, el controlador de la plataforma guardará el valor de compatibilidad a través de of_match_table, que indica con qué dispositivos es compatible el controlador. Por lo tanto, of_match_table será particularmente importante

  • 3. Escribir controlador de plataforma

El controlador de plataforma basado en el árbol de dispositivos es básicamente el mismo que el controlador de plataforma sin árbol de dispositivos en el capítulo anterior. La función de sonda se ejecutará cuando el controlador y el dispositivo coincidan correctamente. Necesitamos ejecutar el controlador del dispositivo de caracteres en la función de sonda. Cuando el módulo del controlador no está registrado, se ejecutará la función de eliminación. Todos son similares.

2. Modifique el archivo del árbol de dispositivos.

Modifique el archivo del árbol de dispositivos, agregue la información del dispositivo que necesitamos, usaremos una luz LED en este capítulo.

2.1 Agregar nodo de dispositivo LED

Cree un nodo de luz LED debajo del nodo raíz "/", el nombre del nodo es "gpioled" y el contenido del nodo es el siguiente:

gpioled {
    #address-cells = <1>;
    #size-cells = <1>;
    compatible = "imx6ull-gpioled";
    pinctrl-name = "default";
    pinctrl-0 = <&pinctrl_led>;
    led-gpio = <&gpio1 3 GPIO_ACTIVE_LOW>;

    status = "okay";
};
2.2 Agregar nodo pinctrl

Las luces LED en la placa de desarrollo I.MX6U-ALPHA usan el PIN GPIO1_IO03. Abra imx6ul-14x14-evk.dtsi y cree un subnodo llamado "pinctrl_led" debajo del subnodo imx6ul-evk del nodo iomuxc. El contenido es el siguiente. Se muestra:

pinctrl_led: ledgrp {
    fsl,pins = <
        MX6UL_PAD_GPIO1_IO03__GPIO1_IO03 0x10B0 /* LED0 */
    >;
};
2.3 Comprobar si el PIN es utilizado por otros periféricos

3. Escritura del controlador de plataforma

Cree un nuevo archivo de controlador llamado dtsplatform_driver.c e ingrese el siguiente contenido en dtsplatform_driver.c:

/***********************************************************
 * Copyright © toto Co., Ltd. 1998-2029. All rights reserved.
 * Description: 
 * Version: 1.0
 * Autor: toto
 * Date: Do not edit
 * LastEditors: Seven
 * LastEditTime: Do not edit
***********************************************************/
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of_gpio.h>
#include <linux/semaphore.h>
#include <linux/irq.h>
#include <linux/fcntl.h>
#include <linux/fs.h>
#include <linux/platform_device.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>

#define LEDDEV_CNT  1               /* 设备号数量 */
#define LEDDEV_NAME "dts_platform_led"  /* 设备名字 */
#define LED_ON      1
#define LED_OFF     0

/* led_dev 设备结构体 */
struct led_dev {
    dev_t devid;                /* 设备号 */
    struct cdev cdev;           /* cdev */
    struct class *class;        /* 类 */
    struct device *device;      /* 设备 */
    int major;                  /* 主设备号 */
    struct device_node *node;   /* 设备节点 */
    int led_gpio;               /* led gpio号 */             
};

struct led_dev leddev; /* led 设备 */

/*
 * @Brief   led 打开、关闭接口
 * @Param   sta:1打开,0关闭
 * @Note    NOne
 * @RetVal  NOne
 */
void led_switch(u8 sta)
{
    if (sta == LED_ON) {
        gpio_set_value(leddev.led_gpio, 0);
    } else if (sta == LED_OFF) {
        gpio_set_value(leddev.led_gpio, 1);
    }
}

/*
 * @Brief   打开设备
 * @Param   inode:传递给驱动的inode
 * @Param   filp:设备文件
 * @Note    NOne
 * @RetVal  NOne
 */
static int led_open(struct inode *inode, struct file *filp)
{
    /* 设置私有数据 */
    filp->private_data = &leddev;

    return 0;
}

/*
 * @Brief   向设备写数据
 * @Param   filp:设备文件
 * @Param   buf:要写入设备的数据
 * @Param   cnt:要写入的数据长度
 * @Param   offt:相对于文件首地址的偏移
 * @Note    NOne
 * @RetVal  写入的字节数,如果为负值,表示写入失败
 */
static ssize_t led_write(struct file *filp, const char __user *buf,
                        size_t cnt, loff_t *offt)
{
    int ret;
    unsigned char databuf[1];
    unsigned char ledstat;

    ret = copy_from_user(databuf, buf, cnt);
    if (ret < 0) {
        return -EFAULT;
    }

    ledstat = databuf[0];
    led_switch(ledstat);

    return 0;
}

/* 设备操作函数 */
static struct file_operations led_fops = {
    .owner = THIS_MODULE,
    .open  = led_open,
    .write = led_write, 
};

/*
 * @Brief
 * @Param   None
 * @Note    NOne
 * @RetVal  NOne
 */
static int led_probe(struct platform_device *dev)
{
    printk(KERN_INFO "led driver and device has matched\n");

    /* 注册字符设备驱动 */
    /* 1.创建设备号 */
    if (leddev.major) {
        leddev.devid = MKDEV(leddev.major, 0);
        register_chrdev_region(leddev.devid, LEDDEV_CNT, LEDDEV_NAME);
    } else {
        alloc_chrdev_region(&leddev.devid, 0, LEDDEV_CNT, LEDDEV_NAME);
        leddev.major = MAJOR(leddev.devid);
    }

    /* 2.注册设备 */
    cdev_init(&leddev.cdev, &led_fops);
    cdev_add(&leddev.cdev, leddev.devid, LEDDEV_CNT);

    /* 3.创建类 */
    leddev.class = class_create(THIS_MODULE, LEDDEV_NAME);
    if (IS_ERR(leddev.class)) {
        return PTR_ERR(leddev.class);
    }

    /* 4.创建设备 */
    leddev.device = device_create(leddev.class, NULL, leddev.devid,
                                    NULL, LEDDEV_NAME);
    if (IS_ERR(leddev.device)) {
        return PTR_ERR(leddev.device);
    }

    /* 5.初始化IO */
    leddev.node = of_find_node_by_path("/gpioled");
    if (leddev.node == NULL) {
        printk("gpioled node not found\n");
        return -EINVAL;
    }

    leddev.led_gpio = of_get_named_gpio(leddev.node, "led-gpio", 0);
    if (leddev.led_gpio < 0) {
        printk("can't get led-gpio\n");
        return -EINVAL;
    }

    gpio_request(leddev.led_gpio, "my_led");
    /* 设置输出模式,默认高电平 */
    gpio_direction_output(leddev.led_gpio, 1);

    return 0;
}

/*
 * @Brief   移除 platform 驱动函数
 * @Param   dev:platform设备
 * @Note    NOne
 * @RetVal  NOne
 */
static int led_remove(struct platform_device *dev)
{
    gpio_set_value(leddev.led_gpio, 1);

    cdev_del(&leddev.cdev);
    unregister_chrdev_region(leddev.devid, LEDDEV_CNT);
    device_destroy(leddev.class, leddev.devid);
    class_destroy(leddev.class);

    return 0;
}

/* 匹配列表 */
static const struct of_device_id led_of_match[] = {
    { .compatible = "imx6ull-gpioled"},
    { /* sentinel */}
};

/* platform 驱动结构体 */
static struct platform_driver led_driver = {
    .driver = {
        .name = "imx6ull-led",          /* 驱动名字,用于和设备匹配 */
        .of_match_table = led_of_match, /* 设备树匹配表 */
    },
    .probe = led_probe,
    .remove = led_remove,
};

/*
 * @Brief   驱动模块加载函数
 * @Param   None
 * @Note    NOne
 * @RetVal  NOne
 */
static int __init leddriver_init(void)
{
    return platform_driver_register(&led_driver);
}

/*
 * @Brief   驱动模块卸载函数
 * @Param   None
 * @Note    NOne
 * @RetVal  NOne
 */
static void __exit leddriver_exit(void)
{
    platform_driver_unregister(&led_driver);
}

module_init(leddriver_init);
module_exit(leddriver_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("toto");
  • Líneas 33 a 112, el controlador de dispositivo de caracteres tradicional, nada que decir.

  • Líneas 120 ~ 164, la función de sonda del controlador de plataforma. Esta función se ejecutará cuando el nodo del dispositivo en el árbol de dispositivos y el controlador coincidan exitosamente. El trabajo realizado originalmente en la función de carga del controlador ahora se completa en la función de sonda.

  • Líneas 171 ~ 180, función de eliminación, esta función se ejecutará cuando se desinstale el controlador de la plataforma. En esta función, se libera memoria, se anula el registro de los dispositivos de caracteres, etc., es decir, todo el trabajo en la función de desinstalación del controlador original se completa en la función de eliminación.

  • Las líneas 183 ~ 186, la tabla de coincidencias, describen con qué tipo de dispositivos coincide este controlador.

  • La línea 184 agrega un valor de atributo compatible con el valor "atkalpha-gpioled". Cuando el valor de atributo compatible de un nodo de dispositivo en el árbol de dispositivos también es "atkalpha-gpioled", coincidirá con este controlador.

  • Líneas 189 ~ 196, estructura del controlador platform_driver, la línea 191 establece el nombre de este controlador de plataforma en "imx6ulled", por lo tanto, cuando el controlador se carga correctamente, habrá un archivo llamado "imx6uled".

  • La línea 192 establece of_match_table en led_of_match arriba.

  • Líneas 203 ~ 206, función de carga del módulo del controlador. En esta función, registre el controlador led_driver con el kernel de Linux a través de platform_driver_register.

  • Líneas 213 ~ 216, función de desinstalación del módulo del controlador. En esta función, el controlador led_driver se desinstala del kernel de Linux a través de platform_driver_unregister.

4. Preparación de la aplicación de prueba

Cree un nuevo archivo de programa de prueba llamado dtsplatform_app.c e ingrese el siguiente contenido en dtsplatform_app.c:

/***********************************************************
 * Copyright © toto Co., Ltd. 1998-2029. All rights reserved.
 * Description: 
 * Version: 1.0
 * Autor: toto
 * Date: Do not edit
 * LastEditors: Seven
 * LastEditTime: Do not edit
***********************************************************/
#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"

#define LEDON   1
#define LEDOFF  0 

/*
 * @Brief
 * @Param   None
 * @Note    NOne
 * @RetVal  NOne
 */
int main(int argc, char *argv[])
{
    int fd, retval;
    char *filename;
    unsigned char databuf[1];

    if (argc != 3) {
        printf("Error argc par cnt\n");
        return -1;
    }

    filename = argv[1];

    fd = open(filename, O_RDWR);
    if (fd < 0) {
        printf("file %s open failed\n", filename);
        return -1;
    }

    databuf[0] = atoi(argv[2]);
    retval = write(fd, databuf, sizeof(databuf));
    if (retval < 0) {
        printf("led control failed\n");
        close(fd);
        return -1;
    }

    close(fd);

    return 0;
}

5. Ejecute la prueba

5.1 Compilación

1. Compile el controlador y escriba el Makefile. El contenido del Makefile es el siguiente:

KERNELDIR := /home/toto/workspace/linux/linux-5.19
CURRENT_PATH := $(shell pwd)
obj-m := dtsplatform_driver.o

build: kernel_modules

kernel_modules:
    $(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules
clean:
    $(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean

Comando de compilación:

make -j8

Una vez que la compilación sea exitosa, se generará un archivo de módulo de controlador llamado "dtsplatform_driver.ko".

2. Compile la aplicación de prueba Comando de compilación:

arm-linux-gnueabihf-gcc dtsplatform_app.c -o dtsplatform_app

Una vez que la compilación sea exitosa, se generará la aplicación platform_app.

5.2 Ejecución de pruebas

Encienda la placa de desarrollo, copie los dos archivos dtsplatform_driver.ko y dtsplatform_app al directorio /lib/modules/5.19.0-g794a2f7be62d-dirty/ e ingrese el siguiente comando para cargar el módulo del controlador dtsplatform_driver.ko:

insmod dtsplatform_driver.ko

Después de cargar el módulo del controlador, vaya al directorio /sys/bus/platform/drivers/ para verificar si el controlador existe. Configuramos el campo de nombre de led_driver (tipo de controlador de plataforma) en "imx6ull-led" en dtsplatform_driver.c, por lo que estará en /sys/. Hay un archivo llamado "imx6ull-led" en el directorio bus/platform/drivers/, y el resultado se muestra en la siguiente figura:

imagen

De la misma manera, también hay archivos de dispositivos LED en el directorio /sys/bus/platform/devices/, que es el nodo gpioled en el árbol de dispositivos. Los resultados son los siguientes:

imagen

Después de que el módulo del controlador y el módulo del dispositivo se carguen exitosamente, el bus de la plataforma coincidirá. Cuando el controlador y el dispositivo coincidan exitosamente, se generará una línea de declaraciones como se muestra a continuación:

/lib/modules/5.19.0-g794a2f7be62d-dirty # insmod dtsplatform_driver.ko
[   45.657342] led driver and device has matched

Después de que el controlador y el dispositivo coincidan exitosamente, puede probar el controlador de la luz LED. Ingrese el siguiente comando para encender la luz LED:

./dtsplatform_app /dev/dts_platform_led 1

Ingrese el siguiente comando para apagar la luz LED:

./dtsplatform_app /dev/dts_platform_led 0

El comando para desinstalar el controlador es el siguiente:

rmmod dtsplatform_driver.ko

Supongo que te gusta

Origin blog.csdn.net/weixin_41114301/article/details/132776583
Recomendado
Clasificación