                   Руководство программиста по работе
                 с драйвером TMK1553B v4.10 для QNX 6.x
          при работе с платами терминала мультиплексного канала
             (обеспечивающими сопряжение ПЭВМ типа IBM PC AT
              c мультиплексным каналом по ГОСТ 26765.52-87).
                   (Дополнение к файлу tmkguide.doc).

     Основой драйвера TMK1553B плат терминалов мультиплексного канала по 
ГОСТ 26765.52-87 (MIL-STD-1553B) для QNX 6.x является администратор
ресурса tmk1553b.

     Администратор ресурса управляет всеми платами ТМК, установленными в 
компьютер, конфигурация плат задается при старте tmk1553b. При этом 
каждой отдельной платой в каждый момент времени может управлять только 
один процесс, захвативший плату вызовом tmkconfig.

     API драйвера TMK1553B для QNX 6.x практически полностью 
повторяет набор функций драйвера TMKLL4 для MS-DOS. Исключение составляют 
функции инициализации, конфигурации и завершения работы драйвера, а также 
обслуживание прерываний от плат. Для работы с драйвером необходимо с 
помощью директивы 

#include "qnx6tmk.c"

подключить в текст программы на языке C/C++ файл qnx6tmk.c, содержащий
определения всех констант и функций трансляции вызовов TMKLL4 в вызовы 
TMK1553B.

     Начинать работу с драйвером необходимо с вызова функции TmkOpen: 

DWORD TmkOpen();

которая динамически загружает драйвер в память и производит его начальную 
настройку. При успешном завершении функция возвращает нулевое значение. 

     После окончания работы с TMK1553B должна вызываться функция TmkClose:

void TmkClose();

     Все платы, установленные в компьютере, описываются в параметрах вызова
tmk1553b, при этом каждой плате присваивается фиксированный номер от 0 до 7.
Чтобы начать работу с конкретной платой, необходимо вызвать функцию
tmkconfig, задав в качестве единственного параметра номер платы: 

int tmkconfig(int tmkNumber);

Если плата с заданным номером описана при вызове драйвера tmk1553b и с 
платой к этому моменту не работает ни один процесс, то функция закрепляет
эту плату за вызвавшим tmkconfig процессом, и в дальнейшем этот процесс
получает исключительный доступ к плате. При этом функция tmkconfig возвращает
нулевое значение. Если плата уже закреплена за каким-либо процессом или 
вообще не описана в реестре, то возвращается ненулевой код ошибки.

     Процесс освобождает платы, с которыми закончил работу, через вызов 
функции tmkdone:

int tmkdone(int tmkNumber);

В качестве параметра можно задавать либо номер конкретной платы, либо 
константу ALL_TMKS, при этом освобождаются все платы, захваченные 
процессом.

     После захвата платы в вызове tmkconfig вся работа (за исключением 
работы с прерываниями) осуществляется полностью аналогично работе через 
драйвер TMKLL4 для MS-DOS, т.е. точно также существуют понятия текущей 
платы (устанавливается функций tmkselect), текущей базы в режимах КК и 
МТ, текущего подадреса в режиме ОУ.

Описание работы с драйвером содержится в файлах tmkguide.doc, tmkref.doc.

     Из состава функций драйвера TMKLL4 в драйвере TMK1553B исключены все 
функции работы с прерываниями: bcdefintnorm, bcdefintexc, bcdefintx, 
bcdefintsig, rtdefintcmd, rtdefinterr, rtdefintdata, mtdefintx, 
mtdefintsig, bcrestore, rtrestore, mtrestore, tmksave, tmkrestore, 
tmkdefirq, tmkundefirq.

В драйвере tmk1553b работа с прерываниями осуществляется следующим образом.
Введено понятие маски прерываний плат - это 32-разрядное число, в котором каждый
из 31 младших бит соответствует прерыванию от платы с номером этого бита, т.е. 
бит 0 отвечает за прерывание ТМК 0, бит 1 - за прерывание ТМК 1 и т.д.

Для получения информации об возникших прерываниях или для ожидания прерываний 
процесс может вызвать функцию tmkwaitevents:

DWORD tmkwaitevents(DWORD mask, DWORD wait);

