static void ptasync(void *callback_arg, u_int32_t code, struct cam_path *path, void *arg) { struct cam_periph *periph; periph = (struct cam_periph *)callback_arg; switch (code) { case AC_FOUND_DEVICE: { struct ccb_getdev *cgd; cam_status status; cgd = (struct ccb_getdev *)arg; if (cgd == NULL) break; if (cgd->protocol != PROTO_SCSI) break; if (SID_TYPE(&cgd->inq_data) != T_PROCESSOR) break; /* * Allocate a peripheral instance for * this device and start the probe * process. */ status = cam_periph_alloc(ptctor, ptoninvalidate, ptdtor, ptstart, "pt", CAM_PERIPH_BIO, cgd->ccb_h.path, ptasync, AC_FOUND_DEVICE, cgd); if (status != CAM_REQ_CMP && status != CAM_REQ_INPROG) printf("ptasync: Unable to attach to new device " "due to status 0x%x\n", status); break; } case AC_SENT_BDR: case AC_BUS_RESET: { struct pt_softc *softc; struct ccb_hdr *ccbh; softc = (struct pt_softc *)periph->softc; /* * Don't fail on the expected unit attention * that will occur. */ softc->flags |= PT_FLAG_RETRY_UA; LIST_FOREACH(ccbh, &softc->pending_ccbs, periph_links.le) ccbh->ccb_state |= PT_CCB_RETRY_UA; } /* FALLTHROUGH */ default: cam_periph_async(periph, code, path, arg); break; } }
/******************************************************************************** * Handle completion of a command submitted via CAM. * Completion for extended cdb */ static void amr_cam_complete_extcdb(struct amr_command *ac) { struct amr_ext_passthrough *aep = (struct amr_ext_passthrough *)ac->ac_data; struct ccb_scsiio *csio = (struct ccb_scsiio *)ac->ac_private; struct scsi_inquiry_data *inq = (struct scsi_inquiry_data *)csio->data_ptr; /* XXX note that we're ignoring ac->ac_status - good idea? */ debug(1, "status 0x%x scsi_status 0x%x", ac->ac_status, aep->ap_scsi_status); /* * Hide disks from CAM so that they're not picked up and treated as 'normal' disks. * * If the configuration provides a mechanism to mark a disk a "not managed", we * could add handling for that to allow disks to be selectively visible. */ if ((aep->ap_cdb[0] == INQUIRY) && (SID_TYPE(inq) == T_DIRECT)) { bzero(csio->data_ptr, csio->dxfer_len); if (aep->ap_scsi_status == 0xf0) { csio->ccb_h.status = CAM_SCSI_STATUS_ERROR; } else { csio->ccb_h.status = CAM_DEV_NOT_THERE; } } else { /* handle passthrough SCSI status */ switch(aep->ap_scsi_status) { case 0: /* completed OK */ csio->ccb_h.status = CAM_REQ_CMP; break; case 0x02: csio->ccb_h.status = CAM_SCSI_STATUS_ERROR; csio->scsi_status = SCSI_STATUS_CHECK_COND; bcopy(aep->ap_request_sense_area, &csio->sense_data, AMR_MAX_REQ_SENSE_LEN); csio->sense_len = AMR_MAX_REQ_SENSE_LEN; csio->ccb_h.status |= CAM_AUTOSNS_VALID; break; case 0x08: csio->ccb_h.status = CAM_SCSI_BUSY; break; case 0xf0: case 0xf4: default: csio->ccb_h.status = CAM_REQ_CMP_ERR; break; } } free(aep, M_DEVBUF); if ((csio->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE) debug(2, "%*D\n", imin(csio->dxfer_len, 16), csio->data_ptr, " "); xpt_done((union ccb *)csio); amr_releasecmd(ac); }
static cam_status ptctor(struct cam_periph *periph, void *arg) { struct pt_softc *softc; struct ccb_getdev *cgd; cgd = (struct ccb_getdev *)arg; if (periph == NULL) { kprintf("ptregister: periph was NULL!!\n"); return(CAM_REQ_CMP_ERR); } if (cgd == NULL) { kprintf("ptregister: no getdev CCB, can't register device\n"); return(CAM_REQ_CMP_ERR); } softc = kmalloc(sizeof(*softc), M_DEVBUF, M_INTWAIT | M_ZERO); LIST_INIT(&softc->pending_ccbs); softc->state = PT_STATE_NORMAL; bioq_init(&softc->bio_queue); softc->io_timeout = SCSI_PT_DEFAULT_TIMEOUT * 1000; periph->softc = softc; cam_periph_unlock(periph); cam_extend_set(ptperiphs, periph->unit_number, periph); devstat_add_entry(&softc->device_stats, "pt", periph->unit_number, 0, DEVSTAT_NO_BLOCKSIZE, SID_TYPE(&cgd->inq_data) | DEVSTAT_TYPE_IF_SCSI, DEVSTAT_PRIORITY_OTHER); make_dev(&pt_ops, periph->unit_number, UID_ROOT, GID_OPERATOR, 0600, "%s%d", periph->periph_name, periph->unit_number); cam_periph_lock(periph); /* * Add async callbacks for bus reset and * bus device reset calls. I don't bother * checking if this fails as, in most cases, * the system will function just fine without * them and the only alternative would be to * not attach the device on failure. */ xpt_register_async(AC_SENT_BDR | AC_BUS_RESET | AC_LOST_DEVICE, ptasync, periph, periph->path); /* Tell the user we've attached to the device */ xpt_announce_periph(periph, NULL); return(CAM_REQ_CMP); }
/* * Open a given device. The path argument isn't strictly necessary, but it * is copied into the cam_device structure as a convenience to the user. */ static struct cam_device * cam_real_open_device(const char *path, int flags, struct cam_device *device, const char *given_path, const char *given_dev_name, int given_unit_number) { char *func_name = "cam_real_open_device"; union ccb ccb; int fd = -1, malloced_device = 0; /* * See if the user wants us to malloc a device for him. */ if (device == NULL) { if ((device = (struct cam_device *)malloc( sizeof(struct cam_device))) == NULL) { snprintf(cam_errbuf, CAM_ERRBUF_SIZE, "%s: device structure malloc" " failed\n%s: %s", func_name, func_name, strerror(errno)); return(NULL); } device->fd = -1; malloced_device = 1; } /* * If the user passed in a path, save it for him. */ if (given_path != NULL) strlcpy(device->device_path, given_path, sizeof(device->device_path)); else device->device_path[0] = '\0'; /* * If the user passed in a device name and unit number pair, save * those as well. */ if (given_dev_name != NULL) strlcpy(device->given_dev_name, given_dev_name, sizeof(device->given_dev_name)); else device->given_dev_name[0] = '\0'; device->given_unit_number = given_unit_number; if ((fd = open(path, flags)) < 0) { snprintf(cam_errbuf, CAM_ERRBUF_SIZE, "%s: couldn't open passthrough device %s\n" "%s: %s", func_name, path, func_name, strerror(errno)); goto crod_bailout; } device->fd = fd; bzero(&ccb, sizeof(union ccb)); /* * Unlike the transport layer version of the GETPASSTHRU ioctl, * we don't have to set any fields. */ ccb.ccb_h.func_code = XPT_GDEVLIST; /* * We're only doing this to get some information on the device in * question. Otherwise, we'd have to pass in yet another * parameter: the passthrough driver unit number. */ if (ioctl(fd, CAMGETPASSTHRU, &ccb) == -1) { /* * At this point we know the passthrough device must exist * because we just opened it above. The only way this * ioctl can fail is if the ccb size is wrong. */ snprintf(cam_errbuf, CAM_ERRBUF_SIZE, "%s: CAMGETPASSTHRU ioctl failed\n" "%s: %s", func_name, func_name, strerror(errno)); goto crod_bailout; } /* * If the ioctl returned the right status, but we got an error back * in the ccb, that means that the kernel found the device the user * passed in, but was unable to find the passthrough device for * the device the user gave us. */ if (ccb.cgdl.status == CAM_GDEVLIST_ERROR) { snprintf(cam_errbuf, CAM_ERRBUF_SIZE, "%s: passthrough device does not exist!", func_name); goto crod_bailout; } device->dev_unit_num = ccb.cgdl.unit_number; strlcpy(device->device_name, ccb.cgdl.periph_name, sizeof(device->device_name)); device->path_id = ccb.ccb_h.path_id; device->target_id = ccb.ccb_h.target_id; device->target_lun = ccb.ccb_h.target_lun; ccb.ccb_h.func_code = XPT_PATH_INQ; if (ioctl(fd, CAMIOCOMMAND, &ccb) == -1) { snprintf(cam_errbuf, CAM_ERRBUF_SIZE, "%s: Path Inquiry CCB failed\n" "%s: %s", func_name, func_name, strerror(errno)); goto crod_bailout; } strlcpy(device->sim_name, ccb.cpi.dev_name, sizeof(device->sim_name)); device->sim_unit_number = ccb.cpi.unit_number; device->bus_id = ccb.cpi.bus_id; /* * It doesn't really matter what is in the payload for a getdev * CCB, the kernel doesn't look at it. */ ccb.ccb_h.func_code = XPT_GDEV_TYPE; if (ioctl(fd, CAMIOCOMMAND, &ccb) == -1) { snprintf(cam_errbuf, CAM_ERRBUF_SIZE, "%s: Get Device Type CCB failed\n" "%s: %s", func_name, func_name, strerror(errno)); goto crod_bailout; } device->pd_type = SID_TYPE(&ccb.cgd.inq_data); bcopy(&ccb.cgd.inq_data, &device->inq_data, sizeof(struct scsi_inquiry_data)); device->serial_num_len = ccb.cgd.serial_num_len; bcopy(&ccb.cgd.serial_num, &device->serial_num, device->serial_num_len); /* * Zero the payload, the kernel does look at the flags. */ bzero(&(&ccb.ccb_h)[1], sizeof(struct ccb_trans_settings)); /* * Get transfer settings for this device. */ ccb.ccb_h.func_code = XPT_GET_TRAN_SETTINGS; ccb.cts.type = CTS_TYPE_CURRENT_SETTINGS; if (ioctl(fd, CAMIOCOMMAND, &ccb) == -1) { snprintf(cam_errbuf, CAM_ERRBUF_SIZE, "%s: Get Transfer Settings CCB failed\n" "%s: %s", func_name, func_name, strerror(errno)); goto crod_bailout; } if (ccb.cts.transport == XPORT_SPI) { struct ccb_trans_settings_spi *spi = &ccb.cts.xport_specific.spi; device->sync_period = spi->sync_period; device->sync_offset = spi->sync_offset; device->bus_width = spi->bus_width; } else { device->sync_period = 0; device->sync_offset = 0; device->bus_width = 0; } return(device); crod_bailout: if (fd >= 0) close(fd); if (malloced_device) free(device); return(NULL); }
/********************************************************************** * Handle completion of a command submitted via CAM. */ static void amr_cam_complete(struct amr_command *ac) { struct amr_passthrough *ap; struct amr_ext_passthrough *aep; struct ccb_scsiio *csio; struct scsi_inquiry_data *inq; int scsi_status, cdb0; ap = &ac->ac_ccb->ccb_pthru; aep = &ac->ac_ccb->ccb_epthru; csio = (struct ccb_scsiio *)ac->ac_private; inq = (struct scsi_inquiry_data *)csio->data_ptr; if (ac->ac_mailbox.mb_command == AMR_CMD_EXTPASS) scsi_status = aep->ap_scsi_status; else scsi_status = ap->ap_scsi_status; debug(1, "status 0x%x AP scsi_status 0x%x", ac->ac_status, scsi_status); /* Make sure the status is sane */ if ((ac->ac_status != AMR_STATUS_SUCCESS) && (scsi_status == 0)) { csio->ccb_h.status = CAM_REQ_CMP_ERR; goto out; } /* * Hide disks from CAM so that they're not picked up and treated as * 'normal' disks. * * If the configuration provides a mechanism to mark a disk a "not * managed", we could add handling for that to allow disks to be * selectively visible. */ /* handle passthrough SCSI status */ switch(scsi_status) { case 0: /* completed OK */ if (ac->ac_mailbox.mb_command == AMR_CMD_EXTPASS) cdb0 = aep->ap_cdb[0]; else cdb0 = ap->ap_cdb[0]; if ((cdb0 == INQUIRY) && (SID_TYPE(inq) == T_DIRECT)) inq->device = (inq->device & 0xe0) | T_NODEVICE; csio->ccb_h.status = CAM_REQ_CMP; break; case 0x02: csio->ccb_h.status = CAM_SCSI_STATUS_ERROR; csio->scsi_status = SCSI_STATUS_CHECK_COND; if (ac->ac_mailbox.mb_command == AMR_CMD_EXTPASS) bcopy(aep->ap_request_sense_area, &csio->sense_data, AMR_MAX_REQ_SENSE_LEN); else bcopy(ap->ap_request_sense_area, &csio->sense_data, AMR_MAX_REQ_SENSE_LEN); csio->sense_len = AMR_MAX_REQ_SENSE_LEN; csio->ccb_h.status |= CAM_AUTOSNS_VALID; break; case 0x08: csio->ccb_h.status = CAM_SCSI_BUSY; break; case 0xf0: case 0xf4: default: /* * Non-zero LUNs are already filtered, so there's no need * to return CAM_DEV_NOT_THERE. */ csio->ccb_h.status = CAM_SEL_TIMEOUT; break; } out: if ((csio->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE) debug(2, "%*D\n", imin(csio->dxfer_len, 16), csio->data_ptr, " "); mtx_lock(&ac->ac_sc->amr_list_lock); xpt_done((union ccb *)csio); amr_releasecmd(ac); mtx_unlock(&ac->ac_sc->amr_list_lock); }
static cam_status ptctor(struct cam_periph *periph, void *arg) { struct pt_softc *softc; struct ccb_setasync csa; struct ccb_getdev *cgd; cgd = (struct ccb_getdev *)arg; if (periph == NULL) { printf("ptregister: periph was NULL!!\n"); return(CAM_REQ_CMP_ERR); } if (cgd == NULL) { printf("ptregister: no getdev CCB, can't register device\n"); return(CAM_REQ_CMP_ERR); } softc = (struct pt_softc *)malloc(sizeof(*softc),M_DEVBUF,M_NOWAIT); if (softc == NULL) { printf("daregister: Unable to probe new device. " "Unable to allocate softc\n"); return(CAM_REQ_CMP_ERR); } bzero(softc, sizeof(*softc)); LIST_INIT(&softc->pending_ccbs); softc->state = PT_STATE_NORMAL; bioq_init(&softc->bio_queue); softc->io_timeout = SCSI_PT_DEFAULT_TIMEOUT * 1000; periph->softc = softc; devstat_add_entry(&softc->device_stats, "pt", periph->unit_number, 0, DEVSTAT_NO_BLOCKSIZE, SID_TYPE(&cgd->inq_data) | DEVSTAT_TYPE_IF_SCSI, DEVSTAT_PRIORITY_OTHER); softc->dev = make_dev(&pt_cdevsw, periph->unit_number, UID_ROOT, GID_OPERATOR, 0600, "%s%d", periph->periph_name, periph->unit_number); softc->dev->si_drv1 = periph; /* * Add async callbacks for bus reset and * bus device reset calls. I don't bother * checking if this fails as, in most cases, * the system will function just fine without * them and the only alternative would be to * not attach the device on failure. */ xpt_setup_ccb(&csa.ccb_h, periph->path, /*priority*/5); csa.ccb_h.func_code = XPT_SASYNC_CB; csa.event_enable = AC_SENT_BDR | AC_BUS_RESET | AC_LOST_DEVICE; csa.callback = ptasync; csa.callback_arg = periph; xpt_action((union ccb *)&csa); /* Tell the user we've attached to the device */ xpt_announce_periph(periph, NULL); return(CAM_REQ_CMP); }