2019-05-27 09:55:21 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2017-11-01 22:25:27 +03:00
/*
* Dell WMI descriptor driver
*
* Copyright ( C ) 2017 Dell Inc . All Rights Reserved .
*/
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
# include <linux/acpi.h>
# include <linux/list.h>
# include <linux/module.h>
# include <linux/wmi.h>
# include "dell-wmi-descriptor.h"
2017-11-09 20:49:10 +03:00
# define DELL_WMI_DESCRIPTOR_GUID "8D9DDCBC-A997-11DA-B012-B622A1EF5492"
2017-11-01 22:25:27 +03:00
struct descriptor_priv {
struct list_head list ;
u32 interface_version ;
u32 size ;
2017-11-17 07:13:12 +03:00
u32 hotfix ;
2017-11-01 22:25:27 +03:00
} ;
2017-11-09 20:49:10 +03:00
static int descriptor_valid = - EPROBE_DEFER ;
2017-11-01 22:25:27 +03:00
static LIST_HEAD ( wmi_list ) ;
static DEFINE_MUTEX ( list_mutex ) ;
2017-11-09 20:49:10 +03:00
int dell_wmi_get_descriptor_valid ( void )
{
if ( ! wmi_has_guid ( DELL_WMI_DESCRIPTOR_GUID ) )
return - ENODEV ;
return descriptor_valid ;
}
EXPORT_SYMBOL_GPL ( dell_wmi_get_descriptor_valid ) ;
2017-11-01 22:25:27 +03:00
bool dell_wmi_get_interface_version ( u32 * version )
{
struct descriptor_priv * priv ;
bool ret = false ;
mutex_lock ( & list_mutex ) ;
priv = list_first_entry_or_null ( & wmi_list ,
struct descriptor_priv ,
list ) ;
if ( priv ) {
* version = priv - > interface_version ;
ret = true ;
}
mutex_unlock ( & list_mutex ) ;
return ret ;
}
EXPORT_SYMBOL_GPL ( dell_wmi_get_interface_version ) ;
bool dell_wmi_get_size ( u32 * size )
{
struct descriptor_priv * priv ;
bool ret = false ;
mutex_lock ( & list_mutex ) ;
priv = list_first_entry_or_null ( & wmi_list ,
struct descriptor_priv ,
list ) ;
if ( priv ) {
* size = priv - > size ;
ret = true ;
}
mutex_unlock ( & list_mutex ) ;
return ret ;
}
EXPORT_SYMBOL_GPL ( dell_wmi_get_size ) ;
2017-11-17 07:13:12 +03:00
bool dell_wmi_get_hotfix ( u32 * hotfix )
{
struct descriptor_priv * priv ;
bool ret = false ;
mutex_lock ( & list_mutex ) ;
priv = list_first_entry_or_null ( & wmi_list ,
struct descriptor_priv ,
list ) ;
if ( priv ) {
* hotfix = priv - > hotfix ;
ret = true ;
}
mutex_unlock ( & list_mutex ) ;
return ret ;
}
EXPORT_SYMBOL_GPL ( dell_wmi_get_hotfix ) ;
2017-11-01 22:25:27 +03:00
/*
* Descriptor buffer is 128 byte long and contains :
*
* Name Offset Length Value
* Vendor Signature 0 4 " DELL "
* Object Signature 4 4 " WMI "
* WMI Interface Version 8 4 < version >
* WMI buffer length 12 4 < length >
2017-11-17 07:13:12 +03:00
* WMI hotfix number 16 4 < hotfix >
2017-11-01 22:25:27 +03:00
*/
2019-05-27 19:21:30 +03:00
static int dell_wmi_descriptor_probe ( struct wmi_device * wdev ,
const void * context )
2017-11-01 22:25:27 +03:00
{
union acpi_object * obj = NULL ;
struct descriptor_priv * priv ;
u32 * buffer ;
int ret ;
obj = wmidev_block_query ( wdev , 0 ) ;
if ( ! obj ) {
dev_err ( & wdev - > dev , " failed to read Dell WMI descriptor \n " ) ;
ret = - EIO ;
goto out ;
}
if ( obj - > type ! = ACPI_TYPE_BUFFER ) {
dev_err ( & wdev - > dev , " Dell descriptor has wrong type \n " ) ;
ret = - EINVAL ;
2017-11-09 20:49:10 +03:00
descriptor_valid = ret ;
2017-11-01 22:25:27 +03:00
goto out ;
}
/* Although it's not technically a failure, this would lead to
* unexpected behavior
*/
if ( obj - > buffer . length ! = 128 ) {
dev_err ( & wdev - > dev ,
" Dell descriptor buffer has unexpected length (%d) \n " ,
obj - > buffer . length ) ;
ret = - EINVAL ;
2017-11-09 20:49:10 +03:00
descriptor_valid = ret ;
2017-11-01 22:25:27 +03:00
goto out ;
}
buffer = ( u32 * ) obj - > buffer . pointer ;
if ( strncmp ( obj - > string . pointer , " DELL WMI " , 8 ) ! = 0 ) {
dev_err ( & wdev - > dev , " Dell descriptor buffer has invalid signature (%8ph) \n " ,
buffer ) ;
ret = - EINVAL ;
2017-11-09 20:49:10 +03:00
descriptor_valid = ret ;
2017-11-01 22:25:27 +03:00
goto out ;
}
2017-11-09 20:49:10 +03:00
descriptor_valid = 0 ;
2017-11-01 22:25:27 +03:00
if ( buffer [ 2 ] ! = 0 & & buffer [ 2 ] ! = 1 )
dev_warn ( & wdev - > dev , " Dell descriptor buffer has unknown version (%lu) \n " ,
( unsigned long ) buffer [ 2 ] ) ;
priv = devm_kzalloc ( & wdev - > dev , sizeof ( struct descriptor_priv ) ,
GFP_KERNEL ) ;
2017-11-09 20:49:09 +03:00
if ( ! priv ) {
ret = - ENOMEM ;
goto out ;
}
2017-11-01 22:25:27 +03:00
priv - > interface_version = buffer [ 2 ] ;
priv - > size = buffer [ 3 ] ;
2017-11-17 07:13:12 +03:00
priv - > hotfix = buffer [ 4 ] ;
2017-11-01 22:25:27 +03:00
ret = 0 ;
dev_set_drvdata ( & wdev - > dev , priv ) ;
mutex_lock ( & list_mutex ) ;
list_add_tail ( & priv - > list , & wmi_list ) ;
mutex_unlock ( & list_mutex ) ;
2017-11-17 07:13:12 +03:00
dev_dbg ( & wdev - > dev , " Detected Dell WMI interface version %lu, buffer size %lu, hotfix %lu \n " ,
2017-11-01 22:25:27 +03:00
( unsigned long ) priv - > interface_version ,
2017-11-17 07:13:12 +03:00
( unsigned long ) priv - > size ,
( unsigned long ) priv - > hotfix ) ;
2017-11-01 22:25:27 +03:00
out :
kfree ( obj ) ;
return ret ;
}
static int dell_wmi_descriptor_remove ( struct wmi_device * wdev )
{
struct descriptor_priv * priv = dev_get_drvdata ( & wdev - > dev ) ;
mutex_lock ( & list_mutex ) ;
list_del ( & priv - > list ) ;
mutex_unlock ( & list_mutex ) ;
return 0 ;
}
static const struct wmi_device_id dell_wmi_descriptor_id_table [ ] = {
{ . guid_string = DELL_WMI_DESCRIPTOR_GUID } ,
{ } ,
} ;
static struct wmi_driver dell_wmi_descriptor_driver = {
. driver = {
. name = " dell-wmi-descriptor " ,
} ,
. probe = dell_wmi_descriptor_probe ,
. remove = dell_wmi_descriptor_remove ,
. id_table = dell_wmi_descriptor_id_table ,
} ;
module_wmi_driver ( dell_wmi_descriptor_driver ) ;
2019-02-19 22:59:52 +03:00
MODULE_DEVICE_TABLE ( wmi , dell_wmi_descriptor_id_table ) ;
2017-11-01 22:25:27 +03:00
MODULE_AUTHOR ( " Mario Limonciello <mario.limonciello@dell.com> " ) ;
MODULE_DESCRIPTION ( " Dell WMI descriptor driver " ) ;
MODULE_LICENSE ( " GPL " ) ;