46f3ff7986
- ppbus now supports PLIP via the if_plip driver - ieee1284 infrastructure added, including parallel-port PnP - port microsequencer added, for scripting the sort of port I/O that is common with parallel devices without endless calls up and down through the driver structure. - improved bus ownership behaviour among the ppbus-using drivers. - improved I/O chipset feature detection The vpo driver is now implemented using the microsequencer, leading to some performance improvements as well as providing an extensive example of its use. Reviewed by: msmith Submitted by: Nicolas Souchu <Nicolas.Souchu@prism.uvsq.fr>
342 lines
7.2 KiB
C
342 lines
7.2 KiB
C
/*-
|
|
* Copyright (c) 1997, 1998 Nicolas Souchu
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*
|
|
* $Id: vpo.c,v 1.4 1997/09/01 00:51:52 bde Exp $
|
|
*
|
|
*/
|
|
|
|
#ifdef KERNEL
|
|
#include <sys/param.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/malloc.h>
|
|
#include <sys/buf.h>
|
|
|
|
#include <machine/clock.h>
|
|
|
|
#endif /* KERNEL */
|
|
#include <scsi/scsi_disk.h>
|
|
#include <scsi/scsiconf.h>
|
|
|
|
#ifdef KERNEL
|
|
#include <sys/kernel.h>
|
|
#endif /*KERNEL */
|
|
|
|
#include <dev/ppbus/ppbconf.h>
|
|
#include <dev/ppbus/vpoio.h>
|
|
|
|
#define VP0_BUFFER_SIZE 0x12000
|
|
|
|
struct vpo_sense {
|
|
struct scsi_sense cmd;
|
|
unsigned int stat;
|
|
unsigned int count;
|
|
};
|
|
|
|
struct vpo_data {
|
|
unsigned short vpo_unit;
|
|
|
|
int vpo_stat;
|
|
int vpo_count;
|
|
int vpo_error;
|
|
|
|
struct ppb_status vpo_status;
|
|
struct vpo_sense vpo_sense;
|
|
|
|
unsigned char vpo_buffer[VP0_BUFFER_SIZE];
|
|
|
|
struct vpoio_data vpo_io; /* interface to low level functions */
|
|
|
|
struct scsi_link sc_link;
|
|
};
|
|
|
|
|
|
static int32_t vpo_scsi_cmd(struct scsi_xfer *);
|
|
static void vpominphys(struct buf *);
|
|
static u_int32_t vpo_adapter_info(int);
|
|
|
|
static int nvpo = 0;
|
|
#define MAXVP0 8 /* XXX not much better! */
|
|
static struct vpo_data *vpodata[MAXVP0];
|
|
|
|
static struct scsi_adapter vpo_switch =
|
|
{
|
|
vpo_scsi_cmd,
|
|
vpominphys,
|
|
0,
|
|
0,
|
|
vpo_adapter_info,
|
|
"vpo",
|
|
{ 0, 0 }
|
|
};
|
|
|
|
/*
|
|
* The below structure is so we have a default dev struct
|
|
* for out link struct.
|
|
*/
|
|
static struct scsi_device vpo_dev =
|
|
{
|
|
NULL, /* Use default error handler */
|
|
NULL, /* have a queue, served by this */
|
|
NULL, /* have no async handler */
|
|
NULL, /* Use default 'done' routine */
|
|
"vpo",
|
|
0,
|
|
{ 0, 0 }
|
|
};
|
|
|
|
|
|
/*
|
|
* Make ourselves visible as a ppbus driver
|
|
*/
|
|
|
|
static struct ppb_device *vpoprobe(struct ppb_data *ppb);
|
|
static int vpoattach(struct ppb_device *dev);
|
|
|
|
static struct ppb_driver vpodriver = {
|
|
vpoprobe, vpoattach, "vpo"
|
|
};
|
|
DATA_SET(ppbdriver_set, vpodriver);
|
|
|
|
|
|
static u_int32_t
|
|
vpo_adapter_info(int unit)
|
|
{
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* vpoprobe()
|
|
*
|
|
* Called by ppb_attachdevs().
|
|
*/
|
|
static struct ppb_device *
|
|
vpoprobe(struct ppb_data *ppb)
|
|
{
|
|
struct vpo_data *vpo;
|
|
struct ppb_device *dev;
|
|
|
|
if (nvpo >= MAXVP0) {
|
|
printf("vpo: Too many devices (max %d)\n", MAXVP0);
|
|
return(NULL);
|
|
}
|
|
|
|
vpo = (struct vpo_data *)malloc(sizeof(struct vpo_data),
|
|
M_DEVBUF, M_NOWAIT);
|
|
if (!vpo) {
|
|
printf("vpo: cannot malloc!\n");
|
|
return(NULL);
|
|
}
|
|
bzero(vpo, sizeof(struct vpo_data));
|
|
|
|
vpodata[nvpo] = vpo;
|
|
|
|
/* vpo dependent initialisation */
|
|
vpo->vpo_unit = nvpo;
|
|
|
|
/* ok, go to next device on next probe */
|
|
nvpo ++;
|
|
|
|
/* low level probe */
|
|
vpoio_set_unit(&vpo->vpo_io, vpo->vpo_unit);
|
|
|
|
if (!(dev = vpoio_probe(ppb, &vpo->vpo_io))) {
|
|
free(vpo, M_DEVBUF);
|
|
return (NULL);
|
|
}
|
|
|
|
return (dev);
|
|
}
|
|
|
|
/*
|
|
* vpoattach()
|
|
*
|
|
* Called by ppb_attachdevs().
|
|
*/
|
|
static int
|
|
vpoattach(struct ppb_device *dev)
|
|
{
|
|
|
|
struct scsibus_data *scbus;
|
|
struct vpo_data *vpo = vpodata[dev->id_unit];
|
|
|
|
/* low level attachment */
|
|
if (!vpoio_attach(&vpo->vpo_io))
|
|
return (0);
|
|
|
|
vpo->sc_link.adapter_unit = vpo->vpo_unit;
|
|
vpo->sc_link.adapter_targ = VP0_INITIATOR;
|
|
vpo->sc_link.adapter = &vpo_switch;
|
|
vpo->sc_link.device = &vpo_dev;
|
|
vpo->sc_link.opennings = VP0_OPENNINGS;
|
|
|
|
/*
|
|
* Prepare the scsibus_data area for the upperlevel
|
|
* scsi code.
|
|
*/
|
|
scbus = scsi_alloc_bus();
|
|
if(!scbus)
|
|
return (0);
|
|
scbus->adapter_link = &vpo->sc_link;
|
|
|
|
/* all went ok */
|
|
printf("vpo%d: <Iomega PPA-3/VPI0 SCSI controller>\n", dev->id_unit);
|
|
|
|
scsi_attachdevs(scbus);
|
|
|
|
return (1);
|
|
}
|
|
|
|
static void
|
|
vpominphys(struct buf *bp)
|
|
{
|
|
|
|
if (bp->b_bcount > VP0_BUFFER_SIZE)
|
|
bp->b_bcount = VP0_BUFFER_SIZE;
|
|
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* vpo_intr()
|
|
*/
|
|
static void
|
|
vpo_intr(struct vpo_data *vpo, struct scsi_xfer *xs)
|
|
{
|
|
|
|
int errno; /* error in errno.h */
|
|
|
|
if (xs->datalen && !(xs->flags & SCSI_DATA_IN))
|
|
bcopy(xs->data, vpo->vpo_buffer, xs->datalen);
|
|
|
|
errno = vpoio_do_scsi(&vpo->vpo_io, VP0_INITIATOR,
|
|
xs->sc_link->target, (char *)xs->cmd, xs->cmdlen,
|
|
vpo->vpo_buffer, xs->datalen, &vpo->vpo_stat, &vpo->vpo_count,
|
|
&vpo->vpo_error);
|
|
|
|
#ifdef VP0_DEBUG
|
|
printf("vpo_do_scsi = %d, status = 0x%x, count = %d, vpo_error = %d\n",
|
|
errno, vpo->vpo_stat, vpo->vpo_count, vpo->vpo_error);
|
|
#endif
|
|
|
|
if (errno) {
|
|
#ifdef VP0_WARNING
|
|
log(LOG_WARNING, "vpo%d: errno = %d\n", vpo->vpo_unit, errno);
|
|
#endif
|
|
/* connection to ppbus interrupted */
|
|
xs->error = XS_DRIVER_STUFFUP;
|
|
goto error;
|
|
}
|
|
|
|
/* if a timeout occured, no sense */
|
|
if (vpo->vpo_error) {
|
|
xs->error = XS_TIMEOUT;
|
|
goto error;
|
|
}
|
|
|
|
#define RESERVED_BITS_MASK 0x3e /* 00111110b */
|
|
#define NO_SENSE 0x0
|
|
#define CHECK_CONDITION 0x02
|
|
|
|
switch (vpo->vpo_stat & RESERVED_BITS_MASK) {
|
|
case NO_SENSE:
|
|
break;
|
|
|
|
case CHECK_CONDITION:
|
|
vpo->vpo_sense.cmd.op_code = REQUEST_SENSE;
|
|
vpo->vpo_sense.cmd.length = sizeof(xs->sense);
|
|
vpo->vpo_sense.cmd.control = 0;
|
|
|
|
errno = vpoio_do_scsi(&vpo->vpo_io, VP0_INITIATOR,
|
|
xs->sc_link->target, (char *)&vpo->vpo_sense.cmd,
|
|
sizeof(vpo->vpo_sense.cmd),
|
|
(char *)&xs->sense, sizeof(xs->sense),
|
|
&vpo->vpo_sense.stat, &vpo->vpo_sense.count,
|
|
&vpo->vpo_error);
|
|
|
|
if (errno)
|
|
/* connection to ppbus interrupted */
|
|
xs->error = XS_DRIVER_STUFFUP;
|
|
else
|
|
xs->error = XS_SENSE;
|
|
|
|
goto error;
|
|
|
|
default: /* BUSY or RESERVATION_CONFLICT */
|
|
xs->error = XS_TIMEOUT;
|
|
goto error;
|
|
}
|
|
|
|
if (xs->datalen && (xs->flags & SCSI_DATA_IN))
|
|
bcopy(vpo->vpo_buffer, xs->data, xs->datalen);
|
|
|
|
done:
|
|
xs->resid = 0;
|
|
xs->error = XS_NOERROR;
|
|
|
|
error:
|
|
xs->flags |= ITSDONE;
|
|
scsi_done(xs);
|
|
|
|
return;
|
|
}
|
|
|
|
static int32_t
|
|
vpo_scsi_cmd(struct scsi_xfer *xs)
|
|
{
|
|
|
|
int s;
|
|
|
|
if (xs->sc_link->lun > 0) {
|
|
xs->error = XS_DRIVER_STUFFUP;
|
|
return TRY_AGAIN_LATER;
|
|
}
|
|
|
|
if (xs->flags & SCSI_DATA_UIO) {
|
|
printf("UIO not supported by vpo_driver !\n");
|
|
xs->error = XS_DRIVER_STUFFUP;
|
|
return TRY_AGAIN_LATER;
|
|
}
|
|
|
|
#ifdef VP0_DEBUG
|
|
printf("vpo_scsi_cmd(): xs->flags = 0x%x, "\
|
|
"xs->data = 0x%x, xs->datalen = %d\ncommand : %*D\n",
|
|
xs->flags, xs->data, xs->datalen,
|
|
xs->cmdlen, xs->cmd, " " );
|
|
#endif
|
|
|
|
if (xs->flags & SCSI_NOMASK) {
|
|
vpo_intr(vpodata[xs->sc_link->adapter_unit], xs);
|
|
return COMPLETE;
|
|
}
|
|
|
|
s = splbio();
|
|
|
|
vpo_intr(vpodata[xs->sc_link->adapter_unit], xs);
|
|
|
|
splx(s);
|
|
return SUCCESSFULLY_QUEUED;
|
|
}
|