/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* XXX */ #include "transport.h" #include "drive.h" #include "sg.h" #include "spc.h" #include "mmc.h" #include "sbc.h" #include "debug.h" #include "toc.h" #include "util.h" #include "libdax_msgs.h" extern struct libdax_msgs *libdax_messenger; static void enumerate_common(char *fname, int bus_no, int host_no, int channel_no, int target_no, int lun_no); /* ts A51221 */ int burn_drive_is_banned(char *device_address); /* ts A60813 : storage objects are in libburn/init.c wether to use O_EXCL wether to use O_NOBLOCK with open(2) on devices wether to take O_EXCL rejection as fatal error */ extern int burn_sg_open_o_excl; extern int burn_sg_open_o_nonblock; extern int burn_sg_open_abort_busy; /* ts A60821 <<< debug: for tracing calls which might use open drive fds */ int mmc_function_spy(char * text); /** Returns the next index number and the next enumerated drive address. @param idx An opaque handle. Make no own theories about it. @param adr Takes the reply @param adr_size Gives maximum size of reply including final 0 @param initialize 1 = start new, 0 = continue, use no other values for now -1 = finish @return 1 = reply is a valid address , 0 = no further address available -1 = severe error (e.g. adr_size too small) */ int sg_give_next_adr(burn_drive_enumerator_t *idx, char adr[], int adr_size, int initialize) { return (0); } int sg_is_enumerable_adr(char* adr) { return (0); } int sg_obtain_scsi_adr(char *path, int *bus_no, int *host_no, int *channel_no, int *target_no, int *lun_no) { return (0); } int sg_close_drive(struct burn_drive * d) { if (d->cam != NULL) { cam_close_device(d->cam); d->cam = NULL; } return 0; } void ata_enumerate(void) { /* Do not supported */ } void sg_enumerate(void) { union ccb ccb; int bufsize, fd; unsigned int i; int skip_device = 0; if ((fd = open(XPT_DEVICE, O_RDWR)) == -1) { warn("couldn't open %s", XPT_DEVICE); return; } bzero(&ccb, sizeof(union ccb)); ccb.ccb_h.path_id = CAM_XPT_PATH_ID; ccb.ccb_h.target_id = CAM_TARGET_WILDCARD; ccb.ccb_h.target_lun = CAM_LUN_WILDCARD; ccb.ccb_h.func_code = XPT_DEV_MATCH; bufsize = sizeof(struct dev_match_result) * 100; ccb.cdm.match_buf_len = bufsize; ccb.cdm.matches = (struct dev_match_result *)malloc(bufsize); if (ccb.cdm.matches == NULL) { warnx("can't malloc memory for matches"); close(fd); return; } ccb.cdm.num_matches = 0; /* * We fetch all nodes, since we display most of them in the default * case, and all in the verbose case. */ ccb.cdm.num_patterns = 0; ccb.cdm.pattern_buf_len = 0; /* * We do the ioctl multiple times if necessary, in case there are * more than 100 nodes in the EDT. */ do { if (ioctl(fd, CAMIOCOMMAND, &ccb) == -1) { warn("error sending CAMIOCOMMAND ioctl"); break; } if ((ccb.ccb_h.status != CAM_REQ_CMP) || ((ccb.cdm.status != CAM_DEV_MATCH_LAST) && (ccb.cdm.status != CAM_DEV_MATCH_MORE))) { warnx("got CAM error %#x, CDM error %d\n", ccb.ccb_h.status, ccb.cdm.status); break; } for (i = 0; i < ccb.cdm.num_matches; i++) { switch (ccb.cdm.matches[i].type) { case DEV_MATCH_BUS: break; case DEV_MATCH_DEVICE: { struct device_match_result* result; result = &ccb.cdm.matches[i].result.device_result; if (result->flags & DEV_RESULT_UNCONFIGURED) skip_device = 1; else skip_device = 0; break; } case DEV_MATCH_PERIPH: { struct periph_match_result* result; char buf[64]; result = &ccb.cdm.matches[i].result.periph_result; if (skip_device || strcmp(result->periph_name, "pass") == 0) break; snprintf(buf, sizeof (buf), "/dev/%s%d", result->periph_name, result->unit_number); /* ts A51221 */ if (burn_drive_is_banned(buf)) break; enumerate_common(buf, result->path_id, result->path_id, 0, result->target_id, result->target_lun); break; } default: fprintf(stdout, "unknown match type\n"); break; } } } while ((ccb.ccb_h.status == CAM_REQ_CMP) && (ccb.cdm.status == CAM_DEV_MATCH_MORE)); close(fd); } 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 = burn_strdup(fname); out.cam = NULL; out.start_lba= -2000000000; out.end_lba= -2000000000; out.read_atip = mmc_read_atip; out.grab = sg_grab; out.release = sg_release; 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_sectors = mmc_read_sectors; 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.idata = malloc(sizeof(struct burn_scsi_inquiry_data)); out.idata->valid = 0; out.mdata = malloc(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("enumerate_common : -------- doing grab"); /* try to get the drive info */ if (t->grab(t)) { burn_print(2, "getting drive info\n"); t->getcaps(t); t->unlock(t); t->released = 1; } else { burn_print(2, "unable to grab new located drive\n"); } /* ts A60821 <<< debug: for tracing calls which might use open drive fds */ mmc_function_spy("enumerate_common : ----- would release "); } /* we use the sg reference count to decide whether we can use the drive or not. if refcount is not one, drive is open somewhere else. */ int sg_grab(struct burn_drive *d) { int count; struct cam_device *cam; mmc_function_spy("sg_grab"); assert(d->cam == NULL); 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", 0/*os_errno*/, 0); return 0; } /* er = ioctl(fd, SG_GET_ACCESS_COUNT, &count);*/ count = 1; if (1 == count) { d->cam = cam; fcntl(cam->fd, F_SETOWN, getpid()); d->released = 0; return 1; } burn_print(1, "could not acquire drive - already open\n"); sg_close_drive(d); return 0; } /* non zero return means you still have the drive and it's not in a state to be released? (is that even possible?) */ int sg_release(struct burn_drive *d) { mmc_function_spy("sg_release"); if (d->cam == NULL) { burn_print(1, "release an ungrabbed drive. die\n"); return 0; } mmc_function_spy("sg_release ----------- closing."); sg_close_drive(d); return 0; } int sg_issue_command(struct burn_drive *d, struct command *c) { int done = 0; int err; union ccb *ccb; char buf[161]; snprintf(buf, sizeof (buf), "sg_issue_command d->cam=%p d->released=%d", (void*)d->cam, d->released); mmc_function_spy(buf); if (d->cam == NULL) { c->error = 0; return 0; } c->error = 0; 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 */ 30*1000); /* 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; } ccb->csio.cdb_len = c->oplen; memcpy(&ccb->csio.cdb_io.cdb_bytes, &c->opcode, c->oplen); memset(&ccb->csio.sense_data, 0, sizeof (ccb->csio.sense_data)); if (c->page) { ccb->csio.data_ptr = c->page->data; if (c->dir == FROM_DRIVE) { ccb->csio.dxfer_len = BUFFER_SIZE; /* touch page so we can use valgrind */ memset(c->page->data, 0, BUFFER_SIZE); } else { assert(c->page->bytes > 0); ccb->csio.dxfer_len = c->page->bytes; } } else { ccb->csio.data_ptr = NULL; ccb->csio.dxfer_len = 0; } do { err = cam_send_ccb(d->cam, ccb); if (err == -1) { 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); cam_freeccb(ccb); sg_close_drive(d); d->released = 1; d->busy = BURN_DRIVE_IDLE; c->error = 1; return -1; } /* XXX */ memcpy(c->sense, &ccb->csio.sense_data, ccb->csio.sense_len); if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { if (!c->retry) { c->error = 1; cam_freeccb(ccb); return 1; } switch (scsi_error(d, c->sense, 0)) { case RETRY: done = 0; break; case FAIL: done = 1; c->error = 1; break; } } else { done = 1; } } while (!done); cam_freeccb(ccb); return 1; } enum response scsi_error(struct burn_drive *d, unsigned char *sense, int senselen) { int key, asc, ascq; senselen = senselen; key = sense[2]; asc = sense[12]; ascq = sense[13]; burn_print(12, "CONDITION: 0x%x 0x%x 0x%x on %s %s\n", key, asc, ascq, d->idata->vendor, d->idata->product); switch (asc) { case 0: burn_print(12, "NO ERROR!\n"); return RETRY; case 2: burn_print(1, "not ready\n"); return RETRY; case 4: burn_print(1, "logical unit is in the process of becoming ready\n"); return RETRY; case 0x20: if (key == 5) burn_print(1, "bad opcode\n"); return FAIL; case 0x21: burn_print(1, "invalid address or something\n"); return FAIL; case 0x24: if (key == 5) burn_print(1, "invalid field in cdb\n"); else break; return FAIL; case 0x26: if (key == 5) burn_print( 1, "invalid field in parameter list\n" ); return FAIL; case 0x28: if (key == 6) burn_print(1, "Not ready to ready change, medium may have changed\n"); else break; return RETRY; case 0x3A: burn_print(12, "Medium not present in %s %s\n", d->idata->vendor, d->idata->product); d->status = BURN_DISC_EMPTY; return FAIL; } burn_print(1, "unknown failure\n"); burn_print(1, "key:0x%x, asc:0x%x, ascq:0x%x\n", key, asc, ascq); return FAIL; }