xref: /illumos-gate/usr/src/lib/libsmedia/plugins/floppy/common/f_format.c (revision 581cede61ac9c14d8d4ea452562a567189eead78)
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 
55 extern void my_perror(char *err_string);
56 /*
57  * local functions
58  */
59 static void	restore_default_chars(int32_t fd,
60 				    struct fd_char save_fdchar,
61 				    struct dk_allmap save_allmap);
62 static int32_t
63 format_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 
513 static void
514 restore_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 
540 int32_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 
571 int32_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