1/*
2 * Copyright 2008, Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *		François Revol, revol@free.fr
7 *
8 * Copyright 2005, François Revol.
9 */
10
11/*
12	Description: Implements a tty on top of the parallel port,
13				 using PLIP-like byte-by-byte protocol.
14				 Low level stuff.
15*/
16
17
18#include <Drivers.h>
19//#include <parallel_driver.h>
20#include <KernelExport.h>
21#include <driver_settings.h>
22#include <OS.h>
23#include <stdlib.h>
24#include <string.h>
25#include <sys/stat.h>
26#include <sys/types.h>
27#include <unistd.h>
28#include <ISA.h>
29
30//XXX: move to Jamfile when adding driver
31#define _BUILDING_kernel 1
32
33#if _BUILDING_kernel
34#include <debug.h>
35#endif
36
37#include "laplinkll.h"
38
39enum {
40	st_sync = 0, // syncing...
41	st_lsb,
42	st_msb,
43	st_error
44};
45
46static isa_module_info *sISAModule;
47
48#pragma mark // raw access
49
50static inline uint8 read_status(uint32 port)
51{
52	uint8 val;
53	val = sISAModule->read_io_8(port+1);
54	return val;
55}
56
57static inline void write_control(uint32 port, uint8 val)
58{
59	sISAModule->write_io_8(port+2, val);
60}
61
62static inline void write_data(uint32 port, uint8 val)
63{
64	sISAModule->write_io_8(port, val);
65}
66
67#pragma mark // framing
68
69status_t ll_send_sof(laplink_state *st)
70{
71	uint8 v;
72	int tries = LPTSOFTRIES;
73	if (st->rstate != st_sync || st->wstate != st_sync)
74		return B_TIMED_OUT;
75	// check for idle bus
76	if ((read_status(st->port) & 0xf8) != 0x80)
77		return B_TIMED_OUT;
78	// raise ACK
79	write_data(st->port, 0x08);
80	do {
81		spin(LPTSPIN);
82		v = read_status(st->port);
83		if (st->rstate != st_sync)
84			return B_TIMED_OUT;
85		if (tries-- == 0)
86			return B_TIMED_OUT;
87	} while (!(v & 0x08));
88	st->wstate = st_lsb;
89	return B_OK;
90}
91
92status_t ll_check_sof(laplink_state *st)
93{
94	uint8 v;
95	if (st->rstate != st_sync || st->wstate != st_sync)
96		return EINTR;
97	v = read_status(st->port);
98	if ((v & 0xf8) != 0xc0)
99		return EINTR;
100	return B_OK;
101}
102
103status_t ll_ack_sof(laplink_state *st)
104{
105	write_data(st->port, 0x01); // ack the sof
106	st->rstate = st_lsb;
107	return B_OK;
108}
109
110status_t ll_send_eof(laplink_state *st)
111{
112	/*
113	if (st->rstate != st_sync || st->wstate != st_sync)
114		return B_TIMED_OUT;
115	*/
116	st->rstate = st_sync;
117	st->wstate = st_sync;
118	write_data(st->port, 0x00);
119	return B_OK;
120}
121
122#pragma mark // nibbles
123
124status_t ll_send_lnibble(laplink_state *st, uint8 v)
125{
126	int tries = LPTNIBTRIES;
127	uint8 s;
128	if (st->rstate != st_sync)
129		goto err;
130	if (st->wstate != st_lsb)
131		goto err;
132	write_data(st->port, v & 0x0f);
133	spin(10);
134	write_data(st->port, (v & 0x0f) | 0x10);
135	// wait for ack
136	do {
137		s = read_status(st->port);
138		if (tries-- == 0)
139			goto err;
140		spin(LPTSPIN);
141	} while (s & 0x80);
142	st->wstate = st_msb;
143	return B_OK;
144err:
145	st->wstate = st_sync;
146	return B_TIMED_OUT;
147}
148
149status_t ll_send_mnibble(laplink_state *st, uint8 v)
150{
151	int tries = LPTNIBTRIES;
152	uint8 s;
153	if (st->rstate != st_sync)
154		goto err;
155	if (st->wstate != st_msb)
156		goto err;
157	write_data(st->port, (v >> 4) | 0x10);
158	spin(10);
159	write_data(st->port, (v >> 4));
160	// wait for ack
161	do {
162		s = read_status(st->port);
163		if (tries-- == 0)
164			goto err;
165		spin(LPTSPIN);
166	} while (!(s & 0x80));
167	st->wstate = st_lsb;//st_sync;
168	return B_OK;
169err:
170	st->wstate = st_sync;
171	return B_TIMED_OUT;
172}
173
174status_t ll_wait_lnibble(laplink_state *st, uint8 *v)
175{
176	int tries = LPTNIBTRIES;
177	uint8 s;
178	// wait for data
179	do {
180		s = read_status(st->port);
181		if (tries-- == 0)
182			goto err;
183		spin(LPTSPIN);
184	} while ((s & 0x80) || (s != read_status(st->port)));
185	// get the nibble
186	*v = (s >> 3) & 0x0f;
187	st->rstate = st_msb;
188	// tell we got that one
189	write_data(st->port, 0x10);
190	return B_OK;
191err:
192	st->rstate = st_sync;
193	return B_TIMED_OUT;
194}
195
196status_t ll_wait_mnibble(laplink_state *st, uint8 *v)
197{
198	int tries = LPTNIBTRIES;
199	uint8 s;
200	// wait for data
201	do {
202		s = read_status(st->port);
203		if (tries-- == 0)
204			goto err;
205		spin(LPTSPIN);
206	} while (!(s & 0x80) || (s != read_status(st->port)));
207	// get the nibble
208	*v |= (s << (4-3)) & 0xf0;
209	st->rstate = st_sync;
210	// tell we got that one
211	write_data(st->port, 0x00);
212	return B_OK;
213err:
214	st->rstate = st_sync;
215	return B_TIMED_OUT;
216}
217
218#pragma mark // byte mode
219
220status_t ll_send_byte(laplink_state *st, uint8 v)
221{
222	status_t err;
223	err = ll_send_sof(st);
224	if (!err)
225		err = ll_send_lnibble(st, v);
226	if (!err)
227		err = ll_send_mnibble(st, v);
228	if (!err)
229		err = ll_send_eof(st);
230	return err;
231}
232
233status_t ll_check_byte(laplink_state *st, uint8 *v)
234{
235	status_t err;
236	*v = 0;
237	err = ll_check_sof(st);
238	if (err)
239		return err;
240	err = ll_ack_sof(st);
241	if (!err)
242		err = ll_wait_lnibble(st, v);
243	if (!err)
244		err = ll_wait_mnibble(st, v);
245	if (!err)
246		err = ll_send_eof(st);
247	return err;
248}
249
250status_t ll_wait_byte(laplink_state *st, uint8 *v)
251{
252	status_t err;
253	do {
254		spin(LPTSPIN);
255		err = ll_check_byte(st, v);
256	} while (err < B_OK);//	} while (err == B_TIMED_OUT);
257	return err;
258}
259
260#pragma mark // frame mode
261
262// unframed
263static inline status_t ll_send_byte_uf(laplink_state *st, uint8 v)
264{
265	status_t err;
266	err = ll_send_lnibble(st, v);
267	if (!err)
268		err = ll_send_mnibble(st, v);
269	return err;
270}
271
272status_t ll_get_byte_uf(laplink_state *st, uint8 *v)
273{
274	status_t err;
275	*v = 0;
276	err = ll_wait_lnibble(st, v);
277	if (!err)
278		err = ll_wait_mnibble(st, v);
279	return err;
280}
281
282status_t ll_send_frame(laplink_state *st, const uint8 *buff, size_t *len)
283{
284	status_t err;
285	uint16 pktlen = *len;
286	uint8 cksum = 0;
287	*len = 0;
288	err = ll_send_sof(st);
289	if (err)
290		return err;
291	err = ll_send_byte_uf(st, pktlen & 0xff);
292	if (err)
293		goto err;
294	err = ll_send_byte_uf(st, pktlen >> 8);
295	if (err)
296		goto err;
297	for (*len = 0; *len < pktlen; (*len)++) {
298		err = ll_send_byte_uf(st, buff[*len]);
299		if (err)
300			goto err;
301		cksum += buff[*len];
302	}
303	err = ll_send_byte_uf(st, cksum);
304	if (err)
305		goto err;
306
307	/*err =*/ ll_send_eof(st);
308err:
309
310	if (err) { // back to idle
311		*len = 0;
312		ll_send_eof(st);
313	}
314	return err;
315}
316
317status_t ll_check_frame(laplink_state *st, uint8 *buff, size_t *len)
318{
319	status_t err;
320	uint16 pktlen = 0;
321	uint16 wanted;
322	uint8 cksum = 0;
323	uint8 byte;
324	int i;
325	err = ll_check_sof(st);
326	if (err)
327		goto err;
328	err = ll_ack_sof(st);
329	if (err)
330		goto err;
331	// pktlen
332	err = ll_get_byte_uf(st, &byte);
333	if (err)
334		goto err;
335	pktlen = byte;
336	err = ll_get_byte_uf(st, &byte);
337	if (err)
338		goto err;
339	pktlen |= byte << 8;
340	cksum = 0;
341	/*if (pktlen > *len) {
342		dprintf("laplink: check_frame: packet truncated from %d to %d\n", pktlen, *len);
343	}*/
344	wanted = MIN(pktlen, *len);
345	for (*len = 0; (*len < pktlen); (*len)++) {
346		err = ll_get_byte_uf(st, &byte);
347		if (err)
348			goto err;
349		if (*len < wanted)
350			buff[*len] = byte;
351		cksum += byte;
352	}
353	err = ll_get_byte_uf(st, &byte);
354	if (err)
355		goto err;
356	/*
357	if (cksum != byte) {
358		dprintf("laplink: check_frame: wrong cksum\n");
359	}*/
360err:
361	ll_send_eof(st);
362	return err;
363}
364
365status_t ll_wait_frame(laplink_state *st, uint8 *buff, size_t *len)
366{
367	status_t err;
368	do {
369		spin(LPTSPIN);
370		err = ll_check_frame(st, buff, len);
371	} while (err < B_OK);//	} while (err == B_TIMED_OUT);
372	return err;
373}
374
375#pragma mark // kdebug io handler
376
377#if _BUILDING_kernel
378#define BUFFSZ 256
379
380static laplink_state llst;
381static char laplink_in_buf[BUFFSZ];
382static char *laplink_in_ptr;
383static size_t laplink_in_avail;
384static char laplink_out_buf[BUFFSZ];
385
386//XXX: cleanup
387static status_t debug_init_laplink(void *kernel_settings)
388{
389	(void)kernel_settings;
390	llst.port = LPTBASE;
391	llst.rstate = st_sync;
392	llst.wstate = st_sync;
393	laplink_in_ptr = laplink_in_buf;
394	laplink_in_avail = 0;
395	return B_OK;
396}
397
398static int debug_write_laplink(int f, const char *buf, int count)
399{
400	status_t err;
401	size_t len;
402	int tries;
403	int i, prev = 0;
404
405	// fix up CR LF issues... to a local buffer (which will get truncated)
406	if (count > 1) {
407		for (i = 0; (i < BUFFSZ-1) && count; i++, buf++, count--) {
408			if ((*buf == '\n') && (prev != '\r'))
409				laplink_out_buf[i++] = '\r';
410			laplink_out_buf[i] = *buf;
411			prev = *buf;
412		}
413		count = i;
414		buf = laplink_out_buf;
415	}
416
417	tries = 5;
418	do {
419		len = count;
420		err = ll_send_frame(&llst, (const uint8 *)buf, &len);
421	} while (err && tries--);
422	if (err)
423		return 0;
424	return len;
425}
426
427static int debug_read_laplink(void)
428{
429	status_t err = B_OK;
430	while (laplink_in_avail < 1) {
431		laplink_in_avail = BUFFSZ;
432		laplink_in_ptr = laplink_in_buf;
433		err = ll_wait_frame(&llst, (uint8 *)laplink_in_buf, &laplink_in_avail);
434		if (err)
435			laplink_in_avail = 0;
436	}
437	laplink_in_avail--;
438	return *laplink_in_ptr++;
439}
440
441
442static int
443debugger_puts(const char *s, int32 length)
444{
445	return debug_write_laplink(0, s, (int)length);
446}
447
448
449static status_t
450std_ops(int32 op, ...)
451{
452	void *handle;
453	bool load = true;//false;
454
455	switch (op) {
456	case B_MODULE_INIT:
457		handle = load_driver_settings("kernel");
458		if (handle) {
459			load = get_driver_boolean_parameter(handle,
460				"laplinkll_debug_output", load, true);
461			unload_driver_settings(handle);
462		}
463		if (load) {
464			if (get_module(B_ISA_MODULE_NAME, (module_info **)&sISAModule) < B_OK)
465				return B_ERROR;
466			debug_init_laplink(NULL);
467		}
468		return load ? B_OK : B_ERROR;
469	case B_MODULE_UNINIT:
470		put_module(B_ISA_MODULE_NAME);
471		return B_OK;
472	}
473	return B_BAD_VALUE;
474}
475
476
477static struct debugger_module_info sModuleInfo = {
478	{
479		"debugger/laplinkll/v1",
480		0,
481		&std_ops
482	},
483	NULL,
484	NULL,
485	debugger_puts,
486	NULL
487};
488
489module_info *modules[] = {
490	(module_info *)&sModuleInfo,
491	NULL
492};
493
494
495#endif
496
497