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:
properties:
compatible:
items:
- const: elan,ekth6915
enum:
- elan,ekth6915
- ilitek,ili2901
reg:
const: 0x10
......
......@@ -761,14 +761,15 @@ config HID_MULTITOUCH
module will be called hid-multitouch.
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 LEDS_CLASS
select POWER_SUPPLY
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
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
module will be called hid-nintendo.
......@@ -779,9 +780,9 @@ config NINTENDO_FF
select INPUT_FF_MEMLESS
help
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
controller. For the pro controller, both rumble motors can be controlled
individually.
force feedback support for it. This works for both joy-cons, the pro
controller, and the NSO N64 controller. For the pro controller, both
rumble motors can be controlled individually.
config HID_NTI
tristate "NTI keyboard adapters"
......@@ -1296,6 +1297,15 @@ config HID_ALPS
Say Y here if you have a Alps touchpads over i2c-hid or usbhid
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
tristate "Microchip MCP2221 HID USB-to-I2C/SMbus host support"
depends on USB_HID && I2C
......
......@@ -79,6 +79,7 @@ obj-$(CONFIG_HID_LOGITECH_HIDPP) += hid-logitech-hidpp.o
obj-$(CONFIG_HID_MACALLY) += hid-macally.o
obj-$(CONFIG_HID_MAGICMOUSE) += hid-magicmouse.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_MAYFLASH) += hid-mf.o
obj-$(CONFIG_HID_MEGAWORLD_FF) += hid-megaworld.o
......
......@@ -37,6 +37,11 @@ struct amd_mp2_sensor_info {
dma_addr_t dma_address;
};
struct sfh_dev_status {
bool is_hpd_present;
bool is_als_present;
};
struct amd_mp2_dev {
struct pci_dev *pdev;
struct amdtp_cl_data *cl_data;
......@@ -47,6 +52,7 @@ struct amd_mp2_dev {
struct amd_input_data in_data;
/* mp2 active control status */
u32 mp2_acs;
struct sfh_dev_status dev_en;
};
struct amd_mp2_ops {
......
......@@ -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;
}
static int float_to_int(u32 flt32_val)
int amd_sfh_float_to_int(u32 flt32_val)
{
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,
OFFSET_SENSOR_DATA_DEFAULT;
memcpy_fromio(&accel_data, sensoraddr, sizeof(struct sfh_accel_data));
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_y_value = 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_x_value = amd_sfh_float_to_int(accel_data.acceldata.x) / 100;
acc_input.in_accel_y_value = amd_sfh_float_to_int(accel_data.acceldata.y) / 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));
report_size = sizeof(acc_input);
break;
......@@ -212,9 +212,9 @@ static u8 get_input_rep(u8 current_index, int sensor_idx, int report_id,
OFFSET_SENSOR_DATA_DEFAULT;
memcpy_fromio(&gyro_data, sensoraddr, sizeof(struct sfh_gyro_data));
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_y_value = 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_x_value = amd_sfh_float_to_int(gyro_data.gyrodata.x) / 1000;
gyro_input.in_angel_y_value = amd_sfh_float_to_int(gyro_data.gyrodata.y) / 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));
report_size = sizeof(gyro_input);
break;
......@@ -223,9 +223,9 @@ static u8 get_input_rep(u8 current_index, int sensor_idx, int report_id,
OFFSET_SENSOR_DATA_DEFAULT;
memcpy_fromio(&mag_data, sensoraddr, sizeof(struct sfh_mag_data));
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_y = 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_x = amd_sfh_float_to_int(mag_data.magdata.x) / 100;
magno_input.in_magno_y = amd_sfh_float_to_int(mag_data.magdata.y) / 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;
memcpy(input_report, &magno_input, 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,
OFFSET_SENSOR_DATA_DEFAULT;
memcpy_fromio(&als_data, sensoraddr, sizeof(struct sfh_als_data));
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));
if (binfo.sbase.s_prop[ALS_IDX].sf.feat & 0x2) {
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_y_value = float_to_int(als_data.chromaticity_y);
als_input.chromaticity_x_value =
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);
......
......@@ -73,6 +73,15 @@ static int amd_sfh_hid_client_deinit(struct amd_mp2_dev *privdata)
int i, status;
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) {
privdata->mp2_ops->stop(privdata, cl_data->sensor_idx[i]);
status = amd_sfh_wait_for_response
......@@ -178,6 +187,14 @@ static int amd_sfh1_1_hid_client_init(struct amd_mp2_dev *privdata)
rc = amdtp_hid_probe(i, cl_data);
if (rc)
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",
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)
{
struct amd_mp2_dev *mp2 = privdata;
sfh_deinit_emp2();
amd_sfh_hid_client_deinit(privdata);
mp2->mp2_ops->stop_all(mp2);
pci_intx(mp2->pdev, false);
......@@ -311,12 +329,14 @@ int amd_sfh1_1_init(struct amd_mp2_dev *mp2)
rc = amd_sfh_irq_init(mp2);
if (rc) {
sfh_deinit_emp2();
dev_err(dev, "amd_sfh_irq_init failed\n");
return rc;
}
rc = amd_sfh1_1_hid_client_init(mp2);
if (rc) {
sfh_deinit_emp2();
dev_err(dev, "amd_sfh1_1_hid_client_init failed\n");
return rc;
}
......
......@@ -7,11 +7,14 @@
*
* Author: Basavaraj Natikar <Basavaraj.Natikar@amd.com>
*/
#include <linux/amd-pmf-io.h>
#include <linux/io-64-nonatomic-lo-hi.h>
#include <linux/iopoll.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)
{
struct sfh_cmd_response cmd_resp;
......@@ -73,7 +76,63 @@ static struct amd_mp2_ops amd_sfh_ops = {
.response = amd_sfh_wait_response,
};
void sfh_deinit_emp2(void)
{
emp2 = NULL;
}
void sfh_interface_init(struct amd_mp2_dev *mp2)
{
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 {
};
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);
int amd_sfh_float_to_int(u32 flt32_val);
#endif
......@@ -2749,7 +2749,7 @@ static int hid_uevent(const struct device *dev, struct kobj_uevent_env *env)
return 0;
}
struct bus_type hid_bus_type = {
const struct bus_type hid_bus_type = {
.name = "hid",
.dev_groups = hid_dev_groups,
.drv_groups = hid_drv_groups,
......
......@@ -916,6 +916,7 @@
#define USB_DEVICE_ID_PICK16F1454 0x0042
#define USB_DEVICE_ID_PICK16F1454_V2 0xf2f7
#define USB_DEVICE_ID_LUXAFOR 0xf372
#define USB_DEVICE_ID_MCP2200 0x00df
#define USB_DEVICE_ID_MCP2221 0x00dd
#define USB_VENDOR_ID_MICROSOFT 0x045e
......@@ -987,7 +988,10 @@
#define USB_DEVICE_ID_NINTENDO_JOYCONL 0x2006
#define USB_DEVICE_ID_NINTENDO_JOYCONR 0x2007
#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_DEVICE_ID_NOVATEK_PCT 0x0600
......
......@@ -120,6 +120,9 @@ MODULE_PARM_DESC(report_undeciphered, "Report undeciphered multi-touch state fie
* @scroll_jiffies: Time of last scroll motion.
* @touches: Most recent data for a touch, indexed by tracking ID.
* @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 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 {
MCP2221_I2C_MASK_ADDR_NACK = 0x40,
MCP2221_I2C_WRADDRL_SEND = 0x21,
MCP2221_I2C_ADDR_NACK = 0x25,
MCP2221_I2C_READ_PARTIAL = 0x54,
MCP2221_I2C_READ_COMPL = 0x55,
MCP2221_ALT_F_NOT_GPIOV = 0xEE,
MCP2221_ALT_F_NOT_GPIOD = 0xEF,
......@@ -187,6 +188,25 @@ static int mcp_cancel_last_cmd(struct mcp2221 *mcp)
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)
{
int ret;
......@@ -241,7 +261,7 @@ static int mcp_i2c_write(struct mcp2221 *mcp,
usleep_range(980, 1000);
if (last_status) {
ret = mcp_chk_last_cmd_status(mcp);
ret = mcp_chk_last_cmd_status_free_bus(mcp);
if (ret)
return ret;
}
......@@ -278,6 +298,7 @@ static int mcp_i2c_smbus_read(struct mcp2221 *mcp,
{
int ret;
u16 total_len;
int retries = 0;
mcp->txbuf[0] = type;
if (msg) {
......@@ -301,20 +322,31 @@ static int mcp_i2c_smbus_read(struct mcp2221 *mcp,
mcp->rxbuf_idx = 0;
do {
/* Wait for the data to be read by the device */
usleep_range(980, 1000);
memset(mcp->txbuf, 0, 4);
mcp->txbuf[0] = MCP2221_I2C_GET_DATA;
ret = mcp_send_data_req_status(mcp, mcp->txbuf, 1);
if (ret)
return ret;
ret = mcp_chk_last_cmd_status(mcp);
if (ret)
return ret;
usleep_range(980, 1000);
if (ret) {
if (retries < 5) {
/* The data wasn't ready to read.
* Wait a bit longer and try again.
*/
usleep_range(90, 100);
retries++;
} else {
return ret;
}
} else {
retries = 0;
}
} while (mcp->rxbuf_idx < total_len);
usleep_range(980, 1000);
ret = mcp_chk_last_cmd_status_free_bus(mcp);
return ret;
}
......@@ -328,11 +360,6 @@ static int mcp_i2c_xfer(struct i2c_adapter *adapter,
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 (msgs->flags & I2C_M_RD) {
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,
if (last_status) {
usleep_range(980, 1000);
ret = mcp_chk_last_cmd_status(mcp);
if (ret)
return ret;
ret = mcp_chk_last_cmd_status_free_bus(mcp);
}
return ret;
......@@ -437,10 +462,6 @@ static int mcp_smbus_xfer(struct i2c_adapter *adapter, u16 addr,
mutex_lock(&mcp->lock);
ret = mcp_set_i2c_speed(mcp);
if (ret)
goto exit;
switch (size) {
case I2C_SMBUS_QUICK:
......@@ -791,7 +812,8 @@ static int mcp2221_raw_event(struct hid_device *hdev,
mcp->status = -EIO;
break;
}
if (data[2] == MCP2221_I2C_READ_COMPL) {
if (data[2] == MCP2221_I2C_READ_COMPL ||
data[2] == MCP2221_I2C_READ_PARTIAL) {
buf = mcp->rxbuf;
memcpy(&buf[mcp->rxbuf_idx], &data[4], data[3]);
mcp->rxbuf_idx = mcp->rxbuf_idx + data[3];
......@@ -1150,12 +1172,18 @@ static int mcp2221_probe(struct hid_device *hdev,
if (i2c_clk_freq < 50)
i2c_clk_freq = 50;
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.class = I2C_CLASS_HWMON;
mcp->adapter.algo = &mcp_i2c_algo;
mcp->adapter.retries = 1;
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),
"MCP2221 usb-i2c bridge");
......
This diff is collapsed.
......@@ -632,7 +632,7 @@ static int sensor_hub_probe(struct hid_device *hdev,
}
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) {
hid_err(hdev, "hw start failed\n");
return ret;
......
This diff is collapsed.
......@@ -44,12 +44,11 @@
#include "i2c-hid.h"
/* quirks to control the device */
#define I2C_HID_QUIRK_SET_PWR_WAKEUP_DEV BIT(0)
#define I2C_HID_QUIRK_NO_IRQ_AFTER_RESET BIT(1)
#define I2C_HID_QUIRK_BOGUS_IRQ BIT(4)
#define I2C_HID_QUIRK_RESET_ON_RESUME BIT(5)
#define I2C_HID_QUIRK_BAD_INPUT_SIZE BIT(6)
#define I2C_HID_QUIRK_NO_WAKEUP_AFTER_RESET BIT(7)
#define I2C_HID_QUIRK_NO_IRQ_AFTER_RESET BIT(0)
#define I2C_HID_QUIRK_BOGUS_IRQ BIT(1)
#define I2C_HID_QUIRK_RESET_ON_RESUME BIT(2)
#define I2C_HID_QUIRK_BAD_INPUT_SIZE BIT(3)
#define I2C_HID_QUIRK_NO_WAKEUP_AFTER_RESET BIT(4)
/* Command opcodes */
#define I2C_HID_OPCODE_RESET 0x01
......@@ -120,8 +119,6 @@ static const struct i2c_hid_quirks {
__u16 idProduct;
__u32 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_HID_QUIRK_NO_IRQ_AFTER_RESET },
{ 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)
* The call will get a return value (EREMOTEIO) but device will be
* triggered and activated. After that, it goes like a normal device.
*/
if (power_state == I2C_HID_PWR_ON &&
ihid->quirks & I2C_HID_QUIRK_SET_PWR_WAKEUP_DEV) {
if (power_state == I2C_HID_PWR_ON) {
ret = i2c_hid_set_power_command(ihid, I2C_HID_PWR_ON);
/* Device was already activated */
......@@ -426,12 +422,23 @@ static int i2c_hid_set_power(struct i2c_hid *ihid, int power_state)
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;
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. */
*(__le16 *)ihid->cmdbuf = ihid->hdesc.wCommandRegister;
......@@ -444,60 +451,40 @@ static int i2c_hid_execute_reset(struct i2c_hid *ihid)
ret = i2c_hid_xfer(ihid, ihid->cmdbuf, length, NULL, 0);
if (ret) {
dev_err(&ihid->client->dev, "failed to reset device.\n");
goto out;
}
if (ihid->quirks & I2C_HID_QUIRK_NO_IRQ_AFTER_RESET) {
msleep(100);
goto out;
dev_err(&ihid->client->dev,
"failed to reset device: %d\n", ret);
goto err_clear_reset;
}
i2c_hid_dbg(ihid, "%s: waiting...\n", __func__);
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__);
return 0;
out:
err_clear_reset:
clear_bit(I2C_HID_RESET_PENDING, &ihid->flags);
i2c_hid_set_power(ihid, I2C_HID_PWR_SLEEP);
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__);
/*
* 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;
i2c_hid_dbg(ihid, "%s: waiting...\n", __func__);
ret = i2c_hid_execute_reset(ihid);
if (ret) {
dev_err(&ihid->client->dev,
"failed to reset device: %d\n", ret);
i2c_hid_set_power(ihid, I2C_HID_PWR_SLEEP);
goto out_unlock;
if (ihid->quirks & I2C_HID_QUIRK_NO_IRQ_AFTER_RESET) {
msleep(100);
clear_bit(I2C_HID_RESET_PENDING, &ihid->flags);
} else if (!wait_event_timeout(ihid->wait,
!test_bit(I2C_HID_RESET_PENDING, &ihid->flags),
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 */
if (!(ihid->quirks & I2C_HID_QUIRK_NO_WAKEUP_AFTER_RESET))
ret = i2c_hid_set_power(ihid, I2C_HID_PWR_ON);
out_unlock:
mutex_unlock(&ihid->reset_lock);
return ret;
}
......@@ -729,11 +716,10 @@ static int i2c_hid_parse(struct hid_device *hid)
struct i2c_client *client = hid->driver_data;
struct i2c_hid *ihid = i2c_get_clientdata(client);
struct i2c_hid_desc *hdesc = &ihid->hdesc;
char *rdesc = NULL, *use_override = NULL;
unsigned int rsize;
char *rdesc;
int ret;
int tries = 3;
char *use_override;
i2c_hid_dbg(ihid, "entering %s\n", __func__);
......@@ -743,14 +729,15 @@ static int i2c_hid_parse(struct hid_device *hid)
return -EINVAL;
}
mutex_lock(&ihid->reset_lock);
do {
ret = i2c_hid_hwreset(ihid);
ret = i2c_hid_start_hwreset(ihid);
if (ret)
msleep(1000);
} while (tries-- > 0 && ret);
if (ret)
return ret;
goto abort_reset;
use_override = i2c_hid_get_dmi_hid_report_desc_override(client->name,
&rsize);
......@@ -762,8 +749,8 @@ static int i2c_hid_parse(struct hid_device *hid)
rdesc = kzalloc(rsize, GFP_KERNEL);
if (!rdesc) {
dbg_hid("couldn't allocate rdesc memory\n");
return -ENOMEM;
ret = -ENOMEM;
goto abort_reset;
}
i2c_hid_dbg(ihid, "asking HID report descriptor\n");
......@@ -773,23 +760,34 @@ static int i2c_hid_parse(struct hid_device *hid)
rdesc, rsize);
if (ret) {
hid_err(hid, "reading report descriptor failed\n");
kfree(rdesc);
return -EIO;
goto abort_reset;
}
}
/*
* 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);
ret = hid_parse_report(hid, rdesc, rsize);
if (ret)
dbg_hid("parsing report descriptor failed\n");
out:
if (!use_override)
kfree(rdesc);
if (ret) {
dbg_hid("parsing report descriptor failed\n");
return ret;
}
return 0;
return ret;
}
static int i2c_hid_start(struct hid_device *hid)
......@@ -987,10 +985,15 @@ static int i2c_hid_core_resume(struct i2c_hid *ihid)
* However some ALPS touchpads generate IRQ storm without reset, so
* let's still reset them here.
*/
if (ihid->quirks & I2C_HID_QUIRK_RESET_ON_RESUME)
ret = i2c_hid_hwreset(ihid);
else
if (ihid->quirks & I2C_HID_QUIRK_RESET_ON_RESUME) {
mutex_lock(&ihid->reset_lock);
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);
}
if (ret)
return ret;
......
......@@ -130,9 +130,17 @@ static const struct elan_i2c_hid_chip_data ilitek_ili9882t_chip_data = {
.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[] = {
{ .compatible = "elan,ekth6915", .data = &elan_ekth6915_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);
......
......@@ -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;
}
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
* @pdev: pci device
......@@ -233,7 +189,7 @@ static int ish_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
/* Enable PME for EHL */
if (pdev->device == EHL_Ax_DEVICE_ID)
enable_pme_wake(pdev);
device_init_wakeup(dev, true);
ret = ish_init(ishtp);
if (ret)
......@@ -256,6 +212,19 @@ static void ish_remove(struct pci_dev *pdev)
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;
/* 50ms to get resume response */
......@@ -378,13 +347,6 @@ static int __maybe_unused ish_resume(struct device *device)
struct pci_dev *pdev = to_pci_dev(device);
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;
dev->resume_flag = 1;
......@@ -400,6 +362,7 @@ static struct pci_driver ish_driver = {
.id_table = ish_pci_tbl,
.probe = ish_probe,
.remove = ish_remove,
.shutdown = ish_shutdown,
.driver.pm = &ish_pm_ops,
};
......
......@@ -840,43 +840,22 @@ static void load_fw_from_host_handler(struct work_struct *work)
*
* 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;
struct ishtp_fw_client *fw_client;
struct ishtp_cl_data *client_data =
ishtp_get_client_data(loader_ishtp_cl);
dev_dbg(cl_data_to_dev(client_data), "reset flag: %d\n", reset);
rv = ishtp_cl_link(loader_ishtp_cl);
if (rv < 0) {
dev_err(cl_data_to_dev(client_data), "ishtp_cl_link failed\n");
return rv;
}
/* 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);
rv = ishtp_cl_establish_connection(loader_ishtp_cl,
&loader_ishtp_id_table[0].guid,
LOADER_CL_TX_RING_SIZE,
LOADER_CL_RX_RING_SIZE,
reset);
if (rv < 0) {
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");
......@@ -885,17 +864,14 @@ static int loader_init(struct ishtp_cl *loader_ishtp_cl, int reset)
return 0;
err_cl_unlink:
ishtp_cl_unlink(loader_ishtp_cl);
err_cl_disconnect:
ishtp_cl_destroy_connection(loader_ishtp_cl, reset);
return rv;
}
static void loader_deinit(struct ishtp_cl *loader_ishtp_cl)
{
ishtp_set_connection_state(loader_ishtp_cl, ISHTP_CL_DISCONNECTING);
ishtp_cl_disconnect(loader_ishtp_cl);
ishtp_cl_unlink(loader_ishtp_cl);
ishtp_cl_flush_queues(loader_ishtp_cl);
ishtp_cl_destroy_connection(loader_ishtp_cl, false);
/* Disband and free all Tx and Rx client-level rings */
ishtp_cl_free(loader_ishtp_cl);
......@@ -914,19 +890,7 @@ static void reset_handler(struct work_struct *work)
loader_ishtp_cl = client_data->loader_ishtp_cl;
cl_device = client_data->cl_device;
/* Unlink, flush queues & start again */
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;
ishtp_cl_destroy_connection(loader_ishtp_cl, true);
rv = loader_init(loader_ishtp_cl, 1);
if (rv < 0) {
......@@ -974,7 +938,7 @@ static int loader_ishtp_cl_probe(struct ishtp_cl_device *cl_device)
INIT_WORK(&client_data->work_fw_load,
load_fw_from_host_handler);
rv = loader_init(loader_ishtp_cl, 0);
rv = loader_init(loader_ishtp_cl, false);
if (rv < 0) {
ishtp_cl_free(loader_ishtp_cl);
return rv;
......
......@@ -639,47 +639,26 @@ static int ishtp_get_report_descriptor(struct ishtp_cl *hid_ishtp_cl,
*
* 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_fw_client *fw_client;
int i;
int rv;
dev_dbg(cl_data_to_dev(client_data), "%s\n", __func__);
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;
dev = ishtp_get_ishtp_device(hid_ishtp_cl);
/* Connect to FW client */
ishtp_set_tx_ring_size(hid_ishtp_cl, HID_CL_TX_RING_SIZE);
ishtp_set_rx_ring_size(hid_ishtp_cl, HID_CL_RX_RING_SIZE);
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);
rv = ishtp_cl_establish_connection(hid_ishtp_cl,
&hid_ishtp_id_table[0].guid,
HID_CL_TX_RING_SIZE,
HID_CL_RX_RING_SIZE,
reset);
if (rv) {
dev_err(cl_data_to_dev(client_data),
"client connect fail\n");
goto err_cl_unlink;
goto err_cl_disconnect;
}
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)
return 0;
err_cl_disconnect:
ishtp_set_connection_state(hid_ishtp_cl, ISHTP_CL_DISCONNECTING);
ishtp_cl_disconnect(hid_ishtp_cl);
err_cl_unlink:
ishtp_cl_unlink(hid_ishtp_cl);
ishtp_cl_destroy_connection(hid_ishtp_cl, reset);
return rv;
}
......@@ -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)
{
ishtp_cl_unlink(hid_ishtp_cl);
ishtp_cl_flush_queues(hid_ishtp_cl);
ishtp_cl_destroy_connection(hid_ishtp_cl, false);
/* disband and free all Tx and Rx client-level rings */
ishtp_cl_free(hid_ishtp_cl);
......@@ -749,33 +724,23 @@ static void hid_ishtp_cl_reset_handler(struct work_struct *work)
{
struct ishtp_cl_data *client_data;
struct ishtp_cl *hid_ishtp_cl;
struct ishtp_cl_device *cl_device;
int retry;
int rv;
client_data = container_of(work, struct ishtp_cl_data, work);
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_cl);
dev_dbg(ishtp_device(client_data->cl_device), "%s\n", __func__);
hid_ishtp_cl_deinit(hid_ishtp_cl);
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;
ishtp_cl_destroy_connection(hid_ishtp_cl, true);
client_data->num_hid_devices = 0;
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)
break;
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)
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) {
ishtp_cl_free(hid_ishtp_cl);
return rv;
......@@ -868,11 +833,9 @@ static void hid_ishtp_cl_remove(struct ishtp_cl_device *cl_device)
hid_ishtp_cl);
dev_dbg(ishtp_device(cl_device), "%s\n", __func__);
ishtp_set_connection_state(hid_ishtp_cl, ISHTP_CL_DISCONNECTING);
ishtp_cl_disconnect(hid_ishtp_cl);
hid_ishtp_cl_deinit(hid_ishtp_cl);
ishtp_put_device(cl_device);
ishtp_hid_remove(client_data);
hid_ishtp_cl_deinit(hid_ishtp_cl);
hid_ishtp_cl = NULL;
......
......@@ -378,7 +378,7 @@ static const struct dev_pm_ops ishtp_cl_bus_dev_pm_ops = {
.restore = ishtp_cl_device_resume,
};
static struct bus_type ishtp_cl_bus_type = {
static const struct bus_type ishtp_cl_bus_type = {
.name = "ishtp",
.dev_groups = ishtp_cl_dev_groups,
.probe = ishtp_cl_device_probe,
......
......@@ -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
*
* Send a connect request for a client to firmware. If successful it will
* RX and TX ring buffers
* Send a connect request to the firmware and wait for firmware response.
* 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
* to bind and allocate ring buffers or error code on failure
* Return: 0 for success and 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;
int rets;
......@@ -358,8 +359,6 @@ int ishtp_cl_connect(struct ishtp_cl *cl)
dev = cl->dev;
dev->print_log(dev, "%s() current_state = %d\n", __func__, cl->state);
if (ishtp_cl_is_other_connecting(cl)) {
dev->print_log(dev, "%s() Busy\n", __func__);
return -EBUSY;
......@@ -405,6 +404,38 @@ int ishtp_cl_connect(struct ishtp_cl *cl)
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);
if (rets) {
dev->print_log(dev, "%s() Alloc RX ring failed\n", __func__);
......@@ -422,15 +453,147 @@ int ishtp_cl_connect(struct ishtp_cl *cl)
return rets;
}
/* Upon successful connection and allocation, emit flow-control */
/*
* Upon successful connection and allocation, start flow-control.
*/
rets = ishtp_cl_read_start(cl);
dev->print_log(dev, "%s() successful\n", __func__);
return rets;
}
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
* @cl: client device instance
......
......@@ -164,6 +164,7 @@ struct wacom {
struct work_struct battery_work;
struct work_struct remote_work;
struct delayed_work init_work;
struct delayed_work aes_battery_work;
struct wacom_remote *remote;
struct work_struct mode_change_work;
struct timer_list idleprox_timer;
......
......@@ -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,
struct device_attribute
*attr, char *buf)
......@@ -2794,6 +2801,7 @@ static int wacom_probe(struct hid_device *hdev,
mutex_init(&wacom->lock);
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->battery_work, wacom_battery_work);
INIT_WORK(&wacom->remote_work, wacom_remote_work);
......
......@@ -2528,11 +2528,12 @@ static void wacom_wac_pen_report(struct hid_device *hdev,
struct input_dev *input = wacom_wac->pen_input;
bool range = wacom_wac->hid_data.inrange_state;
bool sense = wacom_wac->hid_data.sense_state;
bool entering_range = !wacom_wac->tool[0] && range;
if (wacom_wac->is_invalid_bt_frame)
return;
if (!wacom_wac->tool[0] && range) { /* first in range */
if (entering_range) { /* first in range */
/* Going into range select tool */
if (wacom_wac->hid_data.invert_state)
wacom_wac->tool[0] = BTN_TOOL_RUBBER;
......@@ -2583,6 +2584,15 @@ static void wacom_wac_pen_report(struct hid_device *hdev,
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) {
wacom_wac->tool[0] = 0;
wacom_wac->id[0] = 0;
......@@ -2649,8 +2659,8 @@ static void wacom_wac_finger_slot(struct wacom_wac *wacom_wac,
{
struct hid_data *hid_data = &wacom_wac->hid_data;
bool mt = wacom_wac->features.touch_max > 1;
bool prox = hid_data->tipswitch &&
report_touch_events(wacom_wac);
bool touch_down = hid_data->tipswitch && hid_data->confidence;
bool prox = touch_down && report_touch_events(wacom_wac);
if (touch_is_muted(wacom_wac)) {
if (!wacom_wac->shared->touch_down)
......@@ -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,
struct hid_field *field, struct hid_usage *usage, __s32 value)
{
......@@ -2768,14 +2760,8 @@ static void wacom_wac_finger_event(struct hid_device *hdev,
}
if (usage->usage_index + 1 == field->report_count) {
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);
}
}
if (equivalent_usage == wacom_wac->hid_data.last_slot_field)
wacom_wac_finger_slot(wacom_wac, wacom_wac->touch_input);
}
}
......
......@@ -14,6 +14,7 @@
#define WACOM_MAX_REMOTES 5
#define WACOM_STATUS_UNKNOWN 255
#define WACOM_REMOTE_BATTERY_TIMEOUT 21000000000ll
#define WACOM_AES_BATTERY_TIMEOUT 1800000
/* packet length for individual models */
#define WACOM_PKGLEN_BBFUN 9
......
......@@ -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_cl: ISHTP client instance
* @reset: true if called from reset handler
*
* This function complete the initializtion of the client.
*
* 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;
struct ishtp_device *dev;
struct ishtp_fw_client *fw_client;
struct ishtp_cl_data *client_data = ishtp_get_client_data(cros_ish_cl);
rv = ishtp_cl_link(cros_ish_cl);
if (rv) {
dev_err(cl_data_to_dev(client_data),
"ishtp_cl_link failed\n");
return rv;
}
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);
rv = ishtp_cl_establish_connection(cros_ish_cl,
&cros_ec_ishtp_id_table[0].guid,
CROS_ISH_CL_TX_RING_SIZE,
CROS_ISH_CL_RX_RING_SIZE,
reset);
if (rv) {
dev_err(cl_data_to_dev(client_data),
"client connect fail\n");
goto err_cl_unlink;
goto err_cl_disconnect;
}
ishtp_register_event_cb(client_data->cl_device, ish_event_cb);
return 0;
err_cl_unlink:
ishtp_cl_unlink(cros_ish_cl);
err_cl_disconnect:
ishtp_cl_destroy_connection(cros_ish_cl, reset);
return rv;
}
......@@ -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)
{
ishtp_set_connection_state(cros_ish_cl, ISHTP_CL_DISCONNECTING);
ishtp_cl_disconnect(cros_ish_cl);
ishtp_cl_unlink(cros_ish_cl);
ishtp_cl_flush_queues(cros_ish_cl);
ishtp_cl_destroy_connection(cros_ish_cl, false);
/* Disband and free all Tx and Rx client-level rings */
ishtp_cl_free(cros_ish_cl);
......@@ -592,7 +567,6 @@ static void reset_handler(struct work_struct *work)
int rv;
struct device *dev;
struct ishtp_cl *cros_ish_cl;
struct ishtp_cl_device *cl_device;
struct ishtp_cl_data *client_data =
container_of(work, struct ishtp_cl_data, work_ishtp_reset);
......@@ -600,26 +574,11 @@ static void reset_handler(struct work_struct *work)
down_write(&init_lock);
cros_ish_cl = client_data->cros_ish_cl;
cl_device = client_data->cl_device;
/* Unlink, flush queues & start again */
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;
ishtp_cl_destroy_connection(cros_ish_cl, true);
rv = cros_ish_init(cros_ish_cl);
rv = cros_ish_init(cros_ish_cl, true);
if (rv) {
ishtp_cl_free(cros_ish_cl);
dev_err(cl_data_to_dev(client_data), "Reset Failed\n");
up_write(&init_lock);
return;
......@@ -672,7 +631,7 @@ static int cros_ec_ishtp_probe(struct ishtp_cl_device *cl_device)
INIT_WORK(&client_data->work_ec_evt,
ish_evt_handler);
rv = cros_ish_init(cros_ish_cl);
rv = cros_ish_init(cros_ish_cl, false);
if (rv)
goto end_ishtp_cl_init_error;
......@@ -690,10 +649,7 @@ static int cros_ec_ishtp_probe(struct ishtp_cl_device *cl_device)
return 0;
end_cros_ec_dev_init_error:
ishtp_set_connection_state(cros_ish_cl, ISHTP_CL_DISCONNECTING);
ishtp_cl_disconnect(cros_ish_cl);
ishtp_cl_unlink(cros_ish_cl);
ishtp_cl_flush_queues(cros_ish_cl);
ishtp_cl_destroy_connection(cros_ish_cl, false);
ishtp_put_device(cl_device);
end_ishtp_cl_init_error:
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 *);
extern int hid_add_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 *,
struct module *, const char *mod_name);
......
......@@ -115,7 +115,7 @@ struct hid_bpf_ops {
size_t len, enum hid_report_type rtype,
enum hid_class_request reqtype);
struct module *owner;
struct bus_type *bus_type;
const struct bus_type *bus_type;
};
extern struct hid_bpf_ops *hid_bpf_ops;
......
......@@ -94,6 +94,9 @@ int ishtp_cl_link(struct ishtp_cl *cl);
void ishtp_cl_unlink(struct ishtp_cl *cl);
int ishtp_cl_disconnect(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_flush_queues(struct ishtp_cl *cl);
int ishtp_cl_io_rb_recycle(struct ishtp_cl_rb *rb);
......
......@@ -14,7 +14,7 @@ import logging
from hidtools.device.base_device import BaseDevice, EvdevMatch, SysfsFile
from pathlib import Path
from typing import Final
from typing import Final, List, Tuple
logger = logging.getLogger("hidtools.test.base")
......@@ -155,7 +155,7 @@ class BaseTestCase:
# if any module is not available (not compiled), the test will skip.
# Each element is a tuple '(kernel driver name, kernel module)',
# for example ("playstation", "hid-playstation")
kernel_modules = []
kernel_modules: List[Tuple[str, str]] = []
def assertInputEventsIn(self, expected_events, effective_events):
effective_events = effective_events.copy()
......@@ -238,8 +238,7 @@ class BaseTestCase:
try:
with HIDTestUdevRule.instance():
with new_uhdev as self.uhdev:
skip_cond = request.node.get_closest_marker("skip_if_uhdev")
if skip_cond:
for skip_cond in request.node.iter_markers("skip_if_uhdev"):
test, message, *rest = skip_cond.args
if test(self.uhdev):
......
......@@ -52,13 +52,13 @@ class BaseMouse(base.UHIDTestDevice):
:param reportID: the numeric report ID for this report, if needed
"""
if buttons is not None:
l, r, m = buttons
if l is not None:
self.left = l
if r is not None:
self.right = r
if m is not None:
self.middle = m
left, right, middle = buttons
if left is not None:
self.left = left
if right is not None:
self.right = right
if middle is not None:
self.middle = middle
left = self.left
right = self.right
middle = self.middle
......
......@@ -19,12 +19,12 @@ esac
SCRIPT_DIR="$(dirname $(realpath $0))"
OUTPUT_DIR="$SCRIPT_DIR/results"
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)"
LOG_FILE_BASE="$(date +"hid_selftests.%Y-%m-%d_%H-%M-%S")"
LOG_FILE="${LOG_FILE_BASE}.log"
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})}"
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
usage()
{
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
the source kernel direcory. e.g:
......@@ -55,6 +55,7 @@ Options:
-u) Update the boot2container script to a newer version.
-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
(default: ${NUM_COMPILE_JOBS})
-s) Instead of powering off the VM, start an interactive
......@@ -191,8 +192,9 @@ main()
local command="${DEFAULT_COMMAND}"
local update_b2c="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
u)
update_b2c="yes"
......@@ -207,6 +209,9 @@ main()
command="/bin/sh"
debug_shell="yes"
;;
b)
build_only="yes"
;;
h)
usage
exit 0
......@@ -226,8 +231,7 @@ main()
shift $((OPTIND -1))
# trap 'catch "$?"' EXIT
if [[ "${debug_shell}" == "no" ]]; then
if [[ "${build_only}" == "no" && "${debug_shell}" == "no" ]]; then
if [[ $# -eq 0 ]]; then
echo "No command specified, will run ${DEFAULT_COMMAND} in the vm"
else
......@@ -267,24 +271,26 @@ main()
update_kconfig "${kernel_checkout}" "${kconfig_file}"
recompile_kernel "${kernel_checkout}" "${make_command}"
update_selftests "${kernel_checkout}" "${make_command}"
if [[ "${update_b2c}" == "no" && ! -f "${b2c}" ]]; then
echo "vm2c script not found in ${b2c}"
update_b2c="yes"
fi
if [[ "${build_only}" == "no" ]]; then
if [[ "${update_b2c}" == "no" && ! -f "${b2c}" ]]; then
echo "vm2c script not found in ${b2c}"
update_b2c="yes"
fi
if [[ "${update_b2c}" == "yes" ]]; then
download $B2C_URL $b2c
chmod +x $b2c
fi
if [[ "${update_b2c}" == "yes" ]]; then
download $B2C_URL $b2c
chmod +x $b2c
fi
update_selftests "${kernel_checkout}" "${make_command}"
run_vm "${kernel_checkout}" $b2c "${kernel_bzimage}" "${command}"
if [[ "${debug_shell}" != "yes" ]]; then
echo "Logs saved in ${OUTPUT_DIR}/${LOG_FILE}"
fi
run_vm "${kernel_checkout}" $b2c "${kernel_bzimage}" "${command}"
if [[ "${debug_shell}" != "yes" ]]; then
echo "Logs saved in ${OUTPUT_DIR}/${LOG_FILE}"
fi
exit $(cat ${OUTPUT_DIR}/${EXIT_STATUS_FILE})
exit $(cat ${OUTPUT_DIR}/${EXIT_STATUS_FILE})
fi
}
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