列舉 Mac 下可用的串列埠(Serial Port)
I/O 瀏覽工具
Mac下使用Objective-C來了解硬體資訊必需要依靠 IOKit Framework ,如果在還未寫程式時也可以利用 IORegistry Explorer 看一下硬體資訊,要取得該工具程式的方法很多,如果您是開發者更可以在Downloads for Apple Developer取得工具 Hardwre Tools for Xcode ,其中內建許多硬體有關的資訊,包含了 IORegistry Explorer。
尋找可用的Serial Port
還未寫程式之前,使用IORegistry Explorer 尋找可用的Serial Port,Mac上規範如果你的驅動是Serial Port的話,它的IOClass
必需為IOSerilaBSDClient
,利用此特性將尋找的關鍵字定為IOSerialBSDClient
:
上圖中能看到符合的條件的數量有3,如此一來就取得可用的Serial Port,接下來就要使用IOKit Framework來完成。
使用IOKit
IOKit Framework 必需要先引用標頭檔:
//-----------start----------- #import <IOKit/IOKitLib.h> #import <IOKit/serial/IOSerialKeys.h> //------------end------------
依照先前工具操作方式,尋找IOSerialBSDClient
的程式如下:
//-----------start----------- CFMutableDictionaryRef keywordDict = IOServiceMatching(kIOSerialBSDServiceValue); // io_object_t port; io_iterator_t portIterator = 0; kern_return_t result = IOServiceGetMatchingServices(kIOMasterPortDefault, keywordDict, &portIterator); if (result){ NSLog(@"Error getting for serial port"); return; } //------------end------------
- IOServiceMatching 使用它來產生要被尋找的關鍵字,它以字典的型式,所以可以輸入多個條件
- io_iterator_t portIterator 存放列舉資訊的變數
- IOServiceGetMatchingServices 當尋找到成立的條件時會將資料紀錄並加入
portIterator
,portIterator
這是一個Iterator結構,必需要使用它提供的Function將內容列舉。
上述程式最後確認result沒問題的話就不會有Error getting for serial port
訊息,之後使用portIterator
將符合的條件內容顯示:
//-----------start----------- while ((port = IOIteratorNext(portIterator))) { CFStringRef stringIOTTYBaseNameKey = (CFStringRef)IORegistryEntryCreateCFProperty(port, (__bridge CFStringRef)(NSString*)CFSTR(kIOTTYBaseNameKey), kCFAllocatorDefault, 0); CFStringRef stringIOSerialBSDTypeKey = (CFStringRef)IORegistryEntryCreateCFProperty(port, (__bridge CFStringRef)(NSString*)CFSTR(kIOSerialBSDTypeKey), kCFAllocatorDefault, 0); CFStringRef stringIOCalloutDeviceKey = (CFStringRef)IORegistryEntryCreateCFProperty(port, (__bridge CFStringRef)(NSString*)CFSTR(kIOCalloutDeviceKey), kCFAllocatorDefault, 0); CFStringRef stringIODialinDeviceKey = (CFStringRef)IORegistryEntryCreateCFProperty(port, (__bridge CFStringRef)(NSString*)CFSTR(kIODialinDeviceKey), kCFAllocatorDefault, 0); NSLog(@"\r Name:%@ \r SerialBSDTypeKey:%@ \r CalloutDevice:%@ \r DialinDevice:%@",stringIOTTYBaseNameKey,stringIOSerialBSDTypeKey,stringIOCalloutDeviceKey,stringIODialinDeviceKey); // CFRelease(stringIOTTYBaseNameKey); CFRelease(stringIOSerialBSDTypeKey); CFRelease(stringIOCalloutDeviceKey); CFRelease(stringIODialinDeviceKey); IOObjectRelease(port); } //------------end------------
- IOIteratorNext 檢查並取得符合條件的物件,在這裡因為取得的都是
值
,還需要利用一些相關的Function取得資訊 - IORegistryEntryCreateCFProperty 取得的物件後,在其中取得所指定的資訊並會轉換成Objective-C的NSString,上面例子取用四個欄位的資訊
依照<IOKit/serial/IOSerialKeys.h>
所提供能取得的資訊欄位如下:
/* Service Matching That is the 'IOProviderClass' */ #define kIOSerialBSDServiceValue "IOSerialBSDClient" /* Matching keys */ #define kIOSerialBSDTypeKey "IOSerialBSDClientType" /* Currently possible kIOSerialBSDTypeKey values. */ #define kIOSerialBSDAllTypes "IOSerialStream" #define kIOSerialBSDModemType "IOModemSerialStream" #define kIOSerialBSDRS232Type "IORS232SerialStream" // Properties that resolve to a /dev device node to open for // a particular service #define kIOTTYDeviceKey "IOTTYDevice" #define kIOTTYBaseNameKey "IOTTYBaseName" #define kIOTTYSuffixKey "IOTTYSuffix" #define kIOCalloutDeviceKey "IOCalloutDevice" #define kIODialinDeviceKey "IODialinDevice" // Property 'ioctl' wait for the tty device to go idle. #define kIOTTYWaitForIdleKey "IOTTYWaitForIdle"
最後程式執行的結果:
2014-05-05 17:13:35.924 GetSerialBSDService[29208:303] Name:Bluetooth-Incoming-Port SerialBSDTypeKey:IORS232SerialStream CalloutDevice:/dev/cu.Bluetooth-Incoming-Port DialinDevice:/dev/tty.Bluetooth-Incoming-Port 2014-05-05 17:13:35.925 GetSerialBSDService[29208:303] Name:Bluetooth-Modem SerialBSDTypeKey:IOModemSerialStream CalloutDevice:/dev/cu.Bluetooth-Modem DialinDevice:/dev/tty.Bluetooth-Modem 2014-05-05 17:13:35.925 GetSerialBSDService[29208:303] Name:HOLUX_M-241-SPPSlave SerialBSDTypeKey:IORS232SerialStream CalloutDevice:/dev/cu.HOLUX_M-241-SPPSlave DialinDevice:/dev/tty.HOLUX_M-241-SPPSlave 2014-05-05 17:13:35.926 GetSerialBSDService[29208:303] Name:usbserial SerialBSDTypeKey:IORS232SerialStream CalloutDevice:/dev/cu.usbserial DialinDevice:/dev/tty.usbserial
以測試環境中能看到執行結果列舉了4個Serial Port,其中CalloutDevice
、DialinDevice
與Linux類似結構,它就是Serial週邊存取的裝置名稱,我們執行命令查看一下:
ls -l /dev/cu* tty.*
命令結果:
crw-rw-rw- 1 root wheel 17, 1 5 5 09:52 /dev/cu.Bluetooth-Incoming-Port crw-rw-rw- 1 root wheel 17, 3 5 5 09:52 /dev/cu.Bluetooth-Modem crw-rw-rw- 1 root wheel 17, 5 5 5 09:52 /dev/cu.HOLUX_M-241-SPPSlave crw-rw-rw- 1 root wheel 17, 13 5 5 16:30 /dev/cu.usbserial crw-rw-rw- 1 root wheel 17, 0 5 5 09:52 /dev/tty.Bluetooth-Incoming-Port crw-rw-rw- 1 root wheel 17, 2 5 5 09:52 /dev/tty.Bluetooth-Modem crw-rw-rw- 1 root wheel 17, 4 5 5 09:52 /dev/tty.HOLUX_M-241-SPPSlave crw-rw-rw- 1 root wheel 17, 12 5 5 16:30 /dev/tty.usbserial
如果你要使用Serial Port就能在CalloutDevice
、DialinDevice
取得之後,使用Open/Read/Write的檔案存取命令來對Serial Port操作。
程式內容:
//-----------start----------- - (void)runTest1 { CFMutableDictionaryRef keywordDict = IOServiceMatching(kIOSerialBSDServiceValue); // io_object_t port; io_iterator_t portIterator = 0; kern_return_t result = IOServiceGetMatchingServices(kIOMasterPortDefault, keywordDict, &portIterator); NSLog(@"%i",result); if (result){ NSLog(@"Error getting for serial port"); return; } while ((port = IOIteratorNext(portIterator))) { CFStringRef stringIOTTYBaseNameKey = (CFStringRef)IORegistryEntryCreateCFProperty(port, (__bridge CFStringRef)(NSString*)CFSTR(kIOTTYBaseNameKey), kCFAllocatorDefault, 0); CFStringRef stringIOSerialBSDTypeKey = (CFStringRef)IORegistryEntryCreateCFProperty(port, (__bridge CFStringRef)(NSString*)CFSTR(kIOSerialBSDTypeKey), kCFAllocatorDefault, 0); CFStringRef stringIOCalloutDeviceKey = (CFStringRef)IORegistryEntryCreateCFProperty(port, (__bridge CFStringRef)(NSString*)CFSTR(kIOCalloutDeviceKey), kCFAllocatorDefault, 0); CFStringRef stringIODialinDeviceKey = (CFStringRef)IORegistryEntryCreateCFProperty(port, (__bridge CFStringRef)(NSString*)CFSTR(kIODialinDeviceKey), kCFAllocatorDefault, 0); NSLog(@"\r Name:%@ \r SerialBSDTypeKey:%@ \r CalloutDevice:%@ \r DialinDevice:%@",stringIOTTYBaseNameKey,stringIOSerialBSDTypeKey,stringIOCalloutDeviceKey,stringIODialinDeviceKey); // CFRelease(stringIOTTYBaseNameKey); CFRelease(stringIOSerialBSDTypeKey); CFRelease(stringIOCalloutDeviceKey); CFRelease(stringIODialinDeviceKey); IOObjectRelease(port); } //CFRelease(keywordDict); } //------------end------------