// SPDX-License-Identifier: GPL-2.0 /* * USB Skeleton driver - 2.2 * * Copyright (C) 2001-2004 Greg Kroah-Hartman (greg@kroah.com) * * This driver is based on the 2.6.3 version of drivers/usb/usb-skeleton.c * but has been rewritten to be easier to read and use.
*/
/* Define these values to match your devices */ #define USB_SKEL_VENDOR_ID 0xfff0 #define USB_SKEL_PRODUCT_ID 0xfff0
/* table of devices that work with this driver */ staticconststruct usb_device_id skel_table[] = {
{ USB_DEVICE(USB_SKEL_VENDOR_ID, USB_SKEL_PRODUCT_ID) },
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, skel_table);
/* Get a minor range for your devices from the usb maintainer */ #define USB_SKEL_MINOR_BASE 192
/* our private defines. if this grows any larger, use your own .h file */ #define MAX_TRANSFER (PAGE_SIZE - 512) /* * MAX_TRANSFER is chosen so that the VM is not stressed by * allocations > PAGE_SIZE and the number of packets in a page * is an integer 512 is the largest possible packet on EHCI
*/ #define WRITES_IN_FLIGHT 8 /* arbitrarily chosen */
/* Structure to hold all of our device specific stuff */ struct usb_skel { struct usb_device *udev; /* the usb device for this device */ struct usb_interface *interface; /* the interface for this device */ struct semaphore limit_sem; /* limiting the number of writes in progress */ struct usb_anchor submitted; /* in case we need to retract our submissions */ struct urb *bulk_in_urb; /* the urb to read data with */ unsignedchar *bulk_in_buffer; /* the buffer to receive data */
size_t bulk_in_size; /* the size of the receive buffer */
size_t bulk_in_filled; /* number of bytes in the buffer */
size_t bulk_in_copied; /* already copied to user space */
__u8 bulk_in_endpointAddr; /* the address of the bulk in endpoint */
__u8 bulk_out_endpointAddr; /* the address of the bulk out endpoint */ int errors; /* the last request tanked */ bool ongoing_read; /* a read is going on */
spinlock_t err_lock; /* lock for errors */ struct kref kref; struct mutex io_mutex; /* synchronize I/O with disconnect */ unsignedlong disconnected:1;
wait_queue_head_t bulk_in_wait; /* to wait for an ongoing read */
}; #define to_skel_dev(d) container_of(d, struct usb_skel, kref)
/* no concurrent readers */
rv = mutex_lock_interruptible(&dev->io_mutex); if (rv < 0) return rv;
if (dev->disconnected) { /* disconnect() was called */
rv = -ENODEV; gotoexit;
}
/* if IO is under way, we must not touch things */
retry:
spin_lock_irq(&dev->err_lock);
ongoing_io = dev->ongoing_read;
spin_unlock_irq(&dev->err_lock);
if (ongoing_io) { /* nonblocking IO shall not wait */ if (file->f_flags & O_NONBLOCK) {
rv = -EAGAIN; gotoexit;
} /* * IO may take forever * hence wait in an interruptible state
*/
rv = wait_event_interruptible(dev->bulk_in_wait, (!dev->ongoing_read)); if (rv < 0) gotoexit;
}
/* errors must be reported */
rv = dev->errors; if (rv < 0) { /* any error is reported once */
dev->errors = 0; /* to preserve notifications about reset */
rv = (rv == -EPIPE) ? rv : -EIO; /* report it */ gotoexit;
}
/* * if the buffer is filled we may satisfy the read * else we need to start IO
*/
if (dev->bulk_in_filled) { /* we had read data */
size_t available = dev->bulk_in_filled - dev->bulk_in_copied;
size_t chunk = min(available, count);
if (!available) { /* * all data has been used * actual IO needs to be done
*/
rv = skel_do_read_io(dev, count); if (rv < 0) gotoexit; else goto retry;
} /* * data is available * chunk tells us how much shall be copied
*/
/* * if we are asked for more than we have, * we start IO but don't wait
*/ if (available < count)
skel_do_read_io(dev, count - chunk);
} else { /* no data in the buffer */
rv = skel_do_read_io(dev, count); if (rv < 0) gotoexit; else goto retry;
} exit:
mutex_unlock(&dev->io_mutex); return rv;
}
/* verify that we actually have some data to write */ if (count == 0) gotoexit;
/* * limit the number of URBs in flight to stop a user from using up all * RAM
*/ if (!(file->f_flags & O_NONBLOCK)) { if (down_interruptible(&dev->limit_sem)) {
retval = -ERESTARTSYS; gotoexit;
}
} else { if (down_trylock(&dev->limit_sem)) {
retval = -EAGAIN; gotoexit;
}
}
spin_lock_irq(&dev->err_lock);
retval = dev->errors; if (retval < 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 */
urb = usb_alloc_urb(0, GFP_KERNEL); if (!urb) {
retval = -ENOMEM; goto error;
}
/* this lock makes sure we don't submit URBs to gone devices */
mutex_lock(&dev->io_mutex); if (dev->disconnected) { /* disconnect() was called */
mutex_unlock(&dev->io_mutex);
retval = -ENODEV; goto error;
}
/* * usb class driver info in order to get a minor number from the usb core, * and to have the device registered with the driver core
*/ staticstruct usb_class_driver skel_class = {
.name = "skel%d",
.fops = &skel_fops,
.minor_base = USB_SKEL_MINOR_BASE,
};
/* set up the endpoint information */ /* use only the first bulk-in and bulk-out endpoints */
retval = usb_find_common_endpoints(interface->cur_altsetting,
&bulk_in, &bulk_out, NULL, NULL); if (retval) {
dev_err(&interface->dev, "Could not find both bulk-in and bulk-out endpoints\n"); goto error;
}
/* save our data pointer in this interface device */
usb_set_intfdata(interface, dev);
/* we can register the device now, as it is ready */
retval = usb_register_dev(interface, &skel_class); if (retval) { /* something prevented us from registering this driver */
dev_err(&interface->dev, "Not able to get a minor for this device.\n");
usb_set_intfdata(interface, NULL); goto error;
}
/* let the user know what node this device is now attached to */
dev_info(&interface->dev, "USB Skeleton device now attached to USBSkel-%d",
interface->minor); return 0;
error: /* this frees allocated memory */
kref_put(&dev->kref, skel_delete);
return retval;
}
staticvoid skel_disconnect(struct usb_interface *interface)
{ struct usb_skel *dev; int minor = interface->minor;
dev = usb_get_intfdata(interface);
/* give back our minor */
usb_deregister_dev(interface, &skel_class);
/* prevent more I/O from starting */
mutex_lock(&dev->io_mutex);
dev->disconnected = 1;
mutex_unlock(&dev->io_mutex);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.