1 /*- 2 * Copyright (c) 1997, 1998, 1999 Nicolas Souchu 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 * $FreeBSD: src/sys/dev/ppbus/vpo.c,v 1.20.2.1 2000/05/07 21:08:18 n_hibma Exp $ 27 */ 28 29 #include <sys/param.h> 30 #include <sys/systm.h> 31 #include <sys/module.h> 32 #include <sys/bus.h> 33 #include <sys/malloc.h> 34 #include <sys/thread2.h> /* for crit_*() */ 35 36 #include <machine/clock.h> 37 38 #include <bus/cam/cam.h> 39 #include <bus/cam/cam_ccb.h> 40 #include <bus/cam/cam_sim.h> 41 #include <bus/cam/cam_xpt.h> 42 #include <bus/cam/cam_xpt_sim.h> 43 #include <bus/cam/cam_xpt_periph.h> 44 #include <bus/cam/cam_debug.h> 45 #include <bus/cam/cam_periph.h> 46 47 48 #include <bus/cam/scsi/scsi_all.h> 49 #include <bus/cam/scsi/scsi_message.h> 50 #include <bus/cam/scsi/scsi_da.h> 51 52 #include <sys/kernel.h> 53 54 #include "opt_vpo.h" 55 56 #include <bus/ppbus/ppbconf.h> 57 #include "vpoio.h" 58 59 #include "ppbus_if.h" 60 61 struct vpo_sense { 62 struct scsi_sense cmd; 63 unsigned int stat; 64 unsigned int count; 65 }; 66 67 struct vpo_data { 68 unsigned short vpo_unit; 69 70 int vpo_stat; 71 int vpo_count; 72 int vpo_error; 73 74 int vpo_isplus; 75 76 struct cam_sim *sim; 77 78 struct vpo_sense vpo_sense; 79 80 struct vpoio_data vpo_io; /* interface to low level functions */ 81 }; 82 83 #define DEVTOSOFTC(dev) \ 84 ((struct vpo_data *)device_get_softc(dev)) 85 86 /* cam related functions */ 87 static void vpo_action(struct cam_sim *sim, union ccb *ccb); 88 static void vpo_poll(struct cam_sim *sim); 89 static void vpo_cam_rescan_callback(struct cam_periph *periph, 90 union ccb *ccb); 91 static void vpo_cam_rescan(struct vpo_data *vpo); 92 93 /* 94 * vpo_probe() 95 */ 96 static int 97 vpo_probe(device_t dev) 98 { 99 struct vpo_data *vpo; 100 int error; 101 102 vpo = DEVTOSOFTC(dev); 103 bzero(vpo, sizeof(struct vpo_data)); 104 105 /* vpo dependent initialisation */ 106 vpo->vpo_unit = device_get_unit(dev); 107 108 /* low level probe */ 109 vpoio_set_unit(&vpo->vpo_io, vpo->vpo_unit); 110 111 /* check ZIP before ZIP+ or imm_probe() will send controls to 112 * the printer or whatelse connected to the port */ 113 if ((error = vpoio_probe(dev, &vpo->vpo_io)) == 0) { 114 vpo->vpo_isplus = 0; 115 device_set_desc(dev, 116 "Iomega VPI0 Parallel to SCSI interface"); 117 } else if ((error = imm_probe(dev, &vpo->vpo_io)) == 0) { 118 vpo->vpo_isplus = 1; 119 device_set_desc(dev, 120 "Iomega Matchmaker Parallel to SCSI interface"); 121 } else { 122 return (error); 123 } 124 125 return (0); 126 } 127 128 /* 129 * vpo_attach() 130 */ 131 static int 132 vpo_attach(device_t dev) 133 { 134 struct vpo_data *vpo = DEVTOSOFTC(dev); 135 struct cam_devq *devq; 136 int error; 137 138 /* low level attachment */ 139 if (vpo->vpo_isplus) { 140 if ((error = imm_attach(&vpo->vpo_io))) 141 return (error); 142 } else { 143 if ((error = vpoio_attach(&vpo->vpo_io))) 144 return (error); 145 } 146 147 /* 148 ** Now tell the generic SCSI layer 149 ** about our bus. 150 */ 151 devq = cam_simq_alloc(/*maxopenings*/1); 152 /* XXX What about low-level detach on error? */ 153 if (devq == NULL) 154 return (ENXIO); 155 156 vpo->sim = cam_sim_alloc(vpo_action, vpo_poll, "vpo", vpo, 157 device_get_unit(dev), &sim_mplock, 158 /*untagged*/1, /*tagged*/0, devq); 159 cam_simq_release(devq); 160 if (vpo->sim == NULL) { 161 return (ENXIO); 162 } 163 164 if (xpt_bus_register(vpo->sim, /*bus*/0) != CAM_SUCCESS) { 165 cam_sim_free(vpo->sim); 166 return (ENXIO); 167 } 168 169 /* all went ok */ 170 171 vpo_cam_rescan(vpo); /* have CAM rescan the bus */ 172 173 return (0); 174 } 175 176 static void 177 vpo_cam_rescan_callback(struct cam_periph *periph, union ccb *ccb) 178 { 179 xpt_free_ccb(&ccb->ccb_h); 180 } 181 182 static void 183 vpo_cam_rescan(struct vpo_data *vpo) 184 { 185 struct cam_path *path; 186 union ccb *ccb; 187 188 ccb = xpt_alloc_ccb(); 189 if (xpt_create_path(&path, xpt_periph, cam_sim_path(vpo->sim), 0, 0) 190 != CAM_REQ_CMP) { 191 /* A failure is benign as the user can do a manual rescan */ 192 return; 193 } 194 195 xpt_setup_ccb(&ccb->ccb_h, path, 5/*priority (low)*/); 196 ccb->ccb_h.func_code = XPT_SCAN_BUS; 197 ccb->ccb_h.cbfcnp = vpo_cam_rescan_callback; 198 ccb->crcn.flags = CAM_FLAG_NONE; 199 xpt_action(ccb); 200 201 /* The scan is in progress now. */ 202 } 203 204 /* 205 * vpo_intr() 206 */ 207 static void 208 vpo_intr(struct vpo_data *vpo, struct ccb_scsiio *csio) 209 { 210 int error; 211 #ifdef VP0_DEBUG 212 int i; 213 #endif 214 215 crit_enter(); 216 217 if (vpo->vpo_isplus) { 218 error = imm_do_scsi(&vpo->vpo_io, VP0_INITIATOR, 219 csio->ccb_h.target_id, 220 (char *)&csio->cdb_io.cdb_bytes, csio->cdb_len, 221 (char *)csio->data_ptr, csio->dxfer_len, 222 &vpo->vpo_stat, &vpo->vpo_count, &vpo->vpo_error); 223 } else { 224 error = vpoio_do_scsi(&vpo->vpo_io, VP0_INITIATOR, 225 csio->ccb_h.target_id, 226 (char *)&csio->cdb_io.cdb_bytes, csio->cdb_len, 227 (char *)csio->data_ptr, csio->dxfer_len, 228 &vpo->vpo_stat, &vpo->vpo_count, &vpo->vpo_error); 229 } 230 231 #ifdef VP0_DEBUG 232 kprintf("vpo_do_scsi = %d, status = 0x%x, count = %d, vpo_error = %d\n", 233 error, vpo->vpo_stat, vpo->vpo_count, vpo->vpo_error); 234 235 /* dump of command */ 236 for (i=0; i<csio->cdb_len; i++) 237 kprintf("%x ", ((char *)&csio->cdb_io.cdb_bytes)[i]); 238 239 kprintf("\n"); 240 #endif 241 242 if (error) { 243 /* connection to ppbus interrupted */ 244 csio->ccb_h.status = CAM_CMD_TIMEOUT; 245 goto error; 246 } 247 248 /* if a timeout occured, no sense */ 249 if (vpo->vpo_error) { 250 if (vpo->vpo_error != VP0_ESELECT_TIMEOUT) 251 kprintf("vpo%d: VP0 error/timeout (%d)\n", 252 vpo->vpo_unit, vpo->vpo_error); 253 254 csio->ccb_h.status = CAM_CMD_TIMEOUT; 255 goto error; 256 } 257 258 /* check scsi status */ 259 if (vpo->vpo_stat != SCSI_STATUS_OK) { 260 csio->scsi_status = vpo->vpo_stat; 261 262 /* check if we have to sense the drive */ 263 if ((vpo->vpo_stat & SCSI_STATUS_CHECK_COND) != 0) { 264 265 vpo->vpo_sense.cmd.opcode = REQUEST_SENSE; 266 vpo->vpo_sense.cmd.length = csio->sense_len; 267 vpo->vpo_sense.cmd.control = 0; 268 269 if (vpo->vpo_isplus) { 270 error = imm_do_scsi(&vpo->vpo_io, VP0_INITIATOR, 271 csio->ccb_h.target_id, 272 (char *)&vpo->vpo_sense.cmd, 273 sizeof(vpo->vpo_sense.cmd), 274 (char *)&csio->sense_data, csio->sense_len, 275 &vpo->vpo_sense.stat, &vpo->vpo_sense.count, 276 &vpo->vpo_error); 277 } else { 278 error = vpoio_do_scsi(&vpo->vpo_io, VP0_INITIATOR, 279 csio->ccb_h.target_id, 280 (char *)&vpo->vpo_sense.cmd, 281 sizeof(vpo->vpo_sense.cmd), 282 (char *)&csio->sense_data, csio->sense_len, 283 &vpo->vpo_sense.stat, &vpo->vpo_sense.count, 284 &vpo->vpo_error); 285 } 286 287 288 #ifdef VP0_DEBUG 289 kprintf("(sense) vpo_do_scsi = %d, status = 0x%x, count = %d, vpo_error = %d\n", 290 error, vpo->vpo_sense.stat, vpo->vpo_sense.count, vpo->vpo_error); 291 #endif 292 293 /* check sense return status */ 294 if (error == 0 && vpo->vpo_sense.stat == SCSI_STATUS_OK) { 295 /* sense ok */ 296 csio->ccb_h.status = CAM_AUTOSNS_VALID | CAM_SCSI_STATUS_ERROR; 297 csio->sense_resid = csio->sense_len - vpo->vpo_sense.count; 298 299 #ifdef VP0_DEBUG 300 /* dump of sense info */ 301 kprintf("(sense) "); 302 for (i=0; i<vpo->vpo_sense.count; i++) 303 kprintf("%x ", ((char *)&csio->sense_data)[i]); 304 kprintf("\n"); 305 #endif 306 307 } else { 308 /* sense failed */ 309 csio->ccb_h.status = CAM_AUTOSENSE_FAIL; 310 } 311 } else { 312 /* no sense */ 313 csio->ccb_h.status = CAM_SCSI_STATUS_ERROR; 314 } 315 316 goto error; 317 } 318 319 csio->resid = csio->dxfer_len - vpo->vpo_count; 320 csio->ccb_h.status = CAM_REQ_CMP; 321 322 error: 323 crit_exit(); 324 325 return; 326 } 327 328 static void 329 vpo_action(struct cam_sim *sim, union ccb *ccb) 330 { 331 332 struct vpo_data *vpo = (struct vpo_data *)sim->softc; 333 334 switch (ccb->ccb_h.func_code) { 335 case XPT_SCSI_IO: 336 { 337 struct ccb_scsiio *csio; 338 339 csio = &ccb->csio; 340 341 #ifdef VP0_DEBUG 342 kprintf("vpo%d: XPT_SCSI_IO (0x%x) request\n", 343 vpo->vpo_unit, csio->cdb_io.cdb_bytes[0]); 344 #endif 345 346 vpo_intr(vpo, csio); 347 348 xpt_done(ccb); 349 350 break; 351 } 352 case XPT_CALC_GEOMETRY: 353 { 354 struct ccb_calc_geometry *ccg; 355 356 ccg = &ccb->ccg; 357 358 #ifdef VP0_DEBUG 359 kprintf("vpo%d: XPT_CALC_GEOMETRY (bs=%d,vs=%ju,c=%d,h=%d,spt=%d) request\n", 360 vpo->vpo_unit, 361 ccg->block_size, 362 (uintmax_t)ccg->volume_size, 363 ccg->cylinders, 364 ccg->heads, 365 ccg->secs_per_track); 366 #endif 367 368 ccg->heads = 64; 369 ccg->secs_per_track = 32; 370 ccg->cylinders = ccg->volume_size / 371 (ccg->heads * ccg->secs_per_track); 372 373 ccb->ccb_h.status = CAM_REQ_CMP; 374 xpt_done(ccb); 375 break; 376 } 377 case XPT_RESET_BUS: /* Reset the specified SCSI bus */ 378 { 379 380 #ifdef VP0_DEBUG 381 kprintf("vpo%d: XPT_RESET_BUS request\n", vpo->vpo_unit); 382 #endif 383 384 if (vpo->vpo_isplus) { 385 if (imm_reset_bus(&vpo->vpo_io)) { 386 ccb->ccb_h.status = CAM_REQ_CMP_ERR; 387 xpt_done(ccb); 388 return; 389 } 390 } else { 391 if (vpoio_reset_bus(&vpo->vpo_io)) { 392 ccb->ccb_h.status = CAM_REQ_CMP_ERR; 393 xpt_done(ccb); 394 return; 395 } 396 } 397 398 ccb->ccb_h.status = CAM_REQ_CMP; 399 xpt_done(ccb); 400 break; 401 } 402 case XPT_PATH_INQ: /* Path routing inquiry */ 403 { 404 struct ccb_pathinq *cpi = &ccb->cpi; 405 406 #ifdef VP0_DEBUG 407 kprintf("vpo%d: XPT_PATH_INQ request\n", vpo->vpo_unit); 408 #endif 409 cpi->version_num = 1; /* XXX??? */ 410 cpi->hba_inquiry = 0; 411 cpi->target_sprt = 0; 412 cpi->hba_misc = 0; 413 cpi->hba_eng_cnt = 0; 414 cpi->max_target = 7; 415 cpi->max_lun = 0; 416 cpi->initiator_id = VP0_INITIATOR; 417 cpi->bus_id = sim->bus_id; 418 cpi->base_transfer_speed = 93; 419 strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN); 420 strncpy(cpi->hba_vid, "Iomega", HBA_IDLEN); 421 strncpy(cpi->dev_name, sim->sim_name, DEV_IDLEN); 422 cpi->unit_number = sim->unit_number; 423 424 cpi->ccb_h.status = CAM_REQ_CMP; 425 xpt_done(ccb); 426 break; 427 } 428 default: 429 ccb->ccb_h.status = CAM_REQ_INVALID; 430 xpt_done(ccb); 431 break; 432 } 433 434 return; 435 } 436 437 static void 438 vpo_poll(struct cam_sim *sim) 439 { 440 /* The ZIP is actually always polled throw vpo_action() */ 441 return; 442 } 443 444 /* 445 * Always create a "vpo" device under ppbus. Use device_identify to 446 * create the static entry for any attached ppbus. 447 */ 448 static devclass_t vpo_devclass; 449 450 static device_method_t vpo_methods[] = { 451 /* device interface */ 452 DEVMETHOD(device_identify, bus_generic_identify), 453 DEVMETHOD(device_probe, vpo_probe), 454 DEVMETHOD(device_attach, vpo_attach), 455 DEVMETHOD_END 456 }; 457 458 static driver_t vpo_driver = { 459 "vpo", 460 vpo_methods, 461 sizeof(struct vpo_data), 462 }; 463 DRIVER_MODULE(vpo, ppbus, vpo_driver, vpo_devclass, NULL, NULL); 464 465