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

Last change on this file since f32f89a was f32f89a, checked in by Jiri Svoboda <jiri@…>, 14 months ago

Fix format specifiers not correct for 32-bit

  • Property mode set to 100644
File size: 26.2 KB
Line 
1/*
2 * Copyright (c) 2024 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/** Enable device I/O.
259 *
260 * @param fdc Floppy controller
261 * @return EOK on success or an error code
262 */
263static errno_t pc_fdc_init_io(pc_fdc_t *fdc)
264{
265 errno_t rc;
266 void *vaddr;
267
268 rc = pio_enable((void *) fdc->regs_physical, sizeof(pc_fdc_regs_t),
269 &vaddr);
270 if (rc != EOK) {
271 ddf_msg(LVL_ERROR, "Cannot initialize device I/O space.");
272 return rc;
273 }
274
275 fdc->regs = vaddr;
276 return EOK;
277}
278
279/** Clean up device I/O.
280 *
281 * @param fdc Floppy controller
282 */
283static void pc_fdc_fini_io(pc_fdc_t *fdc)
284{
285 (void)fdc;
286 /* XXX TODO */
287}
288
289/** Initialize IRQ.
290 *
291 * @param fdc Floppy controller
292 * @return EOK on success or an error code
293 */
294static errno_t pc_fdc_init_irq(pc_fdc_t *fdc)
295{
296 irq_code_t irq_code;
297 irq_pio_range_t *ranges;
298 irq_cmd_t *cmds;
299 async_sess_t *parent_sess;
300 errno_t rc;
301
302 if (fdc->irq < 0)
303 return EOK;
304
305 ranges = malloc(sizeof(pc_fdc_irq_ranges));
306 if (ranges == NULL)
307 return ENOMEM;
308
309 cmds = malloc(sizeof(pc_fdc_irq_cmds));
310 if (cmds == NULL) {
311 free(cmds);
312 return ENOMEM;
313 }
314
315 memcpy(ranges, &pc_fdc_irq_ranges, sizeof(pc_fdc_irq_ranges));
316 ranges[0].base = fdc->regs_physical;
317 memcpy(cmds, &pc_fdc_irq_cmds, sizeof(pc_fdc_irq_cmds));
318
319 irq_code.rangecount = sizeof(pc_fdc_irq_ranges) / sizeof(irq_pio_range_t);
320 irq_code.ranges = ranges;
321 irq_code.cmdcount = sizeof(pc_fdc_irq_cmds) / sizeof(irq_cmd_t);
322 irq_code.cmds = cmds;
323
324 ddf_msg(LVL_NOTE, "IRQ %d", fdc->irq);
325
326 rc = register_interrupt_handler(fdc->dev, fdc->irq,
327 pc_fdc_irq_handler, (void *)fdc, &irq_code, &fdc->ihandle);
328 if (rc != EOK) {
329 ddf_msg(LVL_ERROR, "Error registering IRQ.");
330 goto error;
331 }
332
333 parent_sess = ddf_dev_parent_sess_get(fdc->dev);
334
335 rc = hw_res_enable_interrupt(parent_sess, fdc->irq);
336 if (rc != EOK) {
337 ddf_msg(LVL_ERROR, "Error enabling IRQ.");
338 return rc;
339 }
340
341 ddf_msg(LVL_DEBUG, "Interrupt handler registered");
342 free(ranges);
343 free(cmds);
344 return EOK;
345error:
346 free(ranges);
347 free(cmds);
348 return rc;
349}
350
351/** Clean up IRQ.
352 *
353 * @param fdc Floppy disk controller
354 */
355static void pc_fdc_fini_irq(pc_fdc_t *fdc)
356{
357 errno_t rc;
358 async_sess_t *parent_sess;
359
360 parent_sess = ddf_dev_parent_sess_get(fdc->dev);
361
362 rc = hw_res_disable_interrupt(parent_sess, fdc->irq);
363 if (rc != EOK)
364 ddf_msg(LVL_ERROR, "Error disabling IRQ.");
365
366 (void) unregister_interrupt_handler(fdc->dev, fdc->ihandle);
367}
368
369/** Get DDF function name for drive.
370 *
371 * @param fdc Floppy disk controller
372 * @param idx Drive index
373 * @return Function name (newly allocated string)
374 */
375static char *pc_fdc_fun_name(pc_fdc_t *fdc, unsigned idx)
376{
377 char *fun_name;
378
379 if (asprintf(&fun_name, "d%u", idx) < 0)
380 return NULL;
381
382 return fun_name;
383}
384
385/** Block device connection handler.
386 *
387 * @param icall Incoming call (pc_fdc_drive_t *)
388 * @param arg Argument
389 */
390static void pc_fdc_connection(ipc_call_t *icall, void *arg)
391{
392 pc_fdc_drive_t *drive;
393
394 drive = (pc_fdc_drive_t *)ddf_fun_data_get((ddf_fun_t *)arg);
395 bd_conn(icall, &drive->bds);
396}
397
398/** Create floppy drive object.
399 *
400 * @param fdc Floppy disk controller
401 * @param idx Drive index
402 * @param rdrive Place to store pointer to drive
403 *
404 * @return EOK on success or an error code
405 */
406static errno_t pc_fdc_drive_create(pc_fdc_t *fdc, unsigned idx,
407 pc_fdc_drive_t **rdrive)
408{
409 errno_t rc;
410 char *fun_name = NULL;
411 ddf_fun_t *fun = NULL;
412 pc_fdc_drive_t *drive = NULL;
413 bool bound = false;
414
415 fun_name = pc_fdc_fun_name(fdc, idx);
416 if (fun_name == NULL) {
417 ddf_msg(LVL_ERROR, "Out of memory.");
418 rc = ENOMEM;
419 goto error;
420 }
421
422 fun = ddf_fun_create(fdc->dev, fun_exposed, fun_name);
423 if (fun == NULL) {
424 ddf_msg(LVL_ERROR, "Failed creating DDF function.");
425 rc = ENOMEM;
426 goto error;
427 }
428
429 /* Allocate soft state */
430 drive = ddf_fun_data_alloc(fun, sizeof(pc_fdc_drive_t));
431 if (drive == NULL) {
432 ddf_msg(LVL_ERROR, "Failed allocating softstate.");
433 rc = ENOMEM;
434 goto error;
435 }
436
437 drive->fdc = fdc;
438 drive->fun = fun;
439
440 bd_srvs_init(&drive->bds);
441 drive->bds.ops = &pc_fdc_bd_ops;
442 drive->bds.sarg = (void *)drive;
443
444 /* Set up a connection handler. */
445 ddf_fun_set_conn_handler(fun, pc_fdc_connection);
446
447 rc = ddf_fun_bind(fun);
448 if (rc != EOK) {
449 ddf_msg(LVL_ERROR, "Failed binding DDF function %s: %s",
450 fun_name, str_error(rc));
451 goto error;
452 }
453
454 bound = true;
455
456#if 0
457 rc = ddf_fun_add_to_category(fun, "partition");
458 if (rc != EOK) {
459 ddf_msg(LVL_ERROR, "Failed adding function %s to "
460 "category 'partition': %s", fun_name, str_error(rc));
461 goto error;
462 }
463#endif
464
465 free(fun_name);
466 *rdrive = drive;
467 return EOK;
468error:
469 if (bound)
470 ddf_fun_unbind(fun);
471 if (fun != NULL)
472 ddf_fun_destroy(fun);
473 if (fun_name != NULL)
474 free(fun_name);
475
476 return rc;
477}
478
479/** Send byte to FDC data register.
480 *
481 * @param fdc Floppy controller
482 * @param byte Data byte
483 *
484 * @return EOK on success or an error code
485 */
486static errno_t pc_fdc_send_byte(pc_fdc_t *fdc, uint8_t byte)
487{
488 unsigned cnt;
489 uint8_t status;
490 stopwatch_t sw;
491 nsec_t nsec;
492
493 /*
494 * We need to wait for up to 250 usec in total
495 * per Intel 82077AA programming guidelines
496 */
497 stopwatch_init(&sw);
498 stopwatch_start(&sw);
499
500 status = pio_read_8(&fdc->regs->msr);
501 ddf_msg(LVL_DEBUG, "pc_fdc_send_byte: status=0x%x", status);
502 do {
503 cnt = msr_read_cycles;
504 while (cnt > 0) {
505 if ((status & fmsr_rqm) != 0 &&
506 (status & fmsr_dio) == 0) {
507 pio_write_8(&fdc->regs->data, byte);
508 return EOK;
509 }
510
511 --cnt;
512 status = pio_read_8(&fdc->regs->msr);
513 }
514
515 stopwatch_stop(&sw);
516 nsec = stopwatch_get_nanos(&sw);
517 ddf_msg(LVL_DEBUG, "nsec=%lld", nsec);
518 } while (nsec < 1000 * msr_max_wait_usec);
519
520 ddf_msg(LVL_ERROR, "pc_fdc_send_byte: FAILED (status=0x%x)", status);
521 return EIO;
522}
523
524/** Send a block of data to FDC data register.
525 *
526 * @param fdc Floppy controller
527 * @param data Data block
528 * @parma nbytes Size of data block in bytes
529 *
530 * @return EOK on success or an error code
531 */
532static errno_t pc_fdc_send(pc_fdc_t *fdc, const void *data, size_t nbytes)
533{
534 size_t i;
535 errno_t rc;
536 uint8_t status;
537
538 for (i = 0; i < nbytes; i++) {
539 rc = pc_fdc_send_byte(fdc, ((uint8_t *)data)[i]);
540 if (rc != EOK)
541 return rc;
542 }
543
544 status = pio_read_8(&fdc->regs->msr);
545 ddf_msg(LVL_DEBUG, "pc_fdc_send: final status=0x%x", status);
546 return EOK;
547}
548
549/** Get byte from FDC data register.
550 *
551 * @param fdc Floppy controller
552 * @param byte Place to store data byte
553 *
554 * @return EOK on success or an error code
555 */
556static errno_t pc_fdc_get_byte(pc_fdc_t *fdc, uint8_t *byte)
557{
558 unsigned cnt;
559 uint8_t status;
560 stopwatch_t sw;
561 nsec_t nsec;
562
563 /*
564 * We need to wait for up to 250 usec in total
565 * per Intel 82077AA programming guidelines
566 */
567 stopwatch_init(&sw);
568 stopwatch_start(&sw);
569
570 status = pio_read_8(&fdc->regs->msr);
571 ddf_msg(LVL_DEBUG, "pc_fdc_get_byte: status=0x%x", status);
572 do {
573 cnt = msr_read_cycles;
574 while (cnt > 0) {
575 if ((status & fmsr_rqm) != 0 &&
576 (status & fmsr_dio) != 0) {
577 *byte = pio_read_8(&fdc->regs->data);
578 return EOK;
579 }
580
581 --cnt;
582 status = pio_read_8(&fdc->regs->msr);
583 }
584
585 stopwatch_stop(&sw);
586 nsec = stopwatch_get_nanos(&sw);
587 } while (nsec / 1000 < 1000 * msr_max_wait_usec);
588
589 ddf_msg(LVL_ERROR, "pc_fdc_get_byte: FAILED (status=0x%x)", status);
590 return EIO;
591}
592
593/** Receive a block of data to FDC data register.
594 *
595 * @param fdc Floppy controller
596 * @param buf Data buffer
597 * @param bsize Size of data buffer in bytes
598 *
599 * @return EOK on success or an error code
600 */
601static errno_t pc_fdc_get(pc_fdc_t *fdc, void *buf, size_t bsize)
602{
603 size_t i;
604 errno_t rc;
605 uint8_t status;
606
607 for (i = 0; i < bsize; i++) {
608 rc = pc_fdc_get_byte(fdc, &((uint8_t *)buf)[i]);
609 if (rc != EOK) {
610 ddf_msg(LVL_ERROR, "pc_fdc_get: abort after "
611 "reading %zu bytes", i);
612 return rc;
613 }
614 }
615
616 ddf_msg(LVL_DEBUG, "pc_fdc_get: successfully read %zu bytes", i);
617 status = pio_read_8(&fdc->regs->msr);
618 ddf_msg(LVL_DEBUG, "pc_fdc_get: final status=0x%x", status);
619 return EOK;
620}
621
622/** Reset floppy controller.
623 *
624 * @param fdc Floppy controller
625 * @return EOK on success or an error code
626 */
627static errno_t pc_fdc_reset(pc_fdc_t *fdc)
628{
629 uint8_t dor;
630
631 /* Use DSR reset for 82072 (or older) compatibility */
632 pio_write_8(&fdc->regs->dsr, fdsr_sw_reset | fdsr_drate_500kbps);
633
634 /* Clear DOR reset in case it was set (i.e., nreset := 1) */
635 dor = pio_read_8(&fdc->regs->dor);
636 ddf_msg(LVL_DEBUG, "pc_fdc_reset: old DOR=0x%x, DOR := 0x%x", dor,
637 dor & ~fdor_nreset);
638 pio_write_8(&fdc->regs->dor, dor & ~fdor_nreset);
639
640 dor = pio_read_8(&fdc->regs->dor);
641 ddf_msg(LVL_DEBUG, "pc_fdc_reset: read DOR: value= 0x%x", dor);
642
643 fibril_usleep(4);
644
645 ddf_msg(LVL_DEBUG, "pc_fdc_reset: old DOR=0x%x, DOR := 0x%x", dor,
646 dor | fdor_nreset | fdor_ndmagate);
647 pio_write_8(&fdc->regs->dor, dor | fdor_nreset | fdor_ndmagate);
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 return EOK;
653}
654
655/** Perform Read ID command.
656 *
657 * @param fdc Floppy controller
658 * @param mfm Enable MFM (double density) mode
659 * @param drive Drive (0-3)
660 * @param head Head (0-1)
661 *
662 * @return EOK on success or an error code
663 */
664static errno_t pc_fdc_read_id(pc_fdc_t *fdc, bool mfm, uint8_t drive,
665 uint8_t head)
666{
667 pc_fdc_read_id_data_t cmd;
668 pc_fdc_cmd_status_t status;
669 uint8_t dor;
670 errno_t rc;
671
672 dor = pio_read_8(&fdc->regs->dor);
673 ddf_msg(LVL_DEBUG, "pc_fdc_read_id: read DOR: value= 0x%x", dor);
674
675 dor |= fdor_me0; /* turn drive 0 motor on */
676 dor = (dor & ~0x03) | 0x00; /* select drive 0 */
677 pio_write_8(&fdc->regs->dor, dor);
678 ddf_msg(LVL_DEBUG, "pc_fdc_read_id: DOR := 0x%x", dor);
679
680 dor = pio_read_8(&fdc->regs->dor);
681 ddf_msg(LVL_DEBUG, "pc_fdc_read_id: read DOR: value= 0x%x", dor);
682
683 /* 500 ms to let drive spin up */
684 fibril_usleep(500 * 1000);
685
686 cmd.flags_cc = fcf_mf | fcc_read_id;
687 cmd.hd_us = 0x00;
688
689 ddf_msg(LVL_DEBUG, "read ID: send");
690 rc = pc_fdc_send(fdc, &cmd, sizeof(cmd));
691 if (rc != EOK) {
692 ddf_msg(LVL_WARN, "Failed sending READ ID command.");
693 return rc;
694 }
695
696 ddf_msg(LVL_DEBUG, "read ID: get");
697 rc = pc_fdc_get(fdc, &status, sizeof(status));
698 if (rc != EOK) {
699 ddf_msg(LVL_WARN, "Failed getting status for READ ID");
700 return rc;
701 }
702
703 ddf_msg(LVL_DEBUG, "read ID: DONE");
704 ddf_msg(LVL_DEBUG, "st0=0x%x st1=0x%x st2=0x%x cyl=%u head=%u rec=%u "
705 "number=%u", status.st0, status.st1, status.st2,
706 status.cyl, status.head, status.rec, status.number);
707
708 /* Check for success status */
709 if ((status.st0 & fsr0_ic_mask) != 0)
710 return EIO;
711
712 return EOK;
713}
714
715/** Perform Read Data command.
716 *
717 * @param drive Floppy drive
718 * @param cyl Cylinder
719 * @param head Head
720 * @param sec Sector
721 * @param buf Destination buffer
722 * @param buf_size Destination buffer size
723 *
724 * @return EOK on success or an error code
725 */
726static errno_t pc_fdc_drive_read_data(pc_fdc_drive_t *drive,
727 uint8_t cyl, uint8_t head, uint8_t sec, void *buf, size_t buf_size)
728{
729 pc_fdc_t *fdc = drive->fdc;
730 pc_fdc_cmd_data_t cmd;
731 pc_fdc_cmd_status_t status;
732 async_sess_t *sess;
733 size_t csize;
734 errno_t rc;
735
736 ddf_msg(LVL_DEBUG, "pc_fdc_drive_read_data");
737
738 memset(fdc->dma_buf, 0, fdc->dma_buf_size);
739
740 sess = ddf_dev_parent_sess_get(fdc->dev);
741 ddf_msg(LVL_DEBUG, "hw_res_dma_channel_setup(sess=%p, chan=%d "
742 "pa=%" PRIuPTR " size=%zu", sess, fdc->dma, fdc->dma_buf_pa,
743 fdc->dma_buf_size);
744 rc = hw_res_dma_channel_setup(sess, fdc->dma, fdc->dma_buf_pa,
745 fdc->dma_buf_size, DMA_MODE_READ | DMA_MODE_AUTO |
746 DMA_MODE_ON_DEMAND);
747 ddf_msg(LVL_DEBUG, "hw_res_dma_channel_setup->%d", rc);
748
749 cmd.flags_cc = fcf_mf | fcc_read_data;
750 cmd.hd_us = (head & 1) << 2 | 0x00 /* drive 0 */;
751 cmd.cyl = cyl;
752 cmd.head = head;
753 cmd.rec = sec;
754 cmd.number = 2; /* 512 bytes */
755 cmd.eot = sec;
756 cmd.gpl = 0x1b;
757 cmd.dtl = 0xff;
758
759 ddf_msg(LVL_DEBUG, "read data: send");
760 rc = pc_fdc_send(fdc, &cmd, sizeof(cmd));
761 if (rc != EOK) {
762 ddf_msg(LVL_WARN, "Failed sending Read Data command.");
763 return rc;
764 }
765
766 ddf_msg(LVL_DEBUG, "read data: get");
767 rc = pc_fdc_get(fdc, &status, sizeof(status));
768 if (rc != EOK) {
769 ddf_msg(LVL_WARN, "Failed getting status for Read Data");
770 return rc;
771 }
772
773 ddf_msg(LVL_DEBUG, "read data: DONE");
774 ddf_msg(LVL_DEBUG, "st0=0x%x st1=0x%x st2=0x%x cyl=%u head=%u rec=%u "
775 "number=%u", status.st0, status.st1, status.st2,
776 status.cyl, status.head, status.rec, status.number);
777
778 /* Check for success status */
779 if ((status.st0 & fsr0_ic_mask) != 0)
780 return EIO;
781
782 /* Copy data from DMA buffer to destination buffer */
783 csize = min(fdc->dma_buf_size, buf_size);
784 memcpy(buf, fdc->dma_buf, csize);
785
786 return EOK;
787}
788
789/** Perform Write Data command.
790 *
791 * @param drive Floppy drive
792 * @param cyl Cylinder
793 * @param head Head
794 * @param sec Sector
795 * @param buf Source buffer
796 * @param buf_size Source buffer size
797 *
798 * @return EOK on success or an error code
799 */
800static errno_t pc_fdc_drive_write_data(pc_fdc_drive_t *drive,
801 uint8_t cyl, uint8_t head, uint8_t sec, const void *buf, size_t buf_size)
802{
803 pc_fdc_t *fdc = drive->fdc;
804 pc_fdc_cmd_data_t cmd;
805 pc_fdc_cmd_status_t status;
806 async_sess_t *sess;
807 size_t csize;
808 errno_t rc;
809
810 ddf_msg(LVL_DEBUG, "pc_fdc_drive_write_data");
811
812 /* Copy data from source buffer to DMA buffer */
813 csize = min(fdc->dma_buf_size, buf_size);
814 memcpy(fdc->dma_buf, buf, csize);
815
816 sess = ddf_dev_parent_sess_get(fdc->dev);
817 ddf_msg(LVL_DEBUG, "hw_res_dma_channel_setup(sess=%p, chan=%d "
818 "pa=%" PRIuPTR " size=%zu", sess, fdc->dma, fdc->dma_buf_pa,
819 fdc->dma_buf_size);
820 rc = hw_res_dma_channel_setup(sess, fdc->dma, fdc->dma_buf_pa,
821 fdc->dma_buf_size, DMA_MODE_WRITE | DMA_MODE_AUTO |
822 DMA_MODE_ON_DEMAND);
823 ddf_msg(LVL_DEBUG, "hw_res_dma_channel_setup->%d", rc);
824
825 cmd.flags_cc = fcf_mf | fcc_write_data;
826 cmd.hd_us = (head & 1) << 2 | 0x00 /* drive 0 */;
827 cmd.cyl = cyl;
828 cmd.head = head;
829 cmd.rec = sec;
830 cmd.number = 2; /* 512 bytes */
831 cmd.eot = sec;
832 cmd.gpl = 0x1b;
833 cmd.dtl = 0xff;
834
835 ddf_msg(LVL_DEBUG, "write data: send");
836 rc = pc_fdc_send(fdc, &cmd, sizeof(cmd));
837 if (rc != EOK) {
838 ddf_msg(LVL_WARN, "Failed sending Write Data command.");
839 return rc;
840 }
841
842 ddf_msg(LVL_DEBUG, "write data: get");
843 rc = pc_fdc_get(fdc, &status, sizeof(status));
844 if (rc != EOK) {
845 ddf_msg(LVL_WARN, "Failed getting status for Write Data");
846 return rc;
847 }
848
849 ddf_msg(LVL_DEBUG, "write data: DONE");
850 ddf_msg(LVL_DEBUG, "st0=0x%x st1=0x%x st2=0x%x cyl=%u head=%u rec=%u "
851 "number=%u", status.st0, status.st1, status.st2,
852 status.cyl, status.head, status.rec, status.number);
853
854 /* Check for success status */
855 if ((status.st0 & fsr0_ic_mask) != 0)
856 return EIO;
857
858 return EOK;
859}
860
861/** Perform Sense Interrupt Status command.
862 *
863 * @param fdc Floppy controller
864 * @param mfm Enable MFM (double density) mode
865 * @param drive Drive (0-3)
866 * @param head Head (0-1)
867 *
868 * @return EOK on success or an error code
869 */
870static errno_t pc_fdc_sense_int_sts(pc_fdc_t *fdc)
871{
872 pc_fdc_sense_int_sts_data_t cmd;
873 pc_fdc_sense_int_sts_status_t status;
874 errno_t rc;
875
876 cmd.cc = fcc_sense_int_sts;
877
878 ddf_msg(LVL_DEBUG, "Sense Interrupt Status: send");
879 rc = pc_fdc_send(fdc, &cmd, sizeof(cmd));
880 if (rc != EOK) {
881 ddf_msg(LVL_WARN, "Failed sending Sense Interrupt Status "
882 "command.");
883 return rc;
884 }
885
886 ddf_msg(LVL_DEBUG, "Sense Interrupt Status: get");
887 rc = pc_fdc_get(fdc, &status, sizeof(status));
888 if (rc != EOK) {
889 ddf_msg(LVL_WARN, "Failed getting status for Sense Interrupt "
890 "Status");
891 return rc;
892 }
893
894 ddf_msg(LVL_DEBUG, "Sense Interrupt Status: DONE");
895 ddf_msg(LVL_DEBUG, "st0=0x%x pcn=0x%x", status.st0, status.pcn);
896
897 return EOK;
898}
899
900/** Interrupt handler.
901 *
902 * @param call Call data
903 * @param arg Argument (pc_fdc_channel_t *)
904 */
905static void pc_fdc_irq_handler(ipc_call_t *call, void *arg)
906{
907 pc_fdc_t *fdc = (pc_fdc_t *)arg;
908 uint8_t st0;
909 uint8_t st1;
910 uint8_t st2;
911 uint8_t c, h, n;
912 async_sess_t *parent_sess;
913
914 st0 = ipc_get_arg1(call);
915 st1 = ipc_get_arg2(call);
916 st2 = ipc_get_arg3(call);
917 c = ipc_get_arg4(call);
918 h = ipc_get_arg5(call);
919 n = ipc_get_imethod(call);
920 ddf_msg(LVL_DEBUG, "pc_fdc_irq_handler st0=%x st1=%x st2=%x c=%u h=%u n=%u",
921 st0, st1, st2, c, h, n);
922
923 parent_sess = ddf_dev_parent_sess_get(fdc->dev);
924 hw_res_clear_interrupt(parent_sess, fdc->irq);
925}
926
927/** Get floppy drive from block device service.
928 *
929 * @param bd Block device
930 * @return Floppy drive
931 */
932static pc_fdc_drive_t *bd_srv_drive(bd_srv_t *bd)
933{
934 return (pc_fdc_drive_t *)bd->srvs->sarg;
935}
936
937/** Convert LBA to CHS.
938 *
939 * @param drive Floppy drive
940 * @param ba Logical block address
941 * @param cyl Place to store cylinder number
942 * @param head Place to store head number
943 * @param sec Place to store sector number
944 */
945static void pc_fdc_drive_ba_to_chs(pc_fdc_drive_t *drive, uint64_t ba,
946 uint8_t *cyl, uint8_t *head, uint8_t *sec)
947{
948 unsigned ch;
949
950 *sec = 1 + (ba % drive->sectors);
951 ch = ba / drive->sectors;
952 *head = ch % drive->heads;
953 *cyl = ch / drive->heads;
954}
955
956/** Open block device.
957 *
958 * @param bds Block device server
959 * @param bd Block device
960 * @return EOK on success or an error code
961 */
962static errno_t pc_fdc_bd_open(bd_srvs_t *bds, bd_srv_t *bd)
963{
964 return EOK;
965}
966
967/** Close block device.
968 *
969 * @param bd Block device
970 * @return EOK on success or an error code
971 */
972static errno_t pc_fdc_bd_close(bd_srv_t *bd)
973{
974 return EOK;
975}
976
977/** Read multiple blocks from block device.
978 *
979 * @param bd Block device
980 * @param ba Address of first block
981 * @param cnt Number of blocks
982 * @param buf Destination buffer
983 * @param size Size of destination buffer in bytes
984 *
985 * @return EOK on success or an error code
986 */
987static errno_t pc_fdc_bd_read_blocks(bd_srv_t *bd, uint64_t ba, size_t cnt,
988 void *buf, size_t size)
989{
990 pc_fdc_drive_t *drive = bd_srv_drive(bd);
991 uint8_t cyl, head, sec;
992 errno_t rc;
993
994 ddf_msg(LVL_DEBUG, "pc_fdc_bd_read_blocks");
995
996 if (size < cnt * drive->sec_size) {
997 rc = EINVAL;
998 goto error;
999 }
1000
1001 while (cnt > 0) {
1002 pc_fdc_drive_ba_to_chs(drive, ba, &cyl, &head, &sec);
1003
1004 /* Read one block */
1005 rc = pc_fdc_drive_read_data(drive, cyl, head, sec, buf,
1006 drive->sec_size);
1007 if (rc != EOK)
1008 goto error;
1009
1010 ++ba;
1011 --cnt;
1012 buf += drive->sec_size;
1013 }
1014
1015 return EOK;
1016error:
1017 ddf_msg(LVL_ERROR, "pc_fdc_bd_read_blocks: rc=%d", rc);
1018 return rc;
1019}
1020
1021/** Read TOC from block device.
1022 *
1023 * @param bd Block device
1024 * @param session Session number
1025 * @param buf Destination buffer
1026 * @param size Size of destination buffer in bytes
1027 *
1028 * @return EOK on success or an error code
1029 */
1030static errno_t pc_fdc_bd_read_toc(bd_srv_t *bd, uint8_t session, void *buf, size_t size)
1031{
1032 return ENOTSUP;
1033}
1034
1035/** Write multiple blocks to block device.
1036 *
1037 * @param bd Block device
1038 * @param ba Address of first block
1039 * @param cnt Number of blocks
1040 * @param buf Source buffer
1041 * @param size Size of source buffer in bytes
1042 *
1043 * @return EOK on success or an error code
1044 */
1045static errno_t pc_fdc_bd_write_blocks(bd_srv_t *bd, uint64_t ba, size_t cnt,
1046 const void *buf, size_t size)
1047{
1048 pc_fdc_drive_t *drive = bd_srv_drive(bd);
1049 uint8_t cyl, head, sec;
1050 errno_t rc;
1051
1052 ddf_msg(LVL_DEBUG, "pc_fdc_bd_write_blocks");
1053
1054 if (size < cnt * drive->sec_size) {
1055 rc = EINVAL;
1056 goto error;
1057 }
1058
1059 while (cnt > 0) {
1060 pc_fdc_drive_ba_to_chs(drive, ba, &cyl, &head, &sec);
1061
1062 /* Write one block */
1063 rc = pc_fdc_drive_write_data(drive, cyl, head, sec, buf,
1064 drive->sec_size);
1065 if (rc != EOK)
1066 goto error;
1067
1068 ++ba;
1069 --cnt;
1070 buf += drive->sec_size;
1071 }
1072
1073 return EOK;
1074error:
1075 ddf_msg(LVL_ERROR, "pc_fdc_bd_write_blocks: rc=%d", rc);
1076 return rc;
1077}
1078
1079/** Get device block size. */
1080static errno_t pc_fdc_bd_get_block_size(bd_srv_t *bd, size_t *rbsize)
1081{
1082 pc_fdc_drive_t *drive = bd_srv_drive(bd);
1083
1084 *rbsize = drive->sec_size;
1085 return EOK;
1086}
1087
1088/** Get device number of blocks. */
1089static errno_t pc_fdc_bd_get_num_blocks(bd_srv_t *bd, aoff64_t *rnb)
1090{
1091 pc_fdc_drive_t *drive = bd_srv_drive(bd);
1092
1093 *rnb = drive->cylinders * drive->heads * drive->sectors;
1094 return EOK;
1095}
1096
1097/** Flush cache. */
1098static errno_t pc_fdc_bd_sync_cache(bd_srv_t *bd, uint64_t ba, size_t cnt)
1099{
1100 return EOK;
1101}
1102
1103/**
1104 * @}
1105 */
Note: See TracBrowser for help on using the repository browser.