
之前编辑了一个platform设备,我们需要写一个设备程序,之前使用设备树发现设备程序中的内容我们都可以用设备树来实现,因此我们可以通过platform总线驱动设备树中的硬件设备,这样我们就可以只写一个驱动程序就可以了。 使用platform总线和设备树中的节点之间有一个pinctrl节点来关联,这里需要改动的文件有两个:stm32mp15-pinctrl.dtsi和stm32mp157a-dk1.dts两个。 stm32mp15-pinctrl.dtsi中添加pinctrl节点: led_pins_xhy:gpioled-0 { pins { pinmux= <STM32_PINMUX('A', 14, GPIO)>; //设置 PA14 复用为 GPIO 功能 drive-push-pull; //推挽输出 bias-pull-up; //内部上拉 output-high; //输出高电平 slew-rate =<0>; //速度为0 档 }; }; stm32mp157a-dk1.dts中xhy_led加入 pinctrl-names和pinctrl-0节点信息。 xhy_led { compatible= "xhy,led"; status= "okay"; led-gpio= <&gpioa 14 GPIO_ACTIVE_LOW>; pinctrl-names= "default"; pinctrl-0= <&led_pins_xhy>; }; 然后重新编译.dtb文件: make ARCH=arm CROSS_COMPILE=arm-none-linux-gnueabihf- dtbs 然后将dts/stm32mp157a-dk1.dtb复制到tftp文件夹中: cp arch/arm/boot/dts/stm32mp157a-dk1.dtb /home/helloxhy/tftp/ -rf 驱动编写 这只需要编写一个led.c文件就可以了,由于这次即使用了platform总线有使用了设备树,属于之前几种编写方式的集合,所以我们从新编写led.c文件: 首先写入头文件: #include <linux/types.h> #include <linux/kernel.h> #include <linux/delay.h> #include <linux/ide.h> #include <linux/init.h> #include <linux/module.h> #include <linux/errno.h> #include <linux/gpio.h> #include <linux/cdev.h> #include <linux/device.h> #include <linux/of_gpio.h> #include <linux/semaphore.h> #include <linux/timer.h> #include <linux/irq.h> #include <linux/wait.h> #include <linux/poll.h> #include <linux/fs.h> #include <linux/fcntl.h> #include <linux/platform_device.h> #include <asm/mach/map.h> #include <asm/uaccess.h> #include <asm/io.h> 然后定义设备结构体: struct leddev_dev{ /* 用于注册设备 */ dev_tdevid; structcdev cdev; structclass *class; structdevice *device; /* 用于从设备树获取信息 */ structdevice_node *node; intgpio_led; }; struct leddev_dev leddev; 之后定义驱动结构体,由于使用了platform总线所以release由设备驱动定义: static struct file_operations led_fops = { .owner= THIS_MODULE, .open= led_open, .read =led_read, .write= led_write, }; 其中.open和.read为空函数,原因是.read没有调用,这里只写没有读IO口,.open没有需要执行的信息。 所以只需要写.write程序就可以了,其中只调用了两个函数 copy_from_user(databuf, buf, cnt)获取需要执行的信息 gpio_set_value(leddev.gpio_led, 0)对IO口进行操作 : static ssize_t led_write(struct file *filp,const char __user *buf, size_t cnt, loff_t *offt) { intretvalue; unsignedchar databuf[1]; unsignedchar ledstat; 0 ~1 s V$ Q* j retvalue= copy_from_user(databuf, buf, cnt); if(retvalue< 0) { printk("kernelwrite failed!\r\n"); return-EFAULT; } ledstat= databuf[0]; if(ledstat == LEDON) { lgpio_set_value(leddev.gpio_led,0); }else if (ledstat == LEDOFF) { gpio_set_value(leddev.gpio_led,1); } return0; } 接下来需要对platform总线进行操作,驱动的入口和出口函数分别是对platform模块的加载和卸载: static int __init leddriver_init(void) { returnplatform_driver_register(&led_driver); //加载led_driver } static void __exit leddriver_exit(void) { platform_driver_unregister(&led_driver);//卸载led_driver } 由此可见需要定义platform模块,名称为led_driver,其中需要定义的由四个: 1设备树的名称,便于关联设备树。2、驱动的名称。 3、flatform驱动初始化。4、flatform驱动清除: static struct platform_driver led_driver ={ .driver = { .name = "xhy-led", // 2、驱动的名称。 .of_match_table = led_of_match, // 1设备树的名称,便于关联设备树 }, .probe = led_probe, //3、flatform驱动初始化 .remove = led_remove, //4、flatform驱动清除 }; 接下来需要定义led_of_match其中.compatible是设备树中定义的内容: static const struct of_device_idled_of_match[] = { {.compatible = " xhy,led " }, { } }; MODULE_DEVICE_TABLE(of, led_of_match); 最后需要编写flatform驱动初始化和flatform驱动清除两个函数: flatform驱动初始化包括gpio的初始化和设备注册。 gpio初始化使用函数:of_get_named_gpio、gpio_request和gpio_direction_output,分别为从设备树中获取gpio,从flatform总线申请gpio,以及配置。 leddev.gpio_led = of_get_named_gpio(nd,"led-gpio", 0); //"led-gpio"在设备树中的内容为led-gpio = <&gpioi 0 GPIO_ACTIVE_LOW>; ret = gpio_request(leddev.gpio_led,"LED0"); gpio_direction_output(leddev.gpio_led,1); 接下来是设备注册,直接使用新字符设备中的设备注册: /* 1、设置设备号 */ ret =alloc_chrdev_region(&leddev.devid, 0, LEDDEV_CNT, LEDDEV_NAME); if(ret< 0) { pr_err("%sCouldn't alloc_chrdev_region, ret=%d\r\n", LEDDEV_NAME, ret); gotofree_gpio; } , Q, Q" P6 I' [7 a7 ~ \- u/ Z /*2、初始化cdev */ leddev.cdev.owner= THIS_MODULE; cdev_init(&leddev.cdev,&led_fops); : D P/ v9 V4 V /*3、添加一个cdev */ ret= cdev_add(&leddev.cdev, leddev.devid, LEDDEV_CNT); if(ret< 0) gotodel_unregister; /*4、创建类 */ leddev.class= class_create(THIS_MODULE, LEDDEV_NAME); if(IS_ERR(leddev.class)) { gotodel_cdev; } /*5、创建设备 */ leddev.device= device_create(leddev.class, NULL, leddev.devid, NULL, LEDDEV_NAME); if(IS_ERR(leddev.device)) { gotodestroy_class; } 最后是platform驱动的remove函数,主要是led关闭和设备的注销: gpio_set_value(leddev.gpio_led,1); /* 卸载驱动的时候关闭LED*/ gpio_free(leddev.gpio_led); /* 注销GPIO */ cdev_del(&leddev.cdev); /* 删除cdev */ unregister_chrdev_region(leddev.devid,LEDDEV_CNT); /* 注销设备号 */ device_destroy(leddev.class,leddev.devid); /* 注销设备 */ class_destroy(leddev.class);/* 注销类 */ 编译测试: Ubuntu编译: make arm-none-linux-gnueabihf-gcc ledApp.c -o ledApp cp led.ko ledApp /home/helloxhy/nfs/lib/modules/5.4.56/ -rf 板子上测试: depmod modprobe led ./ledApp /dev/newled 0 ./ledApp /dev/newled 1 ![]() |
基于STM32MP1和STM32MP2在嵌入式Linux平台上部署有效的安全保护机制
利用STM32MP1和STM32MP2为嵌入式Linux提供有效的安全措施:供当今决策者参考的3条宝贵经验
STM32MP1 WiFi连接
【STM32MP157】从ST官方例程中分析RPMsg-TTY/SDB核间通信的使用方法
【STM32MPU 安全启动】 TF-A BL2 TrustedBoot原理学习
《STM32MPU安全启动》学**结
《STM32MPU安全启动》学习笔记之optee 如何加载CORTEX-M核和使能校验
《STM32MPU安全启动》学习笔记之TF-A BL2校验optee和uboot的流程以及如何使能
《STM32MPU 安全启动》课程学习心得+开启一扇通往嵌入式系统安全领域深处的大门。
《STM32MPU安全启动》 课程学习心得