/smc-command/smc.c

https://github.com/hholtmann/smcFanControl · C · 699 lines · 567 code · 109 blank · 23 comment · 176 complexity · b98a0181359255dad7c52a4552a75c68 MD5 · raw file

  1. /*
  2. * Apple System Management Control (SMC) Tool
  3. * Copyright (C) 2006 devnull
  4. * Portions Copyright (C) 2013 Michael Wilber
  5. *
  6. * This program is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU General Public License
  8. * as published by the Free Software Foundation; either version 2
  9. * of the License, or (at your option) any later version.
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. * You should have received a copy of the GNU General Public License
  15. * along with this program; if not, write to the Free Software
  16. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  17. */
  18. #include <unistd.h>
  19. #include <stdio.h>
  20. #include <stdlib.h>
  21. #include <string.h>
  22. #include <IOKit/IOKitLib.h>
  23. #include "smc.h"
  24. #include <libkern/OSAtomic.h>
  25. // Cache the keyInfo to lower the energy impact of SMCReadKey() / SMCReadKey2()
  26. #define KEY_INFO_CACHE_SIZE 100
  27. struct {
  28. UInt32 key;
  29. SMCKeyData_keyInfo_t keyInfo;
  30. } g_keyInfoCache[KEY_INFO_CACHE_SIZE];
  31. int g_keyInfoCacheCount = 0;
  32. OSSpinLock g_keyInfoSpinLock = 0;
  33. kern_return_t SMCCall2(int index, SMCKeyData_t *inputStructure, SMCKeyData_t *outputStructure, io_connect_t conn);
  34. #pragma mark C Helpers
  35. UInt32 _strtoul(char *str, int size, int base)
  36. {
  37. UInt32 total = 0;
  38. int i;
  39. for (i = 0; i < size; i++)
  40. {
  41. if (base == 16)
  42. total += str[i] << (size - 1 - i) * 8;
  43. else
  44. total += ((unsigned char) (str[i]) << (size - 1 - i) * 8);
  45. }
  46. return total;
  47. }
  48. void _ultostr(char *str, UInt32 val)
  49. {
  50. str[0] = '\0';
  51. sprintf(str, "%c%c%c%c",
  52. (unsigned int) val >> 24,
  53. (unsigned int) val >> 16,
  54. (unsigned int) val >> 8,
  55. (unsigned int) val);
  56. }
  57. float _strtof(unsigned char *str, int size, int e)
  58. {
  59. float total = 0;
  60. int i;
  61. for (i = 0; i < size; i++)
  62. {
  63. if (i == (size - 1))
  64. total += (str[i] & 0xff) >> e;
  65. else
  66. total += str[i] << (size - 1 - i) * (8 - e);
  67. }
  68. total += (str[size-1] & 0x03) * 0.25;
  69. return total;
  70. }
  71. void printFP1F(SMCVal_t val)
  72. {
  73. printf("%.5f ", ntohs(*(UInt16*)val.bytes) / 32768.0);
  74. }
  75. void printFP4C(SMCVal_t val)
  76. {
  77. printf("%.5f ", ntohs(*(UInt16*)val.bytes) / 4096.0);
  78. }
  79. void printFP5B(SMCVal_t val)
  80. {
  81. printf("%.5f ", ntohs(*(UInt16*)val.bytes) / 2048.0);
  82. }
  83. void printFP6A(SMCVal_t val)
  84. {
  85. printf("%.4f ", ntohs(*(UInt16*)val.bytes) / 1024.0);
  86. }
  87. void printFP79(SMCVal_t val)
  88. {
  89. printf("%.4f ", ntohs(*(UInt16*)val.bytes) / 512.0);
  90. }
  91. void printFP88(SMCVal_t val)
  92. {
  93. printf("%.3f ", ntohs(*(UInt16*)val.bytes) / 256.0);
  94. }
  95. void printFPA6(SMCVal_t val)
  96. {
  97. printf("%.2f ", ntohs(*(UInt16*)val.bytes) / 64.0);
  98. }
  99. void printFPC4(SMCVal_t val)
  100. {
  101. printf("%.2f ", ntohs(*(UInt16*)val.bytes) / 16.0);
  102. }
  103. void printFPE2(SMCVal_t val)
  104. {
  105. printf("%.2f ", ntohs(*(UInt16*)val.bytes) / 4.0);
  106. }
  107. void printUInt(SMCVal_t val)
  108. {
  109. printf("%u ", (unsigned int) _strtoul((char *)val.bytes, val.dataSize, 10));
  110. }
  111. void printSP1E(SMCVal_t val)
  112. {
  113. printf("%.5f ", ((SInt16)ntohs(*(UInt16*)val.bytes)) / 16384.0);
  114. }
  115. void printSP3C(SMCVal_t val)
  116. {
  117. printf("%.5f ", ((SInt16)ntohs(*(UInt16*)val.bytes)) / 4096.0);
  118. }
  119. void printSP4B(SMCVal_t val)
  120. {
  121. printf("%.4f ", ((SInt16)ntohs(*(UInt16*)val.bytes)) / 2048.0);
  122. }
  123. void printSP5A(SMCVal_t val)
  124. {
  125. printf("%.4f ", ((SInt16)ntohs(*(UInt16*)val.bytes)) / 1024.0);
  126. }
  127. void printSP69(SMCVal_t val)
  128. {
  129. printf("%.3f ", ((SInt16)ntohs(*(UInt16*)val.bytes)) / 512.0);
  130. }
  131. void printSP78(SMCVal_t val)
  132. {
  133. printf("%.3f ", ((SInt16)ntohs(*(UInt16*)val.bytes)) / 256.0);
  134. }
  135. void printSP87(SMCVal_t val)
  136. {
  137. printf("%.3f ", ((SInt16)ntohs(*(UInt16*)val.bytes)) / 128.0);
  138. }
  139. void printSP96(SMCVal_t val)
  140. {
  141. printf("%.2f ", ((SInt16)ntohs(*(UInt16*)val.bytes)) / 64.0);
  142. }
  143. void printSPB4(SMCVal_t val)
  144. {
  145. printf("%.2f ", ((SInt16)ntohs(*(UInt16*)val.bytes)) / 16.0);
  146. }
  147. void printSPF0(SMCVal_t val)
  148. {
  149. printf("%.0f ", (float)ntohs(*(UInt16*)val.bytes));
  150. }
  151. void printSI8(SMCVal_t val)
  152. {
  153. printf("%d ", (signed char)*val.bytes);
  154. }
  155. void printSI16(SMCVal_t val)
  156. {
  157. printf("%d ", ntohs(*(SInt16*)val.bytes));
  158. }
  159. void printPWM(SMCVal_t val)
  160. {
  161. printf("%.1f%% ", ntohs(*(UInt16*)val.bytes) * 100 / 65536.0);
  162. }
  163. void printBytesHex(SMCVal_t val)
  164. {
  165. int i;
  166. printf("(bytes");
  167. for (i = 0; i < val.dataSize; i++)
  168. printf(" %02x", (unsigned char) val.bytes[i]);
  169. printf(")\n");
  170. }
  171. void printVal(SMCVal_t val)
  172. {
  173. printf(" %-4s [%-4s] ", val.key, val.dataType);
  174. if (val.dataSize > 0)
  175. {
  176. if ((strcmp(val.dataType, DATATYPE_UINT8) == 0) ||
  177. (strcmp(val.dataType, DATATYPE_UINT16) == 0) ||
  178. (strcmp(val.dataType, DATATYPE_UINT32) == 0))
  179. printUInt(val);
  180. else if (strcmp(val.dataType, DATATYPE_FP1F) == 0 && val.dataSize == 2)
  181. printFP1F(val);
  182. else if (strcmp(val.dataType, DATATYPE_FP4C) == 0 && val.dataSize == 2)
  183. printFP4C(val);
  184. else if (strcmp(val.dataType, DATATYPE_FP5B) == 0 && val.dataSize == 2)
  185. printFP5B(val);
  186. else if (strcmp(val.dataType, DATATYPE_FP6A) == 0 && val.dataSize == 2)
  187. printFP6A(val);
  188. else if (strcmp(val.dataType, DATATYPE_FP79) == 0 && val.dataSize == 2)
  189. printFP79(val);
  190. else if (strcmp(val.dataType, DATATYPE_FP88) == 0 && val.dataSize == 2)
  191. printFP88(val);
  192. else if (strcmp(val.dataType, DATATYPE_FPA6) == 0 && val.dataSize == 2)
  193. printFPA6(val);
  194. else if (strcmp(val.dataType, DATATYPE_FPC4) == 0 && val.dataSize == 2)
  195. printFPC4(val);
  196. else if (strcmp(val.dataType, DATATYPE_FPE2) == 0 && val.dataSize == 2)
  197. printFPE2(val);
  198. else if (strcmp(val.dataType, DATATYPE_SP1E) == 0 && val.dataSize == 2)
  199. printSP1E(val);
  200. else if (strcmp(val.dataType, DATATYPE_SP3C) == 0 && val.dataSize == 2)
  201. printSP3C(val);
  202. else if (strcmp(val.dataType, DATATYPE_SP4B) == 0 && val.dataSize == 2)
  203. printSP4B(val);
  204. else if (strcmp(val.dataType, DATATYPE_SP5A) == 0 && val.dataSize == 2)
  205. printSP5A(val);
  206. else if (strcmp(val.dataType, DATATYPE_SP69) == 0 && val.dataSize == 2)
  207. printSP69(val);
  208. else if (strcmp(val.dataType, DATATYPE_SP78) == 0 && val.dataSize == 2)
  209. printSP78(val);
  210. else if (strcmp(val.dataType, DATATYPE_SP87) == 0 && val.dataSize == 2)
  211. printSP87(val);
  212. else if (strcmp(val.dataType, DATATYPE_SP96) == 0 && val.dataSize == 2)
  213. printSP96(val);
  214. else if (strcmp(val.dataType, DATATYPE_SPB4) == 0 && val.dataSize == 2)
  215. printSPB4(val);
  216. else if (strcmp(val.dataType, DATATYPE_SPF0) == 0 && val.dataSize == 2)
  217. printSPF0(val);
  218. else if (strcmp(val.dataType, DATATYPE_SI8) == 0 && val.dataSize == 1)
  219. printSI8(val);
  220. else if (strcmp(val.dataType, DATATYPE_SI16) == 0 && val.dataSize == 2)
  221. printSI16(val);
  222. else if (strcmp(val.dataType, DATATYPE_PWM) == 0 && val.dataSize == 2)
  223. printPWM(val);
  224. printBytesHex(val);
  225. }
  226. else
  227. {
  228. printf("no data\n");
  229. }
  230. }
  231. #pragma mark Shared SMC functions
  232. kern_return_t SMCOpen(io_connect_t *conn)
  233. {
  234. kern_return_t result;
  235. mach_port_t masterPort;
  236. io_iterator_t iterator;
  237. io_object_t device;
  238. IOMasterPort(MACH_PORT_NULL, &masterPort);
  239. CFMutableDictionaryRef matchingDictionary = IOServiceMatching("AppleSMC");
  240. result = IOServiceGetMatchingServices(masterPort, matchingDictionary, &iterator);
  241. if (result != kIOReturnSuccess)
  242. {
  243. printf("Error: IOServiceGetMatchingServices() = %08x\n", result);
  244. return 1;
  245. }
  246. device = IOIteratorNext(iterator);
  247. IOObjectRelease(iterator);
  248. if (device == 0)
  249. {
  250. printf("Error: no SMC found\n");
  251. return 1;
  252. }
  253. result = IOServiceOpen(device, mach_task_self(), 0, conn);
  254. IOObjectRelease(device);
  255. if (result != kIOReturnSuccess)
  256. {
  257. printf("Error: IOServiceOpen() = %08x\n", result);
  258. return 1;
  259. }
  260. return kIOReturnSuccess;
  261. }
  262. kern_return_t SMCClose(io_connect_t conn)
  263. {
  264. return IOServiceClose(conn);
  265. }
  266. kern_return_t SMCCall2(int index, SMCKeyData_t *inputStructure, SMCKeyData_t *outputStructure,io_connect_t conn)
  267. {
  268. size_t structureInputSize;
  269. size_t structureOutputSize;
  270. structureInputSize = sizeof(SMCKeyData_t);
  271. structureOutputSize = sizeof(SMCKeyData_t);
  272. return IOConnectCallStructMethod(conn, index, inputStructure, structureInputSize, outputStructure, &structureOutputSize);
  273. }
  274. // Provides key info, using a cache to dramatically improve the energy impact of smcFanControl
  275. kern_return_t SMCGetKeyInfo(UInt32 key, SMCKeyData_keyInfo_t* keyInfo, io_connect_t conn)
  276. {
  277. SMCKeyData_t inputStructure;
  278. SMCKeyData_t outputStructure;
  279. kern_return_t result = kIOReturnSuccess;
  280. int i = 0;
  281. OSSpinLockLock(&g_keyInfoSpinLock);
  282. for (; i < g_keyInfoCacheCount; ++i)
  283. {
  284. if (key == g_keyInfoCache[i].key)
  285. {
  286. *keyInfo = g_keyInfoCache[i].keyInfo;
  287. break;
  288. }
  289. }
  290. if (i == g_keyInfoCacheCount)
  291. {
  292. // Not in cache, must look it up.
  293. memset(&inputStructure, 0, sizeof(inputStructure));
  294. memset(&outputStructure, 0, sizeof(outputStructure));
  295. inputStructure.key = key;
  296. inputStructure.data8 = SMC_CMD_READ_KEYINFO;
  297. result = SMCCall2(KERNEL_INDEX_SMC, &inputStructure, &outputStructure, conn);
  298. if (result == kIOReturnSuccess)
  299. {
  300. *keyInfo = outputStructure.keyInfo;
  301. if (g_keyInfoCacheCount < KEY_INFO_CACHE_SIZE)
  302. {
  303. g_keyInfoCache[g_keyInfoCacheCount].key = key;
  304. g_keyInfoCache[g_keyInfoCacheCount].keyInfo = outputStructure.keyInfo;
  305. ++g_keyInfoCacheCount;
  306. }
  307. }
  308. }
  309. OSSpinLockUnlock(&g_keyInfoSpinLock);
  310. return result;
  311. }
  312. kern_return_t SMCReadKey2(UInt32Char_t key, SMCVal_t *val,io_connect_t conn)
  313. {
  314. kern_return_t result;
  315. SMCKeyData_t inputStructure;
  316. SMCKeyData_t outputStructure;
  317. memset(&inputStructure, 0, sizeof(SMCKeyData_t));
  318. memset(&outputStructure, 0, sizeof(SMCKeyData_t));
  319. memset(val, 0, sizeof(SMCVal_t));
  320. inputStructure.key = _strtoul(key, 4, 16);
  321. sprintf(val->key, key);
  322. result = SMCGetKeyInfo(inputStructure.key, &outputStructure.keyInfo, conn);
  323. if (result != kIOReturnSuccess)
  324. {
  325. return result;
  326. }
  327. val->dataSize = outputStructure.keyInfo.dataSize;
  328. _ultostr(val->dataType, outputStructure.keyInfo.dataType);
  329. inputStructure.keyInfo.dataSize = val->dataSize;
  330. inputStructure.data8 = SMC_CMD_READ_BYTES;
  331. result = SMCCall2(KERNEL_INDEX_SMC, &inputStructure, &outputStructure,conn);
  332. if (result != kIOReturnSuccess)
  333. {
  334. return result;
  335. }
  336. memcpy(val->bytes, outputStructure.bytes, sizeof(outputStructure.bytes));
  337. return kIOReturnSuccess;
  338. }
  339. #pragma mark Command line only
  340. // Exclude command-line only code from smcFanControl UI
  341. #ifdef CMD_TOOL
  342. io_connect_t g_conn = 0;
  343. void smc_init(){
  344. SMCOpen(&g_conn);
  345. }
  346. void smc_close(){
  347. SMCClose(g_conn);
  348. }
  349. kern_return_t SMCCall(int index, SMCKeyData_t *inputStructure, SMCKeyData_t *outputStructure)
  350. {
  351. return SMCCall2(index, inputStructure, outputStructure, g_conn);
  352. }
  353. kern_return_t SMCReadKey(UInt32Char_t key, SMCVal_t *val)
  354. {
  355. return SMCReadKey2(key, val, g_conn);
  356. }
  357. kern_return_t SMCWriteKey2(SMCVal_t writeVal, io_connect_t conn)
  358. {
  359. kern_return_t result;
  360. SMCKeyData_t inputStructure;
  361. SMCKeyData_t outputStructure;
  362. SMCVal_t readVal;
  363. result = SMCReadKey2(writeVal.key, &readVal,conn);
  364. if (result != kIOReturnSuccess)
  365. return result;
  366. if (readVal.dataSize != writeVal.dataSize)
  367. return kIOReturnError;
  368. memset(&inputStructure, 0, sizeof(SMCKeyData_t));
  369. memset(&outputStructure, 0, sizeof(SMCKeyData_t));
  370. inputStructure.key = _strtoul(writeVal.key, 4, 16);
  371. inputStructure.data8 = SMC_CMD_WRITE_BYTES;
  372. inputStructure.keyInfo.dataSize = writeVal.dataSize;
  373. memcpy(inputStructure.bytes, writeVal.bytes, sizeof(writeVal.bytes));
  374. result = SMCCall2(KERNEL_INDEX_SMC, &inputStructure, &outputStructure,conn);
  375. if (result != kIOReturnSuccess)
  376. return result;
  377. return kIOReturnSuccess;
  378. }
  379. kern_return_t SMCWriteKey(SMCVal_t writeVal)
  380. {
  381. return SMCWriteKey2(writeVal, g_conn);
  382. }
  383. UInt32 SMCReadIndexCount(void)
  384. {
  385. SMCVal_t val;
  386. SMCReadKey("#KEY", &val);
  387. return _strtoul((char *)val.bytes, val.dataSize, 10);
  388. }
  389. kern_return_t SMCPrintAll(void)
  390. {
  391. kern_return_t result;
  392. SMCKeyData_t inputStructure;
  393. SMCKeyData_t outputStructure;
  394. int totalKeys, i;
  395. UInt32Char_t key;
  396. SMCVal_t val;
  397. totalKeys = SMCReadIndexCount();
  398. for (i = 0; i < totalKeys; i++)
  399. {
  400. memset(&inputStructure, 0, sizeof(SMCKeyData_t));
  401. memset(&outputStructure, 0, sizeof(SMCKeyData_t));
  402. memset(&val, 0, sizeof(SMCVal_t));
  403. inputStructure.data8 = SMC_CMD_READ_INDEX;
  404. inputStructure.data32 = i;
  405. result = SMCCall(KERNEL_INDEX_SMC, &inputStructure, &outputStructure);
  406. if (result != kIOReturnSuccess)
  407. continue;
  408. _ultostr(key, outputStructure.key);
  409. SMCReadKey(key, &val);
  410. printVal(val);
  411. }
  412. return kIOReturnSuccess;
  413. }
  414. kern_return_t SMCPrintFans(void)
  415. {
  416. kern_return_t result;
  417. SMCVal_t val;
  418. UInt32Char_t key;
  419. int totalFans, i;
  420. result = SMCReadKey("FNum", &val);
  421. if (result != kIOReturnSuccess)
  422. return kIOReturnError;
  423. totalFans = _strtoul((char *)val.bytes, val.dataSize, 10);
  424. printf("Total fans in system: %d\n", totalFans);
  425. for (i = 0; i < totalFans; i++)
  426. {
  427. printf("\nFan #%d:\n", i);
  428. sprintf(key, "F%dID", i);
  429. SMCReadKey(key, &val);
  430. printf(" Fan ID : %s\n", val.bytes+4);
  431. sprintf(key, "F%dAc", i);
  432. SMCReadKey(key, &val);
  433. printf(" Actual speed : %.0f\n", _strtof(val.bytes, val.dataSize, 2));
  434. sprintf(key, "F%dMn", i);
  435. SMCReadKey(key, &val);
  436. printf(" Minimum speed: %.0f\n", _strtof(val.bytes, val.dataSize, 2));
  437. sprintf(key, "F%dMx", i);
  438. SMCReadKey(key, &val);
  439. printf(" Maximum speed: %.0f\n", _strtof(val.bytes, val.dataSize, 2));
  440. sprintf(key, "F%dSf", i);
  441. SMCReadKey(key, &val);
  442. printf(" Safe speed : %.0f\n", _strtof(val.bytes, val.dataSize, 2));
  443. sprintf(key, "F%dTg", i);
  444. SMCReadKey(key, &val);
  445. printf(" Target speed : %.0f\n", _strtof(val.bytes, val.dataSize, 2));
  446. SMCReadKey("FS! ", &val);
  447. if ((_strtoul((char *)val.bytes, 2, 16) & (1 << i)) == 0)
  448. printf(" Mode : auto\n");
  449. else
  450. printf(" Mode : forced\n");
  451. }
  452. return kIOReturnSuccess;
  453. }
  454. void usage(char* prog)
  455. {
  456. printf("Apple System Management Control (SMC) tool %s\n", VERSION);
  457. printf("Usage:\n");
  458. printf("%s [options]\n", prog);
  459. printf(" -f : fan info decoded\n");
  460. printf(" -h : help\n");
  461. printf(" -k <key> : key to manipulate\n");
  462. printf(" -l : list all keys and values\n");
  463. printf(" -r : read the value of a key\n");
  464. printf(" -w <value> : write the specified value to a key\n");
  465. printf(" -v : version\n");
  466. printf("\n");
  467. }
  468. kern_return_t SMCWriteSimple(UInt32Char_t key, char *wvalue, io_connect_t conn)
  469. {
  470. kern_return_t result;
  471. SMCVal_t val;
  472. int i;
  473. char c[3];
  474. for (i = 0; i < strlen(wvalue); i++)
  475. {
  476. sprintf(c, "%c%c", wvalue[i * 2], wvalue[(i * 2) + 1]);
  477. val.bytes[i] = (int) strtol(c, NULL, 16);
  478. }
  479. val.dataSize = i / 2;
  480. sprintf(val.key, key);
  481. result = SMCWriteKey2(val, conn);
  482. if (result != kIOReturnSuccess)
  483. printf("Error: SMCWriteKey() = %08x\n", result);
  484. return result;
  485. }
  486. int main(int argc, char *argv[])
  487. {
  488. int c;
  489. extern char *optarg;
  490. kern_return_t result;
  491. int op = OP_NONE;
  492. UInt32Char_t key = { 0 };
  493. SMCVal_t val;
  494. while ((c = getopt(argc, argv, "fhk:lrw:v")) != -1)
  495. {
  496. switch(c)
  497. {
  498. case 'f':
  499. op = OP_READ_FAN;
  500. break;
  501. case 'k':
  502. strncpy(key, optarg, sizeof(key)); //fix for buffer overflow
  503. key[sizeof(key) - 1] = '\0';
  504. break;
  505. case 'l':
  506. op = OP_LIST;
  507. break;
  508. case 'r':
  509. op = OP_READ;
  510. break;
  511. case 'v':
  512. printf("%s\n", VERSION);
  513. return 0;
  514. break;
  515. case 'w':
  516. op = OP_WRITE;
  517. {
  518. int i;
  519. char c[3];
  520. for (i = 0; i < strlen(optarg); i++)
  521. {
  522. sprintf(c, "%c%c", optarg[i * 2], optarg[(i * 2) + 1]);
  523. val.bytes[i] = (int) strtol(c, NULL, 16);
  524. }
  525. val.dataSize = i / 2;
  526. if ((val.dataSize * 2) != strlen(optarg))
  527. {
  528. printf("Error: value is not valid\n");
  529. return 1;
  530. }
  531. }
  532. break;
  533. case 'h':
  534. case '?':
  535. op = OP_NONE;
  536. break;
  537. }
  538. }
  539. if (op == OP_NONE)
  540. {
  541. usage(argv[0]);
  542. return 1;
  543. }
  544. smc_init();
  545. switch(op)
  546. {
  547. case OP_LIST:
  548. result = SMCPrintAll();
  549. if (result != kIOReturnSuccess)
  550. printf("Error: SMCPrintAll() = %08x\n", result);
  551. break;
  552. case OP_READ:
  553. if (strlen(key) > 0)
  554. {
  555. result = SMCReadKey(key, &val);
  556. if (result != kIOReturnSuccess)
  557. printf("Error: SMCReadKey() = %08x\n", result);
  558. else
  559. printVal(val);
  560. }
  561. else
  562. {
  563. printf("Error: specify a key to read\n");
  564. }
  565. break;
  566. case OP_READ_FAN:
  567. result = SMCPrintFans();
  568. if (result != kIOReturnSuccess)
  569. printf("Error: SMCPrintFans() = %08x\n", result);
  570. break;
  571. case OP_WRITE:
  572. if (strlen(key) > 0)
  573. {
  574. sprintf(val.key, key);
  575. result = SMCWriteKey(val);
  576. if (result != kIOReturnSuccess)
  577. printf("Error: SMCWriteKey() = %08x\n", result);
  578. }
  579. else
  580. {
  581. printf("Error: specify a key to write\n");
  582. }
  583. break;
  584. }
  585. smc_close();
  586. return 0;
  587. }
  588. #endif //#ifdef CMD_TOOL