1 /* ======================================================================== */
2 /*  Cartridge Reader Interface routines.                                    */
3 /*  By Joe Zbiciak                                                          */
4 /*  Using hardware designed by Scott Nudds                                  */
5 /*  (Based on routines from my own SUCK.BAS.)                               */
6 /* ======================================================================== */
7 
8 #include "config.h"
9 #include "cart.h"
10 
11 #if (!defined(PLAT_LINUX) && !defined(WIN32)) || !defined(i386)
cr_set_disp(cart_rd_t * cr,unsigned display)12 void cr_set_disp(cart_rd_t *cr, unsigned display) { UNUSED(cr); UNUSED(display); }
cr_set_ctrl(cart_rd_t * cr,unsigned control)13 void cr_set_ctrl(cart_rd_t *cr, unsigned control) { UNUSED(cr); UNUSED(control); }
cr_set_data(cart_rd_t * cr,unsigned value)14 void cr_set_data(cart_rd_t *cr, unsigned value) { UNUSED(cr); UNUSED(value); }
cr_deselect(cart_rd_t * cr)15 void cr_deselect(cart_rd_t *cr) { UNUSED(cr); }
cr_loopback(cart_rd_t * cr,unsigned value,unsigned * rd)16 int cr_loopback(cart_rd_t *cr, unsigned value, unsigned *rd)
17 {
18     UNUSED(cr); UNUSED(value); UNUSED(rd);
19     return -1;
20 }
cr_detect(unsigned port)21 unsigned cr_detect(unsigned port)
22 {
23     UNUSED(port);
24     return 0;
25 }
cr_selftest(cart_rd_t * cr,unsigned * w,unsigned * r)26 int cr_selftest(cart_rd_t *cr, unsigned *w, unsigned *r)
27 {
28     UNUSED(cr); UNUSED(w); UNUSED(r);
29     return -1;
30 }
cr_sleep(long len)31 void cr_sleep(long len) { UNUSED(len); }
cr_do_reset(cart_rd_t * cr)32 void cr_do_reset(cart_rd_t *cr) { UNUSED(cr); }
cr_do_read(cart_rd_t * cr,unsigned addr)33 unsigned cr_do_read(cart_rd_t *cr, unsigned addr)
34 {
35     UNUSED(cr); UNUSED(addr);
36     return 0;
37 }
cr_do_write(cart_rd_t * cr,unsigned addr,unsigned data)38 void cr_do_write(cart_rd_t *cr, unsigned addr, unsigned data)
39 {
40     UNUSED(cr); UNUSED(addr); UNUSED(data);
41 }
cr_init_ports(unsigned long base)42 void cr_init_ports(unsigned long base) { UNUSED(base); }
43 
44 
45 #else
46 
47 static unsigned long ports[3] = { 0x378, 0x278, 0x3BC }; /* Printer ports  */
48 
49 #define cr_outb(x,y) outb(y,x)
50 #define cr_inb(x)    inb(x)
51 
52 /* ------------------------------------------------------------------------ */
53 /*  CR_SET_DISP      -- Set the display register to an 8-bit value.         */
54 /* ------------------------------------------------------------------------ */
cr_set_disp(cart_rd_t * cr,unsigned display)55 void cr_set_disp(cart_rd_t *cr, unsigned display)
56 {
57     /* -------------------------------------------------------------------- */
58     /*  Remember this display word.                                         */
59     /* -------------------------------------------------------------------- */
60     cr->disp = display & 0xFF;
61 
62     /* -------------------------------------------------------------------- */
63     /*  Output the control word to the DISPLAY latch.                       */
64     /* -------------------------------------------------------------------- */
65     cr_outb(cr->port + 2, LAT_DEN | LAT_INH);
66     cr_outb(cr->port,     display & 0xFF);
67     cr_outb(cr->port + 2, LAT_DEN);
68     cr_outb(cr->port + 2, LAT_DEN | LAT_INH);
69     cr_outb(cr->port + 2, LAT_INH);
70 }
71 
72 /* ------------------------------------------------------------------------ */
73 /*  CR_SET_CTRL      -- Set the bus control lines to a given state.         */
74 /* ------------------------------------------------------------------------ */
cr_set_ctrl(cart_rd_t * cr,unsigned control)75 void cr_set_ctrl(cart_rd_t *cr, unsigned control)
76 {
77     /* -------------------------------------------------------------------- */
78     /*  Remember this control word.                                         */
79     /* -------------------------------------------------------------------- */
80     cr->ctrl = (control & ~DS9) | (cr->seg9 ? DS9 : 0);
81 
82     /* -------------------------------------------------------------------- */
83     /*  Output the control word to the CONTROL latch.                       */
84     /* -------------------------------------------------------------------- */
85     cr_outb(cr->port + 2, LAT_CEN | LAT_INH);
86     cr_outb(cr->port,     control & 0xFF);
87     cr_outb(cr->port + 2, LAT_CEN);
88     cr_outb(cr->port + 2, LAT_CEN | LAT_INH);
89     cr_outb(cr->port + 2, LAT_INH);
90 }
91 
92 /* ------------------------------------------------------------------------ */
93 /*  CR_SET_DATA      -- Set the data bus lines to a given value.            */
94 /* ------------------------------------------------------------------------ */
cr_set_data(cart_rd_t * cr,unsigned value)95 void cr_set_data(cart_rd_t *cr, unsigned value)
96 {
97     /* -------------------------------------------------------------------- */
98     /*  Output the high-half of the value.                                  */
99     /* -------------------------------------------------------------------- */
100     cr_outb(cr->port + 2, LAT_XEN | LAT_INH);
101     cr_outb(cr->port,     (value >> 8) & 0xFF);
102     cr_outb(cr->port + 2, LAT_XEN);
103     cr_outb(cr->port + 2, LAT_XEN | LAT_INH);
104 
105     /* -------------------------------------------------------------------- */
106     /*  Output the low-half of the value.                                   */
107     /* -------------------------------------------------------------------- */
108     cr_outb(cr->port + 2, LAT_YEN | LAT_INH);
109     cr_outb(cr->port,     value & 0xFF);
110     cr_outb(cr->port + 2, LAT_YEN);
111     cr_outb(cr->port + 2, LAT_YEN | LAT_INH);
112 }
113 
114 /* ------------------------------------------------------------------------ */
115 /*  CR_GET_DATA      -- Read the current values that are on the data bus.   */
116 /* ------------------------------------------------------------------------ */
cr_get_data(cart_rd_t * cr)117 unsigned cr_get_data(cart_rd_t *cr)
118 {
119     int i;
120     unsigned data = 0, temp;
121     unsigned old_ctrl = cr->ctrl;
122     unsigned ctrl = (cr->ctrl | SL | OC | CLK);
123     ctrl     = (    ctrl & ~DS9) | (cr->seg9 ? DS9 : 0);
124     old_ctrl = (old_ctrl & ~DS9) | (cr->seg9 ? DS9 : 0);
125 
126     /* -------------------------------------------------------------------- */
127     /*  Select the CONTROL latch.                                           */
128     /* -------------------------------------------------------------------- */
129     cr_outb(cr->port + 2, LAT_CEN | LAT_INH);
130     cr_outb(cr->port,     ctrl);
131     cr_outb(cr->port + 2, LAT_CEN);
132 
133     for (i = 0; i < 8; i++)
134     {
135         /* ---------------------------------------------------------------- */
136         /*  Set the control latch directly (done this way for speed)        */
137         /* ---------------------------------------------------------------- */
138         cr_outb(cr->port, ctrl      );
139         cr_outb(cr->port, ctrl ^ CLK);
140 
141         /* ---------------------------------------------------------------- */
142         /*  Read the shift registers, and merge the bits into our data.     */
143         /* ---------------------------------------------------------------- */
144         temp = cr_inb(cr->port + 1);
145         data <<= 1;
146         if ((temp & 0x40) != 0) data |= 0x0001;
147         if ((temp & 0x80) == 0) data |= 0x0100;
148     }
149 
150     /* -------------------------------------------------------------------- */
151     /*  Restore the control word and deselect the CONTROL latch.            */
152     /* -------------------------------------------------------------------- */
153     cr_outb(cr->port, cr->ctrl = old_ctrl);
154 
155     return data;
156 }
157 
158 /* ------------------------------------------------------------------------ */
159 /*  CR_DESELECT      -- Make sure the Cartridge Reader is deselected.       */
160 /* ------------------------------------------------------------------------ */
cr_deselect(cart_rd_t * cr)161 void cr_deselect(cart_rd_t *cr)
162 {
163     cr_outb(cr->port + 2, LAT_INH);    /* Inhibit all latches.              */
164 }
165 
166 /* ------------------------------------------------------------------------ */
167 /*  CR_LOOPBACK      -- Writes, then reads, a given value on the bus.       */
168 /*                      Returns whether the value read matched what was     */
169 /*                      actually written.  (0 == Match, 1 == Mismatch.)     */
170 /* ------------------------------------------------------------------------ */
cr_loopback(cart_rd_t * cr,unsigned value,unsigned * rd)171 int cr_loopback(cart_rd_t *cr, unsigned value, unsigned *rd)
172 {
173     unsigned read_back;
174     value &= 0xFFFF;                /* limit to 16 bits (our bus width)     */
175     cr_set_ctrl(cr, CTRL(NACT,0));  /* go to NACT phase during loopback     */
176     cr_set_data(cr, value);         /* write value                          */
177     read_back = cr_get_data(cr);
178     if (rd) *rd = read_back;
179 
180     return read_back != value;                      /* Read back and test   */
181 }
182 
183 /* ------------------------------------------------------------------------ */
184 /*  CR_DETECT        -- Detect the cartridge reader on a given port.        */
185 /*                      Set the port to 0 to do an autodetect.              */
186 /* ------------------------------------------------------------------------ */
cr_detect(unsigned port)187 unsigned cr_detect(unsigned port)
188 {
189     unsigned patt[4] = { 0x0000, 0xFFFF, 0xAA55, 0x55AA };
190     int i = 0, j, rd;
191     cart_rd_t try;
192 
193     memset(&try, 0, sizeof(try));
194 
195     if (port) i = 4;
196     do
197     {
198         if (!port) port = ports[i];
199         printf("> Scanning for cartridge reader at port $%.4X\n", port);
200 
201         try.port = port;
202 
203         cr_set_disp(&try, 255);
204 
205         for (j = 0; j < 4; j++) if (cr_loopback(&try, patt[j], &rd)) break;
206         cr_deselect(&try);
207 
208 
209         if (j > 0 && j != 4)
210         {
211             printf("> Partial detect on port $%.4X: "
212                    "Wrote %.4X, read %.4X\n", port, patt[j], rd);
213         }
214 
215         if (j == 4)
216         {
217             cr_set_disp(&try, 0);
218             cr_set_ctrl(&try, CTRL(NACT, 0));
219             cr_set_data(&try, 0x0000);
220             cr_deselect(&try);
221 
222             break;
223         } else
224             port = 0;
225     } while (++i < 3);
226 
227     if (port)   printf("> Found cartridge reader at port $%.4X\n", port);
228     else        printf("> No cartridge reader found!\n");
229 
230     return port;
231 }
232 
233 /* ------------------------------------------------------------------------ */
234 /*  CR_SELFTEST      -- Perform a self-test on the cartridge reader.        */
235 /* ------------------------------------------------------------------------ */
cr_selftest(cart_rd_t * cr,unsigned * w,unsigned * r)236 int cr_selftest(cart_rd_t *cr, unsigned *w, unsigned *r)
237 {
238     unsigned value, xvalue, rd;
239 
240     cr_set_ctrl(cr, CTRL(NACT, DS9));
241     for (value = 0x0000; value <= 0xFFFF; value++)
242     {
243         if ((value & 0xFF) == 0)
244         {
245             cr_set_disp(cr, (value >> 8) ^ 0xFF);
246             cr_deselect(cr);
247         }
248         xvalue = (0x00FF & (value >> 8)) | (0xFF00 & (value << 8));
249         if (cr_loopback(cr, value,  &rd))
250         {
251             *w = value;
252             *r = rd;
253             return -1;
254         }
255         if (cr_loopback(cr, xvalue, &rd))
256         {
257             *w = xvalue;
258             *r = rd;
259             return -1;
260         }
261     }
262 
263     return 0;
264 }
265 
266 /* ------------------------------------------------------------------------ */
267 /*  CR_SLEEP         -- Wrapper around nanosleep().                         */
268 /* ------------------------------------------------------------------------ */
cr_sleep(long len)269 inline void cr_sleep(long len)
270 {
271 #ifndef NO_NANOSLEEP
272     struct timespec delay, remain;
273 
274     delay.tv_sec  = 0;
275     delay.tv_nsec = len * 1000000;
276 
277     while (nanosleep(&delay, &remain) == -1 && errno == EINTR)
278     {
279         delay = remain;
280         if (remain.tv_nsec < 10000) break;
281     }
282 #else
283     struct timeval x, y;
284     long diff = 0;
285 
286     gettimeofday(&x, 0);
287     while (diff < len)
288     {
289         gettimeofday(&y, 0);
290         diff = (y.tv_sec  - x.tv_sec ) * 1000000 +
291                (y.tv_usec - x.tv_usec);
292     }
293 #endif
294     return;
295 }
296 
297 
298 /* ------------------------------------------------------------------------ */
299 /*  CR_DO_RESET      -- Pulse MSYNC low for awhile, then let it high.       */
300 /* ------------------------------------------------------------------------ */
cr_do_reset(cart_rd_t * cr)301 void cr_do_reset(cart_rd_t *cr)
302 {
303     unsigned cur_disp = cr->disp;
304 
305     cr_set_ctrl(cr, CTRL(RESET, cr->ctrl));     /* Go into RESET.           */
306     cr_set_disp(cr, 0);
307 
308     if (cr->reset_delay)
309         cr_sleep(cr->reset_delay);
310 
311     cr_set_ctrl(cr, CTRL(NACT,  cr->ctrl));     /* Go into NACT.            */
312     cr_set_disp(cr, cur_disp);
313 }
314 
315 /* ------------------------------------------------------------------------ */
316 /*  CR_DO_READ       -- Do read via a BAR - NACT - DTB - NACT cycle         */
317 /* ------------------------------------------------------------------------ */
cr_do_read(cart_rd_t * cr,unsigned addr)318 unsigned cr_do_read(cart_rd_t *cr, unsigned addr)
319 {
320     unsigned data;
321 
322     cr_set_ctrl(cr, CTRL(NACT, OC));    /* Force NACT initially     */
323     cr_set_data(cr, addr);              /* Send out the address     */
324     cr_set_ctrl(cr, CTRL(BAR,   0));    /* Enter BAR phase          */
325     cr_set_ctrl(cr, CTRL(NACT,  0));    /* Enter NACT phase         */
326     cr_set_ctrl(cr, CTRL(NACT, OC));    /* Let address fade         */
327     cr_set_ctrl(cr, CTRL(DTB,  OC));    /* Enter DTB phase          */
328     cr_set_ctrl(cr, cr->ctrl |  CLK);   /* Latch data during DTB    */
329     cr_set_ctrl(cr, cr->ctrl & ~CLK);   /* (Second half of clock)   */
330     data = cr_get_data(cr);             /* Read the shift regs.     */
331     cr_set_ctrl(cr, CTRL(NACT, OC));    /* End in the NACT phase.   */
332     cr_deselect(cr);                    /* Deselect the device.     */
333 
334     return data;
335 }
336 
337 /* ------------------------------------------------------------------------ */
338 /*  CR_DO_WRITE      -- Do write via a BAR - NACT - DW - DWS - NACT cycle   */
339 /* ------------------------------------------------------------------------ */
cr_do_write(cart_rd_t * cr,unsigned addr,unsigned data)340 void cr_do_write(cart_rd_t *cr, unsigned addr, unsigned data)
341 {
342     cr_set_ctrl(cr, CTRL(NACT, OC));    /* Force NACT initially     */
343     cr_set_data(cr, addr);              /* Send out the address     */
344     cr_set_ctrl(cr, CTRL(BAR,   0));    /* Enter BAR phase          */
345     cr_set_ctrl(cr, CTRL(NACT,  0));    /* Enter NACT phase         */
346     cr_set_ctrl(cr, CTRL(NACT, OC));    /* Let address fade         */
347     cr_set_data(cr, data);              /* Send out the write data  */
348     cr_set_ctrl(cr, CTRL(NACT,  0));    /* Let data be asserted     */
349     cr_set_ctrl(cr, CTRL(DW,    0));    /* Enter DW phase           */
350     cr_set_ctrl(cr, CTRL(DWS,   0));    /* Enter DWS phase          */
351     cr_set_ctrl(cr, CTRL(NACT, OC));    /* End in the NACT phase.   */
352     cr_deselect(cr);                    /* Deselect the device.     */
353 }
354 
355 
356 /* ======================================================================== */
357 /*  CR_INIT_PORTS    -- Get access to the desired I/O ports and drop        */
358 /*                      root.  Don't bother returning an error if we fail.  */
359 /*                      Just abort.                                         */
360 /* ======================================================================== */
cr_init_ports(unsigned long base)361 void cr_init_ports(unsigned long base)
362 {
363     int i;
364 
365     /* -------------------------------------------------------------------- */
366     /*  First, sanity check the port number.  If it is not one of the       */
367     /*  standard printer port #'s, then abort.                              */
368     /* -------------------------------------------------------------------- */
369     if (base && base != 0x378 && base != 0x278 && base != 0x3BC)
370     {
371         fprintf(stderr, "cr_init_ports:  Invalid base address 0x%.4lX\n", base);
372         exit(1);
373     }
374 
375 #ifndef NO_SETUID
376     if (base)
377     {
378         /* ---------------------------------------------------------------- */
379         /*  Grant ourself perms to access ports 'base' through 'base+2'.    */
380         /* ---------------------------------------------------------------- */
381         if (ioperm(base, 3, 1))
382         {
383             fprintf(stderr, "cr_init_ports:  Unable to set I/O permissions\n");
384             perror("cr_init_ports: ioperm()");
385             exit(1);
386         }
387         printf("> Granted access to $%.4X (%d)\n", (int)base, (int)base);
388     } else
389     {
390         /* ---------------------------------------------------------------- */
391         /*  Grant ourself perms to access ports 'base' through 'base+2'.    */
392         /* ---------------------------------------------------------------- */
393         for (i = 0; i < 3; i++)
394         {
395             if (ioperm(ports[i], 3, 1))
396             {
397                 fprintf(stderr, "cr_init_ports: "
398                                 " Unable to set I/O permissions\n");
399                 perror("cr_init_ports: ioperm()");
400                 exit(1);
401             }
402             printf("> Granted access to $%.4X (%d)\n",
403                    (int)ports[i], (int)ports[i]);
404         }
405     }
406 
407     /* -------------------------------------------------------------------- */
408     /*  Drop elevated privs if we have them.                                */
409     /* -------------------------------------------------------------------- */
410     if (getegid() != getgid()) setgid(getgid());
411     if (geteuid() != getuid()) setuid(getuid());
412 #endif
413 
414     return;
415 }
416 
417 #endif
418 
419 /* ======================================================================== */
420 /*  This program is free software; you can redistribute it and/or modify    */
421 /*  it under the terms of the GNU General Public License as published by    */
422 /*  the Free Software Foundation; either version 2 of the License, or       */
423 /*  (at your option) any later version.                                     */
424 /*                                                                          */
425 /*  This program is distributed in the hope that it will be useful,         */
426 /*  but WITHOUT ANY WARRANTY; without even the implied warranty of          */
427 /*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU       */
428 /*  General Public License for more details.                                */
429 /*                                                                          */
430 /*  You should have received a copy of the GNU General Public License along */
431 /*  with this program; if not, write to the Free Software Foundation, Inc., */
432 /*  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.             */
433 /* ======================================================================== */
434 /*                 Copyright (c) 1998-2001, Joseph Zbiciak                  */
435 /* ======================================================================== */
436