《[arm驅動]linux內核時鐘》涉及內核驅動函數四個,內核結構體一個,分析了內核驅動函數一個;可參考的相關應用程序模板或內核驅動模板一個,可參考的相關應用程序模板或內核驅動一個

一、內核定時器
? ?意義:內核定時器是軟件意義上的定時器,最終依賴定時器來實現。時鐘中斷處理程序會喚起Timer_softirq軟中斷,運行當前處理器上到期的所有定時器。
二、linux設備驅動編程
? ?linux內核提供一組函數,時鐘數據結構;這組函數和數據結構使驅動工程師不用關心具體的軟件定時器究竟對應著怎樣的內核和硬件行為。
三、數據結構和函數:
? ?1)數據結構
結構體一)Linux在include/linux/timer.h頭文件中定義了數據結構timer_list來描述一個內核定時器:?

1
2
3
4
5
6
7
struct?timer_list {
???????struct?list_head list;?//定時器列表
???????unsigned?long?expires;?//定時器到期時間,單位HZ等效與秒
???????void?(*function)(unsigned?long);?//處理函數
???????unsigned?long?data;//作為參數被輸入定時器處理函數
???????.................
???};

? ?2)定時器定義及處理函數
? ?a)定義一個內核定時器

1
struct?timer_list my_timer;

內核驅動函數一)b)初始化定時器的timer_list的entry中的next指針為null

1
void?init_timer(struct *timer_list timer);

內核源碼一)init_timer內核源碼

1
2
3
4
5
6
7
8
9
10
void?fastcall init_timer(struct?timer_list *timer)
{
????timer->entry.next = NULL;
????timer->base = __raw_get_cpu_var(tvec_bases);
#ifdef CONFIG_TIMER_STATS
????timer->start_site = NULL;
????timer->start_pid = -1;
????memset(timer->start_comm, 0, TASK_COMM_LEN);
#endif
}


內核驅動函數二)c)添加定時器

1
void?add_timer(struct?timer_list *timer)

內核驅動函數三)d)刪除定時器

1
int?del_timer(struct?timer_list * timer)

內核驅動函數四)e)修改定時器的expire

1
int?mod_timer(struct?timer_list *timer, unsigned?long?expires)


模板一)四、內核定時器使用模板(結合虛擬內存)

a)結構體定義

1
2
3
4
5
#define TIMERDELAY 2
struct?VirtualDisk{
????struct?cdev cdev;//詳細看cdev機制
????timer_list my_timer;//設備要用的定時器
};

b)結構體賦值,添加時鐘,時鐘要處理的函數

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/***********時鐘函數,只需調用initMytimer************/
//完成工作后,往往會延后expires并將定時器再次添加到內核定時器鏈表,以便定時器能再次被觸發
static?void?domytimer(unsigned?long?arg){//just used by initMytimer function
????struct?VirtualDisk* myVirtualDiskp = (struct?VirtualDisk *)(arg);//很重要
????//..........加上你還要做的代碼............
????printk("arg is %ld\n", myVirtualDiskp->mytimer.expires);//打印現在的expires
????myVirtualDiskp->mytimer.expires = jiffies + (TIMERDELAY*HZ);//"HZ"等效與"秒"
????add_timer(&myVirtualDiskp->mytimer);
????//.......................
}
static?void?initMytimer(struct?VirtualDisk *myVirtualDiskp,?int?delay){
????init_timer(&myVirtualDiskp->mytimer);
????myVirtualDiskp->mytimer.data = (unsigned?long) myVirtualDiskp;
????myVirtualDiskp->mytimer.function = &domytimer;
????myVirtualDiskp->mytimer.expires = jiffies + (delay*HZ);
????add_timer(&myVirtualDiskp->mytimer);
}
/***********************/

c)exit()或其他釋放函數中刪除時鐘

1
if(VirtualDiskp)del_timer(&VirtualDiskp->mytimer);

