dcd_confsubr.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (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
* or http://www.opensolaris.org/os/licensing.
* 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 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* Utility DCD configuration routines.
*/
#include <sys/dada/dada.h>
#include <sys/modctl.h>
extern struct mod_ops mod_miscops;
static struct modlmisc modlmisc = {
&mod_miscops, /* Type of module */
" ATA Bus Utility Routines"
};
static struct modlinkage modlinkage = {
MODREV_1, (void *)&modlmisc, NULL
};
static int dcd_test(struct dcd_pkt *);
void makecommand(struct dcd_pkt *, int, uchar_t, uint32_t,
uchar_t, uint32_t, uchar_t, uchar_t);
int
_init()
{
(void) dcd_initialize_hba_interface();
return (mod_install(&modlinkage));
}
/*
* There is no _fini() routine because this module is never unloaded.
*/
int
_info(modinfop)
struct modinfo *modinfop;
{
return (mod_info(&modlinkage, modinfop));
}
/*
* The implementation of dcd_probe allows a particular HBA to intercept the call
* for any post or pre-processing it may need. The default, if the HBA does not
* override it, is to call dcd_hba_probe.
*/
int
dcd_probe(struct dcd_device *devp, int (*callback)())
{
dcd_hba_tran_t *hba_tran = devp->dcd_address->a_hba_tran;
if (hba_tran->tran_tgt_probe != NULL) {
return ((*hba_tran->tran_tgt_probe)(devp, callback));
} else {
return (dcd_hba_probe(devp, callback));
}
}
/*
* Undo the dcd_probe
*/
void
dcd_unprobe(struct dcd_device *devp)
{
if (devp->dcd_ident) {
kmem_free((caddr_t)devp->dcd_ident, SUN_IDENTSIZE);
devp->dcd_ident = (struct dcd_identify *)NULL;
}
}
#define ROUTE (devp->dcd_address)
int
dcd_hba_probe(struct dcd_device *devp, int (*callback)())
{
struct dcd_pkt *ident_pkt = NULL;
int rval = DCDPROBE_NOMEM;
struct buf *ident_bp = NULL;
int (*cb_flag)();
if (devp->dcd_ident == NULL) {
#ifdef DEBUG1
printf("Dcd_ident is NULL\n");
#endif
devp->dcd_ident = (struct dcd_identify *)
kmem_alloc(SUN_IDENTSIZE, ((callback == SLEEP_FUNC)?
KM_SLEEP : KM_NOSLEEP));
if (devp->dcd_ident == NULL) {
goto out;
}
}
if (callback != SLEEP_FUNC && callback != NULL_FUNC) {
cb_flag = NULL_FUNC;
} else {
cb_flag = callback;
}
ident_bp = dcd_alloc_consistent_buf(ROUTE, (struct buf *)NULL,
(uint_t)SUN_IDENTSIZE, B_READ, cb_flag, NULL);
if (ident_bp == NULL) {
goto out;
}
ident_pkt = dcd_init_pkt(ROUTE, (struct dcd_pkt *)NULL,
ident_bp, sizeof (struct dcd_cmd), 2, 0,
PKT_CONSISTENT,
callback, NULL);
if (ident_pkt == NULL) {
if (ident_bp->b_error == 0)
rval = DCDPROBE_NOMEM_CB;
goto out;
}
bp_mapin(ident_bp);
bzero((caddr_t)devp->dcd_ident, SUN_IDENTSIZE);
makecommand(ident_pkt, FLAG_NOINTR, IDENTIFY, 0, ADD_LBA_MODE,
SUN_IDENTSIZE, DATA_READ, 0);
/*
* The first identify will tell us whether the target responded
* or not.
*/
if (dcd_test(ident_pkt) < 0) {
#ifdef DEBUG1
printf("dcd_test: failed\n");
#endif
if (ident_pkt->pkt_reason == CMD_INCOMPLETE) {
rval = DCDPROBE_NORESP;
goto out;
} else {
/*
* retry one more time
*/
if (dcd_test(ident_pkt) < 0) {
rval = DCDPROBE_FAILURE;
goto out;
}
}
}
#ifdef DEBUG1
printf("Pkt reason %x, scsbp %x\n", ident_pkt->pkt_reason,
*ident_pkt->pkt_scbp);
#endif
/*
* If we are lucky, this identify succeeded
*/
if ((ident_pkt->pkt_reason == CMD_CMPLT) &&
(((*ident_pkt->pkt_scbp) & STATUS_ATA_MASK) == 0)) {
goto done;
}
/*
* the second inquiry, allows the host adapters to try again.
*/
if (dcd_test(ident_pkt) < 0) {
if (ident_pkt->pkt_reason == CMD_INCOMPLETE)
rval = DCDPROBE_NORESP;
else
rval = DCDPROBE_FAILURE;
goto out;
}
/*
* At this point we are guarenteed that something responded
* to this target. We don't know yest what kind of device it is.
*/
if (dcd_test(ident_pkt) < 0) {
rval = DCDPROBE_FAILURE;
goto out;
}
done:
/*
* If we got no error then receive the indentify data,
*/
if ((ident_pkt->pkt_state & STATE_XFERRED_DATA) == 0 &&
ident_pkt->pkt_resid > 0) {
rval = DCDPROBE_NONCCS;
} else {
bcopy((caddr_t)ident_bp->b_un.b_addr,
(caddr_t)devp->dcd_ident, SUN_IDENTSIZE);
rval = DCDPROBE_EXISTS;
}
out:
if (ident_pkt) {
dcd_destroy_pkt(ident_pkt);
}
if (ident_bp) {
dcd_free_consistent_buf(ident_bp);
}
return (rval);
}
static int
dcd_test(struct dcd_pkt *pkt)
{
int rval = -1;
pkt->pkt_flags |= FLAG_NOINTR;
pkt->pkt_time = DCD_POLL_TIMEOUT;
#ifdef DEBUG1
printf("flags %x: timeout %x\n", pkt->pkt_flags, pkt->pkt_time);
#endif
if (dcd_transport(pkt) != TRAN_ACCEPT) {
goto error;
} else if (pkt->pkt_reason == CMD_INCOMPLETE &&
pkt->pkt_state == 0) {
goto error;
} else if (pkt->pkt_reason != CMD_CMPLT) {
goto error;
} else if (((*pkt->pkt_scbp) & STATUS_ATA_MASK) == STATUS_ATA_BUSY) {
rval = 0;
} else {
rval = 0;
}
error:
#ifdef DEBUG1
printf("dcd_test: rval is %x\n", rval);
#endif
return (rval);
}
void
makecommand(struct dcd_pkt *pkt,
int flags,
uchar_t command,
uint32_t block,
uchar_t address_mode,
uint32_t size,
uchar_t direction,
uchar_t features)
{
struct dcd_cmd *cdbp = (struct dcd_cmd *)pkt->pkt_cdbp;
cdbp->cmd = command;
cdbp->sector_num.lba_num = block;
cdbp->address_mode = address_mode;
cdbp->direction = direction;
cdbp->size = size; /* Size in bytes */
cdbp->features = features;
pkt->pkt_flags = flags;
#ifdef DEBUG1
printf("pkt flags set in dada %x\n", pkt->pkt_flags);
printf("command %x, flags %x, block %x, address_mode %x, size %x\n",
command, flags, block, address_mode, size);
#endif
}