[linux-pm] [PATCH 3/6] [-mm]: ACPI: duplicate ACPI sleep "alarm" attribute in sysfs

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



From: Zhang Rui <rui.zhang at intel.com>

Create /sys/power/alarm.
The way it works is exactly the same as /proc/acpi/alarm.
I.e. "#echo yyyy-mm-dd hh-mm-ss >/sys/power/alarm" supports existing absolute time.
And "#echo +yyyy-mm-dd hh-mm-ss >/sys/power/alarm" supports a duration.

NOTE: It's not very clean to refer struct subsystem power_subsys in ACPI.
But I think /sys/power is the most reasonable place to locate this "alarm" attribute.
So anybody who get better ideas, please let me know.

Signed-off-by: Zhang Rui <rui.zhang at intel.com>
---
 drivers/acpi/sleep/proc.c |  313 ++++++++++++++++++++++++++++++++++++++++++----
 1 files changed, 287 insertions(+), 26 deletions(-)

Index: linux-2.6.20-rc2-mm1/drivers/acpi/sleep/proc.c
===================================================================
--- linux-2.6.20-rc2-mm1.orig/drivers/acpi/sleep/proc.c	2007-01-06 17:53:17.000000000 +0800
+++ linux-2.6.20-rc2-mm1/drivers/acpi/sleep/proc.c	2007-01-06 18:17:53.000000000 +0800
@@ -15,6 +15,290 @@
 
 #define _COMPONENT		ACPI_SYSTEM_COMPONENT
 ACPI_MODULE_NAME("sleep")
