/*  Copyright 1986-1992 Emmet P. Gray.
 *  Copyright 1996-2003,2006,2007,2009 Alain Knaff.
 *  This file is part of mtools.
 *
 *  Mtools 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 3 of the License, or
 *  (at your option) any later version.
 *
 *  Mtools 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 Mtools.  If not, see <http://www.gnu.org/licenses/>.
 */

/*
 * Device tables.  See the Configure file for a complete description.
 */

#define NO_TERMIO
#include "sysincludes.h"
#include "mtools.h"
#include "devices.h"

#define INIT_NOOP

#define DEF_ARG1(x) (x), 0x2,0,(char *)0, 0, 0, 0, 0, 0, 0, NULL
#define DEF_ARG0(x) 0,DEF_ARG1(x)

#define MDEF_ARG 0L,DEF_ARG0(MFORMAT_ONLY_FLAG)
#define FDEF_ARG 0L,DEF_ARG0(0)

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-macros"

#define VOLD_DEF_ARG 0L,DEF_ARG0(VOLD_FLAG|MFORMAT_ONLY_FLAG)

#define MED312	12,0,80,2,36,0,MDEF_ARG /* 3 1/2 extra density */
#define MHD312	12,0,80,2,18,0,MDEF_ARG /* 3 1/2 high density */
#define MDD312	12,0,80,2, 9,0,MDEF_ARG /* 3 1/2 double density */
#define MHD514	12,0,80,2,15,0,MDEF_ARG /* 5 1/4 high density */
#define MDD514	12,0,40,2, 9,0,MDEF_ARG /* 5 1/4 double density (360k) */
#define MSS514	12,0,40,1, 9,0,MDEF_ARG /* 5 1/4 single sided DD, (180k) */
#define MDDsmall	12,0,40,2, 8,0,MDEF_ARG /* 5 1/4 double density (320k) */
#define MSSsmall	12,0,40,1, 8,0,MDEF_ARG /* 5 1/4 single sided DD, (160k) */

#define FED312	12,0,80,2,36,0,FDEF_ARG /* 3 1/2 extra density */
#define FHD312	12,0,80,2,18,0,FDEF_ARG /* 3 1/2 high density */
#define FDD312	12,0,80,2, 9,0,FDEF_ARG /* 3 1/2 double density */
#define FHD514	12,0,80,2,15,0,FDEF_ARG /* 5 1/4 high density */
#define FDD514	12,0,40,2, 9,0,FDEF_ARG /* 5 1/4 double density (360k) */
#define FSS514	12,0,40,1, 9,0,FDEF_ARG /* 5 1/4 single sided DD, (180k) */
#define FDDsmall	12,0,40,2, 8,0,FDEF_ARG /* 5 1/4 double density (320k) */
#define FSSsmall	12,0,40,1, 8,0,FDEF_ARG /* 5 1/4 single sided DD, (160k) */

#define GENHD	16,0, 0,0, 0,0,MDEF_ARG /* Generic 16 bit FAT fs */
#define GENFD	12,0,80,2,18,0,MDEF_ARG /* Generic 12 bit FAT fs */
#define VOLDFD	12,0,80,2,18,0,VOLD_DEF_ARG /* Generic 12 bit FAT fs with vold */
#define GEN    	 0,0, 0,0, 0,0,MDEF_ARG /* Generic fs of any FAT bits */

#define ZIPJAZ(x,c,h,s,y) 16,(x),(c),(h),(s),(s),0L, 4, \
		DEF_ARG1((y)|MFORMAT_ONLY_FLAG) /* Jaz disks */

#define JAZ(x)	 ZIPJAZ(x,1021, 64, 32, 0)
#define RJAZ(x)	 ZIPJAZ(x,1021, 64, 32, SCSI_FLAG|PRIV_FLAG)
#define ZIP(x)	 ZIPJAZ(x,96, 64, 32, 0)
#define RZIP(x)	 ZIPJAZ(x,96, 64, 32, SCSI_FLAG|PRIV_FLAG)

#pragma GCC diagnostic pop

#define REMOTE    {"$DISPLAY", 'X', 0,0, 0,0, 0,0,0L, DEF_ARG0(FLOPPYD_FLAG)}

#define devices const_devices

#if (!defined(predefined_devices) && defined (CPU_m68000) && defined (OS_sysv))

#undef F_LOCK
/* F_LOCK is doubly defined!, both in unistd.h and sys/gdisk.h , with
   different value! */

#include <sys/gdioctl.h>

#define predefined_devices
struct device devices[] = {
	{"/dev/rfp020",		'A', 0, O_NDELAY, 40, 2, 9, 0, FDEF_ARG },
	{"/usr/bin/DOS/dvd000", 'C', GENFD },
	REMOTE
};

#undef INIT_NOOP
int init_geom(int fd, struct device *dev, struct device *orig_dev,
	      struct MT_STAT *statbuf)
{
	struct gdctl gdbuf;

