|
Packit Service |
db8df9 |
/*
|
|
Packit Service |
db8df9 |
* Intel(R) Enclosure LED Utilities
|
|
Packit Service |
db8df9 |
* Copyright (C) 2011-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 |
|
|
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 <linux/bsg.h>
|
|
Packit Service |
db8df9 |
#include <scsi/sg.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/ioctl.h>
|
|
Packit Service |
db8df9 |
#include <sys/stat.h>
|
|
Packit Service |
db8df9 |
#include <unistd.h>
|
|
Packit Service |
db8df9 |
#include <sys/sysmacros.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 "block.h"
|
|
Packit Service |
db8df9 |
#include "cntrl.h"
|
|
Packit Service |
db8df9 |
#include "config.h"
|
|
Packit Service |
db8df9 |
#include "enclosure.h"
|
|
Packit Service |
db8df9 |
#include "ibpi.h"
|
|
Packit Service |
db8df9 |
#include "list.h"
|
|
Packit Service |
db8df9 |
#include "scsi.h"
|
|
Packit Service |
db8df9 |
#include "smp.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 |
#define GPIO_TX_GP1 0x01
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
#define INIT_IBPI(act, loc, err) \
|
|
Packit Service |
db8df9 |
{ .error = err, \
|
|
Packit Service |
db8df9 |
.locate = loc, \
|
|
Packit Service |
db8df9 |
.activity = act \
|
|
Packit Service |
db8df9 |
}
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
#define LED_OFF 0
|
|
Packit Service |
db8df9 |
#define LED_ON 1
|
|
Packit Service |
db8df9 |
#define LED_4HZ 2
|
|
Packit Service |
db8df9 |
#define LED_I4HZ 3
|
|
Packit Service |
db8df9 |
#define LED_EOF 4
|
|
Packit Service |
db8df9 |
#define LED_SOF 5
|
|
Packit Service |
db8df9 |
#define LED_2HZ 6
|
|
Packit Service |
db8df9 |
#define LED_I2HZ 7
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
static const struct gpio_rx_table {
|
|
Packit Service |
db8df9 |
struct gpio_tx_register_byte pattern;
|
|
Packit Service |
db8df9 |
int support_mask;
|
|
Packit Service |
db8df9 |
} ibpi2sgpio[] = {
|
|
Packit Service |
db8df9 |
[IBPI_PATTERN_UNKNOWN] = { INIT_IBPI(LED_SOF,LED_OFF,LED_OFF), 1 }, /* OK */
|
|
Packit Service |
db8df9 |
[IBPI_PATTERN_ONESHOT_NORMAL] = { INIT_IBPI(LED_SOF,LED_OFF,LED_OFF), 1 }, /* OK */
|
|
Packit Service |
db8df9 |
[IBPI_PATTERN_NORMAL] = { INIT_IBPI(LED_SOF,LED_OFF,LED_OFF), 1 }, /* OK */
|
|
Packit Service |
db8df9 |
[IBPI_PATTERN_DEGRADED] = { INIT_IBPI(LED_SOF,LED_OFF,LED_OFF), 0 }, /* NO */
|
|
Packit Service |
db8df9 |
[IBPI_PATTERN_REBUILD] = { INIT_IBPI(LED_SOF,LED_ON,LED_ON), 1 }, /* OK */
|
|
Packit Service |
db8df9 |
[IBPI_PATTERN_FAILED_ARRAY] = { INIT_IBPI(LED_SOF,LED_4HZ,LED_OFF), 0 }, /* NO */
|
|
Packit Service |
db8df9 |
[IBPI_PATTERN_HOTSPARE] = { INIT_IBPI(LED_SOF,LED_OFF,LED_4HZ), 0 }, /* NO */
|
|
Packit Service |
db8df9 |
[IBPI_PATTERN_PFA] = { INIT_IBPI(LED_SOF,LED_OFF,LED_2HZ), 0 }, /* NO */
|
|
Packit Service |
db8df9 |
[IBPI_PATTERN_FAILED_DRIVE] = { INIT_IBPI(LED_SOF,LED_OFF,LED_ON), 1 }, /* OK */
|
|
Packit Service |
db8df9 |
[IBPI_PATTERN_LOCATE] = { INIT_IBPI(LED_SOF,LED_ON,LED_OFF), 1 }, /* OK */
|
|
Packit Service |
db8df9 |
[IBPI_PATTERN_LOCATE_OFF] = { INIT_IBPI(LED_SOF,LED_OFF,LED_OFF), 1 } /* OK */
|
|
Packit Service |
db8df9 |
};
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
struct smp_read_response_frame_header {
|
|
Packit Service |
db8df9 |
uint8_t frame_type; /* =0x41 */
|
|
Packit Service |
db8df9 |
uint8_t function; /* =0x02 for read, 0x82 for write */
|
|
Packit Service |
db8df9 |
uint8_t function_result;
|
|
Packit Service |
db8df9 |
uint8_t reserved;
|
|
Packit Service |
db8df9 |
uint32_t read_data[]; /* variable length of data */
|
|
Packit Service |
db8df9 |
/* uint32_t crc; */
|
|
Packit Service |
db8df9 |
} __attribute__ ((__packed__));
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
struct smp_write_response_frame {
|
|
Packit Service |
db8df9 |
uint8_t frame_type; /* =0x41 */
|
|
Packit Service |
db8df9 |
uint8_t function; /* =0x02 for read, 0x82 for write */
|
|
Packit Service |
db8df9 |
uint8_t function_result;
|
|
Packit Service |
db8df9 |
uint8_t reserved;
|
|
Packit Service |
db8df9 |
uint32_t crc;
|
|
Packit Service |
db8df9 |
} __attribute__ ((__packed__));
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
struct smp_read_request_frame {
|
|
Packit Service |
db8df9 |
uint8_t frame_type; /* =0x40 */
|
|
Packit Service |
db8df9 |
uint8_t function; /* =0x02 for read, 0x82 for write */
|
|
Packit Service |
db8df9 |
uint8_t register_type;
|
|
Packit Service |
db8df9 |
uint8_t register_index;
|
|
Packit Service |
db8df9 |
uint8_t register_count;
|
|
Packit Service |
db8df9 |
uint8_t reserved[3];
|
|
Packit Service |
db8df9 |
uint32_t crc;
|
|
Packit Service |
db8df9 |
} __attribute__ ((__packed__));
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
struct smp_write_request_frame_header {
|
|
Packit Service |
db8df9 |
uint8_t frame_type; /* =0x40 */
|
|
Packit Service |
db8df9 |
uint8_t function; /* =0x02 for read, 0x82 for write */
|
|
Packit Service |
db8df9 |
uint8_t register_type;
|
|
Packit Service |
db8df9 |
uint8_t register_index;
|
|
Packit Service |
db8df9 |
uint8_t register_count;
|
|
Packit Service |
db8df9 |
uint8_t reserved[3];
|
|
Packit Service |
db8df9 |
uint32_t data[]; /* variable length of data */
|
|
Packit Service |
db8df9 |
/* uint32_t crc; */
|
|
Packit Service |
db8df9 |
} __attribute__ ((__packed__));
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
/**
|
|
Packit Service |
db8df9 |
* to_sas_gpio_gp_bit - given the gpio frame data find the byte/bit position of 'od'
|
|
Packit Service |
db8df9 |
* @od: od bit to find
|
|
Packit Service |
db8df9 |
* @data: incoming bitstream (from frame)
|
|
Packit Service |
db8df9 |
* @index: requested data register index (from frame)
|
|
Packit Service |
db8df9 |
* @count: total number of registers in the bitstream (from frame)
|
|
Packit Service |
db8df9 |
* @bit: bit position of 'od' in the returned byte
|
|
Packit Service |
db8df9 |
*
|
|
Packit Service |
db8df9 |
* returns NULL if 'od' is not in 'data'
|
|
Packit Service |
db8df9 |
*
|
|
Packit Service |
db8df9 |
* From SFF-8485 v0.7:
|
|
Packit Service |
db8df9 |
* "In GPIO_TX[1], bit 0 of byte 3 contains the first bit (i.e., OD0.0)
|
|
Packit Service |
db8df9 |
* and bit 7 of byte 0 contains the 32nd bit (i.e., OD10.1).
|
|
Packit Service |
db8df9 |
*
|
|
Packit Service |
db8df9 |
* In GPIO_TX[2], bit 0 of byte 3 contains the 33rd bit (i.e., OD10.2)
|
|
Packit Service |
db8df9 |
* and bit 7 of byte 0 contains the 64th bit (i.e., OD21.0)."
|
|
Packit Service |
db8df9 |
*
|
|
Packit Service |
db8df9 |
* The general-purpose (raw-bitstream) RX registers have the same layout
|
|
Packit Service |
db8df9 |
* although 'od' is renamed 'id' for 'input data'.
|
|
Packit Service |
db8df9 |
*
|
|
Packit Service |
db8df9 |
* SFF-8489 defines the behavior of the LEDs in response to the 'od' values.
|
|
Packit Service |
db8df9 |
*/
|
|
Packit Service |
db8df9 |
static unsigned char *to_sas_gpio_gp_bit(unsigned int od, unsigned char *data,
|
|
Packit Service |
db8df9 |
unsigned char index,
|
|
Packit Service |
db8df9 |
unsigned char count,
|
|
Packit Service |
db8df9 |
unsigned char *bit)
|
|
Packit Service |
db8df9 |
{
|
|
Packit Service |
db8df9 |
unsigned int reg;
|
|
Packit Service |
db8df9 |
unsigned char byte;
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
/* gp registers start at index 1 */
|
|
Packit Service |
db8df9 |
if (index == 0)
|
|
Packit Service |
db8df9 |
return NULL;
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
index--; /* make index 0-based */
|
|
Packit Service |
db8df9 |
if (od < index * 32)
|
|
Packit Service |
db8df9 |
return NULL;
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
od -= index * 32;
|
|
Packit Service |
db8df9 |
reg = od >> 5;
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
if (reg >= count)
|
|
Packit Service |
db8df9 |
return NULL;
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
od &= (1 << 5) - 1;
|
|
Packit Service |
db8df9 |
byte = 3 - (od >> 3);
|
|
Packit Service |
db8df9 |
*bit = od & ((1 << 3) - 1);
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
return &data[reg * 4 + byte];
|
|
Packit Service |
db8df9 |
}
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
int try_test_sas_gpio_gp_bit(unsigned int od, unsigned char *data,
|
|
Packit Service |
db8df9 |
unsigned char index, unsigned char count)
|
|
Packit Service |
db8df9 |
{
|
|
Packit Service |
db8df9 |
unsigned char *byte;
|
|
Packit Service |
db8df9 |
unsigned char bit;
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
byte = to_sas_gpio_gp_bit(od, data, index, count, &bit;;
|
|
Packit Service |
db8df9 |
if (!byte)
|
|
Packit Service |
db8df9 |
return -1;
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
return (*byte >> bit) & 1;
|
|
Packit Service |
db8df9 |
}
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
int try_set_sas_gpio_gp_bit(unsigned int od, unsigned char *data,
|
|
Packit Service |
db8df9 |
unsigned char index, unsigned char count)
|
|
Packit Service |
db8df9 |
{
|
|
Packit Service |
db8df9 |
unsigned char *byte;
|
|
Packit Service |
db8df9 |
unsigned char bit;
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
byte = to_sas_gpio_gp_bit(od, data, index, count, &bit;;
|
|
Packit Service |
db8df9 |
if (!byte)
|
|
Packit Service |
db8df9 |
return 0;
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
*byte |= 1 << bit;
|
|
Packit Service |
db8df9 |
return 1;
|
|
Packit Service |
db8df9 |
}
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
int try_clear_sas_gpio_gp_bit(unsigned int od, unsigned char *data,
|
|
Packit Service |
db8df9 |
unsigned char index, unsigned char count)
|
|
Packit Service |
db8df9 |
{
|
|
Packit Service |
db8df9 |
unsigned char *byte;
|
|
Packit Service |
db8df9 |
unsigned char bit;
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
byte = to_sas_gpio_gp_bit(od, data, index, count, &bit;;
|
|
Packit Service |
db8df9 |
if (!byte)
|
|
Packit Service |
db8df9 |
return 0;
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
*byte &= ~(1 << bit);
|
|
Packit Service |
db8df9 |
return 1;
|
|
Packit Service |
db8df9 |
}
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
/**
|
|
Packit Service |
db8df9 |
* set_raw_pattern - turn a tx register into a tx_gp bitstream
|
|
Packit Service |
db8df9 |
*
|
|
Packit Service |
db8df9 |
* takes @dev_idx (phy index) and a @pattern (error, locate, activity)
|
|
Packit Service |
db8df9 |
* tuple and modifies the bitstream in @data accordingly */
|
|
Packit Service |
db8df9 |
int set_raw_pattern(unsigned int dev_idx, unsigned char *data,
|
|
Packit Service |
db8df9 |
const struct gpio_tx_register_byte *pattern)
|
|
Packit Service |
db8df9 |
{
|
|
Packit Service |
db8df9 |
int od_offset = dev_idx * 3;
|
|
Packit Service |
db8df9 |
int rc = 0;
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
if (pattern->activity == LED_ON)
|
|
Packit Service |
db8df9 |
rc +=
|
|
Packit Service |
db8df9 |
try_set_sas_gpio_gp_bit(od_offset + 0, data, GPIO_TX_GP1,
|
|
Packit Service |
db8df9 |
1);
|
|
Packit Service |
db8df9 |
else
|
|
Packit Service |
db8df9 |
rc +=
|
|
Packit Service |
db8df9 |
try_clear_sas_gpio_gp_bit(od_offset + 0, data, GPIO_TX_GP1,
|
|
Packit Service |
db8df9 |
1);
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
if (pattern->locate == LED_ON)
|
|
Packit Service |
db8df9 |
rc +=
|
|
Packit Service |
db8df9 |
try_set_sas_gpio_gp_bit(od_offset + 1, data, GPIO_TX_GP1,
|
|
Packit Service |
db8df9 |
1);
|
|
Packit Service |
db8df9 |
else
|
|
Packit Service |
db8df9 |
rc +=
|
|
Packit Service |
db8df9 |
try_clear_sas_gpio_gp_bit(od_offset + 1, data, GPIO_TX_GP1,
|
|
Packit Service |
db8df9 |
1);
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
if (pattern->error == LED_ON)
|
|
Packit Service |
db8df9 |
rc +=
|
|
Packit Service |
db8df9 |
try_set_sas_gpio_gp_bit(od_offset + 2, data, GPIO_TX_GP1,
|
|
Packit Service |
db8df9 |
1);
|
|
Packit Service |
db8df9 |
else
|
|
Packit Service |
db8df9 |
rc +=
|
|
Packit Service |
db8df9 |
try_clear_sas_gpio_gp_bit(od_offset + 2, data, GPIO_TX_GP1,
|
|
Packit Service |
db8df9 |
1);
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
return rc == 3;
|
|
Packit Service |
db8df9 |
}
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
/**
|
|
Packit Service |
db8df9 |
* @brief open device for smp protocol
|
|
Packit Service |
db8df9 |
*/
|
|
Packit Service |
db8df9 |
static int _open_smp_device(const char *filename)
|
|
Packit Service |
db8df9 |
{
|
|
Packit Service |
db8df9 |
char buf[PATH_MAX];
|
|
Packit Service |
db8df9 |
FILE *df;
|
|
Packit Service |
db8df9 |
int hba_fd;
|
|
Packit Service |
db8df9 |
int dmaj, dmin;
|
|
Packit Service |
db8df9 |
snprintf(buf, sizeof(buf), "%s/dev", filename);
|
|
Packit Service |
db8df9 |
df = fopen(buf, "r");
|
|
Packit Service |
db8df9 |
if (!df)
|
|
Packit Service |
db8df9 |
return -1;
|
|
Packit Service |
db8df9 |
if (fgets(buf, sizeof(buf), df) == NULL) {
|
|
Packit Service |
db8df9 |
fclose(df);
|
|
Packit Service |
db8df9 |
return -1;
|
|
Packit Service |
db8df9 |
}
|
|
Packit Service |
db8df9 |
if (sscanf(buf, "%d:%d", &dmaj, &dmin) != 2) {
|
|
Packit Service |
db8df9 |
fclose(df);
|
|
Packit Service |
db8df9 |
return -1;
|
|
Packit Service |
db8df9 |
}
|
|
Packit Service |
db8df9 |
fclose(df);
|
|
Packit Service |
db8df9 |
snprintf(buf, sizeof(buf), "/var/tmp/led.%d.%d.%d", dmaj, dmin,
|
|
Packit Service |
db8df9 |
getpid());
|
|
Packit Service |
db8df9 |
if (mknod(buf, S_IFCHR | S_IRUSR | S_IWUSR, makedev(dmaj, dmin)) < 0)
|
|
Packit Service |
db8df9 |
return -1;
|
|
Packit Service |
db8df9 |
hba_fd = open(buf, O_RDWR);
|
|
Packit Service |
db8df9 |
unlink(buf);
|
|
Packit Service |
db8df9 |
if (hba_fd < 0)
|
|
Packit Service |
db8df9 |
return -1;
|
|
Packit Service |
db8df9 |
return hba_fd;
|
|
Packit Service |
db8df9 |
}
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
/**
|
|
Packit Service |
db8df9 |
* @brief close smp device
|
|
Packit Service |
db8df9 |
*/
|
|
Packit Service |
db8df9 |
static int _close_smp_device(int fd)
|
|
Packit Service |
db8df9 |
{
|
|
Packit Service |
db8df9 |
return close(fd);
|
|
Packit Service |
db8df9 |
}
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
/**
|
|
Packit Service |
db8df9 |
@brief use sg protocol in order to send data directly to hba driver
|
|
Packit Service |
db8df9 |
*/
|
|
Packit Service |
db8df9 |
static int _send_smp_frame(int hba, void *data, size_t data_size,
|
|
Packit Service |
db8df9 |
void *response, size_t *response_size)
|
|
Packit Service |
db8df9 |
{
|
|
Packit Service |
db8df9 |
struct sg_io_v4 sg_frame;
|
|
Packit Service |
db8df9 |
uint8_t request_buf[SCSI_MAX_CDB_LENGTH];
|
|
Packit Service |
db8df9 |
int response_status = 0;
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
/* wrap the frame into sg structure */
|
|
Packit Service |
db8df9 |
memset(&sg_frame, 0, sizeof(sg_frame));
|
|
Packit Service |
db8df9 |
sg_frame.guard = 'Q';
|
|
Packit Service |
db8df9 |
sg_frame.protocol = BSG_PROTOCOL_SCSI;
|
|
Packit Service |
db8df9 |
sg_frame.subprotocol = BSG_SUB_PROTOCOL_SCSI_TRANSPORT;
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
sg_frame.request_len = sizeof(request_buf);
|
|
Packit Service |
db8df9 |
sg_frame.request = (uintptr_t) request_buf;
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
sg_frame.dout_xfer_len = data_size;
|
|
Packit Service |
db8df9 |
sg_frame.dout_xferp = (uintptr_t) data;
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
sg_frame.din_xfer_len = *response_size;
|
|
Packit Service |
db8df9 |
sg_frame.din_xferp = (uintptr_t) response;
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
sg_frame.timeout = SG_RESPONSE_TIMEOUT;
|
|
Packit Service |
db8df9 |
/* send ioctl */
|
|
Packit Service |
db8df9 |
if (ioctl(hba, SG_IO, &sg_frame) < 0)
|
|
Packit Service |
db8df9 |
return -1;
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
/* return status */
|
|
Packit Service |
db8df9 |
if (sg_frame.driver_status)
|
|
Packit Service |
db8df9 |
response_status = sg_frame.driver_status;
|
|
Packit Service |
db8df9 |
else if (sg_frame.transport_status)
|
|
Packit Service |
db8df9 |
response_status = sg_frame.transport_status;
|
|
Packit Service |
db8df9 |
else if (sg_frame.device_status)
|
|
Packit Service |
db8df9 |
response_status = sg_frame.device_status;
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
*response_size = sg_frame.din_xfer_len - sg_frame.din_resid;
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
return response_status;
|
|
Packit Service |
db8df9 |
}
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
/* 1024 bytes for data, 4 for crc */
|
|
Packit Service |
db8df9 |
#define MAX_SMP_FRAME_DATA 1024
|
|
Packit Service |
db8df9 |
#define MAX_SMP_FRAME_LEN (sizeof(struct smp_write_request_frame_header) + \
|
|
Packit Service |
db8df9 |
MAX_SMP_FRAME_DATA + SMP_FRAME_CRC_LEN)
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
/**
|
|
Packit Service |
db8df9 |
@brief prepare full smp frame ready to send to hba
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
@note len is a number of 32bit words
|
|
Packit Service |
db8df9 |
*/
|
|
Packit Service |
db8df9 |
static int _start_smp_write_gpio(int hba,
|
|
Packit Service |
db8df9 |
struct smp_write_request_frame_header *header,
|
|
Packit Service |
db8df9 |
void *data, size_t len)
|
|
Packit Service |
db8df9 |
{
|
|
Packit Service |
db8df9 |
uint8_t buf[MAX_SMP_FRAME_LEN];
|
|
Packit Service |
db8df9 |
struct smp_write_response_frame response;
|
|
Packit Service |
db8df9 |
size_t response_size = sizeof(response);
|
|
Packit Service |
db8df9 |
int status;
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
memset(&response, 0, sizeof(response));
|
|
Packit Service |
db8df9 |
/* create full frame */
|
|
Packit Service |
db8df9 |
if (len * SMP_DATA_CHUNK_SIZE > MAX_SMP_FRAME_DATA)
|
|
Packit Service |
db8df9 |
__set_errno_and_return(EINVAL);
|
|
Packit Service |
db8df9 |
memset(buf, 0, sizeof(buf));
|
|
Packit Service |
db8df9 |
memcpy(buf, header, sizeof(*header));
|
|
Packit Service |
db8df9 |
memcpy(buf + sizeof(*header), data, len * SMP_DATA_CHUNK_SIZE);
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
status =
|
|
Packit Service |
db8df9 |
_send_smp_frame(hba, buf,
|
|
Packit Service |
db8df9 |
sizeof(*header) + len * SMP_DATA_CHUNK_SIZE +
|
|
Packit Service |
db8df9 |
SMP_FRAME_CRC_LEN, &response, &response_size);
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
/* if frame is somehow malformed return failure */
|
|
Packit Service |
db8df9 |
if (status != GPIO_STATUS_OK ||
|
|
Packit Service |
db8df9 |
response.frame_type != SMP_FRAME_TYPE_RESP ||
|
|
Packit Service |
db8df9 |
response.function != header->function) {
|
|
Packit Service |
db8df9 |
return GPIO_STATUS_FAILURE;
|
|
Packit Service |
db8df9 |
}
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
return response.function_result;
|
|
Packit Service |
db8df9 |
}
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
/**
|
|
Packit Service |
db8df9 |
@brief prepare smp frame header
|
|
Packit Service |
db8df9 |
*/
|
|
Packit Service |
db8df9 |
int smp_write_gpio(const char *path, int smp_reg_type,
|
|
Packit Service |
db8df9 |
int smp_reg_index, int smp_reg_count, void *data,
|
|
Packit Service |
db8df9 |
size_t len)
|
|
Packit Service |
db8df9 |
{
|
|
Packit Service |
db8df9 |
struct smp_write_request_frame_header header;
|
|
Packit Service |
db8df9 |
int status;
|
|
Packit Service |
db8df9 |
header.frame_type = SMP_FRAME_TYPE_REQ;
|
|
Packit Service |
db8df9 |
header.function = SMP_FUNC_GPIO_WRITE;
|
|
Packit Service |
db8df9 |
header.register_type = smp_reg_type;
|
|
Packit Service |
db8df9 |
header.register_index = smp_reg_index;
|
|
Packit Service |
db8df9 |
header.register_count = smp_reg_count;
|
|
Packit Service |
db8df9 |
memset(header.reserved, 0, sizeof(header.reserved));
|
|
Packit Service |
db8df9 |
int fd = _open_smp_device(path);
|
|
Packit Service |
db8df9 |
status = _start_smp_write_gpio(fd, &header, data, len);
|
|
Packit Service |
db8df9 |
_close_smp_device(fd);
|
|
Packit Service |
db8df9 |
return status;
|
|
Packit Service |
db8df9 |
}
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
#define BLINK_GEN_1HZ 8
|
|
Packit Service |
db8df9 |
#define BLINK_GEN_2HZ 4
|
|
Packit Service |
db8df9 |
#define BLINK_GEN_4HZ 2
|
|
Packit Service |
db8df9 |
#define DEFAULT_FORCED_ACTIVITY_OFF 1
|
|
Packit Service |
db8df9 |
#define DEFAULT_MAXIMUM_ACTIVITY_ON 2
|
|
Packit Service |
db8df9 |
#define DEFAULT_STRETCH_ACTIVITY_OFF 0
|
|
Packit Service |
db8df9 |
#define DEFAULT_STRETCH_ACTIVITY_ON 0
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
/* one data chunk is 32bit long */
|
|
Packit Service |
db8df9 |
#define SMP_DATA_CHUNKS 1
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
struct gpio_tx_register_byte *get_bdev_ibpi_buffer(struct block_device *bdevice)
|
|
Packit Service |
db8df9 |
{
|
|
Packit Service |
db8df9 |
if (bdevice && bdevice->host)
|
|
Packit Service |
db8df9 |
return bdevice->host->ibpi_state_buffer;
|
|
Packit Service |
db8df9 |
return NULL;
|
|
Packit Service |
db8df9 |
}
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
/**
|
|
Packit Service |
db8df9 |
*/
|
|
Packit Service |
db8df9 |
int scsi_smp_fill_buffer(struct block_device *device, enum ibpi_pattern ibpi)
|
|
Packit Service |
db8df9 |
{
|
|
Packit Service |
db8df9 |
const char *sysfs_path = device->cntrl_path;
|
|
Packit Service |
db8df9 |
struct gpio_tx_register_byte *gpio_tx;
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
if (sysfs_path == NULL)
|
|
Packit Service |
db8df9 |
__set_errno_and_return(EINVAL);
|
|
Packit Service |
db8df9 |
if ((ibpi < IBPI_PATTERN_NORMAL) || (ibpi > IBPI_PATTERN_LOCATE_OFF))
|
|
Packit Service |
db8df9 |
__set_errno_and_return(ERANGE);
|
|
Packit Service |
db8df9 |
if (!device->cntrl) {
|
|
Packit Service |
db8df9 |
log_debug("No ctrl dev for '%s'", strstr(sysfs_path, "host"));
|
|
Packit Service |
db8df9 |
__set_errno_and_return(ENODEV);
|
|
Packit Service |
db8df9 |
}
|
|
Packit Service |
db8df9 |
if (device->cntrl->cntrl_type != CNTRL_TYPE_SCSI) {
|
|
Packit Service |
db8df9 |
log_debug("No SCSI ctrl dev '%s'", strstr(sysfs_path, "host"));
|
|
Packit Service |
db8df9 |
__set_errno_and_return(EINVAL);
|
|
Packit Service |
db8df9 |
}
|
|
Packit Service |
db8df9 |
if (!device->host) {
|
|
Packit Service |
db8df9 |
log_debug("No host for '%s'", strstr(sysfs_path, "host"));
|
|
Packit Service |
db8df9 |
__set_errno_and_return(ENODEV);
|
|
Packit Service |
db8df9 |
}
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
if (device->cntrl->isci_present && !ibpi2sgpio[ibpi].support_mask) {
|
|
Packit Service |
db8df9 |
char *c = strrchr(device->sysfs_path, '/');
|
|
Packit Service |
db8df9 |
if (c++) {
|
|
Packit Service |
db8df9 |
log_debug
|
|
Packit Service |
db8df9 |
("pattern %s not supported for device (/dev/%s)",
|
|
Packit Service |
db8df9 |
ibpi2str(ibpi), c);
|
|
Packit Service |
db8df9 |
fprintf(stderr,
|
|
Packit Service |
db8df9 |
"%s(): pattern %s not supported for device (/dev/%s)\n",
|
|
Packit Service |
db8df9 |
__func__, ibpi2str(ibpi), c);
|
|
Packit Service |
db8df9 |
} else {
|
|
Packit Service |
db8df9 |
log_debug("pattern %s not supported for device %s",
|
|
Packit Service |
db8df9 |
ibpi2str(ibpi), device->sysfs_path);
|
|
Packit Service |
db8df9 |
fprintf(stderr,
|
|
Packit Service |
db8df9 |
"%s(): pattern %s not supported for device\n\t(%s)\n",
|
|
Packit Service |
db8df9 |
__func__, ibpi2str(ibpi), device->sysfs_path);
|
|
Packit Service |
db8df9 |
}
|
|
Packit Service |
db8df9 |
__set_errno_and_return(ENOTSUP);
|
|
Packit Service |
db8df9 |
}
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
gpio_tx = get_bdev_ibpi_buffer(device);
|
|
Packit Service |
db8df9 |
if (!gpio_tx) {
|
|
Packit Service |
db8df9 |
log_debug("%s(): no IBPI buffer. Skipping.", __func__);
|
|
Packit Service |
db8df9 |
__set_errno_and_return(ENODEV);
|
|
Packit Service |
db8df9 |
}
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
if (device->cntrl->isci_present) {
|
|
Packit Service |
db8df9 |
/* update bit stream for this device */
|
|
Packit Service |
db8df9 |
set_raw_pattern(device->phy_index,
|
|
Packit Service |
db8df9 |
&device->host->bitstream[0], &ibpi2sgpio[ibpi].pattern);
|
|
Packit Service |
db8df9 |
} else {
|
|
Packit Service |
db8df9 |
/*
|
|
Packit Service |
db8df9 |
* GPIO_TX[n] register has the highest numbered drive of the
|
|
Packit Service |
db8df9 |
* four in the first byte and the lowest numbered drive in the
|
|
Packit Service |
db8df9 |
* fourth byte. See SFF-8485 Rev. 0.7 Table 24.
|
|
Packit Service |
db8df9 |
*/
|
|
Packit Service |
db8df9 |
gpio_tx[device->phy_index + 3 - (device->phy_index % 4) * 2] =
|
|
Packit Service |
db8df9 |
ibpi2sgpio[ibpi].pattern;
|
|
Packit Service |
db8df9 |
}
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
/* write only if state has changed */
|
|
Packit Service |
db8df9 |
if (ibpi != device->ibpi_prev)
|
|
Packit Service |
db8df9 |
device->host->flush = 1;
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
return 1;
|
|
Packit Service |
db8df9 |
}
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
int scsi_smp_write_buffer(struct block_device *device)
|
|
Packit Service |
db8df9 |
{
|
|
Packit Service |
db8df9 |
const char *sysfs_path = device->cntrl_path;
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
if (sysfs_path == NULL)
|
|
Packit Service |
db8df9 |
__set_errno_and_return(EINVAL);
|
|
Packit Service |
db8df9 |
if (!device->host)
|
|
Packit Service |
db8df9 |
__set_errno_and_return(ENODEV);
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
if (device->host->flush) {
|
|
Packit Service |
db8df9 |
device->host->flush = 0;
|
|
Packit Service |
db8df9 |
/* re-transmit the bitstream */
|
|
Packit Service |
db8df9 |
if (device->cntrl->isci_present) {
|
|
Packit Service |
db8df9 |
return smp_write_gpio(sysfs_path,
|
|
Packit Service |
db8df9 |
GPIO_REG_TYPE_TX_GP,
|
|
Packit Service |
db8df9 |
GPIO_TX_GP1, 1,
|
|
Packit Service |
db8df9 |
&device->host->bitstream[0],
|
|
Packit Service |
db8df9 |
SMP_DATA_CHUNKS);
|
|
Packit Service |
db8df9 |
} else {
|
|
Packit Service |
db8df9 |
return smp_write_gpio(sysfs_path,
|
|
Packit Service |
db8df9 |
GPIO_REG_TYPE_TX,
|
|
Packit Service |
db8df9 |
0, (device->host->ports+3)/4,
|
|
Packit Service |
db8df9 |
device->host->ibpi_state_buffer,
|
|
Packit Service |
db8df9 |
(device->host->ports+3)/4);
|
|
Packit Service |
db8df9 |
}
|
|
Packit Service |
db8df9 |
} else
|
|
Packit Service |
db8df9 |
return 1;
|
|
Packit Service |
db8df9 |
}
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
/**
|
|
Packit Service |
db8df9 |
*/
|
|
Packit Service |
db8df9 |
static void init_smp(struct cntrl_device *device)
|
|
Packit Service |
db8df9 |
{
|
|
Packit Service |
db8df9 |
struct _host_type *hosts;
|
|
Packit Service |
db8df9 |
int i;
|
|
Packit Service |
db8df9 |
if (!device)
|
|
Packit Service |
db8df9 |
return;
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
for (hosts = device->hosts; hosts; hosts = hosts->next) {
|
|
Packit Service |
db8df9 |
/* already initialized */
|
|
Packit Service |
db8df9 |
if (hosts->ibpi_state_buffer)
|
|
Packit Service |
db8df9 |
continue;
|
|
Packit Service |
db8df9 |
hosts->ibpi_state_buffer =
|
|
Packit Service |
db8df9 |
calloc(hosts->ports,
|
|
Packit Service |
db8df9 |
sizeof(struct
|
|
Packit Service |
db8df9 |
gpio_tx_register_byte));
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
if (!hosts->ibpi_state_buffer)
|
|
Packit Service |
db8df9 |
continue;
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
for (i = 0; i < hosts->ports; i++)
|
|
Packit Service |
db8df9 |
set_raw_pattern(i, &hosts->bitstream[0],
|
|
Packit Service |
db8df9 |
&ibpi2sgpio
|
|
Packit Service |
db8df9 |
[IBPI_PATTERN_ONESHOT_NORMAL].pattern);
|
|
Packit Service |
db8df9 |
hosts->flush = 0;
|
|
Packit Service |
db8df9 |
}
|
|
Packit Service |
db8df9 |
}
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
/**
|
|
Packit Service |
db8df9 |
*/
|
|
Packit Service |
db8df9 |
int cntrl_init_smp(const char *path, struct cntrl_device *cntrl)
|
|
Packit Service |
db8df9 |
{
|
|
Packit Service |
db8df9 |
char *path2 = NULL;
|
|
Packit Service |
db8df9 |
char *c;
|
|
Packit Service |
db8df9 |
int host, port = 0;
|
|
Packit Service |
db8df9 |
struct dirent *de;
|
|
Packit Service |
db8df9 |
DIR *d;
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
if (!cntrl)
|
|
Packit Service |
db8df9 |
return port;
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
/* Other case - just init controller. */
|
|
Packit Service |
db8df9 |
if (path && strstr(path, "port-")) {
|
|
Packit Service |
db8df9 |
path2 = str_dup(path);
|
|
Packit Service |
db8df9 |
if (!path2)
|
|
Packit Service |
db8df9 |
return port;
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
c = strstr(path2, "port-");
|
|
Packit Service |
db8df9 |
if (!c) {
|
|
Packit Service |
db8df9 |
/* Should not happen. */
|
|
Packit Service |
db8df9 |
log_debug("%s() missing 'port' in path '%s'", __func__,
|
|
Packit Service |
db8df9 |
path2);
|
|
Packit Service |
db8df9 |
free(path2);
|
|
Packit Service |
db8df9 |
return port;
|
|
Packit Service |
db8df9 |
}
|
|
Packit Service |
db8df9 |
c = strchr(c, '/');
|
|
Packit Service |
db8df9 |
if (!c) {
|
|
Packit Service |
db8df9 |
free(path2);
|
|
Packit Service |
db8df9 |
return port;
|
|
Packit Service |
db8df9 |
}
|
|
Packit Service |
db8df9 |
*c = 0;
|
|
Packit Service |
db8df9 |
/* And now path2 has only up to 'port-...' string. */
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
/* this should open port-XX:X directory
|
|
Packit Service |
db8df9 |
* FIXME: for enclosure it may be port-XX:Y:Z but it's a second
|
|
Packit Service |
db8df9 |
* occurrence
|
|
Packit Service |
db8df9 |
*
|
|
Packit Service |
db8df9 |
* We may try this on the missing device.
|
|
Packit Service |
db8df9 |
* */
|
|
Packit Service |
db8df9 |
d = opendir(path2);
|
|
Packit Service |
db8df9 |
if (!d) {
|
|
Packit Service |
db8df9 |
log_debug("%s() Error dir open '%s', path ='%s'",
|
|
Packit Service |
db8df9 |
__func__, path2, path);
|
|
Packit Service |
db8df9 |
free(path2);
|
|
Packit Service |
db8df9 |
return port;
|
|
Packit Service |
db8df9 |
}
|
|
Packit Service |
db8df9 |
while ((de = readdir(d))) {
|
|
Packit Service |
db8df9 |
if ((strcmp(de->d_name, ".") == 0) ||
|
|
Packit Service |
db8df9 |
(strcmp(de->d_name, "..")) == 0) {
|
|
Packit Service |
db8df9 |
continue;
|
|
Packit Service |
db8df9 |
}
|
|
Packit Service |
db8df9 |
if (strncmp(de->d_name, "phy-", strlen("phy-")) == 0) {
|
|
Packit Service |
db8df9 |
/* Need link called "phy-XX:Y
|
|
Packit Service |
db8df9 |
* Y is real phy we need.
|
|
Packit Service |
db8df9 |
* This can also be found
|
|
Packit Service |
db8df9 |
* in phy_identifier file */
|
|
Packit Service |
db8df9 |
if (sscanf(de->d_name, "phy-%d:%d", &host, &port) != 2)
|
|
Packit Service |
db8df9 |
continue;
|
|
Packit Service |
db8df9 |
|
|
Packit Service |
db8df9 |
break;
|
|
Packit Service |
db8df9 |
}
|
|
Packit Service |
db8df9 |
}
|
|
Packit Service |
db8df9 |
closedir(d);
|
|
Packit Service |
db8df9 |
free(path2);
|
|
Packit Service |
db8df9 |
}
|
|
Packit Service |
db8df9 |
init_smp(cntrl);
|
|
Packit Service |
db8df9 |
return port;
|
|
Packit Service |
db8df9 |
}
|