VServer 1.9.2 (patch-2.6.8.1-vs1.9.2.diff)
[linux-2.6.git] / drivers / acpi / sleep / proc.c
1 #include <linux/proc_fs.h>
2 #include <linux/seq_file.h>
3 #include <linux/suspend.h>
4 #include <linux/bcd.h>
5 #include <asm/uaccess.h>
6
7 #include <acpi/acpi_bus.h>
8 #include <acpi/acpi_drivers.h>
9
10 #ifdef CONFIG_X86
11 #include <linux/mc146818rtc.h>
12 #endif
13
14 #include "sleep.h"
15
16 #define ACPI_SYSTEM_FILE_SLEEP          "sleep"
17 #define ACPI_SYSTEM_FILE_ALARM          "alarm"
18
19 #define _COMPONENT              ACPI_SYSTEM_COMPONENT
20 ACPI_MODULE_NAME                ("sleep")
21
22
23 static int acpi_system_sleep_seq_show(struct seq_file *seq, void *offset)
24 {
25         int                     i;
26
27         ACPI_FUNCTION_TRACE("acpi_system_sleep_seq_show");
28
29         for (i = 0; i <= ACPI_STATE_S5; i++) {
30                 if (sleep_states[i]) {
31                         seq_printf(seq,"S%d ", i);
32                         if (i == ACPI_STATE_S4 && acpi_gbl_FACS->S4bios_f)
33                                 seq_printf(seq, "S4bios ");
34                 }
35         }
36
37         seq_puts(seq, "\n");
38
39         return 0;
40 }
41
42 static int acpi_system_sleep_open_fs(struct inode *inode, struct file *file)
43 {
44         return single_open(file, acpi_system_sleep_seq_show, PDE(inode)->data);
45 }
46
47 static ssize_t
48 acpi_system_write_sleep (
49         struct file             *file,
50         const char __user       *buffer,
51         size_t                  count,
52         loff_t                  *ppos)
53 {
54         char    str[12];
55         u32     state = 0;
56         int     error = 0;
57
58         if (count > sizeof(str) - 1)
59                 goto Done;
60         memset(str,0,sizeof(str));
61         if (copy_from_user(str, buffer, count))
62                 return -EFAULT;
63
64         /* Check for S4 bios request */
65         if (!strcmp(str,"4b")) {
66                 error = acpi_suspend(4);
67                 goto Done;
68         }
69         state = simple_strtoul(str, NULL, 0);
70 #ifdef CONFIG_SOFTWARE_SUSPEND
71         if (state == 4) {
72                 software_suspend();
73                 goto Done;
74         }
75 #endif
76         error = acpi_suspend(state);
77  Done:
78         return error ? error : count;
79 }
80
81 static int acpi_system_alarm_seq_show(struct seq_file *seq, void *offset)
82 {
83         u32                     sec, min, hr;
84         u32                     day, mo, yr;
85
86         ACPI_FUNCTION_TRACE("acpi_system_alarm_seq_show");
87
88         spin_lock(&rtc_lock);
89
90         sec = CMOS_READ(RTC_SECONDS_ALARM);
91         min = CMOS_READ(RTC_MINUTES_ALARM);
92         hr = CMOS_READ(RTC_HOURS_ALARM);
93
94 #if 0   /* If we ever get an FACP with proper values... */
95         if (acpi_gbl_FADT->day_alrm)
96                 day = CMOS_READ(acpi_gbl_FADT->day_alrm);
97         else
98                 day =  CMOS_READ(RTC_DAY_OF_MONTH);
99         if (acpi_gbl_FADT->mon_alrm)
100                 mo = CMOS_READ(acpi_gbl_FADT->mon_alrm);
101         else
102                 mo = CMOS_READ(RTC_MONTH);
103         if (acpi_gbl_FADT->century)
104                 yr = CMOS_READ(acpi_gbl_FADT->century) * 100 + CMOS_READ(RTC_YEAR);
105         else
106                 yr = CMOS_READ(RTC_YEAR);
107 #else
108         day = CMOS_READ(RTC_DAY_OF_MONTH);
109         mo = CMOS_READ(RTC_MONTH);
110         yr = CMOS_READ(RTC_YEAR);
111 #endif
112
113         spin_unlock(&rtc_lock);
114
115         BCD_TO_BIN(sec);
116         BCD_TO_BIN(min);
117         BCD_TO_BIN(hr);
118         BCD_TO_BIN(day);
119         BCD_TO_BIN(mo);
120         BCD_TO_BIN(yr);
121
122 #if 0
123         /* we're trusting the FADT (see above)*/
124 #else
125         /* If we're not trusting the FADT, we should at least make it
126          * right for _this_ century... ehm, what is _this_ century?
127          *
128          * TBD:
129          *  ASAP: find piece of code in the kernel, e.g. star tracker driver,
130          *        which we can trust to determine the century correctly. Atom
131          *        watch driver would be nice, too...
132          *
133          *  if that has not happened, change for first release in 2050:
134          *        if (yr<50)
135          *                yr += 2100;
136          *        else
137          *                yr += 2000;   // current line of code
138          *
139          *  if that has not happened either, please do on 2099/12/31:23:59:59
140          *        s/2000/2100
141          *
142          */
143         yr += 2000;
144 #endif
145
146         seq_printf(seq,"%4.4u-", yr);
147         (mo > 12)  ? seq_puts(seq, "**-")  : seq_printf(seq, "%2.2u-", mo);
148         (day > 31) ? seq_puts(seq, "** ")  : seq_printf(seq, "%2.2u ", day);
149         (hr > 23)  ? seq_puts(seq, "**:")  : seq_printf(seq, "%2.2u:", hr);
150         (min > 59) ? seq_puts(seq, "**:")  : seq_printf(seq, "%2.2u:", min);
151         (sec > 59) ? seq_puts(seq, "**\n") : seq_printf(seq, "%2.2u\n", sec);
152
153         return 0;
154 }
155
156 static int acpi_system_alarm_open_fs(struct inode *inode, struct file *file)
157 {
158         return single_open(file, acpi_system_alarm_seq_show, PDE(inode)->data);
159 }
160
161
162 static int
163 get_date_field (
164         char                    **p,
165         u32                     *value)
166 {
167         char                    *next = NULL;
168         char                    *string_end = NULL;
169         int                     result = -EINVAL;
170
171         /*
172          * Try to find delimeter, only to insert null.  The end of the
173          * string won't have one, but is still valid.
174          */
175         next = strpbrk(*p, "- :");
176         if (next)
177                 *next++ = '\0';
178
179         *value = simple_strtoul(*p, &string_end, 10);
180
181         /* Signal success if we got a good digit */
182         if (string_end != *p)
183                 result = 0;
184
185         if (next)
186                 *p = next;
187
188         return result;
189 }
190
191
192 static ssize_t
193 acpi_system_write_alarm (
194         struct file             *file,
195         const char __user       *buffer,
196         size_t                  count,
197         loff_t                  *ppos)
198 {
199         int                     result = 0;
200         char                    alarm_string[30] = {'\0'};
201         char                    *p = alarm_string;
202         u32                     sec, min, hr, day, mo, yr;
203         int                     adjust = 0;
204         unsigned char           rtc_control = 0;
205
206         ACPI_FUNCTION_TRACE("acpi_system_write_alarm");
207
208         if (count > sizeof(alarm_string) - 1)
209                 return_VALUE(-EINVAL);
210         
211         if (copy_from_user(alarm_string, buffer, count))
212                 return_VALUE(-EFAULT);
213
214         alarm_string[count] = '\0';
215
216         /* check for time adjustment */
217         if (alarm_string[0] == '+') {
218                 p++;
219                 adjust = 1;
220         }
221
222         if ((result = get_date_field(&p, &yr)))
223                 goto end;
224         if ((result = get_date_field(&p, &mo)))
225                 goto end;
226         if ((result = get_date_field(&p, &day)))
227                 goto end;
228         if ((result = get_date_field(&p, &hr)))
229                 goto end;
230         if ((result = get_date_field(&p, &min)))
231                 goto end;
232         if ((result = get_date_field(&p, &sec)))
233                 goto end;
234
235         if (sec > 59) {
236                 min += 1;
237                 sec -= 60;
238         }
239         if (min > 59) {
240                 hr += 1;
241                 min -= 60;
242         }
243         if (hr > 23) {
244                 day += 1;
245                 hr -= 24;
246         }
247         if (day > 31) {
248                 mo += 1;
249                 day -= 31;
250         }
251         if (mo > 12) {
252                 yr += 1;
253                 mo -= 12;
254         }
255
256         spin_lock_irq(&rtc_lock);
257
258         rtc_control = CMOS_READ(RTC_CONTROL);
259         if (!(rtc_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) {
260                 BIN_TO_BCD(yr);
261                 BIN_TO_BCD(mo);
262                 BIN_TO_BCD(day);
263                 BIN_TO_BCD(hr);
264                 BIN_TO_BCD(min);
265                 BIN_TO_BCD(sec);
266         }
267
268         if (adjust) {
269                 yr  += CMOS_READ(RTC_YEAR);
270                 mo  += CMOS_READ(RTC_MONTH);
271                 day += CMOS_READ(RTC_DAY_OF_MONTH);
272                 hr  += CMOS_READ(RTC_HOURS);
273                 min += CMOS_READ(RTC_MINUTES);
274                 sec += CMOS_READ(RTC_SECONDS);
275         }
276
277         spin_unlock_irq(&rtc_lock);
278
279         if (!(rtc_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) {
280                 BCD_TO_BIN(yr);
281                 BCD_TO_BIN(mo);
282                 BCD_TO_BIN(day);
283                 BCD_TO_BIN(hr);
284                 BCD_TO_BIN(min);
285                 BCD_TO_BIN(sec);
286         }
287
288         if (sec > 59) {
289                 min++;
290                 sec -= 60;
291         }
292         if (min > 59) {
293                 hr++;
294                 min -= 60;
295         }
296         if (hr > 23) {
297                 day++;
298                 hr -= 24;
299         }
300         if (day > 31) {
301                 mo++;
302                 day -= 31;
303         }
304         if (mo > 12) {
305                 yr++;
306                 mo -= 12;
307         }
308         if (!(rtc_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) {
309                 BIN_TO_BCD(yr);
310                 BIN_TO_BCD(mo);
311                 BIN_TO_BCD(day);
312                 BIN_TO_BCD(hr);
313                 BIN_TO_BCD(min);
314                 BIN_TO_BCD(sec);
315         }
316
317         spin_lock_irq(&rtc_lock);
318
319         /* write the fields the rtc knows about */
320         CMOS_WRITE(hr, RTC_HOURS_ALARM);
321         CMOS_WRITE(min, RTC_MINUTES_ALARM);
322         CMOS_WRITE(sec, RTC_SECONDS_ALARM);
323
324         /*
325          * If the system supports an enhanced alarm it will have non-zero
326          * offsets into the CMOS RAM here -- which for some reason are pointing
327          * to the RTC area of memory.
328          */
329 #if 0
330         if (acpi_gbl_FADT->day_alrm)
331                 CMOS_WRITE(day, acpi_gbl_FADT->day_alrm);
332         if (acpi_gbl_FADT->mon_alrm)
333                 CMOS_WRITE(mo, acpi_gbl_FADT->mon_alrm);
334         if (acpi_gbl_FADT->century)
335                 CMOS_WRITE(yr/100, acpi_gbl_FADT->century);
336 #endif
337         /* enable the rtc alarm interrupt */
338         if (!(rtc_control & RTC_AIE)) {
339                 rtc_control |= RTC_AIE;
340                 CMOS_WRITE(rtc_control,RTC_CONTROL);
341                 CMOS_READ(RTC_INTR_FLAGS);
342         }
343
344         spin_unlock_irq(&rtc_lock);
345
346         acpi_set_register(ACPI_BITREG_RT_CLOCK_ENABLE, 1, ACPI_MTX_LOCK);
347
348         *ppos += count;
349
350         result = 0;
351 end:
352         return_VALUE(result ? result : count);
353 }
354
355
356 static struct file_operations acpi_system_sleep_fops = {
357         .open           = acpi_system_sleep_open_fs,
358         .read           = seq_read,
359         .write          = acpi_system_write_sleep,
360         .llseek         = seq_lseek,
361         .release        = single_release,
362 };
363
364 static struct file_operations acpi_system_alarm_fops = {
365         .open           = acpi_system_alarm_open_fs,
366         .read           = seq_read,
367         .write          = acpi_system_write_alarm,
368         .llseek         = seq_lseek,
369         .release        = single_release,
370 };
371
372
373 static int acpi_sleep_proc_init(void)
374 {
375         struct proc_dir_entry   *entry = NULL;
376
377         if (acpi_disabled)
378                 return 0;
379  
380         /* 'sleep' [R/W]*/
381         entry = create_proc_entry(ACPI_SYSTEM_FILE_SLEEP,
382                                   S_IFREG|S_IRUGO|S_IWUSR, acpi_root_dir);
383         if (entry)
384                 entry->proc_fops = &acpi_system_sleep_fops;
385
386         /* 'alarm' [R/W] */
387         entry = create_proc_entry(ACPI_SYSTEM_FILE_ALARM,
388                 S_IFREG|S_IRUGO|S_IWUSR, acpi_root_dir);
389         if (entry)
390                 entry->proc_fops = &acpi_system_alarm_fops;
391         return 0;
392 }
393
394 late_initcall(acpi_sleep_proc_init);