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