1 /* @(#)scsitransp.c 1.100 16/01/21 Copyright 1988,1995,2000-2016 J. Schilling */
2 /*#ifndef lint*/
3 static char sccsid[] =
4 "@(#)scsitransp.c 1.100 16/01/21 Copyright 1988,1995,2000-2016 J. Schilling";
5 /*#endif*/
6 /*
7 * SCSI user level command transport routines (generic part).
8 *
9 * Warning: you may change this source, but if you do that
10 * you need to change the _scg_version and _scg_auth* string below.
11 * You may not return "schily" for an SCG_AUTHOR request anymore.
12 * Choose your name instead of "schily" and make clear that the version
13 * string is related to a modified source.
14 *
15 * Copyright (c) 1988,1995,2000-2016 J. Schilling
16 */
17 /*
18 * The contents of this file are subject to the terms of the
19 * Common Development and Distribution License, Version 1.0 only
20 * (the "License"). You may not use this file except in compliance
21 * with the License.
22 *
23 * See the file CDDL.Schily.txt in this distribution for details.
24 * A copy of the CDDL is also available via the Internet at
25 * http://www.opensource.org/licenses/cddl1.txt
26 *
27 * The following exceptions apply:
28 * CDDL �3.6 needs to be replaced by: "You may create a Larger Work by
29 * combining Covered Software with other code if all other code is governed by
30 * the terms of a license that is OSI approved (see www.opensource.org) and
31 * you may distribute the Larger Work as a single product. In such a case,
32 * You must make sure the requirements of this License are fulfilled for
33 * the Covered Software."
34 *
35 * When distributing Covered Code, include this CDDL HEADER in each
36 * file and include the License file CDDL.Schily.txt from this distribution.
37 */
38
39 #include <schily/mconfig.h>
40 #include <schily/stdio.h>
41 #include <schily/standard.h>
42 #include <schily/stdlib.h>
43 #include <schily/unistd.h>
44 #include <schily/errno.h>
45 #include <schily/time.h>
46 #include <schily/string.h>
47 #include <schily/schily.h>
48
49 #include <scg/scgcmd.h>
50 #include <scg/scsireg.h>
51 #include <scg/scsitransp.h>
52 #include "scgtimes.h"
53
54 /*
55 * Warning: you may change this source, but if you do that
56 * you need to change the _scg_version and _scg_auth* string below.
57 * You may not return "schily" for an SCG_AUTHOR request anymore.
58 * Choose your name instead of "schily" and make clear that the version
59 * string is related to a modified source.
60 */
61 LOCAL char _scg_version[] = "0.9"; /* The global libscg version */
62 LOCAL char _scg_auth_schily[] = "schily"; /* The author for this module */
63
64 #define DEFTIMEOUT 20 /* Default timeout for SCSI command transport */
65
66 EXPORT char *scg_version __PR((SCSI *scgp, int what));
67 EXPORT int scg__open __PR((SCSI *scgp, char *device));
68 EXPORT int scg__close __PR((SCSI *scgp));
69 EXPORT int scg_numbus __PR((SCSI *scgp));
70 EXPORT BOOL scg_havebus __PR((SCSI *scgp, int));
71 EXPORT int scg_initiator_id __PR((SCSI *scgp));
72 EXPORT int scg_isatapi __PR((SCSI *scgp));
73 EXPORT int scg_reset __PR((SCSI *scgp, int what));
74 EXPORT void *scg_getbuf __PR((SCSI *scgp, long));
75 EXPORT void scg_freebuf __PR((SCSI *scgp));
76 EXPORT long scg_bufsize __PR((SCSI *scgp, long));
77 EXPORT void scg_setnonstderrs __PR((SCSI *scgp, const char **));
78 EXPORT BOOL scg_yes __PR((char *));
79 #ifdef nonono
80 LOCAL void scg_sighandler __PR((int));
81 #endif
82 EXPORT int scg_cmd __PR((SCSI *scgp));
83 EXPORT void scg_vhead __PR((SCSI *scgp));
84 EXPORT int scg_svhead __PR((SCSI *scgp, char *buf, int maxcnt));
85 EXPORT int scg_vtail __PR((SCSI *scgp));
86 EXPORT int scg_svtail __PR((SCSI *scgp, int *retp, char *buf, int maxcnt));
87 EXPORT void scg_vsetup __PR((SCSI *scgp));
88 EXPORT int scg_getresid __PR((SCSI *scgp));
89 EXPORT int scg_getdmacnt __PR((SCSI *scgp));
90 EXPORT BOOL scg_cmd_err __PR((SCSI *scgp));
91 EXPORT void scg_printerr __PR((SCSI *scgp));
92 EXPORT void scg_fprinterr __PR((SCSI *scgp, FILE *f));
93 EXPORT int scg_sprinterr __PR((SCSI *scgp, char *buf, int maxcnt));
94 EXPORT int scg__sprinterr __PR((SCSI *scgp, char *buf, int maxcnt));
95 EXPORT void scg_printcdb __PR((SCSI *scgp));
96 EXPORT int scg_sprintcdb __PR((SCSI *scgp, char *buf, int maxcnt));
97 EXPORT void scg_printwdata __PR((SCSI *scgp));
98 EXPORT int scg_sprintwdata __PR((SCSI *scgp, char *buf, int maxcnt));
99 EXPORT void scg_printrdata __PR((SCSI *scgp));
100 EXPORT int scg_sprintrdata __PR((SCSI *scgp, char *buf, int maxcnt));
101 EXPORT void scg_printresult __PR((SCSI *scgp));
102 EXPORT int scg_sprintresult __PR((SCSI *scgp, char *buf, int maxcnt));
103 EXPORT void scg_printstatus __PR((SCSI *scgp));
104 EXPORT int scg_sprintstatus __PR((SCSI *scgp, char *buf, int maxcnt));
105 EXPORT void scg_fprbytes __PR((FILE *, char *, unsigned char *, int));
106 EXPORT void scg_fprascii __PR((FILE *, char *, unsigned char *, int));
107 EXPORT void scg_prbytes __PR((char *, unsigned char *, int));
108 EXPORT void scg_prascii __PR((char *, unsigned char *, int));
109 EXPORT int scg_sprbytes __PR((char *buf, int maxcnt, char *, unsigned char *, int));
110 EXPORT int scg_sprascii __PR((char *buf, int maxcnt, char *, unsigned char *, int));
111 EXPORT void scg_fprsense __PR((FILE *f, unsigned char *, int));
112 EXPORT int scg_sprsense __PR((char *buf, int maxcnt, unsigned char *, int));
113 EXPORT void scg_prsense __PR((unsigned char *, int));
114 EXPORT int scg_cmd_status __PR((SCSI *scgp));
115 EXPORT int scg_sense_key __PR((SCSI *scgp));
116 EXPORT int scg_sense_code __PR((SCSI *scgp));
117 EXPORT int scg_sense_qual __PR((SCSI *scgp));
118 EXPORT void scg_fprintdev __PR((FILE *, struct scsi_inquiry *));
119 EXPORT void scg_printdev __PR((struct scsi_inquiry *));
120 EXPORT int scg_printf __PR((SCSI *scgp, const char *form, ...));
121 EXPORT int scg_errflush __PR((SCSI *scgp));
122 EXPORT int scg_errfflush __PR((SCSI *scgp, FILE *f));
123
124 /*
125 * Return version information for the SCSI transport code.
126 * This has been introduced to make it easier to trace down problems
127 * in applications.
128 *
129 * If scgp is NULL, return general library version information.
130 * If scgp is != NULL, return version information for the low level transport.
131 */
132 EXPORT char *
scg_version(scgp,what)133 scg_version(scgp, what)
134 SCSI *scgp;
135 int what;
136 {
137 if (scgp == (SCSI *)0) {
138 switch (what) {
139
140 case SCG_VERSION:
141 return (_scg_version);
142 /*
143 * If you changed this source, you are not allowed to
144 * return "schily" for the SCG_AUTHOR request.
145 */
146 case SCG_AUTHOR:
147 return (_scg_auth_schily);
148 case SCG_SCCS_ID:
149 return (sccsid);
150 default:
151 return ((char *)0);
152 }
153 }
154 return (SCGO_VERSION(scgp, what));
155 }
156
157 /*
158 * Call low level SCSI open routine from transport abstraction layer.
159 */
160 EXPORT int
scg__open(scgp,device)161 scg__open(scgp, device)
162 SCSI *scgp;
163 char *device;
164 {
165 int ret;
166 scg_ops_t *ops;
167 extern scg_ops_t scg_std_ops;
168
169 /*
170 * Begin restricted code for quality assurance.
171 *
172 * Warning: you are not allowed to modify the quality ensurance code below.
173 *
174 * This restiction is introduced because this way, I hope that people
175 * contribute to the project instead of creating branches.
176 */
177 #if !defined(IS_SCHILY_XCONFIG)
178 printf("\nWarning: This version of libscg has not been configured via the standard\n");
179 printf("autoconfiguration method of the Schily makefile system. There is a high risk\n");
180 printf("that the code is not configured correctly and for this reason will not behave\n");
181 printf("as expected.\n");
182 #endif
183 /*
184 * End restricted code for quality assurance.
185 */
186
187 scgp->ops = &scg_std_ops;
188
189 if (device && strncmp(device, "REMOTE", 6) == 0) {
190 ops = scg_remote();
191 if (ops != NULL)
192 scgp->ops = ops;
193 }
194
195 ret = SCGO_OPEN(scgp, device);
196 if (ret < 0)
197 return (ret);
198
199 /*
200 * Now make scgp->fd valid if possible.
201 * Note that scg_scsibus(scgp)/scg_target(scgp)/scg_lun(scgp) may have
202 * changed in SCGO_OPEN().
203 */
204 scg_settarget(scgp, scg_scsibus(scgp), scg_target(scgp), scg_lun(scgp));
205 return (ret);
206 }
207
208 /*
209 * Call low level SCSI close routine from transport abstraction layer.
210 */
211 EXPORT int
scg__close(scgp)212 scg__close(scgp)
213 SCSI *scgp;
214 {
215 return (SCGO_CLOSE(scgp));
216 }
217
218 /*
219 * Retrieve max DMA count for this target.
220 */
221 EXPORT long
scg_bufsize(scgp,amt)222 scg_bufsize(scgp, amt)
223 SCSI *scgp;
224 long amt;
225 {
226 long maxdma;
227
228 maxdma = SCGO_MAXDMA(scgp, amt);
229 if (amt <= 0 || amt > maxdma)
230 amt = maxdma;
231
232 scgp->maxdma = maxdma; /* Max possible */
233 scgp->maxbuf = amt; /* Current value */
234
235 return (amt);
236 }
237
238 /*
239 * Allocate a buffer that may be used for DMA.
240 */
241 EXPORT void *
scg_getbuf(scgp,amt)242 scg_getbuf(scgp, amt)
243 SCSI *scgp;
244 long amt;
245 {
246 void *buf;
247
248 if (amt <= 0 || amt > scg_bufsize(scgp, amt))
249 return ((void *)0);
250
251 buf = SCGO_GETBUF(scgp, amt);
252 scgp->bufptr = buf;
253 return (buf);
254 }
255
256 /*
257 * Free DMA buffer.
258 */
259 EXPORT void
scg_freebuf(scgp)260 scg_freebuf(scgp)
261 SCSI *scgp;
262 {
263 SCGO_FREEBUF(scgp);
264 scgp->bufptr = NULL;
265 }
266
267 /*
268 * Return the max. number of SCSI busses.
269 */
270 EXPORT BOOL
scg_numbus(scgp)271 scg_numbus(scgp)
272 SCSI *scgp;
273 {
274 return (SCGO_NUMBUS(scgp));
275 }
276
277 /*
278 * Check if 'busno' is a valid SCSI bus number.
279 */
280 EXPORT BOOL
scg_havebus(scgp,busno)281 scg_havebus(scgp, busno)
282 SCSI *scgp;
283 int busno;
284 {
285 return (SCGO_HAVEBUS(scgp, busno));
286 }
287
288 /*
289 * Return SCSI initiator ID for current SCSI bus if available.
290 */
291 EXPORT int
scg_initiator_id(scgp)292 scg_initiator_id(scgp)
293 SCSI *scgp;
294 {
295 return (SCGO_INITIATOR_ID(scgp));
296 }
297
298 /*
299 * Return a hint whether current SCSI target refers to a ATAPI device.
300 */
301 EXPORT int
scg_isatapi(scgp)302 scg_isatapi(scgp)
303 SCSI *scgp;
304 {
305 return (SCGO_ISATAPI(scgp));
306 }
307
308 /*
309 * Reset SCSI bus or target.
310 */
311 EXPORT int
scg_reset(scgp,what)312 scg_reset(scgp, what)
313 SCSI *scgp;
314 int what;
315 {
316 return (SCGO_RESET(scgp, what));
317 }
318
319 /*
320 * Set up nonstd error vector for curren target.
321 * To clear additional error table, call scg_setnonstderrs(scgp, NULL);
322 * Note: do not use this when scanning the SCSI bus.
323 */
324 EXPORT void
scg_setnonstderrs(scgp,vec)325 scg_setnonstderrs(scgp, vec)
326 SCSI *scgp;
327 const char **vec;
328 {
329 scgp->nonstderrs = vec;
330 }
331
332 /*
333 * Simple Yes/No answer checker.
334 */
335 EXPORT BOOL
scg_yes(msg)336 scg_yes(msg)
337 char *msg;
338 {
339 char okbuf[10];
340
341 js_printf("%s", msg);
342 flush();
343 if (getline(okbuf, sizeof (okbuf)) == EOF)
344 exit(EX_BAD);
345 if (streql(okbuf, "y") || streql(okbuf, "yes") ||
346 streql(okbuf, "Y") || streql(okbuf, "YES"))
347 return (TRUE);
348 else
349 return (FALSE);
350 }
351
352 #ifdef nonono
353 LOCAL void
scg_sighandler(sig)354 scg_sighandler(sig)
355 int sig;
356 {
357 js_printf("\n");
358 if (scsi_running) {
359 js_printf("Running command: %s\n", scsi_command);
360 js_printf("Resetting SCSI - Bus.\n");
361 if (scg_reset(scgp) < 0)
362 errmsg("Cannot reset SCSI - Bus.\n");
363 }
364 if (scg_yes("EXIT ? "))
365 exit(sig);
366 }
367 #endif
368
369 /*
370 * Send a SCSI command.
371 * Do error checking and reporting depending on the values of
372 * scgp->verbose, scgp->debug and scgp->silent.
373 */
374 EXPORT int
scg_cmd(scgp)375 scg_cmd(scgp)
376 SCSI *scgp;
377 {
378 int ret;
379 register struct scg_cmd *scmd = scgp->scmd;
380
381 /*
382 * Reset old error messages in scgp->errstr
383 */
384 scgp->errptr = scgp->errbeg = scgp->errstr;
385
386 scmd->kdebug = scgp->kdebug;
387 if (scmd->timeout == 0 || scmd->timeout < scgp->deftimeout)
388 scmd->timeout = scgp->deftimeout;
389 if (scgp->disre_disable)
390 scmd->flags &= ~SCG_DISRE_ENA;
391 if (scgp->noparity)
392 scmd->flags |= SCG_NOPARITY;
393
394 scmd->u_sense.cmd_sense[0] = 0; /* Paranioa */
395 if (scmd->sense_len > SCG_MAX_SENSE)
396 scmd->sense_len = SCG_MAX_SENSE;
397 else if (scmd->sense_len < 0)
398 scmd->sense_len = 0;
399
400 if (scgp->verbose) {
401 scg_vhead(scgp);
402 scg_errflush(scgp);
403 }
404
405 if (scgp->running) {
406 if (scgp->curcmdname) {
407 error("Currently running '%s' command.\n",
408 scgp->curcmdname);
409 }
410 raisecond("SCSI ALREADY RUNNING !!", 0L);
411 }
412 scgp->cb_fun = NULL;
413 gettimeofday(scgp->cmdstart, (struct timezone *)0);
414 scgp->curcmdname = scgp->cmdname;
415 scgp->running = TRUE;
416 ret = SCGO_SEND(scgp);
417 scgp->running = FALSE;
418 __scg_times(scgp);
419 if (scgp->flags & SCGF_IGN_RESID)
420 scmd->resid = 0;
421 if (ret < 0) {
422 if (scmd->ux_errno == 0)
423 scmd->ux_errno = geterrno();
424 if (scmd->error == SCG_NO_ERROR)
425 scmd->error = SCG_FATAL;
426 if (scgp->debug > 0) {
427 errmsg("ret < 0 errno: %d ux_errno: %d error: %d\n",
428 geterrno(), scmd->ux_errno, scmd->error);
429 }
430 if (scmd->ux_errno == EPERM && scgp->flags & SCGF_PERM_PRINT) {
431 char errbuf[SCSI_ERRSTR_SIZE];
432 int amt;
433
434 amt = scg__sprinterr(scgp, errbuf, sizeof (errbuf));
435 if (amt > 0) {
436 FILE *f = scgp->errfile;
437
438 if (f == NULL)
439 f = stderr;
440 filewrite(f, errbuf, amt);
441 ferrmsgno(f, scmd->ux_errno,
442 "Cannot send SCSI cmd via ioctl.\n");
443 fflush(f);
444 }
445 }
446 /*
447 * Old /dev/scg versions will not allow to access targets > 7.
448 * Include a workaround to make this non fatal.
449 */
450 if (scg_target(scgp) < 8 || scmd->ux_errno != EINVAL) {
451
452 if (scmd->ux_errno != EPERM ||
453 (scgp->flags & SCGF_PERM_PRINT) == 0) {
454 errmsgno(scmd->ux_errno,
455 "Cannot send SCSI cmd via ioctl.\n");
456 }
457 if (scgp->flags & SCGF_PERM_EXIT)
458 comexit(scmd->ux_errno);
459 }
460 }
461
462 ret = scg_vtail(scgp);
463 scg_errflush(scgp);
464 if (scgp->cb_fun != NULL)
465 (*scgp->cb_fun)(scgp->cb_arg);
466 return (ret);
467 }
468
469 /*
470 * Fill the head of verbose printing into the SCSI error buffer.
471 * Action depends on SCSI verbose status.
472 */
473 EXPORT void
scg_vhead(scgp)474 scg_vhead(scgp)
475 SCSI *scgp;
476 {
477 scgp->errptr += scg_svhead(scgp, scgp->errptr, scg_errrsize(scgp));
478 }
479
480 /*
481 * Fill the head of verbose printing into a buffer.
482 * Action depends on SCSI verbose status.
483 */
484 EXPORT int
scg_svhead(scgp,buf,maxcnt)485 scg_svhead(scgp, buf, maxcnt)
486 SCSI *scgp;
487 char *buf;
488 int maxcnt;
489 {
490 register char *p = buf;
491 register int amt;
492
493 if (scgp->verbose <= 0)
494 return (0);
495
496 amt = js_snprintf(p, maxcnt,
497 "\nExecuting '%s' command on Bus %d Target %d, Lun %d timeout %ds\n",
498 /* XXX Really this ??? */
499 /* scgp->cmdname, scg_scsibus(scgp), scg_target(scgp), scgp->scmd->cdb.g0_cdb.lun,*/
500 scgp->cmdname, scg_scsibus(scgp), scg_target(scgp), scg_lun(scgp),
501 scgp->scmd->timeout);
502 if (amt < 0)
503 return (amt);
504 p += amt;
505 maxcnt -= amt;
506
507 amt = scg_sprintcdb(scgp, p, maxcnt);
508 if (amt < 0)
509 return (amt);
510 p += amt;
511 maxcnt -= amt;
512
513 if (scgp->verbose > 1) {
514 amt = scg_sprintwdata(scgp, p, maxcnt);
515 if (amt < 0)
516 return (amt);
517 p += amt;
518 maxcnt -= amt;
519 }
520 return (p - buf);
521 }
522
523 /*
524 * Fill the tail of verbose printing into the SCSI error buffer.
525 * Action depends on SCSI verbose status.
526 */
527 EXPORT int
scg_vtail(scgp)528 scg_vtail(scgp)
529 SCSI *scgp;
530 {
531 int ret;
532
533 scgp->errptr += scg_svtail(scgp, &ret, scgp->errptr, scg_errrsize(scgp));
534 return (ret);
535 }
536
537 /*
538 * Fill the tail of verbose printing into a buffer.
539 * Action depends on SCSI verbose status.
540 */
541 EXPORT int
scg_svtail(scgp,retp,buf,maxcnt)542 scg_svtail(scgp, retp, buf, maxcnt)
543 SCSI *scgp;
544 int *retp;
545 char *buf;
546 int maxcnt;
547 {
548 register char *p = buf;
549 register int amt;
550 int ret;
551
552 ret = scg_cmd_err(scgp) ? -1 : 0;
553 if (retp)
554 *retp = ret;
555 if (ret) {
556 if (scgp->silent <= 0 || scgp->verbose) {
557 amt = scg__sprinterr(scgp, p, maxcnt);
558 if (amt < 0)
559 return (amt);
560 p += amt;
561 maxcnt -= amt;
562 }
563 }
564 if ((scgp->silent <= 0 || scgp->verbose) && scgp->scmd->resid) {
565 if (scgp->scmd->resid < 0) {
566 /*
567 * An operating system that does DMA the right way
568 * will not allow DMA overruns - it will stop DMA
569 * before bad things happen.
570 * A DMA residual count < 0 (-1) is a hint for a DMA
571 * overrun but does not affect the transfer count.
572 */
573 amt = js_snprintf(p, maxcnt, "DMA overrun, ");
574 if (amt < 0)
575 return (amt);
576 p += amt;
577 maxcnt -= amt;
578 }
579 amt = js_snprintf(p, maxcnt, "resid: %d\n", scgp->scmd->resid);
580 if (amt < 0)
581 return (amt);
582 p += amt;
583 maxcnt -= amt;
584 }
585 if (scgp->verbose > 0 || (ret < 0 && scgp->silent <= 0)) {
586 amt = scg_sprintresult(scgp, p, maxcnt);
587 if (amt < 0)
588 return (amt);
589 p += amt;
590 maxcnt -= amt;
591 }
592 return (p - buf);
593 }
594
595 /*
596 * Set up SCSI error buffer with verbose print data.
597 * Action depends on SCSI verbose status.
598 */
599 EXPORT void
scg_vsetup(scgp)600 scg_vsetup(scgp)
601 SCSI *scgp;
602 {
603 scg_vhead(scgp);
604 scg_vtail(scgp);
605 }
606
607 /*
608 * Return the residual DMA count for last command.
609 * If this count is < 0, then a DMA overrun occured.
610 */
611 EXPORT int
scg_getresid(scgp)612 scg_getresid(scgp)
613 SCSI *scgp;
614 {
615 return (scgp->scmd->resid);
616 }
617
618 /*
619 * Return the actual DMA count for last command.
620 */
621 EXPORT int
scg_getdmacnt(scgp)622 scg_getdmacnt(scgp)
623 SCSI *scgp;
624 {
625 register struct scg_cmd *scmd = scgp->scmd;
626
627 if (scmd->resid < 0)
628 return (scmd->size);
629
630 return (scmd->size - scmd->resid);
631 }
632
633 /*
634 * Test if last SCSI command got an error.
635 */
636 EXPORT BOOL
scg_cmd_err(scgp)637 scg_cmd_err(scgp)
638 SCSI *scgp;
639 {
640 register struct scg_cmd *cp = scgp->scmd;
641
642 if (cp->error != SCG_NO_ERROR ||
643 cp->ux_errno != 0 ||
644 *(Uchar *)&cp->scb != 0 ||
645 cp->u_sense.cmd_sense[0] != 0) /* Paranioa */
646 return (TRUE);
647 return (FALSE);
648 }
649
650 /*
651 * Used to print error messges if the command itself has been run silently.
652 *
653 * print the following SCSI codes:
654 *
655 * - command transport status
656 * - CDB
657 * - SCSI status byte
658 * - Sense Bytes
659 * - Decoded Sense data
660 * - DMA status
661 * - SCSI timing
662 *
663 * to SCSI errfile.
664 */
665 EXPORT void
scg_printerr(scgp)666 scg_printerr(scgp)
667 SCSI *scgp;
668 {
669 scg_fprinterr(scgp, (FILE *)scgp->errfile);
670 }
671
672 /*
673 * print the following SCSI codes:
674 *
675 * - command transport status
676 * - CDB
677 * - SCSI status byte
678 * - Sense Bytes
679 * - Decoded Sense data
680 * - DMA status
681 * - SCSI timing
682 *
683 * to a file.
684 */
685 EXPORT void
scg_fprinterr(scgp,f)686 scg_fprinterr(scgp, f)
687 SCSI *scgp;
688 FILE *f;
689 {
690 char errbuf[SCSI_ERRSTR_SIZE];
691 int amt;
692
693 amt = scg_sprinterr(scgp, errbuf, sizeof (errbuf));
694 if (amt > 0) {
695 filewrite(f, errbuf, amt);
696 fflush(f);
697 }
698 }
699
700 /*
701 * print the following SCSI codes:
702 *
703 * - command transport status
704 * - CDB
705 * - SCSI status byte
706 * - Sense Bytes
707 * - Decoded Sense data
708 * - DMA status
709 * - SCSI timing
710 *
711 * into a buffer.
712 */
713 EXPORT int
scg_sprinterr(scgp,buf,maxcnt)714 scg_sprinterr(scgp, buf, maxcnt)
715 SCSI *scgp;
716 char *buf;
717 int maxcnt;
718 {
719 int amt;
720 int osilent = scgp->silent;
721 int overbose = scgp->verbose;
722
723 scgp->silent = 0;
724 scgp->verbose = 0;
725 amt = scg_svtail(scgp, NULL, buf, maxcnt);
726 scgp->silent = osilent;
727 scgp->verbose = overbose;
728 return (amt);
729 }
730
731 /*
732 * print the following SCSI codes:
733 *
734 * - command transport status
735 * - CDB
736 * - SCSI status byte
737 * - Sense Bytes
738 * - Decoded Sense data
739 *
740 * into a buffer.
741 */
742 EXPORT int
scg__sprinterr(scgp,buf,maxcnt)743 scg__sprinterr(scgp, buf, maxcnt)
744 SCSI *scgp;
745 char *buf;
746 int maxcnt;
747 {
748 register struct scg_cmd *cp = scgp->scmd;
749 register char *err;
750 char *cmdname = "SCSI command name not set by caller";
751 char errbuf[64];
752 register char *p = buf;
753 register int amt;
754
755 switch (cp->error) {
756
757 case SCG_NO_ERROR : err = "no error"; break;
758 case SCG_RETRYABLE: err = "retryable error"; break;
759 case SCG_FATAL : err = "fatal error"; break;
760 /*
761 * We need to cast timeval->* to long because
762 * of the broken sys/time.h in Linux.
763 */
764 case SCG_TIMEOUT : js_snprintf(errbuf, sizeof (errbuf),
765 "cmd timeout after %ld.%03ld (%d) s",
766 (long)scgp->cmdstop->tv_sec,
767 (long)scgp->cmdstop->tv_usec/1000,
768 cp->timeout);
769 err = errbuf;
770 break;
771 default: js_snprintf(errbuf, sizeof (errbuf),
772 "error: %d", cp->error);
773 err = errbuf;
774 }
775
776 if (scgp->cmdname != NULL && scgp->cmdname[0] != '\0')
777 cmdname = scgp->cmdname;
778 amt = serrmsgno(cp->ux_errno, p, maxcnt, "%s: scsi sendcmd: %s\n", cmdname, err);
779 if (amt < 0)
780 return (amt);
781 p += amt;
782 maxcnt -= amt;
783
784 amt = scg_sprintcdb(scgp, p, maxcnt);
785 if (amt < 0)
786 return (amt);
787 p += amt;
788 maxcnt -= amt;
789
790 if (cp->error <= SCG_RETRYABLE) {
791 amt = scg_sprintstatus(scgp, p, maxcnt);
792 if (amt < 0)
793 return (amt);
794 p += amt;
795 maxcnt -= amt;
796 }
797
798 if (cp->scb.chk) {
799 amt = scg_sprsense(p, maxcnt, (Uchar *)&cp->sense, cp->sense_count);
800 if (amt < 0)
801 return (amt);
802 p += amt;
803 maxcnt -= amt;
804 amt = scg__errmsg(scgp, p, maxcnt, &cp->sense, &cp->scb, -1);
805 if (amt < 0)
806 return (amt);
807 p += amt;
808 maxcnt -= amt;
809 }
810 return (p - buf);
811 }
812
813 /*
814 * XXX Do we need this function?
815 *
816 * print the SCSI Command descriptor block to XXX stderr.
817 */
818 EXPORT void
scg_printcdb(scgp)819 scg_printcdb(scgp)
820 SCSI *scgp;
821 {
822 scg_prbytes("CDB: ", scgp->scmd->cdb.cmd_cdb, scgp->scmd->cdb_len);
823 }
824
825 /*
826 * print the SCSI Command descriptor block into a buffer.
827 */
828 EXPORT int
scg_sprintcdb(scgp,buf,maxcnt)829 scg_sprintcdb(scgp, buf, maxcnt)
830 SCSI *scgp;
831 char *buf;
832 int maxcnt;
833 {
834 int cnt;
835
836 cnt = scg_sprbytes(buf, maxcnt, "CDB: ", scgp->scmd->cdb.cmd_cdb, scgp->scmd->cdb_len);
837 if (cnt < 0)
838 cnt = 0;
839 return (cnt);
840 }
841
842 /*
843 * XXX Do we need this function?
844 * XXX scg_printrdata() is used.
845 * XXX We need to check if we should write to stderr or better to scg->errfile
846 *
847 * print the SCSI send data to stderr.
848 */
849 EXPORT void
scg_printwdata(scgp)850 scg_printwdata(scgp)
851 SCSI *scgp;
852 {
853 register struct scg_cmd *scmd = scgp->scmd;
854
855 if (scmd->size > 0 && (scmd->flags & SCG_RECV_DATA) == 0) {
856 js_fprintf(stderr, "Sending %d (0x%X) bytes of data.\n",
857 scmd->size, scmd->size);
858 scg_prbytes("Write Data: ",
859 (Uchar *)scmd->addr,
860 scmd->size > 100 ? 100 : scmd->size);
861 }
862 }
863
864 /*
865 * print the SCSI send data into a buffer.
866 */
867 EXPORT int
scg_sprintwdata(scgp,buf,maxcnt)868 scg_sprintwdata(scgp, buf, maxcnt)
869 SCSI *scgp;
870 char *buf;
871 int maxcnt;
872 {
873 register struct scg_cmd *scmd = scgp->scmd;
874 register char *p = buf;
875 register int amt;
876
877 if (scmd->size > 0 && (scmd->flags & SCG_RECV_DATA) == 0) {
878 amt = js_snprintf(p, maxcnt,
879 "Sending %d (0x%X) bytes of data.\n",
880 scmd->size, scmd->size);
881 if (amt < 0)
882 return (amt);
883 p += amt;
884 maxcnt -= amt;
885 amt = scg_sprbytes(p, maxcnt, "Write Data: ",
886 (Uchar *)scmd->addr,
887 scmd->size > 100 ? 100 : scmd->size);
888 if (amt < 0)
889 return (amt);
890 p += amt;
891 }
892 return (p - buf);
893 }
894
895 /*
896 * XXX We need to check if we should write to stderr or better to scg->errfile
897 *
898 * print the SCSI received data to stderr.
899 */
900 EXPORT void
scg_printrdata(scgp)901 scg_printrdata(scgp)
902 SCSI *scgp;
903 {
904 register struct scg_cmd *scmd = scgp->scmd;
905 register int trcnt = scg_getdmacnt(scgp);
906
907 if (scmd->size > 0 && (scmd->flags & SCG_RECV_DATA) != 0) {
908 js_fprintf(stderr, "Got %d (0x%X), expecting %d (0x%X) bytes of data.\n",
909 trcnt, trcnt,
910 scmd->size, scmd->size);
911 scg_prbytes("Received Data: ",
912 (Uchar *)scmd->addr,
913 trcnt > 100 ? 100 : trcnt);
914 }
915 }
916
917 /*
918 * print the SCSI received data into a buffer.
919 */
920 EXPORT int
scg_sprintrdata(scgp,buf,maxcnt)921 scg_sprintrdata(scgp, buf, maxcnt)
922 SCSI *scgp;
923 char *buf;
924 int maxcnt;
925 {
926 register struct scg_cmd *scmd = scgp->scmd;
927 register char *p = buf;
928 register int amt;
929 register int trcnt = scg_getdmacnt(scgp);
930
931 if (scmd->size > 0 && (scmd->flags & SCG_RECV_DATA) != 0) {
932 amt = js_snprintf(p, maxcnt,
933 "Got %d (0x%X), expecting %d (0x%X) bytes of data.\n",
934 trcnt, trcnt,
935 scmd->size, scmd->size);
936 if (amt < 0)
937 return (amt);
938 p += amt;
939 maxcnt -= amt;
940 amt = scg_sprbytes(p, maxcnt,
941 "Received Data: ",
942 (Uchar *)scmd->addr,
943 trcnt > 100 ? 100 : trcnt);
944 if (amt < 0)
945 return (amt);
946 p += amt;
947 }
948 return (p - buf);
949 }
950
951 /*
952 * XXX We need to check if we should write to stderr or better to scg->errfile
953 *
954 * print the SCSI timings and (depending on verbose) received data to stderr.
955 */
956 EXPORT void
scg_printresult(scgp)957 scg_printresult(scgp)
958 SCSI *scgp;
959 {
960 js_fprintf(stderr, "cmd finished after %ld.%03lds timeout %ds\n",
961 (long)scgp->cmdstop->tv_sec,
962 (long)scgp->cmdstop->tv_usec/1000,
963 scgp->scmd->timeout);
964 if (scgp->verbose > 1)
965 scg_printrdata(scgp);
966 flush();
967 }
968
969 /*
970 * print the SCSI timings and (depending on verbose) received data into a buffer.
971 */
972 EXPORT int
scg_sprintresult(scgp,buf,maxcnt)973 scg_sprintresult(scgp, buf, maxcnt)
974 SCSI *scgp;
975 char *buf;
976 int maxcnt;
977 {
978 register char *p = buf;
979 register int amt;
980
981 amt = js_snprintf(p, maxcnt,
982 "cmd finished after %ld.%03lds timeout %ds\n",
983 (long)scgp->cmdstop->tv_sec,
984 (long)scgp->cmdstop->tv_usec/1000,
985 scgp->scmd->timeout);
986 if (amt < 0)
987 return (amt);
988 p += amt;
989 maxcnt -= amt;
990 if (scgp->verbose > 1) {
991 amt = scg_sprintrdata(scgp, p, maxcnt);
992 if (amt < 0)
993 return (amt);
994 p += amt;
995 }
996 return (p - buf);
997 }
998
999 /*
1000 * XXX Do we need this function?
1001 *
1002 * print the SCSI status byte in human readable form to the SCSI error file.
1003 */
1004 EXPORT void
scg_printstatus(scgp)1005 scg_printstatus(scgp)
1006 SCSI *scgp;
1007 {
1008 char errbuf[SCSI_ERRSTR_SIZE];
1009 int amt;
1010
1011 amt = scg_sprintstatus(scgp, errbuf, sizeof (errbuf));
1012 if (amt > 0) {
1013 filewrite((FILE *)scgp->errfile, errbuf, amt);
1014 fflush((FILE *)scgp->errfile);
1015 }
1016 }
1017
1018 /*
1019 * print the SCSI status byte in human readable form into a buffer.
1020 */
1021 EXPORT int
scg_sprintstatus(scgp,buf,maxcnt)1022 scg_sprintstatus(scgp, buf, maxcnt)
1023 SCSI *scgp;
1024 char *buf;
1025 int maxcnt;
1026 {
1027 register struct scg_cmd *cp = scgp->scmd;
1028 char *err;
1029 char *err2 = "";
1030 register char *p = buf;
1031 register int amt;
1032
1033 amt = js_snprintf(p, maxcnt, "status: 0x%x ", *(Uchar *)&cp->scb);
1034 if (amt < 0)
1035 return (amt);
1036 p += amt;
1037 maxcnt -= amt;
1038 #ifdef SCSI_EXTENDED_STATUS
1039 if (cp->scb.ext_st1) {
1040 amt = js_snprintf(p, maxcnt, "0x%x ", ((Uchar *)&cp->scb)[1]);
1041 if (amt < 0)
1042 return (amt);
1043 p += amt;
1044 maxcnt -= amt;
1045 }
1046 if (cp->scb.ext_st2) {
1047 amt = js_snprintf(p, maxcnt, "0x%x ", ((Uchar *)&cp->scb)[2]);
1048 if (amt < 0)
1049 return (amt);
1050 p += amt;
1051 maxcnt -= amt;
1052 }
1053 #endif
1054 switch (*(Uchar *)&cp->scb & 036) {
1055
1056 case 0 : err = "GOOD STATUS"; break;
1057 case 02 : err = "CHECK CONDITION"; break;
1058 case 04 : err = "CONDITION MET/GOOD"; break;
1059 case 010: err = "BUSY"; break;
1060 case 020: err = "INTERMEDIATE GOOD STATUS"; break;
1061 case 024: err = "INTERMEDIATE CONDITION MET/GOOD"; break;
1062 case 030: err = "RESERVATION CONFLICT"; break;
1063 default : err = "Reserved"; break;
1064 }
1065 #ifdef SCSI_EXTENDED_STATUS
1066 if (cp->scb.ext_st1 && cp->scb.ha_er)
1067 err2 = " host adapter detected error";
1068 #endif
1069 amt = js_snprintf(p, maxcnt, "(%s%s)\n", err, err2);
1070 if (amt < 0)
1071 return (amt);
1072 p += amt;
1073 return (p - buf);
1074 }
1075
1076 /*
1077 * print some bytes in hex to a file.
1078 */
1079 EXPORT void
scg_fprbytes(f,s,cp,n)1080 scg_fprbytes(f, s, cp, n)
1081 FILE *f;
1082 char *s;
1083 register Uchar *cp;
1084 register int n;
1085 {
1086 js_fprintf(f, "%s", s);
1087 while (--n >= 0)
1088 js_fprintf(f, " %02X", *cp++);
1089 js_fprintf(f, "\n");
1090 }
1091
1092 /*
1093 * print some bytes in ascii to a file.
1094 */
1095 EXPORT void
scg_fprascii(f,s,cp,n)1096 scg_fprascii(f, s, cp, n)
1097 FILE *f;
1098 char *s;
1099 register Uchar *cp;
1100 register int n;
1101 {
1102 register int c;
1103
1104 js_fprintf(f, "%s", s);
1105 while (--n >= 0) {
1106 c = *cp++;
1107 if (c >= ' ' && c < 0177)
1108 js_fprintf(f, "%c", c);
1109 else
1110 js_fprintf(f, ".");
1111 }
1112 js_fprintf(f, "\n");
1113 }
1114
1115 /*
1116 * XXX We need to check if we should write to stderr or better to scg->errfile
1117 *
1118 * print some bytes in hex to stderr.
1119 */
1120 EXPORT void
scg_prbytes(s,cp,n)1121 scg_prbytes(s, cp, n)
1122 char *s;
1123 register Uchar *cp;
1124 register int n;
1125 {
1126 scg_fprbytes(stderr, s, cp, n);
1127 }
1128
1129 /*
1130 * XXX We need to check if we should write to stderr or better to scg->errfile
1131 *
1132 * print some bytes in ascii to stderr.
1133 */
1134 EXPORT void
scg_prascii(s,cp,n)1135 scg_prascii(s, cp, n)
1136 char *s;
1137 register Uchar *cp;
1138 register int n;
1139 {
1140 scg_fprascii(stderr, s, cp, n);
1141 }
1142
1143 /*
1144 * print some bytes in hex into a buffer.
1145 */
1146 EXPORT int
scg_sprbytes(buf,maxcnt,s,cp,n)1147 scg_sprbytes(buf, maxcnt, s, cp, n)
1148 char *buf;
1149 int maxcnt;
1150 char *s;
1151 register Uchar *cp;
1152 register int n;
1153 {
1154 register char *p = buf;
1155 register int amt;
1156
1157 amt = js_snprintf(p, maxcnt, "%s", s);
1158 if (amt < 0)
1159 return (amt);
1160 p += amt;
1161 maxcnt -= amt;
1162
1163 while (--n >= 0) {
1164 amt = js_snprintf(p, maxcnt, " %02X", *cp++);
1165 if (amt < 0)
1166 return (amt);
1167 p += amt;
1168 maxcnt -= amt;
1169 }
1170 amt = js_snprintf(p, maxcnt, "\n");
1171 if (amt < 0)
1172 return (amt);
1173 p += amt;
1174 return (p - buf);
1175 }
1176
1177 /*
1178 * print some bytes in ascii into a buffer.
1179 */
1180 EXPORT int
scg_sprascii(buf,maxcnt,s,cp,n)1181 scg_sprascii(buf, maxcnt, s, cp, n)
1182 char *buf;
1183 int maxcnt;
1184 char *s;
1185 register Uchar *cp;
1186 register int n;
1187 {
1188 register char *p = buf;
1189 register int amt;
1190 register int c;
1191
1192 amt = js_snprintf(p, maxcnt, "%s", s);
1193 if (amt < 0)
1194 return (amt);
1195 p += amt;
1196 maxcnt -= amt;
1197
1198 while (--n >= 0) {
1199 c = *cp++;
1200 if (c >= ' ' && c < 0177)
1201 amt = js_snprintf(p, maxcnt, "%c", c);
1202 else
1203 amt = js_snprintf(p, maxcnt, ".");
1204 if (amt < 0)
1205 return (amt);
1206 p += amt;
1207 maxcnt -= amt;
1208 }
1209 amt = js_snprintf(p, maxcnt, "\n");
1210 if (amt < 0)
1211 return (amt);
1212 p += amt;
1213 return (p - buf);
1214 }
1215
1216 /*
1217 * print the SCSI sense data for last command in hex to a file.
1218 */
1219 EXPORT void
scg_fprsense(f,cp,n)1220 scg_fprsense(f, cp, n)
1221 FILE *f;
1222 Uchar *cp;
1223 int n;
1224 {
1225 scg_fprbytes(f, "Sense Bytes:", cp, n);
1226 }
1227
1228 /*
1229 * XXX We need to check if we should write to stderr or better to scg->errfile
1230 *
1231 * print the SCSI sense data for last command in hex to stderr.
1232 */
1233 EXPORT void
scg_prsense(cp,n)1234 scg_prsense(cp, n)
1235 Uchar *cp;
1236 int n;
1237 {
1238 scg_fprsense(stderr, cp, n);
1239 }
1240
1241 /*
1242 * print the SCSI sense data for last command in hex into a buffer.
1243 */
1244 EXPORT int
scg_sprsense(buf,maxcnt,cp,n)1245 scg_sprsense(buf, maxcnt, cp, n)
1246 char *buf;
1247 int maxcnt;
1248 Uchar *cp;
1249 int n;
1250 {
1251 return (scg_sprbytes(buf, maxcnt, "Sense Bytes:", cp, n));
1252 }
1253
1254 /*
1255 * Return the SCSI status byte for last command.
1256 */
1257 EXPORT int
scg_cmd_status(scgp)1258 scg_cmd_status(scgp)
1259 SCSI *scgp;
1260 {
1261 struct scg_cmd *cp = scgp->scmd;
1262 int cmdstatus = *(Uchar *)&cp->scb;
1263
1264 return (cmdstatus);
1265 }
1266
1267 /*
1268 * Return the SCSI sense key for last command.
1269 */
1270 EXPORT int
scg_sense_key(scgp)1271 scg_sense_key(scgp)
1272 SCSI *scgp;
1273 {
1274 register struct scg_cmd *cp = scgp->scmd;
1275 int key = -1;
1276
1277 if (!scg_cmd_err(scgp))
1278 return (0);
1279
1280 if (cp->sense.code >= 0x70)
1281 key = ((struct scsi_ext_sense *)&(cp->sense))->key;
1282 return (key);
1283 }
1284
1285 /*
1286 * Return the SCSI sense code for last command.
1287 */
1288 EXPORT int
scg_sense_code(scgp)1289 scg_sense_code(scgp)
1290 SCSI *scgp;
1291 {
1292 register struct scg_cmd *cp = scgp->scmd;
1293 int code = -1;
1294
1295 if (!scg_cmd_err(scgp))
1296 return (0);
1297
1298 if (cp->sense.code >= 0x70)
1299 code = ((struct scsi_ext_sense *)&(cp->sense))->sense_code;
1300 else
1301 code = cp->sense.code;
1302 return (code);
1303 }
1304
1305 /*
1306 * Return the SCSI sense qualifier for last command.
1307 */
1308 EXPORT int
scg_sense_qual(scgp)1309 scg_sense_qual(scgp)
1310 SCSI *scgp;
1311 {
1312 register struct scg_cmd *cp = scgp->scmd;
1313
1314 if (!scg_cmd_err(scgp))
1315 return (0);
1316
1317 if (cp->sense.code >= 0x70)
1318 return (((struct scsi_ext_sense *)&(cp->sense))->qual_code);
1319 else
1320 return (0);
1321 }
1322
1323 /*
1324 * Print the device type from the SCSI inquiry buffer to file.
1325 */
1326 EXPORT void
scg_fprintdev(f,ip)1327 scg_fprintdev(f, ip)
1328 FILE *f;
1329 struct scsi_inquiry *ip;
1330 {
1331 if (ip->removable)
1332 js_fprintf(f, "Removable ");
1333 if (ip->data_format >= 2) {
1334 switch (ip->qualifier) {
1335
1336 case INQ_DEV_PRESENT:
1337 break;
1338 case INQ_DEV_NOTPR:
1339 js_fprintf(f, "not present ");
1340 break;
1341 case INQ_DEV_RES:
1342 js_fprintf(f, "reserved ");
1343 break;
1344 case INQ_DEV_NOTSUP:
1345 if (ip->type == INQ_NODEV) {
1346 js_fprintf(f, "unsupported\n"); return;
1347 }
1348 js_fprintf(f, "unsupported ");
1349 break;
1350 default:
1351 js_fprintf(f, "vendor specific %d ",
1352 (int)ip->qualifier);
1353 }
1354 }
1355 switch (ip->type) {
1356
1357 case INQ_DASD:
1358 js_fprintf(f, "Disk"); break;
1359 case INQ_SEQD:
1360 js_fprintf(f, "Tape"); break;
1361 case INQ_PRTD:
1362 js_fprintf(f, "Printer"); break;
1363 case INQ_PROCD:
1364 js_fprintf(f, "Processor"); break;
1365 case INQ_WORM:
1366 js_fprintf(f, "WORM"); break;
1367 case INQ_ROMD:
1368 js_fprintf(f, "CD-ROM"); break;
1369 case INQ_SCAN:
1370 js_fprintf(f, "Scanner"); break;
1371 case INQ_OMEM:
1372 js_fprintf(f, "Optical Storage"); break;
1373 case INQ_JUKE:
1374 js_fprintf(f, "Juke Box"); break;
1375 case INQ_COMM:
1376 js_fprintf(f, "Communication"); break;
1377 case INQ_IT8_1:
1378 js_fprintf(f, "IT8 1"); break;
1379 case INQ_IT8_2:
1380 js_fprintf(f, "IT8 2"); break;
1381 case INQ_STARR:
1382 js_fprintf(f, "Storage array"); break;
1383 case INQ_ENCL:
1384 js_fprintf(f, "Enclosure services"); break;
1385 case INQ_SDAD:
1386 js_fprintf(f, "Simple direct access"); break;
1387 case INQ_OCRW:
1388 js_fprintf(f, "Optical card r/w"); break;
1389 case INQ_BRIDGE:
1390 js_fprintf(f, "Bridging expander"); break;
1391 case INQ_OSD:
1392 js_fprintf(f, "Object based storage"); break;
1393 case INQ_ADC:
1394 js_fprintf(f, "Automation/Drive Interface"); break;
1395 case INQ_WELLKNOWN:
1396 js_fprintf(f, "Well known lun"); break;
1397
1398 case INQ_NODEV:
1399 if (ip->data_format >= 2) {
1400 js_fprintf(f, "unknown/no device");
1401 break;
1402 } else if (ip->qualifier == INQ_DEV_NOTSUP) {
1403 js_fprintf(f, "unit not present");
1404 break;
1405 }
1406 default:
1407 js_fprintf(f, "unknown device type 0x%x",
1408 (int)ip->type);
1409 }
1410 js_fprintf(f, "\n");
1411 }
1412
1413 /*
1414 * Print the device type from the SCSI inquiry buffer to stdout.
1415 */
1416 EXPORT void
scg_printdev(ip)1417 scg_printdev(ip)
1418 struct scsi_inquiry *ip;
1419 {
1420 scg_fprintdev(stdout, ip);
1421 }
1422
1423 #include <schily/varargs.h>
1424
1425 /*
1426 * print into the SCSI error buffer, adjust the next write pointer.
1427 */
1428 /* VARARGS2 */
1429 EXPORT int
1430 #ifdef PROTOTYPES
scg_printf(SCSI * scgp,const char * form,...)1431 scg_printf(SCSI *scgp, const char *form, ...)
1432 #else
1433 scg_printf(scgp, form, va_alist)
1434 SCSI *scgp;
1435 char *form;
1436 va_dcl
1437 #endif
1438 {
1439 int cnt;
1440 va_list args;
1441
1442 #ifdef PROTOTYPES
1443 va_start(args, form);
1444 #else
1445 va_start(args);
1446 #endif
1447 cnt = js_snprintf(scgp->errptr, scg_errrsize(scgp), "%r", form, args);
1448 va_end(args);
1449
1450 if (cnt < 0) {
1451 scgp->errptr[0] = '\0';
1452 } else {
1453 scgp->errptr += cnt;
1454 }
1455 return (cnt);
1456 }
1457
1458 /*
1459 * Flush the SCSI error buffer to SCSI errfile.
1460 * Clear error buffer after flushing.
1461 */
1462 EXPORT int
scg_errflush(scgp)1463 scg_errflush(scgp)
1464 SCSI *scgp;
1465 {
1466 if (scgp->errfile == NULL)
1467 return (0);
1468
1469 return (scg_errfflush(scgp, (FILE *)scgp->errfile));
1470 }
1471
1472 /*
1473 * Flush the SCSI error buffer to a file.
1474 * Clear error buffer after flushing.
1475 */
1476 EXPORT int
scg_errfflush(scgp,f)1477 scg_errfflush(scgp, f)
1478 SCSI *scgp;
1479 FILE *f;
1480 {
1481 int cnt;
1482
1483 cnt = scgp->errptr - scgp->errbeg;
1484 if (cnt > 0) {
1485 filewrite(f, scgp->errbeg, cnt);
1486 fflush(f);
1487 scgp->errbeg = scgp->errptr;
1488 }
1489 return (cnt);
1490 }
1491