Работа с последовательным устройством

Эта глава обеспечивает пример кода, демонстрирующий, как использовать механизм файла устройств, чтобы найти и связаться с последовательным устройством, явно утверждающим, что было модемом, такой как встроенное или USB-модем. Поскольку нет никакого безопасного, программируемого способа определить, является ли устройство, присоединенное к последовательному порту, действительно модемом, пример кода в этой главе не находит модемы с другой стороны последовательного порта.

Фрагменты кода в этой главе от примера приложения SerialPortSample, доступный полностью в http://developer.apple.com/samplecode/SerialPortSample.

Несмотря на то, что пример кода в этой главе был скомпилирован и протестирован до некоторой степени, Apple не рекомендует непосредственно включить этот код в коммерческое применение. Например, только ограниченная обработка ошибок показана — необходимо разработать собственные методы для обнаружения и ошибок из-за неправильного обращения.

Доступ последовательного устройства в основанном на Intel Macintosh

Этот раздел кратко обрисовывает в общих чертах некоторые проблемы, связанные с разработкой универсальной версии двоичных файлов приложения Mac, использующего файлы устройств для доступа к последовательному устройству. Перед чтением этого раздела, убедиться считать Универсальные Двоичные Инструкции по Программированию, Второй Выпуск. Тот документ касается архитектурных различий, и порядок байтов форматирует и обеспечивает всесторонние инструкции для модификации кода и создания универсальных двоичных файлов. Инструкции в том документе применяются ко всем типам приложений, включая тех который аппаратные средства доступа.

Прежде чем Вы создадите свое приложение как универсальный двоичный файл, удостоверьтесь что:

Если Ваше приложение использует POSIX API стандартным способом, Вы не должны испытывать никакие затруднения при разработке универсальной версии двоичных файлов приложения. Как с любым приложением доступа к устройствам, однако при чтении многобайтовых целочисленных данных (вместо или в дополнение к символьным потокам) необходимо знать о разностях потенциалов в формате порядка байтов.

Если Вы решаете, что свопинг байта необходим, помните следующие инструкции:

Доступ к последовательному устройству

Для передачи с последовательным устройством из приложения Mac используйте функции Набора I/O для получения пути к файлу устройств для того устройства. Затем реализуйте традиционный доступ последовательного порта UNIX с помощью POSIX termios API. Ваше приложение может считать и записать данные с помощью файла устройств.

В частности пример кода в этой главе демонстрирует как к:

Пример кода, показанный в этой главе, является от XCode “проектом” Инструмента CoreFoundation. Проект создает инструмент, не имеющий никакого пользовательского интерфейса и отправляющий его вывод в консоль. Можно просмотреть вывод или путем выполнения инструмента в XCode или путем выполнения утилиты Console, в которой можно найти /Applications/Utilities/Console. Можно, конечно, записать подобный код без этих ограничений. Для подробной документации относительно использования XCode посмотрите http://developer .apple.com/referencelibrary/DeveloperTools/index.html.

Много функций, типов данных и констант, используемых в примере кода в этой главе, определяются в заголовочных файлах в Kernel.framework, System.framework, или в каталоге /usr/include (в чьем содержании можно исследовать использование Терминального приложения, расположенного /Applications/Utilities/Terminal). Определенные заголовочные файлы отмечены, где это необходимо. Некоторые функции и типы данных, такие как те для работы с CFStringRef введите, определяются в заголовочных файлах в CoreFoundation.framework.

Некоторые функции и типы данных, используемые в этой главе, описаны в страницах справочника UNIX. Для просмотра справочной документации для них см. Страницы справочника OS X. Также можно просмотреть документацию путем ввода manfunction_name (например, man tcsetattr) в Окне терминала. Многие фрагменты кода в этой главе обращаются к определенным страницам справочника в комментариях к коду.

Включая заголовочные файлы и макросы определения и константы

