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