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