linux平台设备(以RTC为例)
LINUX平台设备分为
1 设备层(主要是描述设备资源)?
2 驱动层(我们写驱动要实现的)
?
设备层:主要定义个设备的资源。?
用platform_device结构体来描述一个平台设备。定义在(/include/LINUX/platform_device.h)?
struct platform_device {?
const char * name;//设备名称?
int?? id;?
struct device dev;?
u32?? num_resources;//设备使用资源的数量?
struct resource * resource;//使用的资源?
};?
例如:/* RTC */?
/**************************************************************/?
arch/arm/plat-s3c24xx/devs.c中就定义了很多平台设备,截取RTC的设备结构?
static struct resource s3c_rtc_resource[] = {?
[0] = {//IO端口资源范围?
?? .start = S3C24XX_PA_RTC,?
?? .end?? = S3C24XX_PA_RTC + 0xff,?
?? .flags = IORESOURCE_MEM,//定义了这个资源的类型(通俗点就是要内存映射的类型)/include/linux/ioport.h?
},?
[1] = {//RTC报警中断资源?
?? .start = IRQ_RTC,?
?? .end?? = IRQ_RTC,?
?? .flags = IORESOURCE_IRQ,//这个资源是中断类型的?
},?
[2] = {//TICK节拍时间中断资源?
?? .start = IRQ_TICK,?
?? .end?? = IRQ_TICK,?
?? .flags = IORESOURCE_IRQ,?
}?
};
struct platform_device s3c_device_rtc = {?
.name??? = "s3c2410-rtc",//RTC名字(与驱动里面的name要一样)?
.id??? = -1,?
.num_resources?? = ARRAY_SIZE(s3c_rtc_resource),(对上面的s3c_rtc_resource求值,求出的是定义了几个资源)?
.resource?? = s3c_rtc_resource,//对应上面的 struct resource s3c_rtc_resource[]?
};
EXPORT_SYMBOL(s3c_device_rtc);?
/**************************************************************/?
以上定义了RTC所有的设备资源!然后我们要使用这些资源,就要添加到内核里面!?
打开arch/arm/mach-s3c2440/mach-smdk2440.c这个ARM 2440平台的系统入口文件。里面有这两个!?
static struct platform_device *smdk2440_devices[] __initdata = {?
&s3c_device_usb,?
&s3c_device_lcd,?
&s3c_device_wdt,?
&s3c_device_i2c0,?
&s3c_device_iis,?
&s3c_device_rtc,//因为我们要加入RTC设备到系统,在这里加入?
};?
static void __init smdk2440_machine_init(void)?
{?
s3c24xx_fb_set_platdata(&smdk2440_fb_info);?
s3c_i2c0_set_platdata(NULL);
platform_add_devices(smdk2440_devices, ARRAY_SIZE(smdk2440_devices));//这里是添加设备的注册函数!刚好调用上面的smdk2440_devices。?
smdk_machine_init();?
}?
上面就吧设备资源加入到了内核!
?
下面就是实现我们的驱动!?
具体步骤和RTC驱动的实现参照黄刚前辈的博客!http://blog.chinaunix.net/u3/101649/showart_2142248.html?
?
驱动层用struct platform_driver 结构体来定义驱动。
static struct platform_driver rtc_driver = //定义platform_driver 结构体?
{?
??? .probe?? = rtc_probe, /*RTC探测函数,?
??? .remove = __devexit_p(rtc_remove),/*RTC移除函数,*/?
??? .suspend = rtc_suspend, /*RTC挂起函数,?
??? .resume = rtc_resume, /*RTC恢复函数,?
??? .driver =?
??? {?
??????? /*注意这里的名称一定要和系统中定义平台设备的地方一致,这样才能把平台设备与该平台设备的驱动关联起来*/?
??????? .name?? = "s3c2410-rtc",?
??????? .owner = THIS_MODULE,?
??? },?
};
static int __init rtc_init(void)?
{?
??? /*将RTC注册成平台设备驱动*/?
??? return platform_driver_register(&rtc_driver);?
}
static void __exit rtc_exit(void)?
{?
??? /*注销RTC平台设备驱动*/?
??? platform_driver_unregister(&rtc_driver);?
}
module_init(rtc_init);?
module_exit(rtc_exit);
MODULE_LICENSE("GPL");?
MODULE_AUTHOR("Huang Gang");?
MODULE_DESCRIPTION("My2440 RTC driver");?
这个地方还跟我们以前写字符模块驱动一样,用module_init(),,但是我们把类似于中断注册,IO映射等等都都放到rtc_probe里面实现!所以这里只是调用return platform_driver_register(&rtc_driver)将RTC注册成平台设备驱动。?
其实很多地方我也不懂,查查资料,我是这样理解的:为了保持低功耗,我们只是在用的某个设备的时候才将其打开,平时不用的时候我们可以挂起。因为ARM一般都用到消费电子上,所以对功耗有很高的要求!这也是我们采用平台设备的目的!?
开始我以为这里就是驱动实现函数(类似于open ,read等等)!看到后来才发现不是!接口函数在后面!?
注册完设备,此时系统只是知道有这个设备,但是不知道要使用什么资源!于是rtc_probe这个探测函数出场了!它主要负责探测资源,初始化,(其实设备的初始化可以放到__init rtc_init()函数里面进行的)。我们在探测函数里面注册中断(RTC有2个中断),进行内存映射(IOMAP()),就像我们写字符驱动的初始化一样!但王刚的注册中断在后面的open里面,iomap在这里面,我觉得根据实际情况来,其实放哪里都可以,比如中断,如果牵扯到共享中断,还是要放到open里面来注册。?
probe函数见黄刚前辈的博客!
一切初始化完后,我们要注册一个RTC设备类。?
| /*将RTC注册为RTC设备类,RTC设备类在RTC驱动核心部分中由系统定义好的,?
|???? 注意rtcops这个参数是一个结构体,该结构体的作用和里面的接口函数实现在第③步中。?
|???? rtc_device_register函数在rtc.h中定义,在drivers/rtc/class.c中实现*/?
|??? rtc = rtc_device_register("my2440", &pdev->dev, &rtcops, THIS_MODULE);?
|??? if (IS_ERR(rtc))?
|??? {?
|??????? /*错误处理*/?
|??????? dev_err(&pdev->dev, "cannot attach rtc\n");?
|??????? ret = PTR_ERR(rtc);?
|??????? goto err_nortc;?
|??? }?
|
static const struct rtc_class_ops rtcops = {???????? / /RTC类的file_operation函数)?
.open = rtc_open,?
.release???????? = rtc_release,?
.irq_set_freq??? = rtc_setfreq,
.irq_set_state?? = rtc_setpie,?
.read_time?????? = rtc_gettime,?
.set_time??????? = rtc_settime,?
.read_alarm????? = rtc_getalarm,?
.set_alarm?????? = rtc_setalarm,?
};?
//这个是RTC类注册函数的原型!?
struct rtc_device *rtc_device_register(const char *name,?
????? struct device *dev,?
????? const struct rtc_class_ops *ops,?
????? struct module *owner);
我也不太清楚这个类的作用,我理解的是为了统一管理吗???不清楚!知道的说下!?
类注册的函数里面有一个struct rtc_class_ops *ops, 这个结构体很重要,他就是我们写驱动时候那些个(READ ,WRITE等),我们主要来实现这个rtc_class_ops。?
struct rtc_class_ops {?
int (*open)(struct device *);//打开?
void (*release)(struct device *);//关闭?
int (*ioctl)(struct device *, unsigned int, unsigned long);//IOCTL?
int (*read_time)(struct device *, struct rtc_time *);//读时间?
int (*set_time)(struct device *, struct rtc_time *);//设置时间?
int (*read_alarm)(struct device *, struct rtc_wkalrm *);?
int (*set_alarm)(struct device *, struct rtc_wkalrm *);?
int (*proc)(struct device *, struct seq_file *);?
int (*set_mmss)(struct device *, unsigned long secs);?
int (*irq_set_state)(struct device *, int enabled);?
int (*irq_set_freq)(struct device *, int freq);?
int (*read_callback)(struct device *, int data);?
};?
看看,跟我们的file_operation是多么的相似!可以说是完全一样!我们就是来实现这些函数来操作我们的RTC.
?
RTC在Linux中的整体结构:?
就个人理解,RTC在Linux中整体结构分为两个部分。第一个是部分就是上面所讲的作为平台设备被挂接到系统总线中,这里我把他叫做设备层(呵呵,可能不是很准确的叫法);第二部分就是驱动部分,这里叫做驱动层。在Linux中要使一个驱动在不同的平台中都能够使用似乎是不可能的,所以我们先看2.6.30.4内核驱动中的RTC部分是单独的一个文件夹,在文件夹中包含了很多不同体系结构的RTC驱动,当然也有S3C2440的RTC驱动,然而在这些驱动中他们都使用了一组文件里面的方法,那么这组文件就是RTC的核心(注意这里的核心不是指对RTC硬件的操作,指的是对RTC操作的方法。对硬件寄存器的操作还是在具体的驱动中)。
?
?