	if (ioctl(fd, GDGETA, &gdbuf) == -1) {
		ioctl(fd, GDDISMNT, &gdbuf);
		return 1;
	}
	SET_INT(gdbuf.params.cyls,dev->tracks);
	SET_INT(gdbuf.params.heads,dev->heads);
	SET_INT(gdbuf.params.psectrk,dev->sectors);
	dev->tracks = gdbuf.params.cyls;
	dev->heads = gdbuf.params.heads;
	dev->sectors = gdbuf.params.psectrk;

	gdbuf.params.pseccyl = gdbuf.params.psectrk * gdbuf.params.heads;
	gdbuf.params.flags = 1;		/* disk type flag */
	gdbuf.params.step = 0;		/* step rate for controller */
	gdbuf.params.sectorsz = 512;	/* sector size */

	/* On AT&T UnixPC, the GDSETA needs to be performed unconditionally
	 * even if parameters didn't actually change, or else the driver
	 * forbids reading/writing to the device
	 */
	if (ioctl(fd, GDSETA, &gdbuf) < 0) {
		ioctl(fd, GDDISMNT, &gdbuf);
		return(1);
	}
	return(0);
}
#endif /* (defined (m68000) && defined (sysv))*/


#if defined(INIT_GENERIC) || defined(INIT_NOOP)
static int compare_geom(struct device *dev, struct device *orig_dev)
{
	if(IS_MFORMAT_ONLY(orig_dev))
		return 0; /* geometry only for mformatting ==> ok */
	if(!orig_dev || !orig_dev->tracks || !dev || !dev->tracks)
		return 0; /* no original device. This is ok */
	return(orig_dev->tracks != dev->tracks ||
	       orig_dev->heads != dev->heads ||
	       orig_dev->sectors  != dev->sectors);
}
#endif



#ifdef __CYGWIN__
#define predefined_devices
struct device devices[] = {
   {"\\\\\\\\.\\\\A:", 'A', GENFD },
};
#endif /* CYGWIN */


#ifdef OS_aux
#define predefined_devices
struct device devices[] = {
   {"/dev/floppy0", 'A', GENFD },
   {"/dev/rdsk/c104d0s31", 'J', JAZ(O_EXCL) },
   {"/dev/rdsk/c105d0s31", 'Z', ZIP(O_EXCL) },
   REMOTE
};
#endif /* aux */


#ifdef OS_lynxos
#define predefined_devices
struct device devices[] = {
	{"/dev/fd1440.0", 	'A', MHD312 },
	REMOTE
};
#endif


#ifdef __BEOS__
#define predefined_devices
struct device devices[] = {
	{"/dev/disk/floppy/raw", 	'A', MHD312 },
	REMOTE
};
#endif /* BEBOX */


#ifdef OS_hpux

#define predefined_devices
struct device devices[] = {
#ifdef OS_hpux10
/* hpux10 uses different device names according to Frank Maritato
 * <frank@math.hmc.edu> */
	{"/dev/floppy/c0t0d0",		'A', MHD312 },
	{"/dev/floppy/c0t0d1",		'B', MHD312 }, /* guessed by me */
 	{"/dev/rscsi",			'C', GENHD }, /* guessed by me */
#else
/* Use rfloppy, according to Simao Campos <simao@iris.ctd.comsat.com> */
	{"/dev/rfloppy/c201d0s0",	'A', FHD312 },
	{"/dev/rfloppy/c20Ad0s0", 	'A', FHD312 },
 	{"/dev/rfloppy/c201d1s0",	'B', FHD312 },
 	{"/dev/rfloppy/c20Ad1s0",	'B', FHD312 },
 	{"/dev/rscsi",			'C', GENHD },
#endif
	{"/dev/rdsk/c201d4",		'J', RJAZ(O_EXCL) },
	{"/dev/rdsk/c201d4s0",		'J', RJAZ(O_EXCL) },
	{"/dev/rdsk/c201d5",		'Z', RZIP(O_EXCL) },
	{"/dev/rdsk/c201d5s0",		'Z', RZIP(O_EXCL) },
	REMOTE
};

#ifdef HAVE_SYS_FLOPPY
/* geometry setting ioctl's contributed by Paolo Zeppegno
 * <paolo@to.sem.it>, may cause "Not a typewriter" messages on other
 * versions according to support@vital.com */

#include <sys/floppy.h>
#undef SSIZE

struct generic_floppy_struct
{
  struct floppy_geometry fg;
};

#define BLOCK_MAJOR 24
#define CHAR_MAJOR 112

static inline int get_parameters(int fd, struct generic_floppy_struct *floppy)
{
	if (ioctl(fd, FLOPPY_GET_GEOMETRY, &(floppy->fg)) != 0) {
		perror("FLOPPY_GET_GEOMETRY");
		return(1);
	}

	return 0;
}

#define TRACKS(floppy) floppy.fg.tracks
#define HEADS(floppy) floppy.fg.heads
#define SECTORS(floppy) floppy.fg.sectors
#define FD_SECTSIZE(floppy) floppy.fg.sector_size
#define FD_SET_SECTSIZE(floppy,v) { floppy.fg.sector_size = v; }

