Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Initial commit.

  • Loading branch information...
commit 83722f37a93ea68fe26e20e00a0a26f5fe28da16 0 parents
Ward Witt authored
Showing with 11,650 additions and 0 deletions.
  1. +2 −0  .gitignore
  2. +37 −0 AMSDKCompatibility.h
  3. +29 −0 AMSerialErrors.h
  4. +267 −0 AMSerialPort.h
  5. +812 −0 AMSerialPort.m
  6. +98 −0 AMSerialPortAdditions.h
  7. +626 −0 AMSerialPortAdditions.m
  8. +57 −0 AMSerialPortList.h
  9. +349 −0 AMSerialPortList.m
  10. +19 −0 AMSerialPortList_Deprecated.h
  11. +20 −0 AMSerialPortList_Deprecated.m
  12. +81 −0 AMSerialPort_Deprecated.h
  13. +182 −0 AMSerialPort_Deprecated.m
  14. +32 −0 AMStandardEnumerator.h
  15. +59 −0 AMStandardEnumerator.m
  16. BIN  Blue.png
  17. +2 −0  English.lproj/InfoPlist.strings
  18. +5,688 −0 English.lproj/MainMenu.xib
  19. BIN  Green.png
  20. BIN  Klein.icns
  21. +98 −0 KleinController.h
  22. +403 −0 KleinController.m
  23. +32 −0 KleinView-Info.plist
  24. +381 −0 KleinView.xcodeproj/project.pbxproj
  25. +7 −0 KleinView.xcodeproj/project.xcworkspace/contents.xcworkspacedata
  26. BIN  KleinView.xcodeproj/project.xcworkspace/xcuserdata/wardwitt.xcuserdatad/UserInterfaceState.xcuserstate
  27. +20 −0 KleinView.xcodeproj/project.xcworkspace/xcuserdata/wardwitt.xcuserdatad/WorkspaceSettings.xcsettings
  28. +1,465 −0 KleinView.xcodeproj/wardwitt.mode1v3
  29. +464 −0 KleinView.xcodeproj/wardwitt.pbxuser
  30. +21 −0 KleinView.xcodeproj/xcuserdata/wardwitt.xcuserdatad/xcdebugger/Breakpoints.xcbkptlist
  31. +76 −0 KleinView.xcodeproj/xcuserdata/wardwitt.xcuserdatad/xcschemes/KleinView.xcscheme
  32. +22 −0 KleinView.xcodeproj/xcuserdata/wardwitt.xcuserdatad/xcschemes/xcschememanagement.plist
  33. +31 −0 KleinViewAppDelegate.h
  34. +32 −0 KleinViewAppDelegate.m
  35. +7 −0 KleinView_Prefix.pch
  36. BIN  Red.png
  37. +40 −0 WWLevelIndicator.h
  38. +68 −0 WWLevelIndicator.m
  39. BIN  base.png
  40. +14 −0 main.m
  41. +34 −0 wwLabVIew.h
  42. +75 −0 wwLabVIew.m