Параметр mask - это как раз маска прерываний, интересующих процесс. Если в 
момент вызова функции есть необработанные прерывания, подпадающие под 
указанную маску, то функция сразу возвращается и в качестве результата выдает
маску существующих необработанных прерываний. Если же на момент вызова 
tmkwaitevents необработанных прерываний нет, то поведение функции зависит от 
параметра wait, который может принимать значения 0 или положительное значение.
При значении 0 происходит немедленный выход из функции (соответственно, маска
ожидающих прерываний, возвращаемая функцией, будет равна 0, если ожидающих 
прерываний нет). Если в параметре wait задано положительное значение, то 
функция воспринимает его как максимальное время ожидания в миллисекундах.
В этом случае выход из функции производится либо по получению прерываний, либо
по истечению времени ожидания.

     После получения информации о прерываниях процесс может запросить структуру 
данных, описывающих прерывание. Для этого предназначена функция tmkgetevd:

void tmkgetevd(TTmkEventData *pEvD);

В качестве параметра передается указатель на структуру типа TTmkEventData, 
которую и заполняет драйвер соответствующими значениями:

typedef struct
{
  int nInt;
  unsigned short wMode;
  union
  {
    struct
    {
      unsigned short wResult;
      unsigned short wAW1;
      unsigned short wAW2;
    } bc;
    struct
    {
      unsigned short wBase;
      unsigned short wResultX;
    } bcx;
    struct
    {
      unsigned short wStatus;
      unsigned short wCmd;
    } rt;
    struct
    {
      unsigned short wBase;
      unsigned short wResultX;
    } mt;
    struct
    {
      unsigned short wStatus;
    } mrt;
  };
} TTmkEventData;

     Память под структуру должна быть выделена заранее в самом процессе, 
драйвер только заполняет ее! Первыми двумя полями определяется тип 
прерывания и соответствующие этому типу дополнительные поля. Существует 
прямое соответствие между типами прерываний, существующих в драйвере 
TMKLL4 для MS-DOS и в драйвере tmk1553b для QNX 6.x. С 
использованием имен функций задания подпрограмм пользовательской обработки 
прерывания из TMKLL4 соответствие следующее:

bcdefintnorm   wMode=BC_MODE, nInt=1, дополнительные данные bc.wResult;
bcdefintexc    wMode=BC_MODE, nInt=2, дополнительные данные bc.wResult,
               bc.wAW1, bc.wAW2;
bcdefintx      wMode=BC_MODE, nInt=3, дополнительные данные bcx.wResultX,
               bcx.wBase;
bcdefintsig    wMode=BC_MODE, nInt=4, дополнительные данные bcx.wBase;

rtdefintcmd    wMode=RT_MODE, nInt=1, дополнительные данные rt.wCmd;
rtdefinterr    wMode=RT_MODE, nInt=2, дополнительные данные rt.wStatus;
rtdefintdata   wMode=RT_MODE, nInt=3, дополнительные данные rt.wStatus;

mtdefintx      wMode=MT_MODE, nInt=3, дополнительные данные mt.wResultX,
               mt.wBase;
mtdefintsig    wMode=MT_MODE, nInt=4, дополнительные данные mt.wBase.
mtdefintx      wMode

    Начиная с версии v2.00 драйвер поддерживает работу с платами TX6/TE6 в 
режиме многоадресного ОУ (МОУ). В этом режиме на одном физическом устройстве 
многоадресного ОУ реализуются 4 виртуальных ОУ (ВОУ) с разными адресами в
канале. Если задать при конфигурации драйвера для какого-либо устройства
режим МОУ, в списке устройств драйвера будет как это устройство с номером,
заданным в реестре, так и дополнительно 4 ВОУ с номерами, присвоенными самим
драйвером.

    Для работы с МОУ введены новые функции:
TMK_DATA rtenable(TMK_DATA rtEnable);
int mrtgetmaxn();
DWORD mrtconfig(int mrtNumber);
int mrtselected();
TMK_DATA mrtgetstate();
void mrtdefbrcsubaddr0();
int mrtreset();

    Введена функция rtenable, основное назначение которой - включение или
