00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
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
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
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
00290 dh = IPC_GET_ARG1(*icall);
00291
00292
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
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
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
00383 rc = drive_identify(disk_id, &idata);
00384 if (rc == EOK) {
00385
00386 printf("ATA register-only device found.\n");
00387 d->dev_type = ata_reg_dev;
00388 } else if (rc == EIO) {
00389
00390
00391
00392
00393
00394
00395
00396
00397
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
00406 d->dev_type = ata_pkt_dev;
00407 } else {
00408 return EIO;
00409 }
00410 } else {
00411
00412 return EIO;
00413 }
00414 } else {
00415
00416 return EIO;
00417 }
00418
00419 if (d->dev_type == ata_pkt_dev) {
00420
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
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
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
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
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
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
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
00499 d->block_size = 2048;
00500 } else {
00501
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
00582
00583
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
00595
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
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
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
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
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
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
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
00725 data_size = (uint16_t) pio_read_8(&cmd->cylinder_low) +
00726 ((uint16_t) pio_read_8(&cmd->cylinder_high) << 8);
00727
00728
00729 if (data_size > obuf_size) {
00730
00731 fibril_mutex_unlock(&d->lock);
00732 return EIO;
00733 }
00734
00735
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);
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
00833 memset(&bc, 0, sizeof(bc));
00834
00835
00836 if (coord_calc(d, ba, &bc) != EOK)
00837 return EINVAL;
00838
00839
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
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
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
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
00909 memset(&bc, 0, sizeof(bc));
00910
00911
00912 if (coord_calc(d, ba, &bc) != EOK)
00913 return EINVAL;
00914
00915
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
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
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
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
00978 if (ba >= d->blocks)
00979 return EINVAL;
00980
00981 bc->amode = d->amode;
00982
00983 switch (d->amode) {
00984 case am_chs:
00985
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
00997 bc->c0 = ba & 0xff;
00998 bc->c1 = (ba >> 8) & 0xff;
00999 bc->c2 = (ba >> 16) & 0xff;
01000 bc->h = (ba >> 24) & 0x0f;
01001 break;
01002
01003 case am_lba48:
01004
01005 bc->c0 = ba & 0xff;
01006 bc->c1 = (ba >> 8) & 0xff;
01007 bc->c2 = (ba >> 16) & 0xff;
01008 bc->c3 = (ba >> 24) & 0xff;
01009 bc->c4 = (ba >> 32) & 0xff;
01010 bc->c5 = (ba >> 40) & 0xff;
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
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
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
01061
01062
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