.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
---|
1 | 2 | /* |
---|
2 | 3 | * smscufx.c -- Framebuffer driver for SMSC UFX USB controller |
---|
3 | 4 | * |
---|
.. | .. |
---|
5 | 6 | * Copyright (C) 2009 Roberto De Ioris <roberto@unbit.it> |
---|
6 | 7 | * Copyright (C) 2009 Jaya Kumar <jayakumar.lkml@gmail.com> |
---|
7 | 8 | * Copyright (C) 2009 Bernie Thompson <bernie@plugable.com> |
---|
8 | | - * |
---|
9 | | - * This file is subject to the terms and conditions of the GNU General Public |
---|
10 | | - * License v2. See the file COPYING in the main directory of this archive for |
---|
11 | | - * more details. |
---|
12 | 9 | * |
---|
13 | 10 | * Based on udlfb, with work from Florian Echtler, Henrik Bjerregaard Pedersen, |
---|
14 | 11 | * and others. |
---|
.. | .. |
---|
100 | 97 | struct kref kref; |
---|
101 | 98 | int fb_count; |
---|
102 | 99 | bool virtualized; /* true when physical usb device not present */ |
---|
103 | | - struct delayed_work free_framebuffer_work; |
---|
104 | 100 | atomic_t usb_active; /* 0 = update virtual buffer, but no usb traffic */ |
---|
105 | 101 | atomic_t lost_pixels; /* 1 = a render op failed. Need screen refresh */ |
---|
106 | 102 | u8 *edid; /* null until we read edid from hw or get from sysfs */ |
---|
.. | .. |
---|
139 | 135 | static int ufx_submit_urb(struct ufx_data *dev, struct urb * urb, size_t len); |
---|
140 | 136 | static int ufx_alloc_urb_list(struct ufx_data *dev, int count, size_t size); |
---|
141 | 137 | static void ufx_free_urb_list(struct ufx_data *dev); |
---|
| 138 | + |
---|
| 139 | +static DEFINE_MUTEX(disconnect_mutex); |
---|
142 | 140 | |
---|
143 | 141 | /* reads a control register */ |
---|
144 | 142 | static int ufx_reg_read(struct ufx_data *dev, u32 index, u32 *data) |
---|
.. | .. |
---|
1073 | 1071 | if (user == 0 && !console) |
---|
1074 | 1072 | return -EBUSY; |
---|
1075 | 1073 | |
---|
| 1074 | + mutex_lock(&disconnect_mutex); |
---|
| 1075 | + |
---|
1076 | 1076 | /* If the USB device is gone, we don't accept new opens */ |
---|
1077 | | - if (dev->virtualized) |
---|
| 1077 | + if (dev->virtualized) { |
---|
| 1078 | + mutex_unlock(&disconnect_mutex); |
---|
1078 | 1079 | return -ENODEV; |
---|
| 1080 | + } |
---|
1079 | 1081 | |
---|
1080 | 1082 | dev->fb_count++; |
---|
1081 | 1083 | |
---|
.. | .. |
---|
1099 | 1101 | pr_debug("open /dev/fb%d user=%d fb_info=%p count=%d", |
---|
1100 | 1102 | info->node, user, info, dev->fb_count); |
---|
1101 | 1103 | |
---|
| 1104 | + mutex_unlock(&disconnect_mutex); |
---|
| 1105 | + |
---|
1102 | 1106 | return 0; |
---|
1103 | 1107 | } |
---|
1104 | 1108 | |
---|
.. | .. |
---|
1111 | 1115 | { |
---|
1112 | 1116 | struct ufx_data *dev = container_of(kref, struct ufx_data, kref); |
---|
1113 | 1117 | |
---|
1114 | | - /* this function will wait for all in-flight urbs to complete */ |
---|
1115 | | - if (dev->urbs.count > 0) |
---|
1116 | | - ufx_free_urb_list(dev); |
---|
1117 | | - |
---|
1118 | | - pr_debug("freeing ufx_data %p", dev); |
---|
1119 | | - |
---|
1120 | 1118 | kfree(dev); |
---|
1121 | 1119 | } |
---|
| 1120 | + |
---|
| 1121 | +static void ufx_ops_destory(struct fb_info *info) |
---|
| 1122 | +{ |
---|
| 1123 | + struct ufx_data *dev = info->par; |
---|
| 1124 | + int node = info->node; |
---|
| 1125 | + |
---|
| 1126 | + /* Assume info structure is freed after this point */ |
---|
| 1127 | + framebuffer_release(info); |
---|
| 1128 | + |
---|
| 1129 | + pr_debug("fb_info for /dev/fb%d has been freed", node); |
---|
| 1130 | + |
---|
| 1131 | + /* release reference taken by kref_init in probe() */ |
---|
| 1132 | + kref_put(&dev->kref, ufx_free); |
---|
| 1133 | +} |
---|
| 1134 | + |
---|
1122 | 1135 | |
---|
1123 | 1136 | static void ufx_release_urb_work(struct work_struct *work) |
---|
1124 | 1137 | { |
---|
.. | .. |
---|
1128 | 1141 | up(&unode->dev->urbs.limit_sem); |
---|
1129 | 1142 | } |
---|
1130 | 1143 | |
---|
1131 | | -static void ufx_free_framebuffer_work(struct work_struct *work) |
---|
| 1144 | +static void ufx_free_framebuffer(struct ufx_data *dev) |
---|
1132 | 1145 | { |
---|
1133 | | - struct ufx_data *dev = container_of(work, struct ufx_data, |
---|
1134 | | - free_framebuffer_work.work); |
---|
1135 | 1146 | struct fb_info *info = dev->info; |
---|
1136 | | - int node = info->node; |
---|
1137 | | - |
---|
1138 | | - unregister_framebuffer(info); |
---|
1139 | 1147 | |
---|
1140 | 1148 | if (info->cmap.len != 0) |
---|
1141 | 1149 | fb_dealloc_cmap(&info->cmap); |
---|
.. | .. |
---|
1146 | 1154 | fb_destroy_modelist(&info->modelist); |
---|
1147 | 1155 | |
---|
1148 | 1156 | dev->info = NULL; |
---|
1149 | | - |
---|
1150 | | - /* Assume info structure is freed after this point */ |
---|
1151 | | - framebuffer_release(info); |
---|
1152 | | - |
---|
1153 | | - pr_debug("fb_info for /dev/fb%d has been freed", node); |
---|
1154 | 1157 | |
---|
1155 | 1158 | /* ref taken in probe() as part of registering framebfufer */ |
---|
1156 | 1159 | kref_put(&dev->kref, ufx_free); |
---|
.. | .. |
---|
1163 | 1166 | { |
---|
1164 | 1167 | struct ufx_data *dev = info->par; |
---|
1165 | 1168 | |
---|
| 1169 | + mutex_lock(&disconnect_mutex); |
---|
| 1170 | + |
---|
1166 | 1171 | dev->fb_count--; |
---|
1167 | 1172 | |
---|
1168 | 1173 | /* We can't free fb_info here - fbmem will touch it when we return */ |
---|
1169 | 1174 | if (dev->virtualized && (dev->fb_count == 0)) |
---|
1170 | | - schedule_delayed_work(&dev->free_framebuffer_work, HZ); |
---|
| 1175 | + ufx_free_framebuffer(dev); |
---|
1171 | 1176 | |
---|
1172 | 1177 | if ((dev->fb_count == 0) && (info->fbdefio)) { |
---|
1173 | 1178 | fb_deferred_io_cleanup(info); |
---|
1174 | 1179 | kfree(info->fbdefio); |
---|
1175 | 1180 | info->fbdefio = NULL; |
---|
1176 | | - info->fbops->fb_mmap = ufx_ops_mmap; |
---|
1177 | 1181 | } |
---|
1178 | 1182 | |
---|
1179 | 1183 | pr_debug("released /dev/fb%d user=%d count=%d", |
---|
1180 | 1184 | info->node, user, dev->fb_count); |
---|
1181 | 1185 | |
---|
1182 | 1186 | kref_put(&dev->kref, ufx_free); |
---|
| 1187 | + |
---|
| 1188 | + mutex_unlock(&disconnect_mutex); |
---|
1183 | 1189 | |
---|
1184 | 1190 | return 0; |
---|
1185 | 1191 | } |
---|
.. | .. |
---|
1272 | 1278 | return 0; |
---|
1273 | 1279 | } |
---|
1274 | 1280 | |
---|
1275 | | -static struct fb_ops ufx_ops = { |
---|
| 1281 | +static const struct fb_ops ufx_ops = { |
---|
1276 | 1282 | .owner = THIS_MODULE, |
---|
1277 | 1283 | .fb_read = fb_sys_read, |
---|
1278 | 1284 | .fb_write = ufx_ops_write, |
---|
.. | .. |
---|
1287 | 1293 | .fb_blank = ufx_ops_blank, |
---|
1288 | 1294 | .fb_check_var = ufx_ops_check_var, |
---|
1289 | 1295 | .fb_set_par = ufx_ops_set_par, |
---|
| 1296 | + .fb_destroy = ufx_ops_destory, |
---|
1290 | 1297 | }; |
---|
1291 | 1298 | |
---|
1292 | 1299 | /* Assumes &info->lock held by caller |
---|
.. | .. |
---|
1614 | 1621 | struct usb_device *usbdev; |
---|
1615 | 1622 | struct ufx_data *dev; |
---|
1616 | 1623 | struct fb_info *info; |
---|
1617 | | - int retval; |
---|
| 1624 | + int retval = -ENOMEM; |
---|
1618 | 1625 | u32 id_rev, fpga_rev; |
---|
1619 | 1626 | |
---|
1620 | 1627 | /* usb initialization */ |
---|
.. | .. |
---|
1646 | 1653 | |
---|
1647 | 1654 | if (!ufx_alloc_urb_list(dev, WRITES_IN_FLIGHT, MAX_TRANSFER)) { |
---|
1648 | 1655 | dev_err(dev->gdev, "ufx_alloc_urb_list failed\n"); |
---|
1649 | | - goto e_nomem; |
---|
| 1656 | + goto put_ref; |
---|
1650 | 1657 | } |
---|
1651 | 1658 | |
---|
1652 | 1659 | /* We don't register a new USB class. Our client interface is fbdev */ |
---|
.. | .. |
---|
1655 | 1662 | info = framebuffer_alloc(0, &usbdev->dev); |
---|
1656 | 1663 | if (!info) { |
---|
1657 | 1664 | dev_err(dev->gdev, "framebuffer_alloc failed\n"); |
---|
1658 | | - goto e_nomem; |
---|
| 1665 | + goto free_urb_list; |
---|
1659 | 1666 | } |
---|
1660 | 1667 | |
---|
1661 | 1668 | dev->info = info; |
---|
1662 | 1669 | info->par = dev; |
---|
1663 | 1670 | info->pseudo_palette = dev->pseudo_palette; |
---|
1664 | 1671 | info->fbops = &ufx_ops; |
---|
| 1672 | + INIT_LIST_HEAD(&info->modelist); |
---|
1665 | 1673 | |
---|
1666 | 1674 | retval = fb_alloc_cmap(&info->cmap, 256, 0); |
---|
1667 | 1675 | if (retval < 0) { |
---|
1668 | 1676 | dev_err(dev->gdev, "fb_alloc_cmap failed %x\n", retval); |
---|
1669 | 1677 | goto destroy_modedb; |
---|
1670 | 1678 | } |
---|
1671 | | - |
---|
1672 | | - INIT_DELAYED_WORK(&dev->free_framebuffer_work, |
---|
1673 | | - ufx_free_framebuffer_work); |
---|
1674 | | - |
---|
1675 | | - INIT_LIST_HEAD(&info->modelist); |
---|
1676 | 1679 | |
---|
1677 | 1680 | retval = ufx_reg_read(dev, 0x3000, &id_rev); |
---|
1678 | 1681 | check_warn_goto_error(retval, "error %d reading 0x3000 register from device", retval); |
---|
.. | .. |
---|
1703 | 1706 | check_warn_goto_error(retval, "unable to find common mode for display and adapter"); |
---|
1704 | 1707 | |
---|
1705 | 1708 | retval = ufx_reg_set_bits(dev, 0x4000, 0x00000001); |
---|
1706 | | - check_warn_goto_error(retval, "error %d enabling graphics engine", retval); |
---|
| 1709 | + if (retval < 0) { |
---|
| 1710 | + dev_err(dev->gdev, "error %d enabling graphics engine", retval); |
---|
| 1711 | + goto setup_modes; |
---|
| 1712 | + } |
---|
1707 | 1713 | |
---|
1708 | 1714 | /* ready to begin using device */ |
---|
1709 | 1715 | atomic_set(&dev->usb_active, 1); |
---|
1710 | 1716 | |
---|
1711 | 1717 | dev_dbg(dev->gdev, "checking var"); |
---|
1712 | 1718 | retval = ufx_ops_check_var(&info->var, info); |
---|
1713 | | - check_warn_goto_error(retval, "error %d ufx_ops_check_var", retval); |
---|
| 1719 | + if (retval < 0) { |
---|
| 1720 | + dev_err(dev->gdev, "error %d ufx_ops_check_var", retval); |
---|
| 1721 | + goto reset_active; |
---|
| 1722 | + } |
---|
1714 | 1723 | |
---|
1715 | 1724 | dev_dbg(dev->gdev, "setting par"); |
---|
1716 | 1725 | retval = ufx_ops_set_par(info); |
---|
1717 | | - check_warn_goto_error(retval, "error %d ufx_ops_set_par", retval); |
---|
| 1726 | + if (retval < 0) { |
---|
| 1727 | + dev_err(dev->gdev, "error %d ufx_ops_set_par", retval); |
---|
| 1728 | + goto reset_active; |
---|
| 1729 | + } |
---|
1718 | 1730 | |
---|
1719 | 1731 | dev_dbg(dev->gdev, "registering framebuffer"); |
---|
1720 | 1732 | retval = register_framebuffer(info); |
---|
1721 | | - check_warn_goto_error(retval, "error %d register_framebuffer", retval); |
---|
| 1733 | + if (retval < 0) { |
---|
| 1734 | + dev_err(dev->gdev, "error %d register_framebuffer", retval); |
---|
| 1735 | + goto reset_active; |
---|
| 1736 | + } |
---|
1722 | 1737 | |
---|
1723 | 1738 | dev_info(dev->gdev, "SMSC UDX USB device /dev/fb%d attached. %dx%d resolution." |
---|
1724 | 1739 | " Using %dK framebuffer memory\n", info->node, |
---|
.. | .. |
---|
1726 | 1741 | |
---|
1727 | 1742 | return 0; |
---|
1728 | 1743 | |
---|
1729 | | -error: |
---|
1730 | | - fb_dealloc_cmap(&info->cmap); |
---|
1731 | | -destroy_modedb: |
---|
| 1744 | +reset_active: |
---|
| 1745 | + atomic_set(&dev->usb_active, 0); |
---|
| 1746 | +setup_modes: |
---|
1732 | 1747 | fb_destroy_modedb(info->monspecs.modedb); |
---|
1733 | 1748 | vfree(info->screen_base); |
---|
1734 | 1749 | fb_destroy_modelist(&info->modelist); |
---|
| 1750 | +error: |
---|
| 1751 | + fb_dealloc_cmap(&info->cmap); |
---|
| 1752 | +destroy_modedb: |
---|
1735 | 1753 | framebuffer_release(info); |
---|
| 1754 | +free_urb_list: |
---|
| 1755 | + if (dev->urbs.count > 0) |
---|
| 1756 | + ufx_free_urb_list(dev); |
---|
1736 | 1757 | put_ref: |
---|
1737 | 1758 | kref_put(&dev->kref, ufx_free); /* ref for framebuffer */ |
---|
1738 | 1759 | kref_put(&dev->kref, ufx_free); /* last ref from kref_init */ |
---|
1739 | 1760 | return retval; |
---|
1740 | | - |
---|
1741 | | -e_nomem: |
---|
1742 | | - retval = -ENOMEM; |
---|
1743 | | - goto put_ref; |
---|
1744 | 1761 | } |
---|
1745 | 1762 | |
---|
1746 | 1763 | static void ufx_usb_disconnect(struct usb_interface *interface) |
---|
1747 | 1764 | { |
---|
1748 | 1765 | struct ufx_data *dev; |
---|
| 1766 | + struct fb_info *info; |
---|
| 1767 | + |
---|
| 1768 | + mutex_lock(&disconnect_mutex); |
---|
1749 | 1769 | |
---|
1750 | 1770 | dev = usb_get_intfdata(interface); |
---|
| 1771 | + info = dev->info; |
---|
1751 | 1772 | |
---|
1752 | 1773 | pr_debug("USB disconnect starting\n"); |
---|
1753 | 1774 | |
---|
.. | .. |
---|
1761 | 1782 | |
---|
1762 | 1783 | /* if clients still have us open, will be freed on last close */ |
---|
1763 | 1784 | if (dev->fb_count == 0) |
---|
1764 | | - schedule_delayed_work(&dev->free_framebuffer_work, 0); |
---|
| 1785 | + ufx_free_framebuffer(dev); |
---|
1765 | 1786 | |
---|
1766 | | - /* release reference taken by kref_init in probe() */ |
---|
1767 | | - kref_put(&dev->kref, ufx_free); |
---|
| 1787 | + /* this function will wait for all in-flight urbs to complete */ |
---|
| 1788 | + if (dev->urbs.count > 0) |
---|
| 1789 | + ufx_free_urb_list(dev); |
---|
1768 | 1790 | |
---|
1769 | | - /* consider ufx_data freed */ |
---|
| 1791 | + pr_debug("freeing ufx_data %p", dev); |
---|
| 1792 | + |
---|
| 1793 | + unregister_framebuffer(info); |
---|
| 1794 | + |
---|
| 1795 | + mutex_unlock(&disconnect_mutex); |
---|
1770 | 1796 | } |
---|
1771 | 1797 | |
---|
1772 | 1798 | static struct usb_driver ufx_driver = { |
---|