1 |
|
|
/* $OpenBSD: libscsi.c,v 1.11 2016/01/28 17:26:10 gsoares Exp $ */ |
2 |
|
|
|
3 |
|
|
/* Copyright (c) 1994 HD Associates |
4 |
|
|
* (contact: dufault@hda.com) |
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 |
|
|
* 3. All advertising materials mentioning features or use of this software |
16 |
|
|
* must display the following acknowledgement: |
17 |
|
|
* This product includes software developed by HD Associates |
18 |
|
|
* 4. Neither the name of the HD Associaates nor the names of its contributors |
19 |
|
|
* may be used to endorse or promote products derived from this software |
20 |
|
|
* without specific prior written permission. |
21 |
|
|
* |
22 |
|
|
* THIS SOFTWARE IS PROVIDED BY HD ASSOCIATES``AS IS'' AND |
23 |
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
24 |
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
25 |
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL HD ASSOCIATES OR CONTRIBUTORS BE LIABLE |
26 |
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
27 |
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
28 |
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
29 |
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
30 |
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
31 |
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
32 |
|
|
* SUCH DAMAGE. |
33 |
|
|
* |
34 |
|
|
* $FreeBSD: scsi.c,v 1.6 1995/05/30 05:47:26 rgrimes Exp $ |
35 |
|
|
*/ |
36 |
|
|
#include <stdlib.h> |
37 |
|
|
#include <stdio.h> |
38 |
|
|
#include <ctype.h> |
39 |
|
|
#include <string.h> |
40 |
|
|
#include <sys/scsiio.h> |
41 |
|
|
#include <errno.h> |
42 |
|
|
#include <stdarg.h> |
43 |
|
|
#include <fcntl.h> |
44 |
|
|
|
45 |
|
|
#include "libscsi.h" |
46 |
|
|
|
47 |
|
|
static struct { |
48 |
|
|
FILE *db_f; |
49 |
|
|
int db_level; |
50 |
|
|
int db_trunc; |
51 |
|
|
} behave; |
52 |
|
|
|
53 |
|
|
/* scsireq_reset: Reset a scsireq structure. |
54 |
|
|
*/ |
55 |
|
|
scsireq_t * |
56 |
|
|
scsireq_reset(scsireq_t *scsireq) |
57 |
|
|
{ |
58 |
|
|
if (scsireq == 0) |
59 |
|
|
return scsireq; |
60 |
|
|
|
61 |
|
|
scsireq->flags = 0; /* info about the request status and type */ |
62 |
|
|
scsireq->timeout = 2000; /* 2 seconds */ |
63 |
|
|
bzero(scsireq->cmd, sizeof(scsireq->cmd)); |
64 |
|
|
scsireq->cmdlen = 0; |
65 |
|
|
/* Leave scsireq->databuf alone */ |
66 |
|
|
/* Leave scsireq->datalen alone */ |
67 |
|
|
scsireq->datalen_used = 0; |
68 |
|
|
bzero(scsireq->sense, sizeof(scsireq->sense)); |
69 |
|
|
scsireq->senselen = sizeof(scsireq->sense); |
70 |
|
|
scsireq->senselen_used = 0; |
71 |
|
|
scsireq->status = 0; |
72 |
|
|
scsireq->retsts = 0; |
73 |
|
|
scsireq->error = 0; |
74 |
|
|
|
75 |
|
|
return scsireq; |
76 |
|
|
} |
77 |
|
|
|
78 |
|
|
/* scsireq_new: Allocate and initialize a new scsireq. |
79 |
|
|
*/ |
80 |
|
|
scsireq_t * |
81 |
|
|
scsireq_new(void) |
82 |
|
|
{ |
83 |
|
|
scsireq_t *p = malloc(sizeof(scsireq_t)); |
84 |
|
|
|
85 |
|
|
if (p) |
86 |
|
|
scsireq_reset(p); |
87 |
|
|
|
88 |
|
|
return p; |
89 |
|
|
} |
90 |
|
|
|
91 |
|
|
/* |
92 |
|
|
* Decode: Decode the data section of a scsireq. This decodes |
93 |
|
|
* trivial grammar: |
94 |
|
|
* |
95 |
|
|
* fields : field fields |
96 |
|
|
* ; |
97 |
|
|
* |
98 |
|
|
* field : field_specifier |
99 |
|
|
* | control |
100 |
|
|
* ; |
101 |
|
|
* |
102 |
|
|
* control : 's' seek_value |
103 |
|
|
* | 's' '+' seek_value |
104 |
|
|
* ; |
105 |
|
|
* |
106 |
|
|
* seek_value : DECIMAL_NUMBER |
107 |
|
|
* | 'v' // For indirect seek, i.e., value from the arg list |
108 |
|
|
* ; |
109 |
|
|
* |
110 |
|
|
* field_specifier : type_specifier field_width |
111 |
|
|
* | '{' NAME '}' type_specifier field_width |
112 |
|
|
* ; |
113 |
|
|
* |
114 |
|
|
* field_width : DECIMAL_NUMBER |
115 |
|
|
* ; |
116 |
|
|
* |
117 |
|
|
* type_specifier : 'i' // Integral types (i1, i2, i3, i4) |
118 |
|
|
* | 'b' // Bits |
119 |
|
|
* | 't' // Bits |
120 |
|
|
* | 'c' // Character arrays |
121 |
|
|
* | 'z' // Character arrays with zeroed trailing spaces |
122 |
|
|
* ; |
123 |
|
|
* |
124 |
|
|
* Notes: |
125 |
|
|
* 1. Integral types are swapped into host order. |
126 |
|
|
* 2. Bit fields are allocated MSB to LSB to match the SCSI spec documentation. |
127 |
|
|
* 3. 's' permits "seeking" in the string. "s+DECIMAL" seeks relative to |
128 |
|
|
* DECIMAL; "sDECIMAL" seeks absolute to decimal. |
129 |
|
|
* 4. 's' permits an indirect reference. "sv" or "s+v" will get the |
130 |
|
|
* next integer value from the arg array. |
131 |
|
|
* 5. Field names can be anything between the braces |
132 |
|
|
* |
133 |
|
|
* BUGS: |
134 |
|
|
* i and b types are promoted to ints. |
135 |
|
|
* |
136 |
|
|
*/ |
137 |
|
|
static int |
138 |
|
|
do_buff_decode(u_char *databuf, size_t len, |
139 |
|
|
void (*arg_put)(void *, int , void *, int, char *), |
140 |
|
|
void *puthook, char *fmt, va_list ap) |
141 |
|
|
{ |
142 |
|
|
int assigned = 0; |
143 |
|
|
int width; |
144 |
|
|
int suppress; |
145 |
|
|
int plus; |
146 |
|
|
int done = 0; |
147 |
|
|
static u_char mask[] = {0, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff}; |
148 |
|
|
int value; |
149 |
|
|
u_char *base = databuf; |
150 |
|
|
char letter; |
151 |
|
|
char field_name[80]; |
152 |
|
|
|
153 |
|
|
# define ARG_PUT(ARG) \ |
154 |
|
|
do \ |
155 |
|
|
{ \ |
156 |
|
|
if (!suppress) { \ |
157 |
|
|
if (arg_put) \ |
158 |
|
|
(*arg_put)(puthook, (letter == 't' ? 'b' : letter), \ |
159 |
|
|
(void *)((long)(ARG)), 1, field_name); \ |
160 |
|
|
else \ |
161 |
|
|
*(va_arg(ap, int *)) = (ARG); \ |
162 |
|
|
assigned++; \ |
163 |
|
|
} \ |
164 |
|
|
field_name[0] = 0; \ |
165 |
|
|
suppress = 0; \ |
166 |
|
|
} while (0) |
167 |
|
|
|
168 |
|
|
u_char bits = 0; /* For bit fields */ |
169 |
|
|
int shift = 0; /* Bits already shifted out */ |
170 |
|
|
suppress = 0; |
171 |
|
|
field_name[0] = 0; |
172 |
|
|
|
173 |
|
|
while (!done) { |
174 |
|
|
switch (letter = *fmt) { |
175 |
|
|
case ' ': /* White space */ |
176 |
|
|
case '\t': |
177 |
|
|
case '\r': |
178 |
|
|
case '\n': |
179 |
|
|
case '\f': |
180 |
|
|
fmt++; |
181 |
|
|
break; |
182 |
|
|
|
183 |
|
|
case '#': /* Comment */ |
184 |
|
|
while (*fmt && (*fmt != '\n')) |
185 |
|
|
fmt++; |
186 |
|
|
if (fmt) |
187 |
|
|
fmt++; /* Skip '\n' */ |
188 |
|
|
break; |
189 |
|
|
|
190 |
|
|
case '*': /* Suppress assignment */ |
191 |
|
|
fmt++; |
192 |
|
|
suppress = 1; |
193 |
|
|
break; |
194 |
|
|
|
195 |
|
|
case '{': /* Field Name */ |
196 |
|
|
{ |
197 |
|
|
int i = 0; |
198 |
|
|
fmt++; /* Skip '{' */ |
199 |
|
|
while (*fmt && (*fmt != '}')) { |
200 |
|
|
if (i < sizeof(field_name)-1) |
201 |
|
|
field_name[i++] = *fmt; |
202 |
|
|
|
203 |
|
|
fmt++; |
204 |
|
|
} |
205 |
|
|
if (fmt) |
206 |
|
|
fmt++; /* Skip '}' */ |
207 |
|
|
field_name[i] = 0; |
208 |
|
|
} |
209 |
|
|
break; |
210 |
|
|
|
211 |
|
|
case 't': /* Bit (field) */ |
212 |
|
|
case 'b': /* Bits */ |
213 |
|
|
fmt++; |
214 |
|
|
width = strtol(fmt, &fmt, 10); |
215 |
|
|
if (width > 8) |
216 |
|
|
done = 1; |
217 |
|
|
else { |
218 |
|
|
if (shift <= 0) { |
219 |
|
|
bits = *databuf++; |
220 |
|
|
shift = 8; |
221 |
|
|
} |
222 |
|
|
value = (bits >> (shift - width)) & mask[width]; |
223 |
|
|
|
224 |
|
|
#if 0 |
225 |
|
|
printf("shift %2d bits %02x value %02x width %2d mask %02x\n", |
226 |
|
|
shift, bits, value, width, mask[width]); |
227 |
|
|
#endif |
228 |
|
|
|
229 |
|
|
ARG_PUT(value); |
230 |
|
|
|
231 |
|
|
shift -= width; |
232 |
|
|
} |
233 |
|
|
|
234 |
|
|
break; |
235 |
|
|
|
236 |
|
|
case 'i': /* Integral values */ |
237 |
|
|
shift = 0; |
238 |
|
|
fmt++; |
239 |
|
|
width = strtol(fmt, &fmt, 10); |
240 |
|
|
switch (width) { |
241 |
|
|
case 1: |
242 |
|
|
ARG_PUT(*databuf); |
243 |
|
|
databuf++; |
244 |
|
|
break; |
245 |
|
|
|
246 |
|
|
case 2: |
247 |
|
|
ARG_PUT((*databuf) << 8 | *(databuf + 1)); |
248 |
|
|
databuf += 2; |
249 |
|
|
break; |
250 |
|
|
|
251 |
|
|
case 3: |
252 |
|
|
ARG_PUT( |
253 |
|
|
(*databuf) << 16 | |
254 |
|
|
(*(databuf + 1)) << 8 | |
255 |
|
|
*(databuf + 2)); |
256 |
|
|
databuf += 3; |
257 |
|
|
break; |
258 |
|
|
|
259 |
|
|
case 4: |
260 |
|
|
ARG_PUT( |
261 |
|
|
(*databuf) << 24 | |
262 |
|
|
(*(databuf + 1)) << 16 | |
263 |
|
|
(*(databuf + 2)) << 8 | |
264 |
|
|
*(databuf + 3)); |
265 |
|
|
databuf += 4; |
266 |
|
|
break; |
267 |
|
|
|
268 |
|
|
default: |
269 |
|
|
done = 1; |
270 |
|
|
} |
271 |
|
|
|
272 |
|
|
break; |
273 |
|
|
|
274 |
|
|
case 'c': /* Characters (i.e., not swapped) */ |
275 |
|
|
case 'z': /* Characters with zeroed trailing spaces */ |
276 |
|
|
shift = 0; |
277 |
|
|
fmt++; |
278 |
|
|
width = strtol(fmt, &fmt, 10); |
279 |
|
|
if (!suppress) { |
280 |
|
|
if (arg_put) |
281 |
|
|
(*arg_put)(puthook, (letter == 't' ? 'b' : letter), |
282 |
|
|
databuf, width, field_name); |
283 |
|
|
else { |
284 |
|
|
char *dest; |
285 |
|
|
dest = va_arg(ap, char *); |
286 |
|
|
bcopy(databuf, dest, width); |
287 |
|
|
if (letter == 'z') { |
288 |
|
|
char *p; |
289 |
|
|
for (p = dest + width - 1; |
290 |
|
|
(p >= (char *)dest) && (*p == ' '); p--) |
291 |
|
|
*p = 0; |
292 |
|
|
} |
293 |
|
|
} |
294 |
|
|
assigned++; |
295 |
|
|
} |
296 |
|
|
databuf += width; |
297 |
|
|
field_name[0] = 0; |
298 |
|
|
suppress = 0; |
299 |
|
|
break; |
300 |
|
|
|
301 |
|
|
case 's': /* Seek */ |
302 |
|
|
shift = 0; |
303 |
|
|
fmt++; |
304 |
|
|
if (*fmt == '+') { |
305 |
|
|
plus = 1; |
306 |
|
|
fmt++; |
307 |
|
|
} else |
308 |
|
|
plus = 0; |
309 |
|
|
|
310 |
|
|
if (tolower((unsigned char)*fmt) == 'v') { |
311 |
|
|
/* You can't suppress a seek value. You also |
312 |
|
|
* can't have a variable seek when you are using |
313 |
|
|
* "arg_put". |
314 |
|
|
*/ |
315 |
|
|
width = (arg_put) ? 0 : va_arg(ap, int); |
316 |
|
|
fmt++; |
317 |
|
|
} else |
318 |
|
|
width = strtol(fmt, &fmt, 10); |
319 |
|
|
|
320 |
|
|
if (plus) |
321 |
|
|
databuf += width; /* Relative seek */ |
322 |
|
|
else |
323 |
|
|
databuf = base + width; /* Absolute seek */ |
324 |
|
|
|
325 |
|
|
break; |
326 |
|
|
|
327 |
|
|
case 0: |
328 |
|
|
done = 1; |
329 |
|
|
break; |
330 |
|
|
|
331 |
|
|
default: |
332 |
|
|
fprintf(stderr, "Unknown letter in format: %c\n", letter); |
333 |
|
|
fmt++; |
334 |
|
|
} |
335 |
|
|
} |
336 |
|
|
|
337 |
|
|
return assigned; |
338 |
|
|
} |
339 |
|
|
|
340 |
|
|
int |
341 |
|
|
scsireq_decode_visit(scsireq_t *scsireq, char *fmt, |
342 |
|
|
void (*arg_put)(void *, int , void *, int, char *), void *puthook) |
343 |
|
|
{ |
344 |
|
|
va_list ap; |
345 |
|
|
int ret; |
346 |
|
|
|
347 |
|
|
ret = do_buff_decode(scsireq->databuf, (size_t)scsireq->datalen, |
348 |
|
|
arg_put, puthook, fmt, ap); |
349 |
|
|
va_end (ap); |
350 |
|
|
return (ret); |
351 |
|
|
} |
352 |
|
|
|
353 |
|
|
int |
354 |
|
|
scsireq_buff_decode_visit(u_char *buff, size_t len, char *fmt, |
355 |
|
|
void (*arg_put)(void *, int, void *, int, char *), void *puthook) |
356 |
|
|
{ |
357 |
|
|
va_list ap; |
358 |
|
|
|
359 |
|
|
/* XXX */ |
360 |
|
|
return do_buff_decode(buff, len, arg_put, puthook, fmt, ap); |
361 |
|
|
} |
362 |
|
|
|
363 |
|
|
/* next_field: Return the next field in a command specifier. This |
364 |
|
|
* builds up a SCSI command using this trivial grammar: |
365 |
|
|
* |
366 |
|
|
* fields : field fields |
367 |
|
|
* ; |
368 |
|
|
* |
369 |
|
|
* field : value |
370 |
|
|
* | value ':' field_width |
371 |
|
|
* ; |
372 |
|
|
* |
373 |
|
|
* field_width : digit |
374 |
|
|
* | 'i' digit // i2 = 2 byte integer, i3 = 3 byte integer etc. |
375 |
|
|
* ; |
376 |
|
|
* |
377 |
|
|
* value : HEX_NUMBER |
378 |
|
|
* | 'v' // For indirection. |
379 |
|
|
* ; |
380 |
|
|
* |
381 |
|
|
* Notes: |
382 |
|
|
* Bit fields are specified MSB first to match the SCSI spec. |
383 |
|
|
* |
384 |
|
|
* Examples: |
385 |
|
|
* TUR: "0 0 0 0 0 0" |
386 |
|
|
* WRITE BUFFER: "38 v:3 0:2 0:3 v v:i3 v:i3 0", mode, buffer_id, list_length |
387 |
|
|
* |
388 |
|
|
* The function returns the value: |
389 |
|
|
* 0: For reached end, with error_p set if an error was found |
390 |
|
|
* 1: For valid stuff setup |
391 |
|
|
* 2: For "v" was entered as the value (implies use varargs) |
392 |
|
|
* |
393 |
|
|
*/ |
394 |
|
|
static int |
395 |
|
|
next_field(char **pp, char *fmt, int *width_p, int *value_p, char *name, |
396 |
|
|
int n_name, int *error_p, int *suppress_p) |
397 |
|
|
{ |
398 |
|
|
char *p = *pp; |
399 |
|
|
|
400 |
|
|
int something = 0; |
401 |
|
|
|
402 |
|
|
enum { BETWEEN_FIELDS, START_FIELD, GET_FIELD, DONE } state; |
403 |
|
|
|
404 |
|
|
int value = 0; |
405 |
|
|
int field_size; /* Default to byte field type... */ |
406 |
|
|
int field_width; /* 1 byte wide */ |
407 |
|
|
int is_error = 0; |
408 |
|
|
int suppress = 0; |
409 |
|
|
|
410 |
|
|
field_size = 8; /* Default to byte field type... */ |
411 |
|
|
*fmt = 'i'; |
412 |
|
|
field_width = 1; /* 1 byte wide */ |
413 |
|
|
if (name) |
414 |
|
|
*name = 0; |
415 |
|
|
|
416 |
|
|
state = BETWEEN_FIELDS; |
417 |
|
|
|
418 |
|
|
while (state != DONE) |
419 |
|
|
switch (state) |
420 |
|
|
{ |
421 |
|
|
case BETWEEN_FIELDS: |
422 |
|
|
if (*p == 0) |
423 |
|
|
state = DONE; |
424 |
|
|
else if (isspace((unsigned char)*p)) |
425 |
|
|
p++; |
426 |
|
|
else if (*p == '#') { |
427 |
|
|
while (*p && *p != '\n') |
428 |
|
|
p++; |
429 |
|
|
|
430 |
|
|
if (p) |
431 |
|
|
p++; |
432 |
|
|
} else if (*p == '{') { |
433 |
|
|
int i = 0; |
434 |
|
|
|
435 |
|
|
p++; |
436 |
|
|
|
437 |
|
|
while (*p && *p != '}') { |
438 |
|
|
if (name && i < n_name) { |
439 |
|
|
name[i] = *p; |
440 |
|
|
i++; |
441 |
|
|
} |
442 |
|
|
p++; |
443 |
|
|
} |
444 |
|
|
|
445 |
|
|
if (name && i < n_name) |
446 |
|
|
name[i] = 0; |
447 |
|
|
|
448 |
|
|
if (*p == '}') |
449 |
|
|
p++; |
450 |
|
|
} else if (*p == '*') { |
451 |
|
|
p++; |
452 |
|
|
suppress = 1; |
453 |
|
|
} else if (isxdigit((unsigned char)*p)) { |
454 |
|
|
something = 1; |
455 |
|
|
value = strtol(p, &p, 16); |
456 |
|
|
state = START_FIELD; |
457 |
|
|
} else if (tolower((unsigned char)*p) == 'v') { |
458 |
|
|
p++; |
459 |
|
|
something = 2; |
460 |
|
|
value = *value_p; |
461 |
|
|
state = START_FIELD; |
462 |
|
|
} |
463 |
|
|
/* try to work without the 'v' */ |
464 |
|
|
else if (tolower((unsigned char)*p) == 'i') { |
465 |
|
|
something = 2; |
466 |
|
|
value = *value_p; |
467 |
|
|
p++; |
468 |
|
|
|
469 |
|
|
*fmt = 'i'; |
470 |
|
|
field_size = 8; |
471 |
|
|
field_width = strtol(p, &p, 10); |
472 |
|
|
state = DONE; |
473 |
|
|
} else if (tolower((unsigned char)*p) == 't') { |
474 |
|
|
/* XXX: B can't work: Sees the 'b' |
475 |
|
|
* as a hex digit in "isxdigit". |
476 |
|
|
* try "t" for bit field. |
477 |
|
|
*/ |
478 |
|
|
something = 2; |
479 |
|
|
value = *value_p; |
480 |
|
|
p++; |
481 |
|
|
|
482 |
|
|
*fmt = 'b'; |
483 |
|
|
field_size = 1; |
484 |
|
|
field_width = strtol(p, &p, 10); |
485 |
|
|
state = DONE; |
486 |
|
|
} else if (tolower((unsigned char)*p) == 's') { /* Seek */ |
487 |
|
|
*fmt = 's'; |
488 |
|
|
p++; |
489 |
|
|
if (tolower((unsigned char)*p) == 'v') { |
490 |
|
|
p++; |
491 |
|
|
something = 2; |
492 |
|
|
value = *value_p; |
493 |
|
|
} else { |
494 |
|
|
something = 1; |
495 |
|
|
value = strtol(p, &p, 0); |
496 |
|
|
} |
497 |
|
|
state = DONE; |
498 |
|
|
} else { |
499 |
|
|
fprintf(stderr, "Invalid starting character: %c\n", *p); |
500 |
|
|
is_error = 1; |
501 |
|
|
state = DONE; |
502 |
|
|
} |
503 |
|
|
break; |
504 |
|
|
|
505 |
|
|
case START_FIELD: |
506 |
|
|
if (*p == ':') { |
507 |
|
|
p++; |
508 |
|
|
field_size = 1; /* Default to bits when specified */ |
509 |
|
|
state = GET_FIELD; |
510 |
|
|
} else |
511 |
|
|
state = DONE; |
512 |
|
|
break; |
513 |
|
|
|
514 |
|
|
case GET_FIELD: |
515 |
|
|
if (isdigit((unsigned char)*p)) { |
516 |
|
|
*fmt = 'b'; |
517 |
|
|
field_size = 1; |
518 |
|
|
field_width = strtol(p, &p, 10); |
519 |
|
|
state = DONE; |
520 |
|
|
} else if (*p == 'i') { /* Integral (bytes) */ |
521 |
|
|
p++; |
522 |
|
|
|
523 |
|
|
*fmt = 'i'; |
524 |
|
|
field_size = 8; |
525 |
|
|
field_width = strtol(p, &p, 10); |
526 |
|
|
state = DONE; |
527 |
|
|
} else if (*p == 'b') { /* Bits */ |
528 |
|
|
p++; |
529 |
|
|
|
530 |
|
|
*fmt = 'b'; |
531 |
|
|
field_size = 1; |
532 |
|
|
field_width = strtol(p, &p, 10); |
533 |
|
|
state = DONE; |
534 |
|
|
} else { |
535 |
|
|
fprintf(stderr, "Invalid startfield %c (%02x)\n", |
536 |
|
|
*p, *p); |
537 |
|
|
is_error = 1; |
538 |
|
|
state = DONE; |
539 |
|
|
} |
540 |
|
|
break; |
541 |
|
|
|
542 |
|
|
case DONE: |
543 |
|
|
break; |
544 |
|
|
} |
545 |
|
|
|
546 |
|
|
if (is_error) { |
547 |
|
|
*error_p = 1; |
548 |
|
|
return 0; |
549 |
|
|
} |
550 |
|
|
|
551 |
|
|
*error_p = 0; |
552 |
|
|
*pp = p; |
553 |
|
|
*width_p = field_width * field_size; |
554 |
|
|
*value_p = value; |
555 |
|
|
*suppress_p = suppress; |
556 |
|
|
|
557 |
|
|
return something; |
558 |
|
|
} |
559 |
|
|
|
560 |
|
|
static int |
561 |
|
|
do_encode(u_char *buff, size_t vec_max, size_t *used, |
562 |
|
|
int (*arg_get)(void *, char *), |
563 |
|
|
void *gethook, char *fmt, va_list ap) |
564 |
|
|
{ |
565 |
|
|
int ind; |
566 |
|
|
int shift; |
567 |
|
|
u_char val; |
568 |
|
|
int ret; |
569 |
|
|
int width, value, error, suppress; |
570 |
|
|
char c; |
571 |
|
|
int encoded = 0; |
572 |
|
|
char field_name[80]; |
573 |
|
|
|
574 |
|
|
ind = 0; |
575 |
|
|
shift = 0; |
576 |
|
|
val = 0; |
577 |
|
|
|
578 |
|
|
while ((ret = next_field(&fmt, &c, &width, &value, field_name, |
579 |
|
|
sizeof(field_name), &error, &suppress))) |
580 |
|
|
{ |
581 |
|
|
encoded++; |
582 |
|
|
|
583 |
|
|
if (ret == 2) { |
584 |
|
|
if (suppress) |
585 |
|
|
value = 0; |
586 |
|
|
else |
587 |
|
|
value = arg_get ? (*arg_get)(gethook, field_name) : va_arg(ap, int); |
588 |
|
|
} |
589 |
|
|
|
590 |
|
|
#if 0 |
591 |
|
|
printf( |
592 |
|
|
"do_encode: ret %d fmt %c width %d value %d name \"%s\"" |
593 |
|
|
"error %d suppress %d\n", |
594 |
|
|
ret, c, width, value, field_name, error, suppress); |
595 |
|
|
#endif |
596 |
|
|
|
597 |
|
|
if (c == 's') /* Absolute seek */ |
598 |
|
|
{ |
599 |
|
|
ind = value; |
600 |
|
|
continue; |
601 |
|
|
} |
602 |
|
|
|
603 |
|
|
if (width < 8) /* A width of < 8 is a bit field. */ |
604 |
|
|
{ |
605 |
|
|
|
606 |
|
|
/* This is a bit field. We start with the high bits |
607 |
|
|
* so it reads the same as the SCSI spec. |
608 |
|
|
*/ |
609 |
|
|
|
610 |
|
|
shift += width; |
611 |
|
|
|
612 |
|
|
val |= (value << (8 - shift)); |
613 |
|
|
|
614 |
|
|
if (shift == 8) { |
615 |
|
|
if (ind < vec_max) { |
616 |
|
|
buff[ind++] = val; |
617 |
|
|
val = 0; |
618 |
|
|
} |
619 |
|
|
shift = 0; |
620 |
|
|
} |
621 |
|
|
} else { |
622 |
|
|
if (shift) { |
623 |
|
|
if (ind < vec_max) { |
624 |
|
|
buff[ind++] = val; |
625 |
|
|
val = 0; |
626 |
|
|
} |
627 |
|
|
shift = 0; |
628 |
|
|
} |
629 |
|
|
switch (width) |
630 |
|
|
{ |
631 |
|
|
case 8: /* 1 byte integer */ |
632 |
|
|
if (ind < vec_max) |
633 |
|
|
buff[ind++] = value; |
634 |
|
|
break; |
635 |
|
|
|
636 |
|
|
case 16: /* 2 byte integer */ |
637 |
|
|
if (ind < vec_max - 2 + 1) { |
638 |
|
|
buff[ind++] = value >> 8; |
639 |
|
|
buff[ind++] = value; |
640 |
|
|
} |
641 |
|
|
break; |
642 |
|
|
|
643 |
|
|
case 24: /* 3 byte integer */ |
644 |
|
|
if (ind < vec_max - 3 + 1) { |
645 |
|
|
buff[ind++] = value >> 16; |
646 |
|
|
buff[ind++] = value >> 8; |
647 |
|
|
buff[ind++] = value; |
648 |
|
|
} |
649 |
|
|
break; |
650 |
|
|
|
651 |
|
|
case 32: /* 4 byte integer */ |
652 |
|
|
if (ind < vec_max - 4 + 1) { |
653 |
|
|
buff[ind++] = value >> 24; |
654 |
|
|
buff[ind++] = value >> 16; |
655 |
|
|
buff[ind++] = value >> 8; |
656 |
|
|
buff[ind++] = value; |
657 |
|
|
} |
658 |
|
|
break; |
659 |
|
|
|
660 |
|
|
default: |
661 |
|
|
fprintf(stderr, "do_encode: Illegal width\n"); |
662 |
|
|
break; |
663 |
|
|
} |
664 |
|
|
} |
665 |
|
|
} |
666 |
|
|
|
667 |
|
|
/* Flush out any remaining bits */ |
668 |
|
|
if (shift && ind < vec_max) { |
669 |
|
|
buff[ind++] = val; |
670 |
|
|
val = 0; |
671 |
|
|
} |
672 |
|
|
|
673 |
|
|
|
674 |
|
|
if (used) |
675 |
|
|
*used = ind; |
676 |
|
|
|
677 |
|
|
if (error) |
678 |
|
|
return -1; |
679 |
|
|
|
680 |
|
|
return encoded; |
681 |
|
|
} |
682 |
|
|
|
683 |
|
|
scsireq_t * |
684 |
|
|
scsireq_build(scsireq_t *scsireq, u_long datalen, caddr_t databuf, |
685 |
|
|
u_long flags, char *cmd_spec, ...) |
686 |
|
|
{ |
687 |
|
|
size_t cmdlen; |
688 |
|
|
va_list ap; |
689 |
|
|
|
690 |
|
|
if (scsireq == 0) |
691 |
|
|
return 0; |
692 |
|
|
|
693 |
|
|
scsireq_reset(scsireq); |
694 |
|
|
|
695 |
|
|
if (databuf) { |
696 |
|
|
scsireq->databuf = databuf; |
697 |
|
|
scsireq->datalen = datalen; |
698 |
|
|
scsireq->flags = flags; |
699 |
|
|
} |
700 |
|
|
else if (datalen) { |
701 |
|
|
/* XXX: Good way to get a memory leak. Perhaps this should be |
702 |
|
|
* removed. |
703 |
|
|
*/ |
704 |
|
|
if ( (scsireq->databuf = malloc(datalen)) == NULL) |
705 |
|
|
return 0; |
706 |
|
|
|
707 |
|
|
scsireq->datalen = datalen; |
708 |
|
|
scsireq->flags = flags; |
709 |
|
|
} |
710 |
|
|
|
711 |
|
|
va_start(ap, cmd_spec); |
712 |
|
|
|
713 |
|
|
if (do_encode(scsireq->cmd, CMDBUFLEN, &cmdlen, 0, 0, cmd_spec, ap) == -1) |
714 |
|
|
return 0; |
715 |
|
|
va_end (ap); |
716 |
|
|
|
717 |
|
|
scsireq->cmdlen = cmdlen; |
718 |
|
|
return scsireq; |
719 |
|
|
} |
720 |
|
|
|
721 |
|
|
scsireq_t |
722 |
|
|
*scsireq_build_visit(scsireq_t *scsireq, u_long datalen, caddr_t databuf, |
723 |
|
|
u_long flags, char *cmd_spec, |
724 |
|
|
int (*arg_get)(void *hook, char *field_name), void *gethook) |
725 |
|
|
{ |
726 |
|
|
size_t cmdlen; |
727 |
|
|
va_list ap; |
728 |
|
|
|
729 |
|
|
if (scsireq == 0) |
730 |
|
|
return 0; |
731 |
|
|
|
732 |
|
|
scsireq_reset(scsireq); |
733 |
|
|
|
734 |
|
|
if (databuf) { |
735 |
|
|
scsireq->databuf = databuf; |
736 |
|
|
scsireq->datalen = datalen; |
737 |
|
|
scsireq->flags = flags; |
738 |
|
|
} else if (datalen) { |
739 |
|
|
/* XXX: Good way to get a memory leak. Perhaps this should be |
740 |
|
|
* removed. |
741 |
|
|
*/ |
742 |
|
|
if ( (scsireq->databuf = malloc(datalen)) == NULL) |
743 |
|
|
return 0; |
744 |
|
|
|
745 |
|
|
scsireq->datalen = datalen; |
746 |
|
|
scsireq->flags = flags; |
747 |
|
|
} |
748 |
|
|
|
749 |
|
|
if (do_encode(scsireq->cmd, CMDBUFLEN, &cmdlen, arg_get, gethook, |
750 |
|
|
cmd_spec, ap) == -1) |
751 |
|
|
return 0; |
752 |
|
|
|
753 |
|
|
scsireq->cmdlen = cmdlen; |
754 |
|
|
|
755 |
|
|
return scsireq; |
756 |
|
|
} |
757 |
|
|
|
758 |
|
|
int |
759 |
|
|
scsireq_buff_encode_visit(u_char *buff, size_t len, char *fmt, |
760 |
|
|
int (*arg_get)(void *hook, char *field_name), void *gethook) |
761 |
|
|
{ |
762 |
|
|
va_list ap; |
763 |
|
|
return do_encode(buff, len, 0, arg_get, gethook, fmt, ap); |
764 |
|
|
} |
765 |
|
|
|
766 |
|
|
int |
767 |
|
|
scsireq_encode_visit(scsireq_t *scsireq, char *fmt, |
768 |
|
|
int (*arg_get)(void *hook, char *field_name), void *gethook) |
769 |
|
|
{ |
770 |
|
|
va_list ap; |
771 |
|
|
return do_encode(scsireq->databuf, scsireq->datalen, 0, |
772 |
|
|
arg_get, gethook, fmt, ap); |
773 |
|
|
} |
774 |
|
|
|
775 |
|
|
FILE * |
776 |
|
|
scsi_debug_output(char *s) |
777 |
|
|
{ |
778 |
|
|
if (s == 0) |
779 |
|
|
behave.db_f = 0; |
780 |
|
|
else { |
781 |
|
|
behave.db_f = fopen(s, "w"); |
782 |
|
|
|
783 |
|
|
if (behave.db_f == 0) |
784 |
|
|
behave.db_f = stderr; |
785 |
|
|
} |
786 |
|
|
|
787 |
|
|
return behave.db_f; |
788 |
|
|
} |
789 |
|
|
|
790 |
|
|
#define SCSI_TRUNCATE -1 |
791 |
|
|
|
792 |
|
|
typedef struct scsi_assoc { |
793 |
|
|
int code; |
794 |
|
|
char *text; |
795 |
|
|
} scsi_assoc_t; |
796 |
|
|
|
797 |
|
|
static scsi_assoc_t retsts[] = |
798 |
|
|
{ |
799 |
|
|
{ SCCMD_OK, "No error" }, |
800 |
|
|
{ SCCMD_TIMEOUT, "Command Timeout" }, |
801 |
|
|
{ SCCMD_BUSY, "Busy" }, |
802 |
|
|
{ SCCMD_SENSE, "Sense Returned" }, |
803 |
|
|
{ SCCMD_UNKNOWN, "Unknown return status" }, |
804 |
|
|
|
805 |
|
|
{ 0, 0 } |
806 |
|
|
}; |
807 |
|
|
|
808 |
|
|
static char * |
809 |
|
|
scsi_assoc_text(int code, scsi_assoc_t *tab) |
810 |
|
|
{ |
811 |
|
|
while (tab->text) { |
812 |
|
|
if (tab->code == code) |
813 |
|
|
return tab->text; |
814 |
|
|
|
815 |
|
|
tab++; |
816 |
|
|
} |
817 |
|
|
|
818 |
|
|
return "Unknown code"; |
819 |
|
|
} |
820 |
|
|
|
821 |
|
|
void |
822 |
|
|
scsi_dump(FILE *f, char *text, u_char *p, int req, int got, int dump_print) |
823 |
|
|
{ |
824 |
|
|
int i; |
825 |
|
|
int trunc = 0; |
826 |
|
|
|
827 |
|
|
if (f == 0 || req == 0) |
828 |
|
|
return; |
829 |
|
|
|
830 |
|
|
fprintf(f, "%s (%d of %d):\n", text, got, req); |
831 |
|
|
|
832 |
|
|
if (behave.db_trunc != -1 && got > behave.db_trunc) { |
833 |
|
|
trunc = 1; |
834 |
|
|
got = behave.db_trunc; |
835 |
|
|
} |
836 |
|
|
|
837 |
|
|
for (i = 0; i < got; i++) { |
838 |
|
|
fprintf(f, "%02x", p[i]); |
839 |
|
|
|
840 |
|
|
putc(' ', f); |
841 |
|
|
|
842 |
|
|
if ((i % 16) == 15 || i == got - 1) { |
843 |
|
|
int j; |
844 |
|
|
if (dump_print) { |
845 |
|
|
fprintf(f, " # "); |
846 |
|
|
for (j = i - 15; j <= i; j++) |
847 |
|
|
putc((isprint(p[j]) ? p[j] : '.'), f); |
848 |
|
|
|
849 |
|
|
putc('\n', f); |
850 |
|
|
} else |
851 |
|
|
putc('\n', f); |
852 |
|
|
} |
853 |
|
|
} |
854 |
|
|
|
855 |
|
|
fprintf(f, "%s", (trunc) ? "(truncated)...\n" : "\n"); |
856 |
|
|
} |
857 |
|
|
|
858 |
|
|
/* XXX: sense_7x_dump and scsi_sense dump was just sort of |
859 |
|
|
* grabbed out of the old ds |
860 |
|
|
* library and not really merged in carefully. It should use the |
861 |
|
|
* new buffer decoding stuff. |
862 |
|
|
*/ |
863 |
|
|
|
864 |
|
|
/* Get unsigned long. |
865 |
|
|
*/ |
866 |
|
|
static u_long |
867 |
|
|
g_u_long(u_char *s) |
868 |
|
|
{ |
869 |
|
|
return (s[0] << 24) | (s[1] << 16) | (s[2] << 8) | s[3]; |
870 |
|
|
} |
871 |
|
|
|
872 |
|
|
/* In the old software you could patch in a special error table: |
873 |
|
|
*/ |
874 |
|
|
static scsi_assoc_t *error_table = 0; |
875 |
|
|
|
876 |
|
|
static void |
877 |
|
|
sense_7x_dump(FILE *f, scsireq_t *scsireq) |
878 |
|
|
{ |
879 |
|
|
int code; |
880 |
|
|
u_char *s = (u_char *)scsireq->sense; |
881 |
|
|
int valid = (*s) & 0x80; |
882 |
|
|
u_long val; |
883 |
|
|
|
884 |
|
|
static scsi_assoc_t sense[] = { |
885 |
|
|
{ 0, "No sense" }, |
886 |
|
|
{ 1, "Recovered error" }, |
887 |
|
|
{ 2, "Not Ready" }, |
888 |
|
|
{ 3, "Medium error" }, |
889 |
|
|
{ 4, "Hardware error" }, |
890 |
|
|
{ 5, "Illegal request" }, |
891 |
|
|
{ 6, "Unit attention" }, |
892 |
|
|
{ 7, "Data protect" }, |
893 |
|
|
{ 8, "Blank check" }, |
894 |
|
|
{ 9, "Vendor specific" }, |
895 |
|
|
{ 0xa, "Copy aborted" }, |
896 |
|
|
{ 0xb, "Aborted Command" }, |
897 |
|
|
{ 0xc, "Equal" }, |
898 |
|
|
{ 0xd, "Volume overflow" }, |
899 |
|
|
{ 0xe, "Miscompare" }, |
900 |
|
|
{ 0, 0 }, |
901 |
|
|
}; |
902 |
|
|
|
903 |
|
|
static scsi_assoc_t code_tab[] = { |
904 |
|
|
{0x70, "current errors"}, |
905 |
|
|
{0x71, "deferred errors"}, |
906 |
|
|
}; |
907 |
|
|
|
908 |
|
|
fprintf(f, "Error code is \"%s\"\n", scsi_assoc_text(s[0]&0x7F, code_tab)); |
909 |
|
|
fprintf(f, "Segment number is %02x\n", s[1]); |
910 |
|
|
|
911 |
|
|
if (s[2] & 0x20) |
912 |
|
|
fprintf(f, "Incorrect Length Indicator is set.\n"); |
913 |
|
|
|
914 |
|
|
fprintf(f, "Sense key is \"%s\"\n", scsi_assoc_text(s[2] & 0x7, sense)); |
915 |
|
|
|
916 |
|
|
val = g_u_long(s + 3); |
917 |
|
|
fprintf(f, "The Information field is%s %08lx (%ld).\n", |
918 |
|
|
valid ? "" : " not valid but contains", (long)val, (long)val); |
919 |
|
|
|
920 |
|
|
val = g_u_long(s + 8); |
921 |
|
|
fprintf(f, "The Command Specific Information field is %08lx (%ld).\n", |
922 |
|
|
(long)val, (long)val); |
923 |
|
|
|
924 |
|
|
fprintf(f, "Additional sense code: %02x\n", s[12]); |
925 |
|
|
fprintf(f, "Additional sense code qualifier: %02x\n", s[13]); |
926 |
|
|
|
927 |
|
|
code = (s[12] << 8) | s[13]; |
928 |
|
|
|
929 |
|
|
if (error_table) |
930 |
|
|
fprintf(f, "%s\n", scsi_assoc_text(code, error_table)); |
931 |
|
|
|
932 |
|
|
if (s[15] & 0x80) { |
933 |
|
|
if ((s[2] & 0x7) == 0x05) /* Illegal request */ |
934 |
|
|
{ |
935 |
|
|
int byte; |
936 |
|
|
u_char value, bit; |
937 |
|
|
int bad_par = ((s[15] & 0x40) == 0); |
938 |
|
|
fprintf(f, "Illegal value in the %s.\n", |
939 |
|
|
(bad_par ? "parameter list" : "command descriptor block")); |
940 |
|
|
byte = ((s[16] << 8) | s[17]); |
941 |
|
|
value = bad_par ? (u_char)scsireq->databuf[byte] : (u_char)scsireq->cmd[byte]; |
942 |
|
|
bit = s[15] & 0x7; |
943 |
|
|
if (s[15] & 0x08) |
944 |
|
|
fprintf(f, "Bit %d of byte %d (value %02x) is illegal.\n", |
945 |
|
|
bit, byte, value); |
946 |
|
|
else |
947 |
|
|
fprintf(f, "Byte %d (value %02x) is illegal.\n", byte, value); |
948 |
|
|
} else { |
949 |
|
|
fprintf(f, "Sense Key Specific (valid but not illegal request):\n"); |
950 |
|
|
fprintf(f, "%02x %02x %02x\n", s[15] & 0x7f, s[16], s[17]); |
951 |
|
|
} |
952 |
|
|
} |
953 |
|
|
} |
954 |
|
|
|
955 |
|
|
/* scsi_sense_dump: Dump the sense portion of the scsireq structure. |
956 |
|
|
*/ |
957 |
|
|
static void |
958 |
|
|
scsi_sense_dump(FILE *f, scsireq_t *scsireq) |
959 |
|
|
{ |
960 |
|
|
u_char *s = (u_char *)scsireq->sense; |
961 |
|
|
int code = (*s) & 0x7f; |
962 |
|
|
|
963 |
|
|
if (scsireq->senselen_used == 0) { |
964 |
|
|
fprintf(f, "No sense sent.\n"); |
965 |
|
|
return; |
966 |
|
|
} |
967 |
|
|
|
968 |
|
|
#if 0 |
969 |
|
|
if (!valid) |
970 |
|
|
fprintf(f, "The sense data is not valid.\n"); |
971 |
|
|
#endif |
972 |
|
|
|
973 |
|
|
switch (code) { |
974 |
|
|
case 0x70: |
975 |
|
|
case 0x71: |
976 |
|
|
sense_7x_dump(f, scsireq); |
977 |
|
|
break; |
978 |
|
|
|
979 |
|
|
default: |
980 |
|
|
fprintf(f, "No sense dump for error code %02x.\n", code); |
981 |
|
|
} |
982 |
|
|
scsi_dump(f, "sense", s, scsireq->senselen, scsireq->senselen_used, 0); |
983 |
|
|
} |
984 |
|
|
|
985 |
|
|
static void |
986 |
|
|
scsi_retsts_dump(FILE *f, scsireq_t *scsireq) |
987 |
|
|
{ |
988 |
|
|
if (scsireq->retsts == 0) |
989 |
|
|
return; |
990 |
|
|
|
991 |
|
|
fprintf(f, "return status %d (%s)", |
992 |
|
|
scsireq->retsts, scsi_assoc_text(scsireq->retsts, retsts)); |
993 |
|
|
|
994 |
|
|
switch (scsireq->retsts) { |
995 |
|
|
case SCCMD_TIMEOUT: |
996 |
|
|
fprintf(f, " after %ld ms", scsireq->timeout); |
997 |
|
|
break; |
998 |
|
|
|
999 |
|
|
default: |
1000 |
|
|
break; |
1001 |
|
|
} |
1002 |
|
|
} |
1003 |
|
|
|
1004 |
|
|
int |
1005 |
|
|
scsi_debug(FILE *f, int ret, scsireq_t *scsireq) |
1006 |
|
|
{ |
1007 |
|
|
char *d; |
1008 |
|
|
if (f == 0) |
1009 |
|
|
return 0; |
1010 |
|
|
|
1011 |
|
|
fprintf(f, "SCIOCCOMMAND ioctl"); |
1012 |
|
|
|
1013 |
|
|
if (ret == 0) |
1014 |
|
|
fprintf(f, ": Command accepted."); |
1015 |
|
|
else { |
1016 |
|
|
if (ret != -1) |
1017 |
|
|
fprintf(f, ", return value %d?", ret); |
1018 |
|
|
|
1019 |
|
|
if (errno) { |
1020 |
|
|
fprintf(f, ": %s", strerror(errno)); |
1021 |
|
|
errno = 0; |
1022 |
|
|
} |
1023 |
|
|
} |
1024 |
|
|
|
1025 |
|
|
fputc('\n', f); |
1026 |
|
|
|
1027 |
|
|
if (ret == 0 && (scsireq->status || scsireq->retsts || behave.db_level)) |
1028 |
|
|
{ |
1029 |
|
|
scsi_retsts_dump(f, scsireq); |
1030 |
|
|
|
1031 |
|
|
if (scsireq->status) |
1032 |
|
|
fprintf(f, " host adapter status %d\n", scsireq->status); |
1033 |
|
|
|
1034 |
|
|
if (scsireq->flags & SCCMD_READ) |
1035 |
|
|
d = "Data in"; |
1036 |
|
|
else if (scsireq->flags & SCCMD_WRITE) |
1037 |
|
|
d = "Data out"; |
1038 |
|
|
else |
1039 |
|
|
d = "No data transfer?"; |
1040 |
|
|
|
1041 |
|
|
if (scsireq->cmdlen == 0) |
1042 |
|
|
fprintf(f, "Zero length command????\n"); |
1043 |
|
|
|
1044 |
|
|
scsi_dump(f, "Command out", |
1045 |
|
|
(u_char *)scsireq->cmd, scsireq->cmdlen, scsireq->cmdlen, 0); |
1046 |
|
|
scsi_dump(f, d, |
1047 |
|
|
(u_char *)scsireq->databuf, scsireq->datalen, |
1048 |
|
|
scsireq->datalen_used, 1); |
1049 |
|
|
scsi_sense_dump(f, scsireq); |
1050 |
|
|
} |
1051 |
|
|
|
1052 |
|
|
fflush(f); |
1053 |
|
|
|
1054 |
|
|
return ret; |
1055 |
|
|
} |
1056 |
|
|
|
1057 |
|
|
static char *debug_output; |
1058 |
|
|
|
1059 |
|
|
int |
1060 |
|
|
scsi_open(const char *path, int flags) |
1061 |
|
|
{ |
1062 |
|
|
int fd = open(path, flags); |
1063 |
|
|
|
1064 |
|
|
if (fd != -1) { |
1065 |
|
|
char *p; |
1066 |
|
|
debug_output = getenv("SU_DEBUG_OUTPUT"); |
1067 |
|
|
(void)scsi_debug_output(debug_output); |
1068 |
|
|
|
1069 |
|
|
if ((p = getenv("SU_DEBUG_LEVEL"))) |
1070 |
|
|
sscanf(p, "%d", &behave.db_level); |
1071 |
|
|
|
1072 |
|
|
if ((p = getenv("SU_DEBUG_TRUNCATE"))) |
1073 |
|
|
sscanf(p, "%d", &behave.db_trunc); |
1074 |
|
|
else |
1075 |
|
|
behave.db_trunc = SCSI_TRUNCATE; |
1076 |
|
|
} |
1077 |
|
|
|
1078 |
|
|
return fd; |
1079 |
|
|
} |
1080 |
|
|
|
1081 |
|
|
int |
1082 |
|
|
scsireq_enter(int fid, scsireq_t *scsireq) |
1083 |
|
|
{ |
1084 |
|
|
int ret; |
1085 |
|
|
|
1086 |
|
|
ret = ioctl(fid, SCIOCCOMMAND, (void *)scsireq); |
1087 |
|
|
|
1088 |
|
|
if (behave.db_f) |
1089 |
|
|
scsi_debug(behave.db_f, ret, scsireq); |
1090 |
|
|
|
1091 |
|
|
return ret; |
1092 |
|
|
} |