xref: /openbmc/linux/drivers/scsi/scsi_common.c (revision 416dace6)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
207e38420SBart Van Assche /*
307e38420SBart Van Assche  * SCSI functions used by both the initiator and the target code.
407e38420SBart Van Assche  */
507e38420SBart Van Assche 
607e38420SBart Van Assche #include <linux/bug.h>
707e38420SBart Van Assche #include <linux/kernel.h>
807e38420SBart Van Assche #include <linux/string.h>
9f5a8b3a7SSagi Grimberg #include <linux/errno.h>
1033ff4ce4SChristoph Hellwig #include <linux/module.h>
110730b163SMike Christie #include <uapi/linux/pr.h>
127708c165SSagi Grimberg #include <asm/unaligned.h>
1307e38420SBart Van Assche #include <scsi/scsi_common.h>
1407e38420SBart Van Assche 
1533ff4ce4SChristoph Hellwig MODULE_LICENSE("GPL v2");
1633ff4ce4SChristoph Hellwig 
17b69367dfSChristoph Hellwig /* Command group 3 is reserved and should never be used.  */
18b69367dfSChristoph Hellwig const unsigned char scsi_command_size_tbl[8] = {
19b69367dfSChristoph Hellwig 	6, 10, 10, 12, 16, 12, 10, 10
20b69367dfSChristoph Hellwig };
21b69367dfSChristoph Hellwig EXPORT_SYMBOL(scsi_command_size_tbl);
22b69367dfSChristoph Hellwig 
2307e38420SBart Van Assche /* NB: These are exposed through /proc/scsi/scsi and form part of the ABI.
2407e38420SBart Van Assche  * You may not alter any existing entry (although adding new ones is
25749a1122SRandy Dunlap  * encouraged once assigned by ANSI/INCITS T10).
2607e38420SBart Van Assche  */
2707e38420SBart Van Assche static const char *const scsi_device_types[] = {
2807e38420SBart Van Assche 	"Direct-Access    ",
2907e38420SBart Van Assche 	"Sequential-Access",
3007e38420SBart Van Assche 	"Printer          ",
3107e38420SBart Van Assche 	"Processor        ",
3207e38420SBart Van Assche 	"WORM             ",
3307e38420SBart Van Assche 	"CD-ROM           ",
3407e38420SBart Van Assche 	"Scanner          ",
3507e38420SBart Van Assche 	"Optical Device   ",
3607e38420SBart Van Assche 	"Medium Changer   ",
3707e38420SBart Van Assche 	"Communications   ",
3807e38420SBart Van Assche 	"ASC IT8          ",
3907e38420SBart Van Assche 	"ASC IT8          ",
4007e38420SBart Van Assche 	"RAID             ",
4107e38420SBart Van Assche 	"Enclosure        ",
4207e38420SBart Van Assche 	"Direct-Access-RBC",
4307e38420SBart Van Assche 	"Optical card     ",
4407e38420SBart Van Assche 	"Bridge controller",
4507e38420SBart Van Assche 	"Object storage   ",
4607e38420SBart Van Assche 	"Automation/Drive ",
4707e38420SBart Van Assche 	"Security Manager ",
4807e38420SBart Van Assche 	"Direct-Access-ZBC",
4907e38420SBart Van Assche };
5007e38420SBart Van Assche 
5107e38420SBart Van Assche /**
52749a1122SRandy Dunlap  * scsi_device_type - Return 17-char string indicating device type.
5307e38420SBart Van Assche  * @type: type number to look up
5407e38420SBart Van Assche  */
scsi_device_type(unsigned type)5507e38420SBart Van Assche const char *scsi_device_type(unsigned type)
5607e38420SBart Van Assche {
5707e38420SBart Van Assche 	if (type == 0x1e)
5807e38420SBart Van Assche 		return "Well-known LUN   ";
5907e38420SBart Van Assche 	if (type == 0x1f)
6007e38420SBart Van Assche 		return "No Device        ";
6107e38420SBart Van Assche 	if (type >= ARRAY_SIZE(scsi_device_types))
6207e38420SBart Van Assche 		return "Unknown          ";
6307e38420SBart Van Assche 	return scsi_device_types[type];
6407e38420SBart Van Assche }
6507e38420SBart Van Assche EXPORT_SYMBOL(scsi_device_type);
6607e38420SBart Van Assche 
scsi_pr_type_to_block(enum scsi_pr_type type)670af7b5e2SMike Christie enum pr_type scsi_pr_type_to_block(enum scsi_pr_type type)
680af7b5e2SMike Christie {
690af7b5e2SMike Christie 	switch (type) {
700af7b5e2SMike Christie 	case SCSI_PR_WRITE_EXCLUSIVE:
710af7b5e2SMike Christie 		return PR_WRITE_EXCLUSIVE;
720af7b5e2SMike Christie 	case SCSI_PR_EXCLUSIVE_ACCESS:
730af7b5e2SMike Christie 		return PR_EXCLUSIVE_ACCESS;
740af7b5e2SMike Christie 	case SCSI_PR_WRITE_EXCLUSIVE_REG_ONLY:
750af7b5e2SMike Christie 		return PR_WRITE_EXCLUSIVE_REG_ONLY;
760af7b5e2SMike Christie 	case SCSI_PR_EXCLUSIVE_ACCESS_REG_ONLY:
770af7b5e2SMike Christie 		return PR_EXCLUSIVE_ACCESS_REG_ONLY;
780af7b5e2SMike Christie 	case SCSI_PR_WRITE_EXCLUSIVE_ALL_REGS:
790af7b5e2SMike Christie 		return PR_WRITE_EXCLUSIVE_ALL_REGS;
800af7b5e2SMike Christie 	case SCSI_PR_EXCLUSIVE_ACCESS_ALL_REGS:
810af7b5e2SMike Christie 		return PR_EXCLUSIVE_ACCESS_ALL_REGS;
820af7b5e2SMike Christie 	}
830af7b5e2SMike Christie 
840af7b5e2SMike Christie 	return 0;
850af7b5e2SMike Christie }
860af7b5e2SMike Christie EXPORT_SYMBOL_GPL(scsi_pr_type_to_block);
870af7b5e2SMike Christie 
block_pr_type_to_scsi(enum pr_type type)880730b163SMike Christie enum scsi_pr_type block_pr_type_to_scsi(enum pr_type type)
890730b163SMike Christie {
900730b163SMike Christie 	switch (type) {
910730b163SMike Christie 	case PR_WRITE_EXCLUSIVE:
920730b163SMike Christie 		return SCSI_PR_WRITE_EXCLUSIVE;
930730b163SMike Christie 	case PR_EXCLUSIVE_ACCESS:
940730b163SMike Christie 		return SCSI_PR_EXCLUSIVE_ACCESS;
950730b163SMike Christie 	case PR_WRITE_EXCLUSIVE_REG_ONLY:
960730b163SMike Christie 		return SCSI_PR_WRITE_EXCLUSIVE_REG_ONLY;
970730b163SMike Christie 	case PR_EXCLUSIVE_ACCESS_REG_ONLY:
980730b163SMike Christie 		return SCSI_PR_EXCLUSIVE_ACCESS_REG_ONLY;
990730b163SMike Christie 	case PR_WRITE_EXCLUSIVE_ALL_REGS:
1000730b163SMike Christie 		return SCSI_PR_WRITE_EXCLUSIVE_ALL_REGS;
1010730b163SMike Christie 	case PR_EXCLUSIVE_ACCESS_ALL_REGS:
1020730b163SMike Christie 		return SCSI_PR_EXCLUSIVE_ACCESS_ALL_REGS;
1030730b163SMike Christie 	}
1040730b163SMike Christie 
1050730b163SMike Christie 	return 0;
1060730b163SMike Christie }
1070730b163SMike Christie EXPORT_SYMBOL_GPL(block_pr_type_to_scsi);
1080730b163SMike Christie 
10907e38420SBart Van Assche /**
11007e38420SBart Van Assche  * scsilun_to_int - convert a scsi_lun to an int
11107e38420SBart Van Assche  * @scsilun:	struct scsi_lun to be converted.
11207e38420SBart Van Assche  *
11307e38420SBart Van Assche  * Description:
114749a1122SRandy Dunlap  *     Convert @scsilun from a struct scsi_lun to a four-byte host byte-ordered
11507e38420SBart Van Assche  *     integer, and return the result. The caller must check for
11607e38420SBart Van Assche  *     truncation before using this function.
11707e38420SBart Van Assche  *
11807e38420SBart Van Assche  * Notes:
11907e38420SBart Van Assche  *     For a description of the LUN format, post SCSI-3 see the SCSI
12007e38420SBart Van Assche  *     Architecture Model, for SCSI-3 see the SCSI Controller Commands.
12107e38420SBart Van Assche  *
12207e38420SBart Van Assche  *     Given a struct scsi_lun of: d2 04 0b 03 00 00 00 00, this function
12307e38420SBart Van Assche  *     returns the integer: 0x0b03d204
12407e38420SBart Van Assche  *
12507e38420SBart Van Assche  *     This encoding will return a standard integer LUN for LUNs smaller
12607e38420SBart Van Assche  *     than 256, which typically use a single level LUN structure with
12707e38420SBart Van Assche  *     addressing method 0.
12807e38420SBart Van Assche  */
scsilun_to_int(struct scsi_lun * scsilun)12907e38420SBart Van Assche u64 scsilun_to_int(struct scsi_lun *scsilun)
13007e38420SBart Van Assche {
13107e38420SBart Van Assche 	int i;
13207e38420SBart Van Assche 	u64 lun;
13307e38420SBart Van Assche 
13407e38420SBart Van Assche 	lun = 0;
13507e38420SBart Van Assche 	for (i = 0; i < sizeof(lun); i += 2)
13607e38420SBart Van Assche 		lun = lun | (((u64)scsilun->scsi_lun[i] << ((i + 1) * 8)) |
13707e38420SBart Van Assche 			     ((u64)scsilun->scsi_lun[i + 1] << (i * 8)));
13807e38420SBart Van Assche 	return lun;
13907e38420SBart Van Assche }
14007e38420SBart Van Assche EXPORT_SYMBOL(scsilun_to_int);
14107e38420SBart Van Assche 
14207e38420SBart Van Assche /**
14307e38420SBart Van Assche  * int_to_scsilun - reverts an int into a scsi_lun
14407e38420SBart Van Assche  * @lun:        integer to be reverted
14507e38420SBart Van Assche  * @scsilun:	struct scsi_lun to be set.
14607e38420SBart Van Assche  *
14707e38420SBart Van Assche  * Description:
14807e38420SBart Van Assche  *     Reverts the functionality of the scsilun_to_int, which packed
14907e38420SBart Van Assche  *     an 8-byte lun value into an int. This routine unpacks the int
15007e38420SBart Van Assche  *     back into the lun value.
15107e38420SBart Van Assche  *
15207e38420SBart Van Assche  * Notes:
15307e38420SBart Van Assche  *     Given an integer : 0x0b03d204, this function returns a
15407e38420SBart Van Assche  *     struct scsi_lun of: d2 04 0b 03 00 00 00 00
15507e38420SBart Van Assche  *
15607e38420SBart Van Assche  */
int_to_scsilun(u64 lun,struct scsi_lun * scsilun)15707e38420SBart Van Assche void int_to_scsilun(u64 lun, struct scsi_lun *scsilun)
15807e38420SBart Van Assche {
15907e38420SBart Van Assche 	int i;
16007e38420SBart Van Assche 
16107e38420SBart Van Assche 	memset(scsilun->scsi_lun, 0, sizeof(scsilun->scsi_lun));
16207e38420SBart Van Assche 
16307e38420SBart Van Assche 	for (i = 0; i < sizeof(lun); i += 2) {
16407e38420SBart Van Assche 		scsilun->scsi_lun[i] = (lun >> 8) & 0xFF;
16507e38420SBart Van Assche 		scsilun->scsi_lun[i+1] = lun & 0xFF;
16607e38420SBart Van Assche 		lun = lun >> 16;
16707e38420SBart Van Assche 	}
16807e38420SBart Van Assche }
16907e38420SBart Van Assche EXPORT_SYMBOL(int_to_scsilun);
17007e38420SBart Van Assche 
17107e38420SBart Van Assche /**
17207e38420SBart Van Assche  * scsi_normalize_sense - normalize main elements from either fixed or
17307e38420SBart Van Assche  *			descriptor sense data format into a common format.
17407e38420SBart Van Assche  *
17507e38420SBart Van Assche  * @sense_buffer:	byte array containing sense data returned by device
17607e38420SBart Van Assche  * @sb_len:		number of valid bytes in sense_buffer
17707e38420SBart Van Assche  * @sshdr:		pointer to instance of structure that common
17807e38420SBart Van Assche  *			elements are written to.
17907e38420SBart Van Assche  *
18007e38420SBart Van Assche  * Notes:
18107e38420SBart Van Assche  *	The "main elements" from sense data are: response_code, sense_key,
18207e38420SBart Van Assche  *	asc, ascq and additional_length (only for descriptor format).
18307e38420SBart Van Assche  *
18407e38420SBart Van Assche  *	Typically this function can be called after a device has
18507e38420SBart Van Assche  *	responded to a SCSI command with the CHECK_CONDITION status.
18607e38420SBart Van Assche  *
18707e38420SBart Van Assche  * Return value:
18807e38420SBart Van Assche  *	true if valid sense data information found, else false;
18907e38420SBart Van Assche  */
scsi_normalize_sense(const u8 * sense_buffer,int sb_len,struct scsi_sense_hdr * sshdr)19007e38420SBart Van Assche bool scsi_normalize_sense(const u8 *sense_buffer, int sb_len,
19107e38420SBart Van Assche 			  struct scsi_sense_hdr *sshdr)
19207e38420SBart Van Assche {
193148cff67SChristoph Hellwig 	memset(sshdr, 0, sizeof(struct scsi_sense_hdr));
194148cff67SChristoph Hellwig 
19507e38420SBart Van Assche 	if (!sense_buffer || !sb_len)
19607e38420SBart Van Assche 		return false;
19707e38420SBart Van Assche 
19807e38420SBart Van Assche 	sshdr->response_code = (sense_buffer[0] & 0x7f);
19907e38420SBart Van Assche 
20007e38420SBart Van Assche 	if (!scsi_sense_valid(sshdr))
20107e38420SBart Van Assche 		return false;
20207e38420SBart Van Assche 
20307e38420SBart Van Assche 	if (sshdr->response_code >= 0x72) {
20407e38420SBart Van Assche 		/*
20507e38420SBart Van Assche 		 * descriptor format
20607e38420SBart Van Assche 		 */
20707e38420SBart Van Assche 		if (sb_len > 1)
20807e38420SBart Van Assche 			sshdr->sense_key = (sense_buffer[1] & 0xf);
20907e38420SBart Van Assche 		if (sb_len > 2)
21007e38420SBart Van Assche 			sshdr->asc = sense_buffer[2];
21107e38420SBart Van Assche 		if (sb_len > 3)
21207e38420SBart Van Assche 			sshdr->ascq = sense_buffer[3];
21307e38420SBart Van Assche 		if (sb_len > 7)
21407e38420SBart Van Assche 			sshdr->additional_length = sense_buffer[7];
21507e38420SBart Van Assche 	} else {
21607e38420SBart Van Assche 		/*
21707e38420SBart Van Assche 		 * fixed format
21807e38420SBart Van Assche 		 */
21907e38420SBart Van Assche 		if (sb_len > 2)
22007e38420SBart Van Assche 			sshdr->sense_key = (sense_buffer[2] & 0xf);
22107e38420SBart Van Assche 		if (sb_len > 7) {
222*416dace6SBart Van Assche 			sb_len = min(sb_len, sense_buffer[7] + 8);
22307e38420SBart Van Assche 			if (sb_len > 12)
22407e38420SBart Van Assche 				sshdr->asc = sense_buffer[12];
22507e38420SBart Van Assche 			if (sb_len > 13)
22607e38420SBart Van Assche 				sshdr->ascq = sense_buffer[13];
22707e38420SBart Van Assche 		}
22807e38420SBart Van Assche 	}
22907e38420SBart Van Assche 
23007e38420SBart Van Assche 	return true;
23107e38420SBart Van Assche }
23207e38420SBart Van Assche EXPORT_SYMBOL(scsi_normalize_sense);
2337708c165SSagi Grimberg 
2347708c165SSagi Grimberg /**
2357708c165SSagi Grimberg  * scsi_sense_desc_find - search for a given descriptor type in	descriptor sense data format.
2367708c165SSagi Grimberg  * @sense_buffer:	byte array of descriptor format sense data
2377708c165SSagi Grimberg  * @sb_len:		number of valid bytes in sense_buffer
2387708c165SSagi Grimberg  * @desc_type:		value of descriptor type to find
2397708c165SSagi Grimberg  *			(e.g. 0 -> information)
2407708c165SSagi Grimberg  *
2417708c165SSagi Grimberg  * Notes:
2427708c165SSagi Grimberg  *	only valid when sense data is in descriptor format
2437708c165SSagi Grimberg  *
2447708c165SSagi Grimberg  * Return value:
2457708c165SSagi Grimberg  *	pointer to start of (first) descriptor if found else NULL
2467708c165SSagi Grimberg  */
scsi_sense_desc_find(const u8 * sense_buffer,int sb_len,int desc_type)2477708c165SSagi Grimberg const u8 * scsi_sense_desc_find(const u8 * sense_buffer, int sb_len,
2487708c165SSagi Grimberg 				int desc_type)
2497708c165SSagi Grimberg {
2507708c165SSagi Grimberg 	int add_sen_len, add_len, desc_len, k;
2517708c165SSagi Grimberg 	const u8 * descp;
2527708c165SSagi Grimberg 
2537708c165SSagi Grimberg 	if ((sb_len < 8) || (0 == (add_sen_len = sense_buffer[7])))
2547708c165SSagi Grimberg 		return NULL;
2557708c165SSagi Grimberg 	if ((sense_buffer[0] < 0x72) || (sense_buffer[0] > 0x73))
2567708c165SSagi Grimberg 		return NULL;
2577708c165SSagi Grimberg 	add_sen_len = (add_sen_len < (sb_len - 8)) ?
2587708c165SSagi Grimberg 			add_sen_len : (sb_len - 8);
2597708c165SSagi Grimberg 	descp = &sense_buffer[8];
2607708c165SSagi Grimberg 	for (desc_len = 0, k = 0; k < add_sen_len; k += desc_len) {
2617708c165SSagi Grimberg 		descp += desc_len;
2627708c165SSagi Grimberg 		add_len = (k < (add_sen_len - 1)) ? descp[1]: -1;
2637708c165SSagi Grimberg 		desc_len = add_len + 2;
2647708c165SSagi Grimberg 		if (descp[0] == desc_type)
2657708c165SSagi Grimberg 			return descp;
2667708c165SSagi Grimberg 		if (add_len < 0) // short descriptor ??
2677708c165SSagi Grimberg 			break;
2687708c165SSagi Grimberg 	}
2697708c165SSagi Grimberg 	return NULL;
2707708c165SSagi Grimberg }
2717708c165SSagi Grimberg EXPORT_SYMBOL(scsi_sense_desc_find);
2727708c165SSagi Grimberg 
2737708c165SSagi Grimberg /**
2747708c165SSagi Grimberg  * scsi_build_sense_buffer - build sense data in a buffer
275749a1122SRandy Dunlap  * @desc:	Sense format (non-zero == descriptor format,
2767708c165SSagi Grimberg  *              0 == fixed format)
2777708c165SSagi Grimberg  * @buf:	Where to build sense data
2787708c165SSagi Grimberg  * @key:	Sense key
2797708c165SSagi Grimberg  * @asc:	Additional sense code
2807708c165SSagi Grimberg  * @ascq:	Additional sense code qualifier
2817708c165SSagi Grimberg  *
2827708c165SSagi Grimberg  **/
scsi_build_sense_buffer(int desc,u8 * buf,u8 key,u8 asc,u8 ascq)2837708c165SSagi Grimberg void scsi_build_sense_buffer(int desc, u8 *buf, u8 key, u8 asc, u8 ascq)
2847708c165SSagi Grimberg {
2857708c165SSagi Grimberg 	if (desc) {
2867708c165SSagi Grimberg 		buf[0] = 0x72;	/* descriptor, current */
2877708c165SSagi Grimberg 		buf[1] = key;
2887708c165SSagi Grimberg 		buf[2] = asc;
2897708c165SSagi Grimberg 		buf[3] = ascq;
2907708c165SSagi Grimberg 		buf[7] = 0;
2917708c165SSagi Grimberg 	} else {
2927708c165SSagi Grimberg 		buf[0] = 0x70;	/* fixed, current */
2937708c165SSagi Grimberg 		buf[2] = key;
2947708c165SSagi Grimberg 		buf[7] = 0xa;
2957708c165SSagi Grimberg 		buf[12] = asc;
2967708c165SSagi Grimberg 		buf[13] = ascq;
2977708c165SSagi Grimberg 	}
2987708c165SSagi Grimberg }
2997708c165SSagi Grimberg EXPORT_SYMBOL(scsi_build_sense_buffer);
3007708c165SSagi Grimberg 
3017708c165SSagi Grimberg /**
3027708c165SSagi Grimberg  * scsi_set_sense_information - set the information field in a
3037708c165SSagi Grimberg  *		formatted sense data buffer
3047708c165SSagi Grimberg  * @buf:	Where to build sense data
305f5a8b3a7SSagi Grimberg  * @buf_len:    buffer length
3067708c165SSagi Grimberg  * @info:	64-bit information value to be set
3077708c165SSagi Grimberg  *
308f5a8b3a7SSagi Grimberg  * Return value:
309749a1122SRandy Dunlap  *	0 on success or -EINVAL for invalid sense buffer length
3107708c165SSagi Grimberg  **/
scsi_set_sense_information(u8 * buf,int buf_len,u64 info)311f5a8b3a7SSagi Grimberg int scsi_set_sense_information(u8 *buf, int buf_len, u64 info)
3127708c165SSagi Grimberg {
3137708c165SSagi Grimberg 	if ((buf[0] & 0x7f) == 0x72) {
3147708c165SSagi Grimberg 		u8 *ucp, len;
3157708c165SSagi Grimberg 
3167708c165SSagi Grimberg 		len = buf[7];
3177708c165SSagi Grimberg 		ucp = (char *)scsi_sense_desc_find(buf, len + 8, 0);
3187708c165SSagi Grimberg 		if (!ucp) {
31912306b42SSagi Grimberg 			buf[7] = len + 0xc;
3207708c165SSagi Grimberg 			ucp = buf + 8 + len;
3217708c165SSagi Grimberg 		}
322f5a8b3a7SSagi Grimberg 
323f5a8b3a7SSagi Grimberg 		if (buf_len < len + 0xc)
324f5a8b3a7SSagi Grimberg 			/* Not enough room for info */
325f5a8b3a7SSagi Grimberg 			return -EINVAL;
326f5a8b3a7SSagi Grimberg 
3277708c165SSagi Grimberg 		ucp[0] = 0;
3287708c165SSagi Grimberg 		ucp[1] = 0xa;
3297708c165SSagi Grimberg 		ucp[2] = 0x80; /* Valid bit */
3307708c165SSagi Grimberg 		ucp[3] = 0;
3317708c165SSagi Grimberg 		put_unaligned_be64(info, &ucp[4]);
3327708c165SSagi Grimberg 	} else if ((buf[0] & 0x7f) == 0x70) {
333ba083116SHannes Reinecke 		/*
334ba083116SHannes Reinecke 		 * Only set the 'VALID' bit if we can represent the value
335ba083116SHannes Reinecke 		 * correctly; otherwise just fill out the lower bytes and
336ba083116SHannes Reinecke 		 * clear the 'VALID' flag.
337ba083116SHannes Reinecke 		 */
338ba083116SHannes Reinecke 		if (info <= 0xffffffffUL)
3397708c165SSagi Grimberg 			buf[0] |= 0x80;
340ba083116SHannes Reinecke 		else
341ba083116SHannes Reinecke 			buf[0] &= 0x7f;
342ba083116SHannes Reinecke 		put_unaligned_be32((u32)info, &buf[3]);
3437708c165SSagi Grimberg 	}
344f5a8b3a7SSagi Grimberg 
345f5a8b3a7SSagi Grimberg 	return 0;
3467708c165SSagi Grimberg }
3477708c165SSagi Grimberg EXPORT_SYMBOL(scsi_set_sense_information);
34878db6e30SHannes Reinecke 
34978db6e30SHannes Reinecke /**
35078db6e30SHannes Reinecke  * scsi_set_sense_field_pointer - set the field pointer sense key
35178db6e30SHannes Reinecke  *		specific information in a formatted sense data buffer
35278db6e30SHannes Reinecke  * @buf:	Where to build sense data
35378db6e30SHannes Reinecke  * @buf_len:    buffer length
35478db6e30SHannes Reinecke  * @fp:		field pointer to be set
35578db6e30SHannes Reinecke  * @bp:		bit pointer to be set
35678db6e30SHannes Reinecke  * @cd:		command/data bit
35778db6e30SHannes Reinecke  *
35878db6e30SHannes Reinecke  * Return value:
359749a1122SRandy Dunlap  *	0 on success or -EINVAL for invalid sense buffer length
36078db6e30SHannes Reinecke  */
scsi_set_sense_field_pointer(u8 * buf,int buf_len,u16 fp,u8 bp,bool cd)36178db6e30SHannes Reinecke int scsi_set_sense_field_pointer(u8 *buf, int buf_len, u16 fp, u8 bp, bool cd)
36278db6e30SHannes Reinecke {
36378db6e30SHannes Reinecke 	u8 *ucp, len;
36478db6e30SHannes Reinecke 
36578db6e30SHannes Reinecke 	if ((buf[0] & 0x7f) == 0x72) {
36678db6e30SHannes Reinecke 		len = buf[7];
36778db6e30SHannes Reinecke 		ucp = (char *)scsi_sense_desc_find(buf, len + 8, 2);
36878db6e30SHannes Reinecke 		if (!ucp) {
36978db6e30SHannes Reinecke 			buf[7] = len + 8;
37078db6e30SHannes Reinecke 			ucp = buf + 8 + len;
37178db6e30SHannes Reinecke 		}
37278db6e30SHannes Reinecke 
37378db6e30SHannes Reinecke 		if (buf_len < len + 8)
37478db6e30SHannes Reinecke 			/* Not enough room for info */
37578db6e30SHannes Reinecke 			return -EINVAL;
37678db6e30SHannes Reinecke 
37778db6e30SHannes Reinecke 		ucp[0] = 2;
37878db6e30SHannes Reinecke 		ucp[1] = 6;
37978db6e30SHannes Reinecke 		ucp[4] = 0x80; /* Valid bit */
38078db6e30SHannes Reinecke 		if (cd)
38178db6e30SHannes Reinecke 			ucp[4] |= 0x40;
38278db6e30SHannes Reinecke 		if (bp < 0x8)
38378db6e30SHannes Reinecke 			ucp[4] |= 0x8 | bp;
38478db6e30SHannes Reinecke 		put_unaligned_be16(fp, &ucp[5]);
38578db6e30SHannes Reinecke 	} else if ((buf[0] & 0x7f) == 0x70) {
38678db6e30SHannes Reinecke 		len = buf[7];
38778db6e30SHannes Reinecke 		if (len < 18)
38878db6e30SHannes Reinecke 			buf[7] = 18;
38978db6e30SHannes Reinecke 
39078db6e30SHannes Reinecke 		buf[15] = 0x80;
39178db6e30SHannes Reinecke 		if (cd)
39278db6e30SHannes Reinecke 			buf[15] |= 0x40;
39378db6e30SHannes Reinecke 		if (bp < 0x8)
39478db6e30SHannes Reinecke 			buf[15] |= 0x8 | bp;
39578db6e30SHannes Reinecke 		put_unaligned_be16(fp, &buf[16]);
39678db6e30SHannes Reinecke 	}
39778db6e30SHannes Reinecke 
39878db6e30SHannes Reinecke 	return 0;
39978db6e30SHannes Reinecke }
40078db6e30SHannes Reinecke EXPORT_SYMBOL(scsi_set_sense_field_pointer);
401