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#include <linux/backlight.h> 28#include <linux/lcd.h> 29 30#include <linux/leds.h> 31 32#include <linux/seq_file.h> 33#include <linux/debugfs.h> 34 35#include <linux/completion.h> 36#include <linux/uaccess.h> 37#include <linux/module.h> 38 39#include "hid-picolcd.h" 40 41 42void picolcd_leds_set(struct picolcd_data *data) 43{ 44 struct hid_report *report; 45 unsigned long flags; 46 47 if (!data->led[0]) 48 return; 49 report = picolcd_out_report(REPORT_LED_STATE, data->hdev); 50 if (!report || report->maxfield != 1 || report->field[0]->report_count != 1) 51 return; 52 53 spin_lock_irqsave(&data->lock, flags); 54 hid_set_field(report->field[0], 0, data->led_state); 55 if (!(data->status & PICOLCD_FAILED)) 56 hid_hw_request(data->hdev, report, HID_REQ_SET_REPORT); 57 spin_unlock_irqrestore(&data->lock, flags); 58} 59 60static void picolcd_led_set_brightness(struct led_classdev *led_cdev, 61 enum led_brightness value) 62{ 63 struct device *dev; 64 struct hid_device *hdev; 65 struct picolcd_data *data; 66 int i, state = 0; 67 68 dev = led_cdev->dev->parent; 69 hdev = container_of(dev, struct hid_device, dev); 70 data = hid_get_drvdata(hdev); 71 if (!data) 72 return; 73 for (i = 0; i < 8; i++) { 74 if (led_cdev != data->led[i]) 75 continue; 76 state = (data->led_state >> i) & 1; 77 if (value == LED_OFF && state) { 78 data->led_state &= ~(1 << i); 79 picolcd_leds_set(data); 80 } else if (value != LED_OFF && !state) { 81 data->led_state |= 1 << i; 82 picolcd_leds_set(data); 83 } 84 break; 85 } 86} 87 88static enum led_brightness picolcd_led_get_brightness(struct led_classdev *led_cdev) 89{ 90 struct device *dev; 91 struct hid_device *hdev; 92 struct picolcd_data *data; 93 int i, value = 0; 94 95 dev = led_cdev->dev->parent; 96 hdev = container_of(dev, struct hid_device, dev); 97 data = hid_get_drvdata(hdev); 98 for (i = 0; i < 8; i++) 99 if (led_cdev == data->led[i]) { 100 value = (data->led_state >> i) & 1; 101 break; 102 } 103 return value ? LED_FULL : LED_OFF; 104} 105 106int picolcd_init_leds(struct picolcd_data *data, struct hid_report *report) 107{ 108 struct device *dev = &data->hdev->dev; 109 struct led_classdev *led; 110 size_t name_sz = strlen(dev_name(dev)) + 8; 111 char *name; 112 int i, ret = 0; 113 114 if (!report) 115 return -ENODEV; 116 if (report->maxfield != 1 || report->field[0]->report_count != 1 || 117 report->field[0]->report_size != 8) { 118 dev_err(dev, "unsupported LED_STATE report"); 119 return -EINVAL; 120 } 121 122 for (i = 0; i < 8; i++) { 123 led = kzalloc(sizeof(struct led_classdev)+name_sz, GFP_KERNEL); 124 if (!led) { 125 dev_err(dev, "can't allocate memory for LED %d\n", i); 126 ret = -ENOMEM; 127 goto err; 128 } 129 name = (void *)(&led[1]); 130 snprintf(name, name_sz, "%s::GPO%d", dev_name(dev), i); 131 led->name = name; 132 led->brightness = 0; 133 led->max_brightness = 1; 134 led->brightness_get = picolcd_led_get_brightness; 135 led->brightness_set = picolcd_led_set_brightness; 136 137 data->led[i] = led; 138 ret = led_classdev_register(dev, data->led[i]); 139 if (ret) { 140 data->led[i] = NULL; 141 kfree(led); 142 dev_err(dev, "can't register LED %d\n", i); 143 goto err; 144 } 145 } 146 return 0; 147err: 148 for (i = 0; i < 8; i++) 149 if (data->led[i]) { 150 led = data->led[i]; 151 data->led[i] = NULL; 152 led_classdev_unregister(led); 153 kfree(led); 154 } 155 return ret; 156} 157 158void picolcd_exit_leds(struct picolcd_data *data) 159{ 160 struct led_classdev *led; 161 int i; 162 163 for (i = 0; i < 8; i++) { 164 led = data->led[i]; 165 data->led[i] = NULL; 166 if (!led) 167 continue; 168 led_classdev_unregister(led); 169 kfree(led); 170 } 171} 172 173 174