新聞中心

EEPW首頁(yè) > 嵌入式系統(tǒng) > 設(shè)計(jì)應(yīng)用 > arm驅(qū)動(dòng)linux內(nèi)核時(shí)鐘

arm驅(qū)動(dòng)linux內(nèi)核時(shí)鐘

作者: 時(shí)間:2016-11-19 來(lái)源:網(wǎng)絡(luò) 收藏
《[arm驅(qū)動(dòng)]linux內(nèi)核時(shí)鐘》涉及內(nèi)核驅(qū)動(dòng)函數(shù)四個(gè),內(nèi)核結(jié)構(gòu)體一個(gè),分析了內(nèi)核驅(qū)動(dòng)函數(shù)一個(gè);可參考的相關(guān)應(yīng)用程序模板或內(nèi)核驅(qū)動(dòng)模板一個(gè),可參考的相關(guān)應(yīng)用程序模板或內(nèi)核驅(qū)動(dòng)一個(gè)

一、內(nèi)核定時(shí)器
意義:內(nèi)核定時(shí)器是軟件意義上的定時(shí)器,最終依賴定時(shí)器來(lái)實(shí)現(xiàn)。時(shí)鐘中斷處理程序會(huì)喚起Timer_softirq軟中斷,運(yùn)行當(dāng)前處理器上到期的所有定時(shí)器。
二、linux設(shè)備驅(qū)動(dòng)編程
linux內(nèi)核提供一組函數(shù),時(shí)鐘數(shù)據(jù)結(jié)構(gòu);這組函數(shù)和數(shù)據(jù)結(jié)構(gòu)使驅(qū)動(dòng)工程師不用關(guān)心具體的軟件定時(shí)器究竟對(duì)應(yīng)著怎樣的內(nèi)核和硬件行為。
三、數(shù)據(jù)結(jié)構(gòu)和函數(shù):
1)數(shù)據(jù)結(jié)構(gòu)
結(jié)構(gòu)體一)Linux在include/linux/timer.h頭文件中定義了數(shù)據(jù)結(jié)構(gòu)timer_list來(lái)描述一個(gè)內(nèi)核定時(shí)器:

本文引用地址:http://m.butianyuan.cn/article/201611/318226.htm

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

2)定時(shí)器定義及處理函數(shù)
a)定義一個(gè)內(nèi)核定時(shí)器

struct timer_list my_timer;

內(nèi)核驅(qū)動(dòng)函數(shù)一)b)初始化定時(shí)器的timer_list的entry中的next指針為null

void init_timer(struct *timer_list timer);

內(nèi)核源碼一)init_timer內(nèi)核源碼

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
}

內(nèi)核驅(qū)動(dòng)函數(shù)二)c)添加定時(shí)器

void add_timer(struct timer_list *timer)

內(nèi)核驅(qū)動(dòng)函數(shù)三)d)刪除定時(shí)器

int del_timer(struct timer_list * timer)

內(nèi)核驅(qū)動(dòng)函數(shù)四)e)修改定時(shí)器的expire

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

模板一)四、內(nèi)核定時(shí)器使用模板(結(jié)合虛擬內(nèi)存)

a)結(jié)構(gòu)體定義

#define TIMERDELAY 2
struct VirtualDisk{
struct cdev cdev;//詳細(xì)看cdev機(jī)制
timer_list my_timer;//設(shè)備要用的定時(shí)器
};

b)結(jié)構(gòu)體賦值,添加時(shí)鐘,時(shí)鐘要處理的函數(shù)

/***********時(shí)鐘函數(shù),只需調(diào)用initMytimer************/
//完成工作后,往往會(huì)延后expires并將定時(shí)器再次添加到內(nèi)核定時(shí)器鏈表,以便定時(shí)器能再次被觸發(fā)
static void domytimer(unsigned long arg){//just used by initMytimer function
struct VirtualDisk* myVirtualDiskp = (struct VirtualDisk *)(arg);//很重要
//..........加上你還要做的代碼............
printk("arg is %ldn", myVirtualDiskp->mytimer.expires);//打印現(xiàn)在的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()或其他釋放函數(shù)中刪除時(shí)鐘

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

實(shí)例一)五、完整代碼

