1/*  *********************************************************************
2    *  Broadcom Common Firmware Environment (CFE)
3    *
4    *  TCP Console Driver			File: dev_tcpconsole.c
5    *
6    *  Evil hack:  A console driver that uses a TCP socket.
7    *
8    *  Author:  Mitch Lichtenberg (mpl@broadcom.com)
9    *
10    *********************************************************************
11    *
12    *  Copyright 2000,2001,2002,2003
13    *  Broadcom Corporation. All rights reserved.
14    *
15    *  This software is furnished under license and may be used and
16    *  copied only in accordance with the following terms and
17    *  conditions.  Subject to these conditions, you may download,
18    *  copy, install, use, modify and distribute modified or unmodified
19    *  copies of this software in source and/or binary form.  No title
20    *  or ownership is transferred hereby.
21    *
22    *  1) Any source code used, modified or distributed must reproduce
23    *     and retain this copyright notice and list of conditions
24    *     as they appear in the source file.
25    *
26    *  2) No right is granted to use any trade name, trademark, or
27    *     logo of Broadcom Corporation.  The "Broadcom Corporation"
28    *     name may not be used to endorse or promote products derived
29    *     from this software without the prior written permission of
30    *     Broadcom Corporation.
31    *
32    *  3) THIS SOFTWARE IS PROVIDED "AS-IS" AND ANY EXPRESS OR
33    *     IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, ANY IMPLIED
34    *     WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
35    *     PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT
36    *     SHALL BROADCOM BE LIABLE FOR ANY DAMAGES WHATSOEVER, AND IN
37    *     PARTICULAR, BROADCOM SHALL NOT BE LIABLE FOR DIRECT, INDIRECT,
38    *     INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
39    *     (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
40    *     GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
41    *     BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
42    *     OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
43    *     TORT (INCLUDING NEGLIGENCE OR OTHERWISE), EVEN IF ADVISED OF
44    *     THE POSSIBILITY OF SUCH DAMAGE.
45    ********************************************************************* */
46
47
48#include "lib_types.h"
49#include "lib_malloc.h"
50#include "lib_printf.h"
51#include "cfe_iocb.h"
52#include "cfe_device.h"
53#include "cfe_ioctl.h"
54#include "addrspace.h"
55
56#include "cfe_timer.h"
57
58#include "bsp_config.h"
59
60#if CFG_TCP
61#include "net_ebuf.h"
62#include "net_api.h"
63
64/*
65 * Friendly warning:  Don't put printfs in here or enable any
66 * debugging messages in the TCP stack if you're really
67 * going to use this for your console device.  You'll end up
68 * with a recursion loop!
69 */
70
71/*  *********************************************************************
72    *  Constants
73    ********************************************************************* */
74
75#define TCPCONSOLE_DEFAULT_PORT	23		/* telnet */
76
77/*  *********************************************************************
78    *  Forward
79    ********************************************************************* */
80
81static void tcpconsole_probe(cfe_driver_t *drv,
82			       unsigned long probe_a, unsigned long probe_b,
83			       void *probe_ptr);
84
85static int tcpconsole_open(cfe_devctx_t *ctx);
86static int tcpconsole_read(cfe_devctx_t *ctx,iocb_buffer_t *buffer);
87static int tcpconsole_inpstat(cfe_devctx_t *ctx,iocb_inpstat_t *inpstat);
88static int tcpconsole_write(cfe_devctx_t *ctx,iocb_buffer_t *buffer);
89static int tcpconsole_ioctl(cfe_devctx_t *ctx,iocb_buffer_t *buffer);
90static int tcpconsole_close(cfe_devctx_t *ctx);
91
92const static cfe_devdisp_t tcpconsole_dispatch = {
93    tcpconsole_open,
94    tcpconsole_read,
95    tcpconsole_inpstat,
96    tcpconsole_write,
97    tcpconsole_ioctl,
98    tcpconsole_close,
99    NULL,
100    NULL
101};
102
103const cfe_driver_t tcpconsole = {
104    "TCP Console",
105    "tcpconsole",
106    CFE_DEV_SERIAL,
107    &tcpconsole_dispatch,
108    tcpconsole_probe
109};
110
111
112/*  *********************************************************************
113    *  tcpconsole structure
114    ********************************************************************* */
115
116/*
117 * States our connection can be in
118 */
119
120#define TCPCONSTAT_IDLE		0
121#define TCPCONSTAT_LISTEN	1
122#define TCPCONSTAT_CONNECTED	2
123#define TCPCONSTAT_DISCONNECTED	3
124#define TCPCONSTAT_BROKEN	4
125
126/*
127 * state information
128 */
129
130typedef struct tcpconsole_s {
131    int tcp_socket;
132    int tcp_status;
133    int tcp_port;
134} tcpconsole_t;
135
136
137static void tcpconsole_probe(cfe_driver_t *drv,
138			       unsigned long probe_a, unsigned long probe_b,
139			       void *probe_ptr)
140{
141    tcpconsole_t *softc;
142    char descr[80];
143
144    softc = (tcpconsole_t *) KMALLOC(sizeof(tcpconsole_t),0);
145    if (softc) {
146	softc->tcp_socket = -1;
147	softc->tcp_status = TCPCONSTAT_IDLE;
148	xsprintf(descr, "%s", drv->drv_description);
149
150	if (probe_a == 0) probe_a = TCPCONSOLE_DEFAULT_PORT;
151
152	softc->tcp_port = (int)probe_a;
153
154	cfe_attach(drv, softc, NULL, descr);
155	}
156}
157
158
159static int tcpconsole_isready(tcpconsole_t *softc,int *rxbytes)
160{
161    int res;
162    int connstat,rxeof;
163
164    res = tcp_status(softc->tcp_socket,&connstat,rxbytes,&rxeof);
165
166    /*
167     * Return:
168     *   -1 if we could not get status
169     *    0 if we are not connected or are connected and at EOF
170     *    1 if we are connected.
171     */
172
173    if (res < 0) return res;
174    if (connstat != TCPSTATUS_CONNECTED) return 0;
175    if (!rxeof) return 1;
176
177    return 0;
178}
179
180
181static int tcpconsole_process(tcpconsole_t *softc)
182{
183    int res = 0;
184
185    switch (softc->tcp_status) {
186	case TCPCONSTAT_IDLE:
187	    /* Idle, set up listening socket */
188	    res = tcp_socket();
189	    if (res < 0) {
190		softc->tcp_status = TCPCONSTAT_BROKEN;
191		return res;
192		}
193	    softc->tcp_socket = res;
194
195	    res = tcp_listen(softc->tcp_socket,softc->tcp_port);
196
197	    if (res < 0) {
198		tcp_close(softc->tcp_socket);
199		softc->tcp_status = TCPCONSTAT_BROKEN;
200		softc->tcp_socket = -1;
201		return res;
202		}
203	    softc->tcp_status = TCPCONSTAT_LISTEN;
204	    break;
205
206	case TCPCONSTAT_LISTEN:
207	    /* Still waiting for a connection */
208	    res = 0;
209	    if (tcpconsole_isready(softc,NULL) > 0) {
210		softc->tcp_status = TCPCONSTAT_CONNECTED;
211		}
212	    break;
213
214	case TCPCONSTAT_CONNECTED:
215	    res = 0;	/* do nothing, we're okay */
216	    break;
217
218	case TCPCONSTAT_DISCONNECTED:
219	    /* Currently connected, kill off this connection */
220	    tcp_close(softc->tcp_socket);
221	    softc->tcp_socket = -1;
222	    softc->tcp_status = TCPCONSTAT_IDLE;
223	    break;
224
225	case TCPCONSTAT_BROKEN:
226	    /* Broken.  Stay broken. */
227	    res = 0;
228	    break;
229	}
230
231    return res;
232}
233
234
235static int tcpconsole_open(cfe_devctx_t *ctx)
236{
237    return 0;
238}
239
240static int tcpconsole_read(cfe_devctx_t *ctx, iocb_buffer_t *buffer)
241{
242    tcpconsole_t *softc = ctx->dev_softc;
243    unsigned char *bptr;
244    int blen;
245    int res;
246
247    POLL();
248    tcpconsole_process(softc);
249
250    buffer->buf_retlen = 0;
251
252    if (softc->tcp_status == TCPCONSTAT_CONNECTED) {
253	bptr = buffer->buf_ptr;
254	blen = buffer->buf_length;
255
256	if (tcpconsole_isready(softc,NULL) <= 0) {
257	    softc->tcp_status = TCPCONSTAT_DISCONNECTED;
258	    return 0;
259	    }
260
261	res = tcp_recv(softc->tcp_socket,bptr,blen);
262
263	if (res > 0) {
264	    buffer->buf_retlen = res;
265	    }
266	}
267
268    return 0;
269}
270
271static int tcpconsole_inpstat(cfe_devctx_t *ctx, iocb_inpstat_t *inpstat)
272{
273    tcpconsole_t *softc = ctx->dev_softc;
274    unsigned int rxbytes;
275
276    POLL();
277    tcpconsole_process(softc);
278
279    inpstat->inp_status = 0;
280
281    if (softc->tcp_status == TCPCONSTAT_CONNECTED) {
282	if (tcpconsole_isready(softc,&rxbytes) <= 0) {
283	    softc->tcp_status = TCPCONSTAT_DISCONNECTED;
284	    }
285	else {
286	    inpstat->inp_status = (rxbytes > 0);
287	    }
288	}
289
290    return 0;
291}
292
293static int tcpconsole_write(cfe_devctx_t *ctx, iocb_buffer_t *buffer)
294{
295    tcpconsole_t *softc = ctx->dev_softc;
296    unsigned char *bptr;
297    int blen;
298    int res;
299
300    POLL();
301    tcpconsole_process(softc);
302
303    buffer->buf_retlen = 0;
304
305    if (softc->tcp_status == TCPCONSTAT_CONNECTED) {
306	bptr = buffer->buf_ptr;
307	blen = buffer->buf_length;
308
309	if (tcpconsole_isready(softc,NULL) <= 0) {
310	    softc->tcp_status = TCPCONSTAT_DISCONNECTED;
311	    return 0;
312	    }
313
314	res = tcp_send(softc->tcp_socket,bptr,blen);
315
316	if (res > 0) {
317	    buffer->buf_retlen = res;
318	    }
319	}
320
321    return 0;
322}
323
324static int tcpconsole_ioctl(cfe_devctx_t *ctx, iocb_buffer_t *buffer)
325{
326    return -1;
327}
328
329static int tcpconsole_close(cfe_devctx_t *ctx)
330{
331    tcpconsole_t *softc = ctx->dev_softc;
332
333    if (softc->tcp_status == TCPCONSTAT_CONNECTED) {
334	    softc->tcp_status = TCPCONSTAT_DISCONNECTED;
335	}
336
337    return 0;
338}
339
340
341#endif
342