Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

726 lines
17 KiB

  1. /* Copyright 1999 American Power Conversion, All Rights Reserved
  2. *
  3. * Description:
  4. * Implements the generic UPS
  5. *
  6. * Revision History:
  7. * mholly 19Apr1999 initial revision.
  8. * mholly 21Apr1999 hold shutoff pin for 5 sec instead of 3
  9. * mholly 12May1999 UPSInit no longer takes the comm port param
  10. *
  11. */
  12. #include <tchar.h>
  13. #include <windows.h>
  14. #include "gnrcups.h"
  15. #include "upsreg.h"
  16. //
  17. // provide meaningful names to the comm port pins
  18. //
  19. #define LINE_FAIL MS_CTS_ON
  20. #define LOW_BATT MS_RLSD_ON
  21. #define LINE_FAIL_MASK EV_CTS
  22. #define LOW_BATT_MASK EV_RLSD
  23. // Amount of time to wait in order for the port to settle down
  24. #define DEBOUNCE_DELAY_TIME 250
  25. //
  26. // private functions used in the implementation of the
  27. // public Generic UPS funtions
  28. //
  29. static DWORD openCommPort(LPCTSTR aCommPort);
  30. static DWORD setupCommPort(DWORD aSignalsMask);
  31. static DWORD getSignalsMask(void);
  32. static void updateUpsState(DWORD aModemStatus);
  33. static BOOL upsLineAsserted(DWORD ModemStatus, DWORD Line);
  34. static DWORD startUpsMonitoring(void);
  35. static DWORD WINAPI UpsMonitoringThread(LPVOID unused);
  36. //
  37. // UPSDRIVERCONTEXT
  38. //
  39. // provides a framework to encapsulate data that is
  40. // shared among the functions in this file
  41. //
  42. struct UPSDRIVERCONTEXT
  43. {
  44. HANDLE theCommPort;
  45. DWORD theState;
  46. HANDLE theStateChangedEvent;
  47. DWORD theSignalsMask;
  48. HANDLE theMonitoringThreadHandle;
  49. HANDLE theStopMonitoringEvent;
  50. };
  51. //
  52. // _theUps
  53. //
  54. // provides a single instance of a UPSDRIVERCONTEXT
  55. // structure that all functions in this file will use
  56. //
  57. static struct UPSDRIVERCONTEXT _theUps;
  58. /**
  59. * GenericUPSInit
  60. *
  61. * Description:
  62. * Retrieves the UPS signalling information from the
  63. * NT Registry and attempts to open the comm port and
  64. * configure it as defined by the signalling data.
  65. * Also creates a inter-thread signal, theStateChangedEvent,
  66. * and starts the monitoring of the UPS on a separate thread
  67. * via a call to startUpsMonitoring.
  68. * The GenericUPSInit function must be called before any
  69. * other function in this file
  70. *
  71. * Parameters:
  72. * None
  73. *
  74. * Returns:
  75. * UPS_INITOK: Initalization was successful
  76. * UPS_INITREGISTRYERROR: The 'Options' registry value is corrupt
  77. * UPS_INITCOMMOPENERROR: The comm port could not be opened
  78. * UPS_INITCOMMSETUPERROR: The comm port could not be configured
  79. * UPS_INITUNKNOWNERROR: Undefined error has occurred
  80. *
  81. */
  82. DWORD GenericUPSInit(void)
  83. {
  84. DWORD init_err = UPS_INITOK;
  85. TCHAR comm_port[MAX_PATH];
  86. _theUps.theStateChangedEvent = NULL;
  87. _theUps.theState = UPS_ONLINE;
  88. _theUps.theCommPort = NULL;
  89. _theUps.theMonitoringThreadHandle = NULL;
  90. _theUps.theStopMonitoringEvent = NULL;
  91. if (ERROR_SUCCESS != getSignalsMask()) {
  92. init_err = UPS_INITREGISTRYERROR;
  93. goto init_end;
  94. }
  95. //
  96. // Initialize registry functions
  97. //
  98. InitUPSConfigBlock();
  99. //
  100. // get the comm port to use
  101. //
  102. if (ERROR_SUCCESS != GetUPSConfigPort(comm_port)) {
  103. init_err = UPS_INITREGISTRYERROR;
  104. goto init_end;
  105. }
  106. if (ERROR_SUCCESS != openCommPort(comm_port)) {
  107. init_err = UPS_INITCOMMOPENERROR;
  108. goto init_end;
  109. }
  110. _theUps.theStateChangedEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
  111. if (!_theUps.theStateChangedEvent) {
  112. init_err = UPS_INITUNKNOWNERROR;
  113. goto init_end;
  114. }
  115. if (ERROR_SUCCESS != setupCommPort(_theUps.theSignalsMask)) {
  116. init_err = UPS_INITCOMMSETUPERROR;
  117. goto init_end;
  118. }
  119. if (ERROR_SUCCESS != startUpsMonitoring()) {
  120. init_err = UPS_INITUNKNOWNERROR;
  121. goto init_end;
  122. }
  123. init_end:
  124. if (UPS_INITOK != init_err) {
  125. GenericUPSStop();
  126. }
  127. return init_err;
  128. }
  129. /**
  130. * GenericUPSStop
  131. *
  132. * Description:
  133. * calls GenericUPSCancelWait to release pending threads
  134. * Stops the thread that is monitoring the UPS
  135. * Closes the comm port
  136. * Resets all data to default values
  137. * After a call to GenericUPSStop, only the GenericUPSInit
  138. * function is valid
  139. *
  140. * Parameters:
  141. * None
  142. *
  143. * Returns:
  144. * None
  145. *
  146. */
  147. void GenericUPSStop(void)
  148. {
  149. GenericUPSCancelWait();
  150. if (_theUps.theStopMonitoringEvent) {
  151. SetEvent(_theUps.theStopMonitoringEvent);
  152. }
  153. if (_theUps.theMonitoringThreadHandle) {
  154. WaitForSingleObject(_theUps.theMonitoringThreadHandle, INFINITE);
  155. CloseHandle(_theUps.theMonitoringThreadHandle);
  156. _theUps.theMonitoringThreadHandle = NULL;
  157. }
  158. if (_theUps.theStopMonitoringEvent) {
  159. CloseHandle(_theUps.theStopMonitoringEvent);
  160. _theUps.theStopMonitoringEvent = NULL;
  161. }
  162. if (_theUps.theCommPort) {
  163. CloseHandle(_theUps.theCommPort);
  164. _theUps.theCommPort = NULL;
  165. }
  166. if (_theUps.theStateChangedEvent) {
  167. CloseHandle(_theUps.theStateChangedEvent);
  168. _theUps.theStateChangedEvent = NULL;
  169. }
  170. _theUps.theState = UPS_ONLINE;
  171. _theUps.theSignalsMask = 0;
  172. }
  173. /**
  174. * GenericUPSWaitForStateChange
  175. *
  176. * Description:
  177. * Blocks until the state of the UPS differs
  178. * from the value passed in via aState or
  179. * anInterval milliseconds has expired. If
  180. * anInterval has a value of INFINITE this
  181. * function will never timeout
  182. *
  183. * Parameters:
  184. * aState: defines the state to wait for a change from,
  185. * possible values:
  186. * UPS_ONLINE
  187. * UPS_ONBATTERY
  188. * UPS_LOWBATTERY
  189. * UPS_NOCOMM
  190. *
  191. * anInterval: timeout in milliseconds, or INFINITE for
  192. * no timeout interval
  193. *
  194. * Returns:
  195. * None
  196. *
  197. */
  198. void GenericUPSWaitForStateChange(DWORD aLastState, DWORD anInterval)
  199. {
  200. if (aLastState == _theUps.theState) {
  201. //
  202. // wait for a state change from the UPS
  203. //
  204. if (_theUps.theStateChangedEvent) {
  205. WaitForSingleObject(_theUps.theStateChangedEvent, anInterval);
  206. }
  207. }
  208. }
  209. /**
  210. * GenericUPSGetState
  211. *
  212. * Description:
  213. * returns the current state of the UPS
  214. *
  215. * Parameters:
  216. * None
  217. *
  218. * Returns:
  219. * possible values:
  220. * UPS_ONLINE
  221. * UPS_ONBATTERY
  222. * UPS_LOWBATTERY
  223. * UPS_NOCOMM
  224. *
  225. */
  226. DWORD GenericUPSGetState(void)
  227. {
  228. return _theUps.theState;
  229. }
  230. /**
  231. * GenericUPSCancelWait
  232. *
  233. * Description:
  234. * interrupts pending calls to GenericUPSWaitForStateChange
  235. * without regard to timout or state change
  236. *
  237. * Parameters:
  238. * None
  239. *
  240. * Returns:
  241. * None
  242. *
  243. */
  244. void GenericUPSCancelWait(void)
  245. {
  246. if (_theUps.theStateChangedEvent) {
  247. SetEvent(_theUps.theStateChangedEvent);
  248. }
  249. }
  250. /**
  251. * GenericUPSTurnOff
  252. *
  253. * Description:
  254. * Attempts to turnoff the UPS after the specified delay.
  255. * Simple signaling UPS do not support this feature, so
  256. * this function does nothing.
  257. *
  258. * Parameters:
  259. * aTurnOffDelay: the minimum amount of time to wait before
  260. * turning off the outlets on the UPS
  261. *
  262. * Returns:
  263. * None
  264. *
  265. */
  266. void GenericUPSTurnOff(DWORD aTurnOffDelay)
  267. {
  268. // UPS turn off is not supported in simple mode, do nothing
  269. }
  270. /**
  271. * getSignalsMask
  272. *
  273. * Description:
  274. * Queries the registry for the 'Options' value
  275. * The 'Options' value defines a bitmask that is
  276. * used to configure the comm port settings
  277. *
  278. * Parameters:
  279. * None
  280. *
  281. * Returns:
  282. * ERROR_SUCCESS: signals mask retrieved OK
  283. * any other return code indicates failure to
  284. * retrieve the value from the registry
  285. *
  286. */
  287. DWORD getSignalsMask(void)
  288. {
  289. DWORD status = ERROR_SUCCESS;
  290. DWORD value = 0;
  291. status = GetUPSConfigOptions(&value);
  292. if (ERROR_SUCCESS == status) {
  293. _theUps.theSignalsMask = value;
  294. }
  295. else {
  296. _theUps.theSignalsMask = 0;
  297. }
  298. return status;
  299. }
  300. /**
  301. * openCommPort
  302. *
  303. * Description:
  304. * Attempts to open the comm port
  305. *
  306. * Parameters:
  307. * aCommPort: indicates which comm port
  308. * on the system to open
  309. *
  310. * Returns:
  311. * ERROR_SUCCESS: comm port opened OK
  312. * any other return code indicates failure to
  313. * open the comm port - the exact error code
  314. * is set by the CreateFile function
  315. *
  316. */
  317. DWORD openCommPort(LPCTSTR aCommPort)
  318. {
  319. DWORD err = ERROR_SUCCESS;
  320. _theUps.theCommPort = CreateFile(
  321. aCommPort,
  322. GENERIC_READ | GENERIC_WRITE,
  323. 0,
  324. NULL,
  325. OPEN_EXISTING,
  326. FILE_FLAG_OVERLAPPED,
  327. NULL
  328. );
  329. if (_theUps.theCommPort == INVALID_HANDLE_VALUE) {
  330. err = GetLastError();
  331. }
  332. return err;
  333. }
  334. /**
  335. * setupCommPort
  336. *
  337. * Description:
  338. * Attempts to setup the comm port - this method
  339. * initializes the shutoff pin to an unsignalled
  340. * state, and tells the system what other pins
  341. * it should monitor for changes
  342. *
  343. * Parameters:
  344. * aSignalsMask: defines a bitmask that will
  345. * be used to configure the
  346. * comm port
  347. *
  348. * Returns:
  349. * ERROR_SUCCESS: comm port setup OK
  350. * any other return code indicates failure to
  351. * setup the comm port - the exact error code
  352. * is set by the EscapeCommFunction function
  353. *
  354. */
  355. DWORD setupCommPort(DWORD aSignalsMask)
  356. {
  357. DWORD err = ERROR_SUCCESS;
  358. DWORD ModemStatus = 0;
  359. DWORD UpsActiveSignals = 0;
  360. DWORD UpsCommMask = 0;
  361. //
  362. // first set the 'shutoff' pin to the
  363. // unsignaled state, don't want to
  364. // shutoff the UPS now...
  365. //
  366. if (aSignalsMask & UPS_POSSIGSHUTOFF) {
  367. ModemStatus = CLRDTR;
  368. }
  369. else {
  370. ModemStatus = SETDTR;
  371. }
  372. if (!EscapeCommFunction(_theUps.theCommPort, ModemStatus)) {
  373. err = GetLastError();
  374. }
  375. if (!EscapeCommFunction(_theUps.theCommPort, SETRTS)) {
  376. err = GetLastError();
  377. }
  378. if (!EscapeCommFunction(_theUps.theCommPort, SETXOFF)) {
  379. err = GetLastError();
  380. }
  381. //
  382. // determine what pins should be monitored for activity
  383. //
  384. UpsActiveSignals =
  385. (aSignalsMask & ( UPS_POWERFAILSIGNAL | UPS_LOWBATTERYSIGNAL));
  386. switch (UpsActiveSignals) {
  387. case UPS_POWERFAILSIGNAL:
  388. UpsCommMask = LINE_FAIL_MASK;
  389. break;
  390. case UPS_LOWBATTERYSIGNAL:
  391. UpsCommMask = LOW_BATT_MASK;
  392. break;
  393. case (UPS_LOWBATTERYSIGNAL | UPS_POWERFAILSIGNAL):
  394. UpsCommMask = (LINE_FAIL_MASK | LOW_BATT_MASK);
  395. break;
  396. }
  397. //
  398. // tell the system what pins we are interested in
  399. // monitoring activity
  400. //
  401. if (!SetCommMask(_theUps.theCommPort, UpsCommMask)) {
  402. err = GetLastError();
  403. }
  404. //
  405. // simply wait for 3 seconds for the pins to 'settle',
  406. // failure to do so results in misleading status to
  407. // be returned from GetCommModemStatus
  408. //
  409. WaitForSingleObject(_theUps.theStateChangedEvent, 3000);
  410. GetCommModemStatus( _theUps.theCommPort, &ModemStatus);
  411. updateUpsState(ModemStatus);
  412. return err;
  413. }
  414. /**
  415. * startUpsMonitoring
  416. *
  417. * Description:
  418. * Creates a separate thread to perform the monitoring
  419. * of the comm port to which the UPS is connected
  420. * Also creates an event that other threads can signal
  421. * to indicate that the monitoring thread should exit
  422. *
  423. * Parameters:
  424. * None
  425. *
  426. * Returns:
  427. * ERROR_SUCCESS: thread creation OK
  428. * any other return code indicates failure to
  429. * start the thread, either the thread was not
  430. * created or the stop event was not created
  431. *
  432. */
  433. DWORD startUpsMonitoring()
  434. {
  435. DWORD err = ERROR_SUCCESS;
  436. DWORD thread_id = 0;
  437. _theUps.theStopMonitoringEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  438. if (!_theUps.theStopMonitoringEvent) {
  439. err = GetLastError();
  440. }
  441. else {
  442. _theUps.theMonitoringThreadHandle =
  443. CreateThread(NULL, 0, UpsMonitoringThread, NULL, 0, &thread_id);
  444. if (!_theUps.theMonitoringThreadHandle) {
  445. err = GetLastError();
  446. }
  447. }
  448. return err;
  449. }
  450. /**
  451. * updateUpsState
  452. *
  453. * Description:
  454. * Determines the state of the UPS based upon
  455. * the state of the line fail and low battery
  456. * pins of the UPS
  457. * If the state of the UPS changes, then this
  458. * method will signal theStateChangedEvent, this
  459. * in-turn will release any threads waiting in
  460. * the GenericUPSWaitForStateChange function
  461. *
  462. * Parameters:
  463. * aModemStatus: a bitmask that represents the
  464. * state of the comm port pins, this
  465. * value should be retrieved by a call
  466. * to GetCommModemStatus
  467. *
  468. * Returns:
  469. * None
  470. *
  471. */
  472. void updateUpsState(DWORD aModemStatus)
  473. {
  474. DWORD old_state = _theUps.theState;
  475. if (upsLineAsserted(aModemStatus, LINE_FAIL)) {
  476. if (upsLineAsserted(aModemStatus, LOW_BATT)) {
  477. _theUps.theState = UPS_LOWBATTERY;
  478. }
  479. else {
  480. _theUps.theState = UPS_ONBATTERY;
  481. }
  482. }
  483. else {
  484. _theUps.theState = UPS_ONLINE;
  485. }
  486. if (old_state != _theUps.theState) {
  487. SetEvent(_theUps.theStateChangedEvent);
  488. }
  489. }
  490. /**
  491. * upsLineAsserted
  492. *
  493. * Description:
  494. * Determines if the signal, either LINE_FAIL or LOW_BATT,
  495. * is asserted or not. The line is asserted based on the
  496. * voltage levels set in _theUps.theSignalsMask.
  497. * The aModemStatus bitmask signals a positive voltage
  498. * by a value of 1.
  499. * Below is a chart showing the logic used in determining
  500. * if a line is asserted or not
  501. *
  502. * --------------------------------------------------------------
  503. * | UPS positive signals | UPS negative signals
  504. * --------------------------------------------------------------
  505. * line positive | asserted | not asserted
  506. * ---------------|-----------------------|----------------------
  507. * line negative | not asserted | asserted
  508. * ---------------|-----------------------|----------------------
  509. *
  510. * Parameters:
  511. * aModemStatus: a bitmask that represents the
  512. * state of the comm port pins - the value
  513. * should be retrieved by a call to
  514. * GetCommModemStatus
  515. * Line: either LINE_FAIL or LOW_BATT
  516. *
  517. * Returns:
  518. * TRUE if line is asserted, FALSE otherwise
  519. *
  520. */
  521. BOOL upsLineAsserted(DWORD aModemStatus, DWORD aLine)
  522. {
  523. DWORD asserted;
  524. DWORD status;
  525. DWORD assertion;
  526. //
  527. // only look at the line that was selected
  528. // this filters out the other fields in the
  529. // aModemStatus bitmask
  530. //
  531. status = aLine & aModemStatus;
  532. //
  533. // determine if the line is asserted based
  534. // on positive or negative voltages
  535. //
  536. assertion = (aLine == LINE_FAIL) ?
  537. (_theUps.theSignalsMask & UPS_POSSIGONPOWERFAIL) :
  538. (_theUps.theSignalsMask & UPS_POSSIGONLOWBATTERY);
  539. if (status) {
  540. //
  541. // the line has positive voltage
  542. //
  543. if (assertion) {
  544. //
  545. // the UPS uses positive voltage to
  546. // assert the line
  547. //
  548. asserted = TRUE;
  549. }
  550. else {
  551. //
  552. // the UPS uses negative voltage to
  553. // assert the line
  554. //
  555. asserted = FALSE;
  556. }
  557. }
  558. else {
  559. //
  560. // the line has negative voltage
  561. //
  562. if (assertion) {
  563. //
  564. // the UPS uses positive voltage to
  565. // assert the line
  566. //
  567. asserted = FALSE;
  568. }
  569. else {
  570. //
  571. // the UPS uses negative voltage to
  572. // assert the line
  573. //
  574. asserted = TRUE;
  575. }
  576. }
  577. return asserted;
  578. }
  579. /**
  580. * UpsMonitoringThread
  581. *
  582. * Description:
  583. * Method used by the thread to monitor the UPS port
  584. * for changes. The thread will exit when the event
  585. * _theUps.theStopMonitoringEvent is signalled
  586. *
  587. * Parameters:
  588. * unused: not used
  589. *
  590. * Returns:
  591. * ERROR_SUCCESS
  592. *
  593. */
  594. DWORD WINAPI UpsMonitoringThread(LPVOID unused)
  595. {
  596. DWORD ModemStatus = 0;
  597. HANDLE events[2];
  598. OVERLAPPED UpsPinOverlap;
  599. //
  600. // create an event for the OVERLAPPED struct, this event
  601. // will signalled when one of the pins that we are
  602. // monitoring, defined by SetCommMask in setupCommPort,
  603. // indicates a change in its signal state
  604. //
  605. UpsPinOverlap.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  606. //
  607. // since the thread reacts to two events, a signal from the
  608. // comm port, and a Stop event, we initialize an array of events
  609. // to pass into WaitForMultipleObjects
  610. //
  611. events[0] = _theUps.theStopMonitoringEvent;
  612. events[1] = UpsPinOverlap.hEvent;
  613. while (TRUE) {
  614. //
  615. // tell the system to wait for comm events again
  616. //
  617. WaitCommEvent(_theUps.theCommPort, &ModemStatus, &UpsPinOverlap);
  618. //
  619. // block waiting for either a comm port event, or a stop
  620. // request from another thread
  621. //
  622. WaitForMultipleObjects(2, events, FALSE, INFINITE);
  623. //
  624. // Test to see if the stop event is signalled, if it
  625. // is then break out of the while loop.
  626. //
  627. // The wait is to allow for the port to settle before reading
  628. // the value.
  629. //
  630. if (WAIT_OBJECT_0 ==
  631. WaitForSingleObject(_theUps.theStopMonitoringEvent, DEBOUNCE_DELAY_TIME)) {
  632. break;
  633. }
  634. //
  635. // ask the system about the status of the comm port
  636. // and pass the value to updateUpsState so it can
  637. // determine the new state of the UPS
  638. // Then simply continue monitoring the port.
  639. //
  640. GetCommModemStatus(_theUps.theCommPort, &ModemStatus);
  641. updateUpsState(ModemStatus);
  642. }
  643. CloseHandle(UpsPinOverlap.hEvent);
  644. return ERROR_SUCCESS;
  645. }