1*d65b419eSXinChen /*
2*d65b419eSXinChen  * CDDL HEADER START
3*d65b419eSXinChen  *
4*d65b419eSXinChen  * The contents of this file are subject to the terms of the
5*d65b419eSXinChen  * Common Development and Distribution License (the "License").
6*d65b419eSXinChen  * You may not use this file except in compliance with the License.
7*d65b419eSXinChen  *
8*d65b419eSXinChen  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*d65b419eSXinChen  * or http://www.opensolaris.org/os/licensing.
10*d65b419eSXinChen  * See the License for the specific language governing permissions
11*d65b419eSXinChen  * and limitations under the License.
12*d65b419eSXinChen  *
13*d65b419eSXinChen  * When distributing Covered Code, include this CDDL HEADER in each
14*d65b419eSXinChen  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*d65b419eSXinChen  * If applicable, add the following below this CDDL HEADER, with the
16*d65b419eSXinChen  * fields enclosed by brackets "[]" replaced with your own identifying
17*d65b419eSXinChen  * information: Portions Copyright [yyyy] [name of copyright owner]
18*d65b419eSXinChen  *
19*d65b419eSXinChen  * CDDL HEADER END
20*d65b419eSXinChen  */
21*d65b419eSXinChen /*
22*d65b419eSXinChen  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23*d65b419eSXinChen  * Use is subject to license terms.
24*d65b419eSXinChen  */
25*d65b419eSXinChen 
26*d65b419eSXinChen /*
27*d65b419eSXinChen  * sd / ssd (SCSI Direct-attached Device) specific functions.
28*d65b419eSXinChen  */
29*d65b419eSXinChen #include <libnvpair.h>
30*d65b419eSXinChen #include <stdio.h>
31*d65b419eSXinChen #include <stdlib.h>
32*d65b419eSXinChen #include <unistd.h>
33*d65b419eSXinChen #include <sys/types.h>
34*d65b419eSXinChen #include <sys/sysmacros.h>
35*d65b419eSXinChen #include <sys/queue.h>
36*d65b419eSXinChen #include <fcntl.h>
37*d65b419eSXinChen #include <string.h>
38*d65b419eSXinChen #include <errno.h>
39*d65b419eSXinChen #include <scsi/libscsi.h>
40*d65b419eSXinChen #include <libintl.h> /* for gettext(3c) */
41*d65b419eSXinChen #include <fwflash/fwflash.h>
42*d65b419eSXinChen 
43*d65b419eSXinChen typedef struct sam4_statdesc {
44*d65b419eSXinChen 	int status;
45*d65b419eSXinChen 	char *message;
46*d65b419eSXinChen } sam4_statdesc_t;
47*d65b419eSXinChen 
48*d65b419eSXinChen static sam4_statdesc_t sam4_status[] = {
49*d65b419eSXinChen 	{ SAM4_STATUS_GOOD, "Status: GOOD (success)" },
50*d65b419eSXinChen 	{ SAM4_STATUS_CHECK_CONDITION, "Status: CHECK CONDITION" },
51*d65b419eSXinChen 	{ SAM4_STATUS_CONDITION_MET, "Status: CONDITION MET" },
52*d65b419eSXinChen 	{ SAM4_STATUS_BUSY, "Status: Device is BUSY" },
53*d65b419eSXinChen 	{ SAM4_STATUS_RESERVATION_CONFLICT, "Status: Device is RESERVED" },
54*d65b419eSXinChen 	{ SAM4_STATUS_TASK_SET_FULL,
55*d65b419eSXinChen 	    "Status: TASK SET FULL (insufficient resources in command queue" },
56*d65b419eSXinChen 	{ SAM4_STATUS_TASK_ABORTED, "Status: TASK ABORTED" },
57*d65b419eSXinChen 	{ NULL, NULL }
58*d65b419eSXinChen };
59*d65b419eSXinChen 
60*d65b419eSXinChen #define	NSAM4_STATUS	\
61*d65b419eSXinChen 	(sizeof (sam4_status) / sizeof (sam4_status[0]))
62*d65b419eSXinChen 
63*d65b419eSXinChen #define	FW_SD_FREE_DEVPATH(devpath)	{	\
64*d65b419eSXinChen 		di_devfs_path_free((devpath));	\
65*d65b419eSXinChen 	}
66*d65b419eSXinChen #define	FW_SD_FREE_DEVICELIST(thisdev, devpath) {	\
67*d65b419eSXinChen 		free((thisdev));	\
68*d65b419eSXinChen 		FW_SD_FREE_DEVPATH((devpath))	\
69*d65b419eSXinChen 	}
70*d65b419eSXinChen #define	FW_SD_FREE_ACC_NAME(thisdev, devpath) {	\
71*d65b419eSXinChen 		free((thisdev)->access_devname);	\
72*d65b419eSXinChen 		FW_SD_FREE_DEVICELIST(thisdev, devpath)	\
73*d65b419eSXinChen 	}
74*d65b419eSXinChen #define	FW_SD_FREE_DRV_NAME(thisdev, devpath) {	\
75*d65b419eSXinChen 		free((thisdev)->drvname);	\
76*d65b419eSXinChen 		FW_SD_FREE_ACC_NAME((thisdev), (devpath))	\
77*d65b419eSXinChen 	}
78*d65b419eSXinChen #define	FW_SD_FREE_CLS_NAME(thisdev, devpath) {	\
79*d65b419eSXinChen 		free((thisdev)->classname);	\
80*d65b419eSXinChen 		FW_SD_FREE_DRV_NAME((thisdev), (devpath))	\
81*d65b419eSXinChen 	}
82*d65b419eSXinChen #define	FW_SD_FREE_IDENT(thisdev, devpath) {	\
83*d65b419eSXinChen 		free((thisdev)->ident);	\
84*d65b419eSXinChen 		FW_SD_FREE_CLS_NAME((thisdev), (devpath))	\
85*d65b419eSXinChen 	}
86*d65b419eSXinChen #define	FW_SD_FREE_IDENT_VID(thisdev, devpath) {	\
87*d65b419eSXinChen 		free((thisdev)->ident->vid);	\
88*d65b419eSXinChen 		FW_SD_FREE_IDENT((thisdev), (devpath))	\
89*d65b419eSXinChen 	}
90*d65b419eSXinChen #define	FW_SD_FREE_IDENT_PID(thisdev, devpath) {	\
91*d65b419eSXinChen 		free((thisdev)->ident->pid);	\
92*d65b419eSXinChen 		FW_SD_FREE_IDENT_VID((thisdev), (devpath))	\
93*d65b419eSXinChen 	}
94*d65b419eSXinChen #define	FW_SD_FREE_IDENT_ALL(thisdev, devpath) {	\
95*d65b419eSXinChen 		free((thisdev)->ident->revid);	\
96*d65b419eSXinChen 		FW_SD_FREE_IDENT_PID((thisdev), (devpath))	\
97*d65b419eSXinChen 	}
98*d65b419eSXinChen 
99*d65b419eSXinChen int errno;
100*d65b419eSXinChen char drivername[] = "sd\0";
101*d65b419eSXinChen int plugin_version = FWPLUGIN_VERSION_2;
102*d65b419eSXinChen 
103*d65b419eSXinChen static char *devprefix = "/devices";
104*d65b419eSXinChen extern di_node_t rootnode;
105*d65b419eSXinChen extern struct fw_plugin *self;
106*d65b419eSXinChen extern struct vrfyplugin *verifier;
107*d65b419eSXinChen extern int fwflash_debug;
108*d65b419eSXinChen 
109*d65b419eSXinChen /* required functions for this plugin */
110*d65b419eSXinChen int fw_readfw(struct devicelist *device, char *filename);
111*d65b419eSXinChen int fw_writefw(struct devicelist *device);
112*d65b419eSXinChen int fw_identify(int start);
113*d65b419eSXinChen int fw_devinfo(struct devicelist *thisdev);
114*d65b419eSXinChen void fw_cleanup(struct devicelist *thisdev);
115*d65b419eSXinChen 
116*d65b419eSXinChen /* helper functions */
117*d65b419eSXinChen static char *find_link(di_node_t bnode);
118*d65b419eSXinChen static int link_cb(di_devlink_t devlink, void *arg);
119*d65b419eSXinChen static int sd_idtfy_custmz(struct devicelist *device, char *sp);
120*d65b419eSXinChen 
121*d65b419eSXinChen /*
122*d65b419eSXinChen  * We don't currently support reading firmware from a disk. If we do eventually
123*d65b419eSXinChen  * support it, we would use the scsi READ BUFFER command to do so.
124*d65b419eSXinChen  */
125*d65b419eSXinChen int
126*d65b419eSXinChen fw_readfw(struct devicelist *flashdev, char *filename)
127*d65b419eSXinChen {
128*d65b419eSXinChen 
129*d65b419eSXinChen 	logmsg(MSG_INFO,
130*d65b419eSXinChen 	    "%s: not writing firmware for device %s to file %s\n",
131*d65b419eSXinChen 	    flashdev->drvname, flashdev->access_devname, filename);
132*d65b419eSXinChen 	logmsg(MSG_ERROR,
133*d65b419eSXinChen 	    gettext("\n\nReading of firmware images from %s-attached "
134*d65b419eSXinChen 	    "devices is not supported\n\n"),
135*d65b419eSXinChen 	    flashdev->drvname);
136*d65b419eSXinChen 
137*d65b419eSXinChen 	return (FWFLASH_SUCCESS);
138*d65b419eSXinChen }
139*d65b419eSXinChen 
140*d65b419eSXinChen int
141*d65b419eSXinChen fw_writefw(struct devicelist *flashdev)
142*d65b419eSXinChen {
143*d65b419eSXinChen 	int rv;
144*d65b419eSXinChen 	int i = 0;
145*d65b419eSXinChen 	libscsi_hdl_t	*handle;
146*d65b419eSXinChen 	libscsi_target_t *target;
147*d65b419eSXinChen 	libscsi_action_t *action;
148*d65b419eSXinChen 	libscsi_errno_t serr;
149*d65b419eSXinChen 	spc3_write_buffer_cdb_t *wb_cdb;
150*d65b419eSXinChen 	sam4_status_t samstatus;
151*d65b419eSXinChen 
152*d65b419eSXinChen 	if ((verifier == NULL) || (verifier->imgsize == 0) ||
153*d65b419eSXinChen 	    (verifier->fwimage == NULL)) {
154*d65b419eSXinChen 		/* should _NOT_ happen */
155*d65b419eSXinChen 		logmsg(MSG_ERROR,
156*d65b419eSXinChen 		    gettext("%s: Firmware image has not been verified\n"),
157*d65b419eSXinChen 		    flashdev->drvname);
158*d65b419eSXinChen 		return (FWFLASH_FAILURE);
159*d65b419eSXinChen 	}
160*d65b419eSXinChen 
161*d65b419eSXinChen 	if ((handle = libscsi_init(LIBSCSI_VERSION, &serr)) == NULL) {
162*d65b419eSXinChen 		logmsg(MSG_ERROR, gettext("%s: failed to initialize libscsi\n"),
163*d65b419eSXinChen 		    flashdev->drvname);
164*d65b419eSXinChen 		return (FWFLASH_FAILURE);
165*d65b419eSXinChen 	}
166*d65b419eSXinChen 
167*d65b419eSXinChen 	if ((target = libscsi_open(handle, NULL, flashdev->access_devname))
168*d65b419eSXinChen 	    == NULL) {
169*d65b419eSXinChen 		logmsg(MSG_ERROR,
170*d65b419eSXinChen 		    gettext("%s: unable to open device %s\n"),
171*d65b419eSXinChen 		    flashdev->drvname, flashdev->access_devname);
172*d65b419eSXinChen 		libscsi_fini(handle);
173*d65b419eSXinChen 		return (FWFLASH_FAILURE);
174*d65b419eSXinChen 	}
175*d65b419eSXinChen 
176*d65b419eSXinChen 	action = libscsi_action_alloc(handle, SPC3_CMD_WRITE_BUFFER,
177*d65b419eSXinChen 	    LIBSCSI_AF_WRITE|LIBSCSI_AF_RQSENSE,
178*d65b419eSXinChen 	    (void *)verifier->fwimage, (size_t)verifier->imgsize);
179*d65b419eSXinChen 
180*d65b419eSXinChen 	wb_cdb = (spc3_write_buffer_cdb_t *)libscsi_action_get_cdb(action);
181*d65b419eSXinChen 
182*d65b419eSXinChen 	wb_cdb->wbc_mode = SPC3_WB_MODE_DL_UCODE_SAVE;
183*d65b419eSXinChen 	wb_cdb->wbc_bufferid = verifier->flashbuf;
184*d65b419eSXinChen 
185*d65b419eSXinChen 	wb_cdb->wbc_buffer_offset[0] = 0;
186*d65b419eSXinChen 	wb_cdb->wbc_buffer_offset[1] = 0;
187*d65b419eSXinChen 	wb_cdb->wbc_buffer_offset[2] = 0;
188*d65b419eSXinChen 
189*d65b419eSXinChen 	wb_cdb->wbc_parameter_list_len[0] =
190*d65b419eSXinChen 	    (verifier->imgsize & 0xff0000) >> 16;
191*d65b419eSXinChen 	wb_cdb->wbc_parameter_list_len[1] = (verifier->imgsize & 0xff00) >> 8;
192*d65b419eSXinChen 	wb_cdb->wbc_parameter_list_len[2] = (verifier->imgsize & 0xff);
193*d65b419eSXinChen 
194*d65b419eSXinChen 	rv = libscsi_exec(action, target);
195*d65b419eSXinChen 	samstatus = libscsi_action_get_status(action);
196*d65b419eSXinChen 
197*d65b419eSXinChen 	logmsg(MSG_INFO, "\nscsi_writebuffer: ret 0x%0x, samstatus 0x%0x\n",
198*d65b419eSXinChen 	    rv, samstatus);
199*d65b419eSXinChen 
200*d65b419eSXinChen 	libscsi_action_free(action);
201*d65b419eSXinChen 	libscsi_close(handle, target);
202*d65b419eSXinChen 	libscsi_fini(handle);
203*d65b419eSXinChen 
204*d65b419eSXinChen 	if (rv != FWFLASH_SUCCESS)
205*d65b419eSXinChen 		return (FWFLASH_FAILURE);
206*d65b419eSXinChen 
207*d65b419eSXinChen 	for (i = 0; i < NSAM4_STATUS; i++) {
208*d65b419eSXinChen 		if (sam4_status[i].status == samstatus) {
209*d65b419eSXinChen 			logmsg(MSG_ERROR, gettext("RETURN STATUS: %s\n"),
210*d65b419eSXinChen 			    (sam4_status[i].message));
211*d65b419eSXinChen 			break;
212*d65b419eSXinChen 		}
213*d65b419eSXinChen 	}
214*d65b419eSXinChen 	if (i == NSAM4_STATUS)
215*d65b419eSXinChen 		logmsg(MSG_ERROR, gettext("Status UNKNOWN\n"));
216*d65b419eSXinChen 
217*d65b419eSXinChen 	if (samstatus == SAM4_STATUS_GOOD) {
218*d65b419eSXinChen 		logmsg(MSG_ERROR, gettext("Note: For flash based disks "
219*d65b419eSXinChen 		    "(SSD, etc). You may need power off the system to wait a "
220*d65b419eSXinChen 		    "few minutes for supercap to fully discharge, then power "
221*d65b419eSXinChen 		    "on the system again to activate the new firmware\n"));
222*d65b419eSXinChen 		return (FWFLASH_SUCCESS);
223*d65b419eSXinChen 	}
224*d65b419eSXinChen 	return (FWFLASH_FAILURE);
225*d65b419eSXinChen }
226*d65b419eSXinChen 
227*d65b419eSXinChen /*
228*d65b419eSXinChen  * The fw_identify() function walks the device
229*d65b419eSXinChen  * tree trying to find devices which this plugin
230*d65b419eSXinChen  * can work with.
231*d65b419eSXinChen  *
232*d65b419eSXinChen  * The parameter "start" gives us the starting index number
233*d65b419eSXinChen  * to give the device when we add it to the fw_devices list.
234*d65b419eSXinChen  *
235*d65b419eSXinChen  * firstdev is allocated by us and we add space as needed
236*d65b419eSXinChen  *
237*d65b419eSXinChen  * When we store the desired information, inquiry-serial-no
238*d65b419eSXinChen  * goes in thisdev->addresses[1], and client-guid goes in
239*d65b419eSXinChen  * thisdev->addresses[2].
240*d65b419eSXinChen  */
241*d65b419eSXinChen int
242*d65b419eSXinChen fw_identify(int start)
243*d65b419eSXinChen {
244*d65b419eSXinChen 	int idx = start;
245*d65b419eSXinChen 	int fw_sata_disk = 0;
246*d65b419eSXinChen 	int *exists;
247*d65b419eSXinChen 	di_node_t thisnode;
248*d65b419eSXinChen 	struct devicelist *newdev = NULL;
249*d65b419eSXinChen 	char *devpath = NULL;
250*d65b419eSXinChen 	char *driver = NULL;
251*d65b419eSXinChen 	char *sp_temp;
252*d65b419eSXinChen 	char *sp_temp_cut;
253*d65b419eSXinChen 
254*d65b419eSXinChen 	/* We need to inquiry information manually by sending probe command */
255*d65b419eSXinChen 	libscsi_hdl_t *handle;
256*d65b419eSXinChen 	libscsi_target_t *target;
257*d65b419eSXinChen 	libscsi_errno_t serr;
258*d65b419eSXinChen 
259*d65b419eSXinChen 	/* Just in case we've got an FC-attached device on sparc */
260*d65b419eSXinChen 	if (strcmp(self->drvname, "ssd") == 0) {
261*d65b419eSXinChen 		driver = self->drvname;
262*d65b419eSXinChen 	} else
263*d65b419eSXinChen 		driver = drivername;
264*d65b419eSXinChen 
265*d65b419eSXinChen 	thisnode = di_drv_first_node(driver, rootnode);
266*d65b419eSXinChen 
267*d65b419eSXinChen 	if (thisnode == DI_NODE_NIL) {
268*d65b419eSXinChen 		logmsg(MSG_INFO, "No %s nodes in this system\n", driver);
269*d65b419eSXinChen 		return (FWFLASH_FAILURE);
270*d65b419eSXinChen 	}
271*d65b419eSXinChen 
272*d65b419eSXinChen 	if ((devpath = calloc(1, MAXPATHLEN + 1)) == NULL) {
273*d65b419eSXinChen 		logmsg(MSG_ERROR,
274*d65b419eSXinChen 		    gettext("%s: Unable to allocate space for a device node\n"),
275*d65b419eSXinChen 		    driver);
276*d65b419eSXinChen 		return (FWFLASH_FAILURE);
277*d65b419eSXinChen 	}
278*d65b419eSXinChen 
279*d65b419eSXinChen 	if ((handle = libscsi_init(LIBSCSI_VERSION, &serr)) == NULL) {
280*d65b419eSXinChen 		logmsg(MSG_ERROR, gettext("%s: failed to initialize "
281*d65b419eSXinChen 		    "libscsi\n"), newdev->drvname);
282*d65b419eSXinChen 		FW_SD_FREE_DEVPATH(devpath)
283*d65b419eSXinChen 		return (FWFLASH_FAILURE);
284*d65b419eSXinChen 	}
285*d65b419eSXinChen 
286*d65b419eSXinChen 	/* we've found one, at least */
287*d65b419eSXinChen 	for (; thisnode != DI_NODE_NIL; thisnode = di_drv_next_node(thisnode)) {
288*d65b419eSXinChen 		devpath = di_devfs_path(thisnode);
289*d65b419eSXinChen 		/*
290*d65b419eSXinChen 		 * We check if this is removable device, in which case
291*d65b419eSXinChen 		 * we really aren't interested, so exit stage left
292*d65b419eSXinChen 		 */
293*d65b419eSXinChen 		if (di_prop_lookup_ints(DDI_DEV_T_ANY, thisnode,
294*d65b419eSXinChen 		    "removable-media", &exists) > -1) {
295*d65b419eSXinChen 			logmsg(MSG_INFO,
296*d65b419eSXinChen 			    "%s: not interested in removable media device\n"
297*d65b419eSXinChen 			    "%s\n", driver, devpath);
298*d65b419eSXinChen 			continue;
299*d65b419eSXinChen 		}
300*d65b419eSXinChen 
301*d65b419eSXinChen 		if ((newdev = calloc(1, sizeof (struct devicelist)))
302*d65b419eSXinChen 		    == NULL) {
303*d65b419eSXinChen 			logmsg(MSG_ERROR,
304*d65b419eSXinChen 			    gettext("%s: identification function unable "
305*d65b419eSXinChen 			    "to allocate space for device entry\n"),
306*d65b419eSXinChen 			    driver);
307*d65b419eSXinChen 			libscsi_fini(handle);
308*d65b419eSXinChen 			FW_SD_FREE_DEVPATH(devpath)
309*d65b419eSXinChen 			return (FWFLASH_FAILURE);
310*d65b419eSXinChen 		}
311*d65b419eSXinChen 
312*d65b419eSXinChen 		if ((newdev->access_devname = calloc(1, MAXPATHLEN)) == NULL) {
313*d65b419eSXinChen 			logmsg(MSG_ERROR,
314*d65b419eSXinChen 			    gettext("%s: Unable to allocate space for a devfs "
315*d65b419eSXinChen 			    "name\n"), driver);
316*d65b419eSXinChen 			libscsi_fini(handle);
317*d65b419eSXinChen 			FW_SD_FREE_DEVICELIST(newdev, devpath)
318*d65b419eSXinChen 			return (FWFLASH_FAILURE);
319*d65b419eSXinChen 		}
320*d65b419eSXinChen 
321*d65b419eSXinChen 		/* save the /devices name */
322*d65b419eSXinChen 		(void) snprintf(newdev->access_devname, MAXPATHLEN,
323*d65b419eSXinChen 		    "%s%s:c,raw", devprefix, devpath);
324*d65b419eSXinChen 
325*d65b419eSXinChen 		/* and the /dev/rdsk/ name */
326*d65b419eSXinChen 		newdev->addresses[0] = calloc(1, MAXPATHLEN);
327*d65b419eSXinChen 		if (newdev->addresses[0])
328*d65b419eSXinChen 			newdev->addresses[0] = find_link(thisnode);
329*d65b419eSXinChen 		if (newdev->addresses[0] == NULL) {
330*d65b419eSXinChen 			libscsi_fini(handle);
331*d65b419eSXinChen 			FW_SD_FREE_DEVICELIST(newdev, devpath)
332*d65b419eSXinChen 			return (FWFLASH_FAILURE);
333*d65b419eSXinChen 		}
334*d65b419eSXinChen 
335*d65b419eSXinChen 		if ((newdev->drvname = calloc(1, strlen(driver) + 1))
336*d65b419eSXinChen 		    == NULL) {
337*d65b419eSXinChen 			logmsg(MSG_ERROR,
338*d65b419eSXinChen 			    gettext("%s: Unable to allocate space to store a "
339*d65b419eSXinChen 			    "driver name\n"), driver);
340*d65b419eSXinChen 			libscsi_fini(handle);
341*d65b419eSXinChen 			FW_SD_FREE_ACC_NAME(newdev, devpath)
342*d65b419eSXinChen 			return (FWFLASH_FAILURE);
343*d65b419eSXinChen 		}
344*d65b419eSXinChen 		(void) strlcpy(newdev->drvname, driver, strlen(driver) + 1);
345*d65b419eSXinChen 
346*d65b419eSXinChen 		if ((newdev->classname = calloc(1, strlen(driver) + 1))
347*d65b419eSXinChen 		    == NULL) {
348*d65b419eSXinChen 			logmsg(MSG_ERROR,
349*d65b419eSXinChen 			    gettext("%s: Unable to allocate space for a class "
350*d65b419eSXinChen 			    "name\n"), drivername);
351*d65b419eSXinChen 			libscsi_fini(handle);
352*d65b419eSXinChen 			FW_SD_FREE_DRV_NAME(newdev, devpath)
353*d65b419eSXinChen 			return (FWFLASH_FAILURE);
354*d65b419eSXinChen 		}
355*d65b419eSXinChen 		(void) strlcpy(newdev->classname, driver, strlen(driver) + 1);
356*d65b419eSXinChen 
357*d65b419eSXinChen 		/*
358*d65b419eSXinChen 		 * Only alloc as much as we truly need, and DON'T forget
359*d65b419eSXinChen 		 * that libdevinfo manages the memory!
360*d65b419eSXinChen 		 */
361*d65b419eSXinChen 		if ((newdev->ident = calloc(1, sizeof (struct vpr))) == NULL) {
362*d65b419eSXinChen 			logmsg(MSG_ERROR,
363*d65b419eSXinChen 			    gettext("%s: Unable to allocate space for SCSI "
364*d65b419eSXinChen 			    "INQUIRY data\n"), driver);
365*d65b419eSXinChen 			libscsi_fini(handle);
366*d65b419eSXinChen 			FW_SD_FREE_CLS_NAME(newdev, devpath)
367*d65b419eSXinChen 			return (FWFLASH_FAILURE);
368*d65b419eSXinChen 		}
369*d65b419eSXinChen 
370*d65b419eSXinChen 		/* We don't use new->ident->encap_ident currently */
371*d65b419eSXinChen 
372*d65b419eSXinChen 		/* Retrive information by using libscsi */
373*d65b419eSXinChen 		if ((target = libscsi_open(handle, NULL,
374*d65b419eSXinChen 		    newdev->access_devname)) == NULL) {
375*d65b419eSXinChen 			logmsg(MSG_INFO, "%s: unable to open device %s\n",
376*d65b419eSXinChen 			    newdev->drvname, newdev->access_devname);
377*d65b419eSXinChen 			FW_SD_FREE_IDENT(newdev, devpath)
378*d65b419eSXinChen 			continue;
379*d65b419eSXinChen 		}
380*d65b419eSXinChen 
381*d65b419eSXinChen 		/* Vendor ID */
382*d65b419eSXinChen 		sp_temp = (char *)libscsi_vendor(target);
383*d65b419eSXinChen 		if (strncmp(sp_temp, "ATA", 3) == 0) {
384*d65b419eSXinChen 			/* We need to do customize the output for SATA disks */
385*d65b419eSXinChen 			fw_sata_disk = 1;
386*d65b419eSXinChen 		} else {
387*d65b419eSXinChen 			fw_sata_disk = 0;
388*d65b419eSXinChen 			if ((newdev->ident->vid =
389*d65b419eSXinChen 			    calloc(1, strlen(sp_temp) + 1)) == NULL ||
390*d65b419eSXinChen 			    sp_temp == NULL) {
391*d65b419eSXinChen 				if (!sp_temp) {
392*d65b419eSXinChen 					logmsg(MSG_ERROR, gettext("%s: unable "
393*d65b419eSXinChen 					    "to get vendor id of %s\n"),
394*d65b419eSXinChen 					    newdev->drvname,
395*d65b419eSXinChen 					    newdev->access_devname);
396*d65b419eSXinChen 				} else {
397*d65b419eSXinChen 					logmsg(MSG_ERROR, gettext("Memory "
398*d65b419eSXinChen 					    "allocation failure\n"));
399*d65b419eSXinChen 				}
400*d65b419eSXinChen 
401*d65b419eSXinChen 				libscsi_close(handle, target);
402*d65b419eSXinChen 				libscsi_fini(handle);
403*d65b419eSXinChen 				FW_SD_FREE_IDENT(newdev, devpath)
404*d65b419eSXinChen 				return (FWFLASH_FAILURE);
405*d65b419eSXinChen 			}
406*d65b419eSXinChen 			strlcpy(newdev->ident->vid, sp_temp,
407*d65b419eSXinChen 			    strlen(sp_temp) + 1);
408*d65b419eSXinChen 		}
409*d65b419eSXinChen 
410*d65b419eSXinChen 		/* Product ID */
411*d65b419eSXinChen 		sp_temp = (char *)libscsi_product(target);
412*d65b419eSXinChen 		if (fw_sata_disk) {
413*d65b419eSXinChen 			sp_temp_cut = strchr(sp_temp, ' ');
414*d65b419eSXinChen 			if (!sp_temp_cut) {
415*d65b419eSXinChen 				/* Customize strings for special SATA disks */
416*d65b419eSXinChen 				if (sd_idtfy_custmz(newdev, sp_temp)
417*d65b419eSXinChen 				    != FWFLASH_SUCCESS) {
418*d65b419eSXinChen 					libscsi_close(handle, target);
419*d65b419eSXinChen 					libscsi_fini(handle);
420*d65b419eSXinChen 					FW_SD_FREE_IDENT(newdev, devpath)
421*d65b419eSXinChen 					return (FWFLASH_FAILURE);
422*d65b419eSXinChen 				}
423*d65b419eSXinChen 			} else {
424*d65b419eSXinChen 				/* The first string is vendor id */
425*d65b419eSXinChen 				if ((newdev->ident->vid = calloc(1,
426*d65b419eSXinChen 				    (sp_temp_cut - sp_temp + 1))) == NULL) {
427*d65b419eSXinChen 					logmsg(MSG_ERROR, gettext("%s: unable "
428*d65b419eSXinChen 					    "to get sata vendor id of %s\n"),
429*d65b419eSXinChen 					    newdev->drvname,
430*d65b419eSXinChen 					    newdev->access_devname);
431*d65b419eSXinChen 
432*d65b419eSXinChen 					libscsi_close(handle, target);
433*d65b419eSXinChen 					libscsi_fini(handle);
434*d65b419eSXinChen 					FW_SD_FREE_IDENT(newdev, devpath)
435*d65b419eSXinChen 					return (FWFLASH_FAILURE);
436*d65b419eSXinChen 				}
437*d65b419eSXinChen 				strlcpy(newdev->ident->vid, sp_temp,
438*d65b419eSXinChen 				    sp_temp_cut - sp_temp + 1);
439*d65b419eSXinChen 
440*d65b419eSXinChen 				/* The second string is product id */
441*d65b419eSXinChen 				if ((newdev->ident->pid =
442*d65b419eSXinChen 				    calloc(1, strlen(sp_temp) -
443*d65b419eSXinChen 				    strlen(newdev->ident->vid))) == NULL) {
444*d65b419eSXinChen 					logmsg(MSG_ERROR, gettext("%s: unable "
445*d65b419eSXinChen 					    "to get sata product id of %s\n"),
446*d65b419eSXinChen 					    newdev->drvname,
447*d65b419eSXinChen 					    newdev->access_devname);
448*d65b419eSXinChen 
449*d65b419eSXinChen 					libscsi_close(handle, target);
450*d65b419eSXinChen 					libscsi_fini(handle);
451*d65b419eSXinChen 					FW_SD_FREE_IDENT_VID(newdev, devpath)
452*d65b419eSXinChen 					return (FWFLASH_FAILURE);
453*d65b419eSXinChen 				}
454*d65b419eSXinChen 				strlcpy(newdev->ident->pid, sp_temp_cut + 1,
455*d65b419eSXinChen 				    strlen(sp_temp) -
456*d65b419eSXinChen 				    strlen(newdev->ident->vid));
457*d65b419eSXinChen 			}
458*d65b419eSXinChen 		} else {
459*d65b419eSXinChen 			if ((newdev->ident->pid =
460*d65b419eSXinChen 			    calloc(1, strlen(sp_temp) + 1)) == NULL ||
461*d65b419eSXinChen 			    sp_temp == NULL) {
462*d65b419eSXinChen 				logmsg(MSG_ERROR, gettext("%s: unable to get "
463*d65b419eSXinChen 				    "product id of %s\n"), newdev->drvname,
464*d65b419eSXinChen 				    newdev->access_devname);
465*d65b419eSXinChen 				FW_SD_FREE_IDENT_VID(newdev, devpath)
466*d65b419eSXinChen 				libscsi_close(handle, target);
467*d65b419eSXinChen 				libscsi_fini(handle);
468*d65b419eSXinChen 				return (FWFLASH_FAILURE);
469*d65b419eSXinChen 			}
470*d65b419eSXinChen 			strlcpy(newdev->ident->pid, sp_temp,
471*d65b419eSXinChen 			    strlen(sp_temp) + 1);
472*d65b419eSXinChen 		}
473*d65b419eSXinChen 
474*d65b419eSXinChen 		/* Revision ID */
475*d65b419eSXinChen 		sp_temp = (char *)libscsi_revision(target);
476*d65b419eSXinChen 		if ((newdev->ident->revid = calloc(1, strlen(sp_temp) + 1))
477*d65b419eSXinChen 		    == NULL || sp_temp == NULL) {
478*d65b419eSXinChen 			logmsg(MSG_ERROR, gettext("%s: unable to get revision "
479*d65b419eSXinChen 			    "id of %s\n"), newdev->drvname,
480*d65b419eSXinChen 			    newdev->access_devname);
481*d65b419eSXinChen 			libscsi_close(handle, target);
482*d65b419eSXinChen 			libscsi_fini(handle);
483*d65b419eSXinChen 			FW_SD_FREE_IDENT_PID(newdev, devpath)
484*d65b419eSXinChen 			return (FWFLASH_FAILURE);
485*d65b419eSXinChen 		}
486*d65b419eSXinChen 		strlcpy(newdev->ident->revid, sp_temp, strlen(sp_temp) + 1);
487*d65b419eSXinChen 
488*d65b419eSXinChen 		/* Finish using libscsi */
489*d65b419eSXinChen 		libscsi_close(handle, target);
490*d65b419eSXinChen 
491*d65b419eSXinChen 		if (di_prop_lookup_strings(DDI_DEV_T_ANY, thisnode,
492*d65b419eSXinChen 		    "inquiry-serial-no", &newdev->addresses[1]) < 0) {
493*d65b419eSXinChen 			logmsg(MSG_INFO,
494*d65b419eSXinChen 			    "%s: no inquiry-serial-no property for %s\n",
495*d65b419eSXinChen 			    driver, newdev->access_devname);
496*d65b419eSXinChen 			logmsg(MSG_INFO, "The errno is %d\n", errno);
497*d65b419eSXinChen 		}
498*d65b419eSXinChen 
499*d65b419eSXinChen 		if ((di_prop_lookup_strings(DDI_DEV_T_ANY, thisnode,
500*d65b419eSXinChen 		    "client-guid", &newdev->addresses[2])) < 0) {
501*d65b419eSXinChen 			logmsg(MSG_INFO,
502*d65b419eSXinChen 			    "%s: no client-guid property "
503*d65b419eSXinChen 			    "for device %s\n",
504*d65b419eSXinChen 			    driver, newdev->access_devname);
505*d65b419eSXinChen 			/* try fallback */
506*d65b419eSXinChen 			if ((di_prop_lookup_strings(DDI_DEV_T_ANY, thisnode,
507*d65b419eSXinChen 			    "guid", &newdev->addresses[2])) < 0) {
508*d65b419eSXinChen 				logmsg(MSG_INFO,
509*d65b419eSXinChen 				    "%s: no guid property for device %s\n",
510*d65b419eSXinChen 				    driver, newdev->access_devname);
511*d65b419eSXinChen 			}
512*d65b419eSXinChen 		} else {
513*d65b419eSXinChen 			logmsg(MSG_INFO,
514*d65b419eSXinChen 			    "client-guid property: %s\n",
515*d65b419eSXinChen 			    newdev->addresses[2]);
516*d65b419eSXinChen 		}
517*d65b419eSXinChen 
518*d65b419eSXinChen 		newdev->index = idx;
519*d65b419eSXinChen 		++idx;
520*d65b419eSXinChen 		newdev->plugin = self;
521*d65b419eSXinChen 
522*d65b419eSXinChen 		TAILQ_INSERT_TAIL(fw_devices, newdev, nextdev);
523*d65b419eSXinChen 	}
524*d65b419eSXinChen 	libscsi_fini(handle);
525*d65b419eSXinChen 	FW_SD_FREE_DEVPATH(devpath)
526*d65b419eSXinChen 
527*d65b419eSXinChen 	/* Check if sd targets presented are all unflashable. */
528*d65b419eSXinChen 	if (idx == start)
529*d65b419eSXinChen 		return (FWFLASH_FAILURE);
530*d65b419eSXinChen 
531*d65b419eSXinChen 	if (fwflash_debug != 0) {
532*d65b419eSXinChen 		struct devicelist *tempdev;
533*d65b419eSXinChen 
534*d65b419eSXinChen 		TAILQ_FOREACH(tempdev, fw_devices, nextdev) {
535*d65b419eSXinChen 			logmsg(MSG_INFO, "%s:fw_identify:\n",
536*d65b419eSXinChen 			    driver);
537*d65b419eSXinChen 			logmsg(MSG_INFO,
538*d65b419eSXinChen 			    "\ttempdev @ 0x%lx\n"
539*d65b419eSXinChen 			    "\t\taccess_devname: %s\n"
540*d65b419eSXinChen 			    "\t\tdrvname: %s\tclassname: %s\n"
541*d65b419eSXinChen 			    "\t\tident->vid:   %s\n"
542*d65b419eSXinChen 			    "\t\tident->pid:   %s\n"
543*d65b419eSXinChen 			    "\t\tident->revid: %s\n"
544*d65b419eSXinChen 			    "\t\tindex:	%d\n"
545*d65b419eSXinChen 			    "\t\taddress[0]:   %s\n"
546*d65b419eSXinChen 			    "\t\taddress[1]:   %s\n"
547*d65b419eSXinChen 			    "\t\taddress[2]:   %s\n"
548*d65b419eSXinChen 			    "\t\tplugin @ 0x%lx\n\n",
549*d65b419eSXinChen 			    &tempdev,
550*d65b419eSXinChen 			    tempdev->access_devname,
551*d65b419eSXinChen 			    tempdev->drvname, newdev->classname,
552*d65b419eSXinChen 			    tempdev->ident->vid,
553*d65b419eSXinChen 			    tempdev->ident->pid,
554*d65b419eSXinChen 			    tempdev->ident->revid,
555*d65b419eSXinChen 			    tempdev->index,
556*d65b419eSXinChen 			    tempdev->addresses[0],
557*d65b419eSXinChen 			    (tempdev->addresses[1] ? tempdev->addresses[1] :
558*d65b419eSXinChen 			    "(not supported)"),
559*d65b419eSXinChen 			    (tempdev->addresses[2] ? tempdev->addresses[2] :
560*d65b419eSXinChen 			    "(not supported)"),
561*d65b419eSXinChen 			    &tempdev->plugin);
562*d65b419eSXinChen 		}
563*d65b419eSXinChen 	}
564*d65b419eSXinChen 	return (FWFLASH_SUCCESS);
565*d65b419eSXinChen }
566*d65b419eSXinChen 
567*d65b419eSXinChen int
568*d65b419eSXinChen fw_devinfo(struct devicelist *thisdev)
569*d65b419eSXinChen {
570*d65b419eSXinChen 	fprintf(stdout, gettext("Device[%d]\t\t\t%s\n"
571*d65b419eSXinChen 	    "  Class [%s]\t\t\t%s\n"),
572*d65b419eSXinChen 	    thisdev->index, thisdev->access_devname,
573*d65b419eSXinChen 	    thisdev->classname, thisdev->addresses[0]);
574*d65b419eSXinChen 
575*d65b419eSXinChen 	fprintf(stdout,
576*d65b419eSXinChen 	    gettext(
577*d65b419eSXinChen 	    "\tVendor\t\t\t: %s\n"
578*d65b419eSXinChen 	    "\tProduct\t\t\t: %s\n"
579*d65b419eSXinChen 	    "\tFirmware revision\t: %-s\n"
580*d65b419eSXinChen 	    "\tInquiry Serial Number   : %-s\n"
581*d65b419eSXinChen 	    "\tGUID\t\t\t: %s\n"),
582*d65b419eSXinChen 	    thisdev->ident->vid,
583*d65b419eSXinChen 	    thisdev->ident->pid,
584*d65b419eSXinChen 	    thisdev->ident->revid,
585*d65b419eSXinChen 	    (thisdev->addresses[1] ? thisdev->addresses[1] :
586*d65b419eSXinChen 	    "(not supported)"),
587*d65b419eSXinChen 	    (thisdev->addresses[2] ? thisdev->addresses[2] :
588*d65b419eSXinChen 	    "(not supported)"));
589*d65b419eSXinChen 
590*d65b419eSXinChen 	fprintf(stdout, "\n\n");
591*d65b419eSXinChen 
592*d65b419eSXinChen 	return (FWFLASH_SUCCESS);
593*d65b419eSXinChen }
594*d65b419eSXinChen 
595*d65b419eSXinChen void
596*d65b419eSXinChen fw_cleanup(struct devicelist *thisdev)
597*d65b419eSXinChen {
598*d65b419eSXinChen 	/*
599*d65b419eSXinChen 	 * Function to clean up all the memory allocated
600*d65b419eSXinChen 	 * by this plugin, for thisdev.
601*d65b419eSXinChen 	 */
602*d65b419eSXinChen 	free(thisdev->access_devname);
603*d65b419eSXinChen 	free(thisdev->drvname);
604*d65b419eSXinChen 	free(thisdev->classname);
605*d65b419eSXinChen 
606*d65b419eSXinChen 	/*
607*d65b419eSXinChen 	 * Note that we DO NOT free addresses[1,2] because _IF_
608*d65b419eSXinChen 	 * these elements are valid, they are managed by libdevinfo
609*d65b419eSXinChen 	 * and we didn't allocate any space for them.
610*d65b419eSXinChen 	 */
611*d65b419eSXinChen 	free(thisdev->addresses[0]);
612*d65b419eSXinChen 
613*d65b419eSXinChen 	/* what this points to is freed in common code */
614*d65b419eSXinChen 	thisdev->plugin = NULL;
615*d65b419eSXinChen 
616*d65b419eSXinChen 	free(thisdev->ident->vid);
617*d65b419eSXinChen 	free(thisdev->ident->pid);
618*d65b419eSXinChen 	free(thisdev->ident->revid);
619*d65b419eSXinChen 
620*d65b419eSXinChen 	thisdev->ident = NULL;
621*d65b419eSXinChen }
622*d65b419eSXinChen 
623*d65b419eSXinChen /*
624*d65b419eSXinChen  * Helper functions
625*d65b419eSXinChen  */
626*d65b419eSXinChen static int
627*d65b419eSXinChen link_cb(di_devlink_t devlink, void *arg)
628*d65b419eSXinChen {
629*d65b419eSXinChen 	const char *result;
630*d65b419eSXinChen 
631*d65b419eSXinChen 	result = di_devlink_path(devlink);
632*d65b419eSXinChen 	if (result == NULL) {
633*d65b419eSXinChen 		arg = (void *)"(null)";
634*d65b419eSXinChen 	} else {
635*d65b419eSXinChen 		(void) strlcpy(arg, result, strlen(result) + 1);
636*d65b419eSXinChen 	}
637*d65b419eSXinChen 
638*d65b419eSXinChen 	logmsg(MSG_INFO, "\nlink_cb::linkdata->resultstr = %s\n",
639*d65b419eSXinChen 	    ((result != NULL) ? result : "(null)"));
640*d65b419eSXinChen 
641*d65b419eSXinChen 	return (DI_WALK_CONTINUE);
642*d65b419eSXinChen }
643*d65b419eSXinChen 
644*d65b419eSXinChen static char *
645*d65b419eSXinChen find_link(di_node_t bnode)
646*d65b419eSXinChen {
647*d65b419eSXinChen 	di_minor_t devminor = DI_MINOR_NIL;
648*d65b419eSXinChen 	di_devlink_handle_t	hdl;
649*d65b419eSXinChen 	char *devfspath = NULL;
650*d65b419eSXinChen 	char *minorpath = NULL;
651*d65b419eSXinChen 	char *cbresult = NULL;
652*d65b419eSXinChen 	char linkname[] = "^rdsk/\0";
653*d65b419eSXinChen 
654*d65b419eSXinChen 	devfspath = di_devfs_path(bnode);
655*d65b419eSXinChen 	if (bnode == DI_NODE_NIL) {
656*d65b419eSXinChen 		logmsg(MSG_ERROR,
657*d65b419eSXinChen 		    gettext("find_link must be called with non-null "
658*d65b419eSXinChen 		    "di_node_t\n"));
659*d65b419eSXinChen 		FW_SD_FREE_DEVPATH(devfspath)
660*d65b419eSXinChen 		return (NULL);
661*d65b419eSXinChen 	}
662*d65b419eSXinChen 
663*d65b419eSXinChen 	logmsg(MSG_INFO, "find_link: devfspath %s\n", devfspath);
664*d65b419eSXinChen 
665*d65b419eSXinChen 	if (((cbresult = calloc(1, MAXPATHLEN)) == NULL) ||
666*d65b419eSXinChen 	    ((minorpath = calloc(1, MAXPATHLEN)) == NULL)) {
667*d65b419eSXinChen 		logmsg(MSG_ERROR, gettext("unable to allocate space for dev "
668*d65b419eSXinChen 		    "link\n"));
669*d65b419eSXinChen 		FW_SD_FREE_DEVPATH(devfspath)
670*d65b419eSXinChen 		return (NULL);
671*d65b419eSXinChen 	}
672*d65b419eSXinChen 
673*d65b419eSXinChen 	devminor = di_minor_next(bnode, devminor);
674*d65b419eSXinChen 	errno = 0;
675*d65b419eSXinChen 	hdl = di_devlink_init(di_devfs_minor_path(devminor), DI_MAKE_LINK);
676*d65b419eSXinChen 	if (hdl == NULL) {
677*d65b419eSXinChen 		if (errno == EPERM || errno == EACCES) {
678*d65b419eSXinChen 			logmsg(MSG_ERROR,
679*d65b419eSXinChen 			    gettext("%s: You must be super-user to use this "
680*d65b419eSXinChen 			    "plugin.\n"), drivername);
681*d65b419eSXinChen 		} else {
682*d65b419eSXinChen 			logmsg(MSG_ERROR,
683*d65b419eSXinChen 			    gettext("unable to take devlink snapshot: %s\n"),
684*d65b419eSXinChen 			    strerror(errno));
685*d65b419eSXinChen 		}
686*d65b419eSXinChen 		FW_SD_FREE_DEVPATH(devfspath)
687*d65b419eSXinChen 		return (NULL);
688*d65b419eSXinChen 	}
689*d65b419eSXinChen 
690*d65b419eSXinChen 	(void) snprintf(minorpath, MAXPATHLEN, "%s:c,raw", devfspath);
691*d65b419eSXinChen 
692*d65b419eSXinChen 	errno = 0;
693*d65b419eSXinChen 	if (di_devlink_walk(hdl, linkname, minorpath, DI_PRIMARY_LINK,
694*d65b419eSXinChen 	    (void *)cbresult, link_cb) < 0) {
695*d65b419eSXinChen 		logmsg(MSG_ERROR,
696*d65b419eSXinChen 		    gettext("Unable to walk devlink snapshot for %s: %s\n"),
697*d65b419eSXinChen 		    minorpath, strerror(errno));
698*d65b419eSXinChen 		FW_SD_FREE_DEVPATH(devfspath)
699*d65b419eSXinChen 		return (NULL);
700*d65b419eSXinChen 	}
701*d65b419eSXinChen 
702*d65b419eSXinChen 	if (di_devlink_fini(&hdl) < 0) {
703*d65b419eSXinChen 		logmsg(MSG_ERROR,
704*d65b419eSXinChen 		    gettext("Unable to close devlink snapshot: %s\n"),
705*d65b419eSXinChen 		    strerror(errno));
706*d65b419eSXinChen 	}
707*d65b419eSXinChen 	free(minorpath); /* don't need this now */
708*d65b419eSXinChen 	FW_SD_FREE_DEVPATH(devfspath)
709*d65b419eSXinChen 
710*d65b419eSXinChen 	logmsg(MSG_INFO, "cbresult: %s\n", cbresult);
711*d65b419eSXinChen 	return (cbresult);
712*d65b419eSXinChen }
713*d65b419eSXinChen 
714*d65b419eSXinChen static int
715*d65b419eSXinChen sd_idtfy_custmz(struct devicelist *device, char *sp)
716*d65b419eSXinChen {
717*d65b419eSXinChen 	/* vid customization */
718*d65b419eSXinChen 	if (strncmp(sp, "ST", 2) == 0) {
719*d65b419eSXinChen 		/* Customize retail Seagate disks */
720*d65b419eSXinChen 		if ((device->ident->vid = strdup("SEAGATE")) == NULL) {
721*d65b419eSXinChen 			return (FWFLASH_FAILURE);
722*d65b419eSXinChen 		}
723*d65b419eSXinChen 	} else if (strncmp(sp, "SSD", 3) == 0) {
724*d65b419eSXinChen 		/* Customize retail INTEL disks */
725*d65b419eSXinChen 		if ((device->ident->vid = strdup("INTEL")) == NULL) {
726*d65b419eSXinChen 			return (FWFLASH_FAILURE);
727*d65b419eSXinChen 		}
728*d65b419eSXinChen 	} else {
729*d65b419eSXinChen 		/* disks to do in the furture, fill 'ATA' first */
730*d65b419eSXinChen 		if ((device->ident->vid = strdup("ATA")) == NULL) {
731*d65b419eSXinChen 			return (FWFLASH_FAILURE);
732*d65b419eSXinChen 		}
733*d65b419eSXinChen 	}
734*d65b419eSXinChen 
735*d65b419eSXinChen 	/* pid customization */
736*d65b419eSXinChen 	if ((device->ident->pid = calloc(1, strlen(sp) + 1)) == NULL) {
737*d65b419eSXinChen 		logmsg(MSG_ERROR, gettext("Unable to allocate space for "
738*d65b419eSXinChen 		    "product id\n"));
739*d65b419eSXinChen 		free(device->ident->vid);
740*d65b419eSXinChen 		return (FWFLASH_FAILURE);
741*d65b419eSXinChen 	}
742*d65b419eSXinChen 	strlcpy(device->ident->pid, sp, strlen(sp) + 1);
743*d65b419eSXinChen 
744*d65b419eSXinChen 	return (FWFLASH_SUCCESS);
745*d65b419eSXinChen }
746