source: mainline/uspace/srv/bd/ata_bd/ata_bd.c@ 1a5b252

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

Merge mainline changes.

  • Property mode set to 100644
File size: 25.8 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/bd.h>
54#include <async.h>
55#include <as.h>
56#include <fibril_synch.h>
57#include <stdint.h>
58#include <str.h>
59#include <loc.h>
60#include <sys/types.h>
61#include <inttypes.h>
62#include <errno.h>
63#include <bool.h>
64#include <byteorder.h>
65#include <task.h>
66#include <macros.h>
67
68#include "ata_hw.h"
69#include "ata_bd.h"
70
71#define NAME "ata_bd"
72#define NAMESPACE "bd"
73
74/** Number of defined legacy controller base addresses. */
75#define LEGACY_CTLS 4
76
77/**
78 * Size of data returned from Identify Device or Identify Packet Device
79 * command.
80 */
81static const size_t identify_data_size = 512;
82
83/** I/O base address of the command registers. */
84static uintptr_t cmd_physical;
85/** I/O base address of the control registers. */
86static uintptr_t ctl_physical;
87
88/** I/O base addresses for legacy (ISA-compatible) controllers. */
89static ata_base_t legacy_base[LEGACY_CTLS] = {
90 { 0x1f0, 0x3f0 },
91 { 0x170, 0x370 },
92 { 0x1e8, 0x3e8 },
93 { 0x168, 0x368 }
94};
95
96static ata_cmd_t *cmd;
97static ata_ctl_t *ctl;
98
99/** Per-disk state. */
100static disk_t disk[MAX_DISKS];
101
102static void print_syntax(void);
103static int ata_bd_init(void);
104static void ata_bd_connection(ipc_callid_t iid, ipc_call_t *icall, void *);
105static int ata_bd_read_blocks(int disk_id, uint64_t ba, size_t cnt,
106 void *buf);
107static int ata_bd_write_blocks(int disk_id, uint64_t ba, size_t cnt,
108 const void *buf);
109static int ata_rcmd_read(int disk_id, uint64_t ba, size_t cnt,
110 void *buf);
111static int ata_rcmd_write(int disk_id, uint64_t ba, size_t cnt,
112 const void *buf);
113static int disk_init(disk_t *d, int disk_id);
114static int drive_identify(int drive_id, void *buf);
115static int identify_pkt_dev(int dev_idx, void *buf);
116static int ata_cmd_packet(int dev_idx, const void *cpkt, size_t cpkt_size,
117 void *obuf, size_t obuf_size);
118static int ata_pcmd_inquiry(int dev_idx, void *obuf, size_t obuf_size);
119static int ata_pcmd_read_12(int dev_idx, uint64_t ba, size_t cnt,
120 void *obuf, size_t obuf_size);
121static void disk_print_summary(disk_t *d);
122static int coord_calc(disk_t *d, uint64_t ba, block_coord_t *bc);
123static void coord_sc_program(const block_coord_t *bc, uint16_t scnt);
124static int wait_status(unsigned set, unsigned n_reset, uint8_t *pstatus,
125 unsigned timeout);
126
127int main(int argc, char **argv)
128{
129 char name[16];
130 int i, rc;
131 int n_disks;
132 unsigned ctl_num;
133 char *eptr;
134
135 printf(NAME ": ATA disk driver\n");
136
137 if (argc > 1) {
138 ctl_num = strtoul(argv[1], &eptr, 0);
139 if (*eptr != '\0' || ctl_num == 0 || ctl_num > 4) {
140 printf("Invalid argument.\n");
141 print_syntax();
142 return -1;
143 }
144 } else {
145 ctl_num = 1;
146 }
147
148 cmd_physical = legacy_base[ctl_num - 1].cmd;
149 ctl_physical = legacy_base[ctl_num - 1].ctl;
150
151 printf("I/O address %p/%p\n", (void *) cmd_physical,
152 (void *) ctl_physical);
153
154 if (ata_bd_init() != EOK)
155 return -1;
156
157 for (i = 0; i < MAX_DISKS; i++) {
158 printf("Identify drive %d... ", i);
159 fflush(stdout);
160
161 rc = disk_init(&disk[i], i);
162
163 if (rc == EOK) {
164 disk_print_summary(&disk[i]);
165 } else {
166 printf("Not found.\n");
167 }
168 }
169
170 n_disks = 0;
171
172 for (i = 0; i < MAX_DISKS; i++) {
173 /* Skip unattached drives. */
174 if (disk[i].present == false)
175 continue;
176
177 snprintf(name, 16, "%s/ata%udisk%d", NAMESPACE, ctl_num, i);
178 rc = loc_service_register(name, &disk[i].service_id);
179 if (rc != EOK) {
180 printf(NAME ": Unable to register device %s.\n", name);
181 return rc;
182 }
183 ++n_disks;
184 }
185
186 if (n_disks == 0) {
187 printf("No disks detected.\n");
188 return -1;
189 }
190
191 printf(NAME ": Accepting connections\n");
192 task_retval(0);
193 async_manager();
194
195 /* Not reached */
196 return 0;
197}
198
199
200static void print_syntax(void)
201{
202 printf("Syntax: " NAME " <controller_number>\n");
203 printf("Controller number = 1..4\n");
204}
205
206/** Print one-line device summary. */
207static void disk_print_summary(disk_t *d)
208{
209 uint64_t mbytes;
210
211 printf("%s: ", d->model);
212
213 if (d->dev_type == ata_reg_dev) {
214 switch (d->amode) {
215 case am_chs:
216 printf("CHS %u cylinders, %u heads, %u sectors",
217 disk->geom.cylinders, disk->geom.heads,
218 disk->geom.sectors);
219 break;
220 case am_lba28:
221 printf("LBA-28");
222 break;
223 case am_lba48:
224 printf("LBA-48");
225 break;
226 }
227 } else {
228 printf("PACKET");
229 }
230
231 printf(" %" PRIu64 " blocks", d->blocks);
232
233 mbytes = d->blocks / (2 * 1024);
234 if (mbytes > 0)
235 printf(" %" PRIu64 " MB.", mbytes);
236
237 printf("\n");
238}
239
240/** Register driver and enable device I/O. */
241static int ata_bd_init(void)
242{
243 void *vaddr;
244 int rc;
245
246 rc = loc_server_register(NAME, ata_bd_connection);
247 if (rc < 0) {
248 printf(NAME ": Unable to register driver.\n");
249 return rc;
250 }
251
252 rc = pio_enable((void *) cmd_physical, sizeof(ata_cmd_t), &vaddr);
253 if (rc != EOK) {
254 printf(NAME ": Could not initialize device I/O space.\n");
255 return rc;
256 }
257
258 cmd = vaddr;
259
260 rc = pio_enable((void *) ctl_physical, sizeof(ata_ctl_t), &vaddr);
261 if (rc != EOK) {
262 printf(NAME ": Could not initialize device I/O space.\n");
263 return rc;
264 }
265
266 ctl = vaddr;
267
268
269 return EOK;
270}
271
272/** Block device connection handler */
273static void ata_bd_connection(ipc_callid_t iid, ipc_call_t *icall, void *arg)
274{
275 void *fs_va = NULL;
276 ipc_callid_t callid;
277 ipc_call_t call;
278 sysarg_t method;
279 service_id_t dsid;
280 size_t comm_size; /**< Size of the communication area. */
281 unsigned int flags;
282 int retval;
283 uint64_t ba;
284 size_t cnt;
285 int disk_id, i;
286
287 /* Get the device service ID. */
288 dsid = IPC_GET_ARG1(*icall);
289
290 /* Determine which disk device is the client connecting to. */
291 disk_id = -1;
292 for (i = 0; i < MAX_DISKS; i++)
293 if (disk[i].service_id == dsid)
294 disk_id = i;
295
296 if (disk_id < 0 || disk[disk_id].present == false) {
297 async_answer_0(iid, EINVAL);
298 return;
299 }
300
301 /* Answer the IPC_M_CONNECT_ME_TO call. */
302 async_answer_0(iid, EOK);
303
304 if (!async_share_out_receive(&callid, &comm_size, &flags)) {
305 async_answer_0(callid, EHANGUP);
306 return;
307 }
308
309 fs_va = as_get_mappable_page(comm_size);
310 if (fs_va == NULL) {
311 async_answer_0(callid, EHANGUP);
312 return;
313 }
314
315 (void) async_share_out_finalize(callid, fs_va);
316
317 while (true) {
318 callid = async_get_call(&call);
319 method = IPC_GET_IMETHOD(call);
320
321 if (!method) {
322 /* The other side has hung up. */
323 async_answer_0(callid, EOK);
324 return;
325 }
326
327 switch (method) {
328 case BD_READ_BLOCKS:
329 ba = MERGE_LOUP32(IPC_GET_ARG1(call),
330 IPC_GET_ARG2(call));
331 cnt = IPC_GET_ARG3(call);
332 if (cnt * disk[disk_id].block_size > comm_size) {
333 retval = ELIMIT;
334 break;
335 }
336 retval = ata_bd_read_blocks(disk_id, ba, cnt, fs_va);
337 break;
338 case BD_WRITE_BLOCKS:
339 ba = MERGE_LOUP32(IPC_GET_ARG1(call),
340 IPC_GET_ARG2(call));
341 cnt = IPC_GET_ARG3(call);
342 if (cnt * disk[disk_id].block_size > comm_size) {
343 retval = ELIMIT;
344 break;
345 }
346 retval = ata_bd_write_blocks(disk_id, ba, cnt, fs_va);
347 break;
348 case BD_GET_BLOCK_SIZE:
349 async_answer_1(callid, EOK, disk[disk_id].block_size);
350 continue;
351 case BD_GET_NUM_BLOCKS:
352 async_answer_2(callid, EOK, LOWER32(disk[disk_id].blocks),
353 UPPER32(disk[disk_id].blocks));
354 continue;
355 default:
356 retval = EINVAL;
357 break;
358 }
359 async_answer_0(callid, retval);
360 }
361}
362
363/** Initialize a disk.
364 *
365 * Probes for a disk, determines its parameters and initializes
366 * the disk structure.
367 */
368static int disk_init(disk_t *d, int disk_id)
369{
370 identify_data_t idata;
371 uint8_t model[40];
372 ata_inquiry_data_t inq_data;
373 uint16_t w;
374 uint8_t c;
375 uint16_t bc;
376 size_t pos, len;
377 int rc;
378 unsigned i;
379
380 d->present = false;
381 fibril_mutex_initialize(&d->lock);
382
383 /* Try identify command. */
384 rc = drive_identify(disk_id, &idata);
385 if (rc == EOK) {
386 /* Success. It's a register (non-packet) device. */
387 printf("ATA register-only device found.\n");
388 d->dev_type = ata_reg_dev;
389 } else if (rc == EIO) {
390 /*
391 * There is something, but not a register device. Check to see
392 * whether the IDENTIFY command left the packet signature in
393 * the registers in case this is a packet device.
394 *
395 * According to the ATA specification, the LBA low and
396 * interrupt reason registers should be set to 0x01. However,
397 * there are many devices that do not follow this and only set
398 * the byte count registers. So, only check these.
399 */
400 bc = ((uint16_t)pio_read_8(&cmd->cylinder_high) << 8) |
401 pio_read_8(&cmd->cylinder_low);
402
403 if (bc == PDEV_SIGNATURE_BC) {
404 rc = identify_pkt_dev(disk_id, &idata);
405 if (rc == EOK) {
406 /* We have a packet device. */
407 d->dev_type = ata_pkt_dev;
408 } else {
409 return EIO;
410 }
411 } else {
412 /* Nope. Something's there, but not recognized. */
413 return EIO;
414 }
415 } else {
416 /* Operation timed out. That means there is no device there. */
417 return EIO;
418 }
419
420 if (d->dev_type == ata_pkt_dev) {
421 /* Packet device */
422 d->amode = 0;
423
424 d->geom.cylinders = 0;
425 d->geom.heads = 0;
426 d->geom.sectors = 0;
427
428 d->blocks = 0;
429 } else if ((idata.caps & rd_cap_lba) == 0) {
430 /* Device only supports CHS addressing. */
431 d->amode = am_chs;
432
433 d->geom.cylinders = idata.cylinders;
434 d->geom.heads = idata.heads;
435 d->geom.sectors = idata.sectors;
436
437 d->blocks = d->geom.cylinders * d->geom.heads * d->geom.sectors;
438 } else if ((idata.cmd_set1 & cs1_addr48) == 0) {
439 /* Device only supports LBA-28 addressing. */
440 d->amode = am_lba28;
441
442 d->geom.cylinders = 0;
443 d->geom.heads = 0;
444 d->geom.sectors = 0;
445
446 d->blocks =
447 (uint32_t) idata.total_lba28_0 |
448 ((uint32_t) idata.total_lba28_1 << 16);
449 } else {
450 /* Device supports LBA-48 addressing. */
451 d->amode = am_lba48;
452
453 d->geom.cylinders = 0;
454 d->geom.heads = 0;
455 d->geom.sectors = 0;
456
457 d->blocks =
458 (uint64_t) idata.total_lba48_0 |
459 ((uint64_t) idata.total_lba48_1 << 16) |
460 ((uint64_t) idata.total_lba48_2 << 32) |
461 ((uint64_t) idata.total_lba48_3 << 48);
462 }
463
464 /*
465 * Convert model name to string representation.
466 */
467 for (i = 0; i < 20; i++) {
468 w = idata.model_name[i];
469 model[2 * i] = w >> 8;
470 model[2 * i + 1] = w & 0x00ff;
471 }
472
473 len = 40;
474 while (len > 0 && model[len - 1] == 0x20)
475 --len;
476
477 pos = 0;
478 for (i = 0; i < len; ++i) {
479 c = model[i];
480 if (c >= 0x80) c = '?';
481
482 chr_encode(c, d->model, &pos, 40);
483 }
484 d->model[pos] = '\0';
485
486 if (d->dev_type == ata_pkt_dev) {
487 /* Send inquiry. */
488 rc = ata_pcmd_inquiry(0, &inq_data, sizeof(inq_data));
489 if (rc != EOK) {
490 printf("Device inquiry failed.\n");
491 d->present = false;
492 return EIO;
493 }
494
495 /* Check device type. */
496 if (INQUIRY_PDEV_TYPE(inq_data.pdev_type) != PDEV_TYPE_CDROM)
497 printf("Warning: Peripheral device type is not CD-ROM.\n");
498
499 /* Assume 2k block size for now. */
500 d->block_size = 2048;
501 } else {
502 /* Assume register Read always uses 512-byte blocks. */
503 d->block_size = 512;
504 }
505
506 d->present = true;
507 return EOK;
508}
509
510/** Read multiple blocks from the device. */
511static int ata_bd_read_blocks(int disk_id, uint64_t ba, size_t cnt,
512 void *buf) {
513
514 int rc;
515
516 while (cnt > 0) {
517 if (disk[disk_id].dev_type == ata_reg_dev)
518 rc = ata_rcmd_read(disk_id, ba, 1, buf);
519 else
520 rc = ata_pcmd_read_12(disk_id, ba, 1, buf,
521 disk[disk_id].block_size);
522
523 if (rc != EOK)
524 return rc;
525
526 ++ba;
527 --cnt;
528 buf += disk[disk_id].block_size;
529 }
530
531 return EOK;
532}
533
534/** Write multiple blocks to the device. */
535static int ata_bd_write_blocks(int disk_id, uint64_t ba, size_t cnt,
536 const void *buf) {
537
538 int rc;
539
540 if (disk[disk_id].dev_type != ata_reg_dev)
541 return ENOTSUP;
542
543 while (cnt > 0) {
544 rc = ata_rcmd_write(disk_id, ba, 1, buf);
545 if (rc != EOK)
546 return rc;
547
548 ++ba;
549 --cnt;
550 buf += disk[disk_id].block_size;
551 }
552
553 return EOK;
554}
555
556/** Issue IDENTIFY command.
557 *
558 * Reads @c identify data into the provided buffer. This is used to detect
559 * whether an ATA device is present and if so, to determine its parameters.
560 *
561 * @param disk_id Device ID, 0 or 1.
562 * @param buf Pointer to a 512-byte buffer.
563 *
564 * @return ETIMEOUT on timeout (this can mean the device is
565 * not present). EIO if device responds with error.
566 */
567static int drive_identify(int disk_id, void *buf)
568{
569 uint16_t data;
570 uint8_t status;
571 uint8_t drv_head;
572 size_t i;
573
574 drv_head = ((disk_id != 0) ? DHR_DRV : 0);
575
576 if (wait_status(0, ~SR_BSY, NULL, TIMEOUT_PROBE) != EOK)
577 return ETIMEOUT;
578
579 pio_write_8(&cmd->drive_head, drv_head);
580
581 /*
582 * Do not wait for DRDY to be set in case this is a packet device.
583 * We determine whether the device is present by waiting for DRQ to be
584 * set after issuing the command.
585 */
586 if (wait_status(0, ~SR_BSY, NULL, TIMEOUT_PROBE) != EOK)
587 return ETIMEOUT;
588
589 pio_write_8(&cmd->command, CMD_IDENTIFY_DRIVE);
590
591 if (wait_status(0, ~SR_BSY, &status, TIMEOUT_PROBE) != EOK)
592 return ETIMEOUT;
593
594 /*
595 * If ERR is set, this may be a packet device, so return EIO to cause
596 * the caller to check for one.
597 */
598 if ((status & SR_ERR) != 0) {
599 return EIO;
600 }
601
602 if (wait_status(SR_DRQ, ~SR_BSY, &status, TIMEOUT_PROBE) != EOK)
603 return ETIMEOUT;
604
605 /* Read data from the disk buffer. */
606
607 for (i = 0; i < identify_data_size / 2; i++) {
608 data = pio_read_16(&cmd->data_port);
609 ((uint16_t *) buf)[i] = data;
610 }
611
612 return EOK;
613}
614
615/** Issue Identify Packet Device command.
616 *
617 * Reads @c identify data into the provided buffer. This is used to detect
618 * whether an ATAPI device is present and if so, to determine its parameters.
619 *
620 * @param dev_idx Device index, 0 or 1.
621 * @param buf Pointer to a 512-byte buffer.
622 */
623static int identify_pkt_dev(int dev_idx, void *buf)
624{
625 uint16_t data;
626 uint8_t status;
627 uint8_t drv_head;
628 size_t i;
629
630 drv_head = ((dev_idx != 0) ? DHR_DRV : 0);
631
632 if (wait_status(0, ~SR_BSY, NULL, TIMEOUT_PROBE) != EOK)
633 return EIO;
634
635 pio_write_8(&cmd->drive_head, drv_head);
636
637 /* For ATAPI commands we do not need to wait for DRDY. */
638 if (wait_status(0, ~SR_BSY, NULL, TIMEOUT_PROBE) != EOK)
639 return EIO;
640
641 pio_write_8(&cmd->command, CMD_IDENTIFY_PKT_DEV);
642
643 if (wait_status(0, ~SR_BSY, &status, TIMEOUT_BSY) != EOK)
644 return EIO;
645
646 /* Read data from the device buffer. */
647
648 if ((status & SR_DRQ) != 0) {
649 for (i = 0; i < identify_data_size / 2; i++) {
650 data = pio_read_16(&cmd->data_port);
651 ((uint16_t *) buf)[i] = data;
652 }
653 }
654
655 if ((status & SR_ERR) != 0)
656 return EIO;
657
658 return EOK;
659}
660
661/** Issue packet command (i. e. write a command packet to the device).
662 *
663 * Only data-in commands are supported (e.g. inquiry, read).
664 *
665 * @param dev_idx Device index (0 or 1)
666 * @param obuf Buffer for storing data read from device
667 * @param obuf_size Size of obuf in bytes
668 *
669 * @return EOK on success, EIO on error.
670 */
671static int ata_cmd_packet(int dev_idx, const void *cpkt, size_t cpkt_size,
672 void *obuf, size_t obuf_size)
673{
674 size_t i;
675 uint8_t status;
676 uint8_t drv_head;
677 disk_t *d;
678 size_t data_size;
679 uint16_t val;
680
681 d = &disk[dev_idx];
682 fibril_mutex_lock(&d->lock);
683
684 /* New value for Drive/Head register */
685 drv_head =
686 ((dev_idx != 0) ? DHR_DRV : 0);
687
688 if (wait_status(0, ~SR_BSY, NULL, TIMEOUT_PROBE) != EOK) {
689 fibril_mutex_unlock(&d->lock);
690 return EIO;
691 }
692
693 pio_write_8(&cmd->drive_head, drv_head);
694
695 if (wait_status(0, ~(SR_BSY|SR_DRQ), NULL, TIMEOUT_BSY) != EOK) {
696 fibril_mutex_unlock(&d->lock);
697 return EIO;
698 }
699
700 /* Byte count <- max. number of bytes we can read in one transfer. */
701 pio_write_8(&cmd->cylinder_low, 0xfe);
702 pio_write_8(&cmd->cylinder_high, 0xff);
703
704 pio_write_8(&cmd->command, CMD_PACKET);
705
706 if (wait_status(SR_DRQ, ~SR_BSY, &status, TIMEOUT_BSY) != EOK) {
707 fibril_mutex_unlock(&d->lock);
708 return EIO;
709 }
710
711 /* Write command packet. */
712 for (i = 0; i < (cpkt_size + 1) / 2; i++)
713 pio_write_16(&cmd->data_port, ((uint16_t *) cpkt)[i]);
714
715 if (wait_status(0, ~SR_BSY, &status, TIMEOUT_BSY) != EOK) {
716 fibril_mutex_unlock(&d->lock);
717 return EIO;
718 }
719
720 if ((status & SR_DRQ) == 0) {
721 fibril_mutex_unlock(&d->lock);
722 return EIO;
723 }
724
725 /* Read byte count. */
726 data_size = (uint16_t) pio_read_8(&cmd->cylinder_low) +
727 ((uint16_t) pio_read_8(&cmd->cylinder_high) << 8);
728
729 /* Check whether data fits into output buffer. */
730 if (data_size > obuf_size) {
731 /* Output buffer is too small to store data. */
732 fibril_mutex_unlock(&d->lock);
733 return EIO;
734 }
735
736 /* Read data from the device buffer. */
737 for (i = 0; i < (data_size + 1) / 2; i++) {
738 val = pio_read_16(&cmd->data_port);
739 ((uint16_t *) obuf)[i] = val;
740 }
741
742 if (status & SR_ERR) {
743 fibril_mutex_unlock(&d->lock);
744 return EIO;
745 }
746
747 fibril_mutex_unlock(&d->lock);
748
749 return EOK;
750}
751
752/** Issue ATAPI Inquiry.
753 *
754 * @param dev_idx Device index (0 or 1)
755 * @param obuf Buffer for storing inquiry data read from device
756 * @param obuf_size Size of obuf in bytes
757 *
758 * @return EOK on success, EIO on error.
759 */
760static int ata_pcmd_inquiry(int dev_idx, void *obuf, size_t obuf_size)
761{
762 ata_pcmd_inquiry_t cp;
763 int rc;
764
765 memset(&cp, 0, sizeof(cp));
766
767 cp.opcode = PCMD_INQUIRY;
768 cp.alloc_len = min(obuf_size, 0xff); /* Allocation length */
769
770 rc = ata_cmd_packet(0, &cp, sizeof(cp), obuf, obuf_size);
771 if (rc != EOK)
772 return rc;
773
774 return EOK;
775}
776
777/** Issue ATAPI read(12) command.
778 *
779 * Output buffer must be large enough to hold the data, otherwise the
780 * function will fail.
781 *
782 * @param dev_idx Device index (0 or 1)
783 * @param ba Starting block address
784 * @param cnt Number of blocks to read
785 * @param obuf Buffer for storing inquiry data read from device
786 * @param obuf_size Size of obuf in bytes
787 *
788 * @return EOK on success, EIO on error.
789 */
790static int ata_pcmd_read_12(int dev_idx, uint64_t ba, size_t cnt,
791 void *obuf, size_t obuf_size)
792{
793 ata_pcmd_read_12_t cp;
794 int rc;
795
796 if (ba > UINT32_MAX)
797 return EINVAL;
798
799 memset(&cp, 0, sizeof(cp));
800
801 cp.opcode = PCMD_READ_12;
802 cp.ba = host2uint32_t_be(ba);
803 cp.nblocks = host2uint32_t_be(cnt);
804
805 rc = ata_cmd_packet(0, &cp, sizeof(cp), obuf, obuf_size);
806 if (rc != EOK)
807 return rc;
808
809 return EOK;
810}
811
812/** Read a physical from the device.
813 *
814 * @param disk_id Device index (0 or 1)
815 * @param ba Address the first block.
816 * @param cnt Number of blocks to transfer.
817 * @param buf Buffer for holding the data.
818 *
819 * @return EOK on success, EIO on error.
820 */
821static int ata_rcmd_read(int disk_id, uint64_t ba, size_t blk_cnt,
822 void *buf)
823{
824 size_t i;
825 uint16_t data;
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 Read 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_READ_SECTORS_EXT : CMD_READ_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 /* Read data from the device buffer. */
875
876 for (i = 0; i < disk[disk_id].block_size / 2; i++) {
877 data = pio_read_16(&cmd->data_port);
878 ((uint16_t *) buf)[i] = data;
879 }
880 }
881
882 if ((status & SR_ERR) != 0)
883 return EIO;
884
885 fibril_mutex_unlock(&d->lock);
886 return EOK;
887}
888
889/** Write a physical block to the device.
890 *
891 * @param disk_id Device index (0 or 1)
892 * @param ba Address of the first block.
893 * @param cnt Number of blocks to transfer.
894 * @param buf Buffer holding the data to write.
895 *
896 * @return EOK on success, EIO on error.
897 */
898static int ata_rcmd_write(int disk_id, uint64_t ba, size_t cnt,
899 const void *buf)
900{
901 size_t i;
902 uint8_t status;
903 uint8_t drv_head;
904 disk_t *d;
905 block_coord_t bc;
906
907 d = &disk[disk_id];
908
909 /* Silence warning. */
910 memset(&bc, 0, sizeof(bc));
911
912 /* Compute block coordinates. */
913 if (coord_calc(d, ba, &bc) != EOK)
914 return EINVAL;
915
916 /* New value for Drive/Head register */
917 drv_head =
918 ((disk_id != 0) ? DHR_DRV : 0) |
919 ((d->amode != am_chs) ? DHR_LBA : 0) |
920 (bc.h & 0x0f);
921
922 fibril_mutex_lock(&d->lock);
923
924 /* Program a Write Sectors operation. */
925
926 if (wait_status(0, ~SR_BSY, NULL, TIMEOUT_BSY) != EOK) {
927 fibril_mutex_unlock(&d->lock);
928 return EIO;
929 }
930
931 pio_write_8(&cmd->drive_head, drv_head);
932
933 if (wait_status(SR_DRDY, ~SR_BSY, NULL, TIMEOUT_DRDY) != EOK) {
934 fibril_mutex_unlock(&d->lock);
935 return EIO;
936 }
937
938 /* Program block coordinates into the device. */
939 coord_sc_program(&bc, 1);
940
941 pio_write_8(&cmd->command, d->amode == am_lba48 ?
942 CMD_WRITE_SECTORS_EXT : CMD_WRITE_SECTORS);
943
944 if (wait_status(0, ~SR_BSY, &status, TIMEOUT_BSY) != EOK) {
945 fibril_mutex_unlock(&d->lock);
946 return EIO;
947 }
948
949 if ((status & SR_DRQ) != 0) {
950 /* Write data to the device buffer. */
951
952 for (i = 0; i < disk[disk_id].block_size / 2; i++) {
953 pio_write_16(&cmd->data_port, ((uint16_t *) buf)[i]);
954 }
955 }
956
957 fibril_mutex_unlock(&d->lock);
958
959 if (status & SR_ERR)
960 return EIO;
961
962 return EOK;
963}
964
965/** Calculate block coordinates.
966 *
967 * Calculates block coordinates in the best coordinate system supported
968 * by the device. These can be later programmed into the device using
969 * @c coord_sc_program().
970 *
971 * @return EOK on success or EINVAL if block index is past end of device.
972 */
973static int coord_calc(disk_t *d, uint64_t ba, block_coord_t *bc)
974{
975 uint64_t c;
976 uint64_t idx;
977
978 /* Check device bounds. */
979 if (ba >= d->blocks)
980 return EINVAL;
981
982 bc->amode = d->amode;
983
984 switch (d->amode) {
985 case am_chs:
986 /* Compute CHS coordinates. */
987 c = ba / (d->geom.heads * d->geom.sectors);
988 idx = ba % (d->geom.heads * d->geom.sectors);
989
990 bc->cyl_lo = c & 0xff;
991 bc->cyl_hi = (c >> 8) & 0xff;
992 bc->h = (idx / d->geom.sectors) & 0x0f;
993 bc->sector = (1 + (idx % d->geom.sectors)) & 0xff;
994 break;
995
996 case am_lba28:
997 /* Compute LBA-28 coordinates. */
998 bc->c0 = ba & 0xff; /* bits 0-7 */
999 bc->c1 = (ba >> 8) & 0xff; /* bits 8-15 */
1000 bc->c2 = (ba >> 16) & 0xff; /* bits 16-23 */
1001 bc->h = (ba >> 24) & 0x0f; /* bits 24-27 */
1002 break;
1003
1004 case am_lba48:
1005 /* Compute LBA-48 coordinates. */
1006 bc->c0 = ba & 0xff; /* bits 0-7 */
1007 bc->c1 = (ba >> 8) & 0xff; /* bits 8-15 */
1008 bc->c2 = (ba >> 16) & 0xff; /* bits 16-23 */
1009 bc->c3 = (ba >> 24) & 0xff; /* bits 24-31 */
1010 bc->c4 = (ba >> 32) & 0xff; /* bits 32-39 */
1011 bc->c5 = (ba >> 40) & 0xff; /* bits 40-47 */
1012 bc->h = 0;
1013 break;
1014 }
1015
1016 return EOK;
1017}
1018
1019/** Program block coordinates and sector count into ATA registers.
1020 *
1021 * Note that bc->h must be programmed separately into the device/head register.
1022 */
1023static void coord_sc_program(const block_coord_t *bc, uint16_t scnt)
1024{
1025 if (bc->amode == am_lba48) {
1026 /* Write high-order bits. */
1027 pio_write_8(&cmd->sector_count, scnt >> 8);
1028 pio_write_8(&cmd->sector_number, bc->c3);
1029 pio_write_8(&cmd->cylinder_low, bc->c4);
1030 pio_write_8(&cmd->cylinder_high, bc->c5);
1031 }
1032
1033 /* Write low-order bits. */
1034 pio_write_8(&cmd->sector_count, scnt & 0x00ff);
1035 pio_write_8(&cmd->sector_number, bc->c0);
1036 pio_write_8(&cmd->cylinder_low, bc->c1);
1037 pio_write_8(&cmd->cylinder_high, bc->c2);
1038}
1039
1040/** Wait until some status bits are set and some are reset.
1041 *
1042 * Example: wait_status(SR_DRDY, ~SR_BSY) waits for SR_DRDY to become
1043 * set and SR_BSY to become reset.
1044 *
1045 * @param set Combination if bits which must be all set.
1046 * @param n_reset Negated combination of bits which must be all reset.
1047 * @param pstatus Pointer where to store last read status or NULL.
1048 * @param timeout Timeout in 10ms units.
1049 *
1050 * @return EOK on success, EIO on timeout.
1051 */
1052static int wait_status(unsigned set, unsigned n_reset, uint8_t *pstatus,
1053 unsigned timeout)
1054{
1055 uint8_t status;
1056 int cnt;
1057
1058 status = pio_read_8(&cmd->status);
1059
1060 /*
1061 * This is crude, yet simple. First try with 1us delays
1062 * (most likely the device will respond very fast). If not,
1063 * start trying every 10 ms.
1064 */
1065
1066 cnt = 100;
1067 while ((status & ~n_reset) != 0 || (status & set) != set) {
1068 async_usleep(1);
1069 --cnt;
1070 if (cnt <= 0) break;
1071
1072 status = pio_read_8(&cmd->status);
1073 }
1074
1075 cnt = timeout;
1076 while ((status & ~n_reset) != 0 || (status & set) != set) {
1077 async_usleep(10000);
1078 --cnt;
1079 if (cnt <= 0) break;
1080
1081 status = pio_read_8(&cmd->status);
1082 }
1083
1084 if (pstatus)
1085 *pstatus = status;
1086
1087 if (cnt == 0)
1088 return EIO;
1089
1090 return EOK;
1091}
1092
1093/**
1094 * @}
1095 */
Note: See TracBrowser for help on using the repository browser.