Работа с последовательным устройством
Эта глава обеспечивает пример кода, демонстрирующий, как использовать механизм файла устройств, чтобы найти и связаться с последовательным устройством, явно утверждающим, что было модемом, такой как встроенное или USB-модем. Поскольку нет никакого безопасного, программируемого способа определить, является ли устройство, присоединенное к последовательному порту, действительно модемом, пример кода в этой главе не находит модемы с другой стороны последовательного порта.
Фрагменты кода в этой главе от примера приложения SerialPortSample
, доступный полностью в http://developer.apple.com/samplecode/SerialPortSample.
Несмотря на то, что пример кода в этой главе был скомпилирован и протестирован до некоторой степени, Apple не рекомендует непосредственно включить этот код в коммерческое применение. Например, только ограниченная обработка ошибок показана — необходимо разработать собственные методы для обнаружения и ошибок из-за неправильного обращения.
Доступ последовательного устройства в основанном на Intel Macintosh
Этот раздел кратко обрисовывает в общих чертах некоторые проблемы, связанные с разработкой универсальной версии двоичных файлов приложения Mac, использующего файлы устройств для доступа к последовательному устройству. Перед чтением этого раздела, убедиться считать Универсальные Двоичные Инструкции по Программированию, Второй Выпуск. Тот документ касается архитектурных различий, и порядок байтов форматирует и обеспечивает всесторонние инструкции для модификации кода и создания универсальных двоичных файлов. Инструкции в том документе применяются ко всем типам приложений, включая тех который аппаратные средства доступа.
Прежде чем Вы создадите свое приложение как универсальный двоичный файл, удостоверьтесь что:
Вы портируете свой проект на GCC 4 (XCode использует GCC 4 для предназначения для основанных на Intel компьютеров Macintosh),
Вы устанавливаете OS X v10.4 универсальный SDK
Вы разрабатываете свой проект в Xcode 2.1 или позже
Если Ваше приложение использует POSIX API стандартным способом, Вы не должны испытывать никакие затруднения при разработке универсальной версии двоичных файлов приложения. Как с любым приложением доступа к устройствам, однако при чтении многобайтовых целочисленных данных (вместо или в дополнение к символьным потокам) необходимо знать о разностях потенциалов в формате порядка байтов.
Если Вы решаете, что свопинг байта необходим, помните следующие инструкции:
Избегите безусловного подкачивающего байт кода, всегда подкачивающего от одного формата порядка байтов до другого. Вместо этого используйте условные подкачивающие байт макросы, определенные в
libkern/OSByteOrder.h
. Даже при том, что этот заголовочный файл находится в платформе Ядра, макросы доступны приложениям. Используя эти макросы означают, что компилятор оптимизирует Ваш код, таким образом, подпрограммы выполняются, только если они необходимы для архитектуры, в которой работает Ваше приложение.Будьте непротиворечивыми о том, когда Вы подкачаете байты. Вы могли бы принять решение выполнить надлежащую подкачку байта на данных после того, как это читается в буфер, и выполните противоположную подкачку байта на данных, которые готовы быть выписанными к устройству. Или, Вы могли бы принять решение выполнить весь свопинг байта на данных, поскольку это читается в или пишется из буфера. Право преимущественной покупки означает, что данные в буфере находятся в корректном формате порядка байтов для устройства, и вторая опция означает, что данные в буфере находятся в корректном формате порядка байтов для архитектуры, в которой работает Ваше приложение. Какой бы ни альтернатива, которую Вы выбираете, реализуйте его последовательно всюду по Вашему приложению.
Доступ к последовательному устройству
Для передачи с последовательным устройством из приложения Mac используйте функции Набора I/O для получения пути к файлу устройств для того устройства. Затем реализуйте традиционный доступ последовательного порта UNIX с помощью POSIX termios
API. Ваше приложение может считать и записать данные с помощью файла устройств.
В частности пример кода в этой главе демонстрирует как к:
Найдите все последовательные устройства, утверждающие, что были модемами
Получите путь к файлу устройств первого найденного модема
Используйте POSIX 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. Также можно просмотреть документацию путем ввода man
function_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
функция выполняет свою работу путем вызывания следующих функций, показанных в других разделах:
MyFindModems
(Находящий все модемы)MyGetModemPath
(Получение Пути к Файлу устройств для Модема)MyOpenSerialPort
(Открытие последовательного порта)MyInitializeModem
(Связывающийся с модемом)MyCloseSerialPort
(Закрытие последовательного порта)
Типы 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
. Возможные значения этого ключа:
kIOSerialBSDAllTypes
kIOSerialBSDModemType
kIOSerialBSDRS232Type
Этот демонстрационный проект интересуется только модемами, таким образом, 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
цикл исследует каждый объект итератора, выполняя следующие операции:
Это вызывает функцию Набора I/O
IORegistryEntryCreateCFProperty
, передача ключаkIOCalloutDeviceKey
, получить aCFTypeRef
к модемному файлу устройств.Если успешный в получении файла устройств модема, это вызывает
CFStringGetCString
получить полный путь к файлу устройств как струна до, на которую указываютdeviceFilePath
параметр.Если это находит имя, это распечатывает его, то выпуски
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
функция, передавая путь файла устройств, а также следующие константы:
O_RDWR
: открытый для чтения и записиO_NOCTTY
: не присваивайте терминал управленияO_NONBLOCK
: позвольте последующему I/O на устройстве неблокировать
Эти константы и open
и fcntl
функции определяются в fcntl.h
.
Если open
возвращает дескриптор правильного файла, MyOpenSerialPort
выполняет выполняющие дополнительные шаги:
Это вызывает
ioctl
функция, передаваяTIOCEXCL
, предотвратить дополнительный открывается на устройстве, кроме от корневого процесса.Это вызывает
fcntl
функция, передавая значениеF_SETFL
очищатьсяO_NONBLOCK
отметьте, таким образом, последующий I/O блокирует.Это вызывает
tcgetattr
функция для сохранения текущих настроек файла в глобальной статической структуреgOriginalTTYAttrs
, из типаtermios
. Эти значения будут восстановлены позжеMyCLoseSerialPort
функция (Перечисление 1-10).termios
структура иtcgetattr
иtcsetattr
функции определяются в заголовкеtermios.h
.Это устанавливает некоторые поля
options
, локальная переменнаяtermios
структура, с помощью значений определяется в заголовкеtermios.h
. Эти опции указывают, среди прочего, необработанный режим ввода, одно второе значение тайм-аута для блокирования чтений и скоростей в бодах ввода и вывода.MyOpenSerialPort
тогда передачиoptions
структура кtcsetattr
функционируйте для установки новых значений для последовательного порта (изменения не вступят в силу до вызова кtcsetattr
). КонстантаTCSANOW
также определяется вtermios.h
, и указывает, что изменение должно быть сразу внесено.Наконец, это возвращает дескриптор файла, полученный от вызова до
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
функция выполняет следующие шаги:
Это отправляет команду «AT» в модем, с помощью
write
функция, определяемая вunistd.h
.Это пытается считать ответ из модема, с помощью
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
функция. Эта функция выполняет следующие шаги:
Это блокирует, пока весь вывод не был отправлен от устройства.
Это восстанавливает предыдущее состояние последовательного порта, с помощью значений, сохраненных в статической структуре типа
termios
MyOpenSerialPort
функция (Перечисление 1-7).termios
структура определяется в заголовкеtermios.h
вSystem.framework
.Закрыть последовательный порт,
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); |
} |