Commit | Line | Data |
---|---|---|
519fb2b7 RG |
1 | /* |
2 | * Contributed by HD Associates (hd@world.std.com). | |
3 | * Copyright (c) 1992, 1993 HD Associates | |
4 | * | |
5 | * Berkeley style copyright. | |
6 | * | |
7 | * | |
8 | */ | |
1d7f5b47 | 9 | #include <sys/types.h> |
519fb2b7 | 10 | #include <sys/param.h> |
1d7f5b47 DG |
11 | #include <machine/param.h> |
12 | #include <vm/vm_statistics.h> | |
13 | #include <vm/vm_param.h> | |
14 | #include <vm/lock.h> | |
15 | #include <machine/pmap.h> | |
16 | #include <machine/vmparam.h> | |
fde1aeb2 GW |
17 | #include "systm.h" |
18 | #include <sys/errno.h> | |
519fb2b7 RG |
19 | #include <sys/malloc.h> |
20 | #include <sys/buf.h> | |
21 | #define b_screq b_driver1 /* a patch in buf.h */ | |
22 | #define b_sc_link b_driver2 /* a patch in buf.h */ | |
23 | #include <sys/proc.h> | |
24 | ||
25 | #include "scbus.h" | |
26 | #include <scsi/scsi_all.h> | |
27 | #include <scsi/scsiconf.h> | |
28 | #include <sys/scsiio.h> | |
29 | ||
4c45483e | 30 | void scsierr(struct buf *, int); /* XXX ??? */ |
519fb2b7 RG |
31 | |
32 | /* | |
33 | * We let the user interpret his own sense in the generic scsi world. | |
34 | * This routine is called at interrupt time if the SCSI_USER bit was set | |
35 | * in the flags passed to scsi_scsi_cmd(). No other completion processing | |
36 | * takes place, even if we are running over another device driver. | |
37 | * The lower level routines that call us here, will free the xs and restart | |
38 | * the device's queue if such exists. | |
39 | */ | |
40 | #ifndef min | |
41 | #define min(A,B) ((A<B) ? A : B ) | |
42 | #endif | |
43 | ||
44 | void scsi_user_done(xs) | |
45 | struct scsi_xfer *xs; | |
46 | { | |
47 | ||
48 | struct buf *bp; | |
49 | scsireq_t *screq; | |
50 | ||
51 | bp = xs->bp; | |
52 | if(!bp) { /* ALL user requests must have a buf */ | |
53 | sc_print_addr(xs->sc_link); | |
54 | printf("User command with no buf\n"); | |
55 | return ; | |
56 | } | |
57 | screq = bp->b_screq; | |
58 | if (!screq) { /* Is it one of ours? (the SCSI_USER bit says it is) */ | |
59 | sc_print_addr(xs->sc_link); | |
60 | printf("User command with no request\n"); | |
61 | return ; | |
62 | } | |
63 | ||
64 | SC_DEBUG(xs->sc_link,SDEV_DB2,("user-done\n")); | |
65 | screq->retsts = 0; | |
66 | screq->status = xs->status; | |
fde1aeb2 | 67 | switch((int)xs->error) { |
519fb2b7 RG |
68 | case XS_NOERROR: |
69 | SC_DEBUG(xs->sc_link,SDEV_DB3,("no error\n")); | |
70 | screq->datalen_used = xs->datalen - xs->resid; /* probably rubbish */ | |
71 | screq->retsts = SCCMD_OK; | |
72 | break; | |
73 | ||
74 | case XS_SENSE: | |
75 | SC_DEBUG(xs->sc_link,SDEV_DB3,("have sense\n")); | |
76 | screq->senselen_used = min(sizeof(xs->sense),SENSEBUFLEN); | |
77 | bcopy(&xs->sense,screq->sense,screq->senselen); | |
78 | screq->retsts = SCCMD_SENSE; | |
79 | break; | |
80 | ||
81 | case XS_DRIVER_STUFFUP: | |
82 | sc_print_addr(xs->sc_link); | |
83 | printf("host adapter code inconsistency\n"); | |
84 | screq->retsts = SCCMD_UNKNOWN; | |
85 | break; | |
86 | ||
87 | case XS_TIMEOUT: | |
88 | SC_DEBUG(xs->sc_link,SDEV_DB3,("timeout\n")); | |
89 | screq->retsts = SCCMD_TIMEOUT; | |
90 | break; | |
91 | ||
92 | case XS_BUSY: | |
93 | SC_DEBUG(xs->sc_link,SDEV_DB3,("busy\n")); | |
94 | screq->retsts = SCCMD_BUSY; | |
95 | break; | |
96 | ||
97 | default: | |
98 | sc_print_addr(xs->sc_link); | |
99 | printf("unknown error category from host adapter code\n"); | |
100 | screq->retsts = SCCMD_UNKNOWN; | |
101 | break; | |
102 | } | |
103 | biodone(bp); /* we're waiting on it in scsi_strategy() */ | |
104 | return; /* it'll free the xs and restart any queue */ | |
105 | } | |
106 | ||
107 | ||
108 | /* Pseudo strategy function | |
109 | * Called by scsi_do_ioctl() via physio/physstrat if there is to | |
110 | * be data transfered, and directly if there is no data transfer. | |
111 | * | |
112 | * Should I reorganize this so it returns to physio instead | |
113 | * of sleeping in scsiio_scsi_cmd? Is there any advantage, other | |
114 | * than avoiding the probable duplicate wakeup in iodone? [PD] | |
115 | * | |
116 | * No, seems ok to me... [JRE] | |
117 | * (I don't see any duplicate wakeups) | |
118 | * | |
119 | * Can't be used with block devices or raw_read/raw_write directly | |
120 | * from the cdevsw/bdevsw tables because they couldn't have added | |
121 | * the screq structure. [JRE] | |
122 | */ | |
123 | void scsistrategy(struct buf *bp) | |
124 | { | |
125 | errval err; | |
126 | struct scsi_link *sc_link = bp->b_sc_link; | |
127 | scsireq_t *screq; | |
128 | u_int32 flags = 0; | |
129 | int s; | |
130 | ||
131 | ||
132 | if(!sc_link) { | |
133 | printf("user_strat: No link pointer\n"); | |
134 | scsierr(bp,EINVAL); | |
135 | return; | |
136 | } | |
137 | SC_DEBUG(sc_link,SDEV_DB2,("user_strategy\n")); | |
138 | screq = bp->b_screq; | |
139 | if(!screq) { | |
140 | sc_print_addr(sc_link); | |
141 | printf("No request block\n"); | |
142 | scsierr(bp,EINVAL); | |
143 | return; | |
144 | } | |
145 | ||
146 | /* We're in trouble if physio tried to break up the | |
147 | * transfer: | |
148 | */ | |
149 | if (bp->b_bcount != screq->datalen) { | |
150 | sc_print_addr(sc_link); | |
151 | printf("physio split the request.. cannot proceed\n"); | |
152 | scsierr(bp, EIO); | |
153 | return; | |
154 | } | |
155 | ||
156 | if (screq->timeout == 0) { | |
157 | scsierr(bp, EINVAL); | |
158 | return; | |
159 | } | |
160 | ||
161 | if (screq->cmdlen > sizeof(struct scsi_generic)) { | |
162 | sc_print_addr(sc_link); | |
163 | printf("cmdlen too big "); | |
164 | scsierr(bp, EFAULT); | |
165 | return; | |
166 | } | |
167 | ||
168 | ||
169 | if (screq->flags & SCCMD_READ) | |
170 | flags |= SCSI_DATA_IN; | |
171 | ||
172 | if (screq->flags & SCCMD_WRITE) | |
173 | flags |= SCSI_DATA_OUT; | |
174 | ||
175 | if (screq->flags & SCCMD_TARGET) | |
176 | flags |= SCSI_TARGET; | |
177 | ||
178 | if (screq->flags & SCCMD_ESCAPE) | |
179 | flags |= SCSI_ESCAPE; | |
180 | err = scsi_scsi_cmd(sc_link, | |
181 | (struct scsi_generic *)screq->cmd, | |
182 | screq->cmdlen, | |
183 | (u_char *)bp->b_un.b_addr, | |
184 | screq->datalen, | |
185 | 0, /* user must do the retries *//* ignored */ | |
186 | screq->timeout, | |
187 | bp, | |
188 | flags | SCSI_USER); | |
189 | ||
190 | ||
191 | ||
192 | /*because there is a bp, scsi_scsi_cmd will return immediatly*/ | |
193 | if (err) | |
194 | { | |
195 | scsierr(bp, err); | |
196 | return; | |
197 | } | |
198 | SC_DEBUG(sc_link,SDEV_DB3,("about to sleep\n")); | |
199 | s = splbio(); | |
200 | while(!(bp->b_flags & B_DONE)) | |
201 | { | |
42587237 | 202 | tsleep((caddr_t)bp, PRIBIO, "scsistrat", 0); |
519fb2b7 RG |
203 | } |
204 | splx(s); | |
205 | SC_DEBUG(sc_link,SDEV_DB3,("back from sleep\n")); | |
206 | return; | |
207 | } | |
208 | ||
209 | void scsiminphys(struct buf *bp) | |
210 | { | |
211 | /*XXX*//* call the adapter's minphys */ | |
212 | } | |
213 | ||
214 | ||
215 | /* | |
216 | * Something (e.g. another driver) has called us | |
217 | * with an sc_link for a target/lun/adapter, and a scsi | |
218 | * specific ioctl to perform, better try. | |
219 | * If user-level type command, we must still be running | |
220 | * in the context of the calling process | |
221 | */ | |
222 | errval scsi_do_ioctl(struct scsi_link *sc_link, int cmd, caddr_t addr, int f) | |
223 | { | |
224 | errval ret = 0; | |
225 | int phys; | |
226 | ||
227 | SC_DEBUG(sc_link,SDEV_DB2,("scsi_do_ioctl(0x%x)\n",cmd)); | |
228 | switch(cmd) | |
229 | { | |
230 | #ifndef NetBSD | |
231 | case SCIOCCOMMAND: | |
232 | { | |
233 | /* | |
234 | * You won't believe this, but the arg copied in | |
235 | * from the user space, is on the kernel stack | |
236 | * for this process, so we can't write | |
237 | * to it at interrupt time.. | |
238 | * we need to copy it in and out! | |
239 | * Make a static copy using malloc! | |
240 | */ | |
241 | scsireq_t *screq2 = (scsireq_t *)addr; | |
242 | scsireq_t *screq = (scsireq_t *)addr; | |
243 | int rwflag = (screq->flags & SCCMD_READ) ? B_READ : B_WRITE; | |
244 | struct buf *bp; | |
245 | caddr_t d_addr; | |
246 | int len; | |
247 | ||
1d7f5b47 | 248 | if((unsigned int)screq < (unsigned int)KERNBASE) |
519fb2b7 RG |
249 | { |
250 | screq = malloc(sizeof(scsireq_t),M_TEMP,M_WAITOK); | |
251 | bcopy(screq2,screq,sizeof(scsireq_t)); | |
252 | } | |
253 | bp = malloc(sizeof (struct buf),M_TEMP,M_WAITOK); | |
254 | bzero(bp,sizeof(struct buf)); | |
255 | d_addr = screq->databuf; | |
256 | bp->b_bcount = len = screq->datalen; | |
257 | bp->b_screq = screq; | |
258 | bp->b_sc_link = sc_link; | |
259 | if (len) { | |
260 | /* have data, translate it. (physio)*/ | |
261 | #ifdef __NetBSD__ | |
262 | #error "dev, mincntfn & uio need defining" | |
263 | ret = physio(scsistrategy, bp, dev, rwflag, | |
264 | mincntfn, uio); | |
265 | #else | |
266 | ret = physio(scsistrategy,0,bp,0,rwflag, | |
267 | d_addr,&len,curproc); | |
268 | #endif | |
269 | } else { | |
270 | /* if no data, no need to translate it.. */ | |
271 | bp->b_un.b_addr = 0; | |
272 | bp->b_dev = -1; /* irrelevant info */ | |
273 | bp->b_flags = 0; | |
274 | ||
275 | scsistrategy(bp); | |
276 | ret = bp->b_error; | |
277 | } | |
278 | free(bp,M_TEMP); | |
1d7f5b47 | 279 | if((unsigned int)screq2 < (unsigned int)KERNBASE) |
519fb2b7 RG |
280 | { |
281 | bcopy(screq,screq2,sizeof(scsireq_t)); | |
282 | free(screq,M_TEMP); | |
283 | } | |
284 | break; | |
285 | } | |
286 | #endif /* !NetBSD */ | |
287 | case SCIOCDEBUG: | |
288 | { | |
289 | int level = *((int *)addr); | |
290 | SC_DEBUG(sc_link,SDEV_DB3,("debug set to %d\n",level)); | |
291 | sc_link->flags &= ~SDEV_DBX; /*clear debug bits */ | |
292 | if(level & 1) sc_link->flags |= SDEV_DB1; | |
293 | if(level & 2) sc_link->flags |= SDEV_DB2; | |
294 | if(level & 4) sc_link->flags |= SDEV_DB3; | |
295 | if(level & 8) sc_link->flags |= SDEV_DB4; | |
296 | ret = 0; | |
297 | break; | |
298 | } | |
299 | case SCIOCREPROBE: | |
300 | { | |
301 | extern int scsibus; | |
302 | struct scsi_addr *sca = (struct scsi_addr *) addr; | |
303 | ||
304 | ret = scsi_probe_busses(sca->scbus,sca->target,sca->lun); | |
305 | break; | |
306 | } | |
307 | case SCIOCRECONFIG: | |
308 | case SCIOCDECONFIG: | |
309 | ret = EINVAL; | |
310 | break; | |
311 | case SCIOCIDENTIFY: | |
312 | { | |
313 | struct scsi_addr *sca = (struct scsi_addr *) addr; | |
314 | sca->scbus = sc_link->scsibus; | |
315 | sca->target = sc_link->target; | |
316 | sca->lun = sc_link->lun; | |
317 | break; | |
318 | } | |
319 | ||
320 | default: | |
321 | ret = ENOTTY; | |
322 | break; | |
323 | } | |
324 | ||
325 | return ret; | |
326 | } | |
327 | ||
4c45483e | 328 | void |
519fb2b7 | 329 | scsierr(bp,err) |
4c45483e GW |
330 | struct buf *bp; |
331 | int err; | |
519fb2b7 RG |
332 | { |
333 | bp->b_flags |= B_ERROR; | |
334 | bp->b_error = err; | |
335 | biodone(bp); | |
336 | return; | |
337 | } | |
338 |