8402d7ed9668aa21d2053333e2c754349a23efa4
[dragonfly.git] / sys / bus / cam / scsi / scsi_pt.c
1 /*
2  * Implementation of SCSI Processor Target Peripheral driver for CAM.
3  *
4  * Copyright (c) 1998 Justin T. Gibbs.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
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.
15  *
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
26  * SUCH DAMAGE.
27  *
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.4 2003/07/21 05:50:24 dillon Exp $
30  */
31
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>
37 #include <sys/buf.h>
38 #include <sys/devicestat.h>
39 #include <sys/malloc.h>
40 #include <sys/conf.h>
41 #include <sys/ptio.h>
42
43 #include <cam/cam.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>
49
50 #include <cam/scsi/scsi_all.h>
51 #include <cam/scsi/scsi_message.h>
52 #include <cam/scsi/scsi_pt.h>
53
54 #include "opt_pt.h"
55
56 typedef enum {
57         PT_STATE_PROBE,
58         PT_STATE_NORMAL
59 } pt_state;
60
61 typedef enum {
62         PT_FLAG_NONE            = 0x00,
63         PT_FLAG_OPEN            = 0x01,
64         PT_FLAG_DEVICE_INVALID  = 0x02,
65         PT_FLAG_RETRY_UA        = 0x04
66 } pt_flags;
67
68 typedef enum {
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
73 } pt_ccb_state;
74
75 /* Offsets into our private area for storing information */
76 #define ccb_state       ppriv_field0
77 #define ccb_bp          ppriv_ptr1
78
79 struct pt_softc {
80         struct   buf_queue_head buf_queue;
81         struct   devstat device_stats;
82         LIST_HEAD(, ccb_hdr) pending_ccbs;
83         pt_state state;
84         pt_flags flags; 
85         union    ccb saved_ccb;
86         int      io_timeout;
87         dev_t    dev;
88 };
89
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);
105
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);
111
112 static struct periph_driver ptdriver =
113 {
114         ptinit, "pt",
115         TAILQ_HEAD_INITIALIZER(ptdriver.units), /* generation */ 0
116 };
117
118 DATA_SET(periphdriver_set, ptdriver);
119
120 #define PT_CDEV_MAJOR 61
121
122 static struct cdevsw pt_cdevsw = {
123         /* name */      "pt",
124         /* maj */       PT_CDEV_MAJOR,
125         /* flags */     0,
126         /* port */      NULL,
127         /* autoq */     0,
128
129         /* open */      ptopen,
130         /* close */     ptclose,
131         /* read */      physread,
132         /* write */     physwrite,
133         /* ioctl */     ptioctl,
134         /* poll */      nopoll,
135         /* mmap */      nommap,
136         /* strategy */  ptstrategy,
137         /* dump */      nodump,
138         /* psize */     nopsize
139 };
140
141 static struct extend_array *ptperiphs;
142
143 #ifndef SCSI_PT_DEFAULT_TIMEOUT
144 #define SCSI_PT_DEFAULT_TIMEOUT         60
145 #endif
146
147 static int
148 ptopen(dev_t dev, int flags, int fmt, struct proc *p)
149 {
150         struct cam_periph *periph;
151         struct pt_softc *softc;
152         int unit;
153         int error;
154         int s;
155
156         unit = minor(dev);
157         periph = cam_extend_get(ptperiphs, unit);
158         if (periph == NULL)
159                 return (ENXIO); 
160
161         softc = (struct pt_softc *)periph->softc;
162
163         s = splsoftcam();
164         if (softc->flags & PT_FLAG_DEVICE_INVALID) {
165                 splx(s);
166                 return(ENXIO);
167         }
168
169         CAM_DEBUG(periph->path, CAM_DEBUG_TRACE,
170             ("ptopen: dev=%s (unit %d)\n", devtoname(dev), unit));
171
172         if ((error = cam_periph_lock(periph, PCATCH)) != 0) {
173                 splx(s);
174                 return (error); /* error code from tsleep */
175         }
176
177         splx(s);
178
179         if ((softc->flags & PT_FLAG_OPEN) == 0) {
180                 if (cam_periph_acquire(periph) != CAM_REQ_CMP)
181                         error = ENXIO;
182                 else
183                         softc->flags |= PT_FLAG_OPEN;
184         } else
185                 error = EBUSY;
186
187         cam_periph_unlock(periph);
188         return (error);
189 }
190
191 static int
192 ptclose(dev_t dev, int flag, int fmt, struct proc *p)
193 {
194         struct  cam_periph *periph;
195         struct  pt_softc *softc;
196         int     unit;
197         int     error;
198
199         unit = minor(dev);
200         periph = cam_extend_get(ptperiphs, unit);
201         if (periph == NULL)
202                 return (ENXIO); 
203
204         softc = (struct pt_softc *)periph->softc;
205
206         if ((error = cam_periph_lock(periph, 0)) != 0)
207                 return (error); /* error code from tsleep */
208
209         softc->flags &= ~PT_FLAG_OPEN;
210         cam_periph_unlock(periph);
211         cam_periph_release(periph);
212         return (0);
213 }
214
215 /*
216  * Actually translate the requested transfer into one the physical driver
217  * can understand.  The transfer is described by a buf and will include
218  * only one physical transfer.
219  */
220 static void
221 ptstrategy(struct buf *bp)
222 {
223         struct cam_periph *periph;
224         struct pt_softc *softc;
225         u_int  unit;
226         int    s;
227         
228         unit = minor(bp->b_dev);
229         periph = cam_extend_get(ptperiphs, unit);
230         if (periph == NULL) {
231                 bp->b_error = ENXIO;
232                 goto bad;               
233         }
234         softc = (struct pt_softc *)periph->softc;
235
236         /*
237          * Mask interrupts so that the pack cannot be invalidated until
238          * after we are in the queue.  Otherwise, we might not properly
239          * clean up one of the buffers.
240          */
241         s = splbio();
242         
243         /*
244          * If the device has been made invalid, error out
245          */
246         if ((softc->flags & PT_FLAG_DEVICE_INVALID)) {
247                 splx(s);
248                 bp->b_error = ENXIO;
249                 goto bad;
250         }
251         
252         /*
253          * Place it in the queue of disk activities for this disk
254          */
255         bufq_insert_tail(&softc->buf_queue, bp);
256
257         splx(s);
258         
259         /*
260          * Schedule ourselves for performing the work.
261          */
262         xpt_schedule(periph, /* XXX priority */1);
263
264         return;
265 bad:
266         bp->b_flags |= B_ERROR;
267
268         /*
269          * Correctly set the buf to indicate a completed xfer
270          */
271         bp->b_resid = bp->b_bcount;
272         biodone(bp);
273 }
274
275 static void
276 ptinit(void)
277 {
278         cam_status status;
279         struct cam_path *path;
280
281         /*
282          * Create our extend array for storing the devices we attach to.
283          */
284         ptperiphs = cam_extend_new();
285         if (ptperiphs == NULL) {
286                 printf("pt: Failed to alloc extend array!\n");
287                 return;
288         }
289         
290         /*
291          * Install a global async callback.  This callback will
292          * receive async callbacks like "new device found".
293          */
294         status = xpt_create_path(&path, /*periph*/NULL, CAM_XPT_PATH_ID,
295                                  CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD);
296
297         if (status == CAM_REQ_CMP) {
298                 struct ccb_setasync csa;
299
300                 xpt_setup_ccb(&csa.ccb_h, path, /*priority*/5);
301                 csa.ccb_h.func_code = XPT_SASYNC_CB;
302                 csa.event_enable = AC_FOUND_DEVICE;
303                 csa.callback = ptasync;
304                 csa.callback_arg = NULL;
305                 xpt_action((union ccb *)&csa);
306                 status = csa.ccb_h.status;
307                 xpt_free_path(path);
308         }
309
310         if (status != CAM_REQ_CMP) {
311                 printf("pt: Failed to attach master async callback "
312                        "due to status 0x%x!\n", status);
313         }
314 }
315
316 static cam_status
317 ptctor(struct cam_periph *periph, void *arg)
318 {
319         struct pt_softc *softc;
320         struct ccb_setasync csa;
321         struct ccb_getdev *cgd;
322
323         cgd = (struct ccb_getdev *)arg;
324         if (periph == NULL) {
325                 printf("ptregister: periph was NULL!!\n");
326                 return(CAM_REQ_CMP_ERR);
327         }
328
329         if (cgd == NULL) {
330                 printf("ptregister: no getdev CCB, can't register device\n");
331                 return(CAM_REQ_CMP_ERR);
332         }
333
334         softc = (struct pt_softc *)malloc(sizeof(*softc),M_DEVBUF,M_NOWAIT);
335
336         if (softc == NULL) {
337                 printf("daregister: Unable to probe new device. "
338                        "Unable to allocate softc\n");                           
339                 return(CAM_REQ_CMP_ERR);
340         }
341
342         bzero(softc, sizeof(*softc));
343         LIST_INIT(&softc->pending_ccbs);
344         softc->state = PT_STATE_NORMAL;
345         bufq_init(&softc->buf_queue);
346
347         softc->io_timeout = SCSI_PT_DEFAULT_TIMEOUT * 1000;
348
349         periph->softc = softc;
350         
351         cam_extend_set(ptperiphs, periph->unit_number, periph);
352
353         devstat_add_entry(&softc->device_stats, "pt",
354                           periph->unit_number, 0,
355                           DEVSTAT_NO_BLOCKSIZE,
356                           SID_TYPE(&cgd->inq_data) | DEVSTAT_TYPE_IF_SCSI,
357                           DEVSTAT_PRIORITY_OTHER);
358
359         softc->dev = make_dev(&pt_cdevsw, periph->unit_number, UID_ROOT,
360                               GID_OPERATOR, 0600, "%s%d", periph->periph_name,
361                               periph->unit_number);
362         /*
363          * Add async callbacks for bus reset and
364          * bus device reset calls.  I don't bother
365          * checking if this fails as, in most cases,
366          * the system will function just fine without
367          * them and the only alternative would be to
368          * not attach the device on failure.
369          */
370         xpt_setup_ccb(&csa.ccb_h, periph->path, /*priority*/5);
371         csa.ccb_h.func_code = XPT_SASYNC_CB;
372         csa.event_enable = AC_SENT_BDR | AC_BUS_RESET | AC_LOST_DEVICE;
373         csa.callback = ptasync;
374         csa.callback_arg = periph;
375         xpt_action((union ccb *)&csa);
376
377         /* Tell the user we've attached to the device */
378         xpt_announce_periph(periph, NULL);
379
380         return(CAM_REQ_CMP);
381 }
382
383 static void
384 ptoninvalidate(struct cam_periph *periph)
385 {
386         int s;
387         struct pt_softc *softc;
388         struct buf *q_bp;
389         struct ccb_setasync csa;
390
391         softc = (struct pt_softc *)periph->softc;
392
393         /*
394          * De-register any async callbacks.
395          */
396         xpt_setup_ccb(&csa.ccb_h, periph->path,
397                       /* priority */ 5);
398         csa.ccb_h.func_code = XPT_SASYNC_CB;
399         csa.event_enable = 0;
400         csa.callback = ptasync;
401         csa.callback_arg = periph;
402         xpt_action((union ccb *)&csa);
403
404         softc->flags |= PT_FLAG_DEVICE_INVALID;
405
406         /*
407          * Although the oninvalidate() routines are always called at
408          * splsoftcam, we need to be at splbio() here to keep the buffer
409          * queue from being modified while we traverse it.
410          */
411         s = splbio();
412
413         /*
414          * Return all queued I/O with ENXIO.
415          * XXX Handle any transactions queued to the card
416          *     with XPT_ABORT_CCB.
417          */
418         while ((q_bp = bufq_first(&softc->buf_queue)) != NULL){
419                 bufq_remove(&softc->buf_queue, q_bp);
420                 q_bp->b_resid = q_bp->b_bcount;
421                 q_bp->b_error = ENXIO;
422                 q_bp->b_flags |= B_ERROR;
423                 biodone(q_bp);
424         }
425
426         splx(s);
427
428         xpt_print_path(periph->path);
429         printf("lost device\n");
430 }
431
432 static void
433 ptdtor(struct cam_periph *periph)
434 {
435         struct pt_softc *softc;
436
437         softc = (struct pt_softc *)periph->softc;
438
439         devstat_remove_entry(&softc->device_stats);
440
441         destroy_dev(softc->dev);
442
443         cam_extend_release(ptperiphs, periph->unit_number);
444         xpt_print_path(periph->path);
445         printf("removing device entry\n");
446         free(softc, M_DEVBUF);
447 }
448
449 static void
450 ptasync(void *callback_arg, u_int32_t code, struct cam_path *path, void *arg)
451 {
452         struct cam_periph *periph;
453
454         periph = (struct cam_periph *)callback_arg;
455         switch (code) {
456         case AC_FOUND_DEVICE:
457         {
458                 struct ccb_getdev *cgd;
459                 cam_status status;
460  
461                 cgd = (struct ccb_getdev *)arg;
462
463                 if (SID_TYPE(&cgd->inq_data) != T_PROCESSOR)
464                         break;
465
466                 /*
467                  * Allocate a peripheral instance for
468                  * this device and start the probe
469                  * process.
470                  */
471                 status = cam_periph_alloc(ptctor, ptoninvalidate, ptdtor,
472                                           ptstart, "pt", CAM_PERIPH_BIO,
473                                           cgd->ccb_h.path, ptasync,
474                                           AC_FOUND_DEVICE, cgd);
475
476                 if (status != CAM_REQ_CMP
477                  && status != CAM_REQ_INPROG)
478                         printf("ptasync: Unable to attach to new device "
479                                 "due to status 0x%x\n", status);
480                 break;
481         }
482         case AC_SENT_BDR:
483         case AC_BUS_RESET:
484         {
485                 struct pt_softc *softc;
486                 struct ccb_hdr *ccbh;
487                 int s;
488
489                 softc = (struct pt_softc *)periph->softc;
490                 s = splsoftcam();
491                 /*
492                  * Don't fail on the expected unit attention
493                  * that will occur.
494                  */
495                 softc->flags |= PT_FLAG_RETRY_UA;
496                 for (ccbh = LIST_FIRST(&softc->pending_ccbs);
497                      ccbh != NULL; ccbh = LIST_NEXT(ccbh, periph_links.le))
498                         ccbh->ccb_state |= PT_CCB_RETRY_UA;
499                 splx(s);
500                 /* FALLTHROUGH */
501         }
502         default:
503                 cam_periph_async(periph, code, path, arg);
504                 break;
505         }
506 }
507
508 static void
509 ptstart(struct cam_periph *periph, union ccb *start_ccb)
510 {
511         struct pt_softc *softc;
512         struct buf *bp;
513         int s;
514
515         softc = (struct pt_softc *)periph->softc;
516
517         /*
518          * See if there is a buf with work for us to do..
519          */
520         s = splbio();
521         bp = bufq_first(&softc->buf_queue);
522         if (periph->immediate_priority <= periph->pinfo.priority) {
523                 CAM_DEBUG_PRINT(CAM_DEBUG_SUBTRACE,
524                                 ("queuing for immediate ccb\n"));
525                 start_ccb->ccb_h.ccb_state = PT_CCB_WAITING;
526                 SLIST_INSERT_HEAD(&periph->ccb_list, &start_ccb->ccb_h,
527                                   periph_links.sle);
528                 periph->immediate_priority = CAM_PRIORITY_NONE;
529                 splx(s);
530                 wakeup(&periph->ccb_list);
531         } else if (bp == NULL) {
532                 splx(s);
533                 xpt_release_ccb(start_ccb);
534         } else {
535                 int oldspl;
536
537                 bufq_remove(&softc->buf_queue, bp);
538
539                 devstat_start_transaction(&softc->device_stats);
540
541                 scsi_send_receive(&start_ccb->csio,
542                                   /*retries*/4,
543                                   ptdone,
544                                   MSG_SIMPLE_Q_TAG,
545                                   bp->b_flags & B_READ,
546                                   /*byte2*/0,
547                                   bp->b_bcount,
548                                   bp->b_data,
549                                   /*sense_len*/SSD_FULL_SIZE,
550                                   /*timeout*/softc->io_timeout);
551
552                 start_ccb->ccb_h.ccb_state = PT_CCB_BUFFER_IO;
553
554                 /*
555                  * Block out any asyncronous callbacks
556                  * while we touch the pending ccb list.
557                  */
558                 oldspl = splcam();
559                 LIST_INSERT_HEAD(&softc->pending_ccbs, &start_ccb->ccb_h,
560                                  periph_links.le);
561                 splx(oldspl);
562
563                 start_ccb->ccb_h.ccb_bp = bp;
564                 bp = bufq_first(&softc->buf_queue);
565                 splx(s);
566
567                 xpt_action(start_ccb);
568                 
569                 if (bp != NULL) {
570                         /* Have more work to do, so ensure we stay scheduled */
571                         xpt_schedule(periph, /* XXX priority */1);
572                 }
573         }
574 }
575
576 static void
577 ptdone(struct cam_periph *periph, union ccb *done_ccb)
578 {
579         struct pt_softc *softc;
580         struct ccb_scsiio *csio;
581
582         softc = (struct pt_softc *)periph->softc;
583         csio = &done_ccb->csio;
584         switch (csio->ccb_h.ccb_state) {
585         case PT_CCB_BUFFER_IO:
586         case PT_CCB_BUFFER_IO_UA:
587         {
588                 struct buf *bp;
589                 int    oldspl;
590
591                 bp = (struct buf *)done_ccb->ccb_h.ccb_bp;
592                 if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
593                         int error;
594                         int s;
595                         int sf;
596                         
597                         if ((csio->ccb_h.ccb_state & PT_CCB_RETRY_UA) != 0)
598                                 sf = SF_RETRY_UA;
599                         else
600                                 sf = 0;
601
602                         sf |= SF_RETRY_SELTO;
603
604                         if ((error = pterror(done_ccb, 0, sf)) == ERESTART) {
605                                 /*
606                                  * A retry was scheuled, so
607                                  * just return.
608                                  */
609                                 return;
610                         }
611                         if (error != 0) {
612                                 struct buf *q_bp;
613
614                                 s = splbio();
615
616                                 if (error == ENXIO) {
617                                         /*
618                                          * Catastrophic error.  Mark our device
619                                          * as invalid.
620                                          */
621                                         xpt_print_path(periph->path);
622                                         printf("Invalidating device\n");
623                                         softc->flags |= PT_FLAG_DEVICE_INVALID;
624                                 }
625
626                                 /*
627                                  * return all queued I/O with EIO, so that
628                                  * the client can retry these I/Os in the
629                                  * proper order should it attempt to recover.
630                                  */
631                                 while ((q_bp = bufq_first(&softc->buf_queue))
632                                         != NULL) {
633                                         bufq_remove(&softc->buf_queue, q_bp);
634                                         q_bp->b_resid = q_bp->b_bcount;
635                                         q_bp->b_error = EIO;
636                                         q_bp->b_flags |= B_ERROR;
637                                         biodone(q_bp);
638                                 }
639                                 splx(s);
640                                 bp->b_error = error;
641                                 bp->b_resid = bp->b_bcount;
642                                 bp->b_flags |= B_ERROR;
643                         } else {
644                                 bp->b_resid = csio->resid;
645                                 bp->b_error = 0;
646                                 if (bp->b_resid != 0) {
647                                         /* Short transfer ??? */
648                                         bp->b_flags |= B_ERROR;
649                                 }
650                         }
651                         if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0)
652                                 cam_release_devq(done_ccb->ccb_h.path,
653                                                  /*relsim_flags*/0,
654                                                  /*reduction*/0,
655                                                  /*timeout*/0,
656                                                  /*getcount_only*/0);
657                 } else {
658                         bp->b_resid = csio->resid;
659                         if (bp->b_resid != 0)
660                                 bp->b_flags |= B_ERROR;
661                 }
662
663                 /*
664                  * Block out any asyncronous callbacks
665                  * while we touch the pending ccb list.
666                  */
667                 oldspl = splcam();
668                 LIST_REMOVE(&done_ccb->ccb_h, periph_links.le);
669                 splx(oldspl);
670
671                 devstat_end_transaction_buf(&softc->device_stats, bp);
672                 biodone(bp);
673                 break;
674         }
675         case PT_CCB_WAITING:
676                 /* Caller will release the CCB */
677                 wakeup(&done_ccb->ccb_h.cbfcnp);
678                 return;
679         }
680         xpt_release_ccb(done_ccb);
681 }
682
683 static int
684 pterror(union ccb *ccb, u_int32_t cam_flags, u_int32_t sense_flags)
685 {
686         struct pt_softc   *softc;
687         struct cam_periph *periph;
688
689         periph = xpt_path_periph(ccb->ccb_h.path);
690         softc = (struct pt_softc *)periph->softc;
691
692         return(cam_periph_error(ccb, cam_flags, sense_flags,
693                                 &softc->saved_ccb));
694 }
695
696 static int
697 ptioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p)
698 {
699         struct cam_periph *periph;
700         struct pt_softc *softc;
701         int unit;
702         int error;
703
704         unit = minor(dev);
705         periph = cam_extend_get(ptperiphs, unit);
706
707         if (periph == NULL)
708                 return(ENXIO);
709
710         softc = (struct pt_softc *)periph->softc;
711
712         if ((error = cam_periph_lock(periph, PCATCH)) != 0) {
713                 return (error); /* error code from tsleep */
714         }       
715
716         switch(cmd) {
717         case PTIOCGETTIMEOUT:
718                 if (softc->io_timeout >= 1000)
719                         *(int *)addr = softc->io_timeout / 1000;
720                 else
721                         *(int *)addr = 0;
722                 break;
723         case PTIOCSETTIMEOUT:
724         {
725                 int s;
726
727                 if (*(int *)addr < 1) {
728                         error = EINVAL;
729                         break;
730                 }
731
732                 s = splsoftcam();
733                 softc->io_timeout = *(int *)addr * 1000;
734                 splx(s);
735
736                 break;
737         }
738         default:
739                 error = cam_periph_ioctl(periph, cmd, addr, pterror);
740                 break;
741         }
742
743         cam_periph_unlock(periph);
744
745         return(error);
746 }
747
748 void
749 scsi_send_receive(struct ccb_scsiio *csio, u_int32_t retries,
750                   void (*cbfcnp)(struct cam_periph *, union ccb *),
751                   u_int tag_action, int readop, u_int byte2,
752                   u_int32_t xfer_len, u_int8_t *data_ptr, u_int8_t sense_len,
753                   u_int32_t timeout)
754 {
755         struct scsi_send_receive *scsi_cmd;
756
757         scsi_cmd = (struct scsi_send_receive *)&csio->cdb_io.cdb_bytes;
758         scsi_cmd->opcode = readop ? RECEIVE : SEND;
759         scsi_cmd->byte2 = byte2;
760         scsi_ulto3b(xfer_len, scsi_cmd->xfer_len);
761         scsi_cmd->control = 0;
762
763         cam_fill_csio(csio,
764                       retries,
765                       cbfcnp,
766                       /*flags*/readop ? CAM_DIR_IN : CAM_DIR_OUT,
767                       tag_action,
768                       data_ptr,
769                       xfer_len,
770                       sense_len,
771                       sizeof(*scsi_cmd),
772                       timeout);
773 }