1/*	$NetBSD: ucom.c,v 1.40 2001/11/13 06:24:54 lukem Exp $	*/
2
3/*-
4 * SPDX-License-Identifier: BSD-2-Clause
5 *
6 * Copyright (c) 2001-2003, 2005, 2008
7 *	Shunsuke Akiyama <akiyama@jp.FreeBSD.org>.
8 * All rights reserved.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32#include <sys/cdefs.h>
33/*-
34 * Copyright (c) 1998, 2000 The NetBSD Foundation, Inc.
35 * All rights reserved.
36 *
37 * This code is derived from software contributed to The NetBSD Foundation
38 * by Lennart Augustsson (lennart@augustsson.net) at
39 * Carlstedt Research & Technology.
40 *
41 * Redistribution and use in source and binary forms, with or without
42 * modification, are permitted provided that the following conditions
43 * are met:
44 * 1. Redistributions of source code must retain the above copyright
45 *    notice, this list of conditions and the following disclaimer.
46 * 2. Redistributions in binary form must reproduce the above copyright
47 *    notice, this list of conditions and the following disclaimer in the
48 *    documentation and/or other materials provided with the distribution.
49 *
50 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
51 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
52 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
53 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
54 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
55 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
56 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
57 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
58 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
59 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
60 * POSSIBILITY OF SUCH DAMAGE.
61 */
62
63#include <unistd.h>
64#include <los_queue.h>
65#ifdef LOSCFG_NET_LWIP_SACK
66#include <lwip/netif.h>
67#include <lwip/dhcp.h>
68#include <lwip/netifapi.h>
69#endif
70#include "implementation/global_implementation.h"
71#include "fs/driver.h"
72
73extern int ucom_modem(struct ucom_softc *sc, int sigon, int sigoff);
74
75#undef USB_DEBUG_VAR
76#define	USB_DEBUG_VAR   ucom_debug
77#ifdef LOSCFG_USB_DEBUG
78static int ucom_debug = 0;
79void
80serial_debug_func(int level)
81{
82	ucom_debug = level;
83	PRINTK("The level of usb serial debug is %d\n", level);
84}
85DEBUG_MODULE(serial, serial_debug_func);
86#endif
87
88#define	UCOM_CONS_BUFSIZE 1024
89
90static uint8_t *ucom_cons_rx_buf;
91static uint8_t *ucom_cons_tx_buf;
92
93static unsigned int ucom_cons_rx_low = 0;
94static unsigned int ucom_cons_rx_high = 0;
95
96static unsigned int ucom_cons_tx_low = 0;
97static unsigned int ucom_cons_tx_high = 0;
98
99static struct ucom_softc *ucom_cons_softc = NULL;
100
101static int tx_worked = 0;
102
103static usb_proc_callback_t ucom_cfg_start_transfers;
104static usb_proc_callback_t ucom_cfg_open;
105static usb_proc_callback_t ucom_cfg_close;
106static usb_proc_callback_t ucom_cfg_line_state;
107static usb_proc_callback_t ucom_cfg_status_change;
108
109static int tty_fd = -1;
110
111static void ucom_unit_free(int);
112static int ucom_attach_tty(struct ucom_super_softc *, struct ucom_softc *);
113static void ucom_detach_tty(struct ucom_super_softc *, struct ucom_softc *);
114static void ucom_queue_command(struct ucom_softc *,
115		    usb_proc_callback_t *, struct termios *pt,
116		    struct usb_proc_msg *t0, struct usb_proc_msg *t1);
117static void ucom_shutdown(struct ucom_softc *);
118static void ucom_ring(struct ucom_softc *, uint8_t);
119static void ucom_break(struct ucom_softc *, uint8_t);
120static void ucom_dtr(struct ucom_softc *, uint8_t);
121static void ucom_rts(struct ucom_softc *, uint8_t);
122static void ucom_close(struct ucom_softc *sc);
123
124static UINT32 u3g_tx_init(VOID);
125static UINT32 u3g_tx_deinit(VOID);
126static void ucom_tx_task(void);
127
128static int tty_usb_open(struct file *filep);
129static int tty_usb_close(struct file *filep);
130static ssize_t tty_usb_read(struct file *filep, char *buffer, size_t buflen);
131static ssize_t tty_usb_write(struct file *filep, const char *buffer, size_t buflen);
132
133static const struct file_operations_vfs tty_usb_fops =
134{
135	tty_usb_open,	/* open */
136	tty_usb_close,	/* close */
137	tty_usb_read,	/* read */
138	tty_usb_write,	/* write */
139	NULL,			/* seek */
140	NULL,			/* ioctl */
141	NULL,			/* mmap */
142#ifndef CONFIG_DISABLE_POLL
143	NULL,			/* poll */
144#endif
145	NULL			/* unlink */
146};
147
148#define	UCOM_UNIT_MAX		128	/* maximum number of units */
149#define	UCOM_TTY_PREFIX		"U"
150
151static struct mtx ucom_mtx;
152static int ucom_close_refs;
153
154/*
155 * Mark the unit number as not in use.
156 */
157static void
158ucom_unit_free(int unit)
159{
160	return;
161}
162
163/*
164 * Setup a group of one or more serial ports.
165 *
166 * The mutex pointed to by "mtx" is applied before all
167 * callbacks are called back. Also "mtx" must be applied
168 * before calling into the ucom-layer!
169 */
170int
171ucom_attach(struct ucom_super_softc *ssc, struct ucom_softc *sc,
172    int subunits, void *parent,
173    const struct ucom_callback *callback, struct mtx *umtx)
174{
175	int subunit;
176	int error;
177
178	DPRINTFN(0, "\n");
179
180	if ((sc == NULL) ||
181	    (subunits <= 0) ||
182	    (callback == NULL) ||
183	    (umtx == NULL)) {
184		return (EINVAL);
185	}
186
187	/* allocate a uniq unit number */
188	ssc->sc_unit = 0;
189	if (ssc->sc_unit == -1)
190		return (ENOMEM);
191
192	/* generate TTY name string */
193	(void)snprintf_s(ssc->sc_ttyname, sizeof(ssc->sc_ttyname), sizeof(ssc->sc_ttyname) - 1,
194	    UCOM_TTY_PREFIX "%d", ssc->sc_unit);
195
196	/* create USB request handling process */
197	error = usb_proc_create(&ssc->sc_tq, umtx, "ucom", USB_PRI_MED);
198	if (error) {
199		ucom_unit_free(ssc->sc_unit);
200		return (error);
201	}
202	ssc->sc_subunits = subunits;
203	ssc->sc_flag = UCOM_FLAG_ATTACHED |
204	    UCOM_FLAG_FREE_UNIT;
205
206	if (callback->ucom_free == NULL)
207		ssc->sc_flag |= UCOM_FLAG_WAIT_REFS;
208
209	/* increment reference count */
210	ucom_ref(ssc);
211
212	for (subunit = 0; subunit < ssc->sc_subunits; subunit++) {
213		sc[subunit].sc_subunit = subunit;
214		sc[subunit].sc_super = ssc;
215		sc[subunit].sc_mtx = umtx;
216		sc[subunit].sc_parent = parent;
217		sc[subunit].sc_callback = callback;
218
219		error = ucom_attach_tty(ssc, &sc[subunit]);
220		if (error) {
221			ucom_detach(ssc, &sc[0]);
222			return (error);
223		}
224		/* increment reference count */
225		ucom_ref(ssc);
226
227		/* set subunit attached */
228		sc[subunit].sc_flag = UCOM_FLAG_ATTACHED;
229	}
230
231	(void)u3g_tx_init();
232	ucom_cons_rx_buf = (uint8_t *)memalign(USB_CACHE_ALIGN_SIZE, SKB_DATA_ALIGN(UCOM_CONS_BUFSIZE));
233	ucom_cons_tx_buf = (uint8_t *)memalign(USB_CACHE_ALIGN_SIZE, SKB_DATA_ALIGN(UCOM_CONS_BUFSIZE));
234
235	return (0);
236}
237
238/*
239 * The following function will do nothing if the structure pointed to
240 * by "ssc" and "sc" is zero or has already been detached.
241 */
242void
243ucom_detach(struct ucom_super_softc *ssc, struct ucom_softc *sc)
244{
245	int subunit;
246
247	if (!(ssc->sc_flag & UCOM_FLAG_ATTACHED))
248		return;		/* not initialized */
249	usb_proc_drain(&ssc->sc_tq);
250
251	for (subunit = 0; subunit < ssc->sc_subunits; subunit++) {
252		if (sc[subunit].sc_flag & UCOM_FLAG_ATTACHED) {
253			ucom_detach_tty(ssc, &sc[subunit]);
254
255			/* avoid duplicate detach */
256			sc[subunit].sc_flag &= ~UCOM_FLAG_ATTACHED;
257		}
258	}
259
260	tty_fd = -1;
261
262	(void)u3g_tx_deinit();
263
264	usb_proc_free(&ssc->sc_tq);
265
266	free(ucom_cons_rx_buf);
267	free(ucom_cons_tx_buf);
268	ucom_cons_rx_buf = NULL;
269	ucom_cons_tx_buf = NULL;
270
271	(void)ucom_unref(ssc);
272
273	if (ssc->sc_flag & UCOM_FLAG_WAIT_REFS)
274		ucom_drain(ssc);
275
276	/* make sure we don't detach twice */
277	ssc->sc_flag &= ~UCOM_FLAG_ATTACHED;
278
279	ucom_cons_softc = NULL;
280}
281
282void
283ucom_drain(struct ucom_super_softc *ssc)
284{
285	mtx_lock(&ucom_mtx);
286	while (ssc->sc_refs > 0) {
287		PRINTK("ucom: Waiting for a TTY device to close.\n");
288		usb_pause_mtx(&ucom_mtx, hz);
289	}
290	mtx_unlock(&ucom_mtx);
291}
292
293void
294ucom_drain_all(void *arg)
295{
296	mtx_lock(&ucom_mtx);
297	while (ucom_close_refs > 0) {
298		PRINTK("ucom: Waiting for all detached TTY "
299		    "devices to have open fds closed.\n");
300		usb_pause_mtx(&ucom_mtx, hz);
301	}
302	mtx_unlock(&ucom_mtx);
303}
304
305static int
306ucom_attach_tty(struct ucom_super_softc *ssc, struct ucom_softc *sc)
307{
308	int ret;
309
310	ret = snprintf_s(sc->sc_name, TTY_NAME_LEN, TTY_NAME_LEN - 1, "/dev/ttyUSB%d", sc->sc_subunit);
311	if (ret < 0) {
312		usb_err("snprintf_s failed\n");
313		return (-1);
314	}
315	(void)register_driver(sc->sc_name, &tty_usb_fops, 0666, sc);
316	DPRINTF("unit %d subunit %d is console",
317	    ssc->sc_unit, sc->sc_subunit);
318
319	if (ucom_cons_softc == NULL) {
320		ucom_cons_softc = sc;
321
322		UCOM_MTX_LOCK(ucom_cons_softc);
323		ucom_cons_rx_low = 0;
324		ucom_cons_rx_high = 0;
325		ucom_cons_tx_low = 0;
326		ucom_cons_tx_high = 0;
327		UCOM_MTX_UNLOCK(ucom_cons_softc);
328	}
329
330	return (0);
331}
332
333static void
334ucom_detach_tty(struct ucom_super_softc *ssc, struct ucom_softc *sc)
335{
336	/* the config thread has been stopped when we get here */
337
338	UCOM_MTX_LOCK(sc);
339	sc->sc_flag |= UCOM_FLAG_GONE;
340	sc->sc_flag &= ~(UCOM_FLAG_HL_READY | UCOM_FLAG_LL_READY);
341	ucom_close(sc);	/* close, if any */
342	UCOM_MTX_UNLOCK(sc);
343
344	(void)unregister_driver(sc->sc_name);
345
346	UCOM_MTX_LOCK(sc);
347	/*
348	 * make sure that read and write transfers are stopped
349	 */
350	if (sc->sc_callback->ucom_stop_read)
351		(sc->sc_callback->ucom_stop_read) (sc);
352	if (sc->sc_callback->ucom_stop_write)
353		(sc->sc_callback->ucom_stop_write) (sc);
354	UCOM_MTX_UNLOCK(sc);
355}
356
357void
358ucom_set_pnpinfo_usb(struct ucom_super_softc *ssc, device_t dev)
359{
360	char buf[64];
361	uint8_t iface_index;
362	struct usb_attach_arg *uaa;
363
364	(void)snprintf_s(buf, sizeof(buf), sizeof(buf) - 1, "ttyname=" UCOM_TTY_PREFIX
365	    "%d ttyports=%d", ssc->sc_unit, ssc->sc_subunits);
366
367	/* Store the PNP info in the first interface for the device */
368	uaa = (struct usb_attach_arg *)device_get_ivars(dev);
369	iface_index = uaa->info.bIfaceIndex;
370
371	if (usbd_set_pnpinfo(uaa->device, iface_index, buf) != 0)
372		device_printf(dev, "Could not set PNP info\n");
373
374	/*
375	 * The following information is also replicated in the PNP-info
376	 * string which is registered above:
377	 */
378
379}
380
381static void
382ucom_queue_command(struct ucom_softc *sc,
383    usb_proc_callback_t *fn, struct termios *pt,
384    struct usb_proc_msg *t0, struct usb_proc_msg *t1)
385{
386	struct ucom_super_softc *ssc = sc->sc_super;
387	struct ucom_param_task *task;
388
389	UCOM_MTX_ASSERT(sc, MA_OWNED);
390
391	if (usb_proc_is_gone(&ssc->sc_tq)) {
392		DPRINTF("proc is gone\n");
393		return;		/* nothing to do */
394	}
395	/*
396	 * NOTE: The task cannot get executed before we drop the
397	 * "sc_mtx" mutex. It is safe to update fields in the message
398	 * structure after that the message got queued.
399	 */
400	task = (struct ucom_param_task *)
401	    usb_proc_msignal(&ssc->sc_tq, t0, t1);
402
403	/* Setup callback and softc pointers */
404	task->hdr.pm_callback = fn;
405	task->sc = sc;
406
407	/*
408	 * Make a copy of the termios. This field is only present if
409	 * the "pt" field is not NULL.
410	 */
411	if (pt != NULL)
412		task->termios_copy = *pt;
413
414	/*
415	 * Closing the device should be synchronous.
416	 */
417	if (fn == ucom_cfg_close)
418		usb_proc_mwait(&ssc->sc_tq, t0, t1);
419
420	/*
421	 * In case of multiple configure requests,
422	 * keep track of the last one!
423	 */
424	if (fn == ucom_cfg_start_transfers)
425		sc->sc_last_start_xfer = &task->hdr;
426}
427
428static void
429ucom_shutdown(struct ucom_softc *sc)
430{
431}
432
433/*
434 * Return values:
435 *	0: normal
436 * else: taskqueue is draining or gone
437 */
438uint8_t
439ucom_cfg_is_gone(struct ucom_softc *sc)
440{
441	struct ucom_super_softc *ssc = sc->sc_super;
442
443	return (usb_proc_is_gone(&ssc->sc_tq));
444}
445
446static void
447ucom_cfg_start_transfers(struct usb_proc_msg *_task)
448{
449	struct ucom_cfg_task *task =
450	    (struct ucom_cfg_task *)_task;
451	struct ucom_softc *sc = task->sc;
452
453	if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) {
454		return;
455	}
456	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
457		/* TTY device closed */
458		return;
459	}
460
461	if (_task == sc->sc_last_start_xfer)
462		sc->sc_flag |= UCOM_FLAG_GP_DATA;
463
464	if (sc->sc_callback->ucom_start_read) {
465		(sc->sc_callback->ucom_start_read) (sc);
466	}
467	if (sc->sc_callback->ucom_start_write) {
468		(sc->sc_callback->ucom_start_write) (sc);
469	}
470}
471
472void
473ucom_start_transfers(struct ucom_softc *sc)
474{
475	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
476		return;
477	}
478	/*
479	 * Make sure that data transfers are started in both
480	 * directions:
481	 */
482	if (sc->sc_callback->ucom_start_read) {
483		(sc->sc_callback->ucom_start_read) (sc);
484	}
485	if (sc->sc_callback->ucom_start_write) {
486		(sc->sc_callback->ucom_start_write) (sc);
487	}
488}
489
490static void
491ucom_cfg_open(struct usb_proc_msg *_task)
492{
493	struct ucom_cfg_task *task =
494	    (struct ucom_cfg_task *)_task;
495	struct ucom_softc *sc = task->sc;
496
497	DPRINTF("\n");
498
499	if (sc->sc_flag & UCOM_FLAG_LL_READY) {
500		/* already opened */
501
502	} else {
503		sc->sc_flag |= UCOM_FLAG_LL_READY;
504
505		if (sc->sc_callback->ucom_cfg_open) {
506			(sc->sc_callback->ucom_cfg_open) (sc);
507
508			/* wait a little */
509			usb_pause_mtx(sc->sc_mtx, hz / 10);
510		}
511	}
512}
513
514int
515ucom_open(struct ucom_softc *sc)
516{
517	int error;
518
519	UCOM_MTX_ASSERT(sc, MA_OWNED);
520
521	if (sc->sc_flag & UCOM_FLAG_GONE) {
522		return (ENXIO);
523	}
524	if (sc->sc_flag & UCOM_FLAG_HL_READY) {
525		/* already opened */
526		return (0);
527	}
528	DPRINTF("sc = %p\n", sc);
529
530	if (sc->sc_callback->ucom_pre_open) {
531		/*
532		 * give the lower layer a chance to disallow TTY open, for
533		 * example if the device is not present:
534		 */
535		error = (sc->sc_callback->ucom_pre_open) (sc);
536		if (error) {
537			PRINT_ERR("error %d\n",error);
538			return (error);
539		}
540	}
541	sc->sc_flag |= UCOM_FLAG_HL_READY;
542
543	/* Disable transfers */
544	sc->sc_flag &= ~UCOM_FLAG_GP_DATA;
545
546	sc->sc_lsr = 0;
547	sc->sc_msr = 0;
548	sc->sc_mcr = 0;
549
550	/* reset programmed line state */
551	sc->sc_pls_curr = 0;
552	sc->sc_pls_set = 0;
553	sc->sc_pls_clr = 0;
554
555	/* reset jitter buffer */
556	sc->sc_jitterbuf_in = 0;
557	sc->sc_jitterbuf_out = 0;
558
559	ucom_queue_command(sc, ucom_cfg_open, NULL,
560	    &sc->sc_open_task[0].hdr,
561	    &sc->sc_open_task[1].hdr);
562
563	/* Queue transfer enable command last */
564	ucom_queue_command(sc, ucom_cfg_start_transfers, NULL,
565	    &sc->sc_start_task[0].hdr,
566	    &sc->sc_start_task[1].hdr);
567
568	(void)ucom_modem(sc, SER_DTR | SER_RTS, 0);
569
570	ucom_ring(sc, 0);
571
572	ucom_break(sc, 0);
573
574	return (0);
575}
576
577static void
578ucom_cfg_close(struct usb_proc_msg *_task)
579{
580	struct ucom_cfg_task *task =
581	    (struct ucom_cfg_task *)_task;
582	struct ucom_softc *sc = task->sc;
583
584	DPRINTF("\n");
585
586	if (sc->sc_flag & UCOM_FLAG_LL_READY) {
587		sc->sc_flag &= ~UCOM_FLAG_LL_READY;
588		if (sc->sc_callback->ucom_cfg_close)
589			(sc->sc_callback->ucom_cfg_close) (sc);
590	} else {
591		/* already closed */
592	}
593}
594
595static void
596ucom_close(struct ucom_softc *sc)
597{
598	UCOM_MTX_ASSERT(sc, MA_OWNED);
599
600	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
601		DPRINTF("tp=%p already closed\n", sc);
602		return;
603	}
604	ucom_shutdown(sc);
605
606	ucom_queue_command(sc, ucom_cfg_close, NULL,
607	    &sc->sc_close_task[0].hdr,
608	    &sc->sc_close_task[1].hdr);
609
610	sc->sc_flag &= ~(UCOM_FLAG_HL_READY | UCOM_FLAG_RTS_IFLOW);
611
612	if (sc->sc_callback->ucom_stop_read) {
613		(sc->sc_callback->ucom_stop_read) (sc);
614	}
615
616	ucom_cons_rx_low = 0;
617	ucom_cons_rx_high = 0;
618
619	ucom_cons_tx_low = 0;
620	ucom_cons_tx_high = 0;
621
622}
623
624int
625ucom_modem(struct ucom_softc *sc, int sigon, int sigoff)
626{
627	uint8_t onoff;
628
629	UCOM_MTX_ASSERT(sc, MA_OWNED);
630
631	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
632		return (0);
633	}
634	if ((sigon == 0) && (sigoff == 0)) {
635		if (sc->sc_mcr & SER_DTR) {
636			sigon |= SER_DTR;
637		}
638		if (sc->sc_mcr & SER_RTS) {
639			sigon |= SER_RTS;
640		}
641		if (sc->sc_msr & SER_CTS) {
642			sigon |= SER_CTS;
643		}
644		if (sc->sc_msr & SER_DCD) {
645			sigon |= SER_DCD;
646		}
647		if (sc->sc_msr & SER_DSR) {
648			sigon |= SER_DSR;
649		}
650		if (sc->sc_msr & SER_RI) {
651			sigon |= SER_RI;
652		}
653		return (sigon);
654	}
655	if (sigon & SER_DTR) {
656		sc->sc_mcr |= SER_DTR;
657	}
658	if (sigoff & SER_DTR) {
659		sc->sc_mcr &= ~SER_DTR;
660	}
661	if (sigon & SER_RTS) {
662		sc->sc_mcr |= SER_RTS;
663	}
664	if (sigoff & SER_RTS) {
665		sc->sc_mcr &= ~SER_RTS;
666	}
667	onoff = (sc->sc_mcr & SER_DTR) ? 1 : 0;
668	ucom_dtr(sc, onoff);
669
670	onoff = (sc->sc_mcr & SER_RTS) ? 1 : 0;
671	ucom_rts(sc, onoff);
672
673	return (0);
674}
675
676static void
677ucom_cfg_line_state(struct usb_proc_msg *_task)
678{
679	struct ucom_cfg_task *task =
680	    (struct ucom_cfg_task *)_task;
681	struct ucom_softc *sc = task->sc;
682	uint8_t notch_bits;
683	uint8_t any_bits;
684	uint8_t prev_value;
685	uint8_t last_value;
686	uint8_t mask;
687
688	if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) {
689		return;
690	}
691
692	mask = 0;
693	/* compute callback mask */
694	if (sc->sc_callback->ucom_cfg_set_dtr)
695		mask |= UCOM_LS_DTR;
696	if (sc->sc_callback->ucom_cfg_set_rts)
697		mask |= UCOM_LS_RTS;
698	if (sc->sc_callback->ucom_cfg_set_break)
699		mask |= UCOM_LS_BREAK;
700	if (sc->sc_callback->ucom_cfg_set_ring)
701		mask |= UCOM_LS_RING;
702
703	/* compute the bits we are to program */
704	notch_bits = (sc->sc_pls_set & sc->sc_pls_clr) & mask;
705	any_bits = (sc->sc_pls_set | sc->sc_pls_clr) & mask;
706	prev_value = sc->sc_pls_curr ^ notch_bits;
707	last_value = sc->sc_pls_curr;
708
709	/* reset programmed line state */
710	sc->sc_pls_curr = 0;
711	sc->sc_pls_set = 0;
712	sc->sc_pls_clr = 0;
713
714	/* ensure that we don't lose any levels */
715	if (notch_bits & UCOM_LS_DTR)
716		sc->sc_callback->ucom_cfg_set_dtr(sc,
717		    (prev_value & UCOM_LS_DTR) ? 1 : 0);
718	if (notch_bits & UCOM_LS_RTS)
719		sc->sc_callback->ucom_cfg_set_rts(sc,
720		    (prev_value & UCOM_LS_RTS) ? 1 : 0);
721	if (notch_bits & UCOM_LS_BREAK)
722		sc->sc_callback->ucom_cfg_set_break(sc,
723		    (prev_value & UCOM_LS_BREAK) ? 1 : 0);
724	if (notch_bits & UCOM_LS_RING)
725		sc->sc_callback->ucom_cfg_set_ring(sc,
726		    (prev_value & UCOM_LS_RING) ? 1 : 0);
727
728	/* set last value */
729	if (any_bits & UCOM_LS_DTR)
730		sc->sc_callback->ucom_cfg_set_dtr(sc,
731		    (last_value & UCOM_LS_DTR) ? 1 : 0);
732	if (any_bits & UCOM_LS_RTS)
733		sc->sc_callback->ucom_cfg_set_rts(sc,
734		    (last_value & UCOM_LS_RTS) ? 1 : 0);
735	if (any_bits & UCOM_LS_BREAK)
736		sc->sc_callback->ucom_cfg_set_break(sc,
737		    (last_value & UCOM_LS_BREAK) ? 1 : 0);
738	if (any_bits & UCOM_LS_RING)
739		sc->sc_callback->ucom_cfg_set_ring(sc,
740		    (last_value & UCOM_LS_RING) ? 1 : 0);
741}
742
743static void
744ucom_line_state(struct ucom_softc *sc,
745    uint8_t set_bits, uint8_t clear_bits)
746{
747	UCOM_MTX_ASSERT(sc, MA_OWNED);
748
749	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
750		return;
751	}
752
753	DPRINTF("on=0x%02x, off=0x%02x\n", set_bits, clear_bits);
754
755	/* update current programmed line state */
756	sc->sc_pls_curr |= set_bits;
757	sc->sc_pls_curr &= ~clear_bits;
758	sc->sc_pls_set |= set_bits;
759	sc->sc_pls_clr |= clear_bits;
760
761	/* defer driver programming */
762	ucom_queue_command(sc, ucom_cfg_line_state, NULL,
763	    &sc->sc_line_state_task[0].hdr,
764	    &sc->sc_line_state_task[1].hdr);
765}
766
767static void
768ucom_ring(struct ucom_softc *sc, uint8_t onoff)
769{
770	DPRINTF("onoff = %d\n", onoff);
771
772	if (onoff)
773		ucom_line_state(sc, UCOM_LS_RING, 0);
774	else
775		ucom_line_state(sc, 0, UCOM_LS_RING);
776}
777
778static void
779ucom_break(struct ucom_softc *sc, uint8_t onoff)
780{
781	DPRINTF("onoff = %d\n", onoff);
782
783	if (onoff)
784		ucom_line_state(sc, UCOM_LS_BREAK, 0);
785	else
786		ucom_line_state(sc, 0, UCOM_LS_BREAK);
787}
788
789static void
790ucom_dtr(struct ucom_softc *sc, uint8_t onoff)
791{
792	DPRINTF("onoff = %d\n", onoff);
793
794	if (onoff)
795		ucom_line_state(sc, UCOM_LS_DTR, 0);
796	else
797		ucom_line_state(sc, 0, UCOM_LS_DTR);
798}
799
800static void
801ucom_rts(struct ucom_softc *sc, uint8_t onoff)
802{
803	DPRINTF("onoff = %d\n", onoff);
804
805	if (onoff)
806		ucom_line_state(sc, UCOM_LS_RTS, 0);
807	else
808		ucom_line_state(sc, 0, UCOM_LS_RTS);
809}
810
811static void
812ucom_cfg_status_change(struct usb_proc_msg *_task)
813{
814	struct ucom_cfg_task *task =
815	    (struct ucom_cfg_task *)_task;
816	struct ucom_softc *sc = task->sc;
817	uint8_t new_msr;
818	uint8_t new_lsr;
819	uint8_t onoff;
820	uint8_t lsr_delta;
821
822	UCOM_MTX_ASSERT(sc, MA_OWNED);
823
824	if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) {
825		return;
826	}
827	if (sc->sc_callback->ucom_cfg_get_status == NULL) {
828		return;
829	}
830	/* get status */
831
832	new_msr = 0;
833	new_lsr = 0;
834
835	(sc->sc_callback->ucom_cfg_get_status) (sc, &new_lsr, &new_msr);
836
837	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
838		/* TTY device closed */
839		return;
840	}
841	onoff = ((sc->sc_msr ^ new_msr) & SER_DCD);
842	lsr_delta = (sc->sc_lsr ^ new_lsr);
843
844	sc->sc_msr = new_msr;
845	sc->sc_lsr = new_lsr;
846
847	if (onoff) {
848		onoff = (sc->sc_msr & SER_DCD) ? 1 : 0;
849		DPRINTF("DCD changed to %d\n", onoff);
850	}
851
852	if ((lsr_delta & ULSR_BI) && (sc->sc_lsr & ULSR_BI)) {
853		DPRINTF("BREAK detected\n");
854	}
855
856	if ((lsr_delta & ULSR_FE) && (sc->sc_lsr & ULSR_FE)) {
857		DPRINTF("Frame error detected\n");
858	}
859
860	if ((lsr_delta & ULSR_PE) && (sc->sc_lsr & ULSR_PE)) {
861		DPRINTF("Parity error detected\n");
862	}
863}
864
865void
866ucom_status_change(struct ucom_softc *sc)
867{
868	UCOM_MTX_ASSERT(sc, MA_OWNED);
869
870	if (sc->sc_flag & UCOM_FLAG_CONSOLE)
871		return;		/* not supported */
872
873	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
874		return;
875	}
876	DPRINTF("\n");
877
878	ucom_queue_command(sc, ucom_cfg_status_change, NULL,
879	    &sc->sc_status_task[0].hdr,
880	    &sc->sc_status_task[1].hdr);
881}
882
883static void
884ucom_outwakeup(struct ucom_softc *sc)
885{
886	UCOM_MTX_ASSERT(sc, MA_OWNED);
887
888	DPRINTF("sc = %p\n", sc);
889
890	ucom_start_transfers(sc);
891}
892
893/*------------------------------------------------------------------------*
894 *	ucom_get_data
895 *
896 * Return values:
897 * 0: No data is available.
898 * Else: Data is available.
899 *------------------------------------------------------------------------*/
900uint8_t
901ucom_get_data(struct ucom_softc *sc, struct usb_page_cache *pc,
902    uint32_t offset, uint32_t len, uint32_t *actlen)
903{
904	unsigned int temp;
905	UCOM_MTX_ASSERT(sc, MA_OWNED);
906
907	/* get total TX length */
908	temp = ucom_cons_tx_high - ucom_cons_tx_low;
909	temp %= UCOM_CONS_BUFSIZE;
910
911	if (temp > len)
912		temp = len;
913
914	/* copy in data */
915	if (temp > (UCOM_CONS_BUFSIZE - ucom_cons_tx_low)){
916		unsigned int fisrt_len = (UCOM_CONS_BUFSIZE - ucom_cons_tx_low);
917		usbd_copy_in(pc, offset, ucom_cons_tx_buf + ucom_cons_tx_low, fisrt_len);
918		usbd_copy_in(pc, offset+fisrt_len, ucom_cons_tx_buf, temp-fisrt_len);
919		PRINTK("len1 : %d ; len2 : %d \n", fisrt_len, temp);
920	}else
921		usbd_copy_in(pc, offset, ucom_cons_tx_buf + ucom_cons_tx_low, temp);
922
923	/* update counters */
924	ucom_cons_tx_low += temp;
925	ucom_cons_tx_low %= UCOM_CONS_BUFSIZE;
926
927	/* store actual length */
928
929	*actlen = temp;
930
931	return (temp ? 1 : 0);
932
933}
934
935void
936ucom_put_data(struct ucom_softc *sc, struct usb_page_cache *pc,
937    uint32_t offset, uint32_t len)
938{
939	unsigned int temp;
940	UCOM_MTX_ASSERT(sc, MA_OWNED);
941
942	/* get maximum RX length */
943	temp = (UCOM_CONS_BUFSIZE - 1) - ucom_cons_rx_high + ucom_cons_rx_low;
944	temp %= UCOM_CONS_BUFSIZE;
945
946	if (temp > len)
947		temp = len;
948
949	/* limit RX length */
950	if (temp > (UCOM_CONS_BUFSIZE - ucom_cons_rx_high)){
951		unsigned int first_len = (UCOM_CONS_BUFSIZE - ucom_cons_rx_high);
952		usbd_copy_out(pc, offset, ucom_cons_rx_buf + ucom_cons_rx_high, first_len);
953		usbd_copy_out(pc, offset+first_len, ucom_cons_rx_buf, temp-first_len);
954	/* copy out data */
955	}else
956		usbd_copy_out(pc, offset, ucom_cons_rx_buf + ucom_cons_rx_high, temp);
957
958	/* update counters */
959	ucom_cons_rx_high += temp;
960	ucom_cons_rx_high %= UCOM_CONS_BUFSIZE;
961
962	return;
963}
964
965/*------------------------------------------------------------------------*
966 *	ucom_ref
967 *
968 * This function will increment the super UCOM reference count.
969 *------------------------------------------------------------------------*/
970void
971ucom_ref(struct ucom_super_softc *ssc)
972{
973	mtx_lock(&ucom_mtx);
974	ssc->sc_refs++;
975	mtx_unlock(&ucom_mtx);
976}
977
978/*------------------------------------------------------------------------*
979 *	ucom_free_unit
980 *
981 * This function will free the super UCOM's allocated unit
982 * number. This function can be called on a zero-initialized
983 * structure. This function can be called multiple times.
984 *------------------------------------------------------------------------*/
985static void
986ucom_free_unit(struct ucom_super_softc *ssc)
987{
988	if (!(ssc->sc_flag & UCOM_FLAG_FREE_UNIT))
989		return;
990
991	ucom_unit_free(ssc->sc_unit);
992
993	ssc->sc_flag &= ~UCOM_FLAG_FREE_UNIT;
994}
995
996/*------------------------------------------------------------------------*
997 *	ucom_unref
998 *
999 * This function will decrement the super UCOM reference count.
1000 *
1001 * Return values:
1002 * 0: UCOM structures are still referenced.
1003 * Else: UCOM structures are no longer referenced.
1004 *------------------------------------------------------------------------*/
1005int
1006ucom_unref(struct ucom_super_softc *ssc)
1007{
1008	int retval;
1009
1010	mtx_lock(&ucom_mtx);
1011	retval = (ssc->sc_refs < 2);
1012	ssc->sc_refs--;
1013	mtx_unlock(&ucom_mtx);
1014
1015	if (retval)
1016		ucom_free_unit(ssc);
1017
1018	return (retval);
1019}
1020
1021static void
1022tx_data_copy_in(struct ucom_softc *sc, const void *tx_data, unsigned int len)
1023{
1024	unsigned int temp;
1025	int ret;
1026
1027	UCOM_MTX_LOCK(sc);
1028
1029	if(len > UCOM_CONS_BUFSIZE)
1030		len = UCOM_CONS_BUFSIZE;
1031
1032	/* get maximum TX length */
1033	temp = (UCOM_CONS_BUFSIZE - 1) - ucom_cons_tx_high + ucom_cons_tx_low;
1034	temp %= UCOM_CONS_BUFSIZE;
1035
1036	if (temp >= len)
1037		temp = len;
1038
1039	/* limit RX length */
1040	if (temp > (UCOM_CONS_BUFSIZE - ucom_cons_tx_high)){
1041		temp = (UCOM_CONS_BUFSIZE - ucom_cons_tx_high);
1042		/* copy out data */
1043		ret = usbd_copy_from_user(ucom_cons_tx_buf + ucom_cons_tx_high, temp, tx_data, temp);
1044		if (ret != EOK) {
1045			UCOM_MTX_UNLOCK(sc);
1046			usb_err("memcpy_s failed!, ret:%d\n", ret);
1047			return;
1048		}
1049		ret = usbd_copy_from_user(ucom_cons_tx_buf, UCOM_CONS_BUFSIZE,
1050			    (const void *)((char *)tx_data + temp), len - temp);
1051		if (ret != EOK) {
1052			UCOM_MTX_UNLOCK(sc);
1053			usb_err("memcpy_s failed!, ret:%d\n", ret);
1054			return;
1055		}
1056	} else {
1057		ret = usbd_copy_from_user(ucom_cons_tx_buf + ucom_cons_tx_high,
1058			    UCOM_CONS_BUFSIZE - ucom_cons_tx_high, tx_data, len);
1059		if (ret != EOK) {
1060			UCOM_MTX_UNLOCK(sc);
1061			usb_err("memcpy_s failed!, ret:%d\n", ret);
1062			return;
1063		}
1064	}
1065
1066	/* update counters */
1067	ucom_cons_tx_high += len;
1068	ucom_cons_tx_high %= UCOM_CONS_BUFSIZE;
1069
1070	UCOM_MTX_UNLOCK(sc);
1071	return;
1072}
1073
1074static UINT8 m_auc_ucom_handler_pool[sizeof(OS_MEMBOX_S) +
1075			    ((sizeof(ucom_handler_item_s) + 3) & (~3)) * 8];
1076static UINT32 m_uw_ucom_handler_queue;
1077static UINT32 g_u3g_tx_taskid = 0;
1078static UINT32 g_u3g_rx_taskid = 0;
1079
1080static void
1081ucom_rx_task(void)
1082{
1083	char buffer[128] = {0};
1084	int length;
1085	int i;
1086
1087	while (1) {
1088		if (tty_fd > 0) {
1089			length = read(tty_fd, buffer, 128);
1090			if (length > 0) {
1091				for (i = 0; i < length; i++) {
1092					printf("%c", *(buffer + i));
1093				}
1094				printf("\n");
1095			}
1096		}
1097		LOS_Msleep(50);
1098	}
1099}
1100
1101static UINT32
1102u3g_tx_init(VOID)
1103{
1104	TSK_INIT_PARAM_S stappTask;
1105	UINT32 ret;
1106
1107	(void)memset_s(&stappTask, sizeof(TSK_INIT_PARAM_S), 0, sizeof(TSK_INIT_PARAM_S));
1108	stappTask.pfnTaskEntry = (TSK_ENTRY_FUNC)ucom_tx_task;
1109	stappTask.uwStackSize  = LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE;
1110	stappTask.pcName =		 "u3g_tx_Task";
1111	stappTask.usTaskPrio =	 9;
1112	stappTask.uwResved   =	 LOS_TASK_STATUS_DETACHED;
1113	ret = LOS_TaskCreate(&g_u3g_tx_taskid, &stappTask);
1114	if (ret != LOS_OK) {
1115		PRINT_ERR("Create ucom_tx_task error!\n");
1116		return (ret);
1117	}
1118
1119	(void)memset_s(&stappTask, sizeof(TSK_INIT_PARAM_S), 0, sizeof(TSK_INIT_PARAM_S));
1120	stappTask.pfnTaskEntry = (TSK_ENTRY_FUNC)ucom_rx_task;
1121	stappTask.uwStackSize  = LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE;
1122	stappTask.pcName =		 "u3g_rx_Task";
1123	stappTask.usTaskPrio =	 9;
1124	stappTask.uwResved   =	 LOS_TASK_STATUS_DETACHED;
1125	ret = LOS_TaskCreate(&g_u3g_rx_taskid, &stappTask);
1126	if (ret != LOS_OK) {
1127		PRINT_ERR("Create ucom_rx_task error!\n");
1128		return (ret);
1129	}
1130
1131	return (0);
1132}
1133
1134static UINT32
1135u3g_tx_deinit(VOID)
1136{
1137	UINT32 ret;
1138
1139	ret = LOS_TaskDelete(g_u3g_tx_taskid);
1140	if (ret != LOS_OK) {
1141		return (ret);
1142	}
1143
1144	ret = LOS_TaskDelete(g_u3g_rx_taskid);
1145	if (ret != LOS_OK) {
1146		return (ret);
1147	}
1148
1149	ret = LOS_QueueDelete(m_uw_ucom_handler_queue);
1150	if (ret != LOS_OK) {
1151		return (ret);
1152	}
1153
1154	return (LOS_OK);
1155}
1156
1157static void
1158ucom_tx_task(void)
1159{
1160	ucom_handler_item_s *p_ucom_tx_handler;
1161	UINT32 ret;
1162	int err;
1163
1164	ret = LOS_MemboxInit(m_auc_ucom_handler_pool, sizeof(m_auc_ucom_handler_pool),
1165						    sizeof(ucom_handler_item_s));
1166	if (ret != LOS_OK) {
1167		PRINTK("%s error!\n", __FUNCTION__);
1168		return;
1169	}
1170
1171	ret = LOS_QueueCreate(NULL, 8, &m_uw_ucom_handler_queue, 0, sizeof(ucom_handler_item_s));
1172	if (ret != LOS_OK) {
1173		PRINTK("%s error!\n", __FUNCTION__);
1174		return;
1175	}
1176
1177	tx_worked = 1;
1178	for ( ; ; ) {
1179		ret = LOS_QueueRead(m_uw_ucom_handler_queue, &p_ucom_tx_handler,
1180							    sizeof(ucom_handler_item_s), LOS_WAIT_FOREVER);
1181		if (ret == LOS_OK) {
1182			if (p_ucom_tx_handler != NULL) {
1183				if (tty_fd < 0) {
1184					PRINTK("Please open ttyUBS0 first!\n");
1185				} else {
1186					err = write(tty_fd, p_ucom_tx_handler->p_ucom_tx_data,
1187							    p_ucom_tx_handler->length);
1188					if (err < 0) {
1189					}
1190				}
1191				if (p_ucom_tx_handler->p_ucom_tx_data != NULL) {
1192					free(p_ucom_tx_handler->p_ucom_tx_data);
1193					p_ucom_tx_handler->p_ucom_tx_data = NULL;
1194				}
1195				if (p_ucom_tx_handler != NULL) {
1196					(void)LOS_MemboxFree(m_auc_ucom_handler_pool,
1197									    p_ucom_tx_handler);
1198					p_ucom_tx_handler = NULL;
1199				}
1200			}
1201		}
1202	}
1203}
1204
1205extern struct netif *pnetif_usb0;
1206#ifdef LOSCFG_NET_LWIP_SACK
1207uint32_t
1208u3g_write_from_shell(int argc, const char **argv)
1209{
1210	ucom_handler_item_s *p_ucom_tx_handler;
1211	char *buf;
1212	int ret;
1213	size_t len;
1214
1215	if (tx_worked == 0) {
1216		PRINTK("u3g : no usb 3g/4g modem worked!\n");
1217		return (LOS_NOK);
1218	}
1219
1220	if (argv == NULL) {
1221		PRINTK("u3g : please enter AT command!\n");
1222		return (LOS_NOK);
1223	}
1224
1225	if (argc > 1) {
1226		PRINTK("u3g : only one argc supported!\n");
1227		return (LOS_NOK);
1228	}
1229
1230	if (((argv[0][0]=='A') && (argv[0][1]=='T')) || ((argv[0][0]=='a') && (argv[0][1]=='t'))) {
1231		p_ucom_tx_handler = (ucom_handler_item_s *)LOS_MemboxAlloc(m_auc_ucom_handler_pool);
1232		if (p_ucom_tx_handler != NULL) {
1233			len = strlen(argv[0]);
1234			if (len == 0) {
1235				(void)LOS_MemboxFree(m_auc_ucom_handler_pool, p_ucom_tx_handler);
1236				return (LOS_NOK);
1237			}
1238
1239			buf = (char *)malloc(len + 2);
1240			if (buf == NULL) {
1241				(void)LOS_MemboxFree(m_auc_ucom_handler_pool, p_ucom_tx_handler);
1242				return (LOS_NOK);
1243			}
1244
1245			ret = memcpy_s(buf, (len + 2), argv[0], len);
1246			if (ret != EOK) {
1247				free(buf);
1248				(void)LOS_MemboxFree(m_auc_ucom_handler_pool, p_ucom_tx_handler);
1249				return (LOS_NOK);
1250			}
1251			buf[len] = 0xd;
1252			buf[len+1] = 0xa;
1253
1254			p_ucom_tx_handler->length = len + 2;
1255			p_ucom_tx_handler->p_ucom_tx_data = (void *)buf;
1256			if (LOS_QueueWrite(m_uw_ucom_handler_queue, p_ucom_tx_handler,
1257				    sizeof(UINT32), LOS_NO_WAIT)) {
1258				(void)LOS_MemboxFree(m_auc_ucom_handler_pool, p_ucom_tx_handler);
1259			}
1260		}
1261	} else if (strcmp("dhcp", argv[0]) == 0) {
1262		(void)netifapi_dhcp_start(pnetif_usb0);
1263		while (netifapi_dhcp_is_bound(pnetif_usb0) == 0) {
1264			(void)sleep(1);
1265		}
1266		dprintf("\n----- DHCP success -----\n");
1267	} else if (strcmp("open", argv[0]) == 0) {
1268		tty_fd = open("/dev/ttyUSB0", O_RDWR);
1269		if (tty_fd < 0) {
1270			PRINTK("open /dev/ttyUSB0 error! errno %d\n", tty_fd);
1271			return (LOS_NOK);
1272		}
1273	} else if (strcmp("close", argv[0]) == 0) {
1274		if (tty_fd > 0) {
1275			(void)close(tty_fd);
1276			tty_fd = -1;
1277		} else {
1278			PRINTK("please first open /dev/ttyUSB0\n");
1279			return (LOS_NOK);
1280		}
1281	} else {
1282		PRINTK("u3g : please enter AT command!\n");
1283		return (LOS_NOK);
1284	}
1285
1286	return (LOS_OK);
1287}
1288#endif
1289
1290UINT8 m_auc_tty_usb_handler_pool[sizeof(OS_MEMBOX_S) +
1291	    ((sizeof(ucom_handler_item_s) + 3) & (~3)) * 8];
1292UINT32 m_uw_tty_usb_handler_queue;
1293pthread_mutex_t tty_usb_mutex;
1294
1295static int
1296tty_usb_queue_init(void)
1297{
1298	UINT32 ret;
1299
1300	(void)pthread_mutex_init(&tty_usb_mutex, NULL);
1301
1302	ret = LOS_MemboxInit(m_auc_tty_usb_handler_pool,
1303						    sizeof(m_auc_tty_usb_handler_pool), sizeof(ucom_handler_item_s));
1304	if (ret != LOS_OK) {
1305		dprintf("%s error!\n", __FUNCTION__);
1306		return (EINVAL);
1307	}
1308
1309	ret = LOS_QueueCreate(NULL, 8, &m_uw_tty_usb_handler_queue,
1310			    0, sizeof(ucom_handler_item_s));
1311	if (ret != LOS_OK) {
1312		dprintf("%s error!\n", __FUNCTION__);
1313		return (EINVAL);
1314	}
1315
1316	return (ENOERR);
1317}
1318
1319static void
1320tty_usb_queue_delete(void)
1321{
1322	(void)LOS_QueueDelete(m_uw_tty_usb_handler_queue);
1323}
1324
1325static int
1326tty_usb_write_wait(void)
1327{
1328	ucom_handler_item_s *ptty_usb_write_handler;
1329	UINT32 ret;
1330	unsigned int write_len = 0;
1331
1332	(void)pthread_mutex_lock(&tty_usb_mutex);
1333
1334	ret = LOS_QueueRead(m_uw_tty_usb_handler_queue, &ptty_usb_write_handler,
1335						    sizeof(ucom_handler_item_s), LOS_WAIT_FOREVER);
1336	if (ret == LOS_OK) {
1337		if (ptty_usb_write_handler != NULL) {
1338			write_len = ptty_usb_write_handler->length;
1339			(VOID)LOS_MemboxFree(m_auc_tty_usb_handler_pool, ptty_usb_write_handler);
1340		}
1341	}
1342	(void)pthread_mutex_unlock(&tty_usb_mutex);
1343
1344	return ((int)write_len);
1345}
1346
1347static int
1348tty_usb_open(struct file *filep)
1349{
1350	struct drv_data *drvData = (struct drv_data *)filep->f_vnode->data;
1351	struct ucom_softc *sc = (struct ucom_softc *)drvData->priv;
1352	int ret;
1353
1354	UCOM_MTX_LOCK(sc);
1355	if(tty_usb_queue_init() != ENOERR) {
1356		UCOM_MTX_UNLOCK(sc);
1357		return (-EINVAL);
1358	}
1359
1360	ret = -ucom_open(sc);
1361	UCOM_MTX_UNLOCK(sc);
1362
1363	return (ret);
1364}
1365
1366static int
1367tty_usb_close(struct file *filep)
1368{
1369	struct drv_data *drvData = (struct drv_data *)filep->f_vnode->data;
1370	struct ucom_softc *sc = (struct ucom_softc *)drvData->priv;
1371
1372	UCOM_MTX_LOCK(sc);
1373	tty_usb_queue_delete();
1374	ucom_close(sc);
1375	UCOM_MTX_UNLOCK(sc);
1376
1377	return (0);
1378}
1379
1380static ssize_t
1381tty_usb_read(struct file *filep, char *buffer, size_t buflen)
1382{
1383	struct drv_data *drvData = (struct drv_data *)filep->f_vnode->data;
1384	struct ucom_softc *sc = (struct ucom_softc *)drvData->priv;
1385	int read_len = -1;
1386	int ret;
1387	UCOM_MTX_LOCK(sc);
1388
1389	if (ucom_cons_rx_low != ucom_cons_rx_high) {
1390		unsigned int temp;
1391
1392		/* get total TX length */
1393		temp = ucom_cons_rx_high - ucom_cons_rx_low;
1394		temp %= UCOM_CONS_BUFSIZE;
1395
1396		if (temp > buflen)
1397			temp = buflen;
1398
1399		/* copy in data */
1400		if (temp > (UCOM_CONS_BUFSIZE - ucom_cons_rx_low)) {
1401			unsigned int fisrt_len = (UCOM_CONS_BUFSIZE - ucom_cons_rx_low);
1402			ret = usbd_copy_to_user(buffer, buflen, ucom_cons_rx_buf + ucom_cons_rx_low, fisrt_len);
1403			if (ret != EOK) {
1404				UCOM_MTX_UNLOCK(sc);
1405				usb_err("memcpy_s failed!, ret:%d\n", ret);
1406				return (0);
1407			}
1408			ret = usbd_copy_to_user(buffer + fisrt_len, (buflen - fisrt_len),
1409									ucom_cons_rx_buf, temp - fisrt_len);
1410			if (ret != EOK) {
1411				UCOM_MTX_UNLOCK(sc);
1412				usb_err("memcpy_s failed!, ret:%d\n", ret);
1413				return (0);
1414			}
1415			PRINTK("len1 : %d ; len2 : %d \n", fisrt_len, temp);
1416		}else {
1417			ret = usbd_copy_to_user(buffer, buflen, ucom_cons_rx_buf + ucom_cons_rx_low, temp);
1418			if (ret != EOK) {
1419				UCOM_MTX_UNLOCK(sc);
1420				usb_err("memcpy_s failed!, ret:%d\n", ret);
1421				return (0);
1422			}
1423		}
1424
1425		/* update counters */
1426		ucom_cons_rx_low += temp;
1427		ucom_cons_rx_low %= UCOM_CONS_BUFSIZE;
1428
1429		/* store actual length */
1430		read_len = temp;
1431	}
1432	/* start USB transfers */
1433	ucom_outwakeup(sc);
1434
1435	UCOM_MTX_UNLOCK(sc);
1436	return (read_len);
1437}
1438
1439static ssize_t
1440tty_usb_write(struct file *filep, const char *buffer, size_t buflen)
1441{
1442	struct drv_data *drvData = (struct drv_data *)filep->f_vnode->data;
1443	struct ucom_softc *sc = (struct ucom_softc *)drvData->priv;
1444
1445	tx_data_copy_in(sc, (void *)buffer, buflen);
1446	ucom_outwakeup(sc);
1447
1448	return (tty_usb_write_wait());
1449}
1450
1451#undef USB_DEBUG_VAR
1452