2 * Implementation of SCSI Processor Target Peripheral driver for CAM.
4 * Copyright (c) 1998 Justin T. Gibbs.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions, and the following disclaimer,
12 * without modification, immediately at the beginning of the file.
13 * 2. The name of the author may not be used to endorse or promote products
14 * derived from this software without specific prior written permission.
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
20 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * $FreeBSD: src/sys/cam/scsi/scsi_pt.c,v 1.17 2000/01/17 06:27:37 mjacob Exp $
29 * $DragonFly: src/sys/bus/cam/scsi/scsi_pt.c,v 1.2 2003/06/17 04:28:19 dillon Exp $
32 #include <sys/param.h>
33 #include <sys/queue.h>
34 #include <sys/systm.h>
35 #include <sys/kernel.h>
36 #include <sys/types.h>
38 #include <sys/devicestat.h>
39 #include <sys/malloc.h>
44 #include <cam/cam_ccb.h>
45 #include <cam/cam_extend.h>
46 #include <cam/cam_periph.h>
47 #include <cam/cam_xpt_periph.h>
48 #include <cam/cam_debug.h>
50 #include <cam/scsi/scsi_all.h>
51 #include <cam/scsi/scsi_message.h>
52 #include <cam/scsi/scsi_pt.h>
64 PT_FLAG_DEVICE_INVALID = 0x02,
65 PT_FLAG_RETRY_UA = 0x04
69 PT_CCB_BUFFER_IO = 0x01,
70 PT_CCB_WAITING = 0x02,
71 PT_CCB_RETRY_UA = 0x04,
72 PT_CCB_BUFFER_IO_UA = PT_CCB_BUFFER_IO|PT_CCB_RETRY_UA
75 /* Offsets into our private area for storing information */
76 #define ccb_state ppriv_field0
77 #define ccb_bp ppriv_ptr1
80 struct buf_queue_head buf_queue;
81 struct devstat device_stats;
82 LIST_HEAD(, ccb_hdr) pending_ccbs;
90 static d_open_t ptopen;
91 static d_close_t ptclose;
92 static d_strategy_t ptstrategy;
93 static periph_init_t ptinit;
94 static void ptasync(void *callback_arg, u_int32_t code,
95 struct cam_path *path, void *arg);
96 static periph_ctor_t ptctor;
97 static periph_oninv_t ptoninvalidate;
98 static periph_dtor_t ptdtor;
99 static periph_start_t ptstart;
100 static void ptdone(struct cam_periph *periph,
101 union ccb *done_ccb);
102 static d_ioctl_t ptioctl;
103 static int pterror(union ccb *ccb, u_int32_t cam_flags,
104 u_int32_t sense_flags);
106 void scsi_send_receive(struct ccb_scsiio *csio, u_int32_t retries,
107 void (*cbfcnp)(struct cam_periph *, union ccb *),
108 u_int tag_action, int readop, u_int byte2,
109 u_int32_t xfer_len, u_int8_t *data_ptr,
110 u_int8_t sense_len, u_int32_t timeout);
112 static struct periph_driver ptdriver =
115 TAILQ_HEAD_INITIALIZER(ptdriver.units), /* generation */ 0
118 DATA_SET(periphdriver_set, ptdriver);
120 #define PT_CDEV_MAJOR 61
122 static struct cdevsw pt_cdevsw = {
126 /* write */ physwrite,
130 /* strategy */ ptstrategy,
132 /* maj */ PT_CDEV_MAJOR,
139 static struct extend_array *ptperiphs;
141 #ifndef SCSI_PT_DEFAULT_TIMEOUT
142 #define SCSI_PT_DEFAULT_TIMEOUT 60
146 ptopen(dev_t dev, int flags, int fmt, struct proc *p)
148 struct cam_periph *periph;
149 struct pt_softc *softc;
155 periph = cam_extend_get(ptperiphs, unit);
159 softc = (struct pt_softc *)periph->softc;
162 if (softc->flags & PT_FLAG_DEVICE_INVALID) {
167 CAM_DEBUG(periph->path, CAM_DEBUG_TRACE,
168 ("ptopen: dev=%s (unit %d)\n", devtoname(dev), unit));
170 if ((error = cam_periph_lock(periph, PRIBIO|PCATCH)) != 0) {
172 return (error); /* error code from tsleep */
177 if ((softc->flags & PT_FLAG_OPEN) == 0) {
178 if (cam_periph_acquire(periph) != CAM_REQ_CMP)
181 softc->flags |= PT_FLAG_OPEN;
185 cam_periph_unlock(periph);
190 ptclose(dev_t dev, int flag, int fmt, struct proc *p)
192 struct cam_periph *periph;
193 struct pt_softc *softc;
198 periph = cam_extend_get(ptperiphs, unit);
202 softc = (struct pt_softc *)periph->softc;
204 if ((error = cam_periph_lock(periph, PRIBIO)) != 0)
205 return (error); /* error code from tsleep */
207 softc->flags &= ~PT_FLAG_OPEN;
208 cam_periph_unlock(periph);
209 cam_periph_release(periph);
214 * Actually translate the requested transfer into one the physical driver
215 * can understand. The transfer is described by a buf and will include
216 * only one physical transfer.
219 ptstrategy(struct buf *bp)
221 struct cam_periph *periph;
222 struct pt_softc *softc;
226 unit = minor(bp->b_dev);
227 periph = cam_extend_get(ptperiphs, unit);
228 if (periph == NULL) {
232 softc = (struct pt_softc *)periph->softc;
235 * Mask interrupts so that the pack cannot be invalidated until
236 * after we are in the queue. Otherwise, we might not properly
237 * clean up one of the buffers.
242 * If the device has been made invalid, error out
244 if ((softc->flags & PT_FLAG_DEVICE_INVALID)) {
251 * Place it in the queue of disk activities for this disk
253 bufq_insert_tail(&softc->buf_queue, bp);
258 * Schedule ourselves for performing the work.
260 xpt_schedule(periph, /* XXX priority */1);
264 bp->b_flags |= B_ERROR;
267 * Correctly set the buf to indicate a completed xfer
269 bp->b_resid = bp->b_bcount;
277 struct cam_path *path;
280 * Create our extend array for storing the devices we attach to.
282 ptperiphs = cam_extend_new();
283 if (ptperiphs == NULL) {
284 printf("pt: Failed to alloc extend array!\n");
289 * Install a global async callback. This callback will
290 * receive async callbacks like "new device found".
292 status = xpt_create_path(&path, /*periph*/NULL, CAM_XPT_PATH_ID,
293 CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD);
295 if (status == CAM_REQ_CMP) {
296 struct ccb_setasync csa;
298 xpt_setup_ccb(&csa.ccb_h, path, /*priority*/5);
299 csa.ccb_h.func_code = XPT_SASYNC_CB;
300 csa.event_enable = AC_FOUND_DEVICE;
301 csa.callback = ptasync;
302 csa.callback_arg = NULL;
303 xpt_action((union ccb *)&csa);
304 status = csa.ccb_h.status;
308 if (status != CAM_REQ_CMP) {
309 printf("pt: Failed to attach master async callback "
310 "due to status 0x%x!\n", status);
315 ptctor(struct cam_periph *periph, void *arg)
317 struct pt_softc *softc;
318 struct ccb_setasync csa;
319 struct ccb_getdev *cgd;
321 cgd = (struct ccb_getdev *)arg;
322 if (periph == NULL) {
323 printf("ptregister: periph was NULL!!\n");
324 return(CAM_REQ_CMP_ERR);
328 printf("ptregister: no getdev CCB, can't register device\n");
329 return(CAM_REQ_CMP_ERR);
332 softc = (struct pt_softc *)malloc(sizeof(*softc),M_DEVBUF,M_NOWAIT);
335 printf("daregister: Unable to probe new device. "
336 "Unable to allocate softc\n");
337 return(CAM_REQ_CMP_ERR);
340 bzero(softc, sizeof(*softc));
341 LIST_INIT(&softc->pending_ccbs);
342 softc->state = PT_STATE_NORMAL;
343 bufq_init(&softc->buf_queue);
345 softc->io_timeout = SCSI_PT_DEFAULT_TIMEOUT * 1000;
347 periph->softc = softc;
349 cam_extend_set(ptperiphs, periph->unit_number, periph);
351 devstat_add_entry(&softc->device_stats, "pt",
352 periph->unit_number, 0,
353 DEVSTAT_NO_BLOCKSIZE,
354 SID_TYPE(&cgd->inq_data) | DEVSTAT_TYPE_IF_SCSI,
355 DEVSTAT_PRIORITY_OTHER);
357 softc->dev = make_dev(&pt_cdevsw, periph->unit_number, UID_ROOT,
358 GID_OPERATOR, 0600, "%s%d", periph->periph_name,
359 periph->unit_number);
361 * Add async callbacks for bus reset and
362 * bus device reset calls. I don't bother
363 * checking if this fails as, in most cases,
364 * the system will function just fine without
365 * them and the only alternative would be to
366 * not attach the device on failure.
368 xpt_setup_ccb(&csa.ccb_h, periph->path, /*priority*/5);
369 csa.ccb_h.func_code = XPT_SASYNC_CB;
370 csa.event_enable = AC_SENT_BDR | AC_BUS_RESET | AC_LOST_DEVICE;
371 csa.callback = ptasync;
372 csa.callback_arg = periph;
373 xpt_action((union ccb *)&csa);
375 /* Tell the user we've attached to the device */
376 xpt_announce_periph(periph, NULL);
382 ptoninvalidate(struct cam_periph *periph)
385 struct pt_softc *softc;
387 struct ccb_setasync csa;
389 softc = (struct pt_softc *)periph->softc;
392 * De-register any async callbacks.
394 xpt_setup_ccb(&csa.ccb_h, periph->path,
396 csa.ccb_h.func_code = XPT_SASYNC_CB;
397 csa.event_enable = 0;
398 csa.callback = ptasync;
399 csa.callback_arg = periph;
400 xpt_action((union ccb *)&csa);
402 softc->flags |= PT_FLAG_DEVICE_INVALID;
405 * Although the oninvalidate() routines are always called at
406 * splsoftcam, we need to be at splbio() here to keep the buffer
407 * queue from being modified while we traverse it.
412 * Return all queued I/O with ENXIO.
413 * XXX Handle any transactions queued to the card
414 * with XPT_ABORT_CCB.
416 while ((q_bp = bufq_first(&softc->buf_queue)) != NULL){
417 bufq_remove(&softc->buf_queue, q_bp);
418 q_bp->b_resid = q_bp->b_bcount;
419 q_bp->b_error = ENXIO;
420 q_bp->b_flags |= B_ERROR;
426 xpt_print_path(periph->path);
427 printf("lost device\n");
431 ptdtor(struct cam_periph *periph)
433 struct pt_softc *softc;
435 softc = (struct pt_softc *)periph->softc;
437 devstat_remove_entry(&softc->device_stats);
439 destroy_dev(softc->dev);
441 cam_extend_release(ptperiphs, periph->unit_number);
442 xpt_print_path(periph->path);
443 printf("removing device entry\n");
444 free(softc, M_DEVBUF);
448 ptasync(void *callback_arg, u_int32_t code, struct cam_path *path, void *arg)
450 struct cam_periph *periph;
452 periph = (struct cam_periph *)callback_arg;
454 case AC_FOUND_DEVICE:
456 struct ccb_getdev *cgd;
459 cgd = (struct ccb_getdev *)arg;
461 if (SID_TYPE(&cgd->inq_data) != T_PROCESSOR)
465 * Allocate a peripheral instance for
466 * this device and start the probe
469 status = cam_periph_alloc(ptctor, ptoninvalidate, ptdtor,
470 ptstart, "pt", CAM_PERIPH_BIO,
471 cgd->ccb_h.path, ptasync,
472 AC_FOUND_DEVICE, cgd);
474 if (status != CAM_REQ_CMP
475 && status != CAM_REQ_INPROG)
476 printf("ptasync: Unable to attach to new device "
477 "due to status 0x%x\n", status);
483 struct pt_softc *softc;
484 struct ccb_hdr *ccbh;
487 softc = (struct pt_softc *)periph->softc;
490 * Don't fail on the expected unit attention
493 softc->flags |= PT_FLAG_RETRY_UA;
494 for (ccbh = LIST_FIRST(&softc->pending_ccbs);
495 ccbh != NULL; ccbh = LIST_NEXT(ccbh, periph_links.le))
496 ccbh->ccb_state |= PT_CCB_RETRY_UA;
501 cam_periph_async(periph, code, path, arg);
507 ptstart(struct cam_periph *periph, union ccb *start_ccb)
509 struct pt_softc *softc;
513 softc = (struct pt_softc *)periph->softc;
516 * See if there is a buf with work for us to do..
519 bp = bufq_first(&softc->buf_queue);
520 if (periph->immediate_priority <= periph->pinfo.priority) {
521 CAM_DEBUG_PRINT(CAM_DEBUG_SUBTRACE,
522 ("queuing for immediate ccb\n"));
523 start_ccb->ccb_h.ccb_state = PT_CCB_WAITING;
524 SLIST_INSERT_HEAD(&periph->ccb_list, &start_ccb->ccb_h,
526 periph->immediate_priority = CAM_PRIORITY_NONE;
528 wakeup(&periph->ccb_list);
529 } else if (bp == NULL) {
531 xpt_release_ccb(start_ccb);
535 bufq_remove(&softc->buf_queue, bp);
537 devstat_start_transaction(&softc->device_stats);
539 scsi_send_receive(&start_ccb->csio,
543 bp->b_flags & B_READ,
547 /*sense_len*/SSD_FULL_SIZE,
548 /*timeout*/softc->io_timeout);
550 start_ccb->ccb_h.ccb_state = PT_CCB_BUFFER_IO;
553 * Block out any asyncronous callbacks
554 * while we touch the pending ccb list.
557 LIST_INSERT_HEAD(&softc->pending_ccbs, &start_ccb->ccb_h,
561 start_ccb->ccb_h.ccb_bp = bp;
562 bp = bufq_first(&softc->buf_queue);
565 xpt_action(start_ccb);
568 /* Have more work to do, so ensure we stay scheduled */
569 xpt_schedule(periph, /* XXX priority */1);
575 ptdone(struct cam_periph *periph, union ccb *done_ccb)
577 struct pt_softc *softc;
578 struct ccb_scsiio *csio;
580 softc = (struct pt_softc *)periph->softc;
581 csio = &done_ccb->csio;
582 switch (csio->ccb_h.ccb_state) {
583 case PT_CCB_BUFFER_IO:
584 case PT_CCB_BUFFER_IO_UA:
589 bp = (struct buf *)done_ccb->ccb_h.ccb_bp;
590 if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
595 if ((csio->ccb_h.ccb_state & PT_CCB_RETRY_UA) != 0)
600 sf |= SF_RETRY_SELTO;
602 if ((error = pterror(done_ccb, 0, sf)) == ERESTART) {
604 * A retry was scheuled, so
614 if (error == ENXIO) {
616 * Catastrophic error. Mark our device
619 xpt_print_path(periph->path);
620 printf("Invalidating device\n");
621 softc->flags |= PT_FLAG_DEVICE_INVALID;
625 * return all queued I/O with EIO, so that
626 * the client can retry these I/Os in the
627 * proper order should it attempt to recover.
629 while ((q_bp = bufq_first(&softc->buf_queue))
631 bufq_remove(&softc->buf_queue, q_bp);
632 q_bp->b_resid = q_bp->b_bcount;
634 q_bp->b_flags |= B_ERROR;
639 bp->b_resid = bp->b_bcount;
640 bp->b_flags |= B_ERROR;
642 bp->b_resid = csio->resid;
644 if (bp->b_resid != 0) {
645 /* Short transfer ??? */
646 bp->b_flags |= B_ERROR;
649 if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0)
650 cam_release_devq(done_ccb->ccb_h.path,
656 bp->b_resid = csio->resid;
657 if (bp->b_resid != 0)
658 bp->b_flags |= B_ERROR;
662 * Block out any asyncronous callbacks
663 * while we touch the pending ccb list.
666 LIST_REMOVE(&done_ccb->ccb_h, periph_links.le);
669 devstat_end_transaction_buf(&softc->device_stats, bp);
674 /* Caller will release the CCB */
675 wakeup(&done_ccb->ccb_h.cbfcnp);
678 xpt_release_ccb(done_ccb);
682 pterror(union ccb *ccb, u_int32_t cam_flags, u_int32_t sense_flags)
684 struct pt_softc *softc;
685 struct cam_periph *periph;
687 periph = xpt_path_periph(ccb->ccb_h.path);
688 softc = (struct pt_softc *)periph->softc;
690 return(cam_periph_error(ccb, cam_flags, sense_flags,
695 ptioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p)
697 struct cam_periph *periph;
698 struct pt_softc *softc;
703 periph = cam_extend_get(ptperiphs, unit);
708 softc = (struct pt_softc *)periph->softc;
710 if ((error = cam_periph_lock(periph, PRIBIO|PCATCH)) != 0) {
711 return (error); /* error code from tsleep */
715 case PTIOCGETTIMEOUT:
716 if (softc->io_timeout >= 1000)
717 *(int *)addr = softc->io_timeout / 1000;
721 case PTIOCSETTIMEOUT:
725 if (*(int *)addr < 1) {
731 softc->io_timeout = *(int *)addr * 1000;
737 error = cam_periph_ioctl(periph, cmd, addr, pterror);
741 cam_periph_unlock(periph);
747 scsi_send_receive(struct ccb_scsiio *csio, u_int32_t retries,
748 void (*cbfcnp)(struct cam_periph *, union ccb *),
749 u_int tag_action, int readop, u_int byte2,
750 u_int32_t xfer_len, u_int8_t *data_ptr, u_int8_t sense_len,
753 struct scsi_send_receive *scsi_cmd;
755 scsi_cmd = (struct scsi_send_receive *)&csio->cdb_io.cdb_bytes;
756 scsi_cmd->opcode = readop ? RECEIVE : SEND;
757 scsi_cmd->byte2 = byte2;
758 scsi_ulto3b(xfer_len, scsi_cmd->xfer_len);
759 scsi_cmd->control = 0;
764 /*flags*/readop ? CAM_DIR_IN : CAM_DIR_OUT,