/* * 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 * 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 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include /* * Generic SCSI Host Bus Adapter interface implementation */ #include extern int dcd_options; static kmutex_t dcd_hba_mutex; kmutex_t dcd_log_mutex; struct dcd_hba_inst { dev_info_t *inst_dip; dcd_hba_tran_t *inst_hba_tran; struct dcd_hba_inst *inst_next; struct dcd_hba_inst *inst_prev; }; static struct dcd_hba_inst *dcd_hba_list = NULL; static struct dcd_hba_inst *dcd_hba_list_tail = NULL; _NOTE(READ_ONLY_DATA(dev_ops)) kmutex_t dcd_flag_nointr_mutex; kcondvar_t dcd_flag_nointr_cv; /* * Called from _init when loading the dcd module. */ void dcd_initialize_hba_interface() { mutex_init(&dcd_hba_mutex, NULL, MUTEX_DRIVER, NULL); mutex_init(&dcd_flag_nointr_mutex, NULL, MUTEX_DRIVER, NULL); cv_init(&dcd_flag_nointr_cv, NULL, CV_DRIVER, NULL); mutex_init(&dcd_log_mutex, NULL, MUTEX_DRIVER, NULL); } /* * Called from fini() when unloading the dcd module. */ void dcd_uninitialize_hba_interface() { mutex_destroy(&dcd_hba_mutex); cv_destroy(&dcd_flag_nointr_cv); mutex_destroy(&dcd_flag_nointr_mutex); mutex_destroy(&dcd_log_mutex); } /* * Called by an HBA from _init() */ /* ARGSUSED */ int dcd_hba_init(struct modlinkage *modlp) { return (0); } #ifdef NOTNEEDED /* ARGSUSED */ int dcd_hba_attach(dev_info_t *dip, ddi_dma_lim_t *hba_lim, dcd_hba_tran_t *hba_tran, int flags, void *hba_options) { ddi_dma_attr_t hba_dma_attr; bzero(&hba_dma_attr, sizeof (ddi_dma_attr_t)); hba_dma_attr.dma_attr_burstsizes = hba_lim->dlim_burstsizes; hba_dma_attr.dma_attr_minxfer = hba_lim->dlim_minxfer; return (dcd_hba_attach_setup(dip, &hba_dma_attr, hba_tran, flags)); } #endif int dcd_hba_attach( dev_info_t *dip, ddi_dma_attr_t *hba_dma_attr, dcd_hba_tran_t *hba_tran, int flags) { struct dcd_hba_inst *elem; int value; int len; char *prop_name; char *errmsg = "dcd_hba_attach: cannott create property '%s' for %s%d\n"; /* * Link this instance into the list */ elem = kmem_alloc(sizeof (struct dcd_hba_inst), KM_SLEEP); elem->inst_dip = dip; elem->inst_hba_tran = hba_tran; mutex_enter(&dcd_hba_mutex); elem->inst_next = NULL; elem->inst_prev = dcd_hba_list_tail; if (dcd_hba_list == NULL) { dcd_hba_list = elem; } if (dcd_hba_list_tail) { dcd_hba_list_tail->inst_next = elem; } dcd_hba_list_tail = elem; mutex_exit(&dcd_hba_mutex); /* * Save all the improtant HBA information that must be accessed * later. */ hba_tran->tran_hba_dip = dip; hba_tran->tran_hba_flags = flags; /* * Note: We only need dma_attr_minxfer and dma_attr_burstsize * from the DMA atrributes */ hba_tran->tran_min_xfer = hba_dma_attr->dma_attr_minxfer; hba_tran->tran_min_burst_size = (1<<(ddi_ffs(hba_dma_attr->dma_attr_burstsizes)-1)); hba_tran->tran_max_burst_size = (1<<(ddi_fls(hba_dma_attr->dma_attr_burstsizes)-1)); prop_name = "dcd_options"; len = 0; if (ddi_prop_op(DDI_DEV_T_ANY, dip, PROP_LEN, 0, prop_name, NULL, &len) == DDI_PROP_NOT_FOUND) { value = dcd_options; if (ddi_prop_update_int(DDI_MAJOR_T_UNKNOWN, dip, prop_name, value) != DDI_PROP_SUCCESS) { cmn_err(CE_CONT, errmsg, prop_name, ddi_get_name(dip), ddi_get_instance(dip)); } } /* * XXX : This needs to be removed when code cleanup * ddi_set_driver_private(dip, (caddr_t)hba_tran); */ #ifdef DEBUG1 printf("Called Set driver private with dip %x, tran %x\n", dip, hba_tran); #endif return (DDI_SUCCESS); } /* * called by an HBA to detach an instance of the driver */ int dcd_hba_detach(dev_info_t *dip) { dcd_hba_tran_t *hba; struct dcd_hba_inst *elem; hba = ddi_get_driver_private(dip); ddi_set_driver_private(dip, NULL); ASSERT(hba != NULL); hba->tran_hba_dip = (dev_info_t *)NULL; hba->tran_hba_flags = 0; hba->tran_min_burst_size = (uchar_t)0; hba->tran_max_burst_size = (uchar_t)0; /* * Remove HBA instance from dcd_hba_list */ mutex_enter(&dcd_hba_mutex); for (elem = dcd_hba_list; elem != (struct dcd_hba_inst *)NULL; elem = elem->inst_next) { if (elem->inst_dip == dip) break; } if (elem == (struct dcd_hba_inst *)NULL) { cmn_err(CE_NOTE, "dcd_hba_attach: Unknown HBA instance\n"); mutex_exit(&dcd_hba_mutex); } if (elem == dcd_hba_list) { dcd_hba_list = elem->inst_next; dcd_hba_list->inst_prev = (struct dcd_hba_inst *)NULL; } else if (elem == dcd_hba_list_tail) { dcd_hba_list_tail = elem->inst_prev; dcd_hba_list_tail->inst_next = (struct dcd_hba_inst *)NULL; } else { elem->inst_prev->inst_next = elem->inst_next; elem->inst_next->inst_prev = elem->inst_prev; } mutex_exit(&dcd_hba_mutex); kmem_free(elem, sizeof (struct dcd_hba_inst)); return (DDI_SUCCESS); } void dcd_hba_fini() { } /* ARGSUSED */ dcd_hba_tran_t * dcd_hba_tran_alloc( dev_info_t *dip, int flags) { return (kmem_zalloc(sizeof (dcd_hba_tran_t), (flags & DCD_HBA_CANSLEEP) ? KM_SLEEP: KM_NOSLEEP)); } void dcd_hba_tran_free(dcd_hba_tran_t *hba_tran) { kmem_free(hba_tran, sizeof (dcd_hba_tran_t)); } /* * XXX: Do we really need the following routines. */ /* * private wrapper for dcd_pkt's allocated via scsi_hba_pkt_alloc */ struct dcd_pkt_wrapper { struct dcd_pkt dcd_pkt; int pkt_wrapper_len; }; _NOTE(SCHEME_PROTECTS_DATA("unique per thread", dcd_pkt_wrapper)) /* * Round up all allocations so that we can gurentee * long-long alignment. This is the same alignment * provided by kmem_alloc(). */ #define ROUNDUP(x) (((x) + 0x07) & ~0x07) /* * Called by an HBA to allocate a dcd_pkt */ /* ARGSUSED */ struct dcd_pkt * dcd_hba_pkt_alloc( struct dcd_address *ap, int cmdlen, int statuslen, int tgtlen, int hbalen, int (*callback)(caddr_t arg), caddr_t arg) { struct dcd_pkt *pkt; struct dcd_pkt_wrapper *hba_pkt; caddr_t p; int pktlen; /* * Sanity check */ if (callback != SLEEP_FUNC && callback != NULL_FUNC) { cmn_err(CE_PANIC, " dcd_hba_pkt_alloc: callback must be" " either SLEEP or NULL\n"); } /* * Round up so everything gets allocated on long-word boundaries. */ cmdlen = ROUNDUP(cmdlen); tgtlen = ROUNDUP(tgtlen); hbalen = ROUNDUP(hbalen); statuslen = ROUNDUP(statuslen); pktlen = sizeof (struct dcd_pkt_wrapper) + cmdlen + tgtlen +hbalen + statuslen; hba_pkt = kmem_zalloc(pktlen, (callback = SLEEP_FUNC) ? KM_SLEEP: KM_NOSLEEP); if (hba_pkt == NULL) { ASSERT(callback == NULL_FUNC); return (NULL); } /* * Set up or private info on this pkt */ hba_pkt->pkt_wrapper_len = pktlen; pkt = &hba_pkt->dcd_pkt; p = (caddr_t)(hba_pkt + 1); /* * set up pointers to private data areas, cdb and status. */ if (hbalen > 0) { pkt->pkt_ha_private = (ataopaque_t)p; p += hbalen; } if (tgtlen > 0) { pkt->pkt_private = (ataopaque_t)p; p += tgtlen; } if (statuslen > 0) { pkt->pkt_scbp = (uchar_t *)p; p += statuslen; } if (cmdlen > 0) { pkt->pkt_cdbp = (void *)p; } /* * Initialize the pkt's dcd_address */ pkt->pkt_address = *ap; #ifdef DEBUG1 printf("da_target %x, da_lun %x, a_hba_tran %x\n", pkt->pkt_address.da_target, pkt->pkt_address.da_lun, pkt->pkt_address.a_hba_tran); printf("From address : da_target %x, da_lun %x, a_hba_tran %x\n", ap->da_target, ap->da_lun, ap->a_hba_tran); printf("Pkt %x\n", pkt); #endif return (pkt); } /* ARGSUSED */ void dcd_hba_pkt_free( struct dcd_address *ap, struct dcd_pkt *pkt) { kmem_free((struct dcd_pkt_wrapper *)pkt, ((struct dcd_pkt_wrapper *)pkt)->pkt_wrapper_len); } /* * Called by an HBA to map strings to capability indices */ int dcd_hba_lookup_capstr(char *capstr) { /* * Capability strings, masking the '-' vs '_'. */ static struct cap_strings { char *cap_string; int cap_index; } cap_string[] = { { "dma-max", DCD_CAP_DMA_MAX }, { "dma_max", DCD_CAP_DMA_MAX }, { "ultraata", DCD_CAP_ULTRA_ATA }, { "busmaster", DCD_CAP_BUS_MASTER }, { "overlap", DCD_CAP_OVERLAP }, { "parity", DCD_CAP_PARITY }, { "sector-size", DCD_CAP_SECTOR_SIZE }, { "total-sectors", DCD_CAP_TOTAL_SECTORS }, { "geometry", DCD_CAP_GEOMETRY }, { "block-mode", DCD_CAP_BLOCKMODE }, { "block-factor", DCD_CAP_BLOCKFACTOR }, { "dma-support", DCD_CAP_DMA_SUPPORT }, { "pio-support", DCD_CAP_PIO_SUPPORT }, { "lba-addressing", DCD_CAP_LBA_ADDRESSING }, { NULL, 0 } }; struct cap_strings *cp; for (cp = cap_string; cp->cap_string != NULL; cp++) { if (strcmp(cp->cap_string, capstr) == 0) { return (cp->cap_index); } } return (-1); }