当前位置:首页>Linux>Linux设备驱动 -- LED字符设备驱动(设备树)

Linux设备驱动 -- LED字符设备驱动(设备树)

  • 2026-09-10 02:44:10
Linux设备驱动 -- LED字符设备驱动(设备树)

LED字符设备驱动(设备树)实验


1、设备树的改动

1.1  修改设备增加led节点

内核源码设备树文件位置:ebf-buster-linux/arch/arm/boot/dts/imx6ull-seeed-npi.dts

添加如下节点:

 red_led {                       
#address-cells = <1>; 
#size-cells = <1>;
        compatible = "red_led";  
        status = "okay";
        reg = < 0x20C406C0x04/* CCM_CCGR1 */
0x20E006C0x04/* MUX_PAD_GPIO1_IO04 */
0x209C0000x04/* GPIO1_DR */
0x209C0040x04  >;  /* GPIO1_GDIR */
  };
  • reg  -  操作led相关的寄存器

编译生成的二进制设备树和设备树文件在同一目录,路径:arch/arm/boot/dts/imx6ull-seeed-npi.dtb

dts文件和dtb文件关系类似C语言文件和生成的二进制可执行程序文件

1.2 编译设备树

make_dtb.sh:

#!/bin/sh

make ARCH=arm -j4 CROSS_COMPILE=arm-linux-gnueabihf- dtbs

1.3 更新开发板Linux系统设备树

1.3.1 将设备文件拷贝到根文件系统

copy_dtb_to_nfs_fs.sh:

#!/bin/sh

cp arch/arm/boot/dts/imx6ull-seeed-npi.dtb /home/ares/nfs_rootfs/

1.3.2 拷贝设备到开发板Linux系统设备树文件目录

copy_dtb.sh:

#!/bin/sh

cp /debian/nfs_rootfs/imx6ull-seeed-npi.dtb /boot/dtbs/4.19.71-imx-r1/

1.3.3 重启系统以生效设备树文件

sudo reboot

2、编写驱动代码

2.1 编写字符设备核心

即使使用设备树,字符设备最核心的还是open、write、close等函数。没有这些函数函数设备树也是废物一个。

#define RED_LED_DTS_NODE   "/red_led"//red_led设备节点的位置    //red_led表示根节点下的red_led节点

structred_led_dev {
structcdevchrdev;//字符设备结构
dev_t dev_no;                     //设备号
structclass *led_class;
structdevice_node *dev_node;
unsignedint *virtual_ccgr1;
unsignedint *virtual_gpio1_io4;
unsignedint *virtual_dr;
unsignedint *virtual_gdir;
 };

staticstructred_led_devled_dev;

staticintred_led_drv_open(struct inode *node, struct file *file)
{
int val = 0;

    printk(KERN_INFO"open red led dev\n");
    val = ioread32(led_dev.virtual_ccgr1);
    val &= ~(3 << 26);   
    val |= (3 << 26);   
    iowrite32(val, led_dev.virtual_ccgr1);    

    val = 0;
    val = ioread32(led_dev.virtual_gpio1_io4);     
    val &= ~(0xf << 0);    
    val |= (0x5 << 0);  
    iowrite32(val, led_dev.virtual_gpio1_io4);   

    val = 0;
    val = ioread32(led_dev.virtual_gdir);     
    val |= (0x1 << 4); 
    iowrite32(val, led_dev.virtual_gdir);   
return0;
}

staticssize_tred_led_drv_write(struct file *file, constchar __user *buf, size_t size, loff_t *offset)
{
int err;
char status;
int val = 0;

    printk("write %s %s line %d\r\n", __FILE__, __FUNCTION__, __LINE__);

    err = copy_from_user(&status, buf, 1);

if (status)      
    { 
        val &= ~(1 << 4);
        iowrite32(val, led_dev.virtual_dr);      
    }
else
    {  
        val |= (1 << 4);                 
        iowrite32(val, led_dev.virtual_dr);       
    }

return1;
}

//这个结构体至始至终都是字符设备的核心,说有设备树不用学这个就是骗鬼
staticstructfile_operationsred_led_drv = {
    .owner     = THIS_MODULE,
    .open    = red_led_drv_open,
    .write   = red_led_drv_write,
};

2.2 定义设备树匹配列表

//设备树匹配列表 
staticstructof_device_iddts_match_table[] = {
    {.compatible = "red_led", },   //根据compatible兼容属性进行匹配                   
};

设备节点和驱动是否能匹配成功就看设备树列表。可以看出设备匹配列表可以有多个,这表示驱动是可以匹配多个设备的。

2.3 定义填充驱动结构体

