/*
* 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 different 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.