| .. | .. |
|---|
| 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 |
|---|
| .. | .. |
|---|
| 1653 | 1660 | |
|---|
| 1654 | 1661 | /* allocates framebuffer driver structure, not framebuffer memory */ |
|---|
| 1655 | 1662 | info = framebuffer_alloc(0, &usbdev->dev); |
|---|
| 1656 | | - if (!info) { |
|---|
| 1657 | | - dev_err(dev->gdev, "framebuffer_alloc failed\n"); |
|---|
| 1663 | + if (!info) |
|---|
| 1658 | 1664 | goto e_nomem; |
|---|
| 1659 | | - } |
|---|
| 1660 | 1665 | |
|---|
| 1661 | 1666 | dev->info = info; |
|---|
| 1662 | 1667 | info->par = dev; |
|---|
| 1663 | 1668 | info->pseudo_palette = dev->pseudo_palette; |
|---|
| 1664 | 1669 | info->fbops = &ufx_ops; |
|---|
| 1670 | + INIT_LIST_HEAD(&info->modelist); |
|---|
| 1665 | 1671 | |
|---|
| 1666 | 1672 | retval = fb_alloc_cmap(&info->cmap, 256, 0); |
|---|
| 1667 | 1673 | if (retval < 0) { |
|---|
| 1668 | 1674 | dev_err(dev->gdev, "fb_alloc_cmap failed %x\n", retval); |
|---|
| 1669 | 1675 | goto destroy_modedb; |
|---|
| 1670 | 1676 | } |
|---|
| 1671 | | - |
|---|
| 1672 | | - INIT_DELAYED_WORK(&dev->free_framebuffer_work, |
|---|
| 1673 | | - ufx_free_framebuffer_work); |
|---|
| 1674 | | - |
|---|
| 1675 | | - INIT_LIST_HEAD(&info->modelist); |
|---|
| 1676 | 1677 | |
|---|
| 1677 | 1678 | retval = ufx_reg_read(dev, 0x3000, &id_rev); |
|---|
| 1678 | 1679 | check_warn_goto_error(retval, "error %d reading 0x3000 register from device", retval); |
|---|
| .. | .. |
|---|
| 1746 | 1747 | static void ufx_usb_disconnect(struct usb_interface *interface) |
|---|
| 1747 | 1748 | { |
|---|
| 1748 | 1749 | struct ufx_data *dev; |
|---|
| 1750 | + struct fb_info *info; |
|---|
| 1751 | + |
|---|
| 1752 | + mutex_lock(&disconnect_mutex); |
|---|
| 1749 | 1753 | |
|---|
| 1750 | 1754 | dev = usb_get_intfdata(interface); |
|---|
| 1755 | + info = dev->info; |
|---|
| 1751 | 1756 | |
|---|
| 1752 | 1757 | pr_debug("USB disconnect starting\n"); |
|---|
| 1753 | 1758 | |
|---|
| .. | .. |
|---|
| 1761 | 1766 | |
|---|
| 1762 | 1767 | /* if clients still have us open, will be freed on last close */ |
|---|
| 1763 | 1768 | if (dev->fb_count == 0) |
|---|
| 1764 | | - schedule_delayed_work(&dev->free_framebuffer_work, 0); |
|---|
| 1769 | + ufx_free_framebuffer(dev); |
|---|
| 1765 | 1770 | |
|---|
| 1766 | | - /* release reference taken by kref_init in probe() */ |
|---|
| 1767 | | - kref_put(&dev->kref, ufx_free); |
|---|
| 1771 | + /* this function will wait for all in-flight urbs to complete */ |
|---|
| 1772 | + if (dev->urbs.count > 0) |
|---|
| 1773 | + ufx_free_urb_list(dev); |
|---|
| 1768 | 1774 | |
|---|
| 1769 | | - /* consider ufx_data freed */ |
|---|
| 1775 | + pr_debug("freeing ufx_data %p", dev); |
|---|
| 1776 | + |
|---|
| 1777 | + unregister_framebuffer(info); |
|---|
| 1778 | + |
|---|
| 1779 | + mutex_unlock(&disconnect_mutex); |
|---|
| 1770 | 1780 | } |
|---|
| 1771 | 1781 | |
|---|
| 1772 | 1782 | static struct usb_driver ufx_driver = { |
|---|