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