1 /***************************************************************************
2 *
3 * cdutils.c : CD/DVD utilities
4 *
5 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
6 * Use is subject to license terms.
7 *
8 * Licensed under the Academic Free License version 2.1
9 *
10 **************************************************************************/
11
12
13 #ifdef HAVE_CONFIG_H
14 # include <config.h>
15 #endif
16
17 #include <stdio.h>
18 #include <sys/types.h>
19 #include <sys/scsi/impl/uscsi.h>
20 #include <string.h>
21 #include <strings.h>
22 #include <unistd.h>
23 #include <stdlib.h>
24 #include <errno.h>
25 #include <fcntl.h>
26 #include <sys/dkio.h>
27 #include <libintl.h>
28
29 #include <logger.h>
30
31 #include "cdutils.h"
32
33 #define RQLEN 32
34 #define SENSE_KEY(rqbuf) (rqbuf[2]) /* scsi error category */
35 #define ASC(rqbuf) (rqbuf[12]) /* additional sense code */
36 #define ASCQ(rqbuf) (rqbuf[13]) /* ASC qualifier */
37
38 #define GET16(a) (((a)[0] << 8) | (a)[1])
39 #define GET32(a) (((a)[0] << 24) | ((a)[1] << 16) | ((a)[2] << 8) | (a)[3])
40
41 #define CD_USCSI_TIMEOUT 60
42
43 void
uscsi_cmd_init(struct uscsi_cmd * scmd,char * cdb,int cdblen)44 uscsi_cmd_init(struct uscsi_cmd *scmd, char *cdb, int cdblen)
45 {
46 bzero(scmd, sizeof (*scmd));
47 bzero(cdb, cdblen);
48 scmd->uscsi_cdb = cdb;
49 }
50
51 int
uscsi(int fd,struct uscsi_cmd * scmd)52 uscsi(int fd, struct uscsi_cmd *scmd)
53 {
54 char rqbuf[RQLEN];
55 int ret;
56 int f, i, retries, total_retries;
57 int max_retries = 20;
58
59 f = scmd->uscsi_flags;
60 scmd->uscsi_flags |= USCSI_RQENABLE;
61 scmd->uscsi_rqlen = RQLEN;
62 scmd->uscsi_rqbuf = rqbuf;
63
64 for (retries = 0; retries < max_retries; retries++) {
65 scmd->uscsi_status = 0;
66 memset(rqbuf, 0, RQLEN);
67
68 ret = ioctl(fd, USCSICMD, scmd);
69
70 if ((ret == 0) && (scmd->uscsi_status == 2)) {
71 ret = -1;
72 errno = EIO;
73 }
74 if ((ret < 0) && (scmd->uscsi_status == 2)) {
75 /*
76 * The drive is not ready to recieve commands but
77 * may be in the process of becoming ready.
78 * sleep for a short time then retry command.
79 * SENSE/ASC = 2/4 : not ready
80 * ASCQ = 0 Not Reportable.
81 * ASCQ = 1 Becoming ready.
82 * ASCQ = 4 FORMAT in progress.
83 * ASCQ = 7 Operation in progress.
84 */
85 if ((SENSE_KEY(rqbuf) == 2) && (ASC(rqbuf) == 4) &&
86 ((ASCQ(rqbuf) == 0) || (ASCQ(rqbuf) == 1) ||
87 (ASCQ(rqbuf) == 4)) || (ASCQ(rqbuf) == 7)) {
88 total_retries++;
89 sleep(1);
90 continue;
91 }
92
93 /*
94 * Device is not ready to transmit or a device reset
95 * has occurred. wait for a short period of time then
96 * retry the command.
97 */
98 if ((SENSE_KEY(rqbuf) == 6) && ((ASC(rqbuf) == 0x28) ||
99 (ASC(rqbuf) == 0x29))) {
100 sleep(1);
101 total_retries++;
102 continue;
103 }
104 /*
105 * Blank Sense, we don't know what the error is or if
106 * the command succeeded, Hope for the best. Some
107 * drives return blank sense periodically and will
108 * fail if this is removed.
109 */
110 if ((SENSE_KEY(rqbuf) == 0) && (ASC(rqbuf) == 0) &&
111 (ASCQ(rqbuf) == 0)) {
112 ret = 0;
113 break;
114 }
115
116 HAL_DEBUG (("cmd: 0x%02x ret:%i status:%02x "
117 " sense: %02x ASC: %02x ASCQ:%02x\n",
118 (uchar_t)scmd->uscsi_cdb[0], ret,
119 scmd->uscsi_status,
120 (uchar_t)SENSE_KEY(rqbuf),
121 (uchar_t)ASC(rqbuf), (uchar_t)ASCQ(rqbuf)));
122 }
123
124 break;
125 }
126
127 if (retries) {
128 HAL_DEBUG (("total retries: %d\n", total_retries));
129 }
130
131 /* do not leak address from local stack and restore flags */
132 scmd->uscsi_rqbuf = NULL;
133 scmd->uscsi_flags = f;
134 scmd->uscsi_rqlen = 0;
135 return (ret);
136 }
137
138 int
mode_sense(int fd,uchar_t pc,int dbd,int page_len,uchar_t * buffer)139 mode_sense(int fd, uchar_t pc, int dbd, int page_len, uchar_t *buffer)
140 {
141 struct uscsi_cmd scmd;
142 char cdb[16];
143
144 uscsi_cmd_init(&scmd, cdb, sizeof (cdb));
145 scmd.uscsi_flags = USCSI_READ|USCSI_SILENT;
146 scmd.uscsi_buflen = page_len;
147 scmd.uscsi_bufaddr = (char *)buffer;
148 scmd.uscsi_timeout = CD_USCSI_TIMEOUT;
149 scmd.uscsi_cdblen = 0xa;
150 scmd.uscsi_cdb[0] = 0x5a; /* MODE SENSE 10 */
151 if (dbd) {
152 scmd.uscsi_cdb[1] = 0x8; /* no block descriptors */
153 }
154 scmd.uscsi_cdb[2] = pc;
155 scmd.uscsi_cdb[7] = (page_len >> 8) & 0xff;
156 scmd.uscsi_cdb[8] = page_len & 0xff;
157
158 return (uscsi(fd, &scmd) == 0);
159 }
160
161 /*
162 * will get the mode page only i.e. will strip off the header.
163 */
164 int
get_mode_page(int fd,int page_no,int pc,int buf_len,uchar_t * buffer,int * plen)165 get_mode_page(int fd, int page_no, int pc, int buf_len, uchar_t *buffer, int *plen)
166 {
167 int ret;
168 uchar_t byte2;
169 uchar_t buf[256];
170 uint_t header_len, page_len, copy_cnt;
171
172 byte2 = (uchar_t)(((pc << 6) & 0xC0) | (page_no & 0x3f));
173
174 /* Ask 254 bytes only to make our IDE driver happy */
175 if ((ret = mode_sense(fd, byte2, 1, 254, buf)) == 0) {
176 return (0);
177 }
178
179 header_len = 8 + GET16(&buf[6]);
180 page_len = buf[header_len + 1] + 2;
181
182 copy_cnt = (page_len > buf_len) ? buf_len : page_len;
183 (void) memcpy(buffer, &buf[header_len], copy_cnt);
184
185 if (plen) {
186 *plen = page_len;
187 }
188
189 return (1);
190 }
191
192 /* Get information about the Logical Unit's capabilities */
193 int
get_configuration(int fd,uint16_t feature,int bufsize,uchar_t * buf)194 get_configuration(int fd, uint16_t feature, int bufsize, uchar_t *buf)
195 {
196 struct uscsi_cmd scmd;
197 char cdb[16];
198
199 uscsi_cmd_init(&scmd, cdb, sizeof (cdb));
200 scmd.uscsi_flags = USCSI_READ|USCSI_SILENT;
201 scmd.uscsi_timeout = CD_USCSI_TIMEOUT;
202 scmd.uscsi_cdb[0] = 0x46; /* GET CONFIGURATION */
203 scmd.uscsi_cdb[1] = 0x2; /* request type */
204 scmd.uscsi_cdb[2] = (feature >> 8) & 0xff; /* starting feature # */
205 scmd.uscsi_cdb[3] = feature & 0xff;
206 scmd.uscsi_cdb[7] = (bufsize >> 8) & 0xff; /* allocation length */
207 scmd.uscsi_cdb[8] = bufsize & 0xff;
208 scmd.uscsi_cdblen = 10;
209 scmd.uscsi_bufaddr = (char *)buf;
210 scmd.uscsi_buflen = bufsize;
211
212 return (uscsi(fd, &scmd) == 0);
213 }
214
215 boolean_t
get_current_profile(int fd,int * profile)216 get_current_profile(int fd, int *profile)
217 {
218 size_t i;
219 uchar_t smallbuf[8];
220 size_t buflen;
221 uchar_t *bufp;
222 int ret = B_FALSE;
223
224 /*
225 * first determine amount of memory needed to hold all profiles.
226 * The first four bytes of smallbuf concatenated tell us the
227 * number of bytes of memory we need but do not take themselves
228 * into account. Therefore, add four to allocate that number
229 * of bytes.
230 */
231 if (get_configuration(fd, 0, 8, &smallbuf[0])) {
232 buflen = GET32(smallbuf) + 4;
233 bufp = (uchar_t *)malloc(buflen);
234
235 /* now get all profiles */
236 if (get_configuration(fd, 0, buflen, bufp)) {
237 *profile = GET16(&bufp[6]);
238 ret = B_TRUE;
239 }
240 free(bufp);
241 }
242
243 return (ret);
244 }
245
246 void
walk_profiles(int fd,int (* f)(void *,int,boolean_t),void * arg)247 walk_profiles(int fd, int (*f)(void *, int, boolean_t), void *arg)
248 {
249 size_t i;
250 uint16_t profile, current_profile;
251 uchar_t smallbuf[8];
252 size_t buflen;
253 uchar_t *bufp;
254 int ret;
255
256 /*
257 * first determine amount of memory needed to hold all profiles.
258 * The first four bytes of smallbuf concatenated tell us the
259 * number of bytes of memory we need but do not take themselves
260 * into account. Therefore, add four to allocate that number
261 * of bytes.
262 */
263 if (get_configuration(fd, 0, 8, &smallbuf[0])) {
264 buflen = GET32(smallbuf) + 4;
265 bufp = (uchar_t *)malloc(buflen);
266
267 /* now get all profiles */
268 if (get_configuration(fd, 0, buflen, bufp)) {
269 current_profile = GET16(&bufp[6]);
270 for (i = 8 + 4; i < buflen; i += 4) {
271 profile = GET16(&bufp[i]);
272 ret = f(arg, profile, (profile == current_profile));
273 if (ret == CDUTIL_WALK_STOP) {
274 break;
275 }
276 }
277 }
278
279 free(bufp);
280 }
281 }
282
283 /* retrieve speed list from the Write Speed Performance Descriptor Blocks
284 */
285 void
get_write_speeds(uchar_t * page,int n,intlist_t ** speeds,int * n_speeds,intlist_t ** speeds_mem)286 get_write_speeds(uchar_t *page, int n, intlist_t **speeds, int *n_speeds, intlist_t **speeds_mem)
287 {
288 uchar_t *p = page + 2;
289 int i;
290 intlist_t **nextp;
291 intlist_t *current;
292 boolean_t skip;
293
294 *n_speeds = 0;
295 *speeds = NULL;
296 *speeds_mem = (intlist_t *)calloc(n, sizeof (intlist_t));
297 if (*speeds_mem == NULL) {
298 return;
299 }
300
301 for (i = 0; i < n; i++, p += 4) {
302 current = &(*speeds_mem)[i];
303 current->val = GET16(p);
304
305 /* keep the list sorted */
306 skip = B_FALSE;
307 for (nextp = speeds; *nextp != NULL; nextp = &((*nextp)->next)) {
308 if (current->val == (*nextp)->val) {
309 skip = B_TRUE; /* skip duplicates */
310 break;
311 } else if (current->val > (*nextp)->val) {
312 break;
313 }
314 }
315 if (!skip) {
316 current->next = *nextp;
317 *nextp = current;
318 *n_speeds++;
319 }
320 }
321 }
322
323 void
get_read_write_speeds(int fd,int * read_speed,int * write_speed,intlist_t ** speeds,int * n_speeds,intlist_t ** speeds_mem)324 get_read_write_speeds(int fd, int *read_speed, int *write_speed,
325 intlist_t **speeds, int *n_speeds, intlist_t **speeds_mem)
326 {
327 int page_len;
328 uchar_t p[254];
329 int n; /* number of write speed performance descriptor blocks */
330
331 *read_speed = *write_speed = 0;
332 *speeds = *speeds_mem = NULL;
333
334 if (!get_mode_page(fd, 0x2A, 0, sizeof (p), p, &page_len)) {
335 return;
336 }
337
338 if (page_len > 8) {
339 *read_speed = GET16(&p[8]);
340 }
341 if (page_len > 18) {
342 *write_speed = GET16(&p[18]);
343 }
344 if (page_len < 28) {
345 printf("MMC-2\n");
346 return;
347 } else {
348 printf("MMC-3\n");
349 }
350
351 *write_speed = GET16(&p[28]);
352
353 if (page_len < 30) {
354 return;
355 }
356
357 /* retrieve speed list */
358 n = GET16(&p[30]);
359 n = min(n, (sizeof (p) - 32) / 4);
360
361 get_write_speeds(&p[32], n, speeds, n_speeds, speeds_mem);
362
363 if (*speeds != NULL) {
364 *write_speed = max(*write_speed, (*speeds)[0].val);
365 }
366 }
367
368 boolean_t
get_disc_info(int fd,disc_info_t * di)369 get_disc_info(int fd, disc_info_t *di)
370 {
371 struct uscsi_cmd scmd;
372 char cdb[16];
373 uint8_t buf[32];
374 int bufsize = sizeof (buf);
375
376 bzero(buf, bufsize);
377 uscsi_cmd_init(&scmd, cdb, sizeof (cdb));
378 scmd.uscsi_flags = USCSI_READ|USCSI_SILENT;
379 scmd.uscsi_timeout = CD_USCSI_TIMEOUT;
380 scmd.uscsi_cdb[0] = 0x51; /* READ DISC INFORMATION */
381 scmd.uscsi_cdb[7] = (bufsize >> 8) & 0xff; /* allocation length */
382 scmd.uscsi_cdb[8] = bufsize & 0xff;
383 scmd.uscsi_cdblen = 10;
384 scmd.uscsi_bufaddr = (char *)buf;
385 scmd.uscsi_buflen = bufsize;
386
387 if ((uscsi(fd, &scmd)) != 0) {
388 return (B_FALSE);
389 }
390
391 /*
392 * According to MMC-5 6.22.3.2, the Disc Information Length should be
393 * 32+8*(Number of OPC Tables). Some devices, like U3 sticks, return 0.
394 * Yet some drives can return less than 32. We only need the first 22.
395 */
396 if (GET16(&buf[0]) < 22) {
397 return (B_FALSE);
398 }
399
400 di->disc_status = buf[2] & 0x03;
401 di->erasable = buf[2] & 0x10;
402 if ((buf[21] != 0) && (buf[21] != 0xff)) {
403 di->capacity = ((buf[21] * 60) + buf[22]) * 75;
404 } else {
405 di->capacity = 0;
406 }
407
408 return (B_TRUE);
409 }
410
411 /*
412 * returns current/maximum format capacity in bytes
413 */
414 boolean_t
read_format_capacity(int fd,uint64_t * capacity)415 read_format_capacity(int fd, uint64_t *capacity)
416 {
417 struct uscsi_cmd scmd;
418 char cdb[16];
419 uint8_t buf[32];
420 int bufsize = sizeof (buf);
421 uint32_t num_blocks;
422 uint32_t block_len;
423
424 bzero(buf, bufsize);
425 uscsi_cmd_init(&scmd, cdb, sizeof (cdb));
426 scmd.uscsi_flags = USCSI_READ|USCSI_SILENT;
427 scmd.uscsi_timeout = CD_USCSI_TIMEOUT;
428 scmd.uscsi_cdb[0] = 0x23; /* READ FORMAT CAPACITIRES */
429 scmd.uscsi_cdb[7] = (bufsize >> 8) & 0xff; /* allocation length */
430 scmd.uscsi_cdb[8] = bufsize & 0xff;
431 scmd.uscsi_cdblen = 12;
432 scmd.uscsi_bufaddr = (char *)buf;
433 scmd.uscsi_buflen = bufsize;
434
435 if ((uscsi(fd, &scmd)) != 0) {
436 return (B_FALSE);
437 }
438
439 num_blocks = (uint32_t)(buf[4] << 24) + (buf[5] << 16) + (buf[6] << 8) + buf[7];
440 block_len = (uint32_t)(buf[9] << 16) + (buf[10] << 8) + buf[11];
441 *capacity = (uint64_t)num_blocks * block_len;
442
443 return (B_TRUE);
444 }
445
446 boolean_t
get_media_info(int fd,struct dk_minfo * minfop)447 get_media_info(int fd, struct dk_minfo *minfop)
448 {
449 return (ioctl(fd, DKIOCGMEDIAINFO, minfop) != -1);
450 }
451
452 /*
453 * given current profile, use the best method for determining
454 * disc capacity (in bytes)
455 */
456 boolean_t
get_disc_capacity_for_profile(int fd,int profile,uint64_t * capacity)457 get_disc_capacity_for_profile(int fd, int profile, uint64_t *capacity)
458 {
459 struct dk_minfo mi;
460 disc_info_t di;
461 boolean_t ret = B_FALSE;
462
463 switch (profile) {
464 case 0x08: /* CD-ROM */
465 case 0x10: /* DVD-ROM */
466 if (get_media_info(fd, &mi) && (mi.dki_capacity > 1)) {
467 *capacity = mi.dki_capacity * mi.dki_lbsize;
468 ret = B_TRUE;
469 }
470 break;
471 default:
472 if (read_format_capacity(fd, capacity) && (*capacity > 0)) {
473 ret = B_TRUE;
474 } else if (get_disc_info(fd, &di) && (di.capacity > 0)) {
475 if (get_media_info(fd, &mi)) {
476 *capacity = di.capacity * mi.dki_lbsize;
477 ret = B_TRUE;
478 }
479 }
480 }
481
482 return (ret);
483 }
484
485 boolean_t
read_toc(int fd,int format,int trackno,int buflen,uchar_t * buf)486 read_toc(int fd, int format, int trackno, int buflen, uchar_t *buf)
487 {
488 struct uscsi_cmd scmd;
489 char cdb[16];
490
491 bzero(buf, buflen);
492 uscsi_cmd_init(&scmd, cdb, sizeof (cdb));
493 scmd.uscsi_flags = USCSI_READ|USCSI_SILENT;
494 scmd.uscsi_timeout = CD_USCSI_TIMEOUT;
495 scmd.uscsi_cdb[0] = 0x43 /* READ_TOC_CMD */;
496 scmd.uscsi_cdb[2] = format & 0xf;
497 scmd.uscsi_cdb[6] = trackno;
498 scmd.uscsi_cdb[8] = buflen & 0xff;
499 scmd.uscsi_cdb[7] = (buflen >> 8) & 0xff;
500 scmd.uscsi_cdblen = 10;
501 scmd.uscsi_bufaddr = (char *)buf;
502 scmd.uscsi_buflen = buflen;
503
504 if ((uscsi(fd, &scmd)) != 0) {
505 return (B_FALSE);
506 }
507
508 return (B_TRUE);
509 }
510