您的位置:首页 > 其它

mac下串口通讯工具的编写

2014-09-12 11:10 375 查看
ORSSerialPort.h

#import <Foundation/Foundation.h>
#import <IOKit/IOTypes.h>
#import <termios.h>

//#define LOG_SERIAL_PORT_ERRORS

enum {
ORSSerialPortParityNone = 0,
ORSSerialPortParityOdd,
ORSSerialPortParityEven
}; typedef NSUInteger ORSSerialPortParity;

@protocol ORSSerialPortDelegate;

/**
*  The ORSSerialPort class represents a serial port, and includes methods to
*  configure, open and close a port, and send and receive data to and from
*  a port.
*
*  There is a 1:1 correspondence between port devices on the
*  system and instances of `ORSSerialPort`. That means that repeated requests
*  for a port object for a given device or device path will return the same
*  instance of `ORSSerialPort`.
*
*  Opening a Port and Setting It Up
*  --------------------------------
*
*  You can get an `ORSSerialPort` instance either of two ways. The easiest
*  is to use `ORSSerialPortManager`'s `availablePorts` array. The other way
*  is to get a new `ORSSerialPort` instance using the serial port's BSD device path:
*
*  	ORSSerialPort *port = [ORSSerialPort serialPortWithPath:@"/dev/cu.KeySerial1"];
*
*  Note that you must give `+serialPortWithPath:` the full path to the
*  device, as shown in the example above.
*
*
*  After you've got a port instance, you can open it with the `-open`
*  method. When you're done using the port, close it using the `-close`
*  method.
*
*  Port settings such as baud rate, number of stop bits, parity, and flow
*  control settings can be set using the various properties `ORSSerialPort`
*  provides. Note that all of these properties are Key Value Observing
*  (KVO) compliant. This KVO compliance also applies to read-only
*  properties for reading the state of the CTS, DSR and DCD pins. Among
*  other things, this means it's easy to be notified when the state of one
*  of these pins changes, without having to continually poll them, as well
*  as making them easy to connect to a UI with Cocoa bindings.
*
*  Sending Data
*  ------------
*
*  Send data by passing an `NSData` object to the `-sendData:` method:
*
*  	NSData *dataToSend = [self.sendTextField.stringValue dataUsingEncoding:NSUTF8StringEncoding];
*  	[self.serialPort sendData:dataToSend];
*
*  Receiving Data
*  --------------
*
*  To receive data, you must implement the `ORSSerialPortDelegate`
*  protocol's `-serialPort:didReceiveData:` method, and set the
*  `ORSSerialPort` instance's delegate property. As noted in the documentation
*  for ORSSerialPortDelegate, this method is always called on the main queue.
*  An example implementation is included below:
*
*  	- (void)serialPort:(ORSSerialPort *)serialPort didReceiveData:(NSData *)data
*  	{
*  		NSString *string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
*  		[self.receivedDataTextView.textStorage.mutableString appendString:string];
*  		[self.receivedDataTextView setNeedsDisplay:YES];
*  	}
*/

@interface ORSSerialPort : NSObject

/** ---------------------------------------------------------------------------------------
* @name Getting a Serial Port
*  ---------------------------------------------------------------------------------------
*/

/**
*  Returns an `ORSSerialPort` instance representing the serial port at `devicePath`.
*
*  `devicePath` must be the full, callout (cu.) or tty (tty.) path to an available
*  serial port device on the system.
*
*  @param devicePath The full path (e.g. /dev/cu.usbserial) to the device.
*
*  @return An initalized `ORSSerialPort` instance, or nil if there was an error.
*
*  @see -[ORSSerialPortManager availablePorts]
*  @see -initWithPath:
*/
+ (ORSSerialPort *)serialPortWithPath:(NSString *)devicePath;

/**
*  Returns an `ORSSerialPort` instance for the serial port represented by `device`.
*
*  Generally, `+serialPortWithPath:` is the method to use to get port instances
*  programatically. This method may be useful if you're doing your own
*  device discovery with IOKit functions, or otherwise have an IOKit port object
*  you want to "turn into" an ORSSerialPort. Most people will not use this method
*  directly.
*
*  @param device An IOKit port object representing the serial port device.
*
*  @return An initalized `ORSSerialPort` instance, or nil if there was an error.
*
*  @see -[ORSSerialPortManager availablePorts]
*  @see +serialPortWithPath:
*/
+ (ORSSerialPort *)serialPortWithDevice:(io_object_t)device;

/**
*  Returns an `ORSSerialPort` instance representing the serial port at `devicePath`.
*
*  `devicePath` must be the full, callout (cu.) or tty (tty.) path to an available
*  serial port device on the system.
*
*  @param devicePath The full path (e.g. /dev/cu.usbserial) to the device.
*
*  @return An initalized `ORSSerialPort` instance, or nil if there was an error.
*
*  @see -[ORSSerialPortManager availablePorts]
*  @see +serialPortWithPath:
*/
- (id)initWithPath:(NSString *)devicePath;

/**
*  Returns an `ORSSerialPort` instance for the serial port represented by `device`.
*
*  Generally, `-initWithPath:` is the method to use to get port instances
*  programatically. This method may be useful if you're doing your own
*  device discovery with IOKit functions, or otherwise have an IOKit port object
*  you want to "turn into" an ORSSerialPort. Most people will not use this method
*  directly.
*
*  @param device An IOKit port object representing the serial port device.
*
*  @return An initalized `ORSSerialPort` instance, or nil if there was an error.
*
*  @see -[ORSSerialPortManager availablePorts]
*  @see -initWithPath:
*/
- (id)initWithDevice:(io_object_t)device;

/** ---------------------------------------------------------------------------------------
* @name Opening and Closing
*  ---------------------------------------------------------------------------------------
*/

/**
*  Opens the port represented by the receiver.
*
*  If this method succeeds, the ORSSerialPortDelegate method `-serialPortWasOpened:` will
*  be called.
*
*  If opening the port fails, the ORSSerialPortDelegate method `-serialPort:didEncounterError:` will
*  be called.
*/
- (BOOL)open;

/**
*  Closes the port represented by the receiver.
*
*  If the port is closed successfully, the ORSSerialPortDelegate method `-serialPortWasClosed:` will
*  be called before this method returns.
*
*  @return YES if closing the port was closed successfully, NO if closing the port failed.
*/
- (BOOL)close;

- (void)cleanup DEPRECATED_ATTRIBUTE; // Should never have been called in client code, anyway.

/**
*  Closes the port and cleans up.
*
*  This method should never be called directly. Call `-close` to close a port instead.
*/
- (void)cleanupAfterSystemRemoval;

/** ---------------------------------------------------------------------------------------
* @name Sending Data
*  ---------------------------------------------------------------------------------------
*/