staticintled_red_driver_probe(struct platform_device *dev)
{
int err;
int ret;
    u32 regdata[8];
int i;
structdevice *tmpdev;

    led_dev.dev_node = of_find_node_by_path(RED_LED_DTS_NODE);         //从设备树中查找led字符设备的设备节点
if (!led_dev.dev_node) {          
        printk("red led can not found!\r\n"); 
return -EINVAL; 
    }

    ret = of_property_read_u32_array(led_dev.dev_node,       
"reg",                           //从找到的设备节点中读取寄存器属性的值
                          regdata, 8);
if (ret < 0) {
        printk(KERN_INFO"Failed to get red led reg property\n");
    } else {
for (i = 0; i < 8; i++) {
            printk("reg%d : %x\n", i, regdata[i]);
        }
    }
#if 1
    led_dev.virtual_ccgr1 = ioremap(regdata[0], regdata[1]);   //映射寄存器物理地址为虚拟地址
    led_dev.virtual_gpio1_io4 = ioremap(regdata[2], regdata[3]); 
    led_dev.virtual_dr = ioremap(regdata[4], regdata[5]); 
    led_dev.virtual_gdir = ioremap(regdata[6], regdata[7]); 
#else
    led_dev.virtual_ccgr1 = of_iomap(led_dev.dev_node, 0); 
    led_dev.virtual_gpio1_io4 = of_iomap(led_dev.dev_node, 1); 
    led_dev.virtual_dr = of_iomap(led_dev.dev_node, 2); 
    led_dev.gdir = of_iomap(led_dev.dev_node, 3); 
#endif

    ret = alloc_chrdev_region(&led_dev.dev_no, 0, 1, "red_led"); //申请设备号
if (ret < 0) {
        pr_err("Error: failed to register mbochs_dev, err: %d\n", ret);
return ret;
    }

    cdev_init(&led_dev.chrdev, &red_led_drv);       //初始化字符设备

    cdev_add(&led_dev.chrdev, led_dev.dev_no, 1);   //添加字符设备

    led_dev.led_class = class_create(THIS_MODULE, "red_led_class");
    err = PTR_ERR(led_dev.led_class);
if (IS_ERR(led_dev.led_class)) {
goto failed1;
    }

//创建设备节点
    tmpdev = device_create(led_dev.led_class , NULL, led_dev.dev_no, NULL, "red_led"); 
if (IS_ERR(tmpdev)) {
        ret = -EINVAL;
goto failed2;
    }

       printk(KERN_INFO"%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);

return0;
failed2:
    device_destroy(led_dev.led_class, led_dev.dev_no);
    class_destroy(led_dev.led_class);
failed1:
    cdev_del(&led_dev.chrdev);
    unregister_chrdev_region(led_dev.dev_no, 1);
return ret;
}

intled_red_driver_remove(struct platform_device *dev)
{
    iounmap(led_dev.virtual_ccgr1);    
    iounmap(led_dev.virtual_gpio1_io4);
    iounmap(led_dev.virtual_dr);
    iounmap(led_dev.virtual_gdir);

    printk(KERN_INFO"driver remove %s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
    device_destroy(led_dev.led_class, led_dev.dev_no);
    class_destroy(led_dev.led_class);
    unregister_chrdev_region(led_dev.dev_no, 1);
    cdev_del(&led_dev.chrdev);

return0;
}

staticstructplatform_driverred_led_platform_driver = {
      .probe = led_red_driver_probe,
      .remove = led_red_driver_remove,
      .driver = {
        .name = "fire-rgb-led",
        .owner = THIS_MODULE,
        .of_match_table = dts_match_table,         //加设备树列表加进驱动中
      },
};
  • of_find_node_by_path(RED_LED_DTS_NODE)  - 找到要的设备节点
  • iounmap(led_dev.virtual_ccgr1) -  解除寄存器物理地址映射
  • dts_match_table - 设备树匹配列表

2.4 设备树出口/入口函数

staticint __init red_led_driver_init(void)
{
    printk(" %s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
int ret = platform_driver_register(&red_led_platform_driver);   
return ret;
}

staticvoid __exit red_led_driver_exit(void)
{
    printk(" %s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
    platform_driver_unregister(&red_led_platform_driver);
}

module_init(red_led_driver_init);
module_exit(red_led_driver_exit);

MODULE_AUTHOR("Ares");
MODULE_LICENSE("GPL");
  • platform_driver_register(&red_led_platform_driver)
      -  在入口函数中注册驱动
  • platform_driver_unregister(&red_led_platform_driver)
      -  在出口函数中卸载驱动

最新文章

随机文章