понедельник, 9 мая 2011 г.

13.3. Блоки запроса USB


13.3. Блоки запроса USB
Код USB в ядре Linux взаимодействует со всеми устройствами USB помощью так называемых urb (USB request block, блок запроса USB). Этот блок запроса описывается структурой struct urb и может быть найден в файле include/linux/usb.h.

urb используется для передачи или приёма данных в или из заданной оконечной точки USB на заданное USB устройство в асинхронном режиме. Он используется так же, как в асинхронном коде ввода/вывода в файловой системе используется структура kiocb или как в сетевом коде используется struct skbuff. В зависимости от потребностей, драйвер USB устройства может выделить для одной оконечной точке много urb-ов или может повторно использовать один urb для множества разных оконечных точек. Каждая оконечная точка в устройстве может обрабатывать очередь urb-ов, так что перед тем, как очередь опустеет, к одной оконечной точке может быть отправлено множество urb-ов. Типичный жизненный цикл urb выглядит следующим образом:


Создание драйвером USB.

Назначение в определённую оконечную точку заданного USB устройства.

Передача драйвером USB устройства в USB ядро.

Передача USB ядром в заданный драйвер контроллера USB узла для указанного устройства.

Обработка драйвером контроллера USB узла, который выполняет передачю по USB в устройство.

После завершения работы с urb драйвер контроллера USB узла уведомляет драйвер USB устройства.

Urb-ы также могут быть отменены в любое время драйвером, который передал urb, или ядром USB, если устройство удалено из системы. urb-ы создаются динамически и содержат внутренний счётчик ссылок, что позволяет им быть автоматически освобождаемыми, когда последний пользователь urb-а отпускает его.

Процедура, описанная в этой главе для обработки urb-ов является полезной, поскольку позволяет потоковое и других сложные, совмещённые взаимодействия, которые позволяют драйверам достигать максимально возможных скоростей передачи данных. Но доступны менее громоздкие процедуры, если вы просто хотите отправить отдельные потоковые или управляющие сообщения и не заботитесь о пропускной способности. (Смотрите раздел "USB передачи без Urb-ов".)
struct urb
Полями структуры struct urb, которые имеют значение для драйвера USB устройства являются:

struct usb_device *dev
Указатель на struct usb_device, в которую послан этот urb. Эта переменная должна быть проинициализирована драйвером USB перед  тем, как urb может быть отправлен в ядро USB.
unsigned int pipe
Информация оконечной точки для указанной struct usb_device, которую этот usb будет передавать. Эта переменная должна быть проинициализирована драйвером USB перед тем, как urb может быть отправлен в ядро USB.
Чтобы установить поля этой структуры, драйвер использует по мере необходимости следующие функции, в зависимости от направления передачи. Обратите внимание, что каждая оконечная точка может быть только одного типа.

unsigned int usb_sndctrlpipe(struct usb_device *dev, unsigned int endpoint)
Указывает ВЫХОДНУЮ управляющую оконечную точку для указанного USB устройства с указанным номером оконечной точки.

unsigned int usb_rcvctrlpipe(struct usb_device *dev, unsigned int endpoint)
Указывает ВХОДНУЮ управляющую оконечную точку для указанного USB устройства с указанным номером оконечной точки.

unsigned int usb_sndbulkpipe(struct usb_device *dev, unsigned int endpoint)
Указывает ВЫХОДНУЮ потоковую оконечную точку для указанного USB устройства с указанным номером оконечной точки.

unsigned int usb_rcvbulkpipe(struct usb_device *dev, unsigned int endpoint)
Указывает ВХОДНУЮ потоковую оконечную точку для указанного USB устройства с указанным номером оконечной точки.

unsigned int usb_sndintpipe(struct usb_device *dev, unsigned int endpoint)
Указывает ВЫХОДНУЮ оконечную точку прерывания для указанного USB устройства с указанным номером оконечной точки.

unsigned int usb_rcvintpipe(struct usb_device *dev, unsigned int endpoint)
Указывает ВХОДНУЮ оконечную точку прерывания для указанного USB устройства с указанным номером оконечной точки.

unsigned int usb_sndisocpipe(struct usb_device *dev, unsigned int endpoint)
Указывает ВЫХОДНУЮ изохронную оконечную точку для указанного USB устройства с указанным номером оконечной точки.

