Home | History | Annotate | Download | only in cdrw
      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  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
     24  * Use is subject to license terms.
     25  */
     26 
     27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
     28 
     29 #include <thread.h>
     30 #include <synch.h>
     31 #include <errno.h>
     32 #include <stdlib.h>
     33 #include <string.h>
     34 #include <sys/types.h>
     35 #include <signal.h>
     36 #include <unistd.h>
     37 #include <stdio.h>
     38 
     39 #include "device.h"
     40 #include "bstream.h"
     41 #include "trackio.h"
     42 #include "util.h"
     43 #include "mmc.h"
     44 #include "transport.h"
     45 #include "misc_scsi.h"
     46 #include "main.h"
     47 
     48 /*
     49  * tio data
     50  */
     51 static struct iobuf	tio_iobs[NIOBS];
     52 static uchar_t		tio_synch_initialized, tio_abort, tio_done;
     53 static int		tio_errno;
     54 static mutex_t		tio_mutex;
     55 static cond_t		tio_cond;
     56 static int		tio_fd, tio_trackno;
     57 static int		tio_got_ctrl_c;
     58 
     59 /*
     60  * Progress call back data.
     61  */
     62 static mutex_t	pcb_mutex;
     63 static cond_t	pcb_cond;
     64 static uchar_t	pcb_user_abort, pcb_done, pcb_synch_initialized;
     65 static int64_t	pcb_completed_io_size;
     66 static int	(*pcb_cb)(int64_t, int64_t);
     67 static int64_t	pcb_arg;
     68 
     69 static void
     70 fini_tio_data(void)
     71 {
     72 	int i;
     73 	for (i = 0; i < NIOBS; i++) {
     74 		if (tio_iobs[i].iob_buf) {
     75 			free(tio_iobs[i].iob_buf);
     76 			tio_iobs[i].iob_buf = NULL;
     77 		}
     78 	}
     79 	if (tio_synch_initialized == 1) {
     80 		(void) mutex_destroy(&tio_mutex);
     81 		(void) cond_destroy(&tio_cond);
     82 		tio_synch_initialized = 0;
     83 	}
     84 	tio_abort = tio_done = 0;
     85 }
     86 
     87 static void
     88 init_tio_data(int bsize)
     89 {
     90 	int i;
     91 
     92 	(void) memset(tio_iobs, 0, sizeof (tio_iobs));
     93 	for (i = 0; i < NIOBS; i++) {
     94 		tio_iobs[i].iob_buf = (uchar_t *)my_zalloc(bsize);
     95 		tio_iobs[i].iob_total_size = bsize;
     96 		tio_iobs[i].iob_state = IOBS_EMPTY;
     97 	}
     98 	(void) mutex_init(&tio_mutex, USYNC_THREAD, 0);
     99 	(void) cond_init(&tio_cond, USYNC_THREAD, 0);
    100 	tio_synch_initialized = 1;
    101 	tio_abort = tio_done = 0;
    102 	tio_got_ctrl_c = 0;
    103 }
    104 
    105 static void
    106 init_pcb_data(void)
    107 {
    108 	(void) mutex_init(&pcb_mutex, USYNC_THREAD, 0);
    109 	(void) cond_init(&pcb_cond, USYNC_THREAD, 0);
    110 	pcb_user_abort = pcb_done = 0;
    111 	pcb_completed_io_size = 0;
    112 	pcb_synch_initialized = 1;
    113 }
    114 
    115 static void
    116 fini_pcb_data(void)
    117 {
    118 	if (pcb_synch_initialized == 1) {
    119 		(void) mutex_destroy(&pcb_mutex);
    120 		(void) cond_destroy(&pcb_cond);
    121 		pcb_synch_initialized = 0;
    122 	}
    123 	pcb_user_abort = pcb_done = 0;
    124 	pcb_completed_io_size = 0;
    125 }
    126 
    127 /* ARGSUSED */
    128 static void *
    129 write_to_cd(void *arg)
    130 {
    131 	int i;
    132 
    133 	i = 0;
    134 #ifndef lint
    135 	while (1) {
    136 #endif
    137 		(void) mutex_lock(&tio_mutex);
    138 		while ((tio_iobs[i].iob_state != IOBS_READY) &&
    139 		    (tio_abort == 0)) {
    140 			/* Wait for buffer to become ready */
    141 			(void) cond_wait(&tio_cond, &tio_mutex);
    142 		}
    143 		if (tio_abort == 1) {
    144 			/* Do a flush cache before aborting */
    145 			(void) flush_cache(tio_fd);
    146 			(void) mutex_unlock(&tio_mutex);
    147 			thr_exit((void *)1);
    148 		}
    149 		tio_iobs[i].iob_state = IOBS_UNDER_DEVICE_IO;
    150 
    151 		/* If no more data, then close the track */
    152 		if (tio_iobs[i].iob_data_size == 0) {
    153 			int retry = 20;
    154 
    155 			/* Some drives misbehave if flush_cache is not done */
    156 			(void) flush_cache(tio_fd);
    157 
    158 			if (write_mode == TAO_MODE) {
    159 				/* Its important to try hard to close track */
    160 				if (simulation)
    161 					retry = 5;
    162 
    163 				for (; retry > 0; retry--) {
    164 
    165 					/* OK to hold mutex when close_track */
    166 					if (close_track(tio_fd,
    167 					    tio_trackno, 0, 0))
    168 						break;
    169 
    170 					(void) sleep(1);
    171 				}
    172 			}
    173 
    174 			/* Some drives don't allow close track in test write */
    175 			if ((retry == 0) && (simulation == 0)) {
    176 				if (errno)
    177 					tio_errno = errno;
    178 				else
    179 					tio_errno = -1;
    180 			}
    181 
    182 			tio_done = 1;
    183 			(void) cond_broadcast(&tio_cond);
    184 			(void) mutex_unlock(&tio_mutex);
    185 			thr_exit((void *)0);
    186 		}
    187 
    188 		(void) mutex_unlock(&tio_mutex);
    189 
    190 		if (!write10(tio_fd, tio_iobs[i].iob_start_blk,
    191 		    tio_iobs[i].iob_nblks, tio_iobs[i].iob_buf,
    192 		    tio_iobs[i].iob_data_size)) {
    193 
    194 			int err = errno;
    195 			(void) mutex_lock(&tio_mutex);
    196 			if (err)
    197 				tio_errno = err;
    198 			else
    199 				tio_errno = -1;
    200 			(void) cond_broadcast(&tio_cond);
    201 			(void) mutex_unlock(&tio_mutex);
    202 			thr_exit((void *)2);
    203 		}
    204 
    205 		(void) mutex_lock(&tio_mutex);
    206 		tio_iobs[i].iob_state = IOBS_EMPTY;
    207 		(void) cond_broadcast(&tio_cond);
    208 		(void) mutex_unlock(&tio_mutex);
    209 		i++;
    210 		if (i == NIOBS)
    211 			i = 0;
    212 #ifndef lint
    213 	}
    214 #endif
    215 	return (NULL);
    216 }
    217 
    218 /* ARGSUSED */
    219 static void *
    220 progress_callback(void *arg)
    221 {
    222 	int ret;
    223 
    224 pc_again:
    225 	(void) mutex_lock(&pcb_mutex);
    226 	if (!pcb_done) {
    227 		(void) cond_wait(&pcb_cond, &pcb_mutex);
    228 	}
    229 	if (pcb_done) {
    230 		(void) mutex_unlock(&pcb_mutex);
    231 		if (tio_got_ctrl_c) {
    232 			pcb_cb(pcb_arg, 0xFFFFFFFF);
    233 		}
    234 		thr_exit((void *)0);
    235 	}
    236 	(void) mutex_unlock(&pcb_mutex);
    237 	ret = pcb_cb(pcb_arg, pcb_completed_io_size);
    238 	if (ret != 0) {
    239 		(void) mutex_lock(&pcb_mutex);
    240 		pcb_user_abort = (uchar_t)ret;
    241 		(void) mutex_unlock(&pcb_mutex);
    242 		thr_exit((void *)0);
    243 	}
    244 #ifdef lint
    245 	return (NULL);
    246 #else
    247 	goto pc_again;
    248 #endif
    249 }
    250 
    251 /* ARGSUSED */
    252 static void
    253 trackio_sig_handler(int i)
    254 {
    255 	/* Dont need mutex as it is only modified here */
    256 	tio_got_ctrl_c = 1;
    257 	(void) signal(SIGINT, trackio_sig_handler);
    258 }
    259 
    260 int
    261 write_track(cd_device *dev, struct track_info *ti, bstreamhandle h,
    262 	int (*cb)(int64_t, int64_t), int64_t arg, struct trackio_error *te)
    263 {
    264 	int			blksize, i, sz_read, rem;
    265 	uint32_t		start_b;
    266 	thread_t		tio_thread, pc_thread;
    267 	int			write_cd_thr_created;
    268 	int			progress_callback_thr_created;
    269 	int			signal_handler_installed;
    270 	int			retval;
    271 	void			(*ohandler)(int);
    272 
    273 	write_cd_thr_created = progress_callback_thr_created = 0;
    274 	signal_handler_installed = retval = 0;
    275 
    276 	if (ti->ti_track_mode & 4)
    277 		blksize = DATA_TRACK_BLKSIZE;
    278 	else
    279 		blksize = AUDIO_TRACK_BLKSIZE;
    280 
    281 	/* Initialize buffers */
    282 	init_tio_data(NBLKS_PER_BUF*blksize);
    283 
    284 	/* Fill in all buffers before starting */
    285 	start_b = ti->ti_start_address;
    286 
    287 	/*
    288 	 * Start filling initial buffer to ensure that there is plenty of
    289 	 * data when writing begins.
    290 	 */
    291 	for (i = 0; i < NIOBS; i++) {
    292 		sz_read = h->bstr_read(h, tio_iobs[i].iob_buf,
    293 		    tio_iobs[i].iob_total_size);
    294 
    295 
    296 		/*
    297 		 * We need to read the source file into the buffer and make
    298 		 * sure that the data in the buffer is a multiple of the
    299 		 * blocksize (data or audio blocksize). iob_total_size is a
    300 		 * multiple of the blocksize so this case should only be
    301 		 * encountered at EOF or from piped input.
    302 		 */
    303 		while ((rem = (sz_read % blksize)) != 0) {
    304 			int ret;
    305 
    306 			/*
    307 			 * rem contains the amount of data past the previous
    308 			 * block boundry. we need to subtract it from the
    309 			 * blocksize to get the amount needed to reach the
    310 			 * next block boundry.
    311 			 */
    312 
    313 			if ((sz_read + (blksize - rem)) >
    314 			    tio_iobs[i].iob_total_size) {
    315 
    316 			/*
    317 			 * This should not occur, but we are trying to
    318 			 * write past the end of the buffer. return
    319 			 * with an error.
    320 			 */
    321 				sz_read = -1;
    322 				break;
    323 			}
    324 
    325 			/*
    326 			 * Try to continue reading in case the data is being
    327 			 * piped in.
    328 			 */
    329 			ret = h->bstr_read(h, &tio_iobs[i].iob_buf[sz_read],
    330 			    (blksize - rem));
    331 
    332 			if (ret < 0) {
    333 				sz_read = ret;
    334 				break;
    335 			}
    336 
    337 			/*
    338 			 * No more data. We need to make sure that we are
    339 			 * aligned with the blocksize. so pad the rest of
    340 			 * the buffer with 0s
    341 			 */
    342 
    343 			if (ret == 0) {
    344 				ret = blksize - rem;
    345 				(void) memset(&tio_iobs[i].iob_buf[sz_read],
    346 				    0, ret);
    347 			}
    348 			sz_read += ret;
    349 		}
    350 
    351 		if (sz_read < 0) {
    352 
    353 			/* reading the source failed, clean up and return */
    354 			te->err_type = TRACKIO_ERR_SYSTEM;
    355 			te->te_errno = errno;
    356 			goto write_track_failed;
    357 		}
    358 
    359 		tio_iobs[i].iob_start_blk = start_b;
    360 		tio_iobs[i].iob_nblks = (sz_read/blksize);
    361 		start_b += tio_iobs[i].iob_nblks;
    362 		tio_iobs[i].iob_data_size = sz_read;
    363 		tio_iobs[i].iob_state = IOBS_READY;
    364 		if (sz_read == 0)
    365 			break;
    366 	}
    367 
    368 	tio_fd = dev->d_fd;
    369 	tio_trackno = ti->ti_track_no;
    370 
    371 	/* Install signal handler for CTRL-C */
    372 	ohandler = signal(SIGINT, trackio_sig_handler);
    373 	if (ohandler) {
    374 		signal_handler_installed = 1;
    375 	}
    376 
    377 	/* Create thread which will issue commands to write to device */
    378 	if (thr_create(0, 0, write_to_cd, NULL,
    379 	    THR_BOUND | THR_NEW_LWP, &tio_thread) != 0) {
    380 		te->err_type = TRACKIO_ERR_SYSTEM;
    381 		te->te_errno = errno;
    382 		goto write_track_failed;
    383 	}
    384 	write_cd_thr_created = 1;
    385 
    386 	/* If caller specified a callback, create a thread to do callbacks */
    387 	if (cb != NULL) {
    388 		init_pcb_data();
    389 		pcb_cb = cb;
    390 		pcb_arg = arg;
    391 		if (thr_create(0, 0, progress_callback, NULL,
    392 		    THR_BOUND | THR_NEW_LWP, &pc_thread) != 0) {
    393 			te->err_type = TRACKIO_ERR_SYSTEM;
    394 			te->te_errno = errno;
    395 			goto write_track_failed;
    396 		}
    397 		progress_callback_thr_created = 1;
    398 	}
    399 
    400 	i = 0;
    401 	while (sz_read != 0) {
    402 		(void) mutex_lock(&tio_mutex);
    403 		while ((tio_iobs[i].iob_state != IOBS_EMPTY) &&
    404 		    (tio_errno == 0) && (pcb_user_abort == 0)) {
    405 
    406 			/* Do callbacks only if there is nothing else to do */
    407 			if (cb != NULL) {
    408 				(void) mutex_lock(&pcb_mutex);
    409 				(void) cond_broadcast(&pcb_cond);
    410 				(void) mutex_unlock(&pcb_mutex);
    411 			}
    412 
    413 			/* If user requested abort, bail out */
    414 			if (pcb_user_abort || tio_got_ctrl_c) {
    415 				break;
    416 			}
    417 			(void) cond_wait(&tio_cond, &tio_mutex);
    418 		}
    419 		if (pcb_user_abort || tio_got_ctrl_c) {
    420 			(void) mutex_unlock(&tio_mutex);
    421 			te->err_type = TRACKIO_ERR_USER_ABORT;
    422 			goto write_track_failed;
    423 		}
    424 		/*
    425 		 * We've got a transport error, stop writing, save all
    426 		 * of the error information and clean up the threads.
    427 		 */
    428 		if (tio_errno != 0) {
    429 			(void) mutex_unlock(&tio_mutex);
    430 			te->err_type = TRACKIO_ERR_TRANSPORT;
    431 			te->te_errno = tio_errno;
    432 			te->status = uscsi_status;
    433 			if (uscsi_status == 2) {
    434 				te->key = SENSE_KEY(rqbuf) & 0xf;
    435 				te->asc = ASC(rqbuf);
    436 				te->ascq = ASCQ(rqbuf);
    437 			}
    438 			goto write_track_failed;
    439 		}
    440 		pcb_completed_io_size += tio_iobs[i].iob_data_size;
    441 		tio_iobs[i].iob_state = IOBS_UNDER_FILE_IO;
    442 		(void) mutex_unlock(&tio_mutex);
    443 
    444 		sz_read = h->bstr_read(h, tio_iobs[i].iob_buf,
    445 		    tio_iobs[i].iob_total_size);
    446 
    447 		/*
    448 		 * We need to read the source file into the buffer and make
    449 		 * sure that the data in the buffer is a multiple of the
    450 		 * blocksize (data or audio blocksize). this case should only
    451 		 * be encountered at EOF or from piped input.
    452 		 */
    453 
    454 		while ((rem = (sz_read % blksize)) != 0) {
    455 			int ret;
    456 
    457 
    458 			/*
    459 			 * This should not occur, we are trying to write
    460 			 * past the end of the buffer, return error.
    461 			 */
    462 
    463 			if ((sz_read + (blksize - rem)) >
    464 			    tio_iobs[i].iob_total_size) {
    465 
    466 				sz_read = -1;
    467 				break;
    468 			}
    469 
    470 			/*
    471 			 * Try to continue reading in case the data is being
    472 			 * piped in.
    473 			 */
    474 
    475 			ret = h->bstr_read(h, &tio_iobs[i].iob_buf[sz_read],
    476 			    (blksize - rem));
    477 
    478 			if (ret < 0) {
    479 				sz_read = ret;
    480 				break;
    481 			}
    482 
    483 			/*
    484 			 * No more data. We need to make sure that we are
    485 			 * aligned with the blocksize. so pad the rest of
    486 			 * the buffer with 0s
    487 			 */
    488 
    489 			if (ret == 0) {
    490 				/*
    491 				 * rem contains the amount of data past the
    492 				 * previous block boundry. we need to subtract
    493 				 * it from the blocksize to get the amount
    494 				 * needed to reach the next block boundry.
    495 				 */
    496 				ret = blksize - rem;
    497 				(void) memset(&tio_iobs[i].iob_buf[sz_read],
    498 				    0, ret);
    499 			}
    500 			sz_read += ret;
    501 		}
    502 		if (sz_read < 0) {
    503 			te->err_type = TRACKIO_ERR_SYSTEM;
    504 			te->te_errno = errno;
    505 			goto write_track_failed;
    506 		}
    507 		(void) mutex_lock(&tio_mutex);
    508 		tio_iobs[i].iob_start_blk = start_b;
    509 		tio_iobs[i].iob_nblks = (sz_read/blksize);
    510 		start_b += tio_iobs[i].iob_nblks;
    511 		tio_iobs[i].iob_data_size = sz_read;
    512 		tio_iobs[i].iob_state = IOBS_READY;
    513 		(void) cond_broadcast(&tio_cond);
    514 		(void) mutex_unlock(&tio_mutex);
    515 		i++;
    516 		if (i == NIOBS)
    517 			i = 0;
    518 	}
    519 	(void) mutex_lock(&tio_mutex);
    520 	while ((tio_errno == 0) && (tio_done == 0)) {
    521 
    522 		/* Wait for track IO to complete */
    523 		(void) cond_wait(&tio_cond, &tio_mutex);
    524 		if (tio_errno != 0) {
    525 			te->err_type = TRACKIO_ERR_TRANSPORT;
    526 			te->te_errno = tio_errno;
    527 			te->status = uscsi_status;
    528 			if (uscsi_status == 2) {
    529 				te->key = SENSE_KEY(rqbuf) & 0xf;
    530 				te->asc = ASC(rqbuf);
    531 				te->ascq = ASCQ(rqbuf);
    532 			}
    533 			(void) mutex_unlock(&tio_mutex);
    534 			goto write_track_failed;
    535 		}
    536 		if (cb != NULL) {
    537 			while (tio_iobs[i].iob_state == IOBS_EMPTY) {
    538 				(void) mutex_lock(&pcb_mutex);
    539 				pcb_completed_io_size +=
    540 				    tio_iobs[i].iob_data_size;
    541 				(void) cond_broadcast(&pcb_cond);
    542 				(void) mutex_unlock(&pcb_mutex);
    543 				i++;
    544 				if (i == NIOBS)
    545 					i = 0;
    546 			}
    547 		}
    548 	}
    549 	(void) mutex_unlock(&tio_mutex);
    550 	retval = 1;
    551 write_track_failed:
    552 	if (progress_callback_thr_created) {
    553 		if (thr_kill(pc_thread, 0) == 0) {
    554 			(void) mutex_lock(&pcb_mutex);
    555 
    556 			pcb_done = 1;
    557 			(void) cond_broadcast(&pcb_cond);
    558 			(void) mutex_unlock(&pcb_mutex);
    559 			(void) thr_join(pc_thread, NULL, NULL);
    560 		}
    561 	}
    562 	if (write_cd_thr_created) {
    563 		if (thr_kill(tio_thread, 0) == 0) {
    564 			(void) mutex_lock(&tio_mutex);
    565 			tio_abort = 1;
    566 			(void) cond_broadcast(&tio_cond);
    567 			(void) mutex_unlock(&tio_mutex);
    568 			(void) thr_join(tio_thread, NULL, NULL);
    569 		}
    570 	}
    571 
    572 	if (signal_handler_installed) {
    573 		(void) signal(SIGINT, ohandler);
    574 	}
    575 
    576 	fini_tio_data();
    577 	fini_pcb_data();
    578 	return (retval);
    579 }
    580