summaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
Diffstat (limited to 'sys')
-rw-r--r--sys/scsi/cd.c21
-rw-r--r--sys/scsi/ch.c95
-rw-r--r--sys/scsi/scsi_all.h16
-rw-r--r--sys/scsi/scsi_base.c92
-rw-r--r--sys/scsi/scsi_debug.h4
-rw-r--r--sys/scsi/scsi_disk.h67
-rw-r--r--sys/scsi/scsi_ioctl.c15
-rw-r--r--sys/scsi/scsi_tape.h6
-rw-r--r--sys/scsi/scsiconf.c16
-rw-r--r--sys/scsi/scsiconf.h8
-rw-r--r--sys/scsi/sd.c224
-rw-r--r--sys/scsi/st.c31
12 files changed, 386 insertions, 209 deletions
diff --git a/sys/scsi/cd.c b/sys/scsi/cd.c
index 0bb33ae9974..963b0c6c7be 100644
--- a/sys/scsi/cd.c
+++ b/sys/scsi/cd.c
@@ -1,8 +1,8 @@
-/* $OpenBSD: cd.c,v 1.22 1997/03/29 23:54:20 briggs Exp $ */
-/* $NetBSD: cd.c,v 1.92 1996/05/05 19:52:50 christos Exp $ */
+/* $OpenBSD: cd.c,v 1.23 1997/04/14 04:09:03 downsj Exp $ */
+/* $NetBSD: cd.c,v 1.100 1997/04/02 02:29:30 mycroft Exp $ */
/*
- * Copyright (c) 1994, 1995 Charles M. Hannum. All rights reserved.
+ * Copyright (c) 1994, 1995, 1997 Charles M. Hannum. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -111,7 +111,7 @@ void cdunlock __P((struct cd_softc *));
void cdstart __P((void *));
void cdminphys __P((struct buf *));
void cdgetdisklabel __P((dev_t, struct cd_softc *));
-int cddone __P((struct scsi_xfer *, int));
+void cddone __P((struct scsi_xfer *));
u_long cd_size __P((struct cd_softc *, int));
int cd_get_mode __P((struct cd_softc *, struct cd_mode_data *, int));
int cd_set_mode __P((struct cd_softc *, struct cd_mode_data *));
@@ -642,17 +642,14 @@ cdstart(v)
}
}
-int
-cddone(xs, complete)
+void
+cddone(xs)
struct scsi_xfer *xs;
- int complete;
{
struct cd_softc *cd = xs->sc_link->device_softc;
- if (complete && (xs->bp != NULL))
- disk_unbusy(&cd->sc_dk, (xs->bp->b_bcount - xs->bp->b_resid));
-
- return (0);
+ if (xs->bp != NULL)
+ disk_unbusy(&cd->sc_dk, xs->bp->b_bcount - xs->bp->b_resid);
}
void
@@ -1305,7 +1302,7 @@ cd_read_subchannel(cd, mode, format, track, data, len)
_lto2b(len, scsi_cmd.data_len);
return scsi_scsi_cmd(cd->sc_link, (struct scsi_generic *)&scsi_cmd,
sizeof(struct scsi_read_subchannel), (u_char *)data, len,
- CDRETRIES, 5000, NULL, SCSI_DATA_IN);
+ CDRETRIES, 5000, NULL, SCSI_DATA_IN||SCSI_SILENT);
}
/*
diff --git a/sys/scsi/ch.c b/sys/scsi/ch.c
index b4f6be9e15d..c7fdea0549e 100644
--- a/sys/scsi/ch.c
+++ b/sys/scsi/ch.c
@@ -1,8 +1,8 @@
-/* $OpenBSD: ch.c,v 1.6 1996/08/12 10:21:41 deraadt Exp $ */
-/* $NetBSD: ch.c,v 1.21 1996/04/19 00:02:29 christos Exp $ */
+/* $OpenBSD: ch.c,v 1.7 1997/04/14 04:09:05 downsj Exp $ */
+/* $NetBSD: ch.c,v 1.26 1997/02/21 22:06:52 thorpej Exp $ */
/*
- * Copyright (c) 1996 Jason R. Thorpe <thorpej@and.com>
+ * Copyright (c) 1996, 1997 Jason R. Thorpe <thorpej@and.com>
* All rights reserved.
*
* Partially based on an autochanger driver written by Stefan Grefen
@@ -48,6 +48,7 @@
#include <sys/device.h>
#include <sys/malloc.h>
#include <sys/conf.h>
+#include <sys/fcntl.h>
#include <scsi/scsi_all.h>
#include <scsi/scsi_changer.h>
@@ -81,6 +82,12 @@ struct ch_softc {
u_int8_t sc_exchangemask[4];
int flags; /* misc. info */
+
+ /*
+ * Quirks; see below.
+ */
+ int sc_settledelay; /* delay for settle */
+
};
/* sc_flags */
@@ -114,6 +121,21 @@ int ch_position __P((struct ch_softc *, struct changer_position *));
int ch_usergetelemstatus __P((struct ch_softc *, int, u_int8_t *));
int ch_getelemstatus __P((struct ch_softc *, int, int, caddr_t, size_t));
int ch_get_params __P((struct ch_softc *, int));
+void ch_get_quirks __P((struct ch_softc *, struct scsi_inquiry_data *));
+
+/*
+ * SCSI changer quirks.
+ */
+struct chquirk {
+ struct scsi_inquiry_pattern cq_match; /* device id pattern */
+ int cq_settledelay; /* settle delay, in seconds */
+};
+
+struct chquirk chquirks[] = {
+ {{T_CHANGER, T_REMOV,
+ "SPECTRA", "9000", "0200"},
+ 75},
+};
int
chmatch(parent, match, aux)
@@ -148,24 +170,35 @@ chattach(parent, self, aux)
printf("\n");
/*
+ * Find out our device's quirks.
+ */
+ ch_get_quirks(sc, sa->sa_inqbuf);
+
+ /*
+ * Some changers require a long time to settle out, to do
+ * tape inventory, for instance.
+ */
+ if (sc->sc_settledelay) {
+ printf("%s: waiting %d seconds for changer to settle...\n",
+ sc->sc_dev.dv_xname, sc->sc_settledelay);
+ delay(1000000 * sc->sc_settledelay);
+ }
+
+ /*
* Get information about the device. Note we can't use
* interrupts yet.
*/
if (ch_get_params(sc, SCSI_AUTOCONF))
printf("%s: offline\n", sc->sc_dev.dv_xname);
else {
- printf("%s: %d slot%s, %d drive%s, %d picker%s",
+#define PLURAL(c) (c) == 1 ? "" : "s"
+ printf("%s: %d slot%s, %d drive%s, %d picker%s, %d portal%s\n",
sc->sc_dev.dv_xname,
- sc->sc_counts[CHET_ST], (sc->sc_counts[CHET_ST] > 1) ?
- "s" : "",
- sc->sc_counts[CHET_DT], (sc->sc_counts[CHET_DT] > 1) ?
- "s" : "",
- sc->sc_counts[CHET_MT], (sc->sc_counts[CHET_MT] > 1) ?
- "s" : "");
- if (sc->sc_counts[CHET_IE])
- printf(", %d portal%s", sc->sc_counts[CHET_IE],
- (sc->sc_counts[CHET_IE] > 1) ? "s" : "");
- printf("\n");
+ sc->sc_counts[CHET_ST], PLURAL(sc->sc_counts[CHET_ST]),
+ sc->sc_counts[CHET_DT], PLURAL(sc->sc_counts[CHET_DT]),
+ sc->sc_counts[CHET_MT], PLURAL(sc->sc_counts[CHET_MT]),
+ sc->sc_counts[CHET_IE], PLURAL(sc->sc_counts[CHET_IE]));
+#undef PLURAL
#ifdef CHANGER_DEBUG
printf("%s: move mask: 0x%x 0x%x 0x%x 0x%x\n",
sc->sc_dev.dv_xname,
@@ -250,6 +283,21 @@ chioctl(dev, cmd, data, flags, p)
struct ch_softc *sc = ch_cd.cd_devs[CHUNIT(dev)];
int error = 0;
+ /*
+ * If this command can change the device's state, we must
+ * have the device open for writing.
+ */
+ switch (cmd) {
+ case CHIOGPICKER:
+ case CHIOGPARAMS:
+ case CHIOGSTATUS:
+ break;
+
+ default:
+ if ((flags & FWRITE) == 0)
+ return (EBADF);
+ }
+
switch (cmd) {
case CHIOMOVE:
error = ch_move(sc, (struct changer_move *)data);
@@ -637,3 +685,22 @@ ch_get_params(sc, scsiflags)
sc->sc_link->flags |= SDEV_MEDIA_LOADED;
return (0);
}
+
+void
+ch_get_quirks(sc, inqbuf)
+ struct ch_softc *sc;
+ struct scsi_inquiry_data *inqbuf;
+{
+ struct chquirk *match;
+ int priority;
+
+ sc->sc_settledelay = 0;
+
+ match = (struct chquirk *)scsi_inqmatch(inqbuf,
+ (caddr_t)chquirks,
+ sizeof(chquirks) / sizeof(chquirks[0]),
+ sizeof(chquirks[0]), &priority);
+ if (priority != 0) {
+ sc->sc_settledelay = match->cq_settledelay;
+ }
+}
diff --git a/sys/scsi/scsi_all.h b/sys/scsi/scsi_all.h
index c9492a5bb48..746d6dbf086 100644
--- a/sys/scsi/scsi_all.h
+++ b/sys/scsi/scsi_all.h
@@ -1,5 +1,5 @@
-/* $OpenBSD: scsi_all.h,v 1.4 1996/07/02 20:18:49 deraadt Exp $ */
-/* $NetBSD: scsi_all.h,v 1.7 1996/03/19 03:06:10 mycroft Exp $ */
+/* $OpenBSD: scsi_all.h,v 1.5 1997/04/14 04:09:06 downsj Exp $ */
+/* $NetBSD: scsi_all.h,v 1.10 1996/09/12 01:57:17 thorpej Exp $ */
/*
* SCSI general interface description
@@ -35,8 +35,14 @@
#define SCSI_CTL_LINK 0x01
#define SCSI_CTL_FLAG 0x02
#define SCSI_CTL_VENDOR 0xC0
-#define SCSI_CMD_LUN 0xA0 /* these two should not be needed */
-#define SCSI_CMD_LUN_SHIFT 5 /* LUN in the cmd is no longer SCSI */
+
+
+/*
+ * Some old SCSI devices need the LUN to be set in the top 3 bits of the
+ * second byte of the CDB.
+ */
+#define SCSI_CMD_LUN_MASK 0xe0
+#define SCSI_CMD_LUN_SHIFT 5
struct scsi_generic {
@@ -176,8 +182,6 @@ struct scsi_changedef {
#define CHANGE_DEFINITION 0x40
#define MODE_SENSE_BIG 0x54
#define MODE_SELECT_BIG 0x55
-#define MOVE_MEDIUM 0xa5
-#define READ_ELEMENT_STATUS 0xb8
/*
* Sort of an extra one, for SCSI_RESET.
diff --git a/sys/scsi/scsi_base.c b/sys/scsi/scsi_base.c
index ac52d489caa..42e47dbcb85 100644
--- a/sys/scsi/scsi_base.c
+++ b/sys/scsi/scsi_base.c
@@ -1,8 +1,8 @@
-/* $OpenBSD: scsi_base.c,v 1.10 1996/06/16 03:07:19 downsj Exp $ */
-/* $NetBSD: scsi_base.c,v 1.36 1996/05/03 19:48:20 christos Exp $ */
+/* $OpenBSD: scsi_base.c,v 1.11 1997/04/14 04:09:07 downsj Exp $ */
+/* $NetBSD: scsi_base.c,v 1.43 1997/04/02 02:29:36 mycroft Exp $ */
/*
- * Copyright (c) 1994, 1995 Charles Hannum. All rights reserved.
+ * Copyright (c) 1994, 1995, 1997 Charles M. Hannum. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -14,7 +14,7 @@
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
- * This product includes software developed by Charles Hannum.
+ * This product includes software developed by Charles M. Hannum.
* 4. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
@@ -49,8 +49,6 @@
#include <scsi/scsi_disk.h>
#include <scsi/scsiconf.h>
-void scsi_error __P((struct scsi_xfer *, int));
-
LIST_HEAD(xs_free_list, scsi_xfer) xs_free_list;
static __inline struct scsi_xfer *scsi_make_xs __P((struct scsi_link *,
@@ -181,6 +179,14 @@ scsi_make_xs(sc_link, scsi_cmd, cmdlen, data_addr, datalen,
xs->timeout = timeout;
xs->bp = bp;
+ /*
+ * Set the LUN in the CDB if we have an older device. We also
+ * set it for more modern SCSI-II devices "just in case".
+ */
+ if ((sc_link->scsi_version & SID_ANSII) <= 2)
+ xs->cmd->bytes[0] |=
+ ((sc_link->lun << SCSI_CMD_LUN_SHIFT) & SCSI_CMD_LUN_MASK);
+
return xs;
}
@@ -318,6 +324,7 @@ scsi_done(xs)
struct scsi_xfer *xs;
{
struct scsi_link *sc_link = xs->sc_link;
+ struct buf *bp;
int error;
SC_DEBUG(sc_link, SDEV_DB2, ("scsi_done\n"));
@@ -341,23 +348,7 @@ scsi_done(xs)
return;
}
- /*
- * If the device has it's own done routine, call it first.
- * If it returns a legit error value, return that, otherwise
- * it wants us to continue with normal processing.
- *
- * Make sure the upper-level driver knows that this might not
- * actually be the last time they hear from us. We need to get
- * status back.
- */
- if (sc_link->device->done) {
- SC_DEBUG(sc_link, SDEV_DB2, ("calling private done()\n"));
- error = (*sc_link->device->done)(xs, 0);
- if (error == EJUSTRETURN)
- goto done;
- SC_DEBUG(sc_link, SDEV_DB3, ("continuing with generic done()\n"));
- }
- if (xs->bp == NULL) {
+ if (!((xs->flags & (SCSI_NOSLEEP | SCSI_POLL)) == SCSI_NOSLEEP)) {
/*
* if it's a normal upper level request, then ask
* the upper level code to handle error checking
@@ -366,12 +357,14 @@ scsi_done(xs)
wakeup(xs);
return;
}
+
/*
* Go and handle errors now.
* If it returns ERESTART then we should RETRY
*/
retry:
- if (sc_err1(xs, 1) == ERESTART) {
+ error = sc_err1(xs, 1);
+ if (error == ERESTART) {
switch ((*(sc_link->adapter->scsi_cmd)) (xs)) {
case SUCCESSFULLY_QUEUED:
return;
@@ -382,7 +375,18 @@ retry:
goto retry;
}
}
-done:
+
+ bp = xs->bp;
+ if (bp) {
+ if (error) {
+ bp->b_error = error;
+ bp->b_flags |= B_ERROR;
+ bp->b_resid = bp->b_bcount;
+ } else {
+ bp->b_error = 0;
+ bp->b_resid = xs->resid;
+ }
+ }
if (sc_link->device->done) {
/*
* Tell the device the operation is actually complete.
@@ -390,9 +394,11 @@ done:
* notification of the upper-level driver only; they
* won't be returning any meaningful information to us.
*/
- (void)(*sc_link->device->done)(xs, 1);
+ (*sc_link->device->done)(xs);
}
scsi_free_xs(xs, SCSI_NOSLEEP);
+ if (bp)
+ biodone(bp);
}
int
@@ -417,14 +423,19 @@ retry:
* TRY_AGAIN_LATER, (as for polling)
* After the wakeup, we must still check if it succeeded
*
- * If we have a bp however, all the error processing
- * and the buffer code both expect us to return straight
- * to them, so as soon as the command is queued, return
+ * If we have a SCSI_NOSLEEP (typically because we have a buf)
+ * we just return. All the error proccessing and the buffer
+ * code both expect us to return straight to them, so as soon
+ * as the command is queued, return.
*/
switch ((*(xs->sc_link->adapter->scsi_cmd)) (xs)) {
case SUCCESSFULLY_QUEUED:
- if (xs->bp)
+ if ((xs->flags & (SCSI_NOSLEEP | SCSI_POLL)) == SCSI_NOSLEEP)
return EJUSTRETURN;
+#ifdef DIAGNOSTIC
+ if (xs->flags & SCSI_NOSLEEP)
+ panic("scsi_execute_xs: NOSLEEP and POLL");
+#endif
s = splbio();
while ((xs->flags & ITSDONE) == 0)
tsleep(xs, PRIBIO + 1, "scsi_scsi_cmd", 0);
@@ -561,30 +572,9 @@ sc_err1(xs, async)
break;
}
- scsi_error(xs, error);
return error;
}
-void
-scsi_error(xs, error)
- struct scsi_xfer *xs;
- int error;
-{
- struct buf *bp = xs->bp;
-
- if (bp) {
- if (error) {
- bp->b_error = error;
- bp->b_flags |= B_ERROR;
- bp->b_resid = bp->b_bcount;
- } else {
- bp->b_error = 0;
- bp->b_resid = xs->resid;
- }
- biodone(bp);
- }
-}
-
/*
* Look at the returned sense and act on the error, determining
* the unix error number to pass back. (0 = report no error)
diff --git a/sys/scsi/scsi_debug.h b/sys/scsi/scsi_debug.h
index b190adedd81..b46012c8c8c 100644
--- a/sys/scsi/scsi_debug.h
+++ b/sys/scsi/scsi_debug.h
@@ -1,5 +1,5 @@
-/* $OpenBSD: scsi_debug.h,v 1.4 1996/11/28 13:20:36 niklas Exp $ */
-/* $NetBSD: scsi_debug.h,v 1.5 1994/12/28 19:43:00 mycroft Exp $ */
+/* $OpenBSD: scsi_debug.h,v 1.5 1997/04/14 04:09:08 downsj Exp $ */
+/* $NetBSD: scsi_debug.h,v 1.7 1996/10/12 23:23:16 christos Exp $ */
/*
* Written by Julian Elischer (julian@tfs.com)
diff --git a/sys/scsi/scsi_disk.h b/sys/scsi/scsi_disk.h
index 614e40f83b3..002812619c7 100644
--- a/sys/scsi/scsi_disk.h
+++ b/sys/scsi/scsi_disk.h
@@ -1,5 +1,5 @@
-/* $OpenBSD: scsi_disk.h,v 1.5 1996/10/31 01:09:23 niklas Exp $ */
-/* $NetBSD: scsi_disk.h,v 1.9 1996/03/19 03:07:02 mycroft Exp $ */
+/* $OpenBSD: scsi_disk.h,v 1.6 1997/04/14 04:09:09 downsj Exp $ */
+/* $NetBSD: scsi_disk.h,v 1.10 1996/07/05 16:19:05 christos Exp $ */
/*
* SCSI interface description
@@ -176,41 +176,34 @@ union disk_pages {
u_int8_t reserved3;
} rigid_geometry;
struct page_flex_geometry {
- u_char pg_code; /* page code (should be 5) */
- u_char pg_length; /* page length (should be 0x1e) */
- u_char xfr_rate1;
- u_char xfr_rate0;
- u_char nheads; /* number of heads */
- u_char ph_sec_t; /* physical sectors per track */
- u_char bytes_s_1; /* bytes per sector (MSB) */
- u_char bytes_s_0; /* bytes per sector (LSB) */
- u_char ncyl_1; /* number of cylinders (MSB) */
- u_char ncyl_0; /* number of cylinders (LSB) */
- u_char st_cyl_wp_1; /* starting cyl., write precomp (MSB) */
- u_char st_cyl_wp_0; /* starting cyl., write precomp (LSB) */
- u_char st_cyl_rwc_1; /* starting cyl., red. write cur (MSB) */
- u_char st_cyl_rwc_0; /* starting cyl., red. write cur (LSB) */
- u_char driv_step_1; /* drive step rate (MSB) */
- u_char driv_step_0; /* drive step rate (LSB) */
- u_char driv_step_w; /* drive step pulse width */
- u_char head_settle_1; /* head settle delay (MSB) */
- u_char head_settle_0; /* head settle delay (LSB) */
- u_char motor_on; /* motor on delay */
- u_char motor_off; /* motor off delay */
- u_char flags; /* various flags */
-#define MO 0x20 /* motor on (pin 16)? */
-#define SSN 0x40 /* start at sector 1 */
-#define TRDY 0x20 /* RDY (pin 34) valid */
- u_char step_p_cyl; /* step pulses per cylinder */
- u_char write_pre; /* write precompensation */
- u_char head_load; /* head load delay */
- u_char head_unload; /* head unload delay */
- u_char pin_34_2; /* pin 34 (6) and pin 2 (7/11) definition */
- u_char pin_4_1; /* pin 4 (8/9) and pin 1 (13) definition */
- u_char reserved1;
- u_char reserved2;
- u_char reserved3;
- u_char reserved4;
+ u_int8_t pg_code; /* page code (should be 5) */
+ u_int8_t pg_length; /* page length (should be 0x1e) */
+ u_int8_t xfr_rate[2];
+ u_int8_t nheads; /* number of heads */
+ u_int8_t ph_sec_tr; /* physical sectors per track */
+ u_int8_t bytes_s[2]; /* bytes per sector */
+ u_int8_t ncyl[2]; /* number of cylinders */
+ u_int8_t st_cyl_wp[2]; /* start cyl., write precomp */
+ u_int8_t st_cyl_rwc[2]; /* start cyl., red. write cur */
+ u_int8_t driv_step[2]; /* drive step rate */
+ u_int8_t driv_step_w; /* drive step pulse width */
+ u_int8_t head_settle[2];/* head settle delay */
+ u_int8_t motor_on; /* motor on delay */
+ u_int8_t motor_off; /* motor off delay */
+ u_int8_t flags; /* various flags */
+#define MOTOR_ON 0x20 /* motor on (pin 16)? */
+#define START_AT_SECTOR_1 0x40 /* start at sector 1 */
+#define READY_VALID 0x20 /* RDY (pin 34) valid */
+ u_int8_t step_p_cyl; /* step pulses per cylinder */
+ u_int8_t write_pre; /* write precompensation */
+ u_int8_t head_load; /* head load delay */
+ u_int8_t head_unload; /* head unload delay */
+ u_int8_t pin_34_2; /* pin 34 (6) pin 2 (7/11) definition */
+ u_int8_t pin_4_1; /* pin 4 (8/9) pin 1 (13) definition */
+ u_int8_t reserved1;
+ u_int8_t reserved2;
+ u_int8_t reserved3;
+ u_int8_t reserved4;
} flex_geometry;
};
diff --git a/sys/scsi/scsi_ioctl.c b/sys/scsi/scsi_ioctl.c
index c9c5625dd44..f1fc067dff8 100644
--- a/sys/scsi/scsi_ioctl.c
+++ b/sys/scsi/scsi_ioctl.c
@@ -1,5 +1,5 @@
-/* $OpenBSD: scsi_ioctl.c,v 1.6 1996/08/13 00:06:24 niklas Exp $ */
-/* $NetBSD: scsi_ioctl.c,v 1.20 1996/02/14 21:47:22 christos Exp $ */
+/* $OpenBSD: scsi_ioctl.c,v 1.7 1997/04/14 04:09:11 downsj Exp $ */
+/* $NetBSD: scsi_ioctl.c,v 1.23 1996/10/12 23:23:17 christos Exp $ */
/*
* Copyright (c) 1994 Charles Hannum. All rights reserved.
@@ -46,6 +46,7 @@
#include <sys/buf.h>
#include <sys/proc.h>
#include <sys/device.h>
+#include <sys/fcntl.h>
#include <scsi/scsi_all.h>
#include <scsi/scsiconf.h>
@@ -289,6 +290,16 @@ scsi_do_ioctl(sc_link, dev, cmd, addr, flag, p)
SC_DEBUG(sc_link, SDEV_DB2, ("scsi_do_ioctl(0x%lx)\n", cmd));
+ /* Check for the safe-ness of this request. */
+ switch (cmd) {
+ case SCIOCIDENTIFY:
+ break;
+
+ default:
+ if ((flag & FWRITE) == 0)
+ return EBADF;
+ }
+
switch(cmd) {
case SCIOCCOMMAND: {
scsireq_t *screq = (scsireq_t *)addr;
diff --git a/sys/scsi/scsi_tape.h b/sys/scsi/scsi_tape.h
index 3ac4bc1eb3b..6b613873fd6 100644
--- a/sys/scsi/scsi_tape.h
+++ b/sys/scsi/scsi_tape.h
@@ -1,5 +1,5 @@
-/* $OpenBSD: scsi_tape.h,v 1.5 1996/10/31 01:09:24 niklas Exp $ */
-/* $NetBSD: scsi_tape.h,v 1.8 1996/03/19 03:07:36 mycroft Exp $ */
+/* $OpenBSD: scsi_tape.h,v 1.6 1997/04/14 04:09:13 downsj Exp $ */
+/* $NetBSD: scsi_tape.h,v 1.9 1996/05/24 02:04:47 thorpej Exp $ */
/*
* Copyright (c) 1994 Charles Hannum. All rights reserved.
@@ -210,6 +210,7 @@ struct block_desc_cipher {
0x15 1 45434 RLL CS ECMA TC17 4
0x16 48 10000 MFM C X3.193-1990 1
0x17 48 42500 MFM C X3B5/91-174 1
+ 0x45 73 67733 RLL C QIC3095
where Code means:
NRZI Non Return to Zero, change on ones
@@ -245,5 +246,6 @@ struct block_desc_cipher {
#define QIC_1320 0x12
#define DDS 0x13
#define DAT_1 0x13
+#define QIC_3095 0x45
#endif /* _SCSI_TAPE_H_ */
diff --git a/sys/scsi/scsiconf.c b/sys/scsi/scsiconf.c
index 2f4859caef3..822ffafc3a7 100644
--- a/sys/scsi/scsiconf.c
+++ b/sys/scsi/scsiconf.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: scsiconf.c,v 1.27 1997/04/02 08:01:56 deraadt Exp $ */
+/* $OpenBSD: scsiconf.c,v 1.28 1997/04/14 04:09:14 downsj Exp $ */
/* $NetBSD: scsiconf.c,v 1.57 1996/05/02 01:09:01 neil Exp $ */
/*
@@ -333,6 +333,8 @@ struct scsi_quirk_inquiry_pattern scsi_quirk_patterns[] = {
{{T_CDROM, T_REMOV,
"NEC ", "CD-ROM DRIVE:841", ""}, SDEV_NOLUNS},
{{T_CDROM, T_REMOV,
+ "PIONEER ", "CD-ROM DR-124X ", "1.01"}, SDEV_NOLUNS},
+ {{T_CDROM, T_REMOV,
"SONY ", "CD-ROM CDU-541 ", ""}, SDEV_NOLUNS},
{{T_CDROM, T_REMOV,
"SONY ", "CD-ROM CDU-55S ", ""}, SDEV_NOLUNS},
@@ -361,7 +363,11 @@ struct scsi_quirk_inquiry_pattern scsi_quirk_patterns[] = {
{{T_DIRECT, T_FIXED,
"DEC ", "RZ55 (C) DEC", ""}, SDEV_AUTOSAVE},
{{T_DIRECT, T_FIXED,
- "EMULEX ", "MD21/S2 ESDI", "A00"}, SDEV_FORCELUNS},
+ "EMULEX ", "MD21/S2 ESDI", "A00"}, SDEV_FORCELUNS|SDEV_AUTOSAVE},
+ {{T_DIRECT, T_FIXED,
+ "IBMRAID ", "0662S", ""}, SDEV_AUTOSAVE},
+ {{T_DIRECT, T_FIXED,
+ "IBM ", "0663H", ""}, SDEV_AUTOSAVE},
{{T_DIRECT, T_FIXED,
"MAXTOR ", "XT-3280 ", ""}, SDEV_NOLUNS},
{{T_DIRECT, T_FIXED,
@@ -393,6 +399,8 @@ struct scsi_quirk_inquiry_pattern scsi_quirk_patterns[] = {
{{T_DIRECT, T_FIXED,
"RODIME ", "RO3000S ", ""}, SDEV_NOLUNS},
{{T_DIRECT, T_FIXED,
+ "SEAGATE ", "ST125N ", ""}, SDEV_NOLUNS},
+ {{T_DIRECT, T_FIXED,
"SEAGATE ", "ST157N ", ""}, SDEV_NOLUNS},
{{T_DIRECT, T_FIXED,
"SEAGATE ", "ST296 ", ""}, SDEV_NOLUNS},
@@ -406,6 +414,9 @@ struct scsi_quirk_inquiry_pattern scsi_quirk_patterns[] = {
"IOMEGA", "ZIP 100", ""}, SDEV_NOMODESENSE},
{{T_DIRECT, T_FIXED,
"IBM", "0661467", "G"}, SDEV_NOMODESENSE},
+ /* Letting the motor run kills floppy drives and disks quit fast. */
+ {{T_DIRECT, T_REMOV,
+ "TEAC", "FC-1", ""}, SDEV_NOSTARTUNIT},
/* XXX: QIC-36 tape behind Emulex adapter. Very broken. */
{{T_SEQUENTIAL, T_REMOV,
@@ -631,6 +642,7 @@ scsi_probedev(scsi, target, lun)
if ((inqbuf.version & SID_ANSII) == 0 &&
(sc_link->quirks & SDEV_FORCELUNS) == 0)
sc_link->quirks |= SDEV_NOLUNS;
+ sc_link->scsi_version = inqbuf.version;
if ((sc_link->quirks & SDEV_NOLUNS) == 0)
scsi->moreluns |= (1 << target);
diff --git a/sys/scsi/scsiconf.h b/sys/scsi/scsiconf.h
index bf3c60337a3..d0a460715d3 100644
--- a/sys/scsi/scsiconf.h
+++ b/sys/scsi/scsiconf.h
@@ -1,5 +1,5 @@
-/* $OpenBSD: scsiconf.h,v 1.11 1997/01/18 16:18:32 briggs Exp $ */
-/* $NetBSD: scsiconf.h,v 1.29 1996/03/19 03:07:50 mycroft Exp $ */
+/* $OpenBSD: scsiconf.h,v 1.12 1997/04/14 04:09:15 downsj Exp $ */
+/* $NetBSD: scsiconf.h,v 1.35 1997/04/02 02:29:38 mycroft Exp $ */
/*
* Copyright (c) 1993, 1994, 1995 Charles Hannum. All rights reserved.
@@ -125,7 +125,7 @@ struct scsi_device {
* we're simply notifying the upper-level driver that the command
* is complete and expect no status back.
*/
- int (*done) __P((struct scsi_xfer *, int));
+ void (*done) __P((struct scsi_xfer *));
};
/*
@@ -135,6 +135,7 @@ struct scsi_device {
* as well.
*/
struct scsi_link {
+ u_int8_t scsi_version; /* SCSI-I, SCSI-II, etc. */
u_int8_t scsibus; /* the Nth scsibus */
u_int8_t target; /* targ of this dev */
u_int8_t lun; /* lun of this dev */
@@ -155,6 +156,7 @@ struct scsi_link {
#define SDEV_NOLUNS 0x04 /* does not grok LUNs */
#define SDEV_FORCELUNS 0x08 /* prehistoric drive/ctlr groks LUNs */
#define SDEV_NOMODESENSE 0x10 /* removable media/optical drives */
+#define SDEV_NOSTARTUNIT 0x20 /* do not issue start unit requests in sd.c */
u_int8_t inquiry_flags; /* copy of flags from probe INQUIRY */
struct scsi_device *device; /* device entry points etc. */
void *device_softc; /* needed for call to foo_start */
diff --git a/sys/scsi/sd.c b/sys/scsi/sd.c
index 8a5331d0921..fa818ef3baf 100644
--- a/sys/scsi/sd.c
+++ b/sys/scsi/sd.c
@@ -1,8 +1,8 @@
-/* $OpenBSD: sd.c,v 1.22 1997/01/04 08:50:21 deraadt Exp $ */
-/* $NetBSD: sd.c,v 1.100.4.1 1996/06/04 23:14:08 thorpej Exp $ */
+/* $OpenBSD: sd.c,v 1.23 1997/04/14 04:09:16 downsj Exp $ */
+/* $NetBSD: sd.c,v 1.111 1997/04/02 02:29:41 mycroft Exp $ */
/*
- * Copyright (c) 1994, 1995 Charles M. Hannum. All rights reserved.
+ * Copyright (c) 1994, 1995, 1997 Charles M. Hannum. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -98,6 +98,13 @@ struct sd_softc {
u_long disksize; /* total number sectors */
} params;
struct buf buf_queue;
+ u_int8_t type;
+};
+
+struct scsi_mode_sense_data {
+ struct scsi_mode_header header;
+ struct scsi_blk_desc blk_desc;
+ union disk_pages pages;
};
int sdmatch __P((struct device *, void *, void *));
@@ -107,9 +114,12 @@ void sdunlock __P((struct sd_softc *));
void sdminphys __P((struct buf *));
void sdgetdisklabel __P((dev_t, struct sd_softc *));
void sdstart __P((void *));
-int sddone __P((struct scsi_xfer *, int));
+void sddone __P((struct scsi_xfer *));
int sd_reassign_blocks __P((struct sd_softc *, u_long));
+int sd_get_optparms __P((struct sd_softc *, int, struct disk_parms *));
int sd_get_parms __P((struct sd_softc *, int));
+static int sd_mode_sense __P((struct sd_softc *, struct scsi_mode_sense_data *,
+ int, int));
struct cfattach sd_ca = {
sizeof(struct sd_softc), sdmatch, sdattach
@@ -162,6 +172,7 @@ sdattach(parent, self, aux)
struct device *parent, *self;
void *aux;
{
+ int error;
struct sd_softc *sd = (void *)self;
struct disk_parms *dp = &sd->params;
struct scsibus_attach_args *sa = aux;
@@ -173,6 +184,7 @@ sdattach(parent, self, aux)
* Store information needed to contact our base driver
*/
sd->sc_link = sc_link;
+ sd->type = (sa->sa_inqbuf->device & SID_TYPE);
sc_link->device = &sd_switch;
sc_link->device_softc = sd;
if (sc_link->openings > SDOUTSTANDING)
@@ -200,9 +212,15 @@ sdattach(parent, self, aux)
*/
printf("\n");
printf("%s: ", sd->sc_dev.dv_xname);
- if (scsi_start(sd->sc_link, SSS_START,
- SCSI_AUTOCONF | SCSI_IGNORE_ILLEGAL_REQUEST | SCSI_IGNORE_MEDIA_CHANGE | SCSI_SILENT) ||
- sd_get_parms(sd, SCSI_AUTOCONF) != 0)
+
+ if ((sd->sc_link->quirks & SDEV_NOSTARTUNIT) == 0) {
+ error = scsi_start(sd->sc_link, SSS_START,
+ SCSI_AUTOCONF | SCSI_IGNORE_ILLEGAL_REQUEST |
+ SCSI_IGNORE_MEDIA_CHANGE | SCSI_SILENT);
+ } else
+ error = 0;
+
+ if (error || sd_get_parms(sd, SCSI_AUTOCONF) != 0)
printf("drive offline\n");
else
printf("%ldMB, %d cyl, %d head, %d sec, %d bytes/sec\n",
@@ -295,11 +313,14 @@ sdopen(dev, flag, fmt, p)
goto bad3;
/* Start the pack spinning if necessary. */
- error = scsi_start(sc_link, SSS_START,
- SCSI_IGNORE_ILLEGAL_REQUEST |
- SCSI_IGNORE_MEDIA_CHANGE | SCSI_SILENT);
- if (error)
- goto bad3;
+ if ((sc_link->quirks & SDEV_NOSTARTUNIT) == 0) {
+ error = scsi_start(sc_link, SSS_START,
+ SCSI_IGNORE_ILLEGAL_REQUEST |
+ SCSI_IGNORE_MEDIA_CHANGE |
+ SCSI_SILENT);
+ if (error)
+ goto bad3;
+ }
sc_link->flags |= SDEV_OPEN;
@@ -600,7 +621,7 @@ sdstart(v)
*/
error = scsi_scsi_cmd(sc_link, cmdp, cmdlen,
(u_char *)bp->b_data, bp->b_bcount,
- SDRETRIES, 10000, bp, SCSI_NOSLEEP |
+ SDRETRIES, 60000, bp, SCSI_NOSLEEP |
((bp->b_flags & B_READ) ? SCSI_DATA_IN : SCSI_DATA_OUT));
if (error) {
disk_unbusy(&sd->sc_dk, 0);
@@ -610,17 +631,14 @@ sdstart(v)
}
}
-int
-sddone(xs, complete)
+void
+sddone(xs)
struct scsi_xfer *xs;
- int complete;
{
struct sd_softc *sd = xs->sc_link->device_softc;
- if (complete && (xs->bp != NULL))
+ if (xs->bp != NULL)
disk_unbusy(&sd->sc_dk, (xs->bp->b_bcount - xs->bp->b_resid));
-
- return (0);
}
void
@@ -792,7 +810,10 @@ sdgetdisklabel(dev, sd)
/* as long as it's not 0 - readdisklabel divides by it (?) */
}
- strncpy(lp->d_typename, "SCSI disk", 16);
+ if (sd->type == T_OPTICAL)
+ strncpy(lp->d_typename, "SCSI optical", 16);
+ else
+ strncpy(lp->d_typename, "SCSI disk", 16);
lp->d_type = DTYPE_SCSI;
strncpy(lp->d_packname, "fictitious", 16);
lp->d_secperunit = sd->params.disksize;
@@ -844,16 +865,41 @@ sd_reassign_blocks(sd, blkno)
5000, NULL, SCSI_DATA_OUT);
}
-/*
- * Get the scsi driver to send a full inquiry to the * device and use the
- * results to fill out the disk parameter structure.
- */
+
+static int
+sd_mode_sense(sd, scsi_sense, page, flags)
+ struct sd_softc *sd;
+ struct scsi_mode_sense_data *scsi_sense;
+ int page, flags;
+{
+ struct scsi_mode_sense scsi_cmd;
+
+ /*
+ * Make sure the sense buffer is clean before we do
+ * the mode sense, so that checks for bogus values of
+ * 0 will work in case the mode sense fails.
+ */
+ bzero(scsi_sense, sizeof(*scsi_sense));
+
+ bzero(&scsi_cmd, sizeof(scsi_cmd));
+ scsi_cmd.opcode = MODE_SENSE;
+ scsi_cmd.page = page;
+ scsi_cmd.length = 0x20;
+ /*
+ * If the command worked, use the results to fill out
+ * the parameter structure
+ */
+ return scsi_scsi_cmd(sd->sc_link, (struct scsi_generic *)&scsi_cmd,
+ sizeof(scsi_cmd), (u_char *)scsi_sense, sizeof(*scsi_sense),
+ SDRETRIES, 6000, NULL, flags | SCSI_DATA_IN | SCSI_SILENT);
+}
+
int
-sd_get_parms(sd, flags)
+sd_get_optparms(sd, flags, dp)
struct sd_softc *sd;
int flags;
+ struct disk_parms *dp;
{
- struct disk_parms *dp = &sd->params;
struct scsi_mode_sense scsi_cmd;
struct scsi_mode_sense_data {
struct scsi_mode_header header;
@@ -861,26 +907,67 @@ sd_get_parms(sd, flags)
union disk_pages pages;
} scsi_sense;
u_long sectors;
+ int error;
- if ((sd->sc_link->quirks & SDEV_NOMODESENSE) != 0)
- goto fake_it;
-
- /*
- * do a "mode sense page 4"
+ dp->blksize = 512;
+ if ((sectors = scsi_size(sd->sc_link, flags)) == 0)
+ return 1;
+
+ /* XXX
+ * It is better to get the following params from the
+ * mode sense page 6 only (optical device parameter page).
+ * However, there are stupid optical devices which does NOT
+ * support the page 6. Ghaa....
*/
- bzero(&scsi_sense, sizeof(scsi_sense));
bzero(&scsi_cmd, sizeof(scsi_cmd));
scsi_cmd.opcode = MODE_SENSE;
- scsi_cmd.page = 4;
- scsi_cmd.length = 0x20;
+ scsi_cmd.page = 0x3f; /* all pages */
+ scsi_cmd.length = sizeof(struct scsi_mode_header) +
+ sizeof(struct scsi_blk_desc);
+
+ if ((error = scsi_scsi_cmd(sd->sc_link,
+ (struct scsi_generic *)&scsi_cmd, sizeof(scsi_cmd),
+ (u_char *)&scsi_sense, sizeof(scsi_sense), SDRETRIES,
+ 6000, NULL, flags | SCSI_DATA_IN)) != 0)
+ return error;
+
+ dp->blksize = _3btol(scsi_sense.blk_desc.blklen);
+ if (dp->blksize == 0)
+ dp->blksize = 512;
/*
- * If the command worked, use the results to fill out
- * the parameter structure
+ * Create a pseudo-geometry.
*/
- if (scsi_scsi_cmd(sd->sc_link, (struct scsi_generic *)&scsi_cmd,
- sizeof(scsi_cmd), (u_char *)&scsi_sense, sizeof(scsi_sense),
- SDRETRIES, 6000, NULL, flags | SCSI_DATA_IN) == 0) {
+ dp->heads = 64;
+ dp->sectors = 32;
+ dp->cyls = sectors / (dp->heads * dp->sectors);
+ dp->disksize = sectors;
+
+ return 0;
+}
+
+/*
+ * Get the scsi driver to send a full inquiry to the * device and use the
+ * results to fill out the disk parameter structure.
+ */
+int
+sd_get_parms(sd, flags)
+ struct sd_softc *sd;
+ int flags;
+{
+ struct disk_parms *dp = &sd->params;
+ struct scsi_mode_sense_data scsi_sense;
+ u_long sectors;
+ int page;
+ int error;
+
+ if (sd->type == T_OPTICAL) {
+ if ((error = sd_get_optparms(sd, flags, dp)) != 0)
+ sd->sc_link->flags &= ~SDEV_MEDIA_LOADED;
+ return error;
+ }
+
+ if ((error = sd_mode_sense(sd, &scsi_sense, page = 4, flags)) == 0) {
SC_DEBUG(sd->sc_link, SDEV_DB3,
("%d cyls, %d heads, %d precomp, %d red_write, %d land_zone\n",
_3btol(scsi_sense.pages.rigid_geometry.ncyl),
@@ -899,11 +986,8 @@ sd_get_parms(sd, flags)
dp->cyls = _3btol(scsi_sense.pages.rigid_geometry.ncyl);
dp->blksize = _3btol(scsi_sense.blk_desc.blklen);
- if (dp->heads == 0 || dp->cyls == 0) {
- printf("%s: mode sense (4) returned nonsense",
- sd->sc_dev.dv_xname);
+ if (dp->heads == 0 || dp->cyls == 0)
goto fake_it;
- }
if (dp->blksize == 0)
dp->blksize = 512;
@@ -912,48 +996,40 @@ sd_get_parms(sd, flags)
dp->disksize = sectors;
sectors /= (dp->heads * dp->cyls);
dp->sectors = sectors; /* XXX dubious on SCSI */
+
return 0;
- } else {
- /*
- * do a "mode sense page 5"
- */
- scsi_cmd.opcode = MODE_SENSE;
- scsi_cmd.page = 5;
- scsi_cmd.length = 0x20;
- if (scsi_scsi_cmd(sd->sc_link, (struct scsi_generic *)&scsi_cmd,
- sizeof(scsi_cmd), (u_char *)&scsi_sense, sizeof(scsi_sense),
- SDRETRIES, 6000, NULL,
- flags | SCSI_DATA_IN | SCSI_SILENT) == 0) {
- dp->heads = scsi_sense.pages.flex_geometry.nheads;
- dp->cyls =
- scsi_sense.pages.flex_geometry.ncyl_1 * 256 +
- scsi_sense.pages.flex_geometry.ncyl_0;
- dp->blksize = _3btol(scsi_sense.blk_desc.blklen);
- dp->sectors = scsi_sense.pages.flex_geometry.ph_sec_t;
- dp->disksize = dp->heads * dp->cyls * dp->sectors;
- if (dp->heads == 0 || dp->cyls == 0
- || dp->sectors == 0) {
- printf("%s: mode sense (5) returned nonsense",
- sd->sc_dev.dv_xname);
- goto fake_it;
- }
+ }
- if (dp->blksize == 0)
- dp->blksize = 512;
+ if ((error = sd_mode_sense(sd, &scsi_sense, page = 5, flags)) == 0) {
+ dp->heads = scsi_sense.pages.flex_geometry.nheads;
+ dp->cyls = _2btol(scsi_sense.pages.flex_geometry.ncyl);
+ dp->blksize = _3btol(scsi_sense.blk_desc.blklen);
+ dp->sectors = scsi_sense.pages.flex_geometry.ph_sec_tr;
+ dp->disksize = dp->heads * dp->cyls * dp->sectors;
+ if (dp->disksize == 0)
+ goto fake_it;
+
+ if (dp->blksize == 0)
+ dp->blksize = 512;
- return 0;
- } else
- printf("%s: could not mode sense (4/5)", sd->sc_dev.dv_xname);
+ return 0;
}
fake_it:
+ if ((sd->sc_link->quirks & SDEV_NOMODESENSE) == 0) {
+ if (error == 0)
+ printf("%s: mode sense (%d) returned nonsense",
+ sd->sc_dev.dv_xname, page);
+ else
+ printf("%s: could not mode sense (4/5)",
+ sd->sc_dev.dv_xname);
+ printf("; using fictitious geometry\n");
+ }
/*
* use adaptec standard fictitious geometry
* this depends on which controller (e.g. 1542C is
* different. but we have to put SOMETHING here..)
*/
- if ((sd->sc_link->quirks & SDEV_NOMODESENSE) == 0)
- printf("; using fictitious geometry\n");
sectors = scsi_size(sd->sc_link, flags);
dp->heads = 64;
dp->sectors = 32;
diff --git a/sys/scsi/st.c b/sys/scsi/st.c
index c11c3fa46e9..f10c83d3f75 100644
--- a/sys/scsi/st.c
+++ b/sys/scsi/st.c
@@ -1,5 +1,5 @@
-/* $OpenBSD: st.c,v 1.14 1997/02/24 20:17:35 jkatz Exp $ */
-/* $NetBSD: st.c,v 1.66 1996/05/05 19:53:01 christos Exp $ */
+/* $OpenBSD: st.c,v 1.15 1997/04/14 04:09:17 downsj Exp $ */
+/* $NetBSD: st.c,v 1.71 1997/02/21 23:03:49 thorpej Exp $ */
/*
* Copyright (c) 1994 Charles Hannum. All rights reserved.
@@ -81,8 +81,11 @@
#define STUNIT(z) ((minor(z) >> 4) )
#define CTLMODE 3
-#define SCSI_2_MAX_DENSITY_CODE 0x17 /* maximum density code specified
- * in SCSI II spec. */
+/*
+ * Maximum density code known.
+ */
+#define SCSI_2_MAX_DENSITY_CODE 0x45
+
/*
* Define various devices that we know mis-behave in some way,
* and note how they are bad, so we can correct for them
@@ -99,6 +102,7 @@ struct quirkdata {
#define ST_Q_SENSE_HELP 0x0002 /* must do READ for good MODE SENSE */
#define ST_Q_IGNORE_LOADS 0x0004
#define ST_Q_BLKSIZE 0x0008 /* variable-block media_blksize > 0 */
+#define ST_Q_UNIMODAL 0x0010 /* unimode drive rejects mode select */
u_int page_0_size;
#define MAX_PAGE_0_SIZE 64
struct modes modes[4];
@@ -202,6 +206,13 @@ struct st_quirk_inquiry_pattern st_quirk_patterns[] = {
{0, 0, 0}, /* minor 8-11 */
{0, 0, 0} /* minor 12-15 */
}}},
+ {{T_SEQUENTIAL, T_REMOV,
+ "HP ", "T4000s ", ""}, {ST_Q_UNIMODAL, 0, {
+ {0, 0, QIC_3095}, /* minor 0-3 */
+ {0, 0, QIC_3095}, /* minor 4-7 */
+ {0, 0, QIC_3095}, /* minor 8-11 */
+ {0, 0, QIC_3095}, /* minor 12-15 */
+ }}},
#if 0
{{T_SEQUENTIAL, T_REMOV,
"EXABYTE ", "EXB-8200 ", ""}, {0, 12, {
@@ -1403,6 +1414,18 @@ st_mode_select(st, flags)
scsi_select_len = 12 + st->page_0_size;
/*
+ * This quirk deals with drives that have only one valid mode
+ * and think this gives them license to reject all mode selects,
+ * even if the selected mode is the one that is supported.
+ */
+ if (st->quirks & ST_Q_UNIMODAL) {
+ SC_DEBUG(sc_link, SDEV_DB3,
+ ("not setting density 0x%x blksize 0x%x\n",
+ st->density, st->blksize));
+ return 0;
+ }
+
+ /*
* Set up for a mode select
*/
bzero(&cmd, sizeof(cmd));