/*
* 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
*/
/*
*/
/*
* Direct Attached disk driver for SPARC machines.
*/
/*
* Includes, Declarations and Local Data
*/
/*
* Global Error Levels for Error Reporting
*/
/*
* Local Static Data
*/
#ifndef lint
#endif
/*
* Local Function Prototypes
*/
char *, caddr_t, int *);
/* Function prototypes for cmlb */
void *tg_cookie);
};
/*
* Error and Logging Functions
*/
#ifndef lint
static void dcdrestart(void *arg);
#endif /* lint */
/*
*/
static int dcd_create_errstats(struct dcd_disk *, int);
/*PRINTFLIKE4*/
__KPRINTFLIKE(4);
/*
* Configuration Routines
*/
void **result);
static void *dcd_state;
static int dcd_max_instance;
#define DCDDEBUG
#endif
int dcd_test_flag = 0;
/*
* Debugging macros
*/
#ifdef DCDDEBUG
static int dcddebug = 0;
#else /* DCDDEBUG */
#define dcddebug (0)
#define DEBUGGING (0)
#endif
/*
* we use pkt_private area for storing bp and retry_count
* XXX: Really is this usefull.
*/
struct dcd_pkt_private {
short dcdpp_retry_count;
short dcdpp_victim_retry_count;
};
= n
+= n
/*
* Urk!
*/
kstat_function(IOSP); \
} \
if (IO_PARTITION_STATS) { \
} \
}
if (un->un_errstats) { \
struct dcd_errstats *dtp; \
}
\
return (ENXIO);
/*
* After the following number of sectors, the cylinder number spills over
* 0xFFFF if sectors = 63 and heads = 16.
*/
/*
* Configuration Data
*/
/*
* Device driver ops vector
*/
dcdopen, /* open */
dcdclose, /* close */
dcdstrategy, /* strategy */
nodev, /* print */
dcddump, /* dump */
dcdread, /* read */
dcdwrite, /* write */
dcdioctl, /* ioctl */
nodev, /* devmap */
nodev, /* mmap */
nodev, /* segmap */
nochpoll, /* poll */
dcd_prop_op, /* cb_prop_op */
0, /* streamtab */
CB_REV, /* cb_rev */
dcdaread, /* async I/O read entry point */
dcdawrite /* async I/O write entry point */
};
DEVO_REV, /* devo_rev, */
0, /* refcnt */
dcdinfo, /* info */
nulldev, /* identify */
dcdprobe, /* probe */
dcdattach, /* attach */
dcddetach, /* detach */
dcdreset, /* reset */
&dcd_cb_ops, /* driver operations */
(struct bus_ops *)0, /* bus operations */
dcdpower, /* power */
ddi_quiesce_not_supported, /* devo_quiesce */
};
/*
* This is the loadable module wrapper.
*/
&mod_driverops, /* Type of module. This one is a driver */
"DAD Disk Driver", /* Name of the module. */
&dcd_ops, /* driver ops */
};
};
/*
* the dcd_attach_mutex only protects dcd_max_instance in multi-threaded
* attach situations
*/
int
_init(void)
{
int e;
DCD_MAXUNIT)) != 0)
return (e);
e = mod_install(&modlinkage);
if (e != 0) {
return (e);
}
return (e);
}
int
_fini(void)
{
int e;
if ((e = mod_remove(&modlinkage)) != 0)
return (e);
return (e);
}
int
{
}
static int
{
int instance;
/*
* Keep a count of how many disks (ie. highest instance no) we have
* XXX currently not used but maybe useful later again
*/
if (instance > dcd_max_instance)
return (DDI_PROBE_PARTIAL);
/*
* Turn around and call utility probe routine
* to see whether we actually have a disk at
*/
default:
case DCDPROBE_NORESP:
case DCDPROBE_NONCCS:
case DCDPROBE_NOMEM:
case DCDPROBE_FAILURE:
case DCDPROBE_BUSY:
break;
case DCDPROBE_EXISTS:
/*
* Check whether it is a ATA device and then
* return SUCCESS.
*/
} else
} else {
}
break;
}
"dcdprobe returns %x\n", rval);
return (rval);
}
/*ARGSUSED*/
static int
{
char *pm_comp[] =
{ "NAME=ide-disk", "0=standby", "1=idle", "2=active" };
/* CONSTCOND */
switch (cmd) {
case DDI_ATTACH:
break;
case DDI_RESUME:
return (DDI_FAILURE);
/*
* Restore the state which was saved to give the
* the right state in un_last_state
*/
/*
* Raise the power level of the device to active.
*/
/*
* start unit - if this is a low-activity device
* commands in queue will have to wait until new
* commands come in, which may take awhile.
* Also, we specifically don't check un_ncmds
* because we know that there really are no
* commands in progress after the unit was suspended
* and we could have reached the throttle level, been
* suspended, and have no new commands coming in for
* awhile. Highly unlikely, but so is the low-
* activity disk scenario.
*/
}
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
return (DDI_FAILURE);
}
return (DDI_FAILURE);
}
/*
* Add a zero-length attribute to tell the world we support
* kernel ioctls (for layered drivers)
*/
DDI_KERNEL_IOCTL, NULL, 0);
/*
* Since the dad device does not have the 'reg' property,
* cpr will not call its DDI_SUSPEND/DDI_RESUME entries.
* The following code is to tell cpr that this device
* does need to be suspended and resumed.
*/
/*
* Initialize power management bookkeeping;
* Create components - In IDE case there are 3 levels and one
* component. The levels being - active, idle, standby.
*/
if (rval == DDI_PROP_SUCCESS) {
/*
* Ignore the return value of pm_raise_power
* Even if we check the return values and
* remove the property created above, PM
* framework will not honour the change after
* first call to pm_raise_power. Hence, the
* removal of that property does not help if
* pm_raise_power fails.
*/
}
if (cmlb_attach(devi,
0,
0) != 0) {
return (DDI_FAILURE);
}
(void) dcd_validate_geometry(un);
/* Get devid; create a devid ONLY IF could not get ID */
/* Create the fab'd devid */
(void) dcd_create_devid(un);
}
return (DDI_SUCCESS);
}
static void
{
if (un) {
/*
* Deallocate command packet resources.
*/
}
/*
* Unregister the devid and free devid resources allocated
*/
}
/*
* Delete kstats. Kstats for non CD devices are deleted
* in dcdclose.
*/
}
}
/*
* Cleanup scsi_device resources.
*/
/* unprobe scsi device */
/* Remove properties created during attach */
}
static int
{
int instance;
return (DDI_FAILURE);
switch (cmd) {
case DDI_DETACH:
return (dcd_dr_detach(devi));
case DDI_SUSPEND:
return (DDI_SUCCESS);
}
un->un_throttle = 0;
/*
* Save the last state first
*/
/*
* wait till current operation completed. If we are
* in the resource wait state (with an intr outstanding)
* then we need to wait till the intr completes and
* starts the next cmd. We wait for
* DCD_WAIT_CMDS_COMPLETE seconds before failing the
* DDI_SUSPEND.
*/
/*
* commands Didn't finish in the
* specified time, fail the DDI_SUSPEND.
*/
DCD_DEBUG, "dcddetach: SUSPEND "
"failed due to outstanding cmds\n");
return (DDI_FAILURE);
}
}
return (DDI_SUCCESS);
}
return (DDI_FAILURE);
}
/*
* The reset entry point gets invoked at the system shutdown time or through
* CPR code at system suspend.
* Will be flushing the cache and expect this to be last I/O operation to the
*/
/*ARGSUSED*/
static int
{
int instance;
return (DDI_FAILURE);
return (DDI_SUCCESS);
}
static int
{
/*
* Get scsi_device structure for this instance.
*/
return (DDI_FAILURE);
/*
* Get dcd_disk structure containing target 'private' information
*/
/*
* Verify there are NO outstanding commands issued to this device.
* ie, un_ncmds == 0.
* It's possible to have outstanding commands through the physio
* code path, even though everything's closed.
*/
#ifndef lint
#endif
return (DDI_FAILURE);
}
/*
* Lower the power state of the device
* i.e. the minimum power consumption state - sleep.
*/
/*
* at this point there are no competing threads anymore
* release active MT locks and all device resources.
*/
return (DDI_SUCCESS);
}
static int
{
int instance;
component != 0) {
return (DDI_FAILURE);
}
/*
* if there are active commands for the device or device will be
* active soon. At the same time there is request to lower power
* return failure.
*/
return (DDI_FAILURE);
}
return (DDI_FAILURE);
}
if (level == DCD_DEVICE_ACTIVE) {
/*
* No need to fire any command, just set the state structure
* to indicate previous state and set the level to active
*/
} else {
return (DDI_FAILURE);
}
switch (level) {
case DCD_DEVICE_IDLE:
break;
case DCD_DEVICE_STANDBY:
break;
}
/*
* Issue the appropriate command
*/
return (DDI_FAILURE);
}
}
return (DDI_SUCCESS);
}
static int
{
int instance;
int rval;
int options;
int target;
/*
* Call the routine scsi_probe to do some of the dirty work.
* If the INQUIRY command succeeds, the field dcd_inq in the
* device structure will be filled in. The dcd_sense structure
* will also be allocated.
*/
default:
return (DDI_FAILURE);
case DCDPROBE_EXISTS:
rval = DDI_SUCCESS;
} else {
rval = DDI_FAILURE;
goto error;
}
} else {
rval = DDI_FAILURE;
goto error;
}
}
rval = DDI_FAILURE;
goto error;
}
rval = DDI_FAILURE;
goto error;
}
/* Initialize power management conditional variable */
/*
* Assume CCS drive, assume parity, but call
* it a CDROM if it is a RODIRECT device.
*/
rval = DDI_FAILURE;
goto error;
}
}
} else {
rval = DDI_FAILURE;
goto error;
}
}
/*
* Allow I/O requests at un_secsize offset in multiple of un_secsize.
*/
/*
* If the device is not a removable media device, make sure that
* that the device is ready, by issuing the another identify but
* not needed. Get the capacity from identify data and store here.
*/
}
capacity);
"dcdprobe: drive selected\n");
/*
* Check for the property target<n>-dcd-options to find the option
* set by the HBA driver for this target so that we can set the
* Unit structure variable so that we can send commands accordingly.
*/
prop_str, -1);
if (options < 0) {
"No per target properties");
} else {
} else {
if (options & DCD_BLOCK_MODE)
}
}
/*
* set default max_xfer_size - This should depend on whether the
* Block mode is supported by the device or not.
*/
/*
* Set write cache enable softstate
*
* WCE is only supported in ATAPI-4 or higher; for
* lower rev devices, must assume write cache is
* enabled.
*/
"dcd_doattach returns good\n");
return (rval);
return (rval);
}
#ifdef NOTNEEDED
/*
* This routine is used to set the block mode of operation by issuing the
* Set Block mode ata command with the maximum block mode possible
*/
{
int status;
/* Zero all the required structure */
/*
* Here we should pass what needs to go into sector count REGISTER.
* Eventhough this field indicates the number of bytes to read we
* need to specify the block factor in terms of bytes so that it
* will be programmed by the HBA driver into the sector count register.
*/
ucmd.udcd_flags = 0;
ucmd.udcd_buflen = 0;
return (status);
}
/*
* The following routine is used only for setting the transfer mode
* and it is not designed for transferring any other features subcommand.
*/
{
int status;
/* Zero all the required structure */
/*
* Here we need to pass what needs to go into the sector count register
* But in the case of SET FEATURES command the value taken in the
* sector count register depends what type of subcommand is
* passed in the features register. Since we have defined the size to
* be the size in bytes in this context it does not indicate bytes
* instead it indicates the mode to be programmed.
*/
"size %x, features %x, cmd %x\n",
ucmd.udcd_flags = 0;
ucmd.udcd_buflen = 0;
return (status);
}
#endif
/*
* Validate the geometry for this disk, e.g.,
* see whether it has a valid label.
*/
static int
{
int secsize = 0;
int secdiv;
int rval;
"dcd_validate_geometry: started \n");
if (un->un_lbasize < 0) {
return (DCD_BAD_LABEL);
}
DDI_SUCCESS) {
return (DCD_BAD_LABEL);
}
}
/*
* take a log base 2 of sector size (sorry)
*/
;
/*
* Only DIRECT ACCESS devices will have Sun labels.
* CD's supposedly have a Sun label, too
*/
return (DCD_NO_MEM_FOR_LABEL);
else if (rval != 0)
return (DCD_BAD_LABEL);
} else {
/* it should never get here. */
return (DCD_BAD_LABEL);
}
/*
* take a log base 2 of logical block size
*/
;
/*
* take a log base 2 of the multiple of DEV_BSIZE blocks that
* make up one logical block
*/
;
return (0);
}
/*
* Unix Entry Points
*/
/* ARGSUSED3 */
static int
{
int partmask;
int i;
char *partname;
return (EINVAL);
}
/*
* We use a semaphore here in order to serialize
* open and close requests on the device.
*/
goto done;
}
}
!= DDI_SUCCESS) {
goto done;
}
}
/*
* set make_dcd_cmd() flags and stat_size here since these
* are unlikely to change
*/
un->un_cmd_flags = 0;
(void *)un);
/*
* check for previous exclusive open
*/
"exclopen=%x, flag=%x, regopen=%x\n",
"Exclusive open flag %x, partmask %x\n",
"exclusive open fails\n");
goto done;
}
int i;
goto failed_exclusive;
}
for (i = 0; i < (OTYPCNT - 1); i++) {
goto failed_exclusive;
}
}
}
if (dcd_check_wp(dev)) {
return (EROFS);
}
}
"Check Write Protect handled\n");
if (!nodelay) {
}
(void) pm_idle_component(DCD_DEVINFO, 0);
/*
* Fail if device is not ready or if the number of disk
* blocks is zero or negative for non CD devices.
*/
lblocks <= 0) {
goto done;
}
}
} else {
}
/*
* set up open and exclusive open flags
*/
}
"open of part %d type %d\n",
"Kstats getting updated\n");
/*
* only create kstats for disks, CD kstats created in dcdattach
*/
}
/*
* set up partition statistics for each partition
* with number of blocks > 0
*/
if (!nodelay) {
for (i = 0; i < NDKMAP; i++) {
"partition",
1,
}
}
}
}
/*
* set up error kstats
*/
}
#ifndef lint
#endif
return (0);
done:
return (rval);
}
/*
* Test if disk is ready and has a valid geometry.
*/
static int
{
int g_error = 0;
/*
* cmds outstanding
*/
(void) dcd_unit_ready(dev);
}
/*
* If device is not yet ready here, inform it is offline
*/
goto done;
}
}
if (un->un_format_in_progress == 0) {
}
/*
* check if geometry was valid. We don't check the validity of
* geometry for CDROMS.
*/
if (g_error == DCD_BAD_LABEL) {
rval = 1;
goto done;
}
/*
* the state has changed; inform the media watch routines
*/
rval = 0;
done:
return (rval);
}
/*ARGSUSED*/
static int
{
int i;
return (ENXIO);
"close of part %d type %d\n",
}
} else {
}
break;
}
cp++;
}
}
}
for (i = 0; i < NDKMAP; i++) {
}
}
if (un->un_errstats) {
}
#ifndef lint
#endif
}
return (0);
}
static void
{
if (bechatty)
}
/*
* Given the device number return the devinfo pointer
* from the scsi_device structure.
*/
/*ARGSUSED*/
static int
{
switch (infocmd) {
case DDI_INFO_DEVT2DEVINFO:
return (DDI_FAILURE);
*result = (void *) DCD_DEVINFO;
error = DDI_SUCCESS;
break;
case DDI_INFO_DEVT2INSTANCE:
error = DDI_SUCCESS;
break;
default:
error = DDI_FAILURE;
}
return (error);
}
/*
* property operation routine. return the number of blocks for the partition
* in question or forward the request to the propery facilities.
*/
static int
{
}
/*
* These routines perform raw i/o operations.
*/
/*ARGSUSED*/
void
{
}
static void
{
int instance;
}
/* ARGSUSED2 */
static int
{
int secmask;
#ifdef lint
#endif /* lint */
"file offset not modulo %d\n",
un->un_secsize);
return (EINVAL);
return (EINVAL);
}
}
/* ARGSUSED2 */
static int
{
int secmask;
#ifdef lint
#endif /* lint */
"file offset not modulo %d\n",
un->un_secsize);
return (EINVAL);
return (EINVAL);
}
}
/* ARGSUSED2 */
static int
{
int secmask;
#ifdef lint
#endif /* lint */
"file offset not modulo %d\n",
un->un_secsize);
return (EINVAL);
return (EINVAL);
}
uio));
}
/* ARGSUSED2 */
static int
{
int secmask;
#ifdef lint
#endif /* lint */
"file offset not modulo %d\n",
un->un_secsize);
return (EINVAL);
return (EINVAL);
}
}
/*
* strategy routine
*/
static int
{
int i;
return (0);
}
/*
* If the request size (buf->b_bcount)is greater than the size
* (un->un_max_xfer_size) supported by the target driver fail
* the request with EINVAL error code.
*
* We are not supposed to receive requests exceeding
* un->un_max_xfer_size size because the caller is expected to
* check what is the maximum size that is supported by this
* driver either through ioctl or dcdmin routine(which is private
* to this driver).
* But we have seen cases (like meta driver(md))where dcdstrategy
* called with more than supported size and cause data corruption.
*/
goto error;
}
/*
* Commands may sneak in while we released the mutex in
* DDI_SUSPEND, we should block new commands.
*/
}
(void) pm_idle_component(DCD_DEVINFO, 0);
if (pm_raise_power(DCD_DEVINFO, 0,
DCD_DEVICE_ACTIVE) != DDI_SUCCESS) {
goto error;
}
}
/*
* Map-in the buffer in case starting address is not word aligned.
*/
&lblocks,
NULL,
NULL,
0) == 0) {
"dkblock(bp) is %llu\n", bn);
i = 0;
if (bn < 0) {
i = -1;
/*
* For proper comparison, file system block
* number has to be scaled to actual CD
* transfer size.
* Since all the CDROM operations
* that have Sun Labels are in the correct
* block size this will work for CD's. This
* will have to change when we have different
* sector sizes.
*
* if bn == lblocks,
* Not an error, resid == count
*/
i = -1;
} else {
i = 1;
}
/*
* This should really be:
*
* ... if (bp->b_bcount & (un->un_lbasize-1))
*
*/
i = -1;
} else {
printf("Waring : Zero read or Write\n");
goto error;
}
/*
* sort by absolute block number.
*/
/*
* zero out av_back - this will be a signal
* to dcdstart to go and fetch the resources
*/
}
/*
* Check to see whether or not we are done
* (with or without errors).
*/
if (i != 0) {
if (i < 0) {
}
goto error;
}
} else {
/*
* Check if disk is ready and has a valid geometry
*/
goto validated;
} else {
"i/o to invalid geometry\n");
goto error;
}
}
} else if (BP_HAS_NO_PKT(bp)) {
/*
* This indicates that it is a special buffer
* This could be a udcd-cmd and hence call bp_mapin just
* in case that it could be a PIO command issued.
*/
}
}
/*
* We are doing it a bit non-standard. That is, the
* head of the b_actf chain is *not* the active command-
* it is just the head of the wait queue. The reason
* we do this is that the head of the b_actf chain is
* guaranteed to not be moved by disksort(), so that
* our restart command (pointed to by
* b_forw) and the head of the wait queue (b_actf) can
* have resources granted without it getting lost in
* the queue at some later point (where we would have
* to go and look for it).
*/
} else {
"dcdstrategy_disksort_start: dp 0x%p bp 0x%p un 0x%p",
"dcdstrategy_disksort_end");
}
"ncmd %x , throttle %x, forw 0x%p\n",
/*
* try and map this one
*/
"dcdstrategy_small_window_call (begin)");
"dcdstrategy_small_window_call (end)");
/*
* there is a small window where the active cmd
* completes before make_dcd_cmd returns.
* consequently, this cmd never gets started so
* we start it from here
*/
}
}
done:
return (0);
}
/*
* Unit start and Completion
* NOTE: we assume that the caller has at least checked for:
* (un->un_ncmds < un->un_throttle)
* if not, there is no real harm done, dcd_transport() will
* return BUSY
*/
static void
{
"dcdstart_end (no work)");
return;
}
/*
* remove from active queue
*/
/*
* increment ncmds before calling dcd_transport because dcdintr
* may be called before we return from dcd_transport!
*/
/*
* If measuring stats, mark exit from wait queue and
* entrance into run 'queue' if and only if we are
* going to actually start a command.
* Normally the bp already has a packet at this point
*/
if (BP_HAS_NO_PKT(bp)) {
"dcdstart_end (No Resources)");
goto done;
}
/*
* restore old state
*/
}
goto retry;
} else {
goto done;
}
}
}
/*
* Restore resid from the packet, b_resid had been the
* disksort key.
*/
"bp->b_resid %lx, pkt_resid %lx\n",
/*
* We used to check whether or not to try and link commands here.
* Since we have found that there is no performance improvement
* for linked commands, this has not made much sense.
*/
!= TRAN_ACCEPT) {
"transport returned %x\n", status);
}
} else {
"transport rejected (%d)\n",
status);
}
}
goto retry;
}
}
} else {
/*
* try and map this one
*/
"dcdstart_small_window_start");
"dcdstart_small_window_end");
/*
* there is a small window where the active cmd
* completes before make_dcd_cmd returns.
* consequently, this cmd never gets started so
* we start it from here
*/
goto retry;
}
}
}
done:
}
/*
* make_dcd_cmd: create a pkt
*/
static void
{
long secnt;
long resid;
&lblocks,
NULL,
NULL,
0) != NULL) {
lblocks = 0;
p_lblksrt = 0;
}
/*
* Make sure we don't run off the end of a partition.
*
* Put this test here so that we can adjust b_count
* to accurately reflect the actual amount we are
* goint to transfer.
*/
/*
* First, compute partition-relative block number
*/
/*
* We have an overrun
*/
"overrun by %ld sectors\n",
} else {
resid = 0;
}
/*
* Adjust block number to absolute
*/
/*
* This is for devices having block size different from
* from DEV_BSIZE (e.g. 2K CDROMs).
*/
}
"make_dcd_cmd_init_pkt_call (begin)");
"make_dcd_cmd_init_pkt_call (end): pkt 0x%p", pkt);
if (!pkt) {
"make_dcd_cmd_end (NO_PKT_ALLOCATED1)");
return;
}
com = ATA_READ_DMA;
} else {
else
}
} else {
com = ATA_WRITE_DMA;
} else {
else
}
}
/*
* Save the resid in the packet, temporarily until
* we transport the command.
*/
tval = dcd_io_time;
} else {
/*
* set options
*/
flags |= FLAG_SILENT;
}
flags |= FLAG_DIAGNOSE;
flags |= FLAG_NOINTR;
if (!pkt) {
return;
}
if (scmd->udcd_timeout == 0)
tval = dcd_io_time;
else
/* UDAD interface should be decided. */
"udcd interface\n");
}
}
/*
* Command completion processing
*/
static void
{
int action;
int status;
/*
* do most common case first
*/
(const char *) diskokay);
}
/*
* If the command is a read or a write, and we have
* a non-zero pkt_resid, that is an error. We should
* attempt to retry the operation if possible.
*/
} else {
/*
* if we have exhausted retries
* a command with a residual is in error in
* this case.
*/
}
CE_WARN, "incomplete %s- %s\n",
"giving up");
}
/*
* pkt_resid will reflect, at this point, a residual
* of how many bytes left to be transferred there were
* from the actual scsi command. Add this to b_resid i.e
* the amount this driver could not see to transfer,
* to get the total number of bytes not transfered.
*/
if (action != QUE_COMMAND) {
}
}
/*
* If we are in the middle of syncing or dumping, we have got
* here because dcd_transport has called us explictly after
* completing the command in a polled mode. We don't want to
* have a recursive call into dcd_transport again.
*/
}
/*
* save pkt reason; consecutive failures are not reported unless
* fatal
* do not reset last_pkt_reason when the cmd was retried and
* succeeded because
* there maybe more commands comming back with last_pkt_reason
*/
(PKT_GET_RETRY_CNT(pkt) == 0))) {
}
switch (action) {
case COMMAND_DONE_ERROR:
}
} else {
}
}
/*FALLTHROUGH*/
case COMMAND_DONE:
"dcdintr_end (COMMAND_DONE)");
return;
case QUE_COMMAND:
goto exit;
}
/* reset the pkt reason again */
pkt->pkt_reason = 0;
goto exit;
}
"requeue of command fails (%x)\n", status);
goto exit;
}
break;
case JUST_RETURN:
default:
break;
}
exit:
}
/*
* Done with a command.
*/
static void
{
}
} else {
}
}
if (IO_PARTITION_STATS) {
IOSP_PARTITION->reads++;
} else {
IOSP_PARTITION->writes++;
}
}
/*
* Start the next one before releasing resources on this one
*/
}
} else {
}
(void) pm_idle_component(DCD_DEVINFO, 0);
}
/*
* reset the disk unless the transport layer has already
* cleared the problem
*/
static void
{
"Reset failed");
}
}
}
static int
{
switch (pkt->pkt_reason) {
case CMD_TIMEOUT:
/*
* This Indicates the already the HBA would have reset
* so Just indicate to retry the command
*/
break;
case CMD_INCOMPLETE:
if (action == COMMAND_HARD_ERROR) {
}
break;
case CMD_FATAL:
/*
* Something drastic has gone wrong
*/
break;
case CMD_DMA_DERR:
case CMD_DATA_OVR:
/* FALLTHROUGH */
default:
/*
* the target may still be running the command,
* so we should try and reset that target.
*/
}
break;
}
/*
* The disk that caused it, should get CMD_TIMEOUT with pkt_statistics
*/
/* To be written : XXX */
"Command aborted\n");
}
} else {
if ((rval == COMMAND_DONE_ERROR) &&
(action == COMMAND_SOFT_ERROR) &&
rval = QUE_COMMAND;
}
}
/*
* Looks like someone turned off this shoebox.
*/
(const char *) notresp);
}
/*
* Suppressing the following message for the time being
* dcd_log(DCD_DEVINFO, dcd_label, CE_WARN,
* (const char *) notresp);
*/
} else if (be_chatty) {
(rval == COMMAND_DONE_ERROR) ||
(dcd_error_level == DCD_ERR_ALL)) {
(rval == COMMAND_DONE_ERROR) ?
"giving up": "retrying command");
"retrycount=%x\n",
}
}
}
return (rval);
}
static int
{
int rval = 0;
unsigned char status;
unsigned char error;
/*
* Here we need to check status first and then if error is indicated
* Then the error register.
*/
/*
* There has been a Device Fault - reason for such error
* is vendor specific
* Action to be taken is - Indicate error and reset device.
*/
/*
* The sector read or written is marginal and hence ECC
* Correction has been applied. Indicate to repair
* Here we need to probably re-assign based on the badblock
* mapping.
*/
"Soft Error on block %x\n",
"Command:0x%x,Error:0x%x,Status:0x%x\n",
/* Address make not found */
"Address Mark Not Found");
/* Track 0 Not found */
"Track 0 Not found \n");
" ID not found \n");
"Uncorrectable data Error: Block %x\n",
"Bad block detected: Block %x\n",
/* Aborted Command */
" Aborted Command \n");
}
/*
* Return the soft error so that the command
* will be retried.
*/
}
return (rval);
}
/*
* System Crash Dump routine
*/
static int
{
int i;
int err;
unsigned char com;
#ifdef lint
#endif /* lint */
return (ENXIO);
return (ENXIO);
return (EINVAL);
}
if (pm_raise_power(DCD_DEVINFO, 0,
DCD_DEVICE_ACTIVE) != DDI_SUCCESS) {
return (EIO);
}
}
/*
* When cpr calls dcddump, we know that dad is in a
* a good state, so no bus reset is required
*/
un->un_throttle = 0;
/*
* Reset the bus. I'd like to not have to do this,
* but this is the safest thing to do...
*/
return (EIO);
}
}
/*
* It should be safe to call the allocator here without
* worrying about being locked for DVMA mapping because
* the address we're passed is already a DVMA mapping
*
* We are also not going to worry about semaphore ownership
* in the dump buffer. Dumping is single threaded at present.
*/
for (i = 0; i < NDUMP_RETRIES; i++) {
break;
}
if (i == 0) {
"no resources for dumping; "
"error code: 0x%x, retrying",
} else {
"no resources for dumping; retrying");
}
} else if (i != (NDUMP_RETRIES - 1)) {
"resources for dumping; error code: 0x%x, "
}
} else {
"no resources for dumping; "
"error code: 0x%x, retries failed, "
} else {
"no resources for dumping; "
"retries failed, giving up.\n");
}
return (EIO);
}
delay(10);
}
com = ATA_WRITE_DMA;
} else {
else
}
case STATUS_GOOD:
err = 0;
}
break;
case STATUS_ATA_BUSY:
break;
default:
break;
}
} else if (i > NDUMP_RETRIES/2) {
}
}
return (err);
}
/*
* This routine implements the ioctl calls. It is called
* from the device switch at normal priority.
*/
/* ARGSUSED3 */
static int
{
int i, err;
#ifdef _MULTI_DATAMODEL
#endif
#ifdef lint
#endif /* lint */
switch (cmd) {
#ifdef DCDDEBUG
/*
*/
case DKIOCRESET:
return (EFAULT);
"DKIOCRESET: data = 0x%x\n", data[0]);
return (0);
} else {
return (EIO);
}
case DKIOCABORT:
"DKIOCABORT:\n");
return (0);
} else {
return (EIO);
}
#endif
case DKIOCINFO:
/*
* Controller Information
*/
default:
break;
}
/*
* Unit Information
*/
/*
* Max Transfer size of this device in blocks
*/
/*
* We can't get from here to there yet
*/
i = sizeof (struct dk_cinfo);
return (EFAULT);
else
return (0);
case DKIOCGMEDIAINFO:
/*
* As dad target driver is used for IDE disks only
* Can keep the return value hardcoded to FIXED_DISK
*/
return (EFAULT);
else
return (0);
case DKIOCGGEOM:
case DKIOCGVTOC:
case DKIOCGETEFI:
return (err);
}
}
return (err);
case DKIOCGAPART:
case DKIOCSAPART:
case DKIOCSGEOM:
case DKIOCSVTOC:
case DKIOCSETEFI:
case DKIOCPARTITION:
case DKIOCPARTINFO:
case DKIOCGMBOOT:
case DKIOCSMBOOT:
return (err);
case DIOCTL_RWCMD:
return (EPERM);
}
#ifdef _MULTI_DATAMODEL
case DDI_MODEL_NONE:
sizeof (struct dadkio_rwcmd), flag)) {
return (EFAULT);
}
break;
case DDI_MODEL_ILP32:
sizeof (struct dadkio_rwcmd32), flag)) {
return (EFAULT);
}
break;
}
#else
sizeof (struct dadkio_rwcmd32), flag)) {
return (EFAULT);
}
#endif
/*
* Convert the dadkio_rwcmd structure to udcd_cmd so that
* it can take the normal path to get the io done
*/
else
else
} else {
return (EINVAL);
}
scmd->udcd_status = 0;
scmd->udcd_error_reg = 0;
scmd->udcd_status_reg = 0;
/*
* After return convert the status from scmd to
* dadkio_status
*/
#ifdef _MULTI_DATAMODEL
case DDI_MODEL_NONE: {
int counter;
for (counter = 0;
}
/* Copy out the result back to the user program */
sizeof (struct dadkio_rwcmd), flag)) {
if (i != 0) {
i = EFAULT;
}
}
break;
case DDI_MODEL_ILP32:
/* Copy out the result back to the user program */
sizeof (struct dadkio_rwcmd32), flag)) {
if (i != 0) {
i = EFAULT;
}
}
break;
}
#else
/* Copy out the result back to the user program */
sizeof (struct dadkio_rwcmd32), flag)) {
if (i != 0)
i = EFAULT;
}
#endif
return (i);
case UDCDCMD: {
#ifdef _MULTI_DATAMODEL
/*
* For use when a 32 bit app makes a call into a
* 64 bit ioctl
*/
#endif /* _MULTI_DATAMODEL */
return (EPERM);
}
#ifdef _MULTI_DATAMODEL
case DDI_MODEL_ILP32:
sizeof (struct udcd_cmd32), flag)) {
return (EFAULT);
}
/*
* Convert the ILP32 uscsi data from the
* application to LP64 for internal use.
*/
break;
case DDI_MODEL_NONE:
flag)) {
return (EFAULT);
}
break;
}
#else /* ! _MULTI_DATAMODEL */
return (EFAULT);
}
#endif /* ! _MULTI_DATAMODEL */
#ifdef _MULTI_DATAMODEL
switch (model) {
case DDI_MODEL_ILP32:
/*
* Convert back to ILP32 before copyout to the
* application
*/
if (i != 0)
i = EFAULT;
}
break;
case DDI_MODEL_NONE:
flag)) {
if (i != 0)
i = EFAULT;
}
break;
}
#else /* ! _MULTI_DATAMODE */
if (i != 0)
i = EFAULT;
}
#endif
return (i);
}
case DKIOCFLUSHWRITECACHE: {
if (un->un_flush_not_supported ||
! un->un_write_cache_enabled) {
/*
* 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.
*/
/*
* Did callback and reported error.
* Since we did a callback, ioctl
* should return 0.
*/
i = 0;
}
return (i);
}
/*
* Get the special buffer
*/
while (un->un_sbuf_busy) {
}
is_sync = 0;
}
(void) dcdstrategy(bp);
i = 0;
if (is_sync) {
(void) dcdflushdone(bp);
}
return (i);
}
default:
break;
}
return (ENOTTY);
}
static int
{
}
/*
* Tell anybody who cares that the buffer is now free
*/
un->un_sbuf_busy = 0;
return (0);
}
/*
* dcdrunout:
* the callback function for resource allocation
*
* XXX it would be preferable that dcdrunout() scans the whole
* list for possible candidates for dcdstart(); this avoids
* that a bp at the head of the list whose request cannot be
* satisfied is retried again and again
*/
/*ARGSUSED*/
static int
{
int serviced;
arg);
serviced = 1;
/*
* We now support passing a structure to the callback
* routine.
*/
}
serviced = 0;
}
"dcdrunout_end: serviced %d", serviced);
return (serviced);
}
/*
* This routine called to see whether unit is (still) there. Must not
* be called when un->un_sbufp is in use, and must not be called with
* an unattached disk. Soft state of disk is restored to what it was
* upon entry- up to caller to set the correct state.
*
* We enter with the disk mutex held.
*/
/* ARGSUSED0 */
static int
{
#ifndef lint
#endif
int error;
#ifndef lint
#endif
/*
* Now that we protect the special buffer with
* a mutex, we could probably do a mutex_tryenter
* on it here and return failure if it were held...
*/
error = 0;
return (error);
}
/* ARGSUSED0 */
int
{
int flags = 0;
#ifdef lint
#endif
/*
* Is this a request to reset the bus?
* if so, we need to do reseting.
*/
return (err);
}
/* Do some sanity checks */
if (scmd->udcd_buflen <= 0) {
return (EINVAL);
} else {
scmd->udcd_buflen = 0;
}
}
/* Make a copy of the dcd_cmd passed */
if (cdbspace == UIO_SYSSPACE) {
}
flags)) {
return (EFAULT);
}
/*
* Get the special buffer
*/
while (un->un_sbuf_busy) {
return (EINTR);
}
}
/*
* If we are going to do actual I/O, let physio do all the
* things
*/
if (scmd->udcd_buflen) {
/*
* Let physio do the rest...
*/
} else {
/*
* We have to mimic what physio would do here.
*/
(void) dcdstrategy(bp);
}
done:
/* we need to update the completion status of udcd command */
/* XXX: we need to give error_reg also */
}
/*
* Tell anybody who cares that the buffer is now free
*/
un->un_sbuf_busy = 0;
return (err);
}
static void
{
#ifdef lint
#endif
}
/*
* restart a cmd from timeout() context
*
* the cmd is expected to be in un_utab.b_forw. If this pointer is non-zero
* a restart timeout request has been issued and no new timeouts should
* be requested. b_forw is reset when the cmd eventually completes in
* dcddone_and_mutex_exit()
*/
void
{
int status;
if (bp) {
}
if (bp) {
/* XXX : To be checked */
/*
* if (un->un_throttle > 1) {
* ASSERT(un->un_ncmds >= 0);
* un->un_throttle = un->un_ncmds;
* }
*/
DCD_BSY_TIMEOUT/500);
return;
}
"dcdrestart transport failed (%x)\n", status);
un->un_reissued_timeid = 0L;
return;
}
}
un->un_reissued_timeid = 0L;
}
/*
* This routine gets called to reset the throttle to its saved
* value wheneven we lower the throttle.
*/
void
{
/*
* start any commands that didn't start while throttling.
*/
}
}
/*
* This routine handles the case when a TRAN_BUSY is
* returned by HBA.
*
* If there are some commands already in the transport, the
* bp can be put back on queue and it will
* be retried when the queue is emptied after command
* completes. But if there is no command in the tranport
* and it still return busy, we have to retry the command
* after some time like 10ms.
*/
/* ARGSUSED0 */
static void
{
}
if (!un->un_reissued_timeid) {
}
}
static int
{
int status;
int i;
return (EINVAL);
}
/* Allocate the buffer */
/* Fill in the revision */
/* Copy in the device id */
/* Calculate the chksum */
chksum = 0;
for (i = 0; i < ((un->un_secsize - sizeof (int))/sizeof (int)); i++)
/* Fill in the checksum */
} else {
else
}
return (status);
}
static int
{
int status;
int chksum;
int i, sz;
return (EINVAL);
}
} else {
else
}
if (status != 0) {
return (status);
}
/* Validate the revision */
return (EINVAL);
}
/* Calculate the checksum */
chksum = 0;
for (i = 0; i < ((un->un_secsize - sizeof (int))/sizeof (int)); i++)
/* Compare the checksums */
return (EINVAL);
}
/* VAlidate the device id */
return (EINVAL);
}
/* return a copy of the device id */
return (0);
}
/*
* Return the device id for the device.
* 1. If the device ID exists then just return it - nothing to do in that case.
* 2. Build one from the drives model number and serial number.
* to read it from the acyl region of the disk.
* Note: If this function is unable to return a valid ID then the calling
* point will invoke the routine to create a fabricated ID ans stor it on the
* acyl region of the disk.
*/
static ddi_devid_t
{
int rc;
/* If already registered, return that value */
/* Build a devid from model and serial number, if present */
if (rc != DDI_SUCCESS) {
/* Read the devid from the disk. */
if (dcd_read_deviceid(un))
return (NULL);
}
}
static ddi_devid_t
{
return (NULL);
if (dcd_write_deviceid(un)) {
return (NULL);
}
}
/*
* Build a devid from the model and serial number, if present
* Return DDI_SUCCESS or DDI_FAILURE.
*/
static int
{
char *hwid;
char *model;
int model_len;
char *serno;
int serno_len;
int total_len;
/* initialize the model and serial number information */
/* Verify the model and serial number */
if (model_len == 0) {
rc = DDI_FAILURE;
goto out;
}
if (serno_len == 0) {
rc = DDI_FAILURE;
goto out;
}
/*
* The device ID will be concatenation of the model number,
* the '=' separator, the serial number. Allocate
* the string and concatenate the components.
*/
/* Initialize the device ID, trailing NULL not included */
/* Free the allocated string */
}
/*
* Test for a valid model or serial number. Assume that a valid representation
* contains at least one character that is neither a space, 0 digit, or NULL.
* Trim trailing blanks and NULLS from returned length.
*/
static void
{
char ch;
int i;
int tb;
tb++;
else
tb = 0;
}
/* Atleast one non 0 or blank character. */
} else {
*retlen = 0;
}
}
#ifndef lint
void
{
int i;
for (i = 0; i < len; i++) {
}
}
#endif /* Not lint */
#ifndef lint
/*
* Print a piece of inquiry data- cleaned up for non-printable characters
* and stopping at the first space character after the beginning of the
* passed string;
*/
void
inq_fill(char *p, int l, char *s)
{
unsigned i = 0;
char c;
while (i++ < l) {
if ((c = *p++) < ' ' || c >= 0177) {
c = '*';
} else if (i != 1 && c == ' ') {
break;
}
*s++ = c;
}
*s++ = 0;
}
#endif /* Not lint */
char *
{
switch (status & STATUS_ATA_MASK) {
case STATUS_GOOD:
return ("good status");
case STATUS_ATA_BUSY:
return ("busy");
default:
return ("<unknown status>");
}
}
/* ARGSUSED0 */
char *
{
static char *rnames[] = {
"cmplt",
"incomplete",
"dma_derr",
"tran_err",
"reset",
"aborted",
"timeout",
"data_ovr",
};
if (reason > CMD_DATA_OVR) {
return ("<unknown reason>");
} else {
}
}
/* ARGSUSED0 */
int
{
return (0);
}
/*
* Create device error kstats
*/
static int
{
"device_error", KSTAT_TYPE_NAMED,
sizeof (struct dcd_errstats)/ sizeof (kstat_named_t),
if (un->un_errstats) {
"Transport Errors", KSTAT_DATA_UINT32);
"Device Not Ready", KSTAT_DATA_UINT32);
"Illegal Request", KSTAT_DATA_UINT32);
}
}
return (0);
}
/*
* This has been moved from DADA layer as this does not do anything other than
* retrying the command when it is busy or it does not complete
*/
int
{
void (*savec)();
/*
* Save old flags
*/
/*
* Set the Pkt_comp to NULL
*/
/*
* Set the Pkt time for the polled command
*/
}
/* Now transport the command */
delay(100);
rval = 0;
break;
}
}
delay(100);
continue;
}
}
return (rval);
}
void
{
/*
* The error register is valid only when BSY and DRQ not set
* Assumed that HBA has checked this before it gives the data
*/
} else
}
static void
{
int retry_count;
return;
}
NO_DATA_XFER, 0);
/*
* Send the command. There are chances it might fail on some
* disks since it is not a mandatory command as per ata-4. Try
* 3 times if it fails. The retry count has been randomly selected.
* There is a need for retry since as per the spec FLUSH CACHE can fail
* as a result of unrecoverable error encountered during execution
* of writing data and subsequent command should continue flushing
* cache.
*/
/*
* Set the packet fields.
*/
break;
}
}
/*
* Note the wait time value of 100ms is same as in the
* dcd_poll routine.
*/
drv_usecwait(1000000);
}
(void) dcd_destroy_pkt(pkt);
}
static int
{
int i, rval = 0;
return (ENXIO);
if (!bp) {
"no bp for disk label\n");
return (ENOMEM);
}
if (!pkt) {
"no memory for disk label\n");
return (ENOMEM);
}
} else {
tmp = DATA_WRITE;
}
} else {
}
} else {
else
} else {
else
}
}
buffer_size, tmp, 0);
for (i = 0; i < 3; i++) {
"Status %x, state %x, resid %lx\n",
} else {
break;
}
}
if (rval != 0) {
return (EIO);
}
rval = 0;
}
return (rval);
}
{
if (no_of_lbasec > cap) {
cap = no_of_lbasec;
}
else
return (EINVAL);
return (0);
}
/*ARGSUSED5*/
static int
{
return (EINVAL);
}
static int
{
if (no_of_lbasec > capacity) {
if (capacity > NUM_SECTORS_32G) {
/*
* if the capacity is greater than 32G,
* then 255 is the sectors per track.
* This should be good until 128G disk
* capacity, which is the current ATA-4
* limitation.
*/
}
/*
* If the disk capacity is >= 128GB then no. of
* addressable sectors will be set to 0xfffffff
* in the IDENTIFY info. In that case set the
* no. of pcyl to the Max. 16bit value.
*/
calculated_cylinders = (capacity) /
if (calculated_cylinders >= USHRT_MAX) {
} else {
calculated_cylinders - 2;
}
}
return (0);
} else
return (ENOTSUP);
} else {
return (EINVAL);
}
}
/*ARGSUSED3*/
static int
{
return (ENXIO);
switch (cmd) {
case TG_GETPHYGEOM:
case TG_GETVIRTGEOM:
return (-1);
case TG_GETCAPACITY:
case TG_GETBLOCKSIZE:
if (un->un_diskcapacity <= 0) {
"invalid disk capacity\n");
return (EIO);
}
if (cmd == TG_GETCAPACITY)
else
return (0);
case TG_GETATTR:
"media_is_writable %x\n",
return (0);
default:
return (ENOTTY);
}
}