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