diff --git a/cdrskin/cdrskin_timestamp.h b/cdrskin/cdrskin_timestamp.h index fc449a6..941a1ae 100644 --- a/cdrskin/cdrskin_timestamp.h +++ b/cdrskin/cdrskin_timestamp.h @@ -1 +1 @@ -#define Cdrskin_timestamP "2006.10.21.103653" +#define Cdrskin_timestamP "2006.10.21.185102" diff --git a/libburn/drive.c b/libburn/drive.c index e42dfb0..0d1f674 100644 --- a/libburn/drive.c +++ b/libburn/drive.c @@ -33,6 +33,16 @@ extern struct libdax_msgs *libdax_messenger; static struct burn_drive drive_array[255]; static int drivetop = -1; +/* ts A61021 : the unspecific part of sg.c:enumerate_common() +*/ +int burn_setup_drive(struct burn_drive *d, char *fname) +{ + d->devname = burn_strdup(fname); + memset(&d->params, 0, sizeof(struct params)); + d->released = 1; + d->status = BURN_DISC_UNREADY; + return 1; +} /* ts A60904 : ticket 62, contribution by elmom */ /* splitting former burn_drive_free() (which freed all, into two calls) */ @@ -65,21 +75,8 @@ void burn_drive_free_all(void) /* ts A60822 */ int burn_drive_is_open(struct burn_drive *d) { -#if defined(__FreeBSD__) - - if (d->cam == NULL) - return 0; - -#else /* __FreeBSD__ */ - - /* a bit more detailed case distinction than needed */ - if (d->fd == -1337) - return 0; - if (d->fd < 0) - return 0; - -#endif /* ! __FreeBSD__ */ - return 1; + /* ts A61021 : moved decision to sg.c */ + return d->drive_is_open(d); } @@ -280,6 +277,39 @@ int burn_drive_unregister(struct burn_drive *d) } +/* ts A61021 : after-setup activities from sg.c:enumerate_common() +*/ +struct burn_drive *burn_drive_finish_enum(struct burn_drive *d) +{ + struct burn_drive *t; + /* ts A60821 + <<< debug: for tracing calls which might use open drive fds */ + int mmc_function_spy(char * text); + + t = burn_drive_register(d); + + /* ts A60821 */ + 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"); + burn_drive_unregister(t); + t = NULL; + } + + /* ts A60821 */ + mmc_function_spy("enumerate_common : ----- would release "); + + return t; +} + + void burn_drive_release(struct burn_drive *d, int le) { if (d->released) { diff --git a/libburn/drive.h b/libburn/drive.h index 96d77e5..f511966 100644 --- a/libburn/drive.h +++ b/libburn/drive.h @@ -61,4 +61,13 @@ int burn_drive_is_occupied(struct burn_drive *d); int burn_drive_forget(struct burn_drive *d, int force); int burn_drive_convert_fs_adr_sub(char *path, char adr[], int *rec_count); +/* ts A61021 : the unspecific part of sg.c:enumerate_common() +*/ +int burn_setup_drive(struct burn_drive *d, char *fname); + +/* ts A61021 : after-setup activities from sg.c:enumerate_common() +*/ +struct burn_drive *burn_drive_finish_enum(struct burn_drive *d); + + #endif /* __DRIVE */ diff --git a/libburn/mmc.c b/libburn/mmc.c index 80d5c82..f430322 100644 --- a/libburn/mmc.c +++ b/libburn/mmc.c @@ -757,3 +757,27 @@ void mmc_sync_cache(struct burn_drive *d) c.dir = NO_TRANSFER; d->issue_command(d, &c); } + +/* ts A61021 : the mmc specific part of sg.c:enumerate_common() +*/ +int mmc_setup_drive(struct burn_drive *d) +{ + d->read_atip = mmc_read_atip; + d->read_toc = mmc_read_toc; + d->write = mmc_write; + d->erase = mmc_erase; + d->read_sectors = mmc_read_sectors; + d->perform_opc = mmc_perform_opc; + d->set_speed = mmc_set_speed; + d->send_cue_sheet = mmc_send_cue_sheet; + d->sync_cache = mmc_sync_cache; + d->get_nwa = mmc_get_nwa; + d->close_disc = mmc_close_disc; + d->close_session = mmc_close_session; + + /* ts A61020 */ + d->start_lba= -2000000000; + d->end_lba= -2000000000; + + return 1; +} diff --git a/libburn/mmc.h b/libburn/mmc.h index 6a0044e..74999eb 100644 --- a/libburn/mmc.h +++ b/libburn/mmc.h @@ -39,4 +39,9 @@ void mmc_perform_opc(struct burn_drive *); void mmc_get_configuration(struct burn_drive *); int mmc_get_nwa(struct burn_drive *); void mmc_send_cue_sheet(struct burn_drive *, struct cue_sheet *); + +/* ts A61021 : the mmc specific part of sg.c:enumerate_common() +*/ +int mmc_setup_drive(struct burn_drive *d); + #endif /*__MMC*/ diff --git a/libburn/sbc.c b/libburn/sbc.c index 68df3ac..4be928d 100644 --- a/libburn/sbc.c +++ b/libburn/sbc.c @@ -36,3 +36,13 @@ void sbc_eject(struct burn_drive *d) c.dir = NO_TRANSFER; d->issue_command(d, &c); } + +/* ts A61021 : the sbc specific part of sg.c:enumerate_common() +*/ +int sbc_setup_drive(struct burn_drive *d) +{ + d->eject = sbc_eject; + d->load = sbc_load; + return 1; +} + diff --git a/libburn/sbc.h b/libburn/sbc.h index 3ca028c..7fc82e3 100644 --- a/libburn/sbc.h +++ b/libburn/sbc.h @@ -8,4 +8,8 @@ struct burn_drive; void sbc_load(struct burn_drive *); void sbc_eject(struct burn_drive *); +/* ts A61021 : the sbc specific part of sg.c:enumerate_common() +*/ +int sbc_setup_drive(struct burn_drive *d); + #endif /* __SBC */ diff --git a/libburn/sg-freebsd.c b/libburn/sg-freebsd.c index f353fba..74a5f85 100644 --- a/libburn/sg-freebsd.c +++ b/libburn/sg-freebsd.c @@ -1,5 +1,21 @@ /* -*- 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 + 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 + sg_give_next_adr(). +*/ +#define Scsi_freebsd_old_sg_enumeratE 1 + + #include #include #include @@ -37,33 +53,17 @@ static void enumerate_common(char *fname, int bus_no, int host_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) -*/ + +#ifdef Scsi_freebsd_old_sg_enumeratE + int sg_give_next_adr(burn_drive_enumerator_t *idx, char adr[], int adr_size, int initialize) { - return (0); } @@ -78,6 +78,208 @@ int sg_obtain_scsi_adr(char *path, int *bus_no, int *host_no, int *channel_no, return (0); } +#else /* Scsi_freebsd_old_sg_enumeratE */ + +/* ts A61021 : Moved most code from sg_enumerate under sg_give_next_adr() */ +/* Some helper functions for sg_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; +} + + +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; +} + + +/** 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) +{ + 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 sg_enumerate() */ + + 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 */ +} + + +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); +} + + +/** Try 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); +} + +#endif /* ! Scsi_freebsd_old_sg_enumeratE */ + + int sg_close_drive(struct burn_drive * d) { if (d->cam != NULL) { @@ -87,13 +289,26 @@ int sg_close_drive(struct burn_drive * d) return 0; } +int sg_drive_is_open(struct burn_drive * d) +{ + return (d->cam != NULL); +} + + void ata_enumerate(void) { - /* Do not supported */ + /* 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) { + +#ifdef Scsi_freebsd_old_sg_enumeratE + union ccb ccb; int bufsize, fd; unsigned int i; @@ -188,8 +403,35 @@ void sg_enumerate(void) && (ccb.cdm.status == CAM_DEV_MATCH_MORE)); close(fd); + +#else /* Scsi_freebsd_old_sg_enumeratE */ + + 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); + +#endif /* ! Scsi_freebsd_old_sg_enumeratE */ + } + +#ifdef Scsi_freebsd_make_own_enumeratE + +/* 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) { @@ -212,6 +454,7 @@ static void enumerate_common(char *fname, int bus_no, int host_no, 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; @@ -271,7 +514,41 @@ static void enumerate_common(char *fname, int bus_no, int host_no, } -/* +#else /* Scsi_freebsd_make_own_enumeratE */ + +/* The new, more concise version of enumerate_common */ +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; + + /* 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; + + /* Finally register drive and inquire drive information */ + burn_drive_finish_enum(&out); +} + +#endif /* ! Scsi_freebsd_make_own_enumeratE */ + +/* ts A61021: do not believe this: 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. @@ -305,6 +582,7 @@ int sg_grab(struct burn_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?) diff --git a/libburn/sg-linux.c b/libburn/sg-linux.c index 336454f..a5a760c 100644 --- a/libburn/sg-linux.c +++ b/libburn/sg-linux.c @@ -100,6 +100,15 @@ int sg_close_drive_fd(char *fname, int driveno, int *fd, int sorry) 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) @@ -420,94 +429,37 @@ void sg_enumerate(void) } /* 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, int channel_no, int target_no, int lun_no) { - int i; - struct burn_drive *t; + int ret, i; 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; + /* General libburn drive setup */ + burn_setup_drive(&out, fname); - out.devname = burn_strdup(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; + + /* Operating system adapter is Linux Generic SCSI (sg) */ + /* Adapter specific handles and data */ out.fd = -1337; out.sibling_count = 0; for(i= 0; ivalid = 0; - out.mdata = malloc(sizeof(struct scsi_mode_data)); - out.mdata->valid = 0; - - /* ts A61007 : obsolete Assert in drive_getcaps() */ - 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 (sg_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"); - burn_drive_unregister(t); - } - -/* ts A60821 - <<< debug: for tracing calls which might use open drive fds */ - mmc_function_spy("enumerate_common : ----- would release "); + /* Finally register drive and inquire drive information */ + burn_drive_finish_enum(&out); } /* diff --git a/libburn/sg.h b/libburn/sg.h index ec9cf72..7563a34 100644 --- a/libburn/sg.h +++ b/libburn/sg.h @@ -9,7 +9,16 @@ 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; diff --git a/libburn/spc.c b/libburn/spc.c index 9b9b0bf..5142bac 100644 --- a/libburn/spc.c +++ b/libburn/spc.c @@ -23,6 +23,10 @@ #include "debug.h" #include "options.h" +#include "libdax_msgs.h" +extern struct libdax_msgs *libdax_messenger; + + /* spc command set */ static unsigned char SPC_INQUIRY[] = { 0x12, 0, 0, 0, 255, 0 }; @@ -433,3 +437,61 @@ int spc_block_type(enum burn_block_types b) /* ts A61007 : already prevented in burn_write_opts_set_write_type() */ /* a ssert(0); */; } + +/* ts A61021 : the spc specific part of sg.c:enumerate_common() +*/ +int spc_setup_drive(struct burn_drive *d) +{ + d->getcaps = spc_getcaps; + d->lock = spc_prevent; + d->unlock = spc_allow; + d->read_disc_info = spc_sense_write_params; + d->get_erase_progress = spc_get_erase_progress; + d->test_unit_ready = spc_test_unit_ready; + d->probe_write_modes = spc_probe_write_modes; + d->send_parameters = spc_select_error_params; + d->send_write_parameters = spc_select_write_params; + return 1; +} + +/* ts A61021 : the general SCSI specific part of sg.c:enumerate_common() + @param flag Bitfiled for control purposes + bit0= do not setup spc/sbc/mmc +*/ +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) +{ + int ret; + + /* ts A60923 */ + d->bus_no = bus_no; + d->host = host_no; + d->id = target_no; + d->channel = channel_no; + d->lun = lun_no; + + d->idata = malloc(sizeof(struct burn_scsi_inquiry_data)); + d->idata->valid = 0; + d->mdata = malloc(sizeof(struct scsi_mode_data)); + d->mdata->valid = 0; + + /* ts A61007 : obsolete Assert in drive_getcaps() */ + if(d->idata == NULL || d->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 -1; + } + if(!(flag & 1)) { + ret = spc_setup_drive(d); + if (ret<=0) + return ret; + ret = sbc_setup_drive(d); + if (ret<=0) + return ret; + ret = mmc_setup_drive(d); + if (ret<=0) + return ret; + } + return 1; +} diff --git a/libburn/spc.h b/libburn/spc.h index 2bca4de..d19254a 100644 --- a/libburn/spc.h +++ b/libburn/spc.h @@ -22,4 +22,16 @@ int spc_block_type(enum burn_block_types b); int spc_get_erase_progress(struct burn_drive *d); int spc_test_unit_ready(struct burn_drive *d); +/* ts A61021 : the spc specific part of sg.c:enumerate_common() +*/ +int spc_setup_drive(struct burn_drive *d); + +/* ts A61021 : the general SCSI specific part of sg.c:enumerate_common() + @param flag Bitfield for control purposes + bit0= do not setup spc/sbc/mmc +*/ +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); + + #endif /*__SPC*/ diff --git a/libburn/transport.h b/libburn/transport.h index 42839b5..2e78861 100644 --- a/libburn/transport.h +++ b/libburn/transport.h @@ -145,6 +145,10 @@ struct burn_drive /* transport functions */ int (*grab) (struct burn_drive *); int (*release) (struct burn_drive *); + + /* ts A61021 */ + int (*drive_is_open) (struct burn_drive *); + int (*issue_command) (struct burn_drive *, struct command *); /* lower level functions */