Blame test/driver/mmc_write.c

Packit dd8086
/* -*- C -*-
Packit dd8086
  Copyright (C) 2009 Thomas Schmitt <scdbackup@gmx.net>
Packit dd8086
  Copyright (C) 2010, 2012-2013, 2017 Rocky Bernstein <rocky@gnu.org>
Packit dd8086
Packit dd8086
  This program is free software: you can redistribute it and/or modify
Packit dd8086
  it under the terms of the GNU General Public License as published by
Packit dd8086
  the Free Software Foundation, either version 3 of the License, or
Packit dd8086
  (at your option) any later version.
Packit dd8086
Packit dd8086
  This program is distributed in the hope that it will be useful,
Packit dd8086
  but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit dd8086
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit dd8086
  GNU General Public License for more details.
Packit dd8086
Packit dd8086
  You should have received a copy of the GNU General Public License
Packit dd8086
  along with this program.  If not, see <http://www.gnu.org/licenses/>.
Packit dd8086
*/
Packit dd8086
Packit dd8086
/*
Packit dd8086
   Regression test for MMC commands involving read/write access.
Packit dd8086
*/
Packit dd8086
#ifdef HAVE_CONFIG_H
Packit dd8086
#include "config.h"
Packit dd8086
#define __CDIO_CONFIG_H__ 1
Packit dd8086
#endif
Packit dd8086
Packit dd8086
#ifdef HAVE_STDIO_H
Packit dd8086
#include <stdio.h>
Packit dd8086
#endif
Packit dd8086
#ifdef HAVE_SYS_TYPES_H
Packit dd8086
#include <sys/types.h>
Packit dd8086
#endif
Packit dd8086
#ifdef HAVE_STDLIB_H
Packit dd8086
#include <stdlib.h>
Packit dd8086
#endif
Packit dd8086
#ifdef HAVE_UNISTD_H
Packit dd8086
#include <unistd.h>
Packit dd8086
#endif
Packit dd8086
#ifdef HAVE_STRING_H
Packit dd8086
#include <string.h>
Packit dd8086
#endif
Packit dd8086
#ifdef _WIN32
Packit dd8086
#  undef HAVE_SLEEP
Packit dd8086
#endif
Packit dd8086
#if !defined(HAVE_SLEEP)
Packit dd8086
#  if defined(_WIN32)
Packit dd8086
#     include <windows.h>
Packit dd8086
#     define sleep(s) Sleep(1000*s)
Packit dd8086
#  elif defined(HAVE_USLEEP)
Packit dd8086
#     define sleep(s) usleep(1000000*s)
Packit dd8086
#  else
Packit dd8086
#     define sleep(s) { int i; for(i=0; i<=1000*s; i++); }
Packit dd8086
#  endif
Packit dd8086
#endif
Packit dd8086
Packit dd8086
#include <cdio/cdio.h>
Packit dd8086
#include <cdio/logging.h>
Packit dd8086
#include <cdio/mmc_cmds.h>
Packit dd8086
Packit dd8086
#define SKIP_TEST 77
Packit dd8086
Packit dd8086
/* gcc may warn if no prototypes are given before function definition */
Packit dd8086
Packit dd8086
static int handle_outcome(CdIo_t *p_cdio, int i_status,
Packit dd8086
			       unsigned int *pi_sense_avail,
Packit dd8086
			       cdio_mmc_request_sense_t * p_sense_reply,
Packit dd8086
			       unsigned int i_flag);
Packit dd8086
Packit dd8086
static int load_eject(CdIo_t *p_cdio, unsigned int *pi_sense_avail,
Packit dd8086
		      cdio_mmc_request_sense_t * p_sense_reply,
Packit dd8086
		      unsigned int i_flag);
Packit dd8086
Packit dd8086
static void print_status_sense(int i_status, int sense_valid,
Packit dd8086
				    cdio_mmc_request_sense_t *,
Packit dd8086
				    unsigned int i_flag);
Packit dd8086
Packit dd8086
static int test_eject_load_cycle(CdIo_t *p_cdio, unsigned int i_flag);
Packit dd8086
Packit dd8086
static int test_eject_test_load(CdIo_t *p_cdio, unsigned int i_flag);
Packit dd8086
Packit dd8086
static int test_mode_select(CdIo_t *p_cdio,
Packit dd8086
			    unsigned int *pi_sense_avail,
Packit dd8086
			    cdio_mmc_request_sense_t *p_sense_reply,
Packit dd8086
			    unsigned char *p_buf, unsigned int i_size,
Packit dd8086
			    unsigned int i_flag);
Packit dd8086
Packit dd8086
static int mode_sense(CdIo_t *p_cdio, unsigned int *pi_sense_avail,
Packit dd8086
		      cdio_mmc_request_sense_t * p_sense_reply,
Packit dd8086
		      unsigned int i_page_code,
Packit dd8086
		      unsigned int subpage_code, int i_alloc_len,
Packit dd8086
		      unsigned char *buf, int *i_size,
Packit dd8086
		      unsigned int i_flag);
Packit dd8086
Packit dd8086
static int test_unit_ready(CdIo_t *p_cdio,
Packit dd8086
			   unsigned int *pi_sense_avail,
Packit dd8086
			   cdio_mmc_request_sense_t * p_sense_reply,
Packit dd8086
			   unsigned int i_flag);
Packit dd8086
Packit dd8086
static int test_rwr_mode_page(CdIo_t *p_cdio, unsigned int i_flag);
Packit dd8086
Packit dd8086
static int test_write(char *psz_drive_path, unsigned int i_flag);
Packit dd8086
Packit dd8086
static int wait_for_drive(CdIo_t *p_cdio, unsigned int max_tries, unsigned int i_flag);
Packit dd8086
Packit dd8086
/* ------------------------- Helper functions ---------------------------- */
Packit dd8086
Packit dd8086
Packit dd8086
/* @param i_flag         bit0= verbose
Packit dd8086
*/
Packit dd8086
static int
Packit dd8086
handle_outcome(CdIo_t *p_cdio, driver_return_code_t i_status,
Packit dd8086
                    unsigned int *pi_sense_avail,
Packit dd8086
		    cdio_mmc_request_sense_t * p_sense_reply,
Packit dd8086
		    unsigned int i_flag)
Packit dd8086
{
Packit dd8086
    cdio_mmc_request_sense_t *p_temp_sense_reply = NULL;
Packit dd8086
    *pi_sense_avail = mmc_last_cmd_sense(p_cdio, &p_temp_sense_reply);
Packit dd8086
    print_status_sense(i_status, *pi_sense_avail, p_temp_sense_reply, i_flag & 1);
Packit dd8086
    if (18 <= *pi_sense_avail) {
Packit dd8086
        memset(p_sense_reply, 0, sizeof(cdio_mmc_request_sense_t));
Packit dd8086
        memcpy(p_sense_reply, p_temp_sense_reply, *pi_sense_avail);
Packit dd8086
    } else
Packit dd8086
        memset(p_sense_reply, 0, sizeof(cdio_mmc_request_sense_t));
Packit dd8086
    cdio_free(p_temp_sense_reply);
Packit dd8086
    return i_status;
Packit dd8086
}
Packit dd8086
Packit dd8086
Packit dd8086
/**
Packit dd8086
   @param flag bit0= verbose
Packit dd8086
*/
Packit dd8086
static void
Packit dd8086
print_status_sense(int i_status, int sense_valid,
Packit dd8086
                        cdio_mmc_request_sense_t *p_sense_reply,
Packit dd8086
			unsigned int i_flag)
Packit dd8086
{
Packit dd8086
    if (!(i_flag & 1))
Packit dd8086
	return;
Packit dd8086
    printf("return= %d , sense(%d)", i_status, sense_valid);
Packit dd8086
    if (sense_valid >= 14)
Packit dd8086
	printf(":  KEY=%s (%1.1X), ASC= %2.2X , ASCQ= %2.2X",
Packit dd8086
		mmc_sense_key2str[p_sense_reply->sense_key],
Packit dd8086
		p_sense_reply->sense_key,
Packit dd8086
		p_sense_reply->asc,
Packit dd8086
		p_sense_reply->ascq);
Packit dd8086
    printf("\n");
Packit dd8086
}
Packit dd8086
Packit dd8086
Packit dd8086
/* --------------------------- MMC commands ------------------------------ */
Packit dd8086
Packit dd8086
Packit dd8086
/* OBTRUSIVE. PHYSICAL EFFECT: DANGER OF HUMAN INJURY */
Packit dd8086
/**
Packit dd8086
   @param i_flag bit0= verbose
Packit dd8086
                 bit1= Asynchronous operation
Packit dd8086
                 bit2= Load (else Eject)
Packit dd8086
   @return      return value of mmc_run_cmd()
Packit dd8086
*/
Packit dd8086
static int
Packit dd8086
load_eject(CdIo_t *p_cdio, unsigned int *pi_sense_avail,
Packit dd8086
	   cdio_mmc_request_sense_t *p_sense_reply,
Packit dd8086
	   unsigned int i_flag)
Packit dd8086
{
Packit dd8086
  int i_status;
Packit dd8086
  bool b_eject = !!(i_flag & 4);
Packit dd8086
  bool b_immediate = !!(i_flag & 2);
Packit dd8086
Packit dd8086
  i_status = mmc_start_stop_unit(p_cdio, b_eject, b_immediate, 0, 0);
Packit dd8086
Packit dd8086
  if (i_flag & 1)
Packit dd8086
    printf("load_eject(0x%X) ... ", i_flag);
Packit dd8086
Packit dd8086
  return handle_outcome(p_cdio, i_status, pi_sense_avail, p_sense_reply,
Packit dd8086
			i_flag & 1);
Packit dd8086
}
Packit dd8086
Packit dd8086
Packit dd8086
/* BARELY OBTRUSIVE: MIGHT RUIN A SIMULTANEOUS BURNING OPERATION ON THE DRIVE */
Packit dd8086
/**
Packit dd8086
   Fetch a mode page or a part of it from the drive.
Packit dd8086
   @param i_alloc_len  The number of bytes to be requested from the drive and to
Packit dd8086
                       be copied into parameter buf.
Packit dd8086
                       This has to include the 8 bytes of header and may not
Packit dd8086
                       be less than 10.
Packit dd8086
   @param p_buf        Will contain at most alloc_len many bytes. The first 8 are
Packit dd8086
                       a Mode Parameter Header as of SPC-3 7.4.3, table 240.
Packit dd8086
                       The further bytes are the mode page, typically as of
Packit dd8086
                       MMC-5 7.2. There are meanwhile deprecated mode pages which
Packit dd8086
                       appear only in older versions of MMC.
Packit dd8086
   @param i_size       Will return the number of actually read bytes resp. the
Packit dd8086
                       number of available bytes. See flag bit1.
Packit dd8086
   @param i_flag       bit0= verbose
Packit dd8086
                       bit1= Peek mode:
Packit dd8086
                           Reply number of available bytes in *i_size and not
Packit dd8086
                           the number of actually read bytes.
Packit dd8086
   @return             return value of mmc_run_cmd(),
Packit dd8086
                       or other driver_return_code_t
Packit dd8086
*/
Packit dd8086
static driver_return_code_t
Packit dd8086
mode_sense(CdIo_t *p_cdio, unsigned int *pi_sense_avail,
Packit dd8086
	   cdio_mmc_request_sense_t *p_sense_reply,
Packit dd8086
	   unsigned int i_page_code, unsigned int subpage_code, int i_alloc_len,
Packit dd8086
	   unsigned char *p_buf, int *pi_size, unsigned int i_flag)