unsigned int usb_rcvisocpipe(struct usb_device *dev, unsigned int endpoint)
Указывает ВХОДНУЮ изохронную оконечную точку для указанного USB устройства с указанным номером оконечной точки.

unsigned int transfer_flags
Эта переменная может быть установлена в несколько различных битовых значений, в зависимости от того, что драйвер USB хочет, чтобы происходило с urb. Возможными значениями являются:

URB_SHORT_NOT_OK
Если установлен, он указывает, что любое неполноценное чтение во ВХОДНОЙ оконечной точке, которое может произойти, должно рассматриваться ядром USB как ошибка. Это значение используется только для urb-ов, которые читаются из USB устройства, но не для записи urb-ов.
URB_ISO_ASAP
Если urb изохронный, этот бит может быть установлен, если драйвер хочет, чтобы urb был запланирован, как только это позволит ему использование полосы пропускания, и установить переменную start_frame в urb в этой точке. Если для изохронного urb этот бит не установлен, драйвер должен указать значение start_frame и должен быть в состоянии восстановить его должным образом, если передача не может начаться в тот момент. Для дополнительной информации об изохронных urb-ах смотрите последующий раздел.
URB_NO_TRANSFER_DMA_MAP
Должен быть установлен, когда urb содержит для передачи буфер DMA. Ядро USB использует как указатель на буфер переменную transfer_dma, а не указатель буфера в виде переменной transfer_buffer.
URB_NO_SETUP_DMA_MAP
Как и бит URB_NO_TRANSFER_DMA_MAP, этот бит используется для управления urb-ми, которые имеют уже установленный буфер DMA. Если он установлен, ядро USB использует буфер, на который указывает переменная setup_dma, вместо переменной setup_packet.
URB_ASYNC_UNLINK
Если установлен, вызов usb_unlink_urb для этого urb возвращается почти немедленно и этот urb отсоединяется в фоновом режиме. В противном случае функция ждёт перед возвратом, пока urb отсоединится полностью и завершится. Используйте этот бит с осторожностью, так как это может сделать вопросы синхронизации очень сложными для отладки.
URB_NO_FSBR
Используется только драйвером UHCI USB хост-контроллера и указывает ему не попробовать выполнять логику Front Side Bus Reclamation. Этот бит, как правило, не должен быть установлен, так как машины с UHCI хост-контроллером создают большие накладные расходы для процессора и шина PCI насыщается ожиданием urb-а, который устанавливает этот бит.
URB_ZERO_PACKET
Если установлен, urb выходного потока заканчивается посылкой короткого пакета не содержащего данных, когда данные выравнивается по границе пакета оконечной точки. Это необходимо некоторым нестандартным USB устройствам (таким, как ряд USB ИК-устройства) для того, чтобы работать правильно.
URB_NO_INTERRUPT
Если установлен, оборудование не может генерировать прерывание после завершения urb-а. Этот бит следует использовать с осторожностью и только тогда, когда в очереди к одной оконечной точке находится множество urb-ов. Функции USB ядра используют это для того, чтобы выполнять передачи DMA буфера.

void *transfer_buffer
Указатель на буфер, который будет использоваться при передаче данных в устройство (для ВЫХОДНОГО urb) или при получении данных из устройства (для ВХОДНОГО urb). Для того, чтобы хост-контроллер правильно получал доступ к этому буферу, он должен быть создан вызовом kmalloc, а не на стеке или статически. Для управляющих оконечных точек этот буфер для стадии передачи данных.

dma_addr_t transfer_dma
Буфер для использования для передачи данных в USB устройство с помощью DMA.

int transfer_buffer_length
Длина буфера, на который указывает переменная transfer_buffer или transfer_dma (только одна из них может быть использована для urb). Если она 0, USB ядром никакие буферы передачи не используются.
Для ВЫХОДНОЙ оконечной точки, если максимальный размер оконечной точки меньше значения, указанного в этой переменной, передача в USB устройство разбивается на мелкие куски, чтобы правильно передать данные. Это большая передача происходит в последовательных кадрах USB. Гораздо быстрее поместить большой блок данных в один urb и предоставить контроллеру USB узла разделить его на мелкие куски, чем отправить маленькие буферы в последовательно.

unsigned char *setup_packet
Указатель на установочный пакет для управляющего urb-а. Он передаётся перед данными в буфере передачи. Эта переменная действительна только для управляющих urb-ов.

dma_addr_t setup_dma
DMA буфер для установочного пакета для управляющего urb-а. Он передается перед данными в обычном буфере передачи. Эта переменная действительна только для управляющих urb-ов.

usb_complete_t complete
Указатель на завершающую функцию обработки, которая вызывается USB ядром, когда urb полностью передан или при возникновении ошибки с urb-ом. В этой функции драйвер USB может проверить urb, освободить его, или использовать повторно для другой передачи. (Смотрите раздел "Завершение Urb-ов: завершающий обработчик с обратным вызовом" для более подробной информации о завершающем обработчике.)

Тип usb_complete_t определён как:

typedef void (*usb_complete_t)(struct urb *, struct pt_regs *);

void *context
Указатель на данные blob (Binary Large Object, большой двоичный объект), который может быть установлены USB драйвером. Он может быть использован в завершающем обработчике, когда urb возвращается в драйвер. Смотрите следующий раздел для более подробной информации об этой переменной.

int actual_length
После завершения urb-а эта переменная равна фактической длине данных или отправленных urb-ом (для ВЫХОДНЫХ urb-ов) или полученных urb-ом (для ВХОДНЫХ urb-ов). Для ВХОДНЫХ urb-ов, она должна быть использована вместо переменной transfer_buffer_length, поскольку полученные данные могут быть меньше, чем размер всего буфера.

int status
После завершения urb-а или его обработки USB ядром, эта переменная содержит текущий статус urb-а. Единственным временем, когда USB драйвер может безопасно получить доступ к этой переменной, является функция обработчика завершения urb-а (описанная в разделе "Завершение Urb-ов: завершающий обработчик с обратным вызовом"). Это ограничение является защитой от состояний гонок, которые происходят в то время, как urb обрабатывается USB ядром. Для изохронных urb-ов успешное значение (0) в этой переменной указывает лишь, был ли urb отсоединён. Для получения подробного статуса изохронных urb-ов должны быть проверены переменные iso_frame_desc.

Допустимые значения для этой переменной включают:

0
Передача urb-а была успешной.
-ENOENT
urb был остановлен вызовом usb_kill_urb.
-ECONNRESET
urb были отсоединён вызовом usb_unlink_urb и переменная transfer_flags в urb-е была установлена в URB_ASYNC_UNLINK.
-EINPROGRESS
urb всё ещё обрабатывается контроллерами узлов USB. Если ваш драйвер когда-нибудь увидит это значение, это является ошибкой в вашем драйвере.
-EPROTO
C этим urb-ом произошла одна из следующих ошибок:

Во время передачи произошла ошибка некорректной последовательности битов (bitstuff).

Оборудованием своевременно не был получен ответный пакет.
-EILSEQ
Было несоответствие контрольной суммы (CRC) при передаче urb-а.
-EPIPE
Оконечная точка сейчас застряла. Если данная оконечная точка не является управляющей оконечной точкой, эта ошибка может быть сброшена вызовом функции usb_clear_halt.
-ECOMM
Данные в течение передачи были получены быстрее, чем они могли быть записаны в память системы. Эта ошибка случается только с ВХОДНЫМИ urb-ами.
-ENOSR
Данные не могут быть получены из системной памяти при передаче достаточно быстро, чтобы сохранять запрошенную скорость передачи данных USB. Эта ошибка случается только с ВЫХОДНЫМИ urb-ами.
-EOVERFLOW
С urb-ом произошла ошибка "помеха". Ошибка "помеха" происходит, когда оконечная точка принимает больше информации, чем указанный максимальный размер пакета оконечной точки.
-EREMOTEIO
Возникает только если в переменной urb-а transfer_flags установлен флаг URB_SHORT_NOT_OK и означает, что весь объём данных, запрошенный этим urb-ом, не был принят.
-ENODEV
USB устройство является теперь отключенным от системы.
-EXDEV
Происходит только для изохронного urb-а и означает, что передача была выполнена лишь частично. Для того, чтобы определить, что было передано, драйвер должен посмотреть на статус отдельного фрейма.
-EINVAL
С urb-ом произошло что-то очень плохое. Документация USB ядра описывает, что это значение означает:

