`

linux驱动编程RTC(part one)

阅读更多

一. 逻辑架构文章来源: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, &reg) )
		{
			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, &reg) )
		{
			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) )
		{
			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, &reg) )
		{
			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, &reg_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, &reg_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驱动

    在Linux系统中,RTC(Real-Time Clock)驱动是操作系统与硬件时钟进行交互的重要组成部分,它负责维护系统的时间。在本话题中,我们将深入探讨如何在Linux环境下针对DS1338芯片实现RTC驱动,以及它所涉及的核心功能...

    RTC5 Driver_Rtc4_RTC4demo_RTC5_RTC5驱动_RTC5驱动

    RTC5驱动程序是针对德国scanlab公司的激光扫描振镜系统所设计的一款专业驱动软件。这款驱动主要用于控制RTC4和RTC5系列的激光扫描头,确保它们在精密的激光加工任务中能够准确、高效地工作。在本文中,我们将深入...

    Linux下读取RTC时间

    可以在Linux系统下读取RTC芯片的内部时间,同时实现了读取系统内部自身时间的功能。亲测在Ubuntu下可用!

    linux RTC驱动资料

    Linux RTC(Real-Time Clock)驱动是操作系统与硬件时钟之间的重要接口,用于提供精确的时间保持功能,即使在系统关闭后也能保持时间。RTC驱动在Linux内核中扮演着至关重要的角色,因为它涉及到系统的定时器、日历...

    Drivers_scanlabdemo_DEMO_RTC4驱动_RTC4驱动安装_Rtc4_

    驱动程序的重要性在于它能够将复杂的硬件指令转换为操作系统能理解的语言,使得用户可以通过标准的编程接口或专门的软件来操控硬件设备。 安装RTC4驱动的过程通常包括以下步骤: 1. 下载:首先,你需要从官方网站...

    rtc_test.rar_LINUX rtc test_RTC_linux rtc_rtc linux

    综上所述,这个压缩包提供了学习和实践Linux下RTC开发的机会,涵盖了RTC驱动编程、接口使用、唤醒功能以及电源管理等多个方面的知识。通过分析`rtc_test.c`代码并结合参考资料,开发者可以深入理解RTC在Linux系统中...

    LINUX RTC 驱动相关资料

    在Linux系统中,RTC驱动是连接硬件RTC芯片与操作系统内核的桥梁,确保系统即使在关闭电源后仍能保持准确的时间。这份资料主要涵盖了Linux RTC驱动的相关知识,包括基本概念、工作原理、API接口以及开发与调试技巧。 ...

    linux 下 hym8563、bm8563 RTC驱动

    在allwinner平台下调通的 linux下hym8563、bm8563 RTC驱动、8563 datesheet资料 ,。

    rtc-rx8025t.rar_PNH_RX8025T_RX8025T linux_RX8025T例程_rx8025t驱动

    标题"rtc-rx8025t.rar_PNH_RX8025T_RX8025T linux_RX8025T例程_rx8025t驱动"表明这是一个关于RX8025T RTC芯片的Linux驱动程序,包含了"PNH"项目中的RX8025T例程,可能是一个开发包或者代码库,用于帮助开发者理解和...

    linux系统驱动-RTC驱动-rv-8263-c7

    linux系统驱动-RTC驱动-rv-8263-c7,自己写的rtc驱动源码,参考内核rtc-pcf85063源码移植

    rtc.spl.rar_2410_2410 RTC_Linux 实时_linux 驱动 2410_rtc.spl

    针对"rtc.spl.rar_2410_2410 RTC_Linux 实时_linux 驱动 2410_rtc.spl"这个文件,我们将详细讨论2410平台上的RTC驱动以及Linux实时性相关的知识点。 首先,2410平台通常指的是Samsung S3C2410,这是一个基于ARM920T...

    rtc_test.rar_linux rtc_rtc test

    `rtc_test.c`是这个测试程序的源代码文件,它展示了如何在Linux环境下使用RTC设备驱动进行编程。以下是一些关键的知识点: 1. **RTC设备文件**:在Linux中,RTC通常作为一个字符设备暴露在/dev目录下,如/dev/rtc0...

    RTC3130 LINUX 驱动

    本驱动主要涉及RTC3130在Linux环境下的驱动实现,以下将详细解析这个驱动的相关知识点。 1. **RTC概述**: 实时时钟(RTC)是电子设备中用于保持时间的一种组件,即使在系统电源关闭时也能保持时间的准确性。RTC...

    RTC实时时钟驱动

    RTC 实时时钟驱动实验报告 RTC 驱动实验旨在了解 RTC 的工作原理、掌握 RTC 驱动的编写和加载过程、并测试 RTC 驱动。实验设备包括 PC 机、基于 ARM9 系统教学实验系统、网线、串口线等,软件环境为 ubuntu12.04 ...

    linux的rtc可以定时的时钟驱动代码

    总结来说,学习Linux RTC驱动,特别是针对S3C2410 ARM板的驱动,有助于提升对嵌入式系统时钟管理、中断处理和驱动编程的理解。这不仅对日常的开发工作有帮助,也为解决诸如系统定时、节能运行等实际问题提供了理论...

    RX8025驱动-STM+Linux

    在描述中提到的“自写Rx8025驱动For Linux”,可能是指开发者根据RX8025的数据手册和Linux内核的RTC子系统规范,编写了一版定制的驱动程序。这通常涉及理解RTC设备模型,定义设备节点,注册和注销设备,以及实现读写...

    linux rtc时钟读写和ID获取

    Linux RTC 时钟读写和 ID 获取 RTC(Real-Time Clock)时钟是计算机系统中的一种硬件组件,负责提供当前的日期和时间。Linux 系统中,RTC 时钟的读写和 ID 获取是通过特定的系统调用和设备文件来实现的。 RTC 时钟...

    rtc1339驱动

    编写RTC1339驱动时,需要遵循特定的API(应用程序编程接口)和硬件接口规范,确保兼容性和效率。 4. 编译环境:海思35系列开发环境包含了编译工具链、SDK(软件开发工具包)、调试工具等,用于开发和测试针对海思...

    linux rtc驱动

    Linux RTC 驱动 Linux RTC 驱动是指在 Linux 操作系统中,使用软件模拟的方法完成 I2C 通信,实现对 RTC 实时时钟设备的驱动。下面将对 Linux RTC 驱动的编写思路和测试程序进行详细介绍。 一、Linux 时钟系统简介...

    RX8025T在linux下的驱动

    这是RX8025T的linux驱动,不是RX8025SA,这两款芯片的读写有点不一样。可以自己修改。

Global site tag (gtag.js) - Google Analytics