xref: /dragonfly/sys/dev/disk/vpo/vpoio.c (revision 678e8cc6)
1 /*-
2  * Copyright (c) 1998, 1999 Nicolas Souchu
3  * Copyright (c) 2000 Alcove - Nicolas Souchu
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  *
27  * $FreeBSD: src/sys/dev/ppbus/vpoio.c,v 1.10.2.3 2001/10/02 05:27:20 nsouch Exp $
28  */
29 
30 #ifdef _KERNEL
31 #include <sys/param.h>
32 #include <sys/systm.h>
33 #include <sys/module.h>
34 #include <sys/bus.h>
35 #include <sys/malloc.h>
36 
37 #include <machine/clock.h>
38 
39 #endif
40 
41 #ifdef	_KERNEL
42 #endif
43 
44 #include "opt_vpo.h"
45 
46 #include <bus/ppbus/ppbio.h>
47 #include <bus/ppbus/ppbconf.h>
48 #include <bus/ppbus/ppb_msq.h>
49 #include "vpoio.h"
50 
51 #include "ppbus_if.h"
52 
53 /*
54  * The driver pools the drive. We may add a timeout queue to avoid
55  * active polling on nACK. I've tried this but it leads to unreliable
56  * transfers
57  */
58 #define VP0_SELTMO		5000	/* select timeout */
59 #define VP0_FAST_SPINTMO	500000	/* wait status timeout */
60 #define VP0_LOW_SPINTMO		5000000	/* wait status timeout */
61 
62 /*
63  * Actually, VP0 timings are more accurate (about few 16MHZ cycles),
64  * but succeeding in respecting such timings leads to architecture
65  * dependent considerations.
66  */
67 #define VP0_PULSE		1
68 
69 #define VP0_SECTOR_SIZE	512
70 #define VP0_BUFFER_SIZE	0x12000
71 
72 #define n(flags) (~(flags) & (flags))
73 
74 /*
75  * VP0 connections.
76  */
77 #define H_AUTO		n(AUTOFEED)
78 #define H_nAUTO		AUTOFEED
79 #define H_STROBE	n(STROBE)
80 #define H_nSTROBE	STROBE
81 #define H_BSY		n(nBUSY)
82 #define H_nBSY		nBUSY
83 #define H_SEL		SELECT
84 #define H_nSEL		n(SELECT)
85 #define H_ERR		PERROR
86 #define H_nERR		n(PERROR)
87 #define H_ACK		nACK
88 #define H_nACK		n(nACK)
89 #define H_FLT		nFAULT
90 #define H_nFLT		n(nFAULT)
91 #define H_SELIN		n(SELECTIN)
92 #define H_nSELIN	SELECTIN
93 #define H_INIT		nINIT
94 #define H_nINIT		n(nINIT)
95 
96 /*
97  * Microcode to execute very fast I/O sequences at the lowest bus level.
98  */
99 
100 #define WAIT_RET	MS_PARAM(4, 2, MS_TYP_PTR)
101 #define WAIT_TMO	MS_PARAM(0, 0, MS_TYP_INT)
102 
103 #define DECLARE_WAIT_MICROSEQUENCE			\
104 struct ppb_microseq wait_microseq[] = {			\
105 	MS_SET(MS_UNKNOWN),				\
106 	/* loop */					\
107 	MS_BRSET(nBUSY, 2 /* ready */),			\
108 	MS_DBRA(-2 /* loop */),				\
109 	MS_RET(1), /* timed out */			\
110 	/* ready */					\
111 	MS_RFETCH(MS_REG_STR, 0xf0, MS_UNKNOWN),	\
112 	MS_RET(0) /* no error */			\
113 }
114 
115 /* call this macro to initialize connect/disconnect microsequences */
116 #define INIT_TRIG_MICROSEQ {						\
117 	int i;								\
118 	for (i=1; i <= 7; i+=2) {					\
119 		disconnect_microseq[i].arg[2].c = d_pulse;		\
120 		connect_epp_microseq[i].arg[2].c = 			\
121 		connect_spp_microseq[i].arg[2].c = c_pulse;		\
122 	}								\
123 }
124 
125 #define trig_d_pulse MS_TRIG(MS_REG_CTR,5,MS_UNKNOWN /* d_pulse */)
126 static char d_pulse[] = {
127 	 H_AUTO | H_nSELIN | H_INIT | H_STROBE, 0,
128 	H_nAUTO | H_nSELIN | H_INIT | H_STROBE, VP0_PULSE,
129 	 H_AUTO | H_nSELIN | H_INIT | H_STROBE, 0,
130 	 H_AUTO |  H_SELIN | H_INIT | H_STROBE, VP0_PULSE,
131 	 H_AUTO | H_nSELIN | H_INIT | H_STROBE, VP0_PULSE
132 };
133 
134 #define trig_c_pulse MS_TRIG(MS_REG_CTR,5,MS_UNKNOWN /* c_pulse */)
135 static char c_pulse[] = {
136 	 H_AUTO | H_nSELIN | H_INIT | H_STROBE, 0,
137 	 H_AUTO |  H_SELIN | H_INIT | H_STROBE, 0,
138 	H_nAUTO |  H_SELIN | H_INIT | H_STROBE, VP0_PULSE,
139 	 H_AUTO |  H_SELIN | H_INIT | H_STROBE, 0,
140 	 H_AUTO | H_nSELIN | H_INIT | H_STROBE, VP0_PULSE
141 };
142 
143 static struct ppb_microseq disconnect_microseq[] = {
144 	  MS_DASS(0x0), trig_d_pulse, MS_DASS(0x3c), trig_d_pulse,
145 	  MS_DASS(0x20), trig_d_pulse, MS_DASS(0xf), trig_d_pulse, MS_RET(0)
146 };
147 
148 static struct ppb_microseq connect_epp_microseq[] = {
149 	  MS_DASS(0x0), trig_c_pulse, MS_DASS(0x3c), trig_c_pulse,
150 	  MS_DASS(0x20), trig_c_pulse, MS_DASS(0xcf), trig_c_pulse, MS_RET(0)
151 };
152 
153 static struct ppb_microseq connect_spp_microseq[] = {
154 	  MS_DASS(0x0), trig_c_pulse, MS_DASS(0x3c), trig_c_pulse,
155 	  MS_DASS(0x20), trig_c_pulse, MS_DASS(0x8f), trig_c_pulse, MS_RET(0)
156 };
157 
158 /*
159  * nibble_inbyte_hook()
160  *
161  * Formats high and low nibble into a character
162  */
163 static int
164 nibble_inbyte_hook (void *p, char *ptr)
165 {
166 	struct vpo_nibble *s = (struct vpo_nibble *)p;
167 
168 	/* increment the buffer pointer */
169 	*ptr++ = ((s->l >> 4) & 0x0f) + (s->h & 0xf0);
170 
171 	return (0);
172 }
173 
174 #define INB_NIBBLE_H MS_PARAM(2, 2, MS_TYP_PTR)
175 #define INB_NIBBLE_L MS_PARAM(4, 2, MS_TYP_PTR)
176 #define INB_NIBBLE_F MS_PARAM(5, 0, MS_TYP_FUN)
177 #define INB_NIBBLE_P MS_PARAM(5, 1, MS_TYP_PTR)
178 
179 /*
180  * This is the sub-microseqence for MS_GET in NIBBLE mode
181  * Retrieve the two nibbles and call the C function to generate the character
182  * and store it in the buffer (see nibble_inbyte_hook())
183  */
184 
185 #define DECLARE_NIBBLE_INBYTE_SUBMICROSEQ			\
186 struct ppb_microseq nibble_inbyte_submicroseq[] = {		\
187 /* loop: */							\
188 	  MS_CASS( H_AUTO | H_SELIN | H_INIT | H_STROBE),	\
189 	  MS_DELAY(VP0_PULSE),					\
190 	  MS_RFETCH(MS_REG_STR, MS_FETCH_ALL, MS_UNKNOWN /* high nibble */),\
191 	  MS_CASS(H_nAUTO | H_SELIN | H_INIT | H_STROBE),	\
192 	  MS_RFETCH(MS_REG_STR, MS_FETCH_ALL, MS_UNKNOWN /* low nibble */),\
193 	  /* do a C call to format the received nibbles */	\
194 	  MS_C_CALL(MS_UNKNOWN /* C hook */, MS_UNKNOWN /* param */),\
195 	  MS_DBRA(-7 /* loop */),				\
196 	  MS_CASS(H_AUTO | H_nSELIN | H_INIT | H_STROBE),	\
197 	  MS_RET(0)						\
198 }
199 
200 /*
201  * This is the sub-microseqence for MS_GET in PS2 mode
202  */
203 static struct ppb_microseq ps2_inbyte_submicroseq[] = {
204 	  MS_CASS(PCD | H_AUTO | H_SELIN | H_INIT | H_nSTROBE),
205 
206 /* loop: */
207 	  MS_RFETCH_P(1, MS_REG_DTR, MS_FETCH_ALL),
208 	  MS_CASS(PCD | H_nAUTO | H_SELIN | H_INIT | H_nSTROBE),
209 	  MS_CASS(PCD |  H_AUTO | H_SELIN | H_INIT | H_nSTROBE),
210 	  MS_DBRA(-4 /* loop */),
211 
212 	  MS_CASS(H_AUTO | H_nSELIN | H_INIT | H_STROBE),
213 	  MS_RET(0)
214 };
215 
216 /*
217  * This is the sub-microsequence for MS_PUT in both NIBBLE and PS2 modes
218  */
219 static struct ppb_microseq spp_outbyte_submicroseq[] = {
220 
221 /* loop: */
222 	  MS_RASSERT_P(1, MS_REG_DTR),
223 	  MS_CASS(H_nAUTO | H_nSELIN | H_INIT | H_STROBE),
224 	  MS_CASS( H_AUTO | H_nSELIN | H_INIT | H_STROBE),
225 	  MS_DELAY(VP0_PULSE),
226 	  MS_DBRA(-5 /* loop */),
227 
228 	  /* return from the put call */
229 	  MS_RET(0)
230 };
231 
232 /* EPP 1.7 microsequences, ptr and len set at runtime */
233 static struct ppb_microseq epp17_outstr_body[] = {
234 	  MS_CASS(H_AUTO | H_SELIN | H_INIT | H_STROBE),
235 
236 /* loop: */
237 	  MS_RASSERT_P(1, MS_REG_EPP_D),
238 	  MS_BRSET(TIMEOUT, 3 /* error */),	/* EPP timeout? */
239 	  MS_DBRA(-3 /* loop */),
240 
241 	  MS_CASS(H_AUTO | H_nSELIN | H_INIT | H_STROBE),
242 	  MS_RET(0),
243 /* error: */
244 	  MS_CASS(H_AUTO | H_nSELIN | H_INIT | H_STROBE),
245 	  MS_RET(1)
246 };
247 
248 static struct ppb_microseq epp17_instr_body[] = {
249 	  MS_CASS(PCD | H_AUTO | H_SELIN | H_INIT | H_STROBE),
250 
251 /* loop: */
252 	  MS_RFETCH_P(1, MS_REG_EPP_D, MS_FETCH_ALL),
253 	  MS_BRSET(TIMEOUT, 3 /* error */),	/* EPP timeout? */
254 	  MS_DBRA(-3 /* loop */),
255 
256 	  MS_CASS(PCD | H_AUTO | H_nSELIN | H_INIT | H_STROBE),
257 	  MS_RET(0),
258 /* error: */
259 	  MS_CASS(PCD | H_AUTO | H_nSELIN | H_INIT | H_STROBE),
260 	  MS_RET(1)
261 };
262 
263 static struct ppb_microseq in_disk_mode[] = {
264 	  MS_CASS( H_AUTO | H_nSELIN | H_INIT | H_STROBE),
265 	  MS_CASS(H_nAUTO | H_nSELIN | H_INIT | H_STROBE),
266 
267 	  MS_BRCLEAR(H_FLT, 3 /* error */),
268 	  MS_CASS( H_AUTO | H_nSELIN | H_INIT | H_STROBE),
269 	  MS_BRSET(H_FLT, 1 /* error */),
270 
271 	  MS_RET(1),
272 /* error: */
273 	  MS_RET(0)
274 };
275 
276 static int
277 vpoio_disconnect(struct vpoio_data *vpo)
278 {
279 	device_t ppbus = device_get_parent(vpo->vpo_dev);
280 	int ret;
281 
282 	ppb_MS_microseq(ppbus, vpo->vpo_dev, disconnect_microseq, &ret);
283 	return (ppb_release_bus(ppbus, vpo->vpo_dev));
284 }
285 
286 /*
287  * how	: PPB_WAIT or PPB_DONTWAIT
288  */
289 static int
290 vpoio_connect(struct vpoio_data *vpo, int how)
291 {
292 	device_t ppbus = device_get_parent(vpo->vpo_dev);
293 	int error;
294 	int ret;
295 
296 	if ((error = ppb_request_bus(ppbus, vpo->vpo_dev, how))) {
297 
298 #ifdef VP0_DEBUG
299 		kprintf("%s: can't request bus!\n", __func__);
300 #endif
301 		return error;
302 	}
303 
304 	if (PPB_IN_EPP_MODE(ppbus))
305 		ppb_MS_microseq(ppbus, vpo->vpo_dev, connect_epp_microseq, &ret);
306 	else
307 		ppb_MS_microseq(ppbus, vpo->vpo_dev, connect_spp_microseq, &ret);
308 
309 	return (0);
310 }
311 
312 /*
313  * vpoio_reset()
314  *
315  * SCSI reset signal, the drive must be in disk mode
316  */
317 static void
318 vpoio_reset (struct vpoio_data *vpo)
319 {
320 	device_t ppbus = device_get_parent(vpo->vpo_dev);
321 	int ret;
322 
323 	struct ppb_microseq reset_microseq[] = {
324 
325 		#define INITIATOR	MS_PARAM(0, 1, MS_TYP_INT)
326 
327 		MS_DASS(MS_UNKNOWN),
328 		MS_CASS(H_AUTO | H_nSELIN | H_nINIT | H_STROBE),
329 		MS_DELAY(25),
330 		MS_CASS(H_AUTO | H_nSELIN |  H_INIT | H_STROBE),
331 		MS_RET(0)
332 	};
333 
334 	ppb_MS_init_msq(reset_microseq, 1, INITIATOR, 1 << VP0_INITIATOR);
335 	ppb_MS_microseq(ppbus, vpo->vpo_dev, reset_microseq, &ret);
336 
337 	return;
338 }
339 
340 /*
341  * vpoio_in_disk_mode()
342  */
343 static int
344 vpoio_in_disk_mode(struct vpoio_data *vpo)
345 {
346 	device_t ppbus = device_get_parent(vpo->vpo_dev);
347 	int ret;
348 
349 	ppb_MS_microseq(ppbus, vpo->vpo_dev, in_disk_mode, &ret);
350 
351 	return (ret);
352 }
353 
354 /*
355  * vpoio_detect()
356  *
357  * Detect and initialise the VP0 adapter.
358  */
359 static int
360 vpoio_detect(struct vpoio_data *vpo)
361 {
362 	device_t ppbus = device_get_parent(vpo->vpo_dev);
363 	int error, ret;
364 
365 	/* allocate the bus, then apply microsequences */
366 	if ((error = ppb_request_bus(ppbus, vpo->vpo_dev, PPB_DONTWAIT)))
367                 return (error);
368 
369 	/* Force disconnection */
370 	ppb_MS_microseq(ppbus, vpo->vpo_dev, disconnect_microseq, &ret);
371 
372 	/* Try to enter EPP mode, then connect to the drive in EPP mode */
373 	if (ppb_set_mode(ppbus, PPB_EPP) != -1) {
374 		/* call manually the microseq instead of using the appropriate function
375 		 * since we already requested the ppbus */
376 		ppb_MS_microseq(ppbus, vpo->vpo_dev, connect_epp_microseq, &ret);
377 	}
378 
379 	/* If EPP mode switch failed or ZIP connection in EPP mode failed,
380 	 * try to connect in NIBBLE mode */
381 	if (!vpoio_in_disk_mode(vpo)) {
382 
383 		/* The interface must be at least PS/2 or NIBBLE capable.
384 		 * There is no way to know if the ZIP will work with
385 		 * PS/2 mode since PS/2 and SPP both use the same connect
386 		 * sequence. One must supress PS/2 with boot flags if
387 		 * PS/2 mode fails (see ppc(4)).
388 		 */
389 		if (ppb_set_mode(ppbus, PPB_PS2) != -1) {
390 			vpo->vpo_mode_found = VP0_MODE_PS2;
391 		} else {
392 			if (ppb_set_mode(ppbus, PPB_NIBBLE) == -1)
393 				goto error;
394 
395 			vpo->vpo_mode_found = VP0_MODE_NIBBLE;
396 		}
397 
398 		/* Can't know if the interface is capable of PS/2 yet */
399 		ppb_MS_microseq(ppbus, vpo->vpo_dev, connect_spp_microseq, &ret);
400 		if (!vpoio_in_disk_mode(vpo)) {
401 			vpo->vpo_mode_found = VP0_MODE_UNDEFINED;
402 			if (bootverbose)
403 				kprintf("vpo%d: can't connect to the drive\n",
404 					vpo->vpo_unit);
405 
406 			/* disconnect and release the bus */
407 			ppb_MS_microseq(ppbus, vpo->vpo_dev, disconnect_microseq,
408 					&ret);
409 			goto error;
410 		}
411 	} else {
412 		vpo->vpo_mode_found = VP0_MODE_EPP;
413 	}
414 
415 	/* send SCSI reset signal */
416 	vpoio_reset(vpo);
417 
418 	ppb_MS_microseq(ppbus, vpo->vpo_dev, disconnect_microseq, &ret);
419 
420 	/* ensure we are disconnected or daisy chained peripheral
421 	 * may cause serious problem to the disk */
422 	if (vpoio_in_disk_mode(vpo)) {
423 		if (bootverbose)
424 			kprintf("vpo%d: can't disconnect from the drive\n",
425 				vpo->vpo_unit);
426 		goto error;
427 	}
428 
429 	ppb_release_bus(ppbus, vpo->vpo_dev);
430 	return (0);
431 
432 error:
433 	ppb_release_bus(ppbus, vpo->vpo_dev);
434 	return (VP0_EINITFAILED);
435 }
436 
437 /*
438  * vpoio_outstr()
439  */
440 static int
441 vpoio_outstr(struct vpoio_data *vpo, char *_buffer, int _size)
442 {
443 	union ppb_insarg buffer = { .c = _buffer };
444 	union ppb_insarg size = { .i = _size };
445 	union ppb_insarg unknown = { .i = MS_UNKNOWN };
446 	device_t ppbus = device_get_parent(vpo->vpo_dev);
447 	int error = 0;
448 
449 	ppb_MS_exec(ppbus, vpo->vpo_dev, MS_OP_PUT, buffer, size, unknown,
450 	    &error);
451 
452 	ppb_ecp_sync(ppbus);
453 
454 	return (error);
455 }
456 
457 /*
458  * vpoio_instr()
459  */
460 static int
461 vpoio_instr(struct vpoio_data *vpo, char *_buffer, int _size)
462 {
463 	union ppb_insarg buffer = { .c = _buffer };
464 	union ppb_insarg size = { .i = _size };
465 	union ppb_insarg unknown = { .i = MS_UNKNOWN };
466 	device_t ppbus = device_get_parent(vpo->vpo_dev);
467 	int error = 0;
468 
469 	ppb_MS_exec(ppbus, vpo->vpo_dev, MS_OP_GET, buffer, size, unknown,
470 	    &error);
471 
472 	ppb_ecp_sync(ppbus);
473 
474 	return (error);
475 }
476 
477 static char
478 vpoio_select(struct vpoio_data *vpo, int initiator, int target)
479 {
480 	device_t ppbus = device_get_parent(vpo->vpo_dev);
481 	int ret;
482 
483 	struct ppb_microseq select_microseq[] = {
484 
485 		/* parameter list
486 		 */
487 		#define SELECT_TARGET		MS_PARAM(0, 1, MS_TYP_INT)
488 		#define SELECT_INITIATOR	MS_PARAM(3, 1, MS_TYP_INT)
489 
490 		/* send the select command to the drive */
491 		MS_DASS(MS_UNKNOWN),
492 		MS_CASS(H_nAUTO | H_nSELIN |  H_INIT | H_STROBE),
493 		MS_CASS( H_AUTO | H_nSELIN |  H_INIT | H_STROBE),
494 		MS_DASS(MS_UNKNOWN),
495 		MS_CASS( H_AUTO | H_nSELIN | H_nINIT | H_STROBE),
496 
497 		/* now, wait until the drive is ready */
498 		MS_SET(VP0_SELTMO),
499 /* loop: */	MS_BRSET(H_ACK, 2 /* ready */),
500 		MS_DBRA(-2 /* loop */),
501 /* error: */	MS_RET(1),
502 /* ready: */	MS_RET(0)
503 	};
504 
505 	/* initialize the select microsequence */
506 	ppb_MS_init_msq(select_microseq, 2,
507 			SELECT_TARGET, 1 << target,
508 			SELECT_INITIATOR, 1 << initiator);
509 
510 	ppb_MS_microseq(ppbus, vpo->vpo_dev, select_microseq, &ret);
511 
512 	if (ret)
513 		return (VP0_ESELECT_TIMEOUT);
514 
515 	return (0);
516 }
517 
518 /*
519  * vpoio_wait()
520  *
521  * H_SELIN must be low.
522  *
523  * XXX should be ported to microseq
524  */
525 static char
526 vpoio_wait(struct vpoio_data *vpo, int tmo)
527 {
528 	DECLARE_WAIT_MICROSEQUENCE;
529 
530 	device_t ppbus = device_get_parent(vpo->vpo_dev);
531 	int ret, err;
532 
533 #if 0	/* broken */
534 	if (ppb_poll_device(ppbus, 150, nBUSY, nBUSY, PPB_INTR))
535 		return (0);
536 
537 	return (ppb_rstr(ppbus) & 0xf0);
538 #endif
539 
540 	/*
541 	 * Return some status information.
542 	 * Semantics :	0xc0 = ZIP wants more data
543 	 *		0xd0 = ZIP wants to send more data
544 	 *		0xe0 = ZIP wants command
545 	 *		0xf0 = end of transfer, ZIP is sending status
546 	 */
547 
548 	ppb_MS_init_msq(wait_microseq, 2,
549 			WAIT_RET, (void *)&ret,
550 			WAIT_TMO, tmo);
551 
552 	ppb_MS_microseq(ppbus, vpo->vpo_dev, wait_microseq, &err);
553 
554 	if (err)
555 		return (0);	 /* command timed out */
556 
557 	return(ret);
558 }
559 
560 /*
561  * vpoio_probe()
562  *
563  * Low level probe of vpo device
564  *
565  */
566 int
567 vpoio_probe(device_t dev, struct vpoio_data *vpo)
568 {
569 	int error;
570 
571 	/* ppbus dependent initialisation */
572 	vpo->vpo_dev = dev;
573 
574 	/*
575 	 * Initialize microsequence code
576 	 */
577 	INIT_TRIG_MICROSEQ;
578 
579 	/* now, try to initialise the drive */
580 	if ((error = vpoio_detect(vpo))) {
581 		return (error);
582 	}
583 
584 	return (0);
585 }
586 
587 /*
588  * vpoio_attach()
589  *
590  * Low level attachment of vpo device
591  *
592  */
593 int
594 vpoio_attach(struct vpoio_data *vpo)
595 {
596 	DECLARE_NIBBLE_INBYTE_SUBMICROSEQ;
597 	device_t ppbus = device_get_parent(vpo->vpo_dev);
598 	int error = 0;
599 
600 	vpo->vpo_nibble_inbyte_msq = kmalloc(sizeof(nibble_inbyte_submicroseq),
601 						M_DEVBUF, M_WAITOK);
602 
603 	bcopy((void *)nibble_inbyte_submicroseq,
604 		(void *)vpo->vpo_nibble_inbyte_msq,
605 		sizeof(nibble_inbyte_submicroseq));
606 
607 	ppb_MS_init_msq(vpo->vpo_nibble_inbyte_msq, 4,
608 		INB_NIBBLE_H, (void *)&(vpo)->vpo_nibble.h,
609 		INB_NIBBLE_L, (void *)&(vpo)->vpo_nibble.l,
610 		INB_NIBBLE_F, nibble_inbyte_hook,
611 		INB_NIBBLE_P, (void *)&(vpo)->vpo_nibble);
612 
613 	/*
614 	 * Initialize mode dependent in/out microsequences
615 	 */
616 	if ((error = ppb_request_bus(ppbus, vpo->vpo_dev, PPB_WAIT)))
617 		goto error;
618 
619 	/* ppbus sets automatically the last mode entered during detection */
620 	switch (vpo->vpo_mode_found) {
621 	case VP0_MODE_EPP:
622 		ppb_MS_GET_init(ppbus, vpo->vpo_dev, epp17_instr_body);
623 		ppb_MS_PUT_init(ppbus, vpo->vpo_dev, epp17_outstr_body);
624 		kprintf("vpo%d: EPP mode\n", vpo->vpo_unit);
625 		break;
626 	case VP0_MODE_PS2:
627 		ppb_MS_GET_init(ppbus, vpo->vpo_dev, ps2_inbyte_submicroseq);
628 		ppb_MS_PUT_init(ppbus, vpo->vpo_dev, spp_outbyte_submicroseq);
629 		kprintf("vpo%d: PS2 mode\n", vpo->vpo_unit);
630 		break;
631 	case VP0_MODE_NIBBLE:
632 		ppb_MS_GET_init(ppbus, vpo->vpo_dev, vpo->vpo_nibble_inbyte_msq);
633 		ppb_MS_PUT_init(ppbus, vpo->vpo_dev, spp_outbyte_submicroseq);
634 		kprintf("vpo%d: NIBBLE mode\n", vpo->vpo_unit);
635 		break;
636 	default:
637 		panic("vpo: unknown mode %d", vpo->vpo_mode_found);
638 	}
639 
640 	ppb_release_bus(ppbus, vpo->vpo_dev);
641 
642 error:
643 	return (error);
644 }
645 
646 /*
647  * vpoio_reset_bus()
648  *
649  */
650 int
651 vpoio_reset_bus(struct vpoio_data *vpo)
652 {
653 	/* first, connect to the drive */
654 	if (vpoio_connect(vpo, PPB_WAIT|PPB_INTR) || !vpoio_in_disk_mode(vpo)) {
655 
656 #ifdef VP0_DEBUG
657 		kprintf("%s: not in disk mode!\n", __func__);
658 #endif
659 		/* release ppbus */
660 		vpoio_disconnect(vpo);
661 		return (1);
662 	}
663 
664 	/* reset the SCSI bus */
665 	vpoio_reset(vpo);
666 
667 	/* then disconnect */
668 	vpoio_disconnect(vpo);
669 
670 	return (0);
671 }
672 
673 /*
674  * vpoio_do_scsi()
675  *
676  * Send an SCSI command
677  *
678  */
679 int
680 vpoio_do_scsi(struct vpoio_data *vpo, int host, int target, char *command,
681 		int clen, char *buffer, int blen, int *result, int *count,
682 		int *ret)
683 {
684 	device_t ppbus = device_get_parent(vpo->vpo_dev);
685 	char r;
686 	char l, h = 0;
687 	int len, error = 0;
688 	int k;
689 
690 	/*
691 	 * enter disk state, allocate the ppbus
692 	 *
693 	 * XXX
694 	 * Should we allow this call to be interruptible?
695 	 * The only way to report the interruption is to return
696 	 * EIO do upper SCSI code :^(
697 	 */
698 	if ((error = vpoio_connect(vpo, PPB_WAIT|PPB_INTR)))
699 		return (error);
700 
701 	if (!vpoio_in_disk_mode(vpo)) {
702 		*ret = VP0_ECONNECT; goto error;
703 	}
704 
705 	if ((*ret = vpoio_select(vpo,host,target)))
706 		goto error;
707 
708 	/*
709 	 * Send the command ...
710 	 *
711 	 * set H_SELIN low for vpoio_wait().
712 	 */
713 	ppb_wctr(ppbus, H_AUTO | H_nSELIN | H_INIT | H_STROBE);
714 
715 	for (k = 0; k < clen; k++) {
716 		if (vpoio_wait(vpo, VP0_FAST_SPINTMO) != (char)0xe0) {
717 			*ret = VP0_ECMD_TIMEOUT;
718 			goto error;
719 		}
720 		if (vpoio_outstr(vpo, &command[k], 1)) {
721 			*ret = VP0_EPPDATA_TIMEOUT;
722 			goto error;
723 		}
724 	}
725 
726 	/*
727 	 * Completion ...
728 	 */
729 
730 	*count = 0;
731 	for (;;) {
732 
733 		if (!(r = vpoio_wait(vpo, VP0_LOW_SPINTMO))) {
734 			*ret = VP0_ESTATUS_TIMEOUT; goto error;
735 		}
736 
737 		/* stop when the ZIP wants to send status */
738 		if (r == (char)0xf0)
739 			break;
740 
741 		if (*count >= blen) {
742 			*ret = VP0_EDATA_OVERFLOW;
743 			goto error;
744 		}
745 
746 		/* if in EPP mode or writing bytes, try to transfer a sector
747 		 * otherwise, just send one byte
748 		 */
749 		if (PPB_IN_EPP_MODE(ppbus) || r == (char)0xc0)
750 			len = (((blen - *count) >= VP0_SECTOR_SIZE)) ?
751 				VP0_SECTOR_SIZE : 1;
752 		else
753 			len = 1;
754 
755 		/* ZIP wants to send data? */
756 		if (r == (char)0xc0)
757 			error = vpoio_outstr(vpo, &buffer[*count], len);
758 		else
759 			error = vpoio_instr(vpo, &buffer[*count], len);
760 
761 		if (error) {
762 			*ret = error;
763 			goto error;
764 		}
765 
766 		*count += len;
767 	}
768 
769 	if (vpoio_instr(vpo, &l, 1)) {
770 		*ret = VP0_EOTHER; goto error;
771 	}
772 
773 	/* check if the ZIP wants to send more status */
774 	if (vpoio_wait(vpo, VP0_FAST_SPINTMO) == (char)0xf0)
775 		if (vpoio_instr(vpo, &h, 1)) {
776 			*ret = VP0_EOTHER+2; goto error;
777 		}
778 
779 	*result = ((int) h << 8) | ((int) l & 0xff);
780 
781 error:
782 	/* return to printer state, release the ppbus */
783 	vpoio_disconnect(vpo);
784 	return (0);
785 }
786