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

Commit 96103880 authored by qctecmdr Service's avatar qctecmdr Service Committed by Gerrit - the friendly Code Review server
Browse files

Merge "USB: gadget: Implement COMPAT_IOCTL for ioctls"

parents ceb432de 7d988501
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -780,6 +780,9 @@ static const struct file_operations acc_fops = {
	.read = acc_read,
	.write = acc_write,
	.unlocked_ioctl = acc_ioctl,
#ifdef CONFIG_COMPAT
	.compat_ioctl = acc_ioctl,
#endif
	.open = acc_open,
	.release = acc_release,
};
+242 −91
Original line number Diff line number Diff line
@@ -56,7 +56,7 @@
#define STATE_ERROR                 4   /* error from completion routine */

/* number of tx and rx requests to allocate */
#define TX_REQ_MAX 4
#define MTP_TX_REQ_MAX 8
#define RX_REQ_MAX 2
#define INTR_REQ_MAX 5

@@ -74,6 +74,15 @@
#define MTP_RESPONSE_DEVICE_BUSY    0x2019
#define DRIVER_NAME "mtp"

unsigned int mtp_rx_req_len = MTP_BULK_BUFFER_SIZE;
module_param(mtp_rx_req_len, uint, 0644);

unsigned int mtp_tx_req_len = MTP_BULK_BUFFER_SIZE;
module_param(mtp_tx_req_len, uint, 0644);

unsigned int mtp_tx_reqs = MTP_TX_REQ_MAX;
module_param(mtp_tx_reqs, uint, 0644);

static const char mtp_shortname[] = DRIVER_NAME "_usb";

