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