/*
 * 86Box    A hypervisor and IBM PC system emulator that specializes in
 *          running old operating systems and software designed for IBM
 *          PC systems and compatibles from 1981 through fairly recent
 *          system designs based on the PCI bus.
 *
 *          This file is part of the 86Box distribution.
 *
 *          Common code to handle all sorts of hard disk images.
 *
 * Authors: Miran Grca, <mgrca8@gmail.com>
 *          Fred N. van Kempen, <decwiz@yahoo.com>
 *
 *          Copyright 2016-2019 Miran Grca.
 *          Copyright 2017-2019 Fred N. van Kempen.
 */
#include <stdio.h>
#include <inttypes.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
#include <math.h>
#include <wchar.h>
#include <86box/86box.h>
#include <86box/plat.h>
#include <86box/ui.h>
#include <86box/hdd.h>
#include <86box/cdrom.h>
#include <86box/video.h>
#include <86box/hdd_audio.h>
#include "cpu.h"

#define HDD_OVERHEAD_TIME 50.0

hard_disk_t hdd[HDD_NUM];

int
hdd_init(void)
{
    /* Clear all global data. */
    memset(hdd, 0x00, sizeof(hdd));
    return 0;
}

int
hdd_string_to_bus(char *str, int cdrom)
{
    if (!strcmp(str, "none"))
        return HDD_BUS_DISABLED;

    if (!strcmp(str, "mfm")) {
        if (cdrom) {
no_cdrom:
            ui_msgbox_header(MBX_ERROR, plat_get_string(STRING_INVALID_CONFIG), plat_get_string(STRING_NO_ST506_ESDI_CDROM));
            return 0;
        }

        return HDD_BUS_MFM;
    }

    if (!strcmp(str, "esdi")) {
        if (cdrom)
            goto no_cdrom;

        return HDD_BUS_ESDI;
    }

    if (!strcmp(str, "ide"))
        return HDD_BUS_IDE;

    if (!strcmp(str, "atapi"))
        return HDD_BUS_ATAPI;

    if (!strcmp(str, "xta"))
        return HDD_BUS_XTA;

    if (!strcmp(str, "scsi"))
        return HDD_BUS_SCSI;
    
    if (!strcmp(str, "mitsumi"))
        return CDROM_BUS_MITSUMI;

    if (!strcmp(str, "mke"))
        return CDROM_BUS_MKE;

    return 0;
}

char *
hdd_bus_to_string(int bus, UNUSED(int cdrom))
{
    char *s = "none";

    switch (bus) {
        default:
        if (cdrom) {
            switch (bus) {
                case CDROM_BUS_MITSUMI:
                    s = "mitsumi";
                    break;
                case CDROM_BUS_MKE:
                    s = "mke";
                    break;
            }
            break;
        }
        case HDD_BUS_DISABLED:
            break;

        case HDD_BUS_MFM:
            s = "mfm";
            break;

        case HDD_BUS_XTA:
            s = "xta";
            break;

        case HDD_BUS_ESDI:
            s = "esdi";
            break;

        case HDD_BUS_IDE:
            s = "ide";
            break;

        case HDD_BUS_ATAPI:
            s = "atapi";
            break;

        case HDD_BUS_SCSI:
            s = "scsi";
            break;
    }

    return s;
}

int
hdd_is_valid(int c)
{
    if (hdd[c].bus_type == HDD_BUS_DISABLED)
        return 0;

    if (strlen(hdd[c].fn) == 0)
        return 0;

    if ((hdd[c].tracks == 0) || (hdd[c].hpc == 0) || (hdd[c].spt == 0))
        return 0;

    return 1;
}

double
hdd_seek_get_time(hard_disk_t *hdd, uint32_t dst_addr, uint8_t operation, uint8_t continuous, double max_seek_time)
{
    if (!hdd->speed_preset)
        return HDD_OVERHEAD_TIME;

    const hdd_zone_t *zone = NULL;
    if (hdd->num_zones <= 0) {
#ifdef DO_FATAL
        fatal("hdd_seek_get_time(): hdd->num_zones < 0)\n");
        return 0.0;
#else
        return 1000.0;
#endif
    }
    for (uint32_t i = 0; i < hdd->num_zones; i++) {
        zone = &hdd->zones[i];
        if (zone->end_sector >= dst_addr)
            break;
    }

    double continuous_times[2][2] = {
        {hdd->head_switch_usec,   hdd->cyl_switch_usec  },
        { zone->sector_time_usec, zone->sector_time_usec}
    };
    double times[2] = { HDD_OVERHEAD_TIME, hdd->avg_rotation_lat_usec };

    uint32_t new_track     = zone->start_track + ((dst_addr - zone->start_sector) / zone->sectors_per_track);
    uint32_t new_cylinder  = new_track / hdd->phy_heads;
    uint32_t cylinder_diff = abs((int) hdd->cur_cylinder - (int) new_cylinder);

    bool sequential = dst_addr == hdd->cur_addr + 1;
    continuous      = continuous && sequential;

    double seek_time = 0.0;
    if (continuous)
        seek_time = continuous_times[new_track == hdd->cur_track][!!cylinder_diff];
    else {
        if (!cylinder_diff)
            seek_time = times[operation != HDD_OP_SEEK];
        else {
            seek_time = hdd->cyl_switch_usec + (hdd->full_stroke_usec * (double) cylinder_diff / (double) hdd->phy_cyl) + ((operation != HDD_OP_SEEK) * hdd->avg_rotation_lat_usec);
        }
    }

    if (!max_seek_time || seek_time <= max_seek_time) {
        if (new_cylinder != hdd->cur_cylinder)
            hdd_audio_seek(hdd, new_cylinder);

        hdd->cur_addr     = dst_addr;
        hdd->cur_track    = new_track;
        hdd->cur_cylinder = new_cylinder;
    }

    return seek_time;
}

static void
hdd_readahead_update(hard_disk_t *hdd)
{
    uint64_t elapsed_cycles;
    double   elapsed_us;
    double   seek_time;
    int32_t  max_read_ahead;
    uint32_t space_needed;

    hdd_cache_t *cache = &hdd->cache;
    if (cache->ra_ongoing) {
        hdd_cache_seg_t *segment = &cache->segments[cache->ra_segment];

        elapsed_cycles = tsc - cache->ra_start_time;
        elapsed_us     = (double) elapsed_cycles / cpuclock * 1000000.0;
        /* Do not overwrite data not yet read by host */
        max_read_ahead = (segment->host_addr + cache->segment_size) - segment->ra_addr;

        seek_time = 0.0;

        for (int32_t i = 0; i < max_read_ahead; i++) {
            seek_time += hdd_seek_get_time(hdd, segment->ra_addr, HDD_OP_READ, 1, elapsed_us - seek_time);
            if (seek_time > elapsed_us)
                break;

            segment->ra_addr++;
        }

        if (segment->ra_addr > segment->lba_addr + cache->segment_size) {
            space_needed = segment->ra_addr - (segment->lba_addr + cache->segment_size);
            segment->lba_addr += space_needed;
        }
    }
}

static double
hdd_writecache_flush(hard_disk_t *hdd)
{
    double seek_time = 0.0;

    while (hdd->cache.write_pending) {
        seek_time += hdd_seek_get_time(hdd, hdd->cache.write_addr, HDD_OP_WRITE, 1, 0);
        hdd->cache.write_addr++;
        hdd->cache.write_pending--;
    }

    return seek_time;
}

static void
hdd_writecache_update(hard_disk_t *hdd)
{
    uint64_t elapsed_cycles;
    double   elapsed_us;
    double   seek_time;

    if (hdd->cache.write_pending) {
        elapsed_cycles = tsc - hdd->cache.write_start_time;
        elapsed_us     = (double) elapsed_cycles / cpuclock * 1000000.0;
        seek_time      = 0.0;

        while (hdd->cache.write_pending) {
            seek_time += hdd_seek_get_time(hdd, hdd->cache.write_addr, HDD_OP_WRITE, 1, elapsed_us - seek_time);
            if (seek_time > elapsed_us)
                break;

            hdd->cache.write_addr++;
            hdd->cache.write_pending--;
        }
    }
}

double
hdd_timing_write(hard_disk_t *hdd, uint32_t addr, uint32_t len)
{
    double   seek_time = 0.0;
    uint32_t flush_needed;

    if (!hdd->speed_preset)
        return HDD_OVERHEAD_TIME;

    hdd_readahead_update(hdd);
    hdd_writecache_update(hdd);

    hdd->cache.ra_ongoing = 0;

    if (hdd->cache.write_pending && (addr != (hdd->cache.write_addr + hdd->cache.write_pending))) {
        /* New request is not sequential to existing cache, need to flush it */
        seek_time += hdd_writecache_flush(hdd);
    }

    if (!hdd->cache.write_pending) {
        /* Cache is empty */
        hdd->cache.write_addr = addr;
    }

    hdd->cache.write_pending += len;
    if (hdd->cache.write_pending > hdd->cache.write_size) {
        /* If request is bigger than free cache, flush some data first */
        flush_needed = hdd->cache.write_pending - hdd->cache.write_size;
        for (uint32_t i = 0; i < flush_needed; i++) {
            seek_time += hdd_seek_get_time(hdd, hdd->cache.write_addr, HDD_OP_WRITE, 1, 0);
            hdd->cache.write_addr++;
        }
    }

    hdd->cache.write_start_time = tsc + (uint64_t) (seek_time * cpuclock / 1000000.0);

    return seek_time;
}

double
hdd_timing_read(hard_disk_t *hdd, uint32_t addr, uint32_t len)
{
    double seek_time = 0.0;

    if (!hdd->speed_preset)
        return HDD_OVERHEAD_TIME;

    hdd_readahead_update(hdd);
    hdd_writecache_update(hdd);

    seek_time += hdd_writecache_flush(hdd);

    hdd_cache_t     *cache      = &hdd->cache;
    hdd_cache_seg_t *active_seg = &cache->segments[0];

    for (uint32_t i = 0; i < cache->num_segments; i++) {
        hdd_cache_seg_t *segment = &cache->segments[i];
        if (!segment->valid) {
            active_seg = segment;
            continue;
        }

        if (segment->lba_addr <= addr && (segment->lba_addr + cache->segment_size) >= addr) {
            /* Cache HIT */
            segment->host_addr = addr;
            active_seg         = segment;
            if (addr + len > segment->ra_addr) {
                uint32_t need_read = (addr + len) - segment->ra_addr;
                for (uint32_t j = 0; j < need_read; j++) {
                    seek_time += hdd_seek_get_time(hdd, segment->ra_addr, HDD_OP_READ, 1, 0.0);
                    segment->ra_addr++;
                }
            }
            if (addr + len > segment->lba_addr + cache->segment_size) {
                /* Need to erase some previously cached data */
                uint32_t space_needed = (addr + len) - (segment->lba_addr + cache->segment_size);
                segment->lba_addr += space_needed;
            }
            goto update_lru;
        } else {
            if (segment->lru > active_seg->lru)
                active_seg = segment;
        }
    }

    /* Cache MISS */
    active_seg->lba_addr  = addr;
    active_seg->valid     = 1;
    active_seg->host_addr = addr;
    active_seg->ra_addr   = addr;

    for (uint32_t i = 0; i < len; i++) {
        seek_time += hdd_seek_get_time(hdd, active_seg->ra_addr, HDD_OP_READ, i != 0, 0.0);
        active_seg->ra_addr++;
    }

update_lru:
    for (uint32_t i = 0; i < cache->num_segments; i++)
        cache->segments[i].lru++;

    active_seg->lru = 0;

    cache->ra_ongoing    = 1;
    cache->ra_segment    = active_seg->id;
    cache->ra_start_time = tsc + (uint64_t) (seek_time * cpuclock / 1000000.0);

    return seek_time;
}

static void
hdd_cache_init(hard_disk_t *hdd)
{
    hdd_cache_t *cache = &hdd->cache;

    cache->ra_segment    = 0;
    cache->ra_ongoing    = 0;
    cache->ra_start_time = 0;

    for (uint32_t i = 0; i < cache->num_segments; i++) {
        cache->segments[i].valid     = 0;
        cache->segments[i].lru       = 0;
        cache->segments[i].id        = i;
        cache->segments[i].ra_addr   = 0;
        cache->segments[i].host_addr = 0;
    }
}

static void
hdd_zones_init(hard_disk_t *hdd)
{
    uint32_t    lba = 0;
    uint32_t    track = 0;
    uint32_t    tracks;
    double      revolution_usec = 60.0 / (double) hdd->rpm * 1000000.0;
    hdd_zone_t *zone;

    for (uint32_t i = 0; i < hdd->num_zones; i++) {
        zone                   = &hdd->zones[i];
        zone->start_sector     = lba;
        zone->start_track      = track;
        zone->sector_time_usec = revolution_usec / (double) zone->sectors_per_track;
        tracks                 = zone->cylinders * hdd->phy_heads;
        lba += tracks * zone->sectors_per_track;
        zone->end_sector = lba - 1;
        track += tracks - 1;
    }
}

