* 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 <Drivers.h>
#include <KernelExport.h>
#include <driver_settings.h>
#include <OS.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <ISA.h>
#define _BUILDING_kernel 1
#if _BUILDING_kernel
#include <debug.h>
#endif
#include "laplinkll.h"
enum {
st_sync = 0,
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;
if ((read_status(st->port) & 0xf8) != 0x80)
return B_TIMED_OUT;
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);
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);
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));
do {
s = read_status(st->port);
if (tries-- == 0)
goto err;
spin(LPTSPIN);
} while (!(s & 0x80));
st->wstate = st_lsb;
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;
do {
s = read_status(st->port);
if (tries-- == 0)
goto err;
spin(LPTSPIN);
} while ((s & 0x80) || (s != read_status(st->port)));
*v = (s >> 3) & 0x0f;
st->rstate = st_msb;
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;
do {
s = read_status(st->port);
if (tries-- == 0)
goto err;
spin(LPTSPIN);
} while (!(s & 0x80) || (s != read_status(st->port)));
*v |= (s << (4-3)) & 0xf0;
st->rstate = st_sync;
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);
return err;
}
#pragma mark // frame mode
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;
ll_send_eof(st);
err:
if (err) {
*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;
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;
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);
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];
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;
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;
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