1 /* -*- C -*-
2   Copyright (C) 2009 Thomas Schmitt <scdbackup@gmx.net>
3   Copyright (C) 2010, 2012-2013, 2017 Rocky Bernstein <rocky@gnu.org>
4 
5   This program is free software: you can redistribute it and/or modify
6   it under the terms of the GNU General Public License as published by
7   the Free Software Foundation, either version 3 of the License, or
8   (at your option) any later version.
9 
10   This program is distributed in the hope that it will be useful,
11   but WITHOUT ANY WARRANTY; without even the implied warranty of
12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   GNU General Public License for more details.
14 
15   You should have received a copy of the GNU General Public License
16   along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 */
18 
19 /*
20    Regression test for MMC commands involving read/write access.
21 */
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #define __CDIO_CONFIG_H__ 1
25 #endif
26 
27 #ifdef HAVE_STDIO_H
28 #include <stdio.h>
29 #endif
30 #ifdef HAVE_SYS_TYPES_H
31 #include <sys/types.h>
32 #endif
33 #ifdef HAVE_STDLIB_H
34 #include <stdlib.h>
35 #endif
36 #ifdef HAVE_UNISTD_H
37 #include <unistd.h>
38 #endif
39 #ifdef HAVE_STRING_H
40 #include <string.h>
41 #endif
42 #ifdef _WIN32
43 #  undef HAVE_SLEEP
44 #endif
45 #if !defined(HAVE_SLEEP)
46 #  if defined(_WIN32)
47 #     include <windows.h>
48 #     define sleep(s) Sleep(1000*s)
49 #  elif defined(HAVE_USLEEP)
50 #     define sleep(s) usleep(1000000*s)
51 #  else
52 #     define sleep(s) { int i; for(i=0; i<=1000*s; i++); }
53 #  endif
54 #endif
55 
56 #include <cdio/cdio.h>
57 #include <cdio/logging.h>
58 #include <cdio/mmc_cmds.h>
59 
60 #define SKIP_TEST 77
61 
62 /* gcc may warn if no prototypes are given before function definition */
63 
64 static int handle_outcome(CdIo_t *p_cdio, int i_status,
65 			       unsigned int *pi_sense_avail,
66 			       cdio_mmc_request_sense_t * p_sense_reply,
67 			       unsigned int i_flag);
68 
69 static int load_eject(CdIo_t *p_cdio, unsigned int *pi_sense_avail,
70 		      cdio_mmc_request_sense_t * p_sense_reply,
71 		      unsigned int i_flag);
72 
73 static void print_status_sense(int i_status, int sense_valid,
74 				    cdio_mmc_request_sense_t *,
75 				    unsigned int i_flag);
76 
77 static int test_eject_load_cycle(CdIo_t *p_cdio, unsigned int i_flag);
78 
79 static int test_eject_test_load(CdIo_t *p_cdio, unsigned int i_flag);
80 
81 static int test_mode_select(CdIo_t *p_cdio,
82 			    unsigned int *pi_sense_avail,
83 			    cdio_mmc_request_sense_t *p_sense_reply,
84 			    unsigned char *p_buf, unsigned int i_size,
85 			    unsigned int i_flag);
86 
87 static int mode_sense(CdIo_t *p_cdio, unsigned int *pi_sense_avail,
88 		      cdio_mmc_request_sense_t * p_sense_reply,
89 		      unsigned int i_page_code,
90 		      unsigned int subpage_code, int i_alloc_len,
91 		      unsigned char *buf, int *i_size,
92 		      unsigned int i_flag);
93 
94 static int test_unit_ready(CdIo_t *p_cdio,
95 			   unsigned int *pi_sense_avail,
96 			   cdio_mmc_request_sense_t * p_sense_reply,
97 			   unsigned int i_flag);
98 
99 static int test_rwr_mode_page(CdIo_t *p_cdio, unsigned int i_flag);
100 
101 static int test_write(char *psz_drive_path, unsigned int i_flag);
102 
103 static int wait_for_drive(CdIo_t *p_cdio, unsigned int max_tries, unsigned int i_flag);
104 
105 /* ------------------------- Helper functions ---------------------------- */
106 
107 
108 /* @param i_flag         bit0= verbose
109 */
110 static int
handle_outcome(CdIo_t * p_cdio,driver_return_code_t i_status,unsigned int * pi_sense_avail,cdio_mmc_request_sense_t * p_sense_reply,unsigned int i_flag)111 handle_outcome(CdIo_t *p_cdio, driver_return_code_t i_status,
112                     unsigned int *pi_sense_avail,
113 		    cdio_mmc_request_sense_t * p_sense_reply,
114 		    unsigned int i_flag)
115 {
116     cdio_mmc_request_sense_t *p_temp_sense_reply = NULL;
117     *pi_sense_avail = mmc_last_cmd_sense(p_cdio, &p_temp_sense_reply);
118     print_status_sense(i_status, *pi_sense_avail, p_temp_sense_reply, i_flag & 1);
119     if (18 <= *pi_sense_avail) {
120         memset(p_sense_reply, 0, sizeof(cdio_mmc_request_sense_t));
121         memcpy(p_sense_reply, p_temp_sense_reply, sizeof(cdio_mmc_request_sense_t));
122     } else
123         memset(p_sense_reply, 0, sizeof(cdio_mmc_request_sense_t));
124     cdio_free(p_temp_sense_reply);
125     return i_status;
126 }
127 
128 
129 /**
130    @param flag bit0= verbose
131 */
132 static void
print_status_sense(int i_status,int sense_valid,cdio_mmc_request_sense_t * p_sense_reply,unsigned int i_flag)133 print_status_sense(int i_status, int sense_valid,
134                         cdio_mmc_request_sense_t *p_sense_reply,
135 			unsigned int i_flag)
136 {
137     if (!(i_flag & 1))
138 	return;
139     printf("return= %d , sense(%d)", i_status, sense_valid);
140     if (sense_valid >= 14)
141 	printf(":  KEY=%s (%1.1X), ASC= %2.2X , ASCQ= %2.2X",
142 		mmc_sense_key2str[p_sense_reply->sense_key],
143 		p_sense_reply->sense_key,
144 		p_sense_reply->asc,
145 		p_sense_reply->ascq);
146     printf("\n");
147 }
148 
149 
150 /* --------------------------- MMC commands ------------------------------ */
151 
152 
153 /* OBTRUSIVE. PHYSICAL EFFECT: DANGER OF HUMAN INJURY */
154 /**
155    @param i_flag bit0= verbose
156                  bit1= Asynchronous operation
157                  bit2= Load (else Eject)
158    @return      return value of mmc_run_cmd()
159 */
160 static int
load_eject(CdIo_t * p_cdio,unsigned int * pi_sense_avail,cdio_mmc_request_sense_t * p_sense_reply,unsigned int i_flag)161 load_eject(CdIo_t *p_cdio, unsigned int *pi_sense_avail,
162 	   cdio_mmc_request_sense_t *p_sense_reply,
163 	   unsigned int i_flag)
164 {
165   int i_status;
166   bool b_eject = !!(i_flag & 4);
167   bool b_immediate = !!(i_flag & 2);
168 
169   i_status = mmc_start_stop_unit(p_cdio, b_eject, b_immediate, 0, 0);
170 
171   if (i_flag & 1)
172     printf("load_eject(0x%X) ... ", i_flag);
173 
174   return handle_outcome(p_cdio, i_status, pi_sense_avail, p_sense_reply,
175 			i_flag & 1);
176 }
177 
178 
179 /* BARELY OBTRUSIVE: MIGHT RUIN A SIMULTANEOUS BURNING OPERATION ON THE DRIVE */
180 /**
181    Fetch a mode page or a part of it from the drive.
182    @param i_alloc_len  The number of bytes to be requested from the drive and to
183                        be copied into parameter buf.
184                        This has to include the 8 bytes of header and may not
185                        be less than 10.
186    @param p_buf        Will contain at most alloc_len many bytes. The first 8 are
187                        a Mode Parameter Header as of SPC-3 7.4.3, table 240.
188                        The further bytes are the mode page, typically as of
189                        MMC-5 7.2. There are meanwhile deprecated mode pages which
190                        appear only in older versions of MMC.
191    @param i_size       Will return the number of actually read bytes resp. the
192                        number of available bytes. See flag bit1.
193    @param i_flag       bit0= verbose
194                        bit1= Peek mode:
195                            Reply number of available bytes in *i_size and not
196                            the number of actually read bytes.
197    @return             return value of mmc_run_cmd(),
198                        or other driver_return_code_t
199 */
200 static driver_return_code_t
mode_sense(CdIo_t * p_cdio,unsigned int * pi_sense_avail,cdio_mmc_request_sense_t * p_sense_reply,unsigned int i_page_code,unsigned int subpage_code,int i_alloc_len,unsigned char * p_buf,int * pi_size,unsigned int i_flag)201 mode_sense(CdIo_t *p_cdio, unsigned int *pi_sense_avail,
202 	   cdio_mmc_request_sense_t *p_sense_reply,
203 	   unsigned int i_page_code, unsigned int subpage_code, int i_alloc_len,
204 	   unsigned char *p_buf, int *pi_size, unsigned int i_flag)
205 {
206   driver_return_code_t i_status;
207 
208   if (i_alloc_len < 10)
209     return DRIVER_OP_BAD_PARAMETER;
210 
211   if (i_flag & 1)
212     printf("mode_sense(0x%X, %X, %d) ... ",
213 	   i_page_code, subpage_code, i_alloc_len);
214 
215   i_status = mmc_mode_sense_10(p_cdio, p_buf, i_alloc_len, i_page_code);
216   handle_outcome(p_cdio, i_status, pi_sense_avail, p_sense_reply, i_flag & 1);
217   if (DRIVER_OP_SUCCESS != i_status)
218     return i_status;
219   if (i_flag & 2)
220     *pi_size = p_buf[9] + 10;                  /* MMC-5 7.2.3 */
221   else
222     *pi_size = p_buf[0] * 256 + p_buf[1] + 2;  /* SPC-3 7.4.3, table 240 */
223   return i_status;
224 }
225 
226 
227 /* OBTRUSIVE. RUINS A SIMULTANEOUS BURNING OPERATION ON THE DRIVE
228    and might return minor failure with -ROM drives */
229 /**
230    Send a mode page to the drive.
231    @param buf        Contains the payload bytes. The first 8 shall be a Mode
232                      Parameter Header as of SPC-3 7.4.3, table 240.
233                      The further bytes are the mode page, typically as of
234                      MMC-5 7.2. There are meanwhile deprecated mode pages which
235                      appear only in older versions of MMC.
236    @param i_size   The number of bytes in buf.
237    @param flag       bit0= verbose
238    @return           return value of mmc_run_cmd(),
239                      or other driver_return_code_t
240 */
241 static int
test_mode_select(CdIo_t * p_cdio,unsigned int * pi_sense_avail,cdio_mmc_request_sense_t * p_sense_reply,unsigned char * p_buf,unsigned int i_size,unsigned int i_flag)242 test_mode_select(CdIo_t *p_cdio,
243                  unsigned int *pi_sense_avail,
244 		 cdio_mmc_request_sense_t *p_sense_reply,
245                  unsigned char *p_buf, unsigned int i_size, unsigned int i_flag)
246 {
247     int i_status, i;
248 
249     if (i_size < 10)
250 	return DRIVER_OP_BAD_PARAMETER;
251 
252     if (i_flag & 1) {
253 	printf("-- test_mode_select to drive: %d bytes\n", i_size);
254 	for (i = 0; i < i_size; i++) {
255 	    printf("%2.2X ", (unsigned int) p_buf[i]);
256 	    if ((i % 20) == 19)
257 		printf("\n");
258 	}
259 	if ((i % 20))
260 	    printf("\n");
261     }
262 
263     if (i_flag & 1)
264 	printf("-- test_mode_select(0x%X, %d, %d) ... ",
265 	       (unsigned int) p_buf[8], (unsigned int) p_buf[9], i_size);
266     i_status = mmc_mode_select_10(p_cdio, p_buf, i_size, 0x10, 10000);
267     return handle_outcome(p_cdio, i_status, pi_sense_avail, p_sense_reply,
268 			  i_flag & 1);
269 }
270 
271 /* UNOBTRUSIVE */
272 /**
273    @param pi_sense_avail  Number of available sense bytes
274                           (18 get copied if all 18 exist)
275    @param p_sense_reply  eventual sense bytes
276    @param i_flag          bit0= verbose
277    @return             return value of mmc_run_cmd()
278  */
279 static int
test_unit_ready(CdIo_t * p_cdio,unsigned int * pi_sense_avail,cdio_mmc_request_sense_t * p_sense_reply,unsigned int i_flag)280 test_unit_ready(CdIo_t *p_cdio,
281 		unsigned int *pi_sense_avail,
282 		cdio_mmc_request_sense_t *p_sense_reply,
283 		unsigned int i_flag)
284 {
285   int i_status;
286 
287   if (i_flag & 1)
288     printf("-- test_unit_ready ... ");
289   i_status = mmc_test_unit_ready(p_cdio, 0);
290 
291   return handle_outcome(p_cdio, i_status, pi_sense_avail, p_sense_reply,
292                              i_flag & 1);
293 }
294 
295 
296 /* --------------------------- Larger gestures ----------------------------- */
297 
298 
299 /* UNOBTRUSIVE */
300 /**
301    Watch drive by test unit ready loop until ready, no media or timeout.
302    @param flag bit0= verbose
303                bit1= expect media (do not end on no-media sense)
304    @return     1= all seems well , 0= minor failure , -1= severe failure
305 */
306 static int
wait_for_drive(CdIo_t * p_cdio,unsigned int i_max_tries,unsigned int i_flag)307 wait_for_drive(CdIo_t *p_cdio, unsigned int i_max_tries, unsigned int i_flag)
308 {
309     int i_ret, i;
310     unsigned int i_sense_avail;
311     cdio_mmc_request_sense_t sense_reply;
312 
313     for (i = 0; i < i_max_tries; i++) {
314 	i_ret = test_unit_ready(p_cdio, &i_sense_avail, &sense_reply, !!(i_flag & 1));
315 	if (i_ret == 0) /* Unit is ready */
316 	    return 1;
317 	if (i_sense_avail < 18)
318 	    return -1;
319 	if (2 == sense_reply.sense_key && 0x04 == sense_reply.asc) {
320 
321 	    /* Not ready */;
322 
323 	} else if (6 == sense_reply.sense_key && 0x28 == sense_reply.asc &&
324 		   0 == sense_reply.ascq) {
325 
326 	    /* Media change notice = try again */;
327 
328 	} else if (2 == sense_reply.sense_key && 0x3a == sense_reply.asc) {
329 
330 	    /* Medium not present */;
331 
332 	    if (!(i_flag & 2))
333 		return 1;
334 	} else if (0 == sense_reply.sense_key && 0 == sense_reply.asc) {
335 
336 	    /* Error with no sense */;
337 
338 	    return -1;
339 	    break;
340 	} else {
341 
342 	    /* Other error */;
343 
344 	    return 0;
345 	}
346 	sleep(1);
347     }
348     fprintf(stderr, "wait_for_drive: Drive not ready after %d retries\n",
349 	    i_max_tries);
350     return -1;
351 }
352 
353 
354 /* OBTRUSIVE. Opens and closes drive door - watch your fingers! */
355 /**
356    Eject, wait, load asynchronously, and watch by test unit ready loop.
357    @param i_flag bit0= verbose
358                  bit1= expect media (do not end on no-media sense)
359    @return   1= all seems well , 0= minor failure , -1= severe failure
360 */
361 static int
test_eject_load_cycle(CdIo_t * p_cdio,unsigned int i_flag)362 test_eject_load_cycle(CdIo_t *p_cdio, unsigned int i_flag)
363 {
364     int i_ret;
365     unsigned int i_sense_avail;
366     cdio_mmc_request_sense_t sense_reply;
367 
368     /* Eject synchronously */
369     printf("test_eject_load_cycle: WARNING: EJECTING THE TRAY !\n");
370     sleep(2);
371     load_eject(p_cdio, &i_sense_avail, &sense_reply, 0 | (i_flag & 1));
372 
373     printf("test_eject_load_cycle: waiting for 5 seconds. DO NOT TOUCH THE TRAY !\n");
374     sleep(3);
375 
376     /* Load asynchronously */
377     printf("test_eject_load_cycle: WARNING: LOADING THE TRAY !\n");
378     sleep(2);
379     load_eject(p_cdio, &i_sense_avail, &sense_reply, 4 | 2 | (i_flag & 1));
380 
381     /* Wait for drive attention */
382     i_ret = wait_for_drive(p_cdio, 30, i_flag & 3);
383     return i_ret;
384 }
385 
386 
387 /* OBTRUSIVE , PHYSICAL EFFECT , DANGER OF HUMAN INJURY */
388 /**
389    Eject, wait, test, load. All synchronously.
390    @param flag bit0= verbose
391    @return    1= all seems well , 0= minor failure , -1= severe failure
392 */
393 static int
test_eject_test_load(CdIo_t * p_cdio,unsigned int i_flag)394 test_eject_test_load(CdIo_t *p_cdio, unsigned int i_flag)
395 {
396     int i_ret;
397     unsigned int i_sense_avail;
398     cdio_mmc_request_sense_t sense_reply;
399 
400   /* Eject synchronously */
401   printf("test_eject_test_load: WARNING: EJECTING THE TRAY !\n");
402   sleep(2);
403   load_eject(p_cdio, &i_sense_avail, &sense_reply, 0 | (i_flag & 1));
404 
405   printf("test_eject_test_load: waiting for 5 seconds. DO NOT TOUCH THE TRAY !\n");
406   sleep(3);
407 
408   i_ret = test_unit_ready(p_cdio, &i_sense_avail, &sense_reply, i_flag & 1);
409   if (i_ret == 0) {
410     fprintf(stderr,
411             "test_eject_test_load: Drive ready although tray ejected.\n");
412     fprintf(stderr,
413             "test_eject_test_load: Test aborted. Tray will stay ejected.\n");
414     return -1;
415   }
416   if (i_ret == 0 || i_sense_avail < 18) {
417     fprintf(stderr,
418  "test_eject_test_load: Only %d sense reply bytes returned. Expected >= 18.\n",
419             i_sense_avail);
420     fprintf(stderr,
421             "test_eject_test_load: Test aborted. Tray will stay ejected.\n");
422     return -1;
423   }
424 
425   /* Load synchronously */
426   fprintf(stderr,
427     "test_eject_test_load: WARNING: LOADING THE TRAY !\n");
428   sleep(2);
429   load_eject(p_cdio, &i_sense_avail, &sense_reply, 4 | (i_flag & 1));
430 
431   return 1;
432 }
433 
434 
435 /* OBTRUSIVE */
436 /**
437    Read Mode Page 05h "Write Parameters", change a value, write the page,
438    check effect, restore old value, check again.
439    @param flag bit0= verbose
440    @return    1= all seems well , 0= minor failure , -1= severe failure
441 */
442 static int
test_rwr_mode_page(CdIo_t * p_cdio,unsigned int i_flag)443 test_rwr_mode_page(CdIo_t *p_cdio, unsigned int i_flag)
444 {
445     int i_ret;
446     unsigned int i_sense_avail = 0;
447     int page_code = 5, subpage_code = 0, i_alloc_len, i_size = 0;
448     int write_type, final_return = 1, new_write_type, old_i_size;
449     cdio_mmc_request_sense_t sense_reply;
450     unsigned char buf[265], old_buf[265];        /* page size is max. 255 + 10 */
451     static char w_types[4][8] = {"Packet", "TAO", "SAO", "Raw"};
452 
453     memset(buf, 0, sizeof(buf));
454 
455     i_alloc_len = 10;
456     i_ret = mode_sense(p_cdio, &i_sense_avail, &sense_reply,
457 			    page_code, subpage_code, i_alloc_len,
458 			    buf, &i_size, 2 | (i_flag & 1));
459     if (i_ret != 0) {
460 	fprintf(stderr,
461 		"-- test_rwr_mode_page: Cannot obtain mode page 05h.\n");
462 	return 0;
463     }
464     i_alloc_len = (i_size <= sizeof(buf)) ? i_size : sizeof(buf);
465     i_ret = mode_sense(p_cdio, &i_sense_avail, &sense_reply,
466 			    page_code, subpage_code, i_alloc_len,
467 			    buf, &i_size, (i_flag & 1));
468     if (i_ret != 0) {
469 	fprintf(stderr,
470 		"-- test_rwr_mode_page: Cannot obtain mode page 05h.\n");
471 	return 0;
472     }
473     memcpy(old_buf, buf, sizeof(buf));
474     old_i_size = i_size;
475 
476     write_type = buf[10] & 0x0f;
477     if (i_flag & 1)
478 	printf("test_rwr_mode_page: Write type = %d (%s)\n",
479 	       write_type, write_type < 4 ? w_types[write_type] : "Reserved");
480 
481     /* Choose a conservative CD writer setting.
482      */
483     memset(buf, 0, 8);
484     if (write_type == 1) { /* is TAO -> make SAO */
485 	buf[10] = (buf[10] & 0xf0) | 2;   /* SAO */
486 	new_write_type = 2;
487     } else {
488 	buf[10] = (buf[10] & 0xf0) | 1;   /* TAO */
489 	new_write_type = 1;
490     }
491     buf[11] = 4;                        /* Track Mode 4, no multi-session,
492 					   variable Packet size */
493     buf[12] = 8;                        /* Data Block Type : CD-ROM Mode 1 */
494     buf[16] = 0;                        /* Session Format : "CD-DA or CD-ROM" */
495 
496     i_ret = test_mode_select(p_cdio, &i_sense_avail, &sense_reply,
497 			     buf, i_size, i_flag & 1);
498     if (i_ret != 0) {
499 	fprintf(stderr,
500 		"-- test_rwr_mode_page: Cannot set mode page 05h.\n");
501 	if (DRIVER_OP_NOT_PERMITTED == i_ret) {
502 	    fprintf(stderr,
503 		    "test_rwr_mode_page: DRIVER_OP_NOT_PERMITTED with MODE SELECT.\n");
504 	    return -1;
505 	}
506 	return 0;
507     }
508 
509     /* Read mode page and check whether effect visible in buf[10] */
510     i_ret = mode_sense(p_cdio, &i_sense_avail, &sense_reply,
511 			    page_code, subpage_code, i_alloc_len,
512 			    buf, &i_size, (i_flag & 1));
513     if (0 != i_ret) {
514 	fprintf(stderr, "test_rwr_mode_page: Cannot obtain mode page 05h.\n");
515 	final_return = final_return > 0 ? 0 : final_return;
516     } else if ((buf[10] & 0xf) != new_write_type) {
517 	fprintf(stderr,
518 		"test_rwr_mode_page: Mode page did not get into effect. (expected %d, got %d)\n",
519 		new_write_type, buf[10] & 0xf);
520 	/* One of my DVD burners does this if no media is loaded */
521 	final_return = final_return > 0 ? 0 : final_return;
522     }
523 
524     /* Restore old mode page */
525     i_ret = test_mode_select(p_cdio, &i_sense_avail, &sense_reply,
526 			     old_buf, old_i_size, i_flag & 1);
527     if (0 != i_ret) {
528 	fprintf(stderr, "test_rwr_mode_page: Cannot set mode page 05h.\n");
529 	if (i_ret == DRIVER_OP_NOT_PERMITTED) {
530 	    fprintf(stderr,
531 		    "test_rwr_mode_page: DRIVER_OP_NOT_PERMITTED with MODE SELECT.\n");
532 	    return -1;
533 	}
534 	final_return = final_return > 0 ? 0 : final_return;
535     }
536 
537     /* Read mode page and check whether old_buf is in effect again */
538     i_ret = mode_sense(p_cdio, &i_sense_avail, &sense_reply,
539 		       page_code, subpage_code, i_alloc_len,
540 		       buf, &i_size, (i_flag & 1));
541     if (0 != i_ret) {
542 	fprintf(stderr, "test_rwr_mode_page: Cannot obtain mode page 05h.\n");
543 	return final_return > 0 ? 0 : final_return;
544     } else if (memcmp(buf, old_buf, i_size) != 0) {
545 	fprintf(stderr,
546 		"test_rwr_mode_page: Mode page was not restored to old state.\n");
547 	final_return = final_return > 0 ? -1 : final_return;
548     }
549     if (i_flag & 1)
550 	printf("test_rwr_mode_page: Mode page 2Ah restored to previous state\n");
551     return final_return;
552 }
553 
554 
555 /* ----------- Test of MMC driver enhancements , december 2009 ------------- */
556 
557 
558 /* OBTRUSIVE */
559 /**
560    This function bundles tests for the capability to perform MMC commands
561    of payload direction SCSI_MMC_DATA_WRITE and to detect the sense reply of
562    MMC commands, which indicates error causes or important drive events.
563 
564    The default configuration is not very obtrusive to the drive, although the
565    drive should not be used for other operations at the same time.
566    There are static variables in this function which enable additional
567    more obtrusive tests or simulate the lack of write capabilities.
568    See the statements about obtrusiveness and undesired side effects above
569    the descriptions of the single functions.
570 
571    @param psz_drive_path  a drive address suitable for cdio_open_am()
572    @param flag            bit0= verbose
573    @return                0= no severe failure
574                           else an proposal for an exit() value is returned
575 */
576 static int
test_write(char * psz_drive_path,unsigned int i_flag)577 test_write(char *psz_drive_path, unsigned int i_flag)
578 {
579     unsigned int i_sense_avail = 0;
580     unsigned int i_sense_valid;
581     int i_ret;
582     bool b_verbose = !!(i_flag & 1);
583     int old_log_level = cdio_loglevel_default;
584     cdio_mmc_request_sense_t sense_reply;
585     CdIo_t *p_cdio;
586 
587     /* If true, then the tray will get ejected and loaded again */
588     static bool with_tray_dance = false;
589 
590     /* If true, then an asynchronous test loop will wait 30 s for loaded media */
591     static bool test_cycle_with_media = false;
592 
593     /* If true, then a lack of w-permission will be emulated by using the
594        insufficient access mode "IOCTL" */
595     static bool emul_lack_of_wperm = false;
596 
597     p_cdio = cdio_open_am(psz_drive_path, DRIVER_DEVICE, "MMC_RDWR_EXCL");
598     if (!p_cdio)
599 	i_ret = SKIP_TEST - 16;
600     else  {
601 	const char *psz_access_mode = cdio_get_arg(p_cdio, "access-mode");
602 
603 	if (0 != strncmp(psz_access_mode,
604 			 "MMC_RDWR_EXCL", strlen("MMC_RDWR_EXCL"))) {
605 	    fprintf(stderr,
606 		    "Got %s; Should get back %s, the access mode requested.\n",
607 		    psz_access_mode, "MMC_RDWR_EXCL");
608 	    i_ret = 1; goto ex;
609 	}
610 
611 	/* The further code produces some intentional failures which should not be
612        reported by mmc_run_cmd() as INFO messages.
613 	*/
614 	cdio_loglevel_default = CDIO_LOG_WARN;
615 
616 	/* Test availability of sense reply in case of unready drive.
617 	   E.g. if the tray is already ejected.
618 	*/
619 	i_ret = test_unit_ready(p_cdio, &i_sense_avail, &sense_reply, b_verbose);
620 	if (0 != i_ret && i_sense_avail < 18) {
621 	    fprintf(stderr,
622 		    "Error: Drive not ready. Only %u sense bytes. Expected >= 18.\n",
623 		    i_sense_avail);
624 	    i_ret = 2; goto ex;
625 	}
626 
627 	if (emul_lack_of_wperm) { /* To check behavior with lack of w-permission */
628 	    printf("-- test_write: SIMULATING LACK OF WRITE CAPABILITIES by access mode IOCTL\n");
629 	    cdio_destroy(p_cdio);
630 	    p_cdio = cdio_open_am(psz_drive_path, DRIVER_DEVICE, "IOCTL");
631 	}
632 
633 
634 	/* Test write permission */ /* Try whether a mode page 2Ah can be set */
635 	i_ret = test_rwr_mode_page(p_cdio, b_verbose);
636 	if (i_ret <= 0) {
637 	    if (i_ret < 0) {
638 		fprintf(stderr, "Error: test_rwr_mode_page() had severe failure.\n");
639 		i_ret = 3; goto ex;
640 	    }
641 	    printf("-- Warning: test_rwr_mode_page() had minor failure.\n");
642 	}
643 
644 
645 	if (with_tray_dance) {
646 	    /* More surely provoke a non-trivial sense reply */
647 	    if (test_cycle_with_media) {
648 		/* Eject, wait, load, watch by test unit ready loop */
649 		i_ret = test_eject_load_cycle(p_cdio, 2 | b_verbose);
650 		if (i_ret <= 0) {
651 		    if (i_ret < 0) {
652 			fprintf(stderr,
653 				"Error: test_eject_load_cycle() had severe failure.\n");
654 			i_ret = 4; goto ex;
655 		    }
656 		    printf("Warning: test_eject_load_cycle() had minor failure.\n");
657 		}
658 	    } else {
659 		/* Eject, test for proper unreadiness, load */
660 		i_ret = test_eject_test_load(p_cdio, b_verbose);
661 		if (i_ret <= 0) {
662 		    if (i_ret < 0) {
663 			fprintf(stderr,
664 				"Error: test_eject_test_load() had severe failure.\n");
665 			i_ret = 5; goto ex;
666 		    }
667 		    printf("Warning: test_eject_test_load() had minor failure.\n");
668 		}
669 		/* Wait for drive attention */
670 		wait_for_drive(p_cdio, 15, 2 | b_verbose);
671 	    }
672 	}
673 
674 	/* How are we, finally ? */
675 	i_ret = test_unit_ready(p_cdio, &i_sense_valid, &sense_reply, b_verbose);
676 	if ((i_flag & 1) && 0 != i_ret && 2 == sense_reply.sense_key &&
677 	    0x3a == sense_reply.asc)
678 	    fprintf(stderr, "test_unit_ready: Note: No loaded media detected.\n");
679 	i_ret = 0;
680     }
681 
682   ex:;
683     cdio_loglevel_default = old_log_level;
684     cdio_destroy(p_cdio);
685     return i_ret;
686 }
687 
688 /* ---------------------------      main       ----------------------------- */
689 
690 int
main(int argc,const char * argv[])691 main(int argc, const char *argv[])
692 {
693   CdIo_t *p_cdio;
694   char **ppsz_drives=NULL;
695   int ret;
696   int exitrc = 0;
697   bool b_verbose = (argc > 1);
698 
699   cdio_loglevel_default = b_verbose ? CDIO_LOG_DEBUG : CDIO_LOG_WARN;
700 
701   ppsz_drives = cdio_get_devices(DRIVER_DEVICE);
702   if (!ppsz_drives) {
703     printf("Can't find a CD-ROM drive. Skipping test.\n");
704     exit(SKIP_TEST);
705   }
706 
707   p_cdio = cdio_open(ppsz_drives[0], DRIVER_DEVICE);
708   if (p_cdio) {
709     const char *psz_have_mmc = cdio_get_arg(p_cdio, "mmc-supported?");
710 
711     if ( psz_have_mmc
712 	 && 0 == strncmp("true", psz_have_mmc, sizeof("true")) ) {
713 
714 	/* Test the MMC enhancements of version 0.83 in december 2009 */
715 	ret = test_write(ppsz_drives[0],
716 			cdio_loglevel_default == CDIO_LOG_DEBUG);
717 	if (ret != 0) exit(ret + 16);
718     }
719 
720     cdio_destroy(p_cdio);
721 
722   } else {
723     fprintf(stderr, "cdio_open('%s') failed\n", ppsz_drives[0]);
724     exit(2);
725   }
726 
727   cdio_free_device_list(ppsz_drives);
728 
729   return exitrc;
730 }
731