ATAPI: Difference between revisions

1,186 bytes removed ,  1 year ago
Made a better and working atapi reader code that can read multiple sectors :)
[unchecked revision][unchecked revision]
(Made a better and working atapi reader code that can read multiple sectors :))
Line 269:
|}
 
#define DATA 0
==x86 Examples==
#define ERROR_R 1
#define SECTOR_COUNT 2
#define LBA_LOW 3
#define LBA_MID 4
#define LBA_HIGH 5
#define DRIVE_SELECT 6
#define COMMAND_REGISTER 7
 
#define CONTROL 0x206
Here is an example adapted from a working driver implementation.
 
#define ALTERNATE_STATUS 0
<source lang="c">
/* The default and seemingly universal sector size for CD-ROMs. */
#define ATAPI_SECTOR_SIZE 2048
 
static __inline void insw(uint16_t __port, void *__buf, unsigned long __n) {
/* The default ISA IRQ numbers of the ATA controllers. */
__asm__ __volatile__("cld; rep; insw"
#define ATA_IRQ_PRIMARY 0x0E
: "+D"(__buf), "+c"(__n)
#define ATA_IRQ_SECONDARY 0x0F
: "d"(__port));
}
 
static __inline__ void outsw(uint16_t __port, const void *__buf, unsigned long __n) {
/* The necessary I/O ports, indexed by "bus". */
__asm__ __volatile__("cld; rep; outsw"
#define ATA_DATA(x) (x)
: "+S"(__buf), "+c"(__n)
#define ATA_FEATURES(x) (x+1)
: "d"(__port));
#define ATA_SECTOR_COUNT(x) (x+2)
}
#define ATA_ADDRESS1(x) (x+3)
#define ATA_ADDRESS2(x) (x+4)
#define ATA_ADDRESS3(x) (x+5)
#define ATA_DRIVE_SELECT(x) (x+6)
#define ATA_COMMAND(x) (x+7)
#define ATA_DCR(x) (x+0x206) /* device control register */
 
static void ata_io_wait(const uint8_t p) {
/* valid values for "bus" */
inb(p + CONTROL + ALTERNATE_STATUS);
#define ATA_BUS_PRIMARY 0x1F0
inb(p + CONTROL + ALTERNATE_STATUS);
#define ATA_BUS_SECONDARY 0x170
inb(p + CONTROL + ALTERNATE_STATUS);
/* valid values for "drive" */
inb(p + CONTROL + ALTERNATE_STATUS);
#define ATA_DRIVE_MASTER 0xA0
}
#define ATA_DRIVE_SLAVE 0xB0
 
// Reads sectors starting from lba to buffer
/* ATA specifies a 400ns delay after drive switching -- often
int read_cdrom(uint16_t port, bool slave, uint32_t lba, uint32_t sectors, uint16_t *buffer) {
* implemented as 4 Alternative Status queries. */
volatile uint8_t read_cmd[12] = {0xA8, 0,
#define ATA_SELECT_DELAY(bus) \
(lba >> 0x18) & 0xFF, (lba >> 0x10) & 0xFF, (lba >> 0x08) & 0xFF,
{inb(ATA_DCR(bus));inb(ATA_DCR(bus));inb(ATA_DCR(bus));inb(ATA_DCR(bus));}
(lba >> 0x00) & 0xFF,
(sectors >> 0x18) & 0xFF, (sectors >> 0x10) & 0xFF, (sectors >> 0x08) & 0xFF,
(sectors >> 0x00) & 0xFF,
0, 0};
 
outb(port + DRIVE_SELECT, 0xA0 & (slave << 4));
/* Use the ATAPI protocol to read a single sector from the given
ata_io_wait(port);
* bus/drive into the buffer using logical block address lba. */
outb(port + ERROR_R, 0x00);
int
outb(port + LBA_MID, 2048 & 0xFF);
atapi_drive_read_sector (uint32_t bus, uint32_t drive, uint32_t lba, uint8_t *buffer)
outb(port + LBA_HIGH, 2048 >> 8);
{
outb(port + COMMAND_REGISTER, 0xA0); // Packet command
/* 0xA8 is READ SECTORS command byte. */
ata_io_wait(port);
uint8_t read_cmd[12] = { 0xA8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
 
uint8_t status;
while (1) {
int size;
uint8_t status = inb(port + COMMAND_REGISTER);
/* Tell the scheduler that this process is using the ATA subsystem. */
if ((status & 0x01) == 1)
ata_grab ();
return 1;
/* Select drive (only the slave-bit is set) */
if (!(status & 0x80) && (status & 0x08))
outb (drive & (1 << 4), ATA_DRIVE_SELECT (bus));
break;
ATA_SELECT_DELAY (bus); /* 400ns delay */
ata_io_wait(port);
outb (0x0, ATA_FEATURES (bus)); /* PIO mode */
outb (ATAPI_SECTOR_SIZE & 0xFF, ATA_ADDRESS2 (bus));
outb (ATAPI_SECTOR_SIZE >> 8, ATA_ADDRESS3 (bus));
outb (0xA0, ATA_COMMAND (bus)); /* ATA PACKET command */
while ((status = inb (ATA_COMMAND (bus))) & 0x80) /* BUSY */
asm volatile ("pause");
while (!((status = inb (ATA_COMMAND (bus))) & 0x8) && !(status & 0x1))
asm volatile ("pause");
/* DRQ or ERROR set */
if (status & 0x1) {
size = -1;
goto cleanup;
}
 
read_cmd[9] = 1; /* 1 sector */
outsw(port + DATA, (uint16_t *) read_cmd, 6);
read_cmd[2] = (lba >> 0x18) & 0xFF; /* most sig. byte of LBA */
 
read_cmd[3] = (lba >> 0x10) & 0xFF;
for (uint32_t i = 0; i < sectors; i++) {
read_cmd[4] = (lba >> 0x08) & 0xFF;
while (1) {
read_cmd[5] = (lba >> 0x00) & 0xFF; /* least sig. byte of LBA */
uint8_t status = inb(port + COMMAND_REGISTER);
/* Send ATAPI/SCSI command */
if (status & 0x01)
outsw (ATA_DATA (bus), (uint16_t *) read_cmd, 6);
return 1;
/* Wait for IRQ that says the data is ready. */
if (!(status & 0x80) && (status & 0x08))
schedule ();
break;
/* Read actual size */
}
size =
 
(((int) inb (ATA_ADDRESS3 (bus))) << 8) |
( int) (size = inb (ATA_ADDRESS2port (bus+ LBA_HIGH))); << 8
| inb(port + LBA_MID);
/* This example code only supports the case where the data transfer
 
* of one sector is done in one step. */
insw(port + DATA, (uint16_t *) ((uint8_t *) buffer + i * 0x800), size / 2);
ASSERT (size == ATAPI_SECTOR_SIZE);
}
/* Read data. */
 
insw (ATA_DATA (bus), buffer, size / 2);
return 0;
/* The controller will send another IRQ even though we've read all
* the data we want. Wait for it -- so it doesn't interfere with
* subsequent operations: */
schedule ();
/* Wait for BSY and DRQ to clear, indicating Command Finished */
while ((status = inb (ATA_COMMAND (bus))) & 0x88)
asm volatile ("pause");
cleanup:
/* Exit the ATA subsystem */
ata_release ();
return size;
}
</source>
 
==Detecting a Medium's Capacity==
20

edits