Commit | Line | Data |
---|---|---|
d4284689 RG |
1 | /* |
2 | * Contributed by HD Associates (hd@world.std.com). | |
3 | * Copyright (c) 1992, 1993 HD Associates | |
4 | * | |
5 | * Berkeley style copyright. I've just snarfed it out of stdio.h: | |
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 | * 2. Redistributions in binary form must reproduce the above copyright | |
13 | * notice, this list of conditions and the following disclaimer in the | |
14 | * documentation and/or other materials provided with the distribution. | |
15 | * 3. All advertising materials mentioning features or use of this software | |
16 | * must display the following acknowledgement: | |
17 | * This product includes software developed by the University of | |
18 | * California, Berkeley and its contributors. | |
19 | * 4. Neither the name of the University nor the names of its contributors | |
20 | * may be used to endorse or promote products derived from this software | |
21 | * without specific prior written permission. | |
22 | * | |
23 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | |
24 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
25 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
26 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |
27 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
28 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
29 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
30 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
31 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
32 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
33 | * SUCH DAMAGE. | |
34 | * | |
01e1b05c RG |
35 | * from: @(#)stdio.h 5.17 (Berkeley) 6/3/91 |
36 | * $Id$ | |
d4284689 RG |
37 | */ |
38 | #include <sys/types.h> | |
39 | #include <sys/errno.h> | |
40 | #include <sys/param.h> | |
41 | #include <sys/malloc.h> | |
42 | #include <sys/buf.h> | |
43 | #include <sys/proc.h> | |
44 | ||
45 | #include <scsi/scsi_all.h> | |
46 | #include <scsi/scsiconf.h> | |
47 | #include <scsi/scsi_generic.h> | |
48 | #include "sg.h" | |
49 | #include <sys/sgio.h> | |
50 | ||
51 | #define SGOUTSTANDING 2 | |
52 | #define SG_RETRIES 2 | |
53 | #define SPLSG splbio | |
54 | ||
55 | /* Use one of the implementation defined spare bits | |
56 | * to indicate the escape op: | |
57 | */ | |
58 | #define DSRQ_ESCAPE DSRQ_CTRL1 | |
59 | ||
60 | struct sg | |
61 | { | |
62 | int flags; | |
63 | struct scsi_switch *sc_sw; | |
64 | int ctlr; | |
65 | ||
66 | long int ad_info; /* info about the adapter */ | |
67 | int cmdscount; /* cmds allowed outstanding by the board */ | |
68 | ||
69 | struct scsi_xfer *free_xfer; | |
70 | int free_xfer_wait; | |
71 | }; | |
72 | ||
73 | /* This is used to associate a struct dsreq and a struct buf. | |
74 | */ | |
75 | typedef struct dsbuf | |
76 | { | |
77 | dsreq_t *dsreq; | |
78 | struct buf buf; | |
79 | ||
80 | /* I think this is a portable way to get back to the base of | |
81 | * the enclosing structure: | |
82 | */ | |
83 | # define DSBUF_P(BP) ((dsbuf_t *)((caddr_t)(BP) - (caddr_t)&((dsbuf_t *)0)->buf)) | |
84 | ||
85 | int magic; | |
86 | ||
87 | # define DSBUF_MAGIC 0xDBFACDBF | |
88 | } dsbuf_t; | |
89 | ||
90 | #if NSG > 4 | |
91 | /* The host adapter unit is encoded in the upper 2 bits of the minor number | |
92 | * (the SGI flag bits). | |
93 | */ | |
94 | #error "NSG can't be > 4 unless the method of encoding the board unit changes" | |
95 | #endif | |
96 | ||
97 | struct sg *sgs[NSG]; | |
98 | ||
99 | #define SG(DEV) sgs[G_SCSI_UNIT(DEV)] | |
100 | ||
101 | struct sg *sg_new(int lun) | |
102 | { | |
103 | struct sg *sg = (struct sg *)malloc(sizeof(*sg),M_TEMP, M_NOWAIT); | |
104 | ||
105 | if (sg == 0) | |
106 | return 0; | |
107 | ||
108 | bzero(sg, sizeof(struct sg)); | |
109 | ||
110 | return sg; | |
111 | } | |
112 | ||
113 | int sg_attach(ctlr, scsi_addr, scsi_switch) | |
114 | int ctlr,scsi_addr; | |
115 | struct scsi_switch *scsi_switch; | |
116 | { | |
117 | struct sg *sg; | |
118 | int i; | |
119 | struct scsi_xfer *scsi_xfer; | |
120 | static int next_sg_unit = 0; | |
121 | ||
122 | int unit = next_sg_unit++; | |
123 | ||
124 | if (unit >= NSG) | |
125 | { | |
126 | printf("Too many generic SCSIs (%d > %d); reconfigure the kernel.\n", | |
127 | unit+1, NSG); | |
128 | ||
129 | if (NSG == 4) | |
130 | printf( | |
131 | "You have hit the max of 4. You will have to change the driver.\n"); | |
132 | ||
133 | return 0; | |
134 | } | |
135 | ||
136 | if ((sg = sg_new(0)) == 0) | |
137 | return 0; | |
138 | ||
139 | sgs[unit] = sg; | |
140 | ||
141 | sg->sc_sw = scsi_switch; | |
142 | sg->ctlr = ctlr; | |
143 | ||
144 | /* This is a bit confusing. It looks like Julian calls back into the | |
145 | * adapter to find out how many outstanding transactions it can | |
146 | * handle. How does he handle a tape/disk combo? | |
147 | */ | |
148 | ||
149 | if (sg->sc_sw->adapter_info) | |
150 | { | |
151 | sg->ad_info = ( (*(sg->sc_sw->adapter_info))(unit)); | |
152 | sg->cmdscount = sg->ad_info & AD_INF_MAX_CMDS; | |
153 | if(sg->cmdscount > SGOUTSTANDING) | |
154 | sg->cmdscount = SGOUTSTANDING; | |
155 | } | |
156 | else | |
157 | { | |
158 | sg->ad_info = 1; | |
159 | sg->cmdscount = 1; | |
160 | } | |
161 | ||
162 | i = sg->cmdscount; | |
163 | ||
164 | scsi_xfer = (struct scsi_xfer *)malloc(sizeof(struct scsi_xfer) * | |
165 | i, M_TEMP, M_NOWAIT); | |
166 | ||
167 | if (scsi_xfer == 0) | |
168 | { | |
169 | printf("scsi_generic: Can't malloc.\n"); | |
170 | return 0; | |
171 | } | |
172 | ||
173 | while (i--) | |
174 | { | |
175 | scsi_xfer->next = sg->free_xfer; | |
176 | sg->free_xfer = scsi_xfer; | |
177 | scsi_xfer++; | |
178 | } | |
179 | ||
180 | #ifndef EMBEDDED | |
181 | if (unit == 0) | |
182 | printf(" /dev/gs%d (instance 0) generic SCSI via controller %d\n", | |
183 | scsi_addr, sg->ctlr); | |
184 | else | |
185 | printf(" /dev/gs%d-%d generic SCSI via controller %d\n", | |
186 | unit, scsi_addr, sg->ctlr); | |
187 | #endif | |
188 | ||
189 | return 1; | |
190 | } | |
191 | ||
192 | /* It is trivial to add support for processor target devices | |
193 | * here - enable target mode on open and disable on close | |
194 | * if a flag bit is set in the minor number | |
195 | */ | |
196 | int sgopen(dev_t dev) | |
197 | { | |
198 | if (SG(dev) == 0) | |
199 | return ENXIO; | |
200 | ||
201 | return 0; | |
202 | } | |
203 | ||
204 | int sgclose(dev_t dev) | |
205 | { | |
206 | return 0; | |
207 | } | |
208 | ||
209 | ||
210 | /* Free a scsi_xfer, wake processes waiting for it | |
211 | */ | |
212 | void sg_free_xs(dev_t dev, struct scsi_xfer *xs, int flags) | |
213 | { | |
214 | int s; | |
215 | struct sg *sg = SG(dev); | |
216 | ||
217 | if(flags & SCSI_NOMASK) | |
218 | { | |
219 | if (sg->free_xfer_wait) | |
220 | { | |
221 | printf("sg_free_xs: doing a wakeup from NOMASK mode!\n"); | |
222 | wakeup((caddr_t)&sg->free_xfer); | |
223 | } | |
224 | xs->next = sg->free_xfer; | |
225 | sg->free_xfer = xs; | |
226 | } | |
227 | else | |
228 | { | |
229 | s = SPLSG(); | |
230 | if (sg->free_xfer_wait) | |
231 | wakeup((caddr_t)&sg->free_xfer); | |
232 | xs->next = sg->free_xfer; | |
233 | sg->free_xfer = xs; | |
234 | splx(s); | |
235 | } | |
236 | } | |
237 | ||
238 | /* Get ownership of a scsi_xfer | |
239 | * If need be, sleep on it, until it comes free | |
240 | */ | |
241 | struct scsi_xfer *sg_get_xs(dev_t dev, int flags) | |
242 | { | |
243 | struct scsi_xfer *xs; | |
244 | int s; | |
245 | struct sg *sg = SG(dev); | |
246 | ||
247 | if(flags & (SCSI_NOSLEEP | SCSI_NOMASK)) | |
248 | { | |
249 | if (xs = sg->free_xfer) | |
250 | { | |
251 | sg->free_xfer = xs->next; | |
252 | xs->flags = 0; | |
253 | } | |
254 | } | |
255 | else | |
256 | { | |
257 | s = SPLSG(); | |
258 | while (!(xs = sg->free_xfer)) | |
259 | { | |
260 | sg->free_xfer_wait++; /* someone waiting! */ | |
261 | sleep((caddr_t)&sg->free_xfer, PRIBIO+1); | |
262 | sg->free_xfer_wait--; | |
263 | } | |
264 | sg->free_xfer = xs->next; | |
265 | splx(s); | |
266 | xs->flags = 0; | |
267 | } | |
268 | ||
269 | return xs; | |
270 | } | |
271 | ||
272 | /* We let the user interpret his own sense in the | |
273 | * generic scsi world | |
274 | */ | |
275 | int sg_interpret_sense(dev_t dev, struct scsi_xfer *xs, int *flag_p) | |
276 | { | |
277 | return 0; | |
278 | } | |
279 | ||
280 | /* ITSDONE is really used for things that are marked one | |
281 | * in the interrupt. I'll leave the logic in in case I want | |
282 | * to move done processing (and therefore have a start queue) | |
283 | * back into the interrupt. | |
284 | * BUG: No start queue. | |
285 | */ | |
286 | ||
287 | int sg_done(dev_t dev, | |
288 | struct scsi_xfer *xs) | |
289 | { | |
290 | xs->flags |= ITSDONE; | |
291 | wakeup(xs); | |
292 | return 0; | |
293 | } | |
294 | ||
295 | int sg_submit_cmd(dev_t dev, struct scsi_xfer *xs, dsreq_t *dsreq) | |
296 | { | |
297 | int retval; | |
298 | ||
299 | struct sg *sg = SG(dev); | |
300 | ||
301 | retry: | |
302 | xs->error = XS_NOERROR; | |
303 | ||
304 | xs->bp = 0; /* This bp doesn't seem to be used except to | |
305 | * disable sleeping in the host adapter code. | |
306 | * "st" does set it up, though. | |
307 | */ | |
308 | ||
309 | retval = (*(sg->sc_sw->scsi_cmd))(xs); | |
310 | ||
311 | switch(retval) | |
312 | { | |
313 | case SUCCESSFULLY_QUEUED: | |
314 | while(!(xs->flags & ITSDONE)) | |
315 | sleep(xs,PRIBIO+1); | |
316 | ||
317 | /* Fall through... */ | |
318 | ||
319 | case HAD_ERROR: | |
320 | ||
321 | if (dsreq) | |
322 | dsreq->ds_status = xs->status; | |
323 | ||
324 | switch(xs->error) | |
325 | { | |
326 | case XS_NOERROR: | |
327 | if (dsreq) | |
328 | dsreq->ds_datasent = dsreq->ds_datalen - xs->resid; | |
329 | retval = 0; | |
330 | break; | |
331 | ||
332 | case XS_SENSE: | |
333 | retval = (sg_interpret_sense(dev ,xs, (int *)0)); | |
334 | if (dsreq) | |
335 | { | |
336 | dsreq->ds_sensesent = sizeof(xs->sense); | |
337 | dsreq->ds_ret = DSRT_SENSE; | |
338 | } | |
339 | retval = 0; | |
340 | break; | |
341 | ||
342 | case XS_DRIVER_STUFFUP: | |
343 | if (dsreq) | |
344 | dsreq->ds_ret = DSRT_HOST; | |
345 | printf("sg%d: host adapter code inconsistency\n" ,G_SCSI_UNIT(dev)); | |
346 | retval = EIO; | |
347 | break; | |
348 | ||
349 | case XS_TIMEOUT: | |
350 | if (dsreq) | |
351 | dsreq->ds_ret = DSRT_TIMEOUT; | |
352 | retval = ETIMEDOUT; | |
353 | break; | |
354 | ||
355 | case XS_BUSY: | |
356 | if(xs->retries-- ) | |
357 | { | |
358 | xs->flags &= ~ITSDONE; | |
359 | goto retry; | |
360 | } | |
361 | retval = EBUSY; | |
362 | break; | |
363 | ||
364 | default: | |
365 | printf("sg%d: unknown error category from host adapter code\n" | |
366 | ,G_SCSI_UNIT(dev)); | |
367 | retval = EIO; | |
368 | break; | |
369 | } | |
370 | break; | |
371 | ||
372 | case COMPLETE: | |
373 | if (dsreq) | |
374 | dsreq->ds_datasent = dsreq->ds_datalen - xs->resid; | |
375 | retval = 0; | |
376 | break; | |
377 | ||
378 | case TRY_AGAIN_LATER: | |
379 | if(xs->retries-- ) | |
380 | { | |
381 | xs->flags &= ~ITSDONE; | |
382 | goto retry; | |
383 | } | |
384 | retval = EBUSY; | |
385 | break; | |
386 | ||
387 | case ESCAPE_NOT_SUPPORTED: | |
388 | retval = ENOSYS; /* "Function not implemented" */ | |
389 | break; | |
390 | ||
391 | default: | |
392 | printf("sg%d: illegal return from host adapter code\n", | |
393 | G_SCSI_UNIT(dev)); | |
394 | retval = EIO; | |
395 | break; | |
396 | } | |
397 | ||
398 | return retval; | |
399 | } | |
400 | ||
401 | /* sg_escape: Do a generic SCSI escape | |
402 | */ | |
403 | int sg_escape(dev_t dev, int op_code, u_char *b, int nb) | |
404 | { | |
405 | int retval; | |
406 | ||
407 | struct scsi_generic scsi_generic; | |
408 | ||
409 | int flags = SCSI_ESCAPE; | |
410 | ||
411 | struct scsi_xfer *xs; | |
412 | struct sg *sg = SG(dev); | |
413 | ||
414 | xs = sg_get_xs(dev, flags); | |
415 | ||
416 | if (xs == 0) | |
417 | { | |
418 | printf("sg_target%d: controller busy" | |
419 | " (this should never happen)\n",G_SCSI_UNIT(dev)); | |
420 | return EBUSY; | |
421 | } | |
422 | ||
423 | scsi_generic.opcode = op_code; | |
424 | bcopy(b, scsi_generic.bytes, nb); | |
425 | ||
426 | /* Fill out the scsi_xfer structure | |
427 | */ | |
428 | xs->flags = (flags|INUSE); | |
429 | xs->adapter = sg->ctlr; | |
430 | xs->cmd = &scsi_generic; | |
431 | xs->targ = G_SCSI_ID(dev); | |
432 | xs->lu = G_SCSI_LUN(dev); | |
433 | xs->retries = SG_RETRIES; | |
434 | xs->timeout = 100; | |
435 | xs->when_done = (flags & SCSI_NOMASK) | |
436 | ?(int (*)())0 | |
437 | :(int (*)())sg_done; | |
438 | xs->done_arg = dev; | |
439 | xs->done_arg2 = (int)xs; | |
440 | ||
441 | xs->status = 0; | |
442 | ||
443 | retval = sg_submit_cmd(dev, xs, 0); | |
444 | ||
445 | bcopy(scsi_generic.bytes, b, nb); | |
446 | ||
447 | sg_free_xs(dev,xs,flags); | |
448 | ||
449 | return retval; | |
450 | } | |
451 | ||
452 | /* sg_target: Turn on / off target mode | |
453 | */ | |
454 | int sg_target(dev_t dev, int enable) | |
455 | { | |
456 | u_char b0 = enable; | |
457 | return sg_escape(dev, SCSI_OP_TARGET, &b0, 1); | |
458 | } | |
459 | ||
460 | #ifdef EMBEDDED | |
461 | /* This should REALLY be a select call! | |
462 | * This is used in a stand alone system without an O/S. I didn't | |
463 | * have the time to add select, which the system was missing, | |
464 | * so I added this stuff to poll for the async arrival of | |
465 | * connections for target mode. | |
466 | */ | |
467 | int sg_poll(dev_t dev, int *send, int *recv) | |
468 | { | |
469 | scsi_op_poll_t s; | |
470 | int ret; | |
471 | ||
472 | ret = sg_escape(dev, SCSI_OP_POLL, (u_char *)&s, sizeof(s)); | |
473 | ||
474 | if (ret == 0) | |
475 | { | |
476 | *send = s.send; | |
477 | *recv = s.recv; | |
478 | } | |
479 | ||
480 | return ret; | |
481 | } | |
482 | #endif /* EMBEDDED */ | |
483 | ||
484 | int sg_scsi_cmd(dev_t dev, | |
485 | dsreq_t *dsreq, | |
486 | struct scsi_generic *scsi_cmd, | |
487 | u_char *d_addr, | |
488 | long d_count, | |
489 | struct scsi_sense_data *scsi_sense) | |
490 | { | |
491 | int retval; | |
492 | ||
493 | int flags = 0; | |
494 | struct scsi_xfer *xs; | |
495 | struct sg *sg = SG(dev); | |
496 | ||
497 | if (sg->sc_sw == 0) | |
498 | return ENODEV; | |
499 | ||
500 | dsreq->ds_status = 0; | |
501 | dsreq->ds_sensesent = 0; | |
502 | ||
503 | if (dsreq->ds_flags & DSRQ_READ) | |
504 | flags |= SCSI_DATA_IN; | |
505 | ||
506 | if (dsreq->ds_flags & DSRQ_WRITE) | |
507 | flags |= SCSI_DATA_OUT; | |
508 | ||
509 | if (dsreq->ds_flags & DSRQ_TARGET) | |
510 | flags |= SCSI_TARGET; | |
511 | ||
512 | if (dsreq->ds_flags & DSRQ_ESCAPE) | |
513 | flags |= SCSI_ESCAPE; | |
514 | ||
515 | #ifdef SCSI_PHYSADDR | |
516 | if (dsreq->ds_flags & DSRQ_PHYSADDR) | |
517 | flags |= SCSI_PHYSADDR; | |
518 | #endif | |
519 | ||
520 | xs = sg_get_xs(dev, flags); | |
521 | ||
522 | if (xs == 0) | |
523 | { | |
524 | printf("sg_scsi_cmd%d: controller busy" | |
525 | " (this should never happen)\n",G_SCSI_UNIT(dev)); | |
526 | ||
527 | return EBUSY; | |
528 | } | |
529 | ||
530 | /* Fill out the scsi_xfer structure | |
531 | */ | |
532 | xs->flags |= (flags|INUSE); | |
533 | xs->adapter = sg->ctlr; | |
534 | xs->targ = G_SCSI_ID(dev); | |
535 | xs->lu = G_SCSI_LUN(dev); | |
536 | xs->retries = SG_RETRIES; | |
537 | xs->timeout = dsreq->ds_time; | |
538 | xs->cmd = scsi_cmd; | |
539 | xs->cmdlen = dsreq->ds_cmdlen; | |
540 | xs->data = d_addr; | |
541 | xs->datalen = d_count; | |
542 | xs->resid = d_count; | |
543 | xs->when_done = (flags & SCSI_NOMASK) | |
544 | ?(int (*)())0 | |
545 | :(int (*)())sg_done; | |
546 | xs->done_arg = dev; | |
547 | xs->done_arg2 = (int)xs; | |
548 | ||
549 | xs->req_sense_length = (dsreq->ds_senselen < sizeof(struct scsi_sense_data)) | |
550 | ? dsreq->ds_senselen | |
551 | : sizeof(struct scsi_sense_data); | |
552 | xs->status = 0; | |
553 | ||
554 | retval = sg_submit_cmd(dev, xs, dsreq); | |
555 | ||
556 | if (dsreq->ds_ret == DSRT_SENSE) | |
557 | bcopy(&(xs->sense), scsi_sense, sizeof(xs->sense)); | |
558 | ||
559 | sg_free_xs(dev,xs,flags); | |
560 | ||
561 | return retval; | |
562 | } | |
563 | ||
564 | void sgerr(struct buf *bp, int err) | |
565 | { | |
566 | bp->b_error = err; | |
567 | bp->b_flags |= B_ERROR; | |
568 | ||
569 | iodone(bp); | |
570 | } | |
571 | ||
572 | /* strategy function | |
573 | * | |
574 | * Should I reorganize this so it returns to physio instead | |
575 | * of sleeping in sg_scsi_cmd? Is there any advantage, other | |
576 | * than avoiding the probable duplicate wakeup in iodone? | |
577 | * | |
578 | * Don't create a block device entry point for this | |
579 | * driver without making some fixes: | |
580 | * you have to be able to go from the bp to the dsreq somehow. | |
581 | */ | |
582 | void sgstrategy(struct buf *bp) | |
583 | { | |
584 | int err; | |
585 | struct scsi_generic scsi_generic; | |
586 | struct scsi_sense_data scsi_sense; | |
587 | int lun = G_SCSI_LUN(bp->b_dev); | |
588 | ||
589 | dsbuf_t *dsbuf = DSBUF_P(bp); | |
590 | dsreq_t *dsreq; | |
591 | ||
592 | if (dsbuf->magic != DSBUF_MAGIC) | |
593 | { | |
594 | printf("sgstrategy: struct buf not magic.\n"); | |
595 | sgerr(bp, EFAULT); | |
596 | return; | |
597 | } | |
598 | ||
599 | dsreq = dsbuf->dsreq; | |
600 | ||
601 | /* We're in trouble if physio tried to break up the | |
602 | * transfer: | |
603 | */ | |
604 | if (bp->b_bcount != dsreq->ds_datalen) | |
605 | { | |
606 | printf("sgstrategy unit%d: Transfer broken up.\n", | |
607 | G_SCSI_UNIT(bp->b_dev)); | |
608 | sgerr(bp, EIO); | |
609 | return; | |
610 | } | |
611 | ||
612 | dsreq->ds_ret = DSRT_OK; | |
613 | ||
614 | /* Reject 0 length timeouts. | |
615 | */ | |
616 | if (dsreq->ds_time == 0) | |
617 | { | |
618 | sgerr(bp, EINVAL); | |
619 | return; | |
620 | } | |
621 | ||
622 | if (dsreq->ds_cmdlen > sizeof(struct scsi_generic)) | |
623 | { | |
624 | sgerr(bp, EFAULT); | |
625 | return; | |
626 | } | |
627 | ||
628 | copyin(dsreq->ds_cmdbuf, (char *)&scsi_generic, dsreq->ds_cmdlen); | |
629 | ||
630 | /* Use device unit for the LUN. Using the one the user provided | |
631 | * would be a huge security problem. | |
632 | */ | |
633 | if ((dsreq->ds_flags & DSRQ_ESCAPE) == 0) | |
634 | scsi_generic.bytes[0] = (scsi_generic.bytes[0] & 0x1F) | (lun << 5); | |
635 | ||
636 | err = sg_scsi_cmd(bp->b_dev, dsreq, | |
637 | &scsi_generic, | |
638 | (u_char *)bp->b_un.b_addr, | |
639 | bp->b_bcount, | |
640 | &scsi_sense); | |
641 | ||
642 | if (dsreq->ds_sensesent) | |
643 | { | |
644 | if (dsreq->ds_sensesent > dsreq->ds_senselen) | |
645 | dsreq->ds_sensesent = dsreq->ds_senselen; | |
646 | ||
647 | copyout(&scsi_sense, dsreq->ds_sensebuf, dsreq->ds_sensesent); | |
648 | } | |
649 | ||
650 | if (err) | |
651 | { | |
652 | if (dsreq->ds_ret == DSRT_OK) | |
653 | dsreq->ds_ret = DSRT_DEVSCSI; | |
654 | ||
655 | sgerr(bp, err); | |
656 | return; | |
657 | } | |
658 | ||
659 | /* This is a fake. It would be nice to know if the | |
660 | * command was sent or not instead of pretending it was if | |
661 | * we get this far. That would involve adding "sent" members | |
662 | * to the xs so it could be set up down in the host adapter code. | |
663 | */ | |
664 | dsreq->ds_cmdsent = dsreq->ds_cmdlen; | |
665 | ||
666 | if (dsreq->ds_ret == 0) | |
667 | dsreq->ds_ret = DSRT_OK; | |
668 | ||
669 | iodone(bp); /* Shouldn't this iodone be done in the interrupt? | |
670 | */ | |
671 | ||
672 | return; | |
673 | } | |
674 | ||
675 | void sgminphys(struct buf *bp) | |
676 | { | |
677 | } | |
678 | ||
679 | int sgioctl(dev_t dev, int cmd, caddr_t addr, int f) | |
680 | { | |
681 | int ret = 0; | |
682 | int phys; | |
683 | ||
684 | switch(cmd) | |
685 | { | |
686 | case DS_ENTER: | |
687 | { | |
688 | dsreq_t *dsreq = (dsreq_t *)addr; | |
689 | ||
690 | int rwflag = (dsreq->ds_flags & DSRQ_READ) ? B_READ : B_WRITE; | |
691 | ||
692 | struct dsbuf dsbuf; | |
693 | struct buf *bp = &dsbuf.buf; | |
694 | ||
695 | bzero(&dsbuf, sizeof(dsbuf)); | |
696 | ||
697 | dsbuf.dsreq = dsreq; | |
698 | dsbuf.magic = DSBUF_MAGIC; | |
699 | ||
700 | #ifdef SCSI_PHYSADDR /* Physical memory addressing option */ | |
701 | phys = (dsreq->ds_flags & DSRQ_PHYSADDR); | |
702 | #else | |
703 | phys = 0; | |
704 | #endif | |
705 | ||
706 | if (phys) | |
707 | { | |
708 | bp->b_un.b_addr = dsreq->ds_databuf; | |
709 | bp->b_bcount = dsreq->ds_datalen; | |
710 | bp->b_dev = dev; | |
711 | bp->b_flags = rwflag; | |
712 | ||
713 | sgstrategy(bp); | |
714 | ret = bp->b_error; | |
715 | } | |
716 | else if (dsreq->ds_datalen) | |
717 | { | |
718 | struct uio uio; | |
719 | struct iovec iovec; | |
720 | ||
721 | iovec.iov_base = dsreq->ds_databuf; | |
722 | iovec.iov_len = dsreq->ds_datalen; | |
723 | ||
724 | uio.uio_offset = 0; | |
725 | uio.uio_resid = dsreq->ds_datalen; | |
726 | ||
727 | uio.uio_segflg = UIO_USERSPACE; | |
728 | uio.uio_procp = curproc; | |
729 | uio.uio_rw = (rwflag == B_READ) ? UIO_READ : UIO_WRITE; | |
730 | uio.uio_iov = &iovec; | |
731 | uio.uio_iovcnt = 1; | |
732 | ||
733 | if ((ret = rawio(dev, &uio, bp)) == 0) | |
734 | ret = bp->b_error; | |
735 | } | |
736 | else | |
737 | { | |
738 | bp->b_un.b_addr = 0; | |
739 | bp->b_bcount = 0; | |
740 | bp->b_dev = dev; | |
741 | bp->b_flags = 0; | |
742 | ||
743 | sgstrategy(bp); | |
744 | ret = bp->b_error; | |
745 | } | |
746 | ||
747 | } | |
748 | break; | |
749 | ||
750 | case DS_TARGET: | |
751 | ret = sg_target(dev, *(int *)addr); | |
752 | break; | |
753 | ||
754 | default: | |
755 | ret = ENOTTY; | |
756 | break; | |
757 | } | |
758 | ||
759 | return ret; | |
760 | } |