int sg_release(struct burn_drive *d) { if (mmc_function_spy(d, "sg_release") <= 0) return 0; if (d->cam == NULL) return 0; mmc_function_spy(NULL, "sg_release ----------- closing."); sg_close_drive(d); d->released = 1; return 0; }
/* @param flag bit0= asynchronous waiting */ int sbc_start_unit_flag(struct burn_drive *d, int flag) { struct command *c; int ret; c = &(d->casual_command); if (mmc_function_spy(d, "start_unit") <= 0) return 0; scsi_init_command(c, SBC_START_UNIT, sizeof(SBC_START_UNIT)); c->retry = 1; if (d->do_no_immed && (flag & 1)) c->timeout = 1800 * 1000; else c->opcode[1] |= (flag & 1); /* ts A70918 : Immed */ c->dir = NO_TRANSFER; d->issue_command(d, c); if (c->error) return 0; if (d->do_no_immed || !(flag & 1)) return 1; /* ts A70918 : asynchronous */ ret = spc_wait_unit_attention(d, 1800, "START UNIT", 0); return ret; }
/* ts A90824 : Trying to reduce drive noise */ int sbc_stop_unit(struct burn_drive *d) { struct command *c; int ret; c = &(d->casual_command); if (mmc_function_spy(d, "stop_unit") <= 0) return 0; scsi_init_command(c, SBC_STOP_UNIT, sizeof(SBC_STOP_UNIT)); c->retry = 0; c->opcode[1] |= 1; /* Immed */ c->dir = NO_TRANSFER; d->issue_command(d, c); if (c->error) return 0; ret = spc_wait_unit_attention(d, 1800, "STOP UNIT", 0); d->is_stopped = 1; return ret; }
int sg_grab(struct burn_drive *d) { struct cam_device *cam; char path_string[80]; if (mmc_function_spy(d, "sg_grab") <= 0) return 0; if (burn_drive_is_open(d)) { d->released = 0; return 1; } cam = cam_open_device(d->devname, O_RDWR); if (cam == NULL) { libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020003, LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, "Could not grab drive", errno, 0); return 0; } d->cam = cam; if (burn_sg_open_o_excl & 63) if (sg_lock(d, 0) <= 0) return 0; fcntl(cam->fd, F_SETOWN, getpid()); cam_path_string(d->cam, path_string, sizeof(path_string)); #ifdef Libburn_ahci_verbouS fprintf(stderr, "libburn_EXPERIMENTAL: CAM path = %s\n", path_string); #endif if (strstr(path_string, ":ahcich") != NULL) d->is_ahci = 1; else d->is_ahci = -1; d->released = 0; return 1; }
void sbc_eject(struct burn_drive *d) { struct command *c; c = &(d->casual_command); if (mmc_function_spy(d, "eject") <= 0) return; scsi_init_command(c, SBC_UNLOAD, sizeof(SBC_UNLOAD)); /* c->opcode[1] |= 1; / * ts A70918 : Immed , ts B00109 : revoked */ c->page = NULL; c->dir = NO_TRANSFER; d->issue_command(d, c); /* ts A70918 : Wait long. A late eject could surprise or hurt user. ts B00109 : Asynchronous eject revoked, as one cannot reliably distinguish out from unready. if (c->error) return; spc_wait_unit_attention(d, 1800, "STOP UNIT (+ EJECT)", 0); */ }
void sbc_load(struct burn_drive *d) { struct command *c; c = &(d->casual_command); if (mmc_function_spy(d, "load") <= 0) return; scsi_init_command(c, SBC_LOAD, sizeof(SBC_LOAD)); c->retry = 1; /* ts A70921 : Had to revoke Immed because of LG GSA-4082B */ /* c->opcode[1] |= 1; / * ts A70918 : Immed */ c->dir = NO_TRANSFER; c->timeout = Libburn_mmc_load_timeouT; d->issue_command(d, c); if (c->error) return; /* ts A70923 : Needed regardless of Immed bit. Was once 1 minute, now 5 minutes for loading. If this does not suffice then other commands shall fail righteously. */ spc_wait_unit_attention(d, 300, "waiting after START UNIT (+ LOAD)",0); }
int sg_issue_command(struct burn_drive *d, struct command *c) { int done = 0, err, sense_len = 0, ret, ignore_error, i; int cam_pass_err_recover = 0, key, asc, ascq, timeout_ms; union ccb *ccb; static FILE *fp = NULL; time_t start_time; mmc_function_spy(NULL, "sg_issue_command"); c->error = 0; memset(c->sense, 0, sizeof(c->sense)); if (d->cam == NULL) return 0; if (burn_sg_log_scsi & 1) { if (fp == NULL) { fp= fopen("/tmp/libburn_sg_command_log", "a"); fprintf(fp, "\n-----------------------------------------\n"); } } if (burn_sg_log_scsi & 3) scsi_log_cmd(c,fp,0); c->error = 0; if (c->timeout > 0) timeout_ms = c->timeout; else timeout_ms = 200000; ccb = cam_getccb(d->cam); cam_fill_csio(&ccb->csio, 1, /* retries */ NULL, /* cbfncp */ CAM_DEV_QFRZDIS, /* flags */ MSG_SIMPLE_Q_TAG, /* tag_action */ NULL, /* data_ptr */ 0, /* dxfer_len */ sizeof (ccb->csio.sense_data), /* sense_len */ 0, /* cdb_len */ timeout_ms); /* timeout */ switch (c->dir) { case TO_DRIVE: ccb->csio.ccb_h.flags |= CAM_DIR_OUT; break; case FROM_DRIVE: ccb->csio.ccb_h.flags |= CAM_DIR_IN; break; case NO_TRANSFER: ccb->csio.ccb_h.flags |= CAM_DIR_NONE; break; } #ifdef Libburn_for_freebsd_ahcI /* ts B00325 : Advise by Alexander Motin */ /* Runs well on 8-STABLE (23 Mar 2003) But on 8-RELEASE cam_send_ccb() returns non-zero with errno 6 on eject. Long lasting TEST UNIT READY cycles break with errno 16. */ #ifdef Libburn_ahci_style_for_alL { #else if (d->is_ahci > 0) { #endif ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER; cam_pass_err_recover = 1; } #endif /* Libburn_for_freebsd_ahcI */ ccb->csio.cdb_len = c->oplen; memcpy(&ccb->csio.cdb_io.cdb_bytes, &c->opcode, c->oplen); if (c->page) { ccb->csio.data_ptr = c->page->data; if (c->dir == FROM_DRIVE) { /* ts A90430 : Ticket 148 , by jwehle : "On ... FreeBSD 6.4 which has a usb memory reader in addition to a ATAPI DVD burner sg_issue_command will hang while the SCSI bus is being scanned" */ if (c->dxfer_len >= 0) ccb->csio.dxfer_len = c->dxfer_len; else ccb->csio.dxfer_len = BUFFER_SIZE; /* touch page so we can use valgrind */ memset(c->page->data, 0, BUFFER_SIZE); } else { ccb->csio.dxfer_len = c->page->bytes; } } else { ccb->csio.data_ptr = NULL; ccb->csio.dxfer_len = 0; } start_time = time(NULL); for (i = 0; !done; i++) { memset(&ccb->csio.sense_data, 0, sizeof(ccb->csio.sense_data)); memset(c->sense, 0, sizeof(c->sense)); err = cam_send_ccb(d->cam, ccb); ignore_error = sense_len = 0; /* ts B00325 : CAM_AUTOSNS_VALID advised by Alexander Motin */ if (ccb->ccb_h.status & CAM_AUTOSNS_VALID) { /* ts B00110 */ /* Better curb sense_len */ sense_len = ccb->csio.sense_len; if (sense_len > (int) sizeof(c->sense)) sense_len = sizeof(c->sense); memcpy(c->sense, &ccb->csio.sense_data, sense_len); spc_decode_sense(c->sense, sense_len, &key, &asc, &ascq); if (sense_len >= 14 && cam_pass_err_recover && key) ignore_error = 1; } if (err == -1 && cam_pass_err_recover && ! ignore_error) { #ifdef Libburn_ahci_verbouS fprintf(stderr, "libburn_EXPERIMENTAL: errno = %d . cam_errbuf = '%s'\n", errno, cam_errbuf); #endif if (errno == ENXIO && c->opcode[0] != 0) { /* Operations on empty or ejected tray */ /* MEDIUM NOT PRESENT */ #ifdef Libburn_ahci_verbouS fprintf(stderr, "libburn_EXPERIMENTAL: Emulating [2,3A,00] MEDIUM NOT PRESENT\n"); #endif c->sense[0] = 0x70; /*Fixed format sense data*/ c->sense[2] = 0x02; c->sense[12] = 0x3A; c->sense[13] = 0x00; sense_len = 14; ignore_error = 1; } else if (c->opcode[0] == 0 && (errno == EBUSY || errno == ENXIO)) { /* Timeout of TEST UNIT READY loop */ /* Inquiries while tray is being loaded */ /*LOGICAL UNIT NOT READY,CAUSE NOT REPORTABLE*/ #ifdef Libburn_ahci_verbouS fprintf(stderr, "libburn_EXPERIMENTAL: Emulating [2,04,00] LOGICAL UNIT NOT READY,CAUSE NOT REPORTABLE\n"); #endif c->sense[0] = 0x70; /*Fixed format sense data*/ c->sense[2] = 0x02; c->sense[12] = 0x04; c->sense[13] = 0x00; sense_len = 14; ignore_error = 1; } else if (errno == EINVAL) { /* Inappropriate MODE SENSE */ /* INVALID FIELD IN CDB */ #ifdef Libburn_ahci_verbouS fprintf(stderr, "libburn_EXPERIMENTAL: Emulating [5,24,00] INVALID FIELD IN CDB\n"); #endif c->sense[0] = 0x70; /*Fixed format sense data*/ c->sense[2] = 0x05; c->sense[12] = 0x24; c->sense[13] = 0x00; sense_len = 14; ignore_error = 1; } } if (err == -1 && !ignore_error) { libdax_msgs_submit(libdax_messenger, d->global_index, 0x0002010c, LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, "Failed to transfer command to drive", errno, 0); sg_close_drive(d); d->released = 1; d->busy = BURN_DRIVE_IDLE; c->error = 1; {ret = -1; goto ex;} } /* XXX */ if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { if (sense_len < 14) { /*LOGICAL UNIT NOT READY,CAUSE NOT REPORTABLE*/ #ifdef Libburn_ahci_verbouS fprintf(stderr, "libburn_EXPERIMENTAL: CAM_STATUS= %d .Emulating [2,04,00] LOGICAL UNIT NOT READY,CAUSE NOT REPORTABLE\n", (ccb->ccb_h.status & CAM_STATUS_MASK)); #endif c->sense[0] = 0x70; /*Fixed format sense data*/ c->sense[2] = 0x02; c->sense[12] = 0x04; c->sense[13] = 0x00; done = 1; } /* >>> Need own duration time measurement. Then remove bit1 from flag. */ done = scsi_eval_cmd_outcome(d, c, fp, c->sense, sense_len, 0, start_time, timeout_ms, i, 2 | !!ignore_error); if (d->cancel) done = 1; } else { done = 1; } } while (!done); ret = 1; ex:; cam_freeccb(ccb); return ret; } /* ts B00115 */ /* Return 1 if the given path leads to a regular file or a device that can be seeked, read and eventually written with 2 kB granularity. */ int burn_os_is_2k_seekrw(char *path, int flag) { struct stat stbuf; #ifdef Libburn_DIOCGMEDIASIZE_ISBLK int fd, ret; off_t add_size; #else char *spt; int i, e; #endif /* ! Libburn_DIOCGMEDIASIZE_ISBLK */ if (stat(path, &stbuf) == -1) return 0; if (S_ISREG(stbuf.st_mode)) return 1; if (!S_ISCHR(stbuf.st_mode)) return 0; #ifdef Libburn_DIOCGMEDIASIZE_ISBLK /* If it throws no error with DIOCGMEDIASIZE then it is a 'block device' */ fd = open(path, O_RDONLY); if (fd == -1) return 0; ret = ioctl(fd, DIOCGMEDIASIZE, &add_size); close(fd); return (ret != -1); #else /* Libburn_DIOCGMEDIASIZE_ISBLK */ spt = strrchr(path, '/'); if (spt == NULL) spt = path; else spt++; e = strlen(spt); for (i = strlen(spt) - 1; i > 0; i--) if (spt[i] >= '0' && spt[i] <= '9') e = i; if (strncmp(spt, "da", e) == 0) /* SCSI disk. E.g. USB stick. */ return 1; if (strncmp(spt, "cd", e) == 0) /* SCSI CD drive might be writeable. */ return 1; if (strncmp(spt, "ad", e) == 0) /* IDE hard drive */ return 1; if (strncmp(spt, "acd", e) == 0) /* IDE CD drive might be writeable */ return 1; if (strncmp(spt, "fd", e) == 0) /* Floppy disk */ return 1; if (strncmp(spt, "fla", e) == 0) /* Flash drive */ return 1; return 0; #endif /* ! Libburn_DIOCGMEDIASIZE_ISBLK */ } /* ts A70909 */ /** Estimate the potential payload capacity of a file address. @param path The address of the file to be examined. If it does not exist yet, then the directory will be inquired. @param bytes This value gets modified if an estimation is possible @return -2 = cannot perform necessary operations on file object -1 = neither path nor dirname of path exist 0 = could not estimate size capacity of file object 1 = estimation has been made, bytes was set */ int burn_os_stdio_capacity(char *path, off_t *bytes) { struct stat stbuf; struct statvfs vfsbuf; char *testpath = NULL, *cpt; off_t add_size = 0; int fd, ret; BURN_ALLOC_MEM(testpath, char, 4096); testpath[0] = 0; if (stat(path, &stbuf) == -1) { strcpy(testpath, path); cpt = strrchr(testpath, '/'); if(cpt == NULL) strcpy(testpath, "."); else if(cpt == testpath) testpath[1] = 0; else *cpt = 0; if (stat(testpath, &stbuf) == -1) {ret = -1; goto ex;} #ifdef Libburn_if_this_was_linuX } else if(S_ISBLK(stbuf.st_mode)) { int open_mode = O_RDWR, fd, ret; long blocks; blocks = *bytes / 512; if(burn_sg_open_o_excl) open_mode |= O_EXCL; fd = open(path, open_mode); if (fd == -1) {ret = -2; goto ex;} ret = ioctl(fd, BLKGETSIZE, &blocks); close(fd); if (ret == -1) {ret = -2; goto ex;} *bytes = ((off_t) blocks) * (off_t) 512; #endif /* Libburn_if_this_was_linuX */ } else if(S_ISCHR(stbuf.st_mode)) { fd = open(path, O_RDONLY); if (fd == -1) {ret = -2; goto ex;} ret = ioctl(fd, DIOCGMEDIASIZE, &add_size); close(fd); if (ret == -1) {ret = -2; goto ex;} *bytes = add_size; } else if(S_ISREG(stbuf.st_mode)) { add_size = stbuf.st_blocks * (off_t) 512; strcpy(testpath, path); } else {ret = 0; goto ex;} if (testpath[0]) { if (statvfs(testpath, &vfsbuf) == -1) {ret = -2; goto ex;} *bytes = add_size + ((off_t) vfsbuf.f_frsize) * (off_t) vfsbuf.f_bavail; } ret = 1; ex: BURN_FREE_MEM(testpath); return ret; } /* ts A91122 : an interface to open(O_DIRECT) or similar OS tricks. */ #ifdef Libburn_read_o_direcT /* No special O_DIRECT-like precautions are implemented here */ #endif /* Libburn_read_o_direcT */ int burn_os_open_track_src(char *path, int open_flags, int flag) { int fd; fd = open(path, open_flags); return fd; }
/* ts A61021: The old version which mixes SCSI and operating system adapter */ static void enumerate_common(char *fname, int bus_no, int host_no, int channel_no, int target_no, int lun_no) { struct burn_drive *t; struct burn_drive out; /* ts A60923 */ out.bus_no = bus_no; out.host = host_no; out.id = target_no; out.channel = channel_no; out.lun = lun_no; out.devname = strdup(fname); out.cam = NULL; out.lock_fd = -1; out.is_ahci = 0; out.start_lba= -2000000000; out.end_lba= -2000000000; out.read_atip = mmc_read_atip; out.grab = sg_grab; out.release = sg_release; out.drive_is_open= sg_drive_is_open; out.issue_command = sg_issue_command; out.getcaps = spc_getcaps; out.released = 1; out.status = BURN_DISC_UNREADY; out.eject = sbc_eject; out.load = sbc_load; out.lock = spc_prevent; out.unlock = spc_allow; out.read_disc_info = spc_sense_write_params; out.get_erase_progress = spc_get_erase_progress; out.test_unit_ready = spc_test_unit_ready; out.probe_write_modes = spc_probe_write_modes; out.read_toc = mmc_read_toc; out.write = mmc_write; out.erase = mmc_erase; out.read_cd = mmc_read_cd; out.perform_opc = mmc_perform_opc; out.set_speed = mmc_set_speed; out.send_parameters = spc_select_error_params; out.send_write_parameters = spc_select_write_params; out.send_cue_sheet = mmc_send_cue_sheet; out.sync_cache = mmc_sync_cache; out.get_nwa = mmc_get_nwa; out.close_disc = mmc_close_disc; out.close_session = mmc_close_session; out.close_track_session = mmc_close; out.read_buffer_capacity = mmc_read_buffer_capacity; out.idata = calloc(1, sizeof(struct burn_scsi_inquiry_data)); out.idata->valid = 0; out.mdata = calloc(1, sizeof(struct scsi_mode_data)); out.mdata->valid = 0; if (out.idata == NULL || out.mdata == NULL) { libdax_msgs_submit(libdax_messenger, -1, 0x00020108, LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, "Could not allocate new drive object", 0, 0); return; } memset(&out.params, 0, sizeof(struct params)); t = burn_drive_register(&out); /* ts A60821 <<< debug: for tracing calls which might use open drive fds */ mmc_function_spy(NULL, "enumerate_common : -------- doing grab"); /* try to get the drive info */ if (t->grab(t)) { t->getcaps(t); t->unlock(t); t->released = 1; } /* ts A60821 <<< debug: for tracing calls which might use open drive fds */ mmc_function_spy(NULL, "enumerate_common : ----- would release "); }