[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[1953] 2009-01-22 Christian Franke <address@hidden>
From: |
Christian Franke |
Subject: |
[1953] 2009-01-22 Christian Franke <address@hidden> |
Date: |
Thu, 22 Jan 2009 20:15:05 +0000 |
Revision: 1953
http://svn.sv.gnu.org/viewvc/?view=rev&root=grub&revision=1953
Author: chrfranke
Date: 2009-01-22 20:15:05 +0000 (Thu, 22 Jan 2009)
Log Message:
-----------
2009-01-22 Christian Franke <address@hidden>
* disk/ata.c (grub_ata_wait_status): Replace by ...
(grub_ata_wait_not_busy): ... this function. Checks only BSY bit,
other status bits may be invalid while BSY is asserted.
(grub_ata_check_ready): New function.
(grub_ata_cmd): Removed.
(grub_ata_wait_drq): New function.
(grub_ata_strncpy): Remove inline.
(grub_ata_pio_read): Reduce to actual block transfer. BSY wait
and error check now done by grub_ata_wait_drq ().
(grub_ata_pio_write): Likewise.
(grub_atapi_identify): Set DEV before check for !BSY. Use
grub_ata_wait_drq () to wait for data.
(grub_ata_device_initialize): Add status register check to
detect missing SATA slave devices. Add debug messages.
(grub_atapi_wait_drq): Use grub_ata_wait_not_busy ().
(grub_atapi_packet): Set DEV before check for !BSY. Replace
transfer loop by grub_ata_pio_write ().
(grub_ata_identify): Set DEV before check for !BSY. Use
grub_ata_wait_drq () to wait for data.
(grub_ata_setaddress): Set DEV before check for !BSY.
(grub_ata_readwrite): Remove duplicate code, handle batch/rest and
read/write in one loop. Fix invalid command on write. Fix incomplete
command on (size % batch) == 0. Add missing error check after write of
last block. Add debug messages.
(grub_atapi_read): Replace transfer loop by grub_ata_pio_read ().
Modified Paths:
--------------
trunk/grub2/ChangeLog
trunk/grub2/disk/ata.c
Modified: trunk/grub2/ChangeLog
===================================================================
--- trunk/grub2/ChangeLog 2009-01-19 20:39:57 UTC (rev 1952)
+++ trunk/grub2/ChangeLog 2009-01-22 20:15:05 UTC (rev 1953)
@@ -1,3 +1,31 @@
+2009-01-22 Christian Franke <address@hidden>
+
+ * disk/ata.c (grub_ata_wait_status): Replace by ...
+ (grub_ata_wait_not_busy): ... this function. Checks only BSY bit,
+ other status bits may be invalid while BSY is asserted.
+ (grub_ata_check_ready): New function.
+ (grub_ata_cmd): Removed.
+ (grub_ata_wait_drq): New function.
+ (grub_ata_strncpy): Remove inline.
+ (grub_ata_pio_read): Reduce to actual block transfer. BSY wait
+ and error check now done by grub_ata_wait_drq ().
+ (grub_ata_pio_write): Likewise.
+ (grub_atapi_identify): Set DEV before check for !BSY. Use
+ grub_ata_wait_drq () to wait for data.
+ (grub_ata_device_initialize): Add status register check to
+ detect missing SATA slave devices. Add debug messages.
+ (grub_atapi_wait_drq): Use grub_ata_wait_not_busy ().
+ (grub_atapi_packet): Set DEV before check for !BSY. Replace
+ transfer loop by grub_ata_pio_write ().
+ (grub_ata_identify): Set DEV before check for !BSY. Use
+ grub_ata_wait_drq () to wait for data.
+ (grub_ata_setaddress): Set DEV before check for !BSY.
+ (grub_ata_readwrite): Remove duplicate code, handle batch/rest and
+ read/write in one loop. Fix invalid command on write. Fix incomplete
+ command on (size % batch) == 0. Add missing error check after write of
+ last block. Add debug messages.
+ (grub_atapi_read): Replace transfer loop by grub_ata_pio_read ().
+
2009-01-19 Christian Franke <address@hidden>
* disk/ata.c (GRUB_ATAPI_REG_*): New defines.
Modified: trunk/grub2/disk/ata.c
===================================================================
--- trunk/grub2/disk/ata.c 2009-01-19 20:39:57 UTC (rev 1952)
+++ trunk/grub2/disk/ata.c 2009-01-22 20:15:05 UTC (rev 1953)
@@ -152,25 +152,29 @@
return grub_inb (dev->ioaddress2 + reg);
}
-static inline grub_err_t
-grub_ata_wait_status (struct grub_ata_device *dev,
- grub_uint8_t maskset, grub_uint8_t maskclear,
- int milliseconds)
+/* Wait for !BSY. */
+static grub_err_t
+grub_ata_wait_not_busy (struct grub_ata_device *dev, int milliseconds)
{
- int i;
+ /* ATA requires 400ns (after a write to CMD register) or
+ 1 PIO cycle (after a DRQ block transfer) before
+ first check of BSY. */
+ grub_millisleep (1);
- for (i = 0; i < milliseconds; i++)
+ int i = 1;
+ while (grub_ata_regget (dev, GRUB_ATA_REG_STATUS) & GRUB_ATA_STATUS_BUSY)
{
- grub_uint8_t reg;
+ if (i >= milliseconds)
+ {
+ grub_dprintf ("ata", "timeout: %dms\n", milliseconds);
+ return grub_error (GRUB_ERR_TIMEOUT, "ATA timeout");
+ }
- reg = grub_ata_regget (dev, GRUB_ATA_REG_STATUS);
- if ((reg & maskset) == maskset && (reg & maskclear) == 0)
- return GRUB_ERR_NONE;
-
grub_millisleep (1);
+ i++;
}
- return grub_error (GRUB_ERR_TIMEOUT, "ata timeout");
+ return GRUB_ERR_NONE;
}
static inline void
@@ -179,19 +183,42 @@
grub_millisleep (50);
}
+/* Check for !BSY before issuing a new command. */
+static inline grub_err_t
+grub_ata_check_ready (struct grub_ata_device *dev)
+{
+ if (grub_ata_regget (dev, GRUB_ATA_REG_STATUS) & GRUB_ATA_STATUS_BUSY)
+ return grub_ata_wait_not_busy (dev, GRUB_ATA_TOUT_STD);
+
+ return GRUB_ERR_NONE;
+}
+
+/* Wait for !BSY, DRQ. */
static grub_err_t
-grub_ata_cmd (struct grub_ata_device *dev, int cmd)
+grub_ata_wait_drq (struct grub_ata_device *dev, int rw,
+ int milliseconds)
{
- if (grub_ata_wait_status (dev, 0, GRUB_ATA_STATUS_BUSY, GRUB_ATA_TOUT_STD))
+ if (grub_ata_wait_not_busy (dev, milliseconds))
return grub_errno;
- grub_ata_regset (dev, GRUB_ATA_REG_CMD, cmd);
+ /* !DRQ implies error condition. */
+ grub_uint8_t sts = grub_ata_regget (dev, GRUB_ATA_REG_STATUS);
+ if ((sts & (GRUB_ATA_STATUS_DRQ | GRUB_ATA_STATUS_ERR))
+ != GRUB_ATA_STATUS_DRQ)
+ {
+ grub_dprintf ("ata", "ata error: status=0x%x, error=0x%x\n",
+ sts, grub_ata_regget (dev, GRUB_ATA_REG_ERROR));
+ if (! rw)
+ return grub_error (GRUB_ERR_READ_ERROR, "ATA read error");
+ else
+ return grub_error (GRUB_ERR_WRITE_ERROR, "ATA write error");
+ }
return GRUB_ERR_NONE;
}
/* Byteorder has to be changed before strings can be read. */
-static inline void
+static void
grub_ata_strncpy (char *dst, char *src, grub_size_t len)
{
grub_uint16_t *src16 = (grub_uint16_t *) src;
@@ -203,52 +230,26 @@
dst[len] = '\0';
}
-static grub_err_t
-grub_ata_pio_read (struct grub_ata_device *dev, char *buf,
- grub_size_t size, int milliseconds)
+static void
+grub_ata_pio_read (struct grub_ata_device *dev, char *buf, grub_size_t size)
{
grub_uint16_t *buf16 = (grub_uint16_t *) buf;
unsigned int i;
- if (grub_ata_regget (dev, GRUB_ATA_REG_STATUS) & GRUB_ATA_STATUS_ERR)
- return grub_error (GRUB_ERR_READ_ERROR, "ATA read error");
-
- /* Wait until the data is available. */
- if (grub_ata_wait_status (dev, GRUB_ATA_STATUS_DRQ, 0, milliseconds))
- return grub_errno;;
-
/* Read in the data, word by word. */
for (i = 0; i < size / 2; i++)
buf16[i] = grub_le_to_cpu16 (grub_inw(dev->ioaddress + GRUB_ATA_REG_DATA));
-
- if (grub_ata_regget (dev, GRUB_ATA_REG_STATUS) & GRUB_ATA_STATUS_ERR)
- return grub_error (GRUB_ERR_READ_ERROR, "ATA read error");
-
- return GRUB_ERR_NONE;
}
-static grub_err_t
-grub_ata_pio_write (struct grub_ata_device *dev, char *buf,
- grub_size_t size, int milliseconds)
+static void
+grub_ata_pio_write (struct grub_ata_device *dev, char *buf, grub_size_t size)
{
grub_uint16_t *buf16 = (grub_uint16_t *) buf;
unsigned int i;
- if (grub_ata_regget (dev, GRUB_ATA_REG_STATUS) & GRUB_ATA_STATUS_ERR)
- return grub_error (GRUB_ERR_WRITE_ERROR, "ATA write error");
-
- /* Wait until drive is ready to read data. */
- if (grub_ata_wait_status (dev, GRUB_ATA_STATUS_DRQ, 0, milliseconds))
- return grub_errno;
-
/* Write the data, word by word. */
for (i = 0; i < size / 2; i++)
grub_outw(grub_cpu_to_le16 (buf16[i]), dev->ioaddress + GRUB_ATA_REG_DATA);
-
- if (grub_ata_regget (dev, GRUB_ATA_REG_STATUS) & GRUB_ATA_STATUS_ERR)
- return grub_error (GRUB_ERR_WRITE_ERROR, "ATA write error");
-
- return GRUB_ERR_NONE;
}
static void
@@ -280,26 +281,22 @@
if (! info)
return grub_errno;
- if (grub_ata_wait_status (dev, 0, GRUB_ATA_STATUS_BUSY, GRUB_ATA_TOUT_STD))
- {
- grub_free (info);
- return grub_errno;
- }
-
grub_ata_regset (dev, GRUB_ATA_REG_DISK, 0xE0 | dev->device << 4);
-
- if (grub_ata_cmd (dev, GRUB_ATA_CMD_IDENTIFY_PACKET_DEVICE))
+ if (grub_ata_check_ready (dev))
{
grub_free (info);
return grub_errno;
}
+
+ grub_ata_regset (dev, GRUB_ATA_REG_CMD, GRUB_ATA_CMD_IDENTIFY_PACKET_DEVICE);
grub_ata_wait ();
- if (grub_ata_pio_read (dev, info, GRUB_DISK_SECTOR_SIZE, GRUB_ATA_TOUT_STD))
+ if (grub_ata_wait_drq (dev, 0, GRUB_ATA_TOUT_STD))
{
grub_free (info);
return grub_errno;
}
+ grub_ata_pio_read (dev, info, GRUB_DISK_SECTOR_SIZE);
dev->atapi = 1;
@@ -315,9 +312,8 @@
grub_uint8_t ireason,
int milliseconds)
{
- grub_millisleep (1); /* ATA allows 400ns to assert BSY. */
-
- if (grub_ata_wait_status (dev, 0, GRUB_ATA_STATUS_BUSY, milliseconds))
+ /* Wait for !BSY, DRQ, ireason */
+ if (grub_ata_wait_not_busy (dev, milliseconds))
return grub_errno;
grub_uint8_t sts = grub_ata_regget (dev, GRUB_ATA_REG_STATUS);
@@ -328,9 +324,9 @@
&& (irs & GRUB_ATAPI_IREASON_MASK) == ireason)
return GRUB_ERR_NONE;
- /* No DRQ implies error condition. */
- grub_dprintf("ata", "atapi error: status=0x%x, ireason=0x%x, error=0x%x\n",
- sts, irs, grub_ata_regget (dev, GRUB_ATA_REG_ERROR));
+ /* !DRQ implies error condition. */
+ grub_dprintf ("ata", "atapi error: status=0x%x, ireason=0x%x, error=0x%x\n",
+ sts, irs, grub_ata_regget (dev, GRUB_ATA_REG_ERROR));
if (! (sts & GRUB_ATA_STATUS_DRQ)
&& (irs & GRUB_ATAPI_IREASON_MASK) == GRUB_ATAPI_IREASON_ERROR)
@@ -348,11 +344,11 @@
grub_atapi_packet (struct grub_ata_device *dev, char *packet,
grub_size_t size)
{
- if (grub_ata_wait_status(dev, 0, GRUB_ATA_STATUS_BUSY, GRUB_ATA_TOUT_STD))
+ grub_ata_regset (dev, GRUB_ATA_REG_DISK, dev->device << 4);
+ if (grub_ata_check_ready (dev))
return grub_errno;
/* Send ATA PACKET command. */
- grub_ata_regset (dev, GRUB_ATA_REG_DISK, dev->device << 4);
grub_ata_regset (dev, GRUB_ATA_REG_FEATURES, 0);
grub_ata_regset (dev, GRUB_ATAPI_REG_IREASON, 0);
grub_ata_regset (dev, GRUB_ATAPI_REG_CNTHIGH, size >> 8);
@@ -365,10 +361,7 @@
return grub_errno;
/* Write the packet. */
- grub_uint16_t *buf16 = (grub_uint16_t *) packet;
- unsigned i;
- for (i = 0; i < 12 / 2; i++)
- grub_outw (grub_cpu_to_le16 (buf16[i]), dev->ioaddress +
GRUB_ATA_REG_DATA);
+ grub_ata_pio_write (dev, packet, 12);
return GRUB_ERR_NONE;
}
@@ -385,21 +378,17 @@
info16 = (grub_uint16_t *) info;
- if (grub_ata_wait_status (dev, 0, GRUB_ATA_STATUS_BUSY, GRUB_ATA_TOUT_STD))
- {
- grub_free (info);
- return grub_errno;
- }
-
grub_ata_regset (dev, GRUB_ATA_REG_DISK, 0xE0 | dev->device << 4);
- if (grub_ata_cmd (dev, GRUB_ATA_CMD_IDENTIFY_DEVICE))
+ if (grub_ata_check_ready (dev))
{
grub_free (info);
return grub_errno;
}
+
+ grub_ata_regset (dev, GRUB_ATA_REG_CMD, GRUB_ATA_CMD_IDENTIFY_DEVICE);
grub_ata_wait ();
- if (grub_ata_pio_read (dev, info, GRUB_DISK_SECTOR_SIZE, GRUB_ATA_TOUT_STD))
+ if (grub_ata_wait_drq (dev, 0, GRUB_ATA_TOUT_STD))
{
if (grub_ata_regget (dev, GRUB_ATA_REG_ERROR) & 0x04) /* ABRT */
{
@@ -417,6 +406,8 @@
}
}
+ grub_ata_pio_read (dev, info, GRUB_DISK_SECTOR_SIZE);
+
/* Now it is certain that this is not an ATAPI device. */
dev->atapi = 0;
@@ -471,14 +462,31 @@
dev->ioaddress2 = addr2;
dev->next = NULL;
- /* Try to detect if the port is in use by writing to it,
- waiting for a while and reading it again. If the value
- was preserved, there is a device connected. */
grub_ata_regset (dev, GRUB_ATA_REG_DISK, dev->device << 4);
grub_ata_wait ();
+
+ /* If status is 0x00, it is safe to assume that there
+ is no device (or only a !READY) device connected. */
+ grub_int8_t sts = grub_ata_regget (dev, GRUB_ATA_REG_STATUS);
+ grub_dprintf ("ata", "status=0x%x\n", sts);
+ if (sts == 0x00)
+ {
+ grub_free(dev);
+ return 0;
+ }
+
+ /* Try to detect if the port is in use by writing to it,
+ waiting for a while and reading it again. If the value
+ was preserved, there is a device connected.
+ But this tests often detects a second (slave) device
+ connected to a SATA controller which supports only one
+ (master) device. In this case, the status register
+ check above usually works. */
grub_ata_regset (dev, GRUB_ATA_REG_SECTORS, 0x5A);
grub_ata_wait ();
- if (grub_ata_regget (dev, GRUB_ATA_REG_SECTORS) != 0x5A)
+ grub_int8_t sec = grub_ata_regget (dev, GRUB_ATA_REG_SECTORS);
+ grub_dprintf ("ata", "sectors=0x%x\n", sec);
+ if (sec != 0x5A)
{
grub_free(dev);
return 0;
@@ -612,9 +620,6 @@
grub_disk_addr_t sector,
grub_size_t size)
{
- if (grub_ata_wait_status (dev, 0, GRUB_ATA_STATUS_BUSY, GRUB_ATA_TOUT_STD))
- return grub_errno;
-
switch (addressing)
{
case GRUB_ATA_CHS:
@@ -637,6 +642,9 @@
"using CHS addressing", sector);
grub_ata_regset (dev, GRUB_ATA_REG_DISK, (dev->device << 4) | head);
+ if (grub_ata_check_ready (dev))
+ return grub_errno;
+
grub_ata_regset (dev, GRUB_ATA_REG_SECTNUM, sect);
grub_ata_regset (dev, GRUB_ATA_REG_CYLLSB, cylinder & 0xFF);
grub_ata_regset (dev, GRUB_ATA_REG_CYLMSB, cylinder >> 8);
@@ -649,6 +657,9 @@
size = 0;
grub_ata_regset (dev, GRUB_ATA_REG_DISK,
0xE0 | (dev->device << 4) | ((sector >> 24) & 0x0F));
+ if (grub_ata_check_ready (dev))
+ return grub_errno;
+
grub_ata_setlba (dev, sector, size);
break;
@@ -657,6 +668,9 @@
size = 0;
grub_ata_regset (dev, GRUB_ATA_REG_DISK, 0xE0 | (dev->device << 4));
+ if (grub_ata_check_ready (dev))
+ return grub_errno;
+
/* Set "Previous". */
grub_ata_setlba (dev, sector >> 24, size >> 8);
/* Set "Current". */
@@ -673,15 +687,13 @@
grub_size_t size, char *buf, int rw)
{
struct grub_ata_device *dev = (struct grub_ata_device *) disk->data;
- grub_size_t cnt;
- grub_size_t batch;
- grub_ata_addressing_t addressing;
- int cmd;
- int cmd_write;
- unsigned int sect;
- addressing = dev->addr;
+ grub_dprintf("ata", "grub_ata_readwrite (size=%u, rw=%d)\n", size, rw);
+ grub_ata_addressing_t addressing = dev->addr;
+ grub_size_t batch;
+ int cmd, cmd_write;
+
if (addressing == GRUB_ATA_LBA48 && ((sector + size) >> 28) != 0)
{
batch = 65536;
@@ -697,89 +709,49 @@
cmd_write = GRUB_ATA_CMD_WRITE_SECTORS;
}
- cnt = size / batch;
-
- /* Read/write batches of 256/65536 sectors, when more than 256/65536
- sectors should be read/written. */
- for (; cnt; cnt--)
+ grub_size_t nsectors = 0;
+ while (nsectors < size)
{
+ if (size - nsectors < batch)
+ batch = size - nsectors;
+
+ grub_dprintf("ata", "rw=%d, sector=%llu, batch=%u\n", rw, sector, batch);
+
+ /* Send read/write commmand. */
if (grub_ata_setaddress (dev, addressing, sector, batch))
return grub_errno;
- if (rw == 0)
- {
- /* Read 256/65536 sectors. */
- if (grub_ata_cmd (dev, cmd))
- return grub_errno;
+ grub_ata_regset (dev, GRUB_ATA_REG_CMD, (! rw ? cmd : cmd_write));
- /* Wait for the command to complete. */
- if (grub_ata_wait_status (dev, 0, GRUB_ATA_STATUS_BUSY,
GRUB_ATA_TOUT_DATA))
- return grub_errno;
-
- for (sect = 0; sect < batch; sect++)
- {
- if (grub_ata_pio_read (dev, buf,
- GRUB_DISK_SECTOR_SIZE, GRUB_ATA_TOUT_DATA))
- return grub_errno;
- buf += GRUB_DISK_SECTOR_SIZE;
- }
- }
- else
+ unsigned sect;
+ for (sect = 0; sect < batch; sect++)
{
- /* Write 256/65536 sectors. */
- if (grub_ata_cmd (dev, cmd))
+ /* Wait for !BSY, DRQ. */
+ if (grub_ata_wait_drq (dev, rw, GRUB_ATA_TOUT_DATA))
return grub_errno;
- /* Wait for the command to complete. */
- if (grub_ata_wait_status (dev, 0, GRUB_ATA_STATUS_BUSY,
GRUB_ATA_TOUT_DATA))
- return grub_errno;
+ /* Transfer data. */
+ if (! rw)
+ grub_ata_pio_read (dev, buf, GRUB_DISK_SECTOR_SIZE);
+ else
+ grub_ata_pio_write (dev, buf, GRUB_DISK_SECTOR_SIZE);
- for (sect = 0; sect < batch; sect++)
- {
- if (grub_ata_pio_write (dev, buf,
- GRUB_DISK_SECTOR_SIZE,
GRUB_ATA_TOUT_DATA))
- return grub_error (GRUB_ERR_WRITE_ERROR, "ATA write error");
- buf += GRUB_DISK_SECTOR_SIZE;
- }
+ buf += GRUB_DISK_SECTOR_SIZE;
}
- sector += batch;
- }
- /* Read/write just a "few" sectors. */
- if (grub_ata_setaddress (dev, addressing, sector, size % batch))
- return grub_errno;
-
- if (rw == 0)
- {
- /* Read sectors. */
- if (grub_ata_cmd (dev, cmd))
- return grub_errno;
-
- /* Wait for the command to complete. */
- if (grub_ata_wait_status (dev, 0, GRUB_ATA_STATUS_BUSY,
GRUB_ATA_TOUT_DATA))
- return grub_errno;
-
- for (sect = 0; sect < (size % batch); sect++)
- {
- if (grub_ata_pio_read (dev, buf, GRUB_DISK_SECTOR_SIZE,
GRUB_ATA_TOUT_DATA))
+ if (rw)
+ {
+ /* Check for write error. */
+ if (grub_ata_wait_not_busy (dev, GRUB_ATA_TOUT_DATA))
return grub_errno;
- buf += GRUB_DISK_SECTOR_SIZE;
- }
- } else {
- /* Write sectors. */
- if (grub_ata_cmd (dev, cmd))
- return grub_errno;
- /* Wait for the command to complete. */
- if (grub_ata_wait_status (dev, 0, GRUB_ATA_STATUS_BUSY,
GRUB_ATA_TOUT_DATA))
- return grub_errno;
-
- for (sect = 0; sect < (size % batch); sect++)
- {
- if (grub_ata_pio_write (dev, buf, GRUB_DISK_SECTOR_SIZE,
GRUB_ATA_TOUT_DATA))
+ if (grub_ata_regget (dev, GRUB_ATA_REG_STATUS)
+ & (GRUB_ATA_STATUS_DRQ | GRUB_ATA_STATUS_ERR))
return grub_error (GRUB_ERR_WRITE_ERROR, "ATA write error");
- buf += GRUB_DISK_SECTOR_SIZE;
}
+
+ sector += batch;
+ nsectors += batch;
}
return GRUB_ERR_NONE;
@@ -924,10 +896,7 @@
return grub_error (GRUB_ERR_READ_ERROR, "Invalid ATAPI transfer count");
/* Read the data. */
- grub_uint16_t *buf16 = (grub_uint16_t *) (buf + nread);
- unsigned i;
- for (i = 0; i < cnt / 2; i++)
- buf16[i] = grub_le_to_cpu16 (grub_inw (dev->ioaddress +
GRUB_ATA_REG_DATA));
+ grub_ata_pio_read (dev, buf + nread, cnt);
if (cnt & 1)
buf[nread + cnt - 1] = (char) grub_le_to_cpu16 (grub_inw
(dev->ioaddress + GRUB_ATA_REG_DATA));
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [1953] 2009-01-22 Christian Franke <address@hidden>,
Christian Franke <=