OpenCores
URL https://opencores.org/ocsvn/or1k_old/or1k_old/trunk

Subversion Repositories or1k_old

Compare Revisions

  • This comparison shows the changes necessary to convert path
    /or1k_old/trunk/rc203soc/sw/uClinux/arch/armnommu/drivers/block
    from Rev 1765 to Rev 1782
    Reverse comparison

Rev 1765 → Rev 1782

/mfm.S
0,0 → 1,163
@ Read/Write DMA code for the ST506/MFM hard drive controllers on the A400
@ motherboard on ST506 podules.
@ (c) David Alan Gilbert (gilbertd@cs.man.ac.uk) 1996
 
#include <asm/assembler.h>
_hdc63463_irqdata:
@ Controller base address
.global _hdc63463_baseaddress
_hdc63463_baseaddress:
.word 0
 
.global _hdc63463_irqpolladdress
_hdc63463_irqpolladdress:
.word 0
.global _hdc63463_irqpollmask
_hdc63463_irqpollmask:
.word 0
 
@ where to read/write data from the kernel data space
.global _hdc63463_dataptr
_hdc63463_dataptr:
.word 0
 
@ Number of bytes left to transfer
.global _hdc63463_dataleft
_hdc63463_dataleft:
.word 0
 
@ -------------------------------------------------------------------------
@ hdc63463_writedma: DMA from host to controller
@ internal reg usage: r0=hdc base address, r1=irq poll address, r2=poll mask
@ r3=data ptr, r4=data left, r5,r6=temporary
.global _hdc63463_writedma
_hdc63463_writedma:
stmfd sp!,{r4-r7}
adr r5,_hdc63463_irqdata
ldmia r5,{r0,r1,r2,r3,r4}
 
 
writedma_again:
 
@ test number of remaining bytes to transfer
cmp r4,#0
beq writedma_end
bmi writedma_end
 
@ Check the hdc is interrupting
ldrb r5,[r1,#0]
tst r5,r2
beq writedma_end
 
@ Transfer a block of upto 256 bytes
cmp r4,#256
movlt r7,r4
movge r7,#256
 
@ Check the hdc is still busy and command has not ended and no errors
ldr r5,[r0,#32] @ Status reg - 16 bit - its the top few bits which are status
@ think we should continue DMA until it drops busy - perhaps this was
@ the main problem with corrected errors causing a hang
@tst r5,#0x3c00 @ Test for things which should be off
@bne writedma_end
and r5,r5,#0x8000 @ This is test for things which should be on: Busy
cmp r5,#0x8000
bne writedma_end
 
@ Bytes remaining at end
sub r4,r4,r7
 
@ HDC Write register location
add r0,r0,#32+8
 
writedma_loop:
@ OK - pretty sure we should be doing this
 
ldr r5,[r3],#4 @ Get a word to be written
@ get bottom half to be sent first
mov r6,r5,lsl#16 @ Separate the first 2 bytes
orr r2,r6,r6,lsr #16 @ Duplicate them in the bottom half of the word
@ now the top half
mov r6,r5,lsr#16 @ Get 2nd 2 bytes
orr r6,r6,r6,lsl#16 @ Duplicate
@str r6,[r0] @ to hdc
stmia r0,{r2,r6}
subs r7,r7,#4 @ Dec. number of bytes left
bne writedma_loop
 
@ If we were too slow we had better go through again - DAG - took out with new interrupt routine
@ sub r0,r0,#32+8
@ adr r2,_hdc63463_irqdata
@ ldr r2,[r2,#8]
@ b writedma_again
 
writedma_end:
adr r5,_hdc63463_irqdata+12
stmia r5,{r3,r4}
ldmfd sp!,{r4-r7}
RETINSTR(mov,pc,lr)
 
@ -------------------------------------------------------------------------
@ hdc63463_readdma: DMA from controller to host
@ internal reg usage: r0=hdc base address, r1=irq poll address, r2=poll mask
@ r3=data ptr, r4=data left, r5,r6=temporary
.global _hdc63463_readdma
_hdc63463_readdma:
stmfd sp!,{r4-r7}
adr r5,_hdc63463_irqdata
ldmia r5,{r0,r1,r2,r3,r4}
 
readdma_again:
@ test number of remaining bytes to transfer
cmp r4,#0
beq readdma_end
bmi readdma_end
 
@ Check the hdc is interrupting
ldrb r5,[r1,#0]
tst r5,r2
beq readdma_end
 
@ Check the hdc is still busy and command has not ended and no errors
ldr r5,[r0,#32] @ Status reg - 16 bit - its the top few bits which are status
@ think we should continue DMA until it drops busy - perhaps this was
@ the main problem with corrected errors causing a hang
@tst r5,#0x3c00 @ Test for things which should be off
@bne readdma_end
and r5,r5,#0x8000 @ This is test for things which should be on: Busy
cmp r5,#0x8000
bne readdma_end
 
@ Transfer a block of upto 256 bytes
cmp r4,#256
movlt r7,r4
movge r7,#256
 
@ Bytes remaining at end
sub r4,r4,r7
 
@ Set a pointer to the data register in the HDC
add r0,r0,#8
readdma_loop:
@ OK - pretty sure we should be doing this
ldmia r0,{r5,r6}
mov r5,r5,lsl#16
mov r6,r6,lsl#16
orr r6,r6,r5,lsr #16
str r6,[r3],#4
subs r7,r7,#4 @ Decrement bytes to go
bne readdma_loop
 
@ Try reading multiple blocks - if this was fast enough then I dont think
@ this should help - NO taken out DAG - new interrupt handler has
@ non-consecutive memory blocks
@ sub r0,r0,#8
@ b readdma_again
 
readdma_end:
adr r5,_hdc63463_irqdata+12
stmia r5,{r3,r4}
ldmfd sp!,{r4-r7}
RETINSTR(mov,pc,lr)
/ide.c
0,0 → 1,3913
/*
* linux/drivers/block/ide.c Version 5.53 Jun 24, 1997
*
* Copyright (C) 1994-1996 Linus Torvalds & authors (see below)
*/
#define _IDE_C /* needed by <linux/blk.h> */
 
/*
* Maintained by Mark Lord <mlord@pobox.com>
* and Gadi Oxman <gadio@netvision.net.il>
*
* This is the multiple IDE interface driver, as evolved from hd.c.
* It supports up to four IDE interfaces, on one or more IRQs (usually 14 & 15).
* There can be up to two drives per interface, as per the ATA-2 spec.
*
* Primary: ide0, port 0x1f0; major=3; hda is minor=0; hdb is minor=64
* Secondary: ide1, port 0x170; major=22; hdc is minor=0; hdd is minor=64
* Tertiary: ide2, port 0x???; major=33; hde is minor=0; hdf is minor=64
* Quaternary: ide3, port 0x???; major=34; hdg is minor=0; hdh is minor=64
*
* It is easy to extend ide.c to handle more than four interfaces:
*
* Change the MAX_HWIFS constant in ide.h.
*
* Define some new major numbers (in major.h), and insert them into
* the ide_hwif_to_major table in ide.c.
*
* Fill in the extra values for the new interfaces into the two tables
* inside ide.c: default_io_base[] and default_irqs[].
*
* Create the new request handlers by cloning "do_ide3_request()"
* for each new interface, and add them to the switch statement
* in the ide_init() function in ide.c.
*
* Recompile, create the new /dev/ entries, and it will probably work.
*
* From hd.c:
* |
* | It traverses the request-list, using interrupts to jump between functions.
* | As nearly all functions can be called within interrupts, we may not sleep.
* | Special care is recommended. Have Fun!
* |
* | modified by Drew Eckhardt to check nr of hd's from the CMOS.
* |
* | Thanks to Branko Lankester, lankeste@fwi.uva.nl, who found a bug
* | in the early extended-partition checks and added DM partitions.
* |
* | Early work on error handling by Mika Liljeberg (liljeber@cs.Helsinki.FI).
* |
* | IRQ-unmask, drive-id, multiple-mode, support for ">16 heads",
* | and general streamlining by Mark Lord (mlord@pobox.com).
*
* October, 1994 -- Complete line-by-line overhaul for linux 1.1.x, by:
*
* Mark Lord (mlord@pobox.com) (IDE Perf.Pkg)
* Delman Lee (delman@mipg.upenn.edu) ("Mr. atdisk2")
* Scott Snyder (snyder@fnald0.fnal.gov) (ATAPI IDE cd-rom)
*
* This was a rewrite of just about everything from hd.c, though some original
* code is still sprinkled about. Think of it as a major evolution, with
* inspiration from lots of linux users, esp. hamish@zot.apana.org.au
*
* Version 1.0 ALPHA initial code, primary i/f working okay
* Version 1.3 BETA dual i/f on shared irq tested & working!
* Version 1.4 BETA added auto probing for irq(s)
* Version 1.5 BETA added ALPHA (untested) support for IDE cd-roms,
* ...
* Version 3.5 correct the bios_cyl field if it's too small
* (linux 1.1.76) (to help fdisk with brain-dead BIOSs)
* Version 3.6 cosmetic corrections to comments and stuff
* (linux 1.1.77) reorganise probing code to make it understandable
* added halfway retry to probing for drive identification
* added "hdx=noprobe" command line option
* allow setting multmode even when identification fails
* Version 3.7 move set_geometry=1 from do_identify() to ide_init()
* increase DRQ_WAIT to eliminate nuisance messages
* wait for DRQ_STAT instead of DATA_READY during probing
* (courtesy of Gary Thomas gary@efland.UU.NET)
* Version 3.8 fixed byte-swapping for confused Mitsumi cdrom drives
* update of ide-cd.c from Scott, allows blocksize=1024
* cdrom probe fixes, inspired by jprang@uni-duisburg.de
* Version 3.9 don't use LBA if lba_capacity looks funny
* correct the drive capacity calculations
* fix probing for old Seagates without IDE_ALTSTATUS_REG
* fix byte-ordering for some NEC cdrom drives
* Version 3.10 disable multiple mode by default; was causing trouble
* Version 3.11 fix mis-identification of old WD disks as cdroms
* Version 3,12 simplify logic for selecting initial mult_count
* (fixes problems with buggy WD drives)
* Version 3.13 remove excess "multiple mode disabled" messages
* Version 3.14 fix ide_error() handling of BUSY_STAT
* fix byte-swapped cdrom strings (again.. arghh!)
* ignore INDEX bit when checking the ALTSTATUS reg
* Version 3.15 add SINGLE_THREADED flag for use with dual-CMD i/f
* ignore WRERR_STAT for non-write operations
* added vlb_sync support for DC-2000A & others,
* (incl. some Promise chips), courtesy of Frank Gockel
* Version 3.16 convert vlb_32bit and vlb_sync into runtime flags
* add ioctls to get/set VLB flags (HDIO_[SG]ET_CHIPSET)
* rename SINGLE_THREADED to SUPPORT_SERIALIZE,
* add boot flag to "serialize" operation for CMD i/f
* add optional support for DTC2278 interfaces,
* courtesy of andy@cercle.cts.com (Dyan Wile).
* add boot flag to enable "dtc2278" probe
* add probe to avoid EATA (SCSI) interfaces,
* courtesy of neuffer@goofy.zdv.uni-mainz.de.
* Version 4.00 tidy up verify_area() calls - heiko@colossus.escape.de
* add flag to ignore WRERR_STAT for some drives
* courtesy of David.H.West@um.cc.umich.edu
* assembly syntax tweak to vlb_sync
* removable drive support from scuba@cs.tu-berlin.de
* add transparent support for DiskManager-6.0x "Dynamic
* Disk Overlay" (DDO), most of this is in genhd.c
* eliminate "multiple mode turned off" message at boot
* Version 4.10 fix bug in ioctl for "hdparm -c3"
* fix DM6:DDO support -- now works with LILO, fdisk, ...
* don't treat some naughty WD drives as removable
* Version 4.11 updated DM6 support using info provided by OnTrack
* Version 5.00 major overhaul, multmode setting fixed, vlb_sync fixed
* added support for 3rd/4th/alternative IDE ports
* created ide.h; ide-cd.c now compiles separate from ide.c
* hopefully fixed infinite "unexpected_intr" from cdroms
* zillions of other changes and restructuring
* somehow reduced overall memory usage by several kB
* probably slowed things down slightly, but worth it
* Version 5.01 AT LAST!! Finally understood why "unexpected_intr"
* was happening at various times/places: whenever the
* ide-interface's ctl_port was used to "mask" the irq,
* it also would trigger an edge in the process of masking
* which would result in a self-inflicted interrupt!!
* (such a stupid way to build a hardware interrupt mask).
* This is now fixed (after a year of head-scratching).
* Version 5.02 got rid of need for {enable,disable}_irq_list()
* Version 5.03 tune-ups, comments, remove "busy wait" from drive resets
* removed PROBE_FOR_IRQS option -- no longer needed
* OOOPS! fixed "bad access" bug for 2nd drive on an i/f
* Version 5.04 changed "ira %d" to "irq %d" in DEBUG message
* added more comments, cleaned up unexpected_intr()
* OOOPS! fixed null pointer problem in ide reset code
* added autodetect for Triton chipset -- no effect yet
* Version 5.05 OOOPS! fixed bug in revalidate_disk()
* OOOPS! fixed bug in ide_do_request()
* added ATAPI reset sequence for cdroms
* Version 5.10 added Bus-Mastered DMA support for Triton Chipset
* some (mostly) cosmetic changes
* Version 5.11 added ht6560b support by malafoss@snakemail.hut.fi
* reworked PCI scanning code
* added automatic RZ1000 detection/support
* added automatic PCI CMD640 detection/support
* added option for VLB CMD640 support
* tweaked probe to find cdrom on hdb with disks on hda,hdc
* Version 5.12 some performance tuning
* added message to alert user to bad /dev/hd[cd] entries
* OOOPS! fixed bug in atapi reset
* driver now forces "serialize" again for all cmd640 chips
* noticed REALLY_SLOW_IO had no effect, moved it to ide.c
* made do_drive_cmd() into public ide_do_drive_cmd()
* Version 5.13 fixed typo ('B'), thanks to houston@boyd.geog.mcgill.ca
* fixed ht6560b support
* Version 5.13b (sss) fix problem in calling ide_cdrom_setup()
* don't bother invalidating nonexistent partitions
* Version 5.14 fixes to cmd640 support.. maybe it works now(?)
* added & tested full EZ-DRIVE support -- don't use LILO!
* don't enable 2nd CMD640 PCI port during init - conflict
* Version 5.15 bug fix in init_cmd640_vlb()
* bug fix in interrupt sharing code
* Version 5.16 ugh.. fix "serialize" support, broken in 5.15
* remove "Huh?" from cmd640 code
* added qd6580 interface speed select from Colten Edwards
* Version 5.17 kludge around bug in BIOS32 on Intel triton motherboards
* Version 5.18 new CMD640 code, moved to cmd640.c, #include'd for now
* new UMC8672 code, moved to umc8672.c, #include'd for now
* disallow turning on DMA when h/w not capable of DMA
* Version 5.19 fix potential infinite timeout on resets
* extend reset poll into a general purpose polling scheme
* add atapi tape drive support from Gadi Oxman
* simplify exit from _intr routines -- no IDE_DO_REQUEST
* Version 5.20 leave current rq on blkdev request list during I/O
* generalized ide_do_drive_cmd() for tape/cdrom driver use
* Version 5.21 fix nasty cdrom/tape bug (ide_preempt was messed up)
* Version 5.22 fix ide_xlate_1024() to work with/without drive->id
* Version 5.23 miscellaneous touch-ups
* Version 5.24 fix #if's for SUPPORT_CMD640
* Version 5.25 more touch-ups, fix cdrom resets, ...
* cmd640.c now configs/compiles separate from ide.c
* Version 5.26 keep_settings now maintains the using_dma flag
* fix [EZD] remap message to only output at boot time
* fix "bad /dev/ entry" message to say hdc, not hdc0
* fix ide_xlate_1024() to respect user specified CHS
* use CHS from partn table if it looks translated
* re-merged flags chipset,vlb_32bit,vlb_sync into io_32bit
* keep track of interface chipset type, when known
* add generic PIO mode "tuneproc" mechanism
* fix cmd640_vlb option
* fix ht6560b support (was completely broken)
* umc8672.c now configures/compiles separate from ide.c
* move dtc2278 support to dtc2278.c
* move ht6560b support to ht6560b.c
* move qd6580 support to qd6580.c
* add ali14xx support in ali14xx.c
* Version 5.27 add [no]autotune parameters to help cmd640
* move rz1000 support to rz1000.c
* Version 5.28 #include "ide_modes.h"
* fix disallow_unmask: now per-interface "no_unmask" bit
* force io_32bit to be the same on drive pairs of dtc2278
* improved IDE tape error handling, and tape DMA support
* bugfix in ide_do_drive_cmd() for cdroms + serialize
* Version 5.29 fixed non-IDE check for too many physical heads
* don't use LBA if capacity is smaller than CHS
* Version 5.30 remove real_devices kludge, formerly used by genhd.c
* Version 5.32 change "KB" to "kB"
* fix serialize (was broken in kernel 1.3.72)
* add support for "hdparm -I"
* use common code for disk/tape/cdrom IDE_DRIVE_CMDs
* add support for Promise DC4030VL caching card
* improved serialize support
* put partition check back into alphabetical order
* add config option for PCMCIA baggage
* try to make PCMCIA support safer to use
* improve security on ioctls(): all are suser() only
* Version 5.33 improve handling of HDIO_DRIVE_CMDs that read data
* Version 5.34 fix irq-sharing problem from 5.33
* fix cdrom ioctl problem from 5.33
* Version 5.35 cosmetic changes
* fix cli() problem in try_to_identify()
* Version 5.36 fixes to optional PCMCIA support
* Version 5.37 don't use DMA when "noautotune" is specified
* Version 5.37a (go) fix shared irq probing (was broken in kernel 1.3.72)
* call unplug_device() from ide_do_drive_cmd()
* Version 5.38 add "hdx=none" option, courtesy of Joel Maslak
* mask drive irq after use, if sharing with another hwif
* add code to help debug weird cmd640 problems
* Version 5.39 fix horrible error in earlier irq sharing "fix"
* Version 5.40 fix serialization -- was broken in 5.39
* help sharing by masking device irq after probing
* Version 5.41 more fixes to irq sharing/serialize detection
* disable io_32bit by default on drive reset
* Version 5.42 simplify irq-masking after probe
* fix NULL pointer deref in save_match()
* Version 5.43 Ugh.. unexpected_intr is back: try to exterminate it
* Version 5.44 Fix for "irq probe failed" on cmd640
* change path on message regarding MAKEDEV.ide
* add a throttle to the unexpected_intr() messages
* Version 5.45 fix ugly parameter parsing bugs (thanks Derek)
* include Gadi's magic fix for cmd640 unexpected_intr
* include mc68000 patches from Geert Uytterhoeven
* add Gadi's fix for PCMCIA cdroms
* Version 5.46 remove the mc68000 #ifdefs for 2.0.x
* Version 5.47 fix set_tune race condition
* fix bug in earlier PCMCIA cdrom update
* Version 5.48 if def'd, invoke CMD640_DUMP_REGS when irq probe fails
* lengthen the do_reset1() pulse, for laptops
* add idebus=xx parameter for cmd640 and ali chipsets
* no_unmask flag now per-drive instead of per-hwif
* fix tune_req so that it gets done immediately
* fix missing restore_flags() in ide_ioctl
* prevent use of io_32bit on cmd640 with no prefetch
* Version 5.49 fix minor quirks in probing routines
* Version 5.50 allow values as small as 20 for idebus=
* Version 5.51 force non io_32bit in drive_cmd_intr()
* change delay_10ms() to delay_50ms() to fix problems
* Version 5.52 fix incorrect invalidation of removable devices
* add "hdx=slow" command line option
* Version 5.53 add ATAPI floppy drive support
* change default media for type 0 to floppy
* add support for Exabyte Nest
* add missing set_blocksize() in revalidate_disk()
* handle bad status bit sequencing in ide_wait_stat()
* support partition table translations with 255 heads
* probe all interfaces by default
* add probe for the i82371AB chipset
* acknowledge media change on removable drives
* add work-around for BMI drives
* remove "LBA" from boot messages
* Version 5.53.1 add UDMA "CRC retry" support
* Version 5.53.2 add Promise/33 auto-detection and DMA support
* fix MC_ERR handling
* fix mis-detection of NEC cdrom as floppy
* issue ATAPI reset and re-probe after "no response"
*
*--------------------------------------------------------------------------------
* Ported to ARM architecture by Russell King (rmk@ecs.soton.ac.uk)
*--------------------------------------------------------------------------------
* Some additional driver compile-time options are in ide.h
*
* To do, in likely order of completion:
* - modify kernel to obtain BIOS geometry for drives on 2nd/3rd/4th i/f
*/
 
#undef REALLY_SLOW_IO /* most systems can safely undef this */
 
#include <linux/config.h>
#include <linux/types.h>
#include <linux/string.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/timer.h>
#include <linux/mm.h>
#include <linux/ioport.h>
#include <linux/interrupt.h>
#include <linux/major.h>
#include <linux/blkdev.h>
#include <linux/errno.h>
#include <linux/hdreg.h>
#include <linux/genhd.h>
#include <linux/malloc.h>
 
#include <asm/byteorder.h>
#include <asm/irq.h>
#include <asm/segment.h>
#include <asm/io.h>
 
#ifdef CONFIG_PCI
#include <linux/bios32.h>
#include <linux/pci.h>
#endif /* CONFIG_PCI */
 
#include "ide.h"
#include "ide_modes.h"
 
#ifdef CONFIG_BLK_DEV_PROMISE
#include "promise.h"
#define IS_PROMISE_DRIVE (HWIF(drive)->chipset == ide_promise)
#else
#define IS_PROMISE_DRIVE (0) /* auto-NULLs out Promise code */
#endif /* CONFIG_BLK_DEV_PROMISE */
 
static const byte ide_hwif_to_major[MAX_HWIFS] = {IDE0_MAJOR, IDE1_MAJOR, IDE2_MAJOR, IDE3_MAJOR};
#if defined(CONFIG_ARCH_RPC)
static const unsigned long default_io_base[MAX_HWIFS] = {0x1f0};
static const byte default_irqs[MAX_HWIFS] = {9};
#elif defined (CONFIG_ARCH_A5K)
static const unsigned long default_io_base[MAX_HWIFS] = {0x1f0};
static const byte default_irqs[MAX_HWIFS] = {11};
#else
static const unsigned long default_io_base[MAX_HWIFS] = {0};
static const byte default_irqs[MAX_HWIFS] = {0};
#endif
 
static int idebus_parameter; /* holds the "idebus=" parameter */
static int system_bus_speed; /* holds what we think is VESA/PCI bus speed */
 
/*
* This is declared extern in ide.h, for access by other IDE modules:
*/
ide_hwif_t ide_hwifs[MAX_HWIFS]; /* master data repository */
 
#if (DISK_RECOVERY_TIME > 0)
#error DISK_RECOVERY_TIME is not supported on the ARM.
/*
* For really screwy hardware (hey, at least it *can* be used with Linux)
* we can enforce a minimum delay time between successive operations.
*/
static unsigned long read_timer(void)
{
unsigned long t, flags;
int i;
 
save_flags(flags);
cli();
t = jiffies * 11932;
outb_p(0, 0x43);
i = inb_p(0x40);
i |= inb(0x40) << 8;
restore_flags(flags);
return (t - i);
}
 
static void set_recovery_timer (ide_hwif_t *hwif)
{
hwif->last_time = read_timer();
}
#define SET_RECOVERY_TIMER(drive) set_recovery_timer (drive)
 
#else
 
#define SET_RECOVERY_TIMER(drive)
 
#endif /* DISK_RECOVERY_TIME */
 
 
/*
* Do not even *think* about calling this!
*/
static void init_hwif_data (unsigned int index)
{
byte *p;
unsigned int unit;
ide_hwif_t *hwif = &ide_hwifs[index];
 
/* bulk initialize hwif & drive info with zeros */
p = ((byte *) hwif) + sizeof(ide_hwif_t);
do {
*--p = 0;
} while (p > (byte *) hwif);
 
/* fill in any non-zero initial values */
hwif->index = index;
hwif->io.io_base = default_io_base[index];
hwif->io.ctl_port = hwif->io.io_base ? hwif->io.io_base+0x206 : 0x000;
hwif->io.io_shift = 0;
#ifdef CONFIG_BLK_DEV_HD
if (hwif->io.io_base == HD_DATA)
hwif->noprobe = 1; /* may be overridden by ide_setup() */
#endif /* CONFIG_BLK_DEV_HD */
/* RMK: do not probe ifs set at address 0 */
if (hwif->io.io_base == 0)
hwif->noprobe = 1;
hwif->major = ide_hwif_to_major[index];
hwif->name[0] = 'i';
hwif->name[1] = 'd';
hwif->name[2] = 'e';
hwif->name[3] = '0' + index;
#ifdef CONFIG_BLK_DEV_IDETAPE
hwif->tape_drive = NULL;
#endif /* CONFIG_BLK_DEV_IDETAPE */
for (unit = 0; unit < MAX_DRIVES; ++unit) {
ide_drive_t *drive = &hwif->drives[unit];
 
drive->select.all = (unit<<4)|0xa0;
drive->hwif = hwif;
drive->ctl = 0x08;
drive->ready_stat = READY_STAT;
drive->bad_wstat = BAD_W_STAT;
drive->special.b.recalibrate = 1;
drive->special.b.set_geometry = 1;
drive->name[0] = 'h';
drive->name[1] = 'd';
drive->name[2] = 'a' + (index * MAX_DRIVES) + unit;
}
}
 
/*
* init_ide_data() sets reasonable default values into all fields
* of all instances of the hwifs and drives, but only on the first call.
* Subsequent calls have no effect (they don't wipe out anything).
*
* This routine is normally called at driver initialization time,
* but may also be called MUCH earlier during kernel "command-line"
* parameter processing. As such, we cannot depend on any other parts
* of the kernel (such as memory allocation) to be functioning yet.
*
* This is too bad, as otherwise we could dynamically allocate the
* ide_drive_t structs as needed, rather than always consuming memory
* for the max possible number (MAX_HWIFS * MAX_DRIVES) of them.
*/
#define MAGIC_COOKIE 0x12345678
static void init_ide_data (void)
{
unsigned int index;
static unsigned long magic_cookie = MAGIC_COOKIE;
 
if (magic_cookie != MAGIC_COOKIE)
return; /* already initialized */
magic_cookie = 0;
 
for (index = 0; index < MAX_HWIFS; ++index)
init_hwif_data(index);
 
idebus_parameter = 0;
system_bus_speed = 0;
}
 
/*
* ide_system_bus_speed() returns what we think is the system VESA/PCI
* bus speed (in Mhz). This is used for calculating interface PIO timings.
* The default is 40 for known PCI systems, 50 otherwise.
* The "idebus=xx" parameter can be used to override this value.
* The actual value to be used is computed/displayed the first time through.
*/
int ide_system_bus_speed (void)
{
if (!system_bus_speed) {
if (idebus_parameter)
system_bus_speed = idebus_parameter; /* user supplied value */
#ifdef CONFIG_PCI
else if (pcibios_present())
system_bus_speed = 40; /* safe default value for PCI */
#endif /* CONFIG_PCI */
else
system_bus_speed = 50; /* safe default value for VESA and PCI */
printk("ide: Assuming %dMhz system bus speed for PIO modes; override with idebus=xx\n", system_bus_speed);
}
return system_bus_speed;
}
 
#if SUPPORT_VLB_SYNC
/*
* Some localbus EIDE interfaces require a special access sequence
* when using 32-bit I/O instructions to transfer data. We call this
* the "vlb_sync" sequence, which consists of three successive reads
* of the sector count register location, with interrupts disabled
* to ensure that the reads all happen together.
*/
static inline void do_vlb_sync (unsigned short port) {
(void) inb (port);
(void) inb (port);
(void) inb (port);
}
#endif /* SUPPORT_VLB_SYNC */
 
/*
* This is used for most PIO data transfers *from* the IDE interface
*/
void ide_input_data (ide_drive_t *drive, void *buffer, unsigned int wcount)
{
unsigned long io_base = HWIF(drive)->io.io_base;
unsigned long data_reg = io_base + (IDE_DATA_OFFSET << HWIF(drive)->io.io_shift);
#ifndef __arm__
byte io_32bit = drive->io_32bit;
#else
byte io_32bit = 0;
#endif
 
if (io_32bit) {
#if SUPPORT_VLB_SYNC
if (io_32bit & 2) {
cli();
do_vlb_sync(io_base+IDE_NSECTOR_OFFSET);
insl(data_reg, buffer, wcount);
if (drive->unmask)
sti();
} else
#endif /* SUPPORT_VLB_SYNC */
insl(data_reg, buffer, wcount);
} else {
#if SUPPORT_SLOW_DATA_PORTS
if (drive->slow) {
unsigned short *ptr = (unsigned short *) buffer;
while (wcount--) {
*ptr++ = inw_p(data_reg);
*ptr++ = inw_p(data_reg);
}
} else
#endif /* SUPPORT_SLOW_DATA_PORTS */
insw(data_reg, buffer, wcount<<1);
}
}
 
/*
* This is used for most PIO data transfers *to* the IDE interface
*/
void ide_output_data (ide_drive_t *drive, void *buffer, unsigned int wcount)
{
unsigned long io_base = HWIF(drive)->io.io_base;
unsigned long data_reg = io_base + (IDE_DATA_OFFSET << HWIF(drive)->io.io_shift);
#ifndef __arm__
byte io_32bit = drive->io_32bit;
#else
byte io_32bit = 0;
#endif
 
if (io_32bit) {
#if SUPPORT_VLB_SYNC
if (io_32bit & 2) {
cli();
do_vlb_sync(io_base+IDE_NSECTOR_OFFSET);
outsl(data_reg, buffer, wcount);
if (drive->unmask)
sti();
} else
#endif /* SUPPORT_VLB_SYNC */
outsl(data_reg, buffer, wcount);
} else {
#if SUPPORT_SLOW_DATA_PORTS
if (drive->slow) {
unsigned short *ptr = (unsigned short *) buffer;
while (wcount--) {
outw_p(*ptr++, data_reg);
outw_p(*ptr++, data_reg);
}
} else
#endif /* SUPPORT_SLOW_DATA_PORTS */
outsw(data_reg, buffer, wcount<<1);
}
}
 
/*
* The following routines are mainly used by the ATAPI drivers.
*
* These routines will round up any request for an odd number of bytes,
* so if an odd bytecount is specified, be sure that there's at least one
* extra byte allocated for the buffer.
*/
void atapi_input_bytes (ide_drive_t *drive, void *buffer, unsigned int bytecount)
{
++bytecount;
ide_input_data (drive, buffer, bytecount / 4);
if ((bytecount & 0x03) >= 2)
insw (IDE_DATA_REG, ((byte *)buffer) + (bytecount & ~0x03), 1);
}
 
void atapi_output_bytes (ide_drive_t *drive, void *buffer, unsigned int bytecount)
{
++bytecount;
ide_output_data (drive, buffer, bytecount / 4);
if ((bytecount & 0x03) >= 2)
outsw (IDE_DATA_REG, ((byte *)buffer) + (bytecount & ~0x03), 1);
}
 
/*
* This should get invoked any time we exit the driver to
* wait for an interrupt response from a drive. handler() points
* at the appropriate code to handle the next interrupt, and a
* timer is started to prevent us from waiting forever in case
* something goes wrong (see the timer_expiry() handler later on).
*/
void ide_set_handler (ide_drive_t *drive, ide_handler_t *handler, unsigned int timeout)
{
ide_hwgroup_t *hwgroup = HWGROUP(drive);
#ifdef DEBUG
if (hwgroup->handler != NULL) {
printk("%s: ide_set_handler: handler not null; old=%p, new=%p\n",
drive->name, hwgroup->handler, handler);
}
#endif
hwgroup->handler = handler;
hwgroup->timer.expires = jiffies + timeout;
add_timer(&(hwgroup->timer));
}
 
/*
* lba_capacity_is_ok() performs a sanity check on the claimed "lba_capacity"
* value for this drive (from its reported identification information).
*
* Returns: 1 if lba_capacity looks sensible
* 0 otherwise
*/
static int lba_capacity_is_ok (struct hd_driveid *id)
{
unsigned long lba_sects = id->lba_capacity;
unsigned long chs_sects = id->cyls * id->heads * id->sectors;
unsigned long _10_percent = chs_sects / 10;
 
/* very large drives (8GB+) may lie about the number of cylinders */
if (id->cyls == 16383 && id->heads == 16 && id->sectors == 63 && lba_sects > chs_sects) {
id->cyls = lba_sects / (16 * 63); /* correct cyls */
return 1; /* lba_capacity is our only option */
}
/* perform a rough sanity check on lba_sects: within 10% is "okay" */
if ((lba_sects - chs_sects) < _10_percent)
return 1; /* lba_capacity is good */
 
/* some drives have the word order reversed */
lba_sects = (lba_sects << 16) | (lba_sects >> 16);
if ((lba_sects - chs_sects) < _10_percent) {
id->lba_capacity = lba_sects; /* fix it */
return 1; /* lba_capacity is (now) good */
}
return 0; /* lba_capacity value is bad */
}
 
/*
* current_capacity() returns the capacity (in sectors) of a drive
* according to its current geometry/LBA settings.
*/
static unsigned long current_capacity (ide_drive_t *drive)
{
struct hd_driveid *id = drive->id;
unsigned long capacity = drive->cyl * drive->head * drive->sect;
 
if (!drive->present)
return 0;
#ifdef CONFIG_BLK_DEV_IDEFLOPPY
if (drive->media == ide_floppy)
return idefloppy_capacity(drive);
#endif /* CONFIG_BLK_DEV_IDEFLOPPY */
if (drive->media != ide_disk)
return 0x7fffffff; /* cdrom or tape */
drive->select.b.lba = 0;
/* Determine capacity, and use LBA if the drive properly supports it */
if (id != NULL && (id->capability & 2) && lba_capacity_is_ok(id)) {
if (id->lba_capacity >= capacity) {
drive->cyl = id->lba_capacity / (drive->head * drive->sect);
capacity = id->lba_capacity;
drive->select.b.lba = 1;
}
}
return (capacity - drive->sect0);
}
 
/*
* ide_geninit() is called exactly *once* for each major, from genhd.c,
* at the beginning of the initial partition check for the drives.
*/
static void ide_geninit (struct gendisk *gd)
{
unsigned int unit;
ide_hwif_t *hwif = gd->real_devices;
 
for (unit = 0; unit < gd->nr_real; ++unit) {
ide_drive_t *drive = &hwif->drives[unit];
#ifdef CONFIG_BLK_DEV_IDECD
if (drive->present && drive->media == ide_cdrom)
ide_cdrom_setup(drive);
#endif /* CONFIG_BLK_DEV_IDECD */
#ifdef CONFIG_BLK_DEV_IDETAPE
if (drive->present && drive->media == ide_tape)
idetape_setup(drive);
#endif /* CONFIG_BLK_DEV_IDETAPE */
#ifdef CONFIG_BLK_DEV_IDEFLOPPY
if (drive->present && drive->media == ide_floppy)
idefloppy_setup(drive);
#endif /* CONFIG_BLK_DEV_IDEFLOPPY */
drive->part[0].nr_sects = current_capacity(drive);
if (!drive->present || (drive->media != ide_disk && drive->media != ide_floppy) ||
!drive->part[0].nr_sects) {
drive->part[0].start_sect = -1; /* skip partition check */
}
}
}
 
/*
* init_gendisk() (as opposed to ide_geninit) is called for each major device,
* after probing for drives, to allocate partition tables and other data
* structures needed for the routines in genhd.c. ide_geninit() gets called
* somewhat later, during the partition check.
*/
static void init_gendisk (ide_hwif_t *hwif)
{
struct gendisk *gd, **gdp;
unsigned int unit, units, minors;
int *bs;
 
/* figure out maximum drive number on the interface */
for (units = MAX_DRIVES; units > 0; --units) {
if (hwif->drives[units-1].present)
break;
}
minors = units * (1<<PARTN_BITS);
gd = kmalloc (sizeof(struct gendisk), GFP_KERNEL);
gd->sizes = kmalloc (minors * sizeof(int), GFP_KERNEL);
gd->part = kmalloc (minors * sizeof(struct hd_struct), GFP_KERNEL);
bs = kmalloc (minors*sizeof(int), GFP_KERNEL);
 
memset(gd->part, 0, minors * sizeof(struct hd_struct));
 
/* cdroms and msdos f/s are examples of non-1024 blocksizes */
blksize_size[hwif->major] = bs;
for (unit = 0; unit < minors; ++unit)
*bs++ = BLOCK_SIZE;
 
for (unit = 0; unit < units; ++unit)
hwif->drives[unit].part = &gd->part[unit << PARTN_BITS];
 
gd->major = hwif->major; /* our major device number */
gd->major_name = IDE_MAJOR_NAME; /* treated special in genhd.c */
gd->minor_shift = PARTN_BITS; /* num bits for partitions */
gd->max_p = 1<<PARTN_BITS; /* 1 + max partitions / drive */
gd->max_nr = units; /* max num real drives */
gd->nr_real = units; /* current num real drives */
gd->init = ide_geninit; /* initialization function */
gd->real_devices= hwif; /* ptr to internal data */
gd->next = NULL; /* linked list of major devs */
 
for (gdp = &gendisk_head; *gdp; gdp = &((*gdp)->next)) ;
hwif->gd = *gdp = gd; /* link onto tail of list */
}
 
static void do_reset1 (ide_drive_t *, int); /* needed below */
 
#ifdef CONFIG_BLK_DEV_IDEATAPI
/*
* atapi_reset_pollfunc() gets invoked to poll the interface for completion every 50ms
* during an atapi drive reset operation. If the drive has not yet responded,
* and we have not yet hit our maximum waiting time, then the timer is restarted
* for another 50ms.
*/
static void atapi_reset_pollfunc (ide_drive_t *drive)
{
ide_hwgroup_t *hwgroup = HWGROUP(drive);
byte stat;
 
OUT_BYTE (drive->select.all, IDE_SELECT_REG);
udelay (10);
 
if (OK_STAT(stat=GET_STAT(), 0, BUSY_STAT)) {
printk("%s: ATAPI reset complete\n", drive->name);
} else {
if (jiffies < hwgroup->poll_timeout) {
ide_set_handler (drive, &atapi_reset_pollfunc, HZ/20);
return; /* continue polling */
}
hwgroup->poll_timeout = 0; /* end of polling */
printk("%s: ATAPI reset timed-out, status=0x%02x\n", drive->name, stat);
do_reset1 (drive, 1); /* do it the old fashioned way */
return;
}
hwgroup->poll_timeout = 0; /* done polling */
}
#endif /* CONFIG_BLK_DEV_IDEATAPI */
 
/*
* reset_pollfunc() gets invoked to poll the interface for completion every 50ms
* during an ide reset operation. If the drives have not yet responded,
* and we have not yet hit our maximum waiting time, then the timer is restarted
* for another 50ms.
*/
static void reset_pollfunc (ide_drive_t *drive)
{
ide_hwgroup_t *hwgroup = HWGROUP(drive);
ide_hwif_t *hwif = HWIF(drive);
byte tmp;
 
if (!OK_STAT(tmp=GET_STAT(), 0, BUSY_STAT)) {
if (jiffies < hwgroup->poll_timeout) {
ide_set_handler (drive, &reset_pollfunc, HZ/20);
return; /* continue polling */
}
printk("%s: reset timed-out, status=0x%02x\n", hwif->name, tmp);
} else {
printk("%s: reset: ", hwif->name);
if ((tmp = GET_ERR()) == 1)
printk("success\n");
else {
#if FANCY_STATUS_DUMPS
printk("master: ");
switch (tmp & 0x7f) {
case 1: printk("passed");
break;
case 2: printk("formatter device error");
break;
case 3: printk("sector buffer error");
break;
case 4: printk("ECC circuitry error");
break;
case 5: printk("controlling MPU error");
break;
default:printk("error (0x%02x?)", tmp);
}
if (tmp & 0x80)
printk("; slave: failed");
printk("\n");
#else
printk("failed\n");
#endif /* FANCY_STATUS_DUMPS */
}
}
hwgroup->poll_timeout = 0; /* done polling */
}
 
/*
* do_reset1() attempts to recover a confused drive by resetting it.
* Unfortunately, resetting a disk drive actually resets all devices on
* the same interface, so it can really be thought of as resetting the
* interface rather than resetting the drive.
*
* ATAPI devices have their own reset mechanism which allows them to be
* individually reset without clobbering other devices on the same interface.
*
* Unfortunately, the IDE interface does not generate an interrupt to let
* us know when the reset operation has finished, so we must poll for this.
* Equally poor, though, is the fact that this may a very long time to complete,
* (up to 30 seconds worstcase). So, instead of busy-waiting here for it,
* we set a timer to poll at 50ms intervals.
*/
static void do_reset1 (ide_drive_t *drive, int do_not_try_atapi)
{
unsigned int unit;
unsigned long flags;
ide_hwif_t *hwif = HWIF(drive);
ide_hwgroup_t *hwgroup = HWGROUP(drive);
 
save_flags(flags);
cli(); /* Why ? */
 
#ifdef CONFIG_BLK_DEV_IDEATAPI
/* For an ATAPI device, first try an ATAPI SRST. */
if (drive->media != ide_disk) {
if (!do_not_try_atapi) {
if (!drive->keep_settings) {
drive->unmask = 0;
drive->io_32bit = 0;
}
OUT_BYTE (drive->select.all, IDE_SELECT_REG);
udelay (20);
OUT_BYTE (WIN_SRST, IDE_COMMAND_REG);
hwgroup->poll_timeout = jiffies + WAIT_WORSTCASE;
ide_set_handler (drive, &atapi_reset_pollfunc, HZ/20);
restore_flags (flags);
return;
}
}
#endif /* CONFIG_BLK_DEV_IDEATAPI */
 
/*
* First, reset any device state data we were maintaining
* for any of the drives on this interface.
*/
for (unit = 0; unit < MAX_DRIVES; ++unit) {
ide_drive_t *rdrive = &hwif->drives[unit];
#ifdef CONFIG_BLK_DEV_IDETAPE
if (rdrive->media == ide_tape)
rdrive->tape.reset_issued = 1;
#endif /* CONFIG_BLK_DEV_IDETAPE */
rdrive->special.all = 0;
rdrive->special.b.set_geometry = 1;
rdrive->special.b.recalibrate = 1;
if (OK_TO_RESET_CONTROLLER)
rdrive->mult_count = 0;
if (!rdrive->keep_settings) {
rdrive->mult_req = 0;
rdrive->unmask = 0;
rdrive->io_32bit = 0;
if (rdrive->using_dma) {
rdrive->using_dma = 0;
printk("%s: disabled DMA\n", rdrive->name);
}
}
if (rdrive->mult_req != rdrive->mult_count)
rdrive->special.b.set_multmode = 1;
}
 
#if OK_TO_RESET_CONTROLLER
/*
* Note that we also set nIEN while resetting the device,
* to mask unwanted interrupts from the interface during the reset.
* However, due to the design of PC hardware, this will cause an
* immediate interrupt due to the edge transition it produces.
* This single interrupt gives us a "fast poll" for drives that
* recover from reset very quickly, saving us the first 50ms wait time.
*/
OUT_BYTE(drive->ctl|6,IDE_CONTROL_REG); /* set SRST and nIEN */
udelay(10); /* more than enough time */
OUT_BYTE(drive->ctl|2,IDE_CONTROL_REG); /* clear SRST, leave nIEN */
udelay(10); /* more than enough time */
hwgroup->poll_timeout = jiffies + WAIT_WORSTCASE;
ide_set_handler (drive, &reset_pollfunc, HZ/20);
#endif /* OK_TO_RESET_CONTROLLER */
 
restore_flags (flags);
}
 
/*
* ide_do_reset() is the entry point to the drive/interface reset code.
*/
void ide_do_reset (ide_drive_t *drive)
{
do_reset1 (drive, 0);
#ifdef CONFIG_BLK_DEV_IDETAPE
if (drive->media == ide_tape)
drive->tape.reset_issued=1;
#endif /* CONFIG_BLK_DEV_IDETAPE */
}
 
/*
* Clean up after success/failure of an explicit drive cmd
*/
void ide_end_drive_cmd (ide_drive_t *drive, byte stat, byte err)
{
unsigned long flags;
struct request *rq = HWGROUP(drive)->rq;
 
if (rq->cmd == IDE_DRIVE_CMD) {
byte *args = (byte *) rq->buffer;
rq->errors = !OK_STAT(stat,READY_STAT,BAD_STAT);
if (args) {
args[0] = stat;
args[1] = err;
args[2] = IN_BYTE(IDE_NSECTOR_REG);
}
}
save_flags(flags);
cli();
blk_dev[MAJOR(rq->rq_dev)].current_request = rq->next;
HWGROUP(drive)->rq = NULL;
rq->rq_status = RQ_INACTIVE;
if (rq->sem != NULL)
up(rq->sem);
restore_flags(flags);
}
 
/*
* Error reporting, in human readable form (luxurious, but a memory hog).
*/
byte ide_dump_status (ide_drive_t *drive, const char *msg, byte stat)
{
unsigned long flags;
byte err = 0;
 
save_flags (flags);
sti();
printk("%s: %s: status=0x%02x", drive->name, msg, stat);
#if FANCY_STATUS_DUMPS
if (drive->media == ide_disk) {
printk(" { ");
if (stat & BUSY_STAT)
printk("Busy ");
else {
if (stat & READY_STAT) printk("DriveReady ");
if (stat & WRERR_STAT) printk("DeviceFault ");
if (stat & SEEK_STAT) printk("SeekComplete ");
if (stat & DRQ_STAT) printk("DataRequest ");
if (stat & ECC_STAT) printk("CorrectedError ");
if (stat & INDEX_STAT) printk("Index ");
if (stat & ERR_STAT) printk("Error ");
}
printk("}");
}
#endif /* FANCY_STATUS_DUMPS */
printk("\n");
if ((stat & (BUSY_STAT|ERR_STAT)) == ERR_STAT) {
err = GET_ERR();
printk("%s: %s: error=0x%02x", drive->name, msg, err);
#if FANCY_STATUS_DUMPS
if (drive->media == ide_disk) {
printk(" { ");
if (err & BBD_ERR) printk("BadSector ");
if (err & ECC_ERR) printk("UncorrectableError ");
if (err & ID_ERR) printk("SectorIdNotFound ");
if (err & ABRT_ERR) printk("DriveStatusError ");
if (err & TRK0_ERR) printk("TrackZeroNotFound ");
if (err & MARK_ERR) printk("AddrMarkNotFound ");
printk("}");
if (err & (BBD_ERR|ECC_ERR|ID_ERR|MARK_ERR)) {
byte cur = IN_BYTE(IDE_SELECT_REG);
if (cur & 0x40) { /* using LBA? */
printk(", LBAsect=%ld", (unsigned long)
((cur&0xf)<<24)
|(IN_BYTE(IDE_HCYL_REG)<<16)
|(IN_BYTE(IDE_LCYL_REG)<<8)
| IN_BYTE(IDE_SECTOR_REG));
} else {
printk(", CHS=%d/%d/%d",
(IN_BYTE(IDE_HCYL_REG)<<8) +
IN_BYTE(IDE_LCYL_REG),
cur & 0xf,
IN_BYTE(IDE_SECTOR_REG));
}
if (HWGROUP(drive)->rq)
printk(", sector=%ld", HWGROUP(drive)->rq->sector);
}
}
#endif /* FANCY_STATUS_DUMPS */
printk("\n");
}
restore_flags (flags);
return err;
}
 
/*
* try_to_flush_leftover_data() is invoked in response to a drive
* unexpectedly having its DRQ_STAT bit set. As an alternative to
* resetting the drive, this routine tries to clear the condition
* by read a sector's worth of data from the drive. Of course,
* this may not help if the drive is *waiting* for data from *us*.
*/
static void try_to_flush_leftover_data (ide_drive_t *drive)
{
int i = (drive->mult_count ? drive->mult_count : 1) * SECTOR_WORDS;
 
while (i > 0) {
unsigned long buffer[16];
unsigned int wcount = (i > 16) ? 16 : i;
i -= wcount;
ide_input_data (drive, buffer, wcount);
}
}
 
/*
* ide_error() takes action based on the error returned by the controller.
*/
void ide_error (ide_drive_t *drive, const char *msg, byte stat)
{
struct request *rq;
byte err;
 
err = ide_dump_status(drive, msg, stat);
if ((rq = HWGROUP(drive)->rq) == NULL || drive == NULL)
return;
/* retry only "normal" I/O: */
if (rq->cmd == IDE_DRIVE_CMD) {
rq->errors = 1;
ide_end_drive_cmd(drive, stat, err);
return;
}
if (stat & BUSY_STAT) { /* other bits are useless when BUSY */
rq->errors |= ERROR_RESET;
} else {
if (drive->media == ide_disk && (stat & ERR_STAT)) {
/* err has different meaning on cdrom and tape */
if (err == ABRT_ERR) {
if (drive->select.b.lba && IN_BYTE(IDE_COMMAND_REG) == WIN_SPECIFY)
return; /* some newer drives don't support WIN_SPECIFY */
} else if ((err & (ABRT_ERR | ICRC_ERR)) == (ABRT_ERR | ICRC_ERR))
; /* UDMA crc error -- just retry the operation */
else if (err & (BBD_ERR | ECC_ERR)) /* retries won't help these */
rq->errors = ERROR_MAX;
else if (err & TRK0_ERR) /* help it find track zero */
rq->errors |= ERROR_RECAL;
else if (err & MC_ERR)
drive->special.b.mc = 1;
}
if ((stat & DRQ_STAT) && rq->cmd != WRITE)
try_to_flush_leftover_data(drive);
}
if (GET_STAT() & (BUSY_STAT|DRQ_STAT))
rq->errors |= ERROR_RESET; /* Mmmm.. timing problem */
 
if (rq->errors >= ERROR_MAX) {
#ifdef CONFIG_BLK_DEV_IDETAPE
if (drive->media == ide_tape) {
rq->errors = 0;
idetape_end_request(0, HWGROUP(drive));
} else
#endif /* CONFIG_BLK_DEV_IDETAPE */
#ifdef CONFIG_BLK_DEV_IDEFLOPPY
if (drive->media == ide_floppy) {
rq->errors = 0;
idefloppy_end_request(0, HWGROUP(drive));
} else
#endif /* CONFIG_BLK_DEV_IDEFLOPPY */
#ifdef CONFIG_BLK_DEV_IDESCSI
if (drive->media == ide_scsi) {
rq->errors = 0;
idescsi_end_request(0, HWGROUP(drive));
} else
#endif /* CONFIG_BLK_DEV_IDESCSI */
ide_end_request(0, HWGROUP(drive));
}
else {
if ((rq->errors & ERROR_RESET) == ERROR_RESET) {
++rq->errors;
ide_do_reset(drive);
return;
} else if ((rq->errors & ERROR_RECAL) == ERROR_RECAL)
drive->special.b.recalibrate = 1;
++rq->errors;
}
}
 
/*
* read_intr() is the handler for disk read/multread interrupts
*/
static void read_intr (ide_drive_t *drive)
{
byte stat;
int i;
unsigned int msect, nsect;
struct request *rq;
 
if (!OK_STAT(stat=GET_STAT(),DATA_READY,BAD_R_STAT)) {
ide_error(drive, "read_intr", stat);
return;
}
msect = drive->mult_count;
read_next:
rq = HWGROUP(drive)->rq;
if (msect) {
if ((nsect = rq->current_nr_sectors) > msect)
nsect = msect;
msect -= nsect;
} else
nsect = 1;
ide_input_data(drive, rq->buffer, nsect * SECTOR_WORDS);
#ifdef DEBUG
printk("%s: read: sectors(%ld-%ld), buffer=0x%08lx, remaining=%ld\n",
drive->name, rq->sector, rq->sector+nsect-1,
(unsigned long) rq->buffer+(nsect<<9), rq->nr_sectors-nsect);
#endif
rq->sector += nsect;
rq->buffer += nsect<<9;
rq->errors = 0;
i = (rq->nr_sectors -= nsect);
if ((rq->current_nr_sectors -= nsect) <= 0)
ide_end_request(1, HWGROUP(drive));
if (i > 0) {
if (msect)
goto read_next;
ide_set_handler (drive, &read_intr, WAIT_CMD);
}
}
 
/*
* write_intr() is the handler for disk write interrupts
*/
static void write_intr (ide_drive_t *drive)
{
byte stat;
int i;
ide_hwgroup_t *hwgroup = HWGROUP(drive);
struct request *rq = hwgroup->rq;
 
if (OK_STAT(stat=GET_STAT(),DRIVE_READY,drive->bad_wstat)) {
#ifdef DEBUG
printk("%s: write: sector %ld, buffer=0x%08lx, remaining=%ld\n",
drive->name, rq->sector, (unsigned long) rq->buffer,
rq->nr_sectors-1);
#endif
if ((rq->nr_sectors == 1) ^ ((stat & DRQ_STAT) != 0)) {
rq->sector++;
rq->buffer += 512;
rq->errors = 0;
i = --rq->nr_sectors;
--rq->current_nr_sectors;
if (rq->current_nr_sectors <= 0)
ide_end_request(1, hwgroup);
if (i > 0) {
ide_output_data (drive, rq->buffer, SECTOR_WORDS);
ide_set_handler (drive, &write_intr, WAIT_CMD);
}
return;
}
}
ide_error(drive, "write_intr", stat);
}
 
/*
* ide_multwrite() transfers a block of up to mcount sectors of data
* to a drive as part of a disk multiple-sector write operation.
*/
void ide_multwrite (ide_drive_t *drive, unsigned int mcount)
{
struct request *rq = &HWGROUP(drive)->wrq;
 
do {
unsigned int nsect = rq->current_nr_sectors;
if (nsect > mcount)
nsect = mcount;
mcount -= nsect;
 
ide_output_data(drive, rq->buffer, nsect<<7);
#ifdef DEBUG
printk("%s: multwrite: sector %ld, buffer=0x%08lx, count=%d, remaining=%ld\n",
drive->name, rq->sector, (unsigned long) rq->buffer,
nsect, rq->nr_sectors - nsect);
#endif
if ((rq->nr_sectors -= nsect) <= 0)
break;
if ((rq->current_nr_sectors -= nsect) == 0) {
if ((rq->bh = rq->bh->b_reqnext) != NULL) {
rq->current_nr_sectors = rq->bh->b_size>>9;
rq->buffer = rq->bh->b_data;
} else {
panic("%s: buffer list corrupted\n", drive->name);
break;
}
} else {
rq->buffer += nsect << 9;
}
} while (mcount);
}
 
/*
* multwrite_intr() is the handler for disk multwrite interrupts
*/
static void multwrite_intr (ide_drive_t *drive)
{
byte stat;
int i;
ide_hwgroup_t *hwgroup = HWGROUP(drive);
struct request *rq = &hwgroup->wrq;
 
if (OK_STAT(stat=GET_STAT(),DRIVE_READY,drive->bad_wstat)) {
if (stat & DRQ_STAT) {
if (rq->nr_sectors) {
ide_multwrite(drive, drive->mult_count);
ide_set_handler (drive, &multwrite_intr, WAIT_CMD);
return;
}
} else {
if (!rq->nr_sectors) { /* all done? */
rq = hwgroup->rq;
for (i = rq->nr_sectors; i > 0;){
i -= rq->current_nr_sectors;
ide_end_request(1, hwgroup);
}
return;
}
}
}
ide_error(drive, "multwrite_intr", stat);
}
 
/*
* Issue a simple drive command
* The drive must be selected beforehand.
*/
static void ide_cmd(ide_drive_t *drive, byte cmd, byte nsect, ide_handler_t *handler)
{
ide_set_handler (drive, handler, WAIT_CMD);
OUT_BYTE(drive->ctl,IDE_CONTROL_REG);
OUT_BYTE(nsect,IDE_NSECTOR_REG);
OUT_BYTE(cmd,IDE_COMMAND_REG);
}
 
/*
* set_multmode_intr() is invoked on completion of a WIN_SETMULT cmd.
*/
static void set_multmode_intr (ide_drive_t *drive)
{
byte stat = GET_STAT();
 
sti();
if (OK_STAT(stat,READY_STAT,BAD_STAT)) {
drive->mult_count = drive->mult_req;
} else {
drive->mult_req = drive->mult_count = 0;
drive->special.b.recalibrate = 1;
(void) ide_dump_status(drive, "set_multmode", stat);
}
}
 
/*
* set_geometry_intr() is invoked on completion of a WIN_SPECIFY cmd.
*/
static void set_geometry_intr (ide_drive_t *drive)
{
byte stat = GET_STAT();
 
sti();
if (!OK_STAT(stat,READY_STAT,BAD_STAT))
ide_error(drive, "set_geometry_intr", stat);
}
 
/*
* recal_intr() is invoked on completion of a WIN_RESTORE (recalibrate) cmd.
*/
static void recal_intr (ide_drive_t *drive)
{
byte stat = GET_STAT();
 
sti();
if (!OK_STAT(stat,READY_STAT,BAD_STAT))
ide_error(drive, "recal_intr", stat);
}
 
/*
* mc_intr() is invoked on completion of a WIN_ACKMC cmd.
*/
static void mc_intr (ide_drive_t *drive)
{
byte stat = GET_STAT();
 
sti();
if (!OK_STAT(stat,READY_STAT,BAD_STAT))
ide_error(drive, "mc_intr", stat);
drive->special.b.mc = 0;
}
 
/*
* drive_cmd_intr() is invoked on completion of a special DRIVE_CMD.
*/
static void drive_cmd_intr (ide_drive_t *drive)
{
struct request *rq = HWGROUP(drive)->rq;
byte *args = (byte *) rq->buffer;
byte stat = GET_STAT();
 
sti();
if ((stat & DRQ_STAT) && args && args[3]) {
byte io_32bit = drive->io_32bit;
drive->io_32bit = 0;
ide_input_data(drive, &args[4], args[3] * SECTOR_WORDS);
drive->io_32bit = io_32bit;
stat = GET_STAT();
}
if (OK_STAT(stat,READY_STAT,BAD_STAT))
ide_end_drive_cmd (drive, stat, GET_ERR());
else
ide_error(drive, "drive_cmd", stat); /* calls ide_end_drive_cmd */
}
 
/*
* do_special() is used to issue WIN_SPECIFY, WIN_RESTORE, and WIN_SETMULT
* commands to a drive. It used to do much more, but has been scaled back.
*/
static inline void do_special (ide_drive_t *drive)
{
special_t *s = &drive->special;
 
#ifdef DEBUG
printk("%s: do_special: 0x%02x\n", drive->name, s->all);
#endif
if (s->b.set_geometry) {
s->b.set_geometry = 0;
if (drive->media == ide_disk && !drive->no_geom) {
OUT_BYTE(drive->sect,IDE_SECTOR_REG);
OUT_BYTE(drive->cyl,IDE_LCYL_REG);
OUT_BYTE(drive->cyl>>8,IDE_HCYL_REG);
OUT_BYTE(((drive->head-1)|drive->select.all)&0xBF,IDE_SELECT_REG);
if (!IS_PROMISE_DRIVE)
ide_cmd(drive, WIN_SPECIFY, drive->sect, &set_geometry_intr);
}
} else if (s->b.recalibrate) {
s->b.recalibrate = 0;
if (drive->media == ide_disk && !IS_PROMISE_DRIVE)
ide_cmd(drive, WIN_RESTORE, drive->sect, &recal_intr);
} else if (s->b.set_tune) {
ide_tuneproc_t *tuneproc = HWIF(drive)->tuneproc;
s->b.set_tune = 0;
if (tuneproc != NULL)
tuneproc(drive, drive->tune_req);
} else if (s->b.set_multmode) {
s->b.set_multmode = 0;
if (drive->media == ide_disk) {
if (drive->id && drive->mult_req > drive->id->max_multsect)
drive->mult_req = drive->id->max_multsect;
if (!IS_PROMISE_DRIVE)
ide_cmd(drive, WIN_SETMULT, drive->mult_req, &set_multmode_intr);
} else
drive->mult_req = 0;
} else if (s->b.mc) {
s->b.mc = 0;
if (drive->media == ide_disk && !IS_PROMISE_DRIVE)
ide_cmd(drive, WIN_ACKMC, drive->sect, &mc_intr);
} else if (s->all) {
int special = s->all;
s->all = 0;
printk("%s: bad special flag: 0x%02x\n", drive->name, special);
}
}
 
/*
* This routine busy-waits for the drive status to be not "busy".
* It then checks the status for all of the "good" bits and none
* of the "bad" bits, and if all is okay it returns 0. All other
* cases return 1 after invoking ide_error() -- caller should just return.
*
* This routine should get fixed to not hog the cpu during extra long waits..
* That could be done by busy-waiting for the first jiffy or two, and then
* setting a timer to wake up at half second intervals thereafter,
* until timeout is achieved, before timing out.
*/
int ide_wait_stat (ide_drive_t *drive, byte good, byte bad, unsigned long timeout)
{
byte stat;
unsigned long flags;
 
udelay(1); /* spec allows drive 400ns to assert "BUSY" */
if ((stat = GET_STAT()) & BUSY_STAT) {
save_flags(flags);
sti();
timeout += jiffies;
while ((stat = GET_STAT()) & BUSY_STAT) {
if (jiffies > timeout) {
restore_flags(flags);
ide_error(drive, "status timeout", stat);
return 1;
}
}
restore_flags(flags);
}
udelay(1); /* allow status to settle, then read it again */
if (OK_STAT((stat = GET_STAT()), good, bad))
return 0;
ide_error(drive, "status error", stat);
return 1;
}
 
/*
* do_rw_disk() issues READ and WRITE commands to a disk,
* using LBA if supported, or CHS otherwise, to address sectors.
* It also takes care of issuing special DRIVE_CMDs.
*/
static inline void do_rw_disk (ide_drive_t *drive, struct request *rq, unsigned long block)
{
ide_hwif_t *hwif = HWIF(drive);
unsigned long io_base = hwif->io.io_base;
unsigned long io_shift = hwif->io.io_shift;
#ifdef CONFIG_BLK_DEV_PROMISE
int use_promise_io = 0;
#endif /* CONFIG_BLK_DEV_PROMISE */
 
OUT_BYTE(drive->ctl,IDE_CONTROL_REG);
OUT_BYTE(rq->nr_sectors,io_base + (IDE_NSECTOR_OFFSET << io_shift));
#ifdef CONFIG_BLK_DEV_PROMISE
if (IS_PROMISE_DRIVE) {
if (hwif->is_promise2 || rq->cmd == READ) {
use_promise_io = 1;
}
}
if (drive->select.b.lba || use_promise_io) {
#else /* !CONFIG_BLK_DEV_PROMISE */
if (drive->select.b.lba) {
#endif /* CONFIG_BLK_DEV_PROMISE */
#ifdef DEBUG
printk("%s: %sing: LBAsect=%ld, sectors=%ld, buffer=0x%08lx\n",
drive->name, (rq->cmd==READ)?"read":"writ",
block, rq->nr_sectors, (unsigned long) rq->buffer);
#endif
OUT_BYTE(block,io_base + (IDE_SECTOR_OFFSET << io_shift));
OUT_BYTE(block>>=8,io_base + (IDE_LCYL_OFFSET << io_shift));
OUT_BYTE(block>>=8,io_base + (IDE_HCYL_OFFSET << io_shift));
OUT_BYTE(((block>>8)&0x0f)|drive->select.all,io_base + (IDE_SELECT_OFFSET << io_shift));
} else {
unsigned int sect,head,cyl,track;
track = block / drive->sect;
sect = block % drive->sect + 1;
OUT_BYTE(sect,io_base + (IDE_SECTOR_OFFSET << io_shift));
head = track % drive->head;
cyl = track / drive->head;
OUT_BYTE(cyl,io_base + (IDE_LCYL_OFFSET << io_shift));
OUT_BYTE(cyl>>8,io_base + (IDE_HCYL_OFFSET << io_shift));
OUT_BYTE(head|drive->select.all,io_base + (IDE_SELECT_OFFSET << io_shift));
#ifdef DEBUG
printk("%s: %sing: CHS=%d/%d/%d, sectors=%ld, buffer=0x%08lx\n",
drive->name, (rq->cmd==READ)?"read":"writ", cyl,
head, sect, rq->nr_sectors, (unsigned long) rq->buffer);
#endif
}
#ifdef CONFIG_BLK_DEV_PROMISE
if (use_promise_io) {
do_promise_io (drive, rq);
return;
}
#endif /* CONFIG_BLK_DEV_PROMISE */
if (rq->cmd == READ) {
#ifdef CONFIG_BLK_DEV_TRITON
if (drive->using_dma && !(HWIF(drive)->dmaproc(ide_dma_read, drive)))
return;
#endif /* CONFIG_BLK_DEV_TRITON */
ide_set_handler(drive, &read_intr, WAIT_CMD);
OUT_BYTE(drive->mult_count ? WIN_MULTREAD : WIN_READ, io_base + (IDE_COMMAND_OFFSET << io_shift));
return;
}
if (rq->cmd == WRITE) {
#ifdef CONFIG_BLK_DEV_TRITON
if (drive->using_dma && !(HWIF(drive)->dmaproc(ide_dma_write, drive)))
return;
#endif /* CONFIG_BLK_DEV_TRITON */
OUT_BYTE(drive->mult_count ? WIN_MULTWRITE : WIN_WRITE, io_base + (IDE_COMMAND_OFFSET << io_shift));
if (ide_wait_stat(drive, DATA_READY, drive->bad_wstat, WAIT_DRQ)) {
printk("%s: no DRQ after issuing %s\n", drive->name,
drive->mult_count ? "MULTWRITE" : "WRITE");
return;
}
if (!drive->unmask)
cli();
if (drive->mult_count) {
HWGROUP(drive)->wrq = *rq; /* scratchpad */
ide_set_handler (drive, &multwrite_intr, WAIT_CMD);
ide_multwrite(drive, drive->mult_count);
} else {
ide_set_handler (drive, &write_intr, WAIT_CMD);
ide_output_data(drive, rq->buffer, SECTOR_WORDS);
}
return;
}
printk("%s: bad command: %d\n", drive->name, rq->cmd);
ide_end_request(0, HWGROUP(drive));
}
 
/*
* execute_drive_cmd() issues a special drive command,
* usually initiated by ioctl() from the external hdparm program.
*/
static void execute_drive_cmd (ide_drive_t *drive, struct request *rq)
{
ide_hwif_t *hwif = HWIF(drive);
unsigned long io_base = hwif->io.io_base;
unsigned long io_shift = hwif->io.io_shift;
byte *args = rq->buffer;
if (args) {
#ifdef DEBUG
printk("%s: DRIVE_CMD cmd=0x%02x sc=0x%02x fr=0x%02x xx=0x%02x\n",
drive->name, args[0], args[1], args[2], args[3]);
#endif
OUT_BYTE(args[2],io_base + (IDE_FEATURE_OFFSET << io_shift));
ide_cmd(drive, args[0], args[1], &drive_cmd_intr);
return;
} else {
/*
* NULL is actually a valid way of waiting for
* all current requests to be flushed from the queue.
*/
#ifdef DEBUG
printk("%s: DRIVE_CMD (null)\n", drive->name);
#endif
ide_end_drive_cmd(drive, GET_STAT(), GET_ERR());
return;
}
}
 
/*
* do_request() initiates handling of a new I/O request
*/
static inline void do_request (ide_hwif_t *hwif, struct request *rq)
{
unsigned int minor, unit;
unsigned long block, blockend;
ide_drive_t *drive;
 
sti();
#ifdef DEBUG
printk("%s: do_request: current=0x%08lx\n", hwif->name, (unsigned long) rq);
#endif
minor = MINOR(rq->rq_dev);
unit = minor >> PARTN_BITS;
if (MAJOR(rq->rq_dev) != hwif->major || unit >= MAX_DRIVES) {
printk("%s: bad device number: %s\n",
hwif->name, kdevname(rq->rq_dev));
goto kill_rq;
}
drive = &hwif->drives[unit];
#ifdef DEBUG
if (rq->bh && !buffer_locked(rq->bh)) {
printk("%s: block not locked\n", drive->name);
goto kill_rq;
}
#endif
block = rq->sector;
blockend = block + rq->nr_sectors;
if ((blockend < block) || (blockend > drive->part[minor&PARTN_MASK].nr_sects)) {
printk("%s%c: bad access: block=%ld, count=%ld\n", drive->name,
(minor&PARTN_MASK)?'0'+(minor&PARTN_MASK):' ', block, rq->nr_sectors);
goto kill_rq;
}
block += drive->part[minor&PARTN_MASK].start_sect + drive->sect0;
#if FAKE_FDISK_FOR_EZDRIVE
if (block == 0 && drive->remap_0_to_1)
block = 1; /* redirect MBR access to EZ-Drive partn table */
#endif /* FAKE_FDISK_FOR_EZDRIVE */
((ide_hwgroup_t *)hwif->hwgroup)->drive = drive;
#if (DISK_RECOVERY_TIME > 0)
while ((read_timer() - hwif->last_time) < DISK_RECOVERY_TIME);
#endif
 
#ifdef CONFIG_BLK_DEV_IDETAPE
POLL_HWIF_TAPE_DRIVE; /* macro from ide-tape.h */
#endif /* CONFIG_BLK_DEV_IDETAPE */
 
SELECT_DRIVE(hwif,drive);
if (ide_wait_stat(drive, drive->ready_stat, BUSY_STAT|DRQ_STAT, WAIT_READY)) {
printk("%s: drive not ready for command\n", drive->name);
return;
}
if (!drive->special.all) {
if (rq->cmd == IDE_DRIVE_CMD) {
execute_drive_cmd(drive, rq);
return;
}
#ifdef CONFIG_BLK_DEV_IDEATAPI
switch (drive->media) {
case ide_disk:
do_rw_disk (drive, rq, block);
return;
#ifdef CONFIG_BLK_DEV_IDECD
case ide_cdrom:
ide_do_rw_cdrom (drive, block);
return;
#endif /* CONFIG_BLK_DEV_IDECD */
#ifdef CONFIG_BLK_DEV_IDETAPE
case ide_tape:
idetape_do_request (drive, rq, block);
return;
#endif /* CONFIG_BLK_DEV_IDETAPE */
#ifdef CONFIG_BLK_DEV_IDEFLOPPY
case ide_floppy:
idefloppy_do_request (drive, rq, block);
return;
#endif /* CONFIG_BLK_DEV_IDEFLOPPY */
#ifdef CONFIG_BLK_DEV_IDESCSI
case ide_scsi:
idescsi_do_request (drive, rq, block);
return;
#endif /* CONFIG_BLK_DEV_IDESCSI */
 
default:
printk("%s: media type %d not supported\n",
drive->name, drive->media);
goto kill_rq;
}
#else
do_rw_disk (drive, rq, block); /* simpler and faster */
return;
#endif /* CONFIG_BLK_DEV_IDEATAPI */;
}
do_special(drive);
return;
kill_rq:
ide_end_request(0, hwif->hwgroup);
}
 
/*
* The driver enables interrupts as much as possible. In order to do this,
* (a) the device-interrupt is always masked before entry, and
* (b) the timeout-interrupt is always disabled before entry.
*
* If we enter here from, say irq14, and then start a new request for irq15,
* (possible with "serialize" option) then we cannot ensure that we exit
* before the irq15 hits us. So, we must be careful not to let this bother us.
*
* Interrupts are still masked (by default) whenever we are exchanging
* data/cmds with a drive, because some drives seem to have very poor
* tolerance for latency during I/O. For devices which don't suffer from
* this problem (most don't), the unmask flag can be set using the "hdparm"
* utility, to permit other interrupts during data/cmd transfers.
*/
void ide_do_request (ide_hwgroup_t *hwgroup)
{
cli(); /* paranoia */
if (hwgroup->handler != NULL) {
printk("%s: EEeekk!! handler not NULL in ide_do_request()\n", hwgroup->hwif->name);
return;
}
do {
ide_hwif_t *hwif = hwgroup->hwif;
struct request *rq;
if ((rq = hwgroup->rq) == NULL) {
if (hwif->sharing_irq && hwgroup->drive) /* set nIEN */
OUT_BYTE(hwgroup->drive->ctl|2,hwif->io.ctl_port);
/*
* hwgroup->next_hwif is different from hwgroup->hwif
* only when a request is inserted using "ide_next".
* This saves wear and tear on IDE tapes.
*/
hwif = hwgroup->next_hwif;
do {
rq = blk_dev[hwif->major].current_request;
if (rq != NULL && rq->rq_status != RQ_INACTIVE)
goto got_rq;
} while ((hwif = hwif->next) != hwgroup->next_hwif);
hwgroup->active = 0;
return; /* no work left for this hwgroup */
}
got_rq:
do_request(hwgroup->hwif = hwgroup->next_hwif = hwif, hwgroup->rq = rq);
cli();
} while (hwgroup->handler == NULL);
}
 
/*
* do_hwgroup_request() invokes ide_do_request() after first masking
* all possible interrupts for the current hwgroup. This prevents race
* conditions in the event that an unexpected interrupt occurs while
* we are in the driver.
*
* Note that when an interrupt is used to reenter the driver, the first level
* handler will already have masked the irq that triggered, but any other ones
* for the hwgroup will still be unmasked. The driver tries to be careful
* about such things.
*/
static void do_hwgroup_request (ide_hwgroup_t *hwgroup)
{
if (hwgroup->handler == NULL) {
ide_hwif_t *hgif = hwgroup->hwif;
ide_hwif_t *hwif = hgif;
hwgroup->active = 1;
do {
disable_irq(hwif->irq);
} while ((hwif = hwif->next) != hgif);
ide_do_request (hwgroup);
do {
enable_irq(hwif->irq);
} while ((hwif = hwif->next) != hgif);
}
}
 
static void do_ide0_request (void) /* invoked with cli() */
{
do_hwgroup_request (ide_hwifs[0].hwgroup);
}
 
#if MAX_HWIFS > 1
static void do_ide1_request (void) /* invoked with cli() */
{
do_hwgroup_request (ide_hwifs[1].hwgroup);
}
#endif
 
#if MAX_HWIFS > 2
static void do_ide2_request (void) /* invoked with cli() */
{
do_hwgroup_request (ide_hwifs[2].hwgroup);
}
#endif
 
#if MAX_HWIFS > 3
static void do_ide3_request (void) /* invoked with cli() */
{
do_hwgroup_request (ide_hwifs[3].hwgroup);
}
#endif
 
static void timer_expiry (unsigned long data)
{
ide_hwgroup_t *hwgroup = (ide_hwgroup_t *) data;
ide_drive_t *drive = hwgroup->drive;
unsigned long flags;
 
save_flags(flags);
cli();
 
if (hwgroup->poll_timeout != 0) { /* polling in progress? */
ide_handler_t *handler = hwgroup->handler;
hwgroup->handler = NULL;
handler(drive);
} else if (hwgroup->handler == NULL) { /* not waiting for anything? */
sti(); /* drive must have responded just as the timer expired */
printk("%s: marginal timeout\n", drive->name);
} else {
hwgroup->handler = NULL; /* abort the operation */
if (hwgroup->hwif->dmaproc)
(void) hwgroup->hwif->dmaproc (ide_dma_abort, drive);
ide_error(drive, "irq timeout", GET_STAT());
}
if (hwgroup->handler == NULL)
do_hwgroup_request (hwgroup);
restore_flags(flags);
}
 
/*
* There's nothing really useful we can do with an unexpected interrupt,
* other than reading the status register (to clear it), and logging it.
* There should be no way that an irq can happen before we're ready for it,
* so we needn't worry much about losing an "important" interrupt here.
*
* On laptops (and "green" PCs), an unexpected interrupt occurs whenever the
* drive enters "idle", "standby", or "sleep" mode, so if the status looks
* "good", we just ignore the interrupt completely.
*
* This routine assumes cli() is in effect when called.
*
* If an unexpected interrupt happens on irq15 while we are handling irq14
* and if the two interfaces are "serialized" (CMD640), then it looks like
* we could screw up by interfering with a new request being set up for irq15.
*
* In reality, this is a non-issue. The new command is not sent unless the
* drive is ready to accept one, in which case we know the drive is not
* trying to interrupt us. And ide_set_handler() is always invoked before
* completing the issuance of any new drive command, so we will not be
* accidently invoked as a result of any valid command completion interrupt.
*
*/
static void unexpected_intr (int irq, ide_hwgroup_t *hwgroup)
{
byte stat;
unsigned int unit;
ide_hwif_t *hwif = hwgroup->hwif;
 
/*
* handle the unexpected interrupt
*/
do {
if (hwif->irq == irq) {
for (unit = 0; unit < MAX_DRIVES; ++unit) {
ide_drive_t *drive = &hwif->drives[unit];
if (!drive->present)
continue;
SELECT_DRIVE(hwif,drive);
udelay(100); /* Ugly, but wait_stat() may not be safe here */
if (!OK_STAT(stat=GET_STAT(), drive->ready_stat, BAD_STAT)) {
/* Try to not flood the console with msgs */
static unsigned long last_msgtime = 0;
if ((last_msgtime + (HZ/2)) < jiffies) {
last_msgtime = jiffies;
(void) ide_dump_status(drive, "unexpected_intr", stat);
}
}
if ((stat & DRQ_STAT))
try_to_flush_leftover_data(drive);
}
}
} while ((hwif = hwif->next) != hwgroup->hwif);
SELECT_DRIVE(hwif,hwgroup->drive); /* Ugh.. probably interrupts current I/O */
udelay(100); /* Ugly, but wait_stat() may not be safe here */
}
 
/*
* entry point for all interrupts, caller does cli() for us
*/
void ide_intr (int irq, void *dev_id, struct pt_regs *regs)
{
ide_hwgroup_t *hwgroup = dev_id;
ide_handler_t *handler;
 
if (irq == hwgroup->hwif->irq && (handler = hwgroup->handler) != NULL) {
ide_drive_t *drive = hwgroup->drive;
hwgroup->handler = NULL;
del_timer(&(hwgroup->timer));
if (drive->unmask)
sti();
handler(drive);
cli(); /* this is necessary, as next rq may be different irq */
if (hwgroup->handler == NULL) {
SET_RECOVERY_TIMER(HWIF(drive));
ide_do_request(hwgroup);
}
} else {
unexpected_intr(irq, hwgroup);
}
cli();
}
 
/*
* get_info_ptr() returns the (ide_drive_t *) for a given device number.
* It returns NULL if the given device number does not match any present drives.
*/
static ide_drive_t *get_info_ptr (kdev_t i_rdev)
{
int major = MAJOR(i_rdev);
unsigned int h;
 
for (h = 0; h < MAX_HWIFS; ++h) {
ide_hwif_t *hwif = &ide_hwifs[h];
if (hwif->present && major == hwif->major) {
unsigned unit = DEVICE_NR(i_rdev);
if (unit < MAX_DRIVES) {
ide_drive_t *drive = &hwif->drives[unit];
if (drive->present)
return drive;
} else if (major == IDE0_MAJOR && unit < 4) {
printk("ide: probable bad entry for /dev/hd%c\n", 'a'+unit);
printk("ide: to fix it, run: /usr/src/linux/scripts/MAKEDEV.ide\n");
}
break;
}
}
return NULL;
}
 
/*
* This function is intended to be used prior to invoking ide_do_drive_cmd().
*/
void ide_init_drive_cmd (struct request *rq)
{
rq->buffer = NULL;
rq->cmd = IDE_DRIVE_CMD;
rq->sector = 0;
rq->nr_sectors = 0;
rq->current_nr_sectors = 0;
rq->sem = NULL;
rq->bh = NULL;
rq->bhtail = NULL;
rq->next = NULL;
 
#if 0 /* these are done each time through ide_do_drive_cmd() */
rq->errors = 0;
rq->rq_status = RQ_ACTIVE;
rq->rq_dev = ????;
#endif
}
 
/*
* This function issues a special IDE device request
* onto the request queue.
*
* If action is ide_wait, then then rq is queued at the end of
* the request queue, and the function sleeps until it has been
* processed. This is for use when invoked from an ioctl handler.
*
* If action is ide_preempt, then the rq is queued at the head of
* the request queue, displacing the currently-being-processed
* request and this function returns immediately without waiting
* for the new rq to be completed. This is VERY DANGEROUS, and is
* intended for careful use by the ATAPI tape/cdrom driver code.
*
* If action is ide_next, then the rq is queued immediately after
* the currently-being-processed-request (if any), and the function
* returns without waiting for the new rq to be completed. As above,
* This is VERY DANGEROUS, and is intended for careful use by the
* ATAPI tape/cdrom driver code.
*
* If action is ide_end, then the rq is queued at the end of the
* request queue, and the function returns immediately without waiting
* for the new rq to be completed. This is again intended for careful
* use by the ATAPI tape/cdrom driver code. (Currently used by ide-tape.c,
* when operating in the pipelined operation mode).
*/
int ide_do_drive_cmd (ide_drive_t *drive, struct request *rq, ide_action_t action)
{
unsigned long flags;
unsigned int major = HWIF(drive)->major;
struct request *cur_rq;
struct blk_dev_struct *bdev = &blk_dev[major];
struct semaphore sem = MUTEX_LOCKED;
 
if (IS_PROMISE_DRIVE && rq->buffer != NULL)
return -ENOSYS; /* special drive cmds not supported */
rq->errors = 0;
rq->rq_status = RQ_ACTIVE;
rq->rq_dev = MKDEV(major,(drive->select.b.unit)<<PARTN_BITS);
if (action == ide_wait)
rq->sem = &sem;
unplug_device(bdev);
 
save_flags(flags);
cli();
if (action == ide_next)
HWGROUP(drive)->next_hwif = HWIF(drive);
cur_rq = bdev->current_request;
 
if (cur_rq == NULL || action == ide_preempt) {
rq->next = cur_rq;
bdev->current_request = rq;
if (action == ide_preempt)
HWGROUP(drive)->rq = NULL;
} else {
if (action == ide_wait || action == ide_end) {
while (cur_rq->next != NULL) /* find end of list */
cur_rq = cur_rq->next;
}
rq->next = cur_rq->next;
cur_rq->next = rq;
}
if (!HWGROUP(drive)->active) {
do_hwgroup_request(HWGROUP(drive));
cli();
}
if (action == ide_wait && rq->rq_status != RQ_INACTIVE)
down(&sem); /* wait for it to be serviced */
restore_flags(flags);
return rq->errors ? -EIO : 0; /* return -EIO if errors */
}
 
static int ide_open(struct inode * inode, struct file * filp)
{
ide_drive_t *drive;
unsigned long flags;
 
if ((drive = get_info_ptr(inode->i_rdev)) == NULL)
return -ENXIO;
save_flags(flags);
cli();
while (drive->busy)
sleep_on(&drive->wqueue);
drive->usage++;
restore_flags(flags);
#ifdef CONFIG_BLK_DEV_IDECD
if (drive->media == ide_cdrom)
return ide_cdrom_open (inode, filp, drive);
#endif /* CONFIG_BLK_DEV_IDECD */
#ifdef CONFIG_BLK_DEV_IDETAPE
if (drive->media == ide_tape)
return idetape_blkdev_open (inode, filp, drive);
#endif /* CONFIG_BLK_DEV_IDETAPE */
#ifdef CONFIG_BLK_DEV_IDEFLOPPY
if (drive->media == ide_floppy)
return idefloppy_open (inode, filp, drive);
#endif /* CONFIG_BLK_DEV_IDEFLOPPY */
#ifdef CONFIG_BLK_DEV_IDESCSI
if (drive->media == ide_scsi)
return idescsi_open (inode, filp, drive);
#endif /* CONFIG_BLK_DEV_IDESCSI */
if (drive->removable && drive->usage == 1) {
byte door_lock[] = {WIN_DOORLOCK,0,0,0};
struct request rq;
check_disk_change(inode->i_rdev);
ide_init_drive_cmd (&rq);
rq.buffer = door_lock;
/*
* Ignore the return code from door_lock,
* since the open() has already succeeded,
* and the door_lock is irrelevant at this point.
*/
(void) ide_do_drive_cmd(drive, &rq, ide_wait);
}
return 0;
}
 
/*
* Releasing a block device means we sync() it, so that it can safely
* be forgotten about...
*/
static void ide_release(struct inode * inode, struct file * file)
{
ide_drive_t *drive;
 
if ((drive = get_info_ptr(inode->i_rdev)) != NULL) {
fsync_dev(inode->i_rdev);
drive->usage--;
#ifdef CONFIG_BLK_DEV_IDECD
if (drive->media == ide_cdrom) {
ide_cdrom_release (inode, file, drive);
return;
}
#endif /* CONFIG_BLK_DEV_IDECD */
#ifdef CONFIG_BLK_DEV_IDETAPE
if (drive->media == ide_tape) {
idetape_blkdev_release (inode, file, drive);
return;
}
#endif /* CONFIG_BLK_DEV_IDETAPE */
#ifdef CONFIG_BLK_DEV_IDEFLOPPY
if (drive->media == ide_floppy) {
idefloppy_release (inode, file, drive);
return;
}
#endif /* CONFIG_BLK_DEV_IDEFLOPPY */
#ifdef CONFIG_BLK_DEV_IDESCSI
if (drive->media == ide_scsi) {
idescsi_ide_release (inode, file, drive);
return;
}
#endif /* CONFIG_BLK_DEV_IDESCSI */
if (drive->removable && !drive->usage) {
byte door_unlock[] = {WIN_DOORUNLOCK,0,0,0};
struct request rq;
invalidate_buffers(inode->i_rdev);
ide_init_drive_cmd (&rq);
rq.buffer = door_unlock;
(void) ide_do_drive_cmd(drive, &rq, ide_wait);
}
}
}
 
/*
* This routine is called to flush all partitions and partition tables
* for a changed disk, and then re-read the new partition table.
* If we are revalidating a disk because of a media change, then we
* enter with usage == 0. If we are using an ioctl, we automatically have
* usage == 1 (we need an open channel to use an ioctl :-), so this
* is our limit.
*/
static int revalidate_disk(kdev_t i_rdev)
{
ide_drive_t *drive;
unsigned int p, major, minor;
long flags;
 
if ((drive = get_info_ptr(i_rdev)) == NULL)
return -ENODEV;
 
major = MAJOR(i_rdev);
minor = drive->select.b.unit << PARTN_BITS;
save_flags(flags);
cli();
if (drive->busy || (drive->usage > 1)) {
restore_flags(flags);
return -EBUSY;
};
drive->busy = 1;
restore_flags(flags);
 
for (p = 0; p < (1<<PARTN_BITS); ++p) {
if (drive->part[p].nr_sects > 0) {
kdev_t devp = MKDEV(major, minor+p);
fsync_dev (devp);
invalidate_inodes (devp);
invalidate_buffers (devp);
set_blocksize(devp, 1024);
}
drive->part[p].start_sect = 0;
drive->part[p].nr_sects = 0;
};
 
drive->part[0].nr_sects = current_capacity(drive);
if ((drive->media != ide_disk && drive->media != ide_floppy) || !drive->part[0].nr_sects)
drive->part[0].start_sect = -1;
resetup_one_dev(HWIF(drive)->gd, drive->select.b.unit);
 
drive->busy = 0;
wake_up(&drive->wqueue);
return 0;
}
 
static int write_fs_long (unsigned long useraddr, long value)
{
int err;
 
if (NULL == (long *)useraddr)
return -EINVAL;
if ((err = verify_area(VERIFY_WRITE, (long *)useraddr, sizeof(long))))
return err;
put_user((unsigned)value, (long *) useraddr);
return 0;
}
 
static int ide_ioctl (struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
int err;
ide_drive_t *drive;
unsigned long flags;
struct request rq;
 
if (!inode || !(inode->i_rdev))
return -EINVAL;
if ((drive = get_info_ptr(inode->i_rdev)) == NULL)
return -ENODEV;
ide_init_drive_cmd (&rq);
switch (cmd) {
case HDIO_GETGEO:
{
struct hd_geometry *loc = (struct hd_geometry *) arg;
if (!loc || (drive->media != ide_disk && drive->media != ide_floppy)) return -EINVAL;
err = verify_area(VERIFY_WRITE, loc, sizeof(*loc));
if (err) return err;
put_user(drive->bios_head, (byte *) &loc->heads);
put_user(drive->bios_sect, (byte *) &loc->sectors);
put_user(drive->bios_cyl, (unsigned short *) &loc->cylinders);
put_user((unsigned)drive->part[MINOR(inode->i_rdev)&PARTN_MASK].start_sect,
(unsigned long *) &loc->start);
return 0;
}
case BLKFLSBUF:
if (!suser()) return -EACCES;
fsync_dev(inode->i_rdev);
invalidate_buffers(inode->i_rdev);
return 0;
 
case BLKRASET:
if (!suser()) return -EACCES;
if(arg > 0xff) return -EINVAL;
read_ahead[MAJOR(inode->i_rdev)] = arg;
return 0;
 
case BLKRAGET:
return write_fs_long(arg, read_ahead[MAJOR(inode->i_rdev)]);
 
case BLKGETSIZE: /* Return device size */
return write_fs_long(arg, drive->part[MINOR(inode->i_rdev)&PARTN_MASK].nr_sects);
case BLKRRPART: /* Re-read partition tables */
if (!suser()) return -EACCES;
return revalidate_disk(inode->i_rdev);
 
case HDIO_GET_KEEPSETTINGS:
return write_fs_long(arg, drive->keep_settings);
 
case HDIO_GET_UNMASKINTR:
return write_fs_long(arg, drive->unmask);
 
case HDIO_GET_DMA:
return write_fs_long(arg, drive->using_dma);
 
case HDIO_GET_32BIT:
return write_fs_long(arg, drive->io_32bit);
 
case HDIO_GET_MULTCOUNT:
return write_fs_long(arg, drive->mult_count);
 
case HDIO_GET_IDENTITY:
if (!arg || (MINOR(inode->i_rdev) & PARTN_MASK))
return -EINVAL;
if (drive->id == NULL)
return -ENOMSG;
err = verify_area(VERIFY_WRITE, (char *)arg, sizeof(*drive->id));
if (!err)
memcpy_tofs((char *)arg, (char *)drive->id, sizeof(*drive->id));
return err;
 
case HDIO_GET_NOWERR:
return write_fs_long(arg, drive->bad_wstat == BAD_R_STAT);
 
case HDIO_SET_DMA:
if (!suser()) return -EACCES;
#ifdef CONFIG_BLK_DEV_IDECD
if (drive->media == ide_cdrom)
return -EPERM;
#endif /* CONFIG_BLK_DEV_IDECD */
if (!drive->id || !(drive->id->capability & 1) || !HWIF(drive)->dmaproc)
return -EPERM;
case HDIO_SET_KEEPSETTINGS:
case HDIO_SET_UNMASKINTR:
case HDIO_SET_NOWERR:
if (arg > 1)
return -EINVAL;
case HDIO_SET_32BIT:
if (!suser()) return -EACCES;
if ((MINOR(inode->i_rdev) & PARTN_MASK))
return -EINVAL;
save_flags(flags);
cli();
switch (cmd) {
case HDIO_SET_DMA:
if (!(HWIF(drive)->dmaproc)) {
restore_flags(flags);
return -EPERM;
}
drive->using_dma = arg;
break;
case HDIO_SET_KEEPSETTINGS:
drive->keep_settings = arg;
break;
case HDIO_SET_UNMASKINTR:
if (arg && drive->no_unmask) {
restore_flags(flags);
return -EPERM;
}
drive->unmask = arg;
break;
case HDIO_SET_NOWERR:
drive->bad_wstat = arg ? BAD_R_STAT : BAD_W_STAT;
break;
case HDIO_SET_32BIT:
if (arg > (1 + (SUPPORT_VLB_SYNC<<1))) {
restore_flags(flags);
return -EINVAL;
}
if (arg && drive->no_io_32bit) {
restore_flags(flags);
return -EPERM;
}
drive->io_32bit = arg;
#ifdef CONFIG_BLK_DEV_DTC2278
if (HWIF(drive)->chipset == ide_dtc2278)
HWIF(drive)->drives[!drive->select.b.unit].io_32bit = arg;
#endif /* CONFIG_BLK_DEV_DTC2278 */
break;
}
restore_flags(flags);
return 0;
 
case HDIO_SET_MULTCOUNT:
if (!suser()) return -EACCES;
if (MINOR(inode->i_rdev) & PARTN_MASK)
return -EINVAL;
if (drive->id && arg > drive->id->max_multsect)
return -EINVAL;
save_flags(flags);
cli();
if (drive->special.b.set_multmode) {
restore_flags(flags);
return -EBUSY;
}
drive->mult_req = arg;
drive->special.b.set_multmode = 1;
restore_flags(flags);
(void) ide_do_drive_cmd (drive, &rq, ide_wait);
return (drive->mult_count == arg) ? 0 : -EIO;
 
case HDIO_DRIVE_CMD:
{
byte args[4], *argbuf = args;
int argsize = 4;
if (!suser() || securelevel > 0) return -EACCES;
if (NULL == (void *) arg) {
err = ide_do_drive_cmd(drive, &rq, ide_wait);
} else if (!(err = verify_area(VERIFY_READ,(void *)arg, 4))) {
memcpy_fromfs(args, (void *)arg, 4);
if (args[3]) {
argsize = 4 + (SECTOR_WORDS * 4 * args[3]);
argbuf = kmalloc(argsize, GFP_KERNEL);
if (argbuf == NULL)
return -ENOMEM;
argbuf[0] = args[0];
argbuf[1] = args[1];
argbuf[2] = args[2];
argbuf[3] = args[3];
}
if (!(err = verify_area(VERIFY_WRITE,(void *)arg, argsize))) {
rq.buffer = argbuf;
err = ide_do_drive_cmd(drive, &rq, ide_wait);
memcpy_tofs((void *)arg, argbuf, argsize);
}
if (argsize > 4)
kfree(argbuf);
}
return err;
}
case HDIO_SET_PIO_MODE:
if (!suser()) return -EACCES;
if (MINOR(inode->i_rdev) & PARTN_MASK)
return -EINVAL;
if (!HWIF(drive)->tuneproc)
return -ENOSYS;
save_flags(flags);
cli();
if (drive->special.b.set_tune) {
restore_flags(flags);
return -EBUSY;
}
drive->tune_req = (byte) arg;
drive->special.b.set_tune = 1;
restore_flags(flags);
(void) ide_do_drive_cmd (drive, &rq, ide_wait);
return 0;
 
RO_IOCTLS(inode->i_rdev, arg);
 
default:
#ifdef CONFIG_BLK_DEV_IDECD
if (drive->media == ide_cdrom)
return ide_cdrom_ioctl(drive, inode, file, cmd, arg);
#endif /* CONFIG_BLK_DEV_IDECD */
#ifdef CONFIG_BLK_DEV_IDETAPE
if (drive->media == ide_tape)
return idetape_blkdev_ioctl(drive, inode, file, cmd, arg);
#endif /* CONFIG_BLK_DEV_IDETAPE */
#ifdef CONFIG_BLK_DEV_IDEFLOPPY
if (drive->media == ide_floppy)
return idefloppy_ioctl(drive, inode, file, cmd, arg);
#endif /* CONFIG_BLK_DEV_IDEFLOPPY */
#ifdef CONFIG_BLK_DEV_IDESCSI
if (drive->media == ide_scsi)
return idescsi_ioctl(drive, inode, file, cmd, arg);
#endif /* CONFIG_BLK_DEV_IDESCSI */
return -EPERM;
}
}
 
static int ide_check_media_change (kdev_t i_rdev)
{
ide_drive_t *drive;
 
if ((drive = get_info_ptr(i_rdev)) == NULL)
return -ENODEV;
#ifdef CONFIG_BLK_DEV_IDECD
if (drive->media == ide_cdrom)
return ide_cdrom_check_media_change (drive);
#endif /* CONFIG_BLK_DEV_IDECD */
#ifdef CONFIG_BLK_DEV_IDEFLOPPY
if (drive->media == ide_floppy)
return idefloppy_media_change (drive);
#endif /* CONFIG_BLK_DEV_IDEFLOPPY */
if (drive->removable) /* for disks */
return 1; /* always assume it was changed */
return 0;
}
 
void ide_fixstring (byte *s, const int bytecount, const int byteswap)
{
byte *p = s, *end = &s[bytecount & ~1]; /* bytecount must be even */
 
if (byteswap) {
/* convert from big-endian to host byte order */
for (p = end ; p != s;) {
unsigned short *pp = (unsigned short *) (p -= 2);
*pp = ntohs(*pp);
}
}
 
/* strip leading blanks */
while (s != end && *s == ' ')
++s;
 
/* compress internal blanks and strip trailing blanks */
while (s != end && *s) {
if (*s++ != ' ' || (s != end && *s && *s != ' '))
*p++ = *(s-1);
}
 
/* wipe out trailing garbage */
while (p != end)
*p++ = '\0';
}
 
static inline void do_identify (ide_drive_t *drive, byte cmd)
{
int bswap;
struct hd_driveid *id;
unsigned long capacity, check;
 
id = drive->id = kmalloc (SECTOR_WORDS*4, GFP_KERNEL);
ide_input_data(drive, id, SECTOR_WORDS);/* read 512 bytes of id info */
sti();
 
#if defined (CONFIG_SCSI_EATA_DMA) || defined (CONFIG_SCSI_EATA_PIO) || defined (CONFIG_SCSI_EATA)
/*
* EATA SCSI controllers do a hardware ATA emulation:
* Ignore them if there is a driver for them available.
*/
if ((id->model[0] == 'P' && id->model[1] == 'M')
|| (id->model[0] == 'S' && id->model[1] == 'K')) {
printk("%s: EATA SCSI HBA %.10s\n", drive->name, id->model);
drive->present = 0;
return;
}
#endif
 
/*
* WIN_IDENTIFY returns little-endian info,
* WIN_PIDENTIFY *usually* returns little-endian info.
*/
bswap = 1;
if (cmd == WIN_PIDENTIFY) {
if ((id->model[0] == 'N' && id->model[1] == 'E') /* NEC */
|| (id->model[0] == 'F' && id->model[1] == 'X') /* Mitsumi */
|| (id->model[0] == 'P' && id->model[1] == 'i'))/* Pioneer */
bswap = 0; /* Vertos drives may still be weird */
}
ide_fixstring (id->model, sizeof(id->model), bswap);
ide_fixstring (id->fw_rev, sizeof(id->fw_rev), bswap);
ide_fixstring (id->serial_no, sizeof(id->serial_no), bswap);
 
if (strstr(id->model, "E X A B Y T E N E S T"))
return;
 
#ifdef CONFIG_BLK_DEV_IDEATAPI
/*
* Check for an ATAPI device
*/
if (cmd == WIN_PIDENTIFY) {
byte type = (id->config >> 8) & 0x1f;
printk("%s: %s, ATAPI ", drive->name, id->model);
#ifdef CONFIG_BLK_DEV_PROMISE
if (HWIF(drive)->is_promise2) {
printk(" -- not supported on 2nd Promise port\n");
drive->present = 0;
return;
}
#endif /* CONFIG_BLK_DEV_PROMISE */
if (!drive->ide_scsi) switch (type) {
case 0:
if (!strstr(id->model, "oppy") && !strstr(id->model, "poyp") && !strstr(id->model, "ZIP"))
printk("cdrom or floppy?, assuming ");
if (drive->media != ide_cdrom && !strstr(id->model, "CD-ROM")) {
#ifdef CONFIG_BLK_DEV_IDEFLOPPY
printk("FLOPPY drive\n");
drive->media = ide_floppy;
if (idefloppy_identify_device(drive, id))
drive->present = 1;
return;
#else
printk("FLOPPY ");
break;
#endif /* CONFIG_BLK_DEV_IDEFLOPPY */
}
/* Early cdrom models used zero */
case 5:
#ifdef CONFIG_BLK_DEV_IDECD
printk ("CDROM drive\n");
drive->media = ide_cdrom;
drive->present = 1;
drive->removable = 1;
return;
#else
printk ("CDROM ");
break;
#endif /* CONFIG_BLK_DEV_IDECD */
case 1:
#ifdef CONFIG_BLK_DEV_IDETAPE
printk ("TAPE drive");
if (idetape_identify_device (drive,id)) {
drive->media = ide_tape;
drive->present = 1;
drive->removable = 1;
if (drive->autotune != 2 && HWIF(drive)->dmaproc != NULL) {
if (!HWIF(drive)->dmaproc(ide_dma_check, drive))
printk(", DMA");
}
printk("\n");
}
else {
drive->present = 0;
printk ("\nide-tape: the tape is not supported by this version of the driver\n");
}
return;
#else
printk ("TAPE ");
break;
#endif /* CONFIG_BLK_DEV_IDETAPE */
default:
drive->present = 0;
printk("Type %d - Unknown device\n", type);
return;
}
#ifdef CONFIG_BLK_DEV_IDESCSI
printk("drive - enabling SCSI emulation\n");
drive->media = ide_scsi;
drive->present = 1;
idescsi_setup(drive);
#else
drive->present = 0;
printk("- not supported by this kernel\n");
#endif /* CONFIG_BLK_DEV_IDESCSI */
return;
}
#endif /* CONFIG_BLK_DEV_IDEATAPI */
 
/* check for removable disks (eg. SYQUEST), ignore 'WD' drives */
if (id->config & (1<<7)) { /* removable disk ? */
if (id->model[0] != 'W' || id->model[1] != 'D')
drive->removable = 1;
}
 
/* SunDisk drives: treat as non-removable, force one unit */
if (id->model[0] == 'S' && id->model[1] == 'u') {
drive->removable = 0;
if (drive->select.all & (1<<4)) {
drive->present = 0;
return;
}
}
 
drive->media = ide_disk;
/* Extract geometry if we did not already have one for the drive */
if (!drive->present) {
drive->present = 1;
drive->cyl = drive->bios_cyl = id->cyls;
drive->head = drive->bios_head = id->heads;
drive->sect = drive->bios_sect = id->sectors;
}
/* Handle logical geometry translation by the drive */
if ((id->field_valid & 1) && id->cur_cyls && id->cur_heads
&& (id->cur_heads <= 16) && id->cur_sectors)
{
/*
* Extract the physical drive geometry for our use.
* Note that we purposely do *not* update the bios info.
* This way, programs that use it (like fdisk) will
* still have the same logical view as the BIOS does,
* which keeps the partition table from being screwed.
*
* An exception to this is the cylinder count,
* which we reexamine later on to correct for 1024 limitations.
*/
drive->cyl = id->cur_cyls;
drive->head = id->cur_heads;
drive->sect = id->cur_sectors;
 
/* check for word-swapped "capacity" field in id information */
capacity = drive->cyl * drive->head * drive->sect;
check = (id->cur_capacity0 << 16) | id->cur_capacity1;
if (check == capacity) { /* was it swapped? */
/* yes, bring it into little-endian order: */
id->cur_capacity0 = (capacity >> 0) & 0xffff;
id->cur_capacity1 = (capacity >> 16) & 0xffff;
}
}
/* Use physical geometry if what we have still makes no sense */
if ((!drive->head || drive->head > 16) && id->heads && id->heads <= 16) {
drive->cyl = id->cyls;
drive->head = id->heads;
drive->sect = id->sectors;
}
 
/* calculate drive capacity, and select LBA if possible */
(void) current_capacity (drive);
 
/* Correct the number of cyls if the bios value is too small */
if (drive->sect == drive->bios_sect && drive->head == drive->bios_head) {
if (drive->cyl > drive->bios_cyl)
drive->bios_cyl = drive->cyl;
}
 
if (!strncmp(id->model, "BMI ", 4) &&
strstr(id->model, " ENHANCED IDE ") &&
drive->select.b.lba)
drive->no_geom = 1;
 
printk ("%s: %.40s, %ldMB w/%dkB Cache, CHS=%d/%d/%d",
drive->name, id->model, current_capacity(drive)/2048L, id->buf_size/2,
drive->bios_cyl, drive->bios_head, drive->bios_sect);
 
drive->mult_count = 0;
if (id->max_multsect) {
drive->mult_req = INITIAL_MULT_COUNT;
if (drive->mult_req > id->max_multsect)
drive->mult_req = id->max_multsect;
if (drive->mult_req || ((id->multsect_valid & 1) && id->multsect))
drive->special.b.set_multmode = 1;
}
if (drive->autotune != 2 && HWIF(drive)->dmaproc != NULL) {
if (!(HWIF(drive)->dmaproc(ide_dma_check, drive))) {
if ((id->field_valid & 4) && (id->dma_ultra & (id->dma_ultra >> 8) & 7))
printk(", UDMA");
else
printk(", DMA");
}
}
printk("\n");
}
 
/*
* Delay for *at least* 50ms. As we don't know how much time is left
* until the next tick occurs, we wait an extra tick to be safe.
* This is used only during the probing/polling for drives at boot time.
*/
static void delay_50ms (void)
{
unsigned long timer = jiffies + ((HZ + 19)/20) + 1;
while (timer > jiffies);
}
 
/*
* try_to_identify() sends an ATA(PI) IDENTIFY request to a drive
* and waits for a response. It also monitors irqs while this is
* happening, in hope of automatically determining which one is
* being used by the interface.
*
* Returns: 0 device was identified
* 1 device timed-out (no response to identify request)
* 2 device aborted the command (refused to identify itself)
*/
static int try_to_identify (ide_drive_t *drive, byte cmd)
{
int hd_status, rc;
unsigned long timeout;
unsigned long irqs_on = 0;
int irq_off;
 
if (!HWIF(drive)->irq) { /* already got an IRQ? */
probe_irq_off(probe_irq_on()); /* clear dangling irqs */
irqs_on = probe_irq_on(); /* start monitoring irqs */
OUT_BYTE(drive->ctl,IDE_CONTROL_REG); /* enable device irq */
}
 
delay_50ms(); /* take a deep breath */
if ((IN_BYTE(IDE_ALTSTATUS_REG) ^ IN_BYTE(IDE_STATUS_REG)) & ~INDEX_STAT) {
printk("%s: probing with STATUS instead of ALTSTATUS\n", drive->name);
hd_status = IDE_STATUS_REG; /* ancient Seagate drives */
} else
hd_status = IDE_ALTSTATUS_REG; /* use non-intrusive polling */
 
#if CONFIG_BLK_DEV_PROMISE
if (IS_PROMISE_DRIVE) {
if (promise_cmd(drive,PROMISE_IDENTIFY)) {
if (irqs_on)
(void) probe_irq_off(irqs_on);
return 1;
}
} else
#endif /* CONFIG_BLK_DEV_PROMISE */
OUT_BYTE(cmd,IDE_COMMAND_REG); /* ask drive for ID */
timeout = ((cmd == WIN_IDENTIFY) ? WAIT_WORSTCASE : WAIT_PIDENTIFY) / 2;
timeout += jiffies;
do {
if (jiffies > timeout) {
if (irqs_on)
(void) probe_irq_off(irqs_on);
return 1; /* drive timed-out */
}
delay_50ms(); /* give drive a breather */
} while (IN_BYTE(hd_status) & BUSY_STAT);
 
delay_50ms(); /* wait for IRQ and DRQ_STAT */
if (OK_STAT(GET_STAT(),DRQ_STAT,BAD_R_STAT)) {
unsigned long flags;
save_flags(flags);
cli(); /* some systems need this */
do_identify(drive, cmd); /* drive returned ID */
rc = 0; /* drive responded with ID */
(void) GET_STAT(); /* clear drive IRQ */
restore_flags(flags);
} else
rc = 2; /* drive refused ID */
if (!HWIF(drive)->irq) {
irq_off = probe_irq_off(irqs_on); /* get our irq number */
if (irq_off > 0) {
HWIF(drive)->irq = irq_off; /* save it for later */
irqs_on = probe_irq_on();
OUT_BYTE(drive->ctl|2,IDE_CONTROL_REG); /* mask device irq */
udelay(5);
(void) probe_irq_off(irqs_on);
(void) probe_irq_off(probe_irq_on()); /* clear self-inflicted irq */
(void) GET_STAT(); /* clear drive IRQ */
 
} else { /* Mmmm.. multiple IRQs.. don't know which was ours */
printk("%s: IRQ probe failed (%d)\n", drive->name, irq_off);
#ifdef CONFIG_BLK_DEV_CMD640
#ifdef CMD640_DUMP_REGS
if (HWIF(drive)->chipset == ide_cmd640) {
printk("%s: Hmmm.. probably a driver problem.\n", drive->name);
CMD640_DUMP_REGS;
}
#endif /* CMD640_DUMP_REGS */
#endif /* CONFIG_BLK_DEV_CMD640 */
}
}
return rc;
}
 
/*
* do_probe() has the difficult job of finding a drive if it exists,
* without getting hung up if it doesn't exist, without trampling on
* ethernet cards, and without leaving any IRQs dangling to haunt us later.
*
* If a drive is "known" to exist (from CMOS or kernel parameters),
* but does not respond right away, the probe will "hang in there"
* for the maximum wait time (about 30 seconds), otherwise it will
* exit much more quickly.
*
* Returns: 0 device was identified
* 1 device timed-out (no response to identify request)
* 2 device aborted the command (refused to identify itself)
* 3 bad status from device (possible for ATAPI drives)
* 4 probe was not attempted because failure was obvious
*/
static int do_probe (ide_drive_t *drive, byte cmd)
{
int rc;
ide_hwif_t *hwif = HWIF(drive);
unsigned long timeout;
#ifdef CONFIG_BLK_DEV_IDEATAPI
if (drive->present) { /* avoid waiting for inappropriate probes */
if ((drive->media != ide_disk) && (cmd == WIN_IDENTIFY))
return 4;
}
#endif /* CONFIG_BLK_DEV_IDEATAPI */
#ifdef DEBUG
printk("probing for %s: present=%d, media=%d, probetype=%s\n",
drive->name, drive->present, drive->media,
(cmd == WIN_IDENTIFY) ? "ATA" : "ATAPI");
#endif
SELECT_DRIVE(hwif,drive);
delay_50ms();
if (IN_BYTE(IDE_SELECT_REG) != drive->select.all && !drive->present) {
OUT_BYTE(0xa0,IDE_SELECT_REG); /* exit with drive0 selected */
delay_50ms(); /* allow BUSY_STAT to assert & clear */
return 3; /* no i/f present: avoid killing ethernet cards */
}
 
if (OK_STAT(GET_STAT(),READY_STAT,BUSY_STAT)
|| drive->present || cmd == WIN_PIDENTIFY)
{
if ((rc = try_to_identify(drive,cmd))) /* send cmd and wait */
rc = try_to_identify(drive,cmd); /* failed: try again */
if (rc == 1 && cmd == WIN_PIDENTIFY && drive->autotune != 2) {
printk("%s: no response (status = 0x%02x), resetting drive\n", drive->name, GET_STAT());
delay_50ms();
OUT_BYTE (drive->select.all, IDE_SELECT_REG);
delay_50ms();
OUT_BYTE(WIN_SRST, IDE_COMMAND_REG);
timeout = jiffies;
while ((GET_STAT() & BUSY_STAT) && jiffies < timeout + WAIT_WORSTCASE)
delay_50ms();
rc = try_to_identify(drive, cmd);
}
if (rc == 1)
printk("%s: no response (status = 0x%02x)\n", drive->name, GET_STAT());
(void) GET_STAT(); /* ensure drive irq is clear */
} else {
rc = 3; /* not present or maybe ATAPI */
}
if (drive->select.b.unit != 0) {
OUT_BYTE(0xa0,IDE_SELECT_REG); /* exit with drive0 selected */
delay_50ms();
(void) GET_STAT(); /* ensure drive irq is clear */
}
return rc;
}
 
static void enable_nest (ide_drive_t *drive)
{
unsigned long timeout;
 
printk("%s: enabling %s -- ", HWIF(drive)->name, drive->id->model);
SELECT_DRIVE(HWIF(drive), drive);
delay_50ms();
OUT_BYTE(EXABYTE_ENABLE_NEST, IDE_COMMAND_REG);
timeout = jiffies + WAIT_WORSTCASE;
do {
if (jiffies > timeout) {
printk("failed (timeout)\n");
return;
}
delay_50ms();
} while (GET_STAT() & BUSY_STAT);
delay_50ms();
if (!OK_STAT(GET_STAT(), 0, BAD_STAT))
printk("failed (status = 0x%02x)\n", GET_STAT());
else
printk("success\n");
if (do_probe(drive, WIN_IDENTIFY) >= 2) { /* if !(success||timed-out) */
#ifdef CONFIG_BLK_DEV_IDEATAPI
(void) do_probe(drive, WIN_PIDENTIFY); /* look for ATAPI device */
#endif /* CONFIG_BLK_DEV_IDEATAPI */
}
}
 
/*
* probe_for_drive() tests for existence of a given drive using do_probe().
*
* Returns: 0 no device was found
* 1 device was found (note: drive->present might still be 0)
*/
static inline byte probe_for_drive (ide_drive_t *drive)
{
if (drive->noprobe) /* skip probing? */
return drive->present;
if (do_probe(drive, WIN_IDENTIFY) >= 2) { /* if !(success||timed-out) */
#ifdef CONFIG_BLK_DEV_IDEATAPI
(void) do_probe(drive, WIN_PIDENTIFY); /* look for ATAPI device */
#endif /* CONFIG_BLK_DEV_IDEATAPI */
}
if (drive->id && strstr(drive->id->model, "E X A B Y T E N E S T"))
enable_nest(drive);
if (!drive->present)
return 0; /* drive not found */
if (drive->id == NULL) { /* identification failed? */
if (drive->media == ide_disk) {
printk ("%s: non-IDE drive, CHS=%d/%d/%d\n",
drive->name, drive->cyl, drive->head, drive->sect);
}
#ifdef CONFIG_BLK_DEV_IDECD
else if (drive->media == ide_cdrom) {
printk("%s: ATAPI cdrom (?)\n", drive->name);
}
#endif /* CONFIG_BLK_DEV_IDECD */
else {
drive->present = 0; /* nuke it */
}
}
return 1; /* drive was found */
}
 
/*
* We query CMOS about hard disks : it could be that we have a SCSI/ESDI/etc
* controller that is BIOS compatible with ST-506, and thus showing up in our
* BIOS table, but not register compatible, and therefore not present in CMOS.
*
* Furthermore, we will assume that our ST-506 drives <if any> are the primary
* drives in the system -- the ones reflected as drive 1 or 2. The first
* drive is stored in the high nibble of CMOS byte 0x12, the second in the low
* nibble. This will be either a 4 bit drive type or 0xf indicating use byte
* 0x19 for an 8 bit type, drive 1, 0x1a for drive 2 in CMOS. A non-zero value
* means we have an AT controller hard disk for that drive.
*
* Of course, there is no guarantee that either drive is actually on the
* "primary" IDE interface, but we don't bother trying to sort that out here.
* If a drive is not actually on the primary interface, then these parameters
* will be ignored. This results in the user having to supply the logical
* drive geometry as a boot parameter for each drive not on the primary i/f.
*
* The only "perfect" way to handle this would be to modify the setup.[cS] code
* to do BIOS calls Int13h/Fn08h and Int13h/Fn48h to get all of the drive info
* for us during initialization. I have the necessary docs -- any takers? -ml
*/
static void probe_cmos_for_drives (ide_hwif_t *hwif)
{
#ifdef __i386__
extern struct drive_info_struct drive_info;
byte cmos_disks, *BIOS = (byte *) &drive_info;
int unit;
 
#ifdef CONFIG_BLK_DEV_PROMISE
if (hwif->is_promise2)
return;
#endif /* CONFIG_BLK_DEV_PROMISE */
outb_p(0x12,0x70); /* specify CMOS address 0x12 */
cmos_disks = inb_p(0x71); /* read the data from 0x12 */
/* Extract drive geometry from CMOS+BIOS if not already setup */
for (unit = 0; unit < MAX_DRIVES; ++unit) {
ide_drive_t *drive = &hwif->drives[unit];
if ((cmos_disks & (0xf0 >> (unit*4))) && !drive->present && !drive->nobios) {
drive->cyl = drive->bios_cyl = *(unsigned short *)BIOS;
drive->head = drive->bios_head = *(BIOS+2);
drive->sect = drive->bios_sect = *(BIOS+14);
drive->ctl = *(BIOS+8);
drive->present = 1;
}
BIOS += 16;
}
#endif
}
 
/*
* This routine only knows how to look for drive units 0 and 1
* on an interface, so any setting of MAX_DRIVES > 2 won't work here.
*/
static void probe_hwif (ide_hwif_t *hwif)
{
unsigned int unit;
 
if (hwif->noprobe)
return;
if (hwif->io.io_base == HD_DATA)
probe_cmos_for_drives (hwif);
#if CONFIG_BLK_DEV_PROMISE
if (!hwif->is_promise2 &&
(check_region(hwif->io_base,8) || check_region(hwif->ctl_port,1))) {
#else
if (check_region(hwif->io.io_base,8) || check_region(hwif->io.ctl_port,1)) {
#endif /* CONFIG_BLK_DEV_PROMISE */
int msgout = 0;
for (unit = 0; unit < MAX_DRIVES; ++unit) {
ide_drive_t *drive = &hwif->drives[unit];
if (drive->present) {
drive->present = 0;
printk("%s: ERROR, PORTS ALREADY IN USE\n", drive->name);
msgout = 1;
}
}
if (!msgout)
printk("%s: ports already in use, skipping probe\n", hwif->name);
} else {
unsigned long flags;
save_flags(flags);
 
sti(); /* needed for jiffies and irq probing */
/*
* Second drive should only exist if first drive was found,
* but a lot of cdrom drives are configured as single slaves.
*/
for (unit = 0; unit < MAX_DRIVES; ++unit) {
ide_drive_t *drive = &hwif->drives[unit];
(void) probe_for_drive (drive);
if (drive->present && drive->media == ide_disk) {
if ((!drive->head || drive->head > 16) && !drive->select.b.lba) {
printk("%s: INVALID GEOMETRY: %d PHYSICAL HEADS?\n",
drive->name, drive->head);
drive->present = 0;
}
}
if (drive->present && !hwif->present) {
hwif->present = 1;
request_region(hwif->io.io_base, 8, hwif->name);
request_region(hwif->io.ctl_port, 1, hwif->name);
}
}
restore_flags(flags);
for (unit = 0; unit < MAX_DRIVES; ++unit) {
ide_drive_t *drive = &hwif->drives[unit];
if (drive->present && drive->media != ide_tape) {
ide_tuneproc_t *tuneproc = HWIF(drive)->tuneproc;
if (tuneproc != NULL && drive->autotune == 1)
tuneproc(drive, 255); /* auto-tune PIO mode */
}
}
}
}
 
/*
* stridx() returns the offset of c within s,
* or -1 if c is '\0' or not found within s.
*/
static int stridx (const char *s, char c)
{
char *i = strchr(s, c);
return (i && c) ? i - s : -1;
}
 
/*
* match_parm() does parsing for ide_setup():
*
* 1. the first char of s must be '='.
* 2. if the remainder matches one of the supplied keywords,
* the index (1 based) of the keyword is negated and returned.
* 3. if the remainder is a series of no more than max_vals numbers
* separated by commas, the numbers are saved in vals[] and a
* count of how many were saved is returned. Base10 is assumed,
* and base16 is allowed when prefixed with "0x".
* 4. otherwise, zero is returned.
*/
static int match_parm (char *s, const char *keywords[], int vals[], int max_vals)
{
static const char *decimal = "0123456789";
static const char *hex = "0123456789abcdef";
int i, n;
 
if (*s++ == '=') {
/*
* Try matching against the supplied keywords,
* and return -(index+1) if we match one
*/
if (keywords != NULL) {
for (i = 0; *keywords != NULL; ++i) {
if (!strcmp(s, *keywords++))
return -(i+1);
}
}
/*
* Look for a series of no more than "max_vals"
* numeric values separated by commas, in base10,
* or base16 when prefixed with "0x".
* Return a count of how many were found.
*/
for (n = 0; (i = stridx(decimal, *s)) >= 0;) {
vals[n] = i;
while ((i = stridx(decimal, *++s)) >= 0)
vals[n] = (vals[n] * 10) + i;
if (*s == 'x' && !vals[n]) {
while ((i = stridx(hex, *++s)) >= 0)
vals[n] = (vals[n] * 0x10) + i;
}
if (++n == max_vals)
break;
if (*s == ',')
++s;
}
if (!*s)
return n;
}
return 0; /* zero = nothing matched */
}
 
/*
* ide_setup() gets called VERY EARLY during initialization,
* to handle kernel "command line" strings beginning with "hdx="
* or "ide". Here is the complete set currently supported:
*
* "hdx=" is recognized for all "x" from "a" to "h", such as "hdc".
* "idex=" is recognized for all "x" from "0" to "3", such as "ide1".
*
* "hdx=noprobe" : drive may be present, but do not probe for it
* "hdx=none" : drive is NOT present, ignore cmos and do not probe
* "hdx=nowerr" : ignore the WRERR_STAT bit on this drive
* "hdx=cdrom" : drive is present, and is a cdrom drive
* "hdx=cyl,head,sect" : disk drive is present, with specified geometry
* "hdx=autotune" : driver will attempt to tune interface speed
* to the fastest PIO mode supported,
* if possible for this drive only.
* Not fully supported by all chipset types,
* and quite likely to cause trouble with
* older/odd IDE drives.
*
* "idebus=xx" : inform IDE driver of VESA/PCI bus speed in Mhz,
* where "xx" is between 20 and 66 inclusive,
* used when tuning chipset PIO modes.
* For PCI bus, 25 is correct for a P75 system,
* 30 is correct for P90,P120,P180 systems,
* and 33 is used for P100,P133,P166 systems.
* If in doubt, use idebus=33 for PCI.
* As for VLB, it is safest to not specify it.
*
* "idex=noprobe" : do not attempt to access/use this interface
* "idex=base" : probe for an interface at the addr specified,
* where "base" is usually 0x1f0 or 0x170
* and "ctl" is assumed to be "base"+0x206
* "idex=base,ctl" : specify both base and ctl
* "idex=base,ctl,irq" : specify base, ctl, and irq number
* "idex=autotune" : driver will attempt to tune interface speed
* to the fastest PIO mode supported,
* for all drives on this interface.
* Not fully supported by all chipset types,
* and quite likely to cause trouble with
* older/odd IDE drives.
* "idex=noautotune" : driver will NOT attempt to tune interface speed
* This is the default for most chipsets,
* except the cmd640.
* "idex=serialize" : do not overlap operations on idex and ide(x^1)
*
* The following are valid ONLY on ide0,
* and the defaults for the base,ctl ports must not be altered.
*
* "ide0=dtc2278" : probe/support DTC2278 interface
* "ide0=ht6560b" : probe/support HT6560B interface
* "ide0=cmd640_vlb" : *REQUIRED* for VLB cards with the CMD640 chip
* (not for PCI -- automatically detected)
* "ide0=qd6580" : probe/support qd6580 interface
* "ide0=ali14xx" : probe/support ali14xx chipsets (ALI M1439, M1443, M1445)
* "ide0=umc8672" : probe/support umc8672 chipsets
*/
void ide_setup (char *s)
{
int i, vals[3];
ide_hwif_t *hwif;
ide_drive_t *drive;
unsigned int hw, unit;
const char max_drive = 'a' + ((MAX_HWIFS * MAX_DRIVES) - 1);
const char max_hwif = '0' + (MAX_HWIFS - 1);
 
printk("ide_setup: %s", s);
init_ide_data ();
 
/*
* Look for drive options: "hdx="
*/
if (s[0] == 'h' && s[1] == 'd' && s[2] >= 'a' && s[2] <= max_drive) {
const char *hd_words[] = {"none", "noprobe", "nowerr", "cdrom",
"serialize", "autotune", "noautotune",
"slow", "ide-scsi", NULL};
unit = s[2] - 'a';
hw = unit / MAX_DRIVES;
unit = unit % MAX_DRIVES;
hwif = &ide_hwifs[hw];
drive = &hwif->drives[unit];
switch (match_parm(&s[3], hd_words, vals, 3)) {
case -1: /* "none" */
drive->nobios = 1; /* drop into "noprobe" */
case -2: /* "noprobe" */
drive->noprobe = 1;
goto done;
case -3: /* "nowerr" */
drive->bad_wstat = BAD_R_STAT;
hwif->noprobe = 0;
goto done;
case -4: /* "cdrom" */
drive->present = 1;
drive->media = ide_cdrom;
hwif->noprobe = 0;
goto done;
case -5: /* "serialize" */
printk(" -- USE \"ide%d=serialize\" INSTEAD", hw);
goto do_serialize;
case -6: /* "autotune" */
drive->autotune = 1;
goto done;
case -7: /* "noautotune" */
drive->autotune = 2;
goto done;
case -8: /* "slow" */
drive->slow = 1;
goto done;
case -9: /* "ide-scsi" */
drive->ide_scsi = 1;
goto done;
case 3: /* cyl,head,sect */
drive->media = ide_disk;
drive->cyl = drive->bios_cyl = vals[0];
drive->head = drive->bios_head = vals[1];
drive->sect = drive->bios_sect = vals[2];
drive->present = 1;
drive->forced_geom = 1;
hwif->noprobe = 0;
goto done;
default:
goto bad_option;
}
}
 
if (s[0] != 'i' || s[1] != 'd' || s[2] != 'e')
goto bad_option;
/*
* Look for bus speed option: "idebus="
*/
if (s[3] == 'b' && s[4] == 'u' && s[5] == 's') {
if (match_parm(&s[6], NULL, vals, 1) != 1)
goto bad_option;
if (vals[0] >= 20 && vals[0] <= 66)
idebus_parameter = vals[0];
else
printk(" -- BAD BUS SPEED! Expected value from 20 to 66");
goto done;
}
/*
* Look for interface options: "idex="
*/
if (s[3] >= '0' && s[3] <= max_hwif) {
/*
* Be VERY CAREFUL changing this: note hardcoded indexes below
*/
const char *ide_words[] = {"noprobe", "serialize", "autotune", "noautotune",
"qd6580", "ht6560b", "cmd640_vlb", "dtc2278", "umc8672", "ali14xx", "dc4030", NULL};
hw = s[3] - '0';
hwif = &ide_hwifs[hw];
i = match_parm(&s[4], ide_words, vals, 3);
 
/*
* Cryptic check to ensure chipset not already set for hwif:
*/
if (i > 0 || i <= -5) {
if (hwif->chipset != ide_unknown)
goto bad_option;
if (i <= -5) {
if (ide_hwifs[1].chipset != ide_unknown)
goto bad_option;
/*
* Interface keywords work only for ide0:
*/
if (hw != 0)
goto bad_hwif;
}
}
 
switch (i) {
#ifdef CONFIG_BLK_DEV_PROMISE
case -11: /* "dc4030" */
{
setup_dc4030(hwif);
goto done;
}
#endif /* CONFIG_BLK_DEV_PROMISE */
#ifdef CONFIG_BLK_DEV_ALI14XX
case -10: /* "ali14xx" */
{
extern void init_ali14xx (void);
init_ali14xx();
goto done;
}
#endif /* CONFIG_BLK_DEV_ALI14XX */
#ifdef CONFIG_BLK_DEV_UMC8672
case -9: /* "umc8672" */
{
extern void init_umc8672 (void);
init_umc8672();
goto done;
}
#endif /* CONFIG_BLK_DEV_UMC8672 */
#ifdef CONFIG_BLK_DEV_DTC2278
case -8: /* "dtc2278" */
{
extern void init_dtc2278 (void);
init_dtc2278();
goto done;
}
#endif /* CONFIG_BLK_DEV_DTC2278 */
#ifdef CONFIG_BLK_DEV_CMD640
case -7: /* "cmd640_vlb" */
{
extern int cmd640_vlb; /* flag for cmd640.c */
cmd640_vlb = 1;
goto done;
}
#endif /* CONFIG_BLK_DEV_CMD640 */
#ifdef CONFIG_BLK_DEV_HT6560B
case -6: /* "ht6560b" */
{
extern void init_ht6560b (void);
init_ht6560b();
goto done;
}
#endif /* CONFIG_BLK_DEV_HT6560B */
#if CONFIG_BLK_DEV_QD6580
case -5: /* "qd6580" (has secondary i/f) */
{
extern void init_qd6580 (void);
init_qd6580();
goto done;
}
#endif /* CONFIG_BLK_DEV_QD6580 */
case -4: /* "noautotune" */
hwif->drives[0].autotune = 2;
hwif->drives[1].autotune = 2;
goto done;
case -3: /* "autotune" */
hwif->drives[0].autotune = 1;
hwif->drives[1].autotune = 1;
goto done;
case -2: /* "serialize" */
do_serialize:
ide_hwifs[hw].serialized = 1; /* serialize */
ide_hwifs[hw^1].serialized = 1; /* with mate */
goto done;
 
case -1: /* "noprobe" */
hwif->noprobe = 1;
goto done;
 
case 1: /* base */
vals[1] = vals[0] + 0x206; /* default ctl */
case 2: /* base,ctl */
vals[2] = 0; /* default irq = probe for it */
case 3: /* base,ctl,irq */
hwif->io.io_base = vals[0];
hwif->io.ctl_port = vals[1];
hwif->io.io_shift = 0;
hwif->irq = vals[2];
hwif->noprobe = 0;
hwif->chipset = ide_generic;
goto done;
 
case 0: goto bad_option;
default:
printk(" -- SUPPORT NOT CONFIGURED IN THIS KERNEL\n");
return;
}
}
bad_option:
printk(" -- BAD OPTION\n");
return;
bad_hwif:
printk("-- NOT SUPPORTED ON ide%d", hw);
done:
printk("\n");
}
 
/*
* This routine is called from the partition-table code in genhd.c
* to "convert" a drive to a logical geometry with fewer than 1024 cyls.
*
* The second parameter, "xparm", determines exactly how the translation
* will be handled:
* 0 = convert to CHS with fewer than 1024 cyls
* using the same method as Ontrack DiskManager.
* 1 = same as "0", plus offset everything by 63 sectors.
* -1 = similar to "0", plus redirect sector 0 to sector 1.
* >1 = convert to a CHS geometry with "xparm" heads.
*
* Returns 0 if the translation was not possible, if the device was not
* an IDE disk drive, or if a geometry was "forced" on the commandline.
* Returns 1 if the geometry translation was successful.
*/
int ide_xlate_1024 (kdev_t i_rdev, int xparm, const char *msg)
{
ide_drive_t *drive;
static const byte head_vals[] = {4, 8, 16, 32, 64, 128, 255, 0};
const byte *heads = head_vals;
unsigned long tracks;
 
if ((drive = get_info_ptr(i_rdev)) == NULL || drive->forced_geom)
return 0;
 
if (xparm > 1 && xparm <= drive->bios_head && drive->bios_sect == 63)
return 0; /* we already have a translation */
 
printk("%s ", msg);
 
if (drive->id) {
drive->cyl = drive->id->cyls;
drive->head = drive->id->heads;
drive->sect = drive->id->sectors;
}
drive->bios_cyl = drive->cyl;
drive->bios_head = drive->head;
drive->bios_sect = drive->sect;
drive->special.b.set_geometry = 1;
 
tracks = drive->bios_cyl * drive->bios_head * drive->bios_sect / 63;
drive->bios_sect = 63;
if (xparm > 1) {
drive->bios_head = xparm;
drive->bios_cyl = tracks / drive->bios_head;
} else {
while (drive->bios_cyl >= 1024) {
drive->bios_head = *heads;
drive->bios_cyl = tracks / drive->bios_head;
if (0 == *++heads)
break;
}
#if FAKE_FDISK_FOR_EZDRIVE
if (xparm == -1) {
drive->remap_0_to_1 = 1;
msg = "0->1";
} else
#endif /* FAKE_FDISK_FOR_EZDRIVE */
if (xparm == 1) {
drive->sect0 = 63;
drive->bios_cyl = (tracks - 1) / drive->bios_head;
msg = "+63";
}
printk("[remap %s] ", msg);
}
drive->part[0].nr_sects = current_capacity(drive);
printk("[%d/%d/%d]", drive->bios_cyl, drive->bios_head, drive->bios_sect);
return 1;
}
 
#if MAX_HWIFS > 1
/*
* save_match() is used to simplify logic in init_irq() below.
*
* A loophole here is that we may not know about a particular
* hwif's irq until after that hwif is actually probed/initialized..
* This could be a problem for the case where an hwif is on a
* dual interface that requires serialization (eg. cmd640) and another
* hwif using one of the same irqs is initialized beforehand.
*
* This routine detects and reports such situations, but does not fix them.
*/
static void save_match (ide_hwif_t *hwif, ide_hwif_t *new, ide_hwif_t **match)
{
ide_hwif_t *m = *match;
 
if (m && m->hwgroup && m->hwgroup != new->hwgroup) {
if (!new->hwgroup)
return;
printk("%s: potential irq problem with %s and %s\n", hwif->name, new->name, m->name);
}
if (!m || m->irq != hwif->irq) /* don't undo a prior perfect match */
*match = new;
}
#endif /* MAX_HWIFS > 1 */
 
/*
* This routine sets up the irq for an ide interface, and creates a new
* hwgroup for the irq/hwif if none was previously assigned.
*
* Much of the code is for correctly detecting/handling irq sharing
* and irq serialization situations. This is somewhat complex because
* it handles static as well as dynamic (PCMCIA) IDE interfaces.
*
* The SA_INTERRUPT in sa_flags means ide_intr() is always entered with
* interrupts completely disabled. This can be bad for interrupt latency,
* but anything else has led to problems on some machines. We re-enable
* interrupts as much as we can safely do in most places.
*/
static int init_irq (ide_hwif_t *hwif)
{
unsigned long flags;
#if MAX_HWIFS > 1
unsigned int index;
#endif /* MAX_HWIFS > 1 */
ide_hwgroup_t *hwgroup;
ide_hwif_t *match = NULL;
 
save_flags(flags);
cli();
 
hwif->hwgroup = NULL;
#if MAX_HWIFS > 1
/*
* Group up with any other hwifs that share our irq(s).
*/
for (index = 0; index < MAX_HWIFS; index++) {
ide_hwif_t *h = &ide_hwifs[index];
if (h->hwgroup) { /* scan only initialized hwif's */
if (hwif->irq == h->irq) {
hwif->sharing_irq = h->sharing_irq = 1;
save_match(hwif, h, &match);
}
if (hwif->serialized) {
ide_hwif_t *mate = &ide_hwifs[hwif->index^1];
if (index == mate->index || h->irq == mate->irq)
save_match(hwif, h, &match);
}
if (h->serialized) {
ide_hwif_t *mate = &ide_hwifs[h->index^1];
if (hwif->irq == mate->irq)
save_match(hwif, h, &match);
}
}
}
#endif /* MAX_HWIFS > 1 */
/*
* If we are still without a hwgroup, then form a new one
*/
if (match) {
hwgroup = match->hwgroup;
} else {
hwgroup = kmalloc(sizeof(ide_hwgroup_t), GFP_KERNEL);
hwgroup->hwif = hwgroup->next_hwif = hwif->next = hwif;
hwgroup->rq = NULL;
hwgroup->handler = NULL;
if (hwif->drives[0].present)
hwgroup->drive = &hwif->drives[0];
else
hwgroup->drive = &hwif->drives[1];
hwgroup->poll_timeout = 0;
hwgroup->active = 0;
init_timer(&hwgroup->timer);
hwgroup->timer.function = &timer_expiry;
hwgroup->timer.data = (unsigned long) hwgroup;
}
 
/*
* Allocate the irq, if not already obtained for another hwif
*/
if (!match || match->irq != hwif->irq) {
if (request_irq(hwif->irq, ide_intr, SA_INTERRUPT, hwif->name, hwgroup)) {
if (!match)
kfree(hwgroup);
restore_flags(flags);
return 1;
}
}
 
/*
* Everything is okay, so link us into the hwgroup
*/
hwif->hwgroup = hwgroup;
hwif->next = hwgroup->hwif->next;
hwgroup->hwif->next = hwif;
 
restore_flags(flags); /* safe now that hwif->hwgroup is set up */
 
printk("%s at 0x%03lx-0x%03lx,0x%03lx on irq %d", hwif->name,
hwif->io.io_base, hwif->io.io_base+7, hwif->io.ctl_port, hwif->irq);
if (match)
printk(" (%sed with %s)", hwif->sharing_irq ? "shar" : "serializ", match->name);
printk("\n");
return 0;
}
 
static struct file_operations ide_fops = {
NULL, /* lseek - default */
block_read, /* read - general block-dev read */
block_write, /* write - general block-dev write */
NULL, /* readdir - bad */
NULL, /* select */
ide_ioctl, /* ioctl */
NULL, /* mmap */
ide_open, /* open */
ide_release, /* release */
block_fsync /* fsync */
,NULL, /* fasync */
ide_check_media_change, /* check_media_change */
revalidate_disk /* revalidate */
};
 
#ifdef CONFIG_PCI
#if defined(CONFIG_BLK_DEV_RZ1000) || defined(CONFIG_BLK_DEV_TRITON)
 
typedef void (ide_pci_init_proc_t)(byte, byte);
 
/*
* ide_probe_pci() scans PCI for a specific vendor/device function,
* and invokes the supplied init routine for each instance detected.
*/
static void ide_probe_pci (unsigned short vendor, unsigned short device, ide_pci_init_proc_t *init, int func_adj)
{
unsigned long flags;
unsigned index;
byte fn, bus;
 
save_flags(flags);
cli();
for (index = 0; !pcibios_find_device (vendor, device, index, &bus, &fn); ++index) {
init (bus, fn + func_adj);
}
restore_flags(flags);
}
 
#endif /* defined(CONFIG_BLK_DEV_RZ1000) || defined(CONFIG_BLK_DEV_TRITON) */
 
static void ide_probe_promise_20246(void)
{
byte fn, bus;
unsigned short io[6], count = 0;
unsigned int reg, tmp, i;
ide_hwif_t *hwif;
 
memset(io, 0, 6 * sizeof(unsigned short));
if (pcibios_find_device(PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20246, 0, &bus, &fn))
return;
printk("ide: Promise Technology IDE Ultra-DMA 33 on PCI bus %d function %d\n", bus, fn);
for (reg = PCI_BASE_ADDRESS_0; reg <= PCI_BASE_ADDRESS_5; reg += 4) {
pcibios_read_config_dword(bus, fn, reg, &tmp);
if (tmp & PCI_BASE_ADDRESS_SPACE_IO)
io[count++] = tmp & PCI_BASE_ADDRESS_IO_MASK;
}
for (i = 2; i < 4; i++) {
hwif = ide_hwifs + i;
if (hwif->chipset == ide_generic) {
printk("ide%d: overridden with command line parameter\n", i);
return;
}
tmp = (i - 2) * 2;
if (!io[tmp] || !io[tmp + 1]) {
printk("ide%d: invalid port address %x, %x -- aborting\n", i, io[tmp], io[tmp + 1]);
return;
}
hwif->io_base = io[tmp];
hwif->ctl_port = io[tmp + 1] + 2;
hwif->noprobe = 0;
}
#ifdef CONFIG_BLK_DEV_TRITON
ide_init_promise (bus, fn, &ide_hwifs[2], &ide_hwifs[3], io[4]);
#endif
}
 
#endif /* CONFIG_PCI */
 
/*
* ide_init_pci() finds/initializes "known" PCI IDE interfaces
*
* This routine should ideally be using pcibios_find_class() to find
* all IDE interfaces, but that function causes some systems to "go weird".
*/
static void probe_for_hwifs (void)
{
#ifdef CONFIG_PCI
/*
* Find/initialize PCI IDE interfaces
*/
if (pcibios_present()) {
#ifdef CONFIG_BLK_DEV_RZ1000
ide_pci_init_proc_t init_rz1000;
ide_probe_pci (PCI_VENDOR_ID_PCTECH, PCI_DEVICE_ID_PCTECH_RZ1000, &init_rz1000, 0);
ide_probe_pci (PCI_VENDOR_ID_PCTECH, PCI_DEVICE_ID_PCTECH_RZ1001, &init_rz1000, 0);
#endif /* CONFIG_BLK_DEV_RZ1000 */
#ifdef CONFIG_BLK_DEV_TRITON
/*
* Apparently the BIOS32 services on Intel motherboards are
* buggy and won't find the PCI_DEVICE_ID_INTEL_82371_1 for us.
* So instead, we search for PCI_DEVICE_ID_INTEL_82371_0,
* and then add 1.
*/
ide_probe_pci (PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371_0, &ide_init_triton, 1);
ide_probe_pci (PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371SB_1, &ide_init_triton, 0);
ide_probe_pci (PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB, &ide_init_triton, 0);
#endif /* CONFIG_BLK_DEV_TRITON */
ide_probe_promise_20246();
}
#endif /* CONFIG_PCI */
#ifdef CONFIG_BLK_DEV_CMD640
{
extern void ide_probe_for_cmd640x (void);
ide_probe_for_cmd640x();
}
#endif
#ifdef CONFIG_BLK_DEV_PROMISE
init_dc4030();
#endif
#ifdef CONFIG_BLK_DEV_IDE_ICSIDE
icside_init ();
#endif
#ifdef CONFIG_BLK_DEV_IDE_RAPIDE
rapide_init ();
#endif
}
 
static int hwif_init (int h)
{
ide_hwif_t *hwif = &ide_hwifs[h];
void (*rfn)(void);
if (!hwif->present)
return 0;
if (!hwif->irq) {
if (!(hwif->irq = default_irqs[h])) {
printk("%s: DISABLED, NO IRQ\n", hwif->name);
return (hwif->present = 0);
}
}
#ifdef CONFIG_BLK_DEV_HD
if (hwif->irq == HD_IRQ && hwif->io_base != HD_DATA) {
printk("%s: CANNOT SHARE IRQ WITH OLD HARDDISK DRIVER (hd.c)\n", hwif->name);
return (hwif->present = 0);
}
#endif /* CONFIG_BLK_DEV_HD */
hwif->present = 0; /* we set it back to 1 if all is ok below */
switch (hwif->major) {
case IDE0_MAJOR: rfn = &do_ide0_request; break;
#if MAX_HWIFS > 1
case IDE1_MAJOR: rfn = &do_ide1_request; break;
#endif
#if MAX_HWIFS > 2
case IDE2_MAJOR: rfn = &do_ide2_request; break;
#endif
#if MAX_HWIFS > 3
case IDE3_MAJOR: rfn = &do_ide3_request; break;
#endif
default:
printk("%s: request_fn NOT DEFINED\n", hwif->name);
return (hwif->present = 0);
}
if (register_blkdev (hwif->major, hwif->name, &ide_fops)) {
printk("%s: UNABLE TO GET MAJOR NUMBER %d\n", hwif->name, hwif->major);
} else if (init_irq (hwif)) {
printk("%s: UNABLE TO GET IRQ %d\n", hwif->name, hwif->irq);
(void) unregister_blkdev (hwif->major, hwif->name);
} else {
init_gendisk(hwif);
blk_dev[hwif->major].request_fn = rfn;
read_ahead[hwif->major] = 8; /* (4kB) */
hwif->present = 1; /* success */
}
return hwif->present;
}
 
/*
* A variable so that we know if we're adding drives at boot time,
* so we don't do a partition check on it.
*/
static int ide_at_boot;
/*
* This is gets invoked once during initialization, to set *everything* up
*/
int ide_init (void)
{
int index;
 
ide_at_boot = 1;
 
init_ide_data ();
/*
* Probe for special "known" interface chipsets
*/
probe_for_hwifs ();
 
/*
* Probe for drives in the usual way.. CMOS/BIOS, then poke at ports
*/
for (index = 0; index < MAX_HWIFS; ++index)
probe_hwif (&ide_hwifs[index]);
for (index = 0; index < MAX_HWIFS; ++index)
hwif_init (index);
 
#ifdef CONFIG_BLK_DEV_IDETAPE
idetape_register_chrdev(); /* Register character device interface to the ide tape */
#endif /* CONFIG_BLK_DEV_IDETAPE */
 
ide_at_boot = 0;
 
return 0;
}
 
#ifdef CONFIG_BLK_DEV_IDE_CARDS
int ide_register_port (int io_base, int ctl_port, int stepping, int irq)
{
int index, i, rc = -1;
ide_hwif_t *hwif;
ide_drive_t *drive;
unsigned long flags;
 
save_flags(flags);
cli();
for (index = 0; index < MAX_HWIFS; ++index) {
hwif = &ide_hwifs[index];
if (hwif->present) {
if (hwif->io.io_base == io_base || hwif->io.ctl_port == ctl_port)
break; /* this ide port already exists */
} else {
if (ide_at_boot) {
if (hwif->io.io_base == 0) {
hwif->io.io_base = io_base;
hwif->io.ctl_port = ctl_port;
hwif->io.io_shift = stepping;
hwif->irq = irq;
hwif->noprobe = 0;
rc = index;
break;
}
} else {
hwif->io.io_base = io_base;
hwif->io.ctl_port = ctl_port;
hwif->io.io_shift = stepping;
hwif->irq = irq;
hwif->noprobe = 0;
probe_hwif(hwif);
if (!hwif_init(index))
break;
for (i = 0; i < hwif->gd->nr_real; i++) {
drive = &hwif->drives[i];
revalidate_disk(MKDEV(hwif->major, i<<PARTN_BITS));
#ifdef CONFIG_BLK_DEV_IDECD
if (drive->present && drive->media == ide_cdrom)
ide_cdrom_setup(drive);
#endif /* CONFIG_BLK_DEV_IDECD */
}
rc = index;
break;
}
}
}
restore_flags(flags);
return rc;
}
#endif
 
#ifdef CONFIG_BLK_DEV_IDE_PCMCIA
int ide_register(int io_base, int ctl_port, int irq)
{
int index, i, rc = -1;
ide_hwif_t *hwif;
ide_drive_t *drive;
unsigned long flags;
 
save_flags(flags);
cli();
for (index = 0; index < MAX_HWIFS; ++index) {
hwif = &ide_hwifs[index];
if (hwif->present) {
if (hwif->io.io_base == io_base || hwif->io.ctl_port == ctl_port)
break; /* this ide port already exists */
} else {
hwif->io.io_base = io_base;
hwif->io.ctl_port = ctl_port;
hwif->io.io_shift = 0;
hwif->irq = irq;
hwif->noprobe = 0;
probe_hwif(hwif);
if (!hwif_init(index))
break;
for (i = 0; i < hwif->gd->nr_real; i++) {
drive = &hwif->drives[i];
revalidate_disk(MKDEV(hwif->major, i<<PARTN_BITS));
#ifdef CONFIG_BLK_DEV_IDECD
if (drive->present && drive->media == ide_cdrom)
ide_cdrom_setup(drive);
#endif /* CONFIG_BLK_DEV_IDECD */
}
rc = index;
break;
}
}
restore_flags(flags);
return rc;
}
#endif
 
#if defined(CONFIG_BLK_DEV_IDE_PCMCIA) || defined(CONFIG_BLK_DEV_IDE_CARDS)
void ide_unregister (unsigned int index)
{
struct gendisk *gd, **gdp;
ide_hwif_t *hwif, *g;
ide_hwgroup_t *hwgroup;
int irq_count = 0;
unsigned long flags;
 
if (index >= MAX_HWIFS)
return;
save_flags(flags);
cli();
hwif = &ide_hwifs[index];
if (!hwif->present || hwif->drives[0].busy || hwif->drives[1].busy) {
restore_flags(flags);
return;
}
hwif->present = 0;
hwgroup = hwif->hwgroup;
 
/*
* free the irq if we were the only hwif using it
*/
g = hwgroup->hwif;
do {
if (g->irq == hwif->irq)
++irq_count;
g = g->next;
} while (g != hwgroup->hwif);
if (irq_count == 1)
free_irq(hwif->irq, hwgroup);
 
/*
* Note that we only release the standard ports,
* and do not even try to handle any extra ports
* allocated for weird IDE interface chipsets.
*/
release_region(hwif->io.io_base, 8 << hwif->io.io_shift);
release_region(hwif->io.ctl_port, 1);
 
/*
* Remove us from the hwgroup, and free
* the hwgroup if we were the only member
*/
while (hwgroup->hwif->next != hwif)
hwgroup->hwif = hwgroup->hwif->next;
hwgroup->hwif->next = hwif->next;
if (hwgroup->hwif == hwif)
hwgroup->hwif = hwif->next;
if (hwgroup->next_hwif == hwif)
hwgroup->next_hwif = hwif->next;
if (hwgroup->hwif == hwif)
kfree(hwgroup);
 
/*
* Remove us from the kernel's knowledge
*/
unregister_blkdev(hwif->major, hwif->name);
kfree(blksize_size[hwif->major]);
blk_dev[hwif->major].request_fn = NULL;
blksize_size[hwif->major] = NULL;
for (gdp = &gendisk_head; *gdp; gdp = &((*gdp)->next))
if (*gdp == hwif->gd)
break;
if (*gdp == NULL)
printk("gd not in disk chain!\n");
else {
gd = *gdp; *gdp = gd->next;
kfree(gd->sizes);
kfree(gd->part);
kfree(gd);
}
init_hwif_data (index); /* restore hwif data to pristine status */
restore_flags(flags);
}
#endif /* CONFIG_BLK_DEV_IDE_PCMCIA */
/ide-rapide.c
0,0 → 1,78
/*
* linux/arch/arm/drivers/block/ide-ics.c
*
* Copyright (c) 1996 Russell King.
*
* Changelog:
* 08-06-1996 RMK Created
*/
 
#include <linux/module.h>
#include <linux/malloc.h>
#include <linux/blkdev.h>
#include <linux/errno.h>
#include <asm/ecard.h>
 
#include "ide.h"
 
static const card_ids rapide_cids[] = {
{ 0xffff, 0xffff }
};
 
static struct expansion_card *ec[MAX_ECARDS];
static int result[MAX_ECARDS];
 
static inline int rapide_register (struct expansion_card *ec)
{
unsigned long port = ecard_address (ec, ECARD_MEMC, 0);
 
return ide_register_port (port, port + 0x206, 4, ec->irq);
}
 
int rapide_init (void)
{
int i;
 
for (i = 0; i < MAX_ECARDS; i++)
ec[i] = NULL;
 
ecard_startfind ();
 
for (i = 0; ; i++) {
if ((ec[i] = ecard_find (0, rapide_cids)) == NULL)
break;
 
ecard_claim (ec[i]);
result[i] = rapide_register (ec[i]);
}
for (i = 0; i < MAX_ECARDS; i++)
if (ec[i] && result[i] < 0) {
ecard_release (ec[i]);
ec[i] = NULL;
}
return 0;
}
 
#ifdef MODULE
 
int init_module (void)
{
return rapide_init();
}
 
void cleanup_module (void)
{
int i;
 
for (i = 0; i < MAX_ECARDS; i++)
if (ec[i]) {
unsigned long port;
port = ecard_address (ec[i], ECARD_MEMC, 0);
 
ide_unregister_port (port, ec[i]->irq, 16);
ecard_release (ec[i]);
ec[i] = NULL;
}
}
#endif
 
/rd.c
0,0 → 1,708
/*
* ramdisk.c - Multiple ramdisk driver - gzip-loading version - v. 0.8 beta.
*
* (C) Chad Page, Theodore Ts'o, et. al, 1995.
*
* This ramdisk is designed to have filesystems created on it and mounted
* just like a regular floppy disk.
*
* It also does something suggested by Linus: use the buffer cache as the
* ramdisk data. This makes it possible to dynamically allocate the ramdisk
* buffer - with some consequences I have to deal with as I write this.
*
* This code is based on the original ramdisk.c, written mostly by
* Theodore Ts'o (TYT) in 1991. The code was largely rewritten by
* Chad Page to use the buffer cache to store the ramdisk data in
* 1995; Theodore then took over the driver again, and cleaned it up
* for inclusion in the mainline kernel.
*
* The original CRAMDISK code was written by Richard Lyons, and
* adapted by Chad Page to use the new ramdisk interface. Theodore
* Ts'o rewrote it so that both the compressed ramdisk loader and the
* kernel decompressor uses the same inflate.c codebase. The ramdisk
* loader now also loads into a dynamic (buffer cache based) ramdisk,
* not the old static ramdisk. Support for the old static ramdisk has
* been completely removed.
*
* Loadable module support added by Tom Dyas.
*
* Further cleanups by Chad Page (page0588@sundance.sjsu.edu):
* Cosmetic changes in #ifdef MODULE, code movement, etc...
* When the ramdisk is rmmod'ed, free the protected buffers
* Default ramdisk size changed to 2.88MB
*
* Added initrd: Werner Almesberger & Hans Lermen, Feb '96
*
* 4/25/96 : Made ramdisk size a parameter (default is now 4MB)
* - Chad Page
*/
 
#include <linux/config.h>
#include <linux/sched.h>
#include <linux/minix_fs.h>
#include <linux/ext2_fs.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/mm.h>
#include <linux/mman.h>
#include <linux/malloc.h>
#include <linux/ioctl.h>
#include <linux/fd.h>
#include <linux/module.h>
 
#include <asm/system.h>
#include <asm/segment.h>
 
extern void wait_for_keypress(void);
 
/*
* 35 has been officially registered as the RAMDISK major number, but
* so is the original MAJOR number of 1. We're using 1 in
* include/linux/major.h for now
*/
#define MAJOR_NR RAMDISK_MAJOR
#include <linux/blk.h>
 
/* The ramdisk size is now a parameter */
#define NUM_RAMDISKS 16 /* This cannot be overridden (yet) */
 
#ifndef MODULE
/* We don't have to load ramdisks or gunzip them in a module... */
#define RD_LOADER
#define BUILD_CRAMDISK
 
void rd_load(void);
static int crd_load(struct file *fp, struct file *outfp);
 
#ifdef CONFIG_BLK_DEV_INITRD
static int initrd_users = 0;
#endif
#endif
 
/* Various static variables go here... mostly used within the ramdisk code only. */
 
static int rd_length[NUM_RAMDISKS];
static int rd_blocksizes[NUM_RAMDISKS];
 
/*
* Parameters for the boot-loading of the ramdisk. These are set by
* init/main.c (from arguments to the kernel command line) or from the
* architecture-specific setup routine (from the stored bootsector
* information).
*/
int rd_size = 4096; /* Size of the ramdisks */
 
#ifndef MODULE
int rd_doload = 0; /* 1 = load ramdisk, 0 = don't load */
int rd_prompt = 1; /* 1 = prompt for ramdisk, 0 = don't prompt */
int rd_image_start = 0; /* starting block # of image */
#ifdef CONFIG_BLK_DEV_INITRD
unsigned long initrd_start,initrd_end;
int mount_initrd = 1; /* zero if initrd should not be mounted */
#endif
#endif
 
/*
* Basically, my strategy here is to set up a buffer-head which can't be
* deleted, and make that my Ramdisk. If the request is outside of the
* allocated size, we must get rid of it...
*
*/
static void rd_request(void)
{
unsigned int minor;
int offset, len;
 
repeat:
INIT_REQUEST;
minor = MINOR(CURRENT->rq_dev);
 
if (minor >= NUM_RAMDISKS) {
end_request(0);
goto repeat;
}
offset = CURRENT->sector << 9;
len = CURRENT->current_nr_sectors << 9;
 
if ((offset + len) > rd_length[minor]) {
end_request(0);
goto repeat;
}
 
/*
* If we're reading, fill the buffer with 0's. This is okay since
* we're using protected buffers which should never get freed...
*
* If we're writing, we protect the buffer.
*/
 
if (CURRENT->cmd == READ)
memset(CURRENT->buffer, 0, len);
else
set_bit(BH_Protected, &CURRENT->bh->b_state);
 
end_request(1);
goto repeat;
}
 
static int rd_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{
int err;
if (!inode || !inode->i_rdev)
return -EINVAL;
 
switch (cmd) {
case BLKFLSBUF:
if (!suser()) return -EACCES;
invalidate_buffers(inode->i_rdev);
break;
case BLKGETSIZE: /* Return device size */
if (!arg) return -EINVAL;
err = verify_area(VERIFY_WRITE, (long *) arg,
sizeof(long));
if (err)
return err;
put_user(rd_length[MINOR(inode->i_rdev)] / 512,
(long *) arg);
return 0;
default:
break;
};
 
return 0;
}
 
 
#ifdef CONFIG_BLK_DEV_INITRD
 
static int initrd_read(struct inode *inode,struct file *file,char *buf,
int count)
{
int left;
 
left = initrd_end-initrd_start-file->f_pos;
if (count > left) count = left;
if (count <= 0) return 0;
memcpy_tofs(buf,(char *) initrd_start+file->f_pos,count);
file->f_pos += count;
return count;
}
 
 
static void initrd_release(struct inode *inode,struct file *file)
{
unsigned long i;
 
if (--initrd_users) return;
for (i = initrd_start; i < initrd_end; i += PAGE_SIZE)
free_page(i);
initrd_start = 0;
}
 
 
static struct file_operations initrd_fops = {
NULL, /* lseek */
initrd_read, /* read */
NULL, /* write */
NULL, /* readdir */
NULL, /* select */
NULL, /* ioctl */
NULL, /* mmap */
NULL, /* open */
initrd_release, /* release */
NULL /* fsync */
};
 
#endif
 
 
static int rd_open(struct inode * inode, struct file * filp)
{
#ifdef CONFIG_BLK_DEV_INITRD
if (DEVICE_NR(inode->i_rdev) == INITRD_MINOR) {
if (!initrd_start) return -ENODEV;
initrd_users++;
filp->f_op = &initrd_fops;
return 0;
}
#endif
 
if (DEVICE_NR(inode->i_rdev) >= NUM_RAMDISKS)
return -ENXIO;
 
MOD_INC_USE_COUNT;
 
return 0;
}
 
#ifdef MODULE
static void rd_release(struct inode * inode, struct file * filp)
{
MOD_DEC_USE_COUNT;
}
#endif
 
static struct file_operations fd_fops = {
NULL, /* lseek - default */
block_read, /* read - block dev read */
block_write, /* write - block dev write */
NULL, /* readdir - not here! */
NULL, /* select */
rd_ioctl, /* ioctl */
NULL, /* mmap */
rd_open, /* open */
#ifndef MODULE
NULL, /* no special release code... */
#else
rd_release, /* module needs to decrement use count */
#endif
block_fsync /* fsync */
};
 
/* This is the registration and initialization section of the ramdisk driver */
int rd_init(void)
{
int i;
 
if (register_blkdev(MAJOR_NR, "ramdisk", &fd_fops)) {
printk("RAMDISK: Could not get major %d", MAJOR_NR);
return -EIO;
}
 
blk_dev[MAJOR_NR].request_fn = &rd_request;
 
for (i = 0; i < NUM_RAMDISKS; i++) {
rd_length[i] = (rd_size * 1024);
rd_blocksizes[i] = 1024;
}
 
blksize_size[MAJOR_NR] = rd_blocksizes;
 
printk("Ramdisk driver initialized : %d ramdisks of %dK size\n",
NUM_RAMDISKS, rd_size);
 
return 0;
}
 
/* loadable module support */
 
#ifdef MODULE
 
int init_module(void)
{
int error = rd_init();
if (!error)
printk(KERN_INFO "RAMDISK: Loaded as module.\n");
return error;
}
 
/* Before freeing the module, invalidate all of the protected buffers! */
void cleanup_module(void)
{
int i;
 
for (i = 0 ; i < NUM_RAMDISKS; i++)
invalidate_buffers(MKDEV(MAJOR_NR, i));
 
unregister_blkdev( MAJOR_NR, "ramdisk" );
blk_dev[MAJOR_NR].request_fn = 0;
}
 
#endif /* MODULE */
 
/* End of non-loading portions of the ramdisk driver */
 
#ifdef RD_LOADER
/*
* This routine tries to a ramdisk image to load, and returns the
* number of blocks to read for a non-compressed image, 0 if the image
* is a compressed image, and -1 if an image with the right magic
* numbers could not be found.
*
* We currently check for the following magic numbers:
* minix
* ext2
* gzip
*/
int
identify_ramdisk_image(kdev_t device, struct file *fp, int start_block)
{
const int size = 512;
struct minix_super_block *minixsb;
struct ext2_super_block *ext2sb;
int nblocks = -1;
int max_blocks;
unsigned char *buf;
 
buf = kmalloc(size, GFP_KERNEL);
if (buf == 0)
return -1;
 
minixsb = (struct minix_super_block *) buf;
ext2sb = (struct ext2_super_block *) buf;
memset(buf, 0xe5, size);
 
/*
* Read block 0 to test for gzipped kernel
*/
if (fp->f_op->lseek)
fp->f_op->lseek(fp->f_inode, fp, start_block * BLOCK_SIZE, 0);
fp->f_pos = start_block * BLOCK_SIZE;
fp->f_op->read(fp->f_inode, fp, buf, size);
 
/*
* If it matches the gzip magic numbers, return -1
*/
if (buf[0] == 037 && ((buf[1] == 0213) || (buf[1] == 0236))) {
printk(KERN_NOTICE
"RAMDISK: Compressed image found at block %d\n",
start_block);
nblocks = 0;
goto done;
}
 
/*
* Read block 1 to test for minix and ext2 superblock
*/
if (fp->f_op->lseek)
fp->f_op->lseek(fp->f_inode, fp,
(start_block+1) * BLOCK_SIZE, 0);
fp->f_pos = (start_block+1) * BLOCK_SIZE;
 
fp->f_op->read(fp->f_inode, fp, buf, size);
/* Try minix */
if (minixsb->s_magic == MINIX_SUPER_MAGIC ||
minixsb->s_magic == MINIX_SUPER_MAGIC2) {
printk(KERN_NOTICE
"RAMDISK: Minix filesystem found at block %d\n",
start_block);
nblocks = minixsb->s_nzones << minixsb->s_log_zone_size;
goto done;
}
 
/* Try ext2 */
if (ext2sb->s_magic == EXT2_SUPER_MAGIC) {
printk(KERN_NOTICE
"RAMDISK: Ext2 filesystem found at block %d\n",
start_block);
nblocks = ext2sb->s_blocks_count;
goto done;
}
printk(KERN_NOTICE
"RAMDISK: Couldn't find valid ramdisk image starting at %d.\n",
start_block);
done:
if (fp->f_op->lseek)
fp->f_op->lseek(fp->f_inode, fp, start_block * BLOCK_SIZE, 0);
fp->f_pos = start_block * BLOCK_SIZE;
 
if ((nblocks > 0) && blk_size[MAJOR(device)]) {
max_blocks = blk_size[MAJOR(device)][MINOR(device)];
max_blocks -= start_block;
if (nblocks > max_blocks) {
printk(KERN_NOTICE
"RAMDISK: Restricting filesystem size "
"from %d to %d blocks.\n",
nblocks, max_blocks);
nblocks = max_blocks;
}
}
kfree(buf);
return nblocks;
}
 
/*
* This routine loads in the ramdisk image.
*/
static void rd_load_image(kdev_t device,int offset, int unit)
{
struct inode inode, out_inode;
struct file infile, outfile;
unsigned short fs;
kdev_t ram_device;
int nblocks, i;
char *buf;
unsigned short rotate = 0;
char rotator[4] = { '|' , '/' , '-' , '\\' };
 
ram_device = MKDEV(MAJOR_NR, unit);
 
memset(&infile, 0, sizeof(infile));
memset(&inode, 0, sizeof(inode));
inode.i_rdev = device;
infile.f_mode = 1; /* read only */
infile.f_inode = &inode;
 
memset(&outfile, 0, sizeof(outfile));
memset(&out_inode, 0, sizeof(out_inode));
out_inode.i_rdev = ram_device;
outfile.f_mode = 3; /* read/write */
outfile.f_inode = &out_inode;
 
if (blkdev_open(&inode, &infile) != 0) return;
if (blkdev_open(&out_inode, &outfile) != 0) return;
 
fs = get_fs();
set_fs(KERNEL_DS);
nblocks = identify_ramdisk_image(device, &infile, offset);
if (nblocks < 0)
goto done;
 
if (nblocks == 0) {
#ifdef BUILD_CRAMDISK
if (crd_load(&infile, &outfile) == 0)
goto successful_load;
#else
printk(KERN_NOTICE
"RAMDISK: Kernel does not support compressed "
"ramdisk images\n");
#endif
goto done;
}
 
if (nblocks > (rd_length[0] >> BLOCK_SIZE_BITS)) {
printk("RAMDISK: image too big! (%d/%d blocks)\n",
nblocks, rd_length[0] >> BLOCK_SIZE_BITS);
goto done;
}
/*
* OK, time to copy in the data
*/
buf = kmalloc(BLOCK_SIZE, GFP_KERNEL);
if (buf == 0) {
printk(KERN_ERR "RAMDISK: could not allocate buffer\n");
goto done;
}
 
printk(KERN_NOTICE "RAMDISK: Loading %d blocks into ram disk... ", nblocks);
for (i=0; i < nblocks; i++) {
infile.f_op->read(infile.f_inode, &infile, buf,
BLOCK_SIZE);
outfile.f_op->write(outfile.f_inode, &outfile, buf,
BLOCK_SIZE);
if (!(i % 16)) {
printk("%c\b", rotator[rotate & 0x3]);
rotate++;
}
}
printk("done.\n");
kfree(buf);
 
successful_load:
invalidate_buffers(device);
ROOT_DEV = MKDEV(MAJOR_NR,unit);
 
done:
if (infile.f_op->release)
infile.f_op->release(&inode, &infile);
set_fs(fs);
}
 
 
static void rd_load_disk(int n)
{
#ifdef CONFIG_BLK_DEV_INITRD
extern kdev_t real_root_dev;
#endif
if (rd_doload == 0)
return;
if (MAJOR(ROOT_DEV) != FLOPPY_MAJOR
#ifdef CONFIG_BLK_DEV_INITRD
&& MAJOR(real_root_dev) != FLOPPY_MAJOR
#endif
)
return;
 
if (rd_prompt) {
#ifdef CONFIG_BLK_DEV_FD
floppy_eject();
#endif
printk(KERN_NOTICE
"VFS: Insert root floppy disk to be loaded into ramdisk and press ENTER\n");
wait_for_keypress();
}
 
rd_load_image(ROOT_DEV,rd_image_start,n);
 
}
 
void rd_load(void)
{
rd_load_disk(0);
}
 
void rd_load_secondary(void)
{
rd_load_disk(1);
}
 
#ifdef CONFIG_BLK_DEV_INITRD
void initrd_load(void)
{
rd_load_image(MKDEV(MAJOR_NR, INITRD_MINOR),0,0);
}
#endif
 
#endif /* RD_LOADER */
 
#ifdef BUILD_CRAMDISK
 
/*
* gzip declarations
*/
 
#define OF(args) args
 
#define memzero(s, n) memset ((s), 0, (n))
 
 
typedef unsigned char uch;
typedef unsigned short ush;
typedef unsigned long ulg;
 
#define INBUFSIZ 4096
#define WSIZE 0x8000 /* window size--must be a power of two, and */
/* at least 32K for zip's deflate method */
 
static uch *inbuf;
static uch *window;
 
static unsigned insize = 0; /* valid bytes in inbuf */
static unsigned inptr = 0; /* index of next byte to be processed in inbuf */
static unsigned outcnt = 0; /* bytes in output buffer */
static int exit_code = 0;
static long bytes_out = 0;
static struct file *crd_infp, *crd_outfp;
 
#define get_byte() (inptr < insize ? inbuf[inptr++] : fill_inbuf())
/* Diagnostic functions (stubbed out) */
#define Assert(cond,msg)
#define Trace(x)
#define Tracev(x)
#define Tracevv(x)
#define Tracec(c,x)
#define Tracecv(c,x)
 
#define STATIC static
 
static int fill_inbuf(void);
static void flush_window(void);
static void *malloc(int size);
static void free(void *where);
static void error(char *m);
static void gzip_mark(void **);
static void gzip_release(void **);
 
#include "../../../../lib/inflate.c"
 
static void *malloc(int size)
{
return kmalloc(size, GFP_KERNEL);
}
 
static void free(void *where)
{
kfree(where);
}
 
static void gzip_mark(void **ptr)
{
}
 
static void gzip_release(void **ptr)
{
}
 
 
/* ===========================================================================
* Fill the input buffer. This is called only when the buffer is empty
* and at least one byte is really needed.
*/
static int fill_inbuf()
{
if (exit_code) return -1;
insize = crd_infp->f_op->read(crd_infp->f_inode, crd_infp,
inbuf, INBUFSIZ);
if (insize == 0) return -1;
 
inptr = 1;
 
return inbuf[0];
}
 
/* ===========================================================================
* Write the output window window[0..outcnt-1] and update crc and bytes_out.
* (Used for the decompressed data only.)
*/
static void flush_window()
{
ulg c = crc; /* temporary variable */
unsigned n;
uch *in, ch;
crd_outfp->f_op->write(crd_outfp->f_inode, crd_outfp, window,
outcnt);
in = window;
for (n = 0; n < outcnt; n++) {
ch = *in++;
c = crc_32_tab[((int)c ^ ch) & 0xff] ^ (c >> 8);
}
crc = c;
bytes_out += (ulg)outcnt;
outcnt = 0;
}
 
static void error(char *x)
{
printk(KERN_ERR "%s", x);
exit_code = 1;
}
 
static int
crd_load(struct file * fp, struct file *outfp)
{
int result;
 
insize = 0; /* valid bytes in inbuf */
inptr = 0; /* index of next byte to be processed in inbuf */
outcnt = 0; /* bytes in output buffer */
exit_code = 0;
bytes_out = 0;
crc = 0xFFFFFFFF;
crd_infp = fp;
crd_outfp = outfp;
inbuf = kmalloc(INBUFSIZ, GFP_KERNEL);
if (inbuf == 0) {
printk(KERN_ERR "RAMDISK: Couldn't allocate gzip buffer\n");
return -1;
}
window = kmalloc(WSIZE, GFP_KERNEL);
if (window == 0) {
printk(KERN_ERR "RAMDISK: Couldn't allocate gzip window\n");
kfree(inbuf);
return -1;
}
makecrc();
result = gunzip();
kfree(inbuf);
kfree(window);
return result;
}
 
#endif /* BUILD_CRAMDISK */
 
/ide.h
0,0 → 1,761
/*
* linux/drivers/block/ide.h
*
* Copyright (C) 1994, 1995 Linus Torvalds & authors
*/
 
#include <linux/config.h>
 
/*
* This is the multiple IDE interface driver, as evolved from hd.c.
* It supports up to four IDE interfaces, on one or more IRQs (usually 14 & 15).
* There can be up to two drives per interface, as per the ATA-2 spec.
*
* Primary i/f: ide0: major=3; (hda) minor=0; (hdb) minor=64
* Secondary i/f: ide1: major=22; (hdc or hd1a) minor=0; (hdd or hd1b) minor=64
* Tertiary i/f: ide2: major=33; (hde) minor=0; (hdf) minor=64
* Quaternary i/f: ide3: major=34; (hdg) minor=0; (hdh) minor=64
*/
 
/******************************************************************************
* IDE driver configuration options (play with these as desired):
*
* REALLY_SLOW_IO can be defined in ide.c and ide-cd.c, if necessary
*/
#undef REALLY_FAST_IO /* define if ide ports are perfect */
#define INITIAL_MULT_COUNT 0 /* off=0; on=2,4,8,16,32, etc.. */
 
#ifndef SUPPORT_SLOW_DATA_PORTS /* 1 to support slow data ports */
#define SUPPORT_SLOW_DATA_PORTS 0 /* 0 to reduce kernel size */
#endif
#ifndef SUPPORT_VLB_SYNC /* 1 to support weird 32-bit chips */
#define SUPPORT_VLB_SYNC 0 /* 0 to reduce kernel size */
#endif
#ifndef DISK_RECOVERY_TIME /* off=0; on=access_delay_time */
#define DISK_RECOVERY_TIME 0 /* for hardware that needs it */
#endif
#ifndef OK_TO_RESET_CONTROLLER /* 1 needed for good error recovery */
#define OK_TO_RESET_CONTROLLER 1 /* 0 for use with AH2372A/B interface */
#endif
#ifndef FAKE_FDISK_FOR_EZDRIVE /* 1 to help linux fdisk with EZDRIVE */
#define FAKE_FDISK_FOR_EZDRIVE 1 /* 0 to reduce kernel size */
#endif
#ifndef FANCY_STATUS_DUMPS /* 1 for human-readable drive errors */
#define FANCY_STATUS_DUMPS 1 /* 0 to reduce kernel size */
#endif
 
#ifdef CONFIG_BLK_DEV_CMD640
#if 0 /* change to 1 when debugging cmd640 problems */
void cmd640_dump_regs (void);
#define CMD640_DUMP_REGS cmd640_dump_regs() /* for debugging cmd640 chipset */
#endif
#endif /* CONFIG_BLK_DEV_CMD640 */
 
#if defined(CONFIG_BLK_DEV_IDECD) || defined(CONFIG_BLK_DEV_IDETAPE) || \
defined(CONFIG_BLK_DEV_IDEFLOPPY) || defined(CONFIG_BLK_DEV_IDESCSI)
#define CONFIG_BLK_DEV_IDEATAPI 1
#endif
 
/*
* IDE_DRIVE_CMD is used to implement many features of the hdparm utility
*/
#define IDE_DRIVE_CMD 99 /* (magic) undef to reduce kernel size*/
 
/*
* "No user-serviceable parts" beyond this point :)
*****************************************************************************/
 
#if defined(CONFIG_BLK_DEV_IDESCSI) && !defined(CONFIG_SCSI)
#error "SCSI must also be selected"
#endif
 
typedef unsigned char byte; /* used everywhere */
 
/*
* Probably not wise to fiddle with these
*/
#define ERROR_MAX 8 /* Max read/write errors per sector */
#define ERROR_RESET 3 /* Reset controller every 4th retry */
#define ERROR_RECAL 1 /* Recalibrate every 2nd retry */
 
/*
* Ensure that various configuration flags have compatible settings
*/
#ifdef REALLY_SLOW_IO
#undef REALLY_FAST_IO
#endif
 
/*
* Definitions for accessing IDE controller registers
*/
 
#define HWIF(drive) ((ide_hwif_t *)((drive)->hwif))
#define HWGROUP(drive) ((ide_hwgroup_t *)(HWIF(drive)->hwgroup))
 
#define IDE_DATA_OFFSET (0)
#define IDE_ERROR_OFFSET (1)
#define IDE_NSECTOR_OFFSET (2)
#define IDE_SECTOR_OFFSET (3)
#define IDE_LCYL_OFFSET (4)
#define IDE_HCYL_OFFSET (5)
#define IDE_SELECT_OFFSET (6)
#define IDE_STATUS_OFFSET (7)
#define IDE_FEATURE_OFFSET IDE_ERROR_OFFSET
#define IDE_COMMAND_OFFSET IDE_STATUS_OFFSET
 
#define IDE_DATA_REG (HWIF(drive)->io.io_base+(IDE_DATA_OFFSET<<HWIF(drive)->io.io_shift))
#define IDE_ERROR_REG (HWIF(drive)->io.io_base+(IDE_ERROR_OFFSET<<HWIF(drive)->io.io_shift))
#define IDE_NSECTOR_REG (HWIF(drive)->io.io_base+(IDE_NSECTOR_OFFSET<<HWIF(drive)->io.io_shift))
#define IDE_SECTOR_REG (HWIF(drive)->io.io_base+(IDE_SECTOR_OFFSET<<HWIF(drive)->io.io_shift))
#define IDE_LCYL_REG (HWIF(drive)->io.io_base+(IDE_LCYL_OFFSET<<HWIF(drive)->io.io_shift))
#define IDE_HCYL_REG (HWIF(drive)->io.io_base+(IDE_HCYL_OFFSET<<HWIF(drive)->io.io_shift))
#define IDE_SELECT_REG (HWIF(drive)->io.io_base+(IDE_SELECT_OFFSET<<HWIF(drive)->io.io_shift))
#define IDE_STATUS_REG (HWIF(drive)->io.io_base+(IDE_STATUS_OFFSET<<HWIF(drive)->io.io_shift))
#define IDE_CONTROL_REG (HWIF(drive)->io.ctl_port)
#define IDE_FEATURE_REG IDE_ERROR_REG
#define IDE_COMMAND_REG IDE_STATUS_REG
#define IDE_ALTSTATUS_REG IDE_CONTROL_REG
#define IDE_IREASON_REG IDE_NSECTOR_REG
#define IDE_BCOUNTL_REG IDE_LCYL_REG
#define IDE_BCOUNTH_REG IDE_HCYL_REG
 
#ifdef REALLY_FAST_IO
#define OUT_BYTE(b,p) outb((b),(p))
#define IN_BYTE(p) (byte)inb(p)
#else
#define OUT_BYTE(b,p) outb_p((b),(p))
#define IN_BYTE(p) (byte)inb_p(p)
#endif /* REALLY_FAST_IO */
 
#define GET_ERR() IN_BYTE(IDE_ERROR_REG)
#define GET_STAT() IN_BYTE(IDE_STATUS_REG)
#define OK_STAT(stat,good,bad) (((stat)&((good)|(bad)))==(good))
#define BAD_R_STAT (BUSY_STAT | ERR_STAT)
#define BAD_W_STAT (BAD_R_STAT | WRERR_STAT)
#define BAD_STAT (BAD_R_STAT | DRQ_STAT)
#define DRIVE_READY (READY_STAT | SEEK_STAT)
#define DATA_READY (DRQ_STAT)
 
/*
* Some more useful definitions
*/
#define IDE_MAJOR_NAME "ide" /* the same for all i/f; see also genhd.c */
#define MAJOR_NAME IDE_MAJOR_NAME
#define PARTN_BITS 6 /* number of minor dev bits for partitions */
#define PARTN_MASK ((1<<PARTN_BITS)-1) /* a useful bit mask */
#define MAX_DRIVES 2 /* per interface; 2 assumed by lots of code */
#ifndef MAX_HWIFS
#define MAX_HWIFS 4 /* an arbitrary, but realistic limit */
#endif
#define SECTOR_WORDS (512 / 4) /* number of 32bit words per sector */
 
/*
* Timeouts for various operations:
*/
#define WAIT_DRQ (5*HZ/100) /* 50msec - spec allows up to 20ms */
#ifdef CONFIG_APM
#define WAIT_READY (5*HZ) /* 5sec - some laptops are very slow */
#else
#define WAIT_READY (3*HZ/100) /* 30msec - should be instantaneous */
#endif /* CONFIG_APM */
#define WAIT_PIDENTIFY (1*HZ) /* 1sec - should be less than 3ms (?) */
#define WAIT_WORSTCASE (30*HZ) /* 30sec - worst case when spinning up */
#define WAIT_CMD (10*HZ) /* 10sec - maximum wait for an IRQ to happen */
 
#if defined(CONFIG_BLK_DEV_HT6560B) || defined(CONFIG_BLK_DEV_PROMISE)
#define SELECT_DRIVE(hwif,drive) \
{ \
if (hwif->selectproc) \
hwif->selectproc(drive); \
else \
OUT_BYTE((drive)->select.all, hwif->io_base + (IDE_SELECT_OFFSET << hwif->io.io_shift)); \
}
#else
#define SELECT_DRIVE(hwif,drive) OUT_BYTE((drive)->select.all, hwif->io.io_base + (IDE_SELECT_OFFSET << hwif->io.io_shift));
#endif /* CONFIG_BLK_DEV_HT6560B || CONFIG_BLK_DEV_PROMISE */
#ifdef CONFIG_BLK_DEV_IDETAPE
#include "ide-tape.h"
#endif /* CONFIG_BLK_DEV_IDETAPE */
 
#ifdef CONFIG_BLK_DEV_IDECD
 
struct atapi_request_sense {
unsigned char error_code : 7;
unsigned char valid : 1;
byte reserved1;
unsigned char sense_key : 4;
unsigned char reserved2 : 1;
unsigned char ili : 1;
unsigned char reserved3 : 2;
byte info[4];
byte sense_len;
byte command_info[4];
byte asc;
byte ascq;
byte fru;
byte sense_key_specific[3];
};
 
struct packet_command {
char *buffer;
int buflen;
int stat;
struct atapi_request_sense *sense_data;
unsigned char c[12];
};
 
 
/* Structure of a MSF cdrom address. */
struct atapi_msf {
byte reserved;
byte minute;
byte second;
byte frame;
};
 
 
/* Space to hold the disk TOC. */
 
#define MAX_TRACKS 99
struct atapi_toc_header {
unsigned short toc_length;
byte first_track;
byte last_track;
};
 
struct atapi_toc_entry {
byte reserved1;
unsigned control : 4;
unsigned adr : 4;
byte track;
byte reserved2;
union {
unsigned lba;
struct atapi_msf msf;
} addr;
};
 
struct atapi_toc {
int last_session_lba;
int xa_flag;
unsigned capacity;
struct atapi_toc_header hdr;
struct atapi_toc_entry ent[MAX_TRACKS+1];
/* One extra for the leadout. */
};
 
 
/* This structure is annoyingly close to, but not identical with,
the cdrom_subchnl structure from cdrom.h. */
struct atapi_cdrom_subchnl
{
u_char acdsc_reserved;
u_char acdsc_audiostatus;
u_short acdsc_length;
u_char acdsc_format;
 
u_char acdsc_adr: 4;
u_char acdsc_ctrl: 4;
u_char acdsc_trk;
u_char acdsc_ind;
union {
struct atapi_msf msf;
int lba;
} acdsc_absaddr;
union {
struct atapi_msf msf;
int lba;
} acdsc_reladdr;
};
 
 
/* Extra per-device info for cdrom drives. */
struct cdrom_info {
 
/* Buffer for table of contents. NULL if we haven't allocated
a TOC buffer for this device yet. */
 
struct atapi_toc *toc;
 
/* Sector buffer. If a read request wants only the first part
of a cdrom block, we cache the rest of the block here,
in the expectation that that data is going to be wanted soon.
SECTOR_BUFFERED is the number of the first buffered sector,
and NSECTORS_BUFFERED is the number of sectors in the buffer.
Before the buffer is allocated, we should have
SECTOR_BUFFER == NULL and NSECTORS_BUFFERED == 0. */
 
unsigned long sector_buffered;
unsigned long nsectors_buffered;
char *sector_buffer;
 
/* The result of the last successful request sense command
on this device. */
struct atapi_request_sense sense_data;
 
int max_sectors;
};
 
#endif /* CONFIG_BLK_DEV_IDECD */
 
/*
* Now for the data we need to maintain per-drive: ide_drive_t
*/
 
typedef enum {ide_disk, ide_cdrom, ide_tape, ide_floppy, ide_scsi} ide_media_t;
 
typedef union {
unsigned all : 8; /* all of the bits together */
struct {
unsigned set_geometry : 1; /* respecify drive geometry */
unsigned recalibrate : 1; /* seek to cyl 0 */
unsigned set_multmode : 1; /* set multmode count */
unsigned set_tune : 1; /* tune interface for drive */
unsigned mc : 1; /* acknowledge media change */
unsigned reserved : 3; /* unused */
} b;
} special_t;
 
typedef union {
unsigned all : 8; /* all of the bits together */
struct {
unsigned head : 4; /* always zeros here */
unsigned unit : 1; /* drive select number, 0 or 1 */
unsigned bit5 : 1; /* always 1 */
unsigned lba : 1; /* using LBA instead of CHS */
unsigned bit7 : 1; /* always 1 */
} b;
} select_t;
 
typedef struct ide_drive_s {
special_t special; /* special action flags */
unsigned present : 1; /* drive is physically present */
unsigned noprobe : 1; /* from: hdx=noprobe */
unsigned keep_settings : 1; /* restore settings after drive reset */
unsigned busy : 1; /* currently doing revalidate_disk() */
unsigned removable : 1; /* 1 if need to do check_media_change */
unsigned using_dma : 1; /* disk is using dma for read/write */
unsigned forced_geom : 1; /* 1 if hdx=c,h,s was given at boot */
unsigned unmask : 1; /* flag: okay to unmask other irqs */
unsigned no_unmask : 1; /* disallow setting unmask bit */
unsigned no_io_32bit : 1; /* disallow enabling 32bit I/O */
unsigned nobios : 1; /* flag: do not probe bios for drive */
unsigned slow : 1; /* flag: slow data port */
unsigned autotune : 2; /* 1=autotune, 2=noautotune, 0=default */
#if FAKE_FDISK_FOR_EZDRIVE
unsigned remap_0_to_1 : 1; /* flag: partitioned with ezdrive */
#endif /* FAKE_FDISK_FOR_EZDRIVE */
unsigned no_geom : 1; /* flag: do not set geometry */
ide_media_t media; /* disk, cdrom, tape, floppy */
select_t select; /* basic drive/head select reg value */
byte ctl; /* "normal" value for IDE_CONTROL_REG */
byte ready_stat; /* min status value for drive ready */
byte mult_count; /* current multiple sector setting */
byte mult_req; /* requested multiple sector setting */
byte tune_req; /* requested drive tuning setting */
byte io_32bit; /* 0=16-bit, 1=32-bit, 2/3=32bit+sync */
byte bad_wstat; /* used for ignoring WRERR_STAT */
byte sect0; /* offset of first sector for DM6:DDO */
byte usage; /* current "open()" count for drive */
byte head; /* "real" number of heads */
byte sect; /* "real" sectors per track */
byte bios_head; /* BIOS/fdisk/LILO number of heads */
byte bios_sect; /* BIOS/fdisk/LILO sectors per track */
unsigned short bios_cyl; /* BIOS/fdisk/LILO number of cyls */
unsigned short cyl; /* "real" number of cyls */
void *hwif; /* actually (ide_hwif_t *) */
struct wait_queue *wqueue; /* used to wait for drive in open() */
struct hd_driveid *id; /* drive model identification info */
struct hd_struct *part; /* drive partition table */
char name[4]; /* drive name, such as "hda" */
#ifdef CONFIG_BLK_DEV_IDECD
struct cdrom_info cdrom_info; /* for ide-cd.c */
#endif /* CONFIG_BLK_DEV_IDECD */
#ifdef CONFIG_BLK_DEV_IDETAPE
idetape_tape_t tape; /* for ide-tape.c */
#endif /* CONFIG_BLK_DEV_IDETAPE */
#ifdef CONFIG_BLK_DEV_IDEFLOPPY
void *floppy; /* for ide-floppy.c */
#endif /* CONFIG_BLK_DEV_IDEFLOPPY */
#ifdef CONFIG_BLK_DEV_IDESCSI
void *scsi; /* for ide-scsi.c */
#endif /* CONFIG_BLK_DEV_IDESCSI */
byte ide_scsi; /* use ide-scsi driver */
} ide_drive_t;
 
typedef struct hwreg_s {
unsigned long io_base;
unsigned long io_shift; /* 0 = normal, 4 = ICS ide */
unsigned long ctl_port;
} hwreg_t;
/*
* An ide_dmaproc_t() initiates/aborts DMA read/write operations on a drive.
*
* The caller is assumed to have selected the drive and programmed the drive's
* sector address using CHS or LBA. All that remains is to prepare for DMA
* and then issue the actual read/write DMA/PIO command to the drive.
*
* Returns 0 if all went well.
* Returns 1 if DMA read/write could not be started, in which case the caller
* should either try again later, or revert to PIO for the current request.
*/
typedef enum { ide_dma_read = 0, ide_dma_write = 1,
ide_dma_abort = 2, ide_dma_check = 3,
ide_dma_status_bad = 4, ide_dma_transferred = 5,
ide_dma_begin = 6 }
ide_dma_action_t;
 
typedef int (ide_dmaproc_t)(ide_dma_action_t, ide_drive_t *);
 
 
/*
* An ide_tuneproc_t() is used to set the speed of an IDE interface
* to a particular PIO mode. The "byte" parameter is used
* to select the PIO mode by number (0,1,2,3,4,5), and a value of 255
* indicates that the interface driver should "auto-tune" the PIO mode
* according to the drive capabilities in drive->id;
*
* Not all interface types support tuning, and not all of those
* support all possible PIO settings. They may silently ignore
* or round values as they see fit.
*/
typedef void (ide_tuneproc_t)(ide_drive_t *, byte);
 
/*
* This is used to provide HT6560B & PROMISE interface support.
*/
typedef void (ide_selectproc_t) (ide_drive_t *);
 
/*
* hwif_chipset_t is used to keep track of the specific hardware
* chipset used by each IDE interface, if known.
*/
typedef enum { ide_unknown, ide_generic, ide_triton,
ide_cmd640, ide_dtc2278, ide_ali14xx,
ide_qd6580, ide_umc8672, ide_ht6560b,
ide_promise, ide_promise_udma, ide_dmaics }
hwif_chipset_t;
 
typedef struct hwif_s {
struct hwif_s *next; /* for linked-list in ide_hwgroup_t */
void *hwgroup; /* actually (ide_hwgroup_t *) */
hwreg_t io;
ide_drive_t drives[MAX_DRIVES]; /* drive info */
struct gendisk *gd; /* gendisk structure */
ide_tuneproc_t *tuneproc; /* routine to tune PIO mode for drives */
#if defined(CONFIG_BLK_DEV_HT6560B) || defined(CONFIG_BLK_DEV_PROMISE)
ide_selectproc_t *selectproc; /* tweaks hardware to select drive */
#endif
ide_dmaproc_t *dmaproc; /* dma read/write/abort routine */
unsigned long *dmatable; /* dma physical region descriptor table */
unsigned short dma_base; /* base addr for dma ports (triton) */
byte irq; /* our irq number */
byte major; /* our major number */
char name[5]; /* name of interface, eg. "ide0" */
byte index; /* 0 for ide0; 1 for ide1; ... */
hwif_chipset_t chipset; /* sub-module for tuning.. */
unsigned noprobe : 1; /* don't probe for this interface */
unsigned present : 1; /* this interface exists */
unsigned serialized : 1; /* serialized operation with mate hwif */
unsigned sharing_irq: 1; /* 1 = sharing irq with another hwif */
#ifdef CONFIG_BLK_DEV_PROMISE
unsigned is_promise2: 1; /* 2nd i/f on promise DC4030 */
#endif /* CONFIG_BLK_DEV_PROMISE */
#if (DISK_RECOVERY_TIME > 0)
unsigned long last_time; /* time when previous rq was done */
#endif
#ifdef CONFIG_BLK_DEV_IDECD
struct request request_sense_request; /* from ide-cd.c */
struct packet_command request_sense_pc; /* from ide-cd.c */
#endif /* CONFIG_BLK_DEV_IDECD */
#ifdef CONFIG_BLK_DEV_IDETAPE
ide_drive_t *tape_drive; /* Pointer to the tape on this interface */
#endif /* CONFIG_BLK_DEV_IDETAPE */
} ide_hwif_t;
 
/*
* internal ide interrupt handler type
*/
typedef void (ide_handler_t)(ide_drive_t *);
 
typedef struct hwgroup_s {
ide_handler_t *handler;/* irq handler, if active */
ide_drive_t *drive; /* current drive */
ide_hwif_t *hwif; /* ptr to current hwif in linked-list */
ide_hwif_t *next_hwif; /* next selected hwif (for tape) */
struct request *rq; /* current request */
struct timer_list timer; /* failsafe timer */
struct request wrq; /* local copy of current write rq */
unsigned long poll_timeout; /* timeout value during long polls */
int active; /* set when servicing requests */
} ide_hwgroup_t;
 
/*
* ide_hwifs[] is the master data structure used to keep track
* of just about everything in ide.c. Whenever possible, routines
* should be using pointers to a drive (ide_drive_t *) or
* pointers to a hwif (ide_hwif_t *), rather than indexing this
* structure directly (the allocation/layout may change!).
*
*/
#ifndef _IDE_C
extern ide_hwif_t ide_hwifs[]; /* master data repository */
#endif
 
/*
* One final include file, which references some of the data/defns from above
*/
#define IDE_DRIVER /* "parameter" for blk.h */
#include <linux/blk.h>
 
#if (DISK_RECOVERY_TIME > 0)
void ide_set_recovery_timer (ide_hwif_t *);
#define SET_RECOVERY_TIMER(drive) ide_set_recovery_timer (drive)
#else
#define SET_RECOVERY_TIMER(drive)
#endif
 
/*
* This is used for (nearly) all data transfers from the IDE interface
*/
void ide_input_data (ide_drive_t *drive, void *buffer, unsigned int wcount);
 
/*
* This is used for (nearly) all data transfers to the IDE interface
*/
void ide_output_data (ide_drive_t *drive, void *buffer, unsigned int wcount);
 
/*
* This is used for (nearly) all ATAPI data transfers from/to the IDE interface
*/
void atapi_input_bytes (ide_drive_t *drive, void *buffer, unsigned int bytecount);
void atapi_output_bytes (ide_drive_t *drive, void *buffer, unsigned int bytecount);
 
/*
* This is used on exit from the driver, to designate the next irq handler
* and also to start the safety timer.
*/
void ide_set_handler (ide_drive_t *drive, ide_handler_t *handler, unsigned int timeout);
 
/*
* Error reporting, in human readable form (luxurious, but a memory hog).
*/
byte ide_dump_status (ide_drive_t *drive, const char *msg, byte stat);
 
/*
* ide_error() takes action based on the error returned by the controller.
* The calling function must return afterwards, to restart the request.
*/
void ide_error (ide_drive_t *drive, const char *msg, byte stat);
 
/*
* ide_fixstring() cleans up and (optionally) byte-swaps a text string,
* removing leading/trailing blanks and compressing internal blanks.
* It is primarily used to tidy up the model name/number fields as
* returned by the WIN_[P]IDENTIFY commands.
*/
void ide_fixstring (byte *s, const int bytecount, const int byteswap);
 
/*
* This routine busy-waits for the drive status to be not "busy".
* It then checks the status for all of the "good" bits and none
* of the "bad" bits, and if all is okay it returns 0. All other
* cases return 1 after invoking ide_error() -- caller should return.
*
*/
int ide_wait_stat (ide_drive_t *drive, byte good, byte bad, unsigned long timeout);
 
/*
* This routine is called from the partition-table code in genhd.c
* to "convert" a drive to a logical geometry with fewer than 1024 cyls.
*
* The second parameter, "xparm", determines exactly how the translation
* will be handled:
* 0 = convert to CHS with fewer than 1024 cyls
* using the same method as Ontrack DiskManager.
* 1 = same as "0", plus offset everything by 63 sectors.
* -1 = similar to "0", plus redirect sector 0 to sector 1.
* >1 = convert to a CHS geometry with "xparm" heads.
*
* Returns 0 if the translation was not possible, if the device was not
* an IDE disk drive, or if a geometry was "forced" on the commandline.
* Returns 1 if the geometry translation was successful.
*/
int ide_xlate_1024 (kdev_t, int, const char *);
 
/*
* Start a reset operation for an IDE interface.
* The caller should return immediately after invoking this.
*/
void ide_do_reset (ide_drive_t *);
 
/*
* This function is intended to be used prior to invoking ide_do_drive_cmd().
*/
void ide_init_drive_cmd (struct request *rq);
 
/*
* "action" parameter type for ide_do_drive_cmd() below.
*/
typedef enum
{ide_wait, /* insert rq at end of list, and wait for it */
ide_next, /* insert rq immediately after current request */
ide_preempt, /* insert rq in front of current request */
ide_end} /* insert rq at end of list, but don't wait for it */
ide_action_t;
 
/*
* This function issues a special IDE device request
* onto the request queue.
*
* If action is ide_wait, then then rq is queued at the end of
* the request queue, and the function sleeps until it has been
* processed. This is for use when invoked from an ioctl handler.
*
* If action is ide_preempt, then the rq is queued at the head of
* the request queue, displacing the currently-being-processed
* request and this function returns immediately without waiting
* for the new rq to be completed. This is VERY DANGEROUS, and is
* intended for careful use by the ATAPI tape/cdrom driver code.
*
* If action is ide_next, then the rq is queued immediately after
* the currently-being-processed-request (if any), and the function
* returns without waiting for the new rq to be completed. As above,
* This is VERY DANGEROUS, and is intended for careful use by the
* ATAPI tape/cdrom driver code.
*
* If action is ide_end, then the rq is queued at the end of the
* request queue, and the function returns immediately without waiting
* for the new rq to be completed. This is again intended for careful
* use by the ATAPI tape/cdrom driver code. (Currently used by ide-tape.c,
* when operating in the pipelined operation mode).
*/
int ide_do_drive_cmd (ide_drive_t *drive, struct request *rq, ide_action_t action);
/*
* Clean up after success/failure of an explicit drive cmd.
* stat/err are used only when (HWGROUP(drive)->rq->cmd == IDE_DRIVE_CMD).
*/
void ide_end_drive_cmd (ide_drive_t *drive, byte stat, byte err);
 
/*
* ide_system_bus_speed() returns what we think is the system VESA/PCI
* bus speed (in Mhz). This is used for calculating interface PIO timings.
* The default is 40 for known PCI systems, 50 otherwise.
* The "idebus=xx" parameter can be used to override this value.
*/
int ide_system_bus_speed (void);
 
/*
* ide_multwrite() transfers a block of up to mcount sectors of data
* to a drive as part of a disk multwrite operation.
*/
void ide_multwrite (ide_drive_t *drive, unsigned int mcount);
 
#ifdef CONFIG_BLK_DEV_IDECD
/*
* These are routines in ide-cd.c invoked from ide.c
*/
void ide_do_rw_cdrom (ide_drive_t *, unsigned long);
int ide_cdrom_ioctl (ide_drive_t *, struct inode *, struct file *, unsigned int, unsigned long);
int ide_cdrom_check_media_change (ide_drive_t *);
int ide_cdrom_open (struct inode *, struct file *, ide_drive_t *);
void ide_cdrom_release (struct inode *, struct file *, ide_drive_t *);
void ide_cdrom_setup (ide_drive_t *);
#endif /* CONFIG_BLK_DEV_IDECD */
 
#ifdef CONFIG_BLK_DEV_IDETAPE
 
/*
* Functions in ide-tape.c which are invoked from ide.c:
*/
 
/*
* idetape_identify_device is called during device probing stage to
* probe for an ide atapi tape drive and to initialize global variables
* in ide-tape.c which provide the link between the character device
* and the corresponding block device.
*
* Returns 1 if an ide tape was detected and is supported.
* Returns 0 otherwise.
*/
int idetape_identify_device (ide_drive_t *drive,struct hd_driveid *id);
 
/*
* idetape_setup is called a bit later than idetape_identify_device,
* during the search for disk partitions, to initialize various tape
* state variables in ide_drive_t *drive.
*/
void idetape_setup (ide_drive_t *drive);
 
/*
* idetape_do_request is our request function. It is called by ide.c
* to process a new request.
*/
 
void idetape_do_request (ide_drive_t *drive, struct request *rq, unsigned long block);
 
/*
* idetape_end_request is used to finish servicing a request, and to
* insert a pending pipeline request into the main device queue.
*/
void idetape_end_request (byte uptodate, ide_hwgroup_t *hwgroup);
 
/*
* Block device interface functions.
*/
int idetape_blkdev_ioctl (ide_drive_t *drive, struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg);
int idetape_blkdev_open (struct inode *inode, struct file *filp, ide_drive_t *drive);
void idetape_blkdev_release (struct inode *inode, struct file *filp, ide_drive_t *drive);
 
/*
* idetape_register_chrdev initializes the character device interface to
* the ide tape drive.
*/
void idetape_register_chrdev (void);
 
#endif /* CONFIG_BLK_DEV_IDETAPE */
 
#ifdef CONFIG_BLK_DEV_IDEFLOPPY
int idefloppy_identify_device (ide_drive_t *drive,struct hd_driveid *id);
void idefloppy_setup (ide_drive_t *drive);
void idefloppy_do_request (ide_drive_t *drive, struct request *rq, unsigned long block);
void idefloppy_end_request (byte uptodate, ide_hwgroup_t *hwgroup);
int idefloppy_ioctl (ide_drive_t *drive, struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg);
int idefloppy_open (struct inode *inode, struct file *filp, ide_drive_t *drive);
void idefloppy_release (struct inode *inode, struct file *filp, ide_drive_t *drive);
int idefloppy_media_change (ide_drive_t *drive);
unsigned long idefloppy_capacity (ide_drive_t *drive);
#endif /* CONFIG_BLK_DEV_IDEFLOPPY */
 
#ifdef CONFIG_BLK_DEV_IDESCSI
void idescsi_setup (ide_drive_t *drive);
void idescsi_do_request (ide_drive_t *drive, struct request *rq, unsigned long block);
void idescsi_end_request (byte uptodate, ide_hwgroup_t *hwgroup);
int idescsi_ioctl (ide_drive_t *drive, struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg);
int idescsi_open (struct inode *inode, struct file *filp, ide_drive_t *drive);
void idescsi_ide_release (struct inode *inode, struct file *filp, ide_drive_t *drive);
#endif /* CONFIG_BLK_DEV_IDESCSI */
 
#ifdef CONFIG_BLK_DEV_TRITON
void ide_init_triton (byte, byte);
void ide_init_promise (byte bus, byte fn, ide_hwif_t *hwif0, ide_hwif_t *hwif1, unsigned short dma);
#endif /* CONFIG_BLK_DEV_TRITON */
 
#ifdef CONFIG_BLK_DEV_IDE_CARDS
extern int ide_register_port (int io_base, int ctrl_base, int stepping, int irq);
#ifdef CONFIG_BLK_DEV_IDE_ICSIDE
extern int icside_init (void);
#endif
#ifdef CONFIG_BLK_DEV_IDE_RAPIDE
extern int rapide_init (void);
#endif
#endif
/ll_rw_blk.c
0,0 → 1,699
/*
* linux/drivers/block/ll_rw_blk.c
*
* Copyright (C) 1991, 1992 Linus Torvalds
* Copyright (C) 1994, Karl Keyte: Added support for disk statistics
*/
 
/*
* This handles all read/write requests to block devices
*/
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/kernel_stat.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/config.h>
#include <linux/locks.h>
#include <linux/mm.h>
 
#include <asm/system.h>
#include <asm/io.h>
#include "blk.h"
 
/*
* The request-struct contains all necessary data
* to load a nr of sectors into memory
*/
static struct request all_requests[NR_REQUEST];
 
/*
* The "disk" task queue is used to start the actual requests
* after a plug
*/
DECLARE_TASK_QUEUE(tq_disk);
 
/*
* used to wait on when there are no free requests
*/
struct wait_queue * wait_for_request;
 
/* This specifies how many sectors to read ahead on the disk. */
 
int read_ahead[MAX_BLKDEV];
 
/* blk_dev_struct is:
* *request_fn
* *current_request
*/
struct blk_dev_struct blk_dev[MAX_BLKDEV]; /* initialized by blk_dev_init() */
 
/*
* blk_size contains the size of all block-devices in units of 1024 byte
* sectors:
*
* blk_size[MAJOR][MINOR]
*
* if (!blk_size[MAJOR]) then no minor size checking is done.
*/
int * blk_size[MAX_BLKDEV];
 
/*
* blksize_size contains the size of all block-devices:
*
* blksize_size[MAJOR][MINOR]
*
* if (!blksize_size[MAJOR]) then 1024 bytes is assumed.
*/
int * blksize_size[MAX_BLKDEV];
 
/*
* hardsect_size contains the size of the hardware sector of a device.
*
* hardsect_size[MAJOR][MINOR]
*
* if (!hardsect_size[MAJOR])
* then 512 bytes is assumed.
* else
* sector_size is hardsect_size[MAJOR][MINOR]
* This is currently set by some scsi device and read by the msdos fs driver
* This might be a some uses later.
*/
int * hardsect_size[MAX_BLKDEV];
 
/*
* remove the plug and let it rip..
*/
void unplug_device(void * data)
{
struct blk_dev_struct * dev = (struct blk_dev_struct *) data;
unsigned long flags;
 
save_flags_cli(flags);
if (dev->current_request == &dev->plug) {
struct request * next = dev->plug.next;
dev->current_request = next;
if (next) {
dev->plug.next = NULL;
(dev->request_fn)();
}
}
restore_flags(flags);
}
 
/*
* "plug" the device if there are no outstanding requests: this will
* force the transfer to start only after we have put all the requests
* on the list.
*
* This is called with interrupts off and no requests on the queue.
*/
static inline void plug_device(struct blk_dev_struct * dev)
{
dev->current_request = &dev->plug;
queue_task_irq_off(&dev->plug_tq, &tq_disk);
}
 
/*
* look for a free request in the first N entries.
* NOTE: interrupts must be disabled on the way in, and will still
* be disabled on the way out.
*/
static inline struct request * get_request(int n, kdev_t dev)
{
static struct request *prev_found = NULL, *prev_limit = NULL;
register struct request *req, *limit;
 
if (n <= 0)
panic("get_request(%d): impossible!\n", n);
 
limit = all_requests + n;
if (limit != prev_limit) {
prev_limit = limit;
prev_found = all_requests;
}
req = prev_found;
for (;;) {
req = ((req > all_requests) ? req : limit) - 1;
if (req->rq_status == RQ_INACTIVE)
break;
if (req == prev_found)
return NULL;
}
prev_found = req;
req->rq_status = RQ_ACTIVE;
req->rq_dev = dev;
return req;
}
 
/*
* wait until a free request in the first N entries is available.
*/
static struct request * __get_request_wait(int n, kdev_t dev)
{
register struct request *req;
struct wait_queue wait = { current, NULL };
 
add_wait_queue(&wait_for_request, &wait);
for (;;) {
current->state = TASK_UNINTERRUPTIBLE;
cli();
req = get_request(n, dev);
sti();
if (req)
break;
run_task_queue(&tq_disk);
schedule();
}
remove_wait_queue(&wait_for_request, &wait);
current->state = TASK_RUNNING;
return req;
}
 
static inline struct request * get_request_wait(int n, kdev_t dev)
{
register struct request *req;
 
cli();
req = get_request(n, dev);
sti();
if (req)
return req;
return __get_request_wait(n, dev);
}
 
/* RO fail safe mechanism */
 
static long ro_bits[MAX_BLKDEV][8];
 
int is_read_only(kdev_t dev)
{
int minor,major;
 
major = MAJOR(dev);
minor = MINOR(dev);
if (major < 0 || major >= MAX_BLKDEV) return 0;
return ro_bits[major][minor >> 5] & (1 << (minor & 31));
}
 
void set_device_ro(kdev_t dev,int flag)
{
int minor,major;
 
major = MAJOR(dev);
minor = MINOR(dev);
if (major < 0 || major >= MAX_BLKDEV) return;
if (flag) ro_bits[major][minor >> 5] |= 1 << (minor & 31);
else ro_bits[major][minor >> 5] &= ~(1 << (minor & 31));
}
 
static inline void drive_stat_acct(int cmd, unsigned long nr_sectors,
short disk_index)
{
kstat.dk_drive[disk_index]++;
if (cmd == READ) {
kstat.dk_drive_rio[disk_index]++;
kstat.dk_drive_rblk[disk_index] += nr_sectors;
} else if (cmd == WRITE) {
kstat.dk_drive_wio[disk_index]++;
kstat.dk_drive_wblk[disk_index] += nr_sectors;
} else
printk(KERN_ERR "drive_stat_acct: cmd not R/W?\n");
}
 
/*
* add-request adds a request to the linked list.
* It disables interrupts so that it can muck with the
* request-lists in peace.
*
* By this point, req->cmd is always either READ/WRITE, never READA/WRITEA,
* which is important for drive_stat_acct() above.
*/
 
void add_request(struct blk_dev_struct * dev, struct request * req)
{
struct request * tmp;
short disk_index;
 
switch (MAJOR(req->rq_dev)) {
case SCSI_DISK_MAJOR:
disk_index = (MINOR(req->rq_dev) & 0x0070) >> 4;
if (disk_index < 4)
drive_stat_acct(req->cmd, req->nr_sectors, disk_index);
break;
case IDE0_MAJOR: /* same as HD_MAJOR */
case XT_DISK_MAJOR:
disk_index = (MINOR(req->rq_dev) & 0x0040) >> 6;
drive_stat_acct(req->cmd, req->nr_sectors, disk_index);
break;
case IDE1_MAJOR:
disk_index = ((MINOR(req->rq_dev) & 0x0040) >> 6) + 2;
drive_stat_acct(req->cmd, req->nr_sectors, disk_index);
default:
break;
}
 
req->next = NULL;
cli();
if (req->bh)
mark_buffer_clean(req->bh);
if (!(tmp = dev->current_request)) {
dev->current_request = req;
(dev->request_fn)();
sti();
return;
}
for ( ; tmp->next ; tmp = tmp->next) {
if ((IN_ORDER(tmp,req) ||
!IN_ORDER(tmp,tmp->next)) &&
IN_ORDER(req,tmp->next))
break;
}
req->next = tmp->next;
tmp->next = req;
 
/* for SCSI devices, call request_fn unconditionally */
if (scsi_blk_major(MAJOR(req->rq_dev)))
(dev->request_fn)();
 
sti();
}
 
#define MAX_SECTORS 244
 
static inline void attempt_merge (struct request *req)
{
struct request *next = req->next;
 
if (!next)
return;
if (req->sector + req->nr_sectors != next->sector)
return;
if (next->sem || req->cmd != next->cmd || req->rq_dev != next->rq_dev || req->nr_sectors + next->nr_sectors >= MAX_SECTORS)
return;
#if 0
printk ("%s: merge %ld, %ld + %ld == %ld\n", kdevname(req->rq_dev), req->sector, req->nr_sectors, next->nr_sectors, req->nr_sectors + next->nr_sectors);
#endif
req->bhtail->b_reqnext = next->bh;
req->bhtail = next->bhtail;
req->nr_sectors += next->nr_sectors;
next->rq_status = RQ_INACTIVE;
req->next = next->next;
wake_up (&wait_for_request);
}
 
void make_request(int major,int rw, struct buffer_head * bh)
{
unsigned int sector, count;
struct request * req;
int rw_ahead, max_req;
 
count = bh->b_size >> 9;
sector = bh->b_rsector;
 
/* Uhhuh.. Nasty dead-lock possible here.. */
if (buffer_locked(bh)) {
#if 0
printk("make_request(): buffer already locked\n");
#endif
return;
}
/* Maybe the above fixes it, and maybe it doesn't boot. Life is interesting */
 
lock_buffer(bh);
 
if (blk_size[major])
if (blk_size[major][MINOR(bh->b_rdev)] < (sector + count)>>1) {
bh->b_state &= (1 << BH_Lock) | (1 << BH_FreeOnIO);
/* This may well happen - the kernel calls bread()
without checking the size of the device, e.g.,
when mounting a device. */
printk(KERN_INFO
"attempt to access beyond end of device\n");
printk(KERN_INFO "%s: rw=%d, want=%d, limit=%d\n",
kdevname(bh->b_rdev), rw,
(sector + count)>>1,
blk_size[major][MINOR(bh->b_rdev)]);
unlock_buffer(bh);
return;
}
 
rw_ahead = 0; /* normal case; gets changed below for READA/WRITEA */
switch (rw) {
case READA:
rw_ahead = 1;
rw = READ; /* drop into READ */
case READ:
if (buffer_uptodate(bh)) {
#if 0
printk ("make_request(): buffer uptodate for READ\n");
#endif
unlock_buffer(bh); /* Hmmph! Already have it */
return;
}
kstat.pgpgin++;
max_req = NR_REQUEST; /* reads take precedence */
break;
case WRITEA:
rw_ahead = 1;
rw = WRITE; /* drop into WRITE */
case WRITE:
if (!buffer_dirty(bh)) {
#if 0
printk ("make_request(): buffer clean for WRITE\n");
#endif
unlock_buffer(bh); /* Hmmph! Nothing to write */
return;
}
/* We don't allow the write-requests to fill up the
* queue completely: we want some room for reads,
* as they take precedence. The last third of the
* requests are only for reads.
*/
kstat.pgpgout++;
max_req = (NR_REQUEST * 2) / 3;
break;
default:
printk(KERN_ERR "make_request: bad block dev cmd,"
" must be R/W/RA/WA\n");
unlock_buffer(bh);
return;
}
 
/* look for a free request. */
/* Loop uses two requests, 1 for loop and 1 for the real device.
* Cut max_req in half to avoid running out and deadlocking. */
if (major == LOOP_MAJOR)
max_req >>= 1;
 
/*
* Try to coalesce the new request with old requests
*/
cli();
req = blk_dev[major].current_request;
if (!req) {
/* MD and loop can't handle plugging without deadlocking */
if (major != MD_MAJOR && major != LOOP_MAJOR)
plug_device(blk_dev + major);
} else switch (major) {
case IDE0_MAJOR: /* same as HD_MAJOR */
case IDE1_MAJOR:
case FLOPPY_MAJOR:
case IDE2_MAJOR:
case IDE3_MAJOR:
case XT_DISK_MAJOR:
/*
* The scsi disk and cdrom drivers completely remove the request
* from the queue when they start processing an entry. For this
* reason it is safe to continue to add links to the top entry for
* those devices.
*
* All other drivers need to jump over the first entry, as that
* entry may be busy being processed and we thus can't change it.
*/
req = req->next;
if (!req)
break;
/* fall through */
 
case SCSI_DISK_MAJOR:
case SCSI_CDROM_MAJOR:
 
do {
if (req->sem)
continue;
if (req->cmd != rw)
continue;
if (req->nr_sectors >= MAX_SECTORS)
continue;
if (req->rq_dev != bh->b_rdev)
continue;
/* Can we add it to the end of this request? */
if (req->sector + req->nr_sectors == sector) {
req->bhtail->b_reqnext = bh;
req->bhtail = bh;
req->nr_sectors += count;
/* Can we now merge this req with the next? */
attempt_merge(req);
/* or to the beginning? */
} else if (req->sector - count == sector) {
bh->b_reqnext = req->bh;
req->bh = bh;
req->buffer = bh->b_data;
req->current_nr_sectors = count;
req->sector = sector;
req->nr_sectors += count;
} else
continue;
 
mark_buffer_clean(bh);
sti();
return;
} while ((req = req->next) != NULL);
}
 
/* find an unused request. */
req = get_request(max_req, bh->b_rdev);
sti();
 
/* if no request available: if rw_ahead, forget it; otherwise try again blocking.. */
if (!req) {
if (rw_ahead) {
unlock_buffer(bh);
return;
}
req = __get_request_wait(max_req, bh->b_rdev);
}
 
/* fill up the request-info, and add it to the queue */
req->cmd = rw;
req->errors = 0;
req->sector = sector;
req->nr_sectors = count;
req->current_nr_sectors = count;
req->buffer = bh->b_data;
req->sem = NULL;
req->bh = bh;
req->bhtail = bh;
req->next = NULL;
add_request(major+blk_dev,req);
}
 
/* This function can be used to request a number of buffers from a block
device. Currently the only restriction is that all buffers must belong to
the same device */
 
void ll_rw_block(int rw, int nr, struct buffer_head * bh[])
{
unsigned int major;
int correct_size;
struct blk_dev_struct * dev;
int i;
 
/* Make sure that the first block contains something reasonable */
while (!*bh) {
bh++;
if (--nr <= 0)
return;
}
 
dev = NULL;
if ((major = MAJOR(bh[0]->b_dev)) < MAX_BLKDEV)
dev = blk_dev + major;
if (!dev || !dev->request_fn) {
printk(KERN_ERR
"ll_rw_block: Trying to read nonexistent block-device %s (%ld)\n",
kdevname(bh[0]->b_dev), bh[0]->b_blocknr);
goto sorry;
}
 
/* Determine correct block size for this device. */
correct_size = BLOCK_SIZE;
if (blksize_size[major]) {
i = blksize_size[major][MINOR(bh[0]->b_dev)];
if (i)
correct_size = i;
}
 
/* Verify requested block sizes. */
for (i = 0; i < nr; i++) {
if (bh[i] && bh[i]->b_size != correct_size) {
printk(KERN_NOTICE "ll_rw_block: device %s: "
"only %d-char blocks implemented (%lu)\n",
kdevname(bh[0]->b_dev),
correct_size, bh[i]->b_size);
goto sorry;
}
 
/* Md remaps blocks now */
bh[i]->b_rdev = bh[i]->b_dev;
bh[i]->b_rsector=bh[i]->b_blocknr*(bh[i]->b_size >> 9);
#ifdef CONFIG_BLK_DEV_MD
if (major==MD_MAJOR &&
md_map (MINOR(bh[i]->b_dev), &bh[i]->b_rdev,
&bh[i]->b_rsector, bh[i]->b_size >> 9)) {
printk (KERN_ERR
"Bad md_map in ll_rw_block\n");
goto sorry;
}
#endif
}
 
if ((rw == WRITE || rw == WRITEA) && is_read_only(bh[0]->b_dev)) {
printk(KERN_NOTICE "Can't write to read-only device %s\n",
kdevname(bh[0]->b_dev));
goto sorry;
}
 
for (i = 0; i < nr; i++) {
if (bh[i]) {
set_bit(BH_Req, &bh[i]->b_state);
#ifdef CONFIG_BLK_DEV_MD
if (MAJOR(bh[i]->b_dev) == MD_MAJOR) {
md_make_request(MINOR (bh[i]->b_dev), rw, bh[i]);
continue;
}
#endif
make_request(MAJOR(bh[i]->b_rdev), rw, bh[i]);
}
}
return;
 
sorry:
for (i = 0; i < nr; i++) {
if (bh[i]) {
clear_bit(BH_Dirty, &bh[i]->b_state);
clear_bit(BH_Uptodate, &bh[i]->b_state);
}
}
return;
}
 
void ll_rw_swap_file(int rw, kdev_t dev, unsigned int *b, int nb, char *buf)
{
int i, j;
int buffersize;
int max_req;
unsigned long rsector;
kdev_t rdev;
struct request * req[8];
unsigned int major = MAJOR(dev);
struct semaphore sem = MUTEX_LOCKED;
 
if (major >= MAX_BLKDEV || !(blk_dev[major].request_fn)) {
printk(KERN_NOTICE "ll_rw_swap_file: trying to swap to"
" nonexistent block-device\n");
return;
}
max_req = NR_REQUEST;
switch (rw) {
case READ:
break;
case WRITE:
max_req = (NR_REQUEST * 2) / 3;
if (is_read_only(dev)) {
printk(KERN_NOTICE
"Can't swap to read-only device %s\n",
kdevname(dev));
return;
}
break;
default:
panic("ll_rw_swap: bad block dev cmd, must be R/W");
}
buffersize = PAGE_SIZE / nb;
 
if (major == LOOP_MAJOR)
max_req >>= 1;
for (j=0, i=0; i<nb;)
{
for (; j < 8 && i < nb; j++, i++, buf += buffersize)
{
rdev = dev;
rsector = b[i] * (buffersize >> 9);
#ifdef CONFIG_BLK_DEV_MD
if (major==MD_MAJOR &&
md_map (MINOR(dev), &rdev,
&rsector, buffersize >> 9)) {
printk (KERN_ERR
"Bad md_map in ll_rw_swap_file\n");
return;
}
#endif
if (j == 0) {
req[j] = get_request_wait(max_req, rdev);
} else {
cli();
req[j] = get_request(max_req, rdev);
sti();
if (req[j] == NULL)
break;
}
req[j]->cmd = rw;
req[j]->errors = 0;
req[j]->sector = rsector;
req[j]->nr_sectors = buffersize >> 9;
req[j]->current_nr_sectors = buffersize >> 9;
req[j]->buffer = buf;
req[j]->sem = &sem;
req[j]->bh = NULL;
req[j]->next = NULL;
add_request(MAJOR(rdev)+blk_dev,req[j]);
}
run_task_queue(&tq_disk);
while (j > 0) {
j--;
down(&sem);
}
}
}
 
int blk_dev_init(void)
{
struct request * req;
struct blk_dev_struct *dev;
 
for (dev = blk_dev + MAX_BLKDEV; dev-- != blk_dev;) {
dev->request_fn = NULL;
dev->current_request = NULL;
dev->plug.rq_status = RQ_INACTIVE;
dev->plug.cmd = -1;
dev->plug.next = NULL;
dev->plug_tq.routine = &unplug_device;
dev->plug_tq.data = dev;
}
 
req = all_requests + NR_REQUEST;
while (--req >= all_requests) {
req->rq_status = RQ_INACTIVE;
req->next = NULL;
}
memset(ro_bits,0,sizeof(ro_bits));
#ifdef CONFIG_BLK_DEV_RAM
rd_init();
#endif
#ifdef CONFIG_BLK_DEV_LOOP
loop_init();
#endif
#ifdef CONFIG_BLK_DEV_IDE
ide_init(); /* this MUST precede hd_init */
#endif
#ifdef CONFIG_BLK_DEV_HD
hd_init();
#endif
#ifdef CONFIG_BLK_DEV_XD
xd_init();
#endif
#ifdef CONFIG_BLK_DEV_FD
floppy_init();
#else
#if !defined(CONFIG_ARCH_ARC) && !defined(CONFIG_ARCH_SA110EVAL)
outb_p(0xc, 0x3f2);
#endif
#endif
#ifdef CONFIG_BLK_DEV_MD
md_init();
#endif CONFIG_BLK_DEV_MD
return 0;
}
/floppy.c
0,0 → 1,4276
/*
* linux/arch/arm/drivers/block/floppy.c
* [ was linux/drivers/block/floppy.c ]
*
* Copyright (C) 1991, 1992 Linus Torvalds
* Copyright (C) 1993, 1994 Alain Knaff
* Modifications Copyright (C) 1995 Russell King
*/
/*
* 02.12.91 - Changed to static variables to indicate need for reset
* and recalibrate. This makes some things easier (output_byte reset
* checking etc), and means less interrupt jumping in case of errors,
* so the code is hopefully easier to understand.
*/
 
/*
* This file is certainly a mess. I've tried my best to get it working,
* but I don't like programming floppies, and I have only one anyway.
* Urgel. I should check for more errors, and do more graceful error
* recovery. Seems there are problems with several drives. I've tried to
* correct them. No promises.
*/
 
/*
* As with hd.c, all routines within this file can (and will) be called
* by interrupts, so extreme caution is needed. A hardware interrupt
* handler may not sleep, or a kernel panic will happen. Thus I cannot
* call "floppy-on" directly, but have to set a special timer interrupt
* etc.
*/
 
/*
* 28.02.92 - made track-buffering routines, based on the routines written
* by entropy@wintermute.wpi.edu (Lawrence Foard). Linus.
*/
 
/*
* Automatic floppy-detection and formatting written by Werner Almesberger
* (almesber@nessie.cs.id.ethz.ch), who also corrected some problems with
* the floppy-change signal detection.
*/
 
/*
* 1992/7/22 -- Hennus Bergman: Added better error reporting, fixed
* FDC data overrun bug, added some preliminary stuff for vertical
* recording support.
*
* 1992/9/17: Added DMA allocation & DMA functions. -- hhb.
*
* TODO: Errors are still not counted properly.
*/
 
/* 1992/9/20
* Modifications for ``Sector Shifting'' by Rob Hooft (hooft@chem.ruu.nl)
* modeled after the freeware MS-DOS program fdformat/88 V1.8 by
* Christoph H. Hochst\"atter.
* I have fixed the shift values to the ones I always use. Maybe a new
* ioctl() should be created to be able to modify them.
* There is a bug in the driver that makes it impossible to format a
* floppy as the first thing after bootup.
*/
 
/*
* 1993/4/29 -- Linus -- cleaned up the timer handling in the kernel, and
* this helped the floppy driver as well. Much cleaner, and still seems to
* work.
*/
 
/* 1994/6/24 --bbroad-- added the floppy table entries and made
* minor modifications to allow 2.88 floppies to be run.
*/
 
/* 1994/7/13 -- Paul Vojta -- modified the probing code to allow three or more
* disk types.
*/
 
/*
* 1994/8/8 -- Alain Knaff -- Switched to fdpatch driver: Support for bigger
* format bug fixes, but unfortunately some new bugs too...
*/
 
/* 1994/9/17 -- Koen Holtman -- added logging of physical floppy write
* errors to allow safe writing by specialized programs.
*/
 
/* 1995/4/24 -- Dan Fandrich -- added support for Commodore 1581 3.5" disks
* by defining bit 1 of the "stretch" parameter to mean put sectors on the
* opposite side of the disk, leaving the sector IDs alone (i.e. Commodore's
* drives are "upside-down").
*/
 
/*
* 1995/8/26 -- Andreas Busse -- added Mips support.
*/
 
/*
* 1995/8/16 -- Russell King -- added ARM support.
*/
 
/*
* 1995/10/18 -- Ralf Baechle -- Portability cleanup; move machine dependent
* features to asm/floppy.h.
*/
 
 
#define FLOPPY_SANITY_CHECK
#undef FLOPPY_SILENT_DCL_CLEAR
 
#define REALLY_SLOW_IO
 
#define DEBUGT 2
#define DCL_DEBUG /* debug disk change line */
 
/* do print messages for unexpected interrupts */
static int print_unex=1;
#include <linux/utsname.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/timer.h>
#include <linux/tqueue.h>
#define FDPATCHES
#include <linux/fdreg.h>
#include <linux/fd.h>
 
#define OLDFDRAWCMD 0x020d /* send a raw command to the FDC */
 
struct old_floppy_raw_cmd {
void *data;
long length;
 
unsigned char rate;
unsigned char flags;
unsigned char cmd_count;
unsigned char cmd[9];
unsigned char reply_count;
unsigned char reply[7];
int track;
};
 
#include <linux/errno.h>
#include <linux/malloc.h>
#include <linux/mm.h>
#include <linux/string.h>
#include <linux/fcntl.h>
#include <linux/delay.h>
#ifndef CONFIG_ARM
#include <linux/mc146818rtc.h> /* CMOS defines */
#endif
#include <linux/ioport.h>
#include <linux/interrupt.h>
 
#include <asm/dma.h>
#include <asm/irq.h>
#include <asm/system.h>
#include <asm/io.h>
#include <asm/segment.h>
 
/* the following is the mask of allowed drives. By default units 2 and
* 3 of both floppy controllers are disabled, because switching on the
* motor of these drives causes system hangs on some PCI computers. drive
* 0 is the low bit (0x1), and drive 7 is the high bit (0x80). Bits are on if
* a drive is allowed. */
static int FLOPPY_IRQ=IRQ_FLOPPYDISK;
static int FLOPPY_DMA=DMA_FLOPPY;
static int allowed_drive_mask = 0x33;
static int use_virtual_dma=0; /* virtual DMA for Intel */
static unsigned short virtual_dma_port=0x3f0;
void floppy_interrupt(int irq, void *dev_id, struct pt_regs * regs);
static int set_dor(int fdc, char mask, char data);
static inline int __get_order(unsigned long size);
#include <asm/floppy.h>
 
 
#define MAJOR_NR FLOPPY_MAJOR
 
#include <linux/blk.h>
#include <linux/cdrom.h> /* for the compatibility eject ioctl */
 
 
#ifndef FLOPPY_MOTOR_MASK
#define FLOPPY_MOTOR_MASK 0xf0
#endif
 
#ifndef fd_get_dma_residue
#define fd_get_dma_residue() get_dma_residue(FLOPPY_DMA)
#endif
 
/* Dma Memory related stuff */
 
/* Pure 2^n version of get_order */
static inline int __get_order(unsigned long size)
{
int order;
 
size = (size-1) >> (PAGE_SHIFT-1);
order = -1;
do {
size >>= 1;
order++;
} while (size);
return order;
}
 
#ifndef fd_dma_mem_free
#define fd_dma_mem_free(addr, size) free_pages(addr, __get_order(size))
#endif
 
#ifndef fd_dma_mem_alloc
#define fd_dma_mem_alloc(size) __get_dma_pages(GFP_KERNEL,__get_order(size))
#endif
 
/* End dma memory related stuff */
 
static unsigned int fake_change = 0;
static int initialising=1;
 
static inline int TYPE(kdev_t x) {
return (MINOR(x)>>2) & 0x1f;
}
static inline int DRIVE(kdev_t x) {
return (MINOR(x)&0x03) | ((MINOR(x)&0x80) >> 5);
}
#define ITYPE(x) (((x)>>2) & 0x1f)
#define TOMINOR(x) ((x & 3) | ((x & 4) << 5))
#define UNIT(x) ((x) & 0x03) /* drive on fdc */
#define FDC(x) (((x) & 0x04) >> 2) /* fdc of drive */
#define REVDRIVE(fdc, unit) ((unit) + ((fdc) << 2))
/* reverse mapping from unit and fdc to drive */
#define DP (&drive_params[current_drive])
#define DRS (&drive_state[current_drive])
#define DRWE (&write_errors[current_drive])
#define FDCS (&fdc_state[fdc])
#define CLEARF(x) (clear_bit(x##_BIT, &DRS->flags))
#define SETF(x) (set_bit(x##_BIT, &DRS->flags))
#define TESTF(x) (test_bit(x##_BIT, &DRS->flags))
 
#define UDP (&drive_params[drive])
#define UDRS (&drive_state[drive])
#define UDRWE (&write_errors[drive])
#define UFDCS (&fdc_state[FDC(drive)])
#define UCLEARF(x) (clear_bit(x##_BIT, &UDRS->flags))
#define USETF(x) (set_bit(x##_BIT, &UDRS->flags))
#define UTESTF(x) (test_bit(x##_BIT, &UDRS->flags))
 
#define DPRINT(format, args...) printk(DEVICE_NAME "%d: " format, current_drive , ## args)
 
#define PH_HEAD(floppy,head) (((((floppy)->stretch & 2) >>1) ^ head) << 2)
#define STRETCH(floppy) ((floppy)->stretch & FD_STRETCH)
 
#define CLEARSTRUCT(x) memset((x), 0, sizeof(*(x)))
 
#define INT_OFF save_flags(flags); cli()
#define INT_ON restore_flags(flags)
 
/* read/write */
#define COMMAND raw_cmd->cmd[0]
#define DR_SELECT raw_cmd->cmd[1]
#define TRACK raw_cmd->cmd[2]
#define HEAD raw_cmd->cmd[3]
#define SECTOR raw_cmd->cmd[4]
#define SIZECODE raw_cmd->cmd[5]
#define SECT_PER_TRACK raw_cmd->cmd[6]
#define GAP raw_cmd->cmd[7]
#define SIZECODE2 raw_cmd->cmd[8]
#define NR_RW 9
 
/* format */
#define F_SIZECODE raw_cmd->cmd[2]
#define F_SECT_PER_TRACK raw_cmd->cmd[3]
#define F_GAP raw_cmd->cmd[4]
#define F_FILL raw_cmd->cmd[5]
#define NR_F 6
 
/*
* Maximum disk size (in kilobytes). This default is used whenever the
* current disk size is unknown.
* [Now it is rather a minimum]
*/
#define MAX_DISK_SIZE 4 /* 3984*/
 
#define K_64 0x10000 /* 64KB */
 
/*
* globals used by 'result()'
*/
#define MAX_REPLIES 16
static unsigned char reply_buffer[MAX_REPLIES];
static int inr; /* size of reply buffer, when called from interrupt */
#define ST0 (reply_buffer[0])
#define ST1 (reply_buffer[1])
#define ST2 (reply_buffer[2])
#define ST3 (reply_buffer[0]) /* result of GETSTATUS */
#define R_TRACK (reply_buffer[3])
#define R_HEAD (reply_buffer[4])
#define R_SECTOR (reply_buffer[5])
#define R_SIZECODE (reply_buffer[6])
 
#define SEL_DLY (2*HZ/100)
 
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
/*
* this struct defines the different floppy drive types.
*/
static struct {
struct floppy_drive_params params;
const char *name; /* name printed while booting */
} default_drive_params[]= {
/* NOTE: the time values in jiffies should be in msec!
CMOS drive type
| Maximum data rate supported by drive type
| | Head load time, msec
| | | Head unload time, msec (not used)
| | | | Step rate interval, usec
| | | | | Time needed for spinup time (jiffies)
| | | | | | Timeout for spinning down (jiffies)
| | | | | | | Spindown offset (where disk stops)
| | | | | | | | Select delay
| | | | | | | | | RPS
| | | | | | | | | | Max number of tracks
| | | | | | | | | | | Interrupt timeout
| | | | | | | | | | | | Max nonintlv. sectors
| | | | | | | | | | | | | -Max Errors- flags */
{{0, 500, 16, 16, 8000, 1*HZ, 3*HZ, 0, SEL_DLY, 5, 80, 3*HZ, 20, {3,1,2,0,2}, 0,
0, { 7, 4, 8, 2, 1, 5, 3,10}, 3*HZ/2, 0 }, "unknown" },
 
{{1, 300, 16, 16, 8000, 1*HZ, 3*HZ, 0, SEL_DLY, 5, 40, 3*HZ, 17, {3,1,2,0,2}, 0,
0, { 1, 0, 0, 0, 0, 0, 0, 0}, 3*HZ/2, 1 }, "360K PC" }, /*5 1/4 360 KB PC*/
 
{{2, 500, 16, 16, 6000, 4*HZ/10, 3*HZ, 14, SEL_DLY, 6, 83, 3*HZ, 17, {3,1,2,0,2}, 0,
0, { 2, 5, 6,23,10,20,11, 0}, 3*HZ/2, 2 }, "1.2M" }, /*5 1/4 HD AT*/
 
{{3, 250, 16, 16, 3000, 1*HZ, 3*HZ, 0, SEL_DLY, 5, 83, 3*HZ, 20, {3,1,2,0,2}, 0,
0, { 4,22,21,30, 3, 0, 0, 0}, 3*HZ/2, 4 }, "720k" }, /*3 1/2 DD*/
 
{{4, 500, 16, 16, 4000, 4*HZ/10, 3*HZ, 10, SEL_DLY, 5, 83, 3*HZ, 20, {3,1,2,0,2}, 0,
0, { 7, 4,25,22,31,21,29,11}, 3*HZ/2, 7 }, "1.44M" }, /*3 1/2 HD*/
 
{{5, 1000, 15, 8, 3000, 4*HZ/10, 3*HZ, 10, SEL_DLY, 5, 83, 3*HZ, 40, {3,1,2,0,2}, 0,
0, { 7, 8, 4,25,28,22,31,21}, 3*HZ/2, 8 }, "2.88M AMI BIOS" }, /*3 1/2 ED*/
 
{{6, 1000, 15, 8, 3000, 4*HZ/10, 3*HZ, 10, SEL_DLY, 5, 83, 3*HZ, 40, {3,1,2,0,2}, 0,
0, { 7, 8, 4,25,28,22,31,21}, 3*HZ/2, 8 }, "2.88M" } /*3 1/2 ED*/
/* | --autodetected formats--- | | |
* read_track | | Name printed when booting
* | Native format
* Frequency of disk change checks */
};
 
static struct floppy_drive_params drive_params[N_DRIVE];
static struct floppy_drive_struct drive_state[N_DRIVE];
static struct floppy_write_errors write_errors[N_DRIVE];
static struct floppy_raw_cmd *raw_cmd, default_raw_cmd;
 
/*
* This struct defines the different floppy types.
*
* Bit 0 of 'stretch' tells if the tracks need to be doubled for some
* types (e.g. 360kB diskette in 1.2MB drive, etc.). Bit 1 of 'stretch'
* tells if the disk is in Commodore 1581 format, which means side 0 sectors
* are located on side 1 of the disk but with a side 0 ID, and vice-versa.
* This is the same as the Sharp MZ-80 5.25" CP/M disk format, except that the
* 1581's logical side 0 is on physical side 1, whereas the Sharp's logical
* side 0 is on physical side 0 (but with the misnamed sector IDs).
* 'stretch' should probably be renamed to something more general, like
* 'options'. Other parameters should be self-explanatory (see also
* setfdprm(8)).
*/
/*
Size
| Sectors per track
| | Head
| | | Tracks
| | | | Stretch
| | | | | Gap 1 size
| | | | | | Data rate, | 0x40 for perp
| | | | | | | Spec1 (stepping rate, head unload
| | | | | | | | /fmt gap (gap2) */
static struct floppy_struct floppy_type[32] = {
{ 0, 0,0, 0,0,0x00,0x00,0x00,0x00,NULL }, /* 0 no testing */
{ 720, 9,2,40,0,0x2A,0x02,0xDF,0x50,"d360" }, /* 1 360KB PC */
{ 2400,15,2,80,0,0x1B,0x00,0xDF,0x54,"h1200" }, /* 2 1.2MB AT */
{ 720, 9,1,80,0,0x2A,0x02,0xDF,0x50,"D360" }, /* 3 360KB SS 3.5" */
{ 1440, 9,2,80,0,0x2A,0x02,0xDF,0x50,"D720" }, /* 4 720KB 3.5" */
{ 720, 9,2,40,1,0x23,0x01,0xDF,0x50,"h360" }, /* 5 360KB AT */
{ 1440, 9,2,80,0,0x23,0x01,0xDF,0x50,"h720" }, /* 6 720KB AT */
{ 2880,18,2,80,0,0x1B,0x00,0xCF,0x6C,"H1440" }, /* 7 1.44MB 3.5" */
{ 5760,36,2,80,0,0x1B,0x43,0xAF,0x54,"E2880" }, /* 8 2.88MB 3.5" */
{ 6240,39,2,80,0,0x1B,0x43,0xAF,0x28,"E3120" }, /* 9 3.12MB 3.5" */
 
{ 2880,18,2,80,0,0x25,0x00,0xDF,0x02,"h1440" }, /* 10 1.44MB 5.25" */
{ 3360,21,2,80,0,0x1C,0x00,0xCF,0x0C,"H1680" }, /* 11 1.68MB 3.5" */
{ 820,10,2,41,1,0x25,0x01,0xDF,0x2E,"h410" }, /* 12 410KB 5.25" */
{ 1640,10,2,82,0,0x25,0x02,0xDF,0x2E,"H820" }, /* 13 820KB 3.5" */
{ 2952,18,2,82,0,0x25,0x00,0xDF,0x02,"h1476" }, /* 14 1.48MB 5.25" */
{ 3444,21,2,82,0,0x25,0x00,0xDF,0x0C,"H1722" }, /* 15 1.72MB 3.5" */
{ 840,10,2,42,1,0x25,0x01,0xDF,0x2E,"h420" }, /* 16 420KB 5.25" */
{ 1660,10,2,83,0,0x25,0x02,0xDF,0x2E,"H830" }, /* 17 830KB 3.5" */
{ 2988,18,2,83,0,0x25,0x00,0xDF,0x02,"h1494" }, /* 18 1.49MB 5.25" */
{ 3486,21,2,83,0,0x25,0x00,0xDF,0x0C,"H1743" }, /* 19 1.74 MB 3.5" */
 
{ 1760,11,2,80,0,0x1C,0x09,0xCF,0x00,"h880" }, /* 20 880KB 5.25" */
{ 2080,13,2,80,0,0x1C,0x01,0xCF,0x00,"D1040" }, /* 21 1.04MB 3.5" */
{ 2240,14,2,80,0,0x1C,0x19,0xCF,0x00,"D1120" }, /* 22 1.12MB 3.5" */
{ 3200,20,2,80,0,0x1C,0x20,0xCF,0x2C,"h1600" }, /* 23 1.6MB 5.25" */
{ 3520,22,2,80,0,0x1C,0x08,0xCF,0x2e,"H1760" }, /* 24 1.76MB 3.5" */
{ 3840,24,2,80,0,0x1C,0x20,0xCF,0x00,"H1920" }, /* 25 1.92MB 3.5" */
{ 6400,40,2,80,0,0x25,0x5B,0xCF,0x00,"E3200" }, /* 26 3.20MB 3.5" */
{ 7040,44,2,80,0,0x25,0x5B,0xCF,0x00,"E3520" }, /* 27 3.52MB 3.5" */
{ 7680,48,2,80,0,0x25,0x63,0xCF,0x00,"E3840" }, /* 28 3.84MB 3.5" */
 
{ 3680,23,2,80,0,0x1C,0x10,0xCF,0x00,"H1840" }, /* 29 1.84MB 3.5" */
{ 1600,10,2,80,0,0x25,0x02,0xDF,0x2E,"D800" }, /* 30 800KB 3.5" */
{ 3200,20,2,80,0,0x1C,0x00,0xCF,0x2C,"H1600" }, /* 31 1.6MB 3.5" */
};
 
#define NUMBER(x) (sizeof(x) / sizeof(*(x)))
#define SECTSIZE (_FD_SECTSIZE(*floppy))
 
/* Auto-detection: Disk type used until the next media change occurs. */
static struct floppy_struct *current_type[N_DRIVE] = {
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL
};
 
/*
* User-provided type information. current_type points to
* the respective entry of this array.
*/
static struct floppy_struct user_params[N_DRIVE];
 
static int floppy_sizes[256];
static int floppy_blocksizes[256] = { 0, };
 
/*
* The driver is trying to determine the correct media format
* while probing is set. rw_interrupt() clears it after a
* successful access.
*/
static int probing = 0;
 
/* Synchronization of FDC access. */
#define FD_COMMAND_NONE -1
#define FD_COMMAND_ERROR 2
#define FD_COMMAND_OKAY 3
 
static volatile int command_status = FD_COMMAND_NONE, fdc_busy = 0;
static struct wait_queue *fdc_wait = NULL, *command_done = NULL;
#define NO_SIGNAL (!(current->signal & ~current->blocked) || !interruptible)
#define CALL(x) if ((x) == -EINTR) return -EINTR
#define ECALL(x) if ((ret = (x))) return ret;
#define _WAIT(x,i) CALL(ret=wait_til_done((x),i))
#define WAIT(x) _WAIT((x),interruptible)
#define IWAIT(x) _WAIT((x),1)
 
/* Errors during formatting are counted here. */
static int format_errors;
 
/* Format request descriptor. */
static struct format_descr format_req;
 
/*
* Rate is 0 for 500kb/s, 1 for 300kbps, 2 for 250kbps
* Spec1 is 0xSH, where S is stepping rate (F=1ms, E=2ms, D=3ms etc),
* H is head unload time (1=16ms, 2=32ms, etc)
*/
 
/*
* Track buffer
* Because these are written to by the DMA controller, they must
* not contain a 64k byte boundary crossing, or data will be
* corrupted/lost.
*/
static char *floppy_track_buffer=NULL;
static int max_buffer_sectors=0;
 
static int *errors;
typedef void (*done_f)(int);
static struct cont_t {
void (*interrupt)(void); /* this is called after the interrupt of the
* main command */
void (*redo)(void); /* this is called to retry the operation */
void (*error)(void); /* this is called to tally an error */
done_f done; /* this is called to say if the operation has
* succeeded/failed */
} *cont=NULL;
 
static void floppy_ready(void);
static void floppy_start(void);
static void process_fd_request(void);
static void recalibrate_floppy(void);
static void floppy_shutdown(void);
 
static int floppy_grab_irq_and_dma(void);
static void floppy_release_irq_and_dma(void);
 
/*
* The "reset" variable should be tested whenever an interrupt is scheduled,
* after the commands have been sent. This is to ensure that the driver doesn't
* get wedged when the interrupt doesn't come because of a failed command.
* reset doesn't need to be tested before sending commands, because
* output_byte is automatically disabled when reset is set.
*/
#define CHECK_RESET { if (FDCS->reset){ reset_fdc(); return; } }
static void reset_fdc(void);
 
/*
* These are global variables, as that's the easiest way to give
* information to interrupts. They are the data used for the current
* request.
*/
#define NO_TRACK -1
#define NEED_1_RECAL -2
#define NEED_2_RECAL -3
 
/* */
static int usage_count = 0;
 
 
/* buffer related variables */
static int buffer_track = -1;
static int buffer_drive = -1;
static int buffer_min = -1;
static int buffer_max = -1;
 
/* fdc related variables, should end up in a struct */
static struct floppy_fdc_state fdc_state[N_FDC];
static int fdc; /* current fdc */
 
static struct floppy_struct *_floppy = floppy_type;
static unsigned char current_drive = 0;
static long current_count_sectors = 0;
static unsigned char sector_t; /* sector in track */
 
#ifndef fd_eject
#define fd_eject(x) -EINVAL
#endif
 
#ifdef DEBUGT
static long unsigned debugtimer;
#endif
 
/*
* Debugging
* =========
*/
static inline void set_debugt(void)
{
#ifdef DEBUGT
debugtimer = jiffies;
#endif
}
 
static inline void debugt(const char *message)
{
#ifdef DEBUGT
if (DP->flags & DEBUGT)
printk("%s dtime=%lu\n", message, jiffies-debugtimer);
#endif
}
 
typedef void (*timeout_fn)(unsigned long);
static struct timer_list fd_timeout ={ NULL, NULL, 0, 0,
(timeout_fn) floppy_shutdown };
 
static const char *timeout_message;
 
#ifdef FLOPPY_SANITY_CHECK
static void is_alive(const char *message)
{
/* this routine checks whether the floppy driver is "alive" */
if (fdc_busy && command_status < 2 && !fd_timeout.prev){
DPRINT("timeout handler died: %s\n",message);
}
}
#endif
 
#ifdef FLOPPY_SANITY_CHECK
 
#define OLOGSIZE 20
 
static void (*lasthandler)(void) = NULL;
static int interruptjiffies=0;
static int resultjiffies=0;
static int resultsize=0;
static int lastredo=0;
 
static struct output_log {
unsigned char data;
unsigned char status;
unsigned long jiffies;
} output_log[OLOGSIZE];
 
static int output_log_pos=0;
#endif
 
#define CURRENTD -1
#define MAXTIMEOUT -2
 
static void reschedule_timeout(int drive, const char *message, int marg)
{
if (drive == CURRENTD)
drive = current_drive;
del_timer(&fd_timeout);
if (drive < 0 || drive > N_DRIVE) {
fd_timeout.expires = jiffies + 20*HZ;
drive=0;
} else
fd_timeout.expires = jiffies + UDP->timeout;
add_timer(&fd_timeout);
if (UDP->flags & FD_DEBUG){
DPRINT("reschedule timeout ");
printk(message, marg);
printk("\n");
}
timeout_message = message;
}
 
static int maximum(int a, int b)
{
if(a > b)
return a;
else
return b;
}
#define INFBOUND(a,b) (a)=maximum((a),(b));
 
static int minimum(int a, int b)
{
if(a < b)
return a;
else
return b;
}
#define SUPBOUND(a,b) (a)=minimum((a),(b));
 
 
/*
* Bottom half floppy driver.
* ==========================
*
* This part of the file contains the code talking directly to the hardware,
* and also the main service loop (seek-configure-spinup-command)
*/
 
/*
* disk change.
* This routine is responsible for maintaining the FD_DISK_CHANGE flag,
* and the last_checked date.
*
* last_checked is the date of the last check which showed 'no disk change'
* FD_DISK_CHANGE is set under two conditions:
* 1. The floppy has been changed after some i/o to that floppy already
* took place.
* 2. No floppy disk is in the drive. This is done in order to ensure that
* requests are quickly flushed in case there is no disk in the drive. It
* follows that FD_DISK_CHANGE can only be cleared if there is a disk in
* the drive.
*
* For 1., maxblock is observed. Maxblock is 0 if no i/o has taken place yet.
* For 2., FD_DISK_NEWCHANGE is watched. FD_DISK_NEWCHANGE is cleared on
* each seek. If a disk is present, the disk change line should also be
* cleared on each seek. Thus, if FD_DISK_NEWCHANGE is clear, but the disk
* change line is set, this means either that no disk is in the drive, or
* that it has been removed since the last seek.
*
* This means that we really have a third possibility too:
* The floppy has been changed after the last seek.
*/
 
static int disk_change(int drive)
{
int fdc=FDC(drive);
#ifdef FLOPPY_SANITY_CHECK
if (jiffies - UDRS->select_date < UDP->select_delay)
DPRINT("WARNING disk change called early\n");
if (!(FDCS->dor & (0x10 << UNIT(drive))) ||
(FDCS->dor & 3) != UNIT(drive) ||
fdc != FDC(drive)){
DPRINT("probing disk change on unselected drive\n");
DPRINT("drive=%d fdc=%d dor=%x\n",drive, FDC(drive),
(unsigned int)FDCS->dor);
}
#endif
 
#ifdef DCL_DEBUG
if (UDP->flags & FD_DEBUG){
DPRINT("checking disk change line for drive %d\n",drive);
DPRINT("jiffies=%ld\n", jiffies);
DPRINT("disk change line=%x\n",fd_inb(FD_DIR)&0x80);
DPRINT("flags=%x\n",UDRS->flags);
}
#endif
if (UDP->flags & FD_BROKEN_DCL)
return UTESTF(FD_DISK_CHANGED);
if ((fd_inb(FD_DIR) ^ UDP->flags) & 0x80){
USETF(FD_VERIFY); /* verify write protection */
if (UDRS->maxblock){
/* mark it changed */
USETF(FD_DISK_CHANGED);
}
 
/* invalidate its geometry */
if (UDRS->keep_data >= 0) {
if ((UDP->flags & FTD_MSG) &&
current_type[drive] != NULL)
DPRINT("Disk type is undefined after "
"disk change\n");
current_type[drive] = NULL;
floppy_sizes[TOMINOR(drive)] = MAX_DISK_SIZE;
}
 
/*USETF(FD_DISK_NEWCHANGE);*/
return 1;
} else {
UDRS->last_checked=jiffies;
UCLEARF(FD_DISK_NEWCHANGE);
}
return 0;
}
 
static inline int is_selected(int dor, int unit)
{
return ((dor & (0x10 << unit)) && (dor &3) == unit);
}
 
static int set_dor(int fdc, char mask, char data)
{
register unsigned char drive, unit, newdor,olddor;
 
if (FDCS->address == -1)
return -1;
 
olddor = FDCS->dor;
newdor = (olddor & mask) | data;
if (newdor != olddor){
unit = olddor & 0x3;
if (is_selected(olddor, unit) && !is_selected(newdor,unit)){
drive = REVDRIVE(fdc,unit);
#ifdef DCL_DEBUG
if (UDP->flags & FD_DEBUG){
DPRINT("calling disk change from set_dor\n");
}
#endif
disk_change(drive);
}
FDCS->dor = newdor;
fd_setdor(newdor);
 
unit = newdor & 0x3;
if (!is_selected(olddor, unit) && is_selected(newdor,unit)){
drive = REVDRIVE(fdc,unit);
UDRS->select_date = jiffies;
}
}
if (newdor & FLOPPY_MOTOR_MASK)
floppy_grab_irq_and_dma();
if (olddor & FLOPPY_MOTOR_MASK)
floppy_release_irq_and_dma();
return olddor;
}
 
static void twaddle(void)
{
if (DP->select_delay)
return;
fd_setdor(FDCS->dor & ~(0x10<<UNIT(current_drive)));
fd_setdor(FDCS->dor);
DRS->select_date = jiffies;
}
 
/* reset all driver information about the current fdc. This is needed after
* a reset, and after a raw command. */
static void reset_fdc_info(int mode)
{
int drive;
 
FDCS->spec1 = FDCS->spec2 = -1;
FDCS->need_configure = 1;
FDCS->perp_mode = 1;
FDCS->rawcmd = 0;
for (drive = 0; drive < N_DRIVE; drive++)
if (FDC(drive) == fdc &&
(mode || UDRS->track != NEED_1_RECAL))
UDRS->track = NEED_2_RECAL;
}
 
/* selects the fdc and drive, and enables the fdc's input/dma. */
static void set_fdc(int drive)
{
if (drive >= 0 && drive < N_DRIVE){
fdc = FDC(drive);
current_drive = drive;
}
if (fdc != 1 && fdc != 0) {
printk("bad fdc value\n");
return;
}
set_dor(fdc,~0,8);
#if N_FDC > 1
set_dor(1-fdc, ~8, 0);
#endif
if (FDCS->rawcmd == 2)
reset_fdc_info(1);
if (fd_inb(FD_STATUS) != STATUS_READY)
FDCS->reset = 1;
}
 
/* locks the driver */
static int lock_fdc(int drive, int interruptible)
{
unsigned long flags;
 
if (!usage_count){
printk("trying to lock fdc while usage count=0\n");
return -1;
}
floppy_grab_irq_and_dma();
INT_OFF;
while (fdc_busy && NO_SIGNAL)
interruptible_sleep_on(&fdc_wait);
if (fdc_busy){
INT_ON;
return -EINTR;
}
fdc_busy = 1;
INT_ON;
command_status = FD_COMMAND_NONE;
reschedule_timeout(drive, "lock fdc", 0);
set_fdc(drive);
return 0;
}
 
#define LOCK_FDC(drive,interruptible) \
if (lock_fdc(drive,interruptible)) return -EINTR;
 
 
/* unlocks the driver */
static inline void unlock_fdc(void)
{
raw_cmd = 0;
if (!fdc_busy)
DPRINT("FDC access conflict!\n");
 
if (DEVICE_INTR)
DPRINT("device interrupt still active at FDC release: %p!\n",
DEVICE_INTR);
command_status = FD_COMMAND_NONE;
del_timer(&fd_timeout);
cont = NULL;
fdc_busy = 0;
floppy_release_irq_and_dma();
wake_up(&fdc_wait);
}
 
/* switches the motor off after a given timeout */
static void motor_off_callback(unsigned long nr)
{
unsigned char mask = ~(0x10 << UNIT(nr));
 
set_dor(FDC(nr), mask, 0);
}
 
static struct timer_list motor_off_timer[N_DRIVE] = {
{ NULL, NULL, 0, 0, motor_off_callback },
{ NULL, NULL, 0, 1, motor_off_callback },
{ NULL, NULL, 0, 2, motor_off_callback },
{ NULL, NULL, 0, 3, motor_off_callback },
{ NULL, NULL, 0, 4, motor_off_callback },
{ NULL, NULL, 0, 5, motor_off_callback },
{ NULL, NULL, 0, 6, motor_off_callback },
{ NULL, NULL, 0, 7, motor_off_callback }
};
 
/* schedules motor off */
static void floppy_off(unsigned int drive)
{
unsigned long volatile delta;
register int fdc=FDC(drive);
 
if (!(FDCS->dor & (0x10 << UNIT(drive))))
return;
 
del_timer(motor_off_timer+drive);
 
/* make spindle stop in a position which minimizes spinup time
* next time */
if (UDP->rps){
delta = jiffies - UDRS->first_read_date + HZ -
UDP->spindown_offset;
delta = ((delta * UDP->rps) % HZ) / UDP->rps;
motor_off_timer[drive].expires = jiffies + UDP->spindown - delta;
}
add_timer(motor_off_timer+drive);
}
 
/*
* cycle through all N_DRIVE floppy drives, for disk change testing.
* stopping at current drive. This is done before any long operation, to
* be sure to have up to date disk change information.
*/
static void scandrives(void)
{
int i, drive, saved_drive;
 
if (DP->select_delay)
return;
 
saved_drive = current_drive;
for (i=0; i < N_DRIVE; i++){
drive = (saved_drive + i + 1) % N_DRIVE;
if (UDRS->fd_ref == 0 || UDP->select_delay != 0)
continue; /* skip closed drives */
set_fdc(drive);
if (!(set_dor(fdc, ~3, UNIT(drive) | (0x10 << UNIT(drive))) &
(0x10 << UNIT(drive))))
/* switch the motor off again, if it was off to
* begin with */
set_dor(fdc, ~(0x10 << UNIT(drive)), 0);
}
set_fdc(saved_drive);
}
 
static void empty(void)
{
}
 
static struct tq_struct floppy_tq =
{ 0, 0, 0, 0 };
 
static struct timer_list fd_timer ={ NULL, NULL, 0, 0, 0 };
 
static void cancel_activity(void)
{
CLEAR_INTR;
floppy_tq.routine = (void *)(void *) empty;
del_timer(&fd_timer);
}
 
/* this function makes sure that the disk stays in the drive during the
* transfer */
static void fd_watchdog(void)
{
#ifdef DCL_DEBUG
if (DP->flags & FD_DEBUG){
DPRINT("calling disk change from watchdog\n");
}
#endif
 
if (disk_change(current_drive)){
DPRINT("disk removed during i/o\n");
cancel_activity();
cont->done(0);
reset_fdc();
} else {
del_timer(&fd_timer);
fd_timer.function = (timeout_fn) fd_watchdog;
fd_timer.expires = jiffies + HZ / 10;
add_timer(&fd_timer);
}
}
 
static void main_command_interrupt(void)
{
del_timer(&fd_timer);
cont->interrupt();
}
 
/* waits for a delay (spinup or select) to pass */
static int wait_for_completion(int delay, timeout_fn function)
{
if (FDCS->reset){
reset_fdc(); /* do the reset during sleep to win time
* if we don't need to sleep, it's a good
* occasion anyways */
return 1;
}
 
if ((signed) (jiffies - delay) < 0){
del_timer(&fd_timer);
fd_timer.function = function;
fd_timer.expires = delay;
add_timer(&fd_timer);
return 1;
}
return 0;
}
 
static int hlt_disabled=0;
static void floppy_disable_hlt(void)
{
unsigned long flags;
 
INT_OFF;
if (!hlt_disabled){
hlt_disabled=1;
#ifdef HAVE_DISABLE_HLT
disable_hlt();
#endif
}
INT_ON;
}
 
static void floppy_enable_hlt(void)
{
unsigned long flags;
 
INT_OFF;
if (hlt_disabled){
hlt_disabled=0;
#ifdef HAVE_DISABLE_HLT
enable_hlt();
#endif
}
INT_ON;
}
 
 
static void setup_DMA(void)
{
unsigned long flags;
 
#ifdef FLOPPY_SANITY_CHECK
if (raw_cmd->length == 0){
int i;
 
printk("zero dma transfer size:");
for (i=0; i < raw_cmd->cmd_count; i++)
printk("%x,", raw_cmd->cmd[i]);
printk("\n");
cont->done(0);
FDCS->reset = 1;
return;
}
#if 0
if ((long) raw_cmd->kernel_data % 512){
printk("non aligned address: %p\n", raw_cmd->kernel_data);
cont->done(0);
FDCS->reset=1;
return;
}
if (CROSS_64KB(raw_cmd->kernel_data, raw_cmd->length)) {
printk("DMA crossing 64-K boundary %p-%p\n",
raw_cmd->kernel_data,
raw_cmd->kernel_data + raw_cmd->length);
cont->done(0);
FDCS->reset=1;
return;
}
#endif
#endif
INT_OFF;
fd_disable_dma();
fd_clear_dma_ff();
fd_cacheflush(raw_cmd->kernel_data, raw_cmd->length);
fd_set_dma_mode((raw_cmd->flags & FD_RAW_READ)?
DMA_MODE_READ : DMA_MODE_WRITE);
fd_set_dma_addr(virt_to_bus(raw_cmd->kernel_data));
fd_set_dma_count(raw_cmd->length);
virtual_dma_port = FDCS->address;
fd_enable_dma();
INT_ON;
floppy_disable_hlt();
}
 
void show_floppy(void);
 
/* waits until the fdc becomes ready */
static int wait_til_ready(void)
{
int counter, status;
if(FDCS->reset)
return -1;
for (counter = 0; counter < 10000; counter++) {
status = fd_inb(FD_STATUS);
if (status & STATUS_READY)
return status;
}
if (!initialising) {
DPRINT("Getstatus times out (%x) on fdc %d\n",
status, fdc);
show_floppy();
}
FDCS->reset = 1;
return -1;
}
 
/* sends a command byte to the fdc */
static int output_byte(char byte)
{
int status;
 
if ((status = wait_til_ready()) < 0)
return -1;
if ((status & (STATUS_READY|STATUS_DIR|STATUS_DMA)) == STATUS_READY){
fd_outb(byte,FD_DATA);
#ifdef FLOPPY_SANITY_CHECK
output_log[output_log_pos].data = byte;
output_log[output_log_pos].status = status;
output_log[output_log_pos].jiffies = jiffies;
output_log_pos = (output_log_pos + 1) % OLOGSIZE;
#endif
return 0;
}
FDCS->reset = 1;
if (!initialising) {
DPRINT("Unable to send byte %x to FDC. Fdc=%x Status=%x\n",
byte, fdc, status);
show_floppy();
}
return -1;
}
#define LAST_OUT(x) if (output_byte(x)<0){ reset_fdc();return;}
 
/* gets the response from the fdc */
static int result(void)
{
int i, status;
 
for(i=0; i < MAX_REPLIES; i++) {
if ((status = wait_til_ready()) < 0)
break;
status &= STATUS_DIR|STATUS_READY|STATUS_BUSY|STATUS_DMA;
if ((status & ~STATUS_BUSY) == STATUS_READY){
#ifdef FLOPPY_SANITY_CHECK
resultjiffies = jiffies;
resultsize = i;
#endif
return i;
}
if (status == (STATUS_DIR|STATUS_READY|STATUS_BUSY))
reply_buffer[i] = fd_inb(FD_DATA);
else
break;
}
if(!initialising) {
DPRINT("get result error. Fdc=%d Last status=%x Read bytes=%d\n",
fdc, status, i);
show_floppy();
}
FDCS->reset = 1;
return -1;
}
 
#define MORE_OUTPUT -2
/* does the fdc need more output? */
static int need_more_output(void)
{
int status;
if( (status = wait_til_ready()) < 0)
return -1;
if ((status & (STATUS_READY|STATUS_DIR|STATUS_DMA)) == STATUS_READY)
return MORE_OUTPUT;
return result();
}
 
/* Set perpendicular mode as required, based on data rate, if supported.
* 82077 Now tested. 1Mbps data rate only possible with 82077-1.
*/
static inline void perpendicular_mode(void)
{
unsigned char perp_mode;
 
if (raw_cmd->rate & 0x40){
switch(raw_cmd->rate & 3){
case 0:
perp_mode=2;
break;
case 3:
perp_mode=3;
break;
default:
DPRINT("Invalid data rate for perpendicular mode!\n");
cont->done(0);
FDCS->reset = 1; /* convenient way to return to
* redo without to much hassle (deep
* stack et al. */
return;
}
} else
perp_mode = 0;
 
if (FDCS->perp_mode == perp_mode)
return;
if (FDCS->version >= FDC_82077_ORIG) {
output_byte(FD_PERPENDICULAR);
output_byte(perp_mode);
FDCS->perp_mode = perp_mode;
} else if (perp_mode) {
DPRINT("perpendicular mode not supported by this FDC.\n");
}
} /* perpendicular_mode */
 
static int fifo_depth = 0xa;
static int no_fifo = 0;
 
static int fdc_configure(void)
{
/* Turn on FIFO */
output_byte(FD_CONFIGURE);
if(need_more_output() != MORE_OUTPUT)
return 0;
output_byte(0);
output_byte(0x10 | (no_fifo & 0x20) | (fifo_depth & 0xf));
output_byte(0); /* pre-compensation from track
0 upwards */
return 1;
}
 
#define NOMINAL_DTR 500
 
/* Issue a "SPECIFY" command to set the step rate time, head unload time,
* head load time, and DMA disable flag to values needed by floppy.
*
* The value "dtr" is the data transfer rate in Kbps. It is needed
* to account for the data rate-based scaling done by the 82072 and 82077
* FDC types. This parameter is ignored for other types of FDCs (i.e.
* 8272a).
*
* Note that changing the data transfer rate has a (probably deleterious)
* effect on the parameters subject to scaling for 82072/82077 FDCs, so
* fdc_specify is called again after each data transfer rate
* change.
*
* srt: 1000 to 16000 in microseconds
* hut: 16 to 240 milliseconds
* hlt: 2 to 254 milliseconds
*
* These values are rounded up to the next highest available delay time.
*/
static void fdc_specify(void)
{
unsigned char spec1, spec2;
int srt, hlt, hut;
unsigned long dtr = NOMINAL_DTR;
unsigned long scale_dtr = NOMINAL_DTR;
int hlt_max_code = 0x7f;
int hut_max_code = 0xf;
 
if (FDCS->need_configure && FDCS->version >= FDC_82072A) {
fdc_configure();
FDCS->need_configure = 0;
/*DPRINT("FIFO enabled\n");*/
}
 
switch (raw_cmd->rate & 0x03) {
case 3:
dtr = 1000;
break;
case 1:
dtr = 300;
if (FDCS->version >= FDC_82078) {
/* chose the default rate table, not the one
* where 1 = 2 Mbps */
output_byte(FD_DRIVESPEC);
if(need_more_output() == MORE_OUTPUT) {
output_byte(UNIT(current_drive));
output_byte(0xc0);
}
}
break;
case 2:
dtr = 250;
break;
}
 
if (FDCS->version >= FDC_82072) {
scale_dtr = dtr;
hlt_max_code = 0x00; /* 0==256msec*dtr0/dtr (not linear!) */
hut_max_code = 0x0; /* 0==256msec*dtr0/dtr (not linear!) */
}
 
/* Convert step rate from microseconds to milliseconds and 4 bits */
srt = 16 - (DP->srt*scale_dtr/1000 + NOMINAL_DTR - 1)/NOMINAL_DTR;
SUPBOUND(srt, 0xf);
INFBOUND(srt, 0);
 
hlt = (DP->hlt*scale_dtr/2 + NOMINAL_DTR - 1)/NOMINAL_DTR;
if (hlt < 0x01)
hlt = 0x01;
else if (hlt > 0x7f)
hlt = hlt_max_code;
 
hut = (DP->hut*scale_dtr/16 + NOMINAL_DTR - 1)/NOMINAL_DTR;
if (hut < 0x1)
hut = 0x1;
else if (hut > 0xf)
hut = hut_max_code;
 
spec1 = (srt << 4) | hut;
spec2 = (hlt << 1) | (use_virtual_dma & 1);
 
/* If these parameters did not change, just return with success */
if (FDCS->spec1 != spec1 || FDCS->spec2 != spec2) {
/* Go ahead and set spec1 and spec2 */
output_byte(FD_SPECIFY);
output_byte(FDCS->spec1 = spec1);
output_byte(FDCS->spec2 = spec2);
}
} /* fdc_specify */
 
/* Set the FDC's data transfer rate on behalf of the specified drive.
* NOTE: with 82072/82077 FDCs, changing the data rate requires a reissue
* of the specify command (i.e. using the fdc_specify function).
*/
static int fdc_dtr(void)
{
/* If data rate not already set to desired value, set it. */
if ((raw_cmd->rate & 3) == FDCS->dtr)
return 0;
 
/* Set dtr */
fd_outb(raw_cmd->rate & 3, FD_DCR);
 
/* TODO: some FDC/drive combinations (C&T 82C711 with TEAC 1.2MB)
* need a stabilization period of several milliseconds to be
* enforced after data rate changes before R/W operations.
* Pause 5 msec to avoid trouble. (Needs to be 2 jiffies)
*/
FDCS->dtr = raw_cmd->rate & 3;
return(wait_for_completion(jiffies+2*HZ/100,
(timeout_fn) floppy_ready));
} /* fdc_dtr */
 
static void tell_sector(void)
{
printk(": track %d, head %d, sector %d, size %d",
R_TRACK, R_HEAD, R_SECTOR, R_SIZECODE);
} /* tell_sector */
 
 
/*
* OK, this error interpreting routine is called after a
* DMA read/write has succeeded
* or failed, so we check the results, and copy any buffers.
* hhb: Added better error reporting.
* ak: Made this into a separate routine.
*/
static int interpret_errors(void)
{
char bad;
int res = get_dma_residue(FLOPPY_DMA);
if(res) {printk("\n-- DMA residue (%d)",res); tell_sector(); printk("\n");}
 
if (inr!=7) {
DPRINT("-- FDC reply error");
FDCS->reset = 1;
return 1;
}
 
/* check IC to find cause of interrupt */
switch (ST0 & ST0_INTR) {
case 0x40: /* error occurred during command execution */
if (ST1 & ST1_EOC)
return 0; /* occurs with pseudo-DMA */
bad = 1;
if (ST1 & ST1_WP) {
DPRINT("Drive is write protected\n");
CLEARF(FD_DISK_WRITABLE);
cont->done(0);
bad = 2;
} else if (ST1 & ST1_ND) {
SETF(FD_NEED_TWADDLE);
} else if (ST1 & ST1_OR) {
if (DP->flags & FTD_MSG)
DPRINT("Over/Underrun - retrying\n");
bad = 0;
}else if (*errors >= DP->max_errors.reporting){
DPRINT("");
if (ST0 & ST0_ECE) {
printk("Recalibrate failed!");
} else if (ST2 & ST2_CRC) {
printk("data CRC error");
tell_sector();
} else if (ST1 & ST1_CRC) {
printk("CRC error");
tell_sector();
} else if ((ST1 & (ST1_MAM|ST1_ND)) || (ST2 & ST2_MAM)) {
if (!probing) {
printk("sector not found");
tell_sector();
} else
printk("probe failed...");
} else if (ST2 & ST2_WC) { /* seek error */
printk("wrong cylinder");
} else if (ST2 & ST2_BC) { /* cylinder marked as bad */
printk("bad cylinder");
} else {
printk("unknown error. ST[0..2] are: 0x%x 0x%x 0x%x", ST0, ST1, ST2);
tell_sector();
}
printk("\n");
 
}
if (ST2 & ST2_WC || ST2 & ST2_BC)
/* wrong cylinder => recal */
DRS->track = NEED_2_RECAL;
return bad;
case 0x80: /* invalid command given */
DPRINT("Invalid FDC command given!\n");
cont->done(0);
return 2;
case 0xc0:
DPRINT("Abnormal termination caused by polling\n");
cont->error();
return 2;
default: /* (0) Normal command termination */
return 0;
}
}
 
/*
* This routine is called when everything should be correctly set up
* for the transfer (i.e. floppy motor is on, the correct floppy is
* selected, and the head is sitting on the right track).
*/
static void setup_rw_floppy(void)
{
int i,ready_date,r, flags,dflags;
timeout_fn function;
 
flags = raw_cmd->flags;
if (flags & (FD_RAW_READ | FD_RAW_WRITE))
flags |= FD_RAW_INTR;
 
if ((flags & FD_RAW_SPIN) && !(flags & FD_RAW_NO_MOTOR)){
ready_date = DRS->spinup_date + DP->spinup;
/* If spinup will take a long time, rerun scandrives
* again just before spinup completion. Beware that
* after scandrives, we must again wait for selection.
*/
if ((signed) (ready_date - jiffies) > DP->select_delay){
ready_date -= DP->select_delay;
function = (timeout_fn) floppy_start;
} else
function = (timeout_fn) setup_rw_floppy;
 
/* wait until the floppy is spinning fast enough */
if (wait_for_completion(ready_date,function))
return;
}
dflags = DRS->flags;
 
if ((flags & FD_RAW_READ) || (flags & FD_RAW_WRITE))
setup_DMA();
 
if (flags & FD_RAW_INTR)
SET_INTR(main_command_interrupt);
 
r=0;
for (i=0; i< raw_cmd->cmd_count; i++)
r|=output_byte(raw_cmd->cmd[i]);
 
#ifdef DEBUGT
debugt("rw_command: ");
#endif
if (r){
cont->error();
reset_fdc();
return;
}
 
if (!(flags & FD_RAW_INTR)){
inr = result();
cont->interrupt();
} else if (flags & FD_RAW_NEED_DISK)
fd_watchdog();
}
 
static int blind_seek;
 
/*
* This is the routine called after every seek (or recalibrate) interrupt
* from the floppy controller.
*/
static void seek_interrupt(void)
{
#ifdef DEBUGT
debugt("seek interrupt:");
#endif
if (inr != 2 || (ST0 & 0xF8) != 0x20) {
DPRINT("seek failed\n");
DRS->track = NEED_2_RECAL;
cont->error();
cont->redo();
return;
}
if (DRS->track >= 0 && DRS->track != ST1 && !blind_seek){
#ifdef DCL_DEBUG
if (DP->flags & FD_DEBUG){
DPRINT("clearing NEWCHANGE flag because of effective seek\n");
DPRINT("jiffies=%ld\n", jiffies);
}
#endif
CLEARF(FD_DISK_NEWCHANGE); /* effective seek */
DRS->select_date = jiffies;
}
DRS->track = ST1;
floppy_ready();
}
 
static void check_wp(void)
{
if (TESTF(FD_VERIFY)) {
/* check write protection */
output_byte(FD_GETSTATUS);
output_byte(UNIT(current_drive));
if (result() != 1){
FDCS->reset = 1;
return;
}
CLEARF(FD_VERIFY);
CLEARF(FD_NEED_TWADDLE);
#ifdef DCL_DEBUG
if (DP->flags & FD_DEBUG){
DPRINT("checking whether disk is write protected\n");
DPRINT("wp=%x\n",ST3 & 0x40);
}
#endif
if (!(ST3 & 0x40))
SETF(FD_DISK_WRITABLE);
else
CLEARF(FD_DISK_WRITABLE);
}
}
 
static void seek_floppy(void)
{
int track;
 
blind_seek=0;
 
#ifdef DCL_DEBUG
if (DP->flags & FD_DEBUG){
DPRINT("calling disk change from seek\n");
}
#endif
 
if (!TESTF(FD_DISK_NEWCHANGE) &&
disk_change(current_drive) &&
(raw_cmd->flags & FD_RAW_NEED_DISK)){
/* the media changed flag should be cleared after the seek.
* If it isn't, this means that there is really no disk in
* the drive.
*/
SETF(FD_DISK_CHANGED);
cont->done(0);
cont->redo();
return;
}
if (DRS->track <= NEED_1_RECAL){
recalibrate_floppy();
return;
} else if (TESTF(FD_DISK_NEWCHANGE) &&
(raw_cmd->flags & FD_RAW_NEED_DISK) &&
(DRS->track <= NO_TRACK || DRS->track == raw_cmd->track)) {
/* we seek to clear the media-changed condition. Does anybody
* know a more elegant way, which works on all drives? */
if (raw_cmd->track)
track = raw_cmd->track - 1;
else {
if (DP->flags & FD_SILENT_DCL_CLEAR){
set_dor(fdc, ~(0x10 << UNIT(current_drive)), 0);
blind_seek = 1;
raw_cmd->flags |= FD_RAW_NEED_SEEK;
}
track = 1;
}
} else {
check_wp();
if (raw_cmd->track != DRS->track &&
(raw_cmd->flags & FD_RAW_NEED_SEEK))
track = raw_cmd->track;
else {
setup_rw_floppy();
return;
}
}
 
SET_INTR(seek_interrupt);
output_byte(FD_SEEK);
output_byte(UNIT(current_drive));
LAST_OUT(track);
#ifdef DEBUGT
debugt("seek command:");
#endif
}
 
static void recal_interrupt(void)
{
#ifdef DEBUGT
debugt("recal interrupt:");
#endif
if (inr !=2)
FDCS->reset = 1;
else if (ST0 & ST0_ECE) {
switch(DRS->track){
case NEED_1_RECAL:
#ifdef DEBUGT
debugt("recal interrupt need 1 recal:");
#endif
/* after a second recalibrate, we still haven't
* reached track 0. Probably no drive. Raise an
* error, as failing immediately might upset
* computers possessed by the Devil :-) */
cont->error();
cont->redo();
return;
case NEED_2_RECAL:
#ifdef DEBUGT
debugt("recal interrupt need 2 recal:");
#endif
/* If we already did a recalibrate,
* and we are not at track 0, this
* means we have moved. (The only way
* not to move at recalibration is to
* be already at track 0.) Clear the
* new change flag */
#ifdef DCL_DEBUG
if (DP->flags & FD_DEBUG){
DPRINT("clearing NEWCHANGE flag because of second recalibrate\n");
}
#endif
 
CLEARF(FD_DISK_NEWCHANGE);
DRS->select_date = jiffies;
/* fall through */
default:
#ifdef DEBUGT
debugt("recal interrupt default:");
#endif
/* Recalibrate moves the head by at
* most 80 steps. If after one
* recalibrate we don't have reached
* track 0, this might mean that we
* started beyond track 80. Try
* again. */
DRS->track = NEED_1_RECAL;
break;
}
} else
DRS->track = ST1;
floppy_ready();
}
 
static void print_result(char *message, int inr)
{
int i;
 
DPRINT("%s ", message);
if (inr >= 0)
for (i=0; i<inr; i++)
printk("repl[%d]=%x ", i, reply_buffer[i]);
printk("\n");
}
 
/* interrupt handler */
void floppy_interrupt(int irq, void *dev_id, struct pt_regs * regs)
{
void (*handler)(void) = DEVICE_INTR;
int do_print;
 
lasthandler = handler;
interruptjiffies = jiffies;
 
fd_disable_dma();
floppy_enable_hlt();
CLEAR_INTR;
if (fdc >= N_FDC || FDCS->address == -1){
/* we don't even know which FDC is the culprit */
printk("DOR0=%x\n", (unsigned int)fdc_state[0].dor);
printk("floppy interrupt on bizarre fdc %d\n",fdc);
printk("handler=%p\n", handler);
is_alive("bizarre fdc");
return;
}
 
FDCS->reset = 0;
/* We have to clear the reset flag here, because apparently on boxes
* with level triggered interrupts (PS/2, Sparc, ...), it is needed to
* emit SENSEI's to clear the interrupt line. And FDCS->reset blocks the
* emission of the SENSEI's.
* It is OK to emit floppy commands because we are in an interrupt
* handler here, and thus we have to fear no interference of other
* activity.
*/
 
do_print = !handler && print_unex && !initialising;
 
inr = result();
if(do_print)
print_result("unexpected interrupt", inr);
if (inr == 0){
do {
output_byte(FD_SENSEI);
inr = result();
if(do_print)
print_result("sensei", inr);
} while ((ST0 & 0x83) != UNIT(current_drive) && inr == 2);
}
if (handler) {
if(intr_count >= 2) {
/* expected interrupt */
floppy_tq.routine = (void *)(void *) handler;
queue_task_irq(&floppy_tq, &tq_immediate);
mark_bh(IMMEDIATE_BH);
} else
handler();
} else
FDCS->reset = 1;
is_alive("normal interrupt end");
}
 
static void recalibrate_floppy(void)
{
#ifdef DEBUGT
debugt("recalibrate floppy:");
#endif
SET_INTR(recal_interrupt);
output_byte(FD_RECALIBRATE);
LAST_OUT(UNIT(current_drive));
}
 
/*
* Must do 4 FD_SENSEIs after reset because of ``drive polling''.
*/
static void reset_interrupt(void)
{
#ifdef DEBUGT
debugt("reset interrupt:");
#endif
result(); /* get the status ready for set_fdc */
if (FDCS->reset) {
printk("reset set in interrupt, calling %p\n", cont->error);
cont->error(); /* a reset just after a reset. BAD! */
}
cont->redo();
}
 
/*
* reset is done by pulling bit 2 of DOR low for a while (old FDCs),
* or by setting the self clearing bit 7 of STATUS (newer FDCs)
*/
static void reset_fdc(void)
{
SET_INTR(reset_interrupt);
FDCS->reset = 0;
reset_fdc_info(0);
 
/* Pseudo-DMA may intercept 'reset finished' interrupt. */
/* Irrelevant for systems with true DMA (i386). */
fd_disable_dma();
 
if (FDCS->version >= FDC_82072A)
fd_outb(0x80 | (FDCS->dtr &3), FD_STATUS);
else {
fd_setdor(FDCS->dor & ~0x04);
udelay(FD_RESET_DELAY);
fd_setdor(FDCS->dor);
}
}
 
void show_floppy(void)
{
int i;
 
printk("\n");
printk("floppy driver state\n");
printk("-------------------\n");
printk("now=%ld last interrupt=%d last called handler=%p\n",
jiffies, interruptjiffies, lasthandler);
 
 
#ifdef FLOPPY_SANITY_CHECK
printk("timeout_message=%s\n", timeout_message);
printk("last output bytes:\n");
for (i=0; i < OLOGSIZE; i++)
printk("%2x %2x %ld\n",
output_log[(i+output_log_pos) % OLOGSIZE].data,
output_log[(i+output_log_pos) % OLOGSIZE].status,
output_log[(i+output_log_pos) % OLOGSIZE].jiffies);
printk("last result at %d\n", resultjiffies);
printk("last redo_fd_request at %d\n", lastredo);
for (i=0; i<resultsize; i++){
printk("%2x ", reply_buffer[i]);
}
printk("\n");
#endif
 
printk("status=%x\n", fd_inb(FD_STATUS));
printk("fdc_busy=%d\n", fdc_busy);
if (DEVICE_INTR)
printk("DEVICE_INTR=%p\n", DEVICE_INTR);
if (floppy_tq.sync)
printk("floppy_tq.routine=%p\n", floppy_tq.routine);
if (fd_timer.prev)
printk("fd_timer.function=%p\n", fd_timer.function);
if (fd_timeout.prev){
printk("timer_table=%p\n",fd_timeout.function);
printk("expires=%ld\n",fd_timeout.expires-jiffies);
printk("now=%ld\n",jiffies);
}
printk("cont=%p\n", cont);
printk("CURRENT=%p\n", CURRENT);
printk("command_status=%d\n", command_status);
printk("\n");
}
 
static void floppy_shutdown(void)
{
if (!initialising)
show_floppy();
cancel_activity();
sti();
 
floppy_enable_hlt();
fd_disable_dma();
/* avoid dma going to a random drive after shutdown */
 
if (!initialising)
DPRINT("floppy timeout called\n");
FDCS->reset = 1;
if (cont){
cont->done(0);
cont->redo(); /* this will recall reset when needed */
} else {
printk("no cont in shutdown!\n");
process_fd_request();
}
is_alive("floppy shutdown");
}
/*typedef void (*timeout_fn)(unsigned long);*/
 
/* start motor, check media-changed condition and write protection */
static int start_motor(void (*function)(void) )
{
int mask, data;
 
mask = 0xfc;
data = UNIT(current_drive);
if (!(raw_cmd->flags & FD_RAW_NO_MOTOR)){
if (!(FDCS->dor & (0x10 << UNIT(current_drive)))){
set_debugt();
/* no read since this drive is running */
DRS->first_read_date = 0;
/* note motor start time if motor is not yet running */
DRS->spinup_date = jiffies;
data |= (0x10 << UNIT(current_drive));
}
} else
if (FDCS->dor & (0x10 << UNIT(current_drive)))
mask &= ~(0x10 << UNIT(current_drive));
 
/* starts motor and selects floppy */
del_timer(motor_off_timer + current_drive);
set_dor(fdc, mask, data);
 
/* wait_for_completion also schedules reset if needed. */
return(wait_for_completion(DRS->select_date+DP->select_delay,
(timeout_fn) function));
}
 
static void floppy_ready(void)
{
CHECK_RESET;
if (start_motor(floppy_ready)) return;
if (fdc_dtr()) return;
 
#ifdef DCL_DEBUG
if (DP->flags & FD_DEBUG){
DPRINT("calling disk change from floppy_ready\n");
}
#endif
 
if (!(raw_cmd->flags & FD_RAW_NO_MOTOR) &&
disk_change(current_drive) &&
!DP->select_delay)
twaddle(); /* this clears the dcl on certain drive/controller
* combinations */
 
if (raw_cmd->flags & (FD_RAW_NEED_SEEK | FD_RAW_NEED_DISK)){
perpendicular_mode();
fdc_specify(); /* must be done here because of hut, hlt ... */
seek_floppy();
} else
setup_rw_floppy();
}
 
static void floppy_start(void)
{
reschedule_timeout(CURRENTD, "floppy start", 0);
 
scandrives();
#ifdef DCL_DEBUG
if (DP->flags & FD_DEBUG){
DPRINT("setting NEWCHANGE in floppy_start\n");
}
#endif
SETF(FD_DISK_NEWCHANGE);
floppy_ready();
}
 
/*
* ========================================================================
* here ends the bottom half. Exported routines are:
* floppy_start, floppy_off, floppy_ready, lock_fdc, unlock_fdc, set_fdc,
* start_motor, reset_fdc, reset_fdc_info, interpret_errors.
* Initialization also uses output_byte, result, set_dor, floppy_interrupt
* and set_dor.
* ========================================================================
*/
/*
* General purpose continuations.
* ==============================
*/
 
static void do_wakeup(void)
{
reschedule_timeout(MAXTIMEOUT, "do wakeup", 0);
cont = 0;
command_status += 2;
wake_up(&command_done);
}
 
static struct cont_t wakeup_cont={
empty,
do_wakeup,
empty,
(done_f)empty
};
 
 
static struct cont_t intr_cont={
empty,
process_fd_request,
empty,
(done_f) empty
};
 
static int wait_til_done(void (*handler)(void), int interruptible)
{
int ret;
unsigned long flags;
 
floppy_tq.routine = (void *)(void *) handler;
queue_task(&floppy_tq, &tq_immediate);
mark_bh(IMMEDIATE_BH);
INT_OFF;
while(command_status < 2 && NO_SIGNAL){
is_alive("wait_til_done");
if (interruptible)
interruptible_sleep_on(&command_done);
else
sleep_on(&command_done);
}
if (command_status < 2){
cancel_activity();
cont = &intr_cont;
reset_fdc();
INT_ON;
return -EINTR;
}
INT_ON;
 
if (FDCS->reset)
command_status = FD_COMMAND_ERROR;
if (command_status == FD_COMMAND_OKAY)
ret=0;
else
ret=-EIO;
command_status = FD_COMMAND_NONE;
return ret;
}
 
static void generic_done(int result)
{
command_status = result;
cont = &wakeup_cont;
}
 
static void generic_success(void)
{
cont->done(1);
}
 
static void generic_failure(void)
{
cont->done(0);
}
 
static void success_and_wakeup(void)
{
generic_success();
cont->redo();
}
 
 
/*
* formatting and rw support.
* ==========================
*/
 
static int next_valid_format(void)
{
int probed_format;
 
probed_format = DRS->probed_format;
while(1){
if (probed_format >= 8 ||
!DP->autodetect[probed_format]){
DRS->probed_format = 0;
return 1;
}
if (floppy_type[DP->autodetect[probed_format]].sect){
DRS->probed_format = probed_format;
return 0;
}
probed_format++;
}
}
 
static void bad_flp_intr(void)
{
if (probing){
DRS->probed_format++;
if (!next_valid_format())
return;
}
(*errors)++;
INFBOUND(DRWE->badness, *errors);
if (*errors > DP->max_errors.abort)
cont->done(0);
if (*errors > DP->max_errors.reset)
FDCS->reset = 1;
else if (*errors > DP->max_errors.recal)
DRS->track = NEED_2_RECAL;
}
 
static void set_floppy(kdev_t device)
{
if (TYPE(device))
_floppy = TYPE(device) + floppy_type;
else
_floppy = current_type[ DRIVE(device) ];
}
 
/*
* formatting support.
* ===================
*/
static void format_interrupt(void)
{
switch (interpret_errors()){
case 1:
cont->error();
case 2:
break;
case 0:
cont->done(1);
}
cont->redo();
}
 
#define CODE2SIZE (ssize = ((1 << SIZECODE) + 3) >> 2)
#define FM_MODE(x,y) ((y) & ~(((x)->rate & 0x80) >>1))
#define CT(x) ((x) | 0x40)
static void setup_format_params(int track)
{
struct fparm {
unsigned char track,head,sect,size;
} *here = (struct fparm *)floppy_track_buffer;
int il,n;
int count,head_shift,track_shift;
 
raw_cmd = &default_raw_cmd;
raw_cmd->track = track;
 
raw_cmd->flags = FD_RAW_WRITE | FD_RAW_INTR | FD_RAW_SPIN |
FD_RAW_NEED_DISK | FD_RAW_NEED_SEEK;
raw_cmd->rate = _floppy->rate & 0x43;
raw_cmd->cmd_count = NR_F;
COMMAND = FM_MODE(_floppy,FD_FORMAT);
DR_SELECT = UNIT(current_drive) + PH_HEAD(_floppy,format_req.head);
F_SIZECODE = FD_SIZECODE(_floppy);
F_SECT_PER_TRACK = _floppy->sect << 2 >> F_SIZECODE;
F_GAP = _floppy->fmt_gap;
F_FILL = FD_FILL_BYTE;
 
raw_cmd->kernel_data = floppy_track_buffer;
raw_cmd->length = 4 * F_SECT_PER_TRACK;
 
/* allow for about 30ms for data transport per track */
head_shift = (F_SECT_PER_TRACK + 5) / 6;
 
/* a ``cylinder'' is two tracks plus a little stepping time */
track_shift = 2 * head_shift + 3;
 
/* position of logical sector 1 on this track */
n = (track_shift * format_req.track + head_shift * format_req.head)
% F_SECT_PER_TRACK;
 
/* determine interleave */
il = 1;
if (_floppy->fmt_gap < 0x22)
il++;
 
/* initialize field */
for (count = 0; count < F_SECT_PER_TRACK; ++count) {
here[count].track = format_req.track;
here[count].head = format_req.head;
here[count].sect = 0;
here[count].size = F_SIZECODE;
}
/* place logical sectors */
for (count = 1; count <= F_SECT_PER_TRACK; ++count) {
here[n].sect = count;
n = (n+il) % F_SECT_PER_TRACK;
if (here[n].sect) { /* sector busy, find next free sector */
++n;
if (n>= F_SECT_PER_TRACK) {
n-=F_SECT_PER_TRACK;
while (here[n].sect) ++n;
}
}
}
}
 
static void redo_format(void)
{
buffer_track = -1;
setup_format_params(format_req.track << STRETCH(_floppy));
floppy_start();
#ifdef DEBUGT
debugt("queue format request");
#endif
}
 
static struct cont_t format_cont={
format_interrupt,
redo_format,
bad_flp_intr,
generic_done };
 
static int do_format(kdev_t device, struct format_descr *tmp_format_req)
{
int ret;
int drive=DRIVE(device);
 
LOCK_FDC(drive,1);
set_floppy(device);
if (!_floppy ||
_floppy->track > DP->tracks ||
tmp_format_req->track >= _floppy->track ||
tmp_format_req->head >= _floppy->head ||
(_floppy->sect << 2) % (1 << FD_SIZECODE(_floppy)) ||
!_floppy->fmt_gap) {
process_fd_request();
return -EINVAL;
}
format_req = *tmp_format_req;
format_errors = 0;
cont = &format_cont;
errors = &format_errors;
IWAIT(redo_format);
process_fd_request();
return ret;
}
 
/*
* Buffer read/write and support
* =============================
*/
 
/* new request_done. Can handle physical sectors which are smaller than a
* logical buffer */
static void request_done(int uptodate)
{
int block;
 
probing = 0;
reschedule_timeout(MAXTIMEOUT, "request done %d", uptodate);
 
if (!CURRENT){
DPRINT("request list destroyed in floppy request done\n");
return;
}
 
if (uptodate){
/* maintain values for invalidation on geometry
* change */
block = current_count_sectors + CURRENT->sector;
INFBOUND(DRS->maxblock, block);
if (block > _floppy->sect)
DRS->maxtrack = 1;
 
/* unlock chained buffers */
while (current_count_sectors && CURRENT &&
current_count_sectors >= CURRENT->current_nr_sectors){
current_count_sectors -= CURRENT->current_nr_sectors;
CURRENT->nr_sectors -= CURRENT->current_nr_sectors;
CURRENT->sector += CURRENT->current_nr_sectors;
end_request(1);
}
if (current_count_sectors && CURRENT){
/* "unlock" last subsector */
CURRENT->buffer += current_count_sectors <<9;
CURRENT->current_nr_sectors -= current_count_sectors;
CURRENT->nr_sectors -= current_count_sectors;
CURRENT->sector += current_count_sectors;
return;
}
 
if (current_count_sectors && !CURRENT)
DPRINT("request list destroyed in floppy request done\n");
 
} else {
if (CURRENT->cmd == WRITE) {
/* record write error information */
DRWE->write_errors++;
if (DRWE->write_errors == 1) {
DRWE->first_error_sector = CURRENT->sector;
DRWE->first_error_generation = DRS->generation;
}
DRWE->last_error_sector = CURRENT->sector;
DRWE->last_error_generation = DRS->generation;
}
end_request(0);
}
}
 
/* Interrupt handler evaluating the result of the r/w operation */
static void rw_interrupt(void)
{
int nr_sectors, ssize, eoc;
 
if (!DRS->first_read_date)
DRS->first_read_date = jiffies;
 
nr_sectors = 0;
CODE2SIZE;
 
if(ST1 & ST1_EOC)
eoc = 1;
else
eoc = 0;
nr_sectors = ((R_TRACK-TRACK)*_floppy->head+R_HEAD-HEAD) *
_floppy->sect + ((R_SECTOR-SECTOR+eoc) << SIZECODE >> 2) -
(sector_t % _floppy->sect) % ssize;
 
#ifdef FLOPPY_SANITY_CHECK
if (nr_sectors > current_count_sectors + ssize -
(current_count_sectors + sector_t) % ssize +
sector_t % ssize){
DPRINT("long rw: %x instead of %lx\n",
nr_sectors, current_count_sectors);
printk("rs=%d s=%d\n", R_SECTOR, SECTOR);
printk("rh=%d h=%d\n", R_HEAD, HEAD);
printk("rt=%d t=%d\n", R_TRACK, TRACK);
printk("spt=%d st=%d ss=%d\n", SECT_PER_TRACK,
sector_t, ssize);
}
#endif
INFBOUND(nr_sectors,0);
SUPBOUND(current_count_sectors, nr_sectors);
 
switch (interpret_errors()){
case 2:
cont->redo();
return;
case 1:
if (!current_count_sectors){
cont->error();
cont->redo();
return;
}
break;
case 0:
if (!current_count_sectors){
cont->redo();
return;
}
current_type[current_drive] = _floppy;
floppy_sizes[TOMINOR(current_drive) ]= _floppy->size>>1;
break;
}
 
if (probing) {
if (DP->flags & FTD_MSG)
DPRINT("Auto-detected floppy type %s in fd%d\n",
_floppy->name,current_drive);
current_type[current_drive] = _floppy;
floppy_sizes[TOMINOR(current_drive)] = _floppy->size >> 1;
probing = 0;
}
 
if (CT(COMMAND) != FD_READ ||
raw_cmd->kernel_data == CURRENT->buffer){
/* transfer directly from buffer */
cont->done(1);
} else if (CT(COMMAND) == FD_READ){
buffer_track = raw_cmd->track;
buffer_drive = current_drive;
INFBOUND(buffer_max, nr_sectors + sector_t);
}
cont->redo();
}
 
/* Compute maximal contiguous buffer size. */
static int buffer_chain_size(void)
{
struct buffer_head *bh;
int size;
char *base;
 
base = CURRENT->buffer;
size = CURRENT->current_nr_sectors << 9;
bh = CURRENT->bh;
 
if (bh){
bh = bh->b_reqnext;
while (bh && bh->b_data == base + size){
size += bh->b_size;
bh = bh->b_reqnext;
}
}
return size >> 9;
}
 
/* Compute the maximal transfer size */
static int transfer_size(int ssize, int max_sector, int max_size)
{
SUPBOUND(max_sector, sector_t + max_size);
 
/* alignment */
max_sector -= (max_sector % _floppy->sect) % ssize;
 
/* transfer size, beginning not aligned */
current_count_sectors = max_sector - sector_t ;
 
return max_sector;
}
 
/*
* Move data from/to the track buffer to/from the buffer cache.
*/
static void copy_buffer(int ssize, int max_sector, int max_sector_2)
{
int remaining; /* number of transferred 512-byte sectors */
struct buffer_head *bh;
char *buffer, *dma_buffer;
int size;
 
max_sector = transfer_size(ssize,
minimum(max_sector, max_sector_2),
CURRENT->nr_sectors);
 
if (current_count_sectors <= 0 && CT(COMMAND) == FD_WRITE &&
buffer_max > sector_t + CURRENT->nr_sectors)
current_count_sectors = minimum(buffer_max - sector_t,
CURRENT->nr_sectors);
 
remaining = current_count_sectors << 9;
#ifdef FLOPPY_SANITY_CHECK
if ((remaining >> 9) > CURRENT->nr_sectors &&
CT(COMMAND) == FD_WRITE){
DPRINT("in copy buffer\n");
printk("current_count_sectors=%ld\n", current_count_sectors);
printk("remaining=%d\n", remaining >> 9);
printk("CURRENT->nr_sectors=%ld\n",CURRENT->nr_sectors);
printk("CURRENT->current_nr_sectors=%ld\n",
CURRENT->current_nr_sectors);
printk("max_sector=%d\n", max_sector);
printk("ssize=%d\n", ssize);
}
#endif
 
buffer_max = maximum(max_sector, buffer_max);
 
dma_buffer = floppy_track_buffer + ((sector_t - buffer_min) << 9);
 
bh = CURRENT->bh;
size = CURRENT->current_nr_sectors << 9;
buffer = CURRENT->buffer;
 
while (remaining > 0){
SUPBOUND(size, remaining);
#ifdef FLOPPY_SANITY_CHECK
if (dma_buffer + size >
floppy_track_buffer + (max_buffer_sectors << 10) ||
dma_buffer < floppy_track_buffer){
DPRINT("buffer overrun in copy buffer %d\n",
(int) ((floppy_track_buffer - dma_buffer) >>9));
printk("sector_t=%d buffer_min=%d\n",
sector_t, buffer_min);
printk("current_count_sectors=%ld\n",
current_count_sectors);
if (CT(COMMAND) == FD_READ)
printk("read\n");
if (CT(COMMAND) == FD_READ)
printk("write\n");
break;
}
if (((unsigned long)buffer) % 512)
DPRINT("%p buffer not aligned\n", buffer);
#endif
if (CT(COMMAND) == FD_READ)
memcpy(buffer, dma_buffer, size);
else
memcpy(dma_buffer, buffer, size);
remaining -= size;
if (!remaining)
break;
 
dma_buffer += size;
bh = bh->b_reqnext;
#ifdef FLOPPY_SANITY_CHECK
if (!bh){
DPRINT("bh=null in copy buffer after copy\n");
break;
}
#endif
size = bh->b_size;
buffer = bh->b_data;
}
#ifdef FLOPPY_SANITY_CHECK
if (remaining){
if (remaining > 0)
max_sector -= remaining >> 9;
DPRINT("weirdness: remaining %d\n", remaining>>9);
}
#endif
}
 
/*
* Formulate a read/write request.
* this routine decides where to load the data (directly to buffer, or to
* tmp floppy area), how much data to load (the size of the buffer, the whole
* track, or a single sector)
* All floppy_track_buffer handling goes in here. If we ever add track buffer
* allocation on the fly, it should be done here. No other part should need
* modification.
*/
 
static int make_raw_rw_request(void)
{
int aligned_sector_t;
int max_sector, max_size, tracksize, ssize;
 
set_fdc(DRIVE(CURRENT->rq_dev));
 
raw_cmd = &default_raw_cmd;
raw_cmd->flags = FD_RAW_SPIN | FD_RAW_NEED_DISK | FD_RAW_NEED_DISK |
FD_RAW_NEED_SEEK;
raw_cmd->cmd_count = NR_RW;
if (CURRENT->cmd == READ){
raw_cmd->flags |= FD_RAW_READ;
COMMAND = FM_MODE(_floppy,FD_READ);
} else if (CURRENT->cmd == WRITE){
raw_cmd->flags |= FD_RAW_WRITE;
COMMAND = FM_MODE(_floppy,FD_WRITE);
} else {
DPRINT("make_raw_rw_request: unknown command\n");
return 0;
}
 
max_sector = _floppy->sect * _floppy->head;
 
TRACK = CURRENT->sector / max_sector;
sector_t = CURRENT->sector % max_sector;
if (_floppy->track && TRACK >= _floppy->track)
return 0;
HEAD = sector_t / _floppy->sect;
 
if (((_floppy->stretch & FD_SWAPSIDES) || TESTF(FD_NEED_TWADDLE)) &&
sector_t < _floppy->sect)
max_sector = _floppy->sect;
 
/* 2M disks have phantom sectors on the first track */
if ((_floppy->rate & FD_2M) && (!TRACK) && (!HEAD)){
max_sector = 2 * _floppy->sect / 3;
if (sector_t >= max_sector){
current_count_sectors = minimum(_floppy->sect - sector_t,
CURRENT->nr_sectors);
return 1;
}
SIZECODE = 2;
} else
SIZECODE = FD_SIZECODE(_floppy);
raw_cmd->rate = _floppy->rate & 0x43;
if ((_floppy->rate & FD_2M) &&
(TRACK || HEAD) &&
raw_cmd->rate == 2)
raw_cmd->rate = 1;
 
if (SIZECODE)
SIZECODE2 = 0xff;
else
SIZECODE2 = 0x80;
raw_cmd->track = TRACK << STRETCH(_floppy);
DR_SELECT = UNIT(current_drive) + PH_HEAD(_floppy,HEAD);
GAP = _floppy->gap;
CODE2SIZE;
SECT_PER_TRACK = _floppy->sect << 2 >> SIZECODE;
SECTOR = ((sector_t % _floppy->sect) << 2 >> SIZECODE) + 1;
tracksize = _floppy->sect - _floppy->sect % ssize;
if (tracksize < _floppy->sect){
SECT_PER_TRACK ++;
if (tracksize <= sector_t % _floppy->sect)
SECTOR--;
while (tracksize <= sector_t % _floppy->sect){
while(tracksize + ssize > _floppy->sect){
SIZECODE--;
ssize >>= 1;
}
SECTOR++; SECT_PER_TRACK ++;
tracksize += ssize;
}
max_sector = HEAD * _floppy->sect + tracksize;
} else if (!TRACK && !HEAD && !(_floppy->rate & FD_2M) && probing)
max_sector = _floppy->sect;
 
aligned_sector_t = sector_t - (sector_t % _floppy->sect) % ssize;
max_size = CURRENT->nr_sectors;
if ((raw_cmd->track == buffer_track) &&
(current_drive == buffer_drive) &&
(sector_t >= buffer_min) && (sector_t < buffer_max)) {
/* data already in track buffer */
if (CT(COMMAND) == FD_READ) {
copy_buffer(1, max_sector, buffer_max);
return 1;
}
} else if (aligned_sector_t != sector_t || CURRENT->nr_sectors < ssize){
if (CT(COMMAND) == FD_WRITE){
if (sector_t + CURRENT->nr_sectors > ssize &&
sector_t + CURRENT->nr_sectors < ssize + ssize)
max_size = ssize + ssize;
else
max_size = ssize;
}
raw_cmd->flags &= ~FD_RAW_WRITE;
raw_cmd->flags |= FD_RAW_READ;
COMMAND = FM_MODE(_floppy,FD_READ);
} else if ((unsigned long)CURRENT->buffer < MAX_DMA_ADDRESS) {
unsigned long dma_limit;
int direct, indirect;
 
indirect= transfer_size(ssize,max_sector,max_buffer_sectors*2) -
sector_t;
 
/*
* Do NOT use minimum() here---MAX_DMA_ADDRESS is 64 bits wide
* on a 64 bit machine!
*/
max_size = buffer_chain_size();
#ifndef CONFIG_ARM
dma_limit = (MAX_DMA_ADDRESS - ((unsigned long) CURRENT->buffer)) >> 9;
if ((unsigned long) max_size > dma_limit) {
max_size = dma_limit;
}
/* 64 kb boundaries */
if (CROSS_64KB(CURRENT->buffer, max_size << 9))
max_size = (K_64 - ((long) CURRENT->buffer) % K_64)>>9;
#endif
direct = transfer_size(ssize,max_sector,max_size) - sector_t;
/*
* We try to read tracks, but if we get too many errors, we
* go back to reading just one sector at a time.
*
* This means we should be able to read a sector even if there
* are other bad sectors on this track.
*/
if (!direct ||
(indirect * 2 > direct * 3 &&
*errors < DP->max_errors.read_track &&
/*!TESTF(FD_NEED_TWADDLE) &&*/
((!probing || (DP->read_track&(1<<DRS->probed_format)))))){
max_size = CURRENT->nr_sectors;
} else {
raw_cmd->kernel_data = CURRENT->buffer;
raw_cmd->length = current_count_sectors << 9;
if (raw_cmd->length == 0){
DPRINT("zero dma transfer attempted from make_raw_request\n");
DPRINT("indirect=%d direct=%d sector_t=%d",
indirect, direct, sector_t);
return 0;
}
return 2;
}
}
 
if (CT(COMMAND) == FD_READ)
max_size = max_sector; /* unbounded */
 
/* claim buffer track if needed */
if (buffer_track != raw_cmd->track || /* bad track */
buffer_drive !=current_drive || /* bad drive */
sector_t > buffer_max ||
sector_t < buffer_min ||
((CT(COMMAND) == FD_READ ||
(aligned_sector_t == sector_t && CURRENT->nr_sectors >= ssize))&&
max_sector > 2 * max_buffer_sectors + buffer_min &&
max_size + sector_t > 2 * max_buffer_sectors + buffer_min)
/* not enough space */){
buffer_track = -1;
buffer_drive = current_drive;
buffer_max = buffer_min = aligned_sector_t;
}
raw_cmd->kernel_data = floppy_track_buffer +
((aligned_sector_t-buffer_min)<<9);
 
if (CT(COMMAND) == FD_WRITE){
/* copy write buffer to track buffer.
* if we get here, we know that the write
* is either aligned or the data already in the buffer
* (buffer will be overwritten) */
#ifdef FLOPPY_SANITY_CHECK
if (sector_t != aligned_sector_t && buffer_track == -1)
DPRINT("internal error offset !=0 on write\n");
#endif
buffer_track = raw_cmd->track;
buffer_drive = current_drive;
copy_buffer(ssize, max_sector, 2*max_buffer_sectors+buffer_min);
} else
transfer_size(ssize, max_sector,
2*max_buffer_sectors+buffer_min-aligned_sector_t);
 
/* round up current_count_sectors to get dma xfer size */
raw_cmd->length = sector_t+current_count_sectors-aligned_sector_t;
raw_cmd->length = ((raw_cmd->length -1)|(ssize-1))+1;
raw_cmd->length <<= 9;
#ifdef FLOPPY_SANITY_CHECK
if ((raw_cmd->length < current_count_sectors << 9) ||
(raw_cmd->kernel_data != CURRENT->buffer &&
CT(COMMAND) == FD_WRITE &&
(aligned_sector_t + (raw_cmd->length >> 9) > buffer_max ||
aligned_sector_t < buffer_min)) ||
raw_cmd->length % (128 << SIZECODE) ||
raw_cmd->length <= 0 || current_count_sectors <= 0){
DPRINT("fractionary current count b=%lx s=%lx\n",
raw_cmd->length, current_count_sectors);
if (raw_cmd->kernel_data != CURRENT->buffer)
printk("addr=%d, length=%ld\n",
(int) ((raw_cmd->kernel_data -
floppy_track_buffer) >> 9),
current_count_sectors);
printk("st=%d ast=%d mse=%d msi=%d\n",
sector_t, aligned_sector_t, max_sector, max_size);
printk("ssize=%x SIZECODE=%d\n", ssize, SIZECODE);
printk("command=%x SECTOR=%d HEAD=%d, TRACK=%d\n",
COMMAND, SECTOR, HEAD, TRACK);
printk("buffer drive=%d\n", buffer_drive);
printk("buffer track=%d\n", buffer_track);
printk("buffer_min=%d\n", buffer_min);
printk("buffer_max=%d\n", buffer_max);
return 0;
}
 
if (raw_cmd->kernel_data != CURRENT->buffer){
if (raw_cmd->kernel_data < floppy_track_buffer ||
current_count_sectors < 0 ||
raw_cmd->length < 0 ||
raw_cmd->kernel_data + raw_cmd->length >
floppy_track_buffer + (max_buffer_sectors << 10)){
DPRINT("buffer overrun in schedule dma\n");
printk("sector_t=%d buffer_min=%d current_count=%ld\n",
sector_t, buffer_min,
raw_cmd->length >> 9);
printk("current_count_sectors=%ld\n",
current_count_sectors);
if (CT(COMMAND) == FD_READ)
printk("read\n");
if (CT(COMMAND) == FD_READ)
printk("write\n");
return 0;
}
} else if (raw_cmd->length > CURRENT->nr_sectors << 9 ||
current_count_sectors > CURRENT->nr_sectors){
DPRINT("buffer overrun in direct transfer\n");
return 0;
} else if (raw_cmd->length < current_count_sectors << 9){
DPRINT("more sectors than bytes\n");
printk("bytes=%ld\n", raw_cmd->length >> 9);
printk("sectors=%ld\n", current_count_sectors);
}
if (raw_cmd->length == 0){
DPRINT("zero dma transfer attempted from make_raw_request\n");
return 0;
}
#endif
return 2;
}
 
static void redo_fd_request(void)
{
#define REPEAT {request_done(0); continue; }
kdev_t device;
int tmp;
 
lastredo = jiffies;
if (current_drive < N_DRIVE)
floppy_off(current_drive);
 
if (CURRENT && CURRENT->rq_status == RQ_INACTIVE){
CLEAR_INTR;
unlock_fdc();
return;
}
 
while(1){
if (!CURRENT) {
CLEAR_INTR;
unlock_fdc();
return;
}
if (MAJOR(CURRENT->rq_dev) != MAJOR_NR)
panic(DEVICE_NAME ": request list destroyed");
if (CURRENT->bh && !buffer_locked(CURRENT->bh))
panic(DEVICE_NAME ": block not locked");
 
device = CURRENT->rq_dev;
set_fdc(DRIVE(device));
reschedule_timeout(CURRENTD, "redo fd request", 0);
 
set_floppy(device);
raw_cmd = & default_raw_cmd;
raw_cmd->flags = 0;
if (start_motor(redo_fd_request)) return;
disk_change(current_drive);
if (test_bit(current_drive, &fake_change) ||
TESTF(FD_DISK_CHANGED)){
DPRINT("disk absent or changed during operation\n");
REPEAT;
}
if (!_floppy) { /* Autodetection */
if (!probing){
DRS->probed_format = 0;
if (next_valid_format()){
DPRINT("no autodetectable formats\n");
_floppy = NULL;
REPEAT;
}
}
probing = 1;
_floppy = floppy_type+DP->autodetect[DRS->probed_format];
} else
probing = 0;
errors = & (CURRENT->errors);
tmp = make_raw_rw_request();
if (tmp < 2){
request_done(tmp);
continue;
}
 
if (TESTF(FD_NEED_TWADDLE))
twaddle();
floppy_tq.routine = (void *)(void *) floppy_start;
queue_task(&floppy_tq, &tq_immediate);
mark_bh(IMMEDIATE_BH);
#ifdef DEBUGT
debugt("queue fd request");
#endif
return;
}
#undef REPEAT
}
 
static struct cont_t rw_cont={
rw_interrupt,
redo_fd_request,
bad_flp_intr,
request_done };
 
static struct tq_struct request_tq =
{ 0, 0, (void *) (void *) redo_fd_request, 0 };
 
static void process_fd_request(void)
{
cont = &rw_cont;
queue_task(&request_tq, &tq_immediate);
mark_bh(IMMEDIATE_BH);
}
 
static void do_fd_request(void)
{
sti();
if (fdc_busy){
/* fdc busy, this new request will be treated when the
current one is done */
is_alive("do fd request, old request running");
return;
}
lock_fdc(MAXTIMEOUT,0);
process_fd_request();
is_alive("do fd request");
}
 
static struct cont_t poll_cont={
success_and_wakeup,
floppy_ready,
generic_failure,
generic_done };
 
static int poll_drive(int interruptible, int flag)
{
int ret;
/* no auto-sense, just clear dcl */
raw_cmd = &default_raw_cmd;
raw_cmd->flags= flag;
raw_cmd->track=0;
raw_cmd->cmd_count=0;
cont = &poll_cont;
#ifdef DCL_DEBUG
if (DP->flags & FD_DEBUG){
DPRINT("setting NEWCHANGE in poll_drive\n");
}
#endif
SETF(FD_DISK_NEWCHANGE);
WAIT(floppy_ready);
return ret;
}
 
/*
* User triggered reset
* ====================
*/
 
static void reset_intr(void)
{
printk("weird, reset interrupt called\n");
}
 
static struct cont_t reset_cont={
reset_intr,
success_and_wakeup,
generic_failure,
generic_done };
 
static int user_reset_fdc(int drive, int arg, int interruptible)
{
int ret;
 
ret=0;
LOCK_FDC(drive,interruptible);
if (arg == FD_RESET_ALWAYS)
FDCS->reset=1;
if (FDCS->reset){
cont = &reset_cont;
WAIT(reset_fdc);
}
process_fd_request();
return ret;
}
 
/*
* Misc Ioctl's and support
* ========================
*/
static int fd_copyout(void *param, const void *address, int size)
{
int ret;
 
ECALL(verify_area(VERIFY_WRITE,param,size));
memcpy_tofs(param,(void *) address, size);
return 0;
}
 
static int fd_copyin(void *param, void *address, int size)
{
int ret;
 
ECALL(verify_area(VERIFY_READ,param,size));
memcpy_fromfs((void *) address, param, size);
return 0;
}
 
#define COPYOUT(x) ECALL(fd_copyout((void *)param, &(x), sizeof(x)))
#define COPYIN(x) ECALL(fd_copyin((void *)param, &(x), sizeof(x)))
 
static inline const char *drive_name(int type, int drive)
{
struct floppy_struct *floppy;
 
if (type)
floppy = floppy_type + type;
else {
if (UDP->native_format)
floppy = floppy_type + UDP->native_format;
else
return "(null)";
}
if (floppy->name)
return floppy->name;
else
return "(null)";
}
 
 
/* raw commands */
static void raw_cmd_done(int flag)
{
int i;
 
if (!flag) {
raw_cmd->flags |= FD_RAW_FAILURE;
raw_cmd->flags |= FD_RAW_HARDFAILURE;
} else {
raw_cmd->reply_count = inr;
for (i=0; i< raw_cmd->reply_count; i++)
raw_cmd->reply[i] = reply_buffer[i];
 
if (raw_cmd->flags & (FD_RAW_READ | FD_RAW_WRITE))
raw_cmd->length = fd_get_dma_residue();
if ((raw_cmd->flags & FD_RAW_SOFTFAILURE) &&
(!raw_cmd->reply_count || (raw_cmd->reply[0] & 0xc0)))
raw_cmd->flags |= FD_RAW_FAILURE;
 
if (disk_change(current_drive))
raw_cmd->flags |= FD_RAW_DISK_CHANGE;
else
raw_cmd->flags &= ~FD_RAW_DISK_CHANGE;
if (raw_cmd->flags & FD_RAW_NO_MOTOR_AFTER)
motor_off_callback(current_drive);
 
if (raw_cmd->next &&
(!(raw_cmd->flags & FD_RAW_FAILURE) ||
!(raw_cmd->flags & FD_RAW_STOP_IF_FAILURE)) &&
((raw_cmd->flags & FD_RAW_FAILURE) ||
!(raw_cmd->flags &FD_RAW_STOP_IF_SUCCESS))) {
raw_cmd = raw_cmd->next;
return;
}
}
generic_done(flag);
}
 
 
static struct cont_t raw_cmd_cont={
success_and_wakeup,
floppy_start,
generic_failure,
raw_cmd_done
};
 
static inline int raw_cmd_copyout(int cmd, char *param,
struct floppy_raw_cmd *ptr)
{
struct old_floppy_raw_cmd old_raw_cmd;
int ret;
 
while(ptr) {
if (cmd == OLDFDRAWCMD) {
old_raw_cmd.flags = ptr->flags;
old_raw_cmd.data = ptr->data;
old_raw_cmd.length = ptr->length;
old_raw_cmd.rate = ptr->rate;
old_raw_cmd.reply_count = ptr->reply_count;
memcpy(old_raw_cmd.reply, ptr->reply, 7);
COPYOUT(old_raw_cmd);
param += sizeof(old_raw_cmd);
} else {
COPYOUT(*ptr);
param += sizeof(struct floppy_raw_cmd);
}
 
if ((ptr->flags & FD_RAW_READ) && ptr->buffer_length){
if (ptr->length>=0 && ptr->length<=ptr->buffer_length)
ECALL(fd_copyout(ptr->data,
ptr->kernel_data,
ptr->buffer_length -
ptr->length));
}
ptr = ptr->next;
}
return 0;
}
 
 
static void raw_cmd_free(struct floppy_raw_cmd **ptr)
{
struct floppy_raw_cmd *next,*this;
 
this = *ptr;
*ptr = 0;
while(this) {
if (this->buffer_length) {
fd_dma_mem_free((unsigned long)this->kernel_data,
this->buffer_length);
this->buffer_length = 0;
}
next = this->next;
kfree(this);
this = next;
}
}
 
 
static inline int raw_cmd_copyin(int cmd, char *param,
struct floppy_raw_cmd **rcmd)
{
struct floppy_raw_cmd *ptr;
struct old_floppy_raw_cmd old_raw_cmd;
int ret;
int i;
*rcmd = 0;
while(1) {
ptr = (struct floppy_raw_cmd *)
kmalloc(sizeof(struct floppy_raw_cmd), GFP_USER);
if (!ptr)
return -ENOMEM;
*rcmd = ptr;
if (cmd == OLDFDRAWCMD){
COPYIN(old_raw_cmd);
ptr->flags = old_raw_cmd.flags;
ptr->data = old_raw_cmd.data;
ptr->length = old_raw_cmd.length;
ptr->rate = old_raw_cmd.rate;
ptr->cmd_count = old_raw_cmd.cmd_count;
ptr->track = old_raw_cmd.track;
ptr->phys_length = 0;
ptr->next = 0;
ptr->buffer_length = 0;
memcpy(ptr->cmd, old_raw_cmd.cmd, 9);
param += sizeof(struct old_floppy_raw_cmd);
if (ptr->cmd_count > 9)
return -EINVAL;
} else {
COPYIN(*ptr);
ptr->next = 0;
ptr->buffer_length = 0;
param += sizeof(struct floppy_raw_cmd);
if (ptr->cmd_count > 33)
/* the command may now also take up the space
* initially intended for the reply & the
* reply count. Needed for long 82078 commands
* such as RESTORE, which takes ... 17 command
* bytes. Murphy's law #137: When you reserve
* 16 bytes for a structure, you'll one day
* discover that you really need 17...
*/
return -EINVAL;
}
 
for (i=0; i< 16; i++)
ptr->reply[i] = 0;
ptr->resultcode = 0;
ptr->kernel_data = 0;
 
if (ptr->flags & (FD_RAW_READ | FD_RAW_WRITE)) {
if (ptr->length <= 0)
return -EINVAL;
ptr->kernel_data =(char*)fd_dma_mem_alloc(ptr->length);
if (!ptr->kernel_data)
return -ENOMEM;
ptr->buffer_length = ptr->length;
}
if ( ptr->flags & FD_RAW_READ )
ECALL( verify_area( VERIFY_WRITE, ptr->data,
ptr->length ));
if (ptr->flags & FD_RAW_WRITE)
ECALL(fd_copyin(ptr->data, ptr->kernel_data,
ptr->length));
rcmd = & (ptr->next);
if (!(ptr->flags & FD_RAW_MORE))
return 0;
ptr->rate &= 0x43;
}
}
 
 
static int raw_cmd_ioctl(int cmd, void *param)
{
int drive, ret, ret2;
struct floppy_raw_cmd *my_raw_cmd;
 
if (FDCS->rawcmd <= 1)
FDCS->rawcmd = 1;
for (drive= 0; drive < N_DRIVE; drive++){
if (FDC(drive) != fdc)
continue;
if (drive == current_drive){
if (UDRS->fd_ref > 1){
FDCS->rawcmd = 2;
break;
}
} else if (UDRS->fd_ref){
FDCS->rawcmd = 2;
break;
}
}
 
if (FDCS->reset)
return -EIO;
 
ret = raw_cmd_copyin(cmd, param, &my_raw_cmd);
if (ret) {
raw_cmd_free(&my_raw_cmd);
return ret;
}
 
raw_cmd = my_raw_cmd;
cont = &raw_cmd_cont;
ret=wait_til_done(floppy_start,1);
#ifdef DCL_DEBUG
if (DP->flags & FD_DEBUG){
DPRINT("calling disk change from raw_cmd ioctl\n");
}
#endif
 
if (ret != -EINTR && FDCS->reset)
ret = -EIO;
 
DRS->track = NO_TRACK;
 
ret2 = raw_cmd_copyout(cmd, param, my_raw_cmd);
if (!ret)
ret = ret2;
raw_cmd_free(&my_raw_cmd);
return ret;
}
 
static int invalidate_drive(kdev_t rdev)
{
/* invalidate the buffer track to force a reread */
set_bit(DRIVE(rdev), &fake_change);
process_fd_request();
check_disk_change(rdev);
return 0;
}
 
 
static inline void clear_write_error(int drive)
{
CLEARSTRUCT(UDRWE);
}
 
static inline int set_geometry(unsigned int cmd, struct floppy_struct *g,
int drive, int type, kdev_t device)
{
int cnt;
 
/* sanity checking for parameters.*/
if (g->sect <= 0 ||
g->head <= 0 ||
g->track <= 0 ||
g->track > UDP->tracks>>STRETCH(g) ||
/* check if reserved bits are set */
(g->stretch&~(FD_STRETCH|FD_SWAPSIDES)) != 0)
return -EINVAL;
if (type){
if (!suser())
return -EPERM;
LOCK_FDC(drive,1);
for (cnt = 0; cnt < N_DRIVE; cnt++){
if (ITYPE(drive_state[cnt].fd_device) == type &&
drive_state[cnt].fd_ref)
set_bit(drive, &fake_change);
}
floppy_type[type] = *g;
floppy_type[type].name="user format";
for (cnt = type << 2; cnt < (type << 2) + 4; cnt++)
floppy_sizes[cnt]= floppy_sizes[cnt+0x80]=
floppy_type[type].size>>1;
process_fd_request();
for (cnt = 0; cnt < N_DRIVE; cnt++){
if (ITYPE(drive_state[cnt].fd_device) == type &&
drive_state[cnt].fd_ref)
check_disk_change(
MKDEV(FLOPPY_MAJOR,
drive_state[cnt].fd_device));
}
} else {
LOCK_FDC(drive,1);
if (cmd != FDDEFPRM)
/* notice a disk change immediately, else
* we loose our settings immediately*/
CALL(poll_drive(1, FD_RAW_NEED_DISK));
user_params[drive] = *g;
if (buffer_drive == drive)
SUPBOUND(buffer_max, user_params[drive].sect);
current_type[drive] = &user_params[drive];
floppy_sizes[drive] = user_params[drive].size >> 1;
if (cmd == FDDEFPRM)
DRS->keep_data = -1;
else
DRS->keep_data = 1;
/* invalidation. Invalidate only when needed, i.e.
* when there are already sectors in the buffer cache
* whose number will change. This is useful, because
* mtools often changes the geometry of the disk after
* looking at the boot block */
if (DRS->maxblock > user_params[drive].sect || DRS->maxtrack)
invalidate_drive(device);
else
process_fd_request();
}
return 0;
}
 
/* handle obsolete ioctl's */
static struct translation_entry {
int newcmd;
int oldcmd;
int oldsize; /* size of 0x00xx-style ioctl. Reflects old structures, thus
* use numeric values. NO SIZEOFS */
} translation_table[]= {
{FDCLRPRM, 0, 0},
{FDSETPRM, 1, 28},
{FDDEFPRM, 2, 28},
{FDGETPRM, 3, 28},
{FDMSGON, 4, 0},
{FDMSGOFF, 5, 0},
{FDFMTBEG, 6, 0},
{FDFMTTRK, 7, 12},
{FDFMTEND, 8, 0},
{FDSETEMSGTRESH, 10, 0},
{FDFLUSH, 11, 0},
{FDSETMAXERRS, 12, 20},
{OLDFDRAWCMD, 30, 0},
{FDGETMAXERRS, 14, 20},
{FDGETDRVTYP, 16, 16},
{FDSETDRVPRM, 20, 88},
{FDGETDRVPRM, 21, 88},
{FDGETDRVSTAT, 22, 52},
{FDPOLLDRVSTAT, 23, 52},
{FDRESET, 24, 0},
{FDGETFDCSTAT, 25, 40},
{FDWERRORCLR, 27, 0},
{FDWERRORGET, 28, 24},
{FDRAWCMD, 0, 0},
{FDEJECT, 0, 0},
{FDTWADDLE, 40, 0} };
 
static inline int normalize_0x02xx_ioctl(int *cmd, int *size)
{
int i;
 
for (i=0; i < ARRAY_SIZE(translation_table); i++) {
if ((*cmd & 0xffff) == (translation_table[i].newcmd & 0xffff)){
*size = _IOC_SIZE(*cmd);
*cmd = translation_table[i].newcmd;
if (*size > _IOC_SIZE(*cmd)) {
printk("ioctl not yet supported\n");
return -EFAULT;
}
return 0;
}
}
return -EINVAL;
}
 
static inline int xlate_0x00xx_ioctl(int *cmd, int *size)
{
int i;
/* old ioctls' for kernels <= 1.3.33 */
/* When the next even release will come around, we'll start
* warning against these.
* When the next odd release will come around, we'll fail with
* -EINVAL */
if(strcmp(system_utsname.version, "1.4.0") >= 0)
printk("obsolete floppy ioctl %x\n", *cmd);
if((system_utsname.version[0] == '1' &&
strcmp(system_utsname.version, "1.5.0") >= 0) ||
(system_utsname.version[0] >= '2' &&
strcmp(system_utsname.version, "2.1.0") >= 0))
return -EINVAL;
for (i=0; i < ARRAY_SIZE(translation_table); i++) {
if (*cmd == translation_table[i].oldcmd) {
*size = translation_table[i].oldsize;
*cmd = translation_table[i].newcmd;
return 0;
}
}
return -EINVAL;
}
 
static int fd_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
unsigned long param)
{
#define IOCTL_MODE_BIT 8
#define OPEN_WRITE_BIT 16
#define IOCTL_ALLOWED (filp && (filp->f_mode & IOCTL_MODE_BIT))
#define OUT(c,x) case c: outparam = (const char *) (x); break
#define IN(c,x,tag) case c: *(x) = inparam. tag ; return 0
 
int i,drive,type;
kdev_t device;
int ret;
int size;
union inparam {
struct floppy_struct g; /* geometry */
struct format_descr f;
struct floppy_max_errors max_errors;
struct floppy_drive_params dp;
} inparam; /* parameters coming from user space */
const char *outparam; /* parameters passed back to user space */
 
device = inode->i_rdev;
switch (cmd) {
RO_IOCTLS(device,param);
}
type = TYPE(device);
drive = DRIVE(device);
 
/* convert compatibility eject ioctls into floppy eject ioctl.
* We do this in order to provide a means to eject floppy disks before
* installing the new fdutils package */
if(cmd == CDROMEJECT || /* CD-ROM eject */
cmd == 0x6470 /* SunOS floppy eject */) {
DPRINT("obsolete eject ioctl\n");
DPRINT("please use floppycontrol --eject\n");
cmd = FDEJECT;
}
 
/* convert the old style command into a new style command */
if ((cmd & 0xff00) == 0x0200) {
ECALL(normalize_0x02xx_ioctl(&cmd, &size));
} else if ((cmd & 0xff00) == 0x0000) {
ECALL(xlate_0x00xx_ioctl(&cmd, &size));
} else
return -EINVAL;
 
/* permission checks */
if (((cmd & 0x80) && !suser()) ||
((cmd & 0x40) && !IOCTL_ALLOWED))
return -EPERM;
 
/* verify writability of result, and fail early */
if (_IOC_DIR(cmd) & _IOC_READ)
ECALL(verify_area(VERIFY_WRITE,(void *) param, size));
/* copyin */
CLEARSTRUCT(&inparam);
if (_IOC_DIR(cmd) & _IOC_WRITE)
ECALL(fd_copyin((void *)param, &inparam, size))
 
switch (cmd) {
case FDEJECT:
if(UDRS->fd_ref != 1)
/* somebody else has this drive open */
return -EBUSY;
LOCK_FDC(drive,1);
 
/* do the actual eject. Fails on
* non-Sparc architectures */
ret=fd_eject(UNIT(drive));
 
USETF(FD_DISK_CHANGED);
USETF(FD_VERIFY);
process_fd_request();
return ret;
case FDCLRPRM:
LOCK_FDC(drive,1);
current_type[drive] = NULL;
floppy_sizes[drive] = MAX_DISK_SIZE;
UDRS->keep_data = 0;
return invalidate_drive(device);
case FDSETPRM:
case FDDEFPRM:
return set_geometry(cmd, & inparam.g,
drive, type, device);
case FDGETPRM:
LOCK_FDC(drive,1);
CALL(poll_drive(1,0));
process_fd_request();
if (type)
outparam = (char *) &floppy_type[type];
else
outparam = (char *) current_type[drive];
if(!outparam)
return -ENODEV;
break;
 
case FDMSGON:
UDP->flags |= FTD_MSG;
return 0;
case FDMSGOFF:
UDP->flags &= ~FTD_MSG;
return 0;
 
case FDFMTBEG:
LOCK_FDC(drive,1);
CALL(poll_drive(1, FD_RAW_NEED_DISK));
ret = UDRS->flags;
process_fd_request();
if(ret & FD_VERIFY)
return -ENODEV;
if(!(ret & FD_DISK_WRITABLE))
return -EROFS;
return 0;
case FDFMTTRK:
if (UDRS->fd_ref != 1)
return -EBUSY;
return do_format(device, &inparam.f);
case FDFMTEND:
case FDFLUSH:
LOCK_FDC(drive,1);
return invalidate_drive(device);
 
case FDSETEMSGTRESH:
UDP->max_errors.reporting =
(unsigned short) (param & 0x0f);
return 0;
OUT(FDGETMAXERRS, &UDP->max_errors);
IN(FDSETMAXERRS, &UDP->max_errors, max_errors);
 
case FDGETDRVTYP:
outparam = drive_name(type,drive);
SUPBOUND(size,strlen(outparam)+1);
break;
 
IN(FDSETDRVPRM, UDP, dp);
OUT(FDGETDRVPRM, UDP);
 
case FDPOLLDRVSTAT:
LOCK_FDC(drive,1);
CALL(poll_drive(1, FD_RAW_NEED_DISK));
process_fd_request();
/* fall through */
OUT(FDGETDRVSTAT, UDRS);
 
case FDRESET:
return user_reset_fdc(drive, (int)param, 1);
 
OUT(FDGETFDCSTAT,UFDCS);
 
case FDWERRORCLR:
CLEARSTRUCT(UDRWE);
return 0;
OUT(FDWERRORGET,UDRWE);
 
case OLDFDRAWCMD:
case FDRAWCMD:
if (type)
return -EINVAL;
LOCK_FDC(drive,1);
set_floppy(device);
CALL(i = raw_cmd_ioctl(cmd,(void *) param));
process_fd_request();
return i;
 
case FDTWADDLE:
LOCK_FDC(drive,1);
twaddle();
process_fd_request();
return 0;
 
default:
return -EINVAL;
}
 
if (_IOC_DIR(cmd) & _IOC_READ)
return fd_copyout((void *)param, outparam, size);
else
return 0;
#undef IOCTL_ALLOWED
#undef OUT
#undef IN
}
 
static void config_types(void)
{
int first=1;
int drive;
 
/* read drive info out of physical CMOS */
drive=0;
if (!UDP->cmos)
UDP->cmos= FLOPPY0_TYPE;
drive=1;
if (!UDP->cmos && FLOPPY1_TYPE)
UDP->cmos = FLOPPY1_TYPE;
 
/* XXX */
/* additional physical CMOS drive detection should go here */
 
for (drive=0; drive < N_DRIVE; drive++){
if (UDP->cmos >= 16)
UDP->cmos = 0;
if (UDP->cmos >= 0 && UDP->cmos <= NUMBER(default_drive_params))
memcpy((char *) UDP,
(char *) (&default_drive_params[(int)UDP->cmos].params),
sizeof(struct floppy_drive_params));
if (UDP->cmos){
if (first)
printk(KERN_INFO "Floppy drive(s): ");
else
printk(", ");
first=0;
if (UDP->cmos > 0){
allowed_drive_mask |= 1 << drive;
printk("fd%d is %s", drive,
default_drive_params[(int)UDP->cmos].name);
} else
printk("fd%d is unknown type %d",drive,
(unsigned int)UDP->cmos);
}
}
if (!first)
printk("\n");
}
 
static int floppy_read(struct inode * inode, struct file * filp,
char * buf, int count)
{
int drive = DRIVE(inode->i_rdev);
 
check_disk_change(inode->i_rdev);
if (UTESTF(FD_DISK_CHANGED))
return -ENXIO;
return block_read(inode, filp, buf, count);
}
 
static int floppy_write(struct inode * inode, struct file * filp,
const char * buf, int count)
{
int block;
int ret;
int drive = DRIVE(inode->i_rdev);
 
if (!UDRS->maxblock)
UDRS->maxblock=1;/* make change detectable */
check_disk_change(inode->i_rdev);
if (UTESTF(FD_DISK_CHANGED))
return -ENXIO;
if (!UTESTF(FD_DISK_WRITABLE))
return -EROFS;
block = (filp->f_pos + count) >> 9;
INFBOUND(UDRS->maxblock, block);
ret= block_write(inode, filp, buf, count);
return ret;
}
 
static void floppy_release(struct inode * inode, struct file * filp)
{
int drive;
 
drive = DRIVE(inode->i_rdev);
 
if (!filp || (filp->f_mode & (2 | OPEN_WRITE_BIT)))
/* if the file is mounted OR (writable now AND writable at
* open time) Linus: Does this cover all cases? */
block_fsync(inode,filp);
 
if (UDRS->fd_ref < 0)
UDRS->fd_ref=0;
else if (!UDRS->fd_ref--) {
DPRINT("floppy_release with fd_ref == 0");
UDRS->fd_ref = 0;
}
floppy_release_irq_and_dma();
}
 
/*
* floppy_open check for aliasing (/dev/fd0 can be the same as
* /dev/PS0 etc), and disallows simultaneous access to the same
* drive with different device numbers.
*/
#define RETERR(x) do{floppy_release(inode,filp); return -(x);}while(0)
 
static int floppy_open(struct inode * inode, struct file * filp)
{
int drive;
int old_dev;
int try;
char *tmp;
 
if (!filp) {
DPRINT("Weird, open called with filp=0\n");
return -EIO;
}
 
drive = DRIVE(inode->i_rdev);
if (drive >= N_DRIVE ||
!(allowed_drive_mask & (1 << drive)) ||
fdc_state[FDC(drive)].version == FDC_NONE)
return -ENXIO;
 
if (TYPE(inode->i_rdev) >= NUMBER(floppy_type))
return -ENXIO;
old_dev = UDRS->fd_device;
if (UDRS->fd_ref && old_dev != MINOR(inode->i_rdev))
return -EBUSY;
 
if (!UDRS->fd_ref && (UDP->flags & FD_BROKEN_DCL)){
USETF(FD_DISK_CHANGED);
USETF(FD_VERIFY);
}
 
if (UDRS->fd_ref == -1 ||
(UDRS->fd_ref && (filp->f_flags & O_EXCL)))
return -EBUSY;
 
if (floppy_grab_irq_and_dma())
return -EBUSY;
 
if (filp->f_flags & O_EXCL)
UDRS->fd_ref = -1;
else
UDRS->fd_ref++;
 
if (!floppy_track_buffer){
/* if opening an ED drive, reserve a big buffer,
* else reserve a small one */
if ((UDP->cmos == 6) || (UDP->cmos == 5))
try = 64; /* Only 48 actually useful */
else
try = 32; /* Only 24 actually useful */
 
tmp=(char *)fd_dma_mem_alloc(1024 * try);
if (!tmp) {
try >>= 1; /* buffer only one side */
INFBOUND(try, 16);
tmp= (char *)fd_dma_mem_alloc(1024*try);
}
if (!tmp) {
DPRINT("Unable to allocate DMA memory\n");
RETERR(ENXIO);
}
if (floppy_track_buffer)
fd_dma_mem_free((unsigned long)tmp,try*1024);
else {
buffer_min = buffer_max = -1;
floppy_track_buffer = tmp;
max_buffer_sectors = try;
}
}
 
UDRS->fd_device = MINOR(inode->i_rdev);
if (old_dev != -1 && old_dev != MINOR(inode->i_rdev)) {
if (buffer_drive == drive)
buffer_track = -1;
invalidate_buffers(MKDEV(FLOPPY_MAJOR,old_dev));
}
 
/* Allow ioctls if we have write-permissions even if read-only open */
if ((filp->f_mode & 2) || (permission(inode,2) == 0))
filp->f_mode |= IOCTL_MODE_BIT;
if (filp->f_mode & 2)
filp->f_mode |= OPEN_WRITE_BIT;
 
if (UFDCS->rawcmd == 1)
UFDCS->rawcmd = 2;
 
if (filp->f_flags & O_NDELAY)
return 0;
if (filp->f_mode & 3) {
UDRS->last_checked = 0;
check_disk_change(inode->i_rdev);
if (UTESTF(FD_DISK_CHANGED))
RETERR(ENXIO);
}
if ((filp->f_mode & 2) && !(UTESTF(FD_DISK_WRITABLE)))
RETERR(EROFS);
return 0;
#undef RETERR
}
 
/*
* Check if the disk has been changed or if a change has been faked.
*/
static int check_floppy_change(kdev_t dev)
{
int drive = DRIVE(dev);
 
if (MAJOR(dev) != MAJOR_NR) {
DPRINT("check_floppy_change: not a floppy\n");
return 0;
}
 
if (UTESTF(FD_DISK_CHANGED) || UTESTF(FD_VERIFY))
return 1;
 
if (UDP->checkfreq < jiffies - UDRS->last_checked){
lock_fdc(drive,0);
poll_drive(0,0);
process_fd_request();
}
 
if (UTESTF(FD_DISK_CHANGED) ||
UTESTF(FD_VERIFY) ||
test_bit(drive, &fake_change) ||
(!TYPE(dev) && !current_type[drive]))
return 1;
return 0;
}
 
/* revalidate the floppy disk, i.e. trigger format autodetection by reading
* the bootblock (block 0). "Autodetection" is also needed to check whether
* there is a disk in the drive at all... Thus we also do it for fixed
* geometry formats */
static int floppy_revalidate(kdev_t dev)
{
#define NO_GEOM (!current_type[drive] && !TYPE(dev))
struct buffer_head * bh;
int drive=DRIVE(dev);
int cf;
 
if (UTESTF(FD_DISK_CHANGED) ||
UTESTF(FD_VERIFY) ||
test_bit(drive, &fake_change) ||
NO_GEOM){
lock_fdc(drive,0);
cf = UTESTF(FD_DISK_CHANGED) || UTESTF(FD_VERIFY);
if (!(cf || test_bit(drive, &fake_change) || NO_GEOM)){
process_fd_request(); /*already done by another thread*/
return 0;
}
UDRS->maxblock = 0;
UDRS->maxtrack = 0;
if (buffer_drive == drive)
buffer_track = -1;
clear_bit(drive, &fake_change);
UCLEARF(FD_DISK_CHANGED);
if (cf)
UDRS->generation++;
if (NO_GEOM){
/* auto-sensing */
int size = floppy_blocksizes[MINOR(dev)];
if (!size)
size = 1024;
if (!(bh = getblk(dev,0,size))){
process_fd_request();
return 1;
}
if (bh && !buffer_uptodate(bh))
ll_rw_block(READ, 1, &bh);
process_fd_request();
wait_on_buffer(bh);
brelse(bh);
return 0;
}
if (cf)
poll_drive(0, FD_RAW_NEED_DISK);
process_fd_request();
}
return 0;
}
 
static struct file_operations floppy_fops = {
NULL, /* lseek - default */
floppy_read, /* read - general block-dev read */
floppy_write, /* write - general block-dev write */
NULL, /* readdir - bad */
NULL, /* select */
fd_ioctl, /* ioctl */
NULL, /* mmap */
floppy_open, /* open */
floppy_release, /* release */
block_fsync, /* fsync */
NULL, /* fasync */
check_floppy_change, /* media_change */
floppy_revalidate, /* revalidate */
};
 
/*
* Floppy Driver initialization
* =============================
*/
 
/* Determine the floppy disk controller type */
/* This routine was written by David C. Niemi */
static char get_fdc_version(void)
{
int r;
 
output_byte(FD_DUMPREGS); /* 82072 and better know DUMPREGS */
if (FDCS->reset)
return FDC_NONE;
if ((r = result()) <= 0x00)
return FDC_NONE; /* No FDC present ??? */
if ((r==1) && (reply_buffer[0] == 0x80)){
printk(KERN_INFO "FDC %d is an 8272A\n",fdc);
return FDC_8272A; /* 8272a/765 don't know DUMPREGS */
}
if (r != 10) {
printk("FDC %d init: DUMPREGS: unexpected return of %d bytes.\n",
fdc, r);
return FDC_UNKNOWN;
}
 
if(!fdc_configure()) {
printk(KERN_INFO "FDC %d is an 82072\n",fdc);
return FDC_82072; /* 82072 doesn't know CONFIGURE */
}
 
output_byte(FD_PERPENDICULAR);
if(need_more_output() == MORE_OUTPUT) {
output_byte(0);
} else {
printk(KERN_INFO "FDC %d is an 82072A\n", fdc);
return FDC_82072A; /* 82072A as found on Sparcs. */
}
 
output_byte(FD_UNLOCK);
r = result();
if ((r == 1) && (reply_buffer[0] == 0x80)){
printk(KERN_INFO "FDC %d is a pre-1991 82077\n", fdc);
return FDC_82077_ORIG; /* Pre-1991 82077, doesn't know
* LOCK/UNLOCK */
}
if ((r != 1) || (reply_buffer[0] != 0x00)) {
printk("FDC %d init: UNLOCK: unexpected return of %d bytes.\n",
fdc, r);
return FDC_UNKNOWN;
}
output_byte(FD_PARTID);
r = result();
if (r != 1) {
printk("FDC %d init: PARTID: unexpected return of %d bytes.\n",
fdc, r);
return FDC_UNKNOWN;
}
if (reply_buffer[0] == 0x80) {
printk(KERN_INFO "FDC %d is a post-1991 82077\n",fdc);
return FDC_82077; /* Revised 82077AA passes all the tests */
}
switch (reply_buffer[0] >> 5) {
case 0x0:
/* Either a 82078-1 or a 82078SL running at 5Volt */
printk(KERN_INFO "FDC %d is an 82078.\n",fdc);
return FDC_82078;
case 0x1:
printk(KERN_INFO "FDC %d is a 44pin 82078\n",fdc);
return FDC_82078;
case 0x2:
printk(KERN_INFO "FDC %d is a S82078B\n", fdc);
return FDC_S82078B;
case 0x3:
printk(KERN_INFO "FDC %d is a National Semiconductor PC87306\n", fdc);
return FDC_87306;
default:
printk(KERN_INFO "FDC %d init: 82078 variant with unknown PARTID=%d.\n",
fdc, reply_buffer[0] >> 5);
return FDC_82078_UNKN;
}
} /* get_fdc_version */
 
/* lilo configuration */
 
/* we make the invert_dcl function global. One day, somebody might
* want to centralize all thinkpad related options into one lilo option,
* there are just so many thinkpad related quirks! */
void floppy_invert_dcl(int *ints,int param)
{
int i;
 
for (i=0; i < ARRAY_SIZE(default_drive_params); i++){
if (param)
default_drive_params[i].params.flags |= 0x80;
else
default_drive_params[i].params.flags &= ~0x80;
}
DPRINT("Configuring drives for inverted dcl\n");
}
 
static void daring(int *ints,int param)
{
int i;
 
for (i=0; i < ARRAY_SIZE(default_drive_params); i++){
if (param){
default_drive_params[i].params.select_delay = 0;
default_drive_params[i].params.flags |= FD_SILENT_DCL_CLEAR;
} else {
default_drive_params[i].params.select_delay = 2*HZ/100;
default_drive_params[i].params.flags &= ~FD_SILENT_DCL_CLEAR;
}
}
DPRINT("Assuming %s floppy hardware\n", param ? "standard" : "broken");
}
 
static void set_cmos(int *ints, int dummy)
{
int current_drive=0;
 
if (ints[0] != 2){
DPRINT("wrong number of parameter for cmos\n");
return;
}
current_drive = ints[1];
if (current_drive < 0 || current_drive >= 8){
DPRINT("bad drive for set_cmos\n");
return;
}
if (current_drive >= 4 && !FDC2)
FDC2 = 0x370;
if (ints[2] <= 0 ||
(ints[2] >= NUMBER(default_drive_params) && ints[2] != 16)){
DPRINT("bad cmos code %d\n", ints[2]);
return;
}
DP->cmos = ints[2];
DPRINT("setting cmos code to %d\n", ints[2]);
}
 
static struct param_table {
const char *name;
void (*fn)(int *ints, int param);
int *var;
int def_param;
} config_params[]={
{ "allowed_drive_mask", 0, &allowed_drive_mask, 0xff },
{ "all_drives", 0, &allowed_drive_mask, 0xff },
{ "asus_pci", 0, &allowed_drive_mask, 0x33 },
 
{ "daring", daring, 0, 1},
 
{ "two_fdc", 0, &FDC2, 0x370 },
{ "one_fdc", 0, &FDC2, 0 },
 
{ "thinkpad", floppy_invert_dcl, 0, 1 },
 
{ "nodma", 0, &use_virtual_dma, 1 },
{ "omnibook", 0, &use_virtual_dma, 1 },
{ "dma", 0, &use_virtual_dma, 0 },
 
{ "fifo_depth", 0, &fifo_depth, 0xa },
{ "nofifo", 0, &no_fifo, 0x20 },
{ "usefifo", 0, &no_fifo, 0 },
 
{ "cmos", set_cmos, 0, 0 },
 
{ "unexpected_interrupts", 0, &print_unex, 1 },
{ "no_unexpected_interrupts", 0, &print_unex, 0 },
{ "L40SX", 0, &print_unex, 0 } };
 
#define FLOPPY_SETUP
void floppy_setup(char *str, int *ints)
{
int i;
int param;
if (str)
for (i=0; i< ARRAY_SIZE(config_params); i++){
if (strcmp(str,config_params[i].name) == 0){
if (ints[0])
param = ints[1];
else
param = config_params[i].def_param;
if(config_params[i].fn)
config_params[i].fn(ints,param);
if(config_params[i].var) {
DPRINT("%s=%d\n", str, param);
*config_params[i].var = param;
}
return;
}
}
if (str) {
DPRINT("unknown floppy option [%s]\n", str);
DPRINT("allowed options are:");
for (i=0; i< ARRAY_SIZE(config_params); i++)
printk(" %s",config_params[i].name);
printk("\n");
} else
DPRINT("botched floppy option\n");
DPRINT("Read linux/arch/arm/drivers/block/README.fd\n");
}
 
int floppy_init(void)
{
int i,unit,drive;
int have_no_fdc= -EIO;
 
raw_cmd = NULL;
 
if (register_blkdev(MAJOR_NR,"fd",&floppy_fops)) {
printk("Unable to get major %d for floppy\n",MAJOR_NR);
return -EBUSY;
}
 
for (i=0; i<256; i++)
if (ITYPE(i))
floppy_sizes[i] = floppy_type[ITYPE(i)].size >> 1;
else
floppy_sizes[i] = MAX_DISK_SIZE;
 
blk_size[MAJOR_NR] = floppy_sizes;
blksize_size[MAJOR_NR] = floppy_blocksizes;
blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
reschedule_timeout(MAXTIMEOUT, "floppy init", MAXTIMEOUT);
config_types();
 
for (i = 0; i < N_FDC; i++) {
fdc = i;
CLEARSTRUCT(FDCS);
FDCS->dtr = -1;
FDCS->dor = 0x4;
#ifdef __sparc__
/*sparcs don't have a DOR reset which we can fall back on to*/
FDCS->version = FDC_82072A;
#endif
}
 
fdc_state[0].address = FDC1;
#if N_FDC > 1
fdc_state[1].address = FDC2;
#endif
 
if (floppy_grab_irq_and_dma()){
unregister_blkdev(MAJOR_NR,"fd");
return -EBUSY;
}
 
/* initialise drive state */
for (drive = 0; drive < N_DRIVE; drive++) {
CLEARSTRUCT(UDRS);
CLEARSTRUCT(UDRWE);
UDRS->flags = FD_VERIFY | FD_DISK_NEWCHANGE | FD_DISK_CHANGED;
UDRS->fd_device = -1;
floppy_track_buffer = NULL;
max_buffer_sectors = 0;
}
 
for (i = 0; i < N_FDC; i++) {
fdc = i;
FDCS->driver_version = FD_DRIVER_VERSION;
for (unit=0; unit<4; unit++)
FDCS->track[unit] = 0;
if (FDCS->address == -1)
continue;
FDCS->rawcmd = 2;
if (user_reset_fdc(-1,FD_RESET_ALWAYS,0)){
FDCS->address = -1;
FDCS->version = FDC_NONE;
continue;
}
/* Try to determine the floppy controller type */
FDCS->version = get_fdc_version();
if (FDCS->version == FDC_NONE){
FDCS->address = -1;
continue;
}
 
request_region(FDCS->address, 6, "floppy");
request_region(FDCS->address+7, 1, "floppy DIR");
/* address + 6 is reserved, and may be taken by IDE.
* Unfortunately, Adaptec doesn't know this :-(, */
 
have_no_fdc = 0;
/* Not all FDCs seem to be able to handle the version command
* properly, so force a reset for the standard FDC clones,
* to avoid interrupt garbage.
*/
user_reset_fdc(-1,FD_RESET_ALWAYS,0);
#ifdef CONFIG_ARM
fd_scandrives();
#endif
}
fdc=0;
del_timer(&fd_timeout);
current_drive = 0;
floppy_release_irq_and_dma();
initialising=0;
if (have_no_fdc) {
DPRINT("no floppy controllers found\n");
unregister_blkdev(MAJOR_NR,"fd");
}
return have_no_fdc;
}
 
static int floppy_grab_irq_and_dma(void)
{
int i;
unsigned long flags;
 
INT_OFF;
if (usage_count++){
INT_ON;
return 0;
}
INT_ON;
MOD_INC_USE_COUNT;
for (i=0; i< N_FDC; i++){
if (fdc_state[i].address != -1){
fdc = i;
reset_fdc_info(1);
fd_setdor(FDCS->dor);
}
}
fdc = 0;
set_dor(0, ~0, 8); /* avoid immediate interrupt */
 
if (fd_request_irq()) {
DPRINT("Unable to grab IRQ%d for the floppy driver\n",
FLOPPY_IRQ);
MOD_DEC_USE_COUNT;
usage_count--;
return -1;
}
if (fd_request_dma()) {
DPRINT("Unable to grab DMA%d for the floppy driver\n",
FLOPPY_DMA);
fd_free_irq();
MOD_DEC_USE_COUNT;
usage_count--;
return -1;
}
for (fdc = 0; fdc < N_FDC; fdc++)
if (FDCS->address != -1)
fd_setdor(FDCS->dor);
fdc = 0;
fd_enable_irq();
return 0;
}
 
static void floppy_release_irq_and_dma(void)
{
#ifdef FLOPPY_SANITY_CHECK
int drive;
#endif
long tmpsize;
unsigned long tmpaddr;
unsigned long flags;
 
INT_OFF;
if (--usage_count){
INT_ON;
return;
}
INT_ON;
fd_disable_dma();
fd_free_dma();
fd_disable_irq();
fd_free_irq();
 
set_dor(0, ~0, 8);
#if N_FDC > 1
set_dor(1, ~8, 0);
#endif
floppy_enable_hlt();
 
if (floppy_track_buffer && max_buffer_sectors) {
tmpsize = max_buffer_sectors*1024;
tmpaddr = (unsigned long)floppy_track_buffer;
floppy_track_buffer = NULL;
max_buffer_sectors = 0;
buffer_min = buffer_max = -1;
fd_dma_mem_free(tmpaddr, tmpsize);
}
 
#ifdef FLOPPY_SANITY_CHECK
for (drive=0; drive < N_FDC * 4; drive++)
if (motor_off_timer[drive].next)
printk("motor off timer %d still active\n", drive);
 
if (fd_timeout.next)
printk("floppy timer still active:%s\n", timeout_message);
if (fd_timer.next)
printk("auxiliary floppy timer still active\n");
if (floppy_tq.sync)
printk("task queue still active\n");
#endif
MOD_DEC_USE_COUNT;
}
 
 
#ifdef MODULE
 
extern char *get_options(char *str, int *ints);
 
char *floppy=NULL;
 
static void parse_floppy_cfg_string(char *cfg)
{
char *ptr;
int ints[11];
 
while(*cfg) {
for(ptr = cfg;*cfg && *cfg != ' ' && *cfg != '\t'; cfg++);
if(*cfg) {
*cfg = '\0';
cfg++;
}
if(*ptr)
floppy_setup(get_options(ptr,ints),ints);
}
}
 
static void mod_setup(char *pattern, void (*setup)(char *, int *))
{
unsigned long i;
char c;
int j;
int match;
char buffer[100];
int ints[11];
int length = strlen(pattern)+1;
 
match=0;
j=1;
 
for (i=current->mm->env_start; i< current->mm->env_end; i ++){
c= get_fs_byte(i);
if (match){
if (j==99)
c='\0';
buffer[j] = c;
if (!c || c == ' ' || c == '\t'){
if (j){
buffer[j] = '\0';
setup(get_options(buffer,ints),ints);
}
j=0;
} else
j++;
if (!c)
break;
continue;
}
if ((!j && !c) || (j && c == pattern[j-1]))
j++;
else
j=0;
if (j==length){
match=1;
j=0;
}
}
}
 
 
#ifdef __cplusplus
extern "C" {
#endif
int init_module(void)
{
printk(KERN_INFO "inserting floppy driver for %s\n", kernel_version);
if(floppy)
parse_floppy_cfg_string(floppy);
else
mod_setup("floppy=", floppy_setup);
return floppy_init();
}
 
void cleanup_module(void)
{
int fdc, dummy;
for (fdc=0; fdc<2; fdc++)
if (FDCS->address != -1){
release_region(FDCS->address, 6);
release_region(FDCS->address+7, 1);
}
unregister_blkdev(MAJOR_NR, "fd");
 
blk_dev[MAJOR_NR].request_fn = 0;
/* eject disk, if any */
dummy = fd_eject(0);
}
 
#ifdef __cplusplus
}
#endif
 
#else
/* eject the boot floppy (if we need the drive for a different root floppy) */
/* This should only be called at boot time when we're sure that there's no
* resource contention. */
void floppy_eject(void)
{
int dummy;
floppy_grab_irq_and_dma();
lock_fdc(MAXTIMEOUT,0);
dummy=fd_eject(0);
process_fd_request();
floppy_release_irq_and_dma();
}
#endif
/fd1772.c
0,0 → 1,1671
/*
* linux/kernel/arch/arm/drivers/block/fd1772.c
* Based on ataflop.c in the m68k Linux
* Copyright (C) 1993 Greg Harp
* Atari Support by Bjoern Brauel, Roman Hodek
* Archimedes Support by Dave Gilbert (gilbertd@cs.man.ac.uk)
*
* Big cleanup Sep 11..14 1994 Roman Hodek:
* - Driver now works interrupt driven
* - Support for two drives; should work, but I cannot test that :-(
* - Reading is done in whole tracks and buffered to speed up things
* - Disk change detection and drive deselecting after motor-off
* similar to TOS
* - Autodetection of disk format (DD/HD); untested yet, because I
* don't have an HD drive :-(
*
* Fixes Nov 13 1994 Martin Schaller:
* - Autodetection works now
* - Support for 5 1/4" disks
* - Removed drive type (unknown on atari)
* - Do seeks with 8 Mhz
*
* Changes by Andreas Schwab:
* - After errors in multiple read mode try again reading single sectors
* (Feb 1995):
* - Clean up error handling
* - Set blk_size for proper size checking
* - Initialize track register when testing presence of floppy
* - Implement some ioctl's
*
* Changes by Torsten Lang:
* - When probing the floppies we should add the FDC1772CMDADD_H flag since
* the FDC1772 will otherwise wait forever when no disk is inserted...
*
* Things left to do:
* - Formatting
* - Maybe a better strategy for disk change detection (does anyone
* know one?)
* - There are some strange problems left: The strangest one is
* that, at least on my TT (4+4MB), the first 2 Bytes of the last
* page of the TT-Ram (!) change their contents (some bits get
* set) while a floppy DMA is going on. But there are no accesses
* to these memory locations from the kernel... (I tested that by
* making the page read-only). I cannot explain what's going on...
* - Sometimes the drive-change-detection stops to work. The
* function is still called, but the WP bit always reads as 0...
* Maybe a problem with the status reg mode or a timing problem.
* Note 10/12/94: The change detection now seems to work reliably.
* There is no proof, but I've seen no hang for a long time...
*
* ARCHIMEDES changes: (gilbertd@cs.man.ac.uk)
* 26/12/95 - Changed all names starting with FDC to FDC1772
* Removed all references to clock speed of FDC - we're stuck with 8MHz
* Modified disk_type structure to remove HD formats
*
* 7/ 1/96 - Wrote FIQ code, removed most remaining atariisms
*
* 13/ 1/96 - Well I think its read a single sector; but there is a problem
* fd_rwsec_done which is called in FIQ mode starts another transfer
* off (in fd_rwsec) while still in FIQ mode. Because its still in
* FIQ mode it can't service the DMA and loses data. So need to
* heavily restructure.
* 14/ 1/96 - Found that the definitions of the register numbers of the
* FDC were multiplied by 2 in the header for the 16bit words
* of the atari so half the writes were going in the wrong place.
* Also realised that the FIQ entry didn't make any attempt to
* preserve registers or return correctly; now in assembler.
*
* 11/ 2/96 - Hmm - doesn't work on real machine. Auto detect doesn't
* and hacking that past seems to wait forever - check motor
* being turned on.
*
* 17/ 2/96 - still having problems - forcing track to -1 when selecting
* new drives seems to allow it to read first few sectors
* but then we get solid hangs at apparently random places
* which change depending what is happening.
*
* 9/ 3/96 - Fiddled a lot of stuff around to move to kernel 1.3.35
* A lot of fiddling in DMA stuff. Having problems with it
* constnatly thinking its timeing out. Ah - its timeout
* was set to (6*HZ) rather than jiffies+(6*HZ). Now giving
* duff data!
*
* 5/ 4/96 - Made it use the new IOC_ macros rather than *ioc
* Hmm - giving unexpected FIQ and then timeouts
* 18/ 8/96 - Ran through indent -kr -i8
* Some changes to disc change detect; don't know how well it
* works.
* 24/ 8/96 - Put all the track buffering code back in from the atari
* code - I wonder if it will still work... No :-)
* Still works if I turn off track buffering.
* 25/ 8/96 - Changed the timer expires that I'd added back to be
* jiffies + ....; and it all sprang to life! Got 2.8K/sec
* off a cp -r of a 679K disc (showed 94% cpu usage!)
* (PC gets 14.3K/sec - 0% CPU!) Hmm - hard drive corrupt!
* Also perhaps that compile was with cache off.
* changed cli in fd_readtrack_check to cliIF
* changed vmallocs to kmalloc (whats the difference!!)
* Removed the busy wait loop in do_fd_request and replaced
* by a routine on tq_immediate; only 11% cpu on a dd off the
* raw disc - but the speed is the same.
* 1/ 9/96 - Idea (failed!) - set the 'disable spin-up seqeunce'
* when we read the track if we know the motor is on; didn't
* help - perhaps we have to do it in stepping as well.
* Nope. Still doesn't help.
* Hmm - what seems to be happening is that fd_readtrack_check
* is never getting called. Its job is to terminate the read
* just after we think we should have got the data; otherwise
* the fdc takes 1 second to timeout; which is what's happening
* Now I can see 'readtrack_timer' being set (which should do the
* call); but it never seems to be called - hmm!
* OK - I've moved the check to my tq_immediate code -
* and it WORKS! 13.95K/second at 19% CPU.
* I wish I knew why that timer didn't work.....
*
* 16/11/96 - Fiddled and frigged for 2.0.18
*
* 4/ 9/98 - Cleaned up a few compiler warnings
*/
 
#include <linux/config.h>
#include <linux/sched.h>
#include <linux/fs.h>
#include <linux/fcntl.h>
#include <linux/kernel.h>
#include <linux/interrupt.h>
#include <linux/timer.h>
#include <linux/tqueue.h>
#include <linux/fd.h>
#include <linux/fd1772.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/mm.h>
 
#include <asm/arch/oldlatches.h>
#include <asm/system.h>
#include <asm/bitops.h>
#include <asm/dma.h>
#include <asm/hardware.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/pgtable.h>
#include <asm/segment.h>
 
#define MAJOR_NR FLOPPY_MAJOR
#define FLOPPY_DMA 0
#include "blk.h"
 
/* Note: FD_MAX_UNITS could be redefined to 2 for the Atari (with
* little additional rework in this file). But I'm not yet sure if
* some other code depends on the number of floppies... (It is defined
* in a public header!)
*/
#if 0
#undef FD_MAX_UNITS
#define FD_MAX_UNITS 2
#endif
 
/* Ditto worries for Arc - DAG */
#define FD_MAX_UNITS 4
#define TRACKBUFFER 0
/*#define DEBUG*/
 
#ifdef DEBUG
#define DPRINT(a) printk a
#else
#define DPRINT(a)
#endif
 
/* Disk types: DD */
static struct archy_disk_type {
const char *name;
unsigned spt; /* sectors per track */
unsigned blocks; /* total number of blocks */
unsigned stretch; /* track doubling ? */
} disk_type[] = {
 
{ "d360", 9, 720, 0 }, /* 360kB diskette */
{ "D360", 9, 720, 1 }, /* 360kb in 720kb drive */
{ "D720", 9, 1440, 0 }, /* 720kb diskette (DD) */
/*{ "D820", 10,1640, 0}, *//* DD disk with 82 tracks/10 sectors
- DAG - can't see how type detect can distinguish this
from 720K until it reads block 4 by which time its too late! */
};
 
#define NUM_DISK_TYPES (sizeof(disk_type)/sizeof(*disk_type))
 
/*
* Maximum disk size (in kilobytes). This default is used whenever the
* current disk size is unknown.
*/
#define MAX_DISK_SIZE 720
 
static int floppy_sizes[256];
static int floppy_blocksizes[256] = {0,};
 
/* current info on each unit */
static struct archy_floppy_struct {
int connected; /* !=0 : drive is connected */
int autoprobe; /* !=0 : do autoprobe */
 
struct archy_disk_type *disktype; /* current type of disk */
 
int track; /* current head position or -1
* if unknown */
unsigned int steprate; /* steprate setting */
unsigned int wpstat; /* current state of WP signal
* (for disk change detection) */
} unit[FD_MAX_UNITS];
 
/* DAG: On Arc we spin on a flag being cleared by fdc1772_comendhandler which
is an assembler routine */
extern void fdc1772_comendhandler(void); /* Actually doens't have these parameters - see fd1772.S */
extern volatile int fdc1772_comendstatus;
extern volatile int fdc1772_fdc_int_done;
 
#define FDC1772BASE ((0x210000>>2)|0x80000000)
 
#define FDC1772_READ(reg) inb(FDC1772BASE+(reg/2))
 
/* DAG: You wouldn't be silly to ask why FDC1772_WRITE is a function rather
than the #def below - well simple - the #def won't compile - and I
don't understand why (__outwc not defined) */
/* NOTE: Reg is 0,2,4,6 as opposed to 0,1,2,3 or 0,4,8,12 to keep compatibility
with the ST version of fd1772.h */
/*#define FDC1772_WRITE(reg,val) outw(val,(reg+FDC1772BASE)); */
void FDC1772_WRITE(int reg, unsigned char val)
{
if (reg == FDC1772REG_CMD) {
DPRINT(("FDC1772_WRITE new command 0x%x @ %d\n", val,jiffies));
if (fdc1772_fdc_int_done) {
DPRINT(("FDC1772_WRITE: Hmm fdc1772_fdc_int_done true - resetting\n"));
fdc1772_fdc_int_done = 0;
};
};
outb(val, (reg / 2) + FDC1772BASE);
};
 
#define MAX_SECTORS 22
 
unsigned char *DMABuffer; /* buffer for writes */
/*static unsigned long PhysDMABuffer; *//* physical address */
/* DAG: On Arc we just go straight for the DMA buffer */
#define PhysDMABuffer DMABuffer
 
#ifdef TRACKBUFFER
unsigned char *TrackBuffer; /* buffer for reads */
#define PhysTrackBuffer TrackBuffer /* physical address */
static int BufferDrive, BufferSide, BufferTrack;
static int read_track; /* non-zero if we are reading whole tracks */
#define SECTOR_BUFFER(sec) (TrackBuffer + ((sec)-1)*512)
#define IS_BUFFERED(drive,side,track) \
(BufferDrive == (drive) && BufferSide == (side) && BufferTrack == (track))
#endif
 
/*
* These are global variables, as that's the easiest way to give
* information to interrupts. They are the data used for the current
* request.
*/
static int SelectedDrive = 0;
static int ReqCmd, ReqBlock;
static int ReqSide, ReqTrack, ReqSector, ReqCnt;
static int HeadSettleFlag = 0;
static unsigned char *ReqData, *ReqBuffer;
static int MotorOn = 0, MotorOffTrys;
 
/* Synchronization of FDC1772 access. */
static volatile int fdc_busy = 0;
static struct wait_queue *fdc_wait = NULL;
 
 
static unsigned int changed_floppies = 0xff, fake_change = 0;
#define CHECK_CHANGE_DELAY HZ/2
 
/* DAG - increased to 30*HZ - not sure if this is the correct thing to do */
#define FD_MOTOR_OFF_DELAY (10*HZ)
#define FD_MOTOR_OFF_MAXTRY (10*20)
 
#define FLOPPY_TIMEOUT (6*HZ)
#define RECALIBRATE_ERRORS 4 /* After this many errors the drive
* will be recalibrated. */
#define MAX_ERRORS 8 /* After this many errors the driver
* will give up. */
 
 
#define START_MOTOR_OFF_TIMER(delay) \
do { \
motor_off_timer.expires = jiffies + (delay); \
add_timer( &motor_off_timer ); \
MotorOffTrys = 0; \
} while(0)
 
#define START_CHECK_CHANGE_TIMER(delay) \
do { \
timer_table[FLOPPY_TIMER].expires = jiffies + (delay); \
timer_active |= (1 << FLOPPY_TIMER); \
} while(0)
 
#define START_TIMEOUT() \
do { \
del_timer( &timeout_timer ); \
timeout_timer.expires = jiffies + FLOPPY_TIMEOUT; \
add_timer( &timeout_timer ); \
} while(0)
 
#define STOP_TIMEOUT() \
do { \
del_timer( &timeout_timer ); \
} while(0)
 
#define ENABLE_IRQ() enable_irq(FIQ_FD1772+64);
 
#define DISABLE_IRQ() disable_irq(FIQ_FD1772+64);
 
static void fd1772_checkint(void);
 
struct tq_struct fd1772_tq =
{ 0,0, (void *)fd1772_checkint, 0 };
/*
* The driver is trying to determine the correct media format
* while Probing is set. fd_rwsec_done() clears it after a
* successful access.
*/
static int Probing = 0;
 
/* This flag is set when a dummy seek is necesary to make the WP
* status bit accessible.
*/
static int NeedSeek = 0;
 
 
/***************************** Prototypes *****************************/
 
static void fd_select_side(int side);
static void fd_select_drive(int drive);
static void fd_deselect(void);
static void fd_motor_off_timer(unsigned long dummy);
static void check_change(void);
static __inline__ void set_head_settle_flag(void);
static __inline__ int get_head_settle_flag(void);
static void floppy_irqconsequencehandler(void);
static void fd_error(void);
static void do_fd_action(int drive);
static void fd_calibrate(void);
static void fd_calibrate_done(int status);
static void fd_seek(void);
static void fd_seek_done(int status);
static void fd_rwsec(void);
#ifdef TRACKBUFFER
static void fd_readtrack_check( unsigned long dummy );
#endif
static void fd_rwsec_done(int status);
static void fd_times_out(unsigned long dummy);
static void finish_fdc(void);
static void finish_fdc_done(int dummy);
static void floppy_off(unsigned int nr);
static __inline__ void copy_buffer(void *from, void *to);
static void setup_req_params(int drive);
static void redo_fd_request(void);
static int fd_ioctl(struct inode *inode, struct file *filp, unsigned int
cmd, unsigned long param);
static void fd_probe(int drive);
static int fd_test_drive_present(int drive);
static void config_types(void);
static int floppy_open(struct inode *inode, struct file *filp);
static void floppy_release(struct inode *inode, struct file *filp);
 
/************************* End of Prototypes **************************/
 
static struct timer_list motor_off_timer =
{NULL, NULL, 0, 0, fd_motor_off_timer};
#ifdef TRACKBUFFER
static struct timer_list readtrack_timer =
{ NULL, NULL, 0, 0, fd_readtrack_check };
#endif
static struct timer_list timeout_timer =
{NULL, NULL, 0, 0, fd_times_out};
 
/* DAG: Haven't got a clue what this is? */
int stdma_islocked(void)
{
return 0;
};
 
/* Select the side to use. */
 
static void fd_select_side(int side)
{
unsigned long flags;
 
save_flags(flags);
cli();
 
oldlatch_aupdate(LATCHA_SIDESEL, side ? 0 : LATCHA_SIDESEL);
restore_flags(flags);
}
 
 
/* Select a drive, update the FDC1772's track register
*/
 
static void fd_select_drive(int drive)
{
unsigned long flags;
 
#ifdef DEBUG
printk("fd_select_drive:%d\n", drive);
#endif
/* Hmm - nowhere do we seem to turn the motor on - I'm going to do it here! */
oldlatch_aupdate(LATCHA_MOTOR | LATCHA_INUSE, 0);
 
if (drive == SelectedDrive)
return;
 
save_flags(flags);
cli();
oldlatch_aupdate(LATCHA_FDSELALL, 0xf - (1 << drive));
restore_flags(flags);
 
/* restore track register to saved value */
FDC1772_WRITE(FDC1772REG_TRACK, unit[drive].track);
udelay(25);
 
SelectedDrive = drive;
}
 
 
/* Deselect both drives. */
 
static void fd_deselect(void)
{
unsigned long flags;
 
DPRINT(("fd_deselect\n"));
 
save_flags(flags);
cli();
oldlatch_aupdate(LATCHA_FDSELALL | LATCHA_MOTOR | LATCHA_INUSE, 0xf | LATCHA_MOTOR | LATCHA_INUSE);
restore_flags(flags);
 
SelectedDrive = -1;
}
 
 
/* This timer function deselects the drives when the FDC1772 switched the
* motor off. The deselection cannot happen earlier because the FDC1772
* counts the index signals, which arrive only if one drive is selected.
*/
 
static void fd_motor_off_timer(unsigned long dummy)
{
unsigned long flags;
unsigned char status;
int delay;
 
del_timer(&motor_off_timer);
 
if (SelectedDrive < 0)
/* no drive selected, needn't deselect anyone */
return;
 
save_flags(flags);
cli();
 
if (fdc_busy) /* was stdma_islocked */
goto retry;
 
status = FDC1772_READ(FDC1772REG_STATUS);
 
if (!(status & 0x80)) {
/* motor already turned off by FDC1772 -> deselect drives */
/* In actual fact its this deselection which turns the motor off on the
Arc, since the motor control is actually on Latch A */
DPRINT(("fdc1772: deselecting in fd_motor_off_timer\n"));
fd_deselect();
MotorOn = 0;
restore_flags(flags);
return;
}
/* not yet off, try again */
 
retry:
restore_flags(flags);
/* Test again later; if tested too often, it seems there is no disk
* in the drive and the FDC1772 will leave the motor on forever (or,
* at least until a disk is inserted). So we'll test only twice
* per second from then on...
*/
delay = (MotorOffTrys < FD_MOTOR_OFF_MAXTRY) ?
(++MotorOffTrys, HZ / 20) : HZ / 2;
START_MOTOR_OFF_TIMER(delay);
}
 
 
/* This function is repeatedly called to detect disk changes (as good
* as possible) and keep track of the current state of the write protection.
*/
 
static void check_change(void)
{
static int drive = 0;
 
unsigned long flags;
int stat;
 
if (fdc_busy)
return; /* Don't start poking about if the fdc is busy */
 
return; /* lets just forget it for the mo DAG */
 
if (++drive > 1 || !unit[drive].connected)
drive = 0;
 
save_flags(flags);
cli();
 
if (!stdma_islocked()) {
stat = !!(FDC1772_READ(FDC1772REG_STATUS) & FDC1772STAT_WPROT);
 
/* The idea here is that if the write protect line has changed then
the disc must have changed */
if (stat != unit[drive].wpstat) {
DPRINT(("wpstat[%d] = %d\n", drive, stat));
unit[drive].wpstat = stat;
set_bit(drive, &changed_floppies);
}
}
restore_flags(flags);
 
START_CHECK_CHANGE_TIMER(CHECK_CHANGE_DELAY);
}
 
 
/* Handling of the Head Settling Flag: This flag should be set after each
* seek operation, because we dont't use seeks with verify.
*/
 
static __inline__ void set_head_settle_flag(void)
{
HeadSettleFlag = FDC1772CMDADD_E;
}
 
static __inline__ int get_head_settle_flag(void)
{
int tmp = HeadSettleFlag;
HeadSettleFlag = 0;
return (tmp);
}
 
 
 
 
/* General Interrupt Handling */
 
static void (*FloppyIRQHandler) (int status) = NULL;
 
static void floppy_irqconsequencehandler(void)
{
unsigned char status;
void (*handler) (int);
 
fdc1772_fdc_int_done = 0;
 
handler = FloppyIRQHandler;
FloppyIRQHandler = NULL;
 
if (handler) {
nop();
status = (unsigned char) fdc1772_comendstatus;
DPRINT(("FDC1772 irq, status = %02x handler = %08lx\n", (unsigned int) status, (unsigned long) handler));
handler(status);
} else {
DPRINT(("FDC1772 irq, no handler status=%02x\n", fdc1772_comendstatus));
}
DPRINT(("FDC1772 irq: end of floppy_irq\n"));
}
 
 
/* Error handling: If some error happened, retry some times, then
* recalibrate, then try again, and fail after MAX_ERRORS.
*/
 
static void fd_error(void)
{
printk("FDC1772: fd_error\n");
/*panic("fd1772: fd_error"); *//* DAG tmp */
if (!CURRENT)
return;
CURRENT->errors++;
if (CURRENT->errors >= MAX_ERRORS) {
printk("fd%d: too many errors.\n", SelectedDrive);
end_request(0);
} else if (CURRENT->errors == RECALIBRATE_ERRORS) {
printk("fd%d: recalibrating\n", SelectedDrive);
if (SelectedDrive != -1)
unit[SelectedDrive].track = -1;
}
redo_fd_request();
}
 
 
 
#define SET_IRQ_HANDLER(proc) do { FloppyIRQHandler = (proc); } while(0)
 
 
/* do_fd_action() is the general procedure for a fd request: All
* required parameter settings (drive select, side select, track
* position) are checked and set if needed. For each of these
* parameters and the actual reading or writing exist two functions:
* one that starts the setting (or skips it if possible) and one
* callback for the "done" interrupt. Each done func calls the next
* set function to propagate the request down to fd_rwsec_done().
*/
 
static void do_fd_action(int drive)
{
DPRINT(("do_fd_action unit[drive].track=%d\n", unit[drive].track));
 
#ifdef TRACKBUFFER
repeat:
 
if (IS_BUFFERED( drive, ReqSide, ReqTrack )) {
if (ReqCmd == READ) {
copy_buffer( SECTOR_BUFFER(ReqSector), ReqData );
if (++ReqCnt < CURRENT->current_nr_sectors) {
/* read next sector */
setup_req_params( drive );
goto repeat;
}
else {
/* all sectors finished */
CURRENT->nr_sectors -= CURRENT->current_nr_sectors;
CURRENT->sector += CURRENT->current_nr_sectors;
end_request( 1 );
redo_fd_request();
return;
}
}
else {
/* cmd == WRITE, pay attention to track buffer
* consistency! */
copy_buffer( ReqData, SECTOR_BUFFER(ReqSector) );
}
}
#endif
 
if (SelectedDrive != drive) {
/*unit[drive].track = -1; DAG */
fd_select_drive(drive);
};
 
 
if (unit[drive].track == -1)
fd_calibrate();
else if (unit[drive].track != ReqTrack << unit[drive].disktype->stretch)
fd_seek();
else
fd_rwsec();
}
 
 
/* Seek to track 0 if the current track is unknown */
 
static void fd_calibrate(void)
{
DPRINT(("fd_calibrate\n"));
if (unit[SelectedDrive].track >= 0) {
fd_calibrate_done(0);
return;
}
DPRINT(("fd_calibrate (after track compare)\n"));
SET_IRQ_HANDLER(fd_calibrate_done);
/* we can't verify, since the speed may be incorrect */
FDC1772_WRITE(FDC1772REG_CMD, FDC1772CMD_RESTORE | unit[SelectedDrive].steprate);
 
NeedSeek = 1;
MotorOn = 1;
START_TIMEOUT();
/* wait for IRQ */
}
 
 
static void fd_calibrate_done(int status)
{
DPRINT(("fd_calibrate_done()\n"));
STOP_TIMEOUT();
 
/* set the correct speed now */
if (status & FDC1772STAT_RECNF) {
printk("fd%d: restore failed\n", SelectedDrive);
fd_error();
} else {
unit[SelectedDrive].track = 0;
fd_seek();
}
}
 
 
/* Seek the drive to the requested track. The drive must have been
* calibrated at some point before this.
*/
 
static void fd_seek(void)
{
unsigned long flags;
DPRINT(("fd_seek() to track %d (unit[SelectedDrive].track=%d)\n", ReqTrack,
unit[SelectedDrive].track));
if (unit[SelectedDrive].track == ReqTrack <<
unit[SelectedDrive].disktype->stretch) {
fd_seek_done(0);
return;
}
FDC1772_WRITE(FDC1772REG_DATA, ReqTrack <<
unit[SelectedDrive].disktype->stretch);
udelay(25);
save_flags(flags);
cliIF();
SET_IRQ_HANDLER(fd_seek_done);
FDC1772_WRITE(FDC1772REG_CMD, FDC1772CMD_SEEK | unit[SelectedDrive].steprate |
/* DAG */
(MotorOn?FDC1772CMDADD_H:0));
 
restore_flags(flags);
MotorOn = 1;
set_head_settle_flag();
START_TIMEOUT();
/* wait for IRQ */
}
 
 
static void fd_seek_done(int status)
{
DPRINT(("fd_seek_done()\n"));
STOP_TIMEOUT();
 
/* set the correct speed */
if (status & FDC1772STAT_RECNF) {
printk("fd%d: seek error (to track %d)\n",
SelectedDrive, ReqTrack);
/* we don't know exactly which track we are on now! */
unit[SelectedDrive].track = -1;
fd_error();
} else {
unit[SelectedDrive].track = ReqTrack <<
unit[SelectedDrive].disktype->stretch;
NeedSeek = 0;
fd_rwsec();
}
}
 
 
/* This does the actual reading/writing after positioning the head
* over the correct track.
*/
 
#ifdef TRACKBUFFER
static int MultReadInProgress = 0;
#endif
 
 
static void fd_rwsec(void)
{
unsigned long paddr, flags;
unsigned int rwflag, old_motoron;
unsigned int track;
 
DPRINT(("fd_rwsec(), Sec=%d, Access=%c\n", ReqSector, ReqCmd == WRITE ? 'w' : 'r'));
if (ReqCmd == WRITE) {
/*cache_push( (unsigned long)ReqData, 512 ); */
paddr = (unsigned long) ReqData;
rwflag = 0x100;
} else {
#ifdef TRACKBUFFER
if (read_track)
paddr = (unsigned long)PhysTrackBuffer;
else
paddr =(unsigned long)PhysDMABuffer;
#else
paddr = (unsigned long)PhysDMABuffer;
#endif
rwflag = 0;
}
 
DPRINT(("fd_rwsec() before sidesel rwflag=%d sec=%d trk=%d\n", rwflag,
ReqSector, FDC1772_READ(FDC1772REG_TRACK)));
fd_select_side(ReqSide);
 
/*DPRINT(("fd_rwsec() before start sector \n")); */
/* Start sector of this operation */
#ifdef TRACKBUFFER
FDC1772_WRITE( FDC1772REG_SECTOR, !read_track ? ReqSector : 1 );
#else
FDC1772_WRITE( FDC1772REG_SECTOR, ReqSector );
#endif
 
/* Cheat for track if stretch != 0 */
if (unit[SelectedDrive].disktype->stretch) {
track = FDC1772_READ(FDC1772REG_TRACK);
FDC1772_WRITE(FDC1772REG_TRACK, track >>
unit[SelectedDrive].disktype->stretch);
}
udelay(25);
 
DPRINT(("fd_rwsec() before setup DMA \n"));
/* Setup DMA - Heavily modified by DAG */
save_flags(flags);
cliIF();
disable_dma(FLOPPY_DMA);
set_dma_mode(FLOPPY_DMA, rwflag ? DMA_MODE_WRITE : DMA_MODE_READ);
set_dma_addr(FLOPPY_DMA, (long) paddr); /* DAG - changed from Atari specific */
#ifdef TRACKBUFFER
set_dma_count(FLOPPY_DMA,(!read_track ? 1 : unit[SelectedDrive].disktype->spt)*512);
#else
set_dma_count(FLOPPY_DMA, 512); /* Block/sector size - going to have to change */
#endif
SET_IRQ_HANDLER(fd_rwsec_done);
/* Turn on dma int */
enable_dma(FLOPPY_DMA);
/* Now give it something to do */
FDC1772_WRITE(FDC1772REG_CMD, (rwflag ? (FDC1772CMD_WRSEC | FDC1772CMDADD_P) :
#ifdef TRACKBUFFER
(FDC1772CMD_RDSEC | (read_track ? FDC1772CMDADD_M : 0) |
/* Hmm - the idea here is to stop the FDC spinning the disc
up when we know that we already still have it spinning */
(MotorOn?FDC1772CMDADD_H:0))
#else
FDC1772CMD_RDSEC
#endif
));
 
restore_flags(flags);
DPRINT(("fd_rwsec() after DMA setup flags=0x%08x\n", flags));
/*sti(); *//* DAG - Hmm */
/* Hmm - should do something DAG */
old_motoron = MotorOn;
MotorOn = 1;
NeedSeek = 1;
 
/* wait for interrupt */
 
#ifdef TRACKBUFFER
if (read_track) {
/* If reading a whole track, wait about one disk rotation and
* then check if all sectors are read. The FDC will even
* search for the first non-existant sector and need 1 sec to
* recognise that it isn't present :-(
*/
del_timer( &readtrack_timer );
readtrack_timer.function = fd_readtrack_check;
readtrack_timer.expires = jiffies + HZ/5 + (old_motoron ? 0 : HZ);
/* 1 rot. + 5 rot.s if motor was off */
DPRINT(("Setting readtrack_timer to %d @ %d\n",readtrack_timer.expires,jiffies));
add_timer( &readtrack_timer );
MultReadInProgress = 1;
}
#endif
 
/*DPRINT(("fd_rwsec() before START_TIMEOUT \n")); */
START_TIMEOUT();
/*DPRINT(("fd_rwsec() after START_TIMEOUT \n")); */
}
 
 
#ifdef TRACKBUFFER
 
static void fd_readtrack_check( unsigned long dummy )
 
{ unsigned long flags, addr;
extern unsigned char *fdc1772_dataaddr;
 
DPRINT(("fd_readtrack_check @ %d\n",jiffies));
 
save_flags(flags);
cliIF();
 
del_timer( &readtrack_timer );
 
if (!MultReadInProgress) {
/* This prevents a race condition that could arise if the
* interrupt is triggered while the calling of this timer
* callback function takes place. The IRQ function then has
* already cleared 'MultReadInProgress' when control flow
* gets here.
*/
restore_flags(flags);
return;
}
 
/* get the current DMA address */
addr=fdc1772_dataaddr; /* DAG - ? */
DPRINT(("fd_readtrack_check: addr=%x PhysTrackBuffer=%p\n",addr,PhysTrackBuffer));
 
if (addr >= (unsigned long)PhysTrackBuffer + unit[SelectedDrive].disktype->spt*512) {
/* already read enough data, force an FDC interrupt to stop
* the read operation
*/
SET_IRQ_HANDLER( NULL );
restore_flags(flags);
DPRINT(("fd_readtrack_check(): done\n"));
FDC1772_WRITE( FDC1772REG_CMD, FDC1772CMD_FORCI );
udelay(25);
 
/* No error until now -- the FDC would have interrupted
* otherwise!
*/
fd_rwsec_done( 0 );
}
else {
/* not yet finished, wait another tenth rotation */
restore_flags(flags);
DPRINT(("fd_readtrack_check(): not yet finished\n"));
readtrack_timer.expires = jiffies + HZ/5/10;
add_timer( &readtrack_timer );
}
}
 
#endif
 
static void fd_rwsec_done(int status)
{
unsigned int track;
 
DPRINT(("fd_rwsec_done() status=%d @ %d\n", status,jiffies));
 
#ifdef TRACKBUFFER
if (read_track && !MultReadInProgress) return;
MultReadInProgress = 0;
 
STOP_TIMEOUT();
 
if (read_track)
del_timer( &readtrack_timer );
#endif
 
 
/* Correct the track if stretch != 0 */
if (unit[SelectedDrive].disktype->stretch) {
track = FDC1772_READ(FDC1772REG_TRACK);
FDC1772_WRITE(FDC1772REG_TRACK, track <<
unit[SelectedDrive].disktype->stretch);
}
if (ReqCmd == WRITE && (status & FDC1772STAT_WPROT)) {
printk("fd%d: is write protected\n", SelectedDrive);
goto err_end;
}
if ((status & FDC1772STAT_RECNF)
#ifdef TRACKBUFFER
/* RECNF is no error after a multiple read when the FDC
* searched for a non-existant sector!
*/
&& !(read_track &&
FDC1772_READ(FDC1772REG_SECTOR) > unit[SelectedDrive].disktype->spt)
#endif
) {
if (Probing) {
if (unit[SelectedDrive].disktype > disk_type) {
/* try another disk type */
unit[SelectedDrive].disktype--;
floppy_sizes[SelectedDrive]
= unit[SelectedDrive].disktype->blocks >> 1;
} else
Probing = 0;
} else {
/* record not found, but not probing. Maybe stretch wrong ? Restart probing */
if (unit[SelectedDrive].autoprobe) {
unit[SelectedDrive].disktype = disk_type + NUM_DISK_TYPES - 1;
floppy_sizes[SelectedDrive]
= unit[SelectedDrive].disktype->blocks >> 1;
Probing = 1;
}
}
if (Probing) {
setup_req_params(SelectedDrive);
#ifdef TRACKBUFFER
BufferDrive = -1;
#endif
do_fd_action(SelectedDrive);
return;
}
printk("fd%d: sector %d not found (side %d, track %d)\n",
SelectedDrive, FDC1772_READ(FDC1772REG_SECTOR), ReqSide, ReqTrack);
goto err_end;
}
if (status & FDC1772STAT_CRC) {
printk("fd%d: CRC error (side %d, track %d, sector %d)\n",
SelectedDrive, ReqSide, ReqTrack, FDC1772_READ(FDC1772REG_SECTOR));
goto err_end;
}
if (status & FDC1772STAT_LOST) {
printk("fd%d: lost data (side %d, track %d, sector %d)\n",
SelectedDrive, ReqSide, ReqTrack, FDC1772_READ(FDC1772REG_SECTOR));
goto err_end;
}
Probing = 0;
 
if (ReqCmd == READ) {
#ifdef TRACKBUFFER
if (!read_track)
{
/*cache_clear (PhysDMABuffer, 512);*/
copy_buffer (DMABuffer, ReqData);
}
else
{
/*cache_clear (PhysTrackBuffer, MAX_SECTORS * 512);*/
BufferDrive = SelectedDrive;
BufferSide = ReqSide;
BufferTrack = ReqTrack;
copy_buffer (SECTOR_BUFFER (ReqSector), ReqData);
}
#else
/*cache_clear( PhysDMABuffer, 512 ); */
copy_buffer(DMABuffer, ReqData);
#endif
}
if (++ReqCnt < CURRENT->current_nr_sectors) {
/* read next sector */
setup_req_params(SelectedDrive);
do_fd_action(SelectedDrive);
} else {
/* all sectors finished */
CURRENT->nr_sectors -= CURRENT->current_nr_sectors;
CURRENT->sector += CURRENT->current_nr_sectors;
end_request(1);
redo_fd_request();
}
return;
 
err_end:
#ifdef TRACKBUFFER
BufferDrive = -1;
#endif
 
fd_error();
}
 
 
static void fd_times_out(unsigned long dummy)
{
SET_IRQ_HANDLER(NULL);
/* If the timeout occured while the readtrack_check timer was
* active, we need to cancel it, else bad things will happen */
del_timer( &readtrack_timer );
FDC1772_WRITE(FDC1772REG_CMD, FDC1772CMD_FORCI);
udelay(25);
 
printk("floppy timeout\n");
STOP_TIMEOUT(); /* hmm - should we do this ? */
fd_error();
}
 
 
/* The (noop) seek operation here is needed to make the WP bit in the
* FDC1772 status register accessible for check_change. If the last disk
* operation would have been a RDSEC, this bit would always read as 0
* no matter what :-( To save time, the seek goes to the track we're
* already on.
*/
 
static void finish_fdc(void)
{
/* DAG - just try without this dummy seek! */
finish_fdc_done(0);
return;
 
if (!NeedSeek) {
finish_fdc_done(0);
} else {
DPRINT(("finish_fdc: dummy seek started\n"));
FDC1772_WRITE(FDC1772REG_DATA, unit[SelectedDrive].track);
SET_IRQ_HANDLER(finish_fdc_done);
FDC1772_WRITE(FDC1772REG_CMD, FDC1772CMD_SEEK);
MotorOn = 1;
START_TIMEOUT();
/* we must wait for the IRQ here, because the ST-DMA is
* released immediatly afterwards and the interrupt may be
* delivered to the wrong driver.
*/
}
}
 
 
static void finish_fdc_done(int dummy)
{
unsigned long flags;
 
DPRINT(("finish_fdc_done entered\n"));
STOP_TIMEOUT();
NeedSeek = 0;
 
if ((timer_active & (1 << FLOPPY_TIMER)) &&
timer_table[FLOPPY_TIMER].expires < jiffies + 5)
/* If the check for a disk change is done too early after this
* last seek command, the WP bit still reads wrong :-((
*/
timer_table[FLOPPY_TIMER].expires = jiffies + 5;
else {
/* START_CHECK_CHANGE_TIMER( CHECK_CHANGE_DELAY ); */
};
del_timer(&motor_off_timer);
START_MOTOR_OFF_TIMER(FD_MOTOR_OFF_DELAY);
 
save_flags(flags);
cli();
/* stdma_release(); - not sure if I should do something DAG */
fdc_busy = 0;
wake_up(&fdc_wait);
restore_flags(flags);
 
DPRINT(("finish_fdc() finished\n"));
}
 
 
/* Prevent "aliased" accesses. */
static fd_ref[4] =
{0, 0, 0, 0};
static fd_device[4] =
{0, 0, 0, 0};
 
/*
* Current device number. Taken either from the block header or from the
* format request descriptor.
*/
#define CURRENT_DEVICE (CURRENT->rq_dev)
 
/* Current error count. */
#define CURRENT_ERRORS (CURRENT->errors)
 
 
/* dummy for blk.h */
static void floppy_off(unsigned int nr)
{
}
 
 
/* On the old arcs write protect depends on the particular model
of machine. On the A310, R140, and A440 there is a disc changed
detect, however on the A4x0/1 range there is not. There
is nothing to tell you which machine your on.
At the moment I'm just marking changed always. I've
left the Atari's 'change on write protect change' code in this
part (but nothing sets it).
RiscOS apparently checks the disc serial number etc. to detect changes
- but if it sees a disc change line go high (?) it flips to using
it. Well maybe I'll add that in the future (!?)
*/
static int check_floppy_change(dev_t dev)
{
unsigned int drive = (dev & 0x03);
 
if (MAJOR(dev) != MAJOR_NR) {
printk("floppy_changed: not a floppy\n");
return 0;
}
if (test_bit(drive, &fake_change)) {
/* simulated change (e.g. after formatting) */
return 1;
}
if (test_bit(drive, &changed_floppies)) {
/* surely changed (the WP signal changed at least once) */
return 1;
}
if (unit[drive].wpstat) {
/* WP is on -> could be changed: to be sure, buffers should be
* invalidated...
*/
return 1;
}
return 1; /* DAG - was 0 */
}
 
static int floppy_revalidate(dev_t dev)
{
int drive = dev & 3;
 
if (test_bit(drive, &changed_floppies) || test_bit(drive, &fake_change)
|| unit[drive].disktype == 0) {
#ifdef TRACKBUFFER
BufferDrive = -1;
#endif
clear_bit(drive, &fake_change);
clear_bit(drive, &changed_floppies);
unit[drive].disktype = 0;
}
return 0;
}
 
static __inline__ void copy_buffer(void *from, void *to)
{
ulong *p1 = (ulong *) from, *p2 = (ulong *) to;
int cnt;
 
for (cnt = 512 / 4; cnt; cnt--)
*p2++ = *p1++;
}
 
 
/* This sets up the global variables describing the current request. */
 
static void setup_req_params(int drive)
{
int block = ReqBlock + ReqCnt;
 
ReqTrack = block / unit[drive].disktype->spt;
ReqSector = block - ReqTrack * unit[drive].disktype->spt + 1;
ReqSide = ReqTrack & 1;
ReqTrack >>= 1;
ReqData = ReqBuffer + 512 * ReqCnt;
 
#ifdef TRACKBUFFER
read_track = (ReqCmd == READ && CURRENT_ERRORS == 0);
#endif
 
DPRINT(("Request params: Si=%d Tr=%d Se=%d Data=%08lx\n", ReqSide,
ReqTrack, ReqSector, (unsigned long) ReqData));
}
 
 
static void redo_fd_request(void)
{
int device, drive, type;
struct archy_floppy_struct *floppy;
 
DPRINT(("redo_fd_request: CURRENT=%08lx CURRENT->rq_dev=%04x CURRENT->sector=%ld\n",
(unsigned long) CURRENT, CURRENT ? CURRENT->rq_dev : 0,
CURRENT ? CURRENT->sector : 0));
 
if (CURRENT && CURRENT->rq_status == RQ_INACTIVE)
goto the_end;
 
repeat:
 
if (!CURRENT)
goto the_end;
 
if (MAJOR(CURRENT->rq_dev) != MAJOR_NR)
panic(DEVICE_NAME ": request list destroyed");
 
if (CURRENT->bh) {
if (!buffer_locked(CURRENT->bh))
panic(DEVICE_NAME ": block not locked");
}
device = MINOR(CURRENT_DEVICE);
drive = device & 3;
type = device >> 2;
floppy = &unit[drive];
 
if (!floppy->connected) {
/* drive not connected */
printk("Unknown Device: fd%d\n", drive);
end_request(0);
goto repeat;
}
if (type == 0) {
if (!floppy->disktype) {
Probing = 1;
floppy->disktype = disk_type + NUM_DISK_TYPES - 1;
floppy_sizes[drive] = floppy->disktype->blocks >> 1;
floppy->autoprobe = 1;
}
} else {
/* user supplied disk type */
--type;
if (type >= NUM_DISK_TYPES) {
printk("fd%d: invalid disk format", drive);
end_request(0);
goto repeat;
}
floppy->disktype = &disk_type[type];
floppy_sizes[drive] = disk_type[type].blocks >> 1;
floppy->autoprobe = 0;
}
 
if (CURRENT->sector + 1 > floppy->disktype->blocks) {
end_request(0);
goto repeat;
}
/* stop deselect timer */
del_timer(&motor_off_timer);
 
ReqCnt = 0;
ReqCmd = CURRENT->cmd;
ReqBlock = CURRENT->sector;
ReqBuffer = CURRENT->buffer;
setup_req_params(drive);
do_fd_action(drive);
 
return;
 
the_end:
finish_fdc();
}
 
static void fd1772_checkint(void)
{
extern int fdc1772_bytestogo;
 
/*printk("fd1772_checkint %d\n",fdc1772_fdc_int_done);*/
if (fdc1772_fdc_int_done)
floppy_irqconsequencehandler();
if ((MultReadInProgress) && (fdc1772_bytestogo==0)) fd_readtrack_check(0);
if (fdc_busy) {
queue_task(&fd1772_tq,&tq_immediate);
mark_bh(IMMEDIATE_BH);
};
};
 
void do_fd_request(void)
{
unsigned long flags;
 
DPRINT(("do_fd_request for pid %d\n", current->pid));
if (fdc_busy) return;
save_flags(flags);
cli();
while (fdc_busy)
sleep_on(&fdc_wait);
fdc_busy = 1;
ENABLE_IRQ();
restore_flags(flags);
 
fdc1772_fdc_int_done = 0;
 
redo_fd_request();
 
queue_task(&fd1772_tq,&tq_immediate);
mark_bh(IMMEDIATE_BH);
}
 
 
static int invalidate_drive(int rdev)
{
/* invalidate the buffer track to force a reread */
#ifdef TRACKBUFFER
BufferDrive = -1;
#endif
 
set_bit(rdev & 3, &fake_change);
check_disk_change(rdev);
return 0;
}
 
static int fd_ioctl(struct inode *inode, struct file *filp,
unsigned int cmd, unsigned long param)
{
#define IOCTL_MODE_BIT 8
#define OPEN_WRITE_BIT 16
#define IOCTL_ALLOWED (filp && (filp->f_mode & IOCTL_MODE_BIT))
 
int drive, device;
 
device = inode->i_rdev;
switch (cmd) {
RO_IOCTLS(inode->i_rdev, param);
}
drive = MINOR(device);
if (!IOCTL_ALLOWED)
return -EPERM;
switch (cmd) {
case FDFMTBEG:
return 0;
/* case FDC1772LRPRM: ??? DAG what does this do??
unit[drive].disktype = NULL;
floppy_sizes[drive] = MAX_DISK_SIZE;
return invalidate_drive (device); */
case FDFMTEND:
case FDFLUSH:
return invalidate_drive(drive);
}
if (!suser())
return -EPERM;
if (drive < 0 || drive > 3)
return -EINVAL;
switch (cmd) {
default:
return -EINVAL;
}
return 0;
}
 
 
/* Initialize the 'unit' variable for drive 'drive' */
 
static void fd_probe(int drive)
{
unit[drive].connected = 0;
unit[drive].disktype = NULL;
 
if (!fd_test_drive_present(drive))
return;
 
unit[drive].connected = 1;
unit[drive].track = -1; /* If we put the auto detect back in this can go to 0 */
unit[drive].steprate = FDC1772STEP_6;
MotorOn = 1; /* from probe restore operation! */
}
 
 
/* This function tests the physical presence of a floppy drive (not
* whether a disk is inserted). This is done by issuing a restore
* command, waiting max. 2 seconds (that should be enough to move the
* head across the whole disk) and looking at the state of the "TR00"
* signal. This should now be raised if there is a drive connected
* (and there is no hardware failure :-) Otherwise, the drive is
* declared absent.
*/
 
static int fd_test_drive_present(int drive)
{
unsigned long timeout;
unsigned char status;
int ok;
 
printk("fd_test_drive_present %d\n", drive);
if (drive > 1)
return (0);
return (1); /* Simple hack for the moment - the autodetect doesn't seem to work on arc */
fd_select_drive(drive);
 
/* disable interrupt temporarily */
DISABLE_IRQ();
FDC1772_WRITE(FDC1772REG_TRACK, 0x00); /* was ff00 why? */
FDC1772_WRITE(FDC1772REG_CMD, FDC1772CMD_RESTORE | FDC1772CMDADD_H | FDC1772STEP_6);
 
/*printk("fd_test_drive_present: Going into timeout loop\n"); */
for (ok = 0, timeout = jiffies + 2 * HZ + HZ / 2; jiffies < timeout;) {
/* What does this piece of atariism do? - query for an interrupt? */
/* if (!(mfp.par_dt_reg & 0x20))
break; */
/* Well this is my nearest guess - quit when we get an FDC interrupt */
if (IOC_FIQSTAT & 2)
break;
}
 
/*printk("fd_test_drive_present: Coming out of timeout loop\n"); */
status = FDC1772_READ(FDC1772REG_STATUS);
ok = (status & FDC1772STAT_TR00) != 0;
 
/*printk("fd_test_drive_present: ok=%d\n",ok); */
/* force interrupt to abort restore operation (FDC1772 would try
* about 50 seconds!) */
FDC1772_WRITE(FDC1772REG_CMD, FDC1772CMD_FORCI);
udelay(500);
status = FDC1772_READ(FDC1772REG_STATUS);
udelay(20);
/*printk("fd_test_drive_present: just before OK code %d\n",ok); */
 
if (ok) {
/* dummy seek command to make WP bit accessible */
FDC1772_WRITE(FDC1772REG_DATA, 0);
FDC1772_WRITE(FDC1772REG_CMD, FDC1772CMD_SEEK);
printk("fd_test_drive_present: just before wait for int\n");
/* DAG: Guess means wait for interrupt */
while (!(IOC_FIQSTAT & 2));
printk("fd_test_drive_present: just after wait for int\n");
status = FDC1772_READ(FDC1772REG_STATUS);
}
printk("fd_test_drive_present: just before ENABLE_IRQ\n");
ENABLE_IRQ();
printk("fd_test_drive_present: about to return\n");
return (ok);
}
 
 
/* Look how many and which kind of drives are connected. If there are
* floppies, additionally start the disk-change and motor-off timers.
*/
 
static void config_types(void)
{
int drive, cnt = 0;
 
printk("Probing floppy drive(s):\n");
for (drive = 0; drive < FD_MAX_UNITS; drive++) {
fd_probe(drive);
if (unit[drive].connected) {
printk("fd%d\n", drive);
++cnt;
}
}
 
if (FDC1772_READ(FDC1772REG_STATUS) & FDC1772STAT_BUSY) {
/* If FDC1772 is still busy from probing, give it another FORCI
* command to abort the operation. If this isn't done, the FDC1772
* will interrupt later and its IRQ line stays low, because
* the status register isn't read. And this will block any
* interrupts on this IRQ line :-(
*/
FDC1772_WRITE(FDC1772REG_CMD, FDC1772CMD_FORCI);
udelay(500);
FDC1772_READ(FDC1772REG_STATUS);
udelay(20);
}
if (cnt > 0) {
START_MOTOR_OFF_TIMER(FD_MOTOR_OFF_DELAY);
if (cnt == 1)
fd_select_drive(0);
/*START_CHECK_CHANGE_TIMER( CHECK_CHANGE_DELAY ); */
}
}
 
/*
* floppy_open check for aliasing (/dev/fd0 can be the same as
* /dev/PS0 etc), and disallows simultaneous access to the same
* drive with different device numbers.
*/
 
static int floppy_open(struct inode *inode, struct file *filp)
{
int drive;
int old_dev;
 
if (!filp) {
DPRINT(("Weird, open called with filp=0\n"));
return -EIO;
}
drive = MINOR(inode->i_rdev) & 3;
if ((MINOR(inode->i_rdev) >> 2) > NUM_DISK_TYPES)
return -ENXIO;
 
old_dev = fd_device[drive];
 
if (fd_ref[drive])
if (old_dev != inode->i_rdev)
return -EBUSY;
 
if (fd_ref[drive] == -1 || (fd_ref[drive] && filp->f_flags & O_EXCL))
return -EBUSY;
 
if (filp->f_flags & O_EXCL)
fd_ref[drive] = -1;
else
fd_ref[drive]++;
 
fd_device[drive] = inode->i_rdev;
 
if (old_dev && old_dev != inode->i_rdev)
invalidate_buffers(old_dev);
 
/* Allow ioctls if we have write-permissions even if read-only open */
if (filp->f_mode & 2 || permission(inode, 2) == 0)
filp->f_mode |= IOCTL_MODE_BIT;
if (filp->f_mode & 2)
filp->f_mode |= OPEN_WRITE_BIT;
 
if (filp->f_flags & O_NDELAY)
return 0;
 
if (filp->f_mode & 3) {
check_disk_change(inode->i_rdev);
if (filp->f_mode & 2) {
if (unit[drive].wpstat) {
floppy_release(inode, filp);
return -EROFS;
}
}
}
return 0;
}
 
 
static void floppy_release(struct inode *inode, struct file *filp)
{
int drive;
 
drive = inode->i_rdev & 3;
 
if (!filp || (filp->f_mode & (2 | OPEN_WRITE_BIT)))
/* if the file is mounted OR (writable now AND writable at open
time) Linus: Does this cover all cases? */
block_fsync(inode, filp);
 
if (fd_ref[drive] < 0)
fd_ref[drive] = 0;
else if (!fd_ref[drive]--) {
printk("floppy_release with fd_ref == 0");
fd_ref[drive] = 0;
}
}
 
static struct file_operations floppy_fops =
{
NULL, /* lseek - default */
block_read, /* read - general block-dev read */
block_write, /* write - general block-dev write */
NULL, /* readdir - bad */
NULL, /* select */
fd_ioctl, /* ioctl */
NULL, /* mmap */
floppy_open, /* open */
floppy_release, /* release */
block_fsync, /* fsync */
NULL, /* fasync */
check_floppy_change, /* media_change */
floppy_revalidate, /* revalidate */
};
 
 
int floppy_init(void)
{
int i;
 
if (register_blkdev(MAJOR_NR, "fd", &floppy_fops)) {
printk("Unable to get major %d for floppy\n", MAJOR_NR);
return 1;
}
 
if (request_dma(FLOPPY_DMA, "fd1772")) {
printk("Unable to grab DMA%d for the floppy (1772) driver\n", FLOPPY_DMA);
return 1;
};
 
if (request_dma(FIQ_FD1772, "fd1772 end")) {
printk("Unable to grab DMA%d for the floppy (1772) driver\n", FIQ_FD1772);
free_dma(FLOPPY_DMA);
return 1;
};
enable_dma(FIQ_FD1772); /* This inserts a call to our command end routine */
 
/* initialize variables */
SelectedDrive = -1;
#ifdef TRACKBUFFER
BufferDrive = -1;
#endif
 
/* initialize check_change timer */
timer_table[FLOPPY_TIMER].fn = check_change;
timer_active &= ~(1 << FLOPPY_TIMER);
 
 
#ifdef TRACKBUFFER
DMABuffer = (char *)kmalloc((MAX_SECTORS+1)*512,GFP_KERNEL); /* Atari uses 512 - I want to eventually cope with 1K sectors */
TrackBuffer = DMABuffer + 512;
#else
/* Allocate memory for the DMAbuffer - on the Atari this takes it
out of some special memory... */
DMABuffer = (char *) kmalloc(2048); /* Copes with pretty large sectors */
#endif
#ifdef TRACKBUFFER
BufferDrive = BufferSide = BufferTrack = -1;
#endif
 
for (i = 0; i < FD_MAX_UNITS; i++) {
unit[i].track = -1;
}
 
for (i = 0; i < 256; i++)
if ((i >> 2) > 0 && (i >> 2) <= NUM_DISK_TYPES)
floppy_sizes[i] = disk_type[(i >> 2) - 1].blocks >> 1;
else
floppy_sizes[i] = MAX_DISK_SIZE;
 
blk_size[MAJOR_NR] = floppy_sizes;
blksize_size[MAJOR_NR] = floppy_blocksizes;
blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
 
config_types();
 
return 0;
}
 
/* Just a dummy at the moment */
void floppy_setup(char *str, int *ints)
{
}
 
void floppy_eject(void) {
}
/fd1772dma.S
0,0 → 1,100
#include <asm/hardware.h>
 
@ Code for DMA with the 1772 fdc
.text
 
 
.global _fdc1772_dataaddr
_fdc1772_fiqdata:
@ Number of bytes left to DMA
.global _fdc1772_bytestogo
_fdc1772_bytestogo:
.word 0
@ Place to put/get data from in DMA
.global _fdc1772_dataaddr
_fdc1772_dataaddr:
.word 0
.global _fdc1772_fdc_int_done
_fdc1772_fdc_int_done:
.word 0
.global _fdc1772_comendstatus
_fdc1772_comendstatus:
.word 0
 
@ We hang this off DMA channel 1
.global _fdc1772_comendhandler
_fdc1772_comendhandler:
mov r8,#IOC_BASE
ldrb r9,[r8,#0x34] @ IOC FIQ status
tst r9,#2
subeqs pc,r14,#4 @ should I leave a space here
orr r9,r8,#0x10000 @ FDC base
adr r8,_fdc1772_fdc_int_done
ldrb r10,[r9,#0] @ FDC status
mov r9,#1 @ Got a FIQ flag
stmia r8,{r9,r10}
subs pc,r14,#4
 
 
.global _fdc1772_dma_read
_fdc1772_dma_read:
mov r8,#IOC_BASE
ldrb r9,[r8,#0x34] @ IOC FIQ status
tst r9,#1
beq _fdc1772_dma_read_notours
orr r8,r8,#0x10000 @ FDC base
ldrb r10,[r8,#0xc] @ Read from FDC data reg (also clears interrupt)
ldmia r11,{r8,r9}
subs r8,r8,#1 @ One less byte to go
@ If there was somewhere for this data to go then store it and update pointers
strplb r10,[r9],#1 @ Store the data and increment the pointer
stmplia r11,{r8,r9} @ Update count/pointers
@ Handle any other interrupts if there are any
_fdc1772_dma_read_notours:
@ Cant branch because this code has been copied down to the FIQ vector
ldr pc,[pc,#-4]
.word _fdc1772_comendhandler
.global _fdc1772_dma_read_end
_fdc1772_dma_read_end:
 
.global _fdc1772_dma_write
_fdc1772_dma_write:
mov r8,#IOC_BASE
ldrb r9,[r8,#0x34] @ IOC FIQ status
tst r9,#1
beq _fdc1772_dma_write_notours
orr r8,r8,#0x10000 @ FDC base
ldmia r11,{r9,r10}
subs r9,r9,#1 @ One less byte to go
@ If there really is some data then get it, store it and update count
ldrplb r12,[r10],#1
strplb r12,[r8,#0xc] @ write it to FDC data reg
stmplia r11,{r9,r10} @ Update count and pointer - should clear interrupt
@ Handle any other interrupts
_fdc1772_dma_write_notours:
@ Cant branch because this code has been copied down to the FIQ vector
ldr pc,[pc,#-4]
.word _fdc1772_comendhandler
 
.global _fdc1772_dma_write_end
_fdc1772_dma_write_end:
 
@ Setup the FIQ R11 to point to the data and store the count, address
@ for this dma
@ R0=count
@ R1=address
.global _fdc1772_setupdma
_fdc1772_setupdma:
@ The big job is flipping in and out of FIQ mode
adr r2,_fdc1772_fiqdata @ This is what we really came here for
stmia r2,{r0,r1}
mov r3, pc
teqp pc,#0x0c000001 @ Disable FIQs, IRQs and switch to FIQ mode
mov r0,r0 @ NOP
mov r11,r2
teqp r3,#0 @ Normal mode
mov r0,r0 @ NOP
mov pc,r14
 
/blk.h
0,0 → 1,438
#ifndef _BLK_H
#define _BLK_H
 
#include <linux/blkdev.h>
#include <linux/locks.h>
#include <linux/config.h>
 
/*
* NR_REQUEST is the number of entries in the request-queue.
* NOTE that writes may use only the low 2/3 of these: reads
* take precedence.
*/
#define NR_REQUEST 64
 
/*
* This is used in the elevator algorithm. We don't prioritise reads
* over writes any more --- although reads are more time-critical than
* writes, by treating them equally we increase filesystem throughput.
* This turns out to give better overall performance. -- sct
*/
#define IN_ORDER(s1,s2) \
((s1)->rq_dev < (s2)->rq_dev || (((s1)->rq_dev == (s2)->rq_dev && \
(s1)->sector < (s2)->sector)))
 
/*
* These will have to be changed to be aware of different buffer
* sizes etc.. It actually needs a major cleanup.
*/
#if defined(IDE_DRIVER) || defined(MD_DRIVER)
#define SECTOR_MASK ((BLOCK_SIZE >> 9) - 1)
#else
#define SECTOR_MASK (blksize_size[MAJOR_NR] && \
blksize_size[MAJOR_NR][MINOR(CURRENT->rq_dev)] ? \
((blksize_size[MAJOR_NR][MINOR(CURRENT->rq_dev)] >> 9) - 1) : \
((BLOCK_SIZE >> 9) - 1))
#endif /* IDE_DRIVER */
 
#define SUBSECTOR(block) (CURRENT->current_nr_sectors > 0)
 
#ifdef CONFIG_CDU31A
extern int cdu31a_init(void);
#endif CONFIG_CDU31A
#ifdef CONFIG_MCD
extern int mcd_init(void);
#endif CONFIG_MCD
#ifdef CONFIG_MCDX
extern int mcdx_init(void);
#endif CONFIG_MCDX
#ifdef CONFIG_SBPCD
extern int sbpcd_init(void);
#endif CONFIG_SBPCD
#ifdef CONFIG_AZTCD
extern int aztcd_init(void);
#endif CONFIG_AZTCD
#ifdef CONFIG_CDU535
extern int sony535_init(void);
#endif CONFIG_CDU535
#ifdef CONFIG_GSCD
extern int gscd_init(void);
#endif CONFIG_GSCD
#ifdef CONFIG_CM206
extern int cm206_init(void);
#endif CONFIG_CM206
#ifdef CONFIG_OPTCD
extern int optcd_init(void);
#endif CONFIG_OPTCD
#ifdef CONFIG_SJCD
extern int sjcd_init(void);
#endif CONFIG_SJCD
#ifdef CONFIG_BLK_DEV_HD
extern int hd_init(void);
#endif
#ifdef CONFIG_BLK_DEV_IDE
extern int ide_init(void);
#endif
#ifdef CONFIG_BLK_DEV_XD
extern int xd_init(void);
#endif
#ifdef CONFIG_BLK_DEV_LOOP
extern int loop_init(void);
#endif
#ifdef CONFIG_BLK_DEV_MD
extern int md_init(void);
#endif CONFIG_BLK_DEV_MD
 
extern void set_device_ro(kdev_t dev,int flag);
void add_blkdev_randomness(int major);
 
extern int floppy_init(void);
extern void rd_load(void);
extern int rd_init(void);
extern int rd_doload; /* 1 = load ramdisk, 0 = don't load */
extern int rd_prompt; /* 1 = prompt for ramdisk, 0 = don't prompt */
extern int rd_image_start; /* starting block # of image */
 
#ifdef CONFIG_BLK_DEV_INITRD
 
#define INITRD_MINOR 250 /* shouldn't collide with /dev/ram* too soon ... */
 
extern unsigned long initrd_start,initrd_end;
extern int mount_initrd; /* zero if initrd should not be mounted */
void initrd_init(void);
 
#endif
 
#define RO_IOCTLS(dev,where) \
case BLKROSET: { int __err; if (!suser()) return -EACCES; \
__err = verify_area(VERIFY_READ, (void *) (where), sizeof(long)); \
if (!__err) set_device_ro((dev),get_fs_long((long *) (where))); return __err; } \
case BLKROGET: { int __err = verify_area(VERIFY_WRITE, (void *) (where), sizeof(long)); \
if (!__err) put_fs_long(0!=is_read_only(dev),(long *) (where)); return __err; }
#if defined(MAJOR_NR) || defined(IDE_DRIVER)
 
/*
* Add entries as needed.
*/
 
#ifdef IDE_DRIVER
 
#define DEVICE_NR(device) (MINOR(device) >> PARTN_BITS)
#define DEVICE_ON(device) /* nothing */
#define DEVICE_OFF(device) /* nothing */
 
#elif (MAJOR_NR == RAMDISK_MAJOR)
 
/* ram disk */
#define DEVICE_NAME "ramdisk"
#define DEVICE_REQUEST rd_request
#define DEVICE_NR(device) (MINOR(device))
#define DEVICE_ON(device)
#define DEVICE_OFF(device)
#define DEVICE_NO_RANDOM
 
#elif (MAJOR_NR == FLOPPY_MAJOR)
 
static void floppy_off(unsigned int nr);
 
#define DEVICE_NAME "floppy"
#define DEVICE_INTR do_floppy
#define DEVICE_REQUEST do_fd_request
#define DEVICE_NR(device) ( (MINOR(device) & 3) | ((MINOR(device) & 0x80 ) >> 5 ))
#define DEVICE_ON(device)
#define DEVICE_OFF(device) floppy_off(DEVICE_NR(device))
 
#elif (MAJOR_NR == HD_MAJOR)
 
/* harddisk: timeout is 6 seconds.. */
#define DEVICE_NAME "harddisk"
#define DEVICE_INTR do_hd
#define DEVICE_TIMEOUT HD_TIMER
#define TIMEOUT_VALUE (6*HZ)
#define DEVICE_REQUEST do_hd_request
#define DEVICE_NR(device) (MINOR(device)>>6)
#define DEVICE_ON(device)
#define DEVICE_OFF(device)
 
#elif (MAJOR_NR == SCSI_DISK_MAJOR)
 
#define DEVICE_NAME "scsidisk"
#define DEVICE_INTR do_sd
#define TIMEOUT_VALUE (2*HZ)
#define DEVICE_REQUEST do_sd_request
#define DEVICE_NR(device) (MINOR(device) >> 4)
#define DEVICE_ON(device)
#define DEVICE_OFF(device)
 
/* Kludge to use the same number for both char and block major numbers */
#elif (MAJOR_NR == MD_MAJOR) && defined(MD_DRIVER)
 
#define DEVICE_NAME "Multiple devices driver"
#define DEVICE_REQUEST do_md_request
#define DEVICE_NR(device) (MINOR(device))
#define DEVICE_ON(device)
#define DEVICE_OFF(device)
 
#elif (MAJOR_NR == SCSI_TAPE_MAJOR)
 
#define DEVICE_NAME "scsitape"
#define DEVICE_INTR do_st
#define DEVICE_NR(device) (MINOR(device) & 0x7f)
#define DEVICE_ON(device)
#define DEVICE_OFF(device)
 
#elif (MAJOR_NR == SCSI_CDROM_MAJOR)
 
#define DEVICE_NAME "CD-ROM"
#define DEVICE_INTR do_sr
#define DEVICE_REQUEST do_sr_request
#define DEVICE_NR(device) (MINOR(device))
#define DEVICE_ON(device)
#define DEVICE_OFF(device)
 
#elif (MAJOR_NR == XT_DISK_MAJOR)
 
#define DEVICE_NAME "xt disk"
#define DEVICE_REQUEST do_xd_request
#define DEVICE_NR(device) (MINOR(device) >> 6)
#define DEVICE_ON(device)
#define DEVICE_OFF(device)
 
#elif defined(MFM_DISK_MAJOR) && (MAJOR_NR == MFM_DISK_MAJOR)
 
#define DEVICE_NAME "mfm disk"
#define DEVICE_INTR do_mfm
#define DEVICE_REQUEST do_mfm_request
#define DEVICE_NR(device) (MINOR(device) >> 6)
#define DEVICE_ON(device)
#define DEVICE_OFF(device)
 
#elif (MAJOR_NR == CDU31A_CDROM_MAJOR)
 
#define DEVICE_NAME "CDU31A"
#define DEVICE_REQUEST do_cdu31a_request
#define DEVICE_NR(device) (MINOR(device))
#define DEVICE_ON(device)
#define DEVICE_OFF(device)
 
#elif (MAJOR_NR == MITSUMI_CDROM_MAJOR)
 
#define DEVICE_NAME "Mitsumi CD-ROM"
/* #define DEVICE_INTR do_mcd */
#define DEVICE_REQUEST do_mcd_request
#define DEVICE_NR(device) (MINOR(device))
#define DEVICE_ON(device)
#define DEVICE_OFF(device)
 
#elif (MAJOR_NR == MITSUMI_X_CDROM_MAJOR)
 
#define DEVICE_NAME "Mitsumi CD-ROM"
/* #define DEVICE_INTR do_mcdx */
#define DEVICE_REQUEST do_mcdx_request
#define DEVICE_NR(device) (MINOR(device))
#define DEVICE_ON(device)
#define DEVICE_OFF(device)
 
#elif (MAJOR_NR == MATSUSHITA_CDROM_MAJOR)
 
#define DEVICE_NAME "Matsushita CD-ROM controller #1"
#define DEVICE_REQUEST do_sbpcd_request
#define DEVICE_NR(device) (MINOR(device))
#define DEVICE_ON(device)
#define DEVICE_OFF(device)
 
#elif (MAJOR_NR == MATSUSHITA_CDROM2_MAJOR)
 
#define DEVICE_NAME "Matsushita CD-ROM controller #2"
#define DEVICE_REQUEST do_sbpcd2_request
#define DEVICE_NR(device) (MINOR(device))
#define DEVICE_ON(device)
#define DEVICE_OFF(device)
 
#elif (MAJOR_NR == MATSUSHITA_CDROM3_MAJOR)
 
#define DEVICE_NAME "Matsushita CD-ROM controller #3"
#define DEVICE_REQUEST do_sbpcd3_request
#define DEVICE_NR(device) (MINOR(device))
#define DEVICE_ON(device)
#define DEVICE_OFF(device)
 
#elif (MAJOR_NR == MATSUSHITA_CDROM4_MAJOR)
 
#define DEVICE_NAME "Matsushita CD-ROM controller #4"
#define DEVICE_REQUEST do_sbpcd4_request
#define DEVICE_NR(device) (MINOR(device))
#define DEVICE_ON(device)
#define DEVICE_OFF(device)
 
#elif (MAJOR_NR == AZTECH_CDROM_MAJOR)
 
#define DEVICE_NAME "Aztech CD-ROM"
#define DEVICE_REQUEST do_aztcd_request
#define DEVICE_NR(device) (MINOR(device))
#define DEVICE_ON(device)
#define DEVICE_OFF(device)
 
#elif (MAJOR_NR == CDU535_CDROM_MAJOR)
 
#define DEVICE_NAME "SONY-CDU535"
#define DEVICE_INTR do_cdu535
#define DEVICE_REQUEST do_cdu535_request
#define DEVICE_NR(device) (MINOR(device))
#define DEVICE_ON(device)
#define DEVICE_OFF(device)
 
#elif (MAJOR_NR == GOLDSTAR_CDROM_MAJOR)
 
#define DEVICE_NAME "Goldstar R420"
#define DEVICE_REQUEST do_gscd_request
#define DEVICE_NR(device) (MINOR(device))
#define DEVICE_ON(device)
#define DEVICE_OFF(device)
 
#elif (MAJOR_NR == CM206_CDROM_MAJOR)
#define DEVICE_NAME "Philips/LMS cd-rom cm206"
#define DEVICE_REQUEST do_cm206_request
#define DEVICE_NR(device) (MINOR(device))
#define DEVICE_ON(device)
#define DEVICE_OFF(device)
 
#elif (MAJOR_NR == OPTICS_CDROM_MAJOR)
 
#define DEVICE_NAME "DOLPHIN 8000AT CD-ROM"
#define DEVICE_REQUEST do_optcd_request
#define DEVICE_NR(device) (MINOR(device))
#define DEVICE_ON(device)
#define DEVICE_OFF(device)
 
#elif (MAJOR_NR == SANYO_CDROM_MAJOR)
 
#define DEVICE_NAME "Sanyo H94A CD-ROM"
#define DEVICE_REQUEST do_sjcd_request
#define DEVICE_NR(device) (MINOR(device))
#define DEVICE_ON(device)
#define DEVICE_OFF(device)
 
#endif /* MAJOR_NR == whatever */
 
#if (MAJOR_NR != SCSI_TAPE_MAJOR)
#if !defined(IDE_DRIVER)
 
#ifndef CURRENT
#define CURRENT (blk_dev[MAJOR_NR].current_request)
#endif
 
#define CURRENT_DEV DEVICE_NR(CURRENT->rq_dev)
 
#ifdef DEVICE_INTR
static void (*DEVICE_INTR)(void) = NULL;
#endif
#ifdef DEVICE_TIMEOUT
 
#define SET_TIMER \
((timer_table[DEVICE_TIMEOUT].expires = jiffies + TIMEOUT_VALUE), \
(timer_active |= 1<<DEVICE_TIMEOUT))
 
#define CLEAR_TIMER \
timer_active &= ~(1<<DEVICE_TIMEOUT)
 
#define SET_INTR(x) \
if ((DEVICE_INTR = (x)) != NULL) \
SET_TIMER; \
else \
CLEAR_TIMER;
 
#else
 
#define SET_INTR(x) (DEVICE_INTR = (x))
 
#endif /* DEVICE_TIMEOUT */
 
static void (DEVICE_REQUEST)(void);
 
#ifdef DEVICE_INTR
#define CLEAR_INTR SET_INTR(NULL)
#else
#define CLEAR_INTR
#endif
 
#define INIT_REQUEST \
if (!CURRENT) {\
CLEAR_INTR; \
return; \
} \
if (MAJOR(CURRENT->rq_dev) != MAJOR_NR) \
panic(DEVICE_NAME ": request list destroyed"); \
if (CURRENT->bh) { \
if (!buffer_locked(CURRENT->bh)) \
panic(DEVICE_NAME ": block not locked"); \
}
 
#endif /* !defined(IDE_DRIVER) */
 
/* end_request() - SCSI devices have their own version */
/* - IDE drivers have their own copy too */
 
#if ! SCSI_BLK_MAJOR(MAJOR_NR)
 
#if defined(IDE_DRIVER) && !defined(_IDE_C) /* shared copy for IDE modules */
void ide_end_request(byte uptodate, ide_hwgroup_t *hwgroup);
#else
 
#ifdef IDE_DRIVER
void ide_end_request(byte uptodate, ide_hwgroup_t *hwgroup) {
struct request *req = hwgroup->rq;
#else
static void end_request(int uptodate) {
struct request *req = CURRENT;
#endif /* IDE_DRIVER */
struct buffer_head * bh;
 
req->errors = 0;
if (!uptodate) {
printk("end_request: I/O error, dev %s, sector %lu\n",
kdevname(req->rq_dev), req->sector);
req->nr_sectors--;
req->nr_sectors &= ~SECTOR_MASK;
req->sector += (BLOCK_SIZE / 512);
req->sector &= ~SECTOR_MASK;
}
 
if ((bh = req->bh) != NULL) {
req->bh = bh->b_reqnext;
bh->b_reqnext = NULL;
mark_buffer_uptodate(bh, uptodate);
unlock_buffer(bh);
if ((bh = req->bh) != NULL) {
req->current_nr_sectors = bh->b_size >> 9;
if (req->nr_sectors < req->current_nr_sectors) {
req->nr_sectors = req->current_nr_sectors;
printk("end_request: buffer-list destroyed\n");
}
req->buffer = bh->b_data;
return;
}
}
#ifndef DEVICE_NO_RANDOM
add_blkdev_randomness(MAJOR(req->rq_dev));
#endif
#ifdef IDE_DRIVER
blk_dev[MAJOR(req->rq_dev)].current_request = req->next;
hwgroup->rq = NULL;
#else
DEVICE_OFF(req->rq_dev);
CURRENT = req->next;
#endif /* IDE_DRIVER */
if (req->sem != NULL)
up(req->sem);
req->rq_status = RQ_INACTIVE;
wake_up(&wait_for_request);
}
#endif /* defined(IDE_DRIVER) && !defined(_IDE_C) */
#endif /* ! SCSI_BLK_MAJOR(MAJOR_NR) */
#endif /* (MAJOR_NR != SCSI_TAPE_MAJOR) */
 
#endif /* defined(MAJOR_NR) || defined(IDE_DRIVER) */
 
#endif /* _BLK_H */
/ide-ics.c
0,0 → 1,297
/*
* linux/arch/arm/drivers/block/ide-ics.c
*
* Copyright (c) 1996,1997 Russell King.
*
* Changelog:
* 08-06-1996 RMK Created
* 12-09-1997 RMK Added interrupt enable/disable
*/
 
#include <linux/module.h>
#include <linux/malloc.h>
#include <linux/blkdev.h>
#include <linux/errno.h>
 
#include <asm/dma.h>
#include <asm/ecard.h>
#include <asm/io.h>
 
#include "ide.h"
 
/*
* Maximum number of interfaces per card
*/
#define MAX_IFS 2
 
#define ICS_IDENT_OFFSET 0x8a0
 
#define ICS_ARCIN_V5_INTRSTAT 0x000
#define ICS_ARCIN_V5_INTROFFSET 0x001
#define ICS_ARCIN_V5_IDEOFFSET 0xa00
#define ICS_ARCIN_V5_IDEALTOFFSET 0xae0
#define ICS_ARCIN_V5_IDESTEPPING 4
 
#define ICS_ARCIN_V6_IDEOFFSET_1 0x800
#define ICS_ARCIN_V6_INTROFFSET_1 0x880
#define ICS_ARCIN_V6_INTRSTAT_1 0x8a4
#define ICS_ARCIN_V6_IDEALTOFFSET_1 0x8e0
#define ICS_ARCIN_V6_IDEOFFSET_2 0xc00
#define ICS_ARCIN_V6_INTROFFSET_2 0xc80
#define ICS_ARCIN_V6_INTRSTAT_2 0xca4
#define ICS_ARCIN_V6_IDEALTOFFSET_2 0xce0
#define ICS_ARCIN_V6_IDESTEPPING 4
 
static const card_ids icside_cids[] = {
{ MANU_ICS, PROD_ICS_IDE },
{ MANU_ICS2, PROD_ICS2_IDE },
{ 0xffff, 0xffff }
};
 
typedef enum {
ics_if_unknown,
ics_if_arcin_v5,
ics_if_arcin_v6
} iftype_t;
 
static struct expansion_card *ec[MAX_ECARDS];
static int result[MAX_ECARDS][MAX_IFS];
 
 
/* ---------------- Version 5 PCB Support Functions --------------------- */
/* Prototype: icside_irqenable_arcin_v5 (struct expansion_card *ec, int irqnr)
* Purpose : enable interrupts from card
*/
static void icside_irqenable_arcin_v5 (struct expansion_card *ec, int irqnr)
{
unsigned int memc_port = (unsigned int)ec->irq_data;
outb (0, memc_port + ICS_ARCIN_V5_INTROFFSET);
}
 
/* Prototype: icside_irqdisable_arcin_v5 (struct expansion_card *ec, int irqnr)
* Purpose : disable interrupts from card
*/
static void icside_irqdisable_arcin_v5 (struct expansion_card *ec, int irqnr)
{
unsigned int memc_port = (unsigned int)ec->irq_data;
inb (memc_port + ICS_ARCIN_V5_INTROFFSET);
}
 
static const expansioncard_ops_t icside_ops_arcin_v5 = {
icside_irqenable_arcin_v5,
icside_irqdisable_arcin_v5,
NULL,
NULL,
NULL,
NULL
};
 
 
/* ---------------- Version 6 PCB Support Functions --------------------- */
/* Prototype: icside_irqenable_arcin_v6 (struct expansion_card *ec, int irqnr)
* Purpose : enable interrupts from card
*/
static void icside_irqenable_arcin_v6 (struct expansion_card *ec, int irqnr)
{
unsigned int ide_base_port = (unsigned int)ec->irq_data;
outb (0, ide_base_port + ICS_ARCIN_V6_INTROFFSET_1);
outb (0, ide_base_port + ICS_ARCIN_V6_INTROFFSET_2);
}
 
/* Prototype: icside_irqdisable_arcin_v6 (struct expansion_card *ec, int irqnr)
* Purpose : disable interrupts from card
*/
static void icside_irqdisable_arcin_v6 (struct expansion_card *ec, int irqnr)
{
unsigned int ide_base_port = (unsigned int)ec->irq_data;
inb (ide_base_port + ICS_ARCIN_V6_INTROFFSET_1);
inb (ide_base_port + ICS_ARCIN_V6_INTROFFSET_2);
}
 
/* Prototype: icside_irqprobe(struct expansion_card *ec)
* Purpose : detect an active interrupt from card
*/
static int icside_irqprobe_arcin_v6(struct expansion_card *ec)
{
unsigned int ide_base_port = (unsigned int)ec->irq_data;
 
return inb(ide_base_port + ICS_ARCIN_V6_INTRSTAT_1) & 1 ||
inb(ide_base_port + ICS_ARCIN_V6_INTRSTAT_2) & 1;
}
 
static const expansioncard_ops_t icside_ops_arcin_v6 = {
icside_irqenable_arcin_v6,
icside_irqdisable_arcin_v6,
icside_irqprobe_arcin_v6,
NULL,
NULL,
NULL
};
 
 
 
/* Prototype: icside_identifyif (struct expansion_card *ec)
* Purpose : identify IDE interface type
* Notes : checks the description string
*/
static iftype_t icside_identifyif (struct expansion_card *ec)
{
unsigned int addr;
iftype_t iftype;
int id = 0;
 
iftype = ics_if_unknown;
 
addr = ecard_address (ec, ECARD_IOC, ECARD_FAST) + ICS_IDENT_OFFSET;
 
id = inb (addr) & 1;
id |= (inb (addr + 1) & 1) << 1;
id |= (inb (addr + 2) & 1) << 2;
id |= (inb (addr + 3) & 1) << 3;
 
switch (id) {
case 0: /* A3IN */
printk ("icside: A3IN unsupported\n");
break;
 
case 1: /* A3USER */
printk ("icside: A3USER unsupported\n");
break;
 
case 3: /* ARCIN V6 */
printk ("icside: detected ARCIN V6 in slot %d\n", ec->slot_no);
iftype = ics_if_arcin_v6;
break;
 
case 15:/* ARCIN V5 (no id) */
printk ("icside: detected ARCIN V5 in slot %d\n", ec->slot_no);
iftype = ics_if_arcin_v5;
break;
 
default:/* we don't know - complain very loudly */
printk ("icside: ***********************************\n");
printk ("icside: *** UNKNOWN ICS INTERFACE id=%d ***\n", id);
printk ("icside: ***********************************\n");
printk ("icside: please report this to: linux@arm.linux.org.uk\n");
break;
}
 
return iftype;
}
 
/* Prototype: icside_register (struct expansion_card *ec)
* Purpose : register an ICS IDE card with the IDE driver
* Notes : we make sure that interrupts are disabled from the card
*/
static inline void icside_register (struct expansion_card *ec, int index)
{
unsigned long port = 0, latch;
 
result[index][0] = -1;
result[index][1] = -1;
 
switch (icside_identifyif (ec)) {
case ics_if_unknown:
default:
printk ("icside: *** Warning: ICS IDE Interface unrecognised! ***\n");
break;
 
case ics_if_arcin_v5:
port = ecard_address (ec, ECARD_MEMC, 0);
ec->irqaddr = (unsigned char *)ioaddr(port + ICS_ARCIN_V5_INTRSTAT);
ec->irqmask = 1;
ec->irq_data = (void *)port;
ec->ops = (expansioncard_ops_t *)&icside_ops_arcin_v5;
 
/*
* Be on the safe side - disable interrupts
*/
inb (port + ICS_ARCIN_V5_INTROFFSET);
result[index][0] =
ide_register_port(port + ICS_ARCIN_V5_IDEOFFSET,
port + ICS_ARCIN_V5_IDEALTOFFSET,
ICS_ARCIN_V5_IDESTEPPING,
ec->irq);
break;
 
case ics_if_arcin_v6:
latch = ecard_address (ec, ECARD_IOC, ECARD_FAST);
if (ec->dma == NO_DMA)
port = ecard_address (ec, ECARD_EASI, ECARD_FAST);
if (port)
outb(0x20, latch);
else {
port = latch;
outb(0, port);
}
ec->irq_data = (void *)port;
ec->ops = (expansioncard_ops_t *)&icside_ops_arcin_v6;
 
/*
* Be on the safe side - disable interrupts
*/
inb (port + ICS_ARCIN_V6_INTROFFSET_1);
inb (port + ICS_ARCIN_V6_INTROFFSET_2);
result[index][0] =
ide_register_port(port + ICS_ARCIN_V6_IDEOFFSET_1,
port + ICS_ARCIN_V6_IDEALTOFFSET_1,
ICS_ARCIN_V6_IDESTEPPING,
ec->irq);
result[index][1] =
ide_register_port(port + ICS_ARCIN_V6_IDEOFFSET_2,
port + ICS_ARCIN_V6_IDEALTOFFSET_2,
ICS_ARCIN_V6_IDESTEPPING,
ec->irq);
break;
}
}
 
int icside_init (void)
{
int i;
 
for (i = 0; i < MAX_ECARDS; i++)
ec[i] = NULL;
 
ecard_startfind ();
 
for (i = 0; ; i++) {
if ((ec[i] = ecard_find (0, icside_cids)) == NULL)
break;
 
ecard_claim (ec[i]);
icside_register (ec[i], i);
}
 
for (i = 0; i < MAX_ECARDS; i++)
if (ec[i] && result[i][0] < 0 && result[i][1] < 0) {
ecard_release (ec[i]);
ec[i] = NULL;
}
return 0;
}
 
#ifdef MODULE
int init_module (void)
{
return icside_init();
}
 
void cleanup_module (void)
{
int i;
 
for (i = 0; i < MAX_ECARDS; i++)
if (ec[i]) {
if (result[i][0] >= 0)
ide_unregister (result[i][0]);
 
if (result[i][1] >= 0)
ide_unregister (result[i][1]);
ecard_release (ec[i]);
ec[i] = NULL;
}
}
#endif
 
/genhd.c
0,0 → 1,445
/*
* Code extracted from
* linux/kernel/hd.c
*
* Copyright (C) 1991, 1992 Linus Torvalds
*
*
* Thanks to Branko Lankester, lankeste@fwi.uva.nl, who found a bug
* in the early extended-partition checks and added DM partitions
*
* Support for DiskManager v6.0x added by Mark Lord,
* with information provided by OnTrack. This now works for linux fdisk
* and LILO, as well as loadlin and bootln. Note that disks other than
* /dev/hda *must* have a "DOS" type 0x51 partition in the first slot (hda1).
*
* More flexible handling of extended partitions - aeb, 950831
*
* Modifications Copyright (C) 1995, 1996 Russell King
*
* Origional from linux/drivers/genhd.c
*
* Altered to find image files on ADFS-formatted hard drives.
*/
 
#include <linux/config.h>
#include <linux/fs.h>
#include <linux/genhd.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/string.h>
#ifdef CONFIG_BLK_DEV_INITRD
#include <linux/blk.h>
#endif
 
#include <asm/system.h>
 
/*
* Many architectures don't like unaligned accesses, which is
* frequently the case with the nr_sects and start_sect partition
* table entries.
*/
#include <asm/unaligned.h>
 
#define SYS_IND(p) get_unaligned(&p->sys_ind)
#define NR_SECTS(p) get_unaligned(&p->nr_sects)
#define START_SECT(p) get_unaligned(&p->start_sect)
 
 
struct gendisk *gendisk_head;
 
static int current_minor;
extern int *blk_size[];
extern void rd_load(void);
extern void initrd_load(void);
 
extern int chr_dev_init(void);
extern int blk_dev_init(void);
extern int scsi_dev_init(void);
extern int net_dev_init(void);
 
/*
* disk_name() is used by genhd.c and md.c.
* It formats the devicename of the indicated disk
* into the supplied buffer, and returns a pointer
* to that same buffer (for convenience).
*/
char *disk_name (struct gendisk *hd, int minor, char *buf)
{
unsigned int part;
const char *maj = hd->major_name;
char unit = (minor >> hd->minor_shift) + 'a';
 
#ifdef CONFIG_BLK_DEV_IDE
/*
* IDE devices use multiple major numbers, but the drives
* are named as: {hda,hdb}, {hdc,hdd}, {hde,hdf}, {hdg,hdh}..
* This requires special handling here.
*/
switch (hd->major) {
case IDE3_MAJOR:
unit += 2;
case IDE2_MAJOR:
unit += 2;
case IDE1_MAJOR:
unit += 2;
case IDE0_MAJOR:
maj = "hd";
}
#endif
part = minor & ((1 << hd->minor_shift) - 1);
if (part)
sprintf(buf, "%s%c%d", maj, unit, part);
else
sprintf(buf, "%s%c", maj, unit);
return buf;
}
 
void add_partition (struct gendisk *hd, int minor, int start, int size)
{
char buf[8];
hd->part[minor].start_sect = start;
hd->part[minor].nr_sects = size;
printk(" %s", disk_name(hd, minor, buf));
}
 
static inline int is_extended_partition(struct partition *p)
{
return (SYS_IND(p) == DOS_EXTENDED_PARTITION ||
SYS_IND(p) == LINUX_EXTENDED_PARTITION);
}
 
#ifdef CONFIG_MSDOS_PARTITION
/*
* Create devices for each logical partition in an extended partition.
* The logical partitions form a linked list, with each entry being
* a partition table with two entries. The first entry
* is the real data partition (with a start relative to the partition
* table start). The second is a pointer to the next logical partition
* (with a start relative to the entire extended partition).
* We do not create a Linux partition for the partition tables, but
* only for the actual data partitions.
*/
 
static void extended_partition(struct gendisk *hd, kdev_t dev)
{
struct buffer_head *bh;
struct partition *p;
unsigned long first_sector, first_size, this_sector, this_size;
int mask = (1 << hd->minor_shift) - 1;
int i;
 
first_sector = hd->part[MINOR(dev)].start_sect;
first_size = hd->part[MINOR(dev)].nr_sects;
this_sector = first_sector;
 
while (1) {
if ((current_minor & mask) == 0)
return;
if (!(bh = bread(dev,0,1024)))
return;
/*
* This block is from a device that we're about to stomp on.
* So make sure nobody thinks this block is usable.
*/
bh->b_state = 0;
 
if (*(unsigned short *) (bh->b_data+510) != 0xAA55)
goto done;
 
p = (struct partition *) (0x1BE + bh->b_data);
 
this_size = hd->part[MINOR(dev)].nr_sects;
 
/*
* Usually, the first entry is the real data partition,
* the 2nd entry is the next extended partition, or empty,
* and the 3rd and 4th entries are unused.
* However, DRDOS sometimes has the extended partition as
* the first entry (when the data partition is empty),
* and OS/2 seems to use all four entries.
*/
 
/*
* First process the data partition(s)
*/
for (i=0; i<4; i++, p++) {
if (!NR_SECTS(p) || is_extended_partition(p))
continue;
 
/* Check the 3rd and 4th entries -
these sometimes contain random garbage */
if (i >= 2
&& START_SECT(p) + NR_SECTS(p) > this_size
&& (this_sector + START_SECT(p) < first_sector ||
this_sector + START_SECT(p) + NR_SECTS(p) >
first_sector + first_size))
continue;
 
add_partition(hd, current_minor, this_sector+START_SECT(p), NR_SECTS(p));
current_minor++;
if ((current_minor & mask) == 0)
goto done;
}
/*
* Next, process the (first) extended partition, if present.
* (So far, there seems to be no reason to make
* extended_partition() recursive and allow a tree
* of extended partitions.)
* It should be a link to the next logical partition.
* Create a minor for this just long enough to get the next
* partition table. The minor will be reused for the next
* data partition.
*/
p -= 4;
for (i=0; i<4; i++, p++)
if(NR_SECTS(p) && is_extended_partition(p))
break;
if (i == 4)
goto done; /* nothing left to do */
 
hd->part[current_minor].nr_sects = NR_SECTS(p);
hd->part[current_minor].start_sect = first_sector + START_SECT(p);
this_sector = first_sector + START_SECT(p);
dev = MKDEV(hd->major, current_minor);
brelse(bh);
}
done:
brelse(bh);
}
 
#ifdef CONFIG_BSD_DISKLABEL
/*
* Create devices for BSD partitions listed in a disklabel, under a
* dos-like partition. See extended_partition() for more information.
*/
static void bsd_disklabel_partition(struct gendisk *hd, kdev_t dev)
{
struct buffer_head *bh;
struct bsd_disklabel *l;
struct bsd_partition *p;
int mask = (1 << hd->minor_shift) - 1;
 
if (!(bh = bread(dev,0,1024)))
return;
bh->b_state = 0;
l = (struct bsd_disklabel *) (bh->b_data+512);
if (l->d_magic != BSD_DISKMAGIC) {
brelse(bh);
return;
}
 
p = &l->d_partitions[0];
while (p - &l->d_partitions[0] <= BSD_MAXPARTITIONS) {
if ((current_minor & mask) >= (4 + hd->max_p))
break;
 
if (p->p_fstype != BSD_FS_UNUSED) {
add_partition(hd, current_minor, p->p_offset, p->p_size);
current_minor++;
}
p++;
}
brelse(bh);
 
}
#endif
 
static int msdos_partition(struct gendisk *hd, kdev_t dev, unsigned long first_sector)
{
int i, minor = current_minor;
struct buffer_head *bh;
struct partition *p;
unsigned char *data;
int mask = (1 << hd->minor_shift) - 1;
 
if (!(bh = bread(dev,0,1024))) {
printk(" unable to read partition table\n");
return -1;
}
data = bh->b_data;
/* In some cases we modify the geometry */
/* of the drive (below), so ensure that */
/* nobody else tries to re-use this data. */
bh->b_state = 0;
 
if (*(unsigned short *) (0x1fe + data) != 0xAA55) {
brelse(bh);
return 0;
}
printk (" [DOS]");
p = (struct partition *) (0x1be + data);
 
current_minor += 4; /* first "extra" minor (for extended partitions) */
for (i=1 ; i<=4 ; minor++,i++,p++) {
if (!NR_SECTS(p))
continue;
add_partition(hd, minor, first_sector+START_SECT(p), NR_SECTS(p));
if (is_extended_partition(p)) {
printk(" <");
/*
* If we are rereading the partition table, we need
* to set the size of the partition so that we will
* be able to bread the block containing the extended
* partition info.
*/
hd->sizes[minor] = hd->part[minor].nr_sects
>> (BLOCK_SIZE_BITS - 9);
extended_partition(hd, MKDEV(hd->major, minor));
printk(" >");
/* prevent someone doing mkfs or mkswap on an
extended partition, but leave room for LILO */
if (hd->part[minor].nr_sects > 2)
hd->part[minor].nr_sects = 2;
}
#ifdef CONFIG_BSD_DISKLABEL
if (SYS_IND(p) == BSD_PARTITION) {
printk(" <");
bsd_disklabel_partition(hd, MKDEV(hd->major, minor));
printk(" >");
}
#endif
}
/*
* Check for old-style Disk Manager partition table
*/
if (*(unsigned short *) (data+0xfc) == 0x55AA) {
p = (struct partition *) (0x1be + data);
for (i = 4 ; i < 16 ; i++, current_minor++) {
p--;
if ((current_minor & mask) == 0)
break;
if (!(START_SECT(p) && NR_SECTS(p)))
continue;
add_partition(hd, current_minor, START_SECT(p), NR_SECTS(p));
}
}
printk("\n");
brelse(bh);
return 1;
}
 
#endif /* CONFIG_MSDOS_PARTITION */
 
extern int adfspart_initdev (struct gendisk *hd, kdev_t dev, int first_sector, int minor);
 
static void check_partition(struct gendisk *hd, kdev_t dev)
{
static int first_time = 1;
unsigned long first_sector;
char buf[8];
 
if (first_time)
printk("Partition check:\n");
first_time = 0;
first_sector = hd->part[MINOR(dev)].start_sect;
 
/*
* This is a kludge to allow the partition check to be
* skipped for specific drives (e.g. IDE cd-rom drives)
*/
if ((int)first_sector == -1) {
hd->part[MINOR(dev)].start_sect = 0;
return;
}
 
printk(" %s:", disk_name(hd, MINOR(dev), buf));
#ifdef CONFIG_BLK_DEV_PART
if (adfspart_initdev (hd, dev, first_sector, current_minor))
return;
#endif
#ifdef CONFIG_MSDOS_PARTITION
if (msdos_partition(hd, dev, first_sector))
return;
#endif
printk(" unknown partition table\n");
}
 
/*
* This function is used to re-read partition tables for removable disks.
* Much of the cleanup from the old partition tables should have already been
* done
*/
 
/*
* This function will re-read the partition tables for a given device,
* and set things back up again. There are some important caveats,
* however. You must ensure that no one is using the device, and no one
* can start using the device while this function is being executed.
*/
void resetup_one_dev(struct gendisk *dev, int drive)
{
int i;
int first_minor = drive << dev->minor_shift;
int end_minor = first_minor + dev->max_p;
 
blk_size[dev->major] = NULL;
current_minor = 1 + first_minor;
check_partition(dev, MKDEV(dev->major, first_minor));
 
/*
* We need to set the sizes array before we will be able to access
* any of the partitions on this device.
*/
if (dev->sizes != NULL) { /* optional safeguard in ll_rw_blk.c */
for (i = first_minor; i < end_minor; i++)
dev->sizes[i] = dev->part[i].nr_sects >> (BLOCK_SIZE_BITS - 9);
blk_size[dev->major] = dev->sizes;
}
}
 
static void setup_dev(struct gendisk *dev)
{
int i, drive;
int end_minor = dev->max_nr * dev->max_p;
 
blk_size[dev->major] = NULL;
for (i = 0 ; i < end_minor; i++) {
dev->part[i].start_sect = 0;
dev->part[i].nr_sects = 0;
}
dev->init(dev);
for (drive = 0 ; drive < dev->nr_real ; drive++) {
int first_minor = drive << dev->minor_shift;
current_minor = 1 + first_minor;
check_partition(dev, MKDEV(dev->major, first_minor));
}
if (dev->sizes != NULL) { /* optional safeguard in ll_rw_blk.c */
for (i = 0; i < end_minor; i++)
dev->sizes[i] = dev->part[i].nr_sects >> (BLOCK_SIZE_BITS - 9);
blk_size[dev->major] = dev->sizes;
}
}
 
void device_setup(void)
{
extern void console_map_init(void);
extern void adfsimg_init (void);
struct gendisk *p;
int nr=0;
 
chr_dev_init();
blk_dev_init();
sti();
#ifdef CONFIG_SCSI
scsi_dev_init();
#endif
#ifdef CONFIG_INET
net_dev_init();
#endif
console_map_init();
#ifdef CONFIG_BLK_DEV_IMG
adfsimg_init ();
#endif
for (p = gendisk_head ; p ; p=p->next) {
setup_dev(p);
nr += p->nr_real;
}
#ifdef CONFIG_BLK_DEV_RAM
#ifdef CONFIG_BLK_DEV_INITRD
if (initrd_start && mount_initrd) initrd_load();
else
#endif
rd_load();
#endif
}
/Config.in
0,0 → 1,53
#
# Block device driver configuration
#
mainmenu_option next_comment
comment 'Floppy, IDE, and other block devices'
 
tristate 'Normal floppy disk support' CONFIG_BLK_DEV_FD
bool 'Enhanced IDE disk/cdrom/tape support' CONFIG_BLK_DEV_IDE
comment 'Please see Documentation/ide.txt for help/info on IDE drives'
if [ "$CONFIG_BLK_DEV_IDE" = "n" ]; then
bool 'Old harddisk (IDE) driver' CONFIG_BLK_DEV_HD_ONLY
else
bool ' Use old disk-only driver on primary interface' CONFIG_BLK_DEV_HD_IDE
bool ' Include IDE/ATAPI CDROM support' CONFIG_BLK_DEV_IDECD
bool ' Include IDE/ATAPI TAPE support' CONFIG_BLK_DEV_IDETAPE
bool ' Include IDE/ATAPI FLOPPY support (new)' CONFIG_BLK_DEV_IDEFLOPPY
bool ' SCSI emulation support' CONFIG_BLK_DEV_IDESCSI
bool ' Support removable IDE interfaces (PCMCIA)' CONFIG_BLK_DEV_IDE_PCMCIA
bool ' Support expansion card IDE interfaces' CONFIG_BLK_DEV_IDE_CARDS
if [ "$CONFIG_BLK_DEV_IDE_CARDS" = "y" ]; then
tristate ' ICS IDE interface support' CONFIG_BLK_DEV_IDE_ICSIDE
tristate ' RapIDE interface support' CONFIG_BLK_DEV_IDE_RAPIDE
fi
fi
 
tristate 'MFM harddisk support' CONFIG_BLK_DEV_XD
 
comment 'Additional Block Devices'
 
tristate 'Loopback device support' CONFIG_BLK_DEV_LOOP
bool 'Multiple devices driver support' CONFIG_BLK_DEV_MD
if [ "$CONFIG_BLK_DEV_MD" = "y" ]; then
tristate ' Linear (append) mode' CONFIG_MD_LINEAR
tristate ' RAID-0 (striping) mode' CONFIG_MD_STRIPED
tristate ' RAID-1 (mirroring) mode' CONFIG_MD_MIRRORING
tristate ' RAID-4/RAID-5 mode' CONFIG_MD_RAID5
fi
tristate 'RAM disk support' CONFIG_BLK_DEV_RAM
if [ "$CONFIG_BLK_DEV_RAM" = "y" ]; then
bool ' Initial RAM disk (initrd) support' CONFIG_BLK_DEV_INITRD
fi
bool 'ADFS partition support' CONFIG_BLK_DEV_PART
#if [ "$CONFIG_BLK_DEV_PART" = "y" -a "$CONFIG_EXPERIMENTAL" = "y" ]; then
# bool 'ADFS image file support (EXPERIMENTAL)' CONFIG_BLK_DEV_IMG
#fi
 
if [ "$CONFIG_BLK_DEV_HD_IDE" = "y" -o "$CONFIG_BLK_DEV_HD_ONLY" = "y" ]; then
define_bool CONFIG_BLK_DEV_HD y
else
define_bool CONFIG_BLK_DEV_HD n
fi
 
endmenu
/hd.c
0,0 → 1,1134
/*
* linux/arch/arm/drivers/block/hd.c
* [ origional file: linux/drivers/block/hd.c ]
*
* Copyright (C) 1991, 1992 Linus Torvalds
* Modified 1995 Russell King for ARM processor.
*/
 
/*
* This is the low-level hd interrupt support. It traverses the
* request-list, using interrupts to jump between functions. As
* all the functions are called within interrupts, we may not
* sleep. Special care is recommended.
*
* modified by Drew Eckhardt to check nr of hd's from the CMOS.
*
* Thanks to Branko Lankester, lankeste@fwi.uva.nl, who found a bug
* in the early extended-partition checks and added DM partitions
*
* IRQ-unmask, drive-id, multiple-mode, support for ">16 heads",
* and general streamlining by Mark Lord.
*/
 
#define DEFAULT_MULT_COUNT 0 /* set to 0 to disable multiple mode at boot */
#define DEFAULT_UNMASK_INTR 0 /* set to 0 to *NOT* unmask irq's more often */
 
#include <asm/irq.h>
#include <linux/errno.h>
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/timer.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/hdreg.h>
#include <linux/genhd.h>
#include <linux/malloc.h>
#include <linux/string.h>
#include <linux/ioport.h>
 
#define REALLY_SLOW_IO
#include <asm/system.h>
#include <asm/io.h>
#include <asm/segment.h>
 
#define MAJOR_NR HD_MAJOR
#include <linux/blk.h>
 
#undef HD_IRQ
#define HD_IRQ 11
 
static int revalidate_hddisk(kdev_t, int);
 
#define HD_DELAY 0
 
#define MAX_ERRORS 16 /* Max read/write errors/sector */
#define RESET_FREQ 8 /* Reset controller every 8th retry */
#define RECAL_FREQ 4 /* Recalibrate every 4th retry */
#define MAX_HD 2
 
#define STAT_OK (READY_STAT|SEEK_STAT)
#define OK_STATUS(s) (((s)&(STAT_OK|(BUSY_STAT|WRERR_STAT|ERR_STAT)))==STAT_OK)
 
static void recal_intr(void);
static void bad_rw_intr(void);
 
static char recalibrate[MAX_HD];
static char special_op[MAX_HD];
static int access_count[MAX_HD];
static char busy[MAX_HD];
static struct wait_queue * busy_wait;
 
static int reset;
static int hd_error;
 
/*
* This struct defines the HD's and their types.
*/
struct hd_i_struct {
unsigned int head,sect,cyl,wpcom,lzone,ctl;
};
static struct hd_driveid *hd_ident_info[MAX_HD];
#ifdef HD_TYPE
static struct hd_i_struct hd_info[] = { HD_TYPE };
struct hd_i_struct bios_info[] = { HD_TYPE };
static int NR_HD = ((sizeof (hd_info))/(sizeof (struct hd_i_struct)));
#else
static struct hd_i_struct hd_info[MAX_HD];
struct hd_i_struct bios_info[MAX_HD];
static int NR_HD;
#endif
 
static struct hd_struct hd[MAX_HD<<6];
static int hd_sizes[MAX_HD<<6];
static int hd_blocksizes[MAX_HD<<6];
static int hd_hardsectsizes[MAX_HD<<6];
 
#if (HD_DELAY > 0)
unsigned long last_req;
 
unsigned long read_timer(void)
{
unsigned long t, flags;
int i;
 
save_flags_cli (flags);
t = jiffies * 11932;
outb_p(0, 0x43);
i = inb_p(0x40);
i |= inb(0x40) << 8;
restore_flags(flags);
return(t - i);
}
#endif
 
void hd_setup(char *str, int *ints)
{
int hdind = 0;
 
if (ints[0] != 3)
return;
if (bios_info[0].head != 0)
hdind=1;
bios_info[hdind].head = hd_info[hdind].head = ints[2];
bios_info[hdind].sect = hd_info[hdind].sect = ints[3];
bios_info[hdind].cyl = hd_info[hdind].cyl = ints[1];
bios_info[hdind].wpcom = hd_info[hdind].wpcom = 0;
bios_info[hdind].lzone = hd_info[hdind].lzone = ints[1];
bios_info[hdind].ctl = hd_info[hdind].ctl = (ints[2] > 8 ? 8 : 0);
NR_HD = hdind+1;
}
 
static void dump_status (const char *msg, unsigned int stat)
{
unsigned long flags;
char devc;
 
devc = CURRENT ? 'a' + DEVICE_NR(CURRENT->rq_dev) : '?';
save_flags (flags);
sti();
printk("hd%c: %s: status=0x%02x { ", devc, msg, stat & 0xff);
if (stat & BUSY_STAT) printk("Busy ");
if (stat & READY_STAT) printk("DriveReady ");
if (stat & WRERR_STAT) printk("WriteFault ");
if (stat & SEEK_STAT) printk("SeekComplete ");
if (stat & DRQ_STAT) printk("DataRequest ");
if (stat & ECC_STAT) printk("CorrectedError ");
if (stat & INDEX_STAT) printk("Index ");
if (stat & ERR_STAT) printk("Error ");
printk("}\n");
if ((stat & ERR_STAT) == 0) {
hd_error = 0;
} else {
hd_error = inb(HD_ERROR);
printk("hd%c: %s: error=0x%02x { ", devc, msg, hd_error & 0xff);
if (hd_error & BBD_ERR) printk("BadSector ");
if (hd_error & ECC_ERR) printk("UncorrectableError ");
if (hd_error & ID_ERR) printk("SectorIdNotFound ");
if (hd_error & ABRT_ERR) printk("DriveStatusError ");
if (hd_error & TRK0_ERR) printk("TrackZeroNotFound ");
if (hd_error & MARK_ERR) printk("AddrMarkNotFound ");
printk("}");
if (hd_error & (BBD_ERR|ECC_ERR|ID_ERR|MARK_ERR)) {
printk(", CHS=%d/%d/%d", (inb(HD_HCYL)<<8) + inb(HD_LCYL),
inb(HD_CURRENT) & 0xf, inb(HD_SECTOR));
if (CURRENT)
printk(", sector=%ld", CURRENT->sector);
}
printk("\n");
}
restore_flags (flags);
}
 
void check_status(void)
{
int i = inb_p(HD_STATUS);
 
if (!OK_STATUS(i)) {
dump_status("check_status", i);
bad_rw_intr();
}
}
 
static int controller_busy(void)
{
int retries = 100000;
unsigned char status;
 
do {
status = inb_p(HD_STATUS);
} while ((status & BUSY_STAT) && --retries);
return status;
}
 
static int status_ok(void)
{
unsigned char status = inb_p(HD_STATUS);
 
if (status & BUSY_STAT)
return 1; /* Ancient, but does it make sense??? */
if (status & WRERR_STAT)
return 0;
if (!(status & READY_STAT))
return 0;
if (!(status & SEEK_STAT))
return 0;
return 1;
}
 
static int controller_ready(unsigned int drive, unsigned int head)
{
int retry = 100;
 
do {
if (controller_busy() & BUSY_STAT)
return 0;
outb_p(0xA0 | (drive<<4) | head, HD_CURRENT);
if (status_ok())
return 1;
} while (--retry);
return 0;
}
 
static void hd_out(unsigned int drive,unsigned int nsect,unsigned int sect,
unsigned int head,unsigned int cyl,unsigned int cmd,
void (*intr_addr)(void))
{
unsigned short port;
 
#if (HD_DELAY > 0)
while (read_timer() - last_req < HD_DELAY)
/* nothing */;
#endif
if (reset)
return;
if (!controller_ready(drive, head)) {
reset = 1;
return;
}
SET_INTR(intr_addr);
outb_p(hd_info[drive].ctl,HD_CMD);
port=HD_DATA;
outb_p(hd_info[drive].wpcom>>2,++port);
outb_p(nsect,++port);
outb_p(sect,++port);
outb_p(cyl,++port);
outb_p(cyl>>8,++port);
outb_p(0xA0|(drive<<4)|head,++port);
outb_p(cmd,++port);
}
 
static void hd_request (void);
static unsigned int identified [MAX_HD]; /* 1 = drive ID already displayed */
static unsigned int unmask_intr [MAX_HD]; /* 1 = unmask IRQs during I/O */
static unsigned int max_mult [MAX_HD]; /* max sectors for MultMode */
static unsigned int mult_req [MAX_HD]; /* requested MultMode count */
static unsigned int mult_count [MAX_HD]; /* currently enabled MultMode count */
static struct request WCURRENT;
 
static void fixstring (unsigned char *s, int bytecount)
{
unsigned char *p, *end = &s[bytecount &= ~1]; /* bytecount must be even */
 
/* convert from big-endian to little-endian */
for (p = end ; p != s;) {
unsigned short *pp = (unsigned short *) (p -= 2);
*pp = (*pp >> 8) | (*pp << 8);
}
 
/* strip leading blanks */
while (s != end && *s == ' ')
++s;
 
/* compress internal blanks and strip trailing blanks */
while (s != end && *s) {
if (*s++ != ' ' || (s != end && *s && *s != ' '))
*p++ = *(s-1);
}
 
/* wipe out trailing garbage */
while (p != end)
*p++ = '\0';
}
 
static void identify_intr(void)
{
unsigned int dev = DEVICE_NR(CURRENT->rq_dev);
unsigned short stat = inb_p(HD_STATUS);
struct hd_driveid *id = hd_ident_info[dev];
 
if (unmask_intr[dev])
sti();
if (stat & (BUSY_STAT|ERR_STAT)) {
printk (" hd%c: non-IDE device, %dMB, CHS=%d/%d/%d\n", dev+'a',
hd_info[dev].cyl*hd_info[dev].head*hd_info[dev].sect / 2048,
hd_info[dev].cyl, hd_info[dev].head, hd_info[dev].sect);
if (id != NULL) {
hd_ident_info[dev] = NULL;
kfree_s (id, 512);
}
} else {
insw(HD_DATA, id, 256); /* get ID info */
max_mult[dev] = id->max_multsect;
if ((id->field_valid&1) && id->cur_cyls && id->cur_heads && (id->cur_heads <= 16) && id->cur_sectors) {
/*
* Extract the physical drive geometry for our use.
* Note that we purposely do *not* update the bios_info.
* This way, programs that use it (like fdisk) will
* still have the same logical view as the BIOS does,
* which keeps the partition table from being screwed.
*/
hd_info[dev].cyl = id->cur_cyls;
hd_info[dev].head = id->cur_heads;
hd_info[dev].sect = id->cur_sectors;
}
fixstring (id->serial_no, sizeof(id->serial_no));
fixstring (id->fw_rev, sizeof(id->fw_rev));
fixstring (id->model, sizeof(id->model));
printk (" hd%c: %.40s, %dMB w/%dKB Cache, CHS=%d/%d/%d, MaxMult=%d\n",
dev+'a', id->model, id->cyls*id->heads*id->sectors/2048,
id->buf_size/2, bios_info[dev].cyl, bios_info[dev].head,
bios_info[dev].sect, id->max_multsect);
/*
* Early model Quantum drives go weird at this point,
* but doing a recalibrate seems to "fix" them.
* (Doing a full reset confuses some other model Quantums)
*/
if (!strncmp(id->model, "QUANTUM", 7))
special_op[dev] = recalibrate[dev] = 1;
}
#if (HD_DELAY > 0)
last_req = read_timer();
#endif
hd_request();
return;
}
 
static void set_multmode_intr(void)
{
unsigned int dev = DEVICE_NR(CURRENT->rq_dev), stat = inb_p(HD_STATUS);
 
if (unmask_intr[dev])
sti();
if (stat & (BUSY_STAT|ERR_STAT)) {
mult_req[dev] = mult_count[dev] = 0;
dump_status("set multmode failed", stat);
} else {
if ((mult_count[dev] = mult_req[dev]))
printk (" hd%c: enabled %d-sector multiple mode\n",
dev+'a', mult_count[dev]);
else
printk (" hd%c: disabled multiple mode\n", dev+'a');
}
#if (HD_DELAY > 0)
last_req = read_timer();
#endif
hd_request();
return;
}
 
static int drive_busy(void)
{
unsigned int i;
unsigned char c;
 
for (i = 0; i < 500000 ; i++) {
c = inb_p(HD_STATUS);
if ((c & (BUSY_STAT | READY_STAT | SEEK_STAT)) == STAT_OK)
return 0;
}
dump_status("reset timed out", c);
return 1;
}
 
static void reset_controller(void)
{
int i;
 
outb_p(4,HD_CMD);
for(i = 0; i < 1000; i++) barrier();
outb_p(hd_info[0].ctl & 0x0f,HD_CMD);
for(i = 0; i < 1000; i++) barrier();
if (drive_busy())
printk("hd: controller still busy\n");
else if ((hd_error = inb(HD_ERROR)) != 1)
printk("hd: controller reset failed: %02x\n",hd_error);
}
 
static void reset_hd(void)
{
static int i;
 
repeat:
if (reset) {
reset = 0;
i = -1;
reset_controller();
} else {
check_status();
if (reset)
goto repeat;
}
if (++i < NR_HD) {
special_op[i] = recalibrate[i] = 1;
if (unmask_intr[i]) {
unmask_intr[i] = DEFAULT_UNMASK_INTR;
printk("hd%c: reset irq-unmasking to %d\n",i+'a',
DEFAULT_UNMASK_INTR);
}
if (mult_req[i] || mult_count[i]) {
mult_count[i] = 0;
mult_req[i] = DEFAULT_MULT_COUNT;
printk("hd%c: reset multiple mode to %d\n",i+'a',
DEFAULT_MULT_COUNT);
}
hd_out(i,hd_info[i].sect,hd_info[i].sect,hd_info[i].head-1,
hd_info[i].cyl,WIN_SPECIFY,&reset_hd);
if (reset)
goto repeat;
} else
hd_request();
}
 
/*
* Ok, don't know what to do with the unexpected interrupts: on some machines
* doing a reset and a retry seems to result in an eternal loop. Right now I
* ignore it, and just set the timeout.
*
* On laptops (and "green" PCs), an unexpected interrupt occurs whenever the
* drive enters "idle", "standby", or "sleep" mode, so if the status looks
* "good", we just ignore the interrupt completely.
*/
void unexpected_hd_interrupt(void)
{
unsigned int stat = inb_p(HD_STATUS);
 
if (stat & (BUSY_STAT|DRQ_STAT|ECC_STAT|ERR_STAT)) {
dump_status ("unexpected interrupt", stat);
SET_TIMER;
}
}
 
/*
* bad_rw_intr() now tries to be a bit smarter and does things
* according to the error returned by the controller.
* -Mika Liljeberg (liljeber@cs.Helsinki.FI)
*/
static void bad_rw_intr(void)
{
int dev;
 
if (!CURRENT)
return;
dev = DEVICE_NR(CURRENT->rq_dev);
if (++CURRENT->errors >= MAX_ERRORS || (hd_error & BBD_ERR)) {
end_request(0);
special_op[dev] = recalibrate[dev] = 1;
} else if (CURRENT->errors % RESET_FREQ == 0)
reset = 1;
else if ((hd_error & TRK0_ERR) || CURRENT->errors % RECAL_FREQ == 0)
special_op[dev] = recalibrate[dev] = 1;
/* Otherwise just retry */
}
 
static inline int wait_DRQ(void)
{
int retries = 100000, stat;
 
while (--retries > 0)
if ((stat = inb_p(HD_STATUS)) & DRQ_STAT)
return 0;
dump_status("wait_DRQ", stat);
return -1;
}
 
static void read_intr(void)
{
unsigned int dev = DEVICE_NR(CURRENT->rq_dev);
int i, retries = 100000, msect = mult_count[dev], nsect;
 
if (unmask_intr[dev])
sti(); /* permit other IRQs during xfer */
do {
i = (unsigned) inb_p(HD_STATUS);
if (i & BUSY_STAT)
continue;
if (!OK_STATUS(i))
break;
if (i & DRQ_STAT)
goto ok_to_read;
} while (--retries > 0);
dump_status("read_intr", i);
bad_rw_intr();
hd_request();
return;
ok_to_read:
if (msect) {
if ((nsect = CURRENT->current_nr_sectors) > msect)
nsect = msect;
msect -= nsect;
} else
nsect = 1;
insw(HD_DATA,CURRENT->buffer,nsect<<8);
CURRENT->sector += nsect;
CURRENT->buffer += nsect<<9;
CURRENT->errors = 0;
i = (CURRENT->nr_sectors -= nsect);
 
#ifdef DEBUG
printk("hd%c: read: sectors(%ld-%ld), remaining=%ld, buffer=0x%08lx\n",
dev+'a', CURRENT->sector, CURRENT->sector+nsect,
CURRENT->nr_sectors, (unsigned long) CURRENT->buffer+(nsect<<9));
#endif
if ((CURRENT->current_nr_sectors -= nsect) <= 0)
end_request(1);
if (i > 0) {
if (msect)
goto ok_to_read;
SET_INTR(&read_intr);
return;
}
(void) inb_p(HD_STATUS);
#if (HD_DELAY > 0)
last_req = read_timer();
#endif
if (CURRENT)
hd_request();
return;
}
 
static inline void multwrite (unsigned int dev)
{
unsigned int mcount = mult_count[dev];
 
while (mcount--) {
outsw(HD_DATA,WCURRENT.buffer,256);
if (!--WCURRENT.nr_sectors)
return;
WCURRENT.buffer += 512;
if (!--WCURRENT.current_nr_sectors) {
WCURRENT.bh = WCURRENT.bh->b_reqnext;
if (WCURRENT.bh == NULL)
panic("buffer list corrupted\n");
WCURRENT.current_nr_sectors = WCURRENT.bh->b_size>>9;
WCURRENT.buffer = WCURRENT.bh->b_data;
}
}
}
 
static void multwrite_intr(void)
{
int i;
unsigned int dev = DEVICE_NR(WCURRENT.rq_dev);
 
if (unmask_intr[dev])
sti();
if (OK_STATUS(i=inb_p(HD_STATUS))) {
if (i & DRQ_STAT) {
if (WCURRENT.nr_sectors) {
multwrite(dev);
SET_INTR(&multwrite_intr);
return;
}
} else {
if (!WCURRENT.nr_sectors) { /* all done? */
for (i = CURRENT->nr_sectors; i > 0;){
i -= CURRENT->current_nr_sectors;
end_request(1);
}
#if (HD_DELAY > 0)
last_req = read_timer();
#endif
if (CURRENT)
hd_request();
return;
}
}
}
dump_status("multwrite_intr", i);
bad_rw_intr();
hd_request();
}
 
static void write_intr(void)
{
int i;
int retries = 100000;
 
if (unmask_intr[DEVICE_NR(WCURRENT.rq_dev)])
sti();
do {
i = (unsigned) inb_p(HD_STATUS);
if (i & BUSY_STAT)
continue;
if (!OK_STATUS(i))
break;
if ((CURRENT->nr_sectors <= 1) || (i & DRQ_STAT))
goto ok_to_write;
} while (--retries > 0);
dump_status("write_intr", i);
bad_rw_intr();
hd_request();
return;
ok_to_write:
CURRENT->sector++;
i = --CURRENT->nr_sectors;
--CURRENT->current_nr_sectors;
CURRENT->buffer += 512;
if (!i || (CURRENT->bh && !SUBSECTOR(i)))
end_request(1);
if (i > 0) {
SET_INTR(&write_intr);
outsw(HD_DATA,CURRENT->buffer,256);
sti();
} else {
#if (HD_DELAY > 0)
last_req = read_timer();
#endif
hd_request();
}
return;
}
 
static void recal_intr(void)
{
check_status();
#if (HD_DELAY > 0)
last_req = read_timer();
#endif
hd_request();
}
 
/*
* This is another of the error-routines I don't know what to do with. The
* best idea seems to just set reset, and start all over again.
*/
static void hd_times_out(void)
{
unsigned int dev;
 
DEVICE_INTR = NULL;
if (!CURRENT)
return;
disable_irq(HD_IRQ);
sti();
reset = 1;
dev = DEVICE_NR(CURRENT->rq_dev);
printk("hd%c: timeout\n", dev+'a');
if (++CURRENT->errors >= MAX_ERRORS) {
#ifdef DEBUG
printk("hd%c: too many errors\n", dev+'a');
#endif
end_request(0);
}
cli();
hd_request();
enable_irq(HD_IRQ);
}
 
int do_special_op (unsigned int dev)
{
if (recalibrate[dev]) {
recalibrate[dev] = 0;
hd_out(dev,hd_info[dev].sect,0,0,0,WIN_RESTORE,&recal_intr);
return reset;
}
if (!identified[dev]) {
identified[dev] = 1;
unmask_intr[dev] = DEFAULT_UNMASK_INTR;
mult_req[dev] = DEFAULT_MULT_COUNT;
hd_out(dev,0,0,0,0,WIN_IDENTIFY,&identify_intr);
return reset;
}
if (mult_req[dev] != mult_count[dev]) {
hd_out(dev,mult_req[dev],0,0,0,WIN_SETMULT,&set_multmode_intr);
return reset;
}
if (hd_info[dev].head > 16) {
printk ("hd%c: cannot handle device with more than 16 heads - giving up\n", dev+'a');
end_request(0);
}
special_op[dev] = 0;
return 1;
}
 
/*
* The driver enables interrupts as much as possible. In order to do this,
* (a) the device-interrupt is disabled before entering hd_request(),
* and (b) the timeout-interrupt is disabled before the sti().
*
* Interrupts are still masked (by default) whenever we are exchanging
* data/cmds with a drive, because some drives seem to have very poor
* tolerance for latency during I/O. For devices which don't suffer from
* that problem (most don't), the unmask_intr[] flag can be set to unmask
* other interrupts during data/cmd transfers (by defining DEFAULT_UNMASK_INTR
* to 1, or by using "hdparm -u1 /dev/hd?" from the shell).
*/
static void hd_request(void)
{
unsigned int dev, block, nsect, sec, track, head, cyl;
 
if (CURRENT && CURRENT->rq_status == RQ_INACTIVE) return;
if (DEVICE_INTR)
return;
repeat:
timer_active &= ~(1<<HD_TIMER);
sti();
INIT_REQUEST;
if (reset) {
cli();
reset_hd();
return;
}
dev = MINOR(CURRENT->rq_dev);
block = CURRENT->sector;
nsect = CURRENT->nr_sectors;
if (dev >= (NR_HD<<6) || block >= hd[dev].nr_sects || ((block+nsect) > hd[dev].nr_sects)) {
#ifdef DEBUG
if (dev >= (NR_HD<<6))
printk("hd: bad minor number: device=%s\n",
kdevname(CURRENT->rq_dev));
else
printk("hd%c: bad access: block=%d, count=%d\n",
(MINOR(CURRENT->rq_dev)>>6)+'a', block, nsect);
#endif
end_request(0);
goto repeat;
}
block += hd[dev].start_sect;
dev >>= 6;
if (special_op[dev]) {
if (do_special_op(dev))
goto repeat;
return;
}
sec = block % hd_info[dev].sect + 1;
track = block / hd_info[dev].sect;
head = track % hd_info[dev].head;
cyl = track / hd_info[dev].head;
#ifdef DEBUG
printk("hd%c: %sing: CHS=%d/%d/%d, sectors=%d, buffer=0x%08lx\n",
dev+'a', (CURRENT->cmd == READ)?"read":"writ",
cyl, head, sec, nsect, (unsigned long) CURRENT->buffer);
#endif
if (!unmask_intr[dev])
cli();
if (CURRENT->cmd == READ) {
unsigned int cmd = mult_count[dev] > 1 ? WIN_MULTREAD : WIN_READ;
hd_out(dev,nsect,sec,head,cyl,cmd,&read_intr);
if (reset)
goto repeat;
return;
}
if (CURRENT->cmd == WRITE) {
if (mult_count[dev])
hd_out(dev,nsect,sec,head,cyl,WIN_MULTWRITE,&multwrite_intr);
else
hd_out(dev,nsect,sec,head,cyl,WIN_WRITE,&write_intr);
if (reset)
goto repeat;
if (wait_DRQ()) {
bad_rw_intr();
goto repeat;
}
if (mult_count[dev]) {
WCURRENT = *CURRENT;
multwrite(dev);
} else
outsw(HD_DATA,CURRENT->buffer,256);
return;
}
panic("unknown hd-command");
}
 
static void do_hd_request (void)
{
disable_irq(HD_IRQ);
hd_request();
enable_irq(HD_IRQ);
}
 
static int hd_ioctl(struct inode * inode, struct file * file,
unsigned int cmd, unsigned long arg)
{
struct hd_geometry *loc = (struct hd_geometry *) arg;
int dev, err;
unsigned long flags;
 
if ((!inode) || !(inode->i_rdev))
return -EINVAL;
dev = DEVICE_NR(inode->i_rdev);
if (dev >= NR_HD)
return -EINVAL;
switch (cmd) {
case HDIO_GETGEO:
if (!loc) return -EINVAL;
err = verify_area(VERIFY_WRITE, loc, sizeof(*loc));
if (err)
return err;
put_user(bios_info[dev].head,
(char *) &loc->heads);
put_user(bios_info[dev].sect,
(char *) &loc->sectors);
put_user(bios_info[dev].cyl,
(short *) &loc->cylinders);
put_user(hd[MINOR(inode->i_rdev)].start_sect,
(long *) &loc->start);
return 0;
case BLKRASET:
if(!suser()) return -EACCES;
if(arg > 0xff) return -EINVAL;
read_ahead[MAJOR(inode->i_rdev)] = arg;
return 0;
case BLKRAGET:
if (!arg) return -EINVAL;
err = verify_area(VERIFY_WRITE, (long *) arg, sizeof(long));
if (err)
return err;
put_user(read_ahead[MAJOR(inode->i_rdev)],(long *) arg);
return 0;
case BLKGETSIZE: /* Return device size */
if (!arg) return -EINVAL;
err = verify_area(VERIFY_WRITE, (long *) arg, sizeof(long));
if (err)
return err;
put_user(hd[MINOR(inode->i_rdev)].nr_sects, (long *) arg);
return 0;
case BLKFLSBUF:
if(!suser()) return -EACCES;
fsync_dev(inode->i_rdev);
invalidate_buffers(inode->i_rdev);
return 0;
 
case BLKRRPART: /* Re-read partition tables */
return revalidate_hddisk(inode->i_rdev, 1);
 
case HDIO_SET_UNMASKINTR:
if (!suser()) return -EACCES;
if ((arg > 1) || (MINOR(inode->i_rdev) & 0x3F))
return -EINVAL;
unmask_intr[dev] = arg;
return 0;
 
case HDIO_GET_UNMASKINTR:
if (!arg) return -EINVAL;
err = verify_area(VERIFY_WRITE, (long *) arg, sizeof(long));
if (err)
return err;
put_user(unmask_intr[dev], (long *) arg);
return 0;
 
case HDIO_GET_MULTCOUNT:
if (!arg) return -EINVAL;
err = verify_area(VERIFY_WRITE, (long *) arg, sizeof(long));
if (err)
return err;
put_user(mult_count[dev], (long *) arg);
return 0;
 
case HDIO_SET_MULTCOUNT:
if (!suser()) return -EACCES;
if (MINOR(inode->i_rdev) & 0x3F) return -EINVAL;
save_flags(flags);
cli(); /* a prior request might still be in progress */
if (arg > max_mult[dev])
err = -EINVAL; /* out of range for device */
else if (mult_req[dev] != mult_count[dev]) {
special_op[dev] = 1;
err = -EBUSY; /* busy, try again */
} else {
mult_req[dev] = arg;
special_op[dev] = 1;
err = 0;
}
restore_flags(flags);
return err;
 
case HDIO_GET_IDENTITY:
if (!arg) return -EINVAL;
if (MINOR(inode->i_rdev) & 0x3F) return -EINVAL;
if (hd_ident_info[dev] == NULL) return -ENOMSG;
err = verify_area(VERIFY_WRITE, (char *) arg, sizeof(struct hd_driveid));
if (err)
return err;
memcpy_tofs((char *)arg, (char *) hd_ident_info[dev], sizeof(struct hd_driveid));
return 0;
 
RO_IOCTLS(inode->i_rdev,arg);
default:
return -EINVAL;
}
}
 
static int hd_open(struct inode * inode, struct file * filp)
{
int target;
target = DEVICE_NR(inode->i_rdev);
 
if (target >= NR_HD)
return -ENODEV;
while (busy[target])
sleep_on(&busy_wait);
access_count[target]++;
return 0;
}
 
/*
* Releasing a block device means we sync() it, so that it can safely
* be forgotten about...
*/
static void hd_release(struct inode * inode, struct file * file)
{
int target;
sync_dev(inode->i_rdev);
 
target = DEVICE_NR(inode->i_rdev);
access_count[target]--;
 
}
 
static void hd_geninit(struct gendisk *);
 
static struct gendisk hd_gendisk = {
MAJOR_NR, /* Major number */
"hd", /* Major name */
6, /* Bits to shift to get real from partition */
1 << 6, /* Number of partitions per real */
MAX_HD, /* maximum number of real */
hd_geninit, /* init function */
hd, /* hd struct */
hd_sizes, /* block sizes */
0, /* number */
(void *) bios_info, /* internal */
NULL /* next */
};
static void hd_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
void (*handler)(void) = DEVICE_INTR;
 
DEVICE_INTR = NULL;
timer_active &= ~(1<<HD_TIMER);
if (!handler)
handler = unexpected_hd_interrupt;
handler();
sti();
}
 
/*
* Since we find out the physical drive geometry, we don't touch that.
* We only alter the logical disk geometry that is passed to user programs.
* [as per PC Linux].
*/
void hd_set_geometry (kdev_t dev, unsigned char secspertrack, unsigned char heads,
unsigned long discsize, unsigned int secsize)
{
int minor = MINOR(dev);
int drv = minor >> 6;
 
if (bios_info[drv].cyl == 1) {
bios_info[drv].cyl = discsize / (secspertrack * heads * secsize);
bios_info[drv].head = heads;
bios_info[drv].wpcom = -1;
bios_info[drv].ctl = 8;
bios_info[drv].lzone = bios_info[drv].cyl - 1;
bios_info[drv].sect = secspertrack;
}
hd[minor].start_sect = 0;
hd[minor].nr_sects = discsize / secsize;
}
 
/*
* This is the harddisk IRQ description. The SA_INTERRUPT in sa_flags
* means we run the IRQ-handler with interrupts disabled: this is bad for
* interrupt latency, but anything else has led to problems on some
* machines...
*
* We enable interrupts in some of the routines after making sure it's
* safe.
*/
static void hd_geninit(struct gendisk *ignored)
{
int i;
 
if (!NR_HD) {
int drive;
extern int number_ide_drives;
/*
* Default settings
*
* If we don't know anything about the drive, then set it
* so that we have enough to read the boot sector of the
* ADFS drive. This means that you *MUST* specify the
* drive parameters of *all* drives if you have one IDE
* drive that is not ADFS formatted.
*/
for (drive=0 ; drive<2 ; drive++) {
bios_info[drive].cyl = hd_info[drive].cyl = 1;
bios_info[drive].head = hd_info[drive].head = 1;
bios_info[drive].wpcom = hd_info[drive].wpcom = -1;
bios_info[drive].ctl = hd_info[drive].ctl = 8;
bios_info[drive].lzone = hd_info[drive].lzone = 1;
bios_info[drive].sect = hd_info[drive].sect = 17;
}
 
/*
* We only set this to the one that the host OS gave us
* if the user has not defined any types.
*/
NR_HD = number_ide_drives;
}
 
i = NR_HD;
while (i-- > 0) {
/*
* The newer E-IDE BIOSs handle drives larger than 1024
* cylinders by increasing the number of logical heads
* to keep the number of logical cylinders below the
* sacred INT13 limit of 1024 (10 bits). If that is
* what's happening here, we'll find out and correct
* it later when "identifying" the drive.
*/
hd[i<<6].nr_sects = bios_info[i].head *
bios_info[i].sect * bios_info[i].cyl;
hd_ident_info[i] = (struct hd_driveid *) kmalloc(512,GFP_KERNEL);
special_op[i] = 1;
}
if (NR_HD) {
if (request_irq(HD_IRQ, hd_interrupt, SA_INTERRUPT, "hd", NULL)) {
printk("hd: unable to get IRQ%d for the harddisk driver\n",HD_IRQ);
NR_HD = 0;
} else {
request_region(HD_DATA, 8, "hd");
request_region(HD_CMD, 1, "hd(cmd)");
}
}
hd_gendisk.nr_real = NR_HD;
 
for (i = 0; i < (MAX_HD << 6); i++) {
hd_blocksizes[i] = 1024;
hd_hardsectsizes[i] = 512;
}
blksize_size[MAJOR_NR] = hd_blocksizes;
hardsect_size[MAJOR_NR] = hd_hardsectsizes;
}
 
static struct file_operations hd_fops = {
NULL, /* lseek - default */
block_read, /* read - general block-dev read */
block_write, /* write - general block-dev write */
NULL, /* readdir - bad */
NULL, /* select */
hd_ioctl, /* ioctl */
NULL, /* mmap */
hd_open, /* open */
hd_release, /* release */
block_fsync /* fsync */
};
 
int hd_init(void)
{
if (register_blkdev(MAJOR_NR,"hd",&hd_fops)) {
printk("hd: unable to get major %d for harddisk\n",MAJOR_NR);
return -1;
}
blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
read_ahead[MAJOR_NR] = 8; /* 8 sector (4kB) read-ahead */
hd_gendisk.next = gendisk_head;
gendisk_head = &hd_gendisk;
timer_table[HD_TIMER].fn = hd_times_out;
return 0;
}
 
#define DEVICE_BUSY busy[target]
#define USAGE access_count[target]
#define CAPACITY (bios_info[target].head*bios_info[target].sect*bios_info[target].cyl)
/* We assume that the the bios parameters do not change, so the disk capacity
will not change */
#undef MAYBE_REINIT
#define GENDISK_STRUCT hd_gendisk
 
/*
* This routine is called to flush all partitions and partition tables
* for a changed scsi disk, and then re-read the new partition table.
* If we are revalidating a disk because of a media change, then we
* enter with usage == 0. If we are using an ioctl, we automatically have
* usage == 1 (we need an open channel to use an ioctl :-), so this
* is our limit.
*/
static int revalidate_hddisk(kdev_t dev, int maxusage)
{
int target;
struct gendisk * gdev;
int max_p;
int start;
int i;
long flags;
 
target = DEVICE_NR(dev);
gdev = &GENDISK_STRUCT;
 
save_flags_cli (flags);
if (DEVICE_BUSY || USAGE > maxusage) {
restore_flags(flags);
return -EBUSY;
};
DEVICE_BUSY = 1;
restore_flags(flags);
 
max_p = gdev->max_p;
start = target << gdev->minor_shift;
 
for (i=max_p - 1; i >=0 ; i--) {
int minor = start + i;
kdev_t devi = MKDEV(MAJOR_NR, minor);
sync_dev(devi);
invalidate_inodes(devi);
invalidate_buffers(devi);
gdev->part[minor].start_sect = 0;
gdev->part[minor].nr_sects = 0;
};
 
#ifdef MAYBE_REINIT
MAYBE_REINIT;
#endif
 
gdev->part[start].nr_sects = CAPACITY;
resetup_one_dev(gdev, target);
 
DEVICE_BUSY = 0;
wake_up(&busy_wait);
return 0;
}
 
/mfmhd.c
0,0 → 1,1539
/*
* linux/arch/arm/drivers/block/mfmhd.c
*
* Copyright (C) 1995, 1996 Russell King, Dave Alan Gilbert (linux@treblig.org)
*
* MFM hard drive code [experimental]
*/
 
/*
* Change list:
*
* 3/2/96:DAG: Started a change list :-)
* Set the hardsect_size pointers up since we are running 256 byte
* sectors
* Added DMA code, put it into the rw_intr
* Moved RCAL out of generic interrupt code - don't want to do it
* while DMA'ing - its now in individual handlers.
* Took interrupt handlers off task queue lists and called
* directly - not sure of implications.
*
* 18/2/96:DAG: Well its reading OK I think, well enough for image file code
* to find the image file; but now I've discovered that I actually
* have to put some code in for image files.
*
* Added stuff for image files; seems to work, but I've not
* got a multisegment image file (I don't think!).
* Put in a hack (yep a real hack) for multiple cylinder reads.
* Not convinced its working.
*
* 5/4/96:DAG: Added asm/hardware.h and use IOC_ macros
* Rewrote dma code in mfm.S (again!) - now takes a word at a time
* from main RAM for speed; still doesn't feel speedy!
*
* 20/4/96:DAG: After rewriting mfm.S a heck of a lot of times and speeding
* things up, I've finally figured out why its so damn slow.
* Linux is only reading a block at a time, and so you never
* get more than 1K per disc revoloution ~=60K/second.
*
* 27/4/96:DAG: On Russell's advice I change ll_rw_blk.c to ask it to
* join adjacent blocks together. Everything falls flat on its
* face.
* Four hours of debugging later; I hadn't realised that
* ll_rw_blk would be so generous as to join blocks whose
* results aren't going into consecutive buffers.
*
* OK; severe rehacking of mfm_rw_interrupt; now end_request's
* as soon as its DMA'd each request. Odd thing is that
* we are sometimes getting interrupts where we are not transferring
* any data; why? Is that what happens when you miss? I doubt
* it; are we too fast? No - its just at command ends. Got 240K/s
* better than before, but RiscOS hits 480K/s
*
* 25/6/96:RMK: Fixed init code to allow the MFM podule to work. Increased the
* number of errors for my Miniscribe drive (8425).
*
* 30/6/96:DAG: Russell suggested that a check drive 0 might turn the LEDs off
* - so in request_done just before it clears Busy it sends a
* check drive 0 - and the LEDs go off!!!!
*
* Added test for mainboard controller. - Removes need for separate
* define.
*
* 13/7/96:DAG: Changed hardware sectore size to 512 in attempt to make
* IM drivers work.
* 21/7/96:DAG: Took out old image file stuff (accessing it now produces an IO
* error.)
*
* 17/8/96:DAG: Ran through indent -kr -i8; evil - all my nice 2 character indents
* gone :-( Hand modified afterwards.
* Took out last remains of the older image map system.
*
* 22/9/96:DAG: Changed mfm.S so it will carry on DMA'ing til; BSY is dropped
* Changed mfm_rw_intr so that it doesn't follow the error
* code until BSY is dropped. Nope - still broke. Problem
* may revolve around when it reads the results for the error
* number?
*
*16/11/96:DAG: Modified for 2.0.18; request_irq changed
*
*17/12/96:RMK: Various cleanups, reorganisation, and the changes for new IO system.
* Improved probe for onboard MFM chip - it was hanging on my A5k.
* Added autodetect CHS code such that we don't rely on the presence
* of an ADFS boot block. Added ioport resource manager calls so
* that we don't clash with already-running hardware (eg. RiscPC Ether
* card slots if someone tries this)!
*/
 
/*
* Possible enhancements:
* Multi-thread the code so that it is possible that while one drive
* is seeking, the other one can be reading data/seeking as well.
* This would be a performance boost with dual drive systems.
*/
 
#include <linux/module.h>
#include <linux/config.h>
#include <linux/sched.h>
#include <linux/fs.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/timer.h>
#include <linux/tqueue.h>
#include <linux/mm.h>
#include <linux/errno.h>
#include <linux/genhd.h>
#include <linux/major.h>
#include <linux/ioport.h>
 
#include <asm/system.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/segment.h>
#include <asm/delay.h>
#include <asm/dma.h>
#include <asm/hardware.h>
#include <asm/ecard.h>
 
#define MFM_DISK_MAJOR 13
#undef XT_DISK_MAJOR
#define XT_DISK_MAJOR -1
#define MAJOR_NR MFM_DISK_MAJOR
#include "blk.h"
 
/*
* This sort of stuff should be in a header file shared with ide.c, hd.c, xd.c etc
*/
#ifndef HDIO_GETGEO
#define HDIO_GETGEO 0x301
struct hd_geometry {
unsigned char heads;
unsigned char sectors;
unsigned short cylinders;
unsigned long start;
};
#endif
 
 
/*
* Configuration section
*
* Enable MFM_AUTODETECT_DRIVES to allow the code to find out the real CHS
* for the drive.
*/
#define MFM_AUTODETECT_DRIVES
/*
* This is the maximum number of drives that we accept
*/
#define MFM_MAXDRIVES 2
/*
* Linux I/O address of onboard MFM controller or 0 to disable this
*/
#define ONBOARD_MFM_ADDRESS ((0x002d0000 >> 2) | 0x80000000)
/*
* Uncomment this to enable debugging in the MFM driver...
*/
#ifndef DEBUG
/*#define DEBUG */
#endif
/*
* List of card types that we recognise
*/
static const card_ids mfm_cids[] = {
{ MANU_ACORN, PROD_ACORN_MFM },
{ 0xffff, 0xffff }
};
/*
* End of configuration
*/
 
/*
* This structure contains all information to do with a particular physical
* device.
*/
struct mfm_info {
unsigned char sectors;
unsigned char heads;
unsigned short cylinders;
unsigned short lowcurrent;
unsigned short precomp;
#define NO_TRACK -1
#define NEED_1_RECAL -2
#define NEED_2_RECAL -3
int cylinder;
unsigned int access_count;
unsigned int busy;
struct {
char recal;
char report;
char abort;
} errors;
} mfm_info[MFM_MAXDRIVES];
 
#define MFM_DRV_INFO mfm_info[raw_cmd.dev]
 
static struct hd_struct mfm[MFM_MAXDRIVES << 6];
static int mfm_sizes[MFM_MAXDRIVES << 6];
static int mfm_blocksizes[MFM_MAXDRIVES << 6];
static int mfm_sectsizes[MFM_MAXDRIVES << 6];
static struct wait_queue *mfm_wait_open = NULL;
 
/* Stuff from the assembly routines */
extern unsigned int hdc63463_baseaddress; /* Controller base address */
extern unsigned int hdc63463_irqpolladdress; /* Address to read to test for int */
extern unsigned int hdc63463_irqpollmask; /* Mask for irq register */
extern unsigned int hdc63463_dataptr; /* Pointer to kernel data space to DMA */
extern int hdc63463_dataleft; /* Number of bytes left to transfer */
 
 
 
 
static int lastspecifieddrive;
static unsigned Busy;
 
static unsigned int PartFragRead; /* The number of sectors which have been read
during a partial read split over two
cylinders. If 0 it means a partial
read did not occur. */
 
static unsigned int PartFragRead_RestartBlock; /* Where to restart on a split access */
static unsigned int PartFragRead_SectorsLeft; /* Where to restart on a split access */
 
static int Sectors256LeftInCurrent; /* i.e. 256 byte sectors left in current */
static int SectorsLeftInRequest; /* i.e. blocks left in the thing mfm_request was called for */
static int Copy_Sector; /* The 256 byte sector we are currently at - fragments need to know
where to take over */
static char *Copy_buffer;
 
 
static void mfm_seek(void);
static void mfm_rerequest(void);
static void mfm_request(void);
static int mfm_reread_partitions(kdev_t dev);
static void mfm_specify (void);
static void issue_request(int dev, unsigned int block, unsigned int nsect,
struct request *req);
 
#define mfm_init xd_init
#define mfm_setup xd_setup
 
static unsigned int mfm_addr; /* Controller address */
static unsigned int mfm_IRQPollLoc; /* Address to read for IRQ information */
static unsigned int mfm_irqenable; /* Podule IRQ enable location */
static unsigned char mfm_irq; /* Interrupt number */
static int mfm_drives = 0; /* drives available */
static int mfm_status = 0; /* interrupt status */
static int *errors;
 
static struct rawcmd {
unsigned int dev;
unsigned int cylinder;
unsigned int head;
unsigned int sector;
unsigned int cmdtype;
unsigned int cmdcode;
unsigned char cmddata[16];
unsigned int cmdlen;
} raw_cmd;
 
static unsigned char result[16];
 
static struct cont {
void (*interrupt) (void); /* interrupt handler */
void (*error) (void); /* error handler */
void (*redo) (void); /* redo handler */
void (*done) (int st); /* done handler */
} *cont = NULL;
 
static struct tq_struct mfm_tq = {0, 0, (void (*)(void *)) NULL, 0};
 
int number_mfm_drives = 1;
 
/* ------------------------------------------------------------------------------------------ */
/*
* From the HD63463 data sheet from Hitachi Ltd.
*/
 
#define MFM_COMMAND (mfm_addr + 0)
#define MFM_DATAOUT (mfm_addr + 1)
#define MFM_STATUS (mfm_addr + 8)
#define MFM_DATAIN (mfm_addr + 9)
 
#define CMD_ABT 0xF0 /* Abort */
#define CMD_SPC 0xE8 /* Specify */
#define CMD_TST 0xE0 /* Test */
#define CMD_RCLB 0xC8 /* Recalibrate */
#define CMD_SEK 0xC0 /* Seek */
#define CMD_WFS 0xAB /* Write Format Skew */
#define CMD_WFM 0xA3 /* Write Format */
#define CMD_MTB 0x90 /* Memory to buffer */
#define CMD_CMPD 0x88 /* Compare data */
#define CMD_WD 0x87 /* Write data */
#define CMD_RED 0x70 /* Read erroneous data */
#define CMD_RIS 0x68 /* Read ID skew */
#define CMD_FID 0x61 /* Find ID */
#define CMD_RID 0x60 /* Read ID */
#define CMD_BTM 0x50 /* Buffer to memory */
#define CMD_CKD 0x48 /* Check data */
#define CMD_RD 0x40 /* Read data */
#define CMD_OPBW 0x38 /* Open buffer write */
#define CMD_OPBR 0x30 /* Open buffer read */
#define CMD_CKV 0x28 /* Check drive */
#define CMD_CKE 0x20 /* Check ECC */
#define CMD_POD 0x18 /* Polling disable */
#define CMD_POL 0x10 /* Polling enable */
#define CMD_RCAL 0x08 /* Recall */
 
#define STAT_BSY 0x8000 /* Busy */
#define STAT_CPR 0x4000 /* Command Parameter Rejection */
#define STAT_CED 0x2000 /* Command end */
#define STAT_SED 0x1000 /* Seek end */
#define STAT_DER 0x0800 /* Drive error */
#define STAT_ABN 0x0400 /* Abnormal end */
#define STAT_POL 0x0200 /* Polling */
 
/* ------------------------------------------------------------------------------------------ */
#ifdef DEBUG
static void console_printf(const char *fmt,...)
{
static char buffer[2048]; /* Arbitary! */
extern void console_print(const char *);
unsigned long flags;
va_list ap;
 
save_flags_cli(flags);
 
va_start(ap, fmt);
vsprintf(buffer, fmt, ap);
console_print(buffer);
va_end(fmt);
 
restore_flags(flags);
}; /* console_printf */
 
#define DBG(x...) console_printf(x)
#else
#define DBG(x...)
#endif
 
static void print_status(void)
{
char *error;
static char *errors[] = {
"no error",
"command aborted",
"invalid command",
"parameter error",
"not initialised",
"rejected TEST",
"no useld",
"write fault",
"not ready",
"no scp",
"in seek",
"invalid NCA",
"invalid step rate",
"seek error",
"over run",
"invalid PHA",
"data field EEC error",
"data field CRC error",
"error corrected",
"data field fatal error",
"no data am",
"not hit",
"ID field CRC error",
"time over",
"no ID am",
"not writable"
};
if (result[1] < 0x65)
error = errors[result[1] >> 2];
else
error = "unknown";
printk("(");
if (mfm_status & STAT_BSY) printk("BSY ");
if (mfm_status & STAT_CPR) printk("CPR ");
if (mfm_status & STAT_CED) printk("CED ");
if (mfm_status & STAT_SED) printk("SED ");
if (mfm_status & STAT_DER) printk("DER ");
if (mfm_status & STAT_ABN) printk("ABN ");
if (mfm_status & STAT_POL) printk("POL ");
printk(") SSB = %X (%s)\n", result[1], error);
 
}
 
/* ------------------------------------------------------------------------------------- */
 
static void issue_command(int command, unsigned char *cmdb, int len)
{
int status;
#ifdef DEBUG
int i;
console_printf("issue_command: %02X: ", command);
for (i = 0; i < len; i++)
console_printf("%02X ", cmdb[i]);
console_printf("\n");
#endif
 
do {
status = inw(MFM_STATUS);
} while (status & (STAT_BSY | STAT_POL));
DBG("issue_command: status after pol/bsy loop: %02X:\n ", status >> 8);
 
if (status & (STAT_CPR | STAT_CED | STAT_SED | STAT_DER | STAT_ABN)) {
outw(CMD_RCAL, MFM_COMMAND);
while (inw(MFM_STATUS) & STAT_BSY);
}
status = inw(MFM_STATUS);
DBG("issue_command: status before parameter issue: %02X:\n ", status >> 8);
 
while (len > 0) {
outw(cmdb[1] | (cmdb[0] << 8), MFM_DATAOUT);
len -= 2;
cmdb += 2;
}
status = inw(MFM_STATUS);
DBG("issue_command: status before command issue: %02X:\n ", status >> 8);
 
outw(command, MFM_COMMAND);
status = inw(MFM_STATUS);
DBG("issue_command: status immediatly after command issue: %02X:\n ", status >> 8);
}
 
static void wait_for_completion(void)
{
while ((mfm_status = inw(MFM_STATUS)) & STAT_BSY);
}
 
static void wait_for_command_end(void)
{
int i;
 
while (!((mfm_status = inw(MFM_STATUS)) & STAT_CED));
 
for (i = 0; i < 16;) {
int in;
in = inw(MFM_DATAIN);
result[i++] = in >> 8;
result[i++] = in;
}
outw (CMD_RCAL, MFM_COMMAND);
}
 
/* ------------------------------------------------------------------------------------- */
 
static void mfm_rw_intr(void)
{
int old_status; /* Holds status on entry, we read to see if the command just finished */
#ifdef DEBUG
console_printf("mfm_rw_intr...dataleft=%d\n", hdc63463_dataleft);
print_status();
#endif
 
/* Now don't handle the error until BSY drops */
if ((mfm_status & (STAT_DER | STAT_ABN)) && ((mfm_status&STAT_BSY)==0)) {
/* Something has gone wrong - lets try that again */
outw(CMD_RCAL, MFM_COMMAND); /* Clear interrupt condition */
if (cont) {
DBG("mfm_rw_intr: DER/ABN err\n");
cont->error();
cont->redo();
};
return;
};
 
/* OK so what ever happend its not an error, now I reckon we are left between
a choice of command end or some data which is ready to be collected */
/* I think we have to transfer data while the interrupt line is on and its
not any other type of interrupt */
if (CURRENT->cmd == WRITE) {
extern void hdc63463_writedma(void);
if ((hdc63463_dataleft <= 0) && (!(mfm_status & STAT_CED))) {
printk("mfm_rw_intr: Apparent DMA write request when no more to DMA\n");
if (cont) {
cont->error();
cont->redo();
};
return;
};
hdc63463_writedma();
} else {
extern void hdc63463_readdma(void);
if ((hdc63463_dataleft <= 0) && (!(mfm_status & STAT_CED))) {
printk("mfm_rw_intr: Apparent DMA read request when no more to DMA\n");
if (cont) {
cont->error();
cont->redo();
};
return;
};
DBG("Going to try read dma..............status=0x%x, buffer=%p\n", mfm_status, hdc63463_dataptr);
hdc63463_readdma();
}; /* Read */
 
if (hdc63463_dataptr != ((unsigned int) Copy_buffer + 256)) {
/* If we didn't actually manage to get any data on this interrupt - but why? We got the interrupt */
/* Ah - well looking at the status its just when we get command end; so no problem */
/*console_printf("mfm: dataptr mismatch. dataptr=0x%08x Copy_buffer+256=0x%08p\n",
hdc63463_dataptr,Copy_buffer+256);
print_status(); */
} else {
Sectors256LeftInCurrent--;
Copy_buffer += 256;
Copy_Sector++;
 
/* We have come to the end of this request */
if (!Sectors256LeftInCurrent) {
DBG("mfm: end_request for CURRENT=0x%p CURRENT(sector=%d current_nr_sectors=%d nr_sectors=%d)\n",
CURRENT, CURRENT->sector, CURRENT->current_nr_sectors, CURRENT->nr_sectors);
 
CURRENT->nr_sectors -= CURRENT->current_nr_sectors;
CURRENT->sector += CURRENT->current_nr_sectors;
SectorsLeftInRequest -= CURRENT->current_nr_sectors;
 
end_request(1);
if (SectorsLeftInRequest) {
hdc63463_dataptr = (unsigned int) CURRENT->buffer;
Copy_buffer = CURRENT->buffer;
Sectors256LeftInCurrent = CURRENT->current_nr_sectors * 2;
errors = &(CURRENT->errors);
/* These should match the present calculations of the next logical sector
on the device
Copy_Sector=CURRENT->sector*2; */
 
if (Copy_Sector != CURRENT->sector * 2)
#ifdef DEBUG
/*console_printf*/printk("mfm: Copy_Sector mismatch. Copy_Sector=%d CURRENT->sector*2=%d\n",
Copy_Sector, CURRENT->sector * 2);
#else
printk("mfm: Copy_Sector mismatch! Eek!\n");
#endif
}; /* CURRENT */
}; /* Sectors256LeftInCurrent */
};
 
old_status = mfm_status;
mfm_status = inw(MFM_STATUS);
if (mfm_status & (STAT_DER | STAT_ABN)) {
/* Something has gone wrong - lets try that again */
if (cont) {
DBG("mfm_rw_intr: DER/ABN error\n");
cont->error();
cont->redo();
};
return;
};
 
/* If this code wasn't entered due to command_end but there is
now a command end we must read the command results out. If it was
entered like this then mfm_interrupt_handler would have done the
job. */
if ((!((old_status & (STAT_CPR | STAT_BSY)) == STAT_CPR)) &&
((mfm_status & (STAT_CPR | STAT_BSY)) == STAT_CPR)) {
int len = 0;
while (len < 16) {
int in;
in = inw(MFM_DATAIN);
result[len++] = in >> 8;
result[len++] = in;
};
}; /* Result read */
 
/*console_printf ("mfm_rw_intr nearexit [%02X]\n", inb(mfm_IRQPollLoc)); */
 
/* If end of command move on */
if (mfm_status & (STAT_CED)) {
outw(CMD_RCAL, MFM_COMMAND); /* Clear interrupt condition */
/* End of command - trigger the next command */
if (cont) {
cont->done(1);
}
DBG("mfm_rw_intr: returned from cont->done\n");
} else {
/* Its going to generate another interrupt */
SET_INTR(mfm_rw_intr);
};
}
 
static void mfm_setup_rw(void)
{
DBG("setting up for rw...\n");
 
SET_INTR(mfm_rw_intr);
issue_command(raw_cmd.cmdcode, raw_cmd.cmddata, raw_cmd.cmdlen);
}
 
static void mfm_recal_intr(void)
{
#ifdef DEBUG
console_printf("recal intr - status = ");
print_status();
#endif
outw(CMD_RCAL, MFM_COMMAND); /* Clear interrupt condition */
if (mfm_status & (STAT_DER | STAT_ABN)) {
printk("recal failed\n");
MFM_DRV_INFO.cylinder = NEED_2_RECAL;
if (cont) {
cont->error();
cont->redo();
}
return;
}
/* Thats seek end - we are finished */
if (mfm_status & STAT_SED) {
issue_command(CMD_POD, NULL, 0);
MFM_DRV_INFO.cylinder = 0;
mfm_seek();
return;
}
/* Command end without seek end (see data sheet p.20) for parallel seek
- we have to send a POL command to wait for the seek */
if (mfm_status & STAT_CED) {
SET_INTR(mfm_recal_intr);
issue_command(CMD_POL, NULL, 0);
return;
}
printk("recal: unknown status\n");
}
 
static void mfm_seek_intr(void)
{
#ifdef DEBUG
console_printf("seek intr - status = ");
print_status();
#endif
outw(CMD_RCAL, MFM_COMMAND); /* Clear interrupt condition */
if (mfm_status & (STAT_DER | STAT_ABN)) {
printk("seek failed\n");
MFM_DRV_INFO.cylinder = NEED_2_RECAL;
if (cont) {
cont->error();
cont->redo();
}
return;
}
if (mfm_status & STAT_SED) {
issue_command(CMD_POD, NULL, 0);
MFM_DRV_INFO.cylinder = raw_cmd.cylinder;
mfm_seek();
return;
}
if (mfm_status & STAT_CED) {
SET_INTR(mfm_seek_intr);
issue_command(CMD_POL, NULL, 0);
return;
}
printk("seek: unknown status\n");
}
 
/* IDEA2 seems to work better - its what RiscOS sets my
* disc to - on its SECOND call to specify!
*/
#define IDEA2
#ifndef IDEA2
#define SPEC_SL 0x16
#define SPEC_SH 0xa9 /* Step pulse high=21, Record Length=001 (256 bytes) */
#else
#define SPEC_SL 0x00 /* OM2 - SL - step pulse low */
#define SPEC_SH 0x21 /* Step pulse high=4, Record Length=001 (256 bytes) */
#endif
 
static void mfm_setupspecify (int drive, unsigned char *cmdb)
{
cmdb[0] = 0x1f; /* OM0 - !SECT,!MOD,!DIF,PADP,ECD,CRCP,CRCI,ACOR */
cmdb[1] = 0xc3; /* OM1 - DTM,BRST,!CEDM,!SEDM,!DERM,0,AMEX,PSK */
cmdb[2] = SPEC_SL; /* OM2 - SL - step pulse low */
cmdb[3] = (number_mfm_drives == 1) ? 0x02 : 0x06; /* 1 or 2 drives */
cmdb[4] = 0xfc | ((mfm_info[drive].cylinders - 1) >> 8);/* RW time over/high part of number of cylinders */
cmdb[5] = mfm_info[drive].cylinders - 1; /* low part of number of cylinders */
cmdb[6] = mfm_info[drive].heads - 1; /* Number of heads */
cmdb[7] = mfm_info[drive].sectors - 1; /* Number of sectors */
cmdb[8] = SPEC_SH;
cmdb[9] = 0x0a; /* gap length 1 */
cmdb[10] = 0x0d; /* gap length 2 */
cmdb[11] = 0x0c; /* gap length 3 */
cmdb[12] = (mfm_info[drive].precomp - 1) >> 8; /* pre comp cylinder */
cmdb[13] = mfm_info[drive].precomp - 1;
cmdb[14] = (mfm_info[drive].lowcurrent - 1) >> 8; /* Low current cylinder */
cmdb[15] = mfm_info[drive].lowcurrent - 1;
}
 
static void mfm_specify (void)
{
unsigned char cmdb[16];
 
DBG("specify...dev=%d lastspecified=%d\n", raw_cmd.dev, lastspecifieddrive);
mfm_setupspecify (raw_cmd.dev, cmdb);
 
issue_command (CMD_SPC, cmdb, 16);
/* Ensure that we will do another specify if we move to the other drive */
lastspecifieddrive = raw_cmd.dev;
wait_for_completion();
}
 
static void mfm_seek(void)
{
unsigned char cmdb[4];
 
DBG("seeking...\n");
if (MFM_DRV_INFO.cylinder < 0) {
SET_INTR(mfm_recal_intr);
DBG("mfm_seek: about to call specify\n");
mfm_specify (); /* DAG added this */
 
cmdb[0] = raw_cmd.dev + 1;
cmdb[1] = 0;
 
issue_command(CMD_RCLB, cmdb, 2);
return;
}
if (MFM_DRV_INFO.cylinder != raw_cmd.cylinder) {
cmdb[0] = raw_cmd.dev + 1;
cmdb[1] = 0; /* raw_cmd.head; DAG: My data sheet says this should be 0 */
cmdb[2] = raw_cmd.cylinder >> 8;
cmdb[3] = raw_cmd.cylinder;
 
SET_INTR(mfm_seek_intr);
issue_command(CMD_SEK, cmdb, 4);
} else
mfm_setup_rw();
}
 
static void mfm_initialise(void)
{
DBG("init...\n");
mfm_seek();
}
 
static void request_done(int uptodate)
{
DBG("mfm:request_done\n");
if (uptodate) {
unsigned char block[2] = {0, 0};
 
/* Apparently worked - lets check bytes left to DMA */
if (hdc63463_dataleft != (PartFragRead_SectorsLeft * 256)) {
printk("mfm: request_done - dataleft=%d - should be %d - Eek!\n", hdc63463_dataleft, PartFragRead_SectorsLeft * 256);
end_request(0);
Busy = 0;
};
/* Potentially this means that we've done; but we might be doing
a partial access, (over two cylinders) or we may have a number
of fragments in an image file. First lets deal with partial accesss
*/
if (PartFragRead) {
/* Yep - a partial access */
 
/* and issue the remainder */
issue_request(MINOR(CURRENT->rq_dev), PartFragRead_RestartBlock, PartFragRead_SectorsLeft, CURRENT);
return;
}
 
/* ah well - perhaps there is another fragment to go */
 
/* Increment pointers/counts to start of next fragment */
if (SectorsLeftInRequest > 0) printk("mfm: SectorsLeftInRequest>0 - Eek! Shouldn't happen!\n");
 
/* No - its the end of the line */
/* end_request's should have happened at the end of sector DMAs */
/* Turns Drive LEDs off - may slow it down? */
if (!CURRENT)
issue_command(CMD_CKV, block, 2);
 
Busy = 0;
DBG("request_done: About to mfm_request\n");
/* Next one please */
mfm_request(); /* Moved from mfm_rw_intr */
DBG("request_done: returned from mfm_request\n");
} else {
printk("mfm:request_done: update=0\n");
end_request(0);
Busy = 0;
}
}
 
static void error_handler(void)
{
printk("error detected... status = ");
print_status();
(*errors)++;
if (*errors > MFM_DRV_INFO.errors.abort)
cont->done(0);
if (*errors > MFM_DRV_INFO.errors.recal)
MFM_DRV_INFO.cylinder = NEED_2_RECAL;
}
 
static void rw_interrupt(void)
{
printk("rw_interrupt\n");
}
 
static struct cont rw_cont =
{
rw_interrupt,
error_handler,
mfm_rerequest,
request_done
};
 
/*
* Actually gets round to issuing the request - note everything at this
* point is in 256 byte sectors not Linux 512 byte blocks
*/
static void issue_request(int dev, unsigned int block, unsigned int nsect,
struct request *req)
{
int track, start_head, start_sector;
int sectors_to_next_cyl;
 
dev >>= 6;
 
track = block / mfm_info[dev].sectors;
start_sector = block % mfm_info[dev].sectors;
start_head = track % mfm_info[dev].heads;
 
/* First get the number of whole tracks which are free before the next
track */
sectors_to_next_cyl = (mfm_info[dev].heads - (start_head + 1)) * mfm_info[dev].sectors;
/* Then add in the number of sectors left on this track */
sectors_to_next_cyl += (mfm_info[dev].sectors - start_sector);
 
DBG("issue_request: mfm_info[dev].sectors=%d track=%d\n", mfm_info[dev].sectors, track);
 
raw_cmd.dev = dev;
raw_cmd.sector = start_sector;
raw_cmd.head = start_head;
raw_cmd.cylinder = track / mfm_info[dev].heads;
raw_cmd.cmdtype = CURRENT->cmd;
raw_cmd.cmdcode = CURRENT->cmd == WRITE ? CMD_WD : CMD_RD;
raw_cmd.cmddata[0] = dev + 1; /* DAG: +1 to get US */
raw_cmd.cmddata[1] = raw_cmd.head;
raw_cmd.cmddata[2] = raw_cmd.cylinder >> 8;
raw_cmd.cmddata[3] = raw_cmd.cylinder;
raw_cmd.cmddata[4] = raw_cmd.head;
raw_cmd.cmddata[5] = raw_cmd.sector;
 
/* Was == and worked - how the heck??? */
if (lastspecifieddrive != raw_cmd.dev)
mfm_specify ();
 
if (nsect <= sectors_to_next_cyl) {
raw_cmd.cmddata[6] = nsect >> 8;
raw_cmd.cmddata[7] = nsect;
PartFragRead = 0; /* All in one */
PartFragRead_SectorsLeft = 0; /* Must set this - used in DMA calcs */
} else {
raw_cmd.cmddata[6] = sectors_to_next_cyl >> 8;
raw_cmd.cmddata[7] = sectors_to_next_cyl;
PartFragRead = sectors_to_next_cyl; /* only do this many this time */
PartFragRead_RestartBlock = block + sectors_to_next_cyl; /* Where to restart from */
PartFragRead_SectorsLeft = nsect - sectors_to_next_cyl;
}
raw_cmd.cmdlen = 8;
 
/* Setup DMA pointers */
hdc63463_dataptr = (unsigned int) Copy_buffer;
hdc63463_dataleft = nsect * 256; /* Better way? */
 
DBG("mfm%c: %sing: CHS=%d/%d/%d, sectors=%d, buffer=0x%08lx (%p)\n",
raw_cmd.dev + 'a', (CURRENT->cmd == READ) ? "read" : "writ",
raw_cmd.cylinder,
raw_cmd.head,
raw_cmd.sector, nsect, (unsigned long) Copy_buffer, CURRENT);
 
cont = &rw_cont;
errors = &(CURRENT->errors);
#if 0
mfm_tq.routine = (void (*)(void *)) mfm_initialise;
queue_task(&mfm_tq, &tq_immediate);
mark_bh(IMMEDIATE_BH);
#else
mfm_initialise();
#endif
} /* issue_request */
 
/*
* Called when an error has just happened - need to trick mfm_request
* into thinking we weren't busy
*
* Turn off ints - mfm_request expects them this way
*/
static void mfm_rerequest(void)
{
DBG("mfm_rerequest\n");
cli();
Busy = 0;
mfm_request();
}
 
static void mfm_request(void)
{
DBG("mfm_request CURRENT=%p Busy=%d\n", CURRENT, Busy);
 
if (!CURRENT) {
DBG("mfm_request: Exited due to NULL Current 1\n");
return;
}
 
if (CURRENT->rq_status == RQ_INACTIVE) {
/* Hmm - seems to be happening a lot on 1.3.45 */
/*console_printf("mfm_request: Exited due to INACTIVE Current\n"); */
return;
}
 
/* If we are still processing then return; we will get called again */
if (Busy) {
/* Again seems to be common in 1.3.45 */
/*DBG*/printk("mfm_request: Exiting due to busy\n");
return;
}
Busy = 1;
 
while (1) {
unsigned int dev, block, nsect;
 
DBG("mfm_request: loop start\n");
sti();
 
DBG("mfm_request: before INIT_REQUEST\n");
 
if (!CURRENT) {
printk("mfm_request: Exiting due to !CURRENT (pre)\n");
CLEAR_INTR;
Busy = 0;
return;
};
 
INIT_REQUEST;
 
DBG("mfm_request: before arg extraction\n");
 
dev = MINOR(CURRENT->rq_dev);
block = CURRENT->sector;
nsect = CURRENT->nr_sectors;
#ifdef DEBUG
/*if ((dev>>6)==1) */ console_printf("mfm_request: raw vals: dev=%d (block=512 bytes) block=%d nblocks=%d\n", dev, block, nsect);
#endif
if (dev >= (mfm_drives << 6) ||
block >= mfm[dev].nr_sects || ((block+nsect) > mfm[dev].nr_sects)) {
if (dev >= (mfm_drives << 6))
printk("mfm: bad minor number: device=%s\n", kdevname(CURRENT->rq_dev));
else
printk("mfm%c: bad access: block=%d, count=%d, nr_sects=%ld\n", (dev >> 6)+'a',
block, nsect, mfm[dev].nr_sects);
printk("mfm: continue 1\n");
end_request(0);
Busy = 0;
continue;
}
 
block += mfm[dev].start_sect;
 
/* DAG: Linux doesn't cope with this - even though it has an array telling
it the hardware block size - silly */
block <<= 1; /* Now in 256 byte sectors */
nsect <<= 1; /* Ditto */
 
SectorsLeftInRequest = nsect >> 1;
Sectors256LeftInCurrent = CURRENT->current_nr_sectors * 2;
Copy_buffer = CURRENT->buffer;
Copy_Sector = CURRENT->sector << 1;
 
DBG("mfm_request: block after offset=%d\n", block);
 
if (CURRENT->cmd != READ && CURRENT->cmd != WRITE) {
printk("unknown mfm-command %d\n", CURRENT->cmd);
end_request(0);
Busy = 0;
printk("mfm: continue 4\n");
continue;
}
issue_request(dev, block, nsect, CURRENT);
 
break;
}
DBG("mfm_request: Dropping out bottom\n");
}
 
static void do_mfm_request(void)
{
DBG("do_mfm_request: about to mfm_request\n");
mfm_request();
}
 
static void mfm_interrupt_handler(int unused, void *dev_id, struct pt_regs *regs)
{
void (*handler) (void) = DEVICE_INTR;
 
CLEAR_INTR;
 
DBG("mfm_interrupt_handler (handler=0x%p)\n", handler);
 
mfm_status = inw(MFM_STATUS);
 
/* If CPR (Command Parameter Reject) and not busy it means that the command
has some return message to give us */
if ((mfm_status & (STAT_CPR | STAT_BSY)) == STAT_CPR) {
int len = 0;
while (len < 16) {
int in;
in = inw(MFM_DATAIN);
result[len++] = in >> 8;
result[len++] = in;
}
}
if (handler) {
handler();
return;
}
outw (CMD_RCAL, MFM_COMMAND); /* Clear interrupt condition */
printk ("mfm: unexpected interrupt - status = ");
print_status ();
while (1);
}
 
 
 
 
 
/*
* Tell the user about the drive if we decided it exists. Also,
* set the size of the drive.
*/
static void mfm_geometry (int drive)
{
if (mfm_info[drive].cylinders)
printk ("mfm%c: %dMB CHS=%d/%d/%d LCC=%d RECOMP=%d\n", 'a' + drive,
mfm_info[drive].cylinders * mfm_info[drive].heads * mfm_info[drive].sectors / 4096,
mfm_info[drive].cylinders, mfm_info[drive].heads, mfm_info[drive].sectors,
mfm_info[drive].lowcurrent, mfm_info[drive].precomp);
mfm[drive << 6].start_sect = 0;
mfm[drive << 6].nr_sects = mfm_info[drive].cylinders * mfm_info[drive].heads * mfm_info[drive].sectors / 2;
}
 
#ifdef MFM_AUTODETECT_DRIVES
/*
* Attempt to detect a drive and find its geometry. The drive has already been
* specified...
*
* We first recalibrate the disk, then try to probe sectors, heads and then
* cylinders. NOTE! the cylinder probe may break drives. The xd disk driver
* does something along these lines, so I assume that most drives are up to
* this mistreatment...
*/
static int mfm_detectdrive (int drive)
{
unsigned int mingeo[3], maxgeo[3];
unsigned int attribute, need_recal = 1;
unsigned char cmdb[8];
 
memset (mingeo, 0, sizeof (mingeo));
maxgeo[0] = mfm_info[drive].sectors;
maxgeo[1] = mfm_info[drive].heads;
maxgeo[2] = mfm_info[drive].cylinders;
 
cmdb[0] = drive + 1;
cmdb[6] = 0;
cmdb[7] = 1;
for (attribute = 0; attribute < 3; attribute++) {
while (mingeo[attribute] != maxgeo[attribute]) {
unsigned int variable;
 
variable = (maxgeo[attribute] + mingeo[attribute]) >> 1;
cmdb[1] = cmdb[2] = cmdb[3] = cmdb[4] = cmdb[5] = 0;
 
if (need_recal) {
int tries = 5;
 
do {
issue_command (CMD_RCLB, cmdb, 2);
wait_for_completion ();
wait_for_command_end ();
if (result[1] == 0x20)
break;
} while (result[1] && --tries);
if (result[1]) {
outw (CMD_RCAL, MFM_COMMAND);
return 0;
}
need_recal = 0;
}
 
switch (attribute) {
case 0:
cmdb[5] = variable;
issue_command (CMD_CMPD, cmdb, 8);
break;
case 1:
cmdb[1] = variable;
cmdb[4] = variable;
issue_command (CMD_CMPD, cmdb, 8);
break;
case 2:
cmdb[2] = variable >> 8;
cmdb[3] = variable;
issue_command (CMD_SEK, cmdb, 4);
break;
}
wait_for_completion ();
wait_for_command_end ();
 
switch (result[1]) {
case 0x00:
case 0x50:
mingeo[attribute] = variable + 1;
break;
 
case 0x20:
outw (CMD_RCAL, MFM_COMMAND);
return 0;
 
case 0x24:
need_recal = 1;
default:
maxgeo[attribute] = variable;
break;
}
}
}
mfm_info[drive].cylinders = mingeo[2];
mfm_info[drive].lowcurrent = mingeo[2];
mfm_info[drive].precomp = mingeo[2] / 2;
mfm_info[drive].heads = mingeo[1];
mfm_info[drive].sectors = mingeo[0];
outw (CMD_RCAL, MFM_COMMAND);
return 1;
}
#endif
 
/*
* Initialise all drive information for this controller.
*/
static int mfm_initdrives(void)
{
int drive;
 
if (number_mfm_drives > MFM_MAXDRIVES) {
number_mfm_drives = MFM_MAXDRIVES;
printk("No. of ADFS MFM drives is greater than MFM_MAXDRIVES - you can't have that many!\n");
}
 
for (drive = 0; drive < number_mfm_drives; drive++) {
mfm_info[drive].lowcurrent = 1;
mfm_info[drive].precomp = 1;
mfm_info[drive].cylinder = -1;
mfm_info[drive].errors.recal = 0;
mfm_info[drive].errors.report = 0;
mfm_info[drive].errors.abort = 4;
 
#ifdef MFM_AUTODETECT_DRIVES
mfm_info[drive].cylinders = 1024;
mfm_info[drive].heads = 8;
mfm_info[drive].sectors = 64;
{
unsigned char cmdb[16];
 
mfm_setupspecify (drive, cmdb);
cmdb[1] &= ~0x81;
issue_command (CMD_SPC, cmdb, 16);
wait_for_completion ();
if (!mfm_detectdrive (drive)) {
mfm_info[drive].cylinders = 0;
mfm_info[drive].heads = 0;
mfm_info[drive].sectors = 0;
}
cmdb[0] = cmdb[1] = 0;
issue_command (CMD_CKV, cmdb, 2);
}
#else
mfm_info[drive].cylinders = 1; /* its going to have to figure it out from the partition info */
mfm_info[drive].heads = 4;
mfm_info[drive].sectors = 32;
#endif
}
return number_mfm_drives;
}
 
 
 
/*
* The 'front' end of the mfm driver follows...
*/
 
static int mfm_ioctl(struct inode *inode, struct file *file, u_int cmd, u_long arg)
{
struct hd_geometry *geo = (struct hd_geometry *) arg;
int dev, err;
 
if (!inode || !inode->i_rdev)
return -EINVAL;
dev = DEVICE_NR(MINOR(inode->i_rdev)), err;
if (dev >= mfm_drives)
return -EINVAL;
 
switch (cmd) {
case HDIO_GETGEO:
if (!arg)
return -EINVAL;
err = verify_area (VERIFY_WRITE, geo, sizeof(*geo));
if (err)
return err;
put_user (mfm_info[dev].heads, &geo->heads);
put_user (mfm_info[dev].sectors, &geo->sectors);
put_user (mfm_info[dev].cylinders, &geo->cylinders);
put_user (mfm[MINOR(inode->i_rdev)].start_sect, &geo->start);
return 0;
 
case BLKRASET:
if (!suser())
return -EACCES;
if (arg > 0xff)
return -EINVAL;
read_ahead[MAJOR(inode->i_rdev)] = arg;
return 0;
 
case BLKRAGET:
if (!arg)
return -EINVAL;
err = verify_area (VERIFY_WRITE, (long *) arg, sizeof (long));
if (err)
return err;
put_user(read_ahead[MAJOR(inode->i_rdev)], (long *)arg);
return 0;
 
case BLKGETSIZE:
if (!arg)
return -EINVAL;
err = verify_area (VERIFY_WRITE, (long *) arg, sizeof (long));
if (err)
return err;
put_user (mfm[MINOR(inode->i_rdev)].nr_sects, (long *)arg);
return 0;
 
case BLKFLSBUF:
if (!suser())
return -EACCES;
if (!inode->i_rdev)
return -EINVAL;
fsync_dev(inode->i_rdev);
invalidate_buffers(inode->i_rdev);
return 0;
 
case BLKRRPART:
return mfm_reread_partitions(inode->i_rdev);
 
RO_IOCTLS(inode->i_rdev, arg);
default:
return -EINVAL;
}
}
 
static int mfm_open(struct inode *inode, struct file *file)
{
int dev = DEVICE_NR(MINOR(inode->i_rdev));
 
if (dev >= mfm_drives)
return -ENODEV;
while (mfm_info[dev].busy)
sleep_on (&mfm_wait_open);
mfm_info[dev].access_count++;
return 0;
}
 
/*
* Releasing a block device means we sync() it, so that it can safely
* be forgotten about...
*/
static void mfm_release(struct inode *inode, struct file *file)
{
sync_dev(inode->i_rdev);
mfm_info[DEVICE_NR(MINOR(inode->i_rdev))].access_count--;
}
 
/*
* This is to handle various kernel command line parameters
* specific to this driver.
*/
void mfm_setup(char *str, int *ints)
{
return;
}
 
/*
* Set the CHS from the ADFS boot block if it is present. This is not ideal
* since if there are any non-ADFS partitions on the disk, this won't work!
* Hence, I want to get rid of this...
*/
void xd_set_geometry(kdev_t dev, unsigned char secsptrack, unsigned char heads,
unsigned long discsize, unsigned int secsize)
{
int drive = MINOR(dev) >> 6;
 
if (mfm_info[drive].cylinders == 1) {
mfm_info[drive].sectors = secsptrack;
mfm_info[drive].heads = heads;
mfm_info[drive].cylinders = discsize / (secsptrack * heads * secsize);
 
if ((heads < 1) || (mfm_info[drive].cylinders > 1024)) {
printk("mfm%c: Insane disc shape! Setting to 512/4/32\n",'a' + (dev >> 6));
 
/* These values are fairly arbitary, but are there so that if your
* lucky you can pick apart your disc to find out what is going on -
* I reckon these figures won't hurt MOST drives
*/
mfm_info[drive].sectors = 32;
mfm_info[drive].heads = 4;
mfm_info[drive].cylinders = 512;
}
if (raw_cmd.dev == drive)
mfm_specify ();
mfm_geometry (drive);
}
}
 
static void mfm_geninit (struct gendisk *gdev);
 
static struct gendisk mfm_gendisk = {
MAJOR_NR, /* Major number */
"mfm", /* Major name */
6, /* Bits to shift to get real from partition */
1 << 6, /* Number of partitions per real */
MFM_MAXDRIVES, /* maximum number of real */
mfm_geninit, /* init function */
mfm, /* hd struct */
mfm_sizes, /* block sizes */
0, /* number */
(void *) mfm_info, /* internal */
NULL /* next */
};
 
static void mfm_geninit (struct gendisk *gdev)
{
int i;
 
mfm_drives = mfm_initdrives();
 
printk("mfm: detected %d hard drive%s\n", mfm_drives, mfm_drives == 1 ? "" : "s");
gdev->nr_real = mfm_drives;
 
for (i = 0; i < mfm_drives; i++)
mfm_geometry (i);
 
if (request_irq(mfm_irq, mfm_interrupt_handler, SA_INTERRUPT, "MFM harddisk", NULL))
printk("mfm: unable to get IRQ%d\n", mfm_irq);
 
if (mfm_irqenable)
outw(0x80, mfm_irqenable); /* Required to enable IRQs from MFM podule */
 
for (i = 0; i < (MFM_MAXDRIVES << 6); i++) {
mfm_blocksizes[i] = 1024; /* Can't increase this - if you do all hell breaks loose */
mfm_sectsizes[i] = 512;
}
blksize_size[MAJOR_NR] = mfm_blocksizes;
hardsect_size[MAJOR_NR] = mfm_sectsizes;
}
 
void xd_manual_geo_init (char *command,int *integers) {
printk("MFM:Manual setting of geometry not presently supported\n");
}
 
static struct file_operations mfm_fops =
{
NULL, /* lseek - default */
block_read, /* read - general block-dev read */
block_write, /* write - general block-dev write */
NULL, /* readdir - bad */
NULL, /* select */
mfm_ioctl, /* ioctl */
NULL, /* mmap */
mfm_open, /* open */
mfm_release, /* release */
block_fsync /* fsync */
};
 
 
static struct expansion_card *ecs;
 
/*
* See if there is a controller at the address presently at mfm_addr
*
* We check to see if the controller is busy - if it is, we abort it first,
* and check that the chip is no longer busy after at least 180 clock cycles.
* We then issue a command and check that the BSY or CPR bits are set.
*/
static int mfm_probecontroller (unsigned int mfm_addr)
{
if (check_region (mfm_addr, 10))
return 0;
 
if (inw (MFM_STATUS) & STAT_BSY) {
outw (CMD_ABT, MFM_COMMAND);
udelay (50);
if (inw (MFM_STATUS) & STAT_BSY)
return 0;
}
 
if (inw (MFM_STATUS) & STAT_CED)
outw (CMD_RCAL, MFM_COMMAND);
 
outw (CMD_SEK, MFM_COMMAND);
 
if (inw (MFM_STATUS) & (STAT_BSY | STAT_CPR)) {
unsigned int count = 2000;
while (inw (MFM_STATUS) & STAT_BSY) {
udelay (500);
if (!--count)
return 0;
}
 
outw (CMD_RCAL, MFM_COMMAND);
}
return 1;
}
 
/*
* Look for a MFM controller - first check the motherboard, then the podules
* The podules have an extra interrupt enable that needs to be played with
*
* The HDC is accessed at MEDIUM IOC speeds.
*/
int mfm_init (void)
{
unsigned char irqmask;
 
if (register_blkdev(MAJOR_NR, "mfm", &mfm_fops)) {
printk("mfm_init: unable to get major number %d\n", MAJOR_NR);
return -1;
}
 
if (mfm_probecontroller(ONBOARD_MFM_ADDRESS)) {
mfm_addr = ONBOARD_MFM_ADDRESS;
mfm_IRQPollLoc = IOC_IRQSTATB;
mfm_irqenable = 0;
mfm_irq = IRQ_HARDDISK;
irqmask = 0x08; /* IL3 pin */
} else {
ecs = ecard_find(0, mfm_cids);
if (!ecs) {
mfm_addr = 0;
return -1;
}
 
mfm_addr = ecard_address(ecs, ECARD_IOC, ECARD_MEDIUM) + 0x800;
mfm_IRQPollLoc = mfm_addr + 0x400;
mfm_irqenable = mfm_IRQPollLoc;
mfm_irq = ecs->irq;
irqmask = 0x08;
 
ecard_claim(ecs);
}
 
printk("mfm: found at address %08X, interrupt %d\n", mfm_addr, mfm_irq);
request_region (mfm_addr, 10, "mfm");
 
/* Stuff for the assembler routines to get to */
hdc63463_baseaddress = ioaddr(mfm_addr);
hdc63463_irqpolladdress = ioaddr(mfm_IRQPollLoc);
hdc63463_irqpollmask = irqmask;
 
blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
read_ahead[MAJOR_NR] = 8; /* 8 sector (4kB?) read ahread */
 
#ifndef MODULE
mfm_gendisk.next = gendisk_head;
gendisk_head = &mfm_gendisk;
#endif
 
Busy = 0;
lastspecifieddrive = -1;
 
return 0;
}
 
/*
* This routine is called to flush all partitions and partition tables
* for a changed MFM disk, and then re-read the new partition table.
* If we are revalidating due to an ioctl, we have USAGE == 1.
*/
static int mfm_reread_partitions(kdev_t dev)
{
unsigned int start, i, maxp, target = DEVICE_NR(MINOR(dev));
unsigned long flags;
 
save_flags_cli(flags);
if (mfm_info[target].busy || mfm_info[target].access_count > 1) {
restore_flags (flags);
return -EBUSY;
}
mfm_info[target].busy = 1;
restore_flags (flags);
 
maxp = mfm_gendisk.max_p;
start = target << mfm_gendisk.minor_shift;
 
for (i = maxp - 1; i >= 0; i--) {
int minor = start + i;
kdev_t devi = MKDEV(MAJOR_NR, minor);
 
sync_dev (devi);
invalidate_inodes (devi);
invalidate_buffers (devi);
 
mfm_gendisk.part[minor].start_sect = 0;
mfm_gendisk.part[minor].nr_sects = 0;
}
 
mfm_gendisk.part[start].nr_sects = mfm_info[target].heads *
mfm_info[target].cylinders * mfm_info[target].sectors / 2;
 
resetup_one_dev(&mfm_gendisk, target);
 
mfm_info[target].busy = 0;
wake_up (&mfm_wait_open);
return 0;
}
 
#ifdef MODULE
int init_module(void)
{
int ret;
ret = mfm_init();
if (!ret)
mfm_geninit(&mfm_gendisk);
return ret;
}
 
void cleanup_module(void)
{
if (ecs && mfm_irqenable)
outw (0, mfm_irqenable); /* Required to enable IRQs from MFM podule */
free_irq(mfm_irq, NULL);
unregister_blkdev(MAJOR_NR, "mfm");
if (ecs)
ecard_release(ecs);
if (mfm_addr)
release_region(mfm_addr, 10);
}
#endif
/Makefile
0,0 → 1,185
#
# Makefile for the kernel block device drivers.
#
# Note! Dependencies are done automagically by 'make dep', which also
# removes any old dependencies. DON'T put your own dependencies here
# unless it's something special (ie not a .c file).
#
# Note 2! The CFLAGS definition is now inherited from the
# parent makefile.
#
 
all: links first_rule
 
L_TARGET := block.a
L_OBJS := ll_rw_blk.o genhd.o
M_OBJS :=
MOD_LIST_NAME := BLOCK_MODULES
 
LK = README.fd README.hd README.ide README.md \
ide_modes.h ide-cd.c ide-tape.c ide-tape.h \
linear.c loop.c md.c raid0.c raid1.c raid5.c rd.c
 
# Architecture dependencies
 
ifeq ($(MACHINE),arc)
FLOPPY = fd1772.o fd1772dma.o
FLOPPYMOD = fd1772_mod.o
endif
 
ifeq ($(MACHINE),a5k)
FLOPPY = floppy.o
FLOPPYMOD = floppy.o
endif
 
ifeq ($(MACHINE),rpc)
FLOPPY = floppy.o
FLOPPYMOD = floppy.o
endif
 
# Common dependencies
 
ifdef FLOPPY
ifeq ($(CONFIG_BLK_DEV_FD),y)
L_OBJS += $(FLOPPY)
else
ifeq ($(CONFIG_BLK_DEV_FD),m)
M_OBJS += $(FLOPPYMOD)
endif
endif
endif
 
ifeq ($(CONFIG_BLK_DEV_PART),y)
L_OBJS += adfspart.o
endif
 
ifeq ($(CONFIG_BLK_DEV_RAM),y)
L_OBJS += rd.o
else
ifeq ($(CONFIG_BLK_DEV_RAM),m)
M_OBJS += rd.o
endif
endif
 
ifeq ($(CONFIG_BLK_DEV_LOOP),y)
L_OBJS += loop.o
else
ifeq ($(CONFIG_BLK_DEV_LOOP),m)
M_OBJS += loop.o
endif
endif
 
ifeq ($(CONFIG_BLK_DEV_HD),y)
L_OBJS += hd.o
endif
 
ifeq ($(CONFIG_BLK_DEV_IDE),y)
L_OBJS += ide.o
endif
 
ifeq ($(CONFIG_BLK_DEV_IDE_ICSIDE),y)
L_OBJS += ide-ics.o
else
ifeq ($(CONFIG_BLK_DEV_IDE_ICSIDE),m)
M_OBJS += ide-ics.o
endif
endif
 
ifeq ($(CONFIG_BLK_DEV_IDE_RAPIDE),y)
L_OBJS += ide-rapide.o
else
ifeq ($(CONFIG_BLK_DEV_IDE_RAPIDE),m)
M_OBJS += ide-rapide.o
endif
endif
 
ifeq ($(CONFIG_BLK_DEV_IDECD),y)
L_OBJS += ide-cd.o
endif
 
ifeq ($(CONFIG_BLK_DEV_IDETAPE),y)
L_OBJS += ide-tape.o
endif
 
ifeq ($(CONFIG_BLK_DEV_IDEFLOPPY),y)
L_OBJS += ide-floppy.o
endif
 
ifeq ($(CONFIG_BLK_DEV_XD),y)
L_OBJS += mfmhd.o mfm.o
else
ifeq ($(CONFIG_BLK_DEV_XD),m)
M_OBJS += mfmhd_mod.o
endif
endif
 
ifeq ($(CONFIG_BLK_DEV_MD),y)
LX_OBJS += md.o
 
ifeq ($(CONFIG_MD_LINEAR),y)
L_OBJS += linear.o
else
ifeq ($(CONFIG_MD_LINEAR),m)
M_OBJS += linear.o
endif
endif
 
ifeq ($(CONFIG_MD_STRIPED),y)
L_OBJS += raid0.o
else
ifeq ($(CONFIG_MD_STRIPED),m)
M_OBJS += raid0.o
endif
endif
 
ifeq ($(CONFIG_MD_MIRRORING),y)
L_OBJS += raid1.o
else
ifeq ($(CONFIG_MD_MIRRORING),m)
M_OBJS += raid1.o
endif
endif
 
ifeq ($(CONFIG_MD_RAID5),y)
L_OBJS += raid5.o
else
ifeq ($(CONFIG_MD_RAID5),m)
M_OBJS += raid5.o
endif
endif
 
endif
 
include $(TOPDIR)/Rules.make
 
fastdep: links
 
fd1772_mod.o: $(FLOPPY)
ld -r -o $@ $(FLOPPY)
 
mfmhd_mod.o: mfmhd.o mfm.o
ld -r -o $@ mfmhd.o mfm.o
 
.PHONY: links
links:
-@for f in $(LK); do \
if [ ! -e $$f ]; then \
echo "ln -s ../../../../drivers/block/$$f ."; \
ln -s ../../../../drivers/block/$$f .; \
fi; \
done
 
mrproper:
-@for f in $(LK); do \
if [ -L $$f ]; then \
echo $(RM) $$f; \
$(RM) $$f; \
elif [ -f $$f ]; then \
echo not removing $$f; \
fi; \
done
 
%.o: %.S
$(CC) $(CFLAGS) -E $< | tr ';$$' '\n#' > ..tmp.s
$(CC) $(CFLAGS) -c -o $@ ..tmp.s
$(RM) ..tmp.s

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.