Monitor changes on Thunderbolt port connection - macos

I am working on a requirement where i need to monitor changes in Thunderbolt port connection. (When Thunderbolt cable is connected or disconnected).
I tried to use IOServiceMatching(kIOUSBInterfaceClassName) from IOKit framework but i cannot monitor changes on Thunderbolt port.
Is there any way i can achieve it? Any help is appreciated.

Thunderbolt devices (except displays that use the DisplayPort portion of the Thunderbolt port) are PCI devices, not USB, so they will show up in the IOService registry as IOPCIDevices. They will however also show up as IOThunderboltPort objects in the Thunderbolt subtree, where the "PCI Path" property will indicate the IOService path to the relevant IOPCIDevice. By monitoring the appearance and disappearance of IOThunderboltPort services, and checking their PCI Path property, you can avoid matching other kinds of PCI devices.
To illustrate what I'm talking about, open up IORegistryExplorer or IOJones and hotplug a Thunderbolt device; you should see both the IOThunderboltPort (and a bunch of other types of related object, such as AppleThunderboltPCIUpAdapter etc.) and the IOPCIDevice (as well as the PCI2PCI bridges via which the Thunderbolt bus works) appear. (Alternatively you can use ioreg to take snapshots before and after the hotplug.)
So in summary, I would match IOThunderboltPort services, ignore any without a PCI path property, and look up the corresponding IOPCIDevice in the IO Registry for the ones that have it to get to the actual device.

