700字范文,内容丰富有趣,生活中的好帮手!
700字范文 > Linux驱动_设备树下LED驱动

Linux驱动_设备树下LED驱动

时间:2024-01-30 00:31:30

相关推荐

Linux驱动_设备树下LED驱动

前言

学习完设备树基础知识后,完成设备树下LED驱动实验

一、修改设备树文件

在设备书根/节点下添加子节点led信息:

alphaled {status = "okay";compatible = "alientek,alphaled";reg = < 0X020C406C 0X04/* CCM_CCGR1_BAE */0X020E0068 0X04/* SW_MUX_GPIO1_IO03_BASE */0X020E02F4 0X04/* SW_PAD_GPIO1_IO03_BASE*/0X0209C000 0X04/* GPIO1_DR_BASE */0X0209C004 0X04>;/* GPIO1_GDIR_BASE */};

其中reg属性中保存了led驱动所需要的寄存器地址。修改完成后采用make dtbs命令重新编译设备树,并采用新编译的设备书启动内核。会发现多了alphaled这个子节点。

二、LED驱动编写

#include <linux/kernel.h>#include <linux/module.h>#include <linux/init.h>#include <linux/fs.h>#include <linux/uaccess.h>#include <linux/io.h>#include <linux/cdev.h>#include <linux/device.h>#include <linux/of.h>#include <linux/of_address.h>#include <linux/of_irq.h>#include <linux/slab.h>#define DTSLED_COUNT 1 /*设备号个数*/#define DTSLED_NAME "dtsled" /*设备号名字*/#define LEDON 1#define LEDOFF 0/* 映射后的寄存器虚拟地址指针 */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;void led_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);}}struct dtsled_dev{dev_t devid; /*设备号*/struct cdev cdev; /*字符设备*/struct class *class; /*定义类*/struct device *device; /*定义设备*/struct device_node *node; /*添加jiedian*/int major; /*主设备号*/int minor; /*次设备号*/};struct dtsled_dev dtsled; /*定义led结构体*/static int dtsled_open(struct inode *inode, struct file *filp){filp->private_data = &dtsled; /* 设置私有数据 */return 0;}static ssize_t dtsled_write(struct file *filp, const char __user *buf,size_t cnt, loff_t *offt){struct dtsled_dev *dev = filp->private_data;int retvalue;unsigned char databuf[1];unsigned char ledstat;retvalue = copy_from_user(databuf, buf, cnt);if(retvalue < 0) {printk("kernel write failed!\r\n");return -EFAULT;}ledstat = databuf[0]; /* 获取状态值 */if(ledstat == LEDON) {led_switch(LEDON); /* 打开 LED 灯 */} else if(ledstat == LEDOFF) {led_switch(LEDOFF); /* 关闭 LED 灯 */}return 0;}static int dtsled_release(struct inode *inode, struct file *filp){struct dtsled_dev *dev = filp->private_data;return 0;}static const struct file_operations dtsled_fops = { /*字符设备操作函数集合*/.owner = THIS_MODULE,.write = dtsled_write,.open = dtsled_open,.release = dtsled_release,};static int __init dtsled_init(void){int ret = 0;unsigned int val;const char *str;struct property *proper;u32 regdata[10];printk("dtsled_init \r\n");/*注册字符设备*/dtsled.major = 0; /*设置设备号有内核分配*/if(dtsled.major){ /*如果定义了设备号*/dtsled.devid = MKDEV(dtsled.major, 0);ret = register_chrdev_region(dtsled.devid, DTSLED_COUNT, DTSLED_NAME);}else{ /*没有给定设备号*/ret = alloc_chrdev_region(&dtsled.devid, 0, DTSLED_COUNT, DTSLED_NAME);dtsled.major = MAJOR(dtsled.devid);dtsled.minor = MINOR(dtsled.devid);}if(ret < 0){goto failed_devid;}/*添加字符设备*/dtsled.cdev.owner = THIS_MODULE;cdev_init(&dtsled.cdev, &dtsled_fops);ret = cdev_add(&dtsled.cdev, dtsled.devid, DTSLED_COUNT);if(ret < 0){ /*如果添加字符设备失败*/goto failed_cdev;}/*自动创建设备节点*/dtsled.class = class_create(THIS_MODULE, DTSLED_NAME); /*class_creat(owner,name);*/if(IS_ERR(dtsled.class)){ /*判断是否创建类成功*/ret = PTR_ERR(dtsled.class);goto failed_class;}dtsled.device = device_create(dtsled.class, NULL, dtsled.devid, NULL, DTSLED_NAME);if(IS_ERR(dtsled.device)){ /*判断是否创建类成功*/ret = PTR_ERR(dtsled.device);goto failed_device;}/*获取设备树属性信息*//*找到设备树节点*/dtsled.node = of_find_node_by_path("/alphaled");if(dtsled.node == NULL){ /*寻找节点失败*/ret = -EINVAL;goto failed_findnode;}/*获取 status 属性内容 */ret = of_property_read_string(dtsled.node, "status", &str);if(ret < 0){goto failed_status;} else {printk("status = %s\r\n",str);}/*获取 compatible 属性内容 */proper = of_find_property(dtsled.node, "compatible", NULL);if(proper == NULL) {goto failed_proper;} else {printk("compatible = %s\r\n", (char*)proper->value);}/*获取 reg 属性内容 */ret = of_property_read_u32_array(dtsled.node, "reg", regdata, 10);if(ret < 0) {goto failed_reg;} else {u8 i = 0;printk("reg data:\r\n");for(i = 0; i < 10; i++)printk("%#X ", regdata[i]);printk("\r\n");}/*led初始化*/IMX6U_CCM_CCGR1 = of_iomap(dtsled.node, 0);SW_MUX_GPIO1_IO03 = of_iomap(dtsled.node, 1);SW_PAD_GPIO1_IO03 = of_iomap(dtsled.node, 2);GPIO1_DR = of_iomap(dtsled.node, 3);GPIO1_GDIR = of_iomap(dtsled.node, 4);/* 2、使能 GPIO1 时钟 */val = readl(IMX6U_CCM_CCGR1);val &= ~(3 << 26); /* 清楚以前的设置 */val |= (3 << 26); /* 设置新值 */writel(val, IMX6U_CCM_CCGR1);/* 设置 GPIO1_IO03 的复用功能,将其复用为* GPIO1_IO03,最后设置 IO 属性。*/writel(5, SW_MUX_GPIO1_IO03);/* 寄存器 SW_PAD_GPIO1_IO03 设置 IO 属性 */writel(0x10B0, SW_PAD_GPIO1_IO03);/* 设置 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;failed_reg:failed_proper:failed_status:failed_findnode:device_destroy(dtsled.class, dtsled.devid);failed_device:class_destroy(dtsled.class);failed_class:cdev_del(&dtsled.cdev);failed_cdev:unregister_chrdev_region(dtsled.devid, DTSLED_COUNT);failed_devid:return ret;}static void __exit dtsled_exit(void){/* 取消映射 */iounmap(IMX6U_CCM_CCGR1);iounmap(SW_MUX_GPIO1_IO03);iounmap(SW_PAD_GPIO1_IO03);iounmap(GPIO1_DR);iounmap(GPIO1_GDIR);/*删除字符设备*/cdev_del(&dtsled.cdev);/*卸载设备*/unregister_chrdev_region(dtsled.devid, DTSLED_COUNT);/*摧毁设备*/device_destroy(dtsled.class, dtsled.devid);/*摧毁类*/class_destroy(dtsled.class);}/*模块入口和出口*/module_init(dtsled_init);module_exit(dtsled_exit);MODULE_LICENSE("GPL");MODULE_AUTHOR("ZYC");

led驱动代码和唯一的区别就是参杂了设备树,从设备树上读取并完成寄存器地址映射。使用 of_iomap 函数一次性完成读取 reg 属性以及内存映射, of_iomap 函数是设备树推荐使用的 OF 函数。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。