#define MDMSTATUS 0
#define TXREGEMPTY 2
#define RXDATAREADY 4
#define RLINESTATUS 6
/* Флаги управления потоком XON/XOFF */
#define XON_RCVD 1
#define XOFF_RCVD 0
#define XON_SENT 1
#define XOFF_SENT 0
/* Высший и низший коэффициенты для триггера xon-xoff */
#define HI_TRIGGER(x) (3*x/4)
#define LO_TRIGGER(x) (x/4)
/* Функция получения нулевого бита целого */
#define bit0(i) (i & 0x0001)
/* Макрокоманда для включения прерывания, "номер разрешения
* прерывания" которого "i", в противном случае оно запрещает-
* ся. Например, прерывание THRE запрещается, если XOFF полу-
* чен от удаленной системы.
*/
#define turnon_int(i,j) \
if(((j=inp(IER))&i)==0)outp(IER,(j|i))
#define report_error(s) fprintf(stderr,s)
typedef struct QTYPE /* структура данных для очереди */
{
int count;
int front;
int rear;
int mexsize;
char *data;
} QTYPE;
static char rxbuf[RXQSIZE], txbuf[TXQSIZE];
static QTYPE rcvq = {0, }-1, -1, RXQSIZE, rxbuf},
trmq = {0, }-1, -1, TXQSIZE, txbuf};
/* Признаки общего состояния */
int s_linestatus, s_modemstatus;
- 8-25 -
static QTYPE *txq = &trmq, *rxq = &rcvq;
static short comport=0,
enable_xonxoff = 1,
rcvd_xonxoff = XON_RCVD,
sent_xonxoff = XON_SENT,
send_xon = FALSE,
send_xoff = FALSE,
int_number = 12;
int_enable_mask = 0xef,
int_disable_mask = 0x10;
/* Прототипы функций */
int s_sendchar(int);
int s_rcvchar(void);
int s_setup(short, unsigned);
int s_cleanup(void);
char *q_getfrom( QTYPE *, char *);
int q_puton( QTYPE *, char *);
void interrupt far s_inthndlr(void);
static void s_rda(void);
static void s_trmty(void);
static void (interrupt far *old_handler)();
/*----------------------------------------------------------*/
main(int argc, char **argv)
{
int ch, port_number = 0;
/* Получить номер порта из командной строки */
if(argc > 1) port_number = atoi(argv[1]) - 1;
printf("\nSERIO -- Последовательный ввод/вывод \
c параметрами 1200,8,N,1 через порт COM%d\n", port_number+1);
printf("\nСвязь ...\n");
/* Сначала установить последовательный порт */
s_setup(port_number, COM_PARAMS);
/* Последующий бесконечный цикл имитирует терминал.
* Клавиша Escape служить для общего сброса и возврата.
*/
while (TRUE)
{
if ((ch = s_rcvchar()) != -1) putch(ch);
if ( kbhit() != 0)
{
ch = getch();
if (ch == ESC_KEY)
{
s_cleanup();
return;
}
else
s_sendchar(ch);
- 8-26 -
} /* конец проверки kbhit() */
}
}
/*---------------------------------------------------------*/
/* s _ i n t h n d l r
* Обработчик всех прерываний последовательного порта.
*/
void interrupt far s_inthndlr(void)
{
int c;
registr int int_id, intmask;
/* Немедленно разрешить прерывания */
_enable();
while (TRUE)
{
/* Считать регистр идентификации прерывания , IIR */
int_id = inp(IIR);
if (bit0(int_id) == 1)
{
/* Если бит 0 - 1, то прерывания не ожидаются. Направить прог-
* раммируемому контроллеру прерываний сигнал "конец прерыва-
* ния" и вернуться.
*/
outp(P8259_0, END_OF_INT);
return;
}
if (int_id >= RXDATAREADY)
turnon_int(THREINT,intmask);
/* Обработать прерывание в соответствии с идентификатором.
* Следующий перечень составлен в соответствии с возрастанием
* приоритета.
*/
switch (int_id)
{
case MDMSTATUS: /* считать состояние модема */
s_modemstatus = inp(MSR);
break;
case TXREGEMPTY: s_trmty();
break;
case RXDATAREADY: s_rda();
break;
case RLINESTATUS: /* читать состояние линии */
s_linestatus = inp(LSR);
break;
/* пропустить, если идентификатор не является одним из
* перечисленных */
}
}
}
/*---------------------------------------------------------*/
/* s _ r d a
* Обработать прерывание "доступны данные для приема"
*/
- 8-27 -
static void s_rda(void)
{
registr int intmask;
char c;
/* читать из коммуникационного порта */
c = inp(comport);
if(enable_xonxoff) {
if (c == XON_ASCII) {
rcvd_xonxoff = XON_RCVD;
/* Включить прерывание THRE, если оно выключено. */
turnon_int(THREINT,intmask);
return;
}
if(c == XOFF_ASCII) {
rcvd_xonxoff = XOFF_RCVD;
/* Сбросить прерывания THRE */
intmask = inp(IER);
if (intmask & THREINT)
outp(IER, intmask & THREOFF);
return;
}
}
q_puton(rxq, &c);
/* Проверить, заполнена ли почти очередь (75%) */
if(enable_xonxoff) {
if(rxq->count >= HI_TRIGGER(RXQSIZE) &&
sent_xonxoff != XOFF_SENT ) {
/* Установить флаг для направления XOFF */
send_xoff = TRUE;
/* Включить прерывания THRE так, чтобы послать XOFF */
turnon_int(THREINT,intmask);
}
}
}
/*---------------------------------------------------------*/
/* s _ t r m t y
* Обработать прерывание "регистр хранения передачи
* свободен"
*/
static void s_trmty(void)
{
char c;
registr int ierval;
if (send_xoff == TRUE) {
outp(comport, XOFF_ASCII);
send_off = FALSE;
sent_xonxoff = XOFF_SENT;
return;
}
if (send_xon == TRUE) {
outp(comport, XON_ASCII);
send_xon = FALSE;
sent_xonxoff = XON_SENT;
return;
- 8-28 -
}
/* Поместить символ в регистр хранения передачи */
if( q_getfrom(txq, &c) != NULL){
outp(comport, c);
return;
}
/* Нечего посылать -- сбросить прерывания THRE */
ierval = inp(IER);
if (ierval & THREINT) outp(IER, ierval & THREOFF);
}
/*---------------------------------------------------------*/
/* s _ s e t u p
* Установить все для связи.
* Вернуть 1, если установка прошла успешно, в противном
* случае вернуть 0.
*/
int s_setup(short port_number, unsigned commparams)
{
int intmask;
if (port_number < 0 || port_number > 1)
report_error("Неверный номер порта!\n");
/* Получить базовый адрес последовательного порта из
* области данных BIOS */
comport = *(BIOS_DATA + port_number);
if (comport == 0)
{
report_error("BIOS не может найти порт!\n");
return(0);
}
/* Установить маски для программируемого контроллера
* прерываний 8259A. Для разрешения прерывания порта
* эта маска логически умножается с маской регистра
* в 21h. Для запрещения, логически сложить маску
* запрещения с маской регистра. Номер прерывания
* определяется как 8 + уровень IRQ прерывания.
* Коммуникационный порт 1 имеет IRQ 4, порт 2 имеет
* IRQ 3.
*/
if (port_number == 0)
{
int_enable_mask = 0xef;
int_disable_mask = 0x10;
int_number = 12;
}
if (port_number == 1)
{
int_enable_mask = 0xf7;
int_disable_mask = 8;
int_number = 11;
}
/* Получить номер старого прерывания и сохранить его. */
- 8-29 -
old_handler = _dos_getvect(int_number);
/* Установить новый обработчик с именем s_inthndlr.
* Запретить прерывания при смене обработчика.
*/
_disable();
_dos_setvect(int_number, s_inthndlr);
_enable();
/* Установить коммуникационные параметры */
_bios_serialcom(_COM_INIT, port_number, commparams);
/* Инициализировать флаги XON/XOFF */
rcvd_xonxoff = XON_RCVD;
if (sent_xonxoff == XOFF_SENT)
send_xon = TRUE;
else
send_xon = FALSE;
send_xoff = FALSE;
/* Включить прерывания коммуникационного порта и
* установка 8259A.
*/
_disable();
/* Установить регистр управления модемом (порт = MCR) */
outp(MCR, MCRALL);
/* Разрешить все прерывания последовательной карты */
outp(IER, IERALL);
/* Считать регистр маски прерывания 8259A и записать его
* обратно после логического умножения с _int_enable_mask.
*/
intmask = inp(P8259_1) & int_enable_mask;
outp(P8259_1, intmask);
_enable();
return(1);
}
/*---------------------------------------------------------*/
/* s _ c l e a n u p
/* Очистить после сеанса связи. Сбросить все прерывания. */
int s_cleanup(void)
{
int intmask;
/* Выключить прерывания последовательной карты */
_disable();
/* Первым сбросить регистр разрешения прерывания порта */
outp(IER, IEROFF);
/* Сбросить все биты регистра управления модемом */
outp(MCR, MCROFF);
/* Затем запретить распознавание контроллером 8259A
- 8-30 -