dmi-id.c 6.4 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14
/*
 * Export SMBIOS/DMI info via sysfs to userspace
 *
 * Copyright 2007, Lennart Poettering
 *
 * Licensed under GPLv2
 */

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/dmi.h>
#include <linux/device.h>

15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
struct dmi_device_attribute{
	struct device_attribute dev_attr;
	int field;
};
#define to_dmi_dev_attr(_dev_attr) \
	container_of(_dev_attr, struct dmi_device_attribute, dev_attr)

static ssize_t sys_dmi_field_show(struct device *dev,
				  struct device_attribute *attr,
				  char *page)
{
	int field = to_dmi_dev_attr(attr)->field;
	ssize_t len;
	len = scnprintf(page, PAGE_SIZE, "%s\n", dmi_get_system_info(field));
	page[len-1] = '\n';
	return len;
}

#define DMI_ATTR(_name, _mode, _show, _field)			\
	{ .dev_attr = __ATTR(_name, _mode, _show, NULL),	\
	  .field = _field }

#define DEFINE_DMI_ATTR_WITH_SHOW(_name, _mode, _field)		\
static struct dmi_device_attribute sys_dmi_##_name##_attr =	\
	DMI_ATTR(_name, _mode, sys_dmi_field_show, _field);
40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132

DEFINE_DMI_ATTR_WITH_SHOW(bios_vendor,		0444, DMI_BIOS_VENDOR);
DEFINE_DMI_ATTR_WITH_SHOW(bios_version,		0444, DMI_BIOS_VERSION);
DEFINE_DMI_ATTR_WITH_SHOW(bios_date,		0444, DMI_BIOS_DATE);
DEFINE_DMI_ATTR_WITH_SHOW(sys_vendor,		0444, DMI_SYS_VENDOR);
DEFINE_DMI_ATTR_WITH_SHOW(product_name,		0444, DMI_PRODUCT_NAME);
DEFINE_DMI_ATTR_WITH_SHOW(product_version,	0444, DMI_PRODUCT_VERSION);
DEFINE_DMI_ATTR_WITH_SHOW(product_serial,	0400, DMI_PRODUCT_SERIAL);
DEFINE_DMI_ATTR_WITH_SHOW(product_uuid,		0400, DMI_PRODUCT_UUID);
DEFINE_DMI_ATTR_WITH_SHOW(board_vendor,		0444, DMI_BOARD_VENDOR);
DEFINE_DMI_ATTR_WITH_SHOW(board_name,		0444, DMI_BOARD_NAME);
DEFINE_DMI_ATTR_WITH_SHOW(board_version,	0444, DMI_BOARD_VERSION);
DEFINE_DMI_ATTR_WITH_SHOW(board_serial,		0400, DMI_BOARD_SERIAL);
DEFINE_DMI_ATTR_WITH_SHOW(board_asset_tag,	0444, DMI_BOARD_ASSET_TAG);
DEFINE_DMI_ATTR_WITH_SHOW(chassis_vendor,	0444, DMI_CHASSIS_VENDOR);
DEFINE_DMI_ATTR_WITH_SHOW(chassis_type,		0444, DMI_CHASSIS_TYPE);
DEFINE_DMI_ATTR_WITH_SHOW(chassis_version,	0444, DMI_CHASSIS_VERSION);
DEFINE_DMI_ATTR_WITH_SHOW(chassis_serial,	0400, DMI_CHASSIS_SERIAL);
DEFINE_DMI_ATTR_WITH_SHOW(chassis_asset_tag,	0444, DMI_CHASSIS_ASSET_TAG);

static void ascii_filter(char *d, const char *s)
{
	/* Filter out characters we don't want to see in the modalias string */
	for (; *s; s++)
		if (*s > ' ' && *s < 127 && *s != ':')
			*(d++) = *s;

	*d = 0;
}

static ssize_t get_modalias(char *buffer, size_t buffer_size)
{
	static const struct mafield {
		const char *prefix;
		int field;
	} fields[] = {
		{ "bvn", DMI_BIOS_VENDOR },
		{ "bvr", DMI_BIOS_VERSION },
		{ "bd",  DMI_BIOS_DATE },
		{ "svn", DMI_SYS_VENDOR },
		{ "pn",  DMI_PRODUCT_NAME },
		{ "pvr", DMI_PRODUCT_VERSION },
		{ "rvn", DMI_BOARD_VENDOR },
		{ "rn",  DMI_BOARD_NAME },
		{ "rvr", DMI_BOARD_VERSION },
		{ "cvn", DMI_CHASSIS_VENDOR },
		{ "ct",  DMI_CHASSIS_TYPE },
		{ "cvr", DMI_CHASSIS_VERSION },
		{ NULL,  DMI_NONE }
	};

	ssize_t l, left;
	char *p;
	const struct mafield *f;

	strcpy(buffer, "dmi");
	p = buffer + 3; left = buffer_size - 4;

	for (f = fields; f->prefix && left > 0; f++) {
		const char *c;
		char *t;

		c = dmi_get_system_info(f->field);
		if (!c)
			continue;

		t = kmalloc(strlen(c) + 1, GFP_KERNEL);
		if (!t)
			break;
		ascii_filter(t, c);
		l = scnprintf(p, left, ":%s%s", f->prefix, t);
		kfree(t);

		p += l;
		left -= l;
	}

	p[0] = ':';
	p[1] = 0;

	return p - buffer + 1;
}

