Простой пример функции io_devctl()
Простой пример функции io_devctl()
Клиентский вызов devctl() формально определен так:
#include <sys/types.h>
#include <unistd.h>
#include <devctl.h>
int devctl(int fd, int dcmd, void *dev_data_ptr,
size_t nbytes, int *dev_info_ptr);
Прежде чем рассматривать эту функцию с позиций администратора ресурсов, надо сначала понять, что это за зверь. Функция devctl() применяется для «нестандартных» и «управляющих» операций. Например, вы можете записывать данные в звуковую плату (реальные оцифрованные звуковые фрагменты, которые звуковая плата должна будет конвертировать в аналоговый аудиосигнал) и принять решение об изменении числа каналов от одного (моно) до двух (стерео) или об изменении частоты дискретизации данных от стандарта CD (44.1 кГц) к стандарту DAT (48 кГц). Такие вещи было бы правильно делать при помощи функции devctl(). При написании администратора ресурсов вы можете решить, что вам вообще не нужны никакие devctl(), и что всю необходимую функциональность можно свести к стандартным функциям read() и write(). С другой стороны, вы можете захотеть использовать как вызовы devctl() наряду с вызовами read() и write(), так и только devctl() — это будет зависеть от вашего устройства.
Функция devctl() принимает 5 аргументов:
fd Дескриптор файла администратора ресурсов, которому вы посылаете команду devctl(). dcmd Собственно команда — комбинация из двух разрядов направления обмена данными и 30 разрядов команды (см. ниже). dev_data_ptr Указатель на область данных, которые передаются, принимаются или и то, и другое. nbytes Размер области данных, на которую указывает dev_data_ptr. dev_info_ptr Переменная для дополнительной информации, установку которой может выполнить администратор ресурса.Двумя старшими разрядами команды dcmd кодируется направление обмена данными, если он вообще имеет место. Подробности см. выше в описании функций ввода/вывода (параграф «io_devctl()»).
Когда администратор ресурсов принимает сообщение _IO_DEVCTL, оно обрабатывается вашей функцией io_devctl(). Ниже представлен очень простой пример, который предполагается использовать для настройки каналов и частоты дискретизации для аудиоустройства, как упоминалось выше.
/*
* io_devctl1.c
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <sys/neutrino.h>
#include <sys/iofunc.h>
#define DCMD_AUDIO_SET_CHANNEL_MONO 1
#define DCMD_AUDIO_SET_CHANNEL_STEREO 2
#define DCMD_AUDIO_SET_SAMPLE_RATE_CD 3
#define DCMD_AUDIO_SET_SAMPLE_RATE_DAT 4
int io_devctl(resmgr_context_t *ctp, io_devctl_t *msg,
iofunc_ocb_t *ocb) {
int sts;
// 1) Проверить, не является ли это обычным
// POSIX-совместимым devctl()
if ((sts =
iofunc_devctl_default(ctp, msg, ocb)) !=
_RESMGR_DEFAULT) {
return (sts);
}
// 2) Узнать, что за команда, и отработать ее
switch (msg->i.dcmd) {
case DCMD_AUDIO_SET_CHANNEL_MONO:
audio_set_nchannels(1);
break;
case DCMD_AUDIO_SET_CHANNEL_STEREO:
audio_set_nchannels(2);
break;
case DCMD_AUDIO_SET_SAMPLE_RATE_CD:
audio_set_samplerate(44100);
break;
case DCMD_AUDIO_SET_SAMPLE_RATE_DAT:
audio_set_samplerate(48000);
break;
// 3) Если мы не знаем такой команды, отвергнуть ее
default:
return (ENOSYS);
}
// 4) Сказать клиенту, что все отработано
memset(imsg->о, 0, sizeof(msg->о));
SETIOV(ctp->iov, &msg->o, sizeof(msg->o));
return (_RESMGR_NPARTS(1));
}
Этап 1
На первом этапе мы снова видим применение вспомогательной функции, на этот раз — функции iofunc_devctl_default(), которая используется для выполнения всей обработки по умолчанию для devctl(). Если вы не поставляете свою версию io_devctl(), а только инициализируете таблицы функций ввода/вывода и установления соединения при помощи iofunc_func_init(), будет вызвана именно iofunc_devctl_default(). Мы включаем ее в нашу версию io_devctl(), потому что мы хотим, чтобы она обработала для нас все стандартные POSIX-варианты вызова devctl(). Затем мы проверяем возвращаемое значение; если это не _RESMGR_DEFAULT, значит, функция iofunc_devctl_default() «обработала» запрос, и нам остается только возвратить это значение, выдав его за «наше».
Если возвращенное значение является константой _RESMGR_DEFAULT, это говорит нам, что вспомогательная функция не обработала запрос, и что мы должны выяснить, является ли он одним из «наших».
Этап 2
Эта проверка выполняется на этапе 2 при помощи инструкции switch/case. Мы просто проверяем значение dcmd, которое клиентский код указал во втором параметре функции devctl(), на предмет совпадения с какой-нибудь из «наших» команд. Обратите внимание, что для выполнения фактической «работы» для клиента мы вызываем фиктивные функции audio_set_nchannels() и audio_set_samplerate(). Здесь важно отметить, что мы преднамеренно избегаем обсуждения области данных функции devctl(). Вы можете подумать: «А что если я хочу установить частоту дискретизации в некое значение n? Как это сделать?» На этот вопрос мы ответим в следующем примере io_devctl(), который представлен ниже.
Этап 3
Этот этап — дань концепции защитного программирования. Мы возвращаем код ошибки ENOSYS, чтобы сообщить клиенту, что мы не распознали его запрос.
Этап 4
Наконец, мы обнуляем возвратную структуру и устанавливаем на нее одноэлементный вектор ввода/вывода. Затем мы возвращаем библиотеке администратора ресурсов единицу (1) через макрос _RESMGR_NPARTS(), сообщая ей тем самым, что мы возвращаем одноэлементный вектор ввода/вывода. Это и будет возвращено клиенту. Как вариант, мы могли бы применить макрос _RESMGR_PTR():
// Вместо этого
// 4) Сказать клиенту, что это сработало
memset(imsg->о, 0, sizeof(msg->о));
SETIOV(&ctp->iov, &msg->о, sizeof(msg->o));
return (_RESMGR_NPARTS(1));
//Мы могли бы сделать так:
// 4) Сказать клиенту, что это сработало
memset(imsg->о, 0, sizeof(msg->о));
return (_RESMGR_PTR(ctp, imsg->o, sizeof(msg->o)));
Причиной тому, что мы здесь обнулили возвращаемую структуру (вспомните, в примерах io_read() и io_write() мы этого не делали) является то, что в данном случае возвращаемая структура имеет реальное содержимое! (В случае с io_read() мы возвращали только собственно данные и число считанных байт — никакой «возвращаемой структуры» не было; в случае же с io_write() единственным возвращаемым значением было число записанных байт.)