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