Loading drivers/usb/gadget/function/f_accessory.c +107 −40 Original line number Diff line number Diff line Loading @@ -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> Loading Loading @@ -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; Loading @@ -80,13 +82,13 @@ struct acc_dev { /* online indicates state of function_set_alt & function_unbind * set to 1 when we connect */ int online:1; int online; /* disconnected indicates state of open & release * Set to 1 when we disconnect. * Not cleared until our file is closed. */ int disconnected:1; int disconnected; /* strings sent by the host */ char manufacturer[ACC_STRING_SIZE]; Loading Loading @@ -243,14 +245,48 @@ static struct usb_gadget_strings *acc_strings[] = { NULL, }; /* temporary variable used between acc_open() and acc_gadget_bind() */ 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; const char *name; }; static struct acc_dev *get_acc_dev(void) { 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; /* Cancel any async work */ cancel_delayed_work_sync(&dev->start_work); cancel_work_sync(&dev->hid_work); 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) { return container_of(f, struct acc_dev, function); Loading Loading @@ -316,7 +352,10 @@ static void acc_set_disconnected(struct acc_dev *dev) static void acc_complete_in(struct usb_ep *ep, struct usb_request *req) { struct acc_dev *dev = _acc_dev; struct acc_dev *dev = get_acc_dev(); if (!dev) return; if (req->status == -ESHUTDOWN) { pr_debug("acc_complete_in set disconnected"); Loading @@ -326,11 +365,15 @@ static void acc_complete_in(struct usb_ep *ep, struct usb_request *req) req_put(dev, &dev->tx_idle, req); wake_up(&dev->write_wq); put_acc_dev(dev); } static void acc_complete_out(struct usb_ep *ep, struct usb_request *req) { struct acc_dev *dev = _acc_dev; struct acc_dev *dev = get_acc_dev(); if (!dev) return; dev->rx_done = 1; if (req->status == -ESHUTDOWN) { Loading @@ -339,6 +382,7 @@ static void acc_complete_out(struct usb_ep *ep, struct usb_request *req) } wake_up(&dev->read_wq); put_acc_dev(dev); } static void acc_complete_set_string(struct usb_ep *ep, struct usb_request *req) Loading Loading @@ -803,24 +847,36 @@ static long acc_ioctl(struct file *fp, unsigned code, unsigned long value) static int acc_open(struct inode *ip, struct file *fp) { printk(KERN_INFO "acc_open\n"); if (atomic_xchg(&_acc_dev->open_excl, 1)) struct acc_dev *dev = get_acc_dev(); if (!dev) return -ENODEV; if (atomic_xchg(&dev->open_excl, 1)) { put_acc_dev(dev); return -EBUSY; } _acc_dev->disconnected = 0; fp->private_data = _acc_dev; dev->disconnected = 0; fp->private_data = dev; return 0; } static int acc_release(struct inode *ip, struct file *fp) { printk(KERN_INFO "acc_release\n"); struct acc_dev *dev = fp->private_data; if (!dev) return -ENOENT; WARN_ON(!atomic_xchg(&_acc_dev->open_excl, 0)); /* indicate that we are disconnected * still could be online so don't touch online flag */ _acc_dev->disconnected = 1; dev->disconnected = 1; fp->private_data = NULL; WARN_ON(!atomic_xchg(&dev->open_excl, 0)); put_acc_dev(dev); return 0; } Loading Loading @@ -876,7 +932,7 @@ static void acc_complete_setup_noop(struct usb_ep *ep, struct usb_request *req) int acc_ctrlrequest(struct usb_composite_dev *cdev, const struct usb_ctrlrequest *ctrl) { struct acc_dev *dev = _acc_dev; struct acc_dev *dev = get_acc_dev(); int value = -EOPNOTSUPP; struct acc_hid_dev *hid; int offset; Loading @@ -893,12 +949,6 @@ int acc_ctrlrequest(struct usb_composite_dev *cdev, */ if (!dev) return -ENODEV; /* * printk(KERN_INFO "acc_ctrlrequest " * "%02x.%02x v%04x i%04x l%u\n", * b_requestType, b_request, * w_value, w_index, w_length); */ if (b_requestType == (USB_DIR_OUT | USB_TYPE_VENDOR)) { if (b_request == ACCESSORY_START) { Loading Loading @@ -987,6 +1037,7 @@ err: "%02x.%02x v%04x i%04x l%u\n", ctrl->bRequestType, ctrl->bRequest, w_value, w_index, w_length); put_acc_dev(dev); return value; } EXPORT_SYMBOL_GPL(acc_ctrlrequest); Loading Loading @@ -1065,10 +1116,6 @@ kill_all_hid_devices(struct acc_dev *dev) struct list_head *entry, *temp; unsigned long flags; /* do nothing if usb accessory device doesn't exist */ if (!dev) return; spin_lock_irqsave(&dev->lock, flags); list_for_each_safe(entry, temp, &dev->hid_list) { hid = list_entry(entry, struct acc_hid_dev, list); Loading Loading @@ -1153,12 +1200,15 @@ static void acc_hid_delete(struct acc_hid_dev *hid) static void acc_hid_work(struct work_struct *data) { struct acc_dev *dev = _acc_dev; struct acc_dev *dev = get_acc_dev(); struct list_head *entry, *temp; struct acc_hid_dev *hid; struct list_head new_list, dead_list; unsigned long flags; if (!dev) return; INIT_LIST_HEAD(&new_list); spin_lock_irqsave(&dev->lock, flags); Loading Loading @@ -1204,6 +1254,8 @@ static void acc_hid_work(struct work_struct *data) hid_destroy_device(hid->hid); acc_hid_delete(hid); } put_acc_dev(dev); } static int acc_function_set_alt(struct usb_function *f, Loading Loading @@ -1260,9 +1312,13 @@ 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; if (kref_read(&ref->kref)) return -EBUSY; dev = kzalloc(sizeof(*dev), GFP_KERNEL); if (!dev) return -ENOMEM; Loading @@ -1278,16 +1334,22 @@ static int acc_setup(void) INIT_DELAYED_WORK(&dev->start_work, acc_start_work); INIT_WORK(&dev->hid_work, acc_hid_work); dev->ref = ref; if (cmpxchg_relaxed(&ref->acc_dev, NULL, dev)) { ret = -EBUSY; goto err_free_dev; } ret = misc_register(&acc_device); if (ret) goto err; /* _acc_dev must be set before calling usb_gadget_register_driver */ _acc_dev = dev; goto err_zap_ptr; kref_init(&ref->kref); return 0; err: err_zap_ptr: ref->acc_dev = NULL; err_free_dev: kfree(dev); pr_err("USB accessory gadget driver failed to initialize\n"); return ret; Loading @@ -1295,16 +1357,24 @@ err: void acc_disconnect(void) { struct acc_dev *dev = get_acc_dev(); if (!dev) return; /* unregister all HID devices if USB is disconnected */ kill_all_hid_devices(_acc_dev); kill_all_hid_devices(dev); put_acc_dev(dev); } EXPORT_SYMBOL_GPL(acc_disconnect); static void acc_cleanup(void) { struct acc_dev *dev = get_acc_dev(); misc_deregister(&acc_device); kfree(_acc_dev); _acc_dev = NULL; put_acc_dev(dev); put_acc_dev(dev); /* Pairs with kref_init() in acc_setup() */ } static struct acc_instance *to_acc_instance(struct config_item *item) { Loading Loading @@ -1364,7 +1434,6 @@ static void acc_free_inst(struct usb_function_instance *fi) static struct usb_function_instance *acc_alloc_inst(void) { struct acc_instance *fi_acc; struct acc_dev *dev; int err; fi_acc = kzalloc(sizeof(*fi_acc), GFP_KERNEL); Loading @@ -1376,19 +1445,19 @@ static struct usb_function_instance *acc_alloc_inst(void) err = acc_setup(); if (err) { kfree(fi_acc); pr_err("Error setting ACCESSORY\n"); return ERR_PTR(err); } config_group_init_type_name(&fi_acc->func_inst.group, "", &acc_func_type); dev = _acc_dev; return &fi_acc->func_inst; } static void acc_free(struct usb_function *f) { /*NO-OP: no function specific resource allocation in mtp_alloc*/ struct acc_dev *dev = func_to_dev(f); put_acc_dev(dev); } int acc_ctrlrequest_configfs(struct usb_function *f, Loading @@ -1401,9 +1470,7 @@ int acc_ctrlrequest_configfs(struct usb_function *f, static struct usb_function *acc_alloc(struct usb_function_instance *fi) { struct acc_dev *dev = _acc_dev; pr_info("acc_alloc\n"); struct acc_dev *dev = get_acc_dev(); dev->function.name = "accessory"; dev->function.strings = acc_strings, Loading Loading
drivers/usb/gadget/function/f_accessory.c +107 −40 Original line number Diff line number Diff line Loading @@ -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> Loading Loading @@ -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; Loading @@ -80,13 +82,13 @@ struct acc_dev { /* online indicates state of function_set_alt & function_unbind * set to 1 when we connect */ int online:1; int online; /* disconnected indicates state of open & release * Set to 1 when we disconnect. * Not cleared until our file is closed. */ int disconnected:1; int disconnected; /* strings sent by the host */ char manufacturer[ACC_STRING_SIZE]; Loading Loading @@ -243,14 +245,48 @@ static struct usb_gadget_strings *acc_strings[] = { NULL, }; /* temporary variable used between acc_open() and acc_gadget_bind() */ 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; const char *name; }; static struct acc_dev *get_acc_dev(void) { 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; /* Cancel any async work */ cancel_delayed_work_sync(&dev->start_work); cancel_work_sync(&dev->hid_work); 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) { return container_of(f, struct acc_dev, function); Loading Loading @@ -316,7 +352,10 @@ static void acc_set_disconnected(struct acc_dev *dev) static void acc_complete_in(struct usb_ep *ep, struct usb_request *req) { struct acc_dev *dev = _acc_dev; struct acc_dev *dev = get_acc_dev(); if (!dev) return; if (req->status == -ESHUTDOWN) { pr_debug("acc_complete_in set disconnected"); Loading @@ -326,11 +365,15 @@ static void acc_complete_in(struct usb_ep *ep, struct usb_request *req) req_put(dev, &dev->tx_idle, req); wake_up(&dev->write_wq); put_acc_dev(dev); } static void acc_complete_out(struct usb_ep *ep, struct usb_request *req) { struct acc_dev *dev = _acc_dev; struct acc_dev *dev = get_acc_dev(); if (!dev) return; dev->rx_done = 1; if (req->status == -ESHUTDOWN) { Loading @@ -339,6 +382,7 @@ static void acc_complete_out(struct usb_ep *ep, struct usb_request *req) } wake_up(&dev->read_wq); put_acc_dev(dev); } static void acc_complete_set_string(struct usb_ep *ep, struct usb_request *req) Loading Loading @@ -803,24 +847,36 @@ static long acc_ioctl(struct file *fp, unsigned code, unsigned long value) static int acc_open(struct inode *ip, struct file *fp) { printk(KERN_INFO "acc_open\n"); if (atomic_xchg(&_acc_dev->open_excl, 1)) struct acc_dev *dev = get_acc_dev(); if (!dev) return -ENODEV; if (atomic_xchg(&dev->open_excl, 1)) { put_acc_dev(dev); return -EBUSY; } _acc_dev->disconnected = 0; fp->private_data = _acc_dev; dev->disconnected = 0; fp->private_data = dev; return 0; } static int acc_release(struct inode *ip, struct file *fp) { printk(KERN_INFO "acc_release\n"); struct acc_dev *dev = fp->private_data; if (!dev) return -ENOENT; WARN_ON(!atomic_xchg(&_acc_dev->open_excl, 0)); /* indicate that we are disconnected * still could be online so don't touch online flag */ _acc_dev->disconnected = 1; dev->disconnected = 1; fp->private_data = NULL; WARN_ON(!atomic_xchg(&dev->open_excl, 0)); put_acc_dev(dev); return 0; } Loading Loading @@ -876,7 +932,7 @@ static void acc_complete_setup_noop(struct usb_ep *ep, struct usb_request *req) int acc_ctrlrequest(struct usb_composite_dev *cdev, const struct usb_ctrlrequest *ctrl) { struct acc_dev *dev = _acc_dev; struct acc_dev *dev = get_acc_dev(); int value = -EOPNOTSUPP; struct acc_hid_dev *hid; int offset; Loading @@ -893,12 +949,6 @@ int acc_ctrlrequest(struct usb_composite_dev *cdev, */ if (!dev) return -ENODEV; /* * printk(KERN_INFO "acc_ctrlrequest " * "%02x.%02x v%04x i%04x l%u\n", * b_requestType, b_request, * w_value, w_index, w_length); */ if (b_requestType == (USB_DIR_OUT | USB_TYPE_VENDOR)) { if (b_request == ACCESSORY_START) { Loading Loading @@ -987,6 +1037,7 @@ err: "%02x.%02x v%04x i%04x l%u\n", ctrl->bRequestType, ctrl->bRequest, w_value, w_index, w_length); put_acc_dev(dev); return value; } EXPORT_SYMBOL_GPL(acc_ctrlrequest); Loading Loading @@ -1065,10 +1116,6 @@ kill_all_hid_devices(struct acc_dev *dev) struct list_head *entry, *temp; unsigned long flags; /* do nothing if usb accessory device doesn't exist */ if (!dev) return; spin_lock_irqsave(&dev->lock, flags); list_for_each_safe(entry, temp, &dev->hid_list) { hid = list_entry(entry, struct acc_hid_dev, list); Loading Loading @@ -1153,12 +1200,15 @@ static void acc_hid_delete(struct acc_hid_dev *hid) static void acc_hid_work(struct work_struct *data) { struct acc_dev *dev = _acc_dev; struct acc_dev *dev = get_acc_dev(); struct list_head *entry, *temp; struct acc_hid_dev *hid; struct list_head new_list, dead_list; unsigned long flags; if (!dev) return; INIT_LIST_HEAD(&new_list); spin_lock_irqsave(&dev->lock, flags); Loading Loading @@ -1204,6 +1254,8 @@ static void acc_hid_work(struct work_struct *data) hid_destroy_device(hid->hid); acc_hid_delete(hid); } put_acc_dev(dev); } static int acc_function_set_alt(struct usb_function *f, Loading Loading @@ -1260,9 +1312,13 @@ 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; if (kref_read(&ref->kref)) return -EBUSY; dev = kzalloc(sizeof(*dev), GFP_KERNEL); if (!dev) return -ENOMEM; Loading @@ -1278,16 +1334,22 @@ static int acc_setup(void) INIT_DELAYED_WORK(&dev->start_work, acc_start_work); INIT_WORK(&dev->hid_work, acc_hid_work); dev->ref = ref; if (cmpxchg_relaxed(&ref->acc_dev, NULL, dev)) { ret = -EBUSY; goto err_free_dev; } ret = misc_register(&acc_device); if (ret) goto err; /* _acc_dev must be set before calling usb_gadget_register_driver */ _acc_dev = dev; goto err_zap_ptr; kref_init(&ref->kref); return 0; err: err_zap_ptr: ref->acc_dev = NULL; err_free_dev: kfree(dev); pr_err("USB accessory gadget driver failed to initialize\n"); return ret; Loading @@ -1295,16 +1357,24 @@ err: void acc_disconnect(void) { struct acc_dev *dev = get_acc_dev(); if (!dev) return; /* unregister all HID devices if USB is disconnected */ kill_all_hid_devices(_acc_dev); kill_all_hid_devices(dev); put_acc_dev(dev); } EXPORT_SYMBOL_GPL(acc_disconnect); static void acc_cleanup(void) { struct acc_dev *dev = get_acc_dev(); misc_deregister(&acc_device); kfree(_acc_dev); _acc_dev = NULL; put_acc_dev(dev); put_acc_dev(dev); /* Pairs with kref_init() in acc_setup() */ } static struct acc_instance *to_acc_instance(struct config_item *item) { Loading Loading @@ -1364,7 +1434,6 @@ static void acc_free_inst(struct usb_function_instance *fi) static struct usb_function_instance *acc_alloc_inst(void) { struct acc_instance *fi_acc; struct acc_dev *dev; int err; fi_acc = kzalloc(sizeof(*fi_acc), GFP_KERNEL); Loading @@ -1376,19 +1445,19 @@ static struct usb_function_instance *acc_alloc_inst(void) err = acc_setup(); if (err) { kfree(fi_acc); pr_err("Error setting ACCESSORY\n"); return ERR_PTR(err); } config_group_init_type_name(&fi_acc->func_inst.group, "", &acc_func_type); dev = _acc_dev; return &fi_acc->func_inst; } static void acc_free(struct usb_function *f) { /*NO-OP: no function specific resource allocation in mtp_alloc*/ struct acc_dev *dev = func_to_dev(f); put_acc_dev(dev); } int acc_ctrlrequest_configfs(struct usb_function *f, Loading @@ -1401,9 +1470,7 @@ int acc_ctrlrequest_configfs(struct usb_function *f, static struct usb_function *acc_alloc(struct usb_function_instance *fi) { struct acc_dev *dev = _acc_dev; pr_info("acc_alloc\n"); struct acc_dev *dev = get_acc_dev(); dev->function.name = "accessory"; dev->function.strings = acc_strings, Loading