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_sim.h> 42 #include <bus/cam/cam_debug.h> 43 #include <bus/cam/cam_periph.h> 44 45 #include <bus/cam/scsi/scsi_all.h> 46 #include <bus/cam/scsi/scsi_message.h> 47 #include <bus/cam/scsi/scsi_da.h> 48 49 #include <sys/kernel.h> 50 51 #include "opt_vpo.h" 52 53 #include <bus/ppbus/ppbconf.h> 54 #include "vpoio.h" 55 56 #include "ppbus_if.h" 57 58 struct vpo_sense { 59 struct scsi_sense cmd; 60 unsigned int stat; 61 unsigned int count; 62 }; 63 64 struct vpo_data { 65 unsigned short vpo_unit; 66 67 int vpo_stat; 68 int vpo_count; 69 int vpo_error; 70 71 int vpo_isplus; 72 73 struct cam_sim *sim; 74 75 struct vpo_sense vpo_sense; 76 77 struct vpoio_data vpo_io; /* interface to low level functions */ 78 }; 79 80 #define DEVTOSOFTC(dev) \ 81 ((struct vpo_data *)device_get_softc(dev)) 82 83 /* cam related functions */ 84 static void vpo_action(struct cam_sim *sim, union ccb *ccb); 85 static void vpo_poll(struct cam_sim *sim); 86 static void vpo_cam_rescan_callback(struct cam_periph *periph, 87 union ccb *ccb); 88 static void vpo_cam_rescan(struct vpo_data *vpo); 89 90 /* 91 * vpo_probe() 92 */ 93 static int 94 vpo_probe(device_t dev) 95 { 96 struct vpo_data *vpo; 97 int error; 98 99 vpo = DEVTOSOFTC(dev); 100 bzero(vpo, sizeof(struct vpo_data)); 101 102 /* vpo dependent initialisation */ 103 vpo->vpo_unit = device_get_unit(dev); 104 105 /* low level probe */ 106 vpoio_set_unit(&vpo->vpo_io, vpo->vpo_unit); 107 108 /* check ZIP before ZIP+ or imm_probe() will send controls to 109 * the printer or whatelse connected to the port */ 110 if ((error = vpoio_probe(dev, &vpo->vpo_io)) == 0) { 111 vpo->vpo_isplus = 0; 112 device_set_desc(dev, 113 "Iomega VPI0 Parallel to SCSI interface"); 114 } else if ((error = imm_probe(dev, &vpo->vpo_io)) == 0) { 115 vpo->vpo_isplus = 1; 116 device_set_desc(dev, 117 "Iomega Matchmaker Parallel to SCSI interface"); 118 } else { 119 return (error); 120 } 121 122 return (0); 123 } 124 125 /* 126 * vpo_attach() 127 */ 128 static int 129 vpo_attach(device_t dev) 130 { 131 struct vpo_data *vpo = DEVTOSOFTC(dev); 132 struct cam_devq *devq; 133 int error; 134 135 /* low level attachment */ 136 if (vpo->vpo_isplus) { 137 if ((error = imm_attach(&vpo->vpo_io))) 138 return (error); 139 } else { 140 if ((error = vpoio_attach(&vpo->vpo_io))) 141 return (error); 142 } 143 144 /* 145 ** Now tell the generic SCSI layer 146 ** about our bus. 147 */ 148 devq = cam_simq_alloc(/*maxopenings*/1); 149 /* XXX What about low-level detach on error? */ 150 if (devq == NULL) 151 return (ENXIO); 152 153 vpo->sim = cam_sim_alloc(vpo_action, vpo_poll, "vpo", vpo, 154 device_get_unit(dev), &sim_mplock, 155 /*untagged*/1, /*tagged*/0, devq); 156 cam_simq_release(devq); 157 if (vpo->sim == NULL) { 158 return (ENXIO); 159 } 160 161 if (xpt_bus_register(vpo->sim, /*bus*/0) != CAM_SUCCESS) { 162 cam_sim_free(vpo->sim); 163 return (ENXIO); 164 } 165 166 /* all went ok */ 167 168 vpo_cam_rescan(vpo); /* have CAM rescan the bus */ 169 170 return (0); 171 } 172 173 static void 174 vpo_cam_rescan_callback(struct cam_periph *periph, union ccb *ccb) 175 { 176 kfree(ccb, M_TEMP); 177 } 178 179 static void 180 vpo_cam_rescan(struct vpo_data *vpo) 181 { 182 struct cam_path *path; 183 union ccb *ccb = kmalloc(sizeof(union ccb), M_TEMP, M_WAITOK | M_ZERO); 184 185 if (xpt_create_path(&path, xpt_periph, cam_sim_path(vpo->sim), 0, 0) 186 != CAM_REQ_CMP) { 187 /* A failure is benign as the user can do a manual rescan */ 188 return; 189 } 190 191 xpt_setup_ccb(&ccb->ccb_h, path, 5/*priority (low)*/); 192 ccb->ccb_h.func_code = XPT_SCAN_BUS; 193 ccb->ccb_h.cbfcnp = vpo_cam_rescan_callback; 194 ccb->crcn.flags = CAM_FLAG_NONE; 195 xpt_action(ccb); 196 197 /* The scan is in progress now. */ 198 } 199 200 /* 201 * vpo_intr() 202 */ 203 static void 204 vpo_intr(struct vpo_data *vpo, struct ccb_scsiio *csio) 205 { 206 int error; 207 #ifdef VP0_DEBUG 208 int i; 209 #endif 210 211 crit_enter(); 212 213 if (vpo->vpo_isplus) { 214 error = imm_do_scsi(&vpo->vpo_io, VP0_INITIATOR, 215 csio->ccb_h.target_id, 216 (char *)&csio->cdb_io.cdb_bytes, csio->cdb_len, 217 (char *)csio->data_ptr, csio->dxfer_len, 218 &vpo->vpo_stat, &vpo->vpo_count, &vpo->vpo_error); 219 } else { 220 error = vpoio_do_scsi(&vpo->vpo_io, VP0_INITIATOR, 221 csio->ccb_h.target_id, 222 (char *)&csio->cdb_io.cdb_bytes, csio->cdb_len, 223 (char *)csio->data_ptr, csio->dxfer_len, 224 &vpo->vpo_stat, &vpo->vpo_count, &vpo->vpo_error); 225 } 226 227 #ifdef VP0_DEBUG 228 kprintf("vpo_do_scsi = %d, status = 0x%x, count = %d, vpo_error = %d\n", 229 error, vpo->vpo_stat, vpo->vpo_count, vpo->vpo_error); 230 231 /* dump of command */ 232 for (i=0; i<csio->cdb_len; i++) 233 kprintf("%x ", ((char *)&csio->cdb_io.cdb_bytes)[i]); 234 235 kprintf("\n"); 236 #endif 237 238 if (error) { 239 /* connection to ppbus interrupted */ 240 csio->ccb_h.status = CAM_CMD_TIMEOUT; 241 goto error; 242 } 243 244 /* if a timeout occured, no sense */ 245 if (vpo->vpo_error) { 246 if (vpo->vpo_error != VP0_ESELECT_TIMEOUT) 247 kprintf("vpo%d: VP0 error/timeout (%d)\n", 248 vpo->vpo_unit, vpo->vpo_error); 249 250 csio->ccb_h.status = CAM_CMD_TIMEOUT; 251 goto error; 252 } 253 254 /* check scsi status */ 255 if (vpo->vpo_stat != SCSI_STATUS_OK) { 256 csio->scsi_status = vpo->vpo_stat; 257 258 /* check if we have to sense the drive */ 259 if ((vpo->vpo_stat & SCSI_STATUS_CHECK_COND) != 0) { 260 261 vpo->vpo_sense.cmd.opcode = REQUEST_SENSE; 262 vpo->vpo_sense.cmd.length = csio->sense_len; 263 vpo->vpo_sense.cmd.control = 0; 264 265 if (vpo->vpo_isplus) { 266 error = imm_do_scsi(&vpo->vpo_io, VP0_INITIATOR, 267 csio->ccb_h.target_id, 268 (char *)&vpo->vpo_sense.cmd, 269 sizeof(vpo->vpo_sense.cmd), 270 (char *)&csio->sense_data, csio->sense_len, 271 &vpo->vpo_sense.stat, &vpo->vpo_sense.count, 272 &vpo->vpo_error); 273 } else { 274 error = vpoio_do_scsi(&vpo->vpo_io, VP0_INITIATOR, 275 csio->ccb_h.target_id, 276 (char *)&vpo->vpo_sense.cmd, 277 sizeof(vpo->vpo_sense.cmd), 278 (char *)&csio->sense_data, csio->sense_len, 279 &vpo->vpo_sense.stat, &vpo->vpo_sense.count, 280 &vpo->vpo_error); 281 } 282 283 284 #ifdef VP0_DEBUG 285 kprintf("(sense) vpo_do_scsi = %d, status = 0x%x, count = %d, vpo_error = %d\n", 286 error, vpo->vpo_sense.stat, vpo->vpo_sense.count, vpo->vpo_error); 287 #endif 288 289 /* check sense return status */ 290 if (error == 0 && vpo->vpo_sense.stat == SCSI_STATUS_OK) { 291 /* sense ok */ 292 csio->ccb_h.status = CAM_AUTOSNS_VALID | CAM_SCSI_STATUS_ERROR; 293 csio->sense_resid = csio->sense_len - vpo->vpo_sense.count; 294 295 #ifdef VP0_DEBUG 296 /* dump of sense info */ 297 kprintf("(sense) "); 298 for (i=0; i<vpo->vpo_sense.count; i++) 299 kprintf("%x ", ((char *)&csio->sense_data)[i]); 300 kprintf("\n"); 301 #endif 302 303 } else { 304 /* sense failed */ 305 csio->ccb_h.status = CAM_AUTOSENSE_FAIL; 306 } 307 } else { 308 /* no sense */ 309 csio->ccb_h.status = CAM_SCSI_STATUS_ERROR; 310 } 311 312 goto error; 313 } 314 315 csio->resid = csio->dxfer_len - vpo->vpo_count; 316 csio->ccb_h.status = CAM_REQ_CMP; 317 318 error: 319 crit_exit(); 320 321 return; 322 } 323 324 static void 325 vpo_action(struct cam_sim *sim, union ccb *ccb) 326 { 327 328 struct vpo_data *vpo = (struct vpo_data *)sim->softc; 329 330 switch (ccb->ccb_h.func_code) { 331 case XPT_SCSI_IO: 332 { 333 struct ccb_scsiio *csio; 334 335 csio = &ccb->csio; 336 337 #ifdef VP0_DEBUG 338 kprintf("vpo%d: XPT_SCSI_IO (0x%x) request\n", 339 vpo->vpo_unit, csio->cdb_io.cdb_bytes[0]); 340 #endif 341 342 vpo_intr(vpo, csio); 343 344 xpt_done(ccb); 345 346 break; 347 } 348 case XPT_CALC_GEOMETRY: 349 { 350 struct ccb_calc_geometry *ccg; 351 352 ccg = &ccb->ccg; 353 354 #ifdef VP0_DEBUG 355 kprintf("vpo%d: XPT_CALC_GEOMETRY (bs=%d,vs=%ju,c=%d,h=%d,spt=%d) request\n", 356 vpo->vpo_unit, 357 ccg->block_size, 358 (uintmax_t)ccg->volume_size, 359 ccg->cylinders, 360 ccg->heads, 361 ccg->secs_per_track); 362 #endif 363 364 ccg->heads = 64; 365 ccg->secs_per_track = 32; 366 ccg->cylinders = ccg->volume_size / 367 (ccg->heads * ccg->secs_per_track); 368 369 ccb->ccb_h.status = CAM_REQ_CMP; 370 xpt_done(ccb); 371 break; 372 } 373 case XPT_RESET_BUS: /* Reset the specified SCSI bus */ 374 { 375 376 #ifdef VP0_DEBUG 377 kprintf("vpo%d: XPT_RESET_BUS request\n", vpo->vpo_unit); 378 #endif 379 380 if (vpo->vpo_isplus) { 381 if (imm_reset_bus(&vpo->vpo_io)) { 382 ccb->ccb_h.status = CAM_REQ_CMP_ERR; 383 xpt_done(ccb); 384 return; 385 } 386 } else { 387 if (vpoio_reset_bus(&vpo->vpo_io)) { 388 ccb->ccb_h.status = CAM_REQ_CMP_ERR; 389 xpt_done(ccb); 390 return; 391 } 392 } 393 394 ccb->ccb_h.status = CAM_REQ_CMP; 395 xpt_done(ccb); 396 break; 397 } 398 case XPT_PATH_INQ: /* Path routing inquiry */ 399 { 400 struct ccb_pathinq *cpi = &ccb->cpi; 401 402 #ifdef VP0_DEBUG 403 kprintf("vpo%d: XPT_PATH_INQ request\n", vpo->vpo_unit); 404 #endif 405 cpi->version_num = 1; /* XXX??? */ 406 cpi->hba_inquiry = 0; 407 cpi->target_sprt = 0; 408 cpi->hba_misc = 0; 409 cpi->hba_eng_cnt = 0; 410 cpi->max_target = 7; 411 cpi->max_lun = 0; 412 cpi->initiator_id = VP0_INITIATOR; 413 cpi->bus_id = sim->bus_id; 414 cpi->base_transfer_speed = 93; 415 strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN); 416 strncpy(cpi->hba_vid, "Iomega", HBA_IDLEN); 417 strncpy(cpi->dev_name, sim->sim_name, DEV_IDLEN); 418 cpi->unit_number = sim->unit_number; 419 420 cpi->ccb_h.status = CAM_REQ_CMP; 421 xpt_done(ccb); 422 break; 423 } 424 default: 425 ccb->ccb_h.status = CAM_REQ_INVALID; 426 xpt_done(ccb); 427 break; 428 } 429 430 return; 431 } 432 433 static void 434 vpo_poll(struct cam_sim *sim) 435 { 436 /* The ZIP is actually always polled throw vpo_action() */ 437 return; 438 } 439 440 /* 441 * Always create a "vpo" device under ppbus. Use device_identify to 442 * create the static entry for any attached ppbus. 443 */ 444 static devclass_t vpo_devclass; 445 446 static device_method_t vpo_methods[] = { 447 /* device interface */ 448 DEVMETHOD(device_identify, bus_generic_identify), 449 DEVMETHOD(device_probe, vpo_probe), 450 DEVMETHOD(device_attach, vpo_attach), 451 { 0, 0 } 452 }; 453 454 static driver_t vpo_driver = { 455 "vpo", 456 vpo_methods, 457 sizeof(struct vpo_data), 458 }; 459 DRIVER_MODULE(vpo, ppbus, vpo_driver, vpo_devclass, NULL, NULL); 460 461