/**
*  Sends data out through the serial port represented by the receiver.
*
*  This method attempts to send all data synchronously. If the serial port
*  is unable to accept all the passed in data in a single write operation,
*  The remaining data is buffered and sent later asynchronously.
*
*  If an error occurs, the ORSSerialPortDelegate method `-serialPort:didEncounterError:` will
*  be called. The exception to this is if sending data fails because the port
*  is closed. In that case, this method returns NO, but `-serialPort:didEncounterError:`
*  is *not* called. You can ensure that the port is open by calling `-isOpen` before
*  calling this method.
*
*  @param data An `NSData` object containing the data to be sent.
*
*  @return YES if sending data failed, NO if an error occurred.
*/
- (BOOL)sendData:(NSData *)data;

/** ---------------------------------------------------------------------------------------
* @name Delegate
*  ---------------------------------------------------------------------------------------
*/

/**
*  The delegate for the serial port object. Must implement the `ORSSerialPortDelegate` protocol.
*
*/
@property (nonatomic, unsafe_unretained) id<ORSSerialPortDelegate> delegate;

/** ---------------------------------------------------------------------------------------
* @name Port Object Properties
*  ---------------------------------------------------------------------------------------
*/

/**
*  A Boolean value that indicates whether the port is open. (read-only)
*/
@property (readonly, getter = isOpen) BOOL open;

/**
*  An string representation of the device path for the serial port represented by the receiver. (read-only)
*/
@property (copy, readonly) NSString *path;

/**
*  The IOKit port object for the serial port device represented by the receiver. (read-only)
*/
@property (readonly) io_object_t IOKitDevice;

/**
*  The name of the serial port.
*
*  Can be presented to the user, e.g. in a serial port selection pop up menu.
*/
@property (copy, readonly) NSString *name;

/** ---------------------------------------------------------------------------------------
* @name Configuring the Serial Port
*  ---------------------------------------------------------------------------------------
*/

/**
*  The baud rate for the port.
*
*  This value should be one of the values defined in termios.h:
*
*	- 0
*	- 50
*	- 75
*	- 110
*	- 134
*	- 150
*	- 200
*	- 300
*	- 600
*	- 1200
*	- 1800
*	- 2400
*	- 4800
*	- 9600
*	- 19200
*	- 38400
*	- 7200
*	- 14400
*	- 28800
*	- 57600
*	- 76800
*	- 115200
*	- 230400
*	- 19200
*	- 38400
*/
@property (nonatomic, copy) NSNumber *baudRate;

/**
*  The number of stop bits. Values other than 1 or 2 are invalid.
*/
@property (nonatomic) NSUInteger numberOfStopBits;

/**
*
*/
@property (nonatomic) BOOL shouldEchoReceivedData;

/**
*  The parity setting for the port. Possible values are:
*
*  - ORSSerialPortParityNone
*  - ORSSerialPortParityOdd
*  - ORSSerialPortParityEven
*/
@property (nonatomic) ORSSerialPortParity parity;

/**
*  A Boolean value indicating whether the serial port uses RTS/CTS Flow Control.
*/
@property (nonatomic) BOOL usesRTSCTSFlowControl;

/**
*  A Boolean value indicating whether the serial port uses DTR/DSR Flow Control.
*/
@property (nonatomic) BOOL usesDTRDSRFlowControl;

/**
*  A Boolean value indicating whether the serial port uses DCD Flow Control.
*/
@property (nonatomic) BOOL usesDCDOutputFlowControl;

/** ---------------------------------------------------------------------------------------
* @name Other Port Pins
*  ---------------------------------------------------------------------------------------
*/

/**
*  The state of the serial port's RTS pin.
*
*  - YES means 1 or high state.
*  - NO means 0 or low state.
*
*  This property is observable using Key Value Observing.
*/
@property (nonatomic) BOOL RTS;

/**
*  The state of the serial port's DTR pin.
*
*  - YES means 1 or high state.
*  - NO means 0 or low state.
*
*  This property is observable using Key Value Observing.
*/
@property (nonatomic) BOOL DTR;

/**
*  The state of the serial port's CTS pin.
*
*  - YES means 1 or high state.
*  - NO means 0 or low state.
*
*  This property is observable using Key Value Observing.
*/
@property (nonatomic, readonly) BOOL CTS;

/**
*  The state of the serial port's DSR pin. (read-only)
*
*  - YES means 1 or high state.
*  - NO means 0 or low state.
*
*  This property is observable using Key Value Observing.
*/
@property (nonatomic, readonly) BOOL DSR;

/**
*  The state of the serial port's DCD pin. (read-only)
*
*  - YES means 1 or high state.
*  - NO means 0 or low state.
*
*  This property is observable using Key Value Observing.
*/
@property (nonatomic, readonly) BOOL DCD;

@end

/**
*  The ORSSerialPortDelegate protocol defines methods to be implemented
*  by the delegate of an `ORSSerialPort` object.
*
*  *Note*: All `ORSSerialPortDelegate` methods are always called on the main queue.
*  If you need to handle them on a background queue, you must dispatch your handling
*  to a background queue in your implementation of the delegate method.
*/

@protocol ORSSerialPortDelegate

@required

/**
*  Called when new data is received by the serial port from an external source.
*
*  @param serialPort The `ORSSerialPort` instance representing the port that received `data`.
*  @param data       An `NSData` instance containing the data received.
*/
- (void)serialPort:(ORSSerialPort *)serialPort didReceiveData:(NSData *)data;

/**
*  Called when a serial port is removed from the system, e.g. the user unplugs
*  the USB to serial adapter for the port.
*
*	In this method, you should discard any strong references you have maintained for the
*  passed in `serialPort` object. The behavior of `ORSSerialPort` instances whose underlying
*  serial port has been removed from the system is undefined.
*
*  @param serialPort The `ORSSerialPort` instance representing the port that was removed.
*/
- (void)serialPortWasRemovedFromSystem:(ORSSerialPort *)serialPort;

@optional

/**
*  Called when an error occurs during an operation involving a serial port.
*
*	This method is always used to report errors. No `ORSSerialPort` methods
*  take a passed in `NSError **` reference because errors may occur asynchonously,
*  after a method has returned.
*
*	Currently, errors reported using this method are always in the `NSPOSIXErrorDomain`,
*  and a list of error codes can be found in the system header errno.h.
*
*  The error object's userInfo dictionary contains the following keys:
*
*	- NSLocalizedDescriptionKey - An error message string.
*	- NSFilePathErrorKey - The device path to the serial port. Same as `[serialPort path]`.
*
*  @param serialPort The `ORSSerialPort` instance for the port
*  @param error      An `NSError` object containing information about the error.
*/
- (void)serialPort:(ORSSerialPort *)serialPort didEncounterError:(NSError *)error;

