* - make device locking interruptible
* 2004-04-30 - 0.95 Juergen Stuber <starblue@users.sourceforge.net>
* - check for valid udev on resubmitting and unlinking urbs
+ * 2004-08-03 - 0.96 Juergen Stuber <starblue@users.sourceforge.net>
+ * - move reset into open to clean out spurious data
*/
-#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/smp_lock.h>
#include <linux/completion.h>
+#include <linux/mutex.h>
#include <asm/uaccess.h>
#include <linux/usb.h>
#include <linux/poll.h>
/* Version Information */
-#define DRIVER_VERSION "v0.95"
+#define DRIVER_VERSION "v0.96"
#define DRIVER_AUTHOR "Juergen Stuber <starblue@sourceforge.net>"
#define DRIVER_DESC "LEGO USB Tower Driver"
/* Module parameters */
-MODULE_PARM(debug, "i");
+module_param(debug, int, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(debug, "Debug enabled or not");
/* The defaults are chosen to work with the latest versions of leJOS and NQC.
* In this case read_buffer_size should exceed the maximal packet length
* (417 for datalog uploads), and packet_timeout should be set.
*/
-static size_t read_buffer_size = 480;
-MODULE_PARM(read_buffer_size, "i");
+static int read_buffer_size = 480;
+module_param(read_buffer_size, int, 0);
MODULE_PARM_DESC(read_buffer_size, "Read buffer size");
/* Some legacy software likes to send packets in one piece.
* A problem with long writes is that the following read may time out
* if the software is not prepared to wait long enough.
*/
-static size_t write_buffer_size = 480;
-MODULE_PARM(write_buffer_size, "i");
+static int write_buffer_size = 480;
+module_param(write_buffer_size, int, 0);
MODULE_PARM_DESC(write_buffer_size, "Write buffer size");
/* Some legacy software expects reads to contain whole LASM packets.
* Set it to 0 to disable.
*/
static int packet_timeout = 50;
-MODULE_PARM(packet_timeout, "i");
+module_param(packet_timeout, int, 0);
MODULE_PARM_DESC(packet_timeout, "Packet timeout in ms");
/* Some legacy software expects blocking reads to time out.
* Set it to 0 to disable.
*/
static int read_timeout = 200;
-MODULE_PARM(read_timeout, "i");
+module_param(read_timeout, int, 0);
MODULE_PARM_DESC(read_timeout, "Read timeout in ms");
/* As of kernel version 2.6.4 ehci-hcd uses an
* or set to 0 to use the standard interval from the endpoint descriptors.
*/
static int interrupt_in_interval = 2;
-MODULE_PARM(interrupt_in_interval, "i");
+module_param(interrupt_in_interval, int, 0);
MODULE_PARM_DESC(interrupt_in_interval, "Interrupt in interval in ms");
static int interrupt_out_interval = 8;
-MODULE_PARM(interrupt_out_interval, "i");
+module_param(interrupt_out_interval, int, 0);
MODULE_PARM_DESC(interrupt_out_interval, "Interrupt out interval in ms");
/* Define these values to match your device */
#define LEGO_USB_TOWER_REQUEST_GET_VERSION 0xFD
struct tower_reset_reply {
- __u16 size; /* little-endian */
+ __le16 size; /* little-endian */
__u8 err_code;
__u8 spare;
} __attribute__ ((packed));
struct tower_get_version_reply {
- __u16 size; /* little-endian */
+ __le16 size; /* little-endian */
__u8 err_code;
__u8 spare;
__u8 major;
__u8 minor;
- __u16 build_no; /* little-endian */
+ __le16 build_no; /* little-endian */
} __attribute__ ((packed));
static void tower_abort_transfers (struct lego_usb_tower *dev);
static void tower_check_for_read_packet (struct lego_usb_tower *dev);
-static void tower_interrupt_in_callback (struct urb *urb, struct pt_regs *regs);
-static void tower_interrupt_out_callback (struct urb *urb, struct pt_regs *regs);
+static void tower_interrupt_in_callback (struct urb *urb);
+static void tower_interrupt_out_callback (struct urb *urb);
static int tower_probe (struct usb_interface *interface, const struct usb_device_id *id);
static void tower_disconnect (struct usb_interface *interface);
/* prevent races between open() and disconnect */
-static DECLARE_MUTEX (disconnect_sem);
+static DEFINE_MUTEX (disconnect_mutex);
/* file operations needed when we register this driver */
-static struct file_operations tower_fops = {
+static const struct file_operations tower_fops = {
.owner = THIS_MODULE,
.read = tower_read,
.write = tower_write,
/*
* 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
+ * and to have the device registered with the driver core
*/
static struct usb_class_driver tower_class = {
- .name = "usb/legousbtower%d",
+ .name = "legousbtower%d",
.fops = &tower_fops,
- .mode = S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH,
.minor_base = LEGO_USB_TOWER_MINOR_BASE,
};
/* usb specific object needed to register this driver with the usb subsystem */
static struct usb_driver tower_driver = {
- .owner = THIS_MODULE,
.name = "legousbtower",
.probe = tower_probe,
.disconnect = tower_disconnect,
tower_abort_transfers (dev);
/* free data structures */
- if (dev->interrupt_in_urb != NULL) {
- usb_free_urb (dev->interrupt_in_urb);
- }
- if (dev->interrupt_out_urb != NULL) {
- usb_free_urb (dev->interrupt_out_urb);
- }
+ usb_free_urb(dev->interrupt_in_urb);
+ usb_free_urb(dev->interrupt_out_urb);
kfree (dev->read_buffer);
kfree (dev->interrupt_in_buffer);
kfree (dev->interrupt_out_buffer);
int subminor;
int retval = 0;
struct usb_interface *interface;
+ struct tower_reset_reply reset_reply;
+ int result;
dbg(2, "%s: enter", __FUNCTION__);
nonseekable_open(inode, file);
subminor = iminor(inode);
- down (&disconnect_sem);
+ mutex_lock (&disconnect_mutex);
interface = usb_find_interface (&tower_driver, subminor);
}
dev->open_count = 1;
+ /* reset the tower */
+ result = usb_control_msg (dev->udev,
+ usb_rcvctrlpipe(dev->udev, 0),
+ LEGO_USB_TOWER_REQUEST_RESET,
+ USB_TYPE_VENDOR | USB_DIR_IN | USB_RECIP_DEVICE,
+ 0,
+ 0,
+ &reset_reply,
+ sizeof(reset_reply),
+ 1000);
+ if (result < 0) {
+ err("LEGO USB Tower reset control request failed");
+ retval = result;
+ goto unlock_exit;
+ }
+
/* initialize in direction */
dev->read_buffer_length = 0;
dev->read_packet_length = 0;
dev->udev,
usb_rcvintpipe(dev->udev, dev->interrupt_in_endpoint->bEndpointAddress),
dev->interrupt_in_buffer,
- dev->interrupt_in_endpoint->wMaxPacketSize,
+ le16_to_cpu(dev->interrupt_in_endpoint->wMaxPacketSize),
tower_interrupt_in_callback,
dev,
dev->interrupt_in_interval);
up (&dev->sem);
unlock_disconnect_exit:
- up (&disconnect_sem);
+ mutex_unlock (&disconnect_mutex);
dbg(2, "%s: leave, return value %d ", __FUNCTION__, retval);
if (dev->interrupt_in_running) {
dev->interrupt_in_running = 0;
mb();
- if (dev->interrupt_in_urb != NULL && dev->udev) {
- usb_unlink_urb (dev->interrupt_in_urb);
- }
- }
- if (dev->interrupt_out_busy) {
- if (dev->interrupt_out_urb != NULL && dev->udev) {
- usb_unlink_urb (dev->interrupt_out_urb);
- }
+ if (dev->udev)
+ usb_kill_urb (dev->interrupt_in_urb);
}
+ if (dev->interrupt_out_busy && dev->udev)
+ usb_kill_urb(dev->interrupt_out_urb);
exit:
dbg(2, "%s: leave", __FUNCTION__);
}
/* write the data into interrupt_out_buffer from userspace */
- bytes_to_write = min(count, write_buffer_size);
+ bytes_to_write = min_t(int, count, write_buffer_size);
dbg(4, "%s: count = %Zd, bytes_to_write = %Zd", __FUNCTION__, count, bytes_to_write);
if (copy_from_user (dev->interrupt_out_buffer, buffer, bytes_to_write)) {
/**
* tower_interrupt_in_callback
*/
-static void tower_interrupt_in_callback (struct urb *urb, struct pt_regs *regs)
+static void tower_interrupt_in_callback (struct urb *urb)
{
struct lego_usb_tower *dev = (struct lego_usb_tower *)urb->context;
int retval;
/**
* tower_interrupt_out_callback
*/
-static void tower_interrupt_out_callback (struct urb *urb, struct pt_regs *regs)
+static void tower_interrupt_out_callback (struct urb *urb)
{
struct lego_usb_tower *dev = (struct lego_usb_tower *)urb->context;
struct lego_usb_tower *dev = NULL;
struct usb_host_interface *iface_desc;
struct usb_endpoint_descriptor* endpoint;
- struct tower_reset_reply reset_reply;
struct tower_get_version_reply get_version_reply;
int i;
int retval = -ENOMEM;
info ("udev is NULL.");
}
- /* See if the device offered us matches what we can accept */
- if ((udev->descriptor.idVendor != LEGO_USB_TOWER_VENDOR_ID) ||
- (udev->descriptor.idProduct != LEGO_USB_TOWER_PRODUCT_ID)) {
- return -ENODEV;
- }
-
-
- /* allocate memory for our device state and intialize it */
+ /* allocate memory for our device state and initialize it */
dev = kmalloc (sizeof(struct lego_usb_tower), GFP_KERNEL);
for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
endpoint = &iface_desc->endpoint[i].desc;
- if (((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN) &&
- ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT)) {
- dev->interrupt_in_endpoint = endpoint;
- }
-
- if (((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT) &&
- ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT)) {
- dev->interrupt_out_endpoint = endpoint;
+ if (usb_endpoint_xfer_int(endpoint)) {
+ if (usb_endpoint_dir_in(endpoint))
+ dev->interrupt_in_endpoint = endpoint;
+ else
+ dev->interrupt_out_endpoint = endpoint;
}
}
if(dev->interrupt_in_endpoint == NULL) {
err("Couldn't allocate read_buffer");
goto error;
}
- dev->interrupt_in_buffer = kmalloc (dev->interrupt_in_endpoint->wMaxPacketSize, GFP_KERNEL);
+ dev->interrupt_in_buffer = kmalloc (le16_to_cpu(dev->interrupt_in_endpoint->wMaxPacketSize), GFP_KERNEL);
if (!dev->interrupt_in_buffer) {
err("Couldn't allocate interrupt_in_buffer");
goto error;
/* let the user know what node this device is now attached to */
info ("LEGO USB Tower #%d now attached to major %d minor %d", (dev->minor - LEGO_USB_TOWER_MINOR_BASE), USB_MAJOR, dev->minor);
- /* reset the tower */
- result = usb_control_msg (udev,
- usb_rcvctrlpipe(udev, 0),
- LEGO_USB_TOWER_REQUEST_RESET,
- USB_TYPE_VENDOR | USB_DIR_IN | USB_RECIP_DEVICE,
- 0,
- 0,
- &reset_reply,
- sizeof(reset_reply),
- HZ);
- if (result < 0) {
- err("LEGO USB Tower reset control request failed");
- retval = result;
- goto error;
- }
-
/* get the firmware version and log it */
result = usb_control_msg (udev,
usb_rcvctrlpipe(udev, 0),
0,
&get_version_reply,
sizeof(get_version_reply),
- HZ);
+ 1000);
if (result < 0) {
err("LEGO USB Tower get version control request failed");
retval = result;
dbg(2, "%s: enter", __FUNCTION__);
- down (&disconnect_sem);
+ mutex_lock (&disconnect_mutex);
dev = usb_get_intfdata (interface);
usb_set_intfdata (interface, NULL);
up (&dev->sem);
}
- up (&disconnect_sem);
+ mutex_unlock (&disconnect_mutex);
info("LEGO USB Tower #%d now disconnected", (minor - LEGO_USB_TOWER_MINOR_BASE));