Перечисление 1-1 показывает заголовочные файлы, которые необходимо будет включать в основной файл для примера кода в этой главе. (Некоторые из этих заголовков включают других; более короткий список возможен.) За исключением CoreFoundation.h, эти заголовки обычно являются частью IOKit.framework или Kernel.framework

  Заголовочные файлы перечисления 1-1 для включения для модемного примера кода последовательного порта

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <paths.h>
#include <termios.h>
#include <sysexits.h>
#include <sys/param.h>
#include <sys/select.h>
#include <sys/time.h>
#include <time.h>
 
#include <CoreFoundation/CoreFoundation.h>
 
#include <IOKit/IOKitLib.h>
#include <IOKit/serial/IOSerialKeys.h>
#include <IOKit/IOBSD.h>

По умолчанию внутренние модемы Apple определяют local echo быть «включенным». При использовании примера кода в этой главе, чтобы найти и связаться с другим типом модема, необходимо не определить макрос, показанный в Перечислении 1-2.

  Макрос перечисления 1-2 для определения надлежащей строки модемного ответа

#define LOCAL_ECHO
 
#ifdef LOCAL_ECHO
#define kOKResponseString "AT\r\r\nOK\r\n"
#else
#define kOKResponseString "\r\nOK\r\n"

Пример кода также определяет константы, показанные в Перечислении 1-3.

  Константы перечисления 1-3 используются в модемном примере кода последовательного порта

 
#define kATCommandString        "AT\r"
#define kMyErrReturn            -1
 
enum
{
    kNumRetries = 3
};

Атрибуты последовательного порта, такие как тайм-ауты и скорости в бодах сохранены в termios структура. Пример кода определяет глобальную статическую структуру для хранения текущих атрибутов устройства, таким образом, он может восстановить их после изменения их.

static struct termios gOriginalTTYAttrs;

Установка основной функции

Перечисление 1-4 показывает a main функция, использующая функции Набора I/O, чтобы найти, что модем и функции POSIX получают доступ к нему. main функция выполняет свою работу путем вызывания следующих функций, показанных в других разделах:

Типы io_iterator_t и kern_return_t определяются в заголовочных файлах IOTypes.h и std_types.h, соответственно.

Константы EX_UNAVAILABLE, EX_IOERR, и EX_OK определяются в заголовочном файле sysexits.h.

Перечисление 1-4  , Настраивающее основную функцию для нахождения и доступа к модему

int main(void)
{
    int         fileDescriptor;
    kern_return_t   kernResult;
 
    io_iterator_t   serialPortIterator;
    char        deviceFilePath[MAXPATHLEN];
 
    kernResult = MyFindModems(&serialPortIterator);
 
    kernResult = MyGetModemPath(serialPortIterator, deviceFilePath,
                     sizeof(deviceFilePath));
 
    IOObjectRelease(serialPortIterator);    // Release the iterator.
 
    // Open the modem port, initialize the modem, then close it.
    if (!deviceFilePath[0])
    {
        printf("No modem port found.\n");
        return EX_UNAVAILABLE;
    }
 
    fileDescriptor = MyOpenSerialPort(deviceFilePath);
    if (fileDescriptor == kMyErrReturn)
    {
        return EX_IOERR;
    }
 
    if (MyInitializeModem(fileDescriptor))
    {
        printf("Modem initialized successfully.\n");
    }
    else {
        printf("Could not initialize modem.\n");
    }
 
    MyCloseSerialPort(fileDescriptor);
    printf("Modem port closed.\n");
 
    return EX_OK;
}

main функционируйте выпускает итератор, возвращенный MyFindModems функция, также выпускающая объекты итератора.

Нахождение всех модемов

MyFindModems функция, показанная в Перечислении 1-5, устанавливает соединение с Набором I/O путем вызова IOMasterPort функция, возвращающая порт Маха. Это тогда создает соответствующий словарь путем вызова IOServiceMatching, передача константы kIOSerialBSDServiceValue. Это устанавливает словарь, соответствующий все устройства классу провайдера IOSerialBSDClient.