/**
*  Called when a serial port is successfully opened.
*
*  @param serialPort The `ORSSerialPort` instance representing the port that was opened.
*/
- (void)serialPortWasOpened:(ORSSerialPort *)serialPort;

/**
*  Called when a serial port was closed (e.g. because `-close`) was called.
*
*  @param serialPort The `ORSSerialPort` instance representing the port that was closed.
*/
- (void)serialPortWasClosed:(ORSSerialPort *)serialPort;

@end


ORSSerialPort.m

#if !__has_feature(objc_arc)
#error ORSSerialPort.m must be compiled with ARC. Either turn on ARC for the project or set the -fobjc-arc flag for ORSSerialPort.m in the Build Phases for this target
#endif

#if OS_OBJECT_USE_OBJC && __has_feature(objc_arc)
#define ORS_GCD_RELEASE(x)
#define ORS_GCD_RETAIN(x)
#else
#define ORS_GCD_RELEASE(x) dispatch_release(x)
#define ORS_GCD_RETAIN(x) dispatch_retain(x)
#endif

#import "ORSSerialPort.h"
#import <IOKit/serial/IOSerialKeys.h>
#import <IOKit/serial/ioss.h>
#import <sys/param.h>
#import <sys/filio.h>
#import <sys/ioctl.h>

#ifdef LOG_SERIAL_PORT_ERRORS
#define LOG_SERIAL_PORT_ERROR(fmt, ...) NSLog(fmt, ## __VA_ARGS__)
#else
#define LOG_SERIAL_PORT_ERROR(fmt, ...)
#endif

static __strong NSMutableArray *allSerialPorts;

@interface ORSSerialPort ()
{
struct termios originalPortAttributes;
}

@property (copy, readwrite) NSString *path;
@property (readwrite) io_object_t IOKitDevice;
@property (copy, readwrite) NSString *name;

@property (strong) NSMutableData *writeBuffer;
@property int fileDescriptor;

@property (nonatomic, readwrite) BOOL CTS;
@property (nonatomic, readwrite) BOOL DSR;
@property (nonatomic, readwrite) BOOL DCD;

#if OS_OBJECT_USE_OBJC
@property (nonatomic, strong) dispatch_source_t pinPollTimer;
#else
@property (nonatomic) dispatch_source_t pinPollTimer;
#endif

@end

@implementation ORSSerialPort

+ (void)initialize
{
static dispatch_once_t once;
dispatch_once(&once, ^{
allSerialPorts = [[NSMutableArray alloc] init];
});
}

+ (void)addSerialPort:(ORSSerialPort *)port;
{
[allSerialPorts addObject:[NSValue valueWithNonretainedObject:port]];
}

+ (void)removeSerialPort:(ORSSerialPort *)port;
{
NSValue *valueToRemove = nil;
for (NSValue *value in allSerialPorts)
{
if ([value nonretainedObjectValue] == port)
{
valueToRemove = value;
break;
}
}
if (valueToRemove) [allSerialPorts removeObject:valueToRemove];
}

+ (ORSSerialPort *)existingPortWithPath:(NSString *)path;
{
ORSSerialPort *existingPort = nil;
for (NSValue *value in allSerialPorts)
{
ORSSerialPort *port = [value nonretainedObjectValue];
if ([port.path isEqualToString:path])
{
existingPort = port;
break;
}
}

return existingPort;
}

+ (ORSSerialPort *)serialPortWithPath:(NSString *)devicePath
{
return [[self alloc] initWithPath:devicePath];
}

+ (ORSSerialPort *)serialPortWithDevice:(io_object_t)device;
{
return [[self alloc] initWithDevice:device];
}

- (id)initWithPath:(NSString *)devicePath
{
io_object_t device = [[self class] deviceFromBSDPath:devicePath];
if (device == 0)
{
self = nil;
return self;
}

return [self initWithDevice:device];
}

- (id)initWithDevice:(io_object_t)device;
{
NSAssert(device != 0, @"%s requires non-zero device argument.", __PRETTY_FUNCTION__);

NSString *bsdPath = [[self class] bsdCalloutPathFromDevice:device];
ORSSerialPort *existingPort = [[self class] existingPortWithPath:bsdPath];

if (existingPort != nil)
{
self = nil;
return existingPort;
}

self = [super init];

if (self != nil)
{
self.ioKitDevice = device;
self.path = bsdPath;
self.name = [[self class] modemNameFromDevice:device];
self.writeBuffer = [NSMutableData data];
self.baudRate = @B9600;//@B19200;
self.numberOfStopBits = 1;
self.parity = ORSSerialPortParityNone;
self.shouldEchoReceivedData = NO;
self.usesRTSCTSFlowControl = NO;
self.usesDTRDSRFlowControl = NO;
self.usesDCDOutputFlowControl = NO;
self.RTS = NO;
self.DTR = NO;
}

[[self class] addSerialPort:self];

return self;
}

- (id)init
{
NSAssert(0, @"ORSSerialPort must be init'd using -initWithPath:");
return nil;
}

- (void)dealloc
{
[[self class] removeSerialPort:self];
self.IOKitDevice = 0;

if (_pinPollTimer) {
dispatch_source_cancel(_pinPollTimer);
ORS_GCD_RELEASE(_pinPollTimer);
}
}

- (NSString *)description
{
return self.name;
//	io_object_t device = [[self class] deviceFromBSDPath:self.path];
//	return [NSString stringWithFormat:@"BSD Path:%@, base name:%@, modem name:%@, suffix:%@, service type:%@", [[self class] bsdCalloutPathFromDevice:device], [[self class] baseNameFromDevice:device], [[self class] modemNameFromDevice:device], [[self class] suffixFromDevice:device], [[self class] serviceTypeFromDevice:device]];
}

- (NSUInteger)hash
{
return [self.path hash];
}

- (BOOL)isEqual:(id)object
{
if (![object isKindOfClass:[self class]]) return NO;
if (![object respondsToSelector:@selector(path)]) return NO;

return [self.path isEqual:[object path]];
}

#pragma mark - Public Methods

- (BOOL)open;
{
if (self.isOpen)
return NO;

dispatch_queue_t mainQueue = dispatch_get_main_queue();

int descriptor=0;
descriptor = open([self.path cStringUsingEncoding:NSASCIIStringEncoding], O_RDWR | O_NOCTTY | O_EXLOCK | O_NONBLOCK);
if (descriptor < 1)
{
// Error
[self notifyDelegateOfPosixError];
return NO;
}

// Now that the device is open, clear the O_NONBLOCK flag so subsequent I/O will block.
// See fcntl(2) ("man 2 fcntl") for details.

if (fcntl(descriptor, F_SETFL, 0) == -1)
{
LOG_SERIAL_PORT_ERROR(@"Error clearing O_NONBLOCK %@ - %s(%d).\n", self.path, strerror(errno), errno);
//return NO;
}

self.fileDescriptor = descriptor;

// Port opened successfully, set options
tcgetattr(descriptor, &originalPortAttributes); // Get original options so they can be reset later
[self setPortOptions];

// Get status of RTS and DTR lines
int modemLines=0;
if (ioctl(self.fileDescriptor, TIOCMGET, &modemLines) < 0)
{
LOG_SERIAL_PORT_ERROR(@"Error reading modem lines status");
[self notifyDelegateOfPosixError];
}

BOOL desiredRTS = self.RTS;
BOOL desiredDTR = self.DTR;
self.RTS = modemLines & TIOCM_RTS;
self.DTR = modemLines & TIOCM_DTR;
self.RTS = desiredRTS;
self.DTR = desiredDTR;

if ([(id)self.delegate respondsToSelector:@selector(serialPortWasOpened:)])
{
dispatch_async(mainQueue, ^{
[self.delegate serialPortWasOpened:self];
});
}

// Start a read poller in the background
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

int localPortFD = self.fileDescriptor;
struct timeval timeout;
int result=0;

while (self.isOpen)
{
fd_set localReadFDSet;
FD_ZERO(&localReadFDSet);
FD_SET(localPortFD, &localReadFDSet);

timeout.tv_sec = 0;
timeout.tv_usec = 100000; // Check to see if port closed every 100ms

result = select(localPortFD+1, &localReadFDSet, NULL, NULL, &timeout);
if (!self.isOpen)
break; // Port closed while select call was waiting
if (result < 0)
{
[self notifyDelegateOfPosixError];
continue;
}

if (result == 0 || !FD_ISSET(localPortFD, &localReadFDSet))
continue;

// Data is available
char buf[1024];
long lengthRead = read(localPortFD, buf, sizeof(buf));
if (lengthRead>0)
{
NSData *readData = [NSData dataWithBytes:buf length:lengthRead];
if (readData != nil)
[self receiveData:readData];
}
}
});

// Start another poller to check status of CTS and DSR
dispatch_queue_t pollQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, pollQueue);
dispatch_source_set_timer(timer, dispatch_time(DISPATCH_TIME_NOW, 0), 10*NSEC_PER_MSEC, 5*NSEC_PER_MSEC);
dispatch_source_set_event_handler(timer, ^{
if (!self.isOpen) {
dispatch_async(pollQueue, ^{ dispatch_source_cancel(timer); });
return;
}

int32_t modemLines=0;
int result = ioctl(self.fileDescriptor, TIOCMGET, &modemLines);
if (result < 0)
{
[self notifyDelegateOfPosixErrorWaitingUntilDone:(errno == ENXIO)];
if (errno == ENXIO)
{
[self cleanupAfterSystemRemoval];
}
return;
}

BOOL CTSPin = (modemLines & TIOCM_CTS) != 0;
BOOL DSRPin = (modemLines & TIOCM_DSR) != 0;
BOOL DCDPin = (modemLines & TIOCM_CAR) != 0;

if (CTSPin != self.CTS)
dispatch_sync(mainQueue, ^{self.CTS = CTSPin;});
if (DSRPin != self.DSR)
dispatch_sync(mainQueue, ^{self.DSR = DSRPin;});
if (DCDPin != self.DCD)
dispatch_sync(mainQueue, ^{self.DCD = DCDPin;});
});
self.pinPollTimer = timer;
dispatch_resume(self.pinPollTimer);
ORS_GCD_RELEASE(timer);
return YES;
}

- (BOOL)close;
{
if (!self.isOpen)
return YES;

[self.writeBuffer replaceBytesInRange:NSMakeRange(0, [self.writeBuffer length]) withBytes:NULL length:0];

self.pinPollTimer = nil; // Stop polling CTS/DSR/DCD pins

// The next tcsetattr() call can fail if the port is waiting to send data. This is likely to happen
// e.g. if flow control is on and the CTS line is low. So, turn off flow control before proceeding
struct termios options;
tcgetattr(self.fileDescriptor, &options);
options.c_cflag &= ~CRTSCTS; // RTS/CTS Flow Control
options.c_cflag &= ~(CDTR_IFLOW | CDSR_OFLOW); // DTR/DSR Flow Control
options.c_cflag &= ~CCAR_OFLOW; // DCD Flow Control
tcsetattr(self.fileDescriptor, TCSANOW, &options);

// Set port back the way it was before we used it
tcsetattr(self.fileDescriptor, TCSADRAIN, &originalPortAttributes);

int localFD = self.fileDescriptor;
self.fileDescriptor = 0; // So other threads know that the port should be closed and can stop I/O operations

if (close(localFD))
{
self.fileDescriptor = localFD;
LOG_SERIAL_PORT_ERROR(@"Error closing serial port with file descriptor %i:%i", self.fileDescriptor, errno);
[self notifyDelegateOfPosixError];
return NO;
}

if ([(id)self.delegate respondsToSelector:@selector(serialPortWasClosed:)])
{
dispatch_async(dispatch_get_main_queue(), ^{
[self.delegate serialPortWasClosed:self];
});
}
return YES;
}

- (void)cleanup;
{
NSLog(@"Cleanup is deprecated and was never intended to be called publicly. You should update your code to avoid calling this method.");
[self cleanupAfterSystemRemoval];
}

- (void)cleanupAfterSystemRemoval
{
if ([(id)self.delegate respondsToSelector:@selector(serialPortWasRemovedFromSystem:)])
{
dispatch_async(dispatch_get_main_queue(), ^{
[self.delegate serialPortWasRemovedFromSystem:self];
[self close];
});
}
else
{
[self close];
}
}

- (BOOL)sendData:(NSData *)data;
{
if (!self.isOpen)
return NO;

[self.writeBuffer appendData:data];

if ([self.writeBuffer length] < 1) return YES;

long numBytesWritten = write(self.fileDescriptor, [self.writeBuffer bytes], [self.writeBuffer length]);
if (numBytesWritten < 0)
{
LOG_SERIAL_PORT_ERROR(@"Error writing to serial port:%d", errno);
[self notifyDelegateOfPosixError];
return NO;
}
if (numBytesWritten > 0) [self.writeBuffer replaceBytesInRange:NSMakeRange(0, numBytesWritten) withBytes:NULL length:0];

return YES;
}

#pragma mark - Private Methods

#pragma mark Port Read/Write

- (void)receiveData:(NSData *)data;
{
if ([(id)self.delegate respondsToSelector:@selector(serialPort:didReceiveData:)])
{
dispatch_async(dispatch_get_main_queue(), ^{
[self.delegate serialPort:self didReceiveData:data];
});
}
}

#pragma mark Port Propeties Methods

- (void)setPortOptions;
{
if ([self fileDescriptor] < 1) return;

struct termios options;

tcgetattr(self.fileDescriptor, &options);

cfmakeraw(&options);
options.c_cc[VMIN] = 1; // Wait for at least 1 character before returning
options.c_cc[VTIME] = 2; // Wait 200 milliseconds between bytes before returning from read

// Set 8 data bits
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS8;

// Set parity
switch (self.parity) {
case ORSSerialPortParityNone:
options.c_cflag &= ~PARENB;
break;
case ORSSerialPortParityEven:
options.c_cflag |= PARENB;
options.c_cflag &= ~PARODD;
break;
case ORSSerialPortParityOdd:
options.c_cflag |= PARENB;
options.c_cflag |= PARODD;
break;
default:
break;
}

options.c_cflag = [self numberOfStopBits] > 1 ? options.c_cflag | CSTOPB : options.c_cflag & ~CSTOPB; // number of stop bits
options.c_lflag = [self shouldEchoReceivedData] ? options.c_lflag | ECHO : options.c_lflag & ~ECHO; // echo
options.c_cflag = [self usesRTSCTSFlowControl] ? options.c_cflag | CRTSCTS : options.c_cflag & ~CRTSCTS; // RTS/CTS Flow Control
options.c_cflag = [self usesDTRDSRFlowControl] ? options.c_cflag | (CDTR_IFLOW | CDSR_OFLOW) : options.c_cflag & ~(CDTR_IFLOW | CDSR_OFLOW); // DTR/DSR Flow Control
options.c_cflag = [self usesDCDOutputFlowControl] ? options.c_cflag | CCAR_OFLOW : options.c_cflag & ~CCAR_OFLOW; // DCD Flow Control

options.c_cflag |= HUPCL; // Turn on hangup on close
options.c_cflag |= CLOCAL; // Set local mode on
options.c_cflag |= CREAD; // Enable receiver
options.c_lflag &= ~(ICANON /*| ECHO*/ | ISIG); // Turn off canonical mode and signals

// Set baud rate
cfsetspeed(&options, [[self baudRate] unsignedLongValue]);

// TODO: Call delegate error handling method if this fails
int result = tcsetattr(self.fileDescriptor, TCSANOW, &options);
if (result != 0) NSLog(@"Unable to set options on %@: %i", self, result);
}

+ (io_object_t)deviceFromBSDPath:(NSString *)bsdPath;
{
if ([bsdPath length] < 1) return 0;

CFMutableDictionaryRef matchingDict = NULL;

matchingDict = IOServiceMatching(kIOSerialBSDServiceValue);
CFRetain(matchingDict); // Need to use it twice

CFDictionaryAddValue(matchingDict, CFSTR(kIOSerialBSDTypeKey), CFSTR(kIOSerialBSDAllTypes));

io_iterator_t portIterator = 0;
kern_return_t err = IOServiceGetMatchingServices(kIOMasterPortDefault, matchingDict, &portIterator);
CFRelease(matchingDict);
if (err) return 0;

io_object_t eachPort = 0;
io_object_t result = 0;
while ((eachPort = IOIteratorNext(portIterator)))
{
NSString *calloutPath = [self bsdCalloutPathFromDevice:eachPort];
NSString *dialinPath = [self bsdDialinPathFromDevice:eachPort];
if ([bsdPath isEqualToString:calloutPath] ||
[bsdPath isEqualToString:dialinPath])
{
result = eachPort;
break;
}
IOObjectRelease(eachPort);
}
IOObjectRelease(portIterator);

return result;
}

+ (NSString *)stringPropertyOf:(io_object_t)aDevice forIOSerialKey:(NSString *)key;
{
CFStringRef string = (CFStringRef)IORegistryEntryCreateCFProperty(aDevice,(__bridge CFStringRef)key,kCFAllocatorDefault,0);
return (__bridge_transfer NSString *)string;
}

+ (NSString *)bsdCalloutPathFromDevice:(io_object_t)aDevice;
{
return [self stringPropertyOf:aDevice forIOSerialKey:(NSString*)CFSTR(kIOCalloutDeviceKey)];
}

+ (NSString *)bsdDialinPathFromDevice:(io_object_t)aDevice;
{
return [self stringPropertyOf:aDevice forIOSerialKey:(NSString*)CFSTR(kIODialinDeviceKey)];
}

+ (NSString *)baseNameFromDevice:(io_object_t)aDevice;
{
return [self stringPropertyOf:aDevice forIOSerialKey:(NSString*)CFSTR(kIOTTYBaseNameKey)];
}

+ (NSString *)serviceTypeFromDevice:(io_object_t)aDevice;
{
return [self stringPropertyOf:aDevice forIOSerialKey:(NSString*)CFSTR(kIOSerialBSDTypeKey)];
}

+ (NSString *)modemNameFromDevice:(io_object_t)aDevice;
{
return [self stringPropertyOf:aDevice forIOSerialKey:(NSString*)CFSTR(kIOTTYDeviceKey)];
}

+ (NSString *)suffixFromDevice:(io_object_t)aDevice;
{
return [self stringPropertyOf:aDevice forIOSerialKey:(NSString*)CFSTR(kIOTTYSuffixKey)];
}

#pragma mark Helper Methods

- (void)notifyDelegateOfPosixError
{
[self notifyDelegateOfPosixErrorWaitingUntilDone:NO];
}

- (void)notifyDelegateOfPosixErrorWaitingUntilDone:(BOOL)shouldWait;
{
if (![(id)self.delegate respondsToSelector:@selector(serialPort:didEncounterError:)]) return;

NSDictionary *errDict = @{NSLocalizedDescriptionKey: @(strerror(errno)),
NSFilePathErrorKey: self.path};
NSError *error = [NSError errorWithDomain:NSPOSIXErrorDomain
code:errno
userInfo:errDict];

void (^notifyBlock)(void) = ^{
[self.delegate serialPort:self didEncounterError:error];
};

if ([NSThread isMainThread]) {
notifyBlock();
} else if (shouldWait) {
dispatch_sync(dispatch_get_main_queue(), notifyBlock);
} else {
dispatch_async(dispatch_get_main_queue(), notifyBlock);
}
}

#pragma mark - Properties

+ (NSSet *)keyPathsForValuesAffectingValueForKey:(NSString *)key
{
NSSet *keyPaths = [super keyPathsForValuesAffectingValueForKey:key];
if ([key isEqualToString:@"isOpen"])
{
keyPaths = [keyPaths setByAddingObject:@"fileDescriptor"];
}

return keyPaths;
}

@synthesize delegate = _delegate;

#pragma mark Port Properties

- (BOOL)isOpen
{
return self.fileDescriptor != 0;
}

@synthesize path = _path;

@synthesize IOKitDevice = _IOKitDevice;
- (void)setIoKitDevice:(io_object_t)device
{
if (device != _IOKitDevice) {
if (_IOKitDevice) IOObjectRelease(_IOKitDevice);
_IOKitDevice = device;
if (_IOKitDevice) IOObjectRetain(_IOKitDevice);
}
}

