《[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,如需转载请自行联系原作者