1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #include <sun_sas.h> 28 #include <sys/types.h> 29 #include <sys/stat.h> 30 #include <fcntl.h> 31 #include <unistd.h> 32 #include <libdevinfo.h> 33 #include <netinet/in.h> 34 #include <inttypes.h> 35 36 /* 37 * structure for di_devlink_walk 38 */ 39 typedef struct walk_devlink { 40 char *path; 41 size_t len; 42 char **linkpp; 43 } walk_devlink_t; 44 45 /* 46 * Free the phy allocation. 47 */ 48 static void 49 free_phy_info(struct sun_sas_port *port_ptr) 50 { 51 struct phy_info *phy_ptr, *last_phy; 52 53 phy_ptr = port_ptr->first_phy; 54 while (phy_ptr != NULL) { 55 last_phy = phy_ptr; 56 phy_ptr = phy_ptr->next; 57 free(last_phy); 58 } 59 60 port_ptr->first_phy = NULL; 61 62 } 63 64 /* 65 * callback funtion for di_devlink_walk 66 * Find matching /dev link for the given path argument. 67 * devlink element and callback function argument. 68 * The input path is expected to not have "/devices". 69 */ 70 extern HBA_STATUS 71 get_phy_info(di_node_t node, struct sun_sas_port *port_ptr) 72 { 73 const char ROUTINE[] = "get_phy_info"; 74 char *portDevpath = NULL; 75 uchar_t *propByteData = NULL; 76 struct phy_info *phy_ptr; 77 uint_t nvcount; 78 int rval, count, i; 79 nvlist_t *nvl, **phyInfoVal; 80 uint8_t phyId; 81 int8_t negoRate, prgmMinRate, prgmMaxRate, hwMinRate, hwMaxRate; 82 83 /* 84 * When path is specified, it doesn't have minor 85 * name. Therefore, the ../.. prefixes needs to be stripped. 86 */ 87 if ((portDevpath = di_devfs_path(node)) == NULL) { 88 log(LOG_DEBUG, ROUTINE, 89 "Unable to get device path from portNode."); 90 } 91 92 count = di_prop_lookup_bytes(DDI_DEV_T_ANY, node, "phy-info", 93 (uchar_t **)&propByteData); 94 if (count < 0) { 95 if (portDevpath) { 96 log(LOG_DEBUG, ROUTINE, 97 "Property phy-info not found on port %s%s", 98 DEVICES_DIR, portDevpath); 99 di_devfs_path_free(portDevpath); 100 } else { 101 log(LOG_DEBUG, ROUTINE, "Property phy-info not found."); 102 } 103 return (HBA_STATUS_ERROR); 104 } else { 105 rval = nvlist_unpack((char *)propByteData, count, &nvl, 0); 106 if (rval != 0) { 107 if (portDevpath) { 108 log(LOG_DEBUG, ROUTINE, 109 "nvlist_unpack failed on port %s%s", 110 DEVICES_DIR, portDevpath); 111 di_devfs_path_free(portDevpath); 112 } else { 113 log(LOG_DEBUG, ROUTINE, 114 "nvlist_unpack failed."); 115 } 116 return (HBA_STATUS_ERROR); 117 } else { 118 rval = nvlist_lookup_nvlist_array(nvl, "phy-info-nvl", 119 &phyInfoVal, &nvcount); 120 if (rval != 0) { 121 if (portDevpath) { 122 log(LOG_DEBUG, ROUTINE, 123 "nvlist array phy-info-nvl not\ 124 found on port %s%s", DEVICES_DIR, 125 portDevpath); 126 di_devfs_path_free(portDevpath); 127 } else { 128 log(LOG_DEBUG, ROUTINE, 129 "nvlist array phy-info-nvl not\ 130 found"); 131 } 132 nvlist_free(nvl); 133 return (HBA_STATUS_ERROR); 134 } else { 135 /* indentation moved */ 136 for (i = 0; i < nvcount; i++) { 137 if (nvlist_lookup_uint8(phyInfoVal[i], 138 "PhyIdentifier", &phyId) != 0) { 139 /* Indicate a failure : no better way to set */ 140 phyId = 0xff; 141 } 142 if (nvlist_lookup_int8(phyInfoVal[i], 143 "NegotiatedLinkRate", &negoRate) != 0) { 144 negoRate = HBA_SASSTATE_UNKNOWN; 145 } 146 if (nvlist_lookup_int8(phyInfoVal[i], 147 "ProgrammedMinLinkRate", &prgmMinRate) != 0) { 148 prgmMinRate = HBA_SASSTATE_UNKNOWN; 149 } 150 if (nvlist_lookup_int8(phyInfoVal[i], 151 "ProgrammedMaxLinkRate", &prgmMaxRate) != 0) { 152 prgmMaxRate = HBA_SASSTATE_UNKNOWN; 153 } 154 if (nvlist_lookup_int8(phyInfoVal[i], 155 "HardwareMinLinkRate", &hwMinRate) != 0) { 156 hwMinRate = HBA_SASSTATE_UNKNOWN; 157 } 158 if (nvlist_lookup_int8(phyInfoVal[i], 159 "HardwareMaxLinkRate", &hwMaxRate) != 0) { 160 hwMaxRate = HBA_SASSTATE_UNKNOWN; 161 } 162 163 if ((phy_ptr = (struct phy_info *)calloc(1, 164 sizeof (struct phy_info))) == NULL) { 165 OUT_OF_MEMORY(ROUTINE); 166 if (portDevpath) 167 di_devfs_path_free(portDevpath); 168 free_phy_info(port_ptr); 169 nvlist_free(nvl); 170 return (HBA_STATUS_ERROR); 171 } 172 phy_ptr->phy.PhyIdentifier = phyId; 173 phy_ptr->phy.NegotiatedLinkRate = negoRate; 174 phy_ptr->phy.ProgrammedMinLinkRate = prgmMinRate; 175 phy_ptr->phy.ProgrammedMaxLinkRate = prgmMaxRate; 176 phy_ptr->phy.HardwareMinLinkRate = hwMinRate; 177 phy_ptr->phy.HardwareMaxLinkRate = hwMaxRate; 178 /* 179 * we will fill domain port later. 180 */ 181 (void) memset(phy_ptr->phy.domainPortWWN.wwn, 0, 8); 182 phy_ptr->index = i; 183 if (port_ptr->first_phy == NULL) { 184 port_ptr->first_phy = phy_ptr; 185 } else { 186 phy_ptr->next = port_ptr->first_phy; 187 port_ptr->first_phy = phy_ptr; 188 } 189 190 } 191 nvlist_free(nvl); 192 /* end of indentation move */ 193 } 194 } 195 } 196 197 if (portDevpath) { 198 di_devfs_path_free(portDevpath); 199 } 200 201 return (HBA_STATUS_OK); 202 } 203