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