/* * abcpun80.c * * Emulate a PUN80 network card */ #include "compiler.h" #include "common.h" #include "io.h" #include "abcio.h" #include "config.h" /* ACM channel */ #define PUN_TTY_CHAN 1 #if PUN_TTY_CHAN >= TTY_CHANNELS # error "PUN_TTY_CHAN out of range" #endif #define PUN_DATA TTY_DATA(PUN_TTY_CHAN) #define PUN_WATERCTL TTY_WATERCTL(PUN_TTY_CHAN) #define PUN_STATUS TTY_STATUS(PUN_TTY_CHAN) #define PUN_IRQEN TTY_IRQEN(PUN_TTY_CHAN) #define PUN_IRQPOL TTY_IRQPOL(PUN_TTY_CHAN) #define PUN_IRQ TTY_NIRQ(PUN_TTY_CHAN) #define PUN_IRQ_MASK (TTY_STATUS_TX_HIGH|TTY_STATUS_RX_LOW|\ TTY_STATUS_RX_EMPTY|TTY_STATUS_DTR_IN) static struct abc_dev pun80_iodev; /* * Convert to FT232H-compatible status codes, and advance * the receive FIFO if applicable. */ static __hot void pun80_refresh_input(struct abc_dev *dev, bool advance) { static bool data_loaded; unsigned int status; uint8_t pun80_status; status = PUN_STATUS; if (!data_loaded || advance) { data_loaded = !(status & TTY_STATUS_RX_EMPTY); if (data_loaded) { dev->inp_data_def = dev->inp_data[0] = PUN_DATA; status = PUN_STATUS; } } PUN_IRQPOL = status; pun80_status = 0xe6; pun80_status ^= !!(status & TTY_STATUS_RX_LOW) << 4; pun80_status ^= !!(status & TTY_STATUS_USB_CONFIG) << 3; pun80_status ^= !!(status & TTY_STATUS_DTR_IN) << 2; pun80_status ^= !!(status & TTY_STATUS_TX_HIGH) << 1; pun80_status ^= data_loaded; dev->inp_data[1] = pun80_status; if (abc_selected_dev() == dev) ABC_INP = dev->inp_data_w; } static ABC_CALLBACK(pun80_callback_out) { PUN_DATA = data; CON_DATA = '>'; pun80_refresh_input(dev, false); } static ABC_CALLBACK(pun80_callback_inp) { CON_DATA = '<'; pun80_refresh_input(dev, true); } IRQHANDLER(tty,PUN_TTY_CHAN) { CON_DATA = '#'; pun80_refresh_input(&pun80_iodev, false); } /* The "flush" command in FT232H (C1#) is not needed with our USB stack */ static struct abc_dev pun80_iodev = { .callback_mask = (1 << 0)|(1 << 8), .inp_en = 3, .inp_data[1] = 0xf6, .status_first_out_mask = ~0, .status_first_inp_mask = ~0, .callback_out[0] = pun80_callback_out, .callback_inp[0] = pun80_callback_inp, .name = "pun80" }; void pun80_init(void) { mask_irq(PUN_IRQ); /* * Set the TX high water mark to 1/4 = 256 bytes free. This allows * OTIR to be used unconditionally for large transfers after * polling the TX buffer status only once. The buffer is large * enough that this should always be true in practice. * * The RX low water mark is set to 1/8 = 128 bytes occupied for * similar reasons. This is added as a non-FT232H status bit 4 if * 0 (FT232H always has 1 in this bit position.) */ PUN_WATERCTL = TTY_WATERCTL_TX_LOW(0x3) | TTY_WATERCTL_TX_HIGH(0xc) | TTY_WATERCTL_RX_LOW(0x1) | TTY_WATERCTL_TX_HIGH(0xc); /* * Immediately interrupt on any of: * * USB configured * DTR asserted (host connected) * TX not past high water mark * RX not empty */ PUN_IRQPOL = TTY_STATUS_RX_EMPTY | TTY_STATUS_RX_LOW | TTY_STATUS_TX_HIGH; PUN_IRQEN = PUN_IRQ_MASK; unmask_irq(PUN_IRQ); pun80_config(); } void pun80_config(void) { unsigned int devsel = getvar_uint(config_abc_io_pun80_devsel); if (!getvar_bool(config_abc_io_pun80_enable)) devsel = DEVSEL_NONE; abc_register(&pun80_iodev, devsel); }