static inline int set_parameters(int fd, struct generic_floppy_struct *floppy,
				 struct MT_STAT *buf)
{
	if (ioctl(fd, FLOPPY_SET_GEOMETRY, &(floppy->fg)) != 0) {
		perror("");
		return(1);
	}

	return 0;
}
#define INIT_GENERIC
#endif

#endif /* hpux */


#if (defined(OS_sinix) || defined(VENDOR_sni) || defined(SNI))
#define predefined_devices
struct device devices[] = {
#ifdef CPU_mips     /* for Siemens Nixdorf's  SINIX-N/O (mips) 5.4x SVR4 */
	{ "/dev/at/flp/f0t",    'A', FHD312},
	{ "/dev/fd0",           'A', GENFD},
#else
#ifdef CPU_i386     /* for Siemens Nixdorf's  SINIX-D/L (intel) 5.4x SVR4 */
	{ "/dev/fd0135ds18",	'A', FHD312},
	{ "/dev/fd0135ds9",	'A', FDD312},
	{ "/dev/fd0",		'A', GENFD},
	{ "/dev/fd1135ds15",	'B', FHD514},
	{ "/dev/fd1135ds9",	'B', FDD514},
	{ "/dev/fd1",		'B', GENFD},
#endif /* CPU_i386 */
#endif /*mips*/
	REMOTE
};
#endif

#ifdef OS_ultrix
#define predefined_devices
struct device devices[] = {
	{"/dev/rfd0a",		'A', GENFD}, /* guessed */
	{"/dev/rfd0c",		'A', GENFD}, /* guessed */
	REMOTE
};

#endif


#ifdef OS_isc
#define predefined_devices
#if (defined(OS_isc2) && defined(OLDSTUFF))
struct device devices[] = {
	{"/dev/rdsk/f0d9dt",   	'A', FDD514},
	{"/dev/rdsk/f0q15dt",	'A', FHD514},
	{"/dev/rdsk/f0d8dt",	'A', FDDsmall},
	{"/dev/rdsk/f13ht",	'B', FHD312},
	{"/dev/rdsk/f13dt",	'B', FDD312},
	{"/dev/rdsk/0p1",	'C', GENHD},
	{"/usr/vpix/defaults/C:",'D',12, 0, 0, 0, 0,8704L,DEF_ARG0},
	{"$HOME/vpix/C:", 	'E', 12, 0, 0, 0, 0,8704L,MDEF_ARG},
	REMOTE
};
#else
/* contributed by larry.jones@sdrc.com (Larry Jones) */
struct device devices[] = {
	{"/dev/rfd0",		'A', GEN},
	{"/dev/rfd1",		'B', GEN},
	{"/dev/rdsk/0p1",	'C', GEN},
	{"/usr/vpix/defaults/C:",'D', GEN, 1},
	{"$HOME/vpix/C:", 	'E', GEN, 1},
	REMOTE
};

#include <sys/vtoc.h>
#include <sys/sysmacros.h>
#undef SSIZE
#define BLOCK_MAJOR 1
#define CHAR_MAJOR  1
#define generic_floppy_struct disk_parms
int ioctl(int, int, void *);

static int get_parameters(int fd, struct generic_floppy_struct *floppy)
{
	mt_off_t off;
	char buf[512];

	off = lseek(fd, 0, SEEK_CUR);
	if(off < 0) {
		perror("device seek 1");
		exit(1);
	}
	if (off == 0) {
		/* need to read at least 1 sector to get correct info */
		read(fd, buf, sizeof buf);
		if(lseek(fd, 0, SEEK_SET) < 0) {
			perror("device seek 2");
			exit(1);
		}
	}
	return ioctl(fd, V_GETPARMS, floppy);
}

#define TRACKS(floppy)  (floppy).dp_cyls
#define HEADS(floppy)   (floppy).dp_heads
#define SECTORS(floppy) (floppy).dp_sectors
#define FD_SECTSIZE(floppy) (floppy).dp_secsiz
#define FD_SET_SECTSIZE(floppy,v) { (floppy).dp_secsiz = (v); }

static int set_parameters(int fd, struct generic_floppy_struct *floppy,
	struct MT_STAT *buf)
{
	return 1;
}

#define INIT_GENERIC
#endif
#endif /* isc */

#ifdef CPU_i370
#define predefined_devices
struct device devices[] = {
	{"/dev/rfd0", 'A', GENFD},
	REMOTE
};
#endif /* CPU_i370 */

#ifdef OS_aix
/* modified by Federico Bianchi */
#define predefined_devices
struct device devices[] = {
	{"/dev/fd0",'A',GENFD},
	REMOTE
};
#endif /* aix */


#ifdef OS_osf4
/* modified by Chris Samuel <chris@rivers.dra.hmg.gb> */
#define predefined_devices
struct device devices[] = {
	{"/dev/fd0c",'A',GENFD},
	REMOTE
};
#endif /* OS_osf4 */


#ifdef OS_solaris

#ifdef USING_NEW_VOLD

char *alias_name = NULL;

extern char *media_oldaliases(char *);
extern char *media_findname(char *);

