1 2#include <fcntl.h> 3#include <stdio.h> 4#include <string.h> 5#include <termios.h> 6#include <unistd.h> 7 8#include <sys/ioctl.h> 9 10#include <IOKit/serial/ioss.h> 11 12#include "IOSerialTestLib.h" 13 14int _testIOSSIOSPEEDIoctl(int fd, speed_t speed); 15int _modifyAttributes(int fd, struct termios *originalOptions); 16int _modifyModemLines(int fd); 17 18#pragma mark - 19 20int _testIOSSIOSPEEDIoctl(int fd, speed_t speed) 21{ 22 struct termios options; 23 24 // The IOSSIOSPEED ioctl can be used to set arbitrary baud rates other than 25 // those specified by POSIX. The driver for the underlying serial hardware 26 // ultimately determines which baud rates can be used. This ioctl sets both 27 // the input and output speed. 28 29 if (ioctl(fd, IOSSIOSPEED, &speed) == -1) { 30 printf("[WARN] ioctl(..., IOSSIOSPEED, %lu).\n", speed); 31 goto fail; 32 } 33 34 // Check that speed is properly modified 35 if (tcgetattr(fd, &options) == -1) { 36 printf("[WARN] _modifyAttributes: tcgetattr failed\n"); 37 goto fail; 38 } 39 40 if (cfgetispeed(&options) != speed || 41 cfgetospeed(&options) != speed) { 42 printf("[WARN] _modifyAttributes: cfsetspeed failed, %lu, %lu.\n", 43 speed, 44 cfgetispeed(&options)); 45 goto fail; 46 } 47 48 return 0; 49fail: 50 return -1; 51 52} 53 54int _modifyAttributes(int fd, struct termios *originalOptions) 55{ 56 int result = 0; 57 unsigned long mics = 1UL; 58 struct termios options; 59 60 if (!originalOptions) { 61 printf("[FAIL] _modifyAttributes: NULL argument unexpected\n"); 62 goto fail; 63 } 64 65 // prevent additional opens on the device, except from a root-owned process 66 if (ioctl(fd, TIOCEXCL) == -1) { 67 printf("[FAIL] _modifyAttributes: ioctl TIOCEXCL failed\n"); 68 goto fail; 69 } 70 71 // clear the O_NONBLOCK flag so subsequent I/O will block 72 if (fcntl(fd, F_SETFL, 0) == -1) { 73 printf("[FAIL] _modifyAttributes: fcntl failed\n"); 74 goto fail; 75 } 76 77 // snapshot the current terminal state in originalOptions 78 if (tcgetattr(fd, originalOptions) == -1) { 79 printf("[FAIL] _modifyAttributes: tcgetattr failed\n"); 80 goto fail; 81 } 82 83 options = *originalOptions; 84 85 // Set raw input (non-canonical) mode 86 cfmakeraw(&options); 87 88 options.c_cc[VMIN] = 0; 89 options.c_cc[VTIME] = 10; 90 91 // Set 19200 baud 92 if (cfsetspeed(&options, B19200) == -1) { 93 printf("[FAIL] _modifyAttributes: cfsetspeed failed\n"); 94 goto fail; 95 } 96 97 options.c_cflag |= (CS7 | // Use 7 bit words 98 PARENB | // Parity enable (even parity if PARODD not also set) 99 CCTS_OFLOW | // CTS flow control of output 100 CRTS_IFLOW); // RTS flow control of input 101 102 // Cause the new options to take effect immediately. 103 if (tcsetattr(fd, TCSANOW, &options) == -1) { 104 printf("[FAIL] _modifyAttributes: tcsetattr failed\n"); 105 goto fail; 106 } 107 108 // check that the tcsetattr worked properly 109 if (tcgetattr(fd, &options) == -1) { 110 printf("[FAIL] _modifyAttributes: tcgetattr failed\n"); 111 goto fail; 112 } 113 if ((options.c_cflag & (CS7 | PARENB | CCTS_OFLOW | CRTS_IFLOW)) != 114 (CS7 | PARENB | CCTS_OFLOW | CRTS_IFLOW)) { 115 printf("[FAIL] _modifyAttributes: tcsetattr/tcgetattr failed\n"); 116 goto fail; 117 } 118 119 // Check that speed is 19200 baud 120 if (cfgetispeed(&options) != B19200 || 121 cfgetospeed(&options) != B19200) { 122 printf("[FAIL] _modifyAttributes: cfsetspeed failed\n"); 123 goto fail; 124 } 125 126 // Set the receive latency in microseconds 127 if (ioctl(fd, IOSSDATALAT, &mics) == -1) { 128 // set latency to 1 microsecond 129 printf("[FAIL] _modifyAttributes: ioctl IOSSDATALAT failed\n"); 130 goto fail; 131 } 132 133 return result; 134 135fail: 136 return -1; 137} 138 139int _modifyModemLines(int fd) 140{ 141 int handshake; 142 143 // Assert Data Terminal Ready (DTR) 144 if (ioctl(fd, TIOCSDTR) == -1) { 145 printf("[FAIL] _modifyModemLines: ioctl TIOCSDTR failed\n"); 146 goto fail; 147 } 148 // Clear Data Terminal Ready (DTR) 149 if (ioctl(fd, TIOCCDTR) == -1) { 150 printf("[FAIL] _modifyModemLines: ioctl TIOCCDTR failed\n"); 151 goto fail; 152 } 153 154 // Set the modem lines depending on the bits set in handshake 155 handshake = TIOCM_DTR | TIOCM_RTS | TIOCM_CTS | TIOCM_DSR; 156 if (ioctl(fd, TIOCMSET, &handshake) == -1) { 157 printf("[FAIL] _modifyModemLines: ioctl TIOCMSET failed\n"); 158 goto fail; 159 } 160 161 // Store the state of the modem lines in handshake 162 if (ioctl(fd, TIOCMGET, &handshake) == -1) { 163 printf("[FAIL] _modifyModemLines: ioctl TIOCMGET failed\n"); 164 goto fail; 165 } 166 167 return 0; 168 169fail: 170 return -1; 171} 172 173#pragma mark - 174 175// open and close the serial connection 176int testOpenClose(const char *path) 177{ 178 int fd = -1; 179 int result = 0; 180 181 fd = open(path, O_RDWR|O_NONBLOCK); 182 if (fd == -1) { 183 return -1; 184 } 185 186 result = close(fd); 187 if (result) { 188 return -1; 189 } 190 191 return 0; 192} 193 194// uses ioctl() and fcntl() to modify config of the tty session 195int testModifyConfig(const char *path) 196{ 197 int fd = -1; 198 int result = 0; 199 speed_t speed; 200 201 struct termios originalTTYAttrs; 202 203 fd = open(path, O_RDWR|O_NOCTTY|O_NONBLOCK); 204 if (fd == -1) { 205 printf("[FAIL] testModifyConfig: open failed\n"); 206 goto fail; 207 } 208 209 if (_modifyAttributes(fd, &originalTTYAttrs)) { 210 printf("[FAIL] testModifyConfig: config failed\n"); 211 goto fail; 212 } 213 214 if (_modifyModemLines(fd)) { 215 printf("[FAIL] testModifyConfig: config failed\n"); 216 goto fail; 217 } 218 219 // testing for some arbitrary non-standard values 220 speed = 40000; 221 if (_testIOSSIOSPEEDIoctl(fd, speed)) { 222 printf("[WARN] testModifyConfig: IOSSIOSPEED ioctl with %lu failed\n", speed); 223 } 224 speed = 58000; 225 if (_testIOSSIOSPEEDIoctl(fd, speed)) { 226 printf("[WARN] testModifyConfig: IOSSIOSPEED ioctl with %lu failed\n", speed); 227 } 228 speed = 250000; 229 if (_testIOSSIOSPEEDIoctl(fd, speed)) { 230 printf("[WARN] testModifyConfig: IOSSIOSPEED ioctl with %lu failed\n", speed); 231 } 232 speed = 10400; 233 if (_testIOSSIOSPEEDIoctl(fd, speed)) { 234 printf("[WARN] testModifyConfig: IOSSIOSPEED ioctl with %lu failed\n", speed); 235 } 236 speed = 8192; 237 if (_testIOSSIOSPEEDIoctl(fd, speed)) { 238 printf("[WARN] testModifyConfig: IOSSIOSPEED ioctl with %lu failed\n", speed); 239 } 240 speed = 128000; 241 if (_testIOSSIOSPEEDIoctl(fd, speed)) { 242 printf("[WARN] testModifyConfig: IOSSIOSPEED ioctl with %lu failed\n", speed); 243 } 244 245 // 31250: standard rate used for MIDI signaling, but isn't included in POSIX 246 speed = 31250; 247 if (_testIOSSIOSPEEDIoctl(fd, speed)) { 248 printf("[WARN] testModifyConfig: IOSSIOSPEED ioctl with %lu failed\n", speed); 249 } 250 251 // some standard values 252 speed = 38400; 253 if (_testIOSSIOSPEEDIoctl(fd, speed)) { 254 printf("[FAIL] testModifyConfig: IOSSIOSPEED ioctl with %lu failed\n", speed); 255 goto fail; 256 } 257 speed = 115200; 258 if (_testIOSSIOSPEEDIoctl(fd, speed)) { 259 printf("[FAIL] testModifyConfig: IOSSIOSPEED ioctl with %lu failed\n", speed); 260 goto fail; 261 } 262 speed = 19200; 263 if (_testIOSSIOSPEEDIoctl(fd, speed)) { 264 printf("[FAIL] testModifyConfig: IOSSIOSPEED ioctl with %lu failed\n", speed); 265 goto fail; 266 } 267 268 // resets tty attributes 269 tcsetattr(fd, TCSANOW, &originalTTYAttrs); 270 271 result = close(fd); 272 if (result == -1) { 273 printf("[FAIL] testModifyConfig: close failed\n"); 274 goto fail; 275 } 276 277 return 0; 278 279fail: 280 if (fd != -1) close(fd); 281 return -1; 282} 283 284#define kHelloWorldString "Hello World" 285 286int testReadWrite(const char *readPath, const char *writePath, const char *message) 287{ 288 int readFd = -1; 289 int writeFd = -1; 290 int result = 0; 291 ssize_t numBytes; 292 293 char myMessage[256]; 294 char buffer[256]; // Input buffer 295 char *bufPtr; // Current char in buffer 296 297 struct termios originalReadTTYAttrs; 298 struct termios originalWriteTTYAttrs; 299 300 if (!message) { 301 // no message argument, using default value 302 if (strlcpy(myMessage, kHelloWorldString, sizeof(myMessage)) >= 303 sizeof(myMessage)) { 304 // message argument is too long, got truncated 305 myMessage[sizeof(myMessage)-1] = '\0'; 306 } 307 } 308 else { 309 if (strlcpy(myMessage, message, sizeof(myMessage)) >= sizeof(myMessage)) { 310 // message argument is too long, got truncated 311 myMessage[sizeof(myMessage)-1] = '\0'; 312 } 313 } 314 315 readFd = open(readPath, O_RDWR|O_NOCTTY|O_NONBLOCK); 316 if (readFd == -1) { 317 printf("[FAIL] testReadWrite: open failed\n"); 318 goto fail; 319 } 320 if (_modifyAttributes(readFd, &originalReadTTYAttrs)) { 321 printf("[FAIL] testReadWrite: _modifyAttributes failed\n"); 322 goto fail; 323 } 324 325 writeFd = open(writePath, O_RDWR|O_NOCTTY|O_NONBLOCK); 326 if (writeFd == -1) { 327 printf("[FAIL] testReadWrite: open failed\n"); 328 goto fail; 329 } 330 if (_modifyAttributes(writeFd, &originalWriteTTYAttrs)) { 331 printf("[FAIL] testReadWrite: _modifyAttributes failed\n"); 332 goto fail; 333 } 334 335 if (_modifyModemLines(readFd) || 336 _modifyModemLines(writeFd)) { 337 printf("[FAIL] testReadWrite: _modifyModemLines failed\n"); 338 goto fail; 339 } 340 341 numBytes = write(writeFd, myMessage, strnlen(myMessage, 256)); 342 343 if (numBytes == -1) { 344 printf("[FAIL] write returned -1\n"); 345 goto fail; 346 } 347 if ((size_t)numBytes < strnlen(myMessage, 256)) { 348 printf("[FAIL] write did not complete\n"); 349 goto fail; 350 } 351 352 memset(buffer, 0, sizeof(buffer)); 353 354 bufPtr = buffer; 355 do { 356 numBytes = read(readFd, bufPtr, &buffer[sizeof(buffer)] - bufPtr - 1); 357 if (numBytes > 0) { 358 bufPtr += numBytes; 359 if (*(bufPtr - 1) == '\n' || *(bufPtr - 1) == '\r') { 360 break; 361 } 362 } 363 } while (numBytes > 0); 364 365 if (strncmp(buffer, myMessage, sizeof(myMessage))) { 366 printf("[FAIL] testReadWrite: read string was: \"%s\", " 367 "expected: \"%s\"\n", buffer, myMessage); 368 goto fail; 369 } 370 371 tcsetattr(readFd, TCSANOW, &originalReadTTYAttrs); 372 tcsetattr(writeFd, TCSANOW, &originalWriteTTYAttrs); 373 374 result = close(readFd); 375 readFd = -1; 376 if (result == -1) { 377 printf("[FAIL] testReadWrite: close failed\n"); 378 goto fail; 379 } 380 result = close(writeFd); 381 writeFd = -1; 382 if (result == -1) { 383 printf("[FAIL] testReadWrite: close failed\n"); 384 goto fail; 385 } 386 387 return 0; 388 389fail: 390 if (readFd != -1) close(readFd); 391 if (writeFd != -1) close(writeFd); 392 return -1; 393} 394 395