immio.c revision 67164
1169689Skan/*-
2169689Skan * Copyright (c) 1998, 1999 Nicolas Souchu
3169689Skan * All rights reserved.
4169689Skan *
5169689Skan * Redistribution and use in source and binary forms, with or without
6169689Skan * modification, are permitted provided that the following conditions
7169689Skan * are met:
8169689Skan * 1. Redistributions of source code must retain the above copyright
9169689Skan *    notice, this list of conditions and the following disclaimer.
10169689Skan * 2. Redistributions in binary form must reproduce the above copyright
11169689Skan *    notice, this list of conditions and the following disclaimer in the
12169689Skan *    documentation and/or other materials provided with the distribution.
13169689Skan *
14169689Skan * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15169689Skan * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16169689Skan * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17169689Skan * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18169689Skan * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19169689Skan * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20169689Skan * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21169689Skan * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22169689Skan * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23169689Skan * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24169689Skan * SUCH DAMAGE.
25169689Skan *
26169689Skan * $FreeBSD: head/sys/dev/ppbus/immio.c 67164 2000-10-15 14:19:01Z phk $
27169689Skan *
28169689Skan */
29169689Skan
30169689Skan/*
31169689Skan * Iomega ZIP+ Matchmaker Parallel Port Interface driver
32169689Skan *
33169689Skan * Thanks to David Campbell work on the Linux driver and the Iomega specs
34169689Skan * Thanks to Thiebault Moeglin for the drive
35169689Skan */
36169689Skan#ifdef _KERNEL
37169689Skan#include <sys/param.h>
38169689Skan#include <sys/systm.h>
39169689Skan#include <sys/module.h>
40169689Skan#include <sys/bus.h>
41169689Skan#include <sys/malloc.h>
42169689Skan
43169689Skan
44169689Skan#endif	/* _KERNEL */
45169689Skan
46169689Skan#ifdef	_KERNEL
47169689Skan#endif /* _KERNEL */
48169689Skan
49169689Skan#include "opt_vpo.h"
50169689Skan
51169689Skan#include <dev/ppbus/ppbio.h>
52169689Skan#include <dev/ppbus/ppbconf.h>
53169689Skan#include <dev/ppbus/ppb_msq.h>
54169689Skan#include <dev/ppbus/vpoio.h>
55169689Skan#include <dev/ppbus/ppb_1284.h>
56169689Skan
57169689Skan#include "ppbus_if.h"
58169689Skan
59169689Skan#define VP0_SELTMO		5000	/* select timeout */
60169689Skan#define VP0_FAST_SPINTMO	500000	/* wait status timeout */
61169689Skan#define VP0_LOW_SPINTMO		5000000	/* wait status timeout */
62169689Skan
63169689Skan#define VP0_SECTOR_SIZE	512
64169689Skan
65169689Skan/*
66169689Skan * Microcode to execute very fast I/O sequences at the lowest bus level.
67169689Skan */
68169689Skan
69169689Skan#define SELECT_TARGET		MS_PARAM(6, 1, MS_TYP_CHA)
70169689Skan
71169689Skan#define DECLARE_SELECT_MICROSEQUENCE					\
72169689Skanstruct ppb_microseq select_microseq[] = {				\
73169689Skan	MS_CASS(0xc),							\
74169689Skan	/* first, check there is nothing holding onto the bus */	\
75169689Skan	MS_SET(VP0_SELTMO),						\
76169689Skan/* _loop: */								\
77169689Skan	MS_BRCLEAR(0x8, 2 /* _ready */),				\
78169689Skan	MS_DBRA(-2 /* _loop */),					\
79169689Skan	MS_RET(2),			/* bus busy */			\
80169689Skan/* _ready: */								\
81169689Skan	MS_CASS(0x4),							\
82169689Skan	MS_DASS(MS_UNKNOWN /* 0x80 | 1 << target */),			\
83169689Skan	MS_DELAY(1),							\
84169689Skan	MS_CASS(0xc),							\
85169689Skan	MS_CASS(0xd),							\
86169689Skan	/* now, wait until the drive is ready */			\
87169689Skan	MS_SET(VP0_SELTMO),						\
88169689Skan/* loop: */								\
89169689Skan	MS_BRSET(0x8, 3 /* ready */),					\
90169689Skan	MS_DBRA(-2 /* loop */),						\
91169689Skan/* error: */								\
92169689Skan	MS_CASS(0xc),							\
93169689Skan	MS_RET(VP0_ESELECT_TIMEOUT),					\
94169689Skan/* ready: */								\
95169689Skan	MS_CASS(0xc),							\
96169689Skan	MS_RET(0)							\
97169689Skan}
98169689Skan
99169689Skanstatic struct ppb_microseq transfer_epilog[] = {
100169689Skan	MS_CASS(0x4),
101169689Skan	MS_CASS(0xc),
102169689Skan	MS_CASS(0xe),
103169689Skan	MS_CASS(0x4),
104169689Skan	MS_RET(0)
105169689Skan};
106169689Skan
107169689Skan#define CPP_S1		MS_PARAM(10, 2, MS_TYP_PTR)
108169689Skan#define CPP_S2		MS_PARAM(13, 2, MS_TYP_PTR)
109169689Skan#define CPP_S3		MS_PARAM(16, 2, MS_TYP_PTR)
110169689Skan#define CPP_PARAM	MS_PARAM(17, 1, MS_TYP_CHA)
111169689Skan
112169689Skan#define DECLARE_CPP_MICROSEQ \
113169689Skanstruct ppb_microseq cpp_microseq[] = {					\
114169689Skan	MS_CASS(0x0c), MS_DELAY(2),					\
115169689Skan	MS_DASS(0xaa), MS_DELAY(10),					\
116169689Skan	MS_DASS(0x55), MS_DELAY(10),					\
117169689Skan	MS_DASS(0x00), MS_DELAY(10),					\
118169689Skan	MS_DASS(0xff), MS_DELAY(10),					\
119169689Skan	MS_RFETCH(MS_REG_STR, 0xb8, MS_UNKNOWN /* &s1 */),		\
120169689Skan	MS_DASS(0x87), MS_DELAY(10),					\
121169689Skan	MS_RFETCH(MS_REG_STR, 0xb8, MS_UNKNOWN /* &s2 */),		\
122169689Skan	MS_DASS(0x78), MS_DELAY(10),					\
123169689Skan	MS_RFETCH(MS_REG_STR, 0x38, MS_UNKNOWN /* &s3 */),		\
124169689Skan	MS_DASS(MS_UNKNOWN /* param */),				\
125169689Skan	MS_DELAY(2),							\
126169689Skan	MS_CASS(0x0c), MS_DELAY(10),					\
127169689Skan	MS_CASS(0x0d), MS_DELAY(2),					\
128169689Skan	MS_CASS(0x0c), MS_DELAY(10),					\
129169689Skan	MS_DASS(0xff), MS_DELAY(10),					\
130169689Skan	MS_RET(0)							\
131169689Skan}
132169689Skan
133169689Skan#define NEGOCIATED_MODE		MS_PARAM(2, 1, MS_TYP_CHA)
134169689Skan
135169689Skan#define DECLARE_NEGOCIATE_MICROSEQ \
136169689Skanstatic struct ppb_microseq negociate_microseq[] = { 			\
137169689Skan	MS_CASS(0x4),							\
138169689Skan	MS_DELAY(5),							\
139169689Skan	MS_DASS(MS_UNKNOWN /* mode */),					\
140169689Skan	MS_DELAY(100),							\
141169689Skan	MS_CASS(0x6),							\
142169689Skan	MS_DELAY(5),							\
143169689Skan	MS_BRSET(0x20, 5 /* continue */),				\
144169689Skan	MS_DELAY(5),							\
145169689Skan	MS_CASS(0x7),							\
146169689Skan	MS_DELAY(5),							\
147169689Skan	MS_CASS(0x6),							\
148169689Skan	MS_RET(VP0_ENEGOCIATE),						\
149169689Skan/* continue: */								\
150169689Skan	MS_DELAY(5),							\
151169689Skan	MS_CASS(0x7),							\
152169689Skan	MS_DELAY(5),							\
153	MS_CASS(0x6),							\
154	MS_RET(0)							\
155}
156
157static struct ppb_microseq reset_microseq[] = {
158	MS_CASS(0x04),
159	MS_DASS(0x40),
160	MS_DELAY(1),
161	MS_CASS(0x0c),
162	MS_CASS(0x0d),
163	MS_DELAY(50),
164	MS_CASS(0x0c),
165	MS_CASS(0x04),
166	MS_RET(0)
167};
168
169/*
170 * nibble_inbyte_hook()
171 *
172 * Formats high and low nibble into a character
173 */
174static int
175nibble_inbyte_hook (void *p, char *ptr)
176{
177	struct vpo_nibble *s = (struct vpo_nibble *)p;
178
179	/* increment the buffer pointer */
180	*ptr = ((s->l >> 4) & 0x0f) + (s->h & 0xf0);
181
182	return (0);
183}
184
185/*
186 * Macro used to initialize each vpoio_data structure during
187 * low level attachment
188 *
189 * XXX should be converted to ppb_MS_init_msq()
190 */
191#define INIT_NIBBLE_INBYTE_SUBMICROSEQ(vpo) {		    	\
192	(vpo)->vpo_nibble_inbyte_msq[6].arg[2].p =		\
193			(void *)&(vpo)->vpo_nibble.h;		\
194	(vpo)->vpo_nibble_inbyte_msq[3].arg[2].p =		\
195			(void *)&(vpo)->vpo_nibble.l;		\
196	(vpo)->vpo_nibble_inbyte_msq[9].arg[0].f =		\
197			nibble_inbyte_hook;			\
198	(vpo)->vpo_nibble_inbyte_msq[9].arg[1].p =		\
199			(void *)&(vpo)->vpo_nibble;		\
200}
201
202/*
203 * This is the sub-microseqence for MS_GET in NIBBLE mode
204 * Retrieve the two nibbles and call the C function to generate the character
205 * and store it in the buffer (see nibble_inbyte_hook())
206 */
207static struct ppb_microseq nibble_inbyte_submicroseq[] = {
208	  MS_CASS(0x4),
209
210/* loop: */
211	  MS_CASS(0x6),
212	  MS_DELAY(1),
213	  MS_RFETCH(MS_REG_STR, MS_FETCH_ALL, MS_UNKNOWN /* low nibble */),
214	  MS_CASS(0x5),
215	  MS_DELAY(1),
216	  MS_RFETCH(MS_REG_STR, MS_FETCH_ALL, MS_UNKNOWN /* high nibble */),
217	  MS_CASS(0x4),
218	  MS_DELAY(1),
219
220	  /* do a C call to format the received nibbles */
221	  MS_C_CALL(MS_UNKNOWN /* C hook */, MS_UNKNOWN /* param */),
222	  MS_DBRA(-7 /* loop */),
223	  MS_RET(0)
224};
225
226/*
227 * This is the sub-microseqence for MS_GET in PS2 mode
228 */
229static struct ppb_microseq ps2_inbyte_submicroseq[] = {
230	  MS_CASS(0x4),
231
232/* loop: */
233	  MS_CASS(PCD | 0x6),
234	  MS_RFETCH_P(1, MS_REG_DTR, MS_FETCH_ALL),
235	  MS_CASS(PCD | 0x5),
236	  MS_DBRA(-4 /* loop */),
237
238	  MS_RET(0)
239};
240
241/*
242 * This is the sub-microsequence for MS_PUT in both NIBBLE and PS2 modes
243 */
244static struct ppb_microseq spp_outbyte_submicroseq[] = {
245	  MS_CASS(0x4),
246
247/* loop: */
248	  MS_RASSERT_P(1, MS_REG_DTR),
249	  MS_CASS(0x5),
250	  MS_DBRA(0),				/* decrement counter */
251	  MS_RASSERT_P(1, MS_REG_DTR),
252	  MS_CASS(0x0),
253	  MS_DBRA(-6 /* loop */),
254
255	  /* return from the put call */
256	  MS_CASS(0x4),
257	  MS_RET(0)
258};
259
260/* EPP 1.7 microsequences, ptr and len set at runtime */
261static struct ppb_microseq epp17_outstr[] = {
262	  MS_CASS(0x4),
263	  MS_RASSERT_P(MS_ACCUM, MS_REG_EPP_D),
264	  MS_CASS(0xc),
265	  MS_RET(0),
266};
267
268static struct ppb_microseq epp17_instr[] = {
269	  MS_CASS(PCD | 0x4),
270	  MS_RFETCH_P(MS_ACCUM, MS_REG_EPP_D, MS_FETCH_ALL),
271	  MS_CASS(PCD | 0xc),
272	  MS_RET(0),
273};
274
275static int
276imm_disconnect(struct vpoio_data *vpo, int *connected, int release_bus)
277{
278	DECLARE_CPP_MICROSEQ;
279
280	device_t ppbus = device_get_parent(vpo->vpo_dev);
281	char s1, s2, s3;
282	int ret;
283
284	/* all should be ok */
285	if (connected)
286		*connected = 0;
287
288	ppb_MS_init_msq(cpp_microseq, 4, CPP_S1, (void *)&s1,
289			CPP_S2, (void *)&s2, CPP_S3, (void *)&s3,
290			CPP_PARAM, 0x30);
291
292	ppb_MS_microseq(ppbus, vpo->vpo_dev, cpp_microseq, &ret);
293
294	if ((s1 != (char)0xb8 || s2 != (char)0x18 || s3 != (char)0x38)) {
295		if (bootverbose)
296			printf("imm%d: (disconnect) s1=0x%x s2=0x%x, s3=0x%x\n",
297				vpo->vpo_unit, s1 & 0xff, s2 & 0xff, s3 & 0xff);
298		if (connected)
299			*connected = VP0_ECONNECT;
300	}
301
302	if (release_bus)
303		return (ppb_release_bus(ppbus, vpo->vpo_dev));
304	else
305		return (0);
306}
307
308/*
309 * how	: PPB_WAIT or PPB_DONTWAIT
310 */
311static int
312imm_connect(struct vpoio_data *vpo, int how, int *disconnected, int request_bus)
313{
314	DECLARE_CPP_MICROSEQ;
315
316	device_t ppbus = device_get_parent(vpo->vpo_dev);
317	char s1, s2, s3;
318	int error;
319	int ret;
320
321	/* all should be ok */
322	if (disconnected)
323		*disconnected = 0;
324
325	if (request_bus)
326		if ((error = ppb_request_bus(ppbus, vpo->vpo_dev, how)))
327			return (error);
328
329	ppb_MS_init_msq(cpp_microseq, 3, CPP_S1, (void *)&s1,
330			CPP_S2, (void *)&s2, CPP_S3, (void *)&s3);
331
332	/* select device 0 in compatible mode */
333	ppb_MS_init_msq(cpp_microseq, 1, CPP_PARAM, 0xe0);
334	ppb_MS_microseq(ppbus, vpo->vpo_dev, cpp_microseq, &ret);
335
336	/* disconnect all devices */
337	ppb_MS_init_msq(cpp_microseq, 1, CPP_PARAM, 0x30);
338	ppb_MS_microseq(ppbus, vpo->vpo_dev, cpp_microseq, &ret);
339
340	if (PPB_IN_EPP_MODE(ppbus))
341		ppb_MS_init_msq(cpp_microseq, 1, CPP_PARAM, 0x28);
342	else
343		ppb_MS_init_msq(cpp_microseq, 1, CPP_PARAM, 0xe0);
344
345	ppb_MS_microseq(ppbus, vpo->vpo_dev, cpp_microseq, &ret);
346
347	if ((s1 != (char)0xb8 || s2 != (char)0x18 || s3 != (char)0x30)) {
348		if (bootverbose)
349			printf("imm%d: (connect) s1=0x%x s2=0x%x, s3=0x%x\n",
350				vpo->vpo_unit, s1 & 0xff, s2 & 0xff, s3 & 0xff);
351		if (disconnected)
352			*disconnected = VP0_ECONNECT;
353	}
354
355	return (0);
356}
357
358/*
359 * imm_detect()
360 *
361 * Detect and initialise the VP0 adapter.
362 */
363static int
364imm_detect(struct vpoio_data *vpo)
365{
366	device_t ppbus = device_get_parent(vpo->vpo_dev);
367	int error;
368
369	if ((error = ppb_request_bus(ppbus, vpo->vpo_dev, PPB_DONTWAIT)))
370		return (error);
371
372	/* disconnect the drive, keep the bus */
373	imm_disconnect(vpo, NULL, 0);
374
375	/* we already have the bus, just connect */
376	imm_connect(vpo, PPB_DONTWAIT, &error, 0);
377
378	if (error) {
379		if (bootverbose)
380			printf("imm%d: can't connect to the drive\n",
381				vpo->vpo_unit);
382		goto error;
383	}
384
385	/* send SCSI reset signal */
386	ppb_MS_microseq(ppbus, vpo->vpo_dev, reset_microseq, NULL);
387
388	/* release the bus now */
389	imm_disconnect(vpo, &error, 1);
390
391	/* ensure we are disconnected or daisy chained peripheral
392	 * may cause serious problem to the disk */
393
394	if (error) {
395		if (bootverbose)
396			printf("imm%d: can't disconnect from the drive\n",
397				vpo->vpo_unit);
398		goto error;
399	}
400
401	return (0);
402
403error:
404	ppb_release_bus(ppbus, vpo->vpo_dev);
405	return (VP0_EINITFAILED);
406}
407
408/*
409 * imm_outstr()
410 */
411static int
412imm_outstr(struct vpoio_data *vpo, char *buffer, int size)
413{
414	device_t ppbus = device_get_parent(vpo->vpo_dev);
415	int error = 0;
416
417	if (PPB_IN_EPP_MODE(ppbus))
418		ppb_reset_epp_timeout(ppbus);
419
420	ppb_MS_exec(ppbus, vpo->vpo_dev, MS_OP_PUT, (union ppb_insarg)buffer,
421		(union ppb_insarg)size, (union ppb_insarg)MS_UNKNOWN, &error);
422
423	return (error);
424}
425
426/*
427 * imm_instr()
428 */
429static int
430imm_instr(struct vpoio_data *vpo, char *buffer, int size)
431{
432	device_t ppbus = device_get_parent(vpo->vpo_dev);
433	int error = 0;
434
435	if (PPB_IN_EPP_MODE(ppbus))
436		ppb_reset_epp_timeout(ppbus);
437
438	ppb_MS_exec(ppbus, vpo->vpo_dev, MS_OP_GET, (union ppb_insarg)buffer,
439		(union ppb_insarg)size, (union ppb_insarg)MS_UNKNOWN, &error);
440
441	return (error);
442}
443
444static char
445imm_select(struct vpoio_data *vpo, int initiator, int target)
446{
447	DECLARE_SELECT_MICROSEQUENCE;
448	device_t ppbus = device_get_parent(vpo->vpo_dev);
449	int ret;
450
451	/* initialize the select microsequence */
452	ppb_MS_init_msq(select_microseq, 1,
453			SELECT_TARGET, 1 << initiator | 1 << target);
454
455	ppb_MS_microseq(ppbus, vpo->vpo_dev, select_microseq, &ret);
456
457	return (ret);
458}
459
460/*
461 * imm_wait()
462 *
463 * H_SELIN must be low.
464 *
465 * XXX should be ported to microseq
466 */
467static char
468imm_wait(struct vpoio_data *vpo, int tmo)
469{
470	device_t ppbus = device_get_parent(vpo->vpo_dev);
471	register int	k;
472	register char	r;
473
474	ppb_wctr(ppbus, 0xc);
475
476	/* XXX should be ported to microseq */
477	k = 0;
478	while (!((r = ppb_rstr(ppbus)) & 0x80) && (k++ < tmo))
479		DELAY(1);
480
481	/*
482	 * Return some status information.
483	 * Semantics :	0x88 = ZIP+ wants more data
484	 *		0x98 = ZIP+ wants to send more data
485	 *		0xa8 = ZIP+ wants command
486	 *		0xb8 = end of transfer, ZIP+ is sending status
487	 */
488	ppb_wctr(ppbus, 0x4);
489	if (k < tmo)
490	  return (r & 0xb8);
491
492	return (0);			   /* command timed out */
493}
494
495static int
496imm_negociate(struct vpoio_data *vpo)
497{
498	DECLARE_NEGOCIATE_MICROSEQ;
499	device_t ppbus = device_get_parent(vpo->vpo_dev);
500	int negociate_mode;
501	int ret;
502
503	if (PPB_IN_NIBBLE_MODE(ppbus))
504		negociate_mode = 0;
505	else if (PPB_IN_PS2_MODE(ppbus))
506		negociate_mode = 1;
507	else
508		return (0);
509
510#if 0 /* XXX use standalone code not to depend on ppb_1284 code yet */
511	ret = ppb_1284_negociate(ppbus, negociate_mode);
512
513	if (ret)
514		return (VP0_ENEGOCIATE);
515#endif
516
517	ppb_MS_init_msq(negociate_microseq, 1,
518			NEGOCIATED_MODE, negociate_mode);
519
520	ppb_MS_microseq(ppbus, vpo->vpo_dev, negociate_microseq, &ret);
521
522	return (ret);
523}
524
525/*
526 * imm_probe()
527 *
528 * Low level probe of vpo device
529 *
530 */
531int
532imm_probe(device_t dev, struct vpoio_data *vpo)
533{
534	int error;
535
536	/* ppbus dependent initialisation */
537	vpo->vpo_dev = dev;
538
539	/* now, try to initialise the drive */
540	if ((error = imm_detect(vpo))) {
541		return (error);
542	}
543
544	return (0);
545}
546
547/*
548 * imm_attach()
549 *
550 * Low level attachment of vpo device
551 *
552 */
553int
554imm_attach(struct vpoio_data *vpo)
555{
556	device_t ppbus = device_get_parent(vpo->vpo_dev);
557	int epp;
558
559	/*
560	 * Initialize microsequence code
561	 */
562	vpo->vpo_nibble_inbyte_msq = (struct ppb_microseq *)malloc(
563		sizeof(nibble_inbyte_submicroseq), M_DEVBUF, M_NOWAIT);
564
565	if (!vpo->vpo_nibble_inbyte_msq)
566		return (ENXIO);
567
568	bcopy((void *)nibble_inbyte_submicroseq,
569		(void *)vpo->vpo_nibble_inbyte_msq,
570		sizeof(nibble_inbyte_submicroseq));
571
572	INIT_NIBBLE_INBYTE_SUBMICROSEQ(vpo);
573
574	/*
575	 * Initialize mode dependent in/out microsequences
576	 */
577	ppb_request_bus(ppbus, vpo->vpo_dev, PPB_WAIT);
578
579	/* enter NIBBLE mode to configure submsq */
580	if (ppb_set_mode(ppbus, PPB_NIBBLE) != -1) {
581
582		ppb_MS_GET_init(ppbus, vpo->vpo_dev, vpo->vpo_nibble_inbyte_msq);
583		ppb_MS_PUT_init(ppbus, vpo->vpo_dev, spp_outbyte_submicroseq);
584	}
585
586	/* enter PS2 mode to configure submsq */
587	if (ppb_set_mode(ppbus, PPB_PS2) != -1) {
588
589		ppb_MS_GET_init(ppbus, vpo->vpo_dev, ps2_inbyte_submicroseq);
590		ppb_MS_PUT_init(ppbus, vpo->vpo_dev, spp_outbyte_submicroseq);
591	}
592
593	epp = ppb_get_epp_protocol(ppbus);
594
595	/* enter EPP mode to configure submsq */
596	if (ppb_set_mode(ppbus, PPB_EPP) != -1) {
597
598		switch (epp) {
599		case EPP_1_9:
600		case EPP_1_7:
601			ppb_MS_GET_init(ppbus, vpo->vpo_dev, epp17_instr);
602			ppb_MS_PUT_init(ppbus, vpo->vpo_dev, epp17_outstr);
603			break;
604		default:
605			panic("%s: unknown EPP protocol (0x%x)", __FUNCTION__,
606				epp);
607		}
608	}
609
610	/* try to enter EPP or PS/2 mode, NIBBLE otherwise */
611	if (ppb_set_mode(ppbus, PPB_EPP) != -1) {
612		switch (epp) {
613		case EPP_1_9:
614			printf("imm%d: EPP 1.9 mode\n", vpo->vpo_unit);
615			break;
616		case EPP_1_7:
617			printf("imm%d: EPP 1.7 mode\n", vpo->vpo_unit);
618			break;
619		default:
620			panic("%s: unknown EPP protocol (0x%x)", __FUNCTION__,
621				epp);
622		}
623	} else if (ppb_set_mode(ppbus, PPB_PS2) != -1)
624		printf("imm%d: PS2 mode\n", vpo->vpo_unit);
625
626	else if (ppb_set_mode(ppbus, PPB_NIBBLE) != -1)
627		printf("imm%d: NIBBLE mode\n", vpo->vpo_unit);
628
629	else {
630		printf("imm%d: can't enter NIBBLE, PS2 or EPP mode\n",
631			vpo->vpo_unit);
632
633		ppb_release_bus(ppbus, vpo->vpo_dev);
634
635		free(vpo->vpo_nibble_inbyte_msq, M_DEVBUF);
636		return (ENXIO);
637	}
638
639	ppb_release_bus(ppbus, vpo->vpo_dev);
640
641	return (0);
642}
643
644/*
645 * imm_reset_bus()
646 *
647 */
648int
649imm_reset_bus(struct vpoio_data *vpo)
650{
651	device_t ppbus = device_get_parent(vpo->vpo_dev);
652	int disconnected;
653
654	/* first, connect to the drive and request the bus */
655	imm_connect(vpo, PPB_WAIT|PPB_INTR, &disconnected, 1);
656
657	if (!disconnected) {
658
659		/* reset the SCSI bus */
660		ppb_MS_microseq(ppbus, vpo->vpo_dev, reset_microseq, NULL);
661
662		/* then disconnect */
663		imm_disconnect(vpo, NULL, 1);
664	}
665
666	return (0);
667}
668
669/*
670 * imm_do_scsi()
671 *
672 * Send an SCSI command
673 *
674 */
675int
676imm_do_scsi(struct vpoio_data *vpo, int host, int target, char *command,
677		int clen, char *buffer, int blen, int *result, int *count,
678		int *ret)
679{
680	device_t ppbus = device_get_parent(vpo->vpo_dev);
681	register char r;
682	char l, h = 0;
683	int len, error = 0, not_connected = 0;
684	register int k;
685	int negociated = 0;
686
687	/*
688	 * enter disk state, allocate the ppbus
689	 *
690	 * XXX
691	 * Should we allow this call to be interruptible?
692	 * The only way to report the interruption is to return
693	 * EIO do upper SCSI code :^(
694	 */
695	if ((error = imm_connect(vpo, PPB_WAIT|PPB_INTR, &not_connected, 1)))
696		return (error);
697
698	if (not_connected) {
699		*ret = VP0_ECONNECT; goto error;
700	}
701
702	/*
703	 * Select the drive ...
704	 */
705	if ((*ret = imm_select(vpo,host,target)))
706		goto error;
707
708	/*
709	 * Send the command ...
710	 */
711	for (k = 0; k < clen; k+=2) {
712		if (imm_wait(vpo, VP0_FAST_SPINTMO) != (char)0xa8) {
713			*ret = VP0_ECMD_TIMEOUT;
714			goto error;
715		}
716		if (imm_outstr(vpo, &command[k], 2)) {
717			*ret = VP0_EPPDATA_TIMEOUT;
718			goto error;
719		}
720	}
721
722	if (!(r = imm_wait(vpo, VP0_LOW_SPINTMO))) {
723		*ret = VP0_ESTATUS_TIMEOUT; goto error;
724	}
725
726	if ((r & 0x30) == 0x10) {
727		if (imm_negociate(vpo)) {
728			*ret = VP0_ENEGOCIATE;
729			goto error;
730		} else
731			negociated = 1;
732	}
733
734	/*
735	 * Complete transfer ...
736	 */
737	*count = 0;
738	for (;;) {
739
740		if (!(r = imm_wait(vpo, VP0_LOW_SPINTMO))) {
741			*ret = VP0_ESTATUS_TIMEOUT; goto error;
742		}
743
744		/* stop when the ZIP+ wants to send status */
745		if (r == (char)0xb8)
746			break;
747
748		if (*count >= blen) {
749			*ret = VP0_EDATA_OVERFLOW;
750			goto error;
751		}
752
753		/* ZIP+ wants to send data? */
754		if (r == (char)0x88) {
755			len = (((blen - *count) >= VP0_SECTOR_SIZE)) ?
756				VP0_SECTOR_SIZE : 2;
757
758			error = imm_outstr(vpo, &buffer[*count], len);
759		} else {
760			if (!PPB_IN_EPP_MODE(ppbus))
761				len = 1;
762			else
763				len = (((blen - *count) >= VP0_SECTOR_SIZE)) ?
764					VP0_SECTOR_SIZE : 1;
765
766			error = imm_instr(vpo, &buffer[*count], len);
767		}
768
769		if (error) {
770			*ret = error;
771			goto error;
772		}
773
774		*count += len;
775	}
776
777	if ((PPB_IN_NIBBLE_MODE(ppbus) ||
778			PPB_IN_PS2_MODE(ppbus)) && negociated)
779		ppb_MS_microseq(ppbus, vpo->vpo_dev, transfer_epilog, NULL);
780
781	/*
782	 * Retrieve status ...
783	 */
784	if (imm_negociate(vpo)) {
785		*ret = VP0_ENEGOCIATE;
786		goto error;
787	} else
788		negociated = 1;
789
790	if (imm_instr(vpo, &l, 1)) {
791		*ret = VP0_EOTHER; goto error;
792	}
793
794	/* check if the ZIP+ wants to send more status */
795	if (imm_wait(vpo, VP0_FAST_SPINTMO) == (char)0xb8)
796		if (imm_instr(vpo, &h, 1)) {
797			*ret = VP0_EOTHER+2; goto error;
798		}
799
800	*result = ((int) h << 8) | ((int) l & 0xff);
801
802error:
803	if ((PPB_IN_NIBBLE_MODE(ppbus) ||
804			PPB_IN_PS2_MODE(ppbus)) && negociated)
805		ppb_MS_microseq(ppbus, vpo->vpo_dev, transfer_epilog, NULL);
806
807	/* return to printer state, release the ppbus */
808	imm_disconnect(vpo, NULL, 1);
809
810	return (0);
811}
812