@synthesize name = _name;

@synthesize baudRate = _baudRate;
- (void)setBaudRate:(NSNumber *)rate
{
if (rate != _baudRate)
{
_baudRate = [rate copy];

[self setPortOptions];
}
}

@synthesize numberOfStopBits = _numberOfStopBits;
- (void)setNumberOfStopBits:(NSUInteger)num
{
if (num != _numberOfStopBits)
{
_numberOfStopBits = num;
[self setPortOptions];
}
}

@synthesize shouldEchoReceivedData = _shouldEchoReceivedData;
- (void)setShouldEchoReceivedData:(BOOL)flag
{
if (flag != _shouldEchoReceivedData)
{
_shouldEchoReceivedData = flag;
[self setPortOptions];
}
}

@synthesize parity = _parity;
- (void)setParity:(ORSSerialPortParity)aParity
{
if (aParity != _parity)
{
if (aParity != ORSSerialPortParityNone &&
aParity != ORSSerialPortParityOdd &&
aParity != ORSSerialPortParityEven)
{
aParity = ORSSerialPortParityNone;
}

_parity = aParity;
[self setPortOptions];
}
}

@synthesize usesRTSCTSFlowControl = _usesRTSCTSFlowControl;
- (void)setUsesRTSCTSFlowControl:(BOOL)flag
{
if (flag != _usesRTSCTSFlowControl)
{
// Turning flow control one while the port is open doesn't seem to work right,
// at least with some drivers, so close it then reopen it if needed
BOOL shouldReopen = self.isOpen;
[self close];

_usesRTSCTSFlowControl = flag;

[self setPortOptions];
if (shouldReopen)
[self open];
}
}

@synthesize usesDTRDSRFlowControl = _usesDTRDSRFlowControl;
- (void)setUsesDTRDSRFlowControl:(BOOL)flag
{
if (flag != _usesDTRDSRFlowControl)
{
// Turning flow control one while the port is open doesn't seem to work right,
// at least with some drivers, so close it then reopen it if needed
BOOL shouldReopen = self.isOpen;
[self close];

_usesDTRDSRFlowControl = flag;
[self setPortOptions];
if (shouldReopen)
[self open];
}
}

@synthesize usesDCDOutputFlowControl = _usesDCDOutputFlowControl;
- (void)setUsesDCDOutputFlowControl:(BOOL)flag
{
if (flag != _usesDCDOutputFlowControl)
{
// Turning flow control one while the port is open doesn't seem to work right,
// at least with some drivers, so close it then reopen it if needed
BOOL shouldReopen = self.isOpen;
[self close];

_usesDCDOutputFlowControl = flag;

[self setPortOptions];
if (shouldReopen) [self open];
}
}

@synthesize RTS = _RTS;
- (void)setRTS:(BOOL)flag
{
if (flag != _RTS)
{
_RTS = flag;

if (![self isOpen]) return;

int bits;
ioctl( self.fileDescriptor, TIOCMGET, &bits ) ;
bits = _RTS ? bits | TIOCM_RTS : bits & ~TIOCM_RTS;
if (ioctl( self.fileDescriptor, TIOCMSET, &bits ) < 0)
{
LOG_SERIAL_PORT_ERROR(@"Error in %s", __PRETTY_FUNCTION__);
[self notifyDelegateOfPosixError];
}
}
}

@synthesize DTR = _DTR;
- (void)setDTR:(BOOL)flag
{
if (flag != _DTR)
{
_DTR = flag;

if (![self isOpen]) return;

int bits;
ioctl( self.fileDescriptor, TIOCMGET, &bits ) ;
bits = _DTR ? bits | TIOCM_DTR : bits & ~TIOCM_DTR;
if (ioctl( self.fileDescriptor, TIOCMSET, &bits ) < 0)
{
LOG_SERIAL_PORT_ERROR(@"Error in %s", __PRETTY_FUNCTION__);
[self notifyDelegateOfPosixError];
}
}
}

@synthesize CTS = _CTS;
@synthesize DSR = _DSR;
@synthesize DCD = _DCD;

#pragma mark Private Properties

@synthesize writeBuffer = _writeBuffer;
@synthesize fileDescriptor = _fileDescriptor;
@synthesize pinPollTimer = _pinPollTimer;
- (void)setPinPollTimer:(dispatch_source_t)timer
{
if (timer != _pinPollTimer)
{
if (_pinPollTimer)
{
dispatch_source_cancel(_pinPollTimer);
ORS_GCD_RELEASE(_pinPollTimer);
}

if (timer) {
ORS_GCD_RETAIN(timer);
}
_pinPollTimer = timer;
}
}

@end


ORSSerialPortManager.h

#import <Foundation/Foundation.h>

/// Posted when a serial port is connected to the system
extern NSString * const ORSSerialPortsWereConnectedNotification;

/// Posted when a serial port is disconnected from the system
extern NSString * const ORSSerialPortsWereDisconnectedNotification;

/// Key for connected port in ORSSerialPortWasConnectedNotification userInfo dictionary
extern NSString * const ORSConnectedSerialPortsKey;
/// Key for disconnected port in ORSSerialPortWasDisconnectedNotification userInfo dictionary
extern NSString * const ORSDisconnectedSerialPortsKey;

/**
*  `ORSSerialPortManager` is a singleton class (one instance per
*  application) that can be used to get a list of available serial ports.
*  It will also handle closing open serial ports when the Mac goes to
*  sleep, and reopening them automatically on wake. This prevents problems
*  I've seen with serial port drivers that can hang if the port is left
*  open when putting the machine to sleep. Note that using
*  `ORSSerialPortManager` is optional. It provides some nice functionality,
*  but only `ORSSerialPort` is necessary to simply send and received data.
*
*  Using ORSSerialPortManager
*  --------------------------
*
*  To get the shared serial port
*  manager:
*
*      ORSSerialPortManager *portManager = [ORSSerialPortManager sharedSerialPortManager];
*
*  To get a list of available ports:
*
*      NSArray *availablePorts = portManager.availablePorts;
*
*  Notifications
*  -------------
*
*  `ORSSerialPort` posts notifications when a port is added to or removed from the system.
*  `ORSSerialPortsWereConnectedNotification` is posted when one or more ports
*  are added to the system. `ORSSerialPortsWereDisconnectedNotification` is posted when
*  one ore more ports are removed from the system. The user info dictionary for each
*  notification contains the list of ports added or removed. The keys to access these array
*  are `ORSConnectedSerialPortsKey`, and `ORSDisconnectedSerialPortsKey` respectively.
*
*  KVO Compliance
*  --------------
*
*  `ORSSerialPortManager` is Key-Value Observing (KVO) compliant for its
*  `availablePorts` property. This means that you can observe
*  `availablePorts` to be notified when ports are added to or removed from
*  the system. This also means that you can easily bind UI elements to the
*  serial port manager's `availablePorts` property using Cocoa-bindings.
*  This makes it easy to create a popup menu that displays available serial
*  ports and updates automatically, for example.
*
*  Close-On-Sleep
*  --------------
*
*  `ORSSerialPortManager`'s close-on-sleep, reopen-on-wake functionality is
*  automatic. The only thing necessary to enable it is to make sure that
*  the singleton instance of `ORSSerialPortManager` has been created by
*  calling `+sharedSerialPortManager` at least once. Note that this
*  behavior is only available in Cocoa apps, and is disabled when
*  ORSSerialPort is used in a command-line only app.
*/
@interface ORSSerialPortManager : NSObject

