Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 17 Nov 2011 17:16:21 +0000
From:      Nima Misaghian <nmisaghian@sandvine.com>
To:        Craig Rodrigues <rodrigc@crodrigues.org>
Cc:        "freebsd-current@freebsd.org" <freebsd-current@freebsd.org>
Subject:   RE: Adding disk firmware programming capability to camcontrol
Message-ID:  <0A3573FC36A1BE41AAA3DFF287C7968405688A@wtl-exch-2.sandvine.com>
In-Reply-To: <CAG=rPVfzjZ=aJHVbcrR9r3u4iW3E4ra=0u%2B8-Q7zHAT3GUQTiA@mail.gmail.com>
References:  <20111028204706.GA57454@sandvine.com> <CAG=rPVfzjZ=aJHVbcrR9r3u4iW3E4ra=0u%2B8-Q7zHAT3GUQTiA@mail.gmail.com>

next in thread | previous in thread | raw e-mail | index | archive | help

[-- Attachment #1 --]
I have updated the patch according to feedbacks I received. The main modification was to warn the user and ask for confirmation before downloading a firmware image. A -y option has been added that suppresses the confirmation-- the same as the one for the format command.

The new patch is against HEAD and could be downloaded from:
http://people.freebsd.org/~emaste/patches/fwdownload.diff


Reviews/suggestions are greatly appreciated.


Nima Misaghian 
nmisaghian@sandvine.com


> -----Original Message-----
> From: crodr001@gmail.com [mailto:crodr001@gmail.com] On Behalf Of Craig
> Rodrigues
> Sent: Thursday, November 03, 2011 10:06 PM
> To: Nima Misaghian
> Cc: freebsd-current@freebsd.org
> Subject: Re: Adding disk firmware programming capability to camcontrol
> 
> On Fri, Oct 28, 2011 at 1:47 PM, Nima Misaghian
> <nmisaghian@sandvine.com> wrote:
> > Hi,
> >
> > I have got code developed by Andre Albsmeier that is capable of
> > programming firmware of hard drives from several vendors and  turned
> > it into a camcontrol command.
> 
> +1
> 
> I took a look at your patch and it looks great.  I have worked on a
> storage product before where there
> was a requirement to reprogram the firmware of hard drives.  On tha
> product,
> proprietary Windows tools had to be used.  It would have been nice to
> be able to use camcontrol in FreeBSD.
> 
> I think the patch is fine in its current form.  Very few "regular
> users" need to reprogram hard drive firmware.
> It is a special administrative operation.
> 
> --
> Craig Rodrigues
> rodrigc@crodrigues.org

[-- Attachment #2 --]
--- old/camcontrol.h	2011-11-17 11:51:30.000000000 -0500
+++ new/camcontrol.h	2011-11-17 11:51:30.000000000 -0500
@@ -40,6 +40,8 @@
 	int got;
 };
 
+int fwdownload(struct cam_device *device, int argc, char **argv,
+	       char *combinedopt, int verbose, int retry_count, int timeout);
 void mode_sense(struct cam_device *device, int mode_page, int page_control,
 		int dbd, int retry_count, int timeout, u_int8_t *data,
 		int datalen);
@@ -49,8 +51,11 @@
 	       int edit, int binary, int retry_count, int timeout);
 void mode_list(struct cam_device *device, int page_control, int dbd,
 	       int retry_count, int timeout);
+int scsidoinquiry(struct cam_device *device, int argc, char **argv,
+		  char *combinedopt, int retry_count, int timeout);
 char *cget(void *hook, char *name);
 int iget(void *hook, char *name);
 void arg_put(void *hook, int letter, void *arg, int count, char *name);
+int get_confirmation(void);
 void usage(int verbose);
 #endif /* _CAMCONTROL_H */
--- old/camcontrol.c	2011-11-17 11:51:30.000000000 -0500
+++ new/camcontrol.c	2011-11-17 11:51:30.000000000 -0500
@@ -86,7 +86,8 @@
 	CAM_CMD_SMP_RG		= 0x00000018,
 	CAM_CMD_SMP_PC		= 0x00000019,
 	CAM_CMD_SMP_PHYLIST	= 0x0000001a,