Packit dd8086
{
Packit dd8086
  driver_return_code_t i_status;
Packit dd8086
Packit dd8086
  if (i_alloc_len < 10)
Packit dd8086
    return DRIVER_OP_BAD_PARAMETER;
Packit dd8086
Packit dd8086
  if (i_flag & 1)
Packit dd8086
    printf("mode_sense(0x%X, %X, %d) ... ",
Packit dd8086
	   i_page_code, subpage_code, i_alloc_len);
Packit dd8086
Packit dd8086
  i_status = mmc_mode_sense_10(p_cdio, p_buf, i_alloc_len, i_page_code);
Packit dd8086
  handle_outcome(p_cdio, i_status, pi_sense_avail, p_sense_reply, i_flag & 1);
Packit dd8086
  if (DRIVER_OP_SUCCESS != i_status)
Packit dd8086
    return i_status;
Packit dd8086
  if (i_flag & 2)
Packit dd8086
    *pi_size = p_buf[9] + 10;                  /* MMC-5 7.2.3 */
Packit dd8086
  else
Packit dd8086
    *pi_size = p_buf[0] * 256 + p_buf[1] + 2;  /* SPC-3 7.4.3, table 240 */
Packit dd8086
  return i_status;
Packit dd8086
}
Packit dd8086
Packit dd8086
Packit dd8086
/* OBTRUSIVE. RUINS A SIMULTANEOUS BURNING OPERATION ON THE DRIVE
Packit dd8086
   and might return minor failure with -ROM drives */
Packit dd8086
/**
Packit dd8086
   Send a mode page to the drive.
Packit dd8086
   @param buf        Contains the payload bytes. The first 8 shall be a Mode
Packit dd8086
                     Parameter Header as of SPC-3 7.4.3, table 240.
Packit dd8086
                     The further bytes are the mode page, typically as of
Packit dd8086
                     MMC-5 7.2. There are meanwhile deprecated mode pages which
Packit dd8086
                     appear only in older versions of MMC.
Packit dd8086
   @param i_size   The number of bytes in buf.
Packit dd8086
   @param flag       bit0= verbose
Packit dd8086
   @return           return value of mmc_run_cmd(),
Packit dd8086
                     or other driver_return_code_t
Packit dd8086
*/
Packit dd8086
static int
Packit dd8086
test_mode_select(CdIo_t *p_cdio,
Packit dd8086
                 unsigned int *pi_sense_avail,
Packit dd8086
		 cdio_mmc_request_sense_t *p_sense_reply,
Packit dd8086
                 unsigned char *p_buf, unsigned int i_size, unsigned int i_flag)
Packit dd8086
{
Packit dd8086
    int i_status, i;
Packit dd8086
Packit dd8086
    if (i_size < 10)
Packit dd8086
	return DRIVER_OP_BAD_PARAMETER;
Packit dd8086
Packit dd8086
    if (i_flag & 1) {
Packit dd8086
	printf("-- test_mode_select to drive: %d bytes\n", i_size);
Packit dd8086
	for (i = 0; i < i_size; i++) {
Packit dd8086
	    printf("%2.2X ", (unsigned int) p_buf[i]);
Packit dd8086
	    if ((i % 20) == 19)
Packit dd8086
		printf("\n");
Packit dd8086
	}
Packit dd8086
	if ((i % 20))
Packit dd8086
	    printf("\n");
Packit dd8086
    }
Packit dd8086
Packit dd8086
    if (i_flag & 1)
Packit dd8086
	printf("-- test_mode_select(0x%X, %d, %d) ... ",
Packit dd8086
	       (unsigned int) p_buf[8], (unsigned int) p_buf[9], i_size);
Packit dd8086
    i_status = mmc_mode_select_10(p_cdio, p_buf, i_size, 0x10, 10000);
Packit dd8086
    return handle_outcome(p_cdio, i_status, pi_sense_avail, p_sense_reply,
Packit dd8086
			  i_flag & 1);
Packit dd8086
}
Packit dd8086
Packit dd8086
/* UNOBTRUSIVE */
Packit dd8086
/**
Packit dd8086
   @param pi_sense_avail  Number of available sense bytes
Packit dd8086
                          (18 get copied if all 18 exist)
Packit dd8086
   @param p_sense_reply  eventual sense bytes
Packit dd8086
   @param i_flag          bit0= verbose
Packit dd8086
   @return             return value of mmc_run_cmd()
Packit dd8086
 */