/**
*  Returns the shared (singleton) serial port manager object.
*
*  @return The shared serial port manager.
*/
+ (ORSSerialPortManager *)sharedSerialPortManager;

/**
*  An array containing ORSSerialPort instances representing the
*  serial ports available on the system. (read-only)
*
*  As explained above, this property is Key Value Observing
*  compliant, and can be bound to for example an NSPopUpMenu
*  to easily give the user a way to select an available port
*  on the system.
*/
@property (nonatomic, copy, readonly) NSArray *availablePorts;

@end


ORSSerialPortManager.m

#if !__has_feature(objc_arc)
#error ORSSerialPortManager.m must be compiled with ARC. Either turn on ARC for the project or set the -fobjc-arc flag for ORSSerialPortManager.m in the Build Phases for this target
#endif

#import "ORSSerialPortManager.h"
#import "ORSSerialPort.h"

#import <IOKit/IOKitLib.h>
#import <IOKit/serial/IOSerialKeys.h>

#ifdef LOG_SERIAL_PORT_ERRORS
#define LOG_SERIAL_PORT_ERROR(fmt, ...) NSLog(fmt, ##__VA_ARGS__)
#else
#define LOG_SERIAL_PORT_ERROR(fmt, ...)
#endif

NSString * const ORSSerialPortsWereConnectedNotification = @"ORSSerialPortWasConnectedNotification";
NSString * const ORSSerialPortsWereDisconnectedNotification = @"ORSSerialPortWasDisconnectedNotification";

NSString * const ORSConnectedSerialPortsKey = @"ORSConnectedSerialPortsKey";
NSString * const ORSDisconnectedSerialPortsKey = @"ORSDisconnectedSerialPortsKey";

void ORSSerialPortManagerPortsPublishedNotificationCallback(void *refCon, io_iterator_t iterator);
void ORSSerialPortManagerPortsTerminatedNotificationCallback(void *refCon, io_iterator_t iterator);

@interface ORSSerialPortManager ()

@property (nonatomic, copy, readwrite) NSArray *availablePorts;
@property (nonatomic, strong) NSMutableArray *portsToReopenAfterSleep;

@property (nonatomic) io_iterator_t portPublishedNotificationIterator;
@property (nonatomic) io_iterator_t portTerminatedNotificationIterator;

@end

static ORSSerialPortManager *sharedInstance = nil;

@implementation ORSSerialPortManager
{
NSMutableArray *_availablePorts;
}

#pragma mark - Singleton Methods

- (id)init
{
if (self == sharedInstance)
return sharedInstance; // Already initialized

self = [super init];
if (self != nil)
{
self.portsToReopenAfterSleep = [NSMutableArray array];

[self retrieveAvailablePortsAndRegisterForChangeNotifications];
[self registerForNotifications];
}
return self;
}

+ (ORSSerialPortManager *)sharedSerialPortManager;
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
if (sharedInstance == nil)
sharedInstance = [(ORSSerialPortManager *)[super allocWithZone:NULL] init];
});

return sharedInstance;
}

+ (id)allocWithZone:(NSZone *)zone
{
return [self sharedSerialPortManager];
}

- (id)copyWithZone:(NSZone *)zone
{
return self;
}

- (void)dealloc
{
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
[nc removeObserver:self];

// Stop IOKit notifications for ports being added/removed
IOObjectRelease(_portPublishedNotificationIterator);
_portPublishedNotificationIterator = 0;
IOObjectRelease(_portTerminatedNotificationIterator);
_portTerminatedNotificationIterator = 0;
}

- (void)registerForNotifications
{
// register for notifications (only if AppKit is available)
void (^terminationBlock)(void) = ^{
for (ORSSerialPort *eachPort in self.availablePorts) [eachPort cleanupAfterSystemRemoval];
self.availablePorts = nil;
};

#ifdef NSAppKitVersionNumber10_0
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
[nc addObserverForName:NSApplicationWillTerminateNotification object:nil queue:nil usingBlock:^(NSNotification *notification){
// For some unknown reason, this notification fires twice,
// doesn't cause a problem right now, but be aware
terminationBlock();
}];

[nc addObserver:self selector:@selector(systemWillSleep:) name:NSWorkspaceWillSleepNotification object:NULL];
[nc addObserver:self selector:@selector(systemDidWake:) name:NSWorkspaceDidWakeNotification object:NULL];
#else
// If AppKit isn't available, as in a Foundation command-line tool, cleanup upon exit. Sleep/wake
// notifications don't seem to be available without NSWorkspace.
int result = atexit_b(terminationBlock);
if (result) NSLog(@"ORSSerialPort was unable to register its termination handler for serial port cleanup: %i", errno);
#endif
}

#pragma mark - Public Methods

#pragma mark -
#pragma Sleep/Wake Management

- (void)systemWillSleep:(NSNotification *)notification;
{
NSArray *ports = self.availablePorts;
for (ORSSerialPort *port in ports)
{
if (port.isOpen)
{
if ([port close])
[self.portsToReopenAfterSleep addObject:port];
}
}
}

- (void)systemDidWake:(NSNotification *)notification;
{
NSArray *portsToReopen = [self.portsToReopenAfterSleep copy];
for (ORSSerialPort *port in portsToReopen)
{
[port open];
[self.portsToReopenAfterSleep removeObject:port];
}
}

#pragma mark - Private Methods

- (void)serialPortsWerePublished:(io_iterator_t)iterator;
{
NSMutableArray *newlyConnectedPorts = [[NSMutableArray alloc] init];
io_object_t device;
while ((device = IOIteratorNext(iterator)))
{
ORSSerialPort *port = [[ORSSerialPort alloc] initWithDevice:device];
if (![self.availablePorts containsObject:port]) [newlyConnectedPorts addObject:port];
IOObjectRelease(device);
}

[[self mutableArrayValueForKey:@"availablePorts"] addObjectsFromArray:newlyConnectedPorts];

NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
NSDictionary *userInfo = @{ORSConnectedSerialPortsKey : newlyConnectedPorts};
[nc postNotificationName:ORSSerialPortsWereConnectedNotification object:self userInfo:userInfo];
}