-	CAM_CMD_SMP_MANINFO	= 0x0000001b
+	CAM_CMD_SMP_MANINFO	= 0x0000001b,
+	CAM_CMD_DOWNLOAD_FW	= 0x0000001c
 } cam_cmdmask;
 
 typedef enum {
@@ -180,6 +181,7 @@
 	{"idle", CAM_CMD_IDLE, CAM_ARG_NONE, "t:"},
 	{"standby", CAM_CMD_STANDBY, CAM_ARG_NONE, "t:"},
 	{"sleep", CAM_CMD_SLEEP, CAM_ARG_NONE, ""},
+	{"fwdownload", CAM_CMD_DOWNLOAD_FW, CAM_ARG_NONE, "f:ys"},
 #endif /* MINIMALISTIC */
 	{"help", CAM_CMD_USAGE, CAM_ARG_NONE, NULL},
 	{"-?", CAM_CMD_USAGE, CAM_ARG_NONE, NULL},
@@ -683,7 +685,7 @@
 	return(error);
 }
 
-static int
+int
 scsidoinquiry(struct cam_device *device, int argc, char **argv,
 	      char *combinedopt, int retry_count, int timeout)
 {
@@ -3571,7 +3573,7 @@
 	union ccb *ccb;
 	int c;
 	int ycount = 0, quiet = 0;
-	int error = 0, response = 0, retval = 0;
+	int error = 0, retval = 0;
 	int use_timeout = 10800 * 1000;
 	int immediate = 1;
 	struct format_defect_list_header fh;
@@ -3625,27 +3627,7 @@
 	}
 
 	if (ycount == 0) {
-
-		do {
-			char str[1024];
-
-			fprintf(stdout, "Are you SURE you want to do "
-				"this? (yes/no) ");
-
-			if (fgets(str, sizeof(str), stdin) != NULL) {
-
-				if (strncasecmp(str, "yes", 3) == 0)
-					response = 1;
-				else if (strncasecmp(str, "no", 2) == 0)
-					response = -1;
-				else {
-					fprintf(stdout, "Please answer"
-						" \"yes\" or \"no\"\n");
-				}
-			}
-		} while (response == 0);
-
-		if (response == -1) {
+		if (!get_confirmation()) {
 			error = 1;
 			goto scsiformat_bailout;
 		}
@@ -5693,6 +5675,7 @@
 "        camcontrol idle       [dev_id][generic args][-t time]\n"
 "        camcontrol standby    [dev_id][generic args][-t time]\n"
 "        camcontrol sleep      [dev_id][generic args]\n"
+"        camcontrol fwdownload [dev_id][generic args] <-f fw_image> [-y][-s]\n"
 #endif /* MINIMALISTIC */
 "        camcontrol help\n");
 	if (!verbose)
@@ -5728,6 +5711,7 @@
 "idle        send the ATA IDLE command to the named device\n"
 "standby     send the ATA STANDBY command to the named device\n"
 "sleep       send the ATA SLEEP command to the named device\n"
+"fwdownload  program firmware of the named device with the given image"
 "help        this message\n"
 "Device Identifiers:\n"
 "bus:target        specify the bus and target, lun defaults to 0\n"
@@ -5819,7 +5803,12 @@
 "-w                don't send immediate format command\n"
 "-y                don't ask any questions\n"
 "idle/standby arguments:\n"
-"-t <arg>          number of seconds before respective state.\n");
+"-t <arg>          number of seconds before respective state.\n"
+"fwdownload arguments:\n"
+"-f fw_image       path to firmware image file\n"
+"-y                don't ask any questions\n"
+"-s                run in simulation mode\n"
+"-v                print info for every firmware segment sent to device\n");
 #endif /* MINIMALISTIC */
 }
 
@@ -6135,6 +6124,10 @@
 						 combinedopt, retry_count,
 						 timeout);
 			break;
+		case CAM_CMD_DOWNLOAD_FW:
+			error = fwdownload(cam_dev, argc, argv, combinedopt,
+			    arglist & CAM_ARG_VERBOSE, retry_count, timeout);
+			break;
 #endif /* MINIMALISTIC */
 		case CAM_CMD_USAGE:
 			usage(1);
--- old/fwdownload.c	1969-12-31 19:00:00.000000000 -0500
+++ new/fwdownload.c	2011-11-17 11:51:30.000000000 -0500
@@ -0,0 +1,378 @@
+/*-
+ * Copyright (c) 2011 Sandvine Incorporated. All rights reserved.
+ * Copyright (c) 2002-2011 Andre Albsmeier <andre@albsmeier.net>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer,
+ *    without modification, immediately at the beginning of the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution. 
+ *    
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT  
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * BEWARE:
+ *
+ * The fact that you see your favorite vendor listed below does not
+ * imply that your equipment won't break when you use this software
+ * with it. It only means that the firmware of at least one device type
+ * of each vendor listed has been programmed successfully using this code.
+ *
+ * The -s option simulates a download but does nothing apart from that.
+ * It can be used to check what chunk sizes would have been used with the
+ * specified device.
+ */
+
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <err.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <cam/scsi/scsi_all.h>
+#include <cam/scsi/scsi_message.h>
+#include <camlib.h>
+
+#include "camcontrol.h"
+
+#define	CMD_TIMEOUT 50000	/* 50 seconds */
+
+typedef enum {
+	VENDOR_HITACHI,
+	VENDOR_HP,
+	VENDOR_IBM,
+	VENDOR_PLEXTOR,
+	VENDOR_QUANTUM,
+	VENDOR_SEAGATE,
+	VENDOR_UNKNOWN
+} fw_vendor_t;
+
+struct fw_vendor {
+	fw_vendor_t type;
+	const char *pattern;
+	int max_pkt_size;
+	u_int8_t cdb_byte2;
+	u_int8_t cdb_byte2_last;
+	int inc_cdb_buffer_id;
+	int inc_cdb_offset;
+};
+
+struct fw_vendor vendors_list[] = {
+	{VENDOR_HITACHI,	"HITACHI",	0x8000, 0x05, 0x05, 1, 0},
+	{VENDOR_HP,		"HP",		0x8000, 0x07, 0x07, 0, 1},
+	{VENDOR_IBM,		"IBM",		0x8000, 0x05, 0x05, 1, 0},
+	{VENDOR_PLEXTOR,	"PLEXTOR",	0x2000, 0x04, 0x05, 0, 1},
+	{VENDOR_QUANTUM,	"QUANTUM",	0x2000, 0x04, 0x05, 0, 1},
+	{VENDOR_SEAGATE,	"SEAGATE",	0x8000, 0x07, 0x07, 0, 1},
+	{VENDOR_UNKNOWN,	NULL,		0x0000, 0x00, 0x00, 0, 0}
+};
+
+static struct fw_vendor *fw_get_vendor(struct cam_device *cam_dev);
+static char	*fw_read_img(char *fw_img_path, struct fw_vendor *vp,
+		    int *num_bytes);
+static int	 fw_download_img(struct cam_device *cam_dev,
+		    struct fw_vendor *vp, char *buf, int img_size,
+		    int sim_mode, int verbose, int retry_count, int timeout);
+
+/*
+ * Find entry in vendors list that belongs to
+ * the vendor of given cam device.
+ */
+static struct fw_vendor *
+fw_get_vendor(struct cam_device *cam_dev)
+{
+	char vendor[SID_VENDOR_SIZE + 1];
+	struct fw_vendor *vp;
+
+	if (cam_dev == NULL)
+		return (NULL);
+	cam_strvis((u_char *)vendor, (u_char *)cam_dev->inq_data.vendor,
+	    sizeof(cam_dev->inq_data.vendor), sizeof(vendor));
+	for (vp = vendors_list; vp->pattern != NULL; vp++) {
+		if (!cam_strmatch((const u_char *)vendor,
+		    (const u_char *)vp->pattern, strlen(vendor)))
+			break;
+	}
+	return (vp);
+}
+
+/*
+ * Allocate a buffer and read fw image file into it
+ * from given path. Number of bytes read is stored
+ * in num_bytes.
+ */
+static char *
+fw_read_img(char *fw_img_path, struct fw_vendor *vp, int *num_bytes)
+{
+	int fd;
+	struct stat stbuf;
+	char *buf;
+	off_t img_size;
+	int skip_bytes = 0;
+
+	if ((fd = open(fw_img_path, O_RDONLY)) < 0) {
+		warn("Could not open image file %s", fw_img_path);
+		return (NULL);
+	}
+	if (fstat(fd, &stbuf) < 0) {
+		warn("Could not stat image file %s", fw_img_path);
+		goto bailout1;
+	}
+	if ((img_size = stbuf.st_size) == 0) {
+		warnx("Zero length image file %s", fw_img_path);
+		goto bailout1;
+	}
+	if ((buf = malloc(img_size)) == NULL) {
+		warnx("Could not allocate buffer to read image file %s",
+		    fw_img_path);
+		goto bailout1;
+	}
+	/* Skip headers if applicable. */
+	switch (vp->type) {
+	case VENDOR_SEAGATE:
+		if (read(fd, buf, 16) != 16) {
+			warn("Could not read image file %s", fw_img_path);
+			goto bailout;
+		}
+		if (lseek(fd, 0, SEEK_SET) == -1) {
+			warn("Unable to lseek");
+			goto bailout;
+		}
+		if ((strncmp(buf, "SEAGATE,SEAGATE ", 16) == 0) ||
+		    (img_size % 512 == 80))
+			skip_bytes = 80;
+		break;
+	default:
+		break;
+	}
+	if (skip_bytes != 0) {
+		fprintf(stdout, "Skipping %d byte header.\n", skip_bytes);
+		if (lseek(fd, skip_bytes, SEEK_SET) == -1) {
+			warn("Could not lseek");
+			goto bailout;
+		}
+		img_size -= skip_bytes;
+	}
+	/* Read image into a buffer. */
+	if (read(fd, buf, img_size) != img_size) {
+		warn("Could not read image file %s", fw_img_path);
+		goto bailout;
+	}
+	*num_bytes = img_size;
+	return (buf);
+bailout:
+	free(buf);
+bailout1:
+	close(fd);
+	*num_bytes = 0;
+	return (NULL);
+}
+
+/* 
+ * Download firmware stored in buf to cam_dev. If simulation mode
+ * is enabled, only show what packet sizes would be sent to the 
+ * device but do not sent any actual packets
+ */
+static int
+fw_download_img(struct cam_device *cam_dev, struct fw_vendor *vp,
+    char *buf, int img_size, int sim_mode, int verbose, int retry_count,
+    int timeout)
+{
+	struct scsi_write_buffer cdb;
+	union ccb *ccb;
+	int pkt_count = 0;
+	u_int32_t pkt_size = 0;
+	char *pkt_ptr = buf;
+	u_int32_t offset;
+	int last_pkt = 0;
+
+	if ((ccb = cam_getccb(cam_dev)) == NULL) {
+		warnx("Could not allocate CCB");
+		return (1);
+	}
+	scsi_test_unit_ready(&ccb->csio, 0, NULL, MSG_SIMPLE_Q_TAG,
+	    SSD_FULL_SIZE, 5000);
+	/* Disable freezing the device queue. */
+	ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
+	if (cam_send_ccb(cam_dev, ccb) < 0) {
+		warnx("Error sending test unit ready");
+		if (verbose)
+			cam_error_print(cam_dev, ccb, CAM_ESF_ALL,
+			    CAM_EPF_ALL, stderr);
+		cam_freeccb(ccb);
+		return(1);
+	}
+	if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
+		warnx("Device is not ready");
+		if (verbose)
+			cam_error_print(cam_dev, ccb, CAM_ESF_ALL,
+			    CAM_EPF_ALL, stderr);
+		cam_freeccb(ccb);
+		return (1);
+	}
+	pkt_size = vp->max_pkt_size;
+	if (verbose || sim_mode) {
+		fprintf(stdout,
+		    "--------------------------------------------------\n");
+		fprintf(stdout,
+		    "PktNo.	PktSize	       BytesRemaining	LastPkt\n");
+		fprintf(stdout,
+		    "--------------------------------------------------\n");
+	}
+	/* Download single fw packets. */
+	do {
+		if (img_size <= vp->max_pkt_size) {
+			last_pkt = 1;
+			pkt_size = img_size;
+		}
+		if (verbose || sim_mode)
+			fprintf(stdout, "%3u   %5u (0x%05X)   %7u (0x%06X)   "
+			    "%d\n", pkt_count, pkt_size, pkt_size,
+			    img_size - pkt_size, img_size - pkt_size,
+			    last_pkt);
+		bzero(&cdb, sizeof(cdb));
+		cdb.opcode  = WRITE_BUFFER;
+		cdb.control = 0;
+		/* Parameter list length. */
+		scsi_ulto3b(pkt_size, &cdb.length[0]);
+		offset = vp->inc_cdb_offset ? (pkt_ptr - buf) : 0;
+		scsi_ulto3b(offset, &cdb.offset[0]);
+		cdb.byte2 = last_pkt ? vp->cdb_byte2_last : vp->cdb_byte2;
+		cdb.buffer_id = vp->inc_cdb_buffer_id ? pkt_count : 0;
+		/* Zero out payload of ccb union after ccb header. */
+		bzero((u_char *)ccb + sizeof(struct ccb_hdr),
+		    sizeof(struct ccb_scsiio) - sizeof(struct ccb_hdr));
+		/* Copy previously constructed cdb into ccb_scsiio struct. */
+		bcopy(&cdb, &ccb->csio.cdb_io.cdb_bytes[0],
+		    sizeof(struct scsi_write_buffer));
+		/* Fill rest of ccb_scsiio struct. */
+		if (!sim_mode) {
+			cam_fill_csio(&ccb->csio,		/* ccb_scsiio	*/
+			    retry_count,			/* retries	*/
+			    NULL,				/* cbfcnp	*/
+			    CAM_DIR_OUT | CAM_DEV_QFRZDIS,	/* flags	*/
+			    CAM_TAG_ACTION_NONE,		/* tag_action	*/
+			    (u_char *)pkt_ptr,			/* data_ptr	*/
+			    pkt_size,				/* dxfer_len	*/
+			    SSD_FULL_SIZE,			/* sense_len	*/
+			    sizeof(struct scsi_write_buffer),	/* cdb_len	*/
+			    timeout ? timeout : CMD_TIMEOUT);	/* timeout	*/
+			/* Execute the command. */
+			if (cam_send_ccb(cam_dev, ccb) < 0) {
+				warnx("Error writing image to device");
+				if (verbose)
+					cam_error_print(cam_dev, ccb, CAM_ESF_ALL,
+					    CAM_EPF_ALL, stderr);
+				goto bailout;
+			}
+		}
+		/* Prepare next round. */
+		pkt_count++;
+		pkt_ptr += pkt_size;
+		img_size -= pkt_size;
+	} while(!last_pkt);
+	if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
+		if (verbose)
+			cam_error_print(cam_dev, ccb, CAM_ESF_ALL,
+			    CAM_EPF_ALL, stderr);
+		goto bailout;
+	}
+	cam_freeccb(ccb);
+	return (0);
+bailout:
+	cam_freeccb(ccb);
+	return (1);
+}
+
+int
+fwdownload(struct cam_device *device, int argc, char **argv,
+    char *combinedopt, int verbose, int retry_count, int timeout)
+{
+	struct fw_vendor *vp;
+	char *fw_img_path = NULL;
+	char *buf;
+	int img_size;
+	int c;
+	int sim_mode = 0;
+	int confirmed = 0;
+
+	while ((c = getopt(argc, argv, combinedopt)) != -1) {
+		switch (c) {
+		case 's':
+			sim_mode = 1;
+			confirmed = 1;
+			break;
+		case 'f':
+			fw_img_path = optarg;
+			break;
+		case 'y':
+			confirmed = 1;
+			break;
+		default:
+			break;
+		}
+	}
+
+	if (fw_img_path == NULL)
+		errx(1,
+		    "you must specify a firmware image file using -f option");
+
+	vp = fw_get_vendor(device);
+	if (vp == NULL || vp->type == VENDOR_UNKNOWN)
+		errx(1, "Unsupported device");
+
+	buf = fw_read_img(fw_img_path, vp, &img_size);
+	if (buf == NULL)
+		goto fail;
+
+	if (!confirmed) {
+		fprintf(stdout, "You are about to download firmware image (%s)"
+		    " into the following device:\n",
+		    fw_img_path);
+		if (scsidoinquiry(device, argc, argv, combinedopt, 0,
+		    5000) != 0) {
+			warnx("Error sending inquiry");
+			goto fail;
+		}
+		fprintf(stdout, "\nIt may damage your drive. ");
+		if (!get_confirmation())
+			goto fail;
+	}
+	if (sim_mode)
+		fprintf(stdout, "Running in simulation mode\n");
+
+	if (fw_download_img(device, vp, buf, img_size, sim_mode, verbose,
+	    retry_count, timeout) != 0) {
+		fprintf(stderr, "Firmware download failed\n");
+		goto fail;
+	} else 
+		fprintf(stdout, "Firmware download successful\n");
+
+	free(buf);
+	return (0);
+fail:
+	if (buf != NULL)
+		free(buf);
+	return (1);
+}
+
--- old/camcontrol.8	2011-11-17 11:51:30.000000000 -0500
+++ new/camcontrol.8	2011-11-17 11:51:30.000000000 -0500
@@ -27,7 +27,7 @@
 .\"
 .\" $FreeBSD$
 .\"
-.Dd November 30, 2010
+.Dd November 17, 2011
 .Dt CAMCONTROL 8
 .Os
 .Sh NAME
@@ -220,6 +220,13 @@
 .Op device id
 .Op generic args
 .Nm
+.Ic fwdownload
+.Op device id
+.Op generic args
+.Aq Fl f Ar fw_image
+.Op Fl y
+.Op Fl s
+.Nm
 .Ic help
 .Sh DESCRIPTION
 The
@@ -1060,6 +1067,49 @@
 .It Ic sleep
 Put ATA device into SLEEP state. Note that the only way get device out of
 this state may be reset.
+.It Ic fwdownload
+Program firmware of the named SCSI device using the image file provided.
+.Pp
+Current list of supported vendors:
+.Bl -bullet -offset indent -compact
+.It
+HITACHI
+.It
+HP
+.It
+IBM
+.It
+PLEXTOR
+.It
+QUANTUM
+.It
+SEAGATE
+.El
+.Pp
+.Em WARNING! WARNING! WARNING!
+.Pp
+Very little testing has been done to make sure that different device models
+of each vendor work correctly with fwdownload command. Showing up a vendor 
+name in the supported list basically means firmware of at least one device
+type from that vendor has successfully been programmed with fwdownload
+command. Extra caution should be taken when using this command since there is
+no guarantee it would not break a device from listed vendors.
+.Bl -tag -width 11n
+.It Fl f Ar fw_image
+Path to the firmware image file to be downloaded to the specified device.
+.It Fl y
+Do not ask the user for confirmation.
+.It Fl s
+Run in simulation mode where packet sizes that are going to be sent are shown,
+but no actual packet is sent to the device. No confimation question is asked 
+from user in simulation mode.
+.It Fl v
+Besides showing sense information in case of a failure, the verbose option causes
+.Nm
+to output a line for every firmware segment that is sent to the device by the
+fwdownload command
+-- the same as the ones shown in simulation mode.
+.El
 .It Ic help
 Print out verbose usage information.
 .El
--- old/Makefile	2011-11-17 11:51:30.000000000 -0500
+++ new/Makefile	2011-11-17 11:51:30.000000000 -0500
@@ -1,7 +1,7 @@
 # $FreeBSD$
 
 PROG=	camcontrol
-SRCS=	camcontrol.c util.c
+SRCS=	camcontrol.c fwdownload.c util.c
 .if !defined(RELEASE_CRUNCH)
 SRCS+=	modeedit.c
 .else
--- old/util.c	2011-11-17 11:51:30.000000000 -0500
+++ new/util.c	2011-11-17 11:51:30.000000000 -0500
@@ -154,3 +154,31 @@
 	if (verbose)
 		putchar('\n');
 }
+
+/*
+ * Get confirmation from user
+ * Return values:
+ *    1: confirmed
+ *    0: unconfirmed
+ */
+int
+get_confirmation()
+{
+	char str[1024];
+	int response = -1;
+
+	do {
+		fprintf(stdout, "Are you SURE you want to do this? (yes/no) ");
+		if (fgets(str, sizeof(str), stdin) != NULL) {
+			if (strncasecmp(str, "yes", 3) == 0)
+				response = 1;
+			else if (strncasecmp(str, "no", 2) == 0)
+				response = 0;
+			else
+				fprintf(stdout,
+				    "Please answer \"yes\" or \"no\"\n");
+		} else
+			response = 0;
+	} while (response == -1);
+	return (response);
+}

Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?0A3573FC36A1BE41AAA3DFF287C7968405688A>