2017-11-14 20:38:02 +03:00
// SPDX-License-Identifier: GPL-2.0
2005-04-17 02:20:36 +04:00
/*
* bus driver for ccwgroup
*
2012-05-15 19:49:12 +04:00
* Copyright IBM Corp . 2002 , 2012
2009-06-16 12:30:21 +04:00
*
* Author ( s ) : Arnd Bergmann ( arndb @ de . ibm . com )
* Cornelia Huck ( cornelia . huck @ de . ibm . com )
2005-04-17 02:20:36 +04:00
*/
# include <linux/module.h>
# include <linux/errno.h>
# include <linux/slab.h>
# include <linux/list.h>
# include <linux/device.h>
# include <linux/init.h>
# include <linux/ctype.h>
# include <linux/dcache.h>
2012-05-15 19:52:07 +04:00
# include <asm/cio.h>
2005-04-17 02:20:36 +04:00
# include <asm/ccwdev.h>
# include <asm/ccwgroup.h>
2012-05-15 19:52:07 +04:00
# include "device.h"
# define CCW_BUS_ID_SIZE 10
2008-12-25 15:38:55 +03:00
2005-04-17 02:20:36 +04:00
/* In Linux 2.4, we had a channel device layer called "chandev"
* that did all sorts of obscure stuff for networking devices .
* This is another driver that serves as a replacement for just
* one of its functions , namely the translation of single subchannels
* to devices that use multiple subchannels .
*/
2006-01-05 17:42:09 +03:00
static struct bus_type ccwgroup_bus_type ;
2005-04-17 02:20:36 +04:00
2011-10-30 18:16:53 +04:00
static void __ccwgroup_remove_symlinks ( struct ccwgroup_device * gdev )
2005-04-17 02:20:36 +04:00
{
int i ;
2017-05-04 14:35:33 +03:00
char str [ 16 ] ;
2005-04-17 02:20:36 +04:00
for ( i = 0 ; i < gdev - > count ; i + + ) {
sprintf ( str , " cdev%d " , i ) ;
sysfs_remove_link ( & gdev - > dev . kobj , str ) ;
sysfs_remove_link ( & gdev - > cdev [ i ] - > dev . kobj , " group_device " ) ;
}
}
2012-11-22 18:56:39 +04:00
/**
* ccwgroup_set_online ( ) - enable a ccwgroup device
* @ gdev : target ccwgroup device
*
* This function attempts to put the ccwgroup device into the online state .
* Returns :
* % 0 on success and a negative error value on failure .
*/
int ccwgroup_set_online ( struct ccwgroup_device * gdev )
2011-10-30 18:16:53 +04:00
{
struct ccwgroup_driver * gdrv = to_ccwgroupdrv ( gdev - > dev . driver ) ;
2012-11-09 17:33:06 +04:00
int ret = - EINVAL ;
2011-10-30 18:16:53 +04:00
if ( atomic_cmpxchg ( & gdev - > onoff , 0 , 1 ) ! = 0 )
return - EAGAIN ;
if ( gdev - > state = = CCWGROUP_ONLINE )
goto out ;
if ( gdrv - > set_online )
ret = gdrv - > set_online ( gdev ) ;
if ( ret )
goto out ;
gdev - > state = CCWGROUP_ONLINE ;
out :
atomic_set ( & gdev - > onoff , 0 ) ;
return ret ;
}
2012-11-22 18:56:39 +04:00
EXPORT_SYMBOL ( ccwgroup_set_online ) ;
2011-10-30 18:16:53 +04:00
2012-11-22 18:56:39 +04:00
/**
* ccwgroup_set_offline ( ) - disable a ccwgroup device
* @ gdev : target ccwgroup device
2021-09-21 17:52:17 +03:00
* @ call_gdrv : Call the registered gdrv set_offline function
2012-11-22 18:56:39 +04:00
*
* This function attempts to put the ccwgroup device into the offline state .
* Returns :
* % 0 on success and a negative error value on failure .
*/
2021-09-21 17:52:17 +03:00
int ccwgroup_set_offline ( struct ccwgroup_device * gdev , bool call_gdrv )
2011-10-30 18:16:53 +04:00
{
struct ccwgroup_driver * gdrv = to_ccwgroupdrv ( gdev - > dev . driver ) ;
2012-11-09 17:33:06 +04:00
int ret = - EINVAL ;
2011-10-30 18:16:53 +04:00
if ( atomic_cmpxchg ( & gdev - > onoff , 0 , 1 ) ! = 0 )
return - EAGAIN ;
if ( gdev - > state = = CCWGROUP_OFFLINE )
goto out ;
2021-09-21 17:52:17 +03:00
if ( ! call_gdrv ) {
ret = 0 ;
goto offline ;
}
2011-10-30 18:16:53 +04:00
if ( gdrv - > set_offline )
ret = gdrv - > set_offline ( gdev ) ;
if ( ret )
goto out ;
2021-09-21 17:52:17 +03:00
offline :
2011-10-30 18:16:53 +04:00
gdev - > state = CCWGROUP_OFFLINE ;
out :
atomic_set ( & gdev - > onoff , 0 ) ;
return ret ;
}
2012-11-22 18:56:39 +04:00
EXPORT_SYMBOL ( ccwgroup_set_offline ) ;
2011-10-30 18:16:53 +04:00
2011-10-30 18:16:52 +04:00
static ssize_t ccwgroup_online_store ( struct device * dev ,
struct device_attribute * attr ,
2011-10-30 18:16:53 +04:00
const char * buf , size_t count )
{
struct ccwgroup_device * gdev = to_ccwgroupdev ( dev ) ;
unsigned long value ;
int ret ;
2013-12-16 13:56:46 +04:00
device_lock ( dev ) ;
if ( ! dev - > driver ) {
ret = - EINVAL ;
goto out ;
}
2011-10-30 18:16:53 +04:00
2013-07-22 05:18:15 +04:00
ret = kstrtoul ( buf , 0 , & value ) ;
2011-10-30 18:16:53 +04:00
if ( ret )
goto out ;
if ( value = = 1 )
ret = ccwgroup_set_online ( gdev ) ;
else if ( value = = 0 )
2021-09-21 17:52:17 +03:00
ret = ccwgroup_set_offline ( gdev , true ) ;
2011-10-30 18:16:53 +04:00
else
ret = - EINVAL ;
out :
2013-12-16 13:56:46 +04:00
device_unlock ( dev ) ;
2011-10-30 18:16:53 +04:00
return ( ret = = 0 ) ? count : ret ;
}
2011-10-30 18:16:52 +04:00
static ssize_t ccwgroup_online_show ( struct device * dev ,
struct device_attribute * attr ,
2011-10-30 18:16:53 +04:00
char * buf )
{
struct ccwgroup_device * gdev = to_ccwgroupdev ( dev ) ;
int online ;
online = ( gdev - > state = = CCWGROUP_ONLINE ) ? 1 : 0 ;
return scnprintf ( buf , PAGE_SIZE , " %d \n " , online ) ;
}
2005-04-17 02:20:36 +04:00
/*
* Provide an ' ungroup ' attribute so the user can remove group devices no
* longer needed or accidentially created . Saves memory : )
*/
2014-02-03 23:03:04 +04:00
static void ccwgroup_ungroup ( struct ccwgroup_device * gdev )
2007-03-15 22:50:34 +03:00
{
2007-04-27 18:01:37 +04:00
mutex_lock ( & gdev - > reg_mutex ) ;
2008-01-26 16:10:50 +03:00
if ( device_is_registered ( & gdev - > dev ) ) {
__ccwgroup_remove_symlinks ( gdev ) ;
2014-02-03 23:03:04 +04:00
device_unregister ( & gdev - > dev ) ;
2008-01-26 16:10:50 +03:00
}
2007-04-27 18:01:37 +04:00
mutex_unlock ( & gdev - > reg_mutex ) ;
2007-03-15 22:50:34 +03:00
}
2011-10-30 18:16:53 +04:00
static ssize_t ccwgroup_ungroup_store ( struct device * dev ,
struct device_attribute * attr ,
const char * buf , size_t count )
2005-04-17 02:20:36 +04:00
{
2011-10-30 18:16:53 +04:00
struct ccwgroup_device * gdev = to_ccwgroupdev ( dev ) ;
2014-06-13 19:29:11 +04:00
int rc = 0 ;
2005-04-17 02:20:36 +04:00
2008-12-25 15:39:04 +03:00
/* Prevent concurrent online/offline processing and ungrouping. */
if ( atomic_cmpxchg ( & gdev - > onoff , 0 , 1 ) ! = 0 )
return - EAGAIN ;
if ( gdev - > state ! = CCWGROUP_OFFLINE ) {
rc = - EINVAL ;
goto out ;
}
2014-02-03 23:03:04 +04:00
if ( device_remove_file_self ( dev , attr ) )
ccwgroup_ungroup ( gdev ) ;
2014-06-13 19:29:11 +04:00
else
rc = - ENODEV ;
2008-12-25 15:39:04 +03:00
out :
if ( rc ) {
2014-06-13 19:29:11 +04:00
/* Release onoff "lock" when ungrouping failed. */
atomic_set ( & gdev - > onoff , 0 ) ;
2008-12-25 15:39:04 +03:00
return rc ;
}
2005-04-17 02:20:36 +04:00
return count ;
}
static DEVICE_ATTR ( ungroup , 0200 , NULL , ccwgroup_ungroup_store ) ;
2011-10-30 18:16:52 +04:00
static DEVICE_ATTR ( online , 0644 , ccwgroup_online_show , ccwgroup_online_store ) ;
2020-12-07 16:12:29 +03:00
static struct attribute * ccwgroup_dev_attrs [ ] = {
2011-10-30 18:16:52 +04:00
& dev_attr_online . attr ,
& dev_attr_ungroup . attr ,
NULL ,
} ;
2020-12-07 16:12:29 +03:00
ATTRIBUTE_GROUPS ( ccwgroup_dev ) ;
2005-04-17 02:20:36 +04:00
2014-02-03 23:03:04 +04:00
static void ccwgroup_ungroup_workfn ( struct work_struct * work )
{
struct ccwgroup_device * gdev =
container_of ( work , struct ccwgroup_device , ungroup_work ) ;
ccwgroup_ungroup ( gdev ) ;
2014-06-13 19:02:24 +04:00
put_device ( & gdev - > dev ) ;
2014-02-03 23:03:04 +04:00
}
2011-10-30 18:16:53 +04:00
static void ccwgroup_release ( struct device * dev )
2005-04-17 02:20:36 +04:00
{
2021-04-24 13:12:38 +03:00
struct ccwgroup_device * gdev = to_ccwgroupdev ( dev ) ;
unsigned int i ;
for ( i = 0 ; i < gdev - > count ; i + + ) {
struct ccw_device * cdev = gdev - > cdev [ i ] ;
unsigned long flags ;
if ( cdev ) {
spin_lock_irqsave ( cdev - > ccwlock , flags ) ;
if ( dev_get_drvdata ( & cdev - > dev ) = = gdev )
dev_set_drvdata ( & cdev - > dev , NULL ) ;
spin_unlock_irqrestore ( cdev - > ccwlock , flags ) ;
put_device ( & cdev - > dev ) ;
}
}
kfree ( gdev ) ;
2005-04-17 02:20:36 +04:00
}
2011-10-30 18:16:53 +04:00
static int __ccwgroup_create_symlinks ( struct ccwgroup_device * gdev )
2005-04-17 02:20:36 +04:00
{
2017-05-04 14:35:33 +03:00
char str [ 16 ] ;
2005-04-17 02:20:36 +04:00
int i , rc ;
for ( i = 0 ; i < gdev - > count ; i + + ) {
2011-10-30 18:16:53 +04:00
rc = sysfs_create_link ( & gdev - > cdev [ i ] - > dev . kobj ,
& gdev - > dev . kobj , " group_device " ) ;
2005-04-17 02:20:36 +04:00
if ( rc ) {
for ( - - i ; i > = 0 ; i - - )
sysfs_remove_link ( & gdev - > cdev [ i ] - > dev . kobj ,
" group_device " ) ;
return rc ;
}
}
for ( i = 0 ; i < gdev - > count ; i + + ) {
sprintf ( str , " cdev%d " , i ) ;
2011-10-30 18:16:53 +04:00
rc = sysfs_create_link ( & gdev - > dev . kobj ,
& gdev - > cdev [ i ] - > dev . kobj , str ) ;
2005-04-17 02:20:36 +04:00
if ( rc ) {
for ( - - i ; i > = 0 ; i - - ) {
sprintf ( str , " cdev%d " , i ) ;
sysfs_remove_link ( & gdev - > dev . kobj , str ) ;
}
for ( i = 0 ; i < gdev - > count ; i + + )
sysfs_remove_link ( & gdev - > cdev [ i ] - > dev . kobj ,
" group_device " ) ;
return rc ;
}
}
return 0 ;
}
2012-05-15 19:52:07 +04:00
static int __get_next_id ( const char * * buf , struct ccw_dev_id * id )
2008-04-24 12:15:20 +04:00
{
2012-05-15 19:52:07 +04:00
unsigned int cssid , ssid , devno ;
int ret = 0 , len ;
2008-04-24 12:15:20 +04:00
char * start , * end ;
start = ( char * ) * buf ;
end = strchr ( start , ' , ' ) ;
if ( ! end ) {
/* Last entry. Strip trailing newline, if applicable. */
end = strchr ( start , ' \n ' ) ;
if ( end )
* end = ' \0 ' ;
len = strlen ( start ) + 1 ;
} else {
len = end - start + 1 ;
end + + ;
}
2012-05-15 19:52:07 +04:00
if ( len < = CCW_BUS_ID_SIZE ) {
if ( sscanf ( start , " %2x.%1x.%04x " , & cssid , & ssid , & devno ) ! = 3 )
ret = - EINVAL ;
2008-04-24 12:15:20 +04:00
} else
2012-05-15 19:52:07 +04:00
ret = - EINVAL ;
2008-04-24 12:15:20 +04:00
2012-05-15 19:52:07 +04:00
if ( ! ret ) {
id - > ssid = ssid ;
id - > devno = devno ;
}
* buf = end ;
return ret ;
2008-04-24 12:15:20 +04:00
}
2007-10-12 18:11:17 +04:00
/**
2012-05-15 19:49:12 +04:00
* ccwgroup_create_dev ( ) - create and register a ccw group device
* @ parent : parent device for the new device
* @ gdrv : driver for the new group device
2008-04-24 12:15:20 +04:00
* @ num_devices : number of slave devices
* @ buf : buffer containing comma separated bus ids of slave devices
2007-10-12 18:11:17 +04:00
*
2012-05-15 19:49:12 +04:00
* Create and register a new ccw group device as a child of @ parent . Slave
2012-05-15 19:52:07 +04:00
* devices are obtained from the list of bus ids given in @ buf .
2007-10-12 18:11:17 +04:00
* Returns :
* % 0 on success and an error code on failure .
* Context :
* non - atomic
2005-04-17 02:20:36 +04:00
*/
2012-05-15 20:03:46 +04:00
int ccwgroup_create_dev ( struct device * parent , struct ccwgroup_driver * gdrv ,
int num_devices , const char * buf )
2005-04-17 02:20:36 +04:00
{
struct ccwgroup_device * gdev ;
2012-05-15 19:52:07 +04:00
struct ccw_dev_id dev_id ;
2008-04-24 12:15:20 +04:00
int rc , i ;
2005-04-17 02:20:36 +04:00
2018-01-30 16:39:34 +03:00
if ( num_devices < 1 )
return - EINVAL ;
treewide: Use struct_size() for kmalloc()-family
One of the more common cases of allocation size calculations is finding
the size of a structure that has a zero-sized array at the end, along
with memory for some number of elements for that array. For example:
struct foo {
int stuff;
void *entry[];
};
instance = kmalloc(sizeof(struct foo) + sizeof(void *) * count, GFP_KERNEL);
Instead of leaving these open-coded and prone to type mistakes, we can
now use the new struct_size() helper:
instance = kmalloc(struct_size(instance, entry, count), GFP_KERNEL);
This patch makes the changes for kmalloc()-family (and kvmalloc()-family)
uses. It was done via automatic conversion with manual review for the
"CHECKME" non-standard cases noted below, using the following Coccinelle
script:
// pkey_cache = kmalloc(sizeof *pkey_cache + tprops->pkey_tbl_len *
// sizeof *pkey_cache->table, GFP_KERNEL);
@@
identifier alloc =~ "kmalloc|kzalloc|kvmalloc|kvzalloc";
expression GFP;
identifier VAR, ELEMENT;
expression COUNT;
@@
- alloc(sizeof(*VAR) + COUNT * sizeof(*VAR->ELEMENT), GFP)
+ alloc(struct_size(VAR, ELEMENT, COUNT), GFP)
// mr = kzalloc(sizeof(*mr) + m * sizeof(mr->map[0]), GFP_KERNEL);
@@
identifier alloc =~ "kmalloc|kzalloc|kvmalloc|kvzalloc";
expression GFP;
identifier VAR, ELEMENT;
expression COUNT;
@@
- alloc(sizeof(*VAR) + COUNT * sizeof(VAR->ELEMENT[0]), GFP)
+ alloc(struct_size(VAR, ELEMENT, COUNT), GFP)
// Same pattern, but can't trivially locate the trailing element name,
// or variable name.
@@
identifier alloc =~ "kmalloc|kzalloc|kvmalloc|kvzalloc";
expression GFP;
expression SOMETHING, COUNT, ELEMENT;
@@
- alloc(sizeof(SOMETHING) + COUNT * sizeof(ELEMENT), GFP)
+ alloc(CHECKME_struct_size(&SOMETHING, ELEMENT, COUNT), GFP)
Signed-off-by: Kees Cook <keescook@chromium.org>
2018-05-08 23:45:50 +03:00
gdev = kzalloc ( struct_size ( gdev , cdev , num_devices ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! gdev )
return - ENOMEM ;
atomic_set ( & gdev - > onoff , 0 ) ;
2007-04-27 18:01:37 +04:00
mutex_init ( & gdev - > reg_mutex ) ;
mutex_lock ( & gdev - > reg_mutex ) ;
2014-02-03 23:03:04 +04:00
INIT_WORK ( & gdev - > ungroup_work , ccwgroup_ungroup_workfn ) ;
2008-08-21 21:46:36 +04:00
gdev - > count = num_devices ;
gdev - > dev . bus = & ccwgroup_bus_type ;
2012-05-15 19:49:12 +04:00
gdev - > dev . parent = parent ;
2008-08-21 21:46:36 +04:00
gdev - > dev . release = ccwgroup_release ;
device_initialize ( & gdev - > dev ) ;
2012-05-15 19:52:07 +04:00
for ( i = 0 ; i < num_devices & & buf ; i + + ) {
rc = __get_next_id ( & buf , & dev_id ) ;
2008-04-24 12:15:20 +04:00
if ( rc ! = 0 )
goto error ;
2012-05-15 19:52:07 +04:00
gdev - > cdev [ i ] = get_ccwdev_by_dev_id ( & dev_id ) ;
2008-04-24 12:15:20 +04:00
/*
* All devices have to be of the same type in
* order to be grouped .
*/
2012-05-15 19:52:07 +04:00
if ( ! gdev - > cdev [ i ] | | ! gdev - > cdev [ i ] - > drv | |
gdev - > cdev [ i ] - > drv ! = gdev - > cdev [ 0 ] - > drv | |
gdev - > cdev [ i ] - > id . driver_info ! =
2005-04-17 02:20:36 +04:00
gdev - > cdev [ 0 ] - > id . driver_info ) {
rc = - EINVAL ;
2007-04-27 18:01:37 +04:00
goto error ;
2005-04-17 02:20:36 +04:00
}
/* Don't allow a device to belong to more than one group. */
2010-05-27 01:27:07 +04:00
spin_lock_irq ( gdev - > cdev [ i ] - > ccwlock ) ;
2008-01-26 16:10:46 +03:00
if ( dev_get_drvdata ( & gdev - > cdev [ i ] - > dev ) ) {
2010-05-27 01:27:07 +04:00
spin_unlock_irq ( gdev - > cdev [ i ] - > ccwlock ) ;
2005-04-17 02:20:36 +04:00
rc = - EINVAL ;
2007-04-27 18:01:37 +04:00
goto error ;
2005-04-17 02:20:36 +04:00
}
2008-01-26 16:10:46 +03:00
dev_set_drvdata ( & gdev - > cdev [ i ] - > dev , gdev ) ;
2010-05-27 01:27:07 +04:00
spin_unlock_irq ( gdev - > cdev [ i ] - > ccwlock ) ;
2006-07-27 16:00:33 +04:00
}
2008-04-24 12:15:20 +04:00
/* Check for sufficient number of bus ids. */
2012-05-15 19:52:07 +04:00
if ( i < num_devices ) {
2008-04-24 12:15:20 +04:00
rc = - EINVAL ;
goto error ;
}
/* Check for trailing stuff. */
2019-09-17 21:04:04 +03:00
if ( i = = num_devices & & buf & & strlen ( buf ) > 0 ) {
2008-04-24 12:15:20 +04:00
rc = - EINVAL ;
goto error ;
}
2017-09-14 10:52:32 +03:00
/* Check if the devices are bound to the required ccw driver. */
2018-01-30 16:39:34 +03:00
if ( gdrv & & gdrv - > ccw_driver & &
2017-09-14 10:52:32 +03:00
gdev - > cdev [ 0 ] - > drv ! = gdrv - > ccw_driver ) {
rc = - EINVAL ;
goto error ;
}
2005-04-17 02:20:36 +04:00
2008-10-10 23:33:10 +04:00
dev_set_name ( & gdev - > dev , " %s " , dev_name ( & gdev - > cdev [ 0 ] - > dev ) ) ;
2012-05-15 19:49:12 +04:00
if ( gdrv ) {
gdev - > dev . driver = & gdrv - > driver ;
rc = gdrv - > setup ? gdrv - > setup ( gdev ) : 0 ;
if ( rc )
goto error ;
}
2008-08-21 21:46:36 +04:00
rc = device_add ( & gdev - > dev ) ;
2005-04-17 02:20:36 +04:00
if ( rc )
2007-04-27 18:01:37 +04:00
goto error ;
2005-04-17 02:20:36 +04:00
rc = __ccwgroup_create_symlinks ( gdev ) ;
2011-10-30 18:16:53 +04:00
if ( rc ) {
device_del ( & gdev - > dev ) ;
goto error ;
2005-04-17 02:20:36 +04:00
}
2011-10-30 18:16:53 +04:00
mutex_unlock ( & gdev - > reg_mutex ) ;
return 0 ;
2005-04-17 02:20:36 +04:00
error :
2007-04-27 18:01:37 +04:00
mutex_unlock ( & gdev - > reg_mutex ) ;
put_device ( & gdev - > dev ) ;
2005-04-17 02:20:36 +04:00
return rc ;
}
2012-05-15 19:49:12 +04:00
EXPORT_SYMBOL ( ccwgroup_create_dev ) ;
2009-03-26 17:24:15 +03:00
static int ccwgroup_notifier ( struct notifier_block * nb , unsigned long action ,
2011-10-30 18:16:53 +04:00
void * data )
{
2014-02-03 23:03:04 +04:00
struct ccwgroup_device * gdev = to_ccwgroupdev ( data ) ;
2011-10-30 18:16:53 +04:00
2021-05-05 11:28:21 +03:00
if ( action = = BUS_NOTIFY_UNBOUND_DRIVER ) {
2014-06-13 19:02:24 +04:00
get_device ( & gdev - > dev ) ;
2014-02-03 23:03:04 +04:00
schedule_work ( & gdev - > ungroup_work ) ;
2014-06-13 19:02:24 +04:00
}
2011-10-30 18:16:53 +04:00
return NOTIFY_OK ;
}
2009-03-26 17:24:15 +03:00
static struct notifier_block ccwgroup_nb = {
. notifier_call = ccwgroup_notifier
} ;
static int __init init_ccwgroup ( void )
2005-04-17 02:20:36 +04:00
{
2009-03-26 17:24:15 +03:00
int ret ;
ret = bus_register ( & ccwgroup_bus_type ) ;
if ( ret )
return ret ;
ret = bus_register_notifier ( & ccwgroup_bus_type , & ccwgroup_nb ) ;
if ( ret )
bus_unregister ( & ccwgroup_bus_type ) ;
return ret ;
2005-04-17 02:20:36 +04:00
}
2009-03-26 17:24:15 +03:00
static void __exit cleanup_ccwgroup ( void )
2005-04-17 02:20:36 +04:00
{
2009-03-26 17:24:15 +03:00
bus_unregister_notifier ( & ccwgroup_bus_type , & ccwgroup_nb ) ;
bus_unregister ( & ccwgroup_bus_type ) ;
2005-04-17 02:20:36 +04:00
}
module_init ( init_ccwgroup ) ;
module_exit ( cleanup_ccwgroup ) ;
/************************** driver stuff ******************************/
2021-07-13 22:35:22 +03:00
static void ccwgroup_remove ( struct device * dev )
2005-04-17 02:20:36 +04:00
{
2011-10-30 18:16:53 +04:00
struct ccwgroup_device * gdev = to_ccwgroupdev ( dev ) ;
struct ccwgroup_driver * gdrv = to_ccwgroupdrv ( dev - > driver ) ;
2005-04-17 02:20:36 +04:00
2009-03-26 17:24:14 +03:00
if ( gdrv - > remove )
2005-04-17 02:20:36 +04:00
gdrv - > remove ( gdev ) ;
}
2008-02-05 18:50:36 +03:00
static void ccwgroup_shutdown ( struct device * dev )
{
2011-10-30 18:16:53 +04:00
struct ccwgroup_device * gdev = to_ccwgroupdev ( dev ) ;
struct ccwgroup_driver * gdrv = to_ccwgroupdrv ( dev - > driver ) ;
2008-02-05 18:50:36 +03:00
2009-03-26 17:24:14 +03:00
if ( ! dev - > driver )
return ;
if ( gdrv - > shutdown )
2008-02-05 18:50:36 +03:00
gdrv - > shutdown ( gdev ) ;
}
2006-01-05 17:42:09 +03:00
static struct bus_type ccwgroup_bus_type = {
. name = " ccwgroup " ,
2020-12-07 16:12:29 +03:00
. dev_groups = ccwgroup_dev_groups ,
2006-01-05 17:42:09 +03:00
. remove = ccwgroup_remove ,
2008-02-05 18:50:36 +03:00
. shutdown = ccwgroup_shutdown ,
2006-01-05 17:42:09 +03:00
} ;
2017-02-15 13:45:07 +03:00
bool dev_is_ccwgroup ( struct device * dev )
{
return dev - > bus = = & ccwgroup_bus_type ;
}
EXPORT_SYMBOL ( dev_is_ccwgroup ) ;
2007-10-12 18:11:17 +04:00
/**
* ccwgroup_driver_register ( ) - register a ccw group driver
* @ cdriver : driver to be registered
*
* This function is mainly a wrapper around driver_register ( ) .
*/
int ccwgroup_driver_register ( struct ccwgroup_driver * cdriver )
2005-04-17 02:20:36 +04:00
{
/* register our new driver with the core */
2006-08-30 16:33:35 +04:00
cdriver - > driver . bus = & ccwgroup_bus_type ;
2005-04-17 02:20:36 +04:00
return driver_register ( & cdriver - > driver ) ;
}
2011-10-30 18:16:53 +04:00
EXPORT_SYMBOL ( ccwgroup_driver_register ) ;
2005-04-17 02:20:36 +04:00
2007-10-12 18:11:17 +04:00
/**
* ccwgroup_driver_unregister ( ) - deregister a ccw group driver
* @ cdriver : driver to be deregistered
*
* This function is mainly a wrapper around driver_unregister ( ) .
*/
void ccwgroup_driver_unregister ( struct ccwgroup_driver * cdriver )
2005-04-17 02:20:36 +04:00
{
driver_unregister ( & cdriver - > driver ) ;
}
2011-10-30 18:16:53 +04:00
EXPORT_SYMBOL ( ccwgroup_driver_unregister ) ;
2005-04-17 02:20:36 +04:00
2007-10-12 18:11:17 +04:00
/**
* ccwgroup_probe_ccwdev ( ) - probe function for slave devices
* @ cdev : ccw device to be probed
*
* This is a dummy probe function for ccw devices that are slave devices in
* a ccw group device .
* Returns :
* always % 0
*/
int ccwgroup_probe_ccwdev ( struct ccw_device * cdev )
2005-04-17 02:20:36 +04:00
{
return 0 ;
}
2011-10-30 18:16:53 +04:00
EXPORT_SYMBOL ( ccwgroup_probe_ccwdev ) ;
2005-04-17 02:20:36 +04:00
2007-10-12 18:11:17 +04:00
/**
* ccwgroup_remove_ccwdev ( ) - remove function for slave devices
* @ cdev : ccw device to be removed
*
* This is a remove function for ccw devices that are slave devices in a ccw
* group device . It sets the ccw device offline and also deregisters the
* embedding ccw group device .
*/
void ccwgroup_remove_ccwdev ( struct ccw_device * cdev )
2005-04-17 02:20:36 +04:00
{
struct ccwgroup_device * gdev ;
/* Ignore offlining errors, device is gone anyway. */
ccw_device_set_offline ( cdev ) ;
/* If one of its devices is gone, the whole group is done for. */
2011-01-05 14:48:13 +03:00
spin_lock_irq ( cdev - > ccwlock ) ;
gdev = dev_get_drvdata ( & cdev - > dev ) ;
if ( ! gdev ) {
spin_unlock_irq ( cdev - > ccwlock ) ;
return ;
}
/* Get ccwgroup device reference for local processing. */
get_device ( & gdev - > dev ) ;
spin_unlock_irq ( cdev - > ccwlock ) ;
/* Unregister group device. */
2014-06-13 20:11:37 +04:00
ccwgroup_ungroup ( gdev ) ;
2011-01-05 14:48:13 +03:00
/* Release ccwgroup device reference for local processing. */
put_device ( & gdev - > dev ) ;
2005-04-17 02:20:36 +04:00
}
EXPORT_SYMBOL ( ccwgroup_remove_ccwdev ) ;
2011-10-30 18:16:53 +04:00
MODULE_LICENSE ( " GPL " ) ;