Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
fork

Configure Feed

Select the types of activity you want to include in your feed.

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