Lazarus

Programming => Operating Systems => macOS / Mac OS X => Topic started by: josip on April 11, 2016, 07:12:09 pm

Title: fpc OS X types / functions
Post by: josip on April 11, 2016, 07:12:09 pm
I have program written in fpc that is working just fine, executing from command line on Win / Linux / OS X. With header...

uses
  {$IFDEF WINDOWS}Windows,{$ENDIF}
  {$IFDEF UNIX}BaseUnix, Unix, termio,{$ENDIF}
  SysUtils, StrUtils, Crt;

Now, I need some OS X specific functions like IOServiceMatching, IOServiceGetMatchingServices... and some types like CFMutableDictionaryRef. Google didn't help, so I am asking here is there are something that can be added to uses statement to include this OS X specific things?
Title: Re: fpc OS X types / functions
Post by: skalogryz on April 11, 2016, 07:31:56 pm
Code: Pascal  [Select][+][-]
  1.  uses
  2.   ...
  3.   {$IFDEF DARWIN}MacOSAll,{$endif}
  4.  
Title: Re: fpc OS X types / functions
Post by: josip on April 11, 2016, 10:44:10 pm
Thank you, but something is still missing. IOService* and IORegistry* functions, things related to IOKit. And where is defined io_iterator_t (io_object_t)?
Title: Re: fpc OS X types / functions
Post by: Phil on April 11, 2016, 10:48:06 pm
MacOSAll provides access to a couple of the older C frameworks, CoreFoundation and Carbon.

CocoaAll provides access to AppKit, Foundation and Quartz, which are Objective C frameworks.

Other frameworks can be parsed using Ryan's parser:

https://github.com/genericptr/

An exception to this, unfortunately, is IOKit, which I believe is a C++ framework. You can access C functions and Objective C classes directly from FPC programs, but not C++.

Is it possible there's a command line utility that provides the information you need that you can shell to?

Thanks.

-Phil

Edit: Useful links:

https://developer.apple.com/library/mac/documentation/DeviceDrivers/Conceptual/IOKitFundamentals/Introduction/Introduction.html

https://developer.apple.com/library/mac/documentation/MacOSX/Conceptual/OSX_Technology_Overview/SystemFrameworks/SystemFrameworks.html#//apple_ref/doc/uid/TP40001067-CH210-BBCBEAJD
Title: Re: fpc OS X types / functions
Post by: josip on April 12, 2016, 12:15:14 pm
Program need to scan connected (CDC) serial devices on Win / Linux / OS X, and find the right port name (COM4, ttyACM4, cu.usbmodem1141) selected by VID / PID. I made it for Win / Linux, but on OS X this IOKit functions are needed.

I found (related to Delphi) this...
https://forums.embarcadero.com/thread.jspa?messageID=767167

but for definition like this...

const libIOKit = '/System/Library/Frameworks/IOKit.framework/IOKit';
...
function IOServiceGetMatchingService(masterPort: mach_port_t; matching: CFDictionaryRef): io_service_t; cdecl; external libIOKit name 'IOServiceGetMatchingService';
...

something is not right, because I have fpc linker error

Free Pascal Compiler version 2.6.4 [2014/02/26] for i386
Copyright (c) 1993-2014 by Florian Klaempfl and others
Target OS: Darwin for i386
Compiling fcd.pas
Assembling (pipe) fcd.s
Linking flash
Undefined symbols for architecture i386:
  "_IOServiceGetMatchingServices", referenced from:
      _P$FCD_CDCPORT$SHORTSTRING$$BOOLEAN in fcd.o
  "_IOServiceMatching", referenced from:
      _P$FCD_CDCPORT$SHORTSTRING$$BOOLEAN in fcd.o
ld: symbol(s) not found for architecture i386
An error occurred while linking
Title: Re: fpc OS X types / functions
Post by: Thaddy on April 12, 2016, 12:34:02 pm
First and foremost: update your compiler to a supported one, like (stable) fpc 3.0.0. and not 2.6.4.
Still doesn't work? report back!
Title: Re: fpc OS X types / functions
Post by: bee on April 12, 2016, 12:39:05 pm
What's the version of OS X you're using? I suppose all OS X and Mac today are 64 bit. Unless you're using an old Mac and OS X. If it's indeed 64 bit, then you should use the 64 bit version of FPC as well.
Title: Re: fpc OS X types / functions
Post by: josip on April 12, 2016, 04:39:21 pm
I am running 64-bit kernel. Calling free pascal compiler in OS X terminal by "fpc" is somehow linked to i386 version.