Соответствующий словарь является словарем пар ключ/значение, описывающим свойства устройства Набора I/O или другой службы. Каждый объект последовательного устройства в Реестре I/O имеет свойство с ключом kIOSerialBSDTypeKey. Возможные значения этого ключа:

Этот демонстрационный проект интересуется только модемами, таким образом, MyFindModems функция совершенствовала соответствующий словарь путем вызова CFDictionarySetValue добавить ключ kIOSerialBSDTypeKey и значение kIOSerialBSDModemType. (Помните, если будут модемы с другой стороны последовательного порта, то этот пример кода не найдет их.)

Если Вы хотите изменить пример кода для нахождения другого типа последовательного устройства, можно дать kIOSerialBSDTypeKey ключ одно из других значений. Комментарии после вызова к CFDictionarySetValue в Перечислении 1-5 описывают, как сделать это.

Наконец, MyFindModems передает словарь функции Набора I/O IOServiceGetMatchingServices получить объект итератора, идентифицирующий все модемные устройства в Реестре I/O. Если успешный, MyFindModems использует его параметр указателя для возврата объекта итератора. Функция вызова ответственна за выпуск этого объекта.

Константы такой как kIOSerialBSDTypeKey и kIOSerialBSDModemType определяются в заголовочном файле IOSerialKeys.h. При необходимости в большей информации о процессе нахождения устройств в Реестре I/O посмотрите Аппаратные средства Доступа Из Приложений.

Константа KERN_SUCCESS определяется в заголовочном файле kern_return.h.

Перечисление 1-5  , Находящее все модемы последовательного порта в существующей системе

static kern_return_t MyFindModems(io_iterator_t *matchingServices)
{
    kern_return_t       kernResult;
    mach_port_t         masterPort;
    CFMutableDictionaryRef  classesToMatch;
 
    kernResult = IOMasterPort(MACH_PORT_NULL, &masterPort);
    if (KERN_SUCCESS != kernResult)
    {
        printf("IOMasterPort returned %d\n", kernResult);
    goto exit;
    }
 
    // Serial devices are instances of class IOSerialBSDClient.
    classesToMatch = IOServiceMatching(kIOSerialBSDServiceValue);
    if (classesToMatch == NULL)
    {
        printf("IOServiceMatching returned a NULL dictionary.\n");
    }
    else {
        CFDictionarySetValue(classesToMatch,
                             CFSTR(kIOSerialBSDTypeKey),
                             CFSTR(kIOSerialBSDModemType));
 
        // Each serial device object has a property with key
        // kIOSerialBSDTypeKey and a value that is one of
        // kIOSerialBSDAllTypes, kIOSerialBSDModemType,
        // or kIOSerialBSDRS232Type. You can change the
        // matching dictionary to find other types of serial
        // devices by changing the last parameter in the above call
        // to CFDictionarySetValue.
    }
 
    kernResult = IOServiceGetMatchingServices(masterPort, classesToMatch, matchingServices);
    if (KERN_SUCCESS != kernResult)
    {
        printf("IOServiceGetMatchingServices returned %d\n", kernResult);
    goto exit;
    }
 
exit:
    return kernResult;
}

Получение пути к файлу устройств для модема

Перечисление 1-6 показывает MyGetModemPath функция. Вызывающая сторона этой функции передает итератор списку модемов, указателя на хранение для пути файла устройств и максимального размера пути. Функциональные возвраты, в deviceFilePath параметр, путь к файлу устройств (включая имя файла) для первого модема это находит в итераторе.

Основная часть MyGetModemPath состоит из a while цикл, выполняющий итерации по всем модемным объектам в переданном итераторе. Пока это не находит модем, код в while цикл исследует каждый объект итератора, выполняя следующие операции:

  1. Это вызывает функцию Набора I/O IORegistryEntryCreateCFProperty, передача ключа kIOCalloutDeviceKey, получить a CFTypeRef к модемному файлу устройств.

  2. Если успешный в получении файла устройств модема, это вызывает CFStringGetCString получить полный путь к файлу устройств как струна до, на которую указывают deviceFilePath параметр.

  3. Если это находит имя, это распечатывает его, то выпуски CFTypeRef. Например, путь к файлу может быть /dev/cu.modem.

