9a9512691a9dee3ae4682103047488c81b6a3d2d
[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.14 2006/04/30 17:22:15 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 #include <sys/thread2.h>
44
45 #include "../cam.h"
46 #include "../cam_ccb.h"
47 #include "../cam_extend.h"
48 #include "../cam_periph.h"
49 #include "../cam_xpt_periph.h"
50 #include "../cam_debug.h"
51
52 #include "scsi_all.h"
53 #include "scsi_message.h"
54 #include "scsi_pt.h"
55
56 #include "opt_pt.h"
57
58 typedef enum {
59         PT_STATE_PROBE,
60         PT_STATE_NORMAL
61 } pt_state;
62
63 typedef enum {
64         PT_FLAG_NONE            = 0x00,
65         PT_FLAG_OPEN            = 0x01,
66         PT_FLAG_DEVICE_INVALID  = 0x02,
67         PT_FLAG_RETRY_UA        = 0x04
68 } pt_flags;
69
70 typedef enum {
71         PT_CCB_BUFFER_IO        = 0x01,
72         PT_CCB_WAITING          = 0x02,
73         PT_CCB_RETRY_UA         = 0x04,
74         PT_CCB_BUFFER_IO_UA     = PT_CCB_BUFFER_IO|PT_CCB_RETRY_UA
75 } pt_ccb_state;
76
77 /* Offsets into our private area for storing information */
78 #define ccb_state       ppriv_field0
79 #define ccb_bio         ppriv_ptr1
80
81 struct pt_softc {
82         struct   bio_queue_head bio_queue;
83         struct   devstat device_stats;
84         LIST_HEAD(, ccb_hdr) pending_ccbs;
85         pt_state state;
86         pt_flags flags; 
87         union    ccb saved_ccb;
88         int      io_timeout;
89         dev_t    dev;
90 };
91
92 static  d_open_t        ptopen;
93 static  d_close_t       ptclose;
94 static  d_strategy_t    ptstrategy;
95 static  periph_init_t   ptinit;
96 static  void            ptasync(void *callback_arg, u_int32_t code,
97                                 struct cam_path *path, void *arg);
98 static  periph_ctor_t   ptctor;
99 static  periph_oninv_t  ptoninvalidate;
100 static  periph_dtor_t   ptdtor;
101 static  periph_start_t  ptstart;
102 static  void            ptdone(struct cam_periph *periph,
103                                union ccb *done_ccb);
104 static  d_ioctl_t       ptioctl;
105 static  int             pterror(union ccb *ccb, u_int32_t cam_flags,
106                                 u_int32_t sense_flags);
107
108 void    scsi_send_receive(struct ccb_scsiio *csio, u_int32_t retries,
109                           void (*cbfcnp)(struct cam_periph *, union ccb *),
110                           u_int tag_action, int readop, u_int byte2,
111                           u_int32_t xfer_len, u_int8_t *data_ptr,
112                           u_int8_t sense_len, u_int32_t timeout);
113
114 static struct periph_driver ptdriver =
115 {
116         ptinit, "pt",
117         TAILQ_HEAD_INITIALIZER(ptdriver.units), /* generation */ 0
118 };
119
120 DATA_SET(periphdriver_set, ptdriver);
121
122 #define PT_CDEV_MAJOR 61
123
124 static struct cdevsw pt_cdevsw = {
125         /* name */      "pt",
126         /* maj */       PT_CDEV_MAJOR,
127         /* flags */     0,
128         /* port */      NULL,
129         /* clone */     NULL,
130
131         /* open */      ptopen,
132         /* close */     ptclose,
133         /* read */      physread,
134         /* write */     physwrite,
135         /* ioctl */     ptioctl,
136         /* poll */      nopoll,
137         /* mmap */      nommap,
138         /* strategy */  ptstrategy,
139         /* dump */      nodump,
140         /* psize */     nopsize
141 };
142
143 static struct extend_array *ptperiphs;
144
145 #ifndef SCSI_PT_DEFAULT_TIMEOUT
146 #define SCSI_PT_DEFAULT_TIMEOUT         60
147 #endif
148
149 static int
150 ptopen(dev_t dev, int flags, int fmt, struct thread *td)
151 {
152         struct cam_periph *periph;
153         struct pt_softc *softc;
154         int unit;
155         int error;
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         crit_enter();
165         if (softc->flags & PT_FLAG_DEVICE_INVALID) {
166                 crit_exit();
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                 crit_exit();
175                 return (error); /* error code from tsleep */
176         }
177
178         crit_exit();
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(dev_t dev, struct bio *bio)
223 {
224         struct buf *bp = bio->bio_buf;
225         struct cam_periph *periph;
226         struct pt_softc *softc;
227         u_int  unit;
228         
229         unit = minor(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         crit_enter();
243         
244         /*
245          * If the device has been made invalid, error out
246          */
247         if ((softc->flags & PT_FLAG_DEVICE_INVALID)) {
248                 crit_exit();
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         bioq_insert_tail(&softc->bio_queue, bio);
257
258         crit_exit();
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(bio);
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 = malloc(sizeof(*softc), M_DEVBUF, M_INTWAIT | M_ZERO);
336         LIST_INIT(&softc->pending_ccbs);
337         softc->state = PT_STATE_NORMAL;
338         bioq_init(&softc->bio_queue);
339
340         softc->io_timeout = SCSI_PT_DEFAULT_TIMEOUT * 1000;
341
342         periph->softc = softc;
343         
344         cam_extend_set(ptperiphs, periph->unit_number, periph);
345
346         devstat_add_entry(&softc->device_stats, "pt",
347                           periph->unit_number, 0,
348                           DEVSTAT_NO_BLOCKSIZE,
349                           SID_TYPE(&cgd->inq_data) | DEVSTAT_TYPE_IF_SCSI,
350                           DEVSTAT_PRIORITY_OTHER);
351
352         cdevsw_add(&pt_cdevsw, -1, periph->unit_number);
353         make_dev(&pt_cdevsw, periph->unit_number, UID_ROOT,
354                   GID_OPERATOR, 0600, "%s%d", periph->periph_name,
355                   periph->unit_number);
356         /*
357          * Add async callbacks for bus reset and
358          * bus device reset calls.  I don't bother
359          * checking if this fails as, in most cases,
360          * the system will function just fine without
361          * them and the only alternative would be to
362          * not attach the device on failure.
363          */
364         xpt_setup_ccb(&csa.ccb_h, periph->path, /*priority*/5);
365         csa.ccb_h.func_code = XPT_SASYNC_CB;
366         csa.event_enable = AC_SENT_BDR | AC_BUS_RESET | AC_LOST_DEVICE;
367         csa.callback = ptasync;
368         csa.callback_arg = periph;
369         xpt_action((union ccb *)&csa);
370
371         /* Tell the user we've attached to the device */
372         xpt_announce_periph(periph, NULL);
373
374         return(CAM_REQ_CMP);
375 }
376
377 static void
378 ptoninvalidate(struct cam_periph *periph)
379 {
380         struct pt_softc *softc;
381         struct bio *q_bio;
382         struct buf *q_bp;
383         struct ccb_setasync csa;
384
385         softc = (struct pt_softc *)periph->softc;
386
387         /*
388          * De-register any async callbacks.
389          */
390         xpt_setup_ccb(&csa.ccb_h, periph->path,
391                       /* priority */ 5);
392         csa.ccb_h.func_code = XPT_SASYNC_CB;
393         csa.event_enable = 0;
394         csa.callback = ptasync;
395         csa.callback_arg = periph;
396         xpt_action((union ccb *)&csa);
397
398         softc->flags |= PT_FLAG_DEVICE_INVALID;
399
400         /*
401          * We need to be in a critical section here to keep the buffer
402          * queue from being modified while we traverse it.
403          */
404         crit_enter();
405
406         /*
407          * Return all queued I/O with ENXIO.
408          * XXX Handle any transactions queued to the card
409          *     with XPT_ABORT_CCB.
410          */
411         while ((q_bio = bioq_first(&softc->bio_queue)) != NULL){
412                 bioq_remove(&softc->bio_queue, q_bio);
413                 q_bp = q_bio->bio_buf;
414                 q_bp->b_resid = q_bp->b_bcount;
415                 q_bp->b_error = ENXIO;
416                 q_bp->b_flags |= B_ERROR;
417                 biodone(q_bio);
418         }
419
420         crit_exit();
421
422         xpt_print_path(periph->path);
423         printf("lost device\n");
424 }
425
426 static void
427 ptdtor(struct cam_periph *periph)
428 {
429         struct pt_softc *softc;
430
431         softc = (struct pt_softc *)periph->softc;
432
433         devstat_remove_entry(&softc->device_stats);
434
435         cam_extend_release(ptperiphs, periph->unit_number);
436         xpt_print_path(periph->path);
437         printf("removing device entry\n");
438         cdevsw_remove(&pt_cdevsw, -1, periph->unit_number);
439         free(softc, M_DEVBUF);
440 }
441
442 static void
443 ptasync(void *callback_arg, u_int32_t code, struct cam_path *path, void *arg)
444 {
445         struct cam_periph *periph;
446
447         periph = (struct cam_periph *)callback_arg;
448         switch (code) {
449         case AC_FOUND_DEVICE:
450         {
451                 struct ccb_getdev *cgd;
452                 cam_status status;
453  
454                 cgd = (struct ccb_getdev *)arg;
455
456                 if (SID_TYPE(&cgd->inq_data) != T_PROCESSOR)
457                         break;
458
459                 /*
460                  * Allocate a peripheral instance for
461                  * this device and start the probe
462                  * process.
463                  */
464                 status = cam_periph_alloc(ptctor, ptoninvalidate, ptdtor,
465                                           ptstart, "pt", CAM_PERIPH_BIO,
466                                           cgd->ccb_h.path, ptasync,
467                                           AC_FOUND_DEVICE, cgd);
468
469                 if (status != CAM_REQ_CMP
470                  && status != CAM_REQ_INPROG)
471                         printf("ptasync: Unable to attach to new device "
472                                 "due to status 0x%x\n", status);
473                 break;
474         }
475         case AC_SENT_BDR:
476         case AC_BUS_RESET:
477         {
478                 struct pt_softc *softc;
479                 struct ccb_hdr *ccbh;
480
481                 softc = (struct pt_softc *)periph->softc;
482                 crit_enter();
483                 /*
484                  * Don't fail on the expected unit attention
485                  * that will occur.
486                  */
487                 softc->flags |= PT_FLAG_RETRY_UA;
488                 for (ccbh = LIST_FIRST(&softc->pending_ccbs);
489                      ccbh != NULL; ccbh = LIST_NEXT(ccbh, periph_links.le))
490                         ccbh->ccb_state |= PT_CCB_RETRY_UA;
491                 crit_exit();
492                 /* FALLTHROUGH */
493         }
494         default:
495                 cam_periph_async(periph, code, path, arg);
496                 break;
497         }
498 }
499
500 static void
501 ptstart(struct cam_periph *periph, union ccb *start_ccb)
502 {
503         struct pt_softc *softc;
504         struct buf *bp;
505         struct bio *bio;
506
507         softc = (struct pt_softc *)periph->softc;
508
509         /*
510          * See if there is a buf with work for us to do..
511          */
512         crit_enter();
513         bio = bioq_first(&softc->bio_queue);
514         if (periph->immediate_priority <= periph->pinfo.priority) {
515                 CAM_DEBUG_PRINT(CAM_DEBUG_SUBTRACE,
516                                 ("queuing for immediate ccb\n"));
517                 start_ccb->ccb_h.ccb_state = PT_CCB_WAITING;
518                 SLIST_INSERT_HEAD(&periph->ccb_list, &start_ccb->ccb_h,
519                                   periph_links.sle);
520                 periph->immediate_priority = CAM_PRIORITY_NONE;
521                 crit_exit();
522                 wakeup(&periph->ccb_list);
523         } else if (bio == NULL) {
524                 crit_exit();
525                 xpt_release_ccb(start_ccb);
526         } else {
527                 bioq_remove(&softc->bio_queue, bio);
528                 bp = bio->bio_buf;
529
530                 devstat_start_transaction(&softc->device_stats);
531
532                 scsi_send_receive(&start_ccb->csio,
533                                   /*retries*/4,
534                                   ptdone,
535                                   MSG_SIMPLE_Q_TAG,
536                                   (bp->b_cmd == BUF_CMD_READ),
537                                   /*byte2*/0,
538                                   bp->b_bcount,
539                                   bp->b_data,
540                                   /*sense_len*/SSD_FULL_SIZE,
541                                   /*timeout*/softc->io_timeout);
542
543                 start_ccb->ccb_h.ccb_state = PT_CCB_BUFFER_IO;
544
545                 /*
546                  * Block out any asyncronous callbacks
547                  * while we touch the pending ccb list.
548                  */
549                 LIST_INSERT_HEAD(&softc->pending_ccbs, &start_ccb->ccb_h,
550                                  periph_links.le);
551
552                 start_ccb->ccb_h.ccb_bio = bio;
553                 bio = bioq_first(&softc->bio_queue);
554                 crit_exit();
555
556                 xpt_action(start_ccb);
557                 
558                 if (bio != NULL) {
559                         /* Have more work to do, so ensure we stay scheduled */
560                         xpt_schedule(periph, /* XXX priority */1);
561                 }
562         }
563 }
564
565 static void
566 ptdone(struct cam_periph *periph, union ccb *done_ccb)
567 {
568         struct pt_softc *softc;
569         struct ccb_scsiio *csio;
570
571         softc = (struct pt_softc *)periph->softc;
572         csio = &done_ccb->csio;
573         switch (csio->ccb_h.ccb_state) {
574         case PT_CCB_BUFFER_IO:
575         case PT_CCB_BUFFER_IO_UA:
576         {
577                 struct buf *bp;
578                 struct bio *bio;
579
580                 bio = (struct bio *)done_ccb->ccb_h.ccb_bio;
581                 bp = bio->bio_buf;
582
583                 if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
584                         int error;
585                         int sf;
586                         
587                         if ((csio->ccb_h.ccb_state & PT_CCB_RETRY_UA) != 0)
588                                 sf = SF_RETRY_UA;
589                         else
590                                 sf = 0;
591
592                         sf |= SF_RETRY_SELTO;
593
594                         if ((error = pterror(done_ccb, 0, sf)) == ERESTART) {
595                                 /*
596                                  * A retry was scheuled, so
597                                  * just return.
598                                  */
599                                 return;
600                         }
601                         if (error != 0) {
602                                 struct buf *q_bp;
603                                 struct bio *q_bio;
604
605                                 crit_enter();
606
607                                 if (error == ENXIO) {
608                                         /*
609                                          * Catastrophic error.  Mark our device
610                                          * as invalid.
611                                          */
612                                         xpt_print_path(periph->path);
613                                         printf("Invalidating device\n");
614                                         softc->flags |= PT_FLAG_DEVICE_INVALID;
615                                 }
616
617                                 /*
618                                  * return all queued I/O with EIO, so that
619                                  * the client can retry these I/Os in the
620                                  * proper order should it attempt to recover.
621                                  */
622                                 while ((q_bio = bioq_first(&softc->bio_queue))
623                                         != NULL) {
624                                         bioq_remove(&softc->bio_queue, q_bio);
625                                         q_bp = q_bio->bio_buf;
626                                         q_bp->b_resid = q_bp->b_bcount;
627                                         q_bp->b_error = EIO;
628                                         q_bp->b_flags |= B_ERROR;
629                                         biodone(q_bio);
630                                 }
631                                 crit_exit();
632                                 bp->b_error = error;
633                                 bp->b_resid = bp->b_bcount;
634                                 bp->b_flags |= B_ERROR;
635                         } else {
636                                 bp->b_resid = csio->resid;
637                                 bp->b_error = 0;
638                                 if (bp->b_resid != 0) {
639                                         /* Short transfer ??? */
640                                         bp->b_flags |= B_ERROR;
641                                 }
642                         }
643                         if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0)
644                                 cam_release_devq(done_ccb->ccb_h.path,
645                                                  /*relsim_flags*/0,
646                                                  /*reduction*/0,
647                                                  /*timeout*/0,
648                                                  /*getcount_only*/0);
649                 } else {
650                         bp->b_resid = csio->resid;
651                         if (bp->b_resid != 0)
652                                 bp->b_flags |= B_ERROR;
653                 }
654
655                 /*
656                  * Block out any asyncronous callbacks
657                  * while we touch the pending ccb list.
658                  */
659                 crit_enter();
660                 LIST_REMOVE(&done_ccb->ccb_h, periph_links.le);
661                 crit_exit();
662
663                 devstat_end_transaction_buf(&softc->device_stats, bp);
664                 biodone(bio);
665                 break;
666         }
667         case PT_CCB_WAITING:
668                 /* Caller will release the CCB */
669                 wakeup(&done_ccb->ccb_h.cbfcnp);
670                 return;
671         }
672         xpt_release_ccb(done_ccb);
673 }
674
675 static int
676 pterror(union ccb *ccb, u_int32_t cam_flags, u_int32_t sense_flags)
677 {
678         struct pt_softc   *softc;
679         struct cam_periph *periph;
680
681         periph = xpt_path_periph(ccb->ccb_h.path);
682         softc = (struct pt_softc *)periph->softc;
683
684         return(cam_periph_error(ccb, cam_flags, sense_flags,
685                                 &softc->saved_ccb));
686 }
687
688 static int
689 ptioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct thread *td)
690 {
691         struct cam_periph *periph;
692         struct pt_softc *softc;
693         int unit;
694         int error;
695
696         unit = minor(dev);
697         periph = cam_extend_get(ptperiphs, unit);
698
699         if (periph == NULL)
700                 return(ENXIO);
701
702         softc = (struct pt_softc *)periph->softc;
703
704         if ((error = cam_periph_lock(periph, PCATCH)) != 0) {
705                 return (error); /* error code from tsleep */
706         }       
707
708         switch(cmd) {
709         case PTIOCGETTIMEOUT:
710                 if (softc->io_timeout >= 1000)
711                         *(int *)addr = softc->io_timeout / 1000;
712                 else
713                         *(int *)addr = 0;
714                 break;
715         case PTIOCSETTIMEOUT:
716         {
717                 if (*(int *)addr < 1) {
718                         error = EINVAL;
719                         break;
720                 }
721
722                 crit_enter();
723                 softc->io_timeout = *(int *)addr * 1000;
724                 crit_exit();
725
726                 break;
727         }
728         default:
729                 error = cam_periph_ioctl(periph, cmd, addr, pterror);
730                 break;
731         }
732
733         cam_periph_unlock(periph);
734
735         return(error);
736 }
737
738 void
739 scsi_send_receive(struct ccb_scsiio *csio, u_int32_t retries,
740                   void (*cbfcnp)(struct cam_periph *, union ccb *),
741                   u_int tag_action, int readop, u_int byte2,
742                   u_int32_t xfer_len, u_int8_t *data_ptr, u_int8_t sense_len,
743                   u_int32_t timeout)
744 {
745         struct scsi_send_receive *scsi_cmd;
746
747         scsi_cmd = (struct scsi_send_receive *)&csio->cdb_io.cdb_bytes;
748         scsi_cmd->opcode = readop ? RECEIVE : SEND;
749         scsi_cmd->byte2 = byte2;
750         scsi_ulto3b(xfer_len, scsi_cmd->xfer_len);
751         scsi_cmd->control = 0;
752
753         cam_fill_csio(csio,
754                       retries,
755                       cbfcnp,
756                       /*flags*/readop ? CAM_DIR_IN : CAM_DIR_OUT,
757                       tag_action,
758                       data_ptr,
759                       xfer_len,
760                       sense_len,
761                       sizeof(*scsi_cmd),
762                       timeout);
763 }