diff --git a/src/add-ons/kernel/drivers/arch/x86/floppy/floppy.c b/src/add-ons/kernel/drivers/arch/x86/floppy/floppy.c new file mode 100644 index 0000000000..f82e6e8708 --- /dev/null +++ b/src/add-ons/kernel/drivers/arch/x86/floppy/floppy.c @@ -0,0 +1,523 @@ +/* + * OpenBeOS floppy driver + * (c) 2003, OpenBeOS project. + * François Revol, revol@free.fr + */ + +/* + * 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 +#include +#include +#include +#include +#include +#include +#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 */ +}; + diff --git a/src/add-ons/kernel/drivers/arch/x86/floppy/floppy.h b/src/add-ons/kernel/drivers/arch/x86/floppy/floppy.h new file mode 100644 index 0000000000..2e5226acf5 --- /dev/null +++ b/src/add-ons/kernel/drivers/arch/x86/floppy/floppy.h @@ -0,0 +1,215 @@ +/* + * OpenBeOS floppy driver + * (c) 2003, OpenBeOS project. + * François Revol, revol@free.fr + */ + +#ifndef _FLOPPY_H +#define _FLOPPY_H + +#include +#include +#include +#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 \ No newline at end of file diff --git a/src/add-ons/kernel/drivers/arch/x86/floppy/floppy_ctrl.c b/src/add-ons/kernel/drivers/arch/x86/floppy/floppy_ctrl.c new file mode 100644 index 0000000000..0b4a70b332 --- /dev/null +++ b/src/add-ons/kernel/drivers/arch/x86/floppy/floppy_ctrl.c @@ -0,0 +1,828 @@ +/* + * OpenBeOS floppy driver + * (c) 2003, OpenBeOS project. + * François Revol, revol@free.fr + */ + + +#include +#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; +} + diff --git a/src/add-ons/kernel/drivers/arch/x86/floppy/lock.h b/src/add-ons/kernel/drivers/arch/x86/floppy/lock.h new file mode 100644 index 0000000000..aed1b78091 --- /dev/null +++ b/src/add-ons/kernel/drivers/arch/x86/floppy/lock.h @@ -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 + +#include + +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 diff --git a/src/add-ons/kernel/drivers/arch/x86/floppy/makefile b/src/add-ons/kernel/drivers/arch/x86/floppy/makefile new file mode 100644 index 0000000000..b10306151e --- /dev/null +++ b/src/add-ons/kernel/drivers/arch/x86/floppy/makefile @@ -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
+# 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