/*- * Copyright (c) 2000 - 2005 Søren Schmidt * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification, immediately at the beginning of the file. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* prototypes */ static void ata_cam_poll(struct cam_sim *); static void ata_cam_async(void *, u_int32_t, struct cam_path *, void *); static void ata_cam_action(struct cam_sim *, union ccb *); static void ata_cam_io_ata(struct cam_sim *, union ccb *); static void ata_cam_io_atapi(struct cam_sim *, union ccb *); static void ata_cam_done(struct ata_request *); static void ata_cam_inqdata(struct ata_device *, struct scsi_inquiry_data *); struct ata_cam_softc { struct cam_sim *sim; struct cam_path *path; struct ata_device *devices[2]; int flag; }; static int ata_cam_probe(device_t dev) { device_printf(dev, "probe\n"); return 0; } static int ata_cam_attach(device_t dev) { struct ata_cam_softc *acp = device_get_softc(dev); struct cam_devq *devq; struct cam_sim *sim; struct cam_path *path; struct ccb_setasync csa; device_printf(dev, "attach\n"); GIANT_REQUIRED; if (!(devq = cam_simq_alloc(32))) return ENOMEM; if (!(sim = cam_sim_alloc(ata_cam_action, ata_cam_poll, "atacam", dev, device_get_unit(GRANDPARENT(dev)), 1, 1 /* no tags yet */, devq))) { cam_simq_free(devq); return ENOMEM; } if (xpt_bus_register(sim, device_get_unit(device_get_parent(dev))) != CAM_SUCCESS) { cam_sim_free(sim, TRUE); cam_simq_free(devq); return ENXIO; } if (xpt_create_path(&path, NULL, cam_sim_path(sim), CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) != CAM_REQ_CMP) { xpt_bus_deregister(cam_sim_path(sim)); cam_sim_free(sim, TRUE); cam_simq_free(devq); return ENXIO; } acp->sim = sim; acp->path = path; xpt_setup_ccb(&csa.ccb_h, path, 5); csa.ccb_h.func_code = XPT_SASYNC_CB; csa.event_enable = (AC_FOUND_DEVICE | AC_LOST_DEVICE); csa.callback = ata_cam_async; csa.callback_arg = sim; xpt_action((union ccb *)&csa); #if 0 /* rescan bus */ if (xpt_create_path(&path, xpt_periph, cam_sim_path(sim), CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) != CAM_REQ_CMP) { return 0; } xpt_setup_ccb(&csa.ccb_h, path, 5); csa.ccb_h.func_code = XPT_SCAN_BUS; csa.callback = NULL; xpt_action((union ccb *)&csa); #endif return 0; } static int ata_cam_detach(device_t dev) { struct ata_cam_softc *acp = device_get_softc(dev); device_printf(dev, "detach\n"); mtx_lock(&Giant); if (acp->path) { //setup_async_cb(ch, 0); SOS XXX xpt_free_path(acp->path); acp->path = NULL; // not needed anymore } xpt_bus_deregister(cam_sim_path(acp->sim)); if (acp->sim) { if (xpt_bus_deregister(cam_sim_path(acp->sim))==CAM_REQ_CMP) cam_sim_free(acp->sim, TRUE); else device_printf(dev, "bus %s wont deregister\n", cam_sim_name(acp->sim)); } mtx_unlock(&Giant); return 0; } static int ata_cam_reinit(device_t dev) { struct ata_cam_softc *acp = device_get_softc(dev); device_printf(dev, "reinit\n"); mtx_lock(&Giant); xpt_async(AC_BUS_RESET, acp->path, NULL); mtx_unlock(&Giant); return 0; } static void ata_cam_poll(struct cam_sim *simp) { printf("ata_cam_poll() called\n"); } static void ata_cam_async(void *callback_arg, u_int32_t code, struct cam_path *path, void *arg) { device_t dev = cam_sim_softc(callback_arg); mtx_lock(&Giant); device_printf(dev, "ata_cam_sync() called with "); switch (code) { case AC_LOST_DEVICE: printf("AC_LOST_DEVICE "); break; case AC_FOUND_DEVICE: printf("AC_FOUND_DEVICE "); break; default: printf("unknown code=0x%08x", code); break; } xpt_print_path(path); printf("\n"); mtx_unlock(&Giant); } static void ata_cam_action(struct cam_sim *sim, union ccb *ccb) { device_t dev = cam_sim_softc(sim); struct ata_cam_softc *acp = device_get_softc(dev); struct ata_device *atadev; #if 1 //def DEBUG if (ccb->ccb_h.func_code == XPT_SCSI_IO) { u_int8_t *cdb = ccb->csio.cdb_io.cdb_bytes; printf("ata_cam_action atacam%d:%d:%d cdb: ", device_get_unit(dev), ccb->ccb_h.target_id, ccb->ccb_h.target_lun); printf("%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", cdb[0], cdb[1], cdb[2], cdb[3], cdb[4], cdb[5], cdb[6], cdb[7], cdb[8], cdb[9], cdb[10], cdb[11]); } else { printf("ata_cam_action atacam%d:%d:%d func 0x%x\n", device_get_unit(dev), ccb->ccb_h.target_id, ccb->ccb_h.target_lun, ccb->ccb_h.func_code); } #endif if (ccb->ccb_h.flags & CAM_CDB_POINTER) { printf("scream! CAM_CDB_POINTER\n"); ccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(ccb); return; } switch (ccb->ccb_h.func_code) { case XPT_SCSI_IO: atadev = acp->devices[ccb->ccb_h.target_id]; atadev->dev = dev; device_set_ivars(dev, atadev); if ((atadev->param.config & ATA_PROTO_MASK) == ATA_PROTO_ATA) return ata_cam_io_ata(sim, ccb); if ((atadev->param.config & ATA_PROTO_MASK) == ATA_PROTO_ATAPI_12 || (atadev->param.config & ATA_PROTO_MASK) == ATA_PROTO_ATAPI_16) return ata_cam_io_atapi(sim, ccb); ccb->ccb_h.status = CAM_REQ_INVALID; break; case XPT_PATH_INQ: ccb->cpi.version_num = 1; ccb->cpi.hba_inquiry = 0; ccb->cpi.target_sprt = 0; ccb->cpi.hba_misc = PIM_NO_6_BYTE; ccb->cpi.hba_eng_cnt = 0; ccb->cpi.max_target = 1; ccb->cpi.max_lun = 0; ccb->cpi.initiator_id = 2; ccb->cpi.hpath_id = 0; ccb->cpi.bus_id = cam_sim_bus(sim); ccb->cpi.async_flags = 0; ccb->cpi.unit_number = cam_sim_unit(sim); if (ccb->ccb_h.target_id == CAM_TARGET_WILDCARD) /* SOS get controller channel max speed here */ ccb->cpi.base_transfer_speed = 16500; #if 0 else { atadev = &acp->devices[ccb->ccb_h.target_id]; switch (atadev->mode) { case ATA_PIO0: ccb->cpi.base_transfer_speed = 1650; break; case ATA_PIO1: ccb->cpi.base_transfer_speed = 2600; break; case ATA_PIO2: ccb->cpi.base_transfer_speed = 3500; break; case ATA_PIO3: ccb->cpi.base_transfer_speed = 5500; break; case ATA_PIO4: ccb->cpi.base_transfer_speed = 8000; break; case ATA_WDMA0: ccb->cpi.base_transfer_speed = 3500; break; case ATA_WDMA1: ccb->cpi.base_transfer_speed = 5500; break; case ATA_WDMA2: ccb->cpi.base_transfer_speed = 8000; break; case ATA_UDMA0: ccb->cpi.base_transfer_speed = 8000; break; case ATA_UDMA1: ccb->cpi.base_transfer_speed = 12500; break; case ATA_UDMA2: ccb->cpi.base_transfer_speed = 16500; break; case ATA_UDMA3: ccb->cpi.base_transfer_speed = 20000; break; case ATA_UDMA4: ccb->cpi.base_transfer_speed = 33000; break; case ATA_UDMA5: ccb->cpi.base_transfer_speed = 50000; break; case ATA_UDMA6: ccb->cpi.base_transfer_speed = 66000; break; case ATA_SA150: ccb->cpi.base_transfer_speed = 75000; break; default: ccb->cpi.base_transfer_speed = 16500; break; } } #endif strncpy(ccb->cpi.sim_vid, "FreeBSD", SIM_IDLEN); strncpy(ccb->cpi.hba_vid, "ATA", HBA_IDLEN); strncpy(ccb->cpi.dev_name, cam_sim_name(sim), DEV_IDLEN); ccb->ccb_h.status = CAM_REQ_CMP; break; case XPT_RESET_DEV: atadev = acp->devices[ccb->ccb_h.target_id]; if ((atadev->param.config & ATA_PROTO_MASK) == ATA_PROTO_ATA) { ccb->ccb_h.status = CAM_REQ_INVALID; } if ((atadev->param.config & ATA_PROTO_MASK) == ATA_PROTO_ATAPI_12 || (atadev->param.config & ATA_PROTO_MASK) == ATA_PROTO_ATAPI_16) { ata_controlcmd(atadev, ATA_ATAPI_RESET, 0, 0, 0); ccb->ccb_h.status = CAM_REQ_CMP; } break; case XPT_RESET_BUS: ata_reinit(device_get_parent(dev)); ccb->ccb_h.status = CAM_REQ_CMP; break; case XPT_GET_TRAN_SETTINGS: ccb->cts.flags &= ~(CCB_TRANS_DISC_ENB | CCB_TRANS_TAG_ENB); ccb->cts.bus_width = MSG_EXT_WDTR_BUS_16_BIT; ccb->cts.valid = CCB_TRANS_BUS_WIDTH_VALID; ccb->ccb_h.status = CAM_REQ_CMP; break; case XPT_CALC_GEOMETRY: cam_calc_geometry(&ccb->ccg, 1); ccb->ccb_h.status = CAM_REQ_CMP; break; default: ccb->ccb_h.status = CAM_REQ_INVALID; break; } xpt_done(ccb); } static void ata_cam_io_ata(struct cam_sim *sim, union ccb *ccb) { device_t dev = cam_sim_softc(sim); struct ata_cam_softc *acp = device_get_softc(dev); struct ata_channel *ch = device_get_softc(device_get_parent(dev)); struct ata_device *atadev = acp->devices[ccb->ccb_h.target_id]; u_int8_t *cdb = ccb->csio.cdb_io.cdb_bytes; printf("io_ata\n"); switch(cdb[0]) { case INQUIRY: { struct scsi_inquiry *inqcdbp = (struct scsi_inquiry *) ccb->csio.cdb_io.cdb_bytes; struct scsi_inquiry_data *inqp = (struct scsi_inquiry_data *)ccb->csio.data_ptr; struct scsi_vpd_unit_serial_number *msnp = (struct scsi_vpd_unit_serial_number *)ccb->csio.data_ptr; if (inqcdbp->byte2 & SI_EVPD) { if (inqcdbp->page_code == SVPD_UNIT_SERIAL_NUMBER) { bzero(msnp, min(sizeof(*msnp), ccb->csio.dxfer_len)); msnp->device = T_DIRECT; msnp->page_code = SVPD_UNIT_SERIAL_NUMBER; msnp->length = min(strlen(atadev->param.serial), SVPD_SERIAL_NUM_SIZE); strncpy(msnp->serial_num, atadev->param.serial, msnp->length); ccb->ccb_h.status = CAM_REQ_CMP; break; } else { ccb->ccb_h.status = CAM_REQ_INVALID; break; } } bzero(inqp, min(sizeof(struct scsi_inquiry_data), ccb->csio.dxfer_len)); inqp->device = T_DIRECT; ata_cam_inqdata(atadev, inqp); ccb->ccb_h.status = CAM_REQ_CMP; break; } case READ_CAPACITY: { u_int32_t lba = atadev->param.cylinders * atadev->param.heads * atadev->param.sectors; u_int32_t lba28 = ((u_int32_t)atadev->param.lba_size_1) | ((u_int32_t)atadev->param.lba_size_2 << 16); u_int64_t lba48 = ((u_int64_t)atadev->param.lba_size48_1) | ((u_int64_t)atadev->param.lba_size48_2 << 16) | ((u_int64_t)atadev->param.lba_size48_3 << 32) | ((u_int64_t)atadev->param.lba_size48_4 << 48); struct scsi_read_capacity_data *cap32 = (struct scsi_read_capacity_data *)ccb->csio.data_ptr; device_printf(dev, "lba=%d lba28=%d lba48=%lld\n", lba, lba28, lba48); /* use the 48bit LBA size if valid */ if ((atadev->param.support.command2 & ATA_SUPPORT_ADDRESS48) && lba48 > 268435455) { scsi_ulto4b(lba48, cap32->addr); } else { if (atadev->param.cylinders == 16383 || lba < lba28) scsi_ulto4b(lba28, cap32->addr); else scsi_ulto4b(lba, cap32->addr); } scsi_ulto4b(DEV_BSIZE, cap32->length); device_printf(dev, "size = %d\n", scsi_4btoul(cap32->addr)); ccb->ccb_h.status = CAM_REQ_CMP; break; } case SYNCHRONIZE_CACHE: if ((atadev->param.support.command2 & ATA_SUPPORT_FLUSHCACHE) && ata_controlcmd(atadev, ATA_FLUSHCACHE, 0, 0, 0)) ccb->ccb_h.status = CAM_REQ_CMP_ERR; else ccb->ccb_h.status = CAM_REQ_CMP; break; case TEST_UNIT_READY: /* SOS we should check that a device is there */ ccb->ccb_h.status = CAM_REQ_CMP; break; case READ_6: case WRITE_6: printf("unsupported READ/WRITE 6 command\n"); ccb->ccb_h.status = CAM_REQ_INVALID; break; case READ_10: case WRITE_10: { struct ata_request *request; ccb->ccb_h.status = CAM_REQ_CMP_ERR; break; if(!(request = ata_alloc_request())) { ccb->ccb_h.status = CAM_REQ_CMP_ERR; break; } request->dev = dev; request->driver = ccb; request->callback = ata_cam_done; request->timeout = ccb->ccb_h.timeout; request->retries = 1; request->data = ccb->csio.data_ptr; request->bytecount = ccb->csio.dxfer_len; /* SOS check for not LBA capable device */ request->u.ata.lba = (ccb->csio.cdb_io.cdb_bytes[2] << 24) + (ccb->csio.cdb_io.cdb_bytes[3] << 16) + (ccb->csio.cdb_io.cdb_bytes[4] << 8) + ccb->csio.cdb_io.cdb_bytes[5]; request->u.ata.count = request->bytecount / DEV_BSIZE; if (atadev->mode >= ATA_DMA) request->transfersize = min(request->bytecount,ch->dma->max_iosize); else request->transfersize = min(request->bytecount, 65536); switch (ccb->ccb_h.flags & CAM_DIR_MASK) { case CAM_DIR_IN: request->flags = ATA_R_READ; if (atadev->mode >= ATA_DMA) { request->u.ata.command = ATA_READ_DMA; request->flags |= ATA_R_DMA; } else if (request->transfersize > DEV_BSIZE) request->u.ata.command = ATA_READ_MUL; else request->u.ata.command = ATA_READ; break; case CAM_DIR_OUT: request->flags = ATA_R_WRITE; if (atadev->mode >= ATA_DMA) { request->u.ata.command = ATA_WRITE_DMA; request->flags |= ATA_R_DMA; } else if (request->transfersize > DEV_BSIZE) request->u.ata.command = ATA_WRITE_MUL; else request->u.ata.command = ATA_WRITE; break; default: device_printf(dev, "unknown IO operation\n"); ccb->ccb_h.status = CAM_REQ_INVALID; ata_free_request(request); break; } ata_queue_request(request); return; } default: ccb->ccb_h.status = CAM_REQ_INVALID; } xpt_done(ccb); } static void ata_cam_io_atapi(struct cam_sim *sim, union ccb *ccb) { device_t dev = cam_sim_softc(sim); struct ata_cam_softc *acp = device_get_softc(dev); struct ata_channel *ch = device_get_softc(device_get_parent(dev)); struct ata_device *atadev = acp->devices[ccb->ccb_h.target_id]; struct ata_request *request; u_int8_t *cdb = ccb->csio.cdb_io.cdb_bytes; printf("io_atapi\n"); ccb->ccb_h.status = CAM_REQ_CMP_ERR; xpt_done(ccb); return; switch(cdb[0]) { case INQUIRY: { struct scsi_inquiry_data *inqp = (struct scsi_inquiry_data *)ccb->csio.data_ptr; bzero(inqp, min(sizeof(struct scsi_inquiry_data), ccb->csio.dxfer_len)); inqp->device = (atadev->param.config & ATA_ATAPI_TYPE_MASK) >> 8; ata_cam_inqdata(atadev, inqp); ccb->ccb_h.status = CAM_REQ_CMP; xpt_done(ccb); return; } } if (!(request = ata_alloc_request())) { ccb->ccb_h.status = CAM_REQ_CMP_ERR; xpt_done(ccb); return; } request->dev = dev; request->driver = ccb; request->callback = ata_cam_done; request->timeout = ccb->ccb_h.timeout; request->retries = 1; bcopy(ccb->csio.cdb_io.cdb_bytes, request->u.atapi.ccb, min(ccb->csio.cdb_len, ((atadev->param.config & ATA_PROTO_MASK) == ATA_PROTO_ATAPI_12) ? 12 : 16)); request->data = ccb->csio.data_ptr; request->bytecount = ccb->csio.dxfer_len; request->flags = ATA_R_ATAPI; if (request->bytecount && atadev->mode >= ATA_DMA) { request->transfersize = min(request->bytecount, ch->dma->max_iosize); request->flags |= ATA_R_DMA; } request->transfersize = min(request->bytecount, 65534); switch (ccb->ccb_h.flags & CAM_DIR_MASK) { case CAM_DIR_IN: request->flags |= ATA_R_READ; break; case CAM_DIR_OUT: request->flags |= ATA_R_WRITE; break; case CAM_DIR_NONE: break; default: device_printf(dev, "unknown IO operation\n"); ccb->ccb_h.status = CAM_REQ_INVALID; ata_free_request(request); xpt_done(ccb); return; } ccb->ccb_h.status |= CAM_SIM_QUEUED; ata_queue_request(request); } static void ata_cam_done(struct ata_request *request) { union ccb *ccb = request->driver; mtx_lock(&Giant); ccb->ccb_h.status &= ~(CAM_STATUS_MASK | CAM_SIM_QUEUED); if (request->result) { ccb->ccb_h.status = CAM_SCSI_STATUS_ERROR; if (request->flags & ATA_R_ATAPI) { bcopy(&request->u.atapi.sense_data, &ccb->csio.sense_data, sizeof(struct atapi_sense)); ccb->csio.ccb_h.status |= CAM_AUTOSNS_VALID; } ccb->csio.scsi_status = SCSI_STATUS_CHECK_COND; } else { ccb->ccb_h.status = CAM_REQ_CMP; ccb->csio.scsi_status = SCSI_STATUS_OK; } ccb->csio.resid = request->bytecount - request->donecount; if (ccb->csio.resid < 0) { printf("doh! resid < 0\n"); ccb->csio.resid = 0; } xpt_done(ccb); mtx_unlock(&Giant); ata_free_request(request); } static void ata_cam_inqdata(struct ata_device *atadev, struct scsi_inquiry_data *inqp) { char marker = 0; inqp->dev_qual2 = SID_QUAL2; inqp->version = SCSI_REV_2; inqp->additional_length = 4; /* try to seperate the ATA model string into vendor and model parts */ if (index(atadev->param.model, ' ')) marker = ' '; else if (index(atadev->param.model, '-')) marker = '-'; if (marker) { strncpy(inqp->vendor, atadev->param.model, min(index(atadev->param.model, marker) - (char *)atadev->param.model, SID_VENDOR_SIZE)); strncpy(inqp->product, atadev->param.model + min(index(atadev->param.model, marker) - (char *)atadev->param.model + 1, SID_VENDOR_SIZE), SID_PRODUCT_SIZE); } else { if (!strncmp(atadev->param.model, "ST", 2)) strcpy(inqp->vendor, "Seagate"); else strcpy(inqp->vendor, "Unknown"); strncpy(inqp->product, atadev->param.model, SID_PRODUCT_SIZE); } strncpy(inqp->revision, atadev->param.revision, SID_REVISION_SIZE); } static device_method_t ata_cam_methods[] = { /* device interface */ DEVMETHOD(device_probe, ata_cam_probe), DEVMETHOD(device_attach, ata_cam_attach), DEVMETHOD(device_detach, ata_cam_detach), /* ATA methods */ DEVMETHOD(ata_reinit, ata_cam_reinit), { 0, 0 } }; static driver_t ata_cam_driver = { "atacam", ata_cam_methods, sizeof(struct ata_cam_softc) }; static devclass_t ata_cam_devclass; static int ata_cam_modevent(module_t mod, int what, void *arg) { device_t *devs; int ndevs, i; if (what == MOD_UNLOAD) { if (!devclass_get_devices(ata_cam_devclass, &devs, &ndevs) && devs) { for (i = 0; i < ndevs; i++) device_delete_child(device_get_parent(devs[i]), devs[i]); free(devs, M_TEMP); } } return 0; } DRIVER_MODULE(atacam, ata, ata_cam_driver, ata_cam_devclass, ata_cam_modevent, NULL); MODULE_DEPEND(atacam, ata, 1, 1, 1);