File Index
Files and directories
Sign in
FILE

xhci-dbgtty.c

File preview with syntax highlighting.

linux-6.19.14/drivers/usb/host/xhci-dbgtty.c C
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  3 * xhci-dbgtty.c - tty glue for xHCI debug capability
  4 *
  5 * Copyright (C) 2017 Intel Corporation
  6 *
  7 * Author: Lu Baolu <baolu.lu@linux.intel.com>
  8 */
  9
 10#include <linux/slab.h>
 11#include <linux/tty.h>
 12#include <linux/tty_flip.h>
 13#include <linux/idr.h>
 14
 15#include "xhci.h"
 16#include "xhci-dbgcap.h"
 17
 18static struct tty_driver *dbc_tty_driver;
 19static struct idr dbc_tty_minors;
 20static DEFINE_MUTEX(dbc_tty_minors_lock);
 21
 22static inline struct dbc_port *dbc_to_port(struct xhci_dbc *dbc)
 23{
 24	return dbc->priv;
 25}
 26
 27static unsigned int
 28dbc_kfifo_to_req(struct dbc_port *port, char *packet)
 29{
 30	unsigned int	len;
 31
 32	len = kfifo_len(&port->port.xmit_fifo);
 33
 34	if (len == 0)
 35		return 0;
 36
 37	len = min(len, DBC_MAX_PACKET);
 38
 39	if (port->tx_boundary)
 40		len = min(port->tx_boundary, len);
 41
 42	len = kfifo_out(&port->port.xmit_fifo, packet, len);
 43
 44	if (port->tx_boundary)
 45		port->tx_boundary -= len;
 46
 47	return len;
 48}
 49
 50static int dbc_do_start_tx(struct dbc_port *port)
 51	__releases(&port->port_lock)
 52	__acquires(&port->port_lock)
 53{
 54	int			len;
 55	struct dbc_request	*req;
 56	int			status = 0;
 57	bool			do_tty_wake = false;
 58	struct list_head	*pool = &port->write_pool;
 59
 60	port->tx_running = true;
 61
 62	while (!list_empty(pool)) {
 63		req = list_entry(pool->next, struct dbc_request, list_pool);
 64		len = dbc_kfifo_to_req(port, req->buf);
 65		if (len == 0)
 66			break;
 67		do_tty_wake = true;
 68
 69		req->length = len;
 70		list_del(&req->list_pool);
 71
 72		spin_unlock(&port->port_lock);
 73		status = dbc_ep_queue(req);
 74		spin_lock(&port->port_lock);
 75
 76		if (status) {
 77			list_add(&req->list_pool, pool);
 78			break;
 79		}
 80	}
 81
 82	port->tx_running = false;
 83
 84	if (do_tty_wake && port->port.tty)
 85		tty_wakeup(port->port.tty);
 86
 87	return status;
 88}
 89
 90/* must be called with port->port_lock held */
 91static int dbc_start_tx(struct dbc_port *port)
 92{
 93	lockdep_assert_held(&port->port_lock);
 94
 95	if (port->tx_running)
 96		return -EBUSY;
 97
 98	return dbc_do_start_tx(port);
 99}
