14786d2efSLu Baolu // SPDX-License-Identifier: GPL-2.0
20e1acecaSLee Jones /*
3dfba2174SLu Baolu * xhci-dbgtty.c - tty glue for xHCI debug capability
4dfba2174SLu Baolu *
5dfba2174SLu Baolu * Copyright (C) 2017 Intel Corporation
6dfba2174SLu Baolu *
7dfba2174SLu Baolu * Author: Lu Baolu <baolu.lu@linux.intel.com>
8dfba2174SLu Baolu */
9dfba2174SLu Baolu
10dfba2174SLu Baolu #include <linux/slab.h>
11dfba2174SLu Baolu #include <linux/tty.h>
12dfba2174SLu Baolu #include <linux/tty_flip.h>
13e1ec140fSMathias Nyman #include <linux/idr.h>
14dfba2174SLu Baolu
15dfba2174SLu Baolu #include "xhci.h"
16dfba2174SLu Baolu #include "xhci-dbgcap.h"
17dfba2174SLu Baolu
184521f161SMathias Nyman static struct tty_driver *dbc_tty_driver;
19e1ec140fSMathias Nyman static struct idr dbc_tty_minors;
20e1ec140fSMathias Nyman static DEFINE_MUTEX(dbc_tty_minors_lock);
214521f161SMathias Nyman
dbc_to_port(struct xhci_dbc * dbc)229a360a7cSMathias Nyman static inline struct dbc_port *dbc_to_port(struct xhci_dbc *dbc)
239a360a7cSMathias Nyman {
249a360a7cSMathias Nyman return dbc->priv;
259a360a7cSMathias Nyman }
269a360a7cSMathias Nyman
27*c691c2adSMathias Nyman static unsigned int
dbc_kfifo_to_req(struct dbc_port * port,char * packet)28*c691c2adSMathias Nyman dbc_kfifo_to_req(struct dbc_port *port, char *packet)
29*c691c2adSMathias Nyman {
30*c691c2adSMathias Nyman unsigned int len;
31*c691c2adSMathias Nyman
32*c691c2adSMathias Nyman len = kfifo_len(&port->port.xmit_fifo);
33*c691c2adSMathias Nyman
34*c691c2adSMathias Nyman if (len == 0)
35*c691c2adSMathias Nyman return 0;
36*c691c2adSMathias Nyman
37*c691c2adSMathias Nyman len = min(len, DBC_MAX_PACKET);
38*c691c2adSMathias Nyman
39*c691c2adSMathias Nyman if (port->tx_boundary)
40*c691c2adSMathias Nyman len = min(port->tx_boundary, len);
41*c691c2adSMathias Nyman
42*c691c2adSMathias Nyman len = kfifo_out(&port->port.xmit_fifo, packet, len);
43*c691c2adSMathias Nyman
44*c691c2adSMathias Nyman if (port->tx_boundary)
45*c691c2adSMathias Nyman port->tx_boundary -= len;
46*c691c2adSMathias Nyman
47*c691c2adSMathias Nyman return len;
48*c691c2adSMathias Nyman }
49*c691c2adSMathias Nyman
dbc_start_tx(struct dbc_port * port)50dfba2174SLu Baolu static int dbc_start_tx(struct dbc_port *port)
51dfba2174SLu Baolu __releases(&port->port_lock)
52dfba2174SLu Baolu __acquires(&port->port_lock)
53dfba2174SLu Baolu {
54dfba2174SLu Baolu int len;
55dfba2174SLu Baolu struct dbc_request *req;
56dfba2174SLu Baolu int status = 0;
57dfba2174SLu Baolu bool do_tty_wake = false;
58dfba2174SLu Baolu struct list_head *pool = &port->write_pool;
59dfba2174SLu Baolu
60dfba2174SLu Baolu while (!list_empty(pool)) {
61dfba2174SLu Baolu req = list_entry(pool->next, struct dbc_request, list_pool);
62*c691c2adSMathias Nyman len = dbc_kfifo_to_req(port, req->buf);
63dfba2174SLu Baolu if (len == 0)
64dfba2174SLu Baolu break;
65dfba2174SLu Baolu do_tty_wake = true;
66dfba2174SLu Baolu
67dfba2174SLu Baolu req->length = len;
68dfba2174SLu Baolu list_del(&req->list_pool);
69dfba2174SLu Baolu
70dfba2174SLu Baolu spin_unlock(&port->port_lock);
71e0aa56dcSMathias Nyman status = dbc_ep_queue(req);
72dfba2174SLu Baolu spin_lock(&port->port_lock);
73dfba2174SLu Baolu
74dfba2174SLu Baolu if (status) {
75dfba2174SLu Baolu list_add(&req->list_pool, pool);
76dfba2174SLu Baolu break;
77dfba2174SLu Baolu }
78dfba2174SLu Baolu }
79dfba2174SLu Baolu
80dfba2174SLu Baolu if (do_tty_wake && port->port.tty)
81dfba2174SLu Baolu tty_wakeup(port->port.tty);
82dfba2174SLu Baolu
83dfba2174SLu Baolu return status;
84dfba2174SLu Baolu }
85dfba2174SLu Baolu
dbc_start_rx(struct dbc_port * port)86dfba2174SLu Baolu static void dbc_start_rx(struct dbc_port *port)
87dfba2174SLu Baolu __releases(&port->port_lock)
88dfba2174SLu Baolu __acquires(&port->port_lock)
89dfba2174SLu Baolu {
90dfba2174SLu Baolu struct dbc_request *req;
91dfba2174SLu Baolu int status;
92dfba2174SLu Baolu struct list_head *pool = &port->read_pool;
93dfba2174SLu Baolu
94dfba2174SLu Baolu while (!list_empty(pool)) {
95dfba2174SLu Baolu if (!port->port.tty)
96dfba2174SLu Baolu break;
97dfba2174SLu Baolu
98dfba2174SLu Baolu req = list_entry(pool->next, struct dbc_request, list_pool);
99dfba2174SLu Baolu list_del(&req->list_pool);
100dfba2174SLu Baolu req->length = DBC_MAX_PACKET;
101dfba2174SLu Baolu
102dfba2174SLu Baolu spin_unlock(&port->port_lock);
103e0aa56dcSMathias Nyman status = dbc_ep_queue(req);
104dfba2174SLu Baolu spin_lock(&port->port_lock);
105dfba2174SLu Baolu
106dfba2174SLu Baolu if (status) {
107dfba2174SLu Baolu list_add(&req->list_pool, pool);
108dfba2174SLu Baolu break;
109dfba2174SLu Baolu }
110dfba2174SLu Baolu }
111dfba2174SLu Baolu }
112dfba2174SLu Baolu
113dfba2174SLu Baolu static void
dbc_read_complete(struct xhci_dbc * dbc,struct dbc_request * req)114f39f3afdSMathias Nyman dbc_read_complete(struct xhci_dbc *dbc, struct dbc_request *req)
115dfba2174SLu Baolu {
116a098dc8bSLu Baolu unsigned long flags;
1179a360a7cSMathias Nyman struct dbc_port *port = dbc_to_port(dbc);
118dfba2174SLu Baolu
119a098dc8bSLu Baolu spin_lock_irqsave(&port->port_lock, flags);
120dfba2174SLu Baolu list_add_tail(&req->list_pool, &port->read_queue);
121dfba2174SLu Baolu tasklet_schedule(&port->push);
122a098dc8bSLu Baolu spin_unlock_irqrestore(&port->port_lock, flags);
123dfba2174SLu Baolu }
124dfba2174SLu Baolu
dbc_write_complete(struct xhci_dbc * dbc,struct dbc_request * req)125f39f3afdSMathias Nyman static void dbc_write_complete(struct xhci_dbc *dbc, struct dbc_request *req)
126dfba2174SLu Baolu {
127a098dc8bSLu Baolu unsigned long flags;
1289a360a7cSMathias Nyman struct dbc_port *port = dbc_to_port(dbc);
129dfba2174SLu Baolu
130a098dc8bSLu Baolu spin_lock_irqsave(&port->port_lock, flags);
131dfba2174SLu Baolu list_add(&req->list_pool, &port->write_pool);
132dfba2174SLu Baolu switch (req->status) {
133dfba2174SLu Baolu case 0:
134dfba2174SLu Baolu dbc_start_tx(port);
135dfba2174SLu Baolu break;
136dfba2174SLu Baolu case -ESHUTDOWN:
137dfba2174SLu Baolu break;
138dfba2174SLu Baolu default:
139f39f3afdSMathias Nyman dev_warn(dbc->dev, "unexpected write complete status %d\n",
140dfba2174SLu Baolu req->status);
141dfba2174SLu Baolu break;
142dfba2174SLu Baolu }
143a098dc8bSLu Baolu spin_unlock_irqrestore(&port->port_lock, flags);
144dfba2174SLu Baolu }
145dfba2174SLu Baolu
xhci_dbc_free_req(struct dbc_request * req)146e0aa56dcSMathias Nyman static void xhci_dbc_free_req(struct dbc_request *req)
147dfba2174SLu Baolu {
148dfba2174SLu Baolu kfree(req->buf);
149e0aa56dcSMathias Nyman dbc_free_request(req);
150dfba2174SLu Baolu }
151dfba2174SLu Baolu
152dfba2174SLu Baolu 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 *))153e0aa56dcSMathias Nyman xhci_dbc_alloc_requests(struct xhci_dbc *dbc, unsigned int direction,
154e0aa56dcSMathias Nyman struct list_head *head,
155f39f3afdSMathias Nyman void (*fn)(struct xhci_dbc *, struct dbc_request *))
156dfba2174SLu Baolu {
157dfba2174SLu Baolu int i;
158dfba2174SLu Baolu struct dbc_request *req;
159dfba2174SLu Baolu
160dfba2174SLu Baolu for (i = 0; i < DBC_QUEUE_SIZE; i++) {
161e0aa56dcSMathias Nyman req = dbc_alloc_request(dbc, direction, GFP_KERNEL);
162dfba2174SLu Baolu if (!req)
163dfba2174SLu Baolu break;
164dfba2174SLu Baolu
165dfba2174SLu Baolu req->length = DBC_MAX_PACKET;
166dfba2174SLu Baolu req->buf = kmalloc(req->length, GFP_KERNEL);
167dfba2174SLu Baolu if (!req->buf) {
168e0aa56dcSMathias Nyman dbc_free_request(req);
169dfba2174SLu Baolu break;
170dfba2174SLu Baolu }
171dfba2174SLu Baolu
172dfba2174SLu Baolu req->complete = fn;
173dfba2174SLu Baolu list_add_tail(&req->list_pool, head);
174dfba2174SLu Baolu }
175dfba2174SLu Baolu
176dfba2174SLu Baolu return list_empty(head) ? -ENOMEM : 0;
177dfba2174SLu Baolu }
178dfba2174SLu Baolu
179dfba2174SLu Baolu static void
xhci_dbc_free_requests(struct list_head * head)180e0aa56dcSMathias Nyman xhci_dbc_free_requests(struct list_head *head)
181dfba2174SLu Baolu {
182dfba2174SLu Baolu struct dbc_request *req;
183dfba2174SLu Baolu
184dfba2174SLu Baolu while (!list_empty(head)) {
185dfba2174SLu Baolu req = list_entry(head->next, struct dbc_request, list_pool);
186dfba2174SLu Baolu list_del(&req->list_pool);
187e0aa56dcSMathias Nyman xhci_dbc_free_req(req);
188dfba2174SLu Baolu }
189dfba2174SLu Baolu }
190dfba2174SLu Baolu
dbc_tty_install(struct tty_driver * driver,struct tty_struct * tty)191dfba2174SLu Baolu static int dbc_tty_install(struct tty_driver *driver, struct tty_struct *tty)
192dfba2174SLu Baolu {
193e1ec140fSMathias Nyman struct dbc_port *port;
194e1ec140fSMathias Nyman
195e1ec140fSMathias Nyman mutex_lock(&dbc_tty_minors_lock);
196e1ec140fSMathias Nyman port = idr_find(&dbc_tty_minors, tty->index);
197e1ec140fSMathias Nyman mutex_unlock(&dbc_tty_minors_lock);
198e1ec140fSMathias Nyman
199e1ec140fSMathias Nyman if (!port)
200e1ec140fSMathias Nyman return -ENXIO;
201dfba2174SLu Baolu
202dfba2174SLu Baolu tty->driver_data = port;
203dfba2174SLu Baolu
204dfba2174SLu Baolu return tty_port_install(&port->port, driver, tty);
205dfba2174SLu Baolu }
206dfba2174SLu Baolu
dbc_tty_open(struct tty_struct * tty,struct file * file)207dfba2174SLu Baolu static int dbc_tty_open(struct tty_struct *tty, struct file *file)
208dfba2174SLu Baolu {
209dfba2174SLu Baolu struct dbc_port *port = tty->driver_data;
210dfba2174SLu Baolu
211dfba2174SLu Baolu return tty_port_open(&port->port, tty, file);
212dfba2174SLu Baolu }
213dfba2174SLu Baolu
dbc_tty_close(struct tty_struct * tty,struct file * file)214dfba2174SLu Baolu static void dbc_tty_close(struct tty_struct *tty, struct file *file)
215dfba2174SLu Baolu {
216dfba2174SLu Baolu struct dbc_port *port = tty->driver_data;
217dfba2174SLu Baolu
218dfba2174SLu Baolu tty_port_close(&port->port, tty, file);
219dfba2174SLu Baolu }
220dfba2174SLu Baolu
dbc_tty_write(struct tty_struct * tty,const u8 * buf,size_t count)22195713967SJiri Slaby (SUSE) static ssize_t dbc_tty_write(struct tty_struct *tty, const u8 *buf,
22295713967SJiri Slaby (SUSE) size_t count)
223dfba2174SLu Baolu {
224dfba2174SLu Baolu struct dbc_port *port = tty->driver_data;
225dfba2174SLu Baolu unsigned long flags;
226*c691c2adSMathias Nyman unsigned int written = 0;
227dfba2174SLu Baolu
228dfba2174SLu Baolu spin_lock_irqsave(&port->port_lock, flags);
229*c691c2adSMathias Nyman
230*c691c2adSMathias Nyman /*
231*c691c2adSMathias Nyman * Treat tty write as one usb transfer. Make sure the writes are turned
232*c691c2adSMathias Nyman * into TRB request having the same size boundaries as the tty writes.
233*c691c2adSMathias Nyman * Don't add data to kfifo before previous write is turned into TRBs
234*c691c2adSMathias Nyman */
235*c691c2adSMathias Nyman if (port->tx_boundary) {
236*c691c2adSMathias Nyman spin_unlock_irqrestore(&port->port_lock, flags);
237*c691c2adSMathias Nyman return 0;
238*c691c2adSMathias Nyman }
239*c691c2adSMathias Nyman
240*c691c2adSMathias Nyman if (count) {
241*c691c2adSMathias Nyman written = kfifo_in(&port->port.xmit_fifo, buf, count);
242*c691c2adSMathias Nyman
243*c691c2adSMathias Nyman if (written == count)
244*c691c2adSMathias Nyman port->tx_boundary = kfifo_len(&port->port.xmit_fifo);
245*c691c2adSMathias Nyman
246dfba2174SLu Baolu dbc_start_tx(port);
247*c691c2adSMathias Nyman }
248*c691c2adSMathias Nyman
249dfba2174SLu Baolu spin_unlock_irqrestore(&port->port_lock, flags);
250dfba2174SLu Baolu
251*c691c2adSMathias Nyman return written;
252dfba2174SLu Baolu }
253dfba2174SLu Baolu
dbc_tty_put_char(struct tty_struct * tty,u8 ch)254dcaafbe6SJiri Slaby (SUSE) static int dbc_tty_put_char(struct tty_struct *tty, u8 ch)
255dfba2174SLu Baolu {
256dfba2174SLu Baolu struct dbc_port *port = tty->driver_data;
257dfba2174SLu Baolu unsigned long flags;
258dfba2174SLu Baolu int status;
259dfba2174SLu Baolu
260dfba2174SLu Baolu spin_lock_irqsave(&port->port_lock, flags);
2617cbcb40dSJiri Slaby (SUSE) status = kfifo_put(&port->port.xmit_fifo, ch);
262dfba2174SLu Baolu spin_unlock_irqrestore(&port->port_lock, flags);
263dfba2174SLu Baolu
264dfba2174SLu Baolu return status;
265dfba2174SLu Baolu }
266dfba2174SLu Baolu
dbc_tty_flush_chars(struct tty_struct * tty)267dfba2174SLu Baolu static void dbc_tty_flush_chars(struct tty_struct *tty)
268dfba2174SLu Baolu {
269dfba2174SLu Baolu struct dbc_port *port = tty->driver_data;
270dfba2174SLu Baolu unsigned long flags;
271dfba2174SLu Baolu
272dfba2174SLu Baolu spin_lock_irqsave(&port->port_lock, flags);
273dfba2174SLu Baolu dbc_start_tx(port);
274dfba2174SLu Baolu spin_unlock_irqrestore(&port->port_lock, flags);
275dfba2174SLu Baolu }
276dfba2174SLu Baolu
dbc_tty_write_room(struct tty_struct * tty)27703b3b1a2SJiri Slaby static unsigned int dbc_tty_write_room(struct tty_struct *tty)
278dfba2174SLu Baolu {
279dfba2174SLu Baolu struct dbc_port *port = tty->driver_data;
280dfba2174SLu Baolu unsigned long flags;
28103b3b1a2SJiri Slaby unsigned int room;
282dfba2174SLu Baolu
283dfba2174SLu Baolu spin_lock_irqsave(&port->port_lock, flags);
2847cbcb40dSJiri Slaby (SUSE) room = kfifo_avail(&port->port.xmit_fifo);
285*c691c2adSMathias Nyman
286*c691c2adSMathias Nyman if (port->tx_boundary)
287*c691c2adSMathias Nyman room = 0;
288*c691c2adSMathias Nyman
289dfba2174SLu Baolu spin_unlock_irqrestore(&port->port_lock, flags);
290dfba2174SLu Baolu
291dfba2174SLu Baolu return room;
292dfba2174SLu Baolu }
293dfba2174SLu Baolu
dbc_tty_chars_in_buffer(struct tty_struct * tty)294fff4ef17SJiri Slaby static unsigned int dbc_tty_chars_in_buffer(struct tty_struct *tty)
295dfba2174SLu Baolu {
296dfba2174SLu Baolu struct dbc_port *port = tty->driver_data;
297dfba2174SLu Baolu unsigned long flags;
298fff4ef17SJiri Slaby unsigned int chars;
299dfba2174SLu Baolu
300dfba2174SLu Baolu spin_lock_irqsave(&port->port_lock, flags);
3017cbcb40dSJiri Slaby (SUSE) chars = kfifo_len(&port->port.xmit_fifo);
302dfba2174SLu Baolu spin_unlock_irqrestore(&port->port_lock, flags);
303dfba2174SLu Baolu
304dfba2174SLu Baolu return chars;
305dfba2174SLu Baolu }
306dfba2174SLu Baolu
dbc_tty_unthrottle(struct tty_struct * tty)307dfba2174SLu Baolu static void dbc_tty_unthrottle(struct tty_struct *tty)
308dfba2174SLu Baolu {
309dfba2174SLu Baolu struct dbc_port *port = tty->driver_data;
310dfba2174SLu Baolu unsigned long flags;
311dfba2174SLu Baolu
312dfba2174SLu Baolu spin_lock_irqsave(&port->port_lock, flags);
313dfba2174SLu Baolu tasklet_schedule(&port->push);
314dfba2174SLu Baolu spin_unlock_irqrestore(&port->port_lock, flags);
315dfba2174SLu Baolu }
316dfba2174SLu Baolu
317dfba2174SLu Baolu static const struct tty_operations dbc_tty_ops = {
318dfba2174SLu Baolu .install = dbc_tty_install,
319dfba2174SLu Baolu .open = dbc_tty_open,
320dfba2174SLu Baolu .close = dbc_tty_close,
321dfba2174SLu Baolu .write = dbc_tty_write,
322dfba2174SLu Baolu .put_char = dbc_tty_put_char,
323dfba2174SLu Baolu .flush_chars = dbc_tty_flush_chars,
324dfba2174SLu Baolu .write_room = dbc_tty_write_room,
325dfba2174SLu Baolu .chars_in_buffer = dbc_tty_chars_in_buffer,
326dfba2174SLu Baolu .unthrottle = dbc_tty_unthrottle,
327dfba2174SLu Baolu };
328dfba2174SLu Baolu
dbc_rx_push(struct tasklet_struct * t)32981d324cdSAllen Pais static void dbc_rx_push(struct tasklet_struct *t)
330dfba2174SLu Baolu {
331dfba2174SLu Baolu struct dbc_request *req;
332dfba2174SLu Baolu struct tty_struct *tty;
333a098dc8bSLu Baolu unsigned long flags;
334dfba2174SLu Baolu bool do_push = false;
335dfba2174SLu Baolu bool disconnect = false;
33681d324cdSAllen Pais struct dbc_port *port = from_tasklet(port, t, push);
337dfba2174SLu Baolu struct list_head *queue = &port->read_queue;
338dfba2174SLu Baolu
339a098dc8bSLu Baolu spin_lock_irqsave(&port->port_lock, flags);
340dfba2174SLu Baolu tty = port->port.tty;
341dfba2174SLu Baolu while (!list_empty(queue)) {
342dfba2174SLu Baolu req = list_first_entry(queue, struct dbc_request, list_pool);
343dfba2174SLu Baolu
344dfba2174SLu Baolu if (tty && tty_throttled(tty))
345dfba2174SLu Baolu break;
346dfba2174SLu Baolu
347dfba2174SLu Baolu switch (req->status) {
348dfba2174SLu Baolu case 0:
349dfba2174SLu Baolu break;
350dfba2174SLu Baolu case -ESHUTDOWN:
351dfba2174SLu Baolu disconnect = true;
352dfba2174SLu Baolu break;
353dfba2174SLu Baolu default:
354dfba2174SLu Baolu pr_warn("ttyDBC0: unexpected RX status %d\n",
355dfba2174SLu Baolu req->status);
356dfba2174SLu Baolu break;
357dfba2174SLu Baolu }
358dfba2174SLu Baolu
359dfba2174SLu Baolu if (req->actual) {
360dfba2174SLu Baolu char *packet = req->buf;
361dfba2174SLu Baolu unsigned int n, size = req->actual;
362dfba2174SLu Baolu int count;
363dfba2174SLu Baolu
364dfba2174SLu Baolu n = port->n_read;
365dfba2174SLu Baolu if (n) {
366dfba2174SLu Baolu packet += n;
367dfba2174SLu Baolu size -= n;
368dfba2174SLu Baolu }
369dfba2174SLu Baolu
370dfba2174SLu Baolu count = tty_insert_flip_string(&port->port, packet,
371dfba2174SLu Baolu size);
372dfba2174SLu Baolu if (count)
373dfba2174SLu Baolu do_push = true;
374dfba2174SLu Baolu if (count != size) {
375dfba2174SLu Baolu port->n_read += count;
376dfba2174SLu Baolu break;
377dfba2174SLu Baolu }
378dfba2174SLu Baolu port->n_read = 0;
379dfba2174SLu Baolu }
380dfba2174SLu Baolu
381dfba2174SLu Baolu list_move(&req->list_pool, &port->read_pool);
382dfba2174SLu Baolu }
383dfba2174SLu Baolu
384dfba2174SLu Baolu if (do_push)
385dfba2174SLu Baolu tty_flip_buffer_push(&port->port);
386dfba2174SLu Baolu
387dfba2174SLu Baolu if (!list_empty(queue) && tty) {
388dfba2174SLu Baolu if (!tty_throttled(tty)) {
389dfba2174SLu Baolu if (do_push)
390dfba2174SLu Baolu tasklet_schedule(&port->push);
391dfba2174SLu Baolu else
392dfba2174SLu Baolu pr_warn("ttyDBC0: RX not scheduled?\n");
393dfba2174SLu Baolu }
394dfba2174SLu Baolu }
395dfba2174SLu Baolu
396dfba2174SLu Baolu if (!disconnect)
397dfba2174SLu Baolu dbc_start_rx(port);
398dfba2174SLu Baolu
399a098dc8bSLu Baolu spin_unlock_irqrestore(&port->port_lock, flags);
400dfba2174SLu Baolu }
401dfba2174SLu Baolu
dbc_port_activate(struct tty_port * _port,struct tty_struct * tty)402dfba2174SLu Baolu static int dbc_port_activate(struct tty_port *_port, struct tty_struct *tty)
403dfba2174SLu Baolu {
404a098dc8bSLu Baolu unsigned long flags;
405dfba2174SLu Baolu struct dbc_port *port = container_of(_port, struct dbc_port, port);
406dfba2174SLu Baolu
407a098dc8bSLu Baolu spin_lock_irqsave(&port->port_lock, flags);
408dfba2174SLu Baolu dbc_start_rx(port);
409a098dc8bSLu Baolu spin_unlock_irqrestore(&port->port_lock, flags);
410dfba2174SLu Baolu
411dfba2174SLu Baolu return 0;
412dfba2174SLu Baolu }
413dfba2174SLu Baolu
414dfba2174SLu Baolu static const struct tty_port_operations dbc_port_ops = {
415dfba2174SLu Baolu .activate = dbc_port_activate,
416dfba2174SLu Baolu };
417dfba2174SLu Baolu
418dfba2174SLu Baolu static void
xhci_dbc_tty_init_port(struct xhci_dbc * dbc,struct dbc_port * port)41991aaf974SMathias Nyman xhci_dbc_tty_init_port(struct xhci_dbc *dbc, struct dbc_port *port)
420dfba2174SLu Baolu {
421dfba2174SLu Baolu tty_port_init(&port->port);
422dfba2174SLu Baolu spin_lock_init(&port->port_lock);
42381d324cdSAllen Pais tasklet_setup(&port->push, dbc_rx_push);
424dfba2174SLu Baolu INIT_LIST_HEAD(&port->read_pool);
425dfba2174SLu Baolu INIT_LIST_HEAD(&port->read_queue);
426dfba2174SLu Baolu INIT_LIST_HEAD(&port->write_pool);
427dfba2174SLu Baolu
428dfba2174SLu Baolu port->port.ops = &dbc_port_ops;
429dfba2174SLu Baolu port->n_read = 0;
430dfba2174SLu Baolu }
431dfba2174SLu Baolu
432dfba2174SLu Baolu static void
xhci_dbc_tty_exit_port(struct dbc_port * port)433dfba2174SLu Baolu xhci_dbc_tty_exit_port(struct dbc_port *port)
434dfba2174SLu Baolu {
435dfba2174SLu Baolu tasklet_kill(&port->push);
436dfba2174SLu Baolu tty_port_destroy(&port->port);
437dfba2174SLu Baolu }
438dfba2174SLu Baolu
xhci_dbc_tty_register_device(struct xhci_dbc * dbc)43925252919SWei Yongjun static int xhci_dbc_tty_register_device(struct xhci_dbc *dbc)
440dfba2174SLu Baolu {
441dfba2174SLu Baolu int ret;
442dfba2174SLu Baolu struct device *tty_dev;
4439a360a7cSMathias Nyman struct dbc_port *port = dbc_to_port(dbc);
444dfba2174SLu Baolu
445688915b1SMathias Nyman if (port->registered)
446688915b1SMathias Nyman return -EBUSY;
447688915b1SMathias Nyman
44891aaf974SMathias Nyman xhci_dbc_tty_init_port(dbc, port);
449dfba2174SLu Baolu
450e1ec140fSMathias Nyman mutex_lock(&dbc_tty_minors_lock);
451e1ec140fSMathias Nyman port->minor = idr_alloc(&dbc_tty_minors, port, 0, 64, GFP_KERNEL);
452e1ec140fSMathias Nyman mutex_unlock(&dbc_tty_minors_lock);
453e1ec140fSMathias Nyman
454e1ec140fSMathias Nyman if (port->minor < 0) {
455e1ec140fSMathias Nyman ret = port->minor;
456e1ec140fSMathias Nyman goto err_idr;
457e1ec140fSMathias Nyman }
458e1ec140fSMathias Nyman
4597cbcb40dSJiri Slaby (SUSE) ret = kfifo_alloc(&port->port.xmit_fifo, DBC_WRITE_BUF_SIZE,
4607cbcb40dSJiri Slaby (SUSE) GFP_KERNEL);
461dfba2174SLu Baolu if (ret)
462880de403SJohan Hovold goto err_exit_port;
463dfba2174SLu Baolu
464e0aa56dcSMathias Nyman ret = xhci_dbc_alloc_requests(dbc, BULK_IN, &port->read_pool,
465dfba2174SLu Baolu dbc_read_complete);
466dfba2174SLu Baolu if (ret)
467880de403SJohan Hovold goto err_free_fifo;
468dfba2174SLu Baolu
469e0aa56dcSMathias Nyman ret = xhci_dbc_alloc_requests(dbc, BULK_OUT, &port->write_pool,
470dfba2174SLu Baolu dbc_write_complete);
471dfba2174SLu Baolu if (ret)
472880de403SJohan Hovold goto err_free_requests;
473880de403SJohan Hovold
474880de403SJohan Hovold tty_dev = tty_port_register_device(&port->port,
475e1ec140fSMathias Nyman dbc_tty_driver, port->minor, NULL);
476880de403SJohan Hovold if (IS_ERR(tty_dev)) {
477880de403SJohan Hovold ret = PTR_ERR(tty_dev);
478880de403SJohan Hovold goto err_free_requests;
479880de403SJohan Hovold }
480dfba2174SLu Baolu
481dfba2174SLu Baolu port->registered = true;
482dfba2174SLu Baolu
483dfba2174SLu Baolu return 0;
484dfba2174SLu Baolu
485880de403SJohan Hovold err_free_requests:
486e0aa56dcSMathias Nyman xhci_dbc_free_requests(&port->read_pool);
487e0aa56dcSMathias Nyman xhci_dbc_free_requests(&port->write_pool);
488880de403SJohan Hovold err_free_fifo:
4897cbcb40dSJiri Slaby (SUSE) kfifo_free(&port->port.xmit_fifo);
490880de403SJohan Hovold err_exit_port:
491e1ec140fSMathias Nyman idr_remove(&dbc_tty_minors, port->minor);
492e1ec140fSMathias Nyman err_idr:
493dfba2174SLu Baolu xhci_dbc_tty_exit_port(port);
494dfba2174SLu Baolu
495b396fa39SMathias Nyman dev_err(dbc->dev, "can't register tty port, err %d\n", ret);
496dfba2174SLu Baolu
497dfba2174SLu Baolu return ret;
498dfba2174SLu Baolu }
499dfba2174SLu Baolu
xhci_dbc_tty_unregister_device(struct xhci_dbc * dbc)50025252919SWei Yongjun static void xhci_dbc_tty_unregister_device(struct xhci_dbc *dbc)
501dfba2174SLu Baolu {
5029a360a7cSMathias Nyman struct dbc_port *port = dbc_to_port(dbc);
503dfba2174SLu Baolu
504688915b1SMathias Nyman if (!port->registered)
505688915b1SMathias Nyman return;
506e1ec140fSMathias Nyman tty_unregister_device(dbc_tty_driver, port->minor);
507dfba2174SLu Baolu xhci_dbc_tty_exit_port(port);
508dfba2174SLu Baolu port->registered = false;
509dfba2174SLu Baolu
510e1ec140fSMathias Nyman mutex_lock(&dbc_tty_minors_lock);
511e1ec140fSMathias Nyman idr_remove(&dbc_tty_minors, port->minor);
512e1ec140fSMathias Nyman mutex_unlock(&dbc_tty_minors_lock);
513e1ec140fSMathias Nyman
5147cbcb40dSJiri Slaby (SUSE) kfifo_free(&port->port.xmit_fifo);
515e0aa56dcSMathias Nyman xhci_dbc_free_requests(&port->read_pool);
516e0aa56dcSMathias Nyman xhci_dbc_free_requests(&port->read_queue);
517e0aa56dcSMathias Nyman xhci_dbc_free_requests(&port->write_pool);
518dfba2174SLu Baolu }
5194521f161SMathias Nyman
5206ae6470bSMathias Nyman static const struct dbc_driver dbc_driver = {
5216ae6470bSMathias Nyman .configure = xhci_dbc_tty_register_device,
5226ae6470bSMathias Nyman .disconnect = xhci_dbc_tty_unregister_device,
5236ae6470bSMathias Nyman };
5246ae6470bSMathias Nyman
xhci_dbc_tty_probe(struct device * dev,void __iomem * base,struct xhci_hcd * xhci)5255ce036b9SMathias Nyman int xhci_dbc_tty_probe(struct device *dev, void __iomem *base, struct xhci_hcd *xhci)
5264521f161SMathias Nyman {
5275ce036b9SMathias Nyman struct xhci_dbc *dbc;
5289a360a7cSMathias Nyman struct dbc_port *port;
5294521f161SMathias Nyman int status;
5304521f161SMathias Nyman
5316aec5000SMathias Nyman if (!dbc_tty_driver)
5326aec5000SMathias Nyman return -ENODEV;
5334521f161SMathias Nyman
5349a360a7cSMathias Nyman port = kzalloc(sizeof(*port), GFP_KERNEL);
5356aec5000SMathias Nyman if (!port)
5366aec5000SMathias Nyman return -ENOMEM;
5376ae6470bSMathias Nyman
5385ce036b9SMathias Nyman dbc = xhci_alloc_dbc(dev, base, &dbc_driver);
539e1ec140fSMathias Nyman
5405ce036b9SMathias Nyman if (!dbc) {
5415ce036b9SMathias Nyman status = -ENOMEM;
5425ce036b9SMathias Nyman goto out2;
5435ce036b9SMathias Nyman }
5445ce036b9SMathias Nyman
5455ce036b9SMathias Nyman dbc->priv = port;
5465ce036b9SMathias Nyman
5475ce036b9SMathias Nyman /* get rid of xhci once this is a real driver binding to a device */
5485ce036b9SMathias Nyman xhci->dbc = dbc;
5495ce036b9SMathias Nyman
5504521f161SMathias Nyman return 0;
5515ce036b9SMathias Nyman out2:
5525ce036b9SMathias Nyman kfree(port);
5536aec5000SMathias Nyman
5544521f161SMathias Nyman return status;
5554521f161SMathias Nyman }
5564521f161SMathias Nyman
5574521f161SMathias Nyman /*
5584521f161SMathias Nyman * undo what probe did, assume dbc is stopped already.
5594521f161SMathias Nyman * we also assume tty_unregister_device() is called before this
5604521f161SMathias Nyman */
xhci_dbc_tty_remove(struct xhci_dbc * dbc)5614521f161SMathias Nyman void xhci_dbc_tty_remove(struct xhci_dbc *dbc)
5624521f161SMathias Nyman {
5639a360a7cSMathias Nyman struct dbc_port *port = dbc_to_port(dbc);
5649a360a7cSMathias Nyman
5655ce036b9SMathias Nyman xhci_dbc_remove(dbc);
5669a360a7cSMathias Nyman kfree(port);
5674521f161SMathias Nyman }
5684521f161SMathias Nyman
dbc_tty_init(void)5696aec5000SMathias Nyman int dbc_tty_init(void)
5704521f161SMathias Nyman {
5714521f161SMathias Nyman int ret;
5724521f161SMathias Nyman
573e1ec140fSMathias Nyman idr_init(&dbc_tty_minors);
574e1ec140fSMathias Nyman
575e1ec140fSMathias Nyman dbc_tty_driver = tty_alloc_driver(64, TTY_DRIVER_REAL_RAW |
5764521f161SMathias Nyman TTY_DRIVER_DYNAMIC_DEV);
577e1ec140fSMathias Nyman if (IS_ERR(dbc_tty_driver)) {
578e1ec140fSMathias Nyman idr_destroy(&dbc_tty_minors);
5794521f161SMathias Nyman return PTR_ERR(dbc_tty_driver);
580e1ec140fSMathias Nyman }
5814521f161SMathias Nyman
5824521f161SMathias Nyman dbc_tty_driver->driver_name = "dbc_serial";
5834521f161SMathias Nyman dbc_tty_driver->name = "ttyDBC";
5844521f161SMathias Nyman
5854521f161SMathias Nyman dbc_tty_driver->type = TTY_DRIVER_TYPE_SERIAL;
5864521f161SMathias Nyman dbc_tty_driver->subtype = SERIAL_TYPE_NORMAL;
5874521f161SMathias Nyman dbc_tty_driver->init_termios = tty_std_termios;
5884521f161SMathias Nyman dbc_tty_driver->init_termios.c_cflag =
5894521f161SMathias Nyman B9600 | CS8 | CREAD | HUPCL | CLOCAL;
5904521f161SMathias Nyman dbc_tty_driver->init_termios.c_ispeed = 9600;
5914521f161SMathias Nyman dbc_tty_driver->init_termios.c_ospeed = 9600;
5924521f161SMathias Nyman
5934521f161SMathias Nyman tty_set_operations(dbc_tty_driver, &dbc_tty_ops);
5944521f161SMathias Nyman
5954521f161SMathias Nyman ret = tty_register_driver(dbc_tty_driver);
5964521f161SMathias Nyman if (ret) {
5974521f161SMathias Nyman pr_err("Can't register dbc tty driver\n");
5989f90a4ddSJiri Slaby tty_driver_kref_put(dbc_tty_driver);
599e1ec140fSMathias Nyman idr_destroy(&dbc_tty_minors);
6004521f161SMathias Nyman }
601e1ec140fSMathias Nyman
6024521f161SMathias Nyman return ret;
6034521f161SMathias Nyman }
6044521f161SMathias Nyman
dbc_tty_exit(void)6056aec5000SMathias Nyman void dbc_tty_exit(void)
6064521f161SMathias Nyman {
6074521f161SMathias Nyman if (dbc_tty_driver) {
6084521f161SMathias Nyman tty_unregister_driver(dbc_tty_driver);
6099f90a4ddSJiri Slaby tty_driver_kref_put(dbc_tty_driver);
6104521f161SMathias Nyman dbc_tty_driver = NULL;
6114521f161SMathias Nyman }
612e1ec140fSMathias Nyman
613e1ec140fSMathias Nyman idr_destroy(&dbc_tty_minors);
6144521f161SMathias Nyman }
615