char *getVoldName(struct device *dev, char *name)
{
	char *rname;

	if(!SHOULD_USE_VOLD(dev))
		return name;

	/***
	 * Solaris specific routines to use the volume management
	 * daemon and libraries to get the correct device name...
	 ***/
	rname = media_findname(name);
#ifdef HAVE_MEDIA_OLDALIASES
	if (rname == NULL) {
		if ((alias_name = media_oldaliases(name)) != NULL)
			rname = media_findname(alias_name);
	}
#endif
	if (rname == NULL) {
		fprintf(stderr,
				"No such volume or no media in device: %s.\n",
				name);
		exit(1);
	}
	return rname;
}
#endif /* USING_NEW_VOLD */

#define predefined_devices
struct device devices[] = {
#ifdef  USING_NEW_VOLD
	{"floppy", 'A', VOLDFD },
#elif	USING_VOLD
	{"/vol/dev/aliases/floppy0", 'A', GENFD},
	{"/dev/rdiskette", 'B', GENFD},
#else	/* ! USING_VOLD */
	{"/dev/rdiskette", 'A', GENFD},
	{"/vol/dev/aliases/floppy0", 'B', GENFD},
#endif	/* USING_VOLD */
	{"/dev/rdsk/c0t4d0s2", 'J', RJAZ(O_NDELAY)},
	{"/dev/rdsk/c0t5d0s2", 'Z', RZIP(O_NDELAY)},
	REMOTE
};



#ifdef HAVE_SYS_FDIO_H
/*
 * Ofer Licht <ofer@stat.Berkeley.EDU>, May 14, 1997.
 */

#define INIT_GENERIC

#include <sys/fdio.h>
#include <sys/mkdev.h>	/* for major() */

struct generic_floppy_struct
{
  struct fd_char fdchar;
};

#define BLOCK_MAJOR 36
#define CHAR_MAJOR 36

static inline int get_parameters(int fd, struct generic_floppy_struct *floppy)
{
	if (ioctl(fd, FDIOGCHAR, &(floppy->fdchar)) != 0) {
		perror("");
		ioctl(fd, FDEJECT, NULL);
		return(1);
	}
	return 0;
}

#define TRACKS(floppy) floppy.fdchar.fdc_ncyl
#define HEADS(floppy) floppy.fdchar.fdc_nhead
#define SECTORS(floppy) floppy.fdchar.fdc_secptrack
/* SECTORS_PER_DISK(floppy) not used */
#define FD_SECTSIZE(floppy) floppy.fdchar.fdc_sec_size
#define FD_SET_SECTSIZE(floppy,v) { floppy.fdchar.fdc_sec_size = v; }

static inline int set_parameters(int fd, struct generic_floppy_struct *floppy,
				 struct MT_STAT *buf UNUSEDP)
{
	if (ioctl(fd, FDIOSCHAR, &(floppy->fdchar)) != 0) {
		ioctl(fd, FDEJECT, NULL);
		perror("");
		return(1);
	}
	return 0;
}
#endif
#endif /* solaris */

#ifdef OS_sunos3
#define predefined_devices
struct device devices[] = {
	{"/dev/rfdl0c",	'A', FDD312},
	{"/dev/rfd0c",	'A', FHD312},
	REMOTE
};
#endif /* OS_sunos3 */

#ifdef OS_xenix
#define predefined_devices
struct device devices[] = {
	{"/dev/fd096ds15",	'A', FHD514},
	{"/dev/fd048ds9",	'A', FDD514},
	{"/dev/fd1135ds18",	'B', FHD312},
	{"/dev/fd1135ds9",	'B', FDD312},
	{"/dev/hd0d",		'C', GENHD},
	REMOTE
};
#endif /* OS_xenix */

#ifdef OS_sco
#define predefined_devices
struct device devices[] = {
	{ "/dev/fd0135ds18",	'A', FHD312},
	{ "/dev/fd0135ds9",	'A', FDD312},
	{ "/dev/fd0",		'A', GENFD},
	{ "/dev/fd1135ds15",	'B', FHD514},
	{ "/dev/fd1135ds9",	'B', FDD514},
	{ "/dev/fd1",		'B', GENFD},
	{ "/dev/hd0d",		'C', GENHD},
	REMOTE
};
#endif /* OS_sco */


#ifdef OS_irix
#define predefined_devices
struct device devices[] = {
  { "/dev/rdsk/fds0d2.3.5hi",	'A', FHD312},
  { "/dev/rdsk/fds0d2.3.5",	'A', FDD312},
  { "/dev/rdsk/fds0d2.96",	'A', FHD514},
  {"/dev/rdsk/fds0d2.48",	'A', FDD514},
  REMOTE
};
#endif /* OS_irix */


#ifdef OS_sunos4
#include <sys/ioctl.h>
#include <sun/dkio.h>

#define predefined_devices
struct device devices[] = {
	{"/dev/rfd0c",	'A', GENFD},
	{"/dev/rsd4c",	'J', RJAZ(O_NDELAY)},
	{"/dev/rsd5c",	'Z', RZIP(O_NDELAY)},
	REMOTE
};

