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