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