patch-1.3.25 linux/drivers/char/cyclades.c
Next file: linux/drivers/char/scc.c
Previous file: linux/drivers/char/README.scc
Back to the patch index
Back to the overall index
- Lines: 1842
- Date:
Sat Sep 9 09:36:08 1995
- Orig file:
v1.3.24/linux/drivers/char/cyclades.c
- Orig date:
Sun Sep 3 12:26:52 1995
diff -u --recursive --new-file v1.3.24/linux/drivers/char/cyclades.c linux/drivers/char/cyclades.c
@@ -1,11 +1,17 @@
static char rcsid[] =
-"$Revision: 1.36.1.4 $$Date: 1995/03/29 06:14:14 $";
+"$Revision: 1.36.3.2 $$Date: 1995/09/08 22:07:14 $";
/*
- * linux/kernel/cyclades.c
+ * linux/drivers/char/cyclades.c
*
- * Maintained by Marcio Saito ([email protected]) and
+ * This file contains the driver for the Cyclades Cyclom-Y multiport
+ * serial boards.
+ *
+ * Maintained by Marcio Saito ([email protected]) and
* Randolph Bentson ([email protected])
*
+ * For Technical support and installation problems, please send e-mail
+ * to [email protected].
+ *
* Much of the design and some of the code came from serial.c
* which was copyright (C) 1991, 1992 Linus Torvalds. It was
* extensively rewritten by Theodore Ts'o, 8/16/92 -- 9/14/92,
@@ -18,6 +24,19 @@
* int cy_open(struct tty_struct *tty, struct file *filp);
*
* $Log: cyclades.c,v $
+ * Revision 1.36.3.2 1995/09/08 22:07:14 bentson
+ * remove printk from ISR; fix typo
+ *
+ * Revision 1.36.3.1 1995/09/01 12:00:42 marcio
+ * Minor fixes in the PCI board support. PCI function calls in
+ * conditional compilation (CONFIG_PCI). Thanks to Jim Duncan
+ * <[email protected]>. "bad serial count" message removed.
+ *
+ * Revision 1.36.3 1995/08/22 09:19:42 marcio
+ * Cyclom-Y/PCI support added. Changes in the cy_init routine and
+ * board initialization. Changes in the boot messages. The driver
+ * supports up to 4 boards and 64 ports by default.
+ *
* Revision 1.36.1.4 1995/03/29 06:14:14 bentson
* disambiguate between Cyclom-16Y and Cyclom-32Ye;
*
@@ -224,6 +243,12 @@
#include <asm/segment.h>
#include <asm/bitops.h>
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/bios32.h>
+#include <linux/pci.h>
+
#define small_delay(x) for(j=0;j<x;j++)k++;
@@ -258,28 +283,48 @@
static int cy_wild_int_mask;
static unsigned char *intr_base_addr;
-/* This is the per-card data structure containing a card's base
- address. Here are declarations for some common addresses.
- Add entries to match your configuration (there are sixty-
- four possible from 0x80000 to 0xFE000) */
-struct cyclades_card cy_card[] = {
- /* BASE_ADDR */
- {0xD0000},
- {0xD2000},
- {0xD4000},
- {0xD6000},
- {0xD8000},
- {0xDA000},
- {0xDC000},
- {0xDE000}
+
+/* This is the address lockup table. The driver will probe for Cyclom-Y/ISA
+ boards at all addresses in here. If you want the driver to probe addresses
+ in a different address, add it to this table.
+ If the driver is probing some other board and causing problems, remove the
+ address from this table. */
+
+static unsigned char *cy_isa_addresses[] = {
+ (unsigned char *) 0xD0000,
+ (unsigned char *) 0xD2000,
+ (unsigned char *) 0xD4000,
+ (unsigned char *) 0xD6000,
+ (unsigned char *) 0xD8000,
+ (unsigned char *) 0xDA000,
+ (unsigned char *) 0xDC000,
+ (unsigned char *) 0xDE000,
};
+#define NR_ISA_ADDRESSES (sizeof(cy_isa_addresses)/sizeof(unsigned char *))
+
+/* This is the per-card data structure containing address, irq, number of
+ channels, etc. This driver supports a maximum of NR_CARDS cards. If
+ you need to install more boards, change this constant in the definition
+ bellow. No other change is necesary to support more boards. */
-#define NR_CARDS (sizeof(cy_card)/sizeof(struct cyclades_card))
+#define NR_CARDS 4
+
+static struct cyclades_card cy_card[NR_CARDS];
+
+/* This is the per-channel data structure containing pointers, flags
+ and variables for the port. This driver supports a maximum of NR_PORTS.
+ If the total number of ports is larger than NR_PORTS, change this
+ constant in the definition bellow. No other change is necessary to
+ support more boards/ports. */
+
+#define NR_PORTS 64
+
+static struct cyclades_port cy_port[NR_PORTS];
/* The Cyclom-Ye has placed the sequential chips in non-sequential
* address order. This look-up table overcomes that problem.
*/
-int cy_chip_offset [] =
+static int cy_chip_offset [] =
{ 0x0000,
0x0400,
0x0800,
@@ -290,43 +335,16 @@
0x0E00
};
-/* This is the per-port data structure */
-struct cyclades_port cy_port[] = {
- /* CARD# */
- {-1 }, /* ttyC0 */
- {-1 }, /* ttyC1 */
- {-1 }, /* ttyC2 */
- {-1 }, /* ttyC3 */
- {-1 }, /* ttyC4 */
- {-1 }, /* ttyC5 */
- {-1 }, /* ttyC6 */
- {-1 }, /* ttyC7 */
- {-1 }, /* ttyC8 */
- {-1 }, /* ttyC9 */
- {-1 }, /* ttyC10 */
- {-1 }, /* ttyC11 */
- {-1 }, /* ttyC12 */
- {-1 }, /* ttyC13 */
- {-1 }, /* ttyC14 */
- {-1 }, /* ttyC15 */
- {-1 }, /* ttyC16 */
- {-1 }, /* ttyC17 */
- {-1 }, /* ttyC18 */
- {-1 }, /* ttyC19 */
- {-1 }, /* ttyC20 */
- {-1 }, /* ttyC21 */
- {-1 }, /* ttyC22 */
- {-1 }, /* ttyC23 */
- {-1 }, /* ttyC24 */
- {-1 }, /* ttyC25 */
- {-1 }, /* ttyC26 */
- {-1 }, /* ttyC27 */
- {-1 }, /* ttyC28 */
- {-1 }, /* ttyC29 */
- {-1 }, /* ttyC30 */
- {-1 } /* ttyC31 */
-};
-#define NR_PORTS (sizeof(cy_port)/sizeof(struct cyclades_port))
+/* PCI related definitions */
+
+static unsigned short cy_pci_nboard = 0;
+static unsigned short cy_isa_nboard = 0;
+static unsigned short cy_nboard = 0;
+
+int cy_detect_isa(void);
+int cy_detect_pci(void);
+
+static int cy_next_channel = 0; /* next minor available */
static int serial_refcount;
@@ -334,10 +352,10 @@
static struct termios *serial_termios[NR_PORTS];
static struct termios *serial_termios_locked[NR_PORTS];
-
/* This is the per-irq data structure,
- it maps an irq to the corresponding card */
-struct cyclades_card * IRQ_cards[16];
+ it maps an irq to the corresponding card */
+
+struct cyclades_card *IRQ_cards[16];
/*
@@ -392,7 +410,6 @@
#endif
-
static inline int
serial_paranoia_check(struct cyclades_port *info,
dev_t device, const char *routine)
@@ -456,7 +473,7 @@
didn't finish within the time limit.
*/
u_short
-write_cy_cmd(u_char *base_addr, u_char cmd)
+write_cy_cmd(u_char *base_addr, u_char cmd, int index)
{
unsigned long flags;
volatile int i;
@@ -464,20 +481,20 @@
save_flags(flags); cli();
/* Check to see that the previous command has completed */
for(i = 0 ; i < 100 ; i++){
- if (base_addr[CyCCR] == 0){
+ if (base_addr[CyCCR<<index] == 0){
break;
}
udelay(10L);
}
/* if the CCR never cleared, the previous command
didn't finish within the "reasonable time" */
- if ( i == 10 ) {
+ if ( i == 100 ) {
restore_flags(flags);
return (-1);
}
/* Issue the new command */
- base_addr[CyCCR] = cmd;
+ base_addr[CyCCR<<index] = cmd;
restore_flags(flags);
return(0);
} /* write_cy_cmd */
@@ -492,7 +509,7 @@
struct cyclades_card *cinfo;
struct cyclades_port *info = (struct cyclades_port *)tty->driver_data;
unsigned char *base_addr;
- int chip,channel;
+ int chip,channel,index;
unsigned long flags;
#ifdef SERIAL_DEBUG_OTHER
@@ -503,15 +520,16 @@
return;
cinfo = &cy_card[info->card];
+ index = cinfo->bus_index;
channel = info->line - cinfo->first_line;
chip = channel>>2;
channel &= 0x03;
base_addr = (unsigned char*)
- (cy_card[info->card].base_addr + cy_chip_offset[chip]);
+ (cy_card[info->card].base_addr + (cy_chip_offset[chip]<<index));
save_flags(flags); cli();
- base_addr[CyCAR] = (u_char)(channel & 0x0003); /* index channel */
- base_addr[CySRER] &= ~CyTxMpty;
+ base_addr[CyCAR<<index] = (u_char)(channel & 0x0003); /* index channel */
+ base_addr[CySRER<<index] &= ~CyTxMpty;
restore_flags(flags);
return;
@@ -523,7 +541,7 @@
struct cyclades_card *cinfo;
struct cyclades_port *info = (struct cyclades_port *)tty->driver_data;
unsigned char *base_addr;
- int chip,channel;
+ int chip,channel,index;
unsigned long flags;
#ifdef SERIAL_DEBUG_OTHER
@@ -534,15 +552,16 @@
return;
cinfo = &cy_card[info->card];
+ index = cinfo->bus_index;
channel = info->line - cinfo->first_line;
chip = channel>>2;
channel &= 0x03;
base_addr = (unsigned char*)
- (cy_card[info->card].base_addr + cy_chip_offset[chip]);
+ (cy_card[info->card].base_addr + (cy_chip_offset[chip]<<index));
save_flags(flags); cli();
- base_addr[CyCAR] = (u_char)(channel & 0x0003);
- base_addr[CySRER] |= CyTxMpty;
+ base_addr[CyCAR<<index] = (u_char)(channel & 0x0003);
+ base_addr[CySRER<<index] |= CyTxMpty;
restore_flags(flags);
return;
@@ -572,8 +591,26 @@
static void
cy_probe(int irq, struct pt_regs *regs)
{
+ int save_xir, save_car;
+ int index = 0; /* probing interrupts is only for ISA */
+
cy_irq_triggered = irq;
cy_triggered |= 1 << irq;
+
+ if(intr_base_addr[CySVRR<<index] != 0) {
+ save_xir = (u_char) intr_base_addr[CyTIR<<index];
+ save_car = intr_base_addr[CyCAR<<index];
+ if ((save_xir & 0x3) != 0){
+ SP("channel ");
+ CP2(save_xir);
+ SP(" requesting unexpected interrupt\n");
+ }
+ intr_base_addr[CyCAR<<index] = (save_xir & 0x3);
+ intr_base_addr[CySRER<<index] &= ~CyTxMpty;
+ intr_base_addr[CyTIR<<index] = (save_xir & 0x3f);
+ intr_base_addr[CyCAR<<index] = (save_car);
+ *(intr_base_addr + (Cy_ClrIntr<<index)) = 0; /* Cy_ClrIntr is 0x1800 */
+ }
return;
} /* cy_probe */
@@ -592,10 +629,9 @@
int chip;
int save_xir, channel, save_car;
char data;
- volatile char vdata;
int char_count;
int outch;
- int i,j;
+ int i,j,index;
int too_many;
int had_work;
int mdm_change;
@@ -605,6 +641,9 @@
return; /* spurious interrupt */
}
+ card_base_addr = (unsigned char *)cinfo->base_addr;
+ index = cinfo->bus_index;
+
/* This loop checks all chips in the card. Make a note whenever
_any_ chip had some work to do, as this is considered an
indication that there will be more to do. Only when no chip
@@ -614,9 +653,9 @@
had_work = 0;
for ( chip = 0 ; chip < cinfo->num_chips ; chip ++) {
base_addr = (unsigned char *)
- (cinfo->base_addr + cy_chip_offset[chip]);
+ (cinfo->base_addr + (cy_chip_offset[chip]<<index));
too_many = 0;
- while ( (status = base_addr[CySVRR]) != 0x00) {
+ while ( (status = base_addr[CySVRR<<index]) != 0x00) {
had_work++;
/* The purpose of the following test is to ensure that
no chip can monopolize the driver. This forces the
@@ -627,32 +666,31 @@
break;
}
if (status & CySRReceive) { /* reception interrupt */
-
/* determine the channel and change to that context */
- save_xir = (u_char) base_addr[CyRIR];
+ save_xir = (u_char) base_addr[CyRIR<<index];
channel = (u_short ) (save_xir & CyIRChannel);
i = channel + chip * 4 + cinfo->first_line;
info = &cy_port[i];
info->last_active = jiffies;
- save_car = base_addr[CyCAR];
- base_addr[CyCAR] = save_xir;
+ save_car = base_addr[CyCAR<<index];
+ base_addr[CyCAR<<index] = save_xir;
/* if there is nowhere to put the data, discard it */
if(info->tty == 0){
- j = (base_addr[CyRIVR] & CyIVRMask);
+ j = (base_addr[CyRIVR<<index] & CyIVRMask);
if ( j == CyIVRRxEx ) { /* exception */
- data = base_addr[CyRDSR];
+ data = base_addr[CyRDSR<<index];
} else { /* normal character reception */
- char_count = base_addr[CyRDCR];
+ char_count = base_addr[CyRDCR<<index];
while(char_count--){
- data = base_addr[CyRDSR];
+ data = base_addr[CyRDSR<<index];
}
}
}else{ /* there is an open port for this data */
tty = info->tty;
- j = (base_addr[CyRIVR] & CyIVRMask);
+ j = (base_addr[CyRIVR<<index] & CyIVRMask);
if ( j == CyIVRRxEx ) { /* exception */
- data = base_addr[CyRDSR];
+ data = base_addr[CyRDSR<<index];
if(data & info->ignore_status_mask){
continue;
}
@@ -663,7 +701,7 @@
*tty->flip.flag_buf_ptr++ =
TTY_BREAK;
*tty->flip.char_buf_ptr++ =
- base_addr[CyRDSR];
+ base_addr[CyRDSR<<index];
if (info->flags & ASYNC_SAK){
do_SAK(tty);
}
@@ -671,12 +709,12 @@
*tty->flip.flag_buf_ptr++ =
TTY_FRAME;
*tty->flip.char_buf_ptr++ =
- base_addr[CyRDSR];
+ base_addr[CyRDSR<<index];
}else if(data & CyPARITY){
*tty->flip.flag_buf_ptr++ =
TTY_PARITY;
*tty->flip.char_buf_ptr++ =
- base_addr[CyRDSR];
+ base_addr[CyRDSR<<index];
}else if(data & CyOVERRUN){
*tty->flip.flag_buf_ptr++ =
TTY_OVERRUN;
@@ -690,7 +728,7 @@
*tty->flip.flag_buf_ptr++ =
TTY_NORMAL;
*tty->flip.char_buf_ptr++ =
- base_addr[CyRDSR];
+ base_addr[CyRDSR<<index];
}
/* These two conditions may imply */
/* a normal read should be done. */
@@ -710,7 +748,7 @@
}
} else { /* normal character reception */
/* load # characters available from the chip */
- char_count = base_addr[CyRDCR];
+ char_count = base_addr[CyRDCR<<index];
#ifdef CYCLOM_ENABLE_MONITORING
++info->mon.int_count;
@@ -724,7 +762,7 @@
break;
}
tty->flip.count++;
- data = base_addr[CyRDSR];
+ data = base_addr[CyRDSR<<index];
*tty->flip.flag_buf_ptr++ = TTY_NORMAL;
*tty->flip.char_buf_ptr++ = data;
#ifdef CYCLOM_16Y_HACK
@@ -735,8 +773,8 @@
queue_task_irq_off(&tty->flip.tqueue, &tq_timer);
}
/* end of service */
- base_addr[CyRIR] = (save_xir & 0x3f);
- base_addr[CyCAR] = (save_car);
+ base_addr[CyRIR<<index] = (save_xir & 0x3f);
+ base_addr[CyCAR<<index] = (save_car);
}
@@ -745,21 +783,21 @@
we know we can always stuff a dozen characters. */
/* determine the channel and change to that context */
- save_xir = (u_char) base_addr[CyTIR];
+ save_xir = (u_char) base_addr[CyTIR<<index];
channel = (u_short ) (save_xir & CyIRChannel);
i = channel + chip * 4 + cinfo->first_line;
- save_car = base_addr[CyCAR];
- base_addr[CyCAR] = save_xir;
+ save_car = base_addr[CyCAR<<index];
+ base_addr[CyCAR<<index] = save_xir;
/* validate the port number (as configured and open) */
if( (i < 0) || (NR_PORTS <= i) ){
- base_addr[CySRER] &= ~CyTxMpty;
+ base_addr[CySRER<<index] &= ~CyTxMpty;
goto txend;
}
info = &cy_port[i];
info->last_active = jiffies;
if(info->tty == 0){
- base_addr[CySRER] &= ~CyTxMpty;
+ base_addr[CySRER<<index] &= ~CyTxMpty;
goto txdone;
}
@@ -769,7 +807,7 @@
if(info->x_char) { /* send special char */
outch = info->x_char;
- base_addr[CyTDR] = outch;
+ base_addr[CyTDR<<index] = outch;
char_count--;
info->x_char = 0;
}
@@ -783,28 +821,28 @@
so the delay is duration * 200/HZ, and thus a
break can run from 1/100 sec to about 5/4 sec.
*/
- base_addr[CyTDR] = 0; /* start break */
- base_addr[CyTDR] = 0x81;
- base_addr[CyTDR] = 0; /* delay a bit */
- base_addr[CyTDR] = 0x82;
- base_addr[CyTDR] = info->x_break*200/HZ;
- base_addr[CyTDR] = 0; /* terminate break */
- base_addr[CyTDR] = 0x83;
+ base_addr[CyTDR<<index] = 0; /* start break */
+ base_addr[CyTDR<<index] = 0x81;
+ base_addr[CyTDR<<index] = 0; /* delay a bit */
+ base_addr[CyTDR<<index] = 0x82;
+ base_addr[CyTDR<<index] = info->x_break*200/HZ;
+ base_addr[CyTDR<<index] = 0; /* terminate break */
+ base_addr[CyTDR<<index] = 0x83;
char_count -= 7;
info->x_break = 0;
}
while (char_count-- > 0){
if (!info->xmit_cnt){
- base_addr[CySRER] &= ~CyTxMpty;
+ base_addr[CySRER<<index] &= ~CyTxMpty;
goto txdone;
}
if (info->xmit_buf == 0){
- base_addr[CySRER] &= ~CyTxMpty;
+ base_addr[CySRER<<index] &= ~CyTxMpty;
goto txdone;
}
if (info->tty->stopped || info->tty->hw_stopped){
- base_addr[CySRER] &= ~CyTxMpty;
+ base_addr[CySRER<<index] &= ~CyTxMpty;
goto txdone;
}
/* Because the Embedded Transmit Commands have been
@@ -823,14 +861,14 @@
info->xmit_cnt--;
info->xmit_tail = (info->xmit_tail + 1)
& (PAGE_SIZE - 1);
- base_addr[CyTDR] = outch;
+ base_addr[CyTDR<<index] = outch;
}else{
if(char_count > 1){
info->xmit_cnt--;
info->xmit_tail = (info->xmit_tail + 1)
& (PAGE_SIZE - 1);
- base_addr[CyTDR] = outch;
- base_addr[CyTDR] = 0;
+ base_addr[CyTDR<<index] = outch;
+ base_addr[CyTDR<<index] = 0;
char_count--;
}else{
}
@@ -844,22 +882,22 @@
txend:
/* end of service */
- base_addr[CyTIR] = (save_xir & 0x3f);
- base_addr[CyCAR] = (save_car);
+ base_addr[CyTIR<<index] = (save_xir & 0x3f);
+ base_addr[CyCAR<<index] = (save_car);
}
if (status & CySRModem) { /* modem interrupt */
/* determine the channel and change to that context */
- save_xir = (u_char) base_addr[CyMIR];
+ save_xir = (u_char) base_addr[CyMIR<<index];
channel = (u_short ) (save_xir & CyIRChannel);
info = &cy_port[channel + chip * 4 + cinfo->first_line];
info->last_active = jiffies;
- save_car = base_addr[CyCAR];
- base_addr[CyCAR] = save_xir;
+ save_car = base_addr[CyCAR<<index];
+ base_addr[CyCAR<<index] = save_xir;
- mdm_change = base_addr[CyMISR];
- mdm_status = base_addr[CyMSVR1];
+ mdm_change = base_addr[CyMISR<<index];
+ mdm_status = base_addr[CyMSVR1<<index];
if(info->tty == 0){ /* nowhere to put the data, ignore it */
;
@@ -881,14 +919,14 @@
if(mdm_status & CyCTS){
/* !!! cy_start isn't used because... */
info->tty->stopped = 0;
- base_addr[CySRER] |= CyTxMpty;
+ base_addr[CySRER<<index] |= CyTxMpty;
cy_sched_event(info, Cy_EVENT_WRITE_WAKEUP);
}
}else{
if(!(mdm_status & CyCTS)){
/* !!! cy_stop isn't used because... */
info->tty->stopped = 1;
- base_addr[CySRER] &= ~CyTxMpty;
+ base_addr[CySRER<<index] &= ~CyTxMpty;
}
}
}
@@ -898,16 +936,15 @@
}
}
/* end of service */
- base_addr[CyMIR] = (save_xir & 0x3f);
- base_addr[CyCAR] = save_car;
+ base_addr[CyMIR<<index] = (save_xir & 0x3f);
+ base_addr[CyCAR<<index] = save_car;
}
} /* end while status != 0 */
} /* end loop for chips... */
} while(had_work);
/* clear interrupts */
- card_base_addr = (unsigned char *)cinfo->base_addr;
- vdata = *(card_base_addr + Cy_ClrIntr); /* Cy_ClrIntr is 0x1800 */
+ *(card_base_addr + (Cy_ClrIntr<<index)) = 0; /* Cy_ClrIntr is 0x1800 */
} /* cy_interrupt */
@@ -1025,13 +1062,13 @@
* Delay for 0.1 seconds -- we use a busy loop since this may
* occur during the bootup sequence
*/
- timeout = jiffies+HZ/10;
+ timeout = jiffies+10;
while (timeout >= jiffies)
;
cy_triggered = 0; /* Reset after letting things settle */
- timeout = jiffies+HZ/10;
+ timeout = jiffies+10;
while (timeout >= jiffies)
;
@@ -1052,49 +1089,31 @@
* fool-proof, but it works a large part of the time.
*/
static int
-get_auto_irq(int card)
+get_auto_irq(unsigned char *address)
{
unsigned long timeout;
unsigned char *base_addr;
- int save_xir, save_car;
- volatile char vdata;
+ int index;
- base_addr = (unsigned char*) (cy_card[card].base_addr);
- intr_base_addr = base_addr;
+ index = 0; /* IRQ probing is only for ISA */
+ base_addr = address;
+ intr_base_addr = address;
/*
* Enable interrupts and see who answers
*/
cy_irq_triggered = 0;
cli();
- base_addr[CyCAR] = 0;
- write_cy_cmd(base_addr,CyCHAN_CTL|CyENB_XMTR);
- base_addr[CySRER] |= CyTxMpty;
+ base_addr[CyCAR<<index] = 0;
+ write_cy_cmd(base_addr,CyCHAN_CTL|CyENB_XMTR,index);
+ base_addr[CySRER<<index] |= CyTxMpty;
sti();
- timeout = jiffies+2*HZ/100;
+ timeout = jiffies+2;
while (timeout >= jiffies) {
if (cy_irq_triggered)
break;
}
- /*
- * Now check to see if we got any business, and clean up.
- */
- cli();
- if(intr_base_addr[CySVRR] != 0){
- save_xir = (u_char) intr_base_addr[CyTIR];
- save_car = intr_base_addr[CyCAR];
- if ((save_xir & 0x3) != 0){
- printk("channel %x requesting unexpected interrupt\n",save_xir);
- }
- intr_base_addr[CyCAR] = (save_xir & 0x3);
- intr_base_addr[CySRER] &= ~CyTxMpty;
- intr_base_addr[CyTIR] = (save_xir & 0x3f);
- intr_base_addr[CyCAR] = (save_car);
- vdata = *(intr_base_addr + Cy_ClrIntr); /* Cy_ClrIntr is 0x1800 */
- }
- sti();
-
return(cy_irq_triggered);
} /* get_auto_irq */
@@ -1103,7 +1122,7 @@
* faked out by random interrupts
*/
static int
-do_auto_irq(int card)
+do_auto_irq(unsigned char *address)
{
int irq_lines = 0;
int irq_try_1 = 0, irq_try_2 = 0;
@@ -1119,9 +1138,9 @@
for (retries = 0; retries < 5; retries++) {
if (!irq_try_1)
- irq_try_1 = get_auto_irq(card);
+ irq_try_1 = get_auto_irq(address);
if (!irq_try_2)
- irq_try_2 = get_auto_irq(card);
+ irq_try_2 = get_auto_irq(address);
if (irq_try_1 && irq_try_2) {
if (irq_try_1 == irq_try_2)
break;
@@ -1142,7 +1161,7 @@
{
unsigned long flags;
unsigned char *base_addr;
- int card,chip,channel;
+ int card,chip,channel,index;
if (info->flags & ASYNC_INITIALIZED){
return 0;
@@ -1167,8 +1186,9 @@
channel = (info->line) - (cy_card[card].first_line);
chip = channel>>2;
channel &= 0x03;
+ index = cy_card[card].bus_index;
base_addr = (unsigned char*)
- (cy_card[card].base_addr + cy_chip_offset[chip]);
+ (cy_card[card].base_addr + (cy_chip_offset[chip]<<index));
#ifdef SERIAL_DEBUG_OPEN
printk("startup card %d, chip %d, channel %d, base_addr %lx",
@@ -1176,25 +1196,25 @@
#endif
save_flags(flags); cli();
- base_addr[CyCAR] = (u_char)channel;
+ base_addr[CyCAR<<index] = (u_char)channel;
- base_addr[CyRTPR] = (info->default_timeout
+ base_addr[CyRTPR<<index] = (info->default_timeout
? info->default_timeout
: 0x02); /* 10ms rx timeout */
- write_cy_cmd(base_addr,CyCHAN_CTL|CyENB_RCVR|CyENB_XMTR);
+ write_cy_cmd(base_addr,CyCHAN_CTL|CyENB_RCVR|CyENB_XMTR,index);
- base_addr[CyCAR] = (u_char)channel; /* !!! Is this needed? */
- base_addr[CyMSVR1] = CyRTS;
+ base_addr[CyCAR<<index] = (u_char)channel; /* !!! Is this needed? */
+ base_addr[CyMSVR1<<index] = CyRTS;
/* CP('S');CP('1'); */
- base_addr[CyMSVR2] = CyDTR;
+ base_addr[CyMSVR2<<index] = CyDTR;
#ifdef SERIAL_DEBUG_DTR
printk("cyc: %d: raising DTR\n", __LINE__);
- printk(" status: 0x%x, 0x%x\n", base_addr[CyMSVR1], base_addr[CyMSVR2]);
+ printk(" status: 0x%x, 0x%x\n", base_addr[CyMSVR1<<index], base_addr[CyMSVR2<<index]);
#endif
- base_addr[CySRER] |= CyRxData;
+ base_addr[CySRER<<index] |= CyRxData;
info->flags |= ASYNC_INITIALIZED;
if (info->tty){
@@ -1215,17 +1235,19 @@
{
unsigned long flags;
unsigned char *base_addr;
- int card,chip,channel;
+ int card,chip,channel,index;
card = info->card;
channel = (info->line) - (cy_card[card].first_line);
chip = channel>>2;
channel &= 0x03;
+ index = cy_card[card].bus_index;
base_addr = (unsigned char*)
- (cy_card[card].base_addr + cy_chip_offset[chip]);
+ (cy_card[card].base_addr + (cy_chip_offset[chip]<<index));
+
save_flags(flags); cli();
- base_addr[CyCAR] = channel;
- base_addr[CySRER] |= CyTxMpty;
+ base_addr[CyCAR<<index] = channel;
+ base_addr[CySRER<<index] |= CyTxMpty;
restore_flags(flags);
} /* start_xmit */
@@ -1238,7 +1260,7 @@
{
unsigned long flags;
unsigned char *base_addr;
- int card,chip,channel;
+ int card,chip,channel,index;
if (!(info->flags & ASYNC_INITIALIZED)){
/* CP('$'); */
@@ -1249,8 +1271,9 @@
channel = info->line - cy_card[card].first_line;
chip = channel>>2;
channel &= 0x03;
+ index = cy_card[card].bus_index;
base_addr = (unsigned char*)
- (cy_card[card].base_addr + cy_chip_offset[chip]);
+ (cy_card[card].base_addr + (cy_chip_offset[chip]<<index));
#ifdef SERIAL_DEBUG_OPEN
printk("shutdown card %d, chip %d, channel %d, base_addr %lx\n",
@@ -1269,17 +1292,16 @@
info->xmit_buf = 0;
}
- base_addr[CyCAR] = (u_char)channel;
+ base_addr[CyCAR<<index] = (u_char)channel;
if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) {
- base_addr[CyMSVR1] = ~CyRTS;
-/* CP('C');CP('1'); */
- base_addr[CyMSVR2] = ~CyDTR;
+ base_addr[CyMSVR1<<index] = ~CyRTS;
+ base_addr[CyMSVR2<<index] = ~CyDTR;
#ifdef SERIAL_DEBUG_DTR
printk("cyc: %d: dropping DTR\n", __LINE__);
- printk(" status: 0x%x, 0x%x\n", base_addr[CyMSVR1], base_addr[CyMSVR2]);
+ printk(" status: 0x%x, 0x%x\n", base_addr[CyMSVR1<<index], base_addr[CyMSVR2<<index]);
#endif
}
- write_cy_cmd(base_addr,CyCHAN_CTL|CyDIS_RCVR);
+ write_cy_cmd(base_addr,CyCHAN_CTL|CyDIS_RCVR,index);
/* it may be appropriate to clear _XMIT at
some later date (after testing)!!! */
@@ -1303,7 +1325,7 @@
{
unsigned long flags;
unsigned char *base_addr;
- int card,chip,channel;
+ int card,chip,channel,index;
unsigned cflag;
int i;
@@ -1423,62 +1445,63 @@
channel = (info->line) - (cy_card[card].first_line);
chip = channel>>2;
channel &= 0x03;
+ index = cy_card[card].bus_index;
base_addr = (unsigned char*)
- (cy_card[card].base_addr + cy_chip_offset[chip]);
+ (cy_card[card].base_addr + (cy_chip_offset[chip]<<index));
save_flags(flags); cli();
- base_addr[CyCAR] = (u_char)channel;
+ base_addr[CyCAR<<index] = (u_char)channel;
/* tx and rx baud rate */
- base_addr[CyTCOR] = info->tco;
- base_addr[CyTBPR] = info->tbpr;
- base_addr[CyRCOR] = info->rco;
- base_addr[CyRBPR] = info->rbpr;
+ base_addr[CyTCOR<<index] = info->tco;
+ base_addr[CyTBPR<<index] = info->tbpr;
+ base_addr[CyRCOR<<index] = info->rco;
+ base_addr[CyRBPR<<index] = info->rbpr;
/* set line characteristics according configuration */
- base_addr[CySCHR1] = START_CHAR(info->tty);
- base_addr[CySCHR2] = STOP_CHAR(info->tty);
- base_addr[CyCOR1] = info->cor1;
- base_addr[CyCOR2] = info->cor2;
- base_addr[CyCOR3] = info->cor3;
- base_addr[CyCOR4] = info->cor4;
- base_addr[CyCOR5] = info->cor5;
+ base_addr[CySCHR1<<index] = START_CHAR(info->tty);
+ base_addr[CySCHR2<<index] = STOP_CHAR(info->tty);
+ base_addr[CyCOR1<<index] = info->cor1;
+ base_addr[CyCOR2<<index] = info->cor2;
+ base_addr[CyCOR3<<index] = info->cor3;
+ base_addr[CyCOR4<<index] = info->cor4;
+ base_addr[CyCOR5<<index] = info->cor5;
- write_cy_cmd(base_addr,CyCOR_CHANGE|CyCOR1ch|CyCOR2ch|CyCOR3ch);
+ write_cy_cmd(base_addr,CyCOR_CHANGE|CyCOR1ch|CyCOR2ch|CyCOR3ch,index);
- base_addr[CyCAR] = (u_char)channel; /* !!! Is this needed? */
+ base_addr[CyCAR<<index] = (u_char)channel; /* !!! Is this needed? */
- base_addr[CyRTPR] = (info->default_timeout
+ base_addr[CyRTPR<<index] = (info->default_timeout
? info->default_timeout
: 0x02); /* 10ms rx timeout */
if (C_CLOCAL(info->tty)) {
- base_addr[CySRER] |= 0; /* without modem intr */
+ base_addr[CySRER<<index] |= 0; /* without modem intr */
/* ignore 1->0 modem transitions */
- base_addr[CyMCOR1] = 0x0;
+ base_addr[CyMCOR1<<index] = 0x0;
/* ignore 0->1 modem transitions */
- base_addr[CyMCOR2] = 0x0;
+ base_addr[CyMCOR2<<index] = 0x0;
} else {
- base_addr[CySRER] |= CyMdmCh; /* with modem intr */
+ base_addr[CySRER<<index] |= CyMdmCh; /* with modem intr */
/* act on 1->0 modem transitions */
- base_addr[CyMCOR1] = CyDSR|CyCTS|CyRI|CyDCD;
+ base_addr[CyMCOR1<<index] = CyDSR|CyCTS|CyRI|CyDCD;
/* act on 0->1 modem transitions */
- base_addr[CyMCOR2] = CyDSR|CyCTS|CyRI|CyDCD;
+ base_addr[CyMCOR2<<index] = CyDSR|CyCTS|CyRI|CyDCD;
}
if(i == 0){ /* baud rate is zero, turn off line */
- base_addr[CyMSVR2] = ~CyDTR;
+ base_addr[CyMSVR2<<index] = ~CyDTR;
#ifdef SERIAL_DEBUG_DTR
printk("cyc: %d: dropping DTR\n", __LINE__);
- printk(" status: 0x%x, 0x%x\n", base_addr[CyMSVR1], base_addr[CyMSVR2]);
+ printk(" status: 0x%x, 0x%x\n", base_addr[CyMSVR1<<index], base_addr[CyMSVR2<<index]);
#endif
}else{
- base_addr[CyMSVR2] = CyDTR;
+ base_addr[CyMSVR2<<index] = CyDTR;
#ifdef SERIAL_DEBUG_DTR
printk("cyc: %d: raising DTR\n", __LINE__);
- printk(" status: 0x%x, 0x%x\n", base_addr[CyMSVR1], base_addr[CyMSVR2]);
+ printk(" status: 0x%x, 0x%x\n", base_addr[CyMSVR1<<index], base_addr[CyMSVR2<<index]);
#endif
}
@@ -1526,7 +1549,7 @@
struct cyclades_port *info = (struct cyclades_port *)tty->driver_data;
unsigned long flags;
unsigned char *base_addr;
- int card,chip,channel;
+ int card,chip,channel,index;
#ifdef SERIAL_DEBUG_IO
printk("cy_flush_chars ttyC%d\n", info->line); /* */
@@ -1543,12 +1566,13 @@
channel = info->line - cy_card[card].first_line;
chip = channel>>2;
channel &= 0x03;
+ index = cy_card[card].bus_index;
base_addr = (unsigned char*)
- (cy_card[card].base_addr + cy_chip_offset[chip]);
+ (cy_card[card].base_addr + (cy_chip_offset[chip]<<index));
save_flags(flags); cli();
- base_addr[CyCAR] = channel;
- base_addr[CySRER] |= CyTxMpty;
+ base_addr[CyCAR<<index] = channel;
+ base_addr[CySRER<<index] |= CyTxMpty;
restore_flags(flags);
} /* cy_flush_chars */
@@ -1561,7 +1585,7 @@
*/
static int
cy_write(struct tty_struct * tty, int from_user,
- const unsigned char *buf, int count)
+ unsigned char *buf, int count)
{
struct cyclades_port *info = (struct cyclades_port *)tty->driver_data;
unsigned long flags;
@@ -1682,7 +1706,7 @@
struct cyclades_port *info = (struct cyclades_port *)tty->driver_data;
unsigned long flags;
unsigned char *base_addr;
- int card,chip,channel;
+ int card,chip,channel,index;
#ifdef SERIAL_DEBUG_THROTTLE
char buf[64];
@@ -1705,12 +1729,13 @@
channel = info->line - cy_card[card].first_line;
chip = channel>>2;
channel &= 0x03;
+ index = cy_card[card].bus_index;
base_addr = (unsigned char*)
- (cy_card[card].base_addr + cy_chip_offset[chip]);
+ (cy_card[card].base_addr + (cy_chip_offset[chip]<<index));
save_flags(flags); cli();
- base_addr[CyCAR] = (u_char)channel;
- base_addr[CyMSVR1] = ~CyRTS;
+ base_addr[CyCAR<<index] = (u_char)channel;
+ base_addr[CyMSVR1<<index] = ~CyRTS;
restore_flags(flags);
return;
@@ -1723,7 +1748,7 @@
struct cyclades_port *info = (struct cyclades_port *)tty->driver_data;
unsigned long flags;
unsigned char *base_addr;
- int card,chip,channel;
+ int card,chip,channel,index;
#ifdef SERIAL_DEBUG_THROTTLE
char buf[64];
@@ -1746,12 +1771,13 @@
channel = info->line - cy_card[card].first_line;
chip = channel>>2;
channel &= 0x03;
+ index = cy_card[card].bus_index;
base_addr = (unsigned char*)
- (cy_card[card].base_addr + cy_chip_offset[chip]);
+ (cy_card[card].base_addr + (cy_chip_offset[chip]<<index));
save_flags(flags); cli();
- base_addr[CyCAR] = (u_char)channel;
- base_addr[CyMSVR1] = CyRTS;
+ base_addr[CyCAR<<index] = (u_char)channel;
+ base_addr[CyMSVR1<<index] = CyRTS;
restore_flags(flags);
return;
@@ -1827,7 +1853,7 @@
static int
get_modem_info(struct cyclades_port * info, unsigned int *value)
{
- int card,chip,channel;
+ int card,chip,channel,index;
unsigned char *base_addr;
unsigned long flags;
unsigned char status;
@@ -1837,12 +1863,14 @@
channel = (info->line) - (cy_card[card].first_line);
chip = channel>>2;
channel &= 0x03;
+ index = cy_card[card].bus_index;
base_addr = (unsigned char*)
- (cy_card[card].base_addr + cy_chip_offset[chip]);
+ (cy_card[card].base_addr + (cy_chip_offset[chip]<<index));
save_flags(flags); cli();
- base_addr[CyCAR] = (u_char)channel;
- status = base_addr[CyMSVR1] | base_addr[CyMSVR2];
+ base_addr[CyCAR<<index] = (u_char)channel;
+ status = base_addr[CyMSVR1<<index];
+ status |= base_addr[CyMSVR2<<index];
restore_flags(flags);
result = ((status & CyRTS) ? TIOCM_RTS : 0)
@@ -1851,7 +1879,7 @@
| ((status & CyRI) ? TIOCM_RNG : 0)
| ((status & CyDSR) ? TIOCM_DSR : 0)
| ((status & CyCTS) ? TIOCM_CTS : 0);
- put_user(result,value);
+ put_fs_long(result,(unsigned long *) value);
return 0;
} /* get_modem_info */
@@ -1859,34 +1887,35 @@
set_modem_info(struct cyclades_port * info, unsigned int cmd,
unsigned int *value)
{
- int card,chip,channel;
+ int card,chip,channel,index;
unsigned char *base_addr;
unsigned long flags;
- unsigned int arg = get_user(value);
+ unsigned int arg = get_fs_long((unsigned long *) value);
card = info->card;
channel = (info->line) - (cy_card[card].first_line);
chip = channel>>2;
channel &= 0x03;
+ index = cy_card[card].bus_index;
base_addr = (unsigned char*)
- (cy_card[card].base_addr + cy_chip_offset[chip]);
+ (cy_card[card].base_addr + (cy_chip_offset[chip]<<index));
switch (cmd) {
case TIOCMBIS:
if (arg & TIOCM_RTS){
save_flags(flags); cli();
- base_addr[CyCAR] = (u_char)channel;
- base_addr[CyMSVR1] = CyRTS;
+ base_addr[CyCAR<<index] = (u_char)channel;
+ base_addr[CyMSVR1<<index] = CyRTS;
restore_flags(flags);
}
if (arg & TIOCM_DTR){
save_flags(flags); cli();
- base_addr[CyCAR] = (u_char)channel;
+ base_addr[CyCAR<<index] = (u_char)channel;
/* CP('S');CP('2'); */
- base_addr[CyMSVR2] = CyDTR;
+ base_addr[CyMSVR2<<index] = CyDTR;
#ifdef SERIAL_DEBUG_DTR
printk("cyc: %d: raising DTR\n", __LINE__);
- printk(" status: 0x%x, 0x%x\n", base_addr[CyMSVR1], base_addr[CyMSVR2]);
+ printk(" status: 0x%x, 0x%x\n", base_addr[CyMSVR1<<index], base_addr[CyMSVR2<<index]);
#endif
restore_flags(flags);
}
@@ -1894,18 +1923,18 @@
case TIOCMBIC:
if (arg & TIOCM_RTS){
save_flags(flags); cli();
- base_addr[CyCAR] = (u_char)channel;
- base_addr[CyMSVR1] = ~CyRTS;
+ base_addr[CyCAR<<index] = (u_char)channel;
+ base_addr[CyMSVR1<<index] = ~CyRTS;
restore_flags(flags);
}
if (arg & TIOCM_DTR){
save_flags(flags); cli();
- base_addr[CyCAR] = (u_char)channel;
+ base_addr[CyCAR<<index] = (u_char)channel;
/* CP('C');CP('2'); */
- base_addr[CyMSVR2] = ~CyDTR;
+ base_addr[CyMSVR2<<index] = ~CyDTR;
#ifdef SERIAL_DEBUG_DTR
printk("cyc: %d: dropping DTR\n", __LINE__);
- printk(" status: 0x%x, 0x%x\n", base_addr[CyMSVR1], base_addr[CyMSVR2]);
+ printk(" status: 0x%x, 0x%x\n", base_addr[CyMSVR1<<index], base_addr[CyMSVR2<<index]);
#endif
restore_flags(flags);
}
@@ -1913,33 +1942,33 @@
case TIOCMSET:
if (arg & TIOCM_RTS){
save_flags(flags); cli();
- base_addr[CyCAR] = (u_char)channel;
- base_addr[CyMSVR1] = CyRTS;
+ base_addr[CyCAR<<index] = (u_char)channel;
+ base_addr[CyMSVR1<<index] = CyRTS;
restore_flags(flags);
}else{
save_flags(flags); cli();
- base_addr[CyCAR] = (u_char)channel;
- base_addr[CyMSVR1] = ~CyRTS;
+ base_addr[CyCAR<<index] = (u_char)channel;
+ base_addr[CyMSVR1<<index] = ~CyRTS;
restore_flags(flags);
}
if (arg & TIOCM_DTR){
save_flags(flags); cli();
- base_addr[CyCAR] = (u_char)channel;
+ base_addr[CyCAR<<index] = (u_char)channel;
/* CP('S');CP('3'); */
- base_addr[CyMSVR2] = CyDTR;
+ base_addr[CyMSVR2<<index] = CyDTR;
#ifdef SERIAL_DEBUG_DTR
printk("cyc: %d: raising DTR\n", __LINE__);
- printk(" status: 0x%x, 0x%x\n", base_addr[CyMSVR1], base_addr[CyMSVR2]);
+ printk(" status: 0x%x, 0x%x\n", base_addr[CyMSVR1<<index], base_addr[CyMSVR2<<index]);
#endif
restore_flags(flags);
}else{
save_flags(flags); cli();
- base_addr[CyCAR] = (u_char)channel;
+ base_addr[CyCAR<<index] = (u_char)channel;
/* CP('C');CP('3'); */
- base_addr[CyMSVR2] = ~CyDTR;
+ base_addr[CyMSVR2<<index] = ~CyDTR;
#ifdef SERIAL_DEBUG_DTR
printk("cyc: %d: dropping DTR\n", __LINE__);
- printk(" status: 0x%x, 0x%x\n", base_addr[CyMSVR1], base_addr[CyMSVR2]);
+ printk(" status: 0x%x, 0x%x\n", base_addr[CyMSVR1<<index], base_addr[CyMSVR2<<index]);
#endif
restore_flags(flags);
}
@@ -1977,19 +2006,20 @@
set_threshold(struct cyclades_port * info, unsigned long value)
{
unsigned char *base_addr;
- int card,channel,chip;
+ int card,channel,chip,index;
card = info->card;
channel = info->line - cy_card[card].first_line;
chip = channel>>2;
channel &= 0x03;
+ index = cy_card[card].bus_index;
base_addr = (unsigned char*)
- (cy_card[card].base_addr + cy_chip_offset[chip]);
+ (cy_card[card].base_addr + (cy_chip_offset[chip]<<index));
info->cor3 &= ~CyREC_FIFO;
info->cor3 |= value & CyREC_FIFO;
- base_addr[CyCOR3] = info->cor3;
- write_cy_cmd(base_addr,CyCOR_CHANGE|CyCOR3ch);
+ base_addr[CyCOR3<<index] = info->cor3;
+ write_cy_cmd(base_addr,CyCOR_CHANGE|CyCOR3ch,index);
return 0;
}
@@ -1997,17 +2027,18 @@
get_threshold(struct cyclades_port * info, unsigned long *value)
{
unsigned char *base_addr;
- int card,channel,chip;
+ int card,channel,chip,index;
unsigned long tmp;
card = info->card;
channel = info->line - cy_card[card].first_line;
chip = channel>>2;
channel &= 0x03;
+ index = cy_card[card].bus_index;
base_addr = (unsigned char*)
- (cy_card[card].base_addr + cy_chip_offset[chip]);
+ (cy_card[card].base_addr + (cy_chip_offset[chip]<<index));
- tmp = base_addr[CyCOR3] & CyREC_FIFO;
+ tmp = base_addr[CyCOR3<<index] & CyREC_FIFO;
put_fs_long(tmp,value);
return 0;
}
@@ -2030,16 +2061,17 @@
set_timeout(struct cyclades_port * info, unsigned long value)
{
unsigned char *base_addr;
- int card,channel,chip;
+ int card,channel,chip,index;
card = info->card;
channel = info->line - cy_card[card].first_line;
chip = channel>>2;
channel &= 0x03;
+ index = cy_card[card].bus_index;
base_addr = (unsigned char*)
- (cy_card[card].base_addr + cy_chip_offset[chip]);
+ (cy_card[card].base_addr + (cy_chip_offset[chip]<<index));
- base_addr[CyRTPR] = value & 0xff;
+ base_addr[CyRTPR<<index] = value & 0xff;
return 0;
}
@@ -2047,17 +2079,18 @@
get_timeout(struct cyclades_port * info, unsigned long *value)
{
unsigned char *base_addr;
- int card,channel,chip;
+ int card,channel,chip,index;
unsigned long tmp;
card = info->card;
channel = info->line - cy_card[card].first_line;
chip = channel>>2;
channel &= 0x03;
+ index = cy_card[card].bus_index;
base_addr = (unsigned char*)
- (cy_card[card].base_addr + cy_chip_offset[chip]);
+ (cy_card[card].base_addr + (cy_chip_offset[chip]<<index));
- tmp = base_addr[CyRTPR];
+ tmp = base_addr[CyRTPR<<index];
put_fs_long(tmp,value);
return 0;
}
@@ -2175,8 +2208,8 @@
ret_val = error;
break;
}
- put_user(C_CLOCAL(tty) ? 1 : 0,
- (unsigned int *) arg);
+ put_fs_long(C_CLOCAL(tty) ? 1 : 0,
+ (unsigned long *) arg);
break;
case TIOCSSOFTCAR:
arg = get_fs_long((unsigned long *) arg);
@@ -2283,8 +2316,6 @@
printk("cyc: %d: decrementing count to %d\n", __LINE__, info->count - 1);
#endif
if (--info->count < 0) {
- printk("cy_close: bad serial port count for ttys%d: %d\n",
- info->line, info->count);
#ifdef SERIAL_DEBUG_COUNT
printk("cyc: %d: setting count to 0\n", __LINE__);
#endif
@@ -2302,7 +2333,7 @@
if (info->flags & ASYNC_CALLOUT_ACTIVE)
info->callout_termios = *tty->termios;
if (info->flags & ASYNC_INITIALIZED)
- tty_wait_until_sent(tty, 30*HZ); /* 30 seconds timeout */
+ tty_wait_until_sent(tty, 3000); /* 30 seconds timeout */
shutdown(info);
if (tty->driver.flush_buffer)
tty->driver.flush_buffer(tty);
@@ -2380,7 +2411,7 @@
struct wait_queue wait = { current, NULL };
struct cyclades_card *cinfo;
unsigned long flags;
- int chip, channel;
+ int chip, channel,index;
int retval;
char *base_addr;
@@ -2454,18 +2485,19 @@
channel = info->line - cinfo->first_line;
chip = channel>>2;
channel &= 0x03;
- base_addr = (char *) (cinfo->base_addr + cy_chip_offset[chip]);
+ index = cinfo->bus_index;
+ base_addr = (char *) (cinfo->base_addr + (cy_chip_offset[chip]<<index));
while (1) {
save_flags(flags); cli();
if (!(info->flags & ASYNC_CALLOUT_ACTIVE)){
- base_addr[CyCAR] = (u_char)channel;
- base_addr[CyMSVR1] = CyRTS;
+ base_addr[CyCAR<<index] = (u_char)channel;
+ base_addr[CyMSVR1<<index] = CyRTS;
/* CP('S');CP('4'); */
- base_addr[CyMSVR2] = CyDTR;
+ base_addr[CyMSVR2<<index] = CyDTR;
#ifdef SERIAL_DEBUG_DTR
printk("cyc: %d: raising DTR\n", __LINE__);
- printk(" status: 0x%x, 0x%x\n", base_addr[CyMSVR1], base_addr[CyMSVR2]);
+ printk(" status: 0x%x, 0x%x\n", base_addr[CyMSVR1<<index], base_addr[CyMSVR2<<index]);
#endif
}
restore_flags(flags);
@@ -2480,12 +2512,12 @@
break;
}
save_flags(flags); cli();
- base_addr[CyCAR] = (u_char)channel;
-/* CP('L');CP1(1 && C_CLOCAL(tty)); CP1(1 && (base_addr[CyMSVR1] & CyDCD) ); */
+ base_addr[CyCAR<<index] = (u_char)channel;
+/* CP('L');CP1(1 && C_CLOCAL(tty)); CP1(1 && (base_addr[CyMSVR1<<index] & CyDCD) ); */
if (!(info->flags & ASYNC_CALLOUT_ACTIVE)
&& !(info->flags & ASYNC_CLOSING)
&& (C_CLOCAL(tty)
- || (base_addr[CyMSVR1] & CyDCD))) {
+ || (base_addr[CyMSVR1<<index] & CyDCD))) {
restore_flags(flags);
break;
}
@@ -2617,20 +2649,19 @@
/* initialize chips on card -- return number of valid
chips (which is number of ports/4) */
int
-cy_init_card(unsigned char *true_base_addr)
+cy_init_card(unsigned char *true_base_addr,int index)
{
- volatile unsigned short discard;
unsigned int chip_number;
unsigned char* base_addr;
- discard = true_base_addr[Cy_HwReset]; /* Cy_HwReset is 0x1400 */
- discard = true_base_addr[Cy_ClrIntr]; /* Cy_ClrIntr is 0x1800 */
+ true_base_addr[Cy_HwReset<<index] = 0; /* Cy_HwReset is 0x1400 */
+ true_base_addr[Cy_ClrIntr<<index] = 0; /* Cy_ClrIntr is 0x1800 */
udelay(500L);
for(chip_number=0; chip_number<CyMaxChipsPerCard; chip_number++){
- base_addr = true_base_addr + cy_chip_offset[chip_number];
+ base_addr = true_base_addr + (cy_chip_offset[chip_number]<<index);
udelay(1000L);
- if(base_addr[CyCCR] != 0x00){
+ if(base_addr[CyCCR<<index] != 0x00){
/*************
printk(" chip #%d at %#6lx is never idle (CCR != 0)\n",
chip_number, (unsigned long)base_addr);
@@ -2638,7 +2669,7 @@
return chip_number;
}
- base_addr[CyGFRCR] = 0;
+ base_addr[CyGFRCR<<index] = 0;
udelay(10L);
/* The Cyclom-16Y does not decode address bit 9 and therefore
@@ -2652,25 +2683,31 @@
return chip_number;
}
- base_addr[CyCCR] = CyCHIP_RESET;
+ base_addr[CyCCR<<index] = CyCHIP_RESET;
udelay(1000L);
- if(base_addr[CyGFRCR] == 0x00){
+ if(base_addr[CyGFRCR<<index] == 0x00){
+ /*
printk(" chip #%d at %#6lx is not responding (GFRCR stayed 0)\n",
chip_number, (unsigned long)base_addr);
+ */
return chip_number;
}
- if((0xf0 & base_addr[CyGFRCR]) != 0x40){
+ if((0xf0 & base_addr[CyGFRCR<<index]) != 0x40){
+ /*
printk(" chip #%d at %#6lx is not valid (GFRCR == %#2x)\n",
- chip_number, (unsigned long)base_addr, base_addr[CyGFRCR]);
+ chip_number, (unsigned long)base_addr, base_addr[CyGFRCR<<index]);
+ */
return chip_number;
}
- base_addr[CyGCR] = CyCH0_SERIAL;
- base_addr[CyPPR] = 244; /* better value than CyCLOCK_25_1MS * 5
+ base_addr[CyGCR<<index] = CyCH0_SERIAL;
+ base_addr[CyPPR<<index] = 244; /* better value than CyCLOCK_25_1MS * 5
to run clock at 200 Hz */
+ /*
printk(" chip #%d at %#6lx is rev 0x%2x\n",
- chip_number, (unsigned long)base_addr, base_addr[CyGFRCR]);
+ chip_number, (unsigned long)base_addr, base_addr[CyGFRCR<<index]);
+ */
}
return chip_number;
@@ -2695,15 +2732,9 @@
long
cy_init(long kmem_start)
{
- struct cyclades_port *info;
+ struct cyclades_port *info;
struct cyclades_card *cinfo;
- int good_ports = 0;
- int port_num = 0;
- int index;
-#ifdef notyet
- struct sigaction sa;
-#endif
- int retval;
+ int board,port,i;
scrn[1] = '\0';
show_version();
@@ -2759,50 +2790,54 @@
bh_base[CYCLADES_BH].routine = do_cyclades_bh;
enable_bh(CYCLADES_BH);
- for (index = 0; index < 16; index++) {
- IRQ_cards[index] = 0;
+ for (i = 0; i < 16; i++) {
+ IRQ_cards[i] = 0;
}
- port_num = 0;
- info = cy_port;
- for (index = 0, cinfo = cy_card; index < NR_CARDS; index++,cinfo++) {
- /*** initialize card ***/
- if(0 == (cinfo->num_chips =
- cy_init_card((unsigned char *)cinfo->base_addr))){
- /* this card is not present */
- continue;
- }
-
-#ifndef CY_DONT_PROBE
- /* find out the board's irq by probing */
- cinfo->irq = do_auto_irq(index);
-#endif
-
- /** bind IRQ to card **/
- if (cinfo->irq) {
- retval = request_irq(cinfo->irq, cy_interrupt,
- SA_INTERRUPT, "cyclades");
- if (retval){
- printk("request_irq returned %d\n",retval);
- /* return retval; */
- }
- IRQ_cards[cinfo->irq] = cinfo;
- }else{
- printk("couldn't get board's irq\n");
- continue;
- }
+ for (i = 0; i < NR_CARDS; i++) {
+ /* base_addr=0 indicates board not found */
+ cy_card[i].base_addr = 0;
+ }
+
+ /* the code below is responsible to find the boards. Each different
+ type of board has its own detection routine. If a board is found,
+ the next cy_card structure available is set by the detection
+ routine. These funcions are responsible for checking the availability
+ of cy_card and cy_port data structures and updating the
+ cy_next_channel. */
+
+ /* look for isa boards */
+ cy_isa_nboard = cy_detect_isa();
+
+ /* look for pci boards */
+ cy_pci_nboard = cy_detect_pci();
- printk(" share IRQ %d: ", cinfo->irq);
- good_ports = 4 * cinfo->num_chips;
+ cy_nboard = cy_isa_nboard + cy_pci_nboard;
- if(port_num < NR_PORTS){
- cinfo->first_line = port_num;
- while( good_ports-- && port_num < NR_PORTS){
- /*** initialize port ***/
+ /* invalidate remaining cy_card structures */
+ for (i = 0 ; i < NR_CARDS ; i++) {
+ if (cy_card[i].base_addr == 0) {
+ cy_card[i].first_line = -1;
+ }
+ }
+ /* invalidate remaining cy_port structures */
+ for (i = cy_next_channel ; i < NR_PORTS ; i++) {
+ cy_port[i].line = -1;
+ cy_port[i].magic = -1;
+ }
+
+ /* initialize per-port data structures for each valid board found */
+ for (board = 0 ; board < cy_nboard ; board++) {
+ cinfo = &cy_card[board];
+ for (port = cinfo->first_line ;
+ port < cinfo->first_line + 4*cinfo->num_chips ;
+ port++)
+ {
+ info = &cy_port[port];
info->magic = CYCLADES_MAGIC;
info->type = PORT_CIRRUS;
- info->card = index;
- info->line = port_num;
+ info->card = board;
+ info->line = port;
info->flags = STD_COM_FLAGS;
info->tty = 0;
info->xmit_fifo_size = 12;
@@ -2833,41 +2868,208 @@
info->close_wait = 0;
/* info->session */
/* info->pgrp */
-/*** !!!!!!!! this may expose new bugs !!!!!!!!! *********/
info->read_status_mask = CyTIMEOUT| CySPECHAR| CyBREAK
| CyPARITY| CyFRAME| CyOVERRUN;
/* info->timeout */
-
- printk("ttyC%1d ", info->line);
- port_num++;info++;
- if(!(port_num & 7)){
- printk("\n ");
- }
}
- }else{
- cinfo->first_line = -1;
- }
- printk("\n");
- }
- while( port_num < NR_PORTS){
- info->line = -1;
- port_num++;info++;
}
return kmem_start;
} /* cy_init */
+/*
+ * ---------------------------------------------------------------------
+ * cy_detect_isa() - Probe for Cyclom-Y/ISA boards.
+ * sets global variables and return the number of ISA boards found.
+ * ---------------------------------------------------------------------
+ */
+int
+cy_detect_isa()
+{
+ unsigned int cy_isa_irq,nboard;
+ unsigned char *cy_isa_address;
+ unsigned short i,j,cy_isa_nchan;
+
+ nboard = 0;
+
+ /* scan the address table probing for Cyclom-Y/ISA boards */
+ for (i = 0 ; i < NR_ISA_ADDRESSES ; i++) {
+ cy_isa_address = cy_isa_addresses[i];
+ if (cy_isa_address == 0x0000) {
+ return(nboard);
+ }
+
+ /* probe for CD1400... */
+ cy_isa_nchan = 4 * cy_init_card(cy_isa_address,0);
+ if (cy_isa_nchan == 0) {
+ continue;
+ }
+
+ /* find out the board's irq by probing */
+ cy_isa_irq = do_auto_irq(cy_isa_address);
+ if (cy_isa_irq == 0) {
+ printk("Cyclom-Y/ISA found at 0x%x but the IRQ could not be detected.\n",
+ (unsigned int) cy_isa_address);
+ continue;
+ }
+
+ if((cy_next_channel+cy_isa_nchan) > NR_PORTS) {
+ printk("Cyclom-Y/ISA found at 0x%x but no more channel structures are available.\n",
+ (unsigned int) cy_isa_address);
+ return(nboard);
+ }
+ /* fill the next cy_card structure available */
+ for (j = 0 ; j < NR_CARDS ; j++) {
+ if (cy_card[j].base_addr == 0) break;
+ }
+ if (j == NR_CARDS) { /* no more cy_cards available */
+ printk("Cyclom-Y/ISA found at 0x%x but no more card structures are available.\n",
+ (unsigned int) cy_isa_address);
+ return(nboard);
+ }
+
+ /* allocate IRQ */
+ if(request_irq(cy_isa_irq,cy_interrupt,SA_INTERRUPT,"cyclades"))
+ {
+ printk("Cyclom-Y/ISA found at 0x%x but could not allocate interrupt IRQ#%d.\n",
+ (unsigned int) cy_isa_address,cy_isa_irq);
+ return(nboard);
+ }
+
+ /* set cy_card */
+ cy_card[j].base_addr = (int) cy_isa_address;
+ cy_card[j].irq = (int) cy_isa_irq;
+ cy_card[j].bus_index = 0;
+ cy_card[j].first_line = cy_next_channel;
+ cy_card[j].num_chips = cy_isa_nchan/4;
+ IRQ_cards[cy_isa_irq] = &cy_card[j];
+ nboard++;
+
+ /* print message */
+ printk("Cyclom-Y/ISA #%d: 0x%x-0x%x, IRQ%d, %d channels starting from port %d.\n",
+ j+1,(unsigned int) cy_isa_address,
+ (unsigned int)(cy_isa_address + 0x1fff),
+ cy_isa_irq,cy_isa_nchan,cy_next_channel);
+ cy_next_channel += cy_isa_nchan;
+ }
+ return(nboard);
+
+}
+
+/*
+ * ---------------------------------------------------------------------
+ * cy_detect_pci() - Test PCI bus presence and Cyclom-Ye/PCI.
+ * sets global variables and return the number of PCI boards found.
+ * ---------------------------------------------------------------------
+ */
+int
+cy_detect_pci()
+{
+ unsigned char cyy_bus, cyy_dev_fn, cyy_rev_id;
+ unsigned long pci_intr_ctrl;
+ unsigned char cy_pci_irq;
+ unsigned long cy_pci_address,cy_pci_io;
+ unsigned short i,j,cy_pci_nchan;
+
+#ifndef CONFIG_PCI
+ printk ("Kernel without PCI support.\n");
+ return(0);
+#else
+ if(pcibios_present() == 0) { /* PCI bus not present */
+ return(0);
+ }
+ for (i = 0; i < NR_CARDS; i++) {
+ /* look for a Cyclom-Y card by vendor and device id */
+ if(pcibios_find_device (PCI_VENDOR_ID_CYCLADES,
+ PCI_DEVICE_ID_CYCLOMY,i,
+ &cyy_bus, &cyy_dev_fn) != 0)
+ {
+ break;
+ }
+ /* read PCI configuration area */
+ pcibios_read_config_byte(cyy_bus, cyy_dev_fn,
+ PCI_INTERRUPT_LINE, &cy_pci_irq);
+ pcibios_read_config_dword(cyy_bus, cyy_dev_fn,
+ PCI_BASE_ADDRESS_1, &cy_pci_io);
+ pcibios_read_config_dword(cyy_bus, cyy_dev_fn,
+ PCI_BASE_ADDRESS_2, &cy_pci_address);
+ pcibios_read_config_byte(cyy_bus, cyy_dev_fn,
+ PCI_REVISION_ID, &cyy_rev_id);
+ cy_pci_address &= 0xfffffff0;
+ cy_pci_io &= 0xfffffffc;
+ cy_pci_nchan = 4 * cy_init_card((unsigned char *)
+ cy_pci_address,1);
+ if(cy_pci_nchan == 0) {
+ printk("Cyclom-Y PCI host card with no Serial-Modules at 0x%x.\n",
+ (unsigned int) cy_pci_address);
+ continue;
+ }
+ if((cy_next_channel+cy_pci_nchan) > NR_PORTS) {
+ printk("Cyclom-Y/PCI found at 0x%x but no more channel structures are available.\n",
+ (unsigned int) cy_pci_address);
+ return(i);
+ }
+#ifdef CY_PCI_DEBUG
+ printk("Cyclom-Ye/PCI #%d (bus=0x0%x, pci_id=0x%x, rev_id=%d).\n",
+ i+1,cyy_bus,cyy_dev_fn,cyy_rev_id);
+ printk("Cyclom-Ye/PCI: found at 0x%x, IRQ%d, ioaddr = 0x%lx.\n",
+ cy_pci_address,(int)cy_pci_irq,cy_pci_io);
+#endif
+ /* fill the next cy_card structure available */
+ for (j = 0 ; j < NR_CARDS ; j++) {
+ if (cy_card[j].base_addr == 0) break;
+ }
+ if (j == NR_CARDS) { /* no more cy_cards available */
+ printk("Cyclom-Y/PCI found at 0x%x but no more card structures are available.\n",
+ (unsigned int) cy_pci_address);
+ return(i);
+ }
+
+ /* allocate IRQ */
+ if(request_irq(cy_pci_irq,cy_interrupt,SA_INTERRUPT,"cyclades"))
+ {
+ printk("Cyclom-Y/PCI found at 0x%x but could not allocate interrupt IRQ%d.\n",
+ (unsigned int) cy_pci_address,cy_pci_irq);
+ return(i);
+ }
+
+ /* set cy_card */
+ cy_card[j].base_addr = (int) cy_pci_address;
+ cy_card[j].irq = (int) cy_pci_irq;
+ cy_card[j].bus_index = 1;
+ cy_card[j].first_line = cy_next_channel;
+ cy_card[j].num_chips = cy_pci_nchan/4;
+ IRQ_cards[cy_pci_irq] = &cy_card[j];
+
+ /* enable interrupts in the PCI interface */
+ outw(inw(cy_pci_io+0x68)|0x0900,cy_pci_io+0x68);
+ pci_intr_ctrl = (unsigned long)(inw(cy_pci_io+0x68) | inw(cy_pci_io+0x6a)<<16);
+
+ /* print message */
+ printk("Cyclom-Y/PCI #%d: 0x%x-0x%x, IRQ%d, %d channels starting from port %d.\n",
+ j+1,(unsigned int) cy_pci_address,
+ (unsigned int)(cy_pci_address + 0x3fff),
+ (int)cy_pci_irq,cy_pci_nchan,cy_next_channel);
+
+ cy_next_channel += cy_pci_nchan;
+ }
+ return(i);
+#endif /* ifndef CONFIG_PCI */
+}
+
+
#ifdef CYCLOM_SHOW_STATUS
static void
show_status(int line_num)
{
unsigned char *base_addr;
- int card,chip,channel;
+ int card,chip,channel,index;
struct cyclades_port * info;
unsigned long flags;
info = &cy_port[line_num];
card = info->card;
+ index = cy_card[card].bus_index;
channel = (info->line) - (cy_card[card].first_line);
chip = channel>>2;
channel &= 0x03;
@@ -2899,58 +3101,58 @@
save_flags(flags); cli();
base_addr = (unsigned char*)
- (cy_card[card].base_addr + cy_chip_offset[chip]);
+ (cy_card[card].base_addr + (cy_chip_offset[chip]<<index));
/* Global Registers */
- printk(" CyGFRCR %x\n", base_addr[CyGFRCR]);
- printk(" CyCAR %x\n", base_addr[CyCAR]);
- printk(" CyGCR %x\n", base_addr[CyGCR]);
- printk(" CySVRR %x\n", base_addr[CySVRR]);
- printk(" CyRICR %x\n", base_addr[CyRICR]);
- printk(" CyTICR %x\n", base_addr[CyTICR]);
- printk(" CyMICR %x\n", base_addr[CyMICR]);
- printk(" CyRIR %x\n", base_addr[CyRIR]);
- printk(" CyTIR %x\n", base_addr[CyTIR]);
- printk(" CyMIR %x\n", base_addr[CyMIR]);
- printk(" CyPPR %x\n", base_addr[CyPPR]);
+ printk(" CyGFRCR %x\n", base_addr[CyGFRCR<<index]);
+ printk(" CyCAR %x\n", base_addr[CyCAR<<index]);
+ printk(" CyGCR %x\n", base_addr[CyGCR<<index]);
+ printk(" CySVRR %x\n", base_addr[CySVRR<<index]);
+ printk(" CyRICR %x\n", base_addr[CyRICR<<index]);
+ printk(" CyTICR %x\n", base_addr[CyTICR<<index]);
+ printk(" CyMICR %x\n", base_addr[CyMICR<<index]);
+ printk(" CyRIR %x\n", base_addr[CyRIR<<index]);
+ printk(" CyTIR %x\n", base_addr[CyTIR<<index]);
+ printk(" CyMIR %x\n", base_addr[CyMIR<<index]);
+ printk(" CyPPR %x\n", base_addr[CyPPR<<index]);
- base_addr[CyCAR] = (u_char)channel;
+ base_addr[CyCAR<<index] = (u_char)channel;
/* Virtual Registers */
- printk(" CyRIVR %x\n", base_addr[CyRIVR]);
- printk(" CyTIVR %x\n", base_addr[CyTIVR]);
- printk(" CyMIVR %x\n", base_addr[CyMIVR]);
- printk(" CyMISR %x\n", base_addr[CyMISR]);
+ printk(" CyRIVR %x\n", base_addr[CyRIVR<<index]);
+ printk(" CyTIVR %x\n", base_addr[CyTIVR<<index]);
+ printk(" CyMIVR %x\n", base_addr[CyMIVR<<index]);
+ printk(" CyMISR %x\n", base_addr[CyMISR<<index]);
/* Channel Registers */
- printk(" CyCCR %x\n", base_addr[CyCCR]);
- printk(" CySRER %x\n", base_addr[CySRER]);
- printk(" CyCOR1 %x\n", base_addr[CyCOR1]);
- printk(" CyCOR2 %x\n", base_addr[CyCOR2]);
- printk(" CyCOR3 %x\n", base_addr[CyCOR3]);
- printk(" CyCOR4 %x\n", base_addr[CyCOR4]);
- printk(" CyCOR5 %x\n", base_addr[CyCOR5]);
- printk(" CyCCSR %x\n", base_addr[CyCCSR]);
- printk(" CyRDCR %x\n", base_addr[CyRDCR]);
- printk(" CySCHR1 %x\n", base_addr[CySCHR1]);
- printk(" CySCHR2 %x\n", base_addr[CySCHR2]);
- printk(" CySCHR3 %x\n", base_addr[CySCHR3]);
- printk(" CySCHR4 %x\n", base_addr[CySCHR4]);
- printk(" CySCRL %x\n", base_addr[CySCRL]);
- printk(" CySCRH %x\n", base_addr[CySCRH]);
- printk(" CyLNC %x\n", base_addr[CyLNC]);
- printk(" CyMCOR1 %x\n", base_addr[CyMCOR1]);
- printk(" CyMCOR2 %x\n", base_addr[CyMCOR2]);
- printk(" CyRTPR %x\n", base_addr[CyRTPR]);
- printk(" CyMSVR1 %x\n", base_addr[CyMSVR1]);
- printk(" CyMSVR2 %x\n", base_addr[CyMSVR2]);
- printk(" CyRBPR %x\n", base_addr[CyRBPR]);
- printk(" CyRCOR %x\n", base_addr[CyRCOR]);
- printk(" CyTBPR %x\n", base_addr[CyTBPR]);
- printk(" CyTCOR %x\n", base_addr[CyTCOR]);
+ printk(" CyCCR %x\n", base_addr[CyCCR<<index]);
+ printk(" CySRER %x\n", base_addr[CySRER<<index]);
+ printk(" CyCOR1 %x\n", base_addr[CyCOR1<<index]);
+ printk(" CyCOR2 %x\n", base_addr[CyCOR2<<index]);
+ printk(" CyCOR3 %x\n", base_addr[CyCOR3<<index]);
+ printk(" CyCOR4 %x\n", base_addr[CyCOR4<<index]);
+ printk(" CyCOR5 %x\n", base_addr[CyCOR5<<index]);
+ printk(" CyCCSR %x\n", base_addr[CyCCSR<<index]);
+ printk(" CyRDCR %x\n", base_addr[CyRDCR<<index]);
+ printk(" CySCHR1 %x\n", base_addr[CySCHR1<<index]);
+ printk(" CySCHR2 %x\n", base_addr[CySCHR2<<index]);
+ printk(" CySCHR3 %x\n", base_addr[CySCHR3<<index]);
+ printk(" CySCHR4 %x\n", base_addr[CySCHR4<<index]);
+ printk(" CySCRL %x\n", base_addr[CySCRL<<index]);
+ printk(" CySCRH %x\n", base_addr[CySCRH<<index]);
+ printk(" CyLNC %x\n", base_addr[CyLNC<<index]);
+ printk(" CyMCOR1 %x\n", base_addr[CyMCOR1<<index]);
+ printk(" CyMCOR2 %x\n", base_addr[CyMCOR2<<index]);
+ printk(" CyRTPR %x\n", base_addr[CyRTPR<<index]);
+ printk(" CyMSVR1 %x\n", base_addr[CyMSVR1<<index]);
+ printk(" CyMSVR2 %x\n", base_addr[CyMSVR2<<index]);
+ printk(" CyRBPR %x\n", base_addr[CyRBPR<<index]);
+ printk(" CyRCOR %x\n", base_addr[CyRCOR<<index]);
+ printk(" CyTBPR %x\n", base_addr[CyTBPR<<index]);
+ printk(" CyTCOR %x\n", base_addr[CyTCOR<<index]);
restore_flags(flags);
} /* show_status */
FUNET's LINUX-ADM group, [email protected]
TCL-scripts by Sam Shen, [email protected]
with Sam's (original) version of this