diff options
| author | Ingo Molnar <mingo@elte.hu> | 2008-07-25 05:37:07 -0400 |
|---|---|---|
| committer | Ingo Molnar <mingo@elte.hu> | 2008-07-25 05:37:07 -0400 |
| commit | 0e2f65ee30eee2db054f7fd73f462c5da33ec963 (patch) | |
| tree | 26c61eb7745da0c0d9135e9d12088f570cb8530d /drivers/rtc/interface.c | |
| parent | da7878d75b8520c9ae00d27dfbbce546a7bfdfbb (diff) | |
| parent | fb2e405fc1fc8b20d9c78eaa1c7fd5a297efde43 (diff) | |
Merge branch 'linus' into x86/pebs
Conflicts:
arch/x86/Kconfig.cpu
arch/x86/kernel/cpu/intel.c
arch/x86/kernel/setup_64.c
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'drivers/rtc/interface.c')
| -rw-r--r-- | drivers/rtc/interface.c | 104 |
1 files changed, 92 insertions, 12 deletions
diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c index 7e3ad4f3b343..d397fa5f3a91 100644 --- a/drivers/rtc/interface.c +++ b/drivers/rtc/interface.c | |||
| @@ -126,12 +126,25 @@ int rtc_read_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm) | |||
| 126 | int err; | 126 | int err; |
| 127 | struct rtc_time before, now; | 127 | struct rtc_time before, now; |
| 128 | int first_time = 1; | 128 | int first_time = 1; |
| 129 | unsigned long t_now, t_alm; | ||
| 130 | enum { none, day, month, year } missing = none; | ||
| 131 | unsigned days; | ||
| 129 | 132 | ||
| 130 | /* The lower level RTC driver may not be capable of filling | 133 | /* The lower level RTC driver may return -1 in some fields, |
| 131 | * in all fields of the rtc_time struct (eg. rtc-cmos), | 134 | * creating invalid alarm->time values, for reasons like: |
| 132 | * and so might instead return -1 in some fields. | 135 | * |
| 133 | * We deal with that here by grabbing a current RTC timestamp | 136 | * - The hardware may not be capable of filling them in; |
| 134 | * and using values from that for any missing (-1) values. | 137 | * many alarms match only on time-of-day fields, not |
| 138 | * day/month/year calendar data. | ||
| 139 | * | ||
| 140 | * - Some hardware uses illegal values as "wildcard" match | ||
| 141 | * values, which non-Linux firmware (like a BIOS) may try | ||
| 142 | * to set up as e.g. "alarm 15 minutes after each hour". | ||
| 143 | * Linux uses only oneshot alarms. | ||
| 144 | * | ||
| 145 | * When we see that here, we deal with it by using values from | ||
| 146 | * a current RTC timestamp for any missing (-1) values. The | ||
| 147 | * RTC driver prevents "periodic alarm" modes. | ||
| 135 | * | 148 | * |
| 136 | * But this can be racey, because some fields of the RTC timestamp | 149 | * But this can be racey, because some fields of the RTC timestamp |
| 137 | * may have wrapped in the interval since we read the RTC alarm, | 150 | * may have wrapped in the interval since we read the RTC alarm, |
| @@ -174,6 +187,10 @@ int rtc_read_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm) | |||
| 174 | if (!alarm->enabled) | 187 | if (!alarm->enabled) |
| 175 | return 0; | 188 | return 0; |
| 176 | 189 | ||
| 190 | /* full-function RTCs won't have such missing fields */ | ||
| 191 | if (rtc_valid_tm(&alarm->time) == 0) | ||
| 192 | return 0; | ||
| 193 | |||
| 177 | /* get the "after" timestamp, to detect wrapped fields */ | 194 | /* get the "after" timestamp, to detect wrapped fields */ |
| 178 | err = rtc_read_time(rtc, &now); | 195 | err = rtc_read_time(rtc, &now); |
| 179 | if (err < 0) | 196 | if (err < 0) |
| @@ -183,22 +200,85 @@ int rtc_read_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm) | |||
| 183 | } while ( before.tm_min != now.tm_min | 200 | } while ( before.tm_min != now.tm_min |
| 184 | || before.tm_hour != now.tm_hour | 201 | || before.tm_hour != now.tm_hour |
| 185 | || before.tm_mon != now.tm_mon | 202 | || before.tm_mon != now.tm_mon |
| 186 | || before.tm_year != now.tm_year | 203 | || before.tm_year != now.tm_year); |
| 187 | || before.tm_isdst != now.tm_isdst); | ||
| 188 | 204 | ||
| 189 | /* Fill in any missing alarm fields using the timestamp */ | 205 | /* Fill in the missing alarm fields using the timestamp; we |
| 206 | * know there's at least one since alarm->time is invalid. | ||
| 207 | */ | ||
| 190 | if (alarm->time.tm_sec == -1) | 208 | if (alarm->time.tm_sec == -1) |
| 191 | alarm->time.tm_sec = now.tm_sec; | 209 | alarm->time.tm_sec = now.tm_sec; |
| 192 | if (alarm->time.tm_min == -1) | 210 | if (alarm->time.tm_min == -1) |
| 193 | alarm->time.tm_min = now.tm_min; | 211 | alarm->time.tm_min = now.tm_min; |
| 194 | if (alarm->time.tm_hour == -1) | 212 | if (alarm->time.tm_hour == -1) |
| 195 | alarm->time.tm_hour = now.tm_hour; | 213 | alarm->time.tm_hour = now.tm_hour; |
| 196 | if (alarm->time.tm_mday == -1) | 214 | |
| 215 | /* For simplicity, only support date rollover for now */ | ||
| 216 | if (alarm->time.tm_mday == -1) { | ||
| 197 | alarm->time.tm_mday = now.tm_mday; | 217 | alarm->time.tm_mday = now.tm_mday; |
| 198 | if (alarm->time.tm_mon == -1) | 218 | missing = day; |
| 219 | } | ||
| 220 | if (alarm->time.tm_mon == -1) { | ||
| 199 | alarm->time.tm_mon = now.tm_mon; | 221 | alarm->time.tm_mon = now.tm_mon; |
| 200 | if (alarm->time.tm_year == -1) | 222 | if (missing == none) |
| 223 | missing = month; | ||
| 224 | } | ||
| 225 | if (alarm->time.tm_year == -1) { | ||
| 201 | alarm->time.tm_year = now.tm_year; | 226 | alarm->time.tm_year = now.tm_year; |
| 227 | if (missing == none) | ||
| 228 | missing = year; | ||
| 229 | } | ||
| 230 | |||
| 231 | /* with luck, no rollover is needed */ | ||
| 232 | rtc_tm_to_time(&now, &t_now); | ||
| 233 | rtc_tm_to_time(&alarm->time, &t_alm); | ||
| 234 | if (t_now < t_alm) | ||
| 235 | goto done; | ||
| 236 | |||
| 237 | switch (missing) { | ||
| 238 | |||
| 239 | /* 24 hour rollover ... if it's now 10am Monday, an alarm that | ||
| 240 | * that will trigger at 5am will do so at 5am Tuesday, which | ||
| 241 | * could also be in the next month or year. This is a common | ||
| 242 | * case, especially for PCs. | ||
| 243 | */ | ||
| 244 | case day: | ||
| 245 | dev_dbg(&rtc->dev, "alarm rollover: %s\n", "day"); | ||
| 246 | t_alm += 24 * 60 * 60; | ||
| 247 | rtc_time_to_tm(t_alm, &alarm->time); | ||
| 248 | break; | ||
| 249 | |||
| 250 | /* Month rollover ... if it's the 31th, an alarm on the 3rd will | ||
| 251 | * be next month. An alarm matching on the 30th, 29th, or 28th | ||
| 252 | * may end up in the month after that! Many newer PCs support | ||
| 253 | * this type of alarm. | ||
| 254 | */ | ||
| 255 | case month: | ||
| 256 | dev_dbg(&rtc->dev, "alarm rollover: %s\n", "month"); | ||
| 257 | do { | ||
| 258 | if (alarm->time.tm_mon < 11) | ||
| 259 | alarm->time.tm_mon++; | ||
| 260 | else { | ||
| 261 | alarm->time.tm_mon = 0; | ||
| 262 | alarm->time.tm_year++; | ||
| 263 | } | ||
| 264 | days = rtc_month_days(alarm->time.tm_mon, | ||
| 265 | alarm->time.tm_year); | ||
| 266 | } while (days < alarm->time.tm_mday); | ||
| 267 | break; | ||
| 268 | |||
| 269 | /* Year rollover ... easy except for leap years! */ | ||
| 270 | case year: | ||
| 271 | dev_dbg(&rtc->dev, "alarm rollover: %s\n", "year"); | ||
| 272 | do { | ||
| 273 | alarm->time.tm_year++; | ||
| 274 | } while (!rtc_valid_tm(&alarm->time)); | ||
| 275 | break; | ||
| 276 | |||
| 277 | default: | ||
| 278 | dev_warn(&rtc->dev, "alarm rollover not handled\n"); | ||
| 279 | } | ||
| 280 | |||
| 281 | done: | ||
| 202 | return 0; | 282 | return 0; |
| 203 | } | 283 | } |
| 204 | EXPORT_SYMBOL_GPL(rtc_read_alarm); | 284 | EXPORT_SYMBOL_GPL(rtc_read_alarm); |
| @@ -265,7 +345,7 @@ struct rtc_device *rtc_class_open(char *name) | |||
| 265 | struct device *dev; | 345 | struct device *dev; |
| 266 | struct rtc_device *rtc = NULL; | 346 | struct rtc_device *rtc = NULL; |
| 267 | 347 | ||
| 268 | dev = class_find_device(rtc_class, name, __rtc_match); | 348 | dev = class_find_device(rtc_class, NULL, name, __rtc_match); |
| 269 | if (dev) | 349 | if (dev) |
| 270 | rtc = to_rtc_device(dev); | 350 | rtc = to_rtc_device(dev); |
| 271 | 351 | ||
