/* * Copyright 2008, Haiku, Inc. All Rights Reserved. * Distributed under the terms of the MIT License. * * Authors: * François Revol, revol@free.fr * * Copyright 2005, François Revol. */ /* Description: Implements a tty on top of the parallel port, using PLIP-like byte-by-byte protocol. Low level stuff. */ #include //#include #include #include #include #include #include #include #include #include #include //XXX: move to Jamfile when adding driver #define _BUILDING_kernel 1 #if _BUILDING_kernel #include #endif #include "laplinkll.h" enum { st_sync = 0, // syncing... st_lsb, st_msb, st_error }; static isa_module_info *sISAModule; #pragma mark // raw access static inline uint8 read_status(uint32 port) { uint8 val; val = sISAModule->read_io_8(port+1); return val; } static inline void write_control(uint32 port, uint8 val) { sISAModule->write_io_8(port+2, val); } static inline void write_data(uint32 port, uint8 val) { sISAModule->write_io_8(port, val); } #pragma mark // framing status_t ll_send_sof(laplink_state *st) { uint8 v; int tries = LPTSOFTRIES; if (st->rstate != st_sync || st->wstate != st_sync) return B_TIMED_OUT; // check for idle bus if ((read_status(st->port) & 0xf8) != 0x80) return B_TIMED_OUT; // raise ACK write_data(st->port, 0x08); do { spin(LPTSPIN); v = read_status(st->port); if (st->rstate != st_sync) return B_TIMED_OUT; if (tries-- == 0) return B_TIMED_OUT; } while (!(v & 0x08)); st->wstate = st_lsb; return B_OK; } status_t ll_check_sof(laplink_state *st) { uint8 v; if (st->rstate != st_sync || st->wstate != st_sync) return EINTR; v = read_status(st->port); if ((v & 0xf8) != 0xc0) return EINTR; return B_OK; } status_t ll_ack_sof(laplink_state *st) { write_data(st->port, 0x01); // ack the sof st->rstate = st_lsb; return B_OK; } status_t ll_send_eof(laplink_state *st) { /* if (st->rstate != st_sync || st->wstate != st_sync) return B_TIMED_OUT; */ st->rstate = st_sync; st->wstate = st_sync; write_data(st->port, 0x00); return B_OK; } #pragma mark // nibbles status_t ll_send_lnibble(laplink_state *st, uint8 v) { int tries = LPTNIBTRIES; uint8 s; if (st->rstate != st_sync) goto err; if (st->wstate != st_lsb) goto err; write_data(st->port, v & 0x0f); spin(10); write_data(st->port, (v & 0x0f) | 0x10); // wait for ack do { s = read_status(st->port); if (tries-- == 0) goto err; spin(LPTSPIN); } while (s & 0x80); st->wstate = st_msb; return B_OK; err: st->wstate = st_sync; return B_TIMED_OUT; } status_t ll_send_mnibble(laplink_state *st, uint8 v) { int tries = LPTNIBTRIES; uint8 s; if (st->rstate != st_sync) goto err; if (st->wstate != st_msb) goto err; write_data(st->port, (v >> 4) | 0x10); spin(10); write_data(st->port, (v >> 4)); // wait for ack do { s = read_status(st->port); if (tries-- == 0) goto err; spin(LPTSPIN); } while (!(s & 0x80)); st->wstate = st_lsb;//st_sync; return B_OK; err: st->wstate = st_sync; return B_TIMED_OUT; } status_t ll_wait_lnibble(laplink_state *st, uint8 *v) { int tries = LPTNIBTRIES; uint8 s; // wait for data do { s = read_status(st->port); if (tries-- == 0) goto err; spin(LPTSPIN); } while ((s & 0x80) || (s != read_status(st->port))); // get the nibble *v = (s >> 3) & 0x0f; st->rstate = st_msb; // tell we got that one write_data(st->port, 0x10); return B_OK; err: st->rstate = st_sync; return B_TIMED_OUT; } status_t ll_wait_mnibble(laplink_state *st, uint8 *v) { int tries = LPTNIBTRIES; uint8 s; // wait for data do { s = read_status(st->port); if (tries-- == 0) goto err; spin(LPTSPIN); } while (!(s & 0x80) || (s != read_status(st->port))); // get the nibble *v |= (s << (4-3)) & 0xf0; st->rstate = st_sync; // tell we got that one write_data(st->port, 0x00); return B_OK; err: st->rstate = st_sync; return B_TIMED_OUT; } #pragma mark // byte mode status_t ll_send_byte(laplink_state *st, uint8 v) { status_t err; err = ll_send_sof(st); if (!err) err = ll_send_lnibble(st, v); if (!err) err = ll_send_mnibble(st, v); if (!err) err = ll_send_eof(st); return err; } status_t ll_check_byte(laplink_state *st, uint8 *v) { status_t err; *v = 0; err = ll_check_sof(st); if (err) return err; err = ll_ack_sof(st); if (!err) err = ll_wait_lnibble(st, v); if (!err) err = ll_wait_mnibble(st, v); if (!err) err = ll_send_eof(st); return err; } status_t ll_wait_byte(laplink_state *st, uint8 *v) { status_t err; do { spin(LPTSPIN); err = ll_check_byte(st, v); } while (err < B_OK);// } while (err == B_TIMED_OUT); return err; } #pragma mark // frame mode // unframed static inline status_t ll_send_byte_uf(laplink_state *st, uint8 v) { status_t err; err = ll_send_lnibble(st, v); if (!err) err = ll_send_mnibble(st, v); return err; } status_t ll_get_byte_uf(laplink_state *st, uint8 *v) { status_t err; *v = 0; err = ll_wait_lnibble(st, v); if (!err) err = ll_wait_mnibble(st, v); return err; } status_t ll_send_frame(laplink_state *st, const uint8 *buff, size_t *len) { status_t err; uint16 pktlen = *len; uint8 cksum = 0; *len = 0; err = ll_send_sof(st); if (err) return err; err = ll_send_byte_uf(st, pktlen & 0xff); if (err) goto err; err = ll_send_byte_uf(st, pktlen >> 8); if (err) goto err; for (*len = 0; *len < pktlen; (*len)++) { err = ll_send_byte_uf(st, buff[*len]); if (err) goto err; cksum += buff[*len]; } err = ll_send_byte_uf(st, cksum); if (err) goto err; /*err =*/ ll_send_eof(st); err: if (err) { // back to idle *len = 0; ll_send_eof(st); } return err; } status_t ll_check_frame(laplink_state *st, uint8 *buff, size_t *len) { status_t err; uint16 pktlen = 0; uint16 wanted; uint8 cksum = 0; uint8 byte; int i; err = ll_check_sof(st); if (err) goto err; err = ll_ack_sof(st); if (err) goto err; // pktlen err = ll_get_byte_uf(st, &byte); if (err) goto err; pktlen = byte; err = ll_get_byte_uf(st, &byte); if (err) goto err; pktlen |= byte << 8; cksum = 0; /*if (pktlen > *len) { dprintf("laplink: check_frame: packet truncated from %d to %d\n", pktlen, *len); }*/ wanted = MIN(pktlen, *len); for (*len = 0; (*len < pktlen); (*len)++) { err = ll_get_byte_uf(st, &byte); if (err) goto err; if (*len < wanted) buff[*len] = byte; cksum += byte; } err = ll_get_byte_uf(st, &byte); if (err) goto err; /* if (cksum != byte) { dprintf("laplink: check_frame: wrong cksum\n"); }*/ err: ll_send_eof(st); return err; } status_t ll_wait_frame(laplink_state *st, uint8 *buff, size_t *len) { status_t err; do { spin(LPTSPIN); err = ll_check_frame(st, buff, len); } while (err < B_OK);// } while (err == B_TIMED_OUT); return err; } #pragma mark // kdebug io handler #if _BUILDING_kernel #define BUFFSZ 256 static laplink_state llst; static char laplink_in_buf[BUFFSZ]; static char *laplink_in_ptr; static size_t laplink_in_avail; static char laplink_out_buf[BUFFSZ]; //XXX: cleanup static status_t debug_init_laplink(void *kernel_settings) { (void)kernel_settings; llst.port = LPTBASE; llst.rstate = st_sync; llst.wstate = st_sync; laplink_in_ptr = laplink_in_buf; laplink_in_avail = 0; return B_OK; } static int debug_write_laplink(int f, const char *buf, int count) { status_t err; size_t len; int tries; int i, prev = 0; // fix up CR LF issues... to a local buffer (which will get truncated) if (count > 1) { for (i = 0; (i < BUFFSZ-1) && count; i++, buf++, count--) { if ((*buf == '\n') && (prev != '\r')) laplink_out_buf[i++] = '\r'; laplink_out_buf[i] = *buf; prev = *buf; } count = i; buf = laplink_out_buf; } tries = 5; do { len = count; err = ll_send_frame(&llst, (const uint8 *)buf, &len); } while (err && tries--); if (err) return 0; return len; } static int debug_read_laplink(void) { status_t err = B_OK; while (laplink_in_avail < 1) { laplink_in_avail = BUFFSZ; laplink_in_ptr = laplink_in_buf; err = ll_wait_frame(&llst, (uint8 *)laplink_in_buf, &laplink_in_avail); if (err) laplink_in_avail = 0; } laplink_in_avail--; return *laplink_in_ptr++; } static int debugger_puts(const char *s, int32 length) { return debug_write_laplink(0, s, (int)length); } static status_t std_ops(int32 op, ...) { void *handle; bool load = true;//false; switch (op) { case B_MODULE_INIT: handle = load_driver_settings("kernel"); if (handle) { load = get_driver_boolean_parameter(handle, "laplinkll_debug_output", load, true); unload_driver_settings(handle); } if (load) { if (get_module(B_ISA_MODULE_NAME, (module_info **)&sISAModule) < B_OK) return B_ERROR; debug_init_laplink(NULL); } return load ? B_OK : B_ERROR; case B_MODULE_UNINIT: put_module(B_ISA_MODULE_NAME); return B_OK; } return B_BAD_VALUE; } static struct debugger_module_info sModuleInfo = { { "debugger/laplinkll/v1", 0, &std_ops }, NULL, NULL, debugger_puts, NULL }; module_info *modules[] = { (module_info *)&sModuleInfo, NULL }; #endif