1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License").  You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22
23/*
24 * Copyright 2003 Sun Microsystems, Inc.  All rights reserved.
25 * Use is subject to license terms.
26 */
27
28#pragma ident	"%Z%%M%	%I%	%E% SMI"
29
30/*
31 * f_format.c :
32 *      This file contains the format functions for floppy plug-in for
33 * 	library libsm.so.
34 */
35
36#include <stdio.h>
37#include <sys/types.h>
38#include <sys/dklabel.h>
39#include <sys/dkio.h>
40#include <sys/fdio.h>
41#include <fcntl.h>
42#include <unistd.h>
43#include <locale.h>
44#include <errno.h>
45#include <sys/param.h>
46#include <stdlib.h>
47#include <sys/smedia.h>
48#include "../../../library/inc/rmedia.h"
49#include "f_defines.h"
50
51/*
52 * extern functions
53 */
54
55extern void my_perror(char *err_string);
56/*
57 * local functions
58 */
59static void	restore_default_chars(int32_t fd,
60				    struct fd_char save_fdchar,
61				    struct dk_allmap save_allmap);
62static int32_t
63format_floppy(int32_t fd, void *ip)
64{
65	struct	format_track *ft = (struct format_track *)ip;
66	int32_t format_flags;
67	int32_t	transfer_rate = 1000;   /* transfer rate code */
68	int32_t	sec_size = 512;		/* sector size */
69	uchar_t	gap = 0x54;		/* format gap size */
70	uchar_t  *fbuf, *p;
71	int32_t	cyl_size;
72	int32_t	i;
73	int32_t	chgd;			/* for testing disk changed/present */
74	int32_t	cyl, hd;
75	int32_t	size_of_part, size_of_dev;
76	int32_t	spt = 36;		/* sectors per track */
77	int32_t	drive_size;
78	uchar_t	num_cyl = 80;		/*  max number of cylinders */
79	struct fd_char save_fdchar;	/* original diskette characteristics */
80	struct dk_allmap save_allmap;	/* original diskette partition info */
81	int32_t	D_flag = 0;	/* double (aka low) density flag */
82	int32_t	E_flag = 0;	/* extended density */
83	int32_t	H_flag = 0;	/* high density */
84	int32_t	M_flag = 0;	/* medium density */
85	struct fd_char 		fdchar;
86	struct dk_geom 		fdgeom;
87	struct dk_allmap 	allmap;
88	struct dk_cinfo 	dkinfo;
89	int32_t start_head, end_head, start_cyl, end_cyl;
90
91	/* for verify buffers */
92	static uchar_t	*obuf;
93
94
95	/* FDRAW ioctl command structures for seeking and formatting */
96	struct fd_raw fdr_seek = {
97		FDRAW_SEEK, 0, 0, 0, 0, 0, 0, 0, 0, 0,
98		3,
99		0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
100		0,
101		0
102	};
103
104	struct fd_raw fdr_form = {
105		0x4D, 0, 2, 0, 0x54, (char)0xA5, 0, 0, 0, 0,
106		6,
107		0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
108		0,	/* nbytes */
109		0	/* addr */
110	};
111
112	format_flags = ft->flag;
113
114	DPRINTF1("Format flag is %d\n", format_flags);
115	if (format_flags == SM_FORMAT_HD) {
116		H_flag = 1;
117	} else if (format_flags == SM_FORMAT_DD) {
118		D_flag = 1;
119	} else if (format_flags == SM_FORMAT_ED) {
120		E_flag = 1;
121	} else if (format_flags == SM_FORMAT_MD) {
122		M_flag = 1;
123	} else {
124		DPRINTF("Invalid operation \n");
125		errno = ENOTSUP;
126		return (-1);
127	}
128
129
130	/*
131	 * restore drive to default geometry and characteristics
132	 * (probably not implemented on sparc)
133	 */
134	(void) ioctl(fd, FDDEFGEOCHAR, NULL);
135
136
137	if (ioctl(fd, DKIOCINFO, &dkinfo) < 0) {
138		PERROR("DKIOCINFO failed.");
139		exit(3);
140	}
141
142
143	/* get the default partititon maps */
144	if (ioctl(fd, DKIOCGAPART, &allmap) < 0) {
145		PERROR("DKIOCGAPART failed.");
146		return (-1);
147	}
148
149	/* Save the original default partition maps */
150	save_allmap = allmap;
151
152	/* find out the characteristics of the default diskette */
153	if (ioctl(fd, FDIOGCHAR, &fdchar) < 0) {
154		PERROR("FDIOGCHAR failed.");
155		return (-1);
156	}
157
158	/* Save the original characteristics of the default diskette */
159	save_fdchar = fdchar;
160
161	/*
162	 * The user may only format the entire diskette.
163	 * formatting partion a or b is not allowed
164	 */
165	size_of_part = allmap.dka_map[dkinfo.dki_partition].dkl_nblk
166			* DEV_BSIZE;
167	size_of_dev = fdchar.fdc_ncyl * fdchar.fdc_nhead
168			* fdchar.fdc_secptrack * fdchar.fdc_sec_size;
169
170	if (size_of_part != size_of_dev) {
171		DPRINTF("The entire diskette must be formatted\n");
172		DPRINTF1("size_of_part %d\n", size_of_part);
173		DPRINTF1("size_of_dev %d\n", size_of_dev);
174		errno = ENOTSUP;
175		return (-1);
176	}
177
178	/* find out the geometry of the drive */
179	if (ioctl(fd, DKIOCGGEOM, &fdgeom) < 0) {
180		PERROR("DKIOCGGEOM failed.");
181		return (-1);
182	}
183
184#ifdef sparc
185	fdchar.fdc_medium = 3;
186#endif
187	if (fdchar.fdc_medium == 5)
188		drive_size = 5;
189	else
190		drive_size = 3;
191
192	/*
193	 * set proper density flag in case we're formating to default
194	 * characteristics because no density switch was input
195	 */
196
197/* XXX */
198	if ((E_flag | H_flag | D_flag | M_flag) == 0) {
199		switch (fdchar.fdc_transfer_rate) {
200		case 1000:
201			/* assumes only ED uses 1.0 MB/sec */
202			E_flag++;
203			break;
204		case 500:
205		default:
206			/*
207			 * default to HD even though High density and
208			 * "medium" density both use 500 KB/sec
209			 */
210			H_flag++;
211			break;
212#ifndef sparc
213		case 250:
214			/* assumes only DD uses 250 KB/sec */
215			D_flag++;
216			break;
217#endif
218		}
219	}
220
221	if (H_flag) {
222		transfer_rate = 500;
223		num_cyl = 80;
224		sec_size = 512;
225		if (drive_size == 5) {
226			spt = 15;
227		} else {
228			spt = 18;
229		}
230		gap = 0x54;
231	} else if (D_flag) {
232		transfer_rate = 250;
233		if (drive_size == 5) {
234			if (fdchar.fdc_transfer_rate == 500) {
235				/*
236				 * formatting a 360KB DD diskette in
237				 * a 1.2MB drive is not a good idea
238				 */
239				transfer_rate = 300;
240				fdchar.fdc_steps = 2;
241			}
242			num_cyl = 40;
243			gap = 0x50;
244		} else {
245			num_cyl = 80;
246			gap = 0x54;
247		}
248		sec_size = 512;
249		spt = 9;
250	} else if (M_flag) {
251#ifdef sparc
252		transfer_rate = 500;
253#else
254		/*
255		 * 416.67 KB/sec is the effective transfer rate of a "medium"
256		 * density diskette spun at 300 rpm instead of 360 rpm
257		 */
258		transfer_rate = 417;
259#endif
260		num_cyl = 77;
261		sec_size = 1024;
262		spt = 8;
263		gap = 0x74;
264	} else if (E_flag) {
265		transfer_rate = 1000;
266		num_cyl = 80;
267		sec_size = 512;
268		spt = 36;
269		gap = 0x54;
270	}
271
272	/*
273	 * Medium density diskettes have 1024 byte blocks.  The dk_map
274	 * structure in dklabel.h assumes the blocks size is DEVBSIZE (512)
275	 * bytes.  The dkl_nblk field is in terms of DEVBSIZE byte blocks
276	 * while the spt variable is in terms of the true block size on
277	 * the diskette.
278	 */
279	if (allmap.dka_map[2].dkl_nblk !=
280		(2 * num_cyl * spt * (M_flag ? 2 : 1))) {
281		allmap.dka_map[1].dkl_cylno = num_cyl - 1;
282		allmap.dka_map[0].dkl_nblk = 2 * (num_cyl - 1) * spt *
283			(M_flag ? 2 : 1);
284		allmap.dka_map[1].dkl_nblk = 2 * spt * (M_flag ? 2 : 1);
285		allmap.dka_map[2].dkl_nblk = 2 * num_cyl * spt *
286			(M_flag ? 2 : 1);
287		if (allmap.dka_map[3].dkl_nblk)
288			allmap.dka_map[3].dkl_nblk = 2 * (num_cyl - 1) * spt *
289				(M_flag ? 2 : 1);
290		if (allmap.dka_map[4].dkl_nblk)
291			allmap.dka_map[4].dkl_nblk =
292				2 * spt * (M_flag ? 2 : 1);
293	}
294
295
296
297#ifndef sparc
298	if (num_cyl > fdchar.fdc_ncyl || spt > fdchar.fdc_secptrack ||
299	    transfer_rate > fdchar.fdc_transfer_rate) {
300		PERROR("drive not capable of requested density");
301		return (-1);
302	}
303#endif
304	if (num_cyl != fdchar.fdc_ncyl || spt != fdchar.fdc_secptrack ||
305	    transfer_rate != fdchar.fdc_transfer_rate) {
306		/*
307		 * -- CAUTION --
308		 * The SPARC fd driver is using a non-zero value in
309		 * fdc_medium to indicate the 360 rpm, 77 track,
310		 * 9 sectors/track, 1024 bytes/sector mode of operation
311		 * (similar to an 8", DS/DD, 1.2 MB floppy).
312		 *
313		 * The x86 fd driver uses fdc_medium as the diameter
314		 * indicator, either 3 or 5.  It should not be modified.
315		 */
316#ifdef sparc
317		fdchar.fdc_medium = M_flag ? 1 : 0;
318#endif
319		fdchar.fdc_transfer_rate = transfer_rate;
320		fdchar.fdc_ncyl = num_cyl;
321		fdchar.fdc_sec_size = sec_size;
322		fdchar.fdc_secptrack = spt;
323
324		if (ioctl(fd, FDIOSCHAR, &fdchar) < 0) {
325			PERROR("FDIOSCHAR (density selection) failed");
326			/* restore the default characteristics */
327			restore_default_chars(fd, save_fdchar, save_allmap);
328			return (-1);
329		}
330		if (ioctl(fd, DKIOCSAPART, &allmap) < 0) {
331			PERROR("DKIOCSAPART failed");
332
333			/* restore the default characteristics */
334			restore_default_chars(fd, save_fdchar, save_allmap);
335				return (-1);
336		}
337	}
338
339	cyl_size = 2 * sec_size * spt;
340
341	if ((obuf = (uchar_t *)malloc((size_t)cyl_size)) == 0) {
342		PERROR("car't malloc verify buffer");
343		/* restore the default characteristics */
344		restore_default_chars(fd, save_fdchar, save_allmap);
345		return (-1);
346	}
347	/*
348	 * for those systems that support this ioctl, they will
349	 * return whether or not a diskette is in the drive.
350	 */
351	if (ioctl(fd, FDGETCHANGE, &chgd) == 0) {
352		if (chgd & FDGC_CURRENT) {
353			(void) fprintf(stderr,
354			    gettext("no diskette in drive \n"));
355
356			/* restore the default characteristics */
357			restore_default_chars(fd, save_fdchar, save_allmap);
358			return (-1);
359		}
360		if (chgd & FDGC_CURWPROT) {
361			(void) fprintf(stderr,
362			    gettext("Media is write protected\n"));
363
364			/* restore the default characteristics */
365			restore_default_chars(fd, save_fdchar, save_allmap);
366			return (-1);
367		}
368	}
369
370	if ((fbuf = (uchar_t *)malloc((unsigned)(4 * spt))) == 0) {
371		PERROR("Could not malloc format header buffer");
372		restore_default_chars(fd, save_fdchar, save_allmap);
373		return (-1);
374	}
375	/*
376	 * do the format, a track at a time
377	 */
378	if (ft->track_no == -1) {
379		start_cyl = 0;
380		end_cyl	  = num_cyl;
381		start_head =  0;
382		end_head = fdchar.fdc_nhead;
383	} else {
384		start_cyl = ft->track_no;
385		end_cyl = ft->track_no + 1;
386		start_head = ft->head;
387		end_head = ft->head + 1;
388		if ((end_cyl > num_cyl) || (end_head > fdchar.fdc_nhead)) {
389			errno = EINVAL;
390			return (-1);
391		}
392	}
393
394	for (cyl = start_cyl; cyl < (int32_t)end_cyl; cyl++) {
395		/*
396		 * This is not the optimal ioctl to format the floppy.
397		 * The device driver should do do the work,
398		 * instead of this program mucking with a lot
399		 * of low-level, device-dependent code.
400		 */
401		fdr_seek.fdr_cmd[2] = cyl;
402		if (ioctl(fd, FDRAW, &fdr_seek) < 0) {
403			(void) fprintf(stderr,
404			    gettext(" seek to cyl %d failed\n"),
405			    cyl);
406
407			/* restore the default characteristics */
408			restore_default_chars(fd, save_fdchar, save_allmap);
409			return (-1);
410		}
411		/*
412		 * Assume that the fd driver has issued a SENSE_INT
413		 * command to complete the seek operation.
414		 */
415
416		for (hd = start_head; hd < end_head; hd++) {
417			p = (uchar_t *)fbuf;
418			for (i = 1; i <= spt; i++) {
419				*p++ = (uchar_t)cyl;
420				*p++ = (uchar_t)hd;
421				*p++ = (uchar_t)i; /* sector # */
422				*p++ = (sec_size == 1024) ? 3 : 2;
423			}
424			/*
425			 * ASSUME the fd driver is going to set drive-select
426			 * bits in the second command byte
427			 */
428			fdr_form.fdr_cmd[1] = hd << 2;
429			fdr_form.fdr_cmd[2] = (sec_size == 1024) ? 3 : 2;
430			fdr_form.fdr_cmd[3] = spt;
431			fdr_form.fdr_cmd[4] = gap;
432			fdr_form.fdr_nbytes = 4 * spt;
433			fdr_form.fdr_addr = (char *)fbuf;
434
435			if (ioctl(fd, FDRAW, &fdr_form) < 0) {
436
437
438				(void) fprintf(stderr,
439					gettext(
440					"format of cyl %d head %d failed\n"),
441						cyl, hd);
442
443				/* restore the default characteristics */
444				restore_default_chars(fd, save_fdchar,
445				    save_allmap);
446				return (-1);
447			}
448			if (fdr_form.fdr_result[0] & 0xC0) {
449				if (fdr_form.fdr_result[1] & 0x02) {
450					(void) fprintf(stderr, gettext(
451					/*CSTYLED*/
452					"diskette is write protected\n"));
453
454					/*
455					 * restore the default
456					 * characteristics
457					 */
458					restore_default_chars(fd, save_fdchar,
459					    save_allmap);
460					return (-1);
461				}
462				(void) fprintf(stderr,
463					gettext(
464					"format of cyl %d head %d failed\n"),
465						cyl, hd);
466
467				/* restore the default characteristics */
468				restore_default_chars(fd, save_fdchar,
469				    save_allmap);
470				return (-1);
471			}
472
473		}
474
475		/*
476		 *  do a quick verify
477		 */
478		if (llseek(fd, cyl * cyl_size, 0) != cyl * cyl_size) {
479			PERROR(" bad seek to format verify, ");
480			/* restore the default characteristics */
481			restore_default_chars(fd, save_fdchar,
482			    save_allmap);
483			return (-1);
484		}
485		if (fdchar.fdc_nhead == end_head) {
486			if (read(fd, obuf, cyl_size) != cyl_size) {
487				PERROR("Could not read format data");
488				/* restore the default characteristics */
489				restore_default_chars(fd, save_fdchar,
490				    save_allmap);
491				return (-1);
492			}
493		}
494	}
495	if (llseek(fd, (off_t)0, 0) != 0) {
496		PERROR("seek to blk 0 failed");
497		/* restore the default characteristics */
498		restore_default_chars(fd, save_fdchar, save_allmap);
499		return (-1);
500	}
501	return (0);
502}
503
504
505/*
506 * Restore the default characteristics of the floppy diskette.
507 * Fdformat changes the characteristics in the process of formatting.
508 * If fdformat fails while in the process of doing the format, fdformat
509 * should clean up after itself and reset the driver back to the original
510 * state.
511 */
512
513static void
514restore_default_chars(int32_t fd,
515			struct fd_char save_fdchar,
516			struct dk_allmap save_allmap)
517{
518
519
520	/*
521	 * When this function is called, fdformat is failing anyways,
522	 * so the errors are not processed.
523	 */
524
525	(void) ioctl(fd, FDIOSCHAR, &save_fdchar);
526
527	(void) ioctl(fd, DKIOCSAPART, &save_allmap);
528
529	/*
530	 * Before looking at the diskette's characteristics, format_floppy()
531	 * sets the x86 floppy driver to the default characteristics.
532	 * restore drive to default geometry and
533	 * characteristics.  This ioctl isn't implemented on
534	 * sparc.
535	 */
536	(void) ioctl(fd, FDDEFGEOCHAR, NULL);
537
538}
539
540int32_t
541_m_media_format(rmedia_handle_t *handle, void *ip) {
542	struct format_track ft;
543
544	/* Check for valid handle */
545	if (handle == NULL) {
546		DPRINTF("Null Handle\n");
547		errno = EINVAL;
548		return (-1);
549	}
550	if (handle->sm_signature != (int32_t)LIBSMEDIA_SIGNATURE) {
551		DPRINTF("Invalid signature in handle.\n");
552		DPRINTF2(
553			"Signature expected=0x%x, found=0x%x\n",
554				LIBSMEDIA_SIGNATURE, handle->sm_signature);
555		errno = EINVAL;
556		return (-1);
557	}
558	if (handle->sm_fd < 0) {
559		DPRINTF("Invalid file handle.\n");
560		errno = EINVAL;
561		return (-1);
562	}
563	DPRINTF("Format floppy called \n");
564	ft.track_no = (-1);
565	ft.head = (-1);
566	ft.flag = ((struct format_flags *)ip)->flavor;
567	return (format_floppy(handle->sm_fd, &ft));
568
569}
570
571int32_t
572_m_media_format_track(rmedia_handle_t *handle, void *ip)
573{
574
575	/* Check for valid handle */
576	if (handle == NULL) {
577		DPRINTF("Null Handle\n");
578		errno = EINVAL;
579		return (-1);
580	}
581	if (handle->sm_signature != (int32_t)LIBSMEDIA_SIGNATURE) {
582		DPRINTF("Invalid signature in handle.\n");
583		DPRINTF2(
584			"Signature expected=0x%x, found=0x%x\n",
585				LIBSMEDIA_SIGNATURE, handle->sm_signature);
586		errno = EINVAL;
587		return (-1);
588	}
589	if (handle->sm_fd < 0) {
590		DPRINTF("Invalid file handle.\n");
591		errno = EINVAL;
592		return (-1);
593	}
594#ifdef DEBUG
595	if (ip != NULL) {
596		struct format_track *ft = (struct format_track *)ip;
597		DPRINTF2("Format track %d head %d\n", ft->track_no, ft->head);
598	}
599#endif /* DEBUG */
600	return (format_floppy(handle->sm_fd, ip));
601}
602