字符设备驱动:LED设备树+平台总线实现方式

1. 环境:

1.1 开发板:正点原子 I.MX6U ALPHA V2.2

1.2 开发PC:Ubuntu20.04

1.3 U-boot:uboot-imx-rel_imx_4.1.15_2.1.0_ga.tar.bz2

1.4 LInux内核:linux-imx-rel_imx_4.1.15_2.1.0_ga.tar.bz2

1.5 rootfs:busybox-1.29.0.tar.bz2制作

1.6 交叉编译工具链:gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf.tar.xz

 

2. 硬件控制说明

2.1 由GPIO1 PIN3控制,高---熄灭, 低---点亮

 

3. 设备树修改

3.1 在节点iomuxc/imx6ul-evk下新增pinctrl_led节点,如下

1         pinctrl_led: ledgrp {
2             fsl,pins = <
3                 MX6UL_PAD_GPIO1_IO03__GPIO1_IO03    0X10B0        //设置GPIO的复用功能和电气属性
4                 >;
5         };

 

3.2 在根目录"/"中创建"gpioled"子节点

 1     //创建gpioled节点
 2     gpioled {
 3         #address-cells = <1>;
 4         #size-cells = <1>;
 5         compatible = "imx-gpioled";
 6         pinctrl-names = "default";
 7         pinctrl-0 = <&pinctrl_led>;    //指向iomuxc/imx6ul-evk下的pinctrl_led节点
 8         led-gpio = <&gpio1 3 GPIO_ACTIVE_LOW>;  //给节点赋初始值
 9         status = "okay";
10     };

 

3.3 检查设备树,将本GPIO做其他用途的属性注释掉

 

