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