while цикл в MyGetModemPath выпуски каждый итератор возражает ему, получают при поиске модема последовательного порта, потому что IOIteratorNext функция сохраняет каждый объект, который она возвращает. Функция вызова (в этой выборке, main) ответственно за выпуск самого итератора, также выпускающего объекты итератора.

Наконец, MyGetModemPath возвращает значение результата, указывающее, получила ли функция успешно путь устройства для модема.

Перечисление 1-6  Возвращая файл устройств соединяет каналом для первого модема в переданном итераторе

static kern_return_t MyGetModemPath(io_iterator_t serialPortIterator, char *deviceFilePath, CFIndex maxPathSize)
{
    io_object_t     modemService;
    kern_return_t   kernResult = KERN_FAILURE;
    Boolean     modemFound = false;
 
    // Initialize the returned path
    *deviceFilePath = '\0';
 
    // Iterate across all modems found. In this example, we exit after
    // finding the first modem.
 
    while ((!modemFound) && (modemService = IOIteratorNext(serialPortIterator)))
    {
        CFTypeRef   deviceFilePathAsCFString;
 
    // Get the callout device's path (/dev/cu.xxxxx).
    // The callout device should almost always be
    // used. You would use the dialin device (/dev/tty.xxxxx) when
    // monitoring a serial port for
    // incoming calls, for example, a fax listener.
 
    deviceFilePathAsCFString = IORegistryEntryCreateCFProperty(modemService,
                            CFSTR(kIOCalloutDeviceKey),
                            kCFAllocatorDefault,
                            0);
        if (deviceFilePathAsCFString)
        {
            Boolean result;
 
        // Convert the path from a CFString to a NULL-terminated C string
        // for use with the POSIX open() call.
 
        result = CFStringGetCString(deviceFilePathAsCFString,
                                        deviceFilePath,
                                        maxPathSize,
                                        kCFStringEncodingASCII);
            CFRelease(deviceFilePathAsCFString);
 
            if (result)
            {
                printf("BSD path: %s", deviceFilePath);
                modemFound = true;
                kernResult = KERN_SUCCESS;
            }
        }
 
        printf("\n");
 
        // Release the io_service_t now that we are done with it.
 
    (void) IOObjectRelease(modemService);
    }
 
    return kernResult;
}

Открытие последовательного порта

Открыть последовательный порт, MyOpenSerialPort функция, показанная в Перечислении 1-7, вызывает open функция, передавая путь файла устройств, а также следующие константы:

Эти константы и open и fcntl функции определяются в fcntl.h.

Если open возвращает дескриптор правильного файла, MyOpenSerialPort выполняет выполняющие дополнительные шаги:

  1. Это вызывает ioctl функция, передавая TIOCEXCL, предотвратить дополнительный открывается на устройстве, кроме от корневого процесса.

  2. Это вызывает fcntl функция, передавая значение F_SETFL очищаться O_NONBLOCK отметьте, таким образом, последующий I/O блокирует.

  3. Это вызывает tcgetattr функция для сохранения текущих настроек файла в глобальной статической структуре gOriginalTTYAttrs, из типа termios. Эти значения будут восстановлены позже MyCLoseSerialPort функция (Перечисление 1-10). termios структура и tcgetattr и tcsetattr функции определяются в заголовке termios.h.

  4. Это устанавливает некоторые поля options, локальная переменная termios структура, с помощью значений определяется в заголовке termios.h. Эти опции указывают, среди прочего, необработанный режим ввода, одно второе значение тайм-аута для блокирования чтений и скоростей в бодах ввода и вывода. MyOpenSerialPort тогда передачи options структура к tcsetattr функционируйте для установки новых значений для последовательного порта (изменения не вступят в силу до вызова к tcsetattr). Константа TCSANOW также определяется в termios.h, и указывает, что изменение должно быть сразу внесено.

  5. Наконец, это возвращает дескриптор файла, полученный от вызова до open.

