Exemplo n.º 1
0
static void
wds_intr(struct wds *wp)
{
	struct	 wds_req *rp;
	struct	 wds_mb *in;
	u_int8_t stat;
	u_int8_t c;
	int	 addr = wp->addr;

	DBG(DBX "wds%d: interrupt [\n", wp->unit);
	smallog('[');

	if (inb(addr + WDS_STAT) & WDS_IRQ) {
		c = inb(addr + WDS_IRQSTAT);
		if ((c & WDSI_MASK) == WDSI_MSVC) {
			c = c & ~WDSI_MASK;
			in = &wp->dx->imbs[c];

			rp = cmdtovirt(wp, scsi_3btoul(in->addr));
			stat = in->stat;

			if (rp != NULL)
				wds_done(wp, rp, stat);
			else
				device_printf(wp->dev,
					      "got weird command address %p"
					      "from controller\n", rp);

			in->stat = 0;
		} else
			device_printf(wp->dev,
				      "weird interrupt, irqstat=0x%x\n", c);
		outb(addr + WDS_IRQACK, 0);
	} else {
		smallog('?');
	}
	smallog(']');
	DBG(DBX "wds%d: ]\n", wp->unit);
}
Exemplo n.º 2
0
/*
 * Convert the SCSI command in ccb to an ata_xfer command in xa
 * for ATA_PORT_T_DISK operations.  Set the completion function
 * to convert the response back, then dispatch to the OpenBSD AHCI
 * layer.
 *
 * AHCI DISK commands only support a limited command set, and we
 * fake additional commands to make it play nice with the CAM subsystem.
 */
static
void
ahci_xpt_scsi_disk_io(struct ahci_port *ap, struct ata_port *atx,
		      union ccb *ccb)
{
	struct ccb_hdr *ccbh;
	struct ccb_scsiio *csio;
	struct ata_xfer *xa;
	struct ata_port	*at;
	struct ata_fis_h2d *fis;
	struct ata_pass_12 *atp12;
	struct ata_pass_16 *atp16;
	scsi_cdb_t cdb;
	union scsi_data *rdata;
	int rdata_len;
	u_int64_t capacity;
	u_int64_t lba;
	u_int32_t count;

	ccbh = &ccb->csio.ccb_h;
	csio = &ccb->csio;
	at = atx ? atx : ap->ap_ata[0];

	/*
	 * XXX not passing NULL at for direct attach!
	 */
	xa = ahci_ata_get_xfer(ap, atx);
	rdata = (void *)csio->data_ptr;
	rdata_len = csio->dxfer_len;

	/*
	 * Build the FIS or process the csio to completion.
	 */
	cdb = (void *)((ccbh->flags & CAM_CDB_POINTER) ?
			csio->cdb_io.cdb_ptr : csio->cdb_io.cdb_bytes);

