linux/drivers/media/v4l2-core/v4l2-async.c
<<
>>
Prefs
   1/*
   2 * V4L2 asynchronous subdevice registration API
   3 *
   4 * Copyright (C) 2012-2013, Guennadi Liakhovetski <g.liakhovetski@gmx.de>
   5 *
   6 * This program is free software; you can redistribute it and/or modify
   7 * it under the terms of the GNU General Public License version 2 as
   8 * published by the Free Software Foundation.
   9 */
  10
  11#include <linux/device.h>
  12#include <linux/err.h>
  13#include <linux/i2c.h>
  14#include <linux/list.h>
  15#include <linux/module.h>
  16#include <linux/mutex.h>
  17#include <linux/platform_device.h>
  18#include <linux/slab.h>
  19#include <linux/types.h>
  20
  21#include <media/v4l2-async.h>
  22#include <media/v4l2-device.h>
  23#include <media/v4l2-subdev.h>
  24
  25static bool match_i2c(struct device *dev, struct v4l2_async_subdev *asd)
  26{
  27#if IS_ENABLED(CONFIG_I2C)
  28        struct i2c_client *client = i2c_verify_client(dev);
  29        return client &&
  30                asd->match.i2c.adapter_id == client->adapter->nr &&
  31                asd->match.i2c.address == client->addr;
  32#else
  33        return false;
  34#endif
  35}
  36
  37static bool match_devname(struct device *dev, struct v4l2_async_subdev *asd)
  38{
  39        return !strcmp(asd->match.device_name.name, dev_name(dev));
  40}
  41
  42static bool match_of(struct device *dev, struct v4l2_async_subdev *asd)
  43{
  44        return dev->of_node == asd->match.of.node;
  45}
  46
  47static LIST_HEAD(subdev_list);
  48static LIST_HEAD(notifier_list);
  49static DEFINE_MUTEX(list_lock);
  50
  51static struct v4l2_async_subdev *v4l2_async_belongs(struct v4l2_async_notifier *notifier,
  52                                                    struct v4l2_subdev *sd)
  53{
  54        struct v4l2_async_subdev *asd;
  55        bool (*match)(struct device *, struct v4l2_async_subdev *);
  56
  57        list_for_each_entry(asd, &notifier->waiting, list) {
  58                /* bus_type has been verified valid before */
  59                switch (asd->match_type) {
  60                case V4L2_ASYNC_MATCH_CUSTOM:
  61                        match = asd->match.custom.match;
  62                        if (!match)
  63                                /* Match always */
  64                                return asd;
  65                        break;
  66                case V4L2_ASYNC_MATCH_DEVNAME:
  67                        match = match_devname;
  68                        break;
  69                case V4L2_ASYNC_MATCH_I2C:
  70                        match = match_i2c;
  71                        break;
  72                case V4L2_ASYNC_MATCH_OF:
  73                        match = match_of;
  74                        break;
  75                default:
  76                        /* Cannot happen, unless someone breaks us */
  77                        WARN_ON(true);
  78                        return NULL;
  79                }
  80
  81                /* match cannot be NULL here */
  82                if (match(sd->dev, asd))
  83                        return asd;
  84        }
  85
  86        return NULL;
  87}
  88
  89static int v4l2_async_test_notify(struct v4l2_async_notifier *notifier,
  90                                  struct v4l2_subdev *sd,
  91                                  struct v4l2_async_subdev *asd)
  92{
  93        int ret;
  94
  95        /* Remove from the waiting list */
  96        list_del(&asd->list);
  97        sd->asd = asd;
  98        sd->notifier = notifier;
  99
 100        if (notifier->bound) {
 101                ret = notifier->bound(notifier, sd, asd);
 102                if (ret < 0)
 103                        return ret;
 104        }
 105        /* Move from the global subdevice list to notifier's done */
 106        list_move(&sd->async_list, &notifier->done);
 107
 108        ret = v4l2_device_register_subdev(notifier->v4l2_dev, sd);
 109        if (ret < 0) {
 110                if (notifier->unbind)
 111                        notifier->unbind(notifier, sd, asd);
 112                return ret;
 113        }
 114
 115        if (list_empty(&notifier->waiting) && notifier->complete)
 116                return notifier->complete(notifier);
 117
 118        return 0;
 119}
 120
 121static void v4l2_async_cleanup(struct v4l2_subdev *sd)
 122{
 123        v4l2_device_unregister_subdev(sd);
 124        /* Subdevice driver will reprobe and put the subdev back onto the list */
 125        list_del_init(&sd->async_list);
 126        sd->asd = NULL;
 127        sd->dev = NULL;
 128}
 129
 130int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev,
 131                                 struct v4l2_async_notifier *notifier)
 132{
 133        struct v4l2_subdev *sd, *tmp;
 134        struct v4l2_async_subdev *asd;
 135        int i;
 136
 137        if (!notifier->num_subdevs || notifier->num_subdevs > V4L2_MAX_SUBDEVS)
 138                return -EINVAL;
 139
 140        notifier->v4l2_dev = v4l2_dev;
 141        INIT_LIST_HEAD(&notifier->waiting);
 142        INIT_LIST_HEAD(&notifier->done);
 143
 144        for (i = 0; i < notifier->num_subdevs; i++) {
 145                asd = notifier->subdevs[i];
 146
 147                switch (asd->match_type) {
 148                case V4L2_ASYNC_MATCH_CUSTOM:
 149                case V4L2_ASYNC_MATCH_DEVNAME:
 150                case V4L2_ASYNC_MATCH_I2C:
 151                case V4L2_ASYNC_MATCH_OF:
 152                        break;
 153                default:
 154                        dev_err(notifier->v4l2_dev ? notifier->v4l2_dev->dev : NULL,
 155                                "Invalid match type %u on %p\n",
 156                                asd->match_type, asd);
 157                        return -EINVAL;
 158                }
 159                list_add_tail(&asd->list, &notifier->waiting);
 160        }
 161
 162        mutex_lock(&list_lock);
 163
 164        /* Keep also completed notifiers on the list */
 165        list_add(&notifier->list, &notifier_list);
 166
 167        list_for_each_entry_safe(sd, tmp, &subdev_list, async_list) {
 168                int ret;
 169
 170                asd = v4l2_async_belongs(notifier, sd);
 171                if (!asd)
 172                        continue;
 173
 174                ret = v4l2_async_test_notify(notifier, sd, asd);
 175                if (ret < 0) {
 176                        mutex_unlock(&list_lock);
 177                        return ret;
 178                }
 179        }
 180
 181        mutex_unlock(&list_lock);
 182
 183        return 0;
 184}
 185EXPORT_SYMBOL(v4l2_async_notifier_register);
 186
 187void v4l2_async_notifier_unregister(struct v4l2_async_notifier *notifier)
 188{
 189        struct v4l2_subdev *sd, *tmp;
 190        unsigned int notif_n_subdev = notifier->num_subdevs;
 191        unsigned int n_subdev = min(notif_n_subdev, V4L2_MAX_SUBDEVS);
 192        struct device *dev[n_subdev];
 193        int i = 0;
 194
 195        if (!notifier->v4l2_dev)
 196                return;
 197
 198        mutex_lock(&list_lock);
 199
 200        list_del(&notifier->list);
 201
 202        list_for_each_entry_safe(sd, tmp, &notifier->done, async_list) {
 203                dev[i] = get_device(sd->dev);
 204
 205                v4l2_async_cleanup(sd);
 206
 207                /* If we handled USB devices, we'd have to lock the parent too */
 208                device_release_driver(dev[i++]);
 209
 210                if (notifier->unbind)
 211                        notifier->unbind(notifier, sd, sd->asd);
 212        }
 213
 214        mutex_unlock(&list_lock);
 215
 216        while (i--) {
 217                struct device *d = dev[i];
 218
 219                if (d && device_attach(d) < 0) {
 220                        const char *name = "(none)";
 221                        int lock = device_trylock(d);
 222
 223                        if (lock && d->driver)
 224                                name = d->driver->name;
 225                        dev_err(d, "Failed to re-probe to %s\n", name);
 226                        if (lock)
 227                                device_unlock(d);
 228                }
 229                put_device(d);
 230        }
 231
 232        notifier->v4l2_dev = NULL;
 233
 234        /*
 235         * Don't care about the waiting list, it is initialised and populated
 236         * upon notifier registration.
 237         */
 238}
 239EXPORT_SYMBOL(v4l2_async_notifier_unregister);
 240
 241int v4l2_async_register_subdev(struct v4l2_subdev *sd)
 242{
 243        struct v4l2_async_notifier *notifier;
 244
 245        mutex_lock(&list_lock);
 246
 247        INIT_LIST_HEAD(&sd->async_list);
 248
 249        list_for_each_entry(notifier, &notifier_list, list) {
 250                struct v4l2_async_subdev *asd = v4l2_async_belongs(notifier, sd);
 251                if (asd) {
 252                        int ret = v4l2_async_test_notify(notifier, sd, asd);
 253                        mutex_unlock(&list_lock);
 254                        return ret;
 255                }
 256        }
 257
 258        /* None matched, wait for hot-plugging */
 259        list_add(&sd->async_list, &subdev_list);
 260
 261        mutex_unlock(&list_lock);
 262
 263        return 0;
 264}
 265EXPORT_SYMBOL(v4l2_async_register_subdev);
 266
 267void v4l2_async_unregister_subdev(struct v4l2_subdev *sd)
 268{
 269        struct v4l2_async_notifier *notifier = sd->notifier;
 270
 271        if (!sd->asd) {
 272                if (!list_empty(&sd->async_list))
 273                        v4l2_async_cleanup(sd);
 274                return;
 275        }
 276
 277        mutex_lock(&list_lock);
 278
 279        list_add(&sd->asd->list, &notifier->waiting);
 280
 281        v4l2_async_cleanup(sd);
 282
 283        if (notifier->unbind)
 284                notifier->unbind(notifier, sd, sd->asd);
 285
 286        mutex_unlock(&list_lock);
 287}
 288EXPORT_SYMBOL(v4l2_async_unregister_subdev);
 289