练习要求:
一、设备树
1、配置设备树信息:将3个led灯和1个风扇使用到的设备信息配置到设备树中
二、设备驱动层
1、通过of_find_node_by_name、of_get_named_gpion等内核核心层统一的api接口调用外设;
2、通过udev设备管理器自动注册并创建设备文件
3、通过ioctl控制开启/关闭外设,其中,3个led灯通过传递 设备名称-次设备号minor 控制具体的某栈灯;
三、应用层
1、多线程开发,第一个线程实现三个灯的流水,第二个线程实现风扇的控制
2、通过open、close、ioctl 对应的字符设备文件调用对应的驱动
1、设备树的配置
- arch/arm/boot/dts/stm32mp157a-fsmp1a.dts (根目录 / 里面)
leds{led1-gpio = <&gpioe 10 0>;led2-gpio = <&gpiof 10 0>;led3-gpio = <&gpioe 8 0>;
};fans{fan-gpio = <&gpioe 9 0>;
};
2、源码
2.1、应用程序
- app.c
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <sys/ioctl.h>
#include "led_driver.h"
#include "fan_driver.h"void *led_task(void *args)
{// 打开文件int fd1 = open("/dev/myled1", O_RDWR);int fd2 = open("/dev/myled2", O_RDWR);int fd3 = open("/dev/myled3", O_RDWR);// 操作硬件for (int i = 0;; i++){if (i % 3 == 0){ioctl(fd1, CMD_LED_ON);ioctl(fd2, CMD_LED_OFF);ioctl(fd3, CMD_LED_OFF);}else if (i % 3 == 1){ioctl(fd1, CMD_LED_OFF);ioctl(fd2, CMD_LED_ON);ioctl(fd3, CMD_LED_OFF);}else{ioctl(fd1, CMD_LED_OFF);ioctl(fd2, CMD_LED_OFF);ioctl(fd3, CMD_LED_ON);}sleep(1);}// 关闭文件close(fd1);close(fd2);close(fd3);
}// void *beep_task(void *args)
// {
// // 打开文件
// int fd = open("/dev/mybeep", O_RDWR);
// char cmd[3] = {0};
// unsigned short cnt = 0;
// printf("beep_task\n");
// for (int i = 0;; i++)
// {
// if (cnt % 5 == 0)
// {
// ioctl(fd, CMD_BEEP_ON);
// }
// else
// {
// ioctl(fd, CMD_BEEP_OFF);
// }
// sleep(1);
// }
// // 关闭文件
// close(fd);
// }void *fan_task(void *args)
{// 打开文件int fd = open("/dev/myfan", O_RDWR);for(int i=0;;i++){if (i % 2 == 0){ioctl(fd, CMD_FAN_ON);}else{ioctl(fd, CMD_FAN_OFF);}sleep(5);}// 关闭文件close(fd);
}int main(int argc, char *argv)
{pthread_t pid1, pid2, pid3;pthread_create(&pid1, NULL, led_task, NULL);// pthread_create(&pid2, NULL, beep_task, NULL);pthread_create(&pid3, NULL, fan_task, NULL);pthread_join(pid1, NULL);// pthread_join(pid2, NULL);pthread_join(pid3, NULL);return 0;
}
2.2、led驱动程序
- led.h:
#ifndef _LED_H_
#define _LED_H_//
#define CMD_LED_ON _IO('L', 0)
#define CMD_LED_OFF _IO('L', 1)
//#endif
- led.c:
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/device/class.h>
#include <linux/device.h>
#include <linux/gpio.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include "led_driver.h"int major; // 主设备号
struct device_node *np; // 设备树节点
int led1_gpio_no; // gpio编号
int led2_gpio_no;
int led3_gpio_no;
struct class *pClass; // udev的文件类指针long file_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{int signal = 0;int minor = (int)file->private_data;int gpio_no;if (minor == 1)gpio_no = led1_gpio_no;else if(minor == 2)gpio_no = led2_gpio_no;else gpio_no = led3_gpio_no;if (cmd == CMD_LED_ON){signal=1;}else{signal=0;}//改变gpio的电平值gpio_set_value(gpio_no, signal);return 0;
}int file_open(struct inode *inode, struct file *file)
{file->private_data = (void*)MINOR(inode->i_rdev);return 0;
}int file_close(struct inode *inode, struct file *file)
{return 0;
}struct file_operations led_fops = {.unlocked_ioctl = file_ioctl,.open = file_open,.release = file_close};int __init led_driver_init(void)
{int minor;// 注册字符设备驱动 register_chrdevmajor = register_chrdev(0, "myled", &led_fops);if (major == 0){printk("register_chrdev failed\n");return -1;}printk("register_chrdev succes. major=[%d]\n", major);// 从设备树获取硬件信息np = of_find_node_by_name(NULL, "leds");if (np == NULL){printk("of_find_node_by_name failed\n");return -1;}printk("of_find_node_by_name success\n");// led1 = 获取gpio操作句柄led1_gpio_no = of_get_named_gpio(np, "led1-gpio", 0);if (led1_gpio_no < 0){printk("of_get_named_gpio failed\n");return -1;}printk("of_get_named_gpio success. no1=[%d]\n", led1_gpio_no);if (gpio_request(led1_gpio_no, "led1") < 0){printk("gpio_request failed\n");return -1;}printk("gpio_request success\n");gpio_direction_output(led1_gpio_no, 0);// led1 = 获取gpio操作句柄led2_gpio_no = of_get_named_gpio(np, "led2-gpio", 0);if (led2_gpio_no < 0){printk("of_get_named_gpio failed\n");return -1;}printk("of_get_named_gpio success. no2=[%d]\n", led2_gpio_no);if (gpio_request(led2_gpio_no, "led2") < 0){printk("gpio_request failed\n");return -1;}printk("gpio_request success\n");gpio_direction_output(led2_gpio_no, 0);// led3 = 获取gpio操作句柄led3_gpio_no = of_get_named_gpio(np, "led3-gpio", 0);if (led3_gpio_no < 0){printk("of_get_named_gpio failed\n");return -1;}printk("of_get_named_gpio success. no3=[%d]\n", led3_gpio_no);if (gpio_request(led3_gpio_no, "led3") < 0){printk("gpio_request failed\n");return -1;}printk("gpio_request success\n");gpio_direction_output(led3_gpio_no, 0);// 初始化gpio// 向设备管理器udev注册设备文件信息pClass = class_create(THIS_MODULE, "myled");if (IS_ERR(pClass)){printk("class_create failed\n");return -1;}printk("class_create success\n");for (minor = 1; minor < 4; minor++){struct device *pDev = device_create(pClass, NULL, MKDEV(major, minor), NULL, "myled%d", minor);if (IS_ERR(pDev)){printk("device_create failed\n");return -1;}printk("device_create success. major=[%d], monor=[%d]\n", major, minor);}return 0;
}void __exit led_driver_exit(void)
{int minor;// 从设备管理器udev注销设备文件信息for (minor = 1; minor < 4; minor++){device_destroy(pClass, MKDEV(major, minor));}class_destroy(pClass);// 释放gpio资源gpio_free(led1_gpio_no);// 注销字符设备驱动 unregister_chrdevunregister_chrdev(major, "myled");
}module_init(led_driver_init);
module_exit(led_driver_exit);
MODULE_LICENSE("GPL");
2.3、fan驱动程序
- fan.h:
#ifndef _FAN_H_
#define _FAN_H_#define CMD_FAN_ON _IO('F', 0)
#define CMD_FAN_OFF _IO('F', 1)#endif
- fan.c:
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/device/class.h>
#include <linux/device.h>
#include <linux/gpio.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include "fan_driver.h"int major;//主设备号
struct device_node * np;//设备树节点
int fan_gpio_no;//设备树种gpio编号
struct class *pClass;//udev中的文件分类long file_ioctl(struct file * file, unsigned int cmd, unsigned long arg){if(cmd == CMD_FAN_ON){printk("!!!!开风扇\n");gpio_set_value(fan_gpio_no, 1);}else if(cmd == CMD_FAN_OFF){printk("!!!!关风扇\n");gpio_set_value(fan_gpio_no, 0);}return 0;
}int file_open(struct inode * inode, struct file * file){return 0;
}int file_close(struct inode * inode, struct file * file){return 0;
}struct file_operations led_fops={.unlocked_ioctl = file_ioctl,.open = file_open,.release = file_close
};int __init fan_driver_init(void){//注册字符设备驱动 register_chrdevmajor = register_chrdev(0, "myfan", &led_fops);if(major == 0){printk("register_chrdev failed\n");return -1;}printk("register_chrdev succes. major=[%d]\n", major);//从设备树获取硬件信息np = of_find_node_by_name(NULL, "fans");if(np == NULL){printk("of_find_node_by_name failed\n");return -1; }printk("of_find_node_by_name success\n");//获取gpio操作句柄fan_gpio_no =of_get_named_gpio(np, "fan-gpio", 0);if (fan_gpio_no < 0){printk("of_get_named_gpio failed\n");return -1;}printk("of_get_named_gpio success. fan_gpio_no=[%d]\n", fan_gpio_no);if(gpio_request(fan_gpio_no, "fan-gpio") < 0){printk("gpio_request failed\n");return -1;}printk("gpio_request success\n");//初始化gpio//设置gpio为输出gpio_direction_output(fan_gpio_no, 0);//向设备管理器udev注册设备文件信息pClass = class_create(THIS_MODULE, "fan_class");if (IS_ERR(pClass)) {printk("class_create failed\n");return -1;}printk("class_create success\n");struct device * pDevice=device_create(pClass, NULL, MKDEV(major, 0), NULL, "myfan");if(IS_ERR(pDevice)){printk("device_create failed\n");return -1;}printk("device_create success\n");return 0;
}void __exit fan_driver_exit(void){//从设备管理器udev注销设备文件信息device_destroy(pClass, MKDEV(major, 0));class_destroy(pClass);//释放gpio资源gpio_free(fan_gpio_no);//注销字符设备驱动 unregister_chrdevunregister_chrdev(major, "myfan");
}module_init(fan_driver_init);
module_exit(fan_driver_exit);
MODULE_LICENSE("GPL");
3、疑问
配置设备树中,引用关系并没有搞明白,暂且认为是一种固定的格式。
例如: