字符设备驱动: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");
5. 测试代码
与字符设备驱动:LED平台总线实现方式中的测试代码一致
总结:
1. 设备树驱动,LED使用到了pinctrl和gpio子系统,其初始化内核自动完成,其中pinctrl子系统主要完成对GPIO的功能复用和电气属性的设置,GPIO子系统主要是pinctrl的基础上,对驱动提供资源和GPIO的初始化
2. pinctrl子系统节点,需要以pinctrl_开头
3. 设备树+平台总线方式, 驱动的主要步骤,大致为:1.设备树中增加pinctrl和gpio子系统的节点-->2.编写驱动(格式基本跟platform一致,后面再通过of函数获取资源信息,然后再使用GPIO set函数进行设置)