PC floppy driver, initial rev.
supports reading 1.44M, 720K, 2 drives. no write support yet. git-svn-id: file:///srv/svn/repos/haiku/trunk/current@5668 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -0,0 +1,523 @@
|
|||||||
|
/*
|
||||||
|
* OpenBeOS floppy driver
|
||||||
|
* (c) 2003, OpenBeOS project.
|
||||||
|
* François Revol, [email protected]
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* inspired by the NewOS floppy driver
|
||||||
|
* http://www.newos.org/cgi-bin/fileDownLoad.cgi?FSPC=//depot/newos/kernel/addons/dev/arch/i386/floppy/floppy.c&REV=6
|
||||||
|
* related docs:
|
||||||
|
* - datasheets
|
||||||
|
* http://floppyutil.sourceforge.net/floppy.html
|
||||||
|
* http://www.openppc.org/datasheets/NatSemi_PC87308VUL_full.pdf
|
||||||
|
* http://www.smsc.com/main/datasheets/37c665gt.pdf
|
||||||
|
* http://www.smsc.com/main/datasheets/47b27x.pdf
|
||||||
|
* - sources
|
||||||
|
* http://www.freebsd.org/cgi/cvsweb.cgi/src/sys/isa/fd.c
|
||||||
|
* http://fxr.watson.org/fxr/source/sys/fdcio.h?v=RELENG50
|
||||||
|
* http://fxr.watson.org/fxr/source/isa/fd.c?v=RELENG50
|
||||||
|
* http://lxr.linux.no/source/drivers/block/floppy.c
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* the stupid FDC engine is able to use several drives at a time, but *only* for some commands,
|
||||||
|
* others need exclusive access, as they need the drive to be selected.
|
||||||
|
* we just serialize the whole thing to simplify.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include <Drivers.h>
|
||||||
|
#include <ISA.h>
|
||||||
|
#include <PCI.h>
|
||||||
|
#include <config_manager.h>
|
||||||
|
#include <malloc.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "floppy.h"
|
||||||
|
|
||||||
|
/* compile time configuration */
|
||||||
|
|
||||||
|
/* enables support for more than one drive */
|
||||||
|
#define NEW_DEV_LAYOUT
|
||||||
|
/* don't symlink raw -> 0/raw */
|
||||||
|
#define NO_SYMLINK_OLD
|
||||||
|
/* publish the first drive as floppy/raw */
|
||||||
|
#define FIRST_ONE_NO_NUMBER
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* default values... for 3"5 HD, but we support DD too now */
|
||||||
|
#define SECTORS_PER_TRACK 18
|
||||||
|
#define NUM_HEADS 2
|
||||||
|
#define SECTORS_PER_CYLINDER (SECTORS_PER_TRACK * NUM_HEADS)
|
||||||
|
#define SECTOR_SIZE 512
|
||||||
|
#define TRACK_SIZE (SECTOR_SIZE * SECTORS_PER_CYLINDER)
|
||||||
|
#define NUM_TRACKS 80
|
||||||
|
#define DISK_SIZE (TRACK_SIZE * NUM_TRACKS)
|
||||||
|
|
||||||
|
|
||||||
|
extern const char floppy_icon[];
|
||||||
|
extern const char floppy_mini_icon[];
|
||||||
|
|
||||||
|
#define OLD_DEV_FORMAT "disk/floppy/raw"
|
||||||
|
|
||||||
|
#ifndef NEW_DEV_LAYOUT
|
||||||
|
# define MAX_FLOPPIES 1
|
||||||
|
# define DEV_FORMAT OLD_DEV_FORMAT
|
||||||
|
#else
|
||||||
|
# define MAX_FLOPPIES 4
|
||||||
|
# define DEV_FORMAT "disk/floppy/%d/raw"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static char floppy_dev_name[MAX_FLOPPIES][B_OS_NAME_LENGTH];
|
||||||
|
static const char *dev_names[MAX_FLOPPIES+1];
|
||||||
|
|
||||||
|
extern device_hooks floppy_hooks;
|
||||||
|
int32 api_version = B_CUR_DRIVER_API_VERSION;
|
||||||
|
|
||||||
|
isa_module_info *isa;
|
||||||
|
config_manager_for_driver_module_info *config_man;
|
||||||
|
|
||||||
|
struct floppy floppies[MAX_FLOPPIES];
|
||||||
|
|
||||||
|
/* by order of check */
|
||||||
|
/* from fd.c from fBSD */
|
||||||
|
const floppy_geometry supported_geometries[] = {
|
||||||
|
|
||||||
|
/* { gap, nsecs, data_rate, fmtgap, sd2off, {secsz, s/trk, ntraks, nheads, type, rmable, ro, wonce}, flags, name }, */
|
||||||
|
{ 0x1b, 2880, FDC_500KBPS, 0x6c, 0, { 512, 18, 80, 2, B_DISK, true, true, false}, FL_MFM, "1.44M" },
|
||||||
|
/* { 0x20, 5760, FDC_1MBPS, 0x4c, 1, { 512, 36, 80, 2, B_DISK, true, true, false}, FL_MFM|FL_PERP, "2.88M" }, */
|
||||||
|
{ 0x20, 1440, FDC_250KBPS, 0x50, 0, { 512, 9, 80, 2, B_DISK, true, true, false}, FL_MFM, "720K" },
|
||||||
|
|
||||||
|
{ 0, 0, 0, 0, 0, {0, 0, 0, 0, 0, true, false, false}, 0, NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct floppy_cookie {
|
||||||
|
struct floppy *flp;
|
||||||
|
} floppy_cookie;
|
||||||
|
|
||||||
|
|
||||||
|
void motor_off_daemon(void *t, int tim);
|
||||||
|
|
||||||
|
status_t init_hardware(void)
|
||||||
|
{
|
||||||
|
DPRINT("init_hardware()\n");
|
||||||
|
/* assume there is always one */
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
status_t init_driver(void)
|
||||||
|
{
|
||||||
|
status_t err;
|
||||||
|
unsigned int i, j;
|
||||||
|
int current;
|
||||||
|
uint64 cmcookie = 0;
|
||||||
|
struct device_info info;
|
||||||
|
|
||||||
|
DPRINT("init_driver()\n");
|
||||||
|
if ((err = get_module(B_ISA_MODULE_NAME, (module_info **)&isa)) < B_OK) {
|
||||||
|
dprintf(FLO "no isa module!\n");
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
if ((err = get_module(B_CONFIG_MANAGER_FOR_DRIVER_MODULE_NAME, (module_info **)&config_man)) < B_OK) {
|
||||||
|
dprintf(FLO "no config_manager module!\n");
|
||||||
|
put_module(B_ISA_MODULE_NAME);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
for (i = 0; i < MAX_FLOPPIES; i++)
|
||||||
|
memset(&(floppies[i]), 0, sizeof(floppy_t));
|
||||||
|
current = 0;
|
||||||
|
register_kernel_daemon(motor_off_daemon, floppies, 5);
|
||||||
|
while (config_man->get_next_device_info(B_ISA_BUS, &cmcookie, &info, sizeof(struct device_info)) == B_OK) {
|
||||||
|
struct device_configuration *conf;
|
||||||
|
struct floppy *master;
|
||||||
|
struct floppy *last = NULL;
|
||||||
|
int flops;
|
||||||
|
if ((info.config_status != B_OK) ||
|
||||||
|
(info.devtype.base != PCI_mass_storage) ||
|
||||||
|
(info.devtype.subtype != PCI_floppy) ||
|
||||||
|
(info.devtype.interface != 0) /* XXX: needed ?? */)
|
||||||
|
continue;
|
||||||
|
err = config_man->get_size_of_current_configuration_for(cmcookie);
|
||||||
|
if (err < B_OK)
|
||||||
|
continue;
|
||||||
|
conf = (struct device_configuration *)malloc(err);
|
||||||
|
if (conf == NULL)
|
||||||
|
continue;
|
||||||
|
if (config_man->get_current_configuration_for(cmcookie, conf, err) < B_OK) {
|
||||||
|
free(conf);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
master = &(floppies[current]);
|
||||||
|
for (i = 0; i < conf->num_resources; i++) {
|
||||||
|
if (conf->resources[i].type == B_IO_PORT_RESOURCE)
|
||||||
|
master->iobase = conf->resources[i].d.r.minbase & 0x0FFF8;
|
||||||
|
/* WTF do I get
|
||||||
|
* min 3f2 max 3f2 align 0 len 4 ?
|
||||||
|
* on my K6-2, I even get 2 bytes at 3f2 + 2 bytes at 3f4 !
|
||||||
|
* looks like AT interface, suppose PS/2, which is 8 bytes wide.
|
||||||
|
* answer: because the 8th byte is also for the IDE controller...
|
||||||
|
* good old PC stuff... PPC here I come !
|
||||||
|
*/
|
||||||
|
if (conf->resources[i].type == B_IRQ_RESOURCE) {
|
||||||
|
int val;
|
||||||
|
for (val = 0; val < 32; val++) {
|
||||||
|
if (conf->resources[i].d.m.mask & (1 << val)) {
|
||||||
|
master->irq = val;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (conf->resources[i].type == B_DMA_RESOURCE) {
|
||||||
|
int val;
|
||||||
|
for (val = 0; val < 32; val++) {
|
||||||
|
if (conf->resources[i].d.m.mask & (1 << val)) {
|
||||||
|
master->dma = val;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (master->iobase == 0) {
|
||||||
|
dprintf(FLO "controller doesn't have any io ??\n");
|
||||||
|
goto config_error;
|
||||||
|
}
|
||||||
|
dprintf(FLO "controller at 0x%04lx, irq %ld, dma %ld\n", master->iobase, master->irq, master->dma);
|
||||||
|
//master->dma = -1; // XXX: DEBUG: disable DMA
|
||||||
|
/* allocate resources */
|
||||||
|
master->completion = create_sem(0, "floppy interrupt");
|
||||||
|
if (master->completion < 0)
|
||||||
|
goto config_error;
|
||||||
|
new_lock(&master->ben, "floppy driver access");
|
||||||
|
//if (new_lock(&master->ben, "floppy driver access") < B_OK)
|
||||||
|
// goto config_error;
|
||||||
|
|
||||||
|
/* 20K should hold a full cylinder XXX: B_LOMEM for DMA ! */
|
||||||
|
master->buffer_area = create_area("floppy cylinder buffer", &(master->buffer), B_ANY_KERNEL_ADDRESS, CYL_BUFFER_SIZE, B_FULL_LOCK, B_READ_AREA|B_WRITE_AREA);
|
||||||
|
if (master->buffer_area < B_OK)
|
||||||
|
goto config_error2;
|
||||||
|
master->slock = 0;
|
||||||
|
master->isa = isa;
|
||||||
|
if (install_io_interrupt_handler(master->irq, flo_intr, (void *)master, 0) < B_OK)
|
||||||
|
goto config_error2;
|
||||||
|
|
||||||
|
flops = count_floppies(master); /* actually a bitmap */
|
||||||
|
flops = MAX(flops, 1); /* XXX: assume at least one */
|
||||||
|
DPRINT("drives found: 0x%01x\n", flops);
|
||||||
|
for (i = 0; current < MAX_FLOPPIES && i < 4; i++) {
|
||||||
|
if ((flops & (1 << i)) == 0)
|
||||||
|
continue;
|
||||||
|
floppies[current].next = NULL;
|
||||||
|
if (last)
|
||||||
|
last->next = &(floppies[current]);
|
||||||
|
floppies[current].iobase = master->iobase;
|
||||||
|
floppies[current].irq = master->irq;
|
||||||
|
floppies[current].dma = master->dma;
|
||||||
|
floppies[current].drive_num = i;
|
||||||
|
floppies[current].master = master;
|
||||||
|
floppies[current].isa = master->isa;
|
||||||
|
floppies[current].completion = master->completion;
|
||||||
|
floppies[current].track = -1;
|
||||||
|
last = &(floppies[current]);
|
||||||
|
current++;
|
||||||
|
}
|
||||||
|
/* XXX: TODO: remove "assume at least one" + cleanup if no drive on controller */
|
||||||
|
|
||||||
|
goto config_ok;
|
||||||
|
|
||||||
|
|
||||||
|
config_error2:
|
||||||
|
if (master->buffer_area)
|
||||||
|
delete_area(master->buffer_area);
|
||||||
|
free_lock(&master->ben);
|
||||||
|
config_error:
|
||||||
|
|
||||||
|
if (master->completion > 0)
|
||||||
|
delete_sem(master->completion);
|
||||||
|
master->iobase = 0;
|
||||||
|
put_module(B_CONFIG_MANAGER_FOR_DRIVER_MODULE_NAME);
|
||||||
|
put_module(B_ISA_MODULE_NAME);
|
||||||
|
config_ok:
|
||||||
|
free(conf);
|
||||||
|
if (current >= MAX_FLOPPIES)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* make device names */
|
||||||
|
for (i = 0, j = 0; i < MAX_FLOPPIES; i++) {
|
||||||
|
if (floppies[i].iobase) {
|
||||||
|
#ifdef FIRST_ONE_NO_NUMBER
|
||||||
|
if (!i)
|
||||||
|
sprintf(floppy_dev_name[i], OLD_DEV_FORMAT, i);
|
||||||
|
else
|
||||||
|
#endif
|
||||||
|
sprintf(floppy_dev_name[i], DEV_FORMAT, i);
|
||||||
|
dev_names[j++] = floppy_dev_name[i];
|
||||||
|
DPRINT("names[%d] = %s\n", j-1, dev_names[j-1]);
|
||||||
|
} else
|
||||||
|
strcpy(floppy_dev_name[i], "");
|
||||||
|
}
|
||||||
|
dev_names[j] = NULL;
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef NEW_DEV_LAYOUT
|
||||||
|
#if !defined(NO_SYMLINK_OLD) && !defined(FIRST_ONE_NO_NUMBER)
|
||||||
|
/* fake the good old single drive */
|
||||||
|
mkdir("/dev/disk/floppy", 0755);
|
||||||
|
symlink("0/raw", "/dev/disk/floppy/raw");
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void uninit_driver(void)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
DPRINT("uninit_driver()\n");
|
||||||
|
unregister_kernel_daemon(motor_off_daemon, floppies);
|
||||||
|
for (i = 0; i < MAX_FLOPPIES; i++) {
|
||||||
|
if (floppies[i].iobase)
|
||||||
|
turn_off_motor(&floppies[i]);
|
||||||
|
}
|
||||||
|
DPRINT("dealocating...\n");
|
||||||
|
for (i = 0; i < MAX_FLOPPIES; i++) {
|
||||||
|
if (floppies[i].iobase) {
|
||||||
|
if (floppies[i].master == &(floppies[i])) {
|
||||||
|
remove_io_interrupt_handler(floppies[i].irq, flo_intr, (void *)&(floppies[i]));
|
||||||
|
free_lock(&floppies[i].ben);
|
||||||
|
delete_sem(floppies[i].completion);
|
||||||
|
delete_area(floppies[i].buffer_area);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DPRINT("uninit done\n");
|
||||||
|
put_module(B_CONFIG_MANAGER_FOR_DRIVER_MODULE_NAME);
|
||||||
|
put_module(B_ISA_MODULE_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char **publish_devices(void)
|
||||||
|
{
|
||||||
|
if (dev_names[0] == NULL)
|
||||||
|
return NULL;
|
||||||
|
return dev_names;
|
||||||
|
}
|
||||||
|
|
||||||
|
device_hooks *find_device(const char *name)
|
||||||
|
{
|
||||||
|
(void)name;
|
||||||
|
return &floppy_hooks;
|
||||||
|
}
|
||||||
|
|
||||||
|
status_t flo_open(const char *name, uint32 flags, floppy_cookie **cookie)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
status_t err;
|
||||||
|
DPRINT("open(%s)\n", name);
|
||||||
|
if (flags & O_NONBLOCK)
|
||||||
|
return EINVAL;
|
||||||
|
for (i = 0; i < MAX_FLOPPIES; i++) {
|
||||||
|
if (dev_names[i] && (strncmp(name, dev_names[i], strlen(dev_names[i])) == 0))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (i >= MAX_FLOPPIES)
|
||||||
|
return ENODEV;
|
||||||
|
*cookie = (floppy_cookie *)malloc(sizeof(floppy_cookie));
|
||||||
|
if (*cookie == NULL)
|
||||||
|
return B_NO_MEMORY;
|
||||||
|
(*cookie)->flp = &(floppies[i]);
|
||||||
|
/* if we don't know yet if there's something in, check that */
|
||||||
|
if ((*cookie)->flp->status <= FLOP_MEDIA_CHANGED)
|
||||||
|
err = query_media((*cookie)->flp, false);
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
status_t flo_close(floppy_cookie *cookie)
|
||||||
|
{
|
||||||
|
DPRINT("close()\n");
|
||||||
|
cookie->flp = NULL;
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
status_t flo_free(floppy_cookie *cookie)
|
||||||
|
{
|
||||||
|
DPRINT("free()\n");
|
||||||
|
free(cookie);
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
status_t flo_read(floppy_cookie *cookie, off_t position, void *data, size_t *numbytes)
|
||||||
|
{
|
||||||
|
status_t err;
|
||||||
|
size_t len = *numbytes;
|
||||||
|
size_t bytes_read = 0;
|
||||||
|
int sectsize = SECTOR_SIZE;
|
||||||
|
int cylsize = TRACK_SIZE; /* hmm, badly named, it's actually track_size (a cylinder has 2 tracks, one per head) */
|
||||||
|
device_geometry *geom = NULL;
|
||||||
|
ssize_t disk_size = 0;
|
||||||
|
|
||||||
|
if (cookie->flp->geometry)
|
||||||
|
geom = &(cookie->flp->geometry->g);
|
||||||
|
if (geom) {
|
||||||
|
sectsize = geom->bytes_per_sector;
|
||||||
|
cylsize = sectsize * geom->sectors_per_track/* * geom->head_count*/;
|
||||||
|
disk_size = (geom->bytes_per_sector)
|
||||||
|
* (geom->sectors_per_track)
|
||||||
|
* (geom->head_count)
|
||||||
|
* (geom->cylinder_count);
|
||||||
|
}
|
||||||
|
if (disk_size <= 0) {
|
||||||
|
*numbytes = 0;
|
||||||
|
return B_DEV_NO_MEDIA;
|
||||||
|
}
|
||||||
|
if (position > disk_size) {
|
||||||
|
dprintf(FLO "attempt to seek beyond device end!\n");
|
||||||
|
*numbytes = 0;
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
if (position + *numbytes > disk_size) {
|
||||||
|
dprintf(FLO "attempt to read beyond device end!\n");
|
||||||
|
*numbytes = (size_t)((off_t)disk_size - position);
|
||||||
|
if (*numbytes == 0)
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle partial first block
|
||||||
|
if((position % SECTOR_SIZE) != 0) {
|
||||||
|
size_t toread;
|
||||||
|
DPRINT("read: begin %Ld, %ld\n", position, bytes_read);
|
||||||
|
err = read_sectors(cookie->flp, position / sectsize, 1);
|
||||||
|
DPRINT("PIO READ got %d sect\n", err);
|
||||||
|
if (err <= 0) {
|
||||||
|
*numbytes = 0;
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
toread = MIN(len, sectsize);
|
||||||
|
toread = MIN(toread, sectsize - (position % sectsize));
|
||||||
|
memcpy(data, cookie->flp->master->buffer + position % cylsize/*(sectsize * ) + (position % sectsize)*/, toread);
|
||||||
|
len -= toread;
|
||||||
|
bytes_read += toread;
|
||||||
|
position += toread;
|
||||||
|
}
|
||||||
|
// read the middle blocks
|
||||||
|
while(len >= sectsize) {
|
||||||
|
DPRINT("read: middle %Ld, %ld, %ld\n", position, bytes_read, len);
|
||||||
|
|
||||||
|
// try to read as many sectors as we can
|
||||||
|
err = read_sectors(cookie->flp, position / sectsize, len / sectsize);
|
||||||
|
DPRINT("PIO READ got %d sect\n", err);
|
||||||
|
if (err <= 0) {
|
||||||
|
*numbytes = 0;
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
memcpy(((char *)data) + bytes_read, cookie->flp->master->buffer + position % cylsize, err*sectsize);
|
||||||
|
len -= err * sectsize;
|
||||||
|
bytes_read += err * sectsize;
|
||||||
|
position += err * sectsize;
|
||||||
|
}
|
||||||
|
// handle partial last block
|
||||||
|
if(len > 0 && (len % sectsize) != 0) {
|
||||||
|
DPRINT("read: end %Ld, %ld %ld\n", position, bytes_read, len);
|
||||||
|
|
||||||
|
err = read_sectors(cookie->flp, position / sectsize, 1);
|
||||||
|
DPRINT("PIO READ got %d sect\n", err);
|
||||||
|
if (err <= 0) {
|
||||||
|
*numbytes = 0;
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
memcpy(((char *)data) + bytes_read, cookie->flp->master->buffer + position % cylsize, len);
|
||||||
|
bytes_read += len;
|
||||||
|
position += len;
|
||||||
|
}
|
||||||
|
|
||||||
|
*numbytes = bytes_read;
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
status_t flo_write(floppy_cookie *cookie, off_t position, const void *data, size_t *numbytes)
|
||||||
|
{
|
||||||
|
*numbytes = 0;
|
||||||
|
return B_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
status_t flo_control(floppy_cookie *cookie, uint32 op, void *data, size_t len)
|
||||||
|
{
|
||||||
|
device_geometry *geom;
|
||||||
|
status_t err;
|
||||||
|
floppy_t *flp = cookie->flp;
|
||||||
|
switch (op) {
|
||||||
|
case B_GET_ICON:
|
||||||
|
DPRINT("control(B_GET_ICON, %d)\n", ((device_icon *)data)->icon_size);
|
||||||
|
if (((device_icon *)data)->icon_size == 16) { /* mini icon */
|
||||||
|
memcpy(((device_icon *)data)->icon_data, floppy_mini_icon, (16*16));
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
if (((device_icon *)data)->icon_size == 32) { /* icon */
|
||||||
|
memcpy(((device_icon *)data)->icon_data, floppy_icon, (32*32));
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
return EINVAL;
|
||||||
|
case B_GET_BIOS_DRIVE_ID:
|
||||||
|
DPRINT("control(B_GET_BIOS_DRIVE_ID)\n");
|
||||||
|
*(uint8 *)data = 0;
|
||||||
|
return B_OK;
|
||||||
|
case B_GET_DEVICE_SIZE:
|
||||||
|
DPRINT("control(B_GET_DEVICE_SIZE)\n");
|
||||||
|
err = query_media(cookie->flp, true);
|
||||||
|
*(size_t *)data = (flp->bgeom.bytes_per_sector)
|
||||||
|
* (flp->bgeom.sectors_per_track)
|
||||||
|
* (flp->bgeom.head_count)
|
||||||
|
* (flp->bgeom.cylinder_count);
|
||||||
|
return B_OK;
|
||||||
|
case B_GET_GEOMETRY:
|
||||||
|
case B_GET_BIOS_GEOMETRY:
|
||||||
|
DPRINT("control(B_GET_(BIOS)_GEOMETRY)\n");
|
||||||
|
err = query_media(cookie->flp, false);
|
||||||
|
geom = (device_geometry *)data;
|
||||||
|
geom->bytes_per_sector = flp->bgeom.bytes_per_sector;
|
||||||
|
geom->sectors_per_track = flp->bgeom.sectors_per_track;
|
||||||
|
geom->cylinder_count = flp->bgeom.cylinder_count;
|
||||||
|
geom->head_count = flp->bgeom.head_count;
|
||||||
|
geom->device_type = B_DISK;
|
||||||
|
geom->removable = true;
|
||||||
|
geom->read_only = flp->bgeom.read_only;
|
||||||
|
geom->write_once = false;
|
||||||
|
return B_OK;
|
||||||
|
case B_GET_MEDIA_STATUS:
|
||||||
|
DPRINT("control(B_GET_MEDIA_STATUS)\n");
|
||||||
|
err = query_media(cookie->flp, false);
|
||||||
|
*(status_t *)data = err;
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
DPRINT("control(%ld)\n", op);
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void motor_off_daemon(void *t, int tim)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < MAX_FLOPPIES; i++) {
|
||||||
|
if (floppies[i].iobase && !floppies[i].pending_cmd && floppies[i].motor_timeout > 0) {
|
||||||
|
DPRINT("floppies[%d].motor_timeout = %ld\n", i, floppies[i].motor_timeout);
|
||||||
|
if (atomic_add(&floppies[i].motor_timeout, -500000) <= 500000) {
|
||||||
|
dprintf("turning off motor for drive %d\n", floppies[i].drive_num);
|
||||||
|
turn_off_motor(&floppies[i]);
|
||||||
|
floppies[i].motor_timeout = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
device_hooks floppy_hooks = {
|
||||||
|
(device_open_hook)flo_open,
|
||||||
|
(device_close_hook)flo_close,
|
||||||
|
(device_free_hook)flo_free,
|
||||||
|
(device_control_hook)flo_control,
|
||||||
|
(device_read_hook)flo_read,
|
||||||
|
(device_write_hook)flo_write,
|
||||||
|
NULL, /* select */
|
||||||
|
NULL, /* deselect */
|
||||||
|
NULL, /* readv */
|
||||||
|
NULL /* writev */
|
||||||
|
};
|
||||||
|
|
||||||
@@ -0,0 +1,215 @@
|
|||||||
|
/*
|
||||||
|
* OpenBeOS floppy driver
|
||||||
|
* (c) 2003, OpenBeOS project.
|
||||||
|
* François Revol, [email protected]
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _FLOPPY_H
|
||||||
|
#define _FLOPPY_H
|
||||||
|
|
||||||
|
#include <Drivers.h>
|
||||||
|
#include <ISA.h>
|
||||||
|
#include <KernelExport.h>
|
||||||
|
#include "lock.h"
|
||||||
|
|
||||||
|
#define FLO "floppy: "
|
||||||
|
#if DEBUG > 0
|
||||||
|
#define DPRINT(x...) dprintf(FLO x)
|
||||||
|
#else
|
||||||
|
#define DPRINT(x...)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define MOTOR_TIMEOUT 5000000 // 5 seconds
|
||||||
|
#define MOTOR_SPINUP_DELAY 500000 // 500 msecs
|
||||||
|
#define FLOPPY_CMD_TIMEOUT 1000000 // 5s
|
||||||
|
#define MAX_DRIVES_PER_CTRL 2
|
||||||
|
#define CYL_BUFFER_SIZE (5*B_PAGE_SIZE)
|
||||||
|
|
||||||
|
/*** those belong to ISA.h ***/
|
||||||
|
#define B_DMA_MODE_DEMAND 0x00 // modeByte bits for various dma modes
|
||||||
|
#define B_DMA_MODE_BLOCK 0x80
|
||||||
|
#define B_DMA_MODE_SINGLE 0x40
|
||||||
|
|
||||||
|
#define B_DMA_MODE_MEMR 0x08 // modeByte memory read or write
|
||||||
|
#define B_DMA_MODE_MEMW 0x04
|
||||||
|
/*** !ISA.h ***/
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
FLOP_NO_MEDIA,
|
||||||
|
FLOP_MEDIA_CHANGED,
|
||||||
|
FLOP_MEDIA_UNREADABLE,
|
||||||
|
FLOP_MEDIA_FORMAT_FOUND,
|
||||||
|
FLOP_WORKING
|
||||||
|
} floppy_status;
|
||||||
|
|
||||||
|
#define CMD_MODE 0x01
|
||||||
|
#define CMD_SETTRK 0x21 //21
|
||||||
|
#define CMD_READTRK 0x02
|
||||||
|
#define CMD_SPECIFY 0x03
|
||||||
|
#define CMD_SENSED 0x04
|
||||||
|
#define CMD_WRITED 0x05
|
||||||
|
#define CMD_READD 0x06
|
||||||
|
#define CMD_RECAL 0x07
|
||||||
|
#define CMD_SENSEI 0x08
|
||||||
|
#define CMD_WRITEDD 0x09
|
||||||
|
#define CMD_READID 0x0A
|
||||||
|
#define CMD_READDD 0x0C
|
||||||
|
#define CMD_FORMAT 0x0D
|
||||||
|
#define CMD_DUMPREG 0x0E
|
||||||
|
#define CMD_SEEK 0x0F
|
||||||
|
#define CMD_RELSEEK 0x8F //8F
|
||||||
|
#define CMD_VERSION 0x10
|
||||||
|
#define CMD_SCANE 0x11
|
||||||
|
#define CMD_PERPEND 0x12
|
||||||
|
#define CMD_CONFIG 0x13
|
||||||
|
#define CMD_LOCK 0x14
|
||||||
|
#define CMD_VERIFY 0x16
|
||||||
|
#define CMD_SCANLE 0x19
|
||||||
|
#define CMD_SCANHE 0x1D
|
||||||
|
|
||||||
|
#define CMD_RESET 0xF0
|
||||||
|
#define CMD_SWITCH_MASK 0x1F
|
||||||
|
|
||||||
|
typedef struct floppy_geometry {
|
||||||
|
int gap;
|
||||||
|
int numsectors;
|
||||||
|
int data_rate;
|
||||||
|
int format_gap;
|
||||||
|
int side2_offset;
|
||||||
|
device_geometry g;
|
||||||
|
int flags;
|
||||||
|
const char *name;
|
||||||
|
} floppy_geometry;
|
||||||
|
|
||||||
|
extern const floppy_geometry supported_geometries[];
|
||||||
|
|
||||||
|
#define FL_MFM 0x0001 /* MFM recording */
|
||||||
|
#define FL_2STEP 0x0002 /* 2 steps between cylinders */
|
||||||
|
#define FL_PERP 0x0004 /* perpendicular recording */
|
||||||
|
|
||||||
|
#define FDC_500KBPS 0x00 /* 500KBPS MFM drive transfer rate */
|
||||||
|
#define FDC_300KBPS 0x01 /* 300KBPS MFM drive transfer rate */
|
||||||
|
#define FDC_250KBPS 0x02 /* 250KBPS MFM drive transfer rate */
|
||||||
|
#define FDC_1MBPS 0x03 /* 1MPBS MFM drive transfer rate */
|
||||||
|
|
||||||
|
#define FDC_MAX_CYLINDER 85
|
||||||
|
|
||||||
|
typedef struct floppy {
|
||||||
|
const floppy_geometry *geometry;
|
||||||
|
device_geometry bgeom;
|
||||||
|
uint32 iobase; /* controller address */
|
||||||
|
uint32 irq;
|
||||||
|
int32 dma;
|
||||||
|
int drive_num; /* number of this drive */
|
||||||
|
int current; /* currently selected drive */
|
||||||
|
floppy_status status;
|
||||||
|
sem_id completion;
|
||||||
|
uint16 pending_cmd;
|
||||||
|
uint8 result[8]; /* status of the last finished command */
|
||||||
|
|
||||||
|
lock ben;
|
||||||
|
spinlock slock;
|
||||||
|
isa_module_info *isa;
|
||||||
|
|
||||||
|
uint8 data_rate; /* FDC_*BPS */
|
||||||
|
uint32 motor_timeout;
|
||||||
|
|
||||||
|
area_id buffer_area;
|
||||||
|
uint8 *buffer;
|
||||||
|
long buffer_index; /* index at which to put/get the next byte in the buffer */
|
||||||
|
long avail; /* valid bytes in the buffer */
|
||||||
|
int track; /* the track currently in the cylinder buffer */
|
||||||
|
|
||||||
|
struct floppy *master; /* the 'controller' */
|
||||||
|
struct floppy *next; /* next floppy for same controller */
|
||||||
|
} floppy_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8 id;
|
||||||
|
uint8 drive;
|
||||||
|
uint8 cylinder;
|
||||||
|
uint8 head;
|
||||||
|
uint8 sector;
|
||||||
|
uint8 sector_size;
|
||||||
|
uint8 track_length;
|
||||||
|
uint8 gap3_length;
|
||||||
|
uint8 data_length;
|
||||||
|
} floppy_command;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8 st0;
|
||||||
|
uint8 st1;
|
||||||
|
uint8 st2;
|
||||||
|
uint8 cylinder;
|
||||||
|
uint8 head;
|
||||||
|
uint8 sector;
|
||||||
|
uint8 sector_size;
|
||||||
|
} floppy_result;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
STATUS_A = 0,
|
||||||
|
STATUS_B = 1,
|
||||||
|
DIGITAL_OUT = 2,
|
||||||
|
TAPE_DRIVE = 3,
|
||||||
|
MAIN_STATUS = 4,
|
||||||
|
DATA_RATE_SELECT = 4,
|
||||||
|
DATA = 5,
|
||||||
|
/* RESERVED = 6, */
|
||||||
|
DIGITAL_IN = 7,
|
||||||
|
CONFIG_CONTROL = 7
|
||||||
|
} floppy_reg_selector;
|
||||||
|
|
||||||
|
#define FDC_SR0_IC 0xc0
|
||||||
|
#define FDC_SR0_SE 0x20
|
||||||
|
#define FDC_SR0_EC 0x10
|
||||||
|
#define FDC_SR0_HS 0x04
|
||||||
|
#define FDC_SR0_DS 0x03
|
||||||
|
|
||||||
|
/* low level */
|
||||||
|
extern void write_reg(floppy_t *flp, floppy_reg_selector selector, uint8 data);
|
||||||
|
extern uint8 read_reg(floppy_t *flp, floppy_reg_selector selector);
|
||||||
|
|
||||||
|
/* init */
|
||||||
|
/* initialize the controller */
|
||||||
|
extern void reset_controller(floppy_t *master);
|
||||||
|
/* returns a bitmap of the present drives */
|
||||||
|
extern int count_floppies(floppy_t *master);
|
||||||
|
/* seek to track 0 and detect drive presence */
|
||||||
|
extern int recalibrate_drive(floppy_t *flp);
|
||||||
|
/* debug */
|
||||||
|
/* issue dumpreg command */
|
||||||
|
extern void floppy_dump_reg(floppy_t *flp);
|
||||||
|
|
||||||
|
/* drive handling */
|
||||||
|
extern void drive_select(floppy_t *flp);
|
||||||
|
extern void drive_deselect(floppy_t *flp);
|
||||||
|
extern void turn_on_motor(floppy_t *flp);
|
||||||
|
extern void turn_off_motor(floppy_t *flp);
|
||||||
|
|
||||||
|
/* transactions */
|
||||||
|
extern void wait_for_rqm(floppy_t *flp);
|
||||||
|
extern bool can_read_result(floppy_t *flp);
|
||||||
|
/* send a command and schedule for irq */
|
||||||
|
extern int send_command(floppy_t *flp, const uint8 *data, int len);
|
||||||
|
extern int wait_til_ready(floppy_t *flp);
|
||||||
|
/* called by irq handler */
|
||||||
|
extern int read_result(floppy_t *flp, uint8 *data, int len);
|
||||||
|
/* wait for the irq to occur */
|
||||||
|
extern int wait_result(floppy_t *flp);
|
||||||
|
|
||||||
|
extern int32 flo_intr(void *arg);
|
||||||
|
|
||||||
|
/* read sectors to internal flp->master->buffer */
|
||||||
|
extern ssize_t pio_read_sectors(floppy_t *flp, int lba, int num_sectors);
|
||||||
|
/* write sectors from internal flp->master->buffer */
|
||||||
|
extern ssize_t pio_write_sectors(floppy_t *flp, int lba, int num_sectors);
|
||||||
|
/* read a track to internal flp->master->buffer */
|
||||||
|
extern ssize_t pio_read_track(floppy_t *flp, int lba);
|
||||||
|
|
||||||
|
/* high level */
|
||||||
|
/* query the media type and fill the geometry information */
|
||||||
|
extern status_t query_media(floppy_t *flp, bool forceupdate);
|
||||||
|
|
||||||
|
extern ssize_t read_sectors(floppy_t *flp, int lba, int num_sectors);
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,828 @@
|
|||||||
|
/*
|
||||||
|
* OpenBeOS floppy driver
|
||||||
|
* (c) 2003, OpenBeOS project.
|
||||||
|
* François Revol, [email protected]
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include <Drivers.h>
|
||||||
|
#include "floppy.h"
|
||||||
|
|
||||||
|
/* VERY verbose... */
|
||||||
|
//#define DEBUG_LOWLEVEL
|
||||||
|
|
||||||
|
// debug
|
||||||
|
void PRINT_SR0(uint8 sr0)
|
||||||
|
{
|
||||||
|
const char *result = "ok";
|
||||||
|
switch (sr0 & FDC_SR0_IC) {
|
||||||
|
case 0x80:
|
||||||
|
result = "invalid";
|
||||||
|
break;
|
||||||
|
case 0x40:
|
||||||
|
result = "abterm";
|
||||||
|
break;
|
||||||
|
case 0xc0:
|
||||||
|
result = "drvchngd";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
DPRINT("sr0: ds %d, hs %d, ec %d, se %d, %s\n", (sr0 & FDC_SR0_DS), !!(sr0 & FDC_SR0_HS), !!(sr0 & FDC_SR0_EC), !!(sr0 & FDC_SR0_SE), result);
|
||||||
|
}
|
||||||
|
|
||||||
|
void write_reg(floppy_t *flp, floppy_reg_selector selector, uint8 data)
|
||||||
|
{
|
||||||
|
#ifdef DEBUG_LOWLEVEL
|
||||||
|
DPRINT("write to 0x%lx, data 0x%x\n", flp->iobase + selector, data);
|
||||||
|
#endif
|
||||||
|
flp->isa->write_io_8(flp->iobase + selector, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8 read_reg(floppy_t *flp, floppy_reg_selector selector)
|
||||||
|
{
|
||||||
|
uint8 data;
|
||||||
|
data = flp->isa->read_io_8(flp->iobase + selector);
|
||||||
|
#ifdef DEBUG_LOWLEVEL
|
||||||
|
DPRINT("read from 0x%lx = 0x%x\n", flp->iobase + selector, data);
|
||||||
|
#endif
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset_controller(floppy_t *master)
|
||||||
|
{
|
||||||
|
uint8 command[4]; /* for SPECIFY & CONFIGURE */
|
||||||
|
uint8 result[1]; /* for SPECIFY */
|
||||||
|
|
||||||
|
DPRINT("reset_controller()\n");
|
||||||
|
LOCK(master->ben);
|
||||||
|
|
||||||
|
//master->pending_cmd = CMD_RESET;
|
||||||
|
master->pending_cmd = 0;
|
||||||
|
write_reg(master, DIGITAL_OUT, read_reg(master, DIGITAL_OUT) & ~0x04); /* reset */
|
||||||
|
//wait_result(master);
|
||||||
|
spin(20);
|
||||||
|
write_reg(master, DATA_RATE_SELECT, FDC_500KBPS); // 500 kbps
|
||||||
|
master->data_rate = FDC_500KBPS;
|
||||||
|
write_reg(master, DIGITAL_OUT, /*(master->dma < 0)?0x04:*//*0x0c*/0x04); /* motors[abcd] off, DMA on, deassert reset, drive select(0) */
|
||||||
|
|
||||||
|
wait_result(master); /* wait for reset interrupt */
|
||||||
|
|
||||||
|
command[0] = CMD_CONFIG; // configure command
|
||||||
|
command[1] = 0; // N/A
|
||||||
|
command[2] = 0x57;//0x70; // Implied Seek, FIFO, Poll Disable, THRESH = default
|
||||||
|
command[3] = 0; // PRETRK
|
||||||
|
|
||||||
|
send_command(master, command, sizeof(command));
|
||||||
|
|
||||||
|
command[0] = CMD_SPECIFY;
|
||||||
|
command[1] = (((16 - (3)) << 4) | ((240 / 16))); /* step rate 3ms, head unload time 240ms */
|
||||||
|
command[2] = 0x02 | 0x01; /* head load time 2ms, NO DMA */
|
||||||
|
|
||||||
|
send_command(master, command, 3); /* send SPECIFY */
|
||||||
|
read_result(master, result, 1);
|
||||||
|
|
||||||
|
UNLOCK(master->ben);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* returns a bitmap of the present drives */
|
||||||
|
int count_floppies(floppy_t *master)
|
||||||
|
{
|
||||||
|
int i, err;
|
||||||
|
int flops = 0;
|
||||||
|
|
||||||
|
master->master = master;
|
||||||
|
|
||||||
|
//floppy_dump_reg(master);
|
||||||
|
|
||||||
|
/* reset controller */
|
||||||
|
reset_controller(master);
|
||||||
|
|
||||||
|
|
||||||
|
for (i = 0; i < MAX_DRIVES_PER_CTRL; i++) {
|
||||||
|
master->drive_num = i; /* fake */
|
||||||
|
DPRINT("DETECTING DRIVE %d...\n", i);
|
||||||
|
turn_on_motor(master);
|
||||||
|
drive_select(master);
|
||||||
|
err = recalibrate_drive(master);
|
||||||
|
if (err == 0)
|
||||||
|
flops |= 1 << i;
|
||||||
|
DPRINT("DRIVE %d %s\n", i, err?"NOT here":"is here");
|
||||||
|
drive_deselect(master);
|
||||||
|
//snooze(50000);
|
||||||
|
//turn_off_motor(master);
|
||||||
|
}
|
||||||
|
|
||||||
|
// floppy_dump_reg(master);
|
||||||
|
|
||||||
|
master->drive_num = 0;
|
||||||
|
return flops;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* selects the drive until deselect(), takes the benaphore */
|
||||||
|
void drive_select(floppy_t *flp)
|
||||||
|
{
|
||||||
|
cpu_status st;
|
||||||
|
uint8 reg;
|
||||||
|
DPRINT("drive_select(%d)\n", flp->drive_num);
|
||||||
|
LOCK(flp->master->ben);
|
||||||
|
|
||||||
|
/* must be atomic to not change motor states! */
|
||||||
|
st = disable_interrupts();
|
||||||
|
acquire_spinlock(&(flp->master->slock));
|
||||||
|
|
||||||
|
/* make sure the speed for this drive is correct */
|
||||||
|
if (flp->geometry && (flp->master->data_rate != flp->geometry->data_rate)) {
|
||||||
|
write_reg(flp, DATA_RATE_SELECT, flp->geometry->data_rate); // 500 kbps
|
||||||
|
flp->master->data_rate = flp->geometry->data_rate;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* make sure the drive is selected and DMA is on */
|
||||||
|
reg = read_reg(flp, DIGITAL_OUT);
|
||||||
|
if((reg & 0x0b) != ((flp->drive_num & 0x03) | 0x08))
|
||||||
|
write_reg(flp, DIGITAL_OUT, (reg & 0xfc) | (flp->drive_num & 0x03) | /*(flp->master->dma < 0)?0x04:*/0x0c); /* DMA on, reset off */
|
||||||
|
|
||||||
|
release_spinlock(&(flp->master->slock));
|
||||||
|
restore_interrupts(st);
|
||||||
|
|
||||||
|
flp->master->current = flp->drive_num;
|
||||||
|
}
|
||||||
|
|
||||||
|
void drive_deselect(floppy_t *flp)
|
||||||
|
{
|
||||||
|
UNLOCK(flp->master->ben);
|
||||||
|
DPRINT("drive_deselect(%d)\n", flp->drive_num);
|
||||||
|
}
|
||||||
|
|
||||||
|
void turn_on_motor(floppy_t *flp)
|
||||||
|
{
|
||||||
|
cpu_status st;
|
||||||
|
uint8 reg;
|
||||||
|
bool do_snooze = false;
|
||||||
|
|
||||||
|
DPRINT("turn_on_motor(%d)\n", flp->drive_num);
|
||||||
|
flp->motor_timeout = MOTOR_TIMEOUT;
|
||||||
|
|
||||||
|
/* must be atomic to not deselect a drive! */
|
||||||
|
st = disable_interrupts();
|
||||||
|
acquire_spinlock(&(flp->master->slock));
|
||||||
|
|
||||||
|
if(((reg = read_reg(flp, DIGITAL_OUT)) & (0x10 << flp->drive_num)) == 0) {
|
||||||
|
/* it's off now, turn it on and wait */
|
||||||
|
//reg = 0x0c;
|
||||||
|
//reg &= 0xfc; /* mask out drive num */
|
||||||
|
write_reg(flp, DIGITAL_OUT, (0x10 << flp->drive_num) | reg);
|
||||||
|
do_snooze = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
release_spinlock(&(flp->master->slock));
|
||||||
|
restore_interrupts(st);
|
||||||
|
|
||||||
|
if (do_snooze)
|
||||||
|
snooze(MOTOR_SPINUP_DELAY);
|
||||||
|
}
|
||||||
|
|
||||||
|
void turn_off_motor(floppy_t *flp)
|
||||||
|
{
|
||||||
|
cpu_status st;
|
||||||
|
DPRINT("turn_off_motor(%d)\n", flp->drive_num);
|
||||||
|
|
||||||
|
/* must be atomic to not deselect a drive! */
|
||||||
|
st = disable_interrupts();
|
||||||
|
acquire_spinlock(&(flp->master->slock));
|
||||||
|
|
||||||
|
write_reg(flp, DIGITAL_OUT, read_reg(flp, DIGITAL_OUT) & ~(0x10 << flp->drive_num));
|
||||||
|
//write_reg(flp, DIGITAL_OUT, 0x4);
|
||||||
|
|
||||||
|
flp->motor_timeout = 0;
|
||||||
|
|
||||||
|
release_spinlock(&(flp->master->slock));
|
||||||
|
restore_interrupts(st);
|
||||||
|
}
|
||||||
|
|
||||||
|
void wait_for_rqm(floppy_t *flp)
|
||||||
|
{
|
||||||
|
while((read_reg(flp, MAIN_STATUS) & 0x80) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int send_command(floppy_t *flp, const uint8 *data, int len)
|
||||||
|
{
|
||||||
|
int i, status;
|
||||||
|
|
||||||
|
switch (data[0] & CMD_SWITCH_MASK) {
|
||||||
|
case CMD_SENSEI:
|
||||||
|
case CMD_SENSED:
|
||||||
|
case CMD_CONFIG:
|
||||||
|
case CMD_DUMPREG:
|
||||||
|
/* those don't generate an interrupt
|
||||||
|
* (SENSEI is sent by the intrrupt handler itself!)
|
||||||
|
*/
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
flp->pending_cmd = data[0];
|
||||||
|
}
|
||||||
|
for(i = 0; i < len; i++) {
|
||||||
|
status = wait_til_ready(flp);
|
||||||
|
if ((status < 0) || (status & 0x40))
|
||||||
|
break;
|
||||||
|
write_reg(flp, DATA, data[i]);
|
||||||
|
}
|
||||||
|
#ifdef DEBUG_LOWLEVEL
|
||||||
|
DPRINT("sent %d B\n", i);
|
||||||
|
#endif
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
int wait_result(floppy_t *flp) {
|
||||||
|
status_t err;
|
||||||
|
|
||||||
|
{ int32 c; get_sem_count(flp->completion, &c); DPRINT("SEM< %ld\n", c); }
|
||||||
|
|
||||||
|
if ((err = acquire_sem_etc(flp->completion, 1, B_TIMEOUT, FLOPPY_CMD_TIMEOUT)) < B_OK) {
|
||||||
|
if (err == B_TIMED_OUT) {
|
||||||
|
cpu_status st;
|
||||||
|
DPRINT("timed out! faking intr !!\n");
|
||||||
|
st = disable_interrupts();
|
||||||
|
flo_intr(flp);
|
||||||
|
restore_interrupts(st);
|
||||||
|
acquire_sem_etc(flp->completion, 1, B_TIMEOUT, FLOPPY_CMD_TIMEOUT);
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
{ int32 c; get_sem_count(flp->completion, &c); DPRINT("SEM> %ld\n", c); }
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int read_result(floppy_t *flp, uint8 *data, int len)
|
||||||
|
{
|
||||||
|
int i, status;
|
||||||
|
|
||||||
|
//if (flp->master->need_reset)
|
||||||
|
// return -1;
|
||||||
|
for(i = 0; i < len; i++) {
|
||||||
|
status = wait_til_ready(flp);
|
||||||
|
if ((status < 0) || ((status & 0x40) == 0))
|
||||||
|
break;
|
||||||
|
data[i] = read_reg(flp, DATA);
|
||||||
|
}
|
||||||
|
//flp->master->need_reset = 1;
|
||||||
|
#ifdef DEBUG_LOWLEVEL
|
||||||
|
DPRINT("read %d B\n", i);
|
||||||
|
#endif
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
int wait_til_ready(floppy_t *flp)
|
||||||
|
{
|
||||||
|
int i, status;
|
||||||
|
|
||||||
|
for (i = 0; i < 1000; i++)
|
||||||
|
if ((status = read_reg(flp, MAIN_STATUS)) & 0x80)
|
||||||
|
return status;
|
||||||
|
DPRINT("timeout waiting for %d !\n", flp->drive_num);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int has_drive_changed(floppy_t *flp)
|
||||||
|
{
|
||||||
|
return !!(read_reg(flp, DIGITAL_IN) & 0x80);
|
||||||
|
}
|
||||||
|
|
||||||
|
status_t query_media(floppy_t *flp, bool forceupdate)
|
||||||
|
{
|
||||||
|
status_t err = B_OK;
|
||||||
|
uint8 command[4];
|
||||||
|
uint8 result[7];
|
||||||
|
const floppy_geometry *geom = NULL;
|
||||||
|
|
||||||
|
|
||||||
|
DPRINT("query_media(%d, %s)\n", flp->drive_num, forceupdate?"true":"false");
|
||||||
|
|
||||||
|
turn_on_motor(flp);
|
||||||
|
drive_select(flp);
|
||||||
|
|
||||||
|
if (read_reg(flp, DIGITAL_IN) & 0x80) {/* media changed */
|
||||||
|
flp->status = FLOP_MEDIA_CHANGED;
|
||||||
|
DPRINT("media changed\n");
|
||||||
|
}
|
||||||
|
if (err || (flp->status < FLOP_MEDIA_UNREADABLE) || forceupdate) {
|
||||||
|
geom = supported_geometries;
|
||||||
|
DPRINT("querying format [err %08lx, st %d, fu %s]\n", err, flp->status, forceupdate?"t":"f");
|
||||||
|
|
||||||
|
/* zero the current geometry */
|
||||||
|
flp->geometry = 0;
|
||||||
|
flp->bgeom.bytes_per_sector = 255;
|
||||||
|
flp->bgeom.sectors_per_track = 0;
|
||||||
|
flp->bgeom.cylinder_count = 0;
|
||||||
|
flp->bgeom.head_count = 0;
|
||||||
|
flp->bgeom.read_only = true;
|
||||||
|
|
||||||
|
command[0] = 0x04; // sense drive command
|
||||||
|
command[1] = 0x00 | (flp->drive_num & 0x03); //
|
||||||
|
|
||||||
|
send_command(flp, command, 2);
|
||||||
|
|
||||||
|
read_result(flp, result, 1);
|
||||||
|
DPRINT("sense_drive(%d): wp %d, trk0 %d, hd %d, drivesel %d\n", flp->drive_num, !!(result[0]&0x40), !!(result[0]&0x10), !!(result[0]&0x04), (result[0]&0x03));
|
||||||
|
flp->bgeom.read_only = !!(result[0]&0x40);
|
||||||
|
|
||||||
|
for (; geom->numsectors; geom++) {
|
||||||
|
DPRINT("trying geometry %s\n", geom->name);
|
||||||
|
|
||||||
|
/* apply geometry parameters */
|
||||||
|
if (flp->master->data_rate != geom->data_rate) {
|
||||||
|
write_reg(flp, DATA_RATE_SELECT, geom->data_rate); // 500 kbps
|
||||||
|
flp->master->data_rate = geom->data_rate;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* seek track 0, head 0 */
|
||||||
|
command[0] = 0x0f; // seek command
|
||||||
|
command[1] = (flp->drive_num & 0x03) | 0x00; // drive | head 0
|
||||||
|
command[2] = 0x00; // track 0
|
||||||
|
|
||||||
|
DPRINT("SEEK at track 0 head 0\n");
|
||||||
|
send_command(flp, command, 3);
|
||||||
|
if (wait_result(flp) < 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* if it went there then there si something, even if we can't read it */
|
||||||
|
flp->status = FLOP_MEDIA_UNREADABLE;
|
||||||
|
|
||||||
|
//command[0] = 8; // sense interrupt command
|
||||||
|
//send_command(flp, command, 1);
|
||||||
|
|
||||||
|
//read_result(flp, result, 2); // read the result
|
||||||
|
PRINT_SR0(flp->result[0]);
|
||||||
|
DPRINT("track is %d\n", flp->result[1]);
|
||||||
|
if (flp->result[0] & FDC_SR0_IC)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
command[0] = 0x0a | ((geom->flags&FL_MFM)?0x40:0); // read track id
|
||||||
|
command[1] = (flp->drive_num & 0x03) | 0x00; // drive | head 0
|
||||||
|
|
||||||
|
DPRINT("READ_TRACK_ID\n");
|
||||||
|
send_command(flp, command, 2);
|
||||||
|
if (wait_result(flp) < 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
//read_result(flp, result, 7);
|
||||||
|
PRINT_SR0(flp->result[0]);
|
||||||
|
DPRINT("sr1: %02x\n", flp->result[1]);
|
||||||
|
DPRINT("sr2: %02x\n", flp->result[2]);
|
||||||
|
DPRINT("read id: track %d, head %d, sec %d, bps %d\n", flp->result[3], flp->result[4], flp->result[5], flp->result[6]);
|
||||||
|
if (flp->result[0] & FDC_SR0_IC)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* seek track 2, head 1 */
|
||||||
|
command[0] = 0x0f; // seek command
|
||||||
|
command[1] = (flp->drive_num & 0x03) | 0x04; // drive | head 1
|
||||||
|
command[2] = 0x02; // track 2
|
||||||
|
|
||||||
|
DPRINT("SEEK at track 2 head 1\n");
|
||||||
|
send_command(flp, command, 3);
|
||||||
|
if (wait_result(flp) < 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
//command[0] = 8; // sense interrupt command
|
||||||
|
//send_command(flp, command, 1);
|
||||||
|
|
||||||
|
//read_result(flp, result, 2); // read the result
|
||||||
|
PRINT_SR0(flp->result[0]);
|
||||||
|
DPRINT("track is %d\n", flp->result[1]);
|
||||||
|
if (flp->result[0] & FDC_SR0_IC)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
command[0] = 0x0a | ((geom->flags&FL_MFM)?0x40:0); // read track id
|
||||||
|
command[1] = (flp->drive_num & 0x03) | 0x00; // drive | head 0
|
||||||
|
|
||||||
|
DPRINT("READ_TRACK_ID\n");
|
||||||
|
send_command(flp, command, 2);
|
||||||
|
if (wait_result(flp) < 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
//read_result(flp, result, 7);
|
||||||
|
|
||||||
|
//read_result(flp, result, 7);
|
||||||
|
PRINT_SR0(flp->result[0]);
|
||||||
|
DPRINT("sr1: %02x\n", flp->result[1]);
|
||||||
|
DPRINT("sr2: %02x\n", flp->result[2]);
|
||||||
|
DPRINT("read id: track %d, head %d, sec %d, bps %d\n", flp->result[3], flp->result[4], flp->result[5], flp->result[6]);
|
||||||
|
if (flp->result[0] & FDC_SR0_IC)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (geom->numsectors) {
|
||||||
|
dprintf(FLO "drive %d: media type is %s\n", flp->drive_num, geom->name);
|
||||||
|
flp->status = FLOP_MEDIA_FORMAT_FOUND;
|
||||||
|
err = B_OK;
|
||||||
|
flp->geometry = geom;
|
||||||
|
flp->bgeom.bytes_per_sector = geom->g.bytes_per_sector;
|
||||||
|
flp->bgeom.sectors_per_track = geom->g.sectors_per_track;
|
||||||
|
flp->bgeom.cylinder_count = geom->g.cylinder_count;
|
||||||
|
flp->bgeom.head_count = geom->g.head_count;
|
||||||
|
//flp->bgeom.read_only = true;
|
||||||
|
} else {
|
||||||
|
flp->status = FLOP_NO_MEDIA;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (flp->status) {
|
||||||
|
case FLOP_NO_MEDIA:
|
||||||
|
err = B_DEV_NO_MEDIA;
|
||||||
|
break;
|
||||||
|
case FLOP_MEDIA_CHANGED:
|
||||||
|
err = B_DEV_MEDIA_CHANGED;
|
||||||
|
break;
|
||||||
|
case FLOP_MEDIA_UNREADABLE:
|
||||||
|
err = B_DEV_UNREADABLE;
|
||||||
|
break;
|
||||||
|
case FLOP_MEDIA_FORMAT_FOUND:
|
||||||
|
case FLOP_WORKING:
|
||||||
|
default:
|
||||||
|
err = B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
drive_deselect(flp);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
int recalibrate_drive(floppy_t *flp)
|
||||||
|
{
|
||||||
|
int retry;
|
||||||
|
|
||||||
|
DPRINT("recalibrate_drive(%d)\n", flp->drive_num);
|
||||||
|
|
||||||
|
turn_on_motor(flp);
|
||||||
|
|
||||||
|
for(retry = 0; retry < 1; retry++) {
|
||||||
|
uint8 command[2] = { 7, 0 }; // recalibrate command
|
||||||
|
uint8 result[2];
|
||||||
|
|
||||||
|
command[1] = flp->drive_num & 0x03;
|
||||||
|
// send the recalibrate command
|
||||||
|
send_command(flp, command, sizeof(command));
|
||||||
|
|
||||||
|
if (wait_result(flp) < 0)
|
||||||
|
return 3;
|
||||||
|
|
||||||
|
//command[0] = 8; // sense interrupt command
|
||||||
|
//send_command(flp, command, 1);
|
||||||
|
|
||||||
|
// read the result
|
||||||
|
//read_result(flp, result, sizeof(result));
|
||||||
|
if (flp->result[0] & 0xd0) {
|
||||||
|
DPRINT("recalibration failed\n");
|
||||||
|
return 2;
|
||||||
|
} if (flp->result[1] != 0) {
|
||||||
|
DPRINT("drive is at cylinder %d, didn't make it to 0\n", result[1]);
|
||||||
|
if (retry > 3)
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
// successful
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DPRINT("recalibration done\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32 flo_intr(void *arg)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
int len;
|
||||||
|
int32 err = B_UNHANDLED_INTERRUPT;
|
||||||
|
floppy_t *flp = (floppy_t *)arg;
|
||||||
|
floppy_t *master = flp->master;
|
||||||
|
|
||||||
|
if (master ==NULL)
|
||||||
|
return B_UNHANDLED_INTERRUPT;
|
||||||
|
acquire_spinlock(&master->slock);
|
||||||
|
|
||||||
|
while (flp && (flp->drive_num != master->current) && !flp->pending_cmd)
|
||||||
|
flp = flp->next;
|
||||||
|
if (flp) {
|
||||||
|
uint8 msr;
|
||||||
|
int got = 0;
|
||||||
|
msr = read_reg(flp, MAIN_STATUS);
|
||||||
|
//DPRINT("got irq for %d! MSR=%02x\n", flp->drive_num, msr);
|
||||||
|
if ((((flp->pending_cmd & CMD_SWITCH_MASK) == CMD_READD) ||
|
||||||
|
((flp->pending_cmd & CMD_SWITCH_MASK) == CMD_READTRK)) && ((msr & 0x60) == 0x60)) {
|
||||||
|
/* non DMA xfer(data) & need read */
|
||||||
|
/* uint8 command[1];
|
||||||
|
command[0] = CMD_SENSEI;
|
||||||
|
send_command(flp, command, 1);
|
||||||
|
len = read_result(flp, flp->result, 2);
|
||||||
|
PRINT_SR0(flp->result[0]);*/
|
||||||
|
|
||||||
|
//DPRINT("READi\n");
|
||||||
|
// while ((msr & 0x60) == 0x60 /*&& (got < 256)*/) {
|
||||||
|
while ((msr & 0xF0) == 0xF0 && (master->buffer_index < CYL_BUFFER_SIZE)) {
|
||||||
|
master->buffer[master->buffer_index++] = read_reg(flp, DATA);
|
||||||
|
master->avail++;
|
||||||
|
msr = read_reg(flp, MAIN_STATUS);
|
||||||
|
got++;
|
||||||
|
// spin(10);
|
||||||
|
/* if (got < 30)
|
||||||
|
DPRINT("%02x", msr);
|
||||||
|
else
|
||||||
|
spin(15);*/
|
||||||
|
// if (got > 255)
|
||||||
|
// break;
|
||||||
|
}
|
||||||
|
// DPRINT("intr: READ %d\n", got);
|
||||||
|
} else if (((flp->pending_cmd & CMD_SWITCH_MASK) == CMD_WRITED) && ((msr & 0x60) == 0x20)) {
|
||||||
|
/* non DMA xfer(data) & need write */
|
||||||
|
DPRINT("WRITEi\n");
|
||||||
|
|
||||||
|
} else {
|
||||||
|
len = 8;
|
||||||
|
// if (flp->pending_cmd != CMD_RESET)
|
||||||
|
len = read_result(flp, flp->result, len);
|
||||||
|
// else
|
||||||
|
// len = 0;
|
||||||
|
if (len < 0) {
|
||||||
|
DPRINT("buggy interrupt from %d !\n", flp->drive_num);
|
||||||
|
} else if (len < 1) { /* must pool the interrupt reason */
|
||||||
|
uint8 command[1] = { CMD_SENSEI };
|
||||||
|
for (i = 0; i < 4; i++) { /* might have to issue 4 SENSEI */
|
||||||
|
DPRINT("intr: %dth SENSEI\n", i+1);
|
||||||
|
if (send_command(flp, command, 1) < 1)
|
||||||
|
break;
|
||||||
|
len = read_result(flp, flp->result, 2);
|
||||||
|
if (len > 0)
|
||||||
|
PRINT_SR0(flp->result[0]);
|
||||||
|
if (len != 2)
|
||||||
|
break;
|
||||||
|
if ((flp->result[0] & (0x80|FDC_SR0_DS)) == master->current)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
DPRINT("RES(%d) %02x %02x %02x %02x\n", len, flp->result[0], flp->result[1], flp->result[2], flp->result[3]);
|
||||||
|
/* only do that for !READ && !WRITE */
|
||||||
|
flp->pending_cmd = 0;
|
||||||
|
release_sem_etc(flp->completion, 1, B_DO_NOT_RESCHEDULE);
|
||||||
|
}
|
||||||
|
err = B_HANDLED_INTERRUPT;
|
||||||
|
}
|
||||||
|
|
||||||
|
release_spinlock(&master->slock);
|
||||||
|
|
||||||
|
if (err == B_UNHANDLED_INTERRUPT)
|
||||||
|
DPRINT("unhandled irq!\n");
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void floppy_dump_reg(floppy_t *flp) {
|
||||||
|
uint8 command[1] = { 0x0e }; // dumpreg command
|
||||||
|
//uint8 result[10];
|
||||||
|
uint8 *result = flp->result;
|
||||||
|
|
||||||
|
send_command(flp, command, sizeof(command));
|
||||||
|
|
||||||
|
//wait_result(flp);
|
||||||
|
read_result(flp, result, 8);
|
||||||
|
|
||||||
|
dprintf(FLO "dumpreg: tracks - d1=%d d2=%d d3=%d d4=%d\n",
|
||||||
|
result[0], result[1], result[2], result[3]);
|
||||||
|
dprintf(FLO "dumpreg: step_rate_time=%d motor_off_time=%d motor_on_time=%d dma=%d\n",
|
||||||
|
result[4] >> 4, result[4] & 0x0f, result[5] >> 1, result[5] & 0x01);
|
||||||
|
dprintf(FLO "sec_per_trk/end_of_trk=0x%.2x lock=%d, byte[7]=0x%.2x\n",
|
||||||
|
result[6], result[7] >> 7,result[7]);
|
||||||
|
dprintf(FLO "gap=%d wg=%d eis=%d fifo=%d poll=%d thresh=%d pretrk=%d\n",
|
||||||
|
(result[7] & 0x02) >> 1, result[7] & 0x01, (result[8] & 0x40) >> 6,
|
||||||
|
(result[8] & 0x20) >> 5, (result[8] & 0x10) >> 4, result[8] & 0x0f, result[9]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void fill_command_from_lba(floppy_t *flp, floppy_command *cmd, int lba)
|
||||||
|
{
|
||||||
|
cmd->cylinder = lba / (flp->bgeom.sectors_per_track * flp->bgeom.head_count);
|
||||||
|
cmd->head = (lba / flp->bgeom.sectors_per_track) % flp->bgeom.head_count;
|
||||||
|
cmd->sector = lba % flp->bgeom.sectors_per_track + 1;
|
||||||
|
cmd->drive = (flp->drive_num & 0x3) | (cmd->head << 2) | 0x80; /* Implied Seek */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* does NOT check for valid track number */
|
||||||
|
ssize_t pio_read_sectors(floppy_t *flp, /*void *buf,*/ int lba, int num_sectors)
|
||||||
|
{
|
||||||
|
ssize_t transfered = 0;
|
||||||
|
floppy_command cmd;
|
||||||
|
uint8 cmd2[8];
|
||||||
|
uint8 result[4];
|
||||||
|
//floppy_result res;
|
||||||
|
|
||||||
|
if (flp->status < FLOP_MEDIA_FORMAT_FOUND)
|
||||||
|
return B_DEV_UNREADABLE;
|
||||||
|
turn_on_motor(flp);
|
||||||
|
drive_select(flp);
|
||||||
|
if (flp->geometry == NULL || !flp->bgeom.bytes_per_sector || !flp->bgeom.sectors_per_track) {
|
||||||
|
drive_deselect(flp);
|
||||||
|
return B_DEV_NO_MEDIA;
|
||||||
|
}
|
||||||
|
|
||||||
|
flp->track = -1;
|
||||||
|
|
||||||
|
num_sectors = MIN(num_sectors, (signed)(flp->bgeom.sectors_per_track - (lba % flp->bgeom.sectors_per_track)));
|
||||||
|
cmd.id = CMD_READD | /*0xc0*/0x40; // multi-track, read MFM, one head
|
||||||
|
cmd.sector_size = 2; /* 512 bytes */
|
||||||
|
cmd.track_length = num_sectors;//flp->bgeom.sectors_per_track;
|
||||||
|
cmd.gap3_length = flp->geometry->gap; //27; /* 3.5" floppy */
|
||||||
|
cmd.data_length = 0xff; /* don't care */
|
||||||
|
fill_command_from_lba(flp, &cmd, lba);
|
||||||
|
dprintf(FLO "pio_read_sector(%d, %d={%d,%d,%d}, %d)\n", flp->drive_num, lba, cmd.cylinder, cmd.head, cmd.sector, num_sectors);
|
||||||
|
if (num_sectors < 1) {
|
||||||
|
drive_deselect(flp);
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
//num_sectors = 1;
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
|
||||||
|
/* first issue sense interrupt, to find the current track */
|
||||||
|
DPRINT("track check: SENSEI\n");
|
||||||
|
cmd2[0] = CMD_SENSEI;
|
||||||
|
send_command(flp, cmd2, 1);
|
||||||
|
read_result(flp, result, 2);
|
||||||
|
PRINT_SR0(result[0]);
|
||||||
|
/* if the current track is not the one we want, seek */
|
||||||
|
if (result[1] != cmd.cylinder) {
|
||||||
|
/* seek */
|
||||||
|
cmd2[0] = CMD_SEEK; // seek command
|
||||||
|
cmd2[1] = (flp->drive_num & 0x3) | (cmd.head << 2);
|
||||||
|
cmd2[2] = cmd.cylinder; // track 0
|
||||||
|
|
||||||
|
DPRINT("SEEK at track %d head %d\n", cmd.cylinder, cmd.head);
|
||||||
|
send_command(flp, cmd2, 3);
|
||||||
|
if (wait_result(flp) < 0)
|
||||||
|
return ENXIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
flp->master->avail = 0;
|
||||||
|
flp->master->buffer_index = (lba % (flp->bgeom.sectors_per_track/* * flp->bgeom.head_count*/)) * flp->bgeom.bytes_per_sector;
|
||||||
|
//flp->pending_cmd = CMD_READD;
|
||||||
|
|
||||||
|
send_command(flp, (uint8 *)&cmd, sizeof(cmd));
|
||||||
|
|
||||||
|
|
||||||
|
if (wait_result(flp) < 0) {
|
||||||
|
drive_deselect(flp);
|
||||||
|
return ENXIO;
|
||||||
|
}
|
||||||
|
PRINT_SR0(flp->result[0]);
|
||||||
|
DPRINT("sr1: %02x\n", flp->result[1]);
|
||||||
|
DPRINT("sr2: %02x\n", flp->result[2]);
|
||||||
|
DPRINT("@ track %d, head %d, sec %d, bps %d\n", flp->result[3], flp->result[4], flp->result[5], flp->result[6]);
|
||||||
|
switch (flp->result[0] & FDC_SR0_IC) {
|
||||||
|
case 0x80:
|
||||||
|
transfered = EINVAL;
|
||||||
|
break;
|
||||||
|
case 0x40:
|
||||||
|
//if (flp->result[1] != 0x80) /* End Of Track is not really an error, actually it means it worked :) */
|
||||||
|
// transfered = ENXIO;
|
||||||
|
break;
|
||||||
|
case 0xc0:
|
||||||
|
transfered = B_DEV_MEDIA_CHANGED;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (transfered) {
|
||||||
|
drive_deselect(flp);
|
||||||
|
return ENXIO;
|
||||||
|
}
|
||||||
|
/* normal termination */
|
||||||
|
transfered = flp->avail;
|
||||||
|
/* if (transfered > 0)
|
||||||
|
memcpy(buf, flp->buffer, flp->avail);*/
|
||||||
|
|
||||||
|
drive_deselect(flp);
|
||||||
|
return num_sectors;//transfered;
|
||||||
|
}
|
||||||
|
|
||||||
|
ssize_t pio_write_sectors(floppy_t *flp, /*const void *buf,*/ int lba, int num_sectors)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* does NOT check for valid track number */
|
||||||
|
ssize_t pio_read_track(floppy_t *flp, /*void *buf,*/ int lba)
|
||||||
|
{
|
||||||
|
ssize_t transfered = 0;
|
||||||
|
floppy_command cmd;
|
||||||
|
uint8 cmd2[8];
|
||||||
|
uint8 result[4];
|
||||||
|
int tries = 0;
|
||||||
|
|
||||||
|
if (flp->status < FLOP_MEDIA_FORMAT_FOUND)
|
||||||
|
return B_DEV_UNREADABLE;
|
||||||
|
turn_on_motor(flp);
|
||||||
|
drive_select(flp);
|
||||||
|
if (flp->geometry == NULL || !flp->bgeom.bytes_per_sector || !flp->bgeom.sectors_per_track) {
|
||||||
|
drive_deselect(flp);
|
||||||
|
return B_DEV_NO_MEDIA;
|
||||||
|
}
|
||||||
|
|
||||||
|
flp->track = -1;
|
||||||
|
|
||||||
|
retry_track:
|
||||||
|
|
||||||
|
//num_sectors = MIN(num_sectors, (signed)(flp->bgeom.sectors_per_track - (lba % flp->bgeom.sectors_per_track)));
|
||||||
|
cmd.id = CMD_READTRK | 0x40; // read MFM
|
||||||
|
cmd.sector_size = 2; /* 512 bytes */
|
||||||
|
cmd.track_length = flp->bgeom.sectors_per_track;
|
||||||
|
cmd.gap3_length = flp->geometry->gap; //27; /* 3.5" floppy */
|
||||||
|
cmd.data_length = 0xff; /* don't care */
|
||||||
|
fill_command_from_lba(flp, &cmd, lba);
|
||||||
|
cmd.sector = 1;
|
||||||
|
dprintf(FLO "pio_read_track(%d, %d={%d,%d,%d}) try %d\n", flp->drive_num, lba, cmd.cylinder, cmd.head, cmd.sector, tries);
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
|
||||||
|
/* first issue sense interrupt, to find the current track */
|
||||||
|
DPRINT("track check: SENSEI\n");
|
||||||
|
cmd2[0] = CMD_SENSEI;
|
||||||
|
send_command(flp, cmd2, 1);
|
||||||
|
read_result(flp, result, 2);
|
||||||
|
PRINT_SR0(result[0]);
|
||||||
|
/* if the current track is not the one we want, seek */
|
||||||
|
if (result[1] != cmd.cylinder) {
|
||||||
|
/* seek */
|
||||||
|
cmd2[0] = CMD_SEEK; // seek command
|
||||||
|
cmd2[1] = (flp->drive_num & 0x3) | (cmd.head << 2);
|
||||||
|
cmd2[2] = cmd.cylinder; // track 0
|
||||||
|
|
||||||
|
DPRINT("SEEK at track %d head %d\n", cmd.cylinder, cmd.head);
|
||||||
|
send_command(flp, cmd2, 3);
|
||||||
|
if (wait_result(flp) < 0)
|
||||||
|
return ENXIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
flp->master->avail = 0;
|
||||||
|
flp->master->buffer_index = 0;//(lba % (flp->bgeom.sectors_per_track * flp->bgeom.head_count)) * flp->bgeom.bytes_per_sector;
|
||||||
|
|
||||||
|
send_command(flp, (uint8 *)&cmd, sizeof(cmd));
|
||||||
|
|
||||||
|
|
||||||
|
if (wait_result(flp) < 0) {
|
||||||
|
drive_deselect(flp);
|
||||||
|
return ENXIO;
|
||||||
|
}
|
||||||
|
PRINT_SR0(flp->result[0]);
|
||||||
|
DPRINT("sr1: %02x\n", flp->result[1]);
|
||||||
|
DPRINT("sr2: %02x\n", flp->result[2]);
|
||||||
|
DPRINT("@ track %d, head %d, sec %d, bps %d\n", flp->result[3], flp->result[4], flp->result[5], flp->result[6]);
|
||||||
|
switch (flp->result[0] & FDC_SR0_IC) {
|
||||||
|
case 0x80:
|
||||||
|
transfered = EINVAL;
|
||||||
|
break;
|
||||||
|
case 0x40:
|
||||||
|
dprintf(FLO "sr1: %02x\n", flp->result[1]);
|
||||||
|
if (flp->result[1] != 0x80) {/* End Of Track is not really an error, actually it means it worked :) */
|
||||||
|
if (/*(flp->result[1] == 0x10) && */tries < 3) { /* overrun */
|
||||||
|
tries++;
|
||||||
|
goto retry_track;
|
||||||
|
} else
|
||||||
|
transfered = EIO;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 0xc0:
|
||||||
|
transfered = B_DEV_MEDIA_CHANGED;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (transfered) {
|
||||||
|
drive_deselect(flp);
|
||||||
|
return ENXIO;
|
||||||
|
}
|
||||||
|
/* normal termination */
|
||||||
|
transfered = flp->avail;
|
||||||
|
/* if (transfered > 0)
|
||||||
|
memcpy(buf, flp->buffer, flp->avail);*/
|
||||||
|
flp->master->track = cmd.cylinder*flp->bgeom.head_count + cmd.head;
|
||||||
|
|
||||||
|
drive_deselect(flp);
|
||||||
|
return 1;//transfered;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ssize_t read_sectors(floppy_t *flp, int lba, int num_sectors)
|
||||||
|
{
|
||||||
|
ssize_t transfered = 0;
|
||||||
|
//return pio_read_sectors(flp, lba, num_sectors);
|
||||||
|
/* if (nothing cached || not the same track cached) */
|
||||||
|
dprintf(FLO "read_sector(%d, %d, %d)\n", flp->drive_num, lba, num_sectors);
|
||||||
|
num_sectors = MIN(num_sectors, (signed)(flp->bgeom.sectors_per_track - (lba % flp->bgeom.sectors_per_track)));
|
||||||
|
if ((flp->master->track < 0) ||
|
||||||
|
(flp->master->track != (signed)(lba / (flp->bgeom.sectors_per_track)))) {
|
||||||
|
if ((lba / (flp->bgeom.sectors_per_track)) >= (flp->bgeom.head_count * flp->bgeom.cylinder_count))
|
||||||
|
return ENXIO;
|
||||||
|
transfered = pio_read_track(flp, lba);
|
||||||
|
if (transfered < 0)
|
||||||
|
return transfered;
|
||||||
|
/* XXX: TODO: in case of IO error, retry by single sector */
|
||||||
|
}
|
||||||
|
return num_sectors;
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
Copyright 1999, Be Incorporated. All Rights Reserved.
|
||||||
|
This file may be used under the terms of the Be Sample Code License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _LOCK_H
|
||||||
|
#define _LOCK_H
|
||||||
|
|
||||||
|
#include <BeBuild.h>
|
||||||
|
|
||||||
|
#include <OS.h>
|
||||||
|
|
||||||
|
typedef struct lock lock;
|
||||||
|
typedef struct mlock mlock;
|
||||||
|
|
||||||
|
struct lock {
|
||||||
|
sem_id s;
|
||||||
|
long c;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct mlock {
|
||||||
|
sem_id s;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern _IMPEXP_KERNEL int new_lock(lock *l, const char *name);
|
||||||
|
extern _IMPEXP_KERNEL int free_lock(lock *l);
|
||||||
|
|
||||||
|
#define LOCK(l) if (atomic_add(&l.c, -1) <= 0) acquire_sem(l.s);
|
||||||
|
#define UNLOCK(l) if (atomic_add(&l.c, 1) < 0) release_sem(l.s);
|
||||||
|
|
||||||
|
extern _IMPEXP_KERNEL int new_mlock(mlock *l, long c, const char *name);
|
||||||
|
extern _IMPEXP_KERNEL int free_mlock(mlock *l);
|
||||||
|
|
||||||
|
#define LOCKM(l,cnt) acquire_sem_etc(l.s, cnt, 0, 0)
|
||||||
|
#define UNLOCKM(l,cnt) release_sem_etc(l.s, cnt, 0)
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,120 @@
|
|||||||
|
## BeOS Generic Makefile v2.2 ##
|
||||||
|
|
||||||
|
## Fill in this file to specify the project being created, and the referenced
|
||||||
|
## makefile-engine will do all of the hard work for you. This handles both
|
||||||
|
## Intel and PowerPC builds of the BeOS.
|
||||||
|
|
||||||
|
## Application Specific Settings ---------------------------------------------
|
||||||
|
|
||||||
|
# specify the name of the binary
|
||||||
|
NAME= floppy
|
||||||
|
|
||||||
|
# specify the type of binary
|
||||||
|
# APP: Application
|
||||||
|
# SHARED: Shared library or add-on
|
||||||
|
# STATIC: Static library archive
|
||||||
|
# DRIVER: Kernel Driver
|
||||||
|
TYPE= DRIVER
|
||||||
|
|
||||||
|
# add support for new Pe and Eddie features
|
||||||
|
# to fill in generic makefile
|
||||||
|
|
||||||
|
#%{
|
||||||
|
# @src->@
|
||||||
|
|
||||||
|
# specify the source files to use
|
||||||
|
# full paths or paths relative to the makefile can be included
|
||||||
|
# all files, regardless of directory, will have their object
|
||||||
|
# files created in the common object directory.
|
||||||
|
# Note that this means this makefile will not work correctly
|
||||||
|
# if two source files with the same name (source.c or source.cpp)
|
||||||
|
# are included from different directories. Also note that spaces
|
||||||
|
# in folder names do not work well with this makefile.
|
||||||
|
SRCS= floppy.c floppy_ctrl.c device_icons.c
|
||||||
|
|
||||||
|
# specify the resource files to use
|
||||||
|
# full path or a relative path to the resource file can be used.
|
||||||
|
RSRCS=
|
||||||
|
|
||||||
|
# @<-src@
|
||||||
|
#%}
|
||||||
|
|
||||||
|
# end support for Pe and Eddie
|
||||||
|
|
||||||
|
# specify additional libraries to link against
|
||||||
|
# there are two acceptable forms of library specifications
|
||||||
|
# - if your library follows the naming pattern of:
|
||||||
|
# libXXX.so or libXXX.a you can simply specify XXX
|
||||||
|
# library: libbe.so entry: be
|
||||||
|
#
|
||||||
|
# - if your library does not follow the standard library
|
||||||
|
# naming scheme you need to specify the path to the library
|
||||||
|
# and it's name
|
||||||
|
# library: my_lib.a entry: my_lib.a or path/my_lib.a
|
||||||
|
LIBS=
|
||||||
|
|
||||||
|
# specify additional paths to directories following the standard
|
||||||
|
# libXXX.so or libXXX.a naming scheme. You can specify full paths
|
||||||
|
# or paths relative to the makefile. The paths included may not
|
||||||
|
# be recursive, so include all of the paths where libraries can
|
||||||
|
# be found. Directories where source files are found are
|
||||||
|
# automatically included.
|
||||||
|
LIBPATHS=
|
||||||
|
|
||||||
|
# additional paths to look for system headers
|
||||||
|
# thes use the form: #include <header>
|
||||||
|
# source file directories are NOT auto-included here
|
||||||
|
SYSTEM_INCLUDE_PATHS =
|
||||||
|
|
||||||
|
# additional paths to look for local headers
|
||||||
|
# thes use the form: #include "header"
|
||||||
|
# source file directories are automatically included
|
||||||
|
LOCAL_INCLUDE_PATHS =
|
||||||
|
|
||||||
|
# specify the level of optimization that you desire
|
||||||
|
# NONE, SOME, FULL
|
||||||
|
OPTIMIZE=
|
||||||
|
|
||||||
|
# specify any preprocessor symbols to be defined. The symbols will not
|
||||||
|
# have their values set automatically; you must supply the value (if any)
|
||||||
|
# to use. For example, setting DEFINES to "DEBUG=1" will cause the
|
||||||
|
# compiler option "-DDEBUG=1" to be used. Setting DEFINES to "DEBUG"
|
||||||
|
# would pass "-DDEBUG" on the compiler's command line.
|
||||||
|
DEFINES= DEBUG=0
|
||||||
|
|
||||||
|
# specify special warning levels
|
||||||
|
# if unspecified default warnings will be used
|
||||||
|
# NONE = supress all warnings
|
||||||
|
# ALL = enable all warnings
|
||||||
|
WARNINGS = ALL
|
||||||
|
|
||||||
|
# specify whether image symbols will be created
|
||||||
|
# so that stack crawls in the debugger are meaningful
|
||||||
|
# if TRUE symbols will be created
|
||||||
|
SYMBOLS = TRUE
|
||||||
|
|
||||||
|
# specify debug settings
|
||||||
|
# if TRUE will allow application to be run from a source-level
|
||||||
|
# debugger. Note that this will disable all optimzation.
|
||||||
|
DEBUGGER = TRUE
|
||||||
|
|
||||||
|
# specify additional compiler flags for all files
|
||||||
|
COMPILER_FLAGS =
|
||||||
|
|
||||||
|
# specify additional linker flags
|
||||||
|
LINKER_FLAGS =
|
||||||
|
|
||||||
|
# specify the version of this particular item
|
||||||
|
# (for example, -app 3 4 0 d 0 -short 340 -long "340 "`echo -n -e '\302\251'`"1999 GNU GPL")
|
||||||
|
# This may also be specified in a resource.
|
||||||
|
APP_VERSION =
|
||||||
|
|
||||||
|
# (for TYPE == DRIVER only) Specify desired location of driver in the /dev
|
||||||
|
# hierarchy. Used by the driverinstall rule. E.g., DRIVER_PATH = video/usb will
|
||||||
|
# instruct the driverinstall rule to place a symlink to your driver's binary in
|
||||||
|
# ~/add-ons/kernel/drivers/dev/video/usb, so that your driver will appear at
|
||||||
|
# /dev/video/usb when loaded. Default is "misc".
|
||||||
|
DRIVER_PATH = disk/floppy
|
||||||
|
|
||||||
|
## include the makefile-engine
|
||||||
|
include $(BUILDHOME)/etc/makefile-engine
|
||||||
Reference in New Issue
Block a user