mirror of
https://github.com/torvalds/linux.git
synced 2024-12-14 07:02:23 +00:00
HID: logitech-hidpp: enable HID++ 1.0 battery reporting
Also enable battery reporting for HID++ 1.0 devices through 2 registers: 0x07: battery status -> reports only 4 levels (critical, low, good, full) 0x0D: battery mileage -> reports true pourcentage Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com> Tested-by: Bastien Nocera <hadess@hadess.net> Signed-off-by: Jiri Kosina <jkosina@suse.cz>
This commit is contained in:
parent
696ecef9b5
commit
7f7ce2a258
@ -400,6 +400,211 @@ static void hidpp_prefix_name(char **name, int name_length)
|
||||
#define HIDPP_SET_LONG_REGISTER 0x82
|
||||
#define HIDPP_GET_LONG_REGISTER 0x83
|
||||
|
||||
#define HIDPP_REG_GENERAL 0x00
|
||||
|
||||
static int hidpp10_enable_battery_reporting(struct hidpp_device *hidpp_dev)
|
||||
{
|
||||
struct hidpp_report response;
|
||||
int ret;
|
||||
u8 params[3] = { 0 };
|
||||
|
||||
ret = hidpp_send_rap_command_sync(hidpp_dev,
|
||||
REPORT_ID_HIDPP_SHORT,
|
||||
HIDPP_GET_REGISTER,
|
||||
HIDPP_REG_GENERAL,
|
||||
NULL, 0, &response);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
memcpy(params, response.rap.params, 3);
|
||||
|
||||
/* Set the battery bit */
|
||||
params[0] |= BIT(4);
|
||||
|
||||
return hidpp_send_rap_command_sync(hidpp_dev,
|
||||
REPORT_ID_HIDPP_SHORT,
|
||||
HIDPP_SET_REGISTER,
|
||||
HIDPP_REG_GENERAL,
|
||||
params, 3, &response);
|
||||
}
|
||||
|
||||
#define HIDPP_REG_BATTERY_STATUS 0x07
|
||||
|
||||
static int hidpp10_battery_status_map_level(u8 param)
|
||||
{
|
||||
int level;
|
||||
|
||||
switch (param) {
|
||||
case 1 ... 2:
|
||||
level = POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL;
|
||||
break;
|
||||
case 3 ... 4:
|
||||
level = POWER_SUPPLY_CAPACITY_LEVEL_LOW;
|
||||
break;
|
||||
case 5 ... 6:
|
||||
level = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL;
|
||||
break;
|
||||
case 7:
|
||||
level = POWER_SUPPLY_CAPACITY_LEVEL_HIGH;
|
||||
break;
|
||||
default:
|
||||
level = POWER_SUPPLY_CAPACITY_LEVEL_UNKNOWN;
|
||||
}
|
||||
|
||||
return level;
|
||||
}
|
||||
|
||||
static int hidpp10_battery_status_map_status(u8 param)
|
||||
{
|
||||
int status;
|
||||
|
||||
switch (param) {
|
||||
case 0x00:
|
||||
/* discharging (in use) */
|
||||
status = POWER_SUPPLY_STATUS_DISCHARGING;
|
||||
break;
|
||||
case 0x21: /* (standard) charging */
|
||||
case 0x24: /* fast charging */
|
||||
case 0x25: /* slow charging */
|
||||
status = POWER_SUPPLY_STATUS_CHARGING;
|
||||
break;
|
||||
case 0x26: /* topping charge */
|
||||
case 0x22: /* charge complete */
|
||||
status = POWER_SUPPLY_STATUS_FULL;
|
||||
break;
|
||||
case 0x20: /* unknown */
|
||||
status = POWER_SUPPLY_STATUS_UNKNOWN;
|
||||
break;
|
||||
/*
|
||||
* 0x01...0x1F = reserved (not charging)
|
||||
* 0x23 = charging error
|
||||
* 0x27..0xff = reserved
|
||||
*/
|
||||
default:
|
||||
status = POWER_SUPPLY_STATUS_NOT_CHARGING;
|
||||
break;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static int hidpp10_query_battery_status(struct hidpp_device *hidpp)
|
||||
{
|
||||
struct hidpp_report response;
|
||||
int ret, status;
|
||||
|
||||
ret = hidpp_send_rap_command_sync(hidpp,
|
||||
REPORT_ID_HIDPP_SHORT,
|
||||
HIDPP_GET_REGISTER,
|
||||
HIDPP_REG_BATTERY_STATUS,
|
||||
NULL, 0, &response);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
hidpp->battery.level =
|
||||
hidpp10_battery_status_map_level(response.rap.params[0]);
|
||||
status = hidpp10_battery_status_map_status(response.rap.params[1]);
|
||||
hidpp->battery.status = status;
|
||||
/* the capacity is only available when discharging or full */
|
||||
hidpp->battery.online = status == POWER_SUPPLY_STATUS_DISCHARGING ||
|
||||
status == POWER_SUPPLY_STATUS_FULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define HIDPP_REG_BATTERY_MILEAGE 0x0D
|
||||
|
||||
static int hidpp10_battery_mileage_map_status(u8 param)
|
||||
{
|
||||
int status;
|
||||
|
||||
switch (param >> 6) {
|
||||
case 0x00:
|
||||
/* discharging (in use) */
|
||||
status = POWER_SUPPLY_STATUS_DISCHARGING;
|
||||
break;
|
||||
case 0x01: /* charging */
|
||||
status = POWER_SUPPLY_STATUS_CHARGING;
|
||||
break;
|
||||
case 0x02: /* charge complete */
|
||||
status = POWER_SUPPLY_STATUS_FULL;
|
||||
break;
|
||||
/*
|
||||
* 0x03 = charging error
|
||||
*/
|
||||
default:
|
||||
status = POWER_SUPPLY_STATUS_NOT_CHARGING;
|
||||
break;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static int hidpp10_query_battery_mileage(struct hidpp_device *hidpp)
|
||||
{
|
||||
struct hidpp_report response;
|
||||
int ret, status;
|
||||
|
||||
ret = hidpp_send_rap_command_sync(hidpp,
|
||||
REPORT_ID_HIDPP_SHORT,
|
||||
HIDPP_GET_REGISTER,
|
||||
HIDPP_REG_BATTERY_MILEAGE,
|
||||
NULL, 0, &response);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
hidpp->battery.capacity = response.rap.params[0];
|
||||
status = hidpp10_battery_mileage_map_status(response.rap.params[2]);
|
||||
hidpp->battery.status = status;
|
||||
/* the capacity is only available when discharging or full */
|
||||
hidpp->battery.online = status == POWER_SUPPLY_STATUS_DISCHARGING ||
|
||||
status == POWER_SUPPLY_STATUS_FULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hidpp10_battery_event(struct hidpp_device *hidpp, u8 *data, int size)
|
||||
{
|
||||
struct hidpp_report *report = (struct hidpp_report *)data;
|
||||
int status, capacity, level;
|
||||
bool changed;
|
||||
|
||||
if (report->report_id != REPORT_ID_HIDPP_SHORT)
|
||||
return 0;
|
||||
|
||||
switch (report->rap.sub_id) {
|
||||
case HIDPP_REG_BATTERY_STATUS:
|
||||
capacity = hidpp->battery.capacity;
|
||||
level = hidpp10_battery_status_map_level(report->rawbytes[1]);
|
||||
status = hidpp10_battery_status_map_status(report->rawbytes[2]);
|
||||
break;
|
||||
case HIDPP_REG_BATTERY_MILEAGE:
|
||||
capacity = report->rap.params[0];
|
||||
level = hidpp->battery.level;
|
||||
status = hidpp10_battery_mileage_map_status(report->rawbytes[3]);
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
changed = capacity != hidpp->battery.capacity ||
|
||||
level != hidpp->battery.level ||
|
||||
status != hidpp->battery.status;
|
||||
|
||||
/* the capacity is only available when discharging or full */
|
||||
hidpp->battery.online = status == POWER_SUPPLY_STATUS_DISCHARGING ||
|
||||
status == POWER_SUPPLY_STATUS_FULL;
|
||||
|
||||
if (changed) {
|
||||
hidpp->battery.level = level;
|
||||
hidpp->battery.status = status;
|
||||
if (hidpp->battery.ps)
|
||||
power_supply_changed(hidpp->battery.ps);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define HIDPP_REG_PAIRING_INFORMATION 0xB5
|
||||
#define HIDPP_EXTENDED_PAIRING 0x30
|
||||
#define HIDPP_DEVICE_NAME 0x40
|
||||
@ -2428,6 +2633,12 @@ static int hidpp_raw_hidpp_event(struct hidpp_device *hidpp, u8 *data,
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (hidpp->capabilities & HIDPP_CAPABILITY_HIDPP10_BATTERY) {
|
||||
ret = hidpp10_battery_event(hidpp, data, size);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -2505,7 +2716,16 @@ static int hidpp_initialize_battery(struct hidpp_device *hidpp)
|
||||
return ret;
|
||||
hidpp->capabilities |= HIDPP_CAPABILITY_HIDPP20_BATTERY;
|
||||
} else {
|
||||
return -ENOENT;
|
||||
ret = hidpp10_query_battery_status(hidpp);
|
||||
if (ret) {
|
||||
ret = hidpp10_query_battery_mileage(hidpp);
|
||||
if (ret)
|
||||
return -ENOENT;
|
||||
hidpp->capabilities |= HIDPP_CAPABILITY_BATTERY_MILEAGE;
|
||||
} else {
|
||||
hidpp->capabilities |= HIDPP_CAPABILITY_BATTERY_LEVEL_STATUS;
|
||||
}
|
||||
hidpp->capabilities |= HIDPP_CAPABILITY_HIDPP10_BATTERY;
|
||||
}
|
||||
|
||||
battery_props = devm_kmemdup(&hidpp->hid_dev->dev,
|
||||
@ -2665,11 +2885,17 @@ static void hidpp_connect_event(struct hidpp_device *hidpp)
|
||||
hidpp_initialize_battery(hidpp);
|
||||
|
||||
/* forward current battery state */
|
||||
if (hidpp->capabilities & HIDPP_CAPABILITY_HIDPP20_BATTERY) {
|
||||
if (hidpp->capabilities & HIDPP_CAPABILITY_HIDPP10_BATTERY) {
|
||||
hidpp10_enable_battery_reporting(hidpp);
|
||||
if (hidpp->capabilities & HIDPP_CAPABILITY_BATTERY_MILEAGE)
|
||||
hidpp10_query_battery_mileage(hidpp);
|
||||
else
|
||||
hidpp10_query_battery_status(hidpp);
|
||||
} else if (hidpp->capabilities & HIDPP_CAPABILITY_HIDPP20_BATTERY) {
|
||||
hidpp20_query_battery_info(hidpp);
|
||||
if (hidpp->battery.ps)
|
||||
power_supply_changed(hidpp->battery.ps);
|
||||
}
|
||||
if (hidpp->battery.ps)
|
||||
power_supply_changed(hidpp->battery.ps);
|
||||
|
||||
if (!(hidpp->quirks & HIDPP_QUIRK_NO_HIDINPUT) || hidpp->delayed_input)
|
||||
/* if the input nodes are already created, we can stop now */
|
||||
|
Loading…
Reference in New Issue
Block a user