// SPDX-License-Identifier: GPL-2.0+ /* * adutux - driver for ADU devices from Ontrak Control Systems * This is an experimental driver. Use at your own risk. * This driver is not supported by Ontrak Control Systems. * * Copyright (c) 2003 John Homppi (SCO, leave this notice here) * * derived from the Lego USB Tower driver 0.56: * Copyright (c) 2003 David Glance <davidgsf@sourceforge.net> * 2001 Juergen Stuber <stuber@loria.fr> * that was derived from USB Skeleton driver - 0.5 * Copyright (c) 2001 Greg Kroah-Hartman (greg@kroah.com) *
*/
/* we can have up to this number of device plugged in at once */ #define MAX_DEVICES 16
#define COMMAND_TIMEOUT (2*HZ)
/* * The locking scheme is a vanilla 3-lock: * adu_device.buflock: A spinlock, covers what IRQs touch. * adutux_mutex: A Static lock to cover open_count. It would also cover * any globals, but we don't have them in 2.6. * adu_device.mtx: A mutex to hold across sleepers like copy_from_user. * It covers all of adu_device, except the open_count * and what .buflock covers.
*/
/* Structure to hold all of our device specific stuff */ struct adu_device { struct mutex mtx; struct usb_device *udev; /* save off the usb device pointer */ struct usb_interface *interface; unsignedint minor; /* the starting minor number for this device */ char serial_number[8];
int open_count; /* number of times this port has been opened */ unsignedlong disconnected:1;
char *read_buffer_primary; int read_buffer_length; char *read_buffer_secondary; int secondary_head; int secondary_tail;
spinlock_t buflock;
exit:
dev->read_urb_finished = 1;
spin_unlock_irqrestore(&dev->buflock, flags); /* always wake up so we recover from errors */
wake_up_interruptible(&dev->read_wait);
}
staticvoid adu_interrupt_out_callback(struct urb *urb)
{ struct adu_device *dev = urb->context; int status = urb->status; unsignedlong flags;
staticint adu_open(struct inode *inode, struct file *file)
{ struct adu_device *dev = NULL; struct usb_interface *interface; int subminor; int retval;
subminor = iminor(inode);
retval = mutex_lock_interruptible(&adutux_mutex); if (retval) goto exit_no_lock;
interface = usb_find_interface(&adu_driver, subminor); if (!interface) {
pr_err("%s - error, can't find device for minor %d\n",
__func__, subminor);
retval = -ENODEV; goto exit_no_device;
}
dev = usb_get_intfdata(interface); if (!dev) {
retval = -ENODEV; goto exit_no_device;
}
/* check that nobody else is using the device */ if (dev->open_count) {
retval = -EBUSY; goto exit_no_device;
}
++dev->open_count;
dev_dbg(&dev->udev->dev, "%s: open count %d\n", __func__,
dev->open_count);
/* save device in the file's private structure */
file->private_data = dev;
/* initialize in direction */
dev->read_buffer_length = 0;
/* fixup first read by having urb waiting for it */
usb_fill_int_urb(dev->interrupt_in_urb, dev->udev,
usb_rcvintpipe(dev->udev,
dev->interrupt_in_endpoint->bEndpointAddress),
dev->interrupt_in_buffer,
usb_endpoint_maxp(dev->interrupt_in_endpoint),
adu_interrupt_in_callback, dev,
dev->interrupt_in_endpoint->bInterval);
dev->read_urb_finished = 0; if (usb_submit_urb(dev->interrupt_in_urb, GFP_KERNEL))
dev->read_urb_finished = 1; /* we ignore failure */ /* end of fixup for first read */
/* initialize out direction */
dev->out_urb_finished = 1;
dev = file->private_data; if (dev == NULL) {
retval = -ENODEV; gotoexit;
}
mutex_lock(&adutux_mutex); /* not interruptible */
if (dev->open_count <= 0) {
dev_dbg(&dev->udev->dev, "%s : device not opened\n", __func__);
retval = -ENODEV; goto unlock;
}
adu_release_internal(dev); if (dev->disconnected) { /* the device was unplugged before the file was released */ if (!dev->open_count) /* ... and we're the last user */
adu_delete(dev);
}
unlock:
mutex_unlock(&adutux_mutex); exit: return retval;
}
retval = mutex_lock_interruptible(&dev->mtx); if (retval) goto exit_nolock;
/* verify that the device wasn't unplugged */ if (dev->disconnected) {
retval = -ENODEV;
pr_err("No device or device unplugged %d\n", retval); gotoexit;
}
/* verify that we actually have some data to write */ if (count == 0) {
dev_dbg(&dev->udev->dev, "%s : write request of 0 bytes\n",
__func__); gotoexit;
}
while (count > 0) {
add_wait_queue(&dev->write_wait, &waita);
set_current_state(TASK_INTERRUPTIBLE);
spin_lock_irqsave(&dev->buflock, flags); if (!dev->out_urb_finished) {
spin_unlock_irqrestore(&dev->buflock, flags);
/* * usb class driver info in order to get a minor number from the usb core, * and to have the device registered with devfs and the driver core
*/ staticstruct usb_class_driver adu_class = {
.name = "usb/adutux%d",
.fops = &adu_fops,
.minor_base = ADU_MINOR_BASE,
};
/* * adu_probe * * Called by the usb core when a new device is connected that it thinks * this driver might be interested in.
*/ staticint adu_probe(struct usb_interface *interface, conststruct usb_device_id *id)
{ struct usb_device *udev = interface_to_usbdev(interface); struct adu_device *dev = NULL; int retval = -ENOMEM; int in_end_size; int out_end_size; int res;
/* allocate memory for our device state and initialize it */
dev = kzalloc(sizeof(struct adu_device), GFP_KERNEL); if (!dev) return -ENOMEM;
dev->interrupt_in_buffer = kmalloc(in_end_size, GFP_KERNEL); if (!dev->interrupt_in_buffer) goto error;
/* debug code prime the buffer */
memset(dev->interrupt_in_buffer, 'i', in_end_size);
dev->interrupt_in_urb = usb_alloc_urb(0, GFP_KERNEL); if (!dev->interrupt_in_urb) goto error;
dev->interrupt_out_buffer = kmalloc(out_end_size, GFP_KERNEL); if (!dev->interrupt_out_buffer) goto error;
dev->interrupt_out_urb = usb_alloc_urb(0, GFP_KERNEL); if (!dev->interrupt_out_urb) goto error;
if (!usb_string(udev, udev->descriptor.iSerialNumber, dev->serial_number, sizeof(dev->serial_number))) {
dev_err(&interface->dev, "Could not retrieve serial number\n");
retval = -EIO; goto error;
}
dev_dbg(&interface->dev, "serial_number=%s", dev->serial_number);
/* we can register the device now, as it is ready */
usb_set_intfdata(interface, dev);
retval = usb_register_dev(interface, &adu_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;
}
dev->minor = interface->minor;
/* let the user know what node this device is now attached to */
dev_info(&interface->dev, "ADU%d %s now attached to /dev/usb/adutux%d\n",
le16_to_cpu(udev->descriptor.idProduct), dev->serial_number,
(dev->minor - ADU_MINOR_BASE));
return 0;
error:
adu_delete(dev); return retval;
}
/* * adu_disconnect * * Called by the usb core when the device is removed from the system.
*/ staticvoid adu_disconnect(struct usb_interface *interface)
{ struct adu_device *dev;
mutex_lock(&dev->mtx); /* not interruptible */
dev->disconnected = 1;
mutex_unlock(&dev->mtx);
/* if the device is not opened, then we clean up right now */ if (!dev->open_count)
adu_delete(dev);
mutex_unlock(&adutux_mutex);
}
/* usb specific object needed to register this driver with the usb subsystem */ staticstruct usb_driver adu_driver = {
.name = "adutux",
.probe = adu_probe,
.disconnect = adu_disconnect,
.id_table = device_table,
};
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.