12912Sartem /*************************************************************************** 22912Sartem * 34215Sartem * cdutils.c : CD/DVD utilities 42912Sartem * 5*9905SXiao-Lin.Zhang@Sun.COM * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 62912Sartem * Use is subject to license terms. 72912Sartem * 82912Sartem * Licensed under the Academic Free License version 2.1 92912Sartem * 102912Sartem **************************************************************************/ 112912Sartem 122912Sartem 132912Sartem #ifdef HAVE_CONFIG_H 142912Sartem # include <config.h> 152912Sartem #endif 162912Sartem 172912Sartem #include <stdio.h> 182912Sartem #include <sys/types.h> 192912Sartem #include <sys/scsi/impl/uscsi.h> 202912Sartem #include <string.h> 212912Sartem #include <strings.h> 222912Sartem #include <unistd.h> 232912Sartem #include <stdlib.h> 242912Sartem #include <errno.h> 252912Sartem #include <fcntl.h> 262912Sartem #include <sys/dkio.h> 272912Sartem #include <libintl.h> 282912Sartem 292912Sartem #include <logger.h> 302912Sartem 312912Sartem #include "cdutils.h" 322912Sartem 332912Sartem #define RQLEN 32 342912Sartem #define SENSE_KEY(rqbuf) (rqbuf[2]) /* scsi error category */ 352912Sartem #define ASC(rqbuf) (rqbuf[12]) /* additional sense code */ 362912Sartem #define ASCQ(rqbuf) (rqbuf[13]) /* ASC qualifier */ 372912Sartem 382912Sartem #define GET16(a) (((a)[0] << 8) | (a)[1]) 392912Sartem #define GET32(a) (((a)[0] << 24) | ((a)[1] << 16) | ((a)[2] << 8) | (a)[3]) 402912Sartem 412912Sartem #define CD_USCSI_TIMEOUT 60 422912Sartem 432912Sartem void 442912Sartem uscsi_cmd_init(struct uscsi_cmd *scmd, char *cdb, int cdblen) 452912Sartem { 462912Sartem bzero(scmd, sizeof (*scmd)); 472912Sartem bzero(cdb, cdblen); 482912Sartem scmd->uscsi_cdb = cdb; 492912Sartem } 502912Sartem 512912Sartem int 522912Sartem uscsi(int fd, struct uscsi_cmd *scmd) 532912Sartem { 542912Sartem char rqbuf[RQLEN]; 552912Sartem int ret; 562912Sartem int i, retries, total_retries; 572912Sartem int max_retries = 20; 582912Sartem 592912Sartem scmd->uscsi_flags |= USCSI_RQENABLE; 602912Sartem scmd->uscsi_rqlen = RQLEN; 612912Sartem scmd->uscsi_rqbuf = rqbuf; 622912Sartem 632912Sartem for (retries = 0; retries < max_retries; retries++) { 642912Sartem scmd->uscsi_status = 0; 652912Sartem memset(rqbuf, 0, RQLEN); 662912Sartem 672912Sartem ret = ioctl(fd, USCSICMD, scmd); 682912Sartem 692912Sartem if ((ret == 0) && (scmd->uscsi_status == 2)) { 702912Sartem ret = -1; 712912Sartem errno = EIO; 722912Sartem } 732912Sartem if ((ret < 0) && (scmd->uscsi_status == 2)) { 742912Sartem /* 752912Sartem * The drive is not ready to recieve commands but 762912Sartem * may be in the process of becoming ready. 772912Sartem * sleep for a short time then retry command. 782912Sartem * SENSE/ASC = 2/4 : not ready 792912Sartem * ASCQ = 0 Not Reportable. 802912Sartem * ASCQ = 1 Becoming ready. 812912Sartem * ASCQ = 4 FORMAT in progress. 822912Sartem * ASCQ = 7 Operation in progress. 832912Sartem */ 842912Sartem if ((SENSE_KEY(rqbuf) == 2) && (ASC(rqbuf) == 4) && 852912Sartem ((ASCQ(rqbuf) == 0) || (ASCQ(rqbuf) == 1) || 862912Sartem (ASCQ(rqbuf) == 4)) || (ASCQ(rqbuf) == 7)) { 872912Sartem total_retries++; 882912Sartem sleep(1); 892912Sartem continue; 902912Sartem } 912912Sartem 922912Sartem /* 932912Sartem * Device is not ready to transmit or a device reset 942912Sartem * has occurred. wait for a short period of time then 952912Sartem * retry the command. 962912Sartem */ 972912Sartem if ((SENSE_KEY(rqbuf) == 6) && ((ASC(rqbuf) == 0x28) || 982912Sartem (ASC(rqbuf) == 0x29))) { 992912Sartem sleep(1); 1002912Sartem total_retries++; 1012912Sartem continue; 1022912Sartem } 1032912Sartem /* 1042912Sartem * Blank Sense, we don't know what the error is or if 1052912Sartem * the command succeeded, Hope for the best. Some 1062912Sartem * drives return blank sense periodically and will 1072912Sartem * fail if this is removed. 1082912Sartem */ 1092912Sartem if ((SENSE_KEY(rqbuf) == 0) && (ASC(rqbuf) == 0) && 1102912Sartem (ASCQ(rqbuf) == 0)) { 1112912Sartem ret = 0; 1122912Sartem break; 1132912Sartem } 1142912Sartem 1152912Sartem HAL_DEBUG (("cmd: 0x%02x ret:%i status:%02x " 1162912Sartem " sense: %02x ASC: %02x ASCQ:%02x\n", 1172912Sartem (uchar_t)scmd->uscsi_cdb[0], ret, 1182912Sartem scmd->uscsi_status, 1192912Sartem (uchar_t)SENSE_KEY(rqbuf), 1202912Sartem (uchar_t)ASC(rqbuf), (uchar_t)ASCQ(rqbuf))); 1212912Sartem } 1222912Sartem 1232912Sartem break; 1242912Sartem } 1252912Sartem 1262912Sartem if (retries) { 1272912Sartem HAL_DEBUG (("total retries: %d\n", total_retries)); 1282912Sartem } 1292912Sartem 1302912Sartem return (ret); 1312912Sartem } 1322912Sartem 1332912Sartem int 1342912Sartem mode_sense(int fd, uchar_t pc, int dbd, int page_len, uchar_t *buffer) 1352912Sartem { 1362912Sartem struct uscsi_cmd scmd; 1372912Sartem char cdb[16]; 1382912Sartem 1392912Sartem uscsi_cmd_init(&scmd, cdb, sizeof (cdb)); 1402912Sartem scmd.uscsi_flags = USCSI_READ|USCSI_SILENT; 1412912Sartem scmd.uscsi_buflen = page_len; 1422912Sartem scmd.uscsi_bufaddr = (char *)buffer; 1432912Sartem scmd.uscsi_timeout = CD_USCSI_TIMEOUT; 1442912Sartem scmd.uscsi_cdblen = 0xa; 1452912Sartem scmd.uscsi_cdb[0] = 0x5a; /* MODE SENSE 10 */ 1462912Sartem if (dbd) { 1472912Sartem scmd.uscsi_cdb[1] = 0x8; /* no block descriptors */ 1482912Sartem } 1492912Sartem scmd.uscsi_cdb[2] = pc; 1502912Sartem scmd.uscsi_cdb[7] = (page_len >> 8) & 0xff; 1512912Sartem scmd.uscsi_cdb[8] = page_len & 0xff; 1522912Sartem 1532912Sartem return (uscsi(fd, &scmd) == 0); 1542912Sartem } 1552912Sartem 1562912Sartem /* 1572912Sartem * will get the mode page only i.e. will strip off the header. 1582912Sartem */ 1592912Sartem int 1602912Sartem get_mode_page(int fd, int page_no, int pc, int buf_len, uchar_t *buffer, int *plen) 1612912Sartem { 1622912Sartem int ret; 1632912Sartem uchar_t byte2; 1642912Sartem uchar_t buf[256]; 1652912Sartem uint_t header_len, page_len, copy_cnt; 1662912Sartem 1672912Sartem byte2 = (uchar_t)(((pc << 6) & 0xC0) | (page_no & 0x3f)); 1682912Sartem 1692912Sartem /* Ask 254 bytes only to make our IDE driver happy */ 1702912Sartem if ((ret = mode_sense(fd, byte2, 1, 254, buf)) == 0) { 1712912Sartem return (0); 1722912Sartem } 1732912Sartem 1742912Sartem header_len = 8 + GET16(&buf[6]); 1752912Sartem page_len = buf[header_len + 1] + 2; 1762912Sartem 1772912Sartem copy_cnt = (page_len > buf_len) ? buf_len : page_len; 1782912Sartem (void) memcpy(buffer, &buf[header_len], copy_cnt); 1792912Sartem 1802912Sartem if (plen) { 1812912Sartem *plen = page_len; 1822912Sartem } 1832912Sartem 1842912Sartem return (1); 1852912Sartem } 1862912Sartem 1872912Sartem /* Get information about the Logical Unit's capabilities */ 1882912Sartem int 1892912Sartem get_configuration(int fd, uint16_t feature, int bufsize, uchar_t *buf) 1902912Sartem { 1912912Sartem struct uscsi_cmd scmd; 1922912Sartem char cdb[16]; 1932912Sartem 1942912Sartem uscsi_cmd_init(&scmd, cdb, sizeof (cdb)); 1952912Sartem scmd.uscsi_flags = USCSI_READ|USCSI_SILENT; 1962912Sartem scmd.uscsi_timeout = CD_USCSI_TIMEOUT; 1972912Sartem scmd.uscsi_cdb[0] = 0x46; /* GET CONFIGURATION */ 1982912Sartem scmd.uscsi_cdb[1] = 0x2; /* request type */ 1992912Sartem scmd.uscsi_cdb[2] = (feature >> 8) & 0xff; /* starting feature # */ 2002912Sartem scmd.uscsi_cdb[3] = feature & 0xff; 2012912Sartem scmd.uscsi_cdb[7] = (bufsize >> 8) & 0xff; /* allocation length */ 2022912Sartem scmd.uscsi_cdb[8] = bufsize & 0xff; 2032912Sartem scmd.uscsi_cdblen = 10; 2042912Sartem scmd.uscsi_bufaddr = (char *)buf; 2052912Sartem scmd.uscsi_buflen = bufsize; 2062912Sartem 2072912Sartem return (uscsi(fd, &scmd) == 0); 2082912Sartem } 2092912Sartem 2102912Sartem boolean_t 2112912Sartem get_current_profile(int fd, int *profile) 2122912Sartem { 2132912Sartem size_t i; 214*9905SXiao-Lin.Zhang@Sun.COM uchar_t smallbuf[8]; 2152912Sartem size_t buflen; 2162912Sartem uchar_t *bufp; 2172912Sartem int ret = B_FALSE; 2182912Sartem 219*9905SXiao-Lin.Zhang@Sun.COM /* 220*9905SXiao-Lin.Zhang@Sun.COM * first determine amount of memory needed to hold all profiles. 221*9905SXiao-Lin.Zhang@Sun.COM * The first four bytes of smallbuf concatenated tell us the 222*9905SXiao-Lin.Zhang@Sun.COM * number of bytes of memory we need but do not take themselves 223*9905SXiao-Lin.Zhang@Sun.COM * into account. Therefore, add four to allocate that number 224*9905SXiao-Lin.Zhang@Sun.COM * of bytes. 225*9905SXiao-Lin.Zhang@Sun.COM */ 226*9905SXiao-Lin.Zhang@Sun.COM if (get_configuration(fd, 0, 8, &smallbuf[0])) { 2272912Sartem buflen = GET32(smallbuf) + 4; 2282912Sartem bufp = (uchar_t *)malloc(buflen); 2292912Sartem 2302912Sartem /* now get all profiles */ 2312912Sartem if (get_configuration(fd, 0, buflen, bufp)) { 2322912Sartem *profile = GET16(&bufp[6]); 2332912Sartem ret = B_TRUE; 2342912Sartem } 2352912Sartem free(bufp); 2362912Sartem } 2372912Sartem 2382912Sartem return (ret); 2392912Sartem } 2402912Sartem 2412912Sartem void 2422912Sartem walk_profiles(int fd, int (*f)(void *, int, boolean_t), void *arg) 2432912Sartem { 2442912Sartem size_t i; 2452912Sartem uint16_t profile, current_profile; 246*9905SXiao-Lin.Zhang@Sun.COM uchar_t smallbuf[8]; 2472912Sartem size_t buflen; 2482912Sartem uchar_t *bufp; 2492912Sartem int ret; 2502912Sartem 251*9905SXiao-Lin.Zhang@Sun.COM /* 252*9905SXiao-Lin.Zhang@Sun.COM * first determine amount of memory needed to hold all profiles. 253*9905SXiao-Lin.Zhang@Sun.COM * The first four bytes of smallbuf concatenated tell us the 254*9905SXiao-Lin.Zhang@Sun.COM * number of bytes of memory we need but do not take themselves 255*9905SXiao-Lin.Zhang@Sun.COM * into account. Therefore, add four to allocate that number 256*9905SXiao-Lin.Zhang@Sun.COM * of bytes. 257*9905SXiao-Lin.Zhang@Sun.COM */ 258*9905SXiao-Lin.Zhang@Sun.COM if (get_configuration(fd, 0, 8, &smallbuf[0])) { 2592912Sartem buflen = GET32(smallbuf) + 4; 2602912Sartem bufp = (uchar_t *)malloc(buflen); 2612912Sartem 2622912Sartem /* now get all profiles */ 2632912Sartem if (get_configuration(fd, 0, buflen, bufp)) { 2642912Sartem current_profile = GET16(&bufp[6]); 2652912Sartem for (i = 8 + 4; i < buflen; i += 4) { 2662912Sartem profile = GET16(&bufp[i]); 2672912Sartem ret = f(arg, profile, (profile == current_profile)); 2682912Sartem if (ret == CDUTIL_WALK_STOP) { 2692912Sartem break; 2702912Sartem } 2712912Sartem } 2722912Sartem } 2732912Sartem 2742912Sartem free(bufp); 2752912Sartem } 2762912Sartem } 2772912Sartem 2782912Sartem /* retrieve speed list from the Write Speed Performance Descriptor Blocks 2792912Sartem */ 2802912Sartem void 2812912Sartem get_write_speeds(uchar_t *page, int n, intlist_t **speeds, int *n_speeds, intlist_t **speeds_mem) 2822912Sartem { 2832912Sartem uchar_t *p = page + 2; 2842912Sartem int i; 2852912Sartem intlist_t **nextp; 2862912Sartem intlist_t *current; 2872912Sartem boolean_t skip; 2882912Sartem 2892912Sartem *n_speeds = 0; 2902912Sartem *speeds = NULL; 2912912Sartem *speeds_mem = (intlist_t *)calloc(n, sizeof (intlist_t)); 2922912Sartem if (*speeds_mem == NULL) { 2932912Sartem return; 2942912Sartem } 2952912Sartem 2962912Sartem for (i = 0; i < n; i++, p += 4) { 2972912Sartem current = &(*speeds_mem)[i]; 2982912Sartem current->val = GET16(p); 2992912Sartem 3002912Sartem /* keep the list sorted */ 3012912Sartem skip = B_FALSE; 3022912Sartem for (nextp = speeds; *nextp != NULL; nextp = &((*nextp)->next)) { 3032912Sartem if (current->val == (*nextp)->val) { 3042912Sartem skip = B_TRUE; /* skip duplicates */ 3052912Sartem break; 3062912Sartem } else if (current->val > (*nextp)->val) { 3072912Sartem break; 3082912Sartem } 3092912Sartem } 3102912Sartem if (!skip) { 3112912Sartem current->next = *nextp; 3122912Sartem *nextp = current; 3132912Sartem *n_speeds++; 3142912Sartem } 3152912Sartem } 3162912Sartem } 3172912Sartem 3182912Sartem void 3192912Sartem get_read_write_speeds(int fd, int *read_speed, int *write_speed, 3202912Sartem intlist_t **speeds, int *n_speeds, intlist_t **speeds_mem) 3212912Sartem { 3222912Sartem int page_len; 3232912Sartem uchar_t p[254]; 3242912Sartem int n; /* number of write speed performance descriptor blocks */ 3252912Sartem 3262912Sartem *read_speed = *write_speed = 0; 3272912Sartem *speeds = *speeds_mem = NULL; 3282912Sartem 3292912Sartem if (!get_mode_page(fd, 0x2A, 0, sizeof (p), p, &page_len)) { 3302912Sartem return; 3312912Sartem } 3322912Sartem 3332912Sartem if (page_len > 8) { 3342912Sartem *read_speed = GET16(&p[8]); 3352912Sartem } 3362912Sartem if (page_len > 18) { 3372912Sartem *write_speed = GET16(&p[18]); 3382912Sartem } 3392912Sartem if (page_len < 28) { 3402912Sartem printf("MMC-2\n"); 3412912Sartem return; 3422912Sartem } else { 3432912Sartem printf("MMC-3\n"); 3442912Sartem } 3452912Sartem 3462912Sartem *write_speed = GET16(&p[28]); 3472912Sartem 3482912Sartem if (page_len < 30) { 3492912Sartem return; 3502912Sartem } 3512912Sartem 3522912Sartem /* retrieve speed list */ 3532912Sartem n = GET16(&p[30]); 3542912Sartem n = min(n, (sizeof (p) - 32) / 4); 3552912Sartem 3562912Sartem get_write_speeds(&p[32], n, speeds, n_speeds, speeds_mem); 3572912Sartem 3582912Sartem if (*speeds != NULL) { 3592912Sartem *write_speed = max(*write_speed, (*speeds)[0].val); 3602912Sartem } 3612912Sartem } 3622912Sartem 3632912Sartem boolean_t 3642912Sartem get_disc_info(int fd, disc_info_t *di) 3652912Sartem { 3662912Sartem struct uscsi_cmd scmd; 3672912Sartem char cdb[16]; 3682912Sartem uint8_t buf[32]; 3692912Sartem int bufsize = sizeof (buf); 3702912Sartem 3712912Sartem bzero(buf, bufsize); 3722912Sartem uscsi_cmd_init(&scmd, cdb, sizeof (cdb)); 3732912Sartem scmd.uscsi_flags = USCSI_READ|USCSI_SILENT; 3742912Sartem scmd.uscsi_timeout = CD_USCSI_TIMEOUT; 3752912Sartem scmd.uscsi_cdb[0] = 0x51; /* READ DISC INFORMATION */ 3762912Sartem scmd.uscsi_cdb[7] = (bufsize >> 8) & 0xff; /* allocation length */ 3772912Sartem scmd.uscsi_cdb[8] = bufsize & 0xff; 3782912Sartem scmd.uscsi_cdblen = 10; 3792912Sartem scmd.uscsi_bufaddr = (char *)buf; 3802912Sartem scmd.uscsi_buflen = bufsize; 3812912Sartem 3822912Sartem if ((uscsi(fd, &scmd)) != 0) { 3832912Sartem return (B_FALSE); 3842912Sartem } 3852912Sartem 3864215Sartem /* 3874215Sartem * According to MMC-5 6.22.3.2, the Disc Information Length should be 3884215Sartem * 32+8*(Number of OPC Tables). Some devices, like U3 sticks, return 0. 3896597Sartem * Yet some drives can return less than 32. We only need the first 22. 3904215Sartem */ 3916597Sartem if (GET16(&buf[0]) < 22) { 3924215Sartem return (B_FALSE); 3934215Sartem } 3944215Sartem 3952912Sartem di->disc_status = buf[2] & 0x03; 3962912Sartem di->erasable = buf[2] & 0x10; 3972912Sartem if ((buf[21] != 0) && (buf[21] != 0xff)) { 3982912Sartem di->capacity = ((buf[21] * 60) + buf[22]) * 75; 3992912Sartem } else { 4002912Sartem di->capacity = 0; 4014215Sartem } 4022912Sartem 4032912Sartem return (B_TRUE); 4042912Sartem } 4052912Sartem 4062912Sartem /* 4072912Sartem * returns current/maximum format capacity in bytes 4082912Sartem */ 4092912Sartem boolean_t 4102912Sartem read_format_capacity(int fd, uint64_t *capacity) 4112912Sartem { 4122912Sartem struct uscsi_cmd scmd; 4132912Sartem char cdb[16]; 4142912Sartem uint8_t buf[32]; 4152912Sartem int bufsize = sizeof (buf); 4162912Sartem uint32_t num_blocks; 4172912Sartem uint32_t block_len; 4182912Sartem 4192912Sartem bzero(buf, bufsize); 4202912Sartem uscsi_cmd_init(&scmd, cdb, sizeof (cdb)); 4212912Sartem scmd.uscsi_flags = USCSI_READ|USCSI_SILENT; 4222912Sartem scmd.uscsi_timeout = CD_USCSI_TIMEOUT; 4232912Sartem scmd.uscsi_cdb[0] = 0x23; /* READ FORMAT CAPACITIRES */ 4242912Sartem scmd.uscsi_cdb[7] = (bufsize >> 8) & 0xff; /* allocation length */ 4252912Sartem scmd.uscsi_cdb[8] = bufsize & 0xff; 4262912Sartem scmd.uscsi_cdblen = 12; 4272912Sartem scmd.uscsi_bufaddr = (char *)buf; 4282912Sartem scmd.uscsi_buflen = bufsize; 4292912Sartem 4302912Sartem if ((uscsi(fd, &scmd)) != 0) { 4312912Sartem return (B_FALSE); 4322912Sartem } 4332912Sartem 4342912Sartem num_blocks = (uint32_t)(buf[4] << 24) + (buf[5] << 16) + (buf[6] << 8) + buf[7]; 4352912Sartem block_len = (uint32_t)(buf[9] << 16) + (buf[10] << 8) + buf[11]; 4362912Sartem *capacity = (uint64_t)num_blocks * block_len; 4372912Sartem 4382912Sartem return (B_TRUE); 4392912Sartem } 4402912Sartem 4412912Sartem boolean_t 4422912Sartem get_media_info(int fd, struct dk_minfo *minfop) 4432912Sartem { 4442912Sartem return (ioctl(fd, DKIOCGMEDIAINFO, minfop) != -1); 4452912Sartem } 4462912Sartem 4472912Sartem /* 4482912Sartem * given current profile, use the best method for determining 4492912Sartem * disc capacity (in bytes) 4502912Sartem */ 4512912Sartem boolean_t 4522912Sartem get_disc_capacity_for_profile(int fd, int profile, uint64_t *capacity) 4532912Sartem { 4542912Sartem struct dk_minfo mi; 4552912Sartem disc_info_t di; 4562912Sartem boolean_t ret = B_FALSE; 4572912Sartem 4582912Sartem switch (profile) { 4592912Sartem case 0x08: /* CD-ROM */ 4602912Sartem case 0x10: /* DVD-ROM */ 4612912Sartem if (get_media_info(fd, &mi) && (mi.dki_capacity > 1)) { 4622912Sartem *capacity = mi.dki_capacity * mi.dki_lbsize; 4632912Sartem ret = B_TRUE; 4642912Sartem } 4652912Sartem break; 4662912Sartem default: 4672912Sartem if (read_format_capacity(fd, capacity) && (*capacity > 0)) { 4682912Sartem ret = B_TRUE; 4692912Sartem } else if (get_disc_info(fd, &di) && (di.capacity > 0)) { 4702912Sartem if (get_media_info(fd, &mi)) { 4712912Sartem *capacity = di.capacity * mi.dki_lbsize; 4722912Sartem ret = B_TRUE; 4732912Sartem } 4742912Sartem } 4752912Sartem } 4762912Sartem 4772912Sartem return (ret); 4782912Sartem } 4792912Sartem 4802912Sartem boolean_t 4812912Sartem read_toc(int fd, int format, int trackno, int buflen, uchar_t *buf) 4822912Sartem { 4832912Sartem struct uscsi_cmd scmd; 4842912Sartem char cdb[16]; 4852912Sartem 4862912Sartem bzero(buf, buflen); 4872912Sartem uscsi_cmd_init(&scmd, cdb, sizeof (cdb)); 4882912Sartem scmd.uscsi_flags = USCSI_READ|USCSI_SILENT; 4892912Sartem scmd.uscsi_timeout = CD_USCSI_TIMEOUT; 4902912Sartem scmd.uscsi_cdb[0] = 0x43 /* READ_TOC_CMD */; 4912912Sartem scmd.uscsi_cdb[2] = format & 0xf; 4922912Sartem scmd.uscsi_cdb[6] = trackno; 4932912Sartem scmd.uscsi_cdb[8] = buflen & 0xff; 4942912Sartem scmd.uscsi_cdb[7] = (buflen >> 8) & 0xff; 4952912Sartem scmd.uscsi_cdblen = 10; 4962912Sartem scmd.uscsi_bufaddr = (char *)buf; 4972912Sartem scmd.uscsi_buflen = buflen; 4982912Sartem 4992912Sartem if ((uscsi(fd, &scmd)) != 0) { 5002912Sartem return (B_FALSE); 5012912Sartem } 5022912Sartem 5032912Sartem return (B_TRUE); 5042912Sartem } 505