linux/drivers/scsi/scsi_common.c
<<
>>
Prefs
   1/*
   2 * SCSI functions used by both the initiator and the target code.
   3 */
   4
   5#include <linux/bug.h>
   6#include <linux/kernel.h>
   7#include <linux/string.h>
   8#include <linux/errno.h>
   9#include <asm/unaligned.h>
  10#include <scsi/scsi_common.h>
  11
  12/* NB: These are exposed through /proc/scsi/scsi and form part of the ABI.
  13 * You may not alter any existing entry (although adding new ones is
  14 * encouraged once assigned by ANSI/INCITS T10
  15 */
  16static const char *const scsi_device_types[] = {
  17        "Direct-Access    ",
  18        "Sequential-Access",
  19        "Printer          ",
  20        "Processor        ",
  21        "WORM             ",
  22        "CD-ROM           ",
  23        "Scanner          ",
  24        "Optical Device   ",
  25        "Medium Changer   ",
  26        "Communications   ",
  27        "ASC IT8          ",
  28        "ASC IT8          ",
  29        "RAID             ",
  30        "Enclosure        ",
  31        "Direct-Access-RBC",
  32        "Optical card     ",
  33        "Bridge controller",
  34        "Object storage   ",
  35        "Automation/Drive ",
  36        "Security Manager ",
  37        "Direct-Access-ZBC",
  38};
  39
  40/**
  41 * scsi_device_type - Return 17 char string indicating device type.
  42 * @type: type number to look up
  43 */
  44const char *scsi_device_type(unsigned type)
  45{
  46        if (type == 0x1e)
  47                return "Well-known LUN   ";
  48        if (type == 0x1f)
  49                return "No Device        ";
  50        if (type >= ARRAY_SIZE(scsi_device_types))
  51                return "Unknown          ";
  52        return scsi_device_types[type];
  53}
  54EXPORT_SYMBOL(scsi_device_type);
  55
  56/**
  57 * scsilun_to_int - convert a scsi_lun to an int
  58 * @scsilun:    struct scsi_lun to be converted.
  59 *
  60 * Description:
  61 *     Convert @scsilun from a struct scsi_lun to a four byte host byte-ordered
  62 *     integer, and return the result. The caller must check for
  63 *     truncation before using this function.
  64 *
  65 * Notes:
  66 *     For a description of the LUN format, post SCSI-3 see the SCSI
  67 *     Architecture Model, for SCSI-3 see the SCSI Controller Commands.
  68 *
  69 *     Given a struct scsi_lun of: d2 04 0b 03 00 00 00 00, this function
  70 *     returns the integer: 0x0b03d204
  71 *
  72 *     This encoding will return a standard integer LUN for LUNs smaller
  73 *     than 256, which typically use a single level LUN structure with
  74 *     addressing method 0.
  75 */
  76u64 scsilun_to_int(struct scsi_lun *scsilun)
  77{
  78        int i;
  79        u64 lun;
  80
  81        lun = 0;
  82        for (i = 0; i < sizeof(lun); i += 2)
  83                lun = lun | (((u64)scsilun->scsi_lun[i] << ((i + 1) * 8)) |
  84                             ((u64)scsilun->scsi_lun[i + 1] << (i * 8)));
  85        return lun;
  86}
  87EXPORT_SYMBOL(scsilun_to_int);
  88
  89/**
  90 * int_to_scsilun - reverts an int into a scsi_lun
  91 * @lun:        integer to be reverted
  92 * @scsilun:    struct scsi_lun to be set.
  93 *
  94 * Description:
  95 *     Reverts the functionality of the scsilun_to_int, which packed
  96 *     an 8-byte lun value into an int. This routine unpacks the int
  97 *     back into the lun value.
  98 *
  99 * Notes:
 100 *     Given an integer : 0x0b03d204,  this function returns a
 101 *     struct scsi_lun of: d2 04 0b 03 00 00 00 00
 102 *
 103 */
 104void int_to_scsilun(u64 lun, struct scsi_lun *scsilun)
 105{
 106        int i;
 107
 108        memset(scsilun->scsi_lun, 0, sizeof(scsilun->scsi_lun));
 109
 110        for (i = 0; i < sizeof(lun); i += 2) {
 111                scsilun->scsi_lun[i] = (lun >> 8) & 0xFF;
 112                scsilun->scsi_lun[i+1] = lun & 0xFF;
 113                lun = lun >> 16;
 114        }
 115}
 116EXPORT_SYMBOL(int_to_scsilun);
 117
 118/**
 119 * scsi_normalize_sense - normalize main elements from either fixed or
 120 *                      descriptor sense data format into a common format.
 121 *
 122 * @sense_buffer:       byte array containing sense data returned by device
 123 * @sb_len:             number of valid bytes in sense_buffer
 124 * @sshdr:              pointer to instance of structure that common
 125 *                      elements are written to.
 126 *
 127 * Notes:
 128 *      The "main elements" from sense data are: response_code, sense_key,
 129 *      asc, ascq and additional_length (only for descriptor format).
 130 *
 131 *      Typically this function can be called after a device has
 132 *      responded to a SCSI command with the CHECK_CONDITION status.
 133 *
 134 * Return value:
 135 *      true if valid sense data information found, else false;
 136 */
 137bool scsi_normalize_sense(const u8 *sense_buffer, int sb_len,
 138                          struct scsi_sense_hdr *sshdr)
 139{
 140        if (!sense_buffer || !sb_len)
 141                return false;
 142
 143        memset(sshdr, 0, sizeof(struct scsi_sense_hdr));
 144
 145        sshdr->response_code = (sense_buffer[0] & 0x7f);
 146
 147        if (!scsi_sense_valid(sshdr))
 148                return false;
 149
 150        if (sshdr->response_code >= 0x72) {
 151                /*
 152                 * descriptor format
 153                 */
 154                if (sb_len > 1)
 155                        sshdr->sense_key = (sense_buffer[1] & 0xf);
 156                if (sb_len > 2)
 157                        sshdr->asc = sense_buffer[2];
 158                if (sb_len > 3)
 159                        sshdr->ascq = sense_buffer[3];
 160                if (sb_len > 7)
 161                        sshdr->additional_length = sense_buffer[7];
 162        } else {
 163                /*
 164                 * fixed format
 165                 */
 166                if (sb_len > 2)
 167                        sshdr->sense_key = (sense_buffer[2] & 0xf);
 168                if (sb_len > 7) {
 169                        sb_len = (sb_len < (sense_buffer[7] + 8)) ?
 170                                         sb_len : (sense_buffer[7] + 8);
 171                        if (sb_len > 12)
 172                                sshdr->asc = sense_buffer[12];
 173                        if (sb_len > 13)
 174                                sshdr->ascq = sense_buffer[13];
 175                }
 176        }
 177
 178        return true;
 179}
 180EXPORT_SYMBOL(scsi_normalize_sense);
 181
 182/**
 183 * scsi_sense_desc_find - search for a given descriptor type in descriptor sense data format.
 184 * @sense_buffer:       byte array of descriptor format sense data
 185 * @sb_len:             number of valid bytes in sense_buffer
 186 * @desc_type:          value of descriptor type to find
 187 *                      (e.g. 0 -> information)
 188 *
 189 * Notes:
 190 *      only valid when sense data is in descriptor format
 191 *
 192 * Return value:
 193 *      pointer to start of (first) descriptor if found else NULL
 194 */
 195const u8 * scsi_sense_desc_find(const u8 * sense_buffer, int sb_len,
 196                                int desc_type)
 197{
 198        int add_sen_len, add_len, desc_len, k;
 199        const u8 * descp;
 200
 201        if ((sb_len < 8) || (0 == (add_sen_len = sense_buffer[7])))
 202                return NULL;
 203        if ((sense_buffer[0] < 0x72) || (sense_buffer[0] > 0x73))
 204                return NULL;
 205        add_sen_len = (add_sen_len < (sb_len - 8)) ?
 206                        add_sen_len : (sb_len - 8);
 207        descp = &sense_buffer[8];
 208        for (desc_len = 0, k = 0; k < add_sen_len; k += desc_len) {
 209                descp += desc_len;
 210                add_len = (k < (add_sen_len - 1)) ? descp[1]: -1;
 211                desc_len = add_len + 2;
 212                if (descp[0] == desc_type)
 213                        return descp;
 214                if (add_len < 0) // short descriptor ??
 215                        break;
 216        }
 217        return NULL;
 218}
 219EXPORT_SYMBOL(scsi_sense_desc_find);
 220
 221/**
 222 * scsi_build_sense_buffer - build sense data in a buffer
 223 * @desc:       Sense format (non zero == descriptor format,
 224 *              0 == fixed format)
 225 * @buf:        Where to build sense data
 226 * @key:        Sense key
 227 * @asc:        Additional sense code
 228 * @ascq:       Additional sense code qualifier
 229 *
 230 **/
 231void scsi_build_sense_buffer(int desc, u8 *buf, u8 key, u8 asc, u8 ascq)
 232{
 233        if (desc) {
 234                buf[0] = 0x72;  /* descriptor, current */
 235                buf[1] = key;
 236                buf[2] = asc;
 237                buf[3] = ascq;
 238                buf[7] = 0;
 239        } else {
 240                buf[0] = 0x70;  /* fixed, current */
 241                buf[2] = key;
 242                buf[7] = 0xa;
 243                buf[12] = asc;
 244                buf[13] = ascq;
 245        }
 246}
 247EXPORT_SYMBOL(scsi_build_sense_buffer);
 248
 249/**
 250 * scsi_set_sense_information - set the information field in a
 251 *              formatted sense data buffer
 252 * @buf:        Where to build sense data
 253 * @buf_len:    buffer length
 254 * @info:       64-bit information value to be set
 255 *
 256 * Return value:
 257 *      0 on success or EINVAL for invalid sense buffer length
 258 **/
 259int scsi_set_sense_information(u8 *buf, int buf_len, u64 info)
 260{
 261        if ((buf[0] & 0x7f) == 0x72) {
 262                u8 *ucp, len;
 263
 264                len = buf[7];
 265                ucp = (char *)scsi_sense_desc_find(buf, len + 8, 0);
 266                if (!ucp) {
 267                        buf[7] = len + 0xc;
 268                        ucp = buf + 8 + len;
 269                }
 270
 271                if (buf_len < len + 0xc)
 272                        /* Not enough room for info */
 273                        return -EINVAL;
 274
 275                ucp[0] = 0;
 276                ucp[1] = 0xa;
 277                ucp[2] = 0x80; /* Valid bit */
 278                ucp[3] = 0;
 279                put_unaligned_be64(info, &ucp[4]);
 280        } else if ((buf[0] & 0x7f) == 0x70) {
 281                buf[0] |= 0x80;
 282                put_unaligned_be64(info, &buf[3]);
 283        }
 284
 285        return 0;
 286}
 287EXPORT_SYMBOL(scsi_set_sense_information);
 288