| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * imon.c: input and display driver for SoundGraph iMON IR/VFD/LCD |
|---|
| 3 | 4 | * |
|---|
| .. | .. |
|---|
| 10 | 11 | * which the support for them wouldn't be nearly as good. Thanks |
|---|
| 11 | 12 | * also to the numerous 0xffdc device owners that tested auto-config |
|---|
| 12 | 13 | * support for me and provided debug dumps from their devices. |
|---|
| 13 | | - * |
|---|
| 14 | | - * imon is free software; you can redistribute it and/or modify |
|---|
| 15 | | - * it under the terms of the GNU General Public License as published by |
|---|
| 16 | | - * the Free Software Foundation; either version 2 of the License, or |
|---|
| 17 | | - * (at your option) any later version. |
|---|
| 18 | | - * |
|---|
| 19 | | - * This program is distributed in the hope that it will be useful, |
|---|
| 20 | | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|---|
| 21 | | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|---|
| 22 | | - * GNU General Public License for more details. |
|---|
| 23 | 14 | */ |
|---|
| 24 | 15 | |
|---|
| 25 | 16 | #define pr_fmt(fmt) KBUILD_MODNAME ":%s: " fmt, __func__ |
|---|
| .. | .. |
|---|
| 92 | 83 | __u16 flags; |
|---|
| 93 | 84 | #define IMON_NO_FLAGS 0 |
|---|
| 94 | 85 | #define IMON_NEED_20MS_PKT_DELAY 1 |
|---|
| 86 | +#define IMON_SUPPRESS_REPEATED_KEYS 2 |
|---|
| 95 | 87 | struct imon_panel_key_table key_table[]; |
|---|
| 96 | 88 | }; |
|---|
| 97 | 89 | |
|---|
| .. | .. |
|---|
| 158 | 150 | struct timer_list ttimer; /* touch screen timer */ |
|---|
| 159 | 151 | int touch_x; /* x coordinate on touchscreen */ |
|---|
| 160 | 152 | int touch_y; /* y coordinate on touchscreen */ |
|---|
| 161 | | - struct imon_usb_dev_descr *dev_descr; /* device description with key |
|---|
| 162 | | - table for front panels */ |
|---|
| 153 | + const struct imon_usb_dev_descr *dev_descr; |
|---|
| 154 | + /* device description with key */ |
|---|
| 155 | + /* table for front panels */ |
|---|
| 156 | + /* |
|---|
| 157 | + * Fields for deferring free_imon_context(). |
|---|
| 158 | + * |
|---|
| 159 | + * Since reference to "struct imon_context" is stored into |
|---|
| 160 | + * "struct file"->private_data, we need to remember |
|---|
| 161 | + * how many file descriptors might access this "struct imon_context". |
|---|
| 162 | + */ |
|---|
| 163 | + refcount_t users; |
|---|
| 164 | + /* |
|---|
| 165 | + * Use a flag for telling display_open()/vfd_write()/lcd_write() that |
|---|
| 166 | + * imon_disconnect() was already called. |
|---|
| 167 | + */ |
|---|
| 168 | + bool disconnected; |
|---|
| 169 | + /* |
|---|
| 170 | + * We need to wait for RCU grace period in order to allow |
|---|
| 171 | + * display_open() to safely check ->disconnected and increment ->users. |
|---|
| 172 | + */ |
|---|
| 173 | + struct rcu_head rcu; |
|---|
| 163 | 174 | }; |
|---|
| 164 | 175 | |
|---|
| 165 | 176 | #define TOUCH_TIMEOUT (HZ/30) |
|---|
| .. | .. |
|---|
| 167 | 178 | /* vfd character device file operations */ |
|---|
| 168 | 179 | static const struct file_operations vfd_fops = { |
|---|
| 169 | 180 | .owner = THIS_MODULE, |
|---|
| 170 | | - .open = &display_open, |
|---|
| 171 | | - .write = &vfd_write, |
|---|
| 172 | | - .release = &display_close, |
|---|
| 181 | + .open = display_open, |
|---|
| 182 | + .write = vfd_write, |
|---|
| 183 | + .release = display_close, |
|---|
| 173 | 184 | .llseek = noop_llseek, |
|---|
| 174 | 185 | }; |
|---|
| 175 | 186 | |
|---|
| 176 | 187 | /* lcd character device file operations */ |
|---|
| 177 | 188 | static const struct file_operations lcd_fops = { |
|---|
| 178 | 189 | .owner = THIS_MODULE, |
|---|
| 179 | | - .open = &display_open, |
|---|
| 180 | | - .write = &lcd_write, |
|---|
| 181 | | - .release = &display_close, |
|---|
| 190 | + .open = display_open, |
|---|
| 191 | + .write = lcd_write, |
|---|
| 192 | + .release = display_close, |
|---|
| 182 | 193 | .llseek = noop_llseek, |
|---|
| 183 | 194 | }; |
|---|
| 184 | 195 | |
|---|
| .. | .. |
|---|
| 324 | 335 | } |
|---|
| 325 | 336 | }; |
|---|
| 326 | 337 | |
|---|
| 338 | +/* imon ultrabay front panel key table */ |
|---|
| 339 | +static const struct imon_usb_dev_descr ultrabay_table = { |
|---|
| 340 | + .flags = IMON_SUPPRESS_REPEATED_KEYS, |
|---|
| 341 | + .key_table = { |
|---|
| 342 | + { 0x0000000f0000ffeell, KEY_MEDIA }, /* Go */ |
|---|
| 343 | + { 0x000000000100ffeell, KEY_UP }, |
|---|
| 344 | + { 0x000000000001ffeell, KEY_DOWN }, |
|---|
| 345 | + { 0x000000160000ffeell, KEY_ENTER }, |
|---|
| 346 | + { 0x0000001f0000ffeell, KEY_AUDIO }, /* Music */ |
|---|
| 347 | + { 0x000000200000ffeell, KEY_VIDEO }, /* Movie */ |
|---|
| 348 | + { 0x000000210000ffeell, KEY_CAMERA }, /* Photo */ |
|---|
| 349 | + { 0x000000270000ffeell, KEY_DVD }, /* DVD */ |
|---|
| 350 | + { 0x000000230000ffeell, KEY_TV }, /* TV */ |
|---|
| 351 | + { 0x000000050000ffeell, KEY_PREVIOUS }, /* Previous */ |
|---|
| 352 | + { 0x000000070000ffeell, KEY_REWIND }, |
|---|
| 353 | + { 0x000000040000ffeell, KEY_STOP }, |
|---|
| 354 | + { 0x000000020000ffeell, KEY_PLAYPAUSE }, |
|---|
| 355 | + { 0x000000080000ffeell, KEY_FASTFORWARD }, |
|---|
| 356 | + { 0x000000060000ffeell, KEY_NEXT }, /* Next */ |
|---|
| 357 | + { 0x000100000000ffeell, KEY_VOLUMEUP }, |
|---|
| 358 | + { 0x010000000000ffeell, KEY_VOLUMEDOWN }, |
|---|
| 359 | + { 0x000000010000ffeell, KEY_MUTE }, |
|---|
| 360 | + { 0, KEY_RESERVED }, |
|---|
| 361 | + } |
|---|
| 362 | +}; |
|---|
| 363 | + |
|---|
| 327 | 364 | /* |
|---|
| 328 | 365 | * USB Device ID for iMON USB Control Boards |
|---|
| 329 | 366 | * |
|---|
| .. | .. |
|---|
| 420 | 457 | .id_table = imon_usb_id_table, |
|---|
| 421 | 458 | }; |
|---|
| 422 | 459 | |
|---|
| 423 | | -/* to prevent races between open() and disconnect(), probing, etc */ |
|---|
| 424 | | -static DEFINE_MUTEX(driver_lock); |
|---|
| 425 | | - |
|---|
| 426 | 460 | /* Module bookkeeping bits */ |
|---|
| 427 | 461 | MODULE_AUTHOR(MOD_AUTHOR); |
|---|
| 428 | 462 | MODULE_DESCRIPTION(MOD_DESC); |
|---|
| .. | .. |
|---|
| 462 | 496 | struct device *dev = ictx->dev; |
|---|
| 463 | 497 | |
|---|
| 464 | 498 | usb_free_urb(ictx->tx_urb); |
|---|
| 499 | + WARN_ON(ictx->dev_present_intf0); |
|---|
| 465 | 500 | usb_free_urb(ictx->rx_urb_intf0); |
|---|
| 501 | + WARN_ON(ictx->dev_present_intf1); |
|---|
| 466 | 502 | usb_free_urb(ictx->rx_urb_intf1); |
|---|
| 467 | | - kfree(ictx); |
|---|
| 503 | + kfree_rcu(ictx, rcu); |
|---|
| 468 | 504 | |
|---|
| 469 | 505 | dev_dbg(dev, "%s: iMON context freed\n", __func__); |
|---|
| 470 | 506 | } |
|---|
| .. | .. |
|---|
| 480 | 516 | int subminor; |
|---|
| 481 | 517 | int retval = 0; |
|---|
| 482 | 518 | |
|---|
| 483 | | - /* prevent races with disconnect */ |
|---|
| 484 | | - mutex_lock(&driver_lock); |
|---|
| 485 | | - |
|---|
| 486 | 519 | subminor = iminor(inode); |
|---|
| 487 | 520 | interface = usb_find_interface(&imon_driver, subminor); |
|---|
| 488 | 521 | if (!interface) { |
|---|
| .. | .. |
|---|
| 490 | 523 | retval = -ENODEV; |
|---|
| 491 | 524 | goto exit; |
|---|
| 492 | 525 | } |
|---|
| 493 | | - ictx = usb_get_intfdata(interface); |
|---|
| 494 | 526 | |
|---|
| 495 | | - if (!ictx) { |
|---|
| 527 | + rcu_read_lock(); |
|---|
| 528 | + ictx = usb_get_intfdata(interface); |
|---|
| 529 | + if (!ictx || ictx->disconnected || !refcount_inc_not_zero(&ictx->users)) { |
|---|
| 530 | + rcu_read_unlock(); |
|---|
| 496 | 531 | pr_err("no context found for minor %d\n", subminor); |
|---|
| 497 | 532 | retval = -ENODEV; |
|---|
| 498 | 533 | goto exit; |
|---|
| 499 | 534 | } |
|---|
| 535 | + rcu_read_unlock(); |
|---|
| 500 | 536 | |
|---|
| 501 | 537 | mutex_lock(&ictx->lock); |
|---|
| 502 | 538 | |
|---|
| .. | .. |
|---|
| 514 | 550 | |
|---|
| 515 | 551 | mutex_unlock(&ictx->lock); |
|---|
| 516 | 552 | |
|---|
| 553 | + if (retval && refcount_dec_and_test(&ictx->users)) |
|---|
| 554 | + free_imon_context(ictx); |
|---|
| 555 | + |
|---|
| 517 | 556 | exit: |
|---|
| 518 | | - mutex_unlock(&driver_lock); |
|---|
| 519 | 557 | return retval; |
|---|
| 520 | 558 | } |
|---|
| 521 | 559 | |
|---|
| .. | .. |
|---|
| 525 | 563 | */ |
|---|
| 526 | 564 | static int display_close(struct inode *inode, struct file *file) |
|---|
| 527 | 565 | { |
|---|
| 528 | | - struct imon_context *ictx = NULL; |
|---|
| 566 | + struct imon_context *ictx = file->private_data; |
|---|
| 529 | 567 | int retval = 0; |
|---|
| 530 | | - |
|---|
| 531 | | - ictx = file->private_data; |
|---|
| 532 | | - |
|---|
| 533 | | - if (!ictx) { |
|---|
| 534 | | - pr_err("no context for device\n"); |
|---|
| 535 | | - return -ENODEV; |
|---|
| 536 | | - } |
|---|
| 537 | 568 | |
|---|
| 538 | 569 | mutex_lock(&ictx->lock); |
|---|
| 539 | 570 | |
|---|
| .. | .. |
|---|
| 549 | 580 | } |
|---|
| 550 | 581 | |
|---|
| 551 | 582 | mutex_unlock(&ictx->lock); |
|---|
| 583 | + if (refcount_dec_and_test(&ictx->users)) |
|---|
| 584 | + free_imon_context(ictx); |
|---|
| 552 | 585 | return retval; |
|---|
| 553 | 586 | } |
|---|
| 554 | 587 | |
|---|
| .. | .. |
|---|
| 613 | 646 | pr_err_ratelimited("error submitting urb(%d)\n", retval); |
|---|
| 614 | 647 | } else { |
|---|
| 615 | 648 | /* Wait for transmission to complete (or abort) */ |
|---|
| 616 | | - mutex_unlock(&ictx->lock); |
|---|
| 617 | 649 | retval = wait_for_completion_interruptible( |
|---|
| 618 | 650 | &ictx->tx.finished); |
|---|
| 619 | 651 | if (retval) { |
|---|
| 620 | 652 | usb_kill_urb(ictx->tx_urb); |
|---|
| 621 | 653 | pr_err_ratelimited("task interrupted\n"); |
|---|
| 622 | 654 | } |
|---|
| 623 | | - mutex_lock(&ictx->lock); |
|---|
| 624 | 655 | |
|---|
| 656 | + ictx->tx.busy = false; |
|---|
| 625 | 657 | retval = ictx->tx.status; |
|---|
| 626 | 658 | if (retval) |
|---|
| 627 | 659 | pr_err_ratelimited("packet tx failed (%d)\n", retval); |
|---|
| .. | .. |
|---|
| 772 | 804 | |
|---|
| 773 | 805 | mutex_lock(&ictx->lock); |
|---|
| 774 | 806 | if (ictx->rf_isassociating) |
|---|
| 775 | | - strcpy(buf, "associating\n"); |
|---|
| 807 | + strscpy(buf, "associating\n", PAGE_SIZE); |
|---|
| 776 | 808 | else |
|---|
| 777 | | - strcpy(buf, "closed\n"); |
|---|
| 809 | + strscpy(buf, "closed\n", PAGE_SIZE); |
|---|
| 778 | 810 | |
|---|
| 779 | | - dev_info(d, "Visit http://www.lirc.org/html/imon-24g.html for instructions on how to associate your iMON 2.4G DT/LT remote\n"); |
|---|
| 811 | + dev_info(d, "Visit https://www.lirc.org/html/imon-24g.html for instructions on how to associate your iMON 2.4G DT/LT remote\n"); |
|---|
| 780 | 812 | mutex_unlock(&ictx->lock); |
|---|
| 781 | 813 | return strlen(buf); |
|---|
| 782 | 814 | } |
|---|
| .. | .. |
|---|
| 918 | 950 | int offset; |
|---|
| 919 | 951 | int seq; |
|---|
| 920 | 952 | int retval = 0; |
|---|
| 921 | | - struct imon_context *ictx; |
|---|
| 953 | + struct imon_context *ictx = file->private_data; |
|---|
| 922 | 954 | static const unsigned char vfd_packet6[] = { |
|---|
| 923 | 955 | 0x01, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF }; |
|---|
| 924 | 956 | |
|---|
| 925 | | - ictx = file->private_data; |
|---|
| 926 | | - if (!ictx) { |
|---|
| 927 | | - pr_err_ratelimited("no context for device\n"); |
|---|
| 957 | + if (ictx->disconnected) |
|---|
| 928 | 958 | return -ENODEV; |
|---|
| 929 | | - } |
|---|
| 930 | 959 | |
|---|
| 931 | | - mutex_lock(&ictx->lock); |
|---|
| 960 | + if (mutex_lock_interruptible(&ictx->lock)) |
|---|
| 961 | + return -ERESTARTSYS; |
|---|
| 932 | 962 | |
|---|
| 933 | 963 | if (!ictx->dev_present_intf0) { |
|---|
| 934 | 964 | pr_err_ratelimited("no iMON device present\n"); |
|---|
| .. | .. |
|---|
| 1002 | 1032 | size_t n_bytes, loff_t *pos) |
|---|
| 1003 | 1033 | { |
|---|
| 1004 | 1034 | int retval = 0; |
|---|
| 1005 | | - struct imon_context *ictx; |
|---|
| 1035 | + struct imon_context *ictx = file->private_data; |
|---|
| 1006 | 1036 | |
|---|
| 1007 | | - ictx = file->private_data; |
|---|
| 1008 | | - if (!ictx) { |
|---|
| 1009 | | - pr_err_ratelimited("no context for device\n"); |
|---|
| 1037 | + if (ictx->disconnected) |
|---|
| 1010 | 1038 | return -ENODEV; |
|---|
| 1011 | | - } |
|---|
| 1012 | 1039 | |
|---|
| 1013 | 1040 | mutex_lock(&ictx->lock); |
|---|
| 1014 | 1041 | |
|---|
| .. | .. |
|---|
| 1273 | 1300 | |
|---|
| 1274 | 1301 | static u32 imon_panel_key_lookup(struct imon_context *ictx, u64 code) |
|---|
| 1275 | 1302 | { |
|---|
| 1276 | | - int i; |
|---|
| 1303 | + const struct imon_panel_key_table *key_table; |
|---|
| 1277 | 1304 | u32 keycode = KEY_RESERVED; |
|---|
| 1278 | | - struct imon_panel_key_table *key_table = ictx->dev_descr->key_table; |
|---|
| 1305 | + int i; |
|---|
| 1306 | + |
|---|
| 1307 | + key_table = ictx->dev_descr->key_table; |
|---|
| 1279 | 1308 | |
|---|
| 1280 | 1309 | for (i = 0; key_table[i].hw_code != 0; i++) { |
|---|
| 1281 | 1310 | if (key_table[i].hw_code == (code | 0xffee)) { |
|---|
| .. | .. |
|---|
| 1559 | 1588 | u32 kc; |
|---|
| 1560 | 1589 | u64 scancode; |
|---|
| 1561 | 1590 | int press_type = 0; |
|---|
| 1562 | | - long msec; |
|---|
| 1563 | 1591 | ktime_t t; |
|---|
| 1564 | 1592 | static ktime_t prev_time; |
|---|
| 1565 | 1593 | u8 ktype; |
|---|
| .. | .. |
|---|
| 1661 | 1689 | spin_lock_irqsave(&ictx->kc_lock, flags); |
|---|
| 1662 | 1690 | |
|---|
| 1663 | 1691 | t = ktime_get(); |
|---|
| 1664 | | - /* KEY_MUTE repeats from knob need to be suppressed */ |
|---|
| 1665 | | - if (ictx->kc == KEY_MUTE && ictx->kc == ictx->last_keycode) { |
|---|
| 1666 | | - msec = ktime_ms_delta(t, prev_time); |
|---|
| 1667 | | - if (msec < ictx->idev->rep[REP_DELAY]) { |
|---|
| 1692 | + /* KEY repeats from knob and panel that need to be suppressed */ |
|---|
| 1693 | + if (ictx->kc == KEY_MUTE || |
|---|
| 1694 | + ictx->dev_descr->flags & IMON_SUPPRESS_REPEATED_KEYS) { |
|---|
| 1695 | + if (ictx->kc == ictx->last_keycode && |
|---|
| 1696 | + ktime_ms_delta(t, prev_time) < ictx->idev->rep[REP_DELAY]) { |
|---|
| 1668 | 1697 | spin_unlock_irqrestore(&ictx->kc_lock, flags); |
|---|
| 1669 | 1698 | return; |
|---|
| 1670 | 1699 | } |
|---|
| 1671 | 1700 | } |
|---|
| 1701 | + |
|---|
| 1672 | 1702 | prev_time = t; |
|---|
| 1673 | 1703 | kc = ictx->kc; |
|---|
| 1674 | 1704 | |
|---|
| .. | .. |
|---|
| 1856 | 1886 | dev_info(ictx->dev, "0xffdc iMON Inside, iMON IR"); |
|---|
| 1857 | 1887 | ictx->display_supported = false; |
|---|
| 1858 | 1888 | break; |
|---|
| 1889 | + /* Soundgraph iMON UltraBay */ |
|---|
| 1890 | + case 0x98: |
|---|
| 1891 | + dev_info(ictx->dev, "0xffdc iMON UltraBay, LCD + IR"); |
|---|
| 1892 | + detected_display_type = IMON_DISPLAY_TYPE_LCD; |
|---|
| 1893 | + allowed_protos = RC_PROTO_BIT_IMON | RC_PROTO_BIT_RC6_MCE; |
|---|
| 1894 | + ictx->dev_descr = &ultrabay_table; |
|---|
| 1895 | + break; |
|---|
| 1896 | + |
|---|
| 1859 | 1897 | default: |
|---|
| 1860 | 1898 | dev_info(ictx->dev, "Unknown 0xffdc device, defaulting to VFD and iMON IR"); |
|---|
| 1861 | 1899 | detected_display_type = IMON_DISPLAY_TYPE_VFD; |
|---|
| .. | .. |
|---|
| 1987 | 2025 | |
|---|
| 1988 | 2026 | static struct input_dev *imon_init_idev(struct imon_context *ictx) |
|---|
| 1989 | 2027 | { |
|---|
| 1990 | | - struct imon_panel_key_table *key_table = ictx->dev_descr->key_table; |
|---|
| 2028 | + const struct imon_panel_key_table *key_table; |
|---|
| 1991 | 2029 | struct input_dev *idev; |
|---|
| 1992 | 2030 | int ret, i; |
|---|
| 2031 | + |
|---|
| 2032 | + key_table = ictx->dev_descr->key_table; |
|---|
| 1993 | 2033 | |
|---|
| 1994 | 2034 | idev = input_allocate_device(); |
|---|
| 1995 | 2035 | if (!idev) |
|---|
| .. | .. |
|---|
| 2373 | 2413 | int ifnum, sysfs_err; |
|---|
| 2374 | 2414 | int ret = 0; |
|---|
| 2375 | 2415 | struct imon_context *ictx = NULL; |
|---|
| 2376 | | - struct imon_context *first_if_ctx = NULL; |
|---|
| 2377 | 2416 | u16 vendor, product; |
|---|
| 2378 | 2417 | |
|---|
| 2379 | 2418 | usbdev = usb_get_dev(interface_to_usbdev(interface)); |
|---|
| .. | .. |
|---|
| 2385 | 2424 | dev_dbg(dev, "%s: found iMON device (%04x:%04x, intf%d)\n", |
|---|
| 2386 | 2425 | __func__, vendor, product, ifnum); |
|---|
| 2387 | 2426 | |
|---|
| 2388 | | - /* prevent races probing devices w/multiple interfaces */ |
|---|
| 2389 | | - mutex_lock(&driver_lock); |
|---|
| 2390 | | - |
|---|
| 2391 | 2427 | first_if = usb_ifnum_to_if(usbdev, 0); |
|---|
| 2392 | 2428 | if (!first_if) { |
|---|
| 2393 | 2429 | ret = -ENODEV; |
|---|
| 2394 | 2430 | goto fail; |
|---|
| 2395 | 2431 | } |
|---|
| 2396 | | - |
|---|
| 2397 | | - first_if_ctx = usb_get_intfdata(first_if); |
|---|
| 2398 | 2432 | |
|---|
| 2399 | 2433 | if (ifnum == 0) { |
|---|
| 2400 | 2434 | ictx = imon_init_intf0(interface, id); |
|---|
| .. | .. |
|---|
| 2403 | 2437 | ret = -ENODEV; |
|---|
| 2404 | 2438 | goto fail; |
|---|
| 2405 | 2439 | } |
|---|
| 2440 | + refcount_set(&ictx->users, 1); |
|---|
| 2406 | 2441 | |
|---|
| 2407 | 2442 | } else { |
|---|
| 2408 | 2443 | /* this is the secondary interface on the device */ |
|---|
| 2444 | + struct imon_context *first_if_ctx = usb_get_intfdata(first_if); |
|---|
| 2409 | 2445 | |
|---|
| 2410 | 2446 | /* fail early if first intf failed to register */ |
|---|
| 2411 | 2447 | if (!first_if_ctx) { |
|---|
| .. | .. |
|---|
| 2419 | 2455 | ret = -ENODEV; |
|---|
| 2420 | 2456 | goto fail; |
|---|
| 2421 | 2457 | } |
|---|
| 2458 | + refcount_inc(&ictx->users); |
|---|
| 2422 | 2459 | |
|---|
| 2423 | 2460 | } |
|---|
| 2424 | 2461 | |
|---|
| 2425 | 2462 | usb_set_intfdata(interface, ictx); |
|---|
| 2426 | 2463 | |
|---|
| 2427 | 2464 | if (ifnum == 0) { |
|---|
| 2428 | | - mutex_lock(&ictx->lock); |
|---|
| 2429 | | - |
|---|
| 2430 | 2465 | if (product == 0xffdc && ictx->rf_device) { |
|---|
| 2431 | 2466 | sysfs_err = sysfs_create_group(&interface->dev.kobj, |
|---|
| 2432 | 2467 | &imon_rf_attr_group); |
|---|
| .. | .. |
|---|
| 2437 | 2472 | |
|---|
| 2438 | 2473 | if (ictx->display_supported) |
|---|
| 2439 | 2474 | imon_init_display(ictx, interface); |
|---|
| 2440 | | - |
|---|
| 2441 | | - mutex_unlock(&ictx->lock); |
|---|
| 2442 | 2475 | } |
|---|
| 2443 | 2476 | |
|---|
| 2444 | 2477 | dev_info(dev, "iMON device (%04x:%04x, intf%d) on usb<%d:%d> initialized\n", |
|---|
| 2445 | 2478 | vendor, product, ifnum, |
|---|
| 2446 | 2479 | usbdev->bus->busnum, usbdev->devnum); |
|---|
| 2447 | 2480 | |
|---|
| 2448 | | - mutex_unlock(&driver_lock); |
|---|
| 2449 | 2481 | usb_put_dev(usbdev); |
|---|
| 2450 | 2482 | |
|---|
| 2451 | 2483 | return 0; |
|---|
| 2452 | 2484 | |
|---|
| 2453 | 2485 | fail: |
|---|
| 2454 | | - mutex_unlock(&driver_lock); |
|---|
| 2455 | 2486 | usb_put_dev(usbdev); |
|---|
| 2456 | 2487 | dev_err(dev, "unable to register, err %d\n", ret); |
|---|
| 2457 | 2488 | |
|---|
| .. | .. |
|---|
| 2467 | 2498 | struct device *dev; |
|---|
| 2468 | 2499 | int ifnum; |
|---|
| 2469 | 2500 | |
|---|
| 2470 | | - /* prevent races with multi-interface device probing and display_open */ |
|---|
| 2471 | | - mutex_lock(&driver_lock); |
|---|
| 2472 | | - |
|---|
| 2473 | 2501 | ictx = usb_get_intfdata(interface); |
|---|
| 2502 | + ictx->disconnected = true; |
|---|
| 2474 | 2503 | dev = ictx->dev; |
|---|
| 2475 | 2504 | ifnum = interface->cur_altsetting->desc.bInterfaceNumber; |
|---|
| 2476 | 2505 | |
|---|
| .. | .. |
|---|
| 2511 | 2540 | } |
|---|
| 2512 | 2541 | } |
|---|
| 2513 | 2542 | |
|---|
| 2514 | | - if (!ictx->dev_present_intf0 && !ictx->dev_present_intf1) |
|---|
| 2543 | + if (refcount_dec_and_test(&ictx->users)) |
|---|
| 2515 | 2544 | free_imon_context(ictx); |
|---|
| 2516 | | - |
|---|
| 2517 | | - mutex_unlock(&driver_lock); |
|---|
| 2518 | 2545 | |
|---|
| 2519 | 2546 | dev_dbg(dev, "%s: iMON device (intf%d) disconnected\n", |
|---|
| 2520 | 2547 | __func__, ifnum); |
|---|