"Fossies" - the Fresh Open Source Software Archive

Member "xorriso-1.5.4/libburn/options.c" (30 Jan 2021, 16651 Bytes) of package /linux/misc/xorriso-1.5.4.pl02.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "options.c" see the Fossies "Dox" file reference documentation and the last Fossies "Diffs" side-by-side code changes report: 1.5.2_vs_1.5.4.

    1 
    2 /* Copyright (c) 2004 - 2006 Derek Foreman, Ben Jansens
    3    Copyright (c) 2006 - 2017 Thomas Schmitt <scdbackup@gmx.net>
    4    Provided under GPL version 2 or later.
    5 */
    6 
    7 #ifdef HAVE_CONFIG_H
    8 #include "../config.h"
    9 #endif
   10 
   11 #include "libburn.h"
   12 #include "options.h"
   13 #include "drive.h"
   14 #include "transport.h"
   15 #include "init.h"
   16 #include "write.h"
   17 
   18 /* ts A61007 */
   19 /* #include <a ssert.h> */
   20 
   21 #include <stdlib.h>
   22 #include <string.h>
   23 #include <stdio.h>
   24 
   25 #include "libdax_msgs.h"
   26 extern struct libdax_msgs *libdax_messenger;
   27 
   28 
   29 struct burn_write_opts *burn_write_opts_new(struct burn_drive *drive)
   30 {
   31     struct burn_write_opts *opts;
   32 
   33     opts = calloc(1, sizeof(struct burn_write_opts));
   34     if (opts == NULL) {
   35         libdax_msgs_submit(libdax_messenger, -1, 0x00020111,
   36             LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH,
   37             "Could not allocate new auxiliary object", 0, 0);
   38         return NULL;
   39     }
   40     opts->drive = drive;
   41     opts->refcount = 1;
   42     opts->write_type = BURN_WRITE_TAO;
   43     opts->block_type = BURN_BLOCK_MODE1;
   44     opts->toc_entry = NULL;
   45     opts->toc_entries = 0;
   46     opts->simulate = 0;
   47     opts->underrun_proof = drive->mdata->p2a_valid > 0 &&
   48                            drive->mdata->underrun_proof;
   49     opts->perform_opc = 1;
   50     opts->obs = -1;
   51 
   52 #ifdef Libburn_dvd_always_obs_paD
   53     opts->obs_pad = 1;
   54 #else
   55     opts->obs_pad = 0;
   56 #endif
   57 
   58     opts->start_byte = -1;
   59     opts->fill_up_media = 0;
   60     opts->force_is_set = 0;
   61     opts->do_stream_recording = 0;
   62     opts->dvd_obs_override = 0;
   63     opts->stdio_fsync_size = Libburn_stdio_fsync_limiT;
   64     opts->text_packs = NULL;
   65     opts->num_text_packs = 0;
   66     opts->no_text_pack_crc_check = 0;
   67     opts->has_mediacatalog = 0;
   68     opts->format = BURN_CDROM;
   69     opts->multi = 0;
   70     opts->control = 0;
   71     return opts;
   72 }
   73 
   74 void burn_write_opts_free(struct burn_write_opts *opts)
   75 {
   76     if (--opts->refcount > 0)
   77         return;
   78     if (opts->text_packs != NULL)
   79         free(opts->text_packs);
   80     free(opts);
   81 }
   82 
   83 int burn_write_opts_clone(struct burn_write_opts *from,
   84                           struct burn_write_opts **to, int flag)
   85 {
   86     if (*to != NULL)
   87         burn_write_opts_free(*to);
   88     if (from == NULL)
   89         return 1;
   90     *to = calloc(1, sizeof(struct burn_write_opts));
   91     if (*to == NULL) {
   92 out_of_mem:;
   93         libdax_msgs_submit(libdax_messenger, -1, 0x00000003,
   94                 LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH,
   95                 "Out of virtual memory", 0, 0);
   96         return -1;
   97     }
   98     memcpy(*to, from, sizeof(struct burn_write_opts));
   99     (*to)->text_packs = NULL;
  100     (*to)->num_text_packs = 0;
  101     if (from->text_packs != NULL && from->num_text_packs > 0) {
  102         (*to)->text_packs = calloc(1, from->num_text_packs * 18);
  103         if ((*to)->text_packs == NULL)
  104             goto out_of_mem;
  105         memcpy((*to)->text_packs, from->text_packs,
  106                from->num_text_packs * 18);
  107     }
  108     (*to)->refcount= 1;
  109     return 1;
  110 }
  111 
  112 struct burn_read_opts *burn_read_opts_new(struct burn_drive *drive)
  113 {
  114     struct burn_read_opts *opts;
  115 
  116     opts = calloc(1, sizeof(struct burn_read_opts));
  117     opts->drive = drive;
  118     opts->refcount = 1;
  119     opts->raw = 0;
  120     opts->c2errors = 0;
  121     opts->subcodes_audio = 0;
  122     opts->subcodes_data = 0;
  123     opts->hardware_error_recovery = 0;
  124     opts->report_recovered_errors = 0;
  125     opts->transfer_damaged_blocks = 0;
  126     opts->hardware_error_retries = 3;
  127     opts->dap_bit = 0;
  128 
  129     return opts;
  130 }
  131 
  132 void burn_read_opts_free(struct burn_read_opts *opts)
  133 {
  134     if (--opts->refcount <= 0)
  135         free(opts);
  136 }
  137 
  138 int burn_write_opts_set_write_type(struct burn_write_opts *opts,
  139                    enum burn_write_types write_type,
  140                    int block_type)
  141 {
  142     int sector_get_outmode(enum burn_write_types write_type,
  143                 enum burn_block_types block_type);
  144     int spc_block_type(enum burn_block_types b);
  145     
  146     /* ts A61007 */
  147     if (! ( (write_type == BURN_WRITE_SAO && block_type == BURN_BLOCK_SAO)
  148          || (opts->drive->block_types[write_type] & block_type) ) ) {
  149 bad_combination:;
  150         libdax_msgs_submit(libdax_messenger, -1, 0x00020112,
  151             LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH,
  152             "Bad combination of write_type and block_type", 0, 0);
  153         return 0;
  154     }
  155     /*  ts A61007 : obsoleting Assert in sector.c:get_outmode() */
  156     if (sector_get_outmode(write_type, (enum burn_block_types) block_type)
  157          == -1)
  158         goto bad_combination;
  159     /*  ts A61007 : obsoleting Assert in spc.c:spc_block_type() */
  160     if (spc_block_type((enum burn_block_types) block_type) == -1)
  161         goto bad_combination;
  162 
  163     opts->write_type = write_type;
  164     opts->block_type = block_type;
  165     return 1;
  166 
  167     /* a ssert(0); */
  168 }
  169 
  170 void burn_write_opts_set_toc_entries(struct burn_write_opts *opts, int count,
  171                      struct burn_toc_entry *toc_entries)
  172 {
  173     opts->toc_entries = count;
  174     opts->toc_entry = calloc(count, sizeof(struct burn_toc_entry));
  175     memcpy(opts->toc_entry, &toc_entries,
  176            sizeof(struct burn_toc_entry) * count);
  177 }
  178 
  179 void burn_write_opts_set_format(struct burn_write_opts *opts, int format)
  180 {
  181     opts->format = format;
  182 }
  183 
  184 int burn_write_opts_set_simulate(struct burn_write_opts *opts, int sim)
  185 {
  186     opts->simulate = !!sim;
  187     return 1;
  188 }
  189 
  190 int burn_write_opts_set_underrun_proof(struct burn_write_opts *opts,
  191                        int underrun_proof)
  192 {
  193     if (opts->drive->mdata->p2a_valid <= 0 ||
  194         opts->drive->mdata->underrun_proof) {
  195         opts->underrun_proof = underrun_proof;
  196         return 1;
  197     }
  198     return 0;
  199 }
  200 
  201 void burn_write_opts_set_perform_opc(struct burn_write_opts *opts, int opc)
  202 {
  203     opts->perform_opc = opc;
  204 }
  205 
  206 void burn_write_opts_set_has_mediacatalog(struct burn_write_opts *opts,
  207                       int has_mediacatalog)
  208 {
  209     opts->has_mediacatalog = has_mediacatalog;
  210 }
  211 
  212 void burn_write_opts_set_mediacatalog(struct burn_write_opts *opts,
  213                       unsigned char mediacatalog[13])
  214 {
  215     memcpy(opts->mediacatalog, mediacatalog, 13);
  216 }
  217 
  218 
  219 /* ts A61106 */
  220 void burn_write_opts_set_multi(struct burn_write_opts *opts, int multi)
  221 {
  222     opts->multi = !!multi;
  223 }
  224 
  225 
  226 /* ts B31024 */
  227 /* API */
  228 void burn_write_opts_set_fail21h_sev(struct burn_write_opts *opts,
  229                                      char *severity)
  230 {
  231     int ret, sevno;
  232 
  233     ret = libdax_msgs__text_to_sev(severity, &sevno, 0);
  234     if (ret <= 0)
  235         opts->feat21h_fail_sev = 0;
  236     else
  237         opts->feat21h_fail_sev = sevno;
  238 }
  239 
  240 
  241 /* ts B11204 */
  242 /* @param flag bit0=do not verify checksums
  243                bit1= repair mismatching checksums
  244                bit2= repair checksums if they are 00 00 with each pack
  245 */
  246 int burn_write_opts_set_leadin_text(struct burn_write_opts *opts,
  247                                     unsigned char *text_packs,
  248                                     int num_packs, int flag)
  249 {
  250     int ret;
  251     unsigned char *pack_buffer = NULL;
  252 
  253     if (num_packs > Libburn_leadin_cdtext_packs_maX ) {
  254         libdax_msgs_submit(libdax_messenger, opts->drive->global_index,
  255                 0x0002018b,
  256                 LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH,
  257                 "Too many CD-TEXT packs", 0, 0);
  258         ret= 0; goto ex;
  259     }
  260 
  261     if (num_packs > 0)
  262         BURN_ALLOC_MEM(pack_buffer, unsigned char, num_packs * 18);
  263     
  264     if (opts->text_packs != NULL) {
  265         free(opts->text_packs);
  266         opts->text_packs = NULL;
  267     }
  268 
  269     if (flag & 1) {
  270         opts->no_text_pack_crc_check = 1;
  271     } else {
  272         opts->no_text_pack_crc_check = 0;
  273         ret = burn_cdtext_crc_mismatches(text_packs, num_packs,
  274                             (flag >> 1) & 3);
  275         if (ret > 0) {
  276             libdax_msgs_submit(libdax_messenger, -1, 0x0002018f,
  277                 LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH,
  278                 "CD-TEXT pack CRC mismatch", 0, 0);
  279             ret = 0; goto ex;
  280         } else if (ret < 0) {
  281             libdax_msgs_submit(libdax_messenger, -1, 0x00020190,
  282                 LIBDAX_MSGS_SEV_WARNING, LIBDAX_MSGS_PRIO_HIGH,
  283                 "CD-TEXT pack CRC mismatch had to be corrected",
  284                 0, 0);
  285         }
  286     }
  287 
  288     if (num_packs > 0) {
  289         memcpy(pack_buffer, text_packs, num_packs * 18);
  290         opts->text_packs = pack_buffer;
  291         pack_buffer = NULL;
  292     }
  293     opts->num_text_packs = num_packs;
  294     ret = 1;
  295 ex:;
  296     BURN_FREE_MEM(pack_buffer);
  297     return ret;
  298 }
  299 
  300 
  301 /* ts A61222 */
  302 void burn_write_opts_set_start_byte(struct burn_write_opts *opts, off_t value)
  303 {
  304     opts->start_byte = value;
  305 }
  306 
  307 
  308 /* ts A70207 API */
  309 /** @param flag Bitfield for control purposes:
  310                 bit0= do not choose type but check the one that is already set
  311                 bit1= do not issue error messages via burn_msgs queue
  312 */
  313 enum burn_write_types burn_write_opts_auto_write_type(
  314         struct burn_write_opts *opts, struct burn_disc *disc,
  315         char reasons[BURN_REASONS_LEN], int flag)
  316 {
  317     struct burn_multi_caps *caps = NULL;
  318     struct burn_drive *d = opts->drive;
  319     struct burn_disc_mode_demands demands;
  320     enum burn_write_types wt;
  321     int ret, would_do_sao = 0;
  322     char *reason_pt;
  323 
  324     reasons[0] = 0;
  325 
  326     if (burn_drive_get_bd_r_pow(d)) {
  327         strcat(reasons,
  328                "MEDIA: unsuitable BD-R Pseudo Overwrite formatting, ");
  329         return BURN_WRITE_NONE;
  330     }
  331     if (d->status != BURN_DISC_BLANK &&
  332         d->status != BURN_DISC_APPENDABLE){
  333         if (d->status == BURN_DISC_FULL)
  334             strcat(reasons, "MEDIA: closed or not recordable, ");
  335         else
  336             strcat(reasons,"MEDIA: no writeable media detected, ");
  337         if (!(flag & 3))
  338             libdax_msgs_submit(libdax_messenger, d->global_index,
  339                 0x0002013a,
  340                 LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH,
  341                 "No suitable media detected", 0, 0);
  342         return BURN_WRITE_NONE;
  343     }
  344     ret = burn_disc_get_write_mode_demands(disc, opts, &demands,
  345                         !!opts->fill_up_media);
  346     if (ret <= 0) {
  347         strcat(reasons, "cannot recognize job demands, ");
  348         {wt = BURN_WRITE_NONE; goto ex;}
  349     }
  350     if (demands.exotic_track && !d->current_is_cd_profile) {
  351         if (demands.audio)
  352             strcat(reasons, "audio track prohibited by non-CD, ");
  353         else
  354             strcat(reasons, "exotic track prohibited by non-CD, ");
  355         {wt = BURN_WRITE_NONE; goto ex;}
  356     }
  357     if ((flag & 1) && opts->write_type != BURN_WRITE_SAO)
  358         goto try_tao;
  359     reason_pt = reasons + strlen(reasons);
  360     strcat(reasons, "SAO: ");
  361     if (d->status != BURN_DISC_BLANK) {
  362         strcat(reasons, "write type SAO works only on blank media, ");
  363         goto try_tao;
  364     }
  365     burn_disc_free_multi_caps(&caps);
  366     ret = burn_disc_get_multi_caps(d, BURN_WRITE_SAO, &caps, 0);
  367     if (ret < 0) {
  368 no_caps:;
  369         strcat(reasons, "cannot inquire write mode capabilities, ");
  370         {wt = BURN_WRITE_NONE; goto ex;}
  371     } else if (ret == 0) {
  372         strcat(reasons, "no SAO offered by drive and media, ");
  373         goto no_sao;
  374     }
  375     if ((opts->multi || demands.multi_session) &&
  376         !caps->multi_session)
  377         strcat(reasons, "multi session capability lacking, ");
  378     if (demands.will_append)
  379         strcat(reasons, "appended session capability lacking, ");
  380     if (demands.multi_track && !caps->multi_track)
  381         strcat(reasons, "multi track capability lacking, ");
  382     if (demands.unknown_track_size == 1 &&
  383         (caps->might_do_sao == 1 || caps->might_do_sao == 3))
  384         strcat(reasons, "track size unpredictable, ");
  385     if (demands.mixed_mode)
  386         strcat(reasons, "tracks of different modes mixed, ");
  387     if (demands.exotic_track && !d->current_is_cd_profile)
  388         strcat(reasons, "non-data track on non-cd, ");
  389     else if (d->current_is_cd_profile)
  390         if ((d->block_types[BURN_WRITE_TAO] & demands.block_types) !=
  391             demands.block_types)
  392             strcat(reasons, "drive dislikes block type, ");
  393     if (d->current_is_cd_profile && opts->fill_up_media)
  394         strcat(reasons, "cd sao cannot do media fill up yet, ");
  395     if (strcmp(reason_pt, "SAO: ") != 0)
  396         goto no_sao;
  397     would_do_sao = 1;
  398     if (demands.unknown_track_size == 2 && (!(flag & 1)) &&
  399         (caps->might_do_sao == 1 || caps->might_do_sao == 3)) {
  400         strcat(reasons, "would have to use default track sizes, ");
  401         goto no_sao;
  402     } else if (caps->might_do_sao >= 3 && !(flag & 1))
  403         goto try_tao;
  404 do_sao:;
  405     if (caps->might_simulate == 0 && opts->simulate && !opts->force_is_set)
  406         goto no_simulate;
  407     if (!(flag & 1))
  408         burn_write_opts_set_write_type(
  409                     opts, BURN_WRITE_SAO, BURN_BLOCK_SAO);
  410     {wt = BURN_WRITE_SAO; goto ex;}
  411 no_sao:;
  412 try_tao:;
  413     if (opts->num_text_packs > 0) {
  414         strcat(reasons, "CD-TEXT: write type SAO required, ");
  415         {wt = BURN_WRITE_NONE; goto ex;}
  416     }
  417     if ((flag & 1) && opts->write_type != BURN_WRITE_TAO)
  418         goto try_raw;
  419     reason_pt = reasons + strlen(reasons);
  420     strcat(reasons, "TAO: ");
  421     burn_disc_free_multi_caps(&caps);
  422     ret = burn_disc_get_multi_caps(d, BURN_WRITE_TAO, &caps, 0);
  423     if (ret < 0)
  424         goto no_caps;
  425     if (ret == 0) { 
  426         strcat(reasons, "no TAO offered by drive and media, ");
  427         goto no_tao;
  428     }
  429     if ((opts->multi || demands.multi_session) && !caps->multi_session)
  430         strcat(reasons, "multi session capability lacking, ");
  431     if (demands.multi_track && !caps->multi_track)
  432         strcat(reasons, "multi track capability lacking, ");
  433     if (demands.exotic_track && !d->current_is_cd_profile)
  434         strcat(reasons, "non-data track on non-cd, ");
  435     if (d->current_is_cd_profile && !opts->force_is_set)
  436         if ((d->block_types[BURN_WRITE_TAO] & demands.block_types) !=
  437             demands.block_types)
  438             strcat(reasons, "drive dislikes block type, ");
  439     if (strcmp(reason_pt, "TAO: ") != 0)
  440         goto no_tao;
  441     /* ( TAO data/audio block size will be handled automatically ) */
  442     if (caps->might_simulate == 0 && opts->simulate && !opts->force_is_set)
  443         goto no_simulate;
  444     if (!(flag & 1))
  445         burn_write_opts_set_write_type(
  446                 opts, BURN_WRITE_TAO, BURN_BLOCK_MODE1);
  447     {wt = BURN_WRITE_TAO; goto ex;}
  448 no_tao:;
  449     if (would_do_sao && !(flag & 1))
  450         goto do_sao;
  451     if (!d->current_is_cd_profile)
  452         goto no_write_mode;
  453 try_raw:;
  454     if ((flag & 1) && opts->write_type != BURN_WRITE_RAW)
  455         goto no_write_mode;
  456 
  457     if (!(flag & 1)) /* For now: no automatic raw write modes */
  458         goto no_write_mode;
  459 
  460     reason_pt = reasons + strlen(reasons);
  461     strcat(reasons, "RAW: ");
  462     if (!d->current_is_cd_profile)
  463         strcat(reasons, "write type RAW prohibited by non-cd, ");
  464     else if (d->status != BURN_DISC_BLANK)
  465         strcat(reasons, "write type RAW works only on blank media, ");
  466     else if ((d->block_types[BURN_WRITE_TAO] & demands.block_types) !=
  467                                 demands.block_types)
  468         strcat(reasons, "drive dislikes block type, ");
  469     if (strcmp(reason_pt, "RAW: ") != 0)
  470         goto no_write_mode;
  471     if (!opts->force_is_set)
  472         goto no_simulate;
  473 
  474     /*  For now: no setting of raw write modes */
  475 
  476     {wt = BURN_WRITE_RAW; goto ex;}
  477 
  478 no_write_mode:;
  479     {wt = BURN_WRITE_NONE; goto ex;}
  480 
  481 no_simulate:;
  482     strcat(reasons,
  483            "simulation of write job not supported by drive and media, ");
  484     {wt = BURN_WRITE_NONE; goto ex;}
  485     
  486 ex:;
  487     burn_disc_free_multi_caps(&caps);
  488     if (wt == BURN_WRITE_NONE && !(flag & 3)) {
  489         libdax_msgs_submit(libdax_messenger, d->global_index,
  490             0x0002012b,
  491             LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH,
  492             "Drive offers no suitable write mode with this job",
  493             0, 0);
  494     }
  495     return wt;
  496 }
  497 
  498 
  499 /* ts A70213 : new API function */
  500 void burn_write_opts_set_fillup(struct burn_write_opts *opts,int fill_up_media)
  501 {
  502     opts->fill_up_media = !!fill_up_media;
  503     return;
  504 }
  505 
  506 
  507 /* ts A70303: API */
  508 void burn_write_opts_set_force(struct burn_write_opts *opts, int use_force)
  509 {
  510     opts->force_is_set = !!use_force;
  511 }
  512 
  513 
  514 /* ts A80412: API */
  515 void burn_write_opts_set_stream_recording(struct burn_write_opts *opts,
  516                      int value)
  517 {
  518     opts->do_stream_recording = value;
  519 }
  520 
  521 
  522 /* ts A91115: API */
  523 void burn_write_opts_set_dvd_obs(struct burn_write_opts *opts, int obs)
  524 {
  525     if (obs != 0 && obs != 32 * 1024 && obs != 64 * 1024)
  526         return;
  527     opts->dvd_obs_override = obs;
  528 }
  529 
  530 
  531 /* ts B20406: API */
  532 void burn_write_opts_set_obs_pad(struct burn_write_opts *opts, int pad)
  533 {
  534     opts->obs_pad = 2 * !!pad;
  535 }
  536 
  537 
  538 /* ts A91115: API */
  539 void burn_write_opts_set_stdio_fsync(struct burn_write_opts *opts, int rhythm)
  540 {
  541     if (rhythm == -1)
  542         opts->stdio_fsync_size = -1; /* never */
  543     else if (rhythm == 0)
  544         opts->stdio_fsync_size = Libburn_stdio_fsync_limiT;
  545     else if (rhythm == 1)
  546         opts->stdio_fsync_size = 0; /* only at end of writing */
  547     else if (rhythm >= 32)
  548         opts->stdio_fsync_size = rhythm;
  549 }
  550 
  551 
  552 /* ts A70901: API */
  553 struct burn_drive *burn_write_opts_get_drive(struct burn_write_opts *opts)
  554 {
  555     return opts->drive;
  556 }
  557 
  558 
  559 void burn_read_opts_set_raw(struct burn_read_opts *opts, int raw)
  560 {
  561     opts->raw = raw;
  562 }
  563 
  564 void burn_read_opts_set_c2errors(struct burn_read_opts *opts, int c2errors)
  565 {
  566     opts->c2errors = c2errors;
  567 }
  568 
  569 void burn_read_opts_read_subcodes_audio(struct burn_read_opts *opts,
  570                     int subcodes_audio)
  571 {
  572     opts->subcodes_audio = subcodes_audio;
  573 }
  574 
  575 void burn_read_opts_read_subcodes_data(struct burn_read_opts *opts,
  576                        int subcodes_data)
  577 {
  578     opts->subcodes_data = subcodes_data;
  579 }
  580 
  581 void burn_read_opts_set_hardware_error_recovery(struct burn_read_opts *opts,
  582                         int hardware_error_recovery)
  583 {
  584     opts->hardware_error_recovery = hardware_error_recovery;
  585 }
  586 
  587 void burn_read_opts_report_recovered_errors(struct burn_read_opts *opts,
  588                         int report_recovered_errors)
  589 {
  590     opts->report_recovered_errors = report_recovered_errors;
  591 }
  592 
  593 void burn_read_opts_transfer_damaged_blocks(struct burn_read_opts *opts,
  594                         int transfer_damaged_blocks)
  595 {
  596     opts->transfer_damaged_blocks = transfer_damaged_blocks;
  597 }
  598 
  599 void burn_read_opts_set_hardware_error_retries(struct burn_read_opts *opts,
  600                            unsigned char
  601                            hardware_error_retries)
  602 {
  603     opts->hardware_error_retries = hardware_error_retries;
  604 }
  605