1/* 2 * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24#include <IOKit/pwr_mgt/RootDomain.h> 25 26#include "AppleSmartBatteryManager.h" 27#include "AppleSmartBattery.h" 28 29#define kMaxRetries 5 30 31static int retryDelaysTable[kMaxRetries] = 32 { 1, 10, 100, 1000, 1000 }; 33 34// Power states! 35enum { 36 kMyOnPowerState = 1 37}; 38 39// Commands! 40enum { 41 kInhibitChargingCmd = 0, 42 kDisableInflowCmd 43}; 44 45static IOPMPowerState myTwoStates[2] = { 46 {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 47 {1, kIOPMPowerOn, kIOPMPowerOn, kIOPMPowerOn, 0, 0, 0, 0, 0, 0, 0, 0} 48}; 49 50#define super IOService 51 52OSDefineMetaClassAndStructors(AppleSmartBatteryManager, IOService) 53 54bool AppleSmartBatteryManager::start(IOService *provider) 55{ 56 IOCommandGate * gate; 57 IOWorkLoop * wl; 58 59 if(!super::start(provider)) { 60 return false; 61 } 62 63 fProvider = OSDynamicCast(IOSMBusController, provider); 64 if(!fProvider) { 65 return false; 66 } 67 68 const OSSymbol *ucClassName = 69 OSSymbol::withCStringNoCopy("AppleSmartBatteryManagerUserClient"); 70 setProperty(gIOUserClientClassKey, (OSObject *) ucClassName); 71 ucClassName->release(); 72 73 wl = getWorkLoop(); 74 if (!wl) { 75 return false; 76 } 77 78 // Join power management so that we can get a notification early during 79 // wakeup to re-sample our battery data. We don't actually power manage 80 // any devices. 81 PMinit(); 82 registerPowerDriver(this, myTwoStates, 2); 83 provider->joinPMtree(this); 84 85 fBattery = AppleSmartBattery::smartBattery(); 86 87 if(!fBattery) return false; 88 89 fBattery->attach(this); 90 91 fBattery->start(this); 92 93 // Command gate for SmartBatteryManager 94 fManagerGate = IOCommandGate::commandGate(this); 95 if (!fManagerGate) { 96 return false; 97 } 98 wl->addEventSource(fManagerGate); 99 100 // Command gate for SmartBattery 101 gate = IOCommandGate::commandGate(fBattery); 102 if (!gate) { 103 return false; 104 } 105 wl->addEventSource(gate); 106 fBatteryGate = gate; // enable messages 107 108 // Track UserClient exclusive access to smartbattery 109 fExclusiveUserClient = false; 110 111 fBattery->registerService(0); 112 113 this->registerService(0); 114 115 return true; 116} 117 118// Default polling interval is 30 seconds 119/* 120IOReturn AppleSmartBatteryManager::setPollingInterval( 121 int milliSeconds) 122{ 123 // Discard any negatize or zero arguments 124 if(milliSeconds <= 0) return kIOReturnBadArgument; 125 126 if(fBattery) 127 fBattery->setPollingInterval(milliSeconds); 128 129 setProperty("PollingInterval_msec", milliSeconds, 32); 130 131 return kIOReturnSuccess; 132} 133 */ 134 135/* 136 * performExternalWordTransaction 137 * 138 * Called by AppleSmartBatteryManagerUserClient 139 */ 140IOReturn AppleSmartBatteryManager::performExternalTransaction( 141 void *in, 142 void *out, 143 IOByteCount inSize, 144 IOByteCount *outSize) 145{ 146 uint16_t i; 147 uint16_t retryAttempts = 0; 148 IOSMBusTransaction newTransaction; 149 IOReturn transactionSuccess; 150 EXSMBUSInputStruct *inSMBus = (EXSMBUSInputStruct *)in; 151 EXSMBUSOutputStruct *outSMBus = (EXSMBUSOutputStruct *)out; 152 153 if (!inSMBus || !outSMBus) 154 return kIOReturnBadArgument; 155 156 /* Attempt up to 5 transactions if we get failures 157 */ 158 do { 159 bzero(&newTransaction, sizeof(IOSMBusTransaction)); 160 161 // Input: bus address 162 if (kSMBusAppleDoublerAddr == inSMBus->batterySelector 163 || kSMBusBatteryAddr == inSMBus->batterySelector 164 || kSMBusManagerAddr == inSMBus->batterySelector 165 || kSMBusChargerAddr == inSMBus->batterySelector) 166 { 167 newTransaction.address = inSMBus->batterySelector; 168 } else { 169 if (0 == inSMBus->batterySelector) 170 { 171 newTransaction.address = kSMBusBatteryAddr; 172 } else { 173 newTransaction.address = kSMBusManagerAddr; 174 } 175 } 176 177 // Input: command 178 newTransaction.command = inSMBus->address; 179 180 // Input: Read/Write Word/Block 181 switch (inSMBus->type) { 182 case kEXWriteWord: 183 newTransaction.protocol = kIOSMBusProtocolWriteWord; 184 newTransaction.sendDataCount = 2; 185 break; 186 case kEXReadWord: 187 newTransaction.protocol = kIOSMBusProtocolReadWord; 188 newTransaction.sendDataCount = 0; 189 break; 190 case kEXWriteBlock: 191 newTransaction.protocol = kIOSMBusProtocolWriteBlock; 192 // rdar://5433060 workaround for SMC SMBus blockCount bug 193 // For block writes, clients always increment inByteCount +1 194 // greater than the actual byte count. 195 // We decrement it here for IOSMBusController. 196 newTransaction.sendDataCount = inSMBus->inByteCount - 1; 197 break; 198 case kEXReadBlock: 199 newTransaction.protocol = kIOSMBusProtocolReadBlock; 200 newTransaction.sendDataCount = 0; 201 break; 202 case kEXWriteByte: 203 newTransaction.protocol = kIOSMBusProtocolWriteByte; 204 newTransaction.sendDataCount = 1; 205 break; 206 case kEXReadByte: 207 newTransaction.protocol = kIOSMBusProtocolReadByte; 208 newTransaction.sendDataCount = 0; 209 break; 210 case kEXSendByte: 211 newTransaction.protocol = kIOSMBusProtocolSendByte; 212 newTransaction.sendDataCount = 0; 213 break; 214 default: 215 return kIOReturnBadArgument; 216 } 217 218 // Input: copy data into transaction 219 // only need to copy data for write operations 220 if ((kIOSMBusProtocolWriteWord == newTransaction.protocol) 221 || (kIOSMBusProtocolWriteBlock == newTransaction.protocol)) 222 { 223 for(i = 0; i<MAX_SMBUS_DATA_SIZE; i++) { 224 newTransaction.sendData[i] = inSMBus->inBuf[i]; 225 } 226 } 227 228 229 if (inSMBus->flags & kEXFlagRetry) 230 { 231 if (retryAttempts >= kMaxRetries) { 232 // Don't read off the end of the table... 233 retryAttempts = kMaxRetries - 1; 234 } 235 236 // If this is a retry-on-failure, spin for a few microseconds 237 IODelay( retryDelaysTable[retryAttempts] ); 238 } 239 240 fManagerGate->runAction( 241 (IOCommandGate::Action)OSMemberFunctionCast( 242 IOCommandGate::Action, this, 243 &AppleSmartBatteryManager::performExternalTransactionGated), 244 (void *)&newTransaction, 245 (void *)&transactionSuccess, 246 NULL, 247 NULL); 248 249 250 /* Output: status */ 251 if ((kIOReturnSuccess == transactionSuccess) 252 && (kIOSMBusStatusOK == newTransaction.status)) 253 { 254 outSMBus->status = kIOReturnSuccess; 255 } else { 256 switch (newTransaction.status) { 257 case kIOSMBusStatusUnknownFailure: 258 case kIOSMBusStatusDeviceAddressNotAcknowledged: 259 case kIOSMBusStatusDeviceError: 260 case kIOSMBusStatusDeviceCommandAccessDenied: 261 case kIOSMBusStatusUnknownHostError: 262 outSMBus->status = kIOReturnNoDevice; 263 break; 264 case kIOSMBusStatusTimeout: 265 case kIOSMBusStatusBusy: 266 outSMBus->status = kIOReturnTimeout; 267 break; 268 case kIOSMBusStatusHostUnsupportedProtocol: 269 outSMBus->status = kIOReturnUnsupported; 270 break; 271 default: 272 outSMBus->status = kIOReturnInternalError; 273 break; 274 } 275 } 276 277 /* Retry this transaction if we received a failure 278 */ 279 } while ((inSMBus->flags & kEXFlagRetry) 280 && (outSMBus->status != kIOReturnSuccess) 281 && (++retryAttempts < kMaxRetries)); 282 283 284 /* Output: read word/read block results */ 285 if (((kIOSMBusProtocolReadWord == newTransaction.protocol) 286 || (kIOSMBusProtocolReadBlock == newTransaction.protocol) 287 || (kIOSMBusProtocolReadByte == newTransaction.protocol)) 288 && (kIOSMBusStatusOK == newTransaction.status)) 289 { 290 outSMBus->outByteCount = newTransaction.receiveDataCount; 291 292 for(i = 0; i<outSMBus->outByteCount; i++) { 293 outSMBus->outBuf[i] = newTransaction.receiveData[i]; 294 } 295 } 296 297 return kIOReturnSuccess; 298} 299 300/* 301 * performExternalTransactionGated 302 * 303 * Called by AppleSmartBatteryManagerUserClient 304 */ 305IOReturn AppleSmartBatteryManager::performExternalTransactionGated( 306 void *arg0, 307 void *arg1, 308 void *arg2 __unused, 309 void *arg3 __unused) 310{ 311 IOSMBusTransaction *trans = (IOSMBusTransaction *)arg0; 312 IOReturn *return_code = (IOReturn *)arg1; 313 314 *return_code = fProvider->performTransaction( 315 trans, /* transaction */ 316 OSMemberFunctionCast( 317 IOSMBusTransactionCompletion, /* completion */ 318 this, &AppleSmartBatteryManager::transactionCompletion), 319 (OSObject *)this, /* target */ 320 (void *)trans); /* ref */ 321 322 323 if(kIOReturnSuccess != *return_code) 324 return kIOReturnSuccess; 325 326 /* Sleep the caller's thread until the transaction completion returns */ 327 fManagerGate->commandSleep(trans, THREAD_UNINT); 328 329 /* at this point our transaction is complete; return */ 330 331 return kIOReturnSuccess; 332} 333 334/* 335 * performTransaction 336 * 337 * Called by smart battery children 338 */ 339IOReturn AppleSmartBatteryManager::performTransaction( 340 IOSMBusTransaction * transaction, 341 IOSMBusTransactionCompletion completion, 342 OSObject * target, 343 void * reference) 344{ 345 /* directly pass bus transactions back up to SMBusController */ 346 return fProvider->performTransaction(transaction, 347 completion, 348 target, 349 reference); 350} 351 352/* 353 * setPowerState 354 * 355 */ 356IOReturn AppleSmartBatteryManager::setPowerState( 357 unsigned long which, 358 IOService *whom) 359{ 360 IOReturn ret = IOPMAckImplied; 361 362 if( fBatteryGate ) 363 { 364 // We are waking from sleep - kick off a battery read to make sure 365 // our battery concept is in line with reality. 366 ret = fBatteryGate->runAction(OSMemberFunctionCast(IOCommandGate::Action, 367 fBattery, &AppleSmartBattery::handleSystemSleepWake), 368 (void *) this, (void *) !which, NULL, NULL); 369 } 370 return ret; 371} 372 373 374/* 375 * message 376 * 377 */ 378IOReturn AppleSmartBatteryManager::message( 379 UInt32 type, 380 IOService *provider, 381 void *argument ) 382{ 383 IOSMBusAlarmMessage *alarm = (IOSMBusAlarmMessage *)argument; 384 static uint16_t last_data = 0; 385 uint16_t changed_bits = 0; 386 uint16_t data = 0; 387 388 /* On SMBus alarms from the System Battery Manager, trigger a new 389 poll of battery state. */ 390 391 if(!alarm) return kIOReturnSuccess; 392 393 if( (kIOMessageSMBusAlarm == type) 394 && (kSMBusManagerAddr == alarm->fromAddress) 395 && fBatteryGate) 396 { 397 data = (uint16_t)(alarm->data[0] | (alarm->data[1] << 8)); 398 changed_bits = data ^ last_data; 399 last_data = data; 400 401 if(changed_bits & kMPresentBatt_A_Bit) 402 { 403 if(data & kMPresentBatt_A_Bit) { 404 // Battery inserted 405 fBatteryGate->runAction(OSMemberFunctionCast(IOCommandGate::Action, 406 fBattery, &AppleSmartBattery::handleBatteryInserted), 407 NULL, NULL, NULL, NULL); 408 } else { 409 // Battery removed 410 fBatteryGate->runAction(OSMemberFunctionCast(IOCommandGate::Action, 411 fBattery, &AppleSmartBattery::handleBatteryRemoved), 412 NULL, NULL, NULL, NULL); 413 } 414 } else { 415 // Just an alarm; re-read battery state. 416 fBatteryGate->runAction(OSMemberFunctionCast(IOCommandGate::Action, 417 fBattery, &AppleSmartBattery::pollBatteryState), 418 NULL, NULL, NULL, NULL); 419 } 420 } 421 422 return kIOReturnSuccess; 423} 424 425/* 426 * inhibitCharging 427 * 428 * Called by AppleSmartBatteryManagerUserClient 429 */ 430IOReturn AppleSmartBatteryManager::inhibitCharging(int level) 431{ 432 IOReturn ret = kIOReturnSuccess; 433 434 if(!fManagerGate) return kIOReturnInternalError; 435 436 this->setProperty("Charging Inhibited", level ? true : false); 437 438 fManagerGate->runAction(OSMemberFunctionCast(IOCommandGate::Action, 439 this, &AppleSmartBatteryManager::gatedSendCommand), 440 (void *)kInhibitChargingCmd, (void *)(uintptr_t)level, 441 (void *)&ret, NULL); 442 443 return ret; 444} 445 446/* 447 * disableInflow 448 * 449 * Called by AppleSmartBatteryManagerUserClient 450 */ 451IOReturn AppleSmartBatteryManager::disableInflow(int level) 452{ 453 IOReturn ret = kIOReturnSuccess; 454 455 if(!fManagerGate) return kIOReturnInternalError; 456 457 this->setProperty("Inflow Disabled", level ? true : false); 458 459 fManagerGate->runAction(OSMemberFunctionCast(IOCommandGate::Action, 460 this, &AppleSmartBatteryManager::gatedSendCommand), 461 (void *)kDisableInflowCmd, (void *)(uintptr_t)level, 462 (void *)&ret, NULL); 463 464 return ret; 465} 466 467/* 468 * handleFullDischarge 469 * 470 * Called by AppleSmartBattery 471 * If inflow is disabled, by the user client, we re-enable it and send a message 472 * via IOPMrootDomain indicating that inflow has been re-enabled. 473 */ 474void AppleSmartBatteryManager::handleFullDischarge(void) 475{ 476 IOPMrootDomain *root_domain = getPMRootDomain(); 477 IOReturn ret; 478 void * messageArgument = NULL; 479 480 if( getProperty("Inflow Disabled") ) 481 { 482 messageArgument = (void *)kInflowForciblyEnabledBit; 483 484 /* 485 * Send disable inflow command to SB Manager 486 */ 487 fManagerGate->runAction(OSMemberFunctionCast(IOCommandGate::Action, 488 this, &AppleSmartBatteryManager::gatedSendCommand), 489 (void *)kDisableInflowCmd, (void *)0, /* OFF */ 490 (void *)&ret, NULL); 491 } 492 493 494 /* 495 * Message user space clients that battery is fully discharged. 496 * If appropriate, set kInflowForciblyEnabledBit 497 */ 498 if(root_domain) { 499 root_domain->messageClients( 500 kIOPMMessageInternalBatteryFullyDischarged, 501 messageArgument ); 502 } 503} 504 505/* 506 * requestExclusiveSMBusAcess 507 * 508 * Called by AppleSmartBatteryManagerUserClient 509 * When exclusive SMBus access is requested, we'll hold off on any SmartBattery bus reads. 510 * We do not do any periodic SMBus reads 511 */ 512bool AppleSmartBatteryManager::requestExclusiveSMBusAccess( 513 bool request) 514{ 515 if( request && fExclusiveUserClient) { 516 /* Oops - a second client reaching for exclusive access. 517 * This shouldn't happen. 518 */ 519 return false; 520 } 521 522 fExclusiveUserClient = request; 523 524 /* Signal our driver, and the SMC firmware to either: 525 - stop communicating with the battery 526 - resume communications 527 */ 528 fBatteryGate->runAction( 529 OSMemberFunctionCast( 530 IOCommandGate::Action, this, 531 &AppleSmartBattery::handleExclusiveAccess), 532 (void *)request, NULL, NULL, NULL); 533 534 return true; 535} 536 537bool AppleSmartBatteryManager::hasExclusiveClient(void) { 538 return fExclusiveUserClient; 539} 540 541bool AppleSmartBatteryManager::requestPoll(int type) { 542 IOReturn ret; 543 544 BattLog("AppleSmartBatteryManager requests a poll (type=%d).\n", type); 545 ret = fBatteryGate->runAction(OSMemberFunctionCast(IOCommandGate::Action, 546 fBattery, &AppleSmartBattery::pollBatteryState), 547 (void *)(uintptr_t) type, NULL, NULL, NULL); 548 549 return ret; 550} 551 552void AppleSmartBatteryManager::gatedSendCommand( 553 int cmd, 554 int level, 555 IOReturn *ret_code) 556{ 557 if (fExclusiveUserClient) { 558 *ret_code = kIOReturnExclusiveAccess; 559 return; 560 } 561 562 *ret_code = kIOReturnError; 563 bzero(&fTransaction, sizeof(IOSMBusTransaction)); 564 565 fTransaction.protocol = kIOSMBusProtocolWriteWord; 566 fTransaction.sendDataCount = 2; 567 568 /* 569 * kDisableInflowCmd 570 */ 571 if( kDisableInflowCmd == cmd) 572 { 573 fTransaction.address = kSMBusManagerAddr; 574 fTransaction.command = kMStateContCmd; 575 if( 0 != level ) { 576 fTransaction.sendData[0] = kMCalibrateBit; 577 } else { 578 fTransaction.sendData[0] = 0x0; 579 } 580 } 581 582 /* 583 * kInhibitChargingCmd 584 */ 585 if( kInhibitChargingCmd == cmd) 586 { 587 fTransaction.address = kSMBusChargerAddr; 588 fTransaction.command = kBChargingCurrentCmd; 589 if( 0 != level ) { 590 // non-zero level for chargeinhibit means turn off charging. 591 // We set charge current to 0. 592 fTransaction.sendData[0] = 0x0; 593 } else { 594 // We re-enable charging by setting it to 6000, a signifcantly 595 // large number (> 4500 per Chris C) to ensure charging will resume. 596 fTransaction.sendData[0] = 0x70; 597 fTransaction.sendData[1] = 0x17; 598 } 599 } 600 601 *ret_code = fProvider->performTransaction( 602 &fTransaction, 603 OSMemberFunctionCast( IOSMBusTransactionCompletion, 604 this, &AppleSmartBatteryManager::transactionCompletion), 605 (OSObject *)this); 606 607 return; 608} 609 610bool AppleSmartBatteryManager::transactionCompletion( 611 void *ref, 612 IOSMBusTransaction *transaction) 613{ 614 /* 615 * Transactions may be initiated by SmartBatteryManager or 616 * by external user client; we react differently to each. 617 */ 618 619 if (ref == this) { 620 /* 621 * transaction was initiated by AppleSmartBatteryManager 622 */ 623 624 if( kIOSMBusStatusOK == transaction->status ) 625 { 626 // If we just completed sending the DisableInflow command, give a 627 // callout to our attached battery and let them know whether its 628 // enabled or disabled. 629 if( (kMStateContCmd == transaction->command) 630 && (kSMBusManagerAddr == transaction->address) ) 631 { 632 fBattery->handleInflowDisabled( 633 transaction->sendData[0] ? true : false); 634 } 635 636 // If we just completed sending the ChargeInhibit command, give a 637 // callout to our attached battery and let them know whether its 638 // enabled or disabled. 639 if( (kBChargingCurrentCmd == transaction->command) 640 && (kSMBusChargerAddr == transaction->address) ) 641 { 642 fBattery->handleChargeInhibited( 643 (transaction->sendData[0] | transaction->sendData[1]) 644 ? false : true); 645 } 646 647 } else { 648 BattLog("AppleSmartBatteryManager::transactionCompletion:"\ 649 "ERROR 0x%08x!\n", transaction->status); 650 } 651 } else { 652 /* 653 * transaction was initiated by user client 654 */ 655 656 BattLog("completion: commandWaking: 0x%08x\n", transaction); 657 658 // If it's an external transaction completion; stash the result 659 // in (ref) data structure 660 fManagerGate->commandWakeup( transaction, /* wake all */ false); 661 662 } 663 664 return false; 665} 666 667void BattLog(const char *fmt, ...) 668{ 669#if 0 670 va_list listp; 671 char buf[128]; 672 673 va_start(listp, fmt); 674 vsnprintf(buf, sizeof(buf), fmt, listp); 675 va_end(listp); 676 677 IOLog("BattLog: %s", buf); 678 679 return; 680#endif 681} 682 683