root/drivers/gpu/drm/drm_dp_aux_dev.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. drm_dp_aux_dev_get_by_minor
  2. alloc_drm_dp_aux_dev
  3. release_drm_dp_aux_dev
  4. name_show
  5. auxdev_open
  6. auxdev_llseek
  7. auxdev_read_iter
  8. auxdev_write_iter
  9. auxdev_release
  10. drm_dp_aux_dev_get_by_aux
  11. drm_dp_aux_unregister_devnode
  12. drm_dp_aux_register_devnode
  13. drm_dp_aux_dev_init
  14. drm_dp_aux_dev_exit

   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/init.h>
  31 #include <linux/kernel.h>
  32 #include <linux/module.h>
  33 #include <linux/sched/signal.h>
  34 #include <linux/slab.h>
  35 #include <linux/uaccess.h>
  36 #include <linux/uio.h>
  37 
  38 #include <drm/drm_crtc.h>
  39 #include <drm/drm_dp_helper.h>
  40 #include <drm/drm_dp_mst_helper.h>
  41 #include <drm/drm_print.h>
  42 
  43 #include "drm_crtc_helper_internal.h"
  44 
  45 struct drm_dp_aux_dev {
  46         unsigned index;
  47         struct drm_dp_aux *aux;
  48         struct device *dev;
  49         struct kref refcount;
  50         atomic_t usecount;
  51 };
  52 
  53 #define DRM_AUX_MINORS  256
  54 #define AUX_MAX_OFFSET  (1 << 20)
  55 static DEFINE_IDR(aux_idr);
  56 static DEFINE_MUTEX(aux_idr_mutex);
  57 static struct class *drm_dp_aux_dev_class;
  58 static int drm_dev_major = -1;
  59 
  60 static struct drm_dp_aux_dev *drm_dp_aux_dev_get_by_minor(unsigned index)
  61 {
  62         struct drm_dp_aux_dev *aux_dev = NULL;
  63 
  64         mutex_lock(&aux_idr_mutex);
  65         aux_dev = idr_find(&aux_idr, index);
  66         if (!kref_get_unless_zero(&aux_dev->refcount))
  67                 aux_dev = NULL;
  68         mutex_unlock(&aux_idr_mutex);
  69 
  70         return aux_dev;
  71 }
  72 
  73 static struct drm_dp_aux_dev *alloc_drm_dp_aux_dev(struct drm_dp_aux *aux)
  74 {
  75         struct drm_dp_aux_dev *aux_dev;
  76         int index;
  77 
  78         aux_dev = kzalloc(sizeof(*aux_dev), GFP_KERNEL);
  79         if (!aux_dev)
  80                 return ERR_PTR(-ENOMEM);
  81         aux_dev->aux = aux;
  82         atomic_set(&aux_dev->usecount, 1);
  83         kref_init(&aux_dev->refcount);
  84 
  85         mutex_lock(&aux_idr_mutex);
  86         index = idr_alloc(&aux_idr, aux_dev, 0, DRM_AUX_MINORS, GFP_KERNEL);
  87         mutex_unlock(&aux_idr_mutex);
  88         if (index < 0) {
  89                 kfree(aux_dev);
  90                 return ERR_PTR(index);
  91         }
  92         aux_dev->index = index;
  93 
  94         return aux_dev;
  95 }
  96 
  97 static void release_drm_dp_aux_dev(struct kref *ref)
  98 {
  99         struct drm_dp_aux_dev *aux_dev =
 100                 container_of(ref, struct drm_dp_aux_dev, refcount);
 101 
 102         kfree(aux_dev);
 103 }
 104 
 105 static ssize_t name_show(struct device *dev,
 106                          struct device_attribute *attr, char *buf)
 107 {
 108         ssize_t res;
 109         struct drm_dp_aux_dev *aux_dev =
 110                 drm_dp_aux_dev_get_by_minor(MINOR(dev->devt));
 111 
 112         if (!aux_dev)
 113                 return -ENODEV;
 114 
 115         res = sprintf(buf, "%s\n", aux_dev->aux->name);
 116         kref_put(&aux_dev->refcount, release_drm_dp_aux_dev);
 117 
 118         return res;
 119 }
 120 static DEVICE_ATTR_RO(name);
 121 
 122 static struct attribute *drm_dp_aux_attrs[] = {
 123         &dev_attr_name.attr,
 124         NULL,
 125 };
 126 ATTRIBUTE_GROUPS(drm_dp_aux);
 127 
 128 static int auxdev_open(struct inode *inode, struct file *file)
 129 {
 130         unsigned int minor = iminor(inode);
 131         struct drm_dp_aux_dev *aux_dev;
 132 
 133         aux_dev = drm_dp_aux_dev_get_by_minor(minor);
 134         if (!aux_dev)
 135                 return -ENODEV;
 136 
 137         file->private_data = aux_dev;
 138         return 0;
 139 }
 140 
 141 static loff_t auxdev_llseek(struct file *file, loff_t offset, int whence)
 142 {
 143         return fixed_size_llseek(file, offset, whence, AUX_MAX_OFFSET);
 144 }
 145 
 146 static ssize_t auxdev_read_iter(struct kiocb *iocb, struct iov_iter *to)
 147 {
 148         struct drm_dp_aux_dev *aux_dev = iocb->ki_filp->private_data;
 149         loff_t pos = iocb->ki_pos;
 150         ssize_t res = 0;
 151 
 152         if (!atomic_inc_not_zero(&aux_dev->usecount))
 153                 return -ENODEV;
 154 
 155         iov_iter_truncate(to, AUX_MAX_OFFSET - pos);
 156 
 157         while (iov_iter_count(to)) {
 158                 uint8_t buf[DP_AUX_MAX_PAYLOAD_BYTES];
 159                 ssize_t todo = min(iov_iter_count(to), sizeof(buf));
 160 
 161                 if (signal_pending(current)) {
 162                         res = -ERESTARTSYS;
 163                         break;
 164                 }
 165 
 166                 if (aux_dev->aux->is_remote)
 167                         res = drm_dp_mst_dpcd_read(aux_dev->aux, pos, buf,
 168                                                    todo);
 169                 else
 170                         res = drm_dp_dpcd_read(aux_dev->aux, pos, buf, todo);
 171 
 172                 if (res <= 0)
 173                         break;
 174 
 175                 if (copy_to_iter(buf, res, to) != res) {
 176                         res = -EFAULT;
 177                         break;
 178                 }
 179 
 180                 pos += res;
 181         }
 182 
 183         if (pos != iocb->ki_pos)
 184                 res = pos - iocb->ki_pos;
 185         iocb->ki_pos = pos;
 186 
 187         if (atomic_dec_and_test(&aux_dev->usecount))
 188                 wake_up_var(&aux_dev->usecount);
 189 
 190         return res;
 191 }
 192 
 193 static ssize_t auxdev_write_iter(struct kiocb *iocb, struct iov_iter *from)
 194 {
 195         struct drm_dp_aux_dev *aux_dev = iocb->ki_filp->private_data;
 196         loff_t pos = iocb->ki_pos;
 197         ssize_t res = 0;
 198 
 199         if (!atomic_inc_not_zero(&aux_dev->usecount))
 200                 return -ENODEV;
 201 
 202         iov_iter_truncate(from, AUX_MAX_OFFSET - pos);
 203 
 204         while (iov_iter_count(from)) {
 205                 uint8_t buf[DP_AUX_MAX_PAYLOAD_BYTES];
 206                 ssize_t todo = min(iov_iter_count(from), sizeof(buf));
 207 
 208                 if (signal_pending(current)) {
 209                         res = -ERESTARTSYS;
 210                         break;
 211                 }
 212 
 213                 if (!copy_from_iter_full(buf, todo, from)) {
 214                         res = -EFAULT;
 215                         break;
 216                 }
 217 
 218                 if (aux_dev->aux->is_remote)
 219                         res = drm_dp_mst_dpcd_write(aux_dev->aux, pos, buf,
 220                                                     todo);
 221                 else
 222                         res = drm_dp_dpcd_write(aux_dev->aux, pos, buf, todo);
 223 
 224                 if (res <= 0)
 225                         break;
 226 
 227                 pos += res;
 228         }
 229 
 230         if (pos != iocb->ki_pos)
 231                 res = pos - iocb->ki_pos;
 232         iocb->ki_pos = pos;
 233 
 234         if (atomic_dec_and_test(&aux_dev->usecount))
 235                 wake_up_var(&aux_dev->usecount);
 236 
 237         return res;
 238 }
 239 
 240 static int auxdev_release(struct inode *inode, struct file *file)
 241 {
 242         struct drm_dp_aux_dev *aux_dev = file->private_data;
 243 
 244         kref_put(&aux_dev->refcount, release_drm_dp_aux_dev);
 245         return 0;
 246 }
 247 
 248 static const struct file_operations auxdev_fops = {
 249         .owner          = THIS_MODULE,
 250         .llseek         = auxdev_llseek,
 251         .read_iter      = auxdev_read_iter,
 252         .write_iter     = auxdev_write_iter,
 253         .open           = auxdev_open,
 254         .release        = auxdev_release,
 255 };
 256 
 257 #define to_auxdev(d) container_of(d, struct drm_dp_aux_dev, aux)
 258 
 259 static struct drm_dp_aux_dev *drm_dp_aux_dev_get_by_aux(struct drm_dp_aux *aux)
 260 {
 261         struct drm_dp_aux_dev *iter, *aux_dev = NULL;
 262         int id;
 263 
 264         /* don't increase kref count here because this function should only be
 265          * used by drm_dp_aux_unregister_devnode. Thus, it will always have at
 266          * least one reference - the one that drm_dp_aux_register_devnode
 267          * created
 268          */
 269         mutex_lock(&aux_idr_mutex);
 270         idr_for_each_entry(&aux_idr, iter, id) {
 271                 if (iter->aux == aux) {
 272                         aux_dev = iter;
 273                         break;
 274                 }
 275         }
 276         mutex_unlock(&aux_idr_mutex);
 277         return aux_dev;
 278 }
 279 
 280 void drm_dp_aux_unregister_devnode(struct drm_dp_aux *aux)
 281 {
 282         struct drm_dp_aux_dev *aux_dev;
 283         unsigned int minor;
 284 
 285         aux_dev = drm_dp_aux_dev_get_by_aux(aux);
 286         if (!aux_dev) /* attach must have failed */
 287                 return;
 288 
 289         mutex_lock(&aux_idr_mutex);
 290         idr_remove(&aux_idr, aux_dev->index);
 291         mutex_unlock(&aux_idr_mutex);
 292 
 293         atomic_dec(&aux_dev->usecount);
 294         wait_var_event(&aux_dev->usecount, !atomic_read(&aux_dev->usecount));
 295 
 296         minor = aux_dev->index;
 297         if (aux_dev->dev)
 298                 device_destroy(drm_dp_aux_dev_class,
 299                                MKDEV(drm_dev_major, minor));
 300 
 301         DRM_DEBUG("drm_dp_aux_dev: aux [%s] unregistering\n", aux->name);
 302         kref_put(&aux_dev->refcount, release_drm_dp_aux_dev);
 303 }
 304 
 305 int drm_dp_aux_register_devnode(struct drm_dp_aux *aux)
 306 {
 307         struct drm_dp_aux_dev *aux_dev;
 308         int res;
 309 
 310         aux_dev = alloc_drm_dp_aux_dev(aux);
 311         if (IS_ERR(aux_dev))
 312                 return PTR_ERR(aux_dev);
 313 
 314         aux_dev->dev = device_create(drm_dp_aux_dev_class, aux->dev,
 315                                      MKDEV(drm_dev_major, aux_dev->index), NULL,
 316                                      "drm_dp_aux%d", aux_dev->index);
 317         if (IS_ERR(aux_dev->dev)) {
 318                 res = PTR_ERR(aux_dev->dev);
 319                 aux_dev->dev = NULL;
 320                 goto error;
 321         }
 322 
 323         DRM_DEBUG("drm_dp_aux_dev: aux [%s] registered as minor %d\n",
 324                   aux->name, aux_dev->index);
 325         return 0;
 326 error:
 327         drm_dp_aux_unregister_devnode(aux);
 328         return res;
 329 }
 330 
 331 int drm_dp_aux_dev_init(void)
 332 {
 333         int res;
 334 
 335         drm_dp_aux_dev_class = class_create(THIS_MODULE, "drm_dp_aux_dev");
 336         if (IS_ERR(drm_dp_aux_dev_class)) {
 337                 return PTR_ERR(drm_dp_aux_dev_class);
 338         }
 339         drm_dp_aux_dev_class->dev_groups = drm_dp_aux_groups;
 340 
 341         res = register_chrdev(0, "aux", &auxdev_fops);
 342         if (res < 0)
 343                 goto out;
 344         drm_dev_major = res;
 345 
 346         return 0;
 347 out:
 348         class_destroy(drm_dp_aux_dev_class);
 349         return res;
 350 }
 351 
 352 void drm_dp_aux_dev_exit(void)
 353 {
 354         unregister_chrdev(drm_dev_major, "aux");
 355         class_destroy(drm_dp_aux_dev_class);
 356 }

/* [<][>][^][v][top][bottom][index][help] */