ISO madness, if this happens: Log off and go home
ISO обезумело, если это происходит: завершите работу и идите домой

Это также может произойти, если неправильно установлен какой-то параметр в структуре urb-а или если в USB ядро urb поместил неправильный параметр функции в вызове usb_submit_urb.
-ESHUTDOWN
Был серьёзные ошибки в драйвере контроллера USB узла; теперь он запрещён или устройство было отключено от системы, а urb был получен после удаления устройства. Она может также возникнуть, если во время помещения urb-а в устройство для данного устройства была изменена конфигурация.

Как правило, значения ошибок -EPROTO, -EILSEQ и -EOVERFLOW указывают на проблемы с оборудованием, встроенным программным обеспечением устройства, или кабелем, соединяющим устройство и компьютер.

int start_frame
Устанавливает или возвращает для использования номер начального фрейма для изохронной передачи.

int interval
Интервал, с которым собираются urb-ы. Это справедливо только для urb-ов прерывания или изохронных. Единицы значения существенно отличаются в зависимости от скорости устройства. Для низкоскоростных и полноскоростных устройств единицами являются фреймы, которые эквивалентны миллисекундам. Для высокоскоростных устройств единицами являются микрофреймы, что эквивалентно единицам в 1/8 миллисекунды. Эта значение должно быть установлено драйвером USB для изохронных urb-ов или urb-ов прерывания до того, как urb посылается в USB ядро.

int number_of_packets
Имеет смысл только для изохронных urb-ов и определяет число изохронных буферов передачи, которые должен обработать этот urb. Эта величина должна быть установлена драйвером USB для изохронных urb-ов передачи до того, как urb посылается в USB ядро.

int error_count
Устанавливается USB ядром только для изохронных urb-ов после их завершения. Она определяет число изохронных передач, которые сообщили о любых ошибках.

struct usb_iso_packet_descriptor iso_frame_desc[0]
Имеет смысл только для изохронных urb-ов. Эта переменная представляет собой массив из структур struct usb_iso_packet_descriptor, которые составляют этот urb. Такая структура позволяет сразу одним urb-ом определить число изохронных передач. Она также используется для сбора статуса передачи каждой отдельной передачи.

struct usb_iso_packet_descriptor состоит из следующих полей:

unsigned int offset
Смещение в буфере передачи (начиная с 0 для первого байта), где расположены данные этого пакета.
unsigned int length
Размер буфера передачи для этого пакета.
unsigned int actual_length
Длина данных, полученных в буфере передачи для этого изохронного пакета.
unsigned int status
Статус отдельной изохронной передачи этого пакета. Он может иметь такие же возвращаемые значения, как основная переменная статуса структуры struct urb.
Создание и уничтожение Urb-ов
Структура struct urb не должна быть создана статически в драйвере или внутри другой структуры, потому что это нарушит схему подсчёта ссылок, используемую USB ядром для urb-ов. Она должна быть создана вызовом функции usb_alloc_urb. Эта функция имеет такой прототип:

struct urb *usb_alloc_urb(int iso_packets, int mem_flags);

Первый параметр, iso_packets, это число изохронных пакетов, которые должен содержать этот urb. Если вы не хотите создать изохронный urb, эта переменная должна быть установлена в 0. Второй параметр, mem_flags, имеет такой же тип флага, который передаётся в функцию kmalloc для выделения ядром памяти (для подробной информации об этих флагах смотрите раздел "Аргумент flags" в Главе 8). Если функция успешно выделила для urb-а достаточно пространства, вызывающему возвращается указатель на urb. Если возвращаемое значение равно NULL, внутри ядра USB произошла какая-то ошибка и драйверу необходимо правильно выполнить очистку.

После того, как urb был создан, он должен быть правильно проинициализирован, прежде чем он может быть использован USB ядром. Как проинициализировать различные типы urb-ов, смотрите следующие разделы.

Для того, чтобы сказать USB ядру, что драйвер закончил работу с urb-ом, драйвер должен вызвать функцию usb_free_urb. Эта функция имеет только один аргумент:

void usb_free_urb(struct urb *urb);

Аргумент является указателем на struct urb, которую вы хотите освободить. После вызова этой функции, структура urb-а уходит, и драйвер не может больше получить к ней доступ.
Urb-ы прерывания
Функция usb_fill_int_urb является вспомогательной функцией для правильной инициализации urb-а, посылаемого в оконечную точку прерывания USB устройства:

