gadget: switch ep_io_operations to ->read_iter/->write_iter
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
This commit is contained in:
parent
f01d35a15f
commit
7fe3976e0f
@ -363,97 +363,6 @@ ep_io (struct ep_data *epdata, void *buf, unsigned len)
|
|||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* handle a synchronous OUT bulk/intr/iso transfer */
|
|
||||||
static ssize_t
|
|
||||||
ep_read (struct file *fd, char __user *buf, size_t len, loff_t *ptr)
|
|
||||||
{
|
|
||||||
struct ep_data *data = fd->private_data;
|
|
||||||
void *kbuf;
|
|
||||||
ssize_t value;
|
|
||||||
|
|
||||||
if ((value = get_ready_ep (fd->f_flags, data)) < 0)
|
|
||||||
return value;
|
|
||||||
|
|
||||||
/* halt any endpoint by doing a "wrong direction" i/o call */
|
|
||||||
if (usb_endpoint_dir_in(&data->desc)) {
|
|
||||||
if (usb_endpoint_xfer_isoc(&data->desc)) {
|
|
||||||
mutex_unlock(&data->lock);
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
DBG (data->dev, "%s halt\n", data->name);
|
|
||||||
spin_lock_irq (&data->dev->lock);
|
|
||||||
if (likely (data->ep != NULL))
|
|
||||||
usb_ep_set_halt (data->ep);
|
|
||||||
spin_unlock_irq (&data->dev->lock);
|
|
||||||
mutex_unlock(&data->lock);
|
|
||||||
return -EBADMSG;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* FIXME readahead for O_NONBLOCK and poll(); careful with ZLPs */
|
|
||||||
|
|
||||||
value = -ENOMEM;
|
|
||||||
kbuf = kmalloc (len, GFP_KERNEL);
|
|
||||||
if (unlikely (!kbuf))
|
|
||||||
goto free1;
|
|
||||||
|
|
||||||
value = ep_io (data, kbuf, len);
|
|
||||||
VDEBUG (data->dev, "%s read %zu OUT, status %d\n",
|
|
||||||
data->name, len, (int) value);
|
|
||||||
if (value >= 0 && copy_to_user (buf, kbuf, value))
|
|
||||||
value = -EFAULT;
|
|
||||||
|
|
||||||
free1:
|
|
||||||
mutex_unlock(&data->lock);
|
|
||||||
kfree (kbuf);
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* handle a synchronous IN bulk/intr/iso transfer */
|
|
||||||
static ssize_t
|
|
||||||
ep_write (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
|
|
||||||
{
|
|
||||||
struct ep_data *data = fd->private_data;
|
|
||||||
void *kbuf;
|
|
||||||
ssize_t value;
|
|
||||||
|
|
||||||
if ((value = get_ready_ep (fd->f_flags, data)) < 0)
|
|
||||||
return value;
|
|
||||||
|
|
||||||
/* halt any endpoint by doing a "wrong direction" i/o call */
|
|
||||||
if (!usb_endpoint_dir_in(&data->desc)) {
|
|
||||||
if (usb_endpoint_xfer_isoc(&data->desc)) {
|
|
||||||
mutex_unlock(&data->lock);
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
DBG (data->dev, "%s halt\n", data->name);
|
|
||||||
spin_lock_irq (&data->dev->lock);
|
|
||||||
if (likely (data->ep != NULL))
|
|
||||||
usb_ep_set_halt (data->ep);
|
|
||||||
spin_unlock_irq (&data->dev->lock);
|
|
||||||
mutex_unlock(&data->lock);
|
|
||||||
return -EBADMSG;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* FIXME writebehind for O_NONBLOCK and poll(), qlen = 1 */
|
|
||||||
|
|
||||||
value = -ENOMEM;
|
|
||||||
kbuf = memdup_user(buf, len);
|
|
||||||
if (IS_ERR(kbuf)) {
|
|
||||||
value = PTR_ERR(kbuf);
|
|
||||||
kbuf = NULL;
|
|
||||||
goto free1;
|
|
||||||
}
|
|
||||||
|
|
||||||
value = ep_io (data, kbuf, len);
|
|
||||||
VDEBUG (data->dev, "%s write %zu IN, status %d\n",
|
|
||||||
data->name, len, (int) value);
|
|
||||||
free1:
|
|
||||||
mutex_unlock(&data->lock);
|
|
||||||
kfree (kbuf);
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
ep_release (struct inode *inode, struct file *fd)
|
ep_release (struct inode *inode, struct file *fd)
|
||||||
{
|
{
|
||||||
@ -517,8 +426,8 @@ struct kiocb_priv {
|
|||||||
struct mm_struct *mm;
|
struct mm_struct *mm;
|
||||||
struct work_struct work;
|
struct work_struct work;
|
||||||
void *buf;
|
void *buf;
|
||||||
const struct iovec *iv;
|
struct iov_iter to;
|
||||||
unsigned long nr_segs;
|
const void *to_free;
|
||||||
unsigned actual;
|
unsigned actual;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -541,34 +450,6 @@ static int ep_aio_cancel(struct kiocb *iocb)
|
|||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
static ssize_t ep_copy_to_user(struct kiocb_priv *priv)
|
|
||||||
{
|
|
||||||
ssize_t len, total;
|
|
||||||
void *to_copy;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
/* copy stuff into user buffers */
|
|
||||||
total = priv->actual;
|
|
||||||
len = 0;
|
|
||||||
to_copy = priv->buf;
|
|
||||||
for (i=0; i < priv->nr_segs; i++) {
|
|
||||||
ssize_t this = min((ssize_t)(priv->iv[i].iov_len), total);
|
|
||||||
|
|
||||||
if (copy_to_user(priv->iv[i].iov_base, to_copy, this)) {
|
|
||||||
if (len == 0)
|
|
||||||
len = -EFAULT;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
total -= this;
|
|
||||||
len += this;
|
|
||||||
to_copy += this;
|
|
||||||
if (total == 0)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ep_user_copy_worker(struct work_struct *work)
|
static void ep_user_copy_worker(struct work_struct *work)
|
||||||
{
|
{
|
||||||
struct kiocb_priv *priv = container_of(work, struct kiocb_priv, work);
|
struct kiocb_priv *priv = container_of(work, struct kiocb_priv, work);
|
||||||
@ -577,14 +458,16 @@ static void ep_user_copy_worker(struct work_struct *work)
|
|||||||
size_t ret;
|
size_t ret;
|
||||||
|
|
||||||
use_mm(mm);
|
use_mm(mm);
|
||||||
ret = ep_copy_to_user(priv);
|
ret = copy_to_iter(priv->buf, priv->actual, &priv->to);
|
||||||
unuse_mm(mm);
|
unuse_mm(mm);
|
||||||
|
if (!ret)
|
||||||
|
ret = -EFAULT;
|
||||||
|
|
||||||
/* completing the iocb can drop the ctx and mm, don't touch mm after */
|
/* completing the iocb can drop the ctx and mm, don't touch mm after */
|
||||||
aio_complete(iocb, ret, ret);
|
aio_complete(iocb, ret, ret);
|
||||||
|
|
||||||
kfree(priv->buf);
|
kfree(priv->buf);
|
||||||
kfree(priv->iv);
|
kfree(priv->to_free);
|
||||||
kfree(priv);
|
kfree(priv);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -603,9 +486,9 @@ static void ep_aio_complete(struct usb_ep *ep, struct usb_request *req)
|
|||||||
* don't need to copy anything to userspace, so we can
|
* don't need to copy anything to userspace, so we can
|
||||||
* complete the aio request immediately.
|
* complete the aio request immediately.
|
||||||
*/
|
*/
|
||||||
if (priv->iv == NULL || unlikely(req->actual == 0)) {
|
if (priv->to_free == NULL || unlikely(req->actual == 0)) {
|
||||||
kfree(req->buf);
|
kfree(req->buf);
|
||||||
kfree(priv->iv);
|
kfree(priv->to_free);
|
||||||
kfree(priv);
|
kfree(priv);
|
||||||
iocb->private = NULL;
|
iocb->private = NULL;
|
||||||
/* aio_complete() reports bytes-transferred _and_ faults */
|
/* aio_complete() reports bytes-transferred _and_ faults */
|
||||||
@ -619,6 +502,7 @@ static void ep_aio_complete(struct usb_ep *ep, struct usb_request *req)
|
|||||||
|
|
||||||
priv->buf = req->buf;
|
priv->buf = req->buf;
|
||||||
priv->actual = req->actual;
|
priv->actual = req->actual;
|
||||||
|
INIT_WORK(&priv->work, ep_user_copy_worker);
|
||||||
schedule_work(&priv->work);
|
schedule_work(&priv->work);
|
||||||
}
|
}
|
||||||
spin_unlock(&epdata->dev->lock);
|
spin_unlock(&epdata->dev->lock);
|
||||||
@ -627,45 +511,17 @@ static void ep_aio_complete(struct usb_ep *ep, struct usb_request *req)
|
|||||||
put_ep(epdata);
|
put_ep(epdata);
|
||||||
}
|
}
|
||||||
|
|
||||||
static ssize_t
|
static ssize_t ep_aio(struct kiocb *iocb,
|
||||||
ep_aio_rwtail(
|
struct kiocb_priv *priv,
|
||||||
struct kiocb *iocb,
|
struct ep_data *epdata,
|
||||||
char *buf,
|
char *buf,
|
||||||
size_t len,
|
size_t len)
|
||||||
struct ep_data *epdata,
|
|
||||||
const struct iovec *iv,
|
|
||||||
unsigned long nr_segs
|
|
||||||
)
|
|
||||||
{
|
{
|
||||||
struct kiocb_priv *priv;
|
struct usb_request *req;
|
||||||
struct usb_request *req;
|
ssize_t value;
|
||||||
ssize_t value;
|
|
||||||
|
|
||||||
priv = kzalloc(sizeof *priv, GFP_KERNEL);
|
|
||||||
if (!priv) {
|
|
||||||
value = -ENOMEM;
|
|
||||||
fail:
|
|
||||||
kfree(buf);
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
iocb->private = priv;
|
iocb->private = priv;
|
||||||
priv->iocb = iocb;
|
priv->iocb = iocb;
|
||||||
if (iv) {
|
|
||||||
priv->iv = kmemdup(iv, nr_segs * sizeof(struct iovec),
|
|
||||||
GFP_KERNEL);
|
|
||||||
if (!priv->iv) {
|
|
||||||
kfree(priv);
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
priv->nr_segs = nr_segs;
|
|
||||||
INIT_WORK(&priv->work, ep_user_copy_worker);
|
|
||||||
|
|
||||||
value = get_ready_ep(iocb->ki_filp->f_flags, epdata);
|
|
||||||
if (unlikely(value < 0)) {
|
|
||||||
kfree(priv);
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
kiocb_set_cancel_fn(iocb, ep_aio_cancel);
|
kiocb_set_cancel_fn(iocb, ep_aio_cancel);
|
||||||
get_ep(epdata);
|
get_ep(epdata);
|
||||||
@ -677,76 +533,147 @@ fail:
|
|||||||
* allocate or submit those if the host disconnected.
|
* allocate or submit those if the host disconnected.
|
||||||
*/
|
*/
|
||||||
spin_lock_irq(&epdata->dev->lock);
|
spin_lock_irq(&epdata->dev->lock);
|
||||||
if (likely(epdata->ep)) {
|
value = -ENODEV;
|
||||||
req = usb_ep_alloc_request(epdata->ep, GFP_ATOMIC);
|
if (unlikely(epdata->ep))
|
||||||
if (likely(req)) {
|
goto fail;
|
||||||
priv->req = req;
|
|
||||||
req->buf = buf;
|
req = usb_ep_alloc_request(epdata->ep, GFP_ATOMIC);
|
||||||
req->length = len;
|
value = -ENOMEM;
|
||||||
req->complete = ep_aio_complete;
|
if (unlikely(!req))
|
||||||
req->context = iocb;
|
goto fail;
|
||||||
value = usb_ep_queue(epdata->ep, req, GFP_ATOMIC);
|
|
||||||
if (unlikely(0 != value))
|
priv->req = req;
|
||||||
usb_ep_free_request(epdata->ep, req);
|
req->buf = buf;
|
||||||
} else
|
req->length = len;
|
||||||
value = -EAGAIN;
|
req->complete = ep_aio_complete;
|
||||||
} else
|
req->context = iocb;
|
||||||
value = -ENODEV;
|
value = usb_ep_queue(epdata->ep, req, GFP_ATOMIC);
|
||||||
|
if (unlikely(0 != value)) {
|
||||||
|
usb_ep_free_request(epdata->ep, req);
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
spin_unlock_irq(&epdata->dev->lock);
|
spin_unlock_irq(&epdata->dev->lock);
|
||||||
|
return -EIOCBQUEUED;
|
||||||
|
|
||||||
mutex_unlock(&epdata->lock);
|
fail:
|
||||||
|
spin_unlock_irq(&epdata->dev->lock);
|
||||||
if (unlikely(value)) {
|
kfree(priv->to_free);
|
||||||
kfree(priv->iv);
|
kfree(priv);
|
||||||
kfree(priv);
|
put_ep(epdata);
|
||||||
put_ep(epdata);
|
|
||||||
} else
|
|
||||||
value = -EIOCBQUEUED;
|
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
static ssize_t
|
static ssize_t
|
||||||
ep_aio_read(struct kiocb *iocb, const struct iovec *iov,
|
ep_read_iter(struct kiocb *iocb, struct iov_iter *to)
|
||||||
unsigned long nr_segs, loff_t o)
|
|
||||||
{
|
{
|
||||||
struct ep_data *epdata = iocb->ki_filp->private_data;
|
struct file *file = iocb->ki_filp;
|
||||||
char *buf;
|
struct ep_data *epdata = file->private_data;
|
||||||
|
size_t len = iov_iter_count(to);
|
||||||
|
ssize_t value;
|
||||||
|
char *buf;
|
||||||
|
|
||||||
if (unlikely(usb_endpoint_dir_in(&epdata->desc)))
|
if ((value = get_ready_ep(file->f_flags, epdata)) < 0)
|
||||||
return -EINVAL;
|
return value;
|
||||||
|
|
||||||
buf = kmalloc(iocb->ki_nbytes, GFP_KERNEL);
|
/* halt any endpoint by doing a "wrong direction" i/o call */
|
||||||
if (unlikely(!buf))
|
if (usb_endpoint_dir_in(&epdata->desc)) {
|
||||||
|
if (usb_endpoint_xfer_isoc(&epdata->desc) ||
|
||||||
|
!is_sync_kiocb(iocb)) {
|
||||||
|
mutex_unlock(&epdata->lock);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
DBG (epdata->dev, "%s halt\n", epdata->name);
|
||||||
|
spin_lock_irq(&epdata->dev->lock);
|
||||||
|
if (likely(epdata->ep != NULL))
|
||||||
|
usb_ep_set_halt(epdata->ep);
|
||||||
|
spin_unlock_irq(&epdata->dev->lock);
|
||||||
|
mutex_unlock(&epdata->lock);
|
||||||
|
return -EBADMSG;
|
||||||
|
}
|
||||||
|
|
||||||
|
buf = kmalloc(len, GFP_KERNEL);
|
||||||
|
if (unlikely(!buf)) {
|
||||||
|
mutex_unlock(&epdata->lock);
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
}
|
||||||
return ep_aio_rwtail(iocb, buf, iocb->ki_nbytes, epdata, iov, nr_segs);
|
if (is_sync_kiocb(iocb)) {
|
||||||
|
value = ep_io(epdata, buf, len);
|
||||||
|
if (value >= 0 && copy_to_iter(buf, value, to))
|
||||||
|
value = -EFAULT;
|
||||||
|
} else {
|
||||||
|
struct kiocb_priv *priv = kzalloc(sizeof *priv, GFP_KERNEL);
|
||||||
|
value = -ENOMEM;
|
||||||
|
if (!priv)
|
||||||
|
goto fail;
|
||||||
|
priv->to_free = dup_iter(&priv->to, to, GFP_KERNEL);
|
||||||
|
if (!priv->to_free) {
|
||||||
|
kfree(priv);
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
value = ep_aio(iocb, priv, epdata, buf, len);
|
||||||
|
if (value == -EIOCBQUEUED)
|
||||||
|
buf = NULL;
|
||||||
|
}
|
||||||
|
fail:
|
||||||
|
kfree(buf);
|
||||||
|
mutex_unlock(&epdata->lock);
|
||||||
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
static ssize_t
|
static ssize_t
|
||||||
ep_aio_write(struct kiocb *iocb, const struct iovec *iov,
|
ep_write_iter(struct kiocb *iocb, struct iov_iter *from)
|
||||||
unsigned long nr_segs, loff_t o)
|
|
||||||
{
|
{
|
||||||
struct ep_data *epdata = iocb->ki_filp->private_data;
|
struct file *file = iocb->ki_filp;
|
||||||
char *buf;
|
struct ep_data *epdata = file->private_data;
|
||||||
size_t len = 0;
|
size_t len = iov_iter_count(from);
|
||||||
int i = 0;
|
ssize_t value;
|
||||||
|
char *buf;
|
||||||
|
|
||||||
if (unlikely(!usb_endpoint_dir_in(&epdata->desc)))
|
if ((value = get_ready_ep(file->f_flags, epdata)) < 0)
|
||||||
return -EINVAL;
|
return value;
|
||||||
|
|
||||||
buf = kmalloc(iocb->ki_nbytes, GFP_KERNEL);
|
/* halt any endpoint by doing a "wrong direction" i/o call */
|
||||||
if (unlikely(!buf))
|
if (!usb_endpoint_dir_in(&epdata->desc)) {
|
||||||
return -ENOMEM;
|
if (usb_endpoint_xfer_isoc(&epdata->desc) ||
|
||||||
|
!is_sync_kiocb(iocb)) {
|
||||||
for (i=0; i < nr_segs; i++) {
|
mutex_unlock(&epdata->lock);
|
||||||
if (unlikely(copy_from_user(&buf[len], iov[i].iov_base,
|
return -EINVAL;
|
||||||
iov[i].iov_len) != 0)) {
|
|
||||||
kfree(buf);
|
|
||||||
return -EFAULT;
|
|
||||||
}
|
}
|
||||||
len += iov[i].iov_len;
|
DBG (epdata->dev, "%s halt\n", epdata->name);
|
||||||
|
spin_lock_irq(&epdata->dev->lock);
|
||||||
|
if (likely(epdata->ep != NULL))
|
||||||
|
usb_ep_set_halt(epdata->ep);
|
||||||
|
spin_unlock_irq(&epdata->dev->lock);
|
||||||
|
mutex_unlock(&epdata->lock);
|
||||||
|
return -EBADMSG;
|
||||||
}
|
}
|
||||||
return ep_aio_rwtail(iocb, buf, len, epdata, NULL, 0);
|
|
||||||
|
buf = kmalloc(len, GFP_KERNEL);
|
||||||
|
if (unlikely(!buf)) {
|
||||||
|
mutex_unlock(&epdata->lock);
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (unlikely(copy_from_iter(buf, len, from) != len)) {
|
||||||
|
value = -EFAULT;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_sync_kiocb(iocb)) {
|
||||||
|
value = ep_io(epdata, buf, len);
|
||||||
|
} else {
|
||||||
|
struct kiocb_priv *priv = kzalloc(sizeof *priv, GFP_KERNEL);
|
||||||
|
value = -ENOMEM;
|
||||||
|
if (priv) {
|
||||||
|
value = ep_aio(iocb, priv, epdata, buf, len);
|
||||||
|
if (value == -EIOCBQUEUED)
|
||||||
|
buf = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out:
|
||||||
|
kfree(buf);
|
||||||
|
mutex_unlock(&epdata->lock);
|
||||||
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*----------------------------------------------------------------------*/
|
/*----------------------------------------------------------------------*/
|
||||||
@ -756,13 +683,13 @@ static const struct file_operations ep_io_operations = {
|
|||||||
.owner = THIS_MODULE,
|
.owner = THIS_MODULE,
|
||||||
.llseek = no_llseek,
|
.llseek = no_llseek,
|
||||||
|
|
||||||
.read = ep_read,
|
.read = new_sync_read,
|
||||||
.write = ep_write,
|
.write = new_sync_write,
|
||||||
.unlocked_ioctl = ep_ioctl,
|
.unlocked_ioctl = ep_ioctl,
|
||||||
.release = ep_release,
|
.release = ep_release,
|
||||||
|
|
||||||
.aio_read = ep_aio_read,
|
.read_iter = ep_read_iter,
|
||||||
.aio_write = ep_aio_write,
|
.write_iter = ep_write_iter,
|
||||||
};
|
};
|
||||||
|
|
||||||
/* ENDPOINT INITIALIZATION
|
/* ENDPOINT INITIALIZATION
|
||||||
|
Loading…
Reference in New Issue
Block a user