一. 逻辑架构
虽然感觉上面向过程的程序设计好像更加适合提高驱动的效率,但如果愿意牺牲一点效率采用一些面向对象的设计方法对于驱动新手是件比较舒服的事。
在这次的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(partone) - Linux操作系统:Ubuntu_的详细内容...