static hdd_preset_t hdd_speed_presets[] = {
  // clang-format off
    { .name = "[Generic] RAM Disk (max. speed)",                  .internal_name = "ramdisk",                                                                                                                                                                                 .rcache_num_seg = 16, .rcache_seg_size =  128, .max_multiple = 32 },
    { .name = "[Generic] 1983 MFM (3600 RPM)",                    .internal_name = "mfm_3600rpm",                                                                          .zones =  1, .avg_spt =  17, .heads =  4, .rpm = 3600, .full_stroke_ms = 80, .track_seek_ms = 10,  .rcache_num_seg =  1, .rcache_seg_size =   17, .max_multiple =  1 },
    { .name = "[Generic] 1983 RLL (3600 RPM)",                    .internal_name = "rll_3600rpm",                                                                          .zones =  1, .avg_spt =  26, .heads =  4, .rpm = 3600, .full_stroke_ms = 80, .track_seek_ms = 10,  .rcache_num_seg =  1, .rcache_seg_size =   26, .max_multiple =  1 },
    { .name = "[Generic] 1989 (3500 RPM)",                        .internal_name = "1989_3500rpm",                                                                         .zones =  1, .avg_spt =  35, .heads =  2, .rpm = 3500, .full_stroke_ms = 40, .track_seek_ms = 8,   .rcache_num_seg =  1, .rcache_seg_size =   16, .max_multiple =  8 },
    { .name = "[Generic] 1992 (3600 RPM)",                        .internal_name = "1992_3600rpm",                                                                         .zones =  1, .avg_spt =  45, .heads =  2, .rpm = 3600, .full_stroke_ms = 30, .track_seek_ms = 6,   .rcache_num_seg =  4, .rcache_seg_size =   16, .max_multiple =  8 },
    { .name = "[Generic] 1994 (4500 RPM)",                        .internal_name = "1994_4500rpm",                                                                         .zones =  8, .avg_spt =  80, .heads =  4, .rpm = 4500, .full_stroke_ms = 26, .track_seek_ms = 5,   .rcache_num_seg =  4, .rcache_seg_size =   32, .max_multiple = 16 },
    { .name = "[Generic] 1996 (5400 RPM)",                        .internal_name = "1996_5400rpm",                                                                         .zones = 16, .avg_spt = 135, .heads =  4, .rpm = 5400, .full_stroke_ms = 24, .track_seek_ms = 3,   .rcache_num_seg =  4, .rcache_seg_size =   64, .max_multiple = 16 },
    { .name = "[Generic] 1997 (5400 RPM)",                        .internal_name = "1997_5400rpm",                                                                         .zones = 16, .avg_spt = 185, .heads =  6, .rpm = 5400, .full_stroke_ms = 20, .track_seek_ms = 2.5, .rcache_num_seg =  8, .rcache_seg_size =   64, .max_multiple = 32 },
    { .name = "[Generic] 1998 (5400 RPM)",                        .internal_name = "1998_5400rpm",                                                                         .zones = 16, .avg_spt = 300, .heads =  8, .rpm = 5400, .full_stroke_ms = 20, .track_seek_ms = 2,   .rcache_num_seg =  8, .rcache_seg_size =  128, .max_multiple = 32 },
    { .name = "[Generic] 2000 (7200 RPM)",                        .internal_name = "2000_7200rpm",                                                                         .zones = 16, .avg_spt = 350, .heads =  6, .rpm = 7200, .full_stroke_ms = 15, .track_seek_ms = 2,   .rcache_num_seg = 16, .rcache_seg_size =  128, .max_multiple = 32 },
    /* MFM/RLL drives: single zone, 17 spt (MFM) or 26 spt (RLL), 3600 RPM typical */
    { .name = "[MFM] Seagate ST-225 (20 MB)",                     .internal_name = "ST225",        .model = "ST-225",                                                      .zones =  1, .avg_spt =  17, .heads =  4, .rpm = 3600, .full_stroke_ms = 65, .track_seek_ms = 8,   .rcache_num_seg =  1, .rcache_seg_size =   17, .max_multiple =  1 },
    { .name = "[MFM] Seagate ST-251 (40 MB)",                     .internal_name = "ST251",        .model = "ST-251",                                                      .zones =  1, .avg_spt =  17, .heads =  6, .rpm = 3600, .full_stroke_ms = 65, .track_seek_ms = 8,   .rcache_num_seg =  1, .rcache_seg_size =   17, .max_multiple =  1 },
    { .name = "[MFM] Seagate ST-4038 (32 MB)",                    .internal_name = "ST4038",       .model = "ST-4038",                                                     .zones =  1, .avg_spt =  17, .heads =  5, .rpm = 3600, .full_stroke_ms = 80, .track_seek_ms = 10,  .rcache_num_seg =  1, .rcache_seg_size =   17, .max_multiple =  1 },
    { .name = "[MFM] Seagate ST-4051 (43 MB)",                    .internal_name = "ST4051",       .model = "ST-4051",                                                     .zones =  1, .avg_spt =  17, .heads =  5, .rpm = 3600, .full_stroke_ms = 80, .track_seek_ms = 10,  .rcache_num_seg =  1, .rcache_seg_size =   17, .max_multiple =  1 },
    { .name = "[MFM] Miniscribe 8425 (21 MB)",                    .internal_name = "MNS8425",      .model = "Miniscribe 8425",                                             .zones =  1, .avg_spt =  17, .heads =  4, .rpm = 3600, .full_stroke_ms = 65, .track_seek_ms = 8,   .rcache_num_seg =  1, .rcache_seg_size =   17, .max_multiple =  1 },
    { .name = "[MFM] Miniscribe 8438 (32 MB)",                    .internal_name = "MNS8438",      .model = "Miniscribe 8438",                                             .zones =  1, .avg_spt =  17, .heads =  5, .rpm = 3600, .full_stroke_ms = 65, .track_seek_ms = 8,   .rcache_num_seg =  1, .rcache_seg_size =   17, .max_multiple =  1 },
    { .name = "[MFM] Western Digital WD93028-X (20 MB)",          .internal_name = "WD93028X",     .model = "WD93028-X",                                                   .zones =  1, .avg_spt =  17, .heads =  4, .rpm = 3600, .full_stroke_ms = 75, .track_seek_ms = 10,  .rcache_num_seg =  1, .rcache_seg_size =   17, .max_multiple =  1 },
    { .name = "[RLL] Seagate ST-238R (32 MB)",                    .internal_name = "ST238R",       .model = "ST-238R",                                                     .zones =  1, .avg_spt =  26, .heads =  4, .rpm = 3600, .full_stroke_ms = 65, .track_seek_ms = 8,   .rcache_num_seg =  1, .rcache_seg_size =   26, .max_multiple =  1 },
    { .name = "[RLL] Seagate ST-253R (63 MB)",                    .internal_name = "ST253R",       .model = "ST-253R",                                                     .zones =  1, .avg_spt =  26, .heads =  6, .rpm = 3600, .full_stroke_ms = 65, .track_seek_ms = 8,   .rcache_num_seg =  1, .rcache_seg_size =   26, .max_multiple =  1 },
    { .name = "[PIO IDE] IBM WDA-L42",                            .internal_name = "WDAL42",       .model = "WDA-L42",                                                     .zones =  1, .avg_spt =  85, .heads =  2, .rpm = 3600, .full_stroke_ms = 33, .track_seek_ms = 2.5, .rcache_num_seg =  1, .rcache_seg_size =   32, .max_multiple =  1 },
    { .name = "[PIO-2] IBM DSAA-3270",                            .internal_name = "DSAA3270",     .model = "DSAA-3270",                            .version = "25505120", .zones =  8, .avg_spt = 268, .heads =  2, .rpm = 4500, .full_stroke_ms = 25, .track_seek_ms = 2.1, .rcache_num_seg =  3, .rcache_seg_size =   32, .max_multiple =  8 },
    { .name = "[ATA-1] Conner CP3024",                            .internal_name = "CP3024",       .model = "Conner Peripherals 20MB - CP3024",                            .zones =  1, .avg_spt =  33, .heads =  2, .rpm = 3500, .full_stroke_ms = 50, .track_seek_ms = 8,   .rcache_num_seg =  1, .rcache_seg_size =    8, .max_multiple =  8 }, // Needed for GRiDcase 1520 to work
    { .name = "[ATA-1] Conner CP3044",                            .internal_name = "CP3044",       .model = "Conner Peripherals 40MB - CP3044",                            .zones =  1, .avg_spt =  40, .heads =  2, .rpm = 3500, .full_stroke_ms = 50, .track_seek_ms = 8,   .rcache_num_seg =  1, .rcache_seg_size =    8, .max_multiple =  8 }, // Needed for GRiDcase 1520 to work
    { .name = "[ATA-1] Conner CP3104",                            .internal_name = "CP3104",       .model = "Conner Peripherals 104MB - CP3104",                           .zones =  1, .avg_spt =  33, .heads =  8, .rpm = 3500, .full_stroke_ms = 45, .track_seek_ms = 8,   .rcache_num_seg =  4, .rcache_seg_size =    8, .max_multiple =  8 }, // Needed for GRiDcase 1520 to work
    { .name = "[ATA-1] Conner CFS420A",                           .internal_name = "CFS420A",      .model = "Conner Peripherals 420MB - CFS420A",                          .zones =  1, .avg_spt =  40, .heads =  2, .rpm = 3600, .full_stroke_ms = 33, .track_seek_ms = 3,   .rcache_num_seg =  4, .rcache_seg_size =   32, .max_multiple =  8 },
    { .name = "[ATA-1] HP Kittyhawk",                             .internal_name = "C3014A",       .model = "HP C3014A",                                                   .zones =  6, .avg_spt =  80, .heads =  3, .rpm = 5400, .full_stroke_ms = 18, .track_seek_ms = 3,   .rcache_num_seg =  4, .rcache_seg_size =   16, .max_multiple =  8 },
    { .name = "[ATA-1] IBM H3256-A3",                             .internal_name = "H3256A3",      .model = "H3256-A3",                                                    .zones =  1, .avg_spt =  40, .heads =  2, .rpm = 3600, .full_stroke_ms = 32, .track_seek_ms = 4,   .rcache_num_seg =  4, .rcache_seg_size =   96, .max_multiple =  8 },
    { .name = "[ATA-1] IBM H3342-A4",                             .internal_name = "H3342A4",      .model = "H3342-A4",                                                    .zones =  1, .avg_spt =  40, .heads =  2, .rpm = 3600, .full_stroke_ms = 30, .track_seek_ms = 4,   .rcache_num_seg =  4, .rcache_seg_size =   96, .max_multiple =  8 },
    { .name = "[ATA-1] Kalok KL343",                              .internal_name = "KL343",        .model = "KALOK KL-343",                                                .zones =  1, .avg_spt =  80, .heads =  6, .rpm = 3600, .full_stroke_ms = 50, .track_seek_ms = 2,   .rcache_num_seg =  1, .rcache_seg_size =    8, .max_multiple =  8 },
    { .name = "[ATA-1] Kalok KL3100",                             .internal_name = "KL3100",       .model = "KALOK KL-3100",                                               .zones =  1, .avg_spt = 100, .heads =  6, .rpm = 3662, .full_stroke_ms = 50, .track_seek_ms = 2,   .rcache_num_seg =  1, .rcache_seg_size =   32, .max_multiple =  8 },
    { .name = "[ATA-1] Maxtor 7060AT",                            .internal_name = "7060AT",       .model = "Maxtor 7060 AT",                                              .zones =  1, .avg_spt =  62, .heads =  2, .rpm = 3524, .full_stroke_ms = 30, .track_seek_ms = 3.6, .rcache_num_seg =  1, .rcache_seg_size =   64, .max_multiple =  8 },
    { .name = "[ATA-1] Maxtor 7131AT",                            .internal_name = "7131AT",       .model = "Maxtor 7131 AT",                                              .zones =  2, .avg_spt =  54, .heads =  2, .rpm = 3551, .full_stroke_ms = 27, .track_seek_ms = 4.5, .rcache_num_seg =  1, .rcache_seg_size =   64, .max_multiple =  8 },
    { .name = "[ATA-1] Maxtor 7213AT",                            .internal_name = "7213AT",       .model = "Maxtor 7213 AT",                                              .zones =  4, .avg_spt =  55, .heads =  4, .rpm = 3551, .full_stroke_ms = 28, .track_seek_ms = 6.5, .rcache_num_seg =  1, .rcache_seg_size =   64, .max_multiple =  8 },
    { .name = "[ATA-1] Maxtor 7245AT",                            .internal_name = "7245AT",       .model = "Maxtor 7245 AT",                                              .zones =  4, .avg_spt =  49, .heads =  4, .rpm = 3551, .full_stroke_ms = 27, .track_seek_ms = 4.4, .rcache_num_seg =  8, .rcache_seg_size =   64, .max_multiple =  8 },
    { .name = "[ATA-1] Quantum ProDrive LPS 105",                 .internal_name = "LPS105AT",     .model = "QUANTUM PRODRIVE 105",                                        .zones =  1, .avg_spt =  70, .heads =  2, .rpm = 3662, .full_stroke_ms = 45, .track_seek_ms = 5,   .rcache_num_seg =  1, .rcache_seg_size =   64, .max_multiple =  8 },
    { .name = "[ATA-1] Quantum ProDrive LPS 120AT",               .internal_name = "GM12A012",     .model = "QUANTUM PRODRIVE 120AT",                                      .zones =  1, .avg_spt =  50, .heads =  2, .rpm = 3605, .full_stroke_ms = 45, .track_seek_ms = 4,   .rcache_num_seg =  1, .rcache_seg_size =   64, .max_multiple =  8 },
    { .name = "[ATA-1] Seagate ST3243A",                          .internal_name = "ST3243A",      .model = "ST3243A",                                                     .zones =  1, .avg_spt =  40, .heads =  4, .rpm = 3811, .full_stroke_ms = 32, .track_seek_ms = 4,   .rcache_num_seg =  4, .rcache_seg_size =   32, .max_multiple =  8 },
    { .name = "[ATA-1] Western Digital Caviar 140",               .internal_name = "AC140",        .model = "WDC AC140",                                                   .zones =  4, .avg_spt =  70, .heads =  2, .rpm = 3551, .full_stroke_ms = 28, .track_seek_ms = 6,   .rcache_num_seg =  8, .rcache_seg_size =    8, .max_multiple =  8 },
    { .name = "[ATA-1] Western Digital Caviar 280",               .internal_name = "AC280",        .model = "WDC AC280",                                                   .zones =  4, .avg_spt =  70, .heads =  4, .rpm = 3595, .full_stroke_ms = 28, .track_seek_ms = 6,   .rcache_num_seg =  8, .rcache_seg_size =   32, .max_multiple =  8 },
    { .name = "[ATA-1] Western Digital Caviar 1210",              .internal_name = "AC1210",       .model = "WDC AC1210F",                                                 .zones =  4, .avg_spt =  30, .heads =  2, .rpm = 3314, .full_stroke_ms = 33, .track_seek_ms = 4,   .rcache_num_seg =  4, .rcache_seg_size =  128, .max_multiple =  8 },
    { .name = "[ATA-1] Western Digital Caviar 2120",              .internal_name = "AC2120",       .model = "WDC AC2120M",                                                 .zones =  4, .avg_spt =  40, .heads =  2, .rpm = 3605, .full_stroke_ms = 28, .track_seek_ms = 2.8, .rcache_num_seg =  8, .rcache_seg_size =   32, .max_multiple =  8 },
    { .name = "[ATA-2] IBM DBOA-2720",                            .internal_name = "DBOA2720",     .model = "DBOA-2720",                                                   .zones =  2, .avg_spt = 135, .heads =  2, .rpm = 4000, .full_stroke_ms = 30, .track_seek_ms = 5,   .rcache_num_seg =  4, .rcache_seg_size =   64, .max_multiple =  8 },
    { .name = "[ATA-2] IBM DeskStar 4 (DCAA-32880)",              .internal_name = "DCAA32880",    .model = "IBM-DCAA-32880",                                              .zones =  8, .avg_spt = 185, .heads =  2, .rpm = 5400, .full_stroke_ms = 19, .track_seek_ms = 1.7, .rcache_num_seg =  4, .rcache_seg_size =   96, .max_multiple = 16 },
    { .name = "[ATA-2] IBM DeskStar 4 (DCAA-33610)",              .internal_name = "DCAA33610",    .model = "IBM-DCAA-33610",                                              .zones =  8, .avg_spt = 185, .heads =  3, .rpm = 5400, .full_stroke_ms = 19, .track_seek_ms = 1.7, .rcache_num_seg =  4, .rcache_seg_size =   96, .max_multiple = 16 },
    { .name = "[ATA-2] IBM DeskStar 4 (DCAA-34330)",              .internal_name = "DCAA34330",    .model = "IBM-DCAA-34330",                                              .zones =  8, .avg_spt = 185, .heads =  3, .rpm = 5400, .full_stroke_ms = 19, .track_seek_ms = 1.7, .rcache_num_seg =  4, .rcache_seg_size =   96, .max_multiple = 16 },
    { .name = "[ATA-2] Maxtor 7540AV",                            .internal_name = "7540AV",       .model = "Maxtor 7540 AV",                                              .zones =  2, .avg_spt = 120, .heads =  4, .rpm = 3551, .full_stroke_ms = 31, .track_seek_ms = 4.3, .rcache_num_seg =  4, .rcache_seg_size =   32, .max_multiple =  8 },
    { .name = "[ATA-2] Maxtor 7546AT",                            .internal_name = "7546AT",       .model = "Maxtor 7546 AT",                                              .zones =  2, .avg_spt = 100, .heads =  4, .rpm = 4500, .full_stroke_ms = 28, .track_seek_ms = 2.3, .rcache_num_seg =  4, .rcache_seg_size =  256, .max_multiple =  8 },
    { .name = "[ATA-2] Maxtor 7850AV",                            .internal_name = "7850AV",       .model = "Maxtor 7850 AV",                                              .zones =  4, .avg_spt = 120, .heads =  4, .rpm = 3551, .full_stroke_ms = 31, .track_seek_ms = 3.7, .rcache_num_seg =  4, .rcache_seg_size =   64, .max_multiple =  8 },
    { .name = "[ATA-2] Maxtor 71336AP",                           .internal_name = "71336AP",      .model = "Maxtor 71336 AP",                                             .zones =  4, .avg_spt = 105, .heads =  4, .rpm = 4480, .full_stroke_ms = 12, .track_seek_ms = 3.4, .rcache_num_seg =  8, .rcache_seg_size =  128, .max_multiple = 16 },
    { .name = "[ATA-2] Quantum Bigfoot 1.2AT",                    .internal_name = "BF12A011",     .model = "QUANTUM BIGFOOT BF1.2A",                                      .zones =  2, .avg_spt = 155, .heads =  2, .rpm = 3600, .full_stroke_ms = 30, .track_seek_ms = 3.5, .rcache_num_seg =  4, .rcache_seg_size =  128, .max_multiple = 16 },
    { .name = "[ATA-2] Quantum Bigfoot (CY4320A)",                .internal_name = "CY4320A",      .model = "QUANTUM BIGFOOT_CY4320A",                                     .zones =  2, .avg_spt = 130, .heads =  2, .rpm = 4000, .full_stroke_ms = 29, .track_seek_ms = 2,   .rcache_num_seg =  8, .rcache_seg_size =  128, .max_multiple = 16 }, // from Hardcore Windows NT Final Segment by Kugee
    { .name = "[ATA-2] Quantum Fireball 640AT",                   .internal_name = "FB64A341",     .model = "QUANTUM FIREBALL 640AT",                                      .zones =  2, .avg_spt = 120, .heads =  2, .rpm = 5400, .full_stroke_ms = 24, .track_seek_ms = 3.1, .rcache_num_seg =  4, .rcache_seg_size =  128, .max_multiple =  8 },
    { .name = "[ATA-2] Quantum Fireball TM1080AT",                .internal_name = "TM10A462",     .model = "QUANTUM FIREBALL TM1.0A",                                     .zones =  2, .avg_spt = 120, .heads =  2, .rpm = 4500, .full_stroke_ms = 21, .track_seek_ms = 3,   .rcache_num_seg =  8, .rcache_seg_size =  128, .max_multiple =  8 },
    { .name = "[ATA-2] Quantum Fireball TM1.2AT",                 .internal_name = "TM12A012",     .model = "QUANTUM FIREBALL TM1.2A",                                     .zones =  4, .avg_spt = 120, .heads =  2, .rpm = 4500, .full_stroke_ms = 21, .track_seek_ms = 3,   .rcache_num_seg =  8, .rcache_seg_size =  128, .max_multiple =  8 },
    { .name = "[ATA-2] Samsung PLS-31274A",                       .internal_name = "PLS31274A",    .model = "SAMSUNG PLS-31274A",                                          .zones =  4, .avg_spt = 110, .heads =  4, .rpm = 4500, .full_stroke_ms = 45, .track_seek_ms = 4.5, .rcache_num_seg =  4, .rcache_seg_size =  256, .max_multiple =  8 },
    { .name = "[ATA-2] Samsung Winner-1",                         .internal_name = "WNR31601A",    .model = "SAMSUNG WNR-31601A",                                          .zones =  8, .avg_spt = 110, .heads =  4, .rpm = 5400, .full_stroke_ms = 22, .track_seek_ms = 3,   .rcache_num_seg =  8, .rcache_seg_size =  128, .max_multiple = 16 },
    { .name = "[ATA-2] Seagate Medalist (ST3780A)",               .internal_name = "ST3780A",      .model = "ST3780A",                                                     .zones =  8, .avg_spt = 120, .heads =  4, .rpm = 4500, .full_stroke_ms = 25, .track_seek_ms = 3.5, .rcache_num_seg =  4, .rcache_seg_size =  256, .max_multiple = 16 },
    { .name = "[ATA-2] Seagate Medalist (ST31220A)",              .internal_name = "ST31220A",     .model = "ST31220A",                                                    .zones =  8, .avg_spt = 140, .heads =  6, .rpm = 4500, .full_stroke_ms = 27, .track_seek_ms = 3.5, .rcache_num_seg =  4, .rcache_seg_size =  256, .max_multiple = 16 },
    { .name = "[ATA-2] Seagate Medalist 210xe",                   .internal_name = "ST3250A",      .model = "ST3250A",                                                     .zones =  4, .avg_spt = 148, .heads =  2, .rpm = 3811, .full_stroke_ms = 30, .track_seek_ms = 4.1, .rcache_num_seg =  8, .rcache_seg_size =  120, .max_multiple =  8 },
    { .name = "[ATA-2] Seagate Medalist 275xe",                   .internal_name = "ST3295A",      .model = "ST3295A",                                                     .zones =  4, .avg_spt = 130, .heads =  2, .rpm = 3811, .full_stroke_ms = 30, .track_seek_ms = 3.4, .rcache_num_seg =  3, .rcache_seg_size =  120, .max_multiple =  8 },
    { .name = "[ATA-2] Seagate Medalist 545xe",                   .internal_name = "ST3660A",      .model = "ST3660A",                                                     .zones =  4, .avg_spt = 130, .heads =  4, .rpm = 3811, .full_stroke_ms = 34, .track_seek_ms = 3.4, .rcache_num_seg =  8, .rcache_seg_size =  120, .max_multiple =  8 },
    { .name = "[ATA-2] Seagate Medalist 640xe",                   .internal_name = "ST3630A",      .model = "ST3630A",                                                     .zones =  4, .avg_spt = 130, .heads =  4, .rpm = 3811, .full_stroke_ms = 34, .track_seek_ms = 3.5, .rcache_num_seg =  8, .rcache_seg_size =  120, .max_multiple =  8 },
    { .name = "[ATA-2] Seagate Medalist 850xe",                   .internal_name = "ST3850A",      .model = "ST3850A",                                                     .zones =  8, .avg_spt = 150, .heads =  4, .rpm = 3811, .full_stroke_ms = 34, .track_seek_ms = 3.8, .rcache_num_seg =  8, .rcache_seg_size =  120, .max_multiple =  8 },
    { .name = "[ATA-2] Seagate Medalist 1270SL",                  .internal_name = "ST51270A",     .model = "ST51270A",                                                    .zones =  8, .avg_spt = 205, .heads =  3, .rpm = 5736, .full_stroke_ms = 25, .track_seek_ms = 2,   .rcache_num_seg =  8, .rcache_seg_size =  128, .max_multiple = 16 },
    { .name = "[ATA-2] Seagate Medalist 3240",                    .internal_name = "ST33240A",     .model = "ST33240A",                                                    .zones = 16, .avg_spt = 225, .heads =  8, .rpm = 4500, .full_stroke_ms = 25, .track_seek_ms = 2.5, .rcache_num_seg =  8, .rcache_seg_size =  128, .max_multiple = 16 },
    { .name = "[ATA-2] Toshiba MK2101MAN (HDD2616)",              .internal_name = "HDD2616",      .model = "TOSHIBA MK2101MAN",                                           .zones =  8, .avg_spt = 130, .heads = 10, .rpm = 4200, .full_stroke_ms = 36, .track_seek_ms = 3,   .rcache_num_seg =  4, .rcache_seg_size =  128, .max_multiple = 16 },
    { .name = "[ATA-2] Western Digital Caviar 2540",              .internal_name = "AC2540",       .model = "WDC AC2540H",                                                 .zones =  4, .avg_spt = 150, .heads =  2, .rpm = 4500, .full_stroke_ms = 12, .track_seek_ms = 4,   .rcache_num_seg =  4, .rcache_seg_size =  128, .max_multiple =  8 },
    { .name = "[ATA-2] Western Digital Caviar 2850",              .internal_name = "AC2850",       .model = "WDC AC2850F",                                                 .zones =  4, .avg_spt = 130, .heads =  2, .rpm = 5200, .full_stroke_ms = 12, .track_seek_ms = 4,   .rcache_num_seg =  8, .rcache_seg_size =  128, .max_multiple =  8 },
    { .name = "[ATA-2] Western Digital Caviar 11000",             .internal_name = "AC11000",      .model = "WDC AC11000H",                                                .zones =  4, .avg_spt = 120, .heads =  2, .rpm = 5200, .full_stroke_ms = 12, .track_seek_ms = 3,   .rcache_num_seg =  8, .rcache_seg_size =  128, .max_multiple =  8 },
    { .name = "[ATA-2] Western Digital Caviar 21200",             .internal_name = "AC21200",      .model = "WDC AC21200H",                                                .zones =  4, .avg_spt = 110, .heads =  4, .rpm = 5200, .full_stroke_ms = 39, .track_seek_ms = 3,   .rcache_num_seg =  4, .rcache_seg_size =  128, .max_multiple =  8 },
    { .name = "[ATA-2] Western Digital Caviar 21600",             .internal_name = "AC21600",      .model = "WDC AC21600H",                                                .zones =  8, .avg_spt = 140, .heads =  4, .rpm = 5200, .full_stroke_ms = 30, .track_seek_ms = 3,   .rcache_num_seg =  4, .rcache_seg_size =  128, .max_multiple =  8 },
    { .name = "[ATA-2] Western Digital Caviar 22000",             .internal_name = "AC22000",      .model = "WDC AC22000LA",                                               .zones =  8, .avg_spt = 130, .heads =  3, .rpm = 5200, .full_stroke_ms = 33, .track_seek_ms = 3.5, .rcache_num_seg =  4, .rcache_seg_size =  128, .max_multiple =  8 },
    { .name = "[ATA-2] Western Digital Caviar 22100",             .internal_name = "AC22100",      .model = "WDC AC22100H",                                                .zones =  8, .avg_spt = 140, .heads =  4, .rpm = 5200, .full_stroke_ms = 30, .track_seek_ms = 3,   .rcache_num_seg =  8, .rcache_seg_size =  128, .max_multiple = 16 },
    { .name = "[ATA-2] Western Digital Caviar 31200",             .internal_name = "AC31200",      .model = "WDC AC31200F",                                                .zones =  8, .avg_spt = 210, .heads =  4, .rpm = 4500, .full_stroke_ms = 12, .track_seek_ms = 4,   .rcache_num_seg =  8, .rcache_seg_size =   64, .max_multiple = 16 },
    { .name = "[ATA-3] Conner CFS1275A",                          .internal_name = "CFS1275A",     .model = "Conner Peripherals 1275MB - CFS1275A",                        .zones =  4, .avg_spt = 130, .heads =  2, .rpm = 4500, .full_stroke_ms = 25, .track_seek_ms = 3.8, .rcache_num_seg =  4, .rcache_seg_size =   64, .max_multiple = 16 }, // Either ATA-2 or ATA-3
    { .name = "[ATA-3] Fujitsu MPA3017AT",                        .internal_name = "MPA3017AT",    .model = "FUJITSU MPA3017AT",                                           .zones =  5, .avg_spt = 195, .heads =  2, .rpm = 5400, .full_stroke_ms = 20, .track_seek_ms = 3.2, .rcache_num_seg =  8, .rcache_seg_size =  128, .max_multiple = 16 },
    { .name = "[ATA-3] Fujitsu MPA3026AT",                        .internal_name = "MPA3026AT",    .model = "FUJITSU MPA3026AT",                                           .zones =  8, .avg_spt = 195, .heads =  3, .rpm = 5400, .full_stroke_ms = 20, .track_seek_ms = 3.2, .rcache_num_seg =  8, .rcache_seg_size =  128, .max_multiple = 16 },
    { .name = "[ATA-3] Fujitsu MPA3035AT",                        .internal_name = "MPA3035AT",    .model = "FUJITSU MPA3035AT",                                           .zones = 11, .avg_spt = 195, .heads =  4, .rpm = 5400, .full_stroke_ms = 20, .track_seek_ms = 3.2, .rcache_num_seg =  8, .rcache_seg_size =  128, .max_multiple = 16 },
    { .name = "[ATA-3] Fujitsu MPA3043AT",                        .internal_name = "MPA3043AT",    .model = "FUJITSU MPA3043AT",                                           .zones = 15, .avg_spt = 195, .heads =  5, .rpm = 5400, .full_stroke_ms = 20, .track_seek_ms = 3.2, .rcache_num_seg =  8, .rcache_seg_size =  128, .max_multiple = 16 },
    { .name = "[ATA-3] Fujitsu MPA3052AT",                        .internal_name = "MPA3052AT",    .model = "FUJITSU MPA3052AT",                                           .zones = 16, .avg_spt = 195, .heads =  6, .rpm = 5400, .full_stroke_ms = 20, .track_seek_ms = 3.2, .rcache_num_seg =  8, .rcache_seg_size =  128, .max_multiple = 16 },
    { .name = "[ATA-3] Samsung Voyager 6",                        .internal_name = "SV0844A",      .model = "SAMSUNG SV0844A",                                             .zones =  8, .avg_spt = 205, .heads =  4, .rpm = 5400, .full_stroke_ms = 22, .track_seek_ms = 2,   .rcache_num_seg = 16, .rcache_seg_size =  512, .max_multiple = 32 },
    { .name = "[ATA-3] Samsung Winner 5X",                        .internal_name = "WU33205A",     .model = "SAMSUNG WU33205A",                                            .zones =  6, .avg_spt = 200, .heads =  4, .rpm = 5400, .full_stroke_ms = 20, .track_seek_ms = 3,   .rcache_num_seg =  8, .rcache_seg_size =  128, .max_multiple = 16 },
    { .name = "[ATA-3] Seagate Medalist 636",                     .internal_name = "ST3636A",      .model = "Seagate Technology 636MB - ST3636A",                          .zones =  2, .avg_spt = 130, .heads =  2, .rpm = 4500, .full_stroke_ms = 25, .track_seek_ms = 3.8, .rcache_num_seg =  4, .rcache_seg_size =   64, .max_multiple =  8 },
    { .name = "[ATA-3] Seagate Medalist 1082",                    .internal_name = "ST31082A",     .model = "Seagate Technology 1082MB - ST31082A",                        .zones =  4, .avg_spt = 130, .heads =  3, .rpm = 4500, .full_stroke_ms = 25, .track_seek_ms = 3.8, .rcache_num_seg =  4, .rcache_seg_size =   64, .max_multiple =  8 },
    { .name = "[ATA-3] Seagate Medalist 1276",                    .internal_name = "ST31276A",     .model = "Seagate Technology 1275MB - ST31276A",                        .zones =  4, .avg_spt = 130, .heads =  3, .rpm = 4500, .full_stroke_ms = 25, .track_seek_ms = 3.8, .rcache_num_seg =  4, .rcache_seg_size =   64, .max_multiple = 16 },
    { .name = "[ATA-3] Seagate Medalist 1720",                    .internal_name = "ST31720A",     .model = "ST31720A",                                                    .zones =  4, .avg_spt = 120, .heads =  4, .rpm = 4500, .full_stroke_ms = 25, .track_seek_ms = 2,   .rcache_num_seg =  4, .rcache_seg_size =  128, .max_multiple = 16 },
    { .name = "[ATA-3] Seagate Medalist 2132",                    .internal_name = "ST32132A",     .model = "ST32132A",                                                    .zones =  8, .avg_spt = 125, .heads =  6, .rpm = 4500, .full_stroke_ms = 30, .track_seek_ms = 2.3, .rcache_num_seg =  8, .rcache_seg_size =  120, .max_multiple = 16 },
    { .name = "[ATA-3] Western Digital Caviar 21700",             .internal_name = "AC21700",      .model = "WDC AC21700H",                                                .zones =  8, .avg_spt = 185, .heads =  3, .rpm = 5200, .full_stroke_ms = 21, .track_seek_ms = 3,   .rcache_num_seg =  8, .rcache_seg_size =  128, .max_multiple = 16 }, // Apple Computer OEM only, not retail version
    { .name = "[ATA-4] Fujitsu MPB3021AT",                        .internal_name = "MPB3021AT",    .model = "FUJITSU MPB3021AT",                                           .zones =  7, .avg_spt = 200, .heads =  3, .rpm = 5400, .full_stroke_ms = 20, .track_seek_ms = 2.5, .rcache_num_seg =  8, .rcache_seg_size =  256, .max_multiple = 16 },
    { .name = "[ATA-4] Fujitsu MPD3043AT",                        .internal_name = "MPD3043AT",    .model = "FUJITSU MPD3043AT",                                           .zones =  5, .avg_spt = 195, .heads =  2, .rpm = 5400, .full_stroke_ms = 29, .track_seek_ms = 1.5, .rcache_num_seg =  8, .rcache_seg_size =  512, .max_multiple = 16 },
    { .name = "[ATA-4] Fujitsu MPD3064AT",                        .internal_name = "MPD3064AT",    .model = "FUJITSU MPD3064AT",                                           .zones =  7, .avg_spt = 195, .heads =  3, .rpm = 5400, .full_stroke_ms = 30, .track_seek_ms = 1.5, .rcache_num_seg =  8, .rcache_seg_size =  512, .max_multiple = 16 },
    { .name = "[ATA-4] Fujitsu MPD3084AT",                        .internal_name = "MPD3084AT",    .model = "FUJITSU MPD3084AT",                                           .zones =  7, .avg_spt = 195, .heads =  4, .rpm = 5400, .full_stroke_ms = 19, .track_seek_ms = 1.5, .rcache_num_seg = 16, .rcache_seg_size =  512, .max_multiple = 16 },
    { .name = "[ATA-4] Fujitsu MPE3064AT",                        .internal_name = "MPE3064AT",    .model = "FUJITSU MPE3064AT",                                           .zones =  7, .avg_spt = 295, .heads =  2, .rpm = 5400, .full_stroke_ms = 30, .track_seek_ms = 1.5, .rcache_num_seg = 16, .rcache_seg_size =  512, .max_multiple = 32 },
    { .name = "[ATA-4] Maxtor DiamondMax 2160",                   .internal_name = "86480D6",      .model = "Maxtor 86480D6",                                              .zones =  8, .avg_spt = 197, .heads =  4, .rpm = 5200, .full_stroke_ms = 18, .track_seek_ms = 1,   .rcache_num_seg =  8, .rcache_seg_size =  512, .max_multiple = 32 },
    { .name = "[ATA-4] Maxtor DiamondMax 2880",                   .internal_name = "90432D3",      .model = "Maxtor 90432D3",                                              .zones = 16, .avg_spt = 190, .heads =  3, .rpm = 5400, .full_stroke_ms = 18, .track_seek_ms = 1,   .rcache_num_seg =  8, .rcache_seg_size =  256, .max_multiple = 32 },
    { .name = "[ATA-4] Maxtor DiamondMax 3400",                   .internal_name = "90644D3",      .model = "Maxtor 90644D3",                                              .zones = 16, .avg_spt = 290, .heads =  3, .rpm = 5400, .full_stroke_ms = 20, .track_seek_ms = 0.9, .rcache_num_seg =  8, .rcache_seg_size =  256, .max_multiple = 32 },
    { .name = "[ATA-4] Maxtor DiamondMax 4320 (90432D2)",         .internal_name = "90432D2",      .model = "Maxtor 90432D2",                                              .zones = 16, .avg_spt = 290, .heads =  2, .rpm = 5400, .full_stroke_ms = 20, .track_seek_ms = 0.9, .rcache_num_seg = 16, .rcache_seg_size =  256, .max_multiple = 32 },
    { .name = "[ATA-4] Maxtor DiamondMax 4320 (90845D4)",         .internal_name = "90845D4",      .model = "Maxtor 90845D4",                                              .zones = 16, .avg_spt = 290, .heads =  3, .rpm = 5400, .full_stroke_ms = 18, .track_seek_ms = 0.9, .rcache_num_seg = 16, .rcache_seg_size =  256, .max_multiple = 32 },
    { .name = "[ATA-4] Maxtor DiamondMax Plus 6800 (90683U2)",    .internal_name = "90683U2",      .model = "Maxtor 90683U2",                                              .zones = 16, .avg_spt = 290, .heads =  2, .rpm = 7200, .full_stroke_ms = 20, .track_seek_ms = 1,   .rcache_num_seg = 16, .rcache_seg_size =  256, .max_multiple = 32 },
    { .name = "[ATA-4] Maxtor DiamondMax Plus 6800 (91024U3)",    .internal_name = "91024U3",      .model = "Maxtor 91024U3",                                              .zones = 16, .avg_spt = 290, .heads =  3, .rpm = 7200, .full_stroke_ms = 20, .track_seek_ms = 1,   .rcache_num_seg = 16, .rcache_seg_size =  256, .max_multiple = 32 },
    { .name = "[ATA-4] Maxtor DiamondMax Plus 6800 (91366U4)",    .internal_name = "91366U4",      .model = "Maxtor 91366U4",                                              .zones = 16, .avg_spt = 290, .heads =  4, .rpm = 7200, .full_stroke_ms = 20, .track_seek_ms = 1,   .rcache_num_seg = 16, .rcache_seg_size =  256, .max_multiple = 32 },
    { .name = "[ATA-4] Maxtor DiamondMax Plus 6800 (92049U6)",    .internal_name = "92049U6",      .model = "Maxtor 92049U6",                                              .zones = 16, .avg_spt = 290, .heads =  6, .rpm = 7200, .full_stroke_ms = 20, .track_seek_ms = 1,   .rcache_num_seg = 16, .rcache_seg_size =  256, .max_multiple = 32 },
    { .name = "[ATA-4] Maxtor DiamondMax Plus 6800 (92732U8)",    .internal_name = "92732U8",      .model = "Maxtor 92732U8",                                              .zones = 16, .avg_spt = 290, .heads =  8, .rpm = 7200, .full_stroke_ms = 20, .track_seek_ms = 1,   .rcache_num_seg = 16, .rcache_seg_size =  256, .max_multiple = 32 },
    { .name = "[ATA-4] Quantum Bigfoot TX4.3AT",                  .internal_name = "TX043A011",    .model = "QUANTUM BIGFOOT TX4.3A",                                      .zones =  2, .avg_spt = 220, .heads =  2, .rpm = 4000, .full_stroke_ms = 30, .track_seek_ms = 2.5, .rcache_num_seg =  8, .rcache_seg_size =  128, .max_multiple = 32 },
    { .name = "[ATA-4] Quantum Fireball ST3.2AT",                 .internal_name = "ST32A461",     .model = "QUANTUM FIREBALL ST3.2A",                                     .zones =  4, .avg_spt = 200, .heads =  4, .rpm = 5400, .full_stroke_ms = 21, .track_seek_ms = 2,   .rcache_num_seg =  8, .rcache_seg_size =  128, .max_multiple = 16 },
    { .name = "[ATA-4] Quantum Fireball SE4.3A",                  .internal_name = "SE43A011",     .model = "QUANTUM FIREBALL SE4.3A",                                     .zones =  2, .avg_spt = 200, .heads =  4, .rpm = 5400, .full_stroke_ms = 20, .track_seek_ms = 2,   .rcache_num_seg =  8, .rcache_seg_size =  128, .max_multiple = 16 },
    { .name = "[ATA-4] Quantum Fireball SE6.4A",                  .internal_name = "SE64A011",     .model = "QUANTUM FIREBALL SE6.4A",                                     .zones =  3, .avg_spt = 200, .heads =  6, .rpm = 5400, .full_stroke_ms = 20, .track_seek_ms = 2,   .rcache_num_seg =  8, .rcache_seg_size =  128, .max_multiple = 16 },
    { .name = "[ATA-4] Quantum Fireball SE8.4A",                  .internal_name = "SE84A011",     .model = "QUANTUM FIREBALL SE8.4A",                                     .zones =  4, .avg_spt = 200, .heads =  8, .rpm = 5400, .full_stroke_ms = 20, .track_seek_ms = 2,   .rcache_num_seg =  8, .rcache_seg_size =  128, .max_multiple = 16 },
    { .name = "[ATA-4] Quantum Fireball EL2.5AT",                 .internal_name = "EL25A011",     .model = "QUANTUM FIREBALL EL2.5A",                                     .zones =  1, .avg_spt = 200, .heads =  2, .rpm = 5400, .full_stroke_ms = 18, .track_seek_ms = 2,   .rcache_num_seg =  8, .rcache_seg_size =  128, .max_multiple = 16 },
    { .name = "[ATA-4] Quantum Fireball EL5.1AT",                 .internal_name = "EL51A011",     .model = "QUANTUM FIREBALL EL5.1A",                                     .zones =  2, .avg_spt = 200, .heads =  4, .rpm = 5400, .full_stroke_ms = 18, .track_seek_ms = 2,   .rcache_num_seg =  8, .rcache_seg_size =  128, .max_multiple = 16 },
    { .name = "[ATA-4] Quantum Fireball EL7.6AT",                 .internal_name = "EL76A011",     .model = "QUANTUM FIREBALL EL7.6A",                                     .zones =  3, .avg_spt = 200, .heads =  6, .rpm = 5400, .full_stroke_ms = 18, .track_seek_ms = 2,   .rcache_num_seg =  8, .rcache_seg_size =  128, .max_multiple = 16 },
    { .name = "[ATA-4] Quantum Fireball EL10.2AT",                .internal_name = "EL10A012",     .model = "QUANTUM FIREBALL EL10.2A",                                    .zones =  4, .avg_spt = 200, .heads =  8, .rpm = 5400, .full_stroke_ms = 18, .track_seek_ms = 2,   .rcache_num_seg =  8, .rcache_seg_size =  128, .max_multiple = 16 },
    { .name = "[ATA-4] Quantum Fireball EX12.7A",                 .internal_name = "EX12A012",     .model = "QUANTUM FIREBALL EX12.7A",             .version = "A0A.0D00", .zones =  4, .avg_spt = 200, .heads =  8, .rpm = 5400, .full_stroke_ms = 20, .track_seek_ms = 2,   .rcache_num_seg =  8, .rcache_seg_size =  128, .max_multiple = 16 },
    { .name = "[ATA-4] Quantum Fireball LCT-08 (LA04A011)",       .internal_name = "LA04A011",     .model = "QUANTUM FIREBALLlct08 04",             .version = "A05.0X00", .zones =  8, .avg_spt = 280, .heads =  6, .rpm = 5400, .full_stroke_ms = 40, .track_seek_ms = 3,   .rcache_num_seg =  8, .rcache_seg_size =  512, .max_multiple = 16 },
    { .name = "[ATA-4] Seagate Medalist 2122",                    .internal_name = "ST32122A",     .model = "ST32122A",                                                    .zones = 16, .avg_spt = 215, .heads =  2, .rpm = 4500, .full_stroke_ms = 23, .track_seek_ms = 3.8, .rcache_num_seg = 16, .rcache_seg_size =  128, .max_multiple = 16 },
    { .name = "[ATA-4] Seagate Medalist 3321",                    .internal_name = "ST33221A",     .model = "ST33221A",                                                    .zones = 16, .avg_spt = 210, .heads =  4, .rpm = 5400, .full_stroke_ms = 20, .track_seek_ms = 1.7, .rcache_num_seg = 16, .rcache_seg_size =  128, .max_multiple = 16 },
    { .name = "[ATA-4] Seagate Medalist 4321",                    .internal_name = "ST34321A",     .model = "ST34321A",                                                    .zones = 16, .avg_spt = 210, .heads =  4, .rpm = 5400, .full_stroke_ms = 20, .track_seek_ms = 2.2, .rcache_num_seg = 16, .rcache_seg_size =  128, .max_multiple = 16 },
    { .name = "[ATA-4] Seagate Medalist 6531",                    .internal_name = "ST36531A",     .model = "ST36531A",                                                    .zones = 16, .avg_spt = 215, .heads =  6, .rpm = 5400, .full_stroke_ms = 20, .track_seek_ms = 1.7, .rcache_num_seg = 16, .rcache_seg_size =  128, .max_multiple = 16 },
    { .name = "[ATA-4] Seagate Medalist 8420",                    .internal_name = "ST38420A",     .model = "ST38420A",                                                    .zones = 16, .avg_spt = 290, .heads =  4, .rpm = 5400, .full_stroke_ms = 16, .track_seek_ms = 1.5, .rcache_num_seg = 16, .rcache_seg_size =  512, .max_multiple = 32 },
    { .name = "[ATA-4] Seagate Medalist 13030",                   .internal_name = "ST313030A",    .model = "ST313030A",                                                   .zones = 16, .avg_spt = 290, .heads =  6, .rpm = 5400, .full_stroke_ms = 16, .track_seek_ms = 1.5, .rcache_num_seg = 16, .rcache_seg_size =  512, .max_multiple = 32 },
    { .name = "[ATA-4] Seagate Medalist 17240",                   .internal_name = "ST317240A",    .model = "ST317240A",                                                   .zones = 16, .avg_spt = 290, .heads =  8, .rpm = 5400, .full_stroke_ms = 16, .track_seek_ms = 1.5, .rcache_num_seg = 16, .rcache_seg_size =  512, .max_multiple = 32 },
    { .name = "[ATA-4] Toshiba MK4006MAV",                        .internal_name = "MK4006MAV",    .model = "TOSHIBA MK4006MAV",                                           .zones =  8, .avg_spt = 230, .heads =  6, .rpm = 4200, .full_stroke_ms = 25, .track_seek_ms = 3,   .rcache_num_seg =  8, .rcache_seg_size =  512, .max_multiple = 32 },
    { .name = "[ATA-4] Western Digital Caviar 14300",             .internal_name = "AC14300",      .model = "WDC AC14300RT",                                               .zones = 16, .avg_spt = 195, .heads =  2, .rpm = 5400, .full_stroke_ms = 21, .track_seek_ms = 5.5, .rcache_num_seg =  8, .rcache_seg_size =  512, .max_multiple = 16 },
    { .name = "[ATA-4] Western Digital Caviar 23200",             .internal_name = "AC23200",      .model = "WDC AC23200LB",                                               .zones = 16, .avg_spt = 210, .heads =  4, .rpm = 5400, .full_stroke_ms = 21, .track_seek_ms = 3,   .rcache_num_seg =  8, .rcache_seg_size =  256, .max_multiple = 32 },
    { .name = "[ATA-4] Western Digital Caviar 26400",             .internal_name = "AC26400",      .model = "WDC AC26400RN",                                               .zones = 16, .avg_spt = 295, .heads =  5, .rpm = 5400, .full_stroke_ms = 21, .track_seek_ms = 3,   .rcache_num_seg =  8, .rcache_seg_size =  512, .max_multiple = 32 },
    { .name = "[ATA-4] Western Digital Caviar 33200",             .internal_name = "AC33200",      .model = "WDC AC33200LA",                                               .zones = 16, .avg_spt = 310, .heads =  5, .rpm = 5200, .full_stroke_ms = 40, .track_seek_ms = 3,   .rcache_num_seg = 16, .rcache_seg_size =  256, .max_multiple = 32 },
    { .name = "[ATA-4] Western Digital Caviar 35100",             .internal_name = "AC35100",      .model = "WDC AC35100L",                         .version = "09.09M08", .zones = 16, .avg_spt = 315, .heads =  5, .rpm = 5400, .full_stroke_ms = 40, .track_seek_ms = 3,   .rcache_num_seg = 16, .rcache_seg_size =  256, .max_multiple = 32 },
    { .name = "[ATA-5] IBM Travelstar 6GN",                       .internal_name = "DARA206000",   .model = "IBM-DARA-206000",                                             .zones = 12, .avg_spt = 292, .heads =  2, .rpm = 4200, .full_stroke_ms = 31, .track_seek_ms = 4,   .rcache_num_seg = 16, .rcache_seg_size =  512, .max_multiple = 32 },
    { .name = "[ATA-5] IBM Travelstar 9GN",                       .internal_name = "DARA209000",   .model = "IBM-DARA-209000",                                             .zones = 12, .avg_spt = 292, .heads =  3, .rpm = 4200, .full_stroke_ms = 31, .track_seek_ms = 4,   .rcache_num_seg = 16, .rcache_seg_size =  512, .max_multiple = 32 },
    { .name = "[ATA-5] IBM/Hitachi Travelstar 12GN",              .internal_name = "DARA212000",   .model = "IBM-DARA-212000",                                             .zones = 12, .avg_spt = 292, .heads =  4, .rpm = 4200, .full_stroke_ms = 31, .track_seek_ms = 4,   .rcache_num_seg = 16, .rcache_seg_size =  512, .max_multiple = 32 }, // Either Hitachi or IBM OEM
    { .name = "[ATA-5] Maxtor DiamondMax VL 17",                  .internal_name = "90871U2",      .model = "Maxtor 90871U2",                                              .zones = 16, .avg_spt = 290, .heads =  3, .rpm = 5400, .full_stroke_ms = 20, .track_seek_ms = 0.9, .rcache_num_seg = 16, .rcache_seg_size =  256, .max_multiple = 32 },
    { .name = "[ATA-5] Maxtor DiamondMax VL 20 (91021U2)",        .internal_name = "91021U2",      .model = "Maxtor 91021U2",                                              .zones = 16, .avg_spt = 295, .heads =  2, .rpm = 5400, .full_stroke_ms = 20, .track_seek_ms = 1,   .rcache_num_seg = 16, .rcache_seg_size =  512, .max_multiple = 32 },
    { .name = "[ATA-5] Maxtor DiamondMax VL 20 (91531U3)",        .internal_name = "91531U3",      .model = "Maxtor 91531U3",                                              .zones = 16, .avg_spt = 295, .heads =  3, .rpm = 5400, .full_stroke_ms = 20, .track_seek_ms = 1,   .rcache_num_seg = 16, .rcache_seg_size =  512, .max_multiple = 32 },
    { .name = "[ATA-5] Maxtor DiamondMax VL 20 (92041U4)",        .internal_name = "92041U4",      .model = "Maxtor 92041U4",                                              .zones = 16, .avg_spt = 295, .heads =  4, .rpm = 5400, .full_stroke_ms = 20, .track_seek_ms = 1,   .rcache_num_seg = 16, .rcache_seg_size =  512, .max_multiple = 32 },
    { .name = "[ATA-5] Quantum Fireball EX3.2A",                  .internal_name = "EX32A012",     .model = "QUANTUM FIREBALL EX3.2A",                                     .zones =  1, .avg_spt = 210, .heads =  2, .rpm = 5400, .full_stroke_ms = 18, .track_seek_ms = 2,   .rcache_num_seg =  8, .rcache_seg_size =  512, .max_multiple = 16 },
    { .name = "[ATA-5] Quantum Fireball EX5.1A",                  .internal_name = "EX51A012",     .model = "QUANTUM FIREBALL EX5.1A",                                     .zones =  2, .avg_spt = 210, .heads =  3, .rpm = 5400, .full_stroke_ms = 18, .track_seek_ms = 2,   .rcache_num_seg =  8, .rcache_seg_size =  512, .max_multiple = 16 },
    { .name = "[ATA-5] Quantum Fireball EX6.4A",                  .internal_name = "EX64A012",     .model = "QUANTUM FIREBALL EX6.4A",                                     .zones =  2, .avg_spt = 210, .heads =  4, .rpm = 5400, .full_stroke_ms = 18, .track_seek_ms = 2,   .rcache_num_seg =  8, .rcache_seg_size =  512, .max_multiple = 16 },
    { .name = "[ATA-5] Quantum Fireball EX10.2A",                 .internal_name = "EX10A011",     .model = "QUANTUM FIREBALL EX10.2A",                                    .zones =  3, .avg_spt = 210, .heads =  6, .rpm = 5400, .full_stroke_ms = 18, .track_seek_ms = 2,   .rcache_num_seg =  8, .rcache_seg_size =  512, .max_multiple = 16 },
    { .name = "[ATA-5] Quantum Fireball EX12.7A",                 .internal_name = "EX12A011",     .model = "QUANTUM FIREBALL EX12.7A",                                    .zones =  4, .avg_spt = 210, .heads =  8, .rpm = 5400, .full_stroke_ms = 18, .track_seek_ms = 2,   .rcache_num_seg =  8, .rcache_seg_size =  512, .max_multiple = 16 },
    { .name = "[ATA-5] Quantum Fireball CR4.3A",                  .internal_name = "CR43A013",     .model = "QUANTUM FIREBALL CR4.3A",                                     .zones =  2, .avg_spt = 310, .heads =  3, .rpm = 5400, .full_stroke_ms = 18, .track_seek_ms = 2,   .rcache_num_seg =  8, .rcache_seg_size =  512, .max_multiple = 16 },
    { .name = "[ATA-5] Quantum Fireball CR6.4A",                  .internal_name = "CR64A011",     .model = "QUANTUM FIREBALL CR6.4A",                                     .zones =  2, .avg_spt = 310, .heads =  4, .rpm = 5400, .full_stroke_ms = 18, .track_seek_ms = 2,   .rcache_num_seg =  8, .rcache_seg_size =  512, .max_multiple = 16 },  
    { .name = "[ATA-5] Quantum Fireball CR8.4A",                  .internal_name = "CR84A011",     .model = "QUANTUM FIREBALL CR8.4A",                                     .zones =  3, .avg_spt = 310, .heads =  6, .rpm = 5400, .full_stroke_ms = 18, .track_seek_ms = 2,   .rcache_num_seg =  8, .rcache_seg_size =  512, .max_multiple = 16 },
    { .name = "[ATA-5] Quantum Fireball CR13.0A",                 .internal_name = "CR13A011",     .model = "QUANTUM FIREBALL CR13.0A",                                    .zones =  4, .avg_spt = 310, .heads =  8, .rpm = 5400, .full_stroke_ms = 18, .track_seek_ms = 2,   .rcache_num_seg =  8, .rcache_seg_size =  512, .max_multiple = 16 },
    { .name = "[ATA-5] Samsung SpinPoint V6800 (SV0682D)",        .internal_name = "SV0682D",      .model = "SAMSUNG SV0682D",                                             .zones =  8, .avg_spt = 295, .heads =  2, .rpm = 5400, .full_stroke_ms = 18, .track_seek_ms = 1.3, .rcache_num_seg = 16, .rcache_seg_size =  512, .max_multiple = 32 },
    { .name = "[ATA-5] Samsung SpinPoint V6800 (SV1023D)",        .internal_name = "SV1023D",      .model = "SAMSUNG SV1023D",                                             .zones =  8, .avg_spt = 295, .heads =  3, .rpm = 5400, .full_stroke_ms = 18, .track_seek_ms = 1.3, .rcache_num_seg = 16, .rcache_seg_size =  512, .max_multiple = 32 },
    { .name = "[ATA-5] Samsung SpinPoint V6800 (SV1364D)",        .internal_name = "SV1364D",      .model = "SAMSUNG SV1364D",                                             .zones =  8, .avg_spt = 295, .heads =  4, .rpm = 5400, .full_stroke_ms = 18, .track_seek_ms = 1.3, .rcache_num_seg = 16, .rcache_seg_size =  512, .max_multiple = 32 },
    { .name = "[ATA-5] Samsung SpinPoint V6800 (SV1705D)",        .internal_name = "SV1705D",      .model = "SAMSUNG SV1705D",                                             .zones =  8, .avg_spt = 295, .heads =  5, .rpm = 5400, .full_stroke_ms = 18, .track_seek_ms = 1.3, .rcache_num_seg = 16, .rcache_seg_size =  512, .max_multiple = 32 },
    { .name = "[ATA-5] Samsung SpinPoint V6800 (SV2046D)",        .internal_name = "SV2046D",      .model = "SAMSUNG SV2046D",                                             .zones =  8, .avg_spt = 295, .heads =  6, .rpm = 5400, .full_stroke_ms = 18, .track_seek_ms = 1.3, .rcache_num_seg = 16, .rcache_seg_size =  512, .max_multiple = 32 },
    { .name = "[ATA-5] Seagate U8 - 4.3gb",                       .internal_name = "ST34313A",     .model = "ST34313A",                                                    .zones = 16, .avg_spt = 289, .heads =  1, .rpm = 5400, .full_stroke_ms = 25, .track_seek_ms = 1.5, .rcache_num_seg = 16, .rcache_seg_size =  512, .max_multiple = 32 },
    { .name = "[ATA-5] Seagate U8 - 8.4gb",                       .internal_name = "ST38410A",     .model = "ST38410A",                                                    .zones = 16, .avg_spt = 289, .heads =  2, .rpm = 5400, .full_stroke_ms = 25, .track_seek_ms = 1.5, .rcache_num_seg = 16, .rcache_seg_size =  512, .max_multiple = 32 },
    { .name = "[ATA-5] Seagate U8 - 13gb",                        .internal_name = "ST313021A",    .model = "ST313021A",                                                   .zones = 16, .avg_spt = 289, .heads =  4, .rpm = 5400, .full_stroke_ms = 25, .track_seek_ms = 1.5, .rcache_num_seg = 16, .rcache_seg_size =  512, .max_multiple = 32 },
    { .name = "[ATA-5] Seagate U8 - 17.2gb",                      .internal_name = "ST317221A",    .model = "ST317221A",                                                   .zones = 16, .avg_spt = 289, .heads =  3, .rpm = 5400, .full_stroke_ms = 25, .track_seek_ms = 1.5, .rcache_num_seg = 16, .rcache_seg_size =  512, .max_multiple = 32 },
    { .name = "[ATA-5] Western Digital Caviar 102AA",             .internal_name = "WD102AA",      .model = "WDC WD102AA-00ANA0",                                          .zones = 16, .avg_spt = 295, .heads =  8, .rpm = 5400, .full_stroke_ms = 12, .track_seek_ms = 1.5, .rcache_num_seg = 16, .rcache_seg_size =  512, .max_multiple = 32 },
    { .name = "[ATA-5] Western Digital Expert 135BA",             .internal_name = "WD135BA",      .model = "WDC WD135BA-60AK",                                            .zones = 16, .avg_spt = 350, .heads =  6, .rpm = 7200, .full_stroke_ms = 15, .track_seek_ms = 2,   .rcache_num_seg = 16, .rcache_seg_size = 1920, .max_multiple = 32 },
    { .name = "[ATA-5] Western Digital Expert 200BA",             .internal_name = "WD200BA",      .model = "WDC WD200BA-60AGA0",                                          .zones = 16, .avg_spt = 350, .heads =  6, .rpm = 7200, .full_stroke_ms = 15, .track_seek_ms = 2,   .rcache_num_seg = 16, .rcache_seg_size = 1920, .max_multiple = 32 },
   // clang-format on
};

