11

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):

Serial.h:

//
//  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 <Foundation/Foundation.h>
#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

Serial.mm:

//
//  Serial.mm
//  iOUSB
//
//  Created by Brandon on 2018-05-21.
//  Copyright © 2018 XIO. All rights reserved.
//

#import "Serial.h"
#include <fstream>
#include <iostream>
#include <sys/fcntl.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <termios.h>
#include <unistd.h>

#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(&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, &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(&options, *it);
        cfsetospeed(&options, *it);
        std::cout<<"BAUD_RATE Set Successfully!\n";
    }
    else
    {
        cfsetispeed(&options, DEFAULT_BAUD_RATE);
        cfsetospeed(&options, DEFAULT_BAUD_RATE);
        std::cerr<<"Invalid BAUD_RATE.. Setting to default: "<<DEFAULT_BAUD_RATE<<"\n";
    }

    options.c_cflag |= (CLOCAL | CREAD);
    options.c_cflag |= CS8;
    options.c_cflag &= ~PARENB;
    options.c_cflag &= ~CSTOPB;
    options.c_cflag &= ~CSIZE;
    tcsetattr(self.file_descriptor, TCSANOW, &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, &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 < 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 <= 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

I purchased the OTG adapter: "Lightning to USB 3 Camera Adapter" shown here: https://www.apple.com/ca/shop/product/MK0W2AM/A/lightning-to-usb-3-camera-adapter

I have plugged in the device and it shows up as unsupported device which is fine, since it's not MFI-Certified.

However, when I try to open /dev/tty.iap, I keep getting Error: Resource Busy.

I am able to open /dev/tty.wlan for example.. Only some ports give me trouble like the iap one.

What am I doing wrong that I cannot read tty.iap? I have tried to run the application as root by moving it to /Applications from /var/containers/Bundle/Applications.. I've tried to chown root:wheel the app and chmod 0777 the app. Still I get resource busy..

I've read online that you get this error when you are NOT root.

How can I run my app as root? How can I fix this so I can read the lightning port?

I tried https://stackoverflow.com/a/15970080/1462718 but it doesn't seem to work. The app would launch and then immediately close no matter how many times I try.

Brandon
  • 22,723
  • 11
  • 93
  • 186
  • Did you plug in a USB to RS232 adapter? (You say you plugged in the "device," but I don't know if you're referring to the Lightning/USB-3 adapter or the USB/RS232 adapter.) – Bryan Scott May 30 '18 at 15:03

1 Answers1

4

By default the serial port is in use as an out-of-band management interface (allowing you to connect to the system via a serial connection if the network is down).

You will need to disable this before you can reuse the serial port for any other purpose.

The specifics of how to do this vary by OS version.

Tiago Martins Peres
  • 14,289
  • 18
  • 86
  • 145
  • for your case, serial port-based terminal support is controlled via StartupItem (https://developer.apple.com/library/content/documentation/MacOSX/Conceptual/BPSystemStartup/Chapters/StartupItems.html). Knowing this, you should find the file /System/Library/StartupItems/SerialTerminalSupport. Look for ENABLE SERIALTERMINAL and change it to $FALSE – Tiago Martins Peres Jun 02 '18 at 06:26