[1952] 2009-01-19 Christian Franke <[email protected]>

Christian Franke <[email protected]>
Newsgroups gmane.comp.boot-loaders.grub.cvs
Message-ID <[email protected]>
Revision: 1952
          http://svn.sv.gnu.org/viewvc/?view=rev&root=grub&revision=1952
Author:   chrfranke
Date:     2009-01-19 20:39:57 +0000 (Mon, 19 Jan 2009)

Log Message:
-----------
2009-01-19  Christian Franke  <[email protected]>

	* disk/ata.c (GRUB_ATAPI_REG_*): New defines.
	(GRUB_ATAPI_IREASON_*): Likewise.
	(grub_ata_pio_write): Fix timeout error return.
	(grub_atapi_identify): Add grub_ata_wait () after cmd.
	(grub_atapi_wait_drq): New function.
	(grub_atapi_packet): New parameter `size'.
	Use grub_atapi_wait_drq () and direct write instead of
	grub_ata_pio_write ().
	(grub_atapi_read): Replace grub_ata_pio_read () by a loop which
	reads the number of bytes requested by the device for each DRQ
	assertion.
	(grub_atapi_write): Remove old implementation, return not
	implemented instead.

Modified Paths:
--------------
    trunk/grub2/ChangeLog
    trunk/grub2/disk/ata.c

Modified: trunk/grub2/ChangeLog
===================================================================
--- trunk/grub2/ChangeLog	2009-01-19 20:27:54 UTC (rev 1951)
+++ trunk/grub2/ChangeLog	2009-01-19 20:39:57 UTC (rev 1952)
@@ -1,5 +1,21 @@
 2009-01-19  Christian Franke  <[email protected]>
 
+	* disk/ata.c (GRUB_ATAPI_REG_*): New defines.
+	(GRUB_ATAPI_IREASON_*): Likewise.
+	(grub_ata_pio_write): Fix timeout error return.
+	(grub_atapi_identify): Add grub_ata_wait () after cmd.
+	(grub_atapi_wait_drq): New function.
+	(grub_atapi_packet): New parameter `size'.
+	Use grub_atapi_wait_drq () and direct write instead of
+	grub_ata_pio_write ().
+	(grub_atapi_read): Replace grub_ata_pio_read () by a loop which
+	reads the number of bytes requested by the device for each DRQ
+	assertion.
+	(grub_atapi_write): Remove old implementation, return not
+	implemented instead.
+
+2009-01-19  Christian Franke  <[email protected]>
+
 	* disk/scsi.c (grub_scsi_read10): Use scsi->blocksize instead
 	of 512 to calculate data size.
 	(grub_scsi_read12): Likewise.

Modified: trunk/grub2/disk/ata.c
===================================================================
--- trunk/grub2/disk/ata.c	2009-01-19 20:27:54 UTC (rev 1951)
+++ trunk/grub2/disk/ata.c	2009-01-19 20:39:57 UTC (rev 1952)
@@ -44,12 +44,15 @@
 #define GRUB_ATA_REG_ERROR	1
 #define GRUB_ATA_REG_FEATURES	1
 #define GRUB_ATA_REG_SECTORS	2
+#define GRUB_ATAPI_REG_IREASON	2
 #define GRUB_ATA_REG_SECTNUM	3
 #define GRUB_ATA_REG_CYLLSB	4
 #define GRUB_ATA_REG_CYLMSB	5
 #define GRUB_ATA_REG_LBALOW	3
 #define GRUB_ATA_REG_LBAMID	4
+#define GRUB_ATAPI_REG_CNTLOW	4
 #define GRUB_ATA_REG_LBAHIGH	5
+#define GRUB_ATAPI_REG_CNTHIGH	5
 #define GRUB_ATA_REG_DISK	6
 #define GRUB_ATA_REG_CMD	7
 #define GRUB_ATA_REG_STATUS	7
@@ -65,6 +68,13 @@
 #define GRUB_ATA_STATUS_READY	0x40
 #define GRUB_ATA_STATUS_BUSY	0x80
 
+/* ATAPI interrupt reason values (I/O, D/C bits).  */
+#define GRUB_ATAPI_IREASON_MASK     0x3
+#define GRUB_ATAPI_IREASON_DATA_OUT 0x0
+#define GRUB_ATAPI_IREASON_CMD_OUT  0x1
+#define GRUB_ATAPI_IREASON_DATA_IN  0x2
+#define GRUB_ATAPI_IREASON_ERROR    0x3
+
 enum grub_ata_commands
   {
     GRUB_ATA_CMD_READ_SECTORS = 0x20,
@@ -229,7 +239,7 @@
 
   /* Wait until drive is ready to read data.  */
   if (grub_ata_wait_status (dev, GRUB_ATA_STATUS_DRQ, 0, milliseconds))
-    return 0;
+    return grub_errno;
 
   /* Write the data, word by word.  */
   for (i = 0; i < size / 2; i++)
@@ -283,6 +293,7 @@
       grub_free (info);
       return grub_errno;
     }