Mac: root# ppcx64 fcd.pas
Free Pascal Compiler version 2.6.4 [2014/02/26] for x86_64
Copyright (c) 1993-2014 by Florian Klaempfl and others
Target OS: Darwin for x86_64
Compiling fcd.pas
Assembling (pipe) fcd.s
Linking fcd
Undefined symbols for architecture x86_64:
  "_IOServiceGetMatchingServices", referenced from:
      _P$FCD_CDCPORT$SHORTSTRING$$BOOLEAN in fcd.o
  "_IOServiceMatching", referenced from:
      _P$FCD_CDCPORT$SHORTSTRING$$BOOLEAN in fcd.o
ld: symbol(s) not found for architecture x86_64
An error occurred while linking


I have installed 2.6.4 that is last stable version before 3.0. I can install 3.0, but don't know how this is related to IOKit (that is older than any fpc version).
Title: Re: fpc OS X types / functions
Post by: skalogryz on April 12, 2016, 06:05:34 pm
try not to specify libIOKit explicitly.
Code: Pascal  [Select][+][-]
  1. function IOServiceGetMatchingService(masterPort: mach_port_t;
  2.   matching: CFDictionaryRef
  3.   ): io_service_t; cdecl; external;
  4.  

instead, use linkframe work directive (adding it to the beginning of the unit)
Code: Pascal  [Select][+][-]
  1. {$linkframework IOKit}
  2.  

However, if the function is exported as C++ function the default generated name will not work.
Title: Re: fpc OS X types / functions
Post by: josip on April 13, 2016, 11:04:41 am
Thanks, skalogryz, there is no linking error any more.

I need to translate (part of) something like this from C to FPC.
https://delog.wordpress.com/2012/04/27/access-usb-device-on-mac-os-x-using-io-kit/

Code: C  [Select][+][-]
  1. ...
  2.     CFMutableDictionaryRef matchingDictionary = NULL;
  3.     SInt32 idVendor = 0x0000; // set vendor id
  4.     SInt32 idProduct = 0x0000; // set product id
  5.     io_iterator_t iterator = 0;
  6.     io_service_t usbRef;
  7. ...
  8.     matchingDictionary = IOServiceMatching(kIOUSBDeviceClassName);
  9.     CFDictionaryAddValue(matchingDictionary, CFSTR(kUSBVendorID),
  10.         CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &idVendor));
  11.     CFDictionaryAddValue(matchingDictionary, CFSTR(kUSBProductID),
  12.          CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &idProduct));
  13.     IOServiceGetMatchingServices(kIOMasterPortDefault, matchingDictionary, &iterator);
  14.     usbRef = IOIteratorNext(iterator);
  15.     if (usbRef == 0) printf("Device not found\n");
  16.     IOObjectRelease(iterator);
  17. ...
  18.  

Code: Pascal  [Select][+][-]
  1. {$linkframework IOKit}
  2.  
  3. type
  4.   io_object_t = mach_port_t;
  5.   io_iterator_t = io_object_t;
  6.  
  7. const
  8.   kIOMasterPortDefault: mach_port_t = 0;
  9.  
  10.   kIOUSBDeviceClassName = 'IOUSBDevice';
  11.   kUSBVendorID = 'idVendor';
  12.   kUSBProductID = 'idProduct';
  13.  
  14. function IOServiceMatching(name: PChar): CFMutableDictionaryRef; cdecl; external;
  15.  
  16. function IOServiceGetMatchingServices(masterPort: mach_port_t; matching: CFDictionaryRef;
  17.   existing: io_iterator_t): kern_return_t; cdecl; external;
  18.  
  19. function IOIteratorNext(name: io_iterator_t): io_object_t; cdecl; external:
  20.  
  21. function FindPort: Boolean;
  22. var
  23.   matchingDictionary: CFMutableDictionaryRef;
  24.   iterator: io_iterator_t;
  25. begin
  26.   matchingDictionary := nil;
  27.   iterator := 0;
  28.   matchingDictionary := IOServiceMatching(kIOUSBDeviceClassName);
  29.   //CFDictionaryAddValue(matchingDictionary, CFSTR(kUSBVendorID),
  30.   //  CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, PChar('1234')));
  31.   //CFDictionaryAddValue(matchingDictionary, CFSTR(kUSBProductID),
  32.   //  CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, PChar('5678')));
  33.   IOServiceGetMatchingServices(kIOMasterPortDefault, matchingDictionary, iterator);
  34. ...
  35.  

