int main(int argc, char * argv[]) { int sg_fd, res, c, num, alloc_len, off, pdt; int k, md_len, hdr_len, bd_len, mask_in_len; unsigned u, uu; int dbd = 0; int got_contents = 0; int force = 0; int got_mask = 0; int mode_6 = 0; int pg_code = -1; int sub_pg_code = 0; int save = 0; int verbose = 0; int read_in_len = 0; const char * device_name = NULL; unsigned char read_in[MX_ALLOC_LEN]; unsigned char mask_in[MX_ALLOC_LEN]; unsigned char ref_md[MX_ALLOC_LEN]; char ebuff[EBUFF_SZ]; struct sg_simple_inquiry_resp inq_data; int ret = 0; while (1) { int option_index = 0; c = getopt_long(argc, argv, "c:dfhl:m:p:svV", long_options, &option_index); if (c == -1) break; switch (c) { case 'c': memset(read_in, 0, sizeof(read_in)); if (0 != build_mode_page(optarg, read_in, &read_in_len, sizeof(read_in))) { fprintf(stderr, "bad argument to '--contents'\n"); return SG_LIB_SYNTAX_ERROR; } got_contents = 1; break; case 'd': dbd = 1; break; case 'f': force = 1; break; case 'h': case '?': usage(); return 0; case 'l': num = sscanf(optarg, "%d", &res); if ((1 == num) && ((6 == res) || (10 == res))) mode_6 = (6 == res) ? 1 : 0; else { fprintf(stderr, "length (of cdb) must be 6 or 10\n"); return SG_LIB_SYNTAX_ERROR; } break; case 'm': memset(mask_in, 0xff, sizeof(mask_in)); if (0 != build_mask(optarg, mask_in, &mask_in_len, sizeof(mask_in))) { fprintf(stderr, "bad argument to '--mask'\n"); return SG_LIB_SYNTAX_ERROR; } got_mask = 1; break; case 'p': if (NULL == strchr(optarg, ',')) { num = sscanf(optarg, "%x", &u); if ((1 != num) || (u > 62)) { fprintf(stderr, "Bad page code value after '--page' " "switch\n"); return SG_LIB_SYNTAX_ERROR; } pg_code = u; } else if (2 == sscanf(optarg, "%x,%x", &u, &uu)) { if (uu > 254) { fprintf(stderr, "Bad sub page code value after '--page'" " switch\n"); return SG_LIB_SYNTAX_ERROR; } pg_code = u; sub_pg_code = uu; } else { fprintf(stderr, "Bad page code, subpage code sequence after " "'--page' switch\n"); return SG_LIB_SYNTAX_ERROR; } break; case 's': save = 1; break; case 'v': ++verbose; break; case 'V': fprintf(stderr, ME "version: %s\n", version_str); return 0; default: fprintf(stderr, "unrecognised option code 0x%x ??\n", c); usage(); return SG_LIB_SYNTAX_ERROR; } } if (optind < argc) { if (NULL == device_name) { device_name = argv[optind]; ++optind; } if (optind < argc) { for (; optind < argc; ++optind) fprintf(stderr, "Unexpected extra argument: %s\n", argv[optind]); usage(); return SG_LIB_SYNTAX_ERROR; } } if (NULL == device_name) { fprintf(stderr, "missing device name!\n"); usage(); return SG_LIB_SYNTAX_ERROR; } if (pg_code < 0) { fprintf(stderr, "need page code (see '--page=')\n"); usage(); return SG_LIB_SYNTAX_ERROR; } if (got_mask && force) { fprintf(stderr, "cannot use both '--force' and '--mask'\n"); usage(); return SG_LIB_SYNTAX_ERROR; } sg_fd = sg_cmds_open_device(device_name, 0 /* rw */, verbose); if (sg_fd < 0) { fprintf(stderr, ME "open error: %s: %s\n", device_name, safe_strerror(-sg_fd)); return SG_LIB_FILE_ERROR; } if (0 == sg_simple_inquiry(sg_fd, &inq_data, 0, verbose)) pdt = inq_data.peripheral_type; else pdt = 0x1f; /* do MODE SENSE to fetch current values */ memset(ref_md, 0, MX_ALLOC_LEN); alloc_len = mode_6 ? SHORT_ALLOC_LEN : MX_ALLOC_LEN; if (mode_6) res = sg_ll_mode_sense6(sg_fd, dbd, 0 /*current */, pg_code, sub_pg_code, ref_md, alloc_len, 1, verbose); else res = sg_ll_mode_sense10(sg_fd, 0 /* llbaa */, dbd, 0 /* current */, pg_code, sub_pg_code, ref_md, alloc_len, 1, verbose); ret = res; if (SG_LIB_CAT_INVALID_OP == res) { fprintf(stderr, "MODE SENSE (%d) not supported, try '--len=%d'\n", (mode_6 ? 6 : 10), (mode_6 ? 10 : 6)); goto err_out; } else if (SG_LIB_CAT_NOT_READY == res) { fprintf(stderr, "MODE SENSE (%d) failed, device not ready\n", (mode_6 ? 6 : 10)); goto err_out; } else if (SG_LIB_CAT_UNIT_ATTENTION == res) { fprintf(stderr, "MODE SENSE (%d) failed, unit attention\n", (mode_6 ? 6 : 10)); goto err_out; } else if (SG_LIB_CAT_ABORTED_COMMAND == res) { fprintf(stderr, "MODE SENSE (%d) failed, aborted command\n", (mode_6 ? 6 : 10)); goto err_out; } else if (SG_LIB_CAT_ILLEGAL_REQ == res) { fprintf(stderr, "bad field in MODE SENSE (%d) command\n", (mode_6 ? 6 : 10)); goto err_out; } else if (0 != res) { fprintf(stderr, "MODE SENSE (%d) failed\n", (mode_6 ? 6 : 10)); goto err_out; } off = sg_mode_page_offset(ref_md, alloc_len, mode_6, ebuff, EBUFF_SZ); if (off < 0) { fprintf(stderr, "MODE SENSE (%d): %s\n", (mode_6 ? 6 : 10), ebuff); goto err_out; } if (mode_6) { hdr_len = 4; md_len = ref_md[0] + 1; bd_len = ref_md[3]; } else { hdr_len = 8; md_len = (ref_md[0] << 8) + ref_md[1] + 2; bd_len = (ref_md[6] << 8) + ref_md[7]; } if (got_contents) { if (read_in_len < 2) { fprintf(stderr, "contents length=%d too short\n", read_in_len); goto err_out; } ref_md[0] = 0; /* mode data length reserved for mode select */ if (! mode_6) ref_md[1] = 0; /* mode data length reserved for mode select */ if (0 == pdt) /* for disks mask out DPOFUA bit */ ref_md[mode_6 ? 2 : 3] &= 0xef; if (md_len > alloc_len) { fprintf(stderr, "mode data length=%d exceeds allocation " "length=%d\n", md_len, alloc_len); goto err_out; } if (got_mask) { for (k = 0; k < (md_len - off); ++k) { if ((0x0 == mask_in[k]) || (k > read_in_len)) read_in[k] = ref_md[off + k]; else if (mask_in[k] < 0xff) { c = (ref_md[off + k] & (0xff & ~mask_in[k])); read_in[k] = (c | (read_in[k] & mask_in[k])); } } read_in_len = md_len - off; } if (! force) { if ((! (ref_md[off] & 0x80)) && save) { fprintf(stderr, "PS bit in existing mode page indicates that " "it is not saveable\n but '--save' option given\n"); goto err_out; } read_in[0] &= 0x7f; /* mask out PS bit, reserved in mode select */ if ((md_len - off) != read_in_len) { fprintf(stderr, "contents length=%d but reference mode page " "length=%d\n", read_in_len, md_len - off); goto err_out; } if (pg_code != (read_in[0] & 0x3f)) { fprintf(stderr, "contents page_code=0x%x but reference " "page_code=0x%x\n", (read_in[0] & 0x3f), pg_code); goto err_out; } if ((read_in[0] & 0x40) != (ref_md[off] & 0x40)) { fprintf(stderr, "contents flags subpage but reference page" "does not (or vice versa)\n"); goto err_out; } if ((read_in[0] & 0x40) && (read_in[1] != sub_pg_code)) { fprintf(stderr, "contents subpage_code=0x%x but reference " "sub_page_code=0x%x\n", read_in[1], sub_pg_code); goto err_out; } } else md_len = off + read_in_len; /* force length */ memcpy(ref_md + off, read_in, read_in_len); if (mode_6) res = sg_ll_mode_select6(sg_fd, 1, save, ref_md, md_len, 1, verbose); else res = sg_ll_mode_select10(sg_fd, 1, save, ref_md, md_len, 1, verbose); ret = res; if (SG_LIB_CAT_INVALID_OP == res) { fprintf(stderr, "MODE SELECT (%d) not supported\n", (mode_6 ? 6 : 10)); goto err_out; } else if (SG_LIB_CAT_NOT_READY == res) { fprintf(stderr, "MODE SELECT (%d) failed, device not ready\n", (mode_6 ? 6 : 10)); goto err_out; } else if (SG_LIB_CAT_UNIT_ATTENTION == res) { fprintf(stderr, "MODE SELECT (%d) failed, unit attention\n", (mode_6 ? 6 : 10)); goto err_out; } else if (SG_LIB_CAT_ABORTED_COMMAND == res) { fprintf(stderr, "MODE SELECT (%d) failed, aborted command\n", (mode_6 ? 6 : 10)); goto err_out; } else if (SG_LIB_CAT_ILLEGAL_REQ == res) { fprintf(stderr, "bad field in MODE SELECT (%d) command\n", (mode_6 ? 6 : 10)); goto err_out; } else if (0 != res) { fprintf(stderr, "MODE SELECT (%d) failed\n", (mode_6 ? 6 : 10)); goto err_out; } } else { printf(">>> No contents given, so show current mode page data:\n"); printf(" header:\n"); dStrHex((const char *)ref_md, hdr_len, -1); if (bd_len) { printf(" block descriptor(s):\n"); dStrHex((const char *)(ref_md + hdr_len), bd_len, -1); } else printf(" << no block descriptors >>\n"); printf(" mode page:\n"); dStrHex((const char *)(ref_md + off), md_len - off, -1); } err_out: res = sg_cmds_close_device(sg_fd); if (res < 0) { fprintf(stderr, "close error: %s\n", safe_strerror(-res)); if (0 == ret) return SG_LIB_FILE_ERROR; } return (ret >= 0) ? ret : SG_LIB_CAT_OTHER; }
/* Fetches current, changeable, default and/or saveable modes pages as * indicated by pcontrol_arr for given pg_code and sub_pg_code. If * mode6==0 then use MODE SENSE (10) else use MODE SENSE (6). If * flexible set and mode data length seems wrong then try and * fix (compensating hack for bad device or driver). pcontrol_arr * should have 4 elements for output of current, changeable, default * and saved values respectively. Each element should be NULL or * at least mx_mpage_len bytes long. * Return of 0 -> overall success, various SG_LIB_CAT_* positive values or * -1 -> other errors. * If success_mask pointer is not NULL then first zeros it. Then set bits * 0, 1, 2 and/or 3 if the current, changeable, default and saved values * respectively have been fetched. If error on current page * then stops and returns that error; otherwise continues if an error is * detected but returns the first error encountered. */ int sg_get_mode_page_controls(int sg_fd, int mode6, int pg_code, int sub_pg_code, int dbd, int flexible, int mx_mpage_len, int * success_mask, void * pcontrol_arr[], int * reported_len, int verbose) { int k, n, res, offset, calc_len, xfer_len, resp_mode6; unsigned char buff[MODE_RESP_ARB_LEN]; char ebuff[EBUFF_SZ]; int first_err = 0; if (success_mask) *success_mask = 0; if (reported_len) *reported_len = 0; if (mx_mpage_len < 4) return 0; memset(ebuff, 0, sizeof(ebuff)); /* first try to find length of current page response */ memset(buff, 0, MODE10_RESP_HDR_LEN); if (mode6) /* want first 8 bytes just in case */ res = sg_ll_mode_sense6(sg_fd, dbd, 0 /* pc */, pg_code, sub_pg_code, buff, MODE10_RESP_HDR_LEN, 1, verbose); else res = sg_ll_mode_sense10(sg_fd, 0 /* llbaa */, dbd, 0 /* pc */, pg_code, sub_pg_code, buff, MODE10_RESP_HDR_LEN, 1, verbose); if (0 != res) return res; n = buff[0]; if (reported_len) *reported_len = mode6 ? (n + 1) : (sg_get_unaligned_be16(buff) + 2); resp_mode6 = mode6; if (flexible) { if (mode6 && (n < 3)) { resp_mode6 = 0; if (verbose) pr2ws(">>> msense(6) but resp[0]=%d so try msense(10) " "response processing\n", n); } if ((0 == mode6) && (n > 5)) { if ((n > 11) && (0 == (n % 2)) && (0 == buff[4]) && (0 == buff[5]) && (0 == buff[6])) { buff[1] = n; buff[0] = 0; if (verbose) pr2ws(">>> msense(10) but resp[0]=%d and not msense(6) " "response so fix length\n", n); } else resp_mode6 = 1; } } if (verbose && (resp_mode6 != mode6)) pr2ws(">>> msense(%d) but resp[0]=%d so switch response " "processing\n", (mode6 ? 6 : 10), buff[0]); calc_len = resp_mode6 ? (buff[0] + 1) : (sg_get_unaligned_be16(buff) + 2); if (calc_len > MODE_RESP_ARB_LEN) calc_len = MODE_RESP_ARB_LEN; offset = sg_mode_page_offset(buff, calc_len, resp_mode6, ebuff, EBUFF_SZ); if (offset < 0) { if (('\0' != ebuff[0]) && (verbose > 0)) pr2ws("%s: %s\n", __func__, ebuff); return SG_LIB_CAT_MALFORMED; } xfer_len = calc_len - offset; if (xfer_len > mx_mpage_len) xfer_len = mx_mpage_len; for (k = 0; k < 4; ++k) { if (NULL == pcontrol_arr[k]) continue; memset(pcontrol_arr[k], 0, mx_mpage_len); if (mode6) res = sg_ll_mode_sense6(sg_fd, dbd, k /* pc */, pg_code, sub_pg_code, buff, calc_len, 1, verbose); else res = sg_ll_mode_sense10(sg_fd, 0 /* llbaa */, dbd, k /* pc */, pg_code, sub_pg_code, buff, calc_len, 1, verbose); if (0 != res) { if (0 == first_err) first_err = res; if (0 == k) break; /* if problem on current page, it won't improve */ else continue; } if (xfer_len > 0) memcpy(pcontrol_arr[k], buff + offset, xfer_len); if (success_mask) *success_mask |= (1 << k); } return first_err; }
/* Fetches current, changeable, default and/or saveable modes pages as * indicated by pcontrol_arr for given pg_code and sub_pg_code. If * mode6==false then use MODE SENSE (10) else use MODE SENSE (6). If * flexible set and mode data length seems wrong then try and * fix (compensating hack for bad device or driver). pcontrol_arr * should have 4 elements for output of current, changeable, default * and saved values respectively. Each element should be NULL or * at least mx_mpage_len bytes long. * Return of 0 -> overall success, various SG_LIB_CAT_* positive values or * -1 -> other errors. * If success_mask pointer is not NULL then first zeros it. Then set bits * 0, 1, 2 and/or 3 if the current, changeable, default and saved values * respectively have been fetched. If error on current page * then stops and returns that error; otherwise continues if an error is * detected but returns the first error encountered. */ int sg_get_mode_page_controls(int sg_fd, bool mode6, int pg_code, int sub_pg_code, bool dbd, bool flexible, int mx_mpage_len, int * success_mask, void * pcontrol_arr[], int * reported_lenp, int verbose) { bool resp_mode6; int k, n, res, offset, calc_len, xfer_len; int resid = 0; const int msense10_hlen = MODE10_RESP_HDR_LEN; uint8_t buff[MODE_RESP_ARB_LEN]; char ebuff[EBUFF_SZ]; int first_err = 0; if (success_mask) *success_mask = 0; if (reported_lenp) *reported_lenp = 0; if (mx_mpage_len < 4) return 0; memset(ebuff, 0, sizeof(ebuff)); /* first try to find length of current page response */ memset(buff, 0, msense10_hlen); if (mode6) /* want first 8 bytes just in case */ res = sg_ll_mode_sense6(sg_fd, dbd, 0 /* pc */, pg_code, sub_pg_code, buff, msense10_hlen, true, verbose); else /* MODE SENSE(10) obviously */ res = sg_ll_mode_sense10_v2(sg_fd, false /* llbaa */, dbd, 0 /* pc */, pg_code, sub_pg_code, buff, msense10_hlen, 0, &resid, true, verbose); if (0 != res) return res; n = buff[0]; if (reported_lenp) { int m; m = sg_msense_calc_length(buff, msense10_hlen, mode6, NULL) - resid; if (m < 0) /* Grrr, this should not happen */ m = 0; *reported_lenp = m; } resp_mode6 = mode6; if (flexible) { if (mode6 && (n < 3)) { resp_mode6 = false; if (verbose) pr2ws(">>> msense(6) but resp[0]=%d so try msense(10) " "response processing\n", n); } if ((! mode6) && (n > 5)) { if ((n > 11) && (0 == (n % 2)) && (0 == buff[4]) && (0 == buff[5]) && (0 == buff[6])) { buff[1] = n; buff[0] = 0; if (verbose) pr2ws(">>> msense(10) but resp[0]=%d and not msense(6) " "response so fix length\n", n); } else resp_mode6 = true; } } if (verbose && (resp_mode6 != mode6)) pr2ws(">>> msense(%d) but resp[0]=%d so switch response " "processing\n", (mode6 ? 6 : 10), buff[0]); calc_len = sg_msense_calc_length(buff, msense10_hlen, resp_mode6, NULL); if (calc_len > MODE_RESP_ARB_LEN) calc_len = MODE_RESP_ARB_LEN; offset = sg_mode_page_offset(buff, calc_len, resp_mode6, ebuff, EBUFF_SZ); if (offset < 0) { if (('\0' != ebuff[0]) && (verbose > 0)) pr2ws("%s: %s\n", __func__, ebuff); return SG_LIB_CAT_MALFORMED; } xfer_len = calc_len - offset; if (xfer_len > mx_mpage_len) xfer_len = mx_mpage_len; for (k = 0; k < 4; ++k) { if (NULL == pcontrol_arr[k]) continue; memset(pcontrol_arr[k], 0, mx_mpage_len); resid = 0; if (mode6) res = sg_ll_mode_sense6(sg_fd, dbd, k /* pc */, pg_code, sub_pg_code, buff, calc_len, true, verbose); else res = sg_ll_mode_sense10_v2(sg_fd, false /* llbaa */, dbd, k /* pc */, pg_code, sub_pg_code, buff, calc_len, 0, &resid, true, verbose); if (res || resid) { if (0 == first_err) { if (res) first_err = res; else { first_err = -49; /* unexpected resid != 0 */ if (verbose) pr2ws("%s: unexpected resid=%d, page=0x%x, " "pcontrol=%d\n", __func__, resid, pg_code, k); } } if (0 == k) break; /* if problem on current page, it won't improve */ else continue; } if (xfer_len > 0) memcpy(pcontrol_arr[k], buff + offset, xfer_len); if (success_mask) *success_mask |= (1 << k); } return first_err; }