Commit | Line | Data |
---|---|---|
869c4419 | 1 | /* |
98639498 | 2 | * Written by Julian Elischer (julian@tfs.com) |
869c4419 | 3 | * |
98639498 | 4 | * $Id$ |
15637ed4 RG |
5 | */ |
6 | ||
7 | #include <sys/types.h> | |
8 | #include <ch.h> | |
9 | ||
10 | #include <sys/param.h> | |
11 | #include <sys/systm.h> | |
12 | ||
13 | #include <sys/errno.h> | |
14 | #include <sys/ioctl.h> | |
15 | #include <sys/buf.h> | |
16 | #include <sys/proc.h> | |
17 | #include <sys/user.h> | |
18 | #include <sys/chio.h> | |
19 | ||
20 | #if defined(OSF) | |
21 | #define SECSIZE 512 | |
22 | #endif /* defined(OSF) */ | |
23 | ||
24 | #include <scsi/scsi_all.h> | |
25 | #include <scsi/scsi_changer.h> | |
26 | #include <scsi/scsiconf.h> | |
27 | ||
28 | ||
29 | struct scsi_xfer ch_scsi_xfer[NCH]; | |
30 | int ch_xfer_block_wait[NCH]; | |
31 | ||
32 | ||
33 | #define PAGESIZ 4096 | |
34 | #define STQSIZE 4 | |
35 | #define CH_RETRIES 4 | |
36 | ||
37 | ||
38 | #define MODE(z) ( (minor(z) & 0x0F) ) | |
39 | #define UNIT(z) ( (minor(z) >> 4) ) | |
40 | ||
41 | #ifndef MACH | |
42 | #define ESUCCESS 0 | |
43 | #endif MACH | |
44 | ||
45 | int ch_info_valid[NCH]; /* the info about the device is valid */ | |
46 | int ch_initialized[NCH] ; | |
47 | int ch_debug = 1; | |
48 | ||
49 | int chattach(); | |
50 | int ch_done(); | |
51 | struct ch_data | |
52 | { | |
53 | int flags; | |
54 | struct scsi_switch *sc_sw; /* address of scsi low level switch */ | |
55 | int ctlr; /* so they know which one we want */ | |
56 | int targ; /* our scsi target ID */ | |
57 | int lu; /* out scsi lu */ | |
58 | short chmo; /* Offset of first CHM */ | |
59 | short chms; /* No. of CHM */ | |
60 | short slots; /* No. of Storage Elements */ | |
61 | short sloto; /* Offset of first SE */ | |
62 | short imexs; /* No. of Import/Export Slots */ | |
63 | short imexo; /* Offset of first IM/EX */ | |
64 | short drives; /* No. of CTS */ | |
65 | short driveo; /* Offset of first CTS */ | |
66 | short rot; /* CHM can rotate */ | |
67 | u_long op_matrix; /* possible opertaions */ | |
68 | u_short lsterr; /* details of lasterror */ | |
69 | u_char stor; /* posible Storage locations */ | |
70 | }ch_data[NCH]; | |
71 | ||
72 | #define CH_OPEN 0x01 | |
73 | #define CH_KNOWN 0x02 | |
74 | ||
75 | static int next_ch_unit = 0; | |
76 | /***********************************************************************\ | |
77 | * The routine called by the low level scsi routine when it discovers * | |
78 | * A device suitable for this driver * | |
79 | \***********************************************************************/ | |
80 | ||
81 | int chattach(ctlr,targ,lu,scsi_switch) | |
82 | struct scsi_switch *scsi_switch; | |
83 | { | |
84 | int unit,i,stat; | |
85 | unsigned char *tbl; | |
86 | ||
87 | if(scsi_debug & PRINTROUTINES) printf("chattach: "); | |
88 | /*******************************************************\ | |
89 | * Check we have the resources for another drive * | |
90 | \*******************************************************/ | |
91 | unit = next_ch_unit++; | |
92 | if( unit >= NCH) | |
93 | { | |
869c4419 | 94 | printf("Too many scsi changers..(%d > %d) reconfigure kernel\n",(unit + 1),NCH); |
15637ed4 RG |
95 | return(0); |
96 | } | |
97 | /*******************************************************\ | |
98 | * Store information needed to contact our base driver * | |
99 | \*******************************************************/ | |
100 | ch_data[unit].sc_sw = scsi_switch; | |
101 | ch_data[unit].ctlr = ctlr; | |
102 | ch_data[unit].targ = targ; | |
103 | ch_data[unit].lu = lu; | |
104 | ||
105 | /*******************************************************\ | |
106 | * Use the subdriver to request information regarding * | |
107 | * the drive. We cannot use interrupts yet, so the * | |
108 | * request must specify this. * | |
109 | \*******************************************************/ | |
110 | if((ch_mode_sense(unit, SCSI_NOSLEEP | SCSI_NOMASK /*| SCSI_SILENT*/))) | |
111 | { | |
98639498 | 112 | printf("ch%d: scsi changer, %d slot(s) %d drive(s) %d arm(s) %d i/e-slot(s)\n", |
15637ed4 RG |
113 | unit, ch_data[unit].slots, ch_data[unit].drives, ch_data[unit].chms, ch_data[unit].imexs); |
114 | stat=CH_KNOWN; | |
115 | } | |
116 | else | |
117 | { | |
98639498 | 118 | printf("ch%d: scsi changer :- offline\n", unit); |
15637ed4 RG |
119 | stat=CH_OPEN; |
120 | } | |
121 | ch_initialized[unit] = stat; | |
122 | ||
123 | return; | |
124 | ||
125 | } | |
126 | ||
127 | ||
128 | ||
129 | /*******************************************************\ | |
130 | * open the device. * | |
131 | \*******************************************************/ | |
132 | chopen(dev) | |
133 | { | |
134 | int errcode = 0; | |
135 | int unit,mode; | |
136 | ||
137 | unit = UNIT(dev); | |
138 | mode = MODE(dev); | |
139 | ||
140 | /*******************************************************\ | |
141 | * Check the unit is legal * | |
142 | \*******************************************************/ | |
143 | if ( unit >= NCH ) | |
144 | { | |
98639498 | 145 | printf("ch%d: ch %d > %d\n",unit,unit,NCH); |
15637ed4 RG |
146 | errcode = ENXIO; |
147 | return(errcode); | |
148 | } | |
149 | /*******************************************************\ | |
150 | * Only allow one at a time * | |
151 | \*******************************************************/ | |
152 | if(ch_data[unit].flags & CH_OPEN) | |
153 | { | |
98639498 | 154 | printf("ch%d: already open\n",unit); |
15637ed4 RG |
155 | errcode = ENXIO; |
156 | goto bad; | |
157 | } | |
158 | ||
159 | if(ch_debug||(scsi_debug & (PRINTROUTINES | TRACEOPENS))) | |
160 | printf("chopen: dev=0x%x (unit %d (of %d))\n" | |
161 | , dev, unit, NCH); | |
162 | /*******************************************************\ | |
163 | * Make sure the device has been initialised * | |
164 | \*******************************************************/ | |
165 | ||
166 | if (!ch_initialized[unit]) | |
167 | return(ENXIO); | |
168 | if (ch_initialized[unit]!=CH_KNOWN) { | |
169 | if((ch_mode_sense(unit, SCSI_NOSLEEP | SCSI_NOMASK /*| SCSI_SILENT*/))) | |
170 | { | |
171 | ch_initialized[unit]=CH_KNOWN; | |
172 | } | |
173 | else | |
174 | { | |
98639498 | 175 | printf("ch%d: scsi changer :- offline\n", unit); |
15637ed4 RG |
176 | return(ENXIO); |
177 | } | |
178 | } | |
179 | /*******************************************************\ | |
180 | * Check that it is still responding and ok. * | |
181 | \*******************************************************/ | |
182 | ||
183 | if(ch_debug || (scsi_debug & TRACEOPENS)) | |
184 | printf("device is "); | |
185 | if (!(ch_req_sense(unit, 0))) | |
186 | { | |
187 | errcode = ENXIO; | |
188 | if(ch_debug || (scsi_debug & TRACEOPENS)) | |
189 | printf("not responding\n"); | |
190 | goto bad; | |
191 | } | |
192 | if(ch_debug || (scsi_debug & TRACEOPENS)) | |
193 | printf("ok\n"); | |
194 | ||
195 | if(!(ch_test_ready(unit,0))) | |
196 | { | |
98639498 | 197 | printf("ch%d: not ready\n",unit); |
15637ed4 RG |
198 | return(EIO); |
199 | } | |
200 | ||
201 | ch_info_valid[unit] = TRUE; | |
202 | ||
203 | /*******************************************************\ | |
204 | * Load the physical device parameters * | |
205 | \*******************************************************/ | |
206 | ||
207 | ch_data[unit].flags = CH_OPEN; | |
208 | return(errcode); | |
209 | bad: | |
210 | return(errcode); | |
211 | } | |
212 | ||
213 | /*******************************************************\ | |
214 | * close the device.. only called if we are the LAST * | |
215 | * occurence of an open device * | |
216 | \*******************************************************/ | |
217 | chclose(dev) | |
218 | { | |
219 | unsigned char unit,mode; | |
220 | ||
221 | unit = UNIT(dev); | |
222 | mode = MODE(dev); | |
223 | ||
224 | if(scsi_debug & TRACEOPENS) | |
225 | printf("Closing device"); | |
226 | ch_data[unit].flags = 0; | |
227 | return(0); | |
228 | } | |
229 | ||
230 | ||
231 | ||
232 | /***************************************************************\ | |
233 | * chstart * | |
234 | * This routine is also called after other non-queued requests * | |
235 | * have been made of the scsi driver, to ensure that the queue * | |
236 | * continues to be drained. * | |
237 | \***************************************************************/ | |
238 | /* chstart() is called at splbio */ | |
239 | chstart(unit) | |
240 | { | |
241 | int drivecount; | |
242 | register struct buf *bp = 0; | |
243 | register struct buf *dp; | |
244 | struct scsi_xfer *xs; | |
245 | int blkno, nblk; | |
246 | ||
247 | ||
248 | if(scsi_debug & PRINTROUTINES) printf("chstart%d ",unit); | |
249 | /*******************************************************\ | |
250 | * See if there is a buf to do and we are not already * | |
251 | * doing one * | |
252 | \*******************************************************/ | |
253 | xs=&ch_scsi_xfer[unit]; | |
254 | if(xs->flags & INUSE) | |
255 | { | |
256 | return; /* unit already underway */ | |
257 | } | |
258 | if(ch_xfer_block_wait[unit]) /* a special awaits, let it proceed first */ | |
259 | { | |
260 | wakeup(&ch_xfer_block_wait[unit]); | |
261 | return; | |
262 | } | |
263 | ||
264 | return; | |
265 | ||
266 | } | |
267 | ||
268 | ||
269 | /*******************************************************\ | |
270 | * This routine is called by the scsi interrupt when * | |
271 | * the transfer is complete. | |
272 | \*******************************************************/ | |
273 | int ch_done(unit,xs) | |
274 | int unit; | |
275 | struct scsi_xfer *xs; | |
276 | { | |
277 | struct buf *bp; | |
278 | int retval; | |
279 | ||
280 | if(ch_debug||(scsi_debug & PRINTROUTINES)) printf("ch_done%d ",unit); | |
281 | if (! (xs->flags & INUSE)) | |
282 | panic("scsi_xfer not in use!"); | |
283 | wakeup(xs); | |
284 | } | |
285 | /*******************************************************\ | |
286 | * Perform special action on behalf of the user * | |
287 | * Knows about the internals of this device * | |
288 | \*******************************************************/ | |
289 | chioctl(dev, cmd, arg, mode) | |
290 | dev_t dev; | |
291 | int cmd; | |
292 | caddr_t arg; | |
293 | { | |
294 | /* struct ch_cmd_buf *args;*/ | |
295 | union scsi_cmd *scsi_cmd; | |
296 | register i,j; | |
297 | unsigned int opri; | |
298 | int errcode = 0; | |
299 | unsigned char unit; | |
300 | int number,flags,ret; | |
301 | ||
302 | /*******************************************************\ | |
303 | * Find the device that the user is talking about * | |
304 | \*******************************************************/ | |
305 | flags = 0; /* give error messages, act on errors etc. */ | |
306 | unit = UNIT(dev); | |
307 | ||
308 | switch(cmd) | |
309 | { | |
310 | case CHIOOP: { | |
311 | struct chop *ch=(struct chop *) arg; | |
312 | if (ch_debug) | |
313 | printf("[chtape_chop: %x]\n", ch->ch_op); | |
314 | ||
315 | switch ((short)(ch->ch_op)) { | |
316 | case CHGETPARAM: | |
317 | ch->u.getparam.chmo= ch_data[unit].chmo; | |
318 | ch->u.getparam.chms= ch_data[unit].chms; | |
319 | ch->u.getparam.sloto= ch_data[unit].sloto; | |
320 | ch->u.getparam.slots= ch_data[unit].slots; | |
321 | ch->u.getparam.imexo= ch_data[unit].imexo; | |
322 | ch->u.getparam.imexs= ch_data[unit].imexs; | |
323 | ch->u.getparam.driveo= ch_data[unit].driveo; | |
324 | ch->u.getparam.drives= ch_data[unit].drives; | |
325 | ch->u.getparam.rot= ch_data[unit].rot; | |
326 | ch->result=0; | |
327 | return 0; | |
328 | break; | |
329 | case CHPOSITION: | |
330 | return ch_position(unit,&ch->result,ch->u.position.chm, | |
331 | ch->u.position.to, | |
332 | flags); | |
333 | case CHMOVE: | |
334 | return ch_move(unit,&ch->result, ch->u.position.chm, | |
335 | ch->u.move.from, ch->u.move.to, | |
336 | flags); | |
337 | case CHGETELEM: | |
338 | return ch_getelem(unit,&ch->result, ch->u.get_elem_stat.type, | |
339 | ch->u.get_elem_stat.from, &ch->u.get_elem_stat.elem_data, | |
340 | flags); | |
341 | default: | |
342 | return EINVAL; | |
343 | } | |
344 | ||
345 | } | |
346 | default: | |
347 | return EINVAL; | |
348 | } | |
349 | ||
350 | return(ret?ESUCCESS:EIO); | |
351 | } | |
352 | ||
353 | ch_getelem(unit,stat,type,from,data,flags) | |
354 | int unit,from,flags; | |
355 | short *stat; | |
356 | char *data; | |
357 | { | |
358 | struct scsi_read_element_status scsi_cmd; | |
359 | char elbuf[32]; | |
360 | int ret; | |
361 | ||
362 | bzero(&scsi_cmd, sizeof(scsi_cmd)); | |
363 | scsi_cmd.op_code = READ_ELEMENT_STATUS; | |
869c4419 | 364 | scsi_cmd.byte2 = type; |
15637ed4 RG |
365 | scsi_cmd.starting_element_addr[0]=(from>>8)&0xff; |
366 | scsi_cmd.starting_element_addr[1]=from&0xff; | |
367 | scsi_cmd.number_of_elements[1]=1; | |
368 | scsi_cmd.allocation_length[2]=32; | |
369 | ||
370 | if ((ret=ch_scsi_cmd(unit, | |
371 | &scsi_cmd, | |
372 | sizeof(scsi_cmd), | |
373 | elbuf, | |
374 | 32, | |
375 | 100000, | |
376 | flags) !=ESUCCESS)) { | |
377 | *stat=ch_data[unit].lsterr; | |
378 | bcopy(elbuf+16,data,16); | |
379 | return ret; | |
380 | } | |
381 | bcopy(elbuf+16,data,16); /*Just a hack sh */ | |
382 | return ret; | |
383 | } | |
384 | ||
385 | ch_move(unit,stat,chm,from,to,flags) | |
386 | int unit,chm,from,to,flags; | |
387 | short *stat; | |
388 | { | |
389 | struct scsi_move_medium scsi_cmd; | |
390 | int ret; | |
391 | ||
392 | bzero(&scsi_cmd, sizeof(scsi_cmd)); | |
393 | scsi_cmd.op_code = MOVE_MEDIUM; | |
394 | scsi_cmd.transport_element_address[0]=(chm>>8)&0xff; | |
395 | scsi_cmd.transport_element_address[1]=chm&0xff; | |
396 | scsi_cmd.source_address[0]=(from>>8)&0xff; | |
397 | scsi_cmd.source_address[1]=from&0xff; | |
398 | scsi_cmd.destination_address[0]=(to>>8)&0xff; | |
399 | scsi_cmd.destination_address[1]=to&0xff; | |
400 | scsi_cmd.invert=(chm&CH_INVERT)?1:0; | |
401 | if ((ret=ch_scsi_cmd(unit, | |
402 | &scsi_cmd, | |
403 | sizeof(scsi_cmd), | |
404 | NULL, | |
405 | 0, | |
406 | 100000, | |
407 | flags) !=ESUCCESS)) { | |
408 | *stat=ch_data[unit].lsterr; | |
409 | return ret; | |
410 | } | |
411 | return ret; | |
412 | } | |
413 | ||
414 | ch_position(unit,stat,chm,to,flags) | |
415 | int unit,chm,to,flags; | |
416 | short *stat; | |
417 | { | |
418 | struct scsi_position_to_element scsi_cmd; | |
419 | int ret; | |
420 | ||
421 | bzero(&scsi_cmd, sizeof(scsi_cmd)); | |
422 | scsi_cmd.op_code = POSITION_TO_ELEMENT; | |
423 | scsi_cmd.transport_element_address[0]=(chm>>8)&0xff; | |
424 | scsi_cmd.transport_element_address[1]=chm&0xff; | |
425 | scsi_cmd.source_address[0]=(to>>8)&0xff; | |
426 | scsi_cmd.source_address[1]=to&0xff; | |
427 | scsi_cmd.invert=(chm&CH_INVERT)?1:0; | |
428 | if ((ret=ch_scsi_cmd(unit, | |
429 | &scsi_cmd, | |
430 | sizeof(scsi_cmd), | |
431 | NULL, | |
432 | 0, | |
433 | 100000, | |
434 | flags) !=ESUCCESS)) { | |
435 | *stat=ch_data[unit].lsterr; | |
436 | return ret; | |
437 | } | |
438 | return ret; | |
439 | } | |
440 | ||
441 | /*******************************************************\ | |
442 | * Check with the device that it is ok, (via scsi driver)* | |
443 | \*******************************************************/ | |
444 | ch_req_sense(unit, flags) | |
445 | int flags; | |
446 | { | |
447 | struct scsi_sense_data sense; | |
448 | struct scsi_sense scsi_cmd; | |
449 | ||
450 | bzero(&scsi_cmd, sizeof(scsi_cmd)); | |
451 | scsi_cmd.op_code = REQUEST_SENSE; | |
452 | scsi_cmd.length = sizeof(sense); | |
453 | ||
454 | if (ch_scsi_cmd(unit, | |
455 | &scsi_cmd, | |
456 | sizeof(struct scsi_sense), | |
457 | &sense, | |
458 | sizeof(sense), | |
459 | 100000, | |
460 | flags | SCSI_DATA_IN) != 0) | |
461 | { | |
462 | return(FALSE); | |
463 | } | |
464 | else | |
465 | return(TRUE); | |
466 | } | |
467 | ||
468 | /*******************************************************\ | |
469 | * Get scsi driver to send a "are you ready" command * | |
470 | \*******************************************************/ | |
471 | ch_test_ready(unit,flags) | |
472 | int unit,flags; | |
473 | { | |
474 | struct scsi_test_unit_ready scsi_cmd; | |
475 | ||
476 | bzero(&scsi_cmd, sizeof(scsi_cmd)); | |
477 | scsi_cmd.op_code = TEST_UNIT_READY; | |
478 | ||
479 | if (ch_scsi_cmd(unit, | |
480 | &scsi_cmd, | |
481 | sizeof(struct scsi_test_unit_ready), | |
482 | 0, | |
483 | 0, | |
484 | 100000, | |
485 | flags) != 0) { | |
486 | return(FALSE); | |
487 | } else | |
488 | return(TRUE); | |
489 | } | |
490 | ||
491 | ||
492 | #ifdef __STDC__ | |
493 | #define b2tol(a) (((unsigned)(a##_1) << 8) + (unsigned)a##_0 ) | |
494 | #else | |
495 | #define b2tol(a) (((unsigned)(a/**/_1) << 8) + (unsigned)a/**/_0 ) | |
496 | #endif | |
497 | ||
498 | /*******************************************************\ | |
499 | * Get the scsi driver to send a full inquiry to the * | |
500 | * device and use the results to fill out the global * | |
501 | * parameter structure. * | |
502 | \*******************************************************/ | |
503 | ch_mode_sense(unit, flags) | |
504 | int unit,flags; | |
505 | { | |
506 | struct scsi_mode_sense scsi_cmd; | |
507 | u_char scsi_sense[128]; /* Can't use scsi_mode_sense_data because of */ | |
508 | /* missing block descriptor */ | |
509 | u_char *b; | |
510 | int i,l; | |
511 | ||
512 | /*******************************************************\ | |
513 | * First check if we have it all loaded * | |
514 | \*******************************************************/ | |
515 | if (ch_info_valid[unit]==CH_KNOWN) return(TRUE); | |
516 | /*******************************************************\ | |
517 | * First do a mode sense * | |
518 | \*******************************************************/ | |
519 | ch_info_valid[unit] &= ~CH_KNOWN; | |
520 | for(l=1;l>=0;l--) { | |
521 | bzero(&scsi_cmd, sizeof(scsi_cmd)); | |
522 | scsi_cmd.op_code = MODE_SENSE; | |
869c4419 RG |
523 | scsi_cmd.byte2 = SMS_DBD; |
524 | scsi_cmd.page = 0x3f; /* All Pages */ | |
15637ed4 RG |
525 | scsi_cmd.length = sizeof(scsi_sense); |
526 | /*******************************************************\ | |
527 | * do the command, but we don't need the results * | |
528 | * just print them for our interest's sake * | |
529 | \*******************************************************/ | |
530 | if (ch_scsi_cmd(unit, | |
531 | &scsi_cmd, | |
532 | sizeof(struct scsi_mode_sense), | |
533 | &scsi_sense, | |
534 | sizeof(scsi_sense), | |
535 | 5000, | |
536 | flags | SCSI_DATA_IN) == 0) { | |
537 | ch_info_valid[unit] = CH_KNOWN; | |
538 | break; | |
539 | } | |
540 | } | |
541 | if (ch_info_valid[unit]!=CH_KNOWN) { | |
542 | if(!(flags & SCSI_SILENT)) | |
98639498 | 543 | printf("ch%d: could not mode sense\n", unit); |
15637ed4 RG |
544 | return(FALSE); |
545 | } | |
546 | l=scsi_sense[0]-3; | |
547 | b=&scsi_sense[4]; | |
548 | /*****************************\ | |
549 | * To avoid alignment problems * | |
550 | \*****************************/ | |
551 | /*FIX THIS FOR MSB */ | |
552 | #define p2copy(valp) (valp[1]+ (valp[0]<<8));valp+=2 | |
553 | #define p4copy(valp) (valp[3]+ (valp[2]<<8) + (valp[1]<<16) + (valp[0]<<24));valp+=4 | |
554 | #if 0 | |
555 | printf("\nmode_sense %d\n",l); | |
556 | for(i=0;i<l+4;i++) { | |
557 | printf("%x%c",scsi_sense[i],i%8==7?'\n':':'); | |
558 | } | |
559 | printf("\n"); | |
560 | #endif | |
561 | for(i=0;i<l;) { | |
562 | int pc=(*b++)&0x3f; | |
563 | int pl=*b++; | |
564 | u_char *bb=b; | |
565 | switch(pc) { | |
566 | case 0x1d: | |
567 | ch_data[unit].chmo =p2copy(bb); | |
568 | ch_data[unit].chms =p2copy(bb); | |
569 | ch_data[unit].sloto =p2copy(bb); | |
570 | ch_data[unit].slots =p2copy(bb); | |
571 | ch_data[unit].imexo =p2copy(bb); | |
572 | ch_data[unit].imexs =p2copy(bb); | |
573 | ch_data[unit].driveo =p2copy(bb); | |
574 | ch_data[unit].drives =p2copy(bb); | |
575 | break; | |
576 | case 0x1e: | |
577 | ch_data[unit].rot = (*b)&1; | |
578 | break; | |
579 | case 0x1f: | |
580 | ch_data[unit].stor = *b&0xf; | |
581 | bb+=2; | |
582 | ch_data[unit].stor =p4copy(bb); | |
583 | break; | |
584 | default: | |
585 | break; | |
586 | } | |
587 | b+=pl; | |
588 | i+=pl+2; | |
589 | } | |
590 | if (ch_debug) | |
591 | { | |
592 | printf("unit %d: cht(%d-%d)slot(%d-%d)imex(%d-%d)cts(%d-%d) %s rotate\n", | |
593 | unit, | |
594 | ch_data[unit].chmo, | |
595 | ch_data[unit].chms, | |
596 | ch_data[unit].sloto, | |
597 | ch_data[unit].slots, | |
598 | ch_data[unit].imexo, | |
599 | ch_data[unit].imexs, | |
600 | ch_data[unit].driveo, | |
601 | ch_data[unit].drives, | |
602 | ch_data[unit].rot?"can":"can't"); | |
603 | } | |
604 | return(TRUE); | |
605 | } | |
606 | ||
607 | /*******************************************************\ | |
608 | * ask the scsi driver to perform a command for us. * | |
609 | * Call it through the switch table, and tell it which * | |
610 | * sub-unit we want, and what target and lu we wish to * | |
611 | * talk to. Also tell it where to find the command * | |
612 | * how long int is. * | |
613 | * Also tell it where to read/write the data, and how * | |
614 | * long the data is supposed to be * | |
615 | \*******************************************************/ | |
616 | int ch_scsi_cmd(unit,scsi_cmd,cmdlen,data_addr,datalen,timeout,flags) | |
617 | ||
618 | int unit,flags; | |
619 | struct scsi_generic *scsi_cmd; | |
620 | int cmdlen; | |
621 | int timeout; | |
622 | u_char *data_addr; | |
623 | int datalen; | |
624 | { | |
625 | struct scsi_xfer *xs; | |
626 | int retval; | |
627 | int s; | |
628 | ||
629 | if(ch_debug||(scsi_debug & PRINTROUTINES)) printf("\nch_scsi_cmd%d %x", | |
630 | unit,scsi_cmd->opcode); | |
631 | if(ch_data[unit].sc_sw) /* If we have a scsi driver */ | |
632 | { | |
633 | ||
634 | xs = &(ch_scsi_xfer[unit]); | |
635 | if(!(flags & SCSI_NOMASK)) | |
636 | s = splbio(); | |
637 | ch_xfer_block_wait[unit]++; /* there is someone waiting */ | |
638 | while (xs->flags & INUSE) | |
639 | { | |
640 | sleep(&ch_xfer_block_wait[unit],PRIBIO+1); | |
641 | } | |
642 | ch_xfer_block_wait[unit]--; | |
643 | xs->flags = INUSE; | |
644 | if(!(flags & SCSI_NOMASK)) | |
645 | splx(s); | |
646 | ||
647 | /*******************************************************\ | |
648 | * Fill out the scsi_xfer structure * | |
649 | \*******************************************************/ | |
650 | xs->flags |= flags; | |
651 | xs->adapter = ch_data[unit].ctlr; | |
652 | xs->targ = ch_data[unit].targ; | |
653 | xs->lu = ch_data[unit].lu; | |
654 | xs->retries = CH_RETRIES; | |
655 | xs->timeout = timeout; | |
656 | xs->cmd = scsi_cmd; | |
657 | xs->cmdlen = cmdlen; | |
658 | xs->data = data_addr; | |
659 | xs->datalen = datalen; | |
660 | xs->resid = datalen; | |
661 | xs->when_done = (flags & SCSI_NOMASK) | |
662 | ?(int (*)())0 | |
663 | :ch_done; | |
664 | xs->done_arg = unit; | |
665 | xs->done_arg2 = (int)xs; | |
666 | retry: xs->error = XS_NOERROR; | |
667 | xs->bp = 0; | |
668 | ch_data[unit].lsterr=0; | |
669 | retval = (*(ch_data[unit].sc_sw->scsi_cmd))(xs); | |
670 | switch(retval) | |
671 | { | |
672 | case SUCCESSFULLY_QUEUED: | |
673 | while(!(xs->flags & ITSDONE)) | |
674 | sleep(xs,PRIBIO+1); | |
675 | ||
676 | case HAD_ERROR: | |
677 | case COMPLETE: | |
678 | switch(xs->error) | |
679 | { | |
680 | case XS_NOERROR: | |
681 | retval = ESUCCESS; | |
682 | break; | |
683 | case XS_SENSE: | |
684 | retval = (ch_interpret_sense(unit,xs)); | |
685 | break; | |
686 | case XS_DRIVER_STUFFUP: | |
687 | retval = EIO; | |
688 | break; | |
689 | case XS_TIMEOUT: | |
690 | if(xs->retries-- ) | |
691 | { | |
692 | xs->flags &= ~ITSDONE; | |
693 | goto retry; | |
694 | } | |
695 | retval = EIO; | |
696 | break; | |
697 | case XS_BUSY: | |
698 | if(xs->retries-- ) | |
699 | { | |
700 | xs->flags &= ~ITSDONE; | |
701 | goto retry; | |
702 | } | |
703 | retval = EIO; | |
704 | break; | |
705 | default: | |
706 | retval = EIO; | |
98639498 | 707 | printf("ch%d: unknown error category from scsi driver\n" |
15637ed4 RG |
708 | ,unit); |
709 | break; | |
710 | } | |
711 | break; | |
712 | case TRY_AGAIN_LATER: | |
713 | if(xs->retries-- ) | |
714 | { | |
715 | xs->flags &= ~ITSDONE; | |
716 | goto retry; | |
717 | } | |
718 | retval = EIO; | |
719 | break; | |
720 | default: | |
721 | retval = EIO; | |
722 | } | |
723 | xs->flags = 0; /* it's free! */ | |
724 | chstart(unit); | |
725 | } | |
726 | else | |
727 | { | |
98639498 | 728 | printf("ch%d: not set up\n",unit); |
15637ed4 RG |
729 | return(EINVAL); |
730 | } | |
731 | return(retval); | |
732 | } | |
733 | /***************************************************************\ | |
734 | * Look at the returned sense and act on the error and detirmine * | |
735 | * The unix error number to pass back... (0 = report no error) * | |
736 | \***************************************************************/ | |
737 | ||
738 | int ch_interpret_sense(unit,xs) | |
739 | int unit; | |
740 | struct scsi_xfer *xs; | |
741 | { | |
742 | struct scsi_sense_data *sense; | |
743 | int key; | |
744 | int silent = xs->flags & SCSI_SILENT; | |
745 | ||
746 | /***************************************************************\ | |
747 | * If errors are ok, report a success * | |
748 | \***************************************************************/ | |
749 | if(xs->flags & SCSI_ERR_OK) return(ESUCCESS); | |
750 | ||
751 | /***************************************************************\ | |
752 | * Get the sense fields and work out what CLASS * | |
753 | \***************************************************************/ | |
754 | sense = &(xs->sense); | |
869c4419 | 755 | switch(sense->error_code & SSD_ERRCODE) |
15637ed4 RG |
756 | { |
757 | /***************************************************************\ | |
758 | * If it's class 7, use the extended stuff and interpret the key * | |
759 | \***************************************************************/ | |
869c4419 | 760 | case 0x70: |
15637ed4 | 761 | { |
869c4419 RG |
762 | key=sense->ext.extended.flags & SSD_KEY; |
763 | if(sense->ext.extended.flags & SSD_ILI) | |
15637ed4 RG |
764 | if(!silent) |
765 | { | |
766 | printf("length error "); | |
767 | } | |
869c4419 | 768 | if(sense->error_code & SSD_ERRCODE_VALID) |
15637ed4 RG |
769 | xs->resid = ntohl(*((long *)sense->ext.extended.info)); |
770 | if(xs->bp) | |
771 | { | |
772 | xs->bp->b_flags |= B_ERROR; | |
773 | return(ESUCCESS); | |
774 | } | |
869c4419 | 775 | if(sense->ext.extended.flags & SSD_EOM) |
15637ed4 | 776 | if(!silent) printf("end of medium "); |
869c4419 | 777 | if(sense->ext.extended.flags & SSD_FILEMARK) |
15637ed4 RG |
778 | if(!silent) printf("filemark "); |
779 | if(ch_debug) | |
780 | { | |
869c4419 RG |
781 | printf("code%x valid%x\n" |
782 | ,sense->error_code & SSD_ERRCODE | |
783 | ,sense->error_code & SSD_ERRCODE_VALID); | |
15637ed4 RG |
784 | printf("seg%x key%x ili%x eom%x fmark%x\n" |
785 | ,sense->ext.extended.segment | |
869c4419 RG |
786 | ,sense->ext.extended.flags & SSD_KEY |
787 | ,sense->ext.extended.flags & SSD_ILI | |
788 | ,sense->ext.extended.flags & SSD_EOM | |
789 | ,sense->ext.extended.flags & SSD_FILEMARK); | |
15637ed4 RG |
790 | printf("info: %x %x %x %x followed by %d extra bytes\n" |
791 | ,sense->ext.extended.info[0] | |
792 | ,sense->ext.extended.info[1] | |
793 | ,sense->ext.extended.info[2] | |
794 | ,sense->ext.extended.info[3] | |
795 | ,sense->ext.extended.extra_len); | |
796 | printf("extra: %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x\n" | |
797 | ,sense->ext.extended.extra_bytes[0] | |
798 | ,sense->ext.extended.extra_bytes[1] | |
799 | ,sense->ext.extended.extra_bytes[2] | |
800 | ,sense->ext.extended.extra_bytes[3] | |
801 | ,sense->ext.extended.extra_bytes[4] | |
802 | ,sense->ext.extended.extra_bytes[5] | |
803 | ,sense->ext.extended.extra_bytes[6] | |
804 | ,sense->ext.extended.extra_bytes[7] | |
805 | ,sense->ext.extended.extra_bytes[8] | |
806 | ,sense->ext.extended.extra_bytes[9] | |
807 | ,sense->ext.extended.extra_bytes[10] | |
808 | ,sense->ext.extended.extra_bytes[11] | |
809 | ,sense->ext.extended.extra_bytes[12] | |
810 | ,sense->ext.extended.extra_bytes[13] | |
811 | ,sense->ext.extended.extra_bytes[14] | |
812 | ,sense->ext.extended.extra_bytes[15]); | |
813 | ||
814 | } | |
815 | switch(key) | |
816 | { | |
817 | case 0x0: | |
818 | return(ESUCCESS); | |
819 | case 0x1: | |
820 | if(!silent) | |
821 | { | |
98639498 | 822 | printf("ch%d: soft error(corrected)", unit); |
869c4419 | 823 | if(sense->error_code & SSD_ERRCODE_VALID) |
15637ed4 | 824 | { |
98639498 | 825 | printf(" block no. %d (decimal)", |
15637ed4 RG |
826 | (sense->ext.extended.info[0] <<24)| |
827 | (sense->ext.extended.info[1] <<16)| | |
828 | (sense->ext.extended.info[2] <<8)| | |
829 | (sense->ext.extended.info[3] )); | |
830 | } | |
98639498 | 831 | printf("\n"); |
15637ed4 RG |
832 | } |
833 | return(ESUCCESS); | |
834 | case 0x2: | |
98639498 | 835 | if(!silent) printf("ch%d: not ready\n", unit); |
15637ed4 | 836 | ch_data[unit].lsterr=(sense->ext.extended.info[12]<<8)| |
98639498 | 837 | sense->ext.extended.info[13] ; |
15637ed4 RG |
838 | return(ENODEV); |
839 | case 0x3: | |
840 | if(!silent) | |
841 | { | |
98639498 | 842 | printf("ch%d: medium error", unit); |
869c4419 | 843 | if(sense->error_code & SSD_ERRCODE_VALID) |
15637ed4 | 844 | { |
98639498 | 845 | printf(" block no. %d (decimal)", |
15637ed4 RG |
846 | (sense->ext.extended.info[0] <<24)| |
847 | (sense->ext.extended.info[1] <<16)| | |
848 | (sense->ext.extended.info[2] <<8)| | |
849 | (sense->ext.extended.info[3] )); | |
850 | } | |
98639498 | 851 | printf("\n"); |
15637ed4 RG |
852 | } |
853 | return(EIO); | |
854 | case 0x4: | |
98639498 | 855 | if(!silent) printf("ch%d: non-media hardware failure\n", |
15637ed4 RG |
856 | unit); |
857 | ch_data[unit].lsterr=(sense->ext.extended.info[12]<<8)| | |
98639498 | 858 | sense->ext.extended.info[13] ; |
15637ed4 RG |
859 | return(EIO); |
860 | case 0x5: | |
98639498 | 861 | if(!silent) printf("ch%d: illegal request\n", unit); |
15637ed4 | 862 | ch_data[unit].lsterr=(sense->ext.extended.info[12]<<8)| |
98639498 | 863 | sense->ext.extended.info[13] ; |
15637ed4 RG |
864 | return(EINVAL); |
865 | case 0x6: | |
98639498 | 866 | if(!silent) printf("ch%d: Unit attention\n", unit); |
15637ed4 | 867 | ch_data[unit].lsterr=(sense->ext.extended.info[12]<<8)| |
98639498 | 868 | sense->ext.extended.info[13] ; |
15637ed4 RG |
869 | ch_info_valid[unit] = FALSE; |
870 | if (ch_data[unit].flags & CH_OPEN) /* TEMP!!!! */ | |
871 | return(EIO); | |
872 | else | |
873 | return(ESUCCESS); | |
874 | case 0x7: | |
875 | if(!silent) | |
876 | { | |
98639498 | 877 | printf("ch%d: attempted protection violation" |
15637ed4 | 878 | , unit); |
869c4419 | 879 | if(sense->error_code & SSD_ERRCODE_VALID) |
15637ed4 | 880 | { |
98639498 | 881 | printf(" block no. %d (decimal)\n", |
15637ed4 RG |
882 | (sense->ext.extended.info[0] <<24)| |
883 | (sense->ext.extended.info[1] <<16)| | |
884 | (sense->ext.extended.info[2] <<8)| | |
885 | (sense->ext.extended.info[3] )); | |
886 | } | |
98639498 | 887 | printf("\n"); |
15637ed4 RG |
888 | } |
889 | return(EACCES); | |
890 | case 0x8: | |
891 | if(!silent) | |
892 | { | |
98639498 | 893 | printf("ch%d: block wrong state (worm)" |
15637ed4 | 894 | , unit); |
869c4419 | 895 | if(sense->error_code & SSD_ERRCODE_VALID) |
15637ed4 | 896 | { |
98639498 | 897 | printf(" block no. %d (decimal)", |
15637ed4 RG |
898 | (sense->ext.extended.info[0] <<24)| |
899 | (sense->ext.extended.info[1] <<16)| | |
900 | (sense->ext.extended.info[2] <<8)| | |
901 | (sense->ext.extended.info[3] )); | |
902 | } | |
98639498 | 903 | printf("\n"); |
15637ed4 RG |
904 | } |
905 | return(EIO); | |
906 | case 0x9: | |
98639498 | 907 | if(!silent) printf("ch%d: vendor unique\n", unit); |
15637ed4 RG |
908 | return(EIO); |
909 | case 0xa: | |
98639498 | 910 | if(!silent) printf("ch%d: copy aborted\n", unit); |
15637ed4 RG |
911 | return(EIO); |
912 | case 0xb: | |
98639498 | 913 | if(!silent) printf("ch%d: command aborted\n", unit); |
15637ed4 | 914 | ch_data[unit].lsterr=(sense->ext.extended.info[12]<<8)| |
98639498 | 915 | sense->ext.extended.info[13] ; |
15637ed4 RG |
916 | return(EIO); |
917 | case 0xc: | |
918 | if(!silent) | |
919 | { | |
98639498 | 920 | printf("ch%d: search returned", unit); |
869c4419 | 921 | if(sense->error_code & SSD_ERRCODE_VALID) |
15637ed4 | 922 | { |
98639498 | 923 | printf(" block no. %d (decimal)", |
15637ed4 RG |
924 | (sense->ext.extended.info[0] <<24)| |
925 | (sense->ext.extended.info[1] <<16)| | |
926 | (sense->ext.extended.info[2] <<8)| | |
927 | (sense->ext.extended.info[3] )); | |
928 | } | |
98639498 | 929 | printf("\n"); |
15637ed4 RG |
930 | } |
931 | return(ESUCCESS); | |
932 | case 0xd: | |
98639498 | 933 | if(!silent) printf("ch%d: volume overflow\n", unit); |
15637ed4 RG |
934 | return(ENOSPC); |
935 | case 0xe: | |
936 | if(!silent) | |
937 | { | |
98639498 | 938 | printf("ch%d: verify miscompare", unit); |
869c4419 | 939 | if(sense->error_code & SSD_ERRCODE_VALID) |
15637ed4 | 940 | { |
98639498 | 941 | printf(" block no. %d (decimal)", |
15637ed4 RG |
942 | (sense->ext.extended.info[0] <<24)| |
943 | (sense->ext.extended.info[1] <<16)| | |
944 | (sense->ext.extended.info[2] <<8)| | |
945 | (sense->ext.extended.info[3] )); | |
946 | } | |
98639498 | 947 | printf("\n"); |
15637ed4 RG |
948 | } |
949 | return(EIO); | |
950 | case 0xf: | |
98639498 | 951 | if(!silent) printf("ch%d: unknown error key\n", unit); |
15637ed4 RG |
952 | return(EIO); |
953 | } | |
954 | break; | |
955 | } | |
956 | /***************************************************************\ | |
957 | * If it's NOT class 7, just report it. * | |
958 | \***************************************************************/ | |
869c4419 | 959 | default: |
15637ed4 | 960 | { |
98639498 RG |
961 | if(!silent) |
962 | { | |
963 | printf("ch%d: error code %d", | |
964 | unit, | |
965 | sense->error_code & SSD_ERRCODE); | |
966 | if(sense->error_code & SSD_ERRCODE_VALID) | |
967 | { | |
968 | printf(" block no. %d (decimal)", | |
969 | (sense->ext.unextended.blockhi <<16), | |
970 | + (sense->ext.unextended.blockmed <<8), | |
971 | + (sense->ext.unextended.blocklow )); | |
972 | } | |
973 | printf("\n"); | |
974 | } | |
15637ed4 RG |
975 | } |
976 | return(EIO); | |
977 | } | |
978 | } | |
979 | ||
980 | ||
981 |