100
101static void dbc_start_rx(struct dbc_port *port)
102	__releases(&port->port_lock)
103	__acquires(&port->port_lock)
104{
105	struct dbc_request	*req;
106	int			status;
107	struct list_head	*pool = &port->read_pool;
108
109	while (!list_empty(pool)) {
110		if (!port->port.tty)
111			break;
112
113		req = list_entry(pool->next, struct dbc_request, list_pool);
114		list_del(&req->list_pool);
115		req->length = DBC_MAX_PACKET;
116
117		spin_unlock(&port->port_lock);
118		status = dbc_ep_queue(req);
119		spin_lock(&port->port_lock);
120
121		if (status) {
122			list_add(&req->list_pool, pool);
123			break;
124		}
125	}
126}
127
128/*
129 * Queue received data to tty buffer and push it.
130 *
131 * Returns nr of remaining bytes that didn't fit tty buffer, i.e. 0 if all
132 * bytes sucessfullt moved. In case of error returns negative errno.
133 * Call with lock held
134 */
135static int dbc_rx_push_buffer(struct dbc_port *port, struct dbc_request *req)
136{
137	char		*packet = req->buf;
138	unsigned int	n, size = req->actual;
139	int		count;
140
141	if (!req->actual)
142		return 0;
143
144	/* if n_read is set then request was partially moved to tty buffer */
145	n = port->n_read;
146	if (n) {
147		packet += n;
148		size -= n;
149	}
150
151	count = tty_insert_flip_string(&port->port, packet, size);
152	if (count)
153		tty_flip_buffer_push(&port->port);
154	if (count != size) {
155		port->n_read += count;
156		return size - count;
157	}
158
159	port->n_read = 0;
160	return 0;
161}
162
163static void
164dbc_read_complete(struct xhci_dbc *dbc, struct dbc_request *req)
165{
166	unsigned long		flags;
167	struct dbc_port		*port = dbc_to_port(dbc);
168	struct tty_struct	*tty;
169	int			untransferred;
170
171	tty = port->port.tty;
172
173	spin_lock_irqsave(&port->port_lock, flags);
174
175	/*
176	 * Only defer copyig data to tty buffer in case:
177	 * - !list_empty(&port->read_queue), there are older pending data
178	 * - tty is throttled
179	 * - failed to copy all data to buffer, defer remaining part
180	 */
181
182	if (list_empty(&port->read_queue) && tty && !tty_throttled(tty)) {
183		untransferred = dbc_rx_push_buffer(port, req);
184		if (untransferred == 0) {
185			list_add_tail(&req->list_pool, &port->read_pool);
186			if (req->status != -ESHUTDOWN)
187				dbc_start_rx(port);
188			goto out;
189		}
190	}
191
192	/* defer moving data from req to tty buffer to a tasklet */
193	list_add_tail(&req->list_pool, &port->read_queue);
194	tasklet_schedule(&port->push);
195out:
196	spin_unlock_irqrestore(&port->port_lock, flags);
197}
198
199static void dbc_write_complete(struct xhci_dbc *dbc, struct dbc_request *req)
200{
201	unsigned long		flags;
202	struct dbc_port		*port = dbc_to_port(dbc);
203
204	spin_lock_irqsave(&port->port_lock, flags);
205	list_add(&req->list_pool, &port->write_pool);
206	switch (req->status) {
207	case 0:
208		dbc_start_tx(port);
209		break;
210	case -ESHUTDOWN:
211		break;
212	default:
213		dev_warn(dbc->dev, "unexpected write complete status %d\n",
214			  req->status);
215		break;
216	}
217	spin_unlock_irqrestore(&port->port_lock, flags);
218}
219
220static void xhci_dbc_free_req(struct dbc_request *req)
221{
222	kfree(req->buf);
223	dbc_free_request(req);
224}
225
226static int
227xhci_dbc_alloc_requests(struct xhci_dbc *dbc, unsigned int direction,
228			struct list_head *head,
229			void (*fn)(struct xhci_dbc *, struct dbc_request *))
230{
231	int			i;
232	struct dbc_request	*req;
233
234	for (i = 0; i < DBC_QUEUE_SIZE; i++) {
235		req = dbc_alloc_request(dbc, direction, GFP_KERNEL);
236		if (!req)
237			break;
238
239		req->length = DBC_MAX_PACKET;
240		req->buf = kmalloc(req->length, GFP_KERNEL);
241		if (!req->buf) {
242			dbc_free_request(req);
243			break;
244		}
245
246		req->complete = fn;
247		list_add_tail(&req->list_pool, head);
248	}
249
250	return list_empty(head) ? -ENOMEM : 0;
251}
252
253static void
254xhci_dbc_free_requests(struct list_head *head)
255{
256	struct dbc_request	*req;
257
258	while (!list_empty(head)) {
259		req = list_entry(head->next, struct dbc_request, list_pool);
260		list_del(&req->list_pool);
261		xhci_dbc_free_req(req);
262	}
263}
264
265static int dbc_tty_install(struct tty_driver *driver, struct tty_struct *tty)
266{
267	struct dbc_port		*port;
268
269	mutex_lock(&dbc_tty_minors_lock);
270	port = idr_find(&dbc_tty_minors, tty->index);
271	mutex_unlock(&dbc_tty_minors_lock);
272
273	if (!port)
274		return -ENXIO;
275
276	tty->driver_data = port;
277
278	return tty_port_install(&port->port, driver, tty);
279}
280
281static int dbc_tty_open(struct tty_struct *tty, struct file *file)
282{
283	struct dbc_port		*port = tty->driver_data;
284
285	return tty_port_open(&port->port, tty, file);
286}
287
288static void dbc_tty_close(struct tty_struct *tty, struct file *file)
289{
290	struct dbc_port		*port = tty->driver_data;
291
292	tty_port_close(&port->port, tty, file);
293}
294
295static ssize_t dbc_tty_write(struct tty_struct *tty, const u8 *buf,
296			     size_t count)
297{
298	struct dbc_port		*port = tty->driver_data;
299	unsigned long		flags;
300	unsigned int		written = 0;
301
302	spin_lock_irqsave(&port->port_lock, flags);
303
304	/*
305	 * Treat tty write as one usb transfer. Make sure the writes are turned
306	 * into TRB request having the same size boundaries as the tty writes.
307	 * Don't add data to kfifo before previous write is turned into TRBs
308	 */
309	if (port->tx_boundary) {
310		spin_unlock_irqrestore(&port->port_lock, flags);
311		return 0;
312	}
313
314	if (count) {
315		written = kfifo_in(&port->port.xmit_fifo, buf, count);
316
317		if (written == count)
318			port->tx_boundary = kfifo_len(&port->port.xmit_fifo);
319
320		dbc_start_tx(port);
321	}
322
323	spin_unlock_irqrestore(&port->port_lock, flags);
324
325	return written;
326}
327
328static int dbc_tty_put_char(struct tty_struct *tty, u8 ch)
329{
330	struct dbc_port		*port = tty->driver_data;
331	unsigned long		flags;
332	int			status;
333
334	spin_lock_irqsave(&port->port_lock, flags);
335	status = kfifo_put(&port->port.xmit_fifo, ch);
336	spin_unlock_irqrestore(&port->port_lock, flags);
337
338	return status;
339}
340
341static void dbc_tty_flush_chars(struct tty_struct *tty)
342{
343	struct dbc_port		*port = tty->driver_data;
344	unsigned long		flags;
345
346	spin_lock_irqsave(&port->port_lock, flags);
347	dbc_start_tx(port);
348	spin_unlock_irqrestore(&port->port_lock, flags);
349}
350
351static unsigned int dbc_tty_write_room(struct tty_struct *tty)
352{
353	struct dbc_port		*port = tty->driver_data;
354	unsigned long		flags;
355	unsigned int		room;
356
357	spin_lock_irqsave(&port->port_lock, flags);
358	room = kfifo_avail(&port->port.xmit_fifo);
359
360	if (port->tx_boundary)
361		room = 0;
362
363	spin_unlock_irqrestore(&port->port_lock, flags);
364
365	return room;
366}
367
368static unsigned int dbc_tty_chars_in_buffer(struct tty_struct *tty)
369{
370	struct dbc_port		*port = tty->driver_data;
371	unsigned long		flags;
372	unsigned int		chars;
373
374	spin_lock_irqsave(&port->port_lock, flags);
375	chars = kfifo_len(&port->port.xmit_fifo);
376	spin_unlock_irqrestore(&port->port_lock, flags);
377
378	return chars;
379}
380
381static void dbc_tty_unthrottle(struct tty_struct *tty)
382{
383	struct dbc_port		*port = tty->driver_data;
384	unsigned long		flags;
385
386	spin_lock_irqsave(&port->port_lock, flags);
387	tasklet_schedule(&port->push);
388	spin_unlock_irqrestore(&port->port_lock, flags);
389}
390
391static const struct tty_operations dbc_tty_ops = {
392	.install		= dbc_tty_install,
393	.open			= dbc_tty_open,
394	.close			= dbc_tty_close,
395	.write			= dbc_tty_write,
396	.put_char		= dbc_tty_put_char,
397	.flush_chars		= dbc_tty_flush_chars,
398	.write_room		= dbc_tty_write_room,
399	.chars_in_buffer	= dbc_tty_chars_in_buffer,
400	.unthrottle		= dbc_tty_unthrottle,
401};
402
403static void dbc_rx_push(struct tasklet_struct *t)
404{
405	struct dbc_request	*req;
406	struct tty_struct	*tty;
407	unsigned long		flags;
408	bool			disconnect = false;
409	struct dbc_port		*port = from_tasklet(port, t, push);
410	struct list_head	*queue = &port->read_queue;
411	int			untransferred;
412
413	spin_lock_irqsave(&port->port_lock, flags);
414	tty = port->port.tty;
415	while (!list_empty(queue)) {
416		req = list_first_entry(queue, struct dbc_request, list_pool);
417
418		if (tty && tty_throttled(tty))
419			break;
420
421		switch (req->status) {
422		case 0:
423			break;
424		case -ESHUTDOWN:
425			disconnect = true;
426			break;
427		default:
428			pr_warn("ttyDBC0: unexpected RX status %d\n",
429				req->status);
430			break;
431		}
432
433		untransferred = dbc_rx_push_buffer(port, req);
434		if (untransferred > 0)
435			break;
436
437		list_move_tail(&req->list_pool, &port->read_pool);
438	}
439
440	if (!list_empty(queue))
441		tasklet_schedule(&port->push);
442
443	if (!disconnect)
444		dbc_start_rx(port);
445
446	spin_unlock_irqrestore(&port->port_lock, flags);
447}
448
449static int dbc_port_activate(struct tty_port *_port, struct tty_struct *tty)
450{
451	unsigned long	flags;
452	struct dbc_port	*port = container_of(_port, struct dbc_port, port);
453
454	spin_lock_irqsave(&port->port_lock, flags);
455	dbc_start_rx(port);
456	spin_unlock_irqrestore(&port->port_lock, flags);
457
458	return 0;
459}
460
461static const struct tty_port_operations dbc_port_ops = {
462	.activate =	dbc_port_activate,
463};
464
465static void
466xhci_dbc_tty_init_port(struct xhci_dbc *dbc, struct dbc_port *port)
467{
468	tty_port_init(&port->port);
469	spin_lock_init(&port->port_lock);
470	tasklet_setup(&port->push, dbc_rx_push);
471	INIT_LIST_HEAD(&port->read_pool);
472	INIT_LIST_HEAD(&port->read_queue);
473	INIT_LIST_HEAD(&port->write_pool);
474
475	port->port.ops =	&dbc_port_ops;
476	port->n_read =		0;
477}
478
479static void
480xhci_dbc_tty_exit_port(struct dbc_port *port)
481{
482	tasklet_kill(&port->push);
483	tty_port_destroy(&port->port);
484}
485
486static int xhci_dbc_tty_register_device(struct xhci_dbc *dbc)
487{
488	int			ret;
489	struct device		*tty_dev;
490	struct dbc_port		*port = dbc_to_port(dbc);
491
492	if (port->registered)
493		return -EBUSY;
494
495	xhci_dbc_tty_init_port(dbc, port);
496
497	mutex_lock(&dbc_tty_minors_lock);
498	port->minor = idr_alloc(&dbc_tty_minors, port, 0, 64, GFP_KERNEL);
499	mutex_unlock(&dbc_tty_minors_lock);
500
501	if (port->minor < 0) {
502		ret = port->minor;
503		goto err_idr;
504	}
505
506	ret = kfifo_alloc(&port->port.xmit_fifo, DBC_WRITE_BUF_SIZE,
507			  GFP_KERNEL);
508	if (ret)
509		goto err_exit_port;
510
511	ret = xhci_dbc_alloc_requests(dbc, BULK_IN, &port->read_pool,
512				      dbc_read_complete);
513	if (ret)
514		goto err_free_fifo;
515
516	ret = xhci_dbc_alloc_requests(dbc, BULK_OUT, &port->write_pool,
517				      dbc_write_complete);
518	if (ret)
519		goto err_free_requests;
520
521	tty_dev = tty_port_register_device(&port->port,
522					   dbc_tty_driver, port->minor, NULL);
523	if (IS_ERR(tty_dev)) {
524		ret = PTR_ERR(tty_dev);
525		goto err_free_requests;
526	}
527
528	port->registered = true;
529
530	return 0;
531
532err_free_requests:
533	xhci_dbc_free_requests(&port->read_pool);
534	xhci_dbc_free_requests(&port->write_pool);
535err_free_fifo:
536	kfifo_free(&port->port.xmit_fifo);
537err_exit_port:
538	idr_remove(&dbc_tty_minors, port->minor);
539err_idr:
540	xhci_dbc_tty_exit_port(port);
541
542	dev_err(dbc->dev, "can't register tty port, err %d\n", ret);
543
544	return ret;
545}
546
547static void xhci_dbc_tty_unregister_device(struct xhci_dbc *dbc)
548{
549	struct dbc_port		*port = dbc_to_port(dbc);
550
551	if (!port->registered)
552		return;
553	/*
554	 * Hang up the TTY. This wakes up any blocked
555	 * writers and causes subsequent writes to fail.
556	 */
557	tty_port_tty_vhangup(&port->port);
558
559	tty_unregister_device(dbc_tty_driver, port->minor);
560	xhci_dbc_tty_exit_port(port);
561	port->registered = false;
562
563	mutex_lock(&dbc_tty_minors_lock);
564	idr_remove(&dbc_tty_minors, port->minor);
565	mutex_unlock(&dbc_tty_minors_lock);
566
567	kfifo_free(&port->port.xmit_fifo);
568	xhci_dbc_free_requests(&port->read_pool);
569	xhci_dbc_free_requests(&port->read_queue);
570	xhci_dbc_free_requests(&port->write_pool);
571}
572
573static const struct dbc_driver dbc_driver = {
574	.configure		= xhci_dbc_tty_register_device,
575	.disconnect		= xhci_dbc_tty_unregister_device,
576};
577
578int xhci_dbc_tty_probe(struct device *dev, void __iomem *base, struct xhci_hcd *xhci)
579{
580	struct xhci_dbc		*dbc;
581	struct dbc_port		*port;
582	int			status;
583
584	if (!dbc_tty_driver)
585		return -ENODEV;
586
587	port = kzalloc(sizeof(*port), GFP_KERNEL);
588	if (!port)
589		return -ENOMEM;
590
591	dbc = xhci_alloc_dbc(dev, base, &dbc_driver);
592
593	if (!dbc) {
594		status = -ENOMEM;
595		goto out2;
596	}
597
598	dbc->priv = port;
599
600	/* get rid of xhci once this is a real driver binding to a device */
601	xhci->dbc = dbc;
602
603	return 0;
604out2:
605	kfree(port);
606
607	return status;
608}
609
610/*
611 * undo what probe did, assume dbc is stopped already.
612 * we also assume tty_unregister_device() is called before this
613 */
614void xhci_dbc_tty_remove(struct xhci_dbc *dbc)
615{
616	struct dbc_port         *port = dbc_to_port(dbc);
617
618	xhci_dbc_remove(dbc);
619	kfree(port);
620}
621
622int dbc_tty_init(void)
623{
624	int		ret;
625
626	idr_init(&dbc_tty_minors);
627
628	dbc_tty_driver = tty_alloc_driver(64, TTY_DRIVER_REAL_RAW |
629					  TTY_DRIVER_DYNAMIC_DEV);
630	if (IS_ERR(dbc_tty_driver)) {
631		idr_destroy(&dbc_tty_minors);
632		return PTR_ERR(dbc_tty_driver);
633	}
634
635	dbc_tty_driver->driver_name = "dbc_serial";
636	dbc_tty_driver->name = "ttyDBC";
637
638	dbc_tty_driver->type = TTY_DRIVER_TYPE_SERIAL;
639	dbc_tty_driver->subtype = SERIAL_TYPE_NORMAL;
640	dbc_tty_driver->init_termios = tty_std_termios;
641	dbc_tty_driver->init_termios.c_lflag &= ~ECHO;
642	dbc_tty_driver->init_termios.c_cflag =
643			B9600 | CS8 | CREAD | HUPCL | CLOCAL;
644	dbc_tty_driver->init_termios.c_ispeed = 9600;
645	dbc_tty_driver->init_termios.c_ospeed = 9600;
646
647	tty_set_operations(dbc_tty_driver, &dbc_tty_ops);
648
649	ret = tty_register_driver(dbc_tty_driver);
650	if (ret) {
651		pr_err("Can't register dbc tty driver\n");
652		tty_driver_kref_put(dbc_tty_driver);
653		idr_destroy(&dbc_tty_minors);
654	}
655
656	return ret;
657}
658
659void dbc_tty_exit(void)
660{
661	if (dbc_tty_driver) {
662		tty_unregister_driver(dbc_tty_driver);
663		tty_driver_kref_put(dbc_tty_driver);
664		dbc_tty_driver = NULL;
665	}
666
667	idr_destroy(&dbc_tty_minors);
668}
~/ Read-only file preview.