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

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

Display device size in megabytes.

  • Property mode set to 100644
File size: 15.0 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 currently works only with CHS addressing and uses PIO.
38 * Currently based on the (now obsolete) ATA-1, ATA-2 standards.
39 *
40 * The driver services a single controller which can have up to two disks
41 * attached.
42 */
43
44#include <stdio.h>
45#include <libarch/ddi.h>
46#include <ddi.h>
47#include <ipc/ipc.h>
48#include <ipc/bd.h>
49#include <async.h>
50#include <as.h>
51#include <fibril_sync.h>
52#include <string.h>
53#include <devmap.h>
54#include <sys/types.h>
55#include <errno.h>
56#include <bool.h>
57#include <task.h>
58
59#include "ata_bd.h"
60
61#define NAME "ata_bd"
62
63/** Physical block size. Should be always 512. */
64static const size_t block_size = 512;
65
66/** Size of the communication area. */
67static size_t comm_size;
68
69/** I/O base address of the command registers. */
70static uintptr_t cmd_physical = 0x1f0;
71/** I/O base address of the control registers. */
72static uintptr_t ctl_physical = 0x170;
73
74static ata_cmd_t *cmd;
75static ata_ctl_t *ctl;
76
77/** Per-disk state. */
78static disk_t disk[MAX_DISKS];
79
80static int ata_bd_init(void);
81static void ata_bd_connection(ipc_callid_t iid, ipc_call_t *icall);
82static int ata_bd_rdwr(int disk_id, ipcarg_t method, off_t offset, size_t size,
83 void *buf);
84static int ata_bd_read_block(int disk_id, uint64_t blk_idx, size_t blk_cnt,
85 void *buf);
86static int ata_bd_write_block(int disk_id, uint64_t blk_idx, size_t blk_cnt,
87 const void *buf);
88static int disk_init(disk_t *d, int disk_id);
89static int drive_identify(int drive_id, void *buf);
90static void disk_print_summary(disk_t *d);
91static int wait_status(unsigned set, unsigned n_reset, uint8_t *pstatus,
92 unsigned timeout);
93
94int main(int argc, char **argv)
95{
96 char name[16];
97 int i, rc;
98 int n_disks;
99
100 printf(NAME ": ATA disk driver\n");
101
102 printf("I/O address 0x%x\n", cmd_physical);
103
104 if (ata_bd_init() != EOK)
105 return -1;
106
107 for (i = 0; i < MAX_DISKS; i++) {
108 printf("Identify drive %d... ", i);
109 fflush(stdout);
110
111 rc = disk_init(&disk[i], i);
112
113 if (rc == EOK) {
114 disk_print_summary(&disk[i]);
115 } else {
116 printf("Not found.\n");
117 }
118 }
119
120 n_disks = 0;
121
122 for (i = 0; i < MAX_DISKS; i++) {
123 /* Skip unattached drives. */
124 if (disk[i].present == false)
125 continue;
126
127 snprintf(name, 16, "disk%d", i);
128 rc = devmap_device_register(name, &disk[i].dev_handle);
129 if (rc != EOK) {
130 devmap_hangup_phone(DEVMAP_DRIVER);
131 printf(NAME ": Unable to register device %s.\n",
132 name);
133 return rc;
134 }
135 ++n_disks;
136 }
137
138 if (n_disks == 0) {
139 printf("No disks detected.\n");
140 return -1;
141 }
142
143 printf(NAME ": Accepting connections\n");
144 task_retval(0);
145 async_manager();
146
147 /* Not reached */
148 return 0;
149}
150
151/** Print one-line device summary. */
152static void disk_print_summary(disk_t *d)
153{
154 uint64_t mbytes;
155
156 printf("%s: ", d->model);
157
158 if (d->amode == am_chs) {
159 printf("CHS %u cylinders, %u heads, %u sectors",
160 disk->geom.cylinders, disk->geom.heads, disk->geom.sectors);
161 } else {
162 printf("LBA-28");
163 }
164
165 printf(" %llu blocks", d->blocks, d->blocks / (2 * 1024));
166
167 mbytes = d->blocks / (2 * 1024);
168 if (mbytes > 0)
169 printf(" %llu MB.", mbytes);
170
171 printf("\n");
172}
173
174/** Register driver and enable device I/O. */
175static int ata_bd_init(void)
176{
177 void *vaddr;
178 int rc;
179
180 rc = devmap_driver_register(NAME, ata_bd_connection);
181 if (rc < 0) {
182 printf(NAME ": Unable to register driver.\n");
183 return rc;
184 }
185
186 rc = pio_enable((void *) cmd_physical, sizeof(ata_cmd_t), &vaddr);
187 if (rc != EOK) {
188 printf(NAME ": Could not initialize device I/O space.\n");
189 return rc;
190 }
191
192 cmd = vaddr;
193
194 rc = pio_enable((void *) ctl_physical, sizeof(ata_ctl_t), &vaddr);
195 if (rc != EOK) {
196 printf(NAME ": Could not initialize device I/O space.\n");
197 return rc;
198 }
199
200 ctl = vaddr;
201
202
203 return EOK;
204}
205
206/** Block device connection handler */
207static void ata_bd_connection(ipc_callid_t iid, ipc_call_t *icall)
208{
209 void *fs_va = NULL;
210 ipc_callid_t callid;
211 ipc_call_t call;
212 ipcarg_t method;
213 dev_handle_t dh;
214 int flags;
215 int retval;
216 off_t idx;
217 size_t size;
218 int disk_id, i;
219
220 /* Get the device handle. */
221 dh = IPC_GET_ARG1(*icall);
222
223 /* Determine which disk device is the client connecting to. */
224 disk_id = -1;
225 for (i = 0; i < MAX_DISKS; i++)
226 if (disk[i].dev_handle == dh)
227 disk_id = i;
228
229 if (disk_id < 0 || disk[disk_id].present == false) {
230 ipc_answer_0(iid, EINVAL);
231 return;
232 }
233
234 /* Answer the IPC_M_CONNECT_ME_TO call. */
235 ipc_answer_0(iid, EOK);
236
237 if (!ipc_share_out_receive(&callid, &comm_size, &flags)) {
238 ipc_answer_0(callid, EHANGUP);
239 return;
240 }
241
242 fs_va = as_get_mappable_page(comm_size);
243 if (fs_va == NULL) {
244 ipc_answer_0(callid, EHANGUP);
245 return;
246 }
247
248 (void) ipc_share_out_finalize(callid, fs_va);
249
250 while (1) {
251 callid = async_get_call(&call);
252 method = IPC_GET_METHOD(call);
253 switch (method) {
254 case IPC_M_PHONE_HUNGUP:
255 /* The other side has hung up. */
256 ipc_answer_0(callid, EOK);
257 return;
258 case BD_READ_BLOCK:
259 case BD_WRITE_BLOCK:
260 idx = IPC_GET_ARG1(call);
261 size = IPC_GET_ARG2(call);
262 if (size > comm_size) {
263 retval = EINVAL;
264 break;
265 }
266 retval = ata_bd_rdwr(disk_id, method, idx,
267 size, fs_va);
268 break;
269 default:
270 retval = EINVAL;
271 break;
272 }
273 ipc_answer_0(callid, retval);
274 }
275}
276
277/** Initialize a disk.
278 *
279 * Probes for a disk, determines its parameters and initializes
280 * the disk structure.
281 */
282static int disk_init(disk_t *d, int disk_id)
283{
284 identify_data_t idata;
285 uint8_t model[40];
286 uint16_t w;
287 uint8_t c;
288 size_t pos, len;
289 int rc;
290 int i;
291
292 rc = drive_identify(disk_id, &idata);
293 if (rc != EOK) {
294 d->present = false;
295 return rc;
296 }
297
298 if ((idata.caps & cap_lba) == 0) {
299 /* Device only supports CHS addressing. */
300 d->amode = am_chs;
301
302 d->geom.cylinders = idata.cylinders;
303 d->geom.heads = idata.heads;
304 d->geom.sectors = idata.sectors;
305
306 d->blocks = d->geom.cylinders * d->geom.heads * d->geom.sectors;
307 } else {
308 /* Device supports LBA-28. */
309 d->amode = am_lba28;
310
311 d->geom.cylinders = 0;
312 d->geom.heads = 0;
313 d->geom.sectors = 0;
314
315 d->blocks =
316 (uint32_t) idata.total_lba_sec0 |
317 ((uint32_t) idata.total_lba_sec1 << 16);
318 }
319
320 /*
321 * Convert model name to string representation.
322 */
323 for (i = 0; i < 20; i++) {
324 w = idata.model_name[i];
325 model[2 * i] = w >> 8;
326 model[2 * i + 1] = w & 0x00ff;
327 }
328
329 len = 40;
330 while (len > 0 && model[len - 1] == 0x20)
331 --len;
332
333 pos = 0;
334 for (i = 0; i < len; ++i) {
335 c = model[i];
336 if (c >= 0x80) c = '?';
337
338 chr_encode(c, d->model, &pos, 40);
339 }
340 d->model[pos] = '\0';
341
342 d->present = true;
343 fibril_mutex_initialize(&d->lock);
344
345 return EOK;
346}
347
348/** Transfer a logical block from/to the device.
349 *
350 * @param disk_id Device index (0 or 1)
351 * @param method @c BD_READ_BLOCK or @c BD_WRITE_BLOCK
352 * @param blk_idx Index of the first block.
353 * @param size Size of the logical block.
354 * @param buf Data buffer.
355 *
356 * @return EOK on success, EIO on error.
357 */
358static int ata_bd_rdwr(int disk_id, ipcarg_t method, off_t blk_idx, size_t size,
359 void *buf)
360{
361 int rc;
362 size_t now;
363
364 while (size > 0) {
365 now = size < block_size ? size : block_size;
366 if (now != block_size)
367 return EINVAL;
368
369 if (method == BD_READ_BLOCK)
370 rc = ata_bd_read_block(disk_id, blk_idx, 1, buf);
371 else
372 rc = ata_bd_write_block(disk_id, blk_idx, 1, buf);
373
374 if (rc != EOK)
375 return rc;
376
377 buf += block_size;
378 blk_idx++;
379
380 if (size > block_size)
381 size -= block_size;
382 else
383 size = 0;
384 }
385
386 return EOK;
387}
388
389/** Issue IDENTIFY command.
390 *
391 * Reads @c identify data into the provided buffer. This is used to detect
392 * whether an ATA device is present and if so, to determine its parameters.
393 *
394 * @param disk_id Device ID, 0 or 1.
395 * @param buf Pointer to a 512-byte buffer.
396 */
397static int drive_identify(int disk_id, void *buf)
398{
399 uint16_t data;
400 uint8_t status;
401 uint8_t drv_head;
402 size_t i;
403
404 drv_head = ((disk_id != 0) ? DHR_DRV : 0);
405
406 if (wait_status(0, ~SR_BSY, NULL, TIMEOUT_PROBE) != EOK)
407 return EIO;
408
409 pio_write_8(&cmd->drive_head, drv_head);
410
411 /*
412 * This is where we would most likely expect a non-existing device to
413 * show up by not setting SR_DRDY.
414 */
415 if (wait_status(SR_DRDY, ~SR_BSY, NULL, TIMEOUT_PROBE) != EOK)
416 return EIO;
417
418 pio_write_8(&cmd->command, CMD_IDENTIFY_DRIVE);
419
420 if (wait_status(0, ~SR_BSY, &status, TIMEOUT_PROBE) != EOK)
421 return EIO;
422
423 /* Read data from the disk buffer. */
424
425 if ((status & SR_DRQ) != 0) {
426 for (i = 0; i < block_size / 2; i++) {
427 data = pio_read_16(&cmd->data_port);
428 ((uint16_t *) buf)[i] = data;
429 }
430 }
431
432 if ((status & SR_ERR) != 0)
433 return EIO;
434
435 return EOK;
436}
437
438/** Read a physical from the device.
439 *
440 * @param disk_id Device index (0 or 1)
441 * @param blk_idx Index of the first block.
442 * @param blk_cnt Number of blocks to transfer.
443 * @param buf Buffer for holding the data.
444 *
445 * @return EOK on success, EIO on error.
446 */
447static int ata_bd_read_block(int disk_id, uint64_t blk_idx, size_t blk_cnt,
448 void *buf)
449{
450 size_t i;
451 uint16_t data;
452 uint8_t status;
453 uint64_t c, h, s;
454 uint64_t idx;
455 uint8_t drv_head;
456 disk_t *d;
457
458 d = &disk[disk_id];
459
460 /* Check device bounds. */
461 if (blk_idx >= d->blocks)
462 return EINVAL;
463
464 if (d->amode == am_chs) {
465 /* Compute CHS coordinates. */
466 c = blk_idx / (d->geom.heads * d->geom.sectors);
467 idx = blk_idx % (d->geom.heads * d->geom.sectors);
468
469 h = idx / d->geom.sectors;
470 s = 1 + (idx % d->geom.sectors);
471 } else {
472 /* Compute LBA-28 coordinates. */
473 s = blk_idx & 0xff; /* bits 0-7 */
474 c = (blk_idx >> 8) & 0xffff; /* bits 8-23 */
475 h = (blk_idx >> 24) & 0x0f; /* bits 24-27 */
476 }
477
478 /* New value for Drive/Head register */
479 drv_head =
480 ((disk_id != 0) ? DHR_DRV : 0) |
481 ((d->amode != am_chs) ? DHR_LBA : 0) |
482 (h & 0x0f);
483
484 fibril_mutex_lock(&d->lock);
485
486 /* Program a Read Sectors operation. */
487
488 if (wait_status(0, ~SR_BSY, NULL, TIMEOUT_BSY) != EOK) {
489 fibril_mutex_unlock(&d->lock);
490 return EIO;
491 }
492
493 pio_write_8(&cmd->drive_head, drv_head);
494
495 if (wait_status(SR_DRDY, ~SR_BSY, NULL, TIMEOUT_DRDY) != EOK) {
496 fibril_mutex_unlock(&d->lock);
497 return EIO;
498 }
499
500 pio_write_8(&cmd->sector_count, 1);
501 pio_write_8(&cmd->sector_number, s);
502 pio_write_8(&cmd->cylinder_low, c & 0xff);
503 pio_write_8(&cmd->cylinder_high, c >> 16);
504
505 pio_write_8(&cmd->command, CMD_READ_SECTORS);
506
507 if (wait_status(0, ~SR_BSY, &status, TIMEOUT_BSY) != EOK) {
508 fibril_mutex_unlock(&d->lock);
509 return EIO;
510 }
511
512 if ((status & SR_DRQ) != 0) {
513 /* Read data from the device buffer. */
514
515 for (i = 0; i < block_size / 2; i++) {
516 data = pio_read_16(&cmd->data_port);
517 ((uint16_t *) buf)[i] = data;
518 }
519 }
520
521 if ((status & SR_ERR) != 0)
522 return EIO;
523
524 fibril_mutex_unlock(&d->lock);
525 return EOK;
526}
527
528/** Write a physical block to the device.
529 *
530 * @param disk_id Device index (0 or 1)
531 * @param blk_idx Index of the first block.
532 * @param blk_cnt Number of blocks to transfer.
533 * @param buf Buffer holding the data to write.
534 *
535 * @return EOK on success, EIO on error.
536 */
537static int ata_bd_write_block(int disk_id, uint64_t blk_idx, size_t blk_cnt,
538 const void *buf)
539{
540 size_t i;
541 uint8_t status;
542 uint64_t c, h, s;
543 uint64_t idx;
544 uint8_t drv_head;
545 disk_t *d;
546
547 d = &disk[disk_id];
548
549 /* Check device bounds. */
550 if (blk_idx >= d->blocks)
551 return EINVAL;
552
553 if (d->amode == am_chs) {
554 /* Compute CHS coordinates. */
555 c = blk_idx / (d->geom.heads * d->geom.sectors);
556 idx = blk_idx % (d->geom.heads * d->geom.sectors);
557
558 h = idx / d->geom.sectors;
559 s = 1 + (idx % d->geom.sectors);
560 } else {
561 /* Compute LBA-28 coordinates. */
562 s = blk_idx & 0xff; /* bits 0-7 */
563 c = (blk_idx >> 8) & 0xffff; /* bits 8-23 */
564 h = (blk_idx >> 24) & 0x0f; /* bits 24-27 */
565 }
566
567 /* New value for Drive/Head register */
568 drv_head =
569 ((disk_id != 0) ? DHR_DRV : 0) |
570 ((d->amode != am_chs) ? DHR_LBA : 0) |
571 (h & 0x0f);
572
573 fibril_mutex_lock(&d->lock);
574
575 /* Program a Write Sectors operation. */
576
577 if (wait_status(0, ~SR_BSY, NULL, TIMEOUT_BSY) != EOK) {
578 fibril_mutex_unlock(&d->lock);
579 return EIO;
580 }
581
582 pio_write_8(&cmd->drive_head, drv_head);
583
584 if (wait_status(SR_DRDY, ~SR_BSY, NULL, TIMEOUT_DRDY) != EOK) {
585 fibril_mutex_unlock(&d->lock);
586 return EIO;
587 }
588
589 pio_write_8(&cmd->sector_count, 1);
590 pio_write_8(&cmd->sector_number, s);
591 pio_write_8(&cmd->cylinder_low, c & 0xff);
592 pio_write_8(&cmd->cylinder_high, c >> 16);
593
594 pio_write_8(&cmd->command, CMD_WRITE_SECTORS);
595
596 if (wait_status(0, ~SR_BSY, &status, TIMEOUT_BSY) != EOK) {
597 fibril_mutex_unlock(&d->lock);
598 return EIO;
599 }
600
601 if ((status & SR_DRQ) != 0) {
602 /* Write data to the device buffer. */
603
604 for (i = 0; i < block_size / 2; i++) {
605 pio_write_16(&cmd->data_port, ((uint16_t *) buf)[i]);
606 }
607 }
608
609 fibril_mutex_unlock(&d->lock);
610
611 if (status & SR_ERR)
612 return EIO;
613
614 return EOK;
615}
616
617/** Wait until some status bits are set and some are reset.
618 *
619 * Example: wait_status(SR_DRDY, ~SR_BSY) waits for SR_DRDY to become
620 * set and SR_BSY to become reset.
621 *
622 * @param set Combination if bits which must be all set.
623 * @param n_reset Negated combination of bits which must be all reset.
624 * @param pstatus Pointer where to store last read status or NULL.
625 * @param timeout Timeout in 10ms units.
626 *
627 * @return EOK on success, EIO on timeout.
628 */
629static int wait_status(unsigned set, unsigned n_reset, uint8_t *pstatus,
630 unsigned timeout)
631{
632 uint8_t status;
633 int cnt;
634
635 status = pio_read_8(&cmd->status);
636
637 /*
638 * This is crude, yet simple. First try with 1us delays
639 * (most likely the device will respond very fast). If not,
640 * start trying every 10 ms.
641 */
642
643 cnt = 100;
644 while ((status & ~n_reset) != 0 || (status & set) != set) {
645 async_usleep(1);
646 --cnt;
647 if (cnt <= 0) break;
648
649 status = pio_read_8(&cmd->status);
650 }
651
652 cnt = timeout;
653 while ((status & ~n_reset) != 0 || (status & set) != set) {
654 async_usleep(10000);
655 --cnt;
656 if (cnt <= 0) break;
657
658 status = pio_read_8(&cmd->status);
659 }
660
661 if (pstatus)
662 *pstatus = status;
663
664 if (cnt == 0)
665 return EIO;
666
667 return EOK;
668}
669
670/**
671 * @}
672 */
Note: See TracBrowser for help on using the repository browser.