diff options
Diffstat (limited to 'drivers/staging/dgap/downld.c')
-rw-r--r-- | drivers/staging/dgap/downld.c | 798 |
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)); +} |