2
0

abcpun80.c 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. /*
  2. * abcpun80.c
  3. *
  4. * Emulate a PUN80 network card
  5. */
  6. #include "compiler.h"
  7. #include "common.h"
  8. #include "io.h"
  9. #include "abcio.h"
  10. #include "config.h"
  11. /* ACM channel */
  12. #define PUN_TTY_CHAN 1
  13. #if PUN_TTY_CHAN >= TTY_CHANNELS
  14. # error "PUN_TTY_CHAN out of range"
  15. #endif
  16. #define PUN_DATA TTY_DATA(PUN_TTY_CHAN)
  17. #define PUN_WATERCTL TTY_WATERCTL(PUN_TTY_CHAN)
  18. #define PUN_STATUS TTY_STATUS(PUN_TTY_CHAN)
  19. #define PUN_IRQEN TTY_IRQEN(PUN_TTY_CHAN)
  20. #define PUN_IRQPOL TTY_IRQPOL(PUN_TTY_CHAN)
  21. #define PUN_IRQ TTY_NIRQ(PUN_TTY_CHAN)
  22. #define PUN_IRQ_MASK (TTY_STATUS_TX_HIGH|TTY_STATUS_RX_LOW|\
  23. TTY_STATUS_RX_EMPTY|TTY_STATUS_DTR_IN)
  24. static struct abc_dev pun80_iodev;
  25. /*
  26. * Convert to FT232H-compatible status codes, and advance
  27. * the receive FIFO if applicable.
  28. */
  29. static __hot void
  30. pun80_refresh_input(struct abc_dev *dev, bool advance)
  31. {
  32. static bool data_loaded;
  33. unsigned int status;
  34. uint8_t pun80_status;
  35. status = PUN_STATUS;
  36. if (!data_loaded || advance) {
  37. data_loaded = !(status & TTY_STATUS_RX_EMPTY);
  38. if (data_loaded) {
  39. dev->inp_data_def = dev->inp_data[0] = PUN_DATA;
  40. status = PUN_STATUS;
  41. }
  42. }
  43. PUN_IRQPOL = status;
  44. pun80_status = 0xe6;
  45. pun80_status ^= !!(status & TTY_STATUS_RX_LOW) << 4;
  46. pun80_status ^= !!(status & TTY_STATUS_USB_CONFIG) << 3;
  47. pun80_status ^= !!(status & TTY_STATUS_DTR_IN) << 2;
  48. pun80_status ^= !!(status & TTY_STATUS_TX_HIGH) << 1;
  49. pun80_status ^= data_loaded;
  50. dev->inp_data[1] = pun80_status;
  51. if (abc_selected_dev() == dev)
  52. ABC_INP = dev->inp_data_w;
  53. }
  54. static ABC_CALLBACK(pun80_callback_out)
  55. {
  56. PUN_DATA = data;
  57. CON_DATA = '>';
  58. pun80_refresh_input(dev, false);
  59. }
  60. static ABC_CALLBACK(pun80_callback_inp)
  61. {
  62. CON_DATA = '<';
  63. pun80_refresh_input(dev, true);
  64. }
  65. IRQHANDLER(tty,PUN_TTY_CHAN)
  66. {
  67. CON_DATA = '#';
  68. pun80_refresh_input(&pun80_iodev, false);
  69. }
  70. /* The "flush" command in FT232H (C1#) is not needed with our USB stack */
  71. static struct abc_dev pun80_iodev = {
  72. .callback_mask = (1 << 0)|(1 << 8),
  73. .inp_en = 3,
  74. .inp_data[1] = 0xf6,
  75. .status_first_out_mask = ~0,
  76. .status_first_inp_mask = ~0,
  77. .callback_out[0] = pun80_callback_out,
  78. .callback_inp[0] = pun80_callback_inp,
  79. .name = "pun80"
  80. };
  81. void pun80_init(void)
  82. {
  83. mask_irq(PUN_IRQ);
  84. /*
  85. * Set the TX high water mark to 1/4 = 256 bytes free. This allows
  86. * OTIR to be used unconditionally for large transfers after
  87. * polling the TX buffer status only once. The buffer is large
  88. * enough that this should always be true in practice.
  89. *
  90. * The RX low water mark is set to 1/8 = 128 bytes occupied for
  91. * similar reasons. This is added as a non-FT232H status bit 4 if
  92. * 0 (FT232H always has 1 in this bit position.)
  93. */
  94. PUN_WATERCTL = TTY_WATERCTL_TX_LOW(0x3) | TTY_WATERCTL_TX_HIGH(0xc) |
  95. TTY_WATERCTL_RX_LOW(0x1) | TTY_WATERCTL_TX_HIGH(0xc);
  96. /*
  97. * Immediately interrupt on any of:
  98. *
  99. * USB configured
  100. * DTR asserted (host connected)
  101. * TX not past high water mark
  102. * RX not empty
  103. */
  104. PUN_IRQPOL = TTY_STATUS_RX_EMPTY | TTY_STATUS_RX_LOW | TTY_STATUS_TX_HIGH;
  105. PUN_IRQEN = PUN_IRQ_MASK;
  106. unmask_irq(PUN_IRQ);
  107. pun80_config();
  108. }
  109. void pun80_config(void)
  110. {
  111. unsigned int devsel = getvar_uint(config_abc_io_pun80_devsel);
  112. if (!getvar_bool(config_abc_io_pun80_enable))
  113. devsel = DEVSEL_NONE;
  114. abc_register(&pun80_iodev, devsel);
  115. }