/*
 * Stuffing back the floppy parameters into the driver allows for gems
 * like 10 sector or single sided floppies from Atari ST systems.
 *
 * Martin Schulz, Universite de Moncton, N.B., Canada, March 11, 1991.
 */

#define INIT_GENERIC

struct generic_floppy_struct
{
  struct fdk_char dkbuf;
  struct dk_map dkmap;
};

#define BLOCK_MAJOR 16
#define CHAR_MAJOR 54

static inline int get_parameters(int fd, struct generic_floppy_struct *floppy)
{
	if (ioctl(fd, DKIOCGPART, &(floppy->dkmap)) != 0) {
		perror("DKIOCGPART");
		ioctl(fd, FDKEJECT, NULL);
		return(1);
	}

	if (ioctl(fd, FDKIOGCHAR, &( floppy->dkbuf)) != 0) {
		perror("");
		ioctl(fd, FDKEJECT, NULL);
		return(1);
	}
	return 0;
}

#define TRACKS(floppy) floppy.dkbuf.ncyl
#define HEADS(floppy) floppy.dkbuf.nhead
#define SECTORS(floppy) floppy.dkbuf.secptrack
#define SECTORS_PER_DISK(floppy) floppy.dkmap.dkl_nblk
#define FD_SECTSIZE(floppy) floppy.dkbuf.sec_size
#define FD_SET_SECTSIZE(floppy,v) { floppy.dkbuf.sec_size = v; }

static inline int set_parameters(int fd, struct generic_floppy_struct *floppy,
				 struct MT_STAT *buf)
{
	if (ioctl(fd, FDKIOSCHAR, &(floppy->dkbuf)) != 0) {
		ioctl(fd, FDKEJECT, NULL);
		perror("");
		return(1);
	}

	if (ioctl(fd, ( unsigned int) DKIOCSPART, &(floppy->dkmap)) != 0) {
		ioctl(fd, FDKEJECT, NULL);
		perror("");
		return(1);
	}
	return 0;
}
#define INIT_GENERIC
#endif /* sparc && sunos */


#ifdef DPX1000
#define predefined_devices
struct device devices[] = {
	/* [block device]: DPX1000 has /dev/flbm60, DPX2 has /dev/easyfb */
	{"/dev/flbm60", 'A', MHD514};
	{"/dev/flbm60", 'B', MDD514},
	{"/dev/flbm60", 'C', MDDsmall},
	{"/dev/flbm60", 'D', MSS},
	{"/dev/flbm60", 'E', MSSsmall},
	REMOTE
};
#endif /* DPX1000 */

#ifdef OS_bosx
#define predefined_devices
struct device devices[] = {
	/* [block device]: DPX1000 has /dev/flbm60, DPX2 has /dev/easyfb */
	{"/dev/easyfb", 'A', MHD514},
	{"/dev/easyfb", 'B', MDD514},
	{"/dev/easyfb", 'C', MDDsmall},
	{"/dev/easyfb", 'D', MSS},
	{"/dev/easyfb", 'E', MSSsmall},
	REMOTE
};
#endif /* OS_bosx */

#ifdef OS_linux

static const char *error_msg[22]={
"Missing Data Address Mark",
"Bad cylinder",
"Scan not satisfied",
"Scan equal hit",
"Wrong cylinder",
"CRC error in data field",
"Control Mark = deleted",
0,

"Missing Address Mark",
"Write Protect",
"No Data - unreadable",
0,
"OverRun",
"CRC error in data or address",
0,
"End Of Cylinder",

0,
0,
0,
"Not ready",
"Equipment check error",
"Seek end" };


static __inline__ void print_message(RawRequest_t *raw_cmd,const char *message)
{
	int i, code;
	if(!message)
		return;

	fprintf(stderr,"   ");
	for (i=0; i< raw_cmd->cmd_count; i++)
		fprintf(stderr,"%2.2x ",
			(int)raw_cmd->cmd[i] );
	fprintf(stderr,"\n");
	for (i=0; i< raw_cmd->reply_count; i++)
		fprintf(stderr,"%2.2x ",
			(int)raw_cmd->reply[i] );
	fprintf(stderr,"\n");
	code = (raw_cmd->reply[0] <<16) +
		(raw_cmd->reply[1] << 8) +
		raw_cmd->reply[2];
	for(i=0; i<22; i++){
		if ((code & (1 << i)) && error_msg[i])
			fprintf(stderr,"%s\n",
				error_msg[i]);
	}
}


/* return values:
 *  -1: Fatal error, don't bother retrying.
 *   0: OK
 *   1: minor error, retry
 */

int send_one_cmd(int fd, RawRequest_t *raw_cmd, const char *message)
{
	if (ioctl( fd, FDRAWCMD, raw_cmd) >= 0) {
		if (raw_cmd->reply_count < 7) {
			fprintf(stderr,"Short reply from FDC\n");
			return -1;
		}
		return 0;
	}

	switch(errno) {
		case EBUSY:
			fprintf(stderr, "FDC busy, sleeping for a second\n");
			sleep(1);
			return 1;
		case EIO:
			fprintf(stderr,"resetting controller\n");
			if(ioctl(fd, FDRESET, 2)  < 0){
				perror("reset");
				return -1;
			}
			return 1;
		default:
			perror(message);
			return -1;
	}
}


