]> git.ipfire.org Git - thirdparty/grub.git/commitdiff
splitting generic ata from pata.
authorVladimir 'phcoder' Serbinenko <phcoder@gmail.com>
Fri, 24 Dec 2010 15:07:53 +0000 (16:07 +0100)
committerVladimir 'phcoder' Serbinenko <phcoder@gmail.com>
Fri, 24 Dec 2010 15:07:53 +0000 (16:07 +0100)
commands/hdparm.c
disk/ahci.c
disk/ata.c
disk/ata_pthru.c
disk/scsi.c
disk/usbms.c
include/grub/ata.h
include/grub/disk.h
include/grub/scsi.h
kern/disk.c

index a3f8bbff03f652f4968070a9f849b8124d84a0ee..02ae8f36bcba3fb70970fa9427800395469a3042 100644 (file)
@@ -18,6 +18,7 @@
  */
 
 #include <grub/ata.h>
+#include <grub/scsi.h>
 #include <grub/disk.h>
 #include <grub/dl.h>
 #include <grub/misc.h>
@@ -61,60 +62,60 @@ enum grub_ata_smart_commands
 static int quiet = 0;
 
 static grub_err_t
-grub_hdparm_do_ata_cmd (grub_disk_t disk, grub_uint8_t cmd,
+grub_hdparm_do_ata_cmd (grub_ata_t ata, grub_uint8_t cmd,
                        grub_uint8_t features, grub_uint8_t sectors,
                        void * buffer, int size)
 {
   struct grub_disk_ata_pass_through_parms apt;
   grub_memset (&apt, 0, sizeof (apt));
 
-  apt.taskfile[GRUB_ATA_REG_CMD] = cmd;
-  apt.taskfile[GRUB_ATA_REG_FEATURES] = features;
-  apt.taskfile[GRUB_ATA_REG_SECTORS] = sectors;
+  apt.taskfile.cmd = cmd;
+  apt.taskfile.features = features;
+  apt.taskfile.sectors = sectors;
   apt.buffer = buffer;
   apt.size = size;
 
-  if (grub_disk_ata_pass_through (disk, &apt))
+  if (ata->dev->readwrite (ata, &apt))
     return grub_errno;
 
   return GRUB_ERR_NONE;
 }
 
 static int
-grub_hdparm_do_check_powermode_cmd (grub_disk_t disk)
+grub_hdparm_do_check_powermode_cmd (grub_ata_t ata)
 {
   struct grub_disk_ata_pass_through_parms apt;
   grub_memset (&apt, 0, sizeof (apt));
 
-  apt.taskfile[GRUB_ATA_REG_CMD] = GRUB_ATA_CMD_CHECK_POWER_MODE;
+  apt.taskfile.cmd = GRUB_ATA_CMD_CHECK_POWER_MODE;
 
-  if (grub_disk_ata_pass_through (disk, &apt))
+  if (ata->dev->readwrite (ata, &apt))
     return -1;
 
-  return apt.taskfile[GRUB_ATA_REG_SECTORS];
+  return apt.taskfile.sectors;
 }
 
 static int
-grub_hdparm_do_smart_cmd (grub_disk_t disk, grub_uint8_t features)
+grub_hdparm_do_smart_cmd (grub_ata_t ata, grub_uint8_t features)
 {
   struct grub_disk_ata_pass_through_parms apt;
   grub_memset (&apt, 0, sizeof (apt));
 
-  apt.taskfile[GRUB_ATA_REG_CMD] = GRUB_ATA_CMD_SMART;
-  apt.taskfile[GRUB_ATA_REG_FEATURES] = features;
-  apt.taskfile[GRUB_ATA_REG_LBAMID]  = 0x4f;
-  apt.taskfile[GRUB_ATA_REG_LBAHIGH] = 0xc2;
+  apt.taskfile.cmd = GRUB_ATA_CMD_SMART;
+  apt.taskfile.features = features;
+  apt.taskfile.lba_mid  = 0x4f;
+  apt.taskfile.lba_high = 0xc2;
 
-  if (grub_disk_ata_pass_through (disk, &apt))
+  if (ata->dev->readwrite (ata, &apt))
     return -1;
 
   if (features == GRUB_ATA_FEAT_SMART_STATUS)
     {
-      if (   apt.taskfile[GRUB_ATA_REG_LBAMID]  == 0x4f
-          && apt.taskfile[GRUB_ATA_REG_LBAHIGH] == 0xc2)
+      if (   apt.taskfile.lba_mid  == 0x4f
+          && apt.taskfile.lba_high == 0xc2)
        return 0; /* Good SMART status.  */
-      else if (   apt.taskfile[GRUB_ATA_REG_LBAMID]  == 0xf4
-              && apt.taskfile[GRUB_ATA_REG_LBAHIGH] == 0x2c)
+      else if (   apt.taskfile.lba_mid  == 0xf4
+              && apt.taskfile.lba_high == 0x2c)
        return 1; /* Bad SMART status.  */
       else
        return -1;
@@ -124,12 +125,12 @@ grub_hdparm_do_smart_cmd (grub_disk_t disk, grub_uint8_t features)
 
 static grub_err_t
 grub_hdparm_simple_cmd (const char * msg,
-                       grub_disk_t disk, grub_uint8_t cmd)
+                       grub_ata_t ata, grub_uint8_t cmd)
 {
   if (! quiet && msg)
     grub_printf ("%s", msg);
 
-  grub_err_t err = grub_hdparm_do_ata_cmd (disk, cmd, 0, 0, NULL, 0);
+  grub_err_t err = grub_hdparm_do_ata_cmd (ata, cmd, 0, 0, NULL, 0);
 
   if (! quiet && msg)
     grub_printf ("%s\n", ! err ? "" : ": not supported");
@@ -138,7 +139,7 @@ grub_hdparm_simple_cmd (const char * msg,
 
 static grub_err_t
 grub_hdparm_set_val_cmd (const char * msg, int val,
-                        grub_disk_t disk, grub_uint8_t cmd,
+                        grub_ata_t ata, grub_uint8_t cmd,
                         grub_uint8_t features, grub_uint8_t sectors)
 {
   if (! quiet && msg && *msg)
@@ -149,7 +150,7 @@ grub_hdparm_set_val_cmd (const char * msg, int val,
        grub_printf ("Disable %s", msg);
     }
 
-  grub_err_t err = grub_hdparm_do_ata_cmd (disk, cmd, features, sectors,
+  grub_err_t err = grub_hdparm_do_ata_cmd (ata, cmd, features, sectors,
                                           NULL, 0);
 
   if (! quiet && msg)
@@ -273,6 +274,7 @@ static grub_err_t
 grub_cmd_hdparm (grub_extcmd_t cmd, int argc, char **args) // state????
 {
   struct grub_arg_list *state = cmd->state;
+  struct grub_ata *ata;
 
   /* Check command line.  */
   if (argc != 1)
@@ -283,9 +285,6 @@ grub_cmd_hdparm (grub_extcmd_t cmd, int argc, char **args) // state????
     return grub_error (GRUB_ERR_BAD_ARGUMENT, "argument is not a device name");
   args[0][len - 1] = 0;
 
-  if (! grub_disk_ata_pass_through)
-    return grub_error (GRUB_ERR_BAD_ARGUMENT, "ATA pass through not available");
-
   int i = 0;
   int apm          = get_int_arg (&state[i++]);
   int power        = state[i++].set;
@@ -311,15 +310,37 @@ grub_cmd_hdparm (grub_extcmd_t cmd, int argc, char **args) // state????
       return grub_error (GRUB_ERR_BAD_ARGUMENT, "partition not allowed");
     }
 
+  switch (disk->dev->id)
+    {
+    case GRUB_DISK_DEVICE_ATA_ID:
+      ata = disk->data;
+      break;
+    case GRUB_DISK_DEVICE_SCSI_ID:
+      if (((disk->id >> GRUB_SCSI_ID_SUBSYSTEM_SHIFT) & 0xFF)
+         == GRUB_SCSI_SUBSYSTEM_PATA
+         || (((disk->id >> GRUB_SCSI_ID_SUBSYSTEM_SHIFT) & 0xFF)
+             == GRUB_SCSI_SUBSYSTEM_AHCI))
+       {
+         ata = ((struct grub_scsi *) disk->data)->data;
+         break;
+       }
+    default:
+      return grub_error (GRUB_ERR_IO, "not an ATA device");
+    }
+    
+
   /* Change settings.  */
   if (aam >= 0)
     grub_hdparm_set_val_cmd ("Automatic Acoustic Management", (aam ? aam : -1),
-      disk, GRUB_ATA_CMD_SET_FEATURES, (aam ? 0x42 : 0xc2), aam);
+                            ata, GRUB_ATA_CMD_SET_FEATURES,
+                            (aam ? 0x42 : 0xc2), aam);
 
   if (apm >= 0)
     grub_hdparm_set_val_cmd ("Advanced Power Management",
-      (apm != 255 ? apm : -1), disk, GRUB_ATA_CMD_SET_FEATURES,
-      (apm != 255 ? 0x05 : 0x85), (apm != 255 ? apm : 0));
+                            (apm != 255 ? apm : -1), ata,
+                            GRUB_ATA_CMD_SET_FEATURES,
+                            (apm != 255 ? 0x05 : 0x85),
+                            (apm != 255 ? apm : 0));
 
   if (standby_tout >= 0)
     {
@@ -330,28 +351,28 @@ grub_cmd_hdparm (grub_extcmd_t cmd, int argc, char **args) // state????
          grub_printf (")");
        }
       /* The IDLE cmd sets disk to idle mode and configures standby timer.  */
-      grub_hdparm_set_val_cmd ("", -1, disk, GRUB_ATA_CMD_IDLE, 0, standby_tout);
+      grub_hdparm_set_val_cmd ("", -1, ata, GRUB_ATA_CMD_IDLE, 0, standby_tout);
     }
 
   if (enable_smart >= 0)
     {
       if (! quiet)
        grub_printf ("%sable SMART operations", (enable_smart ? "En" : "Dis"));
-      int err = grub_hdparm_do_smart_cmd (disk, (enable_smart ?
+      int err = grub_hdparm_do_smart_cmd (ata, (enable_smart ?
                  GRUB_ATA_FEAT_SMART_ENABLE : GRUB_ATA_FEAT_SMART_DISABLE));
       if (! quiet)
        grub_printf ("%s\n", err ? ": not supported" : "");
     }
 
   if (sec_freeze)
-    grub_hdparm_simple_cmd ("Freeze security settings", disk,
+    grub_hdparm_simple_cmd ("Freeze security settings", ata,
                             GRUB_ATA_CMD_SECURITY_FREEZE_LOCK);
 
   /* Print/dump IDENTIFY.  */
   if (ident || dumpid)
     {
       char buf[GRUB_DISK_SECTOR_SIZE];
-      if (grub_hdparm_do_ata_cmd (disk, GRUB_ATA_CMD_IDENTIFY_DEVICE,
+      if (grub_hdparm_do_ata_cmd (ata, GRUB_ATA_CMD_IDENTIFY_DEVICE,
           0, 0, buf, sizeof (buf)))
        grub_printf ("Cannot read ATA IDENTIFY data\n");
       else
@@ -367,7 +388,7 @@ grub_cmd_hdparm (grub_extcmd_t cmd, int argc, char **args) // state????
   if (power)
     {
       grub_printf ("Disk power mode is: ");
-      int mode = grub_hdparm_do_check_powermode_cmd (disk);
+      int mode = grub_hdparm_do_check_powermode_cmd (ata);
       if (mode < 0)
         grub_printf ("unknown\n");
       else
@@ -383,7 +404,7 @@ grub_cmd_hdparm (grub_extcmd_t cmd, int argc, char **args) // state????
     {
       if (! quiet)
        grub_printf ("SMART status is: ");
-      int err = grub_hdparm_do_smart_cmd (disk, GRUB_ATA_FEAT_SMART_STATUS);
+      int err = grub_hdparm_do_smart_cmd (ata, GRUB_ATA_FEAT_SMART_STATUS);
       if (! quiet)
        grub_printf ("%s\n", (err  < 0 ? "unknown" :
                              err == 0 ? "OK" : "*BAD*"));
@@ -392,11 +413,11 @@ grub_cmd_hdparm (grub_extcmd_t cmd, int argc, char **args) // state????
 
   /* Change power mode.  */
   if (standby_now)
-    grub_hdparm_simple_cmd ("Set disk to standby mode", disk,
+    grub_hdparm_simple_cmd ("Set disk to standby mode", ata,
                            GRUB_ATA_CMD_STANDBY_IMMEDIATE);
 
   if (sleep_now)
-    grub_hdparm_simple_cmd ("Set disk to sleep mode", disk,
+    grub_hdparm_simple_cmd ("Set disk to sleep mode", ata,
                            GRUB_ATA_CMD_SLEEP);
 
   grub_disk_close (disk);
index 1372fd65163676a76c3677de22f74510bed88091..946db82e15518733ea71573083ab3f22546b0986 100644 (file)
 #include <grub/mm.h>
 #include <grub/time.h>
 #include <grub/pci.h>
+#include <grub/ata.h>
 #include <grub/scsi.h>
+#include <grub/misc.h>
+#include <grub/list.h>
 
 struct grub_ahci_cmd_head
 {
-  grub_uint32_t unused[8];
+  grub_uint32_t config;
+  grub_uint32_t transfered;
+  grub_uint64_t command_table_base;
+  grub_uint32_t unused[4];
+};
+
+struct grub_ahci_prdt_entry
+{
+  grub_uint64_t data_base;
+  grub_uint32_t unused;
+  grub_uint32_t size;
+};
+
+struct grub_ahci_cmd_table
+{
+  grub_uint8_t cfis[0x40];
+  grub_uint8_t command[16];
+  grub_uint32_t reserved[0xc];
+  struct grub_ahci_prdt_entry prdt[1];
 };
 
 struct grub_ahci_hba_port
 {
   grub_uint64_t command_list_base;
-  grub_uint32_t unused[12];
+  grub_uint64_t fis_base;
+  grub_uint32_t intstatus;
+  grub_uint32_t inten;
+  grub_uint32_t command;
+  grub_uint32_t unused1[6];
   grub_uint32_t sata_active;
   grub_uint32_t command_issue;
-  grub_uint32_t unused[16];
+  grub_uint32_t unused2[17];
 };
 
 struct grub_ahci_hba
 {
   grub_uint32_t cap;
-#define GRUB_AHCI_HBA_CAP_MASK 0x1f
   grub_uint32_t global_control;
-#define GRUB_AHCI_HBA_GLOBAL_CONTROL_AHCI_EN 0x80000000
+  grub_uint32_t intr_status;
   grub_uint32_t ports_implemented;
-  grub_uint32_t unused1[7];
+  grub_uint32_t unused1[6];
   grub_uint32_t bios_handoff;
-#define GRUB_AHCI_BIOS_HANDOFF_BIOS_OWNED 1
-#define GRUB_AHCI_BIOS_HANDOFF_OS_OWNED 2
-#define GRUB_AHCI_BIOS_HANDOFF_OS_OWNERSHIP_CHANGED 8
-#define GRUB_AHCI_BIOS_HANDOFF_RWC 8
   grub_uint32_t unused2[53];
   struct grub_ahci_hba_port ports[32];
 };
 
+enum
+  {
+    GRUB_AHCI_HBA_CAP_NPORTS_MASK = 0x1f
+  };
+
+enum
+  {
+    GRUB_AHCI_HBA_GLOBAL_CONTROL_INTR_EN = 0x00000002,
+    GRUB_AHCI_HBA_GLOBAL_CONTROL_AHCI_EN = 0x80000000,
+  };
+
+enum
+  {
+    GRUB_AHCI_BIOS_HANDOFF_BIOS_OWNED = 1,
+    GRUB_AHCI_BIOS_HANDOFF_OS_OWNED = 2,
+    GRUB_AHCI_BIOS_HANDOFF_OS_OWNERSHIP_CHANGED = 8,
+    GRUB_AHCI_BIOS_HANDOFF_RWC = 8
+  };
+
+
 struct grub_ahci_device
 {
   struct grub_ahci_device *next;
@@ -62,19 +102,41 @@ struct grub_ahci_device
   int num;
   struct grub_pci_dma_chunk *command_list_chunk;
   volatile struct grub_ahci_cmd_head *command_list;
+  struct grub_pci_dma_chunk *command_table_chunk;
+  volatile struct grub_ahci_cmd_table *command_table;
 };
 
+enum
+  {
+    GRUB_AHCI_CONFIG_READ = 0,
+    GRUB_AHCI_CONFIG_CFIS_LENGTH_MASK = 0x1f,
+    GRUB_AHCI_CONFIG_ATAPI = 0x20,
+    GRUB_AHCI_CONFIG_WRITE = 0x40,
+    GRUB_AHCI_CONFIG_PREFETCH = 0x80,
+    GRUB_AHCI_CONFIG_RESET = 0x100,
+    GRUB_AHCI_CONFIG_BIST = 0x200,
+    GRUB_AHCI_CONFIG_CLEAR_R_OK = 0x400,
+    GRUB_AHCI_CONFIG_PMP_MASK = 0xf000,
+    GRUB_AHCI_CONFIG_PRDT_LENGTH_MASK = 0xffff0000,
+  };
+#define GRUB_AHCI_CONFIG_CFIS_LENGTH_SHIFT 0
+#define GRUB_AHCI_CONFIG_PMP_SHIFT 12
+#define GRUB_AHCI_CONFIG_PRDT_LENGTH_SHIFT 16
+#define GRUB_AHCI_INTERRUPT_ON_COMPLETE 0x80000000
+
+#define GRUB_AHCI_PRDT_MAX_CHUNK_LENGTH 0x200000
+
 static struct grub_ahci_device *grub_ahci_devices;
 static int numdevs;
 
 static int NESTED_FUNC_ATTR
 grub_ahci_pciinit (grub_pci_device_t dev,
-                 grub_pci_id_t pciid __attribute__ ((unused)))
+                  grub_pci_id_t pciid __attribute__ ((unused)))
 {
   grub_pci_address_t addr;
   grub_uint32_t class;
   grub_uint32_t bar;
-  int nports;
+  unsigned i, nports;
   volatile struct grub_ahci_hba *hba;
 
   /* Read class.  */
@@ -88,8 +150,8 @@ grub_ahci_pciinit (grub_pci_device_t dev,
   addr = grub_pci_make_address (dev, GRUB_PCI_REG_ADDRESS_REG5);
   bar = grub_pci_read (addr);
 
-  if (bar & (GRUB_PCI_ADDR_SPACE_MASK | GRUB_PCI_ADDR_MEM_TYPE_MASK
-            | GRUB_PCI_ADDR_MEM_PREFETCH)
+  if ((bar & (GRUB_PCI_ADDR_SPACE_MASK | GRUB_PCI_ADDR_MEM_TYPE_MASK
+             | GRUB_PCI_ADDR_MEM_PREFETCH))
       != (GRUB_PCI_ADDR_SPACE_MEMORY | GRUB_PCI_ADDR_MEM_TYPE_32))
     return 0;
 
@@ -98,7 +160,7 @@ grub_ahci_pciinit (grub_pci_device_t dev,
 
   hba->global_control |= GRUB_AHCI_HBA_GLOBAL_CONTROL_AHCI_EN;
 
-  nports = hba->cap & GRUB_AHCI_HBA_CAP_MASK;
+  nports = (hba->cap & GRUB_AHCI_HBA_CAP_NPORTS_MASK) + 1;
 
   if (! (hba->bios_handoff & GRUB_AHCI_BIOS_HANDOFF_OS_OWNED))
     {
@@ -122,11 +184,13 @@ grub_ahci_pciinit (grub_pci_device_t dev,
     }
   else
     grub_dprintf ("ahci", "AHCI is already in OS mode\n");
+  grub_dprintf ("ahci", "%d AHCI ports\n", nports);
 
   for (i = 0; i < nports; i++)
     {
       struct grub_ahci_device *adev;
       struct grub_pci_dma_chunk *command_list;
+      struct grub_pci_dma_chunk *command_table;
 
       if (!(hba->ports_implemented & (1 << i)))
        continue;
@@ -136,18 +200,34 @@ grub_ahci_pciinit (grub_pci_device_t dev,
       if (!command_list)
        return 1;
 
+      command_table = grub_memalign_dma32 (1024,
+                                          sizeof (struct grub_ahci_cmd_table));
+      if (!command_table)
+       {
+         grub_dma_free (command_list);
+         return 1;
+       }
+
       adev = grub_malloc (sizeof (*adev));
       if (!adev)
        {
          grub_dma_free (command_list);
+         grub_dma_free (command_table);
          return 1;
        }
 
+      grub_dprintf ("ahci", "found device ahci%d (port %d)\n", numdevs, i);
+
       adev->hba = hba;
       adev->port = i;
       adev->num = numdevs++;
       adev->command_list_chunk = command_list;
       adev->command_list = grub_dma_get_virt (command_list);
+      adev->command_table_chunk = command_table;
+      adev->command_table = grub_dma_get_virt (command_table);
+      adev->command_list->command_table_base
+       = grub_dma_get_phys (command_table);
+
       adev->hba->ports[i].command_list_base = grub_dma_get_phys (command_list);
       grub_list_push (GRUB_AS_LIST_P (&grub_ahci_devices),
                      GRUB_AS_LIST (adev));
@@ -167,12 +247,12 @@ grub_ahci_initialize (void)
 \f
 
 static int
-grub_ahci_iterate (int (*hook) (int bus, int luns))
+grub_ahci_iterate (int (*hook) (int id, int bus))
 {
   struct grub_ahci_device *dev;
 
   FOR_LIST_ELEMENTS(dev, grub_ahci_devices)
-    if (hook (dev->num, 1))
+    if (hook (GRUB_SCSI_SUBSYSTEM_AHCI, dev->num))
       return 1;
 
   return 0;
@@ -195,95 +275,130 @@ find_free_cmd_slot (struct grub_ahci_device *dev)
 }
 #endif
 
-static grub_err_t
-grub_ahci_packet (struct grub_ahci_device *dev, char *packet,
-                 grub_size_t size)
-{
-
-  return GRUB_ERR_NONE;
-}
+enum
+  {
+    GRUB_AHCI_FIS_REG_H2D = 0x27
+  };
 
-static grub_err_t
-grub_ahci_read (struct grub_scsi *scsi,
-                grub_size_t cmdsize __attribute__((unused)),
-                char *cmd, grub_size_t size, char *buf)
+static const int register_map[11] = { 3 /* Features */,
+                                     12 /* Sectors */,
+                                     4 /* LBA low */,
+                                     5 /* LBA mid */,
+                                     6 /* LBA high */,
+                                     7 /* Device */,
+                                     2 /* CMD register */,
+                                     13 /* Sectors 48  */,
+                                     8 /* LBA48 low */,
+                                     9 /* LBA48 mid */,
+                                     10 /* LBA48 high */ }; 
+
+static grub_err_t 
+grub_ahci_readwrite (grub_ata_t disk,
+                    struct grub_disk_ata_pass_through_parms *parms)
 {
-  struct grub_ahci_device *dev = (struct grub_ahci_device *) scsi->data;
-
-  grub_dprintf("ahci", "grub_ahci_read (size=%llu)\n", (unsigned long long) size);
-
-  if (grub_atapi_packet (dev, cmd, size))
-    return grub_errno;
-
-  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;
-
-      /* 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_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));
-
-      nread += cnt;
-    }
+  struct grub_ahci_device *dev = (struct grub_ahci_device *) disk->data;
+  struct grub_pci_dma_chunk *bufc;
+  grub_uint64_t endtime;
+  unsigned i;
+
+  grub_dprintf("ahci", "grub_ahci_read (size=%llu, cmdsize = %llu)\n",
+              (unsigned long long) parms->size,
+              (unsigned long long) parms->cmdsize);
+
+  if (parms->cmdsize != 0 && parms->cmdsize != 12 && parms->cmdsize != 16)
+    return grub_error (GRUB_ERR_BAD_ARGUMENT, "incorrect ATAPI command size");
+
+  if (parms->size > GRUB_AHCI_PRDT_MAX_CHUNK_LENGTH)
+    return grub_error (GRUB_ERR_BAD_ARGUMENT, "too big data buffer");
+
+  bufc = grub_memalign_dma32 (1024, parms->size + (parms->size & 1));
+
+  /* FIXME: support port multipliers.  */
+  dev->command_list[0].config
+    = (4 << GRUB_AHCI_CONFIG_CFIS_LENGTH_SHIFT)
+    | GRUB_AHCI_CONFIG_CLEAR_R_OK
+    | (0 << GRUB_AHCI_CONFIG_PMP_SHIFT)
+    | (1 << GRUB_AHCI_CONFIG_PRDT_LENGTH_SHIFT)
+    | (parms->cmdsize ? GRUB_AHCI_CONFIG_ATAPI : 0)
+    | (parms->write ? GRUB_AHCI_CONFIG_WRITE : GRUB_AHCI_CONFIG_READ);
+  dev->command_list[0].transfered = 0;
+  dev->command_list[0].command_table_base
+    = grub_dma_get_phys (dev->command_table_chunk);
+  grub_memset ((char *) dev->command_list[0].unused, 0,
+              sizeof (dev->command_list[0].unused));
+  grub_memset ((char *) &dev->command_table[0], 0,
+              sizeof (dev->command_table[0]));
+  if (parms->cmdsize)
+    grub_memcpy ((char *) dev->command_table[0].command, parms->cmd,
+                parms->cmdsize);
+
+  dev->command_table[0].cfis[0] = GRUB_AHCI_FIS_REG_H2D;
+  for (i = 0; i < sizeof (parms->taskfile.raw); i++)
+    dev->command_table[0].cfis[register_map[i]] = parms->taskfile.raw[i];
+
+  dev->command_table[0].prdt[0].data_base = grub_dma_get_phys (bufc);
+  dev->command_table[0].prdt[0].unused = 0;
+  dev->command_table[0].prdt[0].size = (parms->size + (parms->size & 1) - 1)
+    | GRUB_AHCI_INTERRUPT_ON_COMPLETE;
+
+  if (parms->write)
+    grub_memcpy ((char *) grub_dma_get_virt (bufc), parms->buffer, parms->size);
+
+  grub_dprintf ("ahci", "AHCI command schedulded\n");
+  dev->hba->ports[dev->port].inten = (1 << 5);
+  dev->hba->ports[dev->port].intstatus = (1 << 5);
+  dev->hba->ports[dev->port].command_issue |= 1;
+  dev->hba->ports[dev->port].command |= 1;
+
+  endtime = grub_get_time_ms () + 1000;  
+  while (!(dev->hba->ports[dev->port].intstatus & (1 << 5)))
+    if (grub_get_time_ms () > endtime)
+      {
+       grub_dprintf ("ahci", "AHCI timeout\n");
+       dev->hba->ports[dev->port].command &= ~1;
+       /* FIXME: free resources.  */
+       return grub_error (GRUB_ERR_IO, "AHCI transfer timeouted");
+      }
+
+  grub_dprintf ("ahci", "AHCI command completed succesfully\n");
+  dev->hba->ports[dev->port].command &= ~1;
+
+  if (!parms->write)
+    grub_memcpy (parms->buffer, (char *) grub_dma_get_virt (bufc), parms->size);
+  grub_dma_free (bufc);
 
   return GRUB_ERR_NONE;
 }
 
 static grub_err_t
-grub_ahci_write (struct grub_scsi *scsi __attribute__((unused)),
-                 grub_size_t cmdsize __attribute__((unused)),
-                 char *cmd __attribute__((unused)),
-                 grub_size_t size __attribute__((unused)),
-                 char *buf __attribute__((unused)))
-{
-  // XXX: scsi.mod does not use write yet.
-  return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, "AHCI write not implemented");
-}
-
-static grub_err_t
-grub_ahci_open (int devnum, struct grub_scsi *scsi)
+grub_ahci_open (int id, int devnum, struct grub_ata *ata)
 {
   struct grub_ahci_device *dev;
 
+  if (id != GRUB_SCSI_SUBSYSTEM_AHCI)
+    return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "not an AHCI device");
+
   FOR_LIST_ELEMENTS(dev, grub_ahci_devices)
     {
       if (dev->num == devnum)
        break;
     }
 
-  grub_dprintf ("ata", "opening AHCI dev `ahci%d'\n", devnum);
-
   if (! dev)
     return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "no such AHCI device");
 
-  scsi->data = dev;
+  grub_dprintf ("ahci", "opening AHCI dev `ahci%d'\n", dev->num);
+
+  ata->data = dev;
 
   return GRUB_ERR_NONE;
 }
 
-
-static struct grub_scsi_dev grub_ahci_dev =
+static struct grub_ata_dev grub_ahci_dev =
   {
-    .name = "ahci",
-    .id = GRUB_SCSI_SUBSYSTEM_AHCI,
     .iterate = grub_ahci_iterate,
     .open = grub_ahci_open,
-    .read = grub_ahci_read,
-    .write = grub_ahci_write
+    .readwrite = grub_ahci_readwrite,
   };
 
 \f
@@ -291,8 +406,8 @@ static struct grub_scsi_dev grub_ahci_dev =
 GRUB_MOD_INIT(ahci)
 {
   /* To prevent two drivers operating on the same disks.  */
-  grub_disk_firmware_is_tainted = 1;
-  if (grub_disk_firmware_fini)
+  //  grub_disk_firmware_is_tainted = 1;
+  if (0 && grub_disk_firmware_fini)
     {
       grub_disk_firmware_fini ();
       grub_disk_firmware_fini = NULL;
@@ -302,10 +417,10 @@ GRUB_MOD_INIT(ahci)
   grub_ahci_initialize ();
 
   /* AHCI devices are handled by scsi.mod.  */
-  grub_scsi_dev_register (&grub_ahci_dev);
+  grub_ata_dev_register (&grub_ahci_dev);
 }
 
 GRUB_MOD_FINI(ahci)
 {
-  grub_scsi_dev_unregister (&grub_ahci_dev);
+  grub_ata_dev_unregister (&grub_ahci_dev);
 }
index cfd58a6d5f6906e7a1761d29a259c1786110715a..3d9de23dd401d611c23103efe8e10c2277ac3b61 100644 (file)
 #include <grub/dl.h>
 #include <grub/disk.h>
 #include <grub/mm.h>
-#include <grub/time.h>
-#include <grub/pci.h>
 #include <grub/scsi.h>
-#include <grub/cs5536.h>
 
-/* At the moment, only two IDE ports are supported.  */
-static const grub_port_t grub_ata_ioaddress[] = { GRUB_ATA_CH0_PORT1,
-                                                 GRUB_ATA_CH1_PORT1 };
-static const grub_port_t grub_ata_ioaddress2[] = { GRUB_ATA_CH0_PORT2, 
-                                                  GRUB_ATA_CH1_PORT2 };
-
-static struct grub_ata_device *grub_ata_devices;
-
-/* Wait for !BSY.  */
-grub_err_t
-grub_ata_wait_not_busy (struct grub_ata_device *dev, int milliseconds)
-{
-  /* 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);
-
-  int i = 1;
-  grub_uint8_t sts;
-  while ((sts = grub_ata_regget (dev, GRUB_ATA_REG_STATUS))
-        & GRUB_ATA_STATUS_BUSY)
-    {
-      if (i >= milliseconds)
-        {
-         grub_dprintf ("ata", "timeout: %dms, status=0x%x\n",
-                       milliseconds, sts);
-         return grub_error (GRUB_ERR_TIMEOUT, "ATA timeout");
-       }
-
-      grub_millisleep (1);
-      i++;
-    }
-
-  return GRUB_ERR_NONE;
-}
-
-static inline void
-grub_ata_wait (void)
-{
-  grub_millisleep (50);
-}
-
-/* Wait for !BSY, DRQ.  */
-grub_err_t
-grub_ata_wait_drq (struct grub_ata_device *dev, int rw,
-                  int milliseconds)
-{
-  if (grub_ata_wait_not_busy (dev, milliseconds))
-    return grub_errno;
-
-  /* !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;
-}
+static grub_ata_dev_t grub_ata_dev_list;
 
 /* Byteorder has to be changed before strings can be read.  */
 static void
@@ -105,30 +38,8 @@ grub_ata_strncpy (char *dst, char *src, grub_size_t len)
   dst[len] = '\0';
 }
 
-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;
-
-  /* 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));
-}
-
-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;
-
-  /* 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);
-}
-
 static void
-grub_ata_dumpinfo (struct grub_ata_device *dev, char *info)
+grub_ata_dumpinfo (struct grub_ata *dev, char *info)
 {
   char text[41];
 
@@ -148,31 +59,27 @@ grub_ata_dumpinfo (struct grub_ata_device *dev, char *info)
 }
 
 static grub_err_t
-grub_atapi_identify (struct grub_ata_device *dev)
+grub_atapi_identify (struct grub_ata *dev)
 {
+  struct grub_disk_ata_pass_through_parms parms;
   char *info;
+  grub_err_t err;
 
   info = grub_malloc (GRUB_DISK_SECTOR_SIZE);
   if (! info)
     return grub_errno;
 
-  grub_ata_regset (dev, GRUB_ATA_REG_DISK, 0xE0 | dev->device << 4);
-  grub_ata_wait ();
-  if (grub_ata_check_ready (dev))
-    {
-      grub_free (info);
-      return grub_errno;
-    }
+  grub_memset (&parms, 0, sizeof (parms));
+  parms.taskfile.disk = 0;
+  parms.taskfile.cmd = GRUB_ATA_CMD_IDENTIFY_PACKET_DEVICE;
 
-  grub_ata_regset (dev, GRUB_ATA_REG_CMD, GRUB_ATA_CMD_IDENTIFY_PACKET_DEVICE);
-  grub_ata_wait ();
+  err = dev->dev->readwrite (dev, &parms);
+  if (err)
+    return err;
 
-  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);
+  if (parms.size != GRUB_DISK_SECTOR_SIZE)
+    return grub_error (GRUB_ERR_UNKNOWN_DEVICE,
+                      "device cannot be identified");
 
   dev->atapi = 1;
 
@@ -184,96 +91,35 @@ grub_atapi_identify (struct grub_ata_device *dev)
 }
 
 static grub_err_t
-grub_atapi_wait_drq (struct grub_ata_device *dev,
-                    grub_uint8_t ireason,
-                    int 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);
-  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;
-
-  /* !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)
-{
-  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_FEATURES, 0);
-  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);
-
-  /* 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_ata_pio_write (dev, packet, 12);
-
-  return GRUB_ERR_NONE;
-}
-
-static grub_err_t
-grub_ata_identify (struct grub_ata_device *dev)
+grub_ata_identify (struct grub_ata *dev)
 {
+  struct grub_disk_ata_pass_through_parms parms;
   char *info;
   grub_uint16_t *info16;
+  grub_err_t err;
 
   info = grub_malloc (GRUB_DISK_SECTOR_SIZE);
   if (! info)
     return grub_errno;
 
   info16 = (grub_uint16_t *) info;
+  grub_memset (&parms, 0, sizeof (parms));
+  parms.buffer = info;
+  parms.taskfile.disk = 0;
 
-  grub_ata_regset (dev, GRUB_ATA_REG_DISK, 0xE0 | dev->device << 4);
-  grub_ata_wait ();
-  if (grub_ata_check_ready (dev))
-    {
-      grub_free (info);
-      return grub_errno;
-    }
+  parms.taskfile.cmd = GRUB_ATA_CMD_IDENTIFY_DEVICE;
 
-  grub_ata_regset (dev, GRUB_ATA_REG_CMD, GRUB_ATA_CMD_IDENTIFY_DEVICE);
-  grub_ata_wait ();
+  err = dev->dev->readwrite (dev, &parms);
+  if (err)
+    return err;
 
-  if (grub_ata_wait_drq (dev, 0, GRUB_ATA_TOUT_STD))
+  if (parms.size != GRUB_DISK_SECTOR_SIZE)
     {
+      grub_uint8_t sts = parms.taskfile.status;
       grub_free (info);
-      grub_errno = GRUB_ERR_NONE;
-      grub_uint8_t sts = grub_ata_regget (dev, GRUB_ATA_REG_STATUS);
-
       if ((sts & (GRUB_ATA_STATUS_BUSY | GRUB_ATA_STATUS_DRQ
          | GRUB_ATA_STATUS_ERR)) == GRUB_ATA_STATUS_ERR
-         && (grub_ata_regget (dev, GRUB_ATA_REG_ERROR) & 0x04 /* ABRT */))
+         && (parms.taskfile.error & 0x04 /* ABRT */))
        /* Device without ATA IDENTIFY, try ATAPI.  */
        return grub_atapi_identify (dev);
 
@@ -287,19 +133,6 @@ grub_ata_identify (struct grub_ata_device *dev)
                           "device cannot be identified");
     }
 
-  grub_ata_pio_read (dev, info, GRUB_DISK_SECTOR_SIZE);
-
-  /* Re-check status to avoid bogus identify data due to stuck DRQ.  */
-  grub_uint8_t sts = grub_ata_regget (dev, GRUB_ATA_REG_STATUS);
-  if (sts & (GRUB_ATA_STATUS_BUSY | GRUB_ATA_STATUS_DRQ | GRUB_ATA_STATUS_ERR))
-    {
-      grub_dprintf ("ata", "bad status=0x%x\n", sts);
-      grub_free (info);
-      /* No device, return error but don't print message.  */
-      grub_errno = GRUB_ERR_NONE;
-      return GRUB_ERR_UNKNOWN_DEVICE;
-    }
-
   /* Now it is certain that this is not an ATAPI device.  */
   dev->atapi = 0;
 
@@ -335,196 +168,12 @@ grub_ata_identify (struct grub_ata_device *dev)
 }
 
 static grub_err_t
-check_device (struct grub_ata_device *dev)
-{
-  grub_ata_regset (dev, GRUB_ATA_REG_DISK, dev->device << 4);
-  grub_ata_wait ();
-
-  /* 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_SECTORS, 0x5A);
-  grub_ata_wait ();
-  grub_uint8_t sec = grub_ata_regget (dev, GRUB_ATA_REG_SECTORS);
-  grub_dprintf ("ata", "sectors=0x%x\n", sec);
-  if (sec != 0x5A)
-    return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "no device connected");
-
-  /* The above test may detect a second (slave) device
-     connected to a SATA controller which supports only one
-     (master) device.  It is not safe to use the status register
-     READY bit to check for controller channel existence.  Some
-     ATAPI commands (RESET, DIAGNOSTIC) may clear this bit.  */
-
-  /* Use the IDENTIFY DEVICE command to query the device.  */
-  return grub_ata_identify (dev);
-}
-
-static grub_err_t
-grub_ata_device_initialize (int port, int device, int addr, int addr2)
-{
-  struct grub_ata_device *dev;
-  struct grub_ata_device **devp;
-  grub_err_t err;
-
-  grub_dprintf ("ata", "detecting device %d,%d (0x%x, 0x%x)\n",
-               port, device, addr, addr2);
-
-  dev = grub_malloc (sizeof(*dev));
-  if (! dev)
-    return grub_errno;
-
-  /* Setup the device information.  */
-  dev->port = port;
-  dev->device = device;
-  dev->ioaddress = addr + GRUB_MACHINE_PCI_IO_BASE;
-  dev->ioaddress2 = addr2 + GRUB_MACHINE_PCI_IO_BASE;
-  dev->next = NULL;
-
-  /* Register the device.  */
-  for (devp = &grub_ata_devices; *devp; devp = &(*devp)->next);
-  *devp = dev;
-
-  err = check_device (dev);
-  if (err)
-    grub_print_error ();
-
-  return 0;
-}
-
-static int NESTED_FUNC_ATTR
-grub_ata_pciinit (grub_pci_device_t dev,
-                 grub_pci_id_t pciid)
-{
-  static int compat_use[2] = { 0 };
-  grub_pci_address_t addr;
-  grub_uint32_t class;
-  grub_uint32_t bar1;
-  grub_uint32_t bar2;
-  int rega;
-  int regb;
-  int i;
-  static int controller = 0;
-  int cs5536 = 0;
-  int nports = 2;
-
-  /* Read class.  */
-  addr = grub_pci_make_address (dev, GRUB_PCI_REG_CLASS);
-  class = grub_pci_read (addr);
-
-  /* AMD CS5536 Southbridge.  */
-  if (pciid == GRUB_CS5536_PCIID)
-    {
-      cs5536 = 1;
-      nports = 1;
-    }
-
-  /* Check if this class ID matches that of a PCI IDE Controller.  */
-  if (!cs5536 && (class >> 16 != 0x0101))
-    return 0;
-
-  for (i = 0; i < nports; i++)
-    {
-      /* Set to 0 when the channel operated in compatibility mode.  */
-      int compat;
-
-      /* We don't support non-compatibility mode for CS5536.  */
-      if (cs5536)
-       compat = 0;
-      else
-       compat = (class >> (8 + 2 * i)) & 1;
-
-      rega = 0;
-      regb = 0;
-
-      /* If the channel is in compatibility mode, just assign the
-        default registers.  */
-      if (compat == 0 && !compat_use[i])
-       {
-         rega = grub_ata_ioaddress[i];
-         regb = grub_ata_ioaddress2[i];
-         compat_use[i] = 1;
-       }
-      else if (compat)
-       {
-         /* Read the BARs, which either contain a mmapped IO address
-            or the IO port address.  */
-         addr = grub_pci_make_address (dev, GRUB_PCI_REG_ADDRESSES
-                                       + sizeof (grub_uint64_t) * i);
-         bar1 = grub_pci_read (addr);
-         addr = grub_pci_make_address (dev, GRUB_PCI_REG_ADDRESSES
-                                       + sizeof (grub_uint64_t) * i
-                                       + sizeof (grub_uint32_t));
-         bar2 = grub_pci_read (addr);
-
-         /* Check if the BARs describe an IO region.  */
-         if ((bar1 & 1) && (bar2 & 1))
-           {
-             rega = bar1 & ~3;
-             regb = bar2 & ~3;
-           }
-       }
-
-      grub_dprintf ("ata",
-                   "PCI dev (%d,%d,%d) compat=%d rega=0x%x regb=0x%x\n",
-                   grub_pci_get_bus (dev), grub_pci_get_device (dev),
-                   grub_pci_get_function (dev), compat, rega, regb);
-
-      if (rega && regb)
-       {
-         grub_errno = GRUB_ERR_NONE;
-         grub_ata_device_initialize (controller * 2 + i, 0, rega, regb);
-
-         /* Most errors raised by grub_ata_device_initialize() are harmless.
-            They just indicate this particular drive is not responding, most
-            likely because it doesn't exist.  We might want to ignore specific
-            error types here, instead of printing them.  */
-         if (grub_errno)
-           {
-             grub_print_error ();
-             grub_errno = GRUB_ERR_NONE;
-           }
-
-         grub_ata_device_initialize (controller * 2 + i, 1, rega, regb);
-
-         /* Likewise.  */
-         if (grub_errno)
-           {
-             grub_print_error ();
-             grub_errno = GRUB_ERR_NONE;
-           }
-       }
-    }
-
-  controller++;
-
-  return 0;
-}
-
-static grub_err_t
-grub_ata_initialize (void)
-{
-  grub_pci_iterate (grub_ata_pciinit);
-  return 0;
-}
-
-static void
-grub_ata_setlba (struct grub_ata_device *dev, grub_disk_addr_t sector,
-                grub_size_t size)
-{
-  grub_ata_regset (dev, GRUB_ATA_REG_SECTORS, size);
-  grub_ata_regset (dev, GRUB_ATA_REG_LBALOW, sector & 0xFF);
-  grub_ata_regset (dev, GRUB_ATA_REG_LBAMID, (sector >> 8) & 0xFF);
-  grub_ata_regset (dev, GRUB_ATA_REG_LBAHIGH, (sector >> 16) & 0xFF);
-}
-
-static grub_err_t
-grub_ata_setaddress (struct grub_ata_device *dev,
-                    grub_ata_addressing_t addressing,
+grub_ata_setaddress (struct grub_ata *dev,
+                    struct grub_disk_ata_pass_through_parms *parms,
                     grub_disk_addr_t sector,
                     grub_size_t size)
 {
-  switch (addressing)
+  switch (dev->addr)
     {
     case GRUB_ATA_CHS:
       {
@@ -544,14 +193,11 @@ grub_ata_setaddress (struct grub_ata_device *dev,
          return grub_error (GRUB_ERR_OUT_OF_RANGE,
                             "sector %d cannot be addressed "
                             "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);
+       
+       parms->taskfile.disk = head;
+       parms->taskfile.sectnum = sect;
+       parms->taskfile.cyllsb = cylinder & 0xFF;
+       parms->taskfile.cylmsb = cylinder >> 8;
 
        break;
       }
@@ -559,26 +205,31 @@ grub_ata_setaddress (struct grub_ata_device *dev,
     case GRUB_ATA_LBA:
       if (size == 256)
        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;
+      parms->taskfile.disk = ((sector >> 24) & 0x0F);
 
-      grub_ata_setlba (dev, sector, size);
+      parms->taskfile.sectors = size;
+      parms->taskfile.lba_low = sector & 0xFF;
+      parms->taskfile.lba_mid = (sector >> 8) & 0xFF;
+      parms->taskfile.lba_high = (sector >> 16) & 0xFF;
       break;
 
     case GRUB_ATA_LBA48:
       if (size == 65536)
        size = 0;
 
-      grub_ata_regset (dev, GRUB_ATA_REG_DISK, 0xE0 | (dev->device << 4));
-      if (grub_ata_check_ready (dev))
-       return grub_errno;
+      parms->taskfile.disk = 0;
 
       /* Set "Previous".  */
-      grub_ata_setlba (dev, sector >> 24, size >> 8);
+      parms->taskfile.sectors = size & 0xFF;
+      parms->taskfile.lba_low = sector & 0xFF;
+      parms->taskfile.lba_mid = (sector >> 8) & 0xFF;
+      parms->taskfile.lba_high = (sector >> 16) & 0xFF;
+
       /* Set "Current".  */
-      grub_ata_setlba (dev, sector, size);
+      parms->taskfile.sectors48 = (size >> 8) & 0xFF;
+      parms->taskfile.lba48_low = (sector >> 24) & 0xFF;
+      parms->taskfile.lba48_mid = (sector >> 32) & 0xFF;
+      parms->taskfile.lba48_high = (sector >> 40) & 0xFF;
 
       break;
     }
@@ -590,14 +241,15 @@ static grub_err_t
 grub_ata_readwrite (grub_disk_t disk, grub_disk_addr_t sector,
                    grub_size_t size, char *buf, int rw)
 {
-  struct grub_ata_device *dev = (struct grub_ata_device *) disk->data;
-
-  grub_dprintf("ata", "grub_ata_readwrite (size=%llu, rw=%d)\n", (unsigned long long) size, rw);
+  struct grub_ata *ata = disk->data;
 
-  grub_ata_addressing_t addressing = dev->addr;
+  grub_ata_addressing_t addressing = ata->addr;
   grub_size_t batch;
   int cmd, cmd_write;
 
+  grub_dprintf("ata", "grub_ata_readwrite (size=%llu, rw=%d)\n",
+              (unsigned long long) size, rw);
+
   if (addressing == GRUB_ATA_LBA48 && ((sector + size) >> 28) != 0)
     {
       batch = 65536;
@@ -608,7 +260,10 @@ grub_ata_readwrite (grub_disk_t disk, grub_disk_addr_t sector,
     {
       if (addressing == GRUB_ATA_LBA48)
        addressing = GRUB_ATA_LBA;
-      batch = 256;
+      if (addressing != GRUB_ATA_CHS)
+       batch = 256;
+      else
+       batch = 1;
       cmd = GRUB_ATA_CMD_READ_SECTORS;
       cmd_write = GRUB_ATA_CMD_WRITE_SECTORS;
     }
@@ -616,44 +271,25 @@ grub_ata_readwrite (grub_disk_t disk, grub_disk_addr_t sector,
   grub_size_t nsectors = 0;
   while (nsectors < size)
     {
+      struct grub_disk_ata_pass_through_parms parms;
+      grub_err_t err;
+
       if (size - nsectors < batch)
        batch = size - nsectors;
 
       grub_dprintf("ata", "rw=%d, sector=%llu, batch=%llu\n", rw, (unsigned long long) sector, (unsigned long long) batch);
-
-      /* Send read/write command.  */
-      if (grub_ata_setaddress (dev, addressing, sector, batch))
-       return grub_errno;
-
-      grub_ata_regset (dev, GRUB_ATA_REG_CMD, (! rw ? cmd : cmd_write));
-
-      unsigned sect;
-      for (sect = 0; sect < batch; sect++)
-       {
-         /* Wait for !BSY, DRQ.  */
-         if (grub_ata_wait_drq (dev, rw, 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);
-
-         buf += GRUB_DISK_SECTOR_SIZE;
-       }
-
-      if (rw)
-        {
-         /* Check for write error.  */
-         if (grub_ata_wait_not_busy (dev, GRUB_ATA_TOUT_DATA))
-           return grub_errno;
-
-         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");
-       }
-
+      grub_memset (&parms, 0, sizeof (parms));
+      grub_ata_setaddress (ata, &parms, sector, batch);
+      parms.taskfile.cmd = (! rw ? cmd : cmd_write);
+      parms.buffer = buf;
+      parms.size = batch * GRUB_DISK_SECTOR_SIZE;
+  
+      err = ata->dev->readwrite (ata, &parms);
+      if (err)
+       return err;
+      if (parms.size != batch * GRUB_DISK_SECTOR_SIZE)
+       return grub_error (GRUB_ERR_READ_ERROR, "incomplete read");
+      buf += GRUB_DISK_SECTOR_SIZE * batch;
       sector += batch;
       nsectors += batch;
     }
@@ -663,76 +299,118 @@ grub_ata_readwrite (grub_disk_t disk, grub_disk_addr_t sector,
 
 \f
 
-static int
-grub_ata_iterate (int (*hook) (const char *name))
+static inline void
+grub_ata_real_close (struct grub_ata *ata)
 {
-  struct grub_ata_device *dev;
+  if (ata->dev->close)
+    ata->dev->close (ata);
+}
+
+static struct grub_ata *
+grub_ata_real_open (int id, int bus)
+{
+  struct grub_ata *ata;
+  grub_ata_dev_t p;
 
-  for (dev = grub_ata_devices; dev; dev = dev->next)
+  ata = grub_malloc (sizeof (*ata));
+  if (!ata)
+    return NULL;
+  for (p = grub_ata_dev_list; p; p = p->next)
     {
-      char devname[10];
       grub_err_t err;
-
-      err = check_device (dev);
-      if (err)
+      if (p->open (id, bus, ata))
        {
          grub_errno = GRUB_ERR_NONE;
          continue;
        }
+      ata->dev = p;
+      /* Use the IDENTIFY DEVICE command to query the device.  */
+      err = grub_ata_identify (ata);
+      if (err)
+       {
+         grub_free (ata);
+         return NULL;
+       }
+      return ata;
+    }
+  grub_free (ata);
+  grub_error (GRUB_ERR_UNKNOWN_DEVICE, "no such ATA device");
+  return NULL;
+}
 
-      if (dev->atapi)
-       continue;
-
-      grub_snprintf (devname, sizeof (devname), 
-                    "ata%d", dev->port * 2 + dev->device);
+static int
+grub_ata_iterate (int (*hook_in) (const char *name))
+{
+  auto int hook (int id, int bus);
+  int hook (int id, int bus)
+  {
+    struct grub_ata *ata;
+    int ret;
+    char devname[40];
 
-      if (hook (devname))
-       return 1;
-    }
+    ata = grub_ata_real_open (id, bus);
 
+    if (!ata)
+      {
+       grub_errno = GRUB_ERR_NONE;
+       return 0;
+      }
+    if (ata->atapi)
+      {
+       grub_ata_real_close (ata);
+       return 0;
+      }
+    grub_snprintf (devname, sizeof (devname), 
+                  "%s%d", grub_scsi_names[id], bus);
+    ret = hook_in (devname);
+    grub_ata_real_close (ata);
+    return ret;
+  }
+
+  grub_ata_dev_t p;
+  
+  for (p = grub_ata_dev_list; p; p = p->next)
+    if (p->iterate && p->iterate (hook))
+      return 1;
   return 0;
 }
 
 static grub_err_t
 grub_ata_open (const char *name, grub_disk_t disk)
 {
-  struct grub_ata_device *dev;
-  grub_err_t err;
-
-  for (dev = grub_ata_devices; dev; dev = dev->next)
-    {
-      char devname[10];
-      grub_snprintf (devname, sizeof (devname),
-                    "ata%d", dev->port * 2 + dev->device);
-      if (grub_strcmp (name, devname) == 0)
-       break;
-    }
-
-  if (! dev)
-    return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "can't open device");
+  unsigned id, bus;
+  struct grub_ata *ata;
 
-  if (dev->atapi)
+  for (id = 0; id < GRUB_SCSI_NUM_SUBSYSTEMS; id++)
+    if (grub_strncmp (grub_scsi_names[id], name,
+                     grub_strlen (grub_scsi_names[id])) == 0
+       && grub_isdigit (name[grub_strlen (grub_scsi_names[id])]))
+      break;
+  if (id == GRUB_SCSI_NUM_SUBSYSTEMS)
     return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "not an ATA harddisk");
+  bus = grub_strtoul (name + grub_strlen (grub_scsi_names[id]), 0, 0);
+  ata = grub_ata_real_open (id, bus);
+  if (!ata)
+    return grub_errno;
 
-  err = check_device (dev);
-
-  if (err)
-    return err;
+  if (ata->atapi)
+    return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "not an ATA harddisk");
 
-  disk->total_sectors = dev->size;
+  disk->total_sectors = ata->size;
 
-  disk->id = (unsigned long) dev;
+  disk->id = grub_make_scsi_id (id, bus, 0);
 
   disk->has_partitions = 1;
-  disk->data = dev;
+  disk->data = ata;
 
   return 0;
 }
 
 static void
-grub_ata_close (grub_disk_t disk __attribute__((unused)))
+grub_ata_close (grub_disk_t disk)
 {
-
+  struct grub_ata *ata = disk->data;
+  grub_ata_real_close (ata);
 }
 
 static grub_err_t
@@ -767,70 +445,35 @@ static struct grub_disk_dev grub_atadisk_dev =
 \f
 /* ATAPI code.  */
 
-static int
-grub_atapi_iterate (int (*hook) (int bus, int luns))
-{
-  struct grub_ata_device *dev;
-
-  for (dev = grub_ata_devices; dev; dev = dev->next)
-    {
-      grub_err_t err;
-
-      err = check_device (dev);
-      if (err)
-       {
-         grub_errno = GRUB_ERR_NONE;
-         continue;
-       }
-
-      if (! dev->atapi)
-       continue;
-
-      if (hook (dev->port * 2 + dev->device, 1))
-       return 1;
-    }
-
-  return 0;
-
-}
-
 static grub_err_t
-grub_atapi_read (struct grub_scsi *scsi,
-                grub_size_t cmdsize __attribute__((unused)),
-                char *cmd, grub_size_t size, char *buf)
+grub_atapi_read (struct grub_scsi *scsi, grub_size_t cmdsize, char *cmd,
+                grub_size_t size, char *buf)
 {
-  struct grub_ata_device *dev = (struct grub_ata_device *) scsi->data;
+  struct grub_ata *dev = scsi->data;
+  struct grub_disk_ata_pass_through_parms parms;
+  grub_err_t err;
 
   grub_dprintf("ata", "grub_atapi_read (size=%llu)\n", (unsigned long long) size);
+  grub_memset (&parms, 0, sizeof (parms));
+
+  parms.taskfile.disk = 0;
+  parms.taskfile.features = 0;
+  parms.taskfile.atapi_ireason = 0;
+  parms.taskfile.atapi_cnthigh = size >> 8;
+  parms.taskfile.atapi_cntlow = size & 0xff;
+  parms.taskfile.cmd = GRUB_ATA_CMD_PACKET;
+  parms.cmd = cmd;
+  parms.cmdsize = cmdsize;
+
+  parms.size = size;
+  parms.buffer = buf;
+  
+  err = dev->dev->readwrite (dev, &parms);
+  if (err)
+    return err;
 
-  if (grub_atapi_packet (dev, cmd, size))
-    return grub_errno;
-
-  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;
-
-      /* 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_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));
-
-      nread += cnt;
-    }
-
+  if (parms.size != size)
+    return grub_error (GRUB_ERR_READ_ERROR, "incomplete ATAPI read");
   return GRUB_ERR_NONE;
 }
 
@@ -846,64 +489,98 @@ grub_atapi_write (struct grub_scsi *scsi __attribute__((unused)),
 }
 
 static grub_err_t
-grub_atapi_open (int devnum, struct grub_scsi *scsi)
+grub_atapi_open (int id, int bus, struct grub_scsi *scsi)
 {
-  struct grub_ata_device *dev;
-  struct grub_ata_device *devfnd = 0;
-  grub_err_t err;
+  struct grub_ata *ata;
 
-  for (dev = grub_ata_devices; dev; dev = dev->next)
-    {
-      if (dev->port * 2 + dev->device == devnum)
-       {
-         devfnd = dev;
-         break;
-       }
-    }
+  ata = grub_ata_real_open (id, bus);
+  if (!ata)
+    return grub_errno;
+    
+  if (! ata->atapi)
+    return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "no such ATAPI device");
 
-  grub_dprintf ("ata", "opening ATAPI dev `ata%d'\n", devnum);
+  scsi->data = ata;
 
-  if (! devfnd)
-    return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "no such ATAPI device");
+  return GRUB_ERR_NONE;
+}
 
-  err = check_device (devfnd);
-  if (err)
-    return err;
+static int
+grub_atapi_iterate (int (*hook_in) (int id, int bus, int luns))
+{
+  auto int hook (int id, int bus);
+  int hook (int id, int bus)
+  {
+    struct grub_ata *ata;
+    int ret;
 
-  if (! devfnd->atapi)
-    return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "no such ATAPI device");
+    ata = grub_ata_real_open (id, bus);
 
-  scsi->data = devfnd;
+    if (!ata)
+      {
+       grub_errno = GRUB_ERR_NONE;
+       return 0;
+      }
+    if (!ata->atapi)
+      {
+       grub_ata_real_close (ata);
+       return 0;
+      }
+    ret = hook_in (id, bus, 1);
+    grub_ata_real_close (ata);
+    return ret;
+  }
+
+  grub_ata_dev_t p;
+  
+  for (p = grub_ata_dev_list; p; p = p->next)
+    if (p->iterate && p->iterate (hook))
+      return 1;
+  return 0;
+}
 
-  return GRUB_ERR_NONE;
+static void
+grub_atapi_close (grub_scsi_t disk)
+{
+  struct grub_ata *ata = disk->data;
+  grub_ata_real_close (ata);
+}
+
+
+void
+grub_ata_dev_register (grub_ata_dev_t dev)
+{
+  dev->next = grub_ata_dev_list;
+  grub_ata_dev_list = dev;
 }
 
+void
+grub_ata_dev_unregister (grub_ata_dev_t dev)
+{
+  grub_ata_dev_t *p, q;
+
+  for (p = &grub_ata_dev_list, q = *p; q; p = &(q->next), q = q->next)
+    if (q == dev)
+      {
+        *p = q->next;
+       break;
+      }
+}
 
 static struct grub_scsi_dev grub_atapi_dev =
   {
-    .name = "ata",
-    .id = GRUB_SCSI_SUBSYSTEM_ATAPI,
     .iterate = grub_atapi_iterate,
     .open = grub_atapi_open,
+    .close = grub_atapi_close,
     .read = grub_atapi_read,
-    .write = grub_atapi_write
+    .write = grub_atapi_write,
+    .next = 0
   };
 
 \f
 
 GRUB_MOD_INIT(ata)
 {
-  /* To prevent two drivers operating on the same disks.  */
-  grub_disk_firmware_is_tainted = 1;
-  if (grub_disk_firmware_fini)
-    {
-      grub_disk_firmware_fini ();
-      grub_disk_firmware_fini = NULL;
-    }
-
-  /* ATA initialization.  */
-  grub_ata_initialize ();
-
   grub_disk_dev_register (&grub_atadisk_dev);
 
   /* ATAPI devices are handled by scsi.mod.  */
index f52725a49fd3dffca2d6acecedbb4d8aed27fe03..b6edf78d68988f58c0347affde655fabf3228980 100644 (file)
  */
 
 #include <grub/ata.h>
+#include <grub/scsi.h>
 #include <grub/disk.h>
 #include <grub/dl.h>
 #include <grub/mm.h>
+#include <grub/pci.h>
+#include <grub/cs5536.h>
+#include <grub/time.h>
 
+/* At the moment, only two IDE ports are supported.  */
+static const grub_port_t grub_pata_ioaddress[] = { GRUB_ATA_CH0_PORT1,
+                                                  GRUB_ATA_CH1_PORT1 };
+static const grub_port_t grub_pata_ioaddress2[] = { GRUB_ATA_CH0_PORT2, 
+                                                   GRUB_ATA_CH1_PORT2 };
 
-/* ATA pass through support, used by hdparm.mod.  */
+struct grub_pata_device
+{
+  /* IDE port to use.  */
+  int port;
+
+  /* IO addresses on which the registers for this device can be
+     found.  */
+  grub_port_t ioaddress;
+  grub_port_t ioaddress2;
+
+  /* Two devices can be connected to a single cable.  Use this field
+     to select device 0 (commonly known as "master") or device 1
+     (commonly known as "slave").  */
+  int device;
+
+  struct grub_pata_device *next;
+};
+
+static struct grub_pata_device *grub_pata_devices;
+
+static inline void
+grub_pata_regset (struct grub_pata_device *dev, int reg, int val)
+{
+  grub_outb (val, dev->ioaddress + reg);
+}
+
+static inline grub_uint8_t
+grub_pata_regget (struct grub_pata_device *dev, int reg)
+{
+  return grub_inb (dev->ioaddress + reg);
+}
+
+static inline void
+grub_pata_regset2 (struct grub_pata_device *dev, int reg, int val)
+{
+  grub_outb (val, dev->ioaddress2 + reg);
+}
+
+static inline grub_uint8_t
+grub_pata_regget2 (struct grub_pata_device *dev, int reg)
+{
+  return grub_inb (dev->ioaddress2 + reg);
+}
+
+/* Wait for !BSY.  */
 static grub_err_t
-grub_ata_pass_through (grub_disk_t disk,
-                      struct grub_disk_ata_pass_through_parms *parms)
+grub_pata_wait_not_busy (struct grub_pata_device *dev, int milliseconds)
+{
+  /* 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);
+
+  int i = 1;
+  grub_uint8_t sts;
+  while ((sts = grub_pata_regget (dev, GRUB_ATA_REG_STATUS))
+        & GRUB_ATA_STATUS_BUSY)
+    {
+      if (i >= milliseconds)
+        {
+         grub_dprintf ("pata", "timeout: %dms, status=0x%x\n",
+                       milliseconds, sts);
+         return grub_error (GRUB_ERR_TIMEOUT, "PATA timeout");
+       }
+
+      grub_millisleep (1);
+      i++;
+    }
+
+  return GRUB_ERR_NONE;
+}
+
+static inline grub_err_t
+grub_pata_check_ready (struct grub_pata_device *dev)
 {
-  if (disk->dev->id != GRUB_DISK_DEVICE_ATA_ID)
-    return grub_error (GRUB_ERR_BAD_DEVICE,
-                      "device not accessed via ata.mod");
+  if (grub_pata_regget (dev, GRUB_ATA_REG_STATUS) & GRUB_ATA_STATUS_BUSY)
+    return grub_pata_wait_not_busy (dev, GRUB_ATA_TOUT_STD);
 
-  struct grub_ata_device *dev = (struct grub_ata_device *) disk->data;
+  return GRUB_ERR_NONE;
+}
 
-  if (! (parms->size == 0 || parms->size == GRUB_DISK_SECTOR_SIZE))
+static inline void
+grub_pata_wait (void)
+{
+  grub_millisleep (50);
+}
+
+static void
+grub_pata_pio_read (struct grub_pata_device *dev, char *buf, grub_size_t size)
+{ 
+  grub_uint16_t *buf16 = (grub_uint16_t *) buf;
+  unsigned int i;
+
+  /* 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 (size & 1)
+    buf[size - 1] = (char) grub_le_to_cpu16 (grub_inw (dev->ioaddress
+                                                      + GRUB_ATA_REG_DATA));
+}
+
+static void
+grub_pata_pio_write (struct grub_pata_device *dev, char *buf, grub_size_t size)
+{
+  grub_uint16_t *buf16 = (grub_uint16_t *) buf;
+  unsigned int i;
+
+  /* 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);
+}
+
+/* ATA pass through support, used by hdparm.mod.  */
+static grub_err_t
+grub_pata_readwrite (struct grub_ata *disk,
+                    struct grub_disk_ata_pass_through_parms *parms)
+{
+  struct grub_pata_device *dev = (struct grub_pata_device *) disk->data;
+  grub_size_t nread = 0;
+
+  if (! (parms->cmdsize == 0 || parms->cmdsize == 12))
     return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
-                      "ATA multi-sector read and DATA OUT not implemented");
+                      "ATAPI non-12 byte commands not supported");
 
-  grub_dprintf ("ata", "ata_pass_through: cmd=0x%x, features=0x%x, sectors=0x%x\n",
-               parms->taskfile[GRUB_ATA_REG_CMD],
-               parms->taskfile[GRUB_ATA_REG_FEATURES],
-               parms->taskfile[GRUB_ATA_REG_SECTORS]);
-  grub_dprintf ("ata", "lba_high=0x%x, lba_mid=0x%x, lba_low=0x%x, size=%d\n",
-               parms->taskfile[GRUB_ATA_REG_LBAHIGH],
-               parms->taskfile[GRUB_ATA_REG_LBAMID],
-               parms->taskfile[GRUB_ATA_REG_LBALOW], parms->size);
+  grub_dprintf ("pata", "pata_pass_through: cmd=0x%x, features=0x%x, sectors=0x%x\n",
+               parms->taskfile.cmd,
+               parms->taskfile.features,
+               parms->taskfile.sectors);
+  grub_dprintf ("pata", "lba_high=0x%x, lba_mid=0x%x, lba_low=0x%x, size=%d\n",
+               parms->taskfile.lba_high,
+               parms->taskfile.lba_mid,
+               parms->taskfile.lba_low, parms->size);
 
   /* Set registers.  */
-  grub_ata_regset (dev, GRUB_ATA_REG_DISK, 0xE0 | dev->device << 4
-                  | (parms->taskfile[GRUB_ATA_REG_DISK] & 0xf));
-  if (grub_ata_check_ready (dev))
+  grub_pata_regset (dev, GRUB_ATA_REG_DISK, (parms->cmdsize ? 0 : 0xE0)
+                   | dev->device << 4
+                   | (parms->taskfile.disk & 0xf));
+  if (grub_pata_check_ready (dev))
     return grub_errno;
 
   int i;
+  for (i = GRUB_ATA_REG_SECTORS; i <= GRUB_ATA_REG_LBAHIGH; i++)
+    grub_pata_regset (dev, i,
+                    parms->taskfile.raw[7 + (i - GRUB_ATA_REG_SECTORS)]);
   for (i = GRUB_ATA_REG_FEATURES; i <= GRUB_ATA_REG_LBAHIGH; i++)
-    grub_ata_regset (dev, i, parms->taskfile[i]);
+    grub_pata_regset (dev, i, parms->taskfile.raw[i - GRUB_ATA_REG_FEATURES]);
 
   /* Start command. */
-  grub_ata_regset (dev, GRUB_ATA_REG_CMD, parms->taskfile[GRUB_ATA_REG_CMD]);
+  grub_pata_regset (dev, GRUB_ATA_REG_CMD, parms->taskfile.cmd);
 
   /* Wait for !BSY.  */
-  if (grub_ata_wait_not_busy (dev, GRUB_ATA_TOUT_DATA))
+  if (grub_pata_wait_not_busy (dev, GRUB_ATA_TOUT_DATA))
     return grub_errno;
 
   /* Check status.  */
-  grub_int8_t sts = grub_ata_regget (dev, GRUB_ATA_REG_STATUS);
-  grub_dprintf ("ata", "status=0x%x\n", sts);
+  grub_int8_t sts = grub_pata_regget (dev, GRUB_ATA_REG_STATUS);
+  grub_dprintf ("pata", "status=0x%x\n", sts);
+
+  if (parms->cmdsize)
+    {
+      grub_uint8_t irs = grub_pata_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) == GRUB_ATAPI_IREASON_CMD_OUT))
+       return grub_error (GRUB_ERR_READ_ERROR, "ATAPI protocol error");
+      /* Write the packet.  */
+      grub_pata_pio_write (dev, parms->cmd, parms->cmdsize);
+    }
 
   /* Transfer data.  */
-  if ((sts & (GRUB_ATA_STATUS_DRQ | GRUB_ATA_STATUS_ERR)) == GRUB_ATA_STATUS_DRQ)
+  while (nread < parms->size
+        && ((sts & (GRUB_ATA_STATUS_DRQ | GRUB_ATA_STATUS_ERR))
+            == GRUB_ATA_STATUS_DRQ)
+        && (!parms->cmdsize 
+            || ((grub_pata_regget (dev, GRUB_ATAPI_REG_IREASON)
+                 & GRUB_ATAPI_IREASON_MASK) == GRUB_ATAPI_IREASON_DATA_IN)))
     {
-      if (parms->size != GRUB_DISK_SECTOR_SIZE)
-        return grub_error (GRUB_ERR_READ_ERROR, "DRQ unexpected");
-      grub_ata_pio_read (dev, parms->buffer, GRUB_DISK_SECTOR_SIZE);
+      unsigned cnt;
+      if (parms->cmdsize)
+       {
+         cnt = grub_pata_regget (dev, GRUB_ATAPI_REG_CNTHIGH) << 8
+           | grub_pata_regget (dev, GRUB_ATAPI_REG_CNTLOW);
+         grub_dprintf("pata", "DRQ count=%u\n", cnt);
+
+         /* Count of last transfer may be uneven.  */
+         if (! (0 < cnt && cnt <= parms->size - nread
+                && (! (cnt & 1) || cnt == parms->size - nread)))
+           return grub_error (GRUB_ERR_READ_ERROR,
+                              "invalid ATAPI transfer count");
+       }
+      else
+       cnt = GRUB_DISK_SECTOR_SIZE;
+      if (cnt > parms->size - nread)
+       cnt = parms->size - nread;
+
+      if (parms->write)
+       grub_pata_pio_write (dev, (char *) parms->buffer + nread, cnt);
+      else
+       grub_pata_pio_read (dev, (char *) parms->buffer + nread, cnt);
+
+      nread += cnt;
+    }
+  if (parms->write)
+    {
+      /* Check for write error.  */
+      if (grub_pata_wait_not_busy (dev, GRUB_ATA_TOUT_DATA))
+       return grub_errno;
+
+      if (grub_pata_regget (dev, GRUB_ATA_REG_STATUS)
+         & (GRUB_ATA_STATUS_DRQ | GRUB_ATA_STATUS_ERR))
+       return grub_error (GRUB_ERR_WRITE_ERROR, "ATA write error");
     }
+  parms->size = nread;
 
   /* Return registers.  */
   for (i = GRUB_ATA_REG_ERROR; i <= GRUB_ATA_REG_STATUS; i++)
-    parms->taskfile[i] = grub_ata_regget (dev, i);
+    parms->taskfile.raw[i - GRUB_ATA_REG_FEATURES] = grub_pata_regget (dev, i);
 
-  grub_dprintf ("ata", "status=0x%x, error=0x%x, sectors=0x%x\n",
-               parms->taskfile[GRUB_ATA_REG_STATUS],
-               parms->taskfile[GRUB_ATA_REG_ERROR],
-               parms->taskfile[GRUB_ATA_REG_SECTORS]);
+  grub_dprintf ("pata", "status=0x%x, error=0x%x, sectors=0x%x\n",
+               parms->taskfile.status,
+               parms->taskfile.error,
+               parms->taskfile.sectors);
 
-  if (parms->taskfile[GRUB_ATA_REG_STATUS]
+  if (parms->taskfile.status
       & (GRUB_ATA_STATUS_DRQ | GRUB_ATA_STATUS_ERR))
-    return grub_error (GRUB_ERR_READ_ERROR, "ATA passthrough failed");
+    return grub_error (GRUB_ERR_READ_ERROR, "PATA passthrough failed");
 
   return GRUB_ERR_NONE;
 }
 
+static grub_err_t
+check_device (struct grub_pata_device *dev)
+{
+  grub_pata_regset (dev, GRUB_ATA_REG_DISK, dev->device << 4);
+  grub_pata_wait ();
+
+  /* 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_pata_regset (dev, GRUB_ATA_REG_SECTORS, 0x5A);
+  grub_pata_wait ();
+  grub_uint8_t sec = grub_pata_regget (dev, GRUB_ATA_REG_SECTORS);
+  grub_dprintf ("ata", "sectors=0x%x\n", sec);
+  if (sec != 0x5A)
+    return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "no device connected");
+
+  /* The above test may detect a second (slave) device
+     connected to a SATA controller which supports only one
+     (master) device.  It is not safe to use the status register
+     READY bit to check for controller channel existence.  Some
+     ATAPI commands (RESET, DIAGNOSTIC) may clear this bit.  */
+
+  return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_pata_device_initialize (int port, int device, int addr, int addr2)
+{
+  struct grub_pata_device *dev;
+  struct grub_pata_device **devp;
+  grub_err_t err;
+
+  grub_dprintf ("pata", "detecting device %d,%d (0x%x, 0x%x)\n",
+               port, device, addr, addr2);
+
+  dev = grub_malloc (sizeof(*dev));
+  if (! dev)
+    return grub_errno;
+
+  /* Setup the device information.  */
+  dev->port = port;
+  dev->device = device;
+  dev->ioaddress = addr + GRUB_MACHINE_PCI_IO_BASE;
+  dev->ioaddress2 = addr2 + GRUB_MACHINE_PCI_IO_BASE;
+  dev->next = NULL;
+
+  /* Register the device.  */
+  for (devp = &grub_pata_devices; *devp; devp = &(*devp)->next);
+  *devp = dev;
+
+  err = check_device (dev);
+  if (err)
+    grub_print_error ();
+
+  return 0;
+}
+
+static int NESTED_FUNC_ATTR
+grub_pata_pciinit (grub_pci_device_t dev,
+                  grub_pci_id_t pciid)
+{
+  static int compat_use[2] = { 0 };
+  grub_pci_address_t addr;
+  grub_uint32_t class;
+  grub_uint32_t bar1;
+  grub_uint32_t bar2;
+  int rega;
+  int regb;
+  int i;
+  static int controller = 0;
+  int cs5536 = 0;
+  int nports = 2;
+
+  /* Read class.  */
+  addr = grub_pci_make_address (dev, GRUB_PCI_REG_CLASS);
+  class = grub_pci_read (addr);
+
+  /* AMD CS5536 Southbridge.  */
+  if (pciid == GRUB_CS5536_PCIID)
+    {
+      cs5536 = 1;
+      nports = 1;
+    }
+
+  /* Check if this class ID matches that of a PCI IDE Controller.  */
+  if (!cs5536 && (class >> 16 != 0x0101))
+    return 0;
+
+  for (i = 0; i < nports; i++)
+    {
+      /* Set to 0 when the channel operated in compatibility mode.  */
+      int compat;
+
+      /* We don't support non-compatibility mode for CS5536.  */
+      if (cs5536)
+       compat = 0;
+      else
+       compat = (class >> (8 + 2 * i)) & 1;
+
+      rega = 0;
+      regb = 0;
+
+      /* If the channel is in compatibility mode, just assign the
+        default registers.  */
+      if (compat == 0 && !compat_use[i])
+       {
+         rega = grub_pata_ioaddress[i];
+         regb = grub_pata_ioaddress2[i];
+         compat_use[i] = 1;
+       }
+      else if (compat)
+       {
+         /* Read the BARs, which either contain a mmapped IO address
+            or the IO port address.  */
+         addr = grub_pci_make_address (dev, GRUB_PCI_REG_ADDRESSES
+                                       + sizeof (grub_uint64_t) * i);
+         bar1 = grub_pci_read (addr);
+         addr = grub_pci_make_address (dev, GRUB_PCI_REG_ADDRESSES
+                                       + sizeof (grub_uint64_t) * i
+                                       + sizeof (grub_uint32_t));
+         bar2 = grub_pci_read (addr);
+
+         /* Check if the BARs describe an IO region.  */
+         if ((bar1 & 1) && (bar2 & 1))
+           {
+             rega = bar1 & ~3;
+             regb = bar2 & ~3;
+           }
+       }
+
+      grub_dprintf ("pata",
+                   "PCI dev (%d,%d,%d) compat=%d rega=0x%x regb=0x%x\n",
+                   grub_pci_get_bus (dev), grub_pci_get_device (dev),
+                   grub_pci_get_function (dev), compat, rega, regb);
+
+      if (rega && regb)
+       {
+         grub_errno = GRUB_ERR_NONE;
+         grub_pata_device_initialize (controller * 2 + i, 0, rega, regb);
+
+         /* Most errors raised by grub_ata_device_initialize() are harmless.
+            They just indicate this particular drive is not responding, most
+            likely because it doesn't exist.  We might want to ignore specific
+            error types here, instead of printing them.  */
+         if (grub_errno)
+           {
+             grub_print_error ();
+             grub_errno = GRUB_ERR_NONE;
+           }
+
+         grub_pata_device_initialize (controller * 2 + i, 1, rega, regb);
+
+         /* Likewise.  */
+         if (grub_errno)
+           {
+             grub_print_error ();
+             grub_errno = GRUB_ERR_NONE;
+           }
+       }
+    }
+
+  controller++;
+
+  return 0;
+}
+
+static grub_err_t
+grub_pata_initialize (void)
+{
+  grub_pci_iterate (grub_pata_pciinit);
+  return 0;
+}
+
+static grub_err_t
+grub_pata_open (int id, int devnum, struct grub_ata *ata)
+{
+  struct grub_pata_device *dev;
+  struct grub_pata_device *devfnd = 0;
+  grub_err_t err;
+
+  if (id != GRUB_SCSI_SUBSYSTEM_PATA)
+    return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "not a PATA device");
+
+  for (dev = grub_pata_devices; dev; dev = dev->next)
+    {
+      if (dev->port * 2 + dev->device == devnum)
+       {
+         devfnd = dev;
+         break;
+       }
+    }
+
+  grub_dprintf ("pata", "opening PATA dev `ata%d'\n", devnum);
+
+  if (! devfnd)
+    return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "no such PATA device");
+
+  err = check_device (devfnd);
+  if (err)
+    return err;
+
+  ata->data = devfnd;
+
+  return GRUB_ERR_NONE;
+}
+
+static int
+grub_pata_iterate (int (*hook) (int id, int bus))
+{
+  struct grub_pata_device *dev;
+
+  for (dev = grub_pata_devices; dev; dev = dev->next)
+    if (hook (GRUB_SCSI_SUBSYSTEM_PATA, dev->port * 2 + dev->device))
+      return 1;
+
+  return 0;
+}
+
+
+static struct grub_ata_dev grub_pata_dev =
+  {
+    .iterate = grub_pata_iterate,
+    .open = grub_pata_open,
+    .readwrite = grub_pata_readwrite,
+  };
+
+
 \f
 
 GRUB_MOD_INIT(ata_pthru)
 {
-  /* Register ATA pass through function.  */
-  grub_disk_ata_pass_through = grub_ata_pass_through;
+  /* To prevent two drivers operating on the same disks.  */
+  grub_disk_firmware_is_tainted = 1;
+  if (grub_disk_firmware_fini)
+    {
+      grub_disk_firmware_fini ();
+      grub_disk_firmware_fini = NULL;
+    }
+
+  /* ATA initialization.  */
+  grub_pata_initialize ();
+
+  grub_ata_dev_register (&grub_pata_dev);
 }
 
 GRUB_MOD_FINI(ata_pthru)
 {
-  if (grub_disk_ata_pass_through == grub_ata_pass_through)
-    grub_disk_ata_pass_through = NULL;
+  grub_ata_dev_unregister (&grub_pata_dev);
 }
index 60192bef5b6b7a0f5c047298a386d394fb7c40e4..f0594e7c7c5e35cfc70df3f03910f6f354015d4d 100644 (file)
 \f
 static grub_scsi_dev_t grub_scsi_dev_list;
 
+char grub_scsi_names[GRUB_SCSI_NUM_SUBSYSTEMS][5] = {
+  [GRUB_SCSI_SUBSYSTEM_USBMS] = "usb",
+  [GRUB_SCSI_SUBSYSTEM_PATA] = "ata",
+  [GRUB_SCSI_SUBSYSTEM_AHCI] = "ahci"
+};
+
 void
 grub_scsi_dev_register (grub_scsi_dev_t dev)
 {
@@ -318,9 +324,9 @@ grub_scsi_iterate (int (*hook) (const char *name))
 {
   grub_scsi_dev_t p;
 
-  auto int scsi_iterate (int bus, int luns);
+  auto int scsi_iterate (int id, int bus, int luns);
 
-  int scsi_iterate (int bus, int luns)
+  int scsi_iterate (int id, int bus, int luns)
     {
       int i;
 
@@ -329,7 +335,7 @@ grub_scsi_iterate (int (*hook) (const char *name))
        {
          char *sname;
          int ret;
-         sname = grub_xasprintf ("%s%d", p->name, bus);
+         sname = grub_xasprintf ("%s%d", grub_scsi_names[id], bus);
          if (!sname)
            return 1;
          ret = hook (sname);
@@ -343,7 +349,7 @@ grub_scsi_iterate (int (*hook) (const char *name))
        {
          char *sname;
          int ret;
-         sname = grub_xasprintf ("%s%d%c", p->name, bus, 'a' + i);
+         sname = grub_xasprintf ("%s%d%c", grub_scsi_names[id], bus, 'a' + i);
          if (!sname)
            return 1;
          ret = hook (sname);
@@ -370,6 +376,7 @@ grub_scsi_open (const char *name, grub_disk_t disk)
   int lun, bus;
   grub_uint64_t maxtime;
   const char *nameend;
+  unsigned id;
 
   nameend = name + grub_strlen (name) - 1;
   /* Try to detect a LUN ('a'-'z'), otherwise just use the first
@@ -394,15 +401,25 @@ grub_scsi_open (const char *name, grub_disk_t disk)
   if (! scsi)
     return grub_errno;
 
-  for (p = grub_scsi_dev_list; p; p = p->next)
+  for (id = 0; id < ARRAY_SIZE (grub_scsi_names); id++)
+    if (grub_strncmp (grub_scsi_names[id], name, nameend - name) == 0)
+      break;
+
+  if (id == ARRAY_SIZE (grub_scsi_names))
     {
-      if (grub_strncmp (p->name, name, nameend - name) != 0)
-       continue;
+      grub_free (scsi);
+      return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "not a SCSI disk");
+    }
 
-      if (p->open (bus, scsi))
-       continue;
+  for (p = grub_scsi_dev_list; p; p = p->next)
+    {
+      if (p->open (id, bus, scsi))
+       {
+         grub_errno = GRUB_ERR_NONE;
+         continue;
+       }
 
-      disk->id = grub_make_scsi_id (p->id, bus, lun);
+      disk->id = grub_make_scsi_id (id, bus, lun);
       disk->data = scsi;
       scsi->dev = p;
       scsi->lun = lun;
@@ -484,7 +501,6 @@ grub_scsi_open (const char *name, grub_disk_t disk)
     }
 
   grub_free (scsi);
-
   return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "not a SCSI disk");
 }
 
index 225761e0f18f1d7b0b00418d5f8740c4d898a253..0ff2e6235ae7db6a31e00421dc538fd72c432c1e 100644 (file)
@@ -205,7 +205,7 @@ grub_usbms_attach (grub_usb_device_t usbdev, int configno, int interfno)
 \f
 
 static int
-grub_usbms_iterate (int (*hook) (int bus, int luns))
+grub_usbms_iterate (int (*hook) (int id, int bus, int luns))
 {
   unsigned i;
 
@@ -214,7 +214,7 @@ grub_usbms_iterate (int (*hook) (int bus, int luns))
   for (i = 0; i < ARRAY_SIZE (grub_usbms_devices); i++)
     if (grub_usbms_devices[i])
       {
-       if (hook (i, grub_usbms_devices[i]->luns))
+       if (hook (GRUB_SCSI_SUBSYSTEM_USBMS, i, grub_usbms_devices[i]->luns))
          return 1;
       }
 
@@ -384,8 +384,12 @@ grub_usbms_write (struct grub_scsi *scsi, grub_size_t cmdsize, char *cmd,
 }
 
 static grub_err_t
-grub_usbms_open (int devnum, struct grub_scsi *scsi)
+grub_usbms_open (int id, int devnum, struct grub_scsi *scsi)
 {
+  if (id != GRUB_SCSI_SUBSYSTEM_USBMS)
+    return grub_error (GRUB_ERR_UNKNOWN_DEVICE,
+                      "not USB Mass Storage device");
+
   grub_usb_poll_devices ();
 
   if (!grub_usbms_devices[devnum])
@@ -400,8 +404,6 @@ grub_usbms_open (int devnum, struct grub_scsi *scsi)
 
 static struct grub_scsi_dev grub_usbms_dev =
   {
-    .name = "usb",
-    .id = GRUB_SCSI_SUBSYSTEM_USBMS,
     .iterate = grub_usbms_iterate,
     .open = grub_usbms_open,
     .read = grub_usbms_read,
index 9e3aaf0e69407eec652328ec1d781c69fbad0088..b1c6ead26399e30f8f404890ea9375350017f732 100644 (file)
@@ -97,21 +97,64 @@ enum grub_ata_timeout_milliseconds
     GRUB_ATA_TOUT_DATA = 10000   /* 10s DATA I/O timeout.  */
   };
 
-struct grub_ata_device
+typedef union
 {
-  /* IDE port to use.  */
-  int port;
-
-  /* IO addresses on which the registers for this device can be
-     found.  */
-  grub_port_t ioaddress;
-  grub_port_t ioaddress2;
+  grub_uint8_t raw[11];
+  struct
+  {
+    union
+    {
+      grub_uint8_t features;
+      grub_uint8_t error;
+    };
+    union
+    {
+      grub_uint8_t sectors;
+      grub_uint8_t atapi_ireason;
+    };
+    union
+    {
+      grub_uint8_t lba_low;
+      grub_uint8_t sectnum;
+    };
+    union
+    {
+      grub_uint8_t lba_mid;
+      grub_uint8_t cyllsb;
+      grub_uint8_t atapi_cntlow;
+    };
+    union
+    {
+      grub_uint8_t lba_high;
+      grub_uint8_t cylmsb;
+      grub_uint8_t atapi_cnthigh;
+    };
+    grub_uint8_t disk;
+    union
+    {
+      grub_uint8_t cmd;
+      grub_uint8_t status;
+    };
+    grub_uint8_t sectors48;
+    grub_uint8_t lba48_low;
+    grub_uint8_t lba48_mid;
+    grub_uint8_t lba48_high;
+  };
+} grub_ata_regs_t;
 
-  /* Two devices can be connected to a single cable.  Use this field
-     to select device 0 (commonly known as "master") or device 1
-     (commonly known as "slave").  */
-  int device;
+/* ATA pass through parameters and function.  */
+struct grub_disk_ata_pass_through_parms
+{
+  grub_ata_regs_t taskfile;
+  void * buffer;
+  grub_size_t size;
+  int write;
+  void *cmd;
+  int cmdsize;
+};
 
+struct grub_ata
+{
   /* Addressing methods available for accessing this device.  If CHS
      is only available, use that.  Otherwise use LBA, except for the
      high sectors.  In that case use LBA48.  */
@@ -128,47 +171,36 @@ struct grub_ata_device
   /* Set to 0 for ATA, set to 1 for ATAPI.  */
   int atapi;
 
-  struct grub_ata_device *next;
+  void *data;
+
+  struct grub_ata_dev *dev;
 };
 
-grub_err_t EXPORT_FUNC(grub_ata_wait_not_busy) (struct grub_ata_device *dev,
-                                                int milliseconds);
-grub_err_t EXPORT_FUNC(grub_ata_wait_drq) (struct grub_ata_device *dev,
-                                          int rw, int milliseconds);
-void EXPORT_FUNC(grub_ata_pio_read) (struct grub_ata_device *dev,
-                                    char *buf, grub_size_t size);
+typedef struct grub_ata *grub_ata_t;
 
-static inline void
-grub_ata_regset (struct grub_ata_device *dev, int reg, int val)
+struct grub_ata_dev
 {
-  grub_outb (val, dev->ioaddress + reg);
-}
+  /* Call HOOK with each device name, until HOOK returns non-zero.  */
+  int (*iterate) (int (*hook) (int id, int bus));
 
-static inline grub_uint8_t
-grub_ata_regget (struct grub_ata_device *dev, int reg)
-{
-  return grub_inb (dev->ioaddress + reg);
-}
+  /* Open the device named NAME, and set up SCSI.  */
+  grub_err_t (*open) (int id, int bus, struct grub_ata *scsi);
 
-static inline void
-grub_ata_regset2 (struct grub_ata_device *dev, int reg, int val)
-{
-  grub_outb (val, dev->ioaddress2 + reg);
-}
+  /* Close the scsi device SCSI.  */
+  void (*close) (struct grub_ata *ata);
 
-static inline grub_uint8_t
-grub_ata_regget2 (struct grub_ata_device *dev, int reg)
-{
-  return grub_inb (dev->ioaddress2 + reg);
-}
+  /* Read SIZE bytes from the device SCSI into BUF after sending the
+     command CMD of size CMDSIZE.  */
+  grub_err_t (*readwrite) (struct grub_ata *ata,
+                          struct grub_disk_ata_pass_through_parms *parms);
 
-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);
+  /* The next scsi device.  */
+  struct grub_ata_dev *next;
+};
+
+typedef struct grub_ata_dev *grub_ata_dev_t;
 
-  return GRUB_ERR_NONE;
-}
+void grub_ata_dev_register (grub_ata_dev_t dev);
+void grub_ata_dev_unregister (grub_ata_dev_t dev);
 
 #endif /* ! GRUB_ATA_HEADER */
index e7f807e0efbe0c10c2ef48cc140d6ad77238a24f..ac3fb649703e7986080ce78d2bb0a5ed98c658fb 100644 (file)
@@ -165,16 +165,5 @@ grub_uint64_t EXPORT_FUNC(grub_disk_get_size) (grub_disk_t disk);
 
 extern void (* EXPORT_VAR(grub_disk_firmware_fini)) (void);
 extern int EXPORT_VAR(grub_disk_firmware_is_tainted);
-
-/* ATA pass through parameters and function.  */
-struct grub_disk_ata_pass_through_parms
-{
-  grub_uint8_t taskfile[8];
-  void * buffer;
-  int size;
-};
-
-extern grub_err_t (* EXPORT_VAR(grub_disk_ata_pass_through)) (grub_disk_t,
-                  struct grub_disk_ata_pass_through_parms *);
-
 #endif /* ! GRUB_DISK_HEADER */
index 289cd8e4a2266ff2fa9b067afee0480f9334dc4a..081189fb077743db017ed367c164a0cf0fa6a550 100644 (file)
@@ -29,10 +29,13 @@ struct grub_scsi;
 enum
   {
     GRUB_SCSI_SUBSYSTEM_USBMS,
-    GRUB_SCSI_SUBSYSTEM_ATAPI,
-    GRUB_SCSI_SUBSYSTEM_AHCI
+    GRUB_SCSI_SUBSYSTEM_PATA,
+    GRUB_SCSI_SUBSYSTEM_AHCI,
+    GRUB_SCSI_NUM_SUBSYSTEMS
   };
 
+extern char grub_scsi_names[GRUB_SCSI_NUM_SUBSYSTEMS][5];
+
 #define GRUB_SCSI_ID_SUBSYSTEM_SHIFT 24
 #define GRUB_SCSI_ID_BUS_SHIFT 8
 #define GRUB_SCSI_ID_LUN_SHIFT 0
@@ -46,16 +49,11 @@ grub_make_scsi_id (int subsystem, int bus, int lun)
 
 struct grub_scsi_dev
 {
-  /* The device name.  */
-  const char *name;
-
-  grub_uint8_t id;
-
   /* Call HOOK with each device name, until HOOK returns non-zero.  */
-  int (*iterate) (int (*hook) (int bus, int luns));
+  int (*iterate) (int (*hook) (int id, int bus, int luns));
 
   /* Open the device named NAME, and set up SCSI.  */
-  grub_err_t (*open) (int bus, struct grub_scsi *scsi);
+  grub_err_t (*open) (int id, int bus, struct grub_scsi *scsi);
 
   /* Close the scsi device SCSI.  */
   void (*close) (struct grub_scsi *scsi);
index ccd5f200f9bb9a60858b9331a0ce2143860666e9..84c48e65ce07ba70936b7146cb20829c4716ee78 100644 (file)
@@ -46,10 +46,6 @@ static struct grub_disk_cache grub_disk_cache_table[GRUB_DISK_CACHE_NUM];
 void (*grub_disk_firmware_fini) (void);
 int grub_disk_firmware_is_tainted;
 
-grub_err_t (* grub_disk_ata_pass_through) (grub_disk_t,
-           struct grub_disk_ata_pass_through_parms *);
-
-
 #if 0
 static unsigned long grub_disk_cache_hits;
 static unsigned long grub_disk_cache_misses;