From 1d570af0b5f4702ff1fbae13b6a83feab705ba1d Mon Sep 17 00:00:00 2001 From: Mario Danic Date: Thu, 12 Oct 2006 17:38:32 +0000 Subject: [PATCH] Implemented freebsd transport layer --- libburn/sg-freebsd.c | 430 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 430 insertions(+) create mode 100644 libburn/sg-freebsd.c diff --git a/libburn/sg-freebsd.c b/libburn/sg-freebsd.c new file mode 100644 index 0000000..cb6620e --- /dev/null +++ b/libburn/sg-freebsd.c @@ -0,0 +1,430 @@ +/* -*- 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" + +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); + +int sg_give_next_adr(int *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.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; + 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; + + cam = cam_open_device(d->devname, O_RDWR); + if (cam == NULL) + 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) +{ +printf("%s\n", __func__); + if (d->cam == NULL) { + burn_print(1, "release an ungrabbed drive. die\n"); + return 0; + } + 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; + + 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); + assert(err != -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; +}