一. 简介
上一篇文章学习编写新字符设备驱动框架代码。文章地址如下:
新字符设备驱动框架代码搭建-CSDN博客
本文在以上这篇文章代码实现的基础上,加入涉及 Led灯的功能函数集实现。
代码实现要求:测试程序(即应用程序)通过设置 Led灯电平的高低,调用到 Led设备驱动程序,从而最终驱动 Led灯的亮灭。
二. 添加Led功能函数
上一篇文章实现了新字符设备驱动代码框架。这里在此基础上加入 Led灯的功能函数的代码。
首先,来完善字符设备Led 的 file_operations结构体,完善后如下:
//字符设备的函数集
const struct file_operations fops = { .owner = THIS_MODULE,.open = led_open,.write = led_write,.release = led_release,
};
其次,来实现 上面字符设备的 file_operations结构体对应的函数实现。可以参考前面 2_led实验的代码编写,其实代码内容是一样的。
所添加的代码内容包括:file_operations结构体的函数集,Led灯的IO初始化工作。
(1) 添加file_operations结构体的函数操作集
打开 3_newchrled工程, 这里需要添加 file_operations结构体的函数操作集。代码如下:
void led_on_off(unsigned int status)
{unsigned int reg_value = 0;if(LED_ON == status) {reg_value = readl(IMX6ULL_DR);reg_value &= ~(1 << 3); //bit3清零,低电平,打开Ledwritel(reg_value, IMX6ULL_DR);}else if(LED_OFF == status){reg_value = readl(IMX6ULL_DR);reg_value |= (1 << 3); //bit3置1,高电平,关闭Ledwritel(reg_value, IMX6ULL_DR);}
}static int newchrled_open(struct inode *inode, struct file *file)
{return 0;
}static ssize_t newchrled_write(struct file * inode, const char __user * buf, size_t count, loff_t * ppos)
{int ret = 0;unsigned char data_buf[1] = {0};ret = copy_from_user(data_buf, buf, count);if(ret < 0){printk("led_write failed!\r\n");return -EFAULT;}led_on_off(data_buf[0]);return 0;
}int newchrled_release(struct inode * inode, struct file * file)
{printk("led_release\r\n");return 0;
}
(2) 添加 Led的IO初始化代码
这里 Led的IO初始化代码,可以添加到 newchrled_init()函数,也就是模块加载接口中;也可以添加到 newchrled_open()函数中,也就是打开设备接口中。
这里我添加到 newchrled_init()接口中,将以下 Led 的IO初始化代码添加到 newchrled_init()接口 中字符设备注册之前:
unsigned int value = 0;/*Led灯的IO初始化 *///地址映射IMX6ULL_CCM_CCGR1 = ioremap(CCM_CCGR1_BASE, 4);IMX6ULL_SW_MUX_GPIO1_IO03 = ioremap(SW_MUX_GPIO1_IO03_BASE, 4);IMX6ULL_SW_PAD_GPIO01_IO03 = ioremap(SW_PAD_GPIO1_IO03_BASE, 4);IMX6ULL_GDIR = ioremap(GPIO1_GDIR_BASE, 4);IMX6ULL_DR = ioremap(GPIO1_DR_BASE, 4);//使能Led时钟value = readl(IMX6ULL_CCM_CCGR1);value &= ~(3 << 26);value |= (3 << 26); //bit26~27置1writel(value, IMX6ULL_CCM_CCGR1);//复用为GPIO功能writel(0X05, IMX6ULL_SW_MUX_GPIO1_IO03);//配置电气属性writel(0X10B0, IMX6ULL_SW_PAD_GPIO01_IO03);//设置为输出功能value = readl(IMX6ULL_GDIR);value |= (1 << 3);writel(value, IMX6ULL_GDIR);//关闭Led灯(设置为高电平)value = readl(IMX6ULL_DR);value |= (1 << 3);writel(value, IMX6ULL_DR);
三. 整体代码实现
newchrled.c文件中代码如下:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/cdev.h>//寄存器的物理地址
#define CCM_CCGR1_BASE (0X020C406C)
#define SW_MUX_GPIO1_IO03_BASE (0X020E0068)
#define SW_PAD_GPIO1_IO03_BASE (0X020E02F4)
#define GPIO1_GDIR_BASE (0X0209C004)
#define GPIO1_DR_BASE (0X0209C000)//地址映射后寄存器的虚拟地址指针
static void __iomem * IMX6ULL_CCM_CCGR1;
static void __iomem * IMX6ULL_SW_MUX_GPIO1_IO03;
static void __iomem * IMX6ULL_SW_PAD_GPIO01_IO03;
static void __iomem * IMX6ULL_GDIR;
static void __iomem * IMX6ULL_DR;#define NEWCHRLED "newchrled" //设备名
#define NEWCHRLED_COUNT 1 //设备个数
#define LED_OFF 0 //关闭Led灯
#define LED_ON 1 //打开Led灯//Led设备结构体
struct newchrled_dev{struct cdev led_cdev; dev_t dev_id; //设备号int major; //主设备号int minor; //次设备号 };struct newchrled_dev newchr_led;void led_on_off(unsigned int status)
{unsigned int reg_value = 0;if(LED_ON == status) {reg_value = readl(IMX6ULL_DR);reg_value &= ~(1 << 3); //bit3清零,低电平,打开Ledwritel(reg_value, IMX6ULL_DR);}else if(LED_OFF == status){reg_value = readl(IMX6ULL_DR);reg_value |= (1 << 3); //bit3置1,高电平,关闭Ledwritel(reg_value, IMX6ULL_DR);}
}static int newchrled_open(struct inode *inode, struct file *file)
{return 0;
}static ssize_t newchrled_write(struct file * inode, const char __user * buf, size_t count, loff_t * ppos)
{int ret = 0;unsigned char data_buf[1] = {0};ret = copy_from_user(data_buf, buf, count);if(ret < 0){printk("led_write failed!\r\n");return -EFAULT;}led_on_off(data_buf[0]);return 0;
}int newchrled_release(struct inode * inode, struct file * file)
{printk("led_release\r\n");return 0;
}//字符设备的函数集
const struct file_operations fops = { .owner = THIS_MODULE,.open = newchrled_open,.write = newchrled_write,.release = newchrled_release,
};/*驱动模块入口函数 */
static int __init newchrled_init(void)
{int ret = 0;unsigned int value = 0;printk("newchrled_init!\r\n");/*Led灯的IO初始化 *///地址映射IMX6ULL_CCM_CCGR1 = ioremap(CCM_CCGR1_BASE, 4);IMX6ULL_SW_MUX_GPIO1_IO03 = ioremap(SW_MUX_GPIO1_IO03_BASE, 4);IMX6ULL_SW_PAD_GPIO01_IO03 = ioremap(SW_PAD_GPIO1_IO03_BASE, 4);IMX6ULL_GDIR = ioremap(GPIO1_GDIR_BASE, 4);IMX6ULL_DR = ioremap(GPIO1_DR_BASE, 4);//使能Led时钟value = readl(IMX6ULL_CCM_CCGR1);value &= ~(3 << 26);value |= (3 << 26); //bit26~27置1writel(value, IMX6ULL_CCM_CCGR1);//复用为GPIO功能writel(0X05, IMX6ULL_SW_MUX_GPIO1_IO03);//配置电气属性writel(0X10B0, IMX6ULL_SW_PAD_GPIO01_IO03);//设置为输出功能value = readl(IMX6ULL_GDIR);value |= (1 << 3);writel(value, IMX6ULL_GDIR);//关闭Led灯(设置为高电平)value = readl(IMX6ULL_DR);value |= (1 << 3);writel(value, IMX6ULL_DR);/*注册字符设备 *///设备号的分配if(newchr_led.major) //给定主设备号{newchr_led.dev_id = MKDEV(newchr_led.major, 0);ret = register_chrdev_region(newchr_led.dev_id, NEWCHRLED_COUNT, NEWCHRLED);}else{ //没有给定主设备号ret = alloc_chrdev_region(&(newchr_led.dev_id), 0, NEWCHRLED_COUNT, NEWCHRLED); newchr_led.major = MAJOR(newchr_led.dev_id);newchr_led.minor = MINOR(newchr_led.dev_id);}if (ret < 0) {printk("register-chrdev failed!\n");return -1;}//初始化设备cdev_init(&newchr_led.led_cdev, &fops);//注册设备cdev_add(&newchr_led.led_cdev, newchr_led.dev_id, NEWCHRLED_COUNT);return 0;
}/*驱动模块出口函数 */
static void __exit newchrled_exit(void)
{printk("newchrled_eixt!\r\n");
/*注销字符设备 *///删除设备cdev_del(&newchr_led.led_cdev);//注销设备号unregister_chrdev_region(newchr_led.dev_id, NEWCHRLED_COUNT);
}/* 注册与卸载驱动设备 */
module_init(newchrled_init);
module_exit(newchrled_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("LingXueWu");
编译驱动代码 3_newchrled,经过编译,可以正常生成 newchrled.ko 驱动文件。下一篇文章对驱动代码进行加载与测试。