Можно счесть заголовки упомянутыми в этом разделе в заголовочных файлах в Kernel.framework, System.framework, или каталог /usr/include.

Перечисление 1-7  , Открывающее последовательный порт, указано переданным файлом устройств

static int MyOpenSerialPort(const char *deviceFilePath)
{
    int         fileDescriptor = -1;
    int         handshake;
    struct termios  options;
 
    // Open the serial port read/write, with no controlling terminal,
    // and don't wait for a connection.
    // The O_NONBLOCK flag also causes subsequent I/O on the device to
    // be non-blocking.
    // See open(2) ("man 2 open") for details.
 
    fileDescriptor = open(deviceFilePath, O_RDWR | O_NOCTTY | O_NONBLOCK);
    if (fileDescriptor == -1)
    {
        printf("Error opening serial port %s - %s(%d).\n",
               deviceFilePath, strerror(errno), errno);
        goto error;
    }
 
    // Note that open() follows POSIX semantics: multiple open() calls to
    // the same file will succeed unless the TIOCEXCL ioctl is issued.
    // This will prevent additional opens except by root-owned processes.
    // See tty(4) ("man 4 tty") and ioctl(2) ("man 2 ioctl") for details.
 
    if (ioctl(fileDescriptor, TIOCEXCL) == kMyErrReturn)
    {
        printf("Error setting TIOCEXCL on %s - %s(%d).\n",
            deviceFilePath, strerror(errno), errno);
        goto error;
    }
 
    // Now that the device is open, clear the O_NONBLOCK flag so
    // subsequent I/O will block.
    // See fcntl(2) ("man 2 fcntl") for details.
 
    if (fcntl(fileDescriptor, F_SETFL, 0) == kMyErrReturn)
    {
        printf("Error clearing O_NONBLOCK %s - %s(%d).\n",
            deviceFilePath, strerror(errno), errno);
        goto error;
    }
 
    // Get the current options and save them so we can restore the
    // default settings later.
    if (tcgetattr(fileDescriptor, &gOriginalTTYAttrs) == kMyErrReturn)
    {
        printf("Error getting tty attributes %s - %s(%d).\n",
            deviceFilePath, strerror(errno), errno);
        goto error;
    }
 
    // The serial port attributes such as timeouts and baud rate are set by
    // modifying the termios structure and then calling tcsetattr to
    // cause the changes to take effect. Note that the
    // changes will not take effect without the tcsetattr() call.
    // See tcsetattr(4) ("man 4 tcsetattr") for details.
 
    options = gOriginalTTYAttrs;
 
    // Print the current input and output baud rates.
    // See tcsetattr(4) ("man 4 tcsetattr") for details.
 
    printf("Current input baud rate is %d\n", (int) cfgetispeed(&options));
    printf("Current output baud rate is %d\n", (int) cfgetospeed(&options));
 
    // Set raw input (non-canonical) mode, with reads blocking until either
    // a single character has been received or a one second timeout expires.
    // See tcsetattr(4) ("man 4 tcsetattr") and termios(4) ("man 4 termios")
    // for details.
 
    cfmakeraw(&options);
    options.c_cc[VMIN] = 1;
    options.c_cc[VTIME] = 10;
 
    // The baud rate, word length, and handshake options can be set as follows:
 
    cfsetspeed(&options, B19200);   // Set 19200 baud
    options.c_cflag |= (CS7        |// Use 7 bit words
            PARENB     |        // Enable parity (even parity if PARODD
                                // not also set)
            CCTS_OFLOW |        // CTS flow control of output
            CRTS_IFLOW);        // RTS flow control of input
 
    // Print the new input and output baud rates.
 
    printf("Input baud rate changed to %d\n", (int) cfgetispeed(&options));
    printf("Output baud rate changed to %d\n", (int) cfgetospeed(&options));
 
    // Cause the new options to take effect immediately.
    if (tcsetattr(fileDescriptor, TCSANOW, &options) == kMyErrReturn)
    {
        printf("Error setting tty attributes %s - %s(%d).\n",
            deviceFilePath, strerror(errno), errno);
        goto error;
    }
 
    // To set the modem handshake lines, use the following ioctls.
    // See tty(4) ("man 4 tty") and ioctl(2) ("man 2 ioctl") for details.
 
    if (ioctl(fileDescriptor, TIOCSDTR) == kMyErrReturn)
    // Assert Data Terminal Ready (DTR)
    {
        printf("Error asserting DTR %s - %s(%d).\n",
            deviceFilePath, strerror(errno), errno);
    }
 
    if (ioctl(fileDescriptor, TIOCCDTR) == kMyErrReturn)
    // Clear Data Terminal Ready (DTR)
    {
        printf("Error clearing DTR %s - %s(%d).\n",
            deviceFilePath, strerror(errno), errno);
    }
 
    handshake = TIOCM_DTR | TIOCM_RTS | TIOCM_CTS | TIOCM_DSR;
    // Set the modem lines depending on the bits set in handshake.
    if (ioctl(fileDescriptor, TIOCMSET, &handshake) == kMyErrReturn)
    {
        printf("Error setting handshake lines %s - %s(%d).\n",
            deviceFilePath, strerror(errno), errno);
    }
 
    // To read the state of the modem lines, use the following ioctl.
    // See tty(4) ("man 4 tty") and ioctl(2) ("man 2 ioctl") for details.
 
    if (ioctl(fileDescriptor, TIOCMGET, &handshake) == kMyErrReturn)
    // Store the state of the modem lines in handshake.
    {
        printf("Error getting handshake lines %s - %s(%d).\n",
            deviceFilePath, strerror(errno), errno);
    }
 
    printf("Handshake lines currently set to %d\n", handshake);
 
    // Success:
    return fileDescriptor;
 
    // Failure:
error:
    if (fileDescriptor != kMyErrReturn)
    {
        close(fileDescriptor);
    }
 
    return -1;
}

