xref: /aosp_15_r20/external/sg3_utils/examples/sg__sat_set_features.c (revision 44704f698541f6367e81f991ef8bb54ccbf3fc18)
1 /*
2  * Copyright (c) 2006-2020 Douglas Gilbert.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. The name of the author may not be used to endorse or promote products
14  *    derived from this software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  *
28  */
29 
30 #include <unistd.h>
31 #include <fcntl.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <errno.h>
36 #include <getopt.h>
37 #include <sys/ioctl.h>
38 #include <sys/types.h>
39 #include <sys/stat.h>
40 
41 #include "sg_lib.h"
42 #include "sg_io_linux.h"
43 
44 /* This program performs a ATA PASS-THROUGH (16) SCSI command in order
45    to perform an ATA SET FEATURES command. See http://www.t10.org
46    SAT draft at time of writing: sat-r09.pdf
47 
48    Invocation:
49         sg_sat_set_features [-c <n>] [-f <n>] [-h] [-L <n>] [-v] [-V] <device>
50 
51 */
52 
53 #define SAT_ATA_PASS_THROUGH16 0x85
54 #define SAT_ATA_PASS_THROUGH16_LEN 16
55 #define SAT_ATA_RETURN_DESC 9  /* ATA Return (sense) Descriptor */
56 
57 #define ATA_SET_FEATURES 0xef
58 
59 #define EBUFF_SZ 512
60 
61 static char * version_str = "1.06 20201125";
62 
63 static struct option long_options[] = {
64         {"count", required_argument, 0, 'c'},
65         {"chk_cond", no_argument, 0, 'C'},
66         {"feature", required_argument, 0, 'f'},
67         {"help", no_argument, 0, 'h'},
68         {"lba", required_argument, 0, 'L'},
69         {"verbose", no_argument, 0, 'v'},
70         {"version", no_argument, 0, 'V'},
71         {0, 0, 0, 0},
72 };
73 
usage()74 void usage()
75 {
76     fprintf(stderr, "Usage: "
77           "sg_sat_set_features [--count=C] [--chk_cond] [--feature=F] "
78           "[--help]\n"
79           "                           [-lba=LBA] [--verbose] [--version] "
80           "DEVICE\n"
81           "  where:\n"
82           "    --count=C|-c C       count field contents (def: 0)\n"
83           "    --chk_cond|-C        set chk_cond field in pass-through "
84           "(def: 0)\n"
85           "    --feature=F|-f F     feature field contents (def: 0)\n"
86           "    --help|-h            output this usage message\n"
87           "    --lba=LBA| -L LBA    LBA field contents (def: 0)\n"
88           "    --verbose|-v         increase verbosity\n"
89           "    --version|-V         print version string and exit\n\n"
90           "Sends an ATA SET FEATURES command via a SAT pass through.\n"
91           "Primary feature code is placed in '--feature=F' with '--count=C' "
92           "and\n"
93           "'--lba=LBA' being auxiliaries for some features.  The arguments C, "
94           "F and LBA\n"
95           "are decimal unless prefixed by '0x' or have a trailing 'h'.\n"
96           "Example enabling write cache: 'sg_sat_set_feature --feature=2 "
97           "/dev/sdc'\n");
98 }
99 
main(int argc,char * argv[])100 int main(int argc, char * argv[])
101 {
102     int sg_fd, c, k;
103     uint8_t apt_cdb[SAT_ATA_PASS_THROUGH16_LEN] =
104                 {SAT_ATA_PASS_THROUGH16, 0, 0, 0, 0, 0, 0, 0,
105                  0, 0, 0, 0, 0, 0, 0, 0};
106     sg_io_hdr_t io_hdr;
107     char device_name[256];
108     char ebuff[EBUFF_SZ];
109     uint8_t sense_buffer[64];
110     int count = 0;
111     int feature = 0;
112     int lba = 0;
113     int verbose = 0;
114     int extend = 0;
115     int chk_cond = 0;   /* set to 1 to read register(s) back */
116     int protocol = 3;   /* non-data data-in */
117     int t_dir = 1;      /* 0 -> to device, 1 -> from device */
118     int byte_block = 1; /* 0 -> bytes, 1 -> 512 byte blocks */
119     int t_length = 0;   /* 0 -> no data transferred, 2 -> sector count */
120     const uint8_t * bp = NULL;
121 
122     memset(device_name, 0, sizeof(device_name));
123     while (1) {
124         int option_index = 0;
125 
126         c = getopt_long(argc, argv, "c:Cf:hL:vV", long_options,
127                         &option_index);
128         if (c == -1)
129             break;
130 
131         switch (c) {
132         case 'c':
133             count = sg_get_num(optarg);
134             if ((count < 0) || (count > 255)) {
135                 fprintf(stderr, "bad argument for '--count'\n");
136                 return SG_LIB_SYNTAX_ERROR;
137             }
138             break;
139         case 'C':
140             chk_cond = 1;
141             break;
142         case 'f':
143             feature = sg_get_num(optarg);
144             if ((feature < 0) || (feature > 255)) {
145                 fprintf(stderr, "bad argument for '--feature'\n");
146                 return SG_LIB_SYNTAX_ERROR;
147             }
148             break;
149         case 'h':
150         case '?':
151             usage();
152             return 0;
153         case 'L':
154             lba = sg_get_num(optarg);
155             if ((lba < 0) || (lba > 255)) {
156                 fprintf(stderr, "bad argument for '--lba'\n");
157                 return SG_LIB_SYNTAX_ERROR;
158             }
159             break;
160         case 'v':
161             ++verbose;
162             break;
163         case 'V':
164             fprintf(stderr, "version: %s\n", version_str);
165             return 0;
166         default:
167             fprintf(stderr, "unrecognised option code 0x%x ??\n", c);
168             usage();
169             return SG_LIB_SYNTAX_ERROR;
170         }
171     }
172     if (optind < argc) {
173         if ('\0' == device_name[0]) {
174             strncpy(device_name, argv[optind], sizeof(device_name) - 1);
175             device_name[sizeof(device_name) - 1] = '\0';
176             ++optind;
177         }
178         if (optind < argc) {
179             for (; optind < argc; ++optind)
180                 fprintf(stderr, "Unexpected extra argument: %s\n",
181                         argv[optind]);
182             usage();
183             return SG_LIB_SYNTAX_ERROR;
184         }
185     }
186 
187     if ('\0' == device_name[0]) {
188         fprintf(stderr, "missing device name!\n");
189         usage();
190         return 1;
191     }
192 
193     if ((sg_fd = open(device_name, O_RDWR)) < 0) {
194         snprintf(ebuff, EBUFF_SZ,
195                  "sg_sat_set_features: error opening file: %s", device_name);
196         perror(ebuff);
197         return 1;
198     }
199 
200     /* Prepare ATA PASS-THROUGH COMMAND (16) command */
201     apt_cdb[14] = ATA_SET_FEATURES;
202     apt_cdb[1] = (protocol << 1) | extend;
203     apt_cdb[2] = (chk_cond << 5) | (t_dir << 3) | (byte_block << 2) |
204                  t_length;
205     apt_cdb[4] = feature;
206     apt_cdb[6] = count;
207     apt_cdb[8] = lba & 0xff;
208     apt_cdb[10] = (lba >> 8) & 0xff;
209     apt_cdb[12] = (lba >> 16) & 0xff;
210     if (verbose) {
211         fprintf(stderr, "    ata pass through(16) cdb: ");
212         for (k = 0; k < SAT_ATA_PASS_THROUGH16_LEN; ++k)
213             fprintf(stderr, "%02x ", apt_cdb[k]);
214         fprintf(stderr, "\n");
215     }
216 
217     memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
218     io_hdr.interface_id = 'S';
219     io_hdr.cmd_len = sizeof(apt_cdb);
220     /* io_hdr.iovec_count = 0; */  /* memset takes care of this */
221     io_hdr.mx_sb_len = sizeof(sense_buffer);
222     io_hdr.dxfer_direction = SG_DXFER_NONE;
223     io_hdr.dxfer_len = 0;
224     io_hdr.dxferp = NULL;
225     io_hdr.cmdp = apt_cdb;
226     io_hdr.sbp = sense_buffer;
227     io_hdr.timeout = 20000;     /* 20000 millisecs == 20 seconds */
228     /* io_hdr.flags = 0; */     /* take defaults: indirect IO, etc */
229     /* io_hdr.pack_id = 0; */
230     /* io_hdr.usr_ptr = NULL; */
231 
232     if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) {
233         perror("sg_sat_set_features: SG_IO ioctl error");
234         close(sg_fd);
235         return 1;
236     }
237 
238     /* error processing: N.B. expect check condition, no sense ... !! */
239     switch (sg_err_category3(&io_hdr)) {
240     case SG_LIB_CAT_CLEAN:
241         break;
242     case SG_LIB_CAT_RECOVERED:  /* sat-r09 uses this sk */
243     case SG_LIB_CAT_NO_SENSE:   /* earlier SAT drafts used this */
244         bp = sg_scsi_sense_desc_find(sense_buffer, sizeof(sense_buffer),
245                                      SAT_ATA_RETURN_DESC);
246         if (NULL == bp) {
247             if (verbose > 1)
248                 printf("ATA Return Descriptor expected in sense but not "
249                        "found\n");
250             sg_chk_n_print3("ATA_16 command error", &io_hdr, 1);
251         } else if (verbose)
252             sg_chk_n_print3("ATA Return Descriptor", &io_hdr, 1);
253         if (bp && bp[3]) {
254             if (bp[3] & 0x4)
255                 printf("error in returned FIS: aborted command\n");
256             else
257                 printf("error=0x%x, status=0x%x\n", bp[3], bp[13]);
258         }
259         break;
260     default:
261         fprintf(stderr, "unexpected SCSI sense category\n");
262         bp = sg_scsi_sense_desc_find(sense_buffer, sizeof(sense_buffer),
263                                      SAT_ATA_RETURN_DESC);
264         if (NULL == bp)
265             sg_chk_n_print3("ATA_16 command error", &io_hdr, 1);
266         else if (verbose)
267             sg_chk_n_print3("ATA Return Descriptor, as expected",
268                              &io_hdr, 1);
269         if (bp && bp[3]) {
270             if (bp[3] & 0x4)
271                 printf("error in returned FIS: aborted command\n");
272             else
273                 printf("error=0x%x, status=0x%x\n", bp[3], bp[13]);
274         }
275         break;
276     }
277 
278     close(sg_fd);
279     return 0;
280 }
281