Platform (OS X 10.6.8) - [Macbook Pro - this is important as I want to deal with the battery handling - not applicable for a desktop]
Forgive me if I've made a basic mistake that I don't see, as I haven't written any C/C++ in five years or more, and I'm not fully following the way apple APIs want you to deal with them, so this is what my question regards. Basically, I want to be able to inhibit charging from the AC adaptor at my command, so that I can choose whether to charge my laptop or not while it's in use and plugged in. I hadn't been able to find any utilities that do this, so I had at first written it off as something that was on the hardware level and couldn't be changed from software, but then I ran across something encouraging:
If you open a Terminal window and run
pmset -g assertionslog
you get a list of assertions, and a 0 or 1 for each, indicating if some process has asserted that assertion. The first of them is titled ChargeInhibit, and I discovered after some digging that this does exactly what I want, at the software level. I just have to figure out how to assert it.
I copied some code from a file in the apple source archive called SetActive.c (link) I copied the function sendSmartBatteryCommand into my XCode project, and spent time linking other headers and copying over definitions until I could get it to compile correctly. Here is the copied function:
// The copied function,
// modified very slightly:
// to return a success/fail value instead of void
kern_return_t sendSmartBatteryCommand(uint32_t which, uint32_t level)
{
io_service_t sbmanager = MACH_PORT_NULL;
io_connect_t sbconnection = MACH_PORT_NULL;
kern_return_t kret = 99;
uint32_t output_count = 1;
uint64_t uc_return = kIOReturnError;
uint64_t level_64 = level;
// Find SmartBattery manager
sbmanager = IOServiceGetMatchingService(MACH_PORT_NULL,
IOServiceMatching("AppleSmartBatteryManager"));
if(MACH_PORT_NULL == sbmanager) {
goto bail;
}
kret = IOServiceOpen( sbmanager, mach_task_self(), 0, &sbconnection);
if(kIOReturnSuccess != kret) {
goto bail;
}
kret = IOConnectCallMethod(
sbconnection, // connection
which, // selector
&level_64, // uint64_t *input
1, // input Count
NULL, // input struct count
0, // input struct count
&uc_return, // output
&output_count, // output count
NULL, // output struct
0); // output struct count
bail:
if (MACH_PORT_NULL != sbconnection) {
IOServiceClose(sbconnection);
}
if (MACH_PORT_NULL != sbmanager) {
IOObjectRelease(sbmanager);
}
return kret;
}
I get success values back when I try to use it to send assertions for charge inhibiting and inflow disabling, but the log from pmset doesn't show any changes, and neither does the actual state of my battery charging / not-charging.
I've also tried modifying the name of the service to look for from "AppleSmartBatteryManager" to a nonsense word just to see if the function returns failure, and it does, so that indicates I'm connecting to a real service.
Any tips on how I can achieve this as simply as possible?
By the way, I've tried to recompile the AppleSmartBatteryManager in the PowerManagement project from the source package on the apple source site, but I way more errors in XCode than I can deal with. I'm looking to try to interact with the existing service in some way that does not make me recompile the AppleSmartBatteryManager source alongside my own project.
EDIT: By the way, this is an example of me calling the function:
int CInh()
{
kern_return_t kret = sendSmartBatteryCommand( kSBUCChargeInhibit, 255); // zero:uninhibit, non-zero:inhibit
if(kret == KERN_SUCCESS)
return 1;
else
return 0;
}
Where options for the "which" parameter are defined in an enum in my header file (also copied from SetActive.c):
enum {
kSBUCInflowDisable = 0,
kSBUCChargeInhibit = 1
};