HID: picoLCD: split driver code

In order to make code maintenance easier, split the vairous
functions into individial files (this removes a bunch of #ifdefs).

Signed-off-by: Bruno Prémont <bonbons@linux-vserver.org>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
This commit is contained in:
Bruno Prémont
2012-07-30 21:38:28 +02:00
committed by Jiri Kosina
parent e8ff13b0bf
commit fabdbf2fd2
11 changed files with 3069 additions and 2748 deletions

View File

@@ -527,6 +527,14 @@ config HID_PICOLCD_LEDS
---help---
Provide access to PicoLCD's GPO pins via leds class.
config HID_PICOLCD_CIR
bool "CIR via RC class" if EXPERT
default !EXPERT
depends on HID_PICOLCD
depends on HID_PICOLCD=RC_CORE || RC_CORE=y
---help---
Provide access to PicoLCD's CIR interface via remote control (LIRC).
config HID_PRIMAX
tristate "Primax non-fully HID-compliant devices"
depends on USB_HID

View File

@@ -69,6 +69,26 @@ obj-$(CONFIG_HID_PRODIKEYS) += hid-prodikeys.o
obj-$(CONFIG_HID_PANTHERLORD) += hid-pl.o
obj-$(CONFIG_HID_PETALYNX) += hid-petalynx.o
obj-$(CONFIG_HID_PICOLCD) += hid-picolcd.o
hid-picolcd-y += hid-picolcd_core.o
ifdef CONFIG_HID_PICOLCD_FB
hid-picolcd-y += hid-picolcd_fb.o
endif
ifdef CONFIG_HID_PICOLCD_BACKLIGHT
hid-picolcd-y += hid-picolcd_backlight.o
endif
ifdef CONFIG_HID_PICOLCD_LCD
hid-picolcd-y += hid-picolcd_lcd.o
endif
ifdef CONFIG_HID_PICOLCD_LEDS
hid-picolcd-y += hid-picolcd_leds.o
endif
ifdef CONFIG_HID_PICOLCD_CIR
hid-picolcd-y += hid-picolcd_cir.o
endif
ifdef CONFIG_DEBUG_FS
hid-picolcd-y += hid-picolcd_debugfs.o
endif
obj-$(CONFIG_HID_PRIMAX) += hid-primax.o
obj-$(CONFIG_HID_ROCCAT) += hid-roccat.o hid-roccat-common.o \
hid-roccat-arvo.o hid-roccat-isku.o hid-roccat-kone.o \

File diff suppressed because it is too large Load Diff

306
drivers/hid/hid-picolcd.h Normal file
View File

@@ -0,0 +1,306 @@
/***************************************************************************
* Copyright (C) 2010-2012 by Bruno Prémont <bonbons@linux-vserver.org> *
* *
* Based on Logitech G13 driver (v0.4) *
* Copyright (C) 2009 by Rick L. Vinyard, Jr. <rvinyard@cs.nmsu.edu> *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, version 2 of the License. *
* *
* This driver is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this software. If not see <http://www.gnu.org/licenses/>. *
***************************************************************************/
#define PICOLCD_NAME "PicoLCD (graphic)"
/* Report numbers */
#define REPORT_ERROR_CODE 0x10 /* LCD: IN[16] */
#define ERR_SUCCESS 0x00
#define ERR_PARAMETER_MISSING 0x01
#define ERR_DATA_MISSING 0x02
#define ERR_BLOCK_READ_ONLY 0x03
#define ERR_BLOCK_NOT_ERASABLE 0x04
#define ERR_BLOCK_TOO_BIG 0x05
#define ERR_SECTION_OVERFLOW 0x06
#define ERR_INVALID_CMD_LEN 0x07
#define ERR_INVALID_DATA_LEN 0x08
#define REPORT_KEY_STATE 0x11 /* LCD: IN[2] */
#define REPORT_IR_DATA 0x21 /* LCD: IN[63] */
#define REPORT_EE_DATA 0x32 /* LCD: IN[63] */
#define REPORT_MEMORY 0x41 /* LCD: IN[63] */
#define REPORT_LED_STATE 0x81 /* LCD: OUT[1] */
#define REPORT_BRIGHTNESS 0x91 /* LCD: OUT[1] */
#define REPORT_CONTRAST 0x92 /* LCD: OUT[1] */
#define REPORT_RESET 0x93 /* LCD: OUT[2] */
#define REPORT_LCD_CMD 0x94 /* LCD: OUT[63] */
#define REPORT_LCD_DATA 0x95 /* LCD: OUT[63] */
#define REPORT_LCD_CMD_DATA 0x96 /* LCD: OUT[63] */
#define REPORT_EE_READ 0xa3 /* LCD: OUT[63] */
#define REPORT_EE_WRITE 0xa4 /* LCD: OUT[63] */
#define REPORT_ERASE_MEMORY 0xb2 /* LCD: OUT[2] */
#define REPORT_READ_MEMORY 0xb3 /* LCD: OUT[3] */
#define REPORT_WRITE_MEMORY 0xb4 /* LCD: OUT[63] */
#define REPORT_SPLASH_RESTART 0xc1 /* LCD: OUT[1] */
#define REPORT_EXIT_KEYBOARD 0xef /* LCD: OUT[2] */
#define REPORT_VERSION 0xf1 /* LCD: IN[2],OUT[1] Bootloader: IN[2],OUT[1] */
#define REPORT_BL_ERASE_MEMORY 0xf2 /* Bootloader: IN[36],OUT[4] */
#define REPORT_BL_READ_MEMORY 0xf3 /* Bootloader: IN[36],OUT[4] */
#define REPORT_BL_WRITE_MEMORY 0xf4 /* Bootloader: IN[36],OUT[36] */
#define REPORT_DEVID 0xf5 /* LCD: IN[5], OUT[1] Bootloader: IN[5],OUT[1] */
#define REPORT_SPLASH_SIZE 0xf6 /* LCD: IN[4], OUT[1] */
#define REPORT_HOOK_VERSION 0xf7 /* LCD: IN[2], OUT[1] */
#define REPORT_EXIT_FLASHER 0xff /* Bootloader: OUT[2] */
/* Description of in-progress IO operation, used for operations
* that trigger response from device */
struct picolcd_pending {
struct hid_report *out_report;
struct hid_report *in_report;
struct completion ready;
int raw_size;
u8 raw_data[64];
};
#define PICOLCD_KEYS 17
/* Per device data structure */
struct picolcd_data {
struct hid_device *hdev;
#ifdef CONFIG_DEBUG_FS
struct dentry *debug_reset;
struct dentry *debug_eeprom;
struct dentry *debug_flash;
struct mutex mutex_flash;
int addr_sz;
#endif
u8 version[2];
unsigned short opmode_delay;
/* input stuff */
u8 pressed_keys[2];
struct input_dev *input_keys;
struct input_dev *input_cir;
unsigned short keycode[PICOLCD_KEYS];
#ifdef CONFIG_HID_PICOLCD_FB
/* Framebuffer stuff */
u8 fb_update_rate;
u8 fb_bpp;
u8 fb_force;
u8 *fb_vbitmap; /* local copy of what was sent to PicoLCD */
u8 *fb_bitmap; /* framebuffer */
struct fb_info *fb_info;
struct fb_deferred_io fb_defio;
#endif /* CONFIG_HID_PICOLCD_FB */
#ifdef CONFIG_HID_PICOLCD_LCD
struct lcd_device *lcd;
u8 lcd_contrast;
#endif /* CONFIG_HID_PICOLCD_LCD */
#ifdef CONFIG_HID_PICOLCD_BACKLIGHT
struct backlight_device *backlight;
u8 lcd_brightness;
u8 lcd_power;
#endif /* CONFIG_HID_PICOLCD_BACKLIGHT */
#ifdef CONFIG_HID_PICOLCD_LEDS
/* LED stuff */
u8 led_state;
struct led_classdev *led[8];
#endif /* CONFIG_HID_PICOLCD_LEDS */
/* Housekeeping stuff */
spinlock_t lock;
struct mutex mutex;
struct picolcd_pending *pending;
int status;
#define PICOLCD_BOOTLOADER 1
#define PICOLCD_FAILED 2
#define PICOLCD_READY_FB 4
};
/* Find a given report */
#define picolcd_in_report(id, dev) picolcd_report(id, dev, HID_INPUT_REPORT)
#define picolcd_out_report(id, dev) picolcd_report(id, dev, HID_OUTPUT_REPORT)
struct hid_report *picolcd_report(int id, struct hid_device *hdev, int dir);
#ifdef CONFIG_DEBUG_FS
void picolcd_debug_out_report(struct picolcd_data *data,
struct hid_device *hdev, struct hid_report *report);
#define usbhid_submit_report(a, b, c) \
do { \
picolcd_debug_out_report(hid_get_drvdata(a), a, b); \
usbhid_submit_report(a, b, c); \
} while (0)
void picolcd_debug_raw_event(struct picolcd_data *data,
struct hid_device *hdev, struct hid_report *report,
u8 *raw_data, int size);
void picolcd_init_devfs(struct picolcd_data *data,
struct hid_report *eeprom_r, struct hid_report *eeprom_w,
struct hid_report *flash_r, struct hid_report *flash_w,
struct hid_report *reset);
void picolcd_exit_devfs(struct picolcd_data *data);
#else
static inline void picolcd_debug_raw_event(struct picolcd_data *data,
struct hid_device *hdev, struct hid_report *report,
u8 *raw_data, int size)
{
}
static inline void picolcd_init_devfs(struct picolcd_data *data,
struct hid_report *eeprom_r, struct hid_report *eeprom_w,
struct hid_report *flash_r, struct hid_report *flash_w,
struct hid_report *reset)
{
}
static inline void picolcd_exit_devfs(struct picolcd_data *data)
{
}
static inline void picolcd_debug_raw_event(struct picolcd_data *data,
struct hid_device *hdev, struct hid_report *report,
u8 *raw_data, int size)
{
}
#endif /* CONFIG_DEBUG_FS */
#ifdef CONFIG_HID_PICOLCD_FB
int picolcd_fb_reset(struct picolcd_data *data, int clear);
int picolcd_init_framebuffer(struct picolcd_data *data);
void picolcd_exit_framebuffer(struct picolcd_data *data);
void picolcd_fb_unload(void);
void picolcd_fb_refresh(struct picolcd_data *data);
#define picolcd_fbinfo(d) ((d)->fb_info)
#else
static inline int picolcd_fb_reset(struct picolcd_data *data, int clear)
{
return 0;
}
static inline int picolcd_init_framebuffer(struct picolcd_data *data)
{
return 0;
}
static inline void picolcd_exit_framebuffer(struct picolcd_data *data)
{
}
static inline void picolcd_fb_unload(void)
{
}
static inline void picolcd_fb_refresh(struct picolcd_data *data)
{
}
#define picolcd_fbinfo(d) NULL
#endif /* CONFIG_HID_PICOLCD_FB */
#ifdef CONFIG_HID_PICOLCD_BACKLIGHT
int picolcd_init_backlight(struct picolcd_data *data,
struct hid_report *report);
void picolcd_exit_backlight(struct picolcd_data *data);
int picolcd_resume_backlight(struct picolcd_data *data);
void picolcd_suspend_backlight(struct picolcd_data *data);
#else
static inline int picolcd_init_backlight(struct picolcd_data *data,
struct hid_report *report)
{
return 0;
}
static inline void picolcd_exit_backlight(struct picolcd_data *data)
{
}
static inline int picolcd_resume_backlight(struct picolcd_data *data)
{
return 0;
}
static inline void picolcd_suspend_backlight(struct picolcd_data *data)
{
}
#endif /* CONFIG_HID_PICOLCD_BACKLIGHT */
#ifdef CONFIG_HID_PICOLCD_LCD
int picolcd_init_lcd(struct picolcd_data *data,
struct hid_report *report);
void picolcd_exit_lcd(struct picolcd_data *data);
int picolcd_resume_lcd(struct picolcd_data *data);
#else
static inline int picolcd_init_lcd(struct picolcd_data *data,
struct hid_report *report)
{
return 0;
}
static inline void picolcd_exit_lcd(struct picolcd_data *data)
{
}
static inline int picolcd_resume_lcd(struct picolcd_data *data)
{
return 0;
}
#endif /* CONFIG_HID_PICOLCD_LCD */
#ifdef CONFIG_HID_PICOLCD_LEDS
int picolcd_init_leds(struct picolcd_data *data,
struct hid_report *report);
void picolcd_exit_leds(struct picolcd_data *data);
void picolcd_leds_set(struct picolcd_data *data);
#else
static inline int picolcd_init_leds(struct picolcd_data *data,
struct hid_report *report)
{
return 0;
}
static inline void picolcd_exit_leds(struct picolcd_data *data)
{
}
static inline void picolcd_leds_set(struct picolcd_data *data)
{
}
#endif /* CONFIG_HID_PICOLCD_LEDS */
#ifdef CONFIG_HID_PICOLCD_CIR
int picolcd_raw_cir(struct picolcd_data *data,
struct hid_report *report, u8 *raw_data, int size);
int picolcd_init_cir(struct picolcd_data *data, struct hid_report *report);
void picolcd_exit_cir(struct picolcd_data *data);
#else
static inline int picolcd_raw_cir(struct picolcd_data *data,
struct hid_report *report, u8 *raw_data, int size)
{
return 1;
}
static inline int picolcd_init_cir(struct picolcd_data *data, struct hid_report *report)
{
return 0;
}
static inline void picolcd_exit_cir(struct picolcd_data *data)
{
}
#endif /* CONFIG_HID_PICOLCD_LIRC */
int picolcd_reset(struct hid_device *hdev);
struct picolcd_pending *picolcd_send_and_wait(struct hid_device *hdev,
int report_id, const u8 *raw_data, int size);

View File

@@ -0,0 +1,121 @@
/***************************************************************************
* Copyright (C) 2010-2012 by Bruno Prémont <bonbons@linux-vserver.org> *
* *
* Based on Logitech G13 driver (v0.4) *
* Copyright (C) 2009 by Rick L. Vinyard, Jr. <rvinyard@cs.nmsu.edu> *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, version 2 of the License. *
* *
* This driver is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this software. If not see <http://www.gnu.org/licenses/>. *
***************************************************************************/
#include <linux/hid.h>
#include "usbhid/usbhid.h"
#include <linux/usb.h>
#include <linux/fb.h>
#include <linux/backlight.h>
#include "hid-picolcd.h"
static int picolcd_get_brightness(struct backlight_device *bdev)
{
struct picolcd_data *data = bl_get_data(bdev);
return data->lcd_brightness;
}
static int picolcd_set_brightness(struct backlight_device *bdev)
{
struct picolcd_data *data = bl_get_data(bdev);
struct hid_report *report = picolcd_out_report(REPORT_BRIGHTNESS, data->hdev);
unsigned long flags;
if (!report || report->maxfield != 1 || report->field[0]->report_count != 1)
return -ENODEV;
data->lcd_brightness = bdev->props.brightness & 0x0ff;
data->lcd_power = bdev->props.power;
spin_lock_irqsave(&data->lock, flags);
hid_set_field(report->field[0], 0, data->lcd_power == FB_BLANK_UNBLANK ? data->lcd_brightness : 0);
usbhid_submit_report(data->hdev, report, USB_DIR_OUT);
spin_unlock_irqrestore(&data->lock, flags);
return 0;
}
static int picolcd_check_bl_fb(struct backlight_device *bdev, struct fb_info *fb)
{
return fb && fb == picolcd_fbinfo((struct picolcd_data *)bl_get_data(bdev));
}
static const struct backlight_ops picolcd_blops = {
.update_status = picolcd_set_brightness,
.get_brightness = picolcd_get_brightness,
.check_fb = picolcd_check_bl_fb,
};
int picolcd_init_backlight(struct picolcd_data *data, struct hid_report *report)
{
struct device *dev = &data->hdev->dev;
struct backlight_device *bdev;
struct backlight_properties props;
if (!report)
return -ENODEV;
if (report->maxfield != 1 || report->field[0]->report_count != 1 ||
report->field[0]->report_size != 8) {
dev_err(dev, "unsupported BRIGHTNESS report");
return -EINVAL;
}
memset(&props, 0, sizeof(props));
props.type = BACKLIGHT_RAW;
props.max_brightness = 0xff;
bdev = backlight_device_register(dev_name(dev), dev, data,
&picolcd_blops, &props);
if (IS_ERR(bdev)) {
dev_err(dev, "failed to register backlight\n");
return PTR_ERR(bdev);
}
bdev->props.brightness = 0xff;
data->lcd_brightness = 0xff;
data->backlight = bdev;
picolcd_set_brightness(bdev);
return 0;
}
void picolcd_exit_backlight(struct picolcd_data *data)
{
struct backlight_device *bdev = data->backlight;
data->backlight = NULL;
if (bdev)
backlight_device_unregister(bdev);
}
int picolcd_resume_backlight(struct picolcd_data *data)
{
if (!data->backlight)
return 0;
return picolcd_set_brightness(data->backlight);
}
#ifdef CONFIG_PM
void picolcd_suspend_backlight(struct picolcd_data *data)
{
int bl_power = data->lcd_power;
if (!data->backlight)
return;
data->backlight->props.power = FB_BLANK_POWERDOWN;
picolcd_set_brightness(data->backlight);
data->lcd_power = data->backlight->props.power = bl_power;
}
#endif /* CONFIG_PM */

View File

@@ -0,0 +1,61 @@
/***************************************************************************
* Copyright (C) 2010-2012 by Bruno Prémont <bonbons@linux-vserver.org> *
* *
* Based on Logitech G13 driver (v0.4) *
* Copyright (C) 2009 by Rick L. Vinyard, Jr. <rvinyard@cs.nmsu.edu> *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, version 2 of the License. *
* *
* This driver is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this software. If not see <http://www.gnu.org/licenses/>. *
***************************************************************************/
#include <linux/hid.h>
#include <linux/hid-debug.h>
#include <linux/input.h>
#include "hid-ids.h"
#include "usbhid/usbhid.h"
#include <linux/usb.h>
#include <linux/fb.h>
#include <linux/vmalloc.h>
#include <linux/backlight.h>
#include <linux/lcd.h>
#include <linux/leds.h>
#include <linux/seq_file.h>
#include <linux/debugfs.h>
#include <linux/completion.h>
#include <linux/uaccess.h>
#include <linux/module.h>
#include "hid-picolcd.h"
int picolcd_raw_cir(struct picolcd_data *data,
struct hid_report *report, u8 *raw_data, int size)
{
/* Need understanding of CIR data format to implement ... */
return 1;
}
/* initialize CIR input device */
int picolcd_init_cir(struct picolcd_data *data, struct hid_report *report)
{
/* support not implemented yet */
return 0;
}
void picolcd_exit_cir(struct picolcd_data *data)
{
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,106 @@
/***************************************************************************
* Copyright (C) 2010-2012 by Bruno Prémont <bonbons@linux-vserver.org> *
* *
* Based on Logitech G13 driver (v0.4) *
* Copyright (C) 2009 by Rick L. Vinyard, Jr. <rvinyard@cs.nmsu.edu> *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, version 2 of the License. *
* *
* This driver is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this software. If not see <http://www.gnu.org/licenses/>. *
***************************************************************************/
#include <linux/hid.h>
#include "usbhid/usbhid.h"
#include <linux/usb.h>
#include <linux/fb.h>
#include <linux/lcd.h>
#include "hid-picolcd.h"
/*
* lcd class device
*/
static int picolcd_get_contrast(struct lcd_device *ldev)
{
struct picolcd_data *data = lcd_get_data(ldev);
return data->lcd_contrast;
}
static int picolcd_set_contrast(struct lcd_device *ldev, int contrast)
{
struct picolcd_data *data = lcd_get_data(ldev);
struct hid_report *report = picolcd_out_report(REPORT_CONTRAST, data->hdev);
unsigned long flags;
if (!report || report->maxfield != 1 || report->field[0]->report_count != 1)
return -ENODEV;
data->lcd_contrast = contrast & 0x0ff;
spin_lock_irqsave(&data->lock, flags);
hid_set_field(report->field[0], 0, data->lcd_contrast);
usbhid_submit_report(data->hdev, report, USB_DIR_OUT);
spin_unlock_irqrestore(&data->lock, flags);
return 0;
}
static int picolcd_check_lcd_fb(struct lcd_device *ldev, struct fb_info *fb)
{
return fb && fb == picolcd_fbinfo((struct picolcd_data *)lcd_get_data(ldev));
}
static struct lcd_ops picolcd_lcdops = {
.get_contrast = picolcd_get_contrast,
.set_contrast = picolcd_set_contrast,
.check_fb = picolcd_check_lcd_fb,
};
int picolcd_init_lcd(struct picolcd_data *data, struct hid_report *report)
{
struct device *dev = &data->hdev->dev;
struct lcd_device *ldev;
if (!report)
return -ENODEV;
if (report->maxfield != 1 || report->field[0]->report_count != 1 ||
report->field[0]->report_size != 8) {
dev_err(dev, "unsupported CONTRAST report");
return -EINVAL;
}
ldev = lcd_device_register(dev_name(dev), dev, data, &picolcd_lcdops);
if (IS_ERR(ldev)) {
dev_err(dev, "failed to register LCD\n");
return PTR_ERR(ldev);
}
ldev->props.max_contrast = 0x0ff;
data->lcd_contrast = 0xe5;
data->lcd = ldev;
picolcd_set_contrast(ldev, 0xe5);
return 0;
}
void picolcd_exit_lcd(struct picolcd_data *data)
{
struct lcd_device *ldev = data->lcd;
data->lcd = NULL;
if (ldev)
lcd_device_unregister(ldev);
}
int picolcd_resume_lcd(struct picolcd_data *data)
{
if (!data->lcd)
return 0;
return picolcd_set_contrast(data->lcd, data->lcd_contrast);
}

View File

@@ -0,0 +1,172 @@
/***************************************************************************
* Copyright (C) 2010-2012 by Bruno Prémont <bonbons@linux-vserver.org> *
* *
* Based on Logitech G13 driver (v0.4) *
* Copyright (C) 2009 by Rick L. Vinyard, Jr. <rvinyard@cs.nmsu.edu> *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, version 2 of the License. *
* *
* This driver is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this software. If not see <http://www.gnu.org/licenses/>. *
***************************************************************************/
#include <linux/hid.h>
#include <linux/hid-debug.h>
#include <linux/input.h>
#include "hid-ids.h"
#include "usbhid/usbhid.h"
#include <linux/usb.h>
#include <linux/fb.h>
#include <linux/vmalloc.h>
#include <linux/backlight.h>
#include <linux/lcd.h>
#include <linux/leds.h>
#include <linux/seq_file.h>
#include <linux/debugfs.h>
#include <linux/completion.h>
#include <linux/uaccess.h>
#include <linux/module.h>
#include "hid-picolcd.h"
void picolcd_leds_set(struct picolcd_data *data)
{
struct hid_report *report;
unsigned long flags;
if (!data->led[0])
return;
report = picolcd_out_report(REPORT_LED_STATE, data->hdev);
if (!report || report->maxfield != 1 || report->field[0]->report_count != 1)
return;
spin_lock_irqsave(&data->lock, flags);
hid_set_field(report->field[0], 0, data->led_state);
usbhid_submit_report(data->hdev, report, USB_DIR_OUT);
spin_unlock_irqrestore(&data->lock, flags);
}
static void picolcd_led_set_brightness(struct led_classdev *led_cdev,
enum led_brightness value)
{
struct device *dev;
struct hid_device *hdev;
struct picolcd_data *data;
int i, state = 0;
dev = led_cdev->dev->parent;
hdev = container_of(dev, struct hid_device, dev);
data = hid_get_drvdata(hdev);
for (i = 0; i < 8; i++) {
if (led_cdev != data->led[i])
continue;
state = (data->led_state >> i) & 1;
if (value == LED_OFF && state) {
data->led_state &= ~(1 << i);
picolcd_leds_set(data);
} else if (value != LED_OFF && !state) {
data->led_state |= 1 << i;
picolcd_leds_set(data);
}
break;
}
}
static enum led_brightness picolcd_led_get_brightness(struct led_classdev *led_cdev)
{
struct device *dev;
struct hid_device *hdev;
struct picolcd_data *data;
int i, value = 0;
dev = led_cdev->dev->parent;
hdev = container_of(dev, struct hid_device, dev);
data = hid_get_drvdata(hdev);
for (i = 0; i < 8; i++)
if (led_cdev == data->led[i]) {
value = (data->led_state >> i) & 1;
break;
}
return value ? LED_FULL : LED_OFF;
}
int picolcd_init_leds(struct picolcd_data *data, struct hid_report *report)
{
struct device *dev = &data->hdev->dev;
struct led_classdev *led;
size_t name_sz = strlen(dev_name(dev)) + 8;
char *name;
int i, ret = 0;
if (!report)
return -ENODEV;
if (report->maxfield != 1 || report->field[0]->report_count != 1 ||
report->field[0]->report_size != 8) {
dev_err(dev, "unsupported LED_STATE report");
return -EINVAL;
}
for (i = 0; i < 8; i++) {
led = kzalloc(sizeof(struct led_classdev)+name_sz, GFP_KERNEL);
if (!led) {
dev_err(dev, "can't allocate memory for LED %d\n", i);
ret = -ENOMEM;
goto err;
}
name = (void *)(&led[1]);
snprintf(name, name_sz, "%s::GPO%d", dev_name(dev), i);
led->name = name;
led->brightness = 0;
led->max_brightness = 1;
led->brightness_get = picolcd_led_get_brightness;
led->brightness_set = picolcd_led_set_brightness;
data->led[i] = led;
ret = led_classdev_register(dev, data->led[i]);
if (ret) {
data->led[i] = NULL;
kfree(led);
dev_err(dev, "can't register LED %d\n", i);
goto err;
}
}
return 0;
err:
for (i = 0; i < 8; i++)
if (data->led[i]) {
led = data->led[i];
data->led[i] = NULL;
led_classdev_unregister(led);
kfree(led);
}
return ret;
}
void picolcd_exit_leds(struct picolcd_data *data)
{
struct led_classdev *led;
int i;
for (i = 0; i < 8; i++) {
led = data->led[i];
data->led[i] = NULL;
if (!led)
continue;
led_classdev_unregister(led);
kfree(led);
}
}