2  .gitignore
@@ -0,0 +1,2 @@
+
+.DS_Store
37 AMSDKCompatibility.h
@@ -0,0 +1,37 @@
+//
+// AMSDKCompatibility.h
+//
+// Created by Nick Zitzmann on 2007-10-22.
+//
+
+// AMSerialPort uses types that were introduced with the 10.5 SDK.
+// This allows older SDKs to be used.
+
+#import <Foundation/Foundation.h>
+
+#ifndef NSINTEGER_DEFINED
+ #ifdef NS_BUILD_32_LIKE_64
+ typedef long NSInteger;
+ typedef unsigned long NSUInteger;
+ #else
+ typedef int NSInteger;
+ typedef unsigned int NSUInteger;
+ #endif
+ #define NSIntegerMax LONG_MAX
+ #define NSIntegerMin LONG_MIN
+ #define NSUIntegerMax ULONG_MAX
+ #define NSINTEGER_DEFINED 1
+#endif
+
+#ifndef CGFLOAT_DEFINED
+ typedef float CGFloat;
+ #define CGFLOAT_MIN FLT_MIN
+ #define CGFLOAT_MAX FLT_MAX
+ #define CGFLOAT_IS_DOUBLE 0
+ #define CGFLOAT_DEFINED 1
+#endif
+
+#ifndef _SUSECONDS_T
+#define _SUSECONDS_T
+typedef int suseconds_t;
+#endif
29 AMSerialErrors.h
@@ -0,0 +1,29 @@
+/*
+ * AMSerialErrors.h
+ *
+ * Created by Andreas on 27.07.06.
+ * Copyright 2006 Andreas Mayer. All rights reserved.
+ *
+ */
+
+
+enum {
+ kAMSerialErrorNone = 0,
+ kAMSerialErrorFatal = 99,
+
+ // reading only
+ kAMSerialErrorTimeout = 100,
+ kAMSerialErrorInternalBufferFull = 101,
+
+ // writing only
+ kAMSerialErrorNoDataToWrite = 200,
+ kAMSerialErrorOnlySomeDataWritten = 201,
+};
+
+enum {
+ // reading only
+ kAMSerialEndOfStream = 0,
+ kAMSerialStopCharReached = 1,
+ kAMSerialStopLengthReached = 2,
+ kAMSerialStopLengthExceeded = 3,
+};
267 AMSerialPort.h
@@ -0,0 +1,267 @@
+//
+// AMSerialPort.h
+//
+// Created by Andreas on 2002-04-24.
+// Copyright (c) 2001-2009 Andreas Mayer. All rights reserved.
+//
+// 2002-09-18 Andreas Mayer
+// - added available & owner
+// 2002-10-17 Andreas Mayer
+// - countWriteInBackgroundThreads and countWriteInBackgroundThreadsLock added
+// 2002-10-25 Andreas Mayer
+// - more additional instance variables for reading and writing in background
+// 2004-02-10 Andreas Mayer
+// - added delegate for background reading/writing
+// 2005-04-04 Andreas Mayer
+// - added setDTR and clearDTR
+// 2006-07-28 Andreas Mayer
+// - added -canonicalMode, -endOfLineCharacter and friends
+// (code contributed by Randy Bradley)
+// - cleaned up accessor methods; moved deprecated methods to "Deprecated" category
+// - -setSpeed: does support arbitrary values on 10.4 and later; returns YES on success, NO otherwiese
+// 2006-08-16 Andreas Mayer
+// - cleaned up the code and removed some (presumably) unnecessary locks
+// 2007-10-26 Sean McBride
+// - made code 64 bit and garbage collection clean
+// 2008-10-21 Sean McBride
+// - Added an API to open a serial port for exclusive use
+// - fixed some memory management issues
+
+
+/*
+ * Standard speeds defined in termios.h
+ *
+#define B0 0
+#define B50 50
+#define B75 75
+#define B110 110
+#define B134 134
+#define B150 150
+#define B200 200
+#define B300 300
+#define B600 600
+#define B1200 1200
+#define B1800 1800
+#define B2400 2400
+#define B4800 4800
+#define B7200 7200
+#define B9600 9600
+#define B14400 14400
+#define B19200 19200
+#define B28800 28800
+#define B38400 38400
+#define B57600 57600
+#define B76800 76800
+#define B115200 115200
+#define B230400 230400
+ */
+
+#import "AMSDKCompatibility.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <paths.h>
+#include <termios.h>
+#include <sys/time.h>
+#include <sysexits.h>
+#include <sys/param.h>
+
+#import <Foundation/Foundation.h>
+
+#define AMSerialOptionServiceName @"AMSerialOptionServiceName"
+#define AMSerialOptionSpeed @"AMSerialOptionSpeed"
+#define AMSerialOptionDataBits @"AMSerialOptionDataBits"
+#define AMSerialOptionParity @"AMSerialOptionParity"
+#define AMSerialOptionStopBits @"AMSerialOptionStopBits"
+#define AMSerialOptionInputFlowControl @"AMSerialOptionInputFlowControl"
+#define AMSerialOptionOutputFlowControl @"AMSerialOptionOutputFlowControl"
+#define AMSerialOptionEcho @"AMSerialOptionEcho"
+#define AMSerialOptionCanonicalMode @"AMSerialOptionCanonicalMode"
+
+// By default, debug code is preprocessed out. If you would like to compile with debug code enabled,
+// "#define AMSerialDebug" before including any AMSerialPort headers, as in your prefix header
+
+typedef enum {
+ kAMSerialParityNone = 0,
+ kAMSerialParityOdd = 1,
+ kAMSerialParityEven = 2
+} AMSerialParity;
+
+typedef enum {
+ kAMSerialStopBitsOne = 1,
+ kAMSerialStopBitsTwo = 2
+} AMSerialStopBits;
+
+// Private constant
+#define AMSER_MAXBUFSIZE 4096UL
+
+extern NSString *const AMSerialErrorDomain;
+
+@interface NSObject (AMSerialDelegate)
+- (void)serialPortReadData:(NSDictionary *)dataDictionary;
+- (void)serialPortWriteProgress:(NSDictionary *)dataDictionary;
+@end
+
+@interface AMSerialPort : NSObject
+{
+@private
+ NSString *bsdPath;
+ NSString *serviceName;
+ NSString *serviceType;
+ int fileDescriptor;
+ struct termios * __strong options;
+ struct termios * __strong originalOptions;
+ NSMutableDictionary *optionsDictionary;
+ NSFileHandle *fileHandle;
+ BOOL gotError;
+ int lastError;
+ id owner;
+ char * __strong buffer;
+ NSTimeInterval readTimeout; // for public blocking read methods and doRead
+ fd_set * __strong readfds;
+ id delegate;
+ BOOL delegateHandlesReadInBackground;
+ BOOL delegateHandlesWriteInBackground;
+ NSLock *writeLock;
+ NSLock *readLock;
+ NSLock *closeLock;
+
+ // used by AMSerialPortAdditions only:
+ id am_readTarget;
+ SEL am_readSelector;
+ BOOL stopWriteInBackground;
+ int countWriteInBackgroundThreads;
+ BOOL stopReadInBackground;
+ int countReadInBackgroundThreads;
+}
+
+- (id)init:(NSString *)path withName:(NSString *)name type:(NSString *)serialType;
+// initializes port
+// path is a bsdPath
+// name is an IOKit service name
+// type is an IOKit service type
+
+- (NSString *)bsdPath;
+// bsdPath (e.g. '/dev/cu.modem')
+
+- (NSString *)name;
+// IOKit service name (e.g. 'modem')
+
+- (NSString *)type;
+// IOKit service type (e.g. kIOSerialBSDRS232Type)
+
+- (NSDictionary *)properties;
+// IORegistry entry properties - see IORegistryEntryCreateCFProperties()
+
+
+- (BOOL)isOpen;
+// YES if port is open
+
+- (AMSerialPort *)obtainBy:(id)sender;
+// get this port exclusively; NULL if it's not free
+
+- (void)free;
+// give it back (and close the port if still open)
+
+- (BOOL)available;
+// check if port is free and can be obtained
+
+- (id)owner;
+// who obtained the port?
+
+
+- (NSFileHandle *)open;
+// opens port for read and write operations, allow shared access of port
+// to actually read or write data use the methods provided by NSFileHandle
+// (alternatively you may use those from AMSerialPortAdditions)
+
+- (NSFileHandle *)openExclusively;
+// opens port for read and write operations, insist on exclusive access to port
+// to actually read or write data use the methods provided by NSFileHandle
+// (alternatively you may use those from AMSerialPortAdditions)
+
+- (void)close;
+// close port - no more read or write operations allowed
+
+- (BOOL)drainInput;
+- (BOOL)flushInput:(BOOL)fIn output:(BOOL)fOut; // (fIn or fOut) must be YES
+- (BOOL)sendBreak;
+
+- (BOOL)setDTR;
+// set DTR - not yet tested!
+
+- (BOOL)clearDTR;
+// clear DTR - not yet tested!
+
+// read and write serial port settings through a dictionary
+
+- (NSDictionary *)options;
+// will open the port to get options if neccessary
+
+- (void)setOptions:(NSDictionary *)options;
+// AMSerialOptionServiceName HAS to match! You may NOT switch ports using this
+// method.
+
+// reading and setting parameters is only useful if the serial port is already open
+- (long)speed;
+- (BOOL)setSpeed:(long)speed;
+
+- (unsigned long)dataBits;
+- (void)setDataBits:(unsigned long)bits; // 5 to 8 (5 may not work)
+
+- (AMSerialParity)parity;
+- (void)setParity:(AMSerialParity)newParity;
+
+- (AMSerialStopBits)stopBits;
+- (void)setStopBits:(AMSerialStopBits)numBits;
+
+- (BOOL)echoEnabled;
+- (void)setEchoEnabled:(BOOL)echo;
+
+- (BOOL)RTSInputFlowControl;
+- (void)setRTSInputFlowControl:(BOOL)rts;
+
+- (BOOL)DTRInputFlowControl;
+- (void)setDTRInputFlowControl:(BOOL)dtr;
+
+- (BOOL)CTSOutputFlowControl;
+- (void)setCTSOutputFlowControl:(BOOL)cts;
+
+- (BOOL)DSROutputFlowControl;
+- (void)setDSROutputFlowControl:(BOOL)dsr;
+
+- (BOOL)CAROutputFlowControl;
+- (void)setCAROutputFlowControl:(BOOL)car;
+
+- (BOOL)hangupOnClose;
+- (void)setHangupOnClose:(BOOL)hangup;
+
+- (BOOL)localMode;
+- (void)setLocalMode:(BOOL)local; // YES = ignore modem status lines
+
+- (BOOL)canonicalMode;
+- (void)setCanonicalMode:(BOOL)flag;
+
+- (char)endOfLineCharacter;
+- (void)setEndOfLineCharacter:(char)eol;
+
+- (void)clearError; // call this before changing any settings
+- (BOOL)commitChanges; // call this after using any of the above set... functions
+- (int)errorCode; // if -commitChanges returns NO, look here for further info
+
+// setting the delegate (for background reading/writing)
+
+- (id)delegate;
+- (void)setDelegate:(id)newDelegate;
+
+// time out for blocking reads in seconds
+- (NSTimeInterval)readTimeout;
+- (void)setReadTimeout:(NSTimeInterval)aReadTimeout;
+
+- (void)readTimeoutAsTimeval:(struct timeval*)timeout;
+
+
+@end
812 AMSerialPort.m
@@ -0,0 +1,812 @@
+//
+// AMSerialPort.m
+//
+// Created by Andreas on 2002-04-24.
+// Copyright (c) 2001-2009 Andreas Mayer. All rights reserved.
+//
+// 2002-09-18 Andreas Mayer
+// - added available & owner
+// 2002-10-10 Andreas Mayer
+// - some log messages changed
+// 2002-10-25 Andreas Mayer
+// - additional locks and other changes for reading and writing in background
+// 2003-11-26 James Watson
+// - in dealloc [self close] reordered to execute before releasing closeLock
+// 2007-05-22 Nick Zitzmann
+// - added -hash and -isEqual: methods
+// 2007-07-18 Sean McBride
+// - behaviour change: -open and -close must now always be matched, -dealloc checks this
+// - added -debugDescription so gdb's 'po' command gives something useful
+// 2007-07-25 Andreas Mayer
+// - replaced -debugDescription by -description; works for both, gdb's 'po' and NSLog()
+// 2007-10-26 Sean McBride
+// - made code 64 bit and garbage collection clean
+// 2008-10-21 Sean McBride
+// - Added an API to open a serial port for exclusive use
+// - fixed some memory management issues
+// 2009-08-06 Sean McBride
+// - no longer compare BOOL against YES (dangerous!)
+// - renamed method to start with lowercase letter, as per Cocoa convention
+
+#import "AMSDKCompatibility.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <paths.h>
+#include <termios.h>
+#include <sys/time.h>
+#include <sysexits.h>
+#include <sys/param.h>
+#include <sys/ioctl.h>
+
+#import "AMSerialPort.h"
+#import "AMSerialErrors.h"
+
+#import <IOKit/serial/IOSerialKeys.h>
+#if defined(MAC_OS_X_VERSION_10_4) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_4)
+ #import <IOKit/serial/ioss.h>
+#endif
+
+NSString *const AMSerialErrorDomain = @"de.harmless.AMSerial.ErrorDomain";
+
+
+@implementation AMSerialPort
+
+- (id)init:(NSString *)path withName:(NSString *)name type:(NSString *)type
+ // path is a bsdPath
+ // name is an IOKit service name
+{
+ if ((self = [super init])) {
+ bsdPath = [path copy];
+ serviceName = [name copy];
+ serviceType = [type copy];
+ optionsDictionary = [[NSMutableDictionary dictionaryWithCapacity:8] retain];
+#ifndef __OBJC_GC__
+ options = (struct termios* __strong)malloc(sizeof(*options));
+ originalOptions = (struct termios* __strong)malloc(sizeof(*originalOptions));
+ buffer = (char* __strong)malloc(AMSER_MAXBUFSIZE);
+ readfds = (fd_set* __strong)malloc(sizeof(*readfds));
+#else
+ options = (struct termios* __strong)NSAllocateCollectable(sizeof(*options), 0);
+ originalOptions = (struct termios* __strong)NSAllocateCollectable(sizeof(*originalOptions), 0);
+ buffer = (char* __strong)NSAllocateCollectable(AMSER_MAXBUFSIZE, 0);
+ readfds = (fd_set* __strong)NSAllocateCollectable(sizeof(*readfds), 0);
+#endif
+ fileDescriptor = -1;
+
+ writeLock = [[NSLock alloc] init];
+ readLock = [[NSLock alloc] init];
+ closeLock = [[NSLock alloc] init];
+
+ // By default blocking read attempts will timeout after 1 second
+ [self setReadTimeout:1.0];
+
+ // These are used by the AMSerialPortAdditions category only; pretend to use them here to silence warnings by the clang static analyzer.
+ (void)am_readTarget;
+ (void)am_readSelector;
+ (void)stopWriteInBackground;
+ (void)countWriteInBackgroundThreads;
+ (void)stopReadInBackground;
+ (void)countReadInBackgroundThreads;
+ }
+ return self;
+}
+
+#ifndef __OBJC_GC__
+
+- (void)dealloc
+{
+#ifdef AMSerialDebug
+ if (fileDescriptor != -1)
+ NSLog(@"It is a programmer error to have not called -close on an AMSerialPort you have opened");
+#endif
+
+ [readLock release]; readLock = nil;
+ [writeLock release]; writeLock = nil;
+ [closeLock release]; closeLock = nil;
+ [am_readTarget release]; am_readTarget = nil;
+
+ free(readfds); readfds = NULL;
+ free(buffer); buffer = NULL;
+ free(originalOptions); originalOptions = NULL;
+ free(options); options = NULL;
+ [optionsDictionary release]; optionsDictionary = nil;
+ [serviceName release]; serviceName = nil;
+ [serviceType release]; serviceType = nil;
+ [bsdPath release]; bsdPath = nil;
+ [super dealloc];
+}
+
+#else
+
+- (void)finalize
+{
+#ifdef AMSerialDebug
+ if (fileDescriptor != -1)
+ NSLog(@"It is a programmer error to have not called -close on an AMSerialPort you have opened");
+#endif
+ assert (fileDescriptor == -1);
+
+ [super finalize];
+}
+
+#endif
+
+// So NSLog and gdb's 'po' command give something useful
+- (NSString *)description
+{
+ NSString *result= [NSString stringWithFormat:@"<%@: address: %p, name: %@, path: %@, type: %@, fileHandle: %@, fileDescriptor: %d>", NSStringFromClass([self class]), self, serviceName, bsdPath, serviceType, fileHandle, fileDescriptor];
+ return result;
+}
+
+- (NSUInteger)hash
+{
+ return [[self bsdPath] hash];
+}
+
+- (BOOL)isEqual:(id)otherObject
+{
+ if ([otherObject isKindOfClass:[AMSerialPort class]])
+ return [[self bsdPath] isEqualToString:[otherObject bsdPath]];
+ return NO;
+}
+
+
+- (id)delegate
+{
+ return delegate;
+}
+
+- (void)setDelegate:(id)newDelegate
+{
+ id old = nil;
+
+ if (newDelegate != delegate) {
+ old = delegate;
+ delegate = [newDelegate retain];
+ [old release];
+ delegateHandlesReadInBackground = [delegate respondsToSelector:@selector(serialPortReadData:)];
+ delegateHandlesWriteInBackground = [delegate respondsToSelector:@selector(serialPortWriteProgress:)];
+ }
+}
+
+
+- (NSString *)bsdPath
+{
+ return bsdPath;
+}
+
+- (NSString *)name
+{
+ return serviceName;
+}
+
+- (NSString *)type
+{
+ return serviceType;
+}
+
+- (NSDictionary *)properties
+{
+ NSDictionary *result = nil;
+ kern_return_t kernResult;
+ CFMutableDictionaryRef matchingDictionary;
+ io_service_t serialService;
+
+ matchingDictionary = IOServiceMatching(kIOSerialBSDServiceValue);
+ CFDictionarySetValue(matchingDictionary, CFSTR(kIOTTYDeviceKey), (CFStringRef)[self name]);
+ if (matchingDictionary != NULL) {
+ CFRetain(matchingDictionary);
+ // This function decrements the refcount of the dictionary passed it
+ serialService = IOServiceGetMatchingService(kIOMasterPortDefault, matchingDictionary);
+
+ if (serialService) {
+ CFMutableDictionaryRef propertiesDict = NULL;
+ kernResult = IORegistryEntryCreateCFProperties(serialService, &propertiesDict, kCFAllocatorDefault, 0);
+ if (kernResult == KERN_SUCCESS) {
+ result = [[(NSDictionary*)propertiesDict copy] autorelease];
+ }
+ if (propertiesDict) {
+ CFRelease(propertiesDict);
+ }
+ // We have sucked this service dry of information so release it now.
+ (void)IOObjectRelease(serialService);
+ } else {
+#ifdef AMSerialDebug
+ NSLog(@"properties: no matching service for %@", matchingDictionary);
+#endif
+ }
+ CFRelease(matchingDictionary);
+ }
+ return result;
+}
+
+
+- (BOOL)isOpen
+{
+ // YES if port is open
+ return (fileDescriptor >= 0);
+}
+
+- (AMSerialPort *)obtainBy:(id)sender
+{
+ // get this port exclusively; NULL if it's not free
+ if (owner == nil) {
+ owner = sender;
+ return self;
+ } else
+ return nil;
+}
+
+- (void)free
+{
+ // give it back
+ owner = nil;
+ [self close]; // you never know ...
+}
+
+- (BOOL)available
+{
+ // check if port is free and can be obtained
+ return (owner == nil);
+}
+
+- (id)owner
+{
+ // who obtained the port?
+ return owner;
+}
+
+// Private
+- (NSFileHandle *)openWithFlags:(int)flags // use returned file handle to read and write
+{
+ NSFileHandle *result = nil;
+
+ const char *path = [bsdPath fileSystemRepresentation];
+ fileDescriptor = open(path, flags);
+
+#ifdef AMSerialDebug
+ NSLog(@"open %@ (%d)\n", bsdPath, fileDescriptor);
+#endif
+
+ if (fileDescriptor < 0) {
+#ifdef AMSerialDebug
+ NSLog(@"Error opening serial port %@ - %s(%d).\n", bsdPath, strerror(errno), errno);
+#endif
+ } else {
+ /*
+ if (fcntl(fileDescriptor, F_SETFL, fcntl(fileDescriptor, F_GETFL, 0) & !O_NONBLOCK) == -1)
+ {
+ NSLog(@"Error clearing O_NDELAY %@ - %s(%d).\n", bsdPath, strerror(errno), errno);
+ } // ... else
+ */
+ // get the current options and save them for later reset
+ if (tcgetattr(fileDescriptor, originalOptions) == -1) {
+#ifdef AMSerialDebug
+ NSLog(@"Error getting tty attributes %@ - %s(%d).\n", bsdPath, strerror(errno), errno);
+#endif
+ } else {
+ // Make an exact copy of the options
+ *options = *originalOptions;
+
+ // This object owns the fileDescriptor and must dispose it later
+ // In other words, you must balance calls to -open with -close
+ fileHandle = [[NSFileHandle alloc] initWithFileDescriptor:fileDescriptor];
+ result = fileHandle;
+ }
+ }
+ if (!result) { // failure
+ if (fileDescriptor >= 0) {
+ close(fileDescriptor);
+ }
+ fileDescriptor = -1;
+ }
+ return result;
+}
+
+// TODO: Sean: why is O_NONBLOCK commented? Do we want it or not?
+
+// use returned file handle to read and write
+- (NSFileHandle *)open
+{
+ return [self openWithFlags:(O_RDWR | O_NOCTTY)]; // | O_NONBLOCK);
+}
+
+// use returned file handle to read and write
+- (NSFileHandle *)openExclusively
+{
+ return [self openWithFlags:(O_RDWR | O_NOCTTY | O_EXLOCK | O_NONBLOCK)]; // | O_NONBLOCK);
+}
+
+- (void)close
+{
+ // Traditionally it is good to reset a serial port back to
+ // the state in which you found it. Let's continue that tradition.
+ if (fileDescriptor >= 0) {
+ //NSLog(@"close - attempt closeLock");
+ [closeLock lock];
+ //NSLog(@"close - closeLock locked");
+
+ // kill pending read by setting O_NONBLOCK
+ if (fcntl(fileDescriptor, F_SETFL, fcntl(fileDescriptor, F_GETFL, 0) | O_NONBLOCK) == -1) {
+#ifdef AMSerialDebug
+ NSLog(@"Error clearing O_NONBLOCK %@ - %s(%d).\n", bsdPath, strerror(errno), errno);
+#endif
+ }
+ if (tcsetattr(fileDescriptor, TCSANOW, originalOptions) == -1) {
+#ifdef AMSerialDebug
+ NSLog(@"Error resetting tty attributes - %s(%d).\n", strerror(errno), errno);
+#endif
+ }
+
+ // Disallows further access to the communications channel
+ [fileHandle closeFile];
+
+ // Release the fileHandle
+ [fileHandle release];
+ fileHandle = nil;
+
+#ifdef AMSerialDebug
+ NSLog(@"close (%d)\n", fileDescriptor);
+#endif
+ // Close the fileDescriptor, that is our responsibility since the fileHandle does not own it
+ close(fileDescriptor);
+ fileDescriptor = -1;
+
+ [closeLock unlock];
+ //NSLog(@"close - closeLock unlocked");
+ }
+}
+
+- (BOOL)drainInput
+{
+ BOOL result = (tcdrain(fileDescriptor) != -1);
+ return result;
+}
+
+- (BOOL)flushInput:(BOOL)fIn output:(BOOL)fOut // (fIn or fOut) must be YES
+{
+ int mode = 0;
+ if (fIn)
+ mode = TCIFLUSH;
+ if (fOut)
+ mode = TCOFLUSH;
+ if (fIn && fOut)
+ mode = TCIOFLUSH;
+
+ BOOL result = (tcflush(fileDescriptor, mode) != -1);
+ return result;
+}
+
+- (BOOL)sendBreak
+{
+ BOOL result = (tcsendbreak(fileDescriptor, 0) != -1);
+ return result;
+}
+
+- (BOOL)setDTR
+{
+ BOOL result = (ioctl(fileDescriptor, TIOCSDTR) != -1);
+ return result;
+}
+
+- (BOOL)clearDTR
+{
+ BOOL result = (ioctl(fileDescriptor, TIOCCDTR) != -1);
+ return result;
+}
+
+
+// read and write serial port settings through a dictionary
+
+- (void)buildOptionsDictionary
+{
+ [optionsDictionary removeAllObjects];
+ [optionsDictionary setObject:[self name] forKey:AMSerialOptionServiceName];
+ [optionsDictionary setObject:[NSString stringWithFormat:@"%ld", [self speed]] forKey:AMSerialOptionSpeed];
+ [optionsDictionary setObject:[NSString stringWithFormat:@"%lu", [self dataBits]] forKey:AMSerialOptionDataBits];
+ switch ([self parity]) {
+ case kAMSerialParityOdd: {
+ [optionsDictionary setObject:@"Odd" forKey:AMSerialOptionParity];
+ break;
+ }
+ case kAMSerialParityEven: {
+ [optionsDictionary setObject:@"Even" forKey:AMSerialOptionParity];
+ break;
+ }
+ default:;
+ }
+
+ [optionsDictionary setObject:[NSString stringWithFormat:@"%d", [self stopBits]] forKey:AMSerialOptionStopBits];
+ if ([self RTSInputFlowControl])
+ [optionsDictionary setObject:@"RTS" forKey:AMSerialOptionInputFlowControl];
+ if ([self DTRInputFlowControl])
+ [optionsDictionary setObject:@"DTR" forKey:AMSerialOptionInputFlowControl];
+
+ if ([self CTSOutputFlowControl])
+ [optionsDictionary setObject:@"CTS" forKey:AMSerialOptionOutputFlowControl];
+ if ([self DSROutputFlowControl])
+ [optionsDictionary setObject:@"DSR" forKey:AMSerialOptionOutputFlowControl];
+ if ([self CAROutputFlowControl])
+ [optionsDictionary setObject:@"CAR" forKey:AMSerialOptionOutputFlowControl];
+
+ if ([self echoEnabled])
+ [optionsDictionary setObject:@"YES" forKey:AMSerialOptionEcho];
+
+ if ([self canonicalMode])
+ [optionsDictionary setObject:@"YES" forKey:AMSerialOptionCanonicalMode];
+
+}
+
+
+- (NSDictionary *)options
+{
+ // will open the port to get options if neccessary
+ if ([optionsDictionary objectForKey:AMSerialOptionServiceName] == nil) {
+ if (fileDescriptor < 0) {
+ [self open];
+ [self close];
+ }
+ [self buildOptionsDictionary];
+ }
+ return [NSMutableDictionary dictionaryWithDictionary:optionsDictionary];
+}
+
+- (void)setOptions:(NSDictionary *)newOptions
+{
+ // AMSerialOptionServiceName HAS to match! You may NOT switch ports using this
+ // method.
+ NSString *temp;
+
+ if ([(NSString *)[newOptions objectForKey:AMSerialOptionServiceName] isEqualToString:[self name]]) {
+ [self clearError];
+ [optionsDictionary addEntriesFromDictionary:newOptions];
+ // parse dictionary
+ temp = (NSString *)[optionsDictionary objectForKey:AMSerialOptionSpeed];
+ [self setSpeed:[temp intValue]];
+
+ temp = (NSString *)[optionsDictionary objectForKey:AMSerialOptionDataBits];
+ [self setDataBits:[temp intValue]];
+
+ temp = (NSString *)[optionsDictionary objectForKey:AMSerialOptionParity];
+ if (temp == nil)
+ [self setParity:kAMSerialParityNone];
+ else if ([temp isEqualToString:@"Odd"])
+ [self setParity:kAMSerialParityOdd];
+ else
+ [self setParity:kAMSerialParityEven];
+
+ temp = (NSString *)[optionsDictionary objectForKey:AMSerialOptionStopBits];
+ int numStopBits = [temp intValue];
+ [self setStopBits:(AMSerialStopBits)numStopBits];
+
+ temp = (NSString *)[optionsDictionary objectForKey:AMSerialOptionInputFlowControl];
+ [self setRTSInputFlowControl:[temp isEqualToString:@"RTS"]];
+ [self setDTRInputFlowControl:[temp isEqualToString:@"DTR"]];
+
+ temp = (NSString *)[optionsDictionary objectForKey:AMSerialOptionOutputFlowControl];
+ [self setCTSOutputFlowControl:[temp isEqualToString:@"CTS"]];
+ [self setDSROutputFlowControl:[temp isEqualToString:@"DSR"]];
+ [self setCAROutputFlowControl:[temp isEqualToString:@"CAR"]];
+
+ temp = (NSString *)[optionsDictionary objectForKey:AMSerialOptionEcho];
+ [self setEchoEnabled:(temp != nil)];
+
+ temp = (NSString *)[optionsDictionary objectForKey:AMSerialOptionCanonicalMode];
+ [self setCanonicalMode:(temp != nil)];
+
+ [self commitChanges];
+ } else {
+#ifdef AMSerialDebug
+ NSLog(@"Error setting options for port %s (wrong port name: %s).\n", [self name], [newOptions objectForKey:AMSerialOptionServiceName]);
+#endif
+ }
+}
+
+
+- (long)speed
+{
+ return cfgetospeed(options); // we should support cfgetispeed too
+}
+
+- (BOOL)setSpeed:(long)speed
+{
+ BOOL result = YES;
+ // we should support setting input and output speed separately
+ int errorCode = 0;
+
+// ***NOTE***: This code does not seem to work. It was taken from Apple's sample code:
+// <http://developer.apple.com/samplecode/SerialPortSample/listing2.html>
+// and that code does not work either. select() times out regularly if this code path is taken.
+#if 0 && defined(MAC_OS_X_VERSION_10_4) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_4)
+ // Starting with Tiger, the IOSSIOSPEED ioctl can be used to set arbitrary baud rates
+ // other than those specified by POSIX. The driver for the underlying serial hardware
+ // ultimately determines which baud rates can be used. This ioctl sets both the input
+ // and output speed.
+
+ speed_t newSpeed = speed;
+ if (fileDescriptor >= 0) {
+ errorCode = ioctl(fileDescriptor, IOSSIOSPEED, &newSpeed);
+ } else {
+ result = NO;
+ gotError = YES;
+ lastError = EBADF; // Bad file descriptor
+ }
+#else
+ // set both the input and output speed
+ errorCode = cfsetspeed(options, speed);
+#endif
+ if (errorCode == -1) {
+ result = NO;
+ gotError = YES;
+ lastError = errno;
+ }
+ return result;
+}
+
+
+- (unsigned long)dataBits
+{
+ return 5 + ((options->c_cflag & CSIZE) >> 8);
+ // man ... I *hate* C syntax ...
+}
+
+- (void)setDataBits:(unsigned long)bits // 5 to 8 (5 is marked as "(pseudo)")
+{
+ // ?? options->c_oflag &= ~OPOST;
+ options->c_cflag &= ~CSIZE;
+ switch (bits) {
+ case 5: options->c_cflag |= CS5; // redundant since CS5 == 0
+ break;
+ case 6: options->c_cflag |= CS6;
+ break;
+ case 7: options->c_cflag |= CS7;
+ break;
+ case 8: options->c_cflag |= CS8;
+ break;
+ }
+}
+
+
+- (AMSerialParity)parity
+{
+ AMSerialParity result;
+ if (options->c_cflag & PARENB) {
+ if (options->c_cflag & PARODD) {
+ result = kAMSerialParityOdd;
+ } else {
+ result = kAMSerialParityEven;
+ }
+ } else {
+ result = kAMSerialParityNone;
+ }
+ return result;
+}
+
+- (void)setParity:(AMSerialParity)newParity
+{
+ switch (newParity) {
+ case kAMSerialParityNone: {
+ options->c_cflag &= ~PARENB;
+ break;
+ }
+ case kAMSerialParityOdd: {
+ options->c_cflag |= PARENB;
+ options->c_cflag |= PARODD;
+ break;
+ }
+ case kAMSerialParityEven: {
+ options->c_cflag |= PARENB;
+ options->c_cflag &= ~PARODD;
+ break;
+ }
+ }
+}
+
+
+- (AMSerialStopBits)stopBits
+{
+ if (options->c_cflag & CSTOPB)
+ return kAMSerialStopBitsTwo;
+ else
+ return kAMSerialStopBitsOne;
+}
+
+- (void)setStopBits:(AMSerialStopBits)numBits
+{
+ if (numBits == kAMSerialStopBitsOne)
+ options->c_cflag &= ~CSTOPB;
+ else if (numBits == kAMSerialStopBitsTwo)
+ options->c_cflag |= CSTOPB;
+}
+
+
+- (BOOL)echoEnabled
+{
+ return (options->c_lflag & ECHO);
+}
+
+- (void)setEchoEnabled:(BOOL)echo
+{
+ if (echo)
+ options->c_lflag |= ECHO;
+ else
+ options->c_lflag &= ~ECHO;
+}
+
+
+- (BOOL)RTSInputFlowControl
+{
+ return (options->c_cflag & CRTS_IFLOW);
+}
+
+- (void)setRTSInputFlowControl:(BOOL)rts
+{
+ if (rts)
+ options->c_cflag |= CRTS_IFLOW;
+ else
+ options->c_cflag &= ~CRTS_IFLOW;
+}
+
+
+- (BOOL)DTRInputFlowControl
+{
+ return (options->c_cflag & CDTR_IFLOW);
+}
+
+- (void)setDTRInputFlowControl:(BOOL)dtr
+{
+ if (dtr)
+ options->c_cflag |= CDTR_IFLOW;
+ else
+ options->c_cflag &= ~CDTR_IFLOW;
+}
+
+
+- (BOOL)CTSOutputFlowControl
+{
+ return (options->c_cflag & CCTS_OFLOW);
+}
+
+- (void)setCTSOutputFlowControl:(BOOL)cts
+{
+ if (cts)
+ options->c_cflag |= CCTS_OFLOW;
+ else
+ options->c_cflag &= ~CCTS_OFLOW;
+}
+
+
+- (BOOL)DSROutputFlowControl
+{
+ return (options->c_cflag & CDSR_OFLOW);
+}
+
+- (void)setDSROutputFlowControl:(BOOL)dsr
+{
+ if (dsr)
+ options->c_cflag |= CDSR_OFLOW;
+ else
+ options->c_cflag &= ~CDSR_OFLOW;
+}
+
+
+- (BOOL)CAROutputFlowControl
+{
+ return (options->c_cflag & CCAR_OFLOW);
+}
+
+- (void)setCAROutputFlowControl:(BOOL)car
+{
+ if (car)
+ options->c_cflag |= CCAR_OFLOW;
+ else
+ options->c_cflag &= ~CCAR_OFLOW;
+}
+
+
+- (BOOL)hangupOnClose
+{
+ return (options->c_cflag & HUPCL);
+}
+
+- (void)setHangupOnClose:(BOOL)hangup
+{
+ if (hangup)
+ options->c_cflag |= HUPCL;
+ else
+ options->c_cflag &= ~HUPCL;
+}
+
+- (BOOL)localMode
+{
+ return (options->c_cflag & CLOCAL);
+}
+
+- (void)setLocalMode:(BOOL)local
+{
+ // YES = ignore modem status lines
+ if (local)
+ options->c_cflag |= CLOCAL;
+ else
+ options->c_cflag &= ~CLOCAL;
+}
+
+- (BOOL)canonicalMode
+{
+ return (options->c_lflag & ICANON);
+}
+
+- (void)setCanonicalMode:(BOOL)flag
+{
+ if (flag)
+ options->c_lflag |= ICANON;
+ else
+ options->c_lflag &= ~ICANON;
+}
+
+- (char)endOfLineCharacter
+{
+ return options->c_cc[VEOL];
+}
+
+- (void)setEndOfLineCharacter:(char)eol
+{
+ options->c_cc[VEOL] = eol;
+}
+
+- (void)clearError
+{
+ // call this before changing any settings
+ gotError = NO;
+}
+
+- (BOOL)commitChanges
+{
+ // call this after using any of the setters above
+ if (gotError)
+ return NO;
+
+ if (tcsetattr(fileDescriptor, TCSANOW, options) == -1) {
+ // something went wrong
+ gotError = YES;
+ lastError = errno;
+ return NO;
+ } else {
+ [self buildOptionsDictionary];
+ return YES;
+ }
+}
+
+- (int)errorCode
+{
+ // if -commitChanges returns NO, look here for further info
+ return lastError;
+}
+
+- (NSTimeInterval)readTimeout
+{
+ return readTimeout;
+}
+
+- (void)setReadTimeout:(NSTimeInterval)aReadTimeout
+{
+ readTimeout = aReadTimeout;
+}
+
+// private methods
+
+- (void)readTimeoutAsTimeval:(struct timeval*)timeout
+{
+ NSTimeInterval timeoutInterval = [self readTimeout];
+ double numSecs = trunc(timeoutInterval);
+ double numUSecs = (timeoutInterval-numSecs)*1000000.0;
+ timeout->tv_sec = (time_t)lrint(numSecs);
+ timeout->tv_usec = (suseconds_t)lrint(numUSecs);
+}
+
+
+@end
98 AMSerialPortAdditions.h
@@ -0,0 +1,98 @@
+//
+// AMSerialPortAdditions.h
+//
+// Created by Andreas on Thu May 02 2002.
+// Copyright (c) 2001-2009 Andreas Mayer. All rights reserved.
+//
+// 2002-10-04 Andreas Mayer
+// - readDataInBackgroundWithTarget:selector: and writeDataInBackground: added
+// 2002-10-10 Andreas Mayer
+// - stopWriteInBackground added
+// 2002-10-17 Andreas Mayer
+// - numberOfWriteInBackgroundThreads added
+// 2002-10-25 Andreas Mayer
+// - readDataInBackground and stopReadInBackground added
+// 2004-02-10 Andreas Mayer
+// - replaced notifications for background reading/writing with direct messages to delegate
+// see informal protocol
+// 2004-08-18 Andreas Mayer
+// - readStringOfLength: added (suggested by Michael Beck)
+// 2006-08-16 Andreas Mayer / Sean McBride
+// - changed interface for blocking read/write access significantly
+// - fixed -checkRead and renamed it to -bytesAvailable
+// - see AMSerialPort_Deprecated for old interfaces
+// 2007-10-26 Sean McBride
+// - made code 64 bit and garbage collection clean
+// 2009-05-08 Sean McBride
+// - added writeBytes:length:error: method
+
+#import "AMSDKCompatibility.h"
+
+#import <Foundation/Foundation.h>
+#import "AMSerialPort.h"
+
+
+@interface AMSerialPort (AMSerialPortAdditions)
+
+// returns the number of bytes available in the input buffer
+// Be careful how you use this information, it may be out of date just after you get it
+- (int)bytesAvailable;
+
+- (void)waitForInput:(id)target selector:(SEL)selector;
+
+
+// all blocking reads returns after [self readTimout] seconds elapse, at the latest
+- (NSData *)readAndReturnError:(NSError **)error;
+
+// returns after 'bytes' bytes are read
+- (NSData *)readBytes:(NSUInteger)bytes error:(NSError **)error;
+
+// returns when 'stopChar' is encountered
+- (NSData *)readUpToChar:(char)stopChar error:(NSError **)error;
+
+// returns after 'bytes' bytes are read or if 'stopChar' is encountered, whatever comes first
+- (NSData *)readBytes:(NSUInteger)bytes upToChar:(char)stopChar error:(NSError **)error;
+
+// data read will be converted into an NSString, using the given encoding
+// NOTE: encodings that take up more than one byte per character may fail if only a part of the final string was received
+- (NSString *)readStringUsingEncoding:(NSStringEncoding)encoding error:(NSError **)error;
+
+- (NSString *)readBytes:(NSUInteger)bytes usingEncoding:(NSStringEncoding)encoding error:(NSError **)error;
+
+// NOTE: 'stopChar' has to be a byte value, using the given encoding; you can not wait for an arbitrary character from a multi-byte encoding
+- (NSString *)readUpToChar:(char)stopChar usingEncoding:(NSStringEncoding)encoding error:(NSError **)error;
+
+- (NSString *)readBytes:(NSUInteger)bytes upToChar:(char)stopChar usingEncoding:(NSStringEncoding)encoding error:(NSError **)error;
+
+// write to the serial port; NO if an error occured
+- (BOOL)writeData:(NSData *)data error:(NSError **)error;
+
+- (BOOL)writeString:(NSString *)string usingEncoding:(NSStringEncoding)encoding error:(NSError **)error;
+
+- (BOOL)writeBytes:(const void *)bytes length:(NSUInteger)length error:(NSError **)error;
+
+
+- (void)readDataInBackground;
+//
+// Will send serialPortReadData: to delegate
+// the dataDictionary object will contain these entries:
+// 1. "serialPort": the AMSerialPort object that sent the message
+// 2. "data": (NSData *)data - received data
+
+- (void)stopReadInBackground;
+
+- (void)writeDataInBackground:(NSData *)data;
+//
+// Will send serialPortWriteProgress: to delegate if task lasts more than
+// approximately three seconds.
+// the dataDictionary object will contain these entries:
+// 1. "serialPort": the AMSerialPort object that sent the message
+// 2. "value": (NSNumber *)value - bytes sent
+// 3. "total": (NSNumber *)total - bytes total
+
+- (void)stopWriteInBackground;
+
+- (int)numberOfWriteInBackgroundThreads;
+
+
+@end
626 AMSerialPortAdditions.m
@@ -0,0 +1,626 @@
+//
+// AMSerialPortAdditions.m
+//
+// Created by Andreas on Thu May 02 2002.
+// Copyright (c) 2001-2009 Andreas Mayer. All rights reserved.
+//
+// 2002-07-02 Andreas Mayer
+// - initialize buffer in readString
+// 2002-10-04 Andreas Mayer
+// - readDataInBackgroundWithTarget:selector: and writeDataInBackground: added
+// 2002-10-10 Andreas Mayer
+// - stopWriteInBackground added
+// - send notifications about sent data through distributed notification center
+// 2002-10-17 Andreas Mayer
+// - numberOfWriteInBackgroundThreads added
+// - if total write time will exceed 3 seconds, send
+// CommXWriteInBackgroundProgressNotification without delay
+// 2002-10-25 Andreas Mayer
+// - readDataInBackground and stopReadInBackground added
+// 2004-08-18 Andreas Mayer
+// - readStringOfLength: added (suggested by Michael Beck)
+// 2005-04-11 Andreas Mayer
+// - attempt at a fix for readDataInBackgroundThread - fileDescriptor could already be closed
+// (thanks to David Bainbridge for the bug report) does not work as of yet
+// 2007-10-26 Sean McBride
+// - made code 64 bit and garbage collection clean
+// 2009-05-08 Sean McBride
+// - added writeBytes:length:error: method
+// - associated a name with created threads (for debugging, 10.6 only)
+
+
+#import "AMSDKCompatibility.h"
+
+#import <sys/ioctl.h>
+#import <sys/filio.h>
+#import <pthread.h>
+
+#import "AMSerialPortAdditions.h"
+#import "AMSerialErrors.h"
+
+
+@interface AMSerialPort (AMSerialPortAdditionsPrivate)
+- (void)readDataInBackgroundThread;
+- (void)writeDataInBackgroundThread:(NSData *)data;
+- (id)am_readTarget;
+- (void)am_setReadTarget:(id)newReadTarget;
+- (NSData *)readAndStopAfterBytes:(BOOL)stopAfterBytes bytes:(NSUInteger)bytes stopAtChar:(BOOL)stopAtChar stopChar:(char)stopChar error:(NSError **)error;
+- (void)reportProgress:(NSUInteger)progress dataLen:(NSUInteger)dataLen;
+@end
+
+
+@implementation AMSerialPort (AMSerialPortAdditions)
+
+
+// ============================================================
+#pragma mark -
+#pragma mark blocking IO
+// ============================================================
+
+- (void)doRead:(NSTimer *)timer
+{
+ (void)timer;
+
+#ifdef AMSerialDebug
+ NSLog(@"doRead");
+#endif
+ int res;
+ struct timeval timeout;
+ if (fileDescriptor >= 0) {
+ FD_ZERO(readfds);
+ FD_SET(fileDescriptor, readfds);
+ [self readTimeoutAsTimeval:&timeout];
+ res = select(fileDescriptor+1, readfds, nil, nil, &timeout);
+ if (res >= 1) {
+ NSString *readStr = [self readStringUsingEncoding:NSUTF8StringEncoding error:NULL];
+ [[self am_readTarget] performSelector:am_readSelector withObject:readStr];
+ [self am_setReadTarget:nil];
+ } else {
+ [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(doRead:) userInfo:self repeats:NO];
+ }
+ } else {
+ // file already closed
+ [self am_setReadTarget:nil];
+ }
+}
+
+// all blocking reads returns after [self readTimout] seconds elapse, at the latest
+- (NSData *)readAndReturnError:(NSError **)error
+{
+ NSData *result = [self readAndStopAfterBytes:NO bytes:0 stopAtChar:NO stopChar:0 error:error];
+ return result;
+}
+
+// returns after 'bytes' bytes are read
+- (NSData *)readBytes:(NSUInteger)bytes error:(NSError **)error
+{
+ NSData *result = [self readAndStopAfterBytes:YES bytes:bytes stopAtChar:NO stopChar:0 error:error];
+ return result;
+}
+
+// returns when 'stopChar' is encountered
+- (NSData *)readUpToChar:(char)stopChar error:(NSError **)error
+{
+ NSData *result = [self readAndStopAfterBytes:NO bytes:0 stopAtChar:YES stopChar:stopChar error:error];
+ return result;
+}
+
+// returns after 'bytes' bytes are read or if 'stopChar' is encountered, whatever comes first
+- (NSData *)readBytes:(NSUInteger)bytes upToChar:(char)stopChar error:(NSError **)error
+{
+ NSData *result = [self readAndStopAfterBytes:YES bytes:bytes stopAtChar:YES stopChar:stopChar error:error];
+ return result;
+}
+
+// data read will be converted into an NSString, using the given encoding
+// NOTE: encodings that take up more than one byte per character may fail if only a part of the final string was received
+- (NSString *)readStringUsingEncoding:(NSStringEncoding)encoding error:(NSError **)error
+{
+ NSString *result = nil;
+ NSData *data = [self readAndStopAfterBytes:NO bytes:0 stopAtChar:NO stopChar:0 error:error];
+ if (data) {
+ result = [[[NSString alloc] initWithData:data encoding:encoding] autorelease];
+ }
+ return result;
+}
+
+- (NSString *)readBytes:(NSUInteger)bytes usingEncoding:(NSStringEncoding)encoding error:(NSError **)error
+{
+ NSString *result = nil;
+ NSData *data = [self readAndStopAfterBytes:YES bytes:bytes stopAtChar:NO stopChar:0 error:error];
+ if (data) {
+ result = [[[NSString alloc] initWithData:data encoding:encoding] autorelease];
+ }
+ return result;
+}
+
+// NOTE: 'stopChar' has to be a byte value, using the given encoding; you can not wait for an arbitrary character from a multi-byte encoding
+- (NSString *)readUpToChar:(char)stopChar usingEncoding:(NSStringEncoding)encoding error:(NSError **)error
+{
+ NSString *result = nil;
+ NSData *data = [self readAndStopAfterBytes:NO bytes:0 stopAtChar:YES stopChar:stopChar error:error];
+ if (data) {
+ result = [[[NSString alloc] initWithData:data encoding:encoding] autorelease];
+ }
+ return result;
+}
+
+- (NSString *)readBytes:(NSUInteger)bytes upToChar:(char)stopChar usingEncoding:(NSStringEncoding)encoding error:(NSError **)error
+{
+ NSString *result = nil;
+ NSData *data = [self readAndStopAfterBytes:YES bytes:bytes stopAtChar:YES stopChar:stopChar error:error];
+ if (data) {
+ result = [[[NSString alloc] initWithData:data encoding:encoding] autorelease];
+ }
+ return result;
+}
+
+
+// write to the serial port; NO if an error occured
+- (BOOL)writeData:(NSData *)data error:(NSError **)error
+{
+#ifdef AMSerialDebug
+ NSLog(@"•wrote: %@%@", data, [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
+#endif
+
+ BOOL result = NO;
+
+ const char *dataBytes = (const char*)[data bytes];
+ NSUInteger dataLen = [data length];
+ ssize_t bytesWritten = 0;
+ int errorCode = kAMSerialErrorNone;
+ if (dataBytes && (dataLen > 0)) {
+ bytesWritten = write(fileDescriptor, dataBytes, dataLen);
+ if (bytesWritten < 0) {
+ errorCode = kAMSerialErrorFatal;
+ } else if ((NSUInteger)bytesWritten == dataLen) {
+ result = YES;
+ } else {
+ errorCode = kAMSerialErrorOnlySomeDataWritten;
+ }
+ } else {
+ errorCode = kAMSerialErrorNoDataToWrite;
+ }
+ if (error) {
+ NSDictionary *userInfo = nil;
+ if (bytesWritten > 0) {
+ NSNumber* bytesWrittenNum = [NSNumber numberWithUnsignedLongLong:bytesWritten];
+ userInfo = [NSDictionary dictionaryWithObject:bytesWrittenNum forKey:@"bytesWritten"];
+ }
+ *error = [NSError errorWithDomain:AMSerialErrorDomain code:errorCode userInfo:userInfo];
+ }
+
+ // To prevent premature collection. (Under GC, the given NSData may have no strong references for all we know, and our inner pointer does not keep the NSData alive. So without this, the data could be collected before we are done with it!)
+ [data self];
+
+ return result;
+}
+
+- (BOOL)writeString:(NSString *)string usingEncoding:(NSStringEncoding)encoding error:(NSError **)error
+{
+ NSData *data = [string dataUsingEncoding:encoding];
+ return [self writeData:data error:error];
+}
+
+- (BOOL)writeBytes:(const void *)bytes length:(NSUInteger)length error:(NSError **)error
+{
+ NSData *data = [NSData dataWithBytes:bytes length:length];
+ return [self writeData:data error:error];
+}
+
+- (int)bytesAvailable
+{
+#ifdef AMSerialDebug
+ NSLog(@"bytesAvailable");
+#endif
+
+ // yes, that cast is correct. ioctl() is declared to take a char* but should be void* as really it
+ // depends on the 2nd parameter. Ahhh, I love crappy old UNIX APIs :)
+ int result = 0;
+ int err = ioctl(fileDescriptor, FIONREAD, (char *)&result);
+ if (err != 0) {
+ result = -1;
+ }
+ return result;
+}
+
+
+- (void)waitForInput:(id)target selector:(SEL)selector
+{
+#ifdef AMSerialDebug
+ NSLog(@"waitForInput");
+#endif
+ [self am_setReadTarget:target];
+ am_readSelector = selector;
+ [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(doRead:) userInfo:self repeats:NO];
+}
+
+// ============================================================
+#pragma mark -
+#pragma mark threaded IO
+// ============================================================
+
+- (void)readDataInBackground
+{
+#ifdef AMSerialDebug
+ NSLog(@"readDataInBackground");
+#endif
+ if (delegateHandlesReadInBackground) {
+ countReadInBackgroundThreads++;
+ [NSThread detachNewThreadSelector:@selector(readDataInBackgroundThread) toTarget:self withObject:nil];
+ } else {
+ // ... throw exception?
+ }
+}
+
+- (void)stopReadInBackground
+{
+#ifdef AMSerialDebug
+ NSLog(@"stopReadInBackground");
+#endif
+ stopReadInBackground = YES;
+}
+
+- (void)writeDataInBackground:(NSData *)data
+{
+#ifdef AMSerialDebug
+ NSLog(@"writeDataInBackground");
+#endif
+ if (delegateHandlesWriteInBackground) {
+ countWriteInBackgroundThreads++;
+ [NSThread detachNewThreadSelector:@selector(writeDataInBackgroundThread:) toTarget:self withObject:data];
+ } else {
+ // ... throw exception?
+ }
+}
+
+- (void)stopWriteInBackground
+{
+#ifdef AMSerialDebug
+ NSLog(@"stopWriteInBackground");
+#endif
+ stopWriteInBackground = YES;
+}
+
+- (int)numberOfWriteInBackgroundThreads
+{
+ return countWriteInBackgroundThreads;
+}
+
+
+@end
+
+#pragma mark -
+
+@implementation AMSerialPort (AMSerialPortAdditionsPrivate)
+
+// ============================================================
+#pragma mark -
+#pragma mark threaded methods
+// ============================================================
+
+- (void)readDataInBackgroundThread
+{
+#if (MAC_OS_X_VERSION_MIN_REQUIRED >= 1060)
+ (void)pthread_setname_np ("de.harmless.AMSerialPort.readDataInBackgroundThread");
+#endif
+
+ NSData *data = nil;
+ void *localBuffer;
+ ssize_t bytesRead = 0;
+ fd_set *localReadFDs = NULL;
+
+ [readLock lock]; // read in sequence
+ //NSLog(@"readDataInBackgroundThread - [readLock lock]");
+
+ localBuffer = malloc(AMSER_MAXBUFSIZE);
+ stopReadInBackground = NO;
+ NSAutoreleasePool *localAutoreleasePool = [[NSAutoreleasePool alloc] init];
+ [closeLock lock];
+ if ((fileDescriptor >= 0) && (!stopReadInBackground)) {
+ //NSLog(@"readDataInBackgroundThread - [closeLock lock]");
+ localReadFDs = (fd_set*)malloc(sizeof(fd_set));
+ FD_ZERO(localReadFDs);
+ FD_SET(fileDescriptor, localReadFDs);
+ [closeLock unlock];
+ //NSLog(@"readDataInBackgroundThread - [closeLock unlock]");
+ int res = select(fileDescriptor+1, localReadFDs, nil, nil, nil); // timeout);
+ if ((res >= 1) && (fileDescriptor >= 0)) {
+ bytesRead = read(fileDescriptor, localBuffer, AMSER_MAXBUFSIZE);
+ }
+ data = [NSData dataWithBytes:localBuffer length:bytesRead];
+ [delegate performSelectorOnMainThread:@selector(serialPortReadData:) withObject:[NSDictionary dictionaryWithObjectsAndKeys: self, @"serialPort", data, @"data", nil] waitUntilDone:NO];
+ } else {
+ [closeLock unlock];
+ }
+ [localAutoreleasePool release];
+ free(localReadFDs);
+ free(localBuffer);
+
+ countReadInBackgroundThreads--;
+
+ [readLock unlock];
+ //NSLog(@"readDataInBackgroundThread - [readLock unlock]");
+
+}
+
+/* new version - does not work yet
+- (void)readDataInBackgroundThread
+{
+ NSData *data = nil;
+ void *localBuffer;
+ int bytesRead = 0;
+ fd_set *localReadFDs;
+
+#ifdef AMSerialDebug
+ NSLog(@"readDataInBackgroundThread: %@", [NSThread currentThread]);
+#endif
+ localBuffer = malloc(AMSER_MAXBUFSIZE);
+ [stopReadInBackgroundLock lock];
+ stopReadInBackground = NO;
+ //NSLog(@"stopReadInBackground set to NO: %@", [NSThread currentThread]);
+ [stopReadInBackgroundLock unlock];
+ //NSLog(@"attempt readLock: %@", [NSThread currentThread]);
+ [readLock lock]; // write in sequence
+ //NSLog(@"readLock locked: %@", [NSThread currentThread]);
+ //NSLog(@"attempt closeLock: %@", [NSThread currentThread]);
+ [closeLock lock];
+ //NSLog(@"closeLock locked: %@", [NSThread currentThread]);
+ if (!stopReadInBackground && (fileDescriptor >= 0)) {
+ NSAutoreleasePool *localAutoreleasePool = [[NSAutoreleasePool alloc] init];
+ localReadFDs = malloc(sizeof(*localReadFDs));
+ FD_ZERO(localReadFDs);
+ FD_SET(fileDescriptor, localReadFDs);
+ int res = select(fileDescriptor+1, localReadFDs, nil, nil, nil); // timeout);
+ if (res >= 1) {
+#ifdef AMSerialDebug
+ NSLog(@"attempt read: %@", [NSThread currentThread]);
+#endif
+ bytesRead = read(fileDescriptor, localBuffer, AMSER_MAXBUFSIZE);
+ }
+#ifdef AMSerialDebug
+ NSLog(@"data read: %@", [NSThread currentThread]);
+#endif
+ data = [NSData dataWithBytes:localBuffer length:bytesRead];
+#ifdef AMSerialDebug
+ NSLog(@"send AMSerialReadInBackgroundDataMessage");
+#endif
+ [delegate performSelectorOnMainThread:@selector(serialPortReadData:) withObject:[NSDictionary dictionaryWithObjectsAndKeys: self, @"serialPort", data, @"data", nil] waitUntilDone:NO];
+ free(localReadFDs);
+ [localAutoreleasePool release];
+ } else {
+#ifdef AMSerialDebug
+ NSLog(@"read stopped: %@", [NSThread currentThread]);
+#endif
+ }
+
+ [closeLock unlock];
+ //NSLog(@"closeLock unlocked: %@", [NSThread currentThread]);
+ [readLock unlock];
+ //NSLog(@"readLock unlocked: %@", [NSThread currentThread]);
+ [countReadInBackgroundThreadsLock lock];
+ countReadInBackgroundThreads--;
+ [countReadInBackgroundThreadsLock unlock];
+
+ free(localBuffer);
+}
+*/
+
+- (void)writeDataInBackgroundThread:(NSData *)data
+{
+#if (MAC_OS_X_VERSION_MIN_REQUIRED >= 1060)
+ (void)pthread_setname_np ("de.harmless.AMSerialPort.writeDataInBackgroundThread");
+#endif
+
+#ifdef AMSerialDebug
+ NSLog(@"writeDataInBackgroundThread");
+#endif
+ void *localBuffer;
+ NSUInteger pos;
+ NSUInteger bufferLen;
+ NSUInteger dataLen;
+ ssize_t written;
+ NSDate *nextNotificationDate;
+ BOOL notificationSent = NO;
+ long speed;
+ long estimatedTime;
+ BOOL error = NO;
+
+ NSAutoreleasePool *localAutoreleasePool = [[NSAutoreleasePool alloc] init];
+
+ [data retain];
+ localBuffer = malloc(AMSER_MAXBUFSIZE);
+ stopWriteInBackground = NO;
+ [writeLock lock]; // write in sequence
+ pos = 0;
+ dataLen = [data length];
+ speed = [self speed];
+ estimatedTime = (dataLen*8)/speed;
+ if (estimatedTime > 3) { // will take more than 3 seconds
+ notificationSent = YES;
+ [self reportProgress:pos dataLen:dataLen];
+ nextNotificationDate = [NSDate dateWithTimeIntervalSinceNow:1.0];
+ } else {
+ nextNotificationDate = [NSDate dateWithTimeIntervalSinceNow:2.0];
+ }
+ while (!stopWriteInBackground && (pos < dataLen) && !error) {
+ bufferLen = MIN(AMSER_MAXBUFSIZE, dataLen-pos);
+
+ [data getBytes:localBuffer range:NSMakeRange(pos, bufferLen)];
+ written = write(fileDescriptor, localBuffer, bufferLen);
+ error = (written == 0); // error condition
+ if (error)
+ break;
+ pos += written;
+
+ if ([(NSDate *)[NSDate date] compare:nextNotificationDate] == NSOrderedDescending) {
+ if (notificationSent || (pos < dataLen)) { // not for last block only
+ notificationSent = YES;
+ [self reportProgress:pos dataLen:dataLen];
+ nextNotificationDate = [NSDate dateWithTimeIntervalSinceNow:1.0];
+ }
+ }
+ }
+ if (notificationSent) {
+ [self reportProgress:pos dataLen:dataLen];
+ }
+ stopWriteInBackground = NO;
+ [writeLock unlock];
+ countWriteInBackgroundThreads--;
+
+ free(localBuffer);
+ [data release];
+ [localAutoreleasePool release];
+}
+
+- (id)am_readTarget
+{
+ return am_readTarget;
+}
+
+- (void)am_setReadTarget:(id)newReadTarget
+{
+ if (am_readTarget != newReadTarget) {
+ [newReadTarget retain];
+ [am_readTarget release];
+ am_readTarget = newReadTarget;
+ }
+}
+
+// Low-level blocking read method.
+// This method reads from the serial port and blocks as necessary, it returns when:
+// - [self readTimeout] seconds has elapsed
+// - if stopAfterBytes is YES, when 'bytesToRead' bytes have been read
+// - if stopAtChar is YES, when 'stopChar' is found at the end of the read buffer
+// - a fatal error occurs
+//
+// Upon return: as long as some data was actually read, and no serious error occured, an autoreleased NSData
+// object with that data is created and returned, otherwise nil is.
+- (NSData *)readAndStopAfterBytes:(BOOL)stopAfterBytes bytes:(NSUInteger)bytesToRead stopAtChar:(BOOL)stopAtChar stopChar:(char)stopChar error:(NSError **)error
+{
+ NSData *result = nil;
+
+ struct timeval timeout;
+ NSUInteger bytesRead = 0;
+ int errorCode = kAMSerialErrorNone;
+ int endCode = kAMSerialEndOfStream;
+ NSError *underlyingError = nil;
+
+ // Note the time that we start
+ NSDate *startTime = [NSDate date];
+
+ // How long, in total, do we block before timing out?
+ NSTimeInterval totalTimeout = [self readTimeout];
+
+ // This value will be decreased each time through the loop
+ NSTimeInterval remainingTimeout = totalTimeout;
+
+ while (YES) {
+ if (remainingTimeout <= 0.0) {
+ errorCode = kAMSerialErrorTimeout;
+ break;
+ } else {
+ // Convert from NSTimeInterval to struct timeval
+ double numSecs = trunc(remainingTimeout);
+ double numUSecs = (remainingTimeout-numSecs)*1000000.0;
+ timeout.tv_sec = (time_t)lrint(numSecs);
+ timeout.tv_usec = (suseconds_t)lrint(numUSecs);
+#ifdef AMSerialDebug
+ NSLog(@"timeout: %fs = %ds and %dus", remainingTimeout, timeout.tv_sec, timeout.tv_usec);
+#endif
+
+ // If the remaining time is so small that it has rounded to zero, bump it up to 1 microsec.
+ // Why? Because passing a zeroed timeval to select() indicates that we want to poll, but we don't.
+ if ((timeout.tv_sec == 0) && (timeout.tv_usec == 0)) {
+ timeout.tv_usec = 1;
+ }
+ FD_ZERO(readfds);
+ FD_SET(fileDescriptor, readfds);
+ [self readTimeoutAsTimeval:&timeout];
+ int selectResult = select(fileDescriptor+1, readfds, NULL, NULL, &timeout);
+ if (selectResult == -1) {
+ errorCode = kAMSerialErrorFatal;
+ break;
+ } else if (selectResult == 0) {
+ errorCode = kAMSerialErrorTimeout;
+ break;
+ } else {
+ size_t sizeToRead;
+ if (stopAfterBytes) {
+ sizeToRead = (MIN(bytesToRead, AMSER_MAXBUFSIZE))-bytesRead;
+ } else {
+ sizeToRead = AMSER_MAXBUFSIZE-bytesRead;
+ }
+ ssize_t readResult = read(fileDescriptor, buffer+bytesRead, sizeToRead);
+ if (readResult > 0) {
+ bytesRead += readResult;
+ if (stopAfterBytes) {
+ if (bytesRead == bytesToRead) {
+ endCode = kAMSerialStopLengthReached;
+ break;
+ } else if (bytesRead > bytesToRead) {
+ endCode = kAMSerialStopLengthExceeded;
+ break;
+ }
+ }
+ if (stopAtChar && (buffer[bytesRead-1] == stopChar)) {
+ endCode = kAMSerialStopCharReached;
+ break;
+ }
+ if (bytesRead >= AMSER_MAXBUFSIZE) {
+ errorCode = kAMSerialErrorInternalBufferFull;
+ break;
+ }
+ } else if (readResult == 0) {
+ // Should not be possible since select() has indicated data is available
+ errorCode = kAMSerialErrorFatal;
+ break;
+ } else {
+ // Make underlying error
+ underlyingError = [NSError errorWithDomain:NSPOSIXErrorDomain code:readResult userInfo:nil];
+ errorCode = kAMSerialErrorFatal;
+ break;
+ }
+ }
+
+ // Reduce the timeout value by the amount of time actually spent so far
+ remainingTimeout = totalTimeout - [[NSDate date] timeIntervalSinceDate:startTime];
+ }
+ }
+
+ if (error) {
+ NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
+ [userInfo setObject:[NSNumber numberWithUnsignedLongLong:bytesRead] forKey:@"bytesRead"];
+ if (underlyingError) {
+ [userInfo setObject:underlyingError forKey:NSUnderlyingErrorKey];
+ }
+ if (errorCode == kAMSerialErrorNone) {
+ [userInfo setObject:[NSNumber numberWithInt:endCode] forKey:@"endCode"];
+ }
+ *error = [NSError errorWithDomain:AMSerialErrorDomain code:errorCode userInfo:userInfo];
+ }
+ if ((bytesRead > 0) && (errorCode != kAMSerialErrorFatal)) {
+ result = [NSData dataWithBytes:buffer length:bytesRead];
+ }
+
+#ifdef AMSerialDebug
+ NSLog(@"• read: %@%@", result, [[NSString alloc] initWithData:result encoding:NSUTF8StringEncoding]);
+#endif
+
+ return result;
+}
+
+- (void)reportProgress:(NSUInteger)progress dataLen:(NSUInteger)dataLen
+{
+#ifdef AMSerialDebug
+ NSLog(@"send AMSerialWriteInBackgroundProgressMessage");
+#endif
+ [delegate performSelectorOnMainThread:@selector(serialPortWriteProgress:) withObject:
+ [NSDictionary dictionaryWithObjectsAndKeys:
+ self, @"serialPort",
+ [NSNumber numberWithUnsignedLongLong:progress], @"value",
+ [NSNumber numberWithUnsignedLongLong:dataLen], @"total", nil]
+ waitUntilDone:NO];
+}
+
+@end
57 AMSerialPortList.h
@@ -0,0 +1,57 @@
+//
+// AMSerialPortList.h
+//
+// Created by Andreas on 2002-04-24.
+// Copyright (c) 2001 Andreas Mayer. All rights reserved.
+//
+// 2002-09-09 Andreas Mayer
+// - reuse AMSerialPort objects when calling init on an existing AMSerialPortList
+// 2002-09-30 Andreas Mayer
+// - added +sharedPortList
+// 2004-02-10 Andreas Mayer
+// - added +portEnumerator
+// 2006-08-16 Andreas Mayer
+// - added methods dealing with ports of a certain serial type
+// - renamed -getSerialPorts to -serialPorts - moved old declaration to Deprecated category
+// 2007-05-22 Nick Zitzmann
+// - added notifications for when serial ports are added/removed
+// 2007-10-26 Sean McBride
+// - made code 64 bit and garbage collection clean
+
+#import "AMSDKCompatibility.h"
+
+#import <Foundation/Foundation.h>
+
+// For constants clients will want to pass to methods that want a 'serialTypeKey'
+#import <IOKit/serial/IOSerialKeys.h>
+// note: the constants are C strings, so use '@' or CFSTR to convert, for example:
+// NSArray *ports = [[AMSerialPort sharedPortList] serialPortsOfType:@kIOSerialBSDModemType];
+// NSArray *ports = [[AMSerialPort sharedPortList] serialPortsOfType:(NSString*)CFSTR(kIOSerialBSDModemType)];
+
+@class AMSerialPort;
+
+extern NSString * const AMSerialPortListDidAddPortsNotification;
+extern NSString * const AMSerialPortListDidRemovePortsNotification;
+extern NSString * const AMSerialPortListAddedPorts;
+extern NSString * const AMSerialPortListRemovedPorts;
+
+@interface AMSerialPortList : NSObject
+{
+@private
+ NSMutableArray *portList;
+}
+
++ (AMSerialPortList *)sharedPortList;
+
++ (NSEnumerator *)portEnumerator;
++ (NSEnumerator *)portEnumeratorForSerialPortsOfType:(NSString *)serialTypeKey;
+
+- (NSUInteger)count;
+- (AMSerialPort *)objectAtIndex:(NSUInteger)idx;
+- (AMSerialPort *)objectWithName:(NSString *)name;
+
+- (NSArray *)serialPorts;
+- (NSArray *)serialPortsOfType:(NSString *)serialTypeKey;
+
+
+@end
349 AMSerialPortList.m
@@ -0,0 +1,349 @@
+//
+// AMSerialPortList.m
+//
+// Created by Andreas on 2002-04-24.
+// Copyright (c) 2001-2009 Andreas Mayer. All rights reserved.
+//
+// 2002-09-09 Andreas Mayer
+// - reuse AMSerialPort objects when calling init on an existing AMSerialPortList
+// 2002-09-30 Andreas Mayer
+// - added +sharedPortList
+// 2004-07-05 Andreas Mayer
+// - added some log statements
+// 2007-05-22 Nick Zitzmann
+// - added notifications for when serial ports are added/removed
+// 2007-07-18 Sean McBride
+// - minor improvements to the added/removed notification support
+// - changed singleton creation technique, now matches Apple's sample code
+// - removed oldPortList as it is no longer needed
+// 2007-10-26 Sean McBride
+// - made code 64 bit and garbage collection clean
+// 2008-10-21 Sean McBride
+// - fixed some memory management issues
+
+
+#import "AMSDKCompatibility.h"
+
+#import "AMSerialPortList.h"
+#import "AMSerialPort.h"
+#import "AMStandardEnumerator.h"
+
+#include <termios.h>
+
+#include <CoreFoundation/CoreFoundation.h>
+
+#include <IOKit/IOKitLib.h>
+#include <IOKit/serial/IOSerialKeys.h>
+#include <IOKit/IOBSD.h>
+
+static AMSerialPortList *AMSerialPortListSingleton = nil;
+
+NSString *const AMSerialPortListDidAddPortsNotification = @"AMSerialPortListDidAddPortsNotification";
+NSString *const AMSerialPortListDidRemovePortsNotification = @"AMSerialPortListDidRemovePortsNotification";
+NSString *const AMSerialPortListAddedPorts = @"AMSerialPortListAddedPorts";
+NSString *const AMSerialPortListRemovedPorts = @"AMSerialPortListRemovedPorts";
+
+
+@implementation AMSerialPortList
+
++ (AMSerialPortList *)sharedPortList
+{
+ @synchronized(self) {
+ if (AMSerialPortListSingleton == nil) {
+#ifndef __OBJC_GC__
+ [[self alloc] init]; // assignment not done here
+#else
+ // Singleton creation is easy in the GC case, just create it if it hasn't been created yet,
+ // it won't get collected since globals are strongly referenced.
+ AMSerialPortListSingleton = [[self alloc] init];
+#endif
+ }
+ }
+ return AMSerialPortListSingleton;
+}
+
+#ifndef __OBJC_GC__
+
++ (id)allocWithZone:(NSZone *)zone
+{
+ id result = nil;
+ @synchronized(self) {
+ if (AMSerialPortListSingleton == nil) {
+ AMSerialPortListSingleton = [super allocWithZone:zone];
+ result = AMSerialPortListSingleton; // assignment and return on first allocation
+ //on subsequent allocation attempts return nil
+ }
+ }
+ return result;
+}
+
+- (id)copyWithZone:(NSZone *)zone
+{
+ (void)zone;
+ return self;
+}
+
+- (id)retain
+{
+ return self;
+}
+
+- (NSUInteger)retainCount
+{
+ return NSUIntegerMax; //denotes an object that cannot be released
+}
+
+- (oneway void)release
+{
+ //do nothing
+}
+
+- (id)autorelease
+{
+ return self;
+}
+
+- (void)dealloc
+{
+ [portList release]; portList = nil;
+ [super dealloc];
+}
+
+#endif
+
++ (NSEnumerator *)portEnumerator
+{
+ return [[[AMStandardEnumerator alloc] initWithCollection:[AMSerialPortList sharedPortList]
+ countSelector:@selector(count) objectAtIndexSelector:@selector(objectAtIndex:)] autorelease];
+}
+
++ (NSEnumerator *)portEnumeratorForSerialPortsOfType:(NSString *)serialTypeKey
+{
+ return [[[AMStandardEnumerator alloc] initWithCollection:[[AMSerialPortList sharedPortList]
+ serialPortsOfType:serialTypeKey] countSelector:@selector(count) objectAtIndexSelector:@selector(objectAtIndex:)] autorelease];
+}
+
+- (AMSerialPort *)portByPath:(NSString *)bsdPath
+{
+ AMSerialPort *result = nil;
+ AMSerialPort *port;
+ NSEnumerator *enumerator;
+
+ enumerator = [portList objectEnumerator];
+ while ((port = [enumerator nextObject]) != nil) {
+ if ([[port bsdPath] isEqualToString:bsdPath]) {
+ result = port;
+ break;
+ }
+ }
+ return result;
+}
+
+- (AMSerialPort *)getNextSerialPort:(io_iterator_t)serialPortIterator
+{
+ AMSerialPort *serialPort = nil;
+
+ io_object_t serialService = IOIteratorNext(serialPortIterator);
+ if (serialService != 0) {
+ CFStringRef modemName = (CFStringRef)IORegistryEntryCreateCFProperty(serialService, CFSTR(kIOTTYDeviceKey), kCFAllocatorDefault, 0);
+ CFStringRef bsdPath = (CFStringRef)IORegistryEntryCreateCFProperty(serialService, CFSTR(kIOCalloutDeviceKey), kCFAllocatorDefault, 0);
+ CFStringRef serviceType = (CFStringRef)IORegistryEntryCreateCFProperty(serialService, CFSTR(kIOSerialBSDTypeKey), kCFAllocatorDefault, 0);
+ if (modemName && bsdPath) {
+ // If the port already exists in the list of ports, we want that one. We only create a new one as a last resort.
+ serialPort = [self portByPath:(NSString*)bsdPath];
+ if (serialPort == nil) {
+ serialPort = [[[AMSerialPort alloc] init:(NSString*)bsdPath withName:(NSString*)modemName type:(NSString*)serviceType] autorelease];
+ }
+ }
+ CFRelease(modemName);
+ CFRelease(bsdPath);
+ CFRelease(serviceType);
+
+ // We have sucked this service dry of information so release it now.
+ (void)IOObjectRelease(serialService);
+ }
+
+ return serialPort;
+}
+
+- (void)portsWereAdded:(io_iterator_t)iterator
+{
+ AMSerialPort *serialPort;
+ NSMutableArray *addedPorts = [NSMutableArray array];
+
+ while ((serialPort = [self getNextSerialPort:iterator]) != nil) {
+ [addedPorts addObject:serialPort];
+ [portList addObject:serialPort];
+ }
+
+ NSNotificationCenter* notifCenter = [NSNotificationCenter defaultCenter];
+ NSDictionary* userInfo = [NSDictionary dictionaryWithObject:addedPorts forKey:AMSerialPortListAddedPorts];
+ [notifCenter postNotificationName:AMSerialPortListDidAddPortsNotification object:self userInfo:userInfo];
+}
+
+- (void)portsWereRemoved:(io_iterator_t)iterator
+{
+ AMSerialPort *serialPort;
+ NSMutableArray *removedPorts = [NSMutableArray array];
+
+ while ((serialPort = [self getNextSerialPort:iterator]) != nil) {
+ // Since the port was removed, one should obviously not attempt to use it anymore -- so 'close' it.
+ // -close does nothing if the port was never opened.
+ [serialPort close];
+
+ [removedPorts addObject:serialPort];
+ [portList removeObject:serialPort];
+ }
+
+ NSNotificationCenter* notifCenter = [NSNotificationCenter defaultCenter];
+ NSDictionary* userInfo = [NSDictionary dictionaryWithObject:removedPorts forKey:AMSerialPortListRemovedPorts];
+ [notifCenter postNotificationName:AMSerialPortListDidRemovePortsNotification object:self userInfo:userInfo];
+}
+
+static void AMSerialPortWasAddedNotification(void *refcon, io_iterator_t iterator)
+{
+ (void)refcon;
+ [[AMSerialPortList sharedPortList] portsWereAdded:iterator];
+}
+
+static void AMSerialPortWasRemovedNotification(void *refcon, io_iterator_t iterator)
+{
+ (void)refcon;
+ [[AMSerialPortList sharedPortList] portsWereRemoved:iterator];
+}
+
+- (void)registerForSerialPortChangeNotifications
+{
+ IONotificationPortRef notificationPort = IONotificationPortCreate(kIOMasterPortDefault);
+ if (notificationPort) {
+ CFRunLoopSourceRef notificationSource = IONotificationPortGetRunLoopSource(notificationPort);
+ if (notificationSource) {
+ // Serial devices are instances of class IOSerialBSDClient
+ CFMutableDictionaryRef classesToMatch1 = IOServiceMatching(kIOSerialBSDServiceValue);
+ if (classesToMatch1) {
+ CFDictionarySetValue(classesToMatch1, CFSTR(kIOSerialBSDTypeKey), CFSTR(kIOSerialBSDAllTypes));
+
+ // Copy classesToMatch1 now, while it has a non-zero ref count.
+ CFMutableDictionaryRef classesToMatch2 = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, classesToMatch1);
+ // Add to the runloop
+ CFRunLoopAddSource([[NSRunLoop currentRunLoop] getCFRunLoop], notificationSource, kCFRunLoopCommonModes);
+
+ // Set up notification for ports being added.
+ io_iterator_t unused;
+ kern_return_t kernResult = IOServiceAddMatchingNotification(notificationPort, kIOPublishNotification, classesToMatch1, AMSerialPortWasAddedNotification, NULL, &unused); // consumes a reference to classesToMatch1
+ if (kernResult != KERN_SUCCESS) {
+#ifdef AMSerialDebug
+ NSLog(@"Error %d when setting up add notifications!", kernResult);
+#endif
+ } else {
+ while (IOIteratorNext(unused)) {} // arm the notification
+ }
+
+ if (classesToMatch2) {
+ // Set up notification for ports being removed.
+ kernResult = IOServiceAddMatchingNotification(notificationPort, kIOTerminatedNotification, classesToMatch2, AMSerialPortWasRemovedNotification, NULL, &unused); // consumes a reference to classesToMatch2
+ if (kernResult != KERN_SUCCESS) {
+#ifdef AMSerialDebug
+ NSLog(@"Error %d when setting up add notifications!", kernResult);
+#endif
+ } else {
+ while (IOIteratorNext(unused)) {} // arm the notification
+ }
+ }
+ } else {
+#ifdef AMSerialDebug
+ NSLog(@"IOServiceMatching returned a NULL dictionary.");
+#endif
+ }
+ }
+ // Note that IONotificationPortDestroy(notificationPort) is deliberately not called here because if it were our port change notifications would never fire. This minor leak is pretty irrelevent since this object is a singleton that lives for the life of the application anyway.
+ }
+}
+
+- (void)addAllSerialPortsToArray:(NSMutableArray *)array
+{
+ kern_return_t kernResult;
+ CFMutableDictionaryRef classesToMatch;
+ io_iterator_t serialPortIterator;
+ AMSerialPort* serialPort;
+
+ // Serial devices are instances of class IOSerialBSDClient
+ classesToMatch = IOServiceMatching(kIOSerialBSDServiceValue);
+ if (classesToMatch != NULL) {
+ CFDictionarySetValue(classesToMatch, CFSTR(kIOSerialBSDTypeKey), CFSTR(kIOSerialBSDAllTypes));
+
+ // This function decrements the refcount of the dictionary passed it
+ kernResult = IOServiceGetMatchingServices(kIOMasterPortDefault, classesToMatch, &serialPortIterator);
+ if (kernResult == KERN_SUCCESS) {
+ while ((serialPort = [self getNextSerialPort:serialPortIterator]) != nil) {
+ [array addObject:serialPort];
+ }
+ (void)IOObjectRelease(serialPortIterator);
+ } else {
+#ifdef AMSerialDebug
+ NSLog(@"IOServiceGetMatchingServices returned %d", kernResult);
+#endif
+ }
+ } else {
+#ifdef AMSerialDebug
+ NSLog(@"IOServiceMatching returned a NULL dictionary.");
+#endif
+ }
+}
+
+- (id)init
+{
+ if ((self = [super init])) {
+ portList = [[NSMutableArray array] retain];
+
+ [self addAllSerialPortsToArray:portList];
+ [self registerForSerialPortChangeNotifications];
+ }
+
+ return self;
+}
+
+- (NSUInteger)count
+{
+ return [portList count];
+}
+
+- (AMSerialPort *)objectAtIndex:(NSUInteger)idx
+{
+ return [portList objectAtIndex:idx];
+}
+
+- (AMSerialPort *)objectWithName:(NSString *)name
+{
+ AMSerialPort *result = nil;
+ NSEnumerator *enumerator = [portList objectEnumerator];
+ AMSerialPort *port;
+ while ((port = [enumerator nextObject]) != nil) {
+ if ([[port name] isEqualToString:name]) {
+ result = port;
+ break;
+ }
+ }
+ return result;
+}
+
+- (NSArray *)serialPorts
+{
+ return [[portList copy] autorelease];
+}
+
+- (NSArray *)serialPortsOfType:(NSString *)serialTypeKey
+{
+ NSMutableArray *result = [NSMutableArray array];
+ NSEnumerator *enumerator = [portList objectEnumerator];
+ AMSerialPort *port;
+ while ((port = [enumerator nextObject]) != nil) {
+ if ([[port type] isEqualToString:serialTypeKey]) {
+ [result addObject:port];
+ }
+ }
+ return result;
+}
+
+
+@end
19 AMSerialPortList_Deprecated.h
@@ -0,0 +1,19 @@
+//
+// AMSerialPortList_Deprecated.h
+// AMSerialTest
+//
+// Created by Andreas on 14.08.06.
+// Copyright 2006 Andreas Mayer. All rights reserved.
+//
+
+#import <Cocoa/Cocoa.h>
+#import "AMSerialPortList.h"
+
+
+@interface AMSerialPortList (Deprecated)
+
+- (NSArray *)getPortList;
+// replaced by -serialPorts;
+
+
+@end
20 AMSerialPortList_Deprecated.m
@@ -0,0 +1,20 @@
+//
+// AMSerialPortList_Deprecated.m
+// AMSerialTest
+//
+// Created by Andreas on 14.08.06.
+// Copyright 2006 Andreas Mayer. All rights reserved.
+//
+
+#import "AMSerialPortList_Deprecated.h"
+
+
+@implementation AMSerialPortList (Deprecated)
+
+- (NSArray *)getPortList
+{
+ return [self serialPorts];
+}
+
+
+@end
81 AMSerialPort_Deprecated.h
@@ -0,0 +1,81 @@
+//
+// AMSerialPort_Deprecated.h
+// AMSerialTest
+//
+// Created by Andreas on 26.07.06.
+// Copyright 2006-2009 Andreas Mayer. All rights reserved.
+//
+
+#import <Cocoa/Cocoa.h>
+#import "AMSerialPort.h"
+
+
+@interface AMSerialPort (Deprecated)
+
+- (id)init:(NSString *)path withName:(NSString *)name DEPRECATED_ATTRIBUTE;
+// replaced by -init:withName:type:
+
+- (NSDictionary *)getOptions DEPRECATED_ATTRIBUTE;
+// renamed to -options
+
+- (long)getSpeed DEPRECATED_ATTRIBUTE;
+// renamed to -speed
+
+- (int)getDataBits DEPRECATED_ATTRIBUTE;
+// renamed to -dataBits
+
+- (BOOL)testParity DEPRECATED_ATTRIBUTE; // NO for "no parity"
+- (BOOL)testParity DEPRECATED_ATTRIBUTE; // meaningful only if TestParity == YES
+- (void)setParityNone DEPRECATED_ATTRIBUTE;
+- (void)setParityEven DEPRECATED_ATTRIBUTE;
+- (void)setParityOdd DEPRECATED_ATTRIBUTE;
+// replaced by -parity and -setParity:
+
+- (int)getStopBits DEPRECATED_ATTRIBUTE;
+// renamed to -stopBits;
+- (void)setStopBits2:(BOOL)two DEPRECATED_ATTRIBUTE; // YES for 2 stop bits, NO for 1 stop bit
+// replaced by -setStopBits:
+
+- (BOOL)testEchoEnabled DEPRECATED_ATTRIBUTE;
+// renamed to -echoEnabled
+
+- (BOOL)testRTSInputFlowControl DEPRECATED_ATTRIBUTE;
+// renamed to -RTSInputFlowControl
+
+- (BOOL)testDTRInputFlowControl DEPRECATED_ATTRIBUTE;
+// renamed to -DTRInputFlowControl
+
+- (BOOL)testCTSOutputFlowControl DEPRECATED_ATTRIBUTE;
+// renamed to -CTSOutputFlowControl
+
+- (BOOL)testDSROutputFlowControl DEPRECATED_ATTRIBUTE;
+// renamed to -DSROutputFlowControl
+
+- (BOOL)testCAROutputFlowControl DEPRECATED_ATTRIBUTE;
+// renamed to -CAROutputFlowControl
+
+- (BOOL)testHangupOnClose DEPRECATED_ATTRIBUTE;
+// renamed to -hangupOnClose
+
+- (BOOL)getLocal DEPRECATED_ATTRIBUTE;
+// renamed to -localMode
+
+- (void)setLocal:(BOOL)local DEPRECATED_ATTRIBUTE;
+// renamed to -setLocalMode:
+
+- (int)checkRead DEPRECATED_ATTRIBUTE;
+// renamed to -bytesAvailable
+
+- (NSString *)readString DEPRECATED_ATTRIBUTE;
+// replaced by -readStringUsingEncoding:error:
+
+- (NSString *)readStringOfLength:(unsigned int)length DEPRECATED_ATTRIBUTE;
+// replaced by -readBytes:usingEncoding:error:
+
+- (int)writeString:(NSString *)string DEPRECATED_ATTRIBUTE;
+// replaced by -writeString:usingEncoding:error:
+
+- (BOOL)flushInput:(BOOL)fIn Output:(BOOL)fOut DEPRECATED_ATTRIBUTE;
+// renamed to -flushInput:output:
+
+@end
182 AMSerialPort_Deprecated.m
@@ -0,0 +1,182 @@
+//
+// AMSerialPort_Deprecated.m
+// AMSerialTest
+//
+// Created by Andreas on 26.07.06.
+// Copyright 2006-2009 Andreas Mayer. All rights reserved.
+//
+
+#import "AMSerialPort_Deprecated.h"
+#import "AMSerialPortAdditions.h"
+
+
+@implementation AMSerialPort (Deprecated)
+
+- (id)init:(NSString *)path withName:(NSString *)name
+{
+ return [self init:path withName:name type:@""];
+}
+
+// renamed to -options
+- (NSDictionary *)getOptions
+{
+ return [self options];
+}
+
+// renamed to -speed
+- (long)getSpeed
+{
+ return [self speed];
+}
+
+// renamed to -dataBits
+- (int)getDataBits
+{
+ return [self dataBits];
+}
+
+// replaced by -parity
+- (BOOL)testParity
+{
+ // NO for "no parity"
+ return (options->c_cflag & PARENB);
+}
+
+// replaced by -parity
+- (BOOL)testParityOdd
+{
+ // meaningful only if TestParity == YES
+ return (options->c_cflag & PARODD);
+}
+
+// replaced by -setParity:
+- (void)setParityNone
+{
+ options->c_cflag &= ~PARENB;
+}
+
+// replaced by -setParity:
+- (void)setParityEven
+{
+ options->c_cflag |= PARENB;
+ options->c_cflag &= ~PARODD;
+}
+
+// replaced by -setParity:
+- (void)setParityOdd
+{
+ options->c_cflag |= PARENB;
+ options->c_cflag |= PARODD;
+}
+
+// renamed to -stopBits;
+- (int)getStopBits
+{
+ return [self stopBits];
+}
+
+// replaced by -setStopBits:
+- (void)setStopBits2:(BOOL)two
+{
+ // YES for 2 stop bits, NO for 1 stop bit
+ if (two) {
+ [self setStopBits:kAMSerialStopBitsTwo];
+ } else {
+ [self setStopBits:kAMSerialStopBitsOne];
+ }
+}
+
+// renamed to -echoEnabled
+- (BOOL)testEchoEnabled
+{
+ return [self echoEnabled];
+}
+
+// renamed to -RTSInputFlowControl
+- (BOOL)testRTSInputFlowControl
+{
+ return [self RTSInputFlowControl];
+}
+
+// renamed to -DTRInputFlowControl
+- (BOOL)testDTRInputFlowControl
+{
+ return [self DTRInputFlowControl];
+}
+
+// renamed to -CTSOutputFlowControl
+- (BOOL)testCTSOutputFlowControl
+{
+ return [self CTSOutputFlowControl];
+}
+
+// renamed to -DSROutputFlowControl
+- (BOOL)testDSROutputFlowControl
+{
+ return [self DSROutputFlowControl];
+}
+
+// renamed to -CAROutputFlowControl
+- (BOOL)testCAROutputFlowControl
+{
+ return [self CAROutputFlowControl];
+}
+
+// renamed to -hangupOnClose
+- (BOOL)testHangupOnClose
+{
+ return [self hangupOnClose];
+}
+
+// renamed to -localMode
+- (BOOL)getLocal
+{
+ return [self localMode];
+}
+
+// renamed to -setLocalMode:
+- (void)setLocal:(BOOL)local
+{
+ return [self setLocalMode:local];
+}
+
+// renamed to -bytesAvailable
+- (int)checkRead
+{
+ return [self bytesAvailable];
+}
+
+// replaced by -readStringUsingEncoding:error:
+- (NSString *)readString
+{
+ return [self readStringUsingEncoding:NSMacOSRomanStringEncoding error:NULL];
+}
+
+// replaced by -readBytes:usingEncoding:error:
+- (NSString *)readStringOfLength:(unsigned int)length
+{
+ return [self readBytes:length usingEncoding:NSMacOSRomanStringEncoding error:NULL];
+}
+
+// replaced by -writeString:usingEncoding:error:
+- (int)writeString:(NSString *)string
+{
+ int result = 0;
+ NSError *error;
+ [self writeString:string usingEncoding:NSMacOSRomanStringEncoding error:&error];
+ if (error) {
+ NSDictionary *userInfo = [error userInfo];
+ if (userInfo) {
+ result = [[userInfo objectForKey:@"bytesWritten"] intValue];
+ }
+ }
+ return result;
+}
+
+// renamed to -flushInput:output:
+- (BOOL)flushInput:(BOOL)fIn Output:(BOOL)fOut
+{
+ return [self flushInput:fIn output:fOut];
+}
+
+@end
32 AMStandardEnumerator.h
@@ -0,0 +1,32 @@
+//
+// AMStandardEnumerator.h
+//
+// Created by Andreas on Mon Aug 04 2003.
+// Copyright (c) 2003 Andreas Mayer. All rights reserved.
+//
+// 2007-10-26 Sean McBride
+// - made code 64 bit and garbage collection clean
+
+#import "AMSDKCompatibility.h"
+
+#import <Foundation/Foundation.h>
+
+typedef NSUInteger (*CountMethod)(id, SEL);
+typedef id (*NextObjectMethod)(id, SEL, NSUInteger);
+
+@interface AMStandardEnumerator : NSEnumerator
+{
+@private
+ id collection;
+ SEL countSelector;
+ SEL nextObjectSelector;
+ CountMethod count;
+ NextObjectMethod nextObject;
+ NSUInteger position;
+}
+
+// Designated initializer
+- (id)initWithCollection:(id)theCollection countSelector:(SEL)theCountSelector objectAtIndexSelector:(SEL)theObjectSelector;
+
+
+@end
59 AMStandardEnumerator.m
@@ -0,0 +1,59 @@
+//
+// AMStandardEnumerator.m
+//
+// Created by Andreas on Mon Aug 04 2003.
+// Copyright (c) 2003-2009 Andreas Mayer. All rights reserved.
+//
+// 2007-10-26 Sean McBride
+// - made code 64 bit and garbage collection clean
+
+#import "AMSDKCompatibility.h"
+
+#import "AMStandardEnumerator.h"
+
+
+@implementation AMStandardEnumerator
+
+// Designated initializer
+- (id)initWithCollection:(id)theCollection countSelector:(SEL)theCountSelector objectAtIndexSelector:(SEL)theObjectSelector
+{
+ self = [super init];
+ if (self) {
+ collection = [theCollection retain];
+ countSelector = theCountSelector;
+ count = (CountMethod)[collection methodForSelector:countSelector];
+ nextObjectSelector = theObjectSelector;
+ nextObject = (NextObjectMethod)[collection methodForSelector:nextObjectSelector];
+ position = 0;
+ }
+ return self;
+}
+
+#ifndef __OBJC_GC__
+
+- (void)dealloc
+{
+ [collection release]; collection = nil;
+ [super dealloc];
+}
+
+#endif
+
+- (id)nextObject
+{
+ if (position >= count(collection, countSelector))
+ return nil;
+
+ return (nextObject(collection, nextObjectSelector, position++));
+}
+
+- (NSArray *)allObjects
+{<