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
free_phy_info(struct sun_sas_port * port_ptr)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
get_phy_info(di_node_t node,struct sun_sas_port * port_ptr)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