Notas de estudio de desarrollo de controladores para Linux [2]: Cree automáticamente controladores LED para nodos de dispositivos

Tabla de contenido

1. Registro y cancelación del número de dispositivo

2. Agregar y eliminar dispositivos de personajes

Tres, crea automáticamente nodos de dispositivos

Cuatro, escritura y prueba del programa


 

1. Registro y cancelación del número de dispositivo

1. Al usar las siguientes funciones para registrar y anular el registro de dispositivos de caracteres, se desperdician muchos números de dispositivos secundarios y el número de dispositivo principal debe especificarse manualmente; y cuando usamos modprobe para cargar el controlador, también necesitamos usar el comando mknod para crear manualmente el dispositivo en el directorio / dev nodo

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

2. Si no se especifica el número de dispositivo, use la siguiente función para solicitar el número de dispositivo

int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name)

Si se proporcionan el número de dispositivo principal y el número de dispositivo menor del dispositivo, utilice la función que se muestra a continuación para registrar el número de dispositivo.

int register_chrdev_region(dev_t from, unsigned count, const char *name)

Después de cancelar el registro de un dispositivo de caracteres, se debe liberar el número de dispositivo. Ya sea el número de dispositivo solicitado a través de la función alloc_chrdev_region o la función register_chrdev_region, las siguientes funciones de liberación se utilizan de manera uniforme:

void unregister_chrdev_region(dev_t from, unsigned count)

2. Agregar y eliminar dispositivos de personajes

En linux, use la estructura include / linux / cdev.h / cdev para representar dispositivos de caracteres

(1) Inicializar el dispositivo de caracteres

cdev_init(struct cdev *, const struct file_operations *);

(2) Agregue un dispositivo de caracteres al kernel de Linux

int cdev_add(struct cdev *, dev_t, unsigned);

(3) Eliminar un dispositivo de carácter

void cdev_del(struct cdev *);

Tres, crea automáticamente nodos de dispositivos

Desde entonces, después de agregar un dispositivo de caracteres al kernel a través de cdev_add , todavía es necesario agregar manualmente el nodo de dispositivo correspondiente en el directorio / dev a través de mknod . Esto no es realista en el desarrollo real del proyecto. Después de agregar el dispositivo de caracteres, debe ser automáticamente Cree el nodo de dispositivo correspondiente, aquí se utilizará el mecanismo udev .

udev es un programa de usuario. En Linux, udev se utiliza para crear y eliminar archivos de dispositivo. udev puede detectar el estado de los dispositivos de hardware en el sistema y puede crear o eliminar archivos de dispositivo de acuerdo con el estado de los dispositivos de hardware en el sistema. Por ejemplo, después de que el módulo del controlador se haya cargado correctamente con el comando modprobe, el archivo de nodo del dispositivo correspondiente se crea automáticamente en el directorio / dev y el archivo del nodo del dispositivo en el directorio / dev se elimina después de que se utiliza el comando rmmod para desinstalar el módulo del controlador. Al usar busybox para construir el sistema de archivos raíz, busybox creará una versión simplificada de udev, mdev, por lo que en Linux embebido usamos mdev para realizar la creación y eliminación automática de archivos de nodo de dispositivo, y los eventos de conexión en caliente en el sistema Linux también son controlados por mdev Management, la siguiente declaración en el archivo /etc/init.d/rcS

echo /sbin/mdev > /proc/sys/kernel/hotplug

Proceso de implementación:

1. Crea y destruye clases

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

2. Crea y elimina dispositivos

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

Cuatro, escritura y prueba del programa

El primer paso : escribir el controlador

#include <linux/types.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/errno.h> 
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <asm/mach/map.h>

#define NEWCHARLED_MAJOR 200
#define NEWCHARLED_NAME  "newcharled"

#define LEDOFF 0
#define LEDON  1