4. 驱动代码

  1 #include <linux/init.h>
  2 #include <linux/module.h>
  3 #include <linux/fs.h>           //文件操作函数,file_operarions
  4 #include <linux/device.h>       //设备申请函数,device_create
  5 #include <linux/platform_device.h>
  6 #include <linux/slab.h>         //内核空间申请函数件,kmalloc
  7 #include <asm/uaccess.h>        //内核与用户空间消息拷贝函数,copy_to_usser  &  copy_from_user
  8 #include <asm/io.h>             //内存地址映射函数,ioremap
  9 #include <linux/cdev.h>
 10 #include <linux/of_gpio.h>
 11 #include <linux/fcntl.h>
 12 
 13 //led控制
 14 #define LED_ON                  1
 15 #define LED_OFF                 0
 16 #define LED_MAJOR               249     //用于静态设备号申请
 17 
 18 
 19 //自定义一个结构体,将一些需要的变量放在其中,以便管理
 20 struct led_device
 21 {
 22     struct class *led_class;
 23     struct device *led_device;
 24     struct cdev led_cdev;  
 25     dev_t   devno;
 26     int major;                   //用于保存主设备号
 27     struct device_node *node;   //LED 设备节点
 28     int led_num;                //LED GPIO标号
 29 };
 30   
 31 static  struct led_device *imx_led_gpio1_3;
 32 
 33 //设备文件的open操作函数
 34 static  int led_open(struct inode *inode, struct file *file)
 35 {
 36 
 37     return 0;
 38 }
 39 
 40 //设备文件的close操作函数
 41 static int led_close(struct inode *inode, struct file *file)
 42 {
 43     return 0;
 44 }
 45 
 46 //设备文件的read操作函数
 47 static ssize_t led_read(struct file *file, char __user *buf, size_t size, loff_t *f_ops)
 48 {
 49     return 0;
 50 }
 51 
 52 //设备文件的write操作函数
 53 static ssize_t led_write(struct file *file, const char __user *buf, size_t size, loff_t *f_ops)
 54 {
 55     int ret;
 56     unsigned char cmd[1];
 57 
 58     ret = copy_from_user(cmd, buf, size);
 59     if(ret < 0) 
 60     {
 61         printk("kernel write failed!\r\n");
 62         return -EFAULT;
 63     }
 64 
 65 
 66    if(*cmd == LED_ON)
 67    {
 68         gpio_set_value(imx_led_gpio1_3->led_num, 0);
 69    }
 70    else if(*cmd == LED_OFF)
 71    {
 72         gpio_set_value(imx_led_gpio1_3->led_num, 1);
 73    }
 74    else
 75    {
 76        printk("led command is invalid!\n");
 77        return -2;
 78    } 
 79 
 80     return 0;
 81 }
 82 
 83 static long led_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 84 {
 85     switch (cmd)
 86     {
 87      case LED_OFF:
 88                 gpio_set_value(imx_led_gpio1_3->led_num, 1);
 89                 break;
 90     case LED_ON:
 91                 gpio_set_value(imx_led_gpio1_3->led_num, 0);
 92                 break;
 93     default:
 94                 gpio_set_value(imx_led_gpio1_3->led_num, 1);
 95                 break;
 96     }
 97 
 98 
 99     return 0;
100 }
101 
102 //填充file_operations结构体
103 static  const struct file_operations led_fops = {
104     .owner               =   THIS_MODULE,
105     .open                =   led_open,
106     .release             =   led_close,
107     .write               =   led_write,
108     .read                =   led_read,
109     .unlocked_ioctl      =   led_ioctl,
110 };
111 
112 
113 static int led_gpio_probe(struct platform_device *pdev)
114 {
115     int ret = -2;
116 
117     //为自定义机构提申请内核内存
118     imx_led_gpio1_3 = kmalloc(sizeof(struct led_device), GFP_KERNEL);
119     if(imx_led_gpio1_3 == NULL)
120     {
121         printk(KERN_ERR "kmalloc fail!\n");
122         return -ENOMEM;
123     }
124 
125     //ret = register_chrdev(LED_MAJOR, "led", &led_fops);
126     //如果使用动态申请,则必须使用cdev_init() & cdev_add()这两个函数;静态则不需要
127     ret = alloc_chrdev_region(&imx_led_gpio1_3->devno, 0, 1, "led");
128     if(ret < 0)
129     {
130         printk(KERN_ERR "register major failed!\n");
131         ret = -EINVAL;
132         goto err1;
133     }
134 
135     imx_led_gpio1_3->major = MAJOR(imx_led_gpio1_3->devno);
136     imx_led_gpio1_3->led_cdev.owner = THIS_MODULE;
137     
138     //将设备与文件操作函数关联
139     cdev_init(&imx_led_gpio1_3->led_cdev, &led_fops);
140 
141     //注册设备
142     cdev_add(&imx_led_gpio1_3->led_cdev,imx_led_gpio1_3->devno, 1);
143 
144     //创建设备类
145     imx_led_gpio1_3->led_class = class_create(THIS_MODULE, "led_class");
146     if(IS_ERR(imx_led_gpio1_3->led_class))
147     {
148         printk(KERN_ERR "failed to create class!\n");
149         ret = PTR_ERR(imx_led_gpio1_3->led_class);
150         goto err2;
151     }
152 
153     //创建设备文件,此函数最后一个参数led,其实就是设备名,应用程序open打开的也是这个文件名
154     imx_led_gpio1_3->led_device = device_create(imx_led_gpio1_3->led_class, NULL, imx_led_gpio1_3->devno, NULL, "led");
155     if(IS_ERR(imx_led_gpio1_3->led_device))
156     {
157         printk(KERN_ERR "failed to create device!\n");
158         ret = PTR_ERR(imx_led_gpio1_3->led_device);
159         goto err3;
160     }
161 
162     //获取设备节点
163     imx_led_gpio1_3->node = of_find_node_by_path("/gpioled");
164     if(imx_led_gpio1_3->node == NULL)
165     {
166         printk("gpioled node nost find!\n");
167         return -EINVAL;
168     }
169 
170     //获取GPIO编号
171     imx_led_gpio1_3->led_num = of_get_named_gpio(imx_led_gpio1_3->node, "led-gpio", 0);
172     if(imx_led_gpio1_3->led_num < 0)
173     {
174         printk("can't get led-gpio\n");
175          goto err4;
176     }
177 
178     //申请一个GPIO的管脚,led0为设置的一个别名
179     gpio_request(imx_led_gpio1_3->led_num, "led0");
180 
181     //设置GPIO为输出,并输出高电平
182     gpio_direction_output(imx_led_gpio1_3->led_num, 1);
183 
184     return 0;
185 
186 //注销设备节点
187 err4:
188     device_destroy(imx_led_gpio1_3->led_class, imx_led_gpio1_3->major);
189 
190 //注销设备类
191 err3:
192     class_destroy(imx_led_gpio1_3->led_class);
193 
194 //注销设备号
195 err2:
196     unregister_chrdev(imx_led_gpio1_3->major, "led");
197 
198 //释放由kmalloc申请的内存
199 err1:
200     kfree(imx_led_gpio1_3);
201     return ret;
202 
203 }
204 
205 
206 static int led_gpio_remove(struct platform_device *pdev)
207 {
208     device_destroy(imx_led_gpio1_3->led_class, imx_led_gpio1_3->major);
209     class_destroy(imx_led_gpio1_3->led_class);
210     unregister_chrdev(imx_led_gpio1_3->major, "led");
211     kfree(imx_led_gpio1_3);
212 
213     return 0;
214 }
215 
216 //驱动匹配列表
217 static const struct of_device_id led_of_match[] = {
218     {.compatible = "imx-gpioled"},
219 
220 };
221 
222 struct platform_driver led_gpio_driver = {
223     .probe  =   led_gpio_probe,
224     .remove =   led_gpio_remove,
225     .driver =   {
226         .owner  =   THIS_MODULE,
227         .name   =   "led_gpio",
228         .of_match_table = led_of_match,
229     },
230 };
231 
232 static int __init led_init(void)
233 {
234     platform_driver_register(&led_gpio_driver);
235     return 0;
236 }
237 
238 static void __exit led_exit(void)
239 {   
240     platform_driver_unregister(&led_gpio_driver);
241 }
242 
243 module_init(led_init);
244 module_exit(led_exit);
245 MODULE_LICENSE("Dual BSD/GPL");
View Code

 

5. 测试代码

字符设备驱动:LED平台总线实现方式中的测试代码一致

 

总结:

1. 设备树驱动,LED使用到了pinctrl和gpio子系统,其初始化内核自动完成,其中pinctrl子系统主要完成对GPIO的功能复用和电气属性的设置,GPIO子系统主要是pinctrl的基础上,对驱动提供资源和GPIO的初始化

2. pinctrl子系统节点,需要以pinctrl_开头

3. 设备树+平台总线方式, 驱动的主要步骤,大致为:1.设备树中增加pinctrl和gpio子系统的节点-->2.编写驱动(格式基本跟platform一致,后面再通过of函数获取资源信息,然后再使用GPIO set函数进行设置)

 

posted @ 2021-10-22 22:31  秋水寒林  阅读(185)  评论(0编辑  收藏  举报