1/*
2 * Copyright (c) 2002-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/*
25 * Modification History
26 *
27 * May 29, 2002		Roger Smith <rsmith@apple.com>
28 * - initial revision
29 */
30
31#include <sys/types.h>
32#include <mach/mach.h>
33#include <pthread.h>
34
35#include <CoreFoundation/CoreFoundation.h>
36#include <CoreFoundation/CFRuntime.h>
37
38#include <SystemConfiguration/SystemConfiguration.h>
39#include <SystemConfiguration/SCValidation.h>
40#include <SystemConfiguration/SCPrivate.h>
41
42#include <IOKit/IOKitLib.h>
43#include "dy_framework.h"
44
45#include "moh_msg.h"
46#include "moh.h"
47#include "DeviceOnHold.h"
48
49
50#define kIODeviceSupportsHoldKey	"V92Modem"
51
52
53typedef struct {
54
55	/* base CFType information */
56	CFRuntimeBase	cfBase;
57
58	/* device name (e.g. "modem") */
59	CFStringRef	name;
60
61	int		sock;
62
63} DeviceOnHoldPrivate, *DeviceOnHoldPrivateRef;
64
65
66static CFStringRef
67__DeviceOnHoldCopyDescription(CFTypeRef cf)
68{
69	CFAllocatorRef		allocator	= CFGetAllocator(cf);
70	CFMutableStringRef	result;
71
72	result = CFStringCreateMutable(allocator, 0);
73	CFStringAppendFormat(result, NULL, CFSTR("<DeviceOnHold %p [%p]> {\n"), cf, allocator);
74	CFStringAppendFormat(result, NULL, CFSTR("}"));
75
76	return result;
77}
78
79
80static void
81__DeviceOnHoldDeallocate(CFTypeRef cf)
82{
83	DeviceOnHoldPrivateRef	DeviceOnHoldPrivate	= (DeviceOnHoldPrivateRef)cf;
84
85	/* release resources */
86	if (DeviceOnHoldPrivate->name)		CFRelease(DeviceOnHoldPrivate->name);
87	if (DeviceOnHoldPrivate->sock != -1) {
88
89	}
90
91	return;
92}
93
94
95static CFTypeID __kDeviceOnHoldTypeID = _kCFRuntimeNotATypeID;
96
97
98static const CFRuntimeClass __DeviceOnHoldClass = {
99	0,				// version
100	"DeviceOnHold",			// className
101	NULL,				// init
102	NULL,				// copy
103	__DeviceOnHoldDeallocate,	// dealloc
104	NULL,				// equal
105	NULL,				// hash
106	NULL,				// copyFormattingDesc
107	__DeviceOnHoldCopyDescription	// copyDebugDesc
108};
109
110
111static pthread_once_t initialized	= PTHREAD_ONCE_INIT;
112
113static void
114__DeviceOnHoldInitialize(void)
115{
116	__kDeviceOnHoldTypeID = _CFRuntimeRegisterClass(&__DeviceOnHoldClass);
117	return;
118}
119
120
121static DeviceOnHoldPrivateRef
122__DeviceOnHoldCreatePrivate(CFAllocatorRef	allocator)
123{
124	DeviceOnHoldPrivateRef	devicePrivate;
125	uint32_t		size;
126
127	/* initialize runtime */
128	pthread_once(&initialized, __DeviceOnHoldInitialize);
129
130	/* allocate session */
131	size          = sizeof(DeviceOnHoldPrivate) - sizeof(CFRuntimeBase);
132	devicePrivate = (DeviceOnHoldPrivateRef)_CFRuntimeCreateInstance(allocator,
133									 __kDeviceOnHoldTypeID,
134									 size,
135									 NULL);
136	if (!devicePrivate) {
137		return NULL;
138	}
139
140	devicePrivate->name	= NULL;
141	devicePrivate->sock	= -1;
142
143	return devicePrivate;
144}
145
146
147CFTypeID
148DeviceOnHoldGetTypeID(void) {
149	pthread_once(&initialized, __DeviceOnHoldInitialize);	/* initialize runtime */
150	return __kDeviceOnHoldTypeID;
151}
152
153
154/*
155 * TBD: We determine whether a device supports on hold capability by looking at
156 * the numeric property DeviceSupportsHold (1 - yes, 0 or no property - no). For
157 * the Apple Dash II internal modem we also use the property V92Modem to track
158 * this same capability.
159 */
160
161Boolean
162IsDeviceOnHoldSupported(CFStringRef	deviceName,	// "modem"
163			CFDictionaryRef	options)
164{
165	CFMutableDictionaryRef	deviceToMatch;
166	uint32_t		deviceSupportsHoldValue;
167	kern_return_t		kr;
168	static mach_port_t	masterPort			= MACH_PORT_NULL;
169	io_iterator_t		matchingServices;
170	CFNumberRef		num;
171	CFMutableDictionaryRef	properties;
172	Boolean			result				= FALSE;
173	io_service_t 		service;
174
175	if (CFStringCompare(deviceName, CFSTR("modem"), 0) == kCFCompareEqualTo) {
176		if (masterPort == MACH_PORT_NULL) {
177			kr = IOMasterPort(MACH_PORT_NULL, &masterPort);
178			if (kr != KERN_SUCCESS) {
179				return FALSE;
180			}
181		}
182
183		deviceToMatch = IOServiceMatching("InternalModemSupport");
184		if (deviceToMatch == NULL) {
185			return FALSE;
186		}
187
188		kr = IOServiceGetMatchingServices(masterPort, deviceToMatch, &matchingServices);
189		if (kr != KERN_SUCCESS) {
190			return FALSE;
191		}
192
193		for ( ; (service = IOIteratorNext(matchingServices)) ; IOObjectRelease(service)) {
194			io_string_t	path;
195
196			kr = IORegistryEntryGetPath(service, kIOServicePlane, path);
197			assert( kr == KERN_SUCCESS );
198
199			// grab a copy of the properties
200			kr = IORegistryEntryCreateCFProperties(service, &properties, kCFAllocatorDefault, kNilOptions);
201			assert( kr == KERN_SUCCESS );
202
203			num = CFDictionaryGetValue(properties, CFSTR(kIODeviceSupportsHoldKey));
204			if (isA_CFNumber(num)) {
205				CFNumberGetValue(num, kCFNumberSInt32Type, &deviceSupportsHoldValue);
206				if (deviceSupportsHoldValue == 1) {
207					result = TRUE;
208				}
209			}
210
211			CFRelease(properties);
212		}
213
214		IOObjectRelease(matchingServices);
215	}
216
217	// Note: The issue for the general case is how to go from the SystemConfiguration
218	//       dynamic store to the actual driver. The devicesupportshold property is not
219	//       copied the either of the setup/state descriptions so the caller would need
220	//       to know the exact driver they are searching for.
221
222	return result;
223}
224
225
226DeviceOnHoldRef
227DeviceOnHoldCreate(CFAllocatorRef	allocator,
228		   CFStringRef		deviceName,	// "modem"
229		   CFDictionaryRef	options)
230{
231	DeviceOnHoldPrivateRef	devicePrivate;
232	int			status;
233
234	if (CFStringCompare(deviceName, CFSTR("modem"), 0) != kCFCompareEqualTo) {
235		return NULL;
236	}
237
238	devicePrivate = __DeviceOnHoldCreatePrivate(allocator);
239	if (!devicePrivate) {
240		return NULL;
241	}
242
243	status = MOHInit(&devicePrivate->sock, deviceName);
244	if (status != 0) {
245		CFRelease(devicePrivate);
246		return NULL;
247	}
248
249	devicePrivate->name = CFStringCreateCopy(NULL, deviceName);
250
251	return (DeviceOnHoldRef)devicePrivate;
252}
253
254
255
256int32_t
257DeviceOnHoldGetStatus(DeviceOnHoldRef	device)
258{
259	DeviceOnHoldPrivateRef	devicePrivate	= (DeviceOnHoldPrivateRef)device;
260	int			err;
261	uint32_t		link		= 1;
262	void			*replyBuf;
263	size_t			replyBufLen;
264	int32_t			result		= -1;
265
266	if (!device) {
267		return -1;
268	}
269
270	if (devicePrivate->sock == -1) {
271		return -1;
272	}
273
274	err = MOHExec(devicePrivate->sock,
275		      link,
276		      MOH_SESSION_GET_STATUS,
277		      NULL,
278		      0,
279		      &replyBuf,
280		      &replyBufLen);
281
282	if (err != 0) {
283		return -1;
284	}
285
286	if (replyBufLen == sizeof(result)) {
287		result = *(int32_t *)replyBuf;
288	}
289
290	if (replyBuf)	CFAllocatorDeallocate(NULL, replyBuf);
291	return result;
292}
293
294
295Boolean
296DeviceOnHoldSuspend(DeviceOnHoldRef	device)
297{
298	DeviceOnHoldPrivateRef	devicePrivate	= (DeviceOnHoldPrivateRef)device;
299	int			err;
300	uint32_t		link		= 1;
301	void			*replyBuf;
302	size_t			replyBufLen;
303	Boolean			result		= FALSE;
304
305	if (!device) {
306		return FALSE;
307	}
308
309	if (devicePrivate->sock == -1) {
310		return FALSE;
311	}
312
313	err = MOHExec(devicePrivate->sock,
314		      link,
315		      MOH_PUT_SESSION_ON_HOLD,
316		      NULL,
317		      0,
318		      &replyBuf,
319		      &replyBufLen);
320
321	if (err != 0) {
322		return -1;
323	}
324
325	if (replyBufLen == sizeof(result)) {
326		result = (*(int32_t *)replyBuf) ? TRUE : FALSE;
327	}
328
329	if (replyBuf)	CFAllocatorDeallocate(NULL, replyBuf);
330	return result;
331}
332
333
334Boolean
335DeviceOnHoldResume(DeviceOnHoldRef	device)
336{
337	DeviceOnHoldPrivateRef	devicePrivate	= (DeviceOnHoldPrivateRef)device;
338	int			err;
339	uint32_t		link		= 1;
340	void			*replyBuf;
341	size_t			replyBufLen;
342	Boolean			result		= FALSE;
343
344	if (!device) {
345		return FALSE;
346	}
347
348	if (devicePrivate->sock == -1) {
349		return FALSE;
350	}
351
352	err = MOHExec(devicePrivate->sock,
353		      link,
354		      MOH_RESUME_SESSION_ON_HOLD,NULL,
355		      0,
356		      &replyBuf,
357		      &replyBufLen);
358
359	if (err != 0) {
360		return -1;
361	}
362
363	if (replyBufLen == sizeof(result)) {
364		result = (*(int32_t *)replyBuf) ? TRUE : FALSE;
365	}
366
367	if (replyBuf)	CFAllocatorDeallocate(NULL, replyBuf);
368	return result;
369}
370