/* TCAD8.C - TCtask - Serial I/O interface routines. V1.1 T.Wagner V1.2 TECON Ltd. V1.3 "DIYA" Co. */ /* The tables used are: port_list The pointer to the list of defined ports. Newly defined ports are added to the end of the list. Ports can never be deleted. Each port descriptor contains the hardware info for the port, plus a pointer to the associated sio-control-block if the port was initialized. port_last The pointer to the last element in port_list. irq_array Is an array of pointers to sio-control-blocks. For each possible IRQ-line the entry in this table points to the first block active for this line. If the IRQ is shared, sio-blocks are chained into this list via their "next" pointer. irq_procs Contains the pointer to the interrupt handler function for the corresponding IRQ-line. sio_data Contains the statically defined sio control blocks. port_descr Contains the statically defined port descriptor blocks. NOTE: You can not dynamically define ports for IRQ-lines that have no interrupt handler function defined. To be completely flexible, you would have to define sioint-handlers for all possible IRQs and enter the addresses into the irq_procs array. CAUTION: Please restrict the installation and removal of v24- ports to *one* task. The manipulation of the lists is not protected, so simultaneous install/remove calls may cause trouble. Since ports are normally installed and removed in the main task, the protection of critical regions seemed unnecessary. */ #include "tsk.h" #include "tclocal.h" #include "sio_ad8.h" #define MAX_IRQ 16 /* Maximum number of interrupt lines, 16 for AT, 8 for XT. Can be left at 16 */ #define CHAIN_IRQBIT 0x04 /* Chained int controller IRQ bit */ #define RESTORE_DEFAULT 1 /* Restore parameter for remove_all */ typedef void (interrupt * intprocptr)(void); #define inta00 0x20 /* 8259 interrupt controller control-port */ #define inta01 0x21 /* 8259 interrupt controller mask-port */ #define inta10 0xa0 /* secondary 8259 control-port for IRQ 8-15 */ #define inta11 0xa1 /* secondary 8259 mask-port for IRQ 8-15 */ #define eoi 0x20 /* end of interrupt signal for 8259 */ #define intdata 0x01 /* Enable Interrupts except Line status */ #define rxreadybit 0x02 /* готов пPиемник */ #define txreadybit 0x01 /* готов пеPедатчик */ #define portready 0x03 /* готов поpт */ #define transmit_on 0x31 /* РазРешение пеРедачи */ #define receive_on 0x14 /* РазРешение пРиема */ #define duplex_on 0x35 /* РазРешение пеРедачи и приема */ #define prog_reset 0x40 /* пРогРаммный сбРос канала */ /* Note: In version 1.1, port offsets start at 0, not 8 */ #define base_0 0x300 /* начальный базовый адРес поРтов */ #define intenable 0x312 /*  РегистР РазРеш/блокиР пРеРывания */ #define reset_all 0x313 /* РегистР общего сбРоса */ #define receiv_red 0x311 /* РегистР готовности пРиемников */ #define transm_red 0x310 /* РегистР готовности пеРедатчиков */ #define linecontrol 0x01 /* Default values for initialising the ports: */ #define dflt_baud 4800 /* Baud Rate Divisor: 4800 Baud */ #define dflt_lcon 0xfe /* Line Control: четность, 2 Stop, 8 Data */ /* Defined baud rates: */ local word_s baud_table [] = { 1200, 128, 2400, 64, 4800, 32, 9600, 16, 19200, 8, 0, 0 }; /*-------------------------------------------------------------------------*/ /* All that ports must be defined on-line. */ /* Note: In version 1.1, port offsets start at 0, not 8 */ /*-------------------------------------------------------------------------*/ local void interrupt sioint3 (void); /* Table of Interrupt handler functions for each IRQ line: */ local intprocptr irq_procs [MAX_IRQ] = { NULL, /* IRQ 0 */ NULL, /* IRQ 1 */ NULL, /* IRQ 2 */ sioint3, /* IRQ 3 */ NULL, /* IRQ 4 */ NULL, /* IRQ 5 */ NULL, /* IRQ 6 */ NULL, /* IRQ 7 */ NULL, /* IRQ 8 */ NULL, /* IRQ 9 */ NULL, /* IRQ 10 */ NULL, /* IRQ 11 */ NULL, /* IRQ 12 */ NULL, /* IRQ 13 */ NULL, /* IRQ 14 */ NULL }; /* IRQ 15 */ /* When adding entries to port_descr, be sure to chain the elements in ascending order via the first field, and to increase the internal port number in the second field. */ local sioptr irq_array [MAX_IRQ] = { NULL }; local portptr port_list = NULL; local portptr port_last = NULL; local word_s ports = 0; local byte mask_i_p = 0; /* маска установленных поРтов */ extern funcptr ad8_remove_func; /*-------------------------------------------------------------------------*/ local void interrupt sioint3 (void) { sioptr curr; tcbptr tsrd; pipeptr pip; byte ready; tsk_sti(); while((tsk_inp(receiv_red) | tsk_inp(transm_red)) & mask_i_p) { for (curr = irq_array [3]; curr != NULL; curr = curr->next) { ready = tsk_inp(curr->port_base+linecontrol); if(ready & portready) { if(ready & txreadybit) { tsk_outp(curr->port_base,*curr->xmit_buf++); if(!--curr->len_xmit) tsk_outp(curr->port_base+linecontrol,receive_on); } else { if(ready & 0x10) curr->flags=1; pip = (pipeptr)&curr->rcv_pipe; tsk_cli(); if(pip->filled < pip->bufsize) { pip->contents[pip->inptr++] = tsk_inp(curr->port_base); if(pip->inptr >= pip->bufsize) pip->inptr = 0; pip->filled++; if((tsrd = pip->wait_read) != NULL) { pip->wait_read = tsk_runable(tsrd); tsrd->retptr = (nearptr)pip->contents[pip->outptr++]; if(pip->outptr >= pip->bufsize) pip->outptr = 0; pip->filled--; } } else tsk_outp(curr->port_base+linecontrol,0x10); /* запpет В-В */ tsk_sti(); } } } } tsk_outp (inta00, eoi); } /*-------------------------------------------------------------------------*/ word_s ad8_define_port (word_s base, byte irq, byte vector) { #if (TSK_DYNAMIC) portptr portp; if (irq >= MAX_IRQ || irq_procs [irq] == NULL) return -1; if ((portp = tsk_alloc (sizeof (port_data))) == NULL) return -1; if(!(portp->pnum = ports)) tsk_outp(reset_all,0); portp->base = base; portp->irq = irq; portp->vector = vector; portp->next = NULL; portp->sio = NULL; if (port_list == NULL) port_list = portp; else port_last->next = portp; port_last = portp; ports++; return portp->pnum; #else return -1; #endif } /*---------------------------------------------------------------*/ local sioptr ret_error (sioptr sio) { sio->port->sio = NULL; #if (TSK_DYNAMIC) tsk_free (sio); #endif return NULL; } /*----------------------------------------------------------------*/ sioptr ad8_install (word_s port, nearptr rcvbuf, word rcvsize) { sioptr sio; portptr portp; word_s pbase; // intprocptr *intptr; word_s i, inta; #if (TSK_NAMEPAR) static char xname [] = "SIOnXMIT", rname [] = "SIOnRCV"; xname [3] = rname [3] = (char)(port & 0x7f) + '0'; #endif if (port < 0 || !rcvsize) return NULL; portp = port_list; if(port > ports) return NULL; while(portp->pnum != port) portp=portp->next; if (portp->sio != NULL) return NULL; /* Port already in use */ #if (TSK_DYNAMIC) if((portp->sio = sio = tsk_alloc(sizeof(sio_datarec)))==NULL) return NULL; #else return NULL; #endif pbase = sio->port_base = portp->base; sio->port = portp; /* Port seems OK */ if (create_pipe (&sio->rcv_pipe, rcvbuf, rcvsize #if (TSK_NAMEPAR) , rname #endif ) == NULL) return ret_error (sio); sio->civect = portp->vector; sio->irqbit = (byte)(1 << (portp->irq & 0x07)); sio->len_xmit=0; sio->clcontrol = dflt_lcon; ad8_change_baud(sio,dflt_baud); tsk_outp (sio->port_base + linecontrol, dflt_lcon); tsk_nop(); tsk_outp (intenable,0); if (irq_array [portp->irq] == NULL) { sio->savvect=get_vect_d (sio->civect); set_vect_d(sio->civect,irq_procs [portp->irq]); // intptr = (intprocptr *)(sio->civect * 4); // tsk_cli (); // sio->savvect = (nearptr)*intptr; // *intptr = irq_procs [portp->irq]; // tsk_sti (); } inta = (portp->irq > 7) ? inta11 : inta01; if (irq_array [portp->irq] == NULL) { if (portp->irq > 7) { i = tsk_inp (inta01) & ~CHAIN_IRQBIT; tsk_nop(); tsk_outp (inta01, (byte)i); } sio->save_irq = (byte)((i = tsk_inp (inta)) & sio->irqbit); tsk_nop(); tsk_outp (inta, (byte)(i & ~sio->irqbit)); } else sio->save_irq = (irq_array [portp->irq])->save_irq; tsk_cli (); sio->next = irq_array [portp->irq]; irq_array [portp->irq] = sio; tsk_sti (); mask_i_p |= 1 << ((sio->port_base - base_0) >> 1); ad8_remove_func =(funcptr)ad8_remove_all; tsk_outp (intenable, intdata); return sio; } /*-----------------------------------------------------------------*/ void ad8_remove (sioptr sio, word_s restore) { // intprocptr *intptr; word_s pbase, i, inta; portptr portp; sioptr curr, last; pbase = sio->port_base; portp = sio->port; last = NULL; curr = irq_array [portp->irq]; while (curr != sio && curr != NULL) { last = curr; curr = curr->next; } if (curr == NULL) return; tsk_outp (intenable, 0); tsk_cli (); if (last == NULL) irq_array [portp->irq] = sio->next; else last->next = sio->next; tsk_sti (); inta = (portp->irq > 7) ? inta11 : inta01; if (restore) { if (irq_array [portp->irq] == NULL) { tsk_cli (); i = tsk_inp (inta) & ~sio->irqbit; tsk_nop(); tsk_outp (inta, (byte)(i | sio->save_irq)); } } else if (irq_array [portp->irq] == NULL) { tsk_cli (); i = tsk_inp (inta) | sio->irqbit; tsk_nop(); tsk_outp (inta, (byte)i); } if (irq_array [portp->irq] == NULL) { set_vect_d(sio->civect,sio->savvect); // tsk_cli (); // intptr = (intprocptr *)(sio->civect * 4); // *intptr = (intprocptr)sio->savvect; } // tsk_sti (); portp->sio = NULL; delete_pipe (&sio->rcv_pipe); mask_i_p &= 1 << ((base_0 - sio->port_base) >> 1); #if (TSK_DYNAMIC) tsk_free (sio); #endif } /*-----------------------------------------------------------------*/ void ad8_remove_all (void) { word_s i; sioptr sio; for (i = 0; i < MAX_IRQ; i++) while ((sio = irq_array [i]) != NULL) ad8_remove (sio, RESTORE_DEFAULT); } /*-------------------------------------------------------------------------*/ void ad8_change_baud (sioptr sio, word_s rate) { word_s i; word_s port_baud, port_mode, val_instr; for (i = 0; baud_table [i]; i += 2) if(baud_table [i] == rate) break; if (!(i = baud_table [i + 1])) return; switch(sio->port_base) { case 0x300 : port_baud=0x314; port_mode=0x317; val_instr=0x16; break; case 0x302 : port_baud=0x315; port_mode=0x317; val_instr=0x56; break; case 0x304 : port_baud=0x316; port_mode=0x317; val_instr=0x96; break; case 0x306 : port_baud=0x318; port_mode=0x31b; val_instr=0x16; break; case 0x308 : port_baud=0x319; port_mode=0x31b; val_instr=0x56; break; case 0x30a : port_baud=0x31a; port_mode=0x31b; val_instr=0x96; break; case 0x30c : port_baud=0x31c; port_mode=0x31f; val_instr=0x16; break; case 0x30e : port_baud=0x31d; port_mode=0x31f; val_instr=0x56; break; } tsk_outp (port_mode, (byte)val_instr); tsk_nop(); tsk_outp (port_baud,(byte)i); } /*----------------------------------------------------------------*/ void ad8_change_mode(sioptr sio, word_s len, word_s par, word_s n) { switch (len) { case 5: len = 0x00; break; case 6: len = 0x04; break; case 7: len = 0x08; break; case 8: len = 0x0c; break; default: return; } switch (n) { case 1: n = 0x40; break; case 2: n = 0xc0; break; default: return; } tsk_outp (sio->port_base + linecontrol, prog_reset); tsk_nop(); sio->clcontrol = 0x12 | len | par | n; tsk_outp (sio->port_base + linecontrol, sio->clcontrol); tsk_nop(); } /*-------------------------------------------------------------------*/ void ad8_send (sioptr sio, char *buf, word len) { sio->xmit_buf = buf; sio->len_xmit = len; tsk_outp(sio->port_base + linecontrol, duplex_on); } /*-------------------------------------------------------------------*/ word_s ad8_receive (sioptr sio, dword timeout) { return read_pipe (&sio->rcv_pipe, timeout); } /*--------------------------------------------------------------------*/ word_s ad8_check (sioptr sio) { return check_pipe (&sio->rcv_pipe); } /*--------------------------------------------------------------------*/