void usb_fill_int_urb(struct urb *urb, struct usb_device *dev,
                      unsigned int pipe, void *transfer_buffer,
                      int buffer_length, usb_complete_t complete,
                      void *context, int interval);

Эта функция содержит много параметров:

struct urb *urb
Указатель на urb для инициализации.

struct usb_device *dev
USB устройство, которому этот urb будет отправлен.

unsigned int pipe
Указывает оконечную точку USB устройства, которому отправляется этот urb. Это значение создаётся упоминавшимся ранее функциями usb_sndintpipe или usb_rcvintpipe.

void *transfer_buffer
Указатель на буфер, из которого берутся исходящие данные или в который принимаются входящие данные. Заметим, что это не может быть статический буфер и он должен быть создан вызовом kmalloc.

int buffer_length
Размер буфера, на который указывает указатель transfer_buffer.

usb_complete_t complete
Указатель на завершающий обработчик, который вызывается при завершении этого urb-а.

void *context
Указатель на массив двоичных данных, который добавляется в структуру urb-а для последующего извлечения с помощью функции обработчика завершения.

int interval
Интервал с которым этот urb должен быть запланирован. Чтобы найти правильные единицы измерения для этого значения, смотрите предыдущее описание структуры struct urb.
Поточные Urb-ы
Поточные urb-ы инициализируются в основном так же, как urb-ы прерывания. Функцией, которая делает это, является usb_fill_bulk_urb, и она выглядит следующим образом:

void usb_fill_bulk_urb(struct urb *urb, struct usb_device *dev,
                       unsigned int pipe, void *transfer_buffer,
                       int buffer_length, usb_complete_t complete,
                       void *context);

Все параметры функции такие же, как в функции usb_fill_int_urb. Однако, нет параметра interval, поскольку поточные urb-ы не имеют значения интервала. Пожалуйста, заметьте, что переменная unsigned int pipe должна быть проинициализирована вызовом функций usb_sndbulkpipe или usb_rcvbulkpipe.

Функция usb_fill_int_urb не устанавливает переменную transfer_flags в urb, так что любые изменения этого поля предстоит сделать самому драйверу.
Управляющие Urb-ы
Управляющие urb-ы инициализируются почти так же, как поточные urb-ы, вызовом функции usb_fill_control_urb:

void usb_fill_control_urb(struct urb *urb, struct usb_device *dev,
                unsigned int pipe, unsigned char *setup_packet,
                void *transfer_buffer, int buffer_length,
                usb_complete_t complete, void *context);

Все параметры функции такие же, как в функции usb_fill_bulk_urb, за исключением того, что есть новый параметр, unsigned char *setup_packet, который должен указывать на настроечный пакет данных, который должен быть передан в оконечную точку. Кроме того, переменная unsigned int pipe должна быть проинициализирована вызовом функций usb_sndctrlpipe или usb_rcvictrlpipe.

Функция usb_fill_control_urb не устанавливает переменную transfer_flags в urb, поэтому любое изменение этого поля предстоит сделать самому драйверу. Большинство драйверов не используют эту функцию, так как намного проще использовать синхронные вызовы API, как описывается в разделе "USB передачи без Urb-ов".
Изохронные Urb-ы
Изохронные urb-ы, к сожалению, не имеют функции инициализации, как urb-ы прерывания, управления и поточные urb-ы. Поэтому они должны быть проинициализированы "вручную" в драйвере до того, как они могут быть отправлены в USB ядро. Ниже приводится пример того, как правильно инициализировать этот тип urb. Он был взят из драйвера ядра konicawc.c, находящегося в каталоге drivers/usb/media в основном дереве исходных текстов ядра.

urb->dev = dev;
urb->context = uvd;
urb->pipe = usb_rcvisocpipe(dev, uvd->video_endp-1);
urb->interval = 1;
urb->transfer_flags = URB_ISO_ASAP;
urb->transfer_buffer = cam->sts_buf[i];
urb->complete = konicawc_isoc_irq;
urb->number_of_packets = FRAMES_PER_DESC;
urb->transfer_buffer_length = FRAMES_PER_DESC;
for (j=0; j < FRAMES_PER_DESC; j++) {
    urb->iso_frame_desc[j].offset = j;
    urb->iso_frame_desc[j].length = 1;
}
Отправка Urb-ов
После того, как urb был надлежащим образом создан и проинициализирован USB драйвером, он готов быть отправленным в USB ядро для передачи в USB устройство. Это делается с помощью вызова функции usb_submit_urb:

