2 * Copyright (c) 2000 Kelly Yancey <kbyanc@posi.net>
3 * Derived from work done by Julian Elischer <julian@tfs.com,
4 * julian@dialix.oz.au>, 1993, and Peter Dufault <dufault@hda.com>, 1994.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer,
12 * without modification, immediately at the beginning of the file.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
32 #include <sys/queue.h>
33 #include <sys/types.h>
45 #include <cam/scsi/scsi_all.h>
47 #include <cam/cam_ccb.h>
49 #include "camcontrol.h"
53 #define DEFAULT_SCSI_MODE_DB "/usr/share/misc/scsi_modes"
54 #define DEFAULT_EDITOR "vi"
55 #define MAX_FORMAT_SPEC 4096 /* Max CDB format specifier. */
56 #define MAX_PAGENUM_LEN 10 /* Max characters in page num. */
57 #define MAX_PAGENAME_LEN 64 /* Max characters in page name. */
58 #define PAGEDEF_START '{' /* Page definition delimiter. */
59 #define PAGEDEF_END '}' /* Page definition delimiter. */
60 #define PAGENAME_START '"' /* Page name delimiter. */
61 #define PAGENAME_END '"' /* Page name delimiter. */
62 #define PAGEENTRY_END ';' /* Page entry terminator (optional). */
63 #define MAX_COMMAND_SIZE 255 /* Mode/Log sense data buffer size. */
64 #define PAGE_CTRL_SHIFT 6 /* Bit offset to page control field. */
67 /* Macros for working with mode pages. */
68 #define MODE_PAGE_HEADER(mh) \
69 (struct scsi_mode_page_header *)find_mode_page_6(mh)
71 #define MODE_PAGE_DATA(mph) \
72 (u_int8_t *)(mph) + sizeof(struct scsi_mode_page_header)
76 STAILQ_ENTRY(editentry
) link
;
86 STAILQ_HEAD(, editentry
) editlist
; /* List of page entries. */
87 int editlist_changed
= 0; /* Whether any entries were changed. */
90 SLIST_ENTRY(pagename
) link
;
94 SLIST_HEAD(, pagename
) namelist
; /* Page number to name mappings. */
96 static char format
[MAX_FORMAT_SPEC
]; /* Buffer for scsi cdb format def. */
98 static FILE *edit_file
= NULL
; /* File handle for edit file. */
99 static char edit_path
[] = "/tmp/camXXXXXX";
102 /* Function prototypes. */
103 static void editentry_create(void *hook
, int letter
, void *arg
,
104 int count
, char *name
);
105 static void editentry_update(void *hook
, int letter
, void *arg
,
106 int count
, char *name
);
107 static int editentry_save(void *hook
, char *name
);
108 static struct editentry
*editentry_lookup(char *name
);
109 static int editentry_set(char *name
, char *newvalue
,
111 static void editlist_populate(struct cam_device
*device
,
112 int modepage
, int page_control
,
113 int dbd
, int retries
, int timeout
);
114 static void editlist_save(struct cam_device
*device
, int modepage
,
115 int page_control
, int dbd
, int retries
,
117 static void nameentry_create(int pagenum
, char *name
);
118 static struct pagename
*nameentry_lookup(int pagenum
);
119 static int load_format(const char *pagedb_path
, int page
);
120 static int modepage_write(FILE *file
, int editonly
);
121 static int modepage_read(FILE *file
);
122 static void modepage_edit(void);
123 static void modepage_dump(struct cam_device
*device
, int page
,
124 int page_control
, int dbd
, int retries
,
126 static void cleanup_editfile(void);
129 #define returnerr(code) do { \
135 #define RTRIM(string) do { \
137 while (isspace(string[_length = strlen(string) - 1])) \
138 string[_length] = '\0'; \
143 editentry_create(void *hook __unused
, int letter
, void *arg
, int count
,
146 struct editentry
*newentry
; /* Buffer to hold new entry. */
148 /* Allocate memory for the new entry and a copy of the entry name. */
149 if ((newentry
= malloc(sizeof(struct editentry
))) == NULL
||
150 (newentry
->name
= strdup(name
)) == NULL
)
153 /* Trim any trailing whitespace for the entry name. */
154 RTRIM(newentry
->name
);
156 newentry
->editable
= (arg
!= NULL
);
157 newentry
->type
= letter
;
158 newentry
->size
= count
; /* Placeholder; not accurate. */
159 newentry
->value
.svalue
= NULL
;
161 STAILQ_INSERT_TAIL(&editlist
, newentry
, link
);
165 editentry_update(void *hook __unused
, int letter
, void *arg
, int count
,
168 struct editentry
*dest
; /* Buffer to hold entry to update. */
170 dest
= editentry_lookup(name
);
171 assert(dest
!= NULL
);
174 dest
->size
= count
; /* We get the real size now. */
176 switch (dest
->type
) {
177 case 'i': /* Byte-sized integral type. */
178 case 'b': /* Bit-sized integral types. */
180 dest
->value
.ivalue
= (intptr_t)arg
;
183 case 'c': /* Character array. */
184 case 'z': /* Null-padded string. */
185 editentry_set(name
, (char *)arg
, 0);
193 editentry_save(void *hook __unused
, char *name
)
195 struct editentry
*src
; /* Entry value to save. */
197 src
= editentry_lookup(name
);
201 case 'i': /* Byte-sized integral type. */
202 case 'b': /* Bit-sized integral types. */
204 return (src
->value
.ivalue
);
207 case 'c': /* Character array. */
208 case 'z': /* Null-padded string. */
209 return ((intptr_t)src
->value
.svalue
);
216 return (0); /* This should never happen. */
219 static struct editentry
*
220 editentry_lookup(char *name
)
222 struct editentry
*scan
;
224 assert(name
!= NULL
);
226 STAILQ_FOREACH(scan
, &editlist
, link
) {
227 if (strcasecmp(scan
->name
, name
) == 0)
231 /* Not found during list traversal. */
236 editentry_set(char *name
, char *newvalue
, int editonly
)
238 struct editentry
*dest
; /* Modepage entry to update. */
239 char *cval
; /* Pointer to new string value. */
240 char *convertend
; /* End-of-conversion pointer. */
241 int ival
; /* New integral value. */
242 int resolution
; /* Resolution in bits for integer conversion. */
245 * Macro to determine the maximum value of the given size for the current
247 * XXX Lovely x86's optimize out the case of shifting by 32 and gcc doesn't
248 * currently workaround it (even for int64's), so we have to kludge it.
250 #define RESOLUTION_MAX(size) ((resolution * (size) == 32)? \
251 (int)0xffffffff: (1 << (resolution * (size))) - 1)
253 assert(newvalue
!= NULL
);
254 if (*newvalue
== '\0')
255 return (0); /* Nothing to do. */
257 if ((dest
= editentry_lookup(name
)) == NULL
)
259 if (!dest
->editable
&& editonly
)
262 switch (dest
->type
) {
263 case 'i': /* Byte-sized integral type. */
264 case 'b': /* Bit-sized integral types. */
266 /* Convert the value string to an integer. */
267 resolution
= (dest
->type
== 'i')? 8: 1;
268 ival
= (int)strtol(newvalue
, &convertend
, 0);
269 if (*convertend
!= '\0')
271 if (ival
> RESOLUTION_MAX(dest
->size
) || ival
< 0) {
272 int newival
= (ival
< 0)? 0: RESOLUTION_MAX(dest
->size
);
273 warnx("value %d is out of range for entry %s; clipping "
274 "to %d", ival
, name
, newival
);
277 if (dest
->value
.ivalue
!= ival
)
278 editlist_changed
= 1;
279 dest
->value
.ivalue
= ival
;
282 case 'c': /* Character array. */
283 case 'z': /* Null-padded string. */
284 if ((cval
= malloc(dest
->size
+ 1)) == NULL
)
286 bzero(cval
, dest
->size
+ 1);
287 strncpy(cval
, newvalue
, dest
->size
);
288 if (dest
->type
== 'z') {
289 /* Convert trailing spaces to nulls. */
292 for (convertend2
= cval
+ dest
->size
;
293 convertend2
>= cval
; convertend2
--) {
294 if (*convertend2
== ' ')
296 else if (*convertend2
!= '\0')
300 if (strncmp(dest
->value
.svalue
, cval
, dest
->size
) == 0) {
301 /* Nothing changed, free the newly allocated string. */
305 if (dest
->value
.svalue
!= NULL
) {
306 /* Free the current string buffer. */
307 free(dest
->value
.svalue
);
308 dest
->value
.svalue
= NULL
;
310 dest
->value
.svalue
= cval
;
311 editlist_changed
= 1;
319 #undef RESOLUTION_MAX
323 nameentry_create(int pagenum
, char *name
) {
324 struct pagename
*newentry
;
326 if (pagenum
< 0 || name
== NULL
|| name
[0] == '\0')
329 /* Allocate memory for the new entry and a copy of the entry name. */
330 if ((newentry
= malloc(sizeof(struct pagename
))) == NULL
||
331 (newentry
->name
= strdup(name
)) == NULL
)
334 /* Trim any trailing whitespace for the page name. */
335 RTRIM(newentry
->name
);
337 newentry
->pagenum
= pagenum
;
338 SLIST_INSERT_HEAD(&namelist
, newentry
, link
);
341 static struct pagename
*
342 nameentry_lookup(int pagenum
) {
343 struct pagename
*scan
;
345 SLIST_FOREACH(scan
, &namelist
, link
) {
346 if (pagenum
== scan
->pagenum
)
350 /* Not found during list traversal. */
355 load_format(const char *pagedb_path
, int page
)
358 char str_pagenum
[MAX_PAGENUM_LEN
];
359 char str_pagename
[MAX_PAGENAME_LEN
];
361 int depth
; /* Quoting depth. */
364 enum { LOCATE
, PAGENAME
, PAGEDEF
} state
;
368 #define SETSTATE_LOCATE do { \
369 str_pagenum[0] = '\0'; \
370 str_pagename[0] = '\0'; \
375 #define SETSTATE_PAGENAME do { \
376 str_pagename[0] = '\0'; \
380 #define SETSTATE_PAGEDEF do { \
385 #define UPDATE_LINENO do { \
390 #define BUFFERFULL(buffer) (strlen(buffer) + 1 >= sizeof(buffer))
392 if ((pagedb
= fopen(pagedb_path
, "r")) == NULL
)
395 SLIST_INIT(&namelist
);
401 while ((ch
= fgetc(pagedb
)) != EOF
) {
403 /* Keep a line count to make error messages more useful. */
406 /* Skip over comments anywhere in the mode database. */
410 } while (ch
!= '\n' && ch
!= EOF
);
416 /* Strip out newline characters. */
420 /* Keep track of the nesting depth for braces. */
421 if (c
== PAGEDEF_START
)
423 else if (c
== PAGEDEF_END
) {
426 errx(EX_OSFILE
, "%s:%d: %s", pagedb_path
,
427 lineno
, "mismatched bracket");
434 * Locate the page the user is interested in, skipping
438 /* Ignore all whitespace between pages. */
440 } else if (depth
== 0 && c
== PAGEENTRY_END
) {
442 * A page entry terminator will reset page
443 * scanning (useful for assigning names to
444 * modes without providing a mode definition).
446 /* Record the name of this page. */
447 pagenum
= strtol(str_pagenum
, NULL
, 0);
448 nameentry_create(pagenum
, str_pagename
);
450 } else if (depth
== 0 && c
== PAGENAME_START
) {
452 } else if (c
== PAGEDEF_START
) {
453 pagenum
= strtol(str_pagenum
, NULL
, 0);
455 /* Record the name of this page. */
456 nameentry_create(pagenum
, str_pagename
);
458 * Only record the format if this is
459 * the page we are interested in.
461 if (page
== pagenum
&& !found
)
464 } else if (c
== PAGEDEF_END
) {
465 /* Reset the processor state. */
467 } else if (depth
== 0 && ! BUFFERFULL(str_pagenum
)) {
468 strncat(str_pagenum
, &c
, 1);
469 } else if (depth
== 0) {
470 errx(EX_OSFILE
, "%s:%d: %s %zd %s", pagedb_path
,
471 lineno
, "page identifier exceeds",
472 sizeof(str_pagenum
) - 1, "characters");
477 if (c
== PAGENAME_END
) {
479 * Return to LOCATE state without resetting the
480 * page number buffer.
483 } else if (! BUFFERFULL(str_pagename
)) {
484 strncat(str_pagename
, &c
, 1);
486 errx(EX_OSFILE
, "%s:%d: %s %zd %s", pagedb_path
,
487 lineno
, "page name exceeds",
488 sizeof(str_pagenum
) - 1, "characters");
494 * Transfer the page definition into a format buffer
495 * suitable for use with CDB encoding/decoding routines.
500 } else if (! BUFFERFULL(format
)) {
501 strncat(format
, &c
, 1);
503 errx(EX_OSFILE
, "%s:%d: %s %zd %s", pagedb_path
,
504 lineno
, "page definition exceeds",
505 sizeof(format
) - 1, "characters");
513 /* Repeat processing loop with next character. */
517 err(EX_OSFILE
, "%s", pagedb_path
);
519 /* Close the SCSI page database. */
522 if (!found
) /* Never found a matching page. */
529 editlist_populate(struct cam_device
*device
, int modepage
, int page_control
,
530 int dbd
, int retries
, int timeout
)
532 u_int8_t data
[MAX_COMMAND_SIZE
];/* Buffer to hold sense data. */
533 u_int8_t
*mode_pars
; /* Pointer to modepage params. */
534 struct scsi_mode_header_6
*mh
; /* Location of mode header. */
535 struct scsi_mode_page_header
*mph
;
537 STAILQ_INIT(&editlist
);
539 /* Fetch changeable values; use to build initial editlist. */
540 mode_sense(device
, modepage
, 1, dbd
, retries
, timeout
, data
,
543 mh
= (struct scsi_mode_header_6
*)data
;
544 mph
= MODE_PAGE_HEADER(mh
);
545 mode_pars
= MODE_PAGE_DATA(mph
);
547 /* Decode the value data, creating edit_entries for each value. */
548 buff_decode_visit(mode_pars
, mh
->data_length
, format
,
549 editentry_create
, 0);
551 /* Fetch the current/saved values; use to set editentry values. */
552 mode_sense(device
, modepage
, page_control
, dbd
, retries
, timeout
, data
,
554 buff_decode_visit(mode_pars
, mh
->data_length
, format
,
555 editentry_update
, 0);
559 editlist_save(struct cam_device
*device
, int modepage
, int page_control
,
560 int dbd
, int retries
, int timeout
)
562 u_int8_t data
[MAX_COMMAND_SIZE
];/* Buffer to hold sense data. */
563 u_int8_t
*mode_pars
; /* Pointer to modepage params. */
564 struct scsi_mode_header_6
*mh
; /* Location of mode header. */
565 struct scsi_mode_page_header
*mph
;
567 /* Make sure that something changed before continuing. */
568 if (! editlist_changed
)
572 * Preload the CDB buffer with the current mode page data.
573 * XXX If buff_encode_visit would return the number of bytes encoded
574 * we *should* use that to build a header from scratch. As it is
575 * now, we need mode_sense to find out the page length.
577 mode_sense(device
, modepage
, page_control
, dbd
, retries
, timeout
, data
,
580 /* Initial headers & offsets. */
581 mh
= (struct scsi_mode_header_6
*)data
;
582 mph
= MODE_PAGE_HEADER(mh
);
583 mode_pars
= MODE_PAGE_DATA(mph
);
585 /* Encode the value data to be passed back to the device. */
586 buff_encode_visit(mode_pars
, mh
->data_length
, format
,
589 /* Eliminate block descriptors. */
590 bcopy(mph
, ((u_int8_t
*)mh
) + sizeof(*mh
),
591 sizeof(*mph
) + mph
->page_length
);
593 /* Recalculate headers & offsets. */
594 mh
->blk_desc_len
= 0; /* No block descriptors. */
595 mh
->dev_spec
= 0; /* Clear device-specific parameters. */
596 mph
= MODE_PAGE_HEADER(mh
);
597 mode_pars
= MODE_PAGE_DATA(mph
);
599 mph
->page_code
&= SMS_PAGE_CODE
;/* Isolate just the page code. */
600 mh
->data_length
= 0; /* Reserved for MODE SELECT command. */
603 * Write the changes back to the device. If the user editted control
604 * page 3 (saved values) then request the changes be permanently
608 (page_control
<< PAGE_CTRL_SHIFT
== SMS_PAGE_CTRL_SAVED
),
609 retries
, timeout
, (u_int8_t
*)mh
,
610 sizeof(*mh
) + mh
->blk_desc_len
+ sizeof(*mph
) + mph
->page_length
);
614 modepage_write(FILE *file
, int editonly
)
616 struct editentry
*scan
;
619 STAILQ_FOREACH(scan
, &editlist
, link
) {
620 if (scan
->editable
|| !editonly
) {
622 if (scan
->type
== 'c' || scan
->type
== 'z') {
623 fprintf(file
, "%s: %s\n", scan
->name
,
626 fprintf(file
, "%s: %d\n", scan
->name
,
635 modepage_read(FILE *file
)
637 char *buffer
; /* Pointer to dynamic line buffer. */
638 char *line
; /* Pointer to static fgetln buffer. */
639 char *name
; /* Name portion of the line buffer. */
640 char *value
; /* Value portion of line buffer. */
641 size_t length
; /* Length of static fgetln buffer. */
643 #define ABORT_READ(message, param) do { \
644 warnx(message, param); \
649 while ((line
= fgetln(file
, &length
)) != NULL
) {
650 /* Trim trailing whitespace (including optional newline). */
651 while (length
> 0 && isspace(line
[length
- 1]))
654 /* Allocate a buffer to hold the line + terminating null. */
655 if ((buffer
= malloc(length
+ 1)) == NULL
)
657 memcpy(buffer
, line
, length
);
658 buffer
[length
] = '\0';
660 /* Strip out comments. */
661 if ((value
= strchr(buffer
, '#')) != NULL
)
664 /* The name is first in the buffer. Trim whitespace.*/
667 while (isspace(*name
))
670 /* Skip empty lines. */
671 if (strlen(name
) == 0)
674 /* The name ends at the colon; the value starts there. */
675 if ((value
= strrchr(buffer
, ':')) == NULL
)
676 ABORT_READ("no value associated with %s", name
);
677 *value
= '\0'; /* Null-terminate name. */
678 value
++; /* Value starts afterwards. */
680 /* Trim leading and trailing whitespace. */
682 while (isspace(*value
))
685 /* Make sure there is a value left. */
686 if (strlen(value
) == 0)
687 ABORT_READ("no value associated with %s", name
);
689 /* Update our in-memory copy of the modepage entry value. */
690 if (editentry_set(name
, value
, 1) != 0) {
691 if (errno
== ENOENT
) {
692 /* No entry by the name. */
693 ABORT_READ("no such modepage entry \"%s\"",
695 } else if (errno
== EINVAL
) {
697 ABORT_READ("Invalid value for entry \"%s\"",
699 } else if (errno
== ERANGE
) {
700 /* Value out of range for entry type. */
701 ABORT_READ("value out of range for %s", name
);
702 } else if (errno
== EPERM
) {
703 /* Entry is not editable; not fatal. */
704 warnx("modepage entry \"%s\" is read-only; "
711 return (ferror(file
)? -1: 0);
724 if (!isatty(fileno(stdin
))) {
725 /* Not a tty, read changes from stdin. */
726 modepage_read(stdin
);
730 /* Lookup editor to invoke. */
731 if ((editor
= getenv("EDITOR")) == NULL
)
732 editor
= DEFAULT_EDITOR
;
734 /* Create temp file for editor to modify. */
735 if ((fd
= mkstemp(edit_path
)) == -1)
736 errx(EX_CANTCREAT
, "mkstemp failed");
738 atexit(cleanup_editfile
);
740 if ((edit_file
= fdopen(fd
, "w")) == NULL
)
741 err(EX_NOINPUT
, "%s", edit_path
);
743 written
= modepage_write(edit_file
, 1);
749 warnx("no editable entries");
755 * Allocate memory to hold the command line (the 2 extra characters
756 * are to hold the argument separator (a space), and the terminating
759 commandline
= malloc(strlen(editor
) + strlen(edit_path
) + 2);
760 if (commandline
== NULL
)
762 sprintf(commandline
, "%s %s", editor
, edit_path
);
764 /* Invoke the editor on the temp file. */
765 if (system(commandline
) == -1)
766 err(EX_UNAVAILABLE
, "could not invoke %s", editor
);
769 if ((edit_file
= fopen(edit_path
, "r")) == NULL
)
770 err(EX_NOINPUT
, "%s", edit_path
);
772 /* Read any changes made to the temp file. */
773 modepage_read(edit_file
);
779 modepage_dump(struct cam_device
*device
, int page
, int page_control
, int dbd
,
780 int retries
, int timeout
)
782 u_int8_t data
[MAX_COMMAND_SIZE
];/* Buffer to hold sense data. */
783 u_int8_t
*mode_pars
; /* Pointer to modepage params. */
784 struct scsi_mode_header_6
*mh
; /* Location of mode header. */
785 struct scsi_mode_page_header
*mph
;
786 int indx
; /* Index for scanning mode params. */
788 mode_sense(device
, page
, page_control
, dbd
, retries
, timeout
, data
,
791 mh
= (struct scsi_mode_header_6
*)data
;
792 mph
= MODE_PAGE_HEADER(mh
);
793 mode_pars
= MODE_PAGE_DATA(mph
);
795 /* Print the raw mode page data with newlines each 8 bytes. */
796 for (indx
= 0; indx
< mph
->page_length
; indx
++) {
797 printf("%02x%c",mode_pars
[indx
],
798 (((indx
+ 1) % 8) == 0) ? '\n' : ' ');
804 cleanup_editfile(void)
806 if (edit_file
== NULL
)
808 if (fclose(edit_file
) != 0 || unlink(edit_path
) != 0)
809 warn("%s", edit_path
);
814 mode_edit(struct cam_device
*device
, int page
, int page_control
, int dbd
,
815 int edit
, int binary
, int retry_count
, int timeout
)
817 const char *pagedb_path
; /* Path to modepage database. */
820 errx(EX_USAGE
, "cannot edit in binary mode.");
823 if ((pagedb_path
= getenv("SCSI_MODES")) == NULL
)
824 pagedb_path
= DEFAULT_SCSI_MODE_DB
;
826 if (load_format(pagedb_path
, page
) != 0 && (edit
|| verbose
)) {
827 if (errno
== ENOENT
) {
828 /* Modepage database file not found. */
829 warn("cannot open modepage database \"%s\"",
831 } else if (errno
== ESRCH
) {
832 /* Modepage entry not found in database. */
833 warnx("modepage %d not found in database"
834 "\"%s\"", page
, pagedb_path
);
836 /* We can recover in display mode, otherwise we exit. */
838 warnx("reverting to binary display only");
844 editlist_populate(device
, page
, page_control
, dbd
, retry_count
,
849 if (page_control
<< PAGE_CTRL_SHIFT
!= SMS_PAGE_CTRL_CURRENT
&&
850 page_control
<< PAGE_CTRL_SHIFT
!= SMS_PAGE_CTRL_SAVED
)
851 errx(EX_USAGE
, "it only makes sense to edit page 0 "
852 "(current) or page 3 (saved values)");
854 editlist_save(device
, page
, page_control
, dbd
, retry_count
,
856 } else if (binary
|| STAILQ_EMPTY(&editlist
)) {
857 /* Display without formatting information. */
858 modepage_dump(device
, page
, page_control
, dbd
, retry_count
,
861 /* Display with format. */
862 modepage_write(stdout
, 0);
867 mode_list(struct cam_device
*device
, int page_control
, int dbd
,
868 int retry_count
, int timeout
)
870 u_int8_t data
[MAX_COMMAND_SIZE
];/* Buffer to hold sense data. */
871 u_int8_t
*mode_pars
; /* Pointer to modepage params. */
872 struct scsi_mode_header_6
*mh
; /* Location of mode header. */
873 struct scsi_mode_page_header
*mph
;
874 struct pagename
*nameentry
;
875 const char *pagedb_path
;
878 if ((pagedb_path
= getenv("SCSI_MODES")) == NULL
)
879 pagedb_path
= DEFAULT_SCSI_MODE_DB
;
881 if (load_format(pagedb_path
, 0) != 0 && verbose
&& errno
== ENOENT
) {
882 /* Modepage database file not found. */
883 warn("cannot open modepage database \"%s\"", pagedb_path
);
886 /* Build the list of all mode pages by querying the "all pages" page. */
887 mode_sense(device
, SMS_ALL_PAGES_PAGE
, page_control
, dbd
, retry_count
,
888 timeout
, data
, sizeof(data
));
890 mh
= (struct scsi_mode_header_6
*)data
;
891 len
= mh
->blk_desc_len
; /* Skip block descriptors. */
892 /* Iterate through the pages in the reply. */
893 while (len
< mh
->data_length
) {
894 /* Locate the next mode page header. */
895 mph
= (struct scsi_mode_page_header
*)
896 ((intptr_t)mh
+ sizeof(*mh
) + len
);
897 mode_pars
= MODE_PAGE_DATA(mph
);
899 mph
->page_code
&= SMS_PAGE_CODE
;
900 nameentry
= nameentry_lookup(mph
->page_code
);
902 if (nameentry
== NULL
|| nameentry
->name
== NULL
)
903 printf("0x%02x\n", mph
->page_code
);
905 printf("0x%02x\t%s\n", mph
->page_code
,
907 len
+= mph
->page_length
+ sizeof(*mph
);