/*寄存器物理地址*/
#define CCM_CCGR1_BASE          (0X020C406C)
#define SW_MUX_GPIO1_IO03_BASE  (0X020E0068)
#define SW_PAD_GPIO1_IO03_BASE  (0X020E02F4)
#define GPIO1_DR_BASE           (0X0209C000)
#define GPIO1_GDIR_BASE         (0X0209C004)
/* 映射后的寄存器虚拟地址指针 */
static void __iomem *IMX6U_CCM_CCGR1;
static void __iomem *SW_MUX_GPIO1_IO03;
static void __iomem *SW_PAD_GPIO1_IO03;
static void __iomem *GPIO1_DR;
static void __iomem *GPIO1_GDIR;

struct newcharled_dev
{
    struct cdev cdev;   /*字符设备*/
    struct class *class;/*类*/
    struct device *device;/*设备*/

    dev_t devid;        /*设备号*/
    int major;          /*主设备号*/
    int minor;          /*次设备号*/
};

struct newcharled_dev   newcharled;

/*
 * @description		: LED打开/关闭
 * @param - sta 	: LEDON(0) 打开LED,LEDOFF(1) 关闭LED
 * @return 			: 无
 */
void newcharled_switch(u8 sta)
{
	u32 val = 0;
    
	if(sta == LEDON) 
    {
		val = readl(GPIO1_DR);
		val &= ~(1 << 3);	
		writel(val, GPIO1_DR);
	}
    else if(sta == LEDOFF) 
    {
		val = readl(GPIO1_DR);
		val|= (1 << 3);	
		writel(val, GPIO1_DR);
	}	
}

static int newcharled_open(struct inode *inode, struct file *file)
{
	u32 val = 0;

    file->private_data = &newcharled;

    /*初始化led,不能直接操作物理地址,需要地址重映射*/
    /*1、寄存器地址映射*/
    IMX6U_CCM_CCGR1   = ioremap(CCM_CCGR1_BASE, 4);
    SW_MUX_GPIO1_IO03 = ioremap(SW_MUX_GPIO1_IO03_BASE, 4);
    SW_PAD_GPIO1_IO03 = ioremap(SW_PAD_GPIO1_IO03_BASE, 4);
    GPIO1_DR          = ioremap(GPIO1_DR_BASE, 4);
    GPIO1_GDIR        = ioremap(GPIO1_GDIR_BASE, 4);

    	/* 2、使能GPIO1时钟 */
	val = readl(IMX6U_CCM_CCGR1);
	val &= ~(3 << 26);	/* 清楚以前的设置 */
	val |= (3 << 26);	/* 设置新值 */
	writel(val, IMX6U_CCM_CCGR1);

	/* 3、设置GPIO1_IO03的复用功能,将其复用为
	 *    GPIO1_IO03,最后设置IO属性。
	 */
	writel(5, SW_MUX_GPIO1_IO03);
	
	/*寄存器SW_PAD_GPIO1_IO03设置IO属性
	 *bit 16:0 HYS关闭
	 *bit [15:14]: 00 默认下拉
     *bit [13]: 0 kepper功能
     *bit [12]: 1 pull/keeper使能
     *bit [11]: 0 关闭开路输出
     *bit [7:6]: 10 速度100Mhz
     *bit [5:3]: 110 R0/6驱动能力
     *bit [0]: 0 低转换率
	 */
	writel(0x10B0, SW_PAD_GPIO1_IO03);

	/* 4、设置GPIO1_IO03为输出功能 */
	val = readl(GPIO1_GDIR);
	val &= ~(1 << 3);	/* 清除以前的设置 */
	val |= (1 << 3);	/* 设置为输出 */
	writel(val, GPIO1_GDIR);

	/* 5、默认关闭LED */
	val = readl(GPIO1_DR);
	val |= (1 << 3);	
	writel(val, GPIO1_DR);

    return 0;
}

