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 25 static 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->bus_type == V4L2_ASYNC_BUS_I2C && 31 asd->match.i2c.adapter_id == client->adapter->nr && 32 asd->match.i2c.address == client->addr; 33 #else 34 return false; 35 #endif 36 } 37 38 static bool match_platform(struct device *dev, struct v4l2_async_subdev *asd) 39 { 40 return asd->bus_type == V4L2_ASYNC_BUS_PLATFORM && 41 !strcmp(asd->match.platform.name, dev_name(dev)); 42 } 43 44 static LIST_HEAD(subdev_list); 45 static LIST_HEAD(notifier_list); 46 static DEFINE_MUTEX(list_lock); 47 48 static struct v4l2_async_subdev *v4l2_async_belongs(struct v4l2_async_notifier *notifier, 49 struct v4l2_async_subdev_list *asdl) 50 { 51 struct v4l2_subdev *sd = v4l2_async_to_subdev(asdl); 52 struct v4l2_async_subdev *asd; 53 bool (*match)(struct device *, 54 struct v4l2_async_subdev *); 55 56 list_for_each_entry(asd, ¬ifier->waiting, list) { 57 /* bus_type has been verified valid before */ 58 switch (asd->bus_type) { 59 case V4L2_ASYNC_BUS_CUSTOM: 60 match = asd->match.custom.match; 61 if (!match) 62 /* Match always */ 63 return asd; 64 break; 65 case V4L2_ASYNC_BUS_PLATFORM: 66 match = match_platform; 67 break; 68 case V4L2_ASYNC_BUS_I2C: 69 match = match_i2c; 70 break; 71 default: 72 /* Cannot happen, unless someone breaks us */ 73 WARN_ON(true); 74 return NULL; 75 } 76 77 /* match cannot be NULL here */ 78 if (match(sd->dev, asd)) 79 return asd; 80 } 81 82 return NULL; 83 } 84 85 static int v4l2_async_test_notify(struct v4l2_async_notifier *notifier, 86 struct v4l2_async_subdev_list *asdl, 87 struct v4l2_async_subdev *asd) 88 { 89 struct v4l2_subdev *sd = v4l2_async_to_subdev(asdl); 90 int ret; 91 92 /* Remove from the waiting list */ 93 list_del(&asd->list); 94 asdl->asd = asd; 95 asdl->notifier = notifier; 96 97 if (notifier->bound) { 98 ret = notifier->bound(notifier, sd, asd); 99 if (ret < 0) 100 return ret; 101 } 102 /* Move from the global subdevice list to notifier's done */ 103 list_move(&asdl->list, ¬ifier->done); 104 105 ret = v4l2_device_register_subdev(notifier->v4l2_dev, sd); 106 if (ret < 0) { 107 if (notifier->unbind) 108 notifier->unbind(notifier, sd, asd); 109 return ret; 110 } 111 112 if (list_empty(¬ifier->waiting) && notifier->complete) 113 return notifier->complete(notifier); 114 115 return 0; 116 } 117 118 static void v4l2_async_cleanup(struct v4l2_async_subdev_list *asdl) 119 { 120 struct v4l2_subdev *sd = v4l2_async_to_subdev(asdl); 121 122 v4l2_device_unregister_subdev(sd); 123 /* Subdevice driver will reprobe and put asdl back onto the list */ 124 list_del_init(&asdl->list); 125 asdl->asd = NULL; 126 sd->dev = NULL; 127 } 128 129 int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev, 130 struct v4l2_async_notifier *notifier) 131 { 132 struct v4l2_async_subdev_list *asdl, *tmp; 133 struct v4l2_async_subdev *asd; 134 int i; 135 136 if (!notifier->num_subdevs || notifier->num_subdevs > V4L2_MAX_SUBDEVS) 137 return -EINVAL; 138 139 notifier->v4l2_dev = v4l2_dev; 140 INIT_LIST_HEAD(¬ifier->waiting); 141 INIT_LIST_HEAD(¬ifier->done); 142 143 for (i = 0; i < notifier->num_subdevs; i++) { 144 asd = notifier->subdev[i]; 145 146 switch (asd->bus_type) { 147 case V4L2_ASYNC_BUS_CUSTOM: 148 case V4L2_ASYNC_BUS_PLATFORM: 149 case V4L2_ASYNC_BUS_I2C: 150 break; 151 default: 152 dev_err(notifier->v4l2_dev ? notifier->v4l2_dev->dev : NULL, 153 "Invalid bus-type %u on %p\n", 154 asd->bus_type, asd); 155 return -EINVAL; 156 } 157 list_add_tail(&asd->list, ¬ifier->waiting); 158 } 159 160 mutex_lock(&list_lock); 161 162 /* Keep also completed notifiers on the list */ 163 list_add(¬ifier->list, ¬ifier_list); 164 165 list_for_each_entry_safe(asdl, tmp, &subdev_list, list) { 166 int ret; 167 168 asd = v4l2_async_belongs(notifier, asdl); 169 if (!asd) 170 continue; 171 172 ret = v4l2_async_test_notify(notifier, asdl, asd); 173 if (ret < 0) { 174 mutex_unlock(&list_lock); 175 return ret; 176 } 177 } 178 179 mutex_unlock(&list_lock); 180 181 return 0; 182 } 183 EXPORT_SYMBOL(v4l2_async_notifier_register); 184 185 void v4l2_async_notifier_unregister(struct v4l2_async_notifier *notifier) 186 { 187 struct v4l2_async_subdev_list *asdl, *tmp; 188 unsigned int notif_n_subdev = notifier->num_subdevs; 189 unsigned int n_subdev = min(notif_n_subdev, V4L2_MAX_SUBDEVS); 190 struct device *dev[n_subdev]; 191 int i = 0; 192 193 mutex_lock(&list_lock); 194 195 list_del(¬ifier->list); 196 197 list_for_each_entry_safe(asdl, tmp, ¬ifier->done, list) { 198 struct v4l2_subdev *sd = v4l2_async_to_subdev(asdl); 199 200 dev[i] = get_device(sd->dev); 201 202 v4l2_async_cleanup(asdl); 203 204 /* If we handled USB devices, we'd have to lock the parent too */ 205 device_release_driver(dev[i++]); 206 207 if (notifier->unbind) 208 notifier->unbind(notifier, sd, sd->asdl.asd); 209 } 210 211 mutex_unlock(&list_lock); 212 213 while (i--) { 214 struct device *d = dev[i]; 215 216 if (d && device_attach(d) < 0) { 217 const char *name = "(none)"; 218 int lock = device_trylock(d); 219 220 if (lock && d->driver) 221 name = d->driver->name; 222 dev_err(d, "Failed to re-probe to %s\n", name); 223 if (lock) 224 device_unlock(d); 225 } 226 put_device(d); 227 } 228 /* 229 * Don't care about the waiting list, it is initialised and populated 230 * upon notifier registration. 231 */ 232 } 233 EXPORT_SYMBOL(v4l2_async_notifier_unregister); 234 235 int v4l2_async_register_subdev(struct v4l2_subdev *sd) 236 { 237 struct v4l2_async_subdev_list *asdl = &sd->asdl; 238 struct v4l2_async_notifier *notifier; 239 240 mutex_lock(&list_lock); 241 242 INIT_LIST_HEAD(&asdl->list); 243 244 list_for_each_entry(notifier, ¬ifier_list, list) { 245 struct v4l2_async_subdev *asd = v4l2_async_belongs(notifier, asdl); 246 if (asd) { 247 int ret = v4l2_async_test_notify(notifier, asdl, asd); 248 mutex_unlock(&list_lock); 249 return ret; 250 } 251 } 252 253 /* None matched, wait for hot-plugging */ 254 list_add(&asdl->list, &subdev_list); 255 256 mutex_unlock(&list_lock); 257 258 return 0; 259 } 260 EXPORT_SYMBOL(v4l2_async_register_subdev); 261 262 void v4l2_async_unregister_subdev(struct v4l2_subdev *sd) 263 { 264 struct v4l2_async_subdev_list *asdl = &sd->asdl; 265 struct v4l2_async_notifier *notifier = asdl->notifier; 266 267 if (!asdl->asd) { 268 if (!list_empty(&asdl->list)) 269 v4l2_async_cleanup(asdl); 270 return; 271 } 272 273 mutex_lock(&list_lock); 274 275 list_add(&asdl->asd->list, ¬ifier->waiting); 276 277 v4l2_async_cleanup(asdl); 278 279 if (notifier->unbind) 280 notifier->unbind(notifier, sd, sd->asdl.asd); 281 282 mutex_unlock(&list_lock); 283 } 284 EXPORT_SYMBOL(v4l2_async_unregister_subdev); 285