通过ioctl函数选择不同硬件的控制,如实现对LED、蜂鸣器、马达、风扇的控制
1.将GPIO的相关寄存器封装成结构体 --------> gpio.h
2.LED相关驱动文件 --------> led.c
3.蜂鸣器相关驱动文件 --------> beep.c
4.风扇相关驱动文件 --------> fan.c
5.马达相关驱动文件 --------> motor.c
6.应用层测试文件 --------> test.c
gpio.h
/** Copyright (c) 2023 by Huijie Xia, All Rights Reserved.* @Author: Huijie Xia* @Date: 2023-07-05 16:21:43* @LastEditTime: 2023-07-05 16:47:21* @FilePath: /B_Drive/day3/led_beep_fan_motor/gpio.h* @version:* @Description:*/
#ifndef __GPIO_H_
#define __GPIO_H_typedef struct {volatile unsigned int MODER; // 0x00volatile unsigned int OTYPER; // 0x04volatile unsigned int OSPEEDR; // 0x08volatile unsigned int PUPDR; // 0x0Cvolatile unsigned int IDR; // 0x10volatile unsigned int ODR; // 0x14volatile unsigned int BSRR; // 0x18volatile unsigned int LCKR; // 0x1Cvolatile unsigned int AFRL; // 0x20volatile unsigned int AFRH; // 0x24volatile unsigned int BRR; // 0x28volatile unsigned int res;volatile unsigned int SECCFGR; // 0x30
} gpio_t;#define PHY_RCC_GPIO 0x50000A28
#define PHY_GPIOB_BASE 0x50003000
#define PHY_GPIOE_BASE 0x50006000
#define PHY_GPIOF_BASE 0x50007000#define LED1_ON _IO('l', 1)
#define LED1_OFF _IO('l', 0)
#define LED_ON _IOW('l', 1, int)
#define LED_OFF _IOW('l', 0, int)#define FAN_ON _IO('f', 1)
#define FAN_OFF _IO('f', 0)#define BEEP_ON _IO('b', 1)
#define BEEP_OFF _IO('b', 0)#define MOTOR_ON _IO('m', 1)
#define MOTOR_OFF _IO('m', 0)#endif
led.c
/** Copyright (c) 2023 by Huijie Xia, All Rights Reserved.* @Author: Huijie Xia* @Date: 2023-06-29 08:56:26* @LastEditTime: 2023-07-05 16:24:06* @FilePath: /B_Drive/day3/led_beep_fan_motor/led.c* @version:* @Description: 驱动控制LED灯*/
#include "gpio.h"
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/uaccess.h>#define CNAME "myled"
struct class* cls;
struct device* dev;int major;
char kbuf[128] = { 0 };
unsigned int* KERNEL_RCC_GPIO;
gpio_t* KERNEL_GPIOE_BASE;
gpio_t* KERNEL_GPIOF_BASE;int mycdev_open(struct inode* inode, struct file* file)
{printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);/* 地址映射 */KERNEL_RCC_GPIO = ioremap(PHY_RCC_GPIO, 4);if (KERNEL_RCC_GPIO == NULL) {printk("ioremap RCC error!\n");return -ENOMEM;}KERNEL_GPIOE_BASE = ioremap(PHY_GPIOE_BASE, sizeof(gpio_t));if (KERNEL_GPIOE_BASE == NULL) {printk("ioremap GPIOE BASE error!\n");return -ENOMEM;}KERNEL_GPIOF_BASE = ioremap(PHY_GPIOF_BASE, sizeof(gpio_t));if (KERNEL_GPIOF_BASE == NULL) {printk("ioremap GPIOF BASE error!\n");return -ENOMEM;}/* LED1(PE10)寄存器初始化(读,改,写) */writel(readl(KERNEL_RCC_GPIO) | (0x1 << 4), KERNEL_RCC_GPIO); /* GPIOE时钟使能 */writel(readl(&KERNEL_GPIOE_BASE->MODER) & (~(0x3 << 20)), &KERNEL_GPIOE_BASE->MODER); /* MODER寄存器21~20bit清零 */writel(readl(&KERNEL_GPIOE_BASE->MODER) | (0x1 << 20), &KERNEL_GPIOE_BASE->MODER); /* MODER寄存器设置为输出模式 */writel(readl(&KERNEL_GPIOE_BASE->ODR) & (~(0x1 << 10)), &KERNEL_GPIOE_BASE->ODR); /* LED1熄灭 *//* LED2(PF10)寄存器初始化(读,改,写) */writel(readl(KERNEL_RCC_GPIO) | (0x1 << 5), KERNEL_RCC_GPIO); /* GPIOF时钟使能 */writel(readl(&KERNEL_GPIOF_BASE->MODER) & (~(0x3 << 20)), &KERNEL_GPIOF_BASE->MODER); /* MODER寄存器21~20bit清零 */writel(readl(&KERNEL_GPIOF_BASE->MODER) | (0x1 << 20), &KERNEL_GPIOF_BASE->MODER); /* MODER寄存器设置为输出模式 */writel(readl(&KERNEL_GPIOF_BASE->ODR) & (~(0x1 << 10)), &KERNEL_GPIOF_BASE->ODR); /* LED2熄灭 *//* LED3(PE8)寄存器初始化(读,改,写) */writel(readl(KERNEL_RCC_GPIO) | (0x1 << 4), KERNEL_RCC_GPIO); /* GPIOE时钟使能 */writel(readl(&KERNEL_GPIOE_BASE->MODER) & (~(0x3 << 16)), &KERNEL_GPIOE_BASE->MODER); /* MODER寄存器21~20bit清零 */writel(readl(&KERNEL_GPIOE_BASE->MODER) | (0x1 << 16), &KERNEL_GPIOE_BASE->MODER); /* MODER寄存器设置为输出模式 */writel(readl(&KERNEL_GPIOE_BASE->ODR) & (~(0x1 << 8)), &KERNEL_GPIOE_BASE->ODR); /* LED3熄灭 */return 0;
}ssize_t mycdev_read(struct file* file, char __user* ubuf,size_t size, loff_t* offs)
{int ret;printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);if (size > sizeof(kbuf)) {size = sizeof(kbuf);}ret = copy_to_user(ubuf, kbuf, size);if (ret) {printk("copy data to user error!\n");return -EIO;}return size;
}
ssize_t mycdev_write(struct file* file, const char __user* ubuf,size_t size, loff_t* offs)
{int ret;printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);if (size > sizeof(kbuf)) {size = sizeof(kbuf);}ret = copy_from_user(kbuf, ubuf, size);if (ret) {printk("copy data from user error!\n");return -EIO;}// switch (kbuf[0]) {// case '1': /* LED1点亮 */// writel(readl(&KERNEL_GPIOE_BASE->ODR) | (0x1 << 10), &KERNEL_GPIOE_BASE->ODR);// break;// case '2': /* LED1熄灭 */// writel(readl(&KERNEL_GPIOE_BASE->ODR) & (~(0x1 << 10)), &KERNEL_GPIOE_BASE->ODR);// break;// case '3': /* LED2点亮 */// writel(readl(&KERNEL_GPIOF_BASE->ODR) | (0x1 << 10), &KERNEL_GPIOF_BASE->ODR);// break;// case '4': /* LED2熄灭 */// writel(readl(&KERNEL_GPIOF_BASE->ODR) & (~(0x1 << 10)), &KERNEL_GPIOF_BASE->ODR);// break;// case '5': /* LED3点亮 */// writel(readl(&KERNEL_GPIOE_BASE->ODR) | (0x1 << 8), &KERNEL_GPIOE_BASE->ODR);// break;// case '6': /* LED3熄灭 */// writel(readl(&KERNEL_GPIOE_BASE->ODR) & (~(0x1 << 8)), &KERNEL_GPIOE_BASE->ODR);// break;// }// kbuf[0] == 1 ? (writel(readl(&KERNEL_GPIOE_BASE->ODR) | ((1 << 10)), &KERNEL_GPIOE_BASE->ODR)) : (writel(readl(&KERNEL_GPIOE_BASE->ODR) & (~(1 << 10)), &KERNEL_GPIOE_BASE->ODR));return size;
}long mycdev_ioctl(struct file* file, unsigned int cmd, unsigned long arg)
{switch (cmd) {case LED_ON:switch (arg) {case 1:writel(readl(&KERNEL_GPIOE_BASE->ODR) | (0x1 << 10), &KERNEL_GPIOE_BASE->ODR);break;case 2:writel(readl(&KERNEL_GPIOF_BASE->ODR) | (0x1 << 10), &KERNEL_GPIOF_BASE->ODR);break;case 3:writel(readl(&KERNEL_GPIOE_BASE->ODR) | (0x1 << 8), &KERNEL_GPIOE_BASE->ODR);break;default:break;}break;case LED_OFF:switch (arg) {case 1:writel(readl(&KERNEL_GPIOE_BASE->ODR) & (~(0x1 << 10)), &KERNEL_GPIOE_BASE->ODR);break;case 2:writel(readl(&KERNEL_GPIOF_BASE->ODR) & (~(0x1 << 10)), &KERNEL_GPIOF_BASE->ODR);break;case 3:writel(readl(&KERNEL_GPIOE_BASE->ODR) & (~(0x1 << 8)), &KERNEL_GPIOE_BASE->ODR);break;default:break;}break;default:break;}return 0;
}int mycdev_close(struct inode* inode, struct file* file)
{printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);return 0;
}struct file_operations fops = {.open = mycdev_open,.read = mycdev_read,.write = mycdev_write,.release = mycdev_close,.unlocked_ioctl = mycdev_ioctl,
};/*** @description: 入口:在安装驱动的时候入口函数执行,做资源申请的工作* demo_init:驱动入口的名字* __init:它是给编译器使用的,告诉编译器将这个函数放在.init.text段中* @return {*}*/
static int __init demo_init(void)
{/* 1. 注册字符设备驱动 */major = register_chrdev(0, CNAME, &fops);if (major < 0) {printk("register char device driver error errorcode = %d!\n", major);return major;}printk("create char device driver successful major = %d!\n", major);/* 2. 向上提交目录 */cls = class_create(THIS_MODULE, CNAME);if (IS_ERR(cls)) {printk("submit directory error\n");return -PTR_ERR(cls);}printk("submit directory ok\n");/* 3. 向上提交设备节点信息 */dev = device_create(cls, NULL, MKDEV(major, 0), NULL, "myled");if (IS_ERR(dev)) {printk("submit device node information error\n");return -PTR_ERR(dev);}printk("submit device node information ok\n");// printk("init:%s:%s:%d\n", __FILE__, __func__, __LINE__);return 0;
}/*** @description: 出口:在卸载驱动的时候出口函数执行,做资源释放的工作* demo_exit:驱动出口的名字* __exit:它是给编译器使用的,告诉编译器将这个函数放在.exit.text段中* @return {*}*/
static void __exit demo_exit(void)
{/* 取消地址映射 */iounmap(KERNEL_RCC_GPIO);iounmap(KERNEL_GPIOE_BASE);/* 1. 销毁提交设备节点信息 */device_destroy(cls, MKDEV(major, 0));/* 2. 销毁目录 */class_destroy(cls);/* 3. 注销字符设备驱动 */unregister_chrdev(major, CNAME);printk("exit:%s:%s:%d\n", __FILE__, __func__, __LINE__);
}/* 告诉内核入口地址 */
module_init(demo_init);/* 告诉内核出口地址 */
module_exit(demo_exit);/* 许可证:遵从GPL协议 */
MODULE_LICENSE("GPL");
beep.c
/** Copyright (c) 2023 by Huijie Xia, All Rights Reserved.* @Author: Huijie Xia* @Date: 2023-06-29 08:56:26* @LastEditTime: 2023-07-05 16:36:58* @FilePath: /B_Drive/day3/led_beep_fan_motor/beep.c* @version:* @Description: 驱动控制风扇*/
#include "gpio.h"
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/uaccess.h>#define CNAME "mybeep"
struct class* cls;
struct device* dev;int major;
char kbuf[128] = { 0 };
unsigned int* KERNEL_RCC_GPIO;
gpio_t* KERNEL_GPIOB_BASE;int mycdev_open(struct inode* inode, struct file* file)
{printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);/* 地址映射 */KERNEL_RCC_GPIO = ioremap(PHY_RCC_GPIO, 4);if (KERNEL_RCC_GPIO == NULL) {printk("ioremap RCC error!\n");return -ENOMEM;}KERNEL_GPIOB_BASE = ioremap(PHY_GPIOB_BASE, sizeof(gpio_t));if (KERNEL_GPIOB_BASE == NULL) {printk("ioremap GPIOB BASE error!\n");return -ENOMEM;}/* 蜂鸣器(PB6)寄存器初始化(读,改,写) */writel(readl(KERNEL_RCC_GPIO) | (0x1 << 1), KERNEL_RCC_GPIO); /* GPIOB时钟使能 */writel(readl(&KERNEL_GPIOB_BASE->MODER) & (~(0x3 << 12)), &KERNEL_GPIOB_BASE->MODER); /* MODER寄存器13~12bit清零 */writel(readl(&KERNEL_GPIOB_BASE->MODER) | (0x1 << 12), &KERNEL_GPIOB_BASE->MODER); /* MODER寄存器设置为输出模式 */writel(readl(&KERNEL_GPIOB_BASE->ODR) & (~(0x1 << 6)), &KERNEL_GPIOB_BASE->ODR); /* 蜂鸣器熄灭 */return 0;
}ssize_t mycdev_read(struct file* file, char __user* ubuf,size_t size, loff_t* offs)
{int ret;printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);if (size > sizeof(kbuf)) {size = sizeof(kbuf);}ret = copy_to_user(ubuf, kbuf, size);if (ret) {printk("copy data to user error!\n");return -EIO;}return size;
}
ssize_t mycdev_write(struct file* file, const char __user* ubuf,size_t size, loff_t* offs)
{int ret;printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);if (size > sizeof(kbuf)) {size = sizeof(kbuf);}ret = copy_from_user(kbuf, ubuf, size);if (ret) {printk("copy data from user error!\n");return -EIO;}return size;
}long mycdev_ioctl(struct file* file, unsigned int cmd, unsigned long arg)
{switch (cmd) {case BEEP_ON:writel(readl(&KERNEL_GPIOB_BASE->ODR) | (0x1 << 6), &KERNEL_GPIOB_BASE->ODR);break;case BEEP_OFF:writel(readl(&KERNEL_GPIOB_BASE->ODR) & (~(0x1 << 6)), &KERNEL_GPIOB_BASE->ODR);break;default:break;}return 0;
}int mycdev_close(struct inode* inode, struct file* file)
{printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);return 0;
}struct file_operations fops = {.open = mycdev_open,.read = mycdev_read,.write = mycdev_write,.release = mycdev_close,.unlocked_ioctl = mycdev_ioctl,
};/*** @description: 入口:在安装驱动的时候入口函数执行,做资源申请的工作* demo_init:驱动入口的名字* __init:它是给编译器使用的,告诉编译器将这个函数放在.init.text段中* @return {*}*/
static int __init demo_init(void)
{/* 1. 注册字符设备驱动 */major = register_chrdev(0, CNAME, &fops);if (major < 0) {printk("register char device driver error errorcode = %d!\n", major);return major;}printk("create char device driver successful major = %d!\n", major);/* 2. 向上提交目录 */cls = class_create(THIS_MODULE, CNAME);if (IS_ERR(cls)) {printk("submit directory error\n");return -PTR_ERR(cls);}printk("submit directory ok\n");/* 3. 向上提交设备节点信息 */dev = device_create(cls, NULL, MKDEV(major, 0), NULL, CNAME);if (IS_ERR(dev)) {printk("submit device node information error\n");return -PTR_ERR(dev);}printk("submit device node information ok\n");// printk("init:%s:%s:%d\n", __FILE__, __func__, __LINE__);return 0;
}/*** @description: 出口:在卸载驱动的时候出口函数执行,做资源释放的工作* demo_exit:驱动出口的名字* __exit:它是给编译器使用的,告诉编译器将这个函数放在.exit.text段中* @return {*}*/
static void __exit demo_exit(void)
{/* 取消地址映射 */iounmap(KERNEL_RCC_GPIO);iounmap(KERNEL_GPIOB_BASE);/* 1. 销毁提交设备节点信息 */device_destroy(cls, MKDEV(major, 0));/* 2. 销毁目录 */class_destroy(cls);/* 3. 注销字符设备驱动 */unregister_chrdev(major, CNAME);printk("exit:%s:%s:%d\n", __FILE__, __func__, __LINE__);
}/* 告诉内核入口地址 */
module_init(demo_init);/* 告诉内核出口地址 */
module_exit(demo_exit);/* 许可证:遵从GPL协议 */
MODULE_LICENSE("GPL");
fan.c
/** Copyright (c) 2023 by Huijie Xia, All Rights Reserved.* @Author: Huijie Xia* @Date: 2023-06-29 08:56:26* @LastEditTime: 2023-07-05 16:59:43* @FilePath: /B_Drive/day3/led_beep_fan_motor/fan.c* @version:* @Description: 驱动控制风扇*/
#include "gpio.h"
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/uaccess.h>#define CNAME "myfan"
struct class* cls;
struct device* dev;int major;
char kbuf[128] = { 0 };
unsigned int* KERNEL_RCC_GPIO;
gpio_t* KERNEL_GPIOE_BASE;
gpio_t* KERNEL_GPIOF_BASE;int mycdev_open(struct inode* inode, struct file* file)
{printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);/* 地址映射 */KERNEL_RCC_GPIO = ioremap(PHY_RCC_GPIO, 4);if (KERNEL_RCC_GPIO == NULL) {printk("ioremap RCC error!\n");return -ENOMEM;}KERNEL_GPIOE_BASE = ioremap(PHY_GPIOE_BASE, sizeof(gpio_t));if (KERNEL_GPIOE_BASE == NULL) {printk("ioremap GPIOE BASE error!\n");return -ENOMEM;}/* 风扇(PE9)寄存器初始化(读,改,写) */writel(readl(KERNEL_RCC_GPIO) | (0x1 << 4), KERNEL_RCC_GPIO); /* GPIOE时钟使能 */writel(readl(&KERNEL_GPIOE_BASE->MODER) & (~(0x3 << 18)), &KERNEL_GPIOE_BASE->MODER); /* MODER寄存器19~18bit清零 */writel(readl(&KERNEL_GPIOE_BASE->MODER) | (0x1 << 18), &KERNEL_GPIOE_BASE->MODER); /* MODER寄存器设置为输出模式 */writel(readl(&KERNEL_GPIOE_BASE->ODR) & (~(0x1 << 9)), &KERNEL_GPIOE_BASE->ODR); /* 风扇熄灭 */return 0;
}ssize_t mycdev_read(struct file* file, char __user* ubuf,size_t size, loff_t* offs)
{int ret;printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);if (size > sizeof(kbuf)) {size = sizeof(kbuf);}ret = copy_to_user(ubuf, kbuf, size);if (ret) {printk("copy data to user error!\n");return -EIO;}return size;
}
ssize_t mycdev_write(struct file* file, const char __user* ubuf,size_t size, loff_t* offs)
{int ret;printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);if (size > sizeof(kbuf)) {size = sizeof(kbuf);}ret = copy_from_user(kbuf, ubuf, size);if (ret) {printk("copy data from user error!\n");return -EIO;}return size;
}long mycdev_ioctl(struct file* file, unsigned int cmd, unsigned long arg)
{switch (cmd) {case FAN_ON:writel(readl(&KERNEL_GPIOE_BASE->ODR) | (0x1 << 9), &KERNEL_GPIOE_BASE->ODR);break;case FAN_OFF:writel(readl(&KERNEL_GPIOE_BASE->ODR) & (~(0x1 << 9)), &KERNEL_GPIOE_BASE->ODR);break;default:break;}return 0;
}int mycdev_close(struct inode* inode, struct file* file)
{printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);return 0;
}struct file_operations fops = {.open = mycdev_open,.read = mycdev_read,.write = mycdev_write,.release = mycdev_close,.unlocked_ioctl = mycdev_ioctl,
};/*** @description: 入口:在安装驱动的时候入口函数执行,做资源申请的工作* demo_init:驱动入口的名字* __init:它是给编译器使用的,告诉编译器将这个函数放在.init.text段中* @return {*}*/
static int __init demo_init(void)
{/* 1. 注册字符设备驱动 */major = register_chrdev(0, CNAME, &fops);if (major < 0) {printk("register char device driver error errorcode = %d!\n", major);return major;}printk("create char device driver successful major = %d!\n", major);/* 2. 向上提交目录 */cls = class_create(THIS_MODULE, CNAME);if (IS_ERR(cls)) {printk("submit directory error\n");return -PTR_ERR(cls);}printk("submit directory ok\n");/* 3. 向上提交设备节点信息 */dev = device_create(cls, NULL, MKDEV(major, 0), NULL, CNAME);if (IS_ERR(dev)) {printk("submit device node information error\n");return -PTR_ERR(dev);}printk("submit device node information ok\n");// printk("init:%s:%s:%d\n", __FILE__, __func__, __LINE__);return 0;
}/*** @description: 出口:在卸载驱动的时候出口函数执行,做资源释放的工作* demo_exit:驱动出口的名字* __exit:它是给编译器使用的,告诉编译器将这个函数放在.exit.text段中* @return {*}*/
static void __exit demo_exit(void)
{/* 取消地址映射 */iounmap(KERNEL_RCC_GPIO);iounmap(KERNEL_GPIOE_BASE);/* 1. 销毁提交设备节点信息 */device_destroy(cls, MKDEV(major, 0));/* 2. 销毁目录 */class_destroy(cls);/* 3. 注销字符设备驱动 */unregister_chrdev(major, CNAME);printk("exit:%s:%s:%d\n", __FILE__, __func__, __LINE__);
}/* 告诉内核入口地址 */
module_init(demo_init);/* 告诉内核出口地址 */
module_exit(demo_exit);/* 许可证:遵从GPL协议 */
MODULE_LICENSE("GPL");
motor.c
/** Copyright (c) 2023 by Huijie Xia, All Rights Reserved.* @Author: Huijie Xia* @Date: 2023-06-29 08:56:26* @LastEditTime: 2023-07-05 16:53:57* @FilePath: /B_Drive/day3/led_beep_fan_motor/motor.c* @version:* @Description: 驱动控制马达*/
#include "gpio.h"
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/uaccess.h>#define CNAME "mymotor"
struct class* cls;
struct device* dev;int major;
char kbuf[128] = { 0 };
unsigned int* KERNEL_RCC_GPIO;
gpio_t* KERNEL_GPIOF_BASE;int mycdev_open(struct inode* inode, struct file* file)
{printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);/* 地址映射 */KERNEL_RCC_GPIO = ioremap(PHY_RCC_GPIO, 4);if (KERNEL_RCC_GPIO == NULL) {printk("ioremap RCC error!\n");return -ENOMEM;}KERNEL_GPIOF_BASE = ioremap(PHY_GPIOF_BASE, sizeof(gpio_t));if (KERNEL_GPIOF_BASE == NULL) {printk("ioremap GPIOB BASE error!\n");return -ENOMEM;}/* 马达(PF6)寄存器初始化(读,改,写) */writel(readl(KERNEL_RCC_GPIO) | (0x1 << 5), KERNEL_RCC_GPIO); /* GPIOB时钟使能 */writel(readl(&KERNEL_GPIOF_BASE->MODER) & (~(0x3 << 12)), &KERNEL_GPIOF_BASE->MODER); /* MODER寄存器13~12bit清零 */writel(readl(&KERNEL_GPIOF_BASE->MODER) | (0x1 << 12), &KERNEL_GPIOF_BASE->MODER); /* MODER寄存器设置为输出模式 */writel(readl(&KERNEL_GPIOF_BASE->ODR) & (~(0x1 << 6)), &KERNEL_GPIOF_BASE->ODR); /* 马达熄灭 */return 0;
}ssize_t mycdev_read(struct file* file, char __user* ubuf,size_t size, loff_t* offs)
{int ret;printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);if (size > sizeof(kbuf)) {size = sizeof(kbuf);}ret = copy_to_user(ubuf, kbuf, size);if (ret) {printk("copy data to user error!\n");return -EIO;}return size;
}
ssize_t mycdev_write(struct file* file, const char __user* ubuf,size_t size, loff_t* offs)
{int ret;printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);if (size > sizeof(kbuf)) {size = sizeof(kbuf);}ret = copy_from_user(kbuf, ubuf, size);if (ret) {printk("copy data from user error!\n");return -EIO;}return size;
}long mycdev_ioctl(struct file* file, unsigned int cmd, unsigned long arg)
{switch (cmd) {case MOTOR_ON:writel(readl(&KERNEL_GPIOF_BASE->ODR) | (0x1 << 6), &KERNEL_GPIOF_BASE->ODR);break;case MOTOR_OFF:writel(readl(&KERNEL_GPIOF_BASE->ODR) & (~(0x1 << 6)), &KERNEL_GPIOF_BASE->ODR);break;default:break;}return 0;
}int mycdev_close(struct inode* inode, struct file* file)
{printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);return 0;
}struct file_operations fops = {.open = mycdev_open,.read = mycdev_read,.write = mycdev_write,.release = mycdev_close,.unlocked_ioctl = mycdev_ioctl,
};/*** @description: 入口:在安装驱动的时候入口函数执行,做资源申请的工作* demo_init:驱动入口的名字* __init:它是给编译器使用的,告诉编译器将这个函数放在.init.text段中* @return {*}*/
static int __init demo_init(void)
{/* 1. 注册字符设备驱动 */major = register_chrdev(0, CNAME, &fops);if (major < 0) {printk("register char device driver error errorcode = %d!\n", major);return major;}printk("create char device driver successful major = %d!\n", major);/* 2. 向上提交目录 */cls = class_create(THIS_MODULE, CNAME);if (IS_ERR(cls)) {printk("submit directory error\n");return -PTR_ERR(cls);}printk("submit directory ok\n");/* 3. 向上提交设备节点信息 */dev = device_create(cls, NULL, MKDEV(major, 0), NULL, CNAME);if (IS_ERR(dev)) {printk("submit device node information error\n");return -PTR_ERR(dev);}printk("submit device node information ok\n");// printk("init:%s:%s:%d\n", __FILE__, __func__, __LINE__);return 0;
}/*** @description: 出口:在卸载驱动的时候出口函数执行,做资源释放的工作* demo_exit:驱动出口的名字* __exit:它是给编译器使用的,告诉编译器将这个函数放在.exit.text段中* @return {*}*/
static void __exit demo_exit(void)
{/* 取消地址映射 */iounmap(KERNEL_RCC_GPIO);iounmap(KERNEL_GPIOF_BASE);/* 1. 销毁提交设备节点信息 */device_destroy(cls, MKDEV(major, 0));/* 2. 销毁目录 */class_destroy(cls);/* 3. 注销字符设备驱动 */unregister_chrdev(major, CNAME);printk("exit:%s:%s:%d\n", __FILE__, __func__, __LINE__);
}/* 告诉内核入口地址 */
module_init(demo_init);/* 告诉内核出口地址 */
module_exit(demo_exit);/* 许可证:遵从GPL协议 */
MODULE_LICENSE("GPL");
test.c
/** Copyright (c) 2023 by Huijie Xia, All Rights Reserved.* @Author: Huijie Xia* @Date: 2023-07-03 14:44:33* @LastEditTime: 2023-07-05 16:59:23* @FilePath: /B_Drive/day3/led_beep_fan_motor/test.c* @version:* @Description: 驱动控制硬件的测试文件*/#include "gpio.h"
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>#define ERR_MSG(msg) \do { \fprintf(stderr, "line : %d\n", __LINE__); \perror(msg); \} while (0)char buf[128] = { 0 };
int main(int argc, const char* argv[])
{int ledfd, beepfd, fanfd, motorfd; /* 文件描述符 */int hardware_which; /* 硬件设备选择 */int choice; /* 设备选择 */int whichled; /* LED选择 *//* 打开LED驱动文件 */if ((ledfd = open("/dev/myled", O_RDWR)) == -1)ERR_MSG("myled open error");/* 打开蜂鸣器驱动文件 */if ((beepfd = open("/dev/mybeep", O_RDWR)) == -1)ERR_MSG("mybeep open error");/* 打开风扇驱动文件 */if ((fanfd = open("/dev/myfan", O_RDWR)) == -1)ERR_MSG("myfan open error");/* 打开马达驱动文件 */if ((motorfd = open("/dev/mymotor", O_RDWR)) == -1)ERR_MSG("mymotor open error");while (1) {printf("请输入你要执行的硬件设备\n");printf("1(LED灯) 2(蜂鸣器) 3(风扇) 4(马达) > ");scanf("%d", &hardware_which);switch (hardware_which) {case 1:printf("请选择要控制的LED\n");printf("1(LED1) 2(LED2) 3(LED3) > ");scanf("%d", &whichled);printf("请选择要实现的功能\n");printf("0(关灯) 1(开灯) > ");scanf("%d", &choice);if (choice == 0) {ioctl(ledfd, LED_OFF, whichled);} else {ioctl(ledfd, LED_ON, whichled);}break;case 2:printf("请选择要实现的蜂鸣器功能\n");printf("0(不响) 1(响) > ");scanf("%d", &choice);if (choice == 0) {ioctl(beepfd, BEEP_OFF);} else {ioctl(beepfd, BEEP_ON);}break;case 3:printf("请选择要实现的风扇功能\n");printf("0(停止) 1(转动) > ");scanf("%d", &choice);if (choice == 0) {ioctl(fanfd, FAN_OFF);} else {ioctl(fanfd, FAN_ON);}break;case 4:printf("请选择要实现的马达功能\n");printf("0(停止) 1(振动) > ");scanf("%d", &choice);if (choice == 0) {ioctl(motorfd, MOTOR_OFF);} else {ioctl(motorfd, MOTOR_ON);}break;default:break;}}close(ledfd);close(beepfd);close(fanfd);close(motorfd);return 0;
}