ata_bd.c

Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2009 Jiri Svoboda
00003  * All rights reserved.
00004  *
00005  * Redistribution and use in source and binary forms, with or without
00006  * modification, are permitted provided that the following conditions
00007  * are met:
00008  *
00009  * - Redistributions of source code must retain the above copyright
00010  *   notice, this list of conditions and the following disclaimer.
00011  * - Redistributions in binary form must reproduce the above copyright
00012  *   notice, this list of conditions and the following disclaimer in the
00013  *   documentation and/or other materials provided with the distribution.
00014  * - The name of the author may not be used to endorse or promote products
00015  *   derived from this software without specific prior written permission.
00016  *
00017  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
00018  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
00019  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
00020  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
00021  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
00022  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
00023  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
00024  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00025  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
00026  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00027  */
00028 
00050 #include <stdio.h>
00051 #include <libarch/ddi.h>
00052 #include <ddi.h>
00053 #include <ipc/bd.h>
00054 #include <async.h>
00055 #include <as.h>
00056 #include <fibril_synch.h>
00057 #include <stdint.h>
00058 #include <str.h>
00059 #include <devmap.h>
00060 #include <sys/types.h>
00061 #include <inttypes.h>
00062 #include <errno.h>
00063 #include <bool.h>
00064 #include <byteorder.h>
00065 #include <task.h>
00066 #include <macros.h>
00067 
00068 #include "ata_hw.h"
00069 #include "ata_bd.h"
00070 
00071 #define NAME       "ata_bd"
00072 #define NAMESPACE  "bd"
00073 
00075 #define LEGACY_CTLS 4
00076 
00081 static const size_t identify_data_size = 512;
00082 
00084 static size_t comm_size;
00085 
00087 static uintptr_t cmd_physical;
00089 static uintptr_t ctl_physical;
00090 
00092 static ata_base_t legacy_base[LEGACY_CTLS] = {
00093         { 0x1f0, 0x3f0 },
00094         { 0x170, 0x370 },
00095         { 0x1e8, 0x3e8 },
00096         { 0x168, 0x368 }
00097 };
00098 
00099 static ata_cmd_t *cmd;
00100 static ata_ctl_t *ctl;
00101 
00103 static disk_t disk[MAX_DISKS];
00104 
00105 static void print_syntax(void);
00106 static int ata_bd_init(void);
00107 static void ata_bd_connection(ipc_callid_t iid, ipc_call_t *icall);
00108 static int ata_bd_read_blocks(int disk_id, uint64_t ba, size_t cnt,
00109     void *buf);
00110 static int ata_bd_write_blocks(int disk_id, uint64_t ba, size_t cnt,
00111     const void *buf);
00112 static int ata_rcmd_read(int disk_id, uint64_t ba, size_t cnt,
00113     void *buf);
00114 static int ata_rcmd_write(int disk_id, uint64_t ba, size_t cnt,
00115     const void *buf);
00116 static int disk_init(disk_t *d, int disk_id);
00117 static int drive_identify(int drive_id, void *buf);
00118 static int identify_pkt_dev(int dev_idx, void *buf);
00119 static int ata_cmd_packet(int dev_idx, const void *cpkt, size_t cpkt_size,
00120     void *obuf, size_t obuf_size);
00121 static int ata_pcmd_inquiry(int dev_idx, void *obuf, size_t obuf_size);
00122 static int ata_pcmd_read_12(int dev_idx, uint64_t ba, size_t cnt,
00123     void *obuf, size_t obuf_size);
00124 static void disk_print_summary(disk_t *d);
00125 static int coord_calc(disk_t *d, uint64_t ba, block_coord_t *bc);
00126 static void coord_sc_program(const block_coord_t *bc, uint16_t scnt);
00127 static int wait_status(unsigned set, unsigned n_reset, uint8_t *pstatus,
00128     unsigned timeout);
00129 
00130 int main(int argc, char **argv)
00131 {
00132         char name[16];
00133         int i, rc;
00134         int n_disks;
00135         unsigned ctl_num;
00136         char *eptr;
00137 
00138         printf(NAME ": ATA disk driver\n");
00139 
00140         if (argc > 1) {
00141                 ctl_num = strtoul(argv[1], &eptr, 0);
00142                 if (*eptr != '\0' || ctl_num == 0 || ctl_num > 4) {
00143                         printf("Invalid argument.\n");
00144                         print_syntax();
00145                         return -1;
00146                 }
00147         } else {
00148                 ctl_num = 1;
00149         }
00150 
00151         cmd_physical = legacy_base[ctl_num - 1].cmd;
00152         ctl_physical = legacy_base[ctl_num - 1].ctl;
00153 
00154         printf("I/O address %p/%p\n", (void *) cmd_physical,
00155             (void *) ctl_physical);
00156 
00157         if (ata_bd_init() != EOK)
00158                 return -1;
00159 
00160         for (i = 0; i < MAX_DISKS; i++) {
00161                 printf("Identify drive %d... ", i);
00162                 fflush(stdout);
00163 
00164                 rc = disk_init(&disk[i], i);
00165 
00166                 if (rc == EOK) {
00167                         disk_print_summary(&disk[i]);
00168                 } else {
00169                         printf("Not found.\n");
00170                 }
00171         }
00172 
00173         n_disks = 0;
00174 
00175         for (i = 0; i < MAX_DISKS; i++) {
00176                 /* Skip unattached drives. */
00177                 if (disk[i].present == false)
00178                         continue;
00179                 
00180                 snprintf(name, 16, "%s/ata%udisk%d", NAMESPACE, ctl_num, i);
00181                 rc = devmap_device_register(name, &disk[i].devmap_handle);
00182                 if (rc != EOK) {
00183                         printf(NAME ": Unable to register device %s.\n", name);
00184                         return rc;
00185                 }
00186                 ++n_disks;
00187         }
00188 
00189         if (n_disks == 0) {
00190                 printf("No disks detected.\n");
00191                 return -1;
00192         }
00193 
00194         printf(NAME ": Accepting connections\n");
00195         task_retval(0);
00196         async_manager();
00197 
00198         /* Not reached */
00199         return 0;
00200 }
00201 
00202 
00203 static void print_syntax(void)
00204 {
00205         printf("Syntax: " NAME " <controller_number>\n");
00206         printf("Controller number = 1..4\n");
00207 }
00208 
00210 static void disk_print_summary(disk_t *d)
00211 {
00212         uint64_t mbytes;
00213 
00214         printf("%s: ", d->model);
00215 
00216         if (d->dev_type == ata_reg_dev) {
00217                 switch (d->amode) {
00218                 case am_chs:
00219                         printf("CHS %u cylinders, %u heads, %u sectors",
00220                             disk->geom.cylinders, disk->geom.heads,
00221                             disk->geom.sectors);
00222                         break;
00223                 case am_lba28:
00224                         printf("LBA-28");
00225                         break;
00226                 case am_lba48:
00227                         printf("LBA-48");
00228                         break;
00229                 }
00230         } else {
00231                 printf("PACKET");
00232         }
00233 
00234         printf(" %" PRIu64 " blocks", d->blocks);
00235 
00236         mbytes = d->blocks / (2 * 1024);
00237         if (mbytes > 0)
00238                 printf(" %" PRIu64 " MB.", mbytes);
00239 
00240         printf("\n");
00241 }
00242 
00244 static int ata_bd_init(void)
00245 {
00246         void *vaddr;
00247         int rc;
00248 
00249         rc = devmap_driver_register(NAME, ata_bd_connection);
00250         if (rc < 0) {
00251                 printf(NAME ": Unable to register driver.\n");
00252                 return rc;
00253         }
00254 
00255         rc = pio_enable((void *) cmd_physical, sizeof(ata_cmd_t), &vaddr);
00256         if (rc != EOK) {
00257                 printf(NAME ": Could not initialize device I/O space.\n");
00258                 return rc;
00259         }
00260 
00261         cmd = vaddr;
00262 
00263         rc = pio_enable((void *) ctl_physical, sizeof(ata_ctl_t), &vaddr);
00264         if (rc != EOK) {
00265                 printf(NAME ": Could not initialize device I/O space.\n");
00266                 return rc;
00267         }
00268 
00269         ctl = vaddr;
00270 
00271 
00272         return EOK;
00273 }
00274 
00276 static void ata_bd_connection(ipc_callid_t iid, ipc_call_t *icall)
00277 {
00278         void *fs_va = NULL;
00279         ipc_callid_t callid;
00280         ipc_call_t call;
00281         sysarg_t method;
00282         devmap_handle_t dh;
00283         unsigned int flags;
00284         int retval;
00285         uint64_t ba;
00286         size_t cnt;
00287         int disk_id, i;
00288 
00289         /* Get the device handle. */
00290         dh = IPC_GET_ARG1(*icall);
00291 
00292         /* Determine which disk device is the client connecting to. */
00293         disk_id = -1;
00294         for (i = 0; i < MAX_DISKS; i++)
00295                 if (disk[i].devmap_handle == dh)
00296                         disk_id = i;
00297 
00298         if (disk_id < 0 || disk[disk_id].present == false) {
00299                 async_answer_0(iid, EINVAL);
00300                 return;
00301         }
00302 
00303         /* Answer the IPC_M_CONNECT_ME_TO call. */
00304         async_answer_0(iid, EOK);
00305 
00306         if (!async_share_out_receive(&callid, &comm_size, &flags)) {
00307                 async_answer_0(callid, EHANGUP);
00308                 return;
00309         }
00310 
00311         fs_va = as_get_mappable_page(comm_size);
00312         if (fs_va == NULL) {
00313                 async_answer_0(callid, EHANGUP);
00314                 return;
00315         }
00316 
00317         (void) async_share_out_finalize(callid, fs_va);
00318 
00319         while (1) {
00320                 callid = async_get_call(&call);
00321                 method = IPC_GET_IMETHOD(call);
00322                 switch (method) {
00323                 case IPC_M_PHONE_HUNGUP:
00324                         /* The other side has hung up. */
00325                         async_answer_0(callid, EOK);
00326                         return;
00327                 case BD_READ_BLOCKS:
00328                         ba = MERGE_LOUP32(IPC_GET_ARG1(call),
00329                             IPC_GET_ARG2(call));
00330                         cnt = IPC_GET_ARG3(call);
00331                         if (cnt * disk[disk_id].block_size > comm_size) {
00332                                 retval = ELIMIT;
00333                                 break;
00334                         }
00335                         retval = ata_bd_read_blocks(disk_id, ba, cnt, fs_va);
00336                         break;
00337                 case BD_WRITE_BLOCKS:
00338                         ba = MERGE_LOUP32(IPC_GET_ARG1(call),
00339                             IPC_GET_ARG2(call));
00340                         cnt = IPC_GET_ARG3(call);
00341                         if (cnt * disk[disk_id].block_size > comm_size) {
00342                                 retval = ELIMIT;
00343                                 break;
00344                         }
00345                         retval = ata_bd_write_blocks(disk_id, ba, cnt, fs_va);
00346                         break;
00347                 case BD_GET_BLOCK_SIZE:
00348                         async_answer_1(callid, EOK, disk[disk_id].block_size);
00349                         continue;
00350                 case BD_GET_NUM_BLOCKS:
00351                         async_answer_2(callid, EOK, LOWER32(disk[disk_id].blocks),
00352                             UPPER32(disk[disk_id].blocks));
00353                         continue;
00354                 default:
00355                         retval = EINVAL;
00356                         break;
00357                 }
00358                 async_answer_0(callid, retval);
00359         }
00360 }
00361 
00367 static int disk_init(disk_t *d, int disk_id)
00368 {
00369         identify_data_t idata;
00370         uint8_t model[40];
00371         ata_inquiry_data_t inq_data;
00372         uint16_t w;
00373         uint8_t c;
00374         uint16_t bc;
00375         size_t pos, len;
00376         int rc;
00377         unsigned i;
00378 
00379         d->present = false;
00380         fibril_mutex_initialize(&d->lock);
00381 
00382         /* Try identify command. */
00383         rc = drive_identify(disk_id, &idata);
00384         if (rc == EOK) {
00385                 /* Success. It's a register (non-packet) device. */
00386                 printf("ATA register-only device found.\n");
00387                 d->dev_type = ata_reg_dev;
00388         } else if (rc == EIO) {
00389                 /*
00390                  * There is something, but not a register device. Check to see
00391                  * whether the IDENTIFY command left the packet signature in
00392                  * the registers in case this is a packet device.
00393                  *
00394                  * According to the ATA specification, the LBA low and
00395                  * interrupt reason registers should be set to 0x01. However,
00396                  * there are many devices that do not follow this and only set
00397                  * the byte count registers. So, only check these.
00398                  */
00399                 bc = ((uint16_t)pio_read_8(&cmd->cylinder_high) << 8) |
00400                     pio_read_8(&cmd->cylinder_low);
00401 
00402                 if (bc == PDEV_SIGNATURE_BC) {
00403                         rc = identify_pkt_dev(disk_id, &idata);
00404                         if (rc == EOK) {
00405                                 /* We have a packet device. */
00406                                 d->dev_type = ata_pkt_dev;
00407                         } else {
00408                                 return EIO;
00409                         }
00410                 } else {
00411                         /* Nope. Something's there, but not recognized. */
00412                         return EIO;
00413                 }
00414         } else {
00415                 /* Operation timed out. That means there is no device there. */
00416                 return EIO;
00417         }
00418 
00419         if (d->dev_type == ata_pkt_dev) {
00420                 /* Packet device */
00421                 d->amode = 0;
00422 
00423                 d->geom.cylinders = 0;
00424                 d->geom.heads = 0;
00425                 d->geom.sectors = 0;
00426 
00427                 d->blocks = 0;
00428         } else if ((idata.caps & rd_cap_lba) == 0) {
00429                 /* Device only supports CHS addressing. */
00430                 d->amode = am_chs;
00431 
00432                 d->geom.cylinders = idata.cylinders;
00433                 d->geom.heads = idata.heads;
00434                 d->geom.sectors = idata.sectors;
00435 
00436                 d->blocks = d->geom.cylinders * d->geom.heads * d->geom.sectors;
00437         } else if ((idata.cmd_set1 & cs1_addr48) == 0) {
00438                 /* Device only supports LBA-28 addressing. */
00439                 d->amode = am_lba28;
00440 
00441                 d->geom.cylinders = 0;
00442                 d->geom.heads = 0;
00443                 d->geom.sectors = 0;
00444 
00445                 d->blocks =
00446                      (uint32_t) idata.total_lba28_0 | 
00447                     ((uint32_t) idata.total_lba28_1 << 16);
00448         } else {
00449                 /* Device supports LBA-48 addressing. */
00450                 d->amode = am_lba48;
00451 
00452                 d->geom.cylinders = 0;
00453                 d->geom.heads = 0;
00454                 d->geom.sectors = 0;
00455 
00456                 d->blocks =
00457                      (uint64_t) idata.total_lba48_0 |
00458                     ((uint64_t) idata.total_lba48_1 << 16) |
00459                     ((uint64_t) idata.total_lba48_2 << 32) | 
00460                     ((uint64_t) idata.total_lba48_3 << 48);
00461         }
00462 
00463         /*
00464          * Convert model name to string representation.
00465          */
00466         for (i = 0; i < 20; i++) {
00467                 w = idata.model_name[i];
00468                 model[2 * i] = w >> 8;
00469                 model[2 * i + 1] = w & 0x00ff;
00470         }
00471 
00472         len = 40;
00473         while (len > 0 && model[len - 1] == 0x20)
00474                 --len;
00475 
00476         pos = 0;
00477         for (i = 0; i < len; ++i) {
00478                 c = model[i];
00479                 if (c >= 0x80) c = '?';
00480 
00481                 chr_encode(c, d->model, &pos, 40);
00482         }
00483         d->model[pos] = '\0';
00484 
00485         if (d->dev_type == ata_pkt_dev) {
00486                 /* Send inquiry. */
00487                 rc = ata_pcmd_inquiry(0, &inq_data, sizeof(inq_data));
00488                 if (rc != EOK) {
00489                         printf("Device inquiry failed.\n");
00490                         d->present = false;
00491                         return EIO;
00492                 }
00493 
00494                 /* Check device type. */
00495                 if (INQUIRY_PDEV_TYPE(inq_data.pdev_type) != PDEV_TYPE_CDROM)
00496                         printf("Warning: Peripheral device type is not CD-ROM.\n");
00497 
00498                 /* Assume 2k block size for now. */
00499                 d->block_size = 2048;
00500         } else {
00501                 /* Assume register Read always uses 512-byte blocks. */
00502                 d->block_size = 512;
00503         }
00504 
00505         d->present = true;
00506         return EOK;
00507 }
00508 
00510 static int ata_bd_read_blocks(int disk_id, uint64_t ba, size_t cnt,
00511     void *buf) {
00512 
00513         int rc;
00514 
00515         while (cnt > 0) {
00516                 if (disk[disk_id].dev_type == ata_reg_dev)
00517                         rc = ata_rcmd_read(disk_id, ba, 1, buf);
00518                 else
00519                         rc = ata_pcmd_read_12(disk_id, ba, 1, buf,
00520                             disk[disk_id].block_size);
00521 
00522                 if (rc != EOK)
00523                         return rc;
00524 
00525                 ++ba;
00526                 --cnt;
00527                 buf += disk[disk_id].block_size;
00528         }
00529 
00530         return EOK;
00531 }
00532 
00534 static int ata_bd_write_blocks(int disk_id, uint64_t ba, size_t cnt,
00535     const void *buf) {
00536 
00537         int rc;
00538 
00539         if (disk[disk_id].dev_type != ata_reg_dev)
00540                 return ENOTSUP;
00541 
00542         while (cnt > 0) {
00543                 rc = ata_rcmd_write(disk_id, ba, 1, buf);
00544                 if (rc != EOK)
00545                         return rc;
00546 
00547                 ++ba;
00548                 --cnt;
00549                 buf += disk[disk_id].block_size;
00550         }
00551 
00552         return EOK;
00553 }
00554 
00566 static int drive_identify(int disk_id, void *buf)
00567 {
00568         uint16_t data;
00569         uint8_t status;
00570         uint8_t drv_head;
00571         size_t i;
00572 
00573         drv_head = ((disk_id != 0) ? DHR_DRV : 0);
00574 
00575         if (wait_status(0, ~SR_BSY, NULL, TIMEOUT_PROBE) != EOK)
00576                 return ETIMEOUT;
00577 
00578         pio_write_8(&cmd->drive_head, drv_head);
00579 
00580         /*
00581          * Do not wait for DRDY to be set in case this is a packet device.
00582          * We determine whether the device is present by waiting for DRQ to be
00583          * set after issuing the command.
00584          */
00585         if (wait_status(0, ~SR_BSY, NULL, TIMEOUT_PROBE) != EOK)
00586                 return ETIMEOUT;
00587 
00588         pio_write_8(&cmd->command, CMD_IDENTIFY_DRIVE);
00589 
00590         if (wait_status(0, ~SR_BSY, &status, TIMEOUT_PROBE) != EOK)
00591                 return ETIMEOUT;
00592 
00593         /*
00594          * If ERR is set, this may be a packet device, so return EIO to cause
00595          * the caller to check for one.
00596          */
00597         if ((status & SR_ERR) != 0) {
00598                 return EIO;
00599         }
00600 
00601         if (wait_status(SR_DRQ, ~SR_BSY, &status, TIMEOUT_PROBE) != EOK)
00602                 return ETIMEOUT;
00603 
00604         /* Read data from the disk buffer. */
00605 
00606         for (i = 0; i < identify_data_size / 2; i++) {
00607                 data = pio_read_16(&cmd->data_port);
00608                 ((uint16_t *) buf)[i] = data;
00609         }
00610 
00611         return EOK;
00612 }
00613 
00622 static int identify_pkt_dev(int dev_idx, void *buf)
00623 {
00624         uint16_t data;
00625         uint8_t status;
00626         uint8_t drv_head;
00627         size_t i;
00628 
00629         drv_head = ((dev_idx != 0) ? DHR_DRV : 0);
00630 
00631         if (wait_status(0, ~SR_BSY, NULL, TIMEOUT_PROBE) != EOK)
00632                 return EIO;
00633 
00634         pio_write_8(&cmd->drive_head, drv_head);
00635 
00636         /* For ATAPI commands we do not need to wait for DRDY. */
00637         if (wait_status(0, ~SR_BSY, NULL, TIMEOUT_PROBE) != EOK)
00638                 return EIO;
00639 
00640         pio_write_8(&cmd->command, CMD_IDENTIFY_PKT_DEV);
00641 
00642         if (wait_status(0, ~SR_BSY, &status, TIMEOUT_BSY) != EOK)
00643                 return EIO;
00644 
00645         /* Read data from the device buffer. */
00646 
00647         if ((status & SR_DRQ) != 0) {
00648                 for (i = 0; i < identify_data_size / 2; i++) {
00649                         data = pio_read_16(&cmd->data_port);
00650                         ((uint16_t *) buf)[i] = data;
00651                 }
00652         }
00653 
00654         if ((status & SR_ERR) != 0)
00655                 return EIO;
00656 
00657         return EOK;
00658 }
00659 
00670 static int ata_cmd_packet(int dev_idx, const void *cpkt, size_t cpkt_size,
00671     void *obuf, size_t obuf_size)
00672 {
00673         size_t i;
00674         uint8_t status;
00675         uint8_t drv_head;
00676         disk_t *d;
00677         size_t data_size;
00678         uint16_t val;
00679 
00680         d = &disk[dev_idx];
00681         fibril_mutex_lock(&d->lock);
00682 
00683         /* New value for Drive/Head register */
00684         drv_head =
00685             ((dev_idx != 0) ? DHR_DRV : 0);
00686 
00687         if (wait_status(0, ~SR_BSY, NULL, TIMEOUT_PROBE) != EOK) {
00688                 fibril_mutex_unlock(&d->lock);
00689                 return EIO;
00690         }
00691 
00692         pio_write_8(&cmd->drive_head, drv_head);
00693 
00694         if (wait_status(0, ~(SR_BSY|SR_DRQ), NULL, TIMEOUT_BSY) != EOK) {
00695                 fibril_mutex_unlock(&d->lock);
00696                 return EIO;
00697         }
00698 
00699         /* Byte count <- max. number of bytes we can read in one transfer. */
00700         pio_write_8(&cmd->cylinder_low, 0xfe);
00701         pio_write_8(&cmd->cylinder_high, 0xff);
00702 
00703         pio_write_8(&cmd->command, CMD_PACKET);
00704 
00705         if (wait_status(SR_DRQ, ~SR_BSY, &status, TIMEOUT_BSY) != EOK) {
00706                 fibril_mutex_unlock(&d->lock);
00707                 return EIO;
00708         }
00709 
00710         /* Write command packet. */
00711         for (i = 0; i < (cpkt_size + 1) / 2; i++)
00712                 pio_write_16(&cmd->data_port, ((uint16_t *) cpkt)[i]);
00713 
00714         if (wait_status(0, ~SR_BSY, &status, TIMEOUT_BSY) != EOK) {
00715                 fibril_mutex_unlock(&d->lock);
00716                 return EIO;
00717         }
00718 
00719         if ((status & SR_DRQ) == 0) {
00720                 fibril_mutex_unlock(&d->lock);
00721                 return EIO;
00722         }
00723 
00724         /* Read byte count. */
00725         data_size = (uint16_t) pio_read_8(&cmd->cylinder_low) +
00726             ((uint16_t) pio_read_8(&cmd->cylinder_high) << 8);
00727 
00728         /* Check whether data fits into output buffer. */
00729         if (data_size > obuf_size) {
00730                 /* Output buffer is too small to store data. */
00731                 fibril_mutex_unlock(&d->lock);
00732                 return EIO;
00733         }
00734 
00735         /* Read data from the device buffer. */
00736         for (i = 0; i < (data_size + 1) / 2; i++) {
00737                 val = pio_read_16(&cmd->data_port);
00738                 ((uint16_t *) obuf)[i] = val;
00739         }
00740 
00741         if (status & SR_ERR) {
00742                 fibril_mutex_unlock(&d->lock);
00743                 return EIO;
00744         }
00745 
00746         fibril_mutex_unlock(&d->lock);
00747 
00748         return EOK;
00749 }
00750 
00759 static int ata_pcmd_inquiry(int dev_idx, void *obuf, size_t obuf_size)
00760 {
00761         ata_pcmd_inquiry_t cp;
00762         int rc;
00763 
00764         memset(&cp, 0, sizeof(cp));
00765 
00766         cp.opcode = PCMD_INQUIRY;
00767         cp.alloc_len = min(obuf_size, 0xff); /* Allocation length */
00768 
00769         rc = ata_cmd_packet(0, &cp, sizeof(cp), obuf, obuf_size);
00770         if (rc != EOK)
00771                 return rc;
00772 
00773         return EOK;
00774 }
00775 
00789 static int ata_pcmd_read_12(int dev_idx, uint64_t ba, size_t cnt,
00790     void *obuf, size_t obuf_size)
00791 {
00792         ata_pcmd_read_12_t cp;
00793         int rc;
00794 
00795         if (ba > UINT32_MAX)
00796                 return EINVAL;
00797 
00798         memset(&cp, 0, sizeof(cp));
00799 
00800         cp.opcode = PCMD_READ_12;
00801         cp.ba = host2uint32_t_be(ba);
00802         cp.nblocks = host2uint32_t_be(cnt);
00803 
00804         rc = ata_cmd_packet(0, &cp, sizeof(cp), obuf, obuf_size);
00805         if (rc != EOK)
00806                 return rc;
00807 
00808         return EOK;
00809 }
00810 
00820 static int ata_rcmd_read(int disk_id, uint64_t ba, size_t blk_cnt,
00821     void *buf)
00822 {
00823         size_t i;
00824         uint16_t data;
00825         uint8_t status;
00826         uint8_t drv_head;
00827         disk_t *d;
00828         block_coord_t bc;
00829 
00830         d = &disk[disk_id];
00831         
00832         /* Silence warning. */
00833         memset(&bc, 0, sizeof(bc));
00834 
00835         /* Compute block coordinates. */
00836         if (coord_calc(d, ba, &bc) != EOK)
00837                 return EINVAL;
00838 
00839         /* New value for Drive/Head register */
00840         drv_head =
00841             ((disk_id != 0) ? DHR_DRV : 0) |
00842             ((d->amode != am_chs) ? DHR_LBA : 0) |
00843             (bc.h & 0x0f);
00844 
00845         fibril_mutex_lock(&d->lock);
00846 
00847         /* Program a Read Sectors operation. */
00848 
00849         if (wait_status(0, ~SR_BSY, NULL, TIMEOUT_BSY) != EOK) {
00850                 fibril_mutex_unlock(&d->lock);
00851                 return EIO;
00852         }
00853 
00854         pio_write_8(&cmd->drive_head, drv_head);
00855 
00856         if (wait_status(SR_DRDY, ~SR_BSY, NULL, TIMEOUT_DRDY) != EOK) {
00857                 fibril_mutex_unlock(&d->lock);
00858                 return EIO;
00859         }
00860 
00861         /* Program block coordinates into the device. */
00862         coord_sc_program(&bc, 1);
00863 
00864         pio_write_8(&cmd->command, d->amode == am_lba48 ?
00865             CMD_READ_SECTORS_EXT : CMD_READ_SECTORS);
00866 
00867         if (wait_status(0, ~SR_BSY, &status, TIMEOUT_BSY) != EOK) {
00868                 fibril_mutex_unlock(&d->lock);
00869                 return EIO;
00870         }
00871 
00872         if ((status & SR_DRQ) != 0) {
00873                 /* Read data from the device buffer. */
00874 
00875                 for (i = 0; i < disk[disk_id].block_size / 2; i++) {
00876                         data = pio_read_16(&cmd->data_port);
00877                         ((uint16_t *) buf)[i] = data;
00878                 }
00879         }
00880 
00881         if ((status & SR_ERR) != 0)
00882                 return EIO;
00883 
00884         fibril_mutex_unlock(&d->lock);
00885         return EOK;
00886 }
00887 
00897 static int ata_rcmd_write(int disk_id, uint64_t ba, size_t cnt,
00898     const void *buf)
00899 {
00900         size_t i;
00901         uint8_t status;
00902         uint8_t drv_head;
00903         disk_t *d;
00904         block_coord_t bc;
00905 
00906         d = &disk[disk_id];
00907         
00908         /* Silence warning. */
00909         memset(&bc, 0, sizeof(bc));
00910 
00911         /* Compute block coordinates. */
00912         if (coord_calc(d, ba, &bc) != EOK)
00913                 return EINVAL;
00914 
00915         /* New value for Drive/Head register */
00916         drv_head =
00917             ((disk_id != 0) ? DHR_DRV : 0) |
00918             ((d->amode != am_chs) ? DHR_LBA : 0) |
00919             (bc.h & 0x0f);
00920 
00921         fibril_mutex_lock(&d->lock);
00922 
00923         /* Program a Write Sectors operation. */
00924 
00925         if (wait_status(0, ~SR_BSY, NULL, TIMEOUT_BSY) != EOK) {
00926                 fibril_mutex_unlock(&d->lock);
00927                 return EIO;
00928         }
00929 
00930         pio_write_8(&cmd->drive_head, drv_head);
00931 
00932         if (wait_status(SR_DRDY, ~SR_BSY, NULL, TIMEOUT_DRDY) != EOK) {
00933                 fibril_mutex_unlock(&d->lock);
00934                 return EIO;
00935         }
00936 
00937         /* Program block coordinates into the device. */
00938         coord_sc_program(&bc, 1);
00939 
00940         pio_write_8(&cmd->command, d->amode == am_lba48 ?
00941             CMD_WRITE_SECTORS_EXT : CMD_WRITE_SECTORS);
00942 
00943         if (wait_status(0, ~SR_BSY, &status, TIMEOUT_BSY) != EOK) {
00944                 fibril_mutex_unlock(&d->lock);
00945                 return EIO;
00946         }
00947 
00948         if ((status & SR_DRQ) != 0) {
00949                 /* Write data to the device buffer. */
00950 
00951                 for (i = 0; i < disk[disk_id].block_size / 2; i++) {
00952                         pio_write_16(&cmd->data_port, ((uint16_t *) buf)[i]);
00953                 }
00954         }
00955 
00956         fibril_mutex_unlock(&d->lock);
00957 
00958         if (status & SR_ERR)
00959                 return EIO;
00960 
00961         return EOK;
00962 }
00963 
00972 static int coord_calc(disk_t *d, uint64_t ba, block_coord_t *bc)
00973 {
00974         uint64_t c;
00975         uint64_t idx;
00976 
00977         /* Check device bounds. */
00978         if (ba >= d->blocks)
00979                 return EINVAL;
00980 
00981         bc->amode = d->amode;
00982 
00983         switch (d->amode) {
00984         case am_chs:
00985                 /* Compute CHS coordinates. */
00986                 c = ba / (d->geom.heads * d->geom.sectors);
00987                 idx = ba % (d->geom.heads * d->geom.sectors);
00988 
00989                 bc->cyl_lo = c & 0xff;
00990                 bc->cyl_hi = (c >> 8) & 0xff;
00991                 bc->h      = (idx / d->geom.sectors) & 0x0f;
00992                 bc->sector = (1 + (idx % d->geom.sectors)) & 0xff;
00993                 break;
00994 
00995         case am_lba28:
00996                 /* Compute LBA-28 coordinates. */
00997                 bc->c0 = ba & 0xff;             /* bits 0-7 */
00998                 bc->c1 = (ba >> 8) & 0xff;      /* bits 8-15 */
00999                 bc->c2 = (ba >> 16) & 0xff;     /* bits 16-23 */
01000                 bc->h  = (ba >> 24) & 0x0f;     /* bits 24-27 */
01001                 break;
01002 
01003         case am_lba48:
01004                 /* Compute LBA-48 coordinates. */
01005                 bc->c0 = ba & 0xff;             /* bits 0-7 */
01006                 bc->c1 = (ba >> 8) & 0xff;      /* bits 8-15 */
01007                 bc->c2 = (ba >> 16) & 0xff;     /* bits 16-23 */
01008                 bc->c3 = (ba >> 24) & 0xff;     /* bits 24-31 */
01009                 bc->c4 = (ba >> 32) & 0xff;     /* bits 32-39 */
01010                 bc->c5 = (ba >> 40) & 0xff;     /* bits 40-47 */
01011                 bc->h  = 0;
01012                 break;
01013         }
01014 
01015         return EOK;
01016 }
01017 
01022 static void coord_sc_program(const block_coord_t *bc, uint16_t scnt)
01023 {
01024         if (bc->amode == am_lba48) {
01025                 /* Write high-order bits. */
01026                 pio_write_8(&cmd->sector_count, scnt >> 8);
01027                 pio_write_8(&cmd->sector_number, bc->c3);
01028                 pio_write_8(&cmd->cylinder_low, bc->c4);
01029                 pio_write_8(&cmd->cylinder_high, bc->c5);
01030         }
01031 
01032         /* Write low-order bits. */
01033         pio_write_8(&cmd->sector_count, scnt & 0x00ff);
01034         pio_write_8(&cmd->sector_number, bc->c0);
01035         pio_write_8(&cmd->cylinder_low, bc->c1);
01036         pio_write_8(&cmd->cylinder_high, bc->c2);
01037 }
01038 
01051 static int wait_status(unsigned set, unsigned n_reset, uint8_t *pstatus,
01052     unsigned timeout)
01053 {
01054         uint8_t status;
01055         int cnt;
01056 
01057         status = pio_read_8(&cmd->status);
01058 
01059         /*
01060          * This is crude, yet simple. First try with 1us delays
01061          * (most likely the device will respond very fast). If not,
01062          * start trying every 10 ms.
01063          */
01064 
01065         cnt = 100;
01066         while ((status & ~n_reset) != 0 || (status & set) != set) {
01067                 async_usleep(1);
01068                 --cnt;
01069                 if (cnt <= 0) break;
01070 
01071                 status = pio_read_8(&cmd->status);
01072         }
01073 
01074         cnt = timeout;
01075         while ((status & ~n_reset) != 0 || (status & set) != set) {
01076                 async_usleep(10000);
01077                 --cnt;
01078                 if (cnt <= 0) break;
01079 
01080                 status = pio_read_8(&cmd->status);
01081         }
01082 
01083         if (pstatus)
01084                 *pstatus = status;
01085 
01086         if (cnt == 0)
01087                 return EIO;
01088 
01089         return EOK;
01090 }
01091 

Generated on Thu Jun 2 07:45:49 2011 for HelenOS/USB by  doxygen 1.4.7