xref: /dragonfly/sys/dev/disk/vpo/immio.c (revision 6a8bb22da78bc713f3d5504bcd702f4e3a8ba2b8)
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 NEGOTIATED_MODE                 MS_PARAM(2, 1, MS_TYP_CHA)
148 
149 #define DECLARE_NEGOTIATE_MICROSEQ \
150 struct ppb_microseq negotiate_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_ENEGOTIATE),                                                         \
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
nibble_inbyte_hook(void * p,char * ptr)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
imm_disconnect(struct vpoio_data * vpo,int * connected,int release_bus)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
imm_connect(struct vpoio_data * vpo,int how,int * disconnected,int request_bus)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
imm_detect(struct vpoio_data * vpo)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
imm_outstr(struct vpoio_data * vpo,char * _buffer,int _size)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
imm_instr(struct vpoio_data * vpo,char * _buffer,int _size)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
imm_select(struct vpoio_data * vpo,int initiator,int target)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
imm_wait(struct vpoio_data * vpo,int tmo)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
imm_negotiate(struct vpoio_data * vpo)523 imm_negotiate(struct vpoio_data *vpo)
524 {
525           DECLARE_NEGOTIATE_MICROSEQ;
526           device_t ppbus = device_get_parent(vpo->vpo_dev);
527           int negotiate_mode;
528           int ret;
529 
530           if (PPB_IN_NIBBLE_MODE(ppbus))
531                     negotiate_mode = 0;
532           else if (PPB_IN_PS2_MODE(ppbus))
533                     negotiate_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_negotiate(ppbus, negotiate_mode);
539 
540           if (ret)
541                     return (VP0_ENEGOTIATE);
542 #endif
543 
544           ppb_MS_init_msq(negotiate_microseq, 1,
545                               NEGOTIATED_MODE, negotiate_mode);
546 
547           ppb_MS_microseq(ppbus, vpo->vpo_dev, negotiate_microseq, &ret);
548 
549           return (ret);
550 }
551 
552 /*
553  * imm_probe()
554  *
555  * Low level probe of vpo device
556  *
557  */
558 int
imm_probe(device_t dev,struct vpoio_data * vpo)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
imm_attach(struct vpoio_data * vpo)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
imm_reset_bus(struct vpoio_data * vpo)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
imm_do_scsi(struct vpoio_data * vpo,int host,int target,char * command,int clen,char * buffer,int blen,int * result,int * count,int * ret)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 negotiated = 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_negotiate(vpo)) {
719                               *ret = VP0_ENEGOTIATE;
720                               goto error;
721                     } else
722                               negotiated = 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)) && negotiated)
770                     ppb_MS_microseq(ppbus, vpo->vpo_dev, transfer_epilog, NULL);
771 
772           /*
773            * Retrieve status ...
774            */
775           if (imm_negotiate(vpo)) {
776                     *ret = VP0_ENEGOTIATE;
777                     goto error;
778           } else
779                     negotiated = 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)) && negotiated)
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 }
807