📄 bddrv.c
字号:
/*****************************************************************************
BDDL: Block Device Driver Library
Chris Giese <geezer@execpc.com>
http://www.execpc.com/~geezer
The ATA and ATAPI code won't run in a Windows DOS box
(the floppy code _will_ work).
If this code is configured to use the floppy, do NOT run it unless
the floppy drive light is off. A timer interrupt in the BIOS may
shut off the floppy after this code turns it on. That will, in turn,
cause this code to freeze up.
Ctrl-Break may get you out of this program if it does freeze up,
but the IDE and floppy interrupt vectors will not be restored.
You will have to reset the PC (or, if run from a Windows DOS
box, exit the DOS box).
With the current #ifdef's, this code will
- expect an ATAPI (IDE) CD-ROM drive as the slave drive
on the primary interface (2nd drive at I/O=1F0h)
- expect a data CD-ROM inserted in the CD-ROM drive
- expect a formatted, BLANK 1.44 meg floppy inserted in drive A:
(any data on the floppy will be overwritten)
- copy 180K (10 floppy tracks) from the CD-ROM to the floppy
- print great wads of debug information (it's probably NOT a
good idea to redirect these messages to a file...)
to do:
- figure out a common API for the floppy, ATA, and ATAPI device drivers
- use the above API to implement a RAM disk
- add a disk cache
- finish ide_error()
- ide_enable_multmode() should also be able to disable multiple mode
- ide_enable_multmode() needs to pass a request_t struct to ide_error()
- need to call ide_select() drive? should I retry it if it fails?
- >>> finish ide_write_sectors()
- review the info retrieved from the drive in ide_identify()
- finish atapi_error()
- atapi_toc_ent() and atapi_mode_sense() request < 2048 bytes
(less than 1 block) from atapi_cmd()
- need a floppy_select() function separate from floppy_motor_on()
- floppy_motor_on() needs to use fdev->blkdev.unit
- floppy_motor_on() has a 500 ms delay that is not interrupt-driven
- floppy_seek() has a 15 ms delay that is not interrupt-driven
- should floppy_error() seek track 1 or track 0 on disk change?
if track 0, should it do recalibrate instead?
- finish floppy_error()
- probe geometry of inserted floppy disk (1.44M, 1.68M, other - how?)
- floppy code needs a timer interrupt to decrement dev->timeout
and shut off the floppy motor when it reaches zero
- add code to set IDE translation? what is this function used for?
- the CD-ROM transfers only 2K on each interrupt? is this normal?
- maybe a flatter hierarchy of structures? this looks ridiculous:
ioadr = dev->blkdev.io.adr[0];
- >>> request queues w/ validation of requests
- add code to format floppy track
- alternate floppy code that uses PIO instead of DMA?
(the CPU will have to poll the FDC status register
and read/write a byte every 13 us; maybe faster)
testing to do:
- WD, Conner, and Maxtor hard drives
- drives on primary interface (I/O=1F0h) and secondary (I/O=170h)
- master (unit=0xA0) and slave (unit=0xB0) drives on both interfaces
- CD-ROM as master, CD-ROM as slave w/ hard drive master, CD-ROM as
stand-alone slave (illegal?), hard drive as stand-alone slave (illegal?)
- multiple floppy drives
notes:
- IDE/ATAPI interrupt usage:
- irq14() and irq15() clear the IRQ at the 8259 PIC chips,
and set _interrupt_occurred (xxx - should clear IRQ at
the drive from the ISR, in case the IRQ is level-sensitive)
- (ide_)await_interrupt() clears _interrupt_occurred
- other code is responsible for clearing the interrupt
at the drive (usu. by reading the status register)
*****************************************************************************/
#include <string.h> /* memset() */
#include <stdio.h> /* printf(), putchar() */
#include <dos.h> /* delay(), inport(), outport(), inportb(), outportb() */
#if 1
#define DEBUG(X) X
#else
#define DEBUG(X) /* nothing */
#endif
#if defined(__TURBOC__)||!defined(__cplusplus)
typedef enum
{
false = 0, true = 1
} bool;
#endif
/* linear address of the sector buffer */
#define LMEM 0x90000L
#if defined(__TURBOC__)
#define INLINE /* nothing */
//#define _DS _DS
/* Put the floppy track buffer at 9000h:0000 */
#define LMEM_SEG (LMEM >> 4)
#define LMEM_OFF (LMEM & 15)
#define msleep(X) delay(X)
#elif defined(__DJGPP__)
#define INLINE __inline__
#define _DS _my_ds()
/* Floppy track buffer at linear address 90000h */
#define LMEM_SEG _dos_ds
#define LMEM_OFF LMEM
#define msleep(X) delay(X)
/* The nice thing about standards is... */
#define inport(P) inportw(P)
#define outport(P,V) outportw(P,V)
#else
#error Not Turbo C, not DJGPP. Sorry.
#endif
/* geezer's Portable Interrupt Macros (tm) */
#if defined(__TURBOC__)
/* getvect(), setvect() in dos.h */
#define INTERRUPT interrupt
#define SAVE_VECT(num, vec) vec = getvect(num)
#define SET_VECT(num, fn) setvect(num, fn)
#define RESTORE_VECT(num, vec) setvect(num, vec)
typedef void interrupt(*vector_t)(void);
#elif defined(__DJGPP__)
#include <dpmi.h> /* _go32_dpmi_... */
#include <go32.h> /* _my_cs() */
#define INTERRUPT /* nothing */
#define SAVE_VECT(num, vec) \
_go32_dpmi_get_protected_mode_interrupt_vector(num, &vec)
#define SET_VECT(num, fn) \
{ \
_go32_dpmi_seginfo new_vector; \
\
new_vector.pm_selector = _my_cs(); \
new_vector.pm_offset = (unsigned long)fn; \
_go32_dpmi_allocate_iret_wrapper(&new_vector); \
_go32_dpmi_set_protected_mode_interrupt_vector \
(num, &new_vector); \
}
#define RESTORE_VECT(num, vec) \
_go32_dpmi_set_protected_mode_interrupt_vector(num, &vec)
typedef _go32_dpmi_seginfo vector_t;
#endif
/* hardware resources used by a device */
#define NUM_IO_SPANS 2
typedef struct
{
unsigned char dma; /* 8-bit DMA mask */
unsigned short irq; /* 16-bit IRQ mask */
unsigned short adr[NUM_IO_SPANS]; /* start of I/O range */
unsigned short span[NUM_IO_SPANS]; /* length of I/O range */
} io_t;
typedef struct
{
/* hardware interface (hwif; or "bus") */
io_t io;
/* which drive on the hwif? 2 for IDE, 4 for floppy, 7 for SCSI */
unsigned char unit;
/* generic info (i.e. used by ALL types of block device) */
unsigned long num_blks;
unsigned short bytes_per_blk;
/* floppy and CHS IDE only */
unsigned short sectors, heads, cyls;
} blkdev_t;
struct _request
{
// sem_t semaphore;
enum
{
BLK_CMD_READ = 1, BLK_CMD_WRITE = 2
} cmd; /* read or write? */
void *dev; /* from/to which device? */
unsigned long blk; /* starting at which block? */
unsigned num_blks; /* how many blocks? */
// unsigned current_num_blks;
unsigned char *buf; /* from/to what memory location? */
unsigned errors; /* status */
// struct _request *next;
};
typedef struct _request request_t;
//////////////////////////////////////////////////////////////////////////////
// MISC/HELPER FUNCTIONS
//////////////////////////////////////////////////////////////////////////////
static volatile unsigned short _interrupt_occurred;
/*****************************************************************************
*****************************************************************************/
static unsigned short await_interrupt(unsigned short irq_mask,
unsigned timeout)
{
unsigned short intr = 0, time;
DEBUG(printf("await_interrupt: irq_mask=0x%04X, timeout=%u\n",
irq_mask, timeout);)
for(time = timeout; time != 0; time--)
{
intr = _interrupt_occurred & irq_mask;
if(intr != 0)
break;
msleep(1);
}
DEBUG(printf("await_interrupt: waited %3u ms\n", timeout - time);)
if(time == 0)
return 0;
_interrupt_occurred &= ~intr;
return intr;
}
/*****************************************************************************
*****************************************************************************/
#pragma argsused
static void nsleep(unsigned nanosecs)
{
}
/*****************************************************************************
*****************************************************************************/
static INLINE void insw(short port, unsigned short *data, unsigned count)
{
for(; count != 0; count--)
{
*data = inport(port);
data++;
}
}
/*****************************************************************************
*****************************************************************************/
static INLINE void outsw(short port, unsigned short *data, unsigned count)
{
for(; count != 0; count--)
{
outport(port, *data);
data++;
}
}
/*****************************************************************************
*****************************************************************************/
#define BPERL 16 /* byte/line for dump */
static void dump(unsigned char *data, unsigned count)
{
unsigned char byte1, byte2;
while(count != 0)
{
for(byte1 = 0; byte1 < BPERL; byte1++)
{
if(count == 0)
break;
printf("%02X ", data[byte1]);
count--;
}
printf("\t");
for(byte2 = 0; byte2 < byte1; byte2++)
{
if(data[byte2] < ' ')
putchar('.');
else
putchar(data[byte2]);
}
printf("\n");
data += BPERL;
}
}
/****************************************************************************
****************************************************************************/
static unsigned short bswap16(unsigned short arg)
{
return ((arg << 8) & 0xFF00) |
((arg >> 8) & 0xFF00);
}
/* these assume little-endian CPU (e.g. x86) and sizeof(short)==2 */
#define read_le16(X) *(unsigned short *)(X)
#define read_be16(X) bswap16(*(unsigned short *)(X))
//////////////////////////////////////////////////////////////////////////////
// IDE (ATA) HARD DRIVES
//////////////////////////////////////////////////////////////////////////////
/* ATA register file (offsets from 0x1F0 or 0x170) */
#define ATA_REG_DATA 0 /* data (16-bit) */
#define ATA_REG_FEAT 1 /* write: feature reg */
#define ATA_REG_ERROR ATA_REG_FEAT /* read: error */
#define ATA_REG_COUNT 2 /* sector count */
#define ATA_REG_SECTOR 3 /* sector */
#define ATA_REG_LOCYL 4 /* LSB of cylinder */
#define ATA_REG_HICYL 5 /* MSB of cylinder */
#define ATA_REG_DRVHD 6 /* drive select; head */
#define ATA_REG_CMD 7 /* write: drive command */
#define ATA_REG_STATUS 7 /* read: status and error flags */
#define ATA_REG_DEVCTRL 0x206 /* write: device control */
//efine ATA_REG_ALTSTAT 0x206 /* read: alternate status/error */
/* a few of the ATA registers are used differently by ATAPI... */
#define ATAPI_REG_REASON 2 /* interrupt reason */
#define ATAPI_REG_LOCNT 4 /* LSB of transfer count */
#define ATAPI_REG_HICNT 5 /* MSB of transfer count */
/* ATA command bytes */
#define ATA_CMD_READ 0x20 /* read sectors */
#define ATA_CMD_PKT 0xA0 /* signals ATAPI packet command */
#define ATA_CMD_PID 0xA1 /* identify ATAPI device */
#define ATA_CMD_READMULT 0xC4 /* read sectors, one interrupt */
#define ATA_CMD_MULTMODE 0xC6
#define ATA_CMD_ID 0xEC /* identify ATA device */
typedef struct
{
/* generic block device info */
blkdev_t blkdev;
/* information specific to IDE drive */
unsigned has_lba : 1;
unsigned use_lba : 1;
unsigned has_dma : 1;
unsigned use_dma : 1;
unsigned has_multmode : 1;
unsigned use_multmode : 1;
unsigned short mult_count;
} ide_t;
/*****************************************************************************
BUSY READY DF DSC DRQ1 CORR IDX ERR
*****************************************************************************/
static int ide_poll_status(unsigned short ioadr, unsigned timeout,
unsigned char stat_mask, unsigned char stat_bits)
{
unsigned time, decr;
unsigned char stat = 0;
DEBUG(printf("ide_poll_status: ioadr=0x%X, stat mask=0x%X, "
"stat bits=0x%X\n", ioadr, stat_mask, stat_bits);)
/* if short timeout, poll every 1 millisecond, else every 50 ms.
If the host OS has a real-time scheduler, and that scheudler runs
every 50 ms, you could do yield() instead of msleep(50) */
decr = (timeout < 500) ? 1 : 50;
for(time = timeout; time != 0; time -= decr)
{
stat = inportb(ioadr + ATA_REG_STATUS);
if((stat & stat_mask) == stat_bits)
break;
msleep(decr);
}
DEBUG(printf("ide_poll_status: waited %3u ms, got stat=0x%X\n",
timeout - time, stat);)
if(time == 0)
{
printf("ide_poll_status: error: timeout, stat=0x%X\n", stat);
return -1;
}
return 0;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -