練習要求:
一、設備樹
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、疑問
配置設備樹中,引用關系并沒有搞明白,暫且認為是一種固定的格式。
例如: