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.12 2008/05/18 20:30:23 pavalos 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), &sim_mplock, 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 | M_ZERO); 186 187 if (xpt_create_path(&path, xpt_periph, cam_sim_path(vpo->sim), 0, 0) 188 != CAM_REQ_CMP) { 189 /* A failure is benign as the user can do a manual rescan */ 190 return; 191 } 192 193 xpt_setup_ccb(&ccb->ccb_h, path, 5/*priority (low)*/); 194 ccb->ccb_h.func_code = XPT_SCAN_BUS; 195 ccb->ccb_h.cbfcnp = vpo_cam_rescan_callback; 196 ccb->crcn.flags = CAM_FLAG_NONE; 197 xpt_action(ccb); 198 199 /* The scan is in progress now. */ 200 } 201 202 /* 203 * vpo_intr() 204 */ 205 static void 206 vpo_intr(struct vpo_data *vpo, struct ccb_scsiio *csio) 207 { 208 int error; 209 #ifdef VP0_DEBUG 210 int i; 211 #endif 212 213 crit_enter(); 214 215 if (vpo->vpo_isplus) { 216 error = imm_do_scsi(&vpo->vpo_io, VP0_INITIATOR, 217 csio->ccb_h.target_id, 218 (char *)&csio->cdb_io.cdb_bytes, csio->cdb_len, 219 (char *)csio->data_ptr, csio->dxfer_len, 220 &vpo->vpo_stat, &vpo->vpo_count, &vpo->vpo_error); 221 } else { 222 error = vpoio_do_scsi(&vpo->vpo_io, VP0_INITIATOR, 223 csio->ccb_h.target_id, 224 (char *)&csio->cdb_io.cdb_bytes, csio->cdb_len, 225 (char *)csio->data_ptr, csio->dxfer_len, 226 &vpo->vpo_stat, &vpo->vpo_count, &vpo->vpo_error); 227 } 228 229 #ifdef VP0_DEBUG 230 kprintf("vpo_do_scsi = %d, status = 0x%x, count = %d, vpo_error = %d\n", 231 error, vpo->vpo_stat, vpo->vpo_count, vpo->vpo_error); 232 233 /* dump of command */ 234 for (i=0; i<csio->cdb_len; i++) 235 kprintf("%x ", ((char *)&csio->cdb_io.cdb_bytes)[i]); 236 237 kprintf("\n"); 238 #endif 239 240 if (error) { 241 /* connection to ppbus interrupted */ 242 csio->ccb_h.status = CAM_CMD_TIMEOUT; 243 goto error; 244 } 245 246 /* if a timeout occured, no sense */ 247 if (vpo->vpo_error) { 248 if (vpo->vpo_error != VP0_ESELECT_TIMEOUT) 249 kprintf("vpo%d: VP0 error/timeout (%d)\n", 250 vpo->vpo_unit, vpo->vpo_error); 251 252 csio->ccb_h.status = CAM_CMD_TIMEOUT; 253 goto error; 254 } 255 256 /* check scsi status */ 257 if (vpo->vpo_stat != SCSI_STATUS_OK) { 258 csio->scsi_status = vpo->vpo_stat; 259 260 /* check if we have to sense the drive */ 261 if ((vpo->vpo_stat & SCSI_STATUS_CHECK_COND) != 0) { 262 263 vpo->vpo_sense.cmd.opcode = REQUEST_SENSE; 264 vpo->vpo_sense.cmd.length = csio->sense_len; 265 vpo->vpo_sense.cmd.control = 0; 266 267 if (vpo->vpo_isplus) { 268 error = imm_do_scsi(&vpo->vpo_io, VP0_INITIATOR, 269 csio->ccb_h.target_id, 270 (char *)&vpo->vpo_sense.cmd, 271 sizeof(vpo->vpo_sense.cmd), 272 (char *)&csio->sense_data, csio->sense_len, 273 &vpo->vpo_sense.stat, &vpo->vpo_sense.count, 274 &vpo->vpo_error); 275 } else { 276 error = vpoio_do_scsi(&vpo->vpo_io, VP0_INITIATOR, 277 csio->ccb_h.target_id, 278 (char *)&vpo->vpo_sense.cmd, 279 sizeof(vpo->vpo_sense.cmd), 280 (char *)&csio->sense_data, csio->sense_len, 281 &vpo->vpo_sense.stat, &vpo->vpo_sense.count, 282 &vpo->vpo_error); 283 } 284 285 286 #ifdef VP0_DEBUG 287 kprintf("(sense) vpo_do_scsi = %d, status = 0x%x, count = %d, vpo_error = %d\n", 288 error, vpo->vpo_sense.stat, vpo->vpo_sense.count, vpo->vpo_error); 289 #endif 290 291 /* check sense return status */ 292 if (error == 0 && vpo->vpo_sense.stat == SCSI_STATUS_OK) { 293 /* sense ok */ 294 csio->ccb_h.status = CAM_AUTOSNS_VALID | CAM_SCSI_STATUS_ERROR; 295 csio->sense_resid = csio->sense_len - vpo->vpo_sense.count; 296 297 #ifdef VP0_DEBUG 298 /* dump of sense info */ 299 kprintf("(sense) "); 300 for (i=0; i<vpo->vpo_sense.count; i++) 301 kprintf("%x ", ((char *)&csio->sense_data)[i]); 302 kprintf("\n"); 303 #endif 304 305 } else { 306 /* sense failed */ 307 csio->ccb_h.status = CAM_AUTOSENSE_FAIL; 308 } 309 } else { 310 /* no sense */ 311 csio->ccb_h.status = CAM_SCSI_STATUS_ERROR; 312 } 313 314 goto error; 315 } 316 317 csio->resid = csio->dxfer_len - vpo->vpo_count; 318 csio->ccb_h.status = CAM_REQ_CMP; 319 320 error: 321 crit_exit(); 322 323 return; 324 } 325 326 static void 327 vpo_action(struct cam_sim *sim, union ccb *ccb) 328 { 329 330 struct vpo_data *vpo = (struct vpo_data *)sim->softc; 331 332 switch (ccb->ccb_h.func_code) { 333 case XPT_SCSI_IO: 334 { 335 struct ccb_scsiio *csio; 336 337 csio = &ccb->csio; 338 339 #ifdef VP0_DEBUG 340 kprintf("vpo%d: XPT_SCSI_IO (0x%x) request\n", 341 vpo->vpo_unit, csio->cdb_io.cdb_bytes[0]); 342 #endif 343 344 vpo_intr(vpo, csio); 345 346 xpt_done(ccb); 347 348 break; 349 } 350 case XPT_CALC_GEOMETRY: 351 { 352 struct ccb_calc_geometry *ccg; 353 354 ccg = &ccb->ccg; 355 356 #ifdef VP0_DEBUG 357 kprintf("vpo%d: XPT_CALC_GEOMETRY (bs=%d,vs=%ju,c=%d,h=%d,spt=%d) request\n", 358 vpo->vpo_unit, 359 ccg->block_size, 360 (uintmax_t)ccg->volume_size, 361 ccg->cylinders, 362 ccg->heads, 363 ccg->secs_per_track); 364 #endif 365 366 ccg->heads = 64; 367 ccg->secs_per_track = 32; 368 ccg->cylinders = ccg->volume_size / 369 (ccg->heads * ccg->secs_per_track); 370 371 ccb->ccb_h.status = CAM_REQ_CMP; 372 xpt_done(ccb); 373 break; 374 } 375 case XPT_RESET_BUS: /* Reset the specified SCSI bus */ 376 { 377 378 #ifdef VP0_DEBUG 379 kprintf("vpo%d: XPT_RESET_BUS request\n", vpo->vpo_unit); 380 #endif 381 382 if (vpo->vpo_isplus) { 383 if (imm_reset_bus(&vpo->vpo_io)) { 384 ccb->ccb_h.status = CAM_REQ_CMP_ERR; 385 xpt_done(ccb); 386 return; 387 } 388 } else { 389 if (vpoio_reset_bus(&vpo->vpo_io)) { 390 ccb->ccb_h.status = CAM_REQ_CMP_ERR; 391 xpt_done(ccb); 392 return; 393 } 394 } 395 396 ccb->ccb_h.status = CAM_REQ_CMP; 397 xpt_done(ccb); 398 break; 399 } 400 case XPT_PATH_INQ: /* Path routing inquiry */ 401 { 402 struct ccb_pathinq *cpi = &ccb->cpi; 403 404 #ifdef VP0_DEBUG 405 kprintf("vpo%d: XPT_PATH_INQ request\n", vpo->vpo_unit); 406 #endif 407 cpi->version_num = 1; /* XXX??? */ 408 cpi->hba_inquiry = 0; 409 cpi->target_sprt = 0; 410 cpi->hba_misc = 0; 411 cpi->hba_eng_cnt = 0; 412 cpi->max_target = 7; 413 cpi->max_lun = 0; 414 cpi->initiator_id = VP0_INITIATOR; 415 cpi->bus_id = sim->bus_id; 416 cpi->base_transfer_speed = 93; 417 strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN); 418 strncpy(cpi->hba_vid, "Iomega", HBA_IDLEN); 419 strncpy(cpi->dev_name, sim->sim_name, DEV_IDLEN); 420 cpi->unit_number = sim->unit_number; 421 422 cpi->ccb_h.status = CAM_REQ_CMP; 423 xpt_done(ccb); 424 break; 425 } 426 default: 427 ccb->ccb_h.status = CAM_REQ_INVALID; 428 xpt_done(ccb); 429 break; 430 } 431 432 return; 433 } 434 435 static void 436 vpo_poll(struct cam_sim *sim) 437 { 438 /* The ZIP is actually always polled throw vpo_action() */ 439 return; 440 } 441 442 /* 443 * Always create a "vpo" device under ppbus. Use device_identify to 444 * create the static entry for any attached ppbus. 445 */ 446 static devclass_t vpo_devclass; 447 448 static device_method_t vpo_methods[] = { 449 /* device interface */ 450 DEVMETHOD(device_identify, bus_generic_identify), 451 DEVMETHOD(device_probe, vpo_probe), 452 DEVMETHOD(device_attach, vpo_attach), 453 { 0, 0 } 454 }; 455 456 static driver_t vpo_driver = { 457 "vpo", 458 vpo_methods, 459 sizeof(struct vpo_data), 460 }; 461 DRIVER_MODULE(vpo, ppbus, vpo_driver, vpo_devclass, 0, 0); 462 463