實例一)五、完整代碼

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
//“timerlist_drv”,"timerlist_"
#include <linux/module.h>//模塊所需的大量符號和函數定義
#include <linux/kernel.h>
#include <linux/fs.h>//文件系統相關的函數和頭文件
#include <linux/init.h> //指定初始化和清除函數
#include <linux/delay.h>
#include <linux/cdev.h> //cdev結構的頭文件包含<linux/kdev_t.h>
#include <linux/device.h>
#include <linux/mm.h>
//#include <linux/sched.h>//包含驅動程序使用的大部分內核API的定義,包括睡眠函數以及各種變量聲明
#include <asm/uaccess.h>//在內核和用戶空間中移動數據的函數
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/arch/regs-gpio.h>
#include <asm/hardware.h>
#include <linux/timer.h>? /*timer*/
#include <asm/uaccess.h>? /*jiffies*/
#define VIRTUALDISK_SIZE? 0x1000//4k
#define MEM_CLEAR 0x1
#define VIRTUALDISK_MAJOR 250
/******timer *******/
#define TIMERDELAY 2
int?VirtualDisk_major = VIRTUALDISK_MAJOR;
struct?VirtualDisk{
????struct?cdev cdev;//詳細看cdev機制
????unsigned?char?mem[VIRTUALDISK_SIZE ];
????long?count;??????????/*記錄設備目前被多少設備打開*/
????struct?timer_list mytimer;
};
static?struct?class?*timerlist_class;
static?struct?class_device??? *timerlist_class_dev;
struct?VirtualDisk *VirtualDiskp;
/*********timer opration*************/
static?void?domytimer(unsigned?long?arg){//just used by follow function
????struct?VirtualDisk* myVirtualDiskp = (struct?VirtualDisk *)(arg);
????printk("arg is %ld\n", myVirtualDiskp->mytimer.expires);
????myVirtualDiskp->mytimer.expires = jiffies + (TIMERDELAY*HZ);
????add_timer(&myVirtualDiskp->mytimer);
}
static?void?initMytimer(struct?VirtualDisk *myVirtualDiskp,?int?delay){
????init_timer(&myVirtualDiskp->mytimer);
????myVirtualDiskp->mytimer.data = (unsigned?long) myVirtualDiskp;
????myVirtualDiskp->mytimer.function = &domytimer;
????myVirtualDiskp->mytimer.expires = jiffies + (delay*HZ);
????add_timer(&myVirtualDiskp->mytimer);
}
/*********timer opration over*************/
static?int?timerlist_drv_open(struct?inode *inode,?struct?file *file)
{
????printk("timerlist_dev read\n");
????file->private_data = VirtualDiskp;
????VirtualDiskp->count++;????/*增加設備打開次數*/
????return?0;
}
static?int?timerlist_drv_release(struct?inode *inode,?struct?file *file)
{
????printk("timerlist_dev release\n");
????VirtualDiskp->count--;??/*減少設備打開次數*/
????return?0;
}
/*seek文件定位函數:seek()函數對文件定位的起始地址可以是文件開頭(SEEK_SET,0)、當前位置(SEEK_CUR,1)、文件尾(SEEK_END,2)*/
static?loff_t timerlist_drv_llseek(struct?file *file, loff_t offset,?int?origin){
????loff_t ret = 0;/*返回的位置偏移*/
??switch?(origin)
??{
????case?SEEK_SET:???/*相對文件開始位置偏移*/
??????if?(offset < 0)/*offset不合法*/
??????{
????????ret =? - EINVAL;????/*無效的指針*/
????????break;
??????}
??????if?((unsigned?int)offset > VIRTUALDISK_SIZE)/*偏移大于設備內存*/
??????{
????????ret =? - EINVAL;????/*無效的指針*/
????????break;
??????}
??????file->f_pos = (unsigned?int)offset;??/*更新文件指針位置*/
??????ret = file->f_pos;/*返回的位置偏移*/
??????break;
????case?SEEK_CUR:???/*相對文件當前位置偏移*/
??????if?((file->f_pos + offset) > VIRTUALDISK_SIZE)/*偏移大于設備內存*/
??????{
????????ret =? - EINVAL;/*無效的指針*/
????????break;
??????}
??????if?((file->f_pos + offset) < 0)/*指針不合法*/
??????{
????????ret =? - EINVAL;/*無效的指針*/
????????break;
??????}
??????file->f_pos += offset;/*更新文件指針位置*/
??????ret = file->f_pos;/*返回的位置偏移*/
??????break;
????default:
??????ret =? - EINVAL;/*無效的指針*/
??????break;
??}
??return?ret;
}
/*設備控制函數:ioctl()函數接受的MEM_CLEAR命令,這個命令將全局內存的有效數據長度清零,對于設備不支持的命令,ioctl()函數應該返回-EINVAL*/
static?int?timerlist_drv_ioctl(struct?inode *inode,?struct?file *file, unsigned?int?cmd, unsigned?long?arg){
?????struct?VirtualDisk *devp = file->private_data;/*獲得設備結構體指針*/
????switch?(cmd)
????{
????case?MEM_CLEAR:/*設備內存清零*/
??????memset(devp->mem, 0, VIRTUALDISK_SIZE);
??????printk(KERN_INFO?"VirtualDisk is set to zero\n");
??????break;
????default:
??????return??- EINVAL;
????}
????return?0;
}
/*讀函數:讀寫函數主要是讓設備結構體的mem[]數組與用戶空間交互數據,并隨著訪問字節數變更返回用戶的文件讀寫偏移位置*/
static?ssize_t timerlist_drv_read(struct?file *file,?const?char?__user *buf,?size_t?count, loff_t * ppos)
{
???printk("timerlist_dev read\n");
???unsigned?long?p =? *ppos;?/*記錄文件指針偏移位置*/
??unsigned?int?countt = count;/*記錄需要讀取的字節數*/
??int?ret = 0;????/*返回值*/
??struct?VirtualDisk *devp = file->private_data;?/*獲得設備結構體指針*/
??/*分析和獲取有效的讀長度*/
??if?(p >= VIRTUALDISK_SIZE )??/*要讀取的偏移大于設備的內存空間*/
????return?countt ?? - ENXIO: 0;/*讀取地址錯誤*/
??if?(countt > VIRTUALDISK_SIZE? - p)/*要讀取的字節大于設備的內存空間*/
????countt = VIRTUALDISK_SIZE? - p;/*將要讀取的字節數設為剩余的字節數*/
?/*內核空間->用戶空間交換數據*/
??if?(copy_to_user(buf, (void*)(devp->mem + p), countt))
??{
????ret =? - EFAULT;
??}
??else
??{
????*ppos += countt;
????ret = countt;
????printk("read %d bytes(s) is? %ld\n", countt, p);
??}
????printk("bytes(s) is? %s\n", buf);
initMytimer(devp, 2);
??return?ret;
}
/*
?file 是文件指針,count 是請求的傳輸數據長度,buff 參數是指向用戶空間的緩沖區,這個緩沖區或者保存要寫入的數據,或者是一個存放新讀入數據的空緩沖區,該地址在內核空間不能直接讀寫,ppos 是一個指針指向一個"long offset type"對象, 它指出用戶正在存取的文件位置. 返回值是一個"signed size type。寫的位置相對于文件開頭的偏移。
?*/
static?ssize_t timerlist_drv_write(struct?file *file,?const?char?__user *buf,?size_t?count, loff_t * ppos)
{
????printk("timerlist_dev write\n");
?????unsigned?long?p =? *ppos;?/*記錄文件指針偏移位置*/
??int?ret = 0;??/*返回值*/
??unsigned?int?countt = count;/*記錄需要寫入的字節數*/
??struct?VirtualDisk *devp = file->private_data;?/*獲得設備結構體指針*/
??/*分析和獲取有效的寫長度*/
??if?(p >= VIRTUALDISK_SIZE )/*要寫入的偏移大于設備的內存空間*/
????return?countt ?? - ENXIO: 0;/*寫入地址錯誤*/
??if?(countt > VIRTUALDISK_SIZE? - p)/*要寫入的字節大于設備的內存空間*/
????countt = VIRTUALDISK_SIZE? - p;/*將要寫入的字節數設為剩余的字節數*/
??/*用戶空間->內核空間*/
??if?(copy_from_user(devp->mem + p, buf, countt))
????ret =? - EFAULT;
??else
??{
????*ppos += countt;/*增加偏移位置*/
????ret = countt;/*返回實際的寫入字節數*/
????printk("written %d bytes(s) from%ld\n", countt, p);
??}
??return?ret;
????return?0;
}
static?struct?file_operations timerlist_drv_fops = {
????.owner? =?? THIS_MODULE,????/* 這是一個宏,推向編譯模塊時自動創建的__this_module變量 */
????.read = timerlist_drv_read,
????.write??? =??? timerlist_drv_write,?
????.open?? =?? timerlist_drv_open,
????.release = timerlist_drv_release,
????.llseek = timerlist_drv_llseek,
????.ioctl = timerlist_drv_ioctl,
};
?/*將 cdev 結構嵌入一個你自己的設備特定的結構,你應當初始化你已經分配的結構使用以上函數,有一個其他的 struct cdev 成員你需要初始化. 象 file_operations 結構,struct cdev 有一個擁有者成員,應當設置為 THIS_MODULE,一旦 cdev 結構建立, 最后的步驟是把它告訴內核, 調用:
???cdev_add(&dev->cdev, devno, 1);*/
static?void?VirtualDisk_setup_cdev(struct?VirtualDisk *dev,?int?minorIndex){
????int?err;
????int?devno = MKDEV(VirtualDisk_major, minorIndex);
????cdev_init(&dev->cdev, &timerlist_drv_fops);
????dev->cdev.owner = THIS_MODULE;
????err = cdev_add(&dev->cdev, devno, 1);
????if(err){
????printk("error %d cdev file added\n", err);
????}
}
static?int?timerlist_drv_init(void)
{
????int?result;
????dev_t devno = MKDEV(VirtualDisk_major, 0);
????if(VirtualDisk_major){
????result = register_chrdev_region(devno, 1,?"timerlist_dev");
????}else{
????result = alloc_chrdev_region(&devno, 0, 1,?"timerlist_dev");
????VirtualDisk_major = MAJOR(devno);
????}
????if(result < 0 ){
????return?result;
????}
????VirtualDiskp = kmalloc(sizeof(struct?VirtualDisk), GFP_KERNEL);
????if(!VirtualDiskp){
????result = -ENOMEM;
????goto?fail_malloc;
????}
????memset(VirtualDiskp, 0,?sizeof(struct?VirtualDisk));
????VirtualDisk_setup_cdev(VirtualDiskp, 0);
????timerlist_class = class_create(THIS_MODULE,?"timerlist_drv");
????if?(IS_ERR(timerlist_class))
????????return?PTR_ERR(timerlist_class);
????timerlist_class_dev = class_device_create(timerlist_class, NULL, MKDEV(VirtualDisk_major, 0), NULL,?"timerlist_dev");?/* /dev/xyz */
????if?(IS_ERR(timerlist_class_dev))
????????return?PTR_ERR(timerlist_class_dev);
????return?0;
????fail_malloc:
????????unregister_chrdev_region(devno, 1);
????????return?result;
}
static?void?timerlist_drv_exit(void)
{
????if(VirtualDiskp)del_timer(&VirtualDiskp->mytimer);
????cdev_del(&VirtualDiskp->cdev);
????kfree(VirtualDiskp);
????unregister_chrdev_region(MKDEV(VirtualDisk_major, 0), 1);
????class_device_unregister(timerlist_class_dev);
????class_destroy(timerlist_class);
}
module_init(timerlist_drv_init);
module_exit(timerlist_drv_exit);
MODULE_LICENSE("GPL");

本文轉自lilin9105 51CTO博客,原文鏈接:http://blog.51cto.com/7071976/1391830,如需轉載請自行聯系原作者