xref: /illumos-gate/usr/src/lib/storage/liba5k/common/lhot.c (revision 20a7641f9918de8574b8b3b47dbe35c4bfc78df1)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 
27 /*LINTLIBRARY*/
28 
29 
30 /*
31  *	This module is part of the photon library
32  */
33 
34 /*
35  * I18N message number ranges
36  *  This file: 8500 - 8999
37  *  Shared common messages: 1 - 1999
38  */
39 
40 /* #define		_POSIX_SOURCE 1 */
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/param.h>
49 #include	<fcntl.h>
50 #include	<unistd.h>
51 #include	<errno.h>
52 #include	<string.h>
53 #include	<time.h>
54 #include	<sys/scsi/scsi.h>
55 #include	<sys/vtoc.h>
56 #include	<nl_types.h>
57 #include	<strings.h>
58 #include	<sys/ddi.h>		/* for max */
59 #include	<l_common.h>
60 #include	<stgcom.h>
61 #include	<l_error.h>
62 #include	<rom.h>
63 #include	<a_state.h>
64 #include	<a5k.h>
65 
66 
67 /*	Global variables	*/
68 extern	uchar_t		g_switch_to_alpa[];
69 extern	uchar_t		g_sf_alpa_to_switch[];
70 
71 
72 /*
73  * This function checks if the passed char pointer has WWN_SIZE nulls (zeroes).
74  *
75  * This is only a convenience function.
76  *
77  * INPUT:
78  * wwn_ptr -	pointer to a character string of length WWN_SIZE
79  *		It is expected to be holding the WWN
80  *		Ex: A WWN like 508002000000ddc1 is expected to be stored as
81  *		the following 8 bytes -
82  *		0x50, 0x80, 0x00, 0x20, ... etc
83  *
84  * RETURNS:
85  * 0 - if there is atleast one of WWN_SIZE bytes is != '\0'
86  * non-zero - if all WWN_SIZE bytes are '\0'
87  */
88 int
89 is_null_wwn(uchar_t *wwn_ptr)
90 {
91 	int i;
92 
93 	for (i = 0; i < WWN_SIZE; i++) {
94 		if (wwn_ptr[i] != '\0' || wwn_ptr[i] != '0')
95 			return (0);
96 	}
97 	return (1);
98 }
99 
100 
101 /*
102  * This functions constructs a device path of the device/enclosure with the
103  * given tid and, for public/fabric cases, on the same area and domain as
104  * the given ses_path.
105  *
106  * INPUT:
107  * ses_path	- pointer to the ses_path
108  * tid		- tid of the device/enclosure whose path is to be constructed
109  * map		- pointer to the map
110  * dtype	- dtype of the device whose path is to be constructed
111  *
112  * OUTPUT:
113  * dev_path	- pointer to the device path of type dtype and with tid
114  *		- Caller has to free this after use
115  *
116  * RETURNS:
117  * 0 - on success
118  * non-zero - otherwise
119  */
120 int
121 l_make_node(char *ses_path, int tid, char *dev_path,
122 			gfc_map_t *map, int dtype)
123 {
124 int			len, i, err;
125 int			this_pid, ses_pid;
126 char			ssd[40], wwn[20];
127 gfc_port_dev_info_t	*dev_addr_ptr;
128 struct stat		stat_buf;
129 WWN_list		*wwnlp, *wwn_list;
130 int			found = 0;
131 
132 	if ((ses_path == NULL) || (dev_path == NULL) || (map == NULL)) {
133 		return (L_INVALID_PATH_FORMAT);
134 	}
135 
136 	switch (map->hba_addr.port_topology) {
137 	case FC_TOP_PRIVATE_LOOP:
138 		for (i = 0, dev_addr_ptr = map->dev_addr;
139 			i < map->count; i++, dev_addr_ptr++) {
140 			if (dev_addr_ptr->gfc_port_dev.priv_port.
141 				sf_al_pa == g_switch_to_alpa[tid])
142 				break;
143 		}
144 		if (i >= map->count) {
145 			*dev_path = '\0';
146 			return (L_INVALID_LOOP_MAP);
147 		}
148 
149 		/* Make sure that the port WWN is valid */
150 		if (is_null_wwn(dev_addr_ptr->gfc_port_dev.
151 			priv_port.sf_port_wwn)) {
152 			*dev_path = '\0';
153 			return (L_INVLD_WWN_FORMAT);
154 		}
155 
156 		(void) g_ll_to_str(dev_addr_ptr->gfc_port_dev.
157 			priv_port.sf_port_wwn, wwn);
158 
159 		if (strstr(ses_path, SCSI_VHCI) != NULL) {
160 			if (err = g_get_wwn_list(&wwn_list, 0)) {
161 				return (err);
162 			}
163 			for (wwnlp = wwn_list, found = 0;
164 				wwnlp != NULL;
165 				wwnlp = wwnlp->wwn_next) {
166 				if (strcmp(wwnlp->port_wwn_s,
167 					wwn) == 0) {
168 					found = 1;
169 					break;
170 				}
171 			}
172 			if (found) {
173 				(void) strcpy(dev_path,
174 					wwnlp->physical_path);
175 			} else {
176 				return (L_INVALID_PATH);
177 			}
178 		} else {
179 
180 			len = strlen(ses_path) -
181 			strlen(strrchr(ses_path, '/'));
182 
183 			if (dtype != DTYPE_ESI) {
184 				(void) sprintf(ssd,
185 					"/ssd@w%s,0:c", wwn);
186 			} else {
187 				(void) sprintf(ssd,
188 					"/ses@w%s,0:c", wwn);
189 			}
190 
191 			/* TBD: Must find path, not just use :c */
192 			(void) strncpy(dev_path, ses_path, len);
193 			dev_path[len] = '\0';
194 			(void) strcat(dev_path, ssd);
195 		}
196 		break;
197 	case FC_TOP_FABRIC:
198 	case FC_TOP_PUBLIC_LOOP:
199 		/* First lets get the PA from the ses path passed in */
200 		if (err = l_get_pid_from_path(ses_path, map, &ses_pid)) {
201 			return (err);
202 		}
203 
204 		/*
205 		 * Now we go through every entry in the map and match the
206 		 * area and domain ids with the PA of the passed ses path.
207 		 * If we find a match, we then match the low order byte
208 		 */
209 		for (i = 0, dev_addr_ptr = map->dev_addr; i < map->count;
210 							i++, dev_addr_ptr++) {
211 			this_pid = dev_addr_ptr->gfc_port_dev.pub_port.
212 								dev_did.port_id;
213 			if ((this_pid & AREA_DOMAIN_ID) ==
214 						(ses_pid & AREA_DOMAIN_ID)) {
215 			    if ((uchar_t)(this_pid & 0xFF) ==
216 							g_switch_to_alpa[tid])
217 				break;
218 			}
219 		}
220 		if (i >= map->count) {
221 			*dev_path = '\0';
222 			return (L_INVALID_LOOP_MAP);
223 		}
224 		/* Make sure that the port WWN is valid */
225 		if (is_null_wwn(dev_addr_ptr->gfc_port_dev.pub_port.
226 							dev_pwwn.raw_wwn)) {
227 			*dev_path = '\0';
228 			return (L_INVLD_WWN_FORMAT);
229 		}
230 		(void) g_ll_to_str(dev_addr_ptr->gfc_port_dev.
231 						pub_port.dev_pwwn.raw_wwn, wwn);
232 
233 
234 
235 		if (strstr(ses_path, SCSI_VHCI) != NULL) {
236 			if (err = g_get_wwn_list(&wwn_list, 0)) {
237 				return (err);
238 			}
239 			for (wwnlp = wwn_list, found = 0; wwnlp != NULL;
240 				wwnlp = wwnlp->wwn_next) {
241 				if (strcmp(wwnlp->port_wwn_s,
242 					wwn) == 0) {
243 						found = 1;
244 				}
245 			}
246 			if (found) {
247 				(void) strcpy(dev_path,
248 					wwnlp->physical_path);
249 			} else {
250 				return (L_INVALID_PATH);
251 			}
252 		} else {
253 			len = strlen(ses_path) -
254 				strlen(strrchr(ses_path, '/'));
255 
256 			if (dtype != DTYPE_ESI) {
257 				(void) sprintf(ssd, "/ssd@w%s,0:c", wwn);
258 			} else {
259 				(void) sprintf(ssd, "/ses@w%s,0:c", wwn);
260 			}
261 
262 			/* TBD: Must find path, not just use :c */
263 			(void) strncpy(dev_path, ses_path, len);
264 			dev_path[len] = '\0';
265 			(void) strcat(dev_path, ssd);
266 		}
267 
268 		if (stat(dev_path, &stat_buf) == -1) {
269 			return (errno);
270 		}
271 
272 		break;
273 	case FC_TOP_PT_PT:
274 		return (L_PT_PT_FC_TOP_NOT_SUPPORTED);
275 	default:
276 		return (L_UNEXPECTED_FC_TOPOLOGY);
277 	}	/* End of switch on port_topology */
278 	return (0);
279 }
280 
281 
282 
283 /*
284  * checks for null wwn to a disk.
285  * and returns -1 if found, 0
286  * otherwise.
287  *
288  * OUTPUT:
289  *	char	*ses_path
290  *
291  * RETURNS:
292  *	0	 if OK
293  *	non-zero otherwise
294  */
295 int
296 l_chk_null_wwn(Path_struct *path_struct, char *ses_path,
297 				L_state *l_state, int verbose)
298 {
299 char		*ptr, boxname[MAXPATHLEN];
300 char		node_wwn_s[WWN_SIZE * 2 + 1];
301 Box_list	*boxlist;
302 
303 
304 	if ((path_struct == NULL) || (ses_path == NULL) ||
305 	    (l_state == NULL)) {
306 		return (L_INVALID_PATH_FORMAT);
307 	}
308 
309 	/*
310 	 * verify and continue only if the argv
311 	 * has a format like box,{f/r}<slot #>.
312 	 * Otherwise, return to the caller.
313 	 * The only way to address null wwn disk
314 	 * is using the box,{f/r}<slot#> format.
315 	 */
316 /* add support for new {f/r/s}<slot#> support for DPM */
317 	(void) strcpy(boxname, path_struct->argv);
318 	if (((ptr = strstr(boxname, ",")) != NULL) &&
319 	    ((*(ptr + 1) == 'f') || (*(ptr + 1) == 'r') ||
320 	    (*(ptr + 1) == 's'))) {
321 		*ptr = '\0';
322 	} else {
323 		return (0);
324 	}
325 
326 
327 	/*
328 	 * Get the list of enclosures
329 	 * connected to the system.
330 	 */
331 	if (l_get_box_list(&boxlist, verbose) != 0) {
332 		return (L_NO_ENCL_LIST_FOUND);
333 	}
334 
335 	*ses_path = '\0';
336 
337 	/*
338 	 * The following method is safer to get an ses path
339 	 * to the enclosure than calling l_get_ses_path(),
340 	 * with physical path to null WWN disk.
341 	 * Because, l_get_ses_path uses the disk's
342 	 * al_pa to get the box id and then ses path
343 	 * to the box. When a disk has null wwn, it may
344 	 * not have a valid al_pa, and hard address.
345 	 * There is a possibility that l_get_ses_path()
346 	 * not returning ses path to the correct enclosure.
347 	 */
348 	while (boxlist != NULL) {
349 		if ((strcmp(boxname, (char *)boxlist->b_name) == 0)) {
350 			(void) strcpy(ses_path, boxlist->b_physical_path);
351 			break;
352 		}
353 		boxlist = boxlist->box_next;
354 	}
355 
356 	/* free the box list */
357 	(void) l_free_box_list(&boxlist);
358 
359 	if ((ses_path != NULL) && (strstr(ses_path, "ses") != NULL)) {
360 		if (l_get_status(ses_path, l_state,
361 				verbose) != 0) {
362 			return (L_GET_STATUS_FAILED);
363 		}
364 		if (path_struct->f_flag) {
365 			(void) strcpy(node_wwn_s,
366 		l_state->drv_front[path_struct->slot].g_disk_state.node_wwn_s);
367 		} else {
368 			(void) strcpy(node_wwn_s,
369 		l_state->drv_rear[path_struct->slot].g_disk_state.node_wwn_s);
370 		}
371 
372 		W_DPRINTF("Found ses path: %s\n"
373 			"and Node WWN: %s\n", ses_path, node_wwn_s);
374 
375 		/* check for null WWN */
376 		if (is_null_wwn((uchar_t *)node_wwn_s) == 0) {
377 			return (0);	/* Non-null wwn */
378 		}
379 		W_DPRINTF("Found NULL WWN: %s\n", node_wwn_s);
380 		return (1);
381 	}
382 
383 	return (0);
384 
385 }
386 
387 
388 
389 /*
390  * If OVERALL_STATUS is sent as the "func",
391  *	the code pointer must be valid (non NULL).
392  * Otherwise NULL is a valid input for the code pointer.
393  *
394  * RETURNS:
395  *	0	 if OK
396  *	non-zero otherwise
397  */
398 int
399 l_encl_status_page_funcs(int func, char *code, int todo, char *ses_path,
400     struct l_state_struct  *l_state, int f_flag, int slot, int verbose_flag)
401 {
402 	uchar_t	*page_buf;
403 	int	fd, front_index, rear_index, offset, err;
404 	unsigned short	page_len;
405 	struct	device_element *elem;
406 
407 	if ((ses_path == NULL) || (l_state == NULL)) {
408 		return (L_INVALID_PATH_FORMAT);
409 	}
410 
411 	if ((page_buf = (uchar_t *)g_zalloc(MAX_REC_DIAG_LENGTH)) == NULL) {
412 		return (L_MALLOC_FAILED);
413 	}
414 
415 	if ((fd = g_object_open(ses_path, O_NDELAY | O_RDWR)) == -1) {
416 		(void) g_destroy_data(page_buf);
417 		return (L_OPEN_PATH_FAIL);
418 	}
419 
420 	if ((err = l_get_envsen_page(fd, page_buf, MAX_REC_DIAG_LENGTH,
421 					L_PAGE_2, verbose_flag)) != 0) {
422 		(void) g_destroy_data(page_buf);
423 		(void) close(fd);
424 		return (err);
425 	}
426 
427 	page_len = (page_buf[2] << 8 | page_buf[3]) + HEADER_LEN;
428 
429 	if ((err = l_get_disk_element_index(l_state, &front_index,
430 							&rear_index)) != 0) {
431 		(void) g_destroy_data(page_buf);
432 		(void) close(fd);
433 		return (err);
434 	}
435 	/* Skip global element */
436 	front_index++;
437 	if ((strncmp((char *)l_state->ib_tbl.config.prod_id, DAK_OFF_NAME,
438 						strlen(DAK_OFF_NAME)) == 0) ||
439 		(strncmp((char *)l_state->ib_tbl.config.prod_id, DAK_PROD_STR,
440 						strlen(DAK_OFF_NAME)) == 0)) {
441 		rear_index += l_state->total_num_drv/2 + 1;
442 	} else
443 		rear_index++;
444 
445 	if (f_flag) {
446 		offset = (8 + (front_index + slot)*4);
447 	} else {
448 		offset = (8 + (rear_index  + slot)*4);
449 	}
450 
451 	elem = (struct device_element *)(page_buf + offset);
452 
453 	switch (func) {
454 		case OVERALL_STATUS:
455 		    if (code == NULL) {
456 			return (L_INVALID_ARG);
457 		    }
458 		    switch (todo) {
459 			case INSERT_DEVICE:
460 				*code = (elem->code != S_OK) ? elem->code : 0;
461 				(void) g_destroy_data(page_buf);
462 				(void) close(fd);
463 				return (0);
464 			case REMOVE_DEVICE:
465 				*code = (elem->code != S_NOT_INSTALLED) ?
466 					elem->code : 0;
467 				(void) g_destroy_data(page_buf);
468 				(void) close(fd);
469 				return (0);
470 		    }
471 		    /* NOTREACHED */
472 		case SET_RQST_INSRT:
473 			bzero(elem, sizeof (struct device_element));
474 			elem->select = 1;
475 			elem->rdy_to_ins = 1;
476 			break;
477 		case SET_RQST_RMV:
478 			bzero(elem, sizeof (struct device_element));
479 			elem->select = 1;
480 			elem->rmv = 1;
481 			elem->dev_off = 1;
482 			elem->en_bypass_a = 1;
483 			elem->en_bypass_b = 1;
484 			break;
485 		case SET_FAULT:
486 			bzero(elem, sizeof (struct device_element));
487 			elem->select = 1;
488 			elem->fault_req = 1;
489 			elem->dev_off = 1;
490 			elem->en_bypass_a = 1;
491 			elem->en_bypass_b = 1;
492 			break;
493 		case SET_DRV_ON:
494 			bzero(elem, sizeof (struct device_element));
495 			elem->select = 1;
496 			break;
497 	}
498 
499 	err = g_scsi_send_diag_cmd(fd, (uchar_t *)page_buf, page_len);
500 	(void) g_destroy_data(page_buf);
501 	(void) close(fd);
502 	return (err);
503 }
504 
505 
506 
507 /*
508  * Finds whether device id (tid) exists in the
509  * Arbitrated loop map or not.
510  *
511  * INPUT:
512  * ses_path	- pointer to a ses path
513  * tid		- the target id of the device we want to check on
514  *		- only the low order 8 bits has the tid
515  * map		- pointer to a map of the system
516  * verbose_flag - self explanatory
517  *
518  * OUTPUT:
519  * dev_path	- the device path of the device with "tid".
520  *                Caller is responsible for freeing it
521  *
522  * RETURNS:
523  *	1	 if device present
524  *	0	 otherwise
525  */
526 int
527 l_device_present(char *ses_path, int tid, gfc_map_t *map,
528 				int verbose_flag, char **dev_path)
529 {
530 char			sf_path[MAXPATHLEN];
531 uchar_t			wwn[40], c;
532 int			len, i, j, k, fnib, snib, this_pid;
533 int			fd, ses_pid, al_pa, err;
534 char			ssd[30];
535 gfc_port_dev_info_t	*dev_addr_ptr;
536 WWN_list		*wwnlp, *wwn_list;
537 
538 
539 	if (dev_path == NULL)
540 		return (0);
541 
542 	if ((ses_path == NULL) || (map == NULL)) {
543 		return (L_NO_SES_PATH);
544 	}
545 
546 	*dev_path = NULL;
547 
548 	switch (map->hba_addr.port_topology) {
549 	case FC_TOP_PRIVATE_LOOP:
550 		for (i = 0, dev_addr_ptr = map->dev_addr; i < map->count;
551 						i++, dev_addr_ptr++) {
552 			if (dev_addr_ptr->gfc_port_dev.
553 				priv_port.sf_inq_dtype != DTYPE_ESI) {
554 				al_pa = dev_addr_ptr->gfc_port_dev.
555 						priv_port.sf_al_pa;
556 				if (tid == g_sf_alpa_to_switch[al_pa]) {
557 					break;
558 				}
559 			}
560 		}
561 		if (i >= map->count)
562 			return (0);
563 		/*
564 		 * Make sure that the port WWN is valid
565 		 */
566 		if (is_null_wwn(dev_addr_ptr->gfc_port_dev.
567 						priv_port.sf_port_wwn)) {
568 			return (0);
569 		}
570 		for (j = 0, k = 0; j < WWN_SIZE; j++) {
571 			c = dev_addr_ptr->gfc_port_dev.priv_port.sf_port_wwn[j];
572 			fnib = (((int)(c & 0xf0)) >> 4);
573 			snib = (c & 0x0f);
574 			if (fnib >= 0 && fnib <= 9)
575 				wwn[k++] = '0' + fnib;
576 			else if (fnib >= 10 && fnib <= 15)
577 				wwn[k++] = 'a' + fnib - 10;
578 			if (snib >= 0 && snib <= 9)
579 				wwn[k++] = '0' + snib;
580 			else if (snib >= 10 && snib <= 15)
581 				wwn[k++] = 'a' + snib - 10;
582 		}
583 		wwn[k] = '\0';
584 		break;
585 	case FC_TOP_PUBLIC_LOOP:
586 	case FC_TOP_FABRIC:
587 		/*
588 		 * Get the phys address (port id) of this ses device
589 		 */
590 		if (err = l_get_pid_from_path(ses_path, map, &ses_pid))
591 			return (err);
592 
593 		for (i = 0, dev_addr_ptr = map->dev_addr; i < map->count;
594 							i++, dev_addr_ptr++) {
595 			if (dev_addr_ptr->gfc_port_dev.pub_port.dev_dtype !=
596 								DTYPE_ESI) {
597 				/*
598 				 * We have a device. First match the area and
599 				 * domain ids and if they match, then see if
600 				 * the 8bit tid matches the last 8 bits of
601 				 * 'this_pid'
602 				 */
603 				this_pid = dev_addr_ptr->gfc_port_dev.
604 						pub_port.dev_did.port_id;
605 				if ((this_pid & AREA_DOMAIN_ID) ==
606 						(ses_pid & AREA_DOMAIN_ID)) {
607 					if (tid == g_sf_alpa_to_switch[
608 							this_pid & 0xFF])
609 						break;
610 				}
611 			}
612 		}
613 
614 		if (i >= map->count)
615 			return (0);
616 		/*
617 		 * Make sure that the port WWN is valid
618 		 */
619 		if (is_null_wwn(dev_addr_ptr->gfc_port_dev.
620 						pub_port.dev_pwwn.raw_wwn)) {
621 			return (0);
622 		}
623 		for (j = 0, k = 0; j < WWN_SIZE; j++) {
624 			c = dev_addr_ptr->gfc_port_dev.pub_port.
625 							dev_pwwn.raw_wwn[j];
626 			fnib = (((int)(c & 0xf0)) >> 4);
627 			snib = (c & 0x0f);
628 			if (fnib >= 0 && fnib <= 9)
629 				wwn[k++] = '0' + fnib;
630 			else if (fnib >= 10 && fnib <= 15)
631 				wwn[k++] = 'a' + fnib - 10;
632 			if (snib >= 0 && snib <= 9)
633 				wwn[k++] = '0' + snib;
634 			else if (snib >= 10 && snib <= 15)
635 				wwn[k++] = 'a' + snib - 10;
636 		}
637 		wwn[k] = '\0';
638 		break;
639 	case FC_TOP_PT_PT:
640 		return (L_PT_PT_FC_TOP_NOT_SUPPORTED);
641 	default:
642 		return (L_UNEXPECTED_FC_TOPOLOGY);
643 	}	/* End of switch on port_topology */
644 
645 	if (strstr(ses_path, SCSI_VHCI) != NULL) {
646 		if (err = g_get_wwn_list(&wwn_list, 0)) {
647 			return (err);
648 		}
649 		for (wwnlp = wwn_list; wwnlp != NULL;
650 						wwnlp = wwnlp->wwn_next) {
651 			if (memcmp(wwnlp->port_wwn_s, wwn, WWN_S_LEN) == 0) {
652 				break;
653 			}
654 		}
655 		if (wwnlp != NULL) {
656 			if ((*dev_path = g_zalloc(MAXPATHLEN)) == NULL) {
657 				g_free_wwn_list(&wwn_list);
658 				return (L_MALLOC_FAILED);
659 			}
660 			(void) strcpy(*dev_path, wwnlp->physical_path);
661 		} else {
662 			g_free_wwn_list(&wwn_list);
663 			return (0);
664 		}
665 	} else {
666 
667 		len = strlen(ses_path) - strlen(strrchr(ses_path, '/'));
668 
669 		(void) sprintf(ssd, "ssd@w%s,0", wwn);
670 
671 		(void) strncpy(sf_path, ses_path, len);
672 		sf_path[len] = '\0';
673 		P_DPRINTF("  l_device_present: wwn=%s, sf_path=%s\n",
674 			wwn, sf_path);
675 
676 		if ((*dev_path = g_zalloc(MAXPATHLEN)) == NULL) {
677 			return (L_MALLOC_FAILED);
678 		}
679 		(void) sprintf(*dev_path, "%s/%s", sf_path, ssd);
680 		P_DPRINTF("  l_device_present: dev_path=%s\n", *dev_path);
681 
682 		(void) strcat(*dev_path, ":c");
683 	}
684 	if ((fd = open(*dev_path, O_RDONLY)) == -1) {
685 		free(*dev_path);
686 		*dev_path = NULL;
687 		return (0);
688 	}
689 	(void) close(fd);
690 	return (1);
691 }
692 
693 
694 
695 /*
696  * onlines the given list of devices
697  * and free up the allocated memory.
698  *
699  * RETURNS:
700  *	N/A
701  */
702 static void
703 online_dev(struct dlist *dl_head, int force_flag)
704 {
705 struct dlist	*dl, *dl1;
706 
707 	for (dl = dl_head; dl != NULL; ) {
708 		(void) g_online_drive(dl->multipath, force_flag);
709 		(void) g_free_multipath(dl->multipath);
710 		dl1 = dl;
711 		dl = dl->next;
712 		(void) g_destroy_data(dl1);
713 	}
714 }
715 
716 
717 
718 /*
719  * offlines all the disks in a
720  * SENA enclosure.
721  *
722  * RETURNS:
723  *	0	 if O.K.
724  *	non-zero otherwise
725  */
726 int
727 l_offline_photon(struct hotplug_disk_list *hotplug_sena,
728 				struct wwn_list_struct *wwn_list,
729 				int force_flag, int verbose_flag)
730 {
731 int		i, err;
732 struct dlist	*dl_head, *dl_tail, *dl, *dl_ses;
733 char		*dev_path, ses_path[MAXPATHLEN];
734 L_state		*l_state = NULL;
735 
736 	if (hotplug_sena == NULL) {
737 		return (L_INVALID_PATH_FORMAT);
738 	}
739 
740 	dl_head = dl_tail = NULL;
741 	if ((l_state = (L_state *)calloc(1, sizeof (L_state))) == NULL) {
742 		return (L_MALLOC_FAILED);
743 	}
744 
745 	/* Get global status for this Photon */
746 	dl_ses = hotplug_sena->seslist;
747 	while (dl_ses) {
748 		(void) strcpy(ses_path, dl_ses->dev_path);
749 		if (l_get_status(ses_path, l_state, verbose_flag) == 0)
750 			break;
751 		dl_ses = dl_ses->next;
752 	}
753 
754 	if (dl_ses == NULL) {
755 		(void) l_free_lstate(&l_state);
756 		return (L_ENCL_INVALID_PATH);
757 	}
758 
759 	for (i = 0; i < l_state->total_num_drv/2; i++) {
760 		if (*l_state->drv_front[i].g_disk_state.physical_path) {
761 			if ((dev_path = g_zalloc(MAXPATHLEN)) == NULL) {
762 				(void) online_dev(dl_head, force_flag);
763 				(void) l_free_lstate(&l_state);
764 				return (L_MALLOC_FAILED);
765 			}
766 			(void) strcpy(dev_path,
767 		(char *)&l_state->drv_front[i].g_disk_state.physical_path);
768 			if ((dl = g_zalloc(sizeof (struct dlist))) == NULL) {
769 				(void) g_destroy_data(dev_path);
770 				(void) online_dev(dl_head, force_flag);
771 				(void) l_free_lstate(&l_state);
772 				return (L_MALLOC_FAILED);
773 			}
774 			dl->dev_path = dev_path;
775 			if ((err = g_get_multipath(dev_path,
776 					&(dl->multipath), wwn_list,  0)) != 0) {
777 				(void) g_destroy_data(dev_path);
778 				if (dl->multipath != NULL) {
779 					(void) g_free_multipath(dl->multipath);
780 				}
781 				(void) g_destroy_data(dl);
782 				(void) online_dev(dl_head, force_flag);
783 				(void) l_free_lstate(&l_state);
784 				return (err);
785 			}
786 			if ((err = g_offline_drive(dl->multipath,
787 					force_flag)) != 0) {
788 				(void) g_destroy_data(dev_path);
789 				(void) g_free_multipath(dl->multipath);
790 				(void) g_destroy_data(dl);
791 				(void) online_dev(dl_head, force_flag);
792 				(void) l_free_lstate(&l_state);
793 				return (err);
794 			}
795 			if (dl_head == NULL) {
796 				dl_head = dl_tail = dl;
797 			} else {
798 				dl_tail->next = dl;
799 				dl->prev = dl_tail;
800 				dl_tail = dl;
801 			}
802 			(void) g_destroy_data(dev_path);
803 		}
804 		if (*l_state->drv_rear[i].g_disk_state.physical_path) {
805 			if ((dev_path = g_zalloc(MAXPATHLEN)) == NULL) {
806 				(void) online_dev(dl_head, force_flag);
807 				(void) l_free_lstate(&l_state);
808 				return (L_MALLOC_FAILED);
809 			}
810 			(void) strcpy(dev_path,
811 		(char *)&l_state->drv_rear[i].g_disk_state.physical_path);
812 			if ((dl = g_zalloc(sizeof (struct dlist))) == NULL) {
813 				(void) g_destroy_data(dev_path);
814 				(void) online_dev(dl_head, force_flag);
815 				(void) l_free_lstate(&l_state);
816 				return (L_MALLOC_FAILED);
817 			}
818 			dl->dev_path = dev_path;
819 			if ((err = g_get_multipath(dev_path,
820 					&(dl->multipath), wwn_list, 0)) != 0) {
821 				(void) g_destroy_data(dev_path);
822 				if (dl->multipath != NULL) {
823 					(void) g_free_multipath(dl->multipath);
824 				}
825 				(void) g_destroy_data(dl);
826 				(void) online_dev(dl_head, force_flag);
827 				(void) l_free_lstate(&l_state);
828 				return (err);
829 			}
830 			if ((err = g_offline_drive(dl->multipath,
831 				force_flag)) != 0) {
832 				(void) g_destroy_data(dev_path);
833 				(void) g_free_multipath(dl->multipath);
834 				(void) g_destroy_data(dl);
835 				(void) online_dev(dl_head, force_flag);
836 				(void) l_free_lstate(&l_state);
837 				return (err);
838 			}
839 			if (dl_head == NULL) {
840 				dl_head = dl_tail = dl;
841 			} else {
842 				dl_tail->next = dl;
843 				dl->prev = dl_tail;
844 				dl_tail = dl;
845 			}
846 			(void) g_destroy_data(dev_path);
847 		}
848 	}
849 	hotplug_sena->dlhead = dl_head;
850 	(void) l_free_lstate(&l_state);
851 	return (0);
852 
853 }
854 
855 
856 
857 /*
858  * prepares a char string
859  * containing the name of the
860  * device which will be hotplugged.
861  *
862  * RETURNS:
863  *	N/A
864  */
865 void
866 l_get_drive_name(char *drive_name, int slot, int f_flag, char *box_name)
867 {
868 int	    enc_type = 0;
869 L_inquiry   inq;
870 char	    *physpath;
871 Path_struct *p_pathstruct;
872 
873 	if ((drive_name == NULL) || (box_name == NULL)) {
874 		return;
875 	}
876 
877 	if (!l_convert_name(box_name, &physpath, &p_pathstruct, 0)) {
878 	    if (!g_get_inquiry(physpath, &inq)) {
879 		enc_type = l_get_enc_type(inq);
880 	    }
881 	}
882 	/* If either of the above fail, we use the default value of 0 */
883 	free(physpath);
884 	free(p_pathstruct);
885 	switch (enc_type) {
886 	case DAK_ENC_TYPE:
887 	    if (f_flag != 0) {
888 		(void) sprintf(drive_name, MSGSTR(8502,
889 			"Drive in \"%s\" slot %d"), box_name, slot);
890 	    } else {
891 		(void) sprintf(drive_name, MSGSTR(8502,
892 			"Drive in \"%s\" slot %d"), box_name,
893 			slot + (MAX_DRIVES_DAK/2));
894 	    }
895 	    break;
896 	default:
897 	    if (f_flag != 0) {
898 		(void) sprintf(drive_name, MSGSTR(8500,
899 		    "Drive in \"%s\" front slot %d"), box_name, slot);
900 	    } else {
901 		(void) sprintf(drive_name, MSGSTR(8501,
902 		    "Drive in \"%s\" rear slot %d"), box_name, slot);
903 	    }
904 	    break;
905 	}
906 }
907