0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015 #include <linux/platform_device.h>
0016 #include <linux/acpi.h>
0017 #include <linux/dma-mapping.h>
0018 #include <linux/dmi.h>
0019 #include <linux/errno.h>
0020 #include <linux/cpu.h>
0021 #include <linux/gfp.h>
0022 #include <linux/init.h>
0023 #include <linux/io.h>
0024 #include <linux/kernel.h>
0025 #include <linux/mc146818rtc.h>
0026 #include <linux/module.h>
0027 #include <linux/reboot.h>
0028 #include <linux/sched.h>
0029 #include <linux/smp.h>
0030 #include <linux/spinlock.h>
0031 #include <linux/string.h>
0032 #include <linux/types.h>
0033 #include <linux/mutex.h>
0034
0035 #include "dcdbas.h"
0036
0037 #define DRIVER_NAME "dcdbas"
0038 #define DRIVER_VERSION "5.6.0-3.4"
0039 #define DRIVER_DESCRIPTION "Dell Systems Management Base Driver"
0040
0041 static struct platform_device *dcdbas_pdev;
0042
0043 static unsigned long max_smi_data_buf_size = MAX_SMI_DATA_BUF_SIZE;
0044 static DEFINE_MUTEX(smi_data_lock);
0045 static u8 *bios_buffer;
0046 static struct smi_buffer smi_buf;
0047
0048 static unsigned int host_control_action;
0049 static unsigned int host_control_smi_type;
0050 static unsigned int host_control_on_shutdown;
0051
0052 static bool wsmt_enabled;
0053
0054 int dcdbas_smi_alloc(struct smi_buffer *smi_buffer, unsigned long size)
0055 {
0056 smi_buffer->virt = dma_alloc_coherent(&dcdbas_pdev->dev, size,
0057 &smi_buffer->dma, GFP_KERNEL);
0058 if (!smi_buffer->virt) {
0059 dev_dbg(&dcdbas_pdev->dev,
0060 "%s: failed to allocate memory size %lu\n",
0061 __func__, size);
0062 return -ENOMEM;
0063 }
0064 smi_buffer->size = size;
0065
0066 dev_dbg(&dcdbas_pdev->dev, "%s: phys: %x size: %lu\n",
0067 __func__, (u32)smi_buffer->dma, smi_buffer->size);
0068
0069 return 0;
0070 }
0071 EXPORT_SYMBOL_GPL(dcdbas_smi_alloc);
0072
0073 void dcdbas_smi_free(struct smi_buffer *smi_buffer)
0074 {
0075 if (!smi_buffer->virt)
0076 return;
0077
0078 dev_dbg(&dcdbas_pdev->dev, "%s: phys: %x size: %lu\n",
0079 __func__, (u32)smi_buffer->dma, smi_buffer->size);
0080 dma_free_coherent(&dcdbas_pdev->dev, smi_buffer->size,
0081 smi_buffer->virt, smi_buffer->dma);
0082 smi_buffer->virt = NULL;
0083 smi_buffer->dma = 0;
0084 smi_buffer->size = 0;
0085 }
0086 EXPORT_SYMBOL_GPL(dcdbas_smi_free);
0087
0088
0089
0090
0091 static void smi_data_buf_free(void)
0092 {
0093 if (!smi_buf.virt || wsmt_enabled)
0094 return;
0095
0096 dcdbas_smi_free(&smi_buf);
0097 }
0098
0099
0100
0101
0102 static int smi_data_buf_realloc(unsigned long size)
0103 {
0104 struct smi_buffer tmp;
0105 int ret;
0106
0107 if (smi_buf.size >= size)
0108 return 0;
0109
0110 if (size > max_smi_data_buf_size)
0111 return -EINVAL;
0112
0113
0114 ret = dcdbas_smi_alloc(&tmp, size);
0115 if (ret)
0116 return ret;
0117
0118
0119 if (smi_buf.virt)
0120 memcpy(tmp.virt, smi_buf.virt, smi_buf.size);
0121
0122
0123 smi_data_buf_free();
0124
0125
0126 smi_buf = tmp;
0127
0128 return 0;
0129 }
0130
0131 static ssize_t smi_data_buf_phys_addr_show(struct device *dev,
0132 struct device_attribute *attr,
0133 char *buf)
0134 {
0135 return sprintf(buf, "%x\n", (u32)smi_buf.dma);
0136 }
0137
0138 static ssize_t smi_data_buf_size_show(struct device *dev,
0139 struct device_attribute *attr,
0140 char *buf)
0141 {
0142 return sprintf(buf, "%lu\n", smi_buf.size);
0143 }
0144
0145 static ssize_t smi_data_buf_size_store(struct device *dev,
0146 struct device_attribute *attr,
0147 const char *buf, size_t count)
0148 {
0149 unsigned long buf_size;
0150 ssize_t ret;
0151
0152 buf_size = simple_strtoul(buf, NULL, 10);
0153
0154
0155 mutex_lock(&smi_data_lock);
0156 ret = smi_data_buf_realloc(buf_size);
0157 mutex_unlock(&smi_data_lock);
0158 if (ret)
0159 return ret;
0160
0161 return count;
0162 }
0163
0164 static ssize_t smi_data_read(struct file *filp, struct kobject *kobj,
0165 struct bin_attribute *bin_attr,
0166 char *buf, loff_t pos, size_t count)
0167 {
0168 ssize_t ret;
0169
0170 mutex_lock(&smi_data_lock);
0171 ret = memory_read_from_buffer(buf, count, &pos, smi_buf.virt,
0172 smi_buf.size);
0173 mutex_unlock(&smi_data_lock);
0174 return ret;
0175 }
0176
0177 static ssize_t smi_data_write(struct file *filp, struct kobject *kobj,
0178 struct bin_attribute *bin_attr,
0179 char *buf, loff_t pos, size_t count)
0180 {
0181 ssize_t ret;
0182
0183 if ((pos + count) > max_smi_data_buf_size)
0184 return -EINVAL;
0185
0186 mutex_lock(&smi_data_lock);
0187
0188 ret = smi_data_buf_realloc(pos + count);
0189 if (ret)
0190 goto out;
0191
0192 memcpy(smi_buf.virt + pos, buf, count);
0193 ret = count;
0194 out:
0195 mutex_unlock(&smi_data_lock);
0196 return ret;
0197 }
0198
0199 static ssize_t host_control_action_show(struct device *dev,
0200 struct device_attribute *attr,
0201 char *buf)
0202 {
0203 return sprintf(buf, "%u\n", host_control_action);
0204 }
0205
0206 static ssize_t host_control_action_store(struct device *dev,
0207 struct device_attribute *attr,
0208 const char *buf, size_t count)
0209 {
0210 ssize_t ret;
0211
0212
0213 mutex_lock(&smi_data_lock);
0214 ret = smi_data_buf_realloc(sizeof(struct apm_cmd));
0215 mutex_unlock(&smi_data_lock);
0216 if (ret)
0217 return ret;
0218
0219 host_control_action = simple_strtoul(buf, NULL, 10);
0220 return count;
0221 }
0222
0223 static ssize_t host_control_smi_type_show(struct device *dev,
0224 struct device_attribute *attr,
0225 char *buf)
0226 {
0227 return sprintf(buf, "%u\n", host_control_smi_type);
0228 }
0229
0230 static ssize_t host_control_smi_type_store(struct device *dev,
0231 struct device_attribute *attr,
0232 const char *buf, size_t count)
0233 {
0234 host_control_smi_type = simple_strtoul(buf, NULL, 10);
0235 return count;
0236 }
0237
0238 static ssize_t host_control_on_shutdown_show(struct device *dev,
0239 struct device_attribute *attr,
0240 char *buf)
0241 {
0242 return sprintf(buf, "%u\n", host_control_on_shutdown);
0243 }
0244
0245 static ssize_t host_control_on_shutdown_store(struct device *dev,
0246 struct device_attribute *attr,
0247 const char *buf, size_t count)
0248 {
0249 host_control_on_shutdown = simple_strtoul(buf, NULL, 10);
0250 return count;
0251 }
0252
0253 static int raise_smi(void *par)
0254 {
0255 struct smi_cmd *smi_cmd = par;
0256
0257 if (smp_processor_id() != 0) {
0258 dev_dbg(&dcdbas_pdev->dev, "%s: failed to get CPU 0\n",
0259 __func__);
0260 return -EBUSY;
0261 }
0262
0263
0264
0265 asm volatile (
0266 "outb %b0,%w1\n"
0267 "inb %w1"
0268 :
0269 : "a" (smi_cmd->command_code),
0270 "d" (smi_cmd->command_address),
0271 "b" (smi_cmd->ebx),
0272 "c" (smi_cmd->ecx)
0273 : "memory"
0274 );
0275
0276 return 0;
0277 }
0278
0279
0280
0281
0282
0283 int dcdbas_smi_request(struct smi_cmd *smi_cmd)
0284 {
0285 int ret;
0286
0287 if (smi_cmd->magic != SMI_CMD_MAGIC) {
0288 dev_info(&dcdbas_pdev->dev, "%s: invalid magic value\n",
0289 __func__);
0290 return -EBADR;
0291 }
0292
0293
0294 cpus_read_lock();
0295 ret = smp_call_on_cpu(0, raise_smi, smi_cmd, true);
0296 cpus_read_unlock();
0297
0298 return ret;
0299 }
0300 EXPORT_SYMBOL(dcdbas_smi_request);
0301
0302
0303
0304
0305
0306
0307
0308
0309
0310
0311
0312
0313 static ssize_t smi_request_store(struct device *dev,
0314 struct device_attribute *attr,
0315 const char *buf, size_t count)
0316 {
0317 struct smi_cmd *smi_cmd;
0318 unsigned long val = simple_strtoul(buf, NULL, 10);
0319 ssize_t ret;
0320
0321 mutex_lock(&smi_data_lock);
0322
0323 if (smi_buf.size < sizeof(struct smi_cmd)) {
0324 ret = -ENODEV;
0325 goto out;
0326 }
0327 smi_cmd = (struct smi_cmd *)smi_buf.virt;
0328
0329 switch (val) {
0330 case 2:
0331
0332 ret = dcdbas_smi_request(smi_cmd);
0333 if (!ret)
0334 ret = count;
0335 break;
0336 case 1:
0337
0338
0339
0340
0341
0342
0343
0344
0345
0346
0347
0348
0349 smi_cmd->ebx = (u32)smi_buf.dma +
0350 offsetof(struct smi_cmd, command_buffer);
0351 ret = dcdbas_smi_request(smi_cmd);
0352 if (!ret)
0353 ret = count;
0354 break;
0355 case 0:
0356 memset(smi_buf.virt, 0, smi_buf.size);
0357 ret = count;
0358 break;
0359 default:
0360 ret = -EINVAL;
0361 break;
0362 }
0363
0364 out:
0365 mutex_unlock(&smi_data_lock);
0366 return ret;
0367 }
0368
0369
0370
0371
0372
0373
0374 static int host_control_smi(void)
0375 {
0376 struct apm_cmd *apm_cmd;
0377 u8 *data;
0378 unsigned long flags;
0379 u32 num_ticks;
0380 s8 cmd_status;
0381 u8 index;
0382
0383 apm_cmd = (struct apm_cmd *)smi_buf.virt;
0384 apm_cmd->status = ESM_STATUS_CMD_UNSUCCESSFUL;
0385
0386 switch (host_control_smi_type) {
0387 case HC_SMITYPE_TYPE1:
0388 spin_lock_irqsave(&rtc_lock, flags);
0389
0390 data = (u8 *)&smi_buf.dma;
0391 for (index = PE1300_CMOS_CMD_STRUCT_PTR;
0392 index < (PE1300_CMOS_CMD_STRUCT_PTR + 4);
0393 index++, data++) {
0394 outb(index,
0395 (CMOS_BASE_PORT + CMOS_PAGE2_INDEX_PORT_PIIX4));
0396 outb(*data,
0397 (CMOS_BASE_PORT + CMOS_PAGE2_DATA_PORT_PIIX4));
0398 }
0399
0400
0401 cmd_status = ESM_STATUS_CMD_UNSUCCESSFUL;
0402 outb((u8) cmd_status, PCAT_APM_STATUS_PORT);
0403
0404
0405 outb(ESM_APM_CMD, PCAT_APM_CONTROL_PORT);
0406 spin_unlock_irqrestore(&rtc_lock, flags);
0407
0408
0409 num_ticks = TIMEOUT_USEC_SHORT_SEMA_BLOCKING;
0410 while ((s8)inb(PCAT_APM_STATUS_PORT) == ESM_STATUS_CMD_UNSUCCESSFUL) {
0411 num_ticks--;
0412 if (num_ticks == EXPIRED_TIMER)
0413 return -ETIME;
0414 }
0415 break;
0416
0417 case HC_SMITYPE_TYPE2:
0418 case HC_SMITYPE_TYPE3:
0419 spin_lock_irqsave(&rtc_lock, flags);
0420
0421 data = (u8 *)&smi_buf.dma;
0422 for (index = PE1400_CMOS_CMD_STRUCT_PTR;
0423 index < (PE1400_CMOS_CMD_STRUCT_PTR + 4);
0424 index++, data++) {
0425 outb(index, (CMOS_BASE_PORT + CMOS_PAGE1_INDEX_PORT));
0426 outb(*data, (CMOS_BASE_PORT + CMOS_PAGE1_DATA_PORT));
0427 }
0428
0429
0430 if (host_control_smi_type == HC_SMITYPE_TYPE3)
0431 outb(ESM_APM_CMD, PCAT_APM_CONTROL_PORT);
0432 else
0433 outb(ESM_APM_CMD, PE1400_APM_CONTROL_PORT);
0434
0435
0436 CMOS_READ(RTC_REG_C);
0437 spin_unlock_irqrestore(&rtc_lock, flags);
0438
0439
0440 cmd_status = inb(PE1400_APM_CONTROL_PORT);
0441
0442
0443 num_ticks = TIMEOUT_USEC_SHORT_SEMA_BLOCKING;
0444 while (apm_cmd->status == ESM_STATUS_CMD_UNSUCCESSFUL) {
0445 num_ticks--;
0446 if (num_ticks == EXPIRED_TIMER)
0447 return -ETIME;
0448 }
0449 break;
0450
0451 default:
0452 dev_dbg(&dcdbas_pdev->dev, "%s: invalid SMI type %u\n",
0453 __func__, host_control_smi_type);
0454 return -ENOSYS;
0455 }
0456
0457 return 0;
0458 }
0459
0460
0461
0462
0463
0464
0465
0466
0467
0468
0469 static void dcdbas_host_control(void)
0470 {
0471 struct apm_cmd *apm_cmd;
0472 u8 action;
0473
0474 if (host_control_action == HC_ACTION_NONE)
0475 return;
0476
0477 action = host_control_action;
0478 host_control_action = HC_ACTION_NONE;
0479
0480 if (!smi_buf.virt) {
0481 dev_dbg(&dcdbas_pdev->dev, "%s: no SMI buffer\n", __func__);
0482 return;
0483 }
0484
0485 if (smi_buf.size < sizeof(struct apm_cmd)) {
0486 dev_dbg(&dcdbas_pdev->dev, "%s: SMI buffer too small\n",
0487 __func__);
0488 return;
0489 }
0490
0491 apm_cmd = (struct apm_cmd *)smi_buf.virt;
0492
0493
0494 if (action & HC_ACTION_HOST_CONTROL_POWEROFF) {
0495 apm_cmd->command = ESM_APM_POWER_CYCLE;
0496 apm_cmd->reserved = 0;
0497 *((s16 *)&apm_cmd->parameters.shortreq.parm[0]) = (s16) 0;
0498 host_control_smi();
0499 } else if (action & HC_ACTION_HOST_CONTROL_POWERCYCLE) {
0500 apm_cmd->command = ESM_APM_POWER_CYCLE;
0501 apm_cmd->reserved = 0;
0502 *((s16 *)&apm_cmd->parameters.shortreq.parm[0]) = (s16) 20;
0503 host_control_smi();
0504 }
0505 }
0506
0507
0508
0509 static u8 checksum(u8 *buffer, u8 length)
0510 {
0511 u8 sum = 0;
0512 u8 *end = buffer + length;
0513
0514 while (buffer < end)
0515 sum += *buffer++;
0516 return sum;
0517 }
0518
0519 static inline struct smm_eps_table *check_eps_table(u8 *addr)
0520 {
0521 struct smm_eps_table *eps = (struct smm_eps_table *)addr;
0522
0523 if (strncmp(eps->smm_comm_buff_anchor, SMM_EPS_SIG, 4) != 0)
0524 return NULL;
0525
0526 if (checksum(addr, eps->length) != 0)
0527 return NULL;
0528
0529 return eps;
0530 }
0531
0532 static int dcdbas_check_wsmt(void)
0533 {
0534 const struct dmi_device *dev = NULL;
0535 struct acpi_table_wsmt *wsmt = NULL;
0536 struct smm_eps_table *eps = NULL;
0537 u64 bios_buf_paddr;
0538 u64 remap_size;
0539 u8 *addr;
0540
0541 acpi_get_table(ACPI_SIG_WSMT, 0, (struct acpi_table_header **)&wsmt);
0542 if (!wsmt)
0543 return 0;
0544
0545
0546 if (!(wsmt->protection_flags & ACPI_WSMT_FIXED_COMM_BUFFERS) ||
0547 !(wsmt->protection_flags & ACPI_WSMT_COMM_BUFFER_NESTED_PTR_PROTECTION))
0548 return 0;
0549
0550
0551
0552
0553
0554
0555
0556 while ((dev = dmi_find_device(DMI_DEV_TYPE_OEM_STRING, NULL, dev)))
0557 if (sscanf(dev->name, "30[%16llx;%8llx]", &bios_buf_paddr,
0558 &remap_size) == 2)
0559 goto remap;
0560
0561
0562 for (addr = (u8 *)__va(0xf0000);
0563 addr < (u8 *)__va(0x100000 - sizeof(struct smm_eps_table));
0564 addr += 16) {
0565 eps = check_eps_table(addr);
0566 if (eps)
0567 break;
0568 }
0569
0570 if (!eps) {
0571 dev_dbg(&dcdbas_pdev->dev, "found WSMT, but no firmware buffer found\n");
0572 return -ENODEV;
0573 }
0574 bios_buf_paddr = eps->smm_comm_buff_addr;
0575 remap_size = eps->num_of_4k_pages * PAGE_SIZE;
0576
0577 remap:
0578
0579
0580
0581
0582 if (upper_32_bits(bios_buf_paddr + 8)) {
0583 dev_warn(&dcdbas_pdev->dev, "found WSMT, but buffer address is above 4GB\n");
0584 return -EINVAL;
0585 }
0586
0587
0588
0589
0590 if (remap_size > MAX_SMI_DATA_BUF_SIZE + 8)
0591 remap_size = MAX_SMI_DATA_BUF_SIZE + 8;
0592
0593 bios_buffer = memremap(bios_buf_paddr, remap_size, MEMREMAP_WB);
0594 if (!bios_buffer) {
0595 dev_warn(&dcdbas_pdev->dev, "found WSMT, but failed to map buffer\n");
0596 return -ENOMEM;
0597 }
0598
0599
0600 smi_buf.dma = bios_buf_paddr + 8;
0601 smi_buf.virt = bios_buffer + 8;
0602 smi_buf.size = remap_size - 8;
0603 max_smi_data_buf_size = smi_buf.size;
0604 wsmt_enabled = true;
0605 dev_info(&dcdbas_pdev->dev,
0606 "WSMT found, using firmware-provided SMI buffer.\n");
0607 return 1;
0608 }
0609
0610
0611
0612
0613 static int dcdbas_reboot_notify(struct notifier_block *nb, unsigned long code,
0614 void *unused)
0615 {
0616 switch (code) {
0617 case SYS_DOWN:
0618 case SYS_HALT:
0619 case SYS_POWER_OFF:
0620 if (host_control_on_shutdown) {
0621
0622 printk(KERN_WARNING "Please wait for shutdown "
0623 "action to complete...\n");
0624 dcdbas_host_control();
0625 }
0626 break;
0627 }
0628
0629 return NOTIFY_DONE;
0630 }
0631
0632 static struct notifier_block dcdbas_reboot_nb = {
0633 .notifier_call = dcdbas_reboot_notify,
0634 .next = NULL,
0635 .priority = INT_MIN
0636 };
0637
0638 static DCDBAS_BIN_ATTR_RW(smi_data);
0639
0640 static struct bin_attribute *dcdbas_bin_attrs[] = {
0641 &bin_attr_smi_data,
0642 NULL
0643 };
0644
0645 static DCDBAS_DEV_ATTR_RW(smi_data_buf_size);
0646 static DCDBAS_DEV_ATTR_RO(smi_data_buf_phys_addr);
0647 static DCDBAS_DEV_ATTR_WO(smi_request);
0648 static DCDBAS_DEV_ATTR_RW(host_control_action);
0649 static DCDBAS_DEV_ATTR_RW(host_control_smi_type);
0650 static DCDBAS_DEV_ATTR_RW(host_control_on_shutdown);
0651
0652 static struct attribute *dcdbas_dev_attrs[] = {
0653 &dev_attr_smi_data_buf_size.attr,
0654 &dev_attr_smi_data_buf_phys_addr.attr,
0655 &dev_attr_smi_request.attr,
0656 &dev_attr_host_control_action.attr,
0657 &dev_attr_host_control_smi_type.attr,
0658 &dev_attr_host_control_on_shutdown.attr,
0659 NULL
0660 };
0661
0662 static const struct attribute_group dcdbas_attr_group = {
0663 .attrs = dcdbas_dev_attrs,
0664 .bin_attrs = dcdbas_bin_attrs,
0665 };
0666
0667 static int dcdbas_probe(struct platform_device *dev)
0668 {
0669 int error;
0670
0671 host_control_action = HC_ACTION_NONE;
0672 host_control_smi_type = HC_SMITYPE_NONE;
0673
0674 dcdbas_pdev = dev;
0675
0676
0677 error = dcdbas_check_wsmt();
0678 if (error < 0)
0679 return error;
0680
0681
0682
0683
0684
0685 error = dma_set_coherent_mask(&dcdbas_pdev->dev, DMA_BIT_MASK(32));
0686 if (error)
0687 return error;
0688
0689 error = sysfs_create_group(&dev->dev.kobj, &dcdbas_attr_group);
0690 if (error)
0691 return error;
0692
0693 register_reboot_notifier(&dcdbas_reboot_nb);
0694
0695 dev_info(&dev->dev, "%s (version %s)\n",
0696 DRIVER_DESCRIPTION, DRIVER_VERSION);
0697
0698 return 0;
0699 }
0700
0701 static int dcdbas_remove(struct platform_device *dev)
0702 {
0703 unregister_reboot_notifier(&dcdbas_reboot_nb);
0704 sysfs_remove_group(&dev->dev.kobj, &dcdbas_attr_group);
0705
0706 return 0;
0707 }
0708
0709 static struct platform_driver dcdbas_driver = {
0710 .driver = {
0711 .name = DRIVER_NAME,
0712 },
0713 .probe = dcdbas_probe,
0714 .remove = dcdbas_remove,
0715 };
0716
0717 static const struct platform_device_info dcdbas_dev_info __initconst = {
0718 .name = DRIVER_NAME,
0719 .id = -1,
0720 .dma_mask = DMA_BIT_MASK(32),
0721 };
0722
0723 static struct platform_device *dcdbas_pdev_reg;
0724
0725
0726
0727
0728 static int __init dcdbas_init(void)
0729 {
0730 int error;
0731
0732 error = platform_driver_register(&dcdbas_driver);
0733 if (error)
0734 return error;
0735
0736 dcdbas_pdev_reg = platform_device_register_full(&dcdbas_dev_info);
0737 if (IS_ERR(dcdbas_pdev_reg)) {
0738 error = PTR_ERR(dcdbas_pdev_reg);
0739 goto err_unregister_driver;
0740 }
0741
0742 return 0;
0743
0744 err_unregister_driver:
0745 platform_driver_unregister(&dcdbas_driver);
0746 return error;
0747 }
0748
0749
0750
0751
0752 static void __exit dcdbas_exit(void)
0753 {
0754
0755
0756
0757
0758 unregister_reboot_notifier(&dcdbas_reboot_nb);
0759
0760
0761
0762
0763
0764
0765
0766 if (dcdbas_pdev)
0767 smi_data_buf_free();
0768 if (bios_buffer)
0769 memunmap(bios_buffer);
0770 platform_device_unregister(dcdbas_pdev_reg);
0771 platform_driver_unregister(&dcdbas_driver);
0772 }
0773
0774 subsys_initcall_sync(dcdbas_init);
0775 module_exit(dcdbas_exit);
0776
0777 MODULE_DESCRIPTION(DRIVER_DESCRIPTION " (version " DRIVER_VERSION ")");
0778 MODULE_VERSION(DRIVER_VERSION);
0779 MODULE_AUTHOR("Dell Inc.");
0780 MODULE_LICENSE("GPL");
0781
0782 MODULE_ALIAS("dmi:*:[bs]vnD[Ee][Ll][Ll]*:*");