root/drivers/platform/x86/dell-smbios-base.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. dell_smbios_error
  2. dell_smbios_register_device
  3. dell_smbios_unregister_device
  4. dell_smbios_call_filter
  5. dell_smbios_call
  6. dell_smbios_find_token
  7. dell_laptop_register_notifier
  8. dell_laptop_unregister_notifier
  9. dell_laptop_call_notifier
  10. parse_da_table
  11. zero_duplicates
  12. find_tokens
  13. match_attribute
  14. location_show
  15. value_show
  16. build_tokens_sysfs
  17. free_group
  18. dell_smbios_init
  19. dell_smbios_exit

   1 // SPDX-License-Identifier: GPL-2.0-only
   2 /*
   3  *  Common functions for kernel modules using Dell SMBIOS
   4  *
   5  *  Copyright (c) Red Hat <mjg@redhat.com>
   6  *  Copyright (c) 2014 Gabriele Mazzotta <gabriele.mzt@gmail.com>
   7  *  Copyright (c) 2014 Pali Rohár <pali.rohar@gmail.com>
   8  *
   9  *  Based on documentation in the libsmbios package:
  10  *  Copyright (C) 2005-2014 Dell Inc.
  11  */
  12 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  13 
  14 #include <linux/kernel.h>
  15 #include <linux/module.h>
  16 #include <linux/capability.h>
  17 #include <linux/dmi.h>
  18 #include <linux/err.h>
  19 #include <linux/mutex.h>
  20 #include <linux/platform_device.h>
  21 #include <linux/slab.h>
  22 #include "dell-smbios.h"
  23 
  24 static u32 da_supported_commands;
  25 static int da_num_tokens;
  26 static struct platform_device *platform_device;
  27 static struct calling_interface_token *da_tokens;
  28 static struct device_attribute *token_location_attrs;
  29 static struct device_attribute *token_value_attrs;
  30 static struct attribute **token_attrs;
  31 static DEFINE_MUTEX(smbios_mutex);
  32 
  33 struct smbios_device {
  34         struct list_head list;
  35         struct device *device;
  36         int (*call_fn)(struct calling_interface_buffer *arg);
  37 };
  38 
  39 struct smbios_call {
  40         u32 need_capability;
  41         int cmd_class;
  42         int cmd_select;
  43 };
  44 
  45 /* calls that are whitelisted for given capabilities */
  46 static struct smbios_call call_whitelist[] = {
  47         /* generally tokens are allowed, but may be further filtered or
  48          * restricted by token blacklist or whitelist
  49          */
  50         {CAP_SYS_ADMIN, CLASS_TOKEN_READ,       SELECT_TOKEN_STD},
  51         {CAP_SYS_ADMIN, CLASS_TOKEN_READ,       SELECT_TOKEN_AC},
  52         {CAP_SYS_ADMIN, CLASS_TOKEN_READ,       SELECT_TOKEN_BAT},
  53         {CAP_SYS_ADMIN, CLASS_TOKEN_WRITE,      SELECT_TOKEN_STD},
  54         {CAP_SYS_ADMIN, CLASS_TOKEN_WRITE,      SELECT_TOKEN_AC},
  55         {CAP_SYS_ADMIN, CLASS_TOKEN_WRITE,      SELECT_TOKEN_BAT},
  56         /* used by userspace: fwupdate */
  57         {CAP_SYS_ADMIN, CLASS_ADMIN_PROP,       SELECT_ADMIN_PROP},
  58         /* used by userspace: fwupd */
  59         {CAP_SYS_ADMIN, CLASS_INFO,             SELECT_DOCK},
  60         {CAP_SYS_ADMIN, CLASS_FLASH_INTERFACE,  SELECT_FLASH_INTERFACE},
  61 };
  62 
  63 /* calls that are explicitly blacklisted */
  64 static struct smbios_call call_blacklist[] = {
  65         {0x0000,  1,  7}, /* manufacturing use */
  66         {0x0000,  6,  5}, /* manufacturing use */
  67         {0x0000, 11,  3}, /* write once */
  68         {0x0000, 11,  7}, /* write once */
  69         {0x0000, 11, 11}, /* write once */
  70         {0x0000, 19, -1}, /* diagnostics */
  71         /* handled by kernel: dell-laptop */
  72         {0x0000, CLASS_INFO, SELECT_RFKILL},
  73         {0x0000, CLASS_KBD_BACKLIGHT, SELECT_KBD_BACKLIGHT},
  74 };
  75 
  76 struct token_range {
  77         u32 need_capability;
  78         u16 min;
  79         u16 max;
  80 };
  81 
  82 /* tokens that are whitelisted for given capabilities */
  83 static struct token_range token_whitelist[] = {
  84         /* used by userspace: fwupdate */
  85         {CAP_SYS_ADMIN, CAPSULE_EN_TOKEN,       CAPSULE_DIS_TOKEN},
  86         /* can indicate to userspace that WMI is needed */
  87         {0x0000,        WSMT_EN_TOKEN,          WSMT_DIS_TOKEN}
  88 };
  89 
  90 /* tokens that are explicitly blacklisted */
  91 static struct token_range token_blacklist[] = {
  92         {0x0000, 0x0058, 0x0059}, /* ME use */
  93         {0x0000, 0x00CD, 0x00D0}, /* raid shadow copy */
  94         {0x0000, 0x013A, 0x01FF}, /* sata shadow copy */
  95         {0x0000, 0x0175, 0x0176}, /* write once */
  96         {0x0000, 0x0195, 0x0197}, /* diagnostics */
  97         {0x0000, 0x01DC, 0x01DD}, /* manufacturing use */
  98         {0x0000, 0x027D, 0x0284}, /* diagnostics */
  99         {0x0000, 0x02E3, 0x02E3}, /* manufacturing use */
 100         {0x0000, 0x02FF, 0x02FF}, /* manufacturing use */
 101         {0x0000, 0x0300, 0x0302}, /* manufacturing use */
 102         {0x0000, 0x0325, 0x0326}, /* manufacturing use */
 103         {0x0000, 0x0332, 0x0335}, /* fan control */
 104         {0x0000, 0x0350, 0x0350}, /* manufacturing use */
 105         {0x0000, 0x0363, 0x0363}, /* manufacturing use */
 106         {0x0000, 0x0368, 0x0368}, /* manufacturing use */
 107         {0x0000, 0x03F6, 0x03F7}, /* manufacturing use */
 108         {0x0000, 0x049E, 0x049F}, /* manufacturing use */
 109         {0x0000, 0x04A0, 0x04A3}, /* disagnostics */
 110         {0x0000, 0x04E6, 0x04E7}, /* manufacturing use */
 111         {0x0000, 0x4000, 0x7FFF}, /* internal BIOS use */
 112         {0x0000, 0x9000, 0x9001}, /* internal BIOS use */
 113         {0x0000, 0xA000, 0xBFFF}, /* write only */
 114         {0x0000, 0xEFF0, 0xEFFF}, /* internal BIOS use */
 115         /* handled by kernel: dell-laptop */
 116         {0x0000, BRIGHTNESS_TOKEN,      BRIGHTNESS_TOKEN},
 117         {0x0000, KBD_LED_OFF_TOKEN,     KBD_LED_AUTO_TOKEN},
 118         {0x0000, KBD_LED_AC_TOKEN,      KBD_LED_AC_TOKEN},
 119         {0x0000, KBD_LED_AUTO_25_TOKEN, KBD_LED_AUTO_75_TOKEN},
 120         {0x0000, KBD_LED_AUTO_100_TOKEN,        KBD_LED_AUTO_100_TOKEN},
 121         {0x0000, GLOBAL_MIC_MUTE_ENABLE,        GLOBAL_MIC_MUTE_DISABLE},
 122 };
 123 
 124 static LIST_HEAD(smbios_device_list);
 125 
 126 int dell_smbios_error(int value)
 127 {
 128         switch (value) {
 129         case 0: /* Completed successfully */
 130                 return 0;
 131         case -1: /* Completed with error */
 132                 return -EIO;
 133         case -2: /* Function not supported */
 134                 return -ENXIO;
 135         default: /* Unknown error */
 136                 return -EINVAL;
 137         }
 138 }
 139 EXPORT_SYMBOL_GPL(dell_smbios_error);
 140 
 141 int dell_smbios_register_device(struct device *d, void *call_fn)
 142 {
 143         struct smbios_device *priv;
 144 
 145         priv = devm_kzalloc(d, sizeof(struct smbios_device), GFP_KERNEL);
 146         if (!priv)
 147                 return -ENOMEM;
 148         get_device(d);
 149         priv->device = d;
 150         priv->call_fn = call_fn;
 151         mutex_lock(&smbios_mutex);
 152         list_add_tail(&priv->list, &smbios_device_list);
 153         mutex_unlock(&smbios_mutex);
 154         dev_dbg(d, "Added device: %s\n", d->driver->name);
 155         return 0;
 156 }
 157 EXPORT_SYMBOL_GPL(dell_smbios_register_device);
 158 
 159 void dell_smbios_unregister_device(struct device *d)
 160 {
 161         struct smbios_device *priv;
 162 
 163         mutex_lock(&smbios_mutex);
 164         list_for_each_entry(priv, &smbios_device_list, list) {
 165                 if (priv->device == d) {
 166                         list_del(&priv->list);
 167                         put_device(d);
 168                         break;
 169                 }
 170         }
 171         mutex_unlock(&smbios_mutex);
 172         dev_dbg(d, "Remove device: %s\n", d->driver->name);
 173 }
 174 EXPORT_SYMBOL_GPL(dell_smbios_unregister_device);
 175 
 176 int dell_smbios_call_filter(struct device *d,
 177                             struct calling_interface_buffer *buffer)
 178 {
 179         u16 t = 0;
 180         int i;
 181 
 182         /* can't make calls over 30 */
 183         if (buffer->cmd_class > 30) {
 184                 dev_dbg(d, "class too big: %u\n", buffer->cmd_class);
 185                 return -EINVAL;
 186         }
 187 
 188         /* supported calls on the particular system */
 189         if (!(da_supported_commands & (1 << buffer->cmd_class))) {
 190                 dev_dbg(d, "invalid command, supported commands: 0x%8x\n",
 191                         da_supported_commands);
 192                 return -EINVAL;
 193         }
 194 
 195         /* match against call blacklist  */
 196         for (i = 0; i < ARRAY_SIZE(call_blacklist); i++) {
 197                 if (buffer->cmd_class != call_blacklist[i].cmd_class)
 198                         continue;
 199                 if (buffer->cmd_select != call_blacklist[i].cmd_select &&
 200                     call_blacklist[i].cmd_select != -1)
 201                         continue;
 202                 dev_dbg(d, "blacklisted command: %u/%u\n",
 203                         buffer->cmd_class, buffer->cmd_select);
 204                 return -EINVAL;
 205         }
 206 
 207         /* if a token call, find token ID */
 208 
 209         if ((buffer->cmd_class == CLASS_TOKEN_READ ||
 210              buffer->cmd_class == CLASS_TOKEN_WRITE) &&
 211              buffer->cmd_select < 3) {
 212                 /* tokens enabled ? */
 213                 if (!da_tokens) {
 214                         dev_dbg(d, "no token support on this system\n");
 215                         return -EINVAL;
 216                 }
 217 
 218                 /* find the matching token ID */
 219                 for (i = 0; i < da_num_tokens; i++) {
 220                         if (da_tokens[i].location != buffer->input[0])
 221                                 continue;
 222                         t = da_tokens[i].tokenID;
 223                         break;
 224                 }
 225 
 226                 /* token call; but token didn't exist */
 227                 if (!t) {
 228                         dev_dbg(d, "token at location %04x doesn't exist\n",
 229                                 buffer->input[0]);
 230                         return -EINVAL;
 231                 }
 232 
 233                 /* match against token blacklist */
 234                 for (i = 0; i < ARRAY_SIZE(token_blacklist); i++) {
 235                         if (!token_blacklist[i].min || !token_blacklist[i].max)
 236                                 continue;
 237                         if (t >= token_blacklist[i].min &&
 238                             t <= token_blacklist[i].max)
 239                                 return -EINVAL;
 240                 }
 241 
 242                 /* match against token whitelist */
 243                 for (i = 0; i < ARRAY_SIZE(token_whitelist); i++) {
 244                         if (!token_whitelist[i].min || !token_whitelist[i].max)
 245                                 continue;
 246                         if (t < token_whitelist[i].min ||
 247                             t > token_whitelist[i].max)
 248                                 continue;
 249                         if (!token_whitelist[i].need_capability ||
 250                             capable(token_whitelist[i].need_capability)) {
 251                                 dev_dbg(d, "whitelisted token: %x\n", t);
 252                                 return 0;
 253                         }
 254 
 255                 }
 256         }
 257         /* match against call whitelist */
 258         for (i = 0; i < ARRAY_SIZE(call_whitelist); i++) {
 259                 if (buffer->cmd_class != call_whitelist[i].cmd_class)
 260                         continue;
 261                 if (buffer->cmd_select != call_whitelist[i].cmd_select)
 262                         continue;
 263                 if (!call_whitelist[i].need_capability ||
 264                     capable(call_whitelist[i].need_capability)) {
 265                         dev_dbg(d, "whitelisted capable command: %u/%u\n",
 266                         buffer->cmd_class, buffer->cmd_select);
 267                         return 0;
 268                 }
 269                 dev_dbg(d, "missing capability %d for %u/%u\n",
 270                         call_whitelist[i].need_capability,
 271                         buffer->cmd_class, buffer->cmd_select);
 272 
 273         }
 274 
 275         /* not in a whitelist, only allow processes with capabilities */
 276         if (capable(CAP_SYS_RAWIO)) {
 277                 dev_dbg(d, "Allowing %u/%u due to CAP_SYS_RAWIO\n",
 278                         buffer->cmd_class, buffer->cmd_select);
 279                 return 0;
 280         }
 281 
 282         return -EACCES;
 283 }
 284 EXPORT_SYMBOL_GPL(dell_smbios_call_filter);
 285 
 286 int dell_smbios_call(struct calling_interface_buffer *buffer)
 287 {
 288         int (*call_fn)(struct calling_interface_buffer *) = NULL;
 289         struct device *selected_dev = NULL;
 290         struct smbios_device *priv;
 291         int ret;
 292 
 293         mutex_lock(&smbios_mutex);
 294         list_for_each_entry(priv, &smbios_device_list, list) {
 295                 if (!selected_dev || priv->device->id >= selected_dev->id) {
 296                         dev_dbg(priv->device, "Trying device ID: %d\n",
 297                                 priv->device->id);
 298                         call_fn = priv->call_fn;
 299                         selected_dev = priv->device;
 300                 }
 301         }
 302 
 303         if (!selected_dev) {
 304                 ret = -ENODEV;
 305                 pr_err("No dell-smbios drivers are loaded\n");
 306                 goto out_smbios_call;
 307         }
 308 
 309         ret = call_fn(buffer);
 310 
 311 out_smbios_call:
 312         mutex_unlock(&smbios_mutex);
 313         return ret;
 314 }
 315 EXPORT_SYMBOL_GPL(dell_smbios_call);
 316 
 317 struct calling_interface_token *dell_smbios_find_token(int tokenid)
 318 {
 319         int i;
 320 
 321         if (!da_tokens)
 322                 return NULL;
 323 
 324         for (i = 0; i < da_num_tokens; i++) {
 325                 if (da_tokens[i].tokenID == tokenid)
 326                         return &da_tokens[i];
 327         }
 328 
 329         return NULL;
 330 }
 331 EXPORT_SYMBOL_GPL(dell_smbios_find_token);
 332 
 333 static BLOCKING_NOTIFIER_HEAD(dell_laptop_chain_head);
 334 
 335 int dell_laptop_register_notifier(struct notifier_block *nb)
 336 {
 337         return blocking_notifier_chain_register(&dell_laptop_chain_head, nb);
 338 }
 339 EXPORT_SYMBOL_GPL(dell_laptop_register_notifier);
 340 
 341 int dell_laptop_unregister_notifier(struct notifier_block *nb)
 342 {
 343         return blocking_notifier_chain_unregister(&dell_laptop_chain_head, nb);
 344 }
 345 EXPORT_SYMBOL_GPL(dell_laptop_unregister_notifier);
 346 
 347 void dell_laptop_call_notifier(unsigned long action, void *data)
 348 {
 349         blocking_notifier_call_chain(&dell_laptop_chain_head, action, data);
 350 }
 351 EXPORT_SYMBOL_GPL(dell_laptop_call_notifier);
 352 
 353 static void __init parse_da_table(const struct dmi_header *dm)
 354 {
 355         /* Final token is a terminator, so we don't want to copy it */
 356         int tokens = (dm->length-11)/sizeof(struct calling_interface_token)-1;
 357         struct calling_interface_token *new_da_tokens;
 358         struct calling_interface_structure *table =
 359                 container_of(dm, struct calling_interface_structure, header);
 360 
 361         /*
 362          * 4 bytes of table header, plus 7 bytes of Dell header
 363          * plus at least 6 bytes of entry
 364          */
 365 
 366         if (dm->length < 17)
 367                 return;
 368 
 369         da_supported_commands = table->supportedCmds;
 370 
 371         new_da_tokens = krealloc(da_tokens, (da_num_tokens + tokens) *
 372                                  sizeof(struct calling_interface_token),
 373                                  GFP_KERNEL);
 374 
 375         if (!new_da_tokens)
 376                 return;
 377         da_tokens = new_da_tokens;
 378 
 379         memcpy(da_tokens+da_num_tokens, table->tokens,
 380                sizeof(struct calling_interface_token) * tokens);
 381 
 382         da_num_tokens += tokens;
 383 }
 384 
 385 static void zero_duplicates(struct device *dev)
 386 {
 387         int i, j;
 388 
 389         for (i = 0; i < da_num_tokens; i++) {
 390                 if (da_tokens[i].tokenID == 0)
 391                         continue;
 392                 for (j = i+1; j < da_num_tokens; j++) {
 393                         if (da_tokens[j].tokenID == 0)
 394                                 continue;
 395                         if (da_tokens[i].tokenID == da_tokens[j].tokenID) {
 396                                 dev_dbg(dev, "Zeroing dup token ID %x(%x/%x)\n",
 397                                         da_tokens[j].tokenID,
 398                                         da_tokens[j].location,
 399                                         da_tokens[j].value);
 400                                 da_tokens[j].tokenID = 0;
 401                         }
 402                 }
 403         }
 404 }
 405 
 406 static void __init find_tokens(const struct dmi_header *dm, void *dummy)
 407 {
 408         switch (dm->type) {
 409         case 0xd4: /* Indexed IO */
 410         case 0xd5: /* Protected Area Type 1 */
 411         case 0xd6: /* Protected Area Type 2 */
 412                 break;
 413         case 0xda: /* Calling interface */
 414                 parse_da_table(dm);
 415                 break;
 416         }
 417 }
 418 
 419 static int match_attribute(struct device *dev,
 420                            struct device_attribute *attr)
 421 {
 422         int i;
 423 
 424         for (i = 0; i < da_num_tokens * 2; i++) {
 425                 if (!token_attrs[i])
 426                         continue;
 427                 if (strcmp(token_attrs[i]->name, attr->attr.name) == 0)
 428                         return i/2;
 429         }
 430         dev_dbg(dev, "couldn't match: %s\n", attr->attr.name);
 431         return -EINVAL;
 432 }
 433 
 434 static ssize_t location_show(struct device *dev,
 435                              struct device_attribute *attr, char *buf)
 436 {
 437         int i;
 438 
 439         if (!capable(CAP_SYS_ADMIN))
 440                 return -EPERM;
 441 
 442         i = match_attribute(dev, attr);
 443         if (i > 0)
 444                 return scnprintf(buf, PAGE_SIZE, "%08x", da_tokens[i].location);
 445         return 0;
 446 }
 447 
 448 static ssize_t value_show(struct device *dev,
 449                           struct device_attribute *attr, char *buf)
 450 {
 451         int i;
 452 
 453         if (!capable(CAP_SYS_ADMIN))
 454                 return -EPERM;
 455 
 456         i = match_attribute(dev, attr);
 457         if (i > 0)
 458                 return scnprintf(buf, PAGE_SIZE, "%08x", da_tokens[i].value);
 459         return 0;
 460 }
 461 
 462 static struct attribute_group smbios_attribute_group = {
 463         .name = "tokens"
 464 };
 465 
 466 static struct platform_driver platform_driver = {
 467         .driver = {
 468                 .name = "dell-smbios",
 469         },
 470 };
 471 
 472 static int build_tokens_sysfs(struct platform_device *dev)
 473 {
 474         char *location_name;
 475         char *value_name;
 476         size_t size;
 477         int ret;
 478         int i, j;
 479 
 480         /* (number of tokens  + 1 for null terminated */
 481         size = sizeof(struct device_attribute) * (da_num_tokens + 1);
 482         token_location_attrs = kzalloc(size, GFP_KERNEL);
 483         if (!token_location_attrs)
 484                 return -ENOMEM;
 485         token_value_attrs = kzalloc(size, GFP_KERNEL);
 486         if (!token_value_attrs)
 487                 goto out_allocate_value;
 488 
 489         /* need to store both location and value + terminator*/
 490         size = sizeof(struct attribute *) * ((2 * da_num_tokens) + 1);
 491         token_attrs = kzalloc(size, GFP_KERNEL);
 492         if (!token_attrs)
 493                 goto out_allocate_attrs;
 494 
 495         for (i = 0, j = 0; i < da_num_tokens; i++) {
 496                 /* skip empty */
 497                 if (da_tokens[i].tokenID == 0)
 498                         continue;
 499                 /* add location */
 500                 location_name = kasprintf(GFP_KERNEL, "%04x_location",
 501                                           da_tokens[i].tokenID);
 502                 if (location_name == NULL)
 503                         goto out_unwind_strings;
 504                 sysfs_attr_init(&token_location_attrs[i].attr);
 505                 token_location_attrs[i].attr.name = location_name;
 506                 token_location_attrs[i].attr.mode = 0444;
 507                 token_location_attrs[i].show = location_show;
 508                 token_attrs[j++] = &token_location_attrs[i].attr;
 509 
 510                 /* add value */
 511                 value_name = kasprintf(GFP_KERNEL, "%04x_value",
 512                                        da_tokens[i].tokenID);
 513                 if (value_name == NULL)
 514                         goto loop_fail_create_value;
 515                 sysfs_attr_init(&token_value_attrs[i].attr);
 516                 token_value_attrs[i].attr.name = value_name;
 517                 token_value_attrs[i].attr.mode = 0444;
 518                 token_value_attrs[i].show = value_show;
 519                 token_attrs[j++] = &token_value_attrs[i].attr;
 520                 continue;
 521 
 522 loop_fail_create_value:
 523                 kfree(location_name);
 524                 goto out_unwind_strings;
 525         }
 526         smbios_attribute_group.attrs = token_attrs;
 527 
 528         ret = sysfs_create_group(&dev->dev.kobj, &smbios_attribute_group);
 529         if (ret)
 530                 goto out_unwind_strings;
 531         return 0;
 532 
 533 out_unwind_strings:
 534         while (i--) {
 535                 kfree(token_location_attrs[i].attr.name);
 536                 kfree(token_value_attrs[i].attr.name);
 537         }
 538         kfree(token_attrs);
 539 out_allocate_attrs:
 540         kfree(token_value_attrs);
 541 out_allocate_value:
 542         kfree(token_location_attrs);
 543 
 544         return -ENOMEM;
 545 }
 546 
 547 static void free_group(struct platform_device *pdev)
 548 {
 549         int i;
 550 
 551         sysfs_remove_group(&pdev->dev.kobj,
 552                                 &smbios_attribute_group);
 553         for (i = 0; i < da_num_tokens; i++) {
 554                 kfree(token_location_attrs[i].attr.name);
 555                 kfree(token_value_attrs[i].attr.name);
 556         }
 557         kfree(token_attrs);
 558         kfree(token_value_attrs);
 559         kfree(token_location_attrs);
 560 }
 561 
 562 static int __init dell_smbios_init(void)
 563 {
 564         int ret, wmi, smm;
 565 
 566         if (!dmi_find_device(DMI_DEV_TYPE_OEM_STRING, "Dell System", NULL) &&
 567             !dmi_find_device(DMI_DEV_TYPE_OEM_STRING, "www.dell.com", NULL)) {
 568                 pr_err("Unable to run on non-Dell system\n");
 569                 return -ENODEV;
 570         }
 571 
 572         dmi_walk(find_tokens, NULL);
 573 
 574         ret = platform_driver_register(&platform_driver);
 575         if (ret)
 576                 goto fail_platform_driver;
 577 
 578         platform_device = platform_device_alloc("dell-smbios", 0);
 579         if (!platform_device) {
 580                 ret = -ENOMEM;
 581                 goto fail_platform_device_alloc;
 582         }
 583         ret = platform_device_add(platform_device);
 584         if (ret)
 585                 goto fail_platform_device_add;
 586 
 587         /* register backends */
 588         wmi = init_dell_smbios_wmi();
 589         if (wmi)
 590                 pr_debug("Failed to initialize WMI backend: %d\n", wmi);
 591         smm = init_dell_smbios_smm();
 592         if (smm)
 593                 pr_debug("Failed to initialize SMM backend: %d\n", smm);
 594         if (wmi && smm) {
 595                 pr_err("No SMBIOS backends available (wmi: %d, smm: %d)\n",
 596                         wmi, smm);
 597                 goto fail_create_group;
 598         }
 599 
 600         if (da_tokens)  {
 601                 /* duplicate tokens will cause problems building sysfs files */
 602                 zero_duplicates(&platform_device->dev);
 603 
 604                 ret = build_tokens_sysfs(platform_device);
 605                 if (ret)
 606                         goto fail_sysfs;
 607         }
 608 
 609         return 0;
 610 
 611 fail_sysfs:
 612         free_group(platform_device);
 613 
 614 fail_create_group:
 615         platform_device_del(platform_device);
 616 
 617 fail_platform_device_add:
 618         platform_device_put(platform_device);
 619 
 620 fail_platform_device_alloc:
 621         platform_driver_unregister(&platform_driver);
 622 
 623 fail_platform_driver:
 624         kfree(da_tokens);
 625         return ret;
 626 }
 627 
 628 static void __exit dell_smbios_exit(void)
 629 {
 630         exit_dell_smbios_wmi();
 631         exit_dell_smbios_smm();
 632         mutex_lock(&smbios_mutex);
 633         if (platform_device) {
 634                 if (da_tokens)
 635                         free_group(platform_device);
 636                 platform_device_unregister(platform_device);
 637                 platform_driver_unregister(&platform_driver);
 638         }
 639         kfree(da_tokens);
 640         mutex_unlock(&smbios_mutex);
 641 }
 642 
 643 module_init(dell_smbios_init);
 644 module_exit(dell_smbios_exit);
 645 
 646 MODULE_AUTHOR("Matthew Garrett <mjg@redhat.com>");
 647 MODULE_AUTHOR("Gabriele Mazzotta <gabriele.mzt@gmail.com>");
 648 MODULE_AUTHOR("Pali Rohár <pali.rohar@gmail.com>");
 649 MODULE_AUTHOR("Mario Limonciello <mario.limonciello@dell.com>");
 650 MODULE_DESCRIPTION("Common functions for kernel modules using Dell SMBIOS");
 651 MODULE_LICENSE("GPL");

/* [<][>][^][v][top][bottom][index][help] */