summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/usb/gadget/function/f_accessory.c38
1 files changed, 32 insertions, 6 deletions
diff --git a/drivers/usb/gadget/function/f_accessory.c b/drivers/usb/gadget/function/f_accessory.c
index 465826b803ff..ce96cd060bfa 100644
--- a/drivers/usb/gadget/function/f_accessory.c
+++ b/drivers/usb/gadget/function/f_accessory.c
@@ -27,6 +27,7 @@
#include <linux/interrupt.h>
#include <linux/kthread.h>
#include <linux/freezer.h>
+#include <linux/kref.h>
#include <linux/types.h>
#include <linux/file.h>
@@ -73,6 +74,7 @@ struct acc_dev {
struct usb_function function;
struct usb_composite_dev *cdev;
spinlock_t lock;
+ struct acc_dev_ref *ref;
struct usb_ep *ep_in;
struct usb_ep *ep_out;
@@ -199,7 +201,14 @@ static struct usb_gadget_strings *acc_strings[] = {
NULL,
};
-static struct acc_dev *_acc_dev;
+struct acc_dev_ref {
+ struct kref kref;
+ struct acc_dev *acc_dev;
+};
+
+static struct acc_dev_ref _acc_dev_ref = {
+ .kref = KREF_INIT(0),
+};
struct acc_instance {
struct usb_function_instance func_inst;
@@ -208,11 +217,26 @@ struct acc_instance {
static struct acc_dev *get_acc_dev(void)
{
- return _acc_dev;
+ struct acc_dev_ref *ref = &_acc_dev_ref;
+
+ return kref_get_unless_zero(&ref->kref) ? ref->acc_dev : NULL;
+}
+
+static void __put_acc_dev(struct kref *kref)
+{
+ struct acc_dev_ref *ref = container_of(kref, struct acc_dev_ref, kref);
+ struct acc_dev *dev = ref->acc_dev;
+
+ ref->acc_dev = NULL;
+ kfree(dev);
}
static void put_acc_dev(struct acc_dev *dev)
{
+ struct acc_dev_ref *ref = dev->ref;
+
+ WARN_ON(ref->acc_dev != dev);
+ kref_put(&ref->kref, __put_acc_dev);
}
static inline struct acc_dev *func_to_dev(struct usb_function *f)
@@ -1231,6 +1255,7 @@ static void acc_function_disable(struct usb_function *f)
static int acc_setup(void)
{
+ struct acc_dev_ref *ref = &_acc_dev_ref;
struct acc_dev *dev;
int ret;
@@ -1249,7 +1274,9 @@ static int acc_setup(void)
INIT_DELAYED_WORK(&dev->start_work, acc_start_work);
INIT_WORK(&dev->hid_work, acc_hid_work);
- _acc_dev = dev;
+ dev->ref = ref;
+ kref_init(&ref->kref);
+ ref->acc_dev = dev;
ret = misc_register(&acc_device);
if (ret)
@@ -1277,12 +1304,11 @@ EXPORT_SYMBOL_GPL(acc_disconnect);
static void acc_cleanup(void)
{
- struct acc_dev *dev = _acc_dev;
+ struct acc_dev *dev = get_acc_dev();
misc_deregister(&acc_device);
put_acc_dev(dev);
- kfree(dev);
- _acc_dev = NULL;
+ put_acc_dev(dev); /* Pairs with kref_init() in acc_setup() */
}
static struct acc_instance *to_acc_instance(struct config_item *item)
{