]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/ppbus/vpoio.c
Correct calculation of RAID0 sizes on VIA RAID arrays.
[FreeBSD/FreeBSD.git] / sys / dev / ppbus / 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  *
28  */
29
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
32
33 #ifdef _KERNEL
34 #include <sys/param.h>
35 #include <sys/systm.h>
36 #include <sys/module.h>
37 #include <sys/bus.h>
38 #include <sys/malloc.h>
39
40 #include <machine/clock.h>
41
42 #endif
43
44 #include "opt_vpo.h"
45
46 #include <dev/ppbus/ppbio.h>
47 #include <dev/ppbus/ppbconf.h>
48 #include <dev/ppbus/ppb_msq.h>
49 #include <dev/ppbus/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] = (union ppb_insarg)d_pulse; \
120                 connect_epp_microseq[i].arg[2] =                        \
121                 connect_spp_microseq[i].arg[2] = (union ppb_insarg)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                 printf("%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                                 printf("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                         printf("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         device_t ppbus = device_get_parent(vpo->vpo_dev);
444         int error = 0;
445
446         ppb_MS_exec(ppbus, vpo->vpo_dev, MS_OP_PUT, (union ppb_insarg)buffer,
447                 (union ppb_insarg)size, (union ppb_insarg)MS_UNKNOWN, &error);
448
449         ppb_ecp_sync(ppbus);
450
451         return (error);
452 }
453
454 /*
455  * vpoio_instr()
456  */
457 static int
458 vpoio_instr(struct vpoio_data *vpo, char *buffer, int size)
459 {
460         device_t ppbus = device_get_parent(vpo->vpo_dev);
461         int error = 0;
462
463         ppb_MS_exec(ppbus, vpo->vpo_dev, MS_OP_GET, (union ppb_insarg)buffer,
464                 (union ppb_insarg)size, (union ppb_insarg)MS_UNKNOWN, &error);
465
466         ppb_ecp_sync(ppbus);
467
468         return (error);
469 }
470
471 static char
472 vpoio_select(struct vpoio_data *vpo, int initiator, int target)
473 {
474         device_t ppbus = device_get_parent(vpo->vpo_dev);
475         int ret;
476
477         struct ppb_microseq select_microseq[] = {
478
479                 /* parameter list
480                  */
481                 #define SELECT_TARGET           MS_PARAM(0, 1, MS_TYP_INT)
482                 #define SELECT_INITIATOR        MS_PARAM(3, 1, MS_TYP_INT)
483
484                 /* send the select command to the drive */
485                 MS_DASS(MS_UNKNOWN),
486                 MS_CASS(H_nAUTO | H_nSELIN |  H_INIT | H_STROBE),
487                 MS_CASS( H_AUTO | H_nSELIN |  H_INIT | H_STROBE),
488                 MS_DASS(MS_UNKNOWN),
489                 MS_CASS( H_AUTO | H_nSELIN | H_nINIT | H_STROBE),
490
491                 /* now, wait until the drive is ready */
492                 MS_SET(VP0_SELTMO),
493 /* loop: */     MS_BRSET(H_ACK, 2 /* ready */),
494                 MS_DBRA(-2 /* loop */),
495 /* error: */    MS_RET(1),
496 /* ready: */    MS_RET(0)
497         };
498
499         /* initialize the select microsequence */
500         ppb_MS_init_msq(select_microseq, 2,
501                         SELECT_TARGET, 1 << target,
502                         SELECT_INITIATOR, 1 << initiator);
503                                 
504         ppb_MS_microseq(ppbus, vpo->vpo_dev, select_microseq, &ret);
505
506         if (ret)
507                 return (VP0_ESELECT_TIMEOUT);
508
509         return (0);
510 }
511
512 /*
513  * vpoio_wait()
514  *
515  * H_SELIN must be low.
516  *
517  * XXX should be ported to microseq
518  */
519 static char
520 vpoio_wait(struct vpoio_data *vpo, int tmo)
521 {
522         DECLARE_WAIT_MICROSEQUENCE;
523
524         device_t ppbus = device_get_parent(vpo->vpo_dev);
525         int ret, err;
526
527 #if 0   /* broken */
528         if (ppb_poll_device(ppbus, 150, nBUSY, nBUSY, PPB_INTR))
529                 return (0);
530
531         return (ppb_rstr(ppbus) & 0xf0);
532 #endif
533
534         /*
535          * Return some status information.
536          * Semantics :  0xc0 = ZIP wants more data
537          *              0xd0 = ZIP wants to send more data
538          *              0xe0 = ZIP wants command
539          *              0xf0 = end of transfer, ZIP is sending status
540          */
541
542         ppb_MS_init_msq(wait_microseq, 2,
543                         WAIT_RET, (void *)&ret,
544                         WAIT_TMO, tmo);
545
546         ppb_MS_microseq(ppbus, vpo->vpo_dev, wait_microseq, &err);
547
548         if (err)
549                 return (0);      /* command timed out */        
550
551         return(ret);
552 }
553
554 /*
555  * vpoio_probe()
556  *
557  * Low level probe of vpo device
558  *
559  */
560 int
561 vpoio_probe(device_t dev, struct vpoio_data *vpo)
562 {
563         int error;
564
565         /* ppbus dependent initialisation */
566         vpo->vpo_dev = dev;
567
568         /*
569          * Initialize microsequence code
570          */
571         INIT_TRIG_MICROSEQ;
572
573         /* now, try to initialise the drive */
574         if ((error = vpoio_detect(vpo))) {
575                 return (error);
576         }
577
578         return (0);
579 }
580
581 /*
582  * vpoio_attach()
583  *
584  * Low level attachment of vpo device
585  *
586  */
587 int
588 vpoio_attach(struct vpoio_data *vpo)
589 {
590         DECLARE_NIBBLE_INBYTE_SUBMICROSEQ;      
591         device_t ppbus = device_get_parent(vpo->vpo_dev);
592         int error = 0;
593
594         vpo->vpo_nibble_inbyte_msq = (struct ppb_microseq *)malloc(
595                 sizeof(nibble_inbyte_submicroseq), M_DEVBUF, M_NOWAIT);
596
597         if (!vpo->vpo_nibble_inbyte_msq)
598                 return (ENXIO);
599
600         bcopy((void *)nibble_inbyte_submicroseq,
601                 (void *)vpo->vpo_nibble_inbyte_msq,
602                 sizeof(nibble_inbyte_submicroseq));
603
604         ppb_MS_init_msq(vpo->vpo_nibble_inbyte_msq, 4,
605                 INB_NIBBLE_H, (void *)&(vpo)->vpo_nibble.h,
606                 INB_NIBBLE_L, (void *)&(vpo)->vpo_nibble.l,
607                 INB_NIBBLE_F, nibble_inbyte_hook,
608                 INB_NIBBLE_P, (void *)&(vpo)->vpo_nibble); 
609
610         /*
611          * Initialize mode dependent in/out microsequences
612          */
613         if ((error = ppb_request_bus(ppbus, vpo->vpo_dev, PPB_WAIT)))
614                 goto error;
615
616         /* ppbus sets automatically the last mode entered during detection */
617         switch (vpo->vpo_mode_found) {
618         case VP0_MODE_EPP:
619                 ppb_MS_GET_init(ppbus, vpo->vpo_dev, epp17_instr_body);
620                 ppb_MS_PUT_init(ppbus, vpo->vpo_dev, epp17_outstr_body);
621                 printf("vpo%d: EPP mode\n", vpo->vpo_unit);
622                 break;
623         case VP0_MODE_PS2:
624                 ppb_MS_GET_init(ppbus, vpo->vpo_dev, ps2_inbyte_submicroseq);
625                 ppb_MS_PUT_init(ppbus, vpo->vpo_dev, spp_outbyte_submicroseq);
626                 printf("vpo%d: PS2 mode\n", vpo->vpo_unit);
627                 break;
628         case VP0_MODE_NIBBLE:
629                 ppb_MS_GET_init(ppbus, vpo->vpo_dev, vpo->vpo_nibble_inbyte_msq);
630                 ppb_MS_PUT_init(ppbus, vpo->vpo_dev, spp_outbyte_submicroseq);
631                 printf("vpo%d: NIBBLE mode\n", vpo->vpo_unit);
632                 break;
633         default:
634                 panic("vpo: unknown mode %d", vpo->vpo_mode_found);
635         }
636
637         ppb_release_bus(ppbus, vpo->vpo_dev);
638
639 error:
640         return (error);
641 }
642
643 /*
644  * vpoio_reset_bus()
645  *
646  */
647 int
648 vpoio_reset_bus(struct vpoio_data *vpo)
649 {
650         /* first, connect to the drive */
651         if (vpoio_connect(vpo, PPB_WAIT|PPB_INTR) || !vpoio_in_disk_mode(vpo)) {
652
653 #ifdef VP0_DEBUG
654                 printf("%s: not in disk mode!\n", __func__);
655 #endif
656                 /* release ppbus */
657                 vpoio_disconnect(vpo);
658                 return (1);
659         }
660
661         /* reset the SCSI bus */
662         vpoio_reset(vpo);
663
664         /* then disconnect */
665         vpoio_disconnect(vpo);
666
667         return (0);
668 }
669
670 /*
671  * vpoio_do_scsi()
672  *
673  * Send an SCSI command
674  *
675  */
676 int 
677 vpoio_do_scsi(struct vpoio_data *vpo, int host, int target, char *command,
678                 int clen, char *buffer, int blen, int *result, int *count,
679                 int *ret)
680 {
681         device_t ppbus = device_get_parent(vpo->vpo_dev);
682         register char r;
683         char l, h = 0;
684         int len, error = 0;
685         register int k;
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 = vpoio_connect(vpo, PPB_WAIT|PPB_INTR)))
696                 return (error);
697
698         if (!vpoio_in_disk_mode(vpo)) {
699                 *ret = VP0_ECONNECT; goto error;
700         }
701
702         if ((*ret = vpoio_select(vpo,host,target)))
703                 goto error;
704
705         /*
706          * Send the command ...
707          *
708          * set H_SELIN low for vpoio_wait().
709          */
710         ppb_wctr(ppbus, H_AUTO | H_nSELIN | H_INIT | H_STROBE);
711
712         for (k = 0; k < clen; k++) {
713                 if (vpoio_wait(vpo, VP0_FAST_SPINTMO) != (char)0xe0) {
714                         *ret = VP0_ECMD_TIMEOUT;
715                         goto error;
716                 }
717                 if (vpoio_outstr(vpo, &command[k], 1)) {
718                         *ret = VP0_EPPDATA_TIMEOUT;
719                         goto error;
720                 }
721         }
722
723         /* 
724          * Completion ... 
725          */
726
727         *count = 0;
728         for (;;) {
729
730                 if (!(r = vpoio_wait(vpo, VP0_LOW_SPINTMO))) {
731                         *ret = VP0_ESTATUS_TIMEOUT; goto error;
732                 }
733
734                 /* stop when the ZIP wants to send status */
735                 if (r == (char)0xf0)
736                         break;
737
738                 if (*count >= blen) {
739                         *ret = VP0_EDATA_OVERFLOW;
740                         goto error;
741                 }
742
743                 /* if in EPP mode or writing bytes, try to transfer a sector
744                  * otherwise, just send one byte
745                  */
746                 if (PPB_IN_EPP_MODE(ppbus) || r == (char)0xc0)
747                         len = (((blen - *count) >= VP0_SECTOR_SIZE)) ?
748                                 VP0_SECTOR_SIZE : 1;
749                 else
750                         len = 1;
751
752                 /* ZIP wants to send data? */
753                 if (r == (char)0xc0)
754                         error = vpoio_outstr(vpo, &buffer[*count], len);
755                 else
756                         error = vpoio_instr(vpo, &buffer[*count], len);
757
758                 if (error) {
759                         *ret = error;
760                         goto error;
761                 }
762
763                 *count += len;
764         }
765
766         if (vpoio_instr(vpo, &l, 1)) {
767                 *ret = VP0_EOTHER; goto error;
768         }
769
770         /* check if the ZIP wants to send more status */
771         if (vpoio_wait(vpo, VP0_FAST_SPINTMO) == (char)0xf0)
772                 if (vpoio_instr(vpo, &h, 1)) {
773                         *ret = VP0_EOTHER+2; goto error;
774                 }
775
776         *result = ((int) h << 8) | ((int) l & 0xff);
777
778 error:
779         /* return to printer state, release the ppbus */
780         vpoio_disconnect(vpo);
781         return (0);
782 }