alistair23-linux/drivers/usb/typec/mux/pi3usb30532.c
Hans de Goede 97341ef707 usb: typec: pi3usb30532: Set switch_ / mux_desc name field to NULL
Since commit ef441dd6af ("usb: typec: mux: Allow the muxes to be named")
the typec_switch_desc and typec_mux_desc structs contain a name field.

The pi3usb30532 driver allocates these structs on the stack and so far did
not explicitly zero the mem used for the structs. This causes the new name
fields to point to a random memory address, which in my test case happens
to be a valid address leading to "interesting" mux / switch names:

[root@localhost ~]# ls -l /sys/class/typec_mux/
total 0
lrwxrwxrwx. 1 root root 0 Apr 14 12:55 ''$'\r''-switch' -> ...
lrwxrwxrwx. 1 root root 0 Apr 14 12:55 ''$'\320\302\006''2'$'...

Explicitly initialize the structs to zero when declaring them on the stack
so that any unused fields get set to 0, fixing this.

Fixes: ef441dd6af ("usb: typec: mux: Allow the muxes to be named")
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Acked-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Link: https://lore.kernel.org/r/20200414133313.131802-1-hdegoede@redhat.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2020-04-16 14:52:09 +02:00

192 lines
4.2 KiB
C

// SPDX-License-Identifier: GPL-2.0+
/*
* Pericom PI3USB30532 Type-C cross switch / mux driver
*
* Copyright (c) 2017-2018 Hans de Goede <hdegoede@redhat.com>
*/
#include <linux/i2c.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/usb/typec_dp.h>
#include <linux/usb/typec_mux.h>
#define PI3USB30532_CONF 0x00
#define PI3USB30532_CONF_OPEN 0x00
#define PI3USB30532_CONF_SWAP 0x01
#define PI3USB30532_CONF_4LANE_DP 0x02
#define PI3USB30532_CONF_USB3 0x04
#define PI3USB30532_CONF_USB3_AND_2LANE_DP 0x06
struct pi3usb30532 {
struct i2c_client *client;
struct mutex lock; /* protects the cached conf register */
struct typec_switch *sw;
struct typec_mux *mux;
u8 conf;
};
static int pi3usb30532_set_conf(struct pi3usb30532 *pi, u8 new_conf)
{
int ret = 0;
if (pi->conf == new_conf)
return 0;
ret = i2c_smbus_write_byte_data(pi->client, PI3USB30532_CONF, new_conf);
if (ret) {
dev_err(&pi->client->dev, "Error writing conf: %d\n", ret);
return ret;
}
pi->conf = new_conf;
return 0;
}
static int pi3usb30532_sw_set(struct typec_switch *sw,
enum typec_orientation orientation)
{
struct pi3usb30532 *pi = typec_switch_get_drvdata(sw);
u8 new_conf;
int ret;
mutex_lock(&pi->lock);
new_conf = pi->conf;
switch (orientation) {
case TYPEC_ORIENTATION_NONE:
new_conf = PI3USB30532_CONF_OPEN;
break;
case TYPEC_ORIENTATION_NORMAL:
new_conf &= ~PI3USB30532_CONF_SWAP;
break;
case TYPEC_ORIENTATION_REVERSE:
new_conf |= PI3USB30532_CONF_SWAP;
break;
}
ret = pi3usb30532_set_conf(pi, new_conf);
mutex_unlock(&pi->lock);
return ret;
}
static int
pi3usb30532_mux_set(struct typec_mux *mux, struct typec_mux_state *state)
{
struct pi3usb30532 *pi = typec_mux_get_drvdata(mux);
u8 new_conf;
int ret;
mutex_lock(&pi->lock);
new_conf = pi->conf;
switch (state->mode) {
case TYPEC_STATE_SAFE:
new_conf = (new_conf & PI3USB30532_CONF_SWAP) |
PI3USB30532_CONF_OPEN;
break;
case TYPEC_STATE_USB:
new_conf = (new_conf & PI3USB30532_CONF_SWAP) |
PI3USB30532_CONF_USB3;
break;
case TYPEC_DP_STATE_C:
case TYPEC_DP_STATE_E:
new_conf = (new_conf & PI3USB30532_CONF_SWAP) |
PI3USB30532_CONF_4LANE_DP;
break;
case TYPEC_DP_STATE_D:
new_conf = (new_conf & PI3USB30532_CONF_SWAP) |
PI3USB30532_CONF_USB3_AND_2LANE_DP;
break;
default:
break;
}
ret = pi3usb30532_set_conf(pi, new_conf);
mutex_unlock(&pi->lock);
return ret;
}
static int pi3usb30532_probe(struct i2c_client *client)
{
struct device *dev = &client->dev;
struct typec_switch_desc sw_desc = { };
struct typec_mux_desc mux_desc = { };
struct pi3usb30532 *pi;
int ret;
pi = devm_kzalloc(dev, sizeof(*pi), GFP_KERNEL);
if (!pi)
return -ENOMEM;
pi->client = client;
mutex_init(&pi->lock);
ret = i2c_smbus_read_byte_data(client, PI3USB30532_CONF);
if (ret < 0) {
dev_err(dev, "Error reading config register %d\n", ret);
return ret;
}
pi->conf = ret;
sw_desc.drvdata = pi;
sw_desc.fwnode = dev->fwnode;
sw_desc.set = pi3usb30532_sw_set;
pi->sw = typec_switch_register(dev, &sw_desc);
if (IS_ERR(pi->sw)) {
dev_err(dev, "Error registering typec switch: %ld\n",
PTR_ERR(pi->sw));
return PTR_ERR(pi->sw);
}
mux_desc.drvdata = pi;
mux_desc.fwnode = dev->fwnode;
mux_desc.set = pi3usb30532_mux_set;
pi->mux = typec_mux_register(dev, &mux_desc);
if (IS_ERR(pi->mux)) {
typec_switch_unregister(pi->sw);
dev_err(dev, "Error registering typec mux: %ld\n",
PTR_ERR(pi->mux));
return PTR_ERR(pi->mux);
}
i2c_set_clientdata(client, pi);
return 0;
}
static int pi3usb30532_remove(struct i2c_client *client)
{
struct pi3usb30532 *pi = i2c_get_clientdata(client);
typec_mux_unregister(pi->mux);
typec_switch_unregister(pi->sw);
return 0;
}
static const struct i2c_device_id pi3usb30532_table[] = {
{ "pi3usb30532" },
{ }
};
MODULE_DEVICE_TABLE(i2c, pi3usb30532_table);
static struct i2c_driver pi3usb30532_driver = {
.driver = {
.name = "pi3usb30532",
},
.probe_new = pi3usb30532_probe,
.remove = pi3usb30532_remove,
.id_table = pi3usb30532_table,
};
module_i2c_driver(pi3usb30532_driver);
MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
MODULE_DESCRIPTION("Pericom PI3USB30532 Type-C mux driver");
MODULE_LICENSE("GPL");