"Fossies" - the Fresh Open Source Software Archive

Member "sdparm-1.12/src/sdparm_access.c" (22 Mar 2021, 15488 Bytes) of package /linux/misc/sdparm-1.12.tgz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file.

    1 /*
    2  * Copyright (c) 2005-2021, 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 are met:
    7  *
    8  * 1. Redistributions of source code must retain the above copyright notice,
    9  *    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  *
   14  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
   15  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   17  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
   18  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
   19  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
   20  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
   21  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
   22  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
   23  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
   24  * THE POSSIBILITY OF SUCH DAMAGE.
   25  */
   26 
   27 #include <stdio.h>
   28 #include <stdlib.h>
   29 #include <stdarg.h>
   30 #include <stdbool.h>
   31 #include <string.h>
   32 #include <errno.h>
   33 #include <ctype.h>
   34 #define __STDC_FORMAT_MACROS 1
   35 #include <inttypes.h>
   36 
   37 #ifdef HAVE_CONFIG_H
   38 #include "config.h"
   39 #endif
   40 
   41 #include "sdparm.h"
   42 #include "sg_lib.h"
   43 #include "sg_unaligned.h"
   44 #include "sg_pr2serr.h"
   45 
   46 /* sdparm_access.c : helpers for sdparm to access tables in
   47  * sdparm_data.c
   48  */
   49 
   50 /* Returns true if left argument is "equal" to the right argument. l_pdt_s
   51  * is a compound PDT (SCSI Peripheral Device Type) or a negative number
   52  * which represents a wildcard (i.e. match anything). r_pdt_s has a similar
   53  * form. PDT values are 5 bits long (0 to 31) and a compound pdt_s is
   54  * formed by shifting the second (upper) PDT by eight bits to the left and
   55  * OR-ing it with the first PDT. The pdt_s values must be defined so
   56  * PDT_DISK (0) is _not_ the upper value in a compound pdt_s. */
   57 bool
   58 pdt_s_eq(int l_pdt_s, int r_pdt_s)
   59 {
   60     bool upper_l = !!(l_pdt_s & PDT_UPPER_MASK);
   61     bool upper_r = !!(r_pdt_s & PDT_UPPER_MASK);
   62 
   63     if ((l_pdt_s < 0) || (r_pdt_s < 0))
   64         return true;
   65     if (!upper_l && !upper_r)
   66         return l_pdt_s == r_pdt_s;
   67     else if (upper_l && upper_r)
   68         return (((PDT_UPPER_MASK & l_pdt_s) == (PDT_UPPER_MASK & r_pdt_s)) ||
   69                 ((PDT_LOWER_MASK & l_pdt_s) == (PDT_LOWER_MASK & r_pdt_s)));
   70     else if (upper_l)
   71         return (((PDT_LOWER_MASK & l_pdt_s) == r_pdt_s) ||
   72                 ((PDT_UPPER_MASK & l_pdt_s) >> 8) == r_pdt_s);
   73     return (((PDT_LOWER_MASK & r_pdt_s) == l_pdt_s) ||
   74             ((PDT_UPPER_MASK & r_pdt_s) >> 8) == l_pdt_s);
   75 }
   76 
   77 /* Returns 1 if strings equal (same length, characters same or only differ
   78  * by case), else returns 0. Assumes 7 bit ASCII (English alphabet). */
   79 int
   80 sdp_strcase_eq(const char * s1p, const char * s2p)
   81 {
   82     int c1, c2;
   83 
   84     do {
   85         c1 = *s1p++;
   86         c2 = *s2p++;
   87         if (c1 != c2) {
   88             if (c2 >= 'a')
   89                 c2 = toupper(c2);
   90             else if (c1 >= 'a')
   91                 c1 = toupper(c1);
   92             else
   93                 return 0;
   94             if (c1 != c2)
   95                 return 0;
   96         }
   97     } while (c1);
   98     return 1;
   99 }
  100 
  101 /* Returns 1 if strings equal up to the nth character (characters same or only
  102  * differ by case), else returns 0. Assumes 7 bit ASCII (English alphabet). */
  103 int
  104 sdp_strcase_eq_upto(const char * s1p, const char * s2p, int n)
  105 {
  106     int k, c1, c2;
  107 
  108     for (k = 0; k < n; ++k) {
  109         c1 = *s1p++;
  110         c2 = *s2p++;
  111         if (c1 != c2) {
  112             if (c2 >= 'a')
  113                 c2 = toupper(c2);
  114             else if (c1 >= 'a')
  115                 c1 = toupper(c1);
  116             else
  117                 return 0;
  118             if (c1 != c2)
  119                 return 0;
  120         }
  121         if (0 == c1)
  122             break;
  123     }
  124     return 1;
  125 }
  126 
  127 
  128 /* Returns length of mode page. Assumes mp pointing at start of a mode
  129  * page (not the start of a MODE SENSE response). */
  130 int
  131 sdp_mpage_len(const uint8_t * mp)
  132 {
  133     /* if SPF (byte 0, bit 6) is set then 4 byte header, else 2 byte header */
  134     return (mp[0] & 0x40) ? (sg_get_unaligned_be16(mp + 2) + 4) : (mp[1] + 2);
  135 }
  136 
  137 const struct sdparm_mode_page_t *
  138 sdp_get_mpage_t(int page_num, int subpage_num, int pdt, int transp_proto,
  139                 int vendor_id)
  140 {
  141     const struct sdparm_mode_page_t * mpp;
  142 
  143     if (vendor_id >= 0) {
  144         const struct sdparm_vendor_pair * vpp;
  145 
  146         vpp = sdp_get_vendor_pair(vendor_id);
  147         mpp = (vpp ? vpp->mpage : NULL);
  148     } else if ((transp_proto >= 0) && (transp_proto < 16))
  149         mpp = sdparm_transport_mp[transp_proto].mpage;
  150     else
  151         mpp = sdparm_gen_mode_pg;
  152     if (NULL == mpp)
  153         return NULL;
  154 
  155     for ( ; mpp->acron; ++mpp) {
  156         if ((page_num == mpp->page) && (subpage_num == mpp->subpage)) {
  157             if ((pdt < 0) || (mpp->pdt_s < 0) || pdt_s_eq(mpp->pdt_s, pdt))
  158                 return mpp;
  159         }
  160     }
  161     return NULL;
  162 }
  163 
  164 const struct sdparm_mode_page_t *
  165 sdp_get_mpt_with_str(int page_num, int subpage_num, int pdt, int transp_proto,
  166                      int vendor_id, bool plus_acron, bool hex, int b_len,
  167                      char * bp)
  168 {
  169     int len = b_len - 1;
  170     const struct sdparm_mode_page_t * mpp = NULL;
  171     const char * cp;
  172 
  173     if (len < 0)
  174         return mpp;
  175     bp[len] = '\0';
  176     /* first try to match given pdt */
  177     mpp = sdp_get_mpage_t(page_num, subpage_num, pdt, transp_proto,
  178                           vendor_id);
  179     if (NULL == mpp) /* didn't match specific pdt so try -1 (ie. SPC) */
  180         mpp = sdp_get_mpage_t(page_num, subpage_num, -1, transp_proto,
  181                               vendor_id);
  182     if (mpp && mpp->name) {
  183         cp = mpp->acron;
  184         if (NULL == cp)
  185             cp = "";
  186         if (hex) {
  187             if (0 == subpage_num) {
  188                 if (plus_acron)
  189                     snprintf(bp, len, "%s [%s: 0x%x]", mpp->name, cp,
  190                              page_num);
  191                 else
  192                     snprintf(bp, len, "%s [0x%x]", mpp->name, page_num);
  193             } else {
  194                 if (plus_acron)
  195                     snprintf(bp, len, "%s [%s: 0x%x,0x%x]", mpp->name, cp,
  196                              page_num, subpage_num);
  197                 else
  198                     snprintf(bp, len, "%s [0x%x,0x%x]", mpp->name, page_num,
  199                              subpage_num);
  200             }
  201         } else {
  202             if (plus_acron)
  203                 snprintf(bp, len, "%s [%s]", mpp->name, cp);
  204             else
  205                 snprintf(bp, len, "%s", mpp->name);
  206         }
  207     } else {
  208         if (0 == subpage_num)
  209             snprintf(bp, len, "[0x%x]", page_num);
  210         else
  211             snprintf(bp, len, "[0x%x,0x%x]", page_num, subpage_num);
  212     }
  213     return mpp;
  214 }
  215 
  216 const struct sdparm_mode_page_t *
  217 sdp_find_mpt_by_acron(const char * ap, int transp_proto, int vendor_id)
  218 {
  219     const struct sdparm_mode_page_t * mpp;
  220 
  221     if (vendor_id >= 0) {
  222         const struct sdparm_vendor_pair * vpp;
  223 
  224         vpp = sdp_get_vendor_pair(vendor_id);
  225         mpp = (vpp ? vpp->mpage : NULL);
  226     } else if ((transp_proto >= 0) && (transp_proto < 16))
  227         mpp = sdparm_transport_mp[transp_proto].mpage;
  228     else
  229         mpp = sdparm_gen_mode_pg;
  230     if (NULL == mpp)
  231         return NULL;
  232 
  233     for ( ; mpp->acron; ++mpp) {
  234         if (sdp_strcase_eq(mpp->acron, ap))
  235             return mpp;
  236     }
  237     return NULL;
  238 }
  239 
  240 const struct sdparm_vpd_page_t *
  241 sdp_get_vpd_detail(int page_num, int subvalue, int pdt)
  242 {
  243     const struct sdparm_vpd_page_t * vpp;
  244     int sv, ty;
  245 
  246     sv = (subvalue < 0) ? 1 : 0;
  247     ty = (pdt < 0) ? 1 : 0;
  248     for (vpp = sdparm_vpd_pg; vpp->acron; ++vpp) {
  249         if ((page_num == vpp->vpd_num) &&
  250             (sv || (subvalue == vpp->subvalue)) &&
  251             (ty || (pdt == vpp->pdt_s)))
  252             return vpp;
  253     }
  254     if (! ty)
  255         return sdp_get_vpd_detail(page_num, subvalue, -1);
  256     if (! sv)
  257         return sdp_get_vpd_detail(page_num, -1, -1);
  258     return NULL;
  259 }
  260 
  261 const struct sdparm_vpd_page_t *
  262 sdp_find_vpd_by_acron(const char * ap)
  263 {
  264     const struct sdparm_vpd_page_t * vpp;
  265 
  266     for (vpp = sdparm_vpd_pg; vpp->acron; ++vpp) {
  267         if (sdp_strcase_eq(vpp->acron, ap))
  268             return vpp;
  269     }
  270     return NULL;
  271 }
  272 
  273 char *
  274 sdp_get_transport_name(int proto_num, int b_len, char * b)
  275 {
  276     char d[128];
  277 
  278     if (NULL == b)
  279         ;
  280     else if (b_len < 2) {
  281         if (1 == b_len)
  282             b[0] = '\0';
  283     } else
  284         snprintf(b, b_len, "%s", sg_get_trans_proto_str(proto_num, sizeof(d),
  285                  d));
  286     return b;
  287 }
  288 
  289 int
  290 sdp_find_transport_id_by_acron(const char * ap)
  291 {
  292     const struct sdparm_val_desc_t * t_vdp;
  293     const struct sdparm_val_desc_t * t_addp;
  294 
  295     for (t_vdp = sdparm_transport_id; t_vdp->desc; ++t_vdp) {
  296         if (sdp_strcase_eq(t_vdp->desc, ap))
  297             return t_vdp->val;
  298     }
  299     /* No match ... try additional transport acronyms */
  300     for (t_addp = sdparm_add_transport_acron; t_addp->desc; ++t_addp) {
  301         if (sdp_strcase_eq(t_addp->desc, ap))
  302             return t_addp->val;
  303     }
  304     return -1;
  305 }
  306 
  307 const char *
  308 sdp_get_vendor_name(int vendor_id)
  309 {
  310     const struct sdparm_vendor_name_t * vnp;
  311 
  312     for (vnp = sdparm_vendor_id; vnp->acron; ++vnp) {
  313         if (vendor_id == vnp->vendor_id)
  314             return vnp->name;
  315     }
  316     return NULL;
  317 }
  318 
  319 const struct sdparm_vendor_name_t *
  320 sdp_find_vendor_by_acron(const char * ap)
  321 {
  322     const struct sdparm_vendor_name_t * vnp;
  323 
  324     for (vnp = sdparm_vendor_id; vnp->acron; ++vnp) {
  325         if (sdp_strcase_eq_upto(vnp->acron, ap, strlen(vnp->acron)))
  326             return vnp;
  327     }
  328     return NULL;
  329 }
  330 
  331 const struct sdparm_vendor_pair *
  332 sdp_get_vendor_pair(int vendor_id)
  333 {
  334      return ((vendor_id >= 0) && (vendor_id < sdparm_vendor_mp_len))
  335             ? (sdparm_vendor_mp + vendor_id) : NULL;
  336 }
  337 
  338 /* Searches mpage items table from (and including) the current position
  339  * looking for the first match on 'ap' (pointer to acromym). Checks
  340  * against the inbuilt table (in sdparm_data.c) of generic (when both
  341  * transp_proto and vendor_id are -1), transport (when transp_proto is
  342  * >= 0) or vendor (when vendor_id is >= 0) mode page items (fields).
  343  * If found a pointer to that mitem is returned and *from_p is set to
  344  * the offset after the match. If not found then NULL is returned and
  345  * *from_p is set to the offset of the sentinel at the end of the
  346  * selected mitem array. Start iteration by setting from_p to NULL or
  347  * point it at -1. */
  348 const struct sdparm_mode_page_item *
  349 sdp_find_mitem_by_acron(const char * ap, int * from_p, int transp_proto,
  350                         int vendor_id)
  351 {
  352     int k = 0;
  353     const struct sdparm_mode_page_item * mpi;
  354 
  355     if (from_p) {
  356         k = *from_p;
  357         if (k < 0)
  358             k = 0;
  359     }
  360     if (vendor_id >= 0) {
  361         const struct sdparm_vendor_pair * vpp;
  362 
  363         vpp = sdp_get_vendor_pair(vendor_id);
  364         mpi = (vpp ? vpp->mitem : NULL);
  365     } else if ((transp_proto >= 0) && (transp_proto < 16))
  366         mpi = sdparm_transport_mp[transp_proto].mitem;
  367     else
  368         mpi = sdparm_mitem_arr;
  369     if (NULL == mpi)
  370         return NULL;
  371 
  372     for (mpi += k; mpi->acron; ++k, ++mpi) {
  373         if (sdp_strcase_eq(mpi->acron, ap))
  374             break;
  375     }
  376     if (NULL == mpi->acron)
  377         mpi = NULL;
  378     if (from_p)
  379         *from_p = (mpi ? (k + 1) : k);
  380     return mpi;
  381 }
  382 
  383 uint64_t
  384 sdp_mitem_get_value(const struct sdparm_mode_page_item *mpi,
  385                     const uint8_t * mp)
  386 {
  387     return sg_get_big_endian(mp + mpi->start_byte, mpi->start_bit,
  388                               mpi->num_bits);
  389 }
  390 
  391 /* Gets a mode page item's value given a pointer to the mode page response
  392  * (mp). If all_setp is non-NULL then checks 8, 16, 24, 32, 48 and 64 bit
  393  * quantities for all bits set (e.g. for 8 bits that would be 0xff) and if
  394  * so sets the bool addressed by all_setp to true. Otherwise if all_setp
  395  * is non-NULL then it sets the bool addressed by all_setp to false.
  396  * Returns the value in an unsigned 64 bit integer. To print a value as a
  397  * signed quantity use sdp_print_signed_decimal(). */
  398 uint64_t
  399 sdp_mitem_get_value_check(const struct sdparm_mode_page_item *mpi,
  400                           const uint8_t * mp, bool * all_setp)
  401 {
  402     uint64_t res;
  403 
  404     res = sg_get_big_endian(mp + mpi->start_byte, mpi->start_bit,
  405                              mpi->num_bits);
  406     if (all_setp) {
  407         switch (mpi->num_bits) {
  408         case 8:
  409             if (0xff == res)
  410                 *all_setp = true;
  411             break;
  412         case 16:
  413             if (0xffff == res)
  414                 *all_setp = true;
  415             break;
  416         case 24:
  417             if (0xffffff == res)
  418                 *all_setp = true;
  419             break;
  420         case 32:
  421             if (0xffffffff == res)
  422                 *all_setp = true;
  423             break;
  424         case 48:
  425             if (0xffffffffffffULL == res)
  426                 *all_setp = true;
  427             break;
  428         case 64:
  429             if (0xffffffffffffffffULL == res)
  430                 *all_setp = true;
  431             break;
  432         default:
  433             *all_setp = false;
  434             break;
  435         }
  436     }
  437     return res;
  438 }
  439 
  440 void
  441 sdp_print_signed_decimal(uint64_t u, int num_bits, bool leading_zeros)
  442 {
  443     unsigned int ui;
  444     uint8_t uc;
  445 
  446     switch (num_bits) {
  447     /* could support other num_bits, as required */
  448     case 4:     /* -8 to 7 */
  449         uc = u & 0xf;
  450         if (0x8 & uc)
  451             uc |= 0xf0;         /* sign extend */
  452         if (leading_zeros)
  453             printf("%02hhd", (signed char)uc);
  454         else
  455             printf("%hhd", (signed char)uc);
  456         break;
  457     case 8:     /* -128 to 127 */
  458         if (leading_zeros)
  459             printf("%02hhd", (signed char)u);
  460         else
  461             printf("%hhd", (signed char)u);
  462         break;
  463     case 16:    /* -32768 to 32767 */
  464         if (leading_zeros)
  465             printf("%02hd", (short int)u);
  466         else
  467             printf("%hd", (short int)u);
  468         break;
  469     case 24:
  470         ui = 0xffffff & u;
  471         if (0x800000 & ui)
  472             ui |= 0xff000000;
  473         if (leading_zeros)
  474             printf("%02d", (int)ui);
  475         else
  476             printf("%d", (int)ui);
  477         break;
  478     case 32:
  479         if (leading_zeros)
  480             printf("%02d", (int)u);
  481         else
  482             printf("%d", (int)u);
  483         break;
  484     case 64:
  485     default:
  486         if (leading_zeros)
  487             printf("%02" PRId64 , (int64_t)u);
  488         else
  489             printf("%" PRId64 , (int64_t)u);
  490         break;
  491     }
  492 }
  493 
  494 /* Place 'val' at an offset to 'mp' as indicated by mpi. */
  495 void
  496 sdp_mitem_set_value(uint64_t val, const struct sdparm_mode_page_item * mpi,
  497                     uint8_t * mp)
  498 {
  499     sg_set_big_endian(val, mp + mpi->start_byte, mpi->start_bit,
  500                       mpi->num_bits);
  501 }
  502 
  503 char *
  504 sdp_get_ansi_version_str(int version, int buff_len, char * buff)
  505 {
  506     version &= 0x7;
  507     buff[buff_len - 1] = '\0';
  508     strncpy(buff, sdparm_ansi_version_arr[version], buff_len - 1);
  509     return buff;
  510 }
  511 
  512 int
  513 sdp_get_desc_id(int flags)
  514 {
  515     return (MF_DESC_ID_MASK & flags) >> MF_DESC_ID_SHIFT;
  516 }