vpo.c revision 109623
1218585Sjkim/*- 2218585Sjkim * Copyright (c) 1997, 1998, 1999 Nicolas Souchu 3218585Sjkim * All rights reserved. 4218585Sjkim * 5218585Sjkim * Redistribution and use in source and binary forms, with or without 6218585Sjkim * modification, are permitted provided that the following conditions 7316303Sjkim * are met: 8316303Sjkim * 1. Redistributions of source code must retain the above copyright 9316303Sjkim * notice, this list of conditions and the following disclaimer. 10316303Sjkim * 2. Redistributions in binary form must reproduce the above copyright 11316303Sjkim * notice, this list of conditions and the following disclaimer in the 12218585Sjkim * documentation and/or other materials provided with the distribution. 13218585Sjkim * 14316303Sjkim * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15316303Sjkim * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16316303Sjkim * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17316303Sjkim * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18316303Sjkim * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19316303Sjkim * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20316303Sjkim * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21316303Sjkim * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22316303Sjkim * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23316303Sjkim * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24316303Sjkim * SUCH DAMAGE. 25316303Sjkim * 26316303Sjkim * $FreeBSD: head/sys/dev/ppbus/vpo.c 109623 2003-01-21 08:56:16Z alfred $ 27316303Sjkim */ 28316303Sjkim 29316303Sjkim#include <sys/param.h> 30316303Sjkim#include <sys/systm.h> 31316303Sjkim#include <sys/module.h> 32316303Sjkim#include <sys/bus.h> 33316303Sjkim#include <sys/malloc.h> 34316303Sjkim#include <sys/devicestat.h> /* for struct devstat */ 35316303Sjkim 36316303Sjkim 37316303Sjkim#include <cam/cam.h> 38316303Sjkim#include <cam/cam_ccb.h> 39316303Sjkim#include <cam/cam_sim.h> 40316303Sjkim#include <cam/cam_xpt_sim.h> 41316303Sjkim#include <cam/cam_debug.h> 42316303Sjkim#include <cam/cam_periph.h> 43316303Sjkim 44316303Sjkim#include <cam/scsi/scsi_all.h> 45316303Sjkim#include <cam/scsi/scsi_message.h> 46316303Sjkim#include <cam/scsi/scsi_da.h> 47316303Sjkim 48316303Sjkim#include <sys/kernel.h> 49316303Sjkim 50316303Sjkim#include "opt_vpo.h" 51316303Sjkim 52316303Sjkim#include <dev/ppbus/ppbconf.h> 53316303Sjkim#include <dev/ppbus/vpoio.h> 54316303Sjkim 55316303Sjkim#include "ppbus_if.h" 56316303Sjkim 57316303Sjkimstruct vpo_sense { 58316303Sjkim struct scsi_sense cmd; 59316303Sjkim unsigned int stat; 60316303Sjkim unsigned int count; 61316303Sjkim}; 62316303Sjkim 63316303Sjkimstruct vpo_data { 64316303Sjkim unsigned short vpo_unit; 65316303Sjkim 66316303Sjkim int vpo_stat; 67316303Sjkim int vpo_count; 68316303Sjkim int vpo_error; 69316303Sjkim 70316303Sjkim int vpo_isplus; 71316303Sjkim 72316303Sjkim struct cam_sim *sim; 73316303Sjkim 74316303Sjkim struct vpo_sense vpo_sense; 75316303Sjkim 76316303Sjkim struct vpoio_data vpo_io; /* interface to low level functions */ 77316303Sjkim}; 78316303Sjkim 79316303Sjkim#define DEVTOSOFTC(dev) \ 80316303Sjkim ((struct vpo_data *)device_get_softc(dev)) 81316303Sjkim 82316303Sjkim/* cam related functions */ 83316303Sjkimstatic void vpo_action(struct cam_sim *sim, union ccb *ccb); 84316303Sjkimstatic void vpo_poll(struct cam_sim *sim); 85316303Sjkimstatic void vpo_cam_rescan_callback(struct cam_periph *periph, 86316303Sjkim union ccb *ccb); 87316303Sjkimstatic void vpo_cam_rescan(struct vpo_data *vpo); 88316303Sjkim 89316303Sjkimstatic void 90316303Sjkimvpo_identify(driver_t *driver, device_t parent) 91316303Sjkim{ 92316303Sjkim 93316303Sjkim BUS_ADD_CHILD(parent, 0, "vpo", -1); 94316303Sjkim} 95316303Sjkim 96316303Sjkim/* 97316303Sjkim * vpo_probe() 98316303Sjkim */ 99316303Sjkimstatic int 100316303Sjkimvpo_probe(device_t dev) 101316303Sjkim{ 102316303Sjkim struct vpo_data *vpo; 103316303Sjkim int error; 104316303Sjkim 105316303Sjkim vpo = DEVTOSOFTC(dev); 106316303Sjkim bzero(vpo, sizeof(struct vpo_data)); 107316303Sjkim 108316303Sjkim /* vpo dependent initialisation */ 109316303Sjkim vpo->vpo_unit = device_get_unit(dev); 110316303Sjkim 111316303Sjkim /* low level probe */ 112316303Sjkim vpoio_set_unit(&vpo->vpo_io, vpo->vpo_unit); 113316303Sjkim 114316303Sjkim /* check ZIP before ZIP+ or imm_probe() will send controls to 115316303Sjkim * the printer or whatelse connected to the port */ 116316303Sjkim if ((error = vpoio_probe(dev, &vpo->vpo_io)) == 0) { 117316303Sjkim vpo->vpo_isplus = 0; 118316303Sjkim device_set_desc(dev, 119218585Sjkim "Iomega VPI0 Parallel to SCSI interface"); 120218585Sjkim } else if ((error = imm_probe(dev, &vpo->vpo_io)) == 0) { 121218585Sjkim vpo->vpo_isplus = 1; 122218585Sjkim device_set_desc(dev, 123218585Sjkim "Iomega Matchmaker Parallel to SCSI interface"); 124218585Sjkim } else { 125218585Sjkim return (error); 126218585Sjkim } 127218585Sjkim 128218585Sjkim return (0); 129218585Sjkim} 130218585Sjkim 131218585Sjkim/* 132218585Sjkim * vpo_attach() 133218585Sjkim */ 134316303Sjkimstatic int 135316303Sjkimvpo_attach(device_t dev) 136316303Sjkim{ 137316303Sjkim struct vpo_data *vpo = DEVTOSOFTC(dev); 138316303Sjkim struct cam_devq *devq; 139316303Sjkim int error; 140316303Sjkim 141316303Sjkim /* low level attachment */ 142316303Sjkim if (vpo->vpo_isplus) { 143316303Sjkim if ((error = imm_attach(&vpo->vpo_io))) 144316303Sjkim return (error); 145316303Sjkim } else { 146316303Sjkim if ((error = vpoio_attach(&vpo->vpo_io))) 147218585Sjkim return (error); 148218585Sjkim } 149218585Sjkim 150316303Sjkim /* 151218585Sjkim ** Now tell the generic SCSI layer 152218590Sjkim ** about our bus. 153220663Sjkim */ 154218585Sjkim devq = cam_simq_alloc(/*maxopenings*/1); 155218585Sjkim /* XXX What about low-level detach on error? */ 156218585Sjkim if (devq == NULL) 157218585Sjkim return (ENXIO); 158218585Sjkim 159218585Sjkim vpo->sim = cam_sim_alloc(vpo_action, vpo_poll, "vpo", vpo, 160218585Sjkim device_get_unit(dev), 161218585Sjkim /*untagged*/1, /*tagged*/0, devq); 162218585Sjkim if (vpo->sim == NULL) { 163218585Sjkim cam_simq_free(devq); 164218585Sjkim return (ENXIO); 165218585Sjkim } 166218585Sjkim 167218585Sjkim if (xpt_bus_register(vpo->sim, /*bus*/0) != CAM_SUCCESS) { 168218585Sjkim cam_sim_free(vpo->sim, /*free_devq*/TRUE); 169220663Sjkim return (ENXIO); 170218585Sjkim } 171220663Sjkim 172220663Sjkim /* all went ok */ 173220663Sjkim 174218585Sjkim vpo_cam_rescan(vpo); /* have CAM rescan the bus */ 175218585Sjkim 176218585Sjkim return (0); 177218585Sjkim} 178218585Sjkim 179220663Sjkimstatic void 180218585Sjkimvpo_cam_rescan_callback(struct cam_periph *periph, union ccb *ccb) 181220663Sjkim{ 182218585Sjkim free(ccb, M_TEMP); 183218585Sjkim} 184220663Sjkim 185218585Sjkimstatic void 186218585Sjkimvpo_cam_rescan(struct vpo_data *vpo) 187218585Sjkim{ 188220663Sjkim struct cam_path *path; 189218585Sjkim union ccb *ccb = malloc(sizeof(union ccb), M_TEMP, M_ZERO); 190220663Sjkim 191220663Sjkim if (xpt_create_path(&path, xpt_periph, cam_sim_path(vpo->sim), 0, 0) 192218585Sjkim != CAM_REQ_CMP) { 193220663Sjkim /* A failure is benign as the user can do a manual rescan */ 194218585Sjkim return; 195218585Sjkim } 196218585Sjkim 197218585Sjkim xpt_setup_ccb(&ccb->ccb_h, path, 5/*priority (low)*/); 198218585Sjkim ccb->ccb_h.func_code = XPT_SCAN_BUS; 199220663Sjkim ccb->ccb_h.cbfcnp = vpo_cam_rescan_callback; 200218585Sjkim ccb->crcn.flags = CAM_FLAG_NONE; 201220663Sjkim xpt_action(ccb); 202220663Sjkim 203220663Sjkim /* The scan is in progress now. */ 204220663Sjkim} 205218585Sjkim 206218585Sjkim/* 207220663Sjkim * vpo_intr() 208220663Sjkim */ 209220663Sjkimstatic void 210220663Sjkimvpo_intr(struct vpo_data *vpo, struct ccb_scsiio *csio) 211220663Sjkim{ 212220663Sjkim int errno; /* error in errno.h */ 213220663Sjkim int s; 214220663Sjkim#ifdef VP0_DEBUG 215220663Sjkim int i; 216220663Sjkim#endif 217220663Sjkim 218220663Sjkim s = splcam(); 219220663Sjkim 220218585Sjkim if (vpo->vpo_isplus) { 221220663Sjkim errno = imm_do_scsi(&vpo->vpo_io, VP0_INITIATOR, 222220663Sjkim csio->ccb_h.target_id, 223220663Sjkim (char *)&csio->cdb_io.cdb_bytes, csio->cdb_len, 224220663Sjkim (char *)csio->data_ptr, csio->dxfer_len, 225220663Sjkim &vpo->vpo_stat, &vpo->vpo_count, &vpo->vpo_error); 226220663Sjkim } else { 227220663Sjkim errno = vpoio_do_scsi(&vpo->vpo_io, VP0_INITIATOR, 228220663Sjkim csio->ccb_h.target_id, 229220663Sjkim (char *)&csio->cdb_io.cdb_bytes, csio->cdb_len, 230220663Sjkim (char *)csio->data_ptr, csio->dxfer_len, 231220663Sjkim &vpo->vpo_stat, &vpo->vpo_count, &vpo->vpo_error); 232220663Sjkim } 233218585Sjkim 234220663Sjkim#ifdef VP0_DEBUG 235250838Sjkim printf("vpo_do_scsi = %d, status = 0x%x, count = %d, vpo_error = %d\n", 236220663Sjkim errno, vpo->vpo_stat, vpo->vpo_count, vpo->vpo_error); 237220663Sjkim 238218585Sjkim /* dump of command */ 239220663Sjkim for (i=0; i<csio->cdb_len; i++) 240250838Sjkim printf("%x ", ((char *)&csio->cdb_io.cdb_bytes)[i]); 241220663Sjkim 242220663Sjkim printf("\n"); 243218585Sjkim#endif 244220663Sjkim 245250838Sjkim if (errno) { 246220663Sjkim /* connection to ppbus interrupted */ 247220663Sjkim csio->ccb_h.status = CAM_CMD_TIMEOUT; 248220663Sjkim goto error; 249220663Sjkim } 250250838Sjkim 251220663Sjkim /* if a timeout occured, no sense */ 252220663Sjkim if (vpo->vpo_error) { 253220663Sjkim if (vpo->vpo_error != VP0_ESELECT_TIMEOUT) 254233250Sjkim printf("vpo%d: VP0 error/timeout (%d)\n", 255220663Sjkim vpo->vpo_unit, vpo->vpo_error); 256218585Sjkim 257298714Sjkim csio->ccb_h.status = CAM_CMD_TIMEOUT; 258220663Sjkim goto error; 259220663Sjkim } 260218585Sjkim 261220663Sjkim /* check scsi status */ 262250838Sjkim if (vpo->vpo_stat != SCSI_STATUS_OK) { 263220663Sjkim csio->scsi_status = vpo->vpo_stat; 264218585Sjkim 265220663Sjkim /* check if we have to sense the drive */ 266233250Sjkim if ((vpo->vpo_stat & SCSI_STATUS_CHECK_COND) != 0) { 267218585Sjkim 268218585Sjkim vpo->vpo_sense.cmd.opcode = REQUEST_SENSE; 269298714Sjkim vpo->vpo_sense.cmd.length = csio->sense_len; 270220663Sjkim vpo->vpo_sense.cmd.control = 0; 271220663Sjkim 272218585Sjkim if (vpo->vpo_isplus) { 273220663Sjkim errno = imm_do_scsi(&vpo->vpo_io, VP0_INITIATOR, 274220663Sjkim csio->ccb_h.target_id, 275220663Sjkim (char *)&vpo->vpo_sense.cmd, 276218585Sjkim sizeof(vpo->vpo_sense.cmd), 277220663Sjkim (char *)&csio->sense_data, csio->sense_len, 278250838Sjkim &vpo->vpo_sense.stat, &vpo->vpo_sense.count, 279220663Sjkim &vpo->vpo_error); 280220663Sjkim } else { 281218585Sjkim errno = vpoio_do_scsi(&vpo->vpo_io, VP0_INITIATOR, 282220663Sjkim csio->ccb_h.target_id, 283250838Sjkim (char *)&vpo->vpo_sense.cmd, 284220663Sjkim sizeof(vpo->vpo_sense.cmd), 285220663Sjkim (char *)&csio->sense_data, csio->sense_len, 286218585Sjkim &vpo->vpo_sense.stat, &vpo->vpo_sense.count, 287220663Sjkim &vpo->vpo_error); 288250838Sjkim } 289220663Sjkim 290220663Sjkim 291218585Sjkim#ifdef VP0_DEBUG 292220663Sjkim printf("(sense) vpo_do_scsi = %d, status = 0x%x, count = %d, vpo_error = %d\n", 293250838Sjkim errno, vpo->vpo_sense.stat, vpo->vpo_sense.count, vpo->vpo_error); 294220663Sjkim#endif 295220663Sjkim 296218585Sjkim /* check sense return status */ 297220663Sjkim if (errno == 0 && vpo->vpo_sense.stat == SCSI_STATUS_OK) { 298250838Sjkim /* sense ok */ 299220663Sjkim csio->ccb_h.status = CAM_AUTOSNS_VALID | CAM_SCSI_STATUS_ERROR; 300220663Sjkim csio->sense_resid = csio->sense_len - vpo->vpo_sense.count; 301218585Sjkim 302220663Sjkim#ifdef VP0_DEBUG 303250838Sjkim /* dump of sense info */ 304220663Sjkim printf("(sense) "); 305220663Sjkim for (i=0; i<vpo->vpo_sense.count; i++) 306218585Sjkim printf("%x ", ((char *)&csio->sense_data)[i]); 307220663Sjkim printf("\n"); 308250838Sjkim#endif 309220663Sjkim 310220663Sjkim } else { 311218585Sjkim /* sense failed */ 312220663Sjkim csio->ccb_h.status = CAM_AUTOSENSE_FAIL; 313250838Sjkim } 314233250Sjkim } else { 315220663Sjkim /* no sense */ 316218585Sjkim csio->ccb_h.status = CAM_SCSI_STATUS_ERROR; 317220663Sjkim } 318250838Sjkim 319220663Sjkim goto error; 320220663Sjkim } 321218585Sjkim 322220663Sjkim csio->resid = csio->dxfer_len - vpo->vpo_count; 323250838Sjkim csio->ccb_h.status = CAM_REQ_CMP; 324220663Sjkim 325220663Sjkimerror: 326218585Sjkim splx(s); 327220663Sjkim 328250838Sjkim return; 329220663Sjkim} 330220663Sjkim 331218585Sjkimstatic void 332220663Sjkimvpo_action(struct cam_sim *sim, union ccb *ccb) 333250838Sjkim{ 334220663Sjkim 335220663Sjkim struct vpo_data *vpo = (struct vpo_data *)sim->softc; 336220663Sjkim 337220663Sjkim switch (ccb->ccb_h.func_code) { 338250838Sjkim case XPT_SCSI_IO: 339220663Sjkim { 340220663Sjkim struct ccb_scsiio *csio; 341220663Sjkim 342220663Sjkim csio = &ccb->csio; 343250838Sjkim 344220663Sjkim#ifdef VP0_DEBUG 345220663Sjkim printf("vpo%d: XPT_SCSI_IO (0x%x) request\n", 346220663Sjkim vpo->vpo_unit, csio->cdb_io.cdb_bytes[0]); 347220663Sjkim#endif 348220663Sjkim 349220663Sjkim vpo_intr(vpo, csio); 350220663Sjkim 351220663Sjkim xpt_done(ccb); 352233250Sjkim 353220663Sjkim break; 354218585Sjkim } 355218585Sjkim case XPT_CALC_GEOMETRY: 356220663Sjkim { 357233250Sjkim struct ccb_calc_geometry *ccg; 358220663Sjkim 359220663Sjkim ccg = &ccb->ccg; 360220663Sjkim 361220663Sjkim#ifdef VP0_DEBUG 362220663Sjkim printf("vpo%d: XPT_CALC_GEOMETRY (bs=%d,vs=%d,c=%d,h=%d,spt=%d) request\n", 363220663Sjkim vpo->vpo_unit, 364218585Sjkim ccg->block_size, 365218585Sjkim ccg->volume_size, 366218585Sjkim ccg->cylinders, 367218585Sjkim ccg->heads, 368218585Sjkim ccg->secs_per_track); 369220663Sjkim#endif 370218585Sjkim 371220663Sjkim ccg->heads = 64; 372218585Sjkim ccg->secs_per_track = 32; 373220663Sjkim ccg->cylinders = ccg->volume_size / 374218585Sjkim (ccg->heads * ccg->secs_per_track); 375220663Sjkim 376218585Sjkim ccb->ccb_h.status = CAM_REQ_CMP; 377218585Sjkim xpt_done(ccb); 378218585Sjkim break; 379220663Sjkim } 380220663Sjkim case XPT_RESET_BUS: /* Reset the specified SCSI bus */ 381220663Sjkim { 382218585Sjkim 383218585Sjkim#ifdef VP0_DEBUG 384218585Sjkim printf("vpo%d: XPT_RESET_BUS request\n", vpo->vpo_unit); 385218585Sjkim#endif 386220663Sjkim 387218585Sjkim if (vpo->vpo_isplus) { 388218585Sjkim if (imm_reset_bus(&vpo->vpo_io)) { 389218585Sjkim ccb->ccb_h.status = CAM_REQ_CMP_ERR; 390220663Sjkim xpt_done(ccb); 391218585Sjkim return; 392220663Sjkim } 393220663Sjkim } else { 394218585Sjkim if (vpoio_reset_bus(&vpo->vpo_io)) { 395220663Sjkim ccb->ccb_h.status = CAM_REQ_CMP_ERR; 396220663Sjkim xpt_done(ccb); 397220663Sjkim return; 398220663Sjkim } 399220663Sjkim } 400220663Sjkim 401218585Sjkim ccb->ccb_h.status = CAM_REQ_CMP; 402218585Sjkim xpt_done(ccb); 403220663Sjkim break; 404218585Sjkim } 405220663Sjkim case XPT_PATH_INQ: /* Path routing inquiry */ 406220663Sjkim { 407218585Sjkim struct ccb_pathinq *cpi = &ccb->cpi; 408220663Sjkim 409218585Sjkim#ifdef VP0_DEBUG 410218585Sjkim printf("vpo%d: XPT_PATH_INQ request\n", vpo->vpo_unit); 411218585Sjkim#endif 412218585Sjkim cpi->version_num = 1; /* XXX??? */ 413218585Sjkim cpi->hba_inquiry = 0; 414218585Sjkim cpi->target_sprt = 0; 415218585Sjkim cpi->hba_misc = 0; 416218585Sjkim cpi->hba_eng_cnt = 0; 417218585Sjkim cpi->max_target = 7; 418218585Sjkim cpi->max_lun = 0; 419218585Sjkim cpi->initiator_id = VP0_INITIATOR; 420218585Sjkim cpi->bus_id = sim->bus_id; 421218585Sjkim cpi->base_transfer_speed = 93; 422218585Sjkim strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN); 423218585Sjkim strncpy(cpi->hba_vid, "Iomega", HBA_IDLEN); 424218585Sjkim strncpy(cpi->dev_name, sim->sim_name, DEV_IDLEN); 425218585Sjkim cpi->unit_number = sim->unit_number; 426218585Sjkim 427218585Sjkim cpi->ccb_h.status = CAM_REQ_CMP; 428218585Sjkim xpt_done(ccb); 429218585Sjkim break; 430218585Sjkim } 431218585Sjkim default: 432218585Sjkim ccb->ccb_h.status = CAM_REQ_INVALID; 433218585Sjkim xpt_done(ccb); 434218585Sjkim break; 435218585Sjkim } 436218585Sjkim 437218585Sjkim return; 438218585Sjkim} 439218585Sjkim 440218585Sjkimstatic void 441218585Sjkimvpo_poll(struct cam_sim *sim) 442218585Sjkim{ 443218585Sjkim /* The ZIP is actually always polled throw vpo_action() */ 444218585Sjkim return; 445218585Sjkim} 446218585Sjkim 447218585Sjkimstatic devclass_t vpo_devclass; 448218585Sjkim 449218585Sjkimstatic device_method_t vpo_methods[] = { 450218585Sjkim /* device interface */ 451218585Sjkim DEVMETHOD(device_identify, vpo_identify), 452218585Sjkim DEVMETHOD(device_probe, vpo_probe), 453218585Sjkim DEVMETHOD(device_attach, vpo_attach), 454218585Sjkim 455218585Sjkim { 0, 0 } 456218585Sjkim}; 457218585Sjkim 458218585Sjkimstatic driver_t vpo_driver = { 459218585Sjkim "vpo", 460218585Sjkim vpo_methods, 461218585Sjkim sizeof(struct vpo_data), 462218585Sjkim}; 463218585SjkimDRIVER_MODULE(vpo, ppbus, vpo_driver, vpo_devclass, 0, 0); 464218585Sjkim