Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1030382AbVIITkN (ORCPT ); Fri, 9 Sep 2005 15:40:13 -0400 Received: (majordomo@vger.kernel.org) by vger.kernel.org id S1030380AbVIITjz (ORCPT ); Fri, 9 Sep 2005 15:39:55 -0400 Received: from magic.adaptec.com ([216.52.22.17]:47558 "EHLO magic.adaptec.com") by vger.kernel.org with ESMTP id S1030362AbVIITjd (ORCPT ); Fri, 9 Sep 2005 15:39:33 -0400 Message-ID: <4321E4EE.20906@adaptec.com> Date: Fri, 09 Sep 2005 15:39:26 -0400 From: Luben Tuikov User-Agent: Mozilla Thunderbird 1.0.6 (X11/20050716) X-Accept-Language: en-us, en MIME-Version: 1.0 To: Linux Kernel Mailing List , SCSI Mailing List Subject: [PATCH 2.6.13 3/14] sas-class: expander_conf.c Expander communication user space program Content-Type: text/plain; charset=ISO-8859-1 Content-Transfer-Encoding: 7bit X-OriginalArrivalTime: 09 Sep 2005 19:39:31.0850 (UTC) FILETIME=[33210EA0:01C5B576] Sender: linux-kernel-owner@vger.kernel.org X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 13851 Lines: 476 Signed-off-by: Luben Tuikov diff -X linux-2.6.13/Documentation/dontdiff -Naur linux-2.6.13-orig/drivers/scsi/sas-class/expander_conf.c linux-2.6.13/drivers/scsi/sas-class/expander_conf.c --- linux-2.6.13-orig/drivers/scsi/sas-class/expander_conf.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.13/drivers/scsi/sas-class/expander_conf.c 2005-09-09 11:14:29.000000000 -0400 @@ -0,0 +1,463 @@ +/* + * Serial Attached SCSI (SAS) Expander communication user space program + * + * Copyright (C) 2005 Adaptec, Inc. All rights reserved. + * Copyright (C) 2005 Luben Tuikov + * + * This file is licensed under GPLv2. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * $Id: //depot/sas-class/expander_conf.c#6 $ + */ + +/* This is a simple program to show how to communicate with + * expander devices in a SAS domain. + * + * The process is simple: + * 1. Build the SMP frame you want to send. The format and layout + * is described in the SAS spec. Leave the CRC field equal 0. + * 2. Open the expander's SMP portal sysfs file in RW mode. + * 3. Write the frame you built in 1. + * 4. Read the amount of data you expect to receive for the frame you built. + * If you receive different amount of data you expected to receive, + * then there was some kind of error. + * All this process is shown in detail in the function do_smp_func() + * and its callers, below. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define LEFT_FIELD_SIZE 25 + +#ifdef __LITTLE_ENDIAN +#define be64_to_cpu(_x) __bswap_64(*(uint64_t *)(&(_x))) +#define be32_to_cpu(_x) __bswap_32(*(uint32_t *)(&(_x))) +#define be16_to_cpu(_x) __bswap_16(*(uint16_t *)(&(_x))) +#define cpu_to_be64(_x) __bswap_64(*(uint64_t *)(&(_x))) +#define cpu_to_be32(_x) __bswap_32(*(uint32_t *)(&(_x))) +#define cpu_to_be16(_x) __bswap_16(*(uint16_t *)(&(_x))) +#else +#define be64_to_cpu(_x) (_x) +#define be32_to_cpu(_x) (_x) +#define be16_to_cpu(_x) (_x) +#define cpu_to_be64(_x) (_x) +#define cpu_to_be32(_x) (_x) +#define cpu_to_be16(_x) (_x) +#endif + +#define SAS_ADDR(_x) ((unsigned long long) be64_to_cpu(*(uint64_t *)(_x))) +#define SAS_ADDR_SIZE 8 + +const char *prog; + +struct route_table_entry { + int disabled; + uint8_t routed_sas_addr[SAS_ADDR_SIZE]; +}; + +struct expander { + int num_phys; + uint8_t *phy_attr; + int route_indexes; +}; + +static int do_smp_func(char *smp_portal_name, void *smp_req, int smp_req_size, + void *smp_resp, int smp_resp_size) +{ + int fd; + ssize_t res; + + fd = open(smp_portal_name, O_RDWR); + if (fd == -1) { + printf("%s: opening %s: %s(%d)\n", prog, smp_portal_name, + strerror(errno), errno); + return fd; + } + + res = write(fd, smp_req, smp_req_size); + if (!res) { + printf("%s: nothing could be written to %s\n", prog, + smp_portal_name); + goto out_err; + } else if (res == -1) { + printf("%s: writing to %s: %s(%d)\n", prog, smp_portal_name, + strerror(errno), errno); + goto out_err; + } + + res = read(fd, smp_resp, smp_resp_size); + if (!res) { + printf("%s: nothing could be read from %s\n", prog, + smp_portal_name); + goto out_err; + } else if (res == -1) { + printf("%s: reading from %s: %s(%d)\n", prog, smp_portal_name, + strerror(errno), errno); + goto out_err; + } + close(fd); + return res; + out_err: + close(fd); + return -1; +} + +#define MI_REQ_SIZE 8 +#define MI_RESP_SIZE 64 + +static unsigned char mi_req[MI_REQ_SIZE] = { 0x40, 1, 0, }; +static unsigned char mi_resp[MI_RESP_SIZE]; + +#define MI_FIELD_SIZE 20 +#define MI_PRINTS(a, b) printf("%*s %*s\n",LEFT_FIELD_SIZE,a,MI_FIELD_SIZE,b) +#define MI_PRINTD(a, b) printf("%*s %*u\n",LEFT_FIELD_SIZE,a,MI_FIELD_SIZE,b) +#define MI_PRINTA(a, b) printf("%*s %0*llx\n",LEFT_FIELD_SIZE,a,MI_FIELD_SIZE,b) + +static int mi_expander(char *smp_portal_name, struct expander *ex) +{ + int res; + + res = do_smp_func(smp_portal_name, mi_req, MI_REQ_SIZE, + mi_resp, MI_RESP_SIZE); + if (res == MI_RESP_SIZE && mi_resp[2] == 0) { + char buf[20]; + + memcpy(buf, mi_resp+12, 8); + buf[8] = 0; + MI_PRINTS("Vendor:", buf); + + memcpy(buf, mi_resp+20, 16); + buf[16] = 0; + MI_PRINTS("Product:", buf); + + memcpy(buf, mi_resp+36, 4); + buf[4] = 0; + MI_PRINTS("Revision:", buf); + + if (!(mi_resp[8] & 1)) + return 0; + + memcpy(buf, mi_resp+40, 8); + buf[8] = 0; + MI_PRINTS("Component:", buf); + + MI_PRINTD("Component ID:", be16_to_cpu(mi_resp[48])); + MI_PRINTD("Component revision:", mi_resp[50]); + } + return 0; +} + +#define RG_REQ_SIZE 8 +#define RG_RESP_SIZE 32 + +static unsigned char rg_req[RG_REQ_SIZE] = { 0x40, 0, }; +static unsigned char rg_resp[RG_RESP_SIZE]; + +static int rg_expander(char *smp_portal_name, struct expander *ex) +{ + int res; + + res = do_smp_func(smp_portal_name, rg_req, RG_REQ_SIZE, rg_resp, + RG_RESP_SIZE); + + if (res == RG_RESP_SIZE && rg_resp[2] == 0) { + MI_PRINTD("Expander Change Count:", be16_to_cpu(rg_resp[4])); + MI_PRINTD("Expander Route Indexes:", be16_to_cpu(rg_resp[6])); + ex->route_indexes = be16_to_cpu(rg_resp[6]); + MI_PRINTD("Number of phys:", rg_resp[9]); + ex->num_phys = rg_resp[9]; + MI_PRINTS("Configuring:", (rg_resp[10] & 2) ? "Yes" : "No"); + MI_PRINTS("Configurable route table:", + (rg_resp[10] & 1) ? "Yes" : "No"); + MI_PRINTA("Enclosure Logical Identifier:", + SAS_ADDR(rg_resp+12)); + ex->phy_attr = malloc(ex->num_phys * sizeof(*ex->phy_attr)); + } + return 0; +} + +#define DISCOVER_REQ_SIZE 16 +#define DISCOVER_RESP_SIZE 56 + +static unsigned char disc_req[DISCOVER_REQ_SIZE] = {0x40, 0x10, 0, }; +static unsigned char disc_resp[DISCOVER_RESP_SIZE]; + +#define PHY_EEXIST 0x10 +#define PHY_VACANT 0x16 + +static const char *attached_dev_type[8] = { + [0] = "none", + [1] = "end device", + [2] = "edge expander", + [3] = "fanout expander", + [4 ... 7] = "unknown", +}; + +static const char *phy_link_rate[16] = { + [0] = "unknown", + [1] = "disabled", + [2] = "phy reset problem", + [3] = "spinup hold", + [4] = "port selector", + [5 ... 7] = "unknown", + [8] = "G1 (1,5 Gb/s)", + [9] = "G2 (3 GB/s)", + [10 ... 15] = "Unknown", +}; + +static const char *proto_table[8] = { + "", + "SMP", "STP", "STP|SMP", "SSP", + "SSP|SMP", "SSP|STP", "SSP|STP|SMP", +}; + +#define DIRECT_ROUTING 0 +#define SUBTRACTIVE_ROUTING 1 +#define TABLE_ROUTING 2 + +static const char *routing_attr[8] = { + [DIRECT_ROUTING] = "D", + [SUBTRACTIVE_ROUTING] = "S", + [TABLE_ROUTING] = "T", + [3 ... 7] = "x", +}; + +static const char *conn_type[0x80] = { + [0] = "No information", + [1] = "SAS external receptacle (i.e., SFF-8470)(see SAS-1.1)", + [2] = "SAS external compact receptacle (i.e., SFF-8088)(see SAS-1.1)", + [3 ... 0x0f ] = "External connector", + [0x10] = "SAS internal wide plug (i.e., SFF-8484)(see SAS-1.1)", + [0x11] = "SAS internal compact wide plug (i.e., SFF-8087)(see SAS-1.1)", + [0x12 ... 0x1f] = "Internal wide connector", + [0x20] = "SAS backplane receptacle (i.e., SFF-8482)(see SAS-1.1)", + [0x21] = "SATA-style host plug (i.e., ATA/ATAPI-7 V3)(see SAS-1.1)", + [0x22] = "SAS plug (i.e., SFF-8482)(see SAS-1.1)", + [0x23] = "SATA device plug (i.e., ATA/ATAPI-7 V3)(see SAS-1.1)", + [0x24 ... 0x2f] = "Internal connector to end device", + [0x30 ... 0x6f] = "Unknown\n", + [0x70 ... 0x7f] = "Vendor specific", +}; + +static int discover_phy(char *smp_portal_name, int phy_id, struct expander *ex) +{ + int res; + const char *dev_str; + + disc_req[9] = phy_id; + + res = do_smp_func(smp_portal_name, disc_req, DISCOVER_REQ_SIZE, + disc_resp, DISCOVER_RESP_SIZE); + + if (res != DISCOVER_RESP_SIZE) { + printf("%s: error disovering phy %d\n", prog, phy_id); + goto out; + } + switch (disc_resp[2]) { + case PHY_VACANT: + printf("phy%02d: vacant\n", phy_id); + goto out; break; + case PHY_EEXIST: + printf("phy%02d doesn't exist\n", phy_id); + goto out; break; + case 0: + break; + default: + printf("phy%02d SMP function result: 0x%x\n", + phy_id, disc_resp[2]); + goto out; + } + + printf("Phy%02d:%s attached: %016llx:%02d chg count:%02d\n", + phy_id, routing_attr[disc_resp[44] & 0x0F], + SAS_ADDR(disc_resp+24), disc_resp[32], disc_resp[42]); + + if (ex->phy_attr) + ex->phy_attr[phy_id] = disc_resp[44] & 0x0F; + + if (disc_resp[14] & 1) + dev_str = "SATA Host"; + else if (disc_resp[15] & 0x80) + dev_str = "SATA Port Selector"; + else if (disc_resp[15] & 1) + dev_str = "SATA device"; + else + dev_str = attached_dev_type[(disc_resp[12] & 0x70) >> 4]; + printf(" Attached device: %15s Link rate: %15s\n", + dev_str, phy_link_rate[disc_resp[13] & 0xf]); + + printf(" Tproto: %15s Iproto: %15s\n", + proto_table[(disc_resp[15] & 0xe) >> 1], + proto_table[(disc_resp[14] & 0xe) >> 1]); + + printf(" Programmed MIN-MAX linkrate: %s - %s\n", + phy_link_rate[(disc_resp[40] & 0xF0)>>4], + phy_link_rate[(disc_resp[41] & 0xF0)>>4]); + + printf(" Hardware MIN-MAX linkrate: %s - %s\n", + phy_link_rate[(disc_resp[40] & 0x0F)], + phy_link_rate[(disc_resp[41] & 0x0F)]); + + printf(" %s phy\n", (disc_resp[43] & 0x80) ? "Virtual" : "Physical"); + printf(" Partial pathway timeout value: %d microseconds\n", + disc_resp[43] & 0x0F); + + printf(" Connector type: %s\n", conn_type[disc_resp[45] & 0x7F]); + printf(" Connector element index: %d\n", disc_resp[46]); + printf(" Connector physical link: %d\n", disc_resp[47]); +out: + return 0; +} + +#define RPEL_REQ_SIZE 16 +#define RPEL_RESP_SIZE 32 + +static unsigned char rpel_req[RPEL_REQ_SIZE] = { 0x40, 0x11, 0, }; +static unsigned char rpel_resp[RPEL_RESP_SIZE]; + +static int report_phy_error_log(char *smp_portal_name, int phy_id) +{ + int res; + + rpel_req[9] = phy_id; + + res = do_smp_func(smp_portal_name, rpel_req, RPEL_REQ_SIZE, + rpel_resp, RPEL_RESP_SIZE); + if (res != RPEL_RESP_SIZE) { + printf("%s: error reading error log for phy %d (%d)\n", + prog, phy_id, res); + goto out; + } + MI_PRINTD(" Invalid DW count:", be32_to_cpu(rpel_resp[12])); + MI_PRINTD(" RD error count:", be32_to_cpu(rpel_resp[16])); + MI_PRINTD(" DW sync loss count:", be32_to_cpu(rpel_resp[20])); + MI_PRINTD(" Reset problem count:", be32_to_cpu(rpel_resp[24])); +out: + return 0; +} + +#define RRI_REQ_SIZE 16 +#define RRI_RESP_SIZE 44 + +static unsigned char rri_req[RRI_REQ_SIZE] = { 0x40, 0x13, 0, }; +static unsigned char rri_resp[RRI_RESP_SIZE]; + +static int show_routing_table(char *smp_portal_name, int phy_id, + struct expander *ex) +{ + struct route_table_entry *rt; + int res, i, last_non_zero = -1; + + if (!ex->phy_attr || ex->phy_attr[phy_id] != TABLE_ROUTING) + return 0; + + rt = malloc(sizeof(struct route_table_entry)*ex->route_indexes); + if (!rt) + return 0; + + rri_req[9] = phy_id; + + for (i = 0; i < ex->route_indexes; i++) { + *(uint16_t *)(rri_req+6) = cpu_to_be16(i); + res = do_smp_func(smp_portal_name, rri_req, RRI_REQ_SIZE, + rri_resp, RRI_RESP_SIZE); + if (res != RRI_RESP_SIZE) { + printf("Error getting phy %d route index %d (%d)\n", + phy_id, i, res); + goto out; + } + if (rri_resp[2] != 0) + break; + if (be16_to_cpu(rri_resp[6]) != i) { + printf("Expander FW error for phy %d index %d\n", + phy_id, i); + goto out; + } + rt[i].disabled = rri_resp[12] & 0x80 ? 1 : 0; + memcpy(rt[i].routed_sas_addr, rri_resp+16, SAS_ADDR_SIZE); + if (rt[i].routed_sas_addr[0]) + last_non_zero = i; + } + printf(" Routing Table\n"); + if (last_non_zero == -1) + printf(" Empty (all zero)\n"); + else { + for (i = 0; i <= last_non_zero; i++) + printf(" %02d %8s %016llx\n", + i, rt[i].disabled ? "disabled" : "enabled", + SAS_ADDR(rt[i].routed_sas_addr)); + } +out: + free(rt); + return 0; +} + +static int discover_expander(char *smp_portal_name, struct expander *ex) +{ + int i; + + for (i = 0; i < ex->num_phys; i++) { + printf("\n"); + discover_phy(smp_portal_name, i, ex); + report_phy_error_log(smp_portal_name, i); + show_routing_table(smp_portal_name, i, ex); + } + + return 0; +} + +static void print_info(void) +{ + printf("%s \n", prog); + printf("\tWhere is the binary attribute file of the" + "\n\texpander device in sysfs.\n"); +} + +int main(int argc, char *argv[]) +{ + prog = strrchr(argv[0], '/'); + if (prog) + prog++; + else + prog = argv[0]; + + if (argc < 2) { + print_info(); + return 0; + } else { + struct expander ex; + + memset(&ex, 0, sizeof(ex)); + + mi_expander(argv[1], &ex); + rg_expander(argv[1], &ex); + discover_expander(argv[1], &ex); + if (ex.phy_attr) + free(ex.phy_attr); + } + return 0; +} - To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/