drivers/hid/hid-lg-g15.c

Source file repositories/reference/linux-study-clean/drivers/hid/hid-lg-g15.c

File Facts

System
Linux kernel
Corpus path
drivers/hid/hid-lg-g15.c
Extension
.c
Size
37812 bytes
Lines
1388
Domain
Driver Families
Bucket
drivers/hid
Inferred role
Driver Families: implementation source
Status
source implementation candidate

Why This File Exists

Repeatable hardware-adapter layer. Deep compatibility for every driver is out of scope; this atlas records patterns, probe lifecycles, bus glue, IRQ/DMA usage, and links back to core abstractions.

Dependency Surface

Detected Declarations

Annotated Snippet

struct g13_input_report {
	u8 report_id;	/* Report ID is always set to 1. */
	u8 joy_x, joy_y;
	u8 keybits[5];
};

struct lg_g15_led {
	union {
		struct led_classdev cdev;
		struct led_classdev_mc mcdev;
	};
	enum led_brightness brightness;
	enum lg_g15_led_type led;
	/* Used to store initial color intensities before subled_info is allocated */
	u8 red, green, blue;
};

struct lg_g15_data {
	/* Must be first for proper dma alignment */
	u8 transfer_buf[LG_G15_TRANSFER_BUF_SIZE];
	/* Protects the transfer_buf and led brightness */
	struct mutex mutex;
	struct work_struct work;
	struct input_dev *input;
	struct input_dev *input_js; /* Separate joystick device for G13. */
	struct hid_device *hdev;
	enum lg_g15_model model;
	struct lg_g15_led leds[LG_G15_LED_MAX];
	bool game_mode_enabled;
	bool backlight_disabled;	/* true == HW backlight toggled *OFF* */
};

/********* G13 LED functions ***********/
/*
 * G13 retains no state across power cycles, and always powers up with the backlight on,
 * color #5AFF6E, all macro key LEDs off.
 */
static int lg_g13_get_leds_state(struct lg_g15_data *g15)
{
	u8 * const tbuf = g15->transfer_buf;
	int ret, high;

	/* RGB backlight. */
	ret = hid_hw_raw_request(g15->hdev, LG_G13_FEATURE_BACKLIGHT_RGB,
				 tbuf, 5,
				 HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
	if (ret != 5) {
		hid_err(g15->hdev, "Error getting backlight brightness: %d\n", ret);
		return (ret < 0) ? ret : -EIO;
	}

	/* Normalize RGB intensities against the highest component. */
	high = max3(tbuf[1], tbuf[2], tbuf[3]);
	if (high) {
		g15->leds[LG_G15_KBD_BRIGHTNESS].red =
			DIV_ROUND_CLOSEST(tbuf[1] * 255, high);
		g15->leds[LG_G15_KBD_BRIGHTNESS].green =
			DIV_ROUND_CLOSEST(tbuf[2] * 255, high);
		g15->leds[LG_G15_KBD_BRIGHTNESS].blue =
			DIV_ROUND_CLOSEST(tbuf[3] * 255, high);
		g15->leds[LG_G15_KBD_BRIGHTNESS].brightness = high;
	} else {
		g15->leds[LG_G15_KBD_BRIGHTNESS].red        = 255;
		g15->leds[LG_G15_KBD_BRIGHTNESS].green      = 255;
		g15->leds[LG_G15_KBD_BRIGHTNESS].blue       = 255;
		g15->leds[LG_G15_KBD_BRIGHTNESS].brightness = 0;
	}

	/* Macro LEDs. */
	ret = hid_hw_raw_request(g15->hdev, LG_G13_FEATURE_M_KEYS_LEDS,
				 tbuf, 5,
				 HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
	if (ret != 5) {
		hid_err(g15->hdev, "Error getting macro LED brightness: %d\n", ret);
		return (ret < 0) ? ret : -EIO;
	}

	for (int i = LG_G15_MACRO_PRESET1; i < LG_G15_LED_MAX; ++i)
		g15->leds[i].brightness = !!(tbuf[1] & (1 << (i - LG_G15_MACRO_PRESET1)));

	/*
	 * Bit 23 of g13_input_report.keybits[] contains the backlight's
	 * current HW toggle state.  Retrieve it from the device.
	 */
	ret = hid_hw_raw_request(g15->hdev, LG_G13_INPUT_REPORT,
				 tbuf, sizeof(struct g13_input_report),
				 HID_INPUT_REPORT, HID_REQ_GET_REPORT);
	if (ret != sizeof(struct g13_input_report)) {
		hid_err(g15->hdev, "Error getting backlight on/off state: %d\n", ret);
		return (ret < 0) ? ret : -EIO;

Annotation

Implementation Notes