1 /* $NetBSD: mmcformat.c,v 1.9 2023/04/04 20:28:01 rillig Exp $ */
2 
3 /*
4  * Copyright (c) 2006, 2008 Reinoud Zandijk
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  *
27  */
28 
29 #include <sys/cdefs.h>
30 __RCSID("$NetBSD: mmcformat.c,v 1.9 2023/04/04 20:28:01 rillig Exp $");
31 
32 #include <sys/types.h>
33 #include <sys/time.h>
34 #include <assert.h>
35 #include <errno.h>
36 #include <fcntl.h>
37 #include <inttypes.h>
38 #include <limits.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <strings.h>
43 #include <unistd.h>
44 
45 #include "uscsilib.h"
46 
47 
48 /* globals */
49 static struct uscsi_dev dev;
50 extern int scsilib_verbose;
51 
52 /* #define DEBUG(a) {a;} */
53 #define DEBUG(a) ;
54 
55 
56 static uint64_t
getmtime(void)57 getmtime(void)
58 {
59           struct timeval tp;
60 
61           gettimeofday(&tp, NULL);
62           return (uint64_t) 1000000 * tp.tv_sec + tp.tv_usec;
63 }
64 
65 
66 static void
print_eta(uint32_t progress,uint64_t now,uint64_t start_time)67 print_eta(uint32_t progress, uint64_t now, uint64_t start_time)
68 {
69           int hours, minutes, seconds;
70           uint64_t tbusy, ttot_est, eta;
71 
72           if (progress == 0) {
73                     printf(" ETA --:--:--");
74                     return;
75           }
76           tbusy    = now - start_time;
77           ttot_est = (tbusy * 0x10000) / progress;
78           eta      = (ttot_est - tbusy) / 1000000;
79 
80           hours   = (int) (eta/3600);
81           minutes = (int) (eta/60) % 60;
82           seconds = (int)  eta % 60;
83           printf(" ETA %02d:%02d:%02d", hours, minutes, seconds);
84 }
85 
86 
87 static void
uscsi_waitop(struct uscsi_dev * mydev)88 uscsi_waitop(struct uscsi_dev *mydev)
89 {
90           scsicmd cmd;
91           struct uscsi_sense sense;
92           uint64_t start_time;
93           uint32_t progress;
94           uint8_t buffer[256];
95           int asc, ascq;
96           int cnt = 0;
97 
98           bzero(cmd, SCSI_CMD_LEN);
99           bzero(buffer, sizeof(buffer));
100 
101           /*
102            * not be to impatient... give the drive some time to start or it
103            * might break off
104            */
105 
106           start_time = getmtime();
107           sleep(10);
108 
109           progress = 0;
110           while (progress < 0x10000) {
111                     /* we need a command that is NOT going to stop the formatting */
112                     bzero(cmd, SCSI_CMD_LEN);
113                     cmd[0] = 0;                             /* test unit ready */
114                     uscsi_command(SCSI_READCMD, mydev,
115                               cmd, 6, buffer, 0, 10000, &sense);
116 
117                     /*
118                      * asc may be `not-ready' or `no-sense'. ascq for format in
119                      * progress is 4 too
120                      */
121                     asc  = sense.asc;
122                     ascq = sense.ascq;
123                     if (((asc == 0) && (ascq == 4)) || (asc == 4)) {
124                               /* drive not ready : operation/format in progress */
125                               if (sense.skey_valid) {
126                                         progress = sense.sense_key;
127                               } else {
128                                         /* finished */
129                                         progress = 0x10000;
130                               }
131                     }
132                     /* check if drive is ready again, ifso break out loop */
133                     if ((asc == 0) && (ascq == 0)) {
134                               progress = 0x10000;
135                     }
136 
137                     printf("%3d %% ", (100 * progress / 0x10000));
138                     printf("%c", "|/-\\" [cnt++ %4]);   /* twirl */
139 
140                     /* print ETA */
141                     print_eta(progress, getmtime(), start_time);
142 
143                     fflush(stdout);
144                     sleep(1);
145                     printf("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b");
146                     fflush(stdout);
147           }
148           printf("\n");
149 
150           return;
151 }
152 
153 
154 static char const *
print_mmc_profile(int profile)155 print_mmc_profile(int profile)
156 {
157           static char scrap[100];
158 
159           switch (profile) {
160           case 0x00 : return "Unknown[0] profile";
161           case 0x01 : return "Non removable disc";
162           case 0x02 : return "Removable disc";
163           case 0x03 : return "Magneto Optical with sector erase";
164           case 0x04 : return "Magneto Optical write once";
165           case 0x05 : return "Advance Storage Magneto Optical";
166           case 0x08 : return "CD-ROM";
167           case 0x09 : return "CD-R recordable";
168           case 0x0a : return "CD-RW rewritable";
169           case 0x10 : return "DVD-ROM";
170           case 0x11 : return "DVD-R sequential";
171           case 0x12 : return "DVD-RAM rewritable";
172           case 0x13 : return "DVD-RW restricted overwrite";
173           case 0x14 : return "DVD-RW sequential";
174           case 0x1a : return "DVD+RW rewritable";
175           case 0x1b : return "DVD+R recordable";
176           case 0x20 : return "DDCD readonly";
177           case 0x21 : return "DDCD-R recordable";
178           case 0x22 : return "DDCD-RW rewritable";
179           case 0x2b : return "DVD+R double layer";
180           case 0x40 : return "BD-ROM";
181           case 0x41 : return "BD-R Sequential Recording (SRM)";
182           case 0x42 : return "BD-R Random Recording (RRM)";
183           case 0x43 : return "BD-RE rewritable";
184           }
185           sprintf(scrap, "Reserved profile 0x%02x", profile);
186           return scrap;
187 }
188 
189 
190 static int
uscsi_get_mmc_profile(struct uscsi_dev * mydev,int * mmc_profile)191 uscsi_get_mmc_profile(struct uscsi_dev *mydev, int *mmc_profile)
192 {
193           scsicmd    cmd;
194           uint8_t  buf[32];
195           int error;
196 
197           *mmc_profile = 0;
198 
199           bzero(cmd, SCSI_CMD_LEN);
200           cmd[ 0] = 0x46;                                   /* Get configuration */
201           cmd[ 8] = 32;                                     /* just a small buffer size */
202           cmd[ 9] = 0;                                      /* control */
203           error = uscsi_command(SCSI_READCMD, mydev, cmd, 10, buf, 32, 30000, NULL);
204           if (!error) {
205                     *mmc_profile = buf[7] | (buf[6] << 8);
206           }
207 
208           return error;
209 }
210 
211 
212 static int
uscsi_set_packet_parameters(struct uscsi_dev * mydev,int blockingnr)213 uscsi_set_packet_parameters(struct uscsi_dev *mydev, int blockingnr)
214 {
215           scsicmd  cmd;
216           int      val_len;
217           uint8_t  res[10000], *pos;
218           int      error;
219 
220           /* Set up CD/DVD recording parameters */
221           DEBUG(printf("Setting device's recording parameters\n"));
222 
223           val_len = 0x32+2+8;
224           bzero(res, val_len);
225 
226           pos = res + 8;
227 
228           bzero(cmd, SCSI_CMD_LEN);
229           pos[ 0] = 0x05;               /* page code 5 : cd writing             */
230           pos[ 1] = 0x32;               /* length in bytes                      */
231           pos[ 2] = 0;                  /* write type 0 : packet/incremental    */
232 
233           /* next session OK, data packet, rec. incr. fixed packets   */
234           pos[ 3] = (3<<6) | 32 | 5;
235           pos[ 4] = 10;                 /* ISO mode 2; XA form 1                */
236           pos[ 8] = 0x20;               /* CD-ROM XA disc or DDCD disc                    */
237           pos[10] = (blockingnr >> 24) & 0xff;    /* MSB packet size  */
238           pos[11] = (blockingnr >> 16) & 0xff;
239           pos[12] = (blockingnr >>  8) & 0xff;
240           pos[13] = (blockingnr      ) & 0xff;    /* LSB packet size  */
241 
242           bzero(cmd, SCSI_CMD_LEN);
243           cmd[0] = 0x55;                          /* MODE SELECT (10)           */
244           cmd[1] = 16;                            /* PF format                            */
245           cmd[7] = val_len >> 8;                  /* length of blob             */
246           cmd[8] = val_len & 0xff;
247           cmd[9] = 0;                             /* control                              */
248 
249           error = uscsi_command(SCSI_WRITECMD, mydev,
250                               cmd, 10, res, val_len, 30000, NULL);
251           if (error) {
252                     perror("While WRTITING parameter page 5");
253                     return error;
254           }
255 
256           /* flag OK */
257           return 0;
258 }
259 
260 
261 static int
get_format_capabilities(struct uscsi_dev * mydev,uint8_t * buf,uint32_t * len)262 get_format_capabilities(struct uscsi_dev *mydev, uint8_t *buf, uint32_t *len)
263 {
264           scsicmd             cmd;
265           int                 list_length;
266           int                 trans_len;
267           size_t              buf_len = 512;
268           int                 error;
269 
270           assert(*len >= buf_len);
271           bzero(buf, buf_len);
272 
273           trans_len = 12;                                   /* only fixed header first */
274           bzero(cmd, SCSI_CMD_LEN);
275           cmd[0] = 0x23;                                    /* Read format capabilities */
276           cmd[7] = trans_len >> 8;                /* MSB allocation length */
277           cmd[8] = trans_len & 0xff;              /* LSB allocation length */
278           cmd[9] = 0;                                       /* control */
279           error = uscsi_command(SCSI_READCMD, mydev,
280                               cmd, 10, buf, trans_len, 30000, NULL);
281           if (error) {
282                     fprintf(stderr, "While reading format capabilities : %s\n",
283                               strerror(error));
284                     return error;
285           }
286 
287           list_length = buf[ 3];
288 
289           if (list_length % 8) {
290                     printf( "\t\tWarning: violating SCSI spec,"
291                               "capacity list length ought to be multiple of 8\n");
292                     printf("\t\tInterpreting as including header of 4 bytes\n");
293                     assert(list_length % 8 == 4);
294                     list_length -= 4;
295           }
296 
297           /* read in full capacity list */
298           trans_len = 12 + list_length;           /* complete structure */
299           bzero(cmd, SCSI_CMD_LEN);
300           cmd[0] = 0x23;                                    /* Read format capabilities */
301           cmd[7] = trans_len >> 8;                /* MSB allocation length */
302           cmd[8] = trans_len & 0xff;              /* LSB allocation length */
303           cmd[9] = 0;                                       /* control */
304           error = uscsi_command(SCSI_READCMD, mydev,
305                               cmd, 10, buf, trans_len, 30000, NULL);
306           if (error) {
307                     fprintf(stderr, "While reading format capabilities : %s\n",
308                               strerror(error));
309                     return error;
310           }
311 
312           *len = list_length;
313           return 0;
314 }
315 
316 
317 static void
print_format(int format_tp,uint32_t num_blks,uint32_t param,int dscr_type,int verbose,int * supported)318 print_format(int format_tp, uint32_t num_blks, uint32_t param,
319           int dscr_type, int verbose, int *supported)
320 {
321           char const *format_str, *nblks_str, *param_str, *user_spec;
322 
323           format_str = nblks_str = param_str = "reserved";
324           user_spec = "";
325           *supported = 1;
326 
327           switch (format_tp) {
328           case  0x00 :
329                     format_str = "full format capacity";
330                     nblks_str  = "sectors";
331                     param_str  = "block length in bytes";
332                     user_spec  = "'-F [-b blockingnr]'";
333                     break;
334           case  0x01 :
335                     format_str = "spare area expansion";
336                     nblks_str  = "extension in blocks";
337                     param_str  = "block length in bytes";
338                     user_spec  = "'-S'";
339                     break;
340           /* 0x02 - 0x03 reserved */
341           case  0x04 :
342                     format_str = "variable length zone'd format";
343                     nblks_str  = "zone length";
344                     param_str  = "zone number";
345                     *supported = 0;
346                     break;
347           case  0x05 :
348                     format_str = "fixed length zone'd format";
349                     nblks_str  = "zone length";
350                     param_str  = "last zone number";
351                     *supported = 0;
352                     break;
353           /* 0x06 - 0x0f reserved */
354           case  0x10 :
355                     format_str = "CD-RW/DVD-RW full packet format";
356                     nblks_str  = "addressable blocks";
357                     param_str  = "fixed packet size/ECC blocksize in sectors";
358                     user_spec  = "'-F -p [-b blockingnr]'";
359                     break;
360           case  0x11 :
361                     format_str = "CD-RW/DVD-RW grow session";
362                     nblks_str  = "addressable blocks";
363                     param_str  = "fixed packet size/ECC blocksize in sectors";
364                     user_spec  = "'-G'";
365                     break;
366           case  0x12 :
367                     format_str = "CD-RW/DVD-RW add session";
368                     nblks_str  = "addressable blocks";
369                     param_str  = "maximum fixed packet size/ECC blocksize "
370                                    "in sectors";
371                     *supported = 0;
372                     break;
373           case  0x13 :
374                     format_str = "DVD-RW max growth of last complete session";
375                     nblks_str  = "addressable blocks";
376                     param_str  = "ECC blocksize in sectors";
377                     user_spec  = "'-G'";
378                     break;
379           case  0x14 :
380                     format_str = "DVD-RW quick grow last session";
381                     nblks_str  = "addressable blocks";
382                     param_str  = "ECC blocksize in sectors";
383                     *supported = 0;
384                     break;
385           case  0x15 :
386                     format_str = "DVD-RW quick full format";
387                     nblks_str  = "addressable blocks";
388                     param_str  = "ECC blocksize in sectors";
389                     *supported = 0;
390                     break;
391           /* 0x16 - 0x23 reserved */
392           case  0x24 :
393                     format_str = "background MRW format";
394                     nblks_str  = "Defect Management Area blocks";
395                     param_str  = "not used";
396                     user_spec  = "'[-R] [-s] [-w] -F -M [-b blockingnr]'";
397                     break;
398           /* 0x25 reserved */
399           case  0x26 :
400                     format_str = "background DVD+RW full format";
401                     nblks_str  = "sectors";
402                     param_str  = "not used";
403                     user_spec  = "'[-R] [-w] -F'";
404                     break;
405           /* 0x27 - 0x2f reserved */
406           case  0x30 :
407                     format_str = "BD-RE full format with spare area";
408                     nblks_str  = "blocks";
409                     param_str  = "total spare area size in clusters";
410                     user_spec  = "'[-s] -F'";
411                     break;
412           case  0x31 :
413                     format_str = "BD-RE full format without spare area";
414                     nblks_str  = "blocks";
415                     param_str  = "block length in bytes";
416                     user_spec  = "'-F'";
417                     break;
418           /* 0x32 - 0x3f reserved */
419           default :
420                     break;
421           }
422 
423           if (verbose) {
424                     printf("\n\tFormat type 0x%02x : %s\n", format_tp, format_str);
425 
426                     switch (dscr_type) {
427                     case  1 :
428                               printf( "\t\tUnformatted media,"
429                                         "maximum formatted capacity\n");
430                               break;
431                     case  2 :
432                               printf( "\t\tFormatted media,"
433                                         "current formatted capacity\n");
434                               break;
435                     case  3 :
436                               printf( "\t\tNo media present or incomplete session, "
437                                         "maximum formatted capacity\n");
438                               break;
439                     default :
440                               printf("\t\tUnspecified descriptor type\n");
441                               break;
442                     }
443 
444                     printf("\t\tNumber of blocks : %12d\t(%s)\n",
445                               num_blks, nblks_str);
446                     printf("\t\tParameter        : %12d\t(%s)\n",
447                               param, param_str);
448 
449                     if (format_tp == 0x24) {
450                               printf( "\t\tExpert select    : "
451                                         "'-X 0x%02x:0xffffff:0' or "
452                                         "'-X 0x%02x:0xffff0000:0'\n",
453                                         format_tp, format_tp);
454                     } else {
455                               printf( "\t\tExpert select    : "
456                                         "'-X 0x%02x:%d:%d'\n",
457                                         format_tp, num_blks, param);
458                     }
459                     if (*supported) {
460                               printf("\t\tmmc_format arg   : %s\n", user_spec);
461                     } else {
462                               printf("\t\t** not supported **\n");
463                     }
464           }
465 }
466 
467 
468 static void
process_format_caps(uint8_t * buf,int list_length,int verbose,uint8_t * allow,uint32_t * blks,uint32_t * params)469 process_format_caps(uint8_t *buf, int list_length, int verbose,
470           uint8_t *allow, uint32_t *blks, uint32_t *params)
471 {
472           uint32_t  num_blks, param;
473           uint8_t          *fcd;
474           int                 dscr_type, format_tp;
475           int                 supported;
476 
477           bzero(allow,  255);
478           bzero(blks,   255*4);
479           bzero(params, 255*4);
480 
481           fcd = buf + 4;
482           list_length -= 4;             /* strip header */
483 
484           if (verbose)
485                     printf("\tCurrent/max capacity followed by additional capacity,"
486                               "reported length of %d bytes (8/entry)\n", list_length);
487 
488           while (list_length > 0) {
489                     num_blks    = fcd[ 3]        | (fcd[ 2] << 8) |
490                                    (fcd[ 1] << 16) | (fcd[ 0] << 24);
491                     dscr_type   = fcd[ 4] & 3;
492                     format_tp   = fcd[ 4] >> 2;
493                     param       = fcd[ 7] | (fcd[ 6] << 8) |  (fcd[ 5] << 16);
494 
495                     print_format(format_tp, num_blks, param, dscr_type, verbose,
496                               &supported);
497 
498                      allow[format_tp] = 1;        /* TODO = supported? */
499                       blks[format_tp] = num_blks;
500                     params[format_tp] = param;
501 
502                     fcd += 8;
503                     list_length-=8;
504           }
505 }
506 
507 
508 
509 /* format a CD-RW disc */
510 /* old style format 7 */
511 static int
uscsi_format_cdrw_mode7(struct uscsi_dev * mydev,uint32_t blocks)512 uscsi_format_cdrw_mode7(struct uscsi_dev *mydev, uint32_t blocks)
513 {
514           scsicmd cmd;
515           struct uscsi_sense sense;
516           uint8_t  buffer[16];
517           int error;
518 
519           if (blocks % 32) {
520                     blocks -= blocks % 32;
521           }
522 
523           bzero(cmd, SCSI_CMD_LEN);
524           bzero(buffer, sizeof(buffer));
525 
526           cmd[0] = 0x04;                          /* format unit                             */
527           cmd[1] = 0x17;                          /* parameter list format 7 follows */
528           cmd[5] = 0;                             /* control                                 */
529 
530           /* format list header */
531           buffer[ 0] = 0;                         /* reserved                                */
532           buffer[ 1] = 0x80 | 0x02;     /* Valid info, immediate return            */
533           buffer[ 2] = 0;                         /* MSB format descriptor length            */
534           buffer[ 3] = 8;                         /* LSB ...                                 */
535 
536           /*
537            * for CD-RW the initialisation pattern bit is reserved, but there IS
538            * one
539            */
540 
541           buffer[ 4] = 0;                         /* no header                               */
542           buffer[ 5] = 0;                         /* default pattern               */
543           buffer[ 6] = 0;                         /* pattern length MSB                      */
544           buffer[ 7] = 0;                         /* pattern length LSB                      */
545 
546           /* 8 bytes of format descriptor */
547           /* (s)ession bit 1<<7, (g)row bit 1<<6  */
548           /* SG     action    */
549           /* 00     format disc with number of user data blocks       */
550           /* 10     create new session with number of data blocks     */
551           /* x1     grow session to be number of data blocks          */
552 
553           buffer[ 8] = 0x00;            /* session and grow bits (7 and 6)  */
554           buffer[ 9] = 0;                         /* reserved */
555           buffer[10] = 0;                         /* reserved */
556           buffer[11] = 0;                         /* reserved */
557           buffer[12] = (blocks >> 24) & 0xff;     /* blocks MSB       */
558           buffer[13] = (blocks >> 16) & 0xff;
559           buffer[14] = (blocks >>  8) & 0xff;
560           buffer[15] = (blocks      ) & 0xff;     /* blocks LSB       */
561 
562           /* this will take a while .... */
563           error = uscsi_command(SCSI_WRITECMD, mydev,
564                               cmd, 6, buffer, sizeof(buffer), UINT_MAX, &sense);
565           if (error)
566                     return error;
567 
568           uscsi_waitop(mydev);
569           return 0;
570 }
571 
572 
573 static int
uscsi_format_disc(struct uscsi_dev * mydev,int immed,int format_type,uint32_t blocks,uint32_t param,int certification,int cmplist)574 uscsi_format_disc(struct uscsi_dev *mydev, int immed, int format_type,
575           uint32_t blocks, uint32_t param, int certification, int cmplist)
576 {
577           scsicmd cmd;
578           struct uscsi_sense sense;
579           uint8_t  buffer[16], fmt_flags;
580           int error;
581 
582           fmt_flags = 0x80;             /* valid info flag */
583           if (immed)
584                     fmt_flags |= 2;
585           if (certification == 0)
586                     fmt_flags |= 32;
587 
588           if (cmplist)
589                     cmplist = 8;
590 
591 #if 0
592           if (mmc_profile != 0x43) {
593                     /* certification specifier only valid for BD-RE */
594                     certification = 0;
595           }
596 #endif
597 
598           bzero(cmd, SCSI_CMD_LEN);
599           bzero(buffer, sizeof(buffer));
600 
601           cmd[0] = 0x04;                          /* format unit                             */
602           cmd[1] = 0x11 | cmplist;      /* parameter list format 1 follows */
603           cmd[5] = 0;                             /* control                                 */
604 
605           /* format list header */
606           buffer[ 0] = 0;                         /* reserved                                */
607           buffer[ 1] = 0x80 | fmt_flags;          /* Valid info, flags follow      */
608           buffer[ 2] = 0;                         /* MSB format descriptor length    */
609           buffer[ 3] = 8;                         /* LSB ...                                 */
610 
611           /* 8 bytes of format descriptor */
612           buffer[ 4] = (blocks >> 24) & 0xff;     /* blocks MSB            */
613           buffer[ 5] = (blocks >> 16) & 0xff;
614           buffer[ 6] = (blocks >>  8) & 0xff;
615           buffer[ 7] = (blocks      ) & 0xff;     /* blocks LSB            */
616           buffer[ 8] = (format_type << 2) | certification;
617           buffer[ 9] = (param  >> 16) & 0xff;     /* parameter MSB     */
618           buffer[10] = (param  >>  8) & 0xff;     /*        packet size  */
619           buffer[11] = (param       ) & 0xff;     /* parameter LSB     */
620 
621           /* this will take a while .... */
622           error = uscsi_command(SCSI_WRITECMD, mydev,
623                               cmd, 6, buffer, 12, UINT_MAX, &sense);
624           if (error)
625                     return error;
626 
627           if (immed)
628                     uscsi_waitop(mydev);
629 
630           return 0;
631 }
632 
633 
634 static int
uscsi_blank_disc(struct uscsi_dev * mydev)635 uscsi_blank_disc(struct uscsi_dev *mydev)
636 {
637           scsicmd cmd;
638           int error;
639 
640           /* XXX check if the device can blank! */
641 
642 
643           /* blank disc */
644           bzero(cmd, SCSI_CMD_LEN);
645           cmd[ 0] = 0xA1;                         /* blank                      */
646           cmd[ 1] = 16;                           /* Immediate, blank complete  */
647           cmd[11] = 0;                            /* control                              */
648 
649           /* this will take a while .... */
650           error = uscsi_command(SCSI_WRITECMD, mydev,
651                               cmd, 12, NULL, 0, UINT_MAX, NULL);
652           if (error)
653                     return error;
654 
655           uscsi_waitop(mydev);
656           return 0;
657 }
658 
659 
660 static int
usage(char * program)661 usage(char *program)
662 {
663           fprintf(stderr, "\n");
664           fprintf(stderr, "Usage: %s [options] devicename\n", program);
665           fprintf(stderr,
666           "-B                 blank cd-rw disc before formatting\n"
667           "-F                 format cd-rw disc\n"
668           "-O                 CD-RW formatting 'old-style' for old CD-RW drives\n"
669           "-M                 select MRW format\n"
670           "-R                 restart MRW & DVD+RW format\n"
671           "-G                 grow last CD-RW/DVD-RW session\n"
672           "-S                 grow spare space DVD-RAM/BD-RE\n"
673           "-s                 format DVD+MRW/BD-RE with extra spare space\n"
674           "-w                 wait until completion of background format\n"
675           "-p                 explicitly set packet format\n"
676           "-c num             media certification for DVD-RAM/BD-RE : "
677                               "0 no, 1 full, 2 quick\n"
678           "-r                 recompile defect list for DVD-RAM (cmplist)\n"
679           "-h -H -I help/inquiry formats\n"
680           "-X format          expert format selector form 'fmt:blks:param' with -c\n"
681           "-b blockingnr      in sectors (for CD-RW)\n"
682           "-D                 verbose SCSI command errors\n"
683           );
684           return 1;
685 }
686 
687 
688 int
main(int argc,char * argv[])689 main(int argc, char *argv[])
690 {
691           struct uscsi_addr saddr;
692           uint32_t blks[256], params[256];
693           uint32_t format_type, format_blks, format_param, blockingnr;
694           uint8_t  allow[256];
695           uint8_t caps[512];
696           uint32_t caps_len = sizeof(caps);
697           char *progname;
698           int blank, format, mrw, background;
699           int inquiry, spare, oldtimer;
700           int expert;
701           int restart_format, grow_session, grow_spare, packet_wr;
702           int mmc_profile, flag, error, display_usage;
703           int certification, cmplist;
704           int wait_until_finished;
705           progname = strdup(argv[0]);
706           if (argc == 1) {
707                     return usage(progname);
708           }
709 
710           blank               = 0;
711           format              = 0;
712           mrw                 = 0;
713           restart_format      = 0;
714           grow_session        = 0;
715           grow_spare          = 0;
716           wait_until_finished = 0;
717           packet_wr           = 0;
718           certification       = 1;
719           cmplist             = 0;
720           inquiry             = 0;
721           spare               = 0;
722           inquiry             = 0;
723           oldtimer            = 0;
724           expert              = 0;
725           display_usage       = 0;
726           blockingnr          = 32;
727           uscsilib_verbose    = 0;
728           while ((flag = getopt(argc, argv, "BFMRGSwpsc:rhHIX:Ob:D")) != -1) {
729                     switch (flag) {
730                               case 'B' :
731                                         blank = 1;
732                                         break;
733                               case 'F' :
734                                         format = 1;
735                                         break;
736                               case 'M' :
737                                         mrw = 1;
738                                         break;
739                               case 'R' :
740                                         restart_format = 1;
741                                         break;
742                               case 'G' :
743                                         grow_session = 1;
744                                         break;
745                               case 'S' :
746                                         grow_spare = 1;
747                                         break;
748                               case 'w' :
749                                         wait_until_finished = 1;
750                                         break;
751                               case 'p' :
752                                         packet_wr = 1;
753                                         break;
754                               case 's' :
755                                         spare = 1;
756                                         break;
757                               case 'c' :
758                                         certification = atoi(optarg);
759                                         break;
760                               case 'r' :
761                                         cmplist = 1;
762                                         break;
763                               case 'h' :
764                               case 'H' :
765                                         display_usage = 1;
766                                         break;
767                               case 'I' :
768                                         inquiry = 1;
769                                         break;
770                               case 'X' :
771                                         /* TODO parse expert mode string */
772                                         printf("-X not implemented yet\n");
773                                         expert = 1;
774                                         exit(1);
775                                         break;
776                               case 'O' :
777                                         /* oldtimer CD-RW format */
778                                         oldtimer = 1;
779                                         format   = 1;
780                                         break;
781                               case 'b' :
782                                         blockingnr = atoi(optarg);
783                                         break;
784                               case 'D' :
785                                         uscsilib_verbose = 1;
786                                         break;
787                               default :
788                                         return usage(progname);
789                     }
790           }
791           argv += optind;
792           argc -= optind;
793 
794           if (!blank && !format && !grow_session && !grow_spare &&
795               !expert && !inquiry && !display_usage) {
796                     fprintf(stderr, "%s : at least one of -B, -F, -G, -h, -H -S, "
797                                         "-X or -I needs to be specified\n\n", progname);
798                     return usage(progname);
799           }
800 
801           if (format + grow_session + grow_spare + expert > 1) {
802                     fprintf(stderr, "%s : at most one of -F, -G, -S or -X "
803                                         "needs to be specified\n\n", progname);
804                     return usage(progname);
805           }
806 
807           if (argc != 1) return usage(progname);
808 
809           /* Open the device */
810           dev.dev_name = strdup(*argv);
811           printf("Opening device %s\n", dev.dev_name);
812           error = uscsi_open(&dev);
813           if (error) {
814                     fprintf(stderr, "Device failed to open : %s\n",
815                               strerror(error));
816                     exit(1);
817           }
818 
819           error = uscsi_check_for_scsi(&dev);
820           if (error) {
821                     fprintf(stderr, "sorry, not a SCSI/ATAPI device : %s\n",
822                               strerror(error));
823                     exit(1);
824           }
825 
826           error = uscsi_identify(&dev, &saddr);
827           if (error) {
828                     fprintf(stderr, "SCSI/ATAPI identify returned : %s\n",
829                               strerror(error));
830                     exit(1);
831           }
832 
833           printf("\nDevice identifies itself as : ");
834 
835           if (saddr.type == USCSI_TYPE_SCSI) {
836                     printf("SCSI   busnum = %d, target = %d, lun = %d\n",
837                               saddr.addr.scsi.scbus, saddr.addr.scsi.target,
838                               saddr.addr.scsi.lun);
839           } else {
840                     printf("ATAPI  busnum = %d, drive = %d\n",
841                               saddr.addr.atapi.atbus, saddr.addr.atapi.drive);
842           }
843 
844           printf("\n");
845 
846           /* get MMC profile */
847           error = uscsi_get_mmc_profile(&dev, &mmc_profile);
848           if (error) {
849                     fprintf(stderr,
850                               "Can't get the disc's MMC profile because of :"
851                               " %s\n", strerror(error));
852                     fprintf(stderr, "aborting\n");
853                     uscsi_close(&dev);
854                     return 1;
855           }
856 
857           /* blank disc section */
858           if (blank) {
859                     printf("\nBlanking disc.... "); fflush(stdout);
860                     error = uscsi_blank_disc(&dev);
861 
862                     if (error) {
863                               printf("fail\n"); fflush(stdout);
864                               fprintf(stderr,
865                                         "Blanking failed because of : %s\n",
866                                         strerror(error));
867                               uscsi_close(&dev);
868 
869                               return 1;
870                     } else {
871                               printf("success!\n\n");
872                     }
873           }
874 
875           /* re-get MMC profile */
876           error = uscsi_get_mmc_profile(&dev, &mmc_profile);
877           if (error) {
878                     fprintf(stderr,
879                               "Can't get the disc's MMC profile because of : %s\n",
880                               strerror(error));
881                     fprintf(stderr, "aborting\n");
882                     uscsi_close(&dev);
883                     return 1;
884           }
885 
886           error = get_format_capabilities(&dev, caps, &caps_len);
887           if (error)
888                     exit(1);
889 
890           process_format_caps(caps, caps_len, inquiry, allow, blks, params);
891 
892           format_type = 0;
893           /* expert format section */
894           if (expert) {
895           }
896 
897           if (!format && !grow_spare && !grow_session) {
898                     /* we're done */
899                     if (display_usage)
900                               usage(progname);
901                     uscsi_close(&dev);
902                     exit(0);
903           }
904 
905           /* normal format section */
906           if (format) {
907                     /* get current mmc profile of disc */
908 
909                     if (oldtimer && mmc_profile != 0x0a) {
910                               printf("Oldtimer flag only defined for CD-RW; "
911                                         "ignored\n");
912                     }
913 
914                     switch (mmc_profile) {
915                     case 0x12 :         /* DVD-RAM          */
916                               format_type = 0x00;
917                               break;
918                     case 0x0a :         /* CD-RW  */
919                               format_type = mrw ? 0x24 : 0x10;
920                               packet_wr   = 1;
921                               break;
922                     case 0x13 :         /* DVD-RW restricted overwrite */
923                     case 0x14 :         /* DVD-RW sequential                    */
924                               format_type = 0x10;
925                               /*
926                                * Some drives suddenly stop supporting this format
927                                * type when packet_wr = 1
928                                */
929                               packet_wr   = 0;
930                               break;
931                     case 0x1a :         /* DVD+RW */
932                               format_type = mrw ? 0x24 : 0x26;
933                               break;
934                     case 0x43 :         /* BD-RE  */
935                               format_type = spare ? 0x30 : 0x31;
936                               break;
937                     default :
938                               fprintf(stderr, "Can't format discs of type %s\n",
939                                         print_mmc_profile(mmc_profile));
940                               uscsi_close(&dev);
941                               exit(1);
942                     }
943           }
944 
945           if (grow_spare) {
946                     switch (mmc_profile) {
947                     case 0x12 :         /* DVD-RAM */
948                     case 0x43 :         /* BD-RE   */
949                               format_type = 0x01;
950                               break;
951                     default :
952                               fprintf(stderr,
953                                         "Can't grow spare area for discs of type %s\n",
954                                         print_mmc_profile(mmc_profile));
955                               uscsi_close(&dev);
956                               exit(1);
957                     }
958           }
959 
960           if (grow_session) {
961                     switch (mmc_profile) {
962                     case 0x0a :         /* CD-RW */
963                               format_type = 0x11;
964                               break;
965                     case 0x13 :         /* DVD-RW restricted overwrite */
966                     case 0x14 :         /* DVD-RW sequential ? */
967                               format_type = 0x13;
968                               break;
969                     default :
970                               uscsi_close(&dev);
971                               fprintf(stderr,
972                                         "Can't grow session for discs of type %s\n",
973                                         print_mmc_profile(mmc_profile));
974                               exit(1);
975                     }
976           }
977 
978           /* check if format type is allowed */
979           format_blks  = blks[format_type];
980           format_param = params[format_type];
981           if (!allow[format_type]) {
982                     if (!inquiry)
983                               process_format_caps(caps, caps_len, 1, allow,
984                                         blks, params);
985 
986                     printf("\n");
987                     fflush(stdout);
988                     fprintf(stderr,
989                               "Drive indicates it can't format with deduced format "
990                               "type 0x%02x\n", format_type);
991                     uscsi_close(&dev);
992                     exit(1);
993           }
994 
995           if (restart_format && !((mmc_profile == 0x1a) || (format_type == 0x24)))
996           {
997                     fprintf(stderr,
998                               "Format restarting only for MRW formats or DVD+RW "
999                               "formats\n");
1000                     uscsi_close(&dev);
1001                     exit(1);
1002           }
1003 
1004           if (restart_format && !wait_until_finished) {
1005                     printf( "Warning : format restarting without waiting for it be "
1006                               "finished is prolly not handy\n");
1007           }
1008 
1009           /* explicitly select packet write just in case */
1010           if (packet_wr) {
1011                     printf("Explicitly setting packet type and blocking number\n");
1012                     error = uscsi_set_packet_parameters(&dev, blockingnr);
1013                     if (error) {
1014                               fprintf(stderr,
1015                                         "Can't set packet writing and blocking number: "
1016                                         "%s\n", strerror(error));
1017                               uscsi_close(&dev);
1018                               exit(1);
1019                     }
1020           }
1021 
1022           /* determine if formatting is done in the background */
1023           background = 0;
1024           if (format_type == 0x24) background = 1;
1025           if (format_type == 0x26) background = 1;
1026 
1027           /* special case format type 0x24 : MRW */
1028           if (format_type == 0x24) {
1029                     format_blks  = spare ? 0xffff0000 : 0xffffffff;
1030                     format_param = restart_format;
1031           }
1032           /* special case format type 0x26 : DVD+RW */
1033           if (format_type == 0x26) {
1034                     format_param = restart_format;
1035           }
1036 
1037           /* verbose to the user */
1038           DEBUG(
1039                     printf("Actual format selected: "
1040                               "format_type 0x%02x, blks %d, param %d, "
1041                               "certification %d, cmplist %d\n",
1042                               format_type, format_blks, format_param,
1043                               certification, cmplist);
1044           );
1045           printf("\nFormatting.... "); fflush(stdout);
1046 
1047           /* formatting time! */
1048           if (oldtimer) {
1049                     error = uscsi_format_cdrw_mode7(&dev, format_blks);
1050                     background = 0;
1051           } else {
1052                     error = uscsi_format_disc(&dev, !background, format_type,
1053                                         format_blks, format_param, certification,
1054                                         cmplist);
1055           }
1056 
1057           /* what now? */
1058           if (error) {
1059                     printf("fail\n"); fflush(stdout);
1060                     fprintf(stderr, "Formatting failed because of : %s\n",
1061                               strerror(error));
1062           } else {
1063                     if (background) {
1064                               printf("background formatting in progress\n");
1065                               if (wait_until_finished) {
1066                                         printf("Waiting for completion ... ");
1067                                         uscsi_waitop(&dev);
1068                               }
1069                               /* explicitly do NOT close disc ... (for now) */
1070                               return 0;
1071                     } else {
1072                               printf("success!\n\n");
1073                     }
1074           }
1075 
1076           /* finish up */
1077           uscsi_close(&dev);
1078 
1079           return error;
1080 }
1081