static int newcharled_release(struct inode *inode, struct file *file)
{
    /* 取消映射 */
	iounmap(IMX6U_CCM_CCGR1);
	iounmap(SW_MUX_GPIO1_IO03);
	iounmap(SW_PAD_GPIO1_IO03);
	iounmap(GPIO1_DR);
	iounmap(GPIO1_GDIR);

    return 0;
}

static ssize_t newcharled_read(struct file *file, char __user *buf, 
        size_t count, loff_t *offset)
{
    int ret = 0;

    return ret;
}

static ssize_t newcharled_write(struct file *file, const char __user *buf,
		size_t count, loff_t *offset)
{
    int ret = 0;
    u8 data[1], ledsta;

    ret = copy_from_user(data, buf, count);
    if (ret < 0){
        printk("kernel write failed!\r\n");
        return -EFAULT;
    }

    ledsta = data[0];
    if (ledsta == LEDOFF || ledsta == LEDON)
        newcharled_switch(ledsta);
    else
        printk("led para error\r\n");
    
    return ret;
}

static const struct file_operations newcharled_fops = 
{
    .owner   = THIS_MODULE,
    .open    = newcharled_open,
    .release = newcharled_release,
    .read    = newcharled_read,
    .write   = newcharled_write,
};

static int __init newchar_led_init(void)
{
    int ret = 0;

    /*1、创建设备号*/
    if (newcharled.major){ /*指定了设备号*/
        newcharled.devid = MKDEV(newcharled.major, 0);
        ret = register_chrdev_region(newcharled.devid, 1, NEWCHARLED_NAME);
    }
    else{ /*没有指定设备号*/
        ret = alloc_chrdev_region(&newcharled.devid, 0, 1, NEWCHARLED_NAME);
        newcharled.major = MAJOR(newcharled.devid);
        newcharled.minor = MINOR(newcharled.devid);
    }

    if (ret < 0){
        printk("newcharled chrdev_region failed\r\n");
        return -1;
    }

    printk("newcharled major = %d, minor = %d\r\n", newcharled.major, newcharled.minor);
    
    /*2、初始化字符设备cdev*/
    newcharled.cdev.owner = THIS_MODULE;
    cdev_init(&newcharled.cdev, &newcharled_fops);

    /*3、向linux内核添加一个字符设备*/
    cdev_add(&newcharled.cdev, newcharled.devid, 1);

    /*4、自动创建设备节点 -- 创建类*/
    newcharled.class = class_create (THIS_MODULE, NEWCHARLED_NAME);
    if (IS_ERR(newcharled.class)){
        return PTR_ERR(newcharled.class);
    }
    /*-- 创建设备*/
    newcharled.device = device_create(newcharled.class, 
                                      NULL, 
                                      newcharled.devid, 
                                      NULL, 
                                      NEWCHARLED_NAME);
    if (IS_ERR(newcharled.device)){
        return PTR_ERR(newcharled.device);
    }

    return 0;
}

static void __exit newchar_led_exit(void)
{
    /*删除字符设备*/
    cdev_del(&newcharled.cdev);

    /*注销设备号*/
    unregister_chrdev_region(newcharled.devid, 1);

    /*注销设备*/
    device_destroy(newcharled.class, newcharled.devid);
    /*注销类*/
    class_destroy(newcharled.class);

    printk("newcharled_exit\r\n");
}

/*注册驱动和卸载驱动*/
module_init(newchar_led_init);
module_exit(newchar_led_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("denghengli");

Paso 2 : compile el controlador, copie el módulo del controlador en el sistema de archivos raíz

make //编译成newcharled.ko
sudo cp gpioled.ko /home/denghengli/linux/nfs/rootfs/lib/modules/4.1.15/

El tercer paso : iniciar Linux, cargar el módulo del controlador

 

 

 

Supongo que te gusta

Origin blog.csdn.net/m0_37845735/article/details/106795666
Recomendado
Clasificación