看门狗驱动程序

大多数设备中都有看门狗硬件,所以驱动开发人员需要去实现这种设备的驱动。看门狗的用途是当CPU进入错误状态后,无法恢复的情况下,使计算机重新启动。

一.看门狗硬件原理

由于计算机在工作时不可避免地要受到各中各样因素的干扰,即使再优秀的计算机程序也可能因为这种干扰使计算机进入一个死循环,更严重的就是导致死机。有两种方法来处理这种情况,- -是采用人工复位的方法,二是依赖某种硬件来执行这个复位工作。这种硬件通常叫做看门狗(Watch Dog, WD)。

看门狗实际上是一个定时器,其硬件内部维护了-一个计数寄存器,每当时钟信号到来时,计数寄存器减1.如果减到0,则重新启动系统;如果在减到0之前,系统又设置计数寄存器为一个较大的值,那么系统永远不会重启。系统的这种设置能力表示系统一直处于一种正常运行状态。反之,如果计算机系统崩溃时,那么就无法重新设置计数寄存器的值。当计数寄存器的值为0时,系统就又重新启动。
1.看门狗的工作原理:
看门狗从一一个PCLK频率到产生一个RESET复位信号的过程如下:
(1)处理器向看门狗提供-一个PCLK时钟信号。其通过一一个8位预分频器(8-bitPrescaler)使频率降低。
(2)8位预分频系数由控制寄存器WTCON的第8~15位决定。分频后的频率就相当于PCLK除以( WTCON[15:8]+1)。
(3)然后再通过一-个4相分频器,分成4种大小的频率。这4种分频系数分别是16、32、64、128。看门狗可以通过控制寄存器的第3、4位来决定使用哪种频率。
(4)当选择的时钟频率到达计数器( Down Counter) 时,会按照工作频率将WTCNT减1。当达到0时,就会产生一个中断信号或者复位信号。
(5)如果控制寄存器WTCON的第2位为1,则发出一个中断信号:如果控制寄存器WTCON的第0位为1,则输出一一个复位信号,使系统重新启动。
在正常的情况下,需要不断地设置计数寄存器WTCNT的值使其不为0,这样可以保证系统不被重启,这称为“喂狗”。在喂狗时,计数寄存器WTCNT的值会被设为数据寄存器WTDAT的值。当系统崩溃时,不能对计数寄存器WTCNT重新设值,最终导致系统复位重启。
看门狗的工作频率=PCLK/((WTCON[15:8]+1)*divider)
divider=(16,32,64,128)

二. 平台设备模型

看门狗驱动中涉及两种设备模型,分别是平台设备和混杂设备。本节将分别对两种设备模型进行讲解。

2.1平台设备模型

从Linux 2.6起引入了一套 新的驱动管理和注册模型,即平台设备platform device 和平台驱动platform driver. Linux 中大部分的设备驱动,都可以使用这套机制,设备用platform _device 表示,驱动用platform _driver 表示。
平台设备模型与传统的device和driver模型相比,一个十分明显的优势在于平台设备模型将设备本身的资源注册进内核,由内核统一管理。 这样提高了驱动和资源管理的独立性,并且拥有较好的可移植性和安全性。

2.2平台设备

在Linux设备驱动中,有一-种设备叫做平台设备。平台设备是指处理器上集成的额外功能的附加设备,如Watch Dog、IC、 IIS. RTC、 ADC等设备。这些格外功能设备是为了节约硬件成本、减少产品功耗、缩小产品形状而集成到处理器内部的。需要注意的是,平台设备并不是与字符设备、块设备和网络设备并列的概念,而是一种平行的概念,其从另一个角度对设备进行了概括。如果从内核开发者的角度来看,平台设备的引入是为了更容易地开发字符设备、块设备和网络设备驱动。

1.平台设备结构体( platform. _device)

平台设备用platform device 结构体来描述,其结构体的定义如下:

struct platform device
{
	const char *name;/*平台设备的名字,与驱动的名字对应*/
	int id;/*与驱动绑定有关,般为-1*/
	struct device dev;/*设备结构体,说明platform device派生于device*/
	u32 num_resources; /*设备使用的资源数量*/
	struct resource *resource; /*指向资源的数组,数量由num resources指定*/
}

s3c2440处理器的内部硬件大多与s3c2410处理器的相同,所以内核并没有对s3c2440处理器的驱动代码进行升级,而沿用了s3c2410 处理器的驱动。在文件linux-2.6.29. 4\arch\arm\plat-s3c24xx\devs.c中定义了处理器的看门狗平台设备,代码如下:

struct platform _device s3c_device_ wdt=
{
	.name= "s3c2410-wdt",/*平台设备的名字*/
	.id=-1,/*一般设为-1中*/
	.num_resources= ARRAY_SIZE(s3c_wdt_resource)/*资源数量*/
	.resource=s3c_wdt_ resource,/*资源的指针*/
};

