Готовый перевод Linux Device Drivers 3 / Драйверы устройств Линукс (третье издание): 18.4. ioctls

Внимание! Этот перевод, возможно, ещё не готов, так как модераторы установили для него статус «перевод редактируется»

18.4. ioctls

Функция ioctls в структуре tty_driver вызывается ядром tty-
устройств, когда происходит вызов ioctls(2) для node устройства. Если
драйвер не знает какие выполнить действия для переданного значения
параметра он должен вернуть значение -ENOIOCTLCMD, чтобы ядро само
попыталось реализовать работу этого вызова.
В ядре 2.6 определены около 70 значений, передаваемых в функцию
ioctls tty-драйвера. Большинство tty-драйверов не обрабатывают их все, а
только небольшое количество общепринятых. Здесь приведен список
наиболее популярных, затем рассказано, как их обрабатывать:

TIOCSERGETLSR

Получить значение регистра состояния линии (LSR - line status
register)

TIOCGSERIAL

Получение информации о последовательной линии. Вызвавший
потенциально может получить большое количество информации от
tty-устройства за один вызов. Некоторые программы (такие как
setserial и dip) вызывают эту функцию чтобы убедится, что
скорость передачи установлена и для получения основной
информации об устройстве. При вызове передается указатель на
большую структуру типа serial_struct, которую tty-драйвер
заполняет соответствующими значениями. Вот пример реализации
этого:

static int tiny_ioctl(struct tty_struct *tty, struct file *file,
unsigned int cmd, unsigned long arg)
{
struct tiny_serial *tiny = tty->driver_data;
if (cmd = = TIOCGSERIAL) {
struct serial_struct tmp;
if (!arg)
return -EFAULT;
memset(&tmp, 0, sizeof(tmp));
tmp.type = tiny->serial.type;
tmp.line = tiny->serial.line;
tmp.port = tiny->serial.port;
tmp.irq = tiny->serial.irq;
tmp.flags = ASYNC_SKIP_TEST | ASYNC_AUTO_IRQ;
tmp.xmit_fifo_size = tiny->serial.xmit_fifo_size;
tmp.baud_base = tiny->serial.baud_base;
tmp.close_delay = 5*HZ;
tmp.closing_wait = 30*HZ;
tmp.custom_divisor = tiny->serial.custom_divisor;
tmp.hub6 = tiny->serial.hub6;
tmp.io_type = tiny->serial.io_type;
if (copy_to_user((void __user *)arg, &tmp, sizeof(tmp)))
return -EFAULT;
return 0;
}
return -ENOIOCTLCMD;
}

TIOCSSERIAL

Установка информации о последовательной линии. Операция
противоположна TIOCGSERIAL, и позволяет пользователю сразу
установить статус последовательной линии tty-устройства. В
функцию передается указатель на структуру struct serial_struct,
содержащую устанавливаемые параметры. Большинство программ
нормально работают, если данную возможность не реализовать.

TIOCMIWAIT

Ожидает изменения MSR. Пользователь редко дает этот запрос в
ioctls, функция ожидает, пока значение регистра MSR не
изменится. Параметр arg содержит тип ожидаемого события.
Обычно используется при ожидании изменения статуса линии,
говорящего, что устройство готово принимать данные.

Аккуратно реализуйте эту функцию, и не используйте
interruptible_sleep_on, это не безопасно в этом случае могут
возникнуть гонки. Вместо этого используйте wait_queue. Приведем
пример:

static int tiny_ioctl(struct tty_struct *tty, struct file *file,
unsigned int cmd, unsigned long arg)
{
struct tiny_serial *tiny = tty->driver_data;
if (cmd = = TIOCMIWAIT) {
DECLARE_WAITQUEUE(wait, current);
struct async_icount cnow;
struct async_icount cprev;
cprev = tiny->icount;
while (1) {
add_wait_queue(&tiny->wait, &wait);
set_current_state(TASK_INTERRUPTIBLE);
schedule( );
remove_wait_queue(&tiny->wait, &wait);
/* see if a signal woke us up */
if (signal_pending(current))
return -ERESTARTSYS;
cnow = tiny->icount;
if (cnow.rng = = cprev.rng && cnow.dsr = = cprev.dsr &&
cnow.dcd = = cprev.dcd && cnow.cts = = cprev.cts)
return -EIO; /* no change => error */
if (((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) ||
((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) ||
((arg & TIOCM_CD) && (cnow.dcd != cprev.dcd)) ||
((arg & TIOCM_CTS) && (cnow.cts != cprev.cts)) ) {
return 0;
}
cprev = cnow;
}
}
return -ENOIOCTLCMD;
}

Чтобы этот код работал должным образом, tty-драйверу необходимо
в каком-либо месте, при изменении регистра MSR, выполнить
функцию:

TIOCGICOUNT

Получить счетчик прерываний. Этот вызов происходит, когда
пользователю требуется узнать, сколько произошло прерываний от
последовательной линии. Если у драйвера есть обработчик
прерывания, драйвер должен определить структуру, для ведения
этой статистики, и увеличивать соответствующий счетчик при
каждом вызове обработчика.

В функцию ioctls передается указатель на структуру
serial_icounter_struct, которая заполняется tty-драйвером. Этот
вызов часто делается совместно с предыдущим вызовом TIOCMIWAIT.
Если при работе tty-драйвера отслеживаются все прерывания, то
реализующий этот вызов код может быть очень простым:

static int tiny_ioctl(struct tty_struct *tty, struct file *file,
unsigned int cmd, unsigned long arg)
{
struct tiny_serial *tiny = tty->driver_data;
if (cmd = = TIOCGICOUNT) {
struct async_icount cnow = tiny->icount;
struct serial_icounter_struct icount;
icount.cts = cnow.cts;
icount.dsr = cnow.dsr;
icount.rng = cnow.rng;
icount.dcd = cnow.dcd;
icount.rx = cnow.rx;
icount.tx = cnow.tx;
icount.frame = cnow.frame;
icount.overrun = cnow.overrun;
icount.parity = cnow.parity;
icount.brk = cnow.brk;
icount.buf_overrun = cnow.buf_overrun;
if (copy_to_user((void __user *)arg, &icount, sizeof(icount)))
return -EFAULT;
return 0;
}
return -ENOIOCTLCMD;
}

Внимание! Этот перевод, возможно, ещё не готов.
Его статус: перевод редактируется

Переведено на Нотабеноиде
http://notabenoid.org/book/11832/38402

Переводчики: hardorsoft

Настройки

Готово:

100.00% КП = 1.0