在Linux 2.6.29版本下實現了RTC的功能,但沒有測試過。
在linux 2.6.30版本下,之用字符設備實現RTC。
代碼如下:
#include <asm/io.h>
//#include <asm/arch/regs-rtc.h>
#include <linux/interrupt.h>
#include <linux/poll.h>
//#include <linxu/devfs_fs_kernel.h>
#include <linux/module.h>
static int major;
#define DEVICE_NAME "RTC"
#define DEVICE_MAJOR major
#define DEVICE_MINOR 0
#define DEVICE_IRQ IRQ_TICK
//寄存器定義
#define RTCCON 0x57000040
#define TICNT 0x57000044
#define RTCSEC 0x57000070
#define RTCMIN 0x57000074
#define RTCHOUR 0x57000078
//寄存器虛擬地址
static void *rtccon;
static void *ticnt;
static void *rtcsec;
static void *rtcmin;
static void *rtchour;
static int flag = 0;
static char time[8];
static int num_to_BCD(int num)
{
int i,j,r;
i = num/10;
j = num%10;
r = (i<<4)|j;
return r;
}
static int num_from_BCD(int num)
{
int i,j,r;
i = (num & 0xf0)>>4;
j = (num &0x0f);
r = i*10+j;
return r;
}
static void set_time(int hour, int min, int sec)
{
time[0] = hour/10 +'0';
time[1] = hour%10 +'0';
time[2] = ':';
time[3] = min/10 +'0';
time[4] = min%10 +'0';
time[5] = ':';
time[6] = sec/10 +'0';
time[7] = sec%10 +'0';
}
static DECLARE_WAIT_QUEUE_HEAD(wq);
static irqreturn_t RTC_interrupt(void)
{
flag++;
wake_up_interruptible(&wq);
return IRQ_HANDLED;
}
//1s我們只能讀一次時間,這是由於RTC_read()會阻塞自己,直到1s中斷到來,喚醒它阻塞的隊列
static int RTC_read(struct file *filp, char __user *buff, size_t count, loff_t *offp)
{
int hour, min, sec;
wait_event_interruptible(wq, flag); // 在此阻塞,直到有中斷產生(flag發生變化)
hour = num_from_BCD(ioread32(rtchour));
min = num_from_BCD(ioread32(rtcmin));
sec = num_from_BCD(ioread32(rtcsec));
set_time(hour,min,sec);
copy_to_user(buff, time, sizeof(time));
flag = 0;
return 0;
}
static int RTC_write(struct file *filp, const char __user *buff, size_t count, loff_t *offp)
{
int hour, min, sec;
int tmp;
copy_from_user(time, buff, sizeof(time));
hour = time[1] -'0' + (time[0]-'0')*10;
min = time[4] -'0' + (time[3] -'0')*10;
sec = time[7] - '0' + (time[6] -'0')*10;
tmp = ioread32(ticnt) & (~(1<<7));
iowrite32(tmp, ticnt);
iowrite32(0x01, rtccon);
iowrite32(num_to_BCD(hour), rtchour);
iowrite32(num_to_BCD(min), rtcmin);
iowrite32(num_to_BCD(sec), rtcsec);
iowrite32(0x00, rtccon);
tmp = ioread32(ticnt) |(1<<7);
iowrite32(tmp, ticnt);
return 0;
}
static int RTC_ioctlr(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
{
switch(cmd)
{
case RTC_CMD_READ:
RTC_read(filp, (char*)arg, 8, 0); // 調用read()函數實現ioctl中的讀命令
break;
case RTC_CMD_WRITE:
RTC_write(filp, (char*)arg, 8, 0); // 這裏的arg是不能有“&”符的,因爲我們在傳遞參數的時候傳的是地址
break;
default:
break;
}
return 0;
}
static int RTC_open(struct inode *inode, struct file *filp)
{
int ret;
ret = request_irq(DEVICE_IRQ, RTC_interrupt, IRQF_DISABLED, DEVICE_NAME, NULL);
if(ret < 0)
{
printk("request irq failed/n");
return ret;
}
// 寄存器單獨映射,並不是我們經常見到的映射寄存器的整個區域
rtccon = ioremap(RTCCON,4);
ticnt = ioremap(TICNT, 4);
rtcmin = ioremap(RTCMIN,4);
rtcsec = ioremap(RTCSEC, 4);
rtchour = ioremap(RTCHOUR, 4);
//初始時間爲12:30:00(在此也可以看出2440的RTC的時間在寄存器中是以BCD碼存在的)
iowrite32(0x01, rtccon);
iowrite32(0, rtcsec);
iowrite32((3<<4)|0, rtcmin);
iowrite32((1<<4)|2, rtchour);
iowrite32(0x00, rtccon);
iowrite32(127|(1<<7), ticnt);
return 0;
}
static int RTC_close(struct inode *inode, struct file *filp)
{
free_irq(DEVICE_IRQ, NULL);
return 0;
}
static struct file_operations RTC_ops =
{
.owner = THIS_MODULE,
.open = RTC_open,
.release = RTC_close,
.read = RTC_read,
.write = RTC_write,
.ioctl = RTC_ioctlr,
};
int RTC_init(void)
{
int ret;
// 在2.6.內核中很少看到register_chrdev來註冊字符設備的,我們一般用cdev_init(),cdev_add()等函數來實現字符設備的註冊
ret = register_chrdev(0, DEVICE_NAME, &RTC_ops);
if(ret < 0)
{
printk("register device failed/n");
return ret;
}
else
major = ret;
printk("register device sucessfully %d/n",ret);
// devfs_mk_cdev是用來創建設備結點的,但在我用的2.6.29的內核中沒有此函數,所以就沒有創建設備結點,需要在插入RTC模塊後
//手動創建設備結點
//devfs_mk_cdev(MKDEV(DEVICE_MAJOR,DEVICE_MINOR),S_IFCHR|S_IRUSR|S_IWUSR|S_IRGRP,DEVICE_NAME);
return 0;
}
void RTC_exit(void)
{
unregister_chrdev(DEVICE_MAJOR, DEVICE_NAME);
//devfs_remove(DEVICE_NAME);
printk("Device has been unregister/n");
}
module_init(RTC_init);
module_exit(RTC_exit);
上面是驅動程序,沒有創建設備結點,需要根據主設備號用mknod自己創建。也只有時間的控制,沒有年月的設置。
下面是應用測試程序:
#include <stdio.h>
#include <fcntl.h>
#include <signal.h>
#define RTC_CMD_READ 0x81
#define RTC_CMD_WRITE 0x82
static char time[8];
static int fd;
void int_to_char(int t[])
{
time[0] = t[0]/10 + '0';
time[1] = t[0]%10 + '0';
time[3] = t[1]/10 + '0';
time[4] = t[1]%10 + '0';
time[6] = t[2]/10 + '0';
time[7] = t[2]%10 + '0';
}
void sig_handle(void)
{
int t[3];
printf("input new time below/n");
printf("hour:");
scanf("%d",&t[0]);
printf("min:");
scanf("%d", &t[1]);
printf("sec:");
scanf("%d", &t[2]);
int_to_char(t);
//write(fd, time,sizeof(time));
ioctl(fd, RTC_CMD_WRITE, time);
}
int main(void)
{
int i;
signal(SIGTSTP, (void*)sig_handle);
fd = open("/dev/rtc0",O_RDWR);
if(fd < 0)
{
printf("open rtc0 error/n");
exit(1);
}
printf("open rtc0 ok/n");
while(1)
{
ioctl(fd, RTC_CMD_READ, time);
for(i=0; i<8; i++)
{
printf("%c",time[i]);
}
printf("press Ctrl+Z to reset time/n");
printf("/n");
}
close(fd);
return 0;
}
這些也是參考http://blog.163.com/hjw_vc/blog/static/114831035200982174353570/?fromdm&fromSearch&isFromSearchEngine=yes的文章做的。
調試過程中遇到的問題:
1)把RTC_ioctl()中的arg取地址了,致使在應用中不能正確打印時間信息。
2)在編譯應用層程序時,剛開始我用gcc rtc_app.c編譯,但最後運行不對,提示有錯誤,大概說是格式不對。
3)在進行時間設置時,如果我們設置24:xx:xx的話,則當到小時增加1時,我們的時間會變爲25:00:00;正確的做法是:時間是不存在24點的,最多有23:xx:xx.
遺留的問題:
如何能在此驅動上實現hwclock的。