Finally i figured out a way indeed to monitor Thunderbolt Connection. Thanks to the apple tech guy who pointed me out in a right direction.
Monitoring the I/O Registry for IOEthernetInterface entries.  It’s relatively easy to filter out Thunderbolt networking (I’m not sure what the best option is, but an easy option is to look for “ThunderboltIP” in the “IOModel” property of the parent IOEthernetController). This was the response from one of the Tech guys from apple on Apple forum. Using the above info i wrote a piece of code which will return you the status of Thunderbolt port.
#include <IOKit/network/IOEthernetController.h>
- (void) monitorThunderboltConnection
{
CFMutableDictionaryRef matchingDict;
io_iterator_t iter;
io_object_t controllerService;
kern_return_t kr;
UInt8 MACAddress[kIOEthernetAddressSize];
QNInterfaceModel *interfaceModel = [[QNInterfaceModel alloc] initWithInterfaceModel];
/* set up a matching dictionary for the class */
matchingDict = IOServiceMatching(kIOEthernetInterfaceClass);
if (matchingDict == NULL)
{
NSLog(#"Failed");
return;
}
/* Now we have a dictionary, get an iterator.*/
kr = IOServiceGetMatchingServices(kIOMasterPortDefault, matchingDict, &iter);
if (kr == kIOReturnSuccess)
{
// Actually iterate through the found devices.
io_registry_entry_t serviceObject;
while ((serviceObject = IOIteratorNext(iter)))
{
// Put this services object into a dictionary object.
kr = IORegistryEntryGetParentEntry(serviceObject,
kIOServicePlane,
&controllerService);
if (KERN_SUCCESS != kr)
{
printf("IORegistryEntryGetParentEntry returned 0x%08x\n", kr);
}
else
{
CFMutableDictionaryRef serviceDictionary;
CFTypeRef networkType;
CFTypeRef MACAddressAsCFData;
NSNumber *linkStatus;
if (IORegistryEntryCreateCFProperties(serviceObject,
&serviceDictionary,
kCFAllocatorDefault,
kNilOptions) == kIOReturnSuccess)
{
networkType = IORegistryEntryCreateCFProperty(controllerService,
CFSTR(kIOModel),
kCFAllocatorDefault,
0);
if(networkType)
{
if (CFGetTypeID(networkType) == CFStringGetTypeID())
{
CFStringRef networkName = networkType;
interfaceModel.interfaceName = (__bridge NSString *)networkName;
}
CFRelease(networkType);
}
if([interfaceModel.interfaceName isEqualToString:#"ThunderboltIP"])
{
MACAddressAsCFData = IORegistryEntryCreateCFProperty(controllerService,
CFSTR(kIOMACAddress),
kCFAllocatorDefault,
0);
if (MACAddressAsCFData)
{
CFShow(MACAddressAsCFData); // for display purposes only; output goes to stderr
// Get the raw bytes of the MAC address from the CFData
CFDataGetBytes(MACAddressAsCFData, CFRangeMake(0, kIOEthernetAddressSize), MACAddress);
if (KERN_SUCCESS != kr)
{
printf("GetMACAddress returned 0x%08x\n", kr);
}
else
{
interfaceModel.macAddress = [[NSString stringWithFormat:#"%02x:%02x:%02x:%02x:%02x:%02x",MACAddress[0], MACAddress[1], MACAddress[2], MACAddress[3], MACAddress[4], MACAddress[5]] uppercaseString];
}
CFRelease(MACAddressAsCFData);
}
linkStatus = (__bridge NSNumber *)(IORegistryEntryCreateCFProperty(controllerService,
CFSTR(kIOLinkStatus),
kCFAllocatorDefault,
0));
if (linkStatus)
{
NSLog(#"%#", [linkStatus stringValue]);
if([linkStatus integerValue] == 3) // Thunderbolt IP is Connnected
{
interfaceModel.connectedStatus = YES;
}
else
{
interfaceModel.connectedStatus = NO;
}
}
CFStringRef bsdName = ( CFStringRef ) IORegistryEntrySearchCFProperty (controllerService,
kIOServicePlane,
CFSTR ( kIOBSDNameKey ),
kCFAllocatorDefault,
kIORegistryIterateRecursively);
interfaceModel.interfaceName = (__bridge NSString *) bsdName;
if(interfaceModel.connectedStatus == YES)
{
NSLog(#"Connected");
}
else
{
NSLog(#"DisConnected");
}
}
// Failed to create a service dictionary, release and go on.
IOObjectRelease(serviceObject);
// Done with the parent Ethernet controller object so we release it.
(void) IOObjectRelease(controllerService);
continue;
}
}
}
}
/* Done, release the iterator */
IOObjectRelease(iter);
}
NOTE: I am using Interface model to collect all the thunderbolt info like Hardware Address, BSD Name, Link Status etc. You also need add I/O Kit framework to your project.

Related

How to get Device Instance path from Bus Relations in Win32 API

Is there a good way to get the Device Instance path(e.g: USB\VID_021D&PID_0C51&MI_00\6&192CE49&4&0000) from Bus Relations (e.g:{0.0.1.00000000}.{4234a4c6-3535-49d6-971c-76ce1f22521e}) ?
I realize some terms in Windows have aliases, by "Device Instance path" and "Bus Relations", I mean the two properties found in the device manager:
I have tried to get the Device Instance path from the PropertyStore
int getProperty(std::string& sampleID)
{
HRESULT hr = S_OK;
IMMDeviceEnumerator* pEnumerator = NULL;
//CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
hr = CoCreateInstance(__uuidof(MMDeviceEnumerator),
NULL,
CLSCTX_INPROC_SERVER,
__uuidof(IMMDeviceEnumerator),
(void**)&pEnumerator);
if (hr != S_OK)
{
// error cleanup
return hr;
}
IMMDevice* pEndpoint = NULL;
std::wstring wSampleID(sampleID.begin(), sampleID.end());
hr = pEnumerator->GetDevice(wSampleID.c_str(), &pEndpoint);
if (hr != S_OK || pEndpoint == NULL)
{
// error cleanup
return hr;
}
if (!pEndpoint)
{
// error cleanup
return hr;
}
IPropertyStore* pProps = NULL;
hr = pEndpoint->OpenPropertyStore(
STGM_READ, &pProps);
PROPVARIANT varName;
// Initialize container for property value.
PropVariantInit(&varName);
hr = pProps->GetValue(
PKEY_AudioEndpoint_GUID, &varName);
if (hr != S_OK)
{
//error cleanup
return hr;
}
std::cout << varName.pwszVal << std::endl;
PropVariantClear(&varName);
SAFE_RELEASE(pProps)
SAFE_RELEASE(pEndpoint)
SAFE_RELEASE(pEnumerator);
return hr;
}
but there does not seem to be a property key to retrieve this piece of information for Audio Endpoint IMMDevice. Any suggestion is appreciated.
An example would be a headphone + microphone device which yields two
Bus Relation strings, one for headphone and the other for microphone.
My goal is to group the Bus Relations belonging to the same physical
device.
Bus Relations belonging to the same physical device have already been grouped and you can get it through "Bus relations" property of the same physical audio device (parent). You can find that via Device Manager (View -> Devices by connection) like this:
Is there a good way to get the Device Instance path(e.g:
USB\VID_021D&PID_0C51&MI_00\6&192CE49&4&0000) from Bus Relations
(e.g:{0.0.1.00000000}.{4234a4c6-3535-49d6-971c-76ce1f22521e}) ?
Although it is not clear that what grouped Bus relations will you use for. But you will find that not all (USB) devices have Device Instance path in this format: USB\VID_xxxx&PID_xxxx*. Take Microphone showed in above snapshot as an example, its Device Instance path like below, same with one of bus relations of its parent.

Programmatically know whether a given path is network path or local path on Mac OS X

How can I programmatically determine whether a given path is network path or local path on Mac OS X at run time?
Ex:
1. /Volumes/abc/xyz (mounted using smb)
2. ../test/pqr (the application is on shared network path, hence the current working directory is also a network path and hence the relative path is also a network path)
Like on Windows below code will determine whether, pPath is a network shared path (like * 1. \TallyDT100\c\test\file.txt
2. z:\test\file.txt (A network path when z: is mapped to some network path).
UNIVERSAL_NAME_INFO * universalname = NULL; ///< for getting the universal path name of file on network share.
DWORD retval; ///< for getting the return value from WNetGetUniversalName
DWORD length = MAX_PATH_LEN; ///< length of universal name which would be made.
// The memory for getting the universal name information is allocated.
universalname = (UNIVERSAL_NAME_INFO *) Calloc (MAX_PATH_LEN * sizeof (Char));
retval = WNetGetUniversalName (pPath, UNIVERSAL_NAME_INFO_LEVEL, universalname, &length);
Free (universalname);
// NO_ERROR is returned only when it's drive mapped for shared network folder.
return (NO_ERROR == retval) ? true : false;
In case anyone stumbles upon this from Google search, here is some useful information from Apple:
There are numerous ways to test whether a volume is a network volume. The best method to use depends on the layer that you're working at:
If you're working on general application code, you should use NSURL for this. Construct a URL for any item on the volume and then call -getResourceValue:forKey:error: to get the NSURLVolumeIsLocalKey key; the returned value will be false (kCFBooleanFalse) for a network volume and true (kCFBooleanTrue) for a local volume.
If you're programming at the BSD layer, you can call statfs and test the MNT_LOCAL flag in the f_flags field that it returns. Alternatively, you can call getattrlist to request the ATTR_VOL_MOUNTFLAGS attribute, which can be more efficient than statfs under some circumstances.
https://developer.apple.com/library/mac/qa/nw09/_index.html
You can use DiskArbitration
#import <DiskArbitration/DiskArbitration.h>
+(BOOL)isNetworkVolume: (NSURL *)volumeURL
{
BOOL result = NO;
int err = 0;
DADiskRef disk;
DASessionRef session;
CFDictionaryRef descDict;
session = DASessionCreate(NULL);
if (session == NULL) {
err = EINVAL;
}
if (err == 0) {
disk = DADiskCreateFromVolumePath(NULL,session,(__bridge CFURLRef)volumeURL);
if (session == NULL) {
err = EINVAL;
}
}
if (err == 0) {
descDict = DADiskCopyDescription(disk);
if (descDict == NULL) {
err = EINVAL;
}
}
if (err == 0) {
CFBooleanRef VolumeNetworkKey = CFDictionaryGetValue(descDict,kDADiskDescriptionVolumeNetworkKey);
if (VolumeNetworkKey != NULL)
{
if (CFEqual(VolumeNetworkKey, kCFBooleanTrue)) {
result = YES;
}
}
}
if (descDict != NULL) {
CFRelease(descDict);
}
if (disk != NULL) {
CFRelease(disk);
}
if (session != NULL) {
CFRelease(session);
}
return result;
}
Check below how to detect whether your path exist on your machine or not:-
NSArray *arr=NSSearchPathForDirectoriesInDomains(NSDesktopDirectory, NSUserDomainMask, YES);
NSString *str=[arr lastObject];
NSString *yourPath=[str stringByAppendingPathComponent:#"Volumes/abc/xyz"];
if([[NSFileManager defaultManager]fileExistsAtPath:yourPath])
{
NSLog(#"This is your Local Path");
}
You may use the NSLocalDomainMask and NSNetworkDomainMask in NSSearchPathForDirectoriesInDomains.
For your reference the link - http://www.filibeto.org/unix/macos/lib/dev/documentation/Cocoa/Conceptual/LowLevelFileMgmt/LowLevelFileMgmt.pdf. The topic " Locating Directories on the System " here discusses the NSSearchPathForDirectoriesInDomains class.

Developing iTunes like application in c#

I need to develop an application in c# that could automatically detect an iPhone when it is connected to the system and read a particular file for the iPhone file system. I basically want this file to be downloaded automatically from device to the PC. I used USBpcap tool that suggests that iTunes connects to phone using some XML format. Any help or insight greatly appreciated. Is there any documentation of Third party APIs that can get me started? There are some applications that can replicate iTunes functionality e.g Copytrans
Is there any protocol or APIs provided by Apple?
I have been digging the internet and found this link Layered communication for iPhone.
Also I am using the LibUsbDotNet libraries for communicating to the usb device(Example). Can any one suggest which EndPoints should be used.
It seems to me that I have to implement usbmuxd in windows application. It is a multilayer protocol. There must be some libraries that implement usbmuxd(I dont think I have to implement the protocol all by my self)
I dont have much idea about iTunes communication as well as USB communication. I am adding as much information as I can(of course with the things I come up with in my R&D). Any help is highly appreciated.
public static DateTime LastDataEventDate = DateTime.Now;
public static UsbDevice MyUsbDevice;
#region SET YOUR USB Vendor and Product ID!
public static UsbDeviceFinder MyUsbFinder = new UsbDeviceFinder(1452, 4768);
#endregion
private void LibUSB()
{
ErrorCode ec = ErrorCode.None;
try
{
// Find and open the usb device.
MyUsbDevice = UsbDevice.OpenUsbDevice(MyUsbFinder);
// If the device is open and ready
if (MyUsbDevice == null)
throw new Exception("Device Not Found.");
// If this is a "whole" usb device (libusb-win32, linux libusb)
// it will have an IUsbDevice interface. If not (WinUSB) the
// variable will be null indicating this is an interface of a
// device.
IUsbDevice wholeUsbDevice = MyUsbDevice as IUsbDevice;
if (!ReferenceEquals(wholeUsbDevice, null))
{
// This is a "whole" USB device. Before it can be used,
// the desired configuration and interface must be selected.
// Select config #1
wholeUsbDevice.SetConfiguration(1);
// Claim interface #0.
wholeUsbDevice.ClaimInterface(0);
}
// open read endpoint 1.
UsbEndpointReader reader = MyUsbDevice.OpenEndpointReader(ReadEndpointID.Ep03);
// open write endpoint 1.
UsbEndpointWriter writer = MyUsbDevice.OpenEndpointWriter(WriteEndpointID.Ep02);
int bytesWritten;
ec = writer.Write(usbmux_header.GetBytes(), 2000, out bytesWritten);
if (ec != ErrorCode.None)
throw new Exception(UsbDevice.LastErrorString);
byte[] readBuffer = new byte[1024];
while (ec == ErrorCode.None)
{
int bytesRead;
// If the device hasn't sent data in the last 100 milliseconds,
// a timeout error (ec = IoTimedOut) will occur.
ec = reader.Read(readBuffer, 10000, out bytesRead);
if (ec == ErrorCode.Win32Error)
throw new Exception("port not open");
if (bytesRead == 0)
throw new Exception("No more bytes!");
// Write that output to the console.
Console.Write(Encoding.Default.GetString(readBuffer, 0, bytesRead));
}
}
catch (Exception ex)
{
Console.WriteLine();
Console.WriteLine((ec != ErrorCode.None ? ec + ":" : String.Empty) + ex.Message);
}
finally
{
if (MyUsbDevice != null)
{
if (MyUsbDevice.IsOpen)
{
// If this is a "whole" usb device (libusb-win32, linux libusb-1.0)
// it exposes an IUsbDevice interface. If not (WinUSB) the
// 'wholeUsbDevice' variable will be null indicating this is
// an interface of a device; it does not require or support
// configuration and interface selection.
IUsbDevice wholeUsbDevice = MyUsbDevice as IUsbDevice;
if (!ReferenceEquals(wholeUsbDevice, null))
{
// Release interface #0.
wholeUsbDevice.ReleaseInterface(0);
}
MyUsbDevice.Close();
}
MyUsbDevice = null;
// Free usb resources
UsbDevice.Exit();
}
}
}
class usbmux_header
{
public static UInt32 length = 10; // length of message, including header
public static UInt32 reserved = 0; // always zero
public static UInt32 type = 3; // message type
public static UInt32 tag = 2; // responses to this query will echo back this tag
public static byte[] GetBytes()
{
byte[] lgth = BitConverter.GetBytes(length);
byte[] res = BitConverter.GetBytes(reserved);
byte[] tpe = BitConverter.GetBytes(type);
byte[] tg = BitConverter.GetBytes(tag);
byte[] retArray = new byte[16];
lgth.CopyTo(retArray, 0);
res.CopyTo(retArray, 4);
tpe.CopyTo(retArray, 8);
tg.CopyTo(retArray, 12);
return retArray;
}
};
I have been trying to send hello packet bytes to iPhone but I am not able to read any response from phone.
To play with ipod you can use SharePodLib
As I understand it, only one client can use the USB connection to iOS at one time. On both macOS and Windows, that one client is usbmux. That library multiplexes TCP connections with higher-level clients, including iTunes, Photos, and (on macOS) the open-source peertalk library.
So on Windows, you wouldn't want to implement your own usbmux, but rather a client that sits on top of that, analogous to peertalk. I haven't seen anything open-source that does this, but a number of developers have accomplished it with their own proprietary software.
If anybody else has pointers about using usbmux on Windows, I'd love to hear about it.
—Dave
You can use imobiledevice-net. It provides a C# API to connect to iOS devices using your PC.
For example, to list all iOS devices connected to your PC, you would run something like this:
ReadOnlyCollection<string> udids;
int count = 0;
var idevice = LibiMobileDevice.Instance.iDevice;
var lockdown = LibiMobileDevice.Instance.Lockdown;
var ret = idevice.idevice_get_device_list(out udids, ref count);
if (ret == iDeviceError.NoDevice)
{
// Not actually an error in our case
return;
}
ret.ThrowOnError();
// Get the device name
foreach (var udid in udids)
{
iDeviceHandle deviceHandle;
idevice.idevice_new(out deviceHandle, udid).ThrowOnError();
LockdownClientHandle lockdownHandle;
lockdown.lockdownd_client_new_with_handshake(deviceHandle, out lockdownHandle, "Quamotion").ThrowOnError();
string deviceName;
lockdown.lockdownd_get_device_name(lockdownHandle, out deviceName).ThrowOnError();
deviceHandle.Dispose();
lockdownHandle.Dispose();
}

Free VRam on OS X

does anyone know how to get the free(!) vram on os x?
I know that you can query for a registry entry:
typeCode = IORegistryEntrySearchCFProperty(dspPort,kIOServicePlane,CFSTR(kIOFBMemorySizeKey),
kCFAllocatorDefault,
kIORegistryIterateRecursively | kIORegistryIterateParents);
but this will return ALL vram, not the free vram. Under windows you can query for free VRAM using directshow
mDDrawResult = DirectDrawCreate(NULL, &mDDraw, NULL);
mDDrawResult = mDDraw->QueryInterface(IID_IDirectDraw2, (LPVOID *)&mDDraw2);
DDSCAPS ddscaps;
DWORD totalmem, freemem;
ddscaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_VIDEOMEMORY;
mDDrawResult = mDDraw2->GetAvailableVidMem(&ddscaps, &totalmem, &freemem);
Ugly, but it works. Anyone knows the osx way?
Best
Wendy
answering myself so others may use this:
#include <IOKit/graphics/IOGraphicsLib.h>
size_t currentFreeVRAM()
{
kern_return_t krc;
mach_port_t masterPort;
krc = IOMasterPort(bootstrap_port, &masterPort);
if (krc == KERN_SUCCESS)
{
CFMutableDictionaryRef pattern = IOServiceMatching(kIOAcceleratorClassName);
//CFShow(pattern);
io_iterator_t deviceIterator;
krc = IOServiceGetMatchingServices(masterPort, pattern, &deviceIterator);
if (krc == KERN_SUCCESS)
{
io_object_t object;
while ((object = IOIteratorNext(deviceIterator)))
{
CFMutableDictionaryRef properties = NULL;
krc = IORegistryEntryCreateCFProperties(object, &properties, kCFAllocatorDefault, (IOOptionBits)0);
if (krc == KERN_SUCCESS)
{
CFMutableDictionaryRef perf_properties = (CFMutableDictionaryRef) CFDictionaryGetValue( properties, CFSTR("PerformanceStatistics") );
//CFShow(perf_properties);
// look for a number of keys (this is mostly reverse engineering and best-guess effort)
const void* free_vram_number = CFDictionaryGetValue(perf_properties, CFSTR("vramFreeBytes"));
if (free_vram_number)
{
ssize_t vramFreeBytes;
CFNumberGetValue( (CFNumberRef) free_vram_number, kCFNumberSInt64Type, &vramFreeBytes);
return vramFreeBytes;
}
}
if (properties) CFRelease(properties);
IOObjectRelease(object);
}
IOObjectRelease(deviceIterator);
}
}
return 0; // when we come here, this is a fail
}
i am somewhat surprised that this query takes almost 3 msec ..
be aware that there may be more than one accelerator on your system ( eg. macbook )
so be sure you select the proper one for the query

Can't edit IORegistryEntry

I am creating a software on Mac and I would like to change the value of an IORegistryEntry. I can view it on the IORegistryExplorer, but I can't edit it. So it's my understanding that I have to edit it via code. Here is my code:
CFMutableDictionaryRef matchingDict = IOServiceNameMatching("AppleUSBMultitouchDriver");
io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault, matchingDict);
if(service) {
CFStringRef manufacturer = IORegistryEntryCreateCFProperty(service, CFSTR("Manufacturer"), kCFAllocatorDefault,0);
NSLog(#"%#", (NSString*)manufacturer);
kern_return_t err = IORegistryEntrySetCFProperty(service, CFSTR("Manufacturer"), CFSTR("test"));
NSLog(#"error = %d", err);
}
This will output
2010-04-10 16:09:09.015 Test[41548:a0f] Apple Inc.
2010-04-10 16:09:09.015 Test[41548:a0f] error = 0
But after I check the value in the IORegistryExplorer, it still doesn't change. Does anybody have any suggestions?
Thank you
In order for this to be possible, usually the driver for the particular hardware you're changing has to implement setProperties() (in IOKit) that makes this change for you.
It's unlikely that Apple will implement setProperty() in their AppleUSBMultitouchDriver in a way that allows you to change the manufacturer name. They want to specify what kind of fruit they are. ;)
Use IOConnectSetCFProperties instead of IORegistryEntrySetCFProperty. Pass it a dictionary with the settings you want to set.
For example to turn off three finger swipe to navigate, call it with a dictionary containing { TrackpadThreeFingerSwipe = 0; }
This is example how to change trackpad settings properly. Trackpad.prefpane do exactly this, but also save this setting somewhere in defaults (if you will not find out where exactly, ask here about it).
P.S. getEVSHandle() may be found in MachineSettings.framework.
P.P.S. Checked only on 10.5 & 10.6.
NSInteger zero = 0, one = 1;
CFNumberRef _numberWith0 = CFNumberCreate(kCFAllocatorDefault, kCFNumberNSIntegerType, &zero);
CFNumberRef _numberWith1 = CFNumberCreate(kCFAllocatorDefault, kCFNumberNSIntegerType, &one);
CFMutableDictionaryRef propertyDict = CFDictionaryCreateMutable(kCFAllocatorDefault, 1, NULL, NULL);
CFDictionarySetValue(propertyDict, #"TrackpadThreeFingerSwipe", flag ? _numberWith1 : _numberWith0);
io_connect_t connect = getEVSHandle();
if (!connect)
{
// error
}
kern_return_t status = IOConnectSetCFProperties(connect, propertyDict);
if (status != KERN_SUCCESS)
{
//error
}
CFRelease(propertyDict);

Resources