1/*
2 * Copyright 2003-2008, Haiku.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *		Fran��ois Revol <revol@free.fr>
7 */
8
9
10#include <Drivers.h>
11#include "floppy.h"
12
13
14/* VERY verbose... */
15//#define DEBUG_LOWLEVEL
16
17#if defined(DEBUG) && DEBUG > 0
18#	define PRINT_SR0(x) print_sr0(x)
19#else
20#	define PRINT_SR0(x) ;
21#endif
22
23
24#if defined(DEBUG) && DEBUG > 0
25static void
26print_sr0(uint8 sr0)
27{
28	const char *result = "ok";
29	switch (sr0 & FDC_SR0_IC) {
30		case 0x80:
31			result = "invalid";
32			break;
33		case 0x40:
34			result = "abterm";
35			break;
36		case 0xc0:
37			result = "drvchngd";
38			break;
39	}
40
41	TRACE("sr0: ds %d, hs %d, ec %d, se %d, %s\n", (sr0 & FDC_SR0_DS), !!(sr0 & FDC_SR0_HS), !!(sr0 & FDC_SR0_EC), !!(sr0 & FDC_SR0_SE), result);
42}
43#endif
44
45
46void
47write_reg(floppy_t *flp, floppy_reg_selector selector, uint8 data)
48{
49#ifdef DEBUG_LOWLEVEL
50	TRACE("write to 0x%lx, data 0x%x\n", flp->iobase + selector, data);
51#endif
52	flp->isa->write_io_8(flp->iobase + selector, data);
53}
54
55uint8 read_reg(floppy_t *flp, floppy_reg_selector selector)
56{
57	uint8 data;
58	data = flp->isa->read_io_8(flp->iobase + selector);
59#ifdef DEBUG_LOWLEVEL
60	TRACE("read from 0x%lx = 0x%x\n", flp->iobase + selector, data);
61#endif
62	return data;
63}
64
65
66void
67reset_controller(floppy_t *master)
68{
69	uint8 command[4]; /* for SPECIFY & CONFIGURE */
70	uint8 result[1]; /* for SPECIFY */
71
72	TRACE("reset_controller()\n");
73	LOCK(master->ben);
74
75	//master->pending_cmd = CMD_RESET;
76	master->pending_cmd = 0;
77	write_reg(master, DIGITAL_OUT, read_reg(master, DIGITAL_OUT) & ~0x04); /* reset */
78	//wait_result(master);
79	spin(20);
80	write_reg(master, DATA_RATE_SELECT, FDC_500KBPS); // 500 kbps
81	master->data_rate = FDC_500KBPS;
82	write_reg(master, DIGITAL_OUT, /*(master->dma < 0)?0x04:*//*0x0c*/0x04); /* motors[abcd] off, DMA on, deassert reset, drive select(0) */
83
84	wait_result(master); /* wait for reset interrupt */
85
86	command[0] = CMD_CONFIG;	// configure command
87	command[1] = 0;		// N/A
88	command[2] = 0x57;//0x70;	// Implied Seek, FIFO, Poll Disable, THRESH = default
89	command[3] = 0;		// PRETRK
90
91	send_command(master, command, sizeof(command));
92
93	command[0] = CMD_SPECIFY;
94	command[1] = (((16 - (3)) << 4) | ((240 / 16))); /* step rate 3ms, head unload time 240ms */
95	command[2] = 0x02 | 0x01; /* head load time 2ms, NO DMA */
96
97	send_command(master, command, 3); /* send SPECIFY */
98	read_result(master, result, 1);
99
100	UNLOCK(master->ben);
101}
102
103
104/* returns a bitmap of the present drives */
105int
106count_floppies(floppy_t *master)
107{
108	int i, err;
109	int flops = 0;
110
111	master->master = master;
112
113	//floppy_dump_reg(master);
114
115	/* reset controller */
116	reset_controller(master);
117
118
119	for (i = 0; i < MAX_DRIVES_PER_CTRL; i++) {
120		master->drive_num = i; /* fake */
121		TRACE("DETECTING DRIVE %d...\n", i);
122		turn_on_motor(master);
123		drive_select(master);
124		err = recalibrate_drive(master);
125		if (err == 0)
126			flops |= 1 << i;
127		TRACE("DRIVE %d %s\n", i, err?"NOT here":"is here");
128		drive_deselect(master);
129		//snooze(50000);
130		//turn_off_motor(master);
131	}
132
133//	floppy_dump_reg(master);
134
135	master->drive_num = 0;
136	return flops;
137}
138
139
140/* selects the drive until deselect(), takes the benaphore */
141void
142drive_select(floppy_t *flp)
143{
144	cpu_status st;
145	uint8 reg;
146	TRACE("drive_select(%d)\n", flp->drive_num);
147	LOCK(flp->master->ben);
148
149	/* must be atomic to not change motor states! */
150	st = disable_interrupts();
151	acquire_spinlock(&(flp->master->slock));
152
153	/* make sure the speed for this drive is correct */
154	if (flp->geometry && (flp->master->data_rate != flp->geometry->data_rate)) {
155		write_reg(flp, DATA_RATE_SELECT, flp->geometry->data_rate); // 500 kbps
156		flp->master->data_rate = flp->geometry->data_rate;
157	}
158
159	/* make sure the drive is selected and DMA is on */
160	reg = read_reg(flp, DIGITAL_OUT);
161	if((reg & 0x0b) != ((flp->drive_num & 0x03) | 0x08))
162		write_reg(flp, DIGITAL_OUT, (reg & 0xfc) | (flp->drive_num & 0x03) | /*(flp->master->dma < 0)?0x04:*/0x0c); /* DMA on, reset off */
163
164	release_spinlock(&(flp->master->slock));
165	restore_interrupts(st);
166
167	flp->master->current = flp->drive_num;
168}
169
170
171void
172drive_deselect(floppy_t *flp)
173{
174	UNLOCK(flp->master->ben);
175	TRACE("drive_deselect(%d)\n", flp->drive_num);
176}
177
178
179void
180turn_on_motor(floppy_t *flp)
181{
182	cpu_status st;
183	uint8 reg;
184	bool do_snooze = false;
185
186	TRACE("turn_on_motor(%d)\n", flp->drive_num);
187	flp->motor_timeout = MOTOR_TIMEOUT;
188
189	/* must be atomic to not deselect a drive! */
190	st = disable_interrupts();
191	acquire_spinlock(&(flp->master->slock));
192
193	if(((reg = read_reg(flp, DIGITAL_OUT)) & (0x10 << flp->drive_num)) == 0) {
194		/* it's off now, turn it on and wait */
195		//reg = 0x0c;
196		//reg &= 0xfc; /* mask out drive num */
197		write_reg(flp, DIGITAL_OUT, (0x10 << flp->drive_num) | reg);
198		do_snooze = true;
199	}
200
201	release_spinlock(&(flp->master->slock));
202	restore_interrupts(st);
203
204	if (do_snooze)
205		snooze(MOTOR_SPINUP_DELAY);
206}
207
208
209void
210turn_off_motor(floppy_t *flp)
211{
212	cpu_status st;
213	TRACE("turn_off_motor(%d)\n", flp->drive_num);
214
215	/* must be atomic to not deselect a drive! */
216	st = disable_interrupts();
217	acquire_spinlock(&(flp->master->slock));
218
219	write_reg(flp, DIGITAL_OUT, read_reg(flp, DIGITAL_OUT) & ~(0x10 << flp->drive_num));
220	//write_reg(flp, DIGITAL_OUT, 0x4);
221
222	flp->motor_timeout = 0;
223
224	release_spinlock(&(flp->master->slock));
225	restore_interrupts(st);
226}
227
228
229void
230wait_for_rqm(floppy_t *flp)
231{
232	while ((read_reg(flp, MAIN_STATUS) & 0x80) == 0);
233}
234
235
236int
237send_command(floppy_t *flp, const uint8 *data, int len)
238{
239	int i, status;
240
241	switch (data[0] & CMD_SWITCH_MASK) {
242	case CMD_SENSEI:
243	case CMD_SENSED:
244	case CMD_CONFIG:
245	case CMD_DUMPREG:
246		/* those don't generate an interrupt
247		 * (SENSEI is sent by the intrrupt handler itself!)
248		 */
249		break;
250	default:
251		flp->pending_cmd = data[0];
252	}
253	for(i = 0; i < len; i++) {
254		status = wait_til_ready(flp);
255		if ((status < 0) || (status & 0x40))
256			break;
257		write_reg(flp, DATA, data[i]);
258	}
259#ifdef DEBUG_LOWLEVEL
260	TRACE("sent %d B\n", i);
261#endif
262	return i;
263}
264
265
266int
267wait_result(floppy_t *flp) {
268	status_t err;
269
270	{ int32 c; get_sem_count(flp->completion, &c); TRACE("SEM< %ld\n", c); }
271
272	if ((err = acquire_sem_etc(flp->completion, 1, B_TIMEOUT, FLOPPY_CMD_TIMEOUT)) < B_OK) {
273		if (err == B_TIMED_OUT) {
274			cpu_status st;
275			TRACE("timed out! faking intr !!\n");
276			st = disable_interrupts();
277			flo_intr(flp);
278			restore_interrupts(st);
279			acquire_sem_etc(flp->completion, 1, B_TIMEOUT, FLOPPY_CMD_TIMEOUT);
280		}
281		return -1;
282	}
283	{ int32 c; get_sem_count(flp->completion, &c); TRACE("SEM> %ld\n", c); }
284	return 0;
285}
286
287
288int
289read_result(floppy_t *flp, uint8 *data, int len)
290{
291	int i, status;
292
293	//if (flp->master->need_reset)
294	//	return -1;
295	for(i = 0; i < len; i++) {
296		status = wait_til_ready(flp);
297		if ((status < 0) || ((status & 0x40) == 0))
298			break;
299		data[i] = read_reg(flp, DATA);
300	}
301	//flp->master->need_reset = 1;
302#ifdef DEBUG_LOWLEVEL
303	TRACE("read %d B\n", i);
304#endif
305	return i;
306}
307
308
309int
310wait_til_ready(floppy_t *flp)
311{
312	int i, status;
313
314	for (i = 0; i < 1000; i++)
315		if ((status = read_reg(flp, MAIN_STATUS)) & 0x80)
316			return status;
317	TRACE("timeout waiting for %d !\n", flp->drive_num);
318	return -1;
319}
320
321#if 0
322static int
323has_drive_changed(floppy_t *flp)
324{
325	return !!(read_reg(flp, DIGITAL_IN) & 0x80);
326}
327#endif
328
329status_t
330query_media(floppy_t *flp, bool forceupdate)
331{
332	status_t err = B_OK;
333	uint8 command[4];
334	uint8 result[7];
335	const floppy_geometry *geom = NULL;
336
337	TRACE("query_media(%d, %s)\n", flp->drive_num, forceupdate?"true":"false");
338
339	turn_on_motor(flp);
340	drive_select(flp);
341
342	if (read_reg(flp, DIGITAL_IN) & 0x80) {/* media changed */
343		flp->status = FLOP_MEDIA_CHANGED;
344		TRACE("media changed\n");
345	}
346	if (err || (flp->status < FLOP_MEDIA_UNREADABLE) || forceupdate) {
347		geom = supported_geometries;
348		TRACE("querying format [err %08lx, st %d, fu %s]\n", err, flp->status, forceupdate?"t":"f");
349
350		/* zero the current geometry */
351		flp->geometry = 0;
352		flp->bgeom.bytes_per_sector = 255;
353		flp->bgeom.sectors_per_track = 0;
354		flp->bgeom.cylinder_count = 0;
355		flp->bgeom.head_count = 0;
356		flp->bgeom.read_only = true;
357
358		command[0] = 0x04;      // sense drive command
359		command[1] = 0x00 | (flp->drive_num & 0x03);         //
360
361		send_command(flp, command, 2);
362
363		read_result(flp, result, 1);
364		TRACE("sense_drive(%d): wp %d, trk0 %d, hd %d, drivesel %d\n", flp->drive_num, !!(result[0]&0x40), !!(result[0]&0x10), !!(result[0]&0x04), (result[0]&0x03));
365		flp->bgeom.read_only = !!(result[0]&0x40);
366
367		for (; geom->numsectors; geom++) {
368			TRACE("trying geometry %s\n", geom->name);
369
370			/* apply geometry parameters */
371			if (flp->master->data_rate != geom->data_rate) {
372				write_reg(flp, DATA_RATE_SELECT, geom->data_rate); // 500 kbps
373				flp->master->data_rate = geom->data_rate;
374			}
375
376			/* seek track 0, head 0 */
377			command[0] = 0x0f; // seek command
378			command[1] = (flp->drive_num & 0x03) | 0x00; // drive | head 0
379			command[2] = 0x00; // track 0
380
381			TRACE("SEEK at track 0 head 0\n");
382			send_command(flp, command, 3);
383			if (wait_result(flp) < 0)
384				continue;
385
386			/* if it went there then there si something, even if we can't read it */
387			flp->status = FLOP_MEDIA_UNREADABLE;
388
389			//command[0] = 8; // sense interrupt command
390			//send_command(flp, command, 1);
391
392			//read_result(flp, result, 2); // read the result
393			PRINT_SR0(flp->result[0]);
394			TRACE("track is %d\n", flp->result[1]);
395			if (flp->result[0] & FDC_SR0_IC)
396				continue;
397
398			command[0] = 0x0a | ((geom->flags&FL_MFM)?0x40:0); // read track id
399			command[1] = (flp->drive_num & 0x03) | 0x00; // drive | head 0
400
401			TRACE("READ_TRACK_ID\n");
402			send_command(flp, command, 2);
403			if (wait_result(flp) < 0)
404				continue;
405
406			//read_result(flp, result, 7);
407			PRINT_SR0(flp->result[0]);
408			TRACE("sr1: %02x\n", flp->result[1]);
409			TRACE("sr2: %02x\n", flp->result[2]);
410			TRACE("read id: track %d, head %d, sec %d, bps %d\n", flp->result[3], flp->result[4], flp->result[5], flp->result[6]);
411			if (flp->result[0] & FDC_SR0_IC)
412				continue;
413
414			/* seek track 2, head 1 */
415			command[0] = 0x0f; // seek command
416			command[1] = (flp->drive_num & 0x03) | 0x04; // drive | head 1
417			command[2] = 0x02; // track 2
418
419			TRACE("SEEK at track 2 head 1\n");
420			send_command(flp, command, 3);
421			if (wait_result(flp) < 0)
422				continue;
423
424			//command[0] = 8; // sense interrupt command
425			//send_command(flp, command, 1);
426
427			//read_result(flp, result, 2); // read the result
428			PRINT_SR0(flp->result[0]);
429			TRACE("track is %d\n", flp->result[1]);
430			if (flp->result[0] & FDC_SR0_IC)
431				continue;
432
433			command[0] = 0x0a | ((geom->flags&FL_MFM)?0x40:0); // read track id
434			command[1] = (flp->drive_num & 0x03) | 0x00; // drive | head 0
435
436			TRACE("READ_TRACK_ID\n");
437			send_command(flp, command, 2);
438			if (wait_result(flp) < 0)
439				continue;
440
441			//read_result(flp, result, 7);
442
443			//read_result(flp, result, 7);
444			PRINT_SR0(flp->result[0]);
445			TRACE("sr1: %02x\n", flp->result[1]);
446			TRACE("sr2: %02x\n", flp->result[2]);
447			TRACE("read id: track %d, head %d, sec %d, bps %d\n", flp->result[3], flp->result[4], flp->result[5], flp->result[6]);
448			if (flp->result[0] & FDC_SR0_IC)
449				continue;
450
451			break;
452		}
453		if (geom->numsectors) {
454			dprintf(FLO "drive %d: media type is %s\n", flp->drive_num, geom->name);
455			flp->status = FLOP_MEDIA_FORMAT_FOUND;
456			err = B_OK;
457			flp->geometry = geom;
458			flp->bgeom.bytes_per_sector = geom->g.bytes_per_sector;
459			flp->bgeom.sectors_per_track = geom->g.sectors_per_track;
460			flp->bgeom.cylinder_count = geom->g.cylinder_count;
461			flp->bgeom.head_count = geom->g.head_count;
462			//flp->bgeom.read_only = true;
463		} else {
464			flp->status = FLOP_NO_MEDIA;
465		}
466	}
467
468	switch (flp->status) {
469	case FLOP_NO_MEDIA:
470		err = B_DEV_NO_MEDIA;
471		break;
472	case FLOP_MEDIA_CHANGED:
473		err = B_DEV_MEDIA_CHANGED;
474		break;
475	case FLOP_MEDIA_UNREADABLE:
476		err = B_DEV_UNREADABLE;
477		break;
478	case FLOP_MEDIA_FORMAT_FOUND:
479	case FLOP_WORKING:
480	default:
481		err = B_OK;
482	}
483
484	drive_deselect(flp);
485	return err;
486}
487
488
489int
490recalibrate_drive(floppy_t *flp)
491{
492	int retry;
493
494	TRACE("recalibrate_drive(%d)\n", flp->drive_num);
495
496	turn_on_motor(flp);
497
498	for(retry = 0; retry < 1; retry++) {
499		uint8 command[2] = { 7, 0 }; // recalibrate command
500
501		command[1] = flp->drive_num & 0x03;
502		// send the recalibrate command
503		send_command(flp, command, sizeof(command));
504
505		if (wait_result(flp) < 0)
506			return 3;
507
508		//command[0] = 8; // sense interrupt command
509		//send_command(flp, command, 1);
510
511		// read the result
512		//read_result(flp, result, sizeof(result));
513		if (flp->result[0] & 0xd0) {
514			TRACE("recalibration failed\n");
515			return 2;
516		} if (flp->result[1] != 0) {
517			TRACE("drive is at cylinder %d, didn't make it to 0\n", flp->result[1]);
518			if (retry > 3)
519				return 1;
520		} else {
521			// successful
522			break;
523		}
524	}
525
526	TRACE("recalibration done\n");
527	return 0;
528}
529
530
531int32
532flo_intr(void *arg)
533{
534	int i;
535	int len;
536	int32 err = B_UNHANDLED_INTERRUPT;
537	floppy_t *flp = (floppy_t *)arg;
538	floppy_t *master = flp->master;
539
540	if (master ==NULL)
541		return B_UNHANDLED_INTERRUPT;
542	acquire_spinlock(&master->slock);
543
544	while (flp && (flp->drive_num != master->current) && !flp->pending_cmd)
545		flp = flp->next;
546	if (flp) {
547		uint8 msr;
548		int got = 0;
549		msr = read_reg(flp, MAIN_STATUS);
550		//TRACE("got irq for %d! MSR=%02x\n", flp->drive_num, msr);
551		if ((((flp->pending_cmd & CMD_SWITCH_MASK) == CMD_READD) ||
552			((flp->pending_cmd & CMD_SWITCH_MASK) == CMD_READTRK)) && ((msr & 0x60) == 0x60)) {
553											/* non DMA xfer(data) & need read */
554/*			uint8 command[1];
555			command[0] = CMD_SENSEI;
556			send_command(flp, command, 1);
557			len = read_result(flp, flp->result, 2);
558			PRINT_SR0(flp->result[0]);*/
559
560			//TRACE("READi\n");
561//			while ((msr & 0x60) == 0x60 /*&& (got < 256)*/) {
562			while ((msr & 0xF0) == 0xF0 && (master->buffer_index < CYL_BUFFER_SIZE)) {
563				master->buffer[master->buffer_index++] = read_reg(flp, DATA);
564				master->avail++;
565				msr = read_reg(flp, MAIN_STATUS);
566				got++;
567//				spin(10);
568/*				if (got < 30)
569					TRACE("%02x", msr);
570				else
571					spin(15);*/
572			//	if (got > 255)
573			//	break;
574			}
575//			TRACE("intr: READ %d\n", got);
576		} else if (((flp->pending_cmd & CMD_SWITCH_MASK) == CMD_WRITED) && ((msr & 0x60) == 0x20)) {
577											/* non DMA xfer(data) & need write */
578			TRACE("WRITEi\n");
579
580		} else {
581			len = 8;
582//			if (flp->pending_cmd != CMD_RESET)
583			len = read_result(flp, flp->result, len);
584//			else
585//				len = 0;
586			if (len < 0) {
587				TRACE("buggy interrupt from %d !\n", flp->drive_num);
588			} else if (len < 1) { /* must pool the interrupt reason */
589				uint8 command[1] = { CMD_SENSEI };
590				for (i = 0; i < 4; i++) { /* might have to issue 4 SENSEI */
591					TRACE("intr: %dth SENSEI\n", i+1);
592					if (send_command(flp, command, 1) < 1)
593						break;
594					len = read_result(flp, flp->result, 2);
595					if (len > 0)
596						PRINT_SR0(flp->result[0]);
597					if (len != 2)
598						break;
599					if ((flp->result[0] & (0x80|FDC_SR0_DS)) == master->current)
600						break;
601				}
602			} else
603				TRACE("RES(%d) %02x %02x %02x %02x\n", len, flp->result[0], flp->result[1], flp->result[2], flp->result[3]);
604			/* only do that for !READ && !WRITE */
605			flp->pending_cmd = 0;
606			release_sem_etc(flp->completion, 1, B_DO_NOT_RESCHEDULE);
607		}
608		err = B_HANDLED_INTERRUPT;
609	}
610
611	release_spinlock(&master->slock);
612
613	if (err == B_UNHANDLED_INTERRUPT)
614		TRACE("unhandled irq!\n");
615	return err;
616}
617
618
619void
620floppy_dump_reg(floppy_t *flp) {
621	uint8 command[1] = { 0x0e }; // dumpreg command
622	//uint8 result[10];
623	uint8 *result = flp->result;
624
625	send_command(flp, command, sizeof(command));
626
627	//wait_result(flp);
628	read_result(flp, result, 8);
629
630	dprintf(FLO "dumpreg: tracks - d1=%d d2=%d d3=%d d4=%d\n",
631		result[0], result[1], result[2], result[3]);
632	dprintf(FLO "dumpreg: step_rate_time=%d motor_off_time=%d motor_on_time=%d dma=%d\n",
633		result[4] >> 4, result[4] & 0x0f, result[5] >> 1, result[5] & 0x01);
634	dprintf(FLO "sec_per_trk/end_of_trk=0x%.2x lock=%d, byte[7]=0x%.2x\n",
635		result[6], result[7] >> 7,result[7]);
636	dprintf(FLO "gap=%d wg=%d eis=%d fifo=%d poll=%d thresh=%d pretrk=%d\n",
637		(result[7] & 0x02) >> 1, result[7] & 0x01, (result[8] & 0x40) >> 6,
638		(result[8] & 0x20) >> 5, (result[8] & 0x10) >> 4, result[8] & 0x0f, result[9]);
639}
640
641
642static void
643fill_command_from_lba(floppy_t *flp, floppy_command *cmd, int lba)
644{
645	cmd->cylinder = lba / (flp->bgeom.sectors_per_track * flp->bgeom.head_count);
646	cmd->head = (lba / flp->bgeom.sectors_per_track) % flp->bgeom.head_count;
647	cmd->sector = lba % flp->bgeom.sectors_per_track + 1;
648	cmd->drive = (flp->drive_num & 0x3) | (cmd->head << 2) | 0x80; /* Implied Seek */
649}
650
651
652/* does NOT check for valid track number */
653ssize_t
654pio_read_sectors(floppy_t *flp, /*void *buf,*/ int lba, int num_sectors)
655{
656	ssize_t transfered = 0;
657	floppy_command cmd;
658#if 0
659	uint8 cmd2[8];
660	uint8 result[4];
661#endif
662	//floppy_result res;
663
664	if (flp->status < FLOP_MEDIA_FORMAT_FOUND)
665		return B_DEV_UNREADABLE;
666	turn_on_motor(flp);
667	drive_select(flp);
668	if (flp->geometry == NULL || !flp->bgeom.bytes_per_sector || !flp->bgeom.sectors_per_track) {
669		drive_deselect(flp);
670		return B_DEV_NO_MEDIA;
671	}
672
673	flp->track = -1;
674
675	num_sectors = MIN(num_sectors, (signed)(flp->bgeom.sectors_per_track - (lba % flp->bgeom.sectors_per_track)));
676	cmd.id = CMD_READD | /*0xc0*/0x40; // multi-track, read MFM, one head
677	cmd.sector_size = 2;	/* 512 bytes */
678	cmd.track_length = num_sectors;//flp->bgeom.sectors_per_track;
679	cmd.gap3_length = flp->geometry->gap;	//27; /* 3.5" floppy */
680	cmd.data_length = 0xff;	/* don't care */
681	fill_command_from_lba(flp, &cmd, lba);
682	dprintf(FLO "pio_read_sector(%d, %d={%d,%d,%d}, %d)\n", flp->drive_num, lba, cmd.cylinder, cmd.head, cmd.sector, num_sectors);
683	if (num_sectors < 1) {
684		drive_deselect(flp);
685		return EINVAL;
686	}
687	//num_sectors = 1;
688
689#if 0
690	/* first issue sense interrupt, to find the current track */
691	TRACE("track check: SENSEI\n");
692	cmd2[0] = CMD_SENSEI;
693	send_command(flp, cmd2, 1);
694	read_result(flp, result, 2);
695	PRINT_SR0(result[0]);
696	/* if the current track is not the one we want, seek */
697	if (result[1] != cmd.cylinder) {
698		/* seek */
699		cmd2[0] = CMD_SEEK; // seek command
700		cmd2[1] = (flp->drive_num & 0x3) | (cmd.head << 2);
701		cmd2[2] = cmd.cylinder; // track 0
702
703		TRACE("SEEK at track %d head %d\n", cmd.cylinder, cmd.head);
704		send_command(flp, cmd2, 3);
705		if (wait_result(flp) < 0)
706			return ENXIO;
707	}
708
709#endif
710
711	flp->master->avail = 0;
712	flp->master->buffer_index = (lba % (flp->bgeom.sectors_per_track/* * flp->bgeom.head_count*/)) * flp->bgeom.bytes_per_sector;
713	//flp->pending_cmd = CMD_READD;
714
715	send_command(flp, (uint8 *)&cmd, sizeof(cmd));
716
717	if (wait_result(flp) < 0) {
718		drive_deselect(flp);
719		return ENXIO;
720	}
721	PRINT_SR0(flp->result[0]);
722	TRACE("sr1: %02x\n", flp->result[1]);
723	TRACE("sr2: %02x\n", flp->result[2]);
724	TRACE("@ track %d, head %d, sec %d, bps %d\n", flp->result[3], flp->result[4], flp->result[5], flp->result[6]);
725	switch (flp->result[0] & FDC_SR0_IC) {
726	case 0x80:
727		transfered = EINVAL;
728		break;
729	case 0x40:
730		//if (flp->result[1] != 0x80) /* End Of Track is not really an error, actually it means it worked :) */
731		//	transfered = ENXIO;
732		break;
733	case 0xc0:
734		transfered = B_DEV_MEDIA_CHANGED;
735		break;
736	}
737	if (transfered) {
738		drive_deselect(flp);
739		return ENXIO;
740	}
741	/* normal termination */
742	transfered = flp->avail;
743/*	if (transfered > 0)
744		memcpy(buf, flp->buffer, flp->avail);*/
745
746	drive_deselect(flp);
747	return num_sectors;//transfered;
748}
749
750
751ssize_t
752pio_write_sectors(floppy_t *flp, /*const void *buf,*/ int lba, int num_sectors)
753{
754	return -1;
755}
756
757
758/* does NOT check for valid track number */
759ssize_t
760pio_read_track(floppy_t *flp, /*void *buf,*/ int lba)
761{
762	ssize_t transfered = 0;
763	floppy_command cmd;
764#if 0
765	uint8 cmd2[8];
766	uint8 result[4];
767#endif
768	int tries = 0;
769
770	if (flp->status < FLOP_MEDIA_FORMAT_FOUND)
771		return B_DEV_UNREADABLE;
772	turn_on_motor(flp);
773	drive_select(flp);
774	if (flp->geometry == NULL || !flp->bgeom.bytes_per_sector || !flp->bgeom.sectors_per_track) {
775		drive_deselect(flp);
776		return B_DEV_NO_MEDIA;
777	}
778
779	flp->track = -1;
780
781retry_track:
782
783	//num_sectors = MIN(num_sectors, (signed)(flp->bgeom.sectors_per_track - (lba % flp->bgeom.sectors_per_track)));
784	cmd.id = CMD_READTRK | 0x40; // read MFM
785	cmd.sector_size = 2;	/* 512 bytes */
786	cmd.track_length = flp->bgeom.sectors_per_track;
787	cmd.gap3_length = flp->geometry->gap;	//27; /* 3.5" floppy */
788	cmd.data_length = 0xff;	/* don't care */
789	fill_command_from_lba(flp, &cmd, lba);
790	cmd.sector = 1;
791	dprintf(FLO "pio_read_track(%d, %d={%d,%d,%d}) try %d\n", flp->drive_num, lba, cmd.cylinder, cmd.head, cmd.sector, tries);
792
793#if 0
794	/* first issue sense interrupt, to find the current track */
795	TRACE("track check: SENSEI\n");
796	cmd2[0] = CMD_SENSEI;
797	send_command(flp, cmd2, 1);
798	read_result(flp, result, 2);
799	PRINT_SR0(result[0]);
800	/* if the current track is not the one we want, seek */
801	if (result[1] != cmd.cylinder) {
802		/* seek */
803		cmd2[0] = CMD_SEEK; // seek command
804		cmd2[1] = (flp->drive_num & 0x3) | (cmd.head << 2);
805		cmd2[2] = cmd.cylinder; // track 0
806
807		TRACE("SEEK at track %d head %d\n", cmd.cylinder, cmd.head);
808		send_command(flp, cmd2, 3);
809		if (wait_result(flp) < 0)
810			return ENXIO;
811	}
812#endif
813
814	flp->master->avail = 0;
815	flp->master->buffer_index = 0;//(lba % (flp->bgeom.sectors_per_track * flp->bgeom.head_count)) * flp->bgeom.bytes_per_sector;
816
817	send_command(flp, (uint8 *)&cmd, sizeof(cmd));
818
819	if (wait_result(flp) < 0) {
820		drive_deselect(flp);
821		return ENXIO;
822	}
823
824	PRINT_SR0(flp->result[0]);
825	TRACE("sr1: %02x\n", flp->result[1]);
826	TRACE("sr2: %02x\n", flp->result[2]);
827	TRACE("@ track %d, head %d, sec %d, bps %d\n", flp->result[3], flp->result[4], flp->result[5], flp->result[6]);
828
829	switch (flp->result[0] & FDC_SR0_IC) {
830	case 0x80:
831		transfered = EINVAL;
832		break;
833	case 0x40:
834		TRACE(FLO "sr1: %02x\n", flp->result[1]);
835		if (flp->result[1] != 0x80) {/* End Of Track is not really an error, actually it means it worked :) */
836			if (/*(flp->result[1] == 0x10) && */tries < 3) { /* overrun */
837				tries++;
838				goto retry_track;
839			} else
840				transfered = EIO;
841		}
842		break;
843	case 0xc0:
844		transfered = B_DEV_MEDIA_CHANGED;
845		break;
846	}
847	if (transfered) {
848		drive_deselect(flp);
849		return ENXIO;
850	}
851	/* normal termination */
852	transfered = flp->avail;
853/*	if (transfered > 0)
854		memcpy(buf, flp->buffer, flp->avail);*/
855	flp->master->track = cmd.cylinder*flp->bgeom.head_count + cmd.head;
856
857	drive_deselect(flp);
858	return 1;//transfered;
859}
860
861
862ssize_t
863read_sectors(floppy_t *flp, int lba, int num_sectors)
864{
865	ssize_t transfered = 0;
866	//return pio_read_sectors(flp, lba, num_sectors);
867	/* if (nothing cached || not the same track cached) */
868	dprintf(FLO "read_sector(%d, %d, %d)\n", flp->drive_num, lba, num_sectors);
869	num_sectors = MIN(num_sectors, (signed)(flp->bgeom.sectors_per_track - (lba % flp->bgeom.sectors_per_track)));
870	if ((flp->master->track < 0) ||
871		(flp->master->track != (signed)(lba / (flp->bgeom.sectors_per_track)))) {
872		if ((lba / (flp->bgeom.sectors_per_track)) >= (flp->bgeom.head_count * flp->bgeom.cylinder_count))
873			return ENXIO;
874		transfered = pio_read_track(flp, lba);
875		if (transfered < 0)
876			return transfered;
877		/* XXX: TODO: in case of IO error, retry by single sector */
878	}
879	return num_sectors;
880}
881
882