Blame src/scsi.c

Packit Service db8df9
/*
Packit Service db8df9
 * Intel(R) Enclosure LED Utilities
Packit Service db8df9
 * Copyright (C) 2009-2019 Intel Corporation.
Packit Service db8df9
 *
Packit Service db8df9
 * This program is free software; you can redistribute it and/or modify it
Packit Service db8df9
 * under the terms and conditions of the GNU General Public License,
Packit Service db8df9
 * version 2, as published by the Free Software Foundation.
Packit Service db8df9
 *
Packit Service db8df9
 * This program is distributed in the hope it will be useful, but WITHOUT
Packit Service db8df9
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
Packit Service db8df9
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
Packit Service db8df9
 * more details.
Packit Service db8df9
 *
Packit Service db8df9
 * You should have received a copy of the GNU General Public License along with
Packit Service db8df9
 * this program; if not, write to the Free Software Foundation, Inc.,
Packit Service db8df9
 * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
Packit Service db8df9
 *
Packit Service db8df9
 */
Packit Service db8df9
Packit Service db8df9
#include <dirent.h>
Packit Service db8df9
#include <errno.h>
Packit Service db8df9
#include <fcntl.h>
Packit Service db8df9
#include <limits.h>
Packit Service db8df9
#include <stdint.h>
Packit Service db8df9
#include <stdio.h>
Packit Service db8df9
#include <stdlib.h>
Packit Service db8df9
#include <string.h>
Packit Service db8df9
#include <sys/stat.h>
Packit Service db8df9
#include <unistd.h>
Packit Service db8df9
Packit Service db8df9
#if _HAVE_DMALLOC_H
Packit Service db8df9
#include <dmalloc.h>
Packit Service db8df9
#endif
Packit Service db8df9
Packit Service db8df9
#include <scsi/sg_lib.h>
Packit Service db8df9
#include <scsi/sg_cmds_extra.h>
Packit Service db8df9
Packit Service db8df9
#include "cntrl.h"
Packit Service db8df9
#include "config.h"
Packit Service db8df9
#include "enclosure.h"
Packit Service db8df9
#include "list.h"
Packit Service db8df9
#include "scsi.h"
Packit Service db8df9
#include "ses.h"
Packit Service db8df9
#include "status.h"
Packit Service db8df9
#include "sysfs.h"
Packit Service db8df9
#include "utils.h"
Packit Service db8df9
Packit Service db8df9
static int debug = 0;
Packit Service db8df9
Packit Service db8df9
static int get_ses_page(int fd, struct ses_page *p, int pg_code)
Packit Service db8df9
{
Packit Service db8df9
	int ret;
Packit Service db8df9
	int retry_count = 3;
Packit Service db8df9
Packit Service db8df9
	do {
Packit Service db8df9
		ret = sg_ll_receive_diag(fd, 1, pg_code, p->buf, sizeof(p->buf),
Packit Service db8df9
					 0, debug);
Packit Service db8df9
	} while (ret && retry_count--);
Packit Service db8df9
Packit Service db8df9
	if (!ret)
Packit Service db8df9
		p->len = (p->buf[2] << 8) + p->buf[3] + 4;
Packit Service db8df9
	return ret;
Packit Service db8df9
}
Packit Service db8df9
Packit Service db8df9
static int process_page1(struct ses_pages *sp)
Packit Service db8df9
{
Packit Service db8df9
	int num_encl;		/* number of subenclosures */
Packit Service db8df9
	unsigned char *ed;	/* Enclosure Descriptor */
Packit Service db8df9
	int len = 0;
Packit Service db8df9
	int sum_headers = 0;	/* Number of Type descriptor headers */
Packit Service db8df9
	int i = 0;
Packit Service db8df9
Packit Service db8df9
	/* How many enclosures is in the main enclosure? */
Packit Service db8df9
	num_encl = sp->page1->buf[1] + 1;
Packit Service db8df9
	/* Go to Enclosure Descriptor */
Packit Service db8df9
	ed = sp->page1->buf + 8;
Packit Service db8df9
	for (i = 0; i < num_encl; i++, ed += len) {
Packit Service db8df9
		if (ed + 3 > sp->page1->buf + sp->page1->len) {
Packit Service db8df9
			log_debug
Packit Service db8df9
			    ("SES: Error, response pare 1 truncated at %d\n",
Packit Service db8df9
			     i);
Packit Service db8df9
			return 1;
Packit Service db8df9
		}
Packit Service db8df9
		sum_headers += ed[2];
Packit Service db8df9
		len = ed[3] + 4;
Packit Service db8df9
		if (len < 40) {
Packit Service db8df9
			log_debug("SES: Response too short for page 1\n");
Packit Service db8df9
			continue;
Packit Service db8df9
		}
Packit Service db8df9
	}
Packit Service db8df9
Packit Service db8df9
	sp->page1_types = (struct type_descriptor_header *)ed;
Packit Service db8df9
	sp->page1_types_len = sum_headers;
Packit Service db8df9
Packit Service db8df9
	/* ed is on type descr header */
Packit Service db8df9
	for (i = 0; i < sum_headers; i++, ed += 4) {
Packit Service db8df9
		if (ed > sp->page1->buf + sp->page1->len) {
Packit Service db8df9
			log_debug("SES: Response page 1 truncated at %d\n", i);
Packit Service db8df9
			return 1;
Packit Service db8df9
		}
Packit Service db8df9
	}
Packit Service db8df9
	return 0;
Packit Service db8df9
}
Packit Service db8df9
Packit Service db8df9
static struct ses_pages *ses_init(void)
Packit Service db8df9
{
Packit Service db8df9
	struct ses_pages *sp;
Packit Service db8df9
	sp = calloc(1, sizeof(*sp));
Packit Service db8df9
	if (!sp)
Packit Service db8df9
		return NULL;
Packit Service db8df9
	sp->page1 = calloc(1, sizeof(struct ses_page));
Packit Service db8df9
	if (!sp->page1)
Packit Service db8df9
		goto sp1;
Packit Service db8df9
	sp->page2 = calloc(1, sizeof(struct ses_page));
Packit Service db8df9
	if (!sp->page2)
Packit Service db8df9
		goto sp2;
Packit Service db8df9
	return sp;
Packit Service db8df9
 sp2:
Packit Service db8df9
	free(sp->page1);
Packit Service db8df9
 sp1:
Packit Service db8df9
	free(sp);
Packit Service db8df9
	return NULL;
Packit Service db8df9
}
Packit Service db8df9
Packit Service db8df9
static void ses_free(struct ses_pages *sp)
Packit Service db8df9
{
Packit Service db8df9
	if (!sp)
Packit Service db8df9
		return;
Packit Service db8df9
	free(sp->page1);
Packit Service db8df9
	free(sp->page2);
Packit Service db8df9
	free(sp->page10);
Packit Service db8df9
	free(sp);
Packit Service db8df9
}
Packit Service db8df9
Packit Service db8df9
static void dump_p10(unsigned char *p)
Packit Service db8df9
{
Packit Service db8df9
	int i;
Packit Service db8df9
	printf("----------------------------------------------\n");
Packit Service db8df9
	for (i = 0; i < 8; i++, p += 16) {
Packit Service db8df9
		printf("%p: %02x %02x %02x %02x %02x %02x %02x " \
Packit Service db8df9
		       "%02x %02x %02x %02x %02x %02x %02x %02x %02x\n", (void *)p,
Packit Service db8df9
		       p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7],
Packit Service db8df9
		       p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15]);
