.. | .. |
---|
13 | 13 | * any later version. |
---|
14 | 14 | */ |
---|
15 | 15 | |
---|
| 16 | +#include <linux/acpi.h> |
---|
16 | 17 | #include <linux/hid.h> |
---|
17 | 18 | #include <linux/leds.h> |
---|
18 | 19 | #include <linux/module.h> |
---|
| 20 | +#include <linux/platform_data/cros_ec_commands.h> |
---|
| 21 | +#include <linux/platform_data/cros_ec_proto.h> |
---|
| 22 | +#include <linux/platform_device.h> |
---|
| 23 | +#include <linux/pm_wakeup.h> |
---|
| 24 | +#include <asm/unaligned.h> |
---|
19 | 25 | |
---|
20 | 26 | #include "hid-ids.h" |
---|
21 | 27 | |
---|
22 | | -#define MAX_BRIGHTNESS 100 |
---|
| 28 | +/* |
---|
| 29 | + * C(hrome)B(ase)A(ttached)S(witch) - switch exported by Chrome EC and reporting |
---|
| 30 | + * state of the "Whiskers" base - attached or detached. Whiskers USB device also |
---|
| 31 | + * reports position of the keyboard - folded or not. Combining base state and |
---|
| 32 | + * position allows us to generate proper "Tablet mode" events. |
---|
| 33 | + */ |
---|
| 34 | +struct cbas_ec { |
---|
| 35 | + struct device *dev; /* The platform device (EC) */ |
---|
| 36 | + struct input_dev *input; |
---|
| 37 | + bool base_present; |
---|
| 38 | + bool base_folded; |
---|
| 39 | + struct notifier_block notifier; |
---|
| 40 | +}; |
---|
23 | 41 | |
---|
24 | | -/* HID usage for keyboard backlight (Alphanumeric display brightness) */ |
---|
25 | | -#define HID_AD_BRIGHTNESS 0x00140046 |
---|
| 42 | +static struct cbas_ec cbas_ec; |
---|
| 43 | +static DEFINE_SPINLOCK(cbas_ec_lock); |
---|
| 44 | +static DEFINE_MUTEX(cbas_ec_reglock); |
---|
| 45 | + |
---|
| 46 | +static bool cbas_parse_base_state(const void *data) |
---|
| 47 | +{ |
---|
| 48 | + u32 switches = get_unaligned_le32(data); |
---|
| 49 | + |
---|
| 50 | + return !!(switches & BIT(EC_MKBP_BASE_ATTACHED)); |
---|
| 51 | +} |
---|
| 52 | + |
---|
| 53 | +static int cbas_ec_query_base(struct cros_ec_device *ec_dev, bool get_state, |
---|
| 54 | + bool *state) |
---|
| 55 | +{ |
---|
| 56 | + struct ec_params_mkbp_info *params; |
---|
| 57 | + struct cros_ec_command *msg; |
---|
| 58 | + int ret; |
---|
| 59 | + |
---|
| 60 | + msg = kzalloc(sizeof(*msg) + max(sizeof(u32), sizeof(*params)), |
---|
| 61 | + GFP_KERNEL); |
---|
| 62 | + if (!msg) |
---|
| 63 | + return -ENOMEM; |
---|
| 64 | + |
---|
| 65 | + msg->command = EC_CMD_MKBP_INFO; |
---|
| 66 | + msg->version = 1; |
---|
| 67 | + msg->outsize = sizeof(*params); |
---|
| 68 | + msg->insize = sizeof(u32); |
---|
| 69 | + params = (struct ec_params_mkbp_info *)msg->data; |
---|
| 70 | + params->info_type = get_state ? |
---|
| 71 | + EC_MKBP_INFO_CURRENT : EC_MKBP_INFO_SUPPORTED; |
---|
| 72 | + params->event_type = EC_MKBP_EVENT_SWITCH; |
---|
| 73 | + |
---|
| 74 | + ret = cros_ec_cmd_xfer_status(ec_dev, msg); |
---|
| 75 | + if (ret >= 0) { |
---|
| 76 | + if (ret != sizeof(u32)) { |
---|
| 77 | + dev_warn(ec_dev->dev, "wrong result size: %d != %zu\n", |
---|
| 78 | + ret, sizeof(u32)); |
---|
| 79 | + ret = -EPROTO; |
---|
| 80 | + } else { |
---|
| 81 | + *state = cbas_parse_base_state(msg->data); |
---|
| 82 | + ret = 0; |
---|
| 83 | + } |
---|
| 84 | + } |
---|
| 85 | + |
---|
| 86 | + kfree(msg); |
---|
| 87 | + |
---|
| 88 | + return ret; |
---|
| 89 | +} |
---|
| 90 | + |
---|
| 91 | +static int cbas_ec_notify(struct notifier_block *nb, |
---|
| 92 | + unsigned long queued_during_suspend, |
---|
| 93 | + void *_notify) |
---|
| 94 | +{ |
---|
| 95 | + struct cros_ec_device *ec = _notify; |
---|
| 96 | + unsigned long flags; |
---|
| 97 | + bool base_present; |
---|
| 98 | + |
---|
| 99 | + if (ec->event_data.event_type == EC_MKBP_EVENT_SWITCH) { |
---|
| 100 | + base_present = cbas_parse_base_state( |
---|
| 101 | + &ec->event_data.data.switches); |
---|
| 102 | + dev_dbg(cbas_ec.dev, |
---|
| 103 | + "%s: base: %d\n", __func__, base_present); |
---|
| 104 | + |
---|
| 105 | + if (device_may_wakeup(cbas_ec.dev) || |
---|
| 106 | + !queued_during_suspend) { |
---|
| 107 | + |
---|
| 108 | + pm_wakeup_event(cbas_ec.dev, 0); |
---|
| 109 | + |
---|
| 110 | + spin_lock_irqsave(&cbas_ec_lock, flags); |
---|
| 111 | + |
---|
| 112 | + /* |
---|
| 113 | + * While input layer dedupes the events, we do not want |
---|
| 114 | + * to disrupt the state reported by the base by |
---|
| 115 | + * overriding it with state reported by the LID. Only |
---|
| 116 | + * report changes, as we assume that on attach the base |
---|
| 117 | + * is not folded. |
---|
| 118 | + */ |
---|
| 119 | + if (base_present != cbas_ec.base_present) { |
---|
| 120 | + input_report_switch(cbas_ec.input, |
---|
| 121 | + SW_TABLET_MODE, |
---|
| 122 | + !base_present); |
---|
| 123 | + input_sync(cbas_ec.input); |
---|
| 124 | + cbas_ec.base_present = base_present; |
---|
| 125 | + } |
---|
| 126 | + |
---|
| 127 | + spin_unlock_irqrestore(&cbas_ec_lock, flags); |
---|
| 128 | + } |
---|
| 129 | + } |
---|
| 130 | + |
---|
| 131 | + return NOTIFY_OK; |
---|
| 132 | +} |
---|
| 133 | + |
---|
| 134 | +static __maybe_unused int cbas_ec_resume(struct device *dev) |
---|
| 135 | +{ |
---|
| 136 | + struct cros_ec_device *ec = dev_get_drvdata(dev->parent); |
---|
| 137 | + bool base_present; |
---|
| 138 | + int error; |
---|
| 139 | + |
---|
| 140 | + error = cbas_ec_query_base(ec, true, &base_present); |
---|
| 141 | + if (error) { |
---|
| 142 | + dev_warn(dev, "failed to fetch base state on resume: %d\n", |
---|
| 143 | + error); |
---|
| 144 | + } else { |
---|
| 145 | + spin_lock_irq(&cbas_ec_lock); |
---|
| 146 | + |
---|
| 147 | + cbas_ec.base_present = base_present; |
---|
| 148 | + |
---|
| 149 | + /* |
---|
| 150 | + * Only report if base is disconnected. If base is connected, |
---|
| 151 | + * it will resend its state on resume, and we'll update it |
---|
| 152 | + * in hammer_event(). |
---|
| 153 | + */ |
---|
| 154 | + if (!cbas_ec.base_present) { |
---|
| 155 | + input_report_switch(cbas_ec.input, SW_TABLET_MODE, 1); |
---|
| 156 | + input_sync(cbas_ec.input); |
---|
| 157 | + } |
---|
| 158 | + |
---|
| 159 | + spin_unlock_irq(&cbas_ec_lock); |
---|
| 160 | + } |
---|
| 161 | + |
---|
| 162 | + return 0; |
---|
| 163 | +} |
---|
| 164 | + |
---|
| 165 | +static SIMPLE_DEV_PM_OPS(cbas_ec_pm_ops, NULL, cbas_ec_resume); |
---|
| 166 | + |
---|
| 167 | +static void cbas_ec_set_input(struct input_dev *input) |
---|
| 168 | +{ |
---|
| 169 | + /* Take the lock so hammer_event() does not race with us here */ |
---|
| 170 | + spin_lock_irq(&cbas_ec_lock); |
---|
| 171 | + cbas_ec.input = input; |
---|
| 172 | + spin_unlock_irq(&cbas_ec_lock); |
---|
| 173 | +} |
---|
| 174 | + |
---|
| 175 | +static int __cbas_ec_probe(struct platform_device *pdev) |
---|
| 176 | +{ |
---|
| 177 | + struct cros_ec_device *ec = dev_get_drvdata(pdev->dev.parent); |
---|
| 178 | + struct input_dev *input; |
---|
| 179 | + bool base_supported; |
---|
| 180 | + int error; |
---|
| 181 | + |
---|
| 182 | + error = cbas_ec_query_base(ec, false, &base_supported); |
---|
| 183 | + if (error) |
---|
| 184 | + return error; |
---|
| 185 | + |
---|
| 186 | + if (!base_supported) |
---|
| 187 | + return -ENXIO; |
---|
| 188 | + |
---|
| 189 | + input = devm_input_allocate_device(&pdev->dev); |
---|
| 190 | + if (!input) |
---|
| 191 | + return -ENOMEM; |
---|
| 192 | + |
---|
| 193 | + input->name = "Whiskers Tablet Mode Switch"; |
---|
| 194 | + input->id.bustype = BUS_HOST; |
---|
| 195 | + |
---|
| 196 | + input_set_capability(input, EV_SW, SW_TABLET_MODE); |
---|
| 197 | + |
---|
| 198 | + error = input_register_device(input); |
---|
| 199 | + if (error) { |
---|
| 200 | + dev_err(&pdev->dev, "cannot register input device: %d\n", |
---|
| 201 | + error); |
---|
| 202 | + return error; |
---|
| 203 | + } |
---|
| 204 | + |
---|
| 205 | + /* Seed the state */ |
---|
| 206 | + error = cbas_ec_query_base(ec, true, &cbas_ec.base_present); |
---|
| 207 | + if (error) { |
---|
| 208 | + dev_err(&pdev->dev, "cannot query base state: %d\n", error); |
---|
| 209 | + return error; |
---|
| 210 | + } |
---|
| 211 | + |
---|
| 212 | + if (!cbas_ec.base_present) |
---|
| 213 | + cbas_ec.base_folded = false; |
---|
| 214 | + |
---|
| 215 | + dev_dbg(&pdev->dev, "%s: base: %d, folded: %d\n", __func__, |
---|
| 216 | + cbas_ec.base_present, cbas_ec.base_folded); |
---|
| 217 | + |
---|
| 218 | + input_report_switch(input, SW_TABLET_MODE, |
---|
| 219 | + !cbas_ec.base_present || cbas_ec.base_folded); |
---|
| 220 | + |
---|
| 221 | + cbas_ec_set_input(input); |
---|
| 222 | + |
---|
| 223 | + cbas_ec.dev = &pdev->dev; |
---|
| 224 | + cbas_ec.notifier.notifier_call = cbas_ec_notify; |
---|
| 225 | + error = blocking_notifier_chain_register(&ec->event_notifier, |
---|
| 226 | + &cbas_ec.notifier); |
---|
| 227 | + if (error) { |
---|
| 228 | + dev_err(&pdev->dev, "cannot register notifier: %d\n", error); |
---|
| 229 | + cbas_ec_set_input(NULL); |
---|
| 230 | + return error; |
---|
| 231 | + } |
---|
| 232 | + |
---|
| 233 | + device_init_wakeup(&pdev->dev, true); |
---|
| 234 | + return 0; |
---|
| 235 | +} |
---|
| 236 | + |
---|
| 237 | +static int cbas_ec_probe(struct platform_device *pdev) |
---|
| 238 | +{ |
---|
| 239 | + int retval; |
---|
| 240 | + |
---|
| 241 | + mutex_lock(&cbas_ec_reglock); |
---|
| 242 | + |
---|
| 243 | + if (cbas_ec.input) { |
---|
| 244 | + retval = -EBUSY; |
---|
| 245 | + goto out; |
---|
| 246 | + } |
---|
| 247 | + |
---|
| 248 | + retval = __cbas_ec_probe(pdev); |
---|
| 249 | + |
---|
| 250 | +out: |
---|
| 251 | + mutex_unlock(&cbas_ec_reglock); |
---|
| 252 | + return retval; |
---|
| 253 | +} |
---|
| 254 | + |
---|
| 255 | +static int cbas_ec_remove(struct platform_device *pdev) |
---|
| 256 | +{ |
---|
| 257 | + struct cros_ec_device *ec = dev_get_drvdata(pdev->dev.parent); |
---|
| 258 | + |
---|
| 259 | + mutex_lock(&cbas_ec_reglock); |
---|
| 260 | + |
---|
| 261 | + blocking_notifier_chain_unregister(&ec->event_notifier, |
---|
| 262 | + &cbas_ec.notifier); |
---|
| 263 | + cbas_ec_set_input(NULL); |
---|
| 264 | + |
---|
| 265 | + mutex_unlock(&cbas_ec_reglock); |
---|
| 266 | + return 0; |
---|
| 267 | +} |
---|
| 268 | + |
---|
| 269 | +static const struct acpi_device_id cbas_ec_acpi_ids[] = { |
---|
| 270 | + { "GOOG000B", 0 }, |
---|
| 271 | + { } |
---|
| 272 | +}; |
---|
| 273 | +MODULE_DEVICE_TABLE(acpi, cbas_ec_acpi_ids); |
---|
| 274 | + |
---|
| 275 | +static struct platform_driver cbas_ec_driver = { |
---|
| 276 | + .probe = cbas_ec_probe, |
---|
| 277 | + .remove = cbas_ec_remove, |
---|
| 278 | + .driver = { |
---|
| 279 | + .name = "cbas_ec", |
---|
| 280 | + .acpi_match_table = ACPI_PTR(cbas_ec_acpi_ids), |
---|
| 281 | + .pm = &cbas_ec_pm_ops, |
---|
| 282 | + }, |
---|
| 283 | +}; |
---|
| 284 | + |
---|
| 285 | +#define MAX_BRIGHTNESS 100 |
---|
26 | 286 | |
---|
27 | 287 | struct hammer_kbd_leds { |
---|
28 | 288 | struct led_classdev cdev; |
---|
.. | .. |
---|
70 | 330 | static int hammer_register_leds(struct hid_device *hdev) |
---|
71 | 331 | { |
---|
72 | 332 | struct hammer_kbd_leds *kbd_backlight; |
---|
| 333 | + int error; |
---|
73 | 334 | |
---|
74 | | - kbd_backlight = devm_kzalloc(&hdev->dev, |
---|
75 | | - sizeof(*kbd_backlight), |
---|
76 | | - GFP_KERNEL); |
---|
| 335 | + kbd_backlight = kzalloc(sizeof(*kbd_backlight), GFP_KERNEL); |
---|
77 | 336 | if (!kbd_backlight) |
---|
78 | 337 | return -ENOMEM; |
---|
79 | 338 | |
---|
.. | .. |
---|
87 | 346 | /* Set backlight to 0% initially. */ |
---|
88 | 347 | hammer_kbd_brightness_set_blocking(&kbd_backlight->cdev, 0); |
---|
89 | 348 | |
---|
90 | | - return devm_led_classdev_register(&hdev->dev, &kbd_backlight->cdev); |
---|
| 349 | + error = led_classdev_register(&hdev->dev, &kbd_backlight->cdev); |
---|
| 350 | + if (error) |
---|
| 351 | + goto err_free_mem; |
---|
| 352 | + |
---|
| 353 | + hid_set_drvdata(hdev, kbd_backlight); |
---|
| 354 | + return 0; |
---|
| 355 | + |
---|
| 356 | +err_free_mem: |
---|
| 357 | + kfree(kbd_backlight); |
---|
| 358 | + return error; |
---|
91 | 359 | } |
---|
92 | 360 | |
---|
93 | | -static int hammer_input_configured(struct hid_device *hdev, |
---|
94 | | - struct hid_input *hi) |
---|
| 361 | +static void hammer_unregister_leds(struct hid_device *hdev) |
---|
95 | 362 | { |
---|
96 | | - struct list_head *report_list = |
---|
97 | | - &hdev->report_enum[HID_OUTPUT_REPORT].report_list; |
---|
98 | | - struct hid_report *report; |
---|
| 363 | + struct hammer_kbd_leds *kbd_backlight = hid_get_drvdata(hdev); |
---|
99 | 364 | |
---|
100 | | - if (list_empty(report_list)) |
---|
101 | | - return 0; |
---|
| 365 | + if (kbd_backlight) { |
---|
| 366 | + led_classdev_unregister(&kbd_backlight->cdev); |
---|
| 367 | + kfree(kbd_backlight); |
---|
| 368 | + } |
---|
| 369 | +} |
---|
102 | 370 | |
---|
103 | | - report = list_first_entry(report_list, struct hid_report, list); |
---|
| 371 | +#define HID_UP_GOOGLEVENDOR 0xffd10000 |
---|
| 372 | +#define HID_VD_KBD_FOLDED 0x00000019 |
---|
| 373 | +#define HID_USAGE_KBD_FOLDED (HID_UP_GOOGLEVENDOR | HID_VD_KBD_FOLDED) |
---|
104 | 374 | |
---|
105 | | - if (report->maxfield == 1 && |
---|
106 | | - report->field[0]->application == HID_GD_KEYBOARD && |
---|
107 | | - report->field[0]->maxusage == 1 && |
---|
108 | | - report->field[0]->usage[0].hid == HID_AD_BRIGHTNESS) { |
---|
109 | | - int err = hammer_register_leds(hdev); |
---|
| 375 | +/* HID usage for keyboard backlight (Alphanumeric display brightness) */ |
---|
| 376 | +#define HID_AD_BRIGHTNESS 0x00140046 |
---|
110 | 377 | |
---|
111 | | - if (err) |
---|
112 | | - hid_warn(hdev, |
---|
113 | | - "Failed to register keyboard backlight: %d\n", |
---|
114 | | - err); |
---|
| 378 | +static int hammer_input_mapping(struct hid_device *hdev, struct hid_input *hi, |
---|
| 379 | + struct hid_field *field, |
---|
| 380 | + struct hid_usage *usage, |
---|
| 381 | + unsigned long **bit, int *max) |
---|
| 382 | +{ |
---|
| 383 | + if (usage->hid == HID_USAGE_KBD_FOLDED) { |
---|
| 384 | + /* |
---|
| 385 | + * We do not want to have this usage mapped as it will get |
---|
| 386 | + * mixed in with "base attached" signal and delivered over |
---|
| 387 | + * separate input device for tablet switch mode. |
---|
| 388 | + */ |
---|
| 389 | + return -1; |
---|
115 | 390 | } |
---|
116 | 391 | |
---|
117 | 392 | return 0; |
---|
| 393 | +} |
---|
| 394 | + |
---|
| 395 | +static int hammer_event(struct hid_device *hid, struct hid_field *field, |
---|
| 396 | + struct hid_usage *usage, __s32 value) |
---|
| 397 | +{ |
---|
| 398 | + unsigned long flags; |
---|
| 399 | + |
---|
| 400 | + if (usage->hid == HID_USAGE_KBD_FOLDED) { |
---|
| 401 | + spin_lock_irqsave(&cbas_ec_lock, flags); |
---|
| 402 | + |
---|
| 403 | + /* |
---|
| 404 | + * If we are getting events from Whiskers that means that it |
---|
| 405 | + * is attached to the lid. |
---|
| 406 | + */ |
---|
| 407 | + cbas_ec.base_present = true; |
---|
| 408 | + cbas_ec.base_folded = value; |
---|
| 409 | + hid_dbg(hid, "%s: base: %d, folded: %d\n", __func__, |
---|
| 410 | + cbas_ec.base_present, cbas_ec.base_folded); |
---|
| 411 | + |
---|
| 412 | + if (cbas_ec.input) { |
---|
| 413 | + input_report_switch(cbas_ec.input, |
---|
| 414 | + SW_TABLET_MODE, value); |
---|
| 415 | + input_sync(cbas_ec.input); |
---|
| 416 | + } |
---|
| 417 | + |
---|
| 418 | + spin_unlock_irqrestore(&cbas_ec_lock, flags); |
---|
| 419 | + return 1; /* We handled this event */ |
---|
| 420 | + } |
---|
| 421 | + |
---|
| 422 | + return 0; |
---|
| 423 | +} |
---|
| 424 | + |
---|
| 425 | +static bool hammer_has_usage(struct hid_device *hdev, unsigned int report_type, |
---|
| 426 | + unsigned application, unsigned usage) |
---|
| 427 | +{ |
---|
| 428 | + struct hid_report_enum *re = &hdev->report_enum[report_type]; |
---|
| 429 | + struct hid_report *report; |
---|
| 430 | + int i, j; |
---|
| 431 | + |
---|
| 432 | + list_for_each_entry(report, &re->report_list, list) { |
---|
| 433 | + if (report->application != application) |
---|
| 434 | + continue; |
---|
| 435 | + |
---|
| 436 | + for (i = 0; i < report->maxfield; i++) { |
---|
| 437 | + struct hid_field *field = report->field[i]; |
---|
| 438 | + |
---|
| 439 | + for (j = 0; j < field->maxusage; j++) |
---|
| 440 | + if (field->usage[j].hid == usage) |
---|
| 441 | + return true; |
---|
| 442 | + } |
---|
| 443 | + } |
---|
| 444 | + |
---|
| 445 | + return false; |
---|
| 446 | +} |
---|
| 447 | + |
---|
| 448 | +static bool hammer_has_folded_event(struct hid_device *hdev) |
---|
| 449 | +{ |
---|
| 450 | + return hammer_has_usage(hdev, HID_INPUT_REPORT, |
---|
| 451 | + HID_GD_KEYBOARD, HID_USAGE_KBD_FOLDED); |
---|
| 452 | +} |
---|
| 453 | + |
---|
| 454 | +static bool hammer_has_backlight_control(struct hid_device *hdev) |
---|
| 455 | +{ |
---|
| 456 | + return hammer_has_usage(hdev, HID_OUTPUT_REPORT, |
---|
| 457 | + HID_GD_KEYBOARD, HID_AD_BRIGHTNESS); |
---|
| 458 | +} |
---|
| 459 | + |
---|
| 460 | +static int hammer_probe(struct hid_device *hdev, |
---|
| 461 | + const struct hid_device_id *id) |
---|
| 462 | +{ |
---|
| 463 | + int error; |
---|
| 464 | + |
---|
| 465 | + error = hid_parse(hdev); |
---|
| 466 | + if (error) |
---|
| 467 | + return error; |
---|
| 468 | + |
---|
| 469 | + error = hid_hw_start(hdev, HID_CONNECT_DEFAULT); |
---|
| 470 | + if (error) |
---|
| 471 | + return error; |
---|
| 472 | + |
---|
| 473 | + /* |
---|
| 474 | + * We always want to poll for, and handle tablet mode events from |
---|
| 475 | + * devices that have folded usage, even when nobody has opened the input |
---|
| 476 | + * device. This also prevents the hid core from dropping early tablet |
---|
| 477 | + * mode events from the device. |
---|
| 478 | + */ |
---|
| 479 | + if (hammer_has_folded_event(hdev)) { |
---|
| 480 | + hdev->quirks |= HID_QUIRK_ALWAYS_POLL; |
---|
| 481 | + error = hid_hw_open(hdev); |
---|
| 482 | + if (error) |
---|
| 483 | + return error; |
---|
| 484 | + } |
---|
| 485 | + |
---|
| 486 | + if (hammer_has_backlight_control(hdev)) { |
---|
| 487 | + error = hammer_register_leds(hdev); |
---|
| 488 | + if (error) |
---|
| 489 | + hid_warn(hdev, |
---|
| 490 | + "Failed to register keyboard backlight: %d\n", |
---|
| 491 | + error); |
---|
| 492 | + } |
---|
| 493 | + |
---|
| 494 | + return 0; |
---|
| 495 | +} |
---|
| 496 | + |
---|
| 497 | +static void hammer_remove(struct hid_device *hdev) |
---|
| 498 | +{ |
---|
| 499 | + unsigned long flags; |
---|
| 500 | + |
---|
| 501 | + if (hammer_has_folded_event(hdev)) { |
---|
| 502 | + hid_hw_close(hdev); |
---|
| 503 | + |
---|
| 504 | + /* |
---|
| 505 | + * If we are disconnecting then most likely Whiskers is |
---|
| 506 | + * being removed. Even if it is not removed, without proper |
---|
| 507 | + * keyboard we should not stay in clamshell mode. |
---|
| 508 | + * |
---|
| 509 | + * The reason for doing it here and not waiting for signal |
---|
| 510 | + * from EC, is that on some devices there are high leakage |
---|
| 511 | + * on Whiskers pins and we do not detect disconnect reliably, |
---|
| 512 | + * resulting in devices being stuck in clamshell mode. |
---|
| 513 | + */ |
---|
| 514 | + spin_lock_irqsave(&cbas_ec_lock, flags); |
---|
| 515 | + if (cbas_ec.input && cbas_ec.base_present) { |
---|
| 516 | + input_report_switch(cbas_ec.input, SW_TABLET_MODE, 1); |
---|
| 517 | + input_sync(cbas_ec.input); |
---|
| 518 | + } |
---|
| 519 | + cbas_ec.base_present = false; |
---|
| 520 | + spin_unlock_irqrestore(&cbas_ec_lock, flags); |
---|
| 521 | + } |
---|
| 522 | + |
---|
| 523 | + hammer_unregister_leds(hdev); |
---|
| 524 | + |
---|
| 525 | + hid_hw_stop(hdev); |
---|
118 | 526 | } |
---|
119 | 527 | |
---|
120 | 528 | static const struct hid_device_id hammer_devices[] = { |
---|
.. | .. |
---|
143 | 551 | static struct hid_driver hammer_driver = { |
---|
144 | 552 | .name = "hammer", |
---|
145 | 553 | .id_table = hammer_devices, |
---|
146 | | - .input_configured = hammer_input_configured, |
---|
| 554 | + .probe = hammer_probe, |
---|
| 555 | + .remove = hammer_remove, |
---|
| 556 | + .input_mapping = hammer_input_mapping, |
---|
| 557 | + .event = hammer_event, |
---|
147 | 558 | }; |
---|
148 | | -module_hid_driver(hammer_driver); |
---|
| 559 | + |
---|
| 560 | +static int __init hammer_init(void) |
---|
| 561 | +{ |
---|
| 562 | + int error; |
---|
| 563 | + |
---|
| 564 | + error = platform_driver_register(&cbas_ec_driver); |
---|
| 565 | + if (error) |
---|
| 566 | + return error; |
---|
| 567 | + |
---|
| 568 | + error = hid_register_driver(&hammer_driver); |
---|
| 569 | + if (error) { |
---|
| 570 | + platform_driver_unregister(&cbas_ec_driver); |
---|
| 571 | + return error; |
---|
| 572 | + } |
---|
| 573 | + |
---|
| 574 | + return 0; |
---|
| 575 | +} |
---|
| 576 | +module_init(hammer_init); |
---|
| 577 | + |
---|
| 578 | +static void __exit hammer_exit(void) |
---|
| 579 | +{ |
---|
| 580 | + hid_unregister_driver(&hammer_driver); |
---|
| 581 | + platform_driver_unregister(&cbas_ec_driver); |
---|
| 582 | +} |
---|
| 583 | +module_exit(hammer_exit); |
---|
149 | 584 | |
---|
150 | 585 | MODULE_LICENSE("GPL"); |
---|