dadk.c revision 1dc8bc23152a02d4586ec1fd8612f7e8f57ceb42
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* Direct Attached Disk
*/
/*
* Local Function Prototypes
*/
static void dadk_restart(void *pktp);
struct tgcom_objops dadk_com_ops = {
0, 0
};
/*
* architecture dependent allocation restrictions for dadk_iob_alloc(). For
* x86, we'll set dma_attr_addr_hi to dadk_max_phys_addr and dma_attr_sgllen
* to dadk_sgl_size during _init().
*/
#if defined(__sparc)
static ddi_dma_attr_t dadk_alloc_attr = {
DMA_ATTR_V0, /* version number */
0x0, /* lowest usable address */
0xFFFFFFFFull, /* high DMA address range */
0xFFFFFFFFull, /* DMA counter register */
1, /* DMA address alignment */
1, /* DMA burstsizes */
1, /* min effective DMA size */
0xFFFFFFFFull, /* max DMA xfer size */
0xFFFFFFFFull, /* segment boundary */
1, /* s/g list length */
512, /* granularity of device */
0, /* DMA transfer flags */
};
static ddi_dma_attr_t dadk_alloc_attr = {
DMA_ATTR_V0, /* version number */
0x0, /* lowest usable address */
0x0, /* high DMA address range [set in _init()] */
0xFFFFull, /* DMA counter register */
512, /* DMA address alignment */
1, /* DMA burstsizes */
1, /* min effective DMA size */
0xFFFFFFFFull, /* max DMA xfer size */
0xFFFFFFFFull, /* segment boundary */
0, /* s/g list length [set in _init()] */
512, /* granularity of device */
0, /* DMA transfer flags */
};
int dadk_sgl_size = 0xFF;
#endif
int silent);
struct tgdk_objops dadk_ops = {
0
};
/*
* Local static data
*/
#ifdef DADK_DEBUG
#define DENT 0x0001
#define DERR 0x0002
#define DIO 0x0004
#define DGEOM 0x0010
#define DSTATE 0x0020
static int dadk_debug = DGEOM;
#endif /* DADK_DEBUG */
static int dadk_dk_maxphys = 0x80000;
static char *dadk_cmds[] = {
"\000Unknown", /* unknown */
"\001read sector", /* DCMD_READ 1 */
"\002write sector", /* DCMD_WRITE 2 */
"\003format track", /* DCMD_FMTTRK 3 */
"\004format whole drive", /* DCMD_FMTDRV 4 */
"\005recalibrate", /* DCMD_RECAL 5 */
"\006seek sector", /* DCMD_SEEK 6 */
"\007read verify", /* DCMD_RDVER 7 */
"\010read defect list", /* DCMD_GETDEF 8 */
"\011lock door", /* DCMD_LOCK 9 */
"\012unlock door", /* DCMD_UNLOCK 10 */
"\013start motor", /* DCMD_START_MOTOR 11 */
"\014stop motor", /* DCMD_STOP_MOTOR 12 */
"\015eject", /* DCMD_EJECT 13 */
"\016update geometry", /* DCMD_UPDATE_GEOM 14 */
"\017get state", /* DCMD_GET_STATE 15 */
"\020cdrom pause", /* DCMD_PAUSE 16 */
"\021cdrom resume", /* DCMD_RESUME 17 */
"\022cdrom play track index", /* DCMD_PLAYTRKIND 18 */
"\023cdrom play msf", /* DCMD_PLAYMSF 19 */
"\024cdrom sub channel", /* DCMD_SUBCHNL 20 */
"\025cdrom read mode 1", /* DCMD_READMODE1 21 */
"\026cdrom read toc header", /* DCMD_READTOCHDR 22 */
"\027cdrom read toc entry", /* DCMD_READTOCENT 23 */
"\030cdrom read offset", /* DCMD_READOFFSET 24 */
"\031cdrom read mode 2", /* DCMD_READMODE2 25 */
"\032cdrom volume control", /* DCMD_VOLCTRL 26 */
"\033flush cache", /* DCMD_FLUSH_CACHE 27 */
};
static char *dadk_sense[] = {
"\000Success", /* DERR_SUCCESS */
"\001address mark not found", /* DERR_AMNF */
"\002track 0 not found", /* DERR_TKONF */
"\003aborted command", /* DERR_ABORT */
"\004write fault", /* DERR_DWF */
"\005ID not found", /* DERR_IDNF */
"\006drive busy", /* DERR_BUSY */
"\007uncorrectable data error", /* DERR_UNC */
"\010bad block detected", /* DERR_BBK */
"\011invalid command", /* DERR_INVCDB */
"\012device hard error", /* DERR_HARD */
"\013illegal length indicated", /* DERR_ILI */
"\014end of media", /* DERR_EOM */
"\015media change requested", /* DERR_MCR */
"\016recovered from error", /* DERR_RECOVER */
"\017device not ready", /* DERR_NOTREADY */
"\020medium error", /* DERR_MEDIUM */
"\021hardware error", /* DERR_HW */
"\022illegal request", /* DERR_ILL */
"\023unit attention", /* DERR_UNIT_ATTN */
"\024data protection", /* DERR_DATA_PROT */
"\025miscompare", /* DERR_MISCOMPARE */
"\026ICRC error during UDMA", /* DERR_ICRC */
"\027reserved", /* DERR_RESV */
};
static char *dadk_name = "Disk";
/*
* This is the loadable module wrapper
*/
extern struct mod_ops mod_miscops;
&mod_miscops, /* Type of module */
"Direct Attached Disk"
};
static struct modlinkage modlinkage = {
};
int
_init(void)
{
#ifdef DADK_DEBUG
if (dadk_debug & DENT)
PRF("dadk_init: call\n");
#endif
#if defined(__x86)
/* set the max physical address for iob allocs on x86 */
/*
* set the sgllen for iob allocs on x86. If this is set less than
* the number of pages the buffer will take (taking into account
* alignment), it would force the allocator to try and allocate
* contiguous pages.
*/
#endif
return (mod_install(&modlinkage));
}
int
_fini(void)
{
#ifdef DADK_DEBUG
if (dadk_debug & DENT)
PRF("dadk_fini: call\n");
#endif
return (mod_remove(&modlinkage));
}
int
{
}
struct tgdk_obj *
{
if (!dkobjp)
return (NULL);
#ifdef DADK_DEBUG
if (dadk_debug & DENT)
#endif
return (dkobjp);
}
int
{
/* initialize the communication object */
dadkp->dad_cmd_count = 0;
}
int
{
return (DDI_SUCCESS);
}
void
{
if (dadkp->dad_bbhobjp) {
}
if (dadkp->dad_flcobjp) {
}
}
/* ARGSUSED */
int
{
struct scsi_device *devp;
char name[80];
return (DDI_PROBE_FAILURE);
}
case DTYPE_DIRECT:
break;
case DTYPE_RODIRECT: /* eg cdrom */
break;
case DTYPE_WORM:
case DTYPE_OPTICAL:
default:
return (DDI_PROBE_FAILURE);
}
dadkp->dad_blkshf = 0;
/* display the device name */
return (DDI_PROBE_SUCCESS);
}
/* ARGSUSED */
int
{
return (DDI_SUCCESS);
}
int
{
/* free the old bbh object */
if (dadkp->dad_bbhobjp)
/* initialize the new bbh object */
return (DDI_SUCCESS);
}
/* ARGSUSED */
int
{
int error;
int wce;
return (DDI_SUCCESS);
}
} else {
DADK_SILENT) ||
DADK_SILENT)) {
return (DDI_FAILURE);
}
}
/*
* get write cache enable state
* If there is an error, must assume that write cache
* is enabled.
* NOTE: Since there is currently no Solaris mechanism to
* change the state of the Write Cache Enable feature,
* this code just checks the value of the WCE bit
* obtained at device init time. If a mechanism
* is added to the driver to change WCE, dad_wce
* must be updated appropriately.
*/
/* logical disk geometry */
return (DDI_FAILURE);
/* get physical disk geometry */
return (DDI_FAILURE);
/* start profiling */
return (DDI_SUCCESS);
}
static void
{
int totsize;
int i;
if (totsize == 0) {
totsize = 2048;
} else {
}
} else {
/* Round down sector size to multiple of 512B */
}
/* set sec,block shift factor - (512->0, 1024->1, 2048->2, etc.) */
;
dadkp->dad_blkshf = i;
}
static void
{
char kstatname[KSTAT_STRLEN];
if (dadkp->dad_errstats)
return;
sizeof (dadk_errstats_t) / sizeof (kstat_named_t),
if (!dadkp->dad_errstats)
return;
"Soft Errors", KSTAT_DATA_UINT32);
"Hard Errors", KSTAT_DATA_UINT32);
"Transport Errors", KSTAT_DATA_UINT32);
"Model", KSTAT_DATA_CHAR);
"Revision", KSTAT_DATA_CHAR);
"Serial No", KSTAT_DATA_CHAR);
"Size", KSTAT_DATA_ULONGLONG);
"Media Error", KSTAT_DATA_UINT32);
"Device Not Ready", KSTAT_DATA_UINT32);
"No Device", KSTAT_DATA_UINT32);
"Recoverable", KSTAT_DATA_UINT32);
"Illegal Request", KSTAT_DATA_UINT32);
/* get model */
/* get serial */
/* Get revision */
/* Get capacity */
}
int
{
}
return (DDI_SUCCESS);
}
static void
{
if (!dadkp->dad_errstats)
return;
}
int
{
return (DDI_FAILURE);
}
return (DDI_FAILURE);
}
dadkp->dad_cmd_count++;
return (DDI_SUCCESS);
}
int
{
if (dadkp->dad_rdonly) {
return (DDI_FAILURE);
}
return (DDI_FAILURE);
}
if (!pktp) {
return (DDI_FAILURE);
}
}
if (pktp->cp_private)
return (DDI_SUCCESS);
}
/* ARGSUSED */
int
{
switch (cmd) {
case DKIOCGETDEF:
{
unsigned char *secbuf;
/*
* copyin header ....
* yields head number and buffer address
*/
flag))
return (EFAULT);
return (ENXIO);
if (!secbuf)
return (ENOMEM);
if (!bp) {
return (ENOMEM);
}
dadkp->dad_cmd_count++;
if (!err) {
}
return (err);
}
case DIOCTL_RWCMD:
{
struct dadkio_rwcmd *rwcmdp;
/*
* copied in by cmdk and, if necessary, converted to the
* correct datamodel
*/
/*
* handle the complex cases here; we pass these
* through to the driver, which will queue them and
* handle the requests asynchronously. The simpler
* cases ,which can return immediately, fail here, and
* the request reverts to the dadk_ioctl routine, while
* will reroute them directly to the ata driver.
*/
case DADKIO_RWCMD_READ :
/*FALLTHROUGH*/
case DADKIO_RWCMD_WRITE:
return (status);
default:
return (EINVAL);
}
}
case DKIOC_UPDATEFW:
/*
* Require PRIV_ALL privilege to invoke DKIOC_UPDATEFW
* to protect the firmware update from malicious use
*/
return (EPERM);
else
case DKIOCFLUSHWRITECACHE:
{
int err = 0;
int is_sync = 1;
/*
* If a callback was requested: a
* callback will always be done if the
* caller saw the DKIOCFLUSHWRITECACHE
* ioctl return 0, and never done if the
* caller saw the ioctl return an error.
*/
err);
/*
* Did callback and reported error.
* Since we did a callback, ioctl
* should return 0.
*/
err = 0;
}
return (err);
}
SET_BP_SEC(bp, 0);
struct dk_callback *dkc2 =
(struct dk_callback *)kmem_zalloc(
sizeof (struct dk_callback), KM_SLEEP);
/*
* Borrow b_list to carry private data
* to the b_iodone func.
*/
is_sync = 0;
}
/*
* Setup command pkt
* dadk_pktprep() can't fail since DDI_DMA_SLEEP set
*/
pktp->cp_byteleft = 0;
pktp->cp_secleft = 0;
pktp->cp_bytexfer = 0;
dadkp->dad_cmd_count++;
if (is_sync) {
}
return (err);
}
default:
}
switch (cmd) {
case CDROMSTOP:
0, DADK_SILENT));
case CDROMSTART:
0, DADK_SILENT));
case DKIOCLOCK:
case DKIOCUNLOCK:
case DKIOCEJECT:
case CDROMEJECT:
{
int ret;
DADK_SILENT)) {
return (ret);
}
DADK_SILENT)) {
return (ret);
}
return (0);
}
default:
return (ENOTTY);
/*
* cdrom audio commands
*/
case CDROMPAUSE:
cmd = DCMD_PAUSE;
break;
case CDROMRESUME:
cmd = DCMD_RESUME;
break;
case CDROMPLAYMSF:
cmd = DCMD_PLAYMSF;
break;
case CDROMPLAYTRKIND:
break;
case CDROMREADTOCHDR:
break;
case CDROMREADTOCENTRY:
break;
case CDROMVOLCTRL:
cmd = DCMD_VOLCTRL;
break;
case CDROMSUBCHNL:
cmd = DCMD_SUBCHNL;
break;
case CDROMREADMODE2:
break;
case CDROMREADMODE1:
break;
case CDROMREADOFFSET:
break;
}
}
int
{
return (0);
}
int
{
sizeof (struct tgdk_geom));
return (DDI_SUCCESS);
}
int
{
sizeof (struct tgdk_geom));
return (DDI_SUCCESS);
}
int
{
return (DDI_SUCCESS);
}
{
return (NULL);
return (NULL);
}
/*
* use i_ddi_mem_alloc() for now until we have an interface to allocate
* memory for DMA which doesn't require a DMA handle. ddi_iopb_alloc()
* is obsolete and we want more flexibility in controlling the DMA
* address constraints..
*/
return (NULL);
}
return (iobp);
}
/* ARGSUSED */
int
{
if (iobp) {
}
}
return (DDI_SUCCESS);
}
/* ARGSUSED */
{
}
{
int err;
return (NULL);
}
/* call flow control */
dadkp->dad_cmd_count++;
if (err)
return (NULL);
}
static void
{
return;
}
static int
{
if (GDA_BP_PKT(bp))
return (DDI_SUCCESS);
if (!pktp)
return (DDI_FAILURE);
}
/*
* Read, Write preparation
*/
static int
{
else
/* setup the bad block list handle */
}
static int
{
int seccnt;
if (pktp->cp_secleft) {
} else {
/* get the first cookie from the bad block list */
if (!pktp->cp_private) {
} else {
pktp->cp_private);
bbhckp);
bbhckp);
}
}
return (DDI_SUCCESS);
} else {
return (DDI_FAILURE);
}
}
static struct cmpkt *
{
arg);
if (pktp) {
}
return (pktp);
}
static void
dadk_restart(void *vpktp)
{
return;
}
static int
{
switch (action) {
case QUE_COMMAND:
return (JUST_RETURN);
}
CE_WARN, "transport of command fails\n");
} else
"exceeds maximum number of retries\n");
/*FALLTHROUGH*/
case COMMAND_DONE_ERROR:
/*
* Flag "unimplemented" responses for
* DCMD_FLUSH_CACHE as ENOTSUP
*/
} else {
}
}
/*FALLTHROUGH*/
case COMMAND_DONE:
default:
return (COMMAND_DONE);
}
}
static void
{
int action;
struct dadkio_rwcmd *rwcmdp;
return;
}
return;
}
else
if (action == JUST_RETURN)
return;
/*
* If we are panicking don't retry the command
* just fail it so we can go down completing all
* of the buffers.
*/
if (action != COMMAND_DONE) {
return;
}
}
static struct dadkio_derr dadk_errtab[] = {
};
static int
{
int err_blkno;
if (scb == DERR_SUCCESS) {
dep = (dadk_errstats_t *)
}
return (COMMAND_DONE);
}
} else
err_blkno = -1;
case GDA_RETRYABLE:
break;
case GDA_FATAL:
break;
default:
break;
}
switch (scb) {
case DERR_INVCDB:
case DERR_ILI:
case DERR_EOM:
case DERR_HW:
case DERR_ICRC:
break;
case DERR_AMNF:
case DERR_TKONF:
case DERR_DWF:
case DERR_BBK:
case DERR_UNC:
case DERR_HARD:
case DERR_MEDIUM:
case DERR_DATA_PROT:
case DERR_MISCOMP:
break;
case DERR_NOTREADY:
break;
case DERR_IDNF:
case DERR_UNIT_ATTN:
break;
case DERR_ILL:
case DERR_RESV:
break;
default:
break;
}
}
/* if attempting to read a sector from a cdrom audio disk */
return (COMMAND_DONE);
}
}
}
}
static void
{
int scb;
case DERR_AMNF:
case DERR_ABORT:
break;
case DERR_DWF:
case DERR_IDNF:
break;
case DERR_TKONF:
case DERR_UNC:
case DERR_BBK:
break;
case DERR_BUSY:
break;
case DERR_INVCDB:
case DERR_HARD:
break;
case DERR_ICRC:
default:
}
return;
}
/*ARGSUSED*/
static void
{
dadkp->dad_cmd_count--;
}
static void
{
/* check for all iodone */
/* transport the next one */
return;
return;
}
/* start next one */
/* free pkt */
if (pktp->cp_private)
dadkp->dad_cmd_count--;
}
int
{
return (ENXIO);
}
#ifdef DADK_DEBUG
if (dadk_debug & DSTATE)
PRF("dadk_check_media: user state %x disk state %x\n",
#endif
/*
* If state already changed just return
*/
return (0);
}
/*
* Startup polling on thread state
*/
if (dadkp->dad_thread_cnt == 0) {
/*
* One thread per removable dadk device
*/
}
dadkp->dad_thread_cnt++;
/*
* Wait for state to change
*/
do {
dadkp->dad_thread_cnt--;
return (EINTR);
}
dadkp->dad_thread_cnt--;
return (0);
}
#define MEDIA_ACCESS_DELAY 2000000
static void
{
enum dkio_state state;
int interval;
do {
DADK_SILENT)) {
/*
* Assume state remained the same
*/
}
/*
* now signal the waiting thread if this is *not* the
* specified state;
* delay the signal if the state is DKIO_INSERTED
* to allow the target to recover
*/
if (state == DKIO_INSERTED) {
/*
* delay the signal to give the drive a chance
* to do what it apparently needs to do
*/
(void) timeout((void(*)(void *))cv_broadcast,
(void *)&dadkp->dad_state_cv,
} else {
}
}
} while (dadkp->dad_thread_cnt);
}
int
{
return (DDI_SUCCESS);
}
return (DDI_FAILURE);
}
static int
{
int err;
return (ENOMEM);
}
if (!pktp) {
return (ENOMEM);
}
return (err);
}
static void
{
/* Start next one */
dadkp->dad_cmd_count--;
}
static int
{
int status;
/* Let physio do the rest... */
return (status);
}
/* Do not let a user gendisk request get too big or */
/* else we could use to many resources. */
static void
{
}
static int
{
bp);
return (0);
}
static void
{
if (!pktp) {
return;
}
dadkp->dad_cmd_count++;
}
/*
* There is no existing way to notify cmdk module
* when the command completed, so add this function
* to calculate how many on-going commands.
*/
int
{
int count;
return (count);
}
/*
* this function was used to calc the cmd for CTL_IOCTL
*/
static int
{
int error;
dadkp->dad_cmd_count++;
dadkp->dad_cmd_count--;
return (error);
}