	switch(cdb->generic.opcode) {
	case REQUEST_SENSE:
		/*
		 * Auto-sense everything, so explicit sense requests
		 * return no-sense.
		 */
		ccbh->status = CAM_SCSI_STATUS_ERROR;
		break;
	case INQUIRY:
		/*
		 * Inquiry supported features
		 *
		 * [opcode, byte2, page_code, length, control]
		 */
		if (cdb->inquiry.byte2 & SI_EVPD) {
			ahci_xpt_page_inquiry(ap, at, ccb);
		} else {
			bzero(rdata, rdata_len);
			if (rdata_len < SHORT_INQUIRY_LENGTH) {
				ccbh->status = CAM_CCB_LEN_ERR;
				break;
			}
			if (rdata_len > sizeof(rdata->inquiry_data))
				rdata_len = sizeof(rdata->inquiry_data);
			rdata->inquiry_data.device = T_DIRECT;
			rdata->inquiry_data.version = SCSI_REV_SPC2;
			rdata->inquiry_data.response_format = 2;
			rdata->inquiry_data.additional_length = 32;
			bcopy("SATA    ", rdata->inquiry_data.vendor, 8);
			bcopy(at->at_identify.model,
			      rdata->inquiry_data.product,
			      sizeof(rdata->inquiry_data.product));
			bcopy(at->at_identify.firmware,
			      rdata->inquiry_data.revision,
			      sizeof(rdata->inquiry_data.revision));
			ccbh->status = CAM_REQ_CMP;
		}
		
		/*
		 * Use the vendor specific area to set the TRIM status
		 * for scsi_da
		 */
		if (at->at_identify.support_dsm) {
			rdata->inquiry_data.vendor_specific1[0] =
			    at->at_identify.support_dsm &ATA_SUPPORT_DSM_TRIM;
			rdata->inquiry_data.vendor_specific1[1] = 
			    at->at_identify.max_dsm_blocks;
		}
		break;
	case READ_CAPACITY_16:
		if (cdb->read_capacity_16.service_action != SRC16_SERVICE_ACTION) {
			ccbh->status = CAM_REQ_INVALID;
			break;
		}
		if (rdata_len < sizeof(rdata->read_capacity_data_16)) {
			ccbh->status = CAM_CCB_LEN_ERR;
			break;
		}
		/* fall through */
	case READ_CAPACITY:
		if (rdata_len < sizeof(rdata->read_capacity_data)) {
			ccbh->status = CAM_CCB_LEN_ERR;
			break;
		}

		capacity = at->at_capacity;

		bzero(rdata, rdata_len);
		if (cdb->generic.opcode == READ_CAPACITY) {
			rdata_len = sizeof(rdata->read_capacity_data);
			if (capacity > 0xFFFFFFFFU)
				capacity = 0xFFFFFFFFU;
			bzero(&rdata->read_capacity_data, rdata_len);
			scsi_ulto4b((u_int32_t)capacity - 1,
				    rdata->read_capacity_data.addr);
			scsi_ulto4b(512, rdata->read_capacity_data.length);
		} else {
			rdata_len = sizeof(rdata->read_capacity_data_16);
			bzero(&rdata->read_capacity_data_16, rdata_len);
			scsi_u64to8b(capacity - 1,
				     rdata->read_capacity_data_16.addr);
			scsi_ulto4b(512, rdata->read_capacity_data_16.length);
		}
		ccbh->status = CAM_REQ_CMP;
		break;
	case SYNCHRONIZE_CACHE:
		/*
		 * Synchronize cache.  Specification says this can take
		 * greater then 30 seconds so give it at least 45.
		 */
		fis = xa->fis;
		fis->flags = ATA_H2D_FLAGS_CMD;
		fis->command = ATA_C_FLUSH_CACHE;
		fis->device = 0;
		if (xa->timeout < 45000)
			xa->timeout = 45000;
		xa->datalen = 0;
		xa->flags = 0;
		xa->complete = ahci_ata_complete_disk_synchronize_cache;
		break;
	case TRIM:
		fis = xa->fis;
		fis->command = ATA_C_DATA_SET_MANAGEMENT;
		fis->features = (u_int8_t)ATA_SF_DSM_TRIM;
		fis->features_exp = (u_int8_t)(ATA_SF_DSM_TRIM>> 8);

		xa->flags = ATA_F_WRITE;
		fis->flags = ATA_H2D_FLAGS_CMD;

		xa->data = csio->data_ptr;
		xa->datalen = csio->dxfer_len;
		xa->timeout = ccbh->timeout*50;	/* milliseconds */

		fis->sector_count =(u_int8_t)(xa->datalen/512);
		fis->sector_count_exp =(u_int8_t)((xa->datalen/512)>>8);

		lba = 0;
		fis->lba_low = (u_int8_t)lba;
		fis->lba_mid = (u_int8_t)(lba >> 8);
		fis->lba_high = (u_int8_t)(lba >> 16);
		fis->lba_low_exp = (u_int8_t)(lba >> 24);
		fis->lba_mid_exp = (u_int8_t)(lba >> 32);
		fis->lba_high_exp = (u_int8_t)(lba >> 40);
		   
		fis->device = ATA_H2D_DEVICE_LBA;
		xa->data = csio->data_ptr;

		xa->complete = ahci_ata_complete_disk_rw;
		ccbh->status = CAM_REQ_INPROG;
		break;
	case TEST_UNIT_READY:
	case START_STOP_UNIT:
	case PREVENT_ALLOW:
		/*
		 * Just silently return success
		 */
		ccbh->status = CAM_REQ_CMP;
		rdata_len = 0;
		break;
	case ATA_PASS_12:
		atp12 = &cdb->ata_pass_12;
		fis = xa->fis;
		/*
		 * Figure out the flags to be used, depending on the direction of the
		 * CAM request.
		 */
		switch (ccbh->flags & CAM_DIR_MASK) {
		case CAM_DIR_IN:
			xa->flags = ATA_F_READ;
			break;
		case CAM_DIR_OUT:
			xa->flags = ATA_F_WRITE;
			break;
		default:
			xa->flags = 0;
		}
		xa->flags |= ATA_F_POLL | ATA_F_EXCLUSIVE;
		xa->data = csio->data_ptr;
		xa->datalen = csio->dxfer_len;
		xa->complete = ahci_ata_complete_disk_rw;
		xa->timeout = ccbh->timeout;

		/*
		 * Populate the fis from the information we received through CAM
		 * ATA passthrough.
		 */
		fis->flags = ATA_H2D_FLAGS_CMD;	/* maybe also atp12->flags ? */
		fis->features = atp12->features;
		fis->sector_count = atp12->sector_count;
		fis->lba_low = atp12->lba_low;
		fis->lba_mid = atp12->lba_mid;
		fis->lba_high = atp12->lba_high;
		fis->device = atp12->device;	/* maybe always 0? */
		fis->command = atp12->command;
		fis->control = atp12->control;

		/*
		 * Mark as in progress so it is sent to the device.
		 */
		ccbh->status = CAM_REQ_INPROG;
		break;
	case ATA_PASS_16:
		atp16 = &cdb->ata_pass_16;
		fis = xa->fis;
		/*
		 * Figure out the flags to be used, depending on the direction of the
		 * CAM request.
		 */
		switch (ccbh->flags & CAM_DIR_MASK) {
		case CAM_DIR_IN:
			xa->flags = ATA_F_READ;
			break;
		case CAM_DIR_OUT:
			xa->flags = ATA_F_WRITE;
			break;
		default:
			xa->flags = 0;
		}
		xa->flags |= ATA_F_POLL | ATA_F_EXCLUSIVE;
		xa->data = csio->data_ptr;
		xa->datalen = csio->dxfer_len;
		xa->complete = ahci_ata_complete_disk_rw;
		xa->timeout = ccbh->timeout;

		/*
		 * Populate the fis from the information we received through CAM
		 * ATA passthrough.
		 */
		fis->flags = ATA_H2D_FLAGS_CMD;	/* maybe also atp16->flags ? */
		fis->features = atp16->features;
		fis->features_exp = atp16->features_ext;
		fis->sector_count = atp16->sector_count;
		fis->sector_count_exp = atp16->sector_count_ext;
		fis->lba_low = atp16->lba_low;
		fis->lba_low_exp = atp16->lba_low_ext;
		fis->lba_mid = atp16->lba_mid;
		fis->lba_mid_exp = atp16->lba_mid_ext;
		fis->lba_high = atp16->lba_high;
		fis->lba_mid_exp = atp16->lba_mid_ext;
		fis->device = atp16->device;	/* maybe always 0? */
		fis->command = atp16->command;

		/*
		 * Mark as in progress so it is sent to the device.
		 */
		ccbh->status = CAM_REQ_INPROG;
		break;
	default:
		switch(cdb->generic.opcode) {
		case READ_6:
			lba = scsi_3btoul(cdb->rw_6.addr) & 0x1FFFFF;
			count = cdb->rw_6.length ? cdb->rw_6.length : 0x100;
			xa->flags = ATA_F_READ;
			break;
		case READ_10:
			lba = scsi_4btoul(cdb->rw_10.addr);
			count = scsi_2btoul(cdb->rw_10.length);
			xa->flags = ATA_F_READ;
			break;
		case READ_12:
			lba = scsi_4btoul(cdb->rw_12.addr);
			count = scsi_4btoul(cdb->rw_12.length);
			xa->flags = ATA_F_READ;
			break;
		case READ_16:
			lba = scsi_8btou64(cdb->rw_16.addr);
			count = scsi_4btoul(cdb->rw_16.length);
			xa->flags = ATA_F_READ;
			break;
		case WRITE_6:
			lba = scsi_3btoul(cdb->rw_6.addr) & 0x1FFFFF;
			count = cdb->rw_6.length ? cdb->rw_6.length : 0x100;
			xa->flags = ATA_F_WRITE;
			break;
		case WRITE_10:
			lba = scsi_4btoul(cdb->rw_10.addr);
			count = scsi_2btoul(cdb->rw_10.length);
			xa->flags = ATA_F_WRITE;
			break;
		case WRITE_12:
			lba = scsi_4btoul(cdb->rw_12.addr);
			count = scsi_4btoul(cdb->rw_12.length);
			xa->flags = ATA_F_WRITE;
			break;
		case WRITE_16:
			lba = scsi_8btou64(cdb->rw_16.addr);
			count = scsi_4btoul(cdb->rw_16.length);
			xa->flags = ATA_F_WRITE;
			break;
		default:
			ccbh->status = CAM_REQ_INVALID;
			break;
		}
		if (ccbh->status != CAM_REQ_INPROG)
			break;

		fis = xa->fis;
		fis->flags = ATA_H2D_FLAGS_CMD;
		fis->lba_low = (u_int8_t)lba;
		fis->lba_mid = (u_int8_t)(lba >> 8);
		fis->lba_high = (u_int8_t)(lba >> 16);
		fis->device = ATA_H2D_DEVICE_LBA;

		/*
		 * NCQ only for direct-attached disks, do not currently
		 * try to use NCQ with port multipliers.
		 */
		if (at->at_ncqdepth > 1 &&
		    ap->ap_type == ATA_PORT_T_DISK &&
		    (ap->ap_sc->sc_cap & AHCI_REG_CAP_SNCQ) &&
		    (ccbh->flags & CAM_POLLED) == 0) {
			/*
			 * Use NCQ - always uses 48 bit addressing
			 */
			xa->flags |= ATA_F_NCQ;
			fis->command = (xa->flags & ATA_F_WRITE) ?
					ATA_C_WRITE_FPDMA : ATA_C_READ_FPDMA;
			fis->lba_low_exp = (u_int8_t)(lba >> 24);
			fis->lba_mid_exp = (u_int8_t)(lba >> 32);
			fis->lba_high_exp = (u_int8_t)(lba >> 40);
			fis->sector_count = xa->tag << 3;
			fis->features = (u_int8_t)count;
			fis->features_exp = (u_int8_t)(count >> 8);
		} else if (count > 0x100 || lba > 0x0FFFFFFFU) {