xref: /openbmc/linux/drivers/usb/usbip/usbip_event.c (revision bb7871ad99ea814565c3d6b551e039c71f24cbb3)
196c27377SValentina Manea /*
296c27377SValentina Manea  * Copyright (C) 2003-2008 Takahiro Hirofuchi
3*bb7871adSNobuo Iwata  * Copyright (C) 2015 Nobuo Iwata
496c27377SValentina Manea  *
596c27377SValentina Manea  * This is free software; you can redistribute it and/or modify
696c27377SValentina Manea  * it under the terms of the GNU General Public License as published by
796c27377SValentina Manea  * the Free Software Foundation; either version 2 of the License, or
896c27377SValentina Manea  * (at your option) any later version.
996c27377SValentina Manea  *
1096c27377SValentina Manea  * This is distributed in the hope that it will be useful,
1196c27377SValentina Manea  * but WITHOUT ANY WARRANTY; without even the implied warranty of
1296c27377SValentina Manea  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1396c27377SValentina Manea  * GNU General Public License for more details.
1496c27377SValentina Manea  *
1596c27377SValentina Manea  * You should have received a copy of the GNU General Public License
1696c27377SValentina Manea  * along with this program; if not, write to the Free Software
1796c27377SValentina Manea  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
1896c27377SValentina Manea  * USA.
1996c27377SValentina Manea  */
2096c27377SValentina Manea 
2196c27377SValentina Manea #include <linux/kthread.h>
2296c27377SValentina Manea #include <linux/export.h>
23*bb7871adSNobuo Iwata #include <linux/slab.h>
24*bb7871adSNobuo Iwata #include <linux/workqueue.h>
2596c27377SValentina Manea 
2696c27377SValentina Manea #include "usbip_common.h"
2796c27377SValentina Manea 
28*bb7871adSNobuo Iwata struct usbip_event {
29*bb7871adSNobuo Iwata 	struct list_head node;
30*bb7871adSNobuo Iwata 	struct usbip_device *ud;
31*bb7871adSNobuo Iwata };
3296c27377SValentina Manea 
33*bb7871adSNobuo Iwata static DEFINE_SPINLOCK(event_lock);
34*bb7871adSNobuo Iwata static LIST_HEAD(event_list);
35*bb7871adSNobuo Iwata 
36*bb7871adSNobuo Iwata static void set_event(struct usbip_device *ud, unsigned long event)
37*bb7871adSNobuo Iwata {
38*bb7871adSNobuo Iwata 	unsigned long flags;
39*bb7871adSNobuo Iwata 
40*bb7871adSNobuo Iwata 	spin_lock_irqsave(&ud->lock, flags);
41*bb7871adSNobuo Iwata 	ud->event |= event;
42*bb7871adSNobuo Iwata 	spin_unlock_irqrestore(&ud->lock, flags);
43*bb7871adSNobuo Iwata }
44*bb7871adSNobuo Iwata 
45*bb7871adSNobuo Iwata static void unset_event(struct usbip_device *ud, unsigned long event)
46*bb7871adSNobuo Iwata {
47*bb7871adSNobuo Iwata 	unsigned long flags;
48*bb7871adSNobuo Iwata 
49*bb7871adSNobuo Iwata 	spin_lock_irqsave(&ud->lock, flags);
50*bb7871adSNobuo Iwata 	ud->event &= ~event;
51*bb7871adSNobuo Iwata 	spin_unlock_irqrestore(&ud->lock, flags);
52*bb7871adSNobuo Iwata }
53*bb7871adSNobuo Iwata 
54*bb7871adSNobuo Iwata static struct usbip_device *get_event(void)
55*bb7871adSNobuo Iwata {
56*bb7871adSNobuo Iwata 	struct usbip_event *ue = NULL;
57*bb7871adSNobuo Iwata 	struct usbip_device *ud = NULL;
58*bb7871adSNobuo Iwata 	unsigned long flags;
59*bb7871adSNobuo Iwata 
60*bb7871adSNobuo Iwata 	spin_lock_irqsave(&event_lock, flags);
61*bb7871adSNobuo Iwata 	if (!list_empty(&event_list)) {
62*bb7871adSNobuo Iwata 		ue = list_first_entry(&event_list, struct usbip_event, node);
63*bb7871adSNobuo Iwata 		list_del(&ue->node);
64*bb7871adSNobuo Iwata 	}
65*bb7871adSNobuo Iwata 	spin_unlock_irqrestore(&event_lock, flags);
66*bb7871adSNobuo Iwata 
67*bb7871adSNobuo Iwata 	if (ue) {
68*bb7871adSNobuo Iwata 		ud = ue->ud;
69*bb7871adSNobuo Iwata 		kfree(ue);
70*bb7871adSNobuo Iwata 	}
71*bb7871adSNobuo Iwata 	return ud;
72*bb7871adSNobuo Iwata }
73*bb7871adSNobuo Iwata 
74*bb7871adSNobuo Iwata static struct task_struct *worker_context;
75*bb7871adSNobuo Iwata 
76*bb7871adSNobuo Iwata static void event_handler(struct work_struct *work)
77*bb7871adSNobuo Iwata {
78*bb7871adSNobuo Iwata 	struct usbip_device *ud;
79*bb7871adSNobuo Iwata 
80*bb7871adSNobuo Iwata 	if (worker_context == NULL) {
81*bb7871adSNobuo Iwata 		worker_context = current;
82*bb7871adSNobuo Iwata 	}
83*bb7871adSNobuo Iwata 
84*bb7871adSNobuo Iwata 	while ((ud = get_event()) != NULL) {
8596c27377SValentina Manea 		usbip_dbg_eh("pending event %lx\n", ud->event);
8696c27377SValentina Manea 
8796c27377SValentina Manea 		/*
8896c27377SValentina Manea 		 * NOTE: shutdown must come first.
8996c27377SValentina Manea 		 * Shutdown the device.
9096c27377SValentina Manea 		 */
9196c27377SValentina Manea 		if (ud->event & USBIP_EH_SHUTDOWN) {
9296c27377SValentina Manea 			ud->eh_ops.shutdown(ud);
93*bb7871adSNobuo Iwata 			unset_event(ud, USBIP_EH_SHUTDOWN);
9496c27377SValentina Manea 		}
9596c27377SValentina Manea 
9696c27377SValentina Manea 		/* Reset the device. */
9796c27377SValentina Manea 		if (ud->event & USBIP_EH_RESET) {
9896c27377SValentina Manea 			ud->eh_ops.reset(ud);
99*bb7871adSNobuo Iwata 			unset_event(ud, USBIP_EH_RESET);
10096c27377SValentina Manea 		}
10196c27377SValentina Manea 
10296c27377SValentina Manea 		/* Mark the device as unusable. */
10396c27377SValentina Manea 		if (ud->event & USBIP_EH_UNUSABLE) {
10496c27377SValentina Manea 			ud->eh_ops.unusable(ud);
105*bb7871adSNobuo Iwata 			unset_event(ud, USBIP_EH_UNUSABLE);
10696c27377SValentina Manea 		}
10796c27377SValentina Manea 
10896c27377SValentina Manea 		/* Stop the error handler. */
10996c27377SValentina Manea 		if (ud->event & USBIP_EH_BYE)
110*bb7871adSNobuo Iwata 			usbip_dbg_eh("removed %p\n", ud);
111*bb7871adSNobuo Iwata 
112*bb7871adSNobuo Iwata 		wake_up(&ud->eh_waitq);
11396c27377SValentina Manea 	}
11496c27377SValentina Manea }
11596c27377SValentina Manea 
11696c27377SValentina Manea int usbip_start_eh(struct usbip_device *ud)
11796c27377SValentina Manea {
11896c27377SValentina Manea 	init_waitqueue_head(&ud->eh_waitq);
11996c27377SValentina Manea 	ud->event = 0;
12096c27377SValentina Manea 	return 0;
12196c27377SValentina Manea }
12296c27377SValentina Manea EXPORT_SYMBOL_GPL(usbip_start_eh);
12396c27377SValentina Manea 
12496c27377SValentina Manea void usbip_stop_eh(struct usbip_device *ud)
12596c27377SValentina Manea {
126*bb7871adSNobuo Iwata 	unsigned long pending = ud->event & ~USBIP_EH_BYE;
12796c27377SValentina Manea 
128*bb7871adSNobuo Iwata 	if (!(ud->event & USBIP_EH_BYE))
129*bb7871adSNobuo Iwata 		usbip_dbg_eh("usbip_eh stopping but not removed\n");
130*bb7871adSNobuo Iwata 
131*bb7871adSNobuo Iwata 	if (pending)
132*bb7871adSNobuo Iwata 		usbip_dbg_eh("usbip_eh waiting completion %lx\n", pending);
133*bb7871adSNobuo Iwata 
134*bb7871adSNobuo Iwata 	wait_event_interruptible(ud->eh_waitq, !(ud->event & ~USBIP_EH_BYE));
135*bb7871adSNobuo Iwata 	usbip_dbg_eh("usbip_eh has stopped\n");
13696c27377SValentina Manea }
13796c27377SValentina Manea EXPORT_SYMBOL_GPL(usbip_stop_eh);
13896c27377SValentina Manea 
139*bb7871adSNobuo Iwata #define WORK_QUEUE_NAME "usbip_event"
140*bb7871adSNobuo Iwata 
141*bb7871adSNobuo Iwata static struct workqueue_struct *usbip_queue;
142*bb7871adSNobuo Iwata static DECLARE_WORK(usbip_work, event_handler);
143*bb7871adSNobuo Iwata 
144*bb7871adSNobuo Iwata int usbip_init_eh(void)
145*bb7871adSNobuo Iwata {
146*bb7871adSNobuo Iwata 	usbip_queue = create_singlethread_workqueue(WORK_QUEUE_NAME);
147*bb7871adSNobuo Iwata 	if (usbip_queue == NULL) {
148*bb7871adSNobuo Iwata 		pr_err("failed to create usbip_event\n");
149*bb7871adSNobuo Iwata 		return -ENOMEM;
150*bb7871adSNobuo Iwata 	}
151*bb7871adSNobuo Iwata 	return 0;
152*bb7871adSNobuo Iwata }
153*bb7871adSNobuo Iwata 
154*bb7871adSNobuo Iwata void usbip_finish_eh(void)
155*bb7871adSNobuo Iwata {
156*bb7871adSNobuo Iwata 	flush_workqueue(usbip_queue);
157*bb7871adSNobuo Iwata 	destroy_workqueue(usbip_queue);
158*bb7871adSNobuo Iwata 	usbip_queue = NULL;
159*bb7871adSNobuo Iwata }
160*bb7871adSNobuo Iwata 
16196c27377SValentina Manea void usbip_event_add(struct usbip_device *ud, unsigned long event)
16296c27377SValentina Manea {
163*bb7871adSNobuo Iwata 	struct usbip_event *ue;
16496c27377SValentina Manea 	unsigned long flags;
16596c27377SValentina Manea 
166*bb7871adSNobuo Iwata 	if (ud->event & USBIP_EH_BYE)
167*bb7871adSNobuo Iwata 		return;
168*bb7871adSNobuo Iwata 
169*bb7871adSNobuo Iwata 	set_event(ud, event);
170*bb7871adSNobuo Iwata 
171*bb7871adSNobuo Iwata 	spin_lock_irqsave(&event_lock, flags);
172*bb7871adSNobuo Iwata 
173*bb7871adSNobuo Iwata 	list_for_each_entry_reverse(ue, &event_list, node) {
174*bb7871adSNobuo Iwata 		if (ue->ud == ud)
175*bb7871adSNobuo Iwata 			goto out;
176*bb7871adSNobuo Iwata 	}
177*bb7871adSNobuo Iwata 
178*bb7871adSNobuo Iwata 	ue = kmalloc(sizeof(struct usbip_event), GFP_ATOMIC);
179*bb7871adSNobuo Iwata 	if (ue == NULL)
180*bb7871adSNobuo Iwata 		goto out;
181*bb7871adSNobuo Iwata 
182*bb7871adSNobuo Iwata 	ue->ud = ud;
183*bb7871adSNobuo Iwata 
184*bb7871adSNobuo Iwata 	list_add_tail(&ue->node, &event_list);
185*bb7871adSNobuo Iwata 	queue_work(usbip_queue, &usbip_work);
186*bb7871adSNobuo Iwata 
187*bb7871adSNobuo Iwata out:
188*bb7871adSNobuo Iwata 	spin_unlock_irqrestore(&event_lock, flags);
18996c27377SValentina Manea }
19096c27377SValentina Manea EXPORT_SYMBOL_GPL(usbip_event_add);
19196c27377SValentina Manea 
19296c27377SValentina Manea int usbip_event_happened(struct usbip_device *ud)
19396c27377SValentina Manea {
19496c27377SValentina Manea 	int happened = 0;
19521619792SAndrew Goodbody 	unsigned long flags;
19696c27377SValentina Manea 
19721619792SAndrew Goodbody 	spin_lock_irqsave(&ud->lock, flags);
19896c27377SValentina Manea 	if (ud->event != 0)
19996c27377SValentina Manea 		happened = 1;
20021619792SAndrew Goodbody 	spin_unlock_irqrestore(&ud->lock, flags);
20196c27377SValentina Manea 
20296c27377SValentina Manea 	return happened;
20396c27377SValentina Manea }
20496c27377SValentina Manea EXPORT_SYMBOL_GPL(usbip_event_happened);
205*bb7871adSNobuo Iwata 
206*bb7871adSNobuo Iwata int usbip_in_eh(struct task_struct *task)
207*bb7871adSNobuo Iwata {
208*bb7871adSNobuo Iwata 	if (task == worker_context)
209*bb7871adSNobuo Iwata 		return 1;
210*bb7871adSNobuo Iwata 
211*bb7871adSNobuo Iwata 	return 0;
212*bb7871adSNobuo Iwata }
213*bb7871adSNobuo Iwata EXPORT_SYMBOL_GPL(usbip_in_eh);
214