device tree:
/* 添加 led 节点 */
led {
#address-cells = <1>;
#size-cells = <1>;
compatible = "s5pv210, led";
ranges; /* 这个非常重要,必须添加,不然of_iomap就不能成功*/
/* 蓝灯子节点 */
blue_led@0xe0200240 {
compatible = "s5pv210, blue_led";
reg = <
0xe0200240 0x4
0xe0200244 0x4
>;
status = "okay";
pin = <0x3>;
};
/* 黄灯子节点 */
yellow_led@0xe0200240 {
compatible = "s5pv210, yellow_led";
reg = <
0xe0200240 0x4
0xe0200244 0x4
>;
status = "okay";
pin = <0x4>;
};
/* 红灯子节点 */
red_led@0xe0200240 {
compatible = "s5pv210, red_led";
reg = <
0xe0200240 0x4
0xe0200244 0x4
>;
status = "okay";
pin = <0x5>;
};
驱动程序:
#include <linux/module.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <asm/io.h>
#include <linux/types.h>
#include <linux/cdev.h>
#include <linux/fs.h>
/* dts */
#include <linux/of.h>
#include <linux/of_address.h>
#define DEV_CNT (1)
#define DEV_NAME "s5pv210_led"
struct led_ctrl
{
void __iomem * blue_led_reg_con;
void __iomem * blue_led_reg_dat;
int blue_led_pin;
void __iomem * yellow_led_reg_con;
void __iomem * yellow_led_reg_dat;
int yellow_led_pin;
void __iomem * red_led_reg_con;
void __iomem * red_led_reg_dat;
int red_led_pin;
/* char device driver*/
dev_t dev_id;
struct cdev led_chr_dev;
struct class * led_class;
struct device * led_device;
};
static struct led_ctrl g_led_ctrl = {0};
static int led_chr_dev_open(struct inode *inode, struct file *filp)
{
printk(KERN_INFO "open driver\n");
return 0;
}
int led_chr_dev_release(struct inode *inode, struct file *filp)
{
printk(KERN_INFO "release driver\n");
return 0;
}
static ssize_t led_chr_dev_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
int ret = -1;
unsigned int user_data = 0;
unsigned int reg_data = 0;
ret = copy_from_user(&user_data, buf, cnt);
if (ret < 0)
{
printk(KERN_INFO "copy_from_user error\n");
return -EINVAL;
}
/* bit[0:7]: blue_led, bit[8:15]: yellow led, bit[16:23]: red led*/
if (user_data & 0xff)
{
reg_data = readl(g_led_ctrl.blue_led_reg_dat);
reg_data &= ~(0x1 << g_led_ctrl.blue_led_pin );
writel(reg_data, g_led_ctrl.blue_led_reg_dat);
}
else
{
reg_data = readl(g_led_ctrl.blue_led_reg_dat);
reg_data &= ~(0x1 << g_led_ctrl.blue_led_pin );
reg_data |= (0x1 << g_led_ctrl.blue_led_pin);
writel(reg_data, g_led_ctrl.blue_led_reg_dat);
}
if ((user_data >> 8) & 0xff)
{
reg_data = readl(g_led_ctrl.yellow_led_reg_dat);
reg_data &= ~(0x1 << g_led_ctrl.yellow_led_pin );
writel(reg_data, g_led_ctrl.yellow_led_reg_dat);
}
else
{
reg_data = readl(g_led_ctrl.yellow_led_reg_dat);
reg_data &= ~(0x1 << g_led_ctrl.yellow_led_pin );
reg_data |= (0x1 << g_led_ctrl.yellow_led_pin);
writel(reg_data, g_led_ctrl.yellow_led_reg_dat);
}
if ((user_data >> 16) & 0xff)
{
reg_data = readl(g_led_ctrl.red_led_reg_dat);
reg_data &= ~(0x1 << g_led_ctrl.red_led_pin );
writel(reg_data, g_led_ctrl.red_led_reg_dat);
}
else
{
reg_data = readl(g_led_ctrl.red_led_reg_dat);
reg_data &= ~(0x1 << g_led_ctrl.red_led_pin );
reg_data |= (0x1 << g_led_ctrl.red_led_pin);
writel(reg_data, g_led_ctrl.red_led_reg_dat);
}
return 0;
}
/*字符设备操作函数集*/
static struct file_operations led_chr_dev_fops =
{
.owner = THIS_MODULE,
.open = led_chr_dev_open,
.write = led_chr_dev_write,
};
static int s5pv210_led_probe(struct platform_device * device)
{
int ret = -1;
unsigned int reg_data = 0;
struct device_node * s5pv210_led = NULL;
struct device_node * s5pv210_led_blue = NULL;
struct device_node * s5pv210_led_yellow = NULL;
struct device_node * s5pv210_led_red = NULL;
/*
find led in /
*/
s5pv210_led = of_find_node_by_path("/led");
if (NULL == s5pv210_led)
{
printk(KERN_INFO "of_find_node_by_path error\n");
goto blue_led_reg_con_err;
}
/*
child blue_led
*/
s5pv210_led_blue = of_find_node_by_name(s5pv210_led, "blue_led");
if (NULL == s5pv210_led_blue)
{
printk(KERN_INFO "of_find_node_by_name error\n");
goto blue_led_reg_con_err;
}
ret = of_property_read_u32(s5pv210_led_blue, "pin", &(g_led_ctrl.blue_led_pin));
if (ret)
{
printk(KERN_ERR "of_property_read_u32 error\n");
goto blue_led_reg_con_err;
}
g_led_ctrl.blue_led_reg_con = of_iomap(s5pv210_led_blue, 0);
if (NULL == g_led_ctrl.blue_led_reg_con)
{
printk(KERN_INFO "of_iomap blue_led_reg_con error\n");
goto blue_led_reg_con_err;
}
g_led_ctrl.blue_led_reg_dat = of_iomap(s5pv210_led_blue, 1);
if (NULL == g_led_ctrl.blue_led_reg_dat)
{
printk(KERN_INFO "of_iomap blue_led_reg_dat error\n");
goto blue_led_reg_dat_err;
}
reg_data = readl(g_led_ctrl.blue_led_reg_con);
reg_data &= ~(0xf << (g_led_ctrl.blue_led_pin * 4));
reg_data |= (0x1 << (g_led_ctrl.blue_led_pin * 4));
writel(reg_data, g_led_ctrl.blue_led_reg_con);
reg_data = readl(g_led_ctrl.blue_led_reg_dat);
reg_data &= ~(0x1 << g_led_ctrl.blue_led_pin );
reg_data |= (0x1 << g_led_ctrl.blue_led_pin);
writel(reg_data, g_led_ctrl.blue_led_reg_dat);
/*
child yellow_led
*/
s5pv210_led_yellow = of_find_node_by_name(s5pv210_led, "yellow_led");
if (NULL == s5pv210_led_yellow)
{
printk(KERN_INFO "of_find_node_by_name error\n");
goto blue_led_reg_dat_err;
}
ret = of_property_read_u32(s5pv210_led_yellow, "pin",
&(g_led_ctrl.yellow_led_pin));
if (ret)
{
printk(KERN_ERR "of_property_read_u32 error\n");
goto blue_led_reg_dat_err;
}
g_led_ctrl.yellow_led_reg_con = of_iomap(s5pv210_led_yellow, 0);
if (NULL == g_led_ctrl.yellow_led_reg_con)
{
printk(KERN_INFO "of_iomap yellow_led_reg_con error\n");
goto yellow_led_reg_con_err;
}
g_led_ctrl.yellow_led_reg_dat = of_iomap(s5pv210_led_yellow, 1);
if (NULL == g_led_ctrl.yellow_led_reg_dat)
{
printk(KERN_INFO "of_iomap yellow_led_reg_dat error\n");
goto yellow_led_reg_dat_err;
}
reg_data = readl(g_led_ctrl.yellow_led_reg_con);
reg_data &= ~(0xf << (g_led_ctrl.yellow_led_pin * 4));
reg_data |= (0x1 << (g_led_ctrl.yellow_led_pin * 4));
writel(reg_data, g_led_ctrl.yellow_led_reg_con);
reg_data = readl(g_led_ctrl.yellow_led_reg_dat);
reg_data &= ~(0x1 << g_led_ctrl.yellow_led_pin );
reg_data |= (0x1 << g_led_ctrl.yellow_led_pin);
writel(reg_data, g_led_ctrl.yellow_led_reg_dat);
/*
child red_led
*/
s5pv210_led_red = of_find_node_by_name(s5pv210_led, "red_led");
if (NULL == s5pv210_led_red)
{
printk(KERN_INFO "of_find_node_by_name error\n");
goto yellow_led_reg_dat_err;
}
ret = of_property_read_u32(s5pv210_led_red, "pin",
&(g_led_ctrl.red_led_pin));
if (ret)
{
printk(KERN_ERR "of_property_read_u32 error\n");
goto yellow_led_reg_dat_err;
}
g_led_ctrl.red_led_reg_con = of_iomap(s5pv210_led_red, 0);
if (NULL == g_led_ctrl.red_led_reg_con)
{
printk(KERN_INFO "of_iomap red_led_reg_con error\n");
goto red_led_reg_con_err;
}
g_led_ctrl.red_led_reg_dat = of_iomap(s5pv210_led_red, 1);
if (NULL == g_led_ctrl.red_led_reg_dat)
{
printk(KERN_INFO "of_iomap red_led_reg_con error\n");
goto red_led_reg_dat_err;
}
reg_data = readl(g_led_ctrl.red_led_reg_con);
reg_data &= ~(0xf << (g_led_ctrl.red_led_pin * 4));
reg_data |= (0x1 << (g_led_ctrl.red_led_pin * 4));
writel(reg_data, g_led_ctrl.red_led_reg_con);
reg_data = readl(g_led_ctrl.red_led_reg_dat);
reg_data &= ~(0x1 << g_led_ctrl.red_led_pin );
reg_data |= (0x1 << g_led_ctrl.red_led_pin);
writel(reg_data, g_led_ctrl.red_led_reg_dat);
/* char device driver*/
ret = alloc_chrdev_region(&g_led_ctrl.dev_id, 0, DEV_CNT, DEV_NAME);
if (ret)
{
printk(KERN_INFO "alloc_chrdev_region error\n");
goto red_led_reg_dat_err;
}
g_led_ctrl.led_chr_dev.owner = THIS_MODULE;
cdev_init(&g_led_ctrl.led_chr_dev, &led_chr_dev_fops);
ret = cdev_add(&g_led_ctrl.led_chr_dev, g_led_ctrl.dev_id, DEV_CNT);
if (ret)
{
printk(KERN_INFO "cdev_add error\n");
goto cdev_add_err;
}
g_led_ctrl.led_class = class_create(THIS_MODULE, DEV_NAME);
if (NULL == g_led_ctrl.led_class)
{
printk(KERN_INFO "class_create error\n");
goto class_create_err;
}
g_led_ctrl.led_device = device_create(g_led_ctrl.led_class, NULL, g_led_ctrl.dev_id, NULL, DEV_NAME);
if (NULL == g_led_ctrl.led_device)
{
printk(KERN_INFO "device_create error\n");
goto device_create_err;
}
return 0;
device_create_err:
class_destroy(g_led_ctrl.led_class);
class_create_err:
cdev_del(&g_led_ctrl.led_chr_dev);
cdev_add_err:
unregister_chrdev_region(g_led_ctrl.dev_id, DEV_CNT);
red_led_reg_dat_err:
iounmap(g_led_ctrl.red_led_reg_con);
red_led_reg_con_err:
iounmap(g_led_ctrl.yellow_led_reg_dat);
yellow_led_reg_dat_err:
iounmap(g_led_ctrl.yellow_led_reg_con);
yellow_led_reg_con_err:
iounmap(g_led_ctrl.blue_led_reg_dat);
blue_led_reg_dat_err:
iounmap(g_led_ctrl.blue_led_reg_con);
blue_led_reg_con_err:
return -EINVAL;
}
static int s5pv210_led_remove(struct platform_device * device)
{
device_destroy(g_led_ctrl.led_class, g_led_ctrl.dev_id);
class_destroy(g_led_ctrl.led_class);
cdev_del(&g_led_ctrl.led_chr_dev);
unregister_chrdev_region(g_led_ctrl.dev_id, DEV_CNT);
iounmap(g_led_ctrl.blue_led_reg_con);
iounmap(g_led_ctrl.blue_led_reg_dat);
iounmap(g_led_ctrl.yellow_led_reg_con);
iounmap(g_led_ctrl.yellow_led_reg_dat);
iounmap(g_led_ctrl.red_led_reg_con);
iounmap(g_led_ctrl.red_led_reg_dat);
return 0;
}
static const struct of_device_id s5pv210_led_of_match_table[] =
{
{.compatible = "s5pv210, led",},
{},
};
static struct platform_driver s5pv210_led_driver =
{
.probe = s5pv210_led_probe,
.remove = s5pv210_led_remove,
.driver = {
.name = "s5pv210_led",
.owner = THIS_MODULE,
.of_match_table = s5pv210_led_of_match_table,
}
};
// 模块安装函数
static int __init chrdev_init(void)
{
int ret = platform_driver_register(&s5pv210_led_driver);
if (ret)
{
printk(KERN_INFO "platform_driver_register fail\n");
return -EINVAL;
}
printk(KERN_INFO "platform_driver_register ok\n");
return 0;
}
// 模块卸载函数
static void __exit chrdev_exit(void)
{
platform_driver_unregister(&s5pv210_led_driver);
printk(KERN_INFO "platform_driver_unregister\n");
}
module_init(chrdev_init);
module_exit(chrdev_exit);
// MODULE_xxx这种宏作用是用来添加模块描述信息
MODULE_LICENSE("GPL"); // 描述模块的许可证
MODULE_AUTHOR("Mark"); // 描述模块的作者
MODULE_DESCRIPTION("test for driver"); // 描述模块的介绍信息
MODULE_ALIAS("alias test"); // 描述模块的别名信息
应用程序:
#include <iostream>
#include <thread>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#define ON (true)
#define OFF (false)
#define DEVICE_NAME "/dev/s5pv210_led"
class Led
{
public:
Led(int on_off);
~Led(void);
void ctrlSubBlueLed(int on_off);
void ctrlSubYellowLed(int on_off);
void ctrlSubRedLed(int on_off);
void ctrlAllLed(int on_off);
private:
unsigned char subBlueStatus;
unsigned char subYellowStatus;
unsigned char subRedStatus;
int fd;
};
Led::Led(int on_off) : fd(0)
{
unsigned int status = 0;
subBlueStatus = on_off;
subYellowStatus = on_off;
subRedStatus = on_off;
fd = open(DEVICE_NAME, O_RDWR);
if (fd < 0)
{
std::cout << DEVICE_NAME << "open fail\n";
return;
}
status = (subBlueStatus) | (subYellowStatus << 8) | (subRedStatus << 16);
write(fd, &status, sizeof(status));
}
Led::~Led(void)
{
unsigned int status = 0;
if (0 != fd)
{
write(fd, &status, sizeof(status));
close(fd);
}
}
void Led::ctrlSubBlueLed(int on_off)
{
unsigned int status = 0;
subBlueStatus = on_off;
status |= subBlueStatus;
write(fd, &status, sizeof(status));
}
void Led::ctrlSubYellowLed(int on_off)
{
unsigned int status = 0;
subYellowStatus = on_off;
status |= (subYellowStatus << 8);
write(fd, &status, sizeof(status));
}
void Led::ctrlSubRedLed(int on_off)
{
unsigned int status = 0;
subRedStatus = on_off;
status |= (subRedStatus << 16);
write(fd, &status, sizeof(status));
}
void Led::ctrlAllLed(int on_off)
{
unsigned int status = 0;
subBlueStatus = on_off;
subYellowStatus = on_off;
subRedStatus = on_off;
status = (subBlueStatus) | (subYellowStatus << 8) | (subRedStatus << 16);
write(fd, &status, sizeof(status));
}
void ledShowTask(void)
{
Led ledCtrlObj(OFF);
std::string cmd = "";
while (1)
{
ledCtrlObj.ctrlAllLed(ON);
usleep(500 * 1000);
ledCtrlObj.ctrlAllLed(OFF);
usleep(500 * 1000);
ledCtrlObj.ctrlSubBlueLed(ON);
usleep(500 * 1000);
ledCtrlObj.ctrlSubBlueLed(OFF);
usleep(500 * 1000);
ledCtrlObj.ctrlSubYellowLed(ON);
usleep(500 * 1000);
ledCtrlObj.ctrlSubYellowLed(OFF);
usleep(500 * 1000);
ledCtrlObj.ctrlSubRedLed(ON);
usleep(500 * 1000);
ledCtrlObj.ctrlSubRedLed(OFF);
usleep(500 * 1000);
}
}
int main(int argc, char const *argv[])
{
std::thread ledShow(ledShowTask);
ledShow.join();
return 0;
}