xref: /illumos-gate/usr/src/lib/storage/libg_fc/common/map.c (revision b07ce584f4e28873b8927d7f83d9d3275a0f3ed2)
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  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 
27 /*LINTLIBRARY*/
28 
29 /*
30  * I18N message number ranges
31  *  This file: 12000 - 12499
32  *  Shared common messages: 1 - 1999
33  */
34 
35 /*
36  *	This module is part of the Fibre Channel Interface library.
37  */
38 
39 /* #define		_POSIX_SOURCE 1 */
40 
41 
42 /*	Includes	*/
43 #include	<stdlib.h>
44 #include	<stdio.h>
45 #include	<sys/file.h>
46 #include	<sys/types.h>
47 #include	<sys/stat.h>
48 #include	<sys/mkdev.h>
49 #include	<sys/param.h>
50 #include	<fcntl.h>
51 #include	<unistd.h>
52 #include	<string.h>
53 #include	<sys/scsi/scsi.h>
54 #include	<dirent.h>		/* for DIR */
55 #include	<sys/vtoc.h>
56 #include	<nl_types.h>
57 #include	<strings.h>
58 #include	<errno.h>
59 #include	<sys/ddi.h>		/* for max */
60 #include	<fnmatch.h>
61 #include	<l_common.h>
62 #include	<stgcom.h>
63 #include	<l_error.h>
64 #include	<g_state.h>
65 #include	<g_scsi.h>
66 #include	<sys/fibre-channel/ulp/fcp_util.h>
67 #include	<sys/fibre-channel/impl/fc_error.h>
68 #include	<sys/fibre-channel/impl/fcph.h>
69 #include	<sys/socalio.h>
70 #include	<libdevinfo.h>
71 #include	<ctype.h>
72 #include	<devid.h>
73 
74 /* Some forward declarations of static functions */
75 /*
76  * becomes extern interface for Tapestry.
77  * static int g_get_inq_dtype(char *, la_wwn_t, uchar_t *);
78  * static int g_get_dev_list(char *, fc_port_dev_t **, int *, int);
79  */
80 static int	g_issue_fcp_ioctl(int, struct fcp_ioctl *, int);
81 static int	g_set_port_state(char *, int);
82 static int	g_dev_log_in_out(char *, la_wwn_t, uint16_t);
83 static int	g_get_dev_port_state(char *, la_wwn_t, uint32_t *);
84 static void	g_free_rls(AL_rls *);
85 static int	g_scsi_inquiry_cmd80(int, uchar_t *, int);
86 static int	get_fca_inq_dtype(char *, la_wwn_t, uchar_t *);
87 static int	g_find_supported_inq_page(int, int);
88 static int	wwn_list_name_compare(const void *, const void *);
89 static int	devid_get_all(ddi_devid_t, di_node_t, char *,
90 			struct mplist_struct **);
91 static int	get_multipath(char *, struct dlist **,
92 			struct wwn_list_struct *);
93 static int	get_multipath_disk(char *, struct dlist **,
94 			struct wwn_list_struct *);
95 static void	mplist_free(struct mplist_struct *);
96 static int	get_wwn_data(di_node_t, uchar_t **, uchar_t **);
97 static int	get_dev_path(struct wwn_list_struct **, char *, char *);
98 static int	insert_missing_pwwn(char *, struct wwn_list_struct **);
99 static int	get_scsi_vhci_port_wwn(char *, uchar_t *);
100 static int	search_wwn_entry(struct wwn_list_found_struct *, uchar_t *,
101 		uchar_t *);
102 static int	add_wwn_entry(struct wwn_list_found_struct **, uchar_t *,
103 		uchar_t *);
104 static int	string_to_wwn(uchar_t *, uchar_t *);
105 static int	get_wwns(char *, uchar_t *, uchar_t *, int *,
106 		struct wwn_list_found_struct **);
107 
108 /* type for g_dev_map_init related routines */
109 
110 #define	S_FREE(x)	(((x) != NULL) ? (free(x), (x) = NULL) : (void *)0)
111 
112 typedef struct impl_map_dev_prop {
113 	char	prop_name[MAXNAMELEN];
114 	int	prop_type;
115 	int	prop_size;
116 	void 	*prop_data;
117 	int 	prop_error;
118 	struct impl_map_dev_prop	*next;
119 } impl_map_dev_prop_t;
120 
121 typedef struct impl_map_dev {
122 	int			flag;
123 	uint_t			topo;
124 	impl_map_dev_prop_t	*prop_list;
125 	struct impl_map_dev	*parent;
126 	struct impl_map_dev	*child;
127 	struct impl_map_dev 	*next;
128 } impl_map_dev_t;
129 
130 /*	Defines 	*/
131 #define	VERBPRINT	if (verbose) (void) printf
132 
133 #define	DIR_MATCH_ST		"*[0-9+]n"
134 #define	DIR_MATCH_SSD		"*s2"
135 
136 #define	PROP_NOEXIST		0
137 #define	PROP_EXIST		1
138 
139 /*	Prototypes	*/
140 static int create_map(char *, gfc_map_t *, int, int);
141 static char ctoi(char);
142 static int	lilp_map_cmp(const void*, const void*);
143 static int	devices_get_all(di_node_t, char *, char *,
144 			struct wwn_list_struct **);
145 static char	*my_devfs_path(di_node_t);
146 static void	my_devfs_path_free(char *path);
147 static void	copy_wwn_data_to_str(char *, const uchar_t *);
148 static void	init_drv(char *, char *, char *);
149 
150 /* static for g_dev_map_init related routines */
151 
152 static int update_map_dev_fc_prop(impl_map_dev_prop_t **, uint32_t,
153 	uchar_t *, uchar_t *, int, int);
154 static int update_map_dev_FCP_prop(impl_map_dev_prop_t **, uchar_t *, int, int);
155 static int handle_map_dev_FCP_prop(minor_t, la_wwn_t, impl_map_dev_prop_t **);
156 static void free_prop_list(impl_map_dev_prop_t **);
157 static void free_child_list(impl_map_dev_t **);
158 static u_longlong_t wwnConversion(uchar_t *wwn);
159 
160 uchar_t g_switch_to_alpa[] = {
161 	0xef, 0xe8, 0xe4, 0xe2, 0xe1, 0xe0, 0xdc, 0xda, 0xd9, 0xd6,
162 	0xd5, 0xd4, 0xd3, 0xd2, 0xd1, 0xce, 0xcd, 0xcc, 0xcb, 0xca,
163 	0xc9, 0xc7, 0xc6, 0xc5, 0xc3, 0xbc, 0xba, 0xb9, 0xb6, 0xb5,
164 	0xb4, 0xb3, 0xb2, 0xb1, 0xae, 0xad, 0xac, 0xab, 0xaa, 0xa9,
165 	0xa7, 0xa6, 0xa5, 0xa3, 0x9f, 0x9e, 0x9d, 0x9b, 0x98, 0x97,
166 	0x90, 0x8f, 0x88, 0x84, 0x82, 0x81, 0x80, 0x7c, 0x7a, 0x79,
167 	0x76, 0x75, 0x74, 0x73, 0x72, 0x71, 0x6e, 0x6d, 0x6c, 0x6b,
168 	0x6a, 0x69, 0x67, 0x66, 0x65, 0x63, 0x5c, 0x5a, 0x59, 0x56,
169 	0x55, 0x54, 0x53, 0x52, 0x51, 0x4e, 0x4d, 0x4c, 0x4b, 0x4a,
170 	0x49, 0x47, 0x46, 0x45, 0x43, 0x3c, 0x3a, 0x39, 0x36, 0x35,
171 	0x34, 0x33, 0x32, 0x31, 0x2e, 0x2d, 0x2c, 0x2b, 0x2a, 0x29,
172 	0x27, 0x26, 0x25, 0x23, 0x1f, 0x1e, 0x1d, 0x1b, 0x18, 0x17,
173 	0x10, 0x0f, 0x08, 0x04, 0x02, 0x01
174 };
175 
176 uchar_t g_sf_alpa_to_switch[] = {
177 	0x00, 0x7d, 0x7c, 0x00, 0x7b, 0x00, 0x00, 0x00, 0x7a, 0x00,
178 	0x00, 0x00, 0x00, 0x00, 0x00, 0x79, 0x78, 0x00, 0x00, 0x00,
179 	0x00, 0x00, 0x00, 0x77, 0x76, 0x00, 0x00, 0x75, 0x00, 0x74,
180 	0x73, 0x72, 0x00, 0x00, 0x00, 0x71, 0x00, 0x70, 0x6f, 0x6e,
181 	0x00, 0x6d, 0x6c, 0x6b, 0x6a, 0x69, 0x68, 0x00, 0x00, 0x67,
182 	0x66, 0x65, 0x64, 0x63, 0x62, 0x00, 0x00, 0x61, 0x60, 0x00,
183 	0x5f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x5d,
184 	0x5c, 0x5b, 0x00, 0x5a, 0x59, 0x58, 0x57, 0x56, 0x55, 0x00,
185 	0x00, 0x54, 0x53, 0x52, 0x51, 0x50, 0x4f, 0x00, 0x00, 0x4e,
186 	0x4d, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4b,
187 	0x00, 0x4a, 0x49, 0x48, 0x00, 0x47, 0x46, 0x45, 0x44, 0x43,
188 	0x42, 0x00, 0x00, 0x41, 0x40, 0x3f, 0x3e, 0x3d, 0x3c, 0x00,
189 	0x00, 0x3b, 0x3a, 0x00, 0x39, 0x00, 0x00, 0x00, 0x38, 0x37,
190 	0x36, 0x00, 0x35, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00,
191 	0x00, 0x00, 0x00, 0x33, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00,
192 	0x00, 0x31, 0x30, 0x00, 0x00, 0x2f, 0x00, 0x2e, 0x2d, 0x2c,
193 	0x00, 0x00, 0x00, 0x2b, 0x00, 0x2a, 0x29, 0x28, 0x00, 0x27,
194 	0x26, 0x25, 0x24, 0x23, 0x22, 0x00, 0x00, 0x21, 0x20, 0x1f,
195 	0x1e, 0x1d, 0x1c, 0x00, 0x00, 0x1b, 0x1a, 0x00, 0x19, 0x00,
196 	0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x17, 0x16, 0x15,
197 	0x00, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0f, 0x00, 0x00, 0x0e,
198 	0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x00, 0x00, 0x08, 0x07, 0x00,
199 	0x06, 0x00, 0x00, 0x00, 0x05, 0x04, 0x03, 0x00, 0x02, 0x00,
200 	0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
201 };
202 
203 
204 
205 /*
206  * Check if device is in the map.
207  *
208  * PARAMS:
209  *	map - loop map returned from fc port
210  *	tid - device ID for private map or 24-bit alpa for fabric map
211  *
212  * RETURNS:
213  *	 1 if device present in the map.
214  *	 0 otherwise.
215  *
216  */
217 int
218 g_device_in_map(gfc_map_t *map, int tid)
219 {
220 	int i, j;
221 	gfc_port_dev_info_t	*dev_ptr;
222 
223 	dev_ptr = map->dev_addr;
224 	if ((map->hba_addr.port_topology == FC_TOP_PUBLIC_LOOP) ||
225 		(map->hba_addr.port_topology == FC_TOP_FABRIC)) {
226 		for (i = 0; i < map->count; i++, dev_ptr++) {
227 			if (dev_ptr->
228 				gfc_port_dev.pub_port.dev_did.port_id == tid) {
229 				/* Does not count if WWN == 0 */
230 				for (j = 0; j < FC_WWN_SIZE; j++)
231 					if (dev_ptr->gfc_port_dev.pub_port.
232 						dev_pwwn.raw_wwn[j] != 0)
233 						return (1);
234 			}
235 		}
236 	} else {
237 		for (i = 0; i < map->count; i++, dev_ptr++) {
238 			if (dev_ptr->gfc_port_dev.priv_port.sf_al_pa ==
239 				(int)g_switch_to_alpa[tid]) {
240 				/* Does not count if WWN == 0 */
241 				for (j = 0; j < WWN_SIZE; j++)
242 					if (dev_ptr->gfc_port_dev.priv_port.
243 						sf_port_wwn[j] != 0)
244 						return (1);
245 			}
246 		}
247 	}
248 	return (0);
249 }
250 
251 /*
252  * Inserts any missing port wwns for mpxio device paths
253  * which are in ONLINE or STANDBY state.
254  */
255 static int
256 insert_missing_pwwn(char *phys_path, struct wwn_list_struct **wwn_list_ptr)
257 {
258 mp_pathlist_t	pathlist;
259 int	i, pathcnt, match;
260 struct	wwn_list_struct *new_wwn, *wwn_list_s, *wwn_list_found;
261 char	pwwn1[WWN_S_LEN];
262 
263 	/*
264 	 * Now check each scsi_vhci device path to find any missed
265 	 * port wwns and insert a new wwn list entry for the missed
266 	 * port wwn
267 	 */
268 	if (g_get_pathlist(phys_path, &pathlist)) {
269 		/* Free memory for pathlist before return */
270 		S_FREE(pathlist.path_info);
271 		return (L_INVALID_PATH);
272 	}
273 
274 	pathcnt = pathlist.path_count;
275 	for (i = 0; i < pathcnt; i++) {
276 		/*
277 		 * Just search for ONLINE and STANDBY paths as
278 		 * those should be the only missing wwn entries.
279 		 * There is a very small window for an offline
280 		 * to have occurred between the time we retrieved
281 		 * the device list and a call to this function.
282 		 * If that happens, we just won't add it to
283 		 * the list which is probably a good thing.
284 		 */
285 		if (pathlist.path_info[i].path_state ==
286 		    MDI_PATHINFO_STATE_ONLINE ||
287 		    pathlist.path_info[i].path_state ==
288 		    MDI_PATHINFO_STATE_STANDBY) {
289 			(void) strncpy(pwwn1, pathlist.path_info[i].path_addr,
290 				WWN_S_LEN - 1);
291 			pwwn1[WWN_S_LEN - 1] = '\0';
292 			/*
293 			 * Now search through wwn list for matching
294 			 * device path AND pwwn
295 			 * If it's found, continue to next path.
296 			 * If it's not found, add it the wwn list.
297 			 */
298 			match = 0;
299 
300 			for (wwn_list_s = *wwn_list_ptr; wwn_list_s != NULL;
301 			    wwn_list_s = wwn_list_s->wwn_next) {
302 				if (strncmp(phys_path,
303 					    wwn_list_s->physical_path,
304 					    strlen(phys_path)) == 0) {
305 					wwn_list_found = wwn_list_s;
306 					if (strncmp(pwwn1,
307 						    wwn_list_s->port_wwn_s,
308 						    WWN_S_LEN) == 0) {
309 						match++;
310 						break;
311 					}
312 				}
313 			}
314 			if (match) {
315 				continue;
316 			} else {
317 				/*
318 				 * didn't find a match but the mpxio
319 				 * device is in the list. Retrieve
320 				 * the info from the wwn_list_found
321 				 * and add it to the list.
322 				 */
323 				if ((new_wwn = (struct  wwn_list_struct *)
324 					calloc(1,
325 					sizeof (struct  wwn_list_struct)))
326 					== NULL) {
327 				    S_FREE(pathlist.path_info);
328 				    return (L_MALLOC_FAILED);
329 				}
330 				if ((new_wwn->physical_path = (char *)
331 					calloc(1,
332 					strlen(wwn_list_found->physical_path)
333 					+1)) == NULL) {
334 				    S_FREE(pathlist.path_info);
335 				    return (L_MALLOC_FAILED);
336 				}
337 				if ((new_wwn->logical_path = (char *)
338 					calloc(1,
339 					strlen(wwn_list_found->logical_path)
340 					+ 1)) == NULL) {
341 				    S_FREE(pathlist.path_info);
342 				    return (L_MALLOC_FAILED);
343 				}
344 
345 				/*
346 				 * Insert new_wwn at the beginning of the list.
347 				 */
348 				new_wwn->wwn_next = *wwn_list_ptr;
349 				(*wwn_list_ptr)->wwn_prev = new_wwn;
350 
351 				/* set new starting ptr */
352 				*wwn_list_ptr = new_wwn;
353 
354 				memcpy(new_wwn->physical_path,
355 				    wwn_list_found->physical_path,
356 					strlen(wwn_list_found->physical_path));
357 				memcpy(new_wwn->logical_path,
358 				    wwn_list_found->logical_path,
359 					strlen(wwn_list_found->logical_path));
360 				/*
361 				 * Copy found node wwn data to this new entry
362 				 * Node wwn is required for the wwn_list
363 				 * however for mpxio devices it is not
364 				 * relevant as it may apply to multiple
365 				 * target controllers, so just use what
366 				 * we already have in wwn_list_found.
367 				 */
368 				memcpy(new_wwn->node_wwn_s,
369 				    wwn_list_found->node_wwn_s, WWN_S_LEN);
370 				memcpy(new_wwn->w_node_wwn,
371 				    wwn_list_found->w_node_wwn, WWN_SIZE);
372 				new_wwn->device_type =
373 				    wwn_list_found->device_type;
374 				memcpy(new_wwn->port_wwn_s, pwwn1, WWN_S_LEN);
375 			}
376 		}
377 	}
378 	S_FREE(pathlist.path_info);
379 	return (0);
380 }
381 
382 /*
383  * gets the port wwn for a scsi_vhci device using ONLINE path priority
384  */
385 static int
386 get_scsi_vhci_port_wwn(char *phys_path, uchar_t *port_wwn)
387 {
388 mp_pathlist_t	pathlist;
389 int	i, pathcnt, found;
390 char	pwwn1[WWN_S_LEN];
391 
392 	if (g_get_pathlist(phys_path, &pathlist)) {
393 		return (L_INVALID_PATH);
394 	}
395 
396 	found = 0;
397 	pathcnt = pathlist.path_count;
398 	/*
399 	 * Look for an ONLINE path first.
400 	 * If that fails, get the STANDBY path port WWN
401 	 * If that fails, give up
402 	 */
403 	for (i = 0; found == 0 && i < pathcnt; i++) {
404 		if (pathlist.path_info[i].path_state ==
405 		    MDI_PATHINFO_STATE_ONLINE) {
406 			(void) strncpy(pwwn1, pathlist.path_info[i].path_addr,
407 				WWN_S_LEN - 1);
408 			pwwn1[WWN_S_LEN - 1] = '\0';
409 			found++;
410 		}
411 	}
412 
413 	for (i = 0; found == 0 && i < pathcnt; i++) {
414 		if (pathlist.path_info[i].path_state ==
415 		    MDI_PATHINFO_STATE_STANDBY) {
416 			(void) strncpy(pwwn1, pathlist.path_info[i].path_addr,
417 				WWN_S_LEN - 1);
418 			pwwn1[WWN_S_LEN - 1] = '\0';
419 			found++;
420 		}
421 	}
422 
423 	S_FREE(pathlist.path_info);
424 	if (found) {
425 		return (string_to_wwn((uchar_t *)pwwn1, port_wwn));
426 	} else {
427 		return (-1);
428 	}
429 }
430 
431 /*
432  * searches wwn_list_found for the pwwn passed in
433  * and sets the corresponding nwwn on return.
434  * If no match is found, -1 is returned and nwwn is not set.
435  */
436 static int
437 search_wwn_entry(struct wwn_list_found_struct *wwn_list_found, uchar_t *pwwn,
438 		uchar_t *nwwn)
439 {
440 struct	wwn_list_found_struct *wwn_list_s;
441 
442 	for (wwn_list_s = wwn_list_found; wwn_list_s != NULL;
443 	    wwn_list_s = wwn_list_s->wwn_next) {
444 		if (memcmp(pwwn,
445 			    wwn_list_s->port_wwn, WWN_SIZE) == 0) {
446 			memcpy(nwwn, wwn_list_s->node_wwn, WWN_SIZE);
447 			return (0);
448 		}
449 	}
450 	return (-1);
451 }
452 
453 /*
454  * adds a nwwn, pwwn entry to the next entry in wwn_list_found list
455  */
456 static int
457 add_wwn_entry(struct wwn_list_found_struct **wwn_list_found, uchar_t *pwwn,
458 		uchar_t *nwwn)
459 {
460 struct wwn_list_found_struct *new_wwn, *temp_wwn_list_found = NULL;
461 
462 	/* Got wwns, load data in list */
463 	if ((new_wwn = (struct  wwn_list_found_struct *)
464 		calloc(1, sizeof (struct  wwn_list_found_struct)))
465 			== NULL) {
466 		return (L_MALLOC_FAILED);
467 	}
468 
469 	memcpy(new_wwn->node_wwn, nwwn, WWN_SIZE);
470 	memcpy(new_wwn->port_wwn, pwwn, WWN_SIZE);
471 
472 	/*
473 	 * Insert new_wwn in the list
474 	 */
475 	if (*wwn_list_found != NULL) {
476 		temp_wwn_list_found = (*wwn_list_found)->wwn_next;
477 		(*wwn_list_found)->wwn_next = new_wwn;
478 	} else {
479 		*wwn_list_found = new_wwn;
480 	}
481 	new_wwn->wwn_next = temp_wwn_list_found;
482 
483 	return (0);
484 }
485 
486 
487 /*
488  * Create a linked list of all the WWN's for all FC_AL disks and
489  * tapes that are attached to this host.
490  *
491  * RETURN VALUES: 0 O.K.
492  *
493  * wwn_list pointer:
494  *			NULL: No devices found.
495  *			!NULL: Devices found
496  *                      wwn_list points to a linked list of wwn's.
497  */
498 int
499 g_get_wwn_list(struct wwn_list_struct **wwn_list_ptr, int verbose)
500 {
501 struct wwn_list_struct *wwn_list_p = NULL, *wwn_list_tmp_p = NULL;
502 struct wwn_list_found_struct *wwn_list_found = NULL;
503 int err;
504 int al_pa;
505 uchar_t node_wwn[WWN_SIZE], port_wwn[WWN_SIZE];
506 hrtime_t	start_time, end_time;
507 char *env = NULL;
508 
509 	/* return L_NULL_WWN_LIST if wwn_list_ptr is NULL */
510 	if (wwn_list_ptr == NULL) {
511 		return (L_NULL_WWN_LIST);
512 	}
513 
514 	if ((env = getenv("_LUX_T_DEBUG")) != NULL) {
515 		start_time = gethrtime();
516 	}
517 
518 	if ((err = g_devices_get_all(wwn_list_ptr))
519 		!= 0) {
520 		return (err);
521 	}
522 
523 	/*
524 	 * retain backward compatibility with g_get_wwn_list
525 	 * and retrieve the WWN for scsi_vhci devices in the
526 	 * same fashion
527 	 * Note that for scsi_vhci devices, the wwn fields are
528 	 * not relevant but in the previous versions
529 	 * we loaded the wwns so...
530 	 */
531 	wwn_list_p = *wwn_list_ptr;
532 	while (wwn_list_p != NULL) {
533 	    if (strstr(wwn_list_p->physical_path, SCSI_VHCI) != NULL) {
534 		/* get port wwn of first ONLINE, STANDBY */
535 		if ((get_scsi_vhci_port_wwn(wwn_list_p->physical_path,
536 			port_wwn)) == 0) {
537 		    if ((search_wwn_entry(wwn_list_found, port_wwn,
538 			node_wwn)) != 0) {
539 			if ((err = get_wwns(wwn_list_p->physical_path, port_wwn,
540 				node_wwn, &al_pa, &wwn_list_found)) != 0) {
541 				g_free_wwn_list_found(&wwn_list_found);
542 				return (err);
543 			}
544 		    }
545 		} else {
546 		    /* Use g_get_wwn as a last resort */
547 		    if ((err = g_get_wwn(wwn_list_p->physical_path, port_wwn,
548 			node_wwn, &al_pa, 0)) != 0) {
549 			/*
550 			 * this is a bad WWN.  remove it from the
551 			 * wwn_list.
552 			 *
553 			 * After removing the bad WWN, wwn_list_p
554 			 * should point to the next node in the list
555 			 */
556 			if ((wwn_list_p->wwn_prev == NULL) &&
557 			    (wwn_list_p->wwn_next == NULL)) {
558 			    *wwn_list_ptr = NULL;
559 			    free(wwn_list_p);
560 			    g_free_wwn_list_found(&wwn_list_found);
561 			    return (L_NO_DEVICES_FOUND);
562 			} else if (wwn_list_p->wwn_prev == NULL) {
563 			    *wwn_list_ptr = wwn_list_p->wwn_next;
564 			    free(wwn_list_p);
565 			    wwn_list_p = *wwn_list_ptr;
566 			    wwn_list_p->wwn_prev = NULL;
567 			} else if (wwn_list_p->wwn_next == NULL) {
568 			    wwn_list_p->wwn_prev->wwn_next = NULL;
569 			    free(wwn_list_p);
570 			    wwn_list_p = NULL;
571 			} else {
572 			    wwn_list_tmp_p = wwn_list_p->wwn_next;
573 			    wwn_list_p->wwn_prev->wwn_next =
574 				wwn_list_p->wwn_next;
575 			    wwn_list_p->wwn_next->wwn_prev =
576 				wwn_list_p->wwn_prev;
577 			    free(wwn_list_p);
578 			    wwn_list_p = wwn_list_tmp_p;
579 			}
580 			continue;
581 		    }
582 		}
583 		copy_wwn_data_to_str(wwn_list_p->node_wwn_s, node_wwn);
584 		copy_wwn_data_to_str(wwn_list_p->port_wwn_s, port_wwn);
585 		memcpy(wwn_list_p->w_node_wwn, node_wwn, WWN_SIZE);
586 	    }
587 	    wwn_list_p = wwn_list_p->wwn_next;
588 	}
589 	g_free_wwn_list_found(&wwn_list_found);
590 
591 	/*
592 	 * Now go through the list one more time to add entries for
593 	 * any missing port wwns.
594 	 * This allows a search on port wwn for any paths which are
595 	 * ONLINE or STANDBY. We don't care about OFFLINE as those won't
596 	 * and should not show up in the list
597 	 */
598 	for (wwn_list_p = *wwn_list_ptr; wwn_list_p != NULL;
599 	    wwn_list_p = wwn_list_p->wwn_next) {
600 	    if (strstr(wwn_list_p->physical_path, SCSI_VHCI) != NULL) {
601 		if ((err = insert_missing_pwwn(wwn_list_p->physical_path,
602 				    wwn_list_ptr)) != 0)
603 			return (err);
604 	    }
605 	}
606 
607 	if (env != NULL) {
608 		end_time = gethrtime();
609 		fprintf(stdout, "      g_get_wwn_list: "
610 		"\t\tTime = %lld millisec\n",
611 		(end_time - start_time)/1000000);
612 	}
613 	return (0);
614 
615 }
616 
617 int
618 g_devices_get_all(struct wwn_list_struct **wwn_list_ptr)
619 {
620 struct wwn_list_struct *tape_ptr = NULL;
621 struct wwn_list_struct *tmp;
622 int err;
623 di_node_t root;
624 hrtime_t	start_time, end_time;
625 char *env = NULL;
626 
627 	if ((env = getenv("_LUX_T_DEBUG")) != NULL) {
628 		start_time = gethrtime();
629 	}
630 
631 	/*
632 	 * Try to prime di_drv_first_node()
633 	 * If there are no nodes bound, di_drv_first_node()
634 	 * will return nothing.
635 	 */
636 	init_drv(DEV_TAPE_DIR, DIR_MATCH_ST, SLSH_DRV_NAME_ST);
637 	init_drv(DEV_RDIR, DIR_MATCH_SSD, SLSH_DRV_NAME_SSD);
638 
639 	if ((root = di_init("/", DINFOCPYALL)) == DI_NODE_NIL) {
640 		return (L_DEV_SNAPSHOT_FAILED);
641 	}
642 
643 	if (env != NULL) {
644 		end_time = gethrtime();
645 		fprintf(stdout, "      di_init - /:  "
646 		"\t\tTime = %lld millisec\n",
647 		(end_time - start_time)/1000000);
648 	}
649 
650 	if (env != NULL) {
651 		start_time = gethrtime();
652 	}
653 
654 	if ((err = devices_get_all(root, SSD_DRVR_NAME, SSD_MINOR_NAME,
655 			wwn_list_ptr)) != 0) {
656 		if (err != L_NO_DEVICES_FOUND) {
657 			di_fini(root);
658 			g_free_wwn_list(&tape_ptr);
659 			g_free_wwn_list(wwn_list_ptr);
660 			return (err);
661 		}
662 	}
663 
664 	if (env != NULL) {
665 		end_time = gethrtime();
666 		fprintf(stdout, "      devices_get_all - ssd:  "
667 		"\t\tTime = %lld millisec\n",
668 		(end_time - start_time)/1000000);
669 	}
670 
671 	if (env != NULL) {
672 		start_time = gethrtime();
673 	}
674 
675 	if ((err = devices_get_all(root, ST_DRVR_NAME, ST_MINOR_NAME,
676 			&tape_ptr)) != 0) {
677 		di_fini(root);
678 		if (err != L_NO_DEVICES_FOUND) {
679 			g_free_wwn_list(&tape_ptr);
680 			g_free_wwn_list(wwn_list_ptr);
681 			return (err);
682 		} else {
683 			/*
684 			 * if *wwn_list_ptr == NULL
685 			 * we have disks but no tapes
686 			 * Just return
687 			 */
688 			if (*wwn_list_ptr != NULL) {
689 				return (0);
690 			} else {
691 				/*
692 				 * No disks or tapes
693 				 */
694 				g_free_wwn_list(&tape_ptr);
695 				g_free_wwn_list(wwn_list_ptr);
696 				return (err);
697 			}
698 		}
699 	}
700 
701 	if (env != NULL) {
702 		end_time = gethrtime();
703 		fprintf(stdout, "      devices_get_all - st: "
704 		"\t\tTime = %lld millisec\n",
705 		(end_time - start_time)/1000000);
706 	}
707 
708 	/* Now link the two together */
709 	if (*wwn_list_ptr != NULL) { /* We have both disks and tapes */
710 		/* Walk to the end of it */
711 		for (tmp = *wwn_list_ptr; tmp->wwn_next != NULL;
712 			tmp = tmp->wwn_next);
713 		tmp->wwn_next = tape_ptr;
714 		tape_ptr->wwn_prev = tmp;
715 		di_fini(root);
716 		return (0);
717 	}
718 
719 	/* else we have no disks */
720 	*wwn_list_ptr = tape_ptr;
721 	di_fini(root);
722 	return (0);
723 }
724 
725 void
726 g_free_wwn_list_found(struct wwn_list_found_struct **wwn_list_found) {
727 	WWN_list_found	    *next = NULL;
728 
729 	/* return if wwn_list_found is NULL */
730 	if (wwn_list_found == NULL) {
731 		return;
732 	}
733 	for (; *wwn_list_found != NULL; *wwn_list_found = next) {
734 		next = (*wwn_list_found)->wwn_next;
735 		g_destroy_data(*wwn_list_found);
736 		*wwn_list_found = NULL;
737 	}
738 }
739 
740 void
741 g_free_wwn_list(struct wwn_list_struct **wwn_list)
742 {
743 WWN_list	*next = NULL;
744 
745 	/* return if wwn_list is NULL */
746 	if (wwn_list == NULL) {
747 		return;
748 	}
749 
750 	for (; *wwn_list != NULL; *wwn_list = next) {
751 		next = (*wwn_list)->wwn_next;
752 		if ((*wwn_list)->physical_path != NULL)
753 			(void) g_destroy_data((*wwn_list)->physical_path);
754 		if ((*wwn_list)->logical_path != NULL)
755 			(void) g_destroy_data((*wwn_list)->logical_path);
756 		(void) g_destroy_data(*wwn_list);
757 	}
758 	wwn_list = NULL;
759 }
760 
761 
762 
763 
764 void
765 g_sort_wwn_list(struct wwn_list_struct **wwn_list)
766 {
767 	int			i, n;
768 	struct wwn_list_struct	**wwn_list_array;
769 	struct wwn_list_struct	*wwn_list_ptr;
770 	struct wwn_list_struct	**wwn_list_array_ptr1;
771 	struct wwn_list_struct	**wwn_list_array_ptr2;
772 
773 	/*
774 	 * Count the number of wwn_list in the list
775 	 */
776 	for (n = 0,  wwn_list_ptr = *wwn_list;
777 	    wwn_list_ptr != NULL;
778 	    wwn_list_ptr = wwn_list_ptr->wwn_next) {
779 		n++;
780 	}
781 	if (n <= 1) {
782 		return;
783 	}
784 
785 	/*
786 	 * Allocate a simple wwn_list array and fill it in
787 	 */
788 	wwn_list_array = (struct wwn_list_struct **)
789 	    g_zalloc((n+1) * sizeof (struct wwn_list_struct *));
790 
791 	wwn_list_array_ptr1 = wwn_list_array;
792 	for (wwn_list_ptr = *wwn_list;
793 	    wwn_list_ptr != NULL;
794 	    wwn_list_ptr = wwn_list_ptr->wwn_next) {
795 		*wwn_list_array_ptr1++ = wwn_list_ptr;
796 	}
797 	*wwn_list_array_ptr1 = NULL;
798 
799 	/*
800 	 * Sort the wwn_list array
801 	 */
802 	qsort((void *) wwn_list_array, n,
803 	    sizeof (struct wwn_list_struct *), wwn_list_name_compare);
804 
805 	/*
806 	 * Rebuild the linked list wwn_list structure
807 	 */
808 	wwn_list_array_ptr1 = wwn_list_array;
809 	*wwn_list = *wwn_list_array_ptr1;
810 	wwn_list_array_ptr2 = wwn_list_array_ptr1 + 1;
811 	(*wwn_list_array_ptr1)->wwn_prev = NULL;
812 	for (i = 0; i < n - 1; i++) {
813 	    (*wwn_list_array_ptr2)->wwn_prev = *wwn_list_array_ptr1;
814 	    (*wwn_list_array_ptr1++)->wwn_next = *wwn_list_array_ptr2++;
815 	}
816 	(*wwn_list_array_ptr1)->wwn_next = NULL;
817 
818 	/*
819 	 * Clean up
820 	 */
821 	(void) g_destroy_data((void *)wwn_list_array);
822 }
823 
824 int
825 wwn_list_name_compare(const void *arg1, const void *arg2)
826 {
827 	char	*s1, *s2;
828 	int	n1, n2;
829 	char	*p1, *p2;
830 
831 	s1 = (*((struct wwn_list_struct **)arg1))->logical_path;
832 	s2 = (*((struct wwn_list_struct **)arg2))->logical_path;
833 	for (;;) {
834 		if (*s1 == 0 || *s2 == 0)
835 			break;
836 		if ((isdigit(*s1) && isdigit(*s2))) {
837 			n1 = strtol(s1, &p1, 10);
838 			n2 = strtol(s2, &p2, 10);
839 			if (n1 != n2) {
840 				return (n1 - n2);
841 			}
842 			s1 = p1;
843 			s2 = p2;
844 		} else if (*s1 != *s2) {
845 			break;
846 		} else {
847 			s1++;
848 			s2++;
849 		}
850 	}
851 	return (*s1 - *s2);
852 }
853 
854 /*
855  * Get the limited map for FC4 devices.
856  * This function is specific to FC4
857  * devices and doesn't work for FC (leadville) devices.
858  *
859  * RETURN VALUES:
860  *	0	 O.K.
861  *	non-zero otherwise
862  *
863  * lilpmap *map_ptr:
864  *		NULL: No devices found
865  *		!NULL: if devices found
866  */
867 int
868 g_get_limited_map(char *path, struct lilpmap *map_ptr, int verbose)
869 {
870 int	fd, i;
871 char	drvr_path[MAXPATHLEN];
872 struct	stat	stbuf;
873 
874 
875 	/* initialize map */
876 	(void) memset(map_ptr, 0, sizeof (struct lilpmap));
877 
878 	(void) strcpy(drvr_path, path);
879 	/*
880 	 * Get the path to the :devctl driver
881 	 *
882 	 * This assumes the path looks something like this:
883 	 * /devices/sbus@1f,0/SUNW,socal@1,0:1
884 	 * or
885 	 * /devices/sbus@1f,0/SUNW,socal@1,0
886 	 * or
887 	 * a 1 level PCI type driver
888 	 */
889 	if (stat(drvr_path, &stbuf) < 0) {
890 		return (L_LSTAT_ERROR);
891 	}
892 	if ((stbuf.st_mode & S_IFMT) == S_IFDIR) {
893 		/* append a port. Just try 0 since they did not give us one */
894 		(void) strcat(drvr_path, ":0");
895 	}
896 
897 	P_DPRINTF("  g_get_limited_map: Geting drive map from:"
898 		" %s\n", drvr_path);
899 
900 	/* open controller */
901 	if ((fd = g_object_open(drvr_path, O_NDELAY | O_RDONLY)) == -1)
902 		return (L_OPEN_PATH_FAIL);
903 
904 	if (ioctl(fd, FCIO_GETMAP, map_ptr) != 0) {
905 		I_DPRINTF("  FCIO_GETMAP ioctl failed\n");
906 		(void) close(fd);
907 		return (L_FCIO_GETMAP_IOCTL_FAIL);
908 	}
909 	(void) close(fd);
910 
911 	/*
912 	 * Check for reasonableness.
913 	 */
914 	if ((map_ptr->lilp_length > 126) || (map_ptr->lilp_magic != 0x1107)) {
915 		return (L_INVALID_LOOP_MAP);
916 	}
917 	for (i = 0; i < (uint_t)map_ptr->lilp_length; i++) {
918 		if (map_ptr->lilp_list[i] > 0xef) {
919 			return (L_INVALID_LOOP_MAP);
920 		}
921 	}
922 
923 	return (0);
924 }
925 
926 
927 /*
928  * For leadville specific HBA's ONLY.
929  * Get the host specific parameters,
930  * al_pa, hard address, node/port WWN etc.
931  *
932  * OUTPUT:
933  *	fc_port_dev_t structure.
934  *
935  * RETURNS:
936  *	0	if  OK
937  *	non-zero in case of error.
938  */
939 int
940 g_get_host_params(char *host_path, fc_port_dev_t *host_val, int verbose)
941 {
942 int		err;
943 int		fd;
944 int		dev_type;
945 fcio_t		fcio;
946 
947 	/* return invalid path if host_path is NULL */
948 	if (host_path == NULL) {
949 		return (L_INVALID_PATH);
950 	}
951 	/* return invalid arg if host_val is NULL */
952 	if (host_val == NULL) {
953 		return (L_INVALID_ARG);
954 	}
955 
956 	dev_type = g_get_path_type(host_path);
957 	if ((dev_type == 0) || !(dev_type & FC_GEN_XPORT)) {
958 		return (L_INVALID_PATH_TYPE);
959 	}
960 	if ((fd = g_object_open(host_path, O_NDELAY | O_RDONLY)) == -1) {
961 		return (L_OPEN_PATH_FAIL);
962 	}
963 
964 	/* initialize structure */
965 	(void) memset(host_val, 0, sizeof (struct fc_port_dev));
966 
967 	fcio.fcio_cmd = FCIO_GET_HOST_PARAMS;
968 	fcio.fcio_xfer = FCIO_XFER_READ;
969 	fcio.fcio_obuf = (caddr_t)host_val;
970 	fcio.fcio_olen = sizeof (fc_port_dev_t);
971 
972 	if (g_issue_fcio_ioctl(fd, &fcio, verbose) != 0) {
973 		I_DPRINTF(" FCIO_GET_HOST_PARAMS ioctl failed.\n");
974 		(void) close(fd);
975 		return (L_FCIO_GET_HOST_PARAMS_FAIL);
976 	}
977 	(void) close(fd);
978 
979 	/* get the inquiry information for the leadville HBA. */
980 	if ((err = get_fca_inq_dtype(host_path, host_val->dev_pwwn,
981 				&host_val->dev_dtype)) != 0) {
982 		return (err);
983 	}
984 	return (0);
985 }
986 
987 
988 
989 /*
990  * Issue FCIO ioctls to the port(fp) driver.
991  * FCIO ioctl needs to be retried when it
992  * is returned with an EINVAL error, wait
993  * time between retries should be atleast
994  * WAIT_FCIO_IOCTL (too much of a time to wait!!)
995  *
996  * OUTPUT:
997  *	fcio_t structure
998  *
999  * RETURNS:
1000  *	0	 if O.K.
1001  *	non-zero otherwise.
1002  */
1003 int
1004 g_issue_fcio_ioctl(int fd, fcio_t *fcio, int verbose)
1005 {
1006 int	ntries;
1007 
1008 	for (ntries = 0; ntries < RETRY_FCIO_IOCTL; ntries++) {
1009 		if (ioctl(fd, FCIO_CMD, fcio) != 0) {
1010 			if ((errno == EAGAIN) &&
1011 				(ntries+1 < RETRY_FCIO_IOCTL)) {
1012 				/* wait WAIT_FCIO_IOCTL */
1013 				(void) usleep(WAIT_FCIO_IOCTL);
1014 				continue;
1015 			}
1016 			I_DPRINTF("FCIO ioctl failed.\n"
1017 				"Error: %s. fc_error = %d (0x%x)\n",
1018 			strerror(errno), fcio->fcio_errno, fcio->fcio_errno);
1019 			if (errno == EINVAL) {
1020 				if (fcio->fcio_errno == FC_TOOMANY) {
1021 					return (L_INVALID_DEVICE_COUNT);
1022 				} else {
1023 					return (errno);
1024 				}
1025 			}
1026 			/*
1027 			 * When port is offlined, qlc
1028 			 * returns the FC_OFFLINE error and errno
1029 			 * is set to EIO.
1030 			 * We do want to ignore this error,
1031 			 * especially when an enclosure is
1032 			 * removed from the loop.
1033 			 */
1034 			if (fcio->fcio_errno == FC_OFFLINE)
1035 				break;
1036 			return (-1);
1037 		}
1038 		break;
1039 	}
1040 
1041 	return (0);
1042 }
1043 
1044 /*
1045  * This function issues the FCP_TGT_INQUIRY ioctl to
1046  * the fcp module
1047  *
1048  * OUTPUT:
1049  *	fcp_ioctl structure in fcp_data is filled in by fcp
1050  *
1051  * RETURN VALUES :
1052  *	0 on Success
1053  *	Non-zero otherwise
1054  */
1055 static int
1056 g_issue_fcp_ioctl(int fd, struct fcp_ioctl *fcp_data, int verbose)
1057 {
1058 	int 			num_tries = 0;
1059 	struct device_data	*dev_data = NULL;
1060 
1061 	/*
1062 	 * Issue the ioctl to FCP
1063 	 * The retries are required because the driver may
1064 	 * need some time to respond at times.
1065 	 */
1066 	while (num_tries++ < RETRY_FCP_IOCTL) {
1067 		/* if ioctl fails it is an error from Solaris operation. */
1068 		if (ioctl(fd, FCP_TGT_INQUIRY, fcp_data) == -1) {
1069 			if (errno == EAGAIN) {
1070 				(void) usleep(WAIT_FCP_IOCTL);
1071 				continue;
1072 			} else {
1073 				break;
1074 			}
1075 		}
1076 		dev_data = (struct device_data *)((void *)(fcp_data->list));
1077 		if (dev_data->dev_status == 0) {
1078 			return (0);
1079 		}
1080 
1081 		if (dev_data->dev_status == EAGAIN) {
1082 			(void) usleep(WAIT_FCP_IOCTL);
1083 			continue;
1084 		} else {
1085 			dev_data->dev0_type = DTYPE_UNKNOWN;
1086 			return (0);
1087 		}
1088 	}
1089 
1090 	return (L_FCP_TGT_INQUIRY_FAIL);
1091 }
1092 
1093 /*
1094  * Get the number of devices and also
1095  * a list of devices accessible through
1096  * the device's port as specified by path.
1097  * The calling function * is responsible for freeing the dev_list.
1098  *
1099  * Acquires inq_dtype from g_get_inq_dtype() and
1100  * stores into dev_dtype field of fc_port_dev.
1101  *
1102  * For fabric devices call FCIO_DEV_LOGIN (if necessary) to execute port login
1103  * and get inq dtype.
1104  *
1105  * dev_list:
1106  *	NULL:	  No devices found, in case of an error
1107  *	Non-NULL: Devices found.
1108  * ndevs:
1109  *	set to the number of devices
1110  *	accessible through the port.
1111  *
1112  * RETURNS:
1113  *	0	 if O.K.
1114  *	non-zero otherwise
1115  */
1116 int
1117 g_get_dev_list(char *path, fc_port_dev_t **dev_list, int *ndevs)
1118 {
1119 int		num_devices = 0;
1120 int		i, err, ulp_failure = 0, new_count = 0;
1121 int		dev_type;
1122 int		fd;
1123 char		fcapath[MAXPATHLEN];
1124 char		*char_ptr;
1125 struct	stat	stbuf;
1126 fcio_t		fcio;
1127 uint32_t	port_top;
1128 fc_port_dev_t	*dlist;
1129 
1130 	*dev_list = dlist = NULL;
1131 	(void) strcpy(fcapath, path);
1132 	/*
1133 	 * Get the path to the :devctl driver
1134 	 *
1135 	 * This assumes the path looks something like this:
1136 	 * /devices/sbus@1f,0/SUNW,socal@1,0/SUNW,sf@0,0/ses@e,0:0
1137 	 * or
1138 	 * /devices/sbus@1f,0/SUNW,socal@1,0/SUNW,sf@0,0
1139 	 * or
1140 	 * /devices/sbus@1f,0/SUNW,socal@1,0/SUNW,sf@0,0:devctl
1141 	 * or
1142 	 * a 1 level PCI type driver but still :devctl
1143 	 */
1144 	if (strstr(fcapath, DRV_NAME_SSD) || strstr(fcapath, SES_NAME)) {
1145 		if ((char_ptr = strrchr(fcapath, '/')) == NULL) {
1146 			return (L_INVALID_PATH);
1147 		}
1148 		*char_ptr = '\0';   /* Terminate sting  */
1149 		/* append controller */
1150 		(void) strcat(fcapath, FC_CTLR);
1151 	} else {
1152 		if (stat(fcapath, &stbuf) < 0) {
1153 			return (L_LSTAT_ERROR);
1154 		}
1155 		if ((stbuf.st_mode & S_IFMT) == S_IFDIR) {
1156 			/* append controller */
1157 			(void) strcat(fcapath, FC_CTLR);
1158 		}
1159 	}
1160 	dev_type = g_get_path_type(fcapath);
1161 	if ((dev_type == 0) || !(dev_type & FC_GEN_XPORT)) {
1162 		return (L_INVALID_PATH_TYPE);
1163 	}
1164 	if ((fd = g_object_open(fcapath, O_NDELAY | O_RDONLY)) == -1) {
1165 		return (L_OPEN_PATH_FAIL);
1166 	}
1167 
1168 	/*
1169 	 * Get the device list from port driver
1170 	 */
1171 	fcio.fcio_cmd = FCIO_GET_NUM_DEVS;
1172 	fcio.fcio_olen = sizeof (num_devices);
1173 	fcio.fcio_xfer = FCIO_XFER_READ;
1174 	fcio.fcio_obuf = (caddr_t)&num_devices;
1175 	if (g_issue_fcio_ioctl(fd, &fcio, 0) != 0) {
1176 		I_DPRINTF(" FCIO_GET_NUM_DEVS ioctl failed.\n");
1177 		(void) close(fd);
1178 		return (L_FCIO_GET_NUM_DEVS_FAIL);
1179 	}
1180 	if (num_devices == 0) {
1181 		*ndevs = 0;
1182 		(void) close(fd);
1183 		return (L_NO_DEVICES_FOUND);
1184 	}
1185 
1186 	if ((dlist = (fc_port_dev_t *)calloc(num_devices,
1187 				sizeof (fc_port_dev_t))) == NULL) {
1188 		(void) close(fd);
1189 		return (L_MALLOC_FAILED);
1190 	}
1191 	bzero((caddr_t)&fcio, sizeof (fcio));
1192 	/* Get the device list */
1193 	fcio.fcio_cmd = FCIO_GET_DEV_LIST;
1194 	/* Information read operation */
1195 	fcio.fcio_xfer = FCIO_XFER_READ;
1196 	fcio.fcio_olen = num_devices * sizeof (fc_port_dev_t);
1197 	fcio.fcio_obuf = (caddr_t)dlist;
1198 	/* new device count */
1199 	fcio.fcio_alen = sizeof (new_count);
1200 	fcio.fcio_abuf = (caddr_t)&new_count;
1201 	if ((err = g_issue_fcio_ioctl(fd, &fcio, 0)) != 0) {
1202 	    if (err == L_INVALID_DEVICE_COUNT) {
1203 		/*
1204 		 * original buffer was small so allocate buffer
1205 		 * with a new count and retry.
1206 		 */
1207 		free(dlist);
1208 		num_devices = new_count;
1209 		new_count = 0;
1210 		if ((dlist = (fc_port_dev_t *)calloc(num_devices,
1211 				sizeof (fc_port_dev_t))) == NULL) {
1212 			(void) close(fd);
1213 			return (L_MALLOC_FAILED);
1214 		}
1215 		fcio.fcio_cmd = FCIO_GET_DEV_LIST;
1216 		/* Information read operation */
1217 		fcio.fcio_xfer = FCIO_XFER_READ;
1218 		fcio.fcio_obuf = (caddr_t)dlist;
1219 		fcio.fcio_olen = num_devices * sizeof (fc_port_dev_t);
1220 		/* new device count */
1221 		fcio.fcio_alen = sizeof (new_count);
1222 		fcio.fcio_abuf = (caddr_t)&new_count;
1223 		if ((err = g_issue_fcio_ioctl(fd, &fcio, 0)) != 0) {
1224 		    if (err == L_INVALID_DEVICE_COUNT) {
1225 			/*
1226 			 * No more retry. There may be severe hardware
1227 			 * problem so return error here.
1228 			 */
1229 			I_DPRINTF(" Device count was %d"
1230 			" should have been %d\n",
1231 			num_devices, new_count);
1232 		    } else {
1233 			I_DPRINTF(" FCIO_GET_DEV_LIST ioctl failed.");
1234 			err = L_FCIO_GET_DEV_LIST_FAIL;
1235 		    }
1236 		    free(dlist);
1237 		    (void) close(fd);
1238 		    return (err);
1239 		}
1240 	    } else {
1241 		I_DPRINTF(" FCIO_GET_DEV_LIST ioctl failed.");
1242 		free(dlist);
1243 		(void) close(fd);
1244 		return (L_FCIO_GET_DEV_LIST_FAIL);
1245 	    }
1246 	}
1247 
1248 	/*
1249 	 * if new count is smaller than the original number from
1250 	 * FCIO_GET_NUM_DEVS, adjust new count and buffer size
1251 	 * and continue.
1252 	 */
1253 	if (new_count < num_devices) {
1254 		if (new_count == 0) {
1255 			*ndevs = 0;
1256 			(void) close(fd);
1257 			S_FREE(dlist);
1258 			return (L_NO_DEVICES_FOUND);
1259 		}
1260 		num_devices = new_count;
1261 		if ((dlist = (fc_port_dev_t *)realloc(dlist,
1262 				(new_count * sizeof (fc_port_dev_t))))
1263 				== NULL) {
1264 			S_FREE(dlist);
1265 			(void) close(fd);
1266 			return (L_MALLOC_FAILED);
1267 		}
1268 	}
1269 
1270 	*dev_list = dlist;
1271 	*ndevs = num_devices;
1272 
1273 	/* close here since fcapath will be passed to other routines. */
1274 	(void) close(fd);
1275 
1276 	if ((err = g_get_fca_port_topology(fcapath, &port_top, 0)) != 0) {
1277 		free(*dev_list);
1278 		*dev_list = NULL;
1279 		return (err);
1280 	}
1281 
1282 	/* Get the inq_dtype for each device on dev list. */
1283 	for (i = 0; i < num_devices; i++, dlist++) {
1284 		/* Get the inq_dtype for each device. */
1285 		if ((err = g_get_inq_dtype(fcapath, dlist->dev_pwwn,
1286 				&dlist->dev_dtype)) != 0) {
1287 			/*
1288 			 * if g_get_inq_dtype failed on g_dev_login
1289 			 * or g_issue_fcp_ioctl, continue to the next
1290 			 * dev on dlist.
1291 			 * L_GET_DEV_LIST_ULP_FAILURE is returned
1292 			 * after processing the whole dlist.
1293 			 */
1294 			if ((err == L_FCIO_DEV_LOGIN_FAIL) ||
1295 				(err == L_FCP_TGT_INQUIRY_FAIL)) {
1296 				ulp_failure = 1;
1297 				dlist->dev_dtype = GFC_ERR_INQ_DTYPE;
1298 			} else {
1299 				(void) free(*dev_list);
1300 				*dev_list = NULL;
1301 				return (err);
1302 			}
1303 		}
1304 	}
1305 
1306 	if (ulp_failure) {
1307 		return (L_GET_DEV_LIST_ULP_FAILURE);
1308 	} else {
1309 		return (0);
1310 	}
1311 }
1312 
1313 
1314 /* Constant used by g_get_inq_dtype() */
1315 #define	FCP_PATH	"/devices/pseudo/fcp@0:fcp"
1316 
1317 /*
1318  * Gets the inq_dtype for devices on the fabric FC driver
1319  * through an ioctl to the FCP module.
1320  *
1321  * OUTPUT:
1322  *	inq_dtype is set to the dtype on success
1323  *
1324  * RETURN VALUES:
1325  *	0 on Success
1326  *	Non-zero on error
1327  */
1328 int
1329 g_get_inq_dtype(char *fcapath, la_wwn_t pwwn, uchar_t *inq_dtype)
1330 {
1331 	int			dev_type, fd;
1332 	int			err, fcp_fd;
1333 	uint32_t		state;
1334 	uint32_t		port_top = 0;
1335 	struct fcp_ioctl	fcp_data;
1336 	struct device_data	inq_data;
1337 	struct stat		sbuf;
1338 
1339 	dev_type = g_get_path_type(fcapath);
1340 	if ((dev_type == 0) || !(dev_type & FC_GEN_XPORT)) {
1341 		return (L_INVALID_PATH_TYPE);
1342 	}
1343 
1344 	if ((err = g_get_fca_port_topology(fcapath, &port_top, 0)) != 0) {
1345 		return (err);
1346 	}
1347 
1348 	if ((port_top == FC_TOP_FABRIC) || (port_top == FC_TOP_PUBLIC_LOOP)) {
1349 		/*
1350 		 * if there is an error on getting port state we will
1351 		 * continue to login.
1352 		 * state can be either of
1353 		 * PORT_DEVICE_INVALID, PORT_DEVICE_VALID,
1354 		 * PORT_DEVICE_LOGGED_IN.  Trying port login
1355 		 * unless already logged in.
1356 		 * It will be examined if there is an adverse
1357 		 * effect on invalid state device.
1358 		 */
1359 		if (((err = g_get_dev_port_state(fcapath, pwwn, &state))
1360 				!= 0) || (state != PORT_DEVICE_LOGGED_IN)) {
1361 			/* do port login to fabric device.  */
1362 			if ((err = g_dev_login(fcapath, pwwn)) != 0) {
1363 				return (err);
1364 			}
1365 		}
1366 	}
1367 
1368 	if ((fd = g_object_open(fcapath, O_NDELAY | O_RDONLY)) == -1)
1369 		return (L_OPEN_PATH_FAIL);
1370 
1371 	if (fstat(fd, &sbuf) == -1) {
1372 		(void) close(fd);
1373 		return (L_FSTAT_ERROR);
1374 	}
1375 
1376 	if ((fcp_fd = g_object_open(FCP_PATH, O_RDONLY)) == -1) {
1377 		(void) close(fd);
1378 		return (L_OPEN_PATH_FAIL);
1379 	}
1380 
1381 	/* Get the minor number for an fp instance */
1382 	fcp_data.fp_minor = minor(sbuf.st_rdev);
1383 
1384 	fcp_data.listlen = 1;
1385 	inq_data.dev_pwwn = pwwn;	/* The port WWN as passed */
1386 	fcp_data.list = (caddr_t)&inq_data;
1387 
1388 	if (err = g_issue_fcp_ioctl(fcp_fd, &fcp_data, 0)) {
1389 		close(fd);
1390 		close(fcp_fd);
1391 		return (err);
1392 	}
1393 	*inq_dtype = inq_data.dev0_type;
1394 
1395 	close(fd);
1396 	close(fcp_fd);
1397 
1398 	return (err);
1399 }
1400 
1401 /*
1402  * Gets the inq_dtype for devices on the fabric FC driver
1403  * through an ioctl to the FCP module.
1404  *
1405  * This is exactly same as g_get_inq_dtype except that it does not do
1406  * g_dev_login(). That is for the case when the FCA tries to get its own
1407  * inq_dtype and in such a case, it cannot PLOGI into itself.
1408  *
1409  * OUTPUT:
1410  *	inq_dtype is set to the dtype on success
1411  *
1412  * RETURN VALUES:
1413  *	0 on Success
1414  *	Non-zero on error
1415  */
1416 static int
1417 get_fca_inq_dtype(char *fcapath, la_wwn_t pwwn, uchar_t *inq_dtype)
1418 {
1419 	int			dev_type, fd;
1420 	int			err, fcp_fd;
1421 	struct fcp_ioctl	fcp_data;
1422 	struct device_data	inq_data;
1423 	struct stat		sbuf;
1424 
1425 	dev_type = g_get_path_type(fcapath);
1426 	if ((dev_type == 0) || !(dev_type & FC_GEN_XPORT)) {
1427 		return (L_INVALID_PATH_TYPE);
1428 	}
1429 
1430 	if ((fd = g_object_open(fcapath, O_NDELAY | O_RDONLY)) == -1) {
1431 		return (L_OPEN_PATH_FAIL);
1432 	}
1433 
1434 	if (fstat(fd, &sbuf) == -1) {
1435 		(void) close(fd);
1436 		return (L_FSTAT_ERROR);
1437 	}
1438 
1439 	if ((fcp_fd = g_object_open(FCP_PATH, O_RDONLY)) == -1) {
1440 		(void) close(fd);
1441 		return (L_OPEN_PATH_FAIL);
1442 	}
1443 
1444 	/* Get the minor number for an fp instance */
1445 	fcp_data.fp_minor = minor(sbuf.st_rdev);
1446 
1447 	fcp_data.listlen = 1;
1448 	inq_data.dev_pwwn = pwwn;	/* The port WWN as passed */
1449 	fcp_data.list = (caddr_t)&inq_data;
1450 
1451 	if (err = g_issue_fcp_ioctl(fcp_fd, &fcp_data, 0)) {
1452 		close(fd);
1453 		close(fcp_fd);
1454 		return (err);
1455 	}
1456 	*inq_dtype = inq_data.dev0_type;
1457 
1458 	close(fd);
1459 	close(fcp_fd);
1460 
1461 	return (0);
1462 }
1463 
1464 /*
1465  * This function returns the traditional g_get_dev_map. Device list
1466  * and local hba seperate.
1467  */
1468 int
1469 g_get_dev_map(char *path, gfc_map_t *map_ptr, int verbose)
1470 {
1471 	return (create_map(path, map_ptr, verbose, MAP_FORMAT_STANDARD));
1472 }
1473 
1474 /*
1475  * This function returns the device map with local hba in physical
1476  * order.  Note: Physical order is only returned properly for
1477  * private loop. local hba is also included seperate
1478  */
1479 int
1480 g_get_lilp_map(char *path, gfc_map_t *map_ptr, int verbose)
1481 {
1482 	return (create_map(path, map_ptr, verbose, MAP_FORMAT_LILP));
1483 }
1484 
1485 /*
1486  * Gets device map from nexus driver
1487  *
1488  * PARAMS:
1489  *	path -	must be the physical path to a device
1490  *	map  -	loop map returned from fc port.
1491  *	verbose - options.
1492  *
1493  * LOGIC:
1494  *	1. check the validity of path via g_get_path_type.
1495  *	2. If FC path, get the topology of the path via
1496  *		g_get_fca_port_topology.
1497  *
1498  *	3. If FC type(Leadville statck)
1499  *		g_get_dev_list to get the device node list of fc_port_dev_t.
1500  *		g_get_host_params to get the fca port node of fc_port_dev_t.
1501  *
1502  *		Case of fabric or public loop topology
1503  *			Check if the port id > 0xffff.
1504  *			Move device node and fca port node to
1505  *			gfc_map structure via gfc_port_dev_info_t
1506  *			pub_port union.
1507  *			Issue g_get_inq_dtype to get FCP inquiry data
1508  *			and store it into gfc_port_dev_info_t.
1509  *
1510  *		Case of private loop topology
1511  *			Check if the port id < 0xff.
1512  *			Move device node and fca port node to
1513  *			gfc_map structure via gfc_port_dev_info_t
1514  *			priv_port union.
1515  *			Issue g_get_inq_dtype to get FCP inquiry data
1516  *			and store it into gfc_port_dev_info_t.
1517  *
1518  *	   else FC4 type(socal/sf or ifp stack)
1519  *		SFIOCGMAP ioctl to get the device and hba nodes of
1520  *			sf_addr_pair_t.
1521  *
1522  *
1523  * RETURNS:
1524  *	0	: if OK
1525  *	non-zero: otherwise
1526  */
1527 int
1528 create_map(char *path, gfc_map_t *map_ptr, int verbose, int map_type)
1529 {
1530 int		fd, i, j, num_devices = 0, err, pathcnt = 1;
1531 char		drvr_path[MAXPATHLEN], drvr_path0[MAXPATHLEN];
1532 char		*char_ptr;
1533 struct stat	stbuf;
1534 fc_port_dev_t	*dev_list, *dlistptr;
1535 uint32_t	hba_port_top = 0;
1536 uint_t		dev_type;
1537 sf_al_map_t	sf_map;
1538 gfc_port_dev_info_t	*dev_ptr;
1539 fc_port_dev_t	fp_hba_port;
1540 mp_pathlist_t	pathlist;
1541 int		p_on = 0, p_st = 0;
1542 
1543 	/* return invalid path if path is NULL */
1544 	if (path == NULL) {
1545 		return (L_INVALID_PATH);
1546 	}
1547 	/* return invalid arg if map_ptr is NULL */
1548 	if (map_ptr == NULL) {
1549 		return (L_INVALID_ARG);
1550 	}
1551 
1552 	map_ptr->dev_addr = NULL;
1553 	map_ptr->count = 0;
1554 	(void) strcpy(drvr_path, path);
1555 	/*
1556 	 * Get the path to the :devctl driver
1557 	 *
1558 	 * This assumes the path looks something like this:
1559 	 * /devices/sbus@1f,0/SUNW,socal@1,0/SUNW,sf@0,0/ses@e,0:0
1560 	 * or
1561 	 * /devices/sbus@1f,0/SUNW,socal@1,0/SUNW,sf@0,0
1562 	 * or
1563 	 * /devices/sbus@1f,0/SUNW,socal@1,0/SUNW,sf@0,0:devctl
1564 	 * or
1565 	 * a 1 level PCI type driver but still :devctl
1566 	 */
1567 	if (strstr(path, SCSI_VHCI)) {
1568 		(void) strcpy(drvr_path0, path);
1569 		if (g_get_pathlist(drvr_path0, &pathlist)) {
1570 			return (L_INVALID_PATH);
1571 		}
1572 		pathcnt = pathlist.path_count;
1573 		p_on = p_st = 0;
1574 		for (i = 0; i < pathcnt; i++) {
1575 			if (pathlist.path_info[i].path_state < MAXPATHSTATE) {
1576 				if (pathlist.path_info[i].path_state ==
1577 					MDI_PATHINFO_STATE_ONLINE) {
1578 					p_on = i;
1579 					break;
1580 				} else if (pathlist.path_info[i].path_state ==
1581 					MDI_PATHINFO_STATE_STANDBY) {
1582 					p_st = i;
1583 				}
1584 			}
1585 		}
1586 		if (pathlist.path_info[p_on].path_state ==
1587 		    MDI_PATHINFO_STATE_ONLINE) {
1588 			/* on_line path */
1589 			(void) strcpy(drvr_path,
1590 				pathlist.path_info[p_on].path_hba);
1591 		} else {
1592 			/* standby or path0 */
1593 			(void) strcpy(drvr_path,
1594 				pathlist.path_info[p_st].path_hba);
1595 		}
1596 		free(pathlist.path_info);
1597 		(void) strcat(drvr_path, FC_CTLR);
1598 	} else {
1599 		(void) strcpy(drvr_path, path);
1600 		if (strstr(drvr_path, DRV_NAME_SSD) ||
1601 			strstr(drvr_path, SES_NAME) ||
1602 			strstr(drvr_path, DRV_NAME_ST)) {
1603 			if ((char_ptr = strrchr(drvr_path, '/')) == NULL) {
1604 				return (L_INVALID_PATH);
1605 			}
1606 			*char_ptr = '\0';   /* Terminate sting  */
1607 			/* append controller */
1608 			(void) strcat(drvr_path, FC_CTLR);
1609 		} else {
1610 			if (stat(drvr_path, &stbuf) < 0) {
1611 				return (L_LSTAT_ERROR);
1612 			}
1613 			if ((stbuf.st_mode & S_IFMT) == S_IFDIR) {
1614 				/* append controller */
1615 				(void) strcat(drvr_path, FC_CTLR);
1616 			}
1617 		}
1618 	}
1619 
1620 	P_DPRINTF("  g_get_dev_map: Geting drive map from:"
1621 		" %s\n", drvr_path);
1622 
1623 	dev_type = g_get_path_type(drvr_path);
1624 	if ((dev_type == 0) || !(dev_type & XPORT_MASK)) {
1625 		return (L_INVALID_PATH_TYPE);
1626 	}
1627 
1628 	/* get fiber topology */
1629 	if ((err = g_get_fca_port_topology(drvr_path,
1630 			&hba_port_top, verbose)) != 0) {
1631 		return (err);
1632 	}
1633 
1634 	/* for FC devices. */
1635 	if (dev_type & FC_FCA_MASK) {
1636 		/*
1637 		 * if g_get_dev_list fails with L_NO_DEVICES_FOUND
1638 		 * we still want to call g_get_host_params to try to find the
1639 		 * HBA.  If we do not see any HBAs on the loop, the
1640 		 * g_get_host_params will fail when it trys to issue the target
1641 		 * inquiry ioctl.  In this case, we would still like to return
1642 		 * L_NO_DEVICES_FOUND.
1643 		 *
1644 		 * If g_get_dev_list fails with L_NO_DEVICES_FOUND and
1645 		 * g_get_host_params fails, the function returns
1646 		 * L_NO_DEVICES_FOUND
1647 		 */
1648 		if ((err = g_get_dev_list(drvr_path, &dev_list,
1649 				&num_devices)) != 0) {
1650 			/*
1651 			 * g_get_dev_map doesn't allow ulp failure
1652 			 * to continue thus we need to free dev_list
1653 			 * here.
1654 			 */
1655 			if (err == L_GET_DEV_LIST_ULP_FAILURE) {
1656 				(void) free(dev_list);
1657 			}
1658 			if (err != L_NO_DEVICES_FOUND) {
1659 				return (err);
1660 			}
1661 		}
1662 
1663 		/* Get local HBA information */
1664 		if ((err = g_get_host_params(drvr_path, &fp_hba_port,
1665 				verbose)) != 0) {
1666 			(void) free(dev_list);
1667 			if (num_devices == 0)
1668 				return (L_NO_DEVICES_FOUND);
1669 			else
1670 				return (err);
1671 		}
1672 
1673 		/* If devices, other than local HBA are found	*/
1674 		/* allocate space for them in the gfc_map.	*/
1675 		if (num_devices > 0) {
1676 
1677 			/* If map type is on MAP_FORMAT_LILP we need	*/
1678 			/* to add space for the local HBA		*/
1679 			if (map_type == MAP_FORMAT_LILP) {
1680 				map_ptr->count = ++num_devices;
1681 			} else {
1682 				map_ptr->count = num_devices;
1683 			}
1684 
1685 			if ((map_ptr->dev_addr = (gfc_port_dev_info_t *)
1686 			    calloc(map_ptr->count,
1687 				sizeof (gfc_port_dev_info_t))) == NULL) {
1688 			    (void) free(dev_list);
1689 			    return (L_MALLOC_FAILED);
1690 			}
1691 		}
1692 
1693 		/* If we want the lilp map then we need to do a little	*/
1694 		/* work here.  The lilp map contains the local hba in	*/
1695 		/* the dev_addr.  Once this has been added qsort the	*/
1696 		/* dev_addr array so it's in physical order.		*/
1697 		/* The lilp map will contain the local hba in the	*/
1698 		/* dev_addr array only when num_devices > 0		*/
1699 		if (map_type == MAP_FORMAT_LILP && num_devices > 0) {
1700 
1701 			/* First we need to allocate one additional	*/
1702 			/* device to the dev_addr structure, for the 	*/
1703 			/* local hba					*/
1704 			if ((dev_list = (fc_port_dev_t *)realloc(dev_list,
1705 				(num_devices * sizeof (fc_port_dev_t))))
1706 				== NULL) {
1707 				S_FREE(dev_list);
1708 				(void) free(map_ptr->dev_addr);
1709 				map_ptr->dev_addr = NULL;
1710 				return (L_MALLOC_FAILED);
1711 			}
1712 
1713 			/* Next, copy the local hba into this new loc.	*/
1714 			if (memcpy(dev_list+(num_devices-1), &fp_hba_port,
1715 					sizeof (fc_port_dev_t)) == NULL) {
1716 				(void) free(dev_list);
1717 				(void) free(map_ptr->dev_addr);
1718 				map_ptr->dev_addr = NULL;
1719 				return (L_MEMCPY_FAILED);
1720 			}
1721 
1722 			/* Now sort by physical location		*/
1723 			qsort((void*)dev_list, num_devices,
1724 				sizeof (fc_port_dev_t), lilp_map_cmp);
1725 		}
1726 
1727 		dlistptr = dev_list;
1728 		dev_ptr = map_ptr->dev_addr;
1729 
1730 		switch (hba_port_top) {
1731 		case FC_TOP_FABRIC:
1732 		case FC_TOP_PUBLIC_LOOP:
1733 			if (fp_hba_port.dev_did.port_id <= 0xffff) {
1734 				(void) free(dlistptr);
1735 				(void) free(map_ptr->dev_addr);
1736 				map_ptr->dev_addr = NULL;
1737 				return (L_INVALID_FABRIC_ADDRESS);
1738 			} else {
1739 				map_ptr->hba_addr.port_topology = hba_port_top;
1740 				map_ptr->hba_addr.gfc_port_dev.pub_port =
1741 					fp_hba_port;
1742 			}
1743 			for (i = 0; i < num_devices; i++, dev_ptr++,
1744 					dev_list++) {
1745 				if (dev_list->dev_did.port_id <= 0xffff) {
1746 					(void) free(dlistptr);
1747 					(void) free(map_ptr->dev_addr);
1748 					map_ptr->dev_addr = NULL;
1749 					return (L_INVALID_FABRIC_ADDRESS);
1750 				} else {
1751 					dev_ptr->port_topology = hba_port_top;
1752 					dev_ptr->gfc_port_dev.pub_port =
1753 						*dev_list;
1754 				}
1755 			}
1756 			break;
1757 		case FC_TOP_PRIVATE_LOOP:
1758 			/*
1759 			 * Map the (new->old) structures here.
1760 			 * Checking (i < SF_NUM_ENTRIES_IN_MAP) just to
1761 			 * make sure that we don't overrun the map structure
1762 			 * since it can hold data for upto 126 devices.
1763 			 */
1764 			if (fp_hba_port.dev_did.port_id > 0xff) {
1765 				(void) free(dlistptr);
1766 				(void) free(map_ptr->dev_addr);
1767 				map_ptr->dev_addr = NULL;
1768 				return (L_INVALID_PRIVATE_LOOP_ADDRESS);
1769 			} else {
1770 				map_ptr->hba_addr.port_topology = hba_port_top;
1771 				map_ptr->hba_addr.gfc_port_dev.
1772 					priv_port.sf_al_pa =
1773 					(uchar_t)fp_hba_port.dev_did.port_id;
1774 				map_ptr->hba_addr.gfc_port_dev.
1775 					priv_port.sf_hard_address = (uchar_t)
1776 					fp_hba_port.dev_hard_addr.hard_addr;
1777 				for (j = 0; j < FC_WWN_SIZE; j++) {
1778 					map_ptr->hba_addr.gfc_port_dev.
1779 						priv_port.sf_node_wwn[j] =
1780 					fp_hba_port.dev_nwwn.raw_wwn[j];
1781 					map_ptr->hba_addr.gfc_port_dev.
1782 						priv_port.sf_port_wwn[j] =
1783 					fp_hba_port.dev_pwwn.raw_wwn[j];
1784 				}
1785 				map_ptr->hba_addr.gfc_port_dev.
1786 					priv_port.sf_inq_dtype =
1787 					fp_hba_port.dev_dtype;
1788 			}
1789 
1790 			for (i = 0; (i < num_devices &&
1791 					i < SF_NUM_ENTRIES_IN_MAP);
1792 					i++, dev_ptr++, dev_list++) {
1793 				/*
1794 				 * Out of 24 bits of port_id, copy only
1795 				 * 8 bits to al_pa. This works okay for
1796 				 * devices that're on a private loop.
1797 				 */
1798 				if (dev_list->dev_did.port_id > 0xff) {
1799 					(void) free(dlistptr);
1800 					(void) free(map_ptr->dev_addr);
1801 					map_ptr->dev_addr = NULL;
1802 					return (L_INVALID_PRIVATE_LOOP_ADDRESS);
1803 				}
1804 				dev_ptr->port_topology = hba_port_top;
1805 				dev_ptr->gfc_port_dev.priv_port.sf_al_pa
1806 					= (uchar_t)dev_list->dev_did.port_id;
1807 				dev_ptr->gfc_port_dev.priv_port.sf_hard_address
1808 					= (uchar_t)dev_list->dev_hard_addr.
1809 						hard_addr;
1810 				for (j = 0; j < FC_WWN_SIZE; j++) {
1811 					dev_ptr->
1812 					gfc_port_dev.priv_port.sf_node_wwn[j] =
1813 						dev_list->dev_nwwn.raw_wwn[j];
1814 					dev_ptr->
1815 					gfc_port_dev.priv_port.sf_port_wwn[j] =
1816 						dev_list->dev_pwwn.raw_wwn[j];
1817 				}
1818 				dev_ptr->gfc_port_dev.priv_port.sf_inq_dtype =
1819 					dev_list->dev_dtype;
1820 			}
1821 			break;
1822 		case FC_TOP_PT_PT:
1823 			(void) free(dlistptr);
1824 			(void) free(map_ptr->dev_addr);
1825 			map_ptr->dev_addr = NULL;
1826 			return (L_PT_PT_FC_TOP_NOT_SUPPORTED);
1827 		default:
1828 			(void) free(dlistptr);
1829 			(void) free(map_ptr->dev_addr);
1830 			map_ptr->dev_addr = NULL;
1831 			return (L_UNEXPECTED_FC_TOPOLOGY);
1832 		}	/* End of switch on port_topology */
1833 		(void) free(dlistptr);
1834 
1835 	} else {	/* sf and fc4/pci devices */
1836 		if ((fd = g_object_open(drvr_path, O_NDELAY | O_RDONLY)) == -1)
1837 			return (errno);
1838 		/* initialize map */
1839 		(void) memset(&sf_map, 0, sizeof (struct sf_al_map));
1840 		if (ioctl(fd, SFIOCGMAP, &sf_map) != 0) {
1841 			I_DPRINTF("  SFIOCGMAP ioctl failed.\n");
1842 			(void) close(fd);
1843 			return (L_SFIOCGMAP_IOCTL_FAIL);
1844 		}
1845 		/* Check for reasonableness. */
1846 		if ((sf_map.sf_count > 126) || (sf_map.sf_count < 0)) {
1847 			(void) close(fd);
1848 			return (L_INVALID_LOOP_MAP);
1849 		}
1850 		if (sf_map.sf_count == 0) {
1851 			(void) close(fd);
1852 			return (L_NO_DEVICES_FOUND);
1853 		}
1854 
1855 		map_ptr->count = sf_map.sf_count;
1856 		if ((map_ptr->dev_addr =
1857 			(gfc_port_dev_info_t *)calloc(map_ptr->count,
1858 			sizeof (gfc_port_dev_info_t))) == NULL) {
1859 			(void) close(fd);
1860 			return (L_MALLOC_FAILED);
1861 		}
1862 		dev_ptr = map_ptr->dev_addr;
1863 		for (i = 0; i < sf_map.sf_count; i++, dev_ptr++) {
1864 			if (sf_map.sf_addr_pair[i].sf_al_pa > 0xef) {
1865 				(void) free(map_ptr->dev_addr);
1866 				map_ptr->dev_addr = NULL;
1867 				(void) close(fd);
1868 				return (L_INVALID_LOOP_MAP);
1869 			}
1870 			dev_ptr->port_topology = hba_port_top;
1871 			dev_ptr->gfc_port_dev.priv_port =
1872 				sf_map.sf_addr_pair[i];
1873 		}
1874 		map_ptr->hba_addr.port_topology = hba_port_top;
1875 		map_ptr->hba_addr.gfc_port_dev.priv_port =
1876 				sf_map.sf_hba_addr;
1877 		(void) close(fd);
1878 	}
1879 
1880 	return (0);
1881 }
1882 
1883 /*
1884  * This function consturct FC proerty list using map_dev_fc_prop_list.
1885  *
1886  * port WWN, node WWN, port addr and hard addr properties is constructed.
1887  *
1888  * return 0 if OK.
1889  * otherwise returns error code.
1890  */
1891 static int
1892 update_map_dev_fc_prop(
1893 	impl_map_dev_prop_t **prop_list, uint32_t map_topo,
1894 	uchar_t *port_wwn, uchar_t *node_wwn, int port_addr,
1895 	int hard_addr)
1896 {
1897 	impl_map_dev_prop_t	*prop_ptr, *pl_start = NULL, *pl_end = NULL;
1898 	uchar_t *port_wwn_data, *node_wwn_data;
1899 	int *port_addr_data, *hard_addr_data;
1900 
1901 	/* consrtruct port addr property. */
1902 	if ((map_topo == FC_TOP_FABRIC) ||
1903 		(map_topo == FC_TOP_PUBLIC_LOOP)) {
1904 		if (port_addr <= 0xffff) {
1905 		    return (L_INVALID_FABRIC_ADDRESS);
1906 		}
1907 	} else if (map_topo == FC_TOP_PRIVATE_LOOP) {
1908 		if (port_addr > 0xff) {
1909 		    return (L_INVALID_PRIVATE_LOOP_ADDRESS);
1910 		}
1911 	}
1912 
1913 	if ((prop_ptr = (impl_map_dev_prop_t *)calloc(
1914 		1, sizeof (impl_map_dev_prop_t))) == NULL) {
1915 		return (L_MALLOC_FAILED);
1916 	}
1917 	(void) strncpy(prop_ptr->prop_name, PORT_ADDR_PROP,
1918 			strlen(PORT_ADDR_PROP));
1919 	prop_ptr->prop_type = GFC_PROP_TYPE_INT;
1920 
1921 	if ((port_addr_data = (int *)calloc(1, sizeof (int))) == NULL) {
1922 		free(prop_ptr);
1923 		return (L_MALLOC_FAILED);
1924 	}
1925 	*port_addr_data = port_addr;
1926 	prop_ptr->prop_data = port_addr_data;
1927 
1928 	pl_start = pl_end = prop_ptr;
1929 
1930 	/* consrtruct port WWN property. */
1931 	if ((prop_ptr = (impl_map_dev_prop_t *)calloc(
1932 		1, sizeof (impl_map_dev_prop_t))) == NULL) {
1933 		free_prop_list(&pl_start);
1934 		return (L_MALLOC_FAILED);
1935 	}
1936 	(void) strncpy(prop_ptr->prop_name, PORT_WWN_PROP,
1937 			strlen(PORT_WWN_PROP));
1938 	prop_ptr->prop_type = GFC_PROP_TYPE_BYTES;
1939 
1940 	if ((port_wwn_data = (uchar_t *)calloc(
1941 		1, FC_WWN_SIZE)) == NULL) {
1942 		free_prop_list(&pl_start);
1943 		return (L_MALLOC_FAILED);
1944 	}
1945 	memcpy(port_wwn_data, port_wwn, FC_WWN_SIZE);
1946 	prop_ptr->prop_data = port_wwn_data;
1947 	prop_ptr->prop_size = FC_WWN_SIZE;
1948 	pl_end->next = prop_ptr;
1949 	pl_end = prop_ptr;
1950 
1951 	/* consrtruct node WWN property. */
1952 	if ((prop_ptr = (impl_map_dev_prop_t *)calloc(
1953 		1, sizeof (impl_map_dev_prop_t))) == NULL) {
1954 		free_prop_list(&pl_start);
1955 		return (L_MALLOC_FAILED);
1956 	}
1957 	(void) strncpy(prop_ptr->prop_name, NODE_WWN_PROP,
1958 			strlen(NODE_WWN_PROP));
1959 	prop_ptr->prop_type = GFC_PROP_TYPE_BYTES;
1960 
1961 	if ((node_wwn_data = (uchar_t *)calloc(
1962 		1, FC_WWN_SIZE)) == NULL) {
1963 		free_prop_list(&pl_start);
1964 		return (L_MALLOC_FAILED);
1965 	}
1966 	memcpy(node_wwn_data, node_wwn, FC_WWN_SIZE);
1967 	prop_ptr->prop_data = node_wwn_data;
1968 	prop_ptr->prop_size = FC_WWN_SIZE;
1969 	pl_end->next = prop_ptr;
1970 	pl_end = prop_ptr;
1971 
1972 	/* consrtruct hard addr property. */
1973 	if ((prop_ptr = (impl_map_dev_prop_t *)calloc(
1974 		1, sizeof (impl_map_dev_prop_t))) == NULL) {
1975 		free_prop_list(&pl_start);
1976 		return (L_MALLOC_FAILED);
1977 	}
1978 	(void) strncpy(prop_ptr->prop_name, HARD_ADDR_PROP,
1979 			strlen(HARD_ADDR_PROP));
1980 	prop_ptr->prop_type = GFC_PROP_TYPE_INT;
1981 
1982 	if ((hard_addr_data = (int *)calloc(
1983 		1, sizeof (int))) == NULL) {
1984 		free_prop_list(&pl_start);
1985 		return (L_MALLOC_FAILED);
1986 	}
1987 	*hard_addr_data = hard_addr;
1988 	prop_ptr->prop_data = hard_addr_data;
1989 	pl_end->next = prop_ptr;
1990 	pl_end = prop_ptr;
1991 
1992 	if (*prop_list == NULL) {
1993 		*prop_list = pl_start;
1994 	} else {
1995 		pl_end->next = (*prop_list)->next;
1996 		*prop_list = pl_start;
1997 	}
1998 
1999 	return (0);
2000 }
2001 
2002 /*
2003  * This function consturct FCP inq dtype propery.
2004  * if inq_dtype is null the property is constrcted with err info.
2005  *
2006  * L_MALLOC_FAILED is the only possible error.
2007  */
2008 static int
2009 update_map_dev_FCP_prop(
2010 	impl_map_dev_prop_t **prop_list,
2011 	uchar_t *inq_dtype, int err, int exist)
2012 {
2013 	impl_map_dev_prop_t	*prop_ptr, *old_prop_ptr;
2014 	uchar_t *inq_dtype_data;
2015 
2016 	if ((prop_ptr = (impl_map_dev_prop_t *)calloc(
2017 		1, sizeof (impl_map_dev_prop_t))) == NULL) {
2018 		return (L_MALLOC_FAILED);
2019 	}
2020 
2021 	(void) strncpy(prop_ptr->prop_name, INQ_DTYPE_PROP,
2022 		strlen(INQ_DTYPE_PROP));
2023 
2024 	if (inq_dtype == NULL) {
2025 		prop_ptr->prop_data = NULL;
2026 		prop_ptr->prop_error = err;
2027 	} else {
2028 		if ((inq_dtype_data = (uchar_t *)calloc(
2029 			1, sizeof (uchar_t))) == NULL) {
2030 			free(prop_ptr);
2031 			return (L_MALLOC_FAILED);
2032 		}
2033 		memcpy(inq_dtype_data, inq_dtype, sizeof (uchar_t));
2034 		prop_ptr->prop_data = inq_dtype_data;
2035 		prop_ptr->prop_type = GFC_PROP_TYPE_BYTES;
2036 		prop_ptr->prop_size = sizeof (uchar_t);
2037 	}
2038 
2039 	if (*prop_list == NULL) {
2040 		*prop_list = prop_ptr;
2041 	} else {
2042 		if (exist == PROP_EXIST) {
2043 			prop_ptr->next = (*prop_list)->next;
2044 			old_prop_ptr = *prop_list;
2045 			*prop_list = prop_ptr;
2046 			free((uchar_t *)(old_prop_ptr->prop_data));
2047 			old_prop_ptr->prop_data = NULL;
2048 			S_FREE(old_prop_ptr);
2049 		} else {
2050 			prop_ptr->next = *prop_list;
2051 			*prop_list = prop_ptr;
2052 		}
2053 	}
2054 
2055 	return (0);
2056 }
2057 
2058 /*
2059  * This function calls FCP_TGT_INQUIRY via g_issue_fcp_ioctl()
2060  * to get the inq_dtype of input device and calls update_map_dev_FCP_prop().
2061  * inq_dtype is set to NULL and pass error code if inq_dtype data is not
2062  * requried.
2063  *
2064  * return error from update_map_dev_FCP_prop().
2065  */
2066 static int
2067 handle_map_dev_FCP_prop(
2068 	minor_t fp_xport_minor,
2069 	la_wwn_t port_wwn,
2070 	impl_map_dev_prop_t **prop_list)
2071 {
2072 	struct device_data	inq_data;
2073 	int 			fcp_fd, err;
2074 	struct fcp_ioctl	fcp_data;
2075 	uchar_t			inq_dtype;
2076 
2077 	if ((fcp_fd = g_object_open(FCP_PATH, O_RDONLY)) == -1) {
2078 		update_map_dev_FCP_prop(prop_list, NULL,
2079 			L_OPEN_PATH_FAIL, PROP_NOEXIST);
2080 	}
2081 
2082 	/* Get the minor number for an fp instance */
2083 	fcp_data.fp_minor = fp_xport_minor;
2084 
2085 	/* Get FCP prop for the hba first. */
2086 	fcp_data.listlen = 1;
2087 	inq_data.dev_pwwn = port_wwn;
2088 	fcp_data.list = (caddr_t)&inq_data;
2089 
2090 	if (err = g_issue_fcp_ioctl(fcp_fd, &fcp_data, 0)) {
2091 		/* if ioctl error then set the prop_error.	*/
2092 	    if ((err = update_map_dev_FCP_prop(
2093 		prop_list, NULL, err, PROP_NOEXIST)) != 0) {
2094 		return (err);
2095 	    }
2096 	} else {
2097 	    inq_dtype = inq_data.dev0_type;
2098 	    if ((err = update_map_dev_FCP_prop(
2099 		prop_list, &inq_dtype, 0, PROP_NOEXIST)) != 0) {
2100 		return (err);
2101 	    }
2102 	}
2103 
2104 	return (0);
2105 }
2106 
2107 /*
2108  * Construct device map tree from nexus driver
2109  *
2110  * PARAMS:
2111  *	path -	must be the physical path to a device
2112  *	l_err  - ptr to an error code.  Set when NULL is returned.
2113  *	flag -  device map fomat and property type.
2114  *
2115  * LOGIC:
2116  *	1. check the validity of path via g_get_path_type.
2117  *	2. If FC path, get the topology of the path via
2118  *		g_get_fca_port_topology.
2119  *
2120  *	3. If FC type(Leadville statck)
2121  *		FCIO_GET_DEV_LIST to get the device node list of fc_port_dev_t.
2122  *		FCIO_GET_HOST_PARAMS to get the fca port node of fc_port_dev_t.
2123  *
2124  *		root of tree is set with host_params info
2125  *			FC propery is set.
2126  *			FCP property is set if reqyested through flag.
2127  *				Issue g_issue_fcp_ioctl to get FCP inquiry data
2128  *		consruruct list of children via dev_list.
2129  *			FC property is set.
2130  *			FCP property is set if reqyested through flag.
2131  *				Issue FCIO_DEV_LOGIN if it is fabric device.
2132  *				Issue g_issue_fcp_ioctl to get FCP inquiry data.
2133  *
2134  *	   else FC4 type(socal/sf or ifp stack)
2135  *		SFIOCGMAP ioctl to get the device and hba nodes of
2136  *			sf_addr_pair_t.
2137  *		FCIO_GETMAP ioctl to get hba port info.
2138  *		consturct map and child tree list and
2139  *		set the properties as private loop devices.
2140  *
2141  * RETURNS:
2142  *	ptr to map is returned if OK.
2143  *	NULL and l_err is set otherwise.
2144  */
2145 gfc_dev_t
2146 g_dev_map_init(char *path, int *l_err, int flag)
2147 {
2148 int		fd, i, num_devices = 0, err, pathcnt = 1, new_count = 0;
2149 char		drvr_path[MAXPATHLEN], drvr_path0[MAXPATHLEN];
2150 char		*char_ptr, *nexus_path;
2151 struct stat	stbuf;
2152 fc_port_dev_t	*dev_list = NULL, *dlist;
2153 uint32_t	hba_port_top, state;
2154 uint_t		path_type;
2155 sf_al_map_t	sf_map;
2156 fc_port_dev_t	fp_hba_port;
2157 mp_pathlist_t	pathlist;
2158 int		p_on = 0, p_st = 0, hba_alpa_found = 0, nexus_fd;
2159 fcio_t		fcio;
2160 struct lilpmap	limited_map;
2161 impl_map_dev_t	*impl_map, *impl_dev, *mdl_start = NULL, *mdl_end = NULL;
2162 struct stat	sbuf;
2163 
2164 	if (l_err == NULL) {
2165 		return (NULL);
2166 	}
2167 
2168 	if (path == NULL) {
2169 		*l_err = L_INVALID_PATH;
2170 		return (NULL);
2171 	}
2172 
2173 	*l_err = 0;
2174 
2175 	(void) strcpy(drvr_path, path);
2176 	/*
2177 	 * Get the path to the :devctl driver
2178 	 *
2179 	 * This assumes the path looks something like this:
2180 	 * /devices/sbus@1f,0/SUNW,socal@1,0/SUNW,sf@0,0/ses@e,0:0
2181 	 * or
2182 	 * /devices/sbus@1f,0/SUNW,socal@1,0/SUNW,sf@0,0
2183 	 * or
2184 	 * /devices/sbus@1f,0/SUNW,socal@1,0/SUNW,sf@0,0:devctl
2185 	 * or
2186 	 * a 1 level PCI type driver but still :devctl
2187 	 */
2188 	if (strstr(path, SCSI_VHCI)) {
2189 		(void) strcpy(drvr_path0, path);
2190 		if (g_get_pathlist(drvr_path0, &pathlist)) {
2191 			*l_err = L_INVALID_PATH;
2192 			return (NULL);
2193 		}
2194 		pathcnt = pathlist.path_count;
2195 		p_on = p_st = 0;
2196 		for (i = 0; i < pathcnt; i++) {
2197 			if (pathlist.path_info[i].path_state < MAXPATHSTATE) {
2198 				if (pathlist.path_info[i].path_state ==
2199 					MDI_PATHINFO_STATE_ONLINE) {
2200 					p_on = i;
2201 					break;
2202 				} else if (pathlist.path_info[i].path_state ==
2203 					MDI_PATHINFO_STATE_STANDBY) {
2204 					p_st = i;
2205 				}
2206 			}
2207 		}
2208 		if (pathlist.path_info[p_on].path_state ==
2209 		    MDI_PATHINFO_STATE_ONLINE) {
2210 			/* on_line path */
2211 			(void) strcpy(drvr_path,
2212 				pathlist.path_info[p_on].path_hba);
2213 		} else {
2214 			/* standby or path0 */
2215 			(void) strcpy(drvr_path,
2216 				pathlist.path_info[p_st].path_hba);
2217 		}
2218 		free(pathlist.path_info);
2219 		(void) strcat(drvr_path, FC_CTLR);
2220 	} else {
2221 		(void) strcpy(drvr_path, path);
2222 		if (strstr(drvr_path, DRV_NAME_SSD) ||
2223 			strstr(drvr_path, SES_NAME) ||
2224 			strstr(drvr_path, DRV_NAME_ST)) {
2225 			if ((char_ptr = strrchr(drvr_path, '/')) == NULL) {
2226 				*l_err = L_INVALID_PATH;
2227 				return (NULL);
2228 			}
2229 			*char_ptr = '\0';   /* Terminate sting  */
2230 			/* append controller */
2231 			(void) strcat(drvr_path, FC_CTLR);
2232 		} else {
2233 			if (stat(drvr_path, &stbuf) < 0) {
2234 				*l_err = L_LSTAT_ERROR;
2235 				return (NULL);
2236 			}
2237 			if ((stbuf.st_mode & S_IFMT) == S_IFDIR) {
2238 				/* append controller */
2239 				(void) strcat(drvr_path, FC_CTLR);
2240 			}
2241 		}
2242 	}
2243 
2244 	P_DPRINTF("  g_dev_map_init: Geting drive map from:"
2245 		" %s\n", drvr_path);
2246 
2247 	path_type = g_get_path_type(drvr_path);
2248 	if ((path_type == 0) || !(path_type & XPORT_MASK)) {
2249 		*l_err = L_INVALID_PATH_TYPE;
2250 		return (NULL);
2251 	}
2252 
2253 	/* get fiber topology */
2254 	if ((err = g_get_fca_port_topology(drvr_path,
2255 			&hba_port_top, 0)) != 0) {
2256 		*l_err = err;
2257 		return (NULL);
2258 	}
2259 
2260 	if ((fd = g_object_open(drvr_path, O_NDELAY | O_RDONLY)) == -1) {
2261 		*l_err = errno;
2262 		return (NULL);
2263 	}
2264 
2265 	/* for FC devices. */
2266 	if (path_type & FC_FCA_MASK) {
2267 		/* get the number of device first. */
2268 	    fcio.fcio_cmd = FCIO_GET_NUM_DEVS;
2269 	    fcio.fcio_olen = sizeof (num_devices);
2270 	    fcio.fcio_xfer = FCIO_XFER_READ;
2271 	    fcio.fcio_obuf = (caddr_t)&num_devices;
2272 	    if (g_issue_fcio_ioctl(fd, &fcio, 0) != 0) {
2273 		I_DPRINTF(" FCIO_GET_NUM_DEVS ioctl failed.\n");
2274 		(void) close(fd);
2275 		*l_err = L_FCIO_GET_NUM_DEVS_FAIL;
2276 		return (NULL);
2277 	    }
2278 	    if (num_devices != 0) {
2279 		if ((dev_list = (fc_port_dev_t *)calloc(num_devices,
2280 			sizeof (fc_port_dev_t))) == NULL) {
2281 		    (void) close(fd);
2282 		    *l_err = L_MALLOC_FAILED;
2283 		    return (NULL);
2284 		}
2285 
2286 		bzero((caddr_t)&fcio, sizeof (fcio));
2287 		/* Get the device list */
2288 		fcio.fcio_cmd = FCIO_GET_DEV_LIST;
2289 		/* Information read operation */
2290 		fcio.fcio_xfer = FCIO_XFER_READ;
2291 		fcio.fcio_olen = num_devices * sizeof (fc_port_dev_t);
2292 		fcio.fcio_obuf = (caddr_t)dev_list;
2293 		/* new device count */
2294 		fcio.fcio_alen = sizeof (new_count);
2295 		fcio.fcio_abuf = (caddr_t)&new_count;
2296 		if ((err = g_issue_fcio_ioctl(fd, &fcio, 0)) != 0) {
2297 		    if (err == L_INVALID_DEVICE_COUNT) {
2298 			/*
2299 			 * original buffer was small so allocate buffer
2300 			 * with a new count and retry.
2301 			 */
2302 			free(dev_list);
2303 			num_devices = new_count;
2304 			new_count = 0;
2305 			if ((dev_list = (fc_port_dev_t *)calloc(num_devices,
2306 				sizeof (fc_port_dev_t))) == NULL) {
2307 			    (void) close(fd);
2308 			    *l_err = L_MALLOC_FAILED;
2309 			    return (NULL);
2310 			}
2311 			fcio.fcio_cmd = FCIO_GET_DEV_LIST;
2312 			/* Information read operation */
2313 			fcio.fcio_xfer = FCIO_XFER_READ;
2314 			fcio.fcio_obuf = (caddr_t)dev_list;
2315 			fcio.fcio_olen = num_devices * sizeof (fc_port_dev_t);
2316 			/* new device count */
2317 			fcio.fcio_alen = sizeof (new_count);
2318 			fcio.fcio_abuf = (caddr_t)&new_count;
2319 			if ((err = g_issue_fcio_ioctl(fd, &fcio, 0)) != 0) {
2320 			    if (err == L_INVALID_DEVICE_COUNT) {
2321 				/*
2322 				 * No more retry. There may be severe hardware
2323 				 * problem so return error here.
2324 				 */
2325 				I_DPRINTF(" Device count was %d"
2326 				" should have been %d\n",
2327 				num_devices, new_count);
2328 				free(dev_list);
2329 				(void) close(fd);
2330 				*l_err = L_INVALID_DEVICE_COUNT;
2331 				return (NULL);
2332 			    } else {
2333 				I_DPRINTF(" FCIO_GET_DEV_LIST ioctl failed.");
2334 				free(dev_list);
2335 				(void) close(fd);
2336 				*l_err = L_FCIO_GET_DEV_LIST_FAIL;
2337 				return (NULL);
2338 			    }
2339 			}
2340 		    }
2341 		}
2342 	    }
2343 
2344 		/*
2345 		 * if new count is smaller than the original number from
2346 		 * FCIO_GET_NUM_DEVS, adjust new count and buffer size
2347 		 * and continue.
2348 		 */
2349 	    if (new_count < num_devices) {
2350 		num_devices = new_count;
2351 		if (new_count > 0) {
2352 		    if ((dev_list = (fc_port_dev_t *)realloc(dev_list,
2353 			(new_count * sizeof (fc_port_dev_t))))
2354 				== NULL) {
2355 			S_FREE(dev_list);
2356 			(void) close(fd);
2357 			*l_err = L_MALLOC_FAILED;
2358 			return (NULL);
2359 		    }
2360 		}
2361 	    }
2362 
2363 		/* get the host param info */
2364 	    (void) memset(&fp_hba_port, 0, sizeof (struct fc_port_dev));
2365 	    fcio.fcio_cmd = FCIO_GET_HOST_PARAMS;
2366 	    fcio.fcio_xfer = FCIO_XFER_READ;
2367 	    fcio.fcio_obuf = (caddr_t)&fp_hba_port;
2368 	    fcio.fcio_olen = sizeof (fc_port_dev_t);
2369 
2370 	    if (g_issue_fcio_ioctl(fd, &fcio, 0) != 0) {
2371 		I_DPRINTF(" FCIO_GET_HOST_PARAMS ioctl failed.\n");
2372 		(void) close(fd);
2373 		if (num_devices == 0) {
2374 			*l_err = L_NO_DEVICES_FOUND;
2375 		} else {
2376 			free(dev_list);
2377 			*l_err = L_FCIO_GET_HOST_PARAMS_FAIL;
2378 		}
2379 		(void) close(fd);
2380 		return (NULL);
2381 	    }
2382 
2383 		/* If we want the lilp map then we need to do a little	*/
2384 		/* work here.  The lilp map contains the local hba in	*/
2385 		/* the dev_addr.  Once this has been added qsort the	*/
2386 		/* dev_addr array so it's in physical order.		*/
2387 	    if ((flag & MAP_FORMAT_LILP) == MAP_FORMAT_LILP) {
2388 		/* First we need to allocate one additional	*/
2389 		/* device to the dev_addr structure, for the 	*/
2390 		/* local hba					*/
2391 		if (num_devices > 0) {
2392 		    if ((dev_list = (fc_port_dev_t *)realloc(dev_list,
2393 			(++num_devices * sizeof (fc_port_dev_t)))) == NULL) {
2394 			(void) close(fd);
2395 			/* in case dev_list is not null free it. */
2396 			S_FREE(dev_list);
2397 			*l_err =  L_MALLOC_FAILED;
2398 			return (NULL);
2399 		    }
2400 
2401 		    /* Next, copy the local hba into this new loc.	*/
2402 		    if (memcpy(dev_list+(num_devices-1), &fp_hba_port,
2403 				sizeof (fc_port_dev_t)) == NULL) {
2404 			(void) free(dev_list);
2405 			(void) close(fd);
2406 			*l_err =  L_MEMCPY_FAILED;
2407 			return (NULL);
2408 		    }
2409 
2410 			/* Now sort by physical location		*/
2411 		    qsort((void*)dev_list, num_devices,
2412 			sizeof (fc_port_dev_t), lilp_map_cmp);
2413 		}
2414 	    }
2415 
2416 
2417 		/* We have dev list info and host param info.	*/
2418 		/* Now constructs map tree with these info.	*/
2419 		/* First consturct the root of the map tree	*/
2420 		/* with host param.				*/
2421 	    if ((impl_map = (impl_map_dev_t *)calloc(
2422 			1, sizeof (impl_map_dev_t))) == NULL) {
2423 		(void) free(dev_list);
2424 		(void) close(fd);
2425 		*l_err = L_MALLOC_FAILED;
2426 		return (NULL);
2427 	    }
2428 	    impl_map->flag = flag;
2429 	    impl_map->topo = hba_port_top;
2430 
2431 		/* consturct hba property list.	*/
2432 	    if ((err = update_map_dev_fc_prop(&impl_map->prop_list,
2433 		    hba_port_top, fp_hba_port.dev_pwwn.raw_wwn,
2434 		    fp_hba_port.dev_nwwn.raw_wwn, fp_hba_port.dev_did.port_id,
2435 		    fp_hba_port.dev_hard_addr.hard_addr)) != 0) {
2436 		(void) free(dev_list);
2437 		(void) close(fd);
2438 		g_dev_map_fini(impl_map);
2439 		*l_err = err;
2440 		return (NULL);
2441 	    }
2442 
2443 	    if ((flag & MAP_XPORT_PROP_ONLY) != MAP_XPORT_PROP_ONLY) {
2444 		if (fstat(fd, &sbuf) == -1) {
2445 		    (void) free(dev_list);
2446 		    (void) close(fd);
2447 		    g_dev_map_fini(impl_map);
2448 		    *l_err = L_FSTAT_ERROR;
2449 		    return (NULL);
2450 		}
2451 		if ((err = handle_map_dev_FCP_prop(minor(sbuf.st_rdev),
2452 			fp_hba_port.dev_pwwn, &impl_map->prop_list)) != 0) {
2453 		    (void) free(dev_list);
2454 		    (void) close(fd);
2455 		    g_dev_map_fini(impl_map);
2456 		    *l_err = err;
2457 		    return (NULL);
2458 		}
2459 	    }
2460 
2461 		/* consturct child for each device and	*/
2462 		/* set device property list.		*/
2463 	    dlist = dev_list;
2464 	    for (i = 0; i < num_devices; i++, dlist++) {
2465 		if ((impl_dev = (impl_map_dev_t *)calloc(
2466 			1, sizeof (impl_map_dev_t))) == NULL) {
2467 		    (void) free(dev_list);
2468 		    (void) close(fd);
2469 		    g_dev_map_fini(impl_map);
2470 		    *l_err = L_MALLOC_FAILED;
2471 		    return (NULL);
2472 		}
2473 		/* set the map as parent */
2474 		impl_dev->parent = impl_map;
2475 		if ((err = update_map_dev_fc_prop(&impl_dev->prop_list,
2476 		    hba_port_top, dlist->dev_pwwn.raw_wwn,
2477 		    dlist->dev_nwwn.raw_wwn, dlist->dev_did.port_id,
2478 		    dlist->dev_hard_addr.hard_addr)) != 0) {
2479 		    (void) free(dev_list);
2480 		    (void) close(fd);
2481 		    g_dev_map_fini(impl_map);
2482 		    *l_err = err;
2483 		    return (NULL);
2484 		}
2485 		if (i == 0) {
2486 		    mdl_start = mdl_end = impl_dev;
2487 		} else {
2488 		    mdl_end->next = impl_dev;
2489 		    mdl_end = impl_dev;
2490 		}
2491 		if ((flag & MAP_XPORT_PROP_ONLY) != MAP_XPORT_PROP_ONLY) {
2492 		    if (((hba_port_top == FC_TOP_PUBLIC_LOOP) ||
2493 			(hba_port_top == FC_TOP_FABRIC)) &&
2494 			(memcmp(fp_hba_port.dev_pwwn.raw_wwn,
2495 			dlist->dev_pwwn.raw_wwn, FC_WWN_SIZE) != 0)) {
2496 			(void) memset(&fcio, 0, sizeof (fcio_t));
2497 			fcio.fcio_cmd = FCIO_GET_STATE;
2498 			fcio.fcio_ilen = sizeof (dlist->dev_pwwn);
2499 			fcio.fcio_ibuf = (caddr_t)&dlist->dev_pwwn;
2500 			fcio.fcio_xfer = FCIO_XFER_READ | FCIO_XFER_WRITE;
2501 			fcio.fcio_olen = sizeof (uint32_t);
2502 			fcio.fcio_obuf = (caddr_t)&state;
2503 			fcio.fcio_alen = 0;
2504 			fcio.fcio_abuf = NULL;
2505 			if (g_issue_fcio_ioctl(fd, &fcio, 0) != 0) {
2506 			    I_DPRINTF(" FCIO_GET_STATE ioctl failed.\n");
2507 			    if ((err = update_map_dev_FCP_prop(
2508 				&impl_dev->prop_list, NULL,
2509 				L_FCIO_GET_STATE_FAIL, PROP_NOEXIST)) != 0) {
2510 				(void) free(dev_list);
2511 				(void) close(fd);
2512 				g_dev_map_fini(impl_map);
2513 				*l_err = err;
2514 				return (NULL);
2515 			    }
2516 			}
2517 			if (state != PORT_DEVICE_LOGGED_IN) {
2518 			    (void) close(fd);
2519 			    if ((fd = g_object_open(drvr_path,
2520 				O_NDELAY | O_RDONLY | O_EXCL)) == -1) {
2521 				(void) free(dev_list);
2522 				g_dev_map_fini(impl_map);
2523 				*l_err = L_OPEN_PATH_FAIL;
2524 				return (NULL);
2525 			    }
2526 			    (void) memset(&fcio, 0, sizeof (fcio_t));
2527 			    fcio.fcio_cmd = FCIO_DEV_LOGIN;
2528 			    fcio.fcio_ilen = sizeof (dlist->dev_pwwn);
2529 			    fcio.fcio_ibuf = (caddr_t)&dlist->dev_pwwn;
2530 			    fcio.fcio_xfer = FCIO_XFER_WRITE;
2531 			    fcio.fcio_olen = fcio.fcio_alen = 0;
2532 			    fcio.fcio_obuf = fcio.fcio_abuf = NULL;
2533 			    if (g_issue_fcio_ioctl(fd, &fcio, 0) != 0) {
2534 				I_DPRINTF(" FCIO_DEV_LOGIN ioctl failed.\n");
2535 				if ((err = update_map_dev_FCP_prop(
2536 				    &impl_dev->prop_list, NULL,
2537 				    L_FCIO_DEV_LOGIN_FAIL,
2538 				    PROP_NOEXIST)) != 0) {
2539 				    (void) free(dev_list);
2540 				    (void) close(fd);
2541 				    g_dev_map_fini(impl_map);
2542 				    *l_err = err;
2543 				    return (NULL);
2544 				}
2545 				/* plogi failed continue to next dev */
2546 				continue;
2547 			    }
2548 			}
2549 		    }
2550 			/* sbuf should be set from hba_port handling. */
2551 		    if ((err = handle_map_dev_FCP_prop(minor(sbuf.st_rdev),
2552 			dlist->dev_pwwn, &impl_dev->prop_list)) != 0) {
2553 			(void) free(dev_list);
2554 			(void) close(fd);
2555 			g_dev_map_fini(impl_map);
2556 			*l_err = err;
2557 			return (NULL);
2558 		    }
2559 		}
2560 	    }
2561 		/* connect the children to to map.	*/
2562 	    impl_map->child = mdl_start;
2563 	    S_FREE(dev_list);
2564 
2565 	} else {	/* sf and fc4/pci devices */
2566 	    /* initialize map */
2567 	    (void) memset(&sf_map, 0, sizeof (struct sf_al_map));
2568 	    if (ioctl(fd, SFIOCGMAP, &sf_map) != 0) {
2569 		I_DPRINTF("  SFIOCGMAP ioctl failed.\n");
2570 		(void) close(fd);
2571 		*l_err = L_SFIOCGMAP_IOCTL_FAIL;
2572 		return (NULL);
2573 	    }
2574 		/* Check for reasonableness. */
2575 	    if ((sf_map.sf_count > 126) || (sf_map.sf_count < 0)) {
2576 		(void) close(fd);
2577 		*l_err = L_INVALID_LOOP_MAP;
2578 		return (NULL);
2579 	    }
2580 
2581 	    if (sf_map.sf_count == 0) {
2582 		(void) close(fd);
2583 		*l_err = L_NO_DEVICES_FOUND;
2584 		return (NULL);
2585 	    }
2586 
2587 	    if ((err = g_get_nexus_path(drvr_path, &nexus_path)) != 0) {
2588 		(void) close(fd);
2589 		*l_err = err;
2590 		return (NULL);
2591 	    }
2592 
2593 	    if ((nexus_fd = g_object_open(nexus_path, O_NDELAY | O_RDONLY)) ==
2594 			-1) {
2595 		(void) close(fd);
2596 		S_FREE(nexus_path);
2597 		*l_err = errno;
2598 		return (NULL);
2599 	    }
2600 
2601 		/* get limited map to get hba param info */
2602 	    if (ioctl(nexus_fd, FCIO_GETMAP, &limited_map) != 0) {
2603 		I_DPRINTF("  FCIO_GETMAP ioctl failed\n");
2604 		(void) close(fd);
2605 		(void) close(nexus_fd);
2606 		S_FREE(nexus_path);
2607 		*l_err = L_FCIO_GETMAP_IOCTL_FAIL;
2608 		return (NULL);
2609 	    }
2610 	    (void) close(nexus_fd);
2611 	    S_FREE(nexus_path);
2612 
2613 	    for (i = 0; i < sf_map.sf_count; i++) {
2614 		if (sf_map.sf_addr_pair[i].sf_al_pa ==
2615 			limited_map.lilp_myalpa) {
2616 			sf_map.sf_hba_addr = sf_map.sf_addr_pair[i];
2617 			hba_alpa_found = 1;
2618 		}
2619 	    }
2620 
2621 	    if (!(hba_alpa_found)) {
2622 		(void) close(fd);
2623 		*l_err = L_INVALID_LOOP_MAP;
2624 		return (NULL);
2625 	    }
2626 
2627 		/* We have dev list info and host param info.	*/
2628 		/* Now constructs map tree with these info.	*/
2629 		/* First consturct the root of the map tree	*/
2630 		/* with host param.				*/
2631 	    if ((impl_map = (impl_map_dev_t *)calloc(
2632 			1, sizeof (impl_map_dev_t))) == NULL) {
2633 		(void) close(fd);
2634 		*l_err = L_MALLOC_FAILED;
2635 		return (NULL);
2636 	    }
2637 	    impl_map->flag = flag;
2638 	    impl_map->topo = hba_port_top;
2639 
2640 		/* consturct hba property list.	*/
2641 	    if ((err = update_map_dev_fc_prop(&impl_map->prop_list,
2642 		    hba_port_top, sf_map.sf_hba_addr.sf_port_wwn,
2643 		    sf_map.sf_hba_addr.sf_node_wwn,
2644 		    (int)sf_map.sf_hba_addr.sf_al_pa,
2645 		    (int)sf_map.sf_hba_addr.sf_hard_address)) != 0) {
2646 		(void) close(fd);
2647 		g_dev_map_fini(impl_map);
2648 		*l_err = err;
2649 		return (NULL);
2650 	    }
2651 
2652 	    if ((flag & MAP_XPORT_PROP_ONLY) != MAP_XPORT_PROP_ONLY) {
2653 		if ((err = update_map_dev_FCP_prop(&impl_map->prop_list,
2654 		    &sf_map.sf_hba_addr.sf_inq_dtype, 0, PROP_NOEXIST)) != 0) {
2655 		    (void) close(fd);
2656 		    g_dev_map_fini(impl_map);
2657 		    *l_err = err;
2658 		    return (NULL);
2659 		}
2660 	    }
2661 
2662 	    for (i = 0; i < sf_map.sf_count; i++) {
2663 		if ((impl_dev = (impl_map_dev_t *)calloc(
2664 			1, sizeof (impl_map_dev_t))) == NULL) {
2665 		    (void) close(fd);
2666 		    g_dev_map_fini(impl_map);
2667 		    *l_err = L_MALLOC_FAILED;
2668 		    return (NULL);
2669 		}
2670 		/* set the map as parent */
2671 		impl_dev->parent = impl_map;
2672 		if ((err = update_map_dev_fc_prop(&impl_dev->prop_list,
2673 		    hba_port_top, sf_map.sf_addr_pair[i].sf_port_wwn,
2674 		    sf_map.sf_addr_pair[i].sf_node_wwn,
2675 		    (int)(sf_map.sf_addr_pair[i].sf_al_pa),
2676 		    (int)(sf_map.sf_addr_pair[i].sf_hard_address))) != 0) {
2677 		    (void) close(fd);
2678 		    g_dev_map_fini(impl_map);
2679 		    *l_err = err;
2680 		    return (NULL);
2681 		}
2682 		if (i == 0) {
2683 		    mdl_start = mdl_end = impl_dev;
2684 		} else {
2685 		    mdl_end->next = impl_dev;
2686 		    mdl_end = impl_dev;
2687 		}
2688 		if ((flag & MAP_XPORT_PROP_ONLY) != MAP_XPORT_PROP_ONLY) {
2689 		    if ((err = update_map_dev_FCP_prop(&impl_dev->prop_list,
2690 			&sf_map.sf_addr_pair[i].sf_inq_dtype, 0,
2691 			PROP_NOEXIST)) != 0) {
2692 			(void) close(fd);
2693 			g_dev_map_fini(impl_map);
2694 			*l_err = err;
2695 			return (NULL);
2696 		    }
2697 		}
2698 	    } /* end of for loop */
2699 
2700 	    impl_map->child = mdl_start;
2701 	} /* end of else */
2702 
2703 	close(fd);
2704 	return ((gfc_dev_t)(impl_map));
2705 }
2706 
2707 /*
2708  * This function deallocates memory for propery list.
2709  */
2710 static void
2711 free_prop_list(impl_map_dev_prop_t **prop_list)
2712 {
2713 	impl_map_dev_prop_t *lp, *olp;
2714 
2715 	lp = *prop_list;
2716 	while (lp != NULL) {
2717 		switch (lp->prop_type) {
2718 		case GFC_PROP_TYPE_BYTES:
2719 			free((uchar_t *)(lp->prop_data));
2720 			break;
2721 		case GFC_PROP_TYPE_INT:
2722 			free((int *)(lp->prop_data));
2723 			break;
2724 		case GFC_PROP_TYPE_STRING:
2725 			free((char *)(lp->prop_data));
2726 			break;
2727 		default:
2728 			break;
2729 		}
2730 		lp->prop_data = NULL;
2731 		olp = lp;
2732 		lp = olp->next;
2733 		S_FREE(olp);
2734 	}
2735 
2736 	*prop_list = NULL;
2737 }
2738 
2739 /*
2740  * This function deallocates memory for children list.
2741  */
2742 static void
2743 free_child_list(impl_map_dev_t **dev_list)
2744 {
2745 	impl_map_dev_t *lp, *olp;
2746 
2747 	lp = *dev_list;
2748 	while (lp != NULL) {
2749 		free_prop_list(&lp->prop_list);
2750 		olp = lp;
2751 		lp = olp->next;
2752 		S_FREE(olp);
2753 	}
2754 
2755 	*dev_list = NULL;
2756 }
2757 
2758 /*
2759  * This function deallocates memory for the whole map.
2760  */
2761 void
2762 g_dev_map_fini(gfc_dev_t map)
2763 {
2764 	impl_map_dev_t *impl_map;
2765 
2766 	impl_map = (impl_map_dev_t *)map;
2767 
2768 	if (impl_map != NULL) {
2769 	    free_prop_list(&impl_map->prop_list);
2770 	    free_child_list(&impl_map->child);
2771 	    S_FREE(impl_map);
2772 	}
2773 }
2774 
2775 /*
2776  * This function passes back topology of the input map.
2777  * input should be a handle form g_dev_map_init().
2778  *
2779  * return 0 if OK.
2780  * return error code otherwise.
2781  */
2782 int
2783 g_get_map_topology(
2784 	gfc_dev_t map,
2785 	uint_t *topology)
2786 {
2787 	impl_map_dev_t	*impl_map;
2788 
2789 	if (map == NULL) {
2790 		return (L_INVALID_MAP_DEV_ADDR);
2791 	}
2792 
2793 	if (topology == NULL) {
2794 		return (L_INVALID_ARG);
2795 	}
2796 
2797 	impl_map = (impl_map_dev_t *)map;
2798 
2799 	*topology = impl_map->topo;
2800 
2801 	return (0);
2802 }
2803 
2804 /*
2805  * This function returns the first device handle of the input map.
2806  * map input should be a handle form g_dev_map_init().
2807  *
2808  * l_err set to 0 if OK.
2809  * l_err set to error code otherwise.
2810  */
2811 gfc_dev_t
2812 g_get_first_dev(
2813 	gfc_dev_t map,
2814 	int *l_err)
2815 {
2816 	impl_map_dev_t	*impl_map;
2817 
2818 	if (l_err == NULL) {
2819 		return (NULL);
2820 	}
2821 
2822 	*l_err = 0;
2823 
2824 	if (map == NULL) {
2825 		*l_err = L_INVALID_MAP_DEV_ADDR;
2826 		return (NULL);
2827 	}
2828 
2829 	impl_map = (impl_map_dev_t *)map;
2830 
2831 	if (impl_map->child == NULL) {
2832 		*l_err = L_NO_SUCH_DEV_FOUND;
2833 	}
2834 
2835 	return ((gfc_dev_t)(impl_map->child));
2836 }
2837 
2838 /*
2839  * This function returns the next device handle of the input map.
2840  * map_dev input should be a handle for device.
2841  *
2842  * l_err set to 0 if OK.
2843  * l_err set to error code otherwise.
2844  */
2845 gfc_dev_t
2846 g_get_next_dev(
2847 	gfc_dev_t map_dev,
2848 	int *l_err)
2849 {
2850 	impl_map_dev_t	*impl_dev;
2851 
2852 	if (l_err == NULL) {
2853 		return (NULL);
2854 	}
2855 
2856 	*l_err = 0;
2857 
2858 	if (map_dev == NULL) {
2859 		*l_err = L_INVALID_MAP_DEV_ADDR;
2860 		return (NULL);
2861 	}
2862 
2863 	impl_dev = (impl_map_dev_t *)map_dev;
2864 
2865 	if (impl_dev->next == NULL) {
2866 		*l_err = L_NO_SUCH_DEV_FOUND;
2867 	}
2868 
2869 	return ((gfc_dev_t)(impl_dev->next));
2870 }
2871 
2872 /*
2873  * This function passes back uchar_t type property and its count.
2874  * map_dev input should be a handle for device.
2875  *
2876  * return 0 if OK.
2877  * return error code otherwise.
2878  */
2879 int
2880 g_dev_prop_lookup_bytes(
2881 	gfc_dev_t map_dev,
2882 	const char *prop_name,
2883 	int *prop_data_count,
2884 	uchar_t **prop_data)
2885 {
2886 	impl_map_dev_t *impl_dev;
2887 	impl_map_dev_prop_t *impl_prop;
2888 	int err;
2889 
2890 	if (map_dev == NULL) {
2891 		return (L_INVALID_MAP_DEV_ADDR);
2892 	}
2893 
2894 	if ((prop_name == NULL) || (prop_data == NULL) ||
2895 		(prop_data_count == NULL)) {
2896 		return (L_INVALID_ARG);
2897 	}
2898 
2899 	impl_dev = (impl_map_dev_t *)map_dev;
2900 	impl_prop = impl_dev->prop_list;
2901 
2902 	err = L_INVALID_MAP_DEV_PROP_NAME;
2903 
2904 	while (impl_prop) {
2905 	    if (strncmp(impl_prop->prop_name, prop_name,
2906 		strlen(prop_name)) == 0) {
2907 		if (impl_prop->prop_type != GFC_PROP_TYPE_BYTES) {
2908 		    err = L_INVALID_MAP_DEV_PROP_TYPE;
2909 		    break;
2910 		}
2911 		if (impl_prop->prop_data) {
2912 		    *prop_data = (uchar_t *)(impl_prop->prop_data);
2913 		    *prop_data_count = impl_prop->prop_size;
2914 		    return (0);
2915 		} else {
2916 		    err = impl_prop->prop_error;
2917 		}
2918 		break;
2919 	    }
2920 	    impl_prop = impl_prop->next;
2921 	}
2922 
2923 	return (err);
2924 }
2925 
2926 /*
2927  * This function passes back int type property.
2928  * map_dev input should be a handle for device.
2929  *
2930  * return 0 if OK.
2931  * return error code otherwise.
2932  */
2933 int
2934 g_dev_prop_lookup_ints(
2935 	gfc_dev_t map_dev,
2936 	const char *prop_name,
2937 	int **prop_data)
2938 {
2939 	impl_map_dev_t *impl_dev;
2940 	impl_map_dev_prop_t *impl_prop;
2941 	int err;
2942 
2943 	if (map_dev == NULL) {
2944 		return (L_INVALID_MAP_DEV_ADDR);
2945 	}
2946 
2947 	if ((prop_name == NULL) || (prop_data == NULL)) {
2948 		return (L_INVALID_ARG);
2949 	}
2950 
2951 	impl_dev = (impl_map_dev_t *)map_dev;
2952 	impl_prop = impl_dev->prop_list;
2953 
2954 	err = L_INVALID_MAP_DEV_PROP_NAME;
2955 
2956 	while (impl_prop) {
2957 	    if (strncmp(impl_prop->prop_name, prop_name,
2958 		strlen(prop_name)) == 0) {
2959 		if (impl_prop->prop_type != GFC_PROP_TYPE_INT) {
2960 		    err = L_INVALID_MAP_DEV_PROP_TYPE;
2961 		    break;
2962 		}
2963 		if (impl_prop->prop_data) {
2964 		    *prop_data = (int *)(impl_prop->prop_data);
2965 		    return (0);
2966 		} else {
2967 		    err = impl_prop->prop_error;
2968 		}
2969 		break;
2970 	    }
2971 	    impl_prop = impl_prop->next;
2972 	}
2973 
2974 	return (err);
2975 }
2976 
2977 /*
2978  * This function passes back int type property.
2979  * map_dev input should be a handle for device.
2980  *
2981  * return 0 if OK.
2982  * return error code otherwise.
2983  */
2984 int
2985 g_dev_prop_lookup_strings(
2986 	gfc_dev_t map_dev,
2987 	const char *prop_name,
2988 	char **prop_data)
2989 {
2990 	impl_map_dev_t *impl_dev;
2991 	impl_map_dev_prop_t *impl_prop;
2992 	int err;
2993 
2994 	if (map_dev == NULL) {
2995 		return (L_INVALID_MAP_DEV_ADDR);
2996 	}
2997 
2998 	if ((prop_name == NULL) || (prop_data == NULL)) {
2999 		return (L_INVALID_ARG);
3000 	}
3001 
3002 	impl_dev = (impl_map_dev_t *)map_dev;
3003 	impl_prop = impl_dev->prop_list;
3004 
3005 	err = L_INVALID_MAP_DEV_PROP_NAME;
3006 
3007 	while (impl_prop) {
3008 	    if (strncmp(impl_prop->prop_name, prop_name,
3009 		strlen(prop_name)) == 0) {
3010 		if (impl_prop->prop_type != GFC_PROP_TYPE_STRING) {
3011 		    err = L_INVALID_MAP_DEV_PROP_TYPE;
3012 		    break;
3013 		}
3014 		if (impl_prop->prop_data) {
3015 		    *prop_data = (char *)(impl_prop->prop_data);
3016 		    return (0);
3017 		} else {
3018 		    err = impl_prop->prop_error;
3019 		}
3020 		break;
3021 	    }
3022 	    impl_prop = impl_prop->next;
3023 	}
3024 
3025 	return (err);
3026 }
3027 
3028 /*
3029  * This function returns the handle for the first property of the input device.
3030  * map_dev input should be a handle form a device.
3031  *
3032  * l_err set to 0 if OK.
3033  * l_err set to error code otherwise.
3034  */
3035 gfc_prop_t
3036 g_get_first_dev_prop(
3037 	gfc_dev_t map_dev,
3038 	int *l_err)
3039 {
3040 	impl_map_dev_t	*impl_dev;
3041 
3042 	if (l_err == NULL) {
3043 		return (NULL);
3044 	}
3045 
3046 	*l_err = 0;
3047 
3048 	if (map_dev == NULL) {
3049 		*l_err = L_INVALID_MAP_DEV_ADDR;
3050 		return (NULL);
3051 	}
3052 
3053 	impl_dev = (impl_map_dev_t *)map_dev;
3054 
3055 	if (impl_dev->prop_list == NULL) {
3056 		*l_err = L_NO_SUCH_PROP_FOUND;
3057 	}
3058 
3059 	return ((gfc_prop_t)(impl_dev->prop_list));
3060 }
3061 
3062 /*
3063  * This function returns the handle for next property handle of the input prop.
3064  * map_prop input should be a handle for property.
3065  *
3066  * l_err set to 0 if OK.
3067  * l_err set to error code otherwise.
3068  */
3069 gfc_prop_t
3070 g_get_next_dev_prop(
3071 	gfc_prop_t map_prop,
3072 	int *l_err)
3073 {
3074 	impl_map_dev_prop_t	*impl_prop;
3075 
3076 	if (l_err == NULL) {
3077 		return (NULL);
3078 	}
3079 
3080 	*l_err = 0;
3081 
3082 	if (map_prop == NULL) {
3083 		*l_err = L_INVALID_MAP_DEV_PROP;
3084 		return (NULL);
3085 	}
3086 
3087 	impl_prop = (impl_map_dev_prop_t *)map_prop;
3088 
3089 	if (impl_prop->next == NULL) {
3090 		*l_err = L_NO_SUCH_PROP_FOUND;
3091 	}
3092 
3093 	return ((gfc_prop_t)(impl_prop->next));
3094 }
3095 
3096 /*
3097  * This function returns the name of the property of the input prop.
3098  * map_prop input should be a handle for property.
3099  *
3100  * return name string if OK.
3101  * returns NULL and l_err set to error code otherwise.
3102  */
3103 char *
3104 g_get_dev_prop_name(
3105 	gfc_prop_t map_prop,
3106 	int *l_err)
3107 {
3108 	impl_map_dev_prop_t	*impl_prop;
3109 
3110 	if (l_err == NULL) {
3111 		return (NULL);
3112 	}
3113 
3114 	*l_err = 0;
3115 
3116 	if (map_prop == NULL) {
3117 		*l_err = L_INVALID_MAP_DEV_PROP;
3118 		return (NULL);
3119 	}
3120 
3121 	impl_prop = (impl_map_dev_prop_t *)map_prop;
3122 
3123 	return (impl_prop->prop_name);
3124 }
3125 
3126 /*
3127  * This function returns the type of the property of the input prop.
3128  * map_prop input should be a handle for property.
3129  *
3130  * return type if OK.
3131  * returns GFC_PROP_TYPE_UNKNOWN and l_err set to error code otherwise.
3132  */
3133 int
3134 g_get_dev_prop_type(
3135 	gfc_prop_t map_prop,
3136 	int *l_err)
3137 {
3138 	impl_map_dev_prop_t	*impl_prop;
3139 
3140 	if (l_err != NULL) {
3141 		*l_err = 0;
3142 	} else {
3143 		return (L_INVALID_ARG);
3144 	}
3145 
3146 	if (map_prop == NULL) {
3147 		*l_err = L_INVALID_MAP_DEV_PROP;
3148 		return (GFC_PROP_TYPE_UNKNOWN);
3149 	}
3150 
3151 	impl_prop = (impl_map_dev_prop_t *)map_prop;
3152 
3153 	return (impl_prop->prop_type);
3154 }
3155 
3156 /*
3157  * This function passes back uchar_t type property and its count.
3158  * map_prop input should be a handle for property.
3159  *
3160  * return 0 if OK.
3161  * return error code otherwise.
3162  */
3163 int
3164 g_get_dev_prop_bytes(
3165 	gfc_prop_t map_prop,
3166 	int *prop_data_count,
3167 	uchar_t **prop_data)
3168 {
3169 	impl_map_dev_prop_t *impl_prop;
3170 
3171 	if (map_prop == NULL) {
3172 		return (L_INVALID_MAP_DEV_ADDR);
3173 	}
3174 
3175 	if ((prop_data == NULL) || (prop_data_count == NULL)) {
3176 		return (L_INVALID_ARG);
3177 	}
3178 
3179 	impl_prop = (impl_map_dev_prop_t *)map_prop;
3180 
3181 	if (impl_prop->prop_type != GFC_PROP_TYPE_BYTES) {
3182 		    return (L_INVALID_MAP_DEV_PROP_TYPE);
3183 	}
3184 	if (impl_prop->prop_data) {
3185 	    *prop_data = (uchar_t *)(impl_prop->prop_data);
3186 	    *prop_data_count = impl_prop->prop_size;
3187 	} else {
3188 	    return (impl_prop->prop_error);
3189 	}
3190 
3191 	return (0);
3192 }
3193 
3194 /*
3195  * This function passes back int type property.
3196  * map_prop input should be a handle for property.
3197  *
3198  * return 0 if OK.
3199  * return error code otherwise.
3200  */
3201 int
3202 g_get_dev_prop_ints(
3203 	gfc_prop_t map_prop,
3204 	int **prop_data)
3205 {
3206 	impl_map_dev_prop_t *impl_prop;
3207 
3208 	if (map_prop == NULL) {
3209 		return (L_INVALID_MAP_DEV_ADDR);
3210 	}
3211 
3212 	if (prop_data == NULL) {
3213 		return (L_INVALID_ARG);
3214 	}
3215 
3216 	impl_prop = (impl_map_dev_prop_t *)map_prop;
3217 
3218 	if (impl_prop->prop_type != GFC_PROP_TYPE_INT) {
3219 		    return (L_INVALID_MAP_DEV_PROP_TYPE);
3220 	}
3221 	if (impl_prop->prop_data) {
3222 	    *prop_data = (int *)(impl_prop->prop_data);
3223 	} else {
3224 	    return (impl_prop->prop_error);
3225 	}
3226 
3227 	return (0);
3228 }
3229 
3230 /*
3231  * This function passes back string type property.
3232  * map_prop input should be a handle for property.
3233  *
3234  * return 0 if OK.
3235  * return error code otherwise.
3236  */
3237 int
3238 g_get_dev_prop_strings(
3239 	gfc_prop_t map_prop,
3240 	char **prop_data)
3241 {
3242 	impl_map_dev_prop_t *impl_prop;
3243 
3244 	if (map_prop == NULL) {
3245 		return (L_INVALID_MAP_DEV_ADDR);
3246 	}
3247 
3248 	if (prop_data == NULL) {
3249 		return (L_INVALID_ARG);
3250 	}
3251 
3252 	impl_prop = (impl_map_dev_prop_t *)map_prop;
3253 
3254 	if (impl_prop->prop_type != GFC_PROP_TYPE_STRING) {
3255 		    return (L_INVALID_MAP_DEV_PROP_TYPE);
3256 	}
3257 	if (impl_prop->prop_data) {
3258 	    *prop_data = (char *)(impl_prop->prop_data);
3259 	} else {
3260 	    return (impl_prop->prop_error);
3261 	}
3262 
3263 	return (0);
3264 }
3265 
3266 /*
3267  * Free the linked list allocated by g_rdls()
3268  */
3269 static void
3270 g_free_rls(AL_rls *rlsptr)
3271 {
3272 	AL_rls *trlsptr;
3273 
3274 	while (rlsptr != NULL) {
3275 		trlsptr = rlsptr->next;
3276 		free(rlsptr);
3277 		rlsptr = trlsptr;
3278 	}
3279 }
3280 
3281 /*
3282  * Read the extended link error status block
3283  * from the specified device and Host Adapter.
3284  *
3285  * PARAMS:
3286  *	path_phys - physical path to an FC device
3287  *	rls_ptr   - pointer to read link state structure
3288  *
3289  * RETURNS:
3290  *	0	: if OK
3291  *	non-zero: otherwise
3292  */
3293 int
3294 g_rdls(char *path_phys, struct al_rls **rls_ptr, int verbose)
3295 {
3296 char		nexus_path[MAXPATHLEN], *nexus_path_ptr;
3297 int		fd, fp_fd, err, length, exp_map_flag = 0, *port_addr;
3298 struct lilpmap	map;
3299 AL_rls		*rls, *c1 = NULL, *c2 = NULL;
3300 uchar_t		i, *port_wwn_byte;
3301 la_wwn_t	port_wwn;
3302 sf_al_map_t	exp_map;
3303 char		*charPtr, fp_path[MAXPATHLEN];
3304 uint_t		dev_type;
3305 struct stat	stbuf;
3306 fcio_t		fcio;
3307 fc_portid_t	rls_req;
3308 fc_rls_acc_t	rls_payload;
3309 gfc_dev_t	map_root, map_dev;
3310 uint32_t	hba_port_top, state;
3311 int		pathcnt = 1, count;
3312 mp_pathlist_t	pathlist;
3313 int		p_on = 0, p_st = 0;
3314 
3315 	/* return invalid path if path_phys is NULL */
3316 	if (path_phys == NULL) {
3317 		return (L_INVALID_PATH);
3318 	}
3319 	/* return invalid arg if rls_ptr is NULL */
3320 	if (rls_ptr == NULL) {
3321 		return (L_INVALID_ARG);
3322 	}
3323 
3324 	*rls_ptr = rls = NULL;
3325 
3326 	if (strstr(path_phys, SCSI_VHCI) != NULL) {
3327 		(void) strcpy(fp_path, path_phys);
3328 		if (g_get_pathlist(fp_path, &pathlist)) {
3329 			return (L_INVALID_PATH);
3330 		}
3331 		pathcnt = pathlist.path_count;
3332 		p_on = p_st = 0;
3333 		for (i = 0; i < pathcnt; i++) {
3334 			if (pathlist.path_info[i].path_state < MAXPATHSTATE) {
3335 				if (pathlist.path_info[i].path_state ==
3336 					MDI_PATHINFO_STATE_ONLINE) {
3337 					p_on = i;
3338 					break;
3339 				} else if (pathlist.path_info[i].path_state ==
3340 					MDI_PATHINFO_STATE_STANDBY) {
3341 					p_st = i;
3342 				}
3343 			}
3344 		}
3345 		if (pathlist.path_info[p_on].path_state ==
3346 		    MDI_PATHINFO_STATE_ONLINE) {
3347 			/* on_line path */
3348 			(void) strcpy(fp_path,
3349 				pathlist.path_info[p_on].path_hba);
3350 		} else {
3351 			/* standby or path0 */
3352 			(void) strcpy(fp_path,
3353 				pathlist.path_info[p_st].path_hba);
3354 		}
3355 		free(pathlist.path_info);
3356 	} else {
3357 		(void) strcpy(fp_path, path_phys);
3358 	}
3359 
3360 	/* Get map of devices on this loop. */
3361 	if ((dev_type = g_get_path_type(fp_path)) == 0) {
3362 		return (L_INVALID_PATH);
3363 	}
3364 	if (dev_type & FC_FCA_MASK) {
3365 		if (strstr(path_phys, SCSI_VHCI) != NULL) {
3366 			(void) strcat(fp_path, FC_CTLR);
3367 		} else if (strstr(fp_path, DRV_NAME_SSD) ||
3368 		    strstr(fp_path, DRV_NAME_ST) ||
3369 				strstr(fp_path, SES_NAME)) {
3370 			if ((charPtr = strrchr(fp_path, '/')) == NULL) {
3371 				return (L_INVALID_PATH);
3372 			}
3373 			*charPtr = '\0';
3374 			/* append devctl to the path */
3375 			(void) strcat(fp_path, FC_CTLR);
3376 		} else {
3377 			if (stat(fp_path, &stbuf) < 0) {
3378 				return (L_LSTAT_ERROR);
3379 			}
3380 			if ((stbuf.st_mode & S_IFMT) == S_IFDIR) {
3381 				/* append devctl to the path */
3382 				(void) strcat(fp_path, FC_CTLR);
3383 			}
3384 		}
3385 
3386 		if ((map_root = g_dev_map_init(fp_path, &err,
3387 			MAP_XPORT_PROP_ONLY)) == NULL) {
3388 			return (err);
3389 		}
3390 
3391 	} else { /* FC4_FCA_MASK type path */
3392 	    (void) memset(&map, 0, sizeof (struct lilpmap));
3393 
3394 	    if ((err = g_get_nexus_path(path_phys,
3395 		    &nexus_path_ptr)) != 0) {
3396 		return (err);
3397 	    }
3398 	    (void) strcpy(nexus_path, nexus_path_ptr);
3399 	    g_destroy_data(nexus_path_ptr);
3400 
3401 		/* open driver */
3402 	    if ((fd = g_object_open(nexus_path,
3403 			O_NDELAY | O_RDONLY)) == -1)
3404 		return (errno);
3405 
3406 		/*
3407 		 * First try using the socal version of the map.
3408 		 * If that fails get the expanded vesion.
3409 		 */
3410 	    if (ioctl(fd, FCIO_GETMAP, &map) != 0) {
3411 		I_DPRINTF("  FCIO_GETMAP ioctl failed.\n");
3412 		if (ioctl(fd, SFIOCGMAP, &exp_map) != 0) {
3413 			I_DPRINTF("  SFIOCGMAP ioctl failed.\n");
3414 			(void) close(fd);
3415 			return (L_SFIOCGMAP_IOCTL_FAIL);
3416 		}
3417 		/* Check for reasonableness. */
3418 		if ((exp_map.sf_count > 126) ||
3419 				(exp_map.sf_count < 0)) {
3420 			(void) close(fd);
3421 			return (L_INVALID_LOOP_MAP);
3422 		}
3423 		for (i = 0; i < exp_map.sf_count; i++) {
3424 			if (exp_map.sf_addr_pair[i].sf_al_pa > 0xef) {
3425 				(void) close(fd);
3426 				return (L_INVALID_LOOP_MAP);
3427 			}
3428 		}
3429 		length = exp_map.sf_count;
3430 		exp_map_flag++;
3431 	    } else {
3432 		I_DPRINTF("  g_rdls:"
3433 			" FCIO_GETMAP ioctl returned %d entries.\n",
3434 			map.lilp_length);
3435 		/* Check for reasonableness. */
3436 		if (map.lilp_length > sizeof (map.lilp_list)) {
3437 			(void) close(fd);
3438 			return (L_FCIOGETMAP_INVLD_LEN);
3439 		}
3440 		length = map.lilp_length;
3441 	    }
3442 	    for (i = 0; i < length; i++) {
3443 		if ((c2 = (struct al_rls *)
3444 			g_zalloc(sizeof (struct al_rls))) == NULL) {
3445 			close(fd);
3446 			return (L_MALLOC_FAILED);
3447 		}
3448 		if (rls == NULL) {
3449 			c1 = rls = c2;
3450 		} else {
3451 			for (c1 = rls; c1->next; c1 =  c1->next) {};
3452 			c1 = c1->next = c2;
3453 		}
3454 		(void) strcpy(c1->driver_path, nexus_path);
3455 		if (exp_map_flag) {
3456 			c1->payload.rls_portno = c1->al_ha =
3457 				exp_map.sf_addr_pair[i].sf_al_pa;
3458 		} else {
3459 			c1->payload.rls_portno = c1->al_ha = map.lilp_list[i];
3460 		}
3461 		c1->payload.rls_linkfail =
3462 				(uint_t)0xff000000; /* get LESB for this port */
3463 		I_DPRINTF("  g_rdls:" " al_pa 0x%x\n", c1->payload.rls_portno);
3464 
3465 		if (ioctl(fd, FCIO_LINKSTATUS, &c1->payload) != 0) {
3466 			/*
3467 			 * The ifp driver will return ENXIO when rls
3468 			 * is issued for same initiator on loop when
3469 			 * there is more than one on the loop.
3470 			 * Rather than completely fail, continue on.
3471 			 * Set values in the payload struct to -1 as
3472 			 * this is what socal is currently doing for
3473 			 * the case of same initiator rls.
3474 			 */
3475 			if ((dev_type & FC4_PCI_FCA) && (errno == ENXIO)) {
3476 				c1->payload.rls_linkfail =
3477 				c1->payload.rls_syncfail =
3478 				c1->payload.rls_sigfail =
3479 				c1->payload.rls_primitiverr =
3480 				c1->payload.rls_invalidword =
3481 				c1->payload.rls_invalidcrc = (uint_t)0xffffffff;
3482 			} else {
3483 				I_DPRINTF("  FCIO_LINKSTATUS ioctl"
3484 				" failed with errno %d.\n", errno);
3485 				g_free_rls(rls);
3486 				(void) close(fd);
3487 				return (L_FCIO_LINKSTATUS_FAILED);
3488 			}
3489 		}
3490 		I_DPRINTF("  g_rdls: al_pa returned by ioctl 0x%x\n",
3491 			c1->payload.rls_portno);
3492 	    }
3493 	    *rls_ptr = rls; /* Pass back pointer */
3494 
3495 	    (void) close(fd);
3496 	    return (0);
3497 	}
3498 
3499 	/* Now we need to take care of FC_FCA_MASK case.	*/
3500 	/* we have map created already via g_dev_map_init.	*/
3501 	if ((err = g_get_map_topology(map_root, &hba_port_top)) != 0) {
3502 		g_dev_map_fini(map_root);
3503 		return (err);
3504 	}
3505 
3506 	if ((map_dev = g_get_first_dev(map_root, &err)) == NULL) {
3507 		g_dev_map_fini(map_root);
3508 		if (err != L_NO_SUCH_DEV_FOUND) {
3509 			return (err);
3510 		} else {
3511 			return (L_NO_DEVICES_FOUND);
3512 		}
3513 	}
3514 
3515 	while (map_dev) {
3516 	    if ((err = g_dev_prop_lookup_ints(
3517 		map_dev, PORT_ADDR_PROP, &port_addr)) != 0) {
3518 		g_dev_map_fini(map_root);
3519 		g_free_rls(rls);
3520 		return (err);
3521 	    }
3522 
3523 	    if ((c2 = (struct al_rls *)
3524 		g_zalloc(sizeof (struct al_rls))) == NULL) {
3525 		g_dev_map_fini(map_root);
3526 		g_free_rls(rls);
3527 		close(fd);
3528 		return (L_MALLOC_FAILED);
3529 	    }
3530 	    if (rls == NULL) {
3531 		c1 = rls = c2;
3532 	    } else {
3533 		for (c1 = rls; c1->next; c1 =  c1->next) {};
3534 		c1 = c1->next = c2;
3535 	    }
3536 	    /* Set the al_ha here */
3537 	    c1->al_ha = rls_req.port_id = *port_addr;
3538 
3539 		/*
3540 		 * fp uses different input/output structures for
3541 		 * rls. Load the values returned for the fp ioctl
3542 		 * into the structure passed back to the caller
3543 		 * Note: There is no reason for the path
3544 		 * to be loaded into AL_rls as is done for socal/ifp
3545 		 * above.
3546 		 */
3547 	    if ((hba_port_top == FC_TOP_FABRIC) ||
3548 		(hba_port_top == FC_TOP_PUBLIC_LOOP)) {
3549 		if ((err = g_dev_prop_lookup_bytes(
3550 			map_dev, PORT_WWN_PROP, &count, &port_wwn_byte)) != 0) {
3551 			g_dev_map_fini(map_root);
3552 			g_free_rls(rls);
3553 			return (err);
3554 		}
3555 		memcpy(port_wwn.raw_wwn, port_wwn_byte, FC_WWN_SIZE);
3556 		if ((err = g_get_dev_port_state(
3557 			fp_path, port_wwn, &state)) == 0) {
3558 		    if (state != PORT_DEVICE_LOGGED_IN) {
3559 			if ((err = g_dev_login(fp_path, port_wwn)) != 0) {
3560 				c1->payload.rls_linkfail =
3561 				c1->payload.rls_syncfail =
3562 				c1->payload.rls_sigfail =
3563 				c1->payload.rls_primitiverr =
3564 				c1->payload.rls_invalidword =
3565 				c1->payload.rls_invalidcrc = (uint_t)0xffffffff;
3566 				if (((map_dev =
3567 					g_get_next_dev(map_dev, &err))
3568 					== NULL) &&
3569 					(err != L_NO_SUCH_DEV_FOUND)) {
3570 					g_dev_map_fini(map_root);
3571 					g_free_rls(rls);
3572 					return (err);
3573 				}
3574 				continue;
3575 			}
3576 		    }
3577 		} /* if g_get_dev_port_state fails proceed. */
3578 	    }
3579 
3580 	    fcio.fcio_cmd_flags = FCIO_CFLAGS_RLS_DEST_NPORT;
3581 	    if ((fp_fd = g_object_open(fp_path, O_RDONLY | O_EXCL)) < 0) {
3582 		g_dev_map_fini(map_root);
3583 		g_free_rls(rls);
3584 		return (L_OPEN_PATH_FAIL);
3585 	    }
3586 	    fcio.fcio_cmd = FCIO_LINK_STATUS;
3587 	    fcio.fcio_ibuf = (caddr_t)&rls_req;
3588 	    fcio.fcio_ilen = sizeof (rls_req);
3589 	    fcio.fcio_xfer = FCIO_XFER_RW;
3590 	    fcio.fcio_flags = 0;
3591 	    fcio.fcio_obuf = (caddr_t)&rls_payload;
3592 	    fcio.fcio_olen = sizeof (rls_payload);
3593 	    if (g_issue_fcio_ioctl(fp_fd, &fcio, verbose) != 0) {
3594 		c1->payload.rls_linkfail =
3595 		c1->payload.rls_syncfail =
3596 		c1->payload.rls_sigfail =
3597 		c1->payload.rls_primitiverr =
3598 		c1->payload.rls_invalidword =
3599 		c1->payload.rls_invalidcrc = (uint_t)0xffffffff;
3600 	    } else {
3601 		/*
3602 		 * Load the values into the struct passed
3603 		 * back to the caller
3604 		 */
3605 		c1->payload.rls_linkfail = rls_payload.rls_link_fail;
3606 		c1->payload.rls_syncfail = rls_payload.rls_sync_loss;
3607 		c1->payload.rls_sigfail = rls_payload.rls_sig_loss;
3608 		c1->payload.rls_primitiverr = rls_payload.rls_prim_seq_err;
3609 		c1->payload.rls_invalidword = rls_payload.rls_invalid_word;
3610 		c1->payload.rls_invalidcrc = rls_payload.rls_invalid_crc;
3611 	    }
3612 	    (void) close(fp_fd);
3613 
3614 	    if (((map_dev = g_get_next_dev(map_dev, &err)) == NULL) &&
3615 		(err != L_NO_SUCH_DEV_FOUND)) {
3616 		g_dev_map_fini(map_root);
3617 		g_free_rls(rls);
3618 		return (err);
3619 	    }
3620 	}
3621 
3622 	/* for Leadville issue a final call for the initiator */
3623 
3624 	if ((err = g_dev_prop_lookup_ints(
3625 		map_root, PORT_ADDR_PROP, &port_addr)) != 0) {
3626 		g_dev_map_fini(map_root);
3627 		g_free_rls(rls);
3628 		return (err);
3629 	}
3630 
3631 	if ((c2 = (struct al_rls *)
3632 		g_zalloc(sizeof (struct al_rls))) == NULL) {
3633 		g_dev_map_fini(map_root);
3634 		g_free_rls(rls);
3635 		return (L_MALLOC_FAILED);
3636 	}
3637 	if (rls == NULL) {
3638 		c1 = rls = c2;
3639 	} else {
3640 		for (c1 = rls; c1->next; c1 =  c1->next) {};
3641 		c1 = c1->next = c2;
3642 	}
3643 
3644 	c1->al_ha = rls_req.port_id = *port_addr;
3645 
3646 	if ((fp_fd = g_object_open(fp_path, O_RDONLY | O_EXCL)) < 0) {
3647 		g_dev_map_fini(map_root);
3648 		g_free_rls(rls);
3649 		return (L_OPEN_PATH_FAIL);
3650 	}
3651 
3652 	fcio.fcio_cmd = FCIO_LINK_STATUS;
3653 	fcio.fcio_ibuf = (caddr_t)&rls_req;
3654 	fcio.fcio_ilen = sizeof (rls_req);
3655 	fcio.fcio_xfer = FCIO_XFER_RW;
3656 	fcio.fcio_flags = 0;
3657 	fcio.fcio_cmd_flags = FCIO_CFLAGS_RLS_DEST_NPORT;
3658 	fcio.fcio_obuf = (caddr_t)&rls_payload;
3659 	fcio.fcio_olen = sizeof (rls_payload);
3660 
3661 	if (g_issue_fcio_ioctl(fp_fd, &fcio, verbose) != 0) {
3662 		c1->payload.rls_linkfail =
3663 		c1->payload.rls_syncfail =
3664 		c1->payload.rls_sigfail =
3665 		c1->payload.rls_primitiverr =
3666 		c1->payload.rls_invalidword =
3667 		c1->payload.rls_invalidcrc = (uint_t)0xffffffff;
3668 	} else {
3669 		/*
3670 		 * Load the values into the struct passed
3671 		 * back to the caller
3672 		 */
3673 		c1->payload.rls_linkfail = rls_payload.rls_link_fail;
3674 		c1->payload.rls_syncfail = rls_payload.rls_sync_loss;
3675 		c1->payload.rls_sigfail = rls_payload.rls_sig_loss;
3676 		c1->payload.rls_primitiverr = rls_payload.rls_prim_seq_err;
3677 		c1->payload.rls_invalidword = rls_payload.rls_invalid_word;
3678 		c1->payload.rls_invalidcrc = rls_payload.rls_invalid_crc;
3679 		(void) close(fp_fd);
3680 	}
3681 	(void) close(fp_fd);
3682 
3683 	*rls_ptr = rls;	/* Pass back pointer */
3684 
3685 	g_dev_map_fini(map_root);
3686 	return (0);
3687 }
3688 
3689 static u_longlong_t wwnConversion(uchar_t *wwn)
3690 {
3691 	u_longlong_t tmp;
3692 	memcpy(&tmp, wwn, sizeof (u_longlong_t));
3693 	return (tmp);
3694 }
3695 
3696 /*
3697  * Get device World Wide Name (port and node) for device at path
3698  * and add all WWNs to the wwn_list_found list.
3699  *
3700  * RETURN: 0 O.K.
3701  *
3702  * INPUTS:
3703  *	- path_phys must be of a device, either an IB or disk.
3704  */
3705 static int
3706 get_wwns(char *path_phys, uchar_t port_wwn[], uchar_t node_wwn[], int *al_pa,
3707 	struct wwn_list_found_struct **wwn_list_found)
3708 {
3709 uint32_t	hba_port_top;
3710 int		i, err, count;
3711 char		*char_ptr, *ptr;
3712 int		found = 0, pathcnt, *port_addr;
3713 unsigned long long 	pwwn;
3714 uchar_t			*port_wwn_byte, *node_wwn_byte;
3715 char		drvr_path[MAXPATHLEN];
3716 int		p_on = 0, p_st = 0;
3717 mp_pathlist_t	pathlist;
3718 char		pwwn1[WWN_S_LEN];
3719 gfc_dev_t	map_root, map_dev;
3720 hrtime_t	start_time, end_time;
3721 char *env = NULL;
3722 
3723 	P_DPRINTF("  g_get_wwn: Getting device WWN"
3724 			" and al_pa for device: %s\n",
3725 			path_phys);
3726 
3727 	if ((env = getenv("_LUX_T_DEBUG")) != NULL) {
3728 		start_time = gethrtime();
3729 	}
3730 
3731 	/*
3732 	 * Get the loop identifier (switch setting) from the path.
3733 	 *
3734 	 * This assumes the path looks something like this:
3735 	 * /devices/.../SUNW,socal@3,0/SUNW,sf@0,0/SUNW,ssd@x,0
3736 	 * or
3737 	 * /devices/.../SUNW,qlc@5/SUNW,fp@0,0/SUNW,ssd@x,0
3738 	 */
3739 	if ((char_ptr = strrchr(path_phys, '@')) == NULL) {
3740 		return (L_INVLD_PATH_NO_ATSIGN_FND);
3741 	}
3742 	char_ptr++;	/* point to the loop identifier or WWN */
3743 
3744 	(void) strcpy(drvr_path, path_phys);
3745 	/* This function allocs mem for map.dev_addr on success */
3746 	if (strstr(drvr_path, SCSI_VHCI)) {
3747 		if (g_get_pathlist(drvr_path, &pathlist)) {
3748 			return (L_INVALID_PATH);
3749 		}
3750 		pathcnt = pathlist.path_count;
3751 		p_on = p_st = 0;
3752 		for (i = 0; i < pathcnt; i++) {
3753 			if (pathlist.path_info[i].path_state < MAXPATHSTATE) {
3754 				if (pathlist.path_info[i].path_state ==
3755 					MDI_PATHINFO_STATE_ONLINE) {
3756 					p_on = i;
3757 					break;
3758 				} else if (pathlist.path_info[i].path_state ==
3759 					MDI_PATHINFO_STATE_STANDBY) {
3760 					p_st = i;
3761 				}
3762 			}
3763 		}
3764 		if (p_on == i) {
3765 			/* on_line path */
3766 			(void) strcpy(drvr_path,
3767 				pathlist.path_info[p_on].path_hba);
3768 			(void) strncpy(pwwn1,
3769 				pathlist.path_info[p_on].path_addr,
3770 				WWN_S_LEN - 1);
3771 			pwwn1[WWN_S_LEN - 1] = '\0';
3772 		} else {
3773 			/* standby or path0 */
3774 			(void) strcpy(drvr_path,
3775 				pathlist.path_info[p_st].path_hba);
3776 			(void) strncpy(pwwn1,
3777 				pathlist.path_info[p_st].path_addr,
3778 				WWN_S_LEN - 1);
3779 			pwwn1[WWN_S_LEN - 1] = '\0';
3780 		}
3781 		free(pathlist.path_info);
3782 		(void) strcat(drvr_path, FC_CTLR);
3783 	}
3784 	if ((map_root = g_dev_map_init(drvr_path, &err,
3785 		MAP_XPORT_PROP_ONLY)) == NULL) {
3786 		return (err);
3787 	}
3788 
3789 	if ((err = g_get_map_topology(map_root, &hba_port_top)) != 0) {
3790 		g_dev_map_fini(map_root);
3791 		return (err);
3792 	}
3793 
3794 	if (strstr(path_phys, SCSI_VHCI)) {
3795 		char_ptr = pwwn1;
3796 	} else {
3797 		/*
3798 		 * Format of WWN is
3799 		 * ssd@w2200002037000f96,0:a,raw
3800 		 */
3801 		if (*char_ptr != 'w') {
3802 			g_dev_map_fini(map_root);
3803 			return (L_INVLD_WWN_FORMAT);
3804 		}
3805 		char_ptr++;
3806 	}
3807 	pwwn = strtoull(char_ptr, &ptr, 16);
3808 	if (ptr == char_ptr) {
3809 		g_dev_map_fini(map_root);
3810 		return (L_NO_WWN_FOUND_IN_PATH);
3811 	}
3812 	P_DPRINTF("  g_get_wwn:  Looking for WWN "
3813 	    "0x%llx\n", pwwn);
3814 
3815 	if (((map_dev = g_get_first_dev(map_root, &err)) == NULL) &&
3816 	    (err != L_NO_SUCH_DEV_FOUND)) {
3817 		g_dev_map_fini(map_root);
3818 		return (err);
3819 	}
3820 
3821 	while (map_dev) {
3822 		if ((err = g_dev_prop_lookup_bytes(map_dev,
3823 			PORT_WWN_PROP, &count, &port_wwn_byte)) != 0) {
3824 			g_dev_map_fini(map_root);
3825 			return (err);
3826 		}
3827 		if ((err = g_dev_prop_lookup_bytes(map_dev,
3828 			NODE_WWN_PROP, &count, &node_wwn_byte)) != 0) {
3829 			g_dev_map_fini(map_root);
3830 			return (err);
3831 		}
3832 
3833 		if (pwwn == wwnConversion(port_wwn_byte) && found != 1) {
3834 			found = 1;
3835 			memcpy(port_wwn, port_wwn_byte, FC_WWN_SIZE);
3836 			memcpy(node_wwn, node_wwn_byte, FC_WWN_SIZE);
3837 			if ((err = g_dev_prop_lookup_ints(
3838 				map_dev, PORT_ADDR_PROP, &port_addr)) != 0) {
3839 				g_dev_map_fini(map_root);
3840 				return (err);
3841 			}
3842 			*al_pa = *port_addr;
3843 		}
3844 		add_wwn_entry(wwn_list_found, port_wwn_byte,
3845 		    node_wwn_byte);
3846 
3847 		if (((map_dev = g_get_next_dev(map_dev, &err)) == NULL) &&
3848 		    (err != L_NO_SUCH_DEV_FOUND)) {
3849 			g_dev_map_fini(map_root);
3850 			return (err);
3851 		}
3852 	}
3853 	if (!found) {
3854 		g_dev_map_fini(map_root);
3855 		return (L_NO_LOOP_ADDRS_FOUND);
3856 	}
3857 
3858 	g_dev_map_fini(map_root);
3859 	if (env != NULL) {
3860 		end_time = gethrtime();
3861 		fprintf(stdout, "      get_wwns: "
3862 		"\t\tTime = %lld millisec\n",
3863 		(end_time - start_time)/1000000);
3864 	}
3865 	return (0);
3866 }
3867 
3868 /*
3869  * Get device World Wide Name and AL_PA for device at path
3870  *
3871  * RETURN: 0 O.K.
3872  *
3873  * INPUTS:
3874  *	- path_phys must be of a device, either an IB or disk.
3875  */
3876 int
3877 g_get_wwn(char *path_phys, uchar_t port_wwn[], uchar_t node_wwn[],
3878 	int *al_pa, int verbose)
3879 {
3880 	struct wwn_list_found_struct *wwn_list_found = NULL;
3881 	int ret;
3882 
3883 	/* return invalid path if the argument is NULL */
3884 	if (path_phys == NULL) {
3885 		return (L_INVALID_PATH);
3886 	}
3887 	/* return invalid arg if the argument is NULL */
3888 	if ((port_wwn == NULL) ||
3889 		(node_wwn == NULL) || (al_pa == NULL)) {
3890 		return (L_INVALID_ARG);
3891 	}
3892 
3893 	ret = get_wwns(path_phys, port_wwn, node_wwn, al_pa, &wwn_list_found);
3894 	g_free_wwn_list_found(&wwn_list_found);
3895 	return (ret);
3896 }
3897 
3898 int
3899 g_get_serial_number(char *path, uchar_t *serial_number,
3900     size_t *serial_number_len)
3901 {
3902 int	    fd, status = 0;
3903 L_inquiry80 inq80;
3904 
3905 	/* return invalid path if path is NULL */
3906 	if (path == NULL) {
3907 		return (L_INVALID_PATH);
3908 	}
3909 	/* return invalid arg if serial_number is NULL */
3910 	if (serial_number == NULL) {
3911 		return (L_INVALID_ARG);
3912 	}
3913 
3914 	P_DPRINTF("  g_get_serial_number: path: %s\n", path);
3915 	if ((fd = g_object_open(path, O_NDELAY | O_RDONLY)) == -1) {
3916 		return (L_OPEN_PATH_FAIL);
3917 	}
3918 	/*
3919 	 * Call the inquiry cmd on page 0x80 only if the vendor
3920 	 * supports page 0x80.
3921 	 */
3922 	if ((g_find_supported_inq_page(fd, 0x80))) {
3923 		/*
3924 		 * Let's retrieve the serial number from page 0x80
3925 		 * and store it in the inquiry structure
3926 		 */
3927 		status = g_scsi_inquiry_cmd80(fd,
3928 		    (uchar_t *)&inq80,
3929 		    sizeof (struct l_inquiry80_struct));
3930 		if (status == 0) {
3931 			if (*serial_number_len > inq80.inq_page_len)
3932 				*serial_number_len = inq80.inq_page_len;
3933 			strncpy((char *)serial_number, (char *)inq80.inq_serial,
3934 			    *serial_number_len);
3935 		} else {
3936 			char unavail[] = "Unavailable";
3937 			status = 0;
3938 			if (*serial_number_len > strlen(unavail))
3939 				*serial_number_len = strlen(unavail);
3940 			strncpy((char *)serial_number, unavail,
3941 			    *serial_number_len);
3942 		}
3943 	} else {
3944 		/*
3945 		 * page 0x80 is not supported, so print the
3946 		 * appropriate message.
3947 		 */
3948 		char unsupp[] = "Unsupported";
3949 		if (*serial_number_len > strlen(unsupp))
3950 			*serial_number_len = strlen(unsupp);
3951 		strncpy((char *)serial_number, unsupp,
3952 		    *serial_number_len);
3953 	}
3954 	(void) close(fd);
3955 	return (status);
3956 }
3957 
3958 int
3959 g_get_inquiry(char *path, L_inquiry *l_inquiry)
3960 {
3961 int	    fd, status;
3962 
3963 	/* return invalid path if path is NULL */
3964 	if (path == NULL) {
3965 		return (L_INVALID_PATH);
3966 	}
3967 	/* return invalid arg if l_inquiry is NULL */
3968 	if (l_inquiry == NULL) {
3969 		return (L_INVALID_ARG);
3970 	}
3971 
3972 	P_DPRINTF("  g_get_inquiry: path: %s\n", path);
3973 	if ((fd = g_object_open(path, O_NDELAY | O_RDONLY)) == -1)
3974 		return (L_OPEN_PATH_FAIL);
3975 	status = g_scsi_inquiry_cmd(fd,
3976 		(uchar_t *)l_inquiry, sizeof (struct l_inquiry_struct));
3977 
3978 	(void) close(fd);
3979 	return (status);
3980 }
3981 
3982 /*
3983  * Function to retrieve inquiry page 0x80 from the device
3984  */
3985 static int
3986 g_scsi_inquiry_cmd80(int fd, uchar_t *buf_ptr, int buf_len)
3987 {
3988 struct uscsi_cmd	ucmd;
3989 my_cdb_g0	cdb = {SCMD_INQUIRY, 0x1, 0x80, 0, 0x10, 0};
3990 struct	scsi_extended_sense	sense;
3991 
3992 	(void) memset(buf_ptr, 0, buf_len);
3993 	(void) memset((char *)&ucmd, 0, sizeof (ucmd));
3994 	cdb.count = (uchar_t)buf_len;
3995 	ucmd.uscsi_cdb = (caddr_t)&cdb;
3996 	ucmd.uscsi_cdblen = CDB_GROUP0;
3997 	ucmd.uscsi_bufaddr = (caddr_t)buf_ptr;
3998 	ucmd.uscsi_buflen = buf_len;
3999 	ucmd.uscsi_rqbuf = (caddr_t)&sense;
4000 	ucmd.uscsi_rqlen = sizeof (struct  scsi_extended_sense);
4001 	ucmd.uscsi_timeout = 60;
4002 	return (cmd(fd, &ucmd, USCSI_READ | USCSI_SILENT));
4003 }
4004 
4005 /*
4006  * Function to determine if the given page is supported by vendor.
4007  */
4008 static int
4009 g_find_supported_inq_page(int fd, int page_num)
4010 {
4011 struct	uscsi_cmd	ucmd;
4012 my_cdb_g0	cdb = {SCMD_INQUIRY, 0x1, 0, 0, 0xff, 0};
4013 struct	scsi_extended_sense	sense;
4014 L_inquiry00			inq00;
4015 uchar_t				*data;
4016 int				status = 0;
4017 int				index;
4018 
4019 	(void) memset((char *)&ucmd, 0, sizeof (ucmd));
4020 	cdb.count = (uchar_t)(sizeof (L_inquiry00));
4021 	ucmd.uscsi_cdb = (caddr_t)&cdb;
4022 	ucmd.uscsi_cdblen = CDB_GROUP0;
4023 	ucmd.uscsi_bufaddr = (caddr_t)&inq00;
4024 	ucmd.uscsi_buflen = sizeof (inq00);
4025 	ucmd.uscsi_rqbuf = (caddr_t)&sense;
4026 	ucmd.uscsi_rqlen = sizeof (struct scsi_extended_sense);
4027 	ucmd.uscsi_timeout = 60;
4028 	status = cmd(fd, &ucmd, USCSI_READ | USCSI_SILENT);
4029 	if (status) {
4030 		return (0);
4031 	}
4032 	data = (uchar_t *)&inq00;
4033 	for (index = 4; (index <= inq00.len+3)&&
4034 	    (data[index] <= page_num); index ++) {
4035 		if (data[index] == page_num) {
4036 			return (1);
4037 		}
4038 	}
4039 	return (0);
4040 }
4041 
4042 int
4043 g_get_perf_statistics(char *path, uchar_t *perf_ptr)
4044 {
4045 int	fd;
4046 
4047 	P_DPRINTF("  g_get_perf_statistics: Get Performance Statistics:"
4048 		"\n  Path:%s\n",
4049 		path);
4050 
4051 	/* initialize tables */
4052 	(void) memset(perf_ptr, 0, sizeof (int));
4053 
4054 	/* open controller */
4055 	if ((fd = g_object_open(path, O_NDELAY | O_RDONLY)) == -1)
4056 		return (L_OPEN_PATH_FAIL);
4057 
4058 
4059 	/* update parameters in the performance table */
4060 
4061 	/* get the period in seconds */
4062 
4063 
4064 	(void) close(fd);
4065 
4066 	return (0);
4067 }
4068 
4069 
4070 int
4071 g_start(char *path)
4072 {
4073 int	status;
4074 int	fd;
4075 
4076 	P_DPRINTF("  g_start: Start: Path %s\n", path);
4077 	if ((fd = g_object_open(path, O_NDELAY | O_RDONLY)) == -1)
4078 		return (L_OPEN_PATH_FAIL);
4079 	status = g_scsi_start_cmd(fd);
4080 	(void) close(fd);
4081 	return (status);
4082 }
4083 
4084 int
4085 g_stop(char *path, int immediate_flag)
4086 {
4087 int	status, fd;
4088 
4089 	P_DPRINTF("  g_stop: Stop: Path %s\n", path);
4090 	if ((fd = g_object_open(path, O_NDELAY | O_RDONLY)) == -1)
4091 		return (errno);
4092 	status = g_scsi_stop_cmd(fd, immediate_flag);
4093 	(void) close(fd);
4094 	return (status);
4095 }
4096 
4097 int
4098 g_reserve(char *path)
4099 {
4100 int 	fd, status;
4101 
4102 	P_DPRINTF("  g_reserve: Reserve: Path %s\n", path);
4103 	if ((fd = g_object_open(path, O_NDELAY | O_RDONLY)) == -1)
4104 		return (L_OPEN_PATH_FAIL);
4105 	status = g_scsi_reserve_cmd(fd);
4106 	(void) close(fd);
4107 	return (status);
4108 }
4109 
4110 int
4111 g_release(char *path)
4112 {
4113 int 	fd, status;
4114 
4115 	P_DPRINTF("  g_release: Release: Path %s\n", path);
4116 	if ((fd = g_object_open(path, O_NDELAY | O_RDONLY)) == -1)
4117 		return (L_OPEN_PATH_FAIL);
4118 	status = g_scsi_release_cmd(fd);
4119 	(void) close(fd);
4120 	return (status);
4121 }
4122 
4123 static char
4124 ctoi(char c)
4125 {
4126 	if ((c >= '0') && (c <= '9'))
4127 		c -= '0';
4128 	else if ((c >= 'A') && (c <= 'F'))
4129 		c = c - 'A' + 10;
4130 	else if ((c >= 'a') && (c <= 'f'))
4131 		c = c - 'a' + 10;
4132 	else
4133 		c = -1;
4134 	return (c);
4135 }
4136 
4137 int
4138 g_string_to_wwn(uchar_t *wwn, uchar_t *wwnp)
4139 {
4140 	int	i;
4141 	char	c, c1;
4142 
4143 	*wwnp++ = 0;
4144 	*wwnp++ = 0;
4145 	for (i = 0; i < WWN_SIZE - 2; i++, wwnp++) {
4146 		c = ctoi(*wwn++);
4147 		c1 = ctoi(*wwn++);
4148 		if (c == -1 || c1 == -1)
4149 			return (-1);
4150 		*wwnp = ((c << 4) + c1);
4151 	}
4152 
4153 	return (0);
4154 
4155 }
4156 
4157 /*
4158  * Converts a string of WWN ASCII characters to a
4159  * binary representation.
4160  *
4161  * Input: string - pointer to uchar_t array
4162  *		WWN in ASCII
4163  *		length: 16 bytes
4164  * Output: wwn - pointer to uchar_t array
4165  *		containing WWN result
4166  *		length: 8 bytes
4167  * Returns:
4168  *	non-zero on error
4169  *	zero on success
4170  */
4171 int
4172 string_to_wwn(uchar_t *string, uchar_t *wwn)
4173 {
4174 	int	i;
4175 	char	c, c1;
4176 	uchar_t *wwnp;
4177 
4178 	wwnp = wwn;
4179 
4180 	for (i = 0; i < WWN_SIZE; i++, wwnp++) {
4181 
4182 		c = ctoi(*string++);
4183 		c1 = ctoi(*string++);
4184 		if (c == -1 || c1 == -1)
4185 			return (-1);
4186 		*wwnp = ((c << 4) + c1);
4187 	}
4188 
4189 	return (0);
4190 
4191 }
4192 
4193 
4194 /*
4195  * Get multiple paths to a given device port.
4196  * INPUTS:
4197  *	port WWN string.
4198  */
4199 int
4200 g_get_port_multipath(char *port_wwn_s, struct dlist **dlh, int verbose)
4201 {
4202 int		err;
4203 WWN_list	*wwn_list, *wwn_list_ptr;
4204 struct dlist	*dlt, *dl;
4205 
4206 
4207 	/* Initialize list structures. */
4208 	dl = *dlh  = dlt = (struct dlist *)NULL;
4209 	wwn_list = wwn_list_ptr = NULL;
4210 
4211 	H_DPRINTF("  g_get_port_multipath: Looking for multiple paths for"
4212 		" device with\n    port WWW:"
4213 		"%s\n", port_wwn_s);
4214 
4215 	if (err = g_get_wwn_list(&wwn_list, verbose)) {
4216 		return (err);
4217 	}
4218 
4219 	for (wwn_list_ptr = wwn_list; wwn_list_ptr != NULL;
4220 				wwn_list_ptr = wwn_list_ptr->wwn_next) {
4221 		if (strcmp(port_wwn_s, wwn_list_ptr->port_wwn_s) == 0) {
4222 			if ((dl = (struct dlist *)
4223 				g_zalloc(sizeof (struct dlist))) == NULL) {
4224 				while (*dlh != NULL) {
4225 					dl = (*dlh)->next;
4226 					(void) g_destroy_data(*dlh);
4227 					*dlh = dl;
4228 				}
4229 				(void) g_free_wwn_list(&wwn_list);
4230 				return (L_MALLOC_FAILED);
4231 			}
4232 			H_DPRINTF("  g_get_port_multipath:"
4233 				" Found multipath:\n    %s\n",
4234 				wwn_list_ptr->physical_path);
4235 			dl->dev_path = strdup(wwn_list_ptr->physical_path);
4236 			dl->logical_path = strdup(wwn_list_ptr->logical_path);
4237 			if (*dlh == NULL) {
4238 				*dlh = dlt = dl;
4239 			} else {
4240 				dlt->next = dl;
4241 				dl->prev = dlt;
4242 				dlt = dl;
4243 			}
4244 		}
4245 	}
4246 	(void) g_free_wwn_list(&wwn_list);
4247 	return (0);
4248 }
4249 
4250 
4251 
4252 /*
4253  * Get multiple paths to a given disk/tape device.
4254  * The arg: devpath should be the physical path to device.
4255  *
4256  * OUTPUT:
4257  *	multipath_list	points to a list of multiple paths to the device.
4258  *	NOTE: The caller must free the allocated list (dlist).
4259  *
4260  * RETURNS:
4261  *	0	 if O.K.
4262  *	non-zero otherwise
4263  */
4264 int
4265 g_get_multipath(char *devpath, struct dlist **multipath_list,
4266 	struct wwn_list_struct *wwn_list, int verbose)
4267 {
4268 int	err;
4269 
4270 	H_DPRINTF("  g_get_multipath: Looking for multiple paths for"
4271 		" device at path: %s\n", devpath);
4272 
4273 	/* return invalid path if devpath is NULL */
4274 	if (devpath == NULL) {
4275 		return (L_INVALID_PATH);
4276 	}
4277 	/* return invalid arg if argument is NULL */
4278 	if ((multipath_list == NULL) || (wwn_list == NULL)) {
4279 		return (L_INVALID_ARG);
4280 	}
4281 
4282 	if (strstr(devpath, DRV_NAME_SSD) != NULL) {
4283 		err = get_multipath_disk(devpath, multipath_list, wwn_list);
4284 	} else {
4285 		err = get_multipath(devpath, multipath_list, wwn_list);
4286 	}
4287 
4288 	return (err);
4289 }
4290 
4291 
4292 /*
4293  * Returns multipath information for a ssd device.
4294  * Inputs:
4295  *	devpath: device path to for requested multipath info
4296  *	wwn_list: returned from g_get_wwn_list or devices_get_all
4297  * Output:
4298  *	multipath_list: dlist list of paths
4299  * Returns:
4300  *	0 on success
4301  *	non-zero on failure
4302  */
4303 int
4304 get_multipath_disk(char *devpath, struct dlist **multipath_list,
4305 	struct wwn_list_struct *wwn_list)
4306 {
4307 WWN_list	*wwn_list_ptr;
4308 struct dlist	*dl = NULL, *dlt = NULL;
4309 ddi_devid_t	devid = NULL;
4310 int		err;
4311 di_node_t	root;
4312 struct mplist_struct	*mplistp = NULL, *mplisth = NULL;
4313 
4314 	if (wwn_list == NULL || multipath_list == NULL || devpath == NULL) {
4315 		return (L_NULL_WWN_LIST);
4316 	}
4317 
4318 	if ((root = di_init("/", DINFOCPYALL)) == DI_NODE_NIL) {
4319 		return (L_DEV_SNAPSHOT_FAILED);
4320 	}
4321 
4322 	if ((err = g_devid_get(devpath, &devid, root, SSD_DRVR_NAME)) != 0) {
4323 		di_fini(root);
4324 		return (err);
4325 	}
4326 
4327 	*multipath_list = (struct dlist *)NULL;
4328 	if ((err = devid_get_all(devid, root, SSD_DRVR_NAME, &mplisth)) != 0) {
4329 		di_fini(root);
4330 		return (err);
4331 	}
4332 
4333 	if (mplisth == NULL) {
4334 		di_fini(root);
4335 		return (L_NULL_WWN_LIST);
4336 	}
4337 
4338 	for (wwn_list_ptr = wwn_list; wwn_list_ptr != NULL;
4339 				wwn_list_ptr = wwn_list_ptr->wwn_next) {
4340 		/*
4341 		 * When a path is found from the list, load the logical
4342 		 * and physical dev path
4343 		 */
4344 		for (mplistp = mplisth; mplistp != NULL;
4345 				mplistp = mplistp->next) {
4346 		    if (strncmp(mplistp->devpath, wwn_list_ptr->physical_path,
4347 			strlen(mplistp->devpath)) == 0) {
4348 
4349 			/* Load multipath list */
4350 			if ((dl = (struct dlist *)
4351 				calloc(1, sizeof (struct dlist))) == NULL) {
4352 				while (*multipath_list != NULL) {
4353 					dl = dlt->next;
4354 					g_destroy_data(dlt);
4355 					dlt = dl;
4356 				}
4357 				di_fini(root);
4358 				return (L_MALLOC_FAILED);
4359 			}
4360 			H_DPRINTF("  g_get_multipath: Found multipath=%s\n",
4361 					wwn_list_ptr->physical_path);
4362 			dl->logical_path = strdup(wwn_list_ptr->logical_path);
4363 			dl->dev_path = strdup(wwn_list_ptr->physical_path);
4364 			if (*multipath_list == NULL) {
4365 				*multipath_list = dlt = dl;
4366 			} else {
4367 				dlt->next = dl;
4368 				dl->prev = dlt;
4369 				dlt = dl;
4370 			}
4371 		    }
4372 		}
4373 	}
4374 	di_fini(root);
4375 	mplist_free(mplisth);
4376 	return (0);
4377 }
4378 
4379 int
4380 get_multipath(char *devpath, struct dlist **multipath_list,
4381 	struct wwn_list_struct *wwn_list)
4382 {
4383 WWN_list	*wwn_list_ptr;
4384 struct dlist	*dl, *dlt;
4385 char		path[MAXPATHLEN], m_phys_path[MAXPATHLEN], *ptr;
4386 int		len;
4387 int		lun_a = -1;
4388 char		node_wwn_s[WWN_S_LEN];
4389 
4390 	if (devpath == NULL) {
4391 		return (L_INVALID_PATH);
4392 	}
4393 
4394 	/* Strip partition information. */
4395 	if ((ptr = strrchr(devpath, ':')) != NULL) {
4396 		len = strlen(devpath) - strlen(ptr);
4397 		(void) strncpy(path, devpath, len);
4398 		path[len] = '\0';
4399 	} else {
4400 		(void) strcpy(path, devpath);
4401 	}
4402 
4403 	*multipath_list = dl = dlt = (struct dlist *)NULL;
4404 
4405 
4406 	if (wwn_list == NULL) {
4407 		return (L_NULL_WWN_LIST);
4408 	}
4409 
4410 	for (*node_wwn_s = NULL, wwn_list_ptr = wwn_list;
4411 				wwn_list_ptr != NULL;
4412 				wwn_list_ptr = wwn_list_ptr->wwn_next) {
4413 
4414 		if ((ptr = strrchr(wwn_list_ptr->physical_path, ':')) != NULL) {
4415 			len = strlen(wwn_list_ptr->physical_path) - strlen(ptr);
4416 			(void) strncpy(m_phys_path, wwn_list_ptr->physical_path,
4417 					len);
4418 			m_phys_path[len] = '\0';
4419 		} else {
4420 			(void) strcpy(m_phys_path, wwn_list_ptr->physical_path);
4421 		}
4422 
4423 		if (strcasecmp(m_phys_path, path) == 0) {
4424 			(void) strcpy(node_wwn_s, wwn_list_ptr->node_wwn_s);
4425 			break;
4426 		}
4427 	}
4428 
4429 	if (*node_wwn_s == NULL) {
4430 		H_DPRINTF("node_wwn_s is NULL!\n");
4431 		return (L_NO_NODE_WWN_IN_WWNLIST);
4432 	}
4433 
4434 	lun_a = g_get_lun_number(wwn_list_ptr->physical_path);
4435 
4436 	for (wwn_list_ptr = wwn_list; wwn_list_ptr != NULL;
4437 				wwn_list_ptr = wwn_list_ptr->wwn_next) {
4438 		if ((strcmp(node_wwn_s, wwn_list_ptr->node_wwn_s) == 0) &&
4439 			((lun_a < 0) || (lun_a ==
4440 			g_get_lun_number(wwn_list_ptr->physical_path)))) {
4441 
4442 			if ((dl = (struct dlist *)
4443 				g_zalloc(sizeof (struct dlist))) == NULL) {
4444 				while (*multipath_list != NULL) {
4445 					dl = dlt->next;
4446 					(void) g_destroy_data(dlt);
4447 					dlt = dl;
4448 				}
4449 				return (L_MALLOC_FAILED);
4450 			}
4451 			H_DPRINTF("  g_get_multipath: Found multipath=%s\n",
4452 					wwn_list_ptr->physical_path);
4453 			dl->dev_path = strdup(wwn_list_ptr->physical_path);
4454 			dl->logical_path = strdup(wwn_list_ptr->logical_path);
4455 			if (*multipath_list == NULL) {
4456 				*multipath_list = dlt = dl;
4457 			} else {
4458 				dlt->next = dl;
4459 				dl->prev = dlt;
4460 				dlt = dl;
4461 			}
4462 		}
4463 	}
4464 	return (0);
4465 }
4466 
4467 /*
4468  * Free a multipath list
4469  *
4470  */
4471 void
4472 g_free_multipath(struct dlist *dlh)
4473 {
4474 struct dlist	*dl;
4475 
4476 	while (dlh != NULL) {
4477 		dl = dlh->next;
4478 		if (dlh->dev_path != NULL)
4479 			(void) g_destroy_data(dlh->dev_path);
4480 		if (dlh->logical_path != NULL)
4481 			(void) g_destroy_data(dlh->logical_path);
4482 		(void) g_destroy_data(dlh);
4483 		dlh = dl;
4484 	}
4485 }
4486 
4487 
4488 
4489 /*
4490  * Get the path to the nexus (HBA) driver.
4491  * This assumes the path looks something like this:
4492  * /devices/sbus@1f,0/SUNW,socal@1,0/SUNW,sf@0,0/ses@e,0:0
4493  * or maybe this
4494  * /devices/sbus@1f,0/SUNW,socal@1,0/SUNW,sf@1,0
4495  * or
4496  * /devices/sbus@1f,0/SUNW,socal@1,0
4497  * or
4498  * /devices/sbus@1f,0/SUNW,socal@1,0:1
4499  * or
4500  * /devices/sbus@1f,0/SUNW,socal@1,0/SUNW,sf@0,0:devctl
4501  * (or "qlc" instead of "socal" and "fp" for "sf")
4502  *
4503  * Which should resolve to a path like this:
4504  * /devices/sbus@1f,0/SUNW,socal@1,0:1
4505  * or
4506  * /devices/pci@6,2000/pci@2/SUNW,qlc@5
4507  *
4508  * or
4509  * /devices/pci@4,2000/scsi@1/ses@w50800200000000d2,0:0
4510  * which should resolve to
4511  * /devices/pci@4,2000/scsi@1:devctl
4512  */
4513 int
4514 g_get_nexus_path(char *path_phys, char **nexus_path)
4515 {
4516 uchar_t		port = 0;
4517 int		port_flag = 0, i = 0, pathcnt = 1;
4518 char		*char_ptr;
4519 char		drvr_path[MAXPATHLEN];
4520 char		buf[MAXPATHLEN];
4521 char		temp_buf[MAXPATHLEN];
4522 struct stat	stbuf;
4523 uint_t		path_type;
4524 mp_pathlist_t	pathlist;
4525 int		p_on = 0, p_st = 0;
4526 
4527 	/* return invalid path if the path_phys is NULL */
4528 	if (path_phys == NULL) {
4529 		return (L_INVALID_PATH);
4530 	}
4531 
4532 	*nexus_path = NULL;
4533 	(void) strcpy(drvr_path, path_phys);
4534 
4535 	if (strstr(path_phys, SCSI_VHCI)) {
4536 		if (g_get_pathlist(drvr_path, &pathlist)) {
4537 			return (L_INVALID_PATH);
4538 		}
4539 		pathcnt = pathlist.path_count;
4540 		p_on = p_st = 0;
4541 		for (i = 0; i < pathcnt; i++) {
4542 			if (pathlist.path_info[i].path_state < MAXPATHSTATE) {
4543 				if (pathlist.path_info[i].path_state ==
4544 					MDI_PATHINFO_STATE_ONLINE) {
4545 					p_on = i;
4546 					break;
4547 				} else if (pathlist.path_info[i].path_state ==
4548 					MDI_PATHINFO_STATE_STANDBY) {
4549 					p_st = i;
4550 				}
4551 			}
4552 		}
4553 		if (pathlist.path_info[p_on].path_state ==
4554 		    MDI_PATHINFO_STATE_ONLINE) {
4555 			/* on_line path */
4556 			(void) strcpy(drvr_path,
4557 				pathlist.path_info[p_on].path_hba);
4558 		} else {
4559 			/* standby or path0 */
4560 			(void) strcpy(drvr_path,
4561 				pathlist.path_info[p_st].path_hba);
4562 		}
4563 		free(pathlist.path_info);
4564 		(void) strcat(drvr_path, FC_CTLR);
4565 	} else {
4566 		if (strstr(drvr_path, DRV_NAME_SSD) || strstr(drvr_path,
4567 			DRV_NAME_ST) || strstr(drvr_path, SES_NAME)) {
4568 			if ((char_ptr = strrchr(drvr_path, '/')) == NULL) {
4569 				return (L_INVALID_PATH);
4570 			}
4571 			*char_ptr = '\0';   /* Terminate string  */
4572 		}
4573 
4574 	path_type = g_get_path_type(drvr_path);
4575 
4576 	if (path_type & FC4_SF_XPORT) {
4577 
4578 		/* sf driver in path so capture the port # */
4579 		if ((char_ptr = strstr(drvr_path, "sf@")) == NULL) {
4580 				return (L_INVALID_PATH);
4581 		}
4582 		port = atoi(char_ptr + 3);
4583 		if (port > 1) {
4584 			return (L_INVLD_PORT_IN_PATH);
4585 		}
4586 
4587 		if ((char_ptr = strrchr(drvr_path, '/')) == NULL) {
4588 			return (L_INVALID_PATH);
4589 		}
4590 		*char_ptr = '\0';   /* Terminate string  */
4591 		port_flag++;
4592 
4593 		L_DPRINTF("  g_get_nexus_path:"
4594 			" sf driver in path so use port #%d.\n",
4595 			port);
4596 	} else if (path_type & FC_GEN_XPORT) {
4597 		/*
4598 		 * check to see if it 3rd party vendor FCA.
4599 		 * if it is return error for this operation since
4600 		 * we don't know how they creates FCA port related minor node.
4601 		 *
4602 		 * As of now there is no supported operation on FCA node so
4603 		 * this should be okay.
4604 		 */
4605 		if ((path_type & FC_FCA_MASK) == FC_FCA_MASK) {
4606 			return (L_INVALID_PATH_TYPE);
4607 		}
4608 		/*
4609 		 * For current Sun FCA driver, appending
4610 		 * port # doesn't work. Just remove transport layer from
4611 		 * input path.
4612 		 */
4613 		if ((char_ptr = strstr(drvr_path, "/fp@")) == NULL) {
4614 			return (L_INVALID_PATH);
4615 		}
4616 		*char_ptr = '\0';   /* Terminate string  */
4617 	}
4618 
4619 	if (stat(drvr_path, &stbuf) != 0) {
4620 		return (L_LSTAT_ERROR);
4621 	}
4622 
4623 	if ((stbuf.st_mode & S_IFMT) == S_IFDIR) {
4624 		/*
4625 		 * Found a directory.
4626 		 * Now append a port number or devctl to the path.
4627 		 */
4628 		if (port_flag) {
4629 			/* append port */
4630 			(void) sprintf(buf, ":%d", port);
4631 		} else {
4632 			/* Try adding port 0 and see if node exists. */
4633 			(void) sprintf(temp_buf, "%s:0", drvr_path);
4634 			if (stat(temp_buf, &stbuf) != 0) {
4635 				/*
4636 				 * Path we guessed at does not
4637 				 * exist so it may be a driver
4638 				 * that ends in :devctl.
4639 				 */
4640 				(void) sprintf(buf, ":devctl");
4641 			} else {
4642 				/*
4643 				 * The path that was entered
4644 				 * did not include a port number
4645 				 * so the port was set to zero, and
4646 				 * then checked. The default path
4647 				 * did exist.
4648 				 */
4649 				ER_DPRINTF("Since a complete path"
4650 					" was not supplied "
4651 					"a default path is being"
4652 					" used:\n  %s\n",
4653 					temp_buf);
4654 				(void) sprintf(buf, ":0");
4655 			}
4656 		}
4657 
4658 		(void) strcat(drvr_path, buf);
4659 	}
4660 
4661 	}
4662 	*nexus_path = g_alloc_string(drvr_path);
4663 	L_DPRINTF("  g_get_nexus_path: Nexus path = %s\n", drvr_path);
4664 	return (0);
4665 }
4666 
4667 
4668 /*
4669  * Get the FC topology for the input device or nexus(HBA) path.
4670  *
4671  * The routine calls g_get_path_type to determine the stack of
4672  * the input path.
4673  *
4674  * 	If it a socal path
4675  *		it returns FC_TOP_PRIVATE_LOOP
4676  *	else
4677  *		calls fc_get_topology ioctl to
4678  *		get the fp topolgy from the driver.
4679  *
4680  * INPUTS:
4681  *	path - a string of device path, transport path.
4682  *		NOTE:  "path" SHOULD NOT BE OPEN BEFORE CALLING
4683  *			THIS FUNCTION BECAUSE THIS FUNCTION DOES
4684  *			AN "O_EXCL" OPEN.
4685  *	port_top - a pointer to the toplogy type.
4686  *
4687  * RETURNS:
4688  *	0 if there is no error.
4689  *	error code.
4690  *
4691  * The input path is expected to be something like below:
4692  * 	1)
4693  * 	/devices/sbus@1f,0/SUNW,socal@1,0/SUNW,sf@0,0/ses@e,0:0
4694  * 	/devices/sbus@1f,0/SUNW,socal@1,0/SUNW,sf@0,0/ssd@..
4695  * 	/devices/sbus@1f,0/SUNW,socal@1,0/SUNW,sf@1,0
4696  * 	/devices/sbus@1f,0/SUNW,socal@1,0
4697  * 	/devices/sbus@1f,0/SUNW,socal@1,0:1
4698  * 	/devices/sbus@1f,0/SUNW,socal@1,0/SUNW,sf@0,0:devctl
4699  * 	(or "qlc" instead of "socal" and "fp" for "sf")
4700  *
4701  * 	Which should resolve to a path like this:
4702  * 	/devices/sbus@1f,0/SUNW,socal@1,0:1
4703  * 	/devices/pci@6,2000/pci@2/SUNW,qlc@5
4704  *
4705  * 	2)
4706  * 	/devices/pci@4,2000/scsi@1/ses@w50800200000000d2,0:0
4707  * 	which should resolve to
4708  * 	/devices/pci@4,2000/scsi@1:devctl
4709  *
4710  *      3) The nexus(hba or nexus) path will get an error only for qlc
4711  *	since the routine need to open fp :devctl node for fcio ioctl.
4712  * 	/devices/sbus@1f,0/SUNW,socal@1,0
4713  * 	/devices/sbus@1f,0/SUNW,socal@1,0:1
4714  * 	/devices/pci@6,2000/pci@2/SUNW,qlc@5 => error
4715  */
4716 int
4717 g_get_fca_port_topology(char *path, uint32_t *port_top, int verbose)
4718 {
4719 fcio_t		fcio;
4720 int		fd, i = 0, pathcnt = 1;
4721 char		drvr_path[MAXPATHLEN];
4722 char		*char_ptr;
4723 struct stat	stbuf;
4724 uint_t		dev_type;
4725 mp_pathlist_t	pathlist;
4726 int		p_on = 0, p_st = 0;
4727 
4728 	/* return invalid path if the path is NULL */
4729 	if (path == NULL) {
4730 		return (L_INVALID_PATH);
4731 	}
4732 	/* return invalid arg if the argument is NULL */
4733 	if (port_top == NULL) {
4734 		return (L_INVALID_ARG);
4735 	}
4736 
4737 	(void) strcpy(drvr_path, path);
4738 	if (strstr(path, SCSI_VHCI)) {
4739 		if (g_get_pathlist(drvr_path, &pathlist)) {
4740 			return (L_INVALID_PATH);
4741 		}
4742 		pathcnt = pathlist.path_count;
4743 		p_on = p_st = 0;
4744 		for (i = 0; i < pathcnt; i++) {
4745 			if (pathlist.path_info[i].path_state < MAXPATHSTATE) {
4746 				if (pathlist.path_info[i].path_state ==
4747 					MDI_PATHINFO_STATE_ONLINE) {
4748 					p_on = i;
4749 					break;
4750 				} else if (pathlist.path_info[i].path_state ==
4751 					MDI_PATHINFO_STATE_STANDBY) {
4752 					p_st = i;
4753 				}
4754 			}
4755 		}
4756 		if (pathlist.path_info[p_on].path_state ==
4757 		    MDI_PATHINFO_STATE_ONLINE) {
4758 			/* on_line path */
4759 			(void) strcpy(drvr_path,
4760 				pathlist.path_info[p_on].path_hba);
4761 		} else {
4762 			/* standby or path0 */
4763 			(void) strcpy(drvr_path,
4764 				pathlist.path_info[p_st].path_hba);
4765 		}
4766 		free(pathlist.path_info);
4767 		(void) strcat(drvr_path, FC_CTLR);
4768 	} else {
4769 	/*
4770 	 * Get the path to the :devctl driver
4771 	 *
4772 	 * This assumes the path looks something like this:
4773 	 * /devices/sbus@1f,0/SUNW,socal@1,0/SUNW,sf@0,0/ses@e,0:0
4774 	 * or
4775 	 * /devices/sbus@1f,0/SUNW,socal@1,0/SUNW,sf@0,0
4776 	 * or
4777 	 * /devices/sbus@1f,0/SUNW,socal@1,0/SUNW,sf@0,0:devctl
4778 	 * or
4779 	 * a 1 level PCI type driver but still :devctl
4780 	 * (or "qlc" in the place of "socal" and "fp" for "sf")
4781 	 *
4782 	 * The dir below doesn't have corresponding :devctl node.
4783 	 * /devices/pci@6,2000/pci@2/SUNW,qlc@5
4784 	 * /devices/sbus@2,0/SUNW,socal@1,0
4785 	 *
4786 	 */
4787 		if ((strstr(drvr_path, DRV_NAME_SSD) ||
4788 			strstr(drvr_path, SES_NAME)) ||
4789 			strstr(drvr_path, DRV_NAME_ST)) {
4790 			if ((char_ptr = strrchr(drvr_path, '/')) == NULL) {
4791 				return (L_INVALID_PATH);
4792 			}
4793 			*char_ptr = '\0';   /* Terminate sting  */
4794 			/* append controller */
4795 			(void) strcat(drvr_path, FC_CTLR);
4796 		} else {
4797 			if (stat(drvr_path, &stbuf) < 0) {
4798 				return (L_LSTAT_ERROR);
4799 			}
4800 			if ((stbuf.st_mode & S_IFMT) == S_IFDIR) {
4801 				/* append controller */
4802 				(void) strcat(drvr_path, FC_CTLR);
4803 			}
4804 		}
4805 	}
4806 
4807 	if ((dev_type = g_get_path_type(drvr_path)) == 0) {
4808 		return (L_INVALID_PATH);
4809 	}
4810 
4811 	if ((dev_type & FC4_XPORT_MASK) || (dev_type & FC4_FCA_MASK)) {
4812 		*port_top = FC_TOP_PRIVATE_LOOP;
4813 		return (0);
4814 	}
4815 
4816 	/* To contiue the path type should be fp :devctl node */
4817 	if (!(dev_type & FC_XPORT_MASK)) {
4818 		return (L_INVALID_PATH);
4819 	}
4820 
4821 	if ((fd = g_object_open(drvr_path, O_NDELAY | O_RDONLY)) == -1)
4822 		return (errno);
4823 
4824 	P_DPRINTF("  g_get_fca_port_topology: Geting topology from:"
4825 		" %s\n", drvr_path);
4826 
4827 	fcio.fcio_cmd = FCIO_GET_TOPOLOGY;
4828 	fcio.fcio_olen = sizeof (uint32_t);
4829 	fcio.fcio_xfer = FCIO_XFER_READ;
4830 	fcio.fcio_obuf = (caddr_t)port_top;
4831 	if (g_issue_fcio_ioctl(fd, &fcio, verbose) != 0) {
4832 		I_DPRINTF(" FCIO_GET_TOPOLOGY ioctl failed.\n");
4833 		close(fd);
4834 		return (L_FCIO_GET_TOPOLOGY_FAIL);
4835 	}
4836 	close(fd);
4837 	return (0);
4838 }
4839 
4840 
4841 /*
4842  * This functions enables or disables a FCA port depending on the
4843  * argument, cmd, passed to it. If cmd is PORT_OFFLINE, the function
4844  * tries to disable the port specified by the argument 'phys_path'. If
4845  * cmd is PORT_ONLINE, the function tries to enable the port specified
4846  * by the argument 'phys_path'.
4847  * INPUTS :
4848  *	nexus_port_ptr - Pointer to the nexus path of the FCA port to
4849  *			operate on
4850  *	cmd       - PORT_OFFLINE or PORT_ONLINE
4851  * RETURNS :
4852  *	0 on success and non-zero otherwise
4853  */
4854 static int
4855 g_set_port_state(char *nexus_port_ptr, int cmd)
4856 {
4857 	int	path_type, fd;
4858 
4859 	if ((path_type = g_get_path_type(nexus_port_ptr)) == 0) {
4860 		return (L_INVALID_PATH);
4861 	}
4862 
4863 	if ((fd = g_object_open(nexus_port_ptr, O_NDELAY|O_RDONLY)) == -1) {
4864 		return (L_OPEN_PATH_FAIL);
4865 	}
4866 
4867 	switch (cmd) {
4868 		case PORT_OFFLINE:
4869 			if (path_type & FC4_SOCAL_FCA) {
4870 				/*
4871 				 * Socal/sf drivers -
4872 				 * The socal driver currently returns EFAULT
4873 				 * even if the ioctl has completed successfully.
4874 				 */
4875 				if (ioctl(fd, FCIO_LOOPBACK_INTERNAL,
4876 							NULL) == -1) {
4877 					close(fd);
4878 					return (L_PORT_OFFLINE_FAIL);
4879 				}
4880 			} else {
4881 				/*
4882 				 * QLogic card -
4883 				 * Can't do much here since the driver currently
4884 				 * doesn't support this feature. We'll just fail
4885 				 * for now. Support can be added when the driver
4886 				 * is enabled with the feature at a later date.
4887 				 */
4888 				close(fd);
4889 				return (L_PORT_OFFLINE_UNSUPPORTED);
4890 			}
4891 			break;
4892 		case PORT_ONLINE:
4893 			if (path_type & FC4_SOCAL_FCA) {
4894 				/*
4895 				 * Socal/sf drivers
4896 				 * The socal driver currently returns EFAULT
4897 				 * even if the ioctl has completed successfully.
4898 				 */
4899 				if (ioctl(fd, FCIO_NO_LOOPBACK, NULL) == -1) {
4900 					close(fd);
4901 					return (L_PORT_ONLINE_FAIL);
4902 				}
4903 			} else {
4904 				/*
4905 				 * QLogic card -
4906 				 * Can't do much here since the driver currently
4907 				 * doesn't support this feature. We'll just fail
4908 				 * for now. Support can be added when the driver
4909 				 * is enabled with the feature at a later date.
4910 				 */
4911 				close(fd);
4912 				return (L_PORT_ONLINE_UNSUPPORTED);
4913 			}
4914 			break;
4915 		default:
4916 			close(fd);
4917 			return (-1);
4918 	}
4919 	close(fd);
4920 	return (0);
4921 }
4922 
4923 /*
4924  * The interfaces defined below (g_port_offline() and g_port_online())
4925  * are what will be exposed to applications. We will hide g_set_port_state().
4926  * They have to be functions (as against macros) because making them
4927  * macros will mean exposing g_set_port_state() and we dont want to do that
4928  */
4929 
4930 int
4931 g_port_offline(char *path)
4932 {
4933 	return (g_set_port_state(path, PORT_OFFLINE));
4934 }
4935 
4936 int
4937 g_port_online(char *path)
4938 {
4939 	return (g_set_port_state(path, PORT_ONLINE));
4940 }
4941 
4942 /*
4943  * This function sets the loopback mode for a port on a HBA
4944  * INPUTS :
4945  *	portpath	- Pointer to the path of the FCA port on which to
4946  *			set the loopback mode
4947  *	cmd       	- EXT_LPBACK
4948  *			  INT_LPBACK
4949  *			  NO_LPBACK
4950  * RETURNS :
4951  *	0 on success and non-zero otherwise
4952  */
4953 int
4954 g_loopback_mode(char *portpath, int cmd)
4955 {
4956 	int	path_type, fd;
4957 
4958 	if ((path_type = g_get_path_type(portpath)) == 0) {
4959 		return (L_INVALID_PATH);
4960 	}
4961 
4962 	if ((fd = g_object_open(portpath, O_NDELAY|O_RDONLY|O_EXCL)) == -1) {
4963 		return (L_OPEN_PATH_FAIL);
4964 	}
4965 
4966 	/*
4967 	 * The loopback calls are currently not fully supported
4968 	 * via fp.
4969 	 *
4970 	 * A fp based general solution is required to support Leadville FCAs
4971 	 * including Qlgc and 3rd party FCA. As of now qlgc provides
4972 	 * some diag functions like echo through qlc private ioctl
4973 	 * which is not supproted by luxadm and libraries.
4974 	 */
4975 	switch (cmd) {
4976 		case EXT_LPBACK:
4977 			if (path_type & FC4_SOCAL_FCA) {
4978 				if (ioctl(fd, FCIO_LOOPBACK_MANUAL,
4979 							NULL) == -1) {
4980 					/* Check for previous mode set */
4981 					if (errno != EALREADY) {
4982 						close(fd);
4983 						return (L_LOOPBACK_FAILED);
4984 					}
4985 				}
4986 			} else {
4987 				/*
4988 				 * Well, it wasn't one of the above cards so..
4989 				 */
4990 				close(fd);
4991 				return (L_LOOPBACK_UNSUPPORTED);
4992 			}
4993 			break;
4994 		case NO_LPBACK:
4995 			if (path_type & FC4_SOCAL_FCA) {
4996 				if (ioctl(fd, FCIO_NO_LOOPBACK, NULL) == -1) {
4997 					close(fd);
4998 					return (L_LOOPBACK_FAILED);
4999 				}
5000 			} else {
5001 				/*
5002 				 * Well, it wasn't one of the above cards so..
5003 				 */
5004 				close(fd);
5005 				return (L_LOOPBACK_UNSUPPORTED);
5006 			}
5007 			break;
5008 		case INT_LPBACK:
5009 			if (path_type & FC4_SOCAL_FCA) {
5010 				if (ioctl(fd, FCIO_LOOPBACK_INTERNAL,
5011 					NULL) == -1) {
5012 					/* Check for previous mode set */
5013 					if (errno != EALREADY) {
5014 						close(fd);
5015 						return (L_LOOPBACK_FAILED);
5016 					}
5017 				}
5018 			} else {
5019 				/*
5020 				 * Well, it wasn't one of the above cards so..
5021 				 */
5022 				close(fd);
5023 				return (L_LOOPBACK_UNSUPPORTED);
5024 			}
5025 			break;
5026 		default:
5027 			close(fd);
5028 			return (L_LOOPBACK_UNSUPPORTED);
5029 	}
5030 	close(fd);
5031 	return (0);
5032 }
5033 
5034 /*
5035  * g_get_port_state(char *portpath, int port_state)
5036  * Purpose: Get port state for a path
5037  * Input:   portpath
5038  *		set to path of port
5039  * Output:  port_state
5040  *	Set to one of the following:
5041  *		PORT_CONNECTED
5042  *		PORT_NOTCONNECTED
5043  * Returns: 0 on success
5044  *	    non-zero on failure
5045  */
5046 int
5047 g_get_port_state(char *portpath, int *portstate, int verbose)
5048 {
5049 	int	fd, err, num_devices = 0;
5050 	struct lilpmap	map;
5051 	uint_t	dev_type;
5052 	gfc_dev_t	map_root;
5053 
5054 
5055 	(void) memset(&map, 0, sizeof (struct lilpmap));
5056 
5057 	/* return invalid path if portpath is NULL */
5058 	if (portpath == NULL) {
5059 		return (L_INVALID_PATH);
5060 	}
5061 	/* return invalid arg if argument is NULL */
5062 	if ((portpath == NULL) || (portstate == NULL)) {
5063 		return (L_INVALID_ARG);
5064 	}
5065 
5066 	if ((dev_type = g_get_path_type(portpath)) == 0) {
5067 		return (L_INVALID_PATH);
5068 	}
5069 
5070 	/*
5071 	 * FCIO_GETMAP returns error when there are * no devices attached.
5072 	 * ENOMEM is returned when no devices are attached.
5073 	 * g_get_first_dev returns NULL without error when there is no
5074 	 * devices are attached.
5075 	 */
5076 	if (dev_type & FC_FCA_MASK) {
5077 		if ((map_root = g_dev_map_init(portpath, &err,
5078 			MAP_XPORT_PROP_ONLY)) == NULL) {
5079 			return (err);
5080 		}
5081 
5082 		if (g_get_first_dev(map_root, &err) == NULL) {
5083 			/* no device is found if err == 0 */
5084 			if (err == L_NO_SUCH_DEV_FOUND) {
5085 				*portstate = PORT_NOTCONNECTED;
5086 			}
5087 			g_dev_map_fini(map_root);
5088 			return (0);
5089 		} else {
5090 			/* Device found okay */
5091 			*portstate = PORT_CONNECTED;
5092 			g_dev_map_fini(map_root);
5093 		}
5094 
5095 	} else {
5096 		/* open controller */
5097 		if ((fd = g_object_open(portpath, O_NDELAY | O_RDONLY)) == -1) {
5098 			return (errno);
5099 		}
5100 
5101 		/*
5102 		 * Note: There is only one error returned by this ioctl. ENOMEM.
5103 		 * Hence the lack of return on error.
5104 		 */
5105 		if (ioctl(fd, FCIO_GETMAP, &map) != 0) {
5106 			map.lilp_length = 0;
5107 		}
5108 		num_devices = map.lilp_length;
5109 
5110 		/* Non-Leadville stacks report the FCA in the count */
5111 		*portstate = (num_devices > 1) ? PORT_CONNECTED :
5112 							PORT_NOTCONNECTED;
5113 		(void) close(fd);
5114 	}
5115 	return (0);
5116 }
5117 
5118 /*
5119  * g_dev_login(char *port_path, la_wwn_t port_wwn)
5120  * Purpose: port login via g_dev_log_in_out()
5121  * Input:   port_path
5122  *		fc transport port with fabric/public loop topology
5123  *	    port_wwn
5124  *		port wwn of device node to login
5125  *
5126  * Returns: return code from g_dev_log_in_out()
5127  */
5128 int
5129 g_dev_login(char *port_path, la_wwn_t port_wwn)
5130 {
5131 	return (g_dev_log_in_out(port_path, port_wwn, FCIO_DEV_LOGIN));
5132 }
5133 
5134 
5135 /*
5136  * g_dev_logout(char *port_path, la_wwn_t port_wwn)
5137  * Purpose: port login via g_dev_log_in_out()
5138  * Input:   port_path
5139  *		fc transport port with fabric/public loop topology
5140  *	    port_wwn
5141  *		port wwn of device node to logout
5142  *
5143  * Returns: return code from g_dev_log_in_out()
5144  */
5145 int
5146 g_dev_logout(char *port_path, la_wwn_t port_wwn)
5147 {
5148 	return (g_dev_log_in_out(port_path, port_wwn, FCIO_DEV_LOGOUT));
5149 }
5150 
5151 
5152 /*
5153  * g_dev_log_in_out(char *port_path, la_wwn_t port_wwn, uint16_t cmd)
5154  * Purpose: port login via FCIO_DEV_LOGOUT and port logout via FCIO_DEV_LOGOUT
5155  *	    IOCTL requires EXCLUSIVE open.
5156  * Input:   port_path
5157  *		fc transport port with fabric/public loop topology
5158  *	    port_wwn
5159  *		port wwn of device node to logout
5160  *	    cmd
5161  *		FCIO_DEV_LOGON or FCIO_DEV_LOGOUT
5162  *
5163  * Returns: 0 on success
5164  *	    non-zero on failure
5165  */
5166 static int
5167 g_dev_log_in_out(char *port_path, la_wwn_t port_wwn, uint16_t cmd)
5168 {
5169 int		fd, err;
5170 uint32_t	hba_port_top;
5171 fcio_t		fcio;
5172 int		verbose = 0;
5173 
5174 	if ((err = g_get_fca_port_topology(port_path,
5175 		&hba_port_top, verbose)) != 0) {
5176 		return (err);
5177 	}
5178 
5179 	if (!((hba_port_top == FC_TOP_PUBLIC_LOOP) ||
5180 		(hba_port_top == FC_TOP_FABRIC))) {
5181 		return (L_OPNOSUPP_ON_TOPOLOGY);
5182 	}
5183 
5184 	/* open controller */
5185 	if ((fd = g_object_open(port_path, O_NDELAY | O_RDONLY | O_EXCL)) == -1)
5186 		return (L_OPEN_PATH_FAIL);
5187 
5188 	/*
5189 	 * stores port_wwn to la_wwn_t raw_wwn field
5190 	 * and construct fcio structures for FCIO_DEV_LOGIN.
5191 	 */
5192 	fcio.fcio_cmd = cmd;
5193 	fcio.fcio_ilen = sizeof (port_wwn);
5194 	fcio.fcio_ibuf = (caddr_t)&port_wwn;
5195 	fcio.fcio_xfer = FCIO_XFER_WRITE;
5196 	fcio.fcio_olen = fcio.fcio_alen = 0;
5197 	fcio.fcio_obuf = fcio.fcio_abuf = NULL;
5198 	if (g_issue_fcio_ioctl(fd, &fcio, verbose) != 0) {
5199 		I_DPRINTF((cmd == FCIO_DEV_LOGIN) ?
5200 			" FCIO_DEV_LOGIN ioctl failed.\n"
5201 			: " FCIO_DEV_LOGOUT ioctl failed.\n");
5202 		(void) close(fd);
5203 		return ((cmd == FCIO_DEV_LOGIN) ?
5204 			L_FCIO_DEV_LOGIN_FAIL
5205 			: L_FCIO_DEV_LOGOUT_FAIL);
5206 	} else {
5207 		(void) close(fd);
5208 		return (0);
5209 	}
5210 }
5211 
5212 /*
5213  * This function will verify if a FC device (represented by input WWN
5214  * is connected on a FCA port by searching the device list from
5215  * g_get_dev_list() for a WWN match.
5216  *
5217  * input:
5218  *   fca_path: pointer to the physical path string, path to a fp node.
5219  *             possible forms are
5220  *		/devices/pci@1f,2000/pci@1/SUNW,qlc@5/fp@0,0:devctl
5221  *   dev_wwn: WWN string
5222  *   flag: indicate that the input WWN is node or port
5223  *
5224  * returned values
5225  *   0: if a match is found.
5226  *   L_WWN_NOT_FOUND_IN_DEV_LIST: if no match found
5227  *   L_UNEXPECTED_FC_TOPOLOGY: existing error code for an error
5228  *	from the topology checking of the input fca path.
5229  *   L_MALLOC_FAILED: existing error code for allocation eror from the
5230  *	g_get_dev_list().
5231  *   L_FCIO_GETMAP_IOCTL_FAIL: existing error code for an error from the
5232  *	FCIO ioctl called by the g_get_dev_list()
5233  *   -1: other failure
5234  *
5235  */
5236 int
5237 g_wwn_in_dev_list(char *fca_path, la_wwn_t dev_wwn, int flag)
5238 {
5239 uint_t		dev_type;
5240 int		i, err;
5241 fc_port_dev_t	*dev_list;
5242 fc_port_dev_t	*dev_list_save;
5243 int		num_devices = 0;
5244 
5245 	if ((dev_type = g_get_path_type(fca_path)) == 0) {
5246 		return (L_INVALID_PATH);
5247 	}
5248 
5249 	if (!(dev_type & FC_XPORT_MASK)) {
5250 		return (L_INVALID_PATH_TYPE);
5251 	}
5252 
5253 	if (((err = g_get_dev_list(fca_path, &dev_list, &num_devices))
5254 		!= 0) && (err != L_GET_DEV_LIST_ULP_FAILURE)) {
5255 		return (err);
5256 	}
5257 
5258 	dev_list_save = dev_list;
5259 
5260 	switch (flag) {
5261 	case MATCH_NODE_WWN:
5262 		for (i = 0; i < num_devices; i++, dev_list++) {
5263 			if (memcmp(dev_list->dev_nwwn.raw_wwn,
5264 					dev_wwn.raw_wwn, FC_WWN_SIZE) == 0) {
5265 				(void) free(dev_list_save);
5266 				return (0);
5267 			}
5268 		}
5269 		(void) free(dev_list_save);
5270 		/* consider a new error code for not found. */
5271 		return (L_WWN_NOT_FOUND_IN_DEV_LIST);
5272 
5273 	case MATCH_PORT_WWN:
5274 		for (i = 0; i < num_devices; i++, dev_list++) {
5275 			if (memcmp(dev_list->dev_pwwn.raw_wwn,
5276 					dev_wwn.raw_wwn, FC_WWN_SIZE) == 0) {
5277 				(void) free(dev_list_save);
5278 				return (0);
5279 			}
5280 		}
5281 		(void) free(dev_list_save);
5282 		/* consider a new error code for not found. */
5283 		return (L_WWN_NOT_FOUND_IN_DEV_LIST);
5284 	}
5285 	(void) free(dev_list_save);
5286 	return (-1);
5287 }
5288 
5289 
5290 /*
5291  * g_get_dev_port_state(char *fca_path, la_wwn_t port_wwn, uint32_t *state)
5292  * Purpose: get the state of device port login via FCIO_GET_STATE ioctl.
5293  *
5294  * Input:   fca_path
5295  *		fc transport port with fabric/public loop topology
5296  *	    port_wwn
5297  *		port wwn of device node to logout
5298  *	    state
5299  *		port login or not
5300  *
5301  * Returns: 0 on success
5302  *	    non-zero on failure
5303  */
5304 static int
5305 g_get_dev_port_state(char *fca_path, la_wwn_t port_wwn, uint32_t *state)
5306 {
5307 int		fd;
5308 int		dev_type;
5309 fcio_t		fcio;
5310 int		verbose = 0;
5311 
5312 	if ((dev_type = g_get_path_type(fca_path)) == 0) {
5313 		return (L_INVALID_PATH);
5314 	}
5315 
5316 	if (!(dev_type & FC_XPORT_MASK)) {
5317 		return (L_INVALID_PATH_TYPE);
5318 	}
5319 
5320 	/* open controller */
5321 	if ((fd = g_object_open(fca_path, O_NDELAY | O_RDONLY)) == -1)
5322 		return (L_OPEN_PATH_FAIL);
5323 
5324 	/*
5325 	 * stores port_wwn to la_wwn_t raw_wwn field
5326 	 * and construct fcio structures for FCIO_DEV_LOGIN.
5327 	 */
5328 	fcio.fcio_cmd = FCIO_GET_STATE;
5329 	fcio.fcio_ilen = sizeof (port_wwn);
5330 	fcio.fcio_ibuf = (caddr_t)&port_wwn;
5331 	fcio.fcio_xfer = FCIO_XFER_READ | FCIO_XFER_WRITE;
5332 	fcio.fcio_olen = sizeof (uint32_t);
5333 	fcio.fcio_obuf = (caddr_t)state;
5334 	fcio.fcio_alen = 0;
5335 	fcio.fcio_abuf = NULL;
5336 	if (g_issue_fcio_ioctl(fd, &fcio, verbose) != 0) {
5337 		I_DPRINTF(" FCIO_GET_STATE ioctl failed.\n");
5338 		(void) close(fd);
5339 		return (L_FCIO_GET_STATE_FAIL);
5340 	} else {
5341 		(void) close(fd);
5342 		return (0);
5343 	}
5344 }
5345 
5346 /*
5347  * Name: lilp_map_cmp
5348  *
5349  * Description: This function is used to compare the physical location
5350  *              of to fc devices in a gfc_map_t.dev_addr arrary.
5351  *
5352  * Params:
5353  *	First device to compare
5354  *	Second device to compare
5355  *
5356  * Return:
5357  *   0 = Devices at equal phyiscal location, How did this happen?
5358  *  >0 = First device have a higher physical location than second
5359  *  <0 = Second device have a higher physical location than first
5360  */
5361 static int lilp_map_cmp(const void* dev1, const void* dev2) {
5362 	int i_dev1 = ((fc_port_dev_t *)dev1)->dev_did.priv_lilp_posit;
5363 	int i_dev2 = ((fc_port_dev_t *)dev2)->dev_did.priv_lilp_posit;
5364 
5365 	if (i_dev1 > i_dev2)
5366 		return (1);
5367 	if (i_dev1 < i_dev2)
5368 		return (-1);
5369 	return (0);
5370 }
5371 
5372 /*
5373  * Description:
5374  *    Retrieves multiple paths to a device based on devid
5375  *    Caller must use mplist_free to free mplist structure
5376  *    This currently only supports ssd devices.
5377  *    The st driver does not register a device id.
5378  *
5379  * Input Values:
5380  *
5381  *    devid: ptr to valid ddi_devid_t struct
5382  *    root: root handle to device tree snapshot
5383  *    drvr_name: driver name to start the node tree search
5384  *
5385  * Return Value:
5386  *    0 on success
5387  *    non-zero on failure
5388  */
5389 
5390 static int
5391 devid_get_all(ddi_devid_t devid, di_node_t root, char *drvr_name,
5392 		struct mplist_struct **mplistp)
5393 {
5394 ddi_devid_t mydevid;
5395 di_node_t node;
5396 char *devfs_path = NULL;
5397 struct mplist_struct *mpl, *mpln;
5398 
5399 	if (devid == NULL || root == NULL || drvr_name == NULL ||
5400 		mplistp == NULL ||
5401 		(strncmp(drvr_name, SSD_DRVR_NAME, strlen(SSD_DRVR_NAME))
5402 			!= 0)) {
5403 		return (EINVAL);
5404 	}
5405 
5406 	*mplistp = mpl = mpln = (struct mplist_struct *)NULL;
5407 
5408 	/* point to first node which matches portdrvr */
5409 	node = di_drv_first_node(drvr_name, root);
5410 	if (node == DI_NODE_NIL) {
5411 		return (L_NO_DRIVER_NODES_FOUND);
5412 	}
5413 
5414 	while (node != DI_NODE_NIL) {
5415 		if ((mydevid = di_devid(node)) != NULL) {
5416 			if (((devid_compare(mydevid, devid)) == 0)) {
5417 			    /* Load multipath list */
5418 			    if ((mpl = (struct mplist_struct *)
5419 				calloc(1, sizeof (struct mplist_struct)))
5420 					== NULL) {
5421 				mplist_free(*mplistp);
5422 				return (L_MALLOC_FAILED);
5423 			    }
5424 			    if ((devfs_path = my_devfs_path(node)) == NULL) {
5425 				node = di_drv_next_node(node);
5426 				S_FREE(mpl);
5427 				continue;
5428 			    }
5429 			    mpl->devpath = (char *)calloc(1,
5430 					strlen(devfs_path) +
5431 					strlen(SSD_MINOR_NAME) + 1);
5432 			    if (mpl->devpath == NULL) {
5433 				S_FREE(mpl);
5434 				mplist_free(*mplistp);
5435 				my_devfs_path_free(devfs_path);
5436 				return (L_MALLOC_FAILED);
5437 			    }
5438 			    sprintf(mpl->devpath, "%s%s", devfs_path,
5439 					SSD_MINOR_NAME);
5440 			    if (*mplistp == NULL) {
5441 				*mplistp = mpln = mpl;
5442 			    } else {
5443 				mpln->next = mpl;
5444 				mpln = mpl;
5445 			    }
5446 			    my_devfs_path_free(devfs_path);
5447 			}
5448 		}
5449 	node = di_drv_next_node(node);
5450 	}
5451 	return (0);
5452 }
5453 
5454 /*
5455  * Frees a previously allocated mplist_struct
5456  */
5457 static void
5458 mplist_free(struct mplist_struct *mplistp)
5459 {
5460 struct mplist_struct *mplistn;
5461 
5462 	while (mplistp != NULL) {
5463 		mplistn = mplistp->next;
5464 		if (mplistp->devpath != NULL) {
5465 			free(mplistp->devpath);
5466 			mplistp->devpath = NULL;
5467 		}
5468 		free(mplistp);
5469 		mplistp = mplistn;
5470 	}
5471 }
5472 
5473 /*
5474  * Description
5475  *	Retrieves all device nodes based on drvr_name
5476  *	Currently supports SSD_DRVR_NAME, ST_DRVR_NAME
5477  *	There will be a device node in the libdevinfo
5478  *	snapshot only if there is at least one node bound.
5479  *
5480  * Input values:
5481  *	root		valid snapshot handle from di_init(3DEVINFO)
5482  *	drvr_name	name of driver to start node search
5483  *	wwn_list_ptr	ptr to ptr to WWN_list struct
5484  *
5485  *
5486  */
5487 static int
5488 devices_get_all(di_node_t root, char *drvr_name, char *minor_name,
5489 	struct wwn_list_struct **wwn_list_ptr)
5490 {
5491 di_node_t node;
5492 char *devfs_path;
5493 char devicepath[MAXPATHLEN];
5494 uchar_t *nwwn = NULL, *pwwn = NULL;
5495 uchar_t node_wwn[WWN_SIZE], port_wwn[WWN_SIZE];
5496 WWN_list *wwn_list, *l1, *l2;
5497 int scsi_vhci = 0;
5498 int err, devtype;
5499 
5500 	if (root == DI_NODE_NIL || drvr_name == NULL ||
5501 		wwn_list_ptr == NULL) {
5502 		return (EINVAL);
5503 	}
5504 
5505 	wwn_list = *wwn_list_ptr = NULL;
5506 
5507 	memset(port_wwn, 0, sizeof (port_wwn));
5508 	memset(node_wwn, 0, sizeof (node_wwn));
5509 
5510 	if (strcmp(drvr_name, SSD_DRVR_NAME) == 0) {
5511 		devtype = DTYPE_DIRECT;
5512 	} else if (strcmp(drvr_name, ST_DRVR_NAME) == 0) {
5513 		devtype = DTYPE_SEQUENTIAL;
5514 	} else {
5515 		/*
5516 		 * An unsupported driver name was passed in
5517 		 */
5518 		return (L_DRIVER_NOTSUPP);
5519 	}
5520 
5521 	/* point to first node which matches portdrvr */
5522 	node = di_drv_first_node(drvr_name, root);
5523 	if (node == DI_NODE_NIL) {
5524 		return (L_NO_DEVICES_FOUND);
5525 	}
5526 
5527 	while (node != DI_NODE_NIL) {
5528 
5529 	    if ((devfs_path = my_devfs_path(node)) != NULL) {
5530 
5531 		/*
5532 		 * Check for offline state
5533 		 */
5534 		if ((di_state(node) & DI_DEVICE_OFFLINE) == DI_DEVICE_OFFLINE) {
5535 			my_devfs_path_free(devfs_path);
5536 			node = di_drv_next_node(node);
5537 			continue;
5538 		}
5539 
5540 		/*
5541 		 * Only support st, ssd nodes
5542 		 */
5543 		if (!strstr(devfs_path, SLSH_DRV_NAME_SSD) &&
5544 			!strstr(devfs_path, SLSH_DRV_NAME_ST)) {
5545 			my_devfs_path_free(devfs_path);
5546 			node = di_drv_next_node(node);
5547 			continue;
5548 		}
5549 
5550 		devicepath[0] = '\0';
5551 
5552 		/*
5553 		 * form device path
5554 		 */
5555 		sprintf(devicepath, "%s%s", devfs_path, minor_name);
5556 
5557 		if ((strstr(devicepath, SCSI_VHCI) == NULL)) {
5558 			if ((err = get_wwn_data(node, &nwwn, &pwwn)) != 0) {
5559 				my_devfs_path_free(devfs_path);
5560 				return (err);
5561 			} else {
5562 				memcpy(node_wwn, nwwn, sizeof (node_wwn));
5563 				memcpy(port_wwn, pwwn, sizeof (port_wwn));
5564 			}
5565 		} else {
5566 			/*
5567 			 * Clear values for SCSI VHCI devices.
5568 			 * node wwn, port wwn are irrevelant at
5569 			 * the SCSI VHCI level
5570 			 */
5571 			scsi_vhci++;
5572 			memset(port_wwn, 0, sizeof (port_wwn));
5573 			memset(node_wwn, 0, sizeof (node_wwn));
5574 		}
5575 
5576 		/* Got wwns, load data in list */
5577 		if ((l2 = (struct  wwn_list_struct *)
5578 			calloc(1, sizeof (struct  wwn_list_struct))) == NULL) {
5579 			my_devfs_path_free(devfs_path);
5580 			return (L_MALLOC_FAILED);
5581 		}
5582 		if ((l2->physical_path = (char *)
5583 			calloc(1, strlen(devicepath) +1)) == NULL) {
5584 			my_devfs_path_free(devfs_path);
5585 			return (L_MALLOC_FAILED);
5586 		}
5587 
5588 		memcpy(l2->w_node_wwn, node_wwn, WWN_SIZE);
5589 
5590 		if (scsi_vhci) {
5591 		    strcpy(l2->node_wwn_s, MSGSTR(12000, "N/A"));
5592 		} else {
5593 		    copy_wwn_data_to_str(l2->node_wwn_s, node_wwn);
5594 		    copy_wwn_data_to_str(l2->port_wwn_s, port_wwn);
5595 		}
5596 
5597 		strcpy(l2->physical_path, devicepath);
5598 
5599 		l2->device_type = devtype;
5600 		if (wwn_list == NULL) {
5601 			l1 = wwn_list = l2;
5602 		} else {
5603 			l2->wwn_prev = l1;
5604 			l1 = l1->wwn_next = l2;
5605 		}
5606 		my_devfs_path_free(devfs_path);
5607 		scsi_vhci = 0;
5608 	    }
5609 	    node = di_drv_next_node(node);
5610 	}
5611 
5612 	*wwn_list_ptr = wwn_list; /* pass back ptr to list */
5613 
5614 	if (*wwn_list_ptr == NULL) {
5615 		return (L_NO_DEVICES_FOUND);
5616 	} else {
5617 		/*
5618 		 * Now load the /dev/ paths
5619 		 */
5620 		if (strcmp(drvr_name, SSD_DRVR_NAME) == 0) {
5621 			if ((err = get_dev_path(wwn_list_ptr, DEV_RDIR,
5622 					DIR_MATCH_SSD)) != 0) {
5623 				g_free_wwn_list(wwn_list_ptr);
5624 				return (err);
5625 			}
5626 		} else if (strcmp(drvr_name, ST_DRVR_NAME) == 0) {
5627 			if ((err = get_dev_path(wwn_list_ptr, DEV_TAPE_DIR,
5628 					DIR_MATCH_ST)) != 0) {
5629 				g_free_wwn_list(wwn_list_ptr);
5630 				return (err);
5631 			}
5632 		}
5633 		return (0);
5634 	}
5635 }
5636 
5637 
5638 /*
5639  * Access the properties for the node to get the node-wwn, port-wwn property
5640  * On error, contents of nwwn, pwwn are unspecified.
5641  * On successful return nwwn and pwwn are WWN_SIZE bytes.
5642  */
5643 static int
5644 get_wwn_data(di_node_t node, uchar_t **nwwn, uchar_t **pwwn)
5645 {
5646 	if (di_prop_lookup_bytes(DDI_DEV_T_ANY, node, NODE_WWN_PROP,
5647 			nwwn) != WWN_SIZE) {
5648 	/* If we didn't get back the right count, return error */
5649 		return (L_NO_WWN_PROP_FOUND);
5650 	}
5651 	if (di_prop_lookup_bytes(DDI_DEV_T_ANY, node, PORT_WWN_PROP,
5652 			pwwn) != WWN_SIZE) {
5653 	/* If we didn't get back the right count, return error */
5654 		return (L_NO_WWN_PROP_FOUND);
5655 	}
5656 	return (0);
5657 }
5658 
5659 /*
5660  * Description
5661  *	retrieves the /dev logical path for a WWN_list of devices.
5662  * Input values
5663  *	wwn_list_ptr	ptr to list returned by devices_get_all
5664  *	dir_name	/dev/ directory to search
5665  *
5666  */
5667 static int
5668 get_dev_path(struct wwn_list_struct **wwn_list_ptr, char *dir_name,
5669 	char *pattern_match)
5670 {
5671 DIR		*dirp;
5672 struct dirent	*entp;
5673 char		namebuf[MAXPATHLEN];
5674 char		*result = NULL;
5675 WWN_list	*wwn_list, *wwn_list_save;
5676 char		*env;
5677 hrtime_t	start_time, end_time;
5678 
5679 	if (wwn_list_ptr == NULL || *wwn_list_ptr == NULL ||
5680 		dir_name == NULL || pattern_match == NULL) {
5681 		return (EINVAL);
5682 	}
5683 
5684 	if ((env = getenv("_LUX_T_DEBUG")) != NULL) {
5685 		start_time = gethrtime();
5686 	}
5687 
5688 	wwn_list = *wwn_list_ptr;
5689 
5690 	if ((dirp = opendir(dir_name)) == NULL) {
5691 		P_DPRINTF("  get_dev_path: No devices found\n");
5692 		return (L_NO_DEVICES_FOUND);
5693 	}
5694 
5695 	while ((entp = readdir(dirp)) != NULL) {
5696 		/*
5697 		 * Ignore current directory and parent directory
5698 		 * entries.
5699 		 */
5700 		if ((strcmp(entp->d_name, ".") == 0) ||
5701 		    (strcmp(entp->d_name, "..") == 0) ||
5702 		    (fnmatch(pattern_match, entp->d_name, 0) != 0))
5703 			continue;
5704 
5705 		memset(namebuf, 0, sizeof (namebuf));
5706 		sprintf(namebuf, "%s/%s", dir_name, entp->d_name);
5707 
5708 		if ((result = g_get_physical_name_from_link(namebuf)) == NULL) {
5709 			ER_DPRINTF("  Warning: Get physical name from"
5710 				" link failed. Link=%s\n", namebuf);
5711 			continue;
5712 		}
5713 		for (wwn_list = *wwn_list_ptr; wwn_list != NULL;
5714 		    wwn_list = wwn_list->wwn_next) {
5715 		    if (strcmp(wwn_list->physical_path, result) == 0) {
5716 			/*
5717 			 * Add information to the list.
5718 			 */
5719 			if ((wwn_list->logical_path = (char *)
5720 				calloc(1, strlen(namebuf) + 1)) == NULL) {
5721 				free(result);
5722 				return (L_MALLOC_FAILED);
5723 			}
5724 			strcpy(wwn_list->logical_path, namebuf);
5725 			break;
5726 		    }
5727 		}
5728 		free(result);
5729 	}
5730 	closedir(dirp);
5731 
5732 	/*
5733 	 * Did we load all of the paths?
5734 	 * Note: if there is a missing entry in /dev then
5735 	 * the user probably did a cleanup of /dev.
5736 	 * Whatever the case, remove the entry as it
5737 	 * is invalid.
5738 	 */
5739 	wwn_list = *wwn_list_ptr;
5740 	while (wwn_list != NULL) {
5741 		if (wwn_list->logical_path == NULL) {
5742 			free(wwn_list->physical_path);
5743 			wwn_list_save = wwn_list;
5744 			if (wwn_list->wwn_prev != NULL) {
5745 				wwn_list->wwn_prev->wwn_next =
5746 					wwn_list->wwn_next;
5747 			} else {
5748 				/*
5749 				 * No previous entries
5750 				 */
5751 				*wwn_list_ptr = wwn_list->wwn_next;
5752 			}
5753 			if (wwn_list->wwn_next != NULL) {
5754 				wwn_list->wwn_next->wwn_prev =
5755 					wwn_list->wwn_prev;
5756 			}
5757 			wwn_list = wwn_list->wwn_next;
5758 			free(wwn_list_save);
5759 		} else {
5760 			wwn_list = wwn_list->wwn_next;
5761 		}
5762 	}
5763 
5764 	if (env != NULL) {
5765 		end_time = gethrtime();
5766 		fprintf(stdout,
5767 		"      get_dev_path %s:  "
5768 		"\t\tTime = %lld millisec\n",
5769 		dir_name, (end_time - start_time)/1000000);
5770 	}
5771 
5772 	if (*wwn_list_ptr == NULL) {
5773 		return (L_NO_DEVICES_FOUND);
5774 	} else {
5775 		return (0);
5776 	}
5777 }
5778 
5779 /*
5780  * This functions calls di_devfs_path and gets the path associated with a
5781  * given devinfo node. If the path returned does not have a '@' in it, it
5782  * checks if the driver is detached and creates a path after looking at the
5783  * driver properties.
5784  *
5785  * di_devfs_path_free is called internally.
5786  *
5787  * The argument 'path' points to the final value upon return.
5788  * Caller must use my_devfs_path_free on returned char *
5789  * Note: Only support FC/SCSI_VHCI devices,
5790  *       for FC check for node-wwn prop
5791  *
5792  */
5793 static char *
5794 my_devfs_path(di_node_t node)
5795 {
5796 	uchar_t	*pwwn = NULL;
5797 	char	pwwns[WWN_SIZE*2+1];
5798 	char	*mypath;
5799 	int	scsi_vhci = 0;
5800 	char	*tptr = NULL, *lun_guid = NULL;
5801 	int	*lunnump = NULL;
5802 
5803 	/* sanity check */
5804 	if (node == DI_NODE_NIL) {
5805 		return (NULL);
5806 	}
5807 
5808 	/* Now go get the path for this node */
5809 	if ((tptr = di_devfs_path(node)) == NULL) {
5810 		return (NULL);
5811 	}
5812 
5813 	if ((mypath = (char *)calloc(1, MAXPATHLEN + 1)) == NULL) {
5814 		di_devfs_path_free(tptr);
5815 		return (NULL);
5816 	}
5817 
5818 	/* Prepend "/devices" to libdevinfo-returned paths */
5819 	sprintf(mypath, "%s%s", DEVICES_DIR, tptr);
5820 
5821 	di_devfs_path_free(tptr);
5822 
5823 
5824 	/*
5825 	 * Is this a FC device?
5826 	 * Check the pwwn property
5827 	 */
5828 	if (strstr(mypath, SCSI_VHCI) == NULL) {
5829 		if (di_prop_lookup_bytes(DDI_DEV_T_ANY, node, PORT_WWN_PROP,
5830 				&pwwn) < 0) {
5831 			/* Not a FC device. Free path and return */
5832 			free(mypath);
5833 			return (NULL);
5834 		}
5835 	} else {
5836 		scsi_vhci++;
5837 	}
5838 
5839 	if ((tptr = strrchr(mypath, '/')) == NULL) {
5840 		free(mypath);
5841 		return (NULL);
5842 	}
5843 
5844 	if (strchr(tptr, '@') != NULL) {
5845 		return (mypath);
5846 	}
5847 
5848 	/*
5849 	 * No '@' in path. This can happen when driver is detached.
5850 	 * We'll check if the state is detached and if it is, we'll construct
5851 	 * the path by looking at the properties.
5852 	 */
5853 
5854 	if ((di_state(node) & DI_DRIVER_DETACHED) != DI_DRIVER_DETACHED) {
5855 		/*
5856 		 * Driver is not detached and no '@' in path.
5857 		 * Can't handle it.
5858 		 */
5859 		free(mypath);
5860 		return (NULL);
5861 	}
5862 
5863 	if (!scsi_vhci) {
5864 		copy_wwn_data_to_str(pwwns, pwwn);
5865 		di_prop_lookup_ints(DDI_DEV_T_ANY, node, LUN_PROP, &lunnump);
5866 		sprintf(&mypath[strlen(mypath)], "@w%s,%x", pwwn, *lunnump);
5867 	} else {
5868 		di_prop_lookup_strings(DDI_DEV_T_ANY, node,
5869 			LUN_GUID_PROP, &lun_guid);
5870 		sprintf(&mypath[strlen(mypath)], "@g%s", lun_guid);
5871 	}
5872 	return (mypath);
5873 }
5874 
5875 static void
5876 my_devfs_path_free(char *path)
5877 {
5878 	if (path != NULL) {
5879 		free(path);
5880 	}
5881 }
5882 
5883 /*
5884  * from_ptr: ptr to uchar_t array of size WWN_SIZE
5885  * to_ptr: char ptr to string of size WWN_SIZE*2+1
5886  */
5887 static void
5888 copy_wwn_data_to_str(char *to_ptr, const uchar_t *from_ptr)
5889 {
5890 	if ((to_ptr == NULL) || (from_ptr == NULL))
5891 		return;
5892 
5893 	sprintf(to_ptr, "%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x",
5894 	from_ptr[0], from_ptr[1], from_ptr[2], from_ptr[3],
5895 	from_ptr[4], from_ptr[5], from_ptr[6], from_ptr[7]);
5896 }
5897 
5898 /*
5899  * Open the requested directory and get one valid open.
5900  * If a device is busy, return.
5901  * Only need to open one device since
5902  * that implies there will be a node returned from
5903  * di_drv_first_node()
5904  * dir_name: logical device name directory
5905  *	(DEV_TAPE_DIR, DEV_RDIR)
5906  * pattern_match: used by fnmatch on directory entry
5907  *	(DIR_MATCH_SSD, DIR_MATCH_ST)
5908  * drvr_path: path type to verify ("/ssd@", "/st@")
5909  *	(SLSH_DRV_NAME_ST, SLSH_DRV_NAME_SSD)
5910  *
5911  * Returns: None
5912  */
5913 static void
5914 init_drv(char *dir_name, char *pattern_match, char *drvr_path)
5915 {
5916 DIR		*dirp;
5917 struct dirent	*entp;
5918 char		namebuf[MAXPATHLEN];
5919 char		*result = NULL;
5920 int		fd;
5921 
5922 	if ((dirp = opendir(dir_name)) == NULL) {
5923 		return;
5924 	}
5925 
5926 	while ((entp = readdir(dirp)) != NULL) {
5927 		/*
5928 		 * Ignore current directory and parent directory
5929 		 * entries.
5930 		 */
5931 		if ((strcmp(entp->d_name, ".") == 0) ||
5932 		    (strcmp(entp->d_name, "..") == 0) ||
5933 		    (fnmatch(pattern_match, entp->d_name, 0) != 0)) {
5934 			continue;
5935 		}
5936 
5937 		memset(namebuf, 0, sizeof (namebuf));
5938 		sprintf(namebuf, "%s/%s", dir_name, entp->d_name);
5939 
5940 		if ((result = g_get_physical_name_from_link(namebuf)) == NULL) {
5941 			ER_DPRINTF("  Warning: Get physical name from"
5942 				" link failed. Link=%s\n", namebuf);
5943 			continue;
5944 		}
5945 
5946 		if (strstr(result, drvr_path) == NULL) {
5947 			free(result);
5948 			result = NULL;
5949 			continue;
5950 		}
5951 
5952 		if ((fd = g_object_open(result, O_NDELAY | O_RDONLY)) != -1) {
5953 			close(fd);
5954 			break;
5955 		} else if (errno != EBUSY) {
5956 			free(result);
5957 			result = NULL;
5958 			continue;
5959 		} else {
5960 			break;
5961 		}
5962 	}
5963 	free(result);
5964 	closedir(dirp);
5965 }
5966