IntelFrameworkModulePkg AcpiS3SaveDxe: Remove ASSERT, add lock and remove RT for...
[mirror_edk2.git] / PcAtChipsetPkg / PcatRealTimeClockRuntimeDxe / PcRtc.c
CommitLineData
fb0b259e 1/** @file\r
2 RTC Architectural Protocol GUID as defined in DxeCis 0.96.\r
8cd4d17c 3\r
e02c57b6 4Copyright (c) 2006 - 2011, Intel Corporation. All rights reserved.<BR>\r
95d48e82 5This program and the accompanying materials\r
3cfb790c 6are licensed and made available under the terms and conditions of the BSD License\r
7which accompanies this distribution. The full text of the license may be found at\r
8http://opensource.org/licenses/bsd-license.php\r
9\r
10THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
11WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
8cd4d17c 12\r
fb0b259e 13**/\r
8cd4d17c 14\r
15#include "PcRtc.h"\r
16\r
8d85dc31 17/**\r
18 Compare the Hour, Minute and Second of the From time and the To time.\r
19 \r
20 Only compare H/M/S in EFI_TIME and ignore other fields here.\r
21\r
22 @param From the first time\r
23 @param To the second time\r
24\r
25 @return >0 The H/M/S of the From time is later than those of To time\r
26 @return ==0 The H/M/S of the From time is same as those of To time\r
27 @return <0 The H/M/S of the From time is earlier than those of To time\r
28**/\r
8cd4d17c 29INTN\r
30CompareHMS (\r
31 IN EFI_TIME *From,\r
32 IN EFI_TIME *To\r
33 );\r
34\r
8d85dc31 35/**\r
36 To check if second date is later than first date within 24 hours.\r
37\r
38 @param From the first date\r
39 @param To the second date\r
40\r
41 @retval TRUE From is previous to To within 24 hours.\r
42 @retval FALSE From is later, or it is previous to To more than 24 hours.\r
43**/\r
8cd4d17c 44BOOLEAN\r
45IsWithinOneDay (\r
46 IN EFI_TIME *From,\r
47 IN EFI_TIME *To\r
48 );\r
49\r
8d85dc31 50/**\r
51 Read RTC content through its registers.\r
52\r
53 @param Address Address offset of RTC. It is recommended to use macros such as\r
54 RTC_ADDRESS_SECONDS.\r
55\r
56 @return The data of UINT8 type read from RTC.\r
57**/\r
8cd4d17c 58UINT8\r
59RtcRead (\r
60 IN UINT8 Address\r
61 )\r
8cd4d17c 62{\r
63 IoWrite8 (PCAT_RTC_ADDRESS_REGISTER, (UINT8) (Address | (UINT8) (IoRead8 (PCAT_RTC_ADDRESS_REGISTER) & 0x80)));\r
64 return IoRead8 (PCAT_RTC_DATA_REGISTER);\r
65}\r
66\r
8d85dc31 67/**\r
68 Write RTC through its registers.\r
69\r
70 @param Address Address offset of RTC. It is recommended to use macros such as\r
71 RTC_ADDRESS_SECONDS.\r
72 @param Data The content you want to write into RTC.\r
73\r
74**/\r
8cd4d17c 75VOID\r
76RtcWrite (\r
77 IN UINT8 Address,\r
78 IN UINT8 Data\r
79 )\r
8cd4d17c 80{\r
81 IoWrite8 (PCAT_RTC_ADDRESS_REGISTER, (UINT8) (Address | (UINT8) (IoRead8 (PCAT_RTC_ADDRESS_REGISTER) & 0x80)));\r
82 IoWrite8 (PCAT_RTC_DATA_REGISTER, Data);\r
83}\r
84\r
8d85dc31 85/**\r
86 Initialize RTC.\r
87\r
88 @param Global For global use inside this module.\r
89\r
90 @retval EFI_DEVICE_ERROR Initialization failed due to device error.\r
91 @retval EFI_SUCCESS Initialization successful.\r
92\r
93**/\r
8cd4d17c 94EFI_STATUS\r
95PcRtcInit (\r
96 IN PC_RTC_MODULE_GLOBALS *Global\r
97 )\r
8cd4d17c 98{\r
99 EFI_STATUS Status;\r
100 RTC_REGISTER_A RegisterA;\r
101 RTC_REGISTER_B RegisterB;\r
102 RTC_REGISTER_D RegisterD;\r
103 UINT8 Century;\r
104 EFI_TIME Time;\r
ec35e997
LG
105 UINTN DataSize;\r
106 UINT32 TimerVar;\r
8cd4d17c 107\r
108 //\r
109 // Acquire RTC Lock to make access to RTC atomic\r
110 //\r
8cd4d17c 111 if (!EfiAtRuntime ()) {\r
8d85dc31 112 EfiAcquireLock (&Global->RtcLock);\r
8cd4d17c 113 }\r
114 //\r
115 // Initialize RTC Register\r
116 //\r
117 // Make sure Division Chain is properly configured,\r
118 // or RTC clock won't "tick" -- time won't increment\r
119 //\r
120 RegisterA.Data = RTC_INIT_REGISTER_A;\r
121 RtcWrite (RTC_ADDRESS_REGISTER_A, RegisterA.Data);\r
122\r
123 //\r
124 // Read Register B\r
125 //\r
126 RegisterB.Data = RtcRead (RTC_ADDRESS_REGISTER_B);\r
127\r
128 //\r
129 // Clear RTC flag register\r
130 //\r
131 RtcRead (RTC_ADDRESS_REGISTER_C);\r
132\r
133 //\r
134 // Clear RTC register D\r
135 //\r
136 RegisterD.Data = RTC_INIT_REGISTER_D;\r
137 RtcWrite (RTC_ADDRESS_REGISTER_D, RegisterD.Data);\r
138\r
139 //\r
140 // Wait for up to 0.1 seconds for the RTC to be updated\r
141 //\r
f8ea3026 142 Status = RtcWaitToUpdate (PcdGet32 (PcdRealTimeClockUpdateTimeout));\r
8cd4d17c 143 if (EFI_ERROR (Status)) {\r
6ff84d99 144 //\r
145 // Set the variable with default value if the RTC is functioning incorrectly.\r
146 //\r
147 Global->SavedTimeZone = EFI_UNSPECIFIED_TIMEZONE;\r
148 Global->Daylight = 0;\r
8cd4d17c 149 if (!EfiAtRuntime ()) {\r
8d85dc31 150 EfiReleaseLock (&Global->RtcLock);\r
8cd4d17c 151 }\r
152 return EFI_DEVICE_ERROR;\r
153 }\r
154 //\r
155 // Get the Time/Date/Daylight Savings values.\r
156 //\r
157 Time.Second = RtcRead (RTC_ADDRESS_SECONDS);\r
158 Time.Minute = RtcRead (RTC_ADDRESS_MINUTES);\r
159 Time.Hour = RtcRead (RTC_ADDRESS_HOURS);\r
160 Time.Day = RtcRead (RTC_ADDRESS_DAY_OF_THE_MONTH);\r
161 Time.Month = RtcRead (RTC_ADDRESS_MONTH);\r
162 Time.Year = RtcRead (RTC_ADDRESS_YEAR);\r
163\r
9a1eee23 164 Century = RtcRead (RTC_ADDRESS_CENTURY);\r
9a1eee23 165 \r
8cd4d17c 166 //\r
167 // Set RTC configuration after get original time\r
ec35e997 168 // The value of bit AIE should be reserved.\r
8cd4d17c 169 //\r
f7033880 170 RtcWrite (RTC_ADDRESS_REGISTER_B, (UINT8)(RTC_INIT_REGISTER_B | (RegisterB.Data & BIT5)));\r
8cd4d17c 171\r
172 //\r
173 // Release RTC Lock.\r
174 //\r
8cd4d17c 175 if (!EfiAtRuntime ()) {\r
8d85dc31 176 EfiReleaseLock (&Global->RtcLock);\r
8cd4d17c 177 }\r
254ba247 178 \r
6bfa178c 179 //\r
180 // Get the data of Daylight saving and time zone, if they have been\r
181 // stored in NV variable during previous boot.\r
182 //\r
183 DataSize = sizeof (UINT32);\r
184 Status = EfiGetVariable (\r
185 L"RTC",\r
186 &gEfiCallerIdGuid,\r
187 NULL,\r
188 &DataSize,\r
189 (VOID *) &TimerVar\r
190 );\r
191 if (!EFI_ERROR (Status)) {\r
192 Time.TimeZone = (INT16) TimerVar;\r
193 Time.Daylight = (UINT8) (TimerVar >> 16);\r
194 } else {\r
195 Time.TimeZone = EFI_UNSPECIFIED_TIMEZONE;\r
196 Time.Daylight = 0; \r
197 }\r
198\r
8cd4d17c 199 //\r
200 // Validate time fields\r
201 //\r
254ba247 202 Status = ConvertRtcTimeToEfiTime (&Time, Century, RegisterB);\r
203 if (!EFI_ERROR (Status)) {\r
204 Status = RtcTimeFieldsValid (&Time);\r
205 }\r
8cd4d17c 206 if (EFI_ERROR (Status)) {\r
44d52203 207 //\r
208 // Report Status Code to indicate that the RTC has bad date and time\r
209 //\r
210 REPORT_STATUS_CODE (\r
211 EFI_ERROR_CODE | EFI_ERROR_MINOR,\r
212 (EFI_SOFTWARE_DXE_RT_DRIVER | EFI_SW_EC_BAD_DATE_TIME)\r
213 );\r
8cd4d17c 214 Time.Second = RTC_INIT_SECOND;\r
215 Time.Minute = RTC_INIT_MINUTE;\r
216 Time.Hour = RTC_INIT_HOUR;\r
217 Time.Day = RTC_INIT_DAY;\r
218 Time.Month = RTC_INIT_MONTH;\r
219 Time.Year = RTC_INIT_YEAR;\r
2ba53a4f 220 Time.Nanosecond = 0;\r
9bb1fced 221 Time.TimeZone = EFI_UNSPECIFIED_TIMEZONE;\r
222 Time.Daylight = 0;\r
8cd4d17c 223 }\r
ec35e997 224\r
ec35e997 225 //\r
8cd4d17c 226 // Reset time value according to new RTC configuration\r
227 //\r
2ba53a4f 228 Status = PcRtcSetTime (&Time, Global);\r
229 if(!EFI_ERROR (Status)) {\r
230 return EFI_SUCCESS;\r
231 } else {\r
232 return EFI_DEVICE_ERROR;\r
233 }\r
8cd4d17c 234}\r
235\r
8d85dc31 236/**\r
237 Returns the current time and date information, and the time-keeping capabilities\r
238 of the hardware platform.\r
8cd4d17c 239\r
8d85dc31 240 @param Time A pointer to storage to receive a snapshot of the current time.\r
241 @param Capabilities An optional pointer to a buffer to receive the real time clock\r
242 device's capabilities.\r
243 @param Global For global use inside this module.\r
8cd4d17c 244\r
8d85dc31 245 @retval EFI_SUCCESS The operation completed successfully.\r
246 @retval EFI_INVALID_PARAMETER Time is NULL.\r
247 @retval EFI_DEVICE_ERROR The time could not be retrieved due to hardware error.\r
8cd4d17c 248\r
8d85dc31 249**/\r
250EFI_STATUS\r
251PcRtcGetTime (\r
252 OUT EFI_TIME *Time,\r
253 OUT EFI_TIME_CAPABILITIES *Capabilities, OPTIONAL\r
254 IN PC_RTC_MODULE_GLOBALS *Global\r
255 )\r
8cd4d17c 256{\r
257 EFI_STATUS Status;\r
258 RTC_REGISTER_B RegisterB;\r
259 UINT8 Century;\r
260\r
261 //\r
262 // Check parameters for null pointer\r
263 //\r
264 if (Time == NULL) {\r
265 return EFI_INVALID_PARAMETER;\r
266\r
267 }\r
268 //\r
269 // Acquire RTC Lock to make access to RTC atomic\r
270 //\r
8cd4d17c 271 if (!EfiAtRuntime ()) {\r
8d85dc31 272 EfiAcquireLock (&Global->RtcLock);\r
8cd4d17c 273 }\r
274 //\r
275 // Wait for up to 0.1 seconds for the RTC to be updated\r
276 //\r
f8ea3026 277 Status = RtcWaitToUpdate (PcdGet32 (PcdRealTimeClockUpdateTimeout));\r
8cd4d17c 278 if (EFI_ERROR (Status)) {\r
8cd4d17c 279 if (!EfiAtRuntime ()) {\r
8d85dc31 280 EfiReleaseLock (&Global->RtcLock);\r
8cd4d17c 281 }\r
282 return Status;\r
283 }\r
284 //\r
285 // Read Register B\r
286 //\r
287 RegisterB.Data = RtcRead (RTC_ADDRESS_REGISTER_B);\r
288\r
289 //\r
290 // Get the Time/Date/Daylight Savings values.\r
291 //\r
292 Time->Second = RtcRead (RTC_ADDRESS_SECONDS);\r
293 Time->Minute = RtcRead (RTC_ADDRESS_MINUTES);\r
294 Time->Hour = RtcRead (RTC_ADDRESS_HOURS);\r
295 Time->Day = RtcRead (RTC_ADDRESS_DAY_OF_THE_MONTH);\r
296 Time->Month = RtcRead (RTC_ADDRESS_MONTH);\r
297 Time->Year = RtcRead (RTC_ADDRESS_YEAR);\r
298\r
9a1eee23 299 Century = RtcRead (RTC_ADDRESS_CENTURY);\r
9a1eee23 300 \r
8cd4d17c 301 //\r
302 // Release RTC Lock.\r
303 //\r
8cd4d17c 304 if (!EfiAtRuntime ()) {\r
8d85dc31 305 EfiReleaseLock (&Global->RtcLock);\r
8cd4d17c 306 }\r
6bfa178c 307\r
8cd4d17c 308 //\r
2d4117c0 309 // Get the variable that contains the TimeZone and Daylight fields\r
8cd4d17c 310 //\r
311 Time->TimeZone = Global->SavedTimeZone;\r
312 Time->Daylight = Global->Daylight;\r
313\r
314 //\r
315 // Make sure all field values are in correct range\r
316 //\r
254ba247 317 Status = ConvertRtcTimeToEfiTime (Time, Century, RegisterB);\r
318 if (!EFI_ERROR (Status)) {\r
319 Status = RtcTimeFieldsValid (Time);\r
320 }\r
8cd4d17c 321 if (EFI_ERROR (Status)) {\r
322 return EFI_DEVICE_ERROR;\r
323 }\r
6bfa178c 324\r
8cd4d17c 325 //\r
326 // Fill in Capabilities if it was passed in\r
327 //\r
8d85dc31 328 if (Capabilities != NULL) {\r
8cd4d17c 329 Capabilities->Resolution = 1;\r
330 //\r
331 // 1 hertz\r
332 //\r
333 Capabilities->Accuracy = 50000000;\r
334 //\r
335 // 50 ppm\r
336 //\r
337 Capabilities->SetsToZero = FALSE;\r
338 }\r
339\r
340 return EFI_SUCCESS;\r
341}\r
342\r
8d85dc31 343/**\r
344 Sets the current local time and date information.\r
345\r
346 @param Time A pointer to the current time.\r
347 @param Global For global use inside this module.\r
348\r
349 @retval EFI_SUCCESS The operation completed successfully.\r
350 @retval EFI_INVALID_PARAMETER A time field is out of range.\r
351 @retval EFI_DEVICE_ERROR The time could not be set due due to hardware error.\r
352\r
353**/\r
8cd4d17c 354EFI_STATUS\r
355PcRtcSetTime (\r
356 IN EFI_TIME *Time,\r
357 IN PC_RTC_MODULE_GLOBALS *Global\r
358 )\r
8cd4d17c 359{\r
360 EFI_STATUS Status;\r
361 EFI_TIME RtcTime;\r
362 RTC_REGISTER_B RegisterB;\r
363 UINT8 Century;\r
ec35e997 364 UINT32 TimerVar;\r
8cd4d17c 365\r
366 if (Time == NULL) {\r
367 return EFI_INVALID_PARAMETER;\r
368 }\r
369 //\r
370 // Make sure that the time fields are valid\r
371 //\r
372 Status = RtcTimeFieldsValid (Time);\r
373 if (EFI_ERROR (Status)) {\r
374 return Status;\r
375 }\r
376\r
377 CopyMem (&RtcTime, Time, sizeof (EFI_TIME));\r
378\r
379 //\r
380 // Acquire RTC Lock to make access to RTC atomic\r
381 //\r
8cd4d17c 382 if (!EfiAtRuntime ()) {\r
8d85dc31 383 EfiAcquireLock (&Global->RtcLock);\r
8cd4d17c 384 }\r
385 //\r
386 // Wait for up to 0.1 seconds for the RTC to be updated\r
387 //\r
f8ea3026 388 Status = RtcWaitToUpdate (PcdGet32 (PcdRealTimeClockUpdateTimeout));\r
8cd4d17c 389 if (EFI_ERROR (Status)) {\r
8cd4d17c 390 if (!EfiAtRuntime ()) {\r
ca08e40b 391 EfiReleaseLock (&Global->RtcLock);\r
8cd4d17c 392 }\r
393 return Status;\r
394 }\r
395 //\r
396 // Read Register B, and inhibit updates of the RTC\r
397 //\r
398 RegisterB.Data = RtcRead (RTC_ADDRESS_REGISTER_B);\r
24115e44 399 RegisterB.Bits.Set = 1;\r
8cd4d17c 400 RtcWrite (RTC_ADDRESS_REGISTER_B, RegisterB.Data);\r
401\r
402 ConvertEfiTimeToRtcTime (&RtcTime, RegisterB, &Century);\r
403\r
404 RtcWrite (RTC_ADDRESS_SECONDS, RtcTime.Second);\r
405 RtcWrite (RTC_ADDRESS_MINUTES, RtcTime.Minute);\r
406 RtcWrite (RTC_ADDRESS_HOURS, RtcTime.Hour);\r
407 RtcWrite (RTC_ADDRESS_DAY_OF_THE_MONTH, RtcTime.Day);\r
408 RtcWrite (RTC_ADDRESS_MONTH, RtcTime.Month);\r
409 RtcWrite (RTC_ADDRESS_YEAR, (UINT8) RtcTime.Year);\r
8cd4d17c 410 RtcWrite (RTC_ADDRESS_CENTURY, Century);\r
411\r
412 //\r
413 // Allow updates of the RTC registers\r
414 //\r
24115e44 415 RegisterB.Bits.Set = 0;\r
8cd4d17c 416 RtcWrite (RTC_ADDRESS_REGISTER_B, RegisterB.Data);\r
417\r
418 //\r
419 // Release RTC Lock.\r
420 //\r
8cd4d17c 421 if (!EfiAtRuntime ()) {\r
8d85dc31 422 EfiReleaseLock (&Global->RtcLock);\r
8cd4d17c 423 }\r
424 //\r
2d4117c0 425 // Set the variable that contains the TimeZone and Daylight fields\r
8cd4d17c 426 //\r
427 Global->SavedTimeZone = Time->TimeZone;\r
428 Global->Daylight = Time->Daylight;\r
ec35e997
LG
429\r
430 TimerVar = Time->Daylight;\r
e02c57b6 431 TimerVar = (UINT32) ((TimerVar << 16) | (UINT16)(Time->TimeZone));\r
ec35e997 432 Status = EfiSetVariable (\r
e0312446 433 L"RTC",\r
434 &gEfiCallerIdGuid,\r
ec35e997
LG
435 EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,\r
436 sizeof (TimerVar),\r
437 &TimerVar\r
438 );\r
439 ASSERT_EFI_ERROR (Status);\r
440\r
441 return EFI_SUCCESS;\r
8cd4d17c 442}\r
443\r
8d85dc31 444/**\r
445 Returns the current wakeup alarm clock setting.\r
446\r
447 @param Enabled Indicates if the alarm is currently enabled or disabled.\r
2d4117c0 448 @param Pending Indicates if the alarm signal is pending and requires acknowledgment.\r
8d85dc31 449 @param Time The current alarm setting.\r
450 @param Global For global use inside this module.\r
451\r
452 @retval EFI_SUCCESS The alarm settings were returned.\r
453 @retval EFI_INVALID_PARAMETER Enabled is NULL.\r
454 @retval EFI_INVALID_PARAMETER Pending is NULL.\r
455 @retval EFI_INVALID_PARAMETER Time is NULL.\r
456 @retval EFI_DEVICE_ERROR The wakeup time could not be retrieved due to a hardware error.\r
457 @retval EFI_UNSUPPORTED A wakeup timer is not supported on this platform.\r
458\r
459**/\r
8cd4d17c 460EFI_STATUS\r
461PcRtcGetWakeupTime (\r
462 OUT BOOLEAN *Enabled,\r
463 OUT BOOLEAN *Pending,\r
464 OUT EFI_TIME *Time,\r
8d85dc31 465 IN PC_RTC_MODULE_GLOBALS *Global\r
8cd4d17c 466 )\r
8cd4d17c 467{\r
468 EFI_STATUS Status;\r
469 RTC_REGISTER_B RegisterB;\r
470 RTC_REGISTER_C RegisterC;\r
471 UINT8 Century;\r
7018623c 472 EFI_TIME RtcTime;\r
473 UINTN DataSize;\r
8cd4d17c 474\r
475 //\r
2d4117c0 476 // Check parameters for null pointers\r
8cd4d17c 477 //\r
478 if ((Enabled == NULL) || (Pending == NULL) || (Time == NULL)) {\r
479 return EFI_INVALID_PARAMETER;\r
480\r
481 }\r
482 //\r
483 // Acquire RTC Lock to make access to RTC atomic\r
484 //\r
8cd4d17c 485 if (!EfiAtRuntime ()) {\r
8d85dc31 486 EfiAcquireLock (&Global->RtcLock);\r
8cd4d17c 487 }\r
488 //\r
489 // Wait for up to 0.1 seconds for the RTC to be updated\r
490 //\r
f8ea3026 491 Status = RtcWaitToUpdate (PcdGet32 (PcdRealTimeClockUpdateTimeout));\r
8cd4d17c 492 if (EFI_ERROR (Status)) {\r
8cd4d17c 493 if (!EfiAtRuntime ()) {\r
494 EfiReleaseLock (&Global->RtcLock);\r
495 }\r
496 return EFI_DEVICE_ERROR;\r
497 }\r
498 //\r
499 // Read Register B and Register C\r
500 //\r
501 RegisterB.Data = RtcRead (RTC_ADDRESS_REGISTER_B);\r
502 RegisterC.Data = RtcRead (RTC_ADDRESS_REGISTER_C);\r
503\r
504 //\r
505 // Get the Time/Date/Daylight Savings values.\r
506 //\r
24115e44 507 *Enabled = RegisterB.Bits.Aie;\r
7018623c 508 *Pending = RegisterC.Bits.Af;\r
509\r
510 Time->Second = RtcRead (RTC_ADDRESS_SECONDS_ALARM);\r
511 Time->Minute = RtcRead (RTC_ADDRESS_MINUTES_ALARM);\r
512 Time->Hour = RtcRead (RTC_ADDRESS_HOURS_ALARM);\r
513 Time->Day = RtcRead (RTC_ADDRESS_DAY_OF_THE_MONTH);\r
514 Time->Month = RtcRead (RTC_ADDRESS_MONTH);\r
515 Time->Year = RtcRead (RTC_ADDRESS_YEAR);\r
516 Time->TimeZone = Global->SavedTimeZone;\r
517 Time->Daylight = Global->Daylight;\r
8cd4d17c 518\r
9a1eee23 519 Century = RtcRead (RTC_ADDRESS_CENTURY);\r
7018623c 520\r
8cd4d17c 521 //\r
7018623c 522 // Get the alarm info from variable\r
8cd4d17c 523 //\r
ffa1e783 524 DataSize = sizeof (EFI_TIME);\r
7018623c 525 Status = EfiGetVariable (\r
526 L"RTCALARM",\r
527 &gEfiCallerIdGuid,\r
528 NULL,\r
529 &DataSize,\r
530 &RtcTime\r
531 );\r
532 if (!EFI_ERROR (Status)) {\r
533 //\r
534 // The alarm variable exists. In this case, we read variable to get info.\r
535 //\r
536 Time->Day = RtcTime.Day;\r
537 Time->Month = RtcTime.Month;\r
538 Time->Year = RtcTime.Year;\r
8cd4d17c 539 }\r
6bfa178c 540\r
541 //\r
7018623c 542 // Release RTC Lock.\r
6bfa178c 543 //\r
7018623c 544 if (!EfiAtRuntime ()) {\r
545 EfiReleaseLock (&Global->RtcLock);\r
546 }\r
6bfa178c 547\r
8cd4d17c 548 //\r
549 // Make sure all field values are in correct range\r
550 //\r
254ba247 551 Status = ConvertRtcTimeToEfiTime (Time, Century, RegisterB);\r
552 if (!EFI_ERROR (Status)) {\r
553 Status = RtcTimeFieldsValid (Time);\r
554 }\r
8cd4d17c 555 if (EFI_ERROR (Status)) {\r
556 return EFI_DEVICE_ERROR;\r
557 }\r
558\r
8cd4d17c 559 return EFI_SUCCESS;\r
560}\r
561\r
8d85dc31 562/**\r
563 Sets the system wakeup alarm clock time.\r
564\r
565 @param Enabled Enable or disable the wakeup alarm.\r
566 @param Time If Enable is TRUE, the time to set the wakeup alarm for.\r
567 If Enable is FALSE, then this parameter is optional, and may be NULL.\r
568 @param Global For global use inside this module.\r
569\r
570 @retval EFI_SUCCESS If Enable is TRUE, then the wakeup alarm was enabled.\r
571 If Enable is FALSE, then the wakeup alarm was disabled.\r
572 @retval EFI_INVALID_PARAMETER A time field is out of range.\r
573 @retval EFI_DEVICE_ERROR The wakeup time could not be set due to a hardware error.\r
574 @retval EFI_UNSUPPORTED A wakeup timer is not supported on this platform.\r
575\r
576**/\r
8cd4d17c 577EFI_STATUS\r
578PcRtcSetWakeupTime (\r
579 IN BOOLEAN Enable,\r
8d85dc31 580 IN EFI_TIME *Time, OPTIONAL\r
8cd4d17c 581 IN PC_RTC_MODULE_GLOBALS *Global\r
582 )\r
8cd4d17c 583{\r
584 EFI_STATUS Status;\r
585 EFI_TIME RtcTime;\r
586 RTC_REGISTER_B RegisterB;\r
587 UINT8 Century;\r
588 EFI_TIME_CAPABILITIES Capabilities;\r
589\r
aca70749 590 ZeroMem (&RtcTime, sizeof (RtcTime));\r
7018623c 591\r
8cd4d17c 592 if (Enable) {\r
593\r
594 if (Time == NULL) {\r
595 return EFI_INVALID_PARAMETER;\r
596 }\r
597 //\r
598 // Make sure that the time fields are valid\r
599 //\r
600 Status = RtcTimeFieldsValid (Time);\r
601 if (EFI_ERROR (Status)) {\r
602 return EFI_INVALID_PARAMETER;\r
603 }\r
604 //\r
605 // Just support set alarm time within 24 hours\r
606 //\r
607 PcRtcGetTime (&RtcTime, &Capabilities, Global);\r
79864f54 608 Status = RtcTimeFieldsValid (&RtcTime);\r
609 if (EFI_ERROR (Status)) {\r
610 return EFI_DEVICE_ERROR;\r
611 }\r
8cd4d17c 612 if (!IsWithinOneDay (&RtcTime, Time)) {\r
613 return EFI_UNSUPPORTED;\r
614 }\r
615 //\r
616 // Make a local copy of the time and date\r
617 //\r
618 CopyMem (&RtcTime, Time, sizeof (EFI_TIME));\r
619\r
620 }\r
621 //\r
622 // Acquire RTC Lock to make access to RTC atomic\r
623 //\r
8cd4d17c 624 if (!EfiAtRuntime ()) {\r
8d85dc31 625 EfiAcquireLock (&Global->RtcLock);\r
8cd4d17c 626 }\r
627 //\r
628 // Wait for up to 0.1 seconds for the RTC to be updated\r
629 //\r
f8ea3026 630 Status = RtcWaitToUpdate (PcdGet32 (PcdRealTimeClockUpdateTimeout));\r
8cd4d17c 631 if (EFI_ERROR (Status)) {\r
8cd4d17c 632 if (!EfiAtRuntime ()) {\r
633 EfiReleaseLock (&Global->RtcLock);\r
634 }\r
635 return EFI_DEVICE_ERROR;\r
636 }\r
637 //\r
638 // Read Register B, and inhibit updates of the RTC\r
639 //\r
640 RegisterB.Data = RtcRead (RTC_ADDRESS_REGISTER_B);\r
641\r
24115e44 642 RegisterB.Bits.Set = 1;\r
8cd4d17c 643 RtcWrite (RTC_ADDRESS_REGISTER_B, RegisterB.Data);\r
644\r
645 if (Enable) {\r
646 ConvertEfiTimeToRtcTime (&RtcTime, RegisterB, &Century);\r
647\r
648 //\r
649 // Set RTC alarm time\r
650 //\r
651 RtcWrite (RTC_ADDRESS_SECONDS_ALARM, RtcTime.Second);\r
652 RtcWrite (RTC_ADDRESS_MINUTES_ALARM, RtcTime.Minute);\r
653 RtcWrite (RTC_ADDRESS_HOURS_ALARM, RtcTime.Hour);\r
654\r
24115e44 655 RegisterB.Bits.Aie = 1;\r
8cd4d17c 656\r
657 } else {\r
24115e44 658 RegisterB.Bits.Aie = 0;\r
7018623c 659 //\r
660 // if the alarm is disable, record the current setting.\r
661 //\r
662 RtcTime.Second = RtcRead (RTC_ADDRESS_SECONDS_ALARM);\r
663 RtcTime.Minute = RtcRead (RTC_ADDRESS_MINUTES_ALARM);\r
664 RtcTime.Hour = RtcRead (RTC_ADDRESS_HOURS_ALARM);\r
665 RtcTime.Day = RtcRead (RTC_ADDRESS_DAY_OF_THE_MONTH);\r
666 RtcTime.Month = RtcRead (RTC_ADDRESS_MONTH);\r
667 RtcTime.Year = RtcRead (RTC_ADDRESS_YEAR);\r
668 RtcTime.TimeZone = Global->SavedTimeZone;\r
669 RtcTime.Daylight = Global->Daylight;\r
8cd4d17c 670 }\r
671 //\r
672 // Allow updates of the RTC registers\r
673 //\r
24115e44 674 RegisterB.Bits.Set = 0;\r
8cd4d17c 675 RtcWrite (RTC_ADDRESS_REGISTER_B, RegisterB.Data);\r
676\r
7018623c 677 //\r
678 // Set the Y/M/D info to variable as it has no corresponding hw registers.\r
679 //\r
680 Status = EfiSetVariable (\r
681 L"RTCALARM",\r
682 &gEfiCallerIdGuid,\r
683 EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,\r
684 sizeof (RtcTime),\r
685 &RtcTime\r
686 );\r
687 if (EFI_ERROR (Status)) {\r
688 return EFI_DEVICE_ERROR;\r
689 }\r
690\r
8cd4d17c 691 //\r
692 // Release RTC Lock.\r
693 //\r
8cd4d17c 694 if (!EfiAtRuntime ()) {\r
8d85dc31 695 EfiReleaseLock (&Global->RtcLock);\r
8cd4d17c 696 }\r
697 return EFI_SUCCESS;\r
698}\r
699\r
8cd4d17c 700\r
254ba247 701/**\r
702 Checks an 8-bit BCD value, and converts to an 8-bit value if valid.\r
703\r
704 This function checks the 8-bit BCD value specified by Value.\r
705 If valid, the function converts it to an 8-bit value and returns it.\r
706 Otherwise, return 0xff.\r
707\r
8d85dc31 708 @param Value The 8-bit BCD value to check and convert\r
254ba247 709\r
8d85dc31 710 @return The 8-bit value converted. Or 0xff if Value is invalid.\r
254ba247 711\r
712**/\r
713UINT8\r
714CheckAndConvertBcd8ToDecimal8 (\r
715 IN UINT8 Value\r
8cd4d17c 716 )\r
254ba247 717{\r
718 if ((Value < 0xa0) && ((Value & 0xf) < 0xa)) {\r
719 return BcdToDecimal8 (Value);\r
720 }\r
8cd4d17c 721\r
254ba247 722 return 0xff;\r
723}\r
8cd4d17c 724\r
254ba247 725/**\r
726 Converts time read from RTC to EFI_TIME format defined by UEFI spec.\r
8cd4d17c 727\r
254ba247 728 This function converts raw time data read from RTC to the EFI_TIME format\r
729 defined by UEFI spec.\r
730 If data mode of RTC is BCD, then converts it to decimal,\r
731 If RTC is in 12-hour format, then converts it to 24-hour format.\r
8cd4d17c 732\r
254ba247 733 @param Time On input, the time data read from RTC to convert\r
734 On output, the time converted to UEFI format\r
735 @param Century Value of century read from RTC.\r
736 @param RegisterB Value of Register B of RTC, indicating data mode\r
737 and hour format.\r
8cd4d17c 738\r
8d85dc31 739 @retval EFI_INVALID_PARAMETER Parameters passed in are invalid.\r
740 @retval EFI_SUCCESS Convert RTC time to EFI time successfully.\r
741\r
254ba247 742**/\r
743EFI_STATUS\r
744ConvertRtcTimeToEfiTime (\r
745 IN OUT EFI_TIME *Time,\r
746 IN UINT8 Century,\r
747 IN RTC_REGISTER_B RegisterB\r
748 )\r
8cd4d17c 749{\r
8d85dc31 750 BOOLEAN IsPM;\r
8cd4d17c 751\r
21176834 752 if ((Time->Hour & 0x80) != 0) {\r
8d85dc31 753 IsPM = TRUE;\r
8cd4d17c 754 } else {\r
8d85dc31 755 IsPM = FALSE;\r
8cd4d17c 756 }\r
757\r
758 Time->Hour = (UINT8) (Time->Hour & 0x7f);\r
759\r
24115e44 760 if (RegisterB.Bits.Dm == 0) {\r
254ba247 761 Time->Year = CheckAndConvertBcd8ToDecimal8 ((UINT8) Time->Year);\r
762 Time->Month = CheckAndConvertBcd8ToDecimal8 (Time->Month);\r
763 Time->Day = CheckAndConvertBcd8ToDecimal8 (Time->Day);\r
764 Time->Hour = CheckAndConvertBcd8ToDecimal8 (Time->Hour);\r
765 Time->Minute = CheckAndConvertBcd8ToDecimal8 (Time->Minute);\r
766 Time->Second = CheckAndConvertBcd8ToDecimal8 (Time->Second);\r
254ba247 767 }\r
42c65073 768 Century = CheckAndConvertBcd8ToDecimal8 (Century);\r
254ba247 769\r
770 if (Time->Year == 0xff || Time->Month == 0xff || Time->Day == 0xff ||\r
771 Time->Hour == 0xff || Time->Minute == 0xff || Time->Second == 0xff ||\r
772 Century == 0xff) {\r
773 return EFI_INVALID_PARAMETER;\r
8cd4d17c 774 }\r
254ba247 775\r
776 Time->Year = (UINT16) (Century * 100 + Time->Year);\r
777\r
8cd4d17c 778 //\r
779 // If time is in 12 hour format, convert it to 24 hour format\r
780 //\r
24115e44 781 if (RegisterB.Bits.Mil == 0) {\r
8d85dc31 782 if (IsPM && Time->Hour < 12) {\r
8cd4d17c 783 Time->Hour = (UINT8) (Time->Hour + 12);\r
784 }\r
785\r
8d85dc31 786 if (!IsPM && Time->Hour == 12) {\r
8cd4d17c 787 Time->Hour = 0;\r
788 }\r
789 }\r
790\r
791 Time->Nanosecond = 0;\r
254ba247 792\r
793 return EFI_SUCCESS;\r
8cd4d17c 794}\r
795\r
8d85dc31 796/**\r
797 Wait for a period for the RTC to be ready.\r
798\r
799 @param Timeout Tell how long it should take to wait.\r
800\r
801 @retval EFI_DEVICE_ERROR RTC device error.\r
802 @retval EFI_SUCCESS RTC is updated and ready. \r
803**/\r
8cd4d17c 804EFI_STATUS\r
805RtcWaitToUpdate (\r
806 UINTN Timeout\r
807 )\r
8cd4d17c 808{\r
809 RTC_REGISTER_A RegisterA;\r
810 RTC_REGISTER_D RegisterD;\r
811\r
812 //\r
813 // See if the RTC is functioning correctly\r
814 //\r
815 RegisterD.Data = RtcRead (RTC_ADDRESS_REGISTER_D);\r
816\r
24115e44 817 if (RegisterD.Bits.Vrt == 0) {\r
8cd4d17c 818 return EFI_DEVICE_ERROR;\r
819 }\r
820 //\r
821 // Wait for up to 0.1 seconds for the RTC to be ready.\r
822 //\r
823 Timeout = (Timeout / 10) + 1;\r
824 RegisterA.Data = RtcRead (RTC_ADDRESS_REGISTER_A);\r
24115e44 825 while (RegisterA.Bits.Uip == 1 && Timeout > 0) {\r
8cd4d17c 826 MicroSecondDelay (10);\r
827 RegisterA.Data = RtcRead (RTC_ADDRESS_REGISTER_A);\r
828 Timeout--;\r
829 }\r
830\r
831 RegisterD.Data = RtcRead (RTC_ADDRESS_REGISTER_D);\r
24115e44 832 if (Timeout == 0 || RegisterD.Bits.Vrt == 0) {\r
8cd4d17c 833 return EFI_DEVICE_ERROR;\r
834 }\r
835\r
836 return EFI_SUCCESS;\r
837}\r
838\r
8d85dc31 839/**\r
840 See if all fields of a variable of EFI_TIME type is correct.\r
841\r
842 @param Time The time to be checked.\r
843\r
844 @retval EFI_INVALID_PARAMETER Some fields of Time are not correct.\r
845 @retval EFI_SUCCESS Time is a valid EFI_TIME variable.\r
846\r
847**/\r
8cd4d17c 848EFI_STATUS\r
849RtcTimeFieldsValid (\r
850 IN EFI_TIME *Time\r
851 )\r
8cd4d17c 852{\r
853 if (Time->Year < 1998 ||\r
854 Time->Year > 2099 ||\r
855 Time->Month < 1 ||\r
856 Time->Month > 12 ||\r
6bfa178c 857 (!DayValid (Time)) ||\r
8cd4d17c 858 Time->Hour > 23 ||\r
859 Time->Minute > 59 ||\r
860 Time->Second > 59 ||\r
861 Time->Nanosecond > 999999999 ||\r
862 (!(Time->TimeZone == EFI_UNSPECIFIED_TIMEZONE || (Time->TimeZone >= -1440 && Time->TimeZone <= 1440))) ||\r
6bfa178c 863 ((Time->Daylight & (~(EFI_TIME_ADJUST_DAYLIGHT | EFI_TIME_IN_DAYLIGHT))) != 0)) {\r
864 return EFI_INVALID_PARAMETER;\r
8cd4d17c 865 }\r
866\r
867 return EFI_SUCCESS;\r
868}\r
869\r
8d85dc31 870/**\r
871 See if field Day of an EFI_TIME is correct.\r
872\r
873 @param Time Its Day field is to be checked.\r
874\r
875 @retval TRUE Day field of Time is correct.\r
876 @retval FALSE Day field of Time is NOT correct.\r
877**/\r
8cd4d17c 878BOOLEAN\r
879DayValid (\r
880 IN EFI_TIME *Time\r
881 )\r
8cd4d17c 882{\r
558bfebb 883 INTN DayOfMonth[12];\r
884\r
885 DayOfMonth[0] = 31;\r
886 DayOfMonth[1] = 29;\r
887 DayOfMonth[2] = 31;\r
888 DayOfMonth[3] = 30;\r
889 DayOfMonth[4] = 31;\r
890 DayOfMonth[5] = 30;\r
891 DayOfMonth[6] = 31;\r
892 DayOfMonth[7] = 31;\r
893 DayOfMonth[8] = 30;\r
894 DayOfMonth[9] = 31;\r
895 DayOfMonth[10] = 30;\r
896 DayOfMonth[11] = 31;\r
8cd4d17c 897\r
96a5ac5b 898 //\r
899 // The validity of Time->Month field should be checked before\r
900 //\r
901 ASSERT (Time->Month >=1);\r
902 ASSERT (Time->Month <=12);\r
8cd4d17c 903 if (Time->Day < 1 ||\r
904 Time->Day > DayOfMonth[Time->Month - 1] ||\r
905 (Time->Month == 2 && (!IsLeapYear (Time) && Time->Day > 28))\r
906 ) {\r
907 return FALSE;\r
908 }\r
909\r
910 return TRUE;\r
911}\r
912\r
8d85dc31 913/**\r
2d4117c0 914 Check if it is a leap year.\r
8d85dc31 915\r
916 @param Time The time to be checked.\r
917\r
2d4117c0 918 @retval TRUE It is a leap year.\r
919 @retval FALSE It is NOT a leap year.\r
8d85dc31 920**/\r
8cd4d17c 921BOOLEAN\r
922IsLeapYear (\r
923 IN EFI_TIME *Time\r
924 )\r
8cd4d17c 925{\r
926 if (Time->Year % 4 == 0) {\r
927 if (Time->Year % 100 == 0) {\r
928 if (Time->Year % 400 == 0) {\r
929 return TRUE;\r
930 } else {\r
931 return FALSE;\r
932 }\r
933 } else {\r
934 return TRUE;\r
935 }\r
936 } else {\r
937 return FALSE;\r
938 }\r
939}\r
940\r
8d85dc31 941/**\r
942 Converts time from EFI_TIME format defined by UEFI spec to RTC's.\r
8cd4d17c 943\r
8d85dc31 944 This function converts time from EFI_TIME format defined by UEFI spec to RTC's.\r
945 If data mode of RTC is BCD, then converts EFI_TIME to it.\r
946 If RTC is in 12-hour format, then converts EFI_TIME to it.\r
8cd4d17c 947\r
8d85dc31 948 @param Time On input, the time data read from UEFI to convert\r
949 On output, the time converted to RTC format\r
950 @param RegisterB Value of Register B of RTC, indicating data mode\r
951 @param Century It is set according to EFI_TIME Time.\r
8cd4d17c 952\r
8d85dc31 953**/\r
954VOID\r
955ConvertEfiTimeToRtcTime (\r
956 IN OUT EFI_TIME *Time,\r
957 IN RTC_REGISTER_B RegisterB,\r
958 OUT UINT8 *Century\r
959 )\r
8cd4d17c 960{\r
8d85dc31 961 BOOLEAN IsPM;\r
8cd4d17c 962\r
8d85dc31 963 IsPM = TRUE;\r
8cd4d17c 964 //\r
8d85dc31 965 // Adjust hour field if RTC is in 12 hour mode\r
8cd4d17c 966 //\r
24115e44 967 if (RegisterB.Bits.Mil == 0) {\r
8cd4d17c 968 if (Time->Hour < 12) {\r
8d85dc31 969 IsPM = FALSE;\r
8cd4d17c 970 }\r
971\r
972 if (Time->Hour >= 13) {\r
973 Time->Hour = (UINT8) (Time->Hour - 12);\r
974 } else if (Time->Hour == 0) {\r
975 Time->Hour = 12;\r
976 }\r
977 }\r
978 //\r
979 // Set the Time/Date/Daylight Savings values.\r
980 //\r
981 *Century = DecimalToBcd8 ((UINT8) (Time->Year / 100));\r
982\r
983 Time->Year = (UINT16) (Time->Year % 100);\r
984\r
24115e44 985 if (RegisterB.Bits.Dm == 0) {\r
8cd4d17c 986 Time->Year = DecimalToBcd8 ((UINT8) Time->Year);\r
987 Time->Month = DecimalToBcd8 (Time->Month);\r
988 Time->Day = DecimalToBcd8 (Time->Day);\r
989 Time->Hour = DecimalToBcd8 (Time->Hour);\r
990 Time->Minute = DecimalToBcd8 (Time->Minute);\r
991 Time->Second = DecimalToBcd8 (Time->Second);\r
992 }\r
993 //\r
994 // If we are in 12 hour mode and PM is set, then set bit 7 of the Hour field.\r
995 //\r
24115e44 996 if (RegisterB.Bits.Mil == 0 && IsPM) {\r
8cd4d17c 997 Time->Hour = (UINT8) (Time->Hour | 0x80);\r
998 }\r
999}\r
1000\r
8d85dc31 1001/**\r
1002 Compare the Hour, Minute and Second of the From time and the To time.\r
1003 \r
1004 Only compare H/M/S in EFI_TIME and ignore other fields here.\r
1005\r
1006 @param From the first time\r
1007 @param To the second time\r
1008\r
1009 @return >0 The H/M/S of the From time is later than those of To time\r
1010 @return ==0 The H/M/S of the From time is same as those of To time\r
1011 @return <0 The H/M/S of the From time is earlier than those of To time\r
1012**/\r
8cd4d17c 1013INTN\r
1014CompareHMS (\r
1015 IN EFI_TIME *From,\r
1016 IN EFI_TIME *To\r
1017 )\r
8cd4d17c 1018{\r
1019 if ((From->Hour > To->Hour) ||\r
1020 ((From->Hour == To->Hour) && (From->Minute > To->Minute)) ||\r
1021 ((From->Hour == To->Hour) && (From->Minute == To->Minute) && (From->Second > To->Second))) {\r
1022 return 1;\r
1023 } else if ((From->Hour == To->Hour) && (From->Minute == To->Minute) && (From->Second == To->Second)) {\r
1024 return 0;\r
1025 } else {\r
1026 return -1;\r
1027 }\r
1028}\r
1029\r
8d85dc31 1030/**\r
1031 To check if second date is later than first date within 24 hours.\r
1032\r
1033 @param From the first date\r
1034 @param To the second date\r
1035\r
1036 @retval TRUE From is previous to To within 24 hours.\r
1037 @retval FALSE From is later, or it is previous to To more than 24 hours.\r
1038**/\r
8cd4d17c 1039BOOLEAN\r
1040IsWithinOneDay (\r
1041 IN EFI_TIME *From,\r
1042 IN EFI_TIME *To\r
1043 )\r
8cd4d17c 1044{\r
558bfebb 1045 UINT8 DayOfMonth[12];\r
1f4cf7b1 1046 BOOLEAN Adjacent;\r
1047\r
558bfebb 1048 DayOfMonth[0] = 31;\r
1049 DayOfMonth[1] = 29;\r
1050 DayOfMonth[2] = 31;\r
1051 DayOfMonth[3] = 30;\r
1052 DayOfMonth[4] = 31;\r
1053 DayOfMonth[5] = 30;\r
1054 DayOfMonth[6] = 31;\r
1055 DayOfMonth[7] = 31;\r
1056 DayOfMonth[8] = 30;\r
1057 DayOfMonth[9] = 31;\r
1058 DayOfMonth[10] = 30;\r
1059 DayOfMonth[11] = 31;\r
1060\r
1f4cf7b1 1061 Adjacent = FALSE;\r
8cd4d17c 1062\r
96a5ac5b 1063 //\r
4cfc3293 1064 // The validity of From->Month field should be checked before\r
96a5ac5b 1065 //\r
1066 ASSERT (From->Month >=1);\r
1067 ASSERT (From->Month <=12);\r
1068 \r
8cd4d17c 1069 if (From->Year == To->Year) {\r
1070 if (From->Month == To->Month) {\r
1071 if ((From->Day + 1) == To->Day) {\r
1072 if ((CompareHMS(From, To) >= 0)) {\r
1073 Adjacent = TRUE;\r
1074 }\r
1075 } else if (From->Day == To->Day) {\r
1076 if ((CompareHMS(From, To) <= 0)) {\r
1077 Adjacent = TRUE;\r
1078 }\r
1079 }\r
1080 } else if (((From->Month + 1) == To->Month) && (To->Day == 1)) {\r
1081 if ((From->Month == 2) && !IsLeapYear(From)) {\r
1082 if (From->Day == 28) {\r
1083 if ((CompareHMS(From, To) >= 0)) {\r
1084 Adjacent = TRUE;\r
1085 }\r
1086 }\r
1087 } else if (From->Day == DayOfMonth[From->Month - 1]) {\r
1088 if ((CompareHMS(From, To) >= 0)) {\r
1089 Adjacent = TRUE;\r
1090 }\r
1091 }\r
1092 }\r
1093 } else if (((From->Year + 1) == To->Year) &&\r
1094 (From->Month == 12) &&\r
1095 (From->Day == 31) &&\r
1096 (To->Month == 1) &&\r
1097 (To->Day == 1)) {\r
1098 if ((CompareHMS(From, To) >= 0)) {\r
1099 Adjacent = TRUE;\r
1100 }\r
1101 }\r
1102\r
1103 return Adjacent;\r
1104}\r
1105\r