2010-07-26 14:56:50 +04:00
/*
* Purpose : Export the firmware instance and label associated with
* a pci device to sysfs
* Copyright ( C ) 2010 Dell Inc .
* by Narendra K < Narendra_K @ dell . com > ,
* Jordan Hargrave < Jordan_Hargrave @ dell . com >
*
2011-03-02 20:04:17 +03:00
* PCI Firmware Specification Revision 3.1 section 4.6 .7 ( DSM for Naming a
* PCI or PCI Express Device Under Operating Systems ) defines an instance
* number and string name . This code retrieves them and exports them to sysfs .
* If the system firmware does not provide the ACPI _DSM ( Device Specific
* Method ) , then the SMBIOS type 41 instance number and string is exported to
* sysfs .
*
2010-07-26 14:56:50 +04:00
* SMBIOS defines type 41 for onboard pci devices . This code retrieves
* the instance number and string from the type 41 record and exports
* it to sysfs .
*
2016-02-10 01:10:16 +03:00
* Please see http : //linux.dell.com/files/biosdevname/ for more
2010-07-26 14:56:50 +04:00
* information .
*/
# include <linux/dmi.h>
# include <linux/sysfs.h>
# include <linux/pci.h>
# include <linux/pci_ids.h>
# include <linux/module.h>
# include <linux/device.h>
2011-03-02 20:04:17 +03:00
# include <linux/nls.h>
# include <linux/acpi.h>
# include <linux/pci-acpi.h>
2010-07-26 14:56:50 +04:00
# include "pci.h"
2014-01-14 04:01:11 +04:00
# ifdef CONFIG_DMI
2010-07-26 14:56:50 +04:00
enum smbios_attr_enum {
SMBIOS_ATTR_NONE = 0 ,
SMBIOS_ATTR_LABEL_SHOW ,
SMBIOS_ATTR_INSTANCE_SHOW ,
} ;
2014-04-19 04:13:49 +04:00
static size_t find_smbios_instance_string ( struct pci_dev * pdev , char * buf ,
enum smbios_attr_enum attribute )
2010-07-26 14:56:50 +04:00
{
const struct dmi_device * dmi ;
struct dmi_dev_onboard * donboard ;
int bus ;
int devfn ;
bus = pdev - > bus - > number ;
devfn = pdev - > devfn ;
dmi = NULL ;
while ( ( dmi = dmi_find_device ( DMI_DEV_TYPE_DEV_ONBOARD ,
NULL , dmi ) ) ! = NULL ) {
donboard = dmi - > device_data ;
if ( donboard & & donboard - > bus = = bus & &
donboard - > devfn = = devfn ) {
if ( buf ) {
if ( attribute = = SMBIOS_ATTR_INSTANCE_SHOW )
return scnprintf ( buf , PAGE_SIZE ,
" %d \n " ,
donboard - > instance ) ;
else if ( attribute = = SMBIOS_ATTR_LABEL_SHOW )
return scnprintf ( buf , PAGE_SIZE ,
" %s \n " ,
dmi - > name ) ;
}
return strlen ( dmi - > name ) ;
}
}
return 0 ;
}
2014-04-19 04:13:49 +04:00
static umode_t smbios_instance_string_exist ( struct kobject * kobj ,
struct attribute * attr , int n )
2010-07-26 14:56:50 +04:00
{
struct device * dev ;
struct pci_dev * pdev ;
2015-12-23 15:28:13 +03:00
dev = kobj_to_dev ( kobj ) ;
2010-07-26 14:56:50 +04:00
pdev = to_pci_dev ( dev ) ;
return find_smbios_instance_string ( pdev , NULL , SMBIOS_ATTR_NONE ) ?
S_IRUGO : 0 ;
}
2014-04-19 04:13:49 +04:00
static ssize_t smbioslabel_show ( struct device * dev ,
struct device_attribute * attr , char * buf )
2010-07-26 14:56:50 +04:00
{
struct pci_dev * pdev ;
pdev = to_pci_dev ( dev ) ;
return find_smbios_instance_string ( pdev , buf ,
SMBIOS_ATTR_LABEL_SHOW ) ;
}
2014-04-19 04:13:49 +04:00
static ssize_t smbiosinstance_show ( struct device * dev ,
struct device_attribute * attr , char * buf )
2010-07-26 14:56:50 +04:00
{
struct pci_dev * pdev ;
pdev = to_pci_dev ( dev ) ;
return find_smbios_instance_string ( pdev , buf ,
SMBIOS_ATTR_INSTANCE_SHOW ) ;
}
static struct device_attribute smbios_attr_label = {
2010-08-04 08:25:31 +04:00
. attr = { . name = " label " , . mode = 0444 } ,
2010-07-26 14:56:50 +04:00
. show = smbioslabel_show ,
} ;
static struct device_attribute smbios_attr_instance = {
2010-08-04 08:25:31 +04:00
. attr = { . name = " index " , . mode = 0444 } ,
2010-07-26 14:56:50 +04:00
. show = smbiosinstance_show ,
} ;
static struct attribute * smbios_attributes [ ] = {
& smbios_attr_label . attr ,
& smbios_attr_instance . attr ,
NULL ,
} ;
static struct attribute_group smbios_attr_group = {
. attrs = smbios_attributes ,
. is_visible = smbios_instance_string_exist ,
} ;
2014-04-19 04:13:49 +04:00
static int pci_create_smbiosname_file ( struct pci_dev * pdev )
2010-07-26 14:56:50 +04:00
{
2011-03-02 20:04:17 +03:00
return sysfs_create_group ( & pdev - > dev . kobj , & smbios_attr_group ) ;
2010-07-26 14:56:50 +04:00
}
2014-04-19 04:13:49 +04:00
static void pci_remove_smbiosname_file ( struct pci_dev * pdev )
2010-07-26 14:56:50 +04:00
{
sysfs_remove_group ( & pdev - > dev . kobj , & smbios_attr_group ) ;
}
2014-01-14 04:01:11 +04:00
# else
2014-04-19 04:13:49 +04:00
static inline int pci_create_smbiosname_file ( struct pci_dev * pdev )
2011-03-02 20:04:17 +03:00
{
return - 1 ;
}
2014-04-19 04:13:49 +04:00
static inline void pci_remove_smbiosname_file ( struct pci_dev * pdev )
2011-03-07 23:55:56 +03:00
{
}
2014-01-14 04:01:11 +04:00
# endif
2011-03-07 23:55:56 +03:00
2014-01-14 04:01:11 +04:00
# ifdef CONFIG_ACPI
2011-03-02 20:04:17 +03:00
enum acpi_attr_enum {
ACPI_ATTR_LABEL_SHOW ,
ACPI_ATTR_INDEX_SHOW ,
} ;
static void dsm_label_utf16s_to_utf8s ( union acpi_object * obj , char * buf )
{
int len ;
2014-06-18 18:55:30 +04:00
len = utf16s_to_utf8s ( ( const wchar_t * ) obj - > buffer . pointer ,
obj - > buffer . length ,
2011-03-02 20:04:17 +03:00
UTF16_LITTLE_ENDIAN ,
buf , PAGE_SIZE ) ;
buf [ len ] = ' \n ' ;
}
2014-04-19 04:13:49 +04:00
static int dsm_get_label ( struct device * dev , char * buf ,
enum acpi_attr_enum attr )
2011-03-02 20:04:17 +03:00
{
2013-12-19 16:38:13 +04:00
acpi_handle handle ;
union acpi_object * obj , * tmp ;
int len = - 1 ;
handle = ACPI_HANDLE ( dev ) ;
if ( ! handle )
2011-03-02 20:04:17 +03:00
return - 1 ;
2015-03-25 09:31:41 +03:00
obj = acpi_evaluate_dsm ( handle , pci_acpi_dsm_uuid , 0x2 ,
2013-12-19 16:38:13 +04:00
DEVICE_LABEL_DSM , NULL ) ;
if ( ! obj )
return - 1 ;
tmp = obj - > package . elements ;
if ( obj - > type = = ACPI_TYPE_PACKAGE & & obj - > package . count = = 2 & &
tmp [ 0 ] . type = = ACPI_TYPE_INTEGER & &
2014-06-18 18:55:30 +04:00
( tmp [ 1 ] . type = = ACPI_TYPE_STRING | |
tmp [ 1 ] . type = = ACPI_TYPE_BUFFER ) ) {
2013-12-19 16:38:14 +04:00
/*
* The second string element is optional even when
* this _DSM is implemented ; when not implemented ,
* this entry must return a null string .
*/
2014-06-18 18:55:30 +04:00
if ( attr = = ACPI_ATTR_INDEX_SHOW ) {
2013-12-19 16:38:14 +04:00
scnprintf ( buf , PAGE_SIZE , " %llu \n " , tmp - > integer . value ) ;
2014-06-18 18:55:30 +04:00
} else if ( attr = = ACPI_ATTR_LABEL_SHOW ) {
if ( tmp [ 1 ] . type = = ACPI_TYPE_STRING )
scnprintf ( buf , PAGE_SIZE , " %s \n " ,
tmp [ 1 ] . string . pointer ) ;
else if ( tmp [ 1 ] . type = = ACPI_TYPE_BUFFER )
dsm_label_utf16s_to_utf8s ( tmp + 1 , buf ) ;
}
2013-12-19 16:38:14 +04:00
len = strlen ( buf ) > 0 ? strlen ( buf ) : - 1 ;
2011-03-02 20:04:17 +03:00
}
2013-12-19 16:38:12 +04:00
2013-12-19 16:38:13 +04:00
ACPI_FREE ( obj ) ;
2013-12-19 16:38:12 +04:00
2013-12-19 16:38:13 +04:00
return len ;
2011-03-02 20:04:17 +03:00
}
2014-04-19 04:13:49 +04:00
static bool device_has_dsm ( struct device * dev )
2011-03-02 20:04:17 +03:00
{
acpi_handle handle ;
2013-11-15 02:17:21 +04:00
handle = ACPI_HANDLE ( dev ) ;
2011-03-02 20:04:17 +03:00
if ( ! handle )
2013-12-19 16:38:14 +04:00
return false ;
2011-03-02 20:04:17 +03:00
2015-03-25 09:31:41 +03:00
return ! ! acpi_check_dsm ( handle , pci_acpi_dsm_uuid , 0x2 ,
2013-12-19 16:38:14 +04:00
1 < < DEVICE_LABEL_DSM ) ;
2011-03-02 20:04:17 +03:00
}
2014-04-19 04:13:49 +04:00
static umode_t acpi_index_string_exist ( struct kobject * kobj ,
struct attribute * attr , int n )
2011-03-02 20:04:17 +03:00
{
struct device * dev ;
2015-12-23 15:28:13 +03:00
dev = kobj_to_dev ( kobj ) ;
2011-03-02 20:04:17 +03:00
if ( device_has_dsm ( dev ) )
return S_IRUGO ;
return 0 ;
}
2014-04-19 04:13:49 +04:00
static ssize_t acpilabel_show ( struct device * dev ,
struct device_attribute * attr , char * buf )
2011-03-02 20:04:17 +03:00
{
2013-12-19 16:38:13 +04:00
return dsm_get_label ( dev , buf , ACPI_ATTR_LABEL_SHOW ) ;
2011-03-02 20:04:17 +03:00
}
2014-04-19 04:13:49 +04:00
static ssize_t acpiindex_show ( struct device * dev ,
struct device_attribute * attr , char * buf )
2011-03-02 20:04:17 +03:00
{
2013-12-19 16:38:13 +04:00
return dsm_get_label ( dev , buf , ACPI_ATTR_INDEX_SHOW ) ;
2011-03-02 20:04:17 +03:00
}
static struct device_attribute acpi_attr_label = {
. attr = { . name = " label " , . mode = 0444 } ,
. show = acpilabel_show ,
} ;
static struct device_attribute acpi_attr_index = {
. attr = { . name = " acpi_index " , . mode = 0444 } ,
. show = acpiindex_show ,
} ;
static struct attribute * acpi_attributes [ ] = {
& acpi_attr_label . attr ,
& acpi_attr_index . attr ,
NULL ,
} ;
static struct attribute_group acpi_attr_group = {
. attrs = acpi_attributes ,
. is_visible = acpi_index_string_exist ,
} ;
2014-04-19 04:13:49 +04:00
static int pci_create_acpi_index_label_files ( struct pci_dev * pdev )
2011-03-02 20:04:17 +03:00
{
return sysfs_create_group ( & pdev - > dev . kobj , & acpi_attr_group ) ;
}
2014-04-19 04:13:49 +04:00
static int pci_remove_acpi_index_label_files ( struct pci_dev * pdev )
2011-03-02 20:04:17 +03:00
{
sysfs_remove_group ( & pdev - > dev . kobj , & acpi_attr_group ) ;
return 0 ;
}
2014-01-14 04:01:11 +04:00
# else
2014-04-19 04:13:49 +04:00
static inline int pci_create_acpi_index_label_files ( struct pci_dev * pdev )
2014-01-14 04:01:11 +04:00
{
return - 1 ;
}
2014-04-19 04:13:49 +04:00
static inline int pci_remove_acpi_index_label_files ( struct pci_dev * pdev )
2014-01-14 04:01:11 +04:00
{
return - 1 ;
}
2014-04-19 04:13:49 +04:00
static inline bool device_has_dsm ( struct device * dev )
2014-01-14 04:01:11 +04:00
{
return false ;
}
2011-03-02 20:04:17 +03:00
# endif
2010-07-26 14:56:50 +04:00
void pci_create_firmware_label_files ( struct pci_dev * pdev )
{
2011-03-02 20:04:17 +03:00
if ( device_has_dsm ( & pdev - > dev ) )
pci_create_acpi_index_label_files ( pdev ) ;
else
pci_create_smbiosname_file ( pdev ) ;
2010-07-26 14:56:50 +04:00
}
void pci_remove_firmware_label_files ( struct pci_dev * pdev )
{
2011-03-02 20:04:17 +03:00
if ( device_has_dsm ( & pdev - > dev ) )
pci_remove_acpi_index_label_files ( pdev ) ;
else
pci_remove_smbiosname_file ( pdev ) ;
2010-07-26 14:56:50 +04:00
}