ch.c revision 1.11
1/* $NetBSD: ch.c,v 1.11 1994/11/21 10:39:14 mycroft Exp $ */ 2 3/* 4 * Copyright (c) 1994 Charles Hannum. All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. All advertising materials mentioning features or use of this software 15 * must display the following acknowledgement: 16 * This product includes software developed by Charles Hannum. 17 * 4. The name of the author may not be used to endorse or promote products 18 * derived from this software without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 21 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 22 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 23 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 24 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 25 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 29 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32/* 33 * Originally written by grefen@????? 34 * Based on scsi drivers by Julian Elischer (julian@tfs.com) 35 */ 36 37#include <sys/types.h> 38#include <sys/param.h> 39#include <sys/systm.h> 40#include <sys/errno.h> 41#include <sys/ioctl.h> 42#include <sys/buf.h> 43#include <sys/proc.h> 44#include <sys/user.h> 45#include <sys/chio.h> 46#include <sys/device.h> 47 48#include <scsi/scsi_all.h> 49#include <scsi/scsi_changer.h> 50#include <scsi/scsiconf.h> 51 52#define CHRETRIES 2 53 54#define CHMODE(z) (minor(z) & 0x0f) 55#define CHUNIT(z) (minor(z) >> 4) 56 57struct ch_data { 58 struct device sc_dev; 59 60 int flags; 61#define CHOPEN 0x01 62 struct scsi_link *sc_link; /* all the inter level info */ 63 u_int16 chmo; /* Offset of first CHM */ 64 u_int16 chms; /* No. of CHM */ 65 u_int16 slots; /* No. of Storage Elements */ 66 u_int16 sloto; /* Offset of first SE */ 67 u_int16 imexs; /* No. of Import/Export Slots */ 68 u_int16 imexo; /* Offset of first IM/EX */ 69 u_int16 drives; /* No. of CTS */ 70 u_int16 driveo; /* Offset of first CTS */ 71 u_int16 rot; /* CHM can rotate */ 72 u_long op_matrix; /* possible opertaions */ 73 u_int16 lsterr; /* details of lasterror */ 74 u_char stor; /* posible Storage locations */ 75}; 76 77void chattach __P((struct device *, struct device *, void *)); 78 79struct cfdriver chcd = { 80 NULL, "ch", scsi_targmatch, chattach, DV_DULL, sizeof(struct ch_data) 81}; 82 83/* 84 * This driver is so simple it uses all the default services 85 */ 86struct scsi_device ch_switch = { 87 NULL, 88 NULL, 89 NULL, 90 NULL, 91 "ch", 92 0 93}; 94 95/* 96 * The routine called by the low level scsi routine when it discovers 97 * a device suitable for this driver. 98 */ 99void 100chattach(parent, self, aux) 101 struct device *parent, *self; 102 void *aux; 103{ 104 struct ch_data *ch = (void *)self; 105 struct scsi_link *sc_link = aux; 106 unsigned char *tbl; 107 108 SC_DEBUG(sc_link, SDEV_DB2, ("chattach: ")); 109 110 /* 111 * Store information needed to contact our base driver 112 */ 113 ch->sc_link = sc_link; 114 sc_link->device = &ch_switch; 115 sc_link->device_softc = ch; 116 117 /* 118 * Use the subdriver to request information regarding 119 * the drive. We cannot use interrupts yet, so the 120 * request must specify this. 121 */ 122 if ((ch_mode_sense(ch, SCSI_NOSLEEP | SCSI_NOMASK))) 123 printf(": offline\n"); 124 else 125 printf(": %d slot(s), %d drive(s), %d arm(s), %d i/e-slot(s)\n", 126 ch->slots, ch->drives, ch->chms, ch->imexs); 127} 128 129/* 130 * open the device. 131 */ 132int 133chopen(dev) 134 dev_t dev; 135{ 136 int error = 0; 137 int unit, mode; 138 struct ch_data *ch; 139 struct scsi_link *sc_link; 140 141 unit = CHUNIT(dev); 142 mode = CHMODE(dev); 143 144 if (unit >= chcd.cd_ndevs) 145 return ENXIO; 146 ch = chcd.cd_devs[unit]; 147 if (!ch) 148 return ENXIO; 149 150 sc_link = ch->sc_link; 151 SC_DEBUG(sc_link, SDEV_DB1, ("chopen: dev=0x%x (unit %d (of %d))\n", 152 dev, unit, chcd.cd_ndevs)); 153 154 if (ch->flags & CHOPEN) 155 return EBUSY; 156 157 /* 158 * Catch any unit attention errors. 159 */ 160 scsi_test_unit_ready(sc_link, SCSI_SILENT); 161 162 sc_link->flags |= SDEV_OPEN; 163 /* 164 * Check that it is still responding and ok. 165 */ 166 if (error = (scsi_test_unit_ready(sc_link, 0))) { 167 printf("%s: not ready\n", ch->sc_dev.dv_xname); 168 sc_link->flags &= ~SDEV_OPEN; 169 return error; 170 } 171 172 /* 173 * Make sure data is loaded 174 */ 175 if (error = (ch_mode_sense(ch, SCSI_NOSLEEP | SCSI_NOMASK))) { 176 printf("%s: offline\n", ch->sc_dev.dv_xname); 177 sc_link->flags &= ~SDEV_OPEN; 178 return error; 179 } 180 181 ch->flags |= CHOPEN; 182 return 0; 183} 184 185/* 186 * close the device.. only called if we are the LAST 187 * occurence of an open device 188 */ 189int 190chclose(dev) 191 dev_t dev; 192{ 193 int unit, mode; 194 struct ch_data *ch; 195 struct scsi_link *sc_link; 196 197 unit = CHUNIT(dev); 198 mode = CHMODE(dev); 199 ch = chcd.cd_devs[unit]; 200 sc_link = ch->sc_link; 201 202 SC_DEBUG(sc_link, SDEV_DB1, ("closing")); 203 ch->flags &= ~CHOPEN; 204 sc_link->flags &= ~SDEV_OPEN; 205 return 0; 206} 207 208/* 209 * Perform special action on behalf of the user 210 * Knows about the internals of this device 211 */ 212int 213chioctl(dev, cmd, arg, mode) 214 dev_t dev; 215 u_long cmd; 216 caddr_t arg; 217 int mode; 218{ 219 struct ch_data *ch = chcd.cd_devs[CHUNIT(dev)]; 220 struct scsi_link *sc_link = ch->sc_link; 221 int number; 222 int flags; 223 224 /* 225 * Find the device that the user is talking about 226 */ 227 flags = 0; /* give error messages, act on errors etc. */ 228 229 switch (cmd) { 230 case CHIOOP: { 231 struct chop *chop = (struct chop *) arg; 232 SC_DEBUG(sc_link, SDEV_DB2, ("[chtape_chop: %x]\n", 233 chop->ch_op)); 234 235 switch (chop->ch_op) { 236 case CHGETPARAM: 237 chop->u.getparam.chmo = ch->chmo; 238 chop->u.getparam.chms = ch->chms; 239 chop->u.getparam.sloto = ch->sloto; 240 chop->u.getparam.slots = ch->slots; 241 chop->u.getparam.imexo = ch->imexo; 242 chop->u.getparam.imexs = ch->imexs; 243 chop->u.getparam.driveo = ch->driveo; 244 chop->u.getparam.drives = ch->drives; 245 chop->u.getparam.rot = ch->rot; 246 chop->result = 0; 247 return 0; 248 break; 249 case CHPOSITION: 250 return ch_position(ch, &chop->result, 251 chop->u.position.chm, chop->u.position.to, flags); 252 case CHMOVE: 253 return ch_move(ch, &chop->result, chop->u.position.chm, 254 chop->u.move.from, chop->u.move.to, flags); 255 case CHGETELEM: 256 return ch_getelem(ch, &chop->result, 257 chop->u.get_elem_stat.type, 258 chop->u.get_elem_stat.from, 259 &chop->u.get_elem_stat.elem_data, flags); 260 default: 261 return EINVAL; 262 } 263 } 264 default: 265 return scsi_do_ioctl(sc_link, dev, cmd, arg, mode); 266 } 267#ifdef DIAGNOSTIC 268 panic("chioctl: impossible"); 269#endif 270} 271 272int 273ch_getelem(ch, stat, type, from, data, flags) 274 struct ch_data *ch; 275 short *stat; 276 int type, from; 277 char *data; 278 int flags; 279{ 280 struct scsi_read_element_status scsi_cmd; 281 char elbuf[32]; 282 int error; 283 284 bzero(&scsi_cmd, sizeof(scsi_cmd)); 285 scsi_cmd.op_code = READ_ELEMENT_STATUS; 286 scsi_cmd.byte2 = type; 287 scsi_cmd.starting_element_addr[0] = (from >> 8) & 0xff; 288 scsi_cmd.starting_element_addr[1] = from & 0xff; 289 scsi_cmd.number_of_elements[1] = 1; 290 scsi_cmd.allocation_length[2] = 32; 291 292 error = scsi_scsi_cmd(ch->sc_link, (struct scsi_generic *) &scsi_cmd, 293 sizeof(scsi_cmd), (u_char *) elbuf, 32, CHRETRIES, 100000, NULL, 294 SCSI_DATA_IN | flags); 295 if (error) 296 *stat = ch->lsterr; 297 else 298 *stat = 0; 299 bcopy(elbuf + 16, data, 16); 300 return error; 301} 302 303int 304ch_move(ch, stat, chm, from, to, flags) 305 struct ch_data *ch; 306 short *stat; 307 int chm, from, to, flags; 308{ 309 struct scsi_move_medium scsi_cmd; 310 int error; 311 312 bzero(&scsi_cmd, sizeof(scsi_cmd)); 313 scsi_cmd.op_code = MOVE_MEDIUM; 314 scsi_cmd.transport_element_address[0] = (chm >> 8) & 0xff; 315 scsi_cmd.transport_element_address[1] = chm & 0xff; 316 scsi_cmd.source_address[0] = (from >> 8) & 0xff; 317 scsi_cmd.source_address[1] = from & 0xff; 318 scsi_cmd.destination_address[0] = (to >> 8) & 0xff; 319 scsi_cmd.destination_address[1] = to & 0xff; 320 scsi_cmd.invert = (chm & CH_INVERT) ? 1 : 0; 321 error = scsi_scsi_cmd(ch->sc_link, (struct scsi_generic *) &scsi_cmd, 322 sizeof(scsi_cmd), NULL, 0, CHRETRIES, 100000, NULL, flags); 323 if (error) 324 *stat = ch->lsterr; 325 else 326 *stat = 0; 327 return error; 328} 329 330int 331ch_position(ch, stat, chm, to, flags) 332 struct ch_data *ch; 333 short *stat; 334 int chm, to, flags; 335{ 336 struct scsi_position_to_element scsi_cmd; 337 int error; 338 339 bzero(&scsi_cmd, sizeof(scsi_cmd)); 340 scsi_cmd.op_code = POSITION_TO_ELEMENT; 341 scsi_cmd.transport_element_address[0] = (chm >> 8) & 0xff; 342 scsi_cmd.transport_element_address[1] = chm & 0xff; 343 scsi_cmd.source_address[0] = (to >> 8) & 0xff; 344 scsi_cmd.source_address[1] = to & 0xff; 345 scsi_cmd.invert = (chm & CH_INVERT) ? 1 : 0; 346 error = scsi_scsi_cmd(ch->sc_link, (struct scsi_generic *) &scsi_cmd, 347 sizeof(scsi_cmd), NULL, 0, CHRETRIES, 100000, NULL, flags); 348 if (error) 349 *stat = ch->lsterr; 350 else 351 *stat = 0; 352 return error; 353} 354 355#ifdef __STDC__ 356#define b2tol(a) (((unsigned)(a##_1) << 8) | (unsigned)a##_0) 357#else 358#define b2tol(a) (((unsigned)(a/**/_1) << 8) | (unsigned)a/**/_0) 359#endif 360 361/* 362 * Get the scsi driver to send a full inquiry to the 363 * device and use the results to fill out the global 364 * parameter structure. 365 */ 366int 367ch_mode_sense(ch, flags) 368 struct ch_data *ch; 369 int flags; 370{ 371 struct scsi_mode_sense scsi_cmd; 372 u_char scsi_sense[128]; /* Can't use scsi_mode_sense_data because of 373 * missing block descriptor. 374 */ 375 u_char *b; 376 int i, l; 377 int error; 378 struct scsi_link *sc_link = ch->sc_link; 379 380 /* 381 * First check if we have it all loaded 382 */ 383 if (sc_link->flags & SDEV_MEDIA_LOADED) 384 return 0; 385 386 /* 387 * First do a mode sense 388 */ 389 /* sc_link->flags &= ~SDEV_MEDIA_LOADED; *//*XXX */ 390 bzero(&scsi_cmd, sizeof(scsi_cmd)); 391 scsi_cmd.op_code = MODE_SENSE; 392 scsi_cmd.byte2 = SMS_DBD; 393 scsi_cmd.page = 0x3f; /* All Pages */ 394 scsi_cmd.length = sizeof(scsi_sense); 395 396 /* 397 * Read in the pages 398 */ 399 error = scsi_scsi_cmd(sc_link, (struct scsi_generic *) &scsi_cmd, 400 sizeof(scsi_cmd), (u_char *) &scsi_sense, sizeof(scsi_sense), 401 CHRETRIES, 5000, NULL, flags | SCSI_DATA_IN); 402 if (error) { 403 if (!(flags & SCSI_SILENT)) 404 printf("%s: could not mode sense\n", 405 ch->sc_dev.dv_xname); 406 return error; 407 } 408 409 sc_link->flags |= SDEV_MEDIA_LOADED; 410 l = scsi_sense[0] - 3; 411 b = &scsi_sense[4]; 412 413 /* 414 * To avoid alignment problems 415 */ 416/* XXXX - FIX THIS FOR MSB */ 417#define p2copy(valp) (valp[1] | (valp[0]<<8)); valp+=2 418#define p4copy(valp) (valp[3] | (valp[2]<<8) | (valp[1]<<16) | (valp[0]<<24)); valp+=4 419#if 0 420 printf("\nmode_sense %d\n", l); 421 for (i = 0; i < l + 4; i++) 422 printf("%x%c", scsi_sense[i], i % 8 == 7 ? '\n' : ':'); 423 printf("\n"); 424#endif 425 for (i = 0; i < l;) { 426 u_char pc = (*b++) & 0x3f; 427 u_char pl = *b++; 428 u_char *bb = b; 429 switch (pc) { 430 case 0x1d: 431 ch->chmo = p2copy(bb); 432 ch->chms = p2copy(bb); 433 ch->sloto = p2copy(bb); 434 ch->slots = p2copy(bb); 435 ch->imexo = p2copy(bb); 436 ch->imexs = p2copy(bb); 437 ch->driveo = p2copy(bb); 438 ch->drives = p2copy(bb); 439 break; 440 case 0x1e: 441 ch->rot = *b & 0x1; 442 break; 443 case 0x1f: 444 ch->stor = *b & 0xf; 445 bb += 2; 446 ch->stor = p4copy(bb); 447 break; 448 default: 449 break; 450 } 451 b += pl; 452 i += pl + 2; 453 } 454 SC_DEBUG(sc_link, SDEV_DB2, 455 (" cht(%d-%d)slot(%d-%d)imex(%d-%d)cts(%d-%d) %s rotate\n", 456 ch->chmo, ch->chms, ch->sloto, ch->slots, ch->imexo, ch->imexs, 457 ch->driveo, ch->drives, ch->rot ? "can" : "can't")); 458 return 0; 459} 460