title: Reading Serial Port iOS
body: <p>I have the following code to read and write to serial ports on iOS 10.3.3 Jailbroken iPhone 6S (I used h3lix to jailbreak):</p>
  
  <p>Serial.h:</p>
  
  <pre><code>//
  //  Serial.h
  //  iOUSB
  //
  //  Created by Brandon on 2018-05-21.
  //  Copyright © 2018 XIO. All rights reserved.
  //
  
  #if !defined(__cplusplus)  //-fmodules -fcxx-modules
  @import Foundation;
  #else
  #pragma clang diagnostic push
  #pragma clang diagnostic ignored "-Wauto-import"
  #import &lt;Foundation/Foundation.h&gt;
  #pragma clang diagnostic pop
  #endif
  
  #define BAUD_RATE 9600
  
  @interface Serial : NSObject
  + (instancetype)shared;
  - (bool)setup:(NSString *)filePath;
  - (bool)disconnect;
  - (size_t)available;
  - (size_t)read:(uint8_t *)bytes length:(int32_t)length;
  - (size_t)write:(uint8_t *)bytes length:(int32_t)length;
  - (void)flushInputStream;
  - (void)flushOutputStream;
  - (void)flush;
  - (NSString *)lastError;
  - (void)eraseLastError;
  @end
  </code></pre>
  
  <p>Serial.mm:</p>
  
  <pre><code>//
  //  Serial.mm
  //  iOUSB
  //
  //  Created by Brandon on 2018-05-21.
  //  Copyright © 2018 XIO. All rights reserved.
  //
  
  #import "Serial.h"
  #include &lt;fstream&gt;
  #include &lt;iostream&gt;
  #include &lt;sys/fcntl.h&gt;
  #include &lt;sys/stat.h&gt;
  #include &lt;sys/ioctl.h&gt;
  #include &lt;termios.h&gt;
  #include &lt;unistd.h&gt;
  
  #define DEFAULT_BAUD_RATE 9600
  
  @interface Serial()
  @property (nonatomic, assign) int file_descriptor;
  @property (nonatomic, strong) NSString *lastError;
  @end
  
  @implementation Serial
  + (instancetype)shared {
      static Serial *instance;
      static dispatch_once_t onceToken;
      dispatch_once(&amp;onceToken, ^{
          instance = [[Serial alloc] init];
      });
      return instance;
  }
  
  - (instancetype)init {
      if ((self = [super init])) {
          self.file_descriptor = -1;
          self.lastError = nil;
      }
      return self;
  }
  
  - (void)dealloc {
      [self disconnect];
  }
  
  - (bool)setup:(NSString *)filePath {
      if (self.file_descriptor != -1)
      {
          return true;
      }
  
      self.file_descriptor = open(filePath.UTF8String, O_RDWR | O_NOCTTY | O_NDELAY | O_NONBLOCK);
      if (self.file_descriptor == -1)
      {
          const char* error = strerror(errno);
          self.lastError = [[NSString alloc] initWithUTF8String:error];
          perror(error);
          return false;
      }
  
      struct termios options;
      struct termios oldoptions;
      tcgetattr(self.file_descriptor, &amp;oldoptions);
      options = oldoptions;
  
      #if !defined(_POSIX_C_SOURCE) || defined(_DARWIN_C_SOURCE)
      int baud_rates[] = {B0, B50, B75, B110, B134, B150, B200, B300, B1200, B1800, B2400, B4800, B9600, B19200, B38400, B7200, B14400, B28800, B57600, B76800, B115200, B230400
      };
      #else
      int baud_rates[] = {B0, B50, B75, B110, B134, B150, B200, B300, B1200, B1800, B2400, B4800, B9600, B19200, B38400};
      #endif
  
      auto it = std::find(std::begin(baud_rates), std::end(baud_rates), BAUD_RATE);
      if (it != std::end(baud_rates))
      {
          cfsetispeed(&amp;options, *it);
          cfsetospeed(&amp;options, *it);
          std::cout&lt;&lt;"BAUD_RATE Set Successfully!\n";
      }
      else
      {
          cfsetispeed(&amp;options, DEFAULT_BAUD_RATE);
          cfsetospeed(&amp;options, DEFAULT_BAUD_RATE);
          std::cerr&lt;&lt;"Invalid BAUD_RATE.. Setting to default: "&lt;&lt;DEFAULT_BAUD_RATE&lt;&lt;"\n";
      }
  
      options.c_cflag |= (CLOCAL | CREAD);
      options.c_cflag |= CS8;
      options.c_cflag &amp;= ~PARENB;
      options.c_cflag &amp;= ~CSTOPB;
      options.c_cflag &amp;= ~CSIZE;
      tcsetattr(self.file_descriptor, TCSANOW, &amp;options);
      return true;
  }
  
  - (bool)disconnect {
      if (self.file_descriptor != -1)
      {
          close(self.file_descriptor);
          self.file_descriptor = -1;
          self.lastError = nil;
      }
  
      return self.file_descriptor == -1;
  }
  
  - (size_t)available {
      if (self.file_descriptor == -1)
      {
          return -1;
      }
  
      int available = -1;
      ioctl(self.file_descriptor, FIONREAD, &amp;available);
      return available;
  }
  
  - (size_t)read:(uint8_t *)bytes length:(int32_t)length {
      if (self.file_descriptor == -1)
      {
          return -1;
      }
  
      ssize_t bytesRead = read(self.file_descriptor, bytes, length);
      if (bytesRead &lt; 0)
      {
          const char* error = strerror(errno);
          self.lastError = [[NSString alloc] initWithUTF8String:error];
          perror(error);
      }
      return bytesRead;
  }
  
  - (size_t)write:(uint8_t *)bytes length:(int32_t)length {
      if (self.file_descriptor == -1)
      {
          return -1;
      }
  
      ssize_t bytesWritten = write(self.file_descriptor, bytes, length);
      if (bytesWritten &lt;= 0)
      {
          const char* error = strerror(errno);
          self.lastError = [[NSString alloc] initWithUTF8String:error];
          perror(error);
      }
      return bytesWritten;
  }
  
  - (void)flushInputStream {
      if (self.file_descriptor == -1)
      {
          return;
      }
  
      tcflush(self.file_descriptor, TCIFLUSH);
  }
  
  - (void)flushOutputStream {
      if (self.file_descriptor == -1)
      {
          return;
      }
  
      tcflush(self.file_descriptor, TCOFLUSH);
  }
  
  - (void)flush {
      if (self.file_descriptor == -1)
      {
          return;
      }
  
      tcflush(self.file_descriptor, TCIOFLUSH);
  }
  
  - (NSString *)lastError {
      return _lastError ?: @"";
  }
  
  - (void)eraseLastError {
      _lastError = nil;
  }
  @end
  </code></pre>
  
  <p>I purchased the OTG adapter: "Lightning to USB 3 Camera Adapter" shown here: <a href="https://www.apple.com/ca/shop/product/MK0W2AM/A/lightning-to-usb-3-camera-adapter" rel="noreferrer">https://www.apple.com/ca/shop/product/MK0W2AM/A/lightning-to-usb-3-camera-adapter</a></p>
  
  <p>I have plugged in the device and it shows up as unsupported device which is fine, since it's not MFI-Certified.</p>
  
  <p>However, when I try to open <code>/dev/tty.iap</code>, I keep getting <code>Error: Resource Busy</code>.</p>
  
  <p>I am able to open <code>/dev/tty.wlan</code> for example.. Only some ports give me trouble like the <code>iap</code> one.</p>
  
  <p>What am I doing wrong that I cannot read <code>tty.iap</code>? I have tried to run the application as root by moving it to <code>/Applications</code> from <code>/var/containers/Bundle/Applications</code>.. I've tried to <code>chown root:wheel</code> the app and <code>chmod 0777</code> the app. Still I get <code>resource busy</code>..</p>
  
  <p>I've read online that you get this error when you are NOT root.</p>
  
  <p><strong>How can I run my app as root?</strong> 
  <strong>How can I fix this so I can read the lightning port?</strong></p>
  
  <p>I tried <a href="https://stackoverflow.com/a/15970080/1462718">https://stackoverflow.com/a/15970080/1462718</a> but it doesn't seem to work. The app would launch and then immediately close no matter how many times I try.</p>