总线设备驱动模型---platform篇
2013-12-19 20:50
134 查看
2012-09-02 10:57:50
分类: LINUX
linux从2.6起就加入了一套新的驱动管理和注册的机制platform平台总线,是一条虚拟的总线,设备用platform_device表示,驱
动用platform_driver进行注册。于传统的bus/device/driver机制相比,platform由内核进行统一管理,在驱动中使用
资源,提高了代码的安全性和可移植性。
下面来看看内核时怎么注册platform总线的过程
点击(此处)折叠或打开
int __init
platform_bus_init(void)
{
int
error;
early_platform_cleanup();
error
= device_register(&platform_bus);//注册了platform的设备
if
(error)
return
error;
error
= bus_register(&platform_bus_type);//注册了platform总线
if
(error)
device_unregister(&platform_bus);
return
error;
}
点击(此处)折叠或打开
struct device platform_bus = {
.init_name =
"platform",
};
点击(此处)折叠或打开
struct bus_type platform_bus_type = {
.name =
"platform",
.dev_attrs =
platform_dev_attrs,
.match =
platform_match,
.uevent =
platform_uevent,
.pm =
&platform_dev_pm_ops,
};
其过程和总线的注册过程差不多,驱动和设备匹配后,调用platform的match函数。由传统的机制,也不难总结出platform的开发流程为
1、定义一个platform_device,并注册
2、定义一个platform_driver,并注册
定义platform_device过程
点击(此处)折叠或打开
struct platform_device *platform_device_alloc(const
char *name, int
id)
{
struct
platform_object *pa;
pa
= kzalloc(sizeof(struct platform_object) +
strlen(name),
GFP_KERNEL);
if
(pa) {
strcpy(pa->name,
name);
pa->pdev.name
= pa->name;
pa->pdev.id
= id;
device_initialize(&pa->pdev.dev);
pa->pdev.dev.release
=
platform_device_release;
arch_setup_pdev_archdata(&pa->pdev);
}
return
pa ? &pa->pdev : NULL;
}
下面首先来看看platform_device的注册过程
点击(此处)折叠或打开
struct platform_device {
const
char *
name;
//设备名
int id;
//设备ID
struct
device dev;
u32 num_resources;
//设备使用的资源的数目
struct
resource *
resource;
//设备使用的资源
const
struct
platform_device_id *id_entry;
/*
MFD cell pointer */
struct
mfd_cell *mfd_cell;
/*
arch specific additions */
struct
pdev_archdata archdata;
};
点击(此处)折叠或打开
struct resource {
resource_size_t
start;
resource_size_t
end;
const
char *name;
unsigned
long flags;
struct
resource *parent, *sibling, *child;
};
在struct
resource结构中我们通常只关心start、end和flags这3个字段,分别表示资源的开始值、结束值和类型,flags可以用内存资源,IO资源,中断资源等。
点击(此处)折叠或打开
int
platform_device_register(struct
platform_device *pdev)
{
device_initialize(&pdev->dev);//dev初始化
arch_setup_pdev_archdata(pdev);
return
platform_device_add(pdev);//加入到dev链表
}
点击(此处)折叠或打开
int
platform_device_add(struct
platform_device *pdev)
{
int
i, ret = 0;
if
(!pdev)
return
-EINVAL;
if
(!pdev->dev.parent)
pdev->dev.parent
= &platform_bus;//父设备设置为platform_bus
pdev->dev.bus
= &platform_bus_type;//设备挂载在platforrm总线上
if
(pdev->id
!= -1)
dev_set_name(&pdev->dev,
"%s.%d", pdev->name, pdev->id);
else
dev_set_name(&pdev->dev,
"%s", pdev->name);
for
(i = 0; i
<</span> pdev->num_resources; i++)
{
//完成资源的初始化
struct
resource *p, *r
= &pdev->resource[i];
if
(r->name ==
NULL)
r->name
= dev_name(&pdev->dev);
p
= r->parent;
if
(!p)
{
if
(resource_type(r)
== IORESOURCE_MEM)
p
= &iomem_resource;
else
if (resource_type(r)
== IORESOURCE_IO)
p
= &ioport_resource;
}
if
(p && insert_resource(p,
r)) {
printk(KERN_ERR
"%s: failed to claim resource
%d\n",
dev_name(&pdev->dev),
i);
ret
= -EBUSY;
goto
failed;
}
}
pr_debug("Registering
platform device '%s'. Parent at %s\n",
dev_name(&pdev->dev),
dev_name(pdev->dev.parent));
ret
= device_add(&pdev->dev);
if
(ret ==
0)
return
ret;
failed:
while
(--i
>= 0)
{
struct
resource *r = &pdev->resource[i];
unsigned
long type =
resource_type(r);
if
(type ==
IORESOURCE_MEM || type ==
IORESOURCE_IO)
release_resource(r);
}
return
ret;
}
其上最后也是调用device_add的,其主要是将device加入到bus总线中,并由device_attach完成设备与驱动之间的匹配,这个过程在设备一篇中已经有详细的分析过程中,再看看驱动的注册过程。
点击(此处)折叠或打开
struct platform_driver {
int
(*probe)(struct platform_device *);
int
(*remove)(struct platform_device *);
void
(*shutdown)(struct platform_device *);
int
(*suspend)(struct platform_device *,
pm_message_t state);
int
(*resume)(struct platform_device *);
struct
device_driver driver;
const
struct platform_device_id *id_table;
};
可见,它包含了设备操作的功能函数,同时包含了device_driver结构。内核提供的platform_driver结构的注册为
点击(此处)折叠或打开
int
platform_driver_register(struct
platform_driver *drv)
{
drv->driver.bus
= &platform_bus_type;//注册到总线上,drv与dev匹配通过platform_bus_type注册的platform_match完成
if
(drv->probe)
drv->driver.probe
=
platform_drv_probe;
if
(drv->remove)
drv->driver.remove
=
platform_drv_remove;
if
(drv->shutdown)
drv->driver.shutdown
=
platform_drv_shutdown;
return
driver_register(&drv->driver);
}
其注册函数中比较重要的还是调用了driver_register,添加到platform总线链表,完成设备与驱动之间的匹配过程,其主要的过程在总线设备驱动模型的驱动篇已经有过分析。
点击(此处)折叠或打开
int
driver_register(struct
device_driver *drv)
{
int
ret;
struct
device_driver *other;
BUG_ON(!drv->bus->p);
if
((drv->bus->probe && drv->probe) ||
(drv->bus->remove && drv->remove) ||
(drv->bus->shutdown && drv->shutdown))
printk(KERN_WARNING
"Driver '%s' needs updating - please
use "
"bus_type
methods\n",
drv->name);
other
= driver_find(drv->name, drv->bus);
if
(other) {
put_driver(other);
printk(KERN_ERR
"Error: Driver '%s' is already
registered, "
"aborting...\n",
drv->name);
return
-EBUSY;
}
ret
= bus_add_driver(drv);
if
(ret)
return
ret;
ret
= driver_add_groups(drv,
drv->groups);
if
(ret)
bus_remove_driver(drv);
return
ret;
}
下面看看看驱动和设备的匹配过程,由以前可以看出,主要是调用bus的match函数来完成匹配。
点击(此处)折叠或打开
static int
platform_match(struct device
*dev, struct device_driver *drv)
{
struct
platform_device *pdev
=
to_platform_device(dev);
struct
platform_driver *pdrv
=
to_platform_driver(drv);
/*
Attempt an OF style match first */
if
(of_driver_match_device(dev,
drv))
return
1;
/*
Then try to match against the id table */
if
(pdrv->id_table)
return
platform_match_id(pdrv->id_table, pdev)
!= NULL;
/*
fall-back to driver name match */
return
(strcmp(pdev->name, drv->name) ==
0);
}
由上面可以看出,只需要比较dev的名字和drv的名字,如果是相同的话就匹配成功。
在platform的设备驱动的编写有两种方法:
1、在bsp版文件中实现定义,在文件中将platform_device被化为一个数组,最后通过platform_add_devices函数注册。对于2440来说位于arch\arm\mach-s3c2440\mach-smdk2440.c中定义
点击(此处)折叠或打开
static struct platform_device *smdk2440_devices[]
__initdata = {
&s3c_device_usb,
&s3c_device_lcd,
&s3c_device_wdt,
&s3c_device_i2c,
&s3c_device_iis,
};
如果我们要实现一个设备的添加,那么只需要加入一个struct
platform_device的数组,然后只需要编写对应的platform_driver驱动程序就可以了。从这种方法可以看出,存在一个很明显的缺点,如果要改写驱动,就要重新的编译内核。
2、第二种方法只需要单独编写一个内核模块加载到内核中。
例子:
device文件:platform_dev.c
点击(此处)折叠或打开
struct platform_device *my_led_dev;
static int __init
platform_dev_init(void)
{
int
ret;
//分配一个
platform_device结构体
my_led_dev
=
platform_device_alloc("platform_led", -1);
ret
=
platform_device_add(my_led_dev);//将自定义的设备添加到内核设备架构中
if(ret)
platform_device_put(my_led_dev);//销毁platform设备结构
return
ret;
}
static void __exit platform_dev_exit(void)
{
platform_device_unregister(my_led_dev);//注销platform_device
}
module_init(platform_dev_init);
module_exit(platform_dev_exit);
MODULE_AUTHOR("Sola");
MODULE_LICENSE("GPL");
drv文件:platform_drv.c
点击(此处)折叠或打开
static int
s3c6410_led_open(struct inode
*inode, struct file *file)
{
unsigned
tmp;
tmp
= readl(S3C64XX_GPMCON);
tmp
= (tmp & ~(0xFFFF))|(0x1111U);
writel(tmp,
S3C64XX_GPMCON);
return
0;
}
static int
s3c6410_led_close(struct inode
*inode, struct file *file)
{
return
0;
}
static int
s3c6410_led_read(struct file
*filp, char __user *buff,
size_t count, loff_t
*offp)
{
printk("#########read######\n");
return
count;
}
static int
s3c6410_led_write (struct file
*filp, const
char __user *buf, size_t count,loff_t *f_pos)
{
char
wbuf[10];
unsigned
tmp;
copy_from_user(wbuf,buf,count);
if(wbuf[0]==1)//1号灯亮
switch(wbuf[1])
{
case
0: //off
tmp
= readl(S3C64XX_GPMDAT);
tmp
|= (0x1U);
writel(tmp,
S3C64XX_GPMDAT);
break;
case
1: //on
tmp
= readl(S3C64XX_GPMDAT);
tmp
&= ~(0x1U);
writel(tmp,
S3C64XX_GPMDAT);
break;
default
:
break;
}
if(wbuf[0]==2)//2号灯亮
switch(wbuf[1])
{
case
0: //off
tmp
= readl(S3C64XX_GPMDAT);
tmp
|= (0x2U);
writel(tmp,
S3C64XX_GPMDAT);
break;
case
1: //on
tmp
= readl(S3C64XX_GPMDAT);
tmp
&= ~(0x2U);
writel(tmp,
S3C64XX_GPMDAT);
break;
default
:
break;
}
if(wbuf[0]==3)//3号灯亮
switch(wbuf[1])
{
case
0: //off
tmp
= readl(S3C64XX_GPMDAT);
tmp
|= (0x4U);
writel(tmp,
S3C64XX_GPMDAT);
break;
case
1: //on
tmp
= readl(S3C64XX_GPMDAT);
tmp
&= ~(0x4U);
writel(tmp,
S3C64XX_GPMDAT);
break;
default
:
break;
}
if(wbuf[0]==4)//4号灯亮
switch(wbuf[1])
{
case
0: //off
tmp
= readl(S3C64XX_GPMDAT);
tmp
|= (0x8U);
writel(tmp,
S3C64XX_GPMDAT);
break;
case
1: //on
tmp
= readl(S3C64XX_GPMDAT);
tmp
&= ~(0x8U);
writel(tmp,
S3C64XX_GPMDAT);
break;
default
:
break;
}
return
count;
}
static struct file_operations led_fops = {
.owner
= THIS_MODULE,
.open
= s3c6410_led_open,
.release
= s3c6410_led_close,
.read
= s3c6410_led_read,
.write
= s3c6410_led_write,
};
static int
my_plat_probe(struct
platform_device *dev)
{
int
rc;
printk("Test
platform_led dev\n");
//注册设备
rc =
register_chrdev(LED_MAJOR,"platform_led",&led_fops);
if (rc
<</span>0)
{
printk
("register %s char dev error\n","led");
return
-1;
}
printk
("ok!\n");
return
0;
}
static int
my_plat_remove(struct
platform_device *dev)
{
printk("my
platfrom device has removed.\n");
return
0;
}
struct platform_driver my_led_drv = {
.probe
= my_plat_probe,
.remove
= my_plat_remove,
.driver
= {
.owner
= THIS_MODULE,
.name
= "platform_led",
},
};
static int __init
platform_drv_init(void)
{
int
ret;
ret
=
platform_driver_register(&my_led_drv);
return
ret;
}
static void __exit platform_drv_exit(void)
{
platform_driver_unregister(&my_led_drv);
}
module_init(platform_drv_init);
module_exit(platform_drv_exit);
MODULE_LICENSE("GPL");
分类: LINUX
linux从2.6起就加入了一套新的驱动管理和注册的机制platform平台总线,是一条虚拟的总线,设备用platform_device表示,驱
动用platform_driver进行注册。于传统的bus/device/driver机制相比,platform由内核进行统一管理,在驱动中使用
资源,提高了代码的安全性和可移植性。
下面来看看内核时怎么注册platform总线的过程
点击(此处)折叠或打开
int __init
platform_bus_init(void)
{
int
error;
early_platform_cleanup();
error
= device_register(&platform_bus);//注册了platform的设备
if
(error)
return
error;
error
= bus_register(&platform_bus_type);//注册了platform总线
if
(error)
device_unregister(&platform_bus);
return
error;
}
点击(此处)折叠或打开
struct device platform_bus = {
.init_name =
"platform",
};
点击(此处)折叠或打开
struct bus_type platform_bus_type = {
.name =
"platform",
.dev_attrs =
platform_dev_attrs,
.match =
platform_match,
.uevent =
platform_uevent,
.pm =
&platform_dev_pm_ops,
};
其过程和总线的注册过程差不多,驱动和设备匹配后,调用platform的match函数。由传统的机制,也不难总结出platform的开发流程为
1、定义一个platform_device,并注册
2、定义一个platform_driver,并注册
定义platform_device过程
点击(此处)折叠或打开
struct platform_device *platform_device_alloc(const
char *name, int
id)
{
struct
platform_object *pa;
pa
= kzalloc(sizeof(struct platform_object) +
strlen(name),
GFP_KERNEL);
if
(pa) {
strcpy(pa->name,
name);
pa->pdev.name
= pa->name;
pa->pdev.id
= id;
device_initialize(&pa->pdev.dev);
pa->pdev.dev.release
=
platform_device_release;
arch_setup_pdev_archdata(&pa->pdev);
}
return
pa ? &pa->pdev : NULL;
}
下面首先来看看platform_device的注册过程
点击(此处)折叠或打开
struct platform_device {
const
char *
name;
//设备名
int id;
//设备ID
struct
device dev;
u32 num_resources;
//设备使用的资源的数目
struct
resource *
resource;
//设备使用的资源
const
struct
platform_device_id *id_entry;
/*
MFD cell pointer */
struct
mfd_cell *mfd_cell;
/*
arch specific additions */
struct
pdev_archdata archdata;
};
点击(此处)折叠或打开
struct resource {
resource_size_t
start;
resource_size_t
end;
const
char *name;
unsigned
long flags;
struct
resource *parent, *sibling, *child;
};
在struct
resource结构中我们通常只关心start、end和flags这3个字段,分别表示资源的开始值、结束值和类型,flags可以用内存资源,IO资源,中断资源等。
点击(此处)折叠或打开
int
platform_device_register(struct
platform_device *pdev)
{
device_initialize(&pdev->dev);//dev初始化
arch_setup_pdev_archdata(pdev);
return
platform_device_add(pdev);//加入到dev链表
}
点击(此处)折叠或打开
int
platform_device_add(struct
platform_device *pdev)
{
int
i, ret = 0;
if
(!pdev)
return
-EINVAL;
if
(!pdev->dev.parent)
pdev->dev.parent
= &platform_bus;//父设备设置为platform_bus
pdev->dev.bus
= &platform_bus_type;//设备挂载在platforrm总线上
if
(pdev->id
!= -1)
dev_set_name(&pdev->dev,
"%s.%d", pdev->name, pdev->id);
else
dev_set_name(&pdev->dev,
"%s", pdev->name);
for
(i = 0; i
<</span> pdev->num_resources; i++)
{
//完成资源的初始化
struct
resource *p, *r
= &pdev->resource[i];
if
(r->name ==
NULL)
r->name
= dev_name(&pdev->dev);
p
= r->parent;
if
(!p)
{
if
(resource_type(r)
== IORESOURCE_MEM)
p
= &iomem_resource;
else
if (resource_type(r)
== IORESOURCE_IO)
p
= &ioport_resource;
}
if
(p && insert_resource(p,
r)) {
printk(KERN_ERR
"%s: failed to claim resource
%d\n",
dev_name(&pdev->dev),
i);
ret
= -EBUSY;
goto
failed;
}
}
pr_debug("Registering
platform device '%s'. Parent at %s\n",
dev_name(&pdev->dev),
dev_name(pdev->dev.parent));
ret
= device_add(&pdev->dev);
if
(ret ==
0)
return
ret;
failed:
while
(--i
>= 0)
{
struct
resource *r = &pdev->resource[i];
unsigned
long type =
resource_type(r);
if
(type ==
IORESOURCE_MEM || type ==
IORESOURCE_IO)
release_resource(r);
}
return
ret;
}
其上最后也是调用device_add的,其主要是将device加入到bus总线中,并由device_attach完成设备与驱动之间的匹配,这个过程在设备一篇中已经有详细的分析过程中,再看看驱动的注册过程。
点击(此处)折叠或打开
struct platform_driver {
int
(*probe)(struct platform_device *);
int
(*remove)(struct platform_device *);
void
(*shutdown)(struct platform_device *);
int
(*suspend)(struct platform_device *,
pm_message_t state);
int
(*resume)(struct platform_device *);
struct
device_driver driver;
const
struct platform_device_id *id_table;
};
可见,它包含了设备操作的功能函数,同时包含了device_driver结构。内核提供的platform_driver结构的注册为
点击(此处)折叠或打开
int
platform_driver_register(struct
platform_driver *drv)
{
drv->driver.bus
= &platform_bus_type;//注册到总线上,drv与dev匹配通过platform_bus_type注册的platform_match完成
if
(drv->probe)
drv->driver.probe
=
platform_drv_probe;
if
(drv->remove)
drv->driver.remove
=
platform_drv_remove;
if
(drv->shutdown)
drv->driver.shutdown
=
platform_drv_shutdown;
return
driver_register(&drv->driver);
}
其注册函数中比较重要的还是调用了driver_register,添加到platform总线链表,完成设备与驱动之间的匹配过程,其主要的过程在总线设备驱动模型的驱动篇已经有过分析。
点击(此处)折叠或打开
int
driver_register(struct
device_driver *drv)
{
int
ret;
struct
device_driver *other;
BUG_ON(!drv->bus->p);
if
((drv->bus->probe && drv->probe) ||
(drv->bus->remove && drv->remove) ||
(drv->bus->shutdown && drv->shutdown))
printk(KERN_WARNING
"Driver '%s' needs updating - please
use "
"bus_type
methods\n",
drv->name);
other
= driver_find(drv->name, drv->bus);
if
(other) {
put_driver(other);
printk(KERN_ERR
"Error: Driver '%s' is already
registered, "
"aborting...\n",
drv->name);
return
-EBUSY;
}
ret
= bus_add_driver(drv);
if
(ret)
return
ret;
ret
= driver_add_groups(drv,
drv->groups);
if
(ret)
bus_remove_driver(drv);
return
ret;
}
下面看看看驱动和设备的匹配过程,由以前可以看出,主要是调用bus的match函数来完成匹配。
点击(此处)折叠或打开
static int
platform_match(struct device
*dev, struct device_driver *drv)
{
struct
platform_device *pdev
=
to_platform_device(dev);
struct
platform_driver *pdrv
=
to_platform_driver(drv);
/*
Attempt an OF style match first */
if
(of_driver_match_device(dev,
drv))
return
1;
/*
Then try to match against the id table */
if
(pdrv->id_table)
return
platform_match_id(pdrv->id_table, pdev)
!= NULL;
/*
fall-back to driver name match */
return
(strcmp(pdev->name, drv->name) ==
0);
}
由上面可以看出,只需要比较dev的名字和drv的名字,如果是相同的话就匹配成功。
在platform的设备驱动的编写有两种方法:
1、在bsp版文件中实现定义,在文件中将platform_device被化为一个数组,最后通过platform_add_devices函数注册。对于2440来说位于arch\arm\mach-s3c2440\mach-smdk2440.c中定义
点击(此处)折叠或打开
static struct platform_device *smdk2440_devices[]
__initdata = {
&s3c_device_usb,
&s3c_device_lcd,
&s3c_device_wdt,
&s3c_device_i2c,
&s3c_device_iis,
};
如果我们要实现一个设备的添加,那么只需要加入一个struct
platform_device的数组,然后只需要编写对应的platform_driver驱动程序就可以了。从这种方法可以看出,存在一个很明显的缺点,如果要改写驱动,就要重新的编译内核。
2、第二种方法只需要单独编写一个内核模块加载到内核中。
例子:
device文件:platform_dev.c
点击(此处)折叠或打开
struct platform_device *my_led_dev;
static int __init
platform_dev_init(void)
{
int
ret;
//分配一个
platform_device结构体
my_led_dev
=
platform_device_alloc("platform_led", -1);
ret
=
platform_device_add(my_led_dev);//将自定义的设备添加到内核设备架构中
if(ret)
platform_device_put(my_led_dev);//销毁platform设备结构
return
ret;
}
static void __exit platform_dev_exit(void)
{
platform_device_unregister(my_led_dev);//注销platform_device
}
module_init(platform_dev_init);
module_exit(platform_dev_exit);
MODULE_AUTHOR("Sola");
MODULE_LICENSE("GPL");
drv文件:platform_drv.c
点击(此处)折叠或打开
static int
s3c6410_led_open(struct inode
*inode, struct file *file)
{
unsigned
tmp;
tmp
= readl(S3C64XX_GPMCON);
tmp
= (tmp & ~(0xFFFF))|(0x1111U);
writel(tmp,
S3C64XX_GPMCON);
return
0;
}
static int
s3c6410_led_close(struct inode
*inode, struct file *file)
{
return
0;
}
static int
s3c6410_led_read(struct file
*filp, char __user *buff,
size_t count, loff_t
*offp)
{
printk("#########read######\n");
return
count;
}
static int
s3c6410_led_write (struct file
*filp, const
char __user *buf, size_t count,loff_t *f_pos)
{
char
wbuf[10];
unsigned
tmp;
copy_from_user(wbuf,buf,count);
if(wbuf[0]==1)//1号灯亮
switch(wbuf[1])
{
case
0: //off
tmp
= readl(S3C64XX_GPMDAT);
tmp
|= (0x1U);
writel(tmp,
S3C64XX_GPMDAT);
break;
case
1: //on
tmp
= readl(S3C64XX_GPMDAT);
tmp
&= ~(0x1U);
writel(tmp,
S3C64XX_GPMDAT);
break;
default
:
break;
}
if(wbuf[0]==2)//2号灯亮
switch(wbuf[1])
{
case
0: //off
tmp
= readl(S3C64XX_GPMDAT);
tmp
|= (0x2U);
writel(tmp,
S3C64XX_GPMDAT);
break;
case
1: //on
tmp
= readl(S3C64XX_GPMDAT);
tmp
&= ~(0x2U);
writel(tmp,
S3C64XX_GPMDAT);
break;
default
:
break;
}
if(wbuf[0]==3)//3号灯亮
switch(wbuf[1])
{
case
0: //off
tmp
= readl(S3C64XX_GPMDAT);
tmp
|= (0x4U);
writel(tmp,
S3C64XX_GPMDAT);
break;
case
1: //on
tmp
= readl(S3C64XX_GPMDAT);
tmp
&= ~(0x4U);
writel(tmp,
S3C64XX_GPMDAT);
break;
default
:
break;
}
if(wbuf[0]==4)//4号灯亮
switch(wbuf[1])
{
case
0: //off
tmp
= readl(S3C64XX_GPMDAT);
tmp
|= (0x8U);
writel(tmp,
S3C64XX_GPMDAT);
break;
case
1: //on
tmp
= readl(S3C64XX_GPMDAT);
tmp
&= ~(0x8U);
writel(tmp,
S3C64XX_GPMDAT);
break;
default
:
break;
}
return
count;
}
static struct file_operations led_fops = {
.owner
= THIS_MODULE,
.open
= s3c6410_led_open,
.release
= s3c6410_led_close,
.read
= s3c6410_led_read,
.write
= s3c6410_led_write,
};
static int
my_plat_probe(struct
platform_device *dev)
{
int
rc;
printk("Test
platform_led dev\n");
//注册设备
rc =
register_chrdev(LED_MAJOR,"platform_led",&led_fops);
if (rc
<</span>0)
{
printk
("register %s char dev error\n","led");
return
-1;
}
printk
("ok!\n");
return
0;
}
static int
my_plat_remove(struct
platform_device *dev)
{
printk("my
platfrom device has removed.\n");
return
0;
}
struct platform_driver my_led_drv = {
.probe
= my_plat_probe,
.remove
= my_plat_remove,
.driver
= {
.owner
= THIS_MODULE,
.name
= "platform_led",
},
};
static int __init
platform_drv_init(void)
{
int
ret;
ret
=
platform_driver_register(&my_led_drv);
return
ret;
}
static void __exit platform_drv_exit(void)
{
platform_driver_unregister(&my_led_drv);
}
module_init(platform_drv_init);
module_exit(platform_drv_exit);
MODULE_LICENSE("GPL");
相关文章推荐
- 总线设备驱动模型---platform篇
- 总线设备驱动模型---platform篇
- 总线设备驱动模型——platform篇
- 总线设备驱动模型---platform篇
- 总线设备驱动模型---platform篇
- 总线设备驱动模型---platform篇
- 总线设备驱动模型---platform篇
- 总线设备驱动模型---platform篇
- 设备模型之总线、设备、驱动
- 设备模型5之总线、设备、驱动(实例,简单演示probe过程)
- 总线设备驱动模型--设备篇
- 总线设备驱动模型 ----LDD3
- 总线设备驱动模型和平台设备模型
- 总线设备驱动模型
- Linux设备驱动之总线,设备,驱动模型
- Linux驱动开发-12、总线设备驱动模型
- Linux SPI总线设备驱动模型详解
- Linux总线设备驱动模型
- 设备驱动基础1:设备模型之总线,驱动,设备
- Linux设备模型(三) 总线、设备和设备驱动