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