+
+static int get_date_field(char **p, u32 * value)
+{
+	char *next = NULL;
+	char *string_end = NULL;
+	int result = -EINVAL;
+
+	/*
+	 * Try to find delimeter, only to insert null.  The end of the
+	 * string won't have one, but is still valid.
+	 */
+	next = strpbrk(*p, "- :");
+	if (next)
+		*next++ = '\0';
+
+	*value = simple_strtoul(*p, &string_end, 10);
+
+	/* Signal success if we got a good digit */
+	if (string_end != *p)
+		result = 0;
+
+	if (next)
+		*p = next;
+
+	return result;
+}
+
+/* --------------------------------------------------------------------------
+                              FS Interface (/sys)
+   -------------------------------------------------------------------------- */
+
+static ssize_t alarm_show(struct subsystem *subsys, char *buf){
+	u32 sec, min, hr;
+	u32 day, mo, yr;
+	unsigned char rtc_control = 0;
+	unsigned long flags;
+	ssize_t result = 0;
+
+	spin_lock_irqsave(&rtc_lock, flags);
+
+	sec = CMOS_READ(RTC_SECONDS_ALARM);
+	min = CMOS_READ(RTC_MINUTES_ALARM);
+	hr = CMOS_READ(RTC_HOURS_ALARM);
+	rtc_control = CMOS_READ(RTC_CONTROL);
+
+	/* If we ever get an FACP with proper values... */
+	if (acpi_gbl_FADT->day_alrm)
+		/* ACPI spec: only low 6 its should be cared */
+		day = CMOS_READ(acpi_gbl_FADT->day_alrm) & 0x3F;
+	else
+		day = CMOS_READ(RTC_DAY_OF_MONTH);
+	if (acpi_gbl_FADT->mon_alrm)
+		mo = CMOS_READ(acpi_gbl_FADT->mon_alrm);
+	else
+		mo = CMOS_READ(RTC_MONTH);
+	if (acpi_gbl_FADT->century)
+		yr = CMOS_READ(acpi_gbl_FADT->century) * 100 +
+		    CMOS_READ(RTC_YEAR);
+	else
+		yr = CMOS_READ(RTC_YEAR);
+
+	spin_unlock_irqrestore(&rtc_lock, flags);
+
+	if (!(rtc_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) {
+		BCD_TO_BIN(sec);
+		BCD_TO_BIN(min);
+		BCD_TO_BIN(hr);
+		BCD_TO_BIN(day);
+		BCD_TO_BIN(mo);
+		BCD_TO_BIN(yr);
+	}
+
+	/* we're trusting the FADT (see above) */
+	if (!acpi_gbl_FADT->century)
+		/* If we're not trusting the FADT, we should at least make it
+		 * right for _this_ century... ehm, what is _this_ century?
+		 *
+		 * TBD:
+		 *  ASAP: find piece of code in the kernel, e.g. star tracker driver,
+		 *        which we can trust to determine the century correctly. Atom
+		 *        watch driver would be nice, too...
+		 *
+		 *  if that has not happened, change for first release in 2050:
+		 *        if (yr<50)
+		 *                yr += 2100;
+		 *        else
+		 *                yr += 2000;   // current line of code
+		 *
+		 *  if that has not happened either, please do on 2099/12/31:23:59:59
+		 *        s/2000/2100
+		 *
+		 */
+		yr += 2000;
+
+	result = sprintf(buf+result, "%4.4u-", yr);
+	if (mo > 12)
+		result += sprintf(buf+result, "**-");
+	else
+		result +=  sprintf(buf+result, "%2.2u-", mo);
+	if(day > 31)
+		result += sprintf(buf+result, "** ");
+	else
+		result +=  sprintf(buf+result, "%2.2u ", day);
+	if(hr > 23)
+		result += sprintf(buf+result, "**:");
+	else
+		result +=  sprintf(buf+result, "%2.2u:", hr);
+	if(min > 59)
+		result += sprintf(buf+result, "**:");
+	else
+		result +=  sprintf(buf+result, "%2.2u:", min);
+	if(sec > 59)
+		result += sprintf(buf+result, "**\n");
+	else
+		result +=  sprintf(buf+result, "%2.2u\n", sec);
+
+	return result;
+}
+
+static ssize_t alarm_store(struct subsystem *subsys, const char *buf, size_t count){
+	int result = 0;
+	char *p = (char*)buf;
+	u32 sec, min, hr, day, mo, yr;
+	int adjust = 0;
+	unsigned char rtc_control = 0;
+
+	if (buf[0] == '+') {
+			p++;
+			adjust = 1;
+		}
+
+	if ((result = get_date_field(&p, &yr)))
+		goto end;
+	if ((result = get_date_field(&p, &mo)))
+		goto end;
+	if ((result = get_date_field(&p, &day)))
+		goto end;
+	if ((result = get_date_field(&p, &hr)))
+		goto end;
+	if ((result = get_date_field(&p, &min)))
+		goto end;
+	if ((result = get_date_field(&p, &sec)))
+		goto end;
+
+	if (sec > 59) {
+		min += 1;
+		sec -= 60;
+	}
+	if (min > 59) {
+		hr += 1;
+		min -= 60;
+	}
+	if (hr > 23) {
+		day += 1;
+		hr -= 24;
+	}
+	if (day > 31) {
+		mo += 1;
+		day -= 31;
+	}
+	if (mo > 12) {
+		yr += 1;
+		mo -= 12;
+	}
+	spin_lock_irq(&rtc_lock);
+
+	rtc_control = CMOS_READ(RTC_CONTROL);
+	if (!(rtc_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) {
+		BIN_TO_BCD(yr);
+		BIN_TO_BCD(mo);
+		BIN_TO_BCD(day);
+		BIN_TO_BCD(hr);
+		BIN_TO_BCD(min);
+		BIN_TO_BCD(sec);
+	}
+
+	if (adjust) {
+		yr += CMOS_READ(RTC_YEAR);
+		mo += CMOS_READ(RTC_MONTH);
+		day += CMOS_READ(RTC_DAY_OF_MONTH);
+		hr += CMOS_READ(RTC_HOURS);
+		min += CMOS_READ(RTC_MINUTES);
+		sec += CMOS_READ(RTC_SECONDS);
+	}
+
+	spin_unlock_irq(&rtc_lock);
+
+	if (!(rtc_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) {
+		BCD_TO_BIN(yr);
+		BCD_TO_BIN(mo);
+		BCD_TO_BIN(day);
+		BCD_TO_BIN(hr);
+		BCD_TO_BIN(min);
+		BCD_TO_BIN(sec);
+	}
+
+	if (sec > 59) {
+		min++;
+		sec -= 60;
+	}
+	if (min > 59) {
+		hr++;
+		min -= 60;
+	}
+	if (hr > 23) {
+		day++;
+		hr -= 24;
+	}
+	if (day > 31) {
+		mo++;
+		day -= 31;
+	}
+	if (mo > 12) {
+		yr++;
+		mo -= 12;
+	}
+	if (!(rtc_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) {
+		BIN_TO_BCD(yr);
+		BIN_TO_BCD(mo);
+		BIN_TO_BCD(day);
+		BIN_TO_BCD(hr);
+		BIN_TO_BCD(min);
+		BIN_TO_BCD(sec);
+	}
+
+	spin_lock_irq(&rtc_lock);
+	/*
+	 * Disable alarm interrupt before setting alarm timer or else
+	 * when ACPI_EVENT_RTC is enabled, a spurious ACPI interrupt occurs
+	 */
+	rtc_control &= ~RTC_AIE;
+	CMOS_WRITE(rtc_control, RTC_CONTROL);
+	CMOS_READ(RTC_INTR_FLAGS);
+
+	/* write the fields the rtc knows about */
+	CMOS_WRITE(hr, RTC_HOURS_ALARM);
+	CMOS_WRITE(min, RTC_MINUTES_ALARM);
+	CMOS_WRITE(sec, RTC_SECONDS_ALARM);
+
+	/*
+	 * If the system supports an enhanced alarm it will have non-zero
+	 * offsets into the CMOS RAM here -- which for some reason are pointing
+	 * to the RTC area of memory.
+	 */
+	if (acpi_gbl_FADT->day_alrm)
+		CMOS_WRITE(day, acpi_gbl_FADT->day_alrm);
+	if (acpi_gbl_FADT->mon_alrm)
+		CMOS_WRITE(mo, acpi_gbl_FADT->mon_alrm);
+	if (acpi_gbl_FADT->century)
+		CMOS_WRITE(yr / 100, acpi_gbl_FADT->century);
+	/* enable the rtc alarm interrupt */
+	rtc_control |= RTC_AIE;
+	CMOS_WRITE(rtc_control, RTC_CONTROL);
+	CMOS_READ(RTC_INTR_FLAGS);
+
+	spin_unlock_irq(&rtc_lock);
+
+	acpi_clear_event(ACPI_EVENT_RTC);
+	acpi_enable_event(ACPI_EVENT_RTC, 0);
+
+	result = count;
+
+      end:
+	return result;
+}
+
+static struct subsys_attribute alarm_attr = {
+	.attr	= {
+		.name = __stringify(alarm),
+		.mode = 0644,
+	},
+	.show	= alarm_show,
+	.store	= alarm_store,
+};
+
+extern struct subsystem power_subsys;
+static int alarm_add_sysfs(void)
+{
+	return sysfs_create_file(&power_subsys.kset.kobj, &alarm_attr.attr);
+}
+
+/* --------------------------------------------------------------------------
+                              FS Interface (/proc)
+   -------------------------------------------------------------------------- */
 #ifdef	CONFIG_ACPI_SLEEP_PROC_SLEEP
 static int acpi_system_sleep_seq_show(struct seq_file *seq, void *offset)
 {
@@ -150,32 +434,6 @@ static int acpi_system_alarm_open_fs(str
 	return single_open(file, acpi_system_alarm_seq_show, PDE(inode)->data);
 }
 
-static int get_date_field(char **p, u32 * value)
-{
-	char *next = NULL;
-	char *string_end = NULL;
-	int result = -EINVAL;
-
-	/*
-	 * Try to find delimeter, only to insert null.  The end of the
-	 * string won't have one, but is still valid.
-	 */
-	next = strpbrk(*p, "- :");
-	if (next)
-		*next++ = '\0';
-
-	*value = simple_strtoul(*p, &string_end, 10);
-
-	/* Signal success if we got a good digit */
-	if (string_end != *p)
-		result = 0;
-
-	if (next)
-		*p = next;
-
-	return result;
-}
-
 static ssize_t
 acpi_system_write_alarm(struct file *file,
 			const char __user * buffer, size_t count, loff_t * ppos)
@@ -498,6 +756,9 @@ static int acpi_sleep_proc_init(void)
 	if (entry)
 		entry->proc_fops = &acpi_system_wakeup_device_fops;
 
+	if(alarm_add_sysfs())
+		return -EINVAL;
+
 	acpi_install_fixed_event_handler(ACPI_EVENT_RTC, rtc_handler, NULL);
 	return 0;
 }


[Index of Archives]     [Linux ACPI]     [Netdev]     [Ethernet Bridging]     [Linux Wireless]     [CPU Freq]     [Kernel Newbies]     [Fedora Kernel]     [Security]     [Linux for Hams]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux RAID]     [Linux Admin]     [Samba]

  Powered by Linux