/* * Copyright 2003 Digi International (www.digi.com) * Scott H Kilau * * 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 #include #include #include #include #include #include "dgap_types.h" #include "digi.h" #include "dgap_fep5.h" #include "dgap_downld.h" #include #include #include #include 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)); }