]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/ppbus/immio.c
unfinished sblive driver, playback/mixer only for now - not enabled in
[FreeBSD/FreeBSD.git] / sys / dev / ppbus / immio.c
1 /*-
2  * Copyright (c) 1998, 1999 Nicolas Souchu
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * $FreeBSD$
27  *
28  */
29
30 /*
31  * Iomega ZIP+ Matchmaker Parallel Port Interface driver
32  *
33  * Thanks to David Campbell work on the Linux driver and the Iomega specs
34  * Thanks to Thiebault Moeglin for the drive
35  */
36 #ifdef _KERNEL
37 #include <sys/param.h>
38 #include <sys/systm.h>
39 #include <sys/module.h>
40 #include <sys/bus.h>
41 #include <sys/malloc.h>
42 #include <sys/buf.h>
43
44 #include <machine/clock.h>
45
46 #endif  /* _KERNEL */
47
48 #ifdef  _KERNEL
49 #include <sys/kernel.h>
50 #endif /* _KERNEL */
51
52 #include "opt_vpo.h"
53
54 #include <dev/ppbus/ppbio.h>
55 #include <dev/ppbus/ppbconf.h>
56 #include <dev/ppbus/ppb_msq.h>
57 #include <dev/ppbus/vpoio.h>
58 #include <dev/ppbus/ppb_1284.h>
59
60 #include "ppbus_if.h"
61
62 #define VP0_SELTMO              5000    /* select timeout */
63 #define VP0_FAST_SPINTMO        500000  /* wait status timeout */
64 #define VP0_LOW_SPINTMO         5000000 /* wait status timeout */
65
66 #define VP0_SECTOR_SIZE 512
67
68 /*
69  * Microcode to execute very fast I/O sequences at the lowest bus level.
70  */
71
72 #define SELECT_TARGET           MS_PARAM(6, 1, MS_TYP_CHA)
73
74 #define DECLARE_SELECT_MICROSEQUENCE                                    \
75 struct ppb_microseq select_microseq[] = {                               \
76         MS_CASS(0xc),                                                   \
77         /* first, check there is nothing holding onto the bus */        \
78         MS_SET(VP0_SELTMO),                                             \
79 /* _loop: */                                                            \
80         MS_BRCLEAR(0x8, 2 /* _ready */),                                \
81         MS_DBRA(-2 /* _loop */),                                        \
82         MS_RET(2),                      /* bus busy */                  \
83 /* _ready: */                                                           \
84         MS_CASS(0x4),                                                   \
85         MS_DASS(MS_UNKNOWN /* 0x80 | 1 << target */),                   \
86         MS_DELAY(1),                                                    \
87         MS_CASS(0xc),                                                   \
88         MS_CASS(0xd),                                                   \
89         /* now, wait until the drive is ready */                        \
90         MS_SET(VP0_SELTMO),                                             \
91 /* loop: */                                                             \
92         MS_BRSET(0x8, 3 /* ready */),                                   \
93         MS_DBRA(-2 /* loop */),                                         \
94 /* error: */                                                            \
95         MS_CASS(0xc),                                                   \
96         MS_RET(VP0_ESELECT_TIMEOUT),                                    \
97 /* ready: */                                                            \
98         MS_CASS(0xc),                                                   \
99         MS_RET(0)                                                       \
100 }
101
102 static struct ppb_microseq transfer_epilog[] = {
103         MS_CASS(0x4),
104         MS_CASS(0xc),
105         MS_CASS(0xe),
106         MS_CASS(0x4),
107         MS_RET(0)
108 };
109
110 #define CPP_S1          MS_PARAM(10, 2, MS_TYP_PTR)
111 #define CPP_S2          MS_PARAM(13, 2, MS_TYP_PTR)
112 #define CPP_S3          MS_PARAM(16, 2, MS_TYP_PTR)
113 #define CPP_PARAM       MS_PARAM(17, 1, MS_TYP_CHA)
114
115 #define DECLARE_CPP_MICROSEQ \
116 struct ppb_microseq cpp_microseq[] = {                                  \
117         MS_CASS(0x0c), MS_DELAY(2),                                     \
118         MS_DASS(0xaa), MS_DELAY(10),                                    \
119         MS_DASS(0x55), MS_DELAY(10),                                    \
120         MS_DASS(0x00), MS_DELAY(10),                                    \
121         MS_DASS(0xff), MS_DELAY(10),                                    \
122         MS_RFETCH(MS_REG_STR, 0xb8, MS_UNKNOWN /* &s1 */),              \
123         MS_DASS(0x87), MS_DELAY(10),                                    \
124         MS_RFETCH(MS_REG_STR, 0xb8, MS_UNKNOWN /* &s2 */),              \
125         MS_DASS(0x78), MS_DELAY(10),                                    \
126         MS_RFETCH(MS_REG_STR, 0x38, MS_UNKNOWN /* &s3 */),              \
127         MS_DASS(MS_UNKNOWN /* param */),                                \
128         MS_DELAY(2),                                                    \
129         MS_CASS(0x0c), MS_DELAY(10),                                    \
130         MS_CASS(0x0d), MS_DELAY(2),                                     \
131         MS_CASS(0x0c), MS_DELAY(10),                                    \
132         MS_DASS(0xff), MS_DELAY(10),                                    \
133         MS_RET(0)                                                       \
134 }
135
136 #define NEGOCIATED_MODE         MS_PARAM(2, 1, MS_TYP_CHA)
137
138 #define DECLARE_NEGOCIATE_MICROSEQ \
139 static struct ppb_microseq negociate_microseq[] = {                     \
140         MS_CASS(0x4),                                                   \
141         MS_DELAY(5),                                                    \
142         MS_DASS(MS_UNKNOWN /* mode */),                                 \
143         MS_DELAY(100),                                                  \
144         MS_CASS(0x6),                                                   \
145         MS_DELAY(5),                                                    \
146         MS_BRSET(0x20, 5 /* continue */),                               \
147         MS_DELAY(5),                                                    \
148         MS_CASS(0x7),                                                   \
149         MS_DELAY(5),                                                    \
150         MS_CASS(0x6),                                                   \
151         MS_RET(VP0_ENEGOCIATE),                                         \
152 /* continue: */                                                         \
153         MS_DELAY(5),                                                    \
154         MS_CASS(0x7),                                                   \
155         MS_DELAY(5),                                                    \
156         MS_CASS(0x6),                                                   \
157         MS_RET(0)                                                       \
158 }
159
160 static struct ppb_microseq reset_microseq[] = {
161         MS_CASS(0x04),
162         MS_DASS(0x40),
163         MS_DELAY(1),
164         MS_CASS(0x0c),
165         MS_CASS(0x0d),
166         MS_DELAY(50),
167         MS_CASS(0x0c),
168         MS_CASS(0x04),
169         MS_RET(0)
170 };
171
172 /*
173  * nibble_inbyte_hook()
174  *
175  * Formats high and low nibble into a character
176  */
177 static int
178 nibble_inbyte_hook (void *p, char *ptr)
179 {
180         struct vpo_nibble *s = (struct vpo_nibble *)p;
181
182         /* increment the buffer pointer */
183         *ptr = ((s->l >> 4) & 0x0f) + (s->h & 0xf0);
184
185         return (0);
186 }
187
188 /*
189  * Macro used to initialize each vpoio_data structure during
190  * low level attachment
191  *
192  * XXX should be converted to ppb_MS_init_msq()
193  */
194 #define INIT_NIBBLE_INBYTE_SUBMICROSEQ(vpo) {                   \
195         (vpo)->vpo_nibble_inbyte_msq[6].arg[2].p =              \
196                         (void *)&(vpo)->vpo_nibble.h;           \
197         (vpo)->vpo_nibble_inbyte_msq[3].arg[2].p =              \
198                         (void *)&(vpo)->vpo_nibble.l;           \
199         (vpo)->vpo_nibble_inbyte_msq[9].arg[0].f =              \
200                         nibble_inbyte_hook;                     \
201         (vpo)->vpo_nibble_inbyte_msq[9].arg[1].p =              \
202                         (void *)&(vpo)->vpo_nibble;             \
203 }
204
205 /*
206  * This is the sub-microseqence for MS_GET in NIBBLE mode
207  * Retrieve the two nibbles and call the C function to generate the character
208  * and store it in the buffer (see nibble_inbyte_hook())
209  */
210 static struct ppb_microseq nibble_inbyte_submicroseq[] = {
211           MS_CASS(0x4),
212
213 /* loop: */
214           MS_CASS(0x6),
215           MS_DELAY(1),
216           MS_RFETCH(MS_REG_STR, MS_FETCH_ALL, MS_UNKNOWN /* low nibble */),
217           MS_CASS(0x5),
218           MS_DELAY(1),
219           MS_RFETCH(MS_REG_STR, MS_FETCH_ALL, MS_UNKNOWN /* high nibble */),
220           MS_CASS(0x4),
221           MS_DELAY(1),
222
223           /* do a C call to format the received nibbles */
224           MS_C_CALL(MS_UNKNOWN /* C hook */, MS_UNKNOWN /* param */),
225           MS_DBRA(-7 /* loop */),
226           MS_RET(0)
227 };
228
229 /*
230  * This is the sub-microseqence for MS_GET in PS2 mode
231  */
232 static struct ppb_microseq ps2_inbyte_submicroseq[] = {
233           MS_CASS(0x4),
234
235 /* loop: */
236           MS_CASS(PCD | 0x6),
237           MS_RFETCH_P(1, MS_REG_DTR, MS_FETCH_ALL),
238           MS_CASS(PCD | 0x5),
239           MS_DBRA(-4 /* loop */),
240
241           MS_RET(0)
242 };
243
244 /*
245  * This is the sub-microsequence for MS_PUT in both NIBBLE and PS2 modes
246  */
247 static struct ppb_microseq spp_outbyte_submicroseq[] = {
248           MS_CASS(0x4),
249
250 /* loop: */
251           MS_RASSERT_P(1, MS_REG_DTR), 
252           MS_CASS(0x5),
253           MS_DBRA(0),                           /* decrement counter */
254           MS_RASSERT_P(1, MS_REG_DTR), 
255           MS_CASS(0x0),
256           MS_DBRA(-6 /* loop */),
257
258           /* return from the put call */
259           MS_CASS(0x4),
260           MS_RET(0)
261 };
262
263 /* EPP 1.7 microsequences, ptr and len set at runtime */
264 static struct ppb_microseq epp17_outstr[] = {
265           MS_CASS(0x4),
266           MS_RASSERT_P(MS_ACCUM, MS_REG_EPP_D), 
267           MS_CASS(0xc),
268           MS_RET(0),
269 };
270
271 static struct ppb_microseq epp17_instr[] = {
272           MS_CASS(PCD | 0x4),
273           MS_RFETCH_P(MS_ACCUM, MS_REG_EPP_D, MS_FETCH_ALL), 
274           MS_CASS(PCD | 0xc),
275           MS_RET(0),
276 };
277
278 static int
279 imm_disconnect(struct vpoio_data *vpo, int *connected, int release_bus)
280 {
281         DECLARE_CPP_MICROSEQ;
282
283         device_t ppbus = device_get_parent(vpo->vpo_dev);
284         char s1, s2, s3;
285         int ret;
286
287         /* all should be ok */
288         if (connected)
289                 *connected = 0;
290
291         ppb_MS_init_msq(cpp_microseq, 4, CPP_S1, (void *)&s1,
292                         CPP_S2, (void *)&s2, CPP_S3, (void *)&s3,
293                         CPP_PARAM, 0x30);
294
295         ppb_MS_microseq(ppbus, vpo->vpo_dev, cpp_microseq, &ret);
296
297         if ((s1 != (char)0xb8 || s2 != (char)0x18 || s3 != (char)0x38)) {
298                 if (bootverbose)
299                         printf("imm%d: (disconnect) s1=0x%x s2=0x%x, s3=0x%x\n",
300                                 vpo->vpo_unit, s1 & 0xff, s2 & 0xff, s3 & 0xff);
301                 if (connected)
302                         *connected = VP0_ECONNECT;
303         }
304
305         if (release_bus)
306                 return (ppb_release_bus(ppbus, vpo->vpo_dev));
307         else
308                 return (0);
309 }
310
311 /*
312  * how  : PPB_WAIT or PPB_DONTWAIT
313  */
314 static int
315 imm_connect(struct vpoio_data *vpo, int how, int *disconnected, int request_bus)
316 {
317         DECLARE_CPP_MICROSEQ;
318
319         device_t ppbus = device_get_parent(vpo->vpo_dev);
320         char s1, s2, s3;
321         int error;
322         int ret;
323
324         /* all should be ok */
325         if (disconnected)
326                 *disconnected = 0;
327
328         if (request_bus)
329                 if ((error = ppb_request_bus(ppbus, vpo->vpo_dev, how)))
330                         return (error);
331
332         ppb_MS_init_msq(cpp_microseq, 3, CPP_S1, (void *)&s1,
333                         CPP_S2, (void *)&s2, CPP_S3, (void *)&s3);
334
335         /* select device 0 in compatible mode */
336         ppb_MS_init_msq(cpp_microseq, 1, CPP_PARAM, 0xe0);
337         ppb_MS_microseq(ppbus, vpo->vpo_dev, cpp_microseq, &ret);
338
339         /* disconnect all devices */
340         ppb_MS_init_msq(cpp_microseq, 1, CPP_PARAM, 0x30);
341         ppb_MS_microseq(ppbus, vpo->vpo_dev, cpp_microseq, &ret);
342
343         if (PPB_IN_EPP_MODE(ppbus))
344                 ppb_MS_init_msq(cpp_microseq, 1, CPP_PARAM, 0x28);
345         else
346                 ppb_MS_init_msq(cpp_microseq, 1, CPP_PARAM, 0xe0);
347
348         ppb_MS_microseq(ppbus, vpo->vpo_dev, cpp_microseq, &ret);
349
350         if ((s1 != (char)0xb8 || s2 != (char)0x18 || s3 != (char)0x30)) {
351                 if (bootverbose)
352                         printf("imm%d: (connect) s1=0x%x s2=0x%x, s3=0x%x\n",
353                                 vpo->vpo_unit, s1 & 0xff, s2 & 0xff, s3 & 0xff);
354                 if (disconnected)
355                         *disconnected = VP0_ECONNECT;
356         }
357
358         return (0);
359 }
360
361 /*
362  * imm_detect()
363  *
364  * Detect and initialise the VP0 adapter.
365  */
366 static int
367 imm_detect(struct vpoio_data *vpo)
368 {
369         device_t ppbus = device_get_parent(vpo->vpo_dev);
370         int error;
371
372         if ((error = ppb_request_bus(ppbus, vpo->vpo_dev, PPB_DONTWAIT)))
373                 return (error);
374
375         /* disconnect the drive, keep the bus */
376         imm_disconnect(vpo, NULL, 0);
377
378         /* we already have the bus, just connect */
379         imm_connect(vpo, PPB_DONTWAIT, &error, 0);
380
381         if (error) {
382                 if (bootverbose)
383                         printf("imm%d: can't connect to the drive\n",
384                                 vpo->vpo_unit);
385                 goto error;
386         }
387
388         /* send SCSI reset signal */
389         ppb_MS_microseq(ppbus, vpo->vpo_dev, reset_microseq, NULL);
390
391         /* release the bus now */
392         imm_disconnect(vpo, &error, 1);
393
394         /* ensure we are disconnected or daisy chained peripheral 
395          * may cause serious problem to the disk */
396
397         if (error) {
398                 if (bootverbose)
399                         printf("imm%d: can't disconnect from the drive\n",
400                                 vpo->vpo_unit);
401                 goto error;
402         }
403
404         return (0);
405
406 error:
407         ppb_release_bus(ppbus, vpo->vpo_dev);
408         return (VP0_EINITFAILED);
409 }
410
411 /*
412  * imm_outstr()
413  */
414 static int
415 imm_outstr(struct vpoio_data *vpo, char *buffer, int size)
416 {
417         device_t ppbus = device_get_parent(vpo->vpo_dev);
418         int error = 0;
419
420         if (PPB_IN_EPP_MODE(ppbus))
421                 ppb_reset_epp_timeout(ppbus);
422
423         ppb_MS_exec(ppbus, vpo->vpo_dev, MS_OP_PUT, (union ppb_insarg)buffer,
424                 (union ppb_insarg)size, (union ppb_insarg)MS_UNKNOWN, &error);
425
426         return (error);
427 }
428
429 /*
430  * imm_instr()
431  */
432 static int
433 imm_instr(struct vpoio_data *vpo, char *buffer, int size)
434 {
435         device_t ppbus = device_get_parent(vpo->vpo_dev);
436         int error = 0;
437
438         if (PPB_IN_EPP_MODE(ppbus))
439                 ppb_reset_epp_timeout(ppbus);
440
441         ppb_MS_exec(ppbus, vpo->vpo_dev, MS_OP_GET, (union ppb_insarg)buffer,
442                 (union ppb_insarg)size, (union ppb_insarg)MS_UNKNOWN, &error);
443
444         return (error);
445 }
446
447 static char
448 imm_select(struct vpoio_data *vpo, int initiator, int target)
449 {
450         DECLARE_SELECT_MICROSEQUENCE;
451         device_t ppbus = device_get_parent(vpo->vpo_dev);
452         int ret;
453
454         /* initialize the select microsequence */
455         ppb_MS_init_msq(select_microseq, 1,
456                         SELECT_TARGET, 1 << initiator | 1 << target);
457                                 
458         ppb_MS_microseq(ppbus, vpo->vpo_dev, select_microseq, &ret);
459
460         return (ret);
461 }
462
463 /*
464  * imm_wait()
465  *
466  * H_SELIN must be low.
467  *
468  * XXX should be ported to microseq
469  */
470 static char
471 imm_wait(struct vpoio_data *vpo, int tmo)
472 {
473         device_t ppbus = device_get_parent(vpo->vpo_dev);
474         register int    k;
475         register char   r;
476
477         ppb_wctr(ppbus, 0xc);
478
479         /* XXX should be ported to microseq */
480         k = 0;
481         while (!((r = ppb_rstr(ppbus)) & 0x80) && (k++ < tmo))
482                 DELAY(1);
483
484         /*
485          * Return some status information.
486          * Semantics :  0x88 = ZIP+ wants more data
487          *              0x98 = ZIP+ wants to send more data
488          *              0xa8 = ZIP+ wants command
489          *              0xb8 = end of transfer, ZIP+ is sending status
490          */
491         ppb_wctr(ppbus, 0x4);
492         if (k < tmo)
493           return (r & 0xb8);
494
495         return (0);                        /* command timed out */      
496 }
497
498 static int
499 imm_negociate(struct vpoio_data *vpo)
500 {
501         DECLARE_NEGOCIATE_MICROSEQ;
502         device_t ppbus = device_get_parent(vpo->vpo_dev);
503         int negociate_mode;
504         int ret;
505
506         if (PPB_IN_NIBBLE_MODE(ppbus))
507                 negociate_mode = 0;
508         else if (PPB_IN_PS2_MODE(ppbus))
509                 negociate_mode = 1;
510         else
511                 return (0);
512
513 #if 0 /* XXX use standalone code not to depend on ppb_1284 code yet */
514         ret = ppb_1284_negociate(ppbus, negociate_mode);
515
516         if (ret)
517                 return (VP0_ENEGOCIATE);
518 #endif
519         
520         ppb_MS_init_msq(negociate_microseq, 1,
521                         NEGOCIATED_MODE, negociate_mode);
522
523         ppb_MS_microseq(ppbus, vpo->vpo_dev, negociate_microseq, &ret);
524
525         return (ret);
526 }
527
528 /*
529  * imm_probe()
530  *
531  * Low level probe of vpo device
532  *
533  */
534 int
535 imm_probe(device_t dev, struct vpoio_data *vpo)
536 {
537         int error;
538
539         /* ppbus dependent initialisation */
540         vpo->vpo_dev = dev;
541
542         /* now, try to initialise the drive */
543         if ((error = imm_detect(vpo))) {
544                 return (error);
545         }
546
547         return (0);
548 }
549
550 /*
551  * imm_attach()
552  *
553  * Low level attachment of vpo device
554  *
555  */
556 int
557 imm_attach(struct vpoio_data *vpo)
558 {
559         device_t ppbus = device_get_parent(vpo->vpo_dev);
560         int epp;
561
562         /*
563          * Initialize microsequence code
564          */
565         vpo->vpo_nibble_inbyte_msq = (struct ppb_microseq *)malloc(
566                 sizeof(nibble_inbyte_submicroseq), M_DEVBUF, M_NOWAIT);
567
568         if (!vpo->vpo_nibble_inbyte_msq)
569                 return (ENXIO);
570
571         bcopy((void *)nibble_inbyte_submicroseq,
572                 (void *)vpo->vpo_nibble_inbyte_msq,
573                 sizeof(nibble_inbyte_submicroseq));
574
575         INIT_NIBBLE_INBYTE_SUBMICROSEQ(vpo);
576
577         /*
578          * Initialize mode dependent in/out microsequences
579          */
580         ppb_request_bus(ppbus, vpo->vpo_dev, PPB_WAIT);
581
582         /* enter NIBBLE mode to configure submsq */
583         if (ppb_set_mode(ppbus, PPB_NIBBLE) != -1) {
584
585                 ppb_MS_GET_init(ppbus, vpo->vpo_dev, vpo->vpo_nibble_inbyte_msq);
586                 ppb_MS_PUT_init(ppbus, vpo->vpo_dev, spp_outbyte_submicroseq);
587         }
588
589         /* enter PS2 mode to configure submsq */
590         if (ppb_set_mode(ppbus, PPB_PS2) != -1) {
591
592                 ppb_MS_GET_init(ppbus, vpo->vpo_dev, ps2_inbyte_submicroseq);
593                 ppb_MS_PUT_init(ppbus, vpo->vpo_dev, spp_outbyte_submicroseq);
594         }
595
596         epp = ppb_get_epp_protocol(ppbus);
597
598         /* enter EPP mode to configure submsq */
599         if (ppb_set_mode(ppbus, PPB_EPP) != -1) {
600
601                 switch (epp) {
602                 case EPP_1_9:
603                 case EPP_1_7:
604                         ppb_MS_GET_init(ppbus, vpo->vpo_dev, epp17_instr);
605                         ppb_MS_PUT_init(ppbus, vpo->vpo_dev, epp17_outstr);
606                         break;
607                 default:
608                         panic("%s: unknown EPP protocol (0x%x)", __FUNCTION__,
609                                 epp);
610                 }
611         }
612
613         /* try to enter EPP or PS/2 mode, NIBBLE otherwise */
614         if (ppb_set_mode(ppbus, PPB_EPP) != -1) {
615                 switch (epp) {
616                 case EPP_1_9:
617                         printf("imm%d: EPP 1.9 mode\n", vpo->vpo_unit);
618                         break;
619                 case EPP_1_7:
620                         printf("imm%d: EPP 1.7 mode\n", vpo->vpo_unit);
621                         break;
622                 default:
623                         panic("%s: unknown EPP protocol (0x%x)", __FUNCTION__,
624                                 epp);
625                 }
626         } else if (ppb_set_mode(ppbus, PPB_PS2) != -1)
627                 printf("imm%d: PS2 mode\n", vpo->vpo_unit);
628
629         else if (ppb_set_mode(ppbus, PPB_NIBBLE) != -1)
630                 printf("imm%d: NIBBLE mode\n", vpo->vpo_unit);
631
632         else {
633                 printf("imm%d: can't enter NIBBLE, PS2 or EPP mode\n",
634                         vpo->vpo_unit);
635
636                 ppb_release_bus(ppbus, vpo->vpo_dev);
637
638                 free(vpo->vpo_nibble_inbyte_msq, M_DEVBUF);
639                 return (ENXIO);
640         }
641
642         ppb_release_bus(ppbus, vpo->vpo_dev);
643
644         return (0);
645 }
646
647 /*
648  * imm_reset_bus()
649  *
650  */
651 int
652 imm_reset_bus(struct vpoio_data *vpo)
653 {
654         device_t ppbus = device_get_parent(vpo->vpo_dev);
655         int disconnected;
656
657         /* first, connect to the drive and request the bus */
658         imm_connect(vpo, PPB_WAIT|PPB_INTR, &disconnected, 1);
659
660         if (!disconnected) {
661
662                 /* reset the SCSI bus */
663                 ppb_MS_microseq(ppbus, vpo->vpo_dev, reset_microseq, NULL);
664
665                 /* then disconnect */
666                 imm_disconnect(vpo, NULL, 1);
667         }
668
669         return (0);
670 }
671
672 /*
673  * imm_do_scsi()
674  *
675  * Send an SCSI command
676  *
677  */
678 int 
679 imm_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         register char r;
685         char l, h = 0;
686         int len, error = 0, not_connected = 0;
687         register int k;
688         int negociated = 0;
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 = imm_connect(vpo, PPB_WAIT|PPB_INTR, &not_connected, 1)))
699                 return (error);
700
701         if (not_connected) {
702                 *ret = VP0_ECONNECT; goto error;
703         }
704
705         /*
706          * Select the drive ...
707          */
708         if ((*ret = imm_select(vpo,host,target)))
709                 goto error;
710
711         /*
712          * Send the command ...
713          */
714         for (k = 0; k < clen; k+=2) {
715                 if (imm_wait(vpo, VP0_FAST_SPINTMO) != (char)0xa8) {
716                         *ret = VP0_ECMD_TIMEOUT;
717                         goto error;
718                 }
719                 if (imm_outstr(vpo, &command[k], 2)) {
720                         *ret = VP0_EPPDATA_TIMEOUT;
721                         goto error;
722                 }
723         }
724
725         if (!(r = imm_wait(vpo, VP0_LOW_SPINTMO))) {
726                 *ret = VP0_ESTATUS_TIMEOUT; goto error;
727         }
728
729         if ((r & 0x30) == 0x10) {
730                 if (imm_negociate(vpo)) {
731                         *ret = VP0_ENEGOCIATE;
732                         goto error;
733                 } else
734                         negociated = 1;
735         }
736
737         /* 
738          * Complete transfer ... 
739          */
740         *count = 0;
741         for (;;) {
742
743                 if (!(r = imm_wait(vpo, VP0_LOW_SPINTMO))) {
744                         *ret = VP0_ESTATUS_TIMEOUT; goto error;
745                 }
746
747                 /* stop when the ZIP+ wants to send status */
748                 if (r == (char)0xb8)
749                         break;
750
751                 if (*count >= blen) {
752                         *ret = VP0_EDATA_OVERFLOW;
753                         goto error;
754                 }
755
756                 /* ZIP+ wants to send data? */
757                 if (r == (char)0x88) {
758                         len = (((blen - *count) >= VP0_SECTOR_SIZE)) ?
759                                 VP0_SECTOR_SIZE : 2;
760
761                         error = imm_outstr(vpo, &buffer[*count], len);
762                 } else {
763                         if (!PPB_IN_EPP_MODE(ppbus))
764                                 len = 1;
765                         else
766                                 len = (((blen - *count) >= VP0_SECTOR_SIZE)) ?
767                                         VP0_SECTOR_SIZE : 1;
768
769                         error = imm_instr(vpo, &buffer[*count], len);
770                 }
771
772                 if (error) {
773                         *ret = error;
774                         goto error;
775                 }
776
777                 *count += len;
778         }
779
780         if ((PPB_IN_NIBBLE_MODE(ppbus) ||
781                         PPB_IN_PS2_MODE(ppbus)) && negociated)
782                 ppb_MS_microseq(ppbus, vpo->vpo_dev, transfer_epilog, NULL);
783
784         /*
785          * Retrieve status ...
786          */
787         if (imm_negociate(vpo)) {
788                 *ret = VP0_ENEGOCIATE;
789                 goto error;
790         } else
791                 negociated = 1;
792
793         if (imm_instr(vpo, &l, 1)) {
794                 *ret = VP0_EOTHER; goto error;
795         }
796
797         /* check if the ZIP+ wants to send more status */
798         if (imm_wait(vpo, VP0_FAST_SPINTMO) == (char)0xb8)
799                 if (imm_instr(vpo, &h, 1)) {
800                         *ret = VP0_EOTHER+2; goto error;
801                 }
802
803         *result = ((int) h << 8) | ((int) l & 0xff);
804
805 error:
806         if ((PPB_IN_NIBBLE_MODE(ppbus) ||
807                         PPB_IN_PS2_MODE(ppbus)) && negociated)
808                 ppb_MS_microseq(ppbus, vpo->vpo_dev, transfer_epilog, NULL);
809
810         /* return to printer state, release the ppbus */
811         imm_disconnect(vpo, NULL, 1);
812
813         return (0);
814 }