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