Commit fef018d8 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'hid-for-linus-2024010801' of git://git.kernel.org/pub/scm/linux/kernel/git/hid/hid

Pull HID updates from Jiri Kosina:

 - assorted functional fixes for hid-steam ported from SteamOS betas
   (Vicki Pfau)

 - fix for custom sensor-hub sensors (hinge angle sensor and LISS
   sensors) not working (Yauhen Kharuzhy)

 - functional fix for handling Confidence in Wacom driver (Jason
   Gerecke)

 - support for Ilitek ili2901 touchscreen (Zhengqiao Xia)

 - power management fix for Wacom userspace battery exporting
   (Tatsunosuke Tobita)

 - rework of wait-for-reset in order to reduce the need for
   I2C_HID_QUIRK_NO_IRQ_AFTER_RESET qurk; the success rate is now 50%
   better, but there are still further improvements to be made (Hans de
   Goede)

 - greatly improved coverage of Tablets in hid-selftests (Benjamin
   Tissoires)

 - support for Nintendo NSO controllers -- SNES, Genesis and N64 (Ryan
   McClelland)

 - support for controlling mcp2200 GPIOs (Johannes Roith)

 - power management improvement for EHL OOB wakeup in intel-ish
   (Kai-Heng Feng)

 - other assorted device-specific fixes and code cleanups

* tag 'hid-for-linus-2024010801' of git://git.kernel.org/pub/scm/linux/kernel/git/hid/hid: (53 commits)
  HID: amd_sfh: Add a new interface for exporting ALS data
  HID: amd_sfh: Add a new interface for exporting HPD data
  HID: amd_sfh: rename float_to_int() to amd_sfh_float_to_int()
  HID: i2c-hid: elan: Add ili2901 timing
  dt-bindings: HID: i2c-hid: elan: Introduce Ilitek ili2901
  HID: bpf: make bus_type const in struct hid_bpf_ops
  HID: make ishtp_cl_bus_type const
  HID: make hid_bus_type const
  HID: hid-steam: Add gamepad-only mode switched to by holding options
  HID: hid-steam: Better handling of serial number length
  HID: hid-steam: Update list of identifiers from SDL
  HID: hid-steam: Make client_opened a counter
  HID: hid-steam: Clean up locking
  HID: hid-steam: Disable watchdog instead of using a heartbeat
  HID: hid-steam: Avoid overwriting smoothing parameter
  HID: magicmouse: fix kerneldoc for struct magicmouse_sc
  HID: sensor-hub: Enable hid core report processing for all devices
  HID: wacom: Add additional tests of confidence behavior
  HID: wacom: Correct behavior when processing some confidence == false touches
  HID: nintendo: add support for nso controllers
  ...
