File: /usr/src/linux/drivers/macintosh/rtc.c

1     /*
2      * Linux/PowerPC Real Time Clock Driver
3      *
4      * heavily based on:
5      * Linux/SPARC Real Time Clock Driver
6      * Copyright (C) 1996 Thomas K. Dyas (tdyas@eden.rutgers.edu)
7      *
8      * This is a little driver that lets a user-level program access
9      * the PPC clocks chip. It is no use unless you
10      * use the modified clock utility.
11      *
12      * Get the modified clock utility from:
13      *   ftp://vger.rutgers.edu/pub/linux/Sparc/userland/clock.c
14      */
15     
16     #include <linux/module.h>
17     #include <linux/types.h>
18     #include <linux/errno.h>
19     #include <linux/miscdevice.h>
20     #include <linux/slab.h>
21     #include <linux/fcntl.h>
22     #include <linux/poll.h>
23     #include <linux/init.h>
24     #include <linux/mc146818rtc.h>
25     #include <asm/system.h>
26     #include <asm/uaccess.h>
27     #include <asm/machdep.h>
28     
29     #include <asm/time.h>
30     
31     static int rtc_busy = 0;
32     
33     /* Retrieve the current date and time from the real time clock. */
34     void get_rtc_time(struct rtc_time *t)
35     {
36     	unsigned long nowtime;
37         
38     	nowtime = (ppc_md.get_rtc_time)();
39     
40     	to_tm(nowtime, t);
41     
42     	t->tm_year -= 1900;
43     	t->tm_mon -= 1;
44     	t->tm_wday -= 1;
45     }
46     
47     /* Set the current date and time in the real time clock. */
48     void set_rtc_time(struct rtc_time *t)
49     {
50     	unsigned long nowtime;
51     
52     	nowtime = mktime(t->tm_year+1900, t->tm_mon+1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec);
53     
54     	(ppc_md.set_rtc_time)(nowtime);
55     }
56     
57     static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
58     	unsigned long arg)
59     {
60     	struct rtc_time rtc_tm;
61     
62     	switch (cmd)
63     	{
64     	case RTC_RD_TIME:
65     		if (ppc_md.get_rtc_time)
66     		{
67     			get_rtc_time(&rtc_tm);
68     
69     			if (copy_to_user((struct rtc_time*)arg, &rtc_tm, sizeof(struct rtc_time)))
70     				return -EFAULT;
71     
72     			return 0;
73     		}
74     		else
75     			return -EINVAL;
76     
77     	case RTC_SET_TIME:
78     		if (!capable(CAP_SYS_TIME))
79     			return -EPERM;
80     
81     		if (ppc_md.set_rtc_time)
82     		{
83     			if (copy_from_user(&rtc_tm, (struct rtc_time*)arg, sizeof(struct rtc_time)))
84     				return -EFAULT;
85     
86     			set_rtc_time(&rtc_tm);
87     
88     			return 0;
89     		}
90     		else
91     			return -EINVAL;
92     
93     	default:
94     		return -EINVAL;
95     	}
96     }
97     
98     static int rtc_open(struct inode *inode, struct file *file)
99     {
100     	if (rtc_busy)
101     		return -EBUSY;
102     
103     	rtc_busy = 1;
104     
105     	MOD_INC_USE_COUNT;
106     
107     	return 0;
108     }
109     
110     static int rtc_release(struct inode *inode, struct file *file)
111     {
112     	MOD_DEC_USE_COUNT;
113     	rtc_busy = 0;
114     	return 0;
115     }
116     
117     static struct file_operations rtc_fops = {
118     	owner:		THIS_MODULE,
119     	llseek:		no_llseek,
120     	ioctl:		rtc_ioctl,
121     	open:		rtc_open,
122     	release:	rtc_release
123     };
124     
125     static struct miscdevice rtc_dev = { RTC_MINOR, "rtc", &rtc_fops };
126     
127     EXPORT_NO_SYMBOLS;
128     
129     static int __init rtc_init(void)
130     {
131     	int error;
132     
133     	error = misc_register(&rtc_dev);
134     	if (error) {
135     		printk(KERN_ERR "rtc: unable to get misc minor\n");
136     		return error;
137     	}
138     
139     	return 0;
140     }
141     
142     static void __exit rtc_exit(void)
143     {
144     	misc_deregister(&rtc_dev);
145     }
146     
147     module_init(rtc_init);
148     module_exit(rtc_exit);
149