Связь с модемом

Перечисление 1-8 показывает простую функцию инициализации модема, MyInitializeModem. Вызывающая сторона этой функции передает дескриптор файла для файла устройств модема. Модемный последовательный порт, как предполагается, открыт. MyInitializeModem функция выполняет следующие шаги:

  1. Это отправляет команду «AT» в модем, с помощью write функция, определяемая в unistd.h.

  2. Это пытается считать ответ из модема, с помощью read команда, проверяя его по желаемому ответу «OK».

Определения для констант kOKResponseString, kATCommandString, и kMyErrReturn показаны в Перечислении 1-3 и Перечислении 1-3.

Повсюду, MyInitializeModem функционируйте использует MyLogString функция (показанный в Перечислении 1-9) для замены печатаемыми эквивалентами непечатных символов в модемных командных строках и в данных, полученных от модема.

Перечисление 1-8  , Инициализирующее модем последовательного порта путем записи в и чтения из его файла устройств

static Boolean MyInitializeModem(int fileDescriptor)
{
    char    buffer[256];    // Input buffer
    char    *bufPtr;        // Current char in buffer
    ssize_t numBytes;       // Number of bytes read or written
    int     tries;          // Number of tries so far
    Boolean result = false;
 
    for (tries = 1; tries <= kNumRetries; tries++)
    {
        printf("Try #%d\n", tries);
 
        // Send an AT command to the modem
        numBytes = write(fileDescriptor, kATCommandString,
                         strlen(kATCommandString));
 
    if (numBytes == kMyErrReturn)
        {
            printf("Error writing to modem - %s(%d).\n", strerror(errno),
                        errno);
            continue;
        }
    else {
        printf("Wrote %d bytes \"%s\"\n", numBytes,
                        MyLogString(kATCommandString));
    }
 
    if (numBytes < strlen(kATCommandString))
    {
            continue;
    }
 
        printf("Looking for \"%s\"\n", MyLogString(kOKResponseString));
 
    // Read characters into our buffer until we get a CR or LF.
        bufPtr = buffer;
        do
        {
            numBytes = read(fileDescriptor, bufPtr, &buffer[sizeof(buffer)]
                        - bufPtr - 1);
            if (numBytes == kMyErrReturn)
            {
                printf("Error reading from modem - %s(%d).\n", strerror(errno),
                        errno);
            }
            else if (numBytes > 0)
            {
                bufPtr += numBytes;
                if (*(bufPtr - 1) == '\n' || *(bufPtr - 1) == '\r')
                {
                    break;
                }
            }
            else {
                printf("Nothing read.\n");
            }
        } while (numBytes > 0);
 
        // NULL terminate the string and see if we got a response of OK.
        *bufPtr = '\0';
 
        printf("Read \"%s\"\n", MyLogString(buffer));
 
        if (strncmp(buffer, kOKResponseString, strlen(kOKResponseString)) == 0)
        {
            result = true;
            break;
        }
    }
 
    return result;
}

MyInitializeModem функционируйте использует служебную вызванную функцию MyLogString это заменяет непечатные символы печатаемыми эквивалентами, с помощью ‘\’ символ. Перечисление 1-9 показывает MyLogString функция.

  Печать Включения перечисления 1-9 трафика данных

static char *MyLogString(char *str)
{
    static char     buf[2048];
    char            *ptr = buf;
    int             i;
 
    *ptr = '\0';
 
    while (*str)
    {
        if (isprint(*str))
        {
            *ptr++ = *str++;
        }
        else {
            switch(*str)
            {
            case ' ':
                *ptr++ = *str;
                break;
 
            case 27:
                *ptr++ = '\\';
                *ptr++ = 'e';
                break;
 
            case '\t':
                *ptr++ = '\\';
                *ptr++ = 't';
                break;
 
            case '\n':
                *ptr++ = '\\';
                *ptr++ = 'n';
                break;
 
            case '\r':
                *ptr++ = '\\';
                *ptr++ = 'r';
                break;
 
            default:
                i = *str;
                (void)sprintf(ptr, "\\%03o", i);
                ptr += 4;
                break;
            }
 
            str++;
        }
        *ptr = '\0';
    }
    return buf;
}

Закрытие последовательного порта

Перечисление 1-10 показывает MyCloseSerialPort функция. Эта функция выполняет следующие шаги:

  1. Это блокирует, пока весь вывод не был отправлен от устройства.

  2. Это восстанавливает предыдущее состояние последовательного порта, с помощью значений, сохраненных в статической структуре типа termios MyOpenSerialPort функция (Перечисление 1-7). termios структура определяется в заголовке termios.h в System.framework.

  3. Закрыть последовательный порт, MyCloseSerialPort вызовы close функция (определенный в unistd.h), передавая дескриптор файла для файла устройств последовательного порта (полученный MyOpenSerialPort функция).

Перечисление 1-10  , Закрывающее последовательный порт, указано переданным дескриптором файла

void MyCloseSerialPort(int fileDescriptor)
{
    // Block until all written output has been sent from the device.
    // Note that this call is simply passed on to the serial device driver.
    // See tcsendbreak(3) ("man 3 tcsendbreak") for details.
    if (tcdrain(fileDescriptor) == kMyErrReturn)
    {
        printf("Error waiting for drain - %s(%d).\n",
            strerror(errno), errno);
    }
 
    // It is good practice to reset a serial port back to the state in
    // which you found it. This is why we saved the original termios struct
    // The constant TCSANOW (defined in termios.h) indicates that
    // the change should take effect immediately.
    if (tcsetattr(fileDescriptor, TCSANOW, &gOriginalTTYAttrs) ==
                    kMyErrReturn)
    {
        printf("Error resetting tty attributes - %s(%d).\n",
                    strerror(errno), errno);
    }
 
    close(fileDescriptor);
}