2
* Copyright (c) 2009 Jiri Svoboda
5
* Redistribution and use in source and binary forms, with or without
6
* modification, are permitted provided that the following conditions
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.
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.
35
* @brief ATA disk driver
37
* This driver currently works only with CHS addressing and uses PIO.
38
* Currently based on the (now obsolete) ANSI X3.221-1994 (ATA-1) standard.
39
* At this point only reading is possible, not writing.
41
* The driver services a single controller which can have up to two disks
46
#include <libarch/ddi.h>
52
#include <fibril_sync.h>
54
#include <sys/types.h>
63
static const size_t block_size = 512;
64
static size_t comm_size;
66
static uintptr_t cmd_physical = 0x1f0;
67
static uintptr_t ctl_physical = 0x170;
68
static ata_cmd_t *cmd;
69
static ata_ctl_t *ctl;
71
/** Per-disk state. */
72
static disk_t disk[MAX_DISKS];
74
static int ata_bd_init(void);
75
static void ata_bd_connection(ipc_callid_t iid, ipc_call_t *icall);
76
static int ata_bd_rdwr(int disk_id, ipcarg_t method, off_t offset, size_t size,
78
static int ata_bd_read_block(int disk_id, uint64_t blk_idx, size_t blk_cnt,
80
static int ata_bd_write_block(int disk_id, uint64_t blk_idx, size_t blk_cnt,
82
static int drive_identify(int drive_id, disk_t *d);
84
int main(int argc, char **argv)
91
printf(NAME ": ATA disk driver\n");
93
printf("I/O address 0x%x\n", cmd_physical);
95
if (ata_bd_init() != EOK)
98
/* Put drives to reset, disable interrupts. */
99
printf("Reset drives... ");
102
pio_write_8(&ctl->device_control, DCR_SRST);
103
/* FIXME: Find out how to do this properly. */
105
pio_write_8(&ctl->device_control, 0);
108
status = pio_read_8(&cmd->status);
109
} while ((status & SR_BSY) != 0);
112
(void) drive_identify(0, &disk[0]);
113
(void) drive_identify(1, &disk[1]);
117
for (i = 0; i < MAX_DISKS; i++) {
118
/* Skip unattached drives. */
119
if (disk[i].present == false)
122
snprintf(name, 16, "disk%d", i);
123
rc = devmap_device_register(name, &disk[i].dev_handle);
125
devmap_hangup_phone(DEVMAP_DRIVER);
126
printf(NAME ": Unable to register device %s.\n",
134
printf("No disks detected.\n");
138
printf(NAME ": Accepting connections\n");
146
static int drive_identify(int disk_id, disk_t *d)
152
printf("Identify drive %d... ", disk_id);
155
pio_write_8(&cmd->drive_head, ((disk_id != 0) ? DHR_DRV : 0));
157
pio_write_8(&cmd->command, CMD_IDENTIFY_DRIVE);
159
status = pio_read_8(&cmd->status);
164
* Detect if drive is present. This is Qemu only! Need to
165
* do the right thing to work with real drives.
167
if ((status & SR_DRDY) == 0) {
168
printf("None attached.\n");
172
for (i = 0; i < block_size / 2; i++) {
174
status = pio_read_8(&cmd->status);
175
} while ((status & SR_DRDY) == 0);
177
data = pio_read_16(&cmd->data_port);
180
case 1: d->cylinders = data; break;
181
case 3: d->heads = data; break;
182
case 6: d->sectors = data; break;
186
d->blocks = d->cylinders * d->heads * d->sectors;
188
printf("Geometry: %u cylinders, %u heads, %u sectors\n",
189
d->cylinders, d->heads, d->sectors);
192
fibril_mutex_initialize(&d->lock);
197
static int ata_bd_init(void)
202
rc = devmap_driver_register(NAME, ata_bd_connection);
204
printf(NAME ": Unable to register driver.\n");
208
rc = pio_enable((void *) cmd_physical, sizeof(ata_cmd_t), &vaddr);
210
printf(NAME ": Could not initialize device I/O space.\n");
216
rc = pio_enable((void *) ctl_physical, sizeof(ata_ctl_t), &vaddr);
218
printf(NAME ": Could not initialize device I/O space.\n");
228
static void ata_bd_connection(ipc_callid_t iid, ipc_call_t *icall)
241
/* Get the device handle. */
242
dh = IPC_GET_ARG1(*icall);
244
/* Determine which disk device is the client connecting to. */
246
for (i = 0; i < MAX_DISKS; i++)
247
if (disk[i].dev_handle == dh)
250
if (disk_id < 0 || disk[disk_id].present == false) {
251
ipc_answer_0(iid, EINVAL);
255
/* Answer the IPC_M_CONNECT_ME_TO call. */
256
ipc_answer_0(iid, EOK);
258
if (!ipc_share_out_receive(&callid, &comm_size, &flags)) {
259
ipc_answer_0(callid, EHANGUP);
263
fs_va = as_get_mappable_page(comm_size);
265
ipc_answer_0(callid, EHANGUP);
269
(void) ipc_share_out_finalize(callid, fs_va);
272
callid = async_get_call(&call);
273
method = IPC_GET_METHOD(call);
275
case IPC_M_PHONE_HUNGUP:
276
/* The other side has hung up. */
277
ipc_answer_0(callid, EOK);
281
idx = IPC_GET_ARG1(call);
282
size = IPC_GET_ARG2(call);
283
if (size > comm_size) {
287
retval = ata_bd_rdwr(disk_id, method, idx,
294
ipc_answer_0(callid, retval);
298
static int ata_bd_rdwr(int disk_id, ipcarg_t method, off_t blk_idx, size_t size,
305
now = size < block_size ? size : block_size;
306
if (now != block_size)
309
if (method == BD_READ_BLOCK)
310
rc = ata_bd_read_block(disk_id, blk_idx, 1, buf);
312
rc = ata_bd_write_block(disk_id, blk_idx, 1, buf);
320
if (size > block_size)
330
static int ata_bd_read_block(int disk_id, uint64_t blk_idx, size_t blk_cnt,
343
/* Check device bounds. */
344
if (blk_idx >= d->blocks)
348
c = blk_idx / (d->heads * d->sectors);
349
idx = blk_idx % (d->heads * d->sectors);
351
h = idx / d->sectors;
352
s = 1 + (idx % d->sectors);
354
/* New value for Drive/Head register */
356
((disk_id != 0) ? DHR_DRV : 0) |
359
fibril_mutex_lock(&d->lock);
361
/* Program a Read Sectors operation. */
363
pio_write_8(&cmd->drive_head, drv_head);
364
pio_write_8(&cmd->sector_count, 1);
365
pio_write_8(&cmd->sector_number, s);
366
pio_write_8(&cmd->cylinder_low, c & 0xff);
367
pio_write_8(&cmd->cylinder_high, c >> 16);
368
pio_write_8(&cmd->command, CMD_READ_SECTORS);
370
/* Read data from the disk buffer. */
372
for (i = 0; i < block_size / 2; i++) {
374
status = pio_read_8(&cmd->status);
375
} while ((status & SR_DRDY) == 0);
377
data = pio_read_16(&cmd->data_port);
378
((uint16_t *) buf)[i] = data;
381
fibril_mutex_unlock(&d->lock);
385
static int ata_bd_write_block(int disk_id, uint64_t blk_idx, size_t blk_cnt,
397
/* Check device bounds. */
398
if (blk_idx >= d->blocks)
402
c = blk_idx / (d->heads * d->sectors);
403
idx = blk_idx % (d->heads * d->sectors);
405
h = idx / d->sectors;
406
s = 1 + (idx % d->sectors);
408
/* New value for Drive/Head register */
410
((disk_id != 0) ? DHR_DRV : 0) |
413
fibril_mutex_lock(&d->lock);
415
/* Program a Read Sectors operation. */
417
pio_write_8(&cmd->drive_head, drv_head);
418
pio_write_8(&cmd->sector_count, 1);
419
pio_write_8(&cmd->sector_number, s);
420
pio_write_8(&cmd->cylinder_low, c & 0xff);
421
pio_write_8(&cmd->cylinder_high, c >> 16);
422
pio_write_8(&cmd->command, CMD_WRITE_SECTORS);
424
/* Write data to the disk buffer. */
426
for (i = 0; i < block_size / 2; i++) {
428
status = pio_read_8(&cmd->status);
429
} while ((status & SR_DRDY) == 0);
431
pio_write_16(&cmd->data_port, ((uint16_t *) buf)[i]);
434
fibril_mutex_unlock(&d->lock);