xref: /OK3568_Linux_fs/kernel/drivers/usb/host/xhci-dbgtty.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * xhci-dbgtty.c - tty glue for xHCI debug capability
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  * Copyright (C) 2017 Intel Corporation
6*4882a593Smuzhiyun  *
7*4882a593Smuzhiyun  * Author: Lu Baolu <baolu.lu@linux.intel.com>
8*4882a593Smuzhiyun  */
9*4882a593Smuzhiyun 
10*4882a593Smuzhiyun #include <linux/slab.h>
11*4882a593Smuzhiyun #include <linux/tty.h>
12*4882a593Smuzhiyun #include <linux/tty_flip.h>
13*4882a593Smuzhiyun 
14*4882a593Smuzhiyun #include "xhci.h"
15*4882a593Smuzhiyun #include "xhci-dbgcap.h"
16*4882a593Smuzhiyun 
17*4882a593Smuzhiyun static int dbc_tty_init(void);
18*4882a593Smuzhiyun static void dbc_tty_exit(void);
19*4882a593Smuzhiyun 
20*4882a593Smuzhiyun static struct tty_driver *dbc_tty_driver;
21*4882a593Smuzhiyun 
dbc_to_port(struct xhci_dbc * dbc)22*4882a593Smuzhiyun static inline struct dbc_port *dbc_to_port(struct xhci_dbc *dbc)
23*4882a593Smuzhiyun {
24*4882a593Smuzhiyun 	return dbc->priv;
25*4882a593Smuzhiyun }
26*4882a593Smuzhiyun 
27*4882a593Smuzhiyun static unsigned int
dbc_send_packet(struct dbc_port * port,char * packet,unsigned int size)28*4882a593Smuzhiyun dbc_send_packet(struct dbc_port *port, char *packet, unsigned int size)
29*4882a593Smuzhiyun {
30*4882a593Smuzhiyun 	unsigned int		len;
31*4882a593Smuzhiyun 
32*4882a593Smuzhiyun 	len = kfifo_len(&port->write_fifo);
33*4882a593Smuzhiyun 	if (len < size)
34*4882a593Smuzhiyun 		size = len;
35*4882a593Smuzhiyun 	if (size != 0)
36*4882a593Smuzhiyun 		size = kfifo_out(&port->write_fifo, packet, size);
37*4882a593Smuzhiyun 	return size;
38*4882a593Smuzhiyun }
39*4882a593Smuzhiyun 
dbc_start_tx(struct dbc_port * port)40*4882a593Smuzhiyun static int dbc_start_tx(struct dbc_port *port)
41*4882a593Smuzhiyun 	__releases(&port->port_lock)
42*4882a593Smuzhiyun 	__acquires(&port->port_lock)
43*4882a593Smuzhiyun {
44*4882a593Smuzhiyun 	int			len;
45*4882a593Smuzhiyun 	struct dbc_request	*req;
46*4882a593Smuzhiyun 	int			status = 0;
47*4882a593Smuzhiyun 	bool			do_tty_wake = false;
48*4882a593Smuzhiyun 	struct list_head	*pool = &port->write_pool;
49*4882a593Smuzhiyun 
50*4882a593Smuzhiyun 	while (!list_empty(pool)) {
51*4882a593Smuzhiyun 		req = list_entry(pool->next, struct dbc_request, list_pool);
52*4882a593Smuzhiyun 		len = dbc_send_packet(port, req->buf, DBC_MAX_PACKET);
53*4882a593Smuzhiyun 		if (len == 0)
54*4882a593Smuzhiyun 			break;
55*4882a593Smuzhiyun 		do_tty_wake = true;
56*4882a593Smuzhiyun 
57*4882a593Smuzhiyun 		req->length = len;
58*4882a593Smuzhiyun 		list_del(&req->list_pool);
59*4882a593Smuzhiyun 
60*4882a593Smuzhiyun 		spin_unlock(&port->port_lock);
61*4882a593Smuzhiyun 		status = dbc_ep_queue(req);
62*4882a593Smuzhiyun 		spin_lock(&port->port_lock);
63*4882a593Smuzhiyun 
64*4882a593Smuzhiyun 		if (status) {
65*4882a593Smuzhiyun 			list_add(&req->list_pool, pool);
66*4882a593Smuzhiyun 			break;
67*4882a593Smuzhiyun 		}
68*4882a593Smuzhiyun 	}
69*4882a593Smuzhiyun 
70*4882a593Smuzhiyun 	if (do_tty_wake && port->port.tty)
71*4882a593Smuzhiyun 		tty_wakeup(port->port.tty);
72*4882a593Smuzhiyun 
73*4882a593Smuzhiyun 	return status;
74*4882a593Smuzhiyun }
75*4882a593Smuzhiyun 
dbc_start_rx(struct dbc_port * port)76*4882a593Smuzhiyun static void dbc_start_rx(struct dbc_port *port)
77*4882a593Smuzhiyun 	__releases(&port->port_lock)
78*4882a593Smuzhiyun 	__acquires(&port->port_lock)
79*4882a593Smuzhiyun {
80*4882a593Smuzhiyun 	struct dbc_request	*req;
81*4882a593Smuzhiyun 	int			status;
82*4882a593Smuzhiyun 	struct list_head	*pool = &port->read_pool;
83*4882a593Smuzhiyun 
84*4882a593Smuzhiyun 	while (!list_empty(pool)) {
85*4882a593Smuzhiyun 		if (!port->port.tty)
86*4882a593Smuzhiyun 			break;
87*4882a593Smuzhiyun 
88*4882a593Smuzhiyun 		req = list_entry(pool->next, struct dbc_request, list_pool);
89*4882a593Smuzhiyun 		list_del(&req->list_pool);
90*4882a593Smuzhiyun 		req->length = DBC_MAX_PACKET;
91*4882a593Smuzhiyun 
92*4882a593Smuzhiyun 		spin_unlock(&port->port_lock);
93*4882a593Smuzhiyun 		status = dbc_ep_queue(req);
94*4882a593Smuzhiyun 		spin_lock(&port->port_lock);
95*4882a593Smuzhiyun 
96*4882a593Smuzhiyun 		if (status) {
97*4882a593Smuzhiyun 			list_add(&req->list_pool, pool);
98*4882a593Smuzhiyun 			break;
99*4882a593Smuzhiyun 		}
100*4882a593Smuzhiyun 	}
101*4882a593Smuzhiyun }
102*4882a593Smuzhiyun 
103*4882a593Smuzhiyun static void
dbc_read_complete(struct xhci_dbc * dbc,struct dbc_request * req)104*4882a593Smuzhiyun dbc_read_complete(struct xhci_dbc *dbc, struct dbc_request *req)
105*4882a593Smuzhiyun {
106*4882a593Smuzhiyun 	unsigned long		flags;
107*4882a593Smuzhiyun 	struct dbc_port		*port = dbc_to_port(dbc);
108*4882a593Smuzhiyun 
109*4882a593Smuzhiyun 	spin_lock_irqsave(&port->port_lock, flags);
110*4882a593Smuzhiyun 	list_add_tail(&req->list_pool, &port->read_queue);
111*4882a593Smuzhiyun 	tasklet_schedule(&port->push);
112*4882a593Smuzhiyun 	spin_unlock_irqrestore(&port->port_lock, flags);
113*4882a593Smuzhiyun }
114*4882a593Smuzhiyun 
dbc_write_complete(struct xhci_dbc * dbc,struct dbc_request * req)115*4882a593Smuzhiyun static void dbc_write_complete(struct xhci_dbc *dbc, struct dbc_request *req)
116*4882a593Smuzhiyun {
117*4882a593Smuzhiyun 	unsigned long		flags;
118*4882a593Smuzhiyun 	struct dbc_port		*port = dbc_to_port(dbc);
119*4882a593Smuzhiyun 
120*4882a593Smuzhiyun 	spin_lock_irqsave(&port->port_lock, flags);
121*4882a593Smuzhiyun 	list_add(&req->list_pool, &port->write_pool);
122*4882a593Smuzhiyun 	switch (req->status) {
123*4882a593Smuzhiyun 	case 0:
124*4882a593Smuzhiyun 		dbc_start_tx(port);
125*4882a593Smuzhiyun 		break;
126*4882a593Smuzhiyun 	case -ESHUTDOWN:
127*4882a593Smuzhiyun 		break;
128*4882a593Smuzhiyun 	default:
129*4882a593Smuzhiyun 		dev_warn(dbc->dev, "unexpected write complete status %d\n",
130*4882a593Smuzhiyun 			  req->status);
131*4882a593Smuzhiyun 		break;
132*4882a593Smuzhiyun 	}
133*4882a593Smuzhiyun 	spin_unlock_irqrestore(&port->port_lock, flags);
134*4882a593Smuzhiyun }
135*4882a593Smuzhiyun 
xhci_dbc_free_req(struct dbc_request * req)136*4882a593Smuzhiyun static void xhci_dbc_free_req(struct dbc_request *req)
137*4882a593Smuzhiyun {
138*4882a593Smuzhiyun 	kfree(req->buf);
139*4882a593Smuzhiyun 	dbc_free_request(req);
140*4882a593Smuzhiyun }
141*4882a593Smuzhiyun 
142*4882a593Smuzhiyun static int
xhci_dbc_alloc_requests(struct xhci_dbc * dbc,unsigned int direction,struct list_head * head,void (* fn)(struct xhci_dbc *,struct dbc_request *))143*4882a593Smuzhiyun xhci_dbc_alloc_requests(struct xhci_dbc *dbc, unsigned int direction,
144*4882a593Smuzhiyun 			struct list_head *head,
145*4882a593Smuzhiyun 			void (*fn)(struct xhci_dbc *, struct dbc_request *))
146*4882a593Smuzhiyun {
147*4882a593Smuzhiyun 	int			i;
148*4882a593Smuzhiyun 	struct dbc_request	*req;
149*4882a593Smuzhiyun 
150*4882a593Smuzhiyun 	for (i = 0; i < DBC_QUEUE_SIZE; i++) {
151*4882a593Smuzhiyun 		req = dbc_alloc_request(dbc, direction, GFP_KERNEL);
152*4882a593Smuzhiyun 		if (!req)
153*4882a593Smuzhiyun 			break;
154*4882a593Smuzhiyun 
155*4882a593Smuzhiyun 		req->length = DBC_MAX_PACKET;
156*4882a593Smuzhiyun 		req->buf = kmalloc(req->length, GFP_KERNEL);
157*4882a593Smuzhiyun 		if (!req->buf) {
158*4882a593Smuzhiyun 			dbc_free_request(req);
159*4882a593Smuzhiyun 			break;
160*4882a593Smuzhiyun 		}
161*4882a593Smuzhiyun 
162*4882a593Smuzhiyun 		req->complete = fn;
163*4882a593Smuzhiyun 		list_add_tail(&req->list_pool, head);
164*4882a593Smuzhiyun 	}
165*4882a593Smuzhiyun 
166*4882a593Smuzhiyun 	return list_empty(head) ? -ENOMEM : 0;
167*4882a593Smuzhiyun }
168*4882a593Smuzhiyun 
169*4882a593Smuzhiyun static void
xhci_dbc_free_requests(struct list_head * head)170*4882a593Smuzhiyun xhci_dbc_free_requests(struct list_head *head)
171*4882a593Smuzhiyun {
172*4882a593Smuzhiyun 	struct dbc_request	*req;
173*4882a593Smuzhiyun 
174*4882a593Smuzhiyun 	while (!list_empty(head)) {
175*4882a593Smuzhiyun 		req = list_entry(head->next, struct dbc_request, list_pool);
176*4882a593Smuzhiyun 		list_del(&req->list_pool);
177*4882a593Smuzhiyun 		xhci_dbc_free_req(req);
178*4882a593Smuzhiyun 	}
179*4882a593Smuzhiyun }
180*4882a593Smuzhiyun 
dbc_tty_install(struct tty_driver * driver,struct tty_struct * tty)181*4882a593Smuzhiyun static int dbc_tty_install(struct tty_driver *driver, struct tty_struct *tty)
182*4882a593Smuzhiyun {
183*4882a593Smuzhiyun 	struct dbc_port		*port = driver->driver_state;
184*4882a593Smuzhiyun 
185*4882a593Smuzhiyun 	tty->driver_data = port;
186*4882a593Smuzhiyun 
187*4882a593Smuzhiyun 	return tty_port_install(&port->port, driver, tty);
188*4882a593Smuzhiyun }
189*4882a593Smuzhiyun 
dbc_tty_open(struct tty_struct * tty,struct file * file)190*4882a593Smuzhiyun static int dbc_tty_open(struct tty_struct *tty, struct file *file)
191*4882a593Smuzhiyun {
192*4882a593Smuzhiyun 	struct dbc_port		*port = tty->driver_data;
193*4882a593Smuzhiyun 
194*4882a593Smuzhiyun 	return tty_port_open(&port->port, tty, file);
195*4882a593Smuzhiyun }
196*4882a593Smuzhiyun 
dbc_tty_close(struct tty_struct * tty,struct file * file)197*4882a593Smuzhiyun static void dbc_tty_close(struct tty_struct *tty, struct file *file)
198*4882a593Smuzhiyun {
199*4882a593Smuzhiyun 	struct dbc_port		*port = tty->driver_data;
200*4882a593Smuzhiyun 
201*4882a593Smuzhiyun 	tty_port_close(&port->port, tty, file);
202*4882a593Smuzhiyun }
203*4882a593Smuzhiyun 
dbc_tty_write(struct tty_struct * tty,const unsigned char * buf,int count)204*4882a593Smuzhiyun static int dbc_tty_write(struct tty_struct *tty,
205*4882a593Smuzhiyun 			 const unsigned char *buf,
206*4882a593Smuzhiyun 			 int count)
207*4882a593Smuzhiyun {
208*4882a593Smuzhiyun 	struct dbc_port		*port = tty->driver_data;
209*4882a593Smuzhiyun 	unsigned long		flags;
210*4882a593Smuzhiyun 
211*4882a593Smuzhiyun 	spin_lock_irqsave(&port->port_lock, flags);
212*4882a593Smuzhiyun 	if (count)
213*4882a593Smuzhiyun 		count = kfifo_in(&port->write_fifo, buf, count);
214*4882a593Smuzhiyun 	dbc_start_tx(port);
215*4882a593Smuzhiyun 	spin_unlock_irqrestore(&port->port_lock, flags);
216*4882a593Smuzhiyun 
217*4882a593Smuzhiyun 	return count;
218*4882a593Smuzhiyun }
219*4882a593Smuzhiyun 
dbc_tty_put_char(struct tty_struct * tty,unsigned char ch)220*4882a593Smuzhiyun static int dbc_tty_put_char(struct tty_struct *tty, unsigned char ch)
221*4882a593Smuzhiyun {
222*4882a593Smuzhiyun 	struct dbc_port		*port = tty->driver_data;
223*4882a593Smuzhiyun 	unsigned long		flags;
224*4882a593Smuzhiyun 	int			status;
225*4882a593Smuzhiyun 
226*4882a593Smuzhiyun 	spin_lock_irqsave(&port->port_lock, flags);
227*4882a593Smuzhiyun 	status = kfifo_put(&port->write_fifo, ch);
228*4882a593Smuzhiyun 	spin_unlock_irqrestore(&port->port_lock, flags);
229*4882a593Smuzhiyun 
230*4882a593Smuzhiyun 	return status;
231*4882a593Smuzhiyun }
232*4882a593Smuzhiyun 
dbc_tty_flush_chars(struct tty_struct * tty)233*4882a593Smuzhiyun static void dbc_tty_flush_chars(struct tty_struct *tty)
234*4882a593Smuzhiyun {
235*4882a593Smuzhiyun 	struct dbc_port		*port = tty->driver_data;
236*4882a593Smuzhiyun 	unsigned long		flags;
237*4882a593Smuzhiyun 
238*4882a593Smuzhiyun 	spin_lock_irqsave(&port->port_lock, flags);
239*4882a593Smuzhiyun 	dbc_start_tx(port);
240*4882a593Smuzhiyun 	spin_unlock_irqrestore(&port->port_lock, flags);
241*4882a593Smuzhiyun }
242*4882a593Smuzhiyun 
dbc_tty_write_room(struct tty_struct * tty)243*4882a593Smuzhiyun static int dbc_tty_write_room(struct tty_struct *tty)
244*4882a593Smuzhiyun {
245*4882a593Smuzhiyun 	struct dbc_port		*port = tty->driver_data;
246*4882a593Smuzhiyun 	unsigned long		flags;
247*4882a593Smuzhiyun 	int			room = 0;
248*4882a593Smuzhiyun 
249*4882a593Smuzhiyun 	spin_lock_irqsave(&port->port_lock, flags);
250*4882a593Smuzhiyun 	room = kfifo_avail(&port->write_fifo);
251*4882a593Smuzhiyun 	spin_unlock_irqrestore(&port->port_lock, flags);
252*4882a593Smuzhiyun 
253*4882a593Smuzhiyun 	return room;
254*4882a593Smuzhiyun }
255*4882a593Smuzhiyun 
dbc_tty_chars_in_buffer(struct tty_struct * tty)256*4882a593Smuzhiyun static int dbc_tty_chars_in_buffer(struct tty_struct *tty)
257*4882a593Smuzhiyun {
258*4882a593Smuzhiyun 	struct dbc_port		*port = tty->driver_data;
259*4882a593Smuzhiyun 	unsigned long		flags;
260*4882a593Smuzhiyun 	int			chars = 0;
261*4882a593Smuzhiyun 
262*4882a593Smuzhiyun 	spin_lock_irqsave(&port->port_lock, flags);
263*4882a593Smuzhiyun 	chars = kfifo_len(&port->write_fifo);
264*4882a593Smuzhiyun 	spin_unlock_irqrestore(&port->port_lock, flags);
265*4882a593Smuzhiyun 
266*4882a593Smuzhiyun 	return chars;
267*4882a593Smuzhiyun }
268*4882a593Smuzhiyun 
dbc_tty_unthrottle(struct tty_struct * tty)269*4882a593Smuzhiyun static void dbc_tty_unthrottle(struct tty_struct *tty)
270*4882a593Smuzhiyun {
271*4882a593Smuzhiyun 	struct dbc_port		*port = tty->driver_data;
272*4882a593Smuzhiyun 	unsigned long		flags;
273*4882a593Smuzhiyun 
274*4882a593Smuzhiyun 	spin_lock_irqsave(&port->port_lock, flags);
275*4882a593Smuzhiyun 	tasklet_schedule(&port->push);
276*4882a593Smuzhiyun 	spin_unlock_irqrestore(&port->port_lock, flags);
277*4882a593Smuzhiyun }
278*4882a593Smuzhiyun 
279*4882a593Smuzhiyun static const struct tty_operations dbc_tty_ops = {
280*4882a593Smuzhiyun 	.install		= dbc_tty_install,
281*4882a593Smuzhiyun 	.open			= dbc_tty_open,
282*4882a593Smuzhiyun 	.close			= dbc_tty_close,
283*4882a593Smuzhiyun 	.write			= dbc_tty_write,
284*4882a593Smuzhiyun 	.put_char		= dbc_tty_put_char,
285*4882a593Smuzhiyun 	.flush_chars		= dbc_tty_flush_chars,
286*4882a593Smuzhiyun 	.write_room		= dbc_tty_write_room,
287*4882a593Smuzhiyun 	.chars_in_buffer	= dbc_tty_chars_in_buffer,
288*4882a593Smuzhiyun 	.unthrottle		= dbc_tty_unthrottle,
289*4882a593Smuzhiyun };
290*4882a593Smuzhiyun 
dbc_rx_push(struct tasklet_struct * t)291*4882a593Smuzhiyun static void dbc_rx_push(struct tasklet_struct *t)
292*4882a593Smuzhiyun {
293*4882a593Smuzhiyun 	struct dbc_request	*req;
294*4882a593Smuzhiyun 	struct tty_struct	*tty;
295*4882a593Smuzhiyun 	unsigned long		flags;
296*4882a593Smuzhiyun 	bool			do_push = false;
297*4882a593Smuzhiyun 	bool			disconnect = false;
298*4882a593Smuzhiyun 	struct dbc_port		*port = from_tasklet(port, t, push);
299*4882a593Smuzhiyun 	struct list_head	*queue = &port->read_queue;
300*4882a593Smuzhiyun 
301*4882a593Smuzhiyun 	spin_lock_irqsave(&port->port_lock, flags);
302*4882a593Smuzhiyun 	tty = port->port.tty;
303*4882a593Smuzhiyun 	while (!list_empty(queue)) {
304*4882a593Smuzhiyun 		req = list_first_entry(queue, struct dbc_request, list_pool);
305*4882a593Smuzhiyun 
306*4882a593Smuzhiyun 		if (tty && tty_throttled(tty))
307*4882a593Smuzhiyun 			break;
308*4882a593Smuzhiyun 
309*4882a593Smuzhiyun 		switch (req->status) {
310*4882a593Smuzhiyun 		case 0:
311*4882a593Smuzhiyun 			break;
312*4882a593Smuzhiyun 		case -ESHUTDOWN:
313*4882a593Smuzhiyun 			disconnect = true;
314*4882a593Smuzhiyun 			break;
315*4882a593Smuzhiyun 		default:
316*4882a593Smuzhiyun 			pr_warn("ttyDBC0: unexpected RX status %d\n",
317*4882a593Smuzhiyun 				req->status);
318*4882a593Smuzhiyun 			break;
319*4882a593Smuzhiyun 		}
320*4882a593Smuzhiyun 
321*4882a593Smuzhiyun 		if (req->actual) {
322*4882a593Smuzhiyun 			char		*packet = req->buf;
323*4882a593Smuzhiyun 			unsigned int	n, size = req->actual;
324*4882a593Smuzhiyun 			int		count;
325*4882a593Smuzhiyun 
326*4882a593Smuzhiyun 			n = port->n_read;
327*4882a593Smuzhiyun 			if (n) {
328*4882a593Smuzhiyun 				packet += n;
329*4882a593Smuzhiyun 				size -= n;
330*4882a593Smuzhiyun 			}
331*4882a593Smuzhiyun 
332*4882a593Smuzhiyun 			count = tty_insert_flip_string(&port->port, packet,
333*4882a593Smuzhiyun 						       size);
334*4882a593Smuzhiyun 			if (count)
335*4882a593Smuzhiyun 				do_push = true;
336*4882a593Smuzhiyun 			if (count != size) {
337*4882a593Smuzhiyun 				port->n_read += count;
338*4882a593Smuzhiyun 				break;
339*4882a593Smuzhiyun 			}
340*4882a593Smuzhiyun 			port->n_read = 0;
341*4882a593Smuzhiyun 		}
342*4882a593Smuzhiyun 
343*4882a593Smuzhiyun 		list_move(&req->list_pool, &port->read_pool);
344*4882a593Smuzhiyun 	}
345*4882a593Smuzhiyun 
346*4882a593Smuzhiyun 	if (do_push)
347*4882a593Smuzhiyun 		tty_flip_buffer_push(&port->port);
348*4882a593Smuzhiyun 
349*4882a593Smuzhiyun 	if (!list_empty(queue) && tty) {
350*4882a593Smuzhiyun 		if (!tty_throttled(tty)) {
351*4882a593Smuzhiyun 			if (do_push)
352*4882a593Smuzhiyun 				tasklet_schedule(&port->push);
353*4882a593Smuzhiyun 			else
354*4882a593Smuzhiyun 				pr_warn("ttyDBC0: RX not scheduled?\n");
355*4882a593Smuzhiyun 		}
356*4882a593Smuzhiyun 	}
357*4882a593Smuzhiyun 
358*4882a593Smuzhiyun 	if (!disconnect)
359*4882a593Smuzhiyun 		dbc_start_rx(port);
360*4882a593Smuzhiyun 
361*4882a593Smuzhiyun 	spin_unlock_irqrestore(&port->port_lock, flags);
362*4882a593Smuzhiyun }
363*4882a593Smuzhiyun 
dbc_port_activate(struct tty_port * _port,struct tty_struct * tty)364*4882a593Smuzhiyun static int dbc_port_activate(struct tty_port *_port, struct tty_struct *tty)
365*4882a593Smuzhiyun {
366*4882a593Smuzhiyun 	unsigned long	flags;
367*4882a593Smuzhiyun 	struct dbc_port	*port = container_of(_port, struct dbc_port, port);
368*4882a593Smuzhiyun 
369*4882a593Smuzhiyun 	spin_lock_irqsave(&port->port_lock, flags);
370*4882a593Smuzhiyun 	dbc_start_rx(port);
371*4882a593Smuzhiyun 	spin_unlock_irqrestore(&port->port_lock, flags);
372*4882a593Smuzhiyun 
373*4882a593Smuzhiyun 	return 0;
374*4882a593Smuzhiyun }
375*4882a593Smuzhiyun 
376*4882a593Smuzhiyun static const struct tty_port_operations dbc_port_ops = {
377*4882a593Smuzhiyun 	.activate =	dbc_port_activate,
378*4882a593Smuzhiyun };
379*4882a593Smuzhiyun 
380*4882a593Smuzhiyun static void
xhci_dbc_tty_init_port(struct xhci_dbc * dbc,struct dbc_port * port)381*4882a593Smuzhiyun xhci_dbc_tty_init_port(struct xhci_dbc *dbc, struct dbc_port *port)
382*4882a593Smuzhiyun {
383*4882a593Smuzhiyun 	tty_port_init(&port->port);
384*4882a593Smuzhiyun 	spin_lock_init(&port->port_lock);
385*4882a593Smuzhiyun 	tasklet_setup(&port->push, dbc_rx_push);
386*4882a593Smuzhiyun 	INIT_LIST_HEAD(&port->read_pool);
387*4882a593Smuzhiyun 	INIT_LIST_HEAD(&port->read_queue);
388*4882a593Smuzhiyun 	INIT_LIST_HEAD(&port->write_pool);
389*4882a593Smuzhiyun 
390*4882a593Smuzhiyun 	port->port.ops =	&dbc_port_ops;
391*4882a593Smuzhiyun 	port->n_read =		0;
392*4882a593Smuzhiyun }
393*4882a593Smuzhiyun 
394*4882a593Smuzhiyun static void
xhci_dbc_tty_exit_port(struct dbc_port * port)395*4882a593Smuzhiyun xhci_dbc_tty_exit_port(struct dbc_port *port)
396*4882a593Smuzhiyun {
397*4882a593Smuzhiyun 	tasklet_kill(&port->push);
398*4882a593Smuzhiyun 	tty_port_destroy(&port->port);
399*4882a593Smuzhiyun }
400*4882a593Smuzhiyun 
xhci_dbc_tty_register_device(struct xhci_dbc * dbc)401*4882a593Smuzhiyun static int xhci_dbc_tty_register_device(struct xhci_dbc *dbc)
402*4882a593Smuzhiyun {
403*4882a593Smuzhiyun 	int			ret;
404*4882a593Smuzhiyun 	struct device		*tty_dev;
405*4882a593Smuzhiyun 	struct dbc_port		*port = dbc_to_port(dbc);
406*4882a593Smuzhiyun 
407*4882a593Smuzhiyun 	if (port->registered)
408*4882a593Smuzhiyun 		return -EBUSY;
409*4882a593Smuzhiyun 
410*4882a593Smuzhiyun 	xhci_dbc_tty_init_port(dbc, port);
411*4882a593Smuzhiyun 
412*4882a593Smuzhiyun 	ret = kfifo_alloc(&port->write_fifo, DBC_WRITE_BUF_SIZE, GFP_KERNEL);
413*4882a593Smuzhiyun 	if (ret)
414*4882a593Smuzhiyun 		goto err_exit_port;
415*4882a593Smuzhiyun 
416*4882a593Smuzhiyun 	ret = xhci_dbc_alloc_requests(dbc, BULK_IN, &port->read_pool,
417*4882a593Smuzhiyun 				      dbc_read_complete);
418*4882a593Smuzhiyun 	if (ret)
419*4882a593Smuzhiyun 		goto err_free_fifo;
420*4882a593Smuzhiyun 
421*4882a593Smuzhiyun 	ret = xhci_dbc_alloc_requests(dbc, BULK_OUT, &port->write_pool,
422*4882a593Smuzhiyun 				      dbc_write_complete);
423*4882a593Smuzhiyun 	if (ret)
424*4882a593Smuzhiyun 		goto err_free_requests;
425*4882a593Smuzhiyun 
426*4882a593Smuzhiyun 	tty_dev = tty_port_register_device(&port->port,
427*4882a593Smuzhiyun 					   dbc_tty_driver, 0, NULL);
428*4882a593Smuzhiyun 	if (IS_ERR(tty_dev)) {
429*4882a593Smuzhiyun 		ret = PTR_ERR(tty_dev);
430*4882a593Smuzhiyun 		goto err_free_requests;
431*4882a593Smuzhiyun 	}
432*4882a593Smuzhiyun 
433*4882a593Smuzhiyun 	port->registered = true;
434*4882a593Smuzhiyun 
435*4882a593Smuzhiyun 	return 0;
436*4882a593Smuzhiyun 
437*4882a593Smuzhiyun err_free_requests:
438*4882a593Smuzhiyun 	xhci_dbc_free_requests(&port->read_pool);
439*4882a593Smuzhiyun 	xhci_dbc_free_requests(&port->write_pool);
440*4882a593Smuzhiyun err_free_fifo:
441*4882a593Smuzhiyun 	kfifo_free(&port->write_fifo);
442*4882a593Smuzhiyun err_exit_port:
443*4882a593Smuzhiyun 	xhci_dbc_tty_exit_port(port);
444*4882a593Smuzhiyun 
445*4882a593Smuzhiyun 	dev_err(dbc->dev, "can't register tty port, err %d\n", ret);
446*4882a593Smuzhiyun 
447*4882a593Smuzhiyun 	return ret;
448*4882a593Smuzhiyun }
449*4882a593Smuzhiyun 
xhci_dbc_tty_unregister_device(struct xhci_dbc * dbc)450*4882a593Smuzhiyun static void xhci_dbc_tty_unregister_device(struct xhci_dbc *dbc)
451*4882a593Smuzhiyun {
452*4882a593Smuzhiyun 	struct dbc_port		*port = dbc_to_port(dbc);
453*4882a593Smuzhiyun 
454*4882a593Smuzhiyun 	if (!port->registered)
455*4882a593Smuzhiyun 		return;
456*4882a593Smuzhiyun 	tty_unregister_device(dbc_tty_driver, 0);
457*4882a593Smuzhiyun 	xhci_dbc_tty_exit_port(port);
458*4882a593Smuzhiyun 	port->registered = false;
459*4882a593Smuzhiyun 
460*4882a593Smuzhiyun 	kfifo_free(&port->write_fifo);
461*4882a593Smuzhiyun 	xhci_dbc_free_requests(&port->read_pool);
462*4882a593Smuzhiyun 	xhci_dbc_free_requests(&port->read_queue);
463*4882a593Smuzhiyun 	xhci_dbc_free_requests(&port->write_pool);
464*4882a593Smuzhiyun }
465*4882a593Smuzhiyun 
466*4882a593Smuzhiyun static const struct dbc_driver dbc_driver = {
467*4882a593Smuzhiyun 	.configure		= xhci_dbc_tty_register_device,
468*4882a593Smuzhiyun 	.disconnect		= xhci_dbc_tty_unregister_device,
469*4882a593Smuzhiyun };
470*4882a593Smuzhiyun 
xhci_dbc_tty_probe(struct xhci_hcd * xhci)471*4882a593Smuzhiyun int xhci_dbc_tty_probe(struct xhci_hcd *xhci)
472*4882a593Smuzhiyun {
473*4882a593Smuzhiyun 	struct xhci_dbc		*dbc = xhci->dbc;
474*4882a593Smuzhiyun 	struct dbc_port		*port;
475*4882a593Smuzhiyun 	int			status;
476*4882a593Smuzhiyun 
477*4882a593Smuzhiyun 	/* dbc_tty_init will be called by module init() in the future */
478*4882a593Smuzhiyun 	status = dbc_tty_init();
479*4882a593Smuzhiyun 	if (status)
480*4882a593Smuzhiyun 		return status;
481*4882a593Smuzhiyun 
482*4882a593Smuzhiyun 	port = kzalloc(sizeof(*port), GFP_KERNEL);
483*4882a593Smuzhiyun 	if (!port) {
484*4882a593Smuzhiyun 		status = -ENOMEM;
485*4882a593Smuzhiyun 		goto out;
486*4882a593Smuzhiyun 	}
487*4882a593Smuzhiyun 
488*4882a593Smuzhiyun 	dbc->driver = &dbc_driver;
489*4882a593Smuzhiyun 	dbc->priv = port;
490*4882a593Smuzhiyun 
491*4882a593Smuzhiyun 
492*4882a593Smuzhiyun 	dbc_tty_driver->driver_state = port;
493*4882a593Smuzhiyun 
494*4882a593Smuzhiyun 	return 0;
495*4882a593Smuzhiyun out:
496*4882a593Smuzhiyun 	/* dbc_tty_exit will be called by module_exit() in the future */
497*4882a593Smuzhiyun 	dbc_tty_exit();
498*4882a593Smuzhiyun 	return status;
499*4882a593Smuzhiyun }
500*4882a593Smuzhiyun 
501*4882a593Smuzhiyun /*
502*4882a593Smuzhiyun  * undo what probe did, assume dbc is stopped already.
503*4882a593Smuzhiyun  * we also assume tty_unregister_device() is called before this
504*4882a593Smuzhiyun  */
xhci_dbc_tty_remove(struct xhci_dbc * dbc)505*4882a593Smuzhiyun void xhci_dbc_tty_remove(struct xhci_dbc *dbc)
506*4882a593Smuzhiyun {
507*4882a593Smuzhiyun 	struct dbc_port         *port = dbc_to_port(dbc);
508*4882a593Smuzhiyun 
509*4882a593Smuzhiyun 	dbc->driver = NULL;
510*4882a593Smuzhiyun 	dbc->priv = NULL;
511*4882a593Smuzhiyun 	kfree(port);
512*4882a593Smuzhiyun 
513*4882a593Smuzhiyun 	/* dbc_tty_exit will be called by  module_exit() in the future */
514*4882a593Smuzhiyun 	dbc_tty_exit();
515*4882a593Smuzhiyun }
516*4882a593Smuzhiyun 
dbc_tty_init(void)517*4882a593Smuzhiyun static int dbc_tty_init(void)
518*4882a593Smuzhiyun {
519*4882a593Smuzhiyun 	int		ret;
520*4882a593Smuzhiyun 
521*4882a593Smuzhiyun 	dbc_tty_driver = tty_alloc_driver(1, TTY_DRIVER_REAL_RAW |
522*4882a593Smuzhiyun 					  TTY_DRIVER_DYNAMIC_DEV);
523*4882a593Smuzhiyun 	if (IS_ERR(dbc_tty_driver))
524*4882a593Smuzhiyun 		return PTR_ERR(dbc_tty_driver);
525*4882a593Smuzhiyun 
526*4882a593Smuzhiyun 	dbc_tty_driver->driver_name = "dbc_serial";
527*4882a593Smuzhiyun 	dbc_tty_driver->name = "ttyDBC";
528*4882a593Smuzhiyun 
529*4882a593Smuzhiyun 	dbc_tty_driver->type = TTY_DRIVER_TYPE_SERIAL;
530*4882a593Smuzhiyun 	dbc_tty_driver->subtype = SERIAL_TYPE_NORMAL;
531*4882a593Smuzhiyun 	dbc_tty_driver->init_termios = tty_std_termios;
532*4882a593Smuzhiyun 	dbc_tty_driver->init_termios.c_cflag =
533*4882a593Smuzhiyun 			B9600 | CS8 | CREAD | HUPCL | CLOCAL;
534*4882a593Smuzhiyun 	dbc_tty_driver->init_termios.c_ispeed = 9600;
535*4882a593Smuzhiyun 	dbc_tty_driver->init_termios.c_ospeed = 9600;
536*4882a593Smuzhiyun 
537*4882a593Smuzhiyun 	tty_set_operations(dbc_tty_driver, &dbc_tty_ops);
538*4882a593Smuzhiyun 
539*4882a593Smuzhiyun 	ret = tty_register_driver(dbc_tty_driver);
540*4882a593Smuzhiyun 	if (ret) {
541*4882a593Smuzhiyun 		pr_err("Can't register dbc tty driver\n");
542*4882a593Smuzhiyun 		put_tty_driver(dbc_tty_driver);
543*4882a593Smuzhiyun 	}
544*4882a593Smuzhiyun 	return ret;
545*4882a593Smuzhiyun }
546*4882a593Smuzhiyun 
dbc_tty_exit(void)547*4882a593Smuzhiyun static void dbc_tty_exit(void)
548*4882a593Smuzhiyun {
549*4882a593Smuzhiyun 	if (dbc_tty_driver) {
550*4882a593Smuzhiyun 		tty_unregister_driver(dbc_tty_driver);
551*4882a593Smuzhiyun 		put_tty_driver(dbc_tty_driver);
552*4882a593Smuzhiyun 		dbc_tty_driver = NULL;
553*4882a593Smuzhiyun 	}
554*4882a593Smuzhiyun }
555