Управлять устройством
#include <sys/types.h>#include <unistd.h>#include <devctl.h>int devctl( int filedes,int dcmd,void *dev_data_ptr,size_t n_bytes,int *dev_info_ptr );int devctlv( int filedes,int dcmd,int sparts,int rparts,const iov_t *sv,const iov_t *rv,int *dev_info_ptr);
<sys/dcmd_*
.h> NULL
. Стоит избегать передачи в качестве аргумента dev_data_ptr буферов, содержащих локальные адреса текущего процесса. Их обработка и возможна посредством функции mem_offset64_peer(), но прибегать к этим механизмам настоятельно не рекомендуется. Если требуется иметь в двух процессах общую область памяти, следует использовать объекты в разделяемой памяти. |
NULL
или указатель на место, где устройство может разместить дополнительную статусную информацию, кроме обычных успеха или ошибки. Данные, возвращаемые с помощью dev_info_ptr, зависят от драйвера устройства.libc
Функция devctl() отправляет специфичную для устройства команду dcmd процессу, управляющему устройством, открытым как filedes. Например, можно отправлять команды для получения определенных свойств таких устройств, как клавиатуры, звуковые карты или последовательные порты.
Функция devctlv() похожа на devctl(), но использует векторы ввода/вывода для передачи данных к драйверу и обратно.
Команды управления устройством
Используйте следующие макросы для настройки команд управления устройством:
Аргументы для этих макросов:
Размер структуры, передаваемой последним полем в макросе __DIO* , должен быть меньше 2^14 == 16 KB. Значение большего размера перекрывает старшие биты поля, указывающие направление передачи. |
Менеджеры ресурсов могут использовать следующие макросы, определенные в <devctl.h>
, при обработке команд:
DEVDIR_TO
, DEVDIR_FROM
, DEVDIR_TOFROM
или DEVDIR_NONE
). Пример 1: Установка RTS для последовательного порта
Это достаточно простой пример установки и отключения RTS (Request to Send) для последовательного порта:
/* For "devctl()" */#include <devctl.h>#include <sys/dcmd_chr.h>/* For "open()" */#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>/* For Errors */#include <stdlib.h>#include <stdio.h>int check_RTS( int fd );int main( void ){int data = 0, fd, error;if ( (fd = open( "/dev/ser2", O_RDONLY )) == -1 ){fprintf( stderr, "Error with open() on /dev/ser2. Make sure it exists.\n" );perror( NULL );exit( EXIT_FAILURE );}check_RTS( fd );/* Let's turn ON RTS now. */data = _CTL_RTS_CHG | _CTL_RTS;if ( error = devctl( fd, DCMD_CHR_SERCTL, &data, sizeof( data ), NULL ) ){fprintf( stderr, "Error setting RTS: %s\n", strerror( error ) );exit( EXIT_FAILURE );}/* RTS should now be ON. */check_RTS( fd );sleep( 2 );/* Now let's turn RTS OFF. */data = _CTL_RTS_CHG | 0;if ( error = devctl( fd, DCMD_CHR_SERCTL, &data, sizeof( data ), NULL ) ){fprintf( stderr, "Error setting RTS: %s\n", strerror( error ) );exit( EXIT_FAILURE );}/* RTS should now be OFF. */check_RTS( fd );close( fd );return (1);}int check_RTS( int fd ){int data = 0, error;/** Let's see if RTS is set, tell devctl() we're requesting* line status information and devctl() then assigns data* the line status information for us. Too easy.*/if ( error = devctl( fd, DCMD_CHR_LINESTATUS, &data, sizeof( data ), NULL ) ){fprintf( stderr, "Error setting RTS: %s\n", strerror( error ) );exit( EXIT_FAILURE );}if ( data & _LINESTATUS_SER_RTS )printf( "RTS is SET!\n" );elseprintf( "RTS is NOT set\n" );return (1);}
Два места, на которые нужно обратить особое внимание - установка data и вызов devctl(). Переменная data используется и для отправки, и для получения данных.
При установке RTS data присваивается значение, отправляемое устройству с помощью devctl().
Значение data: | статус RTS: |
---|---|
_CTL_RTS_CHG | _CTL_RTS | ON |
_CTL_RTS_CHG | OFF |
Для проверки, установлен ли RTS, вызывается devctl() с аргументом dcmd, установленным в макрос DCMD_CHR_LINESTATUS
, и data, содержащим некоторое значение (ноль - чистое). Функция devctl() возвращает в data значение статуса линии. В дальнейшем оно будет использоваться для определения, какие линии установлены для данного устройства. В примере проверяется _LINESTATUS_SER_RTS
.
Чтобы узнать, какие значения используются с различными командами DCMD_*
, смотрите соответствующий заголовочный файл <sys/dcmd_*
.h>. Например, можно найти макросы для следующих значений DCMD_CHR_LINESTATUS
в <sys/dcmd_chr.h>
:
Значение в заголовочном файле с помощью побитового И складывается со значением из data для определения, установлен ли верхний сигнал для линии.
Пример 2: Цикличное изменение Caps Lock, Num Lock и Scroll Lock
В данном примере открывается устройство /dev/kbd
и изменяются свойства Caps Lock, Scroll Lock и Num Lock.
Ключевые моменты в этом примере аналогичны прошлому примеру; они сфокусированы вокруг переменой с данными. Данные представляют собой целочисленное значение, передающееся в функцию devctl(). Переменной данных присваивается значение путем выполнения побитового ИЛИ к предопределенным значениям из заголовочного файла </usr/include/sys/dcmd_chr
.h>. Обратите внимание на значения, используемые в побитовом ИЛИ:
_CONCTL_NUM_CHG
(Console Control Num Lock Change) и _CONCTL_NUM
(Console Control Num Lock) с применением побитового ИЛИ включают Num Lock. _CONCTL_NUM_CHG
сам по себе отключает Num Lock.
Если data: | статус Num Lock: |
---|---|
_CONCTL_NUM_CHG | _CONCTL_NUM | ON |
_CONCTL_NUM_CHG | OFF |
Это относится и к другим и/или значениям из заголовочного файла <dcmd_chr.h>
.
/* For "devctl()" */#include <devctl.h>#include <sys/dcmd_chr.h>/* For "open()" */#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>/* For Errors */#include <stdlib.h>#include <stdio.h>int main( void ){int data, fd, toggle = 1, error;/* Open the device we wish to manipulate. */if ((fd = open( "/dev/kbd", O_RDONLY )) == -1 ){fprintf( stderr, "Error with open() on /dev/kbd. Make sure it exists.\n" );perror( NULL );exit( EXIT_FAILURE );}while( 1 ){switch( toggle ){case 1:{/** Turn on Num Lock and make sure that* Caps and Scroll lock are turned off.*/data = (_CONCTL_NUM_CHG | _CONCTL_NUM) | _CONCTL_CAPS_CHG |_CONCTL_SCROLL_CHG;break;}case 2:{/** Turn off Num Lock and now turn on Caps Lock* (Scroll lock is already off).*/data = _CONCTL_NUM_CHG | (_CONCTL_CAPS_CHG | _CONCTL_CAPS);break;}case 3:{/** Turn off Caps lock and turn on Scroll lock* (Num lock is already off).*/data = _CONCTL_CAPS_CHG | (_CONCTL_SCROLL_CHG | _CONCTL_SCROLL);toggle = 0;break;}}/* Explanation below. */if ( error = devctl( fd, DCMD_CHR_SERCTL, &data, sizeof( data ), NULL ) ){fprintf( stderr, "Error setting KBD: %s\n", strerror( error ) );exit( EXIT_FAILURE );}sleep( 1 );toggle++;}return (1);}
Краткое объяснение использованного выше вызова devctl():
devctl( fd, DCMD_CHR_SERCTL, &data, sizeof( data ), NULL )
Первый параметр fd - файловый дескриптор устройства, состояние которого планируется изменить. Второй параметр - класс устройства, состояние которого планируется изменить. В нашем случае это символьное устройство с подклассом _SERCTL
. Третий параметр - переменная данных; значение, к которому применится побитовое ИЛИ.
Example 3: Пример продолжительности
В этом примере функция tcdropline(), использующаяся для отключения линии передачи данных, использует вызов devctl() (это реальный исходный код, tcdropline() - стандартная библиотечная функция):
#include <termios.h>#include <devctl.h>#include <errno.h>#include <sys/dcmd_chr.h>int tcdropline( int fd, int duration ){int error;duration = ((duration ? duration : 300) << 16) |_SERCTL_DTR_CHG | 0;if ( error = devctl( fd, DCMD_CHR_SERCTL, &duration,sizeof( duration ), 0 ) == -1 ){if ( error == ENOSYS ){errno = ENOTTY;}return (-1);}return (0);}
ЗОСРВ «Нейтрино»
При неудачном завершении devctl(), эффект неудачной команды зависит от драйвера устройства. Соответствующие данные могут быть переданы, частично переданы или полностью утеряны.
Функция devctl() изначально была частью проекта стандарта POSIX 1003.1d; затем она была объявлена устаревшей в стандарте IEEE Approved Draft 10.
iov_t, close(), GETIOVBASE(), GETIOVLEN(), ioctl(), open(), read(), SETIOV(), write()
Предыдущий раздел: Описание API системной библиотеки