设备树下 的LINUX驱动实验

本文介绍了如何在IMX6ULL平台的设备树文件imx6ull-alientek-emmc.dts中添加LED设备节点,以及如何编写并编译Linux内核驱动程序,实现GPIO控制LED灯的开关。涉及内容包括设备树节点的创建、属性获取和初始化,以及字符设备驱动的编写和加载测试。
摘要由CSDN通过智能技术生成

概要

①、在 imx6ull-alientek-emmc.dts 文件中创建相应的设备节点。
②、编写驱动程序 ( 在第四十二章实验基础上完成 ) ,获取设备树中的相关属性值。
③、使用获取到的有关属性值来初始化 LED 所使用的 GPIO。


修改设备树文件

打开 imx6ull-alientek-emmc.dts 文件, 在根节点“/ ”最后面输入如下所示内容:
1 alphaled {
2     #address-cells = <1>;
3     #size-cells = <1>;
4     compatible = "atkalpha-led";
5     status = "okay";
6     reg = < 0X020C406C 0X04 /* CCM_CCGR1_BASE */
7             0X020E0068 0X04 /* SW_MUX_GPIO1_IO03_BASE */
8             0X020E02F4 0X04 /* SW_PAD_GPIO1_IO03_BASE */
9             0X0209C000 0X04 /* GPIO1_DR_BASE */
10             0X0209C004 0X04 >; /* GPIO1_GDIR_BASE */
11 };

修改完之后编译设备树木

make dtbs
编译完成以后得到 imx6ull-alientek-emmc.dtb ,使用新的 imx6ull-alientek-emmc.dtb 启动
Linux 内核。 Linux 启动成功以后进入到 /proc/device-tree/ 目录中查看是否有“ alphaled ”这个节
点,结果如图 所示:
可以进入到图 44.3.1 中的 alphaled 目录中,查看一下都有哪些属性文件,结果如图:

LED 灯驱动程序编写

1 #include <linux/types.h>
2 #include <linux/kernel.h>
3 #include <linux/delay.h>
4 #include <linux/ide.h>
5 #include <linux/init.h>
6 #include <linux/module.h>
7 #include <linux/errno.h>
8 #include <linux/gpio.h>
9 #include <linux/cdev.h>
10 #include <linux/device.h>
11 #include <linux/of.h>
12 #include <linux/of_address.h>
13 #include <asm/mach/map.h>
14 #include <asm/uaccess.h>
15 #include <asm/io.h>

26 #define DTSLED_CNT 1 /* 设备号个数 */
27 #define DTSLED_NAME "dtsled" /* 名字 */
28 #define LEDOFF 0 /* 关灯 */
29 #define LEDON 1 /* 开灯 */
30 
31 /* 映射后的寄存器虚拟地址指针 */
32 static void __iomem *IMX6U_CCM_CCGR1;
33 static void __iomem *SW_MUX_GPIO1_IO03;
34 static void __iomem *SW_PAD_GPIO1_IO03;
35 static void __iomem *GPIO1_DR;
36 static void __iomem *GPIO1_GDIR;
37 
38 /* dtsled 设备结构体 */
39 struct dtsled_dev{
40     dev_t devid; /* 设备号 */
41     struct cdev cdev; /* cdev */
42     struct class *class; /* 类 */
43     struct device *device; /* 设备 */
44     int major; /* 主设备号 */
45     int minor; /* 次设备号 */
46     struct device_node *nd; /* 设备节点 */
47 };
48 
49 struct dtsled_dev dtsled; /* led 设备 */
50 
51 /*
52 * @description : LED 打开/关闭
53 * @param - sta : LEDON(0) 打开 LED,LEDOFF(1) 关闭 LED
54 * @return : 无
55 */
56 void led_switch(u8 sta)
57 {
58     u32 val = 0;
59     if(sta == LEDON) {
60         val = readl(GPIO1_DR);
61         val &= ~(1 << 3); 
62         writel(val, GPIO1_DR);
63     }else if(sta == LEDOFF) {
64         val = readl(GPIO1_DR);
65         val|= (1 << 3);
66         writel(val, GPIO1_DR);
67     } 
68 }
69 
70 /*
71 * @description : 打开设备
72 * @param – inode : 传递给驱动的 inode
73 * @param – filp : 设备文件,file 结构体有个叫做 private_data 的成员变量
74 * 一般在 open 的时候将 private_data 指向设备结构体。
75 * @return : 0 成功;其他 失败
76 */
77 static int led_open(struct inode *inode, struct file *filp)
78 {
79     filp->private_data = &dtsled; /* 设置私有数据 */
80     return 0;
81 }
82 
83 /*
84 * @description : 从设备读取数据
85 * @param – filp : 要打开的设备文件(文件描述符)
86 * @param - buf : 返回给用户空间的数据缓冲区
87 * @param - cnt : 要读取的数据长度
88 * @param – offt : 相对于文件首地址的偏移
89 * @return : 读取的字节数,如果为负值,表示读取失败
90 */
91 static ssize_t led_read(struct file *filp, char __user *buf,
size_t cnt, loff_t *offt)
92 {
93     return 0;
94 }
95 
96 /*
97 * @description : 向设备写数据
98 * @param - filp : 设备文件,表示打开的文件描述符
99 * @param - buf : 要写给设备写入的数据
100 * @param - cnt : 要写入的数据长度
101 * @param – offt : 相对于文件首地址的偏移
102 * @return : 写入的字节数,如果为负值,表示写入失败
103 */
104 static ssize_t led_write(struct file *filp, const char __user *buf,
    size_t cnt, loff_t *offt)
