source: mainline/uspace/srv/bd/ata_bd/ata_bd.c@ aa893e0

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

Add docblock.

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