列举 Mac 下可用的串列埠(Serial Port)

列举 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 当寻找到成立的条件时会将资料纪录并加入portIteratorportIterator这是一个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,其中CalloutDeviceDialinDevice与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就能在CalloutDeviceDialinDevice取得之后,使用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------------

参考资料

Device Access and the I/O Kit

I/O Kit Fundamentals