/* * Copyright (c) 2002 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * The contents of this file constitute Original Code as defined in and * are subject to the Apple Public Source License Version 1.1 (the * "License"). You may not use this file except in compliance with the * License. Please obtain a copy of the License at * http://www.apple.com/publicsource and read it before using this file. * * This Original Code and all software distributed under the License are * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the * License for the specific language governing rights and limitations * under the License. * * @APPLE_LICENSE_HEADER_END@ */ /* * Modification History * * May 29, 2002 Roger Smith * - initial revision */ #include #include #include #include #include #include #include #include #include #include #include #include #include "moh_msg.h" #include "moh.h" #include "DeviceOnHold.h" #define kIODeviceSupportsHoldKey "DeviceSupportsHold" static void * __loadIOKit(void) { static const void *image = NULL; if (NULL == image) { const char *framework = "/System/Library/Frameworks/IOKit.framework/IOKit"; struct stat statbuf; const char *suffix = getenv("DYLD_IMAGE_SUFFIX"); char path[MAXPATHLEN]; strcpy(path, framework); if (suffix) strcat(path, suffix); if (0 <= stat(path, &statbuf)) { image = NSAddImage(path, NSADDIMAGE_OPTION_NONE); } else { image = NSAddImage(framework, NSADDIMAGE_OPTION_NONE); } } return (void *)image; } static io_object_t _IOIteratorNext(io_iterator_t iterator) { static io_object_t (*dyfunc)(io_iterator_t) = NULL; if (!dyfunc) { void *image = __loadIOKit(); if (image) dyfunc = NSAddressOfSymbol(NSLookupSymbolInImage(image, "_IOIteratorNext", NSLOOKUPSYMBOLINIMAGE_OPTION_BIND)); } return dyfunc ? dyfunc(iterator) : 0; } #define IOIteratorNext _IOIteratorNext static kern_return_t _IOMasterPort(mach_port_t bootstrapPort, mach_port_t *masterPort) { static kern_return_t (*dyfunc)(mach_port_t, mach_port_t *) = NULL; if (!dyfunc) { void *image = __loadIOKit(); if (image) dyfunc = NSAddressOfSymbol(NSLookupSymbolInImage(image, "_IOMasterPort", NSLOOKUPSYMBOLINIMAGE_OPTION_BIND)); } return dyfunc ? dyfunc(bootstrapPort, masterPort) : KERN_FAILURE; } #define IOMasterPort _IOMasterPort static kern_return_t _IOObjectRelease(io_object_t object) { static kern_return_t (*dyfunc)(io_object_t) = NULL; if (!dyfunc) { void *image = __loadIOKit(); if (image) dyfunc = NSAddressOfSymbol(NSLookupSymbolInImage(image, "_IOObjectRelease", NSLOOKUPSYMBOLINIMAGE_OPTION_BIND)); } return dyfunc ? dyfunc(object) : KERN_FAILURE; } #define IOObjectRelease _IOObjectRelease static kern_return_t _IORegistryEntryCreateCFProperties(io_registry_entry_t entry, CFMutableDictionaryRef *properties, CFAllocatorRef allocator, IOOptionBits options) { static kern_return_t (*dyfunc)(io_registry_entry_t, CFMutableDictionaryRef *, CFAllocatorRef, IOOptionBits) = NULL; if (!dyfunc) { void *image = __loadIOKit(); if (image) dyfunc = NSAddressOfSymbol(NSLookupSymbolInImage(image, "_IORegistryEntryCreateCFProperties", NSLOOKUPSYMBOLINIMAGE_OPTION_BIND)); } return dyfunc ? dyfunc(entry, properties, allocator, options) : KERN_FAILURE; } #define IORegistryEntryCreateCFProperties _IORegistryEntryCreateCFProperties static kern_return_t _IORegistryEntryGetPath(io_registry_entry_t entry, const io_name_t plane, io_string_t path) { static kern_return_t (*dyfunc)(io_registry_entry_t, const io_name_t, io_string_t) = NULL; if (!dyfunc) { void *image = __loadIOKit(); if (image) dyfunc = NSAddressOfSymbol(NSLookupSymbolInImage(image, "_IORegistryEntryGetPath", NSLOOKUPSYMBOLINIMAGE_OPTION_BIND)); } return dyfunc ? dyfunc(entry, plane, path) : KERN_FAILURE; } #define IORegistryEntryGetPath _IORegistryEntryGetPath static kern_return_t _IOServiceGetMatchingServices(mach_port_t masterPort, CFDictionaryRef matching, io_iterator_t *existing) { static kern_return_t (*dyfunc)(mach_port_t, CFDictionaryRef, io_iterator_t *) = NULL; if (!dyfunc) { void *image = __loadIOKit(); if (image) dyfunc = NSAddressOfSymbol(NSLookupSymbolInImage(image, "_IOServiceGetMatchingServices", NSLOOKUPSYMBOLINIMAGE_OPTION_BIND)); } return dyfunc ? dyfunc(masterPort, matching, existing) : KERN_FAILURE; } #define IOServiceGetMatchingServices _IOServiceGetMatchingServices static CFMutableDictionaryRef _IOServiceMatching(const char *name) { static CFMutableDictionaryRef (*dyfunc)(const char *) = NULL; if (!dyfunc) { void *image = __loadIOKit(); if (image) dyfunc = NSAddressOfSymbol(NSLookupSymbolInImage(image, "_IOServiceMatching", NSLOOKUPSYMBOLINIMAGE_OPTION_BIND)); } return dyfunc ? dyfunc(name) : NULL; } #define IOServiceMatching _IOServiceMatching typedef struct { /* base CFType information */ CFRuntimeBase cfBase; /* device name (e.g. "modem") */ CFStringRef name; int sock; } DeviceOnHoldPrivate, *DeviceOnHoldPrivateRef; static CFStringRef __DeviceOnHoldCopyDescription(CFTypeRef cf) { CFAllocatorRef allocator = CFGetAllocator(cf); CFMutableStringRef result; result = CFStringCreateMutable(allocator, 0); CFStringAppendFormat(result, NULL, CFSTR(" {\n"), cf, allocator); CFStringAppendFormat(result, NULL, CFSTR("}")); return result; } static void __DeviceOnHoldDeallocate(CFTypeRef cf) { DeviceOnHoldPrivateRef DeviceOnHoldPrivate = (DeviceOnHoldPrivateRef)cf; SCLog(_sc_verbose, LOG_DEBUG, CFSTR("__DeviceOnHoldDeallocate:")); /* release resources */ if (DeviceOnHoldPrivate->name) CFRelease(DeviceOnHoldPrivate->name); if (DeviceOnHoldPrivate->sock != -1) { } return; } static CFTypeID __kDeviceOnHoldTypeID = _kCFRuntimeNotATypeID; static const CFRuntimeClass __DeviceOnHoldClass = { 0, // version "DeviceOnHold", // className NULL, // init NULL, // copy __DeviceOnHoldDeallocate, // dealloc NULL, // equal NULL, // hash NULL, // copyFormattingDesc __DeviceOnHoldCopyDescription // copyDebugDesc }; static pthread_once_t initialized = PTHREAD_ONCE_INIT; static void __DeviceOnHoldInitialize(void) { __kDeviceOnHoldTypeID = _CFRuntimeRegisterClass(&__DeviceOnHoldClass); return; } DeviceOnHoldRef __DeviceOnHoldCreatePrivate(CFAllocatorRef allocator) { DeviceOnHoldPrivateRef devicePrivate; UInt32 size; /* initialize runtime */ pthread_once(&initialized, __DeviceOnHoldInitialize); /* allocate session */ size = sizeof(DeviceOnHoldPrivate) - sizeof(CFRuntimeBase); devicePrivate = (DeviceOnHoldPrivateRef)_CFRuntimeCreateInstance(allocator, __kDeviceOnHoldTypeID, size, NULL); if (!devicePrivate) { return NULL; } devicePrivate->name = NULL; devicePrivate->sock = -1; return (DeviceOnHoldRef)devicePrivate; } /* * TBD: We determine whether a device supports on hold capability by looking at * the numeric property DeviceSupportsHold (1 - yes, 0 or no property - no). For * the Apple Dash II internal modem we also use the property V92Modem to track * this same capability. */ Boolean IsDeviceOnHoldSupported(CFStringRef deviceName, // "modem" CFDictionaryRef options) { CFMutableDictionaryRef deviceToMatch; u_int32_t deviceSupportsHoldValue; kern_return_t kr; mach_port_t masterPort; io_iterator_t matchingServices; CFNumberRef num; CFMutableDictionaryRef properties; Boolean result = FALSE; io_service_t service; if (CFStringCompare(deviceName, CFSTR("modem"), NULL) == kCFCompareEqualTo) { kr = IOMasterPort(MACH_PORT_NULL, &masterPort); if (kr != KERN_SUCCESS) { goto errorExit; } deviceToMatch = IOServiceMatching("InternalModemSupport"); if (!deviceToMatch) { goto errorExit; } kr = IOServiceGetMatchingServices(masterPort, deviceToMatch, &matchingServices); if (kr != KERN_SUCCESS) { goto errorExit; } for ( ; service = IOIteratorNext(matchingServices) ; IOObjectRelease(service)) { io_string_t path; kr = IORegistryEntryGetPath(service, kIOServicePlane, path); assert( kr == KERN_SUCCESS ); // grab a copy of the properties kr = IORegistryEntryCreateCFProperties(service, &properties, kCFAllocatorDefault, kNilOptions); assert( kr == KERN_SUCCESS ); num = CFDictionaryGetValue(properties, CFSTR(kIODeviceSupportsHoldKey)); if (isA_CFNumber(num)) { CFNumberGetValue(num, kCFNumberSInt32Type, &deviceSupportsHoldValue); if (deviceSupportsHoldValue == 1) { result = TRUE; } } CFRelease(properties); } IOObjectRelease(matchingServices); } // Note: The issue for the general case is how to go from the SystemConfiguration // dynamic store to the actual driver. The devicesupportshold property is not // copied the either of the setup/state descriptions so the caller would need // to know the exact driver they are searching for. return result; errorExit: return FALSE; } DeviceOnHoldRef DeviceOnHoldCreate(CFAllocatorRef allocator, CFStringRef deviceName, // "modem" CFDictionaryRef options) { DeviceOnHoldRef device = NULL; DeviceOnHoldPrivateRef devicePrivate; int status; if (CFStringCompare(deviceName, CFSTR("modem"), NULL) != kCFCompareEqualTo) { return NULL; } device = __DeviceOnHoldCreatePrivate(allocator); if (!device) { return NULL; } devicePrivate = (DeviceOnHoldPrivateRef)device; status = MOHInit(&devicePrivate->sock, deviceName); if (status != 0) { CFRelease(device); return NULL; } devicePrivate->name = CFRetain(deviceName); return device; } int32_t DeviceOnHoldGetStatus(DeviceOnHoldRef device) { DeviceOnHoldPrivateRef devicePrivate = (DeviceOnHoldPrivateRef)device; int err; u_long link = 1; void *replyBuf; u_long replyBufLen; int32_t result = -1; if (!device) { return -1; } if (devicePrivate->sock == -1) { return -1; } err = MOHExec(devicePrivate->sock, link, MOH_SESSION_GET_STATUS, NULL, 0, &replyBuf, &replyBufLen); if (err != 0) { return -1; } if (replyBufLen == sizeof(result)) { result = *(int32_t *)replyBuf; } if (replyBuf) CFAllocatorDeallocate(NULL, replyBuf); return result; } Boolean DeviceOnHoldSuspend(DeviceOnHoldRef device) { DeviceOnHoldPrivateRef devicePrivate = (DeviceOnHoldPrivateRef)device; int err; u_long link = 1; void *replyBuf; u_long replyBufLen; Boolean result = FALSE; if (!device) { return FALSE; } if (devicePrivate->sock == -1) { return FALSE; } err = MOHExec(devicePrivate->sock, link, MOH_PUT_SESSION_ON_HOLD, NULL, 0, &replyBuf, &replyBufLen); if (err != 0) { return -1; } if (replyBufLen == sizeof(result)) { result = (*(int32_t *)replyBuf) ? TRUE : FALSE; } if (replyBuf) CFAllocatorDeallocate(NULL, replyBuf); return result; } Boolean DeviceOnHoldResume(DeviceOnHoldRef device) { DeviceOnHoldPrivateRef devicePrivate = (DeviceOnHoldPrivateRef)device; int err; u_long link = 1; void *replyBuf; u_long replyBufLen; Boolean result = FALSE; if (!device) { return FALSE; } if (devicePrivate->sock == -1) { return FALSE; } err = MOHExec(devicePrivate->sock, link, MOH_RESUME_SESSION_ON_HOLD,NULL, 0, &replyBuf, &replyBufLen); if (err != 0) { return -1; } if (replyBufLen == sizeof(result)) { result = (*(int32_t *)replyBuf) ? TRUE : FALSE; } if (replyBuf) CFAllocatorDeallocate(NULL, replyBuf); return result; }