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

Commit 1e208d25 authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "usb: misc: diag_ipc_bridge: Add support for QMI messages over BULK"

parents 99dd03c2 a9052dc1
Loading
Loading
Loading
Loading
+192 −43
Original line number Diff line number Diff line
@@ -26,13 +26,22 @@
#include <linux/usb.h>
#include <linux/debugfs.h>
#include <linux/usb/diag_bridge.h>
#include <linux/usb/ipc_bridge.h>

#define DRIVER_DESC	"USB host diag bridge driver"
#define DRIVER_VERSION	"1.0"

#define MAX_DIAG_BRIDGE_DEVS	2
enum {
	DIAG_BRIDGE,
	IPC_BRIDGE,
	MAX_BRIDGE_DEVS,
};

#define AUTOSUSP_DELAY_WITH_USB 1000

#define IPC_BRIDGE_MAX_READ_SZ	(8 * 1024)
#define IPC_BRIDGE_MAX_WRITE_SZ	(8 * 1024)

struct diag_bridge {
	struct usb_device	*udev;
	struct usb_interface	*ifc;
@@ -42,6 +51,13 @@ struct diag_bridge {
	int			err;
	struct kref		kref;
	struct mutex		ifc_mutex;
	struct mutex		read_mutex;
	struct mutex		write_mutex;
	bool			opened;
	struct completion	read_done;
	struct completion	write_done;
	int			read_result;
	int			write_result;
	struct diag_bridge_ops	*ops;
	struct platform_device	*pdev;
	unsigned		default_autosusp_delay;
@@ -58,13 +74,13 @@ struct diag_bridge {
	unsigned		pending_writes;
	unsigned		drop_count;
};
struct diag_bridge *__dev[MAX_DIAG_BRIDGE_DEVS];
struct diag_bridge *__dev[MAX_BRIDGE_DEVS];

int diag_bridge_open(int id, struct diag_bridge_ops *ops)
{
	struct diag_bridge	*dev;

	if (id < 0 || id >= MAX_DIAG_BRIDGE_DEVS) {
	if (id < 0 || id >= MAX_BRIDGE_DEVS) {
		pr_err("Invalid device ID");
		return -ENODEV;
	}
@@ -80,17 +96,25 @@ int diag_bridge_open(int id, struct diag_bridge_ops *ops)
		return -EALREADY;
	}

	mutex_lock(&dev->ifc_mutex);
	if (dev->opened) {
		mutex_unlock(&dev->ifc_mutex);
		pr_err("Bridge already opened");
		return -EBUSY;
	}

	dev->opened = true;
	mutex_unlock(&dev->ifc_mutex);

	dev->ops = ops;
	dev->err = 0;

	if (!id) {
#ifdef CONFIG_PM_RUNTIME
	dev->default_autosusp_delay =
		dev->udev->dev.power.autosuspend_delay;
#endif
	pm_runtime_set_autosuspend_delay(&dev->udev->dev,
			AUTOSUSP_DELAY_WITH_USB);
	}

	kref_get(&dev->kref);

@@ -98,6 +122,14 @@ int diag_bridge_open(int id, struct diag_bridge_ops *ops)
}
EXPORT_SYMBOL(diag_bridge_open);

static int ipc_bridge_open(struct platform_device *pdev)
{
	if (__dev[IPC_BRIDGE]->pdev != pdev)
		return -EINVAL;

	return diag_bridge_open(IPC_BRIDGE, NULL);
}

static void diag_bridge_delete(struct kref *kref)
{
	struct diag_bridge *dev = container_of(kref, struct diag_bridge, kref);
@@ -112,7 +144,7 @@ void diag_bridge_close(int id)
{
	struct diag_bridge	*dev;

	if (id < 0 || id >= MAX_DIAG_BRIDGE_DEVS) {
	if (id < 0 || id >= MAX_BRIDGE_DEVS) {
		pr_err("Invalid device ID");
		return;
	}
@@ -123,26 +155,40 @@ void diag_bridge_close(int id)
		return;
	}

	if (!dev->ops) {
	if (id == DIAG_BRIDGE && !dev->ops) {
		pr_err("can't close bridge that was not open");
		return;
	}

	mutex_lock(&dev->ifc_mutex);
	if (!dev->opened) {
		mutex_unlock(&dev->ifc_mutex);
		pr_err("Bridge not opened");
		return;
	}

	dev->opened = false;
	mutex_unlock(&dev->ifc_mutex);

	dev_dbg(&dev->ifc->dev, "%s:\n", __func__);

	usb_kill_anchored_urbs(&dev->submitted);
	dev->ops = 0;


	if (!id) {
	pm_runtime_set_autosuspend_delay(&dev->udev->dev,
		dev->default_autosusp_delay);
	}

	kref_put(&dev->kref, diag_bridge_delete);
}
EXPORT_SYMBOL(diag_bridge_close);

static void ipc_bridge_close(struct platform_device *pdev)
{
	WARN_ON(__dev[IPC_BRIDGE]->pdev != pdev);
	WARN_ON(__dev[IPC_BRIDGE]->udev->state != USB_STATE_NOTATTACHED);
	diag_bridge_close(IPC_BRIDGE);
}

static void diag_bridge_read_cb(struct urb *urb)
{
	struct diag_bridge	*dev = urb->context;
@@ -155,11 +201,21 @@ static void diag_bridge_read_cb(struct urb *urb)
	if (urb->status == -EPROTO)
		dev->err = urb->status;

	if (cbs && cbs->read_complete_cb)
	if (cbs && cbs->read_complete_cb) {
		cbs->read_complete_cb(cbs->ctxt,
			urb->transfer_buffer,
			urb->transfer_buffer_length,
			urb->status < 0 ? urb->status : urb->actual_length);
	} else {
		if (urb->dev->state == USB_STATE_NOTATTACHED)
			dev->read_result = -ENODEV;
		else if (urb->status < 0)
			dev->read_result = urb->status;
		else
			dev->read_result = urb->actual_length;

		complete(&dev->read_done);
	}

	dev->bytes_to_host += urb->actual_length;
	dev->pending_reads--;
@@ -173,7 +229,7 @@ int diag_bridge_read(int id, char *data, int size)
	struct diag_bridge	*dev;
	int			ret;

	if (id < 0 || id >= MAX_DIAG_BRIDGE_DEVS) {
	if (id < 0 || id >= MAX_BRIDGE_DEVS) {
		pr_err("Invalid device ID");
		return -ENODEV;
	}
@@ -186,13 +242,13 @@ int diag_bridge_read(int id, char *data, int size)
		return -ENODEV;
	}

	mutex_lock(&dev->ifc_mutex);
	mutex_lock(&dev->read_mutex);
	if (!dev->ifc) {
		ret = -ENODEV;
		goto error;
	}

	if (!dev->ops) {
	if (id == DIAG_BRIDGE && !dev->ops) {
		pr_err("bridge is not open");
		ret = -ENODEV;
		goto error;
@@ -206,7 +262,7 @@ int diag_bridge_read(int id, char *data, int size)
	}

	/* if there was a previous unrecoverable error, just quit */
	if (dev->err) {
	if (id == DIAG_BRIDGE && dev->err) {
		ret = -ENODEV;
		goto error;
	}
@@ -245,19 +301,40 @@ int diag_bridge_read(int id, char *data, int size)
		dev->pending_reads--;
		usb_unanchor_urb(urb);
	}

	if (id == IPC_BRIDGE) {
		wait_for_completion(&dev->read_done);
		ret = dev->read_result;
	}

	usb_autopm_put_interface(dev->ifc);

free_error:
	usb_free_urb(urb);
put_error:
	if (ret) /* otherwise this is done in the completion handler */
	if (ret < 0) /* otherwise this is done in the completion handler */
		kref_put(&dev->kref, diag_bridge_delete);
error:
	mutex_unlock(&dev->ifc_mutex);
	mutex_unlock(&dev->read_mutex);
	return ret;
}
EXPORT_SYMBOL(diag_bridge_read);

static int
ipc_bridge_read(struct platform_device *pdev, char *buf, unsigned int count)
{
	if (__dev[IPC_BRIDGE]->pdev != pdev)
		return -EINVAL;
	if (!__dev[IPC_BRIDGE]->opened)
		return -EPERM;
	if (count > IPC_BRIDGE_MAX_READ_SZ)
		return -ENOSPC;
	if (__dev[IPC_BRIDGE]->udev->state == USB_STATE_NOTATTACHED)
		return -ENODEV;

	return diag_bridge_read(IPC_BRIDGE, buf, count);
}

static void diag_bridge_write_cb(struct urb *urb)
{
	struct diag_bridge	*dev = urb->context;
@@ -271,11 +348,21 @@ static void diag_bridge_write_cb(struct urb *urb)
	if (urb->status == -EPROTO)
		dev->err = urb->status;

	if (cbs && cbs->write_complete_cb)
	if (cbs && cbs->write_complete_cb) {
		cbs->write_complete_cb(cbs->ctxt,
			urb->transfer_buffer,
			urb->transfer_buffer_length,
			urb->status < 0 ? urb->status : urb->actual_length);
	} else {
		if (urb->dev->state == USB_STATE_NOTATTACHED)
			dev->write_result = -ENODEV;
		else if (urb->status < 0)
			dev->write_result = urb->status;
		else
			dev->write_result = urb->actual_length;

		complete(&dev->write_done);
	}

	dev->bytes_to_mdm += urb->actual_length;
	dev->pending_writes--;
@@ -289,7 +376,7 @@ int diag_bridge_write(int id, char *data, int size)
	struct diag_bridge	*dev;
	int			ret;

	if (id < 0 || id >= MAX_DIAG_BRIDGE_DEVS) {
	if (id < 0 || id >= MAX_BRIDGE_DEVS) {
		pr_err("Invalid device ID");
		return -ENODEV;
	}
@@ -302,13 +389,13 @@ int diag_bridge_write(int id, char *data, int size)
		return -ENODEV;
	}

	mutex_lock(&dev->ifc_mutex);
	mutex_lock(&dev->write_mutex);
	if (!dev->ifc) {
		ret = -ENODEV;
		goto error;
	}

	if (!dev->ops) {
	if (id == DIAG_BRIDGE && !dev->ops) {
		pr_err("bridge is not open");
		ret = -ENODEV;
		goto error;
@@ -321,7 +408,7 @@ int diag_bridge_write(int id, char *data, int size)
	}

	/* if there was a previous unrecoverable error, just quit */
	if (dev->err) {
	if (id == DIAG_BRIDGE && dev->err) {
		ret = -ENODEV;
		goto error;
	}
@@ -357,17 +444,35 @@ int diag_bridge_write(int id, char *data, int size)
		goto free_error;
	}

	if (id == IPC_BRIDGE) {
		wait_for_completion(&dev->write_done);
		ret = dev->write_result;
	}

free_error:
	usb_free_urb(urb);
put_error:
	if (ret) /* otherwise this is done in the completion handler */
	if (ret < 0) /* otherwise this is done in the completion handler */
		kref_put(&dev->kref, diag_bridge_delete);
error:
	mutex_unlock(&dev->ifc_mutex);
	mutex_unlock(&dev->write_mutex);
	return ret;
}
EXPORT_SYMBOL(diag_bridge_write);

static int
ipc_bridge_write(struct platform_device *pdev, char *buf, unsigned int count)
{
	if (__dev[IPC_BRIDGE]->pdev != pdev)
		return -EINVAL;
	if (!__dev[IPC_BRIDGE]->opened)
		return -EPERM;
	if (count > IPC_BRIDGE_MAX_WRITE_SZ)
		return -EINVAL;

	return diag_bridge_write(IPC_BRIDGE, buf, count);
}

#if defined(CONFIG_DEBUG_FS)
#define DEBUG_BUF_SIZE	512
static ssize_t diag_read_stats(struct file *file, char __user *ubuf,
@@ -380,7 +485,7 @@ static ssize_t diag_read_stats(struct file *file, char __user *ubuf,
	if (!buf)
		return -ENOMEM;

	for (i = 0; i < MAX_DIAG_BRIDGE_DEVS; i++) {
	for (i = 0; i < MAX_BRIDGE_DEVS; i++) {
		struct diag_bridge *dev = __dev[i];

		if (!dev)
@@ -411,7 +516,7 @@ static ssize_t diag_reset_stats(struct file *file, const char __user *buf,
{
	int i;

	for (i = 0; i < MAX_DIAG_BRIDGE_DEVS; i++) {
	for (i = 0; i < MAX_BRIDGE_DEVS; i++) {
		struct diag_bridge *dev = __dev[i];

		if (dev) {
@@ -454,6 +559,15 @@ static inline void diag_bridge_debugfs_init(void) { }
static inline void diag_bridge_debugfs_cleanup(void) { }
#endif

static const struct ipc_bridge_platform_data ipc_bridge_pdata = {
	.max_read_size = IPC_BRIDGE_MAX_READ_SZ,
	.max_write_size = IPC_BRIDGE_MAX_WRITE_SZ,
	.open = ipc_bridge_open,
	.read = ipc_bridge_read,
	.write = ipc_bridge_write,
	.close = ipc_bridge_close,
};

static int
diag_bridge_probe(struct usb_interface *ifc, const struct usb_device_id *id)
{
@@ -465,7 +579,7 @@ diag_bridge_probe(struct usb_interface *ifc, const struct usb_device_id *id)
	pr_debug("id:%lu", id->driver_info);

	devid = id->driver_info & 0xFF;
	if (devid < 0 || devid >= MAX_DIAG_BRIDGE_DEVS)
	if (devid < 0 || devid >= MAX_BRIDGE_DEVS)
		return -ENODEV;

	/* already probed? */
@@ -485,6 +599,10 @@ diag_bridge_probe(struct usb_interface *ifc, const struct usb_device_id *id)
	dev->ifc = ifc;
	kref_init(&dev->kref);
	mutex_init(&dev->ifc_mutex);
	mutex_init(&dev->read_mutex);
	mutex_init(&dev->write_mutex);
	init_completion(&dev->read_done);
	init_completion(&dev->write_done);
	init_usb_anchor(&dev->submitted);

	ifc_desc = ifc->cur_altsetting;
@@ -510,19 +628,47 @@ diag_bridge_probe(struct usb_interface *ifc, const struct usb_device_id *id)

	usb_set_intfdata(ifc, dev);
	diag_bridge_debugfs_init();
	dev->pdev = platform_device_register_simple("diag_bridge", devid,
						    NULL, 0);
	if (devid == DIAG_BRIDGE) {
		dev->pdev = platform_device_register_simple("diag_bridge",
								devid, NULL, 0);
		if (IS_ERR(dev->pdev)) {
			pr_err("unable to allocate platform device");
			ret = PTR_ERR(dev->pdev);
			goto error;
		}
	} else {
		dev->pdev = platform_device_alloc("ipc_bridge", -1);
		if (!dev->pdev) {
			pr_err("unable to allocate platform device");
			ret = -ENOMEM;
			goto error;
		}

		ret = platform_device_add_data(dev->pdev, &ipc_bridge_pdata,
				sizeof(struct ipc_bridge_platform_data));
		if (ret) {
			pr_err("fail to add pdata");
			goto put_pdev;
		}

		ret = platform_device_add(dev->pdev);
		if (ret) {
			pr_err("fail to add pdev");
			goto put_pdev;
		}
	}

	dev_dbg(&dev->ifc->dev, "%s: complete\n", __func__);

	return 0;

put_pdev:
	platform_device_put(dev->pdev);
error:
	diag_bridge_debugfs_cleanup();
	mutex_destroy(&dev->write_mutex);
	mutex_destroy(&dev->read_mutex);
	mutex_destroy(&dev->ifc_mutex);
	if (dev)
		kref_put(&dev->kref, diag_bridge_delete);

@@ -536,12 +682,15 @@ static void diag_bridge_disconnect(struct usb_interface *ifc)
	dev_dbg(&dev->ifc->dev, "%s:\n", __func__);

	platform_device_unregister(dev->pdev);
	diag_bridge_debugfs_cleanup();
	mutex_lock(&dev->ifc_mutex);
	dev->ifc = NULL;
	mutex_unlock(&dev->ifc_mutex);
	diag_bridge_debugfs_cleanup();
	kref_put(&dev->kref, diag_bridge_delete);
	usb_set_intfdata(ifc, NULL);
	mutex_destroy(&dev->write_mutex);
	mutex_destroy(&dev->read_mutex);
	mutex_destroy(&dev->ifc_mutex);
	kref_put(&dev->kref, diag_bridge_delete);
}

static int diag_bridge_suspend(struct usb_interface *ifc, pm_message_t message)
@@ -557,9 +706,9 @@ static int diag_bridge_suspend(struct usb_interface *ifc, pm_message_t message)
				"%s: diag veto'd suspend\n", __func__);
			return ret;
		}
	}

	usb_kill_anchored_urbs(&dev->submitted);
	}

	return ret;
}
+65 −0
Original line number Diff line number Diff line
/* Copyright (c) 2013 The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
 * only version 2 as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */

#ifndef __MSM_IPC_BRIDGE_H__
#define __MSM_IPC_BRIDGE_H__

#include <linux/platform_device.h>

/*
 * The IPC bridge driver adds a IPC bridge platform device when the
 * underlying transport is ready. The IPC transport driver acts as a
 * platform driver for this device. The platform data is populated by
 * IPC bridge driver to facilitate I/O. The callback functions are
 * passed in platform data to avoid export functions. This would allow
 * different bridge drivers to exist in the kernel. The IPC bridge driver
 * removes the platform device when the underly transport is no longer
 * available. It typically happens during shutdown and remote processor's
 * subsystem restart.
 */

/**
 * struct ipc_bridge_platform_data - platform device data for IPC
 *              transport driver.
 * @max_read_size: The maximum possible read size.
 * @max_write_size: The maximum possible write size.
 * @open: The open must be called before starting I/O.  The IPC bridge
 *              driver use the platform device pointer to identify the
 *              underlying transport channel. The IPC bridge driver may
 *              notify that remote processor that it is ready to receive
 *              data. Returns 0 upon success and appropriate error code
 *              upon failure.
 * @read: The read is done synchronously and should be called from process
 *              context. Returns the number of bytes read from remote
 *              processor or error code upon failure. The IPC transport
 *              driver may pass the buffer of max_read_size length if the
 *              available data size is not known in advance.
 * @write: The write is done synchronously and should be called from process
 *              context. The IPC bridge driver uses the same buffer for DMA
 *              to avoid additional memcpy. So it must be physically contiguous.
 *              Returns the number of bytes written or error code upon failure.
 * @close: The close must be called when the IPC bridge platform device
 *              is removed. The IPC transport driver may call close when
 *              it is no longer required to communicate with remote processor.
 */
struct ipc_bridge_platform_data {
	unsigned int max_read_size;
	unsigned int max_write_size;
	int (*open)(struct platform_device *pdev);
	int (*read)(struct platform_device *pdev, char *buf,
			unsigned int count);
	int (*write)(struct platform_device *pdev, char *buf,
			unsigned int count);
	void (*close)(struct platform_device *pdev);
};

#endif