linux/drivers/hid/hid-picolcd_core.c
<<
>>
Prefs
   1/***************************************************************************
   2 *   Copyright (C) 2010-2012 by Bruno Prémont <bonbons@linux-vserver.org>  *
   3 *                                                                         *
   4 *   Based on Logitech G13 driver (v0.4)                                   *
   5 *     Copyright (C) 2009 by Rick L. Vinyard, Jr. <rvinyard@cs.nmsu.edu>   *
   6 *                                                                         *
   7 *   This program is free software: you can redistribute it and/or modify  *
   8 *   it under the terms of the GNU General Public License as published by  *
   9 *   the Free Software Foundation, version 2 of the License.               *
  10 *                                                                         *
  11 *   This driver is distributed in the hope that it will be useful, but    *
  12 *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
  13 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU      *
  14 *   General Public License for more details.                              *
  15 *                                                                         *
  16 *   You should have received a copy of the GNU General Public License     *
  17 *   along with this software. If not see <http://www.gnu.org/licenses/>.  *
  18 ***************************************************************************/
  19
  20#include <linux/hid.h>
  21#include <linux/hid-debug.h>
  22#include <linux/input.h>
  23#include "hid-ids.h"
  24
  25#include <linux/fb.h>
  26#include <linux/vmalloc.h>
  27
  28#include <linux/completion.h>
  29#include <linux/uaccess.h>
  30#include <linux/module.h>
  31
  32#include "hid-picolcd.h"
  33
  34
  35/* Input device
  36 *
  37 * The PicoLCD has an IR receiver header, a built-in keypad with 5 keys
  38 * and header for 4x4 key matrix. The built-in keys are part of the matrix.
  39 */
  40static const unsigned short def_keymap[PICOLCD_KEYS] = {
  41        KEY_RESERVED,   /* none */
  42        KEY_BACK,       /* col 4 + row 1 */
  43        KEY_HOMEPAGE,   /* col 3 + row 1 */
  44        KEY_RESERVED,   /* col 2 + row 1 */
  45        KEY_RESERVED,   /* col 1 + row 1 */
  46        KEY_SCROLLUP,   /* col 4 + row 2 */
  47        KEY_OK,         /* col 3 + row 2 */
  48        KEY_SCROLLDOWN, /* col 2 + row 2 */
  49        KEY_RESERVED,   /* col 1 + row 2 */
  50        KEY_RESERVED,   /* col 4 + row 3 */
  51        KEY_RESERVED,   /* col 3 + row 3 */
  52        KEY_RESERVED,   /* col 2 + row 3 */
  53        KEY_RESERVED,   /* col 1 + row 3 */
  54        KEY_RESERVED,   /* col 4 + row 4 */
  55        KEY_RESERVED,   /* col 3 + row 4 */
  56        KEY_RESERVED,   /* col 2 + row 4 */
  57        KEY_RESERVED,   /* col 1 + row 4 */
  58};
  59
  60
  61/* Find a given report */
  62struct hid_report *picolcd_report(int id, struct hid_device *hdev, int dir)
  63{
  64        struct list_head *feature_report_list = &hdev->report_enum[dir].report_list;
  65        struct hid_report *report = NULL;
  66
  67        list_for_each_entry(report, feature_report_list, list) {
  68                if (report->id == id)
  69                        return report;
  70        }
  71        hid_warn(hdev, "No report with id 0x%x found\n", id);
  72        return NULL;
  73}
  74
  75/* Submit a report and wait for a reply from device - if device fades away
  76 * or does not respond in time, return NULL */
  77struct picolcd_pending *picolcd_send_and_wait(struct hid_device *hdev,
  78                int report_id, const u8 *raw_data, int size)
  79{
  80        struct picolcd_data *data = hid_get_drvdata(hdev);
  81        struct picolcd_pending *work;
  82        struct hid_report *report = picolcd_out_report(report_id, hdev);
  83        unsigned long flags;
  84        int i, j, k;
  85
  86        if (!report || !data)
  87                return NULL;
  88        if (data->status & PICOLCD_FAILED)
  89                return NULL;
  90        work = kzalloc(sizeof(*work), GFP_KERNEL);
  91        if (!work)
  92                return NULL;
  93
  94        init_completion(&work->ready);
  95        work->out_report = report;
  96        work->in_report  = NULL;
  97        work->raw_size   = 0;
  98
  99        mutex_lock(&data->mutex);
 100        spin_lock_irqsave(&data->lock, flags);
 101        for (i = k = 0; i < report->maxfield; i++)
 102                for (j = 0; j < report->field[i]->report_count; j++) {
 103                        hid_set_field(report->field[i], j, k < size ? raw_data[k] : 0);
 104                        k++;
 105                }
 106        if (data->status & PICOLCD_FAILED) {
 107                kfree(work);
 108                work = NULL;
 109        } else {
 110                data->pending = work;
 111                hid_hw_request(data->hdev, report, HID_REQ_SET_REPORT);
 112                spin_unlock_irqrestore(&data->lock, flags);
 113                wait_for_completion_interruptible_timeout(&work->ready, HZ*2);
 114                spin_lock_irqsave(&data->lock, flags);
 115                data->pending = NULL;
 116        }
 117        spin_unlock_irqrestore(&data->lock, flags);
 118        mutex_unlock(&data->mutex);
 119        return work;
 120}
 121
 122/*
 123 * input class device
 124 */
 125static int picolcd_raw_keypad(struct picolcd_data *data,
 126                struct hid_report *report, u8 *raw_data, int size)
 127{
 128        /*
 129         * Keypad event
 130         * First and second data bytes list currently pressed keys,
 131         * 0x00 means no key and at most 2 keys may be pressed at same time
 132         */
 133        int i, j;
 134
 135        /* determine newly pressed keys */
 136        for (i = 0; i < size; i++) {
 137                unsigned int key_code;
 138                if (raw_data[i] == 0)
 139                        continue;
 140                for (j = 0; j < sizeof(data->pressed_keys); j++)
 141                        if (data->pressed_keys[j] == raw_data[i])
 142                                goto key_already_down;
 143                for (j = 0; j < sizeof(data->pressed_keys); j++)
 144                        if (data->pressed_keys[j] == 0) {
 145                                data->pressed_keys[j] = raw_data[i];
 146                                break;
 147                        }
 148                input_event(data->input_keys, EV_MSC, MSC_SCAN, raw_data[i]);
 149                if (raw_data[i] < PICOLCD_KEYS)
 150                        key_code = data->keycode[raw_data[i]];
 151                else
 152                        key_code = KEY_UNKNOWN;
 153                if (key_code != KEY_UNKNOWN) {
 154                        dbg_hid(PICOLCD_NAME " got key press for %u:%d",
 155                                        raw_data[i], key_code);
 156                        input_report_key(data->input_keys, key_code, 1);
 157                }
 158                input_sync(data->input_keys);
 159key_already_down:
 160                continue;
 161        }
 162
 163        /* determine newly released keys */
 164        for (j = 0; j < sizeof(data->pressed_keys); j++) {
 165                unsigned int key_code;
 166                if (data->pressed_keys[j] == 0)
 167                        continue;
 168                for (i = 0; i < size; i++)
 169                        if (data->pressed_keys[j] == raw_data[i])
 170                                goto key_still_down;
 171                input_event(data->input_keys, EV_MSC, MSC_SCAN, data->pressed_keys[j]);
 172                if (data->pressed_keys[j] < PICOLCD_KEYS)
 173                        key_code = data->keycode[data->pressed_keys[j]];
 174                else
 175                        key_code = KEY_UNKNOWN;
 176                if (key_code != KEY_UNKNOWN) {
 177                        dbg_hid(PICOLCD_NAME " got key release for %u:%d",
 178                                        data->pressed_keys[j], key_code);
 179                        input_report_key(data->input_keys, key_code, 0);
 180                }
 181                input_sync(data->input_keys);
 182                data->pressed_keys[j] = 0;
 183key_still_down:
 184                continue;
 185        }
 186        return 1;
 187}
 188
 189static int picolcd_check_version(struct hid_device *hdev)
 190{
 191        struct picolcd_data *data = hid_get_drvdata(hdev);
 192        struct picolcd_pending *verinfo;
 193        int ret = 0;
 194
 195        if (!data)
 196                return -ENODEV;
 197
 198        verinfo = picolcd_send_and_wait(hdev, REPORT_VERSION, NULL, 0);
 199        if (!verinfo) {
 200                hid_err(hdev, "no version response from PicoLCD\n");
 201                return -ENODEV;
 202        }
 203
 204        if (verinfo->raw_size == 2) {
 205                data->version[0] = verinfo->raw_data[1];
 206                data->version[1] = verinfo->raw_data[0];
 207                if (data->status & PICOLCD_BOOTLOADER) {
 208                        hid_info(hdev, "PicoLCD, bootloader version %d.%d\n",
 209                                 verinfo->raw_data[1], verinfo->raw_data[0]);
 210                } else {
 211                        hid_info(hdev, "PicoLCD, firmware version %d.%d\n",
 212                                 verinfo->raw_data[1], verinfo->raw_data[0]);
 213                }
 214        } else {
 215                hid_err(hdev, "confused, got unexpected version response from PicoLCD\n");
 216                ret = -EINVAL;
 217        }
 218        kfree(verinfo);
 219        return ret;
 220}
 221
 222/*
 223 * Reset our device and wait for answer to VERSION request
 224 */
 225int picolcd_reset(struct hid_device *hdev)
 226{
 227        struct picolcd_data *data = hid_get_drvdata(hdev);
 228        struct hid_report *report = picolcd_out_report(REPORT_RESET, hdev);
 229        unsigned long flags;
 230        int error;
 231
 232        if (!data || !report || report->maxfield != 1)
 233                return -ENODEV;
 234
 235        spin_lock_irqsave(&data->lock, flags);
 236        if (hdev->product == USB_DEVICE_ID_PICOLCD_BOOTLOADER)
 237                data->status |= PICOLCD_BOOTLOADER;
 238
 239        /* perform the reset */
 240        hid_set_field(report->field[0], 0, 1);
 241        if (data->status & PICOLCD_FAILED) {
 242                spin_unlock_irqrestore(&data->lock, flags);
 243                return -ENODEV;
 244        }
 245        hid_hw_request(hdev, report, HID_REQ_SET_REPORT);
 246        spin_unlock_irqrestore(&data->lock, flags);
 247
 248        error = picolcd_check_version(hdev);
 249        if (error)
 250                return error;
 251
 252        picolcd_resume_lcd(data);
 253        picolcd_resume_backlight(data);
 254        picolcd_fb_refresh(data);
 255        picolcd_leds_set(data);
 256        return 0;
 257}
 258
 259/*
 260 * The "operation_mode" sysfs attribute
 261 */
 262static ssize_t picolcd_operation_mode_show(struct device *dev,
 263                struct device_attribute *attr, char *buf)
 264{
 265        struct picolcd_data *data = dev_get_drvdata(dev);
 266
 267        if (data->status & PICOLCD_BOOTLOADER)
 268                return snprintf(buf, PAGE_SIZE, "[bootloader] lcd\n");
 269        else
 270                return snprintf(buf, PAGE_SIZE, "bootloader [lcd]\n");
 271}
 272
 273static ssize_t picolcd_operation_mode_store(struct device *dev,
 274                struct device_attribute *attr, const char *buf, size_t count)
 275{
 276        struct picolcd_data *data = dev_get_drvdata(dev);
 277        struct hid_report *report = NULL;
 278        size_t cnt = count;
 279        int timeout = data->opmode_delay;
 280        unsigned long flags;
 281
 282        if (cnt >= 3 && strncmp("lcd", buf, 3) == 0) {
 283                if (data->status & PICOLCD_BOOTLOADER)
 284                        report = picolcd_out_report(REPORT_EXIT_FLASHER, data->hdev);
 285                buf += 3;
 286                cnt -= 3;
 287        } else if (cnt >= 10 && strncmp("bootloader", buf, 10) == 0) {
 288                if (!(data->status & PICOLCD_BOOTLOADER))
 289                        report = picolcd_out_report(REPORT_EXIT_KEYBOARD, data->hdev);
 290                buf += 10;
 291                cnt -= 10;
 292        }
 293        if (!report || report->maxfield != 1)
 294                return -EINVAL;
 295
 296        while (cnt > 0 && (buf[cnt-1] == '\n' || buf[cnt-1] == '\r'))
 297                cnt--;
 298        if (cnt != 0)
 299                return -EINVAL;
 300
 301        spin_lock_irqsave(&data->lock, flags);
 302        hid_set_field(report->field[0], 0, timeout & 0xff);
 303        hid_set_field(report->field[0], 1, (timeout >> 8) & 0xff);
 304        hid_hw_request(data->hdev, report, HID_REQ_SET_REPORT);
 305        spin_unlock_irqrestore(&data->lock, flags);
 306        return count;
 307}
 308
 309static DEVICE_ATTR(operation_mode, 0644, picolcd_operation_mode_show,
 310                picolcd_operation_mode_store);
 311
 312/*
 313 * The "operation_mode_delay" sysfs attribute
 314 */
 315static ssize_t picolcd_operation_mode_delay_show(struct device *dev,
 316                struct device_attribute *attr, char *buf)
 317{
 318        struct picolcd_data *data = dev_get_drvdata(dev);
 319
 320        return snprintf(buf, PAGE_SIZE, "%hu\n", data->opmode_delay);
 321}
 322
 323static ssize_t picolcd_operation_mode_delay_store(struct device *dev,
 324                struct device_attribute *attr, const char *buf, size_t count)
 325{
 326        struct picolcd_data *data = dev_get_drvdata(dev);
 327        unsigned u;
 328        if (sscanf(buf, "%u", &u) != 1)
 329                return -EINVAL;
 330        if (u > 30000)
 331                return -EINVAL;
 332        else
 333                data->opmode_delay = u;
 334        return count;
 335}
 336
 337static DEVICE_ATTR(operation_mode_delay, 0644, picolcd_operation_mode_delay_show,
 338                picolcd_operation_mode_delay_store);
 339
 340/*
 341 * Handle raw report as sent by device
 342 */
 343static int picolcd_raw_event(struct hid_device *hdev,
 344                struct hid_report *report, u8 *raw_data, int size)
 345{
 346        struct picolcd_data *data = hid_get_drvdata(hdev);
 347        unsigned long flags;
 348        int ret = 0;
 349
 350        if (!data)
 351                return 1;
 352
 353        if (report->id == REPORT_KEY_STATE) {
 354                if (data->input_keys)
 355                        ret = picolcd_raw_keypad(data, report, raw_data+1, size-1);
 356        } else if (report->id == REPORT_IR_DATA) {
 357                ret = picolcd_raw_cir(data, report, raw_data+1, size-1);
 358        } else {
 359                spin_lock_irqsave(&data->lock, flags);
 360                /*
 361                 * We let the caller of picolcd_send_and_wait() check if the
 362                 * report we got is one of the expected ones or not.
 363                 */
 364                if (data->pending) {
 365                        memcpy(data->pending->raw_data, raw_data+1, size-1);
 366                        data->pending->raw_size  = size-1;
 367                        data->pending->in_report = report;
 368                        complete(&data->pending->ready);
 369                }
 370                spin_unlock_irqrestore(&data->lock, flags);
 371        }
 372
 373        picolcd_debug_raw_event(data, hdev, report, raw_data, size);
 374        return 1;
 375}
 376
 377#ifdef CONFIG_PM
 378static int picolcd_suspend(struct hid_device *hdev, pm_message_t message)
 379{
 380        if (PMSG_IS_AUTO(message))
 381                return 0;
 382
 383        picolcd_suspend_backlight(hid_get_drvdata(hdev));
 384        dbg_hid(PICOLCD_NAME " device ready for suspend\n");
 385        return 0;
 386}
 387
 388static int picolcd_resume(struct hid_device *hdev)
 389{
 390        int ret;
 391        ret = picolcd_resume_backlight(hid_get_drvdata(hdev));
 392        if (ret)
 393                dbg_hid(PICOLCD_NAME " restoring backlight failed: %d\n", ret);
 394        return 0;
 395}
 396
 397static int picolcd_reset_resume(struct hid_device *hdev)
 398{
 399        int ret;
 400        ret = picolcd_reset(hdev);
 401        if (ret)
 402                dbg_hid(PICOLCD_NAME " resetting our device failed: %d\n", ret);
 403        ret = picolcd_fb_reset(hid_get_drvdata(hdev), 0);
 404        if (ret)
 405                dbg_hid(PICOLCD_NAME " restoring framebuffer content failed: %d\n", ret);
 406        ret = picolcd_resume_lcd(hid_get_drvdata(hdev));
 407        if (ret)
 408                dbg_hid(PICOLCD_NAME " restoring lcd failed: %d\n", ret);
 409        ret = picolcd_resume_backlight(hid_get_drvdata(hdev));
 410        if (ret)
 411                dbg_hid(PICOLCD_NAME " restoring backlight failed: %d\n", ret);
 412        picolcd_leds_set(hid_get_drvdata(hdev));
 413        return 0;
 414}
 415#endif
 416
 417/* initialize keypad input device */
 418static int picolcd_init_keys(struct picolcd_data *data,
 419                struct hid_report *report)
 420{
 421        struct hid_device *hdev = data->hdev;
 422        struct input_dev *idev;
 423        int error, i;
 424
 425        if (!report)
 426                return -ENODEV;
 427        if (report->maxfield != 1 || report->field[0]->report_count != 2 ||
 428                        report->field[0]->report_size != 8) {
 429                hid_err(hdev, "unsupported KEY_STATE report\n");
 430                return -EINVAL;
 431        }
 432
 433        idev = input_allocate_device();
 434        if (idev == NULL) {
 435                hid_err(hdev, "failed to allocate input device\n");
 436                return -ENOMEM;
 437        }
 438        input_set_drvdata(idev, hdev);
 439        memcpy(data->keycode, def_keymap, sizeof(def_keymap));
 440        idev->name = hdev->name;
 441        idev->phys = hdev->phys;
 442        idev->uniq = hdev->uniq;
 443        idev->id.bustype = hdev->bus;
 444        idev->id.vendor  = hdev->vendor;
 445        idev->id.product = hdev->product;
 446        idev->id.version = hdev->version;
 447        idev->dev.parent = &hdev->dev;
 448        idev->keycode     = &data->keycode;
 449        idev->keycodemax  = PICOLCD_KEYS;
 450        idev->keycodesize = sizeof(data->keycode[0]);
 451        input_set_capability(idev, EV_MSC, MSC_SCAN);
 452        set_bit(EV_REP, idev->evbit);
 453        for (i = 0; i < PICOLCD_KEYS; i++)
 454                input_set_capability(idev, EV_KEY, data->keycode[i]);
 455        error = input_register_device(idev);
 456        if (error) {
 457                hid_err(hdev, "error registering the input device\n");
 458                input_free_device(idev);
 459                return error;
 460        }
 461        data->input_keys = idev;
 462        return 0;
 463}
 464
 465static void picolcd_exit_keys(struct picolcd_data *data)
 466{
 467        struct input_dev *idev = data->input_keys;
 468
 469        data->input_keys = NULL;
 470        if (idev)
 471                input_unregister_device(idev);
 472}
 473
 474static int picolcd_probe_lcd(struct hid_device *hdev, struct picolcd_data *data)
 475{
 476        int error;
 477
 478        /* Setup keypad input device */
 479        error = picolcd_init_keys(data, picolcd_in_report(REPORT_KEY_STATE, hdev));
 480        if (error)
 481                goto err;
 482
 483        /* Setup CIR input device */
 484        error = picolcd_init_cir(data, picolcd_in_report(REPORT_IR_DATA, hdev));
 485        if (error)
 486                goto err;
 487
 488        /* Set up the framebuffer device */
 489        error = picolcd_init_framebuffer(data);
 490        if (error)
 491                goto err;
 492
 493        /* Setup lcd class device */
 494        error = picolcd_init_lcd(data, picolcd_out_report(REPORT_CONTRAST, hdev));
 495        if (error)
 496                goto err;
 497
 498        /* Setup backlight class device */
 499        error = picolcd_init_backlight(data, picolcd_out_report(REPORT_BRIGHTNESS, hdev));
 500        if (error)
 501                goto err;
 502
 503        /* Setup the LED class devices */
 504        error = picolcd_init_leds(data, picolcd_out_report(REPORT_LED_STATE, hdev));
 505        if (error)
 506                goto err;
 507
 508        picolcd_init_devfs(data, picolcd_out_report(REPORT_EE_READ, hdev),
 509                        picolcd_out_report(REPORT_EE_WRITE, hdev),
 510                        picolcd_out_report(REPORT_READ_MEMORY, hdev),
 511                        picolcd_out_report(REPORT_WRITE_MEMORY, hdev),
 512                        picolcd_out_report(REPORT_RESET, hdev));
 513        return 0;
 514err:
 515        picolcd_exit_leds(data);
 516        picolcd_exit_backlight(data);
 517        picolcd_exit_lcd(data);
 518        picolcd_exit_framebuffer(data);
 519        picolcd_exit_cir(data);
 520        picolcd_exit_keys(data);
 521        return error;
 522}
 523
 524static int picolcd_probe_bootloader(struct hid_device *hdev, struct picolcd_data *data)
 525{
 526        picolcd_init_devfs(data, NULL, NULL,
 527                        picolcd_out_report(REPORT_BL_READ_MEMORY, hdev),
 528                        picolcd_out_report(REPORT_BL_WRITE_MEMORY, hdev), NULL);
 529        return 0;
 530}
 531
 532static int picolcd_probe(struct hid_device *hdev,
 533                     const struct hid_device_id *id)
 534{
 535        struct picolcd_data *data;
 536        int error = -ENOMEM;
 537
 538        dbg_hid(PICOLCD_NAME " hardware probe...\n");
 539
 540        /*
 541         * Let's allocate the picolcd data structure, set some reasonable
 542         * defaults, and associate it with the device
 543         */
 544        data = kzalloc(sizeof(struct picolcd_data), GFP_KERNEL);
 545        if (data == NULL) {
 546                hid_err(hdev, "can't allocate space for Minibox PicoLCD device data\n");
 547                error = -ENOMEM;
 548                goto err_no_cleanup;
 549        }
 550
 551        spin_lock_init(&data->lock);
 552        mutex_init(&data->mutex);
 553        data->hdev = hdev;
 554        data->opmode_delay = 5000;
 555        if (hdev->product == USB_DEVICE_ID_PICOLCD_BOOTLOADER)
 556                data->status |= PICOLCD_BOOTLOADER;
 557        hid_set_drvdata(hdev, data);
 558
 559        /* Parse the device reports and start it up */
 560        error = hid_parse(hdev);
 561        if (error) {
 562                hid_err(hdev, "device report parse failed\n");
 563                goto err_cleanup_data;
 564        }
 565
 566        error = hid_hw_start(hdev, 0);
 567        if (error) {
 568                hid_err(hdev, "hardware start failed\n");
 569                goto err_cleanup_data;
 570        }
 571
 572        error = hid_hw_open(hdev);
 573        if (error) {
 574                hid_err(hdev, "failed to open input interrupt pipe for key and IR events\n");
 575                goto err_cleanup_hid_hw;
 576        }
 577
 578        error = device_create_file(&hdev->dev, &dev_attr_operation_mode_delay);
 579        if (error) {
 580                hid_err(hdev, "failed to create sysfs attributes\n");
 581                goto err_cleanup_hid_ll;
 582        }
 583
 584        error = device_create_file(&hdev->dev, &dev_attr_operation_mode);
 585        if (error) {
 586                hid_err(hdev, "failed to create sysfs attributes\n");
 587                goto err_cleanup_sysfs1;
 588        }
 589
 590        if (data->status & PICOLCD_BOOTLOADER)
 591                error = picolcd_probe_bootloader(hdev, data);
 592        else
 593                error = picolcd_probe_lcd(hdev, data);
 594        if (error)
 595                goto err_cleanup_sysfs2;
 596
 597        dbg_hid(PICOLCD_NAME " activated and initialized\n");
 598        return 0;
 599
 600err_cleanup_sysfs2:
 601        device_remove_file(&hdev->dev, &dev_attr_operation_mode);
 602err_cleanup_sysfs1:
 603        device_remove_file(&hdev->dev, &dev_attr_operation_mode_delay);
 604err_cleanup_hid_ll:
 605        hid_hw_close(hdev);
 606err_cleanup_hid_hw:
 607        hid_hw_stop(hdev);
 608err_cleanup_data:
 609        kfree(data);
 610err_no_cleanup:
 611        hid_set_drvdata(hdev, NULL);
 612
 613        return error;
 614}
 615
 616static void picolcd_remove(struct hid_device *hdev)
 617{
 618        struct picolcd_data *data = hid_get_drvdata(hdev);
 619        unsigned long flags;
 620
 621        dbg_hid(PICOLCD_NAME " hardware remove...\n");
 622        spin_lock_irqsave(&data->lock, flags);
 623        data->status |= PICOLCD_FAILED;
 624        spin_unlock_irqrestore(&data->lock, flags);
 625
 626        picolcd_exit_devfs(data);
 627        device_remove_file(&hdev->dev, &dev_attr_operation_mode);
 628        device_remove_file(&hdev->dev, &dev_attr_operation_mode_delay);
 629        hid_hw_close(hdev);
 630        hid_hw_stop(hdev);
 631
 632        /* Shortcut potential pending reply that will never arrive */
 633        spin_lock_irqsave(&data->lock, flags);
 634        if (data->pending)
 635                complete(&data->pending->ready);
 636        spin_unlock_irqrestore(&data->lock, flags);
 637
 638        /* Cleanup LED */
 639        picolcd_exit_leds(data);
 640        /* Clean up the framebuffer */
 641        picolcd_exit_backlight(data);
 642        picolcd_exit_lcd(data);
 643        picolcd_exit_framebuffer(data);
 644        /* Cleanup input */
 645        picolcd_exit_cir(data);
 646        picolcd_exit_keys(data);
 647
 648        hid_set_drvdata(hdev, NULL);
 649        mutex_destroy(&data->mutex);
 650        /* Finally, clean up the picolcd data itself */
 651        kfree(data);
 652}
 653
 654static const struct hid_device_id picolcd_devices[] = {
 655        { HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICOLCD) },
 656        { HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICOLCD_BOOTLOADER) },
 657        { }
 658};
 659MODULE_DEVICE_TABLE(hid, picolcd_devices);
 660
 661static struct hid_driver picolcd_driver = {
 662        .name =          "hid-picolcd",
 663        .id_table =      picolcd_devices,
 664        .probe =         picolcd_probe,
 665        .remove =        picolcd_remove,
 666        .raw_event =     picolcd_raw_event,
 667#ifdef CONFIG_PM
 668        .suspend =       picolcd_suspend,
 669        .resume =        picolcd_resume,
 670        .reset_resume =  picolcd_reset_resume,
 671#endif
 672};
 673module_hid_driver(picolcd_driver);
 674
 675MODULE_DESCRIPTION("Minibox graphics PicoLCD Driver");
 676MODULE_LICENSE("GPL v2");
 677
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.