int
hdd_preset_get_num(void)
{
    return sizeof(hdd_speed_presets) / sizeof(hdd_preset_t);
}

const char *
hdd_preset_getname(int preset)
{
    return hdd_speed_presets[preset].name;
}

const char *
hdd_preset_get_internal_name(int preset)
{
    return hdd_speed_presets[preset].internal_name;
}

uint32_t
hdd_preset_get_rpm(int preset)
{
    if (preset < 0 || preset >= hdd_preset_get_num())
        return 0;
    return hdd_speed_presets[preset].rpm;
}

int
hdd_preset_get_from_internal_name(char *s)
{
    int c = 0;

    for (int i = 0; i < (sizeof(hdd_speed_presets) / sizeof(hdd_preset_t)); i++) {
        if (!strcmp(hdd_speed_presets[c].internal_name, s))
            return c;
        c++;
    }

    return 0;
}

void
hdd_preset_apply(int hdd_id)
{
    hard_disk_t *hd = &hdd[hdd_id];
    double       revolution_usec;
    double       zone_percent;
    uint32_t     disk_sectors;
    uint32_t     sectors_per_surface;
    uint32_t     cylinders;
    uint32_t     cylinders_per_zone;
    uint32_t     total_sectors = 0;
    uint32_t     spt;
    uint32_t     zone_sectors;

    if (hd->speed_preset >= hdd_preset_get_num())
        hd->speed_preset = 0;

    const hdd_preset_t *preset = &hdd_speed_presets[hd->speed_preset];

    hd->cache.num_segments = preset->rcache_num_seg;
    hd->cache.segment_size = preset->rcache_seg_size;
    hd->max_multiple_block = preset->max_multiple;
    if (preset->model)
        hd->model = preset->model;

    if (preset->version)
        hd->version = preset->version;

    if (!hd->speed_preset)
        return;

    hd->phy_heads = preset->heads;
    hd->rpm       = preset->rpm;

    revolution_usec           = 60.0 / (double) hd->rpm * 1000000.0;
    hd->avg_rotation_lat_usec = revolution_usec / 2;
    hd->full_stroke_usec      = preset->full_stroke_ms * 1000;
    hd->head_switch_usec      = preset->track_seek_ms * 1000;
    hd->cyl_switch_usec       = preset->track_seek_ms * 1000;

    hd->cache.write_size = 64;

    hd->num_zones = preset->zones;

    disk_sectors        = hd->tracks * hd->hpc * hd->spt;
    sectors_per_surface = (uint32_t) ceil((double) disk_sectors / (double) hd->phy_heads);
    cylinders           = (uint32_t) ceil((double) sectors_per_surface / (double) preset->avg_spt);
    hd->phy_cyl         = cylinders;
    cylinders_per_zone  = cylinders / preset->zones;

    for (uint32_t i = 0; i < preset->zones; i++) {
        zone_percent = i * 100 / (double) preset->zones;

        if (i < preset->zones - 1) {
            /* Function for realistic zone sector density */
            double spt_percent = -0.00341684 * pow(zone_percent, 2) - 0.175811 * zone_percent + 118.48;
            spt                = (uint32_t) ceil((double) preset->avg_spt * spt_percent / 100);
        } else
            spt = (uint32_t) ceil((double) (disk_sectors - total_sectors) / (double) (cylinders_per_zone * preset->heads));

        zone_sectors = spt * cylinders_per_zone * preset->heads;
        total_sectors += zone_sectors;

        hd->zones[i].cylinders         = cylinders_per_zone;
        hd->zones[i].sectors_per_track = spt;
    }

    hdd_zones_init(hd);
    hdd_cache_init(hd);
}
