RTC设备驱动

来源:互联网 发布:单兵口粮淘宝卖犯法吗 编辑:程序博客网 时间:2024/06/05 09:36

转载:http://m.blog.csdn.net/blog/paomadi/8309837
一. RTC设备结构体


struct rtc_device{struct device dev;//设备文件struct module *owner;//模块所有者int id;//RTC次设备char name[RTC_DEVICE_NAME_SIZE];//RTC设备名const struct rtc_class_ops *ops;//RTC类操作函数集struct mutex ops_lock;struct cdev char_dev;//RTC字符设备unsigned long flags;//忙标志位unsigned long irq_data;spinlock_t irq_lock;wait_queue_head_t irq_queue;//等待队列头struct fasync_struct *async_queue;struct rtc_task *irq_task;spinlock_t irq_task_lock;int irq_freq;//中断频率int max_user_freq;//最大频率#ifdef CONFIG_RTC_INTF_DEV_UIE_EMULstruct work_struct uie_task;struct timer_list uie_timer;/* Those fields are protected by rtc->irq_lock */unsigned int oldsecs;unsigned int uie_irq_active:1;unsigned int stop_uie_polling:1;unsigned int uie_task_active:1;unsigned int uie_timer_active:1;#endif};


二. RTC时间结构体


struct rtc_time {int tm_sec;//秒int tm_min;//分int tm_hour;//时int tm_mday;//日int tm_mon;//月int tm_year;//年int tm_wday;//星期int tm_yday;//年中的第几天int tm_isdst;//};



三. 闹钟相关结构体


struct rtc_wkalrm {unsigned char enabled;/* 0 = alarm disabled, 1 = alarm enabled *///是否支持闹钟功能unsigned char pending;/* 0 = alarm not pending, 1 = alarm pending *///是否等待struct rtc_time time;·/* time the alarm is set to *///闹钟时间};


 

四. RTC 类操作函数集


struct rtc_class_ops {int (*open)(struct device *);//open方法void (*release)(struct device *);//release方法int (*ioctl)(struct device *, unsigned int, unsigned long);//控制int (*read_time)(struct device *, struct rtc_time *);//读取时间int (*set_time)(struct device *, struct rtc_time *);//设置时间int (*read_alarm)(struct device *, struct rtc_wkalrm *);//读取闹钟int (*set_alarm)(struct device *, struct rtc_wkalrm *);//设置闹钟int (*proc)(struct device *, struct seq_file *);int (*set_mmss)(struct device *, unsigned long secs);//秒装换为rtc_timeint (*irq_set_state)(struct device *, int enabled);//设置中断状态int (*irq_set_freq)(struct device *, int freq);//设置中断频率int (*read_callback)(struct device *, int data);int (*alarm_irq_enable)(struct device *, unsigned int enabled);//闹钟使能禁用int (*update_irq_enable)(struct device *, unsigned int enabled);//更新中断使能开关};



五. RTC系统初始化
     5.1 rtc系统的初始化


static int __init rtc_init(void){rtc_class = class_create(THIS_MODULE, "rtc");//创建rtc_class类"/sys/class/rtc"if (IS_ERR(rtc_class)) {printk(KERN_ERR "%s: couldn't create class\n", __FILE__);return PTR_ERR(rtc_class);}rtc_class->suspend = rtc_suspend;//RTC挂起rtc_class->resume = rtc_resume;//RTC唤醒rtc_dev_init();//RTC字符设备初始化rtc_sysfs_init(rtc_class);return 0;}


     5.2 作为字符设备的初始化


void __init rtc_dev_init(void){int err;err = alloc_chrdev_region(&rtc_devt, 0, RTC_DEV_MAX, "rtc");//RTC_DEV_MAX=16 系统最大支持16个rtcif (err < 0)printk(KERN_ERR "%s: failed to allocate char dev region\n",__FILE__);}


这里rtc_devt记录了分配的第一个RTC设备的设备号,同样它也可以作为rtc类的主设备号

     5.3 RTC在sysfs下的初始化

     5.3.1


void __init rtc_sysfs_init(struct class *rtc_class){rtc_class->dev_attrs = rtc_attrs;}


     5.3.2


static struct device_attribute rtc_attrs[] = {__ATTR(name, S_IRUGO, rtc_sysfs_show_name, NULL),//“/sys/class/rtc/rtcX/name“__ATTR(date, S_IRUGO, rtc_sysfs_show_date, NULL),//“/sys/class/rtc/rtcX/date“__ATTR(time, S_IRUGO, rtc_sysfs_show_time, NULL),//“/sys/class/rtc/rtcX/time“__ATTR(since_epoch, S_IRUGO, rtc_sysfs_show_since_epoch, NULL),//“/sys/class/rtc/rtcX/since_epoch“__ATTR(max_user_freq, S_IRUGO | S_IWUSR, rtc_sysfs_show_max_user_freq,rtc_sysfs_set_max_user_freq),//.../max_user_freq__ATTR(hctosys, S_IRUGO, rtc_sysfs_show_hctosys, NULL),//"/sys/class/rtc/rtcX/hctosys"{ },};


     5.3.3 /sys/class/rtc文件的运用

定义的这些属性文件会在调用rtc_device_register->device_register(&rtc->dev)注册rtc设备后在/sys/class/rtc/下对应的rtcX文件夹出现,通过cat命令会调用第三个参数值定义的函数,可以查看其相关信息

例如:


cat /sys/class/rtc/rtc0/name 查看设备名rtc_cmoscat /sys/class/rtc/rtc0/time 查看当前时间05:35:11cat /sys/class/rtc/rtc0/date 查看日期2012-12-19cat /sys/class/rtc/rtc0/since_epoch 1355895483cat /sys/class/rtc/rtc0/max_user_freq 查看最大频率64cat /sys/class/rtc/rtc0/hctosys 1


 

 六. RTC设备的注册与注销

     6.1 RTC设备的注册rtc_device_register

 参数:RTC->name名字 ; RTC->parent父设备 ; RTC类的操作函数集 ; 模块所有者THIS_MODULES


struct rtc_device *rtc_device_register(const char *name, struct device *dev,const struct rtc_class_ops *ops,struct module *owner){struct rtc_device *rtc;int id, err;if (idr_pre_get(&rtc_idr, GFP_KERNEL) == 0) {err = -ENOMEM;goto exit;}mutex_lock(&idr_lock);err = idr_get_new(&rtc_idr, NULL, &id);//利用idr机制获得新的idmutex_unlock(&idr_lock);if (err < 0)goto exit;id = id & MAX_ID_MASK;rtc = kzalloc(sizeof(struct rtc_device), GFP_KERNEL);//分配内存if (rtc == NULL) {err = -ENOMEM;goto exit_idr;}rtc->id = id;//次设备号rtc->ops = ops;//RTC类操作函数集rtc->owner = owner;//模块所有者rtc->max_user_freq = 64;//最大频率rtc->dev.parent = dev;//父设备rtc->dev.class = rtc_class;//设备所属的类rtc->dev.release = rtc_device_release;//设备的release方法mutex_init(&rtc->ops_lock);spin_lock_init(&rtc->irq_lock);spin_lock_init(&rtc->irq_task_lock);init_waitqueue_head(&rtc->irq_queue);//初始化等待队列头strlcpy(rtc->name, name, RTC_DEVICE_NAME_SIZE);//RTC设备名dev_set_name(&rtc->dev, "rtc%d", id);//RTC字符设备名rtc_dev_prepare(rtc);//初始化字符设备err = device_register(&rtc->dev);//注册设备if (err) {put_device(&rtc->dev);goto exit_kfree;}rtc_dev_add_device(rtc);//添加RTC字符设备rtc_sysfs_add_device(rtc);//添加sysfs下的rtc文件rtc_proc_add_device(rtc);//添加procfs下的rtc文件dev_info(dev, "rtc core: registered %s as %s\n",rtc->name, dev_name(&rtc->dev));return rtc;exit_kfree:kfree(rtc);exit_idr:mutex_lock(&idr_lock);idr_remove(&rtc_idr, id);mutex_unlock(&idr_lock);exit:dev_err(dev, "rtc core: unable to register %s, err = %d\n",name, err);return ERR_PTR(err);}


     6.2  RTC字符设备相关的操作

     6.2.1 rtc_dev_prepare 字符设备的初始化


void rtc_dev_prepare(struct rtc_device *rtc){if (!rtc_devt)return;if (rtc->id >= RTC_DEV_MAX) {//判断RTC个数是否大于16个pr_debug("%s: too many RTC devices\n", rtc->name);return;}rtc->dev.devt = MKDEV(MAJOR(rtc_devt), rtc->id);//根据主次设备号计算出某个RTC设备号#ifdef CONFIG_RTC_INTF_DEV_UIE_EMULINIT_WORK(&rtc->uie_task, rtc_uie_task);setup_timer(&rtc->uie_timer, rtc_uie_timer, (unsigned long)rtc);#endifcdev_init(&rtc->char_dev, &rtc_dev_fops);//初始化RTC字符设备,并捆绑rtc_dev_fopsrtc->char_dev.owner = rtc->owner;//统一RTC设备及其字符设备的模块所有者}


     6.2.2 rtc_dev_fops 具体的函数集后面分析


static const struct file_operations rtc_dev_fops = {.owner= THIS_MODULE,.llseek= no_llseek,.read= rtc_dev_read,//读.poll= rtc_dev_poll,//轮询.unlocked_ioctl= rtc_dev_ioctl,//控制.open= rtc_dev_open,//打开.release= rtc_dev_release,//释放.fasync= rtc_dev_fasync,//同步};



     6.2.3 rtc_dev_add_device 字符设备添加


void rtc_dev_add_device(struct rtc_device *rtc){if (cdev_add(&rtc->char_dev, rtc->dev.devt, 1))//添加字符设备printk(KERN_WARNING "%s: failed to add char device %d:%d\n",rtc->name, MAJOR(rtc_devt), rtc->id);elsepr_debug("%s: dev (%d:%d)\n", rtc->name,MAJOR(rtc_devt), rtc->id);}



    6.3 sysfs文件系统相关


void rtc_sysfs_add_device(struct rtc_device *rtc){int err;/* not all RTCs support both alarms and wakeup */if (!rtc_does_wakealarm(rtc))//若RTC设备支持闹钟唤醒功能return;err = device_create_file(&rtc->dev, &dev_attr_wakealarm);//则在"/sys/class/rtc/rtcXXX/"添加闹钟属性文件if (err)dev_err(rtc->dev.parent,"failed to create alarm attribute, %d\n", err);}



     6.4 procfs文件系统相关

     6.4.1 创建文件


void rtc_proc_add_device(struct rtc_device *rtc){if (rtc->id == 0)proc_create_data("driver/rtc", 0, NULL, &rtc_proc_fops, rtc);//创建"/proc/driver/rtc"文件}


    6.4.2 rtc_proc_fops


static const struct file_operations rtc_proc_fops = {.open= rtc_proc_open,.read= seq_read,.llseek= seq_lseek,.release= rtc_proc_release,};


     6.4.3 /proc/driver/rtc文件的运用

cat 文件时可以显示时间 日期闹钟等信息


cat /proc/driver/rtc rtc_time: 05:43:48rtc_date: 2012-12-19alrm_time: 00:00:00alrm_date: ****-**-**alarm_IRQ: noalrm_pending: no24hr: yesperiodic_IRQ: noupdate_IRQ: noHPET_emulated: noDST_enable: noperiodic_freq: 1024batt_status: okay


其实现的方式是cat的时候,会打开/proc/driver/rtc调用其open方法执行rtc_proc_open函数,然后调用其read方法执行seq_read函数


static int rtc_proc_open(struct inode *inode, struct file *file){struct rtc_device *rtc = PDE(inode)->data;if (!try_module_get(THIS_MODULE))return -ENODEV;return single_open(file, rtc_proc_show, rtc);//接着调用rtc_proc_show函数}


rtc_proc_show函数


static int rtc_proc_show(struct seq_file *seq, void *offset){int err;struct rtc_device *rtc = seq->private;const struct rtc_class_ops *ops = rtc->ops;struct rtc_wkalrm alrm;struct rtc_time tm;err = rtc_read_time(rtc, &tm);//读取时间,打印时间相关信息if (err == 0) {seq_printf(seq,"rtc_time\t: %02d:%02d:%02d\n""rtc_date\t: %04d-%02d-%02d\n",tm.tm_hour, tm.tm_min, tm.tm_sec,tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);}err = rtc_read_alarm(rtc, &alrm);//读取闹钟,打印闹钟相关信息if (err == 0) {seq_printf(seq, "alrm_time\t: ");if ((unsigned int)alrm.time.tm_hour <= 24)seq_printf(seq, "%02d:", alrm.time.tm_hour);elseseq_printf(seq, "**:");if ((unsigned int)alrm.time.tm_min <= 59)seq_printf(seq, "%02d:", alrm.time.tm_min);elseseq_printf(seq, "**:");if ((unsigned int)alrm.time.tm_sec <= 59)seq_printf(seq, "%02d\n", alrm.time.tm_sec);elseseq_printf(seq, "**\n");seq_printf(seq, "alrm_date\t: ");if ((unsigned int)alrm.time.tm_year <= 200)seq_printf(seq, "%04d-", alrm.time.tm_year + 1900);elseseq_printf(seq, "****-");if ((unsigned int)alrm.time.tm_mon <= 11)seq_printf(seq, "%02d-", alrm.time.tm_mon + 1);elseseq_printf(seq, "**-");if (alrm.time.tm_mday && (unsigned int)alrm.time.tm_mday <= 31)seq_printf(seq, "%02d\n", alrm.time.tm_mday);elseseq_printf(seq, "**\n");seq_printf(seq, "alarm_IRQ\t: %s\n",alrm.enabled ? "yes" : "no");seq_printf(seq, "alrm_pending\t: %s\n",alrm.pending ? "yes" : "no");}seq_printf(seq, "24hr\t\t: yes\n");if (ops->proc)ops->proc(rtc->dev.parent, seq);//如果自定义了proc方法会执行该proc方法return 0;}


这里的打印是打印到文件的私有数据段,而seq_read函数则将其信息从文件读取出来并复制到用户空间的buf里


     6.5 RTC设备的注销 rtc_device_unregister


void rtc_device_unregister(struct rtc_device *rtc){if (get_device(&rtc->dev) != NULL) {mutex_lock(&rtc->ops_lock);/* remove innards of this RTC, then disable it, before * letting any rtc_class_open() users access it again */rtc_sysfs_del_device(rtc);//sysfs删除相关文件rtc_dev_del_device(rtc);//移除字符设备rtc_proc_del_device(rtc);//procfs删除相关文件device_unregister(&rtc->dev);//注销设备rtc->ops = NULL;mutex_unlock(&rtc->ops_lock);put_device(&rtc->dev);}}


七. rtc_dev_fops

     7.1 /dev/rtcX的设备文件是由RTC的字符设备去构建的,自然去打开,读取,操作该设备文件时,调用的是rtc_dev_fops的函数集

     7.2 open方法


static int rtc_dev_open(struct inode *inode, struct file *file){int err;struct rtc_device *rtc = container_of(inode->i_cdev,struct rtc_device, char_dev);//用container_of宏获取rtc_deviceconst struct rtc_class_ops *ops = rtc->ops;//获取器rtc_class_ops函数集指针if (test_and_set_bit_lock(RTC_DEV_BUSY, &rtc->flags))//测试并设置忙标志return -EBUSY;file->private_data = rtc;err = ops->open ? ops->open(rtc->dev.parent) : 0;//rtc类函数集存在open方法,则调用其方法if (err == 0) {spin_lock_irq(&rtc->irq_lock);rtc->irq_data = 0;spin_unlock_irq(&rtc->irq_lock);return 0;}/* something has gone wrong */clear_bit_unlock(RTC_DEV_BUSY, &rtc->flags);//清除忙标志return err;}


     7.3 read方法


static ssize_t rtc_dev_read(struct file *file, char __user *buf, size_t count, loff_t *ppos){struct rtc_device *rtc = file->private_data;DECLARE_WAITQUEUE(wait, current);//声明一个等待队列unsigned long data;ssize_t ret;if (count != sizeof(unsigned int) && count < sizeof(unsigned long))return -EINVAL;add_wait_queue(&rtc->irq_queue, &wait);//添加该等待队列到RTC等待队列中do {__set_current_state(TASK_INTERRUPTIBLE);//设置当前进程为可中断spin_lock_irq(&rtc->irq_lock);data = rtc->irq_data;//获取中断数据rtc->irq_data = 0;spin_unlock_irq(&rtc->irq_lock);if (data != 0) {//若有中断数据跳出循环ret = 0;break;}if (file->f_flags & O_NONBLOCK) {ret = -EAGAIN;break;}if (signal_pending(current)) {//当前进程等待ret = -ERESTARTSYS;break;}schedule();//调度} while (1);set_current_state(TASK_RUNNING);//设置当前进程为运行态remove_wait_queue(&rtc->irq_queue, &wait);//移除等待队列if (ret == 0) {/* Check for any data updates */if (rtc->ops->read_callback)//若RTC类存在read_callback方法data = rtc->ops->read_callback(rtc->dev.parent,data);//则调用if (sizeof(int) != sizeof(long) && count == sizeof(unsigned int))ret = put_user(data, (unsigned int __user *)buf) ?:sizeof(unsigned int);elseret = put_user(data, (unsigned long __user *)buf) ?:sizeof(unsigned long);}return ret;}


    7.4 unlocked_ioctl方法


static long rtc_dev_ioctl(struct file *file,unsigned int cmd, unsigned long arg){int err = 0;struct rtc_device *rtc = file->private_data;const struct rtc_class_ops *ops = rtc->ops;struct rtc_time tm;struct rtc_wkalrm alarm;void __user *uarg = (void __user *) arg;err = mutex_lock_interruptible(&rtc->ops_lock);if (err)return err;switch (cmd) {case RTC_EPOCH_SET:case RTC_SET_TIME:if (!capable(CAP_SYS_TIME))err = -EACCES;break;case RTC_IRQP_SET:if (arg > rtc->max_user_freq && !capable(CAP_SYS_RESOURCE))err = -EACCES;break;case RTC_PIE_ON:if (rtc->irq_freq > rtc->max_user_freq &&!capable(CAP_SYS_RESOURCE))err = -EACCES;break;}if (err)goto done;/* try the driver's ioctl interface */if (ops->ioctl) {err = ops->ioctl(rtc->dev.parent, cmd, arg);if (err != -ENOIOCTLCMD) {mutex_unlock(&rtc->ops_lock);return err;}}switch (cmd) {case RTC_ALM_READ://闹钟读mutex_unlock(&rtc->ops_lock);err = rtc_read_alarm(rtc, &alarm);if (err < 0)return err;if (copy_to_user(uarg, &alarm.time, sizeof(tm)))err = -EFAULT;return err;case RTC_ALM_SET://闹钟设置mutex_unlock(&rtc->ops_lock);if (copy_from_user(&alarm.time, uarg, sizeof(tm)))return -EFAULT;alarm.enabled = 0;alarm.pending = 0;alarm.time.tm_wday = -1;alarm.time.tm_yday = -1;alarm.time.tm_isdst = -1;{unsigned long now, then;err = rtc_read_time(rtc, &tm);if (err < 0)return err;rtc_tm_to_time(&tm, &now);alarm.time.tm_mday = tm.tm_mday;alarm.time.tm_mon = tm.tm_mon;alarm.time.tm_year = tm.tm_year;err  = rtc_valid_tm(&alarm.time);if (err < 0)return err;rtc_tm_to_time(&alarm.time, &then);/* alarm may need to wrap into tomorrow */if (then < now) {rtc_time_to_tm(now + 24 * 60 * 60, &tm);alarm.time.tm_mday = tm.tm_mday;alarm.time.tm_mon = tm.tm_mon;alarm.time.tm_year = tm.tm_year;}}return rtc_set_alarm(rtc, &alarm);case RTC_RD_TIME://读时间mutex_unlock(&rtc->ops_lock);err = rtc_read_time(rtc, &tm);if (err < 0)return err;if (copy_to_user(uarg, &tm, sizeof(tm)))err = -EFAULT;return err;case RTC_SET_TIME://设置时间mutex_unlock(&rtc->ops_lock);if (copy_from_user(&tm, uarg, sizeof(tm)))return -EFAULT;return rtc_set_time(rtc, &tm);case RTC_PIE_ON://开启全局中断err = rtc_irq_set_state(rtc, NULL, 1);break;case RTC_PIE_OFF://关闭全局中断err = rtc_irq_set_state(rtc, NULL, 0);break;case RTC_AIE_ON://开启闹钟中断mutex_unlock(&rtc->ops_lock);return rtc_alarm_irq_enable(rtc, 1);case RTC_AIE_OFF://关闭闹钟中断mutex_unlock(&rtc->ops_lock);return rtc_alarm_irq_enable(rtc, 0);case RTC_UIE_ON://开启更新中断mutex_unlock(&rtc->ops_lock);return rtc_update_irq_enable(rtc, 1);case RTC_UIE_OFF://关闭更新中断mutex_unlock(&rtc->ops_lock);return rtc_update_irq_enable(rtc, 0);case RTC_IRQP_SET://设置中断频率err = rtc_irq_set_freq(rtc, NULL, arg);break;case RTC_IRQP_READ://获取中断频率err = put_user(rtc->irq_freq, (unsigned long __user *)uarg);break;case RTC_WKALM_SET://设置闹钟唤醒mutex_unlock(&rtc->ops_lock);if (copy_from_user(&alarm, uarg, sizeof(alarm)))return -EFAULT;return rtc_set_alarm(rtc, &alarm);case RTC_WKALM_RD://获取闹钟唤醒mutex_unlock(&rtc->ops_lock);err = rtc_read_alarm(rtc, &alarm);if (err < 0)return err;if (copy_to_user(uarg, &alarm, sizeof(alarm)))err = -EFAULT;return err;default:err = -ENOTTY;break;}done:mutex_unlock(&rtc->ops_lock);return err;}


     7.5 poll方法


static unsigned int rtc_dev_poll(struct file *file, poll_table *wait){struct rtc_device *rtc = file->private_data;unsigned long data;poll_wait(file, &rtc->irq_queue, wait);data = rtc->irq_data;//获取中断数据return (data != 0) ? (POLLIN | POLLRDNORM) : 0;}



     7.6 release方法


static int rtc_dev_release(struct inode *inode, struct file *file){struct rtc_device *rtc = file->private_data;rtc_dev_ioctl(file, RTC_UIE_OFF, 0);rtc_update_irq_enable(rtc, 0);//关闭更新中断rtc_irq_set_state(rtc, NULL, 0);//关闭全局中断if (rtc->ops->release)//若RTC类操作函数集有release方法rtc->ops->release(rtc->dev.parent);//则调用其方法clear_bit_unlock(RTC_DEV_BUSY, &rtc->flags);return 0;}


     7.7 fasync方法


static int rtc_dev_fasync(int fd, struct file *file, int on){struct rtc_device *rtc = file->private_data;return fasync_helper(fd, file, on, &rtc->async_queue);}



八. 编写RTC驱动的方法

1.定义个rtc_class_ops结构体,并完成其函数功能

2.调用rtc_device_register注册一个rtc_device即可

 

九. 时间相关的一些方法

     9.1 系统时间

显示系统时间date
修改系统时间date -s hh:mm[:ss] / [YYYY.]MM.DD-hh:mm[:ss] / YYYY-MM-DD hh:mm[:ss] / MMDDhhmm[[YY]YY][.ss]

     9.2 rtc时间
hwclock --show 显示硬件时钟时间
hwclock --hctosys 根据硬件时钟修改系统时间
hwclock --systohc 根据系统时间修改硬件时钟

<script>window._bd_share_config={"common":{"bdSnsKey":{},"bdText":"","bdMini":"2","bdMiniList":false,"bdPic":"","bdStyle":"0","bdSize":"16"},"share":{}};with(document)0[(getElementsByTagName('head')[0]||body).appendChild(createElement('script')).src='http://bdimg.share.baidu.com/static/api/js/share.js?v=89860593.js?cdnversion='+~(-new Date()/36e5)];</script>
阅读(1) | 评论(0) | 转发(0) |
0

上一篇:linux 实时时钟(RTC)驱动 .

下一篇:指针

相关热门文章
  • linux 常见服务端口
  • 【ROOTFS搭建】busybox的httpd...
  • xmanager 2.0 for linux配置
  • 什么是shell
  • linux socket的bug??
  • 请问Linux默认shell的是什么 ...
  • 谁能够帮我解决LINUX 2.6 10...
  • 现在的博客积分不会更新了吗?...
  • shell怎么读取网页内容...
  • ssh等待连接的超时问题...
给主人留下些什么吧!~~
原创粉丝点击