source: mainline/uspace/srv/bd/ata_bd/ata_bd.c@ 135486d

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

Allow more than one client connection to block device.

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