static ssize_t sys_dmi_modalias_show(struct device *dev,
				     struct device_attribute *attr, char *page)
{
	ssize_t r;
	r = get_modalias(page, PAGE_SIZE-1);
	page[r] = '\n';
	page[r+1] = 0;
	return r+1;
}

J
Jean Delvare 已提交
133 134
static struct device_attribute sys_dmi_modalias_attr =
	__ATTR(modalias, 0444, sys_dmi_modalias_show, NULL);
135 136 137 138 139 140 141 142 143 144 145 146

static struct attribute *sys_dmi_attributes[DMI_STRING_MAX+2];

static struct attribute_group sys_dmi_attribute_group = {
	.attrs = sys_dmi_attributes,
};

static struct attribute_group* sys_dmi_attribute_groups[] = {
	&sys_dmi_attribute_group,
	NULL
};

147
static int dmi_dev_uevent(struct device *dev, struct kobj_uevent_env *env)
148
{
149 150 151 152 153 154 155 156 157
	ssize_t len;

	if (add_uevent_var(env, "MODALIAS="))
		return -ENOMEM;
	len = get_modalias(&env->buf[env->buflen - 1],
			   sizeof(env->buf) - env->buflen);
	if (len >= (sizeof(env->buf) - env->buflen))
		return -ENOMEM;
	env->buflen += len;
158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
	return 0;
}

static struct class dmi_class = {
	.name = "dmi",
	.dev_release = (void(*)(struct device *)) kfree,
	.dev_uevent = dmi_dev_uevent,
};

static struct device *dmi_dev;

/* Initialization */

#define ADD_DMI_ATTR(_name, _field) \
	if (dmi_get_system_info(_field)) \
173
		sys_dmi_attributes[i++] = &sys_dmi_##_name##_attr.dev_attr.attr;
174

175 176 177
/* In a separate function to keep gcc 3.2 happy - do NOT merge this in
   dmi_id_init! */
static void __init dmi_id_init_attr_table(void)
178
{
179
	int i;
180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203

	/* Not necessarily all DMI fields are available on all
	 * systems, hence let's built an attribute table of just
	 * what's available */
	i = 0;
	ADD_DMI_ATTR(bios_vendor,       DMI_BIOS_VENDOR);
	ADD_DMI_ATTR(bios_version,      DMI_BIOS_VERSION);
	ADD_DMI_ATTR(bios_date,         DMI_BIOS_DATE);
	ADD_DMI_ATTR(sys_vendor,        DMI_SYS_VENDOR);
	ADD_DMI_ATTR(product_name,      DMI_PRODUCT_NAME);
	ADD_DMI_ATTR(product_version,   DMI_PRODUCT_VERSION);
	ADD_DMI_ATTR(product_serial,    DMI_PRODUCT_SERIAL);
	ADD_DMI_ATTR(product_uuid,      DMI_PRODUCT_UUID);
	ADD_DMI_ATTR(board_vendor,      DMI_BOARD_VENDOR);
	ADD_DMI_ATTR(board_name,        DMI_BOARD_NAME);
	ADD_DMI_ATTR(board_version,     DMI_BOARD_VERSION);
	ADD_DMI_ATTR(board_serial,      DMI_BOARD_SERIAL);
	ADD_DMI_ATTR(board_asset_tag,   DMI_BOARD_ASSET_TAG);
	ADD_DMI_ATTR(chassis_vendor,    DMI_CHASSIS_VENDOR);
	ADD_DMI_ATTR(chassis_type,      DMI_CHASSIS_TYPE);
	ADD_DMI_ATTR(chassis_version,   DMI_CHASSIS_VERSION);
	ADD_DMI_ATTR(chassis_serial,    DMI_CHASSIS_SERIAL);
	ADD_DMI_ATTR(chassis_asset_tag, DMI_CHASSIS_ASSET_TAG);
	sys_dmi_attributes[i++] = &sys_dmi_modalias_attr.attr;
204 205 206 207 208 209 210 211 212 213
}

static int __init dmi_id_init(void)
{
	int ret;

	if (!dmi_available)
		return -ENODEV;

	dmi_id_init_attr_table();
214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242

	ret = class_register(&dmi_class);
	if (ret)
		return ret;

	dmi_dev = kzalloc(sizeof(*dmi_dev), GFP_KERNEL);
	if (!dmi_dev) {
		ret = -ENOMEM;
		goto fail_class_unregister;
	}

	dmi_dev->class = &dmi_class;
	strcpy(dmi_dev->bus_id, "id");
	dmi_dev->groups = sys_dmi_attribute_groups;

	ret = device_register(dmi_dev);
	if (ret)
		goto fail_class_unregister;

	return 0;

fail_class_unregister:

	class_unregister(&dmi_class);

	return ret;
}

arch_initcall(dmi_id_init);