2013-01-08 07:06:31 -03:00
/*
* V4L2 asynchronous subdevice registration API
*
* Copyright ( C ) 2012 - 2013 , Guennadi Liakhovetski < g . liakhovetski @ gmx . de >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*/
# include <linux/device.h>
# include <linux/err.h>
# include <linux/i2c.h>
# include <linux/list.h>
# include <linux/module.h>
# include <linux/mutex.h>
# include <linux/platform_device.h>
# include <linux/slab.h>
# include <linux/types.h>
# include <media/v4l2-async.h>
# include <media/v4l2-device.h>
# include <media/v4l2-subdev.h>
2015-06-11 12:18:01 -07:00
static bool match_i2c ( struct v4l2_subdev * sd , struct v4l2_async_subdev * asd )
2013-01-08 07:06:31 -03:00
{
2013-06-24 05:13:51 -03:00
# if IS_ENABLED(CONFIG_I2C)
2015-06-11 12:18:01 -07:00
struct i2c_client * client = i2c_verify_client ( sd - > dev ) ;
2013-01-08 07:06:31 -03:00
return client & &
asd - > match . i2c . adapter_id = = client - > adapter - > nr & &
asd - > match . i2c . address = = client - > addr ;
2013-06-24 05:13:51 -03:00
# else
return false ;
# endif
2013-01-08 07:06:31 -03:00
}
2015-06-11 12:18:01 -07:00
static bool match_devname ( struct v4l2_subdev * sd ,
struct v4l2_async_subdev * asd )
2013-01-08 07:06:31 -03:00
{
2015-06-11 12:18:01 -07:00
return ! strcmp ( asd - > match . device_name . name , dev_name ( sd - > dev ) ) ;
2013-01-08 07:06:31 -03:00
}
2015-06-11 12:18:01 -07:00
static bool match_of ( struct v4l2_subdev * sd , struct v4l2_async_subdev * asd )
2013-07-19 12:21:29 -03:00
{
2015-06-11 12:18:01 -07:00
return sd - > of_node = = asd - > match . of . node ;
}
static bool match_custom ( struct v4l2_subdev * sd , struct v4l2_async_subdev * asd )
{
if ( ! asd - > match . custom . match )
/* Match always */
return true ;
return asd - > match . custom . match ( sd - > dev , asd ) ;
2013-07-19 12:21:29 -03:00
}
2013-01-08 07:06:31 -03:00
static LIST_HEAD ( subdev_list ) ;
static LIST_HEAD ( notifier_list ) ;
static DEFINE_MUTEX ( list_lock ) ;
static struct v4l2_async_subdev * v4l2_async_belongs ( struct v4l2_async_notifier * notifier ,
2013-07-22 08:01:33 -03:00
struct v4l2_subdev * sd )
2013-01-08 07:06:31 -03:00
{
2015-06-11 12:18:01 -07:00
bool ( * match ) ( struct v4l2_subdev * , struct v4l2_async_subdev * ) ;
2013-01-08 07:06:31 -03:00
struct v4l2_async_subdev * asd ;
list_for_each_entry ( asd , & notifier - > waiting , list ) {
/* bus_type has been verified valid before */
2013-07-19 12:14:46 -03:00
switch ( asd - > match_type ) {
case V4L2_ASYNC_MATCH_CUSTOM :
2015-06-11 12:18:01 -07:00
match = match_custom ;
2013-01-08 07:06:31 -03:00
break ;
2013-07-19 12:14:46 -03:00
case V4L2_ASYNC_MATCH_DEVNAME :
match = match_devname ;
2013-01-08 07:06:31 -03:00
break ;
2013-07-19 12:14:46 -03:00
case V4L2_ASYNC_MATCH_I2C :
2013-01-08 07:06:31 -03:00
match = match_i2c ;
break ;
2013-07-19 12:21:29 -03:00
case V4L2_ASYNC_MATCH_OF :
match = match_of ;
break ;
2013-01-08 07:06:31 -03:00
default :
/* Cannot happen, unless someone breaks us */
WARN_ON ( true ) ;
return NULL ;
}
/* match cannot be NULL here */
2015-06-11 12:18:01 -07:00
if ( match ( sd , asd ) )
2013-01-08 07:06:31 -03:00
return asd ;
}
return NULL ;
}
static int v4l2_async_test_notify ( struct v4l2_async_notifier * notifier ,
2013-07-22 08:01:33 -03:00
struct v4l2_subdev * sd ,
2013-01-08 07:06:31 -03:00
struct v4l2_async_subdev * asd )
{
int ret ;
/* Remove from the waiting list */
list_del ( & asd - > list ) ;
2013-07-22 08:01:33 -03:00
sd - > asd = asd ;
sd - > notifier = notifier ;
2013-01-08 07:06:31 -03:00
if ( notifier - > bound ) {
ret = notifier - > bound ( notifier , sd , asd ) ;
if ( ret < 0 )
return ret ;
}
/* Move from the global subdevice list to notifier's done */
2013-07-22 08:01:33 -03:00
list_move ( & sd - > async_list , & notifier - > done ) ;
2013-01-08 07:06:31 -03:00
ret = v4l2_device_register_subdev ( notifier - > v4l2_dev , sd ) ;
if ( ret < 0 ) {
if ( notifier - > unbind )
notifier - > unbind ( notifier , sd , asd ) ;
return ret ;
}
if ( list_empty ( & notifier - > waiting ) & & notifier - > complete )
return notifier - > complete ( notifier ) ;
return 0 ;
}
2013-07-22 08:01:33 -03:00
static void v4l2_async_cleanup ( struct v4l2_subdev * sd )
2013-01-08 07:06:31 -03:00
{
v4l2_device_unregister_subdev ( sd ) ;
2013-07-22 08:01:33 -03:00
/* Subdevice driver will reprobe and put the subdev back onto the list */
list_del_init ( & sd - > async_list ) ;
sd - > asd = NULL ;
2013-01-08 07:06:31 -03:00
sd - > dev = NULL ;
}
int v4l2_async_notifier_register ( struct v4l2_device * v4l2_dev ,
struct v4l2_async_notifier * notifier )
{
2013-07-22 08:01:33 -03:00
struct v4l2_subdev * sd , * tmp ;
2013-01-08 07:06:31 -03:00
struct v4l2_async_subdev * asd ;
int i ;
if ( ! notifier - > num_subdevs | | notifier - > num_subdevs > V4L2_MAX_SUBDEVS )
return - EINVAL ;
notifier - > v4l2_dev = v4l2_dev ;
INIT_LIST_HEAD ( & notifier - > waiting ) ;
INIT_LIST_HEAD ( & notifier - > done ) ;
for ( i = 0 ; i < notifier - > num_subdevs ; i + + ) {
2013-07-19 12:31:10 -03:00
asd = notifier - > subdevs [ i ] ;
2013-01-08 07:06:31 -03:00
2013-07-19 12:14:46 -03:00
switch ( asd - > match_type ) {
case V4L2_ASYNC_MATCH_CUSTOM :
case V4L2_ASYNC_MATCH_DEVNAME :
case V4L2_ASYNC_MATCH_I2C :
2013-07-19 12:21:29 -03:00
case V4L2_ASYNC_MATCH_OF :
2013-01-08 07:06:31 -03:00
break ;
default :
dev_err ( notifier - > v4l2_dev ? notifier - > v4l2_dev - > dev : NULL ,
2013-07-19 12:14:46 -03:00
" Invalid match type %u on %p \n " ,
asd - > match_type , asd ) ;
2013-01-08 07:06:31 -03:00
return - EINVAL ;
}
list_add_tail ( & asd - > list , & notifier - > waiting ) ;
}
mutex_lock ( & list_lock ) ;
/* Keep also completed notifiers on the list */
list_add ( & notifier - > list , & notifier_list ) ;
2013-07-22 08:01:33 -03:00
list_for_each_entry_safe ( sd , tmp , & subdev_list , async_list ) {
2013-01-08 07:06:31 -03:00
int ret ;
2013-07-22 08:01:33 -03:00
asd = v4l2_async_belongs ( notifier , sd ) ;
2013-01-08 07:06:31 -03:00
if ( ! asd )
continue ;
2013-07-22 08:01:33 -03:00
ret = v4l2_async_test_notify ( notifier , sd , asd ) ;
2013-01-08 07:06:31 -03:00
if ( ret < 0 ) {
mutex_unlock ( & list_lock ) ;
return ret ;
}
}
mutex_unlock ( & list_lock ) ;
return 0 ;
}
EXPORT_SYMBOL ( v4l2_async_notifier_register ) ;
void v4l2_async_notifier_unregister ( struct v4l2_async_notifier * notifier )
{
2013-07-22 08:01:33 -03:00
struct v4l2_subdev * sd , * tmp ;
2013-01-08 07:06:31 -03:00
unsigned int notif_n_subdev = notifier - > num_subdevs ;
unsigned int n_subdev = min ( notif_n_subdev , V4L2_MAX_SUBDEVS ) ;
2013-11-02 06:20:16 -03:00
struct device * * dev ;
2013-01-08 07:06:31 -03:00
int i = 0 ;
2013-07-03 07:49:06 -03:00
if ( ! notifier - > v4l2_dev )
return ;
2013-11-02 06:20:16 -03:00
dev = kmalloc ( n_subdev * sizeof ( * dev ) , GFP_KERNEL ) ;
if ( ! dev ) {
dev_err ( notifier - > v4l2_dev - > dev ,
" Failed to allocate device cache! \n " ) ;
}
2013-01-08 07:06:31 -03:00
mutex_lock ( & list_lock ) ;
list_del ( & notifier - > list ) ;
2013-07-31 13:10:18 -03:00
list_for_each_entry_safe ( sd , tmp , & notifier - > done , async_list ) {
2013-11-02 06:20:16 -03:00
struct device * d ;
d = get_device ( sd - > dev ) ;
2013-01-08 07:06:31 -03:00
2013-07-22 08:01:33 -03:00
v4l2_async_cleanup ( sd ) ;
2013-01-08 07:06:31 -03:00
/* If we handled USB devices, we'd have to lock the parent too */
2013-11-02 06:20:16 -03:00
device_release_driver ( d ) ;
2013-01-08 07:06:31 -03:00
if ( notifier - > unbind )
2013-07-22 08:01:33 -03:00
notifier - > unbind ( notifier , sd , sd - > asd ) ;
2013-11-02 06:20:16 -03:00
/*
* Store device at the device cache , in order to call
* put_device ( ) on the final step
*/
if ( dev )
dev [ i + + ] = d ;
else
put_device ( d ) ;
2013-01-08 07:06:31 -03:00
}
mutex_unlock ( & list_lock ) ;
2013-11-02 06:20:16 -03:00
/*
* Call device_attach ( ) to reprobe devices
*
* NOTE : If dev allocation fails , i is 0 , and the whole loop won ' t be
* executed .
*/
2013-01-08 07:06:31 -03:00
while ( i - - ) {
struct device * d = dev [ i ] ;
if ( d & & device_attach ( d ) < 0 ) {
const char * name = " (none) " ;
int lock = device_trylock ( d ) ;
if ( lock & & d - > driver )
name = d - > driver - > name ;
dev_err ( d , " Failed to re-probe to %s \n " , name ) ;
if ( lock )
device_unlock ( d ) ;
}
put_device ( d ) ;
}
2013-11-02 06:20:16 -03:00
kfree ( dev ) ;
2013-07-03 07:49:06 -03:00
notifier - > v4l2_dev = NULL ;
2013-01-08 07:06:31 -03:00
/*
* Don ' t care about the waiting list , it is initialised and populated
* upon notifier registration .
*/
}
EXPORT_SYMBOL ( v4l2_async_notifier_unregister ) ;
int v4l2_async_register_subdev ( struct v4l2_subdev * sd )
{
struct v4l2_async_notifier * notifier ;
2015-06-11 12:18:01 -07:00
/*
* No reference taken . The reference is held by the device
* ( struct v4l2_subdev . dev ) , and async sub - device does not
* exist independently of the device at any point of time .
*/
if ( ! sd - > of_node & & sd - > dev )
sd - > of_node = sd - > dev - > of_node ;
2013-01-08 07:06:31 -03:00
mutex_lock ( & list_lock ) ;
2013-07-22 08:01:33 -03:00
INIT_LIST_HEAD ( & sd - > async_list ) ;
2013-01-08 07:06:31 -03:00
list_for_each_entry ( notifier , & notifier_list , list ) {
2013-07-22 08:01:33 -03:00
struct v4l2_async_subdev * asd = v4l2_async_belongs ( notifier , sd ) ;
2013-01-08 07:06:31 -03:00
if ( asd ) {
2013-07-22 08:01:33 -03:00
int ret = v4l2_async_test_notify ( notifier , sd , asd ) ;
2013-01-08 07:06:31 -03:00
mutex_unlock ( & list_lock ) ;
return ret ;
}
}
/* None matched, wait for hot-plugging */
2013-07-22 08:01:33 -03:00
list_add ( & sd - > async_list , & subdev_list ) ;
2013-01-08 07:06:31 -03:00
mutex_unlock ( & list_lock ) ;
return 0 ;
}
EXPORT_SYMBOL ( v4l2_async_register_subdev ) ;
void v4l2_async_unregister_subdev ( struct v4l2_subdev * sd )
{
2013-07-22 08:01:33 -03:00
struct v4l2_async_notifier * notifier = sd - > notifier ;
2013-01-08 07:06:31 -03:00
2013-07-22 08:01:33 -03:00
if ( ! sd - > asd ) {
if ( ! list_empty ( & sd - > async_list ) )
v4l2_async_cleanup ( sd ) ;
2013-01-08 07:06:31 -03:00
return ;
}
mutex_lock ( & list_lock ) ;
2013-07-22 08:01:33 -03:00
list_add ( & sd - > asd - > list , & notifier - > waiting ) ;
2013-01-08 07:06:31 -03:00
2013-07-22 08:01:33 -03:00
v4l2_async_cleanup ( sd ) ;
2013-01-08 07:06:31 -03:00
if ( notifier - > unbind )
2013-07-22 08:01:33 -03:00
notifier - > unbind ( notifier , sd , sd - > asd ) ;
2013-01-08 07:06:31 -03:00
mutex_unlock ( & list_lock ) ;
}
EXPORT_SYMBOL ( v4l2_async_unregister_subdev ) ;