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