ipoctal232.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619
  1. /*
  2. * QEMU GE IP-Octal 232 IndustryPack emulation
  3. *
  4. * Copyright (C) 2012 Igalia, S.L.
  5. * Author: Alberto Garcia <agarcia@igalia.com>
  6. *
  7. * This code is licensed under the GNU GPL v2 or (at your option) any
  8. * later version.
  9. */
  10. #include "ipack.h"
  11. #include "qemu/bitops.h"
  12. #include "char/char.h"
  13. /* #define DEBUG_IPOCTAL */
  14. #ifdef DEBUG_IPOCTAL
  15. #define DPRINTF2(fmt, ...) \
  16. do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0)
  17. #else
  18. #define DPRINTF2(fmt, ...) do { } while (0)
  19. #endif
  20. #define DPRINTF(fmt, ...) DPRINTF2("IP-Octal: " fmt, ## __VA_ARGS__)
  21. #define RX_FIFO_SIZE 3
  22. /* The IP-Octal has 8 channels (a-h)
  23. divided into 4 blocks (A-D) */
  24. #define N_CHANNELS 8
  25. #define N_BLOCKS 4
  26. #define REG_MRa 0x01
  27. #define REG_MRb 0x11
  28. #define REG_SRa 0x03
  29. #define REG_SRb 0x13
  30. #define REG_CSRa 0x03
  31. #define REG_CSRb 0x13
  32. #define REG_CRa 0x05
  33. #define REG_CRb 0x15
  34. #define REG_RHRa 0x07
  35. #define REG_RHRb 0x17
  36. #define REG_THRa 0x07
  37. #define REG_THRb 0x17
  38. #define REG_ACR 0x09
  39. #define REG_ISR 0x0B
  40. #define REG_IMR 0x0B
  41. #define REG_OPCR 0x1B
  42. #define CR_ENABLE_RX BIT(0)
  43. #define CR_DISABLE_RX BIT(1)
  44. #define CR_ENABLE_TX BIT(2)
  45. #define CR_DISABLE_TX BIT(3)
  46. #define CR_CMD(cr) ((cr) >> 4)
  47. #define CR_NO_OP 0
  48. #define CR_RESET_MR 1
  49. #define CR_RESET_RX 2
  50. #define CR_RESET_TX 3
  51. #define CR_RESET_ERR 4
  52. #define CR_RESET_BRKINT 5
  53. #define CR_START_BRK 6
  54. #define CR_STOP_BRK 7
  55. #define CR_ASSERT_RTSN 8
  56. #define CR_NEGATE_RTSN 9
  57. #define CR_TIMEOUT_ON 10
  58. #define CR_TIMEOUT_OFF 12
  59. #define SR_RXRDY BIT(0)
  60. #define SR_FFULL BIT(1)
  61. #define SR_TXRDY BIT(2)
  62. #define SR_TXEMT BIT(3)
  63. #define SR_OVERRUN BIT(4)
  64. #define SR_PARITY BIT(5)
  65. #define SR_FRAMING BIT(6)
  66. #define SR_BREAK BIT(7)
  67. #define ISR_TXRDYA BIT(0)
  68. #define ISR_RXRDYA BIT(1)
  69. #define ISR_BREAKA BIT(2)
  70. #define ISR_CNTRDY BIT(3)
  71. #define ISR_TXRDYB BIT(4)
  72. #define ISR_RXRDYB BIT(5)
  73. #define ISR_BREAKB BIT(6)
  74. #define ISR_MPICHG BIT(7)
  75. #define ISR_TXRDY(CH) (((CH) & 1) ? BIT(4) : BIT(0))
  76. #define ISR_RXRDY(CH) (((CH) & 1) ? BIT(5) : BIT(1))
  77. #define ISR_BREAK(CH) (((CH) & 1) ? BIT(6) : BIT(2))
  78. typedef struct IPOctalState IPOctalState;
  79. typedef struct SCC2698Channel SCC2698Channel;
  80. typedef struct SCC2698Block SCC2698Block;
  81. struct SCC2698Channel {
  82. IPOctalState *ipoctal;
  83. CharDriverState *dev;
  84. char *devpath;
  85. bool rx_enabled;
  86. uint8_t mr[2];
  87. uint8_t mr_idx;
  88. uint8_t sr;
  89. uint8_t rhr[RX_FIFO_SIZE];
  90. uint8_t rhr_idx;
  91. uint8_t rx_pending;
  92. };
  93. struct SCC2698Block {
  94. uint8_t imr;
  95. uint8_t isr;
  96. };
  97. struct IPOctalState {
  98. IPackDevice dev;
  99. SCC2698Channel ch[N_CHANNELS];
  100. SCC2698Block blk[N_BLOCKS];
  101. uint8_t irq_vector;
  102. };
  103. #define TYPE_IPOCTAL "ipoctal232"
  104. #define IPOCTAL(obj) \
  105. OBJECT_CHECK(IPOctalState, (obj), TYPE_IPOCTAL)
  106. static const VMStateDescription vmstate_scc2698_channel = {
  107. .name = "scc2698_channel",
  108. .version_id = 1,
  109. .minimum_version_id = 1,
  110. .minimum_version_id_old = 1,
  111. .fields = (VMStateField[]) {
  112. VMSTATE_BOOL(rx_enabled, SCC2698Channel),
  113. VMSTATE_UINT8_ARRAY(mr, SCC2698Channel, 2),
  114. VMSTATE_UINT8(mr_idx, SCC2698Channel),
  115. VMSTATE_UINT8(sr, SCC2698Channel),
  116. VMSTATE_UINT8_ARRAY(rhr, SCC2698Channel, RX_FIFO_SIZE),
  117. VMSTATE_UINT8(rhr_idx, SCC2698Channel),
  118. VMSTATE_UINT8(rx_pending, SCC2698Channel),
  119. VMSTATE_END_OF_LIST()
  120. }
  121. };
  122. static const VMStateDescription vmstate_scc2698_block = {
  123. .name = "scc2698_block",
  124. .version_id = 1,
  125. .minimum_version_id = 1,
  126. .minimum_version_id_old = 1,
  127. .fields = (VMStateField[]) {
  128. VMSTATE_UINT8(imr, SCC2698Block),
  129. VMSTATE_UINT8(isr, SCC2698Block),
  130. VMSTATE_END_OF_LIST()
  131. }
  132. };
  133. static const VMStateDescription vmstate_ipoctal = {
  134. .name = "ipoctal232",
  135. .version_id = 1,
  136. .minimum_version_id = 1,
  137. .minimum_version_id_old = 1,
  138. .fields = (VMStateField[]) {
  139. VMSTATE_IPACK_DEVICE(dev, IPOctalState),
  140. VMSTATE_STRUCT_ARRAY(ch, IPOctalState, N_CHANNELS, 1,
  141. vmstate_scc2698_channel, SCC2698Channel),
  142. VMSTATE_STRUCT_ARRAY(blk, IPOctalState, N_BLOCKS, 1,
  143. vmstate_scc2698_block, SCC2698Block),
  144. VMSTATE_UINT8(irq_vector, IPOctalState),
  145. VMSTATE_END_OF_LIST()
  146. }
  147. };
  148. /* data[10] is 0x0C, not 0x0B as the doc says */
  149. static const uint8_t id_prom_data[] = {
  150. 0x49, 0x50, 0x41, 0x43, 0xF0, 0x22,
  151. 0xA1, 0x00, 0x00, 0x00, 0x0C, 0xCC
  152. };
  153. static void update_irq(IPOctalState *dev, unsigned block)
  154. {
  155. /* Blocks A and B interrupt on INT0#, C and D on INT1#.
  156. Thus, to get the status we have to check two blocks. */
  157. SCC2698Block *blk0 = &dev->blk[block];
  158. SCC2698Block *blk1 = &dev->blk[block^1];
  159. unsigned intno = block / 2;
  160. if ((blk0->isr & blk0->imr) || (blk1->isr & blk1->imr)) {
  161. qemu_irq_raise(dev->dev.irq[intno]);
  162. } else {
  163. qemu_irq_lower(dev->dev.irq[intno]);
  164. }
  165. }
  166. static void write_cr(IPOctalState *dev, unsigned channel, uint8_t val)
  167. {
  168. SCC2698Channel *ch = &dev->ch[channel];
  169. SCC2698Block *blk = &dev->blk[channel / 2];
  170. DPRINTF("Write CR%c %u: ", channel + 'a', val);
  171. /* The lower 4 bits are used to enable and disable Tx and Rx */
  172. if (val & CR_ENABLE_RX) {
  173. DPRINTF2("Rx on, ");
  174. ch->rx_enabled = true;
  175. }
  176. if (val & CR_DISABLE_RX) {
  177. DPRINTF2("Rx off, ");
  178. ch->rx_enabled = false;
  179. }
  180. if (val & CR_ENABLE_TX) {
  181. DPRINTF2("Tx on, ");
  182. ch->sr |= SR_TXRDY | SR_TXEMT;
  183. blk->isr |= ISR_TXRDY(channel);
  184. }
  185. if (val & CR_DISABLE_TX) {
  186. DPRINTF2("Tx off, ");
  187. ch->sr &= ~(SR_TXRDY | SR_TXEMT);
  188. blk->isr &= ~ISR_TXRDY(channel);
  189. }
  190. DPRINTF2("cmd: ");
  191. /* The rest of the bits implement different commands */
  192. switch (CR_CMD(val)) {
  193. case CR_NO_OP:
  194. DPRINTF2("none");
  195. break;
  196. case CR_RESET_MR:
  197. DPRINTF2("reset MR");
  198. ch->mr_idx = 0;
  199. break;
  200. case CR_RESET_RX:
  201. DPRINTF2("reset Rx");
  202. ch->rx_enabled = false;
  203. ch->rx_pending = 0;
  204. ch->sr &= ~SR_RXRDY;
  205. blk->isr &= ~ISR_RXRDY(channel);
  206. break;
  207. case CR_RESET_TX:
  208. DPRINTF2("reset Tx");
  209. ch->sr &= ~(SR_TXRDY | SR_TXEMT);
  210. blk->isr &= ~ISR_TXRDY(channel);
  211. break;
  212. case CR_RESET_ERR:
  213. DPRINTF2("reset err");
  214. ch->sr &= ~(SR_OVERRUN | SR_PARITY | SR_FRAMING | SR_BREAK);
  215. break;
  216. case CR_RESET_BRKINT:
  217. DPRINTF2("reset brk ch int");
  218. blk->isr &= ~(ISR_BREAKA | ISR_BREAKB);
  219. break;
  220. default:
  221. DPRINTF2("unsupported 0x%x", CR_CMD(val));
  222. }
  223. DPRINTF2("\n");
  224. }
  225. static uint16_t io_read(IPackDevice *ip, uint8_t addr)
  226. {
  227. IPOctalState *dev = IPOCTAL(ip);
  228. uint16_t ret = 0;
  229. /* addr[7:6]: block (A-D)
  230. addr[7:5]: channel (a-h)
  231. addr[5:0]: register */
  232. unsigned block = addr >> 5;
  233. unsigned channel = addr >> 4;
  234. /* Big endian, accessed using 8-bit bytes at odd locations */
  235. unsigned offset = (addr & 0x1F) ^ 1;
  236. SCC2698Channel *ch = &dev->ch[channel];
  237. SCC2698Block *blk = &dev->blk[block];
  238. uint8_t old_isr = blk->isr;
  239. switch (offset) {
  240. case REG_MRa:
  241. case REG_MRb:
  242. ret = ch->mr[ch->mr_idx];
  243. DPRINTF("Read MR%u%c: 0x%x\n", ch->mr_idx + 1, channel + 'a', ret);
  244. ch->mr_idx = 1;
  245. break;
  246. case REG_SRa:
  247. case REG_SRb:
  248. ret = ch->sr;
  249. DPRINTF("Read SR%c: 0x%x\n", channel + 'a', ret);
  250. break;
  251. case REG_RHRa:
  252. case REG_RHRb:
  253. ret = ch->rhr[ch->rhr_idx];
  254. if (ch->rx_pending > 0) {
  255. ch->rx_pending--;
  256. if (ch->rx_pending == 0) {
  257. ch->sr &= ~SR_RXRDY;
  258. blk->isr &= ~ISR_RXRDY(channel);
  259. if (ch->dev) {
  260. qemu_chr_accept_input(ch->dev);
  261. }
  262. } else {
  263. ch->rhr_idx = (ch->rhr_idx + 1) % RX_FIFO_SIZE;
  264. }
  265. if (ch->sr & SR_BREAK) {
  266. ch->sr &= ~SR_BREAK;
  267. blk->isr |= ISR_BREAK(channel);
  268. }
  269. }
  270. DPRINTF("Read RHR%c (0x%x)\n", channel + 'a', ret);
  271. break;
  272. case REG_ISR:
  273. ret = blk->isr;
  274. DPRINTF("Read ISR%c: 0x%x\n", block + 'A', ret);
  275. break;
  276. default:
  277. DPRINTF("Read unknown/unsupported register 0x%02x\n", offset);
  278. }
  279. if (old_isr != blk->isr) {
  280. update_irq(dev, block);
  281. }
  282. return ret;
  283. }
  284. static void io_write(IPackDevice *ip, uint8_t addr, uint16_t val)
  285. {
  286. IPOctalState *dev = IPOCTAL(ip);
  287. unsigned reg = val & 0xFF;
  288. /* addr[7:6]: block (A-D)
  289. addr[7:5]: channel (a-h)
  290. addr[5:0]: register */
  291. unsigned block = addr >> 5;
  292. unsigned channel = addr >> 4;
  293. /* Big endian, accessed using 8-bit bytes at odd locations */
  294. unsigned offset = (addr & 0x1F) ^ 1;
  295. SCC2698Channel *ch = &dev->ch[channel];
  296. SCC2698Block *blk = &dev->blk[block];
  297. uint8_t old_isr = blk->isr;
  298. uint8_t old_imr = blk->imr;
  299. switch (offset) {
  300. case REG_MRa:
  301. case REG_MRb:
  302. ch->mr[ch->mr_idx] = reg;
  303. DPRINTF("Write MR%u%c 0x%x\n", ch->mr_idx + 1, channel + 'a', reg);
  304. ch->mr_idx = 1;
  305. break;
  306. /* Not implemented */
  307. case REG_CSRa:
  308. case REG_CSRb:
  309. DPRINTF("Write CSR%c: 0x%x\n", channel + 'a', reg);
  310. break;
  311. case REG_CRa:
  312. case REG_CRb:
  313. write_cr(dev, channel, reg);
  314. break;
  315. case REG_THRa:
  316. case REG_THRb:
  317. if (ch->sr & SR_TXRDY) {
  318. DPRINTF("Write THR%c (0x%x)\n", channel + 'a', reg);
  319. if (ch->dev) {
  320. uint8_t thr = reg;
  321. qemu_chr_fe_write(ch->dev, &thr, 1);
  322. }
  323. } else {
  324. DPRINTF("Write THR%c (0x%x), Tx disabled\n", channel + 'a', reg);
  325. }
  326. break;
  327. /* Not implemented */
  328. case REG_ACR:
  329. DPRINTF("Write ACR%c 0x%x\n", block + 'A', val);
  330. break;
  331. case REG_IMR:
  332. DPRINTF("Write IMR%c 0x%x\n", block + 'A', val);
  333. blk->imr = reg;
  334. break;
  335. /* Not implemented */
  336. case REG_OPCR:
  337. DPRINTF("Write OPCR%c 0x%x\n", block + 'A', val);
  338. break;
  339. default:
  340. DPRINTF("Write unknown/unsupported register 0x%02x %u\n", offset, val);
  341. }
  342. if (old_isr != blk->isr || old_imr != blk->imr) {
  343. update_irq(dev, block);
  344. }
  345. }
  346. static uint16_t id_read(IPackDevice *ip, uint8_t addr)
  347. {
  348. uint16_t ret = 0;
  349. unsigned pos = addr / 2; /* The ID PROM data is stored every other byte */
  350. if (pos < ARRAY_SIZE(id_prom_data)) {
  351. ret = id_prom_data[pos];
  352. } else {
  353. DPRINTF("Attempt to read unavailable PROM data at 0x%x\n", addr);
  354. }
  355. return ret;
  356. }
  357. static void id_write(IPackDevice *ip, uint8_t addr, uint16_t val)
  358. {
  359. IPOctalState *dev = IPOCTAL(ip);
  360. if (addr == 1) {
  361. DPRINTF("Write IRQ vector: %u\n", (unsigned) val);
  362. dev->irq_vector = val; /* Undocumented, but the hw works like that */
  363. } else {
  364. DPRINTF("Attempt to write 0x%x to 0x%x\n", val, addr);
  365. }
  366. }
  367. static uint16_t int_read(IPackDevice *ip, uint8_t addr)
  368. {
  369. IPOctalState *dev = IPOCTAL(ip);
  370. /* Read address 0 to ACK INT0# and address 2 to ACK INT1# */
  371. if (addr != 0 && addr != 2) {
  372. DPRINTF("Attempt to read from 0x%x\n", addr);
  373. return 0;
  374. } else {
  375. /* Update interrupts if necessary */
  376. update_irq(dev, addr);
  377. return dev->irq_vector;
  378. }
  379. }
  380. static void int_write(IPackDevice *ip, uint8_t addr, uint16_t val)
  381. {
  382. DPRINTF("Attempt to write 0x%x to 0x%x\n", val, addr);
  383. }
  384. static uint16_t mem_read16(IPackDevice *ip, uint32_t addr)
  385. {
  386. DPRINTF("Attempt to read from 0x%x\n", addr);
  387. return 0;
  388. }
  389. static void mem_write16(IPackDevice *ip, uint32_t addr, uint16_t val)
  390. {
  391. DPRINTF("Attempt to write 0x%x to 0x%x\n", val, addr);
  392. }
  393. static uint8_t mem_read8(IPackDevice *ip, uint32_t addr)
  394. {
  395. DPRINTF("Attempt to read from 0x%x\n", addr);
  396. return 0;
  397. }
  398. static void mem_write8(IPackDevice *ip, uint32_t addr, uint8_t val)
  399. {
  400. IPOctalState *dev = IPOCTAL(ip);
  401. if (addr == 1) {
  402. DPRINTF("Write IRQ vector: %u\n", (unsigned) val);
  403. dev->irq_vector = val;
  404. } else {
  405. DPRINTF("Attempt to write 0x%x to 0x%x\n", val, addr);
  406. }
  407. }
  408. static int hostdev_can_receive(void *opaque)
  409. {
  410. SCC2698Channel *ch = opaque;
  411. int available_bytes = RX_FIFO_SIZE - ch->rx_pending;
  412. return ch->rx_enabled ? available_bytes : 0;
  413. }
  414. static void hostdev_receive(void *opaque, const uint8_t *buf, int size)
  415. {
  416. SCC2698Channel *ch = opaque;
  417. IPOctalState *dev = ch->ipoctal;
  418. unsigned pos = ch->rhr_idx + ch->rx_pending;
  419. int i;
  420. assert(size + ch->rx_pending <= RX_FIFO_SIZE);
  421. /* Copy data to the RxFIFO */
  422. for (i = 0; i < size; i++) {
  423. pos %= RX_FIFO_SIZE;
  424. ch->rhr[pos++] = buf[i];
  425. }
  426. ch->rx_pending += size;
  427. /* If the RxFIFO was empty raise an interrupt */
  428. if (!(ch->sr & SR_RXRDY)) {
  429. unsigned block, channel = 0;
  430. /* Find channel number to update the ISR register */
  431. while (&dev->ch[channel] != ch) {
  432. channel++;
  433. }
  434. block = channel / 2;
  435. dev->blk[block].isr |= ISR_RXRDY(channel);
  436. ch->sr |= SR_RXRDY;
  437. update_irq(dev, block);
  438. }
  439. }
  440. static void hostdev_event(void *opaque, int event)
  441. {
  442. SCC2698Channel *ch = opaque;
  443. switch (event) {
  444. case CHR_EVENT_OPENED:
  445. DPRINTF("Device %s opened\n", ch->dev->label);
  446. break;
  447. case CHR_EVENT_BREAK: {
  448. uint8_t zero = 0;
  449. DPRINTF("Device %s received break\n", ch->dev->label);
  450. if (!(ch->sr & SR_BREAK)) {
  451. IPOctalState *dev = ch->ipoctal;
  452. unsigned block, channel = 0;
  453. while (&dev->ch[channel] != ch) {
  454. channel++;
  455. }
  456. block = channel / 2;
  457. ch->sr |= SR_BREAK;
  458. dev->blk[block].isr |= ISR_BREAK(channel);
  459. }
  460. /* Put a zero character in the buffer */
  461. hostdev_receive(ch, &zero, 1);
  462. }
  463. break;
  464. default:
  465. DPRINTF("Device %s received event %d\n", ch->dev->label, event);
  466. }
  467. }
  468. static int ipoctal_init(IPackDevice *ip)
  469. {
  470. IPOctalState *s = IPOCTAL(ip);
  471. unsigned i;
  472. for (i = 0; i < N_CHANNELS; i++) {
  473. SCC2698Channel *ch = &s->ch[i];
  474. ch->ipoctal = s;
  475. /* Redirect IP-Octal channels to host character devices */
  476. if (ch->devpath) {
  477. const char chr_name[] = "ipoctal";
  478. char label[ARRAY_SIZE(chr_name) + 2];
  479. static int index;
  480. snprintf(label, sizeof(label), "%s%d", chr_name, index);
  481. ch->dev = qemu_chr_new(label, ch->devpath, NULL);
  482. if (ch->dev) {
  483. index++;
  484. qemu_chr_add_handlers(ch->dev, hostdev_can_receive,
  485. hostdev_receive, hostdev_event, ch);
  486. DPRINTF("Redirecting channel %u to %s (%s)\n",
  487. i, ch->devpath, label);
  488. } else {
  489. DPRINTF("Could not redirect channel %u to %s\n",
  490. i, ch->devpath);
  491. }
  492. }
  493. }
  494. return 0;
  495. }
  496. static Property ipoctal_properties[] = {
  497. DEFINE_PROP_STRING("serial0", IPOctalState, ch[0].devpath),
  498. DEFINE_PROP_STRING("serial1", IPOctalState, ch[1].devpath),
  499. DEFINE_PROP_STRING("serial2", IPOctalState, ch[2].devpath),
  500. DEFINE_PROP_STRING("serial3", IPOctalState, ch[3].devpath),
  501. DEFINE_PROP_STRING("serial4", IPOctalState, ch[4].devpath),
  502. DEFINE_PROP_STRING("serial5", IPOctalState, ch[5].devpath),
  503. DEFINE_PROP_STRING("serial6", IPOctalState, ch[6].devpath),
  504. DEFINE_PROP_STRING("serial7", IPOctalState, ch[7].devpath),
  505. DEFINE_PROP_END_OF_LIST(),
  506. };
  507. static void ipoctal_class_init(ObjectClass *klass, void *data)
  508. {
  509. DeviceClass *dc = DEVICE_CLASS(klass);
  510. IPackDeviceClass *ic = IPACK_DEVICE_CLASS(klass);
  511. ic->init = ipoctal_init;
  512. ic->io_read = io_read;
  513. ic->io_write = io_write;
  514. ic->id_read = id_read;
  515. ic->id_write = id_write;
  516. ic->int_read = int_read;
  517. ic->int_write = int_write;
  518. ic->mem_read16 = mem_read16;
  519. ic->mem_write16 = mem_write16;
  520. ic->mem_read8 = mem_read8;
  521. ic->mem_write8 = mem_write8;
  522. dc->desc = "GE IP-Octal 232 8-channel RS-232 IndustryPack";
  523. dc->props = ipoctal_properties;
  524. dc->vmsd = &vmstate_ipoctal;
  525. }
  526. static const TypeInfo ipoctal_info = {
  527. .name = TYPE_IPOCTAL,
  528. .parent = TYPE_IPACK_DEVICE,
  529. .instance_size = sizeof(IPOctalState),
  530. .class_init = ipoctal_class_init,
  531. };
  532. static void ipoctal_register_types(void)
  533. {
  534. type_register_static(&ipoctal_info);
  535. }
  536. type_init(ipoctal_register_types)