/*
 * return values
 *  -1: error
 *   0: OK, last sector
 *   1: more raw commands follow
 */

int analyze_one_reply(RawRequest_t *raw_cmd, int *bytes, int do_print)
{

	if(raw_cmd->reply_count == 7) {
		int end;

		if (raw_cmd->reply[3] != raw_cmd->cmd[2]) {
			/* end of cylinder */
			end = raw_cmd->cmd[6] + 1;
		} else {
			end = raw_cmd->reply[5];
		}

		*bytes = end - raw_cmd->cmd[4];
		/* FIXME: over/under run */
		*bytes = *bytes << (7 + raw_cmd->cmd[5]);
	} else
		*bytes = 0;

	switch(raw_cmd->reply[0] & 0xc0){
		case 0x40:
			if ((raw_cmd->reply[0] & 0x38) == 0 &&
			    (raw_cmd->reply[1]) == 0x80 &&
			    (raw_cmd->reply[2]) == 0) {
				*bytes += 1 << (7 + raw_cmd->cmd[5]);
				break;
			}

			if ( raw_cmd->reply[1] & ST1_WP ){
				*bytes = 0;
				fprintf(stderr,
					"This disk is write protected\n");
				return -1;
			}
			if(!*bytes && do_print)
				print_message(raw_cmd, "");
			return -1;
		case 0x80:
			*bytes = 0;
			fprintf(stderr,
				"invalid command given\n");
			return -1;
		case 0xc0:
			*bytes = 0;
			fprintf(stderr,
				"abnormal termination caused by polling\n");
			return -1;
		default:
			break;
	}
#ifdef FD_RAW_MORE
	if(raw_cmd->flags & FD_RAW_MORE)
		return 1;
#endif
	return 0;
}

#define predefined_devices
struct device devices[] = {
	{"/dev/fd0", 'A', 0, 0, 80,2, 18,0, MDEF_ARG },
	{"/dev/fd1", 'B', 0, 0, 0,0, 0,0, FDEF_ARG },
	/* we assume that the Zip or Jaz drive is the second on the SCSI bus */
	{"/dev/sdb4",'J', GENHD },
	{"/dev/sdb4",'Z', GENHD },
	/*	{"/dev/sda4",'D', GENHD },*/
	REMOTE
};

/*
 * Stuffing back the floppy parameters into the driver allows for gems
 * like 21 sector or single sided floppies from Atari ST systems.
 *
 * Alain Knaff, Université Joseph Fourier, France, November 12, 1993.
 */


#define INIT_GENERIC
#define generic_floppy_struct floppy_struct
#define BLOCK_MAJOR 2
#define SECTORS(floppy) floppy.sect
#define TRACKS(floppy) floppy.track
#define HEADS(floppy) floppy.head
#define SECTORS_PER_DISK(floppy) floppy.size
#define STRETCH(floppy) floppy.stretch
#define USE_2M(floppy) ((floppy.rate & FD_2M) ? 0xff : 0x80 )
#define SSIZE(floppy) ((((floppy.rate & 0x38) >> 3 ) + 2) % 8)

static __inline__ void set_2m(struct floppy_struct *floppy, unsigned int value)
{
	uint8_t v;
	if (value & 0x7f)
		v = FD_2M;
	else
		v = 0;
	floppy->rate = (floppy->rate & ~FD_2M) | v;
}
#define SET_2M set_2m

static __inline__ void set_ssize(struct floppy_struct *floppy, int value)
{
	uint8_t v = (uint8_t) ((( (value & 7) + 6 ) % 8) << 3);

	floppy->rate = (floppy->rate & ~0x38) | v;
}

#define SET_SSIZE set_ssize

static __inline__ int set_parameters(int fd, struct floppy_struct *floppy,
				     struct MT_STAT *buf)
{
	if ( ( MINOR(buf->st_rdev ) & 0x7f ) > 3 )
		return 1;

	return ioctl(fd, FDSETPRM, floppy);
}

static __inline__ int get_parameters(int fd, struct floppy_struct *floppy)
{
	return ioctl(fd, FDGETPRM, floppy);
}

#include "linux/hdreg.h"
#include "linux/fs.h"

static uint32_t ulong_to_sectors(unsigned long raw_sect) {
	/* Number of sectors must fit into 32bit value */
	if (raw_sect > ULONG_MAX) {
		fprintf(stderr, "Too many sectors for FAT %8lx\n",raw_sect);
		exit(1);
	}
	return (uint32_t) raw_sect;
}

int get_sector_size(int fd) {
	int sec_size;
	if (ioctl(fd, BLKSSZGET, &sec_size) != 0 || sec_size <= 0) {
		fprintf(stderr, "Could not get sector size of device (%s)",
			strerror(errno));
		return -1;
	}

	/* Cap sector size at 4096 */
	if(sec_size > 4096)
		sec_size = 4096;
	return sec_size;
}

