added my responsibility for the `cpm' port
[unix-history] / sys / scsi / ch.c
CommitLineData
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
25static errval ch_mode_sense(u_int32, u_int32);
26
519fb2b7
RG
27struct scsi_xfer ch_scsi_xfer[NCH];
28u_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 39errval chattach();
15637ed4 40
519fb2b7
RG
41/*
42 * This driver is so simple it uses all the default services
43 */
44struct 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
55struct 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 76static 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 */
82errval
83chattach(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 */
127errval
15637ed4 128chopen(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 */
193errval
15637ed4 194chclose(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 */
214errval
15637ed4 215chioctl(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
280errval
281ch_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
316errval
317ch_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
348errval
349ch_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 389static errval
15637ed4 390ch_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}