网站地图    收藏   

主页 > 系统 > linux系统 >

linux驱动编程--RTC(partone) - Linux操作系统:Ubuntu_

来源:自学PHP网    时间:2015-04-14 10:17 作者: 阅读:

[导读] 一 逻辑架构 虽然感觉上面向过程的程序设计好像更加适合提高驱动的效率,但如果愿意牺牲一点效率采用一些面向对象的设计方法对于驱动新手是件比较舒服的事。...

一. 逻辑架构

虽然感觉上面向过程的程序设计好像更加适合提高驱动的效率,但如果愿意牺牲一点效率采用一些面向对象的设计方法对于驱动新手是件比较舒服的事。

在这次的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);

自学PHP网专注网站建设学习,PHP程序学习,平面设计学习,以及操作系统学习

京ICP备14009008号-1@版权所有www.zixuephp.com

网站声明:本站所有视频,教程都由网友上传,站长收集和分享给大家学习使用,如由牵扯版权问题请联系站长邮箱904561283@qq.com

添加评论