Commit | Line | Data |
---|---|---|
869c4419 | 1 | /* |
519fb2b7 RG |
2 | * Written by grefen@????? |
3 | * Based on scsi drivers by Julian Elischer (julian@tfs.com) | |
869c4419 | 4 | * |
46d39670 | 5 | * $Id: ch.c,v 1.7 1993/12/19 00:54:49 wollman Exp $ |
15637ed4 RG |
6 | */ |
7 | ||
8 | #include <sys/types.h> | |
9 | #include <ch.h> | |
10 | ||
11 | #include <sys/param.h> | |
12 | #include <sys/systm.h> | |
13 | ||
14 | #include <sys/errno.h> | |
15 | #include <sys/ioctl.h> | |
16 | #include <sys/buf.h> | |
17 | #include <sys/proc.h> | |
18 | #include <sys/user.h> | |
19 | #include <sys/chio.h> | |
20 | ||
15637ed4 RG |
21 | #include <scsi/scsi_all.h> |
22 | #include <scsi/scsi_changer.h> | |
23 | #include <scsi/scsiconf.h> | |
24 | ||
fde1aeb2 GW |
25 | static errval ch_mode_sense(u_int32, u_int32); |
26 | ||
519fb2b7 RG |
27 | struct scsi_xfer ch_scsi_xfer[NCH]; |
28 | u_int32 ch_xfer_block_wait[NCH]; | |
15637ed4 RG |
29 | |
30 | #define PAGESIZ 4096 | |
31 | #define STQSIZE 4 | |
519fb2b7 | 32 | #define CHRETRIES 2 |
15637ed4 RG |
33 | |
34 | #define MODE(z) ( (minor(z) & 0x0F) ) | |
35 | #define UNIT(z) ( (minor(z) >> 4) ) | |
36 | ||
15637ed4 | 37 | #define ESUCCESS 0 |
15637ed4 | 38 | |
519fb2b7 | 39 | errval chattach(); |
15637ed4 | 40 | |
519fb2b7 RG |
41 | /* |
42 | * This driver is so simple it uses all the default services | |
43 | */ | |
44 | struct scsi_device ch_switch = | |
15637ed4 | 45 | { |
519fb2b7 RG |
46 | NULL, |
47 | NULL, | |
48 | NULL, | |
49 | NULL, | |
50 | "ch", | |
51 | 0, | |
52 | 0, 0 | |
53 | }; | |
54 | ||
55 | struct ch_data { | |
56 | u_int32 flags; | |
57 | struct scsi_link *sc_link; /* all the inter level info */ | |
58 | u_int16 chmo; /* Offset of first CHM */ | |
59 | u_int16 chms; /* No. of CHM */ | |
60 | u_int16 slots; /* No. of Storage Elements */ | |
61 | u_int16 sloto; /* Offset of first SE */ | |
62 | u_int16 imexs; /* No. of Import/Export Slots */ | |
63 | u_int16 imexo; /* Offset of first IM/EX */ | |
64 | u_int16 drives; /* No. of CTS */ | |
65 | u_int16 driveo; /* Offset of first CTS */ | |
66 | u_int16 rot; /* CHM can rotate */ | |
67 | u_long op_matrix; /* possible opertaions */ | |
68 | u_int16 lsterr; /* details of lasterror */ | |
69 | u_char stor; /* posible Storage locations */ | |
70 | u_int32 initialized; | |
71 | } ch_data[NCH]; | |
15637ed4 RG |
72 | |
73 | #define CH_OPEN 0x01 | |
74 | #define CH_KNOWN 0x02 | |
75 | ||
519fb2b7 | 76 | static u_int32 next_ch_unit = 0; |
15637ed4 | 77 | |
519fb2b7 RG |
78 | /* |
79 | * The routine called by the low level scsi routine when it discovers | |
80 | * a device suitable for this driver. | |
81 | */ | |
82 | errval | |
83 | chattach(sc_link) | |
84 | struct scsi_link *sc_link; | |
15637ed4 | 85 | { |
519fb2b7 | 86 | u_int32 unit, i, stat; |
15637ed4 RG |
87 | unsigned char *tbl; |
88 | ||
519fb2b7 RG |
89 | SC_DEBUG(sc_link, SDEV_DB2, ("chattach: ")); |
90 | /* | |
91 | * Check we have the resources for another drive | |
92 | */ | |
15637ed4 | 93 | unit = next_ch_unit++; |
519fb2b7 RG |
94 | if (unit >= NCH) { |
95 | printf("Too many scsi changers..(%d > %d) reconfigure kernel\n", (unit + 1), NCH); | |
96 | return (0); | |
15637ed4 | 97 | } |
519fb2b7 RG |
98 | /* |
99 | * Store information needed to contact our base driver | |
100 | */ | |
101 | ch_data[unit].sc_link = sc_link; | |
102 | sc_link->device = &ch_switch; | |
103 | sc_link->dev_unit = unit; | |
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 */ ))) { | |
98639498 | 111 | printf("ch%d: scsi changer :- offline\n", unit); |
519fb2b7 RG |
112 | stat = CH_OPEN; |
113 | } else { | |
114 | printf("ch%d: scsi changer, %d slot(s) %d drive(s) %d arm(s) %d i/e-slot(s)\n", | |
115 | unit, ch_data[unit].slots, ch_data[unit].drives, ch_data[unit].chms, ch_data[unit].imexs); | |
116 | stat = CH_KNOWN; | |
15637ed4 | 117 | } |
519fb2b7 | 118 | ch_data[unit].initialized = 1; |
15637ed4 | 119 | |
4c45483e GW |
120 | return 1; |
121 | /* XXX ??? is this the right return val? */ | |
15637ed4 RG |
122 | } |
123 | ||
519fb2b7 RG |
124 | /* |
125 | * open the device. | |
126 | */ | |
127 | errval | |
15637ed4 | 128 | chopen(dev) |
4c45483e | 129 | dev_t dev; |
15637ed4 | 130 | { |
519fb2b7 RG |
131 | errval errcode = 0; |
132 | u_int32 unit, mode; | |
133 | struct scsi_link *sc_link; | |
15637ed4 RG |
134 | |
135 | unit = UNIT(dev); | |
136 | mode = MODE(dev); | |
137 | ||
519fb2b7 RG |
138 | /* |
139 | * Check the unit is legal | |
140 | */ | |
141 | if (unit >= NCH) { | |
142 | printf("ch%d: ch %d > %d\n", unit, unit, NCH); | |
15637ed4 | 143 | errcode = ENXIO; |
519fb2b7 | 144 | return (errcode); |
15637ed4 | 145 | } |
519fb2b7 RG |
146 | /* |
147 | * Only allow one at a time | |
148 | */ | |
149 | if (ch_data[unit].flags & CH_OPEN) { | |
150 | printf("ch%d: already open\n", unit); | |
46d39670 | 151 | return EBUSY; |
15637ed4 | 152 | } |
519fb2b7 RG |
153 | /* |
154 | * Make sure the device has been initialised | |
155 | */ | |
156 | if (!ch_data[unit].initialized) | |
157 | return (ENXIO); | |
158 | ||
159 | sc_link = ch_data[unit].sc_link; | |
160 | ||
161 | SC_DEBUG(sc_link, SDEV_DB1, ("chopen: dev=0x%x (unit %d (of %d))\n" | |
162 | ,dev, unit, NCH)); | |
163 | /* | |
164 | * Catch any unit attention errors. | |
165 | */ | |
166 | scsi_test_unit_ready(sc_link, SCSI_SILENT); | |
167 | ||
168 | sc_link->flags |= SDEV_OPEN; | |
169 | /* | |
170 | * Check that it is still responding and ok. | |
171 | */ | |
172 | if (errcode = (scsi_test_unit_ready(sc_link, 0))) { | |
173 | printf("ch%d: not ready\n", unit); | |
174 | sc_link->flags &= ~SDEV_OPEN; | |
175 | return errcode; | |
15637ed4 | 176 | } |
519fb2b7 RG |
177 | /* |
178 | * Make sure data is loaded | |
179 | */ | |
180 | if (errcode = (ch_mode_sense(unit, SCSI_NOSLEEP | SCSI_NOMASK))) { | |
181 | printf("ch%d: scsi changer :- offline\n", unit); | |
182 | sc_link->flags &= ~SDEV_OPEN; | |
183 | return (errcode); | |
15637ed4 | 184 | } |
15637ed4 | 185 | ch_data[unit].flags = CH_OPEN; |
519fb2b7 | 186 | return 0; |
15637ed4 RG |
187 | } |
188 | ||
519fb2b7 RG |
189 | /* |
190 | * close the device.. only called if we are the LAST | |
191 | * occurence of an open device | |
192 | */ | |
193 | errval | |
15637ed4 | 194 | chclose(dev) |
4c45483e | 195 | dev_t dev; |
15637ed4 | 196 | { |
519fb2b7 RG |
197 | unsigned char unit, mode; |
198 | struct scsi_link *sc_link; | |
15637ed4 RG |
199 | |
200 | unit = UNIT(dev); | |
201 | mode = MODE(dev); | |
519fb2b7 | 202 | sc_link = ch_data[unit].sc_link; |
15637ed4 | 203 | |
519fb2b7 | 204 | SC_DEBUG(sc_link, SDEV_DB1, ("Closing device")); |
15637ed4 | 205 | ch_data[unit].flags = 0; |
519fb2b7 RG |
206 | sc_link->flags &= ~SDEV_OPEN; |
207 | return (0); | |
15637ed4 RG |
208 | } |
209 | ||
519fb2b7 RG |
210 | /* |
211 | * Perform special action on behalf of the user | |
212 | * Knows about the internals of this device | |
213 | */ | |
214 | errval | |
15637ed4 | 215 | chioctl(dev, cmd, arg, mode) |
519fb2b7 RG |
216 | dev_t dev; |
217 | u_int32 cmd; | |
218 | caddr_t arg; | |
4c45483e | 219 | int mode; |
15637ed4 | 220 | { |
519fb2b7 | 221 | /* struct ch_cmd_buf *args; */ |
15637ed4 | 222 | union scsi_cmd *scsi_cmd; |
519fb2b7 RG |
223 | register i, j; |
224 | u_int32 opri; | |
225 | errval errcode = 0; | |
15637ed4 | 226 | unsigned char unit; |
519fb2b7 RG |
227 | u_int32 number, flags; |
228 | errval ret; | |
229 | struct scsi_link *sc_link; | |
230 | ||
231 | /* | |
232 | * Find the device that the user is talking about | |
233 | */ | |
234 | flags = 0; /* give error messages, act on errors etc. */ | |
15637ed4 | 235 | unit = UNIT(dev); |
519fb2b7 RG |
236 | sc_link = ch_data[unit].sc_link; |
237 | ||
fde1aeb2 | 238 | switch ((int)cmd) { |
519fb2b7 RG |
239 | case CHIOOP:{ |
240 | struct chop *ch = (struct chop *) arg; | |
241 | SC_DEBUG(sc_link, SDEV_DB2, | |
242 | ("[chtape_chop: %x]\n", ch->ch_op)); | |
243 | ||
244 | switch ((short) (ch->ch_op)) { | |
245 | case CHGETPARAM: | |
246 | ch->u.getparam.chmo = ch_data[unit].chmo; | |
247 | ch->u.getparam.chms = ch_data[unit].chms; | |
248 | ch->u.getparam.sloto = ch_data[unit].sloto; | |
249 | ch->u.getparam.slots = ch_data[unit].slots; | |
250 | ch->u.getparam.imexo = ch_data[unit].imexo; | |
251 | ch->u.getparam.imexs = ch_data[unit].imexs; | |
252 | ch->u.getparam.driveo = ch_data[unit].driveo; | |
253 | ch->u.getparam.drives = ch_data[unit].drives; | |
254 | ch->u.getparam.rot = ch_data[unit].rot; | |
255 | ch->result = 0; | |
256 | return 0; | |
257 | break; | |
258 | case CHPOSITION: | |
259 | return ch_position(unit, &ch->result, ch->u.position.chm, | |
260 | ch->u.position.to, | |
261 | flags); | |
262 | case CHMOVE: | |
263 | return ch_move(unit, &ch->result, ch->u.position.chm, | |
264 | ch->u.move.from, ch->u.move.to, | |
265 | flags); | |
266 | case CHGETELEM: | |
267 | return ch_getelem(unit, &ch->result, ch->u.get_elem_stat.type, | |
268 | ch->u.get_elem_stat.from, &ch->u.get_elem_stat.elem_data, | |
269 | flags); | |
270 | default: | |
271 | return EINVAL; | |
272 | } | |
15637ed4 | 273 | } |
15637ed4 | 274 | default: |
519fb2b7 | 275 | return scsi_do_ioctl(sc_link, cmd, arg, mode); |
15637ed4 | 276 | } |
519fb2b7 | 277 | return (ret ? ESUCCESS : EIO); |
15637ed4 RG |
278 | } |
279 | ||
519fb2b7 RG |
280 | errval |
281 | ch_getelem(unit, stat, type, from, data, flags) | |
282 | u_int32 unit, from, flags; | |
4c45483e | 283 | int type; |
519fb2b7 RG |
284 | short *stat; |
285 | char *data; | |
15637ed4 RG |
286 | { |
287 | struct scsi_read_element_status scsi_cmd; | |
519fb2b7 RG |
288 | char elbuf[32]; |
289 | errval ret; | |
15637ed4 RG |
290 | |
291 | bzero(&scsi_cmd, sizeof(scsi_cmd)); | |
292 | scsi_cmd.op_code = READ_ELEMENT_STATUS; | |
869c4419 | 293 | scsi_cmd.byte2 = type; |
519fb2b7 RG |
294 | scsi_cmd.starting_element_addr[0] = (from >> 8) & 0xff; |
295 | scsi_cmd.starting_element_addr[1] = from & 0xff; | |
296 | scsi_cmd.number_of_elements[1] = 1; | |
297 | scsi_cmd.allocation_length[2] = 32; | |
298 | ||
299 | if ((ret = scsi_scsi_cmd(ch_data[unit].sc_link, | |
300 | (struct scsi_generic *) &scsi_cmd, | |
301 | sizeof(scsi_cmd), | |
302 | (u_char *) elbuf, | |
303 | 32, | |
304 | CHRETRIES, | |
305 | 100000, | |
306 | NULL, | |
307 | SCSI_DATA_IN | flags) != ESUCCESS)) { | |
308 | *stat = ch_data[unit].lsterr; | |
309 | bcopy(elbuf + 16, data, 16); | |
310 | return ret; | |
311 | } | |
312 | bcopy(elbuf + 16, data, 16); /*Just a hack sh */ | |
15637ed4 RG |
313 | return ret; |
314 | } | |
315 | ||
519fb2b7 RG |
316 | errval |
317 | ch_move(unit, stat, chm, from, to, flags) | |
318 | u_int32 unit, chm, from, to, flags; | |
319 | short *stat; | |
15637ed4 RG |
320 | { |
321 | struct scsi_move_medium scsi_cmd; | |
519fb2b7 | 322 | errval ret; |
15637ed4 RG |
323 | |
324 | bzero(&scsi_cmd, sizeof(scsi_cmd)); | |
325 | scsi_cmd.op_code = MOVE_MEDIUM; | |
519fb2b7 RG |
326 | scsi_cmd.transport_element_address[0] = (chm >> 8) & 0xff; |
327 | scsi_cmd.transport_element_address[1] = chm & 0xff; | |
328 | scsi_cmd.source_address[0] = (from >> 8) & 0xff; | |
329 | scsi_cmd.source_address[1] = from & 0xff; | |
330 | scsi_cmd.destination_address[0] = (to >> 8) & 0xff; | |
331 | scsi_cmd.destination_address[1] = to & 0xff; | |
332 | scsi_cmd.invert = (chm & CH_INVERT) ? 1 : 0; | |
333 | if ((ret = scsi_scsi_cmd(ch_data[unit].sc_link, | |
334 | (struct scsi_generic *) &scsi_cmd, | |
335 | sizeof(scsi_cmd), | |
336 | NULL, | |
337 | 0, | |
338 | CHRETRIES, | |
339 | 100000, | |
340 | NULL, | |
341 | flags) != ESUCCESS)) { | |
342 | *stat = ch_data[unit].lsterr; | |
343 | return ret; | |
344 | } | |
15637ed4 RG |
345 | return ret; |
346 | } | |
347 | ||
519fb2b7 RG |
348 | errval |
349 | ch_position(unit, stat, chm, to, flags) | |
350 | u_int32 unit, chm, to, flags; | |
351 | short *stat; | |
15637ed4 RG |
352 | { |
353 | struct scsi_position_to_element scsi_cmd; | |
519fb2b7 | 354 | errval ret; |
15637ed4 RG |
355 | |
356 | bzero(&scsi_cmd, sizeof(scsi_cmd)); | |
357 | scsi_cmd.op_code = POSITION_TO_ELEMENT; | |
519fb2b7 RG |
358 | scsi_cmd.transport_element_address[0] = (chm >> 8) & 0xff; |
359 | scsi_cmd.transport_element_address[1] = chm & 0xff; | |
360 | scsi_cmd.source_address[0] = (to >> 8) & 0xff; | |
361 | scsi_cmd.source_address[1] = to & 0xff; | |
362 | scsi_cmd.invert = (chm & CH_INVERT) ? 1 : 0; | |
363 | if ((ret = scsi_scsi_cmd(ch_data[unit].sc_link, | |
364 | (struct scsi_generic *) &scsi_cmd, | |
365 | sizeof(scsi_cmd), | |
366 | NULL, | |
367 | 0, | |
368 | CHRETRIES, | |
369 | 100000, | |
370 | NULL, | |
371 | flags) != ESUCCESS)) { | |
372 | *stat = ch_data[unit].lsterr; | |
373 | return ret; | |
15637ed4 | 374 | } |
519fb2b7 | 375 | return ret; |
15637ed4 RG |
376 | } |
377 | ||
15637ed4 RG |
378 | #ifdef __STDC__ |
379 | #define b2tol(a) (((unsigned)(a##_1) << 8) + (unsigned)a##_0 ) | |
380 | #else | |
381 | #define b2tol(a) (((unsigned)(a/**/_1) << 8) + (unsigned)a/**/_0 ) | |
382 | #endif | |
383 | ||
519fb2b7 RG |
384 | /* |
385 | * Get the scsi driver to send a full inquiry to the | |
386 | * device and use the results to fill out the global | |
387 | * parameter structure. | |
388 | */ | |
fde1aeb2 | 389 | static errval |
15637ed4 | 390 | ch_mode_sense(unit, flags) |
519fb2b7 | 391 | u_int32 unit, flags; |
15637ed4 RG |
392 | { |
393 | struct scsi_mode_sense scsi_cmd; | |
519fb2b7 RG |
394 | u_char scsi_sense[128]; /* Can't use scsi_mode_sense_data because of |
395 | * missing block descriptor | |
396 | */ | |
15637ed4 | 397 | u_char *b; |
519fb2b7 RG |
398 | int32 i, l; |
399 | errval errcode; | |
400 | struct scsi_link *sc_link = ch_data[unit].sc_link; | |
401 | ||
402 | /* | |
403 | * First check if we have it all loaded | |
404 | */ | |
405 | if (sc_link->flags & SDEV_MEDIA_LOADED) | |
406 | return 0; | |
407 | ||
408 | /* | |
409 | * First do a mode sense | |
410 | */ | |
411 | /* sc_link->flags &= ~SDEV_MEDIA_LOADED; *//*XXX */ | |
412 | bzero(&scsi_cmd, sizeof(scsi_cmd)); | |
413 | scsi_cmd.op_code = MODE_SENSE; | |
414 | scsi_cmd.byte2 = SMS_DBD; | |
415 | scsi_cmd.page = 0x3f; /* All Pages */ | |
416 | scsi_cmd.length = sizeof(scsi_sense); | |
417 | ||
418 | /* | |
419 | * Read in the pages | |
420 | */ | |
421 | if (errcode = scsi_scsi_cmd(sc_link, | |
422 | (struct scsi_generic *) &scsi_cmd, | |
423 | sizeof(struct scsi_mode_sense), | |
424 | (u_char *) & scsi_sense, | |
425 | sizeof (scsi_sense), | |
426 | CHRETRIES, | |
427 | 5000, | |
428 | NULL, | |
429 | flags | SCSI_DATA_IN) != 0) { | |
430 | if (!(flags & SCSI_SILENT)) | |
98639498 | 431 | printf("ch%d: could not mode sense\n", unit); |
519fb2b7 | 432 | return (errcode); |
15637ed4 | 433 | } |
519fb2b7 RG |
434 | sc_link->flags |= SDEV_MEDIA_LOADED; |
435 | l = scsi_sense[0] - 3; | |
436 | b = &scsi_sense[4]; | |
437 | ||
438 | /* | |
439 | * To avoid alignment problems | |
440 | */ | |
441 | /* XXX - FIX THIS FOR MSB */ | |
15637ed4 RG |
442 | #define p2copy(valp) (valp[1]+ (valp[0]<<8));valp+=2 |
443 | #define p4copy(valp) (valp[3]+ (valp[2]<<8) + (valp[1]<<16) + (valp[0]<<24));valp+=4 | |
444 | #if 0 | |
519fb2b7 RG |
445 | printf("\nmode_sense %d\n", l); |
446 | for (i = 0; i < l + 4; i++) { | |
447 | printf("%x%c", scsi_sense[i], i % 8 == 7 ? '\n' : ':'); | |
448 | } printf("\n"); | |
15637ed4 | 449 | #endif |
519fb2b7 RG |
450 | for (i = 0; i < l;) { |
451 | u_int32 pc = (*b++) & 0x3f; | |
452 | u_int32 pl = *b++; | |
453 | u_char *bb = b; | |
fde1aeb2 | 454 | switch ((int)pc) { |
519fb2b7 RG |
455 | case 0x1d: |
456 | ch_data[unit].chmo = p2copy(bb); | |
457 | ch_data[unit].chms = p2copy(bb); | |
458 | ch_data[unit].sloto = p2copy(bb); | |
459 | ch_data[unit].slots = p2copy(bb); | |
460 | ch_data[unit].imexo = p2copy(bb); | |
461 | ch_data[unit].imexs = p2copy(bb); | |
462 | ch_data[unit].driveo = p2copy(bb); | |
463 | ch_data[unit].drives = p2copy(bb); | |
15637ed4 | 464 | break; |
519fb2b7 RG |
465 | case 0x1e: |
466 | ch_data[unit].rot = (*b) & 1; | |
467 | break; | |
468 | case 0x1f: | |
469 | ch_data[unit].stor = *b & 0xf; | |
470 | bb += 2; | |
471 | ch_data[unit].stor = p4copy(bb); | |
15637ed4 RG |
472 | break; |
473 | default: | |
519fb2b7 | 474 | break; |
15637ed4 | 475 | } |
519fb2b7 RG |
476 | b += pl; |
477 | i += pl + 2; | |
15637ed4 | 478 | } |
519fb2b7 RG |
479 | SC_DEBUG(sc_link, SDEV_DB2, |
480 | (" cht(%d-%d)slot(%d-%d)imex(%d-%d)cts(%d-%d) %s rotate\n", | |
481 | ch_data[unit].chmo, ch_data[unit].chms, | |
482 | ch_data[unit].sloto, ch_data[unit].slots, | |
483 | ch_data[unit].imexo, ch_data[unit].imexs, | |
484 | ch_data[unit].driveo, ch_data[unit].drives, | |
485 | ch_data[unit].rot ? "can" : "can't")); | |
486 | return (0); | |
15637ed4 | 487 | } |