+  grub_ata_wait ();
 
   if (grub_ata_pio_read (dev, info, GRUB_DISK_SECTOR_SIZE, GRUB_ATA_TOUT_STD))
     {
@@ -300,21 +311,65 @@
 }
 
 static grub_err_t
-grub_atapi_packet (struct grub_ata_device *dev, char *packet)
+grub_atapi_wait_drq (struct grub_ata_device *dev,
+		     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))
+    return grub_errno;
+
+  grub_uint8_t sts = grub_ata_regget (dev, GRUB_ATA_REG_STATUS);
+  grub_uint8_t irs = grub_ata_regget (dev, GRUB_ATAPI_REG_IREASON);
+
+  /* OK if DRQ is asserted and interrupt reason is as expected.  */
+  if ((sts & GRUB_ATA_STATUS_DRQ)
+      && (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));
+
+  if (! (sts & GRUB_ATA_STATUS_DRQ)
+      && (irs & GRUB_ATAPI_IREASON_MASK) == GRUB_ATAPI_IREASON_ERROR)
+    {
+      if (ireason == GRUB_ATAPI_IREASON_CMD_OUT)
+	return grub_error (GRUB_ERR_READ_ERROR, "ATA PACKET command error");
+      else
+	return grub_error (GRUB_ERR_READ_ERROR, "ATAPI read error");
+    }
+
+  return grub_error (GRUB_ERR_READ_ERROR, "ATAPI protocol error");
+}
+
+static grub_err_t
+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))
+    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_ATA_REG_SECTORS, 0);
-  grub_ata_regset (dev, GRUB_ATA_REG_LBAHIGH, 0xFF);
-  grub_ata_regset (dev, GRUB_ATA_REG_LBAMID, 0xFF);
+  grub_ata_regset (dev, GRUB_ATAPI_REG_IREASON, 0);
+  grub_ata_regset (dev, GRUB_ATAPI_REG_CNTHIGH, size >> 8);
+  grub_ata_regset (dev, GRUB_ATAPI_REG_CNTLOW, size & 0xFF);
 
   grub_ata_regset (dev, GRUB_ATA_REG_CMD, GRUB_ATA_CMD_PACKET);
 
-  grub_ata_wait ();
-
-  if (grub_ata_pio_write (dev, packet, 12, GRUB_ATA_TOUT_STD))
+  /* Wait for !BSY, DRQ, !I/O, C/D.  */
+  if (grub_atapi_wait_drq (dev, GRUB_ATAPI_IREASON_CMD_OUT, GRUB_ATA_TOUT_STD))
     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);
+
   return GRUB_ERR_NONE;
 }
 
@@ -847,27 +902,51 @@
 {
   struct grub_ata_device *dev = (struct grub_ata_device *) scsi->data;
 
-  if (grub_atapi_packet (dev, cmd))
+  grub_dprintf("ata", "grub_atapi_read (size=%u)\n", size);
+
+  if (grub_atapi_packet (dev, cmd, size))
     return grub_errno;
 
-  grub_ata_wait (); /* XXX */
+  grub_size_t nread = 0;
+  while (nread < size)
+    {
+      /* Wait for !BSY, DRQ, I/O, !C/D.  */
+      if (grub_atapi_wait_drq (dev, GRUB_ATAPI_IREASON_DATA_IN, GRUB_ATA_TOUT_DATA))
+	return grub_errno;
 
-  return grub_ata_pio_read (dev, buf, size, GRUB_ATA_TOUT_DATA);
+      /* Get byte count for this DRQ assertion.  */
+      unsigned cnt = grub_ata_regget (dev, GRUB_ATAPI_REG_CNTHIGH) << 8
+		   | grub_ata_regget (dev, GRUB_ATAPI_REG_CNTLOW);
+      grub_dprintf("ata", "DRQ count=%u\n", cnt);
+
+      /* Count of last transfer may be uneven.  */
+      if (! (0 < cnt && cnt <= size - nread && (! (cnt & 1) || cnt == size - nread)))
+	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));
+
+      if (cnt & 1)
+	buf[nread + cnt - 1] = (char) grub_le_to_cpu16 (grub_inw (dev->ioaddress + GRUB_ATA_REG_DATA));
+
+      nread += cnt;
+    }
+
+  return GRUB_ERR_NONE;
 }
 
 static grub_err_t
-grub_atapi_write (struct grub_scsi *scsi,
+grub_atapi_write (struct grub_scsi *scsi __attribute__((unused)),
 		  grub_size_t cmdsize __attribute__((unused)),
-		  char *cmd, grub_size_t size, char *buf)
+		  char *cmd __attribute__((unused)),
+		  grub_size_t size __attribute__((unused)),
+		  char *buf __attribute__((unused)))
 {
-  struct grub_ata_device *dev = (struct grub_ata_device *) scsi->data;
-
-  if (grub_atapi_packet (dev, cmd))
-    return grub_errno;
-
-  grub_ata_wait (); /* XXX */
-
-  return grub_ata_pio_write (dev, buf, size, GRUB_ATA_TOUT_DATA);
+  // XXX: scsi.mod does not use write yet.
+  return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, "ATAPI write not implemented");
 }
 
 static grub_err_t
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.