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