Packit Service db8df9
	}
Packit Service db8df9
}
Packit Service db8df9
Packit Service db8df9
static int enclosure_open(const struct enclosure_device *enclosure)
Packit Service db8df9
{
Packit Service db8df9
	int fd = -1;
Packit Service db8df9
Packit Service db8df9
	if (enclosure->dev_path)
Packit Service db8df9
		fd = open(enclosure->dev_path, O_RDWR);
Packit Service db8df9
Packit Service db8df9
	return fd;
Packit Service db8df9
}
Packit Service db8df9
Packit Service db8df9
static int enclosure_load_pages(struct enclosure_device *enclosure)
Packit Service db8df9
{
Packit Service db8df9
	int ret;
Packit Service db8df9
	int fd;
Packit Service db8df9
	struct ses_pages *sp;
Packit Service db8df9
Packit Service db8df9
	if (enclosure->ses_pages)
Packit Service db8df9
		return 0;
Packit Service db8df9
Packit Service db8df9
	fd = enclosure_open(enclosure);
Packit Service db8df9
	if (fd == -1)
Packit Service db8df9
		return 1;
Packit Service db8df9
Packit Service db8df9
	sp = ses_init();
Packit Service db8df9
	if (!sp) {
Packit Service db8df9
		ret = 1;
Packit Service db8df9
		goto end;
Packit Service db8df9
	}
Packit Service db8df9
Packit Service db8df9
	/* Read configuration. */
Packit Service db8df9
	ret = get_ses_page(fd, sp->page1, ENCL_CFG_DIAG_STATUS);
Packit Service db8df9
	if (ret)
Packit Service db8df9
		goto end;
Packit Service db8df9
Packit Service db8df9
	ret = process_page1(sp);
Packit Service db8df9
	if (ret)
Packit Service db8df9
		goto end;
Packit Service db8df9
Packit Service db8df9
	/* Get Enclosure Status */
Packit Service db8df9
	ret = get_ses_page(fd, sp->page2, ENCL_CTRL_DIAG_STATUS);
Packit Service db8df9
end:
Packit Service db8df9
	close(fd);
Packit Service db8df9
	if (ret)
Packit Service db8df9
		ses_free(sp);
Packit Service db8df9
	else
Packit Service db8df9
		enclosure->ses_pages = sp;
Packit Service db8df9
	return ret;
Packit Service db8df9
}
Packit Service db8df9
Packit Service db8df9
static int enclosure_load_page10(struct enclosure_device *enclosure)
Packit Service db8df9
{
Packit Service db8df9
	int ret;
Packit Service db8df9
	int fd;
Packit Service db8df9
	struct ses_page *p;
Packit Service db8df9
Packit Service db8df9
	if (enclosure->ses_pages && enclosure->ses_pages->page10)
Packit Service db8df9
		return 0;
Packit Service db8df9
Packit Service db8df9
	ret = enclosure_load_pages(enclosure);
Packit Service db8df9
	if (ret)
Packit Service db8df9
		return ret;
Packit Service db8df9
Packit Service db8df9
	fd = enclosure_open(enclosure);
Packit Service db8df9
	if (fd == -1)
Packit Service db8df9
		return 1;
Packit Service db8df9
Packit Service db8df9
	p = calloc(1, sizeof(struct ses_page));
Packit Service db8df9
	if (!p) {
Packit Service db8df9
		ret = 1;
Packit Service db8df9
		goto end;
Packit Service db8df9
	}
Packit Service db8df9
Packit Service db8df9
	/* Additional Element Status */
Packit Service db8df9
	ret = get_ses_page(fd, p, ENCL_ADDITIONAL_EL_STATUS);
Packit Service db8df9
end:
Packit Service db8df9
	close(fd);
Packit Service db8df9
	if (ret)
Packit Service db8df9
		free(p);
Packit Service db8df9
	else
Packit Service db8df9
		enclosure->ses_pages->page10 = p;
Packit Service db8df9
Packit Service db8df9
	return ret;
Packit Service db8df9
}
Packit Service db8df9
Packit Service db8df9
static void enclosure_free_pages(struct enclosure_device *enclosure)
Packit Service db8df9
{
Packit Service db8df9
	ses_free(enclosure->ses_pages);
Packit Service db8df9
	enclosure->ses_pages = NULL;
Packit Service db8df9
}
Packit Service db8df9
Packit Service db8df9
static void print_page10(struct ses_pages *sp)
Packit Service db8df9
{
Packit Service db8df9
	unsigned char *ai = sp->page10->buf + 8;
Packit Service db8df9
	int i = 0, len = 0, eip = 0;
Packit Service db8df9
	unsigned char *sas = NULL;
Packit Service db8df9
Packit Service db8df9
	while (ai < sp->page10->buf + sp->page10->len) {
Packit Service db8df9
		printf("%s()[%d]: Inv: %d, EIP: %d, Proto: 0x%04x\n", __func__,
Packit Service db8df9
		       i++, ((ai[0] & 0x80) >> 7), ((ai[0] & 0x10) >> 4),
Packit Service db8df9
		       (unsigned int) (ai[0] & 0xf));
Packit Service db8df9
		printf("\tDescriptor len (x-1): %d\n", ai[1] + 1);
Packit Service db8df9
		eip = ai[0] & 0x10;
Packit Service db8df9
		if (eip)
Packit Service db8df9
			printf("\tElement Index: %d\n", ai[3]);
Packit Service db8df9
		len = ai[1] + 2;
Packit Service db8df9
		if ((ai[0] & 0xf) == SCSI_PROTOCOL_SAS) {
Packit Service db8df9
			if (eip)
Packit Service db8df9
				sas = ai + 4;
Packit Service db8df9
			else
Packit Service db8df9
				sas = ai + 2;
Packit Service db8df9
			printf("\tProtocol SAS:\n");
Packit Service db8df9
			printf("\tNumber of phy descriptors: %d\n", sas[0]);
Packit Service db8df9
			printf("\tNot all phys: %d, descriptor type: 0x%1x\n",
Packit Service db8df9
			       (sas[1] & 1), ((sas[1] & 0xc0) >> 6));
Packit Service db8df9
			if (eip) {
Packit Service db8df9
				printf("\tDevice slot number: %d\n", sas[3]);
Packit Service db8df9
				sas += 2;
Packit Service db8df9
			}
Packit Service db8df9
			sas += 2;
Packit Service db8df9
			printf("\tDevice type: 0x%01x\n",
Packit Service db8df9
				(unsigned int)((sas[0] & 0x70) >> 4));
Packit Service db8df9
			printf("\tSMP Initiator Port: 0x%01x\n",
Packit Service db8df9
				(unsigned int)((sas[2] & 2) >> 1));
Packit Service db8df9
			printf("\tSTP Initiator Port: 0x%01x\n",
Packit Service db8df9
				(unsigned int)((sas[2] & 4) >> 2));
Packit Service db8df9
			printf("\tSSP Initiator Port: 0x%01x\n",
Packit Service db8df9
				(unsigned int)((sas[2] & 8) >> 3));
Packit Service db8df9
			printf("\tSATA DEVICE: 0x%01x\n",
Packit Service db8df9
				(unsigned int)(sas[3] & 1));
Packit Service db8df9
			printf("\tSMP Target Port: 0x%01x\n",
Packit Service db8df9
				(unsigned int)((sas[3] & 2) >> 1));
Packit Service db8df9
			printf("\tSTP Target Port: 0x%01x\n",
Packit Service db8df9
				(unsigned int)((sas[3] & 4) >> 2));
Packit Service db8df9
			printf("\tSSP Target Port: 0x%01x\n",
Packit Service db8df9
				(unsigned int)((sas[3] & 8) >> 3));
Packit Service db8df9
			printf("\tSATA Port Selector: 0x%01x\n",
Packit Service db8df9
				(unsigned int)((sas[3] & 0X80) >> 7));
Packit Service db8df9
			printf
Packit Service db8df9
			    ("\tAttached SAS Address: 0x%02x%02x%02x%02x%02x%02x%02x%02x\n",
Packit Service db8df9
			     sas[4], sas[5], sas[6], sas[7], sas[8], sas[9],
Packit Service db8df9
			     sas[10], sas[11]);
Packit Service db8df9
			printf
Packit Service db8df9
			    ("\tSAS Address: 0x%02x%02x%02x%02x%02x%02x%02x%02x\n",
Packit Service db8df9
			     sas[12], sas[13], sas[14], sas[15], sas[16],
Packit Service db8df9
			     sas[17], sas[18], sas[19]);
Packit Service db8df9
			printf("\tPHY Identified: 0x%01x\n", sas[20]);
Packit Service db8df9
		} else
Packit Service db8df9
			printf("\tProtocol not SAS: 0x%02x, skipping\n",
Packit Service db8df9
				(unsigned int)(ai[0] & 0xf));
Packit Service db8df9
		/* */
Packit Service db8df9
		ai += len;
Packit Service db8df9
	}
Packit Service db8df9
	return;
Packit Service db8df9
}
Packit Service db8df9
Packit Service db8df9
static enum ibpi_pattern ibpi_to_ses(enum ibpi_pattern ibpi)
Packit Service db8df9
{
Packit Service db8df9
	switch (ibpi) {
Packit Service db8df9
	case IBPI_PATTERN_UNKNOWN:
Packit Service db8df9
	case IBPI_PATTERN_ONESHOT_NORMAL:
Packit Service db8df9
	case IBPI_PATTERN_NORMAL:
Packit Service db8df9
		return SES_REQ_OK;
Packit Service db8df9
	case IBPI_PATTERN_FAILED_ARRAY:
Packit Service db8df9
		return SES_REQ_IFA;
Packit Service db8df9
	case IBPI_PATTERN_DEGRADED:
Packit Service db8df9
		return SES_REQ_ICA;
Packit Service db8df9
	case IBPI_PATTERN_REBUILD:
Packit Service db8df9
		return SES_REQ_REBUILD;
Packit Service db8df9
	case IBPI_PATTERN_FAILED_DRIVE:
Packit Service db8df9
		return SES_REQ_FAULT;
Packit Service db8df9
	case IBPI_PATTERN_LOCATE:
Packit Service db8df9
		return SES_REQ_IDENT;
Packit Service db8df9
	case IBPI_PATTERN_HOTSPARE:
Packit Service db8df9
		return SES_REQ_HOSTSPARE;
Packit Service db8df9
	case IBPI_PATTERN_PFA:
Packit Service db8df9
		return SES_REQ_PRDFAIL;
Packit Service db8df9
	default:
Packit Service db8df9
		return ibpi;
Packit Service db8df9
	}
Packit Service db8df9
}
Packit Service db8df9
Packit Service db8df9
static int ses_set_message(enum ibpi_pattern ibpi, struct ses_slot_ctrl_elem *el)
Packit Service db8df9
{
Packit Service db8df9
	struct ses_slot_ctrl_elem msg;
Packit Service db8df9
Packit Service db8df9
	memset(&msg, 0, sizeof(msg));
Packit Service db8df9
	if (ibpi == IBPI_PATTERN_LOCATE_OFF) {
Packit Service db8df9
		/*
Packit Service db8df9
		 * For locate_off we don't set a new state, just clear the
Packit Service db8df9
		 * IDENT bit and the bits that are reserved or have different
Packit Service db8df9
		 * meanings in Status and Control pages (RQST ACTIVE and
Packit Service db8df9
		 * RQST MISSING).
Packit Service db8df9
		 */
Packit Service db8df9
		_clr_ident(el->b);
Packit Service db8df9
		el->b2 &= 0x4e;
Packit Service db8df9
		el->b3 &= 0x3c;
Packit Service db8df9
		return 0;
Packit Service db8df9
	}
Packit Service db8df9
Packit Service db8df9
	switch (ibpi_to_ses(ibpi)) {
Packit Service db8df9
	case SES_REQ_ABORT:
Packit Service db8df9
		_set_abrt(msg.b);
Packit Service db8df9
		break;
Packit Service db8df9
	case SES_REQ_REBUILD:
Packit Service db8df9
		_set_rebuild(msg.b);
Packit Service db8df9
		break;
Packit Service db8df9
	case SES_REQ_IFA:
Packit Service db8df9
		_set_ifa(msg.b);
Packit Service db8df9
		break;
Packit Service db8df9
	case SES_REQ_ICA:
Packit Service db8df9
		_set_ica(msg.b);
Packit Service db8df9
		break;
Packit Service db8df9
	case SES_REQ_CONS_CHECK:
Packit Service db8df9
		_set_cons_check(msg.b);
Packit Service db8df9
		break;
Packit Service db8df9
	case SES_REQ_HOSTSPARE:
Packit Service db8df9
		_set_hspare(msg.b);
Packit Service db8df9
		break;
Packit Service db8df9
	case SES_REQ_RSVD_DEV:
Packit Service db8df9
		_set_rsvd_dev(msg.b);
Packit Service db8df9
		break;
Packit Service db8df9
	case SES_REQ_OK:
Packit Service db8df9
		_set_ok(msg.b);
Packit Service db8df9
		break;
Packit Service db8df9
	case SES_REQ_IDENT:
Packit Service db8df9
		_set_ident(msg.b);
Packit Service db8df9
		break;
Packit Service db8df9
	case SES_REQ_RM:
Packit Service db8df9
		_set_rm(msg.b);
Packit Service db8df9
		break;
Packit Service db8df9
	case SES_REQ_INS:
Packit Service db8df9
		_set_ins(msg.b);
Packit Service db8df9
		break;
Packit Service db8df9
	case SES_REQ_MISSING:
Packit Service db8df9
		_set_miss(msg.b);
Packit Service db8df9
		break;
Packit Service db8df9
	case SES_REQ_DNR:
Packit Service db8df9
		_set_dnr(msg.b);
Packit Service db8df9
		break;
Packit Service db8df9
	case SES_REQ_ACTIVE:
Packit Service db8df9
		_set_actv(msg.b);
Packit Service db8df9
		break;
Packit Service db8df9
	case SES_REQ_EN_BB:
Packit Service db8df9
		_set_enbb(msg.b);
Packit Service db8df9
		break;
Packit Service db8df9
	case SES_REQ_EN_BA:
Packit Service db8df9
		_set_enba(msg.b);
Packit Service db8df9
		break;
Packit Service db8df9
	case SES_REQ_DEV_OFF:
Packit Service db8df9
		_set_off(msg.b);
Packit Service db8df9
		break;
Packit Service db8df9
	case SES_REQ_FAULT:
Packit Service db8df9
		_set_fault(msg.b);
Packit Service db8df9
		break;
Packit Service db8df9
	case SES_REQ_PRDFAIL:
Packit Service db8df9
		_set_prdfail(msg.b);
Packit Service db8df9
		break;
Packit Service db8df9
	default:
Packit Service db8df9
		return 1;
Packit Service db8df9
	}
Packit Service db8df9
Packit Service db8df9
	*el = msg;
Packit Service db8df9
Packit Service db8df9
	return 0;
Packit Service db8df9
}
Packit Service db8df9
Packit Service db8df9
static int ses_write_msg(enum ibpi_pattern ibpi, struct block_device *device)
Packit Service db8df9
{
Packit Service db8df9
	struct ses_pages *sp = device->enclosure->ses_pages;
Packit Service db8df9
	int idx = device->encl_index;
Packit Service db8df9
	/* Move do descriptors */
Packit Service db8df9
	struct ses_slot_ctrl_elem *descriptors = (void *)(sp->page2->buf + 8);
Packit Service db8df9
	int i;
Packit Service db8df9
	struct ses_slot_ctrl_elem *desc_element = NULL;
Packit Service db8df9
	element_type local_element_type = SES_UNSPECIFIED;
Packit Service db8df9
Packit Service db8df9
	for (i = 0; i < sp->page1_types_len; i++) {
Packit Service db8df9
		struct type_descriptor_header *t = &sp->page1_types[i];
Packit Service db8df9
Packit Service db8df9
		descriptors++; /* At first, skip overall header. */
Packit Service db8df9
Packit Service db8df9
		if (t->element_type == SES_DEVICE_SLOT ||
Packit Service db8df9
		    t->element_type == SES_ARRAY_DEVICE_SLOT) {
Packit Service db8df9
			if (local_element_type < t->element_type &&
Packit Service db8df9
			    t->num_of_elements > idx) {
Packit Service db8df9
				local_element_type = t->element_type;
Packit Service db8df9
				desc_element = &descriptors[idx];
Packit Service db8df9
			}
Packit Service db8df9
		} else {
Packit Service db8df9
			/*
Packit Service db8df9
			 * Device Slot and Array Device Slot elements are
Packit Service db8df9
			 * always first on the type descriptor header list
Packit Service db8df9
			 */
Packit Service db8df9
			break;
Packit Service db8df9
		}
Packit Service db8df9
Packit Service db8df9
		descriptors += t->num_of_elements;
Packit Service db8df9
	}
Packit Service db8df9
Packit Service db8df9
	if (desc_element) {
Packit Service db8df9
		int ret = ses_set_message(ibpi, desc_element);
Packit Service db8df9
		if (ret)
Packit Service db8df9
			return ret;
Packit Service db8df9
		/* keep PRDFAIL, clear rest */
Packit Service db8df9
		desc_element->common_control &= 0x40;
Packit Service db8df9
		/* set select */
Packit Service db8df9
		desc_element->common_control |= 0x80;
Packit Service db8df9
Packit Service db8df9
		/* second byte is valid only for Array Device Slot */
Packit Service db8df9
		if (local_element_type != SES_ARRAY_DEVICE_SLOT)
Packit Service db8df9
			desc_element->array_slot_control = 0;
Packit Service db8df9
Packit Service db8df9
		return 0;
Packit Service db8df9
	}
Packit Service db8df9
Packit Service db8df9
	return 1;
Packit Service db8df9
}
Packit Service db8df9
Packit Service db8df9
static int ses_send_diag(struct enclosure_device *enclosure)
Packit Service db8df9
{
Packit Service db8df9
	int ret;
Packit Service db8df9
	int fd;
Packit Service db8df9
Packit Service db8df9
	fd = enclosure_open(enclosure);
Packit Service db8df9
	if (fd == -1)
Packit Service db8df9
		return 1;
Packit Service db8df9
Packit Service db8df9
	ret = sg_ll_send_diag(fd, 0, 1, 0, 0, 0, 0,
Packit Service db8df9
			      enclosure->ses_pages->page2->buf,
Packit Service db8df9
			      enclosure->ses_pages->page2->len,
Packit Service db8df9
			      0, debug);
Packit Service db8df9
	close(fd);
Packit Service db8df9
	return ret;
Packit Service db8df9
}
Packit Service db8df9
Packit Service db8df9
static char *get_drive_end_dev(const char *path)
Packit Service db8df9
{
Packit Service db8df9
	char *s, *c, *p;
Packit Service db8df9
Packit Service db8df9
	c = strstr(path, "end_device");
Packit Service db8df9
	if (!c)
Packit Service db8df9
		return NULL;
Packit Service db8df9
	s = strchr(c, '/');
Packit Service db8df9
	if (!s)
Packit Service db8df9
		return NULL;
Packit Service db8df9
	p = calloc(s - c + 1, sizeof(*p));
Packit Service db8df9
	if (!p)
Packit Service db8df9
		return NULL;
Packit Service db8df9
Packit Service db8df9
	strncpy(p, c, s - c);
Packit Service db8df9
	return p;
Packit Service db8df9
}
Packit Service db8df9
Packit Service db8df9
static uint64_t get_drive_sas_addr(const char *path)
Packit Service db8df9
{
Packit Service db8df9
	uint64_t ret = 0;
Packit Service db8df9
	size_t size = strlen(path) * 2;
Packit Service db8df9
	char *buff, *end_dev;
Packit Service db8df9
Packit Service db8df9
	/* Make big buffer. */
Packit Service db8df9
	buff = malloc(size + 1);
Packit Service db8df9
	if (!buff)
Packit Service db8df9
		return ret;
Packit Service db8df9
Packit Service db8df9
	end_dev = get_drive_end_dev(path);
Packit Service db8df9
	if (!end_dev) {
Packit Service db8df9
		free(buff);
Packit Service db8df9
		return ret;
Packit Service db8df9
	}
Packit Service db8df9
Packit Service db8df9
	snprintf(buff, size, "/sys/class/sas_end_device/%s/device/sas_device/%s",
Packit Service db8df9
		 end_dev, end_dev);
Packit Service db8df9
Packit Service db8df9
	ret = get_uint64(buff, ret, "sas_address");
Packit Service db8df9
Packit Service db8df9
	free(end_dev);
Packit Service db8df9
	free(buff);
Packit Service db8df9
Packit Service db8df9
	return ret;
Packit Service db8df9
}
Packit Service db8df9
Packit Service db8df9
static int get_encl_slot(struct block_device *device)
Packit Service db8df9
{
Packit Service db8df9
	struct ses_pages *sp;
Packit Service db8df9
	unsigned char *add_desc = NULL;
Packit Service db8df9
	unsigned char *ap = NULL, *addr_p = NULL;
Packit Service db8df9
	int i, j, len = 0;
Packit Service db8df9
	uint64_t addr, addr_cmp;
Packit Service db8df9
	int idx;
Packit Service db8df9
Packit Service db8df9
	/* try to get slot from sysfs */
Packit Service db8df9
	idx = get_int(device->cntrl_path, -1, "slot");
Packit Service db8df9
	if (idx != -1)
Packit Service db8df9
		return idx;
Packit Service db8df9
Packit Service db8df9
	/*
Packit Service db8df9
	 * Older kernels may not have the "slot" sysfs attribute,
Packit Service db8df9
	 * fallback to Page10 method.
Packit Service db8df9
	 */
Packit Service db8df9
	if (enclosure_load_page10(device->enclosure))
Packit Service db8df9
		return -1;
Packit Service db8df9
	sp = device->enclosure->ses_pages;
Packit Service db8df9
Packit Service db8df9
	addr = get_drive_sas_addr(device->sysfs_path);
Packit Service db8df9
	if (!addr)
Packit Service db8df9
		return -1;
Packit Service db8df9
Packit Service db8df9
	if (debug)
Packit Service db8df9
		print_page10(sp);
Packit Service db8df9
Packit Service db8df9
	/* Check Page10 for address. Extract index. */
Packit Service db8df9
	ap = add_desc = sp->page10->buf + 8;
Packit Service db8df9
	for (i = 0; i < sp->page1_types_len; i++) {
Packit Service db8df9
		struct type_descriptor_header *t = &sp->page1_types[i];
Packit Service db8df9
Packit Service db8df9
		if (t->element_type == SES_DEVICE_SLOT ||
Packit Service db8df9
		    t->element_type == SES_ARRAY_DEVICE_SLOT) {
Packit Service db8df9
			for (j = 0; j < t->num_of_elements; j++, ap += len) {
Packit Service db8df9
				if (debug)
Packit Service db8df9
					dump_p10(ap);
Packit Service db8df9
				/* Get Additional Element Status Descriptor */
Packit Service db8df9
				/* length (x-1) */
Packit Service db8df9
				len = ap[1] + 2;
Packit Service db8df9
				if ((ap[0] & 0xf) != SCSI_PROTOCOL_SAS)
Packit Service db8df9
					continue;	/* need SAS PROTO */
Packit Service db8df9
				/* It is a SAS protocol, go on */
Packit Service db8df9
				if ((ap[0] & 0x10))	/* Check EIP */
Packit Service db8df9
					addr_p = ap + 8;
Packit Service db8df9
				else
Packit Service db8df9
					addr_p = ap + 4;
Packit Service db8df9
				/* Process only PHY 0 descriptor. */
Packit Service db8df9
Packit Service db8df9
				/* Convert be64 to le64 */
Packit Service db8df9
				addr_cmp = ((uint64_t)addr_p[12] << 8*7) |
Packit Service db8df9
					   ((uint64_t)addr_p[13] << 8*6) |
Packit Service db8df9
					   ((uint64_t)addr_p[14] << 8*5) |
Packit Service db8df9
					   ((uint64_t)addr_p[15] << 8*4) |
Packit Service db8df9
					   ((uint64_t)addr_p[16] << 8*3) |
Packit Service db8df9
					   ((uint64_t)addr_p[17] << 8*2) |
Packit Service db8df9
					   ((uint64_t)addr_p[18] << 8*1) |
Packit Service db8df9
					   ((uint64_t)addr_p[19]);
Packit Service db8df9
Packit Service db8df9
				if (addr == addr_cmp) {
Packit Service db8df9
					idx = ap[0] & 0x10 ? ap[3] : j;
Packit Service db8df9
					return idx;
Packit Service db8df9
				}
Packit Service db8df9
			}
Packit Service db8df9
		} else {
Packit Service db8df9
			/*
Packit Service db8df9
			 * Device Slot and Array Device Slot elements are
Packit Service db8df9
			 * always first on the type descriptor header list
Packit Service db8df9
			 */
Packit Service db8df9
			break;
Packit Service db8df9
		}
Packit Service db8df9
	}
Packit Service db8df9
	return -1;
Packit Service db8df9
}
Packit Service db8df9
Packit Service db8df9
/**
Packit Service db8df9
 */
Packit Service db8df9
static int _slot_match(const char *slot_path, const char *device_path)
Packit Service db8df9
{
Packit Service db8df9
	char temp[PATH_MAX], link[PATH_MAX];
Packit Service db8df9
Packit Service db8df9
	snprintf(temp, sizeof(temp), "%s/device", slot_path);
Packit Service db8df9
Packit Service db8df9
	if (realpath(temp, link) == NULL)
Packit Service db8df9
		return 0;
Packit Service db8df9
Packit Service db8df9
	return strncmp(link, device_path, strlen(link)) == 0;
Packit Service db8df9
}
Packit Service db8df9
Packit Service db8df9
/**
Packit Service db8df9
 */
Packit Service db8df9
static char *_slot_find(const char *enclo_path, const char *device_path)
Packit Service db8df9
{
Packit Service db8df9
	struct list dir;
Packit Service db8df9
	char *temp, *result = NULL;
Packit Service db8df9
Packit Service db8df9
	if (scan_dir(enclo_path, &dir) == 0) {
Packit Service db8df9
		list_for_each(&dir, temp) {
Packit Service db8df9
			if (_slot_match(temp, device_path)) {
Packit Service db8df9
				result = str_dup(temp);
Packit Service db8df9
				break;
Packit Service db8df9
			}
Packit Service db8df9
		}
Packit Service db8df9
		list_erase(&dir;;
Packit Service db8df9
	}
Packit Service db8df9
	return result;
Packit Service db8df9
}
Packit Service db8df9
Packit Service db8df9
int scsi_get_enclosure(struct block_device *device)
Packit Service db8df9
{
Packit Service db8df9
	struct enclosure_device *encl;
Packit Service db8df9
Packit Service db8df9
	if (!device || !device->sysfs_path)
Packit Service db8df9
		return 0;
Packit Service db8df9
Packit Service db8df9
	list_for_each(sysfs_get_enclosure_devices(), encl) {
Packit Service db8df9
		if (_slot_match(encl->sysfs_path, device->cntrl_path)) {
Packit Service db8df9
			device->enclosure = encl;
Packit Service db8df9
			device->encl_index = get_encl_slot(device);
Packit Service db8df9
			break;
Packit Service db8df9
		}
Packit Service db8df9
	}
Packit Service db8df9
Packit Service db8df9
	return (device->enclosure != NULL && device->encl_index != -1);
Packit Service db8df9
}
Packit Service db8df9
Packit Service db8df9
/**
Packit Service db8df9
 */
Packit Service db8df9
int scsi_ses_write(struct block_device *device, enum ibpi_pattern ibpi)
Packit Service db8df9
{
Packit Service db8df9
	int ret;
Packit Service db8df9
Packit Service db8df9
	if (!device || !device->sysfs_path || !device->enclosure ||
Packit Service db8df9
	    device->encl_index == -1)
Packit Service db8df9
		__set_errno_and_return(EINVAL);
Packit Service db8df9
Packit Service db8df9
	/* write only if state has changed */
Packit Service db8df9
	if (ibpi == device->ibpi_prev)
Packit Service db8df9
		return 1;
Packit Service db8df9
Packit Service db8df9
	if ((ibpi < IBPI_PATTERN_NORMAL) || (ibpi > SES_REQ_FAULT))
Packit Service db8df9
		__set_errno_and_return(ERANGE);
Packit Service db8df9
Packit Service db8df9
	ret = enclosure_load_pages(device->enclosure);
Packit Service db8df9
	if (ret) {
Packit Service db8df9
		log_warning
Packit Service db8df9
		    ("Unable to send %s message to %s. Device is missing?",
Packit Service db8df9
		     ibpi2str(ibpi), strstr(device->sysfs_path, "host"));
Packit Service db8df9
		return ret;
Packit Service db8df9
	}
Packit Service db8df9
Packit Service db8df9
	return ses_write_msg(ibpi, device);
Packit Service db8df9
}
Packit Service db8df9
Packit Service db8df9
int scsi_ses_flush(struct block_device *device)
Packit Service db8df9
{
Packit Service db8df9
	int ret;
Packit Service db8df9
Packit Service db8df9
	if (!device || !device->enclosure)
Packit Service db8df9
		return 1;
Packit Service db8df9
Packit Service db8df9
	if (!device->enclosure->ses_pages)
Packit Service db8df9
		return 0;
Packit Service db8df9
Packit Service db8df9
	ret = ses_send_diag(device->enclosure);
Packit Service db8df9
Packit Service db8df9
	enclosure_free_pages(device->enclosure);
Packit Service db8df9
	return ret;
Packit Service db8df9
}
Packit Service db8df9
Packit Service db8df9
/**
Packit Service db8df9
 * @brief Gets a path to slot of sas controller.
Packit Service db8df9
 *
Packit Service db8df9
 * This function returns a sysfs path to component of enclosure the device
Packit Service db8df9
 * belongs to.
Packit Service db8df9
 *
Packit Service db8df9
 * @param[in]      path           Canonical sysfs path to block device.
Packit Service db8df9
 *
Packit Service db8df9
 * @return A sysfs path to controller device associated with the given
Packit Service db8df9
 *         block device if successful, otherwise NULL pointer.
Packit Service db8df9
 */
Packit Service db8df9
static char *sas_get_slot_path(const char *path, const char *ctrl_path)
Packit Service db8df9
{
Packit Service db8df9
	char *host;
Packit Service db8df9
	char host_path[PATH_MAX] = { 0 };
Packit Service db8df9
	size_t ctrl_path_len = strlen(ctrl_path);
Packit Service db8df9
Packit Service db8df9
	if (strncmp(path, ctrl_path, ctrl_path_len) != 0)
Packit Service db8df9
		return NULL;
Packit Service db8df9
	host = get_path_hostN(path);
Packit Service db8df9
	if (host) {
Packit Service db8df9
		snprintf(host_path, sizeof(host_path), "%s/%s/bsg/sas_%s",
Packit Service db8df9
			 ctrl_path, host, host);
Packit Service db8df9
		free(host);
Packit Service db8df9
	}
Packit Service db8df9
	return str_dup(host_path);
Packit Service db8df9
}
Packit Service db8df9
Packit Service db8df9
/**
Packit Service db8df9
 */
Packit Service db8df9
static char *_get_enc_slot_path(const char *path)
Packit Service db8df9
{
Packit Service db8df9
	struct enclosure_device *device;
Packit Service db8df9
	char *result = NULL;
Packit Service db8df9
Packit Service db8df9
	list_for_each(sysfs_get_enclosure_devices(), device) {
Packit Service db8df9
		result = _slot_find(device->sysfs_path, path);
Packit Service db8df9
		if (result != NULL)
Packit Service db8df9
			break;
Packit Service db8df9
	}
Packit Service db8df9
	return result;
Packit Service db8df9
}
Packit Service db8df9
Packit Service db8df9
/**
Packit Service db8df9
 */
Packit Service db8df9
char *scsi_get_slot_path(const char *path, const char *ctrl_path)
Packit Service db8df9
{
Packit Service db8df9
	char *result = NULL;
Packit Service db8df9
Packit Service db8df9
	result = _get_enc_slot_path(path);
Packit Service db8df9
	if (!result)
Packit Service db8df9
		result = sas_get_slot_path(path, ctrl_path);
Packit Service db8df9
	return result;
Packit Service db8df9
}