devctl()

Управлять устройством

Прототип:

#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);

Аргументы:

filedes
Дескриптор файла, полученный при открытии устройства.
dcmd
Команда процессу, управляющему открытым устройством, зависящая от типа устройства. Набор корректных команд, передаваемые данные, их интерпретация, возвращаемые в dev_info_ptr значения и влияние на устройство полностью зависят от драйвера устройства.
dev_data_ptr
(только для devctl()) В зависимости от команды этот аргумент может быть:

Caution: Стоит избегать передачи в качестве аргумента dev_data_ptr буферов, содержащих локальные адреса текущего процесса. Их обработка и возможна посредством функции mem_offset64_peer(), но прибегать к этим механизмам настоятельно не рекомендуется. Если требуется иметь в двух процессах общую область памяти, следует использовать объекты в разделяемой памяти.

n_bytes
(только для devctl()) Размер данных, отправляемых драйверу, или максимальный размер данных, которые можно принять от драйвера.
sparts
(только для devctlv()) Количество элементов в массиве sv.
rparts
(только для devctlv()) Количество элементов в массиве rv.
sv
(только для devctlv()) Массив объектов iov_t, содержащих данные для отправки в драйвер.
rv
(только для devctlv()) Массив объектов iov_t, в которых драйвер может разместить данные для возврата вызывающему объекту.
dev_info_ptr
NULL или указатель на место, где устройство может разместить дополнительную статусную информацию, кроме обычных успеха или ошибки. Данные, возвращаемые с помощью dev_info_ptr, зависят от драйвера устройства.

Библиотека:

libc

Описание:

Функция devctl() отправляет специфичную для устройства команду dcmd процессу, управляющему устройством, открытым как filedes. Например, можно отправлять команды для получения определенных свойств таких устройств, как клавиатуры, звуковые карты или последовательные порты.

Функция devctlv() похожа на devctl(), но использует векторы ввода/вывода для передачи данных к драйверу и обратно.

Команды управления устройством

Используйте следующие макросы для настройки команд управления устройством:

__DIOF( class, cmd, data )
Команда с получением данных от устройства.
__DION( class, cmd )
Команда без ассоциированных данных.
__DIOT( class, cmd, data )
Команда с передачей данных устройству.
__DIOTF( class, cmd, data )
Команда с передачей и получением данных от устройства.

Аргументы для этих макросов:

class
Основная категория команд. Команды управления устройством разделены на следующие классы для упрощения организации:
_DCMD_ALL
Общие (все серверы ввода/вывода).
_DCMD_CAM
Низкоуровневые (Common Access Method) устройства, такие как диски или CD-ROM.
_DCMD_CHR
Символьные устройства.
_DCMD_FSYS, _DCMD_BLK
Менеджеры ввода/вывода файловой системы/блокировки.
_DCMD_INPUT
Устройства ввода.
_DCMD_IP
Протокол Интернет.
_DCMD_MEM
Карта памяти.
_DCMD_MISC
Разные команды.
_DCMD_MIXER
Миксер (Аудио).
_DCMD_NET
Сетевые устройства.
_DCMD_PHOTON
Photon.
_DCMD_PROC
Менеджер процессов.
cmd
Код специфической команды в пределах класса.
data
Тип данных, передаваемых в и/или от устройства. Аргумент dev_data_ptr функции devctl() должен быть указателем на этот тип данных, а n_bytes - обычно размер этого типа данных.

Note: Размер структуры, передаваемой последним полем в макросе __DIO*, должен быть меньше 2^14 == 16 KB. Значение большего размера перекрывает старшие биты поля, указывающие направление передачи.

Менеджеры ресурсов могут использовать следующие макросы, определенные в <devctl.h>, при обработке команд:

get_device_command( cmd )
Извлекает класс и конкретную команду устройства из cmd (отсекая тем самым тип данных и направление).
get_device_direction( cmd )
Получает направление команды (DEVDIR_TO, DEVDIR_FROM, DEVDIR_TOFROM или DEVDIR_NONE).

Возвращаемое значение:

EOK
Успешное завершение.
EAGAIN
Команда devctl() не может быть завершена, поскольку драйвер устройства задействован в другом процессе, или драйвер не смог выполнить запрос из-за незавершенной выполняемой команды.
EBADF
Некорректный файловый дескриптор filedes.
EINTR
Функция devctl() прервана сигналом.
EINVAL
Драйвер устройства обнаружил ошибку в dev_data_ptr или n_bytes.
EIO
Функция devctl() не может быть завершена из-за аппаратной ошибки.
ENOSYS
Устройство не поддерживает команду dcmd.
ENOTTY
Аргумент dcmd не является корректной командой для данного устройства.
EPERM
Процесс не имеет достаточных привилегий для выполнения запрошенной команды.

Примеры:

Пример 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" );
else
printf( "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>. Обратите внимание на значения, используемые в побитовом ИЛИ:

Если 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 системной библиотеки