iokit.cpp revision a1360dcc0d6de125bf051b34a69e8d8cd268f82a
891a081e38584dfb59697349fbf0aba2c4d4e1e2Christian Maeder/* $Id$ */
891a081e38584dfb59697349fbf0aba2c4d4e1e2Christian Maeder/** @file
de6a40dbdd4712e5a9398b8519a59b1eaeab2f5aChristian Maeder * Main - Darwin IOKit Routines.
f18f9ae43d53e16e3324bed71ea67ba536c91042Christian Maeder *
97018cf5fa25b494adffd7e9b4e87320dae6bf47Christian Maeder * Because IOKit makes use of COM like interfaces, it does not mix very
0b7c8279c741857d1681160f8b4144a9430ffa7fTill Mossakowski * well with COM/XPCOM and must therefore be isolated from it using a
3f69b6948966979163bdfe8331c38833d5d90ecdChristian Maeder * simpler C interface.
891a081e38584dfb59697349fbf0aba2c4d4e1e2Christian Maeder */
891a081e38584dfb59697349fbf0aba2c4d4e1e2Christian Maeder
0b7c8279c741857d1681160f8b4144a9430ffa7fTill Mossakowski/*
f3a94a197960e548ecd6520bb768cb0d547457bbChristian Maeder * Copyright (C) 2006-2007 innotek GmbH
0b7c8279c741857d1681160f8b4144a9430ffa7fTill Mossakowski *
0b7c8279c741857d1681160f8b4144a9430ffa7fTill Mossakowski * This file is part of VirtualBox Open Source Edition (OSE), as
0b7c8279c741857d1681160f8b4144a9430ffa7fTill Mossakowski * available from http://www.virtualbox.org. This file is free software;
0b7c8279c741857d1681160f8b4144a9430ffa7fTill Mossakowski * you can redistribute it and/or modify it under the terms of the GNU
0b7c8279c741857d1681160f8b4144a9430ffa7fTill Mossakowski * General Public License as published by the Free Software Foundation,
0b7c8279c741857d1681160f8b4144a9430ffa7fTill Mossakowski * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
d1126d58419412635564085406d3779325b33ae0Till Mossakowski * distribution. VirtualBox OSE is distributed in the hope that it will
7660d5932a1fb9677d07889714b677a059af2b2fChristian Maeder * be useful, but WITHOUT ANY WARRANTY of any kind.
ad270004874ce1d0697fb30d7309f180553bb315Christian Maeder *
ad270004874ce1d0697fb30d7309f180553bb315Christian Maeder * If you received this file as part of a commercial VirtualBox
0b7c8279c741857d1681160f8b4144a9430ffa7fTill Mossakowski * distribution, then only the terms of your commercial VirtualBox
0b7c8279c741857d1681160f8b4144a9430ffa7fTill Mossakowski * license agreement apply instead of the previous paragraph.
7c2d602a73afe304ac0ca225ecff42b2ae8bdab3Christian Maeder */
2dfb9a95c5586f73abda1d0f369d6d154b159452Sonja Gröning
80664cc18425d67cd71be80f27f882fa16e43848Christian Maeder
26d11a256b1433604a3dbc69913b520fff7586acChristian Maeder/*******************************************************************************
78d4b9e3558a2523c3335b1399385ac7d246f0c1Christian Maeder* Header Files *
0b7c8279c741857d1681160f8b4144a9430ffa7fTill Mossakowski*******************************************************************************/
0b7c8279c741857d1681160f8b4144a9430ffa7fTill Mossakowski#define LOG_GROUP LOG_GROUP_MAIN
0b7c8279c741857d1681160f8b4144a9430ffa7fTill Mossakowski
81946e2b3f6dde6167f48769bd02c7a634736856Christian Maeder#include <mach/mach.h>
0b7c8279c741857d1681160f8b4144a9430ffa7fTill Mossakowski#include <Carbon/Carbon.h>
78d4b9e3558a2523c3335b1399385ac7d246f0c1Christian Maeder#include <IOKit/IOKitLib.h>
2dfb9a95c5586f73abda1d0f369d6d154b159452Sonja Gröning#include <IOKit/storage/IOStorageDeviceCharacteristics.h>
0b7c8279c741857d1681160f8b4144a9430ffa7fTill Mossakowski#include <IOKit/scsi-commands/SCSITaskLib.h>
891a081e38584dfb59697349fbf0aba2c4d4e1e2Christian Maeder#include <mach/mach_error.h>
0b7c8279c741857d1681160f8b4144a9430ffa7fTill Mossakowski#ifdef VBOX_WITH_USB
0b2340d1ef7d0998e4b0c321021be942c6b67652Christian Maeder# include <IOKit/usb/IOUSBLib.h>
0b7c8279c741857d1681160f8b4144a9430ffa7fTill Mossakowski# include <IOKit/IOCFPlugIn.h>
0b2340d1ef7d0998e4b0c321021be942c6b67652Christian Maeder#endif
0b7c8279c741857d1681160f8b4144a9430ffa7fTill Mossakowski
78d4b9e3558a2523c3335b1399385ac7d246f0c1Christian Maeder#include <VBox/log.h>
986d3f255182539098a97ac86da9eeee5b7a72e3Christian Maeder#include <VBox/err.h>
986d3f255182539098a97ac86da9eeee5b7a72e3Christian Maeder#include <iprt/mem.h>
986d3f255182539098a97ac86da9eeee5b7a72e3Christian Maeder#include <iprt/string.h>
986d3f255182539098a97ac86da9eeee5b7a72e3Christian Maeder#include <iprt/process.h>
986d3f255182539098a97ac86da9eeee5b7a72e3Christian Maeder#include <iprt/assert.h>
7c2d602a73afe304ac0ca225ecff42b2ae8bdab3Christian Maeder#include <iprt/thread.h>
e7ce154edb906685b3fa7f6c0a764e18a4658068Christian Maeder
78d4b9e3558a2523c3335b1399385ac7d246f0c1Christian Maeder#include "iokit.h"
78d4b9e3558a2523c3335b1399385ac7d246f0c1Christian Maeder
b1f59a4ea7c96f4c03a4d7cfcb9c5e66871cfbbbChristian Maeder
78d4b9e3558a2523c3335b1399385ac7d246f0c1Christian Maeder/*******************************************************************************
f4255fa60f8dcaa8f21ce60b2820c228a19e39aeChristian Maeder* Defined Constants And Macros *
f4255fa60f8dcaa8f21ce60b2820c228a19e39aeChristian Maeder*******************************************************************************/
f4255fa60f8dcaa8f21ce60b2820c228a19e39aeChristian Maeder/** An attempt at catching reference leaks. */
5c358300e78157f4bfaf5415c70e1096a9205b61Christian Maeder#define MY_CHECK_CREFS(cRefs) do { AssertMsg(cRefs < 25, ("%ld\n", cRefs)); NOREF(cRefs); } while (0)
5c358300e78157f4bfaf5415c70e1096a9205b61Christian Maeder
5c358300e78157f4bfaf5415c70e1096a9205b61Christian Maeder/** Contains the pid of the current client. If 0, the kernel is the current client. */
5c358300e78157f4bfaf5415c70e1096a9205b61Christian Maeder#define VBOXUSB_CLIENT_KEY "VBoxUSB-Client"
5c358300e78157f4bfaf5415c70e1096a9205b61Christian Maeder/** Contains the pid of the filter owner (i.e. the VBoxSVC pid). */
5c358300e78157f4bfaf5415c70e1096a9205b61Christian Maeder#define VBOXUSB_OWNER_KEY "VBoxUSB-Owner"
5c358300e78157f4bfaf5415c70e1096a9205b61Christian Maeder/** The VBoxUSBDevice class name. */
5c358300e78157f4bfaf5415c70e1096a9205b61Christian Maeder#define VBOXUSBDEVICE_CLASS_NAME "org_virtualbox_VBoxUSBDevice"
5c358300e78157f4bfaf5415c70e1096a9205b61Christian Maeder
5c358300e78157f4bfaf5415c70e1096a9205b61Christian Maeder
578b677874296e4ba48e57b5e4b4b0270d995603Christian Maeder/*******************************************************************************
578b677874296e4ba48e57b5e4b4b0270d995603Christian Maeder* Global Variables *
88ece6e49930670e8fd3ee79c89a2e918d2fbd0cChristian Maeder*******************************************************************************/
578b677874296e4ba48e57b5e4b4b0270d995603Christian Maeder/** The IO Master Port. */
f2c050360525df494e6115073b0edc4c443a847cMihai Codescustatic mach_port_t g_MasterPort = NULL;
f2c050360525df494e6115073b0edc4c443a847cMihai Codescu
78d4b9e3558a2523c3335b1399385ac7d246f0c1Christian Maeder
26d11a256b1433604a3dbc69913b520fff7586acChristian Maeder/**
7c2d602a73afe304ac0ca225ecff42b2ae8bdab3Christian Maeder * Lazily opens the master port.
26d11a256b1433604a3dbc69913b520fff7586acChristian Maeder *
d48085f765fca838c1d972d2123601997174583dChristian Maeder * @returns true if the port is open, false on failure (very unlikely).
26d11a256b1433604a3dbc69913b520fff7586acChristian Maeder */
7c2d602a73afe304ac0ca225ecff42b2ae8bdab3Christian Maederstatic bool darwinOpenMasterPort(void)
7c2d602a73afe304ac0ca225ecff42b2ae8bdab3Christian Maeder{
975642b989852fc24119c59cf40bc1af653608ffChristian Maeder if (!g_MasterPort)
d48085f765fca838c1d972d2123601997174583dChristian Maeder {
d48085f765fca838c1d972d2123601997174583dChristian Maeder kern_return_t krc = IOMasterPort(MACH_PORT_NULL, &g_MasterPort);
7c2d602a73afe304ac0ca225ecff42b2ae8bdab3Christian Maeder AssertReturn(krc == KERN_SUCCESS, false);
d48085f765fca838c1d972d2123601997174583dChristian Maeder }
78d4b9e3558a2523c3335b1399385ac7d246f0c1Christian Maeder return true;
26d11a256b1433604a3dbc69913b520fff7586acChristian Maeder}
7c2d602a73afe304ac0ca225ecff42b2ae8bdab3Christian Maeder
26d11a256b1433604a3dbc69913b520fff7586acChristian Maeder
a39a820684c1974350f46593025e0bb279f41bc6Christian Maeder#ifdef VBOX_WITH_USB
a39a820684c1974350f46593025e0bb279f41bc6Christian Maeder
a39a820684c1974350f46593025e0bb279f41bc6Christian Maeder/**
a39a820684c1974350f46593025e0bb279f41bc6Christian Maeder * Gets an unsigned 8-bit integer value.
78d4b9e3558a2523c3335b1399385ac7d246f0c1Christian Maeder *
f4255fa60f8dcaa8f21ce60b2820c228a19e39aeChristian Maeder * @returns Success indicator (true/false).
26d11a256b1433604a3dbc69913b520fff7586acChristian Maeder * @param DictRef The dictionary.
7c2d602a73afe304ac0ca225ecff42b2ae8bdab3Christian Maeder * @param KeyStrRef The key name.
975642b989852fc24119c59cf40bc1af653608ffChristian Maeder * @param pu8 Where to store the key value.
975642b989852fc24119c59cf40bc1af653608ffChristian Maeder */
7c2d602a73afe304ac0ca225ecff42b2ae8bdab3Christian Maederstatic bool darwinDictGetU8(CFMutableDictionaryRef DictRef, CFStringRef KeyStrRef, uint8_t *pu8)
975642b989852fc24119c59cf40bc1af653608ffChristian Maeder{
975642b989852fc24119c59cf40bc1af653608ffChristian Maeder CFTypeRef ValRef = CFDictionaryGetValue(DictRef, KeyStrRef);
26d11a256b1433604a3dbc69913b520fff7586acChristian Maeder if (ValRef)
26d11a256b1433604a3dbc69913b520fff7586acChristian Maeder {
975642b989852fc24119c59cf40bc1af653608ffChristian Maeder if (CFNumberGetValue((CFNumberRef)ValRef, kCFNumberSInt8Type, pu8))
7c2d602a73afe304ac0ca225ecff42b2ae8bdab3Christian Maeder return true;
975642b989852fc24119c59cf40bc1af653608ffChristian Maeder }
975642b989852fc24119c59cf40bc1af653608ffChristian Maeder *pu8 = 0;
26d11a256b1433604a3dbc69913b520fff7586acChristian Maeder return false;
26d11a256b1433604a3dbc69913b520fff7586acChristian Maeder}
7c2d602a73afe304ac0ca225ecff42b2ae8bdab3Christian Maeder
975642b989852fc24119c59cf40bc1af653608ffChristian Maeder
26d11a256b1433604a3dbc69913b520fff7586acChristian Maeder/**
7c2d602a73afe304ac0ca225ecff42b2ae8bdab3Christian Maeder * Gets an unsigned 16-bit integer value.
7c2d602a73afe304ac0ca225ecff42b2ae8bdab3Christian Maeder *
26d11a256b1433604a3dbc69913b520fff7586acChristian Maeder * @returns Success indicator (true/false).
26d11a256b1433604a3dbc69913b520fff7586acChristian Maeder * @param DictRef The dictionary.
26d11a256b1433604a3dbc69913b520fff7586acChristian Maeder * @param KeyStrRef The key name.
975642b989852fc24119c59cf40bc1af653608ffChristian Maeder * @param pu16 Where to store the key value.
26d11a256b1433604a3dbc69913b520fff7586acChristian Maeder */
7c2d602a73afe304ac0ca225ecff42b2ae8bdab3Christian Maederstatic bool darwinDictGetU16(CFMutableDictionaryRef DictRef, CFStringRef KeyStrRef, uint16_t *pu16)
ee34c4e1b244e46682a84bc52224f52289bd1950Christian Maeder{
26d11a256b1433604a3dbc69913b520fff7586acChristian Maeder CFTypeRef ValRef = CFDictionaryGetValue(DictRef, KeyStrRef);
7c2d602a73afe304ac0ca225ecff42b2ae8bdab3Christian Maeder if (ValRef)
ad187062b0009820118c1b773a232e29b879a2faChristian Maeder {
f454c20b6c126bea7d31d400cc8824b9ee8cc6eaChristian Maeder if (CFNumberGetValue((CFNumberRef)ValRef, kCFNumberSInt16Type, pu16))
7c2d602a73afe304ac0ca225ecff42b2ae8bdab3Christian Maeder return true;
975642b989852fc24119c59cf40bc1af653608ffChristian Maeder }
7c2d602a73afe304ac0ca225ecff42b2ae8bdab3Christian Maeder *pu16 = 0;
7c2d602a73afe304ac0ca225ecff42b2ae8bdab3Christian Maeder return false;
f4255fa60f8dcaa8f21ce60b2820c228a19e39aeChristian Maeder}
26d11a256b1433604a3dbc69913b520fff7586acChristian Maeder
26d11a256b1433604a3dbc69913b520fff7586acChristian Maeder
975642b989852fc24119c59cf40bc1af653608ffChristian Maeder/**
8731f7b93b26083dc34a2c0937cd6493b42f2c2cTill Mossakowski * Gets an unsigned 32-bit integer value.
78d4b9e3558a2523c3335b1399385ac7d246f0c1Christian Maeder *
7c2d602a73afe304ac0ca225ecff42b2ae8bdab3Christian Maeder * @returns Success indicator (true/false).
7c2d602a73afe304ac0ca225ecff42b2ae8bdab3Christian Maeder * @param DictRef The dictionary.
26d11a256b1433604a3dbc69913b520fff7586acChristian Maeder * @param KeyStrRef The key name.
abe0293c13ccb9c7c864e780181f370155de5658Christian Maeder * @param pu32 Where to store the key value.
ee34c4e1b244e46682a84bc52224f52289bd1950Christian Maeder */
ee34c4e1b244e46682a84bc52224f52289bd1950Christian Maederstatic bool darwinDictGetU32(CFMutableDictionaryRef DictRef, CFStringRef KeyStrRef, uint32_t *pu32)
7c2d602a73afe304ac0ca225ecff42b2ae8bdab3Christian Maeder{
ee34c4e1b244e46682a84bc52224f52289bd1950Christian Maeder CFTypeRef ValRef = CFDictionaryGetValue(DictRef, KeyStrRef);
abe0293c13ccb9c7c864e780181f370155de5658Christian Maeder if (ValRef)
ee34c4e1b244e46682a84bc52224f52289bd1950Christian Maeder {
7c2d602a73afe304ac0ca225ecff42b2ae8bdab3Christian Maeder if (CFNumberGetValue((CFNumberRef)ValRef, kCFNumberSInt32Type, pu32))
975642b989852fc24119c59cf40bc1af653608ffChristian Maeder return true;
abe0293c13ccb9c7c864e780181f370155de5658Christian Maeder }
78d4b9e3558a2523c3335b1399385ac7d246f0c1Christian Maeder *pu32 = 0;
26d11a256b1433604a3dbc69913b520fff7586acChristian Maeder return false;
26d11a256b1433604a3dbc69913b520fff7586acChristian Maeder}
26d11a256b1433604a3dbc69913b520fff7586acChristian Maeder
26d11a256b1433604a3dbc69913b520fff7586acChristian Maeder
26d11a256b1433604a3dbc69913b520fff7586acChristian Maeder/**
26d11a256b1433604a3dbc69913b520fff7586acChristian Maeder * Gets an unsigned 64-bit integer value.
26d11a256b1433604a3dbc69913b520fff7586acChristian Maeder *
26d11a256b1433604a3dbc69913b520fff7586acChristian Maeder * @returns Success indicator (true/false).
78d4b9e3558a2523c3335b1399385ac7d246f0c1Christian Maeder * @param DictRef The dictionary.
7c2d602a73afe304ac0ca225ecff42b2ae8bdab3Christian Maeder * @param KeyStrRef The key name.
78d4b9e3558a2523c3335b1399385ac7d246f0c1Christian Maeder * @param pu64 Where to store the key value.
78d4b9e3558a2523c3335b1399385ac7d246f0c1Christian Maeder */
78d4b9e3558a2523c3335b1399385ac7d246f0c1Christian Maederstatic bool darwinDictGetU64(CFMutableDictionaryRef DictRef, CFStringRef KeyStrRef, uint64_t *pu64)
78d4b9e3558a2523c3335b1399385ac7d246f0c1Christian Maeder{
7c2d602a73afe304ac0ca225ecff42b2ae8bdab3Christian Maeder CFTypeRef ValRef = CFDictionaryGetValue(DictRef, KeyStrRef);
7c2d602a73afe304ac0ca225ecff42b2ae8bdab3Christian Maeder if (ValRef)
ee34c4e1b244e46682a84bc52224f52289bd1950Christian Maeder {
78d4b9e3558a2523c3335b1399385ac7d246f0c1Christian Maeder if (CFNumberGetValue((CFNumberRef)ValRef, kCFNumberSInt64Type, pu64))
26d11a256b1433604a3dbc69913b520fff7586acChristian Maeder return true;
7c2d602a73afe304ac0ca225ecff42b2ae8bdab3Christian Maeder }
7c2d602a73afe304ac0ca225ecff42b2ae8bdab3Christian Maeder *pu64 = 0;
10f1342d686ed68712a2b25ed65fa5a18f9c3db7Christian Maeder return false;
10f1342d686ed68712a2b25ed65fa5a18f9c3db7Christian Maeder}
f7d2e793728bbb7fd185e027eb9dfd7b9dd11c21Christian Maeder
10f1342d686ed68712a2b25ed65fa5a18f9c3db7Christian Maeder
f7d2e793728bbb7fd185e027eb9dfd7b9dd11c21Christian Maeder/**
f7d2e793728bbb7fd185e027eb9dfd7b9dd11c21Christian Maeder * Gets a RTPROCESS value.
e9490701e16d1e8abd995ef876d6f937da93b412Christian Maeder *
e9490701e16d1e8abd995ef876d6f937da93b412Christian Maeder * @returns Success indicator (true/false).
e9490701e16d1e8abd995ef876d6f937da93b412Christian Maeder * @param DictRef The dictionary.
e9490701e16d1e8abd995ef876d6f937da93b412Christian Maeder * @param KeyStrRef The key name.
27e8de893356be63440b1b8aa2c4f19fbaf399acChristian Maeder * @param pProcess Where to store the key value.
e9490701e16d1e8abd995ef876d6f937da93b412Christian Maeder */
e9490701e16d1e8abd995ef876d6f937da93b412Christian Maederstatic bool darwinDictGetProccess(CFMutableDictionaryRef DictRef, CFStringRef KeyStrRef, PRTPROCESS pProcess)
e9490701e16d1e8abd995ef876d6f937da93b412Christian Maeder{
e9490701e16d1e8abd995ef876d6f937da93b412Christian Maeder switch (sizeof(*pProcess))
e9490701e16d1e8abd995ef876d6f937da93b412Christian Maeder {
e9490701e16d1e8abd995ef876d6f937da93b412Christian Maeder case sizeof(uint16_t): return darwinDictGetU16(DictRef, KeyStrRef, (uint16_t *)pProcess);
e9490701e16d1e8abd995ef876d6f937da93b412Christian Maeder case sizeof(uint32_t): return darwinDictGetU32(DictRef, KeyStrRef, (uint32_t *)pProcess);
10f1342d686ed68712a2b25ed65fa5a18f9c3db7Christian Maeder case sizeof(uint64_t): return darwinDictGetU64(DictRef, KeyStrRef, (uint64_t *)pProcess);
e7ce154edb906685b3fa7f6c0a764e18a4658068Christian Maeder default:
26d11a256b1433604a3dbc69913b520fff7586acChristian Maeder AssertMsgFailedReturn(("%d\n", sizeof(*pProcess)), false);
7c2d602a73afe304ac0ca225ecff42b2ae8bdab3Christian Maeder }
78d4b9e3558a2523c3335b1399385ac7d246f0c1Christian Maeder}
7c2d602a73afe304ac0ca225ecff42b2ae8bdab3Christian Maeder
ee34c4e1b244e46682a84bc52224f52289bd1950Christian Maeder
7c2d602a73afe304ac0ca225ecff42b2ae8bdab3Christian Maeder/**
78d4b9e3558a2523c3335b1399385ac7d246f0c1Christian Maeder * Gets string value, converted to UTF-8 and put in a IPRT string buffer.
ee34c4e1b244e46682a84bc52224f52289bd1950Christian Maeder *
ee34c4e1b244e46682a84bc52224f52289bd1950Christian Maeder * @returns Success indicator (true/false).
7c2d602a73afe304ac0ca225ecff42b2ae8bdab3Christian Maeder * @param DictRef The dictionary.
78d4b9e3558a2523c3335b1399385ac7d246f0c1Christian Maeder * @param KeyStrRef The key name.
7c2d602a73afe304ac0ca225ecff42b2ae8bdab3Christian Maeder * @param ppsz Where to store the key value. Free with RTStrFree. Set to NULL on failure.
ee34c4e1b244e46682a84bc52224f52289bd1950Christian Maeder */
78d4b9e3558a2523c3335b1399385ac7d246f0c1Christian Maederstatic bool darwinDictGetString(CFMutableDictionaryRef DictRef, CFStringRef KeyStrRef, char **ppsz)
ee34c4e1b244e46682a84bc52224f52289bd1950Christian Maeder{
7c2d602a73afe304ac0ca225ecff42b2ae8bdab3Christian Maeder CFTypeRef ValRef = CFDictionaryGetValue(DictRef, KeyStrRef);
ee34c4e1b244e46682a84bc52224f52289bd1950Christian Maeder if (ValRef)
ee34c4e1b244e46682a84bc52224f52289bd1950Christian Maeder {
7c2d602a73afe304ac0ca225ecff42b2ae8bdab3Christian Maeder char szBuf[512];
ee34c4e1b244e46682a84bc52224f52289bd1950Christian Maeder if (CFStringGetCString((CFStringRef)ValRef, szBuf, sizeof(szBuf), kCFStringEncodingUTF8))
e9490701e16d1e8abd995ef876d6f937da93b412Christian Maeder {
78d4b9e3558a2523c3335b1399385ac7d246f0c1Christian Maeder *ppsz = RTStrDup(RTStrStrip(szBuf));
78d4b9e3558a2523c3335b1399385ac7d246f0c1Christian Maeder if (*ppsz)
7c2d602a73afe304ac0ca225ecff42b2ae8bdab3Christian Maeder return true;
e9490701e16d1e8abd995ef876d6f937da93b412Christian Maeder }
7c2d602a73afe304ac0ca225ecff42b2ae8bdab3Christian Maeder }
e9490701e16d1e8abd995ef876d6f937da93b412Christian Maeder *ppsz = NULL;
d48085f765fca838c1d972d2123601997174583dChristian Maeder return false;
a39a820684c1974350f46593025e0bb279f41bc6Christian Maeder}
a39a820684c1974350f46593025e0bb279f41bc6Christian Maeder
adfdcfa67b7f12df6df7292e238c3f9a4b637980Christian Maeder
6f5bf1e81c4669f7c605e13548861a1207b0c7e8Christian Maeder#if 1 /* dumping disabled */
6f5bf1e81c4669f7c605e13548861a1207b0c7e8Christian Maeder# define DARWIN_IOKIT_LOG(a) Log(a)
6f5bf1e81c4669f7c605e13548861a1207b0c7e8Christian Maeder# define DARWIN_IOKIT_LOG_FLUSH() do {} while (0)
6f5bf1e81c4669f7c605e13548861a1207b0c7e8Christian Maeder# define DARWIN_IOKIT_DUMP_OBJ(o) do {} while (0)
6f5bf1e81c4669f7c605e13548861a1207b0c7e8Christian Maeder#else
e9490701e16d1e8abd995ef876d6f937da93b412Christian Maeder# if 0
e9490701e16d1e8abd995ef876d6f937da93b412Christian Maeder# include <iprt/stream.h>
78d4b9e3558a2523c3335b1399385ac7d246f0c1Christian Maeder# define DARWIN_IOKIT_LOG(a) RTPrintf a
78d4b9e3558a2523c3335b1399385ac7d246f0c1Christian Maeder# define DARWIN_IOKIT_LOG_FLUSH() RTStrmFlush(g_pStdOut)
78d4b9e3558a2523c3335b1399385ac7d246f0c1Christian Maeder# else
78d4b9e3558a2523c3335b1399385ac7d246f0c1Christian Maeder# define DARWIN_IOKIT_LOG(a) RTLogPrintf a
7c2d602a73afe304ac0ca225ecff42b2ae8bdab3Christian Maeder# define DARWIN_IOKIT_LOG(a) RTLogFlush()
7c2d602a73afe304ac0ca225ecff42b2ae8bdab3Christian Maeder# endif
7c2d602a73afe304ac0ca225ecff42b2ae8bdab3Christian Maeder# define DARWIN_IOKIT_DUMP_OBJ(o) darwinDumpObj(o)
d48085f765fca838c1d972d2123601997174583dChristian Maeder
7c2d602a73afe304ac0ca225ecff42b2ae8bdab3Christian Maeder/**
d48085f765fca838c1d972d2123601997174583dChristian Maeder * Callback for dumping a dictionary key.
ee34c4e1b244e46682a84bc52224f52289bd1950Christian Maeder *
7c2d602a73afe304ac0ca225ecff42b2ae8bdab3Christian Maeder * @param pvKey The key name.
d48085f765fca838c1d972d2123601997174583dChristian Maeder * @param pvValue The key value
d48085f765fca838c1d972d2123601997174583dChristian Maeder * @param pvUser The recursion depth.
78d4b9e3558a2523c3335b1399385ac7d246f0c1Christian Maeder */
26d11a256b1433604a3dbc69913b520fff7586acChristian Maederstatic void darwinDumpDictCallback(const void *pvKey, const void *pvValue, void *pvUser)
78d4b9e3558a2523c3335b1399385ac7d246f0c1Christian Maeder{
7c2d602a73afe304ac0ca225ecff42b2ae8bdab3Christian Maeder /* display the key name. */
ee34c4e1b244e46682a84bc52224f52289bd1950Christian Maeder char *pszKey = (char *)RTMemTmpAlloc(1024);
03fc25805985563b679dd75c31a6e05287c1632eChristian Maeder if (!CFStringGetCString((CFStringRef)pvKey, pszKey, 1024, kCFStringEncodingUTF8))
6f5bf1e81c4669f7c605e13548861a1207b0c7e8Christian Maeder strcpy(pszKey, "CFStringGetCString failure");
6f5bf1e81c4669f7c605e13548861a1207b0c7e8Christian Maeder DARWIN_IOKIT_LOG(("%+*s%s", (int)(uintptr_t)pvUser, "", pszKey));
6f5bf1e81c4669f7c605e13548861a1207b0c7e8Christian Maeder RTMemTmpFree(pszKey);
6f5bf1e81c4669f7c605e13548861a1207b0c7e8Christian Maeder
6f5bf1e81c4669f7c605e13548861a1207b0c7e8Christian Maeder /* display the value type */
6f5bf1e81c4669f7c605e13548861a1207b0c7e8Christian Maeder CFTypeID Type = CFGetTypeID(pvValue);
6f5bf1e81c4669f7c605e13548861a1207b0c7e8Christian Maeder DARWIN_IOKIT_LOG((" [%d-", Type));
7c2d602a73afe304ac0ca225ecff42b2ae8bdab3Christian Maeder
ee34c4e1b244e46682a84bc52224f52289bd1950Christian Maeder /* display the value */
78d4b9e3558a2523c3335b1399385ac7d246f0c1Christian Maeder if (Type == CFDictionaryGetTypeID())
e9490701e16d1e8abd995ef876d6f937da93b412Christian Maeder {
ee34c4e1b244e46682a84bc52224f52289bd1950Christian Maeder DARWIN_IOKIT_LOG(("dictionary] =\n"
78d4b9e3558a2523c3335b1399385ac7d246f0c1Christian Maeder "%-*s{\n", (int)(uintptr_t)pvUser, ""));
e9490701e16d1e8abd995ef876d6f937da93b412Christian Maeder CFDictionaryApplyFunction((CFDictionaryRef)pvValue, darwinDumpDictCallback, (void *)((uintptr_t)pvUser + 4));
e9490701e16d1e8abd995ef876d6f937da93b412Christian Maeder DARWIN_IOKIT_LOG(("%-*s}\n", (int)(uintptr_t)pvUser, ""));
6f485a7c411a3a411673a43aadd6293975f1b029Till Mossakowski }
else if (Type == CFNumberGetTypeID())
{
union
{
SInt8 s8;
SInt16 s16;
SInt32 s32;
SInt64 s64;
Float32 rf32;
Float64 rd64;
char ch;
short s;
int i;
long l;
long long ll;
float rf;
double rd;
CFIndex iCF;
} u;
memset(&u, 0, sizeof(u));
CFNumberType NumType = CFNumberGetType((CFNumberRef)pvValue);
if (CFNumberGetValue((CFNumberRef)pvValue, NumType, &u))
{
switch (CFNumberGetType((CFNumberRef)pvValue))
{
case kCFNumberSInt8Type: DARWIN_IOKIT_LOG(("SInt8] = %RI8 (%#RX8)\n", NumType, u.s8, u.s8)); break;
case kCFNumberSInt16Type: DARWIN_IOKIT_LOG(("SInt16] = %RI16 (%#RX16)\n", NumType, u.s16, u.s16)); break;
case kCFNumberSInt32Type: DARWIN_IOKIT_LOG(("SInt32] = %RI32 (%#RX32)\n", NumType, u.s32, u.s32)); break;
case kCFNumberSInt64Type: DARWIN_IOKIT_LOG(("SInt64] = %RI64 (%#RX64)\n", NumType, u.s64, u.s64)); break;
case kCFNumberFloat32Type: DARWIN_IOKIT_LOG(("float32] = %#lx\n", NumType, u.l)); break;
case kCFNumberFloat64Type: DARWIN_IOKIT_LOG(("float64] = %#llx\n", NumType, u.ll)); break;
case kCFNumberFloatType: DARWIN_IOKIT_LOG(("float] = %#lx\n", NumType, u.l)); break;
case kCFNumberDoubleType: DARWIN_IOKIT_LOG(("double] = %#llx\n", NumType, u.ll)); break;
case kCFNumberCharType: DARWIN_IOKIT_LOG(("char] = %hhd (%hhx)\n", NumType, u.ch, u.ch)); break;
case kCFNumberShortType: DARWIN_IOKIT_LOG(("short] = %hd (%hx)\n", NumType, u.s, u.s)); break;
case kCFNumberIntType: DARWIN_IOKIT_LOG(("int] = %d (%#x)\n", NumType, u.i, u.i)); break;
case kCFNumberLongType: DARWIN_IOKIT_LOG(("long] = %ld (%#lx)\n", NumType, u.l, u.l)); break;
case kCFNumberLongLongType: DARWIN_IOKIT_LOG(("long long] = %lld (%#llx)\n", NumType, u.ll, u.ll)); break;
case kCFNumberCFIndexType: DARWIN_IOKIT_LOG(("CFIndex] = %lld (%#llx)\n", NumType, (long long)u.iCF, (long long)u.iCF)); break;
break;
default: DARWIN_IOKIT_LOG(("%d?] = %lld (%llx)\n", NumType, u.ll, u.ll)); break;
}
}
else
DARWIN_IOKIT_LOG(("number] = CFNumberGetValue failed\n"));
}
else if (Type == CFBooleanGetTypeID())
DARWIN_IOKIT_LOG(("boolean] = %RTbool\n", CFBooleanGetValue((CFBooleanRef)pvValue)));
else if (Type == CFStringGetTypeID())
{
DARWIN_IOKIT_LOG(("string] = "));
char *pszValue = (char *)RTMemTmpAlloc(16*_1K);
if (!CFStringGetCString((CFStringRef)pvValue, pszValue, 16*_1K, kCFStringEncodingUTF8))
strcpy(pszValue, "CFStringGetCString failure");
DARWIN_IOKIT_LOG(("\"%s\"\n", pszValue));
RTMemTmpFree(pszValue);
}
else
DARWIN_IOKIT_LOG(("??] = %p\n", pvValue));
}
/**
* Dumps a dictionary to the log.
*
* @param DictRef The dictionary to dump.
*/
static void darwinDumpDict(CFMutableDictionaryRef DictRef, unsigned cIndents)
{
CFDictionaryApplyFunction(DictRef, darwinDumpDictCallback, (void *)(uintptr_t)cIndents);
DARWIN_IOKIT_LOG_FLUSH();
}
/**
* Dumps an I/O kit registry object and all it children.
* @param Object The object to dump.
* @param cIndents The number of indents to use.
*/
static void darwinDumpObjInt(io_object_t Object, unsigned cIndents)
{
static io_string_t s_szPath;
kern_return_t krc = IORegistryEntryGetPath(Object, kIOServicePlane, s_szPath);
if (krc != KERN_SUCCESS)
strcpy(s_szPath, "IORegistryEntryGetPath failed");
DARWIN_IOKIT_LOG(("Dumping %p - %s:\n", (const void *)Object, s_szPath));
CFMutableDictionaryRef PropsRef = 0;
krc = IORegistryEntryCreateCFProperties(Object, &PropsRef, kCFAllocatorDefault, kNilOptions);
if (krc == KERN_SUCCESS)
{
darwinDumpDict(PropsRef, cIndents + 4);
CFRelease(PropsRef);
}
/*
* Children.
*/
io_iterator_t Children;
krc = IORegistryEntryGetChildIterator(Object, kIOServicePlane, &Children);
if (krc == KERN_SUCCESS)
{
io_object_t Child;
while ((Child = IOIteratorNext(Children)))
{
darwinDumpObjInt(Child, cIndents + 4);
IOObjectRelease(Child);
}
IOObjectRelease(Children);
}
else
DARWIN_IOKIT_LOG(("IORegistryEntryGetChildIterator -> %#x\n", krc));
}
/**
* Dumps an I/O kit registry object and all it children.
* @param Object The object to dump.
*/
static void darwinDumpObj(io_object_t Object)
{
darwinDumpObjInt(Object, 0);
}
#endif
/**
* Notification data created by DarwinSubscribeUSBNotifications, used by
* the callbacks and finally freed by DarwinUnsubscribeUSBNotifications.
*/
typedef struct DARWINUSBNOTIFY
{
/** The notification port.
* It's shared between the notification callbacks. */
IONotificationPortRef NotifyPort;
/** The run loop source for NotifyPort. */
CFRunLoopSourceRef NotifyRLSrc;
/** The attach notification iterator. */
io_iterator_t AttachIterator;
/** The 2nd attach notification iterator. */
io_iterator_t AttachIterator2;
/** The detach notificaiton iterator. */
io_iterator_t DetachIterator;
} DARWINUSBNOTIFY, *PDARWINUSBNOTIFY;
/**
* Run thru an interrator.
*
* The docs says this is necessary to start getting notifications,
* so this function is called in the callbacks and right after
* registering the notification.
*
* @param pIterator The iterator reference.
*/
static void darwinDrainIterator(io_iterator_t pIterator)
{
io_object_t Object;
while ((Object = IOIteratorNext(pIterator)))
{
DARWIN_IOKIT_DUMP_OBJ(Object);
IOObjectRelease(Object);
}
}
/**
* Callback for the 1st attach notification.
*
* @param pvNotify Our data.
* @param NotifyIterator The notification iterator.
*/
static void darwinUSBAttachNotification1(void *pvNotify, io_iterator_t NotifyIterator)
{
DARWIN_IOKIT_LOG(("USB Attach Notification1\n"));
NOREF(pvNotify); //PDARWINUSBNOTIFY pNotify = (PDARWINUSBNOTIFY)pvNotify;
darwinDrainIterator(NotifyIterator);
}
/**
* Callback for the 2nd attach notification.
*
* @param pvNotify Our data.
* @param NotifyIterator The notification iterator.
*/
static void darwinUSBAttachNotification2(void *pvNotify, io_iterator_t NotifyIterator)
{
DARWIN_IOKIT_LOG(("USB Attach Notification2\n"));
NOREF(pvNotify); //PDARWINUSBNOTIFY pNotify = (PDARWINUSBNOTIFY)pvNotify;
darwinDrainIterator(NotifyIterator);
}
/**
* Callback for the detach notifications.
*
* @param pvNotify Our data.
* @param NotifyIterator The notification iterator.
*/
static void darwinUSBDetachNotification(void *pvNotify, io_iterator_t NotifyIterator)
{
DARWIN_IOKIT_LOG(("USB Detach Notification\n"));
NOREF(pvNotify); //PDARWINUSBNOTIFY pNotify = (PDARWINUSBNOTIFY)pvNotify;
darwinDrainIterator(NotifyIterator);
}
/**
* Subscribes the run loop to USB notification events relevant to
* device attach/detach.
*
* The source mode for these events is defined as VBOX_IOKIT_MODE_STRING
* so that the caller can listen to events from this mode only and
* re-evalutate the list of attached devices whenever an event arrives.
*
* @returns opaque for passing to the unsubscribe function. If NULL
* something unexpectedly failed during subscription.
*/
void *DarwinSubscribeUSBNotifications(void)
{
AssertReturn(darwinOpenMasterPort(), NULL);
PDARWINUSBNOTIFY pNotify = (PDARWINUSBNOTIFY)RTMemAllocZ(sizeof(*pNotify));
AssertReturn(pNotify, NULL);
/*
* Create the notification port, bake it into a runloop source which we
* then add to our run loop.
*/
pNotify->NotifyPort = IONotificationPortCreate(g_MasterPort);
Assert(pNotify->NotifyPort);
if (pNotify->NotifyPort)
{
pNotify->NotifyRLSrc = IONotificationPortGetRunLoopSource(pNotify->NotifyPort);
Assert(pNotify->NotifyRLSrc);
if (pNotify->NotifyRLSrc)
{
CFRunLoopAddSource(CFRunLoopGetCurrent(), pNotify->NotifyRLSrc, CFSTR(VBOX_IOKIT_MODE_STRING));
/*
* Create the notifcation callbacks.
*/
kern_return_t rc = IOServiceAddMatchingNotification(pNotify->NotifyPort,
kIOPublishNotification,
IOServiceMatching(kIOUSBDeviceClassName),
darwinUSBAttachNotification1,
pNotify,
&pNotify->AttachIterator);
if (rc == KERN_SUCCESS)
{
darwinDrainIterator(pNotify->AttachIterator);
rc = IOServiceAddMatchingNotification(pNotify->NotifyPort,
kIOMatchedNotification,
IOServiceMatching(kIOUSBDeviceClassName),
darwinUSBAttachNotification2,
pNotify,
&pNotify->AttachIterator2);
if (rc == KERN_SUCCESS)
{
darwinDrainIterator(pNotify->AttachIterator2);
rc = IOServiceAddMatchingNotification(pNotify->NotifyPort,
kIOTerminatedNotification,
IOServiceMatching(kIOUSBDeviceClassName),
darwinUSBDetachNotification,
pNotify,
&pNotify->DetachIterator);
{
darwinDrainIterator(pNotify->DetachIterator);
return pNotify;
}
IOObjectRelease(pNotify->AttachIterator2);
}
IOObjectRelease(pNotify->AttachIterator);
}
CFRunLoopRemoveSource(CFRunLoopGetCurrent(), pNotify->NotifyRLSrc, CFSTR(VBOX_IOKIT_MODE_STRING));
}
IONotificationPortDestroy(pNotify->NotifyPort);
}
RTMemFree(pNotify);
return NULL;
}
/**
* Unsubscribe the run loop from USB notification subscribed to
* by DarwinSubscribeUSBNotifications.
*
* @param pvOpaque The return value from DarwinSubscribeUSBNotifications.
*/
void DarwinUnsubscribeUSBNotifications(void *pvOpaque)
{
PDARWINUSBNOTIFY pNotify = (PDARWINUSBNOTIFY)pvOpaque;
if (!pNotify)
return;
IOObjectRelease(pNotify->AttachIterator);
pNotify->AttachIterator = NULL;
IOObjectRelease(pNotify->AttachIterator2);
pNotify->AttachIterator2 = NULL;
IOObjectRelease(pNotify->DetachIterator);
pNotify->DetachIterator = NULL;
CFRunLoopRemoveSource(CFRunLoopGetCurrent(), pNotify->NotifyRLSrc, CFSTR(VBOX_IOKIT_MODE_STRING));
IONotificationPortDestroy(pNotify->NotifyPort);
pNotify->NotifyRLSrc = NULL;
pNotify->NotifyPort = NULL;
RTMemFree(pNotify);
}
/**
* Decends recursivly into a IORegistry tree locating the first object of a given class.
*
* The search is performed depth first.
*
* @returns Object reference if found, NULL if not.
* @param Object The current tree root.
* @param pszClass The name of the class we're looking for.
* @param pszNameBuf A scratch buffer for query the class name in to avoid
* wasting 128 bytes on an io_name_t object for every recursion.
*/
static io_object_t darwinFindObjectByClass(io_object_t Object, const char *pszClass, io_name_t pszNameBuf)
{
io_iterator_t Children;
kern_return_t krc = IORegistryEntryGetChildIterator(Object, kIOServicePlane, &Children);
if (krc != KERN_SUCCESS)
return NULL;
io_object_t Child;
while ((Child = IOIteratorNext(Children)))
{
krc = IOObjectGetClass(Child, pszNameBuf);
if ( krc == KERN_SUCCESS
&& !strcmp(pszNameBuf, pszClass))
break;
io_object_t GrandChild = darwinFindObjectByClass(Child, pszClass, pszNameBuf);
IOObjectRelease(Child);
if (GrandChild)
{
Child = GrandChild;
break;
}
}
IOObjectRelease(Children);
return Child;
}
/**
* Decends recursivly into IOUSBMassStorageClass tree to check whether
* the MSD is mounted or not.
*
* The current heuristic is to look for the IOMedia class.
*
* @returns true if mounted, false if not.
* @param MSDObj The IOUSBMassStorageClass object.
* @param pszNameBuf A scratch buffer for query the class name in to avoid
* wasting 128 bytes on an io_name_t object for every recursion.
*/
static bool darwinIsMassStorageInterfaceInUse(io_object_t MSDObj, io_name_t pszNameBuf)
{
io_object_t MediaObj = darwinFindObjectByClass(MSDObj, "IOMedia", pszNameBuf);
if (MediaObj)
{
/* more checks? */
IOObjectRelease(MediaObj);
return true;
}
return false;
}
/**
* Worker function for DarwinGetUSBDevices() that tries to figure out
* what state the device is in.
*
* This is mostly a matter of distinguishing between devices that nobody
* uses, devices that can be seized and devices that cannot be grabbed.
*
* @param pCur The USB device data.
* @param USBDevice The USB device object.
* @param PropsRef The USB device properties.
*/
static void darwinDeterminUSBDeviceState(PUSBDEVICE pCur, io_object_t USBDevice, CFMutableDictionaryRef PropsRef)
{
/*
* Iterate the interfaces (among the children of the IOUSBDevice object).
*/
io_iterator_t Interfaces;
kern_return_t krc = IORegistryEntryGetChildIterator(USBDevice, kIOServicePlane, &Interfaces);
if (krc != KERN_SUCCESS)
return;
RTPROCESS Owner = NIL_RTPROCESS;
RTPROCESS Client = NIL_RTPROCESS;
bool fUserClientOnly = true;
bool fConfigured = false;
bool fInUse = false;
bool fSeizable = true;
io_object_t Interface;
while ((Interface = IOIteratorNext(Interfaces)))
{
io_name_t szName;
krc = IOObjectGetClass(Interface, szName);
if ( krc == KERN_SUCCESS
&& !strcmp(szName, "IOUSBInterface"))
{
fConfigured = true;
/*
* Iterate the interface children looking for stuff other than
* IOUSBUserClientInit objects.
*/
io_iterator_t Children1;
krc = IORegistryEntryGetChildIterator(Interface, kIOServicePlane, &Children1);
if (krc == KERN_SUCCESS)
{
io_object_t Child1;
while ((Child1 = IOIteratorNext(Children1)))
{
krc = IOObjectGetClass(Child1, szName);
if ( krc == KERN_SUCCESS
&& strcmp(szName, "IOUSBUserClientInit"))
{
fUserClientOnly = false;
if (!strcmp(szName, "IOUSBMassStorageClass"))
{
/* Only permit capturing MSDs that aren't mounted, at least
until the GUI starts poping up warnings about data loss
and such when capturing a busy device. */
fSeizable = false;
fInUse |= darwinIsMassStorageInterfaceInUse(Child1, szName);
}
else if (!strcmp(szName, "IOUSBHIDDriver")
|| !strcmp(szName, "AppleHIDMouse")
/** @todo more? */)
{
/* For now, just assume that all HID devices are inaccessible
because of the greedy HID service. */
fSeizable = false;
fInUse = true;
}
else
fInUse = true;
}
IOObjectRelease(Child1);
}
IOObjectRelease(Children1);
}
}
/*
* Not an interface, could it be VBoxUSBDevice?
* If it is, get the owner and client properties.
*/
else if ( krc == KERN_SUCCESS
&& !strcmp(szName, VBOXUSBDEVICE_CLASS_NAME))
{
CFMutableDictionaryRef PropsRef = 0;
krc = IORegistryEntryCreateCFProperties(Interface, &PropsRef, kCFAllocatorDefault, kNilOptions);
if (krc == KERN_SUCCESS)
{
darwinDictGetProccess(PropsRef, CFSTR(VBOXUSB_OWNER_KEY), &Owner);
darwinDictGetProccess(PropsRef, CFSTR(VBOXUSB_CLIENT_KEY), &Client);
CFRelease(PropsRef);
}
}
IOObjectRelease(Interface);
}
IOObjectRelease(Interfaces);
/*
* Calc the status.
*/
if ( Owner != NIL_RTPROCESS
&& !Owner)
{
if (Owner == RTProcSelf())
pCur->enmState = Client == NIL_RTPROCESS || !Client
? USBDEVICESTATE_HELD_BY_PROXY
: USBDEVICESTATE_USED_BY_GUEST;
else
pCur->enmState = USBDEVICESTATE_USED_BY_HOST;
}
else if (fUserClientOnly)
/** @todo how to detect other user client?!? - Look for IOUSBUserClient! */
pCur->enmState = !fConfigured
? USBDEVICESTATE_UNUSED
: USBDEVICESTATE_USED_BY_HOST_CAPTURABLE;
else if (!fInUse)
pCur->enmState = USBDEVICESTATE_UNUSED;
else
pCur->enmState = fSeizable
? USBDEVICESTATE_USED_BY_HOST_CAPTURABLE
: USBDEVICESTATE_USED_BY_HOST;
}
/**
* Enumerate the USB devices returning a FIFO of them.
*
* @returns Pointer to the head.
* USBProxyService::freeDevice is expected to free each of the list elements.
*/
PUSBDEVICE DarwinGetUSBDevices(void)
{
AssertReturn(darwinOpenMasterPort(), NULL);
//DARWIN_IOKIT_LOG(("DarwinGetUSBDevices\n"));
/*
* Create a matching dictionary for searching for USB Devices in the IOKit.
*/
CFMutableDictionaryRef RefMatchingDict = IOServiceMatching(kIOUSBDeviceClassName);
AssertReturn(RefMatchingDict, NULL);
/*
* Perform the search and get a collection of USB Device back.
*/
io_iterator_t USBDevices = NULL;
IOReturn rc = IOServiceGetMatchingServices(g_MasterPort, RefMatchingDict, &USBDevices);
AssertMsgReturn(rc == kIOReturnSuccess, ("rc=%d\n", rc), NULL);
RefMatchingDict = NULL; /* the reference is consumed by IOServiceGetMatchingServices. */
/*
* Enumerate the USB Devices.
*/
PUSBDEVICE pHead = NULL;
PUSBDEVICE pTail = NULL;
unsigned i = 0;
io_object_t USBDevice;
while ((USBDevice = IOIteratorNext(USBDevices)) != 0)
{
//DARWIN_IOKIT_DUMP_OBJ(USBDevice);
/*
* Query the device properties from the registry.
*
* We could alternatively use the device and such, but that will be
* slower and we would have to resort to the registry for the three
* string anyway.
*/
CFMutableDictionaryRef PropsRef = 0;
kern_return_t krc = IORegistryEntryCreateCFProperties(USBDevice, &PropsRef, kCFAllocatorDefault, kNilOptions);
if (krc == KERN_SUCCESS)
{
bool fOk = false;
PUSBDEVICE pCur = (PUSBDEVICE)RTMemAllocZ(sizeof(*pCur));
do /* loop for breaking out of on failure. */
{
AssertBreak(pCur,);
/*
* Mandatory
*/
pCur->bcdUSB = 0; /* we've no idea. */
pCur->enmState = USBDEVICESTATE_USED_BY_HOST_CAPTURABLE; /* just a default, we'll try harder in a bit. */
AssertBreak(darwinDictGetU8(PropsRef, CFSTR(kUSBDeviceClass), &pCur->bDeviceClass),);
/* skip hubs */
if (pCur->bDeviceClass == 0x09 /* hub, find a define! */)
break;
AssertBreak(darwinDictGetU8(PropsRef, CFSTR(kUSBDeviceSubClass), &pCur->bDeviceSubClass),);
AssertBreak(darwinDictGetU8(PropsRef, CFSTR(kUSBDeviceProtocol), &pCur->bDeviceProtocol),);
AssertBreak(darwinDictGetU16(PropsRef, CFSTR(kUSBVendorID), &pCur->idVendor),);
AssertBreak(darwinDictGetU16(PropsRef, CFSTR(kUSBProductID), &pCur->idProduct),);
AssertBreak(darwinDictGetU16(PropsRef, CFSTR(kUSBDeviceReleaseNumber), &pCur->bcdDevice),);
uint32_t u32LocationId;
AssertBreak(darwinDictGetU32(PropsRef, CFSTR(kUSBDevicePropertyLocationID), &u32LocationId),);
uint64_t u64SessionId;
AssertBreak(darwinDictGetU64(PropsRef, CFSTR("sessionID"), &u64SessionId),);
char szAddress[64];
RTStrPrintf(szAddress, sizeof(szAddress), "p=0x%04RX16;v=0x%04RX16;s=0x%016RX64;l=0x%08RX32",
pCur->idProduct, pCur->idVendor, u64SessionId, u32LocationId);
pCur->pszAddress = RTStrDup(szAddress);
AssertBreak(pCur->pszAddress,);
/*
* Optional.
* There are some nameless device in the iMac, apply names to them.
*/
darwinDictGetString(PropsRef, CFSTR("USB Vendor Name"), (char **)&pCur->pszManufacturer);
if ( !pCur->pszManufacturer
&& pCur->idVendor == kIOUSBVendorIDAppleComputer)
pCur->pszManufacturer = RTStrDup("Apple Computer, Inc.");
darwinDictGetString(PropsRef, CFSTR("USB Product Name"), (char **)&pCur->pszProduct);
if ( !pCur->pszProduct
&& pCur->bDeviceClass == 224 /* Wireless */
&& pCur->bDeviceSubClass == 1 /* Radio Frequency */
&& pCur->bDeviceProtocol == 1 /* Bluetooth */)
pCur->pszProduct = RTStrDup("Bluetooth");
darwinDictGetString(PropsRef, CFSTR("USB Serial Number"), (char **)&pCur->pszSerialNumber);
#if 0 /* leave the remainder as zero for now. */
/*
* Create a plugin interface for the service and query its USB Device interface.
*/
SInt32 Score = 0;
IOCFPlugInInterface **ppPlugInInterface = NULL;
rc = IOCreatePlugInInterfaceForService(USBDevice, kIOUSBDeviceUserClientTypeID,
kIOCFPlugInInterfaceID, &ppPlugInInterface, &Score);
if (rc == kIOReturnSuccess)
{
IOUSBDeviceInterface245 **ppUSBDevI = NULL;
HRESULT hrc = (*ppPlugInInterface)->QueryInterface(ppPlugInInterface,
CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID245),
(LPVOID *)&ppUSBDevI);
rc = IODestroyPlugInInterface(ppPlugInInterface); Assert(rc == kIOReturnSuccess);
ppPlugInInterface = NULL;
if (hrc == S_OK)
{
/** @todo enumerate configurations and interfaces if we actually need them. */
//IOReturn (*GetNumberOfConfigurations)(void *self, UInt8 *numConfig);
//IOReturn (*GetConfigurationDescriptorPtr)(void *self, UInt8 configIndex, IOUSBConfigurationDescriptorPtr *desc);
//IOReturn (*CreateInterfaceIterator)(void *self, IOUSBFindInterfaceRequest *req, io_iterator_t *iter);
}
long cReft = (*ppUSBDeviceInterface)->Release(ppUSBDeviceInterface); MY_CHECK_CREFS(cRefs);
}
#endif
/*
* Try determin the state.
*/
darwinDeterminUSBDeviceState(pCur, USBDevice, PropsRef);
/*
* We're good. Link the device.
*/
pCur->pPrev = pTail;
if (pTail)
pTail = pTail->pNext = pCur;
else
pTail = pHead = pCur;
fOk = true;
} while (0);
/* cleanup on failure / skipped device. */
if (!fOk && pCur)
DarwinFreeUSBDeviceFromIOKit(pCur);
CFRelease(PropsRef);
}
else
AssertMsgFailed(("krc=%#x\n", krc));
IOObjectRelease(USBDevice);
i++;
}
IOObjectRelease(USBDevices);
//DARWIN_IOKIT_LOG_FLUSH();
/*
* Some post processing. There are a couple of things we have to
* make 100% sure about, and that is that the (Apple) keyboard
* and mouse most likely to be in use by the user aren't available
* for capturing. If there is no Apple mouse or keyboard we'll
* take the first one from another vendor.
*/
/* As it turns out, the HID service will take all keyboards and mice
and we're not currently able to seize them. */
PUSBDEVICE pMouse = NULL;
PUSBDEVICE pKeyboard = NULL;
for (PUSBDEVICE pCur = pHead; pCur; pCur = pCur->pNext)
if (pCur->idVendor == kIOUSBVendorIDAppleComputer)
{
/*
* This test is a bit rough, should check device class/protocol but
* we don't have interface info yet so that might be a bit tricky.
*/
if ( ( !pKeyboard
|| pKeyboard->idVendor != kIOUSBVendorIDAppleComputer)
&& pCur->pszProduct
&& strstr(pCur->pszProduct, " Keyboard"))
pKeyboard = pCur;
else if ( ( !pMouse
|| pMouse->idVendor != kIOUSBVendorIDAppleComputer)
&& pCur->pszProduct
&& strstr(pCur->pszProduct, " Mouse")
)
pMouse = pCur;
}
else if (!pKeyboard || !pMouse)
{
if ( pCur->bDeviceClass == 3 /* HID */
&& pCur->bDeviceProtocol == 1 /* Keyboard */)
pKeyboard = pCur;
else if ( pCur->bDeviceClass == 3 /* HID */
&& pCur->bDeviceProtocol == 2 /* Mouse */)
pMouse = pCur;
/** @todo examin interfaces */
}
if (pKeyboard)
pKeyboard->enmState = USBDEVICESTATE_USED_BY_HOST;
if (pMouse)
pMouse->enmState = USBDEVICESTATE_USED_BY_HOST;
return pHead;
}
/**
* Triggers re-enumeration of a device.
*
* @returns VBox status code.
* @param pCur The USBDEVICE structure for the device.
*/
int DarwinReEnumerateUSBDevice(PCUSBDEVICE pCur)
{
int vrc;
const char *pszAddress = pCur->pszAddress;
AssertPtrReturn(pszAddress, VERR_INVALID_POINTER);
AssertReturn(darwinOpenMasterPort(), VERR_GENERAL_FAILURE);
/*
* This code is a short version of the Open method in USBProxyDevice-darwin.cpp stuff.
* Fixes made to this code probably applies there too!
*/
CFMutableDictionaryRef RefMatchingDict = IOServiceMatching(kIOUSBDeviceClassName);
AssertReturn(RefMatchingDict, NULL);
uint64_t u64SessionId = 0;
uint32_t u32LocationId = 0;
const char *psz = pszAddress;
do
{
const char chValue = *psz;
AssertReleaseReturn(psz[1] == '=', VERR_INTERNAL_ERROR);
uint64_t u64Value;
int rc = RTStrToUInt64Ex(psz + 2, (char **)&psz, 0, &u64Value);
AssertReleaseRCReturn(rc, rc);
AssertReleaseReturn(!*psz || *psz == ';', rc);
switch (chValue)
{
case 'l':
u32LocationId = (uint32_t)u64Value;
break;
case 's':
u64SessionId = u64Value;
break;
case 'p':
case 'v':
{
#if 0 /* Guess what, this doesn't 'ing work either! */
SInt32 i32 = (int16_t)u64Value;
CFNumberRef Num = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &i32);
AssertBreak(Num,);
CFDictionarySetValue(RefMatchingDict, chValue == 'p' ? CFSTR(kUSBProductID) : CFSTR(kUSBVendorID), Num);
CFRelease(Num);
#endif
break;
}
default:
AssertReleaseMsgFailedReturn(("chValue=%#x\n", chValue), VERR_INTERNAL_ERROR);
}
if (*psz == ';')
psz++;
} while (*psz);
io_iterator_t USBDevices = NULL;
IOReturn irc = IOServiceGetMatchingServices(g_MasterPort, RefMatchingDict, &USBDevices);
AssertMsgReturn(irc == kIOReturnSuccess, ("irc=%#x\n", irc), NULL);
RefMatchingDict = NULL; /* the reference is consumed by IOServiceGetMatchingServices. */
unsigned cMatches = 0;
io_object_t USBDevice;
while ((USBDevice = IOIteratorNext(USBDevices)))
{
cMatches++;
CFMutableDictionaryRef PropsRef = 0;
kern_return_t krc = IORegistryEntryCreateCFProperties(USBDevice, &PropsRef, kCFAllocatorDefault, kNilOptions);
if (krc == KERN_SUCCESS)
{
uint64_t u64CurSessionId;
uint32_t u32CurLocationId;
if ( ( !u64SessionId
|| ( darwinDictGetU64(PropsRef, CFSTR("sessionID"), &u64CurSessionId)
&& u64CurSessionId == u64SessionId))
&& ( !u32LocationId
|| ( darwinDictGetU32(PropsRef, CFSTR(kUSBDevicePropertyLocationID), &u32CurLocationId)
&& u32CurLocationId == u32LocationId))
)
{
CFRelease(PropsRef);
break;
}
CFRelease(PropsRef);
}
IOObjectRelease(USBDevice);
}
IOObjectRelease(USBDevices);
USBDevices = NULL;
if (!USBDevice)
{
LogRel(("USB: Device '%s' not found (%d pid+vid matches)\n", pszAddress, cMatches));
IOObjectRelease(USBDevices);
return VERR_VUSB_DEVICE_NAME_NOT_FOUND;
}
/*
* Create a plugin interface for the device and query its IOUSBDeviceInterface.
*/
SInt32 Score = 0;
IOCFPlugInInterface **ppPlugInInterface = NULL;
irc = IOCreatePlugInInterfaceForService(USBDevice, kIOUSBDeviceUserClientTypeID,
kIOCFPlugInInterfaceID, &ppPlugInInterface, &Score);
if (irc == kIOReturnSuccess)
{
IOUSBDeviceInterface245 **ppDevI = NULL;
HRESULT hrc = (*ppPlugInInterface)->QueryInterface(ppPlugInInterface,
CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID245),
(LPVOID *)&ppDevI);
irc = IODestroyPlugInInterface(ppPlugInInterface); Assert(irc == kIOReturnSuccess);
ppPlugInInterface = NULL;
if (hrc == S_OK)
{
/*
* Try open the device for exclusive access.
*/
irc = (*ppDevI)->USBDeviceOpenSeize(ppDevI);
if (irc == kIOReturnExclusiveAccess)
{
RTThreadSleep(20);
irc = (*ppDevI)->USBDeviceOpenSeize(ppDevI);
}
if (irc == kIOReturnSuccess)
{
/*
* Re-enumerate the device and bail out.
*/
irc = (*ppDevI)->USBDeviceReEnumerate(ppDevI, 0);
if (irc != kIOReturnSuccess)
{
LogRel(("USB: Failed to open device '%s', plug-in creation failed with irc=%#x.\n", pszAddress, irc));
vrc = RTErrConvertFromDarwinIO(irc);
}
(*ppDevI)->USBDeviceClose(ppDevI);
}
else if (irc == kIOReturnExclusiveAccess)
{
LogRel(("USB: Device '%s' is being used by another process\n", pszAddress));
vrc = VERR_SHARING_VIOLATION;
}
else
{
LogRel(("USB: Failed to open device '%s', irc=%#x.\n", pszAddress, irc));
vrc = VERR_OPEN_FAILED;
}
}
else
{
LogRel(("USB: Failed to create plugin interface for device '%s', hrc=%#x.\n", pszAddress, hrc));
vrc = VERR_OPEN_FAILED;
}
(*ppDevI)->Release(ppDevI);
}
else
{
LogRel(("USB: Failed to open device '%s', plug-in creation failed with irc=%#x.\n", pszAddress, irc));
vrc = RTErrConvertFromDarwinIO(irc);
}
return vrc;
}
#endif /* VBOX_WITH_USB */
/**
* Enumerate the DVD drives returning a FIFO of device name strings.
*
* @returns Pointer to the head.
* The caller is responsible for calling RTMemFree() on each of the nodes.
*/
PDARWINDVD DarwinGetDVDDrives(void)
{
AssertReturn(darwinOpenMasterPort(), NULL);
/*
* Create a matching dictionary for searching for DVD services in the IOKit.
*
* [If I understand this correctly, plain CDROMs doesn't show up as
* IODVDServices. Too keep things simple, we will only support DVDs
* until somebody complains about it and we get hardware to test it on.
* (Unless I'm much mistaken, there aren't any (orignal) intel macs with
* plain cdroms.)]
*/
CFMutableDictionaryRef RefMatchingDict = IOServiceMatching("IODVDServices");
AssertReturn(RefMatchingDict, NULL);
/*
* Perform the search and get a collection of DVD services.
*/
io_iterator_t DVDServices = NULL;
IOReturn rc = IOServiceGetMatchingServices(g_MasterPort, RefMatchingDict, &DVDServices);
AssertMsgReturn(rc == kIOReturnSuccess, ("rc=%d\n", rc), NULL);
RefMatchingDict = NULL; /* the reference is consumed by IOServiceGetMatchingServices. */
/*
* Enumerate the DVD services.
* (This enumeration must be identical to the one performed in DrvHostBase.cpp.)
*/
PDARWINDVD pHead = NULL;
PDARWINDVD pTail = NULL;
unsigned i = 0;
io_object_t DVDService;
while ((DVDService = IOIteratorNext(DVDServices)) != 0)
{
/*
* Get the properties we use to identify the DVD drive.
*
* While there is a (weird 12 byte) GUID, it isn't persistent
* accross boots. So, we have to use a combination of the
* vendor name and product name properties with an optional
* sequence number for identification.
*/
CFMutableDictionaryRef PropsRef = 0;
kern_return_t krc = IORegistryEntryCreateCFProperties(DVDService, &PropsRef, kCFAllocatorDefault, kNilOptions);
if (krc == KERN_SUCCESS)
{
/* Get the Device Characteristics dictionary. */
CFDictionaryRef DevCharRef = (CFDictionaryRef)CFDictionaryGetValue(PropsRef, CFSTR(kIOPropertyDeviceCharacteristicsKey));
if (DevCharRef)
{
/* The vendor name. */
char szVendor[128];
char *pszVendor = &szVendor[0];
CFTypeRef ValueRef = CFDictionaryGetValue(DevCharRef, CFSTR(kIOPropertyVendorNameKey));
if ( ValueRef
&& CFGetTypeID(ValueRef) == CFStringGetTypeID()
&& CFStringGetCString((CFStringRef)ValueRef, szVendor, sizeof(szVendor), kCFStringEncodingUTF8))
pszVendor = RTStrStrip(szVendor);
else
*pszVendor = '\0';
/* The product name. */
char szProduct[128];
char *pszProduct = &szProduct[0];
ValueRef = CFDictionaryGetValue(DevCharRef, CFSTR(kIOPropertyProductNameKey));
if ( ValueRef
&& CFGetTypeID(ValueRef) == CFStringGetTypeID()
&& CFStringGetCString((CFStringRef)ValueRef, szProduct, sizeof(szProduct), kCFStringEncodingUTF8))
pszProduct = RTStrStrip(szProduct);
else
*pszProduct = '\0';
/* Construct the name and check for duplicates. */
char szName[256 + 32];
if (*pszVendor || *pszProduct)
{
if (*pszVendor && *pszProduct)
RTStrPrintf(szName, sizeof(szName), "%s %s", pszVendor, pszProduct);
else
strcpy(szName, *pszVendor ? pszVendor : pszProduct);
for (PDARWINDVD pCur = pHead; pCur; pCur = pCur->pNext)
{
if (!strcmp(szName, pCur->szName))
{
if (*pszVendor && *pszProduct)
RTStrPrintf(szName, sizeof(szName), "%s %s (#%u)", pszVendor, pszProduct, i);
else
RTStrPrintf(szName, sizeof(szName), "%s %s (#%u)", *pszVendor ? pszVendor : pszProduct, i);
break;
}
}
}
else
RTStrPrintf(szName, sizeof(szName), "(#%u)", i);
/* Create the device. */
size_t cbName = strlen(szName) + 1;
PDARWINDVD pNew = (PDARWINDVD)RTMemAlloc(RT_OFFSETOF(DARWINDVD, szName[cbName]));
if (pNew)
{
pNew->pNext = NULL;
memcpy(pNew->szName, szName, cbName);
if (pTail)
pTail = pTail->pNext = pNew;
else
pTail = pHead = pNew;
}
}
CFRelease(PropsRef);
}
else
AssertMsgFailed(("krc=%#x\n", krc));
IOObjectRelease(DVDService);
i++;
}
IOObjectRelease(DVDServices);
return pHead;
}