1*4f5c791aSLennart Poettering /* 2*4f5c791aSLennart Poettering * Export SMBIOS/DMI info via sysfs to userspace 3*4f5c791aSLennart Poettering * 4*4f5c791aSLennart Poettering * Copyright 2007, Lennart Poettering 5*4f5c791aSLennart Poettering * 6*4f5c791aSLennart Poettering * Licensed under GPLv2 7*4f5c791aSLennart Poettering */ 8*4f5c791aSLennart Poettering 9*4f5c791aSLennart Poettering #include <linux/module.h> 10*4f5c791aSLennart Poettering #include <linux/kernel.h> 11*4f5c791aSLennart Poettering #include <linux/init.h> 12*4f5c791aSLennart Poettering #include <linux/dmi.h> 13*4f5c791aSLennart Poettering #include <linux/device.h> 14*4f5c791aSLennart Poettering #include <linux/autoconf.h> 15*4f5c791aSLennart Poettering 16*4f5c791aSLennart Poettering #define DEFINE_DMI_ATTR(_name, _mode, _show) \ 17*4f5c791aSLennart Poettering static struct device_attribute sys_dmi_##_name##_attr = \ 18*4f5c791aSLennart Poettering __ATTR(_name, _mode, _show, NULL); 19*4f5c791aSLennart Poettering 20*4f5c791aSLennart Poettering #define DEFINE_DMI_ATTR_WITH_SHOW(_name, _mode, _field) \ 21*4f5c791aSLennart Poettering static ssize_t sys_dmi_##_name##_show(struct device *dev, \ 22*4f5c791aSLennart Poettering struct device_attribute *attr, \ 23*4f5c791aSLennart Poettering char *page) \ 24*4f5c791aSLennart Poettering { \ 25*4f5c791aSLennart Poettering ssize_t len; \ 26*4f5c791aSLennart Poettering len = scnprintf(page, PAGE_SIZE, "%s\n", dmi_get_system_info(_field)); \ 27*4f5c791aSLennart Poettering page[len-1] = '\n'; \ 28*4f5c791aSLennart Poettering return len; \ 29*4f5c791aSLennart Poettering } \ 30*4f5c791aSLennart Poettering DEFINE_DMI_ATTR(_name, _mode, sys_dmi_##_name##_show); 31*4f5c791aSLennart Poettering 32*4f5c791aSLennart Poettering DEFINE_DMI_ATTR_WITH_SHOW(bios_vendor, 0444, DMI_BIOS_VENDOR); 33*4f5c791aSLennart Poettering DEFINE_DMI_ATTR_WITH_SHOW(bios_version, 0444, DMI_BIOS_VERSION); 34*4f5c791aSLennart Poettering DEFINE_DMI_ATTR_WITH_SHOW(bios_date, 0444, DMI_BIOS_DATE); 35*4f5c791aSLennart Poettering DEFINE_DMI_ATTR_WITH_SHOW(sys_vendor, 0444, DMI_SYS_VENDOR); 36*4f5c791aSLennart Poettering DEFINE_DMI_ATTR_WITH_SHOW(product_name, 0444, DMI_PRODUCT_NAME); 37*4f5c791aSLennart Poettering DEFINE_DMI_ATTR_WITH_SHOW(product_version, 0444, DMI_PRODUCT_VERSION); 38*4f5c791aSLennart Poettering DEFINE_DMI_ATTR_WITH_SHOW(product_serial, 0400, DMI_PRODUCT_SERIAL); 39*4f5c791aSLennart Poettering DEFINE_DMI_ATTR_WITH_SHOW(product_uuid, 0400, DMI_PRODUCT_UUID); 40*4f5c791aSLennart Poettering DEFINE_DMI_ATTR_WITH_SHOW(board_vendor, 0444, DMI_BOARD_VENDOR); 41*4f5c791aSLennart Poettering DEFINE_DMI_ATTR_WITH_SHOW(board_name, 0444, DMI_BOARD_NAME); 42*4f5c791aSLennart Poettering DEFINE_DMI_ATTR_WITH_SHOW(board_version, 0444, DMI_BOARD_VERSION); 43*4f5c791aSLennart Poettering DEFINE_DMI_ATTR_WITH_SHOW(board_serial, 0400, DMI_BOARD_SERIAL); 44*4f5c791aSLennart Poettering DEFINE_DMI_ATTR_WITH_SHOW(board_asset_tag, 0444, DMI_BOARD_ASSET_TAG); 45*4f5c791aSLennart Poettering DEFINE_DMI_ATTR_WITH_SHOW(chassis_vendor, 0444, DMI_CHASSIS_VENDOR); 46*4f5c791aSLennart Poettering DEFINE_DMI_ATTR_WITH_SHOW(chassis_type, 0444, DMI_CHASSIS_TYPE); 47*4f5c791aSLennart Poettering DEFINE_DMI_ATTR_WITH_SHOW(chassis_version, 0444, DMI_CHASSIS_VERSION); 48*4f5c791aSLennart Poettering DEFINE_DMI_ATTR_WITH_SHOW(chassis_serial, 0400, DMI_CHASSIS_SERIAL); 49*4f5c791aSLennart Poettering DEFINE_DMI_ATTR_WITH_SHOW(chassis_asset_tag, 0444, DMI_CHASSIS_ASSET_TAG); 50*4f5c791aSLennart Poettering 51*4f5c791aSLennart Poettering static void ascii_filter(char *d, const char *s) 52*4f5c791aSLennart Poettering { 53*4f5c791aSLennart Poettering /* Filter out characters we don't want to see in the modalias string */ 54*4f5c791aSLennart Poettering for (; *s; s++) 55*4f5c791aSLennart Poettering if (*s > ' ' && *s < 127 && *s != ':') 56*4f5c791aSLennart Poettering *(d++) = *s; 57*4f5c791aSLennart Poettering 58*4f5c791aSLennart Poettering *d = 0; 59*4f5c791aSLennart Poettering } 60*4f5c791aSLennart Poettering 61*4f5c791aSLennart Poettering static ssize_t get_modalias(char *buffer, size_t buffer_size) 62*4f5c791aSLennart Poettering { 63*4f5c791aSLennart Poettering static const struct mafield { 64*4f5c791aSLennart Poettering const char *prefix; 65*4f5c791aSLennart Poettering int field; 66*4f5c791aSLennart Poettering } fields[] = { 67*4f5c791aSLennart Poettering { "bvn", DMI_BIOS_VENDOR }, 68*4f5c791aSLennart Poettering { "bvr", DMI_BIOS_VERSION }, 69*4f5c791aSLennart Poettering { "bd", DMI_BIOS_DATE }, 70*4f5c791aSLennart Poettering { "svn", DMI_SYS_VENDOR }, 71*4f5c791aSLennart Poettering { "pn", DMI_PRODUCT_NAME }, 72*4f5c791aSLennart Poettering { "pvr", DMI_PRODUCT_VERSION }, 73*4f5c791aSLennart Poettering { "rvn", DMI_BOARD_VENDOR }, 74*4f5c791aSLennart Poettering { "rn", DMI_BOARD_NAME }, 75*4f5c791aSLennart Poettering { "rvr", DMI_BOARD_VERSION }, 76*4f5c791aSLennart Poettering { "cvn", DMI_CHASSIS_VENDOR }, 77*4f5c791aSLennart Poettering { "ct", DMI_CHASSIS_TYPE }, 78*4f5c791aSLennart Poettering { "cvr", DMI_CHASSIS_VERSION }, 79*4f5c791aSLennart Poettering { NULL, DMI_NONE } 80*4f5c791aSLennart Poettering }; 81*4f5c791aSLennart Poettering 82*4f5c791aSLennart Poettering ssize_t l, left; 83*4f5c791aSLennart Poettering char *p; 84*4f5c791aSLennart Poettering const struct mafield *f; 85*4f5c791aSLennart Poettering 86*4f5c791aSLennart Poettering strcpy(buffer, "dmi"); 87*4f5c791aSLennart Poettering p = buffer + 3; left = buffer_size - 4; 88*4f5c791aSLennart Poettering 89*4f5c791aSLennart Poettering for (f = fields; f->prefix && left > 0; f++) { 90*4f5c791aSLennart Poettering const char *c; 91*4f5c791aSLennart Poettering char *t; 92*4f5c791aSLennart Poettering 93*4f5c791aSLennart Poettering c = dmi_get_system_info(f->field); 94*4f5c791aSLennart Poettering if (!c) 95*4f5c791aSLennart Poettering continue; 96*4f5c791aSLennart Poettering 97*4f5c791aSLennart Poettering t = kmalloc(strlen(c) + 1, GFP_KERNEL); 98*4f5c791aSLennart Poettering if (!t) 99*4f5c791aSLennart Poettering break; 100*4f5c791aSLennart Poettering ascii_filter(t, c); 101*4f5c791aSLennart Poettering l = scnprintf(p, left, ":%s%s", f->prefix, t); 102*4f5c791aSLennart Poettering kfree(t); 103*4f5c791aSLennart Poettering 104*4f5c791aSLennart Poettering p += l; 105*4f5c791aSLennart Poettering left -= l; 106*4f5c791aSLennart Poettering } 107*4f5c791aSLennart Poettering 108*4f5c791aSLennart Poettering p[0] = ':'; 109*4f5c791aSLennart Poettering p[1] = 0; 110*4f5c791aSLennart Poettering 111*4f5c791aSLennart Poettering return p - buffer + 1; 112*4f5c791aSLennart Poettering } 113*4f5c791aSLennart Poettering 114*4f5c791aSLennart Poettering static ssize_t sys_dmi_modalias_show(struct device *dev, 115*4f5c791aSLennart Poettering struct device_attribute *attr, char *page) 116*4f5c791aSLennart Poettering { 117*4f5c791aSLennart Poettering ssize_t r; 118*4f5c791aSLennart Poettering r = get_modalias(page, PAGE_SIZE-1); 119*4f5c791aSLennart Poettering page[r] = '\n'; 120*4f5c791aSLennart Poettering page[r+1] = 0; 121*4f5c791aSLennart Poettering return r+1; 122*4f5c791aSLennart Poettering } 123*4f5c791aSLennart Poettering 124*4f5c791aSLennart Poettering DEFINE_DMI_ATTR(modalias, 0444, sys_dmi_modalias_show); 125*4f5c791aSLennart Poettering 126*4f5c791aSLennart Poettering static struct attribute *sys_dmi_attributes[DMI_STRING_MAX+2]; 127*4f5c791aSLennart Poettering 128*4f5c791aSLennart Poettering static struct attribute_group sys_dmi_attribute_group = { 129*4f5c791aSLennart Poettering .attrs = sys_dmi_attributes, 130*4f5c791aSLennart Poettering }; 131*4f5c791aSLennart Poettering 132*4f5c791aSLennart Poettering static struct attribute_group* sys_dmi_attribute_groups[] = { 133*4f5c791aSLennart Poettering &sys_dmi_attribute_group, 134*4f5c791aSLennart Poettering NULL 135*4f5c791aSLennart Poettering }; 136*4f5c791aSLennart Poettering 137*4f5c791aSLennart Poettering static int dmi_dev_uevent(struct device *dev, char **envp, 138*4f5c791aSLennart Poettering int num_envp, char *buffer, int buffer_size) 139*4f5c791aSLennart Poettering { 140*4f5c791aSLennart Poettering strcpy(buffer, "MODALIAS="); 141*4f5c791aSLennart Poettering get_modalias(buffer+9, buffer_size-9); 142*4f5c791aSLennart Poettering envp[0] = buffer; 143*4f5c791aSLennart Poettering envp[1] = NULL; 144*4f5c791aSLennart Poettering 145*4f5c791aSLennart Poettering return 0; 146*4f5c791aSLennart Poettering } 147*4f5c791aSLennart Poettering 148*4f5c791aSLennart Poettering static struct class dmi_class = { 149*4f5c791aSLennart Poettering .name = "dmi", 150*4f5c791aSLennart Poettering .dev_release = (void(*)(struct device *)) kfree, 151*4f5c791aSLennart Poettering .dev_uevent = dmi_dev_uevent, 152*4f5c791aSLennart Poettering }; 153*4f5c791aSLennart Poettering 154*4f5c791aSLennart Poettering static struct device *dmi_dev; 155*4f5c791aSLennart Poettering 156*4f5c791aSLennart Poettering /* Initialization */ 157*4f5c791aSLennart Poettering 158*4f5c791aSLennart Poettering #define ADD_DMI_ATTR(_name, _field) \ 159*4f5c791aSLennart Poettering if (dmi_get_system_info(_field)) \ 160*4f5c791aSLennart Poettering sys_dmi_attributes[i++] = & sys_dmi_##_name##_attr.attr; 161*4f5c791aSLennart Poettering 162*4f5c791aSLennart Poettering extern int dmi_available; 163*4f5c791aSLennart Poettering 164*4f5c791aSLennart Poettering static int __init dmi_id_init(void) 165*4f5c791aSLennart Poettering { 166*4f5c791aSLennart Poettering int ret, i; 167*4f5c791aSLennart Poettering 168*4f5c791aSLennart Poettering if (!dmi_available) 169*4f5c791aSLennart Poettering return -ENODEV; 170*4f5c791aSLennart Poettering 171*4f5c791aSLennart Poettering /* Not necessarily all DMI fields are available on all 172*4f5c791aSLennart Poettering * systems, hence let's built an attribute table of just 173*4f5c791aSLennart Poettering * what's available */ 174*4f5c791aSLennart Poettering i = 0; 175*4f5c791aSLennart Poettering ADD_DMI_ATTR(bios_vendor, DMI_BIOS_VENDOR); 176*4f5c791aSLennart Poettering ADD_DMI_ATTR(bios_version, DMI_BIOS_VERSION); 177*4f5c791aSLennart Poettering ADD_DMI_ATTR(bios_date, DMI_BIOS_DATE); 178*4f5c791aSLennart Poettering ADD_DMI_ATTR(sys_vendor, DMI_SYS_VENDOR); 179*4f5c791aSLennart Poettering ADD_DMI_ATTR(product_name, DMI_PRODUCT_NAME); 180*4f5c791aSLennart Poettering ADD_DMI_ATTR(product_version, DMI_PRODUCT_VERSION); 181*4f5c791aSLennart Poettering ADD_DMI_ATTR(product_serial, DMI_PRODUCT_SERIAL); 182*4f5c791aSLennart Poettering ADD_DMI_ATTR(product_uuid, DMI_PRODUCT_UUID); 183*4f5c791aSLennart Poettering ADD_DMI_ATTR(board_vendor, DMI_BOARD_VENDOR); 184*4f5c791aSLennart Poettering ADD_DMI_ATTR(board_name, DMI_BOARD_NAME); 185*4f5c791aSLennart Poettering ADD_DMI_ATTR(board_version, DMI_BOARD_VERSION); 186*4f5c791aSLennart Poettering ADD_DMI_ATTR(board_serial, DMI_BOARD_SERIAL); 187*4f5c791aSLennart Poettering ADD_DMI_ATTR(board_asset_tag, DMI_BOARD_ASSET_TAG); 188*4f5c791aSLennart Poettering ADD_DMI_ATTR(chassis_vendor, DMI_CHASSIS_VENDOR); 189*4f5c791aSLennart Poettering ADD_DMI_ATTR(chassis_type, DMI_CHASSIS_TYPE); 190*4f5c791aSLennart Poettering ADD_DMI_ATTR(chassis_version, DMI_CHASSIS_VERSION); 191*4f5c791aSLennart Poettering ADD_DMI_ATTR(chassis_serial, DMI_CHASSIS_SERIAL); 192*4f5c791aSLennart Poettering ADD_DMI_ATTR(chassis_asset_tag, DMI_CHASSIS_ASSET_TAG); 193*4f5c791aSLennart Poettering sys_dmi_attributes[i++] = &sys_dmi_modalias_attr.attr; 194*4f5c791aSLennart Poettering 195*4f5c791aSLennart Poettering ret = class_register(&dmi_class); 196*4f5c791aSLennart Poettering if (ret) 197*4f5c791aSLennart Poettering return ret; 198*4f5c791aSLennart Poettering 199*4f5c791aSLennart Poettering dmi_dev = kzalloc(sizeof(*dmi_dev), GFP_KERNEL); 200*4f5c791aSLennart Poettering if (!dmi_dev) { 201*4f5c791aSLennart Poettering ret = -ENOMEM; 202*4f5c791aSLennart Poettering goto fail_class_unregister; 203*4f5c791aSLennart Poettering } 204*4f5c791aSLennart Poettering 205*4f5c791aSLennart Poettering dmi_dev->class = &dmi_class; 206*4f5c791aSLennart Poettering strcpy(dmi_dev->bus_id, "id"); 207*4f5c791aSLennart Poettering dmi_dev->groups = sys_dmi_attribute_groups; 208*4f5c791aSLennart Poettering 209*4f5c791aSLennart Poettering ret = device_register(dmi_dev); 210*4f5c791aSLennart Poettering if (ret) 211*4f5c791aSLennart Poettering goto fail_class_unregister; 212*4f5c791aSLennart Poettering 213*4f5c791aSLennart Poettering return 0; 214*4f5c791aSLennart Poettering 215*4f5c791aSLennart Poettering fail_class_unregister: 216*4f5c791aSLennart Poettering 217*4f5c791aSLennart Poettering class_unregister(&dmi_class); 218*4f5c791aSLennart Poettering 219*4f5c791aSLennart Poettering return ret; 220*4f5c791aSLennart Poettering } 221*4f5c791aSLennart Poettering 222*4f5c791aSLennart Poettering arch_initcall(dmi_id_init); 223