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 ( * suspend_late)( struct platform_device * , pm_message_t state);
int ( * resume_early)( struct platform_device * );
int ( * resume)( struct platform_device * );
struct device_driver driver;
};
struct device_driver {
const char * name;
struct bus_type * bus;
struct kobject kobj;
struct klist klist_devices;
struct klist_node knode_bus;
struct module * owner;
const char * mod_name; /* used for built-in modules */
struct module_kobject * mkobj;
int ( * probe) ( struct device * dev);
int ( * remove) ( struct device * dev);
void ( * shutdown) ( struct device * dev);
int ( * suspend) ( struct device * dev, pm_message_t state);
int ( * resume) ( struct device * dev);
};
static struct platform_driver s3c2410iis_driver = {
.probe = s3c2410iis_probe,
.remove = s3c2410iis_remove,
.driver = {
.name = " s3c2410-iis " ,
.owner = THIS_MODULE,
},
};
struct platform_device {
const char * name;
int id;
struct device dev;
u32 num_resources;
struct resource * resource;
};
struct platform_device s3c_device_iis = {
.name = " s3c2410-iis " ,
.id = - 1 ,
.num_resources = ARRAY_SIZE(s3c_iis_resource),
.resource = s3c_iis_resource,
.dev = {
.dma_mask = & s3c_device_iis_dmamask,
.coherent_dma_mask = 0xffffffffUL
}
};
struct resource {
resource_size_t start;
resource_size_t end;
const char * name;
unsigned long flags;
struct resource * parent, * sibling, * child;
};
static struct resource s3c_iis_resource[] = {
[ 0 ] = {
.start = S3C24XX_PA_IIS,
.end = S3C24XX_PA_IIS + S3C24XX_SZ_IIS - 1 ,
.flags = IORESOURCE_MEM,
}
};
struct device {
struct klist klist_children;
struct klist_node knode_parent; /* node in sibling list */
struct klist_node knode_driver;
struct klist_node knode_bus;
struct device * parent;
struct kobject kobj;
char bus_id[BUS_ID_SIZE]; /* position on parent bus */
struct device_type * type;
unsigned is_registered: 1 ;
unsigned uevent_suppress: 1 ;
struct semaphore sem; /* semaphore to synchronize calls to
* its driver.
*/
struct bus_type * bus; /* type of bus device is on */
struct device_driver * driver; /* which driver has allocated this
device */
void * driver_data; /* data private to the driver */
void * platform_data; /* Platform specific data, device
core doesn't touch it */
struct dev_pm_info power;
#ifdef CONFIG_NUMA
int numa_node; /* NUMA node this device is close to */
#endif
u64 * dma_mask; /* dma mask (if dma'able device) */
u64 coherent_dma_mask; /* Like dma_mask, but for
alloc_coherent mappings as
not all hardware supports
64 bit addresses for consistent
allocations such descriptors. */
struct list_head dma_pools; /* dma pools (if dma'ble) */
struct dma_coherent_mem * dma_mem; /* internal for coherent mem
override */
/* arch specific additions */
struct dev_archdata archdata;
spinlock_t devres_lock;
struct list_head devres_head;
/* class_device migration path */
struct list_head node;
struct class * class ;
dev_t devt; /* dev_t, creates the sysfs "dev" */
struct attribute_group ** groups; /* optional groups */
void ( * release)( struct device * dev);
};
|
注意函数体的最后一行,它调用的是platform_driver_register这个函数。这个函数定义于driver/base/platform.c中,原型如下:
int platform_driver_register(struct platform_driver *drv)
它的功能就是为上面提到的plarform_driver中的driver这个结构中的probe、remove这些变量指定功能函数。
到目前为止,内核就已经知道了有这么一个驱动模块。内核启动的时候,就会调用与该驱动相关的probe函数。我们来看一下probe函数实现了什么功能。
probe函数的原型为
int xxx_probe(struct platform_device *pdev)
即它的返回类型为int,接收一个platform_device类型的指针作为参数。返回类型就是我们熟悉的错误代码了,而接收的这个参数呢,我们上面已经说过,驱动程序为设备服务,就需要知道设备的信息。而这个参数,就包含了与设备相关的信息。
probe函数接收到plarform_device这个参数后,就需要从中提取出需要的信息。它一般会通过调用内核提供的platform_get_resource和platform_get_irq等函数来获得相关信息。如通过platform_get_resource获得设备的起始地址后,可以对其进行request_mem_region和ioremap等操作,以便应用程序对其进行操作。通过platform_get_irq得到设备的中断号以后,就可以调用request_irq函数来向系统申请中断。这些操作在设备驱动程序中一般都要完成。
在完成了上面这些工作和一些其他必须的初始化操作后,就可以向系统注册我们在/dev目录下能看在的设备文件了。举一个例子,在音频芯片的驱动中,就可以调用register_sound_dsp来注册一个dsp设备文件,lcd的驱动中就可以调用register_framebuffer来注册fb设备文件。这个工作完成以后,系统中就有我们需要的设备文件了。而和设备文件相关的操作都是通过一个file_operations 来实现的。在调用register_sound_dsp等函数的时候,就需要传递一个file_operations 类型的指针。这个指针就提供了可以供用户空间调用的write、read等函数。file_operations结构的定义位于include/linux/fs.h中,列出如下:
|
到目前为止,probe函数的功能就完成了。
当用户打开一个设备,并调用其read、write等函数的时候,就可以通过上面的file_operations来找到相关的函数。所以,用户驱动程序还需要实现这些函数,具体实现和相关的设备有密切的关系,这里就不再介绍了。
原文地址 http://blog.chinaunix.net/u1/57747/showart_1073860.html