atapi.c revision ae115bc77f6fcde83175c75b4206dc2e50747966
/*
* 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 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include "ata_common.h"
#include "atapi.h"
/* SCSA entry points */
/*
* packet callbacks
*/
int do_callback);
/* external dependencies */
char _depends_on[] = "misc/scsi";
/*
* Local static data
*/
#if 0
static ddi_dma_lim_t atapi_dma_limits = {
0, /* address low */
0xffffffffU, /* address high */
0, /* counter max */
1, /* burstsize */
DMA_UNIT_8, /* minimum xfer */
0, /* dma speed */
0xffffffffU, /* address register */
0xffffffffU, /* counter register */
1, /* granular */
0xffffffffU /* request size */
};
#endif
static int atapi_use_static_geometry = TRUE;
static int atapi_arq_enable = TRUE;
/*
*
* Call SCSA init to initialize the ATAPI half of the driver
*
*/
int
{
ADBG_TRACE(("atapi_init entered\n"));
/* allocate transport structure */
ADBG_WARN(("atapi_init: scsi_hba_tran_alloc failed\n"));
goto errout;
}
/* initialize transport structure */
SCSI_HBA_TRAN_CLONE) != DDI_SUCCESS) {
ADBG_WARN(("atapi_init: scsi_hba_attach_setup failed\n"));
goto errout;
}
return (TRUE);
return (FALSE);
}
/*
*
* destroy the atapi sub-system
*
*/
void
{
ADBG_TRACE(("atapi_detach entered\n"));
}
/*
*
* initialize the ATAPI drive's soft-state based on the
* response to IDENTIFY PACKET DEVICE command
*
*/
int
{
ADBG_TRACE(("atapi_init_drive entered\n"));
/* Determine ATAPI CDB size */
case ATAPI_ID_CFG_PKT_12B:
break;
case ATAPI_ID_CFG_PKT_16B:
break;
default:
ADBG_WARN(("atapi_init_drive: bad pkt size support\n"));
return (FALSE);
}
/* determine if drive gives an intr when it wants the CDB */
return (TRUE);
}
/*
*
* destroy an atapi drive
*
*/
/* ARGSUSED */
void
{
ADBG_TRACE(("atapi_uninit_drive entered\n"));
}
/*
*
* Issue an IDENTIFY PACKET (ATAPI) DEVICE command
*
*/
int
{
int rc;
ADBG_TRACE(("atapi_id entered\n"));
if (!rc)
return (FALSE);
return (FALSE);
return (TRUE);
}
/*
*
* Check the device's register block for the ATAPI signature.
*
* registers are also part of the signature, for some unknown reason, this
* routine only checks the cyl hi and cyl low registers. I'm just
* guessing, but it might be because ATA and ATAPI devices return
* identical values in those registers and we actually rely on the
* IDENTIFY DEVICE and IDENTIFY PACKET DEVICE commands to recognize the
* device type.
*
*/
int
{
ADBG_TRACE(("atapi_signature entered\n"));
/*
* The following is a little bit of bullet proofing.
*
* When some drives are configured on a master-only bus they
* "shadow" their registers for the not-present slave drive.
* This is bogus and if you're not careful it may cause a
* master-only drive to be mistakenly recognized as both
* master and slave. By clearing the signature registers here
* I can make certain that when ata_drive_type() switches from
* the master to slave drive that I'll read back non-signature
* values regardless of whether the master-only drive does
* the "shadow" register trick. This prevents a bogus
* IDENTIFY PACKET DEVICE command from being issued which
* a really bogus master-only drive will return "shadow"
* data for.
*/
return (rc);
}
/*
*
* SCSA tran_tgt_init entry point
*
*/
/* ARGSUSED */
static int
struct scsi_device *sd)
{
struct scsi_address *ap;
int rc = DDI_SUCCESS;
ADBG_TRACE(("atapi_tran_tgt_init entered\n"));
/*
* Qualification of targ, lun, and ATAPI device presence
* have already been taken care of by ata_bus_ctl
*/
/* store pointer to drive struct in cloned tran struct */
/*
* Create the "atapi" property so the target driver knows
* to use the correct set of SCSI commands
*/
return (DDI_FAILURE);
}
/* tran_tgt_private points to gtgt_t */
/* gt_tgt_private points to ata_tgt_t */
/* initialize the per-target-instance data */
return (rc);
}
/*
*
* SCSA tran_tgt_probe entry point
*
*/
static int
{
ADBG_TRACE(("atapi_tran_tgt_probe entered\n"));
}
/*
*
* SCSA tran_tgt_free entry point
*
*/
/* ARGSUSED */
static void
struct scsi_device *sd)
{
ADBG_TRACE(("atapi_tran_tgt_free entered\n"));
}
/*
*
* SCSA tran_abort entry point
*
*/
/* ARGSUSED */
static int
struct scsi_address *ap,
{
ADBG_TRACE(("atapi_tran_abort entered\n"));
if (spktp) {
}
NULL));
}
/*
*
* SCSA tran_reset entry point
*
*/
/* ARGSUSED */
static int
struct scsi_address *ap,
int level)
{
ADBG_TRACE(("atapi_tran_reset entered\n"));
if (level == RESET_TARGET)
return (FALSE);
}
/*
*
* SCSA tran_setcap entry point
*
*/
static int
struct scsi_address *ap,
char *capstr,
int value,
int whom)
{
ADBG_TRACE(("atapi_tran_setcap entered\n"));
switch (scsi_hba_lookup_capstr(capstr)) {
case SCSI_CAP_SECTOR_SIZE:
return (TRUE);
case SCSI_CAP_ARQ:
if (whom) {
return (TRUE);
}
break;
case SCSI_CAP_TOTAL_SECTORS:
return (TRUE);
}
return (FALSE);
}
/*
*
* SCSA tran_getcap entry point
*
*/
static int
struct scsi_address *ap,
char *capstr,
int whom)
{
int rval = -1;
ADBG_TRACE(("atapi_tran_getcap entered\n"));
return (-1);
switch (scsi_hba_lookup_capstr(capstr)) {
case SCSI_CAP_ARQ:
break;
case SCSI_CAP_INITIATOR_ID:
rval = 7;
break;
case SCSI_CAP_DMA_MAX:
/* XXX - what should the real limit be?? */
/* limit to 64K ??? */
break;
case SCSI_CAP_GEOMETRY:
/* Default geometry */
if (atapi_use_static_geometry) {
break;
}
/* this code is currently not used */
/*
* retrieve the current IDENTIFY PACKET DEVICE info
*/
ADBG_TRACE(("atapi_tran_getcap geometry failed"));
return (0);
}
/*
* save the new response data
*/
case DTYPE_RODIRECT:
break;
case DTYPE_DIRECT:
case DTYPE_OPTICAL:
break;
default:
rval = 0;
}
break;
}
return (rval);
}
/*
*
* SCSA tran_init_pkt entry point
*
*/
static struct scsi_pkt *
struct scsi_address *ap,
int cmdlen,
int statuslen,
int tgtlen,
int flags,
{
int bytes;
ADBG_TRACE(("atapi_tran_init_pkt entered\n"));
/*
* Determine whether to do PCI-IDE DMA setup, start out by
* assuming we're not.
*/
/* no data to transfer */
goto skip_dma_setup;
}
/* no data to transfer */
goto skip_dma_setup;
}
goto skip_dma_setup;
}
if (ata_dma_disabled)
goto skip_dma_setup;
/*
* The PCI-IDE DMA engine is brain-damaged and can't
* DMA non-aligned buffers.
*/
/*
* if the virtual address isn't aligned, then the
* physical address also isn't aligned.
*/
goto skip_dma_setup;
}
/*
* It also insists that the byte count must be even.
*/
/* something odd here */
goto skip_dma_setup;
}
/*
* Huzza! We're really going to do it
*/
/*
* Call GHD packet init function
*/
return (NULL);
/* reset data direction flags */
if (spktp)
/*
* check for ARQ mode
*/
if (atapi_arq_enable == TRUE &&
ADBG_TRACE(("atapi_tran_init_pkt ARQ\n"));
}
/*
*/
/* determine direction to program the DMA engine later */
} else {
}
return (new_spktp);
}
/*
* Since we're not using DMA, we need to map the buffer into
* kernel address space
*/
/*
* If this is a fresh request map the buffer and
* reset the ap_baddr pointer and the current offset
* and byte count.
*
* The ap_boffset is used to set the ap_v_addr ptr at
* the start of each I/O request.
*
* The ap_bcount is used to update ap_boffset when the
* target driver requests the next segment.
*
*/
if (cmdlen) {
ata_pktp->ap_boffset = 0;
}
/* determine direction for the PIO FSM */
} else {
}
/*
* If the drive has the Single Sector bug, limit
* the transfer to a single sector. This assumes
* ATAPI CD drives always use 2k sectors.
*/
/* adjust offset based on prior request */
/* compute number of bytes left to transfer */
/* limit the transfer to 2k */
/* tell target driver how much is left for next time */
} else {
/* do the whole request in one swell foop */
}
} else {
ata_pktp->ap_boffset = 0;
}
/*
* determine the size of each partial data transfer
*/
return (new_spktp);
}
/*
* GHD ccballoc callback
*
* Initializing the ata_pkt, and return the ptr to the gcmd_t to GHD.
*
*/
/* ARGSUSED */
int
int cmdlen,
int statuslen,
int tgtlen,
int ccblen)
{
ADBG_TRACE(("atapi_ccballoc entered\n"));
/* set the back ptr from the ata_pkt to the gcmd_t */
/* check length of SCSI CDB is not larger than drive expects */
ADBG_WARN(("atapi_ccballoc: SCSI CDB too large!\n"));
return (FALSE);
}
/*
* save length of the SCSI CDB, and calculate CDB padding
* note that for convenience, padding is expressed in shorts.
*/
/* set up callback functions */
/* set-up for start */
return (TRUE);
}
/*
*
* SCSA tran_destroy_pkt entry point
*
*/
static void
struct scsi_address *ap,
{
ADBG_TRACE(("atapi_tran_destroy_pkt entered\n"));
}
}
/*
*
* GHD ccbfree callback function
*
*/
/* ARGSUSED */
void
{
ADBG_TRACE(("atapi_ccbfree entered\n"));
/* nothing to do */
}
/*
*
* SCSA tran_dmafree entry point
*
*/
/*ARGSUSED*/
static void
struct scsi_address *ap,
{
ADBG_TRACE(("atapi_tran_dmafree entered\n"));
}
}
/*
*
* SCSA tran_sync_pkt entry point
*
*/
/*ARGSUSED*/
static void
struct scsi_address *ap,
{
ADBG_TRACE(("atapi_tran_sync_pkt entered\n"));
}
}
/*
*
* SCSA tran_start entry point
*
*/
/* ARGSUSED */
static int
struct scsi_address *ap,
{
int rc;
ADBG_TRACE(("atapi_tran_start entered\n"));
/*
* Basic initialization performed each and every time a
* scsi_pkt is submitted. A single scsi_pkt may be submitted
* multiple times so this routine has to be idempotent. One
* time initializations don't belong here.
*/
/*
* The ap_v_addr pointer is incremented by the PIO data
* transfer routine as each word is transferred. Therefore, need
* to reset ap_v_addr here (rather than atapi_tran_init_pkt())
* in case the target resubmits the same pkt multiple times
* (which is permitted by SCSA).
*/
/* ap_resid is decremented as the data transfer progresses */
/* clear error flags */
spktp->pkt_reason = 0;
spktp->pkt_statistics = 0;
/*
* check for polling pkt
*/
}
/* driver cannot accept tagged commands */
return (TRAN_BADPKT);
}
#endif
/* call common transport routine */
/* see if pkt was not accepted */
if (rc != TRAN_ACCEPT)
return (rc);
return (rc);
}
/*
*
* GHD packet complete callback
*
*/
/* ARGSUSED */
static void
int do_callback)
{
ADBG_TRACE(("atapi_complete entered\n"));
/* update resid */
}
}
}
/* check for fatal errors */
} else {
}
/* non-fatal errors */
else
static struct scsi_status zero_scsi_status = { 0 };
struct scsi_arq_status *arqp;
arqp->sts_rqpkt_resid = 0;
arqp->sts_rqpkt_statistics = 0;
}
ADBG_TRANSPORT(("atapi_complete: reason = 0x%x stats = 0x%x "
}
/*
* Update the IDENTIFY PACKET DEVICE info
*/
static int
{
int rc;
/*
* select the appropriate drive and LUN
*/
/*
* make certain the drive is selected, and wait for not busy
*/
ADBG_ERROR(("atapi_id_update: select failed\n"));
return (ATA_FSM_RC_FINI);
}
if (!rc) {
} else {
}
return (ATA_FSM_RC_FINI);
}
/*
* Both drives on the controller share a common pkt to do
* ARQ processing. Therefore the pkt is only partially
* initialized here. The rest of initialization occurs
* just before starting the ARQ pkt when an error is
* detected.
*/
void
{
}