0001
0002
0003
0004
0005
0006
0007
0008 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
0009
0010 #include <linux/acpi.h>
0011 #include <linux/module.h>
0012 #include <linux/platform_device.h>
0013 #include <linux/dmi.h>
0014 #include <linux/leds.h>
0015
0016 #define LEGACY_CONTROL_GUID "A90597CE-A997-11DA-B012-B622A1EF5492"
0017 #define LEGACY_POWER_CONTROL_GUID "A80593CE-A997-11DA-B012-B622A1EF5492"
0018 #define WMAX_CONTROL_GUID "A70591CE-A997-11DA-B012-B622A1EF5492"
0019
0020 #define WMAX_METHOD_HDMI_SOURCE 0x1
0021 #define WMAX_METHOD_HDMI_STATUS 0x2
0022 #define WMAX_METHOD_BRIGHTNESS 0x3
0023 #define WMAX_METHOD_ZONE_CONTROL 0x4
0024 #define WMAX_METHOD_HDMI_CABLE 0x5
0025 #define WMAX_METHOD_AMPLIFIER_CABLE 0x6
0026 #define WMAX_METHOD_DEEP_SLEEP_CONTROL 0x0B
0027 #define WMAX_METHOD_DEEP_SLEEP_STATUS 0x0C
0028
0029 MODULE_AUTHOR("Mario Limonciello <mario.limonciello@outlook.com>");
0030 MODULE_DESCRIPTION("Alienware special feature control");
0031 MODULE_LICENSE("GPL");
0032 MODULE_ALIAS("wmi:" LEGACY_CONTROL_GUID);
0033 MODULE_ALIAS("wmi:" WMAX_CONTROL_GUID);
0034
0035 enum INTERFACE_FLAGS {
0036 LEGACY,
0037 WMAX,
0038 };
0039
0040 enum LEGACY_CONTROL_STATES {
0041 LEGACY_RUNNING = 1,
0042 LEGACY_BOOTING = 0,
0043 LEGACY_SUSPEND = 3,
0044 };
0045
0046 enum WMAX_CONTROL_STATES {
0047 WMAX_RUNNING = 0xFF,
0048 WMAX_BOOTING = 0,
0049 WMAX_SUSPEND = 3,
0050 };
0051
0052 struct quirk_entry {
0053 u8 num_zones;
0054 u8 hdmi_mux;
0055 u8 amplifier;
0056 u8 deepslp;
0057 };
0058
0059 static struct quirk_entry *quirks;
0060
0061
0062 static struct quirk_entry quirk_inspiron5675 = {
0063 .num_zones = 2,
0064 .hdmi_mux = 0,
0065 .amplifier = 0,
0066 .deepslp = 0,
0067 };
0068
0069 static struct quirk_entry quirk_unknown = {
0070 .num_zones = 2,
0071 .hdmi_mux = 0,
0072 .amplifier = 0,
0073 .deepslp = 0,
0074 };
0075
0076 static struct quirk_entry quirk_x51_r1_r2 = {
0077 .num_zones = 3,
0078 .hdmi_mux = 0,
0079 .amplifier = 0,
0080 .deepslp = 0,
0081 };
0082
0083 static struct quirk_entry quirk_x51_r3 = {
0084 .num_zones = 4,
0085 .hdmi_mux = 0,
0086 .amplifier = 1,
0087 .deepslp = 0,
0088 };
0089
0090 static struct quirk_entry quirk_asm100 = {
0091 .num_zones = 2,
0092 .hdmi_mux = 1,
0093 .amplifier = 0,
0094 .deepslp = 0,
0095 };
0096
0097 static struct quirk_entry quirk_asm200 = {
0098 .num_zones = 2,
0099 .hdmi_mux = 1,
0100 .amplifier = 0,
0101 .deepslp = 1,
0102 };
0103
0104 static struct quirk_entry quirk_asm201 = {
0105 .num_zones = 2,
0106 .hdmi_mux = 1,
0107 .amplifier = 1,
0108 .deepslp = 1,
0109 };
0110
0111 static int __init dmi_matched(const struct dmi_system_id *dmi)
0112 {
0113 quirks = dmi->driver_data;
0114 return 1;
0115 }
0116
0117 static const struct dmi_system_id alienware_quirks[] __initconst = {
0118 {
0119 .callback = dmi_matched,
0120 .ident = "Alienware X51 R3",
0121 .matches = {
0122 DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
0123 DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51 R3"),
0124 },
0125 .driver_data = &quirk_x51_r3,
0126 },
0127 {
0128 .callback = dmi_matched,
0129 .ident = "Alienware X51 R2",
0130 .matches = {
0131 DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
0132 DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51 R2"),
0133 },
0134 .driver_data = &quirk_x51_r1_r2,
0135 },
0136 {
0137 .callback = dmi_matched,
0138 .ident = "Alienware X51 R1",
0139 .matches = {
0140 DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
0141 DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51"),
0142 },
0143 .driver_data = &quirk_x51_r1_r2,
0144 },
0145 {
0146 .callback = dmi_matched,
0147 .ident = "Alienware ASM100",
0148 .matches = {
0149 DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
0150 DMI_MATCH(DMI_PRODUCT_NAME, "ASM100"),
0151 },
0152 .driver_data = &quirk_asm100,
0153 },
0154 {
0155 .callback = dmi_matched,
0156 .ident = "Alienware ASM200",
0157 .matches = {
0158 DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
0159 DMI_MATCH(DMI_PRODUCT_NAME, "ASM200"),
0160 },
0161 .driver_data = &quirk_asm200,
0162 },
0163 {
0164 .callback = dmi_matched,
0165 .ident = "Alienware ASM201",
0166 .matches = {
0167 DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
0168 DMI_MATCH(DMI_PRODUCT_NAME, "ASM201"),
0169 },
0170 .driver_data = &quirk_asm201,
0171 },
0172 {
0173 .callback = dmi_matched,
0174 .ident = "Dell Inc. Inspiron 5675",
0175 .matches = {
0176 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
0177 DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 5675"),
0178 },
0179 .driver_data = &quirk_inspiron5675,
0180 },
0181 {}
0182 };
0183
0184 struct color_platform {
0185 u8 blue;
0186 u8 green;
0187 u8 red;
0188 } __packed;
0189
0190 struct platform_zone {
0191 u8 location;
0192 struct device_attribute *attr;
0193 struct color_platform colors;
0194 };
0195
0196 struct wmax_brightness_args {
0197 u32 led_mask;
0198 u32 percentage;
0199 };
0200
0201 struct wmax_basic_args {
0202 u8 arg;
0203 };
0204
0205 struct legacy_led_args {
0206 struct color_platform colors;
0207 u8 brightness;
0208 u8 state;
0209 } __packed;
0210
0211 struct wmax_led_args {
0212 u32 led_mask;
0213 struct color_platform colors;
0214 u8 state;
0215 } __packed;
0216
0217 static struct platform_device *platform_device;
0218 static struct device_attribute *zone_dev_attrs;
0219 static struct attribute **zone_attrs;
0220 static struct platform_zone *zone_data;
0221
0222 static struct platform_driver platform_driver = {
0223 .driver = {
0224 .name = "alienware-wmi",
0225 }
0226 };
0227
0228 static struct attribute_group zone_attribute_group = {
0229 .name = "rgb_zones",
0230 };
0231
0232 static u8 interface;
0233 static u8 lighting_control_state;
0234 static u8 global_brightness;
0235
0236
0237
0238
0239 static int parse_rgb(const char *buf, struct platform_zone *zone)
0240 {
0241 long unsigned int rgb;
0242 int ret;
0243 union color_union {
0244 struct color_platform cp;
0245 int package;
0246 } repackager;
0247
0248 ret = kstrtoul(buf, 16, &rgb);
0249 if (ret)
0250 return ret;
0251
0252
0253 if (rgb > 0xFFFFFF)
0254 return -EINVAL;
0255
0256 repackager.package = rgb & 0x0f0f0f0f;
0257 pr_debug("alienware-wmi: r: %d g:%d b: %d\n",
0258 repackager.cp.red, repackager.cp.green, repackager.cp.blue);
0259 zone->colors = repackager.cp;
0260 return 0;
0261 }
0262
0263 static struct platform_zone *match_zone(struct device_attribute *attr)
0264 {
0265 u8 zone;
0266
0267 for (zone = 0; zone < quirks->num_zones; zone++) {
0268 if ((struct device_attribute *)zone_data[zone].attr == attr) {
0269 pr_debug("alienware-wmi: matched zone location: %d\n",
0270 zone_data[zone].location);
0271 return &zone_data[zone];
0272 }
0273 }
0274 return NULL;
0275 }
0276
0277
0278
0279
0280 static int alienware_update_led(struct platform_zone *zone)
0281 {
0282 int method_id;
0283 acpi_status status;
0284 char *guid;
0285 struct acpi_buffer input;
0286 struct legacy_led_args legacy_args;
0287 struct wmax_led_args wmax_basic_args;
0288 if (interface == WMAX) {
0289 wmax_basic_args.led_mask = 1 << zone->location;
0290 wmax_basic_args.colors = zone->colors;
0291 wmax_basic_args.state = lighting_control_state;
0292 guid = WMAX_CONTROL_GUID;
0293 method_id = WMAX_METHOD_ZONE_CONTROL;
0294
0295 input.length = (acpi_size) sizeof(wmax_basic_args);
0296 input.pointer = &wmax_basic_args;
0297 } else {
0298 legacy_args.colors = zone->colors;
0299 legacy_args.brightness = global_brightness;
0300 legacy_args.state = 0;
0301 if (lighting_control_state == LEGACY_BOOTING ||
0302 lighting_control_state == LEGACY_SUSPEND) {
0303 guid = LEGACY_POWER_CONTROL_GUID;
0304 legacy_args.state = lighting_control_state;
0305 } else
0306 guid = LEGACY_CONTROL_GUID;
0307 method_id = zone->location + 1;
0308
0309 input.length = (acpi_size) sizeof(legacy_args);
0310 input.pointer = &legacy_args;
0311 }
0312 pr_debug("alienware-wmi: guid %s method %d\n", guid, method_id);
0313
0314 status = wmi_evaluate_method(guid, 0, method_id, &input, NULL);
0315 if (ACPI_FAILURE(status))
0316 pr_err("alienware-wmi: zone set failure: %u\n", status);
0317 return ACPI_FAILURE(status);
0318 }
0319
0320 static ssize_t zone_show(struct device *dev, struct device_attribute *attr,
0321 char *buf)
0322 {
0323 struct platform_zone *target_zone;
0324 target_zone = match_zone(attr);
0325 if (target_zone == NULL)
0326 return sprintf(buf, "red: -1, green: -1, blue: -1\n");
0327 return sprintf(buf, "red: %d, green: %d, blue: %d\n",
0328 target_zone->colors.red,
0329 target_zone->colors.green, target_zone->colors.blue);
0330
0331 }
0332
0333 static ssize_t zone_set(struct device *dev, struct device_attribute *attr,
0334 const char *buf, size_t count)
0335 {
0336 struct platform_zone *target_zone;
0337 int ret;
0338 target_zone = match_zone(attr);
0339 if (target_zone == NULL) {
0340 pr_err("alienware-wmi: invalid target zone\n");
0341 return 1;
0342 }
0343 ret = parse_rgb(buf, target_zone);
0344 if (ret)
0345 return ret;
0346 ret = alienware_update_led(target_zone);
0347 return ret ? ret : count;
0348 }
0349
0350
0351
0352
0353 static int wmax_brightness(int brightness)
0354 {
0355 acpi_status status;
0356 struct acpi_buffer input;
0357 struct wmax_brightness_args args = {
0358 .led_mask = 0xFF,
0359 .percentage = brightness,
0360 };
0361 input.length = (acpi_size) sizeof(args);
0362 input.pointer = &args;
0363 status = wmi_evaluate_method(WMAX_CONTROL_GUID, 0,
0364 WMAX_METHOD_BRIGHTNESS, &input, NULL);
0365 if (ACPI_FAILURE(status))
0366 pr_err("alienware-wmi: brightness set failure: %u\n", status);
0367 return ACPI_FAILURE(status);
0368 }
0369
0370 static void global_led_set(struct led_classdev *led_cdev,
0371 enum led_brightness brightness)
0372 {
0373 int ret;
0374 global_brightness = brightness;
0375 if (interface == WMAX)
0376 ret = wmax_brightness(brightness);
0377 else
0378 ret = alienware_update_led(&zone_data[0]);
0379 if (ret)
0380 pr_err("LED brightness update failed\n");
0381 }
0382
0383 static enum led_brightness global_led_get(struct led_classdev *led_cdev)
0384 {
0385 return global_brightness;
0386 }
0387
0388 static struct led_classdev global_led = {
0389 .brightness_set = global_led_set,
0390 .brightness_get = global_led_get,
0391 .name = "alienware::global_brightness",
0392 };
0393
0394
0395
0396
0397 static ssize_t show_control_state(struct device *dev,
0398 struct device_attribute *attr, char *buf)
0399 {
0400 if (lighting_control_state == LEGACY_BOOTING)
0401 return scnprintf(buf, PAGE_SIZE, "[booting] running suspend\n");
0402 else if (lighting_control_state == LEGACY_SUSPEND)
0403 return scnprintf(buf, PAGE_SIZE, "booting running [suspend]\n");
0404 return scnprintf(buf, PAGE_SIZE, "booting [running] suspend\n");
0405 }
0406
0407 static ssize_t store_control_state(struct device *dev,
0408 struct device_attribute *attr,
0409 const char *buf, size_t count)
0410 {
0411 long unsigned int val;
0412 if (strcmp(buf, "booting\n") == 0)
0413 val = LEGACY_BOOTING;
0414 else if (strcmp(buf, "suspend\n") == 0)
0415 val = LEGACY_SUSPEND;
0416 else if (interface == LEGACY)
0417 val = LEGACY_RUNNING;
0418 else
0419 val = WMAX_RUNNING;
0420 lighting_control_state = val;
0421 pr_debug("alienware-wmi: updated control state to %d\n",
0422 lighting_control_state);
0423 return count;
0424 }
0425
0426 static DEVICE_ATTR(lighting_control_state, 0644, show_control_state,
0427 store_control_state);
0428
0429 static int alienware_zone_init(struct platform_device *dev)
0430 {
0431 u8 zone;
0432 char buffer[10];
0433 char *name;
0434
0435 if (interface == WMAX) {
0436 lighting_control_state = WMAX_RUNNING;
0437 } else if (interface == LEGACY) {
0438 lighting_control_state = LEGACY_RUNNING;
0439 }
0440 global_led.max_brightness = 0x0F;
0441 global_brightness = global_led.max_brightness;
0442
0443
0444
0445
0446
0447
0448
0449
0450 zone_dev_attrs =
0451 kcalloc(quirks->num_zones + 1, sizeof(struct device_attribute),
0452 GFP_KERNEL);
0453 if (!zone_dev_attrs)
0454 return -ENOMEM;
0455
0456 zone_attrs =
0457 kcalloc(quirks->num_zones + 2, sizeof(struct attribute *),
0458 GFP_KERNEL);
0459 if (!zone_attrs)
0460 return -ENOMEM;
0461
0462 zone_data =
0463 kcalloc(quirks->num_zones, sizeof(struct platform_zone),
0464 GFP_KERNEL);
0465 if (!zone_data)
0466 return -ENOMEM;
0467
0468 for (zone = 0; zone < quirks->num_zones; zone++) {
0469 sprintf(buffer, "zone%02hhX", zone);
0470 name = kstrdup(buffer, GFP_KERNEL);
0471 if (name == NULL)
0472 return 1;
0473 sysfs_attr_init(&zone_dev_attrs[zone].attr);
0474 zone_dev_attrs[zone].attr.name = name;
0475 zone_dev_attrs[zone].attr.mode = 0644;
0476 zone_dev_attrs[zone].show = zone_show;
0477 zone_dev_attrs[zone].store = zone_set;
0478 zone_data[zone].location = zone;
0479 zone_attrs[zone] = &zone_dev_attrs[zone].attr;
0480 zone_data[zone].attr = &zone_dev_attrs[zone];
0481 }
0482 zone_attrs[quirks->num_zones] = &dev_attr_lighting_control_state.attr;
0483 zone_attribute_group.attrs = zone_attrs;
0484
0485 led_classdev_register(&dev->dev, &global_led);
0486
0487 return sysfs_create_group(&dev->dev.kobj, &zone_attribute_group);
0488 }
0489
0490 static void alienware_zone_exit(struct platform_device *dev)
0491 {
0492 u8 zone;
0493
0494 sysfs_remove_group(&dev->dev.kobj, &zone_attribute_group);
0495 led_classdev_unregister(&global_led);
0496 if (zone_dev_attrs) {
0497 for (zone = 0; zone < quirks->num_zones; zone++)
0498 kfree(zone_dev_attrs[zone].attr.name);
0499 }
0500 kfree(zone_dev_attrs);
0501 kfree(zone_data);
0502 kfree(zone_attrs);
0503 }
0504
0505 static acpi_status alienware_wmax_command(struct wmax_basic_args *in_args,
0506 u32 command, int *out_data)
0507 {
0508 acpi_status status;
0509 union acpi_object *obj;
0510 struct acpi_buffer input;
0511 struct acpi_buffer output;
0512
0513 input.length = (acpi_size) sizeof(*in_args);
0514 input.pointer = in_args;
0515 if (out_data) {
0516 output.length = ACPI_ALLOCATE_BUFFER;
0517 output.pointer = NULL;
0518 status = wmi_evaluate_method(WMAX_CONTROL_GUID, 0,
0519 command, &input, &output);
0520 if (ACPI_SUCCESS(status)) {
0521 obj = (union acpi_object *)output.pointer;
0522 if (obj && obj->type == ACPI_TYPE_INTEGER)
0523 *out_data = (u32)obj->integer.value;
0524 }
0525 kfree(output.pointer);
0526 } else {
0527 status = wmi_evaluate_method(WMAX_CONTROL_GUID, 0,
0528 command, &input, NULL);
0529 }
0530 return status;
0531 }
0532
0533
0534
0535
0536
0537 static ssize_t show_hdmi_cable(struct device *dev,
0538 struct device_attribute *attr, char *buf)
0539 {
0540 acpi_status status;
0541 u32 out_data;
0542 struct wmax_basic_args in_args = {
0543 .arg = 0,
0544 };
0545 status =
0546 alienware_wmax_command(&in_args, WMAX_METHOD_HDMI_CABLE,
0547 (u32 *) &out_data);
0548 if (ACPI_SUCCESS(status)) {
0549 if (out_data == 0)
0550 return scnprintf(buf, PAGE_SIZE,
0551 "[unconnected] connected unknown\n");
0552 else if (out_data == 1)
0553 return scnprintf(buf, PAGE_SIZE,
0554 "unconnected [connected] unknown\n");
0555 }
0556 pr_err("alienware-wmi: unknown HDMI cable status: %d\n", status);
0557 return scnprintf(buf, PAGE_SIZE, "unconnected connected [unknown]\n");
0558 }
0559
0560 static ssize_t show_hdmi_source(struct device *dev,
0561 struct device_attribute *attr, char *buf)
0562 {
0563 acpi_status status;
0564 u32 out_data;
0565 struct wmax_basic_args in_args = {
0566 .arg = 0,
0567 };
0568 status =
0569 alienware_wmax_command(&in_args, WMAX_METHOD_HDMI_STATUS,
0570 (u32 *) &out_data);
0571
0572 if (ACPI_SUCCESS(status)) {
0573 if (out_data == 1)
0574 return scnprintf(buf, PAGE_SIZE,
0575 "[input] gpu unknown\n");
0576 else if (out_data == 2)
0577 return scnprintf(buf, PAGE_SIZE,
0578 "input [gpu] unknown\n");
0579 }
0580 pr_err("alienware-wmi: unknown HDMI source status: %u\n", status);
0581 return scnprintf(buf, PAGE_SIZE, "input gpu [unknown]\n");
0582 }
0583
0584 static ssize_t toggle_hdmi_source(struct device *dev,
0585 struct device_attribute *attr,
0586 const char *buf, size_t count)
0587 {
0588 acpi_status status;
0589 struct wmax_basic_args args;
0590 if (strcmp(buf, "gpu\n") == 0)
0591 args.arg = 1;
0592 else if (strcmp(buf, "input\n") == 0)
0593 args.arg = 2;
0594 else
0595 args.arg = 3;
0596 pr_debug("alienware-wmi: setting hdmi to %d : %s", args.arg, buf);
0597
0598 status = alienware_wmax_command(&args, WMAX_METHOD_HDMI_SOURCE, NULL);
0599
0600 if (ACPI_FAILURE(status))
0601 pr_err("alienware-wmi: HDMI toggle failed: results: %u\n",
0602 status);
0603 return count;
0604 }
0605
0606 static DEVICE_ATTR(cable, S_IRUGO, show_hdmi_cable, NULL);
0607 static DEVICE_ATTR(source, S_IRUGO | S_IWUSR, show_hdmi_source,
0608 toggle_hdmi_source);
0609
0610 static struct attribute *hdmi_attrs[] = {
0611 &dev_attr_cable.attr,
0612 &dev_attr_source.attr,
0613 NULL,
0614 };
0615
0616 static const struct attribute_group hdmi_attribute_group = {
0617 .name = "hdmi",
0618 .attrs = hdmi_attrs,
0619 };
0620
0621 static void remove_hdmi(struct platform_device *dev)
0622 {
0623 if (quirks->hdmi_mux > 0)
0624 sysfs_remove_group(&dev->dev.kobj, &hdmi_attribute_group);
0625 }
0626
0627 static int create_hdmi(struct platform_device *dev)
0628 {
0629 int ret;
0630
0631 ret = sysfs_create_group(&dev->dev.kobj, &hdmi_attribute_group);
0632 if (ret)
0633 remove_hdmi(dev);
0634 return ret;
0635 }
0636
0637
0638
0639
0640
0641
0642 static ssize_t show_amplifier_status(struct device *dev,
0643 struct device_attribute *attr, char *buf)
0644 {
0645 acpi_status status;
0646 u32 out_data;
0647 struct wmax_basic_args in_args = {
0648 .arg = 0,
0649 };
0650 status =
0651 alienware_wmax_command(&in_args, WMAX_METHOD_AMPLIFIER_CABLE,
0652 (u32 *) &out_data);
0653 if (ACPI_SUCCESS(status)) {
0654 if (out_data == 0)
0655 return scnprintf(buf, PAGE_SIZE,
0656 "[unconnected] connected unknown\n");
0657 else if (out_data == 1)
0658 return scnprintf(buf, PAGE_SIZE,
0659 "unconnected [connected] unknown\n");
0660 }
0661 pr_err("alienware-wmi: unknown amplifier cable status: %d\n", status);
0662 return scnprintf(buf, PAGE_SIZE, "unconnected connected [unknown]\n");
0663 }
0664
0665 static DEVICE_ATTR(status, S_IRUGO, show_amplifier_status, NULL);
0666
0667 static struct attribute *amplifier_attrs[] = {
0668 &dev_attr_status.attr,
0669 NULL,
0670 };
0671
0672 static const struct attribute_group amplifier_attribute_group = {
0673 .name = "amplifier",
0674 .attrs = amplifier_attrs,
0675 };
0676
0677 static void remove_amplifier(struct platform_device *dev)
0678 {
0679 if (quirks->amplifier > 0)
0680 sysfs_remove_group(&dev->dev.kobj, &lifier_attribute_group);
0681 }
0682
0683 static int create_amplifier(struct platform_device *dev)
0684 {
0685 int ret;
0686
0687 ret = sysfs_create_group(&dev->dev.kobj, &lifier_attribute_group);
0688 if (ret)
0689 remove_amplifier(dev);
0690 return ret;
0691 }
0692
0693
0694
0695
0696
0697 static ssize_t show_deepsleep_status(struct device *dev,
0698 struct device_attribute *attr, char *buf)
0699 {
0700 acpi_status status;
0701 u32 out_data;
0702 struct wmax_basic_args in_args = {
0703 .arg = 0,
0704 };
0705 status = alienware_wmax_command(&in_args, WMAX_METHOD_DEEP_SLEEP_STATUS,
0706 (u32 *) &out_data);
0707 if (ACPI_SUCCESS(status)) {
0708 if (out_data == 0)
0709 return scnprintf(buf, PAGE_SIZE,
0710 "[disabled] s5 s5_s4\n");
0711 else if (out_data == 1)
0712 return scnprintf(buf, PAGE_SIZE,
0713 "disabled [s5] s5_s4\n");
0714 else if (out_data == 2)
0715 return scnprintf(buf, PAGE_SIZE,
0716 "disabled s5 [s5_s4]\n");
0717 }
0718 pr_err("alienware-wmi: unknown deep sleep status: %d\n", status);
0719 return scnprintf(buf, PAGE_SIZE, "disabled s5 s5_s4 [unknown]\n");
0720 }
0721
0722 static ssize_t toggle_deepsleep(struct device *dev,
0723 struct device_attribute *attr,
0724 const char *buf, size_t count)
0725 {
0726 acpi_status status;
0727 struct wmax_basic_args args;
0728
0729 if (strcmp(buf, "disabled\n") == 0)
0730 args.arg = 0;
0731 else if (strcmp(buf, "s5\n") == 0)
0732 args.arg = 1;
0733 else
0734 args.arg = 2;
0735 pr_debug("alienware-wmi: setting deep sleep to %d : %s", args.arg, buf);
0736
0737 status = alienware_wmax_command(&args, WMAX_METHOD_DEEP_SLEEP_CONTROL,
0738 NULL);
0739
0740 if (ACPI_FAILURE(status))
0741 pr_err("alienware-wmi: deep sleep control failed: results: %u\n",
0742 status);
0743 return count;
0744 }
0745
0746 static DEVICE_ATTR(deepsleep, S_IRUGO | S_IWUSR, show_deepsleep_status, toggle_deepsleep);
0747
0748 static struct attribute *deepsleep_attrs[] = {
0749 &dev_attr_deepsleep.attr,
0750 NULL,
0751 };
0752
0753 static const struct attribute_group deepsleep_attribute_group = {
0754 .name = "deepsleep",
0755 .attrs = deepsleep_attrs,
0756 };
0757
0758 static void remove_deepsleep(struct platform_device *dev)
0759 {
0760 if (quirks->deepslp > 0)
0761 sysfs_remove_group(&dev->dev.kobj, &deepsleep_attribute_group);
0762 }
0763
0764 static int create_deepsleep(struct platform_device *dev)
0765 {
0766 int ret;
0767
0768 ret = sysfs_create_group(&dev->dev.kobj, &deepsleep_attribute_group);
0769 if (ret)
0770 remove_deepsleep(dev);
0771 return ret;
0772 }
0773
0774 static int __init alienware_wmi_init(void)
0775 {
0776 int ret;
0777
0778 if (wmi_has_guid(LEGACY_CONTROL_GUID))
0779 interface = LEGACY;
0780 else if (wmi_has_guid(WMAX_CONTROL_GUID))
0781 interface = WMAX;
0782 else {
0783 pr_warn("alienware-wmi: No known WMI GUID found\n");
0784 return -ENODEV;
0785 }
0786
0787 dmi_check_system(alienware_quirks);
0788 if (quirks == NULL)
0789 quirks = &quirk_unknown;
0790
0791 ret = platform_driver_register(&platform_driver);
0792 if (ret)
0793 goto fail_platform_driver;
0794 platform_device = platform_device_alloc("alienware-wmi", -1);
0795 if (!platform_device) {
0796 ret = -ENOMEM;
0797 goto fail_platform_device1;
0798 }
0799 ret = platform_device_add(platform_device);
0800 if (ret)
0801 goto fail_platform_device2;
0802
0803 if (quirks->hdmi_mux > 0) {
0804 ret = create_hdmi(platform_device);
0805 if (ret)
0806 goto fail_prep_hdmi;
0807 }
0808
0809 if (quirks->amplifier > 0) {
0810 ret = create_amplifier(platform_device);
0811 if (ret)
0812 goto fail_prep_amplifier;
0813 }
0814
0815 if (quirks->deepslp > 0) {
0816 ret = create_deepsleep(platform_device);
0817 if (ret)
0818 goto fail_prep_deepsleep;
0819 }
0820
0821 ret = alienware_zone_init(platform_device);
0822 if (ret)
0823 goto fail_prep_zones;
0824
0825 return 0;
0826
0827 fail_prep_zones:
0828 alienware_zone_exit(platform_device);
0829 fail_prep_deepsleep:
0830 fail_prep_amplifier:
0831 fail_prep_hdmi:
0832 platform_device_del(platform_device);
0833 fail_platform_device2:
0834 platform_device_put(platform_device);
0835 fail_platform_device1:
0836 platform_driver_unregister(&platform_driver);
0837 fail_platform_driver:
0838 return ret;
0839 }
0840
0841 module_init(alienware_wmi_init);
0842
0843 static void __exit alienware_wmi_exit(void)
0844 {
0845 if (platform_device) {
0846 alienware_zone_exit(platform_device);
0847 remove_hdmi(platform_device);
0848 platform_device_unregister(platform_device);
0849 platform_driver_unregister(&platform_driver);
0850 }
0851 }
0852
0853 module_exit(alienware_wmi_exit);