blob: 9951f0789e6d3b37edb46e0a052c20f522ae10ed [file] [log] [blame]
/* Lecture des disquettes Thomson sous Linux
* Version 2.1
* Copyright (C) 2000-2003 Eric Botcazou
*
* 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 of the License, or
* (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef SCAN_DEPEND
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#ifdef __linux__
#include <linux/fd.h>
#include <linux/fdreg.h>
#endif
#endif
#include "floppy.h"
#define DISK_RETRY 4
#define RETRY2 10
static int rw_flag;
static int fd[2] = {-1, -1};
static int drive_type[2];
#define IS_5_INCHES(drive) ((drive_type[drive]>0) && (drive_type[drive]<3))
#define SET_NO_MULTITRACK(lval) (lval &= ~0x80)
#define SET_NO_MFM(lval) (lval &= ~0x40)
/* ResetDrive:
* Réinitialise le lecteur de disquettes.
*/
static void ResetDrive(int drive, int density)
{
struct floppy_struct fd_prm;
fd_prm.head = (density == 1 ? 1 : 2); /* nr of heads */
fd_prm.track = (IS_5_INCHES(drive) ? 40 : 80); /* nr of tracks */
fd_prm.sect = (density == 1 ? 4 : 8); /* sectors per track (512-byte sector) */
fd_prm.size = fd_prm.head * fd_prm.track * fd_prm.sect; /* nr of sectors total */
fd_prm.stretch = 0;
fd_prm.gap = 0x1B;
fd_prm.rate = (density == 1 ? 0xB1 : 0x3A); /* cf /usr/include/linux/fd.h */
fd_prm.spec1 = 0xDF;
fd_prm.fmt_gap = 0x2C;
ioctl(fd[drive], FDSETPRM, &fd_prm);
}
/* OpenDrive:
* Obtient le descripteur de fichier pour le lecteur de disquettes.
*/
static int OpenDrive(int drive, int density)
{
char dev_str[16];
snprintf(dev_str, sizeof(dev_str), "/dev/fd%d", drive);
if (rw_flag) {
if ((fd[drive]=open(dev_str, O_RDWR | O_NDELAY))>=0)
goto success;
}
else {
if ((fd[drive]=open(dev_str, O_RDONLY | O_NDELAY))>=0)
goto success;
}
return 0;
success:
ResetDrive(drive, density);
return 1;
}
/* ExecCommand:
* Exécute la commande spécifiée via l'appel ioctl() FDRAWCMD.
*/
static int ExecCommand2(int drive, int density, struct floppy_raw_cmd *fd_cmd);
static int ExecCommand(int drive,int density, struct floppy_raw_cmd *fd_cmd) {
int i,ret;
for (i=0;i<RETRY2;i++) {
if ((ret=ExecCommand2(drive,density,fd_cmd))==0) return 0;
ResetDrive(drive,density);
}
return ret;
}
static int ExecCommand2(int drive, int density, struct floppy_raw_cmd *fd_cmd)
{
int i, ret = 0;
if (fd[drive]<0 && !OpenDrive(drive, density))
return 0x10; /* lecteur non prêt */
for (i=0; i<DISK_RETRY; i++) {
ret=ioctl(fd[drive], FDRAWCMD, fd_cmd);
if (ret >= 0)
break;
ResetDrive(drive, density);
}
if (ret<0)
return 0x10; /* lecteur non prêt */
#ifdef DEBUG
printf("fd_cmd reply: ");
for (i=0; i<3; i++)
printf("ST%d=%02x ", i, fd_cmd->reply[i]);
printf("\n");
#endif
switch (fd_cmd->reply[1]) { /* ST1 */
case 0x01: /* Missing Address Mark */
return 0x04; /* erreur sur l'adresse */
case 0x02: /* Write Protected */
return 0x01; /* disk protégé en écriture */
case 0x04: /* No Data - unreadable */
return 0x08; /* erreur sur les données */
case 0x20: /* CRC error in data or addr */
if (fd_cmd->reply[2]==0x20)
return 0x08; /* erreur sur les données */
else
return 0x04; /* erreur sur l'adresse */
default:
return 0; /* OK */
}
}
/* FloppyReadSector:
* Lit le secteur spécifié sur la disquette.
*/
int FloppyReadSector(int drive, int density, int track, int sector, int nsects, unsigned char data[])
{
struct floppy_raw_cmd fd_cmd;
int pc_drive = drive/2;
/* paramètres de commande */
fd_cmd.flags = FD_RAW_READ | FD_RAW_INTR | FD_RAW_NEED_SEEK;
fd_cmd.data = data;
fd_cmd.length = (density == 1 ? 128 : 256)*nsects; /* buffer length */
fd_cmd.rate = IS_5_INCHES(pc_drive) ? 1 : 2;
fd_cmd.track = IS_5_INCHES(pc_drive) ? track*2 : track; /* physical cylinder */
fd_cmd.cmd[0] = FD_READ;
fd_cmd.cmd[1] = (drive%2) << 2; /* physical head */
fd_cmd.cmd[2] = track; /* logical cylinder */
fd_cmd.cmd[3] = 0; /* logical head */
fd_cmd.cmd[4] = sector;
fd_cmd.cmd[5] = density - 1; /* sector size = 128*2^n */
fd_cmd.cmd[6] = 16; /* num sectors */
fd_cmd.cmd[7] = 0x1B; /* gap */
fd_cmd.cmd[8] = 0xFF;
fd_cmd.cmd_count = 9;
SET_NO_MULTITRACK(fd_cmd.cmd[0]);
if (density == 1)
SET_NO_MFM(fd_cmd.cmd[0]); /* FM coding */
return ExecCommand(pc_drive, density, &fd_cmd);
}
/* FloppyWriteSector:
* Ecrit le secteur spécifié sur la disquette.
*/
int FloppyWriteSector(int drive, int density, int track, int sector, int nsects, const unsigned char data[])
{
struct floppy_raw_cmd fd_cmd;
int pc_drive = drive/2;
/* paramètres de commande */
fd_cmd.flags = FD_RAW_WRITE | FD_RAW_INTR | FD_RAW_NEED_SEEK;
fd_cmd.data = (unsigned char *)data;
fd_cmd.length = (density == 1 ? 128 : 256)*nsects; /* buffer length */
fd_cmd.rate = IS_5_INCHES(pc_drive) ? 1 : 2;
fd_cmd.track = IS_5_INCHES(pc_drive) ? track*2 : track; /* physical cylinder */
fd_cmd.cmd[0] = FD_WRITE;
fd_cmd.cmd[1] = (drive%2) << 2; /* physical head */
fd_cmd.cmd[2] = track; /* logical cylinder */
fd_cmd.cmd[3] = 0; /* logical head */
fd_cmd.cmd[4] = sector;
fd_cmd.cmd[5] = density - 1; /* sector size = 128*2^n */
fd_cmd.cmd[6] = 16; /* num sectors */
fd_cmd.cmd[7] = 0x1B; /* gap */
fd_cmd.cmd[8] = 0xFF;
fd_cmd.cmd_count = 9;
SET_NO_MULTITRACK(fd_cmd.cmd[0]);
if (density == 1)
SET_NO_MFM(fd_cmd.cmd[0]); /* FM coding */
return ExecCommand(pc_drive, density, &fd_cmd);
}
/* FloppyFormatTrack:
* Formate la piste en utilisant la table des headers spécifiée.
*/
int FloppyFormatTrack(int drive, int density, int track, const unsigned char header_table[])
{
struct floppy_raw_cmd fd_cmd;
int pc_drive = drive/2;
/* paramètres de commande */
fd_cmd.flags = FD_RAW_WRITE | FD_RAW_INTR | FD_RAW_NEED_SEEK;
fd_cmd.data = (unsigned char *)header_table;
fd_cmd.length = 64;
fd_cmd.rate = IS_5_INCHES(pc_drive) ? 1 : 2;
fd_cmd.track = IS_5_INCHES(pc_drive) ? track*2 : track; /* physical cylinder */
fd_cmd.cmd[0] = FD_FORMAT;
fd_cmd.cmd[1] = (drive%2) << 2;
fd_cmd.cmd[2] = density - 1; /* sector size = 128*2^n */
fd_cmd.cmd[3] = 16; /* num sectors */
fd_cmd.cmd[4] = 0x2C;
fd_cmd.cmd[5] = 0xE5; /* filler_byte */
fd_cmd.cmd_count = 6;
SET_NO_MULTITRACK(fd_cmd.cmd[0]); /* nop */
if (density == 1)
SET_NO_MFM(fd_cmd.cmd[0]); /* FM coding */
return ExecCommand(pc_drive, density, &fd_cmd);
}
/* FloppyInit:
* Initialise le module de lecture de disquettes.
*/
int FloppyInit(struct floppy_info *fi, int enable_write_support)
{
struct floppy_drive_params fd_params;
char dev_str[16];
int i, num_drives = 0;
for (i=0; i<2; i++) {
/* get drive type */
snprintf(dev_str, sizeof(dev_str), "/dev/fd%d", i);
if ((fd[i]=open(dev_str, O_RDONLY | O_NDELAY))<0) {
drive_type[i] = 0;
fi->drive_type[2*i] = 0;
fi->drive_type[2*i+1] = 0;
}
else {
ioctl(fd[i], FDGETDRVPRM, &fd_params);
close(fd[i]);
fd[i] = -1;
if (fd_params.cmos > 6) {
drive_type[i] = 0;
fi->drive_type[2*i] = 0;
fi->drive_type[2*i+1] = 0;
}
else {
drive_type[i] = fd_params.cmos;
fi->drive_type[2*i] = fd_params.cmos;
fi->drive_type[2*i+1] = fd_params.cmos;
}
num_drives++;
}
}
fi->num_drives = num_drives;
fi->fm_support = 1;
fi->write_support = enable_write_support;
rw_flag = enable_write_support;
return num_drives;
}
/* FloppyExit:
* Met au repos le module de lecture de disquettes.
*/
void FloppyExit(void)
{
int i;
for (i=0; i<2; i++) {
if (fd[i] >= 0) {
close(fd[i]);
fd[i] = -1;
}
}
}