+/* Sends an IDENTIFY DEVICE command to disk D and reads the
+ response. Initializes D's capacity member based on the result
+ and prints a message describing the disk to the console. */
+static void
+identify_ata_device (struct disk *d)
+{
+ struct channel *c = d->channel;
+ uint16_t id[DISK_SECTOR_SIZE / 2];
+
+ ASSERT (d->is_ata);
+
+ /* Send the IDENTIFY DEVICE command, wait for an interrupt
+ indicating the device's response is ready, and read the data
+ into our buffer. */
+ select_device_wait (d);
+ issue_pio_command (c, CMD_IDENTIFY_DEVICE);
+ sema_down (&c->completion_wait);
+ if (!wait_while_busy (d))
+ {
+ d->is_ata = false;
+ return;
+ }
+ input_sector (c, id);
+
+ /* Calculate capacity. */
+ d->capacity = id[60] | ((uint32_t) id[61] << 16);
+
+ /* Print identification message. */
+ printf ("%s: detected %'"PRDSNu" sector (", d->name, d->capacity);
+ if (d->capacity > 1024 / DISK_SECTOR_SIZE * 1024 * 1024)
+ printf ("%"PRDSNu" GB",
+ d->capacity / (1024 / DISK_SECTOR_SIZE * 1024 * 1024));
+ else if (d->capacity > 1024 / DISK_SECTOR_SIZE * 1024)
+ printf ("%"PRDSNu" MB", d->capacity / (1024 / DISK_SECTOR_SIZE * 1024));
+ else if (d->capacity > 1024 / DISK_SECTOR_SIZE)
+ printf ("%"PRDSNu" kB", d->capacity / (1024 / DISK_SECTOR_SIZE));
+ else
+ printf ("%"PRDSNu" byte", d->capacity * DISK_SECTOR_SIZE);
+ printf (") disk, model \"");
+ print_ata_string ((char *) &id[27], 40);
+ printf ("\", serial \"");
+ print_ata_string ((char *) &id[10], 20);
+ printf ("\"\n");
+}
+
+/* Prints STRING, which consists of SIZE bytes in a funky format:
+ each pair of bytes is in reverse order. Does not print
+ trailing whitespace and/or nulls. */
+static void
+print_ata_string (char *string, size_t size)
+{
+ size_t i;
+
+ /* Find the last non-white, non-null character. */
+ for (; size > 0; size--)
+ {
+ int c = string[(size - 1) ^ 1];
+ if (c != '\0' && !isspace (c))
+ break;
+ }
+
+ /* Print. */
+ for (i = 0; i < size; i++)
+ printf ("%c", string[i ^ 1]);
+}
+\f
+/* Selects device D, waiting for it to become ready, and then
+ writes SEC_NO to the disk's sector selection registers. (We
+ use LBA mode.) */