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