162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * LEGO USB Tower driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2003 David Glance <davidgsf@sourceforge.net>
662306a36Sopenharmony_ci *               2001-2004 Juergen Stuber <starblue@users.sourceforge.net>
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * derived from USB Skeleton driver - 0.5
962306a36Sopenharmony_ci * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
1062306a36Sopenharmony_ci *
1162306a36Sopenharmony_ci * History:
1262306a36Sopenharmony_ci *
1362306a36Sopenharmony_ci * 2001-10-13 - 0.1 js
1462306a36Sopenharmony_ci *   - first version
1562306a36Sopenharmony_ci * 2001-11-03 - 0.2 js
1662306a36Sopenharmony_ci *   - simplified buffering, one-shot URBs for writing
1762306a36Sopenharmony_ci * 2001-11-10 - 0.3 js
1862306a36Sopenharmony_ci *   - removed IOCTL (setting power/mode is more complicated, postponed)
1962306a36Sopenharmony_ci * 2001-11-28 - 0.4 js
2062306a36Sopenharmony_ci *   - added vendor commands for mode of operation and power level in open
2162306a36Sopenharmony_ci * 2001-12-04 - 0.5 js
2262306a36Sopenharmony_ci *   - set IR mode by default (by oversight 0.4 set VLL mode)
2362306a36Sopenharmony_ci * 2002-01-11 - 0.5? pcchan
2462306a36Sopenharmony_ci *   - make read buffer reusable and work around bytes_to_write issue between
2562306a36Sopenharmony_ci *     uhci and legusbtower
2662306a36Sopenharmony_ci * 2002-09-23 - 0.52 david (david@csse.uwa.edu.au)
2762306a36Sopenharmony_ci *   - imported into lejos project
2862306a36Sopenharmony_ci *   - changed wake_up to wake_up_interruptible
2962306a36Sopenharmony_ci *   - changed to use lego0 rather than tower0
3062306a36Sopenharmony_ci *   - changed dbg() to use __func__ rather than deprecated __func__
3162306a36Sopenharmony_ci * 2003-01-12 - 0.53 david (david@csse.uwa.edu.au)
3262306a36Sopenharmony_ci *   - changed read and write to write everything or
3362306a36Sopenharmony_ci *     timeout (from a patch by Chris Riesen and Brett Thaeler driver)
3462306a36Sopenharmony_ci *   - added ioctl functionality to set timeouts
3562306a36Sopenharmony_ci * 2003-07-18 - 0.54 davidgsf (david@csse.uwa.edu.au)
3662306a36Sopenharmony_ci *   - initial import into LegoUSB project
3762306a36Sopenharmony_ci *   - merge of existing LegoUSB.c driver
3862306a36Sopenharmony_ci * 2003-07-18 - 0.56 davidgsf (david@csse.uwa.edu.au)
3962306a36Sopenharmony_ci *   - port to 2.6 style driver
4062306a36Sopenharmony_ci * 2004-02-29 - 0.6 Juergen Stuber <starblue@users.sourceforge.net>
4162306a36Sopenharmony_ci *   - fix locking
4262306a36Sopenharmony_ci *   - unlink read URBs which are no longer needed
4362306a36Sopenharmony_ci *   - allow increased buffer size, eliminates need for timeout on write
4462306a36Sopenharmony_ci *   - have read URB running continuously
4562306a36Sopenharmony_ci *   - added poll
4662306a36Sopenharmony_ci *   - forbid seeking
4762306a36Sopenharmony_ci *   - added nonblocking I/O
4862306a36Sopenharmony_ci *   - changed back __func__ to __func__
4962306a36Sopenharmony_ci *   - read and log tower firmware version
5062306a36Sopenharmony_ci *   - reset tower on probe, avoids failure of first write
5162306a36Sopenharmony_ci * 2004-03-09 - 0.7 Juergen Stuber <starblue@users.sourceforge.net>
5262306a36Sopenharmony_ci *   - timeout read now only after inactivity, shorten default accordingly
5362306a36Sopenharmony_ci * 2004-03-11 - 0.8 Juergen Stuber <starblue@users.sourceforge.net>
5462306a36Sopenharmony_ci *   - log major, minor instead of possibly confusing device filename
5562306a36Sopenharmony_ci *   - whitespace cleanup
5662306a36Sopenharmony_ci * 2004-03-12 - 0.9 Juergen Stuber <starblue@users.sourceforge.net>
5762306a36Sopenharmony_ci *   - normalize whitespace in debug messages
5862306a36Sopenharmony_ci *   - take care about endianness in control message responses
5962306a36Sopenharmony_ci * 2004-03-13 - 0.91 Juergen Stuber <starblue@users.sourceforge.net>
6062306a36Sopenharmony_ci *   - make default intervals longer to accommodate current EHCI driver
6162306a36Sopenharmony_ci * 2004-03-19 - 0.92 Juergen Stuber <starblue@users.sourceforge.net>
6262306a36Sopenharmony_ci *   - replaced atomic_t by memory barriers
6362306a36Sopenharmony_ci * 2004-04-21 - 0.93 Juergen Stuber <starblue@users.sourceforge.net>
6462306a36Sopenharmony_ci *   - wait for completion of write urb in release (needed for remotecontrol)
6562306a36Sopenharmony_ci *   - corrected poll for write direction (missing negation)
6662306a36Sopenharmony_ci * 2004-04-22 - 0.94 Juergen Stuber <starblue@users.sourceforge.net>
6762306a36Sopenharmony_ci *   - make device locking interruptible
6862306a36Sopenharmony_ci * 2004-04-30 - 0.95 Juergen Stuber <starblue@users.sourceforge.net>
6962306a36Sopenharmony_ci *   - check for valid udev on resubmitting and unlinking urbs
7062306a36Sopenharmony_ci * 2004-08-03 - 0.96 Juergen Stuber <starblue@users.sourceforge.net>
7162306a36Sopenharmony_ci *   - move reset into open to clean out spurious data
7262306a36Sopenharmony_ci */
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci#include <linux/kernel.h>
7762306a36Sopenharmony_ci#include <linux/errno.h>
7862306a36Sopenharmony_ci#include <linux/slab.h>
7962306a36Sopenharmony_ci#include <linux/module.h>
8062306a36Sopenharmony_ci#include <linux/completion.h>
8162306a36Sopenharmony_ci#include <linux/mutex.h>
8262306a36Sopenharmony_ci#include <linux/uaccess.h>
8362306a36Sopenharmony_ci#include <linux/usb.h>
8462306a36Sopenharmony_ci#include <linux/poll.h>
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci#define DRIVER_AUTHOR "Juergen Stuber <starblue@sourceforge.net>"
8862306a36Sopenharmony_ci#define DRIVER_DESC "LEGO USB Tower Driver"
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci/* The defaults are chosen to work with the latest versions of leJOS and NQC.
9262306a36Sopenharmony_ci */
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci/* Some legacy software likes to receive packets in one piece.
9562306a36Sopenharmony_ci * In this case read_buffer_size should exceed the maximal packet length
9662306a36Sopenharmony_ci * (417 for datalog uploads), and packet_timeout should be set.
9762306a36Sopenharmony_ci */
9862306a36Sopenharmony_cistatic int read_buffer_size = 480;
9962306a36Sopenharmony_cimodule_param(read_buffer_size, int, 0);
10062306a36Sopenharmony_ciMODULE_PARM_DESC(read_buffer_size, "Read buffer size");
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci/* Some legacy software likes to send packets in one piece.
10362306a36Sopenharmony_ci * In this case write_buffer_size should exceed the maximal packet length
10462306a36Sopenharmony_ci * (417 for firmware and program downloads).
10562306a36Sopenharmony_ci * A problem with long writes is that the following read may time out
10662306a36Sopenharmony_ci * if the software is not prepared to wait long enough.
10762306a36Sopenharmony_ci */
10862306a36Sopenharmony_cistatic int write_buffer_size = 480;
10962306a36Sopenharmony_cimodule_param(write_buffer_size, int, 0);
11062306a36Sopenharmony_ciMODULE_PARM_DESC(write_buffer_size, "Write buffer size");
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci/* Some legacy software expects reads to contain whole LASM packets.
11362306a36Sopenharmony_ci * To achieve this, characters which arrive before a packet timeout
11462306a36Sopenharmony_ci * occurs will be returned in a single read operation.
11562306a36Sopenharmony_ci * A problem with long reads is that the software may time out
11662306a36Sopenharmony_ci * if it is not prepared to wait long enough.
11762306a36Sopenharmony_ci * The packet timeout should be greater than the time between the
11862306a36Sopenharmony_ci * reception of subsequent characters, which should arrive about
11962306a36Sopenharmony_ci * every 5ms for the standard 2400 baud.
12062306a36Sopenharmony_ci * Set it to 0 to disable.
12162306a36Sopenharmony_ci */
12262306a36Sopenharmony_cistatic int packet_timeout = 50;
12362306a36Sopenharmony_cimodule_param(packet_timeout, int, 0);
12462306a36Sopenharmony_ciMODULE_PARM_DESC(packet_timeout, "Packet timeout in ms");
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci/* Some legacy software expects blocking reads to time out.
12762306a36Sopenharmony_ci * Timeout occurs after the specified time of read and write inactivity.
12862306a36Sopenharmony_ci * Set it to 0 to disable.
12962306a36Sopenharmony_ci */
13062306a36Sopenharmony_cistatic int read_timeout = 200;
13162306a36Sopenharmony_cimodule_param(read_timeout, int, 0);
13262306a36Sopenharmony_ciMODULE_PARM_DESC(read_timeout, "Read timeout in ms");
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci/* As of kernel version 2.6.4 ehci-hcd uses an
13562306a36Sopenharmony_ci * "only one interrupt transfer per frame" shortcut
13662306a36Sopenharmony_ci * to simplify the scheduling of periodic transfers.
13762306a36Sopenharmony_ci * This conflicts with our standard 1ms intervals for in and out URBs.
13862306a36Sopenharmony_ci * We use default intervals of 2ms for in and 8ms for out transfers,
13962306a36Sopenharmony_ci * which is fast enough for 2400 baud and allows a small additional load.
14062306a36Sopenharmony_ci * Increase the interval to allow more devices that do interrupt transfers,
14162306a36Sopenharmony_ci * or set to 0 to use the standard interval from the endpoint descriptors.
14262306a36Sopenharmony_ci */
14362306a36Sopenharmony_cistatic int interrupt_in_interval = 2;
14462306a36Sopenharmony_cimodule_param(interrupt_in_interval, int, 0);
14562306a36Sopenharmony_ciMODULE_PARM_DESC(interrupt_in_interval, "Interrupt in interval in ms");
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_cistatic int interrupt_out_interval = 8;
14862306a36Sopenharmony_cimodule_param(interrupt_out_interval, int, 0);
14962306a36Sopenharmony_ciMODULE_PARM_DESC(interrupt_out_interval, "Interrupt out interval in ms");
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci/* Define these values to match your device */
15262306a36Sopenharmony_ci#define LEGO_USB_TOWER_VENDOR_ID	0x0694
15362306a36Sopenharmony_ci#define LEGO_USB_TOWER_PRODUCT_ID	0x0001
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci/* Vendor requests */
15662306a36Sopenharmony_ci#define LEGO_USB_TOWER_REQUEST_RESET		0x04
15762306a36Sopenharmony_ci#define LEGO_USB_TOWER_REQUEST_GET_VERSION	0xFD
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_cistruct tower_reset_reply {
16062306a36Sopenharmony_ci	__le16 size;
16162306a36Sopenharmony_ci	__u8 err_code;
16262306a36Sopenharmony_ci	__u8 spare;
16362306a36Sopenharmony_ci};
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_cistruct tower_get_version_reply {
16662306a36Sopenharmony_ci	__le16 size;
16762306a36Sopenharmony_ci	__u8 err_code;
16862306a36Sopenharmony_ci	__u8 spare;
16962306a36Sopenharmony_ci	__u8 major;
17062306a36Sopenharmony_ci	__u8 minor;
17162306a36Sopenharmony_ci	__le16 build_no;
17262306a36Sopenharmony_ci};
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci/* table of devices that work with this driver */
17662306a36Sopenharmony_cistatic const struct usb_device_id tower_table[] = {
17762306a36Sopenharmony_ci	{ USB_DEVICE(LEGO_USB_TOWER_VENDOR_ID, LEGO_USB_TOWER_PRODUCT_ID) },
17862306a36Sopenharmony_ci	{ }					/* Terminating entry */
17962306a36Sopenharmony_ci};
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(usb, tower_table);
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci#define LEGO_USB_TOWER_MINOR_BASE	160
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci/* Structure to hold all of our device specific stuff */
18762306a36Sopenharmony_cistruct lego_usb_tower {
18862306a36Sopenharmony_ci	struct mutex		lock;		/* locks this structure */
18962306a36Sopenharmony_ci	struct usb_device	*udev;		/* save off the usb device pointer */
19062306a36Sopenharmony_ci	unsigned char		minor;		/* the starting minor number for this device */
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	int			open_count;	/* number of times this port has been opened */
19362306a36Sopenharmony_ci	unsigned long		disconnected:1;
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	char			*read_buffer;
19662306a36Sopenharmony_ci	size_t			read_buffer_length; /* this much came in */
19762306a36Sopenharmony_ci	size_t			read_packet_length; /* this much will be returned on read */
19862306a36Sopenharmony_ci	spinlock_t		read_buffer_lock;
19962306a36Sopenharmony_ci	int			packet_timeout_jiffies;
20062306a36Sopenharmony_ci	unsigned long		read_last_arrival;
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	wait_queue_head_t	read_wait;
20362306a36Sopenharmony_ci	wait_queue_head_t	write_wait;
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	char			*interrupt_in_buffer;
20662306a36Sopenharmony_ci	struct usb_endpoint_descriptor *interrupt_in_endpoint;
20762306a36Sopenharmony_ci	struct urb		*interrupt_in_urb;
20862306a36Sopenharmony_ci	int			interrupt_in_interval;
20962306a36Sopenharmony_ci	int			interrupt_in_done;
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	char			*interrupt_out_buffer;
21262306a36Sopenharmony_ci	struct usb_endpoint_descriptor *interrupt_out_endpoint;
21362306a36Sopenharmony_ci	struct urb		*interrupt_out_urb;
21462306a36Sopenharmony_ci	int			interrupt_out_interval;
21562306a36Sopenharmony_ci	int			interrupt_out_busy;
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci};
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci/* local function prototypes */
22162306a36Sopenharmony_cistatic ssize_t tower_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos);
22262306a36Sopenharmony_cistatic ssize_t tower_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos);
22362306a36Sopenharmony_cistatic inline void tower_delete(struct lego_usb_tower *dev);
22462306a36Sopenharmony_cistatic int tower_open(struct inode *inode, struct file *file);
22562306a36Sopenharmony_cistatic int tower_release(struct inode *inode, struct file *file);
22662306a36Sopenharmony_cistatic __poll_t tower_poll(struct file *file, poll_table *wait);
22762306a36Sopenharmony_cistatic loff_t tower_llseek(struct file *file, loff_t off, int whence);
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_cistatic void tower_check_for_read_packet(struct lego_usb_tower *dev);
23062306a36Sopenharmony_cistatic void tower_interrupt_in_callback(struct urb *urb);
23162306a36Sopenharmony_cistatic void tower_interrupt_out_callback(struct urb *urb);
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_cistatic int  tower_probe(struct usb_interface *interface, const struct usb_device_id *id);
23462306a36Sopenharmony_cistatic void tower_disconnect(struct usb_interface *interface);
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci/* file operations needed when we register this driver */
23862306a36Sopenharmony_cistatic const struct file_operations tower_fops = {
23962306a36Sopenharmony_ci	.owner =	THIS_MODULE,
24062306a36Sopenharmony_ci	.read  =	tower_read,
24162306a36Sopenharmony_ci	.write =	tower_write,
24262306a36Sopenharmony_ci	.open =		tower_open,
24362306a36Sopenharmony_ci	.release =	tower_release,
24462306a36Sopenharmony_ci	.poll =		tower_poll,
24562306a36Sopenharmony_ci	.llseek =	tower_llseek,
24662306a36Sopenharmony_ci};
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_cistatic char *legousbtower_devnode(const struct device *dev, umode_t *mode)
24962306a36Sopenharmony_ci{
25062306a36Sopenharmony_ci	return kasprintf(GFP_KERNEL, "usb/%s", dev_name(dev));
25162306a36Sopenharmony_ci}
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci/*
25462306a36Sopenharmony_ci * usb class driver info in order to get a minor number from the usb core,
25562306a36Sopenharmony_ci * and to have the device registered with the driver core
25662306a36Sopenharmony_ci */
25762306a36Sopenharmony_cistatic struct usb_class_driver tower_class = {
25862306a36Sopenharmony_ci	.name =		"legousbtower%d",
25962306a36Sopenharmony_ci	.devnode = 	legousbtower_devnode,
26062306a36Sopenharmony_ci	.fops =		&tower_fops,
26162306a36Sopenharmony_ci	.minor_base =	LEGO_USB_TOWER_MINOR_BASE,
26262306a36Sopenharmony_ci};
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci/* usb specific object needed to register this driver with the usb subsystem */
26662306a36Sopenharmony_cistatic struct usb_driver tower_driver = {
26762306a36Sopenharmony_ci	.name =		"legousbtower",
26862306a36Sopenharmony_ci	.probe =	tower_probe,
26962306a36Sopenharmony_ci	.disconnect =	tower_disconnect,
27062306a36Sopenharmony_ci	.id_table =	tower_table,
27162306a36Sopenharmony_ci};
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci/*
27562306a36Sopenharmony_ci *	lego_usb_tower_debug_data
27662306a36Sopenharmony_ci */
27762306a36Sopenharmony_cistatic inline void lego_usb_tower_debug_data(struct device *dev,
27862306a36Sopenharmony_ci					     const char *function, int size,
27962306a36Sopenharmony_ci					     const unsigned char *data)
28062306a36Sopenharmony_ci{
28162306a36Sopenharmony_ci	dev_dbg(dev, "%s - length = %d, data = %*ph\n",
28262306a36Sopenharmony_ci		function, size, size, data);
28362306a36Sopenharmony_ci}
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci/*
28762306a36Sopenharmony_ci *	tower_delete
28862306a36Sopenharmony_ci */
28962306a36Sopenharmony_cistatic inline void tower_delete(struct lego_usb_tower *dev)
29062306a36Sopenharmony_ci{
29162306a36Sopenharmony_ci	/* free data structures */
29262306a36Sopenharmony_ci	usb_free_urb(dev->interrupt_in_urb);
29362306a36Sopenharmony_ci	usb_free_urb(dev->interrupt_out_urb);
29462306a36Sopenharmony_ci	kfree(dev->read_buffer);
29562306a36Sopenharmony_ci	kfree(dev->interrupt_in_buffer);
29662306a36Sopenharmony_ci	kfree(dev->interrupt_out_buffer);
29762306a36Sopenharmony_ci	usb_put_dev(dev->udev);
29862306a36Sopenharmony_ci	kfree(dev);
29962306a36Sopenharmony_ci}
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci/*
30362306a36Sopenharmony_ci *	tower_open
30462306a36Sopenharmony_ci */
30562306a36Sopenharmony_cistatic int tower_open(struct inode *inode, struct file *file)
30662306a36Sopenharmony_ci{
30762306a36Sopenharmony_ci	struct lego_usb_tower *dev = NULL;
30862306a36Sopenharmony_ci	int subminor;
30962306a36Sopenharmony_ci	int retval = 0;
31062306a36Sopenharmony_ci	struct usb_interface *interface;
31162306a36Sopenharmony_ci	struct tower_reset_reply reset_reply;
31262306a36Sopenharmony_ci	int result;
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	nonseekable_open(inode, file);
31562306a36Sopenharmony_ci	subminor = iminor(inode);
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci	interface = usb_find_interface(&tower_driver, subminor);
31862306a36Sopenharmony_ci	if (!interface) {
31962306a36Sopenharmony_ci		pr_err("error, can't find device for minor %d\n", subminor);
32062306a36Sopenharmony_ci		retval = -ENODEV;
32162306a36Sopenharmony_ci		goto exit;
32262306a36Sopenharmony_ci	}
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	dev = usb_get_intfdata(interface);
32562306a36Sopenharmony_ci	if (!dev) {
32662306a36Sopenharmony_ci		retval = -ENODEV;
32762306a36Sopenharmony_ci		goto exit;
32862306a36Sopenharmony_ci	}
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	/* lock this device */
33162306a36Sopenharmony_ci	if (mutex_lock_interruptible(&dev->lock)) {
33262306a36Sopenharmony_ci	        retval = -ERESTARTSYS;
33362306a36Sopenharmony_ci		goto exit;
33462306a36Sopenharmony_ci	}
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	/* allow opening only once */
33862306a36Sopenharmony_ci	if (dev->open_count) {
33962306a36Sopenharmony_ci		retval = -EBUSY;
34062306a36Sopenharmony_ci		goto unlock_exit;
34162306a36Sopenharmony_ci	}
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	/* reset the tower */
34462306a36Sopenharmony_ci	result = usb_control_msg_recv(dev->udev, 0,
34562306a36Sopenharmony_ci				      LEGO_USB_TOWER_REQUEST_RESET,
34662306a36Sopenharmony_ci				      USB_TYPE_VENDOR | USB_DIR_IN | USB_RECIP_DEVICE,
34762306a36Sopenharmony_ci				      0, 0,
34862306a36Sopenharmony_ci				      &reset_reply, sizeof(reset_reply), 1000,
34962306a36Sopenharmony_ci				      GFP_KERNEL);
35062306a36Sopenharmony_ci	if (result < 0) {
35162306a36Sopenharmony_ci		dev_err(&dev->udev->dev,
35262306a36Sopenharmony_ci			"LEGO USB Tower reset control request failed\n");
35362306a36Sopenharmony_ci		retval = result;
35462306a36Sopenharmony_ci		goto unlock_exit;
35562306a36Sopenharmony_ci	}
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	/* initialize in direction */
35862306a36Sopenharmony_ci	dev->read_buffer_length = 0;
35962306a36Sopenharmony_ci	dev->read_packet_length = 0;
36062306a36Sopenharmony_ci	usb_fill_int_urb(dev->interrupt_in_urb,
36162306a36Sopenharmony_ci			 dev->udev,
36262306a36Sopenharmony_ci			 usb_rcvintpipe(dev->udev, dev->interrupt_in_endpoint->bEndpointAddress),
36362306a36Sopenharmony_ci			 dev->interrupt_in_buffer,
36462306a36Sopenharmony_ci			 usb_endpoint_maxp(dev->interrupt_in_endpoint),
36562306a36Sopenharmony_ci			 tower_interrupt_in_callback,
36662306a36Sopenharmony_ci			 dev,
36762306a36Sopenharmony_ci			 dev->interrupt_in_interval);
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci	dev->interrupt_in_done = 0;
37062306a36Sopenharmony_ci	mb();
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci	retval = usb_submit_urb(dev->interrupt_in_urb, GFP_KERNEL);
37362306a36Sopenharmony_ci	if (retval) {
37462306a36Sopenharmony_ci		dev_err(&dev->udev->dev,
37562306a36Sopenharmony_ci			"Couldn't submit interrupt_in_urb %d\n", retval);
37662306a36Sopenharmony_ci		goto unlock_exit;
37762306a36Sopenharmony_ci	}
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	/* save device in the file's private structure */
38062306a36Sopenharmony_ci	file->private_data = dev;
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci	dev->open_count = 1;
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ciunlock_exit:
38562306a36Sopenharmony_ci	mutex_unlock(&dev->lock);
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ciexit:
38862306a36Sopenharmony_ci	return retval;
38962306a36Sopenharmony_ci}
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci/*
39262306a36Sopenharmony_ci *	tower_release
39362306a36Sopenharmony_ci */
39462306a36Sopenharmony_cistatic int tower_release(struct inode *inode, struct file *file)
39562306a36Sopenharmony_ci{
39662306a36Sopenharmony_ci	struct lego_usb_tower *dev;
39762306a36Sopenharmony_ci	int retval = 0;
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci	dev = file->private_data;
40062306a36Sopenharmony_ci	if (dev == NULL) {
40162306a36Sopenharmony_ci		retval = -ENODEV;
40262306a36Sopenharmony_ci		goto exit;
40362306a36Sopenharmony_ci	}
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	mutex_lock(&dev->lock);
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	if (dev->disconnected) {
40862306a36Sopenharmony_ci		/* the device was unplugged before the file was released */
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci		/* unlock here as tower_delete frees dev */
41162306a36Sopenharmony_ci		mutex_unlock(&dev->lock);
41262306a36Sopenharmony_ci		tower_delete(dev);
41362306a36Sopenharmony_ci		goto exit;
41462306a36Sopenharmony_ci	}
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci	/* wait until write transfer is finished */
41762306a36Sopenharmony_ci	if (dev->interrupt_out_busy) {
41862306a36Sopenharmony_ci		wait_event_interruptible_timeout(dev->write_wait, !dev->interrupt_out_busy,
41962306a36Sopenharmony_ci						 2 * HZ);
42062306a36Sopenharmony_ci	}
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci	/* shutdown transfers */
42362306a36Sopenharmony_ci	usb_kill_urb(dev->interrupt_in_urb);
42462306a36Sopenharmony_ci	usb_kill_urb(dev->interrupt_out_urb);
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci	dev->open_count = 0;
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	mutex_unlock(&dev->lock);
42962306a36Sopenharmony_ciexit:
43062306a36Sopenharmony_ci	return retval;
43162306a36Sopenharmony_ci}
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci/*
43462306a36Sopenharmony_ci *	tower_check_for_read_packet
43562306a36Sopenharmony_ci *
43662306a36Sopenharmony_ci *      To get correct semantics for signals and non-blocking I/O
43762306a36Sopenharmony_ci *      with packetizing we pretend not to see any data in the read buffer
43862306a36Sopenharmony_ci *      until it has been there unchanged for at least
43962306a36Sopenharmony_ci *      dev->packet_timeout_jiffies, or until the buffer is full.
44062306a36Sopenharmony_ci */
44162306a36Sopenharmony_cistatic void tower_check_for_read_packet(struct lego_usb_tower *dev)
44262306a36Sopenharmony_ci{
44362306a36Sopenharmony_ci	spin_lock_irq(&dev->read_buffer_lock);
44462306a36Sopenharmony_ci	if (!packet_timeout
44562306a36Sopenharmony_ci	    || time_after(jiffies, dev->read_last_arrival + dev->packet_timeout_jiffies)
44662306a36Sopenharmony_ci	    || dev->read_buffer_length == read_buffer_size) {
44762306a36Sopenharmony_ci		dev->read_packet_length = dev->read_buffer_length;
44862306a36Sopenharmony_ci	}
44962306a36Sopenharmony_ci	dev->interrupt_in_done = 0;
45062306a36Sopenharmony_ci	spin_unlock_irq(&dev->read_buffer_lock);
45162306a36Sopenharmony_ci}
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci/*
45562306a36Sopenharmony_ci *	tower_poll
45662306a36Sopenharmony_ci */
45762306a36Sopenharmony_cistatic __poll_t tower_poll(struct file *file, poll_table *wait)
45862306a36Sopenharmony_ci{
45962306a36Sopenharmony_ci	struct lego_usb_tower *dev;
46062306a36Sopenharmony_ci	__poll_t mask = 0;
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci	dev = file->private_data;
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci	if (dev->disconnected)
46562306a36Sopenharmony_ci		return EPOLLERR | EPOLLHUP;
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	poll_wait(file, &dev->read_wait, wait);
46862306a36Sopenharmony_ci	poll_wait(file, &dev->write_wait, wait);
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci	tower_check_for_read_packet(dev);
47162306a36Sopenharmony_ci	if (dev->read_packet_length > 0)
47262306a36Sopenharmony_ci		mask |= EPOLLIN | EPOLLRDNORM;
47362306a36Sopenharmony_ci	if (!dev->interrupt_out_busy)
47462306a36Sopenharmony_ci		mask |= EPOLLOUT | EPOLLWRNORM;
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci	return mask;
47762306a36Sopenharmony_ci}
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci/*
48162306a36Sopenharmony_ci *	tower_llseek
48262306a36Sopenharmony_ci */
48362306a36Sopenharmony_cistatic loff_t tower_llseek(struct file *file, loff_t off, int whence)
48462306a36Sopenharmony_ci{
48562306a36Sopenharmony_ci	return -ESPIPE;		/* unseekable */
48662306a36Sopenharmony_ci}
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci/*
49062306a36Sopenharmony_ci *	tower_read
49162306a36Sopenharmony_ci */
49262306a36Sopenharmony_cistatic ssize_t tower_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
49362306a36Sopenharmony_ci{
49462306a36Sopenharmony_ci	struct lego_usb_tower *dev;
49562306a36Sopenharmony_ci	size_t bytes_to_read;
49662306a36Sopenharmony_ci	int i;
49762306a36Sopenharmony_ci	int retval = 0;
49862306a36Sopenharmony_ci	unsigned long timeout = 0;
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci	dev = file->private_data;
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci	/* lock this object */
50362306a36Sopenharmony_ci	if (mutex_lock_interruptible(&dev->lock)) {
50462306a36Sopenharmony_ci		retval = -ERESTARTSYS;
50562306a36Sopenharmony_ci		goto exit;
50662306a36Sopenharmony_ci	}
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ci	/* verify that the device wasn't unplugged */
50962306a36Sopenharmony_ci	if (dev->disconnected) {
51062306a36Sopenharmony_ci		retval = -ENODEV;
51162306a36Sopenharmony_ci		goto unlock_exit;
51262306a36Sopenharmony_ci	}
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci	/* verify that we actually have some data to read */
51562306a36Sopenharmony_ci	if (count == 0) {
51662306a36Sopenharmony_ci		dev_dbg(&dev->udev->dev, "read request of 0 bytes\n");
51762306a36Sopenharmony_ci		goto unlock_exit;
51862306a36Sopenharmony_ci	}
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci	if (read_timeout)
52162306a36Sopenharmony_ci		timeout = jiffies + msecs_to_jiffies(read_timeout);
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci	/* wait for data */
52462306a36Sopenharmony_ci	tower_check_for_read_packet(dev);
52562306a36Sopenharmony_ci	while (dev->read_packet_length == 0) {
52662306a36Sopenharmony_ci		if (file->f_flags & O_NONBLOCK) {
52762306a36Sopenharmony_ci			retval = -EAGAIN;
52862306a36Sopenharmony_ci			goto unlock_exit;
52962306a36Sopenharmony_ci		}
53062306a36Sopenharmony_ci		retval = wait_event_interruptible_timeout(dev->read_wait, dev->interrupt_in_done, dev->packet_timeout_jiffies);
53162306a36Sopenharmony_ci		if (retval < 0)
53262306a36Sopenharmony_ci			goto unlock_exit;
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci		/* reset read timeout during read or write activity */
53562306a36Sopenharmony_ci		if (read_timeout
53662306a36Sopenharmony_ci		    && (dev->read_buffer_length || dev->interrupt_out_busy)) {
53762306a36Sopenharmony_ci			timeout = jiffies + msecs_to_jiffies(read_timeout);
53862306a36Sopenharmony_ci		}
53962306a36Sopenharmony_ci		/* check for read timeout */
54062306a36Sopenharmony_ci		if (read_timeout && time_after(jiffies, timeout)) {
54162306a36Sopenharmony_ci			retval = -ETIMEDOUT;
54262306a36Sopenharmony_ci			goto unlock_exit;
54362306a36Sopenharmony_ci		}
54462306a36Sopenharmony_ci		tower_check_for_read_packet(dev);
54562306a36Sopenharmony_ci	}
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci	/* copy the data from read_buffer into userspace */
54862306a36Sopenharmony_ci	bytes_to_read = min(count, dev->read_packet_length);
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci	if (copy_to_user(buffer, dev->read_buffer, bytes_to_read)) {
55162306a36Sopenharmony_ci		retval = -EFAULT;
55262306a36Sopenharmony_ci		goto unlock_exit;
55362306a36Sopenharmony_ci	}
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci	spin_lock_irq(&dev->read_buffer_lock);
55662306a36Sopenharmony_ci	dev->read_buffer_length -= bytes_to_read;
55762306a36Sopenharmony_ci	dev->read_packet_length -= bytes_to_read;
55862306a36Sopenharmony_ci	for (i = 0; i < dev->read_buffer_length; i++)
55962306a36Sopenharmony_ci		dev->read_buffer[i] = dev->read_buffer[i+bytes_to_read];
56062306a36Sopenharmony_ci	spin_unlock_irq(&dev->read_buffer_lock);
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci	retval = bytes_to_read;
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ciunlock_exit:
56562306a36Sopenharmony_ci	/* unlock the device */
56662306a36Sopenharmony_ci	mutex_unlock(&dev->lock);
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ciexit:
56962306a36Sopenharmony_ci	return retval;
57062306a36Sopenharmony_ci}
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci/*
57462306a36Sopenharmony_ci *	tower_write
57562306a36Sopenharmony_ci */
57662306a36Sopenharmony_cistatic ssize_t tower_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos)
57762306a36Sopenharmony_ci{
57862306a36Sopenharmony_ci	struct lego_usb_tower *dev;
57962306a36Sopenharmony_ci	size_t bytes_to_write;
58062306a36Sopenharmony_ci	int retval = 0;
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_ci	dev = file->private_data;
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_ci	/* lock this object */
58562306a36Sopenharmony_ci	if (mutex_lock_interruptible(&dev->lock)) {
58662306a36Sopenharmony_ci		retval = -ERESTARTSYS;
58762306a36Sopenharmony_ci		goto exit;
58862306a36Sopenharmony_ci	}
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ci	/* verify that the device wasn't unplugged */
59162306a36Sopenharmony_ci	if (dev->disconnected) {
59262306a36Sopenharmony_ci		retval = -ENODEV;
59362306a36Sopenharmony_ci		goto unlock_exit;
59462306a36Sopenharmony_ci	}
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ci	/* verify that we actually have some data to write */
59762306a36Sopenharmony_ci	if (count == 0) {
59862306a36Sopenharmony_ci		dev_dbg(&dev->udev->dev, "write request of 0 bytes\n");
59962306a36Sopenharmony_ci		goto unlock_exit;
60062306a36Sopenharmony_ci	}
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_ci	/* wait until previous transfer is finished */
60362306a36Sopenharmony_ci	while (dev->interrupt_out_busy) {
60462306a36Sopenharmony_ci		if (file->f_flags & O_NONBLOCK) {
60562306a36Sopenharmony_ci			retval = -EAGAIN;
60662306a36Sopenharmony_ci			goto unlock_exit;
60762306a36Sopenharmony_ci		}
60862306a36Sopenharmony_ci		retval = wait_event_interruptible(dev->write_wait,
60962306a36Sopenharmony_ci						  !dev->interrupt_out_busy);
61062306a36Sopenharmony_ci		if (retval)
61162306a36Sopenharmony_ci			goto unlock_exit;
61262306a36Sopenharmony_ci	}
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ci	/* write the data into interrupt_out_buffer from userspace */
61562306a36Sopenharmony_ci	bytes_to_write = min_t(int, count, write_buffer_size);
61662306a36Sopenharmony_ci	dev_dbg(&dev->udev->dev, "%s: count = %zd, bytes_to_write = %zd\n",
61762306a36Sopenharmony_ci		__func__, count, bytes_to_write);
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_ci	if (copy_from_user(dev->interrupt_out_buffer, buffer, bytes_to_write)) {
62062306a36Sopenharmony_ci		retval = -EFAULT;
62162306a36Sopenharmony_ci		goto unlock_exit;
62262306a36Sopenharmony_ci	}
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_ci	/* send off the urb */
62562306a36Sopenharmony_ci	usb_fill_int_urb(dev->interrupt_out_urb,
62662306a36Sopenharmony_ci			 dev->udev,
62762306a36Sopenharmony_ci			 usb_sndintpipe(dev->udev, dev->interrupt_out_endpoint->bEndpointAddress),
62862306a36Sopenharmony_ci			 dev->interrupt_out_buffer,
62962306a36Sopenharmony_ci			 bytes_to_write,
63062306a36Sopenharmony_ci			 tower_interrupt_out_callback,
63162306a36Sopenharmony_ci			 dev,
63262306a36Sopenharmony_ci			 dev->interrupt_out_interval);
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_ci	dev->interrupt_out_busy = 1;
63562306a36Sopenharmony_ci	wmb();
63662306a36Sopenharmony_ci
63762306a36Sopenharmony_ci	retval = usb_submit_urb(dev->interrupt_out_urb, GFP_KERNEL);
63862306a36Sopenharmony_ci	if (retval) {
63962306a36Sopenharmony_ci		dev->interrupt_out_busy = 0;
64062306a36Sopenharmony_ci		dev_err(&dev->udev->dev,
64162306a36Sopenharmony_ci			"Couldn't submit interrupt_out_urb %d\n", retval);
64262306a36Sopenharmony_ci		goto unlock_exit;
64362306a36Sopenharmony_ci	}
64462306a36Sopenharmony_ci	retval = bytes_to_write;
64562306a36Sopenharmony_ci
64662306a36Sopenharmony_ciunlock_exit:
64762306a36Sopenharmony_ci	/* unlock the device */
64862306a36Sopenharmony_ci	mutex_unlock(&dev->lock);
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ciexit:
65162306a36Sopenharmony_ci	return retval;
65262306a36Sopenharmony_ci}
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_ci/*
65662306a36Sopenharmony_ci *	tower_interrupt_in_callback
65762306a36Sopenharmony_ci */
65862306a36Sopenharmony_cistatic void tower_interrupt_in_callback(struct urb *urb)
65962306a36Sopenharmony_ci{
66062306a36Sopenharmony_ci	struct lego_usb_tower *dev = urb->context;
66162306a36Sopenharmony_ci	int status = urb->status;
66262306a36Sopenharmony_ci	int retval;
66362306a36Sopenharmony_ci	unsigned long flags;
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_ci	lego_usb_tower_debug_data(&dev->udev->dev, __func__,
66662306a36Sopenharmony_ci				  urb->actual_length, urb->transfer_buffer);
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_ci	if (status) {
66962306a36Sopenharmony_ci		if (status == -ENOENT ||
67062306a36Sopenharmony_ci		    status == -ECONNRESET ||
67162306a36Sopenharmony_ci		    status == -ESHUTDOWN) {
67262306a36Sopenharmony_ci			goto exit;
67362306a36Sopenharmony_ci		} else {
67462306a36Sopenharmony_ci			dev_dbg(&dev->udev->dev,
67562306a36Sopenharmony_ci				"%s: nonzero status received: %d\n", __func__,
67662306a36Sopenharmony_ci				status);
67762306a36Sopenharmony_ci			goto resubmit; /* maybe we can recover */
67862306a36Sopenharmony_ci		}
67962306a36Sopenharmony_ci	}
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci	if (urb->actual_length > 0) {
68262306a36Sopenharmony_ci		spin_lock_irqsave(&dev->read_buffer_lock, flags);
68362306a36Sopenharmony_ci		if (dev->read_buffer_length + urb->actual_length < read_buffer_size) {
68462306a36Sopenharmony_ci			memcpy(dev->read_buffer + dev->read_buffer_length,
68562306a36Sopenharmony_ci			       dev->interrupt_in_buffer,
68662306a36Sopenharmony_ci			       urb->actual_length);
68762306a36Sopenharmony_ci			dev->read_buffer_length += urb->actual_length;
68862306a36Sopenharmony_ci			dev->read_last_arrival = jiffies;
68962306a36Sopenharmony_ci			dev_dbg(&dev->udev->dev, "%s: received %d bytes\n",
69062306a36Sopenharmony_ci				__func__, urb->actual_length);
69162306a36Sopenharmony_ci		} else {
69262306a36Sopenharmony_ci			pr_warn("read_buffer overflow, %d bytes dropped\n",
69362306a36Sopenharmony_ci				urb->actual_length);
69462306a36Sopenharmony_ci		}
69562306a36Sopenharmony_ci		spin_unlock_irqrestore(&dev->read_buffer_lock, flags);
69662306a36Sopenharmony_ci	}
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_ciresubmit:
69962306a36Sopenharmony_ci	retval = usb_submit_urb(dev->interrupt_in_urb, GFP_ATOMIC);
70062306a36Sopenharmony_ci	if (retval) {
70162306a36Sopenharmony_ci		dev_err(&dev->udev->dev, "%s: usb_submit_urb failed (%d)\n",
70262306a36Sopenharmony_ci			__func__, retval);
70362306a36Sopenharmony_ci	}
70462306a36Sopenharmony_ciexit:
70562306a36Sopenharmony_ci	dev->interrupt_in_done = 1;
70662306a36Sopenharmony_ci	wake_up_interruptible(&dev->read_wait);
70762306a36Sopenharmony_ci}
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_ci/*
71162306a36Sopenharmony_ci *	tower_interrupt_out_callback
71262306a36Sopenharmony_ci */
71362306a36Sopenharmony_cistatic void tower_interrupt_out_callback(struct urb *urb)
71462306a36Sopenharmony_ci{
71562306a36Sopenharmony_ci	struct lego_usb_tower *dev = urb->context;
71662306a36Sopenharmony_ci	int status = urb->status;
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_ci	lego_usb_tower_debug_data(&dev->udev->dev, __func__,
71962306a36Sopenharmony_ci				  urb->actual_length, urb->transfer_buffer);
72062306a36Sopenharmony_ci
72162306a36Sopenharmony_ci	/* sync/async unlink faults aren't errors */
72262306a36Sopenharmony_ci	if (status && !(status == -ENOENT ||
72362306a36Sopenharmony_ci			status == -ECONNRESET ||
72462306a36Sopenharmony_ci			status == -ESHUTDOWN)) {
72562306a36Sopenharmony_ci		dev_dbg(&dev->udev->dev,
72662306a36Sopenharmony_ci			"%s: nonzero write bulk status received: %d\n", __func__,
72762306a36Sopenharmony_ci			status);
72862306a36Sopenharmony_ci	}
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_ci	dev->interrupt_out_busy = 0;
73162306a36Sopenharmony_ci	wake_up_interruptible(&dev->write_wait);
73262306a36Sopenharmony_ci}
73362306a36Sopenharmony_ci
73462306a36Sopenharmony_ci
73562306a36Sopenharmony_ci/*
73662306a36Sopenharmony_ci *	tower_probe
73762306a36Sopenharmony_ci *
73862306a36Sopenharmony_ci *	Called by the usb core when a new device is connected that it thinks
73962306a36Sopenharmony_ci *	this driver might be interested in.
74062306a36Sopenharmony_ci */
74162306a36Sopenharmony_cistatic int tower_probe(struct usb_interface *interface, const struct usb_device_id *id)
74262306a36Sopenharmony_ci{
74362306a36Sopenharmony_ci	struct device *idev = &interface->dev;
74462306a36Sopenharmony_ci	struct usb_device *udev = interface_to_usbdev(interface);
74562306a36Sopenharmony_ci	struct lego_usb_tower *dev;
74662306a36Sopenharmony_ci	struct tower_get_version_reply get_version_reply;
74762306a36Sopenharmony_ci	int retval = -ENOMEM;
74862306a36Sopenharmony_ci	int result;
74962306a36Sopenharmony_ci
75062306a36Sopenharmony_ci	/* allocate memory for our device state and initialize it */
75162306a36Sopenharmony_ci	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
75262306a36Sopenharmony_ci	if (!dev)
75362306a36Sopenharmony_ci		goto exit;
75462306a36Sopenharmony_ci
75562306a36Sopenharmony_ci	mutex_init(&dev->lock);
75662306a36Sopenharmony_ci	dev->udev = usb_get_dev(udev);
75762306a36Sopenharmony_ci	spin_lock_init(&dev->read_buffer_lock);
75862306a36Sopenharmony_ci	dev->packet_timeout_jiffies = msecs_to_jiffies(packet_timeout);
75962306a36Sopenharmony_ci	dev->read_last_arrival = jiffies;
76062306a36Sopenharmony_ci	init_waitqueue_head(&dev->read_wait);
76162306a36Sopenharmony_ci	init_waitqueue_head(&dev->write_wait);
76262306a36Sopenharmony_ci
76362306a36Sopenharmony_ci	result = usb_find_common_endpoints_reverse(interface->cur_altsetting,
76462306a36Sopenharmony_ci			NULL, NULL,
76562306a36Sopenharmony_ci			&dev->interrupt_in_endpoint,
76662306a36Sopenharmony_ci			&dev->interrupt_out_endpoint);
76762306a36Sopenharmony_ci	if (result) {
76862306a36Sopenharmony_ci		dev_err(idev, "interrupt endpoints not found\n");
76962306a36Sopenharmony_ci		retval = result;
77062306a36Sopenharmony_ci		goto error;
77162306a36Sopenharmony_ci	}
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_ci	dev->read_buffer = kmalloc(read_buffer_size, GFP_KERNEL);
77462306a36Sopenharmony_ci	if (!dev->read_buffer)
77562306a36Sopenharmony_ci		goto error;
77662306a36Sopenharmony_ci	dev->interrupt_in_buffer = kmalloc(usb_endpoint_maxp(dev->interrupt_in_endpoint), GFP_KERNEL);
77762306a36Sopenharmony_ci	if (!dev->interrupt_in_buffer)
77862306a36Sopenharmony_ci		goto error;
77962306a36Sopenharmony_ci	dev->interrupt_in_urb = usb_alloc_urb(0, GFP_KERNEL);
78062306a36Sopenharmony_ci	if (!dev->interrupt_in_urb)
78162306a36Sopenharmony_ci		goto error;
78262306a36Sopenharmony_ci	dev->interrupt_out_buffer = kmalloc(write_buffer_size, GFP_KERNEL);
78362306a36Sopenharmony_ci	if (!dev->interrupt_out_buffer)
78462306a36Sopenharmony_ci		goto error;
78562306a36Sopenharmony_ci	dev->interrupt_out_urb = usb_alloc_urb(0, GFP_KERNEL);
78662306a36Sopenharmony_ci	if (!dev->interrupt_out_urb)
78762306a36Sopenharmony_ci		goto error;
78862306a36Sopenharmony_ci	dev->interrupt_in_interval = interrupt_in_interval ? interrupt_in_interval : dev->interrupt_in_endpoint->bInterval;
78962306a36Sopenharmony_ci	dev->interrupt_out_interval = interrupt_out_interval ? interrupt_out_interval : dev->interrupt_out_endpoint->bInterval;
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_ci	/* get the firmware version and log it */
79262306a36Sopenharmony_ci	result = usb_control_msg_recv(udev, 0,
79362306a36Sopenharmony_ci				      LEGO_USB_TOWER_REQUEST_GET_VERSION,
79462306a36Sopenharmony_ci				      USB_TYPE_VENDOR | USB_DIR_IN | USB_RECIP_DEVICE,
79562306a36Sopenharmony_ci				      0,
79662306a36Sopenharmony_ci				      0,
79762306a36Sopenharmony_ci				      &get_version_reply,
79862306a36Sopenharmony_ci				      sizeof(get_version_reply),
79962306a36Sopenharmony_ci				      1000, GFP_KERNEL);
80062306a36Sopenharmony_ci	if (result) {
80162306a36Sopenharmony_ci		dev_err(idev, "get version request failed: %d\n", result);
80262306a36Sopenharmony_ci		retval = result;
80362306a36Sopenharmony_ci		goto error;
80462306a36Sopenharmony_ci	}
80562306a36Sopenharmony_ci	dev_info(&interface->dev,
80662306a36Sopenharmony_ci		 "LEGO USB Tower firmware version is %d.%d build %d\n",
80762306a36Sopenharmony_ci		 get_version_reply.major,
80862306a36Sopenharmony_ci		 get_version_reply.minor,
80962306a36Sopenharmony_ci		 le16_to_cpu(get_version_reply.build_no));
81062306a36Sopenharmony_ci
81162306a36Sopenharmony_ci	/* we can register the device now, as it is ready */
81262306a36Sopenharmony_ci	usb_set_intfdata(interface, dev);
81362306a36Sopenharmony_ci
81462306a36Sopenharmony_ci	retval = usb_register_dev(interface, &tower_class);
81562306a36Sopenharmony_ci	if (retval) {
81662306a36Sopenharmony_ci		/* something prevented us from registering this driver */
81762306a36Sopenharmony_ci		dev_err(idev, "Not able to get a minor for this device.\n");
81862306a36Sopenharmony_ci		goto error;
81962306a36Sopenharmony_ci	}
82062306a36Sopenharmony_ci	dev->minor = interface->minor;
82162306a36Sopenharmony_ci
82262306a36Sopenharmony_ci	/* let the user know what node this device is now attached to */
82362306a36Sopenharmony_ci	dev_info(&interface->dev, "LEGO USB Tower #%d now attached to major "
82462306a36Sopenharmony_ci		 "%d minor %d\n", (dev->minor - LEGO_USB_TOWER_MINOR_BASE),
82562306a36Sopenharmony_ci		 USB_MAJOR, dev->minor);
82662306a36Sopenharmony_ci
82762306a36Sopenharmony_ciexit:
82862306a36Sopenharmony_ci	return retval;
82962306a36Sopenharmony_ci
83062306a36Sopenharmony_cierror:
83162306a36Sopenharmony_ci	tower_delete(dev);
83262306a36Sopenharmony_ci	return retval;
83362306a36Sopenharmony_ci}
83462306a36Sopenharmony_ci
83562306a36Sopenharmony_ci
83662306a36Sopenharmony_ci/*
83762306a36Sopenharmony_ci *	tower_disconnect
83862306a36Sopenharmony_ci *
83962306a36Sopenharmony_ci *	Called by the usb core when the device is removed from the system.
84062306a36Sopenharmony_ci */
84162306a36Sopenharmony_cistatic void tower_disconnect(struct usb_interface *interface)
84262306a36Sopenharmony_ci{
84362306a36Sopenharmony_ci	struct lego_usb_tower *dev;
84462306a36Sopenharmony_ci	int minor;
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_ci	dev = usb_get_intfdata(interface);
84762306a36Sopenharmony_ci
84862306a36Sopenharmony_ci	minor = dev->minor;
84962306a36Sopenharmony_ci
85062306a36Sopenharmony_ci	/* give back our minor and prevent further open() */
85162306a36Sopenharmony_ci	usb_deregister_dev(interface, &tower_class);
85262306a36Sopenharmony_ci
85362306a36Sopenharmony_ci	/* stop I/O */
85462306a36Sopenharmony_ci	usb_poison_urb(dev->interrupt_in_urb);
85562306a36Sopenharmony_ci	usb_poison_urb(dev->interrupt_out_urb);
85662306a36Sopenharmony_ci
85762306a36Sopenharmony_ci	mutex_lock(&dev->lock);
85862306a36Sopenharmony_ci
85962306a36Sopenharmony_ci	/* if the device is not opened, then we clean up right now */
86062306a36Sopenharmony_ci	if (!dev->open_count) {
86162306a36Sopenharmony_ci		mutex_unlock(&dev->lock);
86262306a36Sopenharmony_ci		tower_delete(dev);
86362306a36Sopenharmony_ci	} else {
86462306a36Sopenharmony_ci		dev->disconnected = 1;
86562306a36Sopenharmony_ci		/* wake up pollers */
86662306a36Sopenharmony_ci		wake_up_interruptible_all(&dev->read_wait);
86762306a36Sopenharmony_ci		wake_up_interruptible_all(&dev->write_wait);
86862306a36Sopenharmony_ci		mutex_unlock(&dev->lock);
86962306a36Sopenharmony_ci	}
87062306a36Sopenharmony_ci
87162306a36Sopenharmony_ci	dev_info(&interface->dev, "LEGO USB Tower #%d now disconnected\n",
87262306a36Sopenharmony_ci		 (minor - LEGO_USB_TOWER_MINOR_BASE));
87362306a36Sopenharmony_ci}
87462306a36Sopenharmony_ci
87562306a36Sopenharmony_cimodule_usb_driver(tower_driver);
87662306a36Sopenharmony_ci
87762306a36Sopenharmony_ciMODULE_AUTHOR(DRIVER_AUTHOR);
87862306a36Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_DESC);
87962306a36Sopenharmony_ciMODULE_LICENSE("GPL");
880