source: mainline/uspace/srv/bd/ata_bd/ata_bd.c@ 88743b5

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 88743b5 was 88743b5, checked in by Jiri Svoboda <jiri@…>, 15 years ago

Implement 'Packet' command (for data-in packet-based commands). Implement Inquiry as an example packet command.

  • Property mode set to 100644
File size: 23.6 KB
Line 
1/*
2 * Copyright (c) 2009 Jiri Svoboda
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup bd
30 * @{
31 */
32
33/**
34 * @file
35 * @brief ATA disk driver
36 *
37 * This driver supports CHS, 28-bit and 48-bit LBA addressing. It only uses
38 * PIO transfers. There is no support DMA, the PACKET feature set or any other
39 * fancy features such as S.M.A.R.T, removable devices, etc.
40 *
41 * This driver is based on the ATA-1, ATA-2, ATA-3 and ATA/ATAPI-4 through 7
42 * standards, as published by the ANSI, NCITS and INCITS standards bodies,
43 * which are freely available. This driver contains no vendor-specific
44 * code at this moment.
45 *
46 * The driver services a single controller which can have up to two disks
47 * attached.
48 */
49
50#include <stdio.h>
51#include <libarch/ddi.h>
52#include <ddi.h>
53#include <ipc/ipc.h>
54#include <ipc/bd.h>
55#include <async.h>
56#include <as.h>
57#include <fibril_synch.h>
58#include <str.h>
59#include <devmap.h>
60#include <sys/types.h>
61#include <inttypes.h>
62#include <errno.h>
63#include <bool.h>
64#include <task.h>
65#include <macros.h>
66
67#include "ata_hw.h"
68#include "ata_bd.h"
69
70#define NAME "ata_bd"
71#define NAMESPACE "bd"
72
73/** Number of defined legacy controller base addresses. */
74#define LEGACY_CTLS 4
75
76/** Physical block size. Should be always 512. */
77static const size_t block_size = 512;
78
79/** Size of the communication area. */
80static size_t comm_size;
81
82/** I/O base address of the command registers. */
83static uintptr_t cmd_physical;
84/** I/O base address of the control registers. */
85static uintptr_t ctl_physical;
86
87/** I/O base addresses for legacy (ISA-compatible) controllers. */
88static ata_base_t legacy_base[LEGACY_CTLS] = {
89 { 0x1f0, 0x3f0 },
90 { 0x170, 0x370 },
91 { 0x1e8, 0x3e8 },
92 { 0x168, 0x368 }
93};
94
95static ata_cmd_t *cmd;
96static ata_ctl_t *ctl;
97
98/** Per-disk state. */
99static disk_t disk[MAX_DISKS];
100
101static void print_syntax(void);
102static int ata_bd_init(void);
103static void ata_bd_connection(ipc_callid_t iid, ipc_call_t *icall);
104static int ata_bd_read_blocks(int disk_id, uint64_t ba, size_t cnt,
105 void *buf);
106static int ata_bd_write_blocks(int disk_id, uint64_t ba, size_t cnt,
107 const void *buf);
108static int ata_bd_read_block(int disk_id, uint64_t ba, size_t cnt,
109 void *buf);
110static int ata_bd_write_block(int disk_id, uint64_t ba, size_t cnt,
111 const void *buf);
112static int disk_init(disk_t *d, int disk_id);
113static int drive_identify(int drive_id, void *buf);
114static int identify_pkt_dev(int dev_idx, void *buf);
115static int ata_cmd_packet(int dev_idx, const void *cpkt, size_t cpkt_size,
116 void *obuf, size_t obuf_size);
117static int ata_pcmd_inquiry(int dev_idx, void *obuf, size_t obuf_size);
118static void disk_print_summary(disk_t *d);
119static int coord_calc(disk_t *d, uint64_t ba, block_coord_t *bc);
120static void coord_sc_program(const block_coord_t *bc, uint16_t scnt);
121static int wait_status(unsigned set, unsigned n_reset, uint8_t *pstatus,
122 unsigned timeout);
123
124int main(int argc, char **argv)
125{
126 char name[16];
127 int i, rc;
128 int n_disks;
129 unsigned ctl_num;
130 char *eptr;
131
132 printf(NAME ": ATA disk driver\n");
133
134 if (argc > 1) {
135 ctl_num = strtoul(argv[1], &eptr, 0);
136 if (*eptr != '\0' || ctl_num == 0 || ctl_num > 4) {
137 printf("Invalid argument.\n");
138 print_syntax();
139 return -1;
140 }
141 } else {
142 ctl_num = 1;
143 }
144
145 cmd_physical = legacy_base[ctl_num - 1].cmd;
146 ctl_physical = legacy_base[ctl_num - 1].ctl;
147
148 printf("I/O address %p/%p\n", (void *) cmd_physical,
149 (void *) ctl_physical);
150
151 if (ata_bd_init() != EOK)
152 return -1;
153
154 for (i = 0; i < MAX_DISKS; i++) {
155 printf("Identify drive %d... ", i);
156 fflush(stdout);
157
158 rc = disk_init(&disk[i], i);
159
160 if (rc == EOK) {
161 disk_print_summary(&disk[i]);
162 } else {
163 printf("Not found.\n");
164 }
165 }
166
167 n_disks = 0;
168
169 for (i = 0; i < MAX_DISKS; i++) {
170 /* Skip unattached drives. */
171 if (disk[i].present == false)
172 continue;
173
174 snprintf(name, 16, "%s/ata%udisk%d", NAMESPACE, ctl_num, i);
175 rc = devmap_device_register(name, &disk[i].devmap_handle);
176 if (rc != EOK) {
177 printf(NAME ": Unable to register device %s.\n", name);
178 return rc;
179 }
180 ++n_disks;
181 }
182
183 if (n_disks == 0) {
184 printf("No disks detected.\n");
185 return -1;
186 }
187
188 printf(NAME ": Accepting connections\n");
189 task_retval(0);
190 async_manager();
191
192 /* Not reached */
193 return 0;
194}
195
196
197static void print_syntax(void)
198{
199 printf("Syntax: " NAME " <controller_number>\n");
200 printf("Controller number = 1..4\n");
201}
202
203/** Print one-line device summary. */
204static void disk_print_summary(disk_t *d)
205{
206 uint64_t mbytes;
207
208 printf("%s: ", d->model);
209
210 if (d->dev_type == ata_reg_dev) {
211 switch (d->amode) {
212 case am_chs:
213 printf("CHS %u cylinders, %u heads, %u sectors",
214 disk->geom.cylinders, disk->geom.heads,
215 disk->geom.sectors);
216 break;
217 case am_lba28:
218 printf("LBA-28");
219 break;
220 case am_lba48:
221 printf("LBA-48");
222 break;
223 }
224 } else {
225 printf("PACKET");
226 }
227
228 printf(" %" PRIu64 " blocks", d->blocks);
229
230 mbytes = d->blocks / (2 * 1024);
231 if (mbytes > 0)
232 printf(" %" PRIu64 " MB.", mbytes);
233
234 printf("\n");
235}
236
237/** Register driver and enable device I/O. */
238static int ata_bd_init(void)
239{
240 void *vaddr;
241 int rc;
242
243 rc = devmap_driver_register(NAME, ata_bd_connection);
244 if (rc < 0) {
245 printf(NAME ": Unable to register driver.\n");
246 return rc;
247 }
248
249 rc = pio_enable((void *) cmd_physical, sizeof(ata_cmd_t), &vaddr);
250 if (rc != EOK) {
251 printf(NAME ": Could not initialize device I/O space.\n");
252 return rc;
253 }
254
255 cmd = vaddr;
256
257 rc = pio_enable((void *) ctl_physical, sizeof(ata_ctl_t), &vaddr);
258 if (rc != EOK) {
259 printf(NAME ": Could not initialize device I/O space.\n");
260 return rc;
261 }
262
263 ctl = vaddr;
264
265
266 return EOK;
267}
268
269/** Block device connection handler */
270static void ata_bd_connection(ipc_callid_t iid, ipc_call_t *icall)
271{
272 void *fs_va = NULL;
273 ipc_callid_t callid;
274 ipc_call_t call;
275 sysarg_t method;
276 devmap_handle_t dh;
277 int flags;
278 int retval;
279 uint64_t ba;
280 size_t cnt;
281 int disk_id, i;
282
283 /* Get the device handle. */
284 dh = IPC_GET_ARG1(*icall);
285
286 /* Determine which disk device is the client connecting to. */
287 disk_id = -1;
288 for (i = 0; i < MAX_DISKS; i++)
289 if (disk[i].devmap_handle == dh)
290 disk_id = i;
291
292 if (disk_id < 0 || disk[disk_id].present == false) {
293 ipc_answer_0(iid, EINVAL);
294 return;
295 }
296
297 /* Answer the IPC_M_CONNECT_ME_TO call. */
298 ipc_answer_0(iid, EOK);
299
300 if (!async_share_out_receive(&callid, &comm_size, &flags)) {
301 ipc_answer_0(callid, EHANGUP);
302 return;
303 }
304
305 fs_va = as_get_mappable_page(comm_size);
306 if (fs_va == NULL) {
307 ipc_answer_0(callid, EHANGUP);
308 return;
309 }
310
311 (void) async_share_out_finalize(callid, fs_va);
312
313 while (1) {
314 callid = async_get_call(&call);
315 method = IPC_GET_IMETHOD(call);
316 switch (method) {
317 case IPC_M_PHONE_HUNGUP:
318 /* The other side has hung up. */
319 ipc_answer_0(callid, EOK);
320 return;
321 case BD_READ_BLOCKS:
322 ba = MERGE_LOUP32(IPC_GET_ARG1(call),
323 IPC_GET_ARG2(call));
324 cnt = IPC_GET_ARG3(call);
325 if (cnt * block_size > comm_size) {
326 retval = ELIMIT;
327 break;
328 }
329 retval = ata_bd_read_blocks(disk_id, ba, cnt, fs_va);
330 break;
331 case BD_WRITE_BLOCKS:
332 ba = MERGE_LOUP32(IPC_GET_ARG1(call),
333 IPC_GET_ARG2(call));
334 cnt = IPC_GET_ARG3(call);
335 if (cnt * block_size > comm_size) {
336 retval = ELIMIT;
337 break;
338 }
339 retval = ata_bd_write_blocks(disk_id, ba, cnt, fs_va);
340 break;
341 case BD_GET_BLOCK_SIZE:
342 ipc_answer_1(callid, EOK, block_size);
343 continue;
344 case BD_GET_NUM_BLOCKS:
345 ipc_answer_2(callid, EOK, LOWER32(disk[disk_id].blocks),
346 UPPER32(disk[disk_id].blocks));
347 continue;
348 default:
349 retval = EINVAL;
350 break;
351 }
352 ipc_answer_0(callid, retval);
353 }
354}
355
356/** Initialize a disk.
357 *
358 * Probes for a disk, determines its parameters and initializes
359 * the disk structure.
360 */
361static int disk_init(disk_t *d, int disk_id)
362{
363 identify_data_t idata;
364 uint8_t model[40];
365 uint8_t inq_buf[36];
366 uint16_t w;
367 uint8_t c;
368 size_t pos, len;
369 int rc;
370 unsigned i;
371
372 d->present = false;
373 fibril_mutex_initialize(&d->lock);
374
375 /* Try identify command. */
376 rc = drive_identify(disk_id, &idata);
377 if (rc == EOK) {
378 /* Success. It's a register (non-packet) device. */
379 printf("ATA register-only device found.\n");
380 d->dev_type = ata_reg_dev;
381 } else if (rc == EIO) {
382 /*
383 * There is something, but not a register device.
384 * It could be a packet device.
385 */
386 rc = identify_pkt_dev(disk_id, &idata);
387 if (rc == EOK) {
388 /* We have a packet device. */
389 d->dev_type = ata_pkt_dev;
390 } else {
391 /* Nope. Something's there, but not recognized. */
392 return EIO;
393 }
394 } else {
395 /* Operation timed out. That means there is no device there. */
396 return EIO;
397 }
398
399 printf("device caps: 0x%04x\n", idata.caps);
400 if (d->dev_type == ata_pkt_dev) {
401 /* Packet device */
402 d->amode = 0;
403
404 d->geom.cylinders = 0;
405 d->geom.heads = 0;
406 d->geom.sectors = 0;
407
408 d->blocks = 0;
409 } else if ((idata.caps & rd_cap_lba) == 0) {
410 /* Device only supports CHS addressing. */
411 d->amode = am_chs;
412
413 d->geom.cylinders = idata.cylinders;
414 d->geom.heads = idata.heads;
415 d->geom.sectors = idata.sectors;
416
417 d->blocks = d->geom.cylinders * d->geom.heads * d->geom.sectors;
418 } else if ((idata.cmd_set1 & cs1_addr48) == 0) {
419 /* Device only supports LBA-28 addressing. */
420 d->amode = am_lba28;
421
422 d->geom.cylinders = 0;
423 d->geom.heads = 0;
424 d->geom.sectors = 0;
425
426 d->blocks =
427 (uint32_t) idata.total_lba28_0 |
428 ((uint32_t) idata.total_lba28_1 << 16);
429 } else {
430 /* Device supports LBA-48 addressing. */
431 d->amode = am_lba48;
432
433 d->geom.cylinders = 0;
434 d->geom.heads = 0;
435 d->geom.sectors = 0;
436
437 d->blocks =
438 (uint64_t) idata.total_lba48_0 |
439 ((uint64_t) idata.total_lba48_1 << 16) |
440 ((uint64_t) idata.total_lba48_2 << 32) |
441 ((uint64_t) idata.total_lba48_3 << 48);
442 }
443
444 /*
445 * Convert model name to string representation.
446 */
447 for (i = 0; i < 20; i++) {
448 w = idata.model_name[i];
449 model[2 * i] = w >> 8;
450 model[2 * i + 1] = w & 0x00ff;
451 }
452
453 len = 40;
454 while (len > 0 && model[len - 1] == 0x20)
455 --len;
456
457 pos = 0;
458 for (i = 0; i < len; ++i) {
459 c = model[i];
460 if (c >= 0x80) c = '?';
461
462 chr_encode(c, d->model, &pos, 40);
463 }
464 d->model[pos] = '\0';
465
466 if (d->dev_type == ata_pkt_dev) {
467 /* Send inquiry. */
468 rc = ata_pcmd_inquiry(0, inq_buf, 36);
469 if (rc != EOK) {
470 printf("Device inquiry failed.\n");
471 d->present = false;
472 return EIO;
473 }
474
475 /* Check device type. */
476 if ((inq_buf[0] & 0x1f) != 0x05)
477 printf("Warning: Peripheral device type is not CD-ROM.\n");
478 }
479
480 d->present = true;
481 return EOK;
482}
483
484/** Read multiple blocks from the device. */
485static int ata_bd_read_blocks(int disk_id, uint64_t ba, size_t cnt,
486 void *buf) {
487
488 int rc;
489
490 while (cnt > 0) {
491 rc = ata_bd_read_block(disk_id, ba, 1, buf);
492 if (rc != EOK)
493 return rc;
494
495 ++ba;
496 --cnt;
497 buf += block_size;
498 }
499
500 return EOK;
501}
502
503/** Write multiple blocks to the device. */
504static int ata_bd_write_blocks(int disk_id, uint64_t ba, size_t cnt,
505 const void *buf) {
506
507 int rc;
508
509 while (cnt > 0) {
510 rc = ata_bd_write_block(disk_id, ba, 1, buf);
511 if (rc != EOK)
512 return rc;
513
514 ++ba;
515 --cnt;
516 buf += block_size;
517 }
518
519 return EOK;
520}
521
522/** Issue IDENTIFY command.
523 *
524 * Reads @c identify data into the provided buffer. This is used to detect
525 * whether an ATA device is present and if so, to determine its parameters.
526 *
527 * @param disk_id Device ID, 0 or 1.
528 * @param buf Pointer to a 512-byte buffer.
529 *
530 * @return ETIMEOUT on timeout (this can mean the device is
531 * not present). EIO if device responds with error.
532 */
533static int drive_identify(int disk_id, void *buf)
534{
535 uint16_t data;
536 uint8_t status;
537 uint8_t drv_head;
538 size_t i;
539
540 drv_head = ((disk_id != 0) ? DHR_DRV : 0);
541
542 if (wait_status(0, ~SR_BSY, NULL, TIMEOUT_PROBE) != EOK)
543 return ETIMEOUT;
544
545 pio_write_8(&cmd->drive_head, drv_head);
546
547 /*
548 * This is where we would most likely expect a non-existing device to
549 * show up by not setting SR_DRDY.
550 */
551 if (wait_status(SR_DRDY, ~SR_BSY, NULL, TIMEOUT_PROBE) != EOK)
552 return ETIMEOUT;
553
554 pio_write_8(&cmd->command, CMD_IDENTIFY_DRIVE);
555
556 if (wait_status(0, ~SR_BSY, &status, TIMEOUT_PROBE) != EOK)
557 return ETIMEOUT;
558
559 /* Read data from the disk buffer. */
560
561 if ((status & SR_DRQ) != 0) {
562 for (i = 0; i < block_size / 2; i++) {
563 data = pio_read_16(&cmd->data_port);
564 ((uint16_t *) buf)[i] = data;
565 }
566 }
567
568 if ((status & SR_ERR) != 0) {
569 return EIO;
570 }
571
572 return EOK;
573}
574
575/** Issue Identify Packet Device command.
576 *
577 * Reads @c identify data into the provided buffer. This is used to detect
578 * whether an ATAPI device is present and if so, to determine its parameters.
579 *
580 * @param dev_idx Device index, 0 or 1.
581 * @param buf Pointer to a 512-byte buffer.
582 */
583static int identify_pkt_dev(int dev_idx, void *buf)
584{
585 uint16_t data;
586 uint8_t status;
587 uint8_t drv_head;
588 size_t i;
589
590 drv_head = ((dev_idx != 0) ? DHR_DRV : 0);
591
592 if (wait_status(0, ~SR_BSY, NULL, TIMEOUT_PROBE) != EOK)
593 return EIO;
594
595 pio_write_8(&cmd->drive_head, drv_head);
596
597 /* For ATAPI commands we do not need to wait for DRDY. */
598 if (wait_status(0, ~SR_BSY, NULL, TIMEOUT_PROBE) != EOK)
599 return EIO;
600
601 pio_write_8(&cmd->command, CMD_IDENTIFY_PKT_DEV);
602
603 if (wait_status(0, ~SR_BSY, &status, TIMEOUT_BSY) != EOK)
604 return EIO;
605
606 /* Read data from the device buffer. */
607
608 if ((status & SR_DRQ) != 0) {
609 for (i = 0; i < block_size / 2; i++) {
610 data = pio_read_16(&cmd->data_port);
611 ((uint16_t *) buf)[i] = data;
612 }
613 }
614
615 if ((status & SR_ERR) != 0)
616 return EIO;
617
618 return EOK;
619}
620
621/** Issue packet command (i. e. write a command packet to the device).
622 *
623 * Only data-in commands are supported (e.g. inquiry, read).
624 *
625 * @param dev_idx Device index (0 or 1)
626 * @param obuf Buffer for storing data read from device
627 * @param obuf_size Size of obuf in bytes
628 *
629 * @return EOK on success, EIO on error.
630 */
631static int ata_cmd_packet(int dev_idx, const void *cpkt, size_t cpkt_size,
632 void *obuf, size_t obuf_size)
633{
634 size_t i;
635 uint8_t status;
636 uint8_t drv_head;
637 disk_t *d;
638 size_t data_size;
639 uint16_t val;
640
641 d = &disk[dev_idx];
642 fibril_mutex_lock(&d->lock);
643
644 /* New value for Drive/Head register */
645 drv_head =
646 ((dev_idx != 0) ? DHR_DRV : 0);
647
648 if (wait_status(0, ~SR_BSY, NULL, TIMEOUT_PROBE) != EOK) {
649 fibril_mutex_unlock(&d->lock);
650 return EIO;
651 }
652
653 pio_write_8(&cmd->drive_head, drv_head);
654
655 if (wait_status(0, ~(SR_BSY|SR_DRQ), NULL, TIMEOUT_BSY) != EOK) {
656 fibril_mutex_unlock(&d->lock);
657 return EIO;
658 }
659
660 /* Byte count <- max. number of bytes we can read in one transfer. */
661 pio_write_8(&cmd->cylinder_low, 0xfe);
662 pio_write_8(&cmd->cylinder_high, 0xff);
663
664 pio_write_8(&cmd->command, CMD_PACKET);
665
666 if (wait_status(SR_DRQ, ~SR_BSY, &status, TIMEOUT_BSY) != EOK) {
667 fibril_mutex_unlock(&d->lock);
668 return EIO;
669 }
670
671 /* Write command packet. */
672 for (i = 0; i < (cpkt_size + 1) / 2; i++)
673 pio_write_16(&cmd->data_port, ((uint16_t *) cpkt)[i]);
674
675 if (wait_status(0, ~SR_BSY, &status, TIMEOUT_BSY) != EOK) {
676 fibril_mutex_unlock(&d->lock);
677 return EIO;
678 }
679
680 if ((status & SR_DRQ) == 0) {
681 fibril_mutex_unlock(&d->lock);
682 return EIO;
683 }
684
685 /* Read byte count. */
686 data_size = (uint16_t) pio_read_8(&cmd->cylinder_low) +
687 ((uint16_t) pio_read_8(&cmd->cylinder_high) << 8);
688
689 /* Check whether data fits into output buffer. */
690 if (data_size > obuf_size) {
691 /* Output buffer is too small to store data. */
692 fibril_mutex_unlock(&d->lock);
693 return EIO;
694 }
695
696 /* Read data from the device buffer. */
697 for (i = 0; i < (data_size + 1) / 2; i++) {
698 val = pio_read_16(&cmd->data_port);
699 ((uint16_t *) obuf)[i] = val;
700 }
701
702 if (status & SR_ERR) {
703 fibril_mutex_unlock(&d->lock);
704 return EIO;
705 }
706
707 fibril_mutex_unlock(&d->lock);
708
709 return EOK;
710}
711
712/** Send ATAPI Inquiry.
713 *
714 * @param dev_idx Device index (0 or 1)
715 * @param obuf Buffer for storing inquiry data read from device
716 * @param obuf_size Size of obuf in bytes
717 *
718 * @return EOK on success, EIO on error.
719 */
720static int ata_pcmd_inquiry(int dev_idx, void *obuf, size_t obuf_size)
721{
722 uint8_t cp[12];
723 int rc;
724
725 memset(cp, 0, 12);
726 cp[0] = 0x12; /* Inquiry */
727 cp[4] = min(obuf_size, 0xff); /* Allocation length */
728
729 rc = ata_cmd_packet(0, cp, 12, obuf, obuf_size);
730 if (rc != EOK)
731 return rc;
732
733 return EOK;
734}
735
736/** Read a physical from the device.
737 *
738 * @param disk_id Device index (0 or 1)
739 * @param ba Address the first block.
740 * @param cnt Number of blocks to transfer.
741 * @param buf Buffer for holding the data.
742 *
743 * @return EOK on success, EIO on error.
744 */
745static int ata_bd_read_block(int disk_id, uint64_t ba, size_t blk_cnt,
746 void *buf)
747{
748 size_t i;
749 uint16_t data;
750 uint8_t status;
751 uint8_t drv_head;
752 disk_t *d;
753 block_coord_t bc;
754
755 d = &disk[disk_id];
756
757 /* Silence warning. */
758 memset(&bc, 0, sizeof(bc));
759
760 /* Compute block coordinates. */
761 if (coord_calc(d, ba, &bc) != EOK)
762 return EINVAL;
763
764 /* New value for Drive/Head register */
765 drv_head =
766 ((disk_id != 0) ? DHR_DRV : 0) |
767 ((d->amode != am_chs) ? DHR_LBA : 0) |
768 (bc.h & 0x0f);
769
770 fibril_mutex_lock(&d->lock);
771
772 /* Program a Read Sectors operation. */
773
774 if (wait_status(0, ~SR_BSY, NULL, TIMEOUT_BSY) != EOK) {
775 fibril_mutex_unlock(&d->lock);
776 return EIO;
777 }
778
779 pio_write_8(&cmd->drive_head, drv_head);
780
781 if (wait_status(SR_DRDY, ~SR_BSY, NULL, TIMEOUT_DRDY) != EOK) {
782 fibril_mutex_unlock(&d->lock);
783 return EIO;
784 }
785
786 /* Program block coordinates into the device. */
787 coord_sc_program(&bc, 1);
788
789 pio_write_8(&cmd->command, d->amode == am_lba48 ?
790 CMD_READ_SECTORS_EXT : CMD_READ_SECTORS);
791
792 if (wait_status(0, ~SR_BSY, &status, TIMEOUT_BSY) != EOK) {
793 fibril_mutex_unlock(&d->lock);
794 return EIO;
795 }
796
797 if ((status & SR_DRQ) != 0) {
798 /* Read data from the device buffer. */
799
800 for (i = 0; i < block_size / 2; i++) {
801 data = pio_read_16(&cmd->data_port);
802 ((uint16_t *) buf)[i] = data;
803 }
804 }
805
806 if ((status & SR_ERR) != 0)
807 return EIO;
808
809 fibril_mutex_unlock(&d->lock);
810 return EOK;
811}
812
813/** Write a physical block to the device.
814 *
815 * @param disk_id Device index (0 or 1)
816 * @param ba Address of the first block.
817 * @param cnt Number of blocks to transfer.
818 * @param buf Buffer holding the data to write.
819 *
820 * @return EOK on success, EIO on error.
821 */
822static int ata_bd_write_block(int disk_id, uint64_t ba, size_t cnt,
823 const void *buf)
824{
825 size_t i;
826 uint8_t status;
827 uint8_t drv_head;
828 disk_t *d;
829 block_coord_t bc;
830
831 d = &disk[disk_id];
832
833 /* Silence warning. */
834 memset(&bc, 0, sizeof(bc));
835
836 /* Compute block coordinates. */
837 if (coord_calc(d, ba, &bc) != EOK)
838 return EINVAL;
839
840 /* New value for Drive/Head register */
841 drv_head =
842 ((disk_id != 0) ? DHR_DRV : 0) |
843 ((d->amode != am_chs) ? DHR_LBA : 0) |
844 (bc.h & 0x0f);
845
846 fibril_mutex_lock(&d->lock);
847
848 /* Program a Write Sectors operation. */
849
850 if (wait_status(0, ~SR_BSY, NULL, TIMEOUT_BSY) != EOK) {
851 fibril_mutex_unlock(&d->lock);
852 return EIO;
853 }
854
855 pio_write_8(&cmd->drive_head, drv_head);
856
857 if (wait_status(SR_DRDY, ~SR_BSY, NULL, TIMEOUT_DRDY) != EOK) {
858 fibril_mutex_unlock(&d->lock);
859 return EIO;
860 }
861
862 /* Program block coordinates into the device. */
863 coord_sc_program(&bc, 1);
864
865 pio_write_8(&cmd->command, d->amode == am_lba48 ?
866 CMD_WRITE_SECTORS_EXT : CMD_WRITE_SECTORS);
867
868 if (wait_status(0, ~SR_BSY, &status, TIMEOUT_BSY) != EOK) {
869 fibril_mutex_unlock(&d->lock);
870 return EIO;
871 }
872
873 if ((status & SR_DRQ) != 0) {
874 /* Write data to the device buffer. */
875
876 for (i = 0; i < block_size / 2; i++) {
877 pio_write_16(&cmd->data_port, ((uint16_t *) buf)[i]);
878 }
879 }
880
881 fibril_mutex_unlock(&d->lock);
882
883 if (status & SR_ERR)
884 return EIO;
885
886 return EOK;
887}
888
889/** Calculate block coordinates.
890 *
891 * Calculates block coordinates in the best coordinate system supported
892 * by the device. These can be later programmed into the device using
893 * @c coord_sc_program().
894 *
895 * @return EOK on success or EINVAL if block index is past end of device.
896 */
897static int coord_calc(disk_t *d, uint64_t ba, block_coord_t *bc)
898{
899 uint64_t c;
900 uint64_t idx;
901
902 /* Check device bounds. */
903 if (ba >= d->blocks)
904 return EINVAL;
905
906 bc->amode = d->amode;
907
908 switch (d->amode) {
909 case am_chs:
910 /* Compute CHS coordinates. */
911 c = ba / (d->geom.heads * d->geom.sectors);
912 idx = ba % (d->geom.heads * d->geom.sectors);
913
914 bc->cyl_lo = c & 0xff;
915 bc->cyl_hi = (c >> 8) & 0xff;
916 bc->h = (idx / d->geom.sectors) & 0x0f;
917 bc->sector = (1 + (idx % d->geom.sectors)) & 0xff;
918 break;
919
920 case am_lba28:
921 /* Compute LBA-28 coordinates. */
922 bc->c0 = ba & 0xff; /* bits 0-7 */
923 bc->c1 = (ba >> 8) & 0xff; /* bits 8-15 */
924 bc->c2 = (ba >> 16) & 0xff; /* bits 16-23 */
925 bc->h = (ba >> 24) & 0x0f; /* bits 24-27 */
926 break;
927
928 case am_lba48:
929 /* Compute LBA-48 coordinates. */
930 bc->c0 = ba & 0xff; /* bits 0-7 */
931 bc->c1 = (ba >> 8) & 0xff; /* bits 8-15 */
932 bc->c2 = (ba >> 16) & 0xff; /* bits 16-23 */
933 bc->c3 = (ba >> 24) & 0xff; /* bits 24-31 */
934 bc->c4 = (ba >> 32) & 0xff; /* bits 32-39 */
935 bc->c5 = (ba >> 40) & 0xff; /* bits 40-47 */
936 bc->h = 0;
937 break;
938 }
939
940 return EOK;
941}
942
943/** Program block coordinates and sector count into ATA registers.
944 *
945 * Note that bc->h must be programmed separately into the device/head register.
946 */
947static void coord_sc_program(const block_coord_t *bc, uint16_t scnt)
948{
949 if (bc->amode == am_lba48) {
950 /* Write high-order bits. */
951 pio_write_8(&cmd->sector_count, scnt >> 8);
952 pio_write_8(&cmd->sector_number, bc->c3);
953 pio_write_8(&cmd->cylinder_low, bc->c4);
954 pio_write_8(&cmd->cylinder_high, bc->c5);
955 }
956
957 /* Write low-order bits. */
958 pio_write_8(&cmd->sector_count, scnt & 0x00ff);
959 pio_write_8(&cmd->sector_number, bc->c0);
960 pio_write_8(&cmd->cylinder_low, bc->c1);
961 pio_write_8(&cmd->cylinder_high, bc->c2);
962}
963
964/** Wait until some status bits are set and some are reset.
965 *
966 * Example: wait_status(SR_DRDY, ~SR_BSY) waits for SR_DRDY to become
967 * set and SR_BSY to become reset.
968 *
969 * @param set Combination if bits which must be all set.
970 * @param n_reset Negated combination of bits which must be all reset.
971 * @param pstatus Pointer where to store last read status or NULL.
972 * @param timeout Timeout in 10ms units.
973 *
974 * @return EOK on success, EIO on timeout.
975 */
976static int wait_status(unsigned set, unsigned n_reset, uint8_t *pstatus,
977 unsigned timeout)
978{
979 uint8_t status;
980 int cnt;
981
982 status = pio_read_8(&cmd->status);
983
984 /*
985 * This is crude, yet simple. First try with 1us delays
986 * (most likely the device will respond very fast). If not,
987 * start trying every 10 ms.
988 */
989
990 cnt = 100;
991 while ((status & ~n_reset) != 0 || (status & set) != set) {
992 async_usleep(1);
993 --cnt;
994 if (cnt <= 0) break;
995
996 status = pio_read_8(&cmd->status);
997 }
998
999 cnt = timeout;
1000 while ((status & ~n_reset) != 0 || (status & set) != set) {
1001 async_usleep(10000);
1002 --cnt;
1003 if (cnt <= 0) break;
1004
1005 status = pio_read_8(&cmd->status);
1006 }
1007
1008 if (pstatus)
1009 *pstatus = status;
1010
1011 if (cnt == 0)
1012 return EIO;
1013
1014 return EOK;
1015}
1016
1017/**
1018 * @}
1019 */
Note: See TracBrowser for help on using the repository browser.