- (void)serialPortsWereTerminated:(io_iterator_t)iterator;
{
NSMutableArray *newlyDisconnectedPorts = [[NSMutableArray alloc] init];
io_object_t device;
while ((device = IOIteratorNext(iterator)))
{
ORSSerialPort *port = [[ORSSerialPort alloc] initWithDevice:device];
[newlyDisconnectedPorts addObject:port];
IOObjectRelease(device);
}

[newlyDisconnectedPorts makeObjectsPerformSelector:@selector(cleanupAfterSystemRemoval)];
[[self mutableArrayValueForKey:@"availablePorts"] removeObjectsInArray:newlyDisconnectedPorts];

NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
NSDictionary *userInfo = @{ORSDisconnectedSerialPortsKey : newlyDisconnectedPorts};
[nc postNotificationName:ORSSerialPortsWereDisconnectedNotification object:self userInfo:userInfo];
}

- (void)retrieveAvailablePortsAndRegisterForChangeNotifications;
{
IONotificationPortRef notificationPort = IONotificationPortCreate(kIOMasterPortDefault);
CFRunLoopAddSource(CFRunLoopGetCurrent(),IONotificationPortGetRunLoopSource(notificationPort),kCFRunLoopDefaultMode);

CFMutableDictionaryRef matchingDict = NULL;

matchingDict = IOServiceMatching(kIOSerialBSDServiceValue);
CFRetain(matchingDict); // Need to use it twice

CFDictionaryAddValue(matchingDict, CFSTR(kIOSerialBSDTypeKey), CFSTR(kIOSerialBSDAllTypes));

io_iterator_t portIterator = 0;
kern_return_t result = IOServiceAddMatchingNotification(notificationPort,kIOPublishNotification,matchingDict,ORSSerialPortManagerPortsPublishedNotificationCallback,(__bridge void *)(self),// refCon/contextInfo
&portIterator);
if (result){
LOG_SERIAL_PORT_ERROR(@"Error getting serialPort list:%i", result);
if (portIterator) IOObjectRelease(portIterator);
CFRelease(matchingDict); // Above call to IOServiceAddMatchingNotification consumes one reference, but we added a retain for the below call
return;
}

self.portPublishedNotificationIterator = portIterator;
IOObjectRelease(portIterator);

NSMutableArray *ports = [NSMutableArray array];
io_object_t eachPort;
while ((eachPort = IOIteratorNext(self.portPublishedNotificationIterator)))
{
ORSSerialPort *port = [ORSSerialPort serialPortWithDevice:eachPort];
if (port) {
[ports addObject:port];
}
IOObjectRelease(eachPort);
}

self.availablePorts = ports;

// Also register for removal
IONotificationPortRef terminationNotificationPort = IONotificationPortCreate(kIOMasterPortDefault);
CFRunLoopAddSource(CFRunLoopGetCurrent(),IONotificationPortGetRunLoopSource(terminationNotificationPort),kCFRunLoopDefaultMode);
result = IOServiceAddMatchingNotification(terminationNotificationPort,kIOTerminatedNotification,matchingDict,ORSSerialPortManagerPortsTerminatedNotificationCallback,(__bridge void *)(self),// refCon/contextInfo
&portIterator);
if (result) {
LOG_SERIAL_PORT_ERROR(@"Error registering for serial port termination notification:%i.", result);
if (portIterator) IOObjectRelease(portIterator);
return;
}

self.portTerminatedNotificationIterator = portIterator;
IOObjectRelease(portIterator);

while (IOIteratorNext(self.portTerminatedNotificationIterator)) {
}; // Run out the iterator or notifications won't start
}

#pragma mark - Properties

@synthesize availablePorts = _availablePorts;

- (void)setAvailablePorts:(NSArray *)ports
{
if (ports != _availablePorts)
{
_availablePorts = [ports mutableCopy];
}
}

//可用的串口数量
- (NSUInteger)countOfAvailablePorts {
return [_availablePorts count];
}

//返回某个串口
- (id)objectInAvailablePortsAtIndex:(NSUInteger)index {
return [_availablePorts objectAtIndex:index];
}

//插入多个串口
- (void)insertAvailablePorts:(NSArray *)array atIndexes:(NSIndexSet *)indexes {
[_availablePorts insertObjects:array atIndexes:indexes];
}

//插入单个串口
- (void)insertObject:(ORSSerialPort *)object inAvailablePortsAtIndex:(NSUInteger)index {
[_availablePorts insertObject:object atIndex:index];
}

//移除多个串口
- (void)removeAvailablePortsAtIndexes:(NSIndexSet *)indexes { [_availablePorts removeObjectsAtIndexes:indexes];
}

//移除单个串口
- (void)removeObjectFromAvailablePortsAtIndex:(NSUInteger)index {
[_availablePorts removeObjectAtIndex:index];
}

@synthesize portsToReopenAfterSleep = _portsToReopenAfterSleep;

@synthesize portPublishedNotificationIterator = _portPublishedNotificationIterator;
- (void)setPortPublishedNotificationIterator:(io_iterator_t)iterator
{
if (iterator != _portPublishedNotificationIterator)
{
if (_portPublishedNotificationIterator)
IOObjectRelease(_portPublishedNotificationIterator);

_portPublishedNotificationIterator = iterator;
IOObjectRetain(_portPublishedNotificationIterator);
}
}

@synthesize portTerminatedNotificationIterator = _portTerminatedNotificationIterator;
- (void)setPortTerminatedNotificationIterator:(io_iterator_t)iterator
{
if (iterator != _portTerminatedNotificationIterator)
{
if (_portTerminatedNotificationIterator) IOObjectRelease(_portTerminatedNotificationIterator);

_portTerminatedNotificationIterator = iterator;
IOObjectRetain(_portTerminatedNotificationIterator);
}
}

@end

void ORSSerialPortManagerPortsPublishedNotificationCallback(void *refCon, io_iterator_t iterator)
{
ORSSerialPortManager *manager = (__bridge ORSSerialPortManager *)refCon;
if (![manager isKindOfClass:[ORSSerialPortManager class]])
{
NSLog(@"Unexpected context object %@ in %s. Context object should be an instance of ORSSerialPortManager", manager, __PRETTY_FUNCTION__);
return;
}
[manager serialPortsWerePublished:iterator];
}

void ORSSerialPortManagerPortsTerminatedNotificationCallback(void *refCon, io_iterator_t iterator)
{
ORSSerialPortManager *manager = (__bridge ORSSerialPortManager *)refCon;
if (![manager isKindOfClass:[ORSSerialPortManager class]])
{
NSLog(@"Unexpected context object %@ in %s. Context object should be an instance of ORSSerialPortManager", manager, __PRETTY_FUNCTION__);
return;
}
[manager serialPortsWereTerminated:iterator];
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: