Linux 字符设备驱动实例
/** LEDs driver for GPIOs** Copyright (C) 2007 8D Technologies inc.* Raphael Assenat* Copyright (C) 2008 Freescale Semiconductor, Inc.** This program is free software; you can redistrib
·
/*
* LEDs driver for GPIOs
*
* Copyright (C) 2007 8D Technologies inc.
* Raphael Assenat <raph@8d.com>
* Copyright (C) 2008 Freescale Semiconductor, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/of_platform.h>
#include <linux/cdev.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <asm/fsl_lbc.h>
#define DEV_NAME "di8"
struct lbc_di_of_platform_data
{
struct device *pdev;
void __iomem *vbase; /* Chip select base virtual address */
dev_t dev_no;
struct cdev di_cdev;
};
static struct lbc_di_of_platform_data *pPlatform_data;
static int didev_open(struct inode *inode, struct file *file)
{
file->private_data = pPlatform_data;
return 0;
}
static int didev_release(struct inode *inode, struct file *file) {
return 0;
}
static ssize_t didev_read(struct file * file, char __user * buf, size_t size, loff_t *ppos)
{
struct lbc_di_of_platform_data *pdata =
(struct lbc_di_of_platform_data *)(file->private_data);
char result = in_8(pdata->vbase);
result = ~result;
if(size == 0) return 0;
if(copy_to_user(buf,&result,1)) //copy_to_user: if success return 0;else return the number not copied
return 0;
else
return 1;
}
static ssize_t didev_write(struct file *file, const char __user *buf, size_t size, loff_t *ppos)
{
return 0;
}
static struct file_operations didev_fops = {
.owner = THIS_MODULE,
.open = didev_open,
.read = didev_read,
.write = didev_write,
.release = didev_release,
};
static int lbc_di_add_cdev(struct lbc_di_of_platform_data *pdata)
{
int ret,add_ret;
struct class *dev_class = NULL;
struct device *dev_device = NULL;
pdata->dev_no = 0;
ret = alloc_chrdev_region(&pdata->dev_no, 0, 1, DEV_NAME);
if (ret)
{
dev_err(pdata->pdev, "alloc_chrdev_region failed!\n");
return ret;
}
cdev_init(&pdata->di_cdev, &didev_fops);
pdata->di_cdev.owner = THIS_MODULE;
// add a character device
add_ret = cdev_add(&pdata->di_cdev, pdata->dev_no, 1);
if (add_ret)
{
dev_err(pdata->pdev, " cdev_add failed!\n");
goto PROBE_ERR;
}
// create the device class
dev_class = class_create(THIS_MODULE, DEV_NAME);
if (IS_ERR(dev_class))
{
dev_err(pdata->pdev, " class_create failed!\n");
goto PROBE_ERR;
}
// create the device node in /dev
dev_device = device_create(dev_class, NULL, pdata->dev_no,"%s", DEV_NAME);
if (NULL == dev_device)
{
dev_err(pdata->pdev, " device_create failed!\n");
goto PROBE_ERR;
}
dev_info(pdata->pdev, " cdev %s add ok , MAJOR:%d MINOR:%d !\n",
DEV_NAME,MAJOR(pdata->dev_no),MINOR(pdata->dev_no));
return 0;
PROBE_ERR:
if (dev_device)
device_destroy(dev_class, pdata->dev_no);
if (!IS_ERR(dev_class))
class_destroy(dev_class);
if(!add_ret)
cdev_del(&pdata->di_cdev);
if(pdata->dev_no)
unregister_chrdev_region(pdata->dev_no,1);
return -1;
}
static int __devinit of_lbc_di_probe(struct of_device *ofdev,
const struct of_device_id *match)
{
struct device_node *np = ofdev->node;
struct lbc_di_of_platform_data *pdata;
struct resource res;
int ret;
/* get, allocate and map the memory resource */
ret = of_address_to_resource(np , 0, &res);
if (ret) {
dev_err(&ofdev->dev, "failed to get resource\n");
return ret;
}
dev_info(&ofdev->dev, "LBC resource:0x%X -- 0x%X\n",res.start,res.end);
pdata= kzalloc(sizeof(*pdata), GFP_KERNEL);
if (!pdata)
return -ENOMEM;
pdata->pdev = &ofdev->dev;
pdata->vbase = ioremap(res.start, res.end - res.start +1);
if (!pdata->vbase)
{
dev_err(pdata->pdev, "failed to map chip region\n");
ret = -ENOMEM;
goto err;
}
ret = lbc_di_add_cdev(pdata);
if(ret)
{
dev_err(pdata->pdev, "failed to add_cdev.\n");
goto err;
}
dev_set_drvdata(&ofdev->dev, pdata);
pPlatform_data = pdata;
return 0;
err:
if(pdata->vbase)
iounmap(pdata->vbase);
kfree(pdata);
return ret;
}
static int __devexit of_lbc_di_remove(struct of_device *ofdev)
{
return 0;
}
static const struct of_device_id of_lbc_di_match[] = {
{ .compatible = "kt,di8", },
{},
};
static struct of_platform_driver of_lbc_di_driver = {
.driver = {
.name = "lbc_di",
.owner = THIS_MODULE,
},
.match_table = of_lbc_di_match,
.probe = of_lbc_di_probe,
.remove = __devexit_p(of_lbc_di_remove),
};
static int __init lbc_di_init(void)
{
return of_register_platform_driver(&of_lbc_di_driver);
}
static void __exit lbc_di_exit(void)
{
of_unregister_platform_driver(&of_lbc_di_driver);
}
module_init(lbc_di_init);
module_exit(lbc_di_exit);
MODULE_AUTHOR("Raphael Assenat <raph@8d.com>, Trent Piepho <tpiepho@freescale.com>");
MODULE_DESCRIPTION("GPIO LED driver");
MODULE_LICENSE("GPL");
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
已为社区贡献1条内容
所有评论(0)