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