summaryrefslogtreecommitdiff
path: root/drivers/staging/dgap/downld.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/staging/dgap/downld.c')
-rw-r--r--drivers/staging/dgap/downld.c798
1 files changed, 798 insertions, 0 deletions
diff --git a/drivers/staging/dgap/downld.c b/drivers/staging/dgap/downld.c
new file mode 100644
index 000000000000..57dfd6bafcf6
--- /dev/null
+++ b/drivers/staging/dgap/downld.c
@@ -0,0 +1,798 @@
+/*
+ * Copyright 2003 Digi International (www.digi.com)
+ * Scott H Kilau <Scott_Kilau at digi dot com>
+ *
+ * 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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; 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.
+ *
+ * $Id: downld.c,v 1.6 2009/01/14 14:10:54 markh Exp $
+ */
+
+/*
+** downld.c
+**
+** This is the daemon that sends the fep, bios, and concentrator images
+** from user space to the driver.
+** BUGS:
+** If the file changes in the middle of the download, you probably
+** will get what you deserve.
+**
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/errno.h>
+
+#include "dgap_types.h"
+#include "digi.h"
+#include "dgap_fep5.h"
+
+#include "dgap_downld.h"
+
+#include <string.h>
+#include <malloc.h>
+#include <stddef.h>
+#include <unistd.h>
+
+char *pgm;
+void myperror();
+
+/*
+** This structure is used to keep track of the diferent images available
+** to give to the driver. It is arranged so that the things that are
+** constants or that have defaults are first inthe strucutre to simplify
+** the table of initializers.
+*/
+struct image_info {
+ short type; /* bios, fep, conc */
+ short family; /* boards this applies to */
+ short subtype; /* subtype */
+ int len; /* size of image */
+ char *image; /* ioctl struct + image */
+ char *name;
+ char *fname; /* filename of binary (i.e. "asfep.bin") */
+ char *pathname; /* pathname to this binary ("/etc/dgap/xrfep.bin"); */
+ time_t mtime; /* Last modification time */
+};
+
+#define IBIOS 0
+#define IFEP 1
+#define ICONC 2
+#define ICONFIG 3
+#define IBAD 4
+
+#define DEFAULT_LOC "/lib/firmware/dgap/"
+
+struct image_info *image_list;
+int nimages, count;
+
+struct image_info images[] = {
+{IBIOS, T_EPC, SUBTYPE, 0, NULL, "EPC/X", "fxbios.bin", DEFAULT_LOC "fxbios.bin", 0 },
+{IFEP, T_EPC, SUBTYPE, 0, NULL, "EPC/X", "fxfep.bin", DEFAULT_LOC "fxfep.bin", 0 },
+{ICONC, T_EPC, SUBTYPE, 0, NULL, "EPC/X", "fxcon.bin", DEFAULT_LOC "fxcon.bin", 0 },
+
+{IBIOS, T_CX, SUBTYPE, 0, NULL, "C/X", "cxbios.bin", DEFAULT_LOC "cxbios.bin", 0 },
+{IFEP, T_CX, SUBTYPE, 0, NULL, "C/X", "cxhost.bin", DEFAULT_LOC "cxhost.bin", 0 },
+
+{IBIOS, T_CX, T_PCIBUS, 0, NULL, "C/X PCI", "cxpbios.bin", DEFAULT_LOC "cxpbios.bin", 0 },
+{IFEP, T_CX, T_PCIBUS, 0, NULL, "C/X PCI", "cxpfep.bin", DEFAULT_LOC "cxpfep.bin", 0 },
+
+{ICONC, T_CX, SUBTYPE, 0, NULL, "C/X", "cxcon.bin", DEFAULT_LOC "cxcon.bin", 0 },
+{ICONC, T_CX, SUBTYPE, 0, NULL, "C/X", "ibmcxcon.bin", DEFAULT_LOC "ibmcxcon.bin", 0 },
+{ICONC, T_CX, SUBTYPE, 0, NULL, "C/X", "ibmencon.bin", DEFAULT_LOC "ibmencon.bin", 0 },
+
+{IBIOS, FAMILY, T_PCXR, 0, NULL, "PCXR", "xrbios.bin", DEFAULT_LOC "xrbios.bin", 0 },
+{IFEP, FAMILY, T_PCXR, 0, NULL, "PCXR", "xrfep.bin", DEFAULT_LOC "xrfep.bin", 0 },
+
+{IBIOS, T_PCLITE, SUBTYPE, 0, NULL, "X/em", "sxbios.bin", DEFAULT_LOC "sxbios.bin", 0 },
+{IFEP, T_PCLITE, SUBTYPE, 0, NULL, "X/em", "sxfep.bin", DEFAULT_LOC "sxfep.bin", 0 },
+
+{IBIOS, T_EPC, T_PCIBUS, 0, NULL, "PCI", "pcibios.bin", DEFAULT_LOC "pcibios.bin", 0 },
+{IFEP, T_EPC, T_PCIBUS, 0, NULL, "PCI", "pcifep.bin", DEFAULT_LOC "pcifep.bin", 0 },
+{ICONFIG, 0, 0, 0, NULL, NULL, "dgap.conf", "/etc/dgap.conf", 0 },
+
+/* IBAD/NULL entry indicating end-of-table */
+
+{IBAD, 0, 0, 0, NULL, NULL, NULL, NULL, 0 }
+
+} ;
+
+int errorprint = 1;
+int nodldprint = 1;
+int debugflag;
+int fd;
+
+struct downld_t *ip; /* Image pointer in current image */
+struct downld_t *dp; /* conc. download */
+
+
+/*
+ * The same for either the FEP or the BIOS.
+ * Append the downldio header, issue the ioctl, then free
+ * the buffer. Not horribly CPU efficient, but quite RAM efficient.
+ */
+
+void squirt(int req_type, int bdid, struct image_info *ii)
+{
+ struct downldio *dliop;
+ int size_buf;
+ int sfd;
+ struct stat sb;
+
+ /*
+ * If this binary comes from a file, stat it to see how
+ * large it is. Yes, we intentionally do this each
+ * time for the binary may change between loads.
+ */
+
+ if (ii->pathname) {
+ sfd = open(ii->pathname, O_RDONLY);
+
+ if (sfd < 0 ) {
+ myperror(ii->pathname);
+ goto squirt_end;
+ }
+
+ if (fstat(sfd, &sb) == -1 ) {
+ myperror(ii->pathname);
+ goto squirt_end;
+ }
+
+ ii->len = sb.st_size ;
+ }
+
+ size_buf = ii->len + sizeof(struct downldio);
+
+ /*
+ * This buffer will be freed at the end of this function. It is
+ * not resilient and should be around only long enough for the d/l
+ * to happen.
+ */
+ dliop = (struct downldio *) malloc(size_buf);
+
+ if (dliop == NULL) {
+ fprintf(stderr,"%s: can't get %d bytes of memory; aborting\n",
+ pgm, size_buf);
+ exit (1);
+ }
+
+ /* Now, stick the image in fepimage. This can come from either
+ * the compiled-in image or from the filesystem.
+ */
+ if (ii->pathname)
+ read(sfd, dliop->image.fi.fepimage, ii->len);
+ else
+ memcpy(dliop ->image.fi.fepimage, ii->image, ii->len);
+
+ dliop->req_type = req_type;
+ dliop->bdid = bdid;
+
+ dliop->image.fi.len = ii->len;
+
+ if (debugflag)
+ printf("sending %d bytes of %s %s from %s\n",
+ ii->len,
+ (ii->type == IFEP) ? "FEP" : (ii->type == IBIOS) ? "BIOS" : "CONFIG",
+ ii->name ? ii->name : "",
+ (ii->pathname) ? ii->pathname : "internal image" );
+
+ if (ioctl(fd, DIGI_DLREQ_SET, (char *) dliop) == -1) {
+ if(errorprint) {
+ fprintf(stderr,
+ "%s: warning - download ioctl failed\n",pgm);
+ errorprint = 0;
+ }
+ sleep(2);
+ }
+
+squirt_end:
+
+ if (ii->pathname) {
+ close(sfd);
+ }
+ free(dliop);
+}
+
+
+/*
+ * See if we need to reload the download image in core
+ *
+ */
+void consider_file_rescan(struct image_info *ii)
+{
+ int sfd ;
+ int len ;
+ struct stat sb;
+
+ /* This operation only makes sense when we're working from a file */
+
+ if (ii->pathname) {
+
+ sfd = open (ii->pathname, O_RDONLY) ;
+ if (sfd < 0 ) {
+ myperror(ii->pathname);
+ exit(1) ;
+ }
+
+ if( fstat(sfd,&sb) == -1 ) {
+ myperror(ii->pathname);
+ exit(1);
+ }
+
+ /* If the file hasn't changed since we last did this,
+ * and we have not done a free() on the image, bail
+ */
+ if (ii->image && (sb.st_mtime == ii->mtime))
+ goto end_rescan;
+
+ ii->len = len = sb.st_size ;
+
+ /* Record the timestamp of the file */
+ ii->mtime = sb.st_mtime;
+
+ /* image should be NULL unless there is an image malloced
+ * in already. Before we malloc again, make sure we don't
+ * have a memory leak.
+ */
+ if ( ii->image ) {
+ free( ii->image );
+ /* ii->image = NULL; */ /* not necessary */
+ }
+
+ /* This image will be kept only long enough for the
+ * download to happen. After sending the last block,
+ * it will be freed
+ */
+ ii->image = malloc(len) ;
+
+ if (ii->image == NULL) {
+ fprintf(stderr,
+ "%s: can't get %d bytes of memory; aborting\n",
+ pgm, len);
+ exit (1);
+ }
+
+ if (read(sfd, ii->image, len) < len) {
+ fprintf(stderr,"%s: read error on %s; aborting\n",
+ pgm, ii->pathname);
+ exit (1);
+ }
+
+end_rescan:
+ close(sfd);
+
+ }
+}
+
+/*
+ * Scan for images to match the driver requests
+ */
+
+struct image_info * find_conc_image()
+{
+ int x ;
+ struct image_info *i = NULL ;
+
+ for ( x = 0; x < nimages; x++ ) {
+ i=&image_list[x];
+
+ if(i->type != ICONC)
+ continue;
+
+ consider_file_rescan(i) ;
+
+ ip = (struct downld_t *) image_list[x].image;
+ if (ip == NULL) continue;
+
+ /*
+ * When I removed Clusterport, I kept only the code that I
+ * was SURE wasn't ClusterPort. We may not need the next two
+ * lines of code.
+ */
+ if ((dp->dl_type != 'P' ) && ( ip->dl_srev == dp->dl_srev ))
+ return i;
+ }
+ return NULL ;
+}
+
+
+int main(int argc, char **argv)
+{
+ struct downldio dlio;
+ int offset, bsize;
+ int x;
+ char *down, *image, *fname;
+ struct image_info *ii;
+
+ pgm = argv[0];
+ dp = &dlio.image.dl; /* conc. download */
+
+ while((argc > 2) && !strcmp(argv[1],"-d")) {
+ debugflag++ ;
+ argc-- ;
+ argv++ ;
+ }
+
+ if(argc < 2) {
+ fprintf(stderr,
+ "usage: %s download-device [image-file] ...\n",
+ pgm);
+ exit(1);
+ }
+
+
+
+ /*
+ * Daemonize, unless debugging is turned on.
+ */
+ if (debugflag == 0) {
+ switch (fork())
+ {
+ case 0:
+ break;
+
+ case -1:
+ return 1;
+
+ default:
+ return 0;
+ }
+
+ setsid();
+
+ /*
+ * The child no longer needs "stdin", "stdout", or "stderr",
+ * and should not block processes waiting for them to close.
+ */
+ fclose(stdin);
+ fclose(stdout);
+ fclose(stderr);
+
+ }
+
+ while (1) {
+ if( (fd = open(argv[1], O_RDWR)) == -1 ) {
+ sleep(1);
+ }
+ else
+ break;
+ }
+
+ /*
+ ** create a list of images to search through when trying to match
+ ** requests from the driver. Put images from the command line in
+ ** the list before built in images so that the command line images
+ ** can override the built in ones.
+ */
+
+ /* allocate space for the list */
+
+ nimages = argc - 2;
+
+ /* count the number of default list entries */
+
+ for (count = 0; images[count].type != IBAD; ++count) ;
+
+ nimages += count;
+
+ /* Really should just remove the variable "image_list".... robertl */
+ image_list = images ;
+
+ /* get the images from the command line */
+ for(x = 2; x < argc; x++) {
+ int xx;
+
+ /*
+ * strip off any leading path information for
+ * determining file type
+ */
+ if( (fname = strrchr(argv[x],'/')) == NULL)
+ fname = argv[x];
+ else
+ fname++; /* skip the slash */
+
+ for (xx = 0; xx < count; xx++) {
+ if (strcmp(fname, images[xx].fname) == 0 ) {
+ images[xx].pathname = argv[x];
+
+ /* image should be NULL until */
+ /* space is malloced */
+ images[xx].image = NULL ;
+ }
+ }
+ }
+
+ sleep(3);
+
+ /*
+ ** Endless loop: get a request from the fep, and service that request.
+ */
+ for(;;) {
+ /* get the request */
+ if (debugflag)
+ printf("b4 get ioctl...");
+
+ if (ioctl(fd,DIGI_DLREQ_GET, &dlio) == -1 ) {
+ if (errorprint) {
+ fprintf(stderr,
+ "%s: warning - download ioctl failed\n",
+ pgm);
+ errorprint = 0;
+ }
+ sleep(2);
+ } else {
+ if (debugflag)
+ printf("dlio.req_type is %d bd %d\n",
+ dlio.req_type,dlio.bdid);
+
+ switch(dlio.req_type) {
+ case DLREQ_BIOS:
+ /*
+ ** find the bios image for this type
+ */
+ for ( x = 0; x < nimages; x++ ) {
+ if(image_list[x].type != IBIOS)
+ continue;
+
+ if ((dlio.image.fi.type & FAMILY) ==
+ image_list[x].family) {
+
+ if ( image_list[x].family == T_CX ) {
+ if ((dlio.image.fi.type & BUSTYPE)
+ == T_PCIBUS ) {
+ if ( image_list[x].subtype
+ == T_PCIBUS )
+ break;
+ }
+ else {
+ break;
+ }
+ }
+ else if ( image_list[x].family == T_EPC ) {
+ /* If subtype of image is T_PCIBUS, it is */
+ /* a PCI EPC image, so the board must */
+ /* have bus type T_PCIBUS to match */
+ if ((dlio.image.fi.type & BUSTYPE)
+ == T_PCIBUS ) {
+ if ( image_list[x].subtype
+ == T_PCIBUS )
+ break;
+ }
+ else {
+ /* NON PCI EPC doesn't use PCI image */
+ if ( image_list[x].subtype
+ != T_PCIBUS )
+ break;
+ }
+ }
+ else
+ break;
+ }
+ else if ((dlio.image.fi.type & SUBTYPE) == image_list[x].subtype) {
+ /* PCXR board will break out of the loop here */
+ if ( image_list[x].subtype == T_PCXR ) {
+ break;
+ }
+ }
+ }
+
+ if ( x >= nimages) {
+ /*
+ ** no valid images exist
+ */
+ if(nodldprint) {
+ fprintf(stderr,
+ "%s: cannot find correct BIOS image\n",
+ pgm);
+ nodldprint = 0;
+ }
+ dlio.image.fi.type = -1;
+ if (ioctl(fd, DIGI_DLREQ_SET, &dlio) == -1) {
+ if (errorprint) {
+ fprintf(stderr,
+ "%s: warning - download ioctl failed\n",
+ pgm);
+ errorprint = 0;
+ }
+ sleep(2);
+ }
+ break;
+ }
+ squirt(dlio.req_type, dlio.bdid, &image_list[x]);
+ break ;
+
+ case DLREQ_FEP:
+ /*
+ ** find the fep image for this type
+ */
+ for ( x = 0; x < nimages; x++ ) {
+ if(image_list[x].type != IFEP)
+ continue;
+ if( (dlio.image.fi.type & FAMILY) ==
+ image_list[x].family ) {
+ if ( image_list[x].family == T_CX ) {
+ /* C/X PCI board */
+ if ((dlio.image.fi.type & BUSTYPE)
+ == T_PCIBUS ) {
+ if ( image_list[x].subtype
+ == T_PCIBUS )
+ break;
+ }
+ else {
+ /* Regular CX */
+ break;
+ }
+ }
+ else if ( image_list[x].family == T_EPC ) {
+ /* If subtype of image is T_PCIBUS, it is */
+ /* a PCI EPC image, so the board must */
+ /* have bus type T_PCIBUS to match */
+ if ((dlio.image.fi.type & BUSTYPE)
+ == T_PCIBUS ) {
+ if ( image_list[x].subtype
+ == T_PCIBUS )
+ break;
+ }
+ else {
+ /* NON PCI EPC doesn't use PCI image */
+ if ( image_list[x].subtype
+ != T_PCIBUS )
+ break;
+ }
+ }
+ else
+ break;
+ }
+ else if ((dlio.image.fi.type & SUBTYPE) == image_list[x].subtype) {
+ /* PCXR board will break out of the loop here */
+ if ( image_list[x].subtype == T_PCXR ) {
+ break;
+ }
+ }
+ }
+
+ if ( x >= nimages) {
+ /*
+ ** no valid images exist
+ */
+ if(nodldprint) {
+ fprintf(stderr,
+ "%s: cannot find correct FEP image\n",
+ pgm);
+ nodldprint = 0;
+ }
+ dlio.image.fi.type=-1;
+ if( ioctl(fd,DIGI_DLREQ_SET,&dlio) == -1 ) {
+ if(errorprint) {
+ fprintf(stderr,
+ "%s: warning - download ioctl failed\n",
+ pgm);
+ errorprint=0;
+ }
+ sleep(2);
+ }
+ break;
+ }
+ squirt(dlio.req_type, dlio.bdid, &image_list[x]);
+ break;
+
+ case DLREQ_DEVCREATE:
+ {
+ char string[1024];
+#if 0
+ sprintf(string, "%s /proc/dgap/%d/mknod", DEFSHELL, dlio.bdid);
+#endif
+ sprintf(string, "%s /usr/sbin/dgap_updatedevs %d", DEFSHELL, dlio.bdid);
+ system(string);
+
+ if (debugflag)
+ printf("Created Devices.\n");
+ if (ioctl(fd, DIGI_DLREQ_SET, &dlio) == -1 ) {
+ if(errorprint) {
+ fprintf(stderr, "%s: warning - DEVCREATE ioctl failed\n",pgm);
+ errorprint = 0;
+ }
+ sleep(2);
+ }
+ if (debugflag)
+ printf("After ioctl set - Created Device.\n");
+ }
+
+ break;
+
+ case DLREQ_CONFIG:
+ for ( x = 0; x < nimages; x++ ) {
+ if(image_list[x].type != ICONFIG)
+ continue;
+ else
+ break;
+ }
+
+ if ( x >= nimages) {
+ /*
+ ** no valid images exist
+ */
+ if(nodldprint) {
+ fprintf(stderr,
+ "%s: cannot find correct CONFIG image\n",
+ pgm);
+ nodldprint = 0;
+ }
+ dlio.image.fi.type=-1;
+ if (ioctl(fd, DIGI_DLREQ_SET, &dlio) == -1 ) {
+ if(errorprint) {
+ fprintf(stderr,
+ "%s: warning - download ioctl failed\n",
+ pgm);
+ errorprint=0;
+ }
+ sleep(2);
+ }
+ break;
+ }
+
+ squirt(dlio.req_type, dlio.bdid, &image_list[x]);
+ break;
+
+ case DLREQ_CONC:
+ /*
+ ** find the image needed for this download
+ */
+ if ( dp->dl_seq == 0 ) {
+ /*
+ ** find image for hardware rev range
+ */
+ for ( x = 0; x < nimages; x++ ) {
+ ii=&image_list[x];
+
+ if(image_list[x].type != ICONC)
+ continue;
+
+ consider_file_rescan(ii) ;
+
+ ip = (struct downld_t *) image_list[x].image;
+ if (ip == NULL) continue;
+
+ /*
+ * When I removed Clusterport, I kept only the
+ * code that I was SURE wasn't ClusterPort.
+ * We may not need the next four lines of code.
+ */
+
+ if ((dp->dl_type != 'P' ) &&
+ (ip->dl_lrev <= dp->dl_lrev ) &&
+ ( dp->dl_lrev <= ip->dl_hrev))
+ break;
+ }
+
+ if ( x >= nimages ) {
+ /*
+ ** No valid images exist
+ */
+ if(nodldprint) {
+ fprintf(stderr,
+ "%s: cannot find correct download image %d\n",
+ pgm, dp->dl_lrev);
+ nodldprint=0;
+ }
+ continue;
+ }
+
+ } else {
+ /*
+ ** find image version required
+ */
+ if ((ii = find_conc_image()) == NULL ) {
+ /*
+ ** No valid images exist
+ */
+ fprintf(stderr,
+ "%s: can't find rest of download image??\n",
+ pgm);
+ continue;
+ }
+ }
+
+ /*
+ ** download block of image
+ */
+
+ offset = 1024 * dp->dl_seq;
+
+ /*
+ ** test if block requested within image
+ */
+ if ( offset < ii->len ) {
+
+ /*
+ ** if it is, determine block size, set segment,
+ ** set size, set pointers, and copy block
+ */
+ if (( bsize = ii->len - offset ) > 1024 )
+ bsize = 1024;
+
+ /*
+ ** copy image version info to download area
+ */
+ dp->dl_srev = ip->dl_srev;
+ dp->dl_lrev = ip->dl_lrev;
+ dp->dl_hrev = ip->dl_hrev;
+
+ dp->dl_seg = (64 * dp->dl_seq) + ip->dl_seg;
+ dp->dl_size = bsize;
+
+ down = (char *)&dp->dl_data[0];
+ image = (char *)((char *)ip + offset);
+
+ memcpy(down, image, bsize);
+ }
+ else {
+ /*
+ ** Image has been downloaded, set segment and
+ ** size to indicate no more blocks
+ */
+ dp->dl_seg = ip->dl_seg;
+ dp->dl_size = 0;
+
+ /* Now, we can release the concentrator */
+ /* image from memory if we're running */
+ /* from filesystem images */
+
+ if (ii->pathname)
+ if (ii->image) {
+ free(ii->image);
+ ii->image = NULL ;
+ }
+ }
+
+ if (debugflag)
+ printf(
+ "sending conc dl section %d to %s from %s\n",
+ dp->dl_seq, ii->name,
+ ii->pathname ? ii->pathname : "Internal Image");
+
+ if (ioctl(fd, DIGI_DLREQ_SET, &dlio) == -1 ) {
+ if (errorprint) {
+ fprintf(stderr,
+ "%s: warning - download ioctl failed\n",
+ pgm);
+ errorprint=0;
+ }
+ sleep(2);
+ }
+ break;
+ } /* switch */
+ }
+ if (debugflag > 1) {
+ printf("pausing: "); fflush(stdout);
+ fflush(stdin);
+ while(getchar() != '\n');
+ printf("continuing\n");
+ }
+ }
+}
+
+/*
+** myperror()
+**
+** Same as normal perror(), but places the program name at the begining
+** of the message.
+*/
+void myperror(char *s)
+{
+ fprintf(stderr,"%s: %s: %s.\n",pgm, s, strerror(errno));
+}