//“timerlist_drv”,"timerlist_"
#include //模塊所需的大量符號(hào)和函數(shù)定義
#include
#include //文件系統(tǒng)相關(guān)的函數(shù)和頭文件
#include //指定初始化和清除函數(shù)
#include
#include //cdev結(jié)構(gòu)的頭文件包含
#include
#include
//#include //包含驅(qū)動(dòng)程序使用的大部分內(nèi)核API的定義,包括睡眠函數(shù)以及各種變量聲明
#include //在內(nèi)核和用戶空間中移動(dòng)數(shù)據(jù)的函數(shù)
#include
#include
#include
#include
#include /*timer*/
#include /*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;//詳細(xì)看cdev機(jī)制
unsigned char mem[VIRTUALDISK_SIZE ];
long count; /*記錄設(shè)備目前被多少設(shè)備打開(kāi)*/
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 %ldn", 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 readn");
file->private_data = VirtualDiskp;
VirtualDiskp->count++; /*增加設(shè)備打開(kāi)次數(shù)*/
return 0;
}
static int timerlist_drv_release(struct inode *inode, struct file *file)
{
printk("timerlist_dev releasen");
VirtualDiskp->count--; /*減少設(shè)備打開(kāi)次數(shù)*/
return 0;
}
/*seek文件定位函數(shù):seek()函數(shù)對(duì)文件定位的起始地址可以是文件開(kāi)頭(SEEK_SET,0)、當(dāng)前位置(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: /*相對(duì)文件開(kāi)始位置偏移*/
if (offset < 0)/*offset不合法*/
{
ret = - EINVAL; /*無(wú)效的指針*/
break;
}
if ((unsigned int)offset > VIRTUALDISK_SIZE)/*偏移大于設(shè)備內(nèi)存*/
{
ret = - EINVAL; /*無(wú)效的指針*/
break;
}
file->f_pos = (unsigned int)offset; /*更新文件指針位置*/
ret = file->f_pos;/*返回的位置偏移*/
break;
case SEEK_CUR: /*相對(duì)文件當(dāng)前位置偏移*/
if ((file->f_pos + offset) > VIRTUALDISK_SIZE)/*偏移大于設(shè)備內(nèi)存*/
{
ret = - EINVAL;/*無(wú)效的指針*/
break;
}
if ((file->f_pos + offset) < 0)/*指針不合法*/
{
ret = - EINVAL;/*無(wú)效的指針*/
break;
}
file->f_pos += offset;/*更新文件指針位置*/
ret = file->f_pos;/*返回的位置偏移*/
break;
default:
ret = - EINVAL;/*無(wú)效的指針*/
break;
}
return ret;
}
/*設(shè)備控制函數(shù):ioctl()函數(shù)接受的MEM_CLEAR命令,這個(gè)命令將全局內(nèi)存的有效數(shù)據(jù)長(zhǎng)度清零,對(duì)于設(shè)備不支持的命令,ioctl()函數(shù)應(yīng)該返回-EINVAL*/
static int timerlist_drv_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg){
struct VirtualDisk *devp = file->private_data;/*獲得設(shè)備結(jié)構(gòu)體指針*/
switch (cmd)
{
case MEM_CLEAR:/*設(shè)備內(nèi)存清零*/
memset(devp->mem, 0, VIRTUALDISK_SIZE);
printk(KERN_INFO "VirtualDisk is set to zeron");
break;
default:
return - EINVAL;
}
return 0;
}
/*讀函數(shù):讀寫函數(shù)主要是讓設(shè)備結(jié)構(gòu)體的mem[]數(shù)組與用戶空間交互數(shù)據(jù),并隨著訪問(wèn)字節(jié)數(shù)變更返回用戶的文件讀寫偏移位置*/
static ssize_t timerlist_drv_read(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
{
printk("timerlist_dev readn");
unsigned long p = *ppos; /*記錄文件指針偏移位置*/
unsigned int countt = count;/*記錄需要讀取的字節(jié)數(shù)*/
int ret = 0; /*返回值*/
struct VirtualDisk *devp = file->private_data; /*獲得設(shè)備結(jié)構(gòu)體指針*/
/*分析和獲取有效的讀長(zhǎng)度*/
if (p >= VIRTUALDISK_SIZE ) /*要讀取的偏移大于設(shè)備的內(nèi)存空間*/
return countt ? - ENXIO: 0;/*讀取地址錯(cuò)誤*/
if (countt > VIRTUALDISK_SIZE - p)/*要讀取的字節(jié)大于設(shè)備的內(nèi)存空間*/
countt = VIRTUALDISK_SIZE - p;/*將要讀取的字節(jié)數(shù)設(shè)為剩余的字節(jié)數(shù)*/
/*內(nèi)核空間->用戶空間交換數(shù)據(jù)*/
if (copy_to_user(buf, (void*)(devp->mem + p), countt))
{
ret = - EFAULT;
}
else
{
*ppos += countt;
ret = countt;
printk("read %d bytes(s) is %ldn", countt, p);
}
printk("bytes(s) is %sn", buf);
initMytimer(devp, 2);
return ret;
}
/*
file 是文件指針,count 是請(qǐng)求的傳輸數(shù)據(jù)長(zhǎng)度,buff 參數(shù)是指向用戶空間的緩沖區(qū),這個(gè)緩沖區(qū)或者保存要寫入的數(shù)據(jù),或者是一個(gè)存放新讀入數(shù)據(jù)的空緩沖區(qū),該地址在內(nèi)核空間不能直接讀寫,ppos 是一個(gè)指針指向一個(gè)"long offset type"對(duì)象, 它指出用戶正在存取的文件位置. 返回值是一個(gè)"signed size type。寫的位置相對(duì)于文件開(kāi)頭的偏移。
*/
static ssize_t timerlist_drv_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
{
printk("timerlist_dev writen");
unsigned long p = *ppos; /*記錄文件指針偏移位置*/
int ret = 0; /*返回值*/
unsigned int countt = count;/*記錄需要寫入的字節(jié)數(shù)*/
struct VirtualDisk *devp = file->private_data; /*獲得設(shè)備結(jié)構(gòu)體指針*/
/*分析和獲取有效的寫長(zhǎng)度*/
if (p >= VIRTUALDISK_SIZE )/*要寫入的偏移大于設(shè)備的內(nèi)存空間*/
return countt ? - ENXIO: 0;/*寫入地址錯(cuò)誤*/
if (countt > VIRTUALDISK_SIZE - p)/*要寫入的字節(jié)大于設(shè)備的內(nèi)存空間*/
countt = VIRTUALDISK_SIZE - p;/*將要寫入的字節(jié)數(shù)設(shè)為剩余的字節(jié)數(shù)*/
/*用戶空間->內(nèi)核空間*/
if (copy_from_user(devp->mem + p, buf, countt))
ret = - EFAULT;
else
{
*ppos += countt;/*增加偏移位置*/
ret = countt;/*返回實(shí)際的寫入字節(jié)數(shù)*/
printk("written %d bytes(s) from%ldn", countt, p);
}
return ret;
return 0;
}
static struct file_operations timerlist_drv_fops = {
.owner = THIS_MODULE, /* 這是一個(gè)宏,推向編譯模塊時(shí)自動(dòng)創(chuàng)建的__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 結(jié)構(gòu)嵌入一個(gè)你自己的設(shè)備特定的結(jié)構(gòu),你應(yīng)當(dāng)初始化你已經(jīng)分配的結(jié)構(gòu)使用以上函數(shù),有一個(gè)其他的 struct cdev 成員你需要初始化. 象 file_operations 結(jié)構(gòu),struct cdev 有一個(gè)擁有者成員,應(yīng)當(dāng)設(shè)置為 THIS_MODULE,一旦 cdev 結(jié)構(gòu)建立, 最后的步驟是把它告訴內(nèi)核, 調(diào)用:
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 addedn", 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");



評(píng)論


技術(shù)專區(qū)

關(guān)閉