1 /* $NetBSD: drm_dp_aux_dev.c,v 1.3 2021/12/19 11:06:54 riastradh Exp $ */ 2 3 /* 4 * Copyright © 2015 Intel Corporation 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a 7 * copy of this software and associated documentation files (the "Software"), 8 * to deal in the Software without restriction, including without limitation 9 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 * and/or sell copies of the Software, and to permit persons to whom the 11 * Software is furnished to do so, subject to the following conditions: 12 * 13 * The above copyright notice and this permission notice (including the next 14 * paragraph) shall be included in all copies or substantial portions of the 15 * Software. 16 * 17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 20 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 22 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 23 * IN THE SOFTWARE. 24 * 25 * Authors: 26 * Rafael Antognolli <rafael.antognolli@intel.com> 27 * 28 */ 29 30 #include <sys/cdefs.h> 31 __KERNEL_RCSID(0, "$NetBSD: drm_dp_aux_dev.c,v 1.3 2021/12/19 11:06:54 riastradh Exp $"); 32 33 #include <linux/device.h> 34 #include <linux/fs.h> 35 #include <linux/init.h> 36 #include <linux/kernel.h> 37 #include <linux/module.h> 38 #include <linux/sched/signal.h> 39 #include <linux/slab.h> 40 #include <linux/uaccess.h> 41 #include <linux/uio.h> 42 43 #include <drm/drm_crtc.h> 44 #include <drm/drm_dp_helper.h> 45 #include <drm/drm_dp_mst_helper.h> 46 #include <drm/drm_print.h> 47 48 #include "drm_crtc_helper_internal.h" 49 50 struct drm_dp_aux_dev { 51 unsigned index; 52 struct drm_dp_aux *aux; 53 struct device *dev; 54 struct kref refcount; 55 atomic_t usecount; 56 }; 57 58 #define DRM_AUX_MINORS 256 59 #define AUX_MAX_OFFSET (1 << 20) 60 static DEFINE_IDR(aux_idr); 61 static DEFINE_MUTEX(aux_idr_mutex); 62 static struct class *drm_dp_aux_dev_class; 63 static int drm_dev_major = -1; 64 65 static struct drm_dp_aux_dev *drm_dp_aux_dev_get_by_minor(unsigned index) 66 { 67 struct drm_dp_aux_dev *aux_dev = NULL; 68 69 mutex_lock(&aux_idr_mutex); 70 aux_dev = idr_find(&aux_idr, index); 71 if (!kref_get_unless_zero(&aux_dev->refcount)) 72 aux_dev = NULL; 73 mutex_unlock(&aux_idr_mutex); 74 75 return aux_dev; 76 } 77 78 static struct drm_dp_aux_dev *alloc_drm_dp_aux_dev(struct drm_dp_aux *aux) 79 { 80 struct drm_dp_aux_dev *aux_dev; 81 int index; 82 83 aux_dev = kzalloc(sizeof(*aux_dev), GFP_KERNEL); 84 if (!aux_dev) 85 return ERR_PTR(-ENOMEM); 86 aux_dev->aux = aux; 87 atomic_set(&aux_dev->usecount, 1); 88 kref_init(&aux_dev->refcount); 89 90 idr_preload(GFP_KERNEL); 91 mutex_lock(&aux_idr_mutex); 92 index = idr_alloc(&aux_idr, aux_dev, 0, DRM_AUX_MINORS, GFP_KERNEL); 93 mutex_unlock(&aux_idr_mutex); 94 idr_preload_end(); 95 if (index < 0) { 96 kfree(aux_dev); 97 return ERR_PTR(index); 98 } 99 aux_dev->index = index; 100 101 return aux_dev; 102 } 103 104 static void release_drm_dp_aux_dev(struct kref *ref) 105 { 106 struct drm_dp_aux_dev *aux_dev = 107 container_of(ref, struct drm_dp_aux_dev, refcount); 108 109 kfree(aux_dev); 110 } 111 112 static ssize_t name_show(struct device *dev, 113 struct device_attribute *attr, char *buf) 114 { 115 ssize_t res; 116 struct drm_dp_aux_dev *aux_dev = 117 drm_dp_aux_dev_get_by_minor(MINOR(dev->devt)); 118 119 if (!aux_dev) 120 return -ENODEV; 121 122 res = sprintf(buf, "%s\n", aux_dev->aux->name); 123 kref_put(&aux_dev->refcount, release_drm_dp_aux_dev); 124 125 return res; 126 } 127 static DEVICE_ATTR_RO(name); 128 129 static struct attribute *drm_dp_aux_attrs[] = { 130 &dev_attr_name.attr, 131 NULL, 132 }; 133 ATTRIBUTE_GROUPS(drm_dp_aux); 134 135 static int auxdev_open(struct inode *inode, struct file *file) 136 { 137 unsigned int minor = iminor(inode); 138 struct drm_dp_aux_dev *aux_dev; 139 140 aux_dev = drm_dp_aux_dev_get_by_minor(minor); 141 if (!aux_dev) 142 return -ENODEV; 143 144 file->private_data = aux_dev; 145 return 0; 146 } 147 148 static loff_t auxdev_llseek(struct file *file, loff_t offset, int whence) 149 { 150 return fixed_size_llseek(file, offset, whence, AUX_MAX_OFFSET); 151 } 152 153 static ssize_t auxdev_read_iter(struct kiocb *iocb, struct iov_iter *to) 154 { 155 struct drm_dp_aux_dev *aux_dev = iocb->ki_filp->private_data; 156 loff_t pos = iocb->ki_pos; 157 ssize_t res = 0; 158 159 if (!atomic_inc_not_zero(&aux_dev->usecount)) 160 return -ENODEV; 161 162 iov_iter_truncate(to, AUX_MAX_OFFSET - pos); 163 164 while (iov_iter_count(to)) { 165 uint8_t buf[DP_AUX_MAX_PAYLOAD_BYTES]; 166 ssize_t todo = min(iov_iter_count(to), sizeof(buf)); 167 168 if (signal_pending(current)) { 169 res = -ERESTARTSYS; 170 break; 171 } 172 173 res = drm_dp_dpcd_read(aux_dev->aux, pos, buf, todo); 174 175 if (res <= 0) 176 break; 177 178 if (copy_to_iter(buf, res, to) != res) { 179 res = -EFAULT; 180 break; 181 } 182 183 pos += res; 184 } 185 186 if (pos != iocb->ki_pos) 187 res = pos - iocb->ki_pos; 188 iocb->ki_pos = pos; 189 190 if (atomic_dec_and_test(&aux_dev->usecount)) 191 wake_up_var(&aux_dev->usecount); 192 193 return res; 194 } 195 196 static ssize_t auxdev_write_iter(struct kiocb *iocb, struct iov_iter *from) 197 { 198 struct drm_dp_aux_dev *aux_dev = iocb->ki_filp->private_data; 199 loff_t pos = iocb->ki_pos; 200 ssize_t res = 0; 201 202 if (!atomic_inc_not_zero(&aux_dev->usecount)) 203 return -ENODEV; 204 205 iov_iter_truncate(from, AUX_MAX_OFFSET - pos); 206 207 while (iov_iter_count(from)) { 208 uint8_t buf[DP_AUX_MAX_PAYLOAD_BYTES]; 209 ssize_t todo = min(iov_iter_count(from), sizeof(buf)); 210 211 if (signal_pending(current)) { 212 res = -ERESTARTSYS; 213 break; 214 } 215 216 if (!copy_from_iter_full(buf, todo, from)) { 217 res = -EFAULT; 218 break; 219 } 220 221 res = drm_dp_dpcd_write(aux_dev->aux, pos, buf, todo); 222 223 if (res <= 0) 224 break; 225 226 pos += res; 227 } 228 229 if (pos != iocb->ki_pos) 230 res = pos - iocb->ki_pos; 231 iocb->ki_pos = pos; 232 233 if (atomic_dec_and_test(&aux_dev->usecount)) 234 wake_up_var(&aux_dev->usecount); 235 236 return res; 237 } 238 239 static int auxdev_release(struct inode *inode, struct file *file) 240 { 241 struct drm_dp_aux_dev *aux_dev = file->private_data; 242 243 kref_put(&aux_dev->refcount, release_drm_dp_aux_dev); 244 return 0; 245 } 246 247 static const struct file_operations auxdev_fops = { 248 .owner = THIS_MODULE, 249 .llseek = auxdev_llseek, 250 .read_iter = auxdev_read_iter, 251 .write_iter = auxdev_write_iter, 252 .open = auxdev_open, 253 .release = auxdev_release, 254 }; 255 256 #define to_auxdev(d) container_of(d, struct drm_dp_aux_dev, aux) 257 258 static struct drm_dp_aux_dev *drm_dp_aux_dev_get_by_aux(struct drm_dp_aux *aux) 259 { 260 struct drm_dp_aux_dev *iter, *aux_dev = NULL; 261 int id; 262 263 /* don't increase kref count here because this function should only be 264 * used by drm_dp_aux_unregister_devnode. Thus, it will always have at 265 * least one reference - the one that drm_dp_aux_register_devnode 266 * created 267 */ 268 mutex_lock(&aux_idr_mutex); 269 idr_for_each_entry(&aux_idr, iter, id) { 270 if (iter->aux == aux) { 271 aux_dev = iter; 272 break; 273 } 274 } 275 mutex_unlock(&aux_idr_mutex); 276 return aux_dev; 277 } 278 279 void drm_dp_aux_unregister_devnode(struct drm_dp_aux *aux) 280 { 281 struct drm_dp_aux_dev *aux_dev; 282 unsigned int minor; 283 284 aux_dev = drm_dp_aux_dev_get_by_aux(aux); 285 if (!aux_dev) /* attach must have failed */ 286 return; 287 288 mutex_lock(&aux_idr_mutex); 289 idr_remove(&aux_idr, aux_dev->index); 290 mutex_unlock(&aux_idr_mutex); 291 292 atomic_dec(&aux_dev->usecount); 293 wait_var_event(&aux_dev->usecount, !atomic_read(&aux_dev->usecount)); 294 295 minor = aux_dev->index; 296 if (aux_dev->dev) 297 device_destroy(drm_dp_aux_dev_class, 298 MKDEV(drm_dev_major, minor)); 299 300 DRM_DEBUG("drm_dp_aux_dev: aux [%s] unregistering\n", aux->name); 301 kref_put(&aux_dev->refcount, release_drm_dp_aux_dev); 302 } 303 304 int drm_dp_aux_register_devnode(struct drm_dp_aux *aux) 305 { 306 struct drm_dp_aux_dev *aux_dev; 307 int res; 308 309 aux_dev = alloc_drm_dp_aux_dev(aux); 310 if (IS_ERR(aux_dev)) 311 return PTR_ERR(aux_dev); 312 313 aux_dev->dev = device_create(drm_dp_aux_dev_class, aux->dev, 314 MKDEV(drm_dev_major, aux_dev->index), NULL, 315 "drm_dp_aux%d", aux_dev->index); 316 if (IS_ERR(aux_dev->dev)) { 317 res = PTR_ERR(aux_dev->dev); 318 aux_dev->dev = NULL; 319 goto error; 320 } 321 322 DRM_DEBUG("drm_dp_aux_dev: aux [%s] registered as minor %d\n", 323 aux->name, aux_dev->index); 324 return 0; 325 error: 326 drm_dp_aux_unregister_devnode(aux); 327 return res; 328 } 329 330 int drm_dp_aux_dev_init(void) 331 { 332 int res; 333 334 drm_dp_aux_dev_class = class_create(THIS_MODULE, "drm_dp_aux_dev"); 335 if (IS_ERR(drm_dp_aux_dev_class)) { 336 return PTR_ERR(drm_dp_aux_dev_class); 337 } 338 drm_dp_aux_dev_class->dev_groups = drm_dp_aux_groups; 339 340 res = register_chrdev(0, "aux", &auxdev_fops); 341 if (res < 0) 342 goto out; 343 drm_dev_major = res; 344 345 return 0; 346 out: 347 class_destroy(drm_dp_aux_dev_class); 348 return res; 349 } 350 351 void drm_dp_aux_dev_exit(void) 352 { 353 unregister_chrdev(drm_dev_major, "aux"); 354 class_destroy(drm_dp_aux_dev_class); 355 } 356