/* ------------------------------------------------------------------------ digiDload.c Copyright (C) 1996 Digi International. For technical support please email digiLnux@dgii.com or call Digi tech support at (612) 943-0577 This code represents our first attempt at providing a universal code downloader for our products. As we decide to support upcoming products, this code will be modified to provide that support. It is our hope that much of the card specifics can be abstracted out of the kernel and into this application. This will allow for easier development as well as debug, and enable longer release schedules on the driver itself (Since it will have to be changed less often). This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. Change history: V1.3.6 October 1998: PEB -- Added code (ifdef'd to PCIBUG) to circumvent a problem with some PCI chipsets. -- Fixed a bug that kept multiple ISA cards from initializing. -- Previous versions would quit on any error. In a multiple adapter setup a failure to initialize a prior board would also keep subsequent boards from being initialized. This version displays error messages as appropriate, but then continues trying to initialize any subsequent boards. -- Changed makefile to link digiDload using the static option so it will run even on platforms with incompatible libraries. -- Fixed a include file references that prevented compiling digiDload against newer Linux source trees. -- Added messages to display the adapter type and number of ports as they are initialized. This is especially useful in the event that some adapters succeed while others fail to initialize. V1.3.7 January 1999: PEB -- Removed -D__KERNEL__ from Makefile -- unnecessary and it caused compile failures with 2.1.x kernels. -- Added CX initialization. Much of this actually belongs in the driver, but it was easier to develop it here first. -- Added PCI 2R-290 support. -- Added init() time call to memoff() to turn off card memory on non-PCI cards to avoid conflicts with shared memory addresses. V1.3.8 February 2, 1999: PEB -- Removed CX concentrator upload code since that's now taken care of in the driver itself (epca.c) -- Add call to DIGI_DISABLE before initializing cards. Otherwise, subsequent calls to digiDload fail with CX cards since download requests from the card are misinterpreted. V1.3.9 February 3, 1999: PEB -- Add support for multiple CX configurations via "cxconf" and "/etc/digi/cxconf.dat" V1.3.10 February 4, 1999: PEB -- Removed debugging printfs V1.3.11 February 5, 1999: PEB -- Added report_numports() and call to it following DIGI_INIT calls to report "numports" for each card. This is necessary because these values cannot be read from PCI cards until then. V1.3.12 February 25, 1999: PEB -- Added calls to new ioctls: DIGI_CXCON_INIT and DIGI_CXCON_NEXT to upload C/X concentrator binaries from digiDload's user space to the driver (the driver then takes care of getting this to the card as needed). Uploading the concentrator image to the card cannot be digiDload's responsibility since the concentrators may call for image refreshes long after digiDload exits (e.g: if the concentrators are power cycled). V1.3.13 March 9, 1999: PEB -- Added PCI XR_422 support V1.3.14 April 16, 1999: PEB -- Removed the now-extraneous references to PCIXR_REVD V1.3.15 May 24, 1999: CWS -- Made digiDload aware of the EPC/Jupiter board (type 17, PCIEPCJ) -- Added calls to new ioctls: DIGI_EPCXCON_INIT and DIGI_EPCXCON_NEXT to upload EPC/X concentrator binary from digiDload's user space to the driver (the driver then takes care of getting this to the card as needed). -- Added new Concentrator image /etc/digi/fxcon.bin, to deal with EPC/X concentrators. EPC/X concentrators only work on EPC/X cards, but C/X concentrators *AND* EPC/X concentrators are supported on the PCI EPC/X Jupiter card. -- Added debug code ifdef'd to CWSDEBUG V1.3.16 May 27, 1999: CWS/PEB -- In cxmagic(), needed to increment cxindex after each magic string load otherwise all CX's get the same config string. -- Added EPC/X concentrator configuration support to getcxconf() and cxmagic(). CX and EPC/X concentrator topology information now comes from "/etc/digi/cxconf.dat" and "/etc/digi/epcxconf.dat" respectively. V1.3.17 June 9, 1999: CWS/PEB -- In 1.3.16, CWSDEBUG was left on by default. Fixed that. V1.3.18 June 15, 1999: PEB -- Only load C/X and/or EPC/X concentrator images if needed (i.e: if C/X and/or EPC/X adapters have been found). V1.3.19 July 1, 1999: PEB -- Ooops! C/CON modules can be used with either C/X or EPC/X adapters but V1.3.18 over-cleverly uploads the C/CON image only if a C/X adapter is present. Fixed in V1.3.19. --------------------------------------------------------------------------- */ /* Some PCI Chipsets fail on some multi-byte (e.g: 32-bit) transfers. If * PCIBUG is defined here, we will use bytewise transfers (via slow_memcpy()) * rather than using calls to memcpy(). */ #define PCIBUG //#define CWSDEBUG typedef unsigned char unchar; extern int iopl( int level ); #include #include #include #include #include #include #include #include #include #include #include #include /* ---------------------------------------------------------------- NOTE: In asm/io.h outb seems to have its aruments reversed from the norm. ------------------------------------------------------------------- */ #include //#include #include #include #include #include #include #define TRUE 1 #define FALSE 0 #define BIOS 1 #define FEP 2 #define MAXCARDS 7 /* C/X concentrator download image */ char *cxcon_fname = "/etc/digi/cxcon.bin"; char *epcxcon_fname = "/etc/digi/fxcon.bin"; /* C/X concentrator configuration */ #define CXCONFIG "/etc/digi/cxconf.dat" #define EPCXCONFIG "/etc/digi/epcxconf.dat" /* CX Concentrator configuration strings can be a maximum of 21 bytes long */ #define CXCFGLEN 21 unsigned char cx_cfg[MAXCARDS][CXCFGLEN]; unsigned char epcx_cfg[MAXCARDS][CXCFGLEN]; /* How many CX configuration images have we found? */ unsigned int no_cximages = 0; unsigned int no_epcximages = 0; /* Which CX configuration image should we use next? */ unsigned int cxindex = 0; unsigned int epcxindex = 0; /* There should be a way to interrogate board for this */ #define RAM_SIZE 32768 /* ---------------- Begin function prototypes ------------------ */ void mess(int err, int indent, const char * format, ...); void nap(int); int init(int digiFD); void memoff(int card); /*void memon(int card);*/ int reset_cards(int); int binary_is_running(int card, int); int wait_for_binary(int card, int); int load_binary(int card, int); unsigned long int readRAM(int, int, int, int); void writeRAM(int, long, int, int, int); /* ---------------- End function prototypes ------------------ */ struct file_map { int board_type; /* What card does this go on ? */ int binary_type; /* Is it a BIOS, a FEP, or a CONC ? */ char *fname; /* Name of binary in filesystem. */ unsigned long addr; /* Where the binary goes. */ }; struct board_t { volatile char * ram_addr; volatile char * control_addr; struct digi_info digi_info; }; struct board_t board[MAXCARDS] ; /* ---------------------------------------------------- Note: PCXE and PCXEVE share the same binaries. Null filenames mean we are at end of list. ------------------------------------------------------- */ struct file_map file_map[] = { {PCXEM, BIOS, "sxbios.bin", 0x1000}, {PCXEM, FEP, "sxfep.bin", 0x1000}, {PCXR, BIOS, "xrbios.bin", 0x1000}, {PCXR, FEP, "xrfep.bin", 0x1000}, {EISAXEM, BIOS, "sxbios.bin", 0x1000}, {EISAXEM, FEP, "sxfep.bin", 0x1000}, {PCIXR, BIOS, "xrbios.bin", 0x1000}, {PCIXR, FEP, "xrfep.bin", 0x1000}, {PCIXRJ, BIOS, "xrbios.bin", 0x1000}, {PCIXRJ, FEP, "xrfep.bin", 0x1000}, {PCIXEM, BIOS, "sxbios.bin", 0x1000}, {PCIXEM, FEP, "sxfep.bin", 0x1000}, {PCXE, BIOS, "xxbios.bin", 0x1800}, {PCXE, FEP, "xxfep.bin", 0x0}, {PCXEVE, BIOS, "xxbios.bin", 0x1800}, {PCXEVE, FEP, "xxfep.bin", 0x0}, {PCXI, BIOS, "xxbios.bin", 0xFF800}, {PCXI, FEP, "xxfep.bin", 0x2000}, {PC64XE, BIOS, "xxbios.bin", 0xFF800}, {PC64XE, FEP, "xxfep.bin", 0x2000}, {PCI920_4, BIOS, "xrbios.bin", 0x1000}, {PCI920_4, FEP, "xrfep.bin", 0x1000}, {PCI920_8, BIOS, "xrbios.bin", 0x1000}, {PCI920_8, FEP, "xrfep.bin", 0x1000}, {PCI920_2, BIOS, "xrbios.bin", 0x1000}, {PCI920_2, FEP, "xrfep.bin", 0x1000}, {PCIXR_422, BIOS, "xrbios.bin", 0x1000}, {PCIXR_422, FEP, "xrfep.bin", 0x1000}, {PCICX, BIOS, "cxpbios.bin", 0x1000}, {PCICX, FEP, "cxpfep.bin", 0x1000}, {ISACX, BIOS, "cxbios.bin", 0x7800}, {ISACX, FEP, "cxfep.bin", 0x5000}, {EISACX, BIOS, "cxbios.bin", 0x7800}, {EISACX, FEP, "cxfep.bin", 0x5000}, {PCIEPCJ, BIOS, "cxpbios.bin", 0x1000}, {PCIEPCJ, FEP, "cxpfep.bin", 0x1000}, {-1, -1, NULL, -1} /* End of array */ }; /* --------------------------------------------------------------------- The last argument of each file_map above indicates the address at which the binary is to be loaded. In case of the PCXE & PCXEVE BIOS this should be calculated because it is dependant on the size of the binary. ------------------------------------------------------------------------- */ int verbose = FALSE; /* verbose defaults to true */ int board_count = 0; char *version = "1.3.19"; #ifdef PCIBUG // From linux/lib/string.c void * slow_memcpy(void * dest,const void *src,size_t count) { char *tmp = (char *) dest, *s = (char *) src; while (count--) *tmp++ = *s++; return dest; } #endif /* -------------------- Begin usage --------------------- */ void usage() { printf("Usage: digiDload [v]\n"); } /* -------------------- Begin mess --------------------- */ void mess(int err, int indent, const char * format, ...) { /* Begin mess */ va_list args ; va_start (args, format); /* Indent so messages appear grouped under the card id header */ if( indent ) fprintf(stderr, "....." ); if( err ) fprintf(stderr, " - " ); vfprintf(stderr, format, args); va_end(args); } /* End mess */ /* -------------------- Begin nap ----------------------- */ void nap(int ms) { /* Begin nap */ struct timeval tv ; /* Shouldn't this be in the library somewhere? */ tv.tv_sec = 0 ; tv.tv_usec = ms * 1000 ; select(0,0,0,0,&tv) ; } /* End nap */ /* -------------------- Begin writeRAM ---------------------- */ void writeRAM(int target_addr, long value, int size, int card, int bus) { /* Begin writeRAM */ if (bus == PCI_CTL) { /* Begin PCI_CTL */ if (size == SIZE8) *((int_8 *)&board[card].control_addr[target_addr]) = value; else if (size == SIZE16) *((int_16 *)&board[card].control_addr[target_addr]) = value; else if (size == SIZE32) *((int_32 *)&board[card].control_addr[target_addr]) = value; else mess(1, 1, "writeRAM unknown size\n"); } /* End PCI_CTL */ else { /* Begin non-PCI */ if (size == SIZE8) *((int_8 *)&board[card].ram_addr[target_addr]) = value; else if (size == SIZE16) *((int_16 *)&board[card].ram_addr[target_addr]) = value; else if (size == SIZE32) *((int_32 *)&board[card].ram_addr[target_addr]) = value; else mess(1, 1, "writeRAM unknown size\n"); } /* End non-PCI */ } /* End writeRAM */ /* -------------------- Begin readRAM ---------------------- */ unsigned long int readRAM(int target_addr, int size, int card, int bus) { /* Begin readRAM */ unsigned long int retVal; int_8 val8 = 0; int_16 val16 = 0; int_32 val32 = 0; if (bus == PCI_CTL) { /* Begin PCI_CTL */ if (size == SIZE8) val8 = *((int_8 *)&board[card].control_addr[target_addr]); else if (size == SIZE16) val16 = *((int_16 *)&board[card].control_addr[target_addr]); else if (size == SIZE32) val32 = *((int_32 *)&board[card].control_addr[target_addr]); else mess(1, 1, "writeRAM unknown size\n"); } /* End PCI_CTL */ else { /* Begin normal */ if (size == SIZE8) val8 = *((int_8 *)&board[card].ram_addr[target_addr]); else if (size == SIZE16) val16 = *((int_16 *)&board[card].ram_addr[target_addr]); else if (size == SIZE32) val32 = *((int_32 *)&board[card].ram_addr[target_addr]); else mess(1, 1, "readRAM unknown size\n"); } /* End normal */ retVal = val8 | val16 | val32; return(retVal); } /* End readRAM */ /* -------------------- Begin init ---------------------- */ int init(int digiFD) { /* Begin init */ int x; int kmem_fd; unsigned int ram_size; short iopl_active = FALSE; /* This function initializes our internals. It should *NOT* touch any hardware itself. */ kmem_fd = open("/dev/kmem", O_RDWR); if (kmem_fd < 0 ) { mess(0, 1, "Open of /dev/kmem failed, errno %d\n", errno); return( 1 ); } for (x = 0; x < MAXCARDS; x++) { /* Begin for each board */ struct digi_info *di = &board[x].digi_info ; di->board = x ; if (ioctl(digiFD, DIGI_GETINFO, di) < 0) { /* Should we fall completely out here (Not just continue) ? */ continue; } board_count++; /* The membase returned is the physical address of the device in memory. Example 0xd0000, 0xd8000 ... */ /* Make ram size temporarily = 64K. This is abritrary */ if (di->type < PC64XE) ram_size = RAM_SIZE; else ram_size = (RAM_SIZE * 2); board[x].ram_addr = mmap (NULL, ram_size, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_FILE , kmem_fd, (off_t)di->membase) ; if ((int)(board[x].ram_addr) < 0 ) { perror("Mmap failed"); mess(0, 1, "Mmap failed on ram addr , errno %d\n", errno); return(1); } /* We check the type identifier to see if the board is PCI. The identifiers for non-PCI boards are all smaller than the identifier for the PCIXEM. If the board is non-PCI we map the boards i/o ports. Since most Digi PCI boards do not have a seperate address space (None that this program supports), we must insure that the program does not attempt to map i/o for these boards. The below check does that. */ if (di->type < PCIXEM) { /* Begin the board detected is NOT a PCI board */ /* Crank open I/O addresses */ /* iopl gives us access to all possible 65536 ports. It would be better to just open access to the ports we needed, but the closest routine that does that (ioperm) does not handle ports larger than 0x3ff. Since we also support EISA this will not work. */ if (!iopl_active) { if (iopl(3)) perror("iopl failed "); iopl_active = TRUE; } // Turn off this card's memory so it doesn't conflict with initialization // of other cards memoff( x ); } /* End the board detected is NOT a PCI board */ else { /* Begin the board detected IS a PCI board */ /* Most Digi PCI cards have their control registers mapped into memory as opposed to i/o space. We map those registers in here. */ board[x].control_addr = mmap (NULL, RAM_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_FILE , kmem_fd, (off_t)di->port) ; if (board[x].control_addr < 0 ) { mess(0, 1, "Mmap failed on PCI control space, errno %d\n", errno); return(1); } } /* End the board detected IS a PCI board */ } /* End for each board */ if (board_count == 0) { mess(0, 1, "No boards detected. Exiting now\n"); return(1); } /* Turn off the poller. Bad things happen to those who are squirting in code when the poller comes along. */ if (ioctl(digiFD, DIGI_POLLER, 1) < 0 ) { mess(0, 1, "Can't ioctl DIGI_POLLER %d %d\n", digiFD, errno); return( 1 ); } return( 0 ); } /* End init */ /* -------------------- Begin reset_cards ------------------- */ int reset_cards(int card) { /* Begin reset_cards */ int index; unsigned long temp; struct digi_info *di = &board[card].digi_info ; int_16 shiftedAddr; int_8 lowAddr, highAddr; /* Do whatever it takes to reset the card, and make it available. This includes asserting reset, mapping the memory address, and interrupts and possibly setting the windowing mode (Dependant on the card type. */ /* For most Digi cards (Including the PCXEM family) the formula for calculating the address is as follows : Set bitfields in t to select interrupt. disabled -> 0000h 3 -> 0001h 5 -> 0002h 7 -> 0003h 10 -> 0004h 11 -> 0005h 12 -> 0006h 15 -> 0007h Set bitfields 0xFF80 ot according to the bit fields 0xFF8000h of the base address. devb[2] = t and 00FFh devb[3] = t / 100h For example, a XEM board with interrupt 5, and base address of 0x00D8000h would have the below settings: t = 0xD80h + 0002h devb[2] = 82h = t and 00FF devb[3] = 0Dh = t / 100h The PC/XE and PC/XEVE are a little different because you have the option of setting a window. If no window is set the result would be the same (exactly) as the formula above. However, if windows are to be set bit position 0010h should be set in 't' above. For example, a XE board with interrupt 5, and base address of 0x00D8000h with windows on would have the below settings: t = 0xD80h + 0002h + 0010h <- Set windows on devb[2] = 92h = t and 00FF devb[3] = 0Dh = t / 100h Look at the example of the cx configuration on page 17 of the fep writers guide for another example. */ switch (di->type) { /* Begin switch di->type */ case ISACX:/*dbu*/ case EISACX:/*dbu*/ case PCXR: case PCXEM: case EISAXEM: outb(0x04, (int)di->port + 0); /* Hold card in a reset */ for (index = 0; (inb((int)di->port) & 0x0e) != 0x4; index ++) { if (index > 5) { /*dbu*/ if (((di->type) == ISACX) || ((di->type) == EISACX)) mess(1, 1, "reset_card : CX not resetting\n"); /*dbu*/ if ((di->type) == EISAXEM) mess(1, 1, "reset_card : EISAXEM not resetting\n"); else if ((di->type) == PCXR ) mess(1, 1, "reset_card : PCXR not resetting\n"); else mess(1, 1, "reset_card : XEM/CX not resetting\n"); return(1); } nap(200); /* 1/5th of a second */ } shiftedAddr = ((long)(di->membase)) >> 8; lowAddr = shiftedAddr & 0xFF; outb(lowAddr, (int)di->port + 2); highAddr = shiftedAddr >> 8; outb(highAddr, (int)di->port + 3); break; case PCIXR: case PCICX:/*dbu*/ case PCIEPCJ: case PCI920_2: case PCI920_4: case PCI920_8: case PCIXR_422: case PCIXRJ: case PCIXEM: writeRAM(0, 0x04, SIZE8, card, PCI_CTL); /* Hold card in reset */ for (index = 0; ((temp=readRAM(0x0, SIZE8, card, PCI_CTL)) & 0x0e) != 0x4; index ++) { if (index > 5) { mess(1, 1, "reset_card : PCI not resetting\n"); return(1); } nap(200); /* 1/5th of a second */ } break; case PCXE: case PCXEVE: /* XEVE resets the same as the XE */ /* The format of the below is a little different than the PCXEM case. The values are calculated exactly the same as above, BUT if 8K windows are to be used the value 0x10 has to be added to the low byte. Hence 0x00 (For 0xd0000) becomes 0x10. This only occurs if 8K windowing is desired. The only options for the new XE (glasgo) is 8*8K windows or 2*32K windows. The driver knows nothing about 32K windows. Below we automaticly choose windowing. We always choose windowing. */ shiftedAddr = ((long)(di->membase)) >> 8; /* We set bit 0x10 to enable windows */ lowAddr = (shiftedAddr + 0x10) & 0xFF; outb(lowAddr, (int)di->port + 2); highAddr = shiftedAddr >> 8; outb(highAddr, (int)di->port + 3); /* ---------------- Start code to reset board */ outb(0x06, (int)di->port + 0); /* Hold card in a reset */ /* NOTE : The manual talks about enabling the XE memory. They would use an 0x6 above and below. We have been using a 0x4 */ for (index = 0; (inb((int)di->port) & 0x0e) != 0x6; index ++) { if (index > 5) { mess(1, 1, "reset_card : PCXE not resetting\n"); return(1); } nap(200); /* 1/5th of a second */ } break; case PC64XE: /* The format of the below is a little different than the PCXE case. In particular it is not possible to set the older 64K XE boards (Excluding PC/XEVE handled above) to 8K windowing. The values will be calculated the same as the PCXEM case. */ shiftedAddr = ((long)(di->membase)) >> 8; lowAddr = (shiftedAddr) & 0xFF; outb(lowAddr, (int)di->port + 2); highAddr = shiftedAddr >> 8; outb(highAddr, (int)di->port + 3); /* ---------------- Start code to reset board */ outb(0x06, (int)di->port + 0); /* Hold card in a reset */ /* NOTE : The manual talks about enabling the XE memory. They would use an 0x6 above and below. We have been using a 0x4 */ for (index = 0; (inb((int)di->port) & 0x0e) != 0x6; index ++) { if (index > 5) { mess(1, 1, "reset_card : PC64XE not resetting\n"); return(1); } nap(200); /* 1/5th of a second */ } outb(0x06, (int)di->port + 0); /* Hold card in a reset */ break; case PCXI: /* Note, the PCXI has its memory options set via. dip switch. This cannot be changed dynamically hence (unlike above) you will not see code here programming the memory address or the windowing disposition. */ /* --------- Start code to reset board --------- */ outb(0x06, (int)di->port + 0); /* Hold card in a reset */ for (index = 0; (inb((int)di->port) & 0x0e) != 0x6; index ++) { if (index > 5) { mess(1, 1, "reset_card : PCXI not resetting\n"); return(1); } nap(200); /* 1/5th of a second */ } break; } /* End switch di->type */ return (0) ; } /* End reset_cards */ /* -------------------- Begin binary_is_running ----------------- */ int binary_is_running(int card, int binary_type) { /* Begin binary_is_running */ struct digi_info *di = &board[card].digi_info ; //struct digi_info *di; int ret_val = 0 ; //di = &board[card].digi_info; /* When necessary, turn on card RAM */ switch(di->type) { case ISACX:/*dbu*/ case EISACX:/*dbu*/ case PCXR: case PCXEM: case EISAXEM: outb(0x80 , (int)di->port + 1); break; case PCXE: outb(0x02 , (int)di->port + 0 ); break; } switch (binary_type) { /* Begin switch binary type */ case FEP: { char fep_sig0; char fep_sig1; if(di->type == ISACX || di->type == EISACX) { fep_sig0 = readRAM(0xc00, SIZE8, card, NORMAL); fep_sig1 = readRAM(0xc01, SIZE8, card, NORMAL); } else { // For some reason, FEP/OS takes longer to boot on a PCI EPC/J, than // normal. Therefore, we need to nap a bit, to let it complete. CWS if(di->type == PCIEPCJ) { #ifdef CWSDEBUG printf("PCI EPC/J Detected! Allowing more time for FEP/OS boot!\n"); #endif CWSDEBUG nap(150); } fep_sig0 = readRAM(0xd20, SIZE8, card, NORMAL); fep_sig1 = readRAM(0xd21, SIZE8, card, NORMAL); } /* If the FEP boots correctly, it should return the ascii string "OS" in locations 0xD20 and 0xD21. */ if ((fep_sig0 == 'O') && (fep_sig1 == 'S') ) //printf("fep_sig0: %c fep_sig1: %c\n",fep_sig0,fep_sig1); ret_val=1; break ; } case BIOS: { char bios_sig0; char bios_sig1; bios_sig0 = readRAM(0xc00, SIZE8, card, NORMAL); bios_sig1 = readRAM(0xc01, SIZE8, card, NORMAL); /* If the BIOS boots correctly, it should return the ascii string "GD" in locations 0xC00 and 0xC01. */ if (( bios_sig0 == 'G') && (bios_sig1 == 'D') ) ret_val=1; } } /* End switch binary type */ return ret_val ; } /* End binary_is_running */ /* -------------------- Begin wait_for_binary ----------------- */ int wait_for_binary(int card, int binary_type) { /* Begin wait_for_binary */ int interations ; /* Note when I set this for 30 interations (6 seconds) it will only boot if there is one concentrator. Doubling the time allows 2 concentrators. 240 is a lot, but it should handle 8 concentrators. */ for (interations = 0; interations < 480; interations++) { if (binary_is_running(card, binary_type)) { return(0); } nap(200); /* Expressed in milliseconds == 1/5 second */ } mess(0, 1, "Could not start %s on card %d\n", binary_type == FEP ? "FEPOS" : "BIOS", card); return(1); } /* End wait_for_binary */ #ifdef INCLUDE void memon( int card ) { struct digi_info *di = &board[card].digi_info ; /* Select the card memory */ switch( di->type ) { case PCXE: outb(0x02, (int)di->port+0 ); break; case PCXEM: outb(0x80, (int)di->port + 1); break; default: break; } } #endif void memoff( int card ) { struct digi_info *di = &board[card].digi_info ; /* Deselect the card (to make way for the next one) */ switch( di->type ) { case PCXE: case PCXEVE: case PCXI: case PC64XE: outb(0x00, (int)di->port ); break; case EISACX: case ISACX: case PCXR: case PCXEM: outb(0x00, (int)di->port + 1); break; default: break; } } void cxmagic( int card, struct digi_info *di, int epc ) { unsigned char *pt; unsigned int i; unsigned int *index; unsigned int *no_images; #ifdef CWSDEBUG printf("Writing %sC/X card magic string\n", epc? "EP" : "" ); #endif if( epc ) { index = &epcxindex; no_images = &no_epcximages; } else { index = &cxindex; no_images = &no_cximages; } if( *index < *no_images ) { // Enable memory on non-PCI cards if ( di->type != PCICX && di->type != PCIEPCJ ){ outb(0x80, (int)di->port + 1); } // 0xCD0 is magic EPCX concentrator config location i=0; pt = (unsigned char *)&(board[card].ram_addr[0xcd0]); do { if( epc ) *pt = epcx_cfg[*index][i]; else *pt = cx_cfg[*index][i]; ++i; } while( *pt++ != 0xff ); // Increment the global so we use the next magic string // on the next call to cxmagic (for the next card) (*index)++; } else { mess(1, 1, "Cannot find a configuration string for this %sCX concentrator\n", epc? "EP" : "" ); } } /* -------------------- Begin load_binary -------------------- */ int load_binary(int card, int binary_type) { /* Begin load_binary */ struct file_map *fm = file_map; unsigned int cmdInPtr, target_window; unsigned long memory_seg=0, target_addr, target_offset = 0; int binaryFD, index, nbytes, ret_val = 0; struct digi_info *di = &board[card].digi_info ; // Note: buf size needs to be precisely 1024 for CONC files char *temp_name, buf[1024]; for (fm = file_map ; fm->binary_type != -1 ; fm++ ) { /* Begin loop until we run out of binary files. */ if (( fm->binary_type == binary_type) && ( fm->board_type == di->type ) ) { /* If this board doesn't get this type of binary, bail */ if ( fm->fname == NULL ) { return(1) ; } break; } } /* End loop until we run out of binary files. */ /* The PCXI and PCXE can have varying amounts of memory onboard. Because of this we need to calculate where that memory ends, and therefore where we can load bios info. */ if ((di->type == PCXI) || (di->type == PC64XE)) { /* This memory_seg assumes the XI or XE is a 64K board. We need to add code to query this to be sure. See internal release 110c. */ memory_seg = 0xf000; } target_addr = fm->addr ; temp_name = (char *)malloc((size_t)((strlen(fm->fname)) + 11)); *temp_name = 0; /* Make first character null; so strcat can work */ strcat(temp_name,"/etc/digi/"); strcat(temp_name,fm->fname); if (verbose) { mess(0, 1, "Downloading %s to %lx on %s\n", temp_name, (long) fm->addr, board_desc[di->type] ); } /* Note: programmer can do something stupid here and pass a non-buf size alignable value that will make this copy fail. Don't do that. */ if ((binaryFD = open( temp_name, O_RDONLY )) < 0 ) { mess(0, 1, "Open of %s failed, errno %d\n", fm->fname, errno); return( 1 ); } free((void *)temp_name); /* DBU: special for CX: before loading FEP code, put the CX config string onboard */ if( binary_type == FEP ) { switch (di->type) { case PCICX: case ISACX: case EISACX: cxmagic( card, di, 0 ); break; case PCIEPCJ: cxmagic( card, di, 1 ); break; } } /* Now, slurp the whole binary in, jam it on the card */ while ((nbytes = read (binaryFD, buf, sizeof(buf)))) { /* Begin while read loop */ /* This block does whatever is necesary to prepare the card to receive this chunk. */ /* First get all the data on the card; switch windows as necessary */ switch (di->type) { /* Begin switch di->type */ case PCICX: case PCIEPCJ: case PCIXR: case PCI920_2: case PCI920_4: case PCI920_8: case PCIXR_422: case PCIXRJ: case PCIXEM: switch (binary_type) { /* Begin switch binary_type */ case BIOS: case FEP: target_offset = target_addr; break; } /* End switch binary_type */ break; case ISACX: case EISACX: switch (binary_type) { /* Begin switch binary_type */ case BIOS: target_window = 0xf; outb(0x80 | target_window , (int)di->port + 1); target_offset = target_addr; break; case FEP: //target_window = 0x1; target_window = (target_addr >> 15) + 1; outb(0x80 | target_window , (int)di->port + 1); target_offset = target_addr; break; } /* End switch binary_type */ break; case PCXR: case PCXEM: case EISAXEM: switch (binary_type) { /* Begin switch binary_type */ case BIOS: case FEP: /* The below will cause the a window switch if window is about to be exceeded. */ target_offset = target_addr & (RAM_SIZE - 1); target_window = target_addr >> 15; outb(0x80 | target_window , (int)di->port + 1); break; } /* End switch binary_type */ break; case PCXE: case PCXEVE: /* Works same as PCXE */ switch (binary_type) { /* Begin switch binary_type */ case BIOS: /* The below board[card].ram_addr already points at the board mapped in at physical memory. */ /* If target_addr was set to 0x1800 (Assuming 2k bios) the same code should NEARLY work. Watch out for the shift 15 */ /* We mask against 0x2000 because it is an 8K window */ target_offset = target_addr & (0x2000 - 1); /* Later develop code just in case bios > 2K */ /* FIXME : Do not depend on addr = 0x1800 and window = 0xf */ outb(0x80 | 0xf , (int)di->port + 1); break; case FEP: /* We mask against 0x2000 because it is an 8K window */ target_offset = target_addr & (0x2000 - 1); /* Select window 1 always */ outb(0x80 | 0x1 , (int)di->port + 1); break; } /* End switch binary_type */ break; case PCXI: case PC64XE: switch (binary_type) { /* Begin switch binary_type */ case BIOS: outb(0x6 , (int)di->port); target_offset = target_addr - (memory_seg << 4); break; case FEP: target_offset = target_addr; break; } /* End switch binary_type */ break; } /* End switch di->type */ /* Now, we write the chunk to the card */ // Some PCI Chipsets fail on some multi-byte (e.g: 32-bit) transfers. With // these, try doing bytewise transfers instead. #ifdef PCIBUG slow_memcpy((void *)&board[card].ram_addr[target_offset], (const void *)buf, nbytes); #else memcpy((void *)&board[card].ram_addr[target_offset], (const void *)buf, nbytes); #endif //if( memcmp((void *)&board[card].ram_addr[target_offset], (const void *)buf, nbytes) ) // printf( "LOAD BINARY FAILED\n" ); //else // printf( "LOAD BINARY SUCEEDED\n" ); target_addr += nbytes ; } /* End while read loop */ /* Note : When we arrive here the memory window we are currently in is NOT guaranteed to be 0. */ /* Consider puting a check to make sure the code got there okay */ /* Now, all the code is on the card. We just have to execute it. */ switch (di->type) { /* Begin switch di->type */ case PCICX: case PCIEPCJ: case PCIXR: case PCI920_2: case PCI920_4: case PCI920_8: case PCIXR_422: case PCIXRJ: case PCIXEM: { /* Begin PCI XR, XRJ, XRM case */ switch(binary_type) { /* End switch binary_type */ case BIOS: /* Set boot vector */ /* Note - these are long words */ writeRAM(0, 0x0bf00401, SIZE32, card, NORMAL); writeRAM(4, 0x0, SIZE32, card, NORMAL); writeRAM(0xc00, 0x0, SIZE16, card, NORMAL); break; case FEP: /* Clear confirm word */ writeRAM(0xd20, 0x0, SIZE16, card, NORMAL); /* Set initial address and BIOS command */ writeRAM(0xc34, 0xbfc01004L, SIZE32, card, NORMAL); writeRAM(0xc30, 0x3L, SIZE32, card, NORMAL); break; } /* End switch binary_type */ /* Release reset now */ writeRAM(0, 0x0, SIZE8, card, PCI_CTL); /* Wait for binary (BIOS or FEP) to boot */ if(wait_for_binary(card, binary_type)) return(1); break; } /* End PCI XR, XRJ, XRM case */ case ISACX: case EISACX: { /* Begin case ISA/EISA CX */ switch(binary_type) { /* End switch binary_type */ case BIOS: /* Put us back in window 0 */ outb(0x80, (int)di->port + 1); /* Clear confirm word */ writeRAM(0, 0x0c00, SIZE16, card, NORMAL); /* Release reset now */ outb(0x00, (int)di->port); /* Wait for binary to boot */ wait_for_binary(card, binary_type); break; case FEP: /* Select page 0 and enable memory */ outb(0x80, (int)di->port + 1); /* Form BIOS execution request */ writeRAM(0xc40, 0x1, SIZE16, card, NORMAL); writeRAM(0xc42, 0xd00, SIZE16, card, NORMAL); writeRAM(0xc44, 0x4, SIZE16, card, NORMAL); /* Clear confirm word */ writeRAM(0xd20, 0x0, SIZE16, card, NORMAL); outb((inb((int)di->port) & ~0x8), (int)di->port); outb((inb((int)di->port) | 0x8), (int)di->port); /* Wait for binary to boot */ wait_for_binary(card, binary_type); break; } /* End switch binary_type */ break; } /* End case ISA/EISA CX */ case PCXR: case PCXEM: case EISAXEM: { /* Begin case PCXEM */ /* Put us back in window 0 */ outb(0x80, (int)di->port + 1); switch(binary_type) { /* End switch binary_type */ case BIOS: /* Set boot vector */ /* Note - these are long words */ writeRAM(0, 0x0bf00401, SIZE32, card, NORMAL); writeRAM(4, 0x0, SIZE32, card, NORMAL); writeRAM(0xc00, 0x0, SIZE16, card, NORMAL); break; case FEP: /* Clear confirm word */ writeRAM(0xd20, 0x0, SIZE16, card, NORMAL); /* Set initial address and BIOS command */ writeRAM(0xc34, 0xbfc01004L, SIZE32, card, NORMAL); writeRAM(0xc30, 0x3L, SIZE32, card, NORMAL); break; } /* End switch binary_type */ /* Release reset now */ outb(0x00, (int)di->port + 0); // PEB shouldn't this go to port +1 ? //outb(0x00, (int)di->port + 1); /* Wait for binary (BIOS or FEP) to boot */ if(wait_for_binary(card, binary_type)) return(1); //outb(0x00, (int)di->port + 1); /* Deselect the card to make way for the next one */ break; } /* End case PCXEM */ case PCXE: case PCXEVE: { /* Begin case PCXE or PCXEVE */ /* Put us back in window 0 */ outb(0x80, (int)di->port + 1); switch(binary_type) { /* End switch binary_type */ case BIOS: /* Clear confirm word */ writeRAM(0xc00, 0x0, SIZE16, card, NORMAL); /* Release reset now */ outb(0x00, (int)di->port + 0); if(wait_for_binary(card, binary_type)) return(1);; //outb(0x00, (int)di->port+0 ); /* Deselect the card to make way for the next one */ break; case FEP: /* Do not forget to byte swap these for little endian. 0c40 <- 0002h BIOS request to move program 0c42 <- f200 seg + 200h = f2000002h c44 <- 0000h c46 <- 0200h = 02000000h c48 <- 0000h c4a <- 2000h = 20000000h */ /* Interestingly enough the 8E glasgo does not need a command to move the fep program since dual port ram is shadowed over local memory. Additionally while the Glasgo offers the standard 8 * 8K windows, it also offers 2 * 32K windows. It does NOT support the older 64K window. The below command is not needed, but we will keep it for XEVE compatability. As long as we use the 8 * 8K windows the XEVE and XE will look identical to the driver. They only differ when using the 2 * 32K Glasgo window mode or the 1 * 64K XEVE window mode. */ /* Form BIOS request to move program to 0x2000 */ writeRAM(0xc40, 0xf2000002L, SIZE32, card, NORMAL); writeRAM(0xc44, 0x02000000L, SIZE32, card, NORMAL); writeRAM(0xc48, 0x20000000L, SIZE32, card, NORMAL); /* NOTE : The int below is NOT asserted on the host bus unless the function code in 0xc40 = 0. In this case ints are generated on the card AND host. In all other situations the int is generated just on the card. */ /* Toggle board interrupt. This is done to make the BIOS run the command. */ outb(0x8, (int)di->port); outb(0x00, (int)di->port); for (index = 0; index < 4; index++) { if ((readRAM(0xc40, SIZE16, card, NORMAL)) == 0) break; /* We got a 0, we can leave this loop now ! */ sleep(1); } if (index == 4) { mess(0, 1, "Attempt to toggle interrupt failed.\n"); return(1); } /* Form BIOS request to execute FEPOS */ writeRAM(0xc40, 0x02000001L, SIZE32, card, NORMAL); writeRAM(0xc44, 0x4, SIZE16, card, NORMAL); /* Clear confirm word */ writeRAM(0xd20, 0x0, SIZE16, card, NORMAL); /* Toggle board interrupt. This is done to make the BIOS run the command. */ outb(0x08, (int)di->port); outb(0x0, (int)di->port); // PEB shouldnt this got to port +1 ? //outb(0x0, (int)di->port + 1); /* Wait to see if the FEP comes up (BOOTS) */ if(wait_for_binary(card, binary_type)) return(1);; /* Get command in pointer */ cmdInPtr = readRAM(0xd10, SIZE16, card, NORMAL); /* Set buffer space command */ writeRAM((0x400 + cmdInPtr), 0x92, SIZE8, card, NORMAL); writeRAM((0x401 + cmdInPtr), 0x0, SIZE8, card, NORMAL); /* Set up for 32L buffers allow each receive, transmit buffer to have its on 8K window. */ writeRAM((0x402 + cmdInPtr), 0x8000, SIZE16, card, NORMAL); /* Advance the command in pointer */ writeRAM(0xd10, (cmdInPtr + 4), SIZE16, card, NORMAL); break; } /* End switch binary_type */ break; } /* End case for PCXE and PCXEVE */ case PCXI: case PC64XE: /* Works same as PCXI */ { /* Begin case PCXI and PC64XE */ switch (binary_type) { /* Begin switch binary_type */ case BIOS: /* Clear confirm word */ writeRAM(0xc00, 0x0, SIZE16, card, NORMAL); /* Release reset now, but leave memory enabled */ outb(0x02, (int)di->port); if(wait_for_binary(card, binary_type)) return(1);; break; case FEP: /* Do not forget to byte swap these for little endian. 0c40 <- 0002h BIOS request to move program 0c42 <- f200 seg + 200h (f000 for a 64K board) = f2000002h c44 <- 0000h c46 <- 0200h = 02000000h c48 <- 0000h c4a <- 2000h = 20000000h */ outb(0x02, (int)di->port); /* Leave memory enabled */ /* Form BIOS request to move program to 0x2000 */ writeRAM(0xc40, (0x02000002L + (memory_seg << 16)), SIZE32, card, NORMAL); writeRAM(0xc44, 0x02000000L, SIZE32, card, NORMAL); writeRAM(0xc48, 0x20000000L, SIZE32, card, NORMAL); /* NOTE : The int below is NOT asserted on the host bus unless the function code in 0xc40 = 0. In this case ints are generated on the card AND host. In all other situations the int is generated just on the card. */ /* Toggle board interrupt. This is done to make the BIOS run the command. */ outb(0x0A, (int)di->port); outb(0x02, (int)di->port); /* Leave memory enabled */ for (index = 0; index < 4; index++) { if ((readRAM(0xc40, SIZE16, card, NORMAL)) == 0) break; /* We got a 0, we can leave this loop now ! */ sleep(1); } if (index == 4) { mess(0, 1, "Attempt to toggle interrupt failed.\n"); return(1); } /* Form BIOS request to execute FEPOS */ writeRAM(0xc40, 0x02000001L, SIZE32, card, NORMAL); writeRAM(0xc44, 0x0004, SIZE16, card, NORMAL); /* Clear confirm word */ writeRAM(0xd20, 0x0000, SIZE16, card, NORMAL); /* Toggle board interrupt. This is done to make the BIOS run the command. */ outb(0x0A, (int)di->port); outb(0x02, (int)di->port); /* Wait to see if the FEP comes up (BOOTS) */ if(wait_for_binary(card, binary_type)) return(1); break; } /* End switch binary_type */ break; } /* End case PCXI and PC64XE */ } /* End switch di->type */ return(ret_val); } /* End load_binary */ int getcxconf( unsigned char cfg[][CXCFGLEN], char *fname, int epc ) { int fd; unsigned char c; unsigned int cx, offset, nbytes; if ((fd = open( fname, O_RDONLY )) < 0 ) { mess(0, 1, "No %s, using defaults for %sCX concentrators", fname, epc? "EP" : "" ); return( 1 ); } cx = 0; offset = 0; while ( (nbytes = read (fd, &c, 1 )) ) { cfg[cx][offset] = c; if( c == 0xff ) { // We've read a full config string #ifdef CWSDEBUG printf("End of config string #%d! (%sC/X card!)\n",cx, epc? "EP" : "" ); #endif // Update the global if( epc ) ++no_epcximages; else ++no_cximages; // Next card ++cx; offset = 0; } else ++offset; } close(fd); return( 0 ); } void report_numports( int digiFD ) { int card; struct digi_info *di; mess( 0, 0, "Number of ports:\n" ); for (card = 0; card < board_count; card++) { di = &board[card].digi_info ; // Repeat this, because "numports" is not available // on PCI cards until after the card is booted if (ioctl(digiFD, DIGI_GETINFO, di) < 0) mess( 0, 0, "(second DIGI_GETINFO failed!)..." ); else mess( 0, 1, "Card #%d(%s): found %d ports\n", card, board_desc[di->type], di->numports ); } } /* Copy C/X concentrator download image to the driver */ int cxcon_init( int digiFD ) { struct stat stat_buf; char buf[CXCONC_CHUNKSIZE]; CXCONC_UPLOAD cxconc_chunk; int cxcon_fd, nbytes; extern int errno; if( stat( cxcon_fname, &stat_buf ) ) { printf( "Cannot stat '%s'\n", cxcon_fname ); exit(1); //printf( "Length of '%s' is %ld\n", cxcon_fname, stat_buf.st_size ); } if ((cxcon_fd = open( cxcon_fname, O_RDONLY )) < 0 ) { printf("Open of %s failed, errno %d\n", cxcon_fname, errno); return( 1 ); } if (ioctl(digiFD, DIGI_CXCON_INIT, &stat_buf.st_size) < 0 ) { mess(0, 0, "Can't ioctl DIGI_CXCON_INIT %d\n", errno); return(1); } mess(0,1,"Downloading %s to EPCA Memory Space\n",cxcon_fname); while ((nbytes = read (cxcon_fd, buf, sizeof(buf)))) { cxconc_chunk.size = nbytes; memcpy( cxconc_chunk.data, buf, nbytes ); if (ioctl(digiFD, DIGI_CXCON_NEXT, &cxconc_chunk) < 0 ) { mess(0, 0, "Can't ioctl DIGI_CXCON_NEXT %d\n", errno); return(1); } #ifdef CWSDEBUG2 printf("Loaded %s to EPCA Driver!\n",cxcon_fname); #endif } close( cxcon_fd ); return( 0 ); } /* Copy EPC/X concentrator download image to the driver */ int epcxcon_init( int digiFD ) { struct stat stat_buf; char buf[EPCXCONC_CHUNKSIZE]; EPCXCONC_UPLOAD epcxconc_chunk; int epcxcon_fd, nbytes; extern int errno; if( stat( epcxcon_fname, &stat_buf ) ) { printf( "Cannot stat '%s'\n", epcxcon_fname ); exit(1); printf( "Length of '%s' is %ld\n", epcxcon_fname, stat_buf.st_size ); } if ((epcxcon_fd = open( epcxcon_fname, O_RDONLY )) < 0 ) { printf("Open of %s failed, errno %d\n", epcxcon_fname, errno); return( 1 ); } if (ioctl(digiFD, DIGI_EPCXCON_INIT, &stat_buf.st_size) < 0 ) { mess(0, 0, "Can't ioctl DIGI_EPCXCON_INIT %d\n", errno); return(1); } mess(0,1,"Downloading %s to EPCA Memory Space\n",epcxcon_fname); while ((nbytes = read (epcxcon_fd, buf, sizeof(buf)))) { epcxconc_chunk.size = nbytes; memcpy( epcxconc_chunk.data, buf, nbytes ); if (ioctl(digiFD, DIGI_EPCXCON_NEXT, &epcxconc_chunk) < 0 ) { mess(0, 0, "Can't ioctl DIGI_EPCXCON_NEXT %d\n", errno); return(1); } #ifdef CWSDEBUG2 printf("Loaded %s to EPCA Driver!\n",epcxcon_fname); #endif } close( epcxcon_fd ); return( 0 ); } /* ------------------- Begin main ------------------------- */ int main(int argc, char *argv[]) { /* Begin main */ int digiFD, card, someone_there, cx_there, epcx_there; struct digi_info *di; if (argc > 2) { mess(1, 1, "Too many arguments\n"); usage(); return(-1); } if (argc == 2) { /* strcmp returns false if expression is equal */ if (!strcmp("v",argv[1])) verbose = TRUE; else { mess(1, 1, "Invalid argument\n"); usage(); return(-1); } } if ((digiFD = open("/dev/digi_ctl", O_RDWR)) < 0 ) { perror(" cannot open digi ctl "); } if( init(digiFD) ) return( 1 ); if( verbose ) mess(0, 0, "digiDload version %s\n",version); someone_there = 0; // Any Digi cards found? cx_there = 0; // Any C/X cards found? epcx_there = 0; // Any EPC/X cards found? getcxconf( cx_cfg, CXCONFIG, 0 ); getcxconf( epcx_cfg, EPCXCONFIG, 1 ); // if (ioctl(digiFD, DIGI_CLOSE_ALL, 0) < 0 ) { // mess(0, 0, "Can't ioctl DIGI_CLOSE_ALL %d\n", errno); // // } for (card = 0; card < board_count; card++) { di = &board[card].digi_info ; if( verbose ) mess( 0, 0, "Initializing card #%d(%s)\n", card, board_desc[di->type] ); if (ioctl(digiFD, DIGI_DISABLE, di) < 0) mess( 0, 0, "Cannot disable card!..." ); if( !reset_cards(card) && !load_binary(card, BIOS) ) { sleep(1); if( !load_binary(card, FEP) ) { sleep( 2 ); someone_there = 1; switch( di->type ) { case ISACX: case EISACX: case PCICX: cx_there = 1; break; case PCIEPCJ: epcx_there = 1; break; } } } memoff( card ); /* Disable card memory so other cards can be initialized */ } if( someone_there ) { // C/CON modules can be used with either C/X or EPC/X adapters, but ... if( cx_there || epcx_there ) { // Read CX concentrator configurations, if any // getcxconf( cx_cfg, CXCONFIG, 0 ); if( cxcon_init( digiFD ) ) mess(0, 0, "Can't load C/X concentrator image\n" ); } // ... EPC/CON modules are only supported by EPC/X adapters if( epcx_there ) { // getcxconf( epcx_cfg, EPCXCONFIG, 1 ); if( epcxcon_init( digiFD ) ) mess(0, 0, "Can't load EPC/X concentrator image\n" ); } if (ioctl(digiFD, DIGI_INIT, 0) < 0 ) { mess(0, 0, "Can't ioctl DIGI_INIT %d\n", errno); return(1); } if( verbose ) report_numports( digiFD ); if (ioctl(digiFD, DIGI_POLLER, 0) < 0 ) { mess(0, 0, "Can't ioctl DIGI_POLLER %d\n", errno); return(1); } } mess(0, 0, "digiDload complete : Card(s) initialized\n"); return( 0 ); } /* End main */