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.
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.
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]
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
27 * sd / ssd (SCSI Direct-attached Device) specific functions.
29 #include <libnvpair.h>
33 #include <sys/types.h>
34 #include <sys/sysmacros.h>
35 #include <sys/queue.h>
39 #include <scsi/libscsi.h>
40 #include <libintl.h> /* for gettext(3c) */
41 #include <fwflash/fwflash.h>
43 typedef struct sam4_statdesc
{
48 static sam4_statdesc_t sam4_status
[] = {
49 { SAM4_STATUS_GOOD
, "Status: GOOD (success)" },
50 { SAM4_STATUS_CHECK_CONDITION
, "Status: CHECK CONDITION" },
51 { SAM4_STATUS_CONDITION_MET
, "Status: CONDITION MET" },
52 { SAM4_STATUS_BUSY
, "Status: Device is BUSY" },
53 { SAM4_STATUS_RESERVATION_CONFLICT
, "Status: Device is RESERVED" },
54 { SAM4_STATUS_TASK_SET_FULL
,
55 "Status: TASK SET FULL (insufficient resources in command queue" },
56 { SAM4_STATUS_TASK_ABORTED
, "Status: TASK ABORTED" },
60 #define NSAM4_STATUS \
61 (sizeof (sam4_status) / sizeof (sam4_status[0]))
63 #define FW_SD_FREE_DEVPATH(devpath) { \
64 di_devfs_path_free((devpath)); \
66 #define FW_SD_FREE_DEVICELIST(thisdev, devpath) { \
68 FW_SD_FREE_DEVPATH((devpath)) \
70 #define FW_SD_FREE_DRV_NAME(thisdev, devpath) { \
71 free((thisdev)->drvname); \
72 FW_SD_FREE_DEVICELIST((thisdev), (devpath)) \
74 #define FW_SD_FREE_CLS_NAME(thisdev, devpath) { \
75 free((thisdev)->classname); \
76 FW_SD_FREE_DRV_NAME((thisdev), (devpath)) \
78 #define FW_SD_FREE_ACC_NAME(thisdev, devpath) { \
79 free((thisdev)->access_devname); \
80 FW_SD_FREE_CLS_NAME(thisdev, devpath) \
82 #define FW_SD_FREE_ADDR(thisdev, devpath) { \
83 free((thisdev)->addresses[0]); \
84 FW_SD_FREE_ACC_NAME(thisdev, devpath) \
86 #define FW_SD_FREE_IDENT(thisdev, devpath) { \
87 free((thisdev)->ident); \
88 FW_SD_FREE_ADDR((thisdev), (devpath)) \
90 #define FW_SD_FREE_IDENT_VID(thisdev, devpath) { \
91 free((thisdev)->ident->vid); \
92 FW_SD_FREE_IDENT((thisdev), (devpath)) \
94 #define FW_SD_FREE_IDENT_PID(thisdev, devpath) { \
95 free((thisdev)->ident->pid); \
96 FW_SD_FREE_IDENT_VID((thisdev), (devpath)) \
98 #define FW_SD_FREE_IDENT_ALL(thisdev, devpath) { \
99 free((thisdev)->ident->revid); \
100 FW_SD_FREE_IDENT_PID((thisdev), (devpath)) \
104 char drivername
[] = "sd\0";
105 int plugin_version
= FWPLUGIN_VERSION_2
;
107 static char *devprefix
= "/devices";
108 extern di_node_t rootnode
;
109 extern struct fw_plugin
*self
;
110 extern struct vrfyplugin
*verifier
;
111 extern int fwflash_debug
;
113 /* required functions for this plugin */
114 int fw_readfw(struct devicelist
*device
, char *filename
);
115 int fw_writefw(struct devicelist
*device
);
116 int fw_identify(int start
);
117 int fw_devinfo(struct devicelist
*thisdev
);
118 void fw_cleanup(struct devicelist
*thisdev
);
120 /* helper functions */
121 static char *find_link(di_node_t bnode
, char *acc_devname
);
122 static int link_cb(di_devlink_t devlink
, void *arg
);
123 static int sd_idtfy_custmz(struct devicelist
*device
, char *sp
);
126 * We don't currently support reading firmware from a disk. If we do eventually
127 * support it, we would use the scsi READ BUFFER command to do so.
130 fw_readfw(struct devicelist
*flashdev
, char *filename
)
134 "%s: not writing firmware for device %s to file %s\n",
135 flashdev
->drvname
, flashdev
->access_devname
, filename
);
137 gettext("\n\nReading of firmware images from %s-attached "
138 "devices is not supported\n\n"),
141 return (FWFLASH_SUCCESS
);
145 fw_writefw(struct devicelist
*flashdev
)
149 libscsi_hdl_t
*handle
;
150 libscsi_target_t
*target
;
151 libscsi_action_t
*action
;
152 libscsi_errno_t serr
;
153 spc3_write_buffer_cdb_t
*wb_cdb
;
154 sam4_status_t samstatus
;
156 if ((verifier
== NULL
) || (verifier
->imgsize
== 0) ||
157 (verifier
->fwimage
== NULL
)) {
158 /* should _NOT_ happen */
160 gettext("%s: Firmware image has not been verified\n"),
162 return (FWFLASH_FAILURE
);
165 if ((handle
= libscsi_init(LIBSCSI_VERSION
, &serr
)) == NULL
) {
166 logmsg(MSG_ERROR
, gettext("%s: failed to initialize libscsi\n"),
168 return (FWFLASH_FAILURE
);
171 if ((target
= libscsi_open(handle
, NULL
, flashdev
->access_devname
))
174 gettext("%s: unable to open device %s\n"),
175 flashdev
->drvname
, flashdev
->access_devname
);
176 libscsi_fini(handle
);
177 return (FWFLASH_FAILURE
);
180 action
= libscsi_action_alloc(handle
, SPC3_CMD_WRITE_BUFFER
,
181 LIBSCSI_AF_WRITE
|LIBSCSI_AF_RQSENSE
,
182 (void *)verifier
->fwimage
, (size_t)verifier
->imgsize
);
184 wb_cdb
= (spc3_write_buffer_cdb_t
*)libscsi_action_get_cdb(action
);
186 wb_cdb
->wbc_mode
= SPC3_WB_MODE_DL_UCODE_SAVE
;
187 wb_cdb
->wbc_bufferid
= verifier
->flashbuf
;
189 wb_cdb
->wbc_buffer_offset
[0] = 0;
190 wb_cdb
->wbc_buffer_offset
[1] = 0;
191 wb_cdb
->wbc_buffer_offset
[2] = 0;
193 wb_cdb
->wbc_parameter_list_len
[0] =
194 (verifier
->imgsize
& 0xff0000) >> 16;
195 wb_cdb
->wbc_parameter_list_len
[1] = (verifier
->imgsize
& 0xff00) >> 8;
196 wb_cdb
->wbc_parameter_list_len
[2] = (verifier
->imgsize
& 0xff);
198 rv
= libscsi_exec(action
, target
);
199 samstatus
= libscsi_action_get_status(action
);
201 logmsg(MSG_INFO
, "\nscsi_writebuffer: ret 0x%0x, samstatus 0x%0x\n",
204 libscsi_action_free(action
);
205 libscsi_close(handle
, target
);
206 libscsi_fini(handle
);
208 if (rv
!= FWFLASH_SUCCESS
)
209 return (FWFLASH_FAILURE
);
211 for (i
= 0; i
< NSAM4_STATUS
; i
++) {
212 if (sam4_status
[i
].status
== samstatus
) {
213 logmsg(MSG_ERROR
, gettext("RETURN STATUS: %s\n"),
214 (sam4_status
[i
].message
));
218 if (i
== NSAM4_STATUS
)
219 logmsg(MSG_ERROR
, gettext("Status UNKNOWN\n"));
221 if (samstatus
== SAM4_STATUS_GOOD
) {
222 logmsg(MSG_ERROR
, gettext("Note: For flash based disks "
223 "(SSD, etc). You may need power off the system to wait a "
224 "few minutes for supercap to fully discharge, then power "
225 "on the system again to activate the new firmware\n"));
226 return (FWFLASH_SUCCESS
);
228 return (FWFLASH_FAILURE
);
232 * The fw_identify() function walks the device
233 * tree trying to find devices which this plugin
236 * The parameter "start" gives us the starting index number
237 * to give the device when we add it to the fw_devices list.
239 * firstdev is allocated by us and we add space as needed
241 * When we store the desired information, inquiry-serial-no
242 * goes in thisdev->addresses[1], and client-guid goes in
243 * thisdev->addresses[2].
246 fw_identify(int start
)
249 int fw_sata_disk
= 0;
252 struct devicelist
*newdev
= NULL
;
253 char *devpath
= NULL
;
258 /* We need to inquiry information manually by sending probe command */
259 libscsi_hdl_t
*handle
;
260 libscsi_target_t
*target
;
261 libscsi_errno_t serr
;
263 /* Just in case we've got an FC-attached device on sparc */
264 if (strcmp(self
->drvname
, "ssd") == 0) {
265 driver
= self
->drvname
;
269 thisnode
= di_drv_first_node(driver
, rootnode
);
271 if (thisnode
== DI_NODE_NIL
) {
272 logmsg(MSG_INFO
, "No %s nodes in this system\n", driver
);
273 return (FWFLASH_FAILURE
);
276 if ((handle
= libscsi_init(LIBSCSI_VERSION
, &serr
)) == NULL
) {
277 logmsg(MSG_ERROR
, gettext("%s: failed to initialize "
278 "libscsi\n"), newdev
->drvname
);
279 return (FWFLASH_FAILURE
);
282 /* we've found one, at least */
283 for (; thisnode
!= DI_NODE_NIL
; thisnode
= di_drv_next_node(thisnode
)) {
284 /* Need to free by di_devfs_path_free */
285 if ((devpath
= di_devfs_path(thisnode
)) == NULL
) {
286 logmsg(MSG_INFO
, "unable to get device path for "
287 "current node with errno %d\n", errno
);
291 * We check if this is removable device, in which case
292 * we really aren't interested, so exit stage left
294 if (di_prop_lookup_ints(DDI_DEV_T_ANY
, thisnode
,
295 "removable-media", &exists
) > -1) {
297 "%s: not interested in removable media device\n"
298 "%s\n", driver
, devpath
);
299 FW_SD_FREE_DEVPATH(devpath
)
303 if ((newdev
= calloc(1, sizeof (struct devicelist
)))
306 gettext("%s: identification function unable "
307 "to allocate space for device entry\n"),
309 libscsi_fini(handle
);
310 FW_SD_FREE_DEVPATH(devpath
)
311 return (FWFLASH_FAILURE
);
314 if ((newdev
->drvname
= calloc(1, strlen(driver
) + 1))
317 gettext("%s: Unable to allocate space to store a "
318 "driver name\n"), driver
);
319 libscsi_fini(handle
);
320 FW_SD_FREE_DEVICELIST(newdev
, devpath
)
321 return (FWFLASH_FAILURE
);
323 (void) strlcpy(newdev
->drvname
, driver
, strlen(driver
) + 1);
325 if ((newdev
->classname
= calloc(1, strlen(driver
) + 1))
328 gettext("%s: Unable to allocate space for a class "
329 "name\n"), drivername
);
330 libscsi_fini(handle
);
331 FW_SD_FREE_DRV_NAME(newdev
, devpath
)
332 return (FWFLASH_FAILURE
);
334 (void) strlcpy(newdev
->classname
, driver
, strlen(driver
) + 1);
336 /* Get the access name for current node */
337 if ((newdev
->access_devname
= calloc(1, MAXPATHLEN
)) == NULL
) {
339 gettext("%s: Unable to allocate space for a devfs "
341 libscsi_fini(handle
);
342 FW_SD_FREE_CLS_NAME(newdev
, devpath
)
343 return (FWFLASH_FAILURE
);
346 /* The slice number may be 2 or 0, we will try 2 first */
347 (void) snprintf(newdev
->access_devname
, MAXPATHLEN
,
348 "%s%s:c,raw", devprefix
, devpath
);
349 if ((target
= libscsi_open(handle
, NULL
,
350 newdev
->access_devname
)) == NULL
) {
351 /* try 0 for EFI label */
352 (void) snprintf(newdev
->access_devname
, MAXPATHLEN
,
353 "%s%s:a,raw", devprefix
, devpath
);
354 if ((target
= libscsi_open(handle
, NULL
,
355 newdev
->access_devname
)) == NULL
) {
357 "%s: unable to open device %s\n",
358 newdev
->drvname
, newdev
->access_devname
);
359 FW_SD_FREE_ACC_NAME(newdev
, devpath
)
364 /* and the /dev/rdsk/ name */
365 if ((newdev
->addresses
[0] = find_link(thisnode
,
366 newdev
->access_devname
)) == NULL
) {
367 libscsi_fini(handle
);
368 FW_SD_FREE_ACC_NAME(newdev
, devpath
)
369 return (FWFLASH_FAILURE
);
373 * Only alloc as much as we truly need, and DON'T forget
374 * that libdevinfo manages the memory!
376 if ((newdev
->ident
= calloc(1, sizeof (struct vpr
))) == NULL
) {
378 gettext("%s: Unable to allocate space for SCSI "
379 "INQUIRY data\n"), driver
);
380 libscsi_fini(handle
);
381 FW_SD_FREE_ADDR(newdev
, devpath
)
382 return (FWFLASH_FAILURE
);
385 /* We don't use new->ident->encap_ident currently */
387 /* Retrive information by using libscsi */
389 sp_temp
= (char *)libscsi_vendor(target
);
390 if (strncmp(sp_temp
, "ATA", 3) == 0) {
391 /* We need to do customize the output for SATA disks */
395 if ((newdev
->ident
->vid
=
396 calloc(1, strlen(sp_temp
) + 1)) == NULL
||
399 logmsg(MSG_ERROR
, gettext("%s: unable "
400 "to get vendor id of %s\n"),
402 newdev
->access_devname
);
404 logmsg(MSG_ERROR
, gettext("Memory "
405 "allocation failure\n"));
408 libscsi_close(handle
, target
);
409 libscsi_fini(handle
);
410 FW_SD_FREE_IDENT(newdev
, devpath
)
411 return (FWFLASH_FAILURE
);
413 strlcpy(newdev
->ident
->vid
, sp_temp
,
414 strlen(sp_temp
) + 1);
418 sp_temp
= (char *)libscsi_product(target
);
420 sp_temp_cut
= strchr(sp_temp
, ' ');
423 * There is no SPACE character in the PID field
424 * Customize strings for special SATA disks
426 if (sd_idtfy_custmz(newdev
, sp_temp
)
427 != FWFLASH_SUCCESS
) {
428 libscsi_close(handle
, target
);
429 libscsi_fini(handle
);
430 FW_SD_FREE_IDENT(newdev
, devpath
)
431 return (FWFLASH_FAILURE
);
434 /* The first string is vendor id */
435 if ((newdev
->ident
->vid
= calloc(1,
436 (sp_temp_cut
- sp_temp
+ 1))) == NULL
) {
437 logmsg(MSG_ERROR
, gettext("%s: unable "
438 "to get sata vendor id of %s\n"),
440 newdev
->access_devname
);
442 libscsi_close(handle
, target
);
443 libscsi_fini(handle
);
444 FW_SD_FREE_IDENT(newdev
, devpath
)
445 return (FWFLASH_FAILURE
);
447 strlcpy(newdev
->ident
->vid
, sp_temp
,
448 sp_temp_cut
- sp_temp
+ 1);
450 /* The second string is product id */
451 if ((newdev
->ident
->pid
=
452 calloc(1, strlen(sp_temp
) -
453 strlen(newdev
->ident
->vid
))) == NULL
) {
454 logmsg(MSG_ERROR
, gettext("%s: unable "
455 "to get sata product id of %s\n"),
457 newdev
->access_devname
);
459 libscsi_close(handle
, target
);
460 libscsi_fini(handle
);
461 FW_SD_FREE_IDENT_VID(newdev
, devpath
)
462 return (FWFLASH_FAILURE
);
464 strlcpy(newdev
->ident
->pid
, sp_temp_cut
+ 1,
466 strlen(newdev
->ident
->vid
));
469 if ((newdev
->ident
->pid
=
470 calloc(1, strlen(sp_temp
) + 1)) == NULL
||
472 logmsg(MSG_ERROR
, gettext("%s: unable to get "
473 "product id of %s\n"), newdev
->drvname
,
474 newdev
->access_devname
);
475 FW_SD_FREE_IDENT_VID(newdev
, devpath
)
476 libscsi_close(handle
, target
);
477 libscsi_fini(handle
);
478 return (FWFLASH_FAILURE
);
480 strlcpy(newdev
->ident
->pid
, sp_temp
,
481 strlen(sp_temp
) + 1);
485 sp_temp
= (char *)libscsi_revision(target
);
486 if ((newdev
->ident
->revid
= calloc(1, strlen(sp_temp
) + 1))
487 == NULL
|| sp_temp
== NULL
) {
488 logmsg(MSG_ERROR
, gettext("%s: unable to get revision "
489 "id of %s\n"), newdev
->drvname
,
490 newdev
->access_devname
);
491 libscsi_close(handle
, target
);
492 libscsi_fini(handle
);
493 FW_SD_FREE_IDENT_PID(newdev
, devpath
)
494 return (FWFLASH_FAILURE
);
496 strlcpy(newdev
->ident
->revid
, sp_temp
, strlen(sp_temp
) + 1);
498 /* Finish using libscsi */
499 libscsi_close(handle
, target
);
501 if (di_prop_lookup_strings(DDI_DEV_T_ANY
, thisnode
,
502 "inquiry-serial-no", &newdev
->addresses
[1]) < 0) {
504 "%s: no inquiry-serial-no property for %s\n",
505 driver
, newdev
->access_devname
);
506 logmsg(MSG_INFO
, "The errno is %d\n", errno
);
509 if ((di_prop_lookup_strings(DDI_DEV_T_ANY
, thisnode
,
510 "client-guid", &newdev
->addresses
[2])) < 0) {
512 "%s: no client-guid property "
514 driver
, newdev
->access_devname
);
516 if ((di_prop_lookup_strings(DDI_DEV_T_ANY
, thisnode
,
517 "guid", &newdev
->addresses
[2])) < 0) {
519 "%s: no guid property for device %s\n",
520 driver
, newdev
->access_devname
);
524 "client-guid property: %s\n",
525 newdev
->addresses
[2]);
530 newdev
->plugin
= self
;
532 TAILQ_INSERT_TAIL(fw_devices
, newdev
, nextdev
);
533 FW_SD_FREE_DEVPATH(devpath
)
535 libscsi_fini(handle
);
537 /* Check if sd targets presented are all unflashable. */
539 return (FWFLASH_FAILURE
);
541 if (fwflash_debug
!= 0) {
542 struct devicelist
*tempdev
;
544 TAILQ_FOREACH(tempdev
, fw_devices
, nextdev
) {
545 logmsg(MSG_INFO
, "%s:fw_identify:\n",
548 "\ttempdev @ 0x%lx\n"
549 "\t\taccess_devname: %s\n"
550 "\t\tdrvname: %s\tclassname: %s\n"
551 "\t\tident->vid: %s\n"
552 "\t\tident->pid: %s\n"
553 "\t\tident->revid: %s\n"
555 "\t\taddress[0]: %s\n"
556 "\t\taddress[1]: %s\n"
557 "\t\taddress[2]: %s\n"
558 "\t\tplugin @ 0x%lx\n\n",
560 tempdev
->access_devname
,
561 tempdev
->drvname
, newdev
->classname
,
564 tempdev
->ident
->revid
,
566 tempdev
->addresses
[0],
567 (tempdev
->addresses
[1] ? tempdev
->addresses
[1] :
569 (tempdev
->addresses
[2] ? tempdev
->addresses
[2] :
574 return (FWFLASH_SUCCESS
);
578 fw_devinfo(struct devicelist
*thisdev
)
580 fprintf(stdout
, gettext("Device[%d]\t\t\t%s\n"
581 " Class [%s]\t\t\t%s\n"),
582 thisdev
->index
, thisdev
->access_devname
,
583 thisdev
->classname
, thisdev
->addresses
[0]);
587 "\tVendor\t\t\t: %s\n"
588 "\tProduct\t\t\t: %s\n"
589 "\tFirmware revision\t: %-s\n"
590 "\tInquiry Serial Number : %-s\n"
591 "\tGUID\t\t\t: %s\n"),
594 thisdev
->ident
->revid
,
595 (thisdev
->addresses
[1] ? thisdev
->addresses
[1] :
597 (thisdev
->addresses
[2] ? thisdev
->addresses
[2] :
600 fprintf(stdout
, "\n\n");
602 return (FWFLASH_SUCCESS
);
606 fw_cleanup(struct devicelist
*thisdev
)
609 * Function to clean up all the memory allocated
610 * by this plugin, for thisdev.
612 free(thisdev
->access_devname
);
613 free(thisdev
->drvname
);
614 free(thisdev
->classname
);
617 * Note that we DO NOT free addresses[1,2] because _IF_
618 * these elements are valid, they are managed by libdevinfo
619 * and we didn't allocate any space for them.
621 free(thisdev
->addresses
[0]);
623 /* what this points to is freed in common code */
624 thisdev
->plugin
= NULL
;
626 free(thisdev
->ident
->vid
);
627 free(thisdev
->ident
->pid
);
628 free(thisdev
->ident
->revid
);
630 thisdev
->ident
= NULL
;
637 link_cb(di_devlink_t devlink
, void *arg
)
641 result
= di_devlink_path(devlink
);
642 if (result
== NULL
) {
643 arg
= (void *)"(null)";
645 (void) strlcpy(arg
, result
, strlen(result
) + 1);
648 logmsg(MSG_INFO
, "\nlink_cb::linkdata->resultstr = %s\n",
649 ((result
!= NULL
) ? result
: "(null)"));
651 return (DI_WALK_CONTINUE
);
655 find_link(di_node_t bnode
, char *acc_devname
)
657 di_minor_t devminor
= DI_MINOR_NIL
;
658 di_devlink_handle_t hdl
;
659 char *cbresult
= NULL
;
660 char linkname
[] = "^rdsk/\0";
662 if (bnode
== DI_NODE_NIL
) {
664 gettext("find_link must be called with non-null "
669 if ((cbresult
= calloc(1, MAXPATHLEN
)) == NULL
) {
670 logmsg(MSG_ERROR
, gettext("unable to allocate space for dev "
675 devminor
= di_minor_next(bnode
, devminor
);
677 hdl
= di_devlink_init(di_devfs_minor_path(devminor
), DI_MAKE_LINK
);
679 if (errno
== EPERM
|| errno
== EACCES
) {
681 gettext("%s: You must be super-user to use this "
682 "plugin.\n"), drivername
);
685 gettext("unable to take devlink snapshot: %s\n"),
693 if (di_devlink_walk(hdl
, linkname
, acc_devname
+ strlen(devprefix
),
694 DI_PRIMARY_LINK
, (void *)cbresult
, link_cb
) < 0) {
696 gettext("Unable to walk devlink snapshot for %s: %s\n"),
697 acc_devname
, strerror(errno
));
702 if (di_devlink_fini(&hdl
) < 0) {
704 gettext("Unable to close devlink snapshot: %s\n"),
708 logmsg(MSG_INFO
, "cbresult: %s\n", cbresult
);
713 sd_idtfy_custmz(struct devicelist
*device
, char *sp
)
715 /* vid customization */
716 if (strncmp(sp
, "ST", 2) == 0) {
717 /* Customize retail Seagate disks */
718 if ((device
->ident
->vid
= strdup("SEAGATE")) == NULL
) {
719 return (FWFLASH_FAILURE
);
721 } else if (strncmp(sp
, "SSD", 3) == 0) {
722 /* Customize retail INTEL disks */
723 if ((device
->ident
->vid
= strdup("INTEL")) == NULL
) {
724 return (FWFLASH_FAILURE
);
727 /* disks to do in the furture, fill 'ATA' first */
728 if ((device
->ident
->vid
= strdup("ATA")) == NULL
) {
729 return (FWFLASH_FAILURE
);
733 /* pid customization */
734 if ((device
->ident
->pid
= calloc(1, strlen(sp
) + 1)) == NULL
) {
735 logmsg(MSG_ERROR
, gettext("Unable to allocate space for "
737 free(device
->ident
->vid
);
738 return (FWFLASH_FAILURE
);
740 strlcpy(device
->ident
->pid
, sp
, strlen(sp
) + 1);
742 return (FWFLASH_SUCCESS
);