USB: usb-skeleton: usb anchor to implement flush
This patch set introduces usb_anchor and uses it to implement all modern APIs in the skeleton driver. - proper error reporting in the skeleton driver - implementation of flush() Signed-off-by: Oliver Neukum <oneukum@suse.de> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
This commit is contained in:
parent
51a2f077c4
commit
403dfb58c3
|
@ -51,16 +51,20 @@ struct usb_skel {
|
||||||
struct usb_device *udev; /* the usb device for this device */
|
struct usb_device *udev; /* the usb device for this device */
|
||||||
struct usb_interface *interface; /* the interface for this device */
|
struct usb_interface *interface; /* the interface for this device */
|
||||||
struct semaphore limit_sem; /* limiting the number of writes in progress */
|
struct semaphore limit_sem; /* limiting the number of writes in progress */
|
||||||
|
struct usb_anchor submitted; /* in case we need to retract our submissions */
|
||||||
unsigned char *bulk_in_buffer; /* the buffer to receive data */
|
unsigned char *bulk_in_buffer; /* the buffer to receive data */
|
||||||
size_t bulk_in_size; /* the size of the receive buffer */
|
size_t bulk_in_size; /* the size of the receive buffer */
|
||||||
__u8 bulk_in_endpointAddr; /* the address of the bulk in endpoint */
|
__u8 bulk_in_endpointAddr; /* the address of the bulk in endpoint */
|
||||||
__u8 bulk_out_endpointAddr; /* the address of the bulk out endpoint */
|
__u8 bulk_out_endpointAddr; /* the address of the bulk out endpoint */
|
||||||
|
int errors; /* the last request tanked */
|
||||||
|
spinlock_t err_lock; /* lock for errors */
|
||||||
struct kref kref;
|
struct kref kref;
|
||||||
struct mutex io_mutex; /* synchronize I/O with disconnect */
|
struct mutex io_mutex; /* synchronize I/O with disconnect */
|
||||||
};
|
};
|
||||||
#define to_skel_dev(d) container_of(d, struct usb_skel, kref)
|
#define to_skel_dev(d) container_of(d, struct usb_skel, kref)
|
||||||
|
|
||||||
static struct usb_driver skel_driver;
|
static struct usb_driver skel_driver;
|
||||||
|
static void skel_draw_down(struct usb_skel *dev);
|
||||||
|
|
||||||
static void skel_delete(struct kref *kref)
|
static void skel_delete(struct kref *kref)
|
||||||
{
|
{
|
||||||
|
@ -130,6 +134,30 @@ static int skel_release(struct inode *inode, struct file *file)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int skel_flush(struct file *file, fl_owner_t id)
|
||||||
|
{
|
||||||
|
struct usb_skel *dev;
|
||||||
|
int res;
|
||||||
|
|
||||||
|
dev = (struct usb_skel *)file->private_data;
|
||||||
|
if (dev == NULL)
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
|
/* wait for io to stop */
|
||||||
|
mutex_lock(&dev->io_mutex);
|
||||||
|
skel_draw_down(dev);
|
||||||
|
|
||||||
|
/* read out errors, leave subsequent opens a clean slate */
|
||||||
|
spin_lock_irq(&dev->err_lock);
|
||||||
|
res = dev->errors ? (dev->errors == -EPIPE ? -EPIPE : -EIO) : 0;
|
||||||
|
dev->errors = 0;
|
||||||
|
spin_unlock_irq(&dev->err_lock);
|
||||||
|
|
||||||
|
mutex_unlock(&dev->io_mutex);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
static ssize_t skel_read(struct file *file, char *buffer, size_t count, loff_t *ppos)
|
static ssize_t skel_read(struct file *file, char *buffer, size_t count, loff_t *ppos)
|
||||||
{
|
{
|
||||||
struct usb_skel *dev;
|
struct usb_skel *dev;
|
||||||
|
@ -171,12 +199,16 @@ static void skel_write_bulk_callback(struct urb *urb)
|
||||||
dev = (struct usb_skel *)urb->context;
|
dev = (struct usb_skel *)urb->context;
|
||||||
|
|
||||||
/* sync/async unlink faults aren't errors */
|
/* sync/async unlink faults aren't errors */
|
||||||
if (urb->status &&
|
if (urb->status) {
|
||||||
!(urb->status == -ENOENT ||
|
if(!(urb->status == -ENOENT ||
|
||||||
urb->status == -ECONNRESET ||
|
urb->status == -ECONNRESET ||
|
||||||
urb->status == -ESHUTDOWN)) {
|
urb->status == -ESHUTDOWN))
|
||||||
err("%s - nonzero write bulk status received: %d",
|
err("%s - nonzero write bulk status received: %d",
|
||||||
__FUNCTION__, urb->status);
|
__FUNCTION__, urb->status);
|
||||||
|
|
||||||
|
spin_lock(&dev->err_lock);
|
||||||
|
dev->errors = urb->status;
|
||||||
|
spin_unlock(&dev->err_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* free up our allocated buffer */
|
/* free up our allocated buffer */
|
||||||
|
@ -205,6 +237,17 @@ static ssize_t skel_write(struct file *file, const char *user_buffer, size_t cou
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
spin_lock_irq(&dev->err_lock);
|
||||||
|
if ((retval = dev->errors) < 0) {
|
||||||
|
/* any error is reported once */
|
||||||
|
dev->errors = 0;
|
||||||
|
/* to preserve notifications about reset */
|
||||||
|
retval = (retval == -EPIPE) ? retval : -EIO;
|
||||||
|
}
|
||||||
|
spin_unlock_irq(&dev->err_lock);
|
||||||
|
if (retval < 0)
|
||||||
|
goto error;
|
||||||
|
|
||||||
/* create a urb, and a buffer for it, and copy the data to the urb */
|
/* create a urb, and a buffer for it, and copy the data to the urb */
|
||||||
urb = usb_alloc_urb(0, GFP_KERNEL);
|
urb = usb_alloc_urb(0, GFP_KERNEL);
|
||||||
if (!urb) {
|
if (!urb) {
|
||||||
|
@ -236,13 +279,14 @@ static ssize_t skel_write(struct file *file, const char *user_buffer, size_t cou
|
||||||
usb_sndbulkpipe(dev->udev, dev->bulk_out_endpointAddr),
|
usb_sndbulkpipe(dev->udev, dev->bulk_out_endpointAddr),
|
||||||
buf, writesize, skel_write_bulk_callback, dev);
|
buf, writesize, skel_write_bulk_callback, dev);
|
||||||
urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
|
urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
|
||||||
|
usb_anchor_urb(urb, &dev->submitted);
|
||||||
|
|
||||||
/* send the data out the bulk port */
|
/* send the data out the bulk port */
|
||||||
retval = usb_submit_urb(urb, GFP_KERNEL);
|
retval = usb_submit_urb(urb, GFP_KERNEL);
|
||||||
mutex_unlock(&dev->io_mutex);
|
mutex_unlock(&dev->io_mutex);
|
||||||
if (retval) {
|
if (retval) {
|
||||||
err("%s - failed submitting write urb, error %d", __FUNCTION__, retval);
|
err("%s - failed submitting write urb, error %d", __FUNCTION__, retval);
|
||||||
goto error;
|
goto error_unanchor;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* release our reference to this urb, the USB core will eventually free it entirely */
|
/* release our reference to this urb, the USB core will eventually free it entirely */
|
||||||
|
@ -251,6 +295,8 @@ static ssize_t skel_write(struct file *file, const char *user_buffer, size_t cou
|
||||||
|
|
||||||
return writesize;
|
return writesize;
|
||||||
|
|
||||||
|
error_unanchor:
|
||||||
|
usb_unanchor_urb(urb);
|
||||||
error:
|
error:
|
||||||
if (urb) {
|
if (urb) {
|
||||||
usb_buffer_free(dev->udev, writesize, buf, urb->transfer_dma);
|
usb_buffer_free(dev->udev, writesize, buf, urb->transfer_dma);
|
||||||
|
@ -268,6 +314,7 @@ static const struct file_operations skel_fops = {
|
||||||
.write = skel_write,
|
.write = skel_write,
|
||||||
.open = skel_open,
|
.open = skel_open,
|
||||||
.release = skel_release,
|
.release = skel_release,
|
||||||
|
.flush = skel_flush,
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -298,6 +345,8 @@ static int skel_probe(struct usb_interface *interface, const struct usb_device_i
|
||||||
kref_init(&dev->kref);
|
kref_init(&dev->kref);
|
||||||
sema_init(&dev->limit_sem, WRITES_IN_FLIGHT);
|
sema_init(&dev->limit_sem, WRITES_IN_FLIGHT);
|
||||||
mutex_init(&dev->io_mutex);
|
mutex_init(&dev->io_mutex);
|
||||||
|
spin_lock_init(&dev->err_lock);
|
||||||
|
init_usb_anchor(&dev->submitted);
|
||||||
|
|
||||||
dev->udev = usb_get_dev(interface_to_usbdev(interface));
|
dev->udev = usb_get_dev(interface_to_usbdev(interface));
|
||||||
dev->interface = interface;
|
dev->interface = interface;
|
||||||
|
@ -377,6 +426,15 @@ static void skel_disconnect(struct usb_interface *interface)
|
||||||
info("USB Skeleton #%d now disconnected", minor);
|
info("USB Skeleton #%d now disconnected", minor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void skel_draw_down(struct usb_skel *dev)
|
||||||
|
{
|
||||||
|
int time;
|
||||||
|
|
||||||
|
time = usb_wait_anchor_empty_timeout(&dev->submitted, 1000);
|
||||||
|
if (!time)
|
||||||
|
usb_kill_anchored_urbs(&dev->submitted);
|
||||||
|
}
|
||||||
|
|
||||||
static struct usb_driver skel_driver = {
|
static struct usb_driver skel_driver = {
|
||||||
.name = "skeleton",
|
.name = "skeleton",
|
||||||
.probe = skel_probe,
|
.probe = skel_probe,
|
||||||
|
|
Loading…
Reference in New Issue