Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7c7a6077 authored by Alexander Usyskin's avatar Alexander Usyskin Committed by Greg Kroah-Hartman
Browse files

mei: bus: split RX and async notification callbacks



Split callbacks for RX and async notification events on mei bus to
eliminate synchronization problems and to open way for RX optimizations.

Signed-off-by: default avatarAlexander Usyskin <alexander.usyskin@intel.com>
Signed-off-by: default avatarTomas Winkler <tomas.winkler@intel.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent c110cdb1
Loading
Loading
Loading
Loading
+82 −59
Original line number Diff line number Diff line
@@ -209,31 +209,40 @@ ssize_t mei_cldev_recv(struct mei_cl_device *cldev, u8 *buf, size_t length)
EXPORT_SYMBOL_GPL(mei_cldev_recv);

/**
 * mei_cl_bus_event_work  - dispatch rx event for a bus device
 *    and schedule new work
 * mei_cl_bus_rx_work - dispatch rx event for a bus device
 *
 * @work: work
 */
static void mei_cl_bus_event_work(struct work_struct *work)
static void mei_cl_bus_rx_work(struct work_struct *work)
{
	struct mei_cl_device *cldev;
	struct mei_device *bus;

	cldev = container_of(work, struct mei_cl_device, event_work);
	cldev = container_of(work, struct mei_cl_device, rx_work);

	bus = cldev->bus;

	if (cldev->event_cb)
		cldev->event_cb(cldev, cldev->events);
	if (cldev->rx_cb)
		cldev->rx_cb(cldev);

	cldev->events = 0;

	/* Prepare for the next read */
	if (cldev->events_mask & BIT(MEI_CL_EVENT_RX)) {
	mutex_lock(&bus->device_lock);
	mei_cl_read_start(cldev->cl, mei_cl_mtu(cldev->cl), NULL);
	mutex_unlock(&bus->device_lock);
}

/**
 * mei_cl_bus_notif_work - dispatch FW notif event for a bus device
 *
 * @work: work
 */
static void mei_cl_bus_notif_work(struct work_struct *work)
{
	struct mei_cl_device *cldev;

	cldev = container_of(work, struct mei_cl_device, notif_work);

	if (cldev->notif_cb)
		cldev->notif_cb(cldev);
}

/**
@@ -248,18 +257,13 @@ bool mei_cl_bus_notify_event(struct mei_cl *cl)
{
	struct mei_cl_device *cldev = cl->cldev;

	if (!cldev || !cldev->event_cb)
		return false;

	if (!(cldev->events_mask & BIT(MEI_CL_EVENT_NOTIF)))
	if (!cldev || !cldev->notif_cb)
		return false;

	if (!cl->notify_ev)
		return false;

	set_bit(MEI_CL_EVENT_NOTIF, &cldev->events);

	schedule_work(&cldev->event_work);
	schedule_work(&cldev->notif_work);

	cl->notify_ev = false;

@@ -278,64 +282,81 @@ bool mei_cl_bus_rx_event(struct mei_cl *cl)
{
	struct mei_cl_device *cldev = cl->cldev;

	if (!cldev || !cldev->event_cb)
		return false;

	if (!(cldev->events_mask & BIT(MEI_CL_EVENT_RX)))
	if (!cldev || !cldev->rx_cb)
		return false;

	set_bit(MEI_CL_EVENT_RX, &cldev->events);

	schedule_work(&cldev->event_work);
	schedule_work(&cldev->rx_work);

	return true;
}

/**
 * mei_cldev_register_event_cb - register event callback
 * mei_cldev_register_rx_cb - register Rx event callback
 *
 * @cldev: me client devices
 * @event_cb: callback function
 * @events_mask: requested events bitmask
 * @rx_cb: callback function
 *
 * Return: 0 on success
 *         -EALREADY if an callback is already registered
 *         <0 on other errors
 */
int mei_cldev_register_event_cb(struct mei_cl_device *cldev,
				unsigned long events_mask,
				mei_cldev_event_cb_t event_cb)
int mei_cldev_register_rx_cb(struct mei_cl_device *cldev, mei_cldev_cb_t rx_cb)
{
	struct mei_device *bus = cldev->bus;
	int ret;

	if (cldev->event_cb)
	if (!rx_cb)
		return -EINVAL;
	if (cldev->rx_cb)
		return -EALREADY;

	cldev->events = 0;
	cldev->events_mask = events_mask;
	cldev->event_cb = event_cb;
	INIT_WORK(&cldev->event_work, mei_cl_bus_event_work);
	cldev->rx_cb = rx_cb;
	INIT_WORK(&cldev->rx_work, mei_cl_bus_rx_work);

	if (cldev->events_mask & BIT(MEI_CL_EVENT_RX)) {
	mutex_lock(&bus->device_lock);
	ret = mei_cl_read_start(cldev->cl, mei_cl_mtu(cldev->cl), NULL);
	mutex_unlock(&bus->device_lock);
	if (ret && ret != -EBUSY)
		return ret;

	return 0;
}
EXPORT_SYMBOL_GPL(mei_cldev_register_rx_cb);

/**
 * mei_cldev_register_notif_cb - register FW notification event callback
 *
 * @cldev: me client devices
 * @notif_cb: callback function
 *
 * Return: 0 on success
 *         -EALREADY if an callback is already registered
 *         <0 on other errors
 */
int mei_cldev_register_notif_cb(struct mei_cl_device *cldev,
				mei_cldev_cb_t notif_cb)
{
	struct mei_device *bus = cldev->bus;
	int ret;

	if (!notif_cb)
		return -EINVAL;

	if (cldev->notif_cb)
		return -EALREADY;

	cldev->notif_cb = notif_cb;
	INIT_WORK(&cldev->notif_work, mei_cl_bus_notif_work);

	if (cldev->events_mask & BIT(MEI_CL_EVENT_NOTIF)) {
	mutex_lock(&bus->device_lock);
		ret = mei_cl_notify_request(cldev->cl, NULL, event_cb ? 1 : 0);
	ret = mei_cl_notify_request(cldev->cl, NULL, 1);
	mutex_unlock(&bus->device_lock);
	if (ret)
		return ret;
	}

	return 0;
}
EXPORT_SYMBOL_GPL(mei_cldev_register_event_cb);
EXPORT_SYMBOL_GPL(mei_cldev_register_notif_cb);

/**
 * mei_cldev_get_drvdata - driver data getter
@@ -471,8 +492,6 @@ int mei_cldev_disable(struct mei_cl_device *cldev)

	bus = cldev->bus;

	cldev->event_cb = NULL;

	mutex_lock(&bus->device_lock);

	if (!mei_cl_is_connected(cl)) {
@@ -619,9 +638,13 @@ static int mei_cl_device_remove(struct device *dev)
	if (!cldev || !dev->driver)
		return 0;

	if (cldev->event_cb) {
		cldev->event_cb = NULL;
		cancel_work_sync(&cldev->event_work);
	if (cldev->rx_cb) {
		cancel_work_sync(&cldev->rx_work);
		cldev->rx_cb = NULL;
	}
	if (cldev->notif_cb) {
		cancel_work_sync(&cldev->notif_work);
		cldev->notif_cb = NULL;
	}

	cldrv = to_mei_cl_driver(dev->driver);
+5 −0
Original line number Diff line number Diff line
@@ -673,6 +673,11 @@ int mei_cl_unlink(struct mei_cl *cl)
	list_del_init(&cl->link);

	cl->state = MEI_FILE_UNINITIALIZED;
	cl->writing_state = MEI_IDLE;

	WARN_ON(!list_empty(&cl->rd_completed) ||
		!list_empty(&cl->rd_pending) ||
		!list_empty(&cl->link));

	return 0;
}
+16 −20
Original line number Diff line number Diff line
@@ -297,9 +297,11 @@ static int mei_nfc_recv(struct nfc_mei_phy *phy, u8 *buf, size_t length)
}


static void nfc_mei_event_cb(struct mei_cl_device *cldev, u32 events)
static void nfc_mei_rx_cb(struct mei_cl_device *cldev)
{
	struct nfc_mei_phy *phy = mei_cldev_get_drvdata(cldev);
	struct sk_buff *skb;
	int reply_size;

	if (!phy)
		return;
@@ -307,10 +309,6 @@ static void nfc_mei_event_cb(struct mei_cl_device *cldev, u32 events)
	if (phy->hard_fault != 0)
		return;

	if (events & BIT(MEI_CL_EVENT_RX)) {
		struct sk_buff *skb;
		int reply_size;

	skb = alloc_skb(MEI_NFC_MAX_READ, GFP_KERNEL);
	if (!skb)
		return;
@@ -328,7 +326,6 @@ static void nfc_mei_event_cb(struct mei_cl_device *cldev, u32 events)

	nfc_hci_recv_frame(phy->hdev, skb);
}
}

static int nfc_mei_phy_enable(void *phy_id)
{
@@ -358,8 +355,7 @@ static int nfc_mei_phy_enable(void *phy_id)
		goto err;
	}

	r = mei_cldev_register_event_cb(phy->cldev, BIT(MEI_CL_EVENT_RX),
					nfc_mei_event_cb);
	r = mei_cldev_register_rx_cb(phy->cldev, nfc_mei_rx_cb);
	if (r) {
		pr_err("Event cb registration failed %d\n", r);
		goto err;
+11 −25
Original line number Diff line number Diff line
@@ -410,11 +410,11 @@ static void mei_wdt_unregister_work(struct work_struct *work)
}

/**
 * mei_wdt_event_rx - callback for data receive
 * mei_wdt_rx - callback for data receive
 *
 * @cldev: bus device
 */
static void mei_wdt_event_rx(struct mei_cl_device *cldev)
static void mei_wdt_rx(struct mei_cl_device *cldev)
{
	struct mei_wdt *wdt = mei_cldev_get_drvdata(cldev);
	struct mei_wdt_start_response res;
@@ -482,11 +482,11 @@ static void mei_wdt_event_rx(struct mei_cl_device *cldev)
}

/*
 * mei_wdt_notify_event - callback for event notification
 * mei_wdt_notif - callback for event notification
 *
 * @cldev: bus device
 */
static void mei_wdt_notify_event(struct mei_cl_device *cldev)
static void mei_wdt_notif(struct mei_cl_device *cldev)
{
	struct mei_wdt *wdt = mei_cldev_get_drvdata(cldev);

@@ -496,21 +496,6 @@ static void mei_wdt_notify_event(struct mei_cl_device *cldev)
	mei_wdt_register(wdt);
}

/**
 * mei_wdt_event - callback for event receive
 *
 * @cldev: bus device
 * @events: event mask
 */
static void mei_wdt_event(struct mei_cl_device *cldev, u32 events)
{
	if (events & BIT(MEI_CL_EVENT_RX))
		mei_wdt_event_rx(cldev);

	if (events & BIT(MEI_CL_EVENT_NOTIF))
		mei_wdt_notify_event(cldev);
}

#if IS_ENABLED(CONFIG_DEBUG_FS)

static ssize_t mei_dbgfs_read_activation(struct file *file, char __user *ubuf,
@@ -621,16 +606,17 @@ static int mei_wdt_probe(struct mei_cl_device *cldev,
		goto err_out;
	}

	ret = mei_cldev_register_event_cb(wdt->cldev,
					  BIT(MEI_CL_EVENT_RX) |
					  BIT(MEI_CL_EVENT_NOTIF),
					  mei_wdt_event);
	ret = mei_cldev_register_rx_cb(wdt->cldev, mei_wdt_rx);
	if (ret) {
		dev_err(&cldev->dev, "Could not reg rx event ret=%d\n", ret);
		goto err_disable;
	}

	ret = mei_cldev_register_notif_cb(wdt->cldev, mei_wdt_notif);
	/* on legacy devices notification is not supported
	 * this doesn't fail the registration for RX event
	 */
	if (ret && ret != -EOPNOTSUPP) {
		dev_err(&cldev->dev, "Could not register event ret=%d\n", ret);
		dev_err(&cldev->dev, "Could not reg notif event ret=%d\n", ret);
		goto err_disable;
	}

+14 −18
Original line number Diff line number Diff line
@@ -8,8 +8,7 @@
struct mei_cl_device;
struct mei_device;

typedef void (*mei_cldev_event_cb_t)(struct mei_cl_device *cldev,
				     u32 events);
typedef void (*mei_cldev_cb_t)(struct mei_cl_device *cldev);

/**
 * struct mei_cl_device - MEI device handle
@@ -24,11 +23,12 @@ typedef void (*mei_cldev_event_cb_t)(struct mei_cl_device *cldev,
 * @me_cl: me client
 * @cl: mei client
 * @name: device name
 * @event_work: async work to execute event callback
 * @event_cb: Drivers register this callback to get asynchronous ME
 *	events (e.g. Rx buffer pending) notifications.
 * @events_mask: Events bit mask requested by driver.
 * @events: Events bitmask sent to the driver.
 * @rx_work: async work to execute Rx event callback
 * @rx_cb: Drivers register this callback to get asynchronous ME
 *	Rx buffer pending notifications.
 * @notif_work: async work to execute FW notif event callback
 * @notif_cb: Drivers register this callback to get asynchronous ME
 *	FW notification pending notifications.
 *
 * @do_match: wheather device can be matched with a driver
 * @is_added: device is already scanned
@@ -43,10 +43,10 @@ struct mei_cl_device {
	struct mei_cl *cl;
	char name[MEI_CL_NAME_SIZE];

	struct work_struct event_work;
	mei_cldev_event_cb_t event_cb;
	unsigned long events_mask;
	unsigned long events;
	struct work_struct rx_work;
	mei_cldev_cb_t rx_cb;
	struct work_struct notif_work;
	mei_cldev_cb_t notif_cb;

	unsigned int do_match:1;
	unsigned int is_added:1;
@@ -88,13 +88,9 @@ void mei_cldev_driver_unregister(struct mei_cl_driver *cldrv);
ssize_t mei_cldev_send(struct mei_cl_device *cldev, u8 *buf, size_t length);
ssize_t  mei_cldev_recv(struct mei_cl_device *cldev, u8 *buf, size_t length);

int mei_cldev_register_event_cb(struct mei_cl_device *cldev,
				unsigned long event_mask,
				mei_cldev_event_cb_t read_cb);

#define MEI_CL_EVENT_RX 0
#define MEI_CL_EVENT_TX 1
#define MEI_CL_EVENT_NOTIF 2
int mei_cldev_register_rx_cb(struct mei_cl_device *cldev, mei_cldev_cb_t rx_cb);
int mei_cldev_register_notif_cb(struct mei_cl_device *cldev,
				mei_cldev_cb_t notif_cb);

const uuid_le *mei_cldev_uuid(const struct mei_cl_device *cldev);
u8 mei_cldev_ver(const struct mei_cl_device *cldev);