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 * $DragonFly: src/sys/dev/disk/vpo/vpo.c,v 1.9 2006/12/22 23:26:17 swildner Exp $ 28 */ 29 30 #include <sys/param.h> 31 #include <sys/systm.h> 32 #include <sys/module.h> 33 #include <sys/bus.h> 34 #include <sys/malloc.h> 35 #include <sys/devicestat.h> /* for struct devstat */ 36 #include <sys/thread2.h> /* for crit_*() */ 37 38 #include <machine/clock.h> 39 40 #include <bus/cam/cam.h> 41 #include <bus/cam/cam_ccb.h> 42 #include <bus/cam/cam_sim.h> 43 #include <bus/cam/cam_xpt_sim.h> 44 #include <bus/cam/cam_debug.h> 45 #include <bus/cam/cam_periph.h> 46 47 #include <bus/cam/scsi/scsi_all.h> 48 #include <bus/cam/scsi/scsi_message.h> 49 #include <bus/cam/scsi/scsi_da.h> 50 51 #include <sys/kernel.h> 52 53 #include "opt_vpo.h" 54 55 #include <bus/ppbus/ppbconf.h> 56 #include "vpoio.h" 57 58 #include "ppbus_if.h" 59 60 struct vpo_sense { 61 struct scsi_sense cmd; 62 unsigned int stat; 63 unsigned int count; 64 }; 65 66 struct vpo_data { 67 unsigned short vpo_unit; 68 69 int vpo_stat; 70 int vpo_count; 71 int vpo_error; 72 73 int vpo_isplus; 74 75 struct cam_sim *sim; 76 77 struct vpo_sense vpo_sense; 78 79 struct vpoio_data vpo_io; /* interface to low level functions */ 80 }; 81 82 #define DEVTOSOFTC(dev) \ 83 ((struct vpo_data *)device_get_softc(dev)) 84 85 /* cam related functions */ 86 static void vpo_action(struct cam_sim *sim, union ccb *ccb); 87 static void vpo_poll(struct cam_sim *sim); 88 static void vpo_cam_rescan_callback(struct cam_periph *periph, 89 union ccb *ccb); 90 static void vpo_cam_rescan(struct vpo_data *vpo); 91 92 /* 93 * vpo_probe() 94 */ 95 static int 96 vpo_probe(device_t dev) 97 { 98 struct vpo_data *vpo; 99 int error; 100 101 vpo = DEVTOSOFTC(dev); 102 bzero(vpo, sizeof(struct vpo_data)); 103 104 /* vpo dependent initialisation */ 105 vpo->vpo_unit = device_get_unit(dev); 106 107 /* low level probe */ 108 vpoio_set_unit(&vpo->vpo_io, vpo->vpo_unit); 109 110 /* check ZIP before ZIP+ or imm_probe() will send controls to 111 * the printer or whatelse connected to the port */ 112 if ((error = vpoio_probe(dev, &vpo->vpo_io)) == 0) { 113 vpo->vpo_isplus = 0; 114 device_set_desc(dev, 115 "Iomega VPI0 Parallel to SCSI interface"); 116 } else if ((error = imm_probe(dev, &vpo->vpo_io)) == 0) { 117 vpo->vpo_isplus = 1; 118 device_set_desc(dev, 119 "Iomega Matchmaker Parallel to SCSI interface"); 120 } else { 121 return (error); 122 } 123 124 return (0); 125 } 126 127 /* 128 * vpo_attach() 129 */ 130 static int 131 vpo_attach(device_t dev) 132 { 133 struct vpo_data *vpo = DEVTOSOFTC(dev); 134 struct cam_devq *devq; 135 int error; 136 137 /* low level attachment */ 138 if (vpo->vpo_isplus) { 139 if ((error = imm_attach(&vpo->vpo_io))) 140 return (error); 141 } else { 142 if ((error = vpoio_attach(&vpo->vpo_io))) 143 return (error); 144 } 145 146 /* 147 ** Now tell the generic SCSI layer 148 ** about our bus. 149 */ 150 devq = cam_simq_alloc(/*maxopenings*/1); 151 /* XXX What about low-level detach on error? */ 152 if (devq == NULL) 153 return (ENXIO); 154 155 vpo->sim = cam_sim_alloc(vpo_action, vpo_poll, "vpo", vpo, 156 device_get_unit(dev), 157 /*untagged*/1, /*tagged*/0, devq); 158 cam_simq_release(devq); 159 if (vpo->sim == NULL) { 160 return (ENXIO); 161 } 162 163 if (xpt_bus_register(vpo->sim, /*bus*/0) != CAM_SUCCESS) { 164 cam_sim_free(vpo->sim); 165 return (ENXIO); 166 } 167 168 /* all went ok */ 169 170 vpo_cam_rescan(vpo); /* have CAM rescan the bus */ 171 172 return (0); 173 } 174 175 static void 176 vpo_cam_rescan_callback(struct cam_periph *periph, union ccb *ccb) 177 { 178 kfree(ccb, M_TEMP); 179 } 180 181 static void 182 vpo_cam_rescan(struct vpo_data *vpo) 183 { 184 struct cam_path *path; 185 union ccb *ccb = kmalloc(sizeof(union ccb), M_TEMP, M_WAITOK); 186 187 bzero(ccb, sizeof(union ccb)); 188 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=%d,c=%d,h=%d,spt=%d) request\n", 360 vpo->vpo_unit, 361 ccg->block_size, 362 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 { 0, 0 } 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, 0, 0); 464 465