static int get_block_geom(int fd, struct device *dev) {
	struct hd_geometry geom;
	int sec_size;
	unsigned long size;
	uint16_t heads=dev->heads;
	uint16_t sectors=dev->sectors;
	uint32_t sect_per_track;

	if (ioctl(fd, HDIO_GETGEO, &geom) < 0) {
		fprintf(stderr, "Could not get geometry of device (%s)",
			strerror(errno));
		return -1;
	}

	if (ioctl(fd, BLKGETSIZE, &size) < 0) {
		fprintf(stderr, "Could not get size of device (%s)",
			strerror(errno));
		return -1;
	}

	sec_size = get_sector_size(fd);
	if(sec_size < 0)
		return -1;

	dev->ssize = 0;
	while (dev->ssize < 0x7F && (128 << dev->ssize) < sec_size)
		dev->ssize++;

	if(!heads)
		heads = geom.heads;
	if(!sectors)
		sectors = geom.sectors;

	sect_per_track = heads * sectors;
	if(!dev->hidden) {
		uint32_t hidden;
		hidden = geom.start % sect_per_track;
		if(hidden && hidden != sectors) {
			fprintf(stderr,
				"Hidden (%d) does not match sectors (%d)\n",
				hidden, sectors);
			return -1;
		}
		dev->hidden = hidden;
	}
	dev->heads = heads;
	dev->sectors = sectors;
	if(!dev->tracks)
		dev->tracks = ulong_to_sectors((size + dev->hidden % sect_per_track) / sect_per_track);
	return 0;
}

#define HAVE_GET_BLOCK_GEOM

#endif /* linux */


/* OS/2, gcc+emx */
#ifdef __EMX__
#define predefined_devices
struct device devices[] = {
  {"A:", 'A', GENFD},
  {"B:", 'B', GENFD},
};
#define INIT_NOOP
#endif



/*** /jes -- for D.O.S. 486 BL DX2/80 ***/
/*** Jean-Marc Zucconi <jmz@FreeBSD.org> 2001/03/30 ***/
#ifdef OS_freebsd
#define predefined_devices
struct device devices[] = {
	{"/dev/fd0.1440", 'A', FHD312},
	{"/dev/fd0.720",  'A', FDD312},
	{"/dev/fd1.1200", 'B', MHD514},
	{"/dev/sd0s1",     'C', GENHD},
	REMOTE
};
#endif /* __FreeBSD__ */

/*** /jes -- for ALR 486 DX4/100 ***/
#if defined(OS_netbsd) || defined(OS_netbsdelf)
#define predefined_devices
struct device devices[] = {
	{"/dev/rfd0a", 'A', FHD312},
	{"/dev/rfd0f", 'A', FDD312},
	{"/dev/rfd0f", 'S', MDD312},
	{"/dev/rfd1a", 'B', FHD514},
	{"/dev/rfd1d", 'B', FDD514},
	{"/dev/rfd1d", 'T', MDD514},
	{"/dev/rwd0d", 'C', 16, 0, 0, 0, 0, 0, 63L*512L, DEF_ARG0(0)},
	REMOTE
};
#endif /* OS_NetBSD */

/* fgsch@openbsd.org 2000/05/19 */
#if defined(OS_openbsd)
#define predefined_devices
struct device devices[] = {
	{"/dev/rfd0Bc", 'A', FHD312},
	{"/dev/rfd0Fc", 'A', FDD312},
	{"/dev/rfd1Cc", 'B', FHD514},
	{"/dev/rfd1Dc", 'B', FDD514},
	{"/dev/rwd0c", 'C', 16, 0, 0, 0, 0, 0, 63L*512L, DEF_ARG0(0)},
	REMOTE
};
#endif /* OS_openbsd */


#ifdef CPU_alpha
#ifndef OS_osf4
#ifdef __osf__
#include <sys/fcntl.h>
#define predefined_devices
struct device devices[] = {
	{"/dev/rfd0c",		'A', GENFD},
	REMOTE
};
#endif
#endif
#endif

#ifdef OS_osf
#ifndef predefined_devices
#define predefined_devices
struct device devices[] = {
	{"/dev/fd0a", 'A',  MHD312 } };
	REMOTE
#endif
#endif


#ifdef OS_nextstep
#define predefined_devices
struct device devices[] = {
#ifdef CPU_m68k
	{"/dev/rfd0b", 'A', MED312 },
	REMOTE
#else
	{"/dev/rfd0b", 'A', MHD312 },
	REMOTE
#endif
};
#endif


#if (!defined(predefined_devices) && defined(OS_sysv4))
#ifdef __uxp__
#define predefined_devices
struct device devices[] = {
      {"/dev/fpd0",   'A', FHD312},
      {"/dev/fpd0",   'A', FDD312},
	  REMOTE
};
#else
#define predefined_devices
struct device devices[] = {
	{"/dev/rdsk/f1q15dt",	'B', FHD514},
	{"/dev/rdsk/f1d9dt",	'B', FDD514},
	{"/dev/rdsk/f1d8dt",	'B', FDDsmall},
	{"/dev/rdsk/f03ht",	'A', FHD312},
	{"/dev/rdsk/f03dt",	'A', FDD312},
	{"/dev/rdsk/dos",	'C', GENHD},
	REMOTE
};
#endif
#endif /* sysv4 */

