2013-03-27 19:29:53 +04:00
/*
* Intel Management Engine Interface ( Intel MEI ) Linux driver
* Copyright ( c ) 2012 - 2013 , Intel Corporation .
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms and conditions of the GNU General Public License ,
* version 2 , as published by the Free Software Foundation .
*
* This program is distributed in the hope it will be useful , but WITHOUT
* ANY WARRANTY ; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE . See the GNU General Public License for
* more details .
*
*/
# include <linux/module.h>
# include <linux/device.h>
# include <linux/kernel.h>
2013-03-27 19:29:55 +04:00
# include <linux/sched.h>
2013-03-27 19:29:53 +04:00
# include <linux/init.h>
# include <linux/errno.h>
# include <linux/slab.h>
# include <linux/mutex.h>
# include <linux/interrupt.h>
# include <linux/mei_cl_bus.h>
# include "mei_dev.h"
2013-03-27 19:29:55 +04:00
# include "client.h"
2013-03-27 19:29:53 +04:00
# define to_mei_cl_driver(d) container_of(d, struct mei_cl_driver, driver)
# define to_mei_cl_device(d) container_of(d, struct mei_cl_device, dev)
2015-07-23 15:08:35 +03:00
/**
* __mei_cl_send - internal client send ( write )
*
* @ cl : host client
* @ buf : buffer to send
* @ length : buffer length
* @ blocking : wait for write completion
*
* Return : written size bytes or < 0 on error
*/
ssize_t __mei_cl_send ( struct mei_cl * cl , u8 * buf , size_t length ,
bool blocking )
2013-03-27 19:29:53 +04:00
{
2015-07-23 15:08:35 +03:00
struct mei_device * bus ;
struct mei_cl_cb * cb = NULL ;
ssize_t rets ;
2013-03-27 19:29:53 +04:00
2015-07-23 15:08:35 +03:00
if ( WARN_ON ( ! cl | | ! cl - > dev ) )
return - ENODEV ;
2015-05-07 15:54:02 +03:00
2015-07-23 15:08:35 +03:00
bus = cl - > dev ;
2013-03-27 19:29:53 +04:00
2015-07-23 15:08:35 +03:00
mutex_lock ( & bus - > device_lock ) ;
if ( ! mei_cl_is_connected ( cl ) ) {
rets = - ENODEV ;
goto out ;
}
2013-03-27 19:29:53 +04:00
2015-07-23 15:08:35 +03:00
/* Check if we have an ME client device */
if ( ! mei_me_cl_is_active ( cl - > me_cl ) ) {
rets = - ENOTTY ;
goto out ;
}
2015-05-07 15:54:02 +03:00
2015-07-23 15:08:35 +03:00
if ( length > mei_cl_mtu ( cl ) ) {
rets = - EFBIG ;
goto out ;
}
2013-03-27 19:29:53 +04:00
2015-07-23 15:08:35 +03:00
cb = mei_cl_alloc_cb ( cl , length , MEI_FOP_WRITE , NULL ) ;
if ( ! cb ) {
rets = - ENOMEM ;
goto out ;
2013-03-27 19:29:53 +04:00
}
2015-07-23 15:08:35 +03:00
memcpy ( cb - > buf . data , buf , length ) ;
rets = mei_cl_write ( cl , cb , blocking ) ;
out :
mutex_unlock ( & bus - > device_lock ) ;
if ( rets < 0 )
mei_io_cb_free ( cb ) ;
return rets ;
2013-03-27 19:29:53 +04:00
}
2015-07-23 15:08:35 +03:00
/**
* __mei_cl_recv - internal client receive ( read )
*
* @ cl : host client
2015-10-13 15:02:40 +03:00
* @ buf : buffer to receive
2015-07-23 15:08:35 +03:00
* @ length : buffer length
*
* Return : read size in bytes of < 0 on error
*/
ssize_t __mei_cl_recv ( struct mei_cl * cl , u8 * buf , size_t length )
2013-03-27 19:29:53 +04:00
{
2015-07-23 15:08:35 +03:00
struct mei_device * bus ;
struct mei_cl_cb * cb ;
size_t r_length ;
ssize_t rets ;
2013-03-27 19:29:53 +04:00
2015-07-23 15:08:35 +03:00
if ( WARN_ON ( ! cl | | ! cl - > dev ) )
2013-03-27 19:29:53 +04:00
return - ENODEV ;
2015-07-23 15:08:35 +03:00
bus = cl - > dev ;
2013-03-27 19:29:53 +04:00
2015-07-23 15:08:35 +03:00
mutex_lock ( & bus - > device_lock ) ;
2013-03-27 19:29:53 +04:00
2015-07-23 15:08:35 +03:00
cb = mei_cl_read_cb ( cl , NULL ) ;
if ( cb )
goto copy ;
2013-03-27 19:29:53 +04:00
2015-07-23 15:08:35 +03:00
rets = mei_cl_read_start ( cl , length , NULL ) ;
if ( rets & & rets ! = - EBUSY )
goto out ;
2013-03-27 19:29:53 +04:00
2015-07-23 15:08:35 +03:00
/* wait on event only if there is no other waiter */
if ( list_empty ( & cl - > rd_completed ) & & ! waitqueue_active ( & cl - > rx_wait ) ) {
2013-03-27 19:29:53 +04:00
2015-07-23 15:08:35 +03:00
mutex_unlock ( & bus - > device_lock ) ;
2013-03-27 19:29:55 +04:00
2015-07-23 15:08:35 +03:00
if ( wait_event_interruptible ( cl - > rx_wait ,
( ! list_empty ( & cl - > rd_completed ) ) | |
( ! mei_cl_is_connected ( cl ) ) ) ) {
2013-03-27 19:29:53 +04:00
2015-07-23 15:08:35 +03:00
if ( signal_pending ( current ) )
return - EINTR ;
return - ERESTARTSYS ;
}
mutex_lock ( & bus - > device_lock ) ;
if ( ! mei_cl_is_connected ( cl ) ) {
rets = - EBUSY ;
goto out ;
}
2013-03-27 19:29:53 +04:00
}
2015-07-23 15:08:35 +03:00
cb = mei_cl_read_cb ( cl , NULL ) ;
if ( ! cb ) {
rets = 0 ;
goto out ;
}
2013-03-27 19:29:53 +04:00
2015-07-23 15:08:35 +03:00
copy :
if ( cb - > status ) {
rets = cb - > status ;
goto free ;
}
2015-05-07 15:54:03 +03:00
2015-07-23 15:08:35 +03:00
r_length = min_t ( size_t , length , cb - > buf_idx ) ;
memcpy ( buf , cb - > buf . data , r_length ) ;
rets = r_length ;
2015-05-07 15:54:03 +03:00
2015-07-23 15:08:35 +03:00
free :
mei_io_cb_free ( cb ) ;
out :
mutex_unlock ( & bus - > device_lock ) ;
return rets ;
2015-05-07 15:54:03 +03:00
}
2015-07-23 15:08:35 +03:00
/**
2015-09-10 10:18:05 +03:00
* mei_cldev_send - me device send ( write )
2015-07-23 15:08:35 +03:00
*
* @ cldev : me client device
* @ buf : buffer to send
* @ length : buffer length
*
* Return : written size in bytes or < 0 on error
*/
2015-09-10 10:18:05 +03:00
ssize_t mei_cldev_send ( struct mei_cl_device * cldev , u8 * buf , size_t length )
2015-05-07 15:54:03 +03:00
{
2015-07-23 15:08:35 +03:00
struct mei_cl * cl = cldev - > cl ;
2015-05-07 15:54:03 +03:00
2015-07-23 15:08:35 +03:00
if ( cl = = NULL )
return - ENODEV ;
2015-05-07 15:54:03 +03:00
2015-07-23 15:08:35 +03:00
return __mei_cl_send ( cl , buf , length , 1 ) ;
2015-05-07 15:54:03 +03:00
}
2015-09-10 10:18:05 +03:00
EXPORT_SYMBOL_GPL ( mei_cldev_send ) ;
2015-05-07 15:54:03 +03:00
2015-07-23 15:08:35 +03:00
/**
2015-09-10 10:18:05 +03:00
* mei_cldev_recv - client receive ( read )
2015-07-23 15:08:35 +03:00
*
* @ cldev : me client device
2015-10-13 15:02:40 +03:00
* @ buf : buffer to receive
2015-07-23 15:08:35 +03:00
* @ length : buffer length
*
* Return : read size in bytes of < 0 on error
*/
2015-09-10 10:18:05 +03:00
ssize_t mei_cldev_recv ( struct mei_cl_device * cldev , u8 * buf , size_t length )
2013-03-27 19:29:53 +04:00
{
2015-07-23 15:08:35 +03:00
struct mei_cl * cl = cldev - > cl ;
2013-03-27 19:29:53 +04:00
2015-07-23 15:08:35 +03:00
if ( cl = = NULL )
return - ENODEV ;
2013-03-27 19:29:53 +04:00
2015-07-23 15:08:35 +03:00
return __mei_cl_recv ( cl , buf , length ) ;
2013-03-27 19:29:53 +04:00
}
2015-09-10 10:18:05 +03:00
EXPORT_SYMBOL_GPL ( mei_cldev_recv ) ;
2013-03-27 19:29:53 +04:00
2015-07-23 15:08:35 +03:00
/**
2015-09-10 10:18:05 +03:00
* mei_cl_bus_event_work - dispatch rx event for a bus device
2015-07-23 15:08:35 +03:00
* and schedule new work
*
* @ work : work
*/
2015-09-10 10:18:05 +03:00
static void mei_cl_bus_event_work ( struct work_struct * work )
2013-03-27 19:29:53 +04:00
{
2015-07-23 15:08:35 +03:00
struct mei_cl_device * cldev ;
2015-05-07 15:54:02 +03:00
2015-07-23 15:08:35 +03:00
cldev = container_of ( work , struct mei_cl_device , event_work ) ;
2015-05-07 15:54:03 +03:00
2015-07-23 15:08:35 +03:00
if ( cldev - > event_cb )
cldev - > event_cb ( cldev , cldev - > events , cldev - > event_context ) ;
2015-05-07 15:54:03 +03:00
2015-07-23 15:08:35 +03:00
cldev - > events = 0 ;
2013-03-27 19:29:53 +04:00
2015-07-23 15:08:35 +03:00
/* Prepare for the next read */
2015-07-26 09:54:23 +03:00
if ( cldev - > events_mask & BIT ( MEI_CL_EVENT_RX ) )
mei_cl_read_start ( cldev - > cl , 0 , NULL ) ;
}
/**
* mei_cl_bus_notify_event - schedule notify cb on bus client
*
* @ cl : host client
*/
void mei_cl_bus_notify_event ( struct mei_cl * cl )
{
struct mei_cl_device * cldev = cl - > cldev ;
if ( ! cldev | | ! cldev - > event_cb )
return ;
if ( ! ( cldev - > events_mask & BIT ( MEI_CL_EVENT_NOTIF ) ) )
return ;
if ( ! cl - > notify_ev )
return ;
set_bit ( MEI_CL_EVENT_NOTIF , & cldev - > events ) ;
schedule_work ( & cldev - > event_work ) ;
cl - > notify_ev = false ;
2013-03-27 19:29:53 +04:00
}
2015-07-23 15:08:35 +03:00
/**
* mei_cl_bus_rx_event - schedule rx evenet
*
* @ cl : host client
*/
void mei_cl_bus_rx_event ( struct mei_cl * cl )
2013-03-27 19:29:53 +04:00
{
2015-07-23 15:08:35 +03:00
struct mei_cl_device * cldev = cl - > cldev ;
2015-05-04 09:43:54 +03:00
2015-07-23 15:08:35 +03:00
if ( ! cldev | | ! cldev - > event_cb )
2015-05-04 09:43:54 +03:00
return ;
2015-07-26 09:54:23 +03:00
if ( ! ( cldev - > events_mask & BIT ( MEI_CL_EVENT_RX ) ) )
return ;
2015-07-23 15:08:35 +03:00
set_bit ( MEI_CL_EVENT_RX , & cldev - > events ) ;
2013-03-27 19:29:56 +04:00
2015-07-23 15:08:35 +03:00
schedule_work ( & cldev - > event_work ) ;
2013-03-27 19:29:56 +04:00
}
2015-05-04 09:43:54 +03:00
2015-07-23 15:08:35 +03:00
/**
2015-09-10 10:18:05 +03:00
* mei_cldev_register_event_cb - register event callback
2015-07-23 15:08:35 +03:00
*
* @ cldev : me client devices
* @ event_cb : callback function
2015-07-26 09:54:23 +03:00
* @ events_mask : requested events bitmask
2015-07-23 15:08:35 +03:00
* @ context : driver context data
*
* Return : 0 on success
* - EALREADY if an callback is already registered
* < 0 on other errors
*/
2015-09-10 10:18:05 +03:00
int mei_cldev_register_event_cb ( struct mei_cl_device * cldev ,
unsigned long events_mask ,
mei_cldev_event_cb_t event_cb , void * context )
2013-03-27 19:29:53 +04:00
{
2015-07-23 15:08:38 +03:00
int ret ;
2015-07-23 15:08:35 +03:00
if ( cldev - > event_cb )
return - EALREADY ;
2013-03-27 19:29:53 +04:00
2015-07-23 15:08:35 +03:00
cldev - > events = 0 ;
2015-07-26 09:54:23 +03:00
cldev - > events_mask = events_mask ;
2015-07-23 15:08:35 +03:00
cldev - > event_cb = event_cb ;
cldev - > event_context = context ;
2015-09-10 10:18:05 +03:00
INIT_WORK ( & cldev - > event_work , mei_cl_bus_event_work ) ;
2013-03-27 19:29:56 +04:00
2015-07-26 09:54:23 +03:00
if ( cldev - > events_mask & BIT ( MEI_CL_EVENT_RX ) ) {
ret = mei_cl_read_start ( cldev - > cl , 0 , NULL ) ;
if ( ret & & ret ! = - EBUSY )
return ret ;
}
if ( cldev - > events_mask & BIT ( MEI_CL_EVENT_NOTIF ) ) {
mutex_lock ( & cldev - > cl - > dev - > device_lock ) ;
ret = mei_cl_notify_request ( cldev - > cl , NULL , event_cb ? 1 : 0 ) ;
mutex_unlock ( & cldev - > cl - > dev - > device_lock ) ;
if ( ret )
return ret ;
}
2013-03-27 19:29:53 +04:00
2015-07-23 15:08:35 +03:00
return 0 ;
}
2015-09-10 10:18:05 +03:00
EXPORT_SYMBOL_GPL ( mei_cldev_register_event_cb ) ;
2013-03-27 19:29:53 +04:00
2015-07-23 15:08:35 +03:00
/**
2015-09-10 10:18:05 +03:00
* mei_cldev_get_drvdata - driver data getter
2015-07-23 15:08:35 +03:00
*
* @ cldev : mei client device
*
* Return : driver private data
*/
2015-09-10 10:18:05 +03:00
void * mei_cldev_get_drvdata ( const struct mei_cl_device * cldev )
2015-07-23 15:08:35 +03:00
{
return dev_get_drvdata ( & cldev - > dev ) ;
2013-03-27 19:29:53 +04:00
}
2015-09-10 10:18:05 +03:00
EXPORT_SYMBOL_GPL ( mei_cldev_get_drvdata ) ;
2013-03-27 19:29:53 +04:00
2015-07-23 15:08:35 +03:00
/**
2015-09-10 10:18:05 +03:00
* mei_cldev_set_drvdata - driver data setter
2015-07-23 15:08:35 +03:00
*
* @ cldev : mei client device
* @ data : data to store
*/
2015-09-10 10:18:05 +03:00
void mei_cldev_set_drvdata ( struct mei_cl_device * cldev , void * data )
2013-03-27 19:29:53 +04:00
{
2015-07-23 15:08:35 +03:00
dev_set_drvdata ( & cldev - > dev , data ) ;
2013-03-27 19:29:53 +04:00
}
2015-09-10 10:18:05 +03:00
EXPORT_SYMBOL_GPL ( mei_cldev_set_drvdata ) ;
2013-03-27 19:29:54 +04:00
2015-09-10 10:18:02 +03:00
/**
* mei_cldev_uuid - return uuid of the underlying me client
*
* @ cldev : mei client device
*
* Return : me client uuid
*/
const uuid_le * mei_cldev_uuid ( const struct mei_cl_device * cldev )
{
return mei_me_cl_uuid ( cldev - > me_cl ) ;
}
EXPORT_SYMBOL_GPL ( mei_cldev_uuid ) ;
/**
* mei_cldev_ver - return protocol version of the underlying me client
*
* @ cldev : mei client device
*
* Return : me client protocol version
*/
u8 mei_cldev_ver ( const struct mei_cl_device * cldev )
{
return mei_me_cl_ver ( cldev - > me_cl ) ;
}
EXPORT_SYMBOL_GPL ( mei_cldev_ver ) ;
2015-09-10 10:18:03 +03:00
/**
* mei_cldev_enabled - check whether the device is enabled
*
* @ cldev : mei client device
*
* Return : true if me client is initialized and connected
*/
bool mei_cldev_enabled ( struct mei_cl_device * cldev )
{
return cldev - > cl & & mei_cl_is_connected ( cldev - > cl ) ;
}
EXPORT_SYMBOL_GPL ( mei_cldev_enabled ) ;
2015-07-23 15:08:35 +03:00
/**
2015-09-10 10:18:05 +03:00
* mei_cldev_enable_device - enable me client device
2015-07-23 15:08:35 +03:00
* create connection with me client
*
* @ cldev : me client device
*
* Return : 0 on success and < 0 on error
*/
2015-09-10 10:18:05 +03:00
int mei_cldev_enable ( struct mei_cl_device * cldev )
2013-03-27 19:29:54 +04:00
{
2015-07-23 15:08:47 +03:00
struct mei_device * bus = cldev - > bus ;
struct mei_cl * cl ;
int ret ;
2013-03-27 19:29:54 +04:00
2015-07-23 15:08:47 +03:00
cl = cldev - > cl ;
2013-03-27 19:29:54 +04:00
2015-07-23 15:08:47 +03:00
if ( ! cl ) {
mutex_lock ( & bus - > device_lock ) ;
cl = mei_cl_alloc_linked ( bus , MEI_HOST_CLIENT_ID_ANY ) ;
mutex_unlock ( & bus - > device_lock ) ;
if ( IS_ERR ( cl ) )
return PTR_ERR ( cl ) ;
/* update pointers */
cldev - > cl = cl ;
cl - > cldev = cldev ;
}
2013-03-27 19:29:54 +04:00
2015-07-23 15:08:35 +03:00
mutex_lock ( & bus - > device_lock ) ;
if ( mei_cl_is_connected ( cl ) ) {
2015-07-23 15:08:47 +03:00
ret = 0 ;
goto out ;
2015-07-23 15:08:35 +03:00
}
2013-03-27 19:29:54 +04:00
2015-07-23 15:08:47 +03:00
if ( ! mei_me_cl_is_active ( cldev - > me_cl ) ) {
dev_err ( & cldev - > dev , " me client is not active \n " ) ;
ret = - ENOTTY ;
goto out ;
2015-07-23 15:08:35 +03:00
}
2015-07-23 15:08:47 +03:00
ret = mei_cl_connect ( cl , cldev - > me_cl , NULL ) ;
if ( ret < 0 )
dev_err ( & cldev - > dev , " cannot connect \n " ) ;
out :
2015-07-23 15:08:35 +03:00
mutex_unlock ( & bus - > device_lock ) ;
2015-07-23 15:08:47 +03:00
return ret ;
2013-03-27 19:29:54 +04:00
}
2015-09-10 10:18:05 +03:00
EXPORT_SYMBOL_GPL ( mei_cldev_enable ) ;
2013-03-27 19:29:55 +04:00
2015-07-23 15:08:35 +03:00
/**
2015-09-10 10:18:05 +03:00
* mei_cldev_disable - disable me client device
2015-07-23 15:08:35 +03:00
* disconnect form the me client
*
* @ cldev : me client device
*
* Return : 0 on success and < 0 on error
*/
2015-09-10 10:18:05 +03:00
int mei_cldev_disable ( struct mei_cl_device * cldev )
2013-03-27 19:29:55 +04:00
{
2015-07-23 15:08:33 +03:00
struct mei_device * bus ;
2015-07-23 15:08:47 +03:00
struct mei_cl * cl ;
int err ;
2013-03-27 19:29:55 +04:00
2015-07-23 15:08:47 +03:00
if ( ! cldev | | ! cldev - > cl )
2013-03-27 19:29:55 +04:00
return - ENODEV ;
2015-07-23 15:08:47 +03:00
cl = cldev - > cl ;
bus = cldev - > bus ;
2013-04-08 22:56:37 +04:00
2015-07-23 15:08:35 +03:00
cldev - > event_cb = NULL ;
2013-03-27 19:29:55 +04:00
2015-07-23 15:08:35 +03:00
mutex_lock ( & bus - > device_lock ) ;
2013-04-08 22:56:37 +04:00
2015-07-23 15:08:35 +03:00
if ( ! mei_cl_is_connected ( cl ) ) {
dev_err ( bus - > dev , " Already disconnected " ) ;
err = 0 ;
2015-01-11 01:07:16 +03:00
goto out ;
}
2013-04-08 22:56:37 +04:00
2015-07-23 15:08:35 +03:00
err = mei_cl_disconnect ( cl ) ;
2015-07-23 15:08:47 +03:00
if ( err < 0 )
2015-07-23 15:08:35 +03:00
dev_err ( bus - > dev , " Could not disconnect from the ME client " ) ;
2013-03-27 19:29:55 +04:00
2015-07-23 15:08:47 +03:00
out :
2015-07-23 15:08:35 +03:00
/* Flush queues and remove any pending read */
mei_cl_flush_queues ( cl , NULL ) ;
2015-07-23 15:08:47 +03:00
mei_cl_unlink ( cl ) ;
kfree ( cl ) ;
cldev - > cl = NULL ;
2013-03-27 19:29:55 +04:00
2015-07-23 15:08:33 +03:00
mutex_unlock ( & bus - > device_lock ) ;
2015-07-23 15:08:35 +03:00
return err ;
2013-03-27 19:29:55 +04:00
}
2015-09-10 10:18:05 +03:00
EXPORT_SYMBOL_GPL ( mei_cldev_disable ) ;
2013-03-27 19:29:55 +04:00
2015-07-23 15:08:39 +03:00
/**
* mei_cl_device_find - find matching entry in the driver id table
*
* @ cldev : me client device
* @ cldrv : me client driver
*
* Return : id on success ; NULL if no id is matching
*/
static const
struct mei_cl_device_id * mei_cl_device_find ( struct mei_cl_device * cldev ,
struct mei_cl_driver * cldrv )
2013-03-27 19:29:55 +04:00
{
2015-07-23 15:08:35 +03:00
const struct mei_cl_device_id * id ;
const uuid_le * uuid ;
2015-09-10 10:18:01 +03:00
u8 version ;
bool match ;
2013-03-27 19:29:55 +04:00
2015-07-23 15:08:35 +03:00
uuid = mei_me_cl_uuid ( cldev - > me_cl ) ;
2015-09-10 10:18:01 +03:00
version = mei_me_cl_ver ( cldev - > me_cl ) ;
2013-03-27 19:29:55 +04:00
2015-07-23 15:08:35 +03:00
id = cldrv - > id_table ;
while ( uuid_le_cmp ( NULL_UUID_LE , id - > uuid ) ) {
if ( ! uuid_le_cmp ( * uuid , id - > uuid ) ) {
2015-09-10 10:18:01 +03:00
match = true ;
2015-07-23 15:08:39 +03:00
2015-09-10 10:18:01 +03:00
if ( cldev - > name [ 0 ] )
if ( strncmp ( cldev - > name , id - > name ,
sizeof ( id - > name ) ) )
match = false ;
2015-07-23 15:08:39 +03:00
2015-09-10 10:18:01 +03:00
if ( id - > version ! = MEI_CL_VERSION_ANY )
if ( id - > version ! = version )
match = false ;
if ( match )
2015-07-23 15:08:39 +03:00
return id ;
2015-07-23 15:08:35 +03:00
}
2013-09-02 14:29:46 +04:00
2015-07-23 15:08:35 +03:00
id + + ;
}
2013-03-27 19:29:55 +04:00
2015-07-23 15:08:39 +03:00
return NULL ;
}
/**
* mei_cl_device_match - device match function
*
* @ dev : device
* @ drv : driver
*
* Return : 1 if matching device was found 0 otherwise
*/
static int mei_cl_device_match ( struct device * dev , struct device_driver * drv )
{
struct mei_cl_device * cldev = to_mei_cl_device ( dev ) ;
struct mei_cl_driver * cldrv = to_mei_cl_driver ( drv ) ;
const struct mei_cl_device_id * found_id ;
if ( ! cldev )
return 0 ;
2015-07-23 15:08:43 +03:00
if ( ! cldev - > do_match )
return 0 ;
2015-07-23 15:08:39 +03:00
if ( ! cldrv | | ! cldrv - > id_table )
return 0 ;
found_id = mei_cl_device_find ( cldev , cldrv ) ;
if ( found_id )
return 1 ;
2015-07-23 15:08:35 +03:00
return 0 ;
}
2013-09-02 14:29:46 +04:00
2015-07-23 15:08:40 +03:00
/**
* mei_cl_device_probe - bus probe function
*
* @ dev : device
*
* Return : 0 on success ; < 0 otherwise
*/
2015-07-23 15:08:35 +03:00
static int mei_cl_device_probe ( struct device * dev )
{
2015-07-23 15:08:40 +03:00
struct mei_cl_device * cldev ;
2015-07-23 15:08:35 +03:00
struct mei_cl_driver * cldrv ;
2015-07-23 15:08:40 +03:00
const struct mei_cl_device_id * id ;
cldev = to_mei_cl_device ( dev ) ;
cldrv = to_mei_cl_driver ( dev - > driver ) ;
2013-03-27 19:29:55 +04:00
2015-07-23 15:08:35 +03:00
if ( ! cldev )
return 0 ;
2013-03-27 19:29:55 +04:00
2015-07-23 15:08:35 +03:00
if ( ! cldrv | | ! cldrv - > probe )
return - ENODEV ;
2015-07-23 15:08:40 +03:00
id = mei_cl_device_find ( cldev , cldrv ) ;
if ( ! id )
return - ENODEV ;
2015-07-23 15:08:35 +03:00
2015-07-23 15:08:40 +03:00
__module_get ( THIS_MODULE ) ;
2015-07-23 15:08:35 +03:00
2015-07-23 15:08:40 +03:00
return cldrv - > probe ( cldev , id ) ;
2015-07-23 15:08:35 +03:00
}
2013-03-27 19:29:55 +04:00
2015-07-23 15:08:40 +03:00
/**
* mei_cl_device_remove - remove device from the bus
*
* @ dev : device
*
* Return : 0 on success ; < 0 otherwise
*/
2015-07-23 15:08:35 +03:00
static int mei_cl_device_remove ( struct device * dev )
{
struct mei_cl_device * cldev = to_mei_cl_device ( dev ) ;
struct mei_cl_driver * cldrv ;
2015-07-23 15:08:40 +03:00
int ret = 0 ;
2013-03-27 19:29:55 +04:00
2015-07-23 15:08:35 +03:00
if ( ! cldev | | ! dev - > driver )
return 0 ;
if ( cldev - > event_cb ) {
cldev - > event_cb = NULL ;
cancel_work_sync ( & cldev - > event_work ) ;
2015-02-10 11:39:36 +03:00
}
2015-07-23 15:08:35 +03:00
cldrv = to_mei_cl_driver ( dev - > driver ) ;
2015-07-23 15:08:40 +03:00
if ( cldrv - > remove )
ret = cldrv - > remove ( cldev ) ;
2013-03-27 19:29:55 +04:00
2015-07-23 15:08:40 +03:00
module_put ( THIS_MODULE ) ;
dev - > driver = NULL ;
return ret ;
2013-03-27 19:29:55 +04:00
}
2015-07-23 15:08:35 +03:00
static ssize_t name_show ( struct device * dev , struct device_attribute * a ,
char * buf )
2013-03-27 19:29:55 +04:00
{
2015-07-23 15:08:35 +03:00
struct mei_cl_device * cldev = to_mei_cl_device ( dev ) ;
size_t len ;
2013-03-27 19:29:55 +04:00
2015-07-23 15:08:35 +03:00
len = snprintf ( buf , PAGE_SIZE , " %s " , cldev - > name ) ;
2013-03-27 19:29:55 +04:00
2015-07-23 15:08:35 +03:00
return ( len > = PAGE_SIZE ) ? ( PAGE_SIZE - 1 ) : len ;
2013-03-27 19:29:55 +04:00
}
2015-07-23 15:08:35 +03:00
static DEVICE_ATTR_RO ( name ) ;
2013-03-27 19:29:55 +04:00
2015-07-23 15:08:35 +03:00
static ssize_t uuid_show ( struct device * dev , struct device_attribute * a ,
char * buf )
2013-03-27 19:29:55 +04:00
{
2015-07-23 15:08:35 +03:00
struct mei_cl_device * cldev = to_mei_cl_device ( dev ) ;
const uuid_le * uuid = mei_me_cl_uuid ( cldev - > me_cl ) ;
size_t len ;
2013-03-27 19:29:55 +04:00
2015-07-23 15:08:35 +03:00
len = snprintf ( buf , PAGE_SIZE , " %pUl " , uuid ) ;
2013-03-27 19:29:55 +04:00
2015-07-23 15:08:35 +03:00
return ( len > = PAGE_SIZE ) ? ( PAGE_SIZE - 1 ) : len ;
2013-03-27 19:29:55 +04:00
}
2015-07-23 15:08:35 +03:00
static DEVICE_ATTR_RO ( uuid ) ;
2013-03-27 19:29:55 +04:00
2015-09-10 10:18:00 +03:00
static ssize_t version_show ( struct device * dev , struct device_attribute * a ,
char * buf )
{
struct mei_cl_device * cldev = to_mei_cl_device ( dev ) ;
u8 version = mei_me_cl_ver ( cldev - > me_cl ) ;
size_t len ;
len = snprintf ( buf , PAGE_SIZE , " %02X " , version ) ;
return ( len > = PAGE_SIZE ) ? ( PAGE_SIZE - 1 ) : len ;
}
static DEVICE_ATTR_RO ( version ) ;
2015-07-23 15:08:35 +03:00
static ssize_t modalias_show ( struct device * dev , struct device_attribute * a ,
char * buf )
2013-03-27 19:29:55 +04:00
{
2015-07-23 15:08:35 +03:00
struct mei_cl_device * cldev = to_mei_cl_device ( dev ) ;
const uuid_le * uuid = mei_me_cl_uuid ( cldev - > me_cl ) ;
size_t len ;
2013-03-27 19:29:55 +04:00
2015-09-10 10:17:59 +03:00
len = snprintf ( buf , PAGE_SIZE , " mei:%s:%pUl: " , cldev - > name , uuid ) ;
2015-07-23 15:08:35 +03:00
return ( len > = PAGE_SIZE ) ? ( PAGE_SIZE - 1 ) : len ;
2013-03-27 19:29:55 +04:00
}
2015-07-23 15:08:35 +03:00
static DEVICE_ATTR_RO ( modalias ) ;
2013-03-27 19:29:55 +04:00
2015-09-10 10:18:05 +03:00
static struct attribute * mei_cldev_attrs [ ] = {
2015-07-23 15:08:35 +03:00
& dev_attr_name . attr ,
& dev_attr_uuid . attr ,
2015-09-10 10:18:00 +03:00
& dev_attr_version . attr ,
2015-07-23 15:08:35 +03:00
& dev_attr_modalias . attr ,
NULL ,
} ;
2015-09-10 10:18:05 +03:00
ATTRIBUTE_GROUPS ( mei_cldev ) ;
2015-07-23 15:08:35 +03:00
2015-07-23 15:08:36 +03:00
/**
* mei_cl_device_uevent - me client bus uevent handler
*
* @ dev : device
* @ env : uevent kobject
*
* Return : 0 on success - ENOMEM on when add_uevent_var fails
*/
static int mei_cl_device_uevent ( struct device * dev , struct kobj_uevent_env * env )
2013-03-27 19:29:55 +04:00
{
2015-07-23 15:08:35 +03:00
struct mei_cl_device * cldev = to_mei_cl_device ( dev ) ;
const uuid_le * uuid = mei_me_cl_uuid ( cldev - > me_cl ) ;
2015-09-10 10:18:00 +03:00
u8 version = mei_me_cl_ver ( cldev - > me_cl ) ;
if ( add_uevent_var ( env , " MEI_CL_VERSION=%d " , version ) )
return - ENOMEM ;
2013-03-27 19:29:55 +04:00
2015-07-23 15:08:35 +03:00
if ( add_uevent_var ( env , " MEI_CL_UUID=%pUl " , uuid ) )
return - ENOMEM ;
2013-03-27 19:29:55 +04:00
2015-07-23 15:08:35 +03:00
if ( add_uevent_var ( env , " MEI_CL_NAME=%s " , cldev - > name ) )
return - ENOMEM ;
2015-09-10 10:18:01 +03:00
if ( add_uevent_var ( env , " MODALIAS=mei:%s:%pUl:%02X: " ,
cldev - > name , uuid , version ) )
2015-07-23 15:08:35 +03:00
return - ENOMEM ;
2013-03-27 19:29:55 +04:00
return 0 ;
}
2013-03-27 19:29:57 +04:00
2015-07-23 15:08:35 +03:00
static struct bus_type mei_cl_bus_type = {
. name = " mei " ,
2015-09-10 10:18:05 +03:00
. dev_groups = mei_cldev_groups ,
2015-07-23 15:08:35 +03:00
. match = mei_cl_device_match ,
. probe = mei_cl_device_probe ,
. remove = mei_cl_device_remove ,
2015-07-23 15:08:36 +03:00
. uevent = mei_cl_device_uevent ,
2015-07-23 15:08:35 +03:00
} ;
2015-07-23 15:08:41 +03:00
static struct mei_device * mei_dev_bus_get ( struct mei_device * bus )
{
if ( bus )
get_device ( bus - > dev ) ;
return bus ;
}
static void mei_dev_bus_put ( struct mei_device * bus )
{
if ( bus )
put_device ( bus - > dev ) ;
}
2015-09-10 10:18:06 +03:00
static void mei_cl_bus_dev_release ( struct device * dev )
2013-03-27 19:29:59 +04:00
{
2015-07-23 15:08:35 +03:00
struct mei_cl_device * cldev = to_mei_cl_device ( dev ) ;
if ( ! cldev )
return ;
mei_me_cl_put ( cldev - > me_cl ) ;
2015-07-23 15:08:41 +03:00
mei_dev_bus_put ( cldev - > bus ) ;
2015-07-23 15:08:35 +03:00
kfree ( cldev ) ;
2013-03-27 19:29:59 +04:00
}
2015-07-23 15:08:35 +03:00
static struct device_type mei_cl_device_type = {
2015-09-10 10:18:06 +03:00
. release = mei_cl_bus_dev_release ,
2015-07-23 15:08:35 +03:00
} ;
2015-07-23 15:08:43 +03:00
/**
2015-09-10 10:18:06 +03:00
* mei_cl_bus_dev_alloc - initialize and allocate mei client device
2015-07-23 15:08:43 +03:00
*
* @ bus : mei device
* @ me_cl : me client
*
* Return : allocated device structur or NULL on allocation failure
*/
2015-09-10 10:18:06 +03:00
static struct mei_cl_device * mei_cl_bus_dev_alloc ( struct mei_device * bus ,
struct mei_me_client * me_cl )
2015-07-23 15:08:43 +03:00
{
struct mei_cl_device * cldev ;
cldev = kzalloc ( sizeof ( struct mei_cl_device ) , GFP_KERNEL ) ;
if ( ! cldev )
return NULL ;
device_initialize ( & cldev - > dev ) ;
cldev - > dev . parent = bus - > dev ;
cldev - > dev . bus = & mei_cl_bus_type ;
cldev - > dev . type = & mei_cl_device_type ;
cldev - > bus = mei_dev_bus_get ( bus ) ;
cldev - > me_cl = mei_me_cl_get ( me_cl ) ;
cldev - > is_added = 0 ;
INIT_LIST_HEAD ( & cldev - > bus_list ) ;
return cldev ;
}
/**
* mei_cl_dev_setup - setup me client device
* run fix up routines and set the device name
*
* @ bus : mei device
* @ cldev : me client device
*
* Return : true if the device is eligible for enumeration
*/
2015-09-10 10:18:06 +03:00
static bool mei_cl_bus_dev_setup ( struct mei_device * bus ,
struct mei_cl_device * cldev )
2015-07-23 15:08:43 +03:00
{
cldev - > do_match = 1 ;
2015-09-10 10:18:06 +03:00
mei_cl_bus_dev_fixup ( cldev ) ;
2015-07-23 15:08:43 +03:00
if ( cldev - > do_match )
2015-09-10 10:18:01 +03:00
dev_set_name ( & cldev - > dev , " mei:%s:%pUl:%02X " ,
cldev - > name ,
mei_me_cl_uuid ( cldev - > me_cl ) ,
mei_me_cl_ver ( cldev - > me_cl ) ) ;
2015-07-23 15:08:43 +03:00
return cldev - > do_match = = 1 ;
}
/**
* mei_cl_bus_dev_add - add me client devices
*
* @ cldev : me client device
*
* Return : 0 on success ; < 0 on failre
*/
static int mei_cl_bus_dev_add ( struct mei_cl_device * cldev )
{
int ret ;
2015-09-10 10:18:01 +03:00
dev_dbg ( cldev - > bus - > dev , " adding %pUL:%02X \n " ,
mei_me_cl_uuid ( cldev - > me_cl ) ,
mei_me_cl_ver ( cldev - > me_cl ) ) ;
2015-07-23 15:08:43 +03:00
ret = device_add ( & cldev - > dev ) ;
if ( ! ret )
cldev - > is_added = 1 ;
return ret ;
}
2015-07-23 15:08:47 +03:00
/**
* mei_cl_bus_dev_stop - stop the driver
*
* @ cldev : me client device
*/
static void mei_cl_bus_dev_stop ( struct mei_cl_device * cldev )
{
if ( cldev - > is_added )
device_release_driver ( & cldev - > dev ) ;
}
/**
* mei_cl_bus_dev_destroy - destroy me client devices object
*
* @ cldev : me client device
*/
static void mei_cl_bus_dev_destroy ( struct mei_cl_device * cldev )
{
if ( ! cldev - > is_added )
return ;
device_del ( & cldev - > dev ) ;
mutex_lock ( & cldev - > bus - > cl_bus_lock ) ;
list_del_init ( & cldev - > bus_list ) ;
mutex_unlock ( & cldev - > bus - > cl_bus_lock ) ;
cldev - > is_added = 0 ;
put_device ( & cldev - > dev ) ;
}
/**
* mei_cl_bus_remove_device - remove a devices form the bus
*
* @ cldev : me client device
*/
static void mei_cl_bus_remove_device ( struct mei_cl_device * cldev )
{
mei_cl_bus_dev_stop ( cldev ) ;
mei_cl_bus_dev_destroy ( cldev ) ;
}
/**
* mei_cl_bus_remove_devices - remove all devices form the bus
*
* @ bus : mei device
*/
void mei_cl_bus_remove_devices ( struct mei_device * bus )
{
struct mei_cl_device * cldev , * next ;
list_for_each_entry_safe ( cldev , next , & bus - > device_list , bus_list )
mei_cl_bus_remove_device ( cldev ) ;
}
/**
2015-09-10 10:18:06 +03:00
* mei_cl_bus_dev_init - allocate and initializes an mei client devices
2015-07-23 15:08:47 +03:00
* based on me client
*
* @ bus : mei device
* @ me_cl : me client
*/
2015-09-10 10:18:06 +03:00
static void mei_cl_bus_dev_init ( struct mei_device * bus ,
struct mei_me_client * me_cl )
2013-04-09 02:51:38 +04:00
{
2015-07-23 15:08:35 +03:00
struct mei_cl_device * cldev ;
2015-07-23 15:08:47 +03:00
dev_dbg ( bus - > dev , " initializing %pUl " , mei_me_cl_uuid ( me_cl ) ) ;
if ( me_cl - > bus_added )
return ;
2013-04-09 02:51:38 +04:00
2015-09-10 10:18:06 +03:00
cldev = mei_cl_bus_dev_alloc ( bus , me_cl ) ;
2015-07-23 15:08:35 +03:00
if ( ! cldev )
2015-07-23 15:08:47 +03:00
return ;
2013-04-09 02:51:38 +04:00
2015-07-23 15:08:47 +03:00
mutex_lock ( & cldev - > bus - > cl_bus_lock ) ;
me_cl - > bus_added = true ;
list_add_tail ( & cldev - > bus_list , & bus - > device_list ) ;
mutex_unlock ( & cldev - > bus - > cl_bus_lock ) ;
2015-05-04 09:43:53 +03:00
2015-07-23 15:08:47 +03:00
}
2013-04-09 02:51:38 +04:00
2015-07-23 15:08:47 +03:00
/**
* mei_cl_bus_rescan - scan me clients list and add create
* devices for eligible clients
*
* @ bus : mei device
*/
void mei_cl_bus_rescan ( struct mei_device * bus )
{
struct mei_cl_device * cldev , * n ;
struct mei_me_client * me_cl ;
2013-04-09 02:51:38 +04:00
2015-07-23 15:08:47 +03:00
down_read ( & bus - > me_clients_rwsem ) ;
list_for_each_entry ( me_cl , & bus - > me_clients , list )
2015-09-10 10:18:06 +03:00
mei_cl_bus_dev_init ( bus , me_cl ) ;
2015-07-23 15:08:47 +03:00
up_read ( & bus - > me_clients_rwsem ) ;
2013-04-09 02:51:38 +04:00
2015-07-23 15:08:47 +03:00
mutex_lock ( & bus - > cl_bus_lock ) ;
list_for_each_entry_safe ( cldev , n , & bus - > device_list , bus_list ) {
2013-04-09 02:51:38 +04:00
2015-07-23 15:08:47 +03:00
if ( ! mei_me_cl_is_active ( cldev - > me_cl ) ) {
mei_cl_bus_remove_device ( cldev ) ;
continue ;
}
2013-04-09 02:51:38 +04:00
2015-07-23 15:08:47 +03:00
if ( cldev - > is_added )
continue ;
2015-09-10 10:18:06 +03:00
if ( mei_cl_bus_dev_setup ( bus , cldev ) )
2015-07-23 15:08:47 +03:00
mei_cl_bus_dev_add ( cldev ) ;
else {
list_del_init ( & cldev - > bus_list ) ;
put_device ( & cldev - > dev ) ;
}
}
mutex_unlock ( & bus - > cl_bus_lock ) ;
dev_dbg ( bus - > dev , " rescan end " ) ;
2015-07-23 15:08:35 +03:00
}
2013-04-09 02:51:38 +04:00
2015-09-10 10:18:05 +03:00
int __mei_cldev_driver_register ( struct mei_cl_driver * cldrv ,
struct module * owner )
2015-07-23 15:08:35 +03:00
{
int err ;
2013-04-09 02:51:38 +04:00
2015-07-23 15:08:35 +03:00
cldrv - > driver . name = cldrv - > name ;
cldrv - > driver . owner = owner ;
cldrv - > driver . bus = & mei_cl_bus_type ;
2013-04-09 02:51:38 +04:00
2015-07-23 15:08:35 +03:00
err = driver_register ( & cldrv - > driver ) ;
if ( err )
return err ;
2013-04-09 02:51:38 +04:00
2015-07-23 15:08:35 +03:00
pr_debug ( " mei: driver [%s] registered \n " , cldrv - > driver . name ) ;
2013-04-09 02:51:38 +04:00
2015-07-23 15:08:35 +03:00
return 0 ;
2013-04-09 02:51:38 +04:00
}
2015-09-10 10:18:05 +03:00
EXPORT_SYMBOL_GPL ( __mei_cldev_driver_register ) ;
2013-04-09 02:51:38 +04:00
2015-09-10 10:18:05 +03:00
void mei_cldev_driver_unregister ( struct mei_cl_driver * cldrv )
2013-03-27 19:29:57 +04:00
{
2015-07-23 15:08:35 +03:00
driver_unregister ( & cldrv - > driver ) ;
2013-03-27 19:29:57 +04:00
2015-07-23 15:08:35 +03:00
pr_debug ( " mei: driver [%s] unregistered \n " , cldrv - > driver . name ) ;
2013-03-27 19:29:57 +04:00
}
2015-09-10 10:18:05 +03:00
EXPORT_SYMBOL_GPL ( mei_cldev_driver_unregister ) ;
2013-03-27 19:29:57 +04:00
2015-07-23 15:08:47 +03:00
2013-03-27 19:29:57 +04:00
int __init mei_cl_bus_init ( void )
{
return bus_register ( & mei_cl_bus_type ) ;
}
void __exit mei_cl_bus_exit ( void )
{
bus_unregister ( & mei_cl_bus_type ) ;
}