cd64io.c 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101
  1. /*
  2. *
  3. * cd64io.c
  4. *
  5. * I/O routines for CD64 device
  6. *
  7. * (c) 2004 Ryan Underwood
  8. * Portions (c) 2004, 2005, 2015 Daniel Horchner (OpenBSD, FreeBSD, BeOS, Win32, DOS)
  9. *
  10. * May be distributed under the terms of the GNU Lesser/Library General Public
  11. * License, or any later version of the same, as published by the Free Software
  12. * Foundation.
  13. */
  14. #include <stdlib.h>
  15. #include <stdio.h>
  16. #include <fcntl.h>
  17. #include <stdarg.h>
  18. #include <string.h>
  19. #include <errno.h>
  20. #include <sys/types.h>
  21. #ifdef _MSC_VER
  22. #pragma warning(push)
  23. #pragma warning(disable: 4820) /* 'bytes' bytes padding added after construct 'member_name' */
  24. #endif
  25. #include <sys/stat.h>
  26. #ifdef _MSC_VER
  27. #pragma warning(pop)
  28. #endif
  29. #if defined __unix__ || defined __BEOS__ /* ioctl() */
  30. #include <unistd.h>
  31. #endif
  32. #include <ultra64/host/cd64lib.h>
  33. #include "cd64io.h"
  34. #define DEBUG_LOWLEVEL 0
  35. #define BUSY_THRESHOLD 10000
  36. #define MAX_TRIES 5
  37. #ifdef CD64_USE_RAWIO
  38. #if defined _WIN32 || defined __CYGWIN__
  39. #ifdef __CYGWIN__
  40. #define DIR_SEPARATOR_S "/"
  41. #else
  42. #define snprintf _snprintf
  43. #define DIR_SEPARATOR_S "\\"
  44. #endif
  45. /* The next union is a portable means to convert between function and data
  46. * pointers and the only way to silence Visual C++ 2012 other than
  47. * #pragma warning(disable: 4152)
  48. * That is, with /W4. */
  49. typedef union u_func_ptr {
  50. void (*func_ptr)(void);
  51. void *void_ptr;
  52. } u_func_ptr_t;
  53. static void *io_driver = NULL;
  54. static int io_driver_found = 0;
  55. /* inpout32.dll */
  56. static short (__stdcall *Inp32)(short) = NULL;
  57. static void (__stdcall *Outp32)(short, short) = NULL;
  58. /* io.dll */
  59. static char (WINAPI *PortIn)(short int) = NULL;
  60. static void (WINAPI *PortOut)(short int, char) = NULL;
  61. static short int (WINAPI *IsDriverInstalled)(void) = NULL;
  62. /* DlPortIO.dll */
  63. static unsigned char (__stdcall *DlPortReadPortUchar)(unsigned short) = NULL;
  64. static void (__stdcall *DlPortWritePortUchar)(unsigned short, unsigned char) = NULL;
  65. static INLINE uint8_t inb(uint16_t);
  66. static INLINE void outb(uint8_t, uint16_t);
  67. static uint8_t (*input_byte)(uint16_t) = inb;
  68. static void (*output_byte)(uint8_t, uint16_t) = outb;
  69. #endif
  70. #ifdef __BEOS__
  71. static int io_portfd;
  72. typedef struct st_ioport {
  73. unsigned int port;
  74. unsigned char data8;
  75. unsigned short data16;
  76. } st_ioport_t;
  77. #endif
  78. #endif /* CD64_USE_RAWIO */
  79. int cd64_send_byte(struct cd64_t *cd64, uint8_t what) {
  80. return cd64->xfer(cd64, &what, NULL, 0);
  81. }
  82. int cd64_send_dword(struct cd64_t *cd64, uint32_t what) {
  83. int ret = 1;
  84. ret &= cd64_send_byte(cd64, (uint8_t) (what>>24));
  85. ret &= cd64_send_byte(cd64, (uint8_t) (what>>16));
  86. ret &= cd64_send_byte(cd64, (uint8_t) (what>>8));
  87. ret &= cd64_send_byte(cd64, (uint8_t) what);
  88. return ret;
  89. }
  90. int cd64_grab_byte(struct cd64_t *cd64, uint8_t *val) {
  91. return cd64->xfer(cd64, NULL, val, 0);
  92. }
  93. int cd64_grab_dword(struct cd64_t *cd64, uint32_t *val) {
  94. int ret = 1;
  95. uint8_t grab;
  96. if (val == NULL) return 0;
  97. *val = 0;
  98. ret &= cd64_grab_byte(cd64, &grab);
  99. *val |= grab << 24;
  100. ret &= cd64_grab_byte(cd64, &grab);
  101. *val |= grab << 16;
  102. ret &= cd64_grab_byte(cd64, &grab);
  103. *val |= grab << 8;
  104. ret &= cd64_grab_byte(cd64, &grab);
  105. *val |= grab;
  106. return ret;
  107. }
  108. int cd64_trade_bytes(struct cd64_t *cd64, uint8_t give, uint8_t *recv) {
  109. return cd64->xfer(cd64, &give, recv, 0);
  110. }
  111. /* Backend-specific defs go down here. */
  112. #ifdef CD64_USE_LIBIEEE1284
  113. int cd64_open_ieee1284(struct cd64_t *cd64) {
  114. struct parport_list pplist;
  115. int ppflags = F1284_EXCL;
  116. int ppcaps = 0;
  117. int i;
  118. int opened = 0;
  119. if (cd64->ppdev || !cd64->using_ppa) return 0;
  120. if (ieee1284_find_ports(&pplist, 0) < 0) {
  121. cd64->notice_callback2("could not get port list\n");
  122. return 0;
  123. }
  124. if (cd64->port < pplist.portc) {
  125. /* Just use it as an index. */
  126. cd64->ppdev = pplist.portv[cd64->port];
  127. }
  128. else {
  129. /* Search for the ppdev matching its base address. */
  130. for (i = 0; i < pplist.portc; i++) {
  131. if (cd64->port == (int) pplist.portv[i]->base_addr) {
  132. cd64->ppdev = pplist.portv[i];
  133. }
  134. }
  135. }
  136. if (cd64->ppdev) {
  137. if (ieee1284_open(cd64->ppdev, ppflags, &ppcaps) < 0) {
  138. cd64->notice_callback2("failed opening ieee1284 port %d\n", cd64->port);
  139. cd64->ppdev = NULL;
  140. }
  141. else {
  142. opened = 1;
  143. }
  144. }
  145. ieee1284_free_ports(&pplist);
  146. if (opened && ieee1284_claim(cd64->ppdev) < 0) return 0;
  147. else return opened;
  148. }
  149. int cd64_close_ieee1284(struct cd64_t *cd64) {
  150. int ret;
  151. if (cd64->ppdev == NULL) return 1;
  152. ieee1284_release(cd64->ppdev);
  153. ret = ieee1284_close(cd64->ppdev);
  154. if (ret < 0) ret = 0;
  155. else {
  156. cd64->ppdev = NULL;
  157. ret = 1;
  158. }
  159. return ret;
  160. }
  161. static INLINE int cd64_wait_ieee(struct cd64_t *cd64) {
  162. /* With ppdev, could we use an interrupt instead? The PPA
  163. * could be modified... */
  164. int i = 0;
  165. int reset_tries = 0;
  166. while (i < 10000) i++; /* FIXME is this necessary? */
  167. i = 0;
  168. while((ieee1284_read_status(cd64->ppdev)^S1284_INVERTED) & S1284_BUSY) {
  169. i++;
  170. if (i >= BUSY_THRESHOLD) {
  171. /* The PPA is in a weird state.
  172. * Try to knock some sense into it. */
  173. ieee1284_write_control(cd64->ppdev, (C1284_NINIT|C1284_NAUTOFD)^C1284_INVERTED);
  174. ieee1284_write_control(cd64->ppdev, C1284_NINIT^C1284_INVERTED);
  175. ieee1284_write_control(cd64->ppdev, (C1284_NINIT|C1284_NSTROBE)^C1284_INVERTED);
  176. ieee1284_write_control(cd64->ppdev, C1284_NINIT^C1284_INVERTED);
  177. reset_tries++;
  178. i = 0;
  179. MSLEEP(1);
  180. }
  181. if (reset_tries > MAX_TRIES) break;
  182. if (cd64->abort) return 0;
  183. }
  184. return (reset_tries < MAX_TRIES);
  185. }
  186. int cd64_xfer_ieee1284(struct cd64_t *cd64, uint8_t *wr, uint8_t *rd, int delayms) {
  187. if (!cd64_wait_ieee(cd64)) { return 0; }
  188. if (delayms) MSLEEP(delayms);
  189. ieee1284_data_dir(cd64->ppdev, 1);
  190. if (delayms) MSLEEP(delayms);
  191. ieee1284_write_control(cd64->ppdev, (C1284_NINIT|C1284_NAUTOFD)^C1284_INVERTED);
  192. if (delayms) MSLEEP(delayms);
  193. if (rd) {
  194. *rd = ieee1284_read_data(cd64->ppdev);
  195. #if DEBUG_LOWLEVEL
  196. printf("got %xh", *rd);
  197. if (*rd > 0x20) printf(" (%c)", *rd);
  198. printf("\n");
  199. #endif
  200. }
  201. if (delayms) MSLEEP(delayms);
  202. ieee1284_data_dir(cd64->ppdev, 0);
  203. if (delayms) MSLEEP(delayms);
  204. ieee1284_write_control(cd64->ppdev, C1284_NINIT^C1284_INVERTED);
  205. if (delayms) MSLEEP(delayms);
  206. if (wr) {
  207. ieee1284_write_data(cd64->ppdev, *wr);
  208. #if DEBUG_LOWLEVEL
  209. printf("put %xh", *wr);
  210. if (*wr > 0x20) printf(" (%c)", *wr);
  211. printf("\n");
  212. #endif
  213. }
  214. if (delayms) MSLEEP(delayms);
  215. ieee1284_write_control(cd64->ppdev, (C1284_NINIT|C1284_NSTROBE)^C1284_INVERTED);
  216. if (delayms) MSLEEP(delayms);
  217. ieee1284_write_control(cd64->ppdev, C1284_NINIT^C1284_INVERTED);
  218. return 1;
  219. }
  220. #endif /* CD64_USE_LIBIEEE1284 */
  221. #ifdef CD64_USE_PPDEV
  222. int cd64_open_ppdev(struct cd64_t *cd64) {
  223. char *device = "/dev/parport%d";
  224. char realdev[128+1];
  225. if (cd64->ppdevfd || !cd64->using_ppa) return 0;
  226. /* This should be a port number only, not an address */
  227. if (cd64->port > PARPORT_MAX) return 0;
  228. snprintf(realdev, 128+1, device, cd64->port);
  229. realdev[128] = 0;
  230. if ((cd64->ppdevfd = open(realdev, O_RDWR)) == -1) {
  231. cd64->notice_callback2("open: %s", strerror(errno));
  232. cd64->ppdevfd = 0;
  233. return 0;
  234. }
  235. if (ioctl(cd64->ppdevfd, PPEXCL) != 0) {
  236. cd64->notice_callback2("PPEXCL: %s", strerror(errno));
  237. close(cd64->ppdevfd);
  238. cd64->ppdevfd = 0;
  239. return 0;
  240. }
  241. if (ioctl(cd64->ppdevfd, PPCLAIM) != 0) {
  242. cd64->notice_callback2("PPCLAIM: %s", strerror(errno));
  243. close(cd64->ppdevfd);
  244. cd64->ppdevfd = 0;
  245. return 0;
  246. }
  247. return 1;
  248. }
  249. int cd64_close_ppdev(struct cd64_t *cd64) {
  250. int ret = 1;
  251. if (cd64->ppdevfd == 0) return 1;
  252. if (ioctl(cd64->ppdevfd, PPRELEASE) != 0) {
  253. cd64->notice_callback2("PPRELEASE: %s", strerror(errno));
  254. ret = 0;
  255. }
  256. close(cd64->ppdevfd);
  257. cd64->ppdevfd = 0;
  258. return ret;
  259. }
  260. static INLINE int cd64_wait_ppdev(struct cd64_t *cd64) {
  261. /* With ppdev, could we use an interrupt instead? The PPA
  262. * could be modified... */
  263. int i = 0;
  264. int reset_tries = 0;
  265. uint8_t status;
  266. int dir;
  267. i = 0;
  268. if (ioctl(cd64->ppdevfd, PPRSTATUS, &status) != 0) cd64->notice_callback2("PPRSTATUS: %s", strerror(errno));
  269. while(status & 0x80) {
  270. i++;
  271. if (i >= BUSY_THRESHOLD) {
  272. /* The PPA is in a weird state.
  273. * Try to knock some sense into it. */
  274. dir = 1;
  275. if (ioctl(cd64->ppdevfd, PPDATADIR, &dir) != 0) cd64->notice_callback2("PPDATADIR: %s", strerror(errno));
  276. status = PARPORT_CONTROL_INIT | PARPORT_CONTROL_AUTOFD; /* 0x26 */
  277. if (ioctl(cd64->ppdevfd, PPWCONTROL, &status) != 0) cd64->notice_callback2("PPWCONTROL: %s", strerror(errno));
  278. dir = 0;
  279. if (ioctl(cd64->ppdevfd, PPDATADIR, &dir) != 0) cd64->notice_callback2("PPDATADIR: %s", strerror(errno));
  280. status = PARPORT_CONTROL_INIT; /* 0x04 */
  281. if (ioctl(cd64->ppdevfd, PPWCONTROL, &status) != 0) cd64->notice_callback2("PPWCONTROL: %s", strerror(errno));
  282. status = PARPORT_CONTROL_INIT | PARPORT_CONTROL_STROBE; /* 0x05 */
  283. if (ioctl(cd64->ppdevfd, PPWCONTROL, &status) != 0) cd64->notice_callback2("PPWCONTROL: %s", strerror(errno));
  284. status = PARPORT_CONTROL_INIT; /* 0x04 */
  285. if (ioctl(cd64->ppdevfd, PPWCONTROL, &status) != 0) cd64->notice_callback2("PPWCONTROL: %s", strerror(errno));
  286. reset_tries++;
  287. i = 0;
  288. MSLEEP(1);
  289. }
  290. if (cd64->abort) return 0;
  291. if (reset_tries > MAX_TRIES) break;
  292. if (ioctl(cd64->ppdevfd, PPRSTATUS, &status) != 0) cd64->notice_callback2("PPRSTATUS: %s", strerror(errno));
  293. }
  294. return (reset_tries < MAX_TRIES);
  295. }
  296. int cd64_xfer_ppdev(struct cd64_t *cd64, uint8_t *wr, uint8_t *rd, int delayms) {
  297. uint8_t ctl;
  298. int dir;
  299. if (!cd64_wait_ppdev(cd64)) { return 0; }
  300. if (delayms) MSLEEP(delayms);
  301. dir = 1;
  302. if (ioctl(cd64->ppdevfd, PPDATADIR, &dir) != 0) cd64->notice_callback2("PPDATADIR: %s", strerror(errno));
  303. if (delayms) MSLEEP(delayms);
  304. ctl = PARPORT_CONTROL_INIT | PARPORT_CONTROL_AUTOFD;
  305. if (ioctl(cd64->ppdevfd, PPWCONTROL, &ctl) != 0) cd64->notice_callback2("PPWCONTROL: %s", strerror(errno));
  306. if (delayms) MSLEEP(delayms);
  307. if (rd) {
  308. if (ioctl(cd64->ppdevfd, PPRDATA, rd) != 0) cd64->notice_callback2("PPRDATA: %s", strerror(errno));
  309. #if DEBUG_LOWLEVEL
  310. printf("got %xh", *rd);
  311. if (*rd > 0x20) printf(" (%c)", *rd);
  312. printf("\n");
  313. #endif
  314. }
  315. if (delayms) MSLEEP(delayms);
  316. dir = 0;
  317. if (ioctl(cd64->ppdevfd, PPDATADIR, &dir) != 0) cd64->notice_callback2("PPDATADIR: %s", strerror(errno));
  318. if (delayms) MSLEEP(delayms);
  319. ctl = PARPORT_CONTROL_INIT;
  320. if (ioctl(cd64->ppdevfd, PPWCONTROL, &ctl) != 0) cd64->notice_callback2("PPWCONTROL: %s", strerror(errno));
  321. if (delayms) MSLEEP(delayms);
  322. if (wr) {
  323. if (ioctl(cd64->ppdevfd, PPWDATA, wr) != 0) cd64->notice_callback2("PPWDATA: %s", strerror(errno));
  324. #if DEBUG_LOWLEVEL
  325. printf("put %xh", *wr);
  326. if (*wr > 0x20) printf(" (%c)", *wr);
  327. printf("\n");
  328. #endif
  329. }
  330. if (delayms) MSLEEP(delayms);
  331. ctl = PARPORT_CONTROL_INIT | PARPORT_CONTROL_STROBE;
  332. if (ioctl(cd64->ppdevfd, PPWCONTROL, &ctl) != 0) cd64->notice_callback2("PPWCONTROL: %s", strerror(errno));
  333. if (delayms) MSLEEP(delayms);
  334. ctl = PARPORT_CONTROL_INIT;
  335. if (ioctl(cd64->ppdevfd, PPWCONTROL, &ctl) != 0) cd64->notice_callback2("PPWCONTROL: %s", strerror(errno));
  336. return 1;
  337. }
  338. #endif /* CD64_USE_PPDEV */
  339. #ifdef CD64_USE_PORTDEV
  340. int cd64_open_portdev(struct cd64_t *cd64) {
  341. if (cd64->portdevfd || cd64->port == 0) return 0;
  342. if ((cd64->portdevfd = open("/dev/port", O_RDWR)) == -1) {
  343. cd64->notice_callback2("open: %s", strerror(errno));
  344. cd64->notice_callback2("portdev requires CAP_SYS_RAWIO capability");
  345. cd64->portdevfd = 0;
  346. return 0;
  347. }
  348. return 1;
  349. }
  350. int cd64_close_portdev(struct cd64_t *cd64) {
  351. if (cd64->portdevfd == 0) return 1;
  352. if (close(cd64->portdevfd) == -1) {
  353. cd64->notice_callback2("close: %s", strerror(errno));
  354. return 0;
  355. }
  356. cd64->portdevfd = 0;
  357. return 1;
  358. }
  359. static INLINE int cd64_wait_portdev(struct cd64_t *cd64) {
  360. int i = 0;
  361. int reset_tries = 0;
  362. uint8_t status;
  363. int dir;
  364. i = 0;
  365. if (cd64->using_ppa) {
  366. lseek(cd64->portdevfd, cd64->port+1, SEEK_SET);
  367. read(cd64->portdevfd, &status, 1);
  368. while(status & 0x80) {
  369. i++;
  370. if (i >= BUSY_THRESHOLD) {
  371. /* The PPA is in a weird state.
  372. * Try to knock some sense into it. */
  373. dir = 1;
  374. status = 0x06 | (dir << 5);
  375. lseek(cd64->portdevfd, cd64->port+2, SEEK_SET);
  376. write(cd64->portdevfd, &status, 1);
  377. dir = 0;
  378. status = 0x04 | (dir << 5);
  379. lseek(cd64->portdevfd, cd64->port+2, SEEK_SET);
  380. write(cd64->portdevfd, &status, 1);
  381. status = 0x05 | (dir << 5);
  382. lseek(cd64->portdevfd, cd64->port+2, SEEK_SET);
  383. write(cd64->portdevfd, &status, 1);
  384. status = 0x04 | (dir << 5);
  385. lseek(cd64->portdevfd, cd64->port+2, SEEK_SET);
  386. write(cd64->portdevfd, &status, 1);
  387. reset_tries++;
  388. i = 0;
  389. MSLEEP(1);
  390. }
  391. if (cd64->abort) return 0;
  392. if (reset_tries > MAX_TRIES) {
  393. break;
  394. }
  395. lseek(cd64->portdevfd, cd64->port+1, SEEK_SET);
  396. read(cd64->portdevfd, &status, 1);
  397. }
  398. }
  399. else { /* Comms link */
  400. lseek(cd64->portdevfd, cd64->port+2, SEEK_SET);
  401. read(cd64->portdevfd, &status, 1);
  402. while (status & 1) {
  403. /* Do we need to handle a stuck situation here? */
  404. lseek(cd64->portdevfd, cd64->port+2, SEEK_SET);
  405. read(cd64->portdevfd, &status, 1);
  406. }
  407. }
  408. return (reset_tries < MAX_TRIES);
  409. }
  410. int cd64_xfer_portdev(struct cd64_t *cd64, uint8_t *wr, uint8_t *rd, int delayms) {
  411. uint8_t ctl;
  412. int dir;
  413. if (cd64->using_ppa) {
  414. if (!cd64_wait_portdev(cd64)) { return 0; }
  415. if (delayms) MSLEEP(delayms);
  416. dir = 1;
  417. ctl = 0x06 | (dir << 5);
  418. lseek(cd64->portdevfd, cd64->port+2, SEEK_SET);
  419. write(cd64->portdevfd, &ctl, 1);
  420. if (delayms) MSLEEP(delayms);
  421. if (rd) {
  422. lseek(cd64->portdevfd, cd64->port, SEEK_SET);
  423. read(cd64->portdevfd, rd, 1);
  424. #if DEBUG_LOWLEVEL
  425. printf("got %xh", *rd);
  426. if (*rd > 0x20) printf(" (%c)", *rd);
  427. printf("\n");
  428. #endif
  429. }
  430. if (delayms) MSLEEP(delayms);
  431. dir = 0;
  432. ctl = 0x04 | (dir << 5);
  433. lseek(cd64->portdevfd, cd64->port+2, SEEK_SET);
  434. write(cd64->portdevfd, &ctl, 1);
  435. if (delayms) MSLEEP(delayms);
  436. if (wr) {
  437. lseek(cd64->portdevfd, cd64->port, SEEK_SET);
  438. write(cd64->portdevfd, wr, 1);
  439. #if DEBUG_LOWLEVEL
  440. printf("put %xh", *wr);
  441. if (*wr > 0x20) printf(" (%c)", *wr);
  442. printf("\n");
  443. #endif
  444. }
  445. if (delayms) MSLEEP(delayms);
  446. ctl = 0x05 | (dir << 5);
  447. lseek(cd64->portdevfd, cd64->port+2, SEEK_SET);
  448. write(cd64->portdevfd, &ctl, 1);
  449. if (delayms) MSLEEP(delayms);
  450. ctl = 0x04 | (dir << 5);
  451. lseek(cd64->portdevfd, cd64->port+2, SEEK_SET);
  452. write(cd64->portdevfd, &ctl, 1);
  453. }
  454. else { /* Comms link */
  455. lseek(cd64->portdevfd, cd64->port, SEEK_SET);
  456. write(cd64->portdevfd, wr, 1);
  457. if (!cd64_wait_portdev(cd64)) { return 0; }
  458. lseek(cd64->portdevfd, cd64->port, SEEK_SET);
  459. read(cd64->portdevfd, rd, 1);
  460. }
  461. return 1;
  462. }
  463. #endif /* CD64_USE_PORTDEV */
  464. #ifdef CD64_USE_RAWIO
  465. #if defined _WIN32 || defined __CYGWIN__
  466. static void *open_module(char *module_name, struct cd64_t *cd64) {
  467. void *handle = LoadLibrary(module_name);
  468. if (handle == NULL) {
  469. LPTSTR strptr;
  470. cd64->notice_callback2("LoadLibrary: %s", strerror(errno));
  471. FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
  472. FORMAT_MESSAGE_FROM_SYSTEM |
  473. FORMAT_MESSAGE_IGNORE_INSERTS,
  474. NULL, GetLastError(),
  475. MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
  476. (LPTSTR) &strptr, 0, NULL);
  477. cd64->notice_callback2(strptr);
  478. LocalFree(strptr);
  479. exit(1);
  480. }
  481. return handle;
  482. }
  483. static void close_module(void *handle, struct cd64_t *cd64) {
  484. if (!FreeLibrary((HINSTANCE) handle)) {
  485. LPTSTR strptr;
  486. cd64->notice_callback2("FreeLibrary: %s", strerror(errno));
  487. FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
  488. FORMAT_MESSAGE_FROM_SYSTEM |
  489. FORMAT_MESSAGE_IGNORE_INSERTS,
  490. NULL, GetLastError(),
  491. MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
  492. (LPTSTR) &strptr, 0, NULL);
  493. cd64->notice_callback2(strptr);
  494. LocalFree(strptr);
  495. exit(1);
  496. }
  497. }
  498. static void *get_symbol(void *handle, char *symbol_name, struct cd64_t *cd64) {
  499. void *symptr;
  500. u_func_ptr_t sym;
  501. sym.func_ptr = (void (*)(void)) GetProcAddress((HINSTANCE) handle, symbol_name);
  502. symptr = sym.void_ptr;
  503. if (symptr == NULL) {
  504. LPTSTR strptr;
  505. cd64->notice_callback2("GetProcAddress: %s", strerror(errno));
  506. FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
  507. FORMAT_MESSAGE_FROM_SYSTEM |
  508. FORMAT_MESSAGE_IGNORE_INSERTS,
  509. NULL, GetLastError(),
  510. MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
  511. (LPTSTR) &strptr, 0, NULL);
  512. cd64->notice_callback2(strptr);
  513. LocalFree(strptr);
  514. exit(1);
  515. }
  516. return symptr;
  517. }
  518. static void *has_symbol(void *handle, char *symbol_name) {
  519. void *symptr;
  520. u_func_ptr_t sym;
  521. sym.func_ptr = (void (*)(void)) GetProcAddress((HINSTANCE) handle, symbol_name);
  522. symptr = sym.void_ptr;
  523. if (symptr == NULL) symptr = (void *) -1;
  524. return symptr;
  525. }
  526. /* inpout32.dll */
  527. static uint8_t inpout32_input_byte(uint16_t port) {
  528. return (uint8_t) Inp32((short) port);
  529. }
  530. static void inpout32_output_byte(uint8_t byte, uint16_t port) {
  531. Outp32((short) port, (short) byte);
  532. }
  533. /* io.dll */
  534. static uint8_t io_input_byte(uint16_t port) {
  535. return PortIn(port);
  536. }
  537. static void io_output_byte(uint8_t byte, uint16_t port) {
  538. PortOut(port, byte);
  539. }
  540. /* DlPortIO.dll */
  541. static uint8_t dlportio_input_byte(uint16_t port) {
  542. return DlPortReadPortUchar(port);
  543. }
  544. static void dlportio_output_byte(uint8_t byte, uint16_t port) {
  545. DlPortWritePortUchar(port, byte);
  546. }
  547. #define NODRIVER_MSG "ERROR: No (working) I/O port driver\n"
  548. #ifdef __CYGWIN__
  549. static EXCEPTION_DISPOSITION NTAPI new_exception_handler(PEXCEPTION_RECORD exception_record,
  550. void *establisher_frame, PCONTEXT context_record, void *dispatcher_context) {
  551. (void) establisher_frame;
  552. (void) context_record;
  553. (void) dispatcher_context;
  554. if (exception_record->ExceptionCode == EXCEPTION_PRIV_INSTRUCTION) {
  555. fputs(NODRIVER_MSG, stderr);
  556. exit(1);
  557. }
  558. return EXCEPTION_CONTINUE_SEARCH;
  559. }
  560. #elif defined _WIN32
  561. static LONG new_exception_filter(LPEXCEPTION_POINTERS exception_pointers) {
  562. if (exception_pointers->ExceptionRecord->ExceptionCode == EXCEPTION_PRIV_INSTRUCTION) {
  563. fputs(NODRIVER_MSG, stderr);
  564. exit(1);
  565. }
  566. return EXCEPTION_CONTINUE_SEARCH;
  567. }
  568. #endif
  569. #endif /* _WIN32 || __CYGWIN__ */
  570. #if ((defined _WIN32 || defined __CYGWIN__ || defined __BEOS__ || \
  571. defined __MSDOS__) && \
  572. (defined __i386__ || defined __x86_64__)) || defined _MSC_VER
  573. static INLINE uint8_t inb(uint16_t port) {
  574. #ifdef __MSDOS__
  575. return inportb(port);
  576. #elif defined __BEOS__
  577. st_ioport_t temp;
  578. temp.port = port;
  579. ioctl(io_portfd, 'r', &temp, 0);
  580. return temp.data8;
  581. #else /* Win32 */
  582. if (io_driver_found) return input_byte(port);
  583. else {
  584. #ifdef _MSC_VER
  585. return (unsigned char) _inp(port);
  586. #else
  587. unsigned char byte;
  588. __asm__ __volatile__
  589. ("inb %1, %0"
  590. : "=a" (byte)
  591. : "d" (port)
  592. );
  593. return byte;
  594. #endif
  595. }
  596. #endif
  597. }
  598. static INLINE void outb(uint8_t byte, uint16_t port) {
  599. #ifdef __MSDOS__
  600. outportb(port, byte);
  601. #elif defined __BEOS__
  602. st_ioport_t temp;
  603. temp.port = port;
  604. temp.data8 = byte;
  605. ioctl(io_portfd, 'w', &temp, 0);
  606. #else /* Win32 */
  607. if (io_driver_found) output_byte(byte, port);
  608. else {
  609. #ifdef _MSC_VER
  610. _outp(port, byte);
  611. #else
  612. __asm__ __volatile__
  613. ("outb %1, %0"
  614. :
  615. : "d" (port), "a" (byte)
  616. );
  617. #endif
  618. }
  619. #endif
  620. }
  621. #endif /* inb/outb defs */
  622. int cd64_open_rawio(struct cd64_t *cd64) {
  623. int ret;
  624. (void) ret;
  625. /* NOTE: we will soon be able to use ioperm on the entire
  626. * 16-bit port range. Find out what Linux kernels support it. */
  627. if (cd64->port < 0x200) {
  628. cd64->notice_callback2("Erroneous port %xh", cd64->port);
  629. return 0;
  630. }
  631. #ifdef __linux__
  632. if (cd64->port < 0x3fd) {
  633. if (cd64->using_ppa) {
  634. ret = ioperm(cd64->port, 3, 1);
  635. }
  636. else {
  637. ret = ioperm(cd64->port, 1, 1);
  638. ret |= ioperm(cd64->port+2, 1, 1);
  639. }
  640. if (ret == -1) {
  641. cd64->notice_callback2("ioperm: %s", strerror(errno));
  642. cd64->notice_callback2("rawio requires CAP_SYS_RAWIO capability");
  643. return 0;
  644. }
  645. }
  646. else {
  647. ret = iopl(3);
  648. if (ret == -1) {
  649. cd64->notice_callback2("iopl: %s", strerror(errno));
  650. cd64->notice_callback2("rawio requires CAP_SYS_RAWIO capability");
  651. return 0;
  652. }
  653. }
  654. #elif defined __OpenBSD__
  655. /* I cannot test i386_set_ioperm(), so I only use i386_iopl() */
  656. ret = i386_iopl(3);
  657. if (ret == -1) {
  658. cd64->notice_callback2("i386_iopl: %s", strerror(errno));
  659. return 0;
  660. }
  661. #elif defined __FreeBSD__
  662. cd64->portdevfd = open("/dev/io", O_RDWR);
  663. if (cd64->portdevfd == -1) {
  664. cd64->portdevfd = 0;
  665. cd64->notice_callback2("open: %s", strerror(errno));
  666. cd64->notice_callback2("Could not open I/O port device (/dev/io)");
  667. return 0;
  668. }
  669. #elif defined __BEOS__
  670. io_portfd = open("/dev/misc/ioport", O_RDWR | O_NONBLOCK);
  671. if (io_portfd == -1) {
  672. io_portfd = 0;
  673. cd64->notice_callback2("open: %s", strerror(errno));
  674. cd64->notice_callback2("Could not open I/O port device (no driver)");
  675. exit(1);
  676. }
  677. #elif defined _WIN32 || defined __CYGWIN__
  678. #ifdef _MSC_VER
  679. #define access _access
  680. #endif
  681. {
  682. char fname[FILENAME_MAX+1];
  683. u_func_ptr_t sym;
  684. io_driver_found = 0;
  685. if (!cd64->io_driver_dir[0]) strcpy(cd64->io_driver_dir, ".");
  686. snprintf(fname, FILENAME_MAX+1, "%s" DIR_SEPARATOR_S "%s",
  687. cd64->io_driver_dir, "dlportio.dll");
  688. fname[FILENAME_MAX] = 0;
  689. if (access(fname, F_OK) == 0) {
  690. io_driver = open_module(fname, cd64);
  691. io_driver_found = 1;
  692. sym.void_ptr = get_symbol(io_driver, "DlPortReadPortUchar", cd64);
  693. DlPortReadPortUchar = (unsigned char (__stdcall *)(unsigned short)) sym.func_ptr;
  694. sym.void_ptr = get_symbol(io_driver, "DlPortWritePortUchar", cd64);
  695. DlPortWritePortUchar = (void (__stdcall *)(unsigned short, unsigned char)) sym.func_ptr;
  696. input_byte = dlportio_input_byte;
  697. output_byte = dlportio_output_byte;
  698. }
  699. if (!io_driver_found) {
  700. snprintf(fname, FILENAME_MAX+1, "%s" DIR_SEPARATOR_S "%s",
  701. cd64->io_driver_dir, "io.dll");
  702. fname[FILENAME_MAX] = 0;
  703. if (access(fname, F_OK) == 0) {
  704. io_driver = open_module(fname, cd64);
  705. sym.void_ptr = get_symbol(io_driver, "IsDriverInstalled", cd64);
  706. IsDriverInstalled = (short int (WINAPI *)(void)) sym.func_ptr;
  707. if (IsDriverInstalled()) {
  708. io_driver_found = 1;
  709. sym.void_ptr = get_symbol(io_driver, "PortIn", cd64);
  710. PortIn = (char (WINAPI *)(short int)) sym.func_ptr;
  711. sym.void_ptr = get_symbol(io_driver, "PortOut", cd64);
  712. PortOut = (void (WINAPI *)(short int, char)) sym.func_ptr;
  713. input_byte = io_input_byte;
  714. output_byte = io_output_byte;
  715. }
  716. }
  717. }
  718. if (!io_driver_found) {
  719. snprintf(fname, FILENAME_MAX+1, "%s" DIR_SEPARATOR_S "%s",
  720. cd64->io_driver_dir, "inpout32.dll");
  721. fname[FILENAME_MAX] = 0;
  722. if (access(fname, F_OK) == 0) {
  723. io_driver = open_module(fname, cd64);
  724. io_driver_found = 1;
  725. /* Newer ports of inpout32.dll also contain the API provided by
  726. * DlPortIO.dll. Since the API of DlPortIO.dll does not have
  727. * the flaws of inpout32.dll (*signed* short return value and
  728. * arguments), we prefer it if it is present. */
  729. sym.void_ptr = has_symbol(io_driver, "DlPortReadPortUchar");
  730. DlPortReadPortUchar = (unsigned char (__stdcall *)(unsigned short)) sym.func_ptr;
  731. if (DlPortReadPortUchar != (void *) -1) input_byte = dlportio_input_byte;
  732. else {
  733. sym.void_ptr = get_symbol(io_driver, "Inp32", cd64);
  734. Inp32 = (short (__stdcall *)(short)) sym.func_ptr;
  735. input_byte = inpout32_input_byte;
  736. }
  737. sym.void_ptr = has_symbol(io_driver, "DlPortWritePortUchar");
  738. DlPortWritePortUchar = (void (__stdcall *)(unsigned short, unsigned char)) sym.func_ptr;
  739. if (DlPortWritePortUchar != (void *) -1) output_byte = dlportio_output_byte;
  740. else {
  741. sym.void_ptr = get_symbol(io_driver, "Out32", cd64);
  742. Outp32 = (void (__stdcall *)(short, short)) sym.func_ptr;
  743. output_byte = inpout32_output_byte;
  744. }
  745. }
  746. }
  747. }
  748. {
  749. /* __try and __except are not supported by MinGW and Cygwin. MinGW has
  750. * __try1 and __except1, but using them requires more code than we
  751. * currently have. Cygwin does something stupid which breaks
  752. * SetUnhandledExceptionFilter()... */
  753. #ifdef __CYGWIN__ /* Cygwin */
  754. EXCEPTION_REGISTRATION exception_registration;
  755. exception_registration.handler = new_exception_handler;
  756. __asm__ __volatile__
  757. ("movl %%fs:0, %0\n"
  758. "movl %1, %%fs:0"
  759. : "=a" (exception_registration.prev)
  760. : "b" (&exception_registration)
  761. );
  762. input_byte(0x378); /* 0x378 is okay */
  763. /* if we get here accessing I/O port 0x378 did not cause an exception */
  764. __asm__ __volatile__
  765. ("movl %0, %%fs:0"
  766. :
  767. : "r" (exception_registration.prev)
  768. );
  769. #elif defined _WIN32 /* MinGW & Visual C++ */
  770. LPTOP_LEVEL_EXCEPTION_FILTER org_exception_filter =
  771. SetUnhandledExceptionFilter((LPTOP_LEVEL_EXCEPTION_FILTER) new_exception_filter);
  772. input_byte(0x378); /* 0x378 is okay */
  773. /* if we get here accessing I/O port 0x378 did not cause an exception */
  774. SetUnhandledExceptionFilter(org_exception_filter);
  775. #endif
  776. }
  777. #ifdef _MSC_VER
  778. #undef access
  779. #endif
  780. #endif /* _WIN32 || __CYGWIN__ */
  781. return 1;
  782. }
  783. int cd64_close_rawio(struct cd64_t *cd64) {
  784. int ret;
  785. (void) ret;
  786. (void) cd64;
  787. #ifdef __linux__
  788. if (cd64->port < 0x3fd) {
  789. if (cd64->using_ppa) {
  790. ret = ioperm(cd64->port, 3, 0);
  791. }
  792. else {
  793. ret = ioperm(cd64->port, 1, 0);
  794. ret |= ioperm(cd64->port+2, 1, 0);
  795. }
  796. if (ret == -1) {
  797. cd64->notice_callback2("ioperm: %s", strerror(errno));
  798. return 0;
  799. }
  800. }
  801. else {
  802. ret = iopl(0);
  803. if (ret == -1) {
  804. cd64->notice_callback2("iopl: %s", strerror(errno));
  805. return 0;
  806. }
  807. }
  808. #elif defined __OpenBSD__
  809. /* I cannot test i386_set_ioperm(), so I only use i386_iopl() */
  810. ret = i386_iopl(0);
  811. if (ret == -1) {
  812. cd64->notice_callback2("i386_iopl: %s", strerror(errno));
  813. return 0;
  814. }
  815. #elif defined __FreeBSD__
  816. if (close(cd64->portdevfd) == -1) {
  817. cd64->notice_callback2("close: %s", strerror(errno));
  818. return 0;
  819. }
  820. cd64->portdevfd = 0;
  821. #elif defined __BEOS__
  822. if (close(io_portfd) == -1) {
  823. cd64->notice_callback2("close: %s", strerror(errno));
  824. return 0;
  825. }
  826. io_portfd = 0;
  827. #elif defined _WIN32 || defined __CYGWIN__
  828. close_module(io_driver, cd64);
  829. io_driver = NULL;
  830. io_driver_found = 0;
  831. input_byte = inb;
  832. output_byte = outb;
  833. #endif
  834. return 1;
  835. }
  836. static INLINE int cd64_wait_rawio(struct cd64_t *cd64) {
  837. int i = 0;
  838. int reset_tries = 0;
  839. uint8_t status;
  840. uint8_t dir;
  841. i = 0;
  842. if (cd64->using_ppa) {
  843. status = inb((uint16_t) (cd64->port+1));
  844. while(status & 0x80) {
  845. i++;
  846. if (i >= BUSY_THRESHOLD) {
  847. /* The PPA is in a weird state.
  848. * Try to knock some sense into it. */
  849. dir = 1;
  850. status = 0x06 | (dir << 5);
  851. outb(status, (uint16_t) (cd64->port+2));
  852. dir = 0;
  853. status = 0x04 | (dir << 5);
  854. outb(status, (uint16_t) (cd64->port+2));
  855. status = 0x05 | (dir << 5);
  856. outb(status, (uint16_t) (cd64->port+2));
  857. status = 0x04 | (dir << 5);
  858. outb(status, (uint16_t) (cd64->port+2));
  859. reset_tries++;
  860. i = 0;
  861. MSLEEP(1);
  862. }
  863. if (cd64->abort) return 0;
  864. if (reset_tries > MAX_TRIES) {
  865. break;
  866. }
  867. status = inb((uint16_t) (cd64->port+1));
  868. }
  869. }
  870. else { /* Comms link */
  871. status = inb((uint16_t) (cd64->port+2));
  872. while (status & 1) {
  873. /* Do we need to handle a stuck situation here? */
  874. status = inb((uint16_t) (cd64->port+2));
  875. }
  876. }
  877. return (reset_tries < MAX_TRIES);
  878. }
  879. int cd64_xfer_rawio(struct cd64_t *cd64, uint8_t *wr, uint8_t *rd, int delayms) {
  880. uint8_t ctl;
  881. uint8_t dir;
  882. if (cd64->using_ppa) {
  883. if (!cd64_wait_rawio(cd64)) { return 0; }
  884. if (delayms) MSLEEP(delayms);
  885. dir = 1;
  886. ctl = 0x06 | (dir << 5);
  887. outb(ctl, (uint16_t) (cd64->port+2));
  888. if (delayms) MSLEEP(delayms);
  889. if (rd) {
  890. *rd = inb((uint16_t) cd64->port);
  891. #if DEBUG_LOWLEVEL
  892. printf("got %xh", *rd);
  893. if (*rd > 0x20) printf(" (%c)", *rd);
  894. printf("\n");
  895. #endif
  896. }
  897. if (delayms) MSLEEP(delayms);
  898. dir = 0;
  899. ctl = 0x04 | (dir << 5);
  900. outb(ctl, (uint16_t) (cd64->port+2));
  901. if (delayms) MSLEEP(delayms);
  902. if (wr) {
  903. outb(*wr, (uint16_t) cd64->port);
  904. #if DEBUG_LOWLEVEL
  905. printf("put %xh", *wr);
  906. if (*wr > 0x20) printf(" (%c)", *wr);
  907. printf("\n");
  908. #endif
  909. }
  910. if (delayms) MSLEEP(delayms);
  911. ctl = 0x05 | (dir << 5);
  912. outb(ctl, (uint16_t) (cd64->port+2));
  913. if (delayms) MSLEEP(delayms);
  914. ctl = 0x04 | (dir << 5);
  915. outb(ctl, (uint16_t) (cd64->port+2));
  916. }
  917. else { /* Comms link */
  918. outb(*wr, (uint16_t) cd64->port);
  919. if (!cd64_wait_rawio(cd64)) { return 0; }
  920. *rd = inb((uint16_t) cd64->port);
  921. }
  922. return 1;
  923. }
  924. #endif /* CD64_USE_RAWIO */