一. 逻辑架构文章来源:http://www.itnose.net/detail/6036596.html更多文章:http://www.itnose.net/type/110.html
虽然感觉上面向过程的程序设计好像更加适合提高驱动的效率,但如果愿意牺牲一点效率采用一些面向对象的设计方法对于驱动新手是件比较舒服的事。
在这次的RTC编程里,为了使结构更加清晰,将整个驱动结构分成了三层,Hardware, Software, rtc.
Hardware层负责寄存器操作,屏蔽具体的寄存器地址信息。
/** hardware operation*/ struct tagHardware; typedef bool (*REG_OPS_R)( struct tagHardware *hthis, uint8 *val); typedef bool (*REG_OPS_W)( struct tagHardware *hthis, uint8 val); struct tagHardware { /** resource*/ struct resource *res; struct resource *res_mem; void __iomem *base; /** operation*/ //control register REG_OPS_R funcCon_r; REG_OPS_W funcCon_w; REG_OPS_R funcTicnt_r; REG_OPS_W funcTicnt_w; REG_OPS_R funcAlm_r; REG_OPS_W funcAlm_w; //time register REG_OPS_R funcSec_r; REG_OPS_W funcSec_w; REG_OPS_R funcMin_r; REG_OPS_W funcMin_w; REG_OPS_R funcHour_r; REG_OPS_W funcHour_w; REG_OPS_R funcDay_r; REG_OPS_W funcDay_w; REG_OPS_R funcDate_r; REG_OPS_W funcDate_w; REG_OPS_R funcMon_r; REG_OPS_W funcMon_w; REG_OPS_R funcYear_r; REG_OPS_W funcYear_w; //alarm register REG_OPS_R funcASec_r; REG_OPS_W funcASec_w; REG_OPS_R funcAMin_r; REG_OPS_W funcAMin_w; REG_OPS_R funcAHour_r; REG_OPS_W funcAHour_w; REG_OPS_R funcADate_r; REG_OPS_W funcADate_w; REG_OPS_R funcAMon_r; REG_OPS_W funcAMon_w; REG_OPS_R funcAYear_r; REG_OPS_W funcAYear_w; };
Software层负责将寄存器操作组合成有实际意义的操作,例如打开RTCEN使能,设置tick时钟频率。
struct tagSoftware; typedef bool (*BIT_OPS)( struct tagSoftware *sthis, bool isTure); typedef bool (*SETFREQ)( struct tagSoftware *sthis, int freq); typedef bool (*READTIM)( struct tagSoftware *sthis, struct rtc_time *buf); typedef bool (*SETTIM)( struct tagSoftware *sthis, const struct rtc_time *buf); typedef bool (*SETALMEN)( struct tagSoftware *sthis, uint8 *mask); /** basic operation*/ struct tagSoftware { /** resource*/ struct tagHardware *Hardware; struct resource *res_tickno; struct resource *res_alarmno; /** public function*/ BIT_OPS funcRtcen; //总开关,在写时需要打开 BIT_OPS funcClksel; //default as 0 BIT_OPS funcCntsel; //default as 0 BIT_OPS funcClkrst; //RTC clock count reset //BIT_OPS funcInqIRQ_Tick; //enable/disable the irq of tick //BIT_OPS funcInqIRQ_Alarm; //enable/disable the irq of alarm BIT_OPS funcInt_en; //tick time interrupt enable SETFREQ funcSetFreq; //0~127 SETALMEN funcGetAlmEn; BIT_OPS funcAlm_en; BIT_OPS funcYear_en; BIT_OPS funcMon_en; BIT_OPS funcDate_en; BIT_OPS funcHour_en; BIT_OPS funcMin_en; BIT_OPS funcSec_en; READTIM funcGetTime; SETTIM funcSetTime; READTIM funcGetAlm; SETTIM funcSetAlm; };
在rtc处再负责将一些太过琐碎soft的操作组合成较容易理解的操作,到这一步就已经屏蔽了硬件的具体地址细节,之后就可以专心的实现硬件的具体逻辑细节(比如怎么设置值,怎么使能)。
struct tagRTC_Driver; typedef bool (*RTC_EN)( struct tagRTC_Driver *rthis, bool isTrue); typedef bool (*RTC_TIME_OPS)( struct tagRTC_Driver *rthis, struct rtc_time *buf); typedef bool (*RTC_SETTICKEN)( struct tagRTC_Driver *rthis, bool isTrue); typedef bool (*RTC_SETTICKFREQ)( struct tagRTC_Driver *rthis, int freq); typedef bool (*RTC_SETALARMEN)( struct tagRTC_Driver *rthis, uint8 mask); typedef bool (*RTC_GETALARMEN)( struct tagRTC_Driver *rthid, uint8 *mask); struct tagRTC_Driver { //user operation /** resources*/ struct cdev cdev; dev_t devNum; struct semaphore sem; struct class *pClas; struct device *pDev; struct fasync_struct *async_queue; struct tagSoftware *soft_ops; /** public function*/ RTC_EN funcEn; RTC_TIME_OPS funcGetTime; RTC_TIME_OPS funcSetTime; RTC_TIME_OPS funcGetAlarm; RTC_TIME_OPS funcSetAlarm; RTC_SETTICKEN funcSetTickEn; RTC_SETTICKFREQ funcSetTickFreq; RTC_SETALARMEN funcSetAlarmEn; RTC_GETALARMEN funcGetAlarmEn; };
上面的RTC层提供的函数为操作硬件提供了较友好的接口,现在就可以专注内核接口的设计。
二. 一些知识要点
2.1 异步通知异步通知的实现原理较简单,分别从user 和 kernel两条线来看。
从kernel线,凡是申请了异步通知的进程会被登记到一个队列中。当触发条件到达时,就根据队列中的记录信息通知相应的进程。登记信息用到的函数为
fasync_helper(int fd,struct file * filp,int on,struct fasync_struct * * fapp)
根据队列信息通知用户层用到的函数为
kill_fasync(struct fasync_struct * * fp,int sig,int band)
从user线,一个驱动的通知信息只能到达设备文件,一个进程如果要获取通知信息需要使驱动知道自己,这可以通过 fcntl()实现,以及开通自己在这个文件下的异步通知功能。一般这样
fcntl( this->fd, F_SETOWN, getpid()); oflags = fcntl( this->fd, F_GETFL); fcntl( this->fd, F_SETFL, oflags|FASYNC);
2.2 设备添加
在设备模型里面,设备--总线--驱动 三个构成了实际的功能。如果驱动依照这个模型来设计,那么还需要实现的是将设备挂载到总线的设备列表中。关于添加方式,在
http://blog.csdn.net/u012301943/article/details/23095215
已经分析过了自己的思路。
三. RTC驱动代码
3.1 driver//when a tick-irq or alarm-irq arrived, a asynchronous signal ,SIGIO, will be send to user. //a message package , record all information about this interrupt,will be ready for user untill next // interrupt arrive. At this moment, you should read this package immediately to ensure it wouldn't // be lost. #include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/fs.h> #include <linux/cdev.h> #include <asm/uaccess.h> #include <linux/device.h> #include <linux/platform_device.h> #include <asm/io.h> #include <linux/interrupt.h> #include <linux/module.h> #include <linux/fs.h> #include <linux/string.h> #include <linux/init.h> #include <linux/platform_device.h> #include <linux/interrupt.h> #include <linux/bcd.h> #include <linux/clk.h> #include <mach/hardware.h> #include <asm/irq.h> #include "TQ_rtc_drv2.h" typedef unsigned char uint8; /** relative address*/ #define RTC_BASE_ADDR 0x57000040 //for ensure relative address. #define RTC_END_ADDR 0x5700008b #define RELA_ADDR(x) ( x -RTC_BASE_ADDR) #define RTCCON_ADDR RELA_ADDR(0x57000040) #define TICNT_ADDR RELA_ADDR(0x57000044) #define RTCALM_ADDR RELA_ADDR(0x57000050) #define ALMSEC_ADDR RELA_ADDR(0x57000054) #define ALMMIN_ADDR RELA_ADDR(0x57000058) #define ALMHOUR_ADDR RELA_ADDR(0x5700005c) #define ALMDATE_ADDR RELA_ADDR(0x57000060) #define ALMMON_ADDR RELA_ADDR(0x57000064) #define ALMYEAR_ADDR RELA_ADDR(0x57000068) #define BCDSEC_ADDR RELA_ADDR(0x57000070) #define BCDMIN_ADDR RELA_ADDR(0x57000074) #define BCDHOUR_ADDR RELA_ADDR(0x57000078) #define BCDDATE_ADDR RELA_ADDR(0x5700007c) #define BCDDAY_ADDR RELA_ADDR(0x57000080) #define BCDMON_ADDR RELA_ADDR(0x57000084) #define BCDYEAR_ADDR RELA_ADDR(0x57000088) /*****************************************************************/ //state : the entire software was divided into three parts: Hardware, Software, // and rtc( user operation) // //Hardware : basic register operation //Software : some basic operation for user //rtc : complex operation // /*****************************************************************/ /** hardware operation*/ struct tagHardware; typedef bool (*REG_OPS_R)( struct tagHardware *hthis, uint8 *val); typedef bool (*REG_OPS_W)( struct tagHardware *hthis, uint8 val); struct tagHardware { /** resource*/ struct resource *res; struct resource *res_mem; void __iomem *base; /** operation*/ //control register REG_OPS_R funcCon_r; REG_OPS_W funcCon_w; REG_OPS_R funcTicnt_r; REG_OPS_W funcTicnt_w; REG_OPS_R funcAlm_r; REG_OPS_W funcAlm_w; //time register REG_OPS_R funcSec_r; REG_OPS_W funcSec_w; REG_OPS_R funcMin_r; REG_OPS_W funcMin_w; REG_OPS_R funcHour_r; REG_OPS_W funcHour_w; REG_OPS_R funcDay_r; REG_OPS_W funcDay_w; REG_OPS_R funcDate_r; REG_OPS_W funcDate_w; REG_OPS_R funcMon_r; REG_OPS_W funcMon_w; REG_OPS_R funcYear_r; REG_OPS_W funcYear_w; //alarm register REG_OPS_R funcASec_r; REG_OPS_W funcASec_w; REG_OPS_R funcAMin_r; REG_OPS_W funcAMin_w; REG_OPS_R funcAHour_r; REG_OPS_W funcAHour_w; REG_OPS_R funcADate_r; REG_OPS_W funcADate_w; REG_OPS_R funcAMon_r; REG_OPS_W funcAMon_w; REG_OPS_R funcAYear_r; REG_OPS_W funcAYear_w; }; struct tagSoftware; typedef bool (*BIT_OPS)( struct tagSoftware *sthis, bool isTure); typedef bool (*SETFREQ)( struct tagSoftware *sthis, int freq); typedef bool (*READTIM)( struct tagSoftware *sthis, struct rtc_time *buf); typedef bool (*SETTIM)( struct tagSoftware *sthis, const struct rtc_time *buf); typedef bool (*SETALMEN)( struct tagSoftware *sthis, uint8 *mask); /** basic operation*/ struct tagSoftware { /** resource*/ struct tagHardware *Hardware; struct resource *res_tickno; struct resource *res_alarmno; /** public function*/ BIT_OPS funcRtcen; //总开关,在写时需要打开 BIT_OPS funcClksel; //default as 0 BIT_OPS funcCntsel; //default as 0 BIT_OPS funcClkrst; //RTC clock count reset //BIT_OPS funcInqIRQ_Tick; //enable/disable the irq of tick //BIT_OPS funcInqIRQ_Alarm; //enable/disable the irq of alarm BIT_OPS funcInt_en; //tick time interrupt enable SETFREQ funcSetFreq; //0~127 SETALMEN funcGetAlmEn; BIT_OPS funcAlm_en; BIT_OPS funcYear_en; BIT_OPS funcMon_en; BIT_OPS funcDate_en; BIT_OPS funcHour_en; BIT_OPS funcMin_en; BIT_OPS funcSec_en; READTIM funcGetTime; SETTIM funcSetTime; READTIM funcGetAlm; SETTIM funcSetAlm; }; static bool hard_rRTCCON( struct tagHardware *hthis, uint8 *val) { if( NULL==hthis) return false; if( NULL==hthis->base) return false; *val &=0; *val = readb( hthis->base + RTCCON_ADDR); return true; } static bool hard_wRTCCON( struct tagHardware *hthis, uint8 val) { if( NULL==hthis) return false; if( NULL==hthis->base) return false; writeb( val, hthis->base + RTCCON_ADDR); return true; } static bool hard_rTICNT( struct tagHardware *hthis, uint8 *val) { if( NULL==hthis) return false; if( NULL==hthis->base) return false; *val &= 0; *val = readb( hthis->base + TICNT_ADDR); return true; } static bool hard_wTICNT( struct tagHardware *hthis, uint8 val) { if( NULL==hthis) return false; if( NULL==hthis->base) return false; writeb( val, hthis->base + TICNT_ADDR); return true; } static bool hard_rRTCALM( struct tagHardware *hthis, uint8 *val) { if( NULL==hthis) return false; if( NULL==hthis->base) return false; *val &= 0; *val = readb( hthis->base + RTCALM_ADDR); return true; } static bool hard_wRTCALM( struct tagHardware *hthis, uint8 val) { if( NULL==hthis) return false; if( NULL==hthis->base) return false; writeb( val, hthis->base + RTCALM_ADDR); return true; } static bool hard_rBCDSEC( struct tagHardware *hthis, uint8 *val) { if( NULL==hthis) return false; if( NULL==hthis->base) return false; *val &= 0; *val = readb( hthis->base + BCDSEC_ADDR); return true; } static bool hard_wBCDSEC( struct tagHardware *hthis, uint8 val) { if( NULL==hthis) return false; if( NULL==hthis->base) return false; writeb( val, hthis->base + BCDSEC_ADDR); return true; } static bool hard_rBCDMIN( struct tagHardware *hthis, uint8 *val) { if( NULL==hthis) return false; if( NULL==hthis->base) return false; *val &= 0; *val = readb( hthis->base + BCDMIN_ADDR); return true; } static bool hard_wBCDMIN( struct tagHardware *hthis, uint8 val) { if( NULL==hthis) return false; if( NULL==hthis->base) return false; writeb( val, hthis->base + BCDMIN_ADDR); return true; } static bool hard_rBCDHOUR( struct tagHardware *hthis, uint8 *val) { if( NULL==hthis) return false; if( NULL==hthis->base) return false; *val &= 0; *val = readb( hthis->base + BCDHOUR_ADDR); return true; } static bool hard_wBCDHOUR( struct tagHardware *hthis, uint8 val) { if( NULL==hthis) return false; if( NULL==hthis->base) return false; writeb( val, hthis->base + BCDHOUR_ADDR); return true; } static bool hard_rBCDDAY( struct tagHardware *hthis, uint8 *val) { if( NULL==hthis) return false; if( NULL==hthis->base) return false; *val &= 0; *val = readb( hthis->base + BCDDAY_ADDR); return true; } static bool hard_wBCDDAY( struct tagHardware *hthis, uint8 val) { if( NULL==hthis) return false; if( NULL==hthis->base) return false; writeb( val, hthis->base + BCDDAY_ADDR); return true; } static bool hard_rBCDDATE( struct tagHardware *hthis, uint8 *val) { if( NULL==hthis) return false; if( NULL==hthis->base) return false; *val &= 0; *val = readb( hthis->base + BCDDATE_ADDR); return true; } static bool hard_wBCDDATE( struct tagHardware *hthis, uint8 val) { if( NULL==hthis) return false; if( NULL==hthis->base) return false; writeb( val, hthis->base + BCDDATE_ADDR); return true; } static bool hard_rBCDMON( struct tagHardware *hthis, uint8 *val) { if( NULL==hthis) return false; if( NULL==hthis->base) return false; *val &= 0; *val = readb( hthis->base + BCDMON_ADDR); return true; } static bool hard_wBCDMON( struct tagHardware *hthis, uint8 val) { if( NULL==hthis) return false; if( NULL==hthis->base) return false; writeb( val, hthis->base + BCDMON_ADDR); return true; } static bool hard_rBCDYEAR( struct tagHardware *hthis, uint8 *val) { if( NULL==hthis) return false; if( NULL==hthis->base) return false; *val &= 0; *val = readb( hthis->base + BCDYEAR_ADDR); return true; } static bool hard_wBCDYEAR( struct tagHardware *hthis, uint8 val) { if( NULL==hthis) return false; if( NULL==hthis->base) return false; writeb( val, hthis->base + BCDYEAR_ADDR); return true; } static bool hard_rALMSEC( struct tagHardware *hthis, uint8 *val) { if( NULL==hthis) return false; if( NULL==hthis->base) return false; *val &= 0; *val = readb( hthis->base + ALMSEC_ADDR); return true; } static bool hard_wALMSEC( struct tagHardware *hthis, uint8 val) { if( NULL==hthis) return false; if( NULL==hthis->base) return false; writeb( val, hthis->base + ALMSEC_ADDR); return true; } static bool hard_rALMMIN( struct tagHardware *hthis, uint8 *val) { if( NULL==hthis) return false; if( NULL==hthis->base) return false; *val &= 0; *val = readb( hthis->base + ALMMIN_ADDR); return true; } static bool hard_wALMMIN( struct tagHardware *hthis, uint8 val) { if( NULL==hthis) return false; if( NULL==hthis->base) return false; writeb( val, hthis->base + ALMMIN_ADDR); return true; } static bool hard_rALMHOUR( struct tagHardware *hthis, uint8 *val) { if( NULL==hthis) return false; if( NULL==hthis->base) return false; *val &= 0; *val = readb( hthis->base + ALMHOUR_ADDR); return true; } static bool hard_wALMHOUR( struct tagHardware *hthis, uint8 val) { if( NULL==hthis) return false; if( NULL==hthis->base) return false; writeb( val, hthis->base + ALMHOUR_ADDR); return true; } static bool hard_rALMDATE( struct tagHardware *hthis, uint8 *val) { if( NULL==hthis) return false; if( NULL==hthis->base) return false; *val &= 0; *val = readb( hthis->base + ALMDATE_ADDR); return true; } static bool hard_wALMDATE( struct tagHardware *hthis, uint8 val) { if( NULL==hthis) return false; if( NULL==hthis->base) return false; writeb( val, hthis->base + ALMDATE_ADDR); return true; } static bool hard_rALMMON( struct tagHardware *hthis, uint8 *val) { if( NULL==hthis) return false; if( NULL==hthis->base) return false; *val &= 0; *val = readb( hthis->base + ALMMON_ADDR); return true; } static bool hard_wALMMON( struct tagHardware *hthis, uint8 val) { if( NULL==hthis) return false; if( NULL==hthis->base) return false; writeb( val, hthis->base + ALMMON_ADDR); return true; } static bool hard_rALMYEAR( struct tagHardware *hthis, uint8 *val) { if( NULL==hthis) return false; if( NULL==hthis->base) return false; *val &= 0; *val = readb( hthis->base + ALMYEAR_ADDR); return true; } static bool hard_wALMYEAR( struct tagHardware *hthis, uint8 val) { if( NULL==hthis) return false; if( NULL==hthis->base) return false; writeb( val, hthis->base + ALMYEAR_ADDR); return true; } static struct tagHardware hardwar = { /** resource*/ .res = NULL, .res_mem = NULL, .base = NULL, /** public function*/ //control register .funcCon_r = hard_rRTCCON, .funcCon_w = hard_wRTCCON, .funcTicnt_r = hard_rTICNT, .funcTicnt_w = hard_wTICNT, .funcAlm_r = hard_rRTCALM, .funcAlm_w = hard_wRTCALM, //time register .funcSec_r = hard_rBCDSEC, .funcSec_w = hard_wBCDSEC, .funcMin_r = hard_rBCDMIN, .funcMin_w = hard_wBCDMIN, .funcHour_r = hard_rBCDHOUR, .funcHour_w = hard_wBCDHOUR, .funcDay_r = hard_rBCDDAY, .funcDay_w = hard_wBCDDAY, .funcDate_r = hard_rBCDDATE, .funcDate_w = hard_wBCDDATE, .funcMon_r = hard_rBCDMON, .funcMon_w = hard_wBCDMON, .funcYear_r = hard_rBCDYEAR, .funcYear_w = hard_wBCDYEAR, //alarm .funcASec_r = hard_rALMSEC, .funcASec_w = hard_wALMSEC, .funcAMin_r = hard_rALMMIN, .funcAMin_w = hard_wALMMIN, .funcAHour_r = hard_rALMHOUR, .funcAHour_w = hard_wALMHOUR, .funcADate_r = hard_rALMDATE, .funcADate_w = hard_wALMDATE, .funcAMon_r = hard_rALMMON, .funcAMon_w = hard_wALMMON, .funcAYear_r = hard_rALMYEAR, .funcAYear_w = hard_wALMYEAR, }; static bool __RTCCON_BIT(struct tagSoftware *sthis, bool isTrue, uint8 bitval) { struct tagHardware *phard; if( NULL==sthis) return false; phard = sthis->Hardware; if( (NULL!=phard) &&(NULL!=phard->funcCon_r) &&(NULL!=phard->funcCon_w) ) { uint8 reg; if( phard->funcCon_r( phard, ®) ) { if( isTrue) reg |= bitval; else reg &= (~bitval); return phard->funcCon_w( phard, reg); } } return false; } static bool soft_RTCEN( struct tagSoftware *sthis, bool isTrue) //总开关,在写时需要打开 { #define BIT_RTCEN (0x1) return __RTCCON_BIT( sthis, isTrue, BIT_RTCEN); } static bool soft_CLKSEL( struct tagSoftware *sthis, bool isTrue) //default as 0 { #define BIT_CLKSEL (0x1<<1) return __RTCCON_BIT( sthis, isTrue, BIT_CLKSEL); } static bool soft_CNTSEL( struct tagSoftware *sthis, bool isTrue) //default as 0 { #define BIT_CNTSEL (0x1<<2) return __RTCCON_BIT( sthis, isTrue, BIT_CNTSEL); } static bool soft_CLKRST( struct tagSoftware *sthis, bool isTrue) //RTC clock count reset { #define BIT_CLKRST (0x1<<3) return __RTCCON_BIT( sthis, isTrue, BIT_CLKRST); } static bool soft_SetTickEn( struct tagSoftware *sthis, bool isTrue) //tick time interrupt enable { #define BIT_TICNT_EN (0x1<<7) struct tagHardware *phard; if( NULL==sthis) return false; phard = sthis->Hardware; if( (NULL!=phard) &&(NULL!=phard->funcTicnt_r) &&(NULL!=phard->funcTicnt_w) ) { uint8 reg; if( phard->funcTicnt_r( phard, ®) ) { if( isTrue) reg |= BIT_TICNT_EN; else reg &= (~BIT_TICNT_EN); return phard->funcTicnt_w( phard, reg); } } return false; } static bool soft_SetTickFreq( struct tagSoftware *sthis, int freq) //0~127 { struct tagHardware *hard ; uint8 val ; if( NULL==sthis) return false; hard = sthis->Hardware; if( (NULL!=hard) &&(NULL!= hard->funcTicnt_r) &&(NULL!= hard->funcTicnt_w)) { uint8 reg; if( hard->funcTicnt_r( hard, ®) ) { reg &= (BIT_TICNT_EN) ; val = 128/freq -1; reg |= val; return hard->funcTicnt_w( hard, reg); } } return false; } static bool __RTCALM_BIT(struct tagSoftware *sthis, bool isTrue, uint8 bitval) { struct tagHardware *phard; if( NULL==sthis) return false; phard = sthis->Hardware; if( (NULL!=phard) &&(NULL!=phard->funcAlm_r) &&(NULL!=phard->funcAlm_w) ) { uint8 reg; if( phard->funcAlm_r( phard, ®) ) { if( isTrue) reg |= bitval; else reg &= (~bitval); //printk("========================3, %x \n", reg); return phard->funcAlm_w( phard, reg); } } return false; } static bool soft_GetAlmEn( struct tagSoftware *sthis, uint8 *mask) { struct tagHardware *phard; if( NULL==sthis) return false; if( NULL==mask) return false; phard = sthis->Hardware; if( (NULL!=phard) &&(NULL!=phard->funcAlm_r)) { return phard->funcAlm_r( phard, mask); } return false; } static bool soft_ALMEN( struct tagSoftware *sthis, bool isTrue) //alarm global enable { #define BIT_ALMEN (0x1<<6) return __RTCALM_BIT( sthis, isTrue, BIT_ALMEN); } static bool soft_YEAREN( struct tagSoftware *sthis, bool isTrue) { #define BIT_YEAREN (0x1<<5) return __RTCALM_BIT( sthis, isTrue, BIT_YEAREN); } static bool soft_MONEN( struct tagSoftware *sthis, bool isTrue) { #define BIT_MONEN (0x1<<4) return __RTCALM_BIT( sthis, isTrue, BIT_MONEN); } static bool soft_DATEEN( struct tagSoftware *sthis, bool isTrue) { #define BIT_DATEEN (0x1<<3) return __RTCALM_BIT( sthis, isTrue, BIT_DATEEN); } static bool soft_HOUREN( struct tagSoftware *sthis, bool isTrue) { #define BIT_HOUREN (0x1<<2) return __RTCALM_BIT( sthis, isTrue, BIT_HOUREN); } static bool soft_MINEN( struct tagSoftware *sthis, bool isTrue) { #define BIT_MINEN (0x1<<1) return __RTCALM_BIT( sthis, isTrue, BIT_MINEN); } static bool soft_SECEN( struct tagSoftware *sthis, bool isTrue) { #define BIT_SECEN (0x1<<0) return __RTCALM_BIT( sthis, isTrue, BIT_SECEN); } static bool soft_GetTIM( struct tagSoftware *sthis, struct rtc_time *buf) { struct tagHardware *hard; if( (NULL==sthis) ||(NULL==buf) ) return false; hard = sthis->Hardware; if( (NULL==hard)) return false; if( NULL!=hard->funcSec_r) hard->funcSec_r( hard, &(buf->sec)); if( NULL!=hard->funcMin_r) hard->funcMin_r( hard, &(buf->min)); if( NULL!=hard->funcHour_r) hard->funcHour_r( hard, &(buf->hour)); if( NULL!=hard->funcDate_r) hard->funcDate_r( hard, &(buf->date)); if( NULL!=hard->funcMon_r) hard->funcMon_r( hard, &(buf->mon)); if( NULL!=hard->funcYear_r) hard->funcYear_r( hard, &(buf->year)); if( NULL!=hard->funcDay_r) hard->funcDay_r( hard, &(buf->day)); return true; } static bool soft_SetTIM( struct tagSoftware *sthis, const struct rtc_time *buf) { uint8 reg_rtccon; struct tagHardware *hard; if( (NULL==sthis) ||(NULL==buf) ) return false; //check RTCEN, it must be on if want write to those registers of RTC hard = sthis->Hardware; if( (NULL==hard) ||(NULL==hard->funcCon_r)) return false; hard->funcCon_r( hard, ®_rtccon); if( (!reg_rtccon)&BIT_RTCEN ) return false; //write date if( NULL!=hard->funcSec_w) hard->funcSec_w( hard, buf->sec); if( NULL!=hard->funcMin_w) hard->funcMin_w( hard, buf->min); if( NULL!=hard->funcHour_w) hard->funcHour_w( hard, buf->hour); if( NULL!=hard->funcDate_w) hard->funcDate_w( hard, buf->date); if( NULL!=hard->funcMon_w) hard->funcMon_w( hard, buf->mon); if( NULL!=hard->funcYear_w) hard->funcYear_w( hard, buf->year); if( NULL!=hard->funcDay_w) hard->funcDay_w( hard, buf->day); return true; } static bool soft_GetALM( struct tagSoftware *sthis, struct rtc_time *buf) { struct tagHardware *hard; if( (NULL==sthis) ||(NULL==buf) ) return false; hard = sthis->Hardware; if( (NULL==hard)) return false; if( NULL!=hard->funcASec_r) hard->funcASec_r( hard, &(buf->sec)); if( NULL!=hard->funcAMin_r) hard->funcAMin_r( hard, &(buf->min)); if( NULL!=hard->funcAHour_r) hard->funcAHour_r( hard, &(buf->hour)); if( NULL!=hard->funcADate_r) hard->funcADate_r( hard, &(buf->date)); if( NULL!=hard->funcAMon_r) hard->funcAMon_r( hard, &(buf->mon)); if( NULL!=hard->funcAYear_r) hard->funcAYear_r( hard, &(buf->year)); return true; } static bool soft_SetALM( struct tagSoftware *sthis,const struct rtc_time *buf) { uint8 reg_rtccon; struct tagHardware *hard; if( (NULL==sthis) ||(NULL==buf) ) return false; //check RTCEN, it must be on if want write to those registers of RTC hard = sthis->Hardware; if( (NULL==hard) ||(NULL==hard->funcCon_r)) return false; hard->funcCon_r( hard, ®_rtccon); if( (!reg_rtccon)&BIT_RTCEN ) return false; //write date if( NULL!=hard->funcASec_w) hard->funcASec_w( hard, buf->sec); if( NULL!=hard->funcAMin_w) hard->funcAMin_w( hard, buf->min); if( NULL!=hard->funcAHour_w) hard->funcAHour_w( hard, buf->hour); if( NULL!=hard->funcADate_w) hard->funcADate_w( hard, buf->date); if( NULL!=hard->funcAMon_w) hard->funcAMon_w( hard, buf->mon); if( NULL!=hard->funcAYear_w) hard->funcAYear_w( hard, buf->year); return true; } static struct tagSoftware soft = { /** resource*/ .Hardware = &hardwar, /** public function*/ .funcRtcen = soft_RTCEN, //总开关,在写时需要打开 .funcClksel = soft_CLKSEL, //default as 0 .funcCntsel = soft_CNTSEL, //default as 0 .funcClkrst = soft_CLKRST, //RTC clock count reset .funcInt_en = soft_SetTickEn, //tick time interrupt enable .funcSetFreq = soft_SetTickFreq, //0~127 .funcGetAlmEn = soft_GetAlmEn, .funcAlm_en = soft_ALMEN, .funcYear_en = soft_YEAREN, .funcMon_en = soft_MONEN, .funcDate_en = soft_DATEEN, .funcHour_en = soft_HOUREN, .funcMin_en = soft_MINEN, .funcSec_en = soft_SECEN, .funcGetTime = soft_GetTIM, .funcSetTime = soft_SetTIM, .funcGetAlm = soft_GetALM, .funcSetAlm = soft_SetALM, }; /**********************device************************************/ /*****************************************************************/ #define RTC_CLAS_NAME "clas_rtc_test" #define RTC_NAME "rtc_test" #define RTC_DRV_NAME "rtc_drv_test" #define MAJOR_NUM 0 //自动申请设备号 #define MINOR_NUM 0 struct tagRTC_Driver; typedef bool (*RTC_EN)( struct tagRTC_Driver *rthis, bool isTrue); typedef bool (*RTC_TIME_OPS)( struct tagRTC_Driver *rthis, struct rtc_time *buf); typedef bool (*RTC_SETTICKEN)( struct tagRTC_Driver *rthis, bool isTrue); typedef bool (*RTC_SETTICKFREQ)( struct tagRTC_Driver *rthis, int freq); typedef bool (*RTC_SETALARMEN)( struct tagRTC_Driver *rthis, uint8 mask); typedef bool (*RTC_GETALARMEN)( struct tagRTC_Driver *rthid, uint8 *mask); struct tagRTC_Driver { //user operation /** resources*/ struct cdev cdev; dev_t devNum; struct semaphore sem; struct class *pClas; struct device *pDev; struct fasync_struct *async_queue; struct tagSoftware *soft_ops; /** public function*/ RTC_EN funcEn; RTC_TIME_OPS funcGetTime; RTC_TIME_OPS funcSetTime; RTC_TIME_OPS funcGetAlarm; RTC_TIME_OPS funcSetAlarm; RTC_SETTICKEN funcSetTickEn; RTC_SETTICKFREQ funcSetTickFreq; RTC_SETALARMEN funcSetAlarmEn; RTC_GETALARMEN funcGetAlarmEn; }; static bool rtc_en( struct tagRTC_Driver *rthis, bool isTrue) { struct tagSoftware *soft; if( NULL==rthis) return false; soft = rthis->soft_ops; if( NULL==soft) return false; if( isTrue ) { if( ( NULL!=soft->funcRtcen) &&(NULL!=soft->funcClksel) &&(NULL!=soft->funcCntsel) &&(NULL!=soft->funcClkrst) ) { soft->funcRtcen( soft, true); soft->funcClksel( soft, false); soft->funcCntsel( soft, false); soft->funcClkrst( soft, false); } } else { if( (NULL!=soft->funcRtcen) &&(NULL!=soft->funcInt_en)) { soft->funcRtcen( soft, false); soft->funcInt_en( soft, false); } } return true; } static bool rtc_GetTime( struct tagRTC_Driver *rthis, struct rtc_time *buf) { struct tagSoftware *soft ; if( (NULL==rthis) ||(NULL==buf) ) return false; soft = rthis->soft_ops; if( (NULL!=soft) &&(NULL!=soft->funcGetTime)) return soft->funcGetTime( soft, buf); return true; } static bool rtc_setTime( struct tagRTC_Driver *rthis, struct rtc_time *buf) { struct tagSoftware *soft; if( (NULL==rthis) ||(NULL==buf) ) return false; soft = rthis->soft_ops; if( (NULL!=soft) &&(NULL!=soft->funcSetTime)) { return soft->funcSetTime( soft, buf); } return true; } static bool rtc_GetAlarm( struct tagRTC_Driver *rthis, struct rtc_time *buf) { struct tagSoftware *soft ; if( (NULL==rthis) ||(NULL==buf) ) return false; soft = rthis->soft_ops; if( (NULL!=soft) &&(NULL!=soft->funcGetAlm)) return soft->funcGetAlm( soft, buf); return true; } static bool rtc_setAlarm( struct tagRTC_Driver *rthis, struct rtc_time *buf) { struct tagSoftware *soft; if( (NULL==rthis) ||(NULL==buf) ) return false; soft = rthis->soft_ops; if( (NULL!=soft) &&(NULL!=soft->funcSetAlm)) { return soft->funcSetAlm( soft, buf); } return true; } #if 0 static bool rtc_setFreq( struct tagRTC_Driver *rthis, int freq, bool isTrue) { struct tagSoftware *soft ; if( NULL==rthis) return false; soft = rthis->soft_ops; if( ( NULL!=soft) &&( NULL!=soft->funcInt_en) &&( soft->funcInt_en( soft, isTrue) ) ) { freq%=128; return soft->funcSetFreq( soft, freq); } return false; } #endif static bool rtc_SetTickEn( struct tagRTC_Driver *rthis, bool isTrue) { struct tagSoftware *soft; if( NULL==rthis) return false; soft = rthis->soft_ops; if( ( NULL!=soft) &&( NULL!=soft->funcInt_en) ) { return soft->funcInt_en( soft, isTrue); } return false; } static bool rtc_SetTickFreq( struct tagRTC_Driver *rthis, int freq) { struct tagSoftware *soft ; if( NULL==rthis) return false; soft = rthis->soft_ops; if( ( NULL!=soft) &&( NULL!=soft->funcSetFreq) ) { freq%=128; return soft->funcSetFreq( soft, freq); } return false; } static bool _rtc_SetBit( struct tagSoftware *soft, BIT_OPS func, int mask, int x) { bool val; if( NULL==func) return false; val = GETBIT(mask,x); return func( soft, val); } // 1 meaning for enable static bool rtc_SetAlarmEn( struct tagRTC_Driver *rthis, uint8 mask) { struct tagSoftware *soft; bool ret = true; if( NULL==rthis) return false; soft = rthis->soft_ops; if( NULL==soft) return false; if(!_rtc_SetBit( soft, soft->funcSec_en, mask, 0)) ret = false; if(!_rtc_SetBit( soft, soft->funcMin_en, mask, 1)) ret = false; if(!_rtc_SetBit( soft, soft->funcHour_en, mask, 2)) ret = false; if(!_rtc_SetBit( soft, soft->funcDate_en, mask, 3)) ret = false; if(!_rtc_SetBit( soft, soft->funcMon_en, mask, 4)) ret = false; if(!_rtc_SetBit( soft, soft->funcYear_en, mask, 5)) ret = false; if(!_rtc_SetBit( soft, soft->funcAlm_en, mask, 6)) ret = false; return ret; } static bool rtc_GetAlarmEn( struct tagRTC_Driver *rthis, uint8 *mask) { struct tagSoftware *soft; if( NULL==rthis) return false; soft = rthis->soft_ops; if( (NULL!=soft) &&( NULL!=soft->funcGetAlmEn)) { return soft->funcGetAlmEn( soft, mask); } return false; } static struct tagRTC_Driver drvDate = { /** resources*/ .soft_ops = &soft, /** public function*/ .funcEn = rtc_en, .funcGetTime = rtc_GetTime , .funcSetTime = rtc_setTime , .funcGetAlarm = rtc_GetAlarm , .funcSetAlarm = rtc_setAlarm , .funcSetTickEn = rtc_SetTickEn , .funcSetTickFreq = rtc_SetTickFreq , .funcSetAlarmEn = rtc_SetAlarmEn , .funcGetAlarmEn = rtc_GetAlarmEn , }; /*********************************************************/ #define EVENT_MAX 7 struct tagEventQueue; typedef bool (*EVE_OPS)( struct tagEventQueue *ethis, struct rtc_event *eve); struct tagEventQueue { /** data*/ int head; int tail; struct rtc_event queue[EVENT_MAX]; /** function*/ EVE_OPS Add; EVE_OPS Get; }; static bool AddEvent( struct tagEventQueue *ethis, struct rtc_event *inEve) { if( NULL==ethis) return false; if( NULL==inEve) return false; ethis->queue[ethis->tail] = *inEve; ethis->tail ++; if( ethis->tail>=EVENT_MAX ) { ethis->tail = 0; if( ethis->head==0) { ethis->head ++; } } return true; } static bool GetEvent( struct tagEventQueue *ethis, struct rtc_event *OutEve) { if( NULL==ethis) return false; if( NULL==OutEve) return false; if( ethis->head==ethis->tail) { memset( OutEve, 0, sizeof(struct rtc_event)); return false; } *OutEve = ethis->queue[ethis->head]; ethis->head ++ ; if( ethis->head>=EVENT_MAX) ethis->head = 0; return true; } static struct tagEventQueue events = { .head = 0, .tail = 0, .Add = AddEvent , .Get = GetEvent , }; static irqreturn_t rtc_alarmirq( int irq, void *id) { struct rtc_event Eve; printk(" congratulation!!you get alarm-irq\n"); Eve.type = rtc_alarm; Eve.data.alarm.irqnum = irq; if( !events.Add( &events, &Eve)) { printk("warn: fail to record event information, %s, %d\n", __FILE__, __LINE__); } //a notification will be send to user process. kill_fasync( &(drvDate.async_queue), SIGIO, POLL_IN); return IRQ_HANDLED; } static irqreturn_t rtc_tickirq( int irq, void *id) { struct rtc_event Eve; printk(" congratulation!!you get tick-irq, \n"); Eve.type = rtc_tick; Eve.data.tick.irqnum = irq; if( !events.Add( &events, &Eve)) { printk("warn: fail to record event information, %s, %d\n", __FILE__, __LINE__); } kill_fasync( &(drvDate.async_queue), SIGIO, POLL_IN); return IRQ_HANDLED; } static int rtc_open ( struct inode *id, struct file *fp) { //printk("rtc_open...................\n"); int ret; int irq_tick, irq_alarm; if( down_trylock( &(drvDate.sem))) { return -EBUSY; } if( (NULL==drvDate.soft_ops->res_tickno) ||(NULL==drvDate.soft_ops->res_alarmno) ) { printk("error: invalid resource, %s, %d \n", __FILE__, __LINE__); return -EIO; } irq_tick = drvDate.soft_ops->res_tickno->start; ret = request_irq( irq_tick, rtc_tickirq, IRQF_DISABLED, "TQ_rtc_drv2 alarm", &drvDate); if (ret) { printk("error : fail to request irq-alarm\n"); goto ERR_TICK; } irq_alarm = drvDate.soft_ops->res_alarmno->start; ret = request_irq( irq_alarm, rtc_alarmirq, IRQF_DISABLED, "TQ_rtc_drv2 tick", &drvDate); if (ret) { printk("error : fail to request irq-tick\n"); goto ERR_ALARM; } /***************tmp*********************************/ /****************************************************/ return 0; free_irq( drvDate.soft_ops->res_alarmno->start, &drvDate); ERR_ALARM: free_irq( drvDate.soft_ops->res_tickno->start, &drvDate); ERR_TICK: up(&(drvDate.sem)); return ret; } static int rtc_release ( struct inode *id, struct file *fp) { //printk("rtc_release...................\n"); int ret = 0; int irq_tick, irq_alarm; up(&(drvDate.sem)); //close tick close if( (NULL!=drvDate.funcSetTickEn) &&( !drvDate.funcSetTickEn( &drvDate, false)) ) { printk(" error : fail to close tick clock, %s, %d\n", __FILE__, __LINE__); ret = -1; } //release irq if( NULL!=drvDate.soft_ops ) { if( NULL!=drvDate.soft_ops->res_tickno) { irq_tick = drvDate.soft_ops->res_tickno->start; free_irq( irq_tick, &drvDate); } if( NULL!=drvDate.soft_ops->res_alarmno) { irq_alarm = drvDate.soft_ops->res_alarmno->start; free_irq( irq_alarm, &drvDate); } } return ret; } static ssize_t rtc_read ( struct file *fp, char __user *buf, size_t len, loff_t *off) { struct rtc_time time; unsigned long ret = 0; if( (NULL!=drvDate.funcGetTime) &&( !(drvDate.funcGetTime( &drvDate, &time)))) { return 0; } ret = copy_to_user( buf, &time, len); if( ret<0) { printk("warn: fail to copy data to user, %s, %d\n", __FILE__, __LINE__); return 0; } return len; } static ssize_t rtc_write ( struct file *fp, const char __user *buf, size_t len, loff_t *off) { //void __iomem *base = rtcData.addr.base; struct rtc_time time; //unsigned long ret = 0; //printk("rtc_write...................\n"); memset( &time, 0, sizeof(struct rtc_time)); len = copy_from_user( &time, buf, len); if( (NULL!=drvDate.funcSetTime) &&( drvDate.funcSetTime( &drvDate, &time)) ) { return len; //还有len个字节数据没写入 } return -1; } static int rtc_ioctl(struct inode *in, struct file *fp, unsigned int cmd, unsigned long arg) { int ret = -1; //printk(" ioctl : %x, %lx\n", cmd, arg); switch( cmd) { case CMD_SETTIME: { struct rtc_time time; memset( &time, 0, sizeof(struct rtc_time)); if(copy_from_user( &time, (struct rtc_time *)arg, sizeof(struct rtc_time))) { break; } if( (NULL!=drvDate.funcSetTime) && ( drvDate.funcSetTime( &drvDate, &time)) ) { ret = 0; } break; } case CMD_GETTIME: { struct rtc_time time; if( (NULL!=drvDate.funcGetTime) &&( !(drvDate.funcGetTime( &drvDate, &time)))) { break; } if( (0!=arg) &&(copy_to_user( (struct rtc_time *)arg, &time, sizeof(struct rtc_time))<0) ) { printk("warn: fail to copy data to user, %s, %d\n", __FILE__, __LINE__); break; } ret = 0; break; } case CMD_SETALARM: { if( 0!=arg) { struct rtc_time time; copy_from_user( &time, (struct rtc_time *)arg, sizeof(struct rtc_time)); if( (NULL!=drvDate.funcSetAlarm) &&(drvDate.funcSetAlarm( &drvDate, &time))) { ret = 0; } } break; } case CMD_GETALARM: { if( 0!=arg) { struct rtc_time time; if( (NULL!=drvDate.funcGetAlarm) &&(drvDate.funcGetAlarm( &drvDate, &time))) { copy_to_user( (struct rtc_time *)arg, &time, sizeof(struct rtc_time)); ret = 0; } } break; } case CMD_ENTICK: { if( ( NULL!=drvDate.funcSetTickEn) &&(drvDate.funcSetTickEn( &drvDate, true)) ) { ret = 0; } break; } case CMD_DISABTICK: { if( ( NULL!=drvDate.funcSetTickEn) &&(drvDate.funcSetTickEn( &drvDate, false)) ) { ret = 0; } break; } case CMD_SETFREQ: { int freq = arg%128; if( ( NULL!=drvDate.funcSetTickFreq) &&(drvDate.funcSetTickFreq( &drvDate, freq)) ) { ret = 0; } break; } case CMD_SETALARMEN: { int mask = (int)arg; if( ( NULL!=drvDate.funcSetAlarmEn) &&(drvDate.funcSetAlarmEn( &drvDate, mask)) ) { ret = 0; } break; } case CMD_GETALARMEN: { if( 0!=arg) { int *mask = (int *)arg; if( ( NULL!=drvDate.funcGetAlarmEn) &&(drvDate.funcGetAlarmEn( &drvDate, (uint8 *)mask)) ) { ret = 0; } } break; } case CMD_GETEVENT: { struct rtc_event Eve; if( events.Get( &events, &Eve) ) { ret = 0; } if( 0!=arg) copy_to_user( (struct rtc_event *)arg, &Eve, sizeof( struct rtc_event)); break; } default : printk("warn: invalid command\n"); break; } return ret; } static int rtc_fasync(int fd, struct file *filp, int mode) { //process, which is alloc aychronous notification, will be record on the queue by the help of this function. return fasync_helper( fd, filp, mode, &(drvDate.async_queue)); } static struct file_operations rtc_ops = { .fasync = rtc_fasync , .open = rtc_open, .release = rtc_release, .read = rtc_read, .write = rtc_write, .ioctl = rtc_ioctl, }; /********************************************************************/ static int rtc_probe(struct platform_device *dev) { printk("rtc_probe............\n"); //init cdev_init( &(drvDate.cdev), &rtc_ops); drvDate.devNum = 0; drvDate.cdev.owner = THIS_MODULE; sema_init( &(drvDate.sem), 1); drvDate.pClas = NULL; drvDate.pDev = NULL; drvDate.soft_ops = &soft; //alloc device-number if( MAJOR_NUM!=0) { drvDate.devNum = MKDEV( MAJOR_NUM, MINOR_NUM); if(register_chrdev_region( drvDate.devNum, 1, RTC_DRV_NAME)) goto ERR_REG; } else { if(alloc_chrdev_region( &(drvDate.devNum), MINOR_NUM, 1, RTC_DRV_NAME)) goto ERR_REG; } //add char-device if(cdev_add( &(drvDate.cdev), drvDate.devNum, 1)<0) goto ERR_CDEV; //create class drvDate.pClas = class_create( THIS_MODULE, RTC_CLAS_NAME); if( NULL==drvDate.pClas) goto ERR_CLAS; //create device drvDate.pDev= device_create( drvDate.pClas, NULL, drvDate.devNum, NULL, "rtc_device_test"); if( NULL==drvDate.pDev) goto ERR_DEVICE; /***********init device****************************************/ drvDate.soft_ops->Hardware->res = platform_get_resource( dev, IORESOURCE_MEM, 0); if( NULL==drvDate.soft_ops->Hardware->res) { printk("error: %s, %d\n", __FILE__, __LINE__); goto ERR_DEVICE; } drvDate.soft_ops->res_alarmno = platform_get_resource( dev, IORESOURCE_IRQ, 0); if( NULL==drvDate.soft_ops->res_alarmno) { printk("error: %s, %d\n", __FILE__, __LINE__); goto ERR_DEVICE; } drvDate.soft_ops->res_tickno = platform_get_resource( dev, IORESOURCE_IRQ, 1); if( NULL==drvDate.soft_ops->res_tickno) { printk("error: %s, %d\n", __FILE__, __LINE__); goto ERR_DEVICE; } drvDate.soft_ops->Hardware->res_mem = request_mem_region( drvDate.soft_ops->Hardware->res->start, drvDate.soft_ops->Hardware->res->end - drvDate.soft_ops->Hardware->res->start + 1, dev->name); if( NULL==drvDate.soft_ops->Hardware->res_mem ) { printk("error: %s, %d\n", __FILE__, __LINE__); goto ERR_REQ; } drvDate.soft_ops->Hardware->base = ioremap( drvDate.soft_ops->Hardware->res->start, drvDate.soft_ops->Hardware->res->end - drvDate.soft_ops->Hardware->res->start + 1 ); if( NULL==drvDate.soft_ops->Hardware->base) { printk("error: %s, %d\n", __FILE__, __LINE__); goto ERR_REMAP; } if( (NULL==drvDate.funcEn) ||(!(drvDate.funcEn( &drvDate, true)) ) ) goto ERR_EN; /***********************************************************/ return 0; //返回0表示接受这次探测 ERR_EN: iounmap( drvDate.soft_ops->Hardware->base); ERR_REMAP: release_resource( drvDate.soft_ops->Hardware->res_mem); ERR_REQ: device_destroy( drvDate.pClas, drvDate.devNum); ERR_DEVICE: class_destroy( drvDate.pClas); ERR_CLAS: cdev_del( &(drvDate.cdev)); ERR_CDEV: unregister_chrdev_region( drvDate.devNum, 1); ERR_REG: return -1; } static int rtc_remove( struct platform_device *dev) { printk(" rtc_remove.........................\n"); if( NULL!=drvDate.funcEn) drvDate.funcEn( &drvDate, false); iounmap(drvDate.soft_ops->Hardware->base); release_resource( drvDate.soft_ops->Hardware->res_mem); device_destroy( drvDate.pClas, drvDate.devNum); class_destroy( drvDate.pClas); cdev_del( &(drvDate.cdev)); unregister_chrdev_region( drvDate.devNum, 1); return 0; } static struct platform_driver rtc_drv = { .probe = rtc_probe, .remove = rtc_remove, .driver = { .name = RTC_NAME, .owner = THIS_MODULE, }, }; static int __init rtc_init( void) { printk("vision : %s\n", __TIME__); return platform_driver_register( &rtc_drv); } static void __exit rtc_exit( void) { printk("vision : %s\n", __TIME__); platform_driver_unregister( &rtc_drv); } MODULE_LICENSE("GPL"); module_init( rtc_init); module_exit( rtc_exit);
3.2 device
#include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/fs.h> #include <linux/cdev.h> #include <asm/uaccess.h> #include <linux/device.h> #include <linux/platform_device.h> #include <asm/irq.h> #define RTC_NAME "rtc_test" #define RTC_BASE_ADDR 0x57000040 #define RTC_END_ADDR 0x5700008b static struct resource rtc_res[] = { [0] = { .start = RTC_BASE_ADDR, .end = RTC_END_ADDR, .flags = IORESOURCE_MEM, }, [1] = { .start = IRQ_RTC, .end = IRQ_RTC, .flags = IORESOURCE_IRQ, }, [2] = { .start = IRQ_TICK, .end = IRQ_TICK, .flags = IORESOURCE_IRQ }, }; static struct platform_device rtc_dev = { .name = RTC_NAME, .id = 0, .num_resources = ARRAY_SIZE(rtc_res), .resource = rtc_res, }; static int __init rtc_init( void) { printk("%s\n", __TIME__); return platform_device_register( &(rtc_dev)); } static void __exit rtc_exit( void) { printk("%s\n", __TIME__); platform_device_unregister( &rtc_dev); } MODULE_LICENSE("GPL"); module_init( rtc_init); module_exit( rtc_exit);
相关推荐
在Linux系统中,RTC(Real-Time Clock)驱动是操作系统与硬件时钟进行交互的重要组成部分,它负责维护系统的时间。在本话题中,我们将深入探讨如何在Linux环境下针对DS1338芯片实现RTC驱动,以及它所涉及的核心功能...
RTC5驱动程序是针对德国scanlab公司的激光扫描振镜系统所设计的一款专业驱动软件。这款驱动主要用于控制RTC4和RTC5系列的激光扫描头,确保它们在精密的激光加工任务中能够准确、高效地工作。在本文中,我们将深入...
可以在Linux系统下读取RTC芯片的内部时间,同时实现了读取系统内部自身时间的功能。亲测在Ubuntu下可用!
Linux RTC(Real-Time Clock)驱动是操作系统与硬件时钟之间的重要接口,用于提供精确的时间保持功能,即使在系统关闭后也能保持时间。RTC驱动在Linux内核中扮演着至关重要的角色,因为它涉及到系统的定时器、日历...
驱动程序的重要性在于它能够将复杂的硬件指令转换为操作系统能理解的语言,使得用户可以通过标准的编程接口或专门的软件来操控硬件设备。 安装RTC4驱动的过程通常包括以下步骤: 1. 下载:首先,你需要从官方网站...
综上所述,这个压缩包提供了学习和实践Linux下RTC开发的机会,涵盖了RTC驱动编程、接口使用、唤醒功能以及电源管理等多个方面的知识。通过分析`rtc_test.c`代码并结合参考资料,开发者可以深入理解RTC在Linux系统中...
在Linux系统中,RTC驱动是连接硬件RTC芯片与操作系统内核的桥梁,确保系统即使在关闭电源后仍能保持准确的时间。这份资料主要涵盖了Linux RTC驱动的相关知识,包括基本概念、工作原理、API接口以及开发与调试技巧。 ...
在allwinner平台下调通的 linux下hym8563、bm8563 RTC驱动、8563 datesheet资料 ,。
标题"rtc-rx8025t.rar_PNH_RX8025T_RX8025T linux_RX8025T例程_rx8025t驱动"表明这是一个关于RX8025T RTC芯片的Linux驱动程序,包含了"PNH"项目中的RX8025T例程,可能是一个开发包或者代码库,用于帮助开发者理解和...
linux系统驱动-RTC驱动-rv-8263-c7,自己写的rtc驱动源码,参考内核rtc-pcf85063源码移植
针对"rtc.spl.rar_2410_2410 RTC_Linux 实时_linux 驱动 2410_rtc.spl"这个文件,我们将详细讨论2410平台上的RTC驱动以及Linux实时性相关的知识点。 首先,2410平台通常指的是Samsung S3C2410,这是一个基于ARM920T...
`rtc_test.c`是这个测试程序的源代码文件,它展示了如何在Linux环境下使用RTC设备驱动进行编程。以下是一些关键的知识点: 1. **RTC设备文件**:在Linux中,RTC通常作为一个字符设备暴露在/dev目录下,如/dev/rtc0...
本驱动主要涉及RTC3130在Linux环境下的驱动实现,以下将详细解析这个驱动的相关知识点。 1. **RTC概述**: 实时时钟(RTC)是电子设备中用于保持时间的一种组件,即使在系统电源关闭时也能保持时间的准确性。RTC...
RTC 实时时钟驱动实验报告 RTC 驱动实验旨在了解 RTC 的工作原理、掌握 RTC 驱动的编写和加载过程、并测试 RTC 驱动。实验设备包括 PC 机、基于 ARM9 系统教学实验系统、网线、串口线等,软件环境为 ubuntu12.04 ...
总结来说,学习Linux RTC驱动,特别是针对S3C2410 ARM板的驱动,有助于提升对嵌入式系统时钟管理、中断处理和驱动编程的理解。这不仅对日常的开发工作有帮助,也为解决诸如系统定时、节能运行等实际问题提供了理论...
在描述中提到的“自写Rx8025驱动For Linux”,可能是指开发者根据RX8025的数据手册和Linux内核的RTC子系统规范,编写了一版定制的驱动程序。这通常涉及理解RTC设备模型,定义设备节点,注册和注销设备,以及实现读写...
Linux RTC 时钟读写和 ID 获取 RTC(Real-Time Clock)时钟是计算机系统中的一种硬件组件,负责提供当前的日期和时间。Linux 系统中,RTC 时钟的读写和 ID 获取是通过特定的系统调用和设备文件来实现的。 RTC 时钟...
编写RTC1339驱动时,需要遵循特定的API(应用程序编程接口)和硬件接口规范,确保兼容性和效率。 4. 编译环境:海思35系列开发环境包含了编译工具链、SDK(软件开发工具包)、调试工具等,用于开发和测试针对海思...
修改linux内核3.2.0 的 rtc 驱动 rtc-pcf8563.c (源码位置: drivers/rtc), 实现 i2c 驱动层平台设备的动态生成, 而无需在板级初始化中注册 i2c 设备 资源包含两个文件: rtc-pcf8563.c 和 rtc-pcf8563内核源码.c, ...