выключение работы текущего ВОУ в МОУ. Однако с некоторыми ограничениями
этой функцией можно пользоваться и для обычных ОУ. Исключение составляют
ОУ на платах ТМК-400 и МТМК-400, которые не могут быть выключены этой
функцией. ОУ включается вызовом rtenable(RT_ENABLE), а выключается
вызовом rtenable(RT_DISABLE). Получить текущее состояние (RT_ENABLE или
RT_DISABLE) можно через вызов rtenable(RT_GET_ENABLE).

    Кроме того, изменен алгоритм работы функций rtreset и rtdefaddress для
всех ОУ. Если rtreset вызывается первый раз после tmkconfig или bcreset или
mtreset, то устройство переводится в режим ОУ, но остается выключенным как
после вызова rtenable(RT_DISABLE). Включается устройство только после
задания его адреса через rtdefaddress. Повторные вызовы rtreset не меняют
состояние включено/выключено.

    Функция mrtgetmaxn возвращает максимальный возможный номер МОУ. Этот
номер равен максимальному возможному номеру физического устройства в
драйвере. То есть для драйвера, предназначенного для работы с 8
устройствами (с 0 по 7) mrtgetmaxn возвратит 7. Функция tmkgetmaxn без
сконфигурированных МОУ работает также. При наличии МОУ драйвер
присвоит входящим в них ВОУ номера, начиная с 8, а tmkgetmaxn будет
возвращать максимальный присвоенный номер ВОУ. То есть, если будет
сконфигурировано одно МОУ с 4 ВОУ, то tmkgetmaxn вернет 11.

    Функция mrtconfig должна вызываться вместо tmkconfig для МОУ, если
программа пишется специально для работы с МОУ. Эта функция конфигурирует
само МОУ и все ВОУ, входящие в него, для работы с процессом, вызвавшим
mrtconfig. Функция mrtconfig возвращает двойное слово, младшие 16 бит
которого содержат начальный номер ВОУ, принадлежащих этому МОУ, а старшие
16 бит - число ВОУ, принадлежащих этому МОУ. При ошибке (например, если 
заданный номер не является номером МОУ) функция вернет нулевое значение.

    Возможна работа с многоадресными ОУ двумя способами. Можно захватить 
само МОУ и все входящие в него ВОУ вызовом mrtconfig. Другой способ - не 
вызывать mrtconfig для МОУ, а вызывать tmkconfig для каждого конкретного
ВОУ. Необходимо помнить, что МОУ имеет некоторые общие для всех входящих
ВОУ ресурсы, например, арбитр доступа к подадресу БЗУ (используется в
функциях rtlock, rtunlock, rtbusy), биты управления режимами работы
(функции rtdefmode, rtgetmode, rtdefirqmode, rtgetirqmode), регистр
аппаратного сброса (функции rtreset, mrtreset). Если разные ВОУ захвачены
разными процессами через tmkconfig, то вызов этих функций для одного ВОУ,
будет влиять на работу остальных. Функция rtreset, если она вызвана для
ВОУ, выполнит полный аппаратный сброс МОУ только в том случае, если 
захвачено только это ВОУ, а МОУ и остальные ВОУ не захвачены никаким 
процессом. Функция mrtreset выполнит аппаратный сброс МОУ и всех входящих 
в него ВОУ в любом случае.

    Захваченные через mrtconfig или tmkconfig МОУ и/или ВОУ выбираются 
для работы, как и ранее, функцией tmkselect.

    Функция mrtselected позволяет узнать номер выбранного МОУ для 
текущего выбранного ВОУ. Для обычных ОУ функции tmkselected и mrtselected 
будут возвращать одно и то же значение. Для ВОУ tmkselected будет
возвращать номер ВОУ, а mrtselected - номер МОУ.

    Функция mrtreset выполняет аппаратный сброс и инициализацию МОУ и всех 
входящих ВОУ.

    Для ВОУ и МОУ не могут вызываться функции bcreset и mtreset, поэтому 
в универсальных программах, если они могут использоваться на компьютерах 
с установленными МОУ, желательно проверять код возврата из этих функций. 
Для обычных устройств код возврата bcreset будет нулевым. Для МОУ/ВОУ 
bcreset вернет ненулевой код ошибки.

    Функция mrtgetstate позволяет прочитать регистр слова состояния МОУ 
(см. техническое описание режима МОУ). Функция rtgetstate, используемая
для чтения регистра слова состояния обычных ОУ, для ВОУ прочитает 
виртуальное слово состояния, формируемое самим драйвером.

    Функция mrtdefbrcsubaddr0 позволяет задать в качестве текущего
