小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

Linux混雜設(shè)備驅(qū)動(dòng)(watchdog)

 nooly 2011-09-22

WatchDog Timer驅(qū)動(dòng)

混雜設(shè)備

Misc(或miscellaneous)驅(qū)動(dòng)是一些擁有著共同特性的簡(jiǎn)單字符設(shè)備驅(qū)動(dòng)。內(nèi)核抽象出這些特性而形成一些API(在文件drivers/char/misc.c中實(shí)現(xiàn)),以簡(jiǎn)化這些設(shè)備驅(qū)動(dòng)程序的初始化。所有的misc設(shè)備被分配同一個(gè)主設(shè)備號(hào)MISC_MAJOR(10),但是每一個(gè)可以選擇一個(gè)單獨(dú)的次設(shè)備號(hào)。如果一個(gè)字符設(shè)備驅(qū)動(dòng)要驅(qū)動(dòng)多個(gè)設(shè)備,那么它就不應(yīng)該用misc設(shè)備來(lái)實(shí)現(xiàn)。

通常情況下,一個(gè)字符設(shè)備都不得不在初始化的過(guò)程中進(jìn)行下面的步驟:

通過(guò)alloc_chrdev_region()分配主/次設(shè)備號(hào)。

使用cdev_init()cdev_add()來(lái)以一個(gè)字符設(shè)備注冊(cè)自己。

而一個(gè)misc驅(qū)動(dòng),則可以只用一個(gè)調(diào)用misc_register()來(lái)完成這所有的步驟。

所有的miscdevice設(shè)備形成一個(gè)鏈表,對(duì)設(shè)備訪問(wèn)時(shí),內(nèi)核根據(jù)次設(shè)備號(hào)查找對(duì)應(yīng)的miscdevice設(shè)備,然后調(diào)用其file_operations中注冊(cè)的文件操作方法進(jìn)行操作。

Linux內(nèi)核中,使用struct miscdevice來(lái)表示miscdevice。這個(gè)結(jié)構(gòu)體的定義為:

struct miscdevice  {

    int minor;

    const char *name;

    const struct file_operations *fops;

    struct list_head list;

    struct device *parent;

    struct device *this_device;

    const char *nodename;

    mode_t mode;

};

minor是這個(gè)混雜設(shè)備的次設(shè)備號(hào),若由系統(tǒng)自動(dòng)配置,則可以設(shè)置為MISC_DYNANIC_MINOR,name是設(shè)備名。

每一個(gè)misc驅(qū)動(dòng)會(huì)自動(dòng)出現(xiàn)在/sys/class/misc下,而不需要驅(qū)動(dòng)程序作者明確的去做。Linux watchdog timer驅(qū)動(dòng)被實(shí)現(xiàn)為misc 驅(qū)動(dòng),他們被放在drivers/char/watchdog/目錄下。Watchdog 驅(qū)動(dòng)也導(dǎo)出了一個(gè)標(biāo)準(zhǔn)設(shè)備接口到用戶(hù)空間。這樣就可以使符合這個(gè)接口的應(yīng)用程序的實(shí)現(xiàn)獨(dú)立于Watchdog硬件。這個(gè)API在內(nèi)核樹(shù)中的Documentation/watchdog/watchdog-api.txt文件中有詳細(xì)的說(shuō)明。需要watchdog服務(wù)的程序可以操作/dev/watchdog,這個(gè)設(shè)備有一個(gè)misc次設(shè)備號(hào)130。

WDT在內(nèi)核中通常都實(shí)現(xiàn)為misc驅(qū)動(dòng)。

 

Introduction:

 

一個(gè)Watchdog TimerWDT)是一個(gè)在軟件出錯(cuò)的時(shí)候可以復(fù)位計(jì)算機(jī)系統(tǒng)的硬件電路。

通常一個(gè)用戶(hù)空間守護(hù)進(jìn)程會(huì)在正常的時(shí)間間隔內(nèi)通過(guò)/dev/watchdog特殊設(shè)備文件來(lái)通知內(nèi)核的watchdog驅(qū)動(dòng),用戶(hù)空間仍然正常。當(dāng)這樣的一個(gè)通知發(fā)生時(shí),驅(qū)動(dòng)通常會(huì)告訴硬件watchdog一切正常,然后watchdog應(yīng)該再等待一段時(shí)間來(lái)復(fù)位系統(tǒng)。如果用戶(hù)空間出問(wèn)題(RAM錯(cuò)誤,內(nèi)核bug等),則通知將會(huì)停止,然后硬件watchdog將在超時(shí)后復(fù)位系統(tǒng)。

Linuxwatchdog API是一個(gè)相當(dāng)特別的東西,不同的驅(qū)動(dòng)實(shí)現(xiàn)是不同的,而且有時(shí)部分是不兼容的。這個(gè)文檔正是要嘗試著去說(shuō)明已經(jīng)出現(xiàn)的用法,并且使以后的驅(qū)動(dòng)作者把它作為一份參考。

 

最簡(jiǎn)單的 API:

 

所有的設(shè)備驅(qū)動(dòng)都支持的基本的操作模式,一旦/dev/watchdog被打開(kāi),則watchdog激活,并且除非喂狗,否則將在一段時(shí)間之后重啟,這個(gè)時(shí)間被稱(chēng)為timeoutmargin。最簡(jiǎn)單的喂狗方法就是寫(xiě)一些數(shù)據(jù)到設(shè)備。一個(gè)非常簡(jiǎn)單的watchdog守護(hù)進(jìn)程看起來(lái)就像這個(gè)文件這樣:

Documentation/watchdog/src/watchdog-simple.c

 

一個(gè)高級(jí)一些的驅(qū)動(dòng)在喂狗之前,可能還會(huì)做一些其他的事情,比如說(shuō)檢查HTTP服務(wù)器是否依然可以相應(yīng)。

 

當(dāng)設(shè)備關(guān)閉的時(shí)候,除非支持"Magic Close"特性。否則watchdog被關(guān)閉。這并不總是一個(gè)好主意,比如watchdog守護(hù)進(jìn)程出現(xiàn)了bug并且崩潰了,則系統(tǒng)將不會(huì)重啟。因此,某些驅(qū)動(dòng)支持"Disable watchdog shutdown on close", CONFIG_WATCHDOG_NOWAYOUT配置選項(xiàng)。當(dāng)編譯內(nèi)核的時(shí)候這個(gè)選項(xiàng)被設(shè)置為Y,則一旦watchdog被啟動(dòng),則將沒(méi)有辦法能夠停止。這樣,則當(dāng)watchdog守護(hù)進(jìn)程崩潰的時(shí)候,系統(tǒng)仍將在超時(shí)后重啟。Watchdog設(shè)備常常也支持nowayout模塊參數(shù),這樣這個(gè)選項(xiàng)就可以在運(yùn)行時(shí)進(jìn)行控制。

 

Magic Close 特性:

 

如果一個(gè)驅(qū)動(dòng)支持"Magic Close",則除非在關(guān)閉文件前,魔幻字符'V'被發(fā)送到/dev/watchdog,驅(qū)動(dòng)將不停止watchdog。如果用戶(hù)空間守護(hù)進(jìn)程在關(guān)閉文件前沒(méi)有發(fā)送這個(gè)字符,則驅(qū)動(dòng)認(rèn)為用戶(hù)空間崩潰,并在關(guān)閉watchdog前停止喂狗。

這樣的話(huà),如果沒(méi)有在一定的時(shí)間內(nèi)重新打開(kāi)watchdog,則將導(dǎo)致一個(gè)重啟。

 

ioctl API:

 

所有標(biāo)準(zhǔn)的驅(qū)動(dòng)也應(yīng)該支持一個(gè)ioctl API。

 

喂狗使用一個(gè)ioctl

所有的驅(qū)動(dòng)都有一個(gè)ioctl接口支持至少一個(gè)ioctl命令,KEEPALIVE。這個(gè) ioctl 做的事和一個(gè)寫(xiě)watchdog設(shè)備完全一樣,所以,上面程序的主循環(huán)可以替換為:

    while (1) {

       ioctl(fd, WDIOC_KEEPALIVE, 0);

       sleep(10);

    }

ioctl的參數(shù)被忽略。

 

設(shè)置和獲得超時(shí)值:

對(duì)于某些驅(qū)動(dòng)來(lái)說(shuō),在上層使用SETTIMEOUT ioctl命令改變watchdog的超時(shí)值是可能的,那些驅(qū)動(dòng)在他們的選項(xiàng)與中有WDIOF_SETTIMEOUT標(biāo)志。參數(shù)是一個(gè)代表以秒為單位的超時(shí)值,驅(qū)動(dòng)將在同一個(gè)變量中返回實(shí)際使用的超時(shí)值,這個(gè)超時(shí)值可能由于硬件的限制,而不同于所請(qǐng)求的超時(shí)值

    int timeout = 45;

    ioctl(fd, WDIOC_SETTIMEOUT, &timeout);

    printf("The timeout was set to %d seconds\n", timeout);

如果設(shè)備的超時(shí)值的粒度只能到分鐘,則這個(gè)例子可能實(shí)際打印"The timeout was set to 60 seconds"。

自從Linux 2.4.18內(nèi)核,通過(guò)GETTIMEOUT ioctl命令查詢(xún)當(dāng)前超時(shí)值也是可能的:

    ioctl(fd, WDIOC_GETTIMEOUT, &timeout);

    printf("The timeout was is %d seconds\n", timeout);

 

預(yù)處理:

Pretimeouts:

一些watchdog定時(shí)器,可以被設(shè)置為,在他們實(shí)際復(fù)位系統(tǒng)前,有一個(gè)觸發(fā)。這可能通過(guò)一個(gè)NMI,中斷,或其他機(jī)制。這將允許在它復(fù)位系統(tǒng)前Linux去記錄一些有用的信息(比如panic信息和內(nèi)核轉(zhuǎn)儲(chǔ))。

    pretimeout = 10;

    ioctl(fd, WDIOC_SETPRETIMEOUT, &pretimeout);

注意,預(yù)超時(shí)值應(yīng)該是一個(gè)相對(duì)于超時(shí)值提前的秒數(shù)。而不是直到預(yù)超時(shí)的秒數(shù)。

比如,如果你設(shè)置超時(shí)值為60秒,預(yù)超時(shí)值為10秒,那么預(yù)超時(shí)將在50秒后到達(dá)。設(shè)置為0則是禁用它。預(yù)超時(shí)還有一個(gè)get功能:

    ioctl(fd, WDIOC_GETPRETIMEOUT, &timeout);

    printf("The pretimeout was is %d seconds\n", timeout);

不是所有的watchdog驅(qū)動(dòng)都支持一個(gè)預(yù)超時(shí)的。

 

獲得重啟前的秒數(shù)

一些watchdog驅(qū)動(dòng)有一個(gè)報(bào)告在重啟前的剩余時(shí)間的功能。WDIOC_GETTIMELEFT就是返回重啟前的秒數(shù)的ioctl命令。

    ioctl(fd, WDIOC_GETTIMELEFT, &timeleft);

    printf("The timeout was is %d seconds\n", timeleft);

 

環(huán)境監(jiān)視:

Environmental monitoring:

 

所有的watchdog驅(qū)動(dòng)都被要求返回更多關(guān)于系統(tǒng)的信息,有些返回溫度,風(fēng)扇和功率水平監(jiān)測(cè),依稀可以告訴你上一次重啟系統(tǒng)的原因。GETSUPPORT ioctl可以用來(lái)查詢(xún)?cè)O(shè)備可以做什么:

    struct watchdog_info ident;

    ioctl(fd, WDIOC_GETSUPPORT, &ident);

 

ident結(jié)構(gòu)中返回的字段是:

        identity     一個(gè)標(biāo)識(shí)watchdog驅(qū)動(dòng)的字符串

    firmware_version  如果可用的話(huà),就是卡的固件版本

    options           一個(gè)描述設(shè)備支持什么的標(biāo)志

options字段可以有下面的位集,和描述GET_STATUS GET_BOOT_STATUS ioctls可以返回什么種類(lèi)的信息。

 [FIXME -- Is this correct?]

 

WDIOF_OVERHEAT       由于CPU過(guò)熱而復(fù)位

機(jī)器上一次被watchdog復(fù)位是由于溫度過(guò)高。

 

WDIOF_FANFAULT       風(fēng)扇失靈

watchdog監(jiān)測(cè)到一個(gè)系統(tǒng)風(fēng)扇失靈

 

WDIOF_EXTERN1     External relay 1

 

External monitoring relay/source 1 was triggered. Controllers intended for real world applications include external monitoring pins that will trigger a reset.

 

    WDIOF_EXTERN2     External relay 2

 

External monitoring relay/source 2 was triggered

 

    WDIOF_POWERUNDER  Power bad/power fault

 

The machine is showing an undervoltage status

 

    WDIOF_CARDRESET      Card previously reset the CPU

 

The last reboot was caused by the watchdog card

 

    WDIOF_POWEROVER      Power over voltage

 

The machine is showing an overvoltage status. Note that if one level is

under and one over both bits will be set - this may seem odd but makes

sense.

 

    WDIOF_KEEPALIVEPING  Keep alive ping reply

 

The watchdog saw a keepalive ping since it was last queried.

 

    WDIOF_SETTIMEOUT  Can set/get the timeout

 

The watchdog can do pretimeouts.

 

    WDIOF_PRETIMEOUT  Pretimeout (in seconds), get/set

 

 

For those drivers that return any bits set in the option field, the

GETSTATUS and GETBOOTSTATUS ioctls can be used to ask for the current

status, and the status at the last reboot, respectively. 

 

    int flags;

    ioctl(fd, WDIOC_GETSTATUS, &flags);

 

    or

 

    ioctl(fd, WDIOC_GETBOOTSTATUS, &flags);

 

Note that not all devices support these two calls, and some only

support the GETBOOTSTATUS call.

 

Some drivers can measure the temperature using the GETTEMP ioctl.  The

returned value is the temperature in degrees fahrenheit.

 

    int temperature;

    ioctl(fd, WDIOC_GETTEMP, &temperature);

 

Finally the SETOPTIONS ioctl can be used to control some aspects of

the cards operation; right now the pcwd driver is the only one

supporting this ioctl.

 

    int options = 0;

    ioctl(fd, WDIOC_SETOPTIONS, options);

 

The following options are available:

 

    WDIOS_DISABLECARD Turn off the watchdog timer

    WDIOS_ENABLECARD  Turn on the watchdog timer

    WDIOS_TEMPPANIC      Kernel panic on temperature trip

 

[FIXME -- better explanations]

 

 

S3C2440A SoC與看門(mén)狗有關(guān)的寄存器只有三個(gè):看門(mén)狗控制寄存器WDTCON,看門(mén)狗數(shù)據(jù)寄存器WDTDAT和看門(mén)狗計(jì)數(shù)寄存器WDTCNT。驅(qū)動(dòng)在同一時(shí)間只允許一個(gè)進(jìn)程打開(kāi),所以在open()方法中鎖定,也就是獲得一個(gè)信號(hào)量,在release()方法中解除鎖定。然后就是根據(jù)內(nèi)核文檔中的WDT API的語(yǔ)義進(jìn)行盡可能豐富的實(shí)現(xiàn)。

#include <linux/init.h>

#include <linux/module.h>

#include <linux/miscdevice.h>

#include <linux/watchdog.h>

#include <linux/fs.h>

#include <linux/ioport.h>

#include <asm/io.h>

#include <linux/moduleparam.h>

#include <linux/semaphore.h>

#include <asm/uaccess.h>

#include "s3c2440wdt.h"

void *wdtmem = NULL;

int nowayout = NOWAYOUT;

int closetag = 0;

module_param(nowayout, int, S_IRUGO);

unsigned int wdt_timeout = DEFAULT_WATCHDOG_TIMEOUT;

struct semaphore sem;

static int s3c2440wdt_open(struct inode *inode, struct file *filp)

{

  down_interruptible(&sem);

  iowrite32(wdt_timeout, wdtmem + WDTDAT);

  iowrite32(wdt_timeout, wdtmem + WDTCNT);

  iowrite32(DEFAULT_WDTCON, wdtmem + WDTCON);

  return 0;

}

static int s3c2440wdt_close(struct inode *inode, struct file *filp)

{

  unsigned int wdtdat = 0;

  /* If CONFIG_WATCHDOG_NOWAYOUT is chosen during kernel

     configuration, do not disable the watchdog even if the

     application desires to close it.

  */

  if(nowayout)

    return 0;

  if(closetag == 0)

    return 0;

#ifndef CONFIG_WATCHDOG_NOWAYOUT

  /*Disable watchdog */

  wdtdat = ioread32(wdtmem + WDTCON);

  wdtdat &= ~(1 << WENABLE_SHIFT);

  iowrite32(wdtdat, wdtmem + WDTCON);

#endif

  up(&sem);

  return 0;

}

static ssize_t s3c2440wdt_write(struct file *filp, const char __user *buf, size_t count, loff_t *pos)

{

  char usrdata = 0;

  copy_from_user(&usrdata, buf, sizeof(usrdata));

  if(usrdata == MAGIC_CHAR){

    closetag = 1;

  }else {

    closetag = 0;

  }

  iowrite32(wdt_timeout, wdtmem + WDTCNT);

  return 0;

}

static int s3c2440wdt_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)

{

  unsigned int realtimeout = 0;

  int timeout;

  struct watchdog_info *ident;

  switch(cmd){

  case WDIOC_KEEPALIVE:

    /* Write to the watchdog.Application can invoke

       this ioctl instead of writing to the device */

    iowrite32(wdt_timeout, wdtmem + WDTCNT);

    break;

  case WDIOC_SETTIMEOUT:

    copy_from_user(&timeout, (int *)arg, sizeof(int));

    if(timeout > 40)

      timeout = 40;

    copy_to_user((int *)arg, &timeout, sizeof(int));

    wdt_timeout = ((unsigned int)timeout) * PER_SECOND;

    break;

  case WDIOC_GETTIMEOUT:

    timeout = (int)(wdt_timeout / PER_SECOND);

    copy_to_user((int *)arg, &timeout, sizeof(int));

    break;

  case WDIOC_GETTIMELEFT:

    realtimeout = ioread32(wdtmem + WDTCNT);

    realtimeout /= PER_SECOND;

    timeout = (int)realtimeout;

    copy_to_user((int *)arg, &timeout, sizeof(int));

    break;

  case WDIOC_GETSUPPORT:

    ident = kmalloc(sizeof(*ident), GFP_KERNEL);

    if(ident == NULL){

      printk(KERN_NOTICE "s3c2440wdt get memory failure.\n");

      return -EINVAL;

    }

    memset(ident, 0, sizeof(*ident));

    sprintf(ident->identity, "%s", "s3c2440wdt");

    ident->options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING;

    copy_to_user((char *)arg, ident, sizeof(*ident));

    break;

  default:

    return -ENOTTY;

  }

  return 0;

}

 

struct file_operations s3c2440wdt_fops = {

  .owner = THIS_MODULE,

  .open = s3c2440wdt_open,

  .release = s3c2440wdt_close,

  .write = s3c2440wdt_write,

  .ioctl = s3c2440wdt_ioctl,

};

static struct miscdevice s3c2440_wdt_dev = {

  .minor = WATCHDOG_MINOR,

  .name = "s3c2440wdt",

  .fops = &s3c2440wdt_fops,

  .nodename = "watchdog",

};

 

static int s3c2440wdt_init(void)

{

  if(request_mem_region(WDTMEMBASE, WDTMEMLEN, "s3c2440wdt") == NULL){

    printk(KERN_ALERT "Get wdt register memory failure.\n");

    return -EBUSY;

  }

  wdtmem = ioremap(WDTMEMBASE, WDTMEMLEN);

  init_MUTEX(&sem);

  misc_register(&s3c2440_wdt_dev);

  printk(KERN_ALERT "The initialization has been completed successfully!\n");

  return 0;

}

 

static void s3c2440wdt_exit(void)

{

  misc_deregister(&s3c2440_wdt_dev);

  iounmap(wdtmem);

  release_mem_region(WDTMEMBASE, WDTMEMLEN);

  printk(KERN_ALERT "modoule s3c2440wdt eixt.\n");

}

module_init(s3c2440wdt_init);

module_exit(s3c2440wdt_exit);

 

MODULE_LICENSE("Dual BSD/GPL");

MODULE_AUTHOR("Hanpfei, hanpfei@gmail.com");

MODULE_DESCRIPTION("The drivers for mini2440's watch dog timer.");

MODULE_VERSION("v1.0");

MODULE_ALIAS("mini2440-wdt");

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶(hù)發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買(mǎi)等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶(hù) 評(píng)論公約

    類(lèi)似文章 更多