- 在Linux学习笔记(15.2)中,将LED字符设备驱动程序分为led_drv和led_dev两个文件,其中led_drv提供led_drv_open、led_drv_write、led_drv_release等与应用程序对应的函数,而这些函数在实际执行时要通过函数指针调用底层的led_init、led_exit、led_ctrl等函数完成对LED的控制;而led_dev文件则提供LED的资源和实际对LED操作的函数。
为提升程序的可移植性,可进一步将led_dev文件中LED资源和实际对LED操作的函数分成两个文件——与芯片相关的IO控制文件和LED资源文件,前者提供与芯片相关的LED的控制函数,即某款芯片对IO口的操作,后者只提供LED资源信息。这样,如果更换了芯片,led_drv文件无需更改,需要将与芯片相关的IO控制文件和LED资源文件替换成适合变更芯片的目标板即可;如果只是目前板的LED控制引脚或LED数量发生了变化,则只需要将LED资源文件做相应的调整即可。
本文的LED驱动分别提供了led_drv、imx6_gpio_drv和atk_board_led源文件。 - led_drv文件如下
#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/device.h>
#include <linux/platform_device.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include "led_dev.h"
struct gled_driver {
struct class *drv_class;
char *name;
dev_t major;
struct led_ops *ops;
};
static struct gled_driver gled_drv = {
.name = "gled_drv",
.major = 0,
.ops = NULL,
};
void led_ops_register(struct led_ops *p_ops)
{
if (p_ops)
gled_drv.ops = p_ops;
}
void led_device_create(int minor)
{
device_create(gled_drv.drv_class, NULL, MKDEV(gled_drv.major, minor), NULL, "gled%d", minor);
}
void led_device_destroy(int minor)
{
device_destroy(gled_drv.drv_class, MKDEV(gled_drv.major, minor));
}
EXPORT_SYMBOL(led_ops_register);
EXPORT_SYMBOL(led_device_create);
EXPORT_SYMBOL(led_device_destroy);
static int led_drv_open(struct inode *inode, struct file *filp)
{
int minor = iminor(inode);
struct led_ops *ops = gled_drv.ops;
if (ops == NULL) {
printk("Please register led_ops instance! \n");
return 0;
}
if (ops->led_init)
if (ops->led_init(minor) == 0)
printk("The led%d has openned successfully! \n", minor);
else
printk("The led%d has openned failure! \n", minor);
else
printk("Please register led_ops instance! \n");
return 0;
}
static ssize_t led_drv_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{
return 0;
}
static ssize_t led_drv_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
int ret = 0;
u8 data_buf[1];
u8 led_sta;
int minor;
struct inode *inode = file_inode(filp);
struct led_ops *ops = gled_drv.ops;
if (ops == NULL) {
printk("Please register led_ops instance! \n");
return 0;
}
minor = iminor(inode);
ret = copy_from_user(data_buf, buf, 1);
if (ret < 0) {
printk("kenel recive data: %s\r\n", buf);
return -EFAULT;
}
led_sta = data_buf[0];
if (ops->led_ctrl)
ops->led_ctrl(minor, led_sta);
return 0;
}
static int led_drv_release(struct inode *inode, struct file *filp)
{
u8 minor = iminor(inode);
struct led_ops *ops = gled_drv.ops;
if (ops == NULL) {
printk("Please register led_ops instance! \n");
return 0;
}
if (minor >= ops->num)
return -EINVAL;
if (ops->led_exit)
ops->led_exit(minor);
printk("The led driver is closed! %s, %s, line %d\n", __FILE__, __FUNCTION__, __LINE__);
return 0;
}
static struct file_operations led_drv_fops = {
.owner = THIS_MODULE,
.open = led_drv_open,
.read = led_drv_read,
.write = led_drv_write,
.release = led_drv_release,
};
static int __init led_drv_init(void)
{
u8 err = 0;
gled_drv.major = register_chrdev(gled_drv.major, gled_drv.name, &led_drv_fops);
if (gled_drv.major == 0) {
printk("gled driver: Unable to register driver!\n");
return -EIO;
}
printk("gled driver: Register driver successfully!\n");
gled_drv.drv_class = class_create(THIS_MODULE, "gled");
if (IS_ERR(gled_drv.drv_class)) {
err = PTR_ERR(gled_drv.drv_class);
goto out_chrdev;
}
return 0;
out_chrdev:
unregister_chrdev(gled_drv.major, gled_drv.name);
return err;
}
static void __exit led_drv_exit(void)
{
class_destroy(gled_drv.drv_class);
unregister_chrdev(gled_drv.major, gled_drv.name);
}
module_init(led_drv_init);
module_exit(led_drv_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("glen");
- imx6_gpio_drv文件如下
#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/device.h>
#include <linux/platform_device.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include "led_dev.h"
static struct gled_device gled[16];
static void imx6_gled_ctrl(u8 idx, u8 status);
static int imx6_gled_init(u8 idx);
static int imx6_gled_exit(u8 idx);
static struct led_ops imx6_gpio_led_ops = {
.num = 0,
.led_init = imx6_gled_init,
.led_exit = imx6_gled_exit,
.led_ctrl = imx6_gled_ctrl,
};
static void imx6_gled_ctrl(u8 idx, u8 status)
{
u32 val = 0;
if (gled[idx].vir_io.ccgr == NULL)
return;
if (status == ON) {
val = readl(gled[idx].vir_io.dr);
if (gled[idx].on_level == HIGH) {
val |= (1 << (gled[idx].pin));
} else if (gled[idx].on_level == LOW) {
val &= ~(1 << gled[idx].pin);
}
writel(val, gled[idx].vir_io.dr);
printk("The led%d is openned!\r\n", idx);
} else if (status == OFF) {
val = readl(gled[idx].vir_io.dr);
if (gled[idx].on_level == HIGH) {
val &= ~(1 << gled[idx].pin);
} else if (gled[idx].on_level == LOW) {
val |= (1 << (gled[idx].pin));
}
writel(val, gled[idx].vir_io.dr);
printk("The led%d is closed!\r\n", idx);
} else {
printk("Recived parameter is error!\r\n");
}
}
static int imx6_gled_init(u8 idx)
{
u32 val;
if (idx >= imx6_gpio_led_ops.num) {
return -EIO;
}
gled[idx].vir_io.ccgr = ioremap(gled[idx].phy_io.ccgr , 4);
gled[idx].vir_io.sw_mux = ioremap(gled[idx].phy_io.sw_mux, 4);
gled[idx].vir_io.sw_pad = ioremap(gled[idx].phy_io.sw_pad, 4);
gled[idx].vir_io.dr = ioremap(gled[idx].phy_io.dr , 4);
gled[idx].vir_io.dir = ioremap(gled[idx].phy_io.dir , 4);
val = readl(gled[idx].vir_io.ccgr);
val |= (3 << gled[idx].clk_shft_bits);
writel(val, gled[idx].vir_io.ccgr);
writel(5, gled[idx].vir_io.sw_mux);
writel(0x10B0, gled[idx].vir_io.sw_pad);
val = readl(gled[idx].vir_io.dir);
val |= (1 << gled[idx].pin);
writel(val, gled[idx].vir_io.dir);
val = readl(gled[idx].vir_io.dr);
if (gled[idx].dft_status == ON) {
if (gled[idx].on_level == HIGH) {
val |= (1 << gled[idx].pin);
} else if (gled[idx].on_level == LOW) {
val &= ~(1 << gled[idx].pin);
}
} else if (gled[idx].dft_status == OFF) {
if (gled[idx].on_level == HIGH) {
val &= ~(1 << gled[idx].pin);
} else if (gled[idx].on_level == LOW) {
val |= (1 << gled[idx].pin);
}
}
writel(val, gled[idx].vir_io.dr);
return 0;
}
static int imx6_gled_exit(u8 idx)
{
if (idx >= imx6_gpio_led_ops.num) {
return -EIO;
}
iounmap(gled[idx].vir_io.ccgr);
iounmap(gled[idx].vir_io.sw_mux);
iounmap(gled[idx].vir_io.sw_pad);
iounmap(gled[idx].vir_io.dr);
iounmap(gled[idx].vir_io.dir);
return 0;
}
static int imx6_gpio_probe(struct platform_device *pdev)
{
struct resource *res = NULL;
u8 i = 0;
while (1) {
res = platform_get_resource(pdev, IORESOURCE_IRQ, i);
if (res == NULL) {
break;
}
memcpy(&gled[i], (const void *)(res->start), sizeof(struct gled_device));
led_device_create(i);
i++;
}
imx6_gpio_led_ops.num = i;
return 0;
}
static int imx6_gpio_remove(struct platform_device *pdev)
{
u8 i;
for (i = 0; i < imx6_gpio_led_ops.num; i++) {
led_device_destroy(i);
}
imx6_gpio_led_ops.num = 0;
return 0;
}
static struct platform_driver imx6_gpio_driver = {
.driver = {
.name = "gled",
},
.probe = imx6_gpio_probe,
.remove = imx6_gpio_remove,
};
static int __init imx6_gpio_drv_init(void)
{
int ret;
ret = platform_driver_register(&imx6_gpio_driver);
if (ret)
pr_err("Unable to initialize imx6 gpio driver\n");
else
pr_info("The imx6 gpio driver is registered.\n");
led_ops_register(&imx6_gpio_led_ops);
return ret;
}
static void __exit imx6_gpio_drv_exit(void)
{
platform_driver_unregister(&imx6_gpio_driver);
}
module_init(imx6_gpio_drv_init);
module_exit(imx6_gpio_drv_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("glen");
- atk_board_led文件如下
#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/device.h>
#include <linux/platform_device.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include "led_dev.h"
static struct gled_device gled_dev[] = {
{
.dft_status = OFF,
.on_level = LOW,
.clk_shft_bits = 26,
.pin = 3,
.phy_io = {
.ccgr = 0x020C406C,
.sw_mux = 0x020E0068,
.sw_pad = 0x020E02F4,
.dr = 0x0209C000,
.dir = 0x0209C004
},
}, {
.dft_status = ON,
.on_level = LOW,
.clk_shft_bits = 30,
.pin = 1,
.phy_io = {
.ccgr = 0x020C406C,
.sw_mux = 0x0229000C,
.sw_pad = 0x02290050,
.dr = 0x020AC000,
.dir = 0x020AC004
},
}
};
static struct resource led_res[] = {
{
.start = (resource_size_t)&gled_dev[0],
.flags = IORESOURCE_IRQ,
.name = "atk_led_pin",
}, {
.start = (resource_size_t)&gled_dev[1],
.flags = IORESOURCE_IRQ,
.name = "atk_led_pin",
},
};
static struct platform_device led_dev = {
.name = "gled",
.num_resources = ARRAY_SIZE(led_res),
.resource = led_res,
};
static int __init led_dev_init(void)
{
return platform_device_register(&led_dev);
}
module_init(led_dev_init);
static void __exit led_dev_exit(void)
{
platform_device_unregister(&led_dev);
}
module_exit(led_dev_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("glen");
- led_dev头文件
#ifndef __LED_DEV_H__
#define __LED_DEV_H__
#include <linux/types.h>
#define HIGH 1
#define LOW 0
#define OFF 'C'
#define ON 'O'
struct led_ops {
u8 num;
int (* led_init)(u8 idx);
int (* led_exit)(u8 idx);
void (* led_ctrl)(u8 idx, u8 status);
};
struct phy_regs {
phys_addr_t ccgr;
phys_addr_t sw_mux;
phys_addr_t sw_pad;
phys_addr_t dr;
phys_addr_t dir;
};
struct vir_regs {
void __iomem *ccgr;
void __iomem *sw_mux;
void __iomem *sw_pad;
void __iomem *dr;
void __iomem *dir;
};
struct gled_device {
u8 dft_status;
u8 on_level;
u8 clk_shft_bits;
u32 pin;
struct vir_regs vir_io;
struct phy_regs phy_io;
};
void led_ops_register(struct led_ops *p_ops);
void led_device_create(int minor);
void led_device_destroy(int minor);
#endif
- Makefile文件稍作更改,将三个源文件编译成独立的模块
KERNELDIR := /home/glen/linux/imx6ull/linux/glen_linux
CURRENT_PATH := $(shell pwd)
#led_driver-y := led_drv.o led_dev.o
obj-m += led_drv.o atk_board_led.o imx6_gpio_drv.o
build: kernel_modules
kernel_modules:
$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules
clean:
$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean
- 测试程序不做更改,验证参考Linux学习笔记(15)——LED设备驱动