xref: /titanic_44/usr/src/lib/storage/liba5k/common/lhot.c (revision 2ec7cc7fc084163eaed884efee9bbd322cc8951b)
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 = NULL;
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 = NULL;
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,
401 				int f_flag, int slot, int verbose_flag)
402 {
403 uchar_t	*page_buf;
404 int 	fd, front_index, rear_index, offset, err;
405 unsigned short	page_len;
406 struct	device_element *elem;
407 
408 	if ((ses_path == NULL) || (l_state == NULL)) {
409 		return (L_INVALID_PATH_FORMAT);
410 	}
411 
412 	if ((page_buf = (uchar_t *)g_zalloc(MAX_REC_DIAG_LENGTH)) == NULL) {
413 		return (L_MALLOC_FAILED);
414 	}
415 
416 	if ((fd = g_object_open(ses_path, O_NDELAY | O_RDWR)) == -1) {
417 		(void) g_destroy_data(page_buf);
418 		return (L_OPEN_PATH_FAIL);
419 	}
420 
421 	if ((err = l_get_envsen_page(fd, page_buf, MAX_REC_DIAG_LENGTH,
422 					L_PAGE_2, verbose_flag)) != 0) {
423 		(void) g_destroy_data(page_buf);
424 		(void) close(fd);
425 		return (err);
426 	}
427 
428 	page_len = (page_buf[2] << 8 | page_buf[3]) + HEADER_LEN;
429 
430 	if ((err = l_get_disk_element_index(l_state, &front_index,
431 							&rear_index)) != 0) {
432 		(void) g_destroy_data(page_buf);
433 		(void) close(fd);
434 		return (err);
435 	}
436 	/* Skip global element */
437 	front_index++;
438 	if ((strncmp((char *)l_state->ib_tbl.config.prod_id, DAK_OFF_NAME,
439 						strlen(DAK_OFF_NAME)) == 0) ||
440 		(strncmp((char *)l_state->ib_tbl.config.prod_id, DAK_PROD_STR,
441 						strlen(DAK_OFF_NAME)) == 0)) {
442 		rear_index += l_state->total_num_drv/2 + 1;
443 	} else
444 		rear_index++;
445 
446 	if (f_flag) {
447 		offset = (8 + (front_index + slot)*4);
448 	} else {
449 		offset = (8 + (rear_index  + slot)*4);
450 	}
451 
452 	elem = (struct device_element *)(page_buf + offset);
453 
454 	switch (func) {
455 		case OVERALL_STATUS:
456 		    if (code == NULL) {
457 			return (L_INVALID_ARG);
458 		    }
459 		    switch (todo) {
460 			case INSERT_DEVICE:
461 				*code = (elem->code != S_OK) ? elem->code : 0;
462 				(void) g_destroy_data(page_buf);
463 				(void) close(fd);
464 				return (0);
465 			case REMOVE_DEVICE:
466 				*code = (elem->code != S_NOT_INSTALLED) ?
467 					elem->code : 0;
468 				(void) g_destroy_data(page_buf);
469 				(void) close(fd);
470 				return (0);
471 		    }
472 		    /* NOTREACHED */
473 		case SET_RQST_INSRT:
474 			bzero(elem, sizeof (struct device_element));
475 			elem->select = 1;
476 			elem->rdy_to_ins = 1;
477 			break;
478 		case SET_RQST_RMV:
479 			bzero(elem, sizeof (struct device_element));
480 			elem->select = 1;
481 			elem->rmv = 1;
482 			elem->dev_off = 1;
483 			elem->en_bypass_a = 1;
484 			elem->en_bypass_b = 1;
485 			break;
486 		case SET_FAULT:
487 			bzero(elem, sizeof (struct device_element));
488 			elem->select = 1;
489 			elem->fault_req = 1;
490 			elem->dev_off = 1;
491 			elem->en_bypass_a = 1;
492 			elem->en_bypass_b = 1;
493 			break;
494 		case SET_DRV_ON:
495 			bzero(elem, sizeof (struct device_element));
496 			elem->select = 1;
497 			break;
498 	}
499 
500 	err = g_scsi_send_diag_cmd(fd, (uchar_t *)page_buf, page_len);
501 	(void) g_destroy_data(page_buf);
502 	(void) close(fd);
503 	return (err);
504 }
505 
506 
507 
508 /*
509  * Finds whether device id (tid) exists in the
510  * Arbitrated loop map or not.
511  *
512  * INPUT:
513  * ses_path	- pointer to a ses path
514  * tid		- the target id of the device we want to check on
515  *		- only the low order 8 bits has the tid
516  * map		- pointer to a map of the system
517  * verbose_flag - self explanatory
518  *
519  * OUTPUT:
520  * dev_path	- the device path of the device with "tid".
521  *                Caller is responsible for freeing it
522  *
523  * RETURNS:
524  *	1	 if device present
525  *	0	 otherwise
526  */
527 int
528 l_device_present(char *ses_path, int tid, gfc_map_t *map,
529 				int verbose_flag, char **dev_path)
530 {
531 char			sf_path[MAXPATHLEN];
532 uchar_t			wwn[40], c;
533 int			len, i, j, k, fnib, snib, this_pid;
534 int			fd, ses_pid, al_pa, err;
535 char			ssd[30];
536 gfc_port_dev_info_t	*dev_addr_ptr;
537 WWN_list		*wwnlp, *wwn_list;
538 
539 
540 	if (dev_path == NULL)
541 		return (0);
542 
543 	if ((ses_path == NULL) || (map == NULL)) {
544 		return (L_NO_SES_PATH);
545 	}
546 
547 	*dev_path = NULL;
548 
549 	switch (map->hba_addr.port_topology) {
550 	case FC_TOP_PRIVATE_LOOP:
551 		for (i = 0, dev_addr_ptr = map->dev_addr; i < map->count;
552 						i++, dev_addr_ptr++) {
553 			if (dev_addr_ptr->gfc_port_dev.
554 				priv_port.sf_inq_dtype != DTYPE_ESI) {
555 				al_pa = dev_addr_ptr->gfc_port_dev.
556 						priv_port.sf_al_pa;
557 				if (tid == g_sf_alpa_to_switch[al_pa]) {
558 					break;
559 				}
560 			}
561 		}
562 		if (i >= map->count)
563 			return (0);
564 		/*
565 		 * Make sure that the port WWN is valid
566 		 */
567 		if (is_null_wwn(dev_addr_ptr->gfc_port_dev.
568 						priv_port.sf_port_wwn)) {
569 			return (0);
570 		}
571 		for (j = 0, k = 0; j < WWN_SIZE; j++) {
572 			c = dev_addr_ptr->gfc_port_dev.priv_port.sf_port_wwn[j];
573 			fnib = (((int)(c & 0xf0)) >> 4);
574 			snib = (c & 0x0f);
575 			if (fnib >= 0 && fnib <= 9)
576 				wwn[k++] = '0' + fnib;
577 			else if (fnib >= 10 && fnib <= 15)
578 				wwn[k++] = 'a' + fnib - 10;
579 			if (snib >= 0 && snib <= 9)
580 				wwn[k++] = '0' + snib;
581 			else if (snib >= 10 && snib <= 15)
582 				wwn[k++] = 'a' + snib - 10;
583 		}
584 		wwn[k] = '\0';
585 		break;
586 	case FC_TOP_PUBLIC_LOOP:
587 	case FC_TOP_FABRIC:
588 		/*
589 		 * Get the phys address (port id) of this ses device
590 		 */
591 		if (err = l_get_pid_from_path(ses_path, map, &ses_pid))
592 			return (err);
593 
594 		for (i = 0, dev_addr_ptr = map->dev_addr; i < map->count;
595 							i++, dev_addr_ptr++) {
596 			if (dev_addr_ptr->gfc_port_dev.pub_port.dev_dtype !=
597 								DTYPE_ESI) {
598 				/*
599 				 * We have a device. First match the area and
600 				 * domain ids and if they match, then see if
601 				 * the 8bit tid matches the last 8 bits of
602 				 * 'this_pid'
603 				 */
604 				this_pid = dev_addr_ptr->gfc_port_dev.
605 						pub_port.dev_did.port_id;
606 				if ((this_pid & AREA_DOMAIN_ID) ==
607 						(ses_pid & AREA_DOMAIN_ID)) {
608 					if (tid == g_sf_alpa_to_switch[
609 							this_pid & 0xFF])
610 						break;
611 				}
612 			}
613 		}
614 
615 		if (i >= map->count)
616 			return (0);
617 		/*
618 		 * Make sure that the port WWN is valid
619 		 */
620 		if (is_null_wwn(dev_addr_ptr->gfc_port_dev.
621 						pub_port.dev_pwwn.raw_wwn)) {
622 			return (0);
623 		}
624 		for (j = 0, k = 0; j < WWN_SIZE; j++) {
625 			c = dev_addr_ptr->gfc_port_dev.pub_port.
626 							dev_pwwn.raw_wwn[j];
627 			fnib = (((int)(c & 0xf0)) >> 4);
628 			snib = (c & 0x0f);
629 			if (fnib >= 0 && fnib <= 9)
630 				wwn[k++] = '0' + fnib;
631 			else if (fnib >= 10 && fnib <= 15)
632 				wwn[k++] = 'a' + fnib - 10;
633 			if (snib >= 0 && snib <= 9)
634 				wwn[k++] = '0' + snib;
635 			else if (snib >= 10 && snib <= 15)
636 				wwn[k++] = 'a' + snib - 10;
637 		}
638 		wwn[k] = '\0';
639 		break;
640 	case FC_TOP_PT_PT:
641 		return (L_PT_PT_FC_TOP_NOT_SUPPORTED);
642 	default:
643 		return (L_UNEXPECTED_FC_TOPOLOGY);
644 	}	/* End of switch on port_topology */
645 
646 	if (strstr(ses_path, SCSI_VHCI) != NULL) {
647 		if (err = g_get_wwn_list(&wwn_list, 0)) {
648 			return (err);
649 		}
650 		for (wwnlp = wwn_list; wwnlp != NULL;
651 						wwnlp = wwnlp->wwn_next) {
652 			if (memcmp(wwnlp->port_wwn_s, wwn, WWN_S_LEN) == 0) {
653 				break;
654 			}
655 		}
656 		if (wwnlp != NULL) {
657 			if ((*dev_path = g_zalloc(MAXPATHLEN)) == NULL) {
658 				g_free_wwn_list(&wwn_list);
659 				return (L_MALLOC_FAILED);
660 			}
661 			(void) strcpy(*dev_path, wwnlp->physical_path);
662 		} else {
663 			g_free_wwn_list(&wwn_list);
664 			return (0);
665 		}
666 	} else {
667 
668 		len = strlen(ses_path) - strlen(strrchr(ses_path, '/'));
669 
670 		(void) sprintf(ssd, "ssd@w%s,0", wwn);
671 
672 		(void) strncpy(sf_path, ses_path, len);
673 		sf_path[len] = '\0';
674 		P_DPRINTF("  l_device_present: wwn=%s, sf_path=%s\n",
675 			wwn, sf_path);
676 
677 		if ((*dev_path = g_zalloc(MAXPATHLEN)) == NULL) {
678 			return (L_MALLOC_FAILED);
679 		}
680 		(void) sprintf(*dev_path, "%s/%s", sf_path, ssd);
681 		P_DPRINTF("  l_device_present: dev_path=%s\n", *dev_path);
682 
683 		(void) strcat(*dev_path, ":c");
684 	}
685 	if ((fd = open(*dev_path, O_RDONLY)) == -1) {
686 		free(*dev_path);
687 		*dev_path = NULL;
688 		return (0);
689 	}
690 	(void) close(fd);
691 	return (1);
692 }
693 
694 
695 
696 /*
697  * onlines the given list of devices
698  * and free up the allocated memory.
699  *
700  * RETURNS:
701  *	N/A
702  */
703 static void
704 online_dev(struct dlist *dl_head, int force_flag)
705 {
706 struct dlist	*dl, *dl1;
707 
708 	for (dl = dl_head; dl != NULL; ) {
709 		(void) g_online_drive(dl->multipath, force_flag);
710 		(void) g_free_multipath(dl->multipath);
711 		dl1 = dl;
712 		dl = dl->next;
713 		(void) g_destroy_data(dl1);
714 	}
715 }
716 
717 
718 
719 /*
720  * offlines all the disks in a
721  * SENA enclosure.
722  *
723  * RETURNS:
724  *	0	 if O.K.
725  *	non-zero otherwise
726  */
727 int
728 l_offline_photon(struct hotplug_disk_list *hotplug_sena,
729 				struct wwn_list_struct *wwn_list,
730 				int force_flag, int verbose_flag)
731 {
732 int		i, err;
733 struct dlist	*dl_head, *dl_tail, *dl, *dl_ses;
734 char		*dev_path, ses_path[MAXPATHLEN];
735 L_state		*l_state = NULL;
736 
737 	if (hotplug_sena == NULL) {
738 		return (L_INVALID_PATH_FORMAT);
739 	}
740 
741 	dl_head = dl_tail = NULL;
742 	if ((l_state = (L_state *)calloc(1, sizeof (L_state))) == NULL) {
743 		return (L_MALLOC_FAILED);
744 	}
745 
746 	/* Get global status for this Photon */
747 	dl_ses = hotplug_sena->seslist;
748 	while (dl_ses) {
749 		(void) strcpy(ses_path, dl_ses->dev_path);
750 		if (l_get_status(ses_path, l_state, verbose_flag) == 0)
751 			break;
752 		dl_ses = dl_ses->next;
753 	}
754 
755 	if (dl_ses == NULL) {
756 		(void) l_free_lstate(&l_state);
757 		return (L_ENCL_INVALID_PATH);
758 	}
759 
760 	for (i = 0; i < l_state->total_num_drv/2; i++) {
761 		if (*l_state->drv_front[i].g_disk_state.physical_path) {
762 			if ((dev_path = g_zalloc(MAXPATHLEN)) == NULL) {
763 				(void) online_dev(dl_head, force_flag);
764 				(void) l_free_lstate(&l_state);
765 				return (L_MALLOC_FAILED);
766 			}
767 			(void) strcpy(dev_path,
768 		(char *)&l_state->drv_front[i].g_disk_state.physical_path);
769 			if ((dl = g_zalloc(sizeof (struct dlist))) == NULL) {
770 				(void) g_destroy_data(dev_path);
771 				(void) online_dev(dl_head, force_flag);
772 				(void) l_free_lstate(&l_state);
773 				return (L_MALLOC_FAILED);
774 			}
775 			dl->dev_path = dev_path;
776 			if ((err = g_get_multipath(dev_path,
777 					&(dl->multipath), wwn_list,  0)) != 0) {
778 				(void) g_destroy_data(dev_path);
779 				if (dl->multipath != NULL) {
780 					(void) g_free_multipath(dl->multipath);
781 				}
782 				(void) g_destroy_data(dl);
783 				(void) online_dev(dl_head, force_flag);
784 				(void) l_free_lstate(&l_state);
785 				return (err);
786 			}
787 			if ((err = g_offline_drive(dl->multipath,
788 					force_flag)) != 0) {
789 				(void) g_destroy_data(dev_path);
790 				(void) g_free_multipath(dl->multipath);
791 				(void) g_destroy_data(dl);
792 				(void) online_dev(dl_head, force_flag);
793 				(void) l_free_lstate(&l_state);
794 				return (err);
795 			}
796 			if (dl_head == NULL) {
797 				dl_head = dl_tail = dl;
798 			} else {
799 				dl_tail->next = dl;
800 				dl->prev = dl_tail;
801 				dl_tail = dl;
802 			}
803 			(void) g_destroy_data(dev_path);
804 		}
805 		if (*l_state->drv_rear[i].g_disk_state.physical_path) {
806 			if ((dev_path = g_zalloc(MAXPATHLEN)) == NULL) {
807 				(void) online_dev(dl_head, force_flag);
808 				(void) l_free_lstate(&l_state);
809 				return (L_MALLOC_FAILED);
810 			}
811 			(void) strcpy(dev_path,
812 		(char *)&l_state->drv_rear[i].g_disk_state.physical_path);
813 			if ((dl = g_zalloc(sizeof (struct dlist))) == NULL) {
814 				(void) g_destroy_data(dev_path);
815 				(void) online_dev(dl_head, force_flag);
816 				(void) l_free_lstate(&l_state);
817 				return (L_MALLOC_FAILED);
818 			}
819 			dl->dev_path = dev_path;
820 			if ((err = g_get_multipath(dev_path,
821 					&(dl->multipath), wwn_list, 0)) != 0) {
822 				(void) g_destroy_data(dev_path);
823 				if (dl->multipath != NULL) {
824 					(void) g_free_multipath(dl->multipath);
825 				}
826 				(void) g_destroy_data(dl);
827 				(void) online_dev(dl_head, force_flag);
828 				(void) l_free_lstate(&l_state);
829 				return (err);
830 			}
831 			if ((err = g_offline_drive(dl->multipath,
832 				force_flag)) != 0) {
833 				(void) g_destroy_data(dev_path);
834 				(void) g_free_multipath(dl->multipath);
835 				(void) g_destroy_data(dl);
836 				(void) online_dev(dl_head, force_flag);
837 				(void) l_free_lstate(&l_state);
838 				return (err);
839 			}
840 			if (dl_head == NULL) {
841 				dl_head = dl_tail = dl;
842 			} else {
843 				dl_tail->next = dl;
844 				dl->prev = dl_tail;
845 				dl_tail = dl;
846 			}
847 			(void) g_destroy_data(dev_path);
848 		}
849 	}
850 	hotplug_sena->dlhead = dl_head;
851 	(void) l_free_lstate(&l_state);
852 	return (0);
853 
854 }
855 
856 
857 
858 /*
859  * prepares a char string
860  * containing the name of the
861  * device which will be hotplugged.
862  *
863  * RETURNS:
864  *	N/A
865  */
866 void
867 l_get_drive_name(char *drive_name, int slot, int f_flag, char *box_name)
868 {
869 int	    enc_type = 0;
870 L_inquiry   inq;
871 char	    *physpath;
872 Path_struct *p_pathstruct;
873 
874 	if ((drive_name == NULL) || (box_name == NULL)) {
875 		return;
876 	}
877 
878 	if (!l_convert_name(box_name, &physpath, &p_pathstruct, 0)) {
879 	    if (!g_get_inquiry(physpath, &inq)) {
880 		enc_type = l_get_enc_type(inq);
881 	    }
882 	}
883 	/* If either of the above fail, we use the default value of 0 */
884 	free(physpath);
885 	free(p_pathstruct);
886 	switch (enc_type) {
887 	case DAK_ENC_TYPE:
888 	    if (f_flag != NULL) {
889 		(void) sprintf(drive_name, MSGSTR(8502,
890 			"Drive in \"%s\" slot %d"), box_name, slot);
891 	    } else {
892 		(void) sprintf(drive_name, MSGSTR(8502,
893 			"Drive in \"%s\" slot %d"), box_name,
894 			slot + (MAX_DRIVES_DAK/2));
895 	    }
896 	    break;
897 	default:
898 	    if (f_flag != NULL) {
899 		(void) sprintf(drive_name, MSGSTR(8500,
900 		    "Drive in \"%s\" front slot %d"), box_name, slot);
901 	    } else {
902 		(void) sprintf(drive_name, MSGSTR(8501,
903 		    "Drive in \"%s\" rear slot %d"), box_name, slot);
904 	    }
905 	    break;
906 	}
907 }
908