parents d97a7842 0b43615a
...@@ -18,8 +18,9 @@ allOf: ...@@ -18,8 +18,9 @@ allOf:
properties: properties:
compatible: compatible:
items: enum:
- const: elan,ekth6915 - elan,ekth6915
- ilitek,ili2901
reg: reg:
const: 0x10 const: 0x10
......
...@@ -761,14 +761,15 @@ config HID_MULTITOUCH ...@@ -761,14 +761,15 @@ config HID_MULTITOUCH
module will be called hid-multitouch. module will be called hid-multitouch.
config HID_NINTENDO config HID_NINTENDO
tristate "Nintendo Joy-Con and Pro Controller support" tristate "Nintendo Joy-Con, NSO, and Pro Controller support"
depends on NEW_LEDS depends on NEW_LEDS
depends on LEDS_CLASS depends on LEDS_CLASS
select POWER_SUPPLY select POWER_SUPPLY
help help
Adds support for the Nintendo Switch Joy-Cons and Pro Controller. Adds support for the Nintendo Switch Joy-Cons, NSO, Pro Controller.
All controllers support bluetooth, and the Pro Controller also supports All controllers support bluetooth, and the Pro Controller also supports
its USB mode. its USB mode. This also includes support for the Nintendo Switch Online
Controllers which include the Genesis, SNES, and N64 controllers.
To compile this driver as a module, choose M here: the To compile this driver as a module, choose M here: the
module will be called hid-nintendo. module will be called hid-nintendo.
...@@ -779,9 +780,9 @@ config NINTENDO_FF ...@@ -779,9 +780,9 @@ config NINTENDO_FF
select INPUT_FF_MEMLESS select INPUT_FF_MEMLESS
help help
Say Y here if you have a Nintendo Switch controller and want to enable Say Y here if you have a Nintendo Switch controller and want to enable
force feedback support for it. This works for both joy-cons and the pro force feedback support for it. This works for both joy-cons, the pro
controller. For the pro controller, both rumble motors can be controlled controller, and the NSO N64 controller. For the pro controller, both
individually. rumble motors can be controlled individually.
config HID_NTI config HID_NTI
tristate "NTI keyboard adapters" tristate "NTI keyboard adapters"
...@@ -1296,6 +1297,15 @@ config HID_ALPS ...@@ -1296,6 +1297,15 @@ config HID_ALPS
Say Y here if you have a Alps touchpads over i2c-hid or usbhid Say Y here if you have a Alps touchpads over i2c-hid or usbhid
and want support for its special functionalities. and want support for its special functionalities.
config HID_MCP2200
tristate "Microchip MCP2200 HID USB-to-GPIO bridge"
depends on USB_HID && GPIOLIB
help
Provides GPIO functionality over USB-HID through MCP2200 device.
To compile this driver as a module, choose M here: the module
will be called hid-mcp2200.ko.
config HID_MCP2221 config HID_MCP2221
tristate "Microchip MCP2221 HID USB-to-I2C/SMbus host support" tristate "Microchip MCP2221 HID USB-to-I2C/SMbus host support"
depends on USB_HID && I2C depends on USB_HID && I2C
......
...@@ -79,6 +79,7 @@ obj-$(CONFIG_HID_LOGITECH_HIDPP) += hid-logitech-hidpp.o ...@@ -79,6 +79,7 @@ obj-$(CONFIG_HID_LOGITECH_HIDPP) += hid-logitech-hidpp.o
obj-$(CONFIG_HID_MACALLY) += hid-macally.o obj-$(CONFIG_HID_MACALLY) += hid-macally.o
obj-$(CONFIG_HID_MAGICMOUSE) += hid-magicmouse.o obj-$(CONFIG_HID_MAGICMOUSE) += hid-magicmouse.o
obj-$(CONFIG_HID_MALTRON) += hid-maltron.o obj-$(CONFIG_HID_MALTRON) += hid-maltron.o
obj-$(CONFIG_HID_MCP2200) += hid-mcp2200.o
obj-$(CONFIG_HID_MCP2221) += hid-mcp2221.o obj-$(CONFIG_HID_MCP2221) += hid-mcp2221.o
obj-$(CONFIG_HID_MAYFLASH) += hid-mf.o obj-$(CONFIG_HID_MAYFLASH) += hid-mf.o
obj-$(CONFIG_HID_MEGAWORLD_FF) += hid-megaworld.o obj-$(CONFIG_HID_MEGAWORLD_FF) += hid-megaworld.o
......
...@@ -37,6 +37,11 @@ struct amd_mp2_sensor_info { ...@@ -37,6 +37,11 @@ struct amd_mp2_sensor_info {
dma_addr_t dma_address; dma_addr_t dma_address;
}; };
struct sfh_dev_status {
bool is_hpd_present;
bool is_als_present;
};
struct amd_mp2_dev { struct amd_mp2_dev {
struct pci_dev *pdev; struct pci_dev *pdev;
struct amdtp_cl_data *cl_data; struct amdtp_cl_data *cl_data;
...@@ -47,6 +52,7 @@ struct amd_mp2_dev { ...@@ -47,6 +52,7 @@ struct amd_mp2_dev {
struct amd_input_data in_data; struct amd_input_data in_data;
/* mp2 active control status */ /* mp2 active control status */
u32 mp2_acs; u32 mp2_acs;
struct sfh_dev_status dev_en;
}; };
struct amd_mp2_ops { struct amd_mp2_ops {
......
...@@ -132,7 +132,7 @@ static void get_common_inputs(struct common_input_property *common, int report_i ...@@ -132,7 +132,7 @@ static void get_common_inputs(struct common_input_property *common, int report_i
common->event_type = HID_USAGE_SENSOR_EVENT_DATA_UPDATED_ENUM; common->event_type = HID_USAGE_SENSOR_EVENT_DATA_UPDATED_ENUM;
} }
static int float_to_int(u32 flt32_val) int amd_sfh_float_to_int(u32 flt32_val)
{ {
int fraction, shift, mantissa, sign, exp, zeropre; int fraction, shift, mantissa, sign, exp, zeropre;
...@@ -201,9 +201,9 @@ static u8 get_input_rep(u8 current_index, int sensor_idx, int report_id, ...@@ -201,9 +201,9 @@ static u8 get_input_rep(u8 current_index, int sensor_idx, int report_id,
OFFSET_SENSOR_DATA_DEFAULT; OFFSET_SENSOR_DATA_DEFAULT;
memcpy_fromio(&accel_data, sensoraddr, sizeof(struct sfh_accel_data)); memcpy_fromio(&accel_data, sensoraddr, sizeof(struct sfh_accel_data));
get_common_inputs(&acc_input.common_property, report_id); get_common_inputs(&acc_input.common_property, report_id);
acc_input.in_accel_x_value = float_to_int(accel_data.acceldata.x) / 100; acc_input.in_accel_x_value = amd_sfh_float_to_int(accel_data.acceldata.x) / 100;
acc_input.in_accel_y_value = float_to_int(accel_data.acceldata.y) / 100; acc_input.in_accel_y_value = amd_sfh_float_to_int(accel_data.acceldata.y) / 100;
acc_input.in_accel_z_value = float_to_int(accel_data.acceldata.z) / 100; acc_input.in_accel_z_value = amd_sfh_float_to_int(accel_data.acceldata.z) / 100;
memcpy(input_report, &acc_input, sizeof(acc_input)); memcpy(input_report, &acc_input, sizeof(acc_input));
report_size = sizeof(acc_input); report_size = sizeof(acc_input);
break; break;
...@@ -212,9 +212,9 @@ static u8 get_input_rep(u8 current_index, int sensor_idx, int report_id, ...@@ -212,9 +212,9 @@ static u8 get_input_rep(u8 current_index, int sensor_idx, int report_id,
OFFSET_SENSOR_DATA_DEFAULT; OFFSET_SENSOR_DATA_DEFAULT;
memcpy_fromio(&gyro_data, sensoraddr, sizeof(struct sfh_gyro_data)); memcpy_fromio(&gyro_data, sensoraddr, sizeof(struct sfh_gyro_data));
get_common_inputs(&gyro_input.common_property, report_id); get_common_inputs(&gyro_input.common_property, report_id);
gyro_input.in_angel_x_value = float_to_int(gyro_data.gyrodata.x) / 1000; gyro_input.in_angel_x_value = amd_sfh_float_to_int(gyro_data.gyrodata.x) / 1000;
gyro_input.in_angel_y_value = float_to_int(gyro_data.gyrodata.y) / 1000; gyro_input.in_angel_y_value = amd_sfh_float_to_int(gyro_data.gyrodata.y) / 1000;
gyro_input.in_angel_z_value = float_to_int(gyro_data.gyrodata.z) / 1000; gyro_input.in_angel_z_value = amd_sfh_float_to_int(gyro_data.gyrodata.z) / 1000;
memcpy(input_report, &gyro_input, sizeof(gyro_input)); memcpy(input_report, &gyro_input, sizeof(gyro_input));
report_size = sizeof(gyro_input); report_size = sizeof(gyro_input);
break; break;
...@@ -223,9 +223,9 @@ static u8 get_input_rep(u8 current_index, int sensor_idx, int report_id, ...@@ -223,9 +223,9 @@ static u8 get_input_rep(u8 current_index, int sensor_idx, int report_id,
OFFSET_SENSOR_DATA_DEFAULT; OFFSET_SENSOR_DATA_DEFAULT;
memcpy_fromio(&mag_data, sensoraddr, sizeof(struct sfh_mag_data)); memcpy_fromio(&mag_data, sensoraddr, sizeof(struct sfh_mag_data));
get_common_inputs(&magno_input.common_property, report_id); get_common_inputs(&magno_input.common_property, report_id);
magno_input.in_magno_x = float_to_int(mag_data.magdata.x) / 100; magno_input.in_magno_x = amd_sfh_float_to_int(mag_data.magdata.x) / 100;
magno_input.in_magno_y = float_to_int(mag_data.magdata.y) / 100; magno_input.in_magno_y = amd_sfh_float_to_int(mag_data.magdata.y) / 100;
magno_input.in_magno_z = float_to_int(mag_data.magdata.z) / 100; magno_input.in_magno_z = amd_sfh_float_to_int(mag_data.magdata.z) / 100;
magno_input.in_magno_accuracy = mag_data.accuracy / 100; magno_input.in_magno_accuracy = mag_data.accuracy / 100;
memcpy(input_report, &magno_input, sizeof(magno_input)); memcpy(input_report, &magno_input, sizeof(magno_input));
report_size = sizeof(magno_input); report_size = sizeof(magno_input);
...@@ -235,13 +235,15 @@ static u8 get_input_rep(u8 current_index, int sensor_idx, int report_id, ...@@ -235,13 +235,15 @@ static u8 get_input_rep(u8 current_index, int sensor_idx, int report_id,
OFFSET_SENSOR_DATA_DEFAULT; OFFSET_SENSOR_DATA_DEFAULT;
memcpy_fromio(&als_data, sensoraddr, sizeof(struct sfh_als_data)); memcpy_fromio(&als_data, sensoraddr, sizeof(struct sfh_als_data));
get_common_inputs(&als_input.common_property, report_id); get_common_inputs(&als_input.common_property, report_id);
als_input.illuminance_value = float_to_int(als_data.lux); als_input.illuminance_value = amd_sfh_float_to_int(als_data.lux);
memcpy_fromio(&binfo, mp2->vsbase, sizeof(struct sfh_base_info)); memcpy_fromio(&binfo, mp2->vsbase, sizeof(struct sfh_base_info));
if (binfo.sbase.s_prop[ALS_IDX].sf.feat & 0x2) { if (binfo.sbase.s_prop[ALS_IDX].sf.feat & 0x2) {
als_input.light_color_temp = als_data.light_color_temp; als_input.light_color_temp = als_data.light_color_temp;
als_input.chromaticity_x_value = float_to_int(als_data.chromaticity_x); als_input.chromaticity_x_value =
als_input.chromaticity_y_value = float_to_int(als_data.chromaticity_y); amd_sfh_float_to_int(als_data.chromaticity_x);
als_input.chromaticity_y_value =
amd_sfh_float_to_int(als_data.chromaticity_y);
} }
report_size = sizeof(als_input); report_size = sizeof(als_input);
......
...@@ -73,6 +73,15 @@ static int amd_sfh_hid_client_deinit(struct amd_mp2_dev *privdata) ...@@ -73,6 +73,15 @@ static int amd_sfh_hid_client_deinit(struct amd_mp2_dev *privdata)
int i, status; int i, status;
for (i = 0; i < cl_data->num_hid_devices; i++) { for (i = 0; i < cl_data->num_hid_devices; i++) {
switch (cl_data->sensor_idx[i]) {
case HPD_IDX:
privdata->dev_en.is_hpd_present = false;
break;
case ALS_IDX:
privdata->dev_en.is_als_present = false;
break;
}
if (cl_data->sensor_sts[i] == SENSOR_ENABLED) { if (cl_data->sensor_sts[i] == SENSOR_ENABLED) {
privdata->mp2_ops->stop(privdata, cl_data->sensor_idx[i]); privdata->mp2_ops->stop(privdata, cl_data->sensor_idx[i]);
status = amd_sfh_wait_for_response status = amd_sfh_wait_for_response
...@@ -178,6 +187,14 @@ static int amd_sfh1_1_hid_client_init(struct amd_mp2_dev *privdata) ...@@ -178,6 +187,14 @@ static int amd_sfh1_1_hid_client_init(struct amd_mp2_dev *privdata)
rc = amdtp_hid_probe(i, cl_data); rc = amdtp_hid_probe(i, cl_data);
if (rc) if (rc)
goto cleanup; goto cleanup;
switch (cl_data->sensor_idx[i]) {
case HPD_IDX:
privdata->dev_en.is_hpd_present = true;
break;
case ALS_IDX:
privdata->dev_en.is_als_present = true;
break;
}
} }
dev_dbg(dev, "sid 0x%x (%s) status 0x%x\n", dev_dbg(dev, "sid 0x%x (%s) status 0x%x\n",
cl_data->sensor_idx[i], get_sensor_name(cl_data->sensor_idx[i]), cl_data->sensor_idx[i], get_sensor_name(cl_data->sensor_idx[i]),
...@@ -259,6 +276,7 @@ static void amd_mp2_pci_remove(void *privdata) ...@@ -259,6 +276,7 @@ static void amd_mp2_pci_remove(void *privdata)
{ {
struct amd_mp2_dev *mp2 = privdata; struct amd_mp2_dev *mp2 = privdata;
sfh_deinit_emp2();
amd_sfh_hid_client_deinit(privdata); amd_sfh_hid_client_deinit(privdata);
mp2->mp2_ops->stop_all(mp2); mp2->mp2_ops->stop_all(mp2);
pci_intx(mp2->pdev, false); pci_intx(mp2->pdev, false);
...@@ -311,12 +329,14 @@ int amd_sfh1_1_init(struct amd_mp2_dev *mp2) ...@@ -311,12 +329,14 @@ int amd_sfh1_1_init(struct amd_mp2_dev *mp2)
rc = amd_sfh_irq_init(mp2); rc = amd_sfh_irq_init(mp2);
if (rc) { if (rc) {
sfh_deinit_emp2();
dev_err(dev, "amd_sfh_irq_init failed\n"); dev_err(dev, "amd_sfh_irq_init failed\n");
return rc; return rc;
} }
rc = amd_sfh1_1_hid_client_init(mp2); rc = amd_sfh1_1_hid_client_init(mp2);
if (rc) { if (rc) {
sfh_deinit_emp2();
dev_err(dev, "amd_sfh1_1_hid_client_init failed\n"); dev_err(dev, "amd_sfh1_1_hid_client_init failed\n");
return rc; return rc;
} }
......
...@@ -7,11 +7,14 @@ ...@@ -7,11 +7,14 @@
* *
* Author: Basavaraj Natikar <Basavaraj.Natikar@amd.com> * Author: Basavaraj Natikar <Basavaraj.Natikar@amd.com>
*/ */
#include <linux/amd-pmf-io.h>
#include <linux/io-64-nonatomic-lo-hi.h> #include <linux/io-64-nonatomic-lo-hi.h>
#include <linux/iopoll.h> #include <linux/iopoll.h>
#include "amd_sfh_interface.h" #include "amd_sfh_interface.h"
static struct amd_mp2_dev *emp2;
static int amd_sfh_wait_response(struct amd_mp2_dev *mp2, u8 sid, u32 cmd_id) static int amd_sfh_wait_response(struct amd_mp2_dev *mp2, u8 sid, u32 cmd_id)
{ {
struct sfh_cmd_response cmd_resp; struct sfh_cmd_response cmd_resp;
...@@ -73,7 +76,63 @@ static struct amd_mp2_ops amd_sfh_ops = { ...@@ -73,7 +76,63 @@ static struct amd_mp2_ops amd_sfh_ops = {
.response = amd_sfh_wait_response, .response = amd_sfh_wait_response,
}; };
void sfh_deinit_emp2(void)
{
emp2 = NULL;
}
void sfh_interface_init(struct amd_mp2_dev *mp2) void sfh_interface_init(struct amd_mp2_dev *mp2)
{ {
mp2->mp2_ops = &amd_sfh_ops; mp2->mp2_ops = &amd_sfh_ops;
emp2 = mp2;
}
static int amd_sfh_hpd_info(u8 *user_present)
{
struct hpd_status hpdstatus;
if (!user_present)
return -EINVAL;
if (!emp2 || !emp2->dev_en.is_hpd_present)
return -ENODEV;
hpdstatus.val = readl(emp2->mmio + AMD_C2P_MSG(4));
*user_present = hpdstatus.shpd.presence;
return 0;
}
static int amd_sfh_als_info(u32 *ambient_light)
{
struct sfh_als_data als_data;
void __iomem *sensoraddr;
if (!ambient_light)
return -EINVAL;
if (!emp2 || !emp2->dev_en.is_als_present)
return -ENODEV;
sensoraddr = emp2->vsbase +
(ALS_IDX * SENSOR_DATA_MEM_SIZE_DEFAULT) +
OFFSET_SENSOR_DATA_DEFAULT;
memcpy_fromio(&als_data, sensoraddr, sizeof(struct sfh_als_data));
*ambient_light = amd_sfh_float_to_int(als_data.lux);
return 0;
}
int amd_get_sfh_info(struct amd_sfh_info *sfh_info, enum sfh_message_type op)
{
if (sfh_info) {
switch (op) {
case MT_HPD:
return amd_sfh_hpd_info(&sfh_info->user_present);
case MT_ALS:
return amd_sfh_als_info(&sfh_info->ambient_light);
}
}
return -EINVAL;
} }
EXPORT_SYMBOL_GPL(amd_get_sfh_info);
...@@ -165,5 +165,7 @@ struct hpd_status { ...@@ -165,5 +165,7 @@ struct hpd_status {
}; };
void sfh_interface_init(struct amd_mp2_dev *mp2); void sfh_interface_init(struct amd_mp2_dev *mp2);
void sfh_deinit_emp2(void);
void amd_sfh1_1_set_desc_ops(struct amd_mp2_ops *mp2_ops); void amd_sfh1_1_set_desc_ops(struct amd_mp2_ops *mp2_ops);
int amd_sfh_float_to_int(u32 flt32_val);
#endif #endif
...@@ -2749,7 +2749,7 @@ static int hid_uevent(const struct device *dev, struct kobj_uevent_env *env) ...@@ -2749,7 +2749,7 @@ static int hid_uevent(const struct device *dev, struct kobj_uevent_env *env)
return 0; return 0;
} }
struct bus_type hid_bus_type = { const struct bus_type hid_bus_type = {
.name = "hid", .name = "hid",
.dev_groups = hid_dev_groups, .dev_groups = hid_dev_groups,
.drv_groups = hid_drv_groups, .drv_groups = hid_drv_groups,
......
...@@ -916,6 +916,7 @@ ...@@ -916,6 +916,7 @@
#define USB_DEVICE_ID_PICK16F1454 0x0042 #define USB_DEVICE_ID_PICK16F1454 0x0042
#define USB_DEVICE_ID_PICK16F1454_V2 0xf2f7 #define USB_DEVICE_ID_PICK16F1454_V2 0xf2f7
#define USB_DEVICE_ID_LUXAFOR 0xf372 #define USB_DEVICE_ID_LUXAFOR 0xf372
#define USB_DEVICE_ID_MCP2200 0x00df
#define USB_DEVICE_ID_MCP2221 0x00dd #define USB_DEVICE_ID_MCP2221 0x00dd
#define USB_VENDOR_ID_MICROSOFT 0x045e #define USB_VENDOR_ID_MICROSOFT 0x045e
...@@ -987,7 +988,10 @@ ...@@ -987,7 +988,10 @@
#define USB_DEVICE_ID_NINTENDO_JOYCONL 0x2006 #define USB_DEVICE_ID_NINTENDO_JOYCONL 0x2006
#define USB_DEVICE_ID_NINTENDO_JOYCONR 0x2007 #define USB_DEVICE_ID_NINTENDO_JOYCONR 0x2007
#define USB_DEVICE_ID_NINTENDO_PROCON 0x2009 #define USB_DEVICE_ID_NINTENDO_PROCON 0x2009
#define USB_DEVICE_ID_NINTENDO_CHRGGRIP 0x200E #define USB_DEVICE_ID_NINTENDO_CHRGGRIP 0x200e
#define USB_DEVICE_ID_NINTENDO_SNESCON 0x2017
#define USB_DEVICE_ID_NINTENDO_GENCON 0x201e
#define USB_DEVICE_ID_NINTENDO_N64CON 0x2019
#define USB_VENDOR_ID_NOVATEK 0x0603 #define USB_VENDOR_ID_NOVATEK 0x0603
#define USB_DEVICE_ID_NOVATEK_PCT 0x0600 #define USB_DEVICE_ID_NOVATEK_PCT 0x0600
......
...@@ -120,6 +120,9 @@ MODULE_PARM_DESC(report_undeciphered, "Report undeciphered multi-touch state fie ...@@ -120,6 +120,9 @@ MODULE_PARM_DESC(report_undeciphered, "Report undeciphered multi-touch state fie
* @scroll_jiffies: Time of last scroll motion. * @scroll_jiffies: Time of last scroll motion.
* @touches: Most recent data for a touch, indexed by tracking ID. * @touches: Most recent data for a touch, indexed by tracking ID.
* @tracking_ids: Mapping of current touch input data to @touches. * @tracking_ids: Mapping of current touch input data to @touches.
* @hdev: Pointer to the underlying HID device.
* @work: Workqueue to handle initialization retry for quirky devices.
* @battery_timer: Timer for obtaining battery level information.
*/ */
struct magicmouse_sc { struct magicmouse_sc {
struct input_dev *input; struct input_dev *input;
......
// SPDX-License-Identifier: GPL-2.0-only
/*
* MCP2200 - Microchip USB to GPIO bridge
*
* Copyright (c) 2023, Johannes Roith <johannes@gnu-linux.rocks>
*
* Datasheet: https://ww1.microchip.com/downloads/en/DeviceDoc/22228A.pdf
* App Note for HID: https://ww1.microchip.com/downloads/en/DeviceDoc/93066A.pdf
*/
#include <linux/completion.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/gpio/driver.h>
#include <linux/hid.h>
#include <linux/hidraw.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include "hid-ids.h"
/* Commands codes in a raw output report */
#define SET_CLEAR_OUTPUTS 0x08
#define CONFIGURE 0x10
#define READ_EE 0x20
#define WRITE_EE 0x40
#define READ_ALL 0x80
/* MCP GPIO direction encoding */
enum MCP_IO_DIR {
MCP2200_DIR_OUT = 0x00,
MCP2200_DIR_IN = 0x01,
};
/* Altternative pin assignments */
#define TXLED 2
#define RXLED 3
#define USBCFG 6
#define SSPND 7
#define MCP_NGPIO 8
/* CMD to set or clear a GPIO output */
struct mcp_set_clear_outputs {
u8 cmd;
u8 dummys1[10];
u8 set_bmap;
u8 clear_bmap;
u8 dummys2[3];
} __packed;
/* CMD to configure the IOs */
struct mcp_configure {
u8 cmd;
u8 dummys1[3];
u8 io_bmap;
u8 config_alt_pins;
u8 io_default_val_bmap;
u8 config_alt_options;
u8 baud_h;
u8 baud_l;
u8 dummys2[6];
} __packed;
/* CMD to read all parameters */
struct mcp_read_all {
u8 cmd;
u8 dummys[15];
} __packed;
/* Response to the read all cmd */
struct mcp_read_all_resp {
u8 cmd;
u8 eep_addr;
u8 dummy;
u8 eep_val;
u8 io_bmap;
u8 config_alt_pins;
u8 io_default_val_bmap;
u8 config_alt_options;
u8 baud_h;
u8 baud_l;
u8 io_port_val_bmap;
u8 dummys[5];
} __packed;
struct mcp2200 {
struct hid_device *hdev;
struct mutex lock;
struct completion wait_in_report;
u8 gpio_dir;
u8 gpio_val;
u8 gpio_inval;
u8 baud_h;
u8 baud_l;
u8 config_alt_pins;
u8 gpio_reset_val;
u8 config_alt_options;
int status;
struct gpio_chip gc;
u8 hid_report[16];
};
/* this executes the READ_ALL cmd */
static int mcp_cmd_read_all(struct mcp2200 *mcp)
{
struct mcp_read_all *read_all;
int len, t;
reinit_completion(&mcp->wait_in_report);
mutex_lock(&mcp->lock);
read_all = (struct mcp_read_all *) mcp->hid_report;
read_all->cmd = READ_ALL;
len = hid_hw_output_report(mcp->hdev, (u8 *) read_all,
sizeof(struct mcp_read_all));
mutex_unlock(&mcp->lock);
if (len != sizeof(struct mcp_read_all))
return -EINVAL;
t = wait_for_completion_timeout(&mcp->wait_in_report,
msecs_to_jiffies(4000));
if (!t)
return -ETIMEDOUT;
/* return status, negative value if wrong response was received */
return mcp->status;
}
static void mcp_set_multiple(struct gpio_chip *gc, unsigned long *mask,
unsigned long *bits)
{
struct mcp2200 *mcp = gpiochip_get_data(gc);
u8 value;
int status;
struct mcp_set_clear_outputs *cmd;
mutex_lock(&mcp->lock);
cmd = (struct mcp_set_clear_outputs *) mcp->hid_report;
value = mcp->gpio_val & ~*mask;
value |= (*mask & *bits);
cmd->cmd = SET_CLEAR_OUTPUTS;
cmd->set_bmap = value;
cmd->clear_bmap = ~(value);
status = hid_hw_output_report(mcp->hdev, (u8 *) cmd,
sizeof(struct mcp_set_clear_outputs));
if (status == sizeof(struct mcp_set_clear_outputs))
mcp->gpio_val = value;
mutex_unlock(&mcp->lock);
}
static void mcp_set(struct gpio_chip *gc, unsigned int gpio_nr, int value)
{
unsigned long mask = 1 << gpio_nr;
unsigned long bmap_value = value << gpio_nr;
mcp_set_multiple(gc, &mask, &bmap_value);
}
static int mcp_get_multiple(struct gpio_chip *gc, unsigned long *mask,
unsigned long *bits)
{
u32 val;
struct mcp2200 *mcp = gpiochip_get_data(gc);
int status;
status = mcp_cmd_read_all(mcp);
if (status)
return status;
val = mcp->gpio_inval;
*bits = (val & *mask);
return 0;
}
static int mcp_get(struct gpio_chip *gc, unsigned int gpio_nr)
{
unsigned long mask = 0, bits = 0;
mask = (1 << gpio_nr);
mcp_get_multiple(gc, &mask, &bits);
return bits > 0;
}
static int mcp_get_direction(struct gpio_chip *gc, unsigned int gpio_nr)
{
struct mcp2200 *mcp = gpiochip_get_data(gc);
return (mcp->gpio_dir & (MCP2200_DIR_IN << gpio_nr))
? GPIO_LINE_DIRECTION_IN : GPIO_LINE_DIRECTION_OUT;
}
static int mcp_set_direction(struct gpio_chip *gc, unsigned int gpio_nr,
enum MCP_IO_DIR io_direction)
{
struct mcp2200 *mcp = gpiochip_get_data(gc);
struct mcp_configure *conf;
int status;
/* after the configure cmd we will need to set the outputs again */
unsigned long mask = ~(mcp->gpio_dir); /* only set outputs */
unsigned long bits = mcp->gpio_val;
/* Offsets of alternative pins in config_alt_pins, 0 is not used */
u8 alt_pin_conf[8] = {SSPND, USBCFG, 0, 0, 0, 0, RXLED, TXLED};
u8 config_alt_pins = mcp->config_alt_pins;
/* Read in the reset baudrate first, we need it later */
status = mcp_cmd_read_all(mcp);
if (status != 0)
return status;
mutex_lock(&mcp->lock);
conf = (struct mcp_configure *) mcp->hid_report;
/* configure will reset the chip! */
conf->cmd = CONFIGURE;
conf->io_bmap = (mcp->gpio_dir & ~(1 << gpio_nr))
| (io_direction << gpio_nr);
/* Don't overwrite the reset parameters */
conf->baud_h = mcp->baud_h;
conf->baud_l = mcp->baud_l;
conf->config_alt_options = mcp->config_alt_options;
conf->io_default_val_bmap = mcp->gpio_reset_val;
/* Adjust alt. func if necessary */
if (alt_pin_conf[gpio_nr])
config_alt_pins &= ~(1 << alt_pin_conf[gpio_nr]);
conf->config_alt_pins = config_alt_pins;
status = hid_hw_output_report(mcp->hdev, (u8 *) conf,
sizeof(struct mcp_set_clear_outputs));
if (status == sizeof(struct mcp_set_clear_outputs)) {
mcp->gpio_dir = conf->io_bmap;
mcp->config_alt_pins = config_alt_pins;
} else {
mutex_unlock(&mcp->lock);
return -EIO;
}
mutex_unlock(&mcp->lock);
/* Configure CMD will clear all IOs -> rewrite them */
mcp_set_multiple(gc, &mask, &bits);
return 0;
}
static int mcp_direction_input(struct gpio_chip *gc, unsigned int gpio_nr)
{
return mcp_set_direction(gc, gpio_nr, MCP2200_DIR_IN);
}
static int mcp_direction_output(struct gpio_chip *gc, unsigned int gpio_nr,
int value)
{
int ret;
unsigned long mask, bmap_value;
mask = 1 << gpio_nr;
bmap_value = value << gpio_nr;
ret = mcp_set_direction(gc, gpio_nr, MCP2200_DIR_OUT);
if (!ret)
mcp_set_multiple(gc, &mask, &bmap_value);
return ret;
}
static const struct gpio_chip template_chip = {
.label = "mcp2200",
.owner = THIS_MODULE,
.get_direction = mcp_get_direction,
.direction_input = mcp_direction_input,
.direction_output = mcp_direction_output,
.set = mcp_set,
.set_multiple = mcp_set_multiple,
.get = mcp_get,
.get_multiple = mcp_get_multiple,
.base = -1,
.ngpio = MCP_NGPIO,
.can_sleep = true,
};
/*
* MCP2200 uses interrupt endpoint for input reports. This function
* is called by HID layer when it receives i/p report from mcp2200,
* which is actually a response to the previously sent command.
*/
static int mcp2200_raw_event(struct hid_device *hdev, struct hid_report *report,
u8 *data, int size)
{
struct mcp2200 *mcp = hid_get_drvdata(hdev);
struct mcp_read_all_resp *all_resp;
switch (data[0]) {
case READ_ALL:
all_resp = (struct mcp_read_all_resp *) data;
mcp->status = 0;
mcp->gpio_inval = all_resp->io_port_val_bmap;
mcp->baud_h = all_resp->baud_h;
mcp->baud_l = all_resp->baud_l;
mcp->gpio_reset_val = all_resp->io_default_val_bmap;
mcp->config_alt_pins = all_resp->config_alt_pins;
mcp->config_alt_options = all_resp->config_alt_options;
break;
default:
mcp->status = -EIO;
break;
}
complete(&mcp->wait_in_report);
return 0;
}
static int mcp2200_probe(struct hid_device *hdev, const struct hid_device_id *id)
{
int ret;
struct mcp2200 *mcp;
mcp = devm_kzalloc(&hdev->dev, sizeof(*mcp), GFP_KERNEL);
if (!mcp)
return -ENOMEM;
ret = hid_parse(hdev);
if (ret) {
hid_err(hdev, "can't parse reports\n");
return ret;
}
ret = hid_hw_start(hdev, 0);
if (ret) {
hid_err(hdev, "can't start hardware\n");
return ret;
}
hid_info(hdev, "USB HID v%x.%02x Device [%s] on %s\n", hdev->version >> 8,
hdev->version & 0xff, hdev->name, hdev->phys);
ret = hid_hw_open(hdev);
if (ret) {
hid_err(hdev, "can't open device\n");
hid_hw_stop(hdev);
return ret;
}
mutex_init(&mcp->lock);
init_completion(&mcp->wait_in_report);
hid_set_drvdata(hdev, mcp);
mcp->hdev = hdev;
mcp->gc = template_chip;
mcp->gc.parent = &hdev->dev;
ret = devm_gpiochip_add_data(&hdev->dev, &mcp->gc, mcp);
if (ret < 0) {
hid_err(hdev, "Unable to register gpiochip\n");
hid_hw_close(hdev);
hid_hw_stop(hdev);
return ret;
}
return 0;
}
static void mcp2200_remove(struct hid_device *hdev)
{
hid_hw_close(hdev);
hid_hw_stop(hdev);
}
static const struct hid_device_id mcp2200_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_MCP2200) },
{ }
};
MODULE_DEVICE_TABLE(hid, mcp2200_devices);
static struct hid_driver mcp2200_driver = {
.name = "mcp2200",
.id_table = mcp2200_devices,
.probe = mcp2200_probe,
.remove = mcp2200_remove,
.raw_event = mcp2200_raw_event,
};
/* Register with HID core */
module_hid_driver(mcp2200_driver);
MODULE_AUTHOR("Johannes Roith <johannes@gnu-linux.rocks>");
MODULE_DESCRIPTION("MCP2200 Microchip HID USB to GPIO bridge");
MODULE_LICENSE("GPL");
...@@ -49,6 +49,7 @@ enum { ...@@ -49,6 +49,7 @@ enum {
MCP2221_I2C_MASK_ADDR_NACK = 0x40, MCP2221_I2C_MASK_ADDR_NACK = 0x40,
MCP2221_I2C_WRADDRL_SEND = 0x21, MCP2221_I2C_WRADDRL_SEND = 0x21,
MCP2221_I2C_ADDR_NACK = 0x25, MCP2221_I2C_ADDR_NACK = 0x25,
MCP2221_I2C_READ_PARTIAL = 0x54,
MCP2221_I2C_READ_COMPL = 0x55, MCP2221_I2C_READ_COMPL = 0x55,
MCP2221_ALT_F_NOT_GPIOV = 0xEE, MCP2221_ALT_F_NOT_GPIOV = 0xEE,
MCP2221_ALT_F_NOT_GPIOD = 0xEF, MCP2221_ALT_F_NOT_GPIOD = 0xEF,
...@@ -187,6 +188,25 @@ static int mcp_cancel_last_cmd(struct mcp2221 *mcp) ...@@ -187,6 +188,25 @@ static int mcp_cancel_last_cmd(struct mcp2221 *mcp)
return mcp_send_data_req_status(mcp, mcp->txbuf, 8); return mcp_send_data_req_status(mcp, mcp->txbuf, 8);
} }
/* Check if the last command succeeded or failed and return the result.
* If the command did fail, cancel that command which will free the i2c bus.
*/
static int mcp_chk_last_cmd_status_free_bus(struct mcp2221 *mcp)
{
int ret;
ret = mcp_chk_last_cmd_status(mcp);
if (ret) {
/* The last command was a failure.
* Send a cancel which will also free the bus.
*/
usleep_range(980, 1000);
mcp_cancel_last_cmd(mcp);
}
return ret;
}
static int mcp_set_i2c_speed(struct mcp2221 *mcp) static int mcp_set_i2c_speed(struct mcp2221 *mcp)
{ {
int ret; int ret;
...@@ -241,7 +261,7 @@ static int mcp_i2c_write(struct mcp2221 *mcp, ...@@ -241,7 +261,7 @@ static int mcp_i2c_write(struct mcp2221 *mcp,
usleep_range(980, 1000); usleep_range(980, 1000);
if (last_status) { if (last_status) {
ret = mcp_chk_last_cmd_status(mcp); ret = mcp_chk_last_cmd_status_free_bus(mcp);
if (ret) if (ret)
return ret; return ret;
} }
...@@ -278,6 +298,7 @@ static int mcp_i2c_smbus_read(struct mcp2221 *mcp, ...@@ -278,6 +298,7 @@ static int mcp_i2c_smbus_read(struct mcp2221 *mcp,
{ {
int ret; int ret;
u16 total_len; u16 total_len;
int retries = 0;
mcp->txbuf[0] = type; mcp->txbuf[0] = type;
if (msg) { if (msg) {
...@@ -301,19 +322,30 @@ static int mcp_i2c_smbus_read(struct mcp2221 *mcp, ...@@ -301,19 +322,30 @@ static int mcp_i2c_smbus_read(struct mcp2221 *mcp,
mcp->rxbuf_idx = 0; mcp->rxbuf_idx = 0;
do { do {
/* Wait for the data to be read by the device */
usleep_range(980, 1000);
memset(mcp->txbuf, 0, 4); memset(mcp->txbuf, 0, 4);
mcp->txbuf[0] = MCP2221_I2C_GET_DATA; mcp->txbuf[0] = MCP2221_I2C_GET_DATA;
ret = mcp_send_data_req_status(mcp, mcp->txbuf, 1); ret = mcp_send_data_req_status(mcp, mcp->txbuf, 1);
if (ret) if (ret) {
return ret; if (retries < 5) {
/* The data wasn't ready to read.
ret = mcp_chk_last_cmd_status(mcp); * Wait a bit longer and try again.
if (ret) */
usleep_range(90, 100);
retries++;
} else {
return ret; return ret;
}
} else {
retries = 0;
}
} while (mcp->rxbuf_idx < total_len);
usleep_range(980, 1000); usleep_range(980, 1000);
} while (mcp->rxbuf_idx < total_len); ret = mcp_chk_last_cmd_status_free_bus(mcp);
return ret; return ret;
} }
...@@ -328,11 +360,6 @@ static int mcp_i2c_xfer(struct i2c_adapter *adapter, ...@@ -328,11 +360,6 @@ static int mcp_i2c_xfer(struct i2c_adapter *adapter,
mutex_lock(&mcp->lock); mutex_lock(&mcp->lock);
/* Setting speed before every transaction is required for mcp2221 */
ret = mcp_set_i2c_speed(mcp);
if (ret)
goto exit;
if (num == 1) { if (num == 1) {
if (msgs->flags & I2C_M_RD) { if (msgs->flags & I2C_M_RD) {
ret = mcp_i2c_smbus_read(mcp, msgs, MCP2221_I2C_RD_DATA, ret = mcp_i2c_smbus_read(mcp, msgs, MCP2221_I2C_RD_DATA,
...@@ -417,9 +444,7 @@ static int mcp_smbus_write(struct mcp2221 *mcp, u16 addr, ...@@ -417,9 +444,7 @@ static int mcp_smbus_write(struct mcp2221 *mcp, u16 addr,
if (last_status) { if (last_status) {
usleep_range(980, 1000); usleep_range(980, 1000);
ret = mcp_chk_last_cmd_status(mcp); ret = mcp_chk_last_cmd_status_free_bus(mcp);
if (ret)
return ret;
} }
return ret; return ret;
...@@ -437,10 +462,6 @@ static int mcp_smbus_xfer(struct i2c_adapter *adapter, u16 addr, ...@@ -437,10 +462,6 @@ static int mcp_smbus_xfer(struct i2c_adapter *adapter, u16 addr,
mutex_lock(&mcp->lock); mutex_lock(&mcp->lock);
ret = mcp_set_i2c_speed(mcp);
if (ret)
goto exit;
switch (size) { switch (size) {
case I2C_SMBUS_QUICK: case I2C_SMBUS_QUICK:
...@@ -791,7 +812,8 @@ static int mcp2221_raw_event(struct hid_device *hdev, ...@@ -791,7 +812,8 @@ static int mcp2221_raw_event(struct hid_device *hdev,
mcp->status = -EIO; mcp->status = -EIO;
break; break;
} }
if (data[2] == MCP2221_I2C_READ_COMPL) { if (data[2] == MCP2221_I2C_READ_COMPL ||
data[2] == MCP2221_I2C_READ_PARTIAL) {
buf = mcp->rxbuf; buf = mcp->rxbuf;
memcpy(&buf[mcp->rxbuf_idx], &data[4], data[3]); memcpy(&buf[mcp->rxbuf_idx], &data[4], data[3]);
mcp->rxbuf_idx = mcp->rxbuf_idx + data[3]; mcp->rxbuf_idx = mcp->rxbuf_idx + data[3];
...@@ -1150,12 +1172,18 @@ static int mcp2221_probe(struct hid_device *hdev, ...@@ -1150,12 +1172,18 @@ static int mcp2221_probe(struct hid_device *hdev,
if (i2c_clk_freq < 50) if (i2c_clk_freq < 50)
i2c_clk_freq = 50; i2c_clk_freq = 50;
mcp->cur_i2c_clk_div = (12000000 / (i2c_clk_freq * 1000)) - 3; mcp->cur_i2c_clk_div = (12000000 / (i2c_clk_freq * 1000)) - 3;
ret = mcp_set_i2c_speed(mcp);
if (ret) {
hid_err(hdev, "can't set i2c speed: %d\n", ret);
return ret;
}
mcp->adapter.owner = THIS_MODULE; mcp->adapter.owner = THIS_MODULE;
mcp->adapter.class = I2C_CLASS_HWMON; mcp->adapter.class = I2C_CLASS_HWMON;
mcp->adapter.algo = &mcp_i2c_algo; mcp->adapter.algo = &mcp_i2c_algo;
mcp->adapter.retries = 1; mcp->adapter.retries = 1;
mcp->adapter.dev.parent = &hdev->dev; mcp->adapter.dev.parent = &hdev->dev;
ACPI_COMPANION_SET(&mcp->adapter.dev, ACPI_COMPANION(hdev->dev.parent));
snprintf(mcp->adapter.name, sizeof(mcp->adapter.name), snprintf(mcp->adapter.name, sizeof(mcp->adapter.name),
"MCP2221 usb-i2c bridge"); "MCP2221 usb-i2c bridge");
......
This diff is collapsed.
...@@ -632,7 +632,7 @@ static int sensor_hub_probe(struct hid_device *hdev, ...@@ -632,7 +632,7 @@ static int sensor_hub_probe(struct hid_device *hdev,
} }
INIT_LIST_HEAD(&hdev->inputs); INIT_LIST_HEAD(&hdev->inputs);
ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT | HID_CONNECT_DRIVER);
if (ret) { if (ret) {
hid_err(hdev, "hw start failed\n"); hid_err(hdev, "hw start failed\n");
return ret; return ret;
......
This diff is collapsed.
...@@ -44,12 +44,11 @@ ...@@ -44,12 +44,11 @@
#include "i2c-hid.h" #include "i2c-hid.h"
/* quirks to control the device */ /* quirks to control the device */
#define I2C_HID_QUIRK_SET_PWR_WAKEUP_DEV BIT(0) #define I2C_HID_QUIRK_NO_IRQ_AFTER_RESET BIT(0)
#define I2C_HID_QUIRK_NO_IRQ_AFTER_RESET BIT(1) #define I2C_HID_QUIRK_BOGUS_IRQ BIT(1)
#define I2C_HID_QUIRK_BOGUS_IRQ BIT(4) #define I2C_HID_QUIRK_RESET_ON_RESUME BIT(2)
#define I2C_HID_QUIRK_RESET_ON_RESUME BIT(5) #define I2C_HID_QUIRK_BAD_INPUT_SIZE BIT(3)
#define I2C_HID_QUIRK_BAD_INPUT_SIZE BIT(6) #define I2C_HID_QUIRK_NO_WAKEUP_AFTER_RESET BIT(4)
#define I2C_HID_QUIRK_NO_WAKEUP_AFTER_RESET BIT(7)
/* Command opcodes */ /* Command opcodes */
#define I2C_HID_OPCODE_RESET 0x01 #define I2C_HID_OPCODE_RESET 0x01
...@@ -120,8 +119,6 @@ static const struct i2c_hid_quirks { ...@@ -120,8 +119,6 @@ static const struct i2c_hid_quirks {
__u16 idProduct; __u16 idProduct;
__u32 quirks; __u32 quirks;
} i2c_hid_quirks[] = { } i2c_hid_quirks[] = {
{ USB_VENDOR_ID_WEIDA, HID_ANY_ID,
I2C_HID_QUIRK_SET_PWR_WAKEUP_DEV },
{ I2C_VENDOR_ID_HANTICK, I2C_PRODUCT_ID_HANTICK_5288, { I2C_VENDOR_ID_HANTICK, I2C_PRODUCT_ID_HANTICK_5288,
I2C_HID_QUIRK_NO_IRQ_AFTER_RESET }, I2C_HID_QUIRK_NO_IRQ_AFTER_RESET },
{ I2C_VENDOR_ID_ITE, I2C_DEVICE_ID_ITE_VOYO_WINPAD_A15, { I2C_VENDOR_ID_ITE, I2C_DEVICE_ID_ITE_VOYO_WINPAD_A15,
...@@ -395,8 +392,7 @@ static int i2c_hid_set_power(struct i2c_hid *ihid, int power_state) ...@@ -395,8 +392,7 @@ static int i2c_hid_set_power(struct i2c_hid *ihid, int power_state)
* The call will get a return value (EREMOTEIO) but device will be * The call will get a return value (EREMOTEIO) but device will be
* triggered and activated. After that, it goes like a normal device. * triggered and activated. After that, it goes like a normal device.
*/ */
if (power_state == I2C_HID_PWR_ON && if (power_state == I2C_HID_PWR_ON) {
ihid->quirks & I2C_HID_QUIRK_SET_PWR_WAKEUP_DEV) {
ret = i2c_hid_set_power_command(ihid, I2C_HID_PWR_ON); ret = i2c_hid_set_power_command(ihid, I2C_HID_PWR_ON);
/* Device was already activated */ /* Device was already activated */
...@@ -426,12 +422,23 @@ static int i2c_hid_set_power(struct i2c_hid *ihid, int power_state) ...@@ -426,12 +422,23 @@ static int i2c_hid_set_power(struct i2c_hid *ihid, int power_state)
return ret; return ret;
} }
static int i2c_hid_execute_reset(struct i2c_hid *ihid) static int i2c_hid_start_hwreset(struct i2c_hid *ihid)
{ {
size_t length = 0; size_t length = 0;
int ret; int ret;
i2c_hid_dbg(ihid, "resetting...\n"); i2c_hid_dbg(ihid, "%s\n", __func__);
/*
* This prevents sending feature reports while the device is
* being reset. Otherwise we may lose the reset complete
* interrupt.
*/
lockdep_assert_held(&ihid->reset_lock);
ret = i2c_hid_set_power(ihid, I2C_HID_PWR_ON);
if (ret)
return ret;
/* Prepare reset command. Command register goes first. */ /* Prepare reset command. Command register goes first. */
*(__le16 *)ihid->cmdbuf = ihid->hdesc.wCommandRegister; *(__le16 *)ihid->cmdbuf = ihid->hdesc.wCommandRegister;
...@@ -444,60 +451,40 @@ static int i2c_hid_execute_reset(struct i2c_hid *ihid) ...@@ -444,60 +451,40 @@ static int i2c_hid_execute_reset(struct i2c_hid *ihid)
ret = i2c_hid_xfer(ihid, ihid->cmdbuf, length, NULL, 0); ret = i2c_hid_xfer(ihid, ihid->cmdbuf, length, NULL, 0);
if (ret) { if (ret) {
dev_err(&ihid->client->dev, "failed to reset device.\n"); dev_err(&ihid->client->dev,
goto out; "failed to reset device: %d\n", ret);
} goto err_clear_reset;
if (ihid->quirks & I2C_HID_QUIRK_NO_IRQ_AFTER_RESET) {
msleep(100);
goto out;
} }
i2c_hid_dbg(ihid, "%s: waiting...\n", __func__); return 0;
if (!wait_event_timeout(ihid->wait,
!test_bit(I2C_HID_RESET_PENDING, &ihid->flags),
msecs_to_jiffies(5000))) {
ret = -ENODATA;
goto out;
}
i2c_hid_dbg(ihid, "%s: finished.\n", __func__);
out: err_clear_reset:
clear_bit(I2C_HID_RESET_PENDING, &ihid->flags); clear_bit(I2C_HID_RESET_PENDING, &ihid->flags);
i2c_hid_set_power(ihid, I2C_HID_PWR_SLEEP);
return ret; return ret;
} }
static int i2c_hid_hwreset(struct i2c_hid *ihid) static int i2c_hid_finish_hwreset(struct i2c_hid *ihid)
{ {
int ret; int ret = 0;
i2c_hid_dbg(ihid, "%s\n", __func__);
/* i2c_hid_dbg(ihid, "%s: waiting...\n", __func__);
* This prevents sending feature reports while the device is
* being reset. Otherwise we may lose the reset complete
* interrupt.
*/
mutex_lock(&ihid->reset_lock);
ret = i2c_hid_set_power(ihid, I2C_HID_PWR_ON);
if (ret)
goto out_unlock;
ret = i2c_hid_execute_reset(ihid); if (ihid->quirks & I2C_HID_QUIRK_NO_IRQ_AFTER_RESET) {
if (ret) { msleep(100);
dev_err(&ihid->client->dev, clear_bit(I2C_HID_RESET_PENDING, &ihid->flags);
"failed to reset device: %d\n", ret); } else if (!wait_event_timeout(ihid->wait,
i2c_hid_set_power(ihid, I2C_HID_PWR_SLEEP); !test_bit(I2C_HID_RESET_PENDING, &ihid->flags),
goto out_unlock; msecs_to_jiffies(1000))) {
dev_warn(&ihid->client->dev, "device did not ack reset within 1000 ms\n");
clear_bit(I2C_HID_RESET_PENDING, &ihid->flags);
} }
i2c_hid_dbg(ihid, "%s: finished.\n", __func__);
/* At least some SIS devices need this after reset */ /* At least some SIS devices need this after reset */
if (!(ihid->quirks & I2C_HID_QUIRK_NO_WAKEUP_AFTER_RESET)) if (!(ihid->quirks & I2C_HID_QUIRK_NO_WAKEUP_AFTER_RESET))
ret = i2c_hid_set_power(ihid, I2C_HID_PWR_ON); ret = i2c_hid_set_power(ihid, I2C_HID_PWR_ON);
out_unlock:
mutex_unlock(&ihid->reset_lock);
return ret; return ret;
} }
...@@ -729,11 +716,10 @@ static int i2c_hid_parse(struct hid_device *hid) ...@@ -729,11 +716,10 @@ static int i2c_hid_parse(struct hid_device *hid)
struct i2c_client *client = hid->driver_data; struct i2c_client *client = hid->driver_data;
struct i2c_hid *ihid = i2c_get_clientdata(client); struct i2c_hid *ihid = i2c_get_clientdata(client);
struct i2c_hid_desc *hdesc = &ihid->hdesc; struct i2c_hid_desc *hdesc = &ihid->hdesc;
char *rdesc = NULL, *use_override = NULL;
unsigned int rsize; unsigned int rsize;
char *rdesc;
int ret; int ret;
int tries = 3; int tries = 3;
char *use_override;
i2c_hid_dbg(ihid, "entering %s\n", __func__); i2c_hid_dbg(ihid, "entering %s\n", __func__);
...@@ -743,14 +729,15 @@ static int i2c_hid_parse(struct hid_device *hid) ...@@ -743,14 +729,15 @@ static int i2c_hid_parse(struct hid_device *hid)
return -EINVAL; return -EINVAL;
} }
mutex_lock(&ihid->reset_lock);
do { do {
ret = i2c_hid_hwreset(ihid); ret = i2c_hid_start_hwreset(ihid);
if (ret) if (ret)
msleep(1000); msleep(1000);
} while (tries-- > 0 && ret); } while (tries-- > 0 && ret);
if (ret) if (ret)
return ret; goto abort_reset;
use_override = i2c_hid_get_dmi_hid_report_desc_override(client->name, use_override = i2c_hid_get_dmi_hid_report_desc_override(client->name,
&rsize); &rsize);
...@@ -762,8 +749,8 @@ static int i2c_hid_parse(struct hid_device *hid) ...@@ -762,8 +749,8 @@ static int i2c_hid_parse(struct hid_device *hid)
rdesc = kzalloc(rsize, GFP_KERNEL); rdesc = kzalloc(rsize, GFP_KERNEL);
if (!rdesc) { if (!rdesc) {
dbg_hid("couldn't allocate rdesc memory\n"); ret = -ENOMEM;
return -ENOMEM; goto abort_reset;
} }
i2c_hid_dbg(ihid, "asking HID report descriptor\n"); i2c_hid_dbg(ihid, "asking HID report descriptor\n");
...@@ -773,23 +760,34 @@ static int i2c_hid_parse(struct hid_device *hid) ...@@ -773,23 +760,34 @@ static int i2c_hid_parse(struct hid_device *hid)
rdesc, rsize); rdesc, rsize);
if (ret) { if (ret) {
hid_err(hid, "reading report descriptor failed\n"); hid_err(hid, "reading report descriptor failed\n");
kfree(rdesc); goto abort_reset;
return -EIO;
} }
} }
/*
* Windows directly reads the report-descriptor after sending reset
* and then waits for resets completion afterwards. Some touchpads
* actually wait for the report-descriptor to be read before signalling
* reset completion.
*/
ret = i2c_hid_finish_hwreset(ihid);
abort_reset:
clear_bit(I2C_HID_RESET_PENDING, &ihid->flags);
mutex_unlock(&ihid->reset_lock);
if (ret)
goto out;
i2c_hid_dbg(ihid, "Report Descriptor: %*ph\n", rsize, rdesc); i2c_hid_dbg(ihid, "Report Descriptor: %*ph\n", rsize, rdesc);
ret = hid_parse_report(hid, rdesc, rsize); ret = hid_parse_report(hid, rdesc, rsize);
if (ret)
dbg_hid("parsing report descriptor failed\n");
out:
if (!use_override) if (!use_override)
kfree(rdesc); kfree(rdesc);
if (ret) {
dbg_hid("parsing report descriptor failed\n");
return ret; return ret;
}
return 0;
} }
static int i2c_hid_start(struct hid_device *hid) static int i2c_hid_start(struct hid_device *hid)
...@@ -987,10 +985,15 @@ static int i2c_hid_core_resume(struct i2c_hid *ihid) ...@@ -987,10 +985,15 @@ static int i2c_hid_core_resume(struct i2c_hid *ihid)
* However some ALPS touchpads generate IRQ storm without reset, so * However some ALPS touchpads generate IRQ storm without reset, so
* let's still reset them here. * let's still reset them here.
*/ */
if (ihid->quirks & I2C_HID_QUIRK_RESET_ON_RESUME) if (ihid->quirks & I2C_HID_QUIRK_RESET_ON_RESUME) {
ret = i2c_hid_hwreset(ihid); mutex_lock(&ihid->reset_lock);
else ret = i2c_hid_start_hwreset(ihid);
if (ret == 0)
ret = i2c_hid_finish_hwreset(ihid);
mutex_unlock(&ihid->reset_lock);
} else {
ret = i2c_hid_set_power(ihid, I2C_HID_PWR_ON); ret = i2c_hid_set_power(ihid, I2C_HID_PWR_ON);
}
if (ret) if (ret)
return ret; return ret;
......
...@@ -130,9 +130,17 @@ static const struct elan_i2c_hid_chip_data ilitek_ili9882t_chip_data = { ...@@ -130,9 +130,17 @@ static const struct elan_i2c_hid_chip_data ilitek_ili9882t_chip_data = {
.main_supply_name = NULL, .main_supply_name = NULL,
}; };
static const struct elan_i2c_hid_chip_data ilitek_ili2901_chip_data = {
.post_power_delay_ms = 10,
.post_gpio_reset_on_delay_ms = 100,
.hid_descriptor_address = 0x0001,
.main_supply_name = "vcc33",
};
static const struct of_device_id elan_i2c_hid_of_match[] = { static const struct of_device_id elan_i2c_hid_of_match[] = {
{ .compatible = "elan,ekth6915", .data = &elan_ekth6915_chip_data }, { .compatible = "elan,ekth6915", .data = &elan_ekth6915_chip_data },
{ .compatible = "ilitek,ili9882t", .data = &ilitek_ili9882t_chip_data }, { .compatible = "ilitek,ili9882t", .data = &ilitek_ili9882t_chip_data },
{ .compatible = "ilitek,ili2901", .data = &ilitek_ili2901_chip_data },
{ } { }
}; };
MODULE_DEVICE_TABLE(of, elan_i2c_hid_of_match); MODULE_DEVICE_TABLE(of, elan_i2c_hid_of_match);
......
...@@ -119,50 +119,6 @@ static inline bool ish_should_leave_d0i3(struct pci_dev *pdev) ...@@ -119,50 +119,6 @@ static inline bool ish_should_leave_d0i3(struct pci_dev *pdev)
return !pm_resume_via_firmware() || pdev->device == CHV_DEVICE_ID; return !pm_resume_via_firmware() || pdev->device == CHV_DEVICE_ID;
} }
static int enable_gpe(struct device *dev)
{
#ifdef CONFIG_ACPI
acpi_status acpi_sts;
struct acpi_device *adev;
struct acpi_device_wakeup *wakeup;
adev = ACPI_COMPANION(dev);
if (!adev) {
dev_err(dev, "get acpi handle failed\n");
return -ENODEV;
}
wakeup = &adev->wakeup;
/*
* Call acpi_disable_gpe(), so that reference count
* gpe_event_info->runtime_count doesn't overflow.
* When gpe_event_info->runtime_count = 0, the call
* to acpi_disable_gpe() simply return.
*/
acpi_disable_gpe(wakeup->gpe_device, wakeup->gpe_number);
acpi_sts = acpi_enable_gpe(wakeup->gpe_device, wakeup->gpe_number);
if (ACPI_FAILURE(acpi_sts)) {
dev_err(dev, "enable ose_gpe failed\n");
return -EIO;
}
return 0;
#else
return -ENODEV;
#endif
}
static void enable_pme_wake(struct pci_dev *pdev)
{
if ((pci_pme_capable(pdev, PCI_D0) ||
pci_pme_capable(pdev, PCI_D3hot) ||
pci_pme_capable(pdev, PCI_D3cold)) && !enable_gpe(&pdev->dev)) {
pci_pme_active(pdev, true);
dev_dbg(&pdev->dev, "ish ipc driver pme wake enabled\n");
}
}
/** /**
* ish_probe() - PCI driver probe callback * ish_probe() - PCI driver probe callback
* @pdev: pci device * @pdev: pci device
...@@ -233,7 +189,7 @@ static int ish_probe(struct pci_dev *pdev, const struct pci_device_id *ent) ...@@ -233,7 +189,7 @@ static int ish_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
/* Enable PME for EHL */ /* Enable PME for EHL */
if (pdev->device == EHL_Ax_DEVICE_ID) if (pdev->device == EHL_Ax_DEVICE_ID)
enable_pme_wake(pdev); device_init_wakeup(dev, true);
ret = ish_init(ishtp); ret = ish_init(ishtp);
if (ret) if (ret)
...@@ -256,6 +212,19 @@ static void ish_remove(struct pci_dev *pdev) ...@@ -256,6 +212,19 @@ static void ish_remove(struct pci_dev *pdev)
ish_device_disable(ishtp_dev); ish_device_disable(ishtp_dev);
} }
/**
* ish_shutdown() - PCI driver shutdown callback
* @pdev: pci device
*
* This function sets up wakeup for S5
*/
static void ish_shutdown(struct pci_dev *pdev)
{
if (pdev->device == EHL_Ax_DEVICE_ID)
pci_prepare_to_sleep(pdev);
}
static struct device __maybe_unused *ish_resume_device; static struct device __maybe_unused *ish_resume_device;
/* 50ms to get resume response */ /* 50ms to get resume response */
...@@ -378,13 +347,6 @@ static int __maybe_unused ish_resume(struct device *device) ...@@ -378,13 +347,6 @@ static int __maybe_unused ish_resume(struct device *device)
struct pci_dev *pdev = to_pci_dev(device); struct pci_dev *pdev = to_pci_dev(device);
struct ishtp_device *dev = pci_get_drvdata(pdev); struct ishtp_device *dev = pci_get_drvdata(pdev);
/* add this to finish power flow for EHL */
if (dev->pdev->device == EHL_Ax_DEVICE_ID) {
pci_set_power_state(pdev, PCI_D0);
enable_pme_wake(pdev);
dev_dbg(dev->devc, "set power state to D0 for ehl\n");
}
ish_resume_device = device; ish_resume_device = device;
dev->resume_flag = 1; dev->resume_flag = 1;
...@@ -400,6 +362,7 @@ static struct pci_driver ish_driver = { ...@@ -400,6 +362,7 @@ static struct pci_driver ish_driver = {
.id_table = ish_pci_tbl, .id_table = ish_pci_tbl,
.probe = ish_probe, .probe = ish_probe,
.remove = ish_remove, .remove = ish_remove,
.shutdown = ish_shutdown,
.driver.pm = &ish_pm_ops, .driver.pm = &ish_pm_ops,
}; };
......
...@@ -840,43 +840,22 @@ static void load_fw_from_host_handler(struct work_struct *work) ...@@ -840,43 +840,22 @@ static void load_fw_from_host_handler(struct work_struct *work)
* *
* Return: 0 for success, negative error code for failure * Return: 0 for success, negative error code for failure
*/ */
static int loader_init(struct ishtp_cl *loader_ishtp_cl, int reset) static int loader_init(struct ishtp_cl *loader_ishtp_cl, bool reset)
{ {
int rv; int rv;
struct ishtp_fw_client *fw_client;
struct ishtp_cl_data *client_data = struct ishtp_cl_data *client_data =
ishtp_get_client_data(loader_ishtp_cl); ishtp_get_client_data(loader_ishtp_cl);
dev_dbg(cl_data_to_dev(client_data), "reset flag: %d\n", reset); dev_dbg(cl_data_to_dev(client_data), "reset flag: %d\n", reset);
rv = ishtp_cl_link(loader_ishtp_cl); rv = ishtp_cl_establish_connection(loader_ishtp_cl,
if (rv < 0) { &loader_ishtp_id_table[0].guid,
dev_err(cl_data_to_dev(client_data), "ishtp_cl_link failed\n"); LOADER_CL_TX_RING_SIZE,
return rv; LOADER_CL_RX_RING_SIZE,
} reset);
/* Connect to firmware client */
ishtp_set_tx_ring_size(loader_ishtp_cl, LOADER_CL_TX_RING_SIZE);
ishtp_set_rx_ring_size(loader_ishtp_cl, LOADER_CL_RX_RING_SIZE);
fw_client =
ishtp_fw_cl_get_client(ishtp_get_ishtp_device(loader_ishtp_cl),
&loader_ishtp_id_table[0].guid);
if (!fw_client) {
dev_err(cl_data_to_dev(client_data),
"ISH client uuid not found\n");
rv = -ENOENT;
goto err_cl_unlink;
}
ishtp_cl_set_fw_client_id(loader_ishtp_cl,
ishtp_get_fw_client_id(fw_client));
ishtp_set_connection_state(loader_ishtp_cl, ISHTP_CL_CONNECTING);
rv = ishtp_cl_connect(loader_ishtp_cl);
if (rv < 0) { if (rv < 0) {
dev_err(cl_data_to_dev(client_data), "Client connect fail\n"); dev_err(cl_data_to_dev(client_data), "Client connect fail\n");
goto err_cl_unlink; goto err_cl_disconnect;
} }
dev_dbg(cl_data_to_dev(client_data), "Client connected\n"); dev_dbg(cl_data_to_dev(client_data), "Client connected\n");
...@@ -885,17 +864,14 @@ static int loader_init(struct ishtp_cl *loader_ishtp_cl, int reset) ...@@ -885,17 +864,14 @@ static int loader_init(struct ishtp_cl *loader_ishtp_cl, int reset)
return 0; return 0;
err_cl_unlink: err_cl_disconnect:
ishtp_cl_unlink(loader_ishtp_cl); ishtp_cl_destroy_connection(loader_ishtp_cl, reset);
return rv; return rv;
} }
static void loader_deinit(struct ishtp_cl *loader_ishtp_cl) static void loader_deinit(struct ishtp_cl *loader_ishtp_cl)
{ {
ishtp_set_connection_state(loader_ishtp_cl, ISHTP_CL_DISCONNECTING); ishtp_cl_destroy_connection(loader_ishtp_cl, false);
ishtp_cl_disconnect(loader_ishtp_cl);
ishtp_cl_unlink(loader_ishtp_cl);
ishtp_cl_flush_queues(loader_ishtp_cl);
/* Disband and free all Tx and Rx client-level rings */ /* Disband and free all Tx and Rx client-level rings */
ishtp_cl_free(loader_ishtp_cl); ishtp_cl_free(loader_ishtp_cl);
...@@ -914,19 +890,7 @@ static void reset_handler(struct work_struct *work) ...@@ -914,19 +890,7 @@ static void reset_handler(struct work_struct *work)
loader_ishtp_cl = client_data->loader_ishtp_cl; loader_ishtp_cl = client_data->loader_ishtp_cl;
cl_device = client_data->cl_device; cl_device = client_data->cl_device;
/* Unlink, flush queues & start again */ ishtp_cl_destroy_connection(loader_ishtp_cl, true);
ishtp_cl_unlink(loader_ishtp_cl);
ishtp_cl_flush_queues(loader_ishtp_cl);
ishtp_cl_free(loader_ishtp_cl);
loader_ishtp_cl = ishtp_cl_allocate(cl_device);
if (!loader_ishtp_cl)
return;
ishtp_set_drvdata(cl_device, loader_ishtp_cl);
ishtp_set_client_data(loader_ishtp_cl, client_data);
client_data->loader_ishtp_cl = loader_ishtp_cl;
client_data->cl_device = cl_device;
rv = loader_init(loader_ishtp_cl, 1); rv = loader_init(loader_ishtp_cl, 1);
if (rv < 0) { if (rv < 0) {
...@@ -974,7 +938,7 @@ static int loader_ishtp_cl_probe(struct ishtp_cl_device *cl_device) ...@@ -974,7 +938,7 @@ static int loader_ishtp_cl_probe(struct ishtp_cl_device *cl_device)
INIT_WORK(&client_data->work_fw_load, INIT_WORK(&client_data->work_fw_load,
load_fw_from_host_handler); load_fw_from_host_handler);
rv = loader_init(loader_ishtp_cl, 0); rv = loader_init(loader_ishtp_cl, false);
if (rv < 0) { if (rv < 0) {
ishtp_cl_free(loader_ishtp_cl); ishtp_cl_free(loader_ishtp_cl);
return rv; return rv;
......
...@@ -639,47 +639,26 @@ static int ishtp_get_report_descriptor(struct ishtp_cl *hid_ishtp_cl, ...@@ -639,47 +639,26 @@ static int ishtp_get_report_descriptor(struct ishtp_cl *hid_ishtp_cl,
* *
* Return: 0 on success, non zero on error * Return: 0 on success, non zero on error
*/ */
static int hid_ishtp_cl_init(struct ishtp_cl *hid_ishtp_cl, int reset) static int hid_ishtp_cl_init(struct ishtp_cl *hid_ishtp_cl, bool reset)
{ {
struct ishtp_device *dev;
struct ishtp_cl_data *client_data = ishtp_get_client_data(hid_ishtp_cl); struct ishtp_cl_data *client_data = ishtp_get_client_data(hid_ishtp_cl);
struct ishtp_fw_client *fw_client;
int i; int i;
int rv; int rv;
dev_dbg(cl_data_to_dev(client_data), "%s\n", __func__); dev_dbg(cl_data_to_dev(client_data), "%s\n", __func__);
hid_ishtp_trace(client_data, "%s reset flag: %d\n", __func__, reset); hid_ishtp_trace(client_data, "%s reset flag: %d\n", __func__, reset);
rv = ishtp_cl_link(hid_ishtp_cl);
if (rv) {
dev_err(cl_data_to_dev(client_data),
"ishtp_cl_link failed\n");
return -ENOMEM;
}
client_data->init_done = 0; client_data->init_done = 0;
dev = ishtp_get_ishtp_device(hid_ishtp_cl); rv = ishtp_cl_establish_connection(hid_ishtp_cl,
&hid_ishtp_id_table[0].guid,
/* Connect to FW client */ HID_CL_TX_RING_SIZE,
ishtp_set_tx_ring_size(hid_ishtp_cl, HID_CL_TX_RING_SIZE); HID_CL_RX_RING_SIZE,
ishtp_set_rx_ring_size(hid_ishtp_cl, HID_CL_RX_RING_SIZE); reset);
fw_client = ishtp_fw_cl_get_client(dev, &hid_ishtp_id_table[0].guid);
if (!fw_client) {
dev_err(cl_data_to_dev(client_data),
"ish client uuid not found\n");
return -ENOENT;
}
ishtp_cl_set_fw_client_id(hid_ishtp_cl,
ishtp_get_fw_client_id(fw_client));
ishtp_set_connection_state(hid_ishtp_cl, ISHTP_CL_CONNECTING);
rv = ishtp_cl_connect(hid_ishtp_cl);
if (rv) { if (rv) {
dev_err(cl_data_to_dev(client_data), dev_err(cl_data_to_dev(client_data),
"client connect fail\n"); "client connect fail\n");
goto err_cl_unlink; goto err_cl_disconnect;
} }
hid_ishtp_trace(client_data, "%s client connected\n", __func__); hid_ishtp_trace(client_data, "%s client connected\n", __func__);
...@@ -723,10 +702,7 @@ static int hid_ishtp_cl_init(struct ishtp_cl *hid_ishtp_cl, int reset) ...@@ -723,10 +702,7 @@ static int hid_ishtp_cl_init(struct ishtp_cl *hid_ishtp_cl, int reset)
return 0; return 0;
err_cl_disconnect: err_cl_disconnect:
ishtp_set_connection_state(hid_ishtp_cl, ISHTP_CL_DISCONNECTING); ishtp_cl_destroy_connection(hid_ishtp_cl, reset);
ishtp_cl_disconnect(hid_ishtp_cl);
err_cl_unlink:
ishtp_cl_unlink(hid_ishtp_cl);
return rv; return rv;
} }
...@@ -738,8 +714,7 @@ static int hid_ishtp_cl_init(struct ishtp_cl *hid_ishtp_cl, int reset) ...@@ -738,8 +714,7 @@ static int hid_ishtp_cl_init(struct ishtp_cl *hid_ishtp_cl, int reset)
*/ */
static void hid_ishtp_cl_deinit(struct ishtp_cl *hid_ishtp_cl) static void hid_ishtp_cl_deinit(struct ishtp_cl *hid_ishtp_cl)
{ {
ishtp_cl_unlink(hid_ishtp_cl); ishtp_cl_destroy_connection(hid_ishtp_cl, false);
ishtp_cl_flush_queues(hid_ishtp_cl);
/* disband and free all Tx and Rx client-level rings */ /* disband and free all Tx and Rx client-level rings */
ishtp_cl_free(hid_ishtp_cl); ishtp_cl_free(hid_ishtp_cl);
...@@ -749,33 +724,23 @@ static void hid_ishtp_cl_reset_handler(struct work_struct *work) ...@@ -749,33 +724,23 @@ static void hid_ishtp_cl_reset_handler(struct work_struct *work)
{ {
struct ishtp_cl_data *client_data; struct ishtp_cl_data *client_data;
struct ishtp_cl *hid_ishtp_cl; struct ishtp_cl *hid_ishtp_cl;
struct ishtp_cl_device *cl_device;
int retry; int retry;
int rv; int rv;
client_data = container_of(work, struct ishtp_cl_data, work); client_data = container_of(work, struct ishtp_cl_data, work);
hid_ishtp_cl = client_data->hid_ishtp_cl; hid_ishtp_cl = client_data->hid_ishtp_cl;
cl_device = client_data->cl_device;
hid_ishtp_trace(client_data, "%s hid_ishtp_cl %p\n", __func__, hid_ishtp_trace(client_data, "%s hid_ishtp_cl %p\n", __func__,
hid_ishtp_cl); hid_ishtp_cl);
dev_dbg(ishtp_device(client_data->cl_device), "%s\n", __func__); dev_dbg(ishtp_device(client_data->cl_device), "%s\n", __func__);
hid_ishtp_cl_deinit(hid_ishtp_cl); ishtp_cl_destroy_connection(hid_ishtp_cl, true);
hid_ishtp_cl = ishtp_cl_allocate(cl_device);
if (!hid_ishtp_cl)
return;
ishtp_set_drvdata(cl_device, hid_ishtp_cl);
ishtp_set_client_data(hid_ishtp_cl, client_data);
client_data->hid_ishtp_cl = hid_ishtp_cl;
client_data->num_hid_devices = 0; client_data->num_hid_devices = 0;
for (retry = 0; retry < 3; ++retry) { for (retry = 0; retry < 3; ++retry) {
rv = hid_ishtp_cl_init(hid_ishtp_cl, 1); rv = hid_ishtp_cl_init(hid_ishtp_cl, true);
if (!rv) if (!rv)
break; break;
dev_err(cl_data_to_dev(client_data), "Retry reset init\n"); dev_err(cl_data_to_dev(client_data), "Retry reset init\n");
...@@ -841,7 +806,7 @@ static int hid_ishtp_cl_probe(struct ishtp_cl_device *cl_device) ...@@ -841,7 +806,7 @@ static int hid_ishtp_cl_probe(struct ishtp_cl_device *cl_device)
ishtp_hid_print_trace = ishtp_trace_callback(cl_device); ishtp_hid_print_trace = ishtp_trace_callback(cl_device);
rv = hid_ishtp_cl_init(hid_ishtp_cl, 0); rv = hid_ishtp_cl_init(hid_ishtp_cl, false);
if (rv) { if (rv) {
ishtp_cl_free(hid_ishtp_cl); ishtp_cl_free(hid_ishtp_cl);
return rv; return rv;
...@@ -868,11 +833,9 @@ static void hid_ishtp_cl_remove(struct ishtp_cl_device *cl_device) ...@@ -868,11 +833,9 @@ static void hid_ishtp_cl_remove(struct ishtp_cl_device *cl_device)
hid_ishtp_cl); hid_ishtp_cl);
dev_dbg(ishtp_device(cl_device), "%s\n", __func__); dev_dbg(ishtp_device(cl_device), "%s\n", __func__);
ishtp_set_connection_state(hid_ishtp_cl, ISHTP_CL_DISCONNECTING); hid_ishtp_cl_deinit(hid_ishtp_cl);
ishtp_cl_disconnect(hid_ishtp_cl);
ishtp_put_device(cl_device); ishtp_put_device(cl_device);
ishtp_hid_remove(client_data); ishtp_hid_remove(client_data);
hid_ishtp_cl_deinit(hid_ishtp_cl);
hid_ishtp_cl = NULL; hid_ishtp_cl = NULL;
......
...@@ -378,7 +378,7 @@ static const struct dev_pm_ops ishtp_cl_bus_dev_pm_ops = { ...@@ -378,7 +378,7 @@ static const struct dev_pm_ops ishtp_cl_bus_dev_pm_ops = {
.restore = ishtp_cl_device_resume, .restore = ishtp_cl_device_resume,
}; };
static struct bus_type ishtp_cl_bus_type = { static const struct bus_type ishtp_cl_bus_type = {
.name = "ishtp", .name = "ishtp",
.dev_groups = ishtp_cl_dev_groups, .dev_groups = ishtp_cl_dev_groups,
.probe = ishtp_cl_device_probe, .probe = ishtp_cl_device_probe,
......
...@@ -339,16 +339,17 @@ static bool ishtp_cl_is_other_connecting(struct ishtp_cl *cl) ...@@ -339,16 +339,17 @@ static bool ishtp_cl_is_other_connecting(struct ishtp_cl *cl)
} }
/** /**
* ishtp_cl_connect() - Send connect request to firmware * ishtp_cl_connect_to_fw() - Send connect request to firmware
* @cl: client device instance * @cl: client device instance
* *
* Send a connect request for a client to firmware. If successful it will * Send a connect request to the firmware and wait for firmware response.
* RX and TX ring buffers * If there is successful connection response from the firmware, change
* client state to ISHTP_CL_CONNECTED, and bind client to related
* firmware client_id.
* *
* Return: 0 if successful connect response from the firmware and able * Return: 0 for success and error code on failure
* to bind and allocate ring buffers or error code on failure
*/ */
int ishtp_cl_connect(struct ishtp_cl *cl) static int ishtp_cl_connect_to_fw(struct ishtp_cl *cl)
{ {
struct ishtp_device *dev; struct ishtp_device *dev;
int rets; int rets;
...@@ -358,8 +359,6 @@ int ishtp_cl_connect(struct ishtp_cl *cl) ...@@ -358,8 +359,6 @@ int ishtp_cl_connect(struct ishtp_cl *cl)
dev = cl->dev; dev = cl->dev;
dev->print_log(dev, "%s() current_state = %d\n", __func__, cl->state);
if (ishtp_cl_is_other_connecting(cl)) { if (ishtp_cl_is_other_connecting(cl)) {
dev->print_log(dev, "%s() Busy\n", __func__); dev->print_log(dev, "%s() Busy\n", __func__);
return -EBUSY; return -EBUSY;
...@@ -405,6 +404,38 @@ int ishtp_cl_connect(struct ishtp_cl *cl) ...@@ -405,6 +404,38 @@ int ishtp_cl_connect(struct ishtp_cl *cl)
return rets; return rets;
} }
return rets;
}
/**
* ishtp_cl_connect() - Build connection with firmware
* @cl: client device instance
*
* Call ishtp_cl_connect_to_fw() to connect and bind to firmware. If successful,
* allocate RX and TX ring buffers, and start flow control with firmware to
* start communication.
*
* Return: 0 if there is successful connection to the firmware, allocate
* ring buffers.
*/
int ishtp_cl_connect(struct ishtp_cl *cl)
{
struct ishtp_device *dev;
int rets;
if (!cl || !cl->dev)
return -ENODEV;
dev = cl->dev;
dev->print_log(dev, "%s() current_state = %d\n", __func__, cl->state);
rets = ishtp_cl_connect_to_fw(cl);
if (rets) {
dev->print_log(dev, "%s() Connect to fw failed\n", __func__);
return rets;
}
rets = ishtp_cl_alloc_rx_ring(cl); rets = ishtp_cl_alloc_rx_ring(cl);
if (rets) { if (rets) {
dev->print_log(dev, "%s() Alloc RX ring failed\n", __func__); dev->print_log(dev, "%s() Alloc RX ring failed\n", __func__);
...@@ -422,15 +453,147 @@ int ishtp_cl_connect(struct ishtp_cl *cl) ...@@ -422,15 +453,147 @@ int ishtp_cl_connect(struct ishtp_cl *cl)
return rets; return rets;
} }
/* Upon successful connection and allocation, emit flow-control */ /*
* Upon successful connection and allocation, start flow-control.
*/
rets = ishtp_cl_read_start(cl); rets = ishtp_cl_read_start(cl);
dev->print_log(dev, "%s() successful\n", __func__);
return rets; return rets;
} }
EXPORT_SYMBOL(ishtp_cl_connect); EXPORT_SYMBOL(ishtp_cl_connect);
/**
* ishtp_cl_establish_connection() - Establish connection with the firmware
* @cl: client device instance
* @uuid: uuid of the client to search
* @tx_size: TX ring buffer size
* @rx_size: RX ring buffer size
* @reset: true if called for reset connection, otherwise for first connection
*
* This is a helper function for client driver to build connection with firmware.
* If it's first time connecting to the firmware, set reset to false, this
* function will link client to bus, find client id and send connect request to
* the firmware.
*
* If it's called for reset handler where client lost connection after
* firmware reset, set reset to true, this function will reinit client state and
* establish connection again. In this case, this function reuses current client
* structure and ring buffers to avoid allocation failure and memory fragments.
*
* Return: 0 for successful connection with the firmware,
* or error code on failure
*/
int ishtp_cl_establish_connection(struct ishtp_cl *cl, const guid_t *uuid,
int tx_size, int rx_size, bool reset)
{
struct ishtp_device *dev;
struct ishtp_fw_client *fw_client;
int rets;
if (!cl || !cl->dev)
return -ENODEV;
dev = cl->dev;
ishtp_set_connection_state(cl, ISHTP_CL_INITIALIZING);
/* reinit ishtp_cl structure if call for reset */
if (reset) {
cl->host_client_id = 0;
cl->fw_client_id = 0;
cl->ishtp_flow_ctrl_creds = 0;
cl->out_flow_ctrl_creds = 0;
cl->last_tx_path = CL_TX_PATH_IPC;
cl->last_dma_acked = 1;
cl->last_dma_addr = NULL;
cl->last_ipc_acked = 1;
cl->sending = 0;
cl->err_send_msg = 0;
cl->err_send_fc = 0;
cl->send_msg_cnt_ipc = 0;
cl->send_msg_cnt_dma = 0;
cl->recv_msg_cnt_ipc = 0;
cl->recv_msg_cnt_dma = 0;
cl->recv_msg_num_frags = 0;
cl->ishtp_flow_ctrl_cnt = 0;
cl->out_flow_ctrl_cnt = 0;
}
/* link to bus */
rets = ishtp_cl_link(cl);
if (rets) {
dev->print_log(dev, "%s() ishtp_cl_link failed\n", __func__);
return rets;
}
/* find firmware client */
fw_client = ishtp_fw_cl_get_client(dev, uuid);
if (!fw_client) {
dev->print_log(dev,
"%s() ish client uuid not found\n", __func__);
return -ENOENT;
}
ishtp_set_tx_ring_size(cl, tx_size);
ishtp_set_rx_ring_size(cl, rx_size);
ishtp_cl_set_fw_client_id(cl, ishtp_get_fw_client_id(fw_client));
ishtp_set_connection_state(cl, ISHTP_CL_CONNECTING);
/*
* For reset case, not allocate tx/rx ring buffer which are already
* done in ishtp_cl_connect() during first connection.
*/
if (reset) {
rets = ishtp_cl_connect_to_fw(cl);
if (!rets)
rets = ishtp_cl_read_start(cl);
else
dev->print_log(dev,
"%s() connect to fw failed\n", __func__);
} else {
rets = ishtp_cl_connect(cl);
}
return rets;
}
EXPORT_SYMBOL(ishtp_cl_establish_connection);
/**
* ishtp_cl_destroy_connection() - Disconnect with the firmware
* @cl: client device instance
* @reset: true if called for firmware reset, false for normal disconnection
*
* This is a helper function for client driver to disconnect with firmware,
* unlink to bus and flush message queue.
*/
void ishtp_cl_destroy_connection(struct ishtp_cl *cl, bool reset)
{
if (!cl)
return;
if (reset) {
/*
* For reset case, connection is already lost during fw reset.
* Just set state to DISCONNECTED is enough.
*/
ishtp_set_connection_state(cl, ISHTP_CL_DISCONNECTED);
} else {
if (cl->state != ISHTP_CL_DISCONNECTED) {
ishtp_set_connection_state(cl, ISHTP_CL_DISCONNECTING);
ishtp_cl_disconnect(cl);
}
}
ishtp_cl_unlink(cl);
ishtp_cl_flush_queues(cl);
}
EXPORT_SYMBOL(ishtp_cl_destroy_connection);
/** /**
* ishtp_cl_read_start() - Prepare to read client message * ishtp_cl_read_start() - Prepare to read client message
* @cl: client device instance * @cl: client device instance
......
...@@ -164,6 +164,7 @@ struct wacom { ...@@ -164,6 +164,7 @@ struct wacom {
struct work_struct battery_work; struct work_struct battery_work;
struct work_struct remote_work; struct work_struct remote_work;
struct delayed_work init_work; struct delayed_work init_work;
struct delayed_work aes_battery_work;
struct wacom_remote *remote; struct wacom_remote *remote;
struct work_struct mode_change_work; struct work_struct mode_change_work;
struct timer_list idleprox_timer; struct timer_list idleprox_timer;
......
...@@ -1813,6 +1813,13 @@ static void wacom_destroy_battery(struct wacom *wacom) ...@@ -1813,6 +1813,13 @@ static void wacom_destroy_battery(struct wacom *wacom)
} }
} }
static void wacom_aes_battery_handler(struct work_struct *work)
{
struct wacom *wacom = container_of(work, struct wacom, aes_battery_work.work);
wacom_destroy_battery(wacom);
}
static ssize_t wacom_show_speed(struct device *dev, static ssize_t wacom_show_speed(struct device *dev,
struct device_attribute struct device_attribute
*attr, char *buf) *attr, char *buf)
...@@ -2794,6 +2801,7 @@ static int wacom_probe(struct hid_device *hdev, ...@@ -2794,6 +2801,7 @@ static int wacom_probe(struct hid_device *hdev,
mutex_init(&wacom->lock); mutex_init(&wacom->lock);
INIT_DELAYED_WORK(&wacom->init_work, wacom_init_work); INIT_DELAYED_WORK(&wacom->init_work, wacom_init_work);
INIT_DELAYED_WORK(&wacom->aes_battery_work, wacom_aes_battery_handler);
INIT_WORK(&wacom->wireless_work, wacom_wireless_work); INIT_WORK(&wacom->wireless_work, wacom_wireless_work);
INIT_WORK(&wacom->battery_work, wacom_battery_work); INIT_WORK(&wacom->battery_work, wacom_battery_work);
INIT_WORK(&wacom->remote_work, wacom_remote_work); INIT_WORK(&wacom->remote_work, wacom_remote_work);
......
...@@ -2528,11 +2528,12 @@ static void wacom_wac_pen_report(struct hid_device *hdev, ...@@ -2528,11 +2528,12 @@ static void wacom_wac_pen_report(struct hid_device *hdev,
struct input_dev *input = wacom_wac->pen_input; struct input_dev *input = wacom_wac->pen_input;
bool range = wacom_wac->hid_data.inrange_state; bool range = wacom_wac->hid_data.inrange_state;
bool sense = wacom_wac->hid_data.sense_state; bool sense = wacom_wac->hid_data.sense_state;
bool entering_range = !wacom_wac->tool[0] && range;
if (wacom_wac->is_invalid_bt_frame) if (wacom_wac->is_invalid_bt_frame)
return; return;
if (!wacom_wac->tool[0] && range) { /* first in range */ if (entering_range) { /* first in range */
/* Going into range select tool */ /* Going into range select tool */
if (wacom_wac->hid_data.invert_state) if (wacom_wac->hid_data.invert_state)
wacom_wac->tool[0] = BTN_TOOL_RUBBER; wacom_wac->tool[0] = BTN_TOOL_RUBBER;
...@@ -2583,6 +2584,15 @@ static void wacom_wac_pen_report(struct hid_device *hdev, ...@@ -2583,6 +2584,15 @@ static void wacom_wac_pen_report(struct hid_device *hdev,
input_sync(input); input_sync(input);
} }
/* Handle AES battery timeout behavior */
if (wacom_wac->features.quirks & WACOM_QUIRK_AESPEN) {
if (entering_range)
cancel_delayed_work(&wacom->aes_battery_work);
if (!sense)
schedule_delayed_work(&wacom->aes_battery_work,
msecs_to_jiffies(WACOM_AES_BATTERY_TIMEOUT));
}
if (!sense) { if (!sense) {
wacom_wac->tool[0] = 0; wacom_wac->tool[0] = 0;
wacom_wac->id[0] = 0; wacom_wac->id[0] = 0;
...@@ -2649,8 +2659,8 @@ static void wacom_wac_finger_slot(struct wacom_wac *wacom_wac, ...@@ -2649,8 +2659,8 @@ static void wacom_wac_finger_slot(struct wacom_wac *wacom_wac,
{ {
struct hid_data *hid_data = &wacom_wac->hid_data; struct hid_data *hid_data = &wacom_wac->hid_data;
bool mt = wacom_wac->features.touch_max > 1; bool mt = wacom_wac->features.touch_max > 1;
bool prox = hid_data->tipswitch && bool touch_down = hid_data->tipswitch && hid_data->confidence;
report_touch_events(wacom_wac); bool prox = touch_down && report_touch_events(wacom_wac);
if (touch_is_muted(wacom_wac)) { if (touch_is_muted(wacom_wac)) {
if (!wacom_wac->shared->touch_down) if (!wacom_wac->shared->touch_down)
...@@ -2700,24 +2710,6 @@ static void wacom_wac_finger_slot(struct wacom_wac *wacom_wac, ...@@ -2700,24 +2710,6 @@ static void wacom_wac_finger_slot(struct wacom_wac *wacom_wac,
} }
} }
static bool wacom_wac_slot_is_active(struct input_dev *dev, int key)
{
struct input_mt *mt = dev->mt;
struct input_mt_slot *s;
if (!mt)
return false;
for (s = mt->slots; s != mt->slots + mt->num_slots; s++) {
if (s->key == key &&
input_mt_get_value(s, ABS_MT_TRACKING_ID) >= 0) {
return true;
}
}
return false;
}
static void wacom_wac_finger_event(struct hid_device *hdev, static void wacom_wac_finger_event(struct hid_device *hdev,
struct hid_field *field, struct hid_usage *usage, __s32 value) struct hid_field *field, struct hid_usage *usage, __s32 value)
{ {
...@@ -2768,15 +2760,9 @@ static void wacom_wac_finger_event(struct hid_device *hdev, ...@@ -2768,15 +2760,9 @@ static void wacom_wac_finger_event(struct hid_device *hdev,
} }
if (usage->usage_index + 1 == field->report_count) { if (usage->usage_index + 1 == field->report_count) {
if (equivalent_usage == wacom_wac->hid_data.last_slot_field) { if (equivalent_usage == wacom_wac->hid_data.last_slot_field)
bool touch_removed = wacom_wac_slot_is_active(wacom_wac->touch_input,
wacom_wac->hid_data.id) && !wacom_wac->hid_data.tipswitch;
if (wacom_wac->hid_data.confidence || touch_removed) {
wacom_wac_finger_slot(wacom_wac, wacom_wac->touch_input); wacom_wac_finger_slot(wacom_wac, wacom_wac->touch_input);
} }
}
}
} }
static void wacom_wac_finger_pre_report(struct hid_device *hdev, static void wacom_wac_finger_pre_report(struct hid_device *hdev,
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#define WACOM_MAX_REMOTES 5 #define WACOM_MAX_REMOTES 5
#define WACOM_STATUS_UNKNOWN 255 #define WACOM_STATUS_UNKNOWN 255
#define WACOM_REMOTE_BATTERY_TIMEOUT 21000000000ll #define WACOM_REMOTE_BATTERY_TIMEOUT 21000000000ll
#define WACOM_AES_BATTERY_TIMEOUT 1800000
/* packet length for individual models */ /* packet length for individual models */
#define WACOM_PKGLEN_BBFUN 9 #define WACOM_PKGLEN_BBFUN 9
......
...@@ -367,55 +367,33 @@ static void ish_event_cb(struct ishtp_cl_device *cl_device) ...@@ -367,55 +367,33 @@ static void ish_event_cb(struct ishtp_cl_device *cl_device)
/** /**
* cros_ish_init() - Init function for ISHTP client * cros_ish_init() - Init function for ISHTP client
* @cros_ish_cl: ISHTP client instance * @cros_ish_cl: ISHTP client instance
* @reset: true if called from reset handler
* *
* This function complete the initializtion of the client. * This function complete the initializtion of the client.
* *
* Return: 0 for success, negative error code for failure. * Return: 0 for success, negative error code for failure.
*/ */
static int cros_ish_init(struct ishtp_cl *cros_ish_cl) static int cros_ish_init(struct ishtp_cl *cros_ish_cl, bool reset)
{ {
int rv; int rv;
struct ishtp_device *dev;
struct ishtp_fw_client *fw_client;
struct ishtp_cl_data *client_data = ishtp_get_client_data(cros_ish_cl); struct ishtp_cl_data *client_data = ishtp_get_client_data(cros_ish_cl);
rv = ishtp_cl_link(cros_ish_cl); rv = ishtp_cl_establish_connection(cros_ish_cl,
if (rv) { &cros_ec_ishtp_id_table[0].guid,
dev_err(cl_data_to_dev(client_data), CROS_ISH_CL_TX_RING_SIZE,
"ishtp_cl_link failed\n"); CROS_ISH_CL_RX_RING_SIZE,
return rv; reset);
}
dev = ishtp_get_ishtp_device(cros_ish_cl);
/* Connect to firmware client */
ishtp_set_tx_ring_size(cros_ish_cl, CROS_ISH_CL_TX_RING_SIZE);
ishtp_set_rx_ring_size(cros_ish_cl, CROS_ISH_CL_RX_RING_SIZE);
fw_client = ishtp_fw_cl_get_client(dev, &cros_ec_ishtp_id_table[0].guid);
if (!fw_client) {
dev_err(cl_data_to_dev(client_data),
"ish client uuid not found\n");
rv = -ENOENT;
goto err_cl_unlink;
}
ishtp_cl_set_fw_client_id(cros_ish_cl,
ishtp_get_fw_client_id(fw_client));
ishtp_set_connection_state(cros_ish_cl, ISHTP_CL_CONNECTING);
rv = ishtp_cl_connect(cros_ish_cl);
if (rv) { if (rv) {
dev_err(cl_data_to_dev(client_data), dev_err(cl_data_to_dev(client_data),
"client connect fail\n"); "client connect fail\n");
goto err_cl_unlink; goto err_cl_disconnect;
} }
ishtp_register_event_cb(client_data->cl_device, ish_event_cb); ishtp_register_event_cb(client_data->cl_device, ish_event_cb);
return 0; return 0;
err_cl_unlink: err_cl_disconnect:
ishtp_cl_unlink(cros_ish_cl); ishtp_cl_destroy_connection(cros_ish_cl, reset);
return rv; return rv;
} }
...@@ -427,10 +405,7 @@ static int cros_ish_init(struct ishtp_cl *cros_ish_cl) ...@@ -427,10 +405,7 @@ static int cros_ish_init(struct ishtp_cl *cros_ish_cl)
*/ */
static void cros_ish_deinit(struct ishtp_cl *cros_ish_cl) static void cros_ish_deinit(struct ishtp_cl *cros_ish_cl)
{ {
ishtp_set_connection_state(cros_ish_cl, ISHTP_CL_DISCONNECTING); ishtp_cl_destroy_connection(cros_ish_cl, false);
ishtp_cl_disconnect(cros_ish_cl);
ishtp_cl_unlink(cros_ish_cl);
ishtp_cl_flush_queues(cros_ish_cl);
/* Disband and free all Tx and Rx client-level rings */ /* Disband and free all Tx and Rx client-level rings */
ishtp_cl_free(cros_ish_cl); ishtp_cl_free(cros_ish_cl);
...@@ -592,7 +567,6 @@ static void reset_handler(struct work_struct *work) ...@@ -592,7 +567,6 @@ static void reset_handler(struct work_struct *work)
int rv; int rv;
struct device *dev; struct device *dev;
struct ishtp_cl *cros_ish_cl; struct ishtp_cl *cros_ish_cl;
struct ishtp_cl_device *cl_device;
struct ishtp_cl_data *client_data = struct ishtp_cl_data *client_data =
container_of(work, struct ishtp_cl_data, work_ishtp_reset); container_of(work, struct ishtp_cl_data, work_ishtp_reset);
...@@ -600,26 +574,11 @@ static void reset_handler(struct work_struct *work) ...@@ -600,26 +574,11 @@ static void reset_handler(struct work_struct *work)
down_write(&init_lock); down_write(&init_lock);
cros_ish_cl = client_data->cros_ish_cl; cros_ish_cl = client_data->cros_ish_cl;
cl_device = client_data->cl_device;
/* Unlink, flush queues & start again */ ishtp_cl_destroy_connection(cros_ish_cl, true);
ishtp_cl_unlink(cros_ish_cl);
ishtp_cl_flush_queues(cros_ish_cl);
ishtp_cl_free(cros_ish_cl);
cros_ish_cl = ishtp_cl_allocate(cl_device);
if (!cros_ish_cl) {
up_write(&init_lock);
return;
}
ishtp_set_drvdata(cl_device, cros_ish_cl);
ishtp_set_client_data(cros_ish_cl, client_data);
client_data->cros_ish_cl = cros_ish_cl;
rv = cros_ish_init(cros_ish_cl); rv = cros_ish_init(cros_ish_cl, true);
if (rv) { if (rv) {
ishtp_cl_free(cros_ish_cl);
dev_err(cl_data_to_dev(client_data), "Reset Failed\n"); dev_err(cl_data_to_dev(client_data), "Reset Failed\n");
up_write(&init_lock); up_write(&init_lock);
return; return;
...@@ -672,7 +631,7 @@ static int cros_ec_ishtp_probe(struct ishtp_cl_device *cl_device) ...@@ -672,7 +631,7 @@ static int cros_ec_ishtp_probe(struct ishtp_cl_device *cl_device)
INIT_WORK(&client_data->work_ec_evt, INIT_WORK(&client_data->work_ec_evt,
ish_evt_handler); ish_evt_handler);
rv = cros_ish_init(cros_ish_cl); rv = cros_ish_init(cros_ish_cl, false);
if (rv) if (rv)
goto end_ishtp_cl_init_error; goto end_ishtp_cl_init_error;
...@@ -690,10 +649,7 @@ static int cros_ec_ishtp_probe(struct ishtp_cl_device *cl_device) ...@@ -690,10 +649,7 @@ static int cros_ec_ishtp_probe(struct ishtp_cl_device *cl_device)
return 0; return 0;
end_cros_ec_dev_init_error: end_cros_ec_dev_init_error:
ishtp_set_connection_state(cros_ish_cl, ISHTP_CL_DISCONNECTING); ishtp_cl_destroy_connection(cros_ish_cl, false);
ishtp_cl_disconnect(cros_ish_cl);
ishtp_cl_unlink(cros_ish_cl);
ishtp_cl_flush_queues(cros_ish_cl);
ishtp_put_device(cl_device); ishtp_put_device(cl_device);
end_ishtp_cl_init_error: end_ishtp_cl_init_error:
ishtp_cl_free(cros_ish_cl); ishtp_cl_free(cros_ish_cl);
......
/* SPDX-License-Identifier: GPL-2.0 */
/*
* AMD Platform Management Framework Interface
*
* Copyright (c) 2023, Advanced Micro Devices, Inc.
* All Rights Reserved.
*
* Authors: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
* Basavaraj Natikar <Basavaraj.Natikar@amd.com>
*/
#ifndef AMD_PMF_IO_H
#define AMD_PMF_IO_H
#include <linux/types.h>
/**
* enum sfh_message_type - Query the SFH message type
* @MT_HPD: Message ID to know the Human presence info from MP2 FW
* @MT_ALS: Message ID to know the Ambient light info from MP2 FW
*/
enum sfh_message_type {
MT_HPD,
MT_ALS,
};
/**
* enum sfh_hpd_info - Query the Human presence information
* @SFH_NOT_DETECTED: Check the HPD connection information from MP2 FW
* @SFH_USER_PRESENT: Check if the user is present from HPD sensor
* @SFH_USER_AWAY: Check if the user is away from HPD sensor
*/
enum sfh_hpd_info {
SFH_NOT_DETECTED,
SFH_USER_PRESENT,
SFH_USER_AWAY,
};
/**
* struct amd_sfh_info - get HPD sensor info from MP2 FW
* @ambient_light: Populates the ambient light information
* @user_present: Populates the user presence information
*/
struct amd_sfh_info {
u32 ambient_light;
u8 user_present;
};
int amd_get_sfh_info(struct amd_sfh_info *sfh_info, enum sfh_message_type op);
#endif
...@@ -912,7 +912,7 @@ extern bool hid_ignore(struct hid_device *); ...@@ -912,7 +912,7 @@ extern bool hid_ignore(struct hid_device *);
extern int hid_add_device(struct hid_device *); extern int hid_add_device(struct hid_device *);
extern void hid_destroy_device(struct hid_device *); extern void hid_destroy_device(struct hid_device *);
extern struct bus_type hid_bus_type; extern const struct bus_type hid_bus_type;
extern int __must_check __hid_register_driver(struct hid_driver *, extern int __must_check __hid_register_driver(struct hid_driver *,
struct module *, const char *mod_name); struct module *, const char *mod_name);
......
...@@ -115,7 +115,7 @@ struct hid_bpf_ops { ...@@ -115,7 +115,7 @@ struct hid_bpf_ops {
size_t len, enum hid_report_type rtype, size_t len, enum hid_report_type rtype,
enum hid_class_request reqtype); enum hid_class_request reqtype);
struct module *owner; struct module *owner;
struct bus_type *bus_type; const struct bus_type *bus_type;
}; };
extern struct hid_bpf_ops *hid_bpf_ops; extern struct hid_bpf_ops *hid_bpf_ops;
......
...@@ -94,6 +94,9 @@ int ishtp_cl_link(struct ishtp_cl *cl); ...@@ -94,6 +94,9 @@ int ishtp_cl_link(struct ishtp_cl *cl);
void ishtp_cl_unlink(struct ishtp_cl *cl); void ishtp_cl_unlink(struct ishtp_cl *cl);
int ishtp_cl_disconnect(struct ishtp_cl *cl); int ishtp_cl_disconnect(struct ishtp_cl *cl);
int ishtp_cl_connect(struct ishtp_cl *cl); int ishtp_cl_connect(struct ishtp_cl *cl);
int ishtp_cl_establish_connection(struct ishtp_cl *cl, const guid_t *uuid,
int tx_size, int rx_size, bool reset);
void ishtp_cl_destroy_connection(struct ishtp_cl *cl, bool reset);
int ishtp_cl_send(struct ishtp_cl *cl, uint8_t *buf, size_t length); int ishtp_cl_send(struct ishtp_cl *cl, uint8_t *buf, size_t length);
int ishtp_cl_flush_queues(struct ishtp_cl *cl); int ishtp_cl_flush_queues(struct ishtp_cl *cl);
int ishtp_cl_io_rb_recycle(struct ishtp_cl_rb *rb); int ishtp_cl_io_rb_recycle(struct ishtp_cl_rb *rb);
......
...@@ -14,7 +14,7 @@ import logging ...@@ -14,7 +14,7 @@ import logging
from hidtools.device.base_device import BaseDevice, EvdevMatch, SysfsFile from hidtools.device.base_device import BaseDevice, EvdevMatch, SysfsFile
from pathlib import Path from pathlib import Path
from typing import Final from typing import Final, List, Tuple
logger = logging.getLogger("hidtools.test.base") logger = logging.getLogger("hidtools.test.base")
...@@ -155,7 +155,7 @@ class BaseTestCase: ...@@ -155,7 +155,7 @@ class BaseTestCase:
# if any module is not available (not compiled), the test will skip. # if any module is not available (not compiled), the test will skip.
# Each element is a tuple '(kernel driver name, kernel module)', # Each element is a tuple '(kernel driver name, kernel module)',
# for example ("playstation", "hid-playstation") # for example ("playstation", "hid-playstation")
kernel_modules = [] kernel_modules: List[Tuple[str, str]] = []
def assertInputEventsIn(self, expected_events, effective_events): def assertInputEventsIn(self, expected_events, effective_events):
effective_events = effective_events.copy() effective_events = effective_events.copy()
...@@ -238,8 +238,7 @@ class BaseTestCase: ...@@ -238,8 +238,7 @@ class BaseTestCase:
try: try:
with HIDTestUdevRule.instance(): with HIDTestUdevRule.instance():
with new_uhdev as self.uhdev: with new_uhdev as self.uhdev:
skip_cond = request.node.get_closest_marker("skip_if_uhdev") for skip_cond in request.node.iter_markers("skip_if_uhdev"):
if skip_cond:
test, message, *rest = skip_cond.args test, message, *rest = skip_cond.args
if test(self.uhdev): if test(self.uhdev):
......
...@@ -52,13 +52,13 @@ class BaseMouse(base.UHIDTestDevice): ...@@ -52,13 +52,13 @@ class BaseMouse(base.UHIDTestDevice):
:param reportID: the numeric report ID for this report, if needed :param reportID: the numeric report ID for this report, if needed
""" """
if buttons is not None: if buttons is not None:
l, r, m = buttons left, right, middle = buttons
if l is not None: if left is not None:
self.left = l self.left = left
if r is not None: if right is not None:
self.right = r self.right = right
if m is not None: if middle is not None:
self.middle = m self.middle = middle
left = self.left left = self.left
right = self.right right = self.right
middle = self.middle middle = self.middle
......
...@@ -19,12 +19,12 @@ esac ...@@ -19,12 +19,12 @@ esac
SCRIPT_DIR="$(dirname $(realpath $0))" SCRIPT_DIR="$(dirname $(realpath $0))"
OUTPUT_DIR="$SCRIPT_DIR/results" OUTPUT_DIR="$SCRIPT_DIR/results"
KCONFIG_REL_PATHS=("${SCRIPT_DIR}/config" "${SCRIPT_DIR}/config.common" "${SCRIPT_DIR}/config.${ARCH}") KCONFIG_REL_PATHS=("${SCRIPT_DIR}/config" "${SCRIPT_DIR}/config.common" "${SCRIPT_DIR}/config.${ARCH}")
B2C_URL="https://gitlab.freedesktop.org/mupuf/boot2container/-/raw/master/vm2c.py" B2C_URL="https://gitlab.freedesktop.org/gfx-ci/boot2container/-/raw/main/vm2c.py"
NUM_COMPILE_JOBS="$(nproc)" NUM_COMPILE_JOBS="$(nproc)"
LOG_FILE_BASE="$(date +"hid_selftests.%Y-%m-%d_%H-%M-%S")" LOG_FILE_BASE="$(date +"hid_selftests.%Y-%m-%d_%H-%M-%S")"
LOG_FILE="${LOG_FILE_BASE}.log" LOG_FILE="${LOG_FILE_BASE}.log"
EXIT_STATUS_FILE="${LOG_FILE_BASE}.exit_status" EXIT_STATUS_FILE="${LOG_FILE_BASE}.exit_status"
CONTAINER_IMAGE="registry.freedesktop.org/libevdev/hid-tools/fedora/37:2023-02-17.1" CONTAINER_IMAGE="registry.freedesktop.org/bentiss/hid/fedora/39:2023-11-22.1"
TARGETS="${TARGETS:=$(basename ${SCRIPT_DIR})}" TARGETS="${TARGETS:=$(basename ${SCRIPT_DIR})}"
DEFAULT_COMMAND="pip3 install hid-tools; make -C tools/testing/selftests TARGETS=${TARGETS} run_tests" DEFAULT_COMMAND="pip3 install hid-tools; make -C tools/testing/selftests TARGETS=${TARGETS} run_tests"
...@@ -32,7 +32,7 @@ DEFAULT_COMMAND="pip3 install hid-tools; make -C tools/testing/selftests TARGETS ...@@ -32,7 +32,7 @@ DEFAULT_COMMAND="pip3 install hid-tools; make -C tools/testing/selftests TARGETS
usage() usage()
{ {
cat <<EOF cat <<EOF
Usage: $0 [-i] [-s] [-d <output_dir>] -- [<command>] Usage: $0 [-j N] [-s] [-b] [-d <output_dir>] -- [<command>]
<command> is the command you would normally run when you are in <command> is the command you would normally run when you are in
the source kernel direcory. e.g: the source kernel direcory. e.g:
...@@ -55,6 +55,7 @@ Options: ...@@ -55,6 +55,7 @@ Options:
-u) Update the boot2container script to a newer version. -u) Update the boot2container script to a newer version.
-d) Update the output directory (default: ${OUTPUT_DIR}) -d) Update the output directory (default: ${OUTPUT_DIR})
-b) Run only the build steps for the kernel and the selftests
-j) Number of jobs for compilation, similar to -j in make -j) Number of jobs for compilation, similar to -j in make
(default: ${NUM_COMPILE_JOBS}) (default: ${NUM_COMPILE_JOBS})
-s) Instead of powering off the VM, start an interactive -s) Instead of powering off the VM, start an interactive
...@@ -191,8 +192,9 @@ main() ...@@ -191,8 +192,9 @@ main()
local command="${DEFAULT_COMMAND}" local command="${DEFAULT_COMMAND}"
local update_b2c="no" local update_b2c="no"
local debug_shell="no" local debug_shell="no"
local build_only="no"
while getopts ':hsud:j:' opt; do while getopts ':hsud:j:b' opt; do
case ${opt} in case ${opt} in
u) u)
update_b2c="yes" update_b2c="yes"
...@@ -207,6 +209,9 @@ main() ...@@ -207,6 +209,9 @@ main()
command="/bin/sh" command="/bin/sh"
debug_shell="yes" debug_shell="yes"
;; ;;
b)
build_only="yes"
;;
h) h)
usage usage
exit 0 exit 0
...@@ -226,8 +231,7 @@ main() ...@@ -226,8 +231,7 @@ main()
shift $((OPTIND -1)) shift $((OPTIND -1))
# trap 'catch "$?"' EXIT # trap 'catch "$?"' EXIT
if [[ "${build_only}" == "no" && "${debug_shell}" == "no" ]]; then
if [[ "${debug_shell}" == "no" ]]; then
if [[ $# -eq 0 ]]; then if [[ $# -eq 0 ]]; then
echo "No command specified, will run ${DEFAULT_COMMAND} in the vm" echo "No command specified, will run ${DEFAULT_COMMAND} in the vm"
else else
...@@ -267,7 +271,9 @@ main() ...@@ -267,7 +271,9 @@ main()
update_kconfig "${kernel_checkout}" "${kconfig_file}" update_kconfig "${kernel_checkout}" "${kconfig_file}"
recompile_kernel "${kernel_checkout}" "${make_command}" recompile_kernel "${kernel_checkout}" "${make_command}"
update_selftests "${kernel_checkout}" "${make_command}"
if [[ "${build_only}" == "no" ]]; then
if [[ "${update_b2c}" == "no" && ! -f "${b2c}" ]]; then if [[ "${update_b2c}" == "no" && ! -f "${b2c}" ]]; then
echo "vm2c script not found in ${b2c}" echo "vm2c script not found in ${b2c}"
update_b2c="yes" update_b2c="yes"
...@@ -278,13 +284,13 @@ main() ...@@ -278,13 +284,13 @@ main()
chmod +x $b2c chmod +x $b2c
fi fi
update_selftests "${kernel_checkout}" "${make_command}"
run_vm "${kernel_checkout}" $b2c "${kernel_bzimage}" "${command}" run_vm "${kernel_checkout}" $b2c "${kernel_bzimage}" "${command}"
if [[ "${debug_shell}" != "yes" ]]; then if [[ "${debug_shell}" != "yes" ]]; then
echo "Logs saved in ${OUTPUT_DIR}/${LOG_FILE}" echo "Logs saved in ${OUTPUT_DIR}/${LOG_FILE}"
fi fi
exit $(cat ${OUTPUT_DIR}/${EXIT_STATUS_FILE}) exit $(cat ${OUTPUT_DIR}/${EXIT_STATUS_FILE})
fi
} }
main "$@" main "$@"
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment