linux設備驅動歸納總結(十一):簡單的看門狗驅動

linux設備驅動歸納總結(十一):寫個簡單的看門狗驅動


xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

設備驅動的歸納已經差不多了,趁着知識點還沒有遺忘,寫點代碼鞏固一下,來個簡單的看門狗驅動——靜態平臺類的雜設備看門狗驅動,有定時和重啓兩個基本功能

xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx


一、S3C2440中的看門狗——具體請看s3c2440文檔


看門狗應該可以算是S3C2440中最簡單的一個設備的,僅僅只有三個寄存器需要配置。S3C2440中的看門狗有兩種功能(只能二選一):

1、復位功能,在指定時間內,如果沒有執行喂狗操作。指定時間到達後會執行系統重啓操作。

2、定時功能,每隔指定的時間,看門狗就會往處理器發送中斷,執行中斷處理函數。


接下來就要看看控制看門狗的三個寄存器:

1、控制寄存器——WTCON

首先要看看分頻係數Prescaler value[15:8]和時鐘分頻Clock select[4:3]。看門狗的時鐘頻率就是通過這兩個值分頻得出的:

t_watchdog = 1/[ PCLK / (Prescaler value + 1) / Division_factor ]

每個(1/t_watchdog)秒,看門狗計數器減一。

Watchdog timer[5]是看門狗的是能控制位,初始化時必須將此爲置一,不然看門狗無法操作。

Interrupt generation[2]是控制中斷的產生,如果你使用看門狗來執行定時功能,置一代表使能看門狗產生中斷,反之不產生中斷。

Reset enable/disable[0]用控制看門狗是否復位,如果置一,代表當時間到達後系統重啓。


2、數據寄存器WTDAT和計數寄存器WTCNT


這兩個都是用於存放16位數據的寄存器。

1、如果是復位模式,從WTCON[5]置一開始,每隔(1/t_watchdog)秒,WTCNT中的值減一,直到WTCNT0,系統就會復位。在WTCNT還沒有爲0前,可以重新往WTCNT中賦值,這樣WTCNT就會從新的數值開始減一,這就是所謂的喂狗操作,重複地在WTCNT0前喂狗,就可以讓系統不復位。

2、如果是定時模式, 從WTCON[5]置一開始,每隔(1/t_watchdog)秒,WTCNT中的值減一,直到WTCNT0,看門狗往處理器發送中斷信號,並自動將WTDAT賦值給WTCNT,讓WTCNT重新開始減一。這樣的重複操作就實現了看門狗的定時。

注意:不管是哪一種模式,一開始時看門狗都不會將WTDAT的值賦給WTCNT,都是直接以WTCNT的值開始計數。所以,在是能看門狗之前,必須先設備WTCNT的值,指定時間長短。


接下來的程序我需要的時間間隔是1秒,所以,Prescaler value[15:8]我賦值爲99Clock select[4:3]我賦值爲128S3C2440中的PCLK定義爲50000000,通過公式可以計算出:

t_watchdog = 1/[ PCLK / (Prescaler value + 1) / Division_factor ] = 3906.25

所以,看門狗的計數間隔是(1/3906.25)秒,1秒需要計數大約3906


看門狗介紹完畢,接着就開始寫驅動了,在寫驅動前貼張之前我介紹過的圖,我寫驅動的步驟:

接在來我就會按這個順序來寫個簡單的看門狗驅動,實現看門狗的兩種功能,定時和復位。


xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx


二、第一個看門狗程序,代碼路徑11th_wdt/1st


寫驅動前先要寫一些基本的操作代碼,檢驗一下該設備是否能正常工作。


第一個看門狗程序做了三步,簡單實現了看門狗的復位操作:

1、定義了一個維護看門狗數據的結構體:

9 struct _wdt_t {

10      unsigned long phys, virt; //存放物理地址和對應的虛擬地址

11     unsigned long wtcon, wtdat, wtcnt; //存放寄存器

12     unsigned long reg;

13

14     void (*init_reset)(struct _wdt_t *); //操作看門狗的函數指針

15 };

2、實現了看門狗的復位操作:

19 static void s3c_wdt_init_reset(struct _wdt_t *wdt)

20 {

21     iowrite32((int)(WDT_1S * 10), wdt->wtdat); //其實這個不設置也可以

22     iowrite32((int)(WDT_1S * 10), wdt->wtcnt); //設置10秒後系統復位

23     iowrite32(0x6339, wdt->wtcon); //設置wtcon寄存器爲0x6339,使能了看門狗和復位功能

24 }

3、封裝了設備的初始化和註銷函數:

26 int init_wdt_device(struct _wdt_t *wdt)

27 {

28     int ret = 0;

29     //ioremap

30     wdt->phys = 0x53000000;

31     wdt->virt = (unsigned long)ioremap(wdt->phys, 0x0c);

32      wdt->wtcon = wdt->virt + 0x0;

33      wdt->wtdat = wdt->virt + 0x4;

34      wdt->wtcnt = wdt->virt + 0x8;

35

36     //function

37      wdt->init_reset = s3c_wdt_init_reset;

38     return ret;

39 }

40

41 void destroy_wdt_device(struct _wdt_t *wdt)

42 {

43     iounmap((void *)wdt->virt);

44 }

寫完後編譯,效果就是加載模塊後十秒系統重啓,看門狗的復位功能驗證成功。


xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx


三、第二個看門狗程序,代碼路徑11th_wdt/2nd


第一個看門狗程序實現了復位操作,接下來我就要在原來代碼的基礎上加上看門狗的定時功能。很簡單,只貼上就三個函數,其他細節請看源代碼:

1、初始化看門狗,設置爲定時功能,每隔一秒產生一次中斷:

29 static void s3c_wdt_init_interrupt(struct _wdt_t *wdt)

30 {

31     iowrite32((int)(WDT_1S), wdt->wtdat);

32     iowrite32((int)(WDT_1S), wdt->wtcnt);

33     iowrite32(0x6338, wdt->wtcon);

34 }

2、初始話後還不能產生中斷,還需要實現開啓和關閉中斷操作,其實就是設置寄存器的一位:

36 static void s3c_wdt_start(struct _wdt_t *wdt)

37 {

38      wdt->reg = ioread32(wdt->wtcon);

39      wdt->reg |= (1 << 2);

40     iowrite32(wdt->reg, wdt->wtcon);

41 }

42

43 static void s3c_wdt_stop(struct _wdt_t *wdt)

44 {

45     wdt->reg = ioread32(wdt->wtcon);

46     wdt->reg &= (1 << 2);

47     iowrite32(wdt->reg, wdt->wtcon);

48 }


既然是產生了中斷,就要註冊中斷和實現中斷處理函數,中斷的註冊操作應該放在設備初始化函數中:

50 irqreturn_t wdt_irq_handler(int irqno, void *dev_id)

51 {

52     printk("wang wang wang ...\n");

53     return IRQ_HANDLED;

54 }

55

56 int init_wdt_device(struct _wdt_t *wdt)

57 {

58     int ret = 0;

59     //ioremap

60     wdt->phys = 0x53000000;

61     wdt->virt = (unsigned long)ioremap(wdt->phys, 0x0c);

62     wdt->wtcon = wdt->virt + 0x0;

63     wdt->wtdat = wdt->virt + 0x4;

64     wdt->wtcnt = wdt->virt + 0x8;

65

66     //irq

67     ret = request_irq(IRQ_S3C2440_WDT, wdt_irq_handler, IRQF_TRIGGER_NONE,

68                                         "s3c2440_wdt-irq", NULL);

69     if(ret){

70         printk("request wdt-irq failed!\n");

71         goto err;

72     }

73

74     //function

75     wdt->init_reset = s3c_wdt_init_reset;

76     wdt->init_interrupt = s3c_wdt_init_interrupt;

77     wdt->start = s3c_wdt_start;

78     wdt->stop = s3c_wdt_stop;

79

80     return ret;

81

82 err:

83     iounmap((void *)wdt->virt);

84     return ret;

85 }

86

87 void destroy_wdt_device(struct _wdt_t *wdt)

88 {

89     free_irq(IRQ_S3C2440_WDT, NULL);

90     iounmap((void *)wdt->virt);

91 }

寫完後編譯,效果就是加載模塊後每隔一秒打印出一句話,看門狗的定時功能驗證成功。


注意:一般是不能加載成功的,運行命令”cat /proc/interrupt”就知道,系統中本身就註冊了一個看門狗中斷,爲了能夠執行成功,方法有兩個:(之前介紹過的兩男共享一妞)

1、幹掉系統中的看門狗中斷:

方法:配置內核時不要選上看門狗設備。

2、修改內核源代碼,添加共享標記。

我這裏使用的是第一種解決方法。


xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx


四、第三個看門狗程序,代碼路徑11th_wdt/3rd


上面已經實現了圖上的三步:定義面向對象結構體,實現基本的硬件操作函數、定義設備的初始化和註銷函數。

接下來就將它修改成靜態平臺類驅動。方法很簡單,將之前放在wdt_init的操作放在probe函數中,配對成功後自動調用,將之前放在wdt_exit的操作放在remove函數中。

貼上部分代碼:

97 static int s3c_wdt_probe(struct platform_device *pdev)

98 {

99       printk("[%s]\n", __FUNCTION__);

100     my_wdt.phys = pdev->resource[0].start;

101     my_wdt.irqno = pdev->resource[1].start;

102     init_wdt_device(&my_wdt);

103     my_wdt.init_interrupt(&my_wdt);

104     my_wdt.start(&my_wdt);

105     return 0;

106 }

107

108 static int s3c_wdt_remove(struct platform_device *pdev)

109 {

110     printk("[%s]\n", __FUNCTION__);

111     my_wdt.stop(&my_wdt);

112     destroy_wdt_device(&my_wdt);

113     return 0;

114 }

115

116 static struct platform_driver wdt_pdrv = {

117     .probe = s3c_wdt_probe,

118     .remove = s3c_wdt_remove,

119     .driver = {

120         .name = "s3c_wdt_xb",

121     },

122 };

123

124 static int __init wdt_init(void)

125 {

126     platform_driver_register(&wdt_pdrv);

127     printk("hello wdt!\n");

128     return 0;

129 }

130

131 static void __exit wdt_exit(void)

132 {

133     platform_driver_unregister(&wdt_pdrv);

134     printk("bye wdt!\n");

135 }

136

137 module_init(wdt_init);

138 module_exit(wdt_exit);


既然說是靜態平臺類驅動,那就是需要修改內核代碼,方法在linux設備驅動歸納總結(九):1.platform設備驅動有介紹,在三處文件添加代碼:

1arch/arm/mach-s3c2440/mach-mini2440.c

250 static struct platform_device *mini2440_devices[] __initdata = {

251     &s3c_device_usb,

252     &s3c_device_rtc,

253     &s3c_device_lcd,

254     &s3c_device_wdt,

255     &s3c_device_led,

256     &s3c_device_wdt_xb, //這是我新加的

257     &s3c_device_i2c0,

258     &s3c_device_iis,

259     &s3c_device_dm9k,

260     &net_device_cs8900,

261     &s3c24xx_uda134x,

262 };

2arch/arm/plat-s3c24xx/devs.c

379 /* Watchdog xiaobai*/

380

381 static struct resource s3c_wdt_xb_resource[] = {

382     [0] = {

383         .start = 0x53000000,

384         .end = 0x530000ff,

385         .flags = IORESOURCE_MEM,

386     },

387     [1] = {

388         .start = IRQ_S3C2440_WDT,

389         .end = IRQ_S3C2440_WDT,

390         .flags = IORESOURCE_IRQ,

391     }

392 };

393

394 struct platform_device s3c_device_wdt_xb = {

395     .name = "s3c_wdt_xb",

396     .id = -1,

397     .num_resources = ARRAY_SIZE(s3c_wdt_xb_resource),

398     .resource = s3c_wdt_xb_resource,

399 };

400

401 EXPORT_SYMBOL(s3c_device_wdt_xb);

3arch/arm/plat-s3c/include/plat/devs.h

27 extern struct platform_device s3c_device_dm9k;

28 extern struct platform_device net_device_cs8900;

29 extern struct platform_device s3c_device_fb;

30 extern struct platform_device s3c_device_usb;

31 extern struct platform_device s3c_device_lcd;

32 extern struct platform_device s3c_device_wdt;

33 extern struct platform_device s3c_device_led;

34 extern struct platform_device s3c_device_wdt_xb; //這是我添加的

35 extern struct platform_device s3c_device_i2c0;

36 extern struct platform_device s3c_device_i2c1;

37 extern struct platform_device s3c_device_iis;


修改後重新編譯內核,加載模塊後,內核會自動調用probe函數,具體效果就是每隔一秒打印一句話。


xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx


五、第四個看門狗程序,代碼路徑11th_wdt/4th


接下來就要註冊雜設備類驅動,並且提供ioctl命令給用戶空間控制看門狗。


先要在頭文件中實現幾個命令:

/*11th_wdt/4th/ioctl_wdt.h*/

1 #ifndef _WDT_H

2 #define _WDT_H

3

4 #define MAGIC 'x'

5 #define WDT_RESET _IO(MAGIC, 0) //產生一個數字

6 #define WDT_INTERRUPT _IO(MAGIC, 1)

7 #define WDT_START _IO(MAGIC, 2)

8 #define WDT_STOP _IO(MAGIC, 3)

9

10 #endif /* _WDT_H */

再看看ioctl的實現,很簡單,四個命令對應四個不同的操作:

112 int s3c_wdt_ioctl (struct inode *node, struct file *filp,

113 unsigned int cmd, unsigned long args)

114 {

115     switch(cmd){

116         case WDT_RESET:

117             my_wdt.init_reset(&my_wdt);

118             break;

119         case WDT_INTERRUPT:

120             my_wdt.init_interrupt(&my_wdt);

121             break;

122         case WDT_START:

123             my_wdt.start(&my_wdt);

124             break;

125         case WDT_STOP:

126             my_wdt.stop(&my_wdt);

127             break;

128         default:

129             printk("unknow ioctl cmd!\n");

130             return - EINVAL;

131     }

132     return 0;

133 }

134

135 static struct file_operations wdt_fops = {

136     .ioctl = s3c_wdt_ioctl,

137 };

接着是實現雜設備類驅動:

139 /*******************

140 * misc class *

141 *******************/

142

143 static struct miscdevice wdt_misc = {

144     .name = "s3c_wdt",

145     .fops = &wdt_fops,

146 };

147

148 /*******************

149 * platform總線操作*

150 *******************/

151 static int s3c_wdt_probe(struct platform_device *pdev)

152 {

153     printk("[%s]\n", __FUNCTION__);

154     my_wdt.phys = pdev->resource[0].start;

155     my_wdt.irqno = pdev->resource[1].start;

156

157     init_wdt_device(&my_wdt);

158     misc_register(&wdt_misc);

159     return 0;

160 }

161

162 static int s3c_wdt_remove(struct platform_device *pdev)

163 {

164     printk("[%s]\n", __FUNCTION__);

165     misc_deregister(&wdt_misc);

166     destroy_wdt_device(&my_wdt);

167     return 0;

168 }

最後就可以寫個應用程序驗證一下了:

/*11th_wdt/4th/app.c */

8 #include "ioctl_wdt.h"

9

10 int main(int argc, char *argv[])

11 {

12     int fd;

13

14     fd = open("/dev/s3c_wdt", O_RDWR);

15     if(fd < 0)

16         perror("open");

17

18     printf("./app [func]\n");

19     printf("func : reset interrupt start stop\n");

20     printf("[%s]\n", argv[1]);

21

22     if(!strncasecmp("reset", argv[1], 5))

23         ioctl(fd, WDT_RESET);

24     if(!strncasecmp("interrupt", argv[1], 9))

25         ioctl(fd, WDT_INTERRUPT);

26     if(!strncasecmp("start", argv[1], 5))

27         ioctl(fd, WDT_START);

28     if(!strncasecmp("stop", argv[1], 4))

29         ioctl(fd, WDT_STOP);

30

31     return 0;

32 }

編譯後驗證一下,可以使用./app reset啓動復位功能,也可以./app interrupt & ./app start啓動定時功能。


xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx


六、第五個看門狗程序,代碼路徑11th_wdt/5th


看門狗的驅動就基本完成了,最後還有個功能要補充,喂狗,直接上代碼:

65 static void s3c_wdt_feed(struct _wdt_t *wdt)

66 {

67     iowrite32((int)(WDT_1S * 10), wdt->wtcnt);

68 }

然後稍稍修改一下其他代碼就可以了。

編譯後運行,執行./app reset後,只要在10秒內執行./app feed,系統就不會重啓。


xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx


七、總結


簡單的看門狗驅動基本上就完成了,當然還有很多需要完善的地方,如設定定時的時間等。具體包含了以下知識點:

1、字符設備的方法;

2io內存——ioremap

3、中斷註冊;

4platform設備驅動;

5、雜設備驅動;


xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

發佈了0 篇原創文章 · 獲贊 10 · 訪問量 12萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章