105 {
106     int retvalue;
107     unsigned char databuf[1];
108     unsigned char ledstat;
109
110     retvalue = copy_from_user(databuf, buf, cnt);
111     if(retvalue < 0) {
112         printk("kernel write failed!\r\n");
113         return -EFAULT;
114     }
115
116     ledstat = databuf[0]; /* 获取状态值 */
117
118     if(ledstat == LEDON) { 
119         led_switch(LEDON); /* 打开 LED 灯 */
120     } else if(ledstat == LEDOFF) {
121         led_switch(LEDOFF); /* 关闭 LED 灯 */
122 }
123     return 0;
124 }
125
126 /*
127 * @description : 关闭/释放设备
128 * @param – filp : 要关闭的设备文件(文件描述符)
129 * @return : 0 成功;其他 失败
130 */
131 static int led_release(struct inode *inode, struct file *filp)
132 {
133     return 0;
134 }
135
136 /* 设备操作函数 */
137 static struct file_operations dtsled_fops = {
138     .owner = THIS_MODULE,
139     .open = led_open,
140     .read = led_read,
141     .write = led_write,
142     .release = led_release,
143 };
144
145 /*
146 * @description : 驱动入口函数
147 * @param : 无
148 * @return : 无
149 */
150 static int __init led_init(void)
151 {
152     u32 val = 0;
153     int ret;
154     u32 regdata[14];
155     const char *str;
156     struct property *proper;
157
158     /* 获取设备树中的属性数据 */
159     /* 1、获取设备节点:alphaled */
160     dtsled.nd = of_find_node_by_path("/alphaled");
161     if(dtsled.nd == NULL) {
162         printk("alphaled node can not found!\r\n");
163         return -EINVAL;
164     } else {
165         printk("alphaled node has been found!\r\n");
166     }
167
168     /* 2、获取 compatible 属性内容 */
169     proper = of_find_property(dtsled.nd, "compatible", NULL);
170     if(proper == NULL) {
171         printk("compatible property find failed\r\n");
172     } else {
173         printk("compatible = %s\r\n", (char*)proper->value);
174     }
175
176     /* 3、获取 status 属性内容 */
177     ret = of_property_read_string(dtsled.nd, "status", &str);
178     if(ret < 0){
179         printk("status read failed!\r\n");
180     } else {
181     printk("status = %s\r\n",str);
182 }
183
184     /* 4、获取 reg 属性内容 */
185     ret = of_property_read_u32_array(dtsled.nd, "reg", regdata, 10);
186     if(ret < 0) {
187         printk("reg property read failed!\r\n");
188     } else {
189         u8 i = 0;
190         printk("reg data:\r\n");
191         for(i = 0; i < 10; i++)
192             printk("%#X ", regdata[i]);
193         printk("\r\n");
194     }
195
196 /* 初始化 LED */
197 #if 0
198     /* 1、寄存器地址映射 */
199     IMX6U_CCM_CCGR1 = ioremap(regdata[0], regdata[1]);
200     SW_MUX_GPIO1_IO03 = ioremap(regdata[2], regdata[3]);
201     SW_PAD_GPIO1_IO03 = ioremap(regdata[4], regdata[5]);
202     GPIO1_DR = ioremap(regdata[6], regdata[7]);
203     GPIO1_GDIR = ioremap(regdata[8], regdata[9]);
204 #else
205     IMX6U_CCM_CCGR1 = of_iomap(dtsled.nd, 0);
206     SW_MUX_GPIO1_IO03 = of_iomap(dtsled.nd, 1);
207     SW_PAD_GPIO1_IO03 = of_iomap(dtsled.nd, 2);
208     GPIO1_DR = of_iomap(dtsled.nd, 3);
209     GPIO1_GDIR = of_iomap(dtsled.nd, 4);
210 #endif
211
212     /* 2、使能 GPIO1 时钟 */
213     val = readl(IMX6U_CCM_CCGR1);
214     val &= ~(3 << 26); /* 清楚以前的设置 */
215     val |= (3 << 26); /* 设置新值 */
216     writel(val, IMX6U_CCM_CCGR1);
217
218 /* 3、设置 GPIO1_IO03 的复用功能,将其复用为
219 *    GPIO1_IO03,最后设置 IO 属性。
220 */
221     writel(5, SW_MUX_GPIO1_IO03);
222 
223 /* 寄存器 SW_PAD_GPIO1_IO03 设置 IO 属性 */
224     writel(0x10B0, SW_PAD_GPIO1_IO03);
225
226 /* 4、设置 GPIO1_IO03 为输出功能 */
227     val = readl(GPIO1_GDIR);
228     val &= ~(1 << 3); /* 清除以前的设置 */
229     val |= (1 << 3); /* 设置为输出 */
230     writel(val, GPIO1_GDIR);
231
232 /* 5、默认关闭 LED */
233     val = readl(GPIO1_DR);
234     val |= (1 << 3); 
235     writel(val, GPIO1_DR);
236
237 /* 注册字符设备驱动 */
238 /* 1、创建设备号 */
239     if (dtsled.major) { /* 定义了设备号 */
240         dtsled.devid = MKDEV(dtsled.major, 0);
241         register_chrdev_region(dtsled.devid, DTSLED_CNT, DTSLED_NAME);
242     } else { /* 没有定义设备号 */
243         alloc_chrdev_region(&dtsled.devid, 0, DTSLED_CNT, DTSLED_NAME); /* 申请设备*/
244         dtsled.major = MAJOR(dtsled.devid); /* 获取分配号的主设备号 */
245         dtsled.minor = MINOR(dtsled.devid); /* 获取分配号的次设备号 */
246     }
247     printk("dtsled major=%d,minor=%d\r\n",dtsled.major,dtsled.minor); 
248 
249 /* 2、初始化 cdev */
250     dtsled.cdev.owner = THIS_MODULE;
251     cdev_init(&dtsled.cdev, &dtsled_fops);
252 
253 /* 3、添加一个 cdev */
254     cdev_add(&dtsled.cdev, dtsled.devid, DTSLED_CNT);
255
256 /* 4、创建类 */
257     dtsled.class = class_create(THIS_MODULE, DTSLED_NAME);
258     if (IS_ERR(dtsled.class)) {
259         return PTR_ERR(dtsled.class);
260     }
261
262 /* 5、创建设备 */
263     dtsled.device = device_create(dtsled.class, NULL, dtsled.devid, NULL,       DTSLED_NAME);
264     if (IS_ERR(dtsled.device)) {
265         return PTR_ERR(dtsled.device);
266     }
267 
268     return 0;
269 }
270
271 /*
272 * @description : 驱动出口函数
273 * @param : 无
274 * @return : 无
275 */
276 static void __exit led_exit(void)
277 {
278     /* 取消映射 */
279     iounmap(IMX6U_CCM_CCGR1);
280     iounmap(SW_MUX_GPIO1_IO03);
281     iounmap(SW_PAD_GPIO1_IO03);
282     iounmap(GPIO1_DR);
283     iounmap(GPIO1_GDIR);
284
285     /* 注销字符设备驱动 */
286     cdev_del(&dtsled.cdev);/* 删除 cdev */
287     unregister_chrdev_region(dtsled.devid, DTSLED_CNT);/*注销设备号*/
288
289     device_destroy(dtsled.class, dtsled.devid);
290     class_destroy(dtsled.class);
291 }
292
293 module_init(led_init);
294 module_exit(led_exit);
295 MODULE_LICENSE("GPL");
296 MODULE_AUTHOR("zipeng");

编译驱动程序和测试 APP

编译驱动程序

1 KERNELDIR := /home/zipeng/linux/myKernel/linux-imx
rel_imx_4.1.15_2.1.0_ga_alientek
......
4 obj-m := dtsled.o
......
11 clean :
12 $(MAKE) -C $(KERNELDIR) M = $(CURRENT_PATH) clean
命令:make -j32

编译测试 APP

arm-linux-gnueabihf-gcc ledApp.c -o ledApp

运行测试

将dtsled.ko ledApp 这两个文件拷贝到 rootfs/lib/modules/4.1.15 目录
中,重启系统,进入到目录 lib/modules/4.1.15 中,输入如下命令加载 dtsled.ko 驱动模块:
depmod //第一次加载驱动的时候需要运行此命令
modprobe dtsled.ko //加载驱动
./ledApp /dev/dtsled 1   //打开 LED
./ledApp /dev/dtsled 0   //熄灭  LED
卸载驱动:
rmmod dtsled.ko
  • 5
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值