source: mainline/uspace/srv/bd/ata_bd/ata_bd.c@ 7be3638

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 7be3638 was 4046b2f4, checked in by Martin Decky <martin@…>, 14 years ago

TOC reading support
cherrypicked from lp:~jkavalik/cdfs/main

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