Простой пример функции 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() единственным возвращаемым значением было число записанных байт.)