Packit dd8086
static int
Packit dd8086
test_unit_ready(CdIo_t *p_cdio,
Packit dd8086
		unsigned int *pi_sense_avail,
Packit dd8086
		cdio_mmc_request_sense_t *p_sense_reply,
Packit dd8086
		unsigned int i_flag)
Packit dd8086
{
Packit dd8086
  int i_status;
Packit dd8086
Packit dd8086
  if (i_flag & 1)
Packit dd8086
    printf("-- test_unit_ready ... ");
Packit dd8086
  i_status = mmc_test_unit_ready(p_cdio, 0);
Packit dd8086
Packit dd8086
  return handle_outcome(p_cdio, i_status, pi_sense_avail, p_sense_reply,
Packit dd8086
                             i_flag & 1);
Packit dd8086
}
Packit dd8086
Packit dd8086
Packit dd8086
/* --------------------------- Larger gestures ----------------------------- */
Packit dd8086
Packit dd8086
Packit dd8086
/* UNOBTRUSIVE */
Packit dd8086
/**
Packit dd8086
   Watch drive by test unit ready loop until ready, no media or timeout.
Packit dd8086
   @param flag bit0= verbose
Packit dd8086
               bit1= expect media (do not end on no-media sense)
Packit dd8086
   @return     1= all seems well , 0= minor failure , -1= severe failure
Packit dd8086
*/
Packit dd8086
static int
Packit dd8086
wait_for_drive(CdIo_t *p_cdio, unsigned int i_max_tries, unsigned int i_flag)
Packit dd8086
{
Packit dd8086
    int i_ret, i;
Packit dd8086
    unsigned int i_sense_avail;
Packit dd8086
    cdio_mmc_request_sense_t sense_reply;
Packit dd8086
Packit dd8086
    for (i = 0; i < i_max_tries; i++) {
Packit dd8086
	i_ret = test_unit_ready(p_cdio, &i_sense_avail, &sense_reply, !!(i_flag & 1));
Packit dd8086
	if (i_ret == 0) /* Unit is ready */
Packit dd8086
	    return 1;
Packit dd8086
	if (i_sense_avail < 18)
Packit dd8086
	    return -1;
Packit dd8086
	if (2 == sense_reply.sense_key && 0x04 == sense_reply.asc) {
Packit dd8086
Packit dd8086
	    /* Not ready */;
Packit dd8086
Packit dd8086
	} else if (6 == sense_reply.sense_key && 0x28 == sense_reply.asc &&
Packit dd8086
		   0 == sense_reply.ascq) {
Packit dd8086
Packit dd8086
	    /* Media change notice = try again */;
Packit dd8086
Packit dd8086
	} else if (2 == sense_reply.sense_key && 0x3a == sense_reply.asc) {
Packit dd8086
Packit dd8086
	    /* Medium not present */;
Packit dd8086
Packit dd8086
	    if (!(i_flag & 2))
Packit dd8086
		return 1;
Packit dd8086
	} else if (0 == sense_reply.sense_key && 0 == sense_reply.asc) {
Packit dd8086
Packit dd8086
	    /* Error with no sense */;
Packit dd8086
Packit dd8086
	    return -1;
Packit dd8086
	    break;
Packit dd8086
	} else {
Packit dd8086
Packit dd8086
	    /* Other error */;
Packit dd8086
Packit dd8086
	    return 0;
Packit dd8086
	}
Packit dd8086
	sleep(1);
Packit dd8086
    }
Packit dd8086
    fprintf(stderr, "wait_for_drive: Drive not ready after %d retries\n",
Packit dd8086
	    i_max_tries);
Packit dd8086
    return -1;
Packit dd8086
}
Packit dd8086
Packit dd8086
Packit dd8086
/* OBTRUSIVE. Opens and closes drive door - watch your fingers! */
Packit dd8086
/**
Packit dd8086
   Eject, wait, load asynchronously, and watch by test unit ready loop.
Packit dd8086
   @param i_flag bit0= verbose
Packit dd8086
                 bit1= expect media (do not end on no-media sense)
Packit dd8086
   @return   1= all seems well , 0= minor failure , -1= severe failure
Packit dd8086
*/
Packit dd8086
static int
Packit dd8086
test_eject_load_cycle(CdIo_t *p_cdio, unsigned int i_flag)
Packit dd8086
{
Packit dd8086
    int i_ret;
Packit dd8086
    unsigned int i_sense_avail;
Packit dd8086
    cdio_mmc_request_sense_t sense_reply;
Packit dd8086
Packit dd8086
    /* Eject synchronously */
Packit dd8086
    printf("test_eject_load_cycle: WARNING: EJECTING THE TRAY !\n");
Packit dd8086
    sleep(2);
Packit dd8086
    load_eject(p_cdio, &i_sense_avail, &sense_reply, 0 | (i_flag & 1));
Packit dd8086
Packit dd8086
    printf("test_eject_load_cycle: waiting for 5 seconds. DO NOT TOUCH THE TRAY !\n");
Packit dd8086
    sleep(3);
Packit dd8086
Packit dd8086
    /* Load asynchronously */
Packit dd8086
    printf("test_eject_load_cycle: WARNING: LOADING THE TRAY !\n");
Packit dd8086
    sleep(2);
Packit dd8086
    load_eject(p_cdio, &i_sense_avail, &sense_reply, 4 | 2 | (i_flag & 1));
Packit dd8086
Packit dd8086
    /* Wait for drive attention */
Packit dd8086
    i_ret = wait_for_drive(p_cdio, 30, i_flag & 3);
Packit dd8086
    return i_ret;
Packit dd8086
}
Packit dd8086
Packit dd8086
Packit dd8086
/* OBTRUSIVE , PHYSICAL EFFECT , DANGER OF HUMAN INJURY */
Packit dd8086
/**
Packit dd8086
   Eject, wait, test, load. All synchronously.
Packit dd8086
   @param flag bit0= verbose
Packit dd8086
   @return    1= all seems well , 0= minor failure , -1= severe failure
Packit dd8086
*/
Packit dd8086
static int
Packit dd8086
test_eject_test_load(CdIo_t *p_cdio, unsigned int i_flag)
Packit dd8086
{
Packit dd8086
    int i_ret;
Packit dd8086
    unsigned int i_sense_avail;
Packit dd8086
    cdio_mmc_request_sense_t sense_reply;
Packit dd8086
Packit dd8086
  /* Eject synchronously */
Packit dd8086
  printf("test_eject_test_load: WARNING: EJECTING THE TRAY !\n");
Packit dd8086
  sleep(2);
Packit dd8086
  load_eject(p_cdio, &i_sense_avail, &sense_reply, 0 | (i_flag & 1));
Packit dd8086
Packit dd8086
  printf("test_eject_test_load: waiting for 5 seconds. DO NOT TOUCH THE TRAY !\n");
Packit dd8086
  sleep(3);
Packit dd8086
Packit dd8086
  i_ret = test_unit_ready(p_cdio, &i_sense_avail, &sense_reply, i_flag & 1);
Packit dd8086
  if (i_ret == 0) {
Packit dd8086
    fprintf(stderr,
Packit dd8086
            "test_eject_test_load: Drive ready although tray ejected.\n");
Packit dd8086
    fprintf(stderr,
Packit dd8086
            "test_eject_test_load: Test aborted. Tray will stay ejected.\n");
Packit dd8086
    return -1;
Packit dd8086
  }
Packit dd8086
  if (i_ret == 0 || i_sense_avail < 18) {
Packit dd8086
    fprintf(stderr,
Packit dd8086
 "test_eject_test_load: Only %d sense reply bytes returned. Expected >= 18.\n",
Packit dd8086
            i_sense_avail);
Packit dd8086
    fprintf(stderr,
Packit dd8086
            "test_eject_test_load: Test aborted. Tray will stay ejected.\n");
Packit dd8086
    return -1;
Packit dd8086
  }
Packit dd8086
Packit dd8086
  /* Load synchronously */
Packit dd8086
  fprintf(stderr,
Packit dd8086
    "test_eject_test_load: WARNING: LOADING THE TRAY !\n");
Packit dd8086
  sleep(2);
Packit dd8086
  load_eject(p_cdio, &i_sense_avail, &sense_reply, 4 | (i_flag & 1));
Packit dd8086
Packit dd8086
  return 1;
Packit dd8086
}
Packit dd8086
Packit dd8086
Packit dd8086
/* OBTRUSIVE */
Packit dd8086
/**
Packit dd8086
   Read Mode Page 05h "Write Parameters", change a value, write the page,
Packit dd8086
   check effect, restore old value, check again.
Packit dd8086
   @param flag bit0= verbose
Packit dd8086
   @return    1= all seems well , 0= minor failure , -1= severe failure
Packit dd8086
*/
Packit dd8086
static int
Packit dd8086
test_rwr_mode_page(CdIo_t *p_cdio, unsigned int i_flag)
Packit dd8086
{
Packit dd8086
    int i_ret;
Packit dd8086
    unsigned int i_sense_avail = 0;
Packit dd8086
    int page_code = 5, subpage_code = 0, i_alloc_len, i_size = 0;
Packit dd8086
    int write_type, final_return = 1, new_write_type, old_i_size;
Packit dd8086
    cdio_mmc_request_sense_t sense_reply;
Packit dd8086
    unsigned char buf[265], old_buf[265];        /* page size is max. 255 + 10 */
Packit dd8086
    static char w_types[4][8] = {"Packet", "TAO", "SAO", "Raw"};
Packit dd8086
Packit dd8086
    memset(buf, 0, sizeof(buf));
Packit dd8086
Packit dd8086
    i_alloc_len = 10;
Packit dd8086
    i_ret = mode_sense(p_cdio, &i_sense_avail, &sense_reply,
Packit dd8086
			    page_code, subpage_code, i_alloc_len,
Packit dd8086
			    buf, &i_size, 2 | (i_flag & 1));
Packit dd8086
    if (i_ret != 0) {
Packit dd8086
	fprintf(stderr,
Packit dd8086
		"-- test_rwr_mode_page: Cannot obtain mode page 05h.\n");
Packit dd8086
	return 0;
Packit dd8086
    }
Packit dd8086
    i_alloc_len = (i_size <= sizeof(buf)) ? i_size : sizeof(buf);
Packit dd8086
    i_ret = mode_sense(p_cdio, &i_sense_avail, &sense_reply,
Packit dd8086
			    page_code, subpage_code, i_alloc_len,
Packit dd8086
			    buf, &i_size, (i_flag & 1));
Packit dd8086
    if (i_ret != 0) {
Packit dd8086
	fprintf(stderr,
Packit dd8086
		"-- test_rwr_mode_page: Cannot obtain mode page 05h.\n");
Packit dd8086
	return 0;
Packit dd8086
    }
Packit dd8086
    memcpy(old_buf, buf, sizeof(buf));
Packit dd8086
    old_i_size = i_size;
Packit dd8086
Packit dd8086
    write_type = buf[10] & 0x0f;
Packit dd8086
    if (i_flag & 1)
Packit dd8086
	printf("test_rwr_mode_page: Write type = %d (%s)\n",
Packit dd8086
	       write_type, write_type < 4 ? w_types[write_type] : "Reserved");
Packit dd8086
Packit dd8086
    /* Choose a conservative CD writer setting.
Packit dd8086
     */
Packit dd8086
    memset(buf, 0, 8);
Packit dd8086
    if (write_type == 1) { /* is TAO -> make SAO */
Packit dd8086
	buf[10] = (buf[10] & 0xf0) | 2;   /* SAO */
Packit dd8086
	new_write_type = 2;
Packit dd8086
    } else {
Packit dd8086
	buf[10] = (buf[10] & 0xf0) | 1;   /* TAO */
Packit dd8086
	new_write_type = 1;
Packit dd8086
    }
Packit dd8086
    buf[11] = 4;                        /* Track Mode 4, no multi-session,
Packit dd8086
					   variable Packet size */
Packit dd8086
    buf[12] = 8;                        /* Data Block Type : CD-ROM Mode 1 */
Packit dd8086
    buf[16] = 0;                        /* Session Format : "CD-DA or CD-ROM" */
Packit dd8086
Packit dd8086
    i_ret = test_mode_select(p_cdio, &i_sense_avail, &sense_reply,
Packit dd8086
			     buf, i_size, i_flag & 1);
Packit dd8086
    if (i_ret != 0) {
Packit dd8086
	fprintf(stderr,
Packit dd8086
		"-- test_rwr_mode_page: Cannot set mode page 05h.\n");
Packit dd8086
	if (DRIVER_OP_NOT_PERMITTED == i_ret) {
Packit dd8086
	    fprintf(stderr,
Packit dd8086
		    "test_rwr_mode_page: DRIVER_OP_NOT_PERMITTED with MODE SELECT.\n");
Packit dd8086
	    return -1;
Packit dd8086
	}
Packit dd8086
	return 0;
Packit dd8086
    }
Packit dd8086
Packit dd8086
    /* Read mode page and check whether effect visible in buf[10] */
Packit dd8086
    i_ret = mode_sense(p_cdio, &i_sense_avail, &sense_reply,
Packit dd8086
			    page_code, subpage_code, i_alloc_len,
Packit dd8086
			    buf, &i_size, (i_flag & 1));
Packit dd8086
    if (0 != i_ret) {
Packit dd8086
	fprintf(stderr, "test_rwr_mode_page: Cannot obtain mode page 05h.\n");
Packit dd8086
	final_return = final_return > 0 ? 0 : final_return;
Packit dd8086
    } else if ((buf[10] & 0xf) != new_write_type) {
Packit dd8086
	fprintf(stderr,
Packit dd8086
		"test_rwr_mode_page: Mode page did not get into effect. (expected %d, got %d)\n",
Packit dd8086
		new_write_type, buf[10] & 0xf);
Packit dd8086
	/* One of my DVD burners does this if no media is loaded */
Packit dd8086
	final_return = final_return > 0 ? 0 : final_return;
Packit dd8086
    }
Packit dd8086
Packit dd8086
    /* Restore old mode page */
Packit dd8086
    i_ret = test_mode_select(p_cdio, &i_sense_avail, &sense_reply,
Packit dd8086
			     old_buf, old_i_size, i_flag & 1);
Packit dd8086
    if (0 != i_ret) {
Packit dd8086
	fprintf(stderr, "test_rwr_mode_page: Cannot set mode page 05h.\n");
Packit dd8086
	if (i_ret == DRIVER_OP_NOT_PERMITTED) {
Packit dd8086
	    fprintf(stderr,
Packit dd8086
		    "test_rwr_mode_page: DRIVER_OP_NOT_PERMITTED with MODE SELECT.\n");
Packit dd8086
	    return -1;
Packit dd8086
	}
Packit dd8086
	final_return = final_return > 0 ? 0 : final_return;
Packit dd8086
    }
Packit dd8086
Packit dd8086
    /* Read mode page and check whether old_buf is in effect again */
Packit dd8086
    i_ret = mode_sense(p_cdio, &i_sense_avail, &sense_reply,
Packit dd8086
		       page_code, subpage_code, i_alloc_len,
Packit dd8086
		       buf, &i_size, (i_flag & 1));
Packit dd8086
    if (0 != i_ret) {
Packit dd8086
	fprintf(stderr, "test_rwr_mode_page: Cannot obtain mode page 05h.\n");
Packit dd8086
	return final_return > 0 ? 0 : final_return;
Packit dd8086
    } else if (memcmp(buf, old_buf, i_size) != 0) {
Packit dd8086
	fprintf(stderr,
Packit dd8086
		"test_rwr_mode_page: Mode page was not restored to old state.\n");
Packit dd8086
	final_return = final_return > 0 ? -1 : final_return;
Packit dd8086
    }
Packit dd8086
    if (i_flag & 1)
Packit dd8086
	printf("test_rwr_mode_page: Mode page 2Ah restored to previous state\n");
Packit dd8086
    return final_return;
Packit dd8086
}
Packit dd8086
Packit dd8086
Packit dd8086
/* ----------- Test of MMC driver enhancements , december 2009 ------------- */
Packit dd8086
Packit dd8086
Packit dd8086
/* OBTRUSIVE */
Packit dd8086
/**
Packit dd8086
   This function bundles tests for the capability to perform MMC commands
Packit dd8086
   of payload direction SCSI_MMC_DATA_WRITE and to detect the sense reply of
Packit dd8086
   MMC commands, which indicates error causes or important drive events.
Packit dd8086
Packit dd8086
   The default configuration is not very obtrusive to the drive, although the
Packit dd8086
   drive should not be used for other operations at the same time.
Packit dd8086
   There are static variables in this function which enable additional
Packit dd8086
   more obtrusive tests or simulate the lack of write capabilities.
Packit dd8086
   See the statements about obtrusiveness and undesired side effects above
Packit dd8086
   the descriptions of the single functions.
Packit dd8086
Packit dd8086
   @param psz_drive_path  a drive address suitable for cdio_open_am()
Packit dd8086
   @param flag            bit0= verbose
Packit dd8086
   @return                0= no severe failure
Packit dd8086
                          else an proposal for an exit() value is returned
Packit dd8086
*/
Packit dd8086
static int
Packit dd8086
test_write(char *psz_drive_path, unsigned int i_flag)
Packit dd8086
{
Packit dd8086
    unsigned int i_sense_avail = 0;
Packit dd8086
    unsigned int i_sense_valid;
Packit dd8086
    int i_ret;
Packit dd8086
    bool b_verbose = !!(i_flag & 1);
Packit dd8086
    int old_log_level = cdio_loglevel_default;
Packit dd8086
    cdio_mmc_request_sense_t sense_reply;
Packit dd8086
    CdIo_t *p_cdio;
Packit dd8086
Packit dd8086
    /* If true, then the tray will get ejected and loaded again */
Packit dd8086
    static bool with_tray_dance = false;
Packit dd8086
Packit dd8086
    /* If true, then an asynchronous test loop will wait 30 s for loaded media */
Packit dd8086
    static bool test_cycle_with_media = false;
Packit dd8086
Packit dd8086
    /* If true, then a lack of w-permission will be emulated by using the
Packit dd8086
       insufficient access mode "IOCTL" */
Packit dd8086
    static bool emul_lack_of_wperm = false;
Packit dd8086
Packit dd8086
    p_cdio = cdio_open_am(psz_drive_path, DRIVER_DEVICE, "MMC_RDWR_EXCL");
Packit dd8086
    if (!p_cdio)
Packit dd8086
	i_ret = SKIP_TEST - 16;
Packit dd8086
    else  {
Packit dd8086
	const char *psz_access_mode = cdio_get_arg(p_cdio, "access-mode");
Packit dd8086
Packit dd8086
	if (0 != strncmp(psz_access_mode,
Packit dd8086
			 "MMC_RDWR_EXCL", strlen("MMC_RDWR_EXCL"))) {
Packit dd8086
	    fprintf(stderr,
Packit dd8086
		    "Got %s; Should get back %s, the access mode requested.\n",
Packit dd8086
		    psz_access_mode, "MMC_RDWR_EXCL");
Packit dd8086
	    i_ret = 1; goto ex;
Packit dd8086
	}
Packit dd8086
Packit dd8086
	/* The further code produces some intentional failures which should not be
Packit dd8086
       reported by mmc_run_cmd() as INFO messages.
Packit dd8086
	*/
Packit dd8086
	cdio_loglevel_default = CDIO_LOG_WARN;
Packit dd8086
Packit dd8086
	/* Test availability of sense reply in case of unready drive.
Packit dd8086
	   E.g. if the tray is already ejected.
Packit dd8086
	*/
Packit dd8086
	i_ret = test_unit_ready(p_cdio, &i_sense_avail, &sense_reply, b_verbose);
Packit dd8086
	if (0 != i_ret && i_sense_avail < 18) {
Packit dd8086
	    fprintf(stderr,
Packit dd8086
		    "Error: Drive not ready. Only %u sense bytes. Expected >= 18.\n",
Packit dd8086
		    i_sense_avail);
Packit dd8086
	    i_ret = 2; goto ex;
Packit dd8086
	}
Packit dd8086
Packit dd8086
	if (emul_lack_of_wperm) { /* To check behavior with lack of w-permission */
Packit dd8086
	    printf("-- test_write: SIMULATING LACK OF WRITE CAPABILITIES by access mode IOCTL\n");
Packit dd8086
	    cdio_destroy(p_cdio);
Packit dd8086
	    p_cdio = cdio_open_am(psz_drive_path, DRIVER_DEVICE, "IOCTL");
Packit dd8086
	}
Packit dd8086
Packit dd8086
Packit dd8086
	/* Test write permission */ /* Try whether a mode page 2Ah can be set */
Packit dd8086
	i_ret = test_rwr_mode_page(p_cdio, b_verbose);
Packit dd8086
	if (i_ret <= 0) {
Packit dd8086
	    if (i_ret < 0) {
Packit dd8086
		fprintf(stderr, "Error: test_rwr_mode_page() had severe failure.\n");
Packit dd8086
		i_ret = 3; goto ex;
Packit dd8086
	    }
Packit dd8086
	    printf("-- Warning: test_rwr_mode_page() had minor failure.\n");
Packit dd8086
	}
Packit dd8086
Packit dd8086
Packit dd8086
	if (with_tray_dance) {
Packit dd8086
	    /* More surely provoke a non-trivial sense reply */
Packit dd8086
	    if (test_cycle_with_media) {
Packit dd8086
		/* Eject, wait, load, watch by test unit ready loop */
Packit dd8086
		i_ret = test_eject_load_cycle(p_cdio, 2 | b_verbose);
Packit dd8086
		if (i_ret <= 0) {
Packit dd8086
		    if (i_ret < 0) {
Packit dd8086
			fprintf(stderr,
Packit dd8086
				"Error: test_eject_load_cycle() had severe failure.\n");
Packit dd8086
			i_ret = 4; goto ex;
Packit dd8086
		    }
Packit dd8086
		    printf("Warning: test_eject_load_cycle() had minor failure.\n");
Packit dd8086
		}
Packit dd8086
	    } else {
Packit dd8086
		/* Eject, test for proper unreadiness, load */
Packit dd8086
		i_ret = test_eject_test_load(p_cdio, b_verbose);
Packit dd8086
		if (i_ret <= 0) {
Packit dd8086
		    if (i_ret < 0) {
Packit dd8086
			fprintf(stderr,
Packit dd8086
				"Error: test_eject_test_load() had severe failure.\n");
Packit dd8086
			i_ret = 5; goto ex;
Packit dd8086
		    }
Packit dd8086
		    printf("Warning: test_eject_test_load() had minor failure.\n");
Packit dd8086
		}
Packit dd8086
		/* Wait for drive attention */
Packit dd8086
		wait_for_drive(p_cdio, 15, 2 | b_verbose);
Packit dd8086
	    }
Packit dd8086
	}
Packit dd8086
Packit dd8086
	/* How are we, finally ? */
Packit dd8086
	i_ret = test_unit_ready(p_cdio, &i_sense_valid, &sense_reply, b_verbose);
Packit dd8086
	if ((i_flag & 1) && 0 != i_ret && 2 == sense_reply.sense_key &&
Packit dd8086
	    0x3a == sense_reply.asc)
Packit dd8086
	    fprintf(stderr, "test_unit_ready: Note: No loaded media detected.\n");
Packit dd8086
	i_ret = 0;
Packit dd8086
    }
Packit dd8086
Packit dd8086
  ex:;
Packit dd8086
    cdio_loglevel_default = old_log_level;
Packit dd8086
    cdio_destroy(p_cdio);
Packit dd8086
    return i_ret;
Packit dd8086
}
Packit dd8086
Packit dd8086
/* ---------------------------      main       ----------------------------- */
Packit dd8086
Packit dd8086
int
Packit dd8086
main(int argc, const char *argv[])
Packit dd8086
{
Packit dd8086
  CdIo_t *p_cdio;
Packit dd8086
  char **ppsz_drives=NULL;
Packit dd8086
  int ret;
Packit dd8086
  int exitrc = 0;
Packit dd8086
  bool b_verbose = (argc > 1);
Packit dd8086
Packit dd8086
  cdio_loglevel_default = b_verbose ? CDIO_LOG_DEBUG : CDIO_LOG_WARN;
Packit dd8086
Packit dd8086
  ppsz_drives = cdio_get_devices(DRIVER_DEVICE);
Packit dd8086
  if (!ppsz_drives) {
Packit dd8086
    printf("Can't find a CD-ROM drive. Skipping test.\n");
Packit dd8086
    exit(SKIP_TEST);
Packit dd8086
  }
Packit dd8086
Packit dd8086
  p_cdio = cdio_open(ppsz_drives[0], DRIVER_DEVICE);
Packit dd8086
  if (p_cdio) {
Packit dd8086
    const char *psz_have_mmc = cdio_get_arg(p_cdio, "mmc-supported?");
Packit dd8086
Packit dd8086
    if ( psz_have_mmc
Packit dd8086
	 && 0 == strncmp("true", psz_have_mmc, sizeof("true")) ) {
Packit dd8086
Packit dd8086
	/* Test the MMC enhancements of version 0.83 in december 2009 */
Packit dd8086
	ret = test_write(ppsz_drives[0],
Packit dd8086
			cdio_loglevel_default == CDIO_LOG_DEBUG);
Packit dd8086
	if (ret != 0) exit(ret + 16);
Packit dd8086
    }
Packit dd8086
Packit dd8086
    cdio_destroy(p_cdio);
Packit dd8086
Packit dd8086
  } else {
Packit dd8086
    fprintf(stderr, "cdio_open('%s') failed\n", ppsz_drives[0]);
Packit dd8086
    exit(2);
Packit dd8086
  }
Packit dd8086
Packit dd8086
  cdio_free_device_list(ppsz_drives);
Packit dd8086
Packit dd8086
  return exitrc;
Packit dd8086
}