int usb_submit_urb(struct urb *urb, int mem_flags);

Параметр urb является указателем на urb, который будет передан в устройство. Параметр mem_flags эквивалентен тому же параметру, который передаётся при вызове kmalloc и используется, чтобы указать USB ядру, как выделять любые буферы памяти в данный момент времени.

После того, как urb был успешно отправлен в USB ядро, никогда не следует пытаться обращаться к любым полям структуры urb-а до вызова функции complete.

Поскольку функция usb_submit_urb может быть вызвана в любое время (в том числе в контексте прерывания), спецификация переменной mem_flags должна быть правильной. Есть только три действительно допустимых значения, которые должны быть использованы в зависимости от того, когда вызывается usb_submit_urb:

GFP_ATOMIC
Это значение должно быть использовано всегда при выполнении следующих условий:

Вызывающий находится в завершающем обработчике urb, прерывании, нижней половине, тасклете или обратном вызове таймера.

Вызывающий удерживает спин-блокировку или блокировку чтения/записи. Заметим, что если удерживается семафор, это значение не является необходимым.

current->state не TASK_RUNNING. Состояние всегда TASK_RUNNING, пока драйвер не изменил текущее состояние сам.

GFP_NOIO
Эта значение должно быть использовано, если драйвер является частью блочного ввода/вывода. Она должна также быть использована в пути обработки ошибок всех типов устройств хранения.

GFP_KERNEL
Это должно быть использовано во всех других ситуациях, которые не подпадают под одну из вышеупомянутых категорий.
Завершение Urb-ов: завершающий обработчик с обратным вызовом
Если вызов usb_submit_urb был успешен, передавая контроль над urb в USB ядро, функция возвращает 0; иначе возвращается отрицательное число ошибки. Если функция завершается успешно, завершающий обработчик urb (задаваемый указателем на функцию complete) вызывается только один раз, когда urb завершается. Когда вызывается эта функция, ядро USB завершает работу с URB и контроль над ним теперь возвращается драйверу устройства.

Есть только три пути, как urb может быть завершён и как может быть вызвана функция complete:


urb успешно отправлен в устройство и устройство возвращает правильное подтверждение. Для ВЫХОДНОГО urb-а, данные были успешно отправлены, а для ВХОДНОГО urb-а, запрошенные данные были успешно получены. Если это произошло, статусная переменная в urb-е установлена в 0.

Какая-то ошибка произошла во время передачи и приёма данных из устройства. Это отмечено значением ошибки в статусной переменной в структуре urb-а.

urb был "отсоединён" от USB ядра. Это происходит либо когда драйвер приказывает USB ядру отменить отправку urb-а вызовом usb_unlink_urb или usb_kill_urb, или когда устройство удаляется из системы и urb были отправлен ему.

Пример того, как проверяются разные возвращаемые значения в течение завершающего вызова urb-а, показан далее в этой главе.
Отмена Urb-ов
Чтобы остановить urb, который был отправлен в USB ядро, должна называться функции usb_kill_urb или usb_unlink_urb:

int usb_kill_urb(struct urb *urb);
int usb_unlink_urb(struct urb *urb);

Параметр urb для этих обоих функций является указателем на urb, который должен быть отменён.

Когда функцией является usb_kill_urb, жизненный цикл urb-а останавливается. Эта функция обычно используется, когда устройство отключается от системы, в обратном вызове отключения.

Для некоторых драйверов должна быть использована функция usb_unlink_urb, чтобы приказать USB ядру остановить urb. Эта функция не ожидает перед возвращением к вызывающему, пока urb будет полностью остановлен. Это полезно для остановки urb-а в обработчике прерывания или во время удержания спин-блокировки, тогда как ожидание полной остановки urb-а требует способность USB ядра поместить вызывающий процесс в сон. Эта функция требует в urb-е установки значения флага URB_ASYNC_UNLINK, чтобы правильно отработать запрос на остановку.

Комментариев нет:

Отправить комментарий

Примечание. Отправлять комментарии могут только участники этого блога.