1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26 #include <stdio.h>
27 #include <sys/types.h>
28 #include <sys/scsi/impl/uscsi.h>
29 #include <string.h>
30 #include <unistd.h>
31 #include <errno.h>
32 #include <libintl.h>
33
34 #include "transport.h"
35 #include "main.h"
36 #include "util.h"
37 #include "mmc.h"
38
39 char rqbuf[RQBUFLEN];
40 uchar_t uscsi_status, rqstatus, rqresid;
41 static struct uscsi_cmd uscmd;
42 static char ucdb[16];
43 static uint_t total_retries;
44
45 struct uscsi_cmd *
get_uscsi_cmd(void)46 get_uscsi_cmd(void)
47 {
48 (void) memset(&uscmd, 0, sizeof (uscmd));
49 (void) memset(ucdb, 0, 16);
50 uscmd.uscsi_cdb = ucdb;
51 return (&uscmd);
52 }
53
54 int
uscsi(int fd,struct uscsi_cmd * scmd)55 uscsi(int fd, struct uscsi_cmd *scmd)
56 {
57 int ret, global_rqsense;
58 int retries, max_retries;
59
60 /* set up for request sense extensions */
61 if (!(scmd->uscsi_flags & USCSI_RQENABLE)) {
62 scmd->uscsi_flags |= USCSI_RQENABLE;
63 scmd->uscsi_rqlen = RQBUFLEN;
64 scmd->uscsi_rqbuf = rqbuf;
65 global_rqsense = 1;
66 } else {
67 global_rqsense = 0;
68 }
69
70 /*
71 * Some DVD drives may have a delay for writing or sync cache, and
72 * read media info (done after syncing cache). This can take a
73 * significant number of time. Such as the Pioneer A0X which will
74 * generate TOC after the cache is full in the middle of writing.
75 */
76
77 if ((device_type != CD_RW) && ((scmd->uscsi_cdb[0] == WRITE_10_CMD) ||
78 (scmd->uscsi_cdb[0] == READ_INFO_CMD) || (scmd->uscsi_cdb[0] ==
79 SYNC_CACHE_CMD) || (scmd->uscsi_cdb[0] == CLOSE_TRACK_CMD))) {
80
81 max_retries = 500;
82 } else {
83 /*
84 * Pioneer A08/A09 retries approx 30 times.
85 */
86 max_retries = 40;
87 }
88
89 /*
90 * The device may be busy or slow and fail with a not ready status.
91 * we'll allow a limited number of retries to give the drive time
92 * to recover.
93 */
94 for (retries = 0; retries < max_retries; retries++) {
95
96 scmd->uscsi_status = 0;
97
98 if (global_rqsense)
99 (void) memset(rqbuf, 0, RQBUFLEN);
100
101 if (debug && verbose) {
102 int i;
103
104 (void) printf("cmd:[");
105 for (i = 0; i < scmd->uscsi_cdblen; i++)
106 (void) printf("0x%02x ",
107 (uchar_t)scmd->uscsi_cdb[i]);
108 (void) printf("]\n");
109 }
110
111 /*
112 * We need to have root privledges in order to use
113 * uscsi commands on the device.
114 */
115
116 raise_priv();
117 ret = ioctl(fd, USCSICMD, scmd);
118 lower_priv();
119
120 /* maintain consistency in case of sgen */
121 if ((ret == 0) && (scmd->uscsi_status == 2)) {
122 ret = -1;
123 errno = EIO;
124 }
125
126 /* if error and extended request sense, retrieve errors */
127 if (global_rqsense && (ret < 0) && (scmd->uscsi_status == 2)) {
128 /*
129 * The drive is not ready to recieve commands but
130 * may be in the process of becoming ready.
131 * sleep for a short time then retry command.
132 * SENSE/ASC = 2/4 : not ready
133 * ASCQ = 0 Not Reportable.
134 * ASCQ = 1 Becoming ready.
135 * ASCQ = 4 FORMAT in progress.
136 * ASCQ = 7 Operation in progress.
137 * ASCQ = 8 Long write in progress.
138 */
139 if ((SENSE_KEY(rqbuf) == 2) && (ASC(rqbuf) == 4) &&
140 ((ASCQ(rqbuf) == 0) || (ASCQ(rqbuf) == 1) ||
141 (ASCQ(rqbuf) == 4)) || (ASCQ(rqbuf) == 7)) {
142 total_retries++;
143 (void) sleep(3);
144 continue;
145 }
146
147 /*
148 * we do not print this out under normal circumstances
149 * since we have BUFE enabled and do not want to alarm
150 * users with uneccessary messages.
151 */
152 if (debug) {
153 if ((SENSE_KEY(rqbuf) == 5) && (ASC(rqbuf) ==
154 0x21) && (ASCQ(rqbuf) == 2)) {
155 (void) printf(gettext(
156 "Buffer underrun occurred! trying to recover...\n"));
157 }
158 }
159
160 /*
161 * long write operation in progress, ms_delay is
162 * used for some fast drives with a short drive
163 * buffer. Such as Pioneer DVD-RW drives. They will
164 * begin to generate TOC when the buffer is initially
165 * full, then resume operation a few minutes later
166 * with the buffer emptying quickly.
167 */
168 if ((SENSE_KEY(rqbuf) == 2) && (ASC(rqbuf) == 4) &&
169 (ASCQ(rqbuf) == 8)) {
170 total_retries++;
171 /*
172 * In Simulation write mode, we use the
173 * READ_INFO_CMD to check if all the previous
174 * writes completed. Sleeping 500 ms will not
175 * be sufficient in all cases for DVDs.
176 */
177 if ((device_type != CD_RW) &&
178 ((scmd->uscsi_cdb[0] == CLOSE_TRACK_CMD) ||
179 ((scmd->uscsi_cdb[0] == READ_INFO_CMD) &&
180 simulation)))
181 (void) sleep(3);
182 else
183 ms_delay(500);
184 continue;
185 }
186 /*
187 * Device is not ready to transmit or a device reset
188 * has occurred. wait for a short period of time then
189 * retry the command.
190 */
191 if ((SENSE_KEY(rqbuf) == 6) && ((ASC(rqbuf) == 0x28) ||
192 (ASC(rqbuf) == 0x29))) {
193 (void) sleep(3);
194 total_retries++;
195 continue;
196 }
197
198 if ((SENSE_KEY(rqbuf) == 5) &&
199 (device_type == DVD_PLUS ||
200 device_type == DVD_PLUS_W)) {
201 if (scmd->uscsi_cdb[0] == MODE_SELECT_10_CMD &&
202 ASC(rqbuf) == 0x26) {
203 ret = 1;
204 break;
205 }
206
207 if (scmd->uscsi_cdb[0] == REZERO_UNIT_CMD &&
208 ASC(rqbuf) == 0x20) {
209 ret = 1;
210 break;
211 }
212
213 }
214 /*
215 * Blank Sense, we don't know what the error is or if
216 * the command succeeded, Hope for the best. Some
217 * drives return blank sense periodically and will
218 * fail if this is removed.
219 */
220 if ((SENSE_KEY(rqbuf) == 0) && (ASC(rqbuf) == 0) &&
221 (ASCQ(rqbuf) == 0)) {
222 ret = 0;
223 break;
224 }
225
226 if (debug) {
227 (void) printf("cmd: 0x%02x ret:%i status:%02x "
228 " sense: %02x ASC: %02x ASCQ:%02x\n",
229 (uchar_t)scmd->uscsi_cdb[0], ret,
230 scmd->uscsi_status,
231 (uchar_t)SENSE_KEY(rqbuf),
232 (uchar_t)ASC(rqbuf), (uchar_t)ASCQ(rqbuf));
233 }
234 }
235
236 /* no errors we'll return */
237 break;
238 }
239
240 /* store the error status for later debug printing */
241 if ((ret < 0) && (global_rqsense)) {
242 uscsi_status = scmd->uscsi_status;
243 rqstatus = scmd->uscsi_rqstatus;
244 rqresid = scmd->uscsi_rqresid;
245
246 }
247
248 if (debug && retries) {
249 (void) printf("total retries: %d\n", total_retries);
250 }
251
252 return (ret);
253 }
254