source: mainline/uspace/drv/block/pc-floppy/pc-floppy.c

Last change on this file was 07039850, checked in by Jiri Svoboda <jiri@…>, 5 months ago

Implement quiesce in ISA and PCI IDE and in PC Floppy.

  • Property mode set to 100644
File size: 26.3 KB
Line 
1/*
2 * Copyright (c) 2025 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 pc-floppy
30 * @{
31 */
32
33/**
34 * @file
35 * @brief PC floppy disk driver
36 */
37
38#include <bd_srv.h>
39#include <ddi.h>
40#include <ddf/interrupt.h>
41#include <ddf/log.h>
42#include <async.h>
43#include <as.h>
44#include <errno.h>
45#include <fibril_synch.h>
46#include <macros.h>
47#include <perf.h>
48#include <stdint.h>
49#include <stdbool.h>
50#include <stdio.h>
51#include <stddef.h>
52#include <str.h>
53#include <str_error.h>
54#include <inttypes.h>
55#include <errno.h>
56
57#include "pc-floppy.h"
58
59static errno_t pc_fdc_init_io(pc_fdc_t *);
60static void pc_fdc_fini_io(pc_fdc_t *);
61static errno_t pc_fdc_init_irq(pc_fdc_t *);
62static void pc_fdc_fini_irq(pc_fdc_t *);
63static void pc_fdc_irq_handler(ipc_call_t *, void *);
64
65static errno_t pc_fdc_drive_create(pc_fdc_t *, unsigned, pc_fdc_drive_t **);
66
67static errno_t pc_fdc_reset(pc_fdc_t *);
68static errno_t pc_fdc_read_id(pc_fdc_t *, bool, uint8_t, uint8_t);
69static errno_t pc_fdc_sense_int_sts(pc_fdc_t *);
70
71static errno_t pc_fdc_bd_open(bd_srvs_t *, bd_srv_t *);
72static errno_t pc_fdc_bd_close(bd_srv_t *);
73static errno_t pc_fdc_bd_read_blocks(bd_srv_t *, uint64_t, size_t, void *, size_t);
74static errno_t pc_fdc_bd_read_toc(bd_srv_t *, uint8_t, void *, size_t);
75static errno_t pc_fdc_bd_write_blocks(bd_srv_t *, uint64_t, size_t, const void *,
76 size_t);
77static errno_t pc_fdc_bd_get_block_size(bd_srv_t *, size_t *);
78static errno_t pc_fdc_bd_get_num_blocks(bd_srv_t *, aoff64_t *);
79static errno_t pc_fdc_bd_sync_cache(bd_srv_t *, aoff64_t, size_t);
80
81static bd_ops_t pc_fdc_bd_ops = {
82 .open = pc_fdc_bd_open,
83 .close = pc_fdc_bd_close,
84 .read_blocks = pc_fdc_bd_read_blocks,
85 .read_toc = pc_fdc_bd_read_toc,
86 .write_blocks = pc_fdc_bd_write_blocks,
87 .get_block_size = pc_fdc_bd_get_block_size,
88 .get_num_blocks = pc_fdc_bd_get_num_blocks,
89 .sync_cache = pc_fdc_bd_sync_cache
90};
91
92enum {
93 msr_read_cycles = 100,
94 fdc_def_dma_buf_size = 4096
95};
96
97static const irq_pio_range_t pc_fdc_irq_ranges[] = {
98 {
99 .base = 0,
100 .size = sizeof(pc_fdc_regs_t)
101 }
102};
103
104/** PC floppy interrupt pseudo code.
105 *
106 * Floppy interrupts are complex. Since we don't want to change the handler
107 * all the time, need to handle both status-ful and status-less IRQs.
108 *
109 * XXX Do we need to wait for MSR.RQM == 1? That's really difficult
110 * in pseudocode.
111 *
112 * if (MSR.DIO == 0)
113 * send SENSE INT STATUS;
114 *
115 * st[0] = read DATA;
116 * [repeat 5 times]
117 * if (MSR.DIO == 1)
118 * st[1] = read DATA;
119 *
120 */
121static const irq_cmd_t pc_fdc_irq_cmds[] = {
122 {
123 .cmd = CMD_ACCEPT
124 }
125
126};
127
128/** Create PC floppy controller driver instnace.
129 *
130 * @param dev DDF device
131 * @param res HW resources
132 * @param rfdc Place to store pointer to floppy controller
133 */
134errno_t pc_fdc_create(ddf_dev_t *dev, pc_fdc_hwres_t *res, pc_fdc_t **rfdc)
135{
136 errno_t rc;
137 pc_fdc_t *fdc;
138 bool irq_inited = false;
139 void *buffer = NULL;
140
141 ddf_msg(LVL_DEBUG, "pc_fdc_init()");
142
143 fdc = ddf_dev_data_alloc(dev, sizeof(pc_fdc_t));
144 if (fdc == NULL) {
145 ddf_msg(LVL_ERROR, "Failed allocating FDC.");
146 rc = ENOMEM;
147 goto error;
148 }
149
150 fdc->dev = dev;
151
152 fibril_mutex_initialize(&fdc->lock);
153 fdc->regs_physical = res->regs;
154 fdc->irq = res->irq;
155 fdc->dma = res->dma;
156
157 ddf_msg(LVL_NOTE, "I/O address %p", (void *)fdc->regs_physical);
158
159 ddf_msg(LVL_DEBUG, "Init I/O");
160 rc = pc_fdc_init_io(fdc);
161 if (rc != EOK)
162 return rc;
163
164 ddf_msg(LVL_DEBUG, "Init IRQ");
165 rc = pc_fdc_init_irq(fdc);
166 if (rc != EOK) {
167 ddf_msg(LVL_ERROR, "Init IRQ failed");
168 return rc;
169 }
170
171 irq_inited = true;
172
173 ddf_msg(LVL_DEBUG, "pc_fdc_ctrl_init(): read ID");
174
175 fdc->dma_buf_size = fdc_def_dma_buf_size;
176
177 buffer = AS_AREA_ANY;
178 rc = dmamem_map_anonymous(fdc->dma_buf_size, DMAMEM_1MiB | 0xffff,
179 AS_AREA_WRITE | AS_AREA_READ, 0, &fdc->dma_buf_pa, &buffer);
180 if (rc != EOK) {
181 ddf_msg(LVL_ERROR, "Failed allocating PRD table.");
182 goto error;
183 }
184
185 fdc->dma_buf = buffer;
186
187 ddf_msg(LVL_DEBUG, "pc_fdc_ctrl_init: reset controller...");
188 (void)pc_fdc_reset(fdc);
189 ddf_msg(LVL_DEBUG, "pc_fdc_ctrl_init: read_ID..");
190
191 ddf_msg(LVL_DEBUG, "pc_fdc_ctrl_init: MSR=0x%x",
192 pio_read_8(&fdc->regs->msr));
193 ddf_msg(LVL_DEBUG, "pc_fdc_ctrl_init: DIR=0x%x",
194 pio_read_8(&fdc->regs->dir));
195 ddf_msg(LVL_DEBUG, "pc_fdc_ctrl_init: SRA=0x%x",
196 pio_read_8(&fdc->regs->sra));
197 ddf_msg(LVL_DEBUG, "pc_fdc_ctrl_init: SRB=0x%x",
198 pio_read_8(&fdc->regs->srb));
199
200 rc = pc_fdc_sense_int_sts(fdc);
201 if (rc != EOK)
202 return rc;
203 rc = pc_fdc_sense_int_sts(fdc);
204 if (rc != EOK)
205 return rc;
206 rc = pc_fdc_sense_int_sts(fdc);
207 if (rc != EOK)
208 return rc;
209 rc = pc_fdc_sense_int_sts(fdc);
210 if (rc != EOK)
211 return rc;
212
213 /* Read ID MFM, D0, H0 */
214 rc = pc_fdc_read_id(fdc, true, 0, 0);
215 ddf_msg(LVL_DEBUG, "pc_fdc_ctrl_init: read_ID -> %d", rc);
216
217 rc = pc_fdc_drive_create(fdc, 0, &fdc->drive[0]);
218 if (rc != EOK) {
219 ddf_msg(LVL_ERROR, "pc_fdc_ctrl_init: pc_fdc_drive_create "
220 "failed");
221 goto error;
222 }
223
224 fdc->drive[0]->sec_size = 512;
225 fdc->drive[0]->cylinders = 80;
226 fdc->drive[0]->heads = 2;
227 fdc->drive[0]->sectors = 18;
228
229 ddf_msg(LVL_DEBUG, "pc_fdc_ctrl_init: DONE");
230 return EOK;
231error:
232 if (buffer != NULL)
233 dmamem_unmap_anonymous(buffer);
234 if (irq_inited)
235 pc_fdc_fini_irq(fdc);
236 pc_fdc_fini_io(fdc);
237 return rc;
238}
239
240/** Destroy floppy controller instance.
241 *
242 * @param fdc Floppy controller
243 */
244errno_t pc_fdc_destroy(pc_fdc_t *fdc)
245{
246 ddf_msg(LVL_DEBUG, ": pc_fdc_destroy()");
247
248 fibril_mutex_lock(&fdc->lock);
249
250 pc_fdc_fini_irq(fdc);
251 pc_fdc_fini_io(fdc);
252 fibril_mutex_unlock(&fdc->lock);
253
254 free(fdc);
255 return EOK;
256}
257
258/** Quiesce floppy controller.
259 *
260 * @param fdc Floppy controller
261 */
262void pc_fdc_quiesce(pc_fdc_t *fdc)
263{
264 (void)pc_fdc_reset(fdc);
265}
266
267/** Enable device I/O.
268 *
269 * @param fdc Floppy controller
270 * @return EOK on success or an error code
271 */
272static errno_t pc_fdc_init_io(pc_fdc_t *fdc)
273{
274 errno_t rc;
275 void *vaddr;
276
277 rc = pio_enable((void *) fdc->regs_physical, sizeof(pc_fdc_regs_t),
278 &vaddr);
279 if (rc != EOK) {
280 ddf_msg(LVL_ERROR, "Cannot initialize device I/O space.");
281 return rc;
282 }
283
284 fdc->regs = vaddr;
285 return EOK;
286}
287
288/** Clean up device I/O.
289 *
290 * @param fdc Floppy controller
291 */
292static void pc_fdc_fini_io(pc_fdc_t *fdc)
293{
294 (void)fdc;
295 /* XXX TODO */
296}
297
298/** Initialize IRQ.
299 *
300 * @param fdc Floppy controller
301 * @return EOK on success or an error code
302 */
303static errno_t pc_fdc_init_irq(pc_fdc_t *fdc)
304{
305 irq_code_t irq_code;
306 irq_pio_range_t *ranges;
307 irq_cmd_t *cmds;
308 async_sess_t *parent_sess;
309 errno_t rc;
310
311 if (fdc->irq < 0)
312 return EOK;
313
314 ranges = malloc(sizeof(pc_fdc_irq_ranges));
315 if (ranges == NULL)
316 return ENOMEM;
317
318 cmds = malloc(sizeof(pc_fdc_irq_cmds));
319 if (cmds == NULL) {
320 free(cmds);
321 return ENOMEM;
322 }
323
324 memcpy(ranges, &pc_fdc_irq_ranges, sizeof(pc_fdc_irq_ranges));
325 ranges[0].base = fdc->regs_physical;
326 memcpy(cmds, &pc_fdc_irq_cmds, sizeof(pc_fdc_irq_cmds));
327
328 irq_code.rangecount = sizeof(pc_fdc_irq_ranges) / sizeof(irq_pio_range_t);
329 irq_code.ranges = ranges;
330 irq_code.cmdcount = sizeof(pc_fdc_irq_cmds) / sizeof(irq_cmd_t);
331 irq_code.cmds = cmds;
332
333 ddf_msg(LVL_NOTE, "IRQ %d", fdc->irq);
334
335 rc = register_interrupt_handler(fdc->dev, fdc->irq,
336 pc_fdc_irq_handler, (void *)fdc, &irq_code, &fdc->ihandle);
337 if (rc != EOK) {
338 ddf_msg(LVL_ERROR, "Error registering IRQ.");
339 goto error;
340 }
341
342 parent_sess = ddf_dev_parent_sess_get(fdc->dev);
343
344 rc = hw_res_enable_interrupt(parent_sess, fdc->irq);
345 if (rc != EOK) {
346 ddf_msg(LVL_ERROR, "Error enabling IRQ.");
347 return rc;
348 }
349
350 ddf_msg(LVL_DEBUG, "Interrupt handler registered");
351 free(ranges);
352 free(cmds);
353 return EOK;
354error:
355 free(ranges);
356 free(cmds);
357 return rc;
358}
359
360/** Clean up IRQ.
361 *
362 * @param fdc Floppy disk controller
363 */
364static void pc_fdc_fini_irq(pc_fdc_t *fdc)
365{
366 errno_t rc;
367 async_sess_t *parent_sess;
368
369 parent_sess = ddf_dev_parent_sess_get(fdc->dev);
370
371 rc = hw_res_disable_interrupt(parent_sess, fdc->irq);
372 if (rc != EOK)
373 ddf_msg(LVL_ERROR, "Error disabling IRQ.");
374
375 (void) unregister_interrupt_handler(fdc->dev, fdc->ihandle);
376}
377
378/** Get DDF function name for drive.
379 *
380 * @param fdc Floppy disk controller
381 * @param idx Drive index
382 * @return Function name (newly allocated string)
383 */
384static char *pc_fdc_fun_name(pc_fdc_t *fdc, unsigned idx)
385{
386 char *fun_name;
387
388 if (asprintf(&fun_name, "d%u", idx) < 0)
389 return NULL;
390
391 return fun_name;
392}
393
394/** Block device connection handler.
395 *
396 * @param icall Incoming call (pc_fdc_drive_t *)
397 * @param arg Argument
398 */
399static void pc_fdc_connection(ipc_call_t *icall, void *arg)
400{
401 pc_fdc_drive_t *drive;
402
403 drive = (pc_fdc_drive_t *)ddf_fun_data_get((ddf_fun_t *)arg);
404 bd_conn(icall, &drive->bds);
405}
406
407/** Create floppy drive object.
408 *
409 * @param fdc Floppy disk controller
410 * @param idx Drive index
411 * @param rdrive Place to store pointer to drive
412 *
413 * @return EOK on success or an error code
414 */
415static errno_t pc_fdc_drive_create(pc_fdc_t *fdc, unsigned idx,
416 pc_fdc_drive_t **rdrive)
417{
418 errno_t rc;
419 char *fun_name = NULL;
420 ddf_fun_t *fun = NULL;
421 pc_fdc_drive_t *drive = NULL;
422 bool bound = false;
423
424 fun_name = pc_fdc_fun_name(fdc, idx);
425 if (fun_name == NULL) {
426 ddf_msg(LVL_ERROR, "Out of memory.");
427 rc = ENOMEM;
428 goto error;
429 }
430
431 fun = ddf_fun_create(fdc->dev, fun_exposed, fun_name);
432 if (fun == NULL) {
433 ddf_msg(LVL_ERROR, "Failed creating DDF function.");
434 rc = ENOMEM;
435 goto error;
436 }
437
438 /* Allocate soft state */
439 drive = ddf_fun_data_alloc(fun, sizeof(pc_fdc_drive_t));
440 if (drive == NULL) {
441 ddf_msg(LVL_ERROR, "Failed allocating softstate.");
442 rc = ENOMEM;
443 goto error;
444 }
445
446 drive->fdc = fdc;
447 drive->fun = fun;
448
449 bd_srvs_init(&drive->bds);
450 drive->bds.ops = &pc_fdc_bd_ops;
451 drive->bds.sarg = (void *)drive;
452
453 /* Set up a connection handler. */
454 ddf_fun_set_conn_handler(fun, pc_fdc_connection);
455
456 rc = ddf_fun_bind(fun);
457 if (rc != EOK) {
458 ddf_msg(LVL_ERROR, "Failed binding DDF function %s: %s",
459 fun_name, str_error(rc));
460 goto error;
461 }
462
463 bound = true;
464
465#if 0
466 rc = ddf_fun_add_to_category(fun, "partition");
467 if (rc != EOK) {
468 ddf_msg(LVL_ERROR, "Failed adding function %s to "
469 "category 'partition': %s", fun_name, str_error(rc));
470 goto error;
471 }
472#endif
473
474 free(fun_name);
475 *rdrive = drive;
476 return EOK;
477error:
478 if (bound)
479 ddf_fun_unbind(fun);
480 if (fun != NULL)
481 ddf_fun_destroy(fun);
482 if (fun_name != NULL)
483 free(fun_name);
484
485 return rc;
486}
487
488/** Send byte to FDC data register.
489 *
490 * @param fdc Floppy controller
491 * @param byte Data byte
492 *
493 * @return EOK on success or an error code
494 */
495static errno_t pc_fdc_send_byte(pc_fdc_t *fdc, uint8_t byte)
496{
497 unsigned cnt;
498 uint8_t status;
499 stopwatch_t sw;
500 nsec_t nsec;
501
502 /*
503 * We need to wait for up to 250 usec in total
504 * per Intel 82077AA programming guidelines
505 */
506 stopwatch_init(&sw);
507 stopwatch_start(&sw);
508
509 status = pio_read_8(&fdc->regs->msr);
510 ddf_msg(LVL_DEBUG, "pc_fdc_send_byte: status=0x%x", status);
511 do {
512 cnt = msr_read_cycles;
513 while (cnt > 0) {
514 if ((status & fmsr_rqm) != 0 &&
515 (status & fmsr_dio) == 0) {
516 pio_write_8(&fdc->regs->data, byte);
517 return EOK;
518 }
519
520 --cnt;
521 status = pio_read_8(&fdc->regs->msr);
522 }
523
524 stopwatch_stop(&sw);
525 nsec = stopwatch_get_nanos(&sw);
526 ddf_msg(LVL_DEBUG, "nsec=%lld", nsec);
527 } while (nsec < 1000 * msr_max_wait_usec);
528
529 ddf_msg(LVL_ERROR, "pc_fdc_send_byte: FAILED (status=0x%x)", status);
530 return EIO;
531}
532
533/** Send a block of data to FDC data register.
534 *
535 * @param fdc Floppy controller
536 * @param data Data block
537 * @parma nbytes Size of data block in bytes
538 *
539 * @return EOK on success or an error code
540 */
541static errno_t pc_fdc_send(pc_fdc_t *fdc, const void *data, size_t nbytes)
542{
543 size_t i;
544 errno_t rc;
545 uint8_t status;
546
547 for (i = 0; i < nbytes; i++) {
548 rc = pc_fdc_send_byte(fdc, ((uint8_t *)data)[i]);
549 if (rc != EOK)
550 return rc;
551 }
552
553 status = pio_read_8(&fdc->regs->msr);
554 ddf_msg(LVL_DEBUG, "pc_fdc_send: final status=0x%x", status);
555 return EOK;
556}
557
558/** Get byte from FDC data register.
559 *
560 * @param fdc Floppy controller
561 * @param byte Place to store data byte
562 *
563 * @return EOK on success or an error code
564 */
565static errno_t pc_fdc_get_byte(pc_fdc_t *fdc, uint8_t *byte)
566{
567 unsigned cnt;
568 uint8_t status;
569 stopwatch_t sw;
570 nsec_t nsec;
571
572 /*
573 * We need to wait for up to 250 usec in total
574 * per Intel 82077AA programming guidelines
575 */
576 stopwatch_init(&sw);
577 stopwatch_start(&sw);
578
579 status = pio_read_8(&fdc->regs->msr);
580 ddf_msg(LVL_DEBUG, "pc_fdc_get_byte: status=0x%x", status);
581 do {
582 cnt = msr_read_cycles;
583 while (cnt > 0) {
584 if ((status & fmsr_rqm) != 0 &&
585 (status & fmsr_dio) != 0) {
586 *byte = pio_read_8(&fdc->regs->data);
587 return EOK;
588 }
589
590 --cnt;
591 status = pio_read_8(&fdc->regs->msr);
592 }
593
594 stopwatch_stop(&sw);
595 nsec = stopwatch_get_nanos(&sw);
596 } while (nsec / 1000 < 1000 * msr_max_wait_usec);
597
598 ddf_msg(LVL_ERROR, "pc_fdc_get_byte: FAILED (status=0x%x)", status);
599 return EIO;
600}
601
602/** Receive a block of data to FDC data register.
603 *
604 * @param fdc Floppy controller
605 * @param buf Data buffer
606 * @param bsize Size of data buffer in bytes
607 *
608 * @return EOK on success or an error code
609 */
610static errno_t pc_fdc_get(pc_fdc_t *fdc, void *buf, size_t bsize)
611{
612 size_t i;
613 errno_t rc;
614 uint8_t status;
615
616 for (i = 0; i < bsize; i++) {
617 rc = pc_fdc_get_byte(fdc, &((uint8_t *)buf)[i]);
618 if (rc != EOK) {
619 ddf_msg(LVL_ERROR, "pc_fdc_get: abort after "
620 "reading %zu bytes", i);
621 return rc;
622 }
623 }
624
625 ddf_msg(LVL_DEBUG, "pc_fdc_get: successfully read %zu bytes", i);
626 status = pio_read_8(&fdc->regs->msr);
627 ddf_msg(LVL_DEBUG, "pc_fdc_get: final status=0x%x", status);
628 return EOK;
629}
630
631/** Reset floppy controller.
632 *
633 * @param fdc Floppy controller
634 * @return EOK on success or an error code
635 */
636static errno_t pc_fdc_reset(pc_fdc_t *fdc)
637{
638 uint8_t dor;
639
640 /* Use DSR reset for 82072 (or older) compatibility */
641 pio_write_8(&fdc->regs->dsr, fdsr_sw_reset | fdsr_drate_500kbps);
642
643 /* Clear DOR reset in case it was set (i.e., nreset := 1) */
644 dor = pio_read_8(&fdc->regs->dor);
645 ddf_msg(LVL_DEBUG, "pc_fdc_reset: old DOR=0x%x, DOR := 0x%x", dor,
646 dor & ~fdor_nreset);
647 pio_write_8(&fdc->regs->dor, dor & ~fdor_nreset);
648
649 dor = pio_read_8(&fdc->regs->dor);
650 ddf_msg(LVL_DEBUG, "pc_fdc_reset: read DOR: value= 0x%x", dor);
651
652 fibril_usleep(4);
653
654 ddf_msg(LVL_DEBUG, "pc_fdc_reset: old DOR=0x%x, DOR := 0x%x", dor,
655 dor | fdor_nreset | fdor_ndmagate);
656 pio_write_8(&fdc->regs->dor, dor | fdor_nreset | fdor_ndmagate);
657
658 dor = pio_read_8(&fdc->regs->dor);
659 ddf_msg(LVL_DEBUG, "pc_fdc_reset: read DOR: value= 0x%x", dor);
660
661 return EOK;
662}
663
664/** Perform Read ID command.
665 *
666 * @param fdc Floppy controller
667 * @param mfm Enable MFM (double density) mode
668 * @param drive Drive (0-3)
669 * @param head Head (0-1)
670 *
671 * @return EOK on success or an error code
672 */
673static errno_t pc_fdc_read_id(pc_fdc_t *fdc, bool mfm, uint8_t drive,
674 uint8_t head)
675{
676 pc_fdc_read_id_data_t cmd;
677 pc_fdc_cmd_status_t status;
678 uint8_t dor;
679 errno_t rc;
680
681 dor = pio_read_8(&fdc->regs->dor);
682 ddf_msg(LVL_DEBUG, "pc_fdc_read_id: read DOR: value= 0x%x", dor);
683
684 dor |= fdor_me0; /* turn drive 0 motor on */
685 dor = (dor & ~0x03) | 0x00; /* select drive 0 */
686 pio_write_8(&fdc->regs->dor, dor);
687 ddf_msg(LVL_DEBUG, "pc_fdc_read_id: DOR := 0x%x", dor);
688
689 dor = pio_read_8(&fdc->regs->dor);
690 ddf_msg(LVL_DEBUG, "pc_fdc_read_id: read DOR: value= 0x%x", dor);
691
692 /* 500 ms to let drive spin up */
693 fibril_usleep(500 * 1000);
694
695 cmd.flags_cc = fcf_mf | fcc_read_id;
696 cmd.hd_us = 0x00;
697
698 ddf_msg(LVL_DEBUG, "read ID: send");
699 rc = pc_fdc_send(fdc, &cmd, sizeof(cmd));
700 if (rc != EOK) {
701 ddf_msg(LVL_WARN, "Failed sending READ ID command.");
702 return rc;
703 }
704
705 ddf_msg(LVL_DEBUG, "read ID: get");
706 rc = pc_fdc_get(fdc, &status, sizeof(status));
707 if (rc != EOK) {
708 ddf_msg(LVL_WARN, "Failed getting status for READ ID");
709 return rc;
710 }
711
712 ddf_msg(LVL_DEBUG, "read ID: DONE");
713 ddf_msg(LVL_DEBUG, "st0=0x%x st1=0x%x st2=0x%x cyl=%u head=%u rec=%u "
714 "number=%u", status.st0, status.st1, status.st2,
715 status.cyl, status.head, status.rec, status.number);
716
717 /* Check for success status */
718 if ((status.st0 & fsr0_ic_mask) != 0)
719 return EIO;
720
721 return EOK;
722}
723
724/** Perform Read Data command.
725 *
726 * @param drive Floppy drive
727 * @param cyl Cylinder
728 * @param head Head
729 * @param sec Sector
730 * @param buf Destination buffer
731 * @param buf_size Destination buffer size
732 *
733 * @return EOK on success or an error code
734 */
735static errno_t pc_fdc_drive_read_data(pc_fdc_drive_t *drive,
736 uint8_t cyl, uint8_t head, uint8_t sec, void *buf, size_t buf_size)
737{
738 pc_fdc_t *fdc = drive->fdc;
739 pc_fdc_cmd_data_t cmd;
740 pc_fdc_cmd_status_t status;
741 async_sess_t *sess;
742 size_t csize;
743 errno_t rc;
744
745 ddf_msg(LVL_DEBUG, "pc_fdc_drive_read_data");
746
747 memset(fdc->dma_buf, 0, fdc->dma_buf_size);
748
749 sess = ddf_dev_parent_sess_get(fdc->dev);
750 ddf_msg(LVL_DEBUG, "hw_res_dma_channel_setup(sess=%p, chan=%d "
751 "pa=%" PRIuPTR " size=%zu", sess, fdc->dma, fdc->dma_buf_pa,
752 fdc->dma_buf_size);
753 rc = hw_res_dma_channel_setup(sess, fdc->dma, fdc->dma_buf_pa,
754 fdc->dma_buf_size, DMA_MODE_READ | DMA_MODE_AUTO |
755 DMA_MODE_ON_DEMAND);
756 ddf_msg(LVL_DEBUG, "hw_res_dma_channel_setup->%d", rc);
757
758 cmd.flags_cc = fcf_mf | fcc_read_data;
759 cmd.hd_us = (head & 1) << 2 | 0x00 /* drive 0 */;
760 cmd.cyl = cyl;
761 cmd.head = head;
762 cmd.rec = sec;
763 cmd.number = 2; /* 512 bytes */
764 cmd.eot = sec;
765 cmd.gpl = 0x1b;
766 cmd.dtl = 0xff;
767
768 ddf_msg(LVL_DEBUG, "read data: send");
769 rc = pc_fdc_send(fdc, &cmd, sizeof(cmd));
770 if (rc != EOK) {
771 ddf_msg(LVL_WARN, "Failed sending Read Data command.");
772 return rc;
773 }
774
775 ddf_msg(LVL_DEBUG, "read data: get");
776 rc = pc_fdc_get(fdc, &status, sizeof(status));
777 if (rc != EOK) {
778 ddf_msg(LVL_WARN, "Failed getting status for Read Data");
779 return rc;
780 }
781
782 ddf_msg(LVL_DEBUG, "read data: DONE");
783 ddf_msg(LVL_DEBUG, "st0=0x%x st1=0x%x st2=0x%x cyl=%u head=%u rec=%u "
784 "number=%u", status.st0, status.st1, status.st2,
785 status.cyl, status.head, status.rec, status.number);
786
787 /* Check for success status */
788 if ((status.st0 & fsr0_ic_mask) != 0)
789 return EIO;
790
791 /* Copy data from DMA buffer to destination buffer */
792 csize = min(fdc->dma_buf_size, buf_size);
793 memcpy(buf, fdc->dma_buf, csize);
794
795 return EOK;
796}
797
798/** Perform Write Data command.
799 *
800 * @param drive Floppy drive
801 * @param cyl Cylinder
802 * @param head Head
803 * @param sec Sector
804 * @param buf Source buffer
805 * @param buf_size Source buffer size
806 *
807 * @return EOK on success or an error code
808 */
809static errno_t pc_fdc_drive_write_data(pc_fdc_drive_t *drive,
810 uint8_t cyl, uint8_t head, uint8_t sec, const void *buf, size_t buf_size)
811{
812 pc_fdc_t *fdc = drive->fdc;
813 pc_fdc_cmd_data_t cmd;
814 pc_fdc_cmd_status_t status;
815 async_sess_t *sess;
816 size_t csize;
817 errno_t rc;
818
819 ddf_msg(LVL_DEBUG, "pc_fdc_drive_write_data");
820
821 /* Copy data from source buffer to DMA buffer */
822 csize = min(fdc->dma_buf_size, buf_size);
823 memcpy(fdc->dma_buf, buf, csize);
824
825 sess = ddf_dev_parent_sess_get(fdc->dev);
826 ddf_msg(LVL_DEBUG, "hw_res_dma_channel_setup(sess=%p, chan=%d "
827 "pa=%" PRIuPTR " size=%zu", sess, fdc->dma, fdc->dma_buf_pa,
828 fdc->dma_buf_size);
829 rc = hw_res_dma_channel_setup(sess, fdc->dma, fdc->dma_buf_pa,
830 fdc->dma_buf_size, DMA_MODE_WRITE | DMA_MODE_AUTO |
831 DMA_MODE_ON_DEMAND);
832 ddf_msg(LVL_DEBUG, "hw_res_dma_channel_setup->%d", rc);
833
834 cmd.flags_cc = fcf_mf | fcc_write_data;
835 cmd.hd_us = (head & 1) << 2 | 0x00 /* drive 0 */;
836 cmd.cyl = cyl;
837 cmd.head = head;
838 cmd.rec = sec;
839 cmd.number = 2; /* 512 bytes */
840 cmd.eot = sec;
841 cmd.gpl = 0x1b;
842 cmd.dtl = 0xff;
843
844 ddf_msg(LVL_DEBUG, "write data: send");
845 rc = pc_fdc_send(fdc, &cmd, sizeof(cmd));
846 if (rc != EOK) {
847 ddf_msg(LVL_WARN, "Failed sending Write Data command.");
848 return rc;
849 }
850
851 ddf_msg(LVL_DEBUG, "write data: get");
852 rc = pc_fdc_get(fdc, &status, sizeof(status));
853 if (rc != EOK) {
854 ddf_msg(LVL_WARN, "Failed getting status for Write Data");
855 return rc;
856 }
857
858 ddf_msg(LVL_DEBUG, "write data: DONE");
859 ddf_msg(LVL_DEBUG, "st0=0x%x st1=0x%x st2=0x%x cyl=%u head=%u rec=%u "
860 "number=%u", status.st0, status.st1, status.st2,
861 status.cyl, status.head, status.rec, status.number);
862
863 /* Check for success status */
864 if ((status.st0 & fsr0_ic_mask) != 0)
865 return EIO;
866
867 return EOK;
868}
869
870/** Perform Sense Interrupt Status command.
871 *
872 * @param fdc Floppy controller
873 * @param mfm Enable MFM (double density) mode
874 * @param drive Drive (0-3)
875 * @param head Head (0-1)
876 *
877 * @return EOK on success or an error code
878 */
879static errno_t pc_fdc_sense_int_sts(pc_fdc_t *fdc)
880{
881 pc_fdc_sense_int_sts_data_t cmd;
882 pc_fdc_sense_int_sts_status_t status;
883 errno_t rc;
884
885 cmd.cc = fcc_sense_int_sts;
886
887 ddf_msg(LVL_DEBUG, "Sense Interrupt Status: send");
888 rc = pc_fdc_send(fdc, &cmd, sizeof(cmd));
889 if (rc != EOK) {
890 ddf_msg(LVL_WARN, "Failed sending Sense Interrupt Status "
891 "command.");
892 return rc;
893 }
894
895 ddf_msg(LVL_DEBUG, "Sense Interrupt Status: get");
896 rc = pc_fdc_get(fdc, &status, sizeof(status));
897 if (rc != EOK) {
898 ddf_msg(LVL_WARN, "Failed getting status for Sense Interrupt "
899 "Status");
900 return rc;
901 }
902
903 ddf_msg(LVL_DEBUG, "Sense Interrupt Status: DONE");
904 ddf_msg(LVL_DEBUG, "st0=0x%x pcn=0x%x", status.st0, status.pcn);
905
906 return EOK;
907}
908
909/** Interrupt handler.
910 *
911 * @param call Call data
912 * @param arg Argument (pc_fdc_channel_t *)
913 */
914static void pc_fdc_irq_handler(ipc_call_t *call, void *arg)
915{
916 pc_fdc_t *fdc = (pc_fdc_t *)arg;
917 uint8_t st0;
918 uint8_t st1;
919 uint8_t st2;
920 uint8_t c, h, n;
921 async_sess_t *parent_sess;
922
923 st0 = ipc_get_arg1(call);
924 st1 = ipc_get_arg2(call);
925 st2 = ipc_get_arg3(call);
926 c = ipc_get_arg4(call);
927 h = ipc_get_arg5(call);
928 n = ipc_get_imethod(call);
929 ddf_msg(LVL_DEBUG, "pc_fdc_irq_handler st0=%x st1=%x st2=%x c=%u h=%u n=%u",
930 st0, st1, st2, c, h, n);
931
932 parent_sess = ddf_dev_parent_sess_get(fdc->dev);
933 hw_res_clear_interrupt(parent_sess, fdc->irq);
934}
935
936/** Get floppy drive from block device service.
937 *
938 * @param bd Block device
939 * @return Floppy drive
940 */
941static pc_fdc_drive_t *bd_srv_drive(bd_srv_t *bd)
942{
943 return (pc_fdc_drive_t *)bd->srvs->sarg;
944}
945
946/** Convert LBA to CHS.
947 *
948 * @param drive Floppy drive
949 * @param ba Logical block address
950 * @param cyl Place to store cylinder number
951 * @param head Place to store head number
952 * @param sec Place to store sector number
953 */
954static void pc_fdc_drive_ba_to_chs(pc_fdc_drive_t *drive, uint64_t ba,
955 uint8_t *cyl, uint8_t *head, uint8_t *sec)
956{
957 unsigned ch;
958
959 *sec = 1 + (ba % drive->sectors);
960 ch = ba / drive->sectors;
961 *head = ch % drive->heads;
962 *cyl = ch / drive->heads;
963}
964
965/** Open block device.
966 *
967 * @param bds Block device server
968 * @param bd Block device
969 * @return EOK on success or an error code
970 */
971static errno_t pc_fdc_bd_open(bd_srvs_t *bds, bd_srv_t *bd)
972{
973 return EOK;
974}
975
976/** Close block device.
977 *
978 * @param bd Block device
979 * @return EOK on success or an error code
980 */
981static errno_t pc_fdc_bd_close(bd_srv_t *bd)
982{
983 return EOK;
984}
985
986/** Read multiple blocks from block device.
987 *
988 * @param bd Block device
989 * @param ba Address of first block
990 * @param cnt Number of blocks
991 * @param buf Destination buffer
992 * @param size Size of destination buffer in bytes
993 *
994 * @return EOK on success or an error code
995 */
996static errno_t pc_fdc_bd_read_blocks(bd_srv_t *bd, uint64_t ba, size_t cnt,
997 void *buf, size_t size)
998{
999 pc_fdc_drive_t *drive = bd_srv_drive(bd);
1000 uint8_t cyl, head, sec;
1001 errno_t rc;
1002
1003 ddf_msg(LVL_DEBUG, "pc_fdc_bd_read_blocks");
1004
1005 if (size < cnt * drive->sec_size) {
1006 rc = EINVAL;
1007 goto error;
1008 }
1009
1010 while (cnt > 0) {
1011 pc_fdc_drive_ba_to_chs(drive, ba, &cyl, &head, &sec);
1012
1013 /* Read one block */
1014 rc = pc_fdc_drive_read_data(drive, cyl, head, sec, buf,
1015 drive->sec_size);
1016 if (rc != EOK)
1017 goto error;
1018
1019 ++ba;
1020 --cnt;
1021 buf += drive->sec_size;
1022 }
1023
1024 return EOK;
1025error:
1026 ddf_msg(LVL_ERROR, "pc_fdc_bd_read_blocks: rc=%d", rc);
1027 return rc;
1028}
1029
1030/** Read TOC from block device.
1031 *
1032 * @param bd Block device
1033 * @param session Session number
1034 * @param buf Destination buffer
1035 * @param size Size of destination buffer in bytes
1036 *
1037 * @return EOK on success or an error code
1038 */
1039static errno_t pc_fdc_bd_read_toc(bd_srv_t *bd, uint8_t session, void *buf, size_t size)
1040{
1041 return ENOTSUP;
1042}
1043
1044/** Write multiple blocks to block device.
1045 *
1046 * @param bd Block device
1047 * @param ba Address of first block
1048 * @param cnt Number of blocks
1049 * @param buf Source buffer
1050 * @param size Size of source buffer in bytes
1051 *
1052 * @return EOK on success or an error code
1053 */
1054static errno_t pc_fdc_bd_write_blocks(bd_srv_t *bd, uint64_t ba, size_t cnt,
1055 const void *buf, size_t size)
1056{
1057 pc_fdc_drive_t *drive = bd_srv_drive(bd);
1058 uint8_t cyl, head, sec;
1059 errno_t rc;
1060
1061 ddf_msg(LVL_DEBUG, "pc_fdc_bd_write_blocks");
1062
1063 if (size < cnt * drive->sec_size) {
1064 rc = EINVAL;
1065 goto error;
1066 }
1067
1068 while (cnt > 0) {
1069 pc_fdc_drive_ba_to_chs(drive, ba, &cyl, &head, &sec);
1070
1071 /* Write one block */
1072 rc = pc_fdc_drive_write_data(drive, cyl, head, sec, buf,
1073 drive->sec_size);
1074 if (rc != EOK)
1075 goto error;
1076
1077 ++ba;
1078 --cnt;
1079 buf += drive->sec_size;
1080 }
1081
1082 return EOK;
1083error:
1084 ddf_msg(LVL_ERROR, "pc_fdc_bd_write_blocks: rc=%d", rc);
1085 return rc;
1086}
1087
1088/** Get device block size. */
1089static errno_t pc_fdc_bd_get_block_size(bd_srv_t *bd, size_t *rbsize)
1090{
1091 pc_fdc_drive_t *drive = bd_srv_drive(bd);
1092
1093 *rbsize = drive->sec_size;
1094 return EOK;
1095}
1096
1097/** Get device number of blocks. */
1098static errno_t pc_fdc_bd_get_num_blocks(bd_srv_t *bd, aoff64_t *rnb)
1099{
1100 pc_fdc_drive_t *drive = bd_srv_drive(bd);
1101
1102 *rnb = drive->cylinders * drive->heads * drive->sectors;
1103 return EOK;
1104}
1105
1106/** Flush cache. */
1107static errno_t pc_fdc_bd_sync_cache(bd_srv_t *bd, uint64_t ba, size_t cnt)
1108{
1109 return EOK;
1110}
1111
1112/**
1113 * @}
1114 */
Note: See TracBrowser for help on using the repository browser.