Explicación detallada de las funciones del marco del controlador.

El marco del artículo se basa en el capítulo anterior.

https://blog.csdn.net/qq_52749711/article/details/132409329
son casi iguales. Los nombres aquí han sido cambiados, pero siguen siendo fieles a sus orígenes.

función de entrada module_init

Al igual que la función principal en modo usuario, llama a la función dentro de los corchetes para ejecutarse, y la función dentro es la función de entrada.
Por ejemplo:

static int  pin5_drv_init(void);//函数声明
module_init(pin5_drv_init)

La función contiene código como el registro del controlador.

función de salida module_exit

Funciones que deben ejecutarse después de ejecutarse, el principio es el mismo que el anterior

module_exit(pin5_drv_exit);

La función contiene código para desinstalar el controlador, etc.

registrarse_chrdev

register_chrdevLa función es una de las funciones del kernel de Linux que se utiliza para registrar controladores de dispositivos de caracteres. Un dispositivo de caracteres es un dispositivo que interactúa con flujos de caracteres, como terminales, puertos serie, etc. En el kernel de Linux, el controlador de dispositivo de caracteres implementa operaciones como leer y escribir dispositivos de caracteres a través de funciones de operación de archivos.

La función generalmente se declara de la siguiente manera:

int register_chrdev(unsigned int major, const char *name, struct file_operations *fops);

Descripción de parámetros:

major: Especifique el número de dispositivo principal. Para dispositivos de caracteres, el número de dispositivo principal es el número que identifica de forma exclusiva un controlador de dispositivo. El número de dispositivo principal se puede MAJOR(dev_t dev)obtener mediante una macro.
name: Especifique el nombre del dispositivo, utilizado para mostrar el nombre del dispositivo en /proc/devices.
fops: file_operationsPuntero a una estructura que contiene las funciones operativas del controlador del dispositivo de caracteres, como lectura, escritura, apertura, cierre, etc.

valor de retorno:

Tras el registro exitoso, se devuelve el número de dispositivo principal asignado.
Cuando falla el registro, se devuelve un valor negativo, normalmente un código de error.
Después de usar register_chrdevla función para registrar el controlador del dispositivo de caracteres, debe llamar a esta función en la función de inicialización del módulo. Por ejemplo:

#include <linux/module.h>
#include <linux/fs.h>

static int my_chardev_open(struct inode *inode, struct file *file)
{
    
    
    // Open operation implementation
    return 0;
}

static int my_chardev_release(struct inode *inode, struct file *file)
{
    
    
    // Release operation implementation
    return 0;
}

static struct file_operations my_fops = {
    
    
    .open = my_chardev_open,
    .release = my_chardev_release,
    // Other operation implementations
};

static int __init my_chardev_init(void)
{
    
    
    int major = register_chrdev(0, "my_chardev", &my_fops);
    if (major < 0) {
    
    
        printk(KERN_ALERT "Failed to register char device\n");
        return major;
    }
    printk(KERN_INFO "Registered char device with major number %d\n", major);
    return 0;
}

static void __exit my_chardev_exit(void)
{
    
    
    unregister_chrdev(major, "my_chardev");
    printk(KERN_INFO "Unregistered char device\n");
}

