在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的。