I guess that link with IOKit is OK, because IOServiceMatching is executed, but something is wrong with IOServiceGetMatchingServices because code is crashed there.

An unhandled exception occurred at $9BB2F16E : EAccessViolation : Access violation
$9BB2F16E $9BACD294 $00011CF2
Title: Re: fpc OS X types / functions
Post by: skalogryz on April 13, 2016, 02:03:40 pm
btw, IOServiceGetMatchingServices should be declared as following:
Code: Pascal  [Select][+][-]
  1. function IOServiceGetMatchingServices(masterPort: mach_port_t;
  2.   matching: CFDictionaryRef;
  3.   var existing: io_iterator_t
  4. ): kern_return_t; cdecl; external;
  5.  
Note "var" in front of "existing".

It might resolve the issue. The function expects a reference to the variable. Without "var" the function accepts zero (nil) as the reference. Which might cause the AV. 
OSX APIs rarely do any sanity check, though they probably should.
Title: Re: fpc OS X types / functions
Post by: josip on April 14, 2016, 04:49:50 pm
Thank you. Eveything is working now, on Lion (10.7.5) and Mavericks (10.9.5), compiled with 32-bit or 64-bit FPC. Here is part of IOKit that was needed for my program.

Code: Pascal  [Select][+][-]
  1. uses MacOSAll
  2.  
  3. {$linkframework IOKit}
  4.  
  5. //IOKit = '/System/Library/Frameworks/IOKit.framework/IOKit';
  6.  
  7. type
  8.  
  9.   io_name_t = array[0..128] of Char;
  10.   io_object_t = mach_port_t;
  11.   io_iterator_t = io_object_t;
  12.   io_service_t = io_object_t;
  13.   io_registry_entry_t = io_object_t;
  14.   IOOptionBits = UInt32;
  15.  
  16. const
  17.  
  18.   kIOMasterPortDefault: mach_port_t = 0;
  19.   kIOSerialBSDServiceValue = 'IOSerialBSDClient';
  20.   kIOSerialBSDTypeKey      = 'IOSerialBSDClientType';
  21.   kIOSerialBSDModemType    = 'IOModemSerialStream';
  22.   kIOUSBDeviceClassName    = 'IOUSBDevice';
  23.   kUSBVendorID             = 'idVendor';
  24.   kUSBProductID            = 'idProduct';
  25.   kIOTTYDeviceKey          = 'IOTTYDevice';
  26.   kIOCalloutDeviceKey      = 'IOCalloutDevice';
  27.   kIOServicePlane          = 'IOService';
  28.   kUSBInterfaceNumber      = 'bInterfaceNumber';
  29.  
  30. function IOServiceMatching(name: PChar): CFMutableDictionaryRef; cdecl; external;
  31.  
  32. function IOServiceGetMatchingServices(masterPort: mach_port_t; matching: CFDictionaryRef;
  33.   var existing: io_iterator_t): kern_return_t; cdecl; external;
  34.  
  35. function IOIteratorNext(name: io_iterator_t): io_object_t; cdecl; external;
  36.  
  37. function IORegistryEntryCreateCFProperty(entry: io_registry_entry_t; key: CFStringRef;
  38.   allocator: CFAllocatorRef; options: IOOptionBits): CFTypeRef; cdecl; external;
  39.  
  40. function IORegistryEntryGetName(entry: io_registry_entry_t; var name: io_name_t):
  41.   kern_return_t; cdecl; external;
  42.  
  43. function IORegistryEntryGetParentEntry(entry: io_registry_entry_t; plane: io_name_t;
  44.   var parent: io_registry_entry_t): kern_return_t; cdecl; external;
  45.  
  46. function IORegistryEntrySearchCFProperty(entry: io_registry_entry_t; plane: io_name_t;
  47.   key: CFStringRef; allocator: CFAllocatorRef; options: IOOptionBits): CFTypeRef; cdecl; external;
  48.  
  49. function IOObjectRelease(object_: io_object_t): kern_return_t; cdecl; external;
  50.  
Title: Re: fpc OS X types / functions
Post by: skalogryz on April 14, 2016, 05:26:43 pm
Great job! any plans on publishing IOKit headers?
Title: Re: fpc OS X types / functions
Post by: Hansaplast on March 16, 2018, 02:07:26 pm
I know this topic is already 2+ years old, so there is a good chance that I'm not getting a reply.
I'm struggling with IOKit at the moment as well, and it's not a very well covered topic (either with Mr. Google or in this forum).


@Josip; Well done!
Would you be able to post some example code on how to use these functions?
It would be very much appreciated  :)
Title: Re: fpc OS X types / functions
Post by: Phil on March 16, 2018, 03:45:57 pm
I'm struggling with IOKit at the moment as well, and it's not a very well covered topic (either with Mr. Google or in this forum).

You mean other than Apple's docs?

https://developer.apple.com/documentation/iokit/iokitlib.h
Title: Re: fpc OS X types / functions
Post by: Hansaplast on March 17, 2018, 01:02:23 pm
Yeah I did dig through the Apple docs, but I'm definitely struggling with that as well.
Or maybe I should say that doing a 1:1 in Lazarus has proven to be challenging for me  :D


I'm trying to list all drives and read/write straight to drives (kind-a like "dd") without using command line options. I've gone the command line route before and noticed that the output of certain statements change per different macOS release (for example the output of diskutil) - so not the most reliable way. So I consider going the "official" route but at times it's a little over my head.
Title: Re: fpc OS X types / functions
Post by: josip on April 23, 2018, 02:43:53 pm
Would you be able to post some example code on how to use these functions?
It would be very much appreciated  :)

I use it for connected USB device detection by VID / PID. It is working fine till then, but I forgot coding details.
Code: [Select]
{$linkframework IOKit}

//IOKit = '/System/Library/Frameworks/IOKit.framework/IOKit';

type

  io_name_t = array[0..128] of Char;
  io_object_t = mach_port_t;
  io_iterator_t = io_object_t;
  io_service_t = io_object_t;
  io_registry_entry_t = io_object_t;
  IOOptionBits = UInt32;

const

  kIOSerialBSDServiceValue = 'IOSerialBSDClient';
  kIOSerialBSDTypeKey    = 'IOSerialBSDClientType';
  kIOSerialBSDModemType    = 'IOModemSerialStream';
  kIOUSBDeviceClassName    = 'IOUSBDevice';
  kUSBVendorID             = 'idVendor';
  kUSBProductID            = 'idProduct';
  kIOTTYDeviceKey    = 'IOTTYDevice';
  kIOCalloutDeviceKey    = 'IOCalloutDevice';
  kIOServicePlane    = 'IOService';
  kUSBInterfaceNumber      = 'bInterfaceNumber';

function IOServiceMatching(name: PChar): CFMutableDictionaryRef; cdecl; external;

function IOServiceGetMatchingServices(masterPort: mach_port_t; matching: CFDictionaryRef;
  var existing: io_iterator_t): kern_return_t; cdecl; external;

function IOIteratorNext(name: io_iterator_t): io_object_t; cdecl; external;

function IORegistryEntryCreateCFProperty(entry: io_registry_entry_t; key: CFStringRef;
  allocator: CFAllocatorRef; options: IOOptionBits): CFTypeRef; cdecl; external;

function IORegistryEntryGetName(entry: io_registry_entry_t; var name: io_name_t): kern_return_t; cdecl; external;

function IORegistryEntryGetParentEntry(entry: io_registry_entry_t; plane: io_name_t;
  var parent: io_registry_entry_t): kern_return_t; cdecl; external;

function IORegistryEntrySearchCFProperty(entry: io_registry_entry_t; plane: io_name_t;
  key: CFStringRef; allocator: CFAllocatorRef; options: IOOptionBits): CFTypeRef; cdecl; external;

function IOObjectRelease(object_: io_object_t): kern_return_t; cdecl; external;


function RegName(FEntry: io_registry_entry_t): string;
var
  FName: io_name_t;