module_init(my_chardev_init);
module_exit(my_chardev_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("Sample Character Device Driver");

my_chardev_open (función abierta)

my_chardev_openEs una función en el controlador del dispositivo de caracteres, que se utiliza para manejar la operación de apertura del dispositivo. Al registrar un dispositivo de caracteres, debe struct file_operationsestablecer un puntero a su propia función abierta implementada en la estructura. El kernel llamará a esta función cuando se abra el dispositivo.

Esta es la definición de la función my_chardev_open:

static int my_chardev_open(struct inode *inode, struct file *file)
{
    
    
    // Open operation implementation
    return 0;
}

En esta función, puede realizar tareas relacionadas con la operación de apertura del dispositivo, como inicializar el estado del dispositivo, asignar recursos, registrar la cantidad de veces que se abre el dispositivo, etc. Esta función recibirá dos parámetros:

struct inode *inode: Este es un puntero al nodo de índice (inodo) del archivo y contiene información de metadatos sobre el archivo. Cuando se abre el dispositivo, el kernel pasa el inodo relevante a la función de apertura.

  • inodo de estructura *inodo:

  • inode(Nodo de índice) contiene la información de metadatos del archivo, como los permisos, el tamaño, el usuario, etc. Identifica de forma única un archivo en el sistema de archivos.
    Puede utilizar i_modelos campos para obtener la información de permisos del archivo y los campos i_uidy i_gidpara obtener los ID de usuario y grupo del archivo.

  • i_privateLos campos se pueden utilizar para almacenar datos privados relacionados con el dispositivo. Puede configurar este campo durante la inicialización del controlador.

struct file *file: Este es un puntero a una estructura de datos que representa el archivo. Contiene información relacionada con las operaciones de archivos, como el modo de acceso, la ubicación del archivo, etc.

  • archivo de estructura *archivo:

  • La estructura del archivo contiene información relacionada con la apertura del archivo, como la ubicación del archivo, el modo de acceso, etc.

  • El campo f_pos indica el desplazamiento de posición actual del archivo.

  • El campo f_flags contiene los indicadores utilizados al abrir el archivo, como leer, escribir, agregar, etc.

  • El campo f_mode contiene el modo de acceso al abrir el archivo, que se puede determinar mediante operaciones de bits.

  • El campo private_data se puede utilizar para almacenar datos privados relacionados con operaciones de archivos y puede configurarlo al abrir el archivo.

El valor de retorno de una función es un número entero, generalmente utilizado para indicar si la operación fue exitosa. Si la operación de apertura tiene éxito, se acostumbra devolver 0, lo que indica que no se produjo ningún error. Si se produce un error, se puede devolver un valor negativo, correspondiente a un código de error diferente.

A continuación se muestra un ejemplo que muestra cómo my_chardev_openimplementar la operación de apertura en una función:

static int my_chardev_open(struct inode *inode, struct file *file)
{
    
    
    // Perform device-specific tasks during open, if any
    printk(KERN_INFO "Device opened\n");
    
    // Increment the device's open count (if you want to track it)
    try_module_get(THIS_MODULE);
    
    return 0; // Return 0 to indicate success
}

En el ejemplo anterior, utilizamos printkuna función para generar un registro que indica que el dispositivo se ha abierto. Si desea realizar un seguimiento del número de veces que se ha abierto un dispositivo, puede try_module_get(THIS_MODULE)incrementar el recuento de referencia del módulo del kernel usando . module_put(THIS_MODULE)De esta manera puede disminuir el recuento de referencia cuando el dispositivo está apagado .

En resumen, my_chardev_openla función le permite realizar algunas operaciones cuando se abre el dispositivo de caracteres. Puede escribir el código de operación de apertura apropiado según las características y necesidades del dispositivo.

static int my_chardev_open(struct inode *inode, struct file *file)
{
    
    
    // 访问 inode 信息
    printk(KERN_INFO "文件权限: %o\n", inode->i_mode & 0777);
    printk(KERN_INFO "文件所有者用户ID: %d\n", inode->i_uid.val);
    printk(KERN_INFO "文件所有者组ID: %d\n", inode->i_gid.val);

    // 访问文件信息
    printk(KERN_INFO "文件位置: %lld\n", file->f_pos);
    printk(KERN_INFO "文件标志: %x\n", file->f_flags);

    // 在文件结构中设置 private_data
    file->private_data = /* 在此处添加你的私有数据 */;

    return 0;
}

función de escritura

El prototipo de función pin5_writese parece a la función de operación de escritura en el controlador del dispositivo de caracteres, que se utiliza para escribir datos desde el espacio del usuario en el dispositivo. Déjame explicarte qué significan los parámetros de esta función:

file: Este es un puntero de archivo de estructura que representa un archivo abierto y contiene información relacionada con el archivo abierto. Este parámetro especifica el archivo que se escribirá.
buf: Este es un puntero a un búfer de espacio de usuario que contiene los datos que se escribirán en el dispositivo. __user es un indicador que indica que se trata de datos del espacio del usuario y, por lo tanto, deben manejarse con cuidado en el espacio del kernel.
count: Este es el número de bytes a escribir, especificando la longitud de los datos en el búfer.
ppos: Este es un puntero al tipo loff_t, que indica el desplazamiento de posición actual del archivo. Durante una operación de escritura, es posible que el kernel necesite actualizar esta ubicación.

El valor de retorno de la función es de tipo ssize_t, indica el número de bytes escritos, o si ocurre un error, se devuelve un valor negativo, correspondiente a un código de error diferente.
Aquí hay una implementación simplificada de una función pin5_write de ejemplo para ilustrar cómo funciona:

static ssize_t pin5_write(struct file *file,const char __user *buf,size_t count, loff_t *ppos)
{
    
    
        printk("pin5_write\n");
        return 0;
}

En el desarrollo real de controladores, es necesario implementar la función pin5_write de acuerdo con las características y requisitos del dispositivo. Por ejemplo, puede agregar operaciones como escribir datos en el dispositivo y actualizar las compensaciones de archivos según sea necesario. Al mismo tiempo, garantice la copia y verificación de datos adecuadas entre el espacio del kernel y el espacio del usuario para garantizar la seguridad y la estabilidad.

#include <linux/fs.h>
#include <linux/uaccess.h>

// 假设你的设备在打开时已经被初始化为pin5设备

static ssize_t pin5_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
{
    
    
    ssize_t written = 0;

    // 验证用户空间内存并将数据从用户空间复制到内核空间
    if (!access_ok(VERIFY_READ, buf, count))
        return -EFAULT;

    // 在内核中分配一个临时缓冲区
    char *kernel_buf = kmalloc(count, GFP_KERNEL);
    if (!kernel_buf)
        return -ENOMEM;

    // 从用户空间复制数据到内核缓冲区
    if (copy_from_user(kernel_buf, buf, count)) {
    
    
        kfree(kernel_buf);
        return -EFAULT;
    }

    // 在这里执行将数据写入设备的操作,示例中省略

    // 记录写入的字节数
    written = count;

    // 释放临时分配的内存
    kfree(kernel_buf);

    // 更新文件位置偏移
    *ppos += written;

    return written;
}

En el código anterior, primero verificamos si la memoria del espacio de usuario es accesible y utilizamos access_okfunciones para lograrlo. Luego asignamos un búfer temporal en el espacio del kernel y usamos copy_from_useruna función para copiar los datos del espacio del usuario al espacio del kernel.

La siguiente parte debería ser la operación real de escribir datos desde el búfer del kernel al dispositivo. Esto puede implicar el funcionamiento de registros del dispositivo, transmisión de datos, etc., que pueden implementarse en función de las características de su dispositivo.

Finalmente, registramos la cantidad de bytes escritos y liberamos la memoria asignada temporalmente. Además, el desplazamiento de la posición del archivo se ha actualizado para mantener la posición correcta del archivo después de las operaciones de escritura.

Cabe señalar que en el desarrollo real de controladores, es necesario manejarlos adecuadamente de acuerdo con las características del dispositivo, la versión del kernel y los requisitos. Es muy importante garantizar la exactitud y seguridad de los datos y la gestión razonable de la memoria.

clase_crear

La función class_create es una función en el kernel de Linux que se utiliza para crear clases de dispositivos. Las clases de dispositivos son una forma de organizar y administrar dispositivos que permiten agrupar dispositivos relacionados y proporcionar propiedades y operaciones comunes para estos dispositivos. Esto es útil para la gestión de dispositivos de caracteres, dispositivos de bloques, etc.

struct class *class_create(struct module *owner, const char *name);

Descripción de parámetros:

owner: Especifica el módulo del kernel propietario de esta clase. Generalmente, puede configurarlo en THIS_MODULE, lo que significa que el módulo que creó la clase es el módulo actual.
name: Especifique el nombre de la clase de dispositivo. Este nombre se mostrará en el directorio /sys/class y se utilizará para identificar la clase de dispositivo.
valor de retorno:

Si tiene éxito, devuelve un puntero a la clase de dispositivo recién creada.
En caso de error, se devuelve un puntero de error.
Después de llamar class_createa la función, puede registrar los dispositivos relevantes en esta clase y luego el kernel creará los archivos de dispositivo correspondientes para estos dispositivos y /sys/classcreará los directorios correspondientes en el directorio.

A continuación se muestra un ejemplo que muestra cómo utilizar la función class_create:

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/device.h>

static struct class *my_class;

static int __init my_module_init(void)
{
    
    
    my_class = class_create(THIS_MODULE, "my_device_class");
    if (IS_ERR(my_class)) {
    
    
        printk(KERN_ALERT "Failed to create class\n");
        return PTR_ERR(my_class);
    }

    // Create and register devices here

    printk(KERN_INFO "Module initialized\n");
    return 0;
}

static void __exit my_module_exit(void)
{
    
    
    class_destroy(my_class);
    printk(KERN_INFO "Module exited\n");
}

module_init(my_module_init);
module_exit(my_module_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("Sample Device Class Module");

En el ejemplo anterior, class_createprimero se crea una clase de dispositivo denominada usando una función "my_device_class". Luego puede agregar dispositivos relevantes a esta clase creándolos y registrándolos en la función de inicialización del módulo. Cuando el módulo salga, use class_destroyla función para destruir la clase de dispositivo.

Cabe señalar que class_create y las funciones de operación del dispositivo relacionadas se utilizan para el desarrollo del módulo del kernel. Si está desarrollando un controlador de dispositivo de caracteres u otro tipo de módulo del kernel, puede usar estas funciones para administrar clases de dispositivos y dispositivos según sea necesario.

dispositivo_create

device_createLa función es una función utilizada para crear un dispositivo en el kernel de Linux. /sys/class/Crea un subdirectorio para clases de dispositivos y crea un archivo de dispositivo en ese subdirectorio. Esta función normalmente class_createse usa junto con funciones para asociar un dispositivo con una clase de dispositivo específica.

Este es device_createel prototipo de la función:

struct device *device_create(struct class *class, struct device *parent, dev_t devt, void *drvdata, const char *fmt, ...);

Descripción de parámetros:

class: Especifique la clase de dispositivo a la que pertenece el dispositivo. Este parámetro suele ser el puntero de clase de dispositivo devuelto por la función class_create.
parent: Especifica el dispositivo principal del dispositivo. Generalmente, se puede configurar en NULL.
devt: Especifique el número de dispositivo del dispositivo. Puede utilizar la macro MKDEV (mayor, menor) para crear el número de dispositivo.
drvdata:Especifica el puntero de datos privados del dispositivo.
fmt: Especifique el formato del nombre del dispositivo, utilizado para crear subdirectorios y archivos de dispositivo en /sys/class/.

valor de retorno:

Si tiene éxito, devuelve un puntero al dispositivo recién creado.
En caso de error, se devuelve un puntero de error.
A continuación se muestra un ejemplo que muestra cómo utilizar la función device_create:

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/device.h>

static struct class *my_class;
static struct device *my_device;

static int __init my_module_init(void)
{
    
    
    my_class = class_create(THIS_MODULE, "my_device_class");
    if (IS_ERR(my_class)) {
    
    
        printk(KERN_ALERT "Failed to create class\n");
        return PTR_ERR(my_class);
    }

    // Create and register devices here

    my_device = device_create(my_class, NULL, MKDEV(0, 0), NULL, "my_device");
    if (IS_ERR(my_device)) {
    
    
        printk(KERN_ALERT "Failed to create device\n");
        class_destroy(my_class);
        return PTR_ERR(my_device);
    }

    printk(KERN_INFO "Module initialized\n");
    return 0;
}

static void __exit my_module_exit(void)
{
    
    
    device_destroy(my_class, MKDEV(0, 0));
    class_destroy(my_class);
    printk(KERN_INFO "Module exited\n");
}

module_init(my_module_init);
module_exit(my_module_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("Sample Device Module");

En el ejemplo anterior, primero class_createcreamos una clase de dispositivo usando funciones. Luego, se device_createcreó un dispositivo nombrado bajo esa clase usando funciones "my_device". Al salir del módulo, usamos device_destroyuna función para destruir el dispositivo y luego class_destroyuna función para destruir la clase de dispositivo.

Cabe señalar que device_createlas funciones de operación del dispositivo relacionadas se utilizan para el desarrollo del módulo del kernel. Si está desarrollando un controlador de dispositivo de caracteres u otro tipo de módulo del kernel, puede utilizar estas funciones para crear y administrar dispositivos según sea necesario.

ioremapa

En el desarrollo del kernel de Linux, ioremaplas funciones se utilizan para asignar direcciones físicas al espacio del kernel para que el kernel pueda acceder directamente a los registros de hardware, la memoria periférica, etc. en estas direcciones. Esto se debe a que el acceso a los registros de hardware generalmente requiere el uso de instrucciones de E/S especiales, y estas instrucciones solo se pueden ejecutar en modo kernel.

ioremapEl prototipo de la función es el siguiente:

void __iomem *ioremap(resource_size_t phys_addr, size_t size);

Descripción de parámetros:

phys_addr: La dirección física que se asignará.
size: El tamaño del mapa, en bytes.
valor de retorno:

Si tiene éxito, devuelve un puntero al área mapeada.
En caso de error, se devuelve un puntero nulo.
Antes de utilizar ioremapla función, debe asegurarse de que su dirección física sea válida y no sea utilizada por otras partes. Después del mapeo, puede usar punteros en el kernel para acceder al contenido del área mapeada, como si accediera a la memoria normal.

El siguiente es un ejemplo que muestra cómo utilizar la función ioremap para acceder a registros de hardware.

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/io.h>

static void __iomem *hw_regs;

static int __init my_module_init(void)
{
    
    
    // 为物理地址0x12345678映射一个大小为4字节的映射区域
    hw_regs = ioremap(0x12345678, 4);
    if (!hw_regs) {
    
    
        printk(KERN_ALERT "Failed to remap hardware registers\n");
        return -ENOMEM;
    }

    // 使用映射后的指针来访问寄存器
    unsigned int reg_value = readl(hw_regs);
    printk(KERN_INFO "Hardware register value: %u\n", reg_value);

    printk(KERN_INFO "Module initialized\n");
    return 0;
}

static void __exit my_module_exit(void)
{
    
    
    // 取消映射
    iounmap(hw_regs);

    printk(KERN_INFO "Module exited\n");
}

module_init(my_module_init);
module_exit(my_module_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("Sample I/O Remapping Module");

En el ejemplo anterior, primero usamos ioremapuna función para 0x12345678asignar la dirección física al espacio del kernel y luego usamos readlla función para leer el valor del registro de hardware del área asignada. iounmapUtilice funciones para desasignar cuando el módulo salga .

Cabe señalar que ioremap y las funciones relacionadas se usan generalmente para programación de hardware de bajo nivel y deben usarse con precaución para garantizar que se siguen las especificaciones y requisitos del dispositivo de hardware al acceder al área mapeada.

MODULE_LICENSE(“GPL”);

MODULE_LICENSE(“GPL”);
MODULE_AUTHOR(“Su nombre”);
MODULE_DESCRIPTION(“Módulo de reasignación de E/S de muestra”);

MODULE_LICENSE("GPL");:
Esta macro se utiliza para especificar el tipo de licencia del módulo del kernel. En el desarrollo del kernel de Linux, es importante seguir diferentes tipos de licencias. "GPL" significa Licencia Pública General GNU, que es una licencia de software de código abierto que requiere que el código derivado también cumpla con la GPL. Si el módulo utiliza una licencia diferente, puede especificar otros tipos de licencia aquí, como "LGPL", "MIT", etc.

MODULE_AUTHOR("Your Name");:
Esta macro se utiliza para especificar la información del autor del módulo del kernel. Debes reemplazar "Tu nombre" con tu nombre real o logotipo.

MODULE_DESCRIPTION("Sample I/O Remapping Module");:
Esta macro se utiliza para proporcionar una breve descripción del módulo del kernel. Aquí puede describir la función, propósito o características del módulo. Generalmente se muestra una descripción del módulo cuando se carga el módulo.

Estas macros proporcionan una forma estandarizada de registrar y mostrar información sobre los módulos del kernel, lo que ayuda a los desarrolladores a comprender las características y los antecedentes del módulo. Cuando otros desarrolladores o mantenedores del kernel miran su código, pueden comprender fácilmente la información básica sobre el módulo.

Finalizar

Si tiene alguna pregunta, puede hacerla y podremos avanzar juntos.

Supongo que te gusta

Origin blog.csdn.net/qq_52749711/article/details/132431438
Recomendado
Clasificación