Add the DragonFly cvs id and perform general cleanups on cvs/rcs/sccs ids. Most
[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.2 2003/06/17 04:28:19 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         /* open */      ptopen,
124         /* close */     ptclose,
125         /* read */      physread,
126         /* write */     physwrite,
127         /* ioctl */     ptioctl,
128         /* poll */      nopoll,
129         /* mmap */      nommap,
130         /* strategy */  ptstrategy,
131         /* name */      "pt",
132         /* maj */       PT_CDEV_MAJOR,
133         /* dump */      nodump,
134         /* psize */     nopsize,
135         /* flags */     0,
136         /* bmaj */      -1
137 };
138
139 static struct extend_array *ptperiphs;
140
141 #ifndef SCSI_PT_DEFAULT_TIMEOUT
142 #define SCSI_PT_DEFAULT_TIMEOUT         60
143 #endif
144
145 static int
146 ptopen(dev_t dev, int flags, int fmt, struct proc *p)
147 {
148         struct cam_periph *periph;
149         struct pt_softc *softc;
150         int unit;
151         int error;
152         int s;
153
154         unit = minor(dev);
155         periph = cam_extend_get(ptperiphs, unit);
156         if (periph == NULL)
157                 return (ENXIO); 
158
159         softc = (struct pt_softc *)periph->softc;
160
161         s = splsoftcam();
162         if (softc->flags & PT_FLAG_DEVICE_INVALID) {
163                 splx(s);
164                 return(ENXIO);
165         }
166
167         CAM_DEBUG(periph->path, CAM_DEBUG_TRACE,
168             ("ptopen: dev=%s (unit %d)\n", devtoname(dev), unit));
169
170         if ((error = cam_periph_lock(periph, PRIBIO|PCATCH)) != 0) {
171                 splx(s);
172                 return (error); /* error code from tsleep */
173         }
174
175         splx(s);
176
177         if ((softc->flags & PT_FLAG_OPEN) == 0) {
178                 if (cam_periph_acquire(periph) != CAM_REQ_CMP)
179                         error = ENXIO;
180                 else
181                         softc->flags |= PT_FLAG_OPEN;
182         } else
183                 error = EBUSY;
184
185         cam_periph_unlock(periph);
186         return (error);
187 }
188
189 static int
190 ptclose(dev_t dev, int flag, int fmt, struct proc *p)
191 {
192         struct  cam_periph *periph;
193         struct  pt_softc *softc;
194         int     unit;
195         int     error;
196
197         unit = minor(dev);
198         periph = cam_extend_get(ptperiphs, unit);
199         if (periph == NULL)
200                 return (ENXIO); 
201
202         softc = (struct pt_softc *)periph->softc;
203
204         if ((error = cam_periph_lock(periph, PRIBIO)) != 0)
205                 return (error); /* error code from tsleep */
206
207         softc->flags &= ~PT_FLAG_OPEN;
208         cam_periph_unlock(periph);
209         cam_periph_release(periph);
210         return (0);
211 }
212
213 /*
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.
217  */
218 static void
219 ptstrategy(struct buf *bp)
220 {
221         struct cam_periph *periph;
222         struct pt_softc *softc;
223         u_int  unit;
224         int    s;
225         
226         unit = minor(bp->b_dev);
227         periph = cam_extend_get(ptperiphs, unit);
228         if (periph == NULL) {
229                 bp->b_error = ENXIO;
230                 goto bad;               
231         }
232         softc = (struct pt_softc *)periph->softc;
233
234         /*
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.
238          */
239         s = splbio();
240         
241         /*
242          * If the device has been made invalid, error out
243          */
244         if ((softc->flags & PT_FLAG_DEVICE_INVALID)) {
245                 splx(s);
246                 bp->b_error = ENXIO;
247                 goto bad;
248         }
249         
250         /*
251          * Place it in the queue of disk activities for this disk
252          */
253         bufq_insert_tail(&softc->buf_queue, bp);
254
255         splx(s);
256         
257         /*
258          * Schedule ourselves for performing the work.
259          */
260         xpt_schedule(periph, /* XXX priority */1);
261
262         return;
263 bad:
264         bp->b_flags |= B_ERROR;
265
266         /*
267          * Correctly set the buf to indicate a completed xfer
268          */
269         bp->b_resid = bp->b_bcount;
270         biodone(bp);
271 }
272
273 static void
274 ptinit(void)
275 {
276         cam_status status;
277         struct cam_path *path;
278
279         /*
280          * Create our extend array for storing the devices we attach to.
281          */
282         ptperiphs = cam_extend_new();
283         if (ptperiphs == NULL) {
284                 printf("pt: Failed to alloc extend array!\n");
285                 return;
286         }
287         
288         /*
289          * Install a global async callback.  This callback will
290          * receive async callbacks like "new device found".
291          */
292         status = xpt_create_path(&path, /*periph*/NULL, CAM_XPT_PATH_ID,
293                                  CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD);
294
295         if (status == CAM_REQ_CMP) {
296                 struct ccb_setasync csa;
297
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;
305                 xpt_free_path(path);
306         }
307
308         if (status != CAM_REQ_CMP) {
309                 printf("pt: Failed to attach master async callback "
310                        "due to status 0x%x!\n", status);
311         }
312 }
313
314 static cam_status
315 ptctor(struct cam_periph *periph, void *arg)
316 {
317         struct pt_softc *softc;
318         struct ccb_setasync csa;
319         struct ccb_getdev *cgd;
320
321         cgd = (struct ccb_getdev *)arg;
322         if (periph == NULL) {
323                 printf("ptregister: periph was NULL!!\n");
324                 return(CAM_REQ_CMP_ERR);
325         }
326
327         if (cgd == NULL) {
328                 printf("ptregister: no getdev CCB, can't register device\n");
329                 return(CAM_REQ_CMP_ERR);
330         }
331
332         softc = (struct pt_softc *)malloc(sizeof(*softc),M_DEVBUF,M_NOWAIT);
333
334         if (softc == NULL) {
335                 printf("daregister: Unable to probe new device. "
336                        "Unable to allocate softc\n");                           
337                 return(CAM_REQ_CMP_ERR);
338         }
339
340         bzero(softc, sizeof(*softc));
341         LIST_INIT(&softc->pending_ccbs);
342         softc->state = PT_STATE_NORMAL;
343         bufq_init(&softc->buf_queue);
344
345         softc->io_timeout = SCSI_PT_DEFAULT_TIMEOUT * 1000;
346
347         periph->softc = softc;
348         
349         cam_extend_set(ptperiphs, periph->unit_number, periph);
350
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);
356
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);
360         /*
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.
367          */
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);
374
375         /* Tell the user we've attached to the device */
376         xpt_announce_periph(periph, NULL);
377
378         return(CAM_REQ_CMP);
379 }
380
381 static void
382 ptoninvalidate(struct cam_periph *periph)
383 {
384         int s;
385         struct pt_softc *softc;
386         struct buf *q_bp;
387         struct ccb_setasync csa;
388
389         softc = (struct pt_softc *)periph->softc;
390
391         /*
392          * De-register any async callbacks.
393          */
394         xpt_setup_ccb(&csa.ccb_h, periph->path,
395                       /* priority */ 5);
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);
401
402         softc->flags |= PT_FLAG_DEVICE_INVALID;
403
404         /*
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.
408          */
409         s = splbio();
410
411         /*
412          * Return all queued I/O with ENXIO.
413          * XXX Handle any transactions queued to the card
414          *     with XPT_ABORT_CCB.
415          */
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;
421                 biodone(q_bp);
422         }
423
424         splx(s);
425
426         xpt_print_path(periph->path);
427         printf("lost device\n");
428 }
429
430 static void
431 ptdtor(struct cam_periph *periph)
432 {
433         struct pt_softc *softc;
434
435         softc = (struct pt_softc *)periph->softc;
436
437         devstat_remove_entry(&softc->device_stats);
438
439         destroy_dev(softc->dev);
440
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);
445 }
446
447 static void
448 ptasync(void *callback_arg, u_int32_t code, struct cam_path *path, void *arg)
449 {
450         struct cam_periph *periph;
451
452         periph = (struct cam_periph *)callback_arg;
453         switch (code) {
454         case AC_FOUND_DEVICE:
455         {
456                 struct ccb_getdev *cgd;
457                 cam_status status;
458  
459                 cgd = (struct ccb_getdev *)arg;
460
461                 if (SID_TYPE(&cgd->inq_data) != T_PROCESSOR)
462                         break;
463
464                 /*
465                  * Allocate a peripheral instance for
466                  * this device and start the probe
467                  * process.
468                  */
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);
473
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);
478                 break;
479         }
480         case AC_SENT_BDR:
481         case AC_BUS_RESET:
482         {
483                 struct pt_softc *softc;
484                 struct ccb_hdr *ccbh;
485                 int s;
486
487                 softc = (struct pt_softc *)periph->softc;
488                 s = splsoftcam();
489                 /*
490                  * Don't fail on the expected unit attention
491                  * that will occur.
492                  */
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;
497                 splx(s);
498                 /* FALLTHROUGH */
499         }
500         default:
501                 cam_periph_async(periph, code, path, arg);
502                 break;
503         }
504 }
505
506 static void
507 ptstart(struct cam_periph *periph, union ccb *start_ccb)
508 {
509         struct pt_softc *softc;
510         struct buf *bp;
511         int s;
512
513         softc = (struct pt_softc *)periph->softc;
514
515         /*
516          * See if there is a buf with work for us to do..
517          */
518         s = splbio();
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,
525                                   periph_links.sle);
526                 periph->immediate_priority = CAM_PRIORITY_NONE;
527                 splx(s);
528                 wakeup(&periph->ccb_list);
529         } else if (bp == NULL) {
530                 splx(s);
531                 xpt_release_ccb(start_ccb);
532         } else {
533                 int oldspl;
534
535                 bufq_remove(&softc->buf_queue, bp);
536
537                 devstat_start_transaction(&softc->device_stats);
538
539                 scsi_send_receive(&start_ccb->csio,
540                                   /*retries*/4,
541                                   ptdone,
542                                   MSG_SIMPLE_Q_TAG,
543                                   bp->b_flags & B_READ,
544                                   /*byte2*/0,
545                                   bp->b_bcount,
546                                   bp->b_data,
547                                   /*sense_len*/SSD_FULL_SIZE,
548                                   /*timeout*/softc->io_timeout);
549
550                 start_ccb->ccb_h.ccb_state = PT_CCB_BUFFER_IO;
551
552                 /*
553                  * Block out any asyncronous callbacks
554                  * while we touch the pending ccb list.
555                  */
556                 oldspl = splcam();
557                 LIST_INSERT_HEAD(&softc->pending_ccbs, &start_ccb->ccb_h,
558                                  periph_links.le);
559                 splx(oldspl);
560
561                 start_ccb->ccb_h.ccb_bp = bp;
562                 bp = bufq_first(&softc->buf_queue);
563                 splx(s);
564
565                 xpt_action(start_ccb);
566                 
567                 if (bp != NULL) {
568                         /* Have more work to do, so ensure we stay scheduled */
569                         xpt_schedule(periph, /* XXX priority */1);
570                 }
571         }
572 }
573
574 static void
575 ptdone(struct cam_periph *periph, union ccb *done_ccb)
576 {
577         struct pt_softc *softc;
578         struct ccb_scsiio *csio;
579
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:
585         {
586                 struct buf *bp;
587                 int    oldspl;
588
589                 bp = (struct buf *)done_ccb->ccb_h.ccb_bp;
590                 if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
591                         int error;
592                         int s;
593                         int sf;
594                         
595                         if ((csio->ccb_h.ccb_state & PT_CCB_RETRY_UA) != 0)
596                                 sf = SF_RETRY_UA;
597                         else
598                                 sf = 0;
599
600                         sf |= SF_RETRY_SELTO;
601
602                         if ((error = pterror(done_ccb, 0, sf)) == ERESTART) {
603                                 /*
604                                  * A retry was scheuled, so
605                                  * just return.
606                                  */
607                                 return;
608                         }
609                         if (error != 0) {
610                                 struct buf *q_bp;
611
612                                 s = splbio();
613
614                                 if (error == ENXIO) {
615                                         /*
616                                          * Catastrophic error.  Mark our device
617                                          * as invalid.
618                                          */
619                                         xpt_print_path(periph->path);
620                                         printf("Invalidating device\n");
621                                         softc->flags |= PT_FLAG_DEVICE_INVALID;
622                                 }
623
624                                 /*
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.
628                                  */
629                                 while ((q_bp = bufq_first(&softc->buf_queue))
630                                         != NULL) {
631                                         bufq_remove(&softc->buf_queue, q_bp);
632                                         q_bp->b_resid = q_bp->b_bcount;
633                                         q_bp->b_error = EIO;
634                                         q_bp->b_flags |= B_ERROR;
635                                         biodone(q_bp);
636                                 }
637                                 splx(s);
638                                 bp->b_error = error;
639                                 bp->b_resid = bp->b_bcount;
640                                 bp->b_flags |= B_ERROR;
641                         } else {
642                                 bp->b_resid = csio->resid;
643                                 bp->b_error = 0;
644                                 if (bp->b_resid != 0) {
645                                         /* Short transfer ??? */
646                                         bp->b_flags |= B_ERROR;
647                                 }
648                         }
649                         if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0)
650                                 cam_release_devq(done_ccb->ccb_h.path,
651                                                  /*relsim_flags*/0,
652                                                  /*reduction*/0,
653                                                  /*timeout*/0,
654                                                  /*getcount_only*/0);
655                 } else {
656                         bp->b_resid = csio->resid;
657                         if (bp->b_resid != 0)
658                                 bp->b_flags |= B_ERROR;
659                 }
660
661                 /*
662                  * Block out any asyncronous callbacks
663                  * while we touch the pending ccb list.
664                  */
665                 oldspl = splcam();
666                 LIST_REMOVE(&done_ccb->ccb_h, periph_links.le);
667                 splx(oldspl);
668
669                 devstat_end_transaction_buf(&softc->device_stats, bp);
670                 biodone(bp);
671                 break;
672         }
673         case PT_CCB_WAITING:
674                 /* Caller will release the CCB */
675                 wakeup(&done_ccb->ccb_h.cbfcnp);
676                 return;
677         }
678         xpt_release_ccb(done_ccb);
679 }
680
681 static int
682 pterror(union ccb *ccb, u_int32_t cam_flags, u_int32_t sense_flags)
683 {
684         struct pt_softc   *softc;
685         struct cam_periph *periph;
686
687         periph = xpt_path_periph(ccb->ccb_h.path);
688         softc = (struct pt_softc *)periph->softc;
689
690         return(cam_periph_error(ccb, cam_flags, sense_flags,
691                                 &softc->saved_ccb));
692 }
693
694 static int
695 ptioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p)
696 {
697         struct cam_periph *periph;
698         struct pt_softc *softc;
699         int unit;
700         int error;
701
702         unit = minor(dev);
703         periph = cam_extend_get(ptperiphs, unit);
704
705         if (periph == NULL)
706                 return(ENXIO);
707
708         softc = (struct pt_softc *)periph->softc;
709
710         if ((error = cam_periph_lock(periph, PRIBIO|PCATCH)) != 0) {
711                 return (error); /* error code from tsleep */
712         }       
713
714         switch(cmd) {
715         case PTIOCGETTIMEOUT:
716                 if (softc->io_timeout >= 1000)
717                         *(int *)addr = softc->io_timeout / 1000;
718                 else
719                         *(int *)addr = 0;
720                 break;
721         case PTIOCSETTIMEOUT:
722         {
723                 int s;
724
725                 if (*(int *)addr < 1) {
726                         error = EINVAL;
727                         break;
728                 }
729
730                 s = splsoftcam();
731                 softc->io_timeout = *(int *)addr * 1000;
732                 splx(s);
733
734                 break;
735         }
736         default:
737                 error = cam_periph_ioctl(periph, cmd, addr, pterror);
738                 break;
739         }
740
741         cam_periph_unlock(periph);
742
743         return(error);
744 }
745
746 void
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,
751                   u_int32_t timeout)
752 {
753         struct scsi_send_receive *scsi_cmd;
754
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;
760
761         cam_fill_csio(csio,
762                       retries,
763                       cbfcnp,
764                       /*flags*/readop ? CAM_DIR_IN : CAM_DIR_OUT,
765                       tag_action,
766                       data_ptr,
767                       xfer_len,
768                       sense_len,
769                       sizeof(*scsi_cmd),
770                       timeout);
771 }