是一个platform驱动的设计模板。
#include <linux/module.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/init.h>
#include <linux/poll.h>
#include <linux/slab.h>
#include <linux/input.h>
#include <linux/serio.h>
#include <linux/delay.h>
#include <linux/clk.h>
#include <linux/mm.h>
#include <linux/platform_device.h>
#include <linux/cdev.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/system.h>
#include <asm/uaccess.h>
#define DEV_NAME "HELLO"
#define BUFF_SIZE 32*1024
unsigned char *buffer;
unsigned char *kmalloc_area;
typedef struct _hello_dev
{
unsigned char major;
dev_t dev_id;
struct platform_device *plat_dev;
struct class *cls;
struct cdev *char_dev;
struct device *dev;
}HELLO_DEV,*pHELLO_DEV;
static pHELLO_DEV phello_dev;
static int hello_open(struct inode *pinode,struct file *pfile)
{
return 0;
}
static int hello_release(struct inode *pinode,struct file *pfile)
{
return 0;
}
static size_t hello_mmap(struct file *pfile,struct vm_area_struct *vma)
{
/* unsigned long start;
unsigned long len = 0;
start = (unsigned long)buffer;
len = PAGE_ALIGN((start&~PAGE_MASK)+BUFF_SIZE);
start &= PAGE_MASK;
if(remap_pfn_range(vma,start,vma->vm_pgoff,len,vma->vm_page_prot))
return -EAGAIN;*/
int ret;
ret = remap_pfn_range(vma,
vma->vm_start,
virt_to_phys((void*)((unsigned long)kmalloc_area)) >> PAGE_SHIFT,
vma->vm_end-vma->vm_start,
PAGE_SHARED);
if(ret != 0)
{
return -EAGAIN;
}
return 0;
printk(KERN_ALERT "mmap ok!/n");
return 0;
}
static size_t hello_read(struct file *pfile,void __user *buff,size_t count,loff_t *plofft)
{
return 0;
}
static size_t hello_write(struct file *pfile,void __user *buff,size_t count,loff_t *plofft)
{
return 0;
}
struct file_operations hello_fops=
{
.owner = THIS_MODULE,
.read = (void(*))hello_read,
.write = (void(*))hello_write,
.mmap = (void(*))hello_mmap,
.open = (void(*))hello_open,
.release = (void(*))hello_release,
};
static int __devinit hello_probe(struct platform_device *plat_dev)
{
int ret;
printk("device %s-%d is delect",plat_dev->name,plat_dev->id);
plat_dev->dev.devt = phello_dev->dev_id;
cdev_init(phello_dev->char_dev,&hello_fops);
phello_dev->char_dev->owner = THIS_MODULE;
ret = cdev_add(phello_dev->char_dev,phello_dev->dev_id,1);
if(ret)
{
printk("cdev add err!/n");
goto cdev_add_err;
}
platform_set_drvdata(plat_dev,phello_dev->char_dev);
phello_dev->dev = device_create(phello_dev->cls,NULL,phello_dev->dev_id,&(plat_dev->dev),DEV_NAME);
if(IS_ERR(phello_dev->dev))
{
printk("device_create err!/n");
goto device_create_err;
}
goto probe_ok;
device_create_err:
device_destroy(phello_dev->cls,phello_dev->dev_id);
platform_set_drvdata(plat_dev,NULL);
cdev_add_err:
cdev_del(phello_dev->char_dev);
probe_ok:
return 0;
}
static int __devexit hello_remove(struct platform_device *plat_dev)
{
struct cdev *chrdev;
chrdev = platform_get_drvdata(plat_dev);
if(chrdev)
{
cdev_del(phello_dev->char_dev);
}
device_destroy(phello_dev->cls,phello_dev->dev_id);
platform_set_drvdata(plat_dev,NULL);
plat_dev->dev.devt = 0;
return 0;
}
#ifdef CONFIG_PW
static int hello_suspend(struct platform_device *plat_dev, pm_message_t state)
{
return 0;
}
static int hello_resume(struct platform_device *plat_dev)
{
return 0;
}
#else
#define hello_suspend NULL
#define hello_resume NULL
#endif
struct platform_driver hello_driver=
{
.probe = hello_probe,
.remove = __devexit_p(hello_remove),
#ifdef CONFIG_PW
.suspend = hello_suspend,
.resume = hello_resume,
#endif
.driver=
{
.name = DEV_NAME,
.owner = THIS_MODULE,
},
};
static int __init hello_init(void)
{
int ret;
unsigned int i;
phello_dev = kmalloc(sizeof(HELLO_DEV),GFP_KERNEL);
if(!(phello_dev && (phello_dev->char_dev)))
{
ret = -ENOMEM;
goto malloc_mem_err;
}
memset(phello_dev,0,sizeof(HELLO_DEV));
phello_dev->char_dev = kmalloc(sizeof(struct cdev),GFP_KERNEL);
memset(phello_dev->char_dev,0,sizeof(struct cdev));
#ifdef DEV_MAJOR
phello_dev->major = DEV_MAJOR;
phello_dev->dev_id = MKDEV(phello_dev->major,0);
#endif
phello_dev->plat_dev = platform_device_alloc(DEV_NAME,0);
ret = platform_device_add(phello_dev->plat_dev);
if(ret<0)
{
goto platform_device_add_err;
}
if(phello_dev->major)
{
ret = register_chrdev_region(phello_dev->dev_id,1,DEV_NAME);
if(ret < 0)
{
goto register_chrdev_err;
}
}
else
{
ret = alloc_chrdev_region(&(phello_dev->dev_id),0,1,DEV_NAME);
if(ret<0)
{
goto register_chrdev_err;
}
phello_dev->major = MAJOR(phello_dev->dev_id);
}
printk("dev major is %d minor is %d/n",phello_dev->major,MINOR(phello_dev->dev_id));
//add other source such as clk,irq,dma and so on
buffer = kmalloc(BUFF_SIZE+2*PAGE_SIZE,GFP_KERNEL);
kmalloc_area = (unsigned char *)(((unsigned long)buffer + PAGE_SIZE -1) & PAGE_MASK);
for(i=0;i<256;i++)
{
*buffer = i+10;
buffer++;
}
//creat class
phello_dev->cls = class_create(THIS_MODULE,DEV_NAME);
if(IS_ERR(phello_dev->cls))
{
goto class_create_err;
}
printk(KERN_ALERT "hello world !/n");
platform_driver_register(&hello_driver);
goto register_ok;
class_create_err:
register_chrdev_err:
unregister_chrdev_region(phello_dev->dev_id, 1);
platform_device_add_err:
platform_device_put(phello_dev->plat_dev);
malloc_mem_err:
kfree(phello_dev);
register_ok:
return ret;
}
static void __exit hello_exit(void)
{
platform_driver_unregister(&hello_driver);
class_destroy(phello_dev->cls);
unregister_chrdev_region(phello_dev->dev_id, 1);
platform_device_unregister(phello_dev->plat_dev);
kfree(buffer);
kfree(phello_dev->char_dev);
kfree(phello_dev);
printk(KERN_ALERT "remove hello world!/n");
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");