begin
  FillChar(FName, SizeOf(FName), 0);
  IORegistryEntryGetName(FEntry, FName);
  RegName := StrPas(PChar(@FName));
end;


function RegStr(FEntry: io_registry_entry_t; FKey: PChar): string;
var
  FBuf: array[0..255] of Char;
  FCFStr: CFTypeRef;
  FStr: string;
begin
  FCFStr := IORegistryEntryCreateCFProperty(FEntry, CFSTR(FKey), kCFAllocatorDefault, 0);
  FillChar(FBuf, SizeOf(FBuf), 0);
  CFStringGetPascalString(FCFStr, @FBuf[0], 256, CFStringGetSystemEncoding);
  FStr := StrPas(PChar(@FBuf));
  FStr := RightStr(FStr, Length(FStr) - 1);
  RegStr := FStr;
end;


function RegNum(FEntry: io_registry_entry_t; FKey: PChar; var FRes: Int): Boolean;
var
  FCFNumber: CFTypeRef;
  FNum: SInt32;
begin
  RegNum := False;
  FCFNumber := IORegistryEntrySearchCFProperty(FEntry, kIOServicePlane, CFSTR(FKey), kCFAllocatorDefault, 0);
  if FCFNumber <> nil then
  begin
    CFNumberGetValue(FCFNumber, kCFNumberSInt32Type, @FNum);
    FRes := FNum;
    RegNum := True;
  end;
end;


function CdcPort: Boolean;
var
  FMatchDic: CFMutableDictionaryRef;
  FIterator: io_iterator_t;
  FDevice, FDeviceOld: io_registry_entry_t;
  FGetParentRes: kern_return_t;
  FVid, FPid, FIntNum: Int;
  FPortName, FPortPath: string;
begin
  CdcPort := False;

  FMatchDic := IOServiceMatching(kIOSerialBSDServiceValue);
  CFDictionarySetValue(FMatchDic, CFSTR(kIOSerialBSDTypeKey), CFSTR(kIOSerialBSDModemType));

  //if IOServiceGetMatchingServices(kIOMasterPortDefault, FMatchDic, FIterator) = kIOReturnSuccess then
  if IOServiceGetMatchingServices(0, FMatchDic, FIterator) = kIOReturnSuccess then
    while True do
    begin
      FDevice := IOIteratorNext(FIterator);
      if FDevice = 0 then break;

      FPortName := RegStr(FDevice, kIOTTYDeviceKey);
      if LeftStr(FPortName, 8) = 'usbmodem' then
      begin
        FPortPath := RegStr(FDevice, kIOCalloutDeviceKey);
        //WriteLn(FPortName + ':' + FPortPath);
        //WriteLn(RegName(FDevice));
        FVid := -1;
        FGetParentRes := kIOReturnSuccess;
        while (FVid < 0) and (FGetParentRes = kIOReturnSuccess) do
        begin
          //WriteLn(RegName(FDevice));
          FDeviceOld := FDevice;
          FGetParentRes := IORegistryEntryGetParentEntry(FDevice, kIOServicePlane, FDevice);
          if IOObjectRelease(FDeviceOld) = kIOReturnSuccess then ; //WriteLn('*')
          if FGetParentRes = kIOReturnSuccess then
          begin
            RegNum(FDevice, kUSBVendorID, FVid);
            RegNum(FDevice, kUSBProductID, FPid);
    RegNum(FDevice, kUSBInterfaceNumber, FIntNum);
          end;
        end;
        //WriteLn(IntToHex(FVid, 4) + ' ' + IntToHex(FPid, 4) + ' ' + IntToHex(FIntNum, 4));
        if (FVid = $XXXX) and ((FPid = $YYYY) or (FPid = $ZZZZ)) and (FIntNum = 1) then
        begin
          GPortLen := GPortLen + 1;
          GPortName[GPortLen] := FPortName;
          GPortPath[GPortLen] := FPortPath;
          if AnsiCompareText(GPortName[0], GPortName[GPortLen]) = 0 then CdcPort := True;
        end;
      end;
  end;
end;
Title: Re: fpc OS X types / functions
Post by: Hansaplast on April 23, 2018, 04:22:08 pm
Awesome! Thanks Josip!  :)
TinyPortal © 2005-2018