From 13641fcbae09e4c32142ba60c28ae7e6e608a86a Mon Sep 17 00:00:00 2001 From: Thomas Schmitt Date: Wed, 15 Nov 2006 17:10:12 +0000 Subject: [PATCH] Made portability clarifications --- cdrskin/cdrskin_timestamp.h | 2 +- libburn/drive.c | 8 +- libburn/sg-freebsd-port.c | 541 ++++++++++++++++++++++++++++++++ libburn/sg-freebsd.c | 100 +----- libburn/sg-linux.c | 604 ++++++++++++++++++------------------ libburn/sg.h | 35 +-- libburn/spc.c | 106 +++++++ libburn/spc.h | 8 + 8 files changed, 990 insertions(+), 414 deletions(-) create mode 100644 libburn/sg-freebsd-port.c diff --git a/cdrskin/cdrskin_timestamp.h b/cdrskin/cdrskin_timestamp.h index b3aa5f3..4e4e9cc 100644 --- a/cdrskin/cdrskin_timestamp.h +++ b/cdrskin/cdrskin_timestamp.h @@ -1 +1 @@ -#define Cdrskin_timestamP "2006.11.15.091329" +#define Cdrskin_timestamP "2006.11.15.170927" diff --git a/libburn/drive.c b/libburn/drive.c index cf79bcf..69c2545 100644 --- a/libburn/drive.c +++ b/libburn/drive.c @@ -52,7 +52,7 @@ void burn_drive_free(struct burn_drive *d) return; /* ts A60822 : close open fds before forgetting them */ if (burn_drive_is_open(d)) - sg_close_drive(d); + d->release(d); free((void *) d->idata); free((void *) d->mdata); if(d->toc_entry != NULL) @@ -578,8 +578,10 @@ int burn_drive_scan_sync(struct burn_drive_info *drives[], #endif /* 0 */ /* refresh the lib's drives */ - sg_enumerate(); - ata_enumerate(); + + /* ts A61115 : formerly sg_enumerate(); ata_enumerate(); */ + scsi_enumerate_drives(); + count = burn_drive_count(); if (count) *drives = diff --git a/libburn/sg-freebsd-port.c b/libburn/sg-freebsd-port.c new file mode 100644 index 0000000..0562260 --- /dev/null +++ b/libburn/sg-freebsd-port.c @@ -0,0 +1,541 @@ +/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ + +/* +This is the operating system dependent part of libburn. It implements the +transport level aspects of SCSI control and command i/o. + +Present implementation: FreeBSD CAM (untested) + +PORTING: + +There are public functions, used by other parts of libburn, which have to be +implemented in a way that provides libburn with the desired services: + +sg_give_next_adr() iterates over the set of potentially useful drive + address strings. + +scsi_enumerate_drives() brings all available, not-whitelist-banned, and + accessible drives into libburn's list of drives. + +sg_drive_is_open() tells wether libburn has the given drive in use. + +sg_grab() opens the drive for SCSI commands and ensures + undisturbed access. + +sg_release() closes a drive opened by sg_grab() + +sg_issue_command() sends a SCSI command to the drive, receives reply, + and evaluates wether the command succeeded or shall + be retried or finally failed. + +sg_obtain_scsi_adr() tries to obtain SCSI address parameters. + + +Porting hints are marked by the text "PORTING:". +Send feedback to libburn-hackers@pykix.org . + +*/ + + +/** PORTING : ------- OS dependent headers and definitions ------ */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include /* XXX */ + + +/** PORTING : ------ libburn portable headers and definitions ----- */ + +#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; + + +/* is in portable part of libburn */ +int burn_drive_is_banned(char *device_address); + + + +/* ------------------------------------------------------------------------ */ +/* ts A61115: Private functions. Port only if needed by public functions */ +/* (Public functions are listed below) */ +/* ------------------------------------------------------------------------ */ + + +/* Helper function for scsi_give_next_adr() */ +static int sg_init_enumerator(burn_drive_enumerator_t *idx) +{ + idx->skip_device = 0; + + if ((idx->fd = open(XPT_DEVICE, O_RDWR)) == -1) { + warn("couldn't open %s", XPT_DEVICE); + return -1; + } + + bzero(&(idx->ccb), sizeof(union ccb)); + + idx->ccb.ccb_h.path_id = CAM_XPT_PATH_ID; + idx->ccb.ccb_h.target_id = CAM_TARGET_WILDCARD; + idx->ccb.ccb_h.target_lun = CAM_LUN_WILDCARD; + + idx->ccb.ccb_h.func_code = XPT_DEV_MATCH; + idx->bufsize = sizeof(struct dev_match_result) * 100; + idx->ccb.cdm.match_buf_len = idx->bufsize; + idx->ccb.cdm.matches = (struct dev_match_result *)malloc(idx->bufsize); + if (idx->ccb.cdm.matches == NULL) { + warnx("can't malloc memory for matches"); + close(idx->fd); + return -1; + } + idx->ccb.cdm.num_matches = 0; + idx->i = idx->ccb.cdm.num_matches; /* to trigger buffer load */ + + /* + * We fetch all nodes, since we display most of them in the default + * case, and all in the verbose case. + */ + idx->ccb.cdm.num_patterns = 0; + idx->ccb.cdm.pattern_buf_len = 0; + + return 1; +} + + +/* Helper function for scsi_give_next_adr() */ +static int sg_next_enumeration_buffer(burn_drive_enumerator_t *idx) +{ + /* + * We do the ioctl multiple times if necessary, in case there are + * more than 100 nodes in the EDT. + */ + if (ioctl(idx->fd, CAMIOCOMMAND, &(idx->ccb)) == -1) { + warn("error sending CAMIOCOMMAND ioctl"); + return -1; + } + + if ((idx->ccb.ccb_h.status != CAM_REQ_CMP) + || ((idx->ccb.cdm.status != CAM_DEV_MATCH_LAST) + && (idx->ccb.cdm.status != CAM_DEV_MATCH_MORE))) { + warnx("got CAM error %#x, CDM error %d\n", + idx->ccb.ccb_h.status, idx->ccb.cdm.status); + return -1; + } + return 1; +} + + +static int sg_close_drive(struct burn_drive * d) +{ + if (d->cam != NULL) { + cam_close_device(d->cam); + d->cam = NULL; + } + return 0; +} + + +/* ----------------------------------------------------------------------- */ +/* PORTING: Private functions which contain publicly needed functionality. */ +/* Their portable part must be performed. So it is probably best */ +/* to replace the non-portable part and to call these functions */ +/* in your port, too. */ +/* ----------------------------------------------------------------------- */ + + +/** Wraps a detected drive into libburn structures and hands it over to + libburn drive list. +*/ +static void enumerate_common(char *fname, int bus_no, int host_no, + int channel_no, int target_no, int lun_no) +{ + int ret; + struct burn_drive out; + + /* General libburn drive setup */ + burn_setup_drive(&out, fname); + + /* This transport adapter uses SCSI-family commands and models + (seems the adapter would know better than its boss, if ever) */ + ret = burn_scsi_setup_drive(&out, bus_no, host_no, channel_no, + target_no, lun_no, 0); + if (ret<=0) + return; + + /* PORTING: ------------------- non portable part --------------- */ + + /* Operating system adapter is CAM */ + /* Adapter specific handles and data */ + out.cam = NULL; + /* Adapter specific functions */ + out.grab = sg_grab; + out.release = sg_release; + out.drive_is_open = sg_drive_is_open; + out.issue_command = sg_issue_command; + + /* PORTING: ---------------- end of non portable part ------------ */ + + /* Finally register drive and inquire drive information */ + burn_drive_finish_enum(&out); +} + + +/* ts A61115 */ +/* ------------------------------------------------------------------------ */ +/* PORTING: Public functions. These MUST be ported. */ +/* ------------------------------------------------------------------------ */ + + +/** Returns the next index number and the next enumerated drive address. + The enumeration has to cover all available and accessible drives. It is + allowed to return addresses of drives which are not available but under + some (even exotic) circumstances could be available. It is on the other + hand allowed, only to hand out addresses which can really be used right + in the moment of this call. (This implementation chooses the latter.) + @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) +{ + int ret; + + if (initialize == 1) { + ret = sg_init_enumerator(idx); + if (ret<=0) + return ret; + } else if (initialize == -1) { + if(idx->fd != -1) + close(idx->fd); + idx->fd = -1; + return 0; + } + + +try_item:; /* This spaghetti loop keeps the number of tabs small */ + + /* Loop content from old scsi_enumerate_drives() */ + + while (idx->i >= idx->ccb.cdm.num_matches) { + ret = sg_next_enumeration_buffer(idx); + if (ret<=0) + return -1; + if (!((idx->ccb.ccb_h.status == CAM_REQ_CMP) + && (idx->ccb.cdm.status == CAM_DEV_MATCH_MORE)) ) + return 0; + idx->i = 0; + } + + switch (idx->ccb.cdm.matches[idx->i].type) { + case DEV_MATCH_BUS: + break; + case DEV_MATCH_DEVICE: { + struct device_match_result* result; + + result = &(idx->ccb.cdm.matches[i].result.device_result); + if (result->flags & DEV_RESULT_UNCONFIGURED) + idx->skip_device = 1; + else + idx->skip_device = 0; + break; + } + case DEV_MATCH_PERIPH: { + struct periph_match_result* result; + char buf[64]; + + result = &(idx->ccb.cdm.matches[i].result.periph_result); + if (idx->skip_device || + strcmp(result->periph_name, "pass") == 0) + break; + snprintf(buf, sizeof (buf), "/dev/%s%d", + result->periph_name, result->unit_number); + if(adr_size <= strlen(buf) + return -1; + strcpy(adr, buf); + + /* Found next enumerable address */ + return 1; + + } + default: + /* printf(stderr, "unknown match type\n"); */ + break; + } + + (idx->i)++; + goto try_item; /* Regular function exit is return 1 above */ +} + + +/** Brings all available, not-whitelist-banned, and accessible drives into + libburn's list of drives. +*/ +int scsi_enumerate_drives(void) +{ + burn_drive_enumerator_t idx; + int initialize = 1; + char buf[64]; + + while(1) { + ret = sg_give_next_adr(&idx, buf, sizeof(buf), initialize); + initialize = 0; + if (ret <= 0) + break; + if (burn_drive_is_banned(buf)) + continue; + enumerate_common(buf, idx.result->path_id, idx.result->path_id, + 0, idx.result->target_id, + idx.result->target_lun); + } + sg_give_next_adr(&idx, buf, sizeof(buf), -1); +} + + +/** Tells wether libburn has the given drive in use or exclusively reserved. + If it is "open" then libburn will eventually call sg_release() on it when + it is time to give up usage resp. reservation. +*/ +/** Published as burn_drive.drive_is_open() */ +int sg_drive_is_open(struct burn_drive * d) +{ + return (d->cam != NULL); +} + + +/** Opens the drive for SCSI commands and - if burn activities are prone + to external interference on your system - obtains an exclusive access lock + on the drive. (Note: this is not physical tray locking.) + A drive that has been opened with sg_grab() will eventually be handed + over to sg_release() for closing and unreserving. +*/ +int sg_grab(struct burn_drive *d) +{ + int count; + struct cam_device *cam; + + if(d->cam != NULL) + return 0; + + 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; + } + d->cam = cam; + fcntl(cam->fd, F_SETOWN, getpid()); + d->released = 0; + return 1; +} + + +/** PORTING: Is mainly about the call to sg_close_drive() and wether it + implements the demanded functionality. +*/ +/** Gives up the drive for SCSI commands and releases eventual access locks. + (Note: this is not physical tray locking.) +*/ +int sg_release(struct burn_drive *d) +{ + if (d->cam == NULL) { + burn_print(1, "release an ungrabbed drive. die\n"); + return 0; + } + sg_close_drive(d); + return 0; +} + + +/** Sends a SCSI command to the drive, receives reply and evaluates wether + the command succeeded or shall be retried or finally failed. + Returned SCSI errors shall not lead to a return value indicating failure. + The callers get notified by c->error. An SCSI failure which leads not to + a retry shall be notified via scsi_notify_error(). + The Libburn_log_sg_commandS facility might be of help when problems with + a drive have to be examined. It shall stay disabled for normal use. + @return: 1 success , <=0 failure +*/ +int sg_issue_command(struct burn_drive *d, struct command *c) +{ + int done = 0; + int err; + union ccb *ccb; + + 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 { + + /* ts A61115: removed a ssert() */ + if(c->page->bytes <= 0) + return 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; +} + + +/** Tries to obtain SCSI address parameters. + @return 1 is success , 0 is failure +*/ +int sg_obtain_scsi_adr(char *path, int *bus_no, int *host_no, int *channel_no, + int *target_no, int *lun_no) +{ + burn_drive_enumerator_t idx; + int initialize = 1; + char buf[64]; + struct periph_match_result* result; + + while(1) { + ret = sg_give_next_adr(&idx, buf, sizeof(buf), initialize); + initialize = 0; + if (ret <= 0) + break; + if (strcmp(adr, buf) != 0) + continue; + result = &(idx->ccb.cdm.matches[i].result.periph_result); + *bus_no = result->path_id; + *host_no = result->path_id; + *channel_no = 0; + *target_no = result->target_id + *lun_no = result->target_lun; + sg_give_next_adr(&idx, buf, sizeof(buf), -1); + return 1; + } + sg_give_next_adr(&idx, buf, sizeof(buf), -1); + return (0); +} + + +/** Tells wether a text is a persistent address as listed by the enumeration + functions. +*/ +int sg_is_enumerable_adr(char* adr) +{ + burn_drive_enumerator_t idx; + int initialize = 1; + char buf[64]; + + while(1) { + ret = sg_give_next_adr(&idx, buf, sizeof(buf), initialize); + initialize = 0; + if (ret <= 0) + break; + if (strcmp(adr, buf) == 0) { + sg_give_next_adr(&idx, buf, sizeof(buf), -1); + return 1; + } + } + sg_give_next_adr(&idx, buf, sizeof(buf), -1); + return (0); +} + diff --git a/libburn/sg-freebsd.c b/libburn/sg-freebsd.c index c2d92ef..8f5978d 100644 --- a/libburn/sg-freebsd.c +++ b/libburn/sg-freebsd.c @@ -1,19 +1,17 @@ /* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ -/* >>> ts A61021 : for testing the new arrangement of code - please outcomment these defines : */ -/* Keeps alive old enumerate_common(). New version delegates much work +/* Revives old enumerate_common(). New version delegates much work to methods in drive, mmc, spc, and sbc . */ #define Scsi_freebsd_make_own_enumeratE 1 -/* Keeps alive old sg_enumerate(). New version delegates most work to +/* Revives old scsi_enumerate_drives(). New version delegates most work to sg_give_next_adr(). */ -#define Scsi_freebsd_old_sg_enumeratE 1 +#define Scsi_freebsd_old_scsi_enumeratE 1 #include @@ -59,7 +57,7 @@ int burn_drive_is_banned(char *device_address); int mmc_function_spy(char * text); -#ifdef Scsi_freebsd_old_sg_enumeratE +#ifdef Scsi_freebsd_old_scsi_enumeratE int sg_give_next_adr(burn_drive_enumerator_t *idx, char adr[], int adr_size, int initialize) @@ -78,10 +76,11 @@ int sg_obtain_scsi_adr(char *path, int *bus_no, int *host_no, int *channel_no, return (0); } -#else /* Scsi_freebsd_old_sg_enumeratE */ +#else /* Scsi_freebsd_old_scsi_enumeratE */ -/* ts A61021 : Moved most code from sg_enumerate under sg_give_next_adr() */ -/* Some helper functions for sg_give_next_adr() */ +/* ts A61021 : Moved most code from scsi_enumerate_drives under + sg_give_next_adr() */ +/* Some helper functions for scsi_give_next_adr() */ static int sg_init_enumerator(burn_drive_enumerator_t *idx) { @@ -172,7 +171,7 @@ int sg_give_next_adr(burn_drive_enumerator_t *idx, try_item:; /* This spaghetti loop keeps the number of tabs small */ - /* Loop content from old sg_enumerate() */ + /* Loop content from old scsi_enumerate_drives() */ while (idx->i >= idx->ccb.cdm.num_matches) { ret = sg_next_enumeration_buffer(idx); @@ -277,7 +276,7 @@ int sg_obtain_scsi_adr(char *path, int *bus_no, int *host_no, int *channel_no, return (0); } -#endif /* ! Scsi_freebsd_old_sg_enumeratE */ +#endif /* ! Scsi_freebsd_old_scsi_enumeratE */ int sg_close_drive(struct burn_drive * d) @@ -294,20 +293,10 @@ int sg_drive_is_open(struct burn_drive * d) return (d->cam != NULL); } - -void ata_enumerate(void) -{ - /* ts A61021: Only a dummy function is needed in FreeBSD */ - /* The difference between sg and ata should be encapsulated - in sg-linux.c */ - ; -} - - -void sg_enumerate(void) +int scsi_enumerate_drives(void) { -#ifdef Scsi_freebsd_old_sg_enumeratE +#ifdef Scsi_freebsd_old_scsi_enumeratE union ccb ccb; int bufsize, fd; @@ -404,7 +393,7 @@ void sg_enumerate(void) close(fd); -#else /* Scsi_freebsd_old_sg_enumeratE */ +#else /* Scsi_freebsd_old_scsi_enumeratE */ burn_drive_enumerator_t idx; int initialize = 1; @@ -423,7 +412,7 @@ void sg_enumerate(void) } sg_give_next_adr(&idx, buf, sizeof(buf), -1); -#endif /* ! Scsi_freebsd_old_sg_enumeratE */ +#endif /* ! Scsi_freebsd_old_scsi_enumeratE */ } @@ -706,64 +695,3 @@ int sg_issue_command(struct burn_drive *d, struct command *c) 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; -} - diff --git a/libburn/sg-linux.c b/libburn/sg-linux.c index 0f2c8fb..f4fa327 100644 --- a/libburn/sg-linux.c +++ b/libburn/sg-linux.c @@ -1,7 +1,45 @@ /* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ -/* ts A61010 */ -/* #include */ +/* +This is the operating system dependent part of libburn. It implements the +transport level aspects of SCSI control and command i/o. + +Present implementation: Linux SCSI Generic (sg) + +PORTING: + +There are public functions, used by other parts of libburn, which have to be +implemented in a way that provides libburn with the desired services: + +sg_give_next_adr() iterates over the set of potentially useful drive + address strings. + +scsi_enumerate_drives() brings all available, not-whitelist-banned, and + accessible drives into libburn's list of drives. + +sg_drive_is_open() tells wether libburn has the given drive in use. + +sg_grab() opens the drive for SCSI commands and ensures + undisturbed access. + +sg_release() closes a drive opened by sg_grab() + +sg_issue_command() sends a SCSI command to the drive, receives reply, + and evaluates wether the command succeeded or shall + be retried or finally failed. + +sg_obtain_scsi_adr() tries to obtain SCSI address parameters. + + +Porting hints are marked by the text "PORTING:". +Send feedback to libburn-hackers@pykix.org . + +Hint: You should also look into sg-freebsd-port.c, which is a younger and + in some aspects more straightforward implementation of this interface. +*/ + + +/** PORTING : ------- OS dependent headers and definitions ------ */ #include #include @@ -10,9 +48,6 @@ #include #include #include - -/* #include ts A61013 : not in Linux man 3 malloc */ - #include #include #include @@ -20,6 +55,9 @@ #include #include + +/** PORTING : ------ libburn portable headers and definitions ----- */ + #include "transport.h" #include "drive.h" #include "sg.h" @@ -30,20 +68,24 @@ #include "toc.h" #include "util.h" -/* kludge! glibc headers don't define all the SCSI stuff that we use! */ -#ifndef SG_GET_ACCESS_COUNT -# define SG_GET_ACCESS_COUNT 0x2289 -#endif - #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); + +/* ------------------------------------------------------------------------ */ +/* PORTING: Private definitions. Port only if needed by public functions. */ +/* (Public functions are listed below) */ +/* ------------------------------------------------------------------------ */ + + +static void enumerate_common(char *fname, int bus_no, int host_no, + int channel_no, int target_no, int lun_no); + + +/* >>> ts A61115 : this needs mending. A Linux aspect shows up in cdrskin. */ /* ts A60813 : storage objects are in libburn/init.c wether to use O_EXCL wether to use O_NOBLOCK with open(2) on devices @@ -58,6 +100,12 @@ extern int burn_sg_open_abort_busy; int mmc_function_spy(char * text); +/* ------------------------------------------------------------------------ */ +/* PORTING: Private functions. Port only if needed by public functions */ +/* (Public functions are listed below) */ +/* ------------------------------------------------------------------------ */ + + static int sgio_test(int fd) { unsigned char test_ops[] = { 0, 0, 0, 0, 0, 0 }; @@ -73,45 +121,8 @@ static int sgio_test(int fd) } -/* ts A60925 : ticket 74 */ -int sg_close_drive_fd(char *fname, int driveno, int *fd, int sorry) -{ - int ret, os_errno, sevno= LIBDAX_MSGS_SEV_DEBUG; - char msg[4096+100]; - - if(*fd < 0) - return(0); - ret = close(*fd); - *fd = -1337; - if(ret != -1) - return 1; - os_errno= errno; - - if (fname != NULL) - sprintf(msg, "Encountered error when closing drive '%s'", - fname); - else - sprintf(msg, "Encountered error when closing drive"); - - if (sorry) - sevno = LIBDAX_MSGS_SEV_SORRY; - libdax_msgs_submit(libdax_messenger, driveno, 0x00020002, - sevno, LIBDAX_MSGS_PRIO_HIGH, msg, os_errno, 0); - return 0; -} - -int sg_drive_is_open(struct burn_drive * d) -{ - /* a bit more detailed case distinction than needed */ - if (d->fd == -1337) - return 0; - if (d->fd < 0) - return 0; - return 1; -} - /* ts A60924 */ -int sg_handle_busy_device(char *fname, int os_errno) +static int sg_handle_busy_device(char *fname, int os_errno) { char msg[4096]; @@ -135,87 +146,8 @@ int sg_handle_busy_device(char *fname, int os_errno) } -/* ts A60922 ticket 33 */ -/** 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) -{ - /* sg.h : typedef int burn_drive_enumerator_t; */ - static int sg_limit = 32, ata_limit = 26; - int baseno = 0; - - if (initialize == -1) - return 0; - - if (initialize == 1) - *idx = -1; - (*idx)++; - if (*idx >= sg_limit) - goto next_ata; - if (adr_size < 10) - return -1; - sprintf(adr, "/dev/sg%d", *idx); - return 1; -next_ata:; - baseno += sg_limit; - if (*idx - baseno >= ata_limit) - goto next_nothing; - if (adr_size < 9) - return -1; - sprintf(adr, "/dev/hd%c", 'a' + (*idx - baseno)); - return 1; -next_nothing:; - baseno += ata_limit; - return 0; -} - -int sg_is_enumerable_adr(char *adr) -{ - char fname[4096]; - int i, ret = 0, first = 1; - - while (1) { - ret= sg_give_next_adr(&i, fname, sizeof(fname), first); - if(ret <= 0) - break; - first = 0; - if (strcmp(adr, fname) == 0) - return 1; - - } - return(0); -} - - /* ts A60926 */ -int sg_release_siblings(int sibling_fds[], int *sibling_count) -{ - int i; - char msg[81]; - - for(i= 0; i < *sibling_count; i++) - sg_close_drive_fd(NULL, -1, &(sibling_fds[i]), 0); - if(*sibling_count > 0) { - sprintf(msg, "Closed %d O_EXCL scsi siblings", *sibling_count); - libdax_msgs_submit(libdax_messenger, -1, 0x00020007, - LIBDAX_MSGS_SEV_NOTE, LIBDAX_MSGS_PRIO_HIGH, msg, 0,0); - } - *sibling_count = 0; - return 1; -} - - -/* ts A60926 */ -int sg_open_drive_fd(char *fname, int scan_mode) +static int sg_open_drive_fd(char *fname, int scan_mode) { int open_mode = O_RDWR, fd; char msg[81]; @@ -266,8 +198,67 @@ int sg_open_drive_fd(char *fname, int scan_mode) } +/* ts A60925 : ticket 74 */ +static int sg_close_drive_fd(char *fname, int driveno, int *fd, int sorry) +{ + int ret, os_errno, sevno= LIBDAX_MSGS_SEV_DEBUG; + char msg[4096+100]; + + if(*fd < 0) + return(0); + ret = close(*fd); + *fd = -1337; + if(ret != -1) + return 1; + os_errno= errno; + + if (fname != NULL) + sprintf(msg, "Encountered error when closing drive '%s'", + fname); + else + sprintf(msg, "Encountered error when closing drive"); + + if (sorry) + sevno = LIBDAX_MSGS_SEV_SORRY; + libdax_msgs_submit(libdax_messenger, driveno, 0x00020002, + sevno, LIBDAX_MSGS_PRIO_HIGH, msg, os_errno, 0); + return 0; +} + + /* ts A60926 */ -int sg_open_scsi_siblings(char *path, int driveno, +static int sg_release_siblings(int sibling_fds[], int *sibling_count) +{ + int i; + char msg[81]; + + for(i= 0; i < *sibling_count; i++) + sg_close_drive_fd(NULL, -1, &(sibling_fds[i]), 0); + if(*sibling_count > 0) { + sprintf(msg, "Closed %d O_EXCL scsi siblings", *sibling_count); + libdax_msgs_submit(libdax_messenger, -1, 0x00020007, + LIBDAX_MSGS_SEV_NOTE, LIBDAX_MSGS_PRIO_HIGH, msg, 0,0); + } + *sibling_count = 0; + return 1; +} + + +/* ts A60926 */ +static int sg_close_drive(struct burn_drive *d) +{ + int ret; + + if (!burn_drive_is_open(d)) + return 0; + sg_release_siblings(d->sibling_fds, &(d->sibling_count)); + ret = sg_close_drive_fd(d->devname, d->global_index, &(d->fd), 0); + return ret; +} + + +/* ts A60926 */ +static int sg_open_scsi_siblings(char *path, int driveno, int sibling_fds[], int *sibling_count, int host_no, int channel_no, int id_no, int lun_no) { @@ -324,19 +315,9 @@ failed:; } -/* ts A60926 */ -int sg_close_drive(struct burn_drive *d) -{ - int ret; - - if (!burn_drive_is_open(d)) - return 0; - sg_release_siblings(d->sibling_fds, &(d->sibling_count)); - ret = sg_close_drive_fd(d->devname, d->global_index, &(d->fd), 0); - return ret; -} - -void ata_enumerate(void) +/** Speciality of Linux: detect non-SCSI ATAPI (EIDE) which will from + then on used used via generic SCSI as is done with (emulated) SCSI drives */ +static void ata_enumerate(void) { struct hd_driveid tm; int i, fd; @@ -372,7 +353,9 @@ void ata_enumerate(void) } } -void sg_enumerate(void) + +/** Detects (probably emulated) SCSI drives */ +static void sg_enumerate(void) { struct sg_scsi_id sid; int i, fd, sibling_fds[LIBBURN_SG_MAX_SIBLINGS], sibling_count= 0, ret; @@ -428,6 +411,19 @@ void sg_enumerate(void) } } + +/* ts A61115 */ +/* ----------------------------------------------------------------------- */ +/* PORTING: Private functions which contain publicly needed functionality. */ +/* Their portable part must be performed. So it is probably best */ +/* to replace the non-portable part and to call these functions */ +/* in your port, too. */ +/* ----------------------------------------------------------------------- */ + + +/** Wraps a detected drive into libburn structures and hands it over to + libburn drive list. +*/ /* ts A60923 - A61005 : introduced new SCSI parameters */ /* ts A61021 : moved non os-specific code to spc,sbc,mmc,drive */ static void enumerate_common(char *fname, int bus_no, int host_no, @@ -446,6 +442,8 @@ static void enumerate_common(char *fname, int bus_no, int host_no, if (ret<=0) return; + /* PORTING: ------------------- non portable part --------------- */ + /* Operating system adapter is Linux Generic SCSI (sg) */ /* Adapter specific handles and data */ out.fd = -1337; @@ -458,21 +456,122 @@ static void enumerate_common(char *fname, int bus_no, int host_no, out.drive_is_open= sg_drive_is_open; out.issue_command = sg_issue_command; + /* PORTING: ---------------- end of non portable part ------------ */ + /* Finally register drive and inquire drive information */ burn_drive_finish_enum(&out); } -/* - 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. - ts A60813: this test is too late. O_EXCL is the stronger solution. - After all the test was disabled already in icculus.org/burn CVS. +/* ts A61115 */ +/* ------------------------------------------------------------------------ */ +/* PORTING: Public functions. These MUST be ported. */ +/* ------------------------------------------------------------------------ */ + + +/** PORTING: + In this Linux implementation, this function mirrors the enumeration + done in sg_enumerate and ata_enumerate(). It would be better to base those + functions on this sg_give_next_adr() but the situation is not inviting. +*/ +/* ts A60922 ticket 33 : called from drive.c */ +/** Returns the next index number and the next enumerated drive address. + The enumeration has to cover all available and accessible drives. It is + allowed to return addresses of drives which are not available but under + some (even exotic) circumstances could be available. It is on the other + hand allowed, only to hand out addresses which can really be used right + in the moment of this call. (This implementation chooses the former.) + @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) +{ + /* sg.h : typedef int burn_drive_enumerator_t; */ + static int sg_limit = 32, ata_limit = 26; + int baseno = 0; + + if (initialize == -1) + return 0; + + if (initialize == 1) + *idx = -1; + (*idx)++; + if (*idx >= sg_limit) + goto next_ata; + if (adr_size < 10) + return -1; + sprintf(adr, "/dev/sg%d", *idx); + return 1; +next_ata:; + baseno += sg_limit; + if (*idx - baseno >= ata_limit) + goto next_nothing; + if (adr_size < 9) + return -1; + sprintf(adr, "/dev/hd%c", 'a' + (*idx - baseno)); + return 1; +next_nothing:; + baseno += ata_limit; + return 0; +} + + +/** Brings all available, not-whitelist-banned, and accessible drives into + libburn's list of drives. +*/ +/** PORTING: + If not stricken with an incompletely unified situation like in Linux + one would rather implement this by a loop calling sg_give_next_adr(). + If needed with your sg_give_next_adr() results, do a test for existence + and accessability. If burn activities are prone to external interference + on your system it is also necessary to obtain exclusive access locks on + the drives. + Hand over each accepted drive to enumerate_common() resp. its replacement + within your port. + + See FreeBSD port sketch sg-freebsd-port.c for such an implementation. +*/ +/* ts A61115: replacing call to sg-implementation internals from drive.c */ +int scsi_enumerate_drives(void) +{ + sg_enumerate(); + ata_enumerate(); + return 1; +} + + +/** Tells wether libburn has the given drive in use or exclusively reserved. + If it is "open" then libburn will eventually call sg_release() on it when + it is time to give up usage resp. reservation. +*/ +/** Published as burn_drive.drive_is_open() */ +int sg_drive_is_open(struct burn_drive * d) +{ + /* a bit more detailed case distinction than needed */ + if (d->fd == -1337) + return 0; + if (d->fd < 0) + return 0; + return 1; +} + + +/** Opens the drive for SCSI commands and - if burn activities are prone + to external interference on your system - obtains an exclusive access lock + on the drive. (Note: this is not physical tray locking.) + A drive that has been opened with sg_grab() will eventually be handed + over to sg_release() for closing and unreserving. */ int sg_grab(struct burn_drive *d) { - int fd, count, os_errno= 0, ret; + int fd, os_errno= 0, ret; /* ts A60813 */ int open_mode = O_RDWR; @@ -495,12 +594,11 @@ int sg_grab(struct burn_drive *d) if(burn_sg_open_o_nonblock) open_mode |= O_NONBLOCK; - /* ts A60813 + /* ts A60813 - A60822 After enumeration the drive fd is probably still open. -1337 is the initial value of burn_drive.fd and the value after relase of drive. Unclear why not the official error return value -1 of open(2) war used. */ - /* ts A60822: was if(d->fd == -1337) { */ if(! burn_drive_is_open(d)) { /* ts A60821 @@ -523,49 +621,34 @@ int sg_grab(struct burn_drive *d) } else fd= d->fd; - /* ts A61007 : this is redundant */ - /* a ssert(fd != -1337); */ - if (fd >= 0) { - - /* ts A60814: - according to my experiments this test would work now ! */ - - /* ts A60926 : this was disabled */ - /* Tests with growisofs on kernel 2.4.21 yielded that this - does not help against blocking on busy drives. - */ -/* <<< the old dummy */ -/* er = ioctl(fd, SG_GET_ACCESS_COUNT, &count);*/ - count = 1; - - if (1 == count) { - d->fd = fd; - fcntl(fd, F_SETOWN, getpid()); - d->released = 0; - return 1; - } - -drive_is_in_use:; - libdax_msgs_submit(libdax_messenger, d->global_index, - 0x00020003, - LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, - "Could not grab drive - already in use", 0, 0); - sg_close_drive(d); - d->fd = -1337; - return 0; + d->fd = fd; + fcntl(fd, F_SETOWN, getpid()); + d->released = 0; + return 1; } libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020003, LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, "Could not grab drive", os_errno, 0); return 0; + +drive_is_in_use:; + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x00020003, + LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, + "Could not grab drive - already in use", 0, 0); + sg_close_drive(d); + d->fd = -1337; + 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?) -*/ +/** PORTING: Is mainly about the call to sg_close_drive() and wether it + implements the demanded functionality. +*/ +/** Gives up the drive for SCSI commands and releases eventual access locks. + (Note: this is not physical tray locking.) +*/ int sg_release(struct burn_drive *d) { /* ts A60821 @@ -586,6 +669,15 @@ int sg_release(struct burn_drive *d) } +/** Sends a SCSI command to the drive, receives reply and evaluates wether + the command succeeded or shall be retried or finally failed. + Returned SCSI errors shall not lead to a return value indicating failure. + The callers get notified by c->error. An SCSI failure which leads not to + a retry shall be notified via scsi_notify_error(). + The Libburn_log_sg_commandS facility might be of help when problems with + a drive have to be examined. It shall stay disabled for normal use. + @return: 1 success , <=0 failure +*/ int sg_issue_command(struct burn_drive *d, struct command *c) { int done = 0, no_c_page = 0; @@ -596,7 +688,7 @@ int sg_issue_command(struct burn_drive *d, struct command *c) */ #ifdef Libburn_log_sg_commandS - /* <<< ts A61030 */ + /* ts A61030 */ static FILE *fp= NULL; static int fpcount= 0; int i; @@ -610,7 +702,7 @@ int sg_issue_command(struct burn_drive *d, struct command *c) mmc_function_spy(buf); #ifdef Libburn_log_sg_commandS - /* <<< ts A61030 */ + /* ts A61030 */ if(fp==NULL) { fp= fopen("/tmp/libburn_sg_command_log","a"); fprintf(fp,"\n-----------------------------------------\n"); @@ -717,7 +809,6 @@ int sg_issue_command(struct burn_drive *d, struct command *c) /* ts A61106 */ ex:; if (c->error) { - /* >>> to become d->notify_error() */ scsi_notify_error(d, c, s.sbp, s.sb_len_wr, 0); #ifdef Libburn_log_sg_commandS @@ -733,111 +824,8 @@ ex:; } -/* ts A61030 - A61109 */ -/* @param flag bit0=do report conditions which are considered not an error */ -int scsi_notify_error(struct burn_drive *d, struct command *c, - unsigned char *sense, int senselen, int flag) -{ - int key= -1, asc= -1, ascq= -1, ret; - char msg[160]; - - if (d->silent_on_scsi_error) - return 1; - - if (senselen > 2) - key = sense[2]; - if (senselen > 13) { - asc = sense[12]; - ascq = sense[13]; - } - - if(!(flag & 1)) { - /* SPC : TEST UNIT READY command */ - if (c->opcode[0] == 0) - return 1; - /* MMC : READ DISC INFORMATION command */ - if (c->opcode[0] == 0x51) - if (key == 0x2 && asc == 0x3A && - ascq>=0 && ascq <= 0x02) /* MEDIUM NOT PRESENT */ - return 1; - } - - sprintf(msg,"SCSI error condition on command %2.2Xh :", c->opcode[0]); - if (key>=0) - sprintf(msg+strlen(msg), " key=%Xh", key); - if (asc>=0) - sprintf(msg+strlen(msg), " asc=%2.2Xh", asc); - if (ascq>=0) - sprintf(msg+strlen(msg), " ascq=%2.2Xh", ascq); - ret = libdax_msgs_submit(libdax_messenger, d->global_index, 0x0002010f, - LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH, msg,0,0); - return ret; -} - - -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; -} - /* ts A60922 */ -/** Try to obtain SCSI address parameters. +/** Tries to obtain SCSI address parameters. @return 1 is success , 0 is failure */ int sg_obtain_scsi_adr(char *path, int *bus_no, int *host_no, int *channel_no, @@ -882,3 +870,27 @@ int sg_obtain_scsi_adr(char *path, int *bus_no, int *host_no, int *channel_no, #endif return 1; } + + +/* ts A60922 ticket 33 : called from drive.c */ +/** Tells wether a text is a persistent address as listed by the enumeration + functions. +*/ +int sg_is_enumerable_adr(char *adr) +{ + char fname[4096]; + int i, ret = 0, first = 1; + + while (1) { + ret= sg_give_next_adr(&i, fname, sizeof(fname), first); + if(ret <= 0) + break; + first = 0; + if (strcmp(adr, fname) == 0) + return 1; + + } + return(0); +} + + diff --git a/libburn/sg.h b/libburn/sg.h index 1501d65..890a08e 100644 --- a/libburn/sg.h +++ b/libburn/sg.h @@ -5,20 +5,14 @@ #ifdef __FreeBSD__ -/* >>> To hold all state information of BSD device enumeration - which are now local in sg_enumerate() . So that sg_give_next_adr() - can work in BSD and sg_enumerate() can use it. */ +/* To hold all state information of BSD device enumeration + which are now local in sg_enumerate() . So that sg_give_next_adr() + can work in BSD and sg_enumerate() can use it. */ struct burn_drive_enumeration_state { - -#ifdef Scsi_freebsd_old_sg_enumeratE - int dummy; -#else union ccb ccb; int bufsize, fd; unsigned int i; int skip_device; -#endif /* ! Scsi_freebsd_old_sg_enumeratE */ - }; typedef struct burn_drive_enumeration_state burn_drive_enumerator_t; @@ -37,11 +31,6 @@ typedef int burn_drive_enumerator_t; struct burn_drive; struct command; -enum response -{ RETRY, FAIL }; - -/* ts A60925 : ticket 74 */ -int sg_close_drive_fd(char *fname, int driveno, int *fd, int sorry); /* ts A60922 ticket 33 */ int sg_give_next_adr(burn_drive_enumerator_t *enm_context, @@ -50,23 +39,13 @@ int sg_is_enumerable_adr(char *adr); int sg_obtain_scsi_adr(char *path, int *bus_no, int *host_no, int *channel_no, int *target_no, int *lun_no); -/* ts A60926 : ticket 33 ++ */ -int sg_open_scsi_siblings(char *fname, int driveno, - int sibling_fds[], int *sibling_count, - int host_no, int channel_no, int id_no, int lun_no); -int sg_release_siblings(int sibling_fds[], int *sibling_count); -int sg_close_drive(struct burn_drive *d); - -void sg_enumerate(void); -void ata_enumerate(void); int sg_grab(struct burn_drive *); int sg_release(struct burn_drive *); int sg_issue_command(struct burn_drive *, struct command *); -enum response scsi_error(struct burn_drive *, unsigned char *, int); -/* ts A61030 */ -/* @param flag bit0=do also report TEST UNIT READY failures */ -int scsi_notify_error(struct burn_drive *, struct command *c, - unsigned char *sense, int senselen, int flag); +/* ts A61115 : formerly sg_enumerate();ata_enumerate() */ +int scsi_enumerate_drives(void); + +int sg_drive_is_open(struct burn_drive * d); #endif /* __SG */ diff --git a/libburn/spc.c b/libburn/spc.c index aa761ed..0757cf9 100644 --- a/libburn/spc.c +++ b/libburn/spc.c @@ -523,3 +523,109 @@ int burn_scsi_setup_drive(struct burn_drive *d, int bus_no, int host_no, } return 1; } + + +/* ts A61115 moved from sg-*.c */ +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; +} + + +/* ts A61030 - A61115 */ +/* @param flag bit0=do report conditions which are considered not an error */ +int scsi_notify_error(struct burn_drive *d, struct command *c, + unsigned char *sense, int senselen, int flag) +{ + int key= -1, asc= -1, ascq= -1, ret; + char msg[160]; + + if (d->silent_on_scsi_error) + return 1; + + if (senselen > 2) + key = sense[2]; + if (senselen > 13) { + asc = sense[12]; + ascq = sense[13]; + } + + if(!(flag & 1)) { + /* SPC : TEST UNIT READY command */ + if (c->opcode[0] == 0) + return 1; + /* MMC : READ DISC INFORMATION command */ + if (c->opcode[0] == 0x51) + if (key == 0x2 && asc == 0x3A && + ascq>=0 && ascq <= 0x02) /* MEDIUM NOT PRESENT */ + return 1; + } + + sprintf(msg,"SCSI error condition on command %2.2Xh :", c->opcode[0]); + if (key>=0) + sprintf(msg+strlen(msg), " key=%Xh", key); + if (asc>=0) + sprintf(msg+strlen(msg), " asc=%2.2Xh", asc); + if (ascq>=0) + sprintf(msg+strlen(msg), " ascq=%2.2Xh", ascq); + ret = libdax_msgs_submit(libdax_messenger, d->global_index, 0x0002010f, + LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH, msg,0,0); + return ret; +} + diff --git a/libburn/spc.h b/libburn/spc.h index d19254a..3b441d0 100644 --- a/libburn/spc.h +++ b/libburn/spc.h @@ -33,5 +33,13 @@ int spc_setup_drive(struct burn_drive *d); int burn_scsi_setup_drive(struct burn_drive *d, int bus_no, int host_no, int channel_no, int target_no, int lun_no, int flag); +/* ts A61115 moved from sg-*.h */ +enum response { RETRY, FAIL }; +enum response scsi_error(struct burn_drive *, unsigned char *, int); + +/* ts A61030 */ +/* @param flag bit0=do report conditions which are considered not an error */ +int scsi_notify_error(struct burn_drive *, struct command *c, + unsigned char *sense, int senselen, int flag); #endif /*__SPC*/