подадреса МОУ или ВОУ с младшим номером подадрес, в который приходят 
все групповые сообщения (см. техническое описание режима МОУ). Необходимо 
помнить, что даже если ВОУ с младшим номером выключено, но включено какое 
либо другое в составе данного МОУ, то в этот подадрес будут поступать 
данные отсообщений с групповым адресом.

Чтение слова состояния сообщения в режиме ОУ на платах серии TA

В драйвер добавлена функция rtgetmsgsw(), которая читает слово состояния 
сообщения в формате TA1 из текущего подадреса.
В слове состояния сообщения в режиме ОУ можно узнать активную линию передачи 
(по маске 0x0800) и было ли сообщение адресное или групповое (по маске 0x1000).

Для чтения слова состояния команд режима управления надо задавать специальные 
подадреса:
  - для команды "Передать векторное слово":
  rtdefsubaddr(TA_SA_CMD_VECW, TA_SA_CMD_VECW);
  - для команды "Передать слово встроенного контроля":
  rtdefsubaddr(TA_SA_CMD_BITW, TA_SA_CMD_BITW);
  - для всех остальных команд (кроме "внутренних"):
  rtdefsubaddr(TA_SA_CMD, TA_SA_CMD);

Буферизация в режиме ОУ на платах серии TA

Если возможны подряд идущие сообщения в один подадрес, в частности адресные 
и групповые, то во избежание затирания данных одного сообщения другим 
можно использовать буферизацию подадресов.
Изначально все подадреса небуферированы, этот режим обозначен как SABUF_OFF.

Буферизацию на каждый отдельный подадрес приёма можно включить вызовом 
функций:
rtallocabuf(SABUF_STD_R0+subaddr, size);
Здесь:
subadr - номер подадреса приёма (для команд управления - TA_SA_CMD)
size - число буферов, которое надо выделить для подадреса.
Функция возвращает текущее выделенное число буферов.

Узнать максимальное число буферов для каждого подадреса можно вызовом:
rtallocabuf(SABUF_STD_R0+subaddr, GET_MAX_ALLOC);

Узнать текущее число буферов для каждого подадреса можно вызовом:
rtallocabuf(SABUF_STD_R0+subaddr, GET_ALLOC);

В случае использования буферизации подадресов нельзя использовать режимы 
разграничения доступа к подадресу, как то блокировки подадресов функцией 
rtlock и флаговый режим. Функциональность этих режимов может быть реализована 
непосредственным управлением буферами.

Для чтения и записи данных в конкретный буфер его надо выбрать функцией 
rtdefbuf(bufnum), вызываемой после rtdefsubaddr.
Слово состояния сообщения rtgetmsgsw также будет читаться из выбранного 
буфера.

Когда буфера только выделены, каждый из них ссылается по цепочке на себя.
Чтобы данные автоматически буферизовались в разных буферах, надо их 
организовать в цепочку буферов.
Для этого предназначена функция rtdeflink(link), которая для выбранного 
буфера задаёт следующий в цепочке. Параметр link - номер следующего буфера, 
который может комбинироваться по "ИЛИ" с битами RT_LINK_IP (выдавать 
прерывание для буфера по завершению сообщения в нём) и RT_LINK_BS (выдавать 
в линию признак "Абонент занят", запрещая изменение данных в буфере).

Прочитать текцущее значение link можно функцией rtgetlink.

Для того, чтобы явно задать, в какой буфер подадреса должно попасть следующее 
сообщение из канала, адресованное в этот подадрес, предназначена функция 
rtdeflinkbus(link), параметр которой аналогичен rtdeflink. 

Узнать текущее значение номера активного буфера текущего подадреса можно 
функцией rtgetlinkbus.

При получении прерывания номер буфера передаётся вместе с прерыванием. 
В DOS его можно узнать, вызвав внутри обработчика прерывания функцию 
rtgetbufirq. В системах, где данные прерывания передаются через структуру
TTmkEventData в разделе rt добавлена переменная wBuf, то есть номер буфера 
можно узнать как tmkEvD.rt.wBuf, если tmkEvD заполняется вызовом tmkgetevd.

(c) ЗАО "Электронная компания ЭЛКУС", 1998,2013.