为树莓派4B编写platform驱动LED灯(采用DTS方式提供硬件信息)

您所在的位置:网站首页 dts文件编辑 为树莓派4B编写platform驱动LED灯(采用DTS方式提供硬件信息)

为树莓派4B编写platform驱动LED灯(采用DTS方式提供硬件信息)

2023-07-29 06:04| 来源: 网络整理| 查看: 265

最近学习了Linux platform driver子系统和dts的基本知识,写一个platform字符设备驱动来操纵GPIO控制LED灯,采用DTB文件来提供硬件信息,纯粹练手。

修改DTS文件

要修改DTS文件首先要确定自己使用的开发板所用DTB的版本,RPI 4B采用的是bcm2711-rpi-4-b.dts。下载一个在arch/arm/boot/dts/目录下有这个dts文件的linux内核源码,我下的是linux-5.10.31。通过查阅bcm2711的datasheet,找到和GPIO相关的物理地址,RPI4B的GPIO物理地址偏移在0xfe200000。本实现希望的是通过读写GPIO14来控制外接的LED灯,所以需要使用的寄存器有:

0xfe200004 --- GPFSEL1 0xfe20001c --- GPSET0 0xfe200028 --- GPCLR0

于是在dts文件的根节点下添加节点:

dts_led { #address-cells = ; #size-cells = ; compatible = "dts_led"; status = "okay"; reg = < 0xfe200000 0x04 /*BCM_GPIO_BASE*/ 0xfe200004 0x04 /*GPFSEL1*/ 0xfe20001c 0x04 /*GPSET0*/ 0xfe200028 0x04 >; /*GPCLR0*/ };

编译需要自行配置交叉编译链,将编译得到的新的dtb替换原先的dtb。 若一切顺利,进入RPI 4B后在/proc/device-tree目录下可以看到dts_led节点。

编写驱动

驱动方面采用platform driver字符设备驱动,通过dtb文件的dts_led节点来获取硬件信息。 下面直接上代码

/* 面向RPI的LED驱动,对GPIO Pin 14的读写 platform driver版本 - dts */ #include #include #include #include #include /*copy from ... copy to...*/ #include /*device_create class_create*/ #include /*kzalloc kfree*/ #include /*readl writel ioremap*/ #include /*platform_driver_register*/ #include /*struct device_node*/ #include #define DTS_LED_MAJOR 233 #define GPFSEL1 (0x4/4) #define GPSET0 (0x1c/4) #define GPCLR0 (0x28/4) static struct class *dts_led_class; static struct device *dts_led_device; static struct dts_led_dev *dts_led_devp; static dev_t devno; struct dts_led_dev { unsigned int __iomem *bcm_gpio_base; unsigned int *gpfsel1; unsigned int *gpset0; unsigned int *gpclr0; struct device_node *nd; /*存放设备节点信息*/ struct cdev cdev; }; /*修改GPFSEL1,将GPIO Pin14设置为output功能*/ static int dts_led_open(struct inode *inode, struct file *filp){ unsigned int sel1_value = 0; sel1_value = readl(dts_led_devp->gpfsel1); /*将sel1寄存器的12-14位修改为001*/ sel1_value |= (1 unsigned char databuf[1]; if(copy_from_user(databuf, buf, size)){ // 读取用户空间写入的数据 printk(KERN_NOTICE "Failed to write platform_led!\r\n"); return -EFAULT; } unsigned char ledstat = databuf[0]; unsigned int set0_value = readl(dts_led_devp->gpset0); unsigned int clr0_value = readl(dts_led_devp->gpclr0); if(ledstat == 1){ /*打开LED*/ set0_value |= (1 .owner = THIS_MODULE, .open = dts_led_open, .read = dts_led_read, .write = dts_led_write, }; static int dts_led_probe(struct platform_device *pdev){ printk(KERN_NOTICE "%s", "dts_led load!\r\n"); int dts_led_major = DTS_LED_MAJOR; devno = MKDEV(dts_led_major, 0); int ret = 0; if(dts_led_major){ ret = register_chrdev_region(devno, 1, "dts_led"); // 若platform_led的设备号已写死,向 内核申请devno设备号,名为platform_led } else{ ret = alloc_chrdev_region(&devno, 0, 1, "dts_led"); // 若reg_led的设备号需要内核分配 dts_led_major = MAJOR(devno); } if(ret printk(KERN_NOTICE "Failed to assign mem for dts_led!\r\n"); ret = -ENOMEM; goto fail; } /*从dtb文件读取硬件信息*/ /*1. 获取设备节点*/ dts_led_devp->nd = of_find_node_by_path("/dts_led"); if(dts_led_devp->nd == NULL){ printk(KERN_NOTICE "dts_led node can not found!\r\n"); return -EINVAL; } else{ printk(KERN_NOTICE "dts_led node found successfully!\r\n"); } u32 regdata[8]; /*2.获取reg属性内容*/ ret = of_property_read_u32_array(dts_led_devp->nd, "reg", regdata, 8); if(ret printk(KERN_NOTICE "Failed to register the cdev to the kernel!\r\n"); goto fail; } /*现在完成了设备的注册,下面进行class和device的创建*/ dts_led_class = class_create(THIS_MODULE, "dts_led"); dts_led_device = device_create(dts_led_class, NULL, devno, NULL, "dts_led0"); return 0; fail: unregister_chrdev_region(devno, 1); kfree(dts_led_devp); return ret; } static int dts_led_remove(struct platform_device *dev){ device_destroy(dts_led_class, devno); class_unregister(dts_led_class); class_destroy(dts_led_class); cdev_del(&dts_led_devp->cdev); unregister_chrdev_region(devno, 1); kfree(dts_led_devp); printk(KERN_NOTICE "dts_led exit!\r\n"); return 0; } static const struct of_device_id dts_led_of_match[] = { {.compatible = "dts_led"}, }; static struct platform_driver platform_led_driver = { .driver = { .name = "dts_led", .of_match_table = dts_led_of_match, }, .probe = dts_led_probe, .remove = dts_led_remove, }; static int __init dts_led_driver_init(void){ return platform_driver_register(&platform_led_driver); } static void __exit dts_led_driver_exit(void){ platform_driver_unregister(&platform_led_driver); } module_init(dts_led_driver_init); module_exit(dts_led_driver_exit); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Alex Tang"); 测试程序

打开dts_led0设备文件进行读写,写入1将打开LED,写入0将关闭LED

#include #include #include #include #include #include int main(int argc, char* argv[]){ if(argc fprintf(stderr, "write dts_led Failed!\n"); exit(EXIT_FAILURE); } return 0; }


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3