#ifdef OS_mingw32msvc
#define predefined_devices
struct device devices[] = {
   {"\\\\.\\A:", 'A', GENFD },
};
#endif

#ifdef INIT_GENERIC

#ifndef USE_2M
#define USE_2M(x) 0x80
#endif

#ifndef SSIZE
#define SSIZE(x) 0x02
#endif

#ifndef SET_2M
#define SET_2M(x,y) return -1
#endif

#ifndef SET_SSIZE
#define SET_SSIZE(x,y) return -1
#endif

#undef INIT_NOOP
int init_geom(int fd, struct device *dev, struct device *orig_dev,
	      struct MT_STAT *statbuf)
{
	struct generic_floppy_struct floppy;
	int change;

#ifdef HAVE_GET_BLOCK_GEOM
	/**
	 * Block device which *isn't* a floppy device
	 */
	if (S_ISBLK(statbuf->st_mode) &&
	    major(statbuf->st_rdev) != BLOCK_MAJOR) {
		get_block_geom(fd, dev);
		return compare_geom(dev, orig_dev);
	}
#endif

	/*
	 * succeed if we don't have a floppy
	 * this is the case for dosemu floppy image files for instance
	 */
	if (!((S_ISBLK(statbuf->st_mode) &&
	       major(statbuf->st_rdev) == BLOCK_MAJOR)
#ifdef CHAR_MAJOR
	      || (S_ISCHR(statbuf->st_mode) &&
		  major(statbuf->st_rdev) == CHAR_MAJOR)
#endif
		))
		return compare_geom(dev, orig_dev);

	/*
	 * We first try to get the current floppy parameters from the kernel.
	 * This allows us to
	 * 1. get the rate
	 * 2. skip the parameter setting if the parameters are already o.k.
	 */

	if (get_parameters( fd, & floppy ) )
		/*
		 * autodetection failure.
		 * This mostly occurs because of an absent or unformatted disks.
		 *
		 * It might also occur because of bizarre formats (for example
		 * rate 1 on a 3 1/2 disk).

		 * If this is the case, the user should do an explicit
		 * setfdprm before calling mtools
		 *
		 * Another cause might be pre-existing wrong parameters. The
		 * user should do an setfdprm -c to repair this situation.
		 *
		 * ...fail immediately... ( Theoretically, we could try to save
		 * the situation by trying out all rates, but it would be slow
		 * and awkward)
		 */
		return 1;


	/*
	 * if we have already have the correct parameters, keep them.
	 * the number of tracks doesn't need to match exactly, it may be bigger.
	 * the number of heads and sectors must match exactly, to avoid
	 * miscalculation of the location of a block on the disk
	 */
	change = 0;
	if(compare(dev->sectors, SECTORS(floppy))){
		SECTORS(floppy) = dev->sectors;
		change = 1;
	} else
		dev->sectors = (uint16_t) SECTORS(floppy);

	if(compare(dev->heads, HEADS(floppy))){
		HEADS(floppy) = dev->heads;
		change = 1;
	} else
		dev->heads = (uint16_t) HEADS(floppy);

	if(compare(dev->tracks, (unsigned int) TRACKS(floppy))){
		TRACKS(floppy) = dev->tracks;
		change = 1;
	} else
		dev->tracks = TRACKS(floppy);


	if(compare(dev->use_2m, USE_2M(floppy))){
		SET_2M(&floppy, dev->use_2m);
		change = 1;
	} else
		dev->use_2m = USE_2M(floppy);

	if( ! (dev->ssize & 0x80) )
		dev->ssize = 0;
	if(compare(dev->ssize, SSIZE(floppy) + 128)){
		SET_SSIZE(&floppy, dev->ssize);
		change = 1;
	} else
		dev->ssize = SSIZE(floppy);

	if(!change)
		/* no change, succeed */
		return 0;

#ifdef SECTORS_PER_TRACK
	SECTORS_PER_TRACK(floppy) = dev->sectors * dev->heads;
#endif

#ifdef SECTORS_PER_DISK
	SECTORS_PER_DISK(floppy) = dev->sectors * dev->heads * dev->tracks;
#endif

#ifdef STRETCH
	/* ... and the stretch */
	if ( dev->tracks > 41 )
		STRETCH(floppy) = 0;
	else
		STRETCH(floppy) = 1;
#endif

	return set_parameters( fd, &floppy, statbuf);
}
#endif /* INIT_GENERIC */

#ifdef INIT_NOOP
int init_geom(int fd, struct device *dev, struct device *orig_dev,
	      struct MT_STAT *statbuf)
{
	return compare_geom(dev, orig_dev);
}
#endif

#ifdef predefined_devices
const unsigned int nr_const_devices = sizeof(const_devices) / sizeof(*const_devices);
#else
struct device devices[]={
	{"/dev/fd0", 'A', 0, O_EXCL, 0,0, 0,0, MDEF_ARG},
	/* to shut up Ultrix's native compiler, we can't make this empty :( */
};
const unsigned int nr_const_devices = 0;
#endif