2.平台设备的资源

为了便于统-管理平台设备的资源,在platform_device 结构体中定义了平台设备所使用的资源。这些资源都是与特定处理器相关的,需要驱动开发者查阅相关的处理器数据手册来编写。s3c2440 处理器的看门狗资源如下代码所示。

static struct resource s3c_wdt_resource[]=
{
	[0]={
		/*看门狗I/0内存开始位置,被定义为WTCON的地址0x53000000*/
		.start=S3C24XX_PA_WATCHDOG,
		.end= S3C24XX_PA_WATCHDOG + S3C24XX_SZ_ WATCHDOG - 1,/*1M的地址空间*/
		.flags = IORESOURCE_MEM,/*I/0内存资源*/
	};
	[1]={
		.start=IRQ_WDT,/*看门狗的开始中断号,被定义为80*/
		.end=IRO_WDT,/*看门狗的结束中断号*/
		.flags=IORESOURCE_IRQ,/*中断的IRQ资源*/
		}
};

从代码中可以看出s3c2440处理器只使用了I/0内存和IRQ资源。这里的I/O内存指向看门狗的WTCON、WTDAT和WTCNT寄存器。为了更清楚地了解资源的概念,将资源结构体resource列出如下:

struct resource 
{
	resource size t start;/*资源的开始地址,resource size是32位或者64位的无符号整型*/
	resource_size_t end;/*资源的结束地址*/
	const char *name:/*资源名*/
	unsigned 1ong flags;/*资源的类型*/
	struct resource *parent,*sibling, *child;/*用于 构建资源的树形结构*/

resource结构的start 和end的类型是无符号整型。在32位平台上是32位整型:在64位平台上是64位整型。flags 标志表示资源的类型,有10端口(IORESOURCE I0)、内存资源(IORESOURCEMEM)、中断号(IORESOURCE_IRQ)和DMA资源(IORESOURCE DMA)等。parent、 sibling 和child 指针用于将资源构建成一个树, 加快内核的资源访问和管理,驱动程序员不需要关心。

3.平台设备相关的操作函数

通过platform add devices()函数可以将一-组设备添加到系统中,其主要完成以下两个功能:
(1)分配平台设备所使用的资源,并将这些资源挂接到资源树中。
(2)初始化device设备,并将设备注册到系统中。
其函数的原型如下:

int platform_add_devices (struct platform_device **devs, int num);

该函数的第1个参数devs是平台设备数组的指针,第2个参数是平台设备的数量。
通过platform get. _resource()函数可以获得平台设备中的resource资源。

struct resource *platform_get_resource (struct platform_device *dev, unsigned int type, unsigned int num) ;

该函数的第1个参数dev是平台设备的指针。第2个参数type是资源的类型,这些类型可以是1/O端口(IORESOURCE_ IO)、内存资源( IORESOURCE_MEM)、中断号:( IORESOURCE_IRQ) 和DMA资源( IORESOURCE DMA)等。第3个参数num是同种资源的索引。例如一-个平台设备中,有3个IORESOURCE MEM资源,如果要获得第2个资源,那么需要使num等于1。从平台设备pdev中获得第一个内存资源的例子如下所示。

struct resource *res:
res=platform_get_resource(pdev, IORESOURCE_MEM, 0);

2.3平台设备驱动

每一-个平台设备都对应一个平台设备驱动,这个驱动用来对平台设备进行探测、移除、关闭和电源管理等操作。平台设备用驱动platfrm. _device 结构体来描述,其定义如下:

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_1ate) (struct platform_device *,pm_message_t state);/*挂起之后调用的函数*/
	int (*resume_early) (struct platform_device *);//恢复正常状态之前调用的函数
	int (*resume)(struct platform_device *);//恢复正常状态的函数
	struct device_driver driver;//设备驱动核心结构
};

在文件lnux-2.6.29.4\drivers\watchdog\s3c2410_wdt.c中定义了处理器的看门狗平台设备驱动。其中-些函数没有用处,所以没有定义,代码如下:

static struct platform_driver_s3c2410wdt_driver={
	.probe=s3c2410wdt_probe,/*看门狗探测函数*/
	.remove=s3c2410wdt_remove,/*看门狗移除雨数*/
	.shutdown=s3c2410wdt_shutdown,/*看门狗关闭函数*/
	.suspend=s3c241Owdt_ suspend,/*看门狗挂起函数*/
	.resume= s3c2410wdt_resume,/*看门狗恢复函数*/
	.driver={
		.owner=THIS_MODULE,
		.name="s3c2410-wdt",/*设备的名字,与平台设备中的名字对应*/
		},
};
  1. probe()函数

一般来说,在内核启动时,会注册平台设备和平台设备驱动程序。内核将在适当的时候,将平台设备和平台设备驱动程序连接起来。连接的方法,是用系统中的所有平台设备和所有已经注册的平台驱动进行匹配。匹配函数由platform_ match0实现, 代码如下:

static int platform_match(struct device *dev, struct device_driver *drv)
{
	struct platform device *pdev;/*平台设备指针*/
	pdev = container_of (dev, struct platform_device, dev);/*从device获得platform_device */
	return (strcmp (pdev->name, drv->name)==0);/*比较设备名*/
}

该函数比较平台设备的name字段和驱动的name字段,相同时,表示匹配成功;不同时,表示驱动不匹配该设备。函数匹配成功返回1,失败时返回0。该函数将由内核自已调用,当设备找到对应的驱动时,会触发probe()函数,所以probe()兩数一般 是驱动加载成功后调用的第-一个函数,在该函数中可以申请设备所需要的资源。

  1. remove()函数

如果设备可以移除,为了减少所占用的系统资源,那么应该实现remove()函数。该函数一般与probe0函数对应,在probe()函数中申请的资源,应该在remove)丽数中释放。

  1. shutdown()、 suspend()和resume()函数

shutdown()函数当设备断电或者关闭时被调用;suspend()函数使设备处于低功耗状态;resume()函数使设备从低功耗恢复到正常状态。

2.4平台设备驱动的注册和注销

内核关于平台设备最主要的两个函数是注册和注销函数,本节将对这两个函数进行说明。

1.注册函数platform driver register()

需要将平台设备驱动注册到系统中才能使用,内核提供了platform_driver_register()函数实现这个功能。该函数如下:

int platform_driver register (struct platform_driver *drv)
{
	drv->driver.bus = &platform_bus_type;/*平台总线类型*/
	/*如果定义了probe函数,该函数将覆盖driver中定义的函数*/
	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; /*默认的关闭函数*/
	if (drv->8uspend)
		drv->driver.suspend =platform_drv_suspend;/*默认的挂起函数*/
	if (drv->resume)
		drv->driver.resume = platform_drv_ resume ;/*默认的恢复函数*/
	return driver_register (&drv->driver);/*将驱动注册到系统中*/
}

在平台驱动platform_driver和其父结构driver中相同的方法。如果平台设备驱动中定义probe()方法。那么内核将会调用平台驱动中的方法;如果平台设备驱动中没有定义probe()方法,那么将调用driver中的对应方法。platform_driver_register0函数用来完成这种功能,并注册设备驱动到内核中。platform. driver 注册到内核后。

2.注销函数platform. driver unregister()

当模块卸载时需要调用雨数platfom_driver_unregsterO注销平台设备驱动,该函数的原型如下:

void platform_driver_unregister (struct platform_driver *drv) 

2.5混杂设备

混杂设备并没有一个明确的定义。由于设备号比较紧张,所以一些不相关的设备可以使用同一个主设备,不同的次设备号。主设备号通常是10。由于这个原因,一-些设备也可以叫做混杂设备。混杂设备用结构体miscdevice来表示,代码如下所示:

struct miscdevice
{
	int minor;/*次设备号*/
	const char *name;/*混杂设备的名字*/
	const struct file_operations *fops;/*设备的操作函数,与字符设备相同*/
	struct list_head list;/*连向下一个混杂设备的链表*/
	struct device *parent;/*指向父设备*/
	struct device *this_device;/*指向当前设备结构体*/
};

混杂设备中的一个重要成员是fops, 其是一个file_operations 的指针。这里的file_operations 结构与cdev中的结构一样。 开发者完全可以用cdev 代替miscdevice实现看门狗驱动程序。看门狗的混杂设备定义如下代码所示。

static struct miscdevice s3c2410wdt_miscdev =
{
	.minor=WATCHDOG_MINOR,/*次设备号,定义为130*/
	.name= "watchdog",/*混杂设备的名字*/
	.fops=&s3c2410wdt fops,/*混杂设备的操作指针*/
};

2.6混杂设备的注册和注销

驱动程序中需要对混杂设备进行注册和注销,内核提供了misec_register 0和misc_ deregister)两 个函数。

1.注册函数misc register ()

混杂设备的注册非常简单,只需要调用mise registr()函数,并传递一个混杂设备的指针就可以了。其原型如下:
int misc_register (struct miscdevice *misc)
该函数内部检查次设备号是否合法,如果次设备号被占用,则返回设备忙状态。如果misedevice 的成员minor为255,则尝试动态申请一一个次设备号。当次设备号可用时,函数会将混杂设备注册到内核的设备模型中去。

2.注销函数misc_ deregister)

与misc_ regist()函 数对应的是misc_deregister)函数,其原型如下:
int misc_deregister (struct miscdevice *misc) ;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值