struct mtp_dev {
@@ -504,18 +513,46 @@ static int mtp_create_bulk_endpoints(struct mtp_dev *dev,
	ep->driver_data = dev;		/* claim the endpoint */
	dev->ep_intr = ep;

retry_tx_alloc:
	if (mtp_tx_req_len > MTP_BULK_BUFFER_SIZE)
		mtp_tx_reqs = 4;

	/* now allocate requests for our endpoints */
	for (i = 0; i < TX_REQ_MAX; i++) {
		req = mtp_request_new(dev->ep_in, MTP_BULK_BUFFER_SIZE);
		if (!req)
	for (i = 0; i < mtp_tx_reqs; i++) {
		req = mtp_request_new(dev->ep_in, mtp_tx_req_len);
		if (!req) {
			if (mtp_tx_req_len <= MTP_BULK_BUFFER_SIZE)
				goto fail;
			while ((req = mtp_req_get(dev, &dev->tx_idle)))
				mtp_request_free(req, dev->ep_in);
			mtp_tx_req_len = MTP_BULK_BUFFER_SIZE;
			mtp_tx_reqs = MTP_TX_REQ_MAX;
			goto retry_tx_alloc;
		}
		req->complete = mtp_complete_in;
		mtp_req_put(dev, &dev->tx_idle, req);
	}

	/*
	 * The RX buffer should be aligned to EP max packet for
	 * some controllers.  At bind time, we don't know the
	 * operational speed.  Hence assuming super speed max
	 * packet size.
	 */
	if (mtp_rx_req_len % 1024)
		mtp_rx_req_len = MTP_BULK_BUFFER_SIZE;

retry_rx_alloc:
	for (i = 0; i < RX_REQ_MAX; i++) {
		req = mtp_request_new(dev->ep_out, MTP_BULK_BUFFER_SIZE);
		if (!req)
		req = mtp_request_new(dev->ep_out, mtp_rx_req_len);
		if (!req) {
			if (mtp_rx_req_len <= MTP_BULK_BUFFER_SIZE)
				goto fail;
			for (--i; i >= 0; i--)
				mtp_request_free(dev->rx_req[i], dev->ep_out);
			mtp_rx_req_len = MTP_BULK_BUFFER_SIZE;
			goto retry_rx_alloc;
		}
		req->complete = mtp_complete_out;
		dev->rx_req[i] = req;
	}
@@ -540,13 +577,15 @@ static ssize_t mtp_read(struct file *fp, char __user *buf,
	struct mtp_dev *dev = fp->private_data;
	struct usb_composite_dev *cdev = dev->cdev;
	struct usb_request *req;
	ssize_t r = count;
	unsigned xfer;
	ssize_t r = count, xfer, len;
	int ret = 0;
	size_t len = 0;

	DBG(cdev, "mtp_read(%zu)\n", count);

	len = ALIGN(count, dev->ep_out->maxpacket);
	if (len > mtp_rx_req_len)
		return -EINVAL;

	/* we will block until we're online */
	DBG(cdev, "mtp_read: waiting for online state\n");
	ret = wait_event_interruptible(dev->read_wq,
@@ -597,7 +636,17 @@ static ssize_t mtp_read(struct file *fp, char __user *buf,
	}

	/* wait for a request to complete */
	ret = wait_event_interruptible(dev->read_wq, dev->rx_done);
	ret = wait_event_interruptible(dev->read_wq,
				dev->rx_done || dev->state != STATE_BUSY);
	if (dev->state == STATE_CANCELED) {
		r = -ECANCELED;
		if (!dev->rx_done)
			usb_ep_dequeue(dev->ep_out, req);
		spin_lock_irq(&dev->lock);
		dev->state = STATE_CANCELED;
		spin_unlock_irq(&dev->lock);
		goto done;
	}
	if (ret < 0) {
		r = ret;
		usb_ep_dequeue(dev->ep_out, req);
@@ -808,6 +857,7 @@ static void send_file_work(struct work_struct *data)
		ret = usb_ep_queue(dev->ep_in, req, GFP_KERNEL);
		if (ret < 0) {
			DBG(cdev, "send_file_work: xfer error %d\n", ret);
			if (dev->state != STATE_OFFLINE)
				dev->state = STATE_ERROR;
			r = -EIO;
			break;
@@ -848,6 +898,9 @@ static void receive_file_work(struct work_struct *data)
	count = dev->xfer_file_length;

	DBG(cdev, "receive_file_work(%lld)\n", count);
	if (!IS_ALIGNED(count, dev->ep_out->maxpacket))
		DBG(cdev, "%s- count(%lld) not multiple of mtu(%d)\n", __func__,
						count, dev->ep_out->maxpacket);

	while (count > 0 || write_req) {
		if (count > 0) {
@@ -855,12 +908,14 @@ static void receive_file_work(struct work_struct *data)
			read_req = dev->rx_req[cur_buf];
			cur_buf = (cur_buf + 1) % RX_REQ_MAX;

			read_req->length = (count > MTP_BULK_BUFFER_SIZE
					? MTP_BULK_BUFFER_SIZE : count);
			/* some h/w expects size to be aligned to ep's MTU */
			read_req->length = mtp_rx_req_len;

			dev->rx_done = 0;
			ret = usb_ep_queue(dev->ep_out, read_req, GFP_KERNEL);
			if (ret < 0) {
				r = -EIO;
				if (dev->state != STATE_OFFLINE)
					dev->state = STATE_ERROR;
				break;
			}
@@ -873,6 +928,7 @@ static void receive_file_work(struct work_struct *data)
			DBG(cdev, "vfs_write %d\n", ret);
			if (ret != write_req->actual) {
				r = -EIO;
				if (dev->state != STATE_OFFLINE)
					dev->state = STATE_ERROR;
				break;
			}
@@ -883,7 +939,11 @@ static void receive_file_work(struct work_struct *data)
			/* wait for our last read to complete */
			ret = wait_event_interruptible(dev->read_wq,
				dev->rx_done || dev->state != STATE_BUSY);
			if (dev->state == STATE_CANCELED) {
			if (dev->state == STATE_CANCELED
					|| dev->state == STATE_OFFLINE) {
				if (dev->state == STATE_OFFLINE)
					r = -EIO;
				else
					r = -ECANCELED;
				if (!dev->rx_done)
					usb_ep_dequeue(dev->ep_out, read_req);
@@ -893,6 +953,11 @@ static void receive_file_work(struct work_struct *data)
				r = read_req->status;
				break;
			}

			/* Check if we aligned the size due to MTU constraint */
			if (count < read_req->length)
				read_req->actual = (read_req->actual > count ?
						count : read_req->actual);
			/* if xfer_file_length is 0xFFFFFFFF, then we read until
			 * we get a zero length packet
			 */
@@ -949,26 +1014,20 @@ static int mtp_send_event(struct mtp_dev *dev, struct mtp_event *event)
	return ret;
}

static long mtp_ioctl(struct file *fp, unsigned code, unsigned long value)
static long mtp_send_receive_ioctl(struct file *fp, unsigned int code,
	struct mtp_file_range *mfr)
{
	struct mtp_dev *dev = fp->private_data;
	struct file *filp = NULL;
	struct work_struct *work;
	int ret = -EINVAL;

	if (mtp_lock(&dev->ioctl_excl))
		return -EBUSY;

	switch (code) {
	case MTP_SEND_FILE:
	case MTP_RECEIVE_FILE:
	case MTP_SEND_FILE_WITH_HEADER:
	{
		struct mtp_file_range	mfr;
		struct work_struct *work;

	spin_lock_irq(&dev->lock);
	if (dev->state == STATE_CANCELED) {
			/* report cancelation to userspace */
		/* report cancellation to userspace */
		dev->state = STATE_READY;
		spin_unlock_irq(&dev->lock);
		ret = -ECANCELED;
@@ -982,12 +1041,8 @@ static long mtp_ioctl(struct file *fp, unsigned code, unsigned long value)
	dev->state = STATE_BUSY;
	spin_unlock_irq(&dev->lock);

		if (copy_from_user(&mfr, (void __user *)value, sizeof(mfr))) {
			ret = -EFAULT;
			goto fail;
		}
	/* hold a reference to the file while we are working with it */
		filp = fget(mfr.fd);
	filp = fget(mfr->fd);
	if (!filp) {
		ret = -EBADF;
		goto fail;
@@ -995,15 +1050,16 @@ static long mtp_ioctl(struct file *fp, unsigned code, unsigned long value)

	/* write the parameters */
	dev->xfer_file = filp;
		dev->xfer_file_offset = mfr.offset;
		dev->xfer_file_length = mfr.length;
	dev->xfer_file_offset = mfr->offset;
	dev->xfer_file_length = mfr->length;
	/* make sure write is done before parameters are read */
	smp_wmb();

	if (code == MTP_SEND_FILE_WITH_HEADER) {
		work = &dev->send_file_work;
		dev->xfer_send_header = 1;
			dev->xfer_command = mfr.command;
			dev->xfer_transaction_id = mfr.transaction_id;
		dev->xfer_command = mfr->command;
		dev->xfer_transaction_id = mfr->transaction_id;
	} else if (code == MTP_SEND_FILE) {
		work = &dev->send_file_work;
		dev->xfer_send_header = 0;
@@ -1023,11 +1079,40 @@ static long mtp_ioctl(struct file *fp, unsigned code, unsigned long value)
	/* read the result */
	smp_rmb();
	ret = dev->xfer_result;
		break;

fail:
	spin_lock_irq(&dev->lock);
	if (dev->state == STATE_CANCELED)
		ret = -ECANCELED;
	else if (dev->state != STATE_OFFLINE)
		dev->state = STATE_READY;
	spin_unlock_irq(&dev->lock);
out:
	mtp_unlock(&dev->ioctl_excl);
	DBG(dev->cdev, "ioctl returning %d\n", ret);
	return ret;
}
	case MTP_SEND_EVENT:

static long mtp_ioctl(struct file *fp, unsigned int code, unsigned long value)
{
	struct mtp_dev *dev = fp->private_data;
	struct mtp_file_range	mfr;
	struct mtp_event	event;
	int ret = -EINVAL;

	switch (code) {
	case MTP_SEND_FILE:
	case MTP_RECEIVE_FILE:
	case MTP_SEND_FILE_WITH_HEADER:
		if (copy_from_user(&mfr, (void __user *)value, sizeof(mfr))) {
			ret = -EFAULT;
			goto fail;
		}
		ret = mtp_send_receive_ioctl(fp, code, &mfr);
	break;
	case MTP_SEND_EVENT:
		if (mtp_lock(&dev->ioctl_excl))
			return -EBUSY;
		/* return here so we don't change dev->state below,
		 * which would interfere with bulk transfer state.
		 */
@@ -1035,22 +1120,85 @@ static long mtp_ioctl(struct file *fp, unsigned code, unsigned long value)
			ret = -EFAULT;
		else
			ret = mtp_send_event(dev, &event);
		goto out;
		mtp_unlock(&dev->ioctl_excl);
	break;
	default:
		DBG(dev->cdev, "unknown ioctl code: %d\n", code);
	}
fail:
	return ret;
}

fail:
	spin_lock_irq(&dev->lock);
	if (dev->state == STATE_CANCELED)
		ret = -ECANCELED;
	else if (dev->state != STATE_OFFLINE)
		dev->state = STATE_READY;
	spin_unlock_irq(&dev->lock);
out:
/*
 * 32 bit userspace calling into 64 bit kernel. handle ioctl code
 * and userspace pointer
 */
#ifdef CONFIG_COMPAT
static long compat_mtp_ioctl(struct file *fp, unsigned int code,
	unsigned long value)
{
	struct mtp_dev *dev = fp->private_data;
	struct mtp_file_range	mfr;
	struct __compat_mtp_file_range	cmfr;
	struct mtp_event	event;
	struct __compat_mtp_event cevent;
	unsigned int cmd;
	bool send_file = false;
	int ret = -EINVAL;

	switch (code) {
	case COMPAT_MTP_SEND_FILE:
		cmd = MTP_SEND_FILE;
		send_file = true;
		break;
	case COMPAT_MTP_RECEIVE_FILE:
		cmd = MTP_RECEIVE_FILE;
		send_file = true;
		break;
	case COMPAT_MTP_SEND_FILE_WITH_HEADER:
		cmd = MTP_SEND_FILE_WITH_HEADER;
		send_file = true;
		break;
	case COMPAT_MTP_SEND_EVENT:
		cmd = MTP_SEND_EVENT;
		break;
	default:
		DBG(dev->cdev, "unknown compat_ioctl code: %d\n", code);
		ret = -ENOIOCTLCMD;
		goto fail;
	}

	if (send_file) {
		if (copy_from_user(&cmfr, (void __user *)value, sizeof(cmfr))) {
			ret = -EFAULT;
			goto fail;
		}
		mfr.fd = cmfr.fd;
		mfr.offset = cmfr.offset;
		mfr.length = cmfr.length;
		mfr.command = cmfr.command;
		mfr.transaction_id = cmfr.transaction_id;
		ret = mtp_send_receive_ioctl(fp, cmd, &mfr);
	} else {
		if (mtp_lock(&dev->ioctl_excl))
			return -EBUSY;
		/* return here so we don't change dev->state below,
		 * which would interfere with bulk transfer state.
		 */
		if (copy_from_user(&cevent, (void __user *)value,
			sizeof(cevent))) {
			ret = -EFAULT;
			goto fail;
		}
		event.length = cevent.length;
		event.data = compat_ptr(cevent.data);
		ret = mtp_send_event(dev, &event);
		mtp_unlock(&dev->ioctl_excl);
	DBG(dev->cdev, "ioctl returning %d\n", ret);
	}
fail:
	return ret;
}
#endif

static int mtp_open(struct inode *ip, struct file *fp)
{
@@ -1080,6 +1228,9 @@ static const struct file_operations mtp_fops = {
	.read = mtp_read,
	.write = mtp_write,
	.unlocked_ioctl = mtp_ioctl,
#ifdef CONFIG_COMPAT
	.compat_ioctl = compat_mtp_ioctl,
#endif
	.open = mtp_open,
	.release = mtp_release,
};
+30 −0
Original line number Diff line number Diff line
@@ -19,5 +19,35 @@
#define __LINUX_USB_F_MTP_H

#include <uapi/linux/usb/f_mtp.h>
#include <linux/ioctl.h>
#ifdef CONFIG_COMPAT
#include <linux/compat.h>
#endif

#ifdef __KERNEL__

#ifdef CONFIG_COMPAT
struct __compat_mtp_file_range {
	compat_int_t	fd;
	compat_loff_t	offset;
	int64_t		length;
	uint16_t	command;
	uint32_t	transaction_id;
};

struct __compat_mtp_event {
	compat_size_t	length;
	compat_caddr_t	data;
};

#define COMPAT_MTP_SEND_FILE              _IOW('M', 0, \
						struct __compat_mtp_file_range)
#define COMPAT_MTP_RECEIVE_FILE           _IOW('M', 1, \
						struct __compat_mtp_file_range)
#define COMPAT_MTP_SEND_EVENT             _IOW('M', 3, \
						struct __compat_mtp_event)
#define COMPAT_MTP_SEND_FILE_WITH_HEADER  _IOW('M', 4, \
						struct __compat_mtp_file_range)
#endif
#endif
#endif /* __LINUX_USB_F_MTP_H */