VirtualBox

source: vbox/trunk/src/VBox/Devices/Storage/fdc.c@ 31193

Last change on this file since 31193 was 31128, checked in by vboxsync, 14 years ago

Main/Console,Storage/DrvVD+fdc: Allow readonly floppy images (suppress runtime error on VM start for readonly media), and take this as the signal to make the floppy medium readonly. Never attempt to write to such images, and signal the appropriate error.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 95.3 KB
Line 
1#ifdef VBOX
2/* $Id: fdc.c 31128 2010-07-26 19:08:45Z vboxsync $ */
3/** @file
4 * VBox storage devices: Floppy disk controller
5 */
6
7/*
8 * Copyright (C) 2006-2010 Oracle Corporation
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 * --------------------------------------------------------------------
18 *
19 * This code is based on:
20 *
21 * QEMU Floppy disk emulator (Intel 82078)
22 *
23 * Copyright (c) 2003 Jocelyn Mayer
24 *
25 * Permission is hereby granted, free of charge, to any person obtaining a copy
26 * of this software and associated documentation files (the "Software"), to deal
27 * in the Software without restriction, including without limitation the rights
28 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
29 * copies of the Software, and to permit persons to whom the Software is
30 * furnished to do so, subject to the following conditions:
31 *
32 * The above copyright notice and this permission notice shall be included in
33 * all copies or substantial portions of the Software.
34 *
35 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
36 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
37 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
38 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
39 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
40 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
41 * THE SOFTWARE.
42 *
43 */
44
45
46/*******************************************************************************
47* Header Files *
48*******************************************************************************/
49#define LOG_GROUP LOG_GROUP_DEV_FDC
50#include <VBox/pdmdev.h>
51#include <iprt/assert.h>
52#include <iprt/string.h>
53#include <iprt/uuid.h>
54
55#include "Builtins.h"
56#include "../vl_vbox.h"
57
58#define MAX_FD 2
59
60#define PDMIBASE_2_FDRIVE(pInterface) \
61 ((fdrive_t *)((uintptr_t)(pInterface) - RT_OFFSETOF(fdrive_t, IBase)))
62
63#define PDMIMOUNTNOTIFY_2_FDRIVE(p) \
64 ((fdrive_t *)((uintptr_t)(p) - RT_OFFSETOF(fdrive_t, IMountNotify)))
65
66#endif /* VBOX */
67
68#ifndef VBOX
69#include "vl.h"
70#endif /* !VBOX */
71
72/********************************************************/
73/* debug Floppy devices */
74/* #define DEBUG_FLOPPY */
75
76#ifndef VBOX
77 #ifdef DEBUG_FLOPPY
78 #define FLOPPY_DPRINTF(fmt, args...) \
79 do { printf("FLOPPY: " fmt , ##args); } while (0)
80 #endif
81#else /* !VBOX */
82 # ifdef LOG_ENABLED
83 static void FLOPPY_DPRINTF (const char *fmt, ...)
84 {
85 if (LogIsEnabled ()) {
86 va_list args;
87 va_start (args, fmt);
88 RTLogLogger (NULL, NULL, "floppy: %N", fmt, &args); /* %N - nested va_list * type formatting call. */
89 va_end (args);
90 }
91 }
92 # else
93 DECLINLINE(void) FLOPPY_DPRINTF(const char *pszFmt, ...) {}
94 # endif
95#endif /* !VBOX */
96
97#ifndef VBOX
98#define FLOPPY_ERROR(fmt, args...) \
99 do { printf("FLOPPY ERROR: %s: " fmt, __func__ , ##args); } while (0)
100#else /* VBOX */
101# define FLOPPY_ERROR RTLogPrintf
102#endif /* VBOX */
103
104#ifdef VBOX
105typedef struct fdctrl_t fdctrl_t;
106#endif /* VBOX */
107
108/********************************************************/
109/* Floppy drive emulation */
110
111/* Will always be a fixed parameter for us */
112#define FD_SECTOR_LEN 512
113#define FD_SECTOR_SC 2 /* Sector size code */
114
115/* Floppy disk drive emulation */
116typedef enum fdisk_type_t {
117 FDRIVE_DISK_288 = 0x01, /* 2.88 MB disk */
118 FDRIVE_DISK_144 = 0x02, /* 1.44 MB disk */
119 FDRIVE_DISK_720 = 0x03, /* 720 kB disk */
120 FDRIVE_DISK_USER = 0x04, /* User defined geometry */
121 FDRIVE_DISK_NONE = 0x05 /* No disk */
122} fdisk_type_t;
123
124typedef enum fdrive_type_t {
125 FDRIVE_DRV_144 = 0x00, /* 1.44 MB 3"5 drive */
126 FDRIVE_DRV_288 = 0x01, /* 2.88 MB 3"5 drive */
127 FDRIVE_DRV_120 = 0x02, /* 1.2 MB 5"25 drive */
128 FDRIVE_DRV_NONE = 0x03 /* No drive connected */
129#ifdef VBOX
130 , FDRIVE_DRV_DUMMY = INT32_MAX
131#endif
132} fdrive_type_t;
133
134typedef enum fdrive_flags_t {
135 FDRIVE_MOTOR_ON = 0x01, /* motor on/off */
136 FDRIVE_REVALIDATE = 0x02 /* Revalidated */
137#ifdef VBOX
138 , FDRIVE_DUMMY = INT32_MAX
139#endif
140} fdrive_flags_t;
141
142typedef enum fdisk_flags_t {
143 FDISK_DBL_SIDES = 0x01
144#ifdef VBOX
145 , FDDISK_DUMMY = INT32_MAX
146#endif
147} fdisk_flags_t;
148
149#ifdef VBOX
150/**
151 * The status for one drive.
152 *
153 * @implements PDMIBASE
154 * @implements PDMIBLOCKPORT
155 * @implements PDMIMOUNTNOTIFY
156 */
157#endif
158typedef struct fdrive_t {
159#ifndef VBOX
160 BlockDriverState *bs;
161#else /* VBOX */
162 /** Pointer to the attached driver's base interface. */
163 R3PTRTYPE(PPDMIBASE) pDrvBase;
164 /** Pointer to the attached driver's block interface. */
165 R3PTRTYPE(PPDMIBLOCK) pDrvBlock;
166 /** Pointer to the attached driver's block bios interface. */
167 R3PTRTYPE(PPDMIBLOCKBIOS) pDrvBlockBios;
168 /** Pointer to the attached driver's mount interface.
169 * This is NULL if the driver isn't a removable unit. */
170 R3PTRTYPE(PPDMIMOUNT) pDrvMount;
171 /** The base interface. */
172 PDMIBASE IBase;
173 /** The block port interface. */
174 PDMIBLOCKPORT IPort;
175 /** The mount notify interface. */
176 PDMIMOUNTNOTIFY IMountNotify;
177 /** The LUN #. */
178 RTUINT iLUN;
179 /** The LED for this LUN. */
180 PDMLED Led;
181 /** The Diskette present/missing flag. */
182 bool fMediaPresent;
183#endif
184 /* Drive status */
185 fdrive_type_t drive;
186 fdrive_flags_t drflags;
187 uint8_t perpendicular; /* 2.88 MB access mode */
188 /* Position */
189 uint8_t head;
190 uint8_t track;
191 uint8_t sect;
192 /* Last operation status */
193 uint8_t dir; /* Direction */
194 uint8_t rw; /* Read/write */
195 /* Media */
196 fdisk_flags_t flags;
197 uint8_t last_sect; /* Nb sector per track */
198 uint8_t max_track; /* Nb of tracks */
199 uint16_t bps; /* Bytes per sector */
200 uint8_t ro; /* Is read-only */
201} fdrive_t;
202
203#ifndef VBOX
204static void fd_init (fdrive_t *drv, BlockDriverState *bs)
205{
206 /* Drive */
207 drv->bs = bs;
208 drv->drive = FDRIVE_DRV_NONE;
209 drv->drflags = 0;
210 drv->perpendicular = 0;
211 /* Disk */
212 drv->last_sect = 0;
213 drv->max_track = 0;
214}
215#endif
216
217static unsigned _fd_sector (uint8_t head, uint8_t track,
218 uint8_t sect, uint8_t last_sect)
219{
220 return (((track * 2) + head) * last_sect) + sect - 1; /* sect >= 1 */
221}
222
223/* Returns current position, in sectors, for given drive */
224static unsigned fd_sector (fdrive_t *drv)
225{
226 return _fd_sector(drv->head, drv->track, drv->sect, drv->last_sect);
227}
228
229static int fd_seek (fdrive_t *drv, uint8_t head, uint8_t track, uint8_t sect,
230 int enable_seek)
231{
232 uint32_t sector;
233 int ret;
234
235 if (track > drv->max_track ||
236 (head != 0 && (drv->flags & FDISK_DBL_SIDES) == 0)) {
237 FLOPPY_DPRINTF("try to read %d %02x %02x (max=%d %d %02x %02x)\n",
238 head, track, sect, 1,
239 (drv->flags & FDISK_DBL_SIDES) == 0 ? 0 : 1,
240 drv->max_track, drv->last_sect);
241 return 2;
242 }
243 if (sect > drv->last_sect) {
244 FLOPPY_DPRINTF("try to read %d %02x %02x (max=%d %d %02x %02x)\n",
245 head, track, sect, 1,
246 (drv->flags & FDISK_DBL_SIDES) == 0 ? 0 : 1,
247 drv->max_track, drv->last_sect);
248 return 3;
249 }
250 sector = _fd_sector(head, track, sect, drv->last_sect);
251 ret = 0;
252 if (sector != fd_sector(drv)) {
253#if 0
254 if (!enable_seek) {
255 FLOPPY_ERROR("no implicit seek %d %02x %02x (max=%d %02x %02x)\n",
256 head, track, sect, 1, drv->max_track, drv->last_sect);
257 return 4;
258 }
259#endif
260 drv->head = head;
261 if (drv->track != track)
262 ret = 1;
263 drv->track = track;
264 drv->sect = sect;
265 }
266
267 return ret;
268}
269
270/* Set drive back to track 0 */
271static void fd_recalibrate (fdrive_t *drv)
272{
273 FLOPPY_DPRINTF("recalibrate\n");
274 drv->head = 0;
275 drv->track = 0;
276 drv->sect = 1;
277 drv->dir = 1;
278 drv->rw = 0;
279}
280
281/* Recognize floppy formats */
282typedef struct fd_format_t {
283 fdrive_type_t drive;
284 fdisk_type_t disk;
285 uint8_t last_sect;
286 uint8_t max_track;
287 uint8_t max_head;
288 const char *str;
289} fd_format_t;
290
291static fd_format_t fd_formats[] = {
292 /* First entry is default format */
293 /* 1.44 MB 3"1/2 floppy disks */
294 { FDRIVE_DRV_144, FDRIVE_DISK_144, 18, 80, 1, "1.44 MB 3\"1/2", },
295 { FDRIVE_DRV_144, FDRIVE_DISK_144, 20, 80, 1, "1.6 MB 3\"1/2", },
296 { FDRIVE_DRV_144, FDRIVE_DISK_144, 21, 80, 1, "1.68 MB 3\"1/2", },
297 { FDRIVE_DRV_144, FDRIVE_DISK_144, 21, 82, 1, "1.72 MB 3\"1/2", },
298 { FDRIVE_DRV_144, FDRIVE_DISK_144, 21, 83, 1, "1.74 MB 3\"1/2", },
299 { FDRIVE_DRV_144, FDRIVE_DISK_144, 22, 80, 1, "1.76 MB 3\"1/2", },
300 { FDRIVE_DRV_144, FDRIVE_DISK_144, 23, 80, 1, "1.84 MB 3\"1/2", },
301 { FDRIVE_DRV_144, FDRIVE_DISK_144, 24, 80, 1, "1.92 MB 3\"1/2", },
302 /* 2.88 MB 3"1/2 floppy disks */
303 { FDRIVE_DRV_288, FDRIVE_DISK_288, 36, 80, 1, "2.88 MB 3\"1/2", },
304 { FDRIVE_DRV_288, FDRIVE_DISK_288, 39, 80, 1, "3.12 MB 3\"1/2", },
305 { FDRIVE_DRV_288, FDRIVE_DISK_288, 40, 80, 1, "3.2 MB 3\"1/2", },
306 { FDRIVE_DRV_288, FDRIVE_DISK_288, 44, 80, 1, "3.52 MB 3\"1/2", },
307 { FDRIVE_DRV_288, FDRIVE_DISK_288, 48, 80, 1, "3.84 MB 3\"1/2", },
308 /* 720 kB 3"1/2 floppy disks */
309 { FDRIVE_DRV_144, FDRIVE_DISK_720, 9, 80, 1, "720 kB 3\"1/2", },
310 { FDRIVE_DRV_144, FDRIVE_DISK_720, 10, 80, 1, "800 kB 3\"1/2", },
311 { FDRIVE_DRV_144, FDRIVE_DISK_720, 10, 82, 1, "820 kB 3\"1/2", },
312 { FDRIVE_DRV_144, FDRIVE_DISK_720, 10, 83, 1, "830 kB 3\"1/2", },
313 { FDRIVE_DRV_144, FDRIVE_DISK_720, 13, 80, 1, "1.04 MB 3\"1/2", },
314 { FDRIVE_DRV_144, FDRIVE_DISK_720, 14, 80, 1, "1.12 MB 3\"1/2", },
315 /* 1.2 MB 5"1/4 floppy disks */
316 { FDRIVE_DRV_120, FDRIVE_DISK_288, 15, 80, 1, "1.2 kB 5\"1/4", },
317 { FDRIVE_DRV_120, FDRIVE_DISK_288, 18, 80, 1, "1.44 MB 5\"1/4", },
318 { FDRIVE_DRV_120, FDRIVE_DISK_288, 18, 82, 1, "1.48 MB 5\"1/4", },
319 { FDRIVE_DRV_120, FDRIVE_DISK_288, 18, 83, 1, "1.49 MB 5\"1/4", },
320 { FDRIVE_DRV_120, FDRIVE_DISK_288, 20, 80, 1, "1.6 MB 5\"1/4", },
321 /* 720 kB 5"1/4 floppy disks */
322 { FDRIVE_DRV_120, FDRIVE_DISK_288, 9, 80, 1, "720 kB 5\"1/4", },
323 { FDRIVE_DRV_120, FDRIVE_DISK_288, 11, 80, 1, "880 kB 5\"1/4", },
324 /* 360 kB 5"1/4 floppy disks */
325 { FDRIVE_DRV_120, FDRIVE_DISK_288, 9, 40, 1, "360 kB 5\"1/4", },
326 { FDRIVE_DRV_120, FDRIVE_DISK_288, 9, 40, 0, "180 kB 5\"1/4", },
327 { FDRIVE_DRV_120, FDRIVE_DISK_288, 10, 41, 1, "410 kB 5\"1/4", },
328 { FDRIVE_DRV_120, FDRIVE_DISK_288, 10, 42, 1, "420 kB 5\"1/4", },
329 /* 320 kB 5"1/4 floppy disks */
330 { FDRIVE_DRV_120, FDRIVE_DISK_288, 8, 40, 1, "320 kB 5\"1/4", },
331 { FDRIVE_DRV_120, FDRIVE_DISK_288, 8, 40, 0, "160 kB 5\"1/4", },
332 /* 360 kB must match 5"1/4 better than 3"1/2... */
333 { FDRIVE_DRV_144, FDRIVE_DISK_720, 9, 80, 0, "360 kB 3\"1/2", },
334 /* end */
335 { FDRIVE_DRV_NONE, FDRIVE_DISK_NONE, -1, -1, 0, NULL, },
336};
337
338/* Revalidate a disk drive after a disk change */
339static void fd_revalidate (fdrive_t *drv)
340{
341 fd_format_t *parse;
342 int64_t nb_sectors, size;
343 int i, first_match, match;
344 int nb_heads, max_track, last_sect, ro;
345
346 FLOPPY_DPRINTF("revalidate\n");
347 drv->drflags &= ~FDRIVE_REVALIDATE;
348#ifndef VBOX
349 if (drv->bs != NULL && bdrv_is_inserted(drv->bs)) {
350 ro = bdrv_is_read_only(drv->bs);
351 bdrv_get_geometry_hint(drv->bs, &nb_heads, &max_track, &last_sect);
352#else /* VBOX */
353 /** @todo */ /** @todo r=bird: todo what exactly? */
354 if (drv->pDrvBlock
355 && drv->pDrvMount
356 && drv->pDrvMount->pfnIsMounted (drv->pDrvMount)) {
357 ro = drv->pDrvBlock->pfnIsReadOnly (drv->pDrvBlock);
358 nb_heads = 0;
359 max_track = 0;
360 last_sect = 0;
361#endif /* VBOX */
362 if (nb_heads != 0 && max_track != 0 && last_sect != 0) {
363 FLOPPY_DPRINTF("User defined disk (%d %d %d)",
364 nb_heads - 1, max_track, last_sect);
365 } else {
366#ifndef VBOX
367 bdrv_get_geometry(drv->bs, &nb_sectors);
368#else /* VBOX */
369 /* @todo */ /** @todo r=bird: todo what exactly?!?!? */
370 {
371 uint64_t size2 = drv->pDrvBlock->pfnGetSize (drv->pDrvBlock);
372 nb_sectors = size2 / 512;
373 }
374#endif /* VBOX */
375 match = -1;
376 first_match = -1;
377 for (i = 0;; i++) {
378 parse = &fd_formats[i];
379 if (parse->drive == FDRIVE_DRV_NONE)
380 break;
381 if (drv->drive == parse->drive ||
382 drv->drive == FDRIVE_DRV_NONE) {
383 size = (parse->max_head + 1) * parse->max_track *
384 parse->last_sect;
385 if (nb_sectors == size) {
386 match = i;
387 break;
388 }
389 if (first_match == -1)
390 first_match = i;
391 }
392 }
393 if (match == -1) {
394 if (first_match == -1)
395 match = 1;
396 else
397 match = first_match;
398 parse = &fd_formats[match];
399 }
400 nb_heads = parse->max_head + 1;
401 max_track = parse->max_track;
402 last_sect = parse->last_sect;
403 drv->drive = parse->drive;
404 FLOPPY_DPRINTF("%s floppy disk (%d h %d t %d s) %s\n", parse->str,
405 nb_heads, max_track, last_sect, ro ? "ro" : "rw");
406 }
407 if (nb_heads == 1) {
408 drv->flags &= ~FDISK_DBL_SIDES;
409 } else {
410 drv->flags |= FDISK_DBL_SIDES;
411 }
412 drv->max_track = max_track;
413 drv->last_sect = last_sect;
414 drv->ro = ro;
415#ifdef VBOX
416 drv->fMediaPresent = true;
417#endif
418 } else {
419 FLOPPY_DPRINTF("No disk in drive\n");
420 drv->last_sect = 0;
421 drv->max_track = 0;
422 drv->flags &= ~FDISK_DBL_SIDES;
423#ifdef VBOX
424 drv->fMediaPresent = false;
425#endif
426 }
427 drv->drflags |= FDRIVE_REVALIDATE;
428}
429
430/* Motor control */
431static void fd_start (fdrive_t *drv)
432{
433 drv->drflags |= FDRIVE_MOTOR_ON;
434}
435
436static void fd_stop (fdrive_t *drv)
437{
438 drv->drflags &= ~FDRIVE_MOTOR_ON;
439}
440
441/* Re-initialise a drives (motor off, repositioned) */
442static void fd_reset (fdrive_t *drv)
443{
444 fd_stop(drv);
445 fd_recalibrate(drv);
446}
447
448/********************************************************/
449/* Intel 82078 floppy disk controller emulation */
450
451#define target_ulong uint32_t
452static void fdctrl_reset (fdctrl_t *fdctrl, int do_irq);
453static void fdctrl_reset_fifo (fdctrl_t *fdctrl);
454#ifndef VBOX
455static int fdctrl_transfer_handler (void *opaque, int nchan, int dma_pos, int dma_len);
456#else /* VBOX: */
457static DECLCALLBACK(uint32_t) fdctrl_transfer_handler (PPDMDEVINS pDevIns,
458 void *opaque,
459 unsigned nchan,
460 uint32_t dma_pos,
461 uint32_t dma_len);
462#endif /* VBOX */
463static void fdctrl_raise_irq (fdctrl_t *fdctrl, uint8_t status);
464static void fdctrl_result_timer(void *opaque);
465
466static uint32_t fdctrl_read_statusB (fdctrl_t *fdctrl);
467static uint32_t fdctrl_read_dor (fdctrl_t *fdctrl);
468static void fdctrl_write_dor (fdctrl_t *fdctrl, uint32_t value);
469static uint32_t fdctrl_read_tape (fdctrl_t *fdctrl);
470static void fdctrl_write_tape (fdctrl_t *fdctrl, uint32_t value);
471static uint32_t fdctrl_read_main_status (fdctrl_t *fdctrl);
472static void fdctrl_write_rate (fdctrl_t *fdctrl, uint32_t value);
473static uint32_t fdctrl_read_data (fdctrl_t *fdctrl);
474static void fdctrl_write_data (fdctrl_t *fdctrl, uint32_t value);
475static uint32_t fdctrl_read_dir (fdctrl_t *fdctrl);
476
477enum {
478 FD_CTRL_ACTIVE = 0x01, /* XXX: suppress that */
479 FD_CTRL_RESET = 0x02,
480 FD_CTRL_SLEEP = 0x04, /* XXX: suppress that */
481 FD_CTRL_BUSY = 0x08, /* dma transfer in progress */
482 FD_CTRL_INTR = 0x10
483};
484
485enum {
486 FD_DIR_WRITE = 0,
487 FD_DIR_READ = 1,
488 FD_DIR_SCANE = 2,
489 FD_DIR_SCANL = 3,
490 FD_DIR_SCANH = 4
491};
492
493enum {
494 FD_STATE_CMD = 0x00,
495 FD_STATE_STATUS = 0x01,
496 FD_STATE_DATA = 0x02,
497 FD_STATE_STATE = 0x03,
498 FD_STATE_MULTI = 0x10,
499 FD_STATE_SEEK = 0x20,
500 FD_STATE_FORMAT = 0x40
501};
502
503#define FD_STATE(state) ((state) & FD_STATE_STATE)
504#define FD_SET_STATE(state, new_state) \
505do { (state) = ((state) & ~FD_STATE_STATE) | (new_state); } while (0)
506#define FD_MULTI_TRACK(state) ((state) & FD_STATE_MULTI)
507#define FD_DID_SEEK(state) ((state) & FD_STATE_SEEK)
508#define FD_FORMAT_CMD(state) ((state) & FD_STATE_FORMAT)
509
510#ifdef VBOX
511/**
512 * Floppy controller state.
513 *
514 * @implements PDMILEDPORTS
515 */
516#endif
517struct fdctrl_t {
518#ifndef VBOX
519 fdctrl_t *fdctrl;
520#endif
521 /* Controller's identification */
522 uint8_t version;
523 /* HW */
524#ifndef VBOX
525 int irq_lvl;
526 int dma_chann;
527#else
528 uint8_t irq_lvl;
529 uint8_t dma_chann;
530#endif
531 uint32_t io_base;
532 /* Controller state */
533 QEMUTimer *result_timer;
534 uint8_t state;
535 uint8_t dma_en;
536 uint8_t cur_drv;
537 uint8_t bootsel;
538 /* Command FIFO */
539 uint8_t fifo[FD_SECTOR_LEN];
540 uint32_t data_pos;
541 uint32_t data_len;
542 uint8_t data_state;
543 uint8_t data_dir;
544 uint8_t int_status;
545 uint8_t eot; /* last wanted sector */
546 /* States kept only to be returned back */
547 /* Timers state */
548 uint8_t timer0;
549 uint8_t timer1;
550 /* precompensation */
551 uint8_t precomp_trk;
552 uint8_t config;
553 uint8_t lock;
554 /* Power down config (also with status regB access mode */
555 uint8_t pwrd;
556 /* Floppy drives */
557 fdrive_t drives[2];
558#ifdef VBOX
559 /** Pointer to device instance. */
560 PPDMDEVINS pDevIns;
561
562 /** Status LUN: The base interface. */
563 PDMIBASE IBaseStatus;
564 /** Status LUN: The Leds interface. */
565 PDMILEDPORTS ILeds;
566 /** Status LUN: The Partner of ILeds. */
567 PPDMILEDCONNECTORS pLedsConnector;
568#endif
569};
570
571static uint32_t fdctrl_read (void *opaque, uint32_t reg)
572{
573 fdctrl_t *fdctrl = opaque;
574 uint32_t retval;
575
576 switch (reg & 0x07) {
577 case 0x01:
578 retval = fdctrl_read_statusB(fdctrl);
579 break;
580 case 0x02:
581 retval = fdctrl_read_dor(fdctrl);
582 break;
583 case 0x03:
584 retval = fdctrl_read_tape(fdctrl);
585 break;
586 case 0x04:
587 retval = fdctrl_read_main_status(fdctrl);
588 break;
589 case 0x05:
590 retval = fdctrl_read_data(fdctrl);
591 break;
592 case 0x07:
593 retval = fdctrl_read_dir(fdctrl);
594 break;
595 default:
596 retval = (uint32_t)(-1);
597 break;
598 }
599 FLOPPY_DPRINTF("read reg%d: 0x%02x\n", reg & 7, retval);
600
601 return retval;
602}
603
604static void fdctrl_write (void *opaque, uint32_t reg, uint32_t value)
605{
606 fdctrl_t *fdctrl = opaque;
607
608 FLOPPY_DPRINTF("write reg%d: 0x%02x\n", reg & 7, value);
609
610 switch (reg & 0x07) {
611 case 0x02:
612 fdctrl_write_dor(fdctrl, value);
613 break;
614 case 0x03:
615 fdctrl_write_tape(fdctrl, value);
616 break;
617 case 0x04:
618 fdctrl_write_rate(fdctrl, value);
619 break;
620 case 0x05:
621 fdctrl_write_data(fdctrl, value);
622 break;
623 default:
624 break;
625 }
626}
627
628#ifndef VBOX
629static void fd_change_cb (void *opaque)
630{
631 fdrive_t *drv = opaque;
632
633 FLOPPY_DPRINTF("disk change\n");
634 fd_revalidate(drv);
635#if 0
636 fd_recalibrate(drv);
637 fdctrl_reset_fifo(drv->fdctrl);
638 fdctrl_raise_irq(drv->fdctrl, 0x20);
639#endif
640}
641
642#else /* VBOX */
643/**
644 * Called when a media is mounted.
645 *
646 * @param pInterface Pointer to the interface structure
647 * containing the called function pointer.
648 */
649static DECLCALLBACK(void) fdMountNotify(PPDMIMOUNTNOTIFY pInterface)
650{
651 fdrive_t *drv = PDMIMOUNTNOTIFY_2_FDRIVE (pInterface);
652 LogFlow(("fdMountNotify:\n"));
653 fd_revalidate (drv);
654}
655
656/**
657 * Called when a media is unmounted
658 * @param pInterface Pointer to the interface structure containing the called function pointer.
659 */
660static DECLCALLBACK(void) fdUnmountNotify(PPDMIMOUNTNOTIFY pInterface)
661{
662 fdrive_t *drv = PDMIMOUNTNOTIFY_2_FDRIVE (pInterface);
663 LogFlow(("fdUnmountNotify:\n"));
664 fd_revalidate (drv);
665}
666#endif
667
668#ifndef VBOX
669fdctrl_t *fdctrl_init (int irq_lvl, int dma_chann, int mem_mapped,
670 uint32_t io_base,
671 BlockDriverState **fds)
672{
673 fdctrl_t *fdctrl;
674/* // int io_mem; */
675 int i;
676
677 FLOPPY_DPRINTF("init controller\n");
678 fdctrl = qemu_mallocz(sizeof(fdctrl_t));
679 if (!fdctrl)
680 return NULL;
681 fdctrl->result_timer = qemu_new_timer(vm_clock,
682 fdctrl_result_timer, fdctrl);
683
684 fdctrl->version = 0x90; /* Intel 82078 controller */
685 fdctrl->irq_lvl = irq_lvl;
686 fdctrl->dma_chann = dma_chann;
687 fdctrl->io_base = io_base;
688 fdctrl->config = 0x60; /* Implicit seek, polling & FIFO enabled */
689 if (fdctrl->dma_chann != -1) {
690 fdctrl->dma_en = 1;
691 DMA_register_channel(dma_chann, &fdctrl_transfer_handler, fdctrl);
692 } else {
693 fdctrl->dma_en = 0;
694 }
695 for (i = 0; i < 2; i++) {
696 fd_init(&fdctrl->drives[i], fds[i]);
697 if (fds[i]) {
698 bdrv_set_change_cb(fds[i],
699 &fd_change_cb, &fdctrl->drives[i]);
700 }
701 }
702 fdctrl_reset(fdctrl, 0);
703 fdctrl->state = FD_CTRL_ACTIVE;
704 if (mem_mapped) {
705 FLOPPY_ERROR("memory mapped floppy not supported by now !\n");
706#if 0
707 io_mem = cpu_register_io_memory(0, fdctrl_mem_read, fdctrl_mem_write);
708 cpu_register_physical_memory(base, 0x08, io_mem);
709#endif
710 } else {
711 register_ioport_read(io_base + 0x01, 5, 1, &fdctrl_read, fdctrl);
712 register_ioport_read(io_base + 0x07, 1, 1, &fdctrl_read, fdctrl);
713 register_ioport_write(io_base + 0x01, 5, 1, &fdctrl_write, fdctrl);
714 register_ioport_write(io_base + 0x07, 1, 1, &fdctrl_write, fdctrl);
715 }
716 for (i = 0; i < 2; i++) {
717 fd_revalidate(&fdctrl->drives[i]);
718 }
719
720 return fdctrl;
721}
722
723/* XXX: may change if moved to bdrv */
724int fdctrl_get_drive_type(fdctrl_t *fdctrl, int drive_num)
725{
726 return fdctrl->drives[drive_num].drive;
727}
728#endif
729
730/* Change IRQ state */
731static void fdctrl_reset_irq (fdctrl_t *fdctrl)
732{
733 FLOPPY_DPRINTF("Reset interrupt\n");
734#ifdef VBOX
735 PDMDevHlpISASetIrq (fdctrl->pDevIns, fdctrl->irq_lvl, 0);
736#else
737 pic_set_irq(fdctrl->irq_lvl, 0);
738#endif
739 fdctrl->state &= ~FD_CTRL_INTR;
740}
741
742static void fdctrl_raise_irq (fdctrl_t *fdctrl, uint8_t status)
743{
744 if (~(fdctrl->state & FD_CTRL_INTR)) {
745#ifdef VBOX
746 PDMDevHlpISASetIrq (fdctrl->pDevIns, fdctrl->irq_lvl, 1);
747#else
748 pic_set_irq(fdctrl->irq_lvl, 1);
749#endif
750 fdctrl->state |= FD_CTRL_INTR;
751 }
752 FLOPPY_DPRINTF("Set interrupt status to 0x%02x\n", status);
753 fdctrl->int_status = status;
754}
755
756/* Reset controller */
757static void fdctrl_reset (fdctrl_t *fdctrl, int do_irq)
758{
759 int i;
760
761 FLOPPY_DPRINTF("reset controller\n");
762 fdctrl_reset_irq(fdctrl);
763 /* Initialise controller */
764 fdctrl->cur_drv = 0;
765 /* FIFO state */
766 fdctrl->data_pos = 0;
767 fdctrl->data_len = 0;
768 fdctrl->data_state = FD_STATE_CMD;
769 fdctrl->data_dir = FD_DIR_WRITE;
770 for (i = 0; i < MAX_FD; i++)
771 fd_reset(&fdctrl->drives[i]);
772 fdctrl_reset_fifo(fdctrl);
773 if (do_irq)
774 fdctrl_raise_irq(fdctrl, 0xc0);
775}
776
777static inline fdrive_t *drv0 (fdctrl_t *fdctrl)
778{
779 return &fdctrl->drives[fdctrl->bootsel];
780}
781
782static inline fdrive_t *drv1 (fdctrl_t *fdctrl)
783{
784 return &fdctrl->drives[1 - fdctrl->bootsel];
785}
786
787static fdrive_t *get_cur_drv (fdctrl_t *fdctrl)
788{
789 return fdctrl->cur_drv == 0 ? drv0(fdctrl) : drv1(fdctrl);
790}
791
792/* Status B register : 0x01 (read-only) */
793static uint32_t fdctrl_read_statusB (fdctrl_t *fdctrl)
794{
795 FLOPPY_DPRINTF("status register: 0x00\n");
796 return 0;
797}
798
799/* Digital output register : 0x02 */
800static uint32_t fdctrl_read_dor (fdctrl_t *fdctrl)
801{
802 uint32_t retval = 0;
803
804 /* Drive motors state indicators */
805#ifndef VBOX
806 if (drv0(fdctrl)->drflags & FDRIVE_MOTOR_ON)
807 retval |= 1 << 5;
808 if (drv1(fdctrl)->drflags & FDRIVE_MOTOR_ON)
809 retval |= 1 << 4;
810#else
811 /* bit4: 0 = drive 0 motor off/1 = on */
812 if (drv0(fdctrl)->drflags & FDRIVE_MOTOR_ON)
813 retval |= RT_BIT(4);
814 /* bit5: 0 = drive 1 motor off/1 = on */
815 if (drv1(fdctrl)->drflags & FDRIVE_MOTOR_ON)
816 retval |= RT_BIT(5);
817#endif
818 /* DMA enable */
819 retval |= fdctrl->dma_en << 3;
820 /* Reset indicator */
821 retval |= (fdctrl->state & FD_CTRL_RESET) == 0 ? 0x04 : 0;
822 /* Selected drive */
823 retval |= fdctrl->cur_drv;
824 FLOPPY_DPRINTF("digital output register: 0x%02x\n", retval);
825
826 return retval;
827}
828
829static void fdctrl_write_dor (fdctrl_t *fdctrl, uint32_t value)
830{
831 /* Reset mode */
832 if (fdctrl->state & FD_CTRL_RESET) {
833 if (!(value & 0x04)) {
834 FLOPPY_DPRINTF("Floppy controller in RESET state !\n");
835 return;
836 }
837 }
838 FLOPPY_DPRINTF("digital output register set to 0x%02x\n", value);
839 /* Drive motors state indicators */
840 if (value & 0x20)
841 fd_start(drv1(fdctrl));
842 else
843 fd_stop(drv1(fdctrl));
844 if (value & 0x10)
845 fd_start(drv0(fdctrl));
846 else
847 fd_stop(drv0(fdctrl));
848 /* DMA enable */
849#if 0
850 if (fdctrl->dma_chann != -1)
851 fdctrl->dma_en = 1 - ((value >> 3) & 1);
852#endif
853 /* Reset */
854 if (!(value & 0x04)) {
855 if (!(fdctrl->state & FD_CTRL_RESET)) {
856 FLOPPY_DPRINTF("controller enter RESET state\n");
857 fdctrl->state |= FD_CTRL_RESET;
858 }
859 } else {
860 if (fdctrl->state & FD_CTRL_RESET) {
861 FLOPPY_DPRINTF("controller out of RESET state\n");
862 fdctrl_reset(fdctrl, 1);
863 fdctrl->state &= ~(FD_CTRL_RESET | FD_CTRL_SLEEP);
864 }
865 }
866 /* Selected drive */
867 fdctrl->cur_drv = value & 1;
868}
869
870/* Tape drive register : 0x03 */
871static uint32_t fdctrl_read_tape (fdctrl_t *fdctrl)
872{
873 uint32_t retval = 0;
874
875 /* Disk boot selection indicator */
876 retval |= fdctrl->bootsel << 2;
877 /* Tape indicators: never allowed */
878 FLOPPY_DPRINTF("tape drive register: 0x%02x\n", retval);
879
880 return retval;
881}
882
883static void fdctrl_write_tape (fdctrl_t *fdctrl, uint32_t value)
884{
885 /* Reset mode */
886 if (fdctrl->state & FD_CTRL_RESET) {
887 FLOPPY_DPRINTF("Floppy controller in RESET state !\n");
888 return;
889 }
890 FLOPPY_DPRINTF("tape drive register set to 0x%02x\n", value);
891 /* Disk boot selection indicator */
892 fdctrl->bootsel = (value >> 2) & 1;
893 /* Tape indicators: never allow */
894}
895
896/* Main status register : 0x04 (read) */
897static uint32_t fdctrl_read_main_status (fdctrl_t *fdctrl)
898{
899 uint32_t retval = 0;
900
901 fdctrl->state &= ~(FD_CTRL_SLEEP | FD_CTRL_RESET);
902 if (!(fdctrl->state & FD_CTRL_BUSY)) {
903 /* Data transfer allowed */
904 retval |= 0x80;
905 /* Data transfer direction indicator */
906 if (fdctrl->data_dir == FD_DIR_READ)
907 retval |= 0x40;
908 }
909 /* Should handle 0x20 for SPECIFY command */
910 /* Command busy indicator */
911 if (FD_STATE(fdctrl->data_state) == FD_STATE_DATA ||
912 FD_STATE(fdctrl->data_state) == FD_STATE_STATUS)
913 retval |= 0x10;
914 FLOPPY_DPRINTF("main status register: 0x%02x\n", retval);
915
916 return retval;
917}
918
919/* Data select rate register : 0x04 (write) */
920static void fdctrl_write_rate (fdctrl_t *fdctrl, uint32_t value)
921{
922 /* Reset mode */
923 if (fdctrl->state & FD_CTRL_RESET) {
924 FLOPPY_DPRINTF("Floppy controller in RESET state !\n");
925 return;
926 }
927 FLOPPY_DPRINTF("select rate register set to 0x%02x\n", value);
928 /* Reset: autoclear */
929 if (value & 0x80) {
930 fdctrl->state |= FD_CTRL_RESET;
931 fdctrl_reset(fdctrl, 1);
932 fdctrl->state &= ~FD_CTRL_RESET;
933 }
934 if (value & 0x40) {
935 fdctrl->state |= FD_CTRL_SLEEP;
936 fdctrl_reset(fdctrl, 1);
937 }
938/* // fdctrl.precomp = (value >> 2) & 0x07; */
939}
940
941/* Digital input register : 0x07 (read-only) */
942static uint32_t fdctrl_read_dir (fdctrl_t *fdctrl)
943{
944 uint32_t retval = 0;
945
946#ifndef VBOX
947 if (drv0(fdctrl)->drflags & FDRIVE_REVALIDATE ||
948 drv1(fdctrl)->drflags & FDRIVE_REVALIDATE)
949#else
950 fdrive_t *cur_drv = get_cur_drv(fdctrl);
951 if ( drv0(fdctrl)->drflags & FDRIVE_REVALIDATE
952 || drv1(fdctrl)->drflags & FDRIVE_REVALIDATE
953 || !cur_drv->fMediaPresent)
954#endif
955 retval |= 0x80;
956 if (retval != 0)
957 FLOPPY_DPRINTF("Floppy digital input register: 0x%02x\n", retval);
958 drv0(fdctrl)->drflags &= ~FDRIVE_REVALIDATE;
959 drv1(fdctrl)->drflags &= ~FDRIVE_REVALIDATE;
960
961 return retval;
962}
963
964/* FIFO state control */
965static void fdctrl_reset_fifo (fdctrl_t *fdctrl)
966{
967 fdctrl->data_dir = FD_DIR_WRITE;
968 fdctrl->data_pos = 0;
969 FD_SET_STATE(fdctrl->data_state, FD_STATE_CMD);
970}
971
972/* Set FIFO status for the host to read */
973static void fdctrl_set_fifo (fdctrl_t *fdctrl, int fifo_len, int do_irq)
974{
975 fdctrl->data_dir = FD_DIR_READ;
976 fdctrl->data_len = fifo_len;
977 fdctrl->data_pos = 0;
978 FD_SET_STATE(fdctrl->data_state, FD_STATE_STATUS);
979 if (do_irq)
980 fdctrl_raise_irq(fdctrl, 0x00);
981}
982
983/* Set an error: unimplemented/unknown command */
984static void fdctrl_unimplemented (fdctrl_t *fdctrl)
985{
986#if 0
987 fdrive_t *cur_drv;
988
989 cur_drv = get_cur_drv(fdctrl);
990 fdctrl->fifo[0] = 0x60 | (cur_drv->head << 2) | fdctrl->cur_drv;
991 fdctrl->fifo[1] = 0x00;
992 fdctrl->fifo[2] = 0x00;
993 fdctrl_set_fifo(fdctrl, 3, 1);
994#else
995 /* // fdctrl_reset_fifo(fdctrl); */
996 fdctrl->fifo[0] = 0x80;
997 fdctrl_set_fifo(fdctrl, 1, 0);
998#endif
999}
1000
1001/* Callback for transfer end (stop or abort) */
1002static void fdctrl_stop_transfer (fdctrl_t *fdctrl, uint8_t status0,
1003 uint8_t status1, uint8_t status2)
1004{
1005 fdrive_t *cur_drv;
1006
1007 cur_drv = get_cur_drv(fdctrl);
1008 FLOPPY_DPRINTF("transfer status: %02x %02x %02x (%02x)\n",
1009 status0, status1, status2,
1010 status0 | (cur_drv->head << 2) | fdctrl->cur_drv);
1011 fdctrl->fifo[0] = status0 | (cur_drv->head << 2) | fdctrl->cur_drv;
1012 fdctrl->fifo[1] = status1;
1013 fdctrl->fifo[2] = status2;
1014 fdctrl->fifo[3] = cur_drv->track;
1015 fdctrl->fifo[4] = cur_drv->head;
1016 fdctrl->fifo[5] = cur_drv->sect;
1017 fdctrl->fifo[6] = FD_SECTOR_SC;
1018 fdctrl->data_dir = FD_DIR_READ;
1019 if (fdctrl->state & FD_CTRL_BUSY) {
1020#ifdef VBOX
1021 PDMDevHlpDMASetDREQ (fdctrl->pDevIns, fdctrl->dma_chann, 0);
1022#else
1023 DMA_release_DREQ(fdctrl->dma_chann);
1024#endif
1025 fdctrl->state &= ~FD_CTRL_BUSY;
1026 }
1027 fdctrl_set_fifo(fdctrl, 7, 1);
1028}
1029
1030/* Prepare a data transfer (either DMA or FIFO) */
1031static void fdctrl_start_transfer (fdctrl_t *fdctrl, int direction)
1032{
1033 fdrive_t *cur_drv;
1034 uint8_t kh, kt, ks;
1035 int did_seek;
1036
1037 fdctrl->cur_drv = fdctrl->fifo[1] & 1;
1038 cur_drv = get_cur_drv(fdctrl);
1039 kt = fdctrl->fifo[2];
1040 kh = fdctrl->fifo[3];
1041 ks = fdctrl->fifo[4];
1042 FLOPPY_DPRINTF("Start transfer at %d %d %02x %02x (%d)\n",
1043 fdctrl->cur_drv, kh, kt, ks,
1044 _fd_sector(kh, kt, ks, cur_drv->last_sect));
1045 did_seek = 0;
1046 switch (fd_seek(cur_drv, kh, kt, ks, fdctrl->config & 0x40)) {
1047 case 2:
1048 /* sect too big */
1049 fdctrl_stop_transfer(fdctrl, 0x40, 0x00, 0x00);
1050 fdctrl->fifo[3] = kt;
1051 fdctrl->fifo[4] = kh;
1052 fdctrl->fifo[5] = ks;
1053 return;
1054 case 3:
1055 /* track too big */
1056 fdctrl_stop_transfer(fdctrl, 0x40, 0x80, 0x00);
1057 fdctrl->fifo[3] = kt;
1058 fdctrl->fifo[4] = kh;
1059 fdctrl->fifo[5] = ks;
1060 return;
1061 case 4:
1062 /* No seek enabled */
1063 fdctrl_stop_transfer(fdctrl, 0x40, 0x00, 0x00);
1064 fdctrl->fifo[3] = kt;
1065 fdctrl->fifo[4] = kh;
1066 fdctrl->fifo[5] = ks;
1067 return;
1068 case 1:
1069 did_seek = 1;
1070 break;
1071 default:
1072 break;
1073 }
1074 /* Set the FIFO state */
1075 fdctrl->data_dir = direction;
1076 fdctrl->data_pos = 0;
1077 FD_SET_STATE(fdctrl->data_state, FD_STATE_DATA); /* FIFO ready for data */
1078 if (fdctrl->fifo[0] & 0x80)
1079 fdctrl->data_state |= FD_STATE_MULTI;
1080 else
1081 fdctrl->data_state &= ~FD_STATE_MULTI;
1082 if (did_seek)
1083 fdctrl->data_state |= FD_STATE_SEEK;
1084 else
1085 fdctrl->data_state &= ~FD_STATE_SEEK;
1086 if (fdctrl->fifo[5] == 00) {
1087 fdctrl->data_len = fdctrl->fifo[8];
1088 } else {
1089 int tmp;
1090 fdctrl->data_len = 128 << fdctrl->fifo[5];
1091 tmp = (cur_drv->last_sect - ks + 1);
1092 if (fdctrl->fifo[0] & 0x80)
1093 tmp += cur_drv->last_sect;
1094 fdctrl->data_len *= tmp;
1095 }
1096 fdctrl->eot = fdctrl->fifo[6];
1097 if (fdctrl->dma_en) {
1098 int dma_mode;
1099 /* DMA transfer are enabled. Check if DMA channel is well programmed */
1100#ifndef VBOX
1101 dma_mode = DMA_get_channel_mode(fdctrl->dma_chann);
1102#else
1103 dma_mode = PDMDevHlpDMAGetChannelMode (fdctrl->pDevIns, fdctrl->dma_chann);
1104#endif
1105 dma_mode = (dma_mode >> 2) & 3;
1106 FLOPPY_DPRINTF("dma_mode=%d direction=%d (%d - %d)\n",
1107 dma_mode, direction,
1108 (128 << fdctrl->fifo[5]) *
1109 (cur_drv->last_sect - ks + 1), fdctrl->data_len);
1110 if (((direction == FD_DIR_SCANE || direction == FD_DIR_SCANL ||
1111 direction == FD_DIR_SCANH) && dma_mode == 0) ||
1112 (direction == FD_DIR_WRITE && dma_mode == 2) ||
1113 (direction == FD_DIR_READ && dma_mode == 1)) {
1114 /* No access is allowed until DMA transfer has completed */
1115 fdctrl->state |= FD_CTRL_BUSY;
1116 /* Now, we just have to wait for the DMA controller to
1117 * recall us...
1118 */
1119#ifndef VBOX
1120 DMA_hold_DREQ(fdctrl->dma_chann);
1121 DMA_schedule(fdctrl->dma_chann);
1122#else
1123 PDMDevHlpDMASetDREQ (fdctrl->pDevIns, fdctrl->dma_chann, 1);
1124 PDMDevHlpDMASchedule (fdctrl->pDevIns);
1125#endif
1126 return;
1127 } else {
1128 FLOPPY_ERROR("dma_mode=%d direction=%d\n", dma_mode, direction);
1129 }
1130 }
1131 FLOPPY_DPRINTF("start non-DMA transfer\n");
1132 /* IO based transfer: calculate len */
1133 fdctrl_raise_irq(fdctrl, 0x00);
1134
1135 return;
1136}
1137
1138/* Prepare a transfer of deleted data */
1139static void fdctrl_start_transfer_del (fdctrl_t *fdctrl, int direction)
1140{
1141 /* We don't handle deleted data,
1142 * so we don't return *ANYTHING*
1143 */
1144 fdctrl_stop_transfer(fdctrl, 0x60, 0x00, 0x00);
1145}
1146
1147#if 0
1148#include <stdio.h>
1149#include <stdlib.h>
1150
1151static FILE * f;
1152static void dump (void *p, size_t len)
1153{
1154 size_t n;
1155
1156 if (!f) {
1157 f = fopen ("dump", "wb");
1158 if (!f)
1159 exit (0);
1160 }
1161
1162 n = fwrite (p, len, 1, f);
1163 if (n != 1) {
1164 AssertMsgFailed (("failed to dump\n"));
1165 exit (0);
1166 }
1167}
1168#else
1169#define dump(a,b) do { } while (0)
1170#endif
1171
1172/* handlers for DMA transfers */
1173#ifdef VBOX
1174static DECLCALLBACK(uint32_t) fdctrl_transfer_handler (PPDMDEVINS pDevIns,
1175 void *opaque,
1176 unsigned nchan,
1177 uint32_t dma_pos,
1178 uint32_t dma_len)
1179#else
1180static int fdctrl_transfer_handler (void *opaque, int nchan,
1181 int dma_pos, int dma_len)
1182#endif
1183{
1184 fdctrl_t *fdctrl;
1185 fdrive_t *cur_drv;
1186#ifdef VBOX
1187 int rc;
1188 uint32_t len, start_pos, rel_pos;
1189#else
1190 int len, start_pos, rel_pos;
1191#endif
1192 uint8_t status0 = 0x00, status1 = 0x00, status2 = 0x00;
1193
1194 fdctrl = opaque;
1195 if (!(fdctrl->state & FD_CTRL_BUSY)) {
1196 FLOPPY_DPRINTF("Not in DMA transfer mode !\n");
1197 return 0;
1198 }
1199 cur_drv = get_cur_drv(fdctrl);
1200 if (fdctrl->data_dir == FD_DIR_SCANE || fdctrl->data_dir == FD_DIR_SCANL ||
1201 fdctrl->data_dir == FD_DIR_SCANH)
1202 status2 = 0x04;
1203 if (dma_len > fdctrl->data_len)
1204 dma_len = fdctrl->data_len;
1205#ifndef VBOX
1206 if (cur_drv->bs == NULL) {
1207#else /* !VBOX */
1208 if (cur_drv->pDrvBlock == NULL) {
1209#endif
1210 if (fdctrl->data_dir == FD_DIR_WRITE)
1211 fdctrl_stop_transfer(fdctrl, 0x60, 0x00, 0x00);
1212 else
1213 fdctrl_stop_transfer(fdctrl, 0x40, 0x00, 0x00);
1214 len = 0;
1215 goto transfer_error;
1216 }
1217 rel_pos = fdctrl->data_pos % FD_SECTOR_LEN;
1218 for (start_pos = fdctrl->data_pos; fdctrl->data_pos < dma_len;) {
1219 len = dma_len - fdctrl->data_pos;
1220 if (len + rel_pos > FD_SECTOR_LEN)
1221 len = FD_SECTOR_LEN - rel_pos;
1222 FLOPPY_DPRINTF("copy %d bytes (%d %d %d) %d pos %d %02x "
1223 "(%d-0x%08x 0x%08x)\n", len, dma_len, fdctrl->data_pos,
1224 fdctrl->data_len, fdctrl->cur_drv, cur_drv->head,
1225 cur_drv->track, cur_drv->sect, fd_sector(cur_drv),
1226 fd_sector(cur_drv) * 512);
1227 if (fdctrl->data_dir != FD_DIR_WRITE ||
1228 len < FD_SECTOR_LEN || rel_pos != 0) {
1229 /* READ & SCAN commands and realign to a sector for WRITE */
1230#ifndef VBOX
1231 if (bdrv_read(cur_drv->bs, fd_sector(cur_drv),
1232 fdctrl->fifo, 1) < 0) {
1233 FLOPPY_DPRINTF("Floppy: error getting sector %d\n",
1234 fd_sector(cur_drv));
1235 /* Sure, image size is too small... */
1236 memset(fdctrl->fifo, 0, FD_SECTOR_LEN);
1237 }
1238#else
1239 cur_drv->Led.Asserted.s.fReading
1240 = cur_drv->Led.Actual.s.fReading = 1;
1241
1242 rc = cur_drv->pDrvBlock->pfnRead (
1243 cur_drv->pDrvBlock,
1244 fd_sector (cur_drv) * 512,
1245 fdctrl->fifo,
1246 1 * 512
1247 );
1248
1249 cur_drv->Led.Actual.s.fReading = 0;
1250
1251 if (RT_FAILURE (rc)) {
1252 AssertMsgFailed (
1253 ("Floppy: error getting sector %d, rc = %Rrc",
1254 fd_sector (cur_drv), rc));
1255 memset(fdctrl->fifo, 0, FD_SECTOR_LEN);
1256 }
1257#endif
1258 }
1259 switch (fdctrl->data_dir) {
1260 case FD_DIR_READ:
1261 /* READ commands */
1262#ifdef VBOX
1263 {
1264 uint32_t read;
1265 int rc2 = PDMDevHlpDMAWriteMemory(fdctrl->pDevIns, nchan,
1266 fdctrl->fifo + rel_pos,
1267 fdctrl->data_pos,
1268 len, &read);
1269 dump (fdctrl->fifo + rel_pos, len);
1270 AssertMsgRC (rc2, ("DMAWriteMemory -> %Rrc\n", rc2));
1271 }
1272#else
1273 DMA_write_memory (nchan, fdctrl->fifo + rel_pos,
1274 fdctrl->data_pos, len);
1275#endif
1276/* cpu_physical_memory_write(addr + fdctrl->data_pos, */
1277/* fdctrl->fifo + rel_pos, len); */
1278 break;
1279 case FD_DIR_WRITE:
1280 /* WRITE commands */
1281#ifdef VBOX
1282 if (cur_drv->ro)
1283 {
1284 /* Handle readonly medium early, no need to do DMA, touch the
1285 * LED or attempt any writes. A real floppy doesn't attempt
1286 * to write to readonly media either. */
1287 fdctrl_stop_transfer (fdctrl, 0x60, 0x00 | (cur_drv->ro << 1),
1288 0x00);
1289 goto transfer_error;
1290 }
1291
1292 {
1293 uint32_t written;
1294 int rc2 = PDMDevHlpDMAReadMemory(fdctrl->pDevIns, nchan,
1295 fdctrl->fifo + rel_pos,
1296 fdctrl->data_pos,
1297 len, &written);
1298 AssertMsgRC (rc2, ("DMAReadMemory -> %Rrc\n", rc2));
1299 }
1300
1301 cur_drv->Led.Asserted.s.fWriting
1302 = cur_drv->Led.Actual.s.fWriting = 1;
1303
1304 rc = cur_drv->pDrvBlock->pfnWrite (
1305 cur_drv->pDrvBlock,
1306 fd_sector (cur_drv) * 512,
1307 fdctrl->fifo,
1308 1 * 512
1309 );
1310
1311 cur_drv->Led.Actual.s.fWriting = 0;
1312
1313 if (RT_FAILURE (rc)) {
1314 AssertMsgFailed(("Floppy: error writing sector %d. rc=%Rrc",
1315 fd_sector (cur_drv), rc));
1316 fdctrl_stop_transfer (fdctrl, 0x60, 0x00 | (cur_drv->ro << 1),
1317 0x00);
1318 goto transfer_error;
1319 }
1320#else
1321 DMA_read_memory (nchan, fdctrl->fifo + rel_pos,
1322 fdctrl->data_pos, len);
1323/* cpu_physical_memory_read(addr + fdctrl->data_pos, */
1324/* fdctrl->fifo + rel_pos, len); */
1325 if (bdrv_write(cur_drv->bs, fd_sector(cur_drv),
1326 fdctrl->fifo, 1) < 0) {
1327 FLOPPY_ERROR("writting sector %d\n", fd_sector(cur_drv));
1328 fdctrl_stop_transfer(fdctrl, 0x60, 0x00, 0x00);
1329 goto transfer_error;
1330 }
1331#endif
1332 break;
1333 default:
1334 /* SCAN commands */
1335 {
1336 uint8_t tmpbuf[FD_SECTOR_LEN];
1337 int ret;
1338#ifdef VBOX
1339 uint32_t read;
1340 int rc2 = PDMDevHlpDMAReadMemory (fdctrl->pDevIns, nchan, tmpbuf,
1341 fdctrl->data_pos, len, &read);
1342 AssertMsg (RT_SUCCESS (rc2), ("DMAReadMemory -> %Rrc2\n", rc2));
1343#else
1344 DMA_read_memory (nchan, tmpbuf, fdctrl->data_pos, len);
1345#endif
1346/* cpu_physical_memory_read(addr + fdctrl->data_pos, */
1347/* tmpbuf, len); */
1348 ret = memcmp(tmpbuf, fdctrl->fifo + rel_pos, len);
1349 if (ret == 0) {
1350 status2 = 0x08;
1351 goto end_transfer;
1352 }
1353 if ((ret < 0 && fdctrl->data_dir == FD_DIR_SCANL) ||
1354 (ret > 0 && fdctrl->data_dir == FD_DIR_SCANH)) {
1355 status2 = 0x00;
1356 goto end_transfer;
1357 }
1358 }
1359 break;
1360 }
1361 fdctrl->data_pos += len;
1362 rel_pos = fdctrl->data_pos % FD_SECTOR_LEN;
1363 if (rel_pos == 0) {
1364 /* Seek to next sector */
1365 FLOPPY_DPRINTF("seek to next sector (%d %02x %02x => %d) (%d)\n",
1366 cur_drv->head, cur_drv->track, cur_drv->sect,
1367 fd_sector(cur_drv),
1368 fdctrl->data_pos - len);
1369 /* XXX: cur_drv->sect >= cur_drv->last_sect should be an
1370 error in fact */
1371 if (cur_drv->sect >= cur_drv->last_sect ||
1372 cur_drv->sect == fdctrl->eot) {
1373 cur_drv->sect = 1;
1374 if (FD_MULTI_TRACK(fdctrl->data_state)) {
1375 if (cur_drv->head == 0 &&
1376 (cur_drv->flags & FDISK_DBL_SIDES) != 0) {
1377 cur_drv->head = 1;
1378 } else {
1379 cur_drv->head = 0;
1380 cur_drv->track++;
1381 if ((cur_drv->flags & FDISK_DBL_SIDES) == 0)
1382 break;
1383 }
1384 } else {
1385 cur_drv->track++;
1386 break;
1387 }
1388 FLOPPY_DPRINTF("seek to next track (%d %02x %02x => %d)\n",
1389 cur_drv->head, cur_drv->track,
1390 cur_drv->sect, fd_sector(cur_drv));
1391 } else {
1392 cur_drv->sect++;
1393 }
1394 }
1395 }
1396end_transfer:
1397 len = fdctrl->data_pos - start_pos;
1398 FLOPPY_DPRINTF("end transfer %d %d %d\n",
1399 fdctrl->data_pos, len, fdctrl->data_len);
1400 if (fdctrl->data_dir == FD_DIR_SCANE ||
1401 fdctrl->data_dir == FD_DIR_SCANL ||
1402 fdctrl->data_dir == FD_DIR_SCANH)
1403 status2 = 0x08;
1404 if (FD_DID_SEEK(fdctrl->data_state))
1405 status0 |= 0x20;
1406 fdctrl->data_len -= len;
1407 /* if (fdctrl->data_len == 0) */
1408 fdctrl_stop_transfer(fdctrl, status0, status1, status2);
1409transfer_error:
1410
1411 return len;
1412}
1413
1414#if 0
1415{
1416 fdctrl_t *fdctrl;
1417 fdrive_t *cur_drv;
1418 int len, start_pos, rel_pos;
1419 uint8_t status0 = 0x00, status1 = 0x00, status2 = 0x00;
1420
1421 fdctrl = opaque;
1422 if (!(fdctrl->state & FD_CTRL_BUSY)) {
1423 FLOPPY_DPRINTF("Not in DMA transfer mode !\n");
1424 return 0;
1425 }
1426 cur_drv = get_cur_drv(fdctrl);
1427 if (fdctrl->data_dir == FD_DIR_SCANE || fdctrl->data_dir == FD_DIR_SCANL ||
1428 fdctrl->data_dir == FD_DIR_SCANH)
1429 status2 = 0x04;
1430 if (size > fdctrl->data_len)
1431 size = fdctrl->data_len;
1432#ifndef VBOX
1433 if (cur_drv->bs == NULL) {
1434#else
1435 if (cur_drv->pDrvBase == NULL) {
1436#endif
1437 if (fdctrl->data_dir == FD_DIR_WRITE)
1438 fdctrl_stop_transfer(fdctrl, 0x60, 0x00, 0x00);
1439 else
1440 fdctrl_stop_transfer(fdctrl, 0x40, 0x00, 0x00);
1441 len = 0;
1442 goto transfer_error;
1443 }
1444 rel_pos = fdctrl->data_pos % FD_SECTOR_LEN;
1445 for (start_pos = fdctrl->data_pos; fdctrl->data_pos < size;) {
1446 len = size - fdctrl->data_pos;
1447 if (len + rel_pos > FD_SECTOR_LEN)
1448 len = FD_SECTOR_LEN - rel_pos;
1449 FLOPPY_DPRINTF("copy %d bytes (%d %d %d) %d pos %d %02x %02x "
1450 "(%d-0x%08x 0x%08x)\n", len, size, fdctrl->data_pos,
1451 fdctrl->data_len, fdctrl->cur_drv, cur_drv->head,
1452 cur_drv->track, cur_drv->sect, fd_sector(cur_drv),
1453 fd_sector(cur_drv) * 512, addr);
1454 if (fdctrl->data_dir != FD_DIR_WRITE ||
1455 len < FD_SECTOR_LEN || rel_pos != 0) {
1456 /* READ & SCAN commands and realign to a sector for WRITE */
1457#ifndef VBOX
1458 if (bdrv_read(cur_drv->bs, fd_sector(cur_drv),
1459 fdctrl->fifo, 1) < 0) {
1460 FLOPPY_DPRINTF("Floppy: error getting sector %d\n",
1461 fd_sector(cur_drv));
1462 /* Sure, image size is too small... */
1463 memset(fdctrl->fifo, 0, FD_SECTOR_LEN);
1464 }
1465#else
1466 int rc;
1467
1468 cur_drv->Led.Asserted.s.fReading
1469 = cur_drv->Led.Actual.s.fReading = 1;
1470
1471 rc = cur_drv->pDrvBlock->pfnRead (
1472 cur_drv->pDrvBlock,
1473 fd_sector (cur_drv) * 512,
1474 fdctrl->fifo,
1475 1 * 512
1476 );
1477
1478 cur_drv->Led.Actual.s.fReading = 0;
1479
1480 if (RT_FAILURE (rc)) {
1481 AssertMsgFailed (
1482 ("Floppy: error getting sector %d. rc=%Rrc\n",
1483 fd_sector(cur_drv), rc));
1484 /* Sure, image size is too small... */
1485 memset(fdctrl->fifo, 0, FD_SECTOR_LEN);
1486 }
1487 /* *p_fd_activity = 1; */
1488#endif
1489 }
1490 switch (fdctrl->data_dir) {
1491 case FD_DIR_READ:
1492 /* READ commands */
1493#ifdef VBOX
1494 PDMDevHlpPhysWrite (fdctrl->pDevIns, addr + fdctrl->data_pos,
1495 fdctrl->fifo + rel_pos, len);
1496#else
1497 cpu_physical_memory_write(addr + fdctrl->data_pos,
1498 fdctrl->fifo + rel_pos, len);
1499#endif
1500 break;
1501 case FD_DIR_WRITE:
1502 /* WRITE commands */
1503#ifdef VBOX
1504 {
1505 int rc;
1506
1507 PDMDevHlpPhysRead (fdctrl->pDevIns, addr + fdctrl->data_pos,
1508 fdctrl->fifo + rel_pos, len);
1509
1510 cur_drv->Led.Asserted.s.fWriting
1511 = cur_drv->Led.Actual.s.fWriting = 1;
1512
1513 rc = cur_drv->pDrvBlock->pfnWrite (
1514 cur_drv->pDrvBlock,
1515 fd_sector (cur_drv) * 512,
1516 fdctrl->fifo,
1517 1 * 512
1518 );
1519
1520 cur_drv->Led.Actual.s.fWriting = 0;
1521
1522 if (RT_FAILURE (rc)) {
1523 AssertMsgFailed (
1524 ("Floppy: error writting sector %d. rc=%Rrc\n",
1525 fd_sector(cur_drv), rc));
1526 fdctrl_stop_transfer(fdctrl, 0x60, 0x00, 0x00);
1527 goto transfer_error;
1528 }
1529 }
1530 /* *p_fd_activity = 2; */
1531#else /* !VBOX */
1532 cpu_physical_memory_read(addr + fdctrl->data_pos,
1533 fdctrl->fifo + rel_pos, len);
1534 if (bdrv_write(cur_drv->bs, fd_sector(cur_drv),
1535 fdctrl->fifo, 1) < 0) {
1536 FLOPPY_ERROR("writting sector %d\n", fd_sector(cur_drv));
1537 fdctrl_stop_transfer(fdctrl, 0x60, 0x00, 0x00);
1538 goto transfer_error;
1539 }
1540#endif
1541 break;
1542 default:
1543 /* SCAN commands */
1544 {
1545 uint8_t tmpbuf[FD_SECTOR_LEN];
1546 int ret;
1547#ifdef VBOX
1548 PDMDevHlpPhysRead (fdctrl->pDevIns, addr + fdctrl->data_pos,
1549 tmpbuf, len);
1550#else
1551 cpu_physical_memory_read(addr + fdctrl->data_pos,
1552 tmpbuf, len);
1553#endif
1554 ret = memcmp(tmpbuf, fdctrl->fifo + rel_pos, len);
1555 if (ret == 0) {
1556 status2 = 0x08;
1557 goto end_transfer;
1558 }
1559 if ((ret < 0 && fdctrl->data_dir == FD_DIR_SCANL) ||
1560 (ret > 0 && fdctrl->data_dir == FD_DIR_SCANH)) {
1561 status2 = 0x00;
1562 goto end_transfer;
1563 }
1564 }
1565 break;
1566 }
1567 fdctrl->data_pos += len;
1568 rel_pos = fdctrl->data_pos % FD_SECTOR_LEN;
1569 if (rel_pos == 0) {
1570 /* Seek to next sector */
1571 FLOPPY_DPRINTF("seek to next sector (%d %02x %02x => %d) (%d)\n",
1572 cur_drv->head, cur_drv->track, cur_drv->sect,
1573 fd_sector(cur_drv),
1574 fdctrl->data_pos - size);
1575 /* XXX: cur_drv->sect >= cur_drv->last_sect should be an
1576 error in fact */
1577 if (cur_drv->sect >= cur_drv->last_sect ||
1578 cur_drv->sect == fdctrl->eot) {
1579 cur_drv->sect = 1;
1580 if (FD_MULTI_TRACK(fdctrl->data_state)) {
1581 if (cur_drv->head == 0 &&
1582 (cur_drv->flags & FDISK_DBL_SIDES) != 0) {
1583 cur_drv->head = 1;
1584 } else {
1585 cur_drv->head = 0;
1586 cur_drv->track++;
1587 if ((cur_drv->flags & FDISK_DBL_SIDES) == 0)
1588 break;
1589 }
1590 } else {
1591 cur_drv->track++;
1592 break;
1593 }
1594 FLOPPY_DPRINTF("seek to next track (%d %02x %02x => %d)\n",
1595 cur_drv->head, cur_drv->track,
1596 cur_drv->sect, fd_sector(cur_drv));
1597 } else {
1598 cur_drv->sect++;
1599 }
1600 }
1601 }
1602end_transfer:
1603 len = fdctrl->data_pos - start_pos;
1604 FLOPPY_DPRINTF("end transfer %d %d %d\n",
1605 fdctrl->data_pos, len, fdctrl->data_len);
1606 if (fdctrl->data_dir == FD_DIR_SCANE ||
1607 fdctrl->data_dir == FD_DIR_SCANL ||
1608 fdctrl->data_dir == FD_DIR_SCANH)
1609 status2 = 0x08;
1610 if (FD_DID_SEEK(fdctrl->data_state))
1611 status0 |= 0x20;
1612 fdctrl->data_len -= len;
1613 /* // if (fdctrl->data_len == 0) */
1614 fdctrl_stop_transfer(fdctrl, status0, status1, status2);
1615transfer_error:
1616
1617 return len;
1618}
1619#endif
1620
1621/* Data register : 0x05 */
1622static uint32_t fdctrl_read_data (fdctrl_t *fdctrl)
1623{
1624 fdrive_t *cur_drv;
1625 uint32_t retval = 0;
1626 unsigned pos, len;
1627#ifdef VBOX
1628 int rc;
1629#endif
1630
1631 cur_drv = get_cur_drv(fdctrl);
1632 fdctrl->state &= ~FD_CTRL_SLEEP;
1633 if (FD_STATE(fdctrl->data_state) == FD_STATE_CMD) {
1634 FLOPPY_ERROR("can't read data in CMD state\n");
1635 return 0;
1636 }
1637 pos = fdctrl->data_pos;
1638 if (FD_STATE(fdctrl->data_state) == FD_STATE_DATA) {
1639 pos %= FD_SECTOR_LEN;
1640 if (pos == 0) {
1641 len = fdctrl->data_len - fdctrl->data_pos;
1642 if (len > FD_SECTOR_LEN)
1643 len = FD_SECTOR_LEN;
1644#ifdef VBOX
1645 cur_drv->Led.Asserted.s.fReading
1646 = cur_drv->Led.Actual.s.fReading = 1;
1647
1648 Assert(len <= sizeof(fdctrl->fifo));
1649 rc = cur_drv->pDrvBlock->pfnRead (
1650 cur_drv->pDrvBlock,
1651 fd_sector (cur_drv) * 512,
1652 fdctrl->fifo,
1653 len
1654 );
1655
1656 cur_drv->Led.Actual.s.fReading = 0;
1657
1658 if (RT_FAILURE (rc)) {
1659 AssertMsgFailed (
1660 ("Floppy: Failure to read sector %d. rc=%Rrc",
1661 fd_sector (cur_drv), rc));
1662 }
1663#else
1664 bdrv_read(cur_drv->bs, fd_sector(cur_drv),
1665 fdctrl->fifo, len);
1666#endif
1667 }
1668 }
1669 retval = fdctrl->fifo[pos];
1670 if (++fdctrl->data_pos == fdctrl->data_len) {
1671 fdctrl->data_pos = 0;
1672 /* Switch from transfer mode to status mode
1673 * then from status mode to command mode
1674 */
1675 if (FD_STATE(fdctrl->data_state) == FD_STATE_DATA) {
1676 fdctrl_stop_transfer(fdctrl, 0x20, 0x00, 0x00);
1677 } else {
1678 fdctrl_reset_fifo(fdctrl);
1679 fdctrl_reset_irq(fdctrl);
1680 }
1681 }
1682 FLOPPY_DPRINTF("data register: 0x%02x\n", retval);
1683
1684 return retval;
1685}
1686
1687static void fdctrl_format_sector (fdctrl_t *fdctrl)
1688{
1689 fdrive_t *cur_drv;
1690 uint8_t kh, kt, ks;
1691 int did_seek;
1692#ifdef VBOX
1693 int ok = 0, rc;
1694#endif
1695
1696 fdctrl->cur_drv = fdctrl->fifo[1] & 1;
1697 cur_drv = get_cur_drv(fdctrl);
1698 kt = fdctrl->fifo[6];
1699 kh = fdctrl->fifo[7];
1700 ks = fdctrl->fifo[8];
1701 FLOPPY_DPRINTF("format sector at %d %d %02x %02x (%d)\n",
1702 fdctrl->cur_drv, kh, kt, ks,
1703 _fd_sector(kh, kt, ks, cur_drv->last_sect));
1704 did_seek = 0;
1705 switch (fd_seek(cur_drv, kh, kt, ks, fdctrl->config & 0x40)) {
1706 case 2:
1707 /* sect too big */
1708 fdctrl_stop_transfer(fdctrl, 0x40, 0x00, 0x00);
1709 fdctrl->fifo[3] = kt;
1710 fdctrl->fifo[4] = kh;
1711 fdctrl->fifo[5] = ks;
1712 return;
1713 case 3:
1714 /* track too big */
1715 fdctrl_stop_transfer(fdctrl, 0x40, 0x80, 0x00);
1716 fdctrl->fifo[3] = kt;
1717 fdctrl->fifo[4] = kh;
1718 fdctrl->fifo[5] = ks;
1719 return;
1720 case 4:
1721 /* No seek enabled */
1722 fdctrl_stop_transfer(fdctrl, 0x40, 0x00, 0x00);
1723 fdctrl->fifo[3] = kt;
1724 fdctrl->fifo[4] = kh;
1725 fdctrl->fifo[5] = ks;
1726 return;
1727 case 1:
1728 did_seek = 1;
1729 fdctrl->data_state |= FD_STATE_SEEK;
1730 break;
1731 default:
1732 break;
1733 }
1734 memset(fdctrl->fifo, 0, FD_SECTOR_LEN);
1735#ifdef VBOX
1736 /* *p_fd_activity = 2; */
1737 if (cur_drv->pDrvBlock) {
1738 cur_drv->Led.Asserted.s.fWriting = cur_drv->Led.Actual.s.fWriting = 1;
1739
1740 rc = cur_drv->pDrvBlock->pfnWrite (
1741 cur_drv->pDrvBlock,
1742 fd_sector (cur_drv) * 512,
1743 fdctrl->fifo,
1744 1 * 512
1745 );
1746
1747 cur_drv->Led.Actual.s.fWriting = 0;
1748
1749 if (RT_FAILURE (rc)) {
1750 AssertMsgFailed (("Floppy: error formating sector %d. rc=%Rrc\n",
1751 fd_sector (cur_drv), rc));
1752 fdctrl_stop_transfer(fdctrl, 0x60, 0x00, 0x00);
1753 }
1754 else {
1755 ok = 1;
1756 }
1757 }
1758 if (ok) {
1759#else
1760 if (cur_drv->bs == NULL ||
1761 bdrv_write(cur_drv->bs, fd_sector(cur_drv), fdctrl->fifo, 1) < 0) {
1762 FLOPPY_ERROR("formating sector %d\n", fd_sector(cur_drv));
1763 fdctrl_stop_transfer(fdctrl, 0x60, 0x00, 0x00);
1764 } else {
1765#endif
1766 if (cur_drv->sect == cur_drv->last_sect) {
1767 fdctrl->data_state &= ~FD_STATE_FORMAT;
1768 /* Last sector done */
1769 if (FD_DID_SEEK(fdctrl->data_state))
1770 fdctrl_stop_transfer(fdctrl, 0x20, 0x00, 0x00);
1771 else
1772 fdctrl_stop_transfer(fdctrl, 0x00, 0x00, 0x00);
1773 } else {
1774 /* More to do */
1775 fdctrl->data_pos = 0;
1776 fdctrl->data_len = 4;
1777 }
1778 }
1779}
1780
1781static void fdctrl_write_data (fdctrl_t *fdctrl, uint32_t value)
1782{
1783 fdrive_t *cur_drv;
1784
1785 cur_drv = get_cur_drv(fdctrl);
1786 /* Reset mode */
1787 if (fdctrl->state & FD_CTRL_RESET) {
1788 FLOPPY_DPRINTF("Floppy controller in RESET state !\n");
1789 return;
1790 }
1791 fdctrl->state &= ~FD_CTRL_SLEEP;
1792 if (FD_STATE(fdctrl->data_state) == FD_STATE_STATUS) {
1793 FLOPPY_ERROR("can't write data in status mode\n");
1794 return;
1795 }
1796 /* Is it write command time ? */
1797 if (FD_STATE(fdctrl->data_state) == FD_STATE_DATA) {
1798 /* FIFO data write */
1799 fdctrl->fifo[fdctrl->data_pos++] = value;
1800 if (fdctrl->data_pos % FD_SECTOR_LEN == (FD_SECTOR_LEN - 1) ||
1801 fdctrl->data_pos == fdctrl->data_len) {
1802#ifdef VBOX
1803 int rc;
1804 /* *p_fd_activity = 2; */
1805 cur_drv->Led.Asserted.s.fWriting
1806 = cur_drv->Led.Actual.s.fWriting = 1;
1807
1808 rc = cur_drv->pDrvBlock->pfnWrite (
1809 cur_drv->pDrvBlock,
1810 fd_sector (cur_drv) * 512,
1811 fdctrl->fifo,
1812 FD_SECTOR_LEN
1813 );
1814
1815 cur_drv->Led.Actual.s.fWriting = 0;
1816
1817 if (RT_FAILURE (rc)) {
1818 AssertMsgFailed (
1819 ("Floppy: failed to write sector %d. rc=%Rrc\n",
1820 fd_sector (cur_drv), rc));
1821 }
1822#else
1823 bdrv_write(cur_drv->bs, fd_sector(cur_drv),
1824 fdctrl->fifo, FD_SECTOR_LEN);
1825#endif
1826 }
1827 /* Switch from transfer mode to status mode
1828 * then from status mode to command mode
1829 */
1830 if (FD_STATE(fdctrl->data_state) == FD_STATE_DATA)
1831 fdctrl_stop_transfer(fdctrl, 0x20, 0x00, 0x00);
1832 return;
1833 }
1834 if (fdctrl->data_pos == 0) {
1835 /* Command */
1836 switch (value & 0x5F) {
1837 case 0x46:
1838 /* READ variants */
1839 FLOPPY_DPRINTF("READ command\n");
1840 /* 8 parameters cmd */
1841 fdctrl->data_len = 9;
1842 goto enqueue;
1843 case 0x4C:
1844 /* READ_DELETED variants */
1845 FLOPPY_DPRINTF("READ_DELETED command\n");
1846 /* 8 parameters cmd */
1847 fdctrl->data_len = 9;
1848 goto enqueue;
1849 case 0x50:
1850 /* SCAN_EQUAL variants */
1851 FLOPPY_DPRINTF("SCAN_EQUAL command\n");
1852 /* 8 parameters cmd */
1853 fdctrl->data_len = 9;
1854 goto enqueue;
1855 case 0x56:
1856 /* VERIFY variants */
1857 FLOPPY_DPRINTF("VERIFY command\n");
1858 /* 8 parameters cmd */
1859 fdctrl->data_len = 9;
1860 goto enqueue;
1861 case 0x59:
1862 /* SCAN_LOW_OR_EQUAL variants */
1863 FLOPPY_DPRINTF("SCAN_LOW_OR_EQUAL command\n");
1864 /* 8 parameters cmd */
1865 fdctrl->data_len = 9;
1866 goto enqueue;
1867 case 0x5D:
1868 /* SCAN_HIGH_OR_EQUAL variants */
1869 FLOPPY_DPRINTF("SCAN_HIGH_OR_EQUAL command\n");
1870 /* 8 parameters cmd */
1871 fdctrl->data_len = 9;
1872 goto enqueue;
1873 default:
1874 break;
1875 }
1876 switch (value & 0x7F) {
1877 case 0x45:
1878 /* WRITE variants */
1879 FLOPPY_DPRINTF("WRITE command\n");
1880 /* 8 parameters cmd */
1881 fdctrl->data_len = 9;
1882 goto enqueue;
1883 case 0x49:
1884 /* WRITE_DELETED variants */
1885 FLOPPY_DPRINTF("WRITE_DELETED command\n");
1886 /* 8 parameters cmd */
1887 fdctrl->data_len = 9;
1888 goto enqueue;
1889 default:
1890 break;
1891 }
1892 switch (value) {
1893 case 0x03:
1894 /* SPECIFY */
1895 FLOPPY_DPRINTF("SPECIFY command\n");
1896 /* 1 parameter cmd */
1897 fdctrl->data_len = 3;
1898 goto enqueue;
1899 case 0x04:
1900 /* SENSE_DRIVE_STATUS */
1901 FLOPPY_DPRINTF("SENSE_DRIVE_STATUS command\n");
1902 /* 1 parameter cmd */
1903 fdctrl->data_len = 2;
1904 goto enqueue;
1905 case 0x07:
1906 /* RECALIBRATE */
1907 FLOPPY_DPRINTF("RECALIBRATE command\n");
1908 /* 1 parameter cmd */
1909 fdctrl->data_len = 2;
1910 goto enqueue;
1911 case 0x08:
1912 /* SENSE_INTERRUPT_STATUS */
1913 FLOPPY_DPRINTF("SENSE_INTERRUPT_STATUS command (%02x)\n",
1914 fdctrl->int_status);
1915 /* No parameters cmd: returns status if no interrupt */
1916#if 0
1917 fdctrl->fifo[0] =
1918 fdctrl->int_status | (cur_drv->head << 2) | fdctrl->cur_drv;
1919#else
1920 /* XXX: int_status handling is broken for read/write
1921 commands, so we do this hack. It should be suppressed
1922 ASAP */
1923 fdctrl->fifo[0] =
1924 0x20 | (cur_drv->head << 2) | fdctrl->cur_drv;
1925#endif
1926 fdctrl->fifo[1] = cur_drv->track;
1927 fdctrl_set_fifo(fdctrl, 2, 0);
1928 fdctrl_reset_irq(fdctrl);
1929 fdctrl->int_status = 0xC0;
1930 return;
1931 case 0x0E:
1932 /* DUMPREG */
1933 FLOPPY_DPRINTF("DUMPREG command\n");
1934 /* Drives position */
1935 fdctrl->fifo[0] = drv0(fdctrl)->track;
1936 fdctrl->fifo[1] = drv1(fdctrl)->track;
1937 fdctrl->fifo[2] = 0;
1938 fdctrl->fifo[3] = 0;
1939 /* timers */
1940 fdctrl->fifo[4] = fdctrl->timer0;
1941 fdctrl->fifo[5] = (fdctrl->timer1 << 1) | fdctrl->dma_en;
1942 fdctrl->fifo[6] = cur_drv->last_sect;
1943 fdctrl->fifo[7] = (fdctrl->lock << 7) |
1944 (cur_drv->perpendicular << 2);
1945 fdctrl->fifo[8] = fdctrl->config;
1946 fdctrl->fifo[9] = fdctrl->precomp_trk;
1947 fdctrl_set_fifo(fdctrl, 10, 0);
1948 return;
1949 case 0x0F:
1950 /* SEEK */
1951 FLOPPY_DPRINTF("SEEK command\n");
1952 /* 2 parameters cmd */
1953 fdctrl->data_len = 3;
1954 goto enqueue;
1955 case 0x10:
1956 /* VERSION */
1957 FLOPPY_DPRINTF("VERSION command\n");
1958 /* No parameters cmd */
1959 /* Controller's version */
1960 fdctrl->fifo[0] = fdctrl->version;
1961 fdctrl_set_fifo(fdctrl, 1, 1);
1962 return;
1963 case 0x12:
1964 /* PERPENDICULAR_MODE */
1965 FLOPPY_DPRINTF("PERPENDICULAR_MODE command\n");
1966 /* 1 parameter cmd */
1967 fdctrl->data_len = 2;
1968 goto enqueue;
1969 case 0x13:
1970 /* CONFIGURE */
1971 FLOPPY_DPRINTF("CONFIGURE command\n");
1972 /* 3 parameters cmd */
1973 fdctrl->data_len = 4;
1974 goto enqueue;
1975 case 0x14:
1976 /* UNLOCK */
1977 FLOPPY_DPRINTF("UNLOCK command\n");
1978 /* No parameters cmd */
1979 fdctrl->lock = 0;
1980 fdctrl->fifo[0] = 0;
1981 fdctrl_set_fifo(fdctrl, 1, 0);
1982 return;
1983 case 0x17:
1984 /* POWERDOWN_MODE */
1985 FLOPPY_DPRINTF("POWERDOWN_MODE command\n");
1986 /* 2 parameters cmd */
1987 fdctrl->data_len = 3;
1988 goto enqueue;
1989 case 0x18:
1990 /* PART_ID */
1991 FLOPPY_DPRINTF("PART_ID command\n");
1992 /* No parameters cmd */
1993 fdctrl->fifo[0] = 0x41; /* Stepping 1 */
1994 fdctrl_set_fifo(fdctrl, 1, 0);
1995 return;
1996 case 0x2C:
1997 /* SAVE */
1998 FLOPPY_DPRINTF("SAVE command\n");
1999 /* No parameters cmd */
2000 fdctrl->fifo[0] = 0;
2001 fdctrl->fifo[1] = 0;
2002 /* Drives position */
2003 fdctrl->fifo[2] = drv0(fdctrl)->track;
2004 fdctrl->fifo[3] = drv1(fdctrl)->track;
2005 fdctrl->fifo[4] = 0;
2006 fdctrl->fifo[5] = 0;
2007 /* timers */
2008 fdctrl->fifo[6] = fdctrl->timer0;
2009 fdctrl->fifo[7] = fdctrl->timer1;
2010 fdctrl->fifo[8] = cur_drv->last_sect;
2011 fdctrl->fifo[9] = (fdctrl->lock << 7) |
2012 (cur_drv->perpendicular << 2);
2013 fdctrl->fifo[10] = fdctrl->config;
2014 fdctrl->fifo[11] = fdctrl->precomp_trk;
2015 fdctrl->fifo[12] = fdctrl->pwrd;
2016 fdctrl->fifo[13] = 0;
2017 fdctrl->fifo[14] = 0;
2018 fdctrl_set_fifo(fdctrl, 15, 1);
2019 return;
2020 case 0x33:
2021 /* OPTION */
2022 FLOPPY_DPRINTF("OPTION command\n");
2023 /* 1 parameter cmd */
2024 fdctrl->data_len = 2;
2025 goto enqueue;
2026 case 0x42:
2027 /* READ_TRACK */
2028 FLOPPY_DPRINTF("READ_TRACK command\n");
2029 /* 8 parameters cmd */
2030 fdctrl->data_len = 9;
2031 goto enqueue;
2032 case 0x4A:
2033 /* READ_ID */
2034 FLOPPY_DPRINTF("READ_ID command\n");
2035 /* 1 parameter cmd */
2036 fdctrl->data_len = 2;
2037 goto enqueue;
2038 case 0x4C:
2039 /* RESTORE */
2040 FLOPPY_DPRINTF("RESTORE command\n");
2041 /* 17 parameters cmd */
2042 fdctrl->data_len = 18;
2043 goto enqueue;
2044 case 0x4D:
2045 /* FORMAT_TRACK */
2046 FLOPPY_DPRINTF("FORMAT_TRACK command\n");
2047 /* 5 parameters cmd */
2048 fdctrl->data_len = 6;
2049 goto enqueue;
2050 case 0x8E:
2051 /* DRIVE_SPECIFICATION_COMMAND */
2052 FLOPPY_DPRINTF("DRIVE_SPECIFICATION_COMMAND command\n");
2053 /* 5 parameters cmd */
2054 fdctrl->data_len = 6;
2055 goto enqueue;
2056 case 0x8F:
2057 /* RELATIVE_SEEK_OUT */
2058 FLOPPY_DPRINTF("RELATIVE_SEEK_OUT command\n");
2059 /* 2 parameters cmd */
2060 fdctrl->data_len = 3;
2061 goto enqueue;
2062 case 0x94:
2063 /* LOCK */
2064 FLOPPY_DPRINTF("LOCK command\n");
2065 /* No parameters cmd */
2066 fdctrl->lock = 1;
2067 fdctrl->fifo[0] = 0x10;
2068 fdctrl_set_fifo(fdctrl, 1, 1);
2069 return;
2070 case 0xCD:
2071 /* FORMAT_AND_WRITE */
2072 FLOPPY_DPRINTF("FORMAT_AND_WRITE command\n");
2073 /* 10 parameters cmd */
2074 fdctrl->data_len = 11;
2075 goto enqueue;
2076 case 0xCF:
2077 /* RELATIVE_SEEK_IN */
2078 FLOPPY_DPRINTF("RELATIVE_SEEK_IN command\n");
2079 /* 2 parameters cmd */
2080 fdctrl->data_len = 3;
2081 goto enqueue;
2082 default:
2083 /* Unknown command */
2084 FLOPPY_ERROR("unknown command: 0x%02x\n", value);
2085 fdctrl_unimplemented(fdctrl);
2086 return;
2087 }
2088 }
2089enqueue:
2090 FLOPPY_DPRINTF("%s: %02x\n", __func__, value);
2091 fdctrl->fifo[fdctrl->data_pos] = value;
2092 if (++fdctrl->data_pos == fdctrl->data_len) {
2093 /* We now have all parameters
2094 * and will be able to treat the command
2095 */
2096 if (fdctrl->data_state & FD_STATE_FORMAT) {
2097 fdctrl_format_sector(fdctrl);
2098 return;
2099 }
2100 switch (fdctrl->fifo[0] & 0x1F) {
2101 case 0x06:
2102 {
2103 /* READ variants */
2104 FLOPPY_DPRINTF("treat READ command\n");
2105 fdctrl_start_transfer(fdctrl, FD_DIR_READ);
2106 return;
2107 }
2108 case 0x0C:
2109 /* READ_DELETED variants */
2110/* // FLOPPY_DPRINTF("treat READ_DELETED command\n"); */
2111 FLOPPY_ERROR("treat READ_DELETED command\n");
2112 fdctrl_start_transfer_del(fdctrl, FD_DIR_READ);
2113 return;
2114 case 0x16:
2115 /* VERIFY variants */
2116/* // FLOPPY_DPRINTF("treat VERIFY command\n"); */
2117 FLOPPY_ERROR("treat VERIFY command\n");
2118 fdctrl_stop_transfer(fdctrl, 0x20, 0x00, 0x00);
2119 return;
2120 case 0x10:
2121 /* SCAN_EQUAL variants */
2122/* // FLOPPY_DPRINTF("treat SCAN_EQUAL command\n"); */
2123 FLOPPY_ERROR("treat SCAN_EQUAL command\n");
2124 fdctrl_start_transfer(fdctrl, FD_DIR_SCANE);
2125 return;
2126 case 0x19:
2127 /* SCAN_LOW_OR_EQUAL variants */
2128/* // FLOPPY_DPRINTF("treat SCAN_LOW_OR_EQUAL command\n"); */
2129 FLOPPY_ERROR("treat SCAN_LOW_OR_EQUAL command\n");
2130 fdctrl_start_transfer(fdctrl, FD_DIR_SCANL);
2131 return;
2132 case 0x1D:
2133 /* SCAN_HIGH_OR_EQUAL variants */
2134/* // FLOPPY_DPRINTF("treat SCAN_HIGH_OR_EQUAL command\n"); */
2135 FLOPPY_ERROR("treat SCAN_HIGH_OR_EQUAL command\n");
2136 fdctrl_start_transfer(fdctrl, FD_DIR_SCANH);
2137 return;
2138 default:
2139 break;
2140 }
2141 switch (fdctrl->fifo[0] & 0x3F) {
2142 case 0x05:
2143 /* WRITE variants */
2144 FLOPPY_DPRINTF("treat WRITE command (%02x)\n", fdctrl->fifo[0]);
2145 fdctrl_start_transfer(fdctrl, FD_DIR_WRITE);
2146 return;
2147 case 0x09:
2148 /* WRITE_DELETED variants */
2149/* // FLOPPY_DPRINTF("treat WRITE_DELETED command\n"); */
2150 FLOPPY_ERROR("treat WRITE_DELETED command\n");
2151 fdctrl_start_transfer_del(fdctrl, FD_DIR_WRITE);
2152 return;
2153 default:
2154 break;
2155 }
2156 switch (fdctrl->fifo[0]) {
2157 case 0x03:
2158 /* SPECIFY */
2159 FLOPPY_DPRINTF("treat SPECIFY command\n");
2160 fdctrl->timer0 = (fdctrl->fifo[1] >> 4) & 0xF;
2161 fdctrl->timer1 = fdctrl->fifo[2] >> 1;
2162 fdctrl->dma_en = 1 - (fdctrl->fifo[2] & 1) ;
2163 /* No result back */
2164 fdctrl_reset_fifo(fdctrl);
2165 break;
2166 case 0x04:
2167 /* SENSE_DRIVE_STATUS */
2168 FLOPPY_DPRINTF("treat SENSE_DRIVE_STATUS command\n");
2169 fdctrl->cur_drv = fdctrl->fifo[1] & 1;
2170 cur_drv = get_cur_drv(fdctrl);
2171 cur_drv->head = (fdctrl->fifo[1] >> 2) & 1;
2172 /* 1 Byte status back */
2173 fdctrl->fifo[0] = (cur_drv->ro << 6) |
2174 (cur_drv->track == 0 ? 0x10 : 0x00) |
2175 (cur_drv->head << 2) |
2176 fdctrl->cur_drv |
2177 0x28;
2178 fdctrl_set_fifo(fdctrl, 1, 0);
2179 break;
2180 case 0x07:
2181 /* RECALIBRATE */
2182 FLOPPY_DPRINTF("treat RECALIBRATE command\n");
2183 fdctrl->cur_drv = fdctrl->fifo[1] & 1;
2184 cur_drv = get_cur_drv(fdctrl);
2185 fd_recalibrate(cur_drv);
2186 fdctrl_reset_fifo(fdctrl);
2187 /* Raise Interrupt */
2188 fdctrl_raise_irq(fdctrl, 0x20);
2189 break;
2190 case 0x0F:
2191 /* SEEK */
2192 FLOPPY_DPRINTF("treat SEEK command\n");
2193 fdctrl->cur_drv = fdctrl->fifo[1] & 1;
2194 cur_drv = get_cur_drv(fdctrl);
2195 fd_start(cur_drv);
2196 if (fdctrl->fifo[2] <= cur_drv->track)
2197 cur_drv->dir = 1;
2198 else
2199 cur_drv->dir = 0;
2200 fdctrl_reset_fifo(fdctrl);
2201#ifndef VBOX
2202 if (fdctrl->fifo[2] > cur_drv->max_track) {
2203#else
2204 if ( fdctrl->fifo[2] > cur_drv->max_track
2205 && cur_drv->fMediaPresent) {
2206#endif
2207 fdctrl_raise_irq(fdctrl, 0x60);
2208 } else {
2209 cur_drv->track = fdctrl->fifo[2];
2210 /* Raise Interrupt */
2211 fdctrl_raise_irq(fdctrl, 0x20);
2212 }
2213 break;
2214 case 0x12:
2215 /* PERPENDICULAR_MODE */
2216 FLOPPY_DPRINTF("treat PERPENDICULAR_MODE command\n");
2217 if (fdctrl->fifo[1] & 0x80)
2218 cur_drv->perpendicular = fdctrl->fifo[1] & 0x7;
2219 /* No result back */
2220 fdctrl_reset_fifo(fdctrl);
2221 break;
2222 case 0x13:
2223 /* CONFIGURE */
2224 FLOPPY_DPRINTF("treat CONFIGURE command\n");
2225 fdctrl->config = fdctrl->fifo[2];
2226 fdctrl->precomp_trk = fdctrl->fifo[3];
2227 /* No result back */
2228 fdctrl_reset_fifo(fdctrl);
2229 break;
2230 case 0x17:
2231 /* POWERDOWN_MODE */
2232 FLOPPY_DPRINTF("treat POWERDOWN_MODE command\n");
2233 fdctrl->pwrd = fdctrl->fifo[1];
2234 fdctrl->fifo[0] = fdctrl->fifo[1];
2235 fdctrl_set_fifo(fdctrl, 1, 1);
2236 break;
2237 case 0x33:
2238 /* OPTION */
2239 FLOPPY_DPRINTF("treat OPTION command\n");
2240 /* No result back */
2241 fdctrl_reset_fifo(fdctrl);
2242 break;
2243 case 0x42:
2244 /* READ_TRACK */
2245/* // FLOPPY_DPRINTF("treat READ_TRACK command\n"); */
2246 FLOPPY_ERROR("treat READ_TRACK command\n");
2247 fdctrl_start_transfer(fdctrl, FD_DIR_READ);
2248 break;
2249 case 0x4A:
2250 /* READ_ID */
2251 FLOPPY_DPRINTF("treat READ_ID command\n");
2252 /* XXX: should set main status register to busy */
2253 cur_drv->head = (fdctrl->fifo[1] >> 2) & 1;
2254#ifdef VBOX
2255 TMTimerSetMillies(fdctrl->result_timer, 1000 / 50);
2256#else
2257 qemu_mod_timer(fdctrl->result_timer,
2258 qemu_get_clock(vm_clock) + (ticks_per_sec / 50));
2259#endif
2260 break;
2261 case 0x4C:
2262 /* RESTORE */
2263 FLOPPY_DPRINTF("treat RESTORE command\n");
2264 /* Drives position */
2265 drv0(fdctrl)->track = fdctrl->fifo[3];
2266 drv1(fdctrl)->track = fdctrl->fifo[4];
2267 /* timers */
2268 fdctrl->timer0 = fdctrl->fifo[7];
2269 fdctrl->timer1 = fdctrl->fifo[8];
2270 cur_drv->last_sect = fdctrl->fifo[9];
2271 fdctrl->lock = fdctrl->fifo[10] >> 7;
2272 cur_drv->perpendicular = (fdctrl->fifo[10] >> 2) & 0xF;
2273 fdctrl->config = fdctrl->fifo[11];
2274 fdctrl->precomp_trk = fdctrl->fifo[12];
2275 fdctrl->pwrd = fdctrl->fifo[13];
2276 fdctrl_reset_fifo(fdctrl);
2277 break;
2278 case 0x4D:
2279 /* FORMAT_TRACK */
2280 FLOPPY_DPRINTF("treat FORMAT_TRACK command\n");
2281 fdctrl->cur_drv = fdctrl->fifo[1] & 1;
2282 cur_drv = get_cur_drv(fdctrl);
2283 fdctrl->data_state |= FD_STATE_FORMAT;
2284 if (fdctrl->fifo[0] & 0x80)
2285 fdctrl->data_state |= FD_STATE_MULTI;
2286 else
2287 fdctrl->data_state &= ~FD_STATE_MULTI;
2288 fdctrl->data_state &= ~FD_STATE_SEEK;
2289 cur_drv->bps =
2290 fdctrl->fifo[2] > 7 ? 16384 : 128 << fdctrl->fifo[2];
2291#if 0
2292 cur_drv->last_sect =
2293 cur_drv->flags & FDISK_DBL_SIDES ? fdctrl->fifo[3] :
2294 fdctrl->fifo[3] / 2;
2295#else
2296 cur_drv->last_sect = fdctrl->fifo[3];
2297#endif
2298 /* Bochs BIOS is buggy and don't send format informations
2299 * for each sector. So, pretend all's done right now...
2300 */
2301 fdctrl->data_state &= ~FD_STATE_FORMAT;
2302 fdctrl_stop_transfer(fdctrl, 0x00, 0x00, 0x00);
2303 break;
2304 case 0x8E:
2305 /* DRIVE_SPECIFICATION_COMMAND */
2306 FLOPPY_DPRINTF("treat DRIVE_SPECIFICATION_COMMAND command\n");
2307 if (fdctrl->fifo[fdctrl->data_pos - 1] & 0x80) {
2308 /* Command parameters done */
2309 if (fdctrl->fifo[fdctrl->data_pos - 1] & 0x40) {
2310 fdctrl->fifo[0] = fdctrl->fifo[1];
2311 fdctrl->fifo[2] = 0;
2312 fdctrl->fifo[3] = 0;
2313 fdctrl_set_fifo(fdctrl, 4, 1);
2314 } else {
2315 fdctrl_reset_fifo(fdctrl);
2316 }
2317 } else if (fdctrl->data_len > 7) {
2318 /* ERROR */
2319 fdctrl->fifo[0] = 0x80 |
2320 (cur_drv->head << 2) | fdctrl->cur_drv;
2321 fdctrl_set_fifo(fdctrl, 1, 1);
2322 }
2323 break;
2324 case 0x8F:
2325 /* RELATIVE_SEEK_OUT */
2326 FLOPPY_DPRINTF("treat RELATIVE_SEEK_OUT command\n");
2327 fdctrl->cur_drv = fdctrl->fifo[1] & 1;
2328 cur_drv = get_cur_drv(fdctrl);
2329 fd_start(cur_drv);
2330 cur_drv->dir = 0;
2331 if (fdctrl->fifo[2] + cur_drv->track >= cur_drv->max_track) {
2332 cur_drv->track = cur_drv->max_track - 1;
2333 } else {
2334 cur_drv->track += fdctrl->fifo[2];
2335 }
2336 fdctrl_reset_fifo(fdctrl);
2337 fdctrl_raise_irq(fdctrl, 0x20);
2338 break;
2339 case 0xCD:
2340 /* FORMAT_AND_WRITE */
2341/* // FLOPPY_DPRINTF("treat FORMAT_AND_WRITE command\n"); */
2342 FLOPPY_ERROR("treat FORMAT_AND_WRITE command\n");
2343 fdctrl_unimplemented(fdctrl);
2344 break;
2345 case 0xCF:
2346 /* RELATIVE_SEEK_IN */
2347 FLOPPY_DPRINTF("treat RELATIVE_SEEK_IN command\n");
2348 fdctrl->cur_drv = fdctrl->fifo[1] & 1;
2349 cur_drv = get_cur_drv(fdctrl);
2350 fd_start(cur_drv);
2351 cur_drv->dir = 1;
2352 if (fdctrl->fifo[2] > cur_drv->track) {
2353 cur_drv->track = 0;
2354 } else {
2355 cur_drv->track -= fdctrl->fifo[2];
2356 }
2357 fdctrl_reset_fifo(fdctrl);
2358 /* Raise Interrupt */
2359 fdctrl_raise_irq(fdctrl, 0x20);
2360 break;
2361 }
2362 }
2363}
2364
2365static void fdctrl_result_timer(void *opaque)
2366{
2367 fdctrl_t *fdctrl = opaque;
2368 fdctrl_stop_transfer(fdctrl, 0x00, 0x00, 0x00);
2369}
2370
2371
2372#ifdef VBOX
2373static DECLCALLBACK(void) fdc_timer (PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
2374{
2375 fdctrl_t *fdctrl = (fdctrl_t *)pvUser;
2376 fdctrl_result_timer (fdctrl);
2377}
2378
2379static DECLCALLBACK(int) fdc_io_write (PPDMDEVINS pDevIns,
2380 void *pvUser,
2381 RTIOPORT Port,
2382 uint32_t u32,
2383 unsigned cb)
2384{
2385 if (cb == 1) {
2386 fdctrl_write (pvUser, Port, u32);
2387 }
2388 else {
2389 AssertMsgFailed(("Port=%#x cb=%d u32=%#x\n", Port, cb, u32));
2390 }
2391 return VINF_SUCCESS;
2392}
2393
2394static DECLCALLBACK(int) fdc_io_read (PPDMDEVINS pDevIns,
2395 void *pvUser,
2396 RTIOPORT Port,
2397 uint32_t *pu32,
2398 unsigned cb)
2399{
2400 if (cb == 1) {
2401 *pu32 = fdctrl_read (pvUser, Port);
2402 return VINF_SUCCESS;
2403 }
2404 else {
2405 return VERR_IOM_IOPORT_UNUSED;
2406 }
2407}
2408
2409static DECLCALLBACK(int) fdcSaveExec (PPDMDEVINS pDevIns, PSSMHANDLE pSSMHandle)
2410{
2411 fdctrl_t *s = PDMINS_2_DATA (pDevIns, fdctrl_t *);
2412 QEMUFile *f = pSSMHandle;
2413 unsigned int i;
2414
2415 qemu_put_8s (f, &s->version);
2416 qemu_put_8s (f, &s->irq_lvl);
2417 qemu_put_8s (f, &s->dma_chann);
2418 qemu_put_be32s (f, &s->io_base);
2419 qemu_put_8s (f, &s->state);
2420 qemu_put_8s (f, &s->dma_en);
2421 qemu_put_8s (f, &s->cur_drv);
2422 qemu_put_8s (f, &s->bootsel);
2423 qemu_put_buffer (f, s->fifo, FD_SECTOR_LEN);
2424 qemu_put_be32s (f, &s->data_pos);
2425 qemu_put_be32s (f, &s->data_len);
2426 qemu_put_8s (f, &s->data_state);
2427 qemu_put_8s (f, &s->data_dir);
2428 qemu_put_8s (f, &s->int_status);
2429 qemu_put_8s (f, &s->eot);
2430 qemu_put_8s (f, &s->timer0);
2431 qemu_put_8s (f, &s->timer1);
2432 qemu_put_8s (f, &s->precomp_trk);
2433 qemu_put_8s (f, &s->config);
2434 qemu_put_8s (f, &s->lock);
2435 qemu_put_8s (f, &s->pwrd);
2436
2437 for (i = 0; i < sizeof (s->drives) / sizeof (s->drives[0]); ++i) {
2438 fdrive_t *d = &s->drives[i];
2439
2440 SSMR3PutMem (pSSMHandle, &d->Led, sizeof (d->Led));
2441 qemu_put_be32s (f, &d->drive);
2442 qemu_put_be32s (f, &d->drflags);
2443 qemu_put_8s (f, &d->perpendicular);
2444 qemu_put_8s (f, &d->head);
2445 qemu_put_8s (f, &d->track);
2446 qemu_put_8s (f, &d->sect);
2447 qemu_put_8s (f, &d->dir);
2448 qemu_put_8s (f, &d->rw);
2449 qemu_put_be32s (f, &d->flags);
2450 qemu_put_8s (f, &d->last_sect);
2451 qemu_put_8s (f, &d->max_track);
2452 qemu_put_be16s (f, &d->bps);
2453 qemu_put_8s (f, &d->ro);
2454 }
2455 return TMR3TimerSave (s->result_timer, pSSMHandle);
2456}
2457
2458static DECLCALLBACK(int) fdcLoadExec (PPDMDEVINS pDevIns,
2459 PSSMHANDLE pSSMHandle,
2460 uint32_t uVersion,
2461 uint32_t uPass)
2462{
2463 fdctrl_t *s = PDMINS_2_DATA (pDevIns, fdctrl_t *);
2464 QEMUFile *f = pSSMHandle;
2465 unsigned int i;
2466
2467 AssertMsgReturn(uVersion == 1, ("%d\n", uVersion), VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION);
2468 Assert(uPass == SSM_PASS_FINAL); NOREF(uPass);
2469
2470 qemu_get_8s (f, &s->version);
2471 qemu_get_8s (f, &s->irq_lvl);
2472 qemu_get_8s (f, &s->dma_chann);
2473 qemu_get_be32s (f, &s->io_base);
2474 qemu_get_8s (f, &s->state);
2475 qemu_get_8s (f, &s->dma_en);
2476 qemu_get_8s (f, &s->cur_drv);
2477 qemu_get_8s (f, &s->bootsel);
2478 qemu_get_buffer (f, s->fifo, FD_SECTOR_LEN);
2479 qemu_get_be32s (f, &s->data_pos);
2480 qemu_get_be32s (f, &s->data_len);
2481 qemu_get_8s (f, &s->data_state);
2482 qemu_get_8s (f, &s->data_dir);
2483 qemu_get_8s (f, &s->int_status);
2484 qemu_get_8s (f, &s->eot);
2485 qemu_get_8s (f, &s->timer0);
2486 qemu_get_8s (f, &s->timer1);
2487 qemu_get_8s (f, &s->precomp_trk);
2488 qemu_get_8s (f, &s->config);
2489 qemu_get_8s (f, &s->lock);
2490 qemu_get_8s (f, &s->pwrd);
2491
2492 for (i = 0; i < sizeof (s->drives) / sizeof (s->drives[0]); ++i) {
2493 fdrive_t *d = &s->drives[i];
2494
2495 SSMR3GetMem (pSSMHandle, &d->Led, sizeof (d->Led));
2496 qemu_get_be32s (f, &d->drive);
2497 qemu_get_be32s (f, &d->drflags);
2498 qemu_get_8s (f, &d->perpendicular);
2499 qemu_get_8s (f, &d->head);
2500 qemu_get_8s (f, &d->track);
2501 qemu_get_8s (f, &d->sect);
2502 qemu_get_8s (f, &d->dir);
2503 qemu_get_8s (f, &d->rw);
2504 qemu_get_be32s (f, &d->flags);
2505 qemu_get_8s (f, &d->last_sect);
2506 qemu_get_8s (f, &d->max_track);
2507 qemu_get_be16s (f, &d->bps);
2508 qemu_get_8s (f, &d->ro);
2509 }
2510 return TMR3TimerLoad (s->result_timer, pSSMHandle);
2511}
2512
2513/**
2514 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
2515 */
2516static DECLCALLBACK(void *) fdQueryInterface (PPDMIBASE pInterface, const char *pszIID)
2517{
2518 fdrive_t *pDrive = PDMIBASE_2_FDRIVE(pInterface);
2519
2520 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrive->IBase);
2521 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBLOCKPORT, &pDrive->IPort);
2522 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMOUNTNOTIFY, &pDrive->IMountNotify);
2523 return NULL;
2524}
2525
2526/**
2527 * Gets the pointer to the status LED of a unit.
2528 *
2529 * @returns VBox status code.
2530 * @param pInterface Pointer to the interface structure containing the called function pointer.
2531 * @param iLUN The unit which status LED we desire.
2532 * @param ppLed Where to store the LED pointer.
2533 */
2534static DECLCALLBACK(int) fdcStatusQueryStatusLed (PPDMILEDPORTS pInterface,
2535 unsigned iLUN,
2536 PPDMLED *ppLed)
2537{
2538 fdctrl_t *fdctrl = (fdctrl_t *)
2539 ((uintptr_t )pInterface - RT_OFFSETOF (fdctrl_t, ILeds));
2540 if (iLUN < RT_ELEMENTS(fdctrl->drives)) {
2541 *ppLed = &fdctrl->drives[iLUN].Led;
2542 Assert ((*ppLed)->u32Magic == PDMLED_MAGIC);
2543 return VINF_SUCCESS;
2544 }
2545 return VERR_PDM_LUN_NOT_FOUND;
2546}
2547
2548
2549/**
2550 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
2551 */
2552static DECLCALLBACK(void *) fdcStatusQueryInterface(PPDMIBASE pInterface, const char *pszIID)
2553{
2554 fdctrl_t *pThis = RT_FROM_MEMBER (pInterface, fdctrl_t, IBaseStatus);
2555
2556 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThis->IBaseStatus);
2557 PDMIBASE_RETURN_INTERFACE(pszIID, PDMILEDPORTS, &pThis->ILeds);
2558 return NULL;
2559}
2560
2561
2562/**
2563 * Configure a drive.
2564 *
2565 * @returns VBox status code.
2566 * @param drv The drive in question.
2567 * @param pDevIns The driver instance.
2568 */
2569static int fdConfig (fdrive_t *drv, PPDMDEVINS pDevIns)
2570{
2571 static const char *descs[] = {"Floppy Drive A:", "Floppy Drive B"};
2572 int rc;
2573
2574 /*
2575 * Reset the LED just to be on the safe side.
2576 */
2577 Assert (RT_ELEMENTS(descs) > drv->iLUN);
2578 Assert (drv->Led.u32Magic == PDMLED_MAGIC);
2579 drv->Led.Actual.u32 = 0;
2580 drv->Led.Asserted.u32 = 0;
2581
2582 /*
2583 * Try attach the block device and get the interfaces.
2584 */
2585 rc = PDMDevHlpDriverAttach (pDevIns, drv->iLUN, &drv->IBase, &drv->pDrvBase, descs[drv->iLUN]);
2586 if (RT_SUCCESS (rc)) {
2587 drv->pDrvBlock = PDMIBASE_QUERY_INTERFACE(drv->pDrvBase, PDMIBLOCK);
2588 if (drv->pDrvBlock) {
2589 drv->pDrvBlockBios = PDMIBASE_QUERY_INTERFACE(drv->pDrvBase, PDMIBLOCKBIOS);
2590 if (drv->pDrvBlockBios) {
2591 drv->pDrvMount = PDMIBASE_QUERY_INTERFACE(drv->pDrvBase, PDMIMOUNT);
2592 if (drv->pDrvMount) {
2593 /*
2594 * Init the state.
2595 */
2596 drv->drive = FDRIVE_DRV_NONE;
2597 drv->drflags = 0;
2598 drv->perpendicular = 0;
2599 /* Disk */
2600 drv->last_sect = 0;
2601 drv->max_track = 0;
2602 drv->fMediaPresent = false;
2603 } else {
2604 AssertMsgFailed (("Configuration error: LUN#%d without mountable interface!\n", drv->iLUN));
2605 rc = VERR_PDM_MISSING_INTERFACE;
2606 }
2607
2608 } else {
2609 AssertMsgFailed (("Configuration error: LUN#%d hasn't a block BIOS interface!\n", drv->iLUN));
2610 rc = VERR_PDM_MISSING_INTERFACE;
2611 }
2612
2613 } else {
2614 AssertMsgFailed (("Configuration error: LUN#%d hasn't a block interface!\n", drv->iLUN));
2615 rc = VERR_PDM_MISSING_INTERFACE;
2616 }
2617 } else {
2618 AssertMsg (rc == VERR_PDM_NO_ATTACHED_DRIVER,
2619 ("Failed to attach LUN#%d. rc=%Rrc\n", drv->iLUN, rc));
2620 switch (rc) {
2621 case VERR_ACCESS_DENIED:
2622 /* Error already catched by DrvHostBase */
2623 break;
2624 case VERR_PDM_NO_ATTACHED_DRIVER:
2625 /* Legal on architectures without a floppy controller */
2626 break;
2627 default:
2628 rc = PDMDevHlpVMSetError (pDevIns, rc, RT_SRC_POS,
2629 N_ ("The floppy controller cannot attach to the floppy drive"));
2630 break;
2631 }
2632 }
2633
2634 if (RT_FAILURE (rc)) {
2635 drv->pDrvBase = NULL;
2636 drv->pDrvBlock = NULL;
2637 drv->pDrvBlockBios = NULL;
2638 drv->pDrvMount = NULL;
2639 }
2640 LogFlow (("fdConfig: returns %Rrc\n", rc));
2641 return rc;
2642}
2643
2644
2645/**
2646 * Attach command.
2647 *
2648 * This is called when we change block driver for a floppy drive.
2649 *
2650 * @returns VBox status code.
2651 * @param pDevIns The device instance.
2652 * @param iLUN The logical unit which is being detached.
2653 */
2654static DECLCALLBACK(int) fdcAttach (PPDMDEVINS pDevIns,
2655 unsigned iLUN, uint32_t fFlags)
2656{
2657 fdctrl_t *fdctrl = PDMINS_2_DATA (pDevIns, fdctrl_t *);
2658 fdrive_t *drv;
2659 int rc;
2660 LogFlow (("ideDetach: iLUN=%u\n", iLUN));
2661
2662 AssertMsgReturn(fFlags & PDM_TACH_FLAGS_NOT_HOT_PLUG,
2663 ("The FDC device does not support hotplugging\n"),
2664 VERR_INVALID_PARAMETER);
2665
2666 /*
2667 * Validate.
2668 */
2669 if (iLUN > 2) {
2670 AssertMsgFailed (("Configuration error: cannot attach or detach any but the first two LUNs - iLUN=%u\n",
2671 iLUN));
2672 return VERR_PDM_DEVINS_NO_ATTACH;
2673 }
2674
2675 /*
2676 * Locate the drive and stuff.
2677 */
2678 drv = &fdctrl->drives[iLUN];
2679
2680 /* the usual paranoia */
2681 AssertRelease (!drv->pDrvBase);
2682 AssertRelease (!drv->pDrvBlock);
2683 AssertRelease (!drv->pDrvBlockBios);
2684 AssertRelease (!drv->pDrvMount);
2685
2686 rc = fdConfig (drv, pDevIns);
2687 AssertMsg (rc != VERR_PDM_NO_ATTACHED_DRIVER,
2688 ("Configuration error: failed to configure drive %d, rc=%Rrc\n", rc));
2689 if (RT_SUCCESS(rc)) {
2690 fd_revalidate (drv);
2691 }
2692
2693 LogFlow (("floppyAttach: returns %Rrc\n", rc));
2694 return rc;
2695}
2696
2697
2698/**
2699 * Detach notification.
2700 *
2701 * The floppy drive has been temporarily 'unplugged'.
2702 *
2703 * @param pDevIns The device instance.
2704 * @param iLUN The logical unit which is being detached.
2705 */
2706static DECLCALLBACK(void) fdcDetach (PPDMDEVINS pDevIns,
2707 unsigned iLUN, uint32_t fFlags)
2708{
2709 fdctrl_t *fdctrl = PDMINS_2_DATA (pDevIns, fdctrl_t *);
2710 LogFlow (("ideDetach: iLUN=%u\n", iLUN));
2711
2712 switch (iLUN) {
2713 case 0:
2714 case 1: {
2715 fdrive_t *drv = &fdctrl->drives[iLUN];
2716 drv->pDrvBase = NULL;
2717 drv->pDrvBlock = NULL;
2718 drv->pDrvBlockBios = NULL;
2719 drv->pDrvMount = NULL;
2720 break;
2721 }
2722
2723 default:
2724 AssertMsgFailed (("Cannot detach LUN#%d!\n", iLUN));
2725 break;
2726 }
2727}
2728
2729
2730/**
2731 * Handle reset.
2732 *
2733 * I haven't check the specs on what's supposed to happen on reset, but we
2734 * should get any 'FATAL: floppy recal:f07 ctrl not ready' when resetting
2735 * at wrong time like we do if this was all void.
2736 *
2737 * @param pDevIns The device instance.
2738 */
2739static DECLCALLBACK(void) fdcReset (PPDMDEVINS pDevIns)
2740{
2741 fdctrl_t *fdctrl = PDMINS_2_DATA (pDevIns, fdctrl_t *);
2742 unsigned i;
2743 LogFlow (("fdcReset:\n"));
2744
2745 fdctrl_reset(fdctrl, 0);
2746 fdctrl->state = FD_CTRL_ACTIVE;
2747
2748 for (i = 0; i < RT_ELEMENTS(fdctrl->drives); i++) {
2749 fd_revalidate(&fdctrl->drives[i]);
2750 }
2751}
2752
2753
2754/**
2755 * @interface_method_impl{PDMDEVREG,pfnConstruct}
2756 */
2757static DECLCALLBACK(int) fdcConstruct (PPDMDEVINS pDevIns,
2758 int iInstance,
2759 PCFGMNODE pCfg)
2760{
2761 int rc;
2762 fdctrl_t *fdctrl = PDMINS_2_DATA(pDevIns, fdctrl_t*);
2763 unsigned i;
2764 bool mem_mapped;
2765 uint16_t io_base;
2766 uint8_t irq_lvl, dma_chann;
2767 PPDMIBASE pBase;
2768
2769 Assert(iInstance == 0);
2770 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
2771
2772 /*
2773 * Validate configuration.
2774 */
2775 if (!CFGMR3AreValuesValid(pCfg, "IRQ\0DMA\0MemMapped\0IOBase\0"))
2776 return VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES;
2777
2778 /*
2779 * Read the configuration.
2780 */
2781 rc = CFGMR3QueryU8 (pCfg, "IRQ", &irq_lvl);
2782 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
2783 irq_lvl = 6;
2784 else if (RT_FAILURE (rc)) {
2785 AssertMsgFailed (("Configuration error: Failed to read U8 IRQ, rc=%Rrc\n", rc));
2786 return rc;
2787 }
2788
2789 rc = CFGMR3QueryU8 (pCfg, "DMA", &dma_chann);
2790 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
2791 dma_chann = 2;
2792 else if (RT_FAILURE (rc)) {
2793 AssertMsgFailed (("Configuration error: Failed to read U8 DMA, rc=%Rrc\n", rc));
2794 return rc;
2795 }
2796
2797 rc = CFGMR3QueryU16 (pCfg, "IOBase", &io_base);
2798 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
2799 io_base = 0x3f0;
2800 else if (RT_FAILURE (rc)) {
2801 AssertMsgFailed (("Configuration error: Failed to read U16 IOBase, rc=%Rrc\n", rc));
2802 return rc;
2803 }
2804
2805 rc = CFGMR3QueryBool (pCfg, "MemMapped", &mem_mapped);
2806 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
2807 mem_mapped = false;
2808 else if (RT_FAILURE (rc)) {
2809 AssertMsgFailed (("Configuration error: Failed to read bool value MemMapped rc=%Rrc\n", rc));
2810 return rc;
2811 }
2812
2813 /*
2814 * Initialize data.
2815 */
2816 LogFlow(("fdcConstruct: irq_lvl=%d dma_chann=%d io_base=%#x\n", irq_lvl, dma_chann, io_base));
2817 fdctrl->pDevIns = pDevIns;
2818 fdctrl->version = 0x90; /* Intel 82078 controller */
2819 fdctrl->irq_lvl = irq_lvl;
2820 fdctrl->dma_chann = dma_chann;
2821 fdctrl->io_base = io_base;
2822 fdctrl->config = 0x60; /* Implicit seek, polling & FIFO enabled */
2823
2824 fdctrl->IBaseStatus.pfnQueryInterface = fdcStatusQueryInterface;
2825 fdctrl->ILeds.pfnQueryStatusLed = fdcStatusQueryStatusLed;
2826
2827 for (i = 0; i < RT_ELEMENTS(fdctrl->drives); ++i) {
2828 fdrive_t *drv = &fdctrl->drives[i];
2829
2830 drv->drive = FDRIVE_DRV_NONE;
2831 drv->iLUN = i;
2832
2833 drv->IBase.pfnQueryInterface = fdQueryInterface;
2834 drv->IMountNotify.pfnMountNotify = fdMountNotify;
2835 drv->IMountNotify.pfnUnmountNotify = fdUnmountNotify;
2836 drv->Led.u32Magic = PDMLED_MAGIC;
2837 }
2838
2839 /*
2840 * Create the FDC timer.
2841 */
2842 rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL, fdc_timer, fdctrl,
2843 TMTIMER_FLAGS_DEFAULT_CRIT_SECT, "FDC Timer", &fdctrl->result_timer);
2844 if (RT_FAILURE (rc))
2845 return rc;
2846
2847 /*
2848 * Register DMA channel.
2849 */
2850 if (fdctrl->dma_chann != 0xff) {
2851 fdctrl->dma_en = 1;
2852 rc = PDMDevHlpDMARegister (pDevIns, dma_chann, &fdctrl_transfer_handler, fdctrl);
2853 if (RT_FAILURE (rc))
2854 return rc;
2855 } else
2856 fdctrl->dma_en = 0;
2857
2858 /*
2859 * IO / MMIO.
2860 */
2861 if (mem_mapped) {
2862 AssertMsgFailed (("Memory mapped floppy not support by now\n"));
2863 return VERR_NOT_SUPPORTED;
2864#if 0
2865 FLOPPY_ERROR("memory mapped floppy not supported by now !\n");
2866 io_mem = cpu_register_io_memory(0, fdctrl_mem_read, fdctrl_mem_write);
2867 cpu_register_physical_memory(base, 0x08, io_mem);
2868#endif
2869 } else {
2870 rc = PDMDevHlpIOPortRegister (pDevIns, io_base + 0x1, 5, fdctrl,
2871 fdc_io_write, fdc_io_read, NULL, NULL, "FDC#1");
2872 if (RT_FAILURE (rc))
2873 return rc;
2874
2875 rc = PDMDevHlpIOPortRegister (pDevIns, io_base + 0x7, 1, fdctrl,
2876 fdc_io_write, fdc_io_read, NULL, NULL, "FDC#2");
2877 if (RT_FAILURE (rc))
2878 return rc;
2879 }
2880
2881 /*
2882 * Register the saved state data unit.
2883 */
2884 rc = PDMDevHlpSSMRegister (pDevIns, 1 /*uVersion*/, sizeof(*fdctrl), fdcSaveExec, fdcLoadExec);
2885 if (RT_FAILURE(rc))
2886 return rc;
2887
2888 /*
2889 * Attach the status port (optional).
2890 */
2891 rc = PDMDevHlpDriverAttach (pDevIns, PDM_STATUS_LUN, &fdctrl->IBaseStatus, &pBase, "Status Port");
2892 if (RT_SUCCESS (rc)) {
2893 fdctrl->pLedsConnector = PDMIBASE_QUERY_INTERFACE(pBase, PDMILEDCONNECTORS);
2894 } else if (rc != VERR_PDM_NO_ATTACHED_DRIVER) {
2895 AssertMsgFailed (("Failed to attach to status driver. rc=%Rrc\n",
2896 rc));
2897 return rc;
2898 }
2899
2900 /*
2901 * Initialize drives.
2902 */
2903 for (i = 0; i < RT_ELEMENTS(fdctrl->drives); i++) {
2904 fdrive_t *drv = &fdctrl->drives[i];
2905 rc = fdConfig (drv, pDevIns);
2906 if ( RT_FAILURE (rc)
2907 && rc != VERR_PDM_NO_ATTACHED_DRIVER) {
2908 AssertMsgFailed (("Configuration error: failed to configure drive %d, rc=%Rrc\n", rc));
2909 return rc;
2910 }
2911 }
2912
2913 fdctrl_reset(fdctrl, 0);
2914 fdctrl->state = FD_CTRL_ACTIVE;
2915
2916 for (i = 0; i < RT_ELEMENTS(fdctrl->drives); i++)
2917 fd_revalidate(&fdctrl->drives[i]);
2918
2919 return VINF_SUCCESS;
2920}
2921
2922/**
2923 * The device registration structure.
2924 */
2925const PDMDEVREG g_DeviceFloppyController =
2926{
2927 /* u32Version */
2928 PDM_DEVREG_VERSION,
2929 /* szName */
2930 "i82078",
2931 /* szRCMod */
2932 "",
2933 /* szR0Mod */
2934 "",
2935 /* pszDescription */
2936 "Floppy drive controller (Intel 82078)",
2937 /* fFlags */
2938 PDM_DEVREG_FLAGS_DEFAULT_BITS,
2939 /* fClass */
2940 PDM_DEVREG_CLASS_STORAGE,
2941 /* cMaxInstances */
2942 1,
2943 /* cbInstance */
2944 sizeof(fdctrl_t),
2945 /* pfnConstruct */
2946 fdcConstruct,
2947 /* pfnDestruct */
2948 NULL,
2949 /* pfnRelocate */
2950 NULL,
2951 /* pfnIOCtl */
2952 NULL,
2953 /* pfnPowerOn */
2954 NULL,
2955 /* pfnReset */
2956 fdcReset,
2957 /* pfnSuspend */
2958 NULL,
2959 /* pfnResume */
2960 NULL,
2961 /* pfnAttach */
2962 fdcAttach,
2963 /* pfnDetach */
2964 fdcDetach,
2965 /* pfnQueryInterface. */
2966 NULL,
2967 /* pfnInitComplete */
2968 NULL,
2969 /* pfnPowerOff */
2970 NULL,
2971 /* pfnSoftReset */
2972 NULL,
2973 /* u32VersionEnd */
2974 PDM_DEVREG_VERSION
2975};
2976
2977#endif /* VBOX */
2978
2979/*
2980 * Local Variables:
2981 * mode: c
2982 * c-file-style: "k&r"
2983 * indent-tabs-mode: nil
2984 * End:
2985 */
2986
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette