diff options
Diffstat (limited to 'drivers/usb')
192 files changed, 22048 insertions, 2279 deletions
diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig index bcefbddeba5..289d81adfb9 100644 --- a/drivers/usb/Kconfig +++ b/drivers/usb/Kconfig @@ -36,7 +36,8 @@ config USB_ARCH_HAS_OHCI  	default y if PXA3xx  	default y if ARCH_EP93XX  	default y if ARCH_AT91 -	default y if ARCH_PNX4008 +	default y if ARCH_PNX4008 && I2C +	default y if MFD_TC6393XB  	# PPC:  	default y if STB03xxx  	default y if PPC_MPC52xx @@ -97,6 +98,8 @@ source "drivers/usb/core/Kconfig"  source "drivers/usb/mon/Kconfig" +source "drivers/usb/wusbcore/Kconfig" +  source "drivers/usb/host/Kconfig"  source "drivers/usb/musb/Kconfig" diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index a419c42e880..8b7c419b876 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -16,9 +16,12 @@ obj-$(CONFIG_USB_UHCI_HCD)	+= host/  obj-$(CONFIG_USB_SL811_HCD)	+= host/  obj-$(CONFIG_USB_U132_HCD)	+= host/  obj-$(CONFIG_USB_R8A66597_HCD)	+= host/ +obj-$(CONFIG_USB_HWA_HCD)	+= host/  obj-$(CONFIG_USB_C67X00_HCD)	+= c67x00/ +obj-$(CONFIG_USB_WUSB)		+= wusbcore/ +  obj-$(CONFIG_USB_ACM)		+= class/  obj-$(CONFIG_USB_PRINTER)	+= class/ diff --git a/drivers/usb/atm/speedtch.c b/drivers/usb/atm/speedtch.c index 76fce44c2f9..3e862401a63 100644 --- a/drivers/usb/atm/speedtch.c +++ b/drivers/usb/atm/speedtch.c @@ -722,6 +722,16 @@ static void speedtch_atm_stop(struct usbatm_data *usbatm, struct atm_dev *atm_de  	flush_scheduled_work();  } +static int speedtch_pre_reset(struct usb_interface *intf) +{ +	return 0; +} + +static int speedtch_post_reset(struct usb_interface *intf) +{ +	return 0; +} +  /**********  **  USB  ** @@ -740,6 +750,8 @@ static struct usb_driver speedtch_usb_driver = {  	.name		= speedtch_driver_name,  	.probe		= speedtch_usb_probe,  	.disconnect	= usbatm_usb_disconnect, +	.pre_reset	= speedtch_pre_reset, +	.post_reset	= speedtch_post_reset,  	.id_table	= speedtch_usb_ids  }; diff --git a/drivers/usb/atm/usbatm.c b/drivers/usb/atm/usbatm.c index 07228721caf..06dd114910d 100644 --- a/drivers/usb/atm/usbatm.c +++ b/drivers/usb/atm/usbatm.c @@ -344,7 +344,7 @@ static void usbatm_extract_one_cell(struct usbatm_data *instance, unsigned char  				__func__, sarb->len, vcc);  		/* discard cells already received */  		skb_trim(sarb, 0); -		UDSL_ASSERT(sarb->tail + ATM_CELL_PAYLOAD <= sarb->end); +		UDSL_ASSERT(instance, sarb->tail + ATM_CELL_PAYLOAD <= sarb->end);  	}  	memcpy(skb_tail_pointer(sarb), source + ATM_CELL_HEADER, ATM_CELL_PAYLOAD); @@ -432,7 +432,7 @@ static void usbatm_extract_cells(struct usbatm_data *instance,  		unsigned char *cell_buf = instance->cell_buf;  		unsigned int space_left = stride - buf_usage; -		UDSL_ASSERT(buf_usage <= stride); +		UDSL_ASSERT(instance, buf_usage <= stride);  		if (avail_data >= space_left) {  			/* add new data and process cell */ @@ -475,7 +475,7 @@ static unsigned int usbatm_write_cells(struct usbatm_data *instance,  	unsigned int stride = instance->tx_channel.stride;  	vdbg("%s: skb->len=%d, avail_space=%u", __func__, skb->len, avail_space); -	UDSL_ASSERT(!(avail_space % stride)); +	UDSL_ASSERT(instance, !(avail_space % stride));  	for (bytes_written = 0; bytes_written < avail_space && ctrl->len;  	     bytes_written += stride, target += stride) { @@ -547,7 +547,7 @@ static void usbatm_rx_process(unsigned long data)  				if (!urb->iso_frame_desc[i].status) {  					unsigned int actual_length = urb->iso_frame_desc[i].actual_length; -					UDSL_ASSERT(actual_length <= packet_size); +					UDSL_ASSERT(instance, actual_length <= packet_size);  					if (!merge_length)  						merge_start = (unsigned char *)urb->transfer_buffer + urb->iso_frame_desc[i].offset; @@ -640,14 +640,13 @@ static void usbatm_cancel_send(struct usbatm_data *instance,  	atm_dbg(instance, "%s entered\n", __func__);  	spin_lock_irq(&instance->sndqueue.lock); -	for (skb = instance->sndqueue.next, n = skb->next; -	     skb != (struct sk_buff *)&instance->sndqueue; -	     skb = n, n = skb->next) +	skb_queue_walk_safe(&instance->sndqueue, skb, n) {  		if (UDSL_SKB(skb)->atm.vcc == vcc) {  			atm_dbg(instance, "%s: popping skb 0x%p\n", __func__, skb);  			__skb_unlink(skb, &instance->sndqueue);  			usbatm_pop(vcc, skb);  		} +	}  	spin_unlock_irq(&instance->sndqueue.lock);  	tasklet_disable(&instance->tx_channel.tasklet); @@ -1189,7 +1188,7 @@ int usbatm_usb_probe(struct usb_interface *intf, const struct usb_device_id *id,  		struct urb *urb;  		unsigned int iso_packets = usb_pipeisoc(channel->endpoint) ? channel->buf_size / channel->packet_size : 0; -		UDSL_ASSERT(!usb_pipeisoc(channel->endpoint) || usb_pipein(channel->endpoint)); +		UDSL_ASSERT(instance, !usb_pipeisoc(channel->endpoint) || usb_pipein(channel->endpoint));  		urb = usb_alloc_urb(iso_packets, GFP_KERNEL);  		if (!urb) { diff --git a/drivers/usb/atm/usbatm.h b/drivers/usb/atm/usbatm.h index e6887c6cf3c..f6f4508a9d4 100644 --- a/drivers/usb/atm/usbatm.h +++ b/drivers/usb/atm/usbatm.h @@ -40,9 +40,15 @@  */  #ifdef DEBUG -#define UDSL_ASSERT(x)	BUG_ON(!(x)) +#define UDSL_ASSERT(instance, x)	BUG_ON(!(x))  #else -#define UDSL_ASSERT(x)	do { if (!(x)) warn("failed assertion '%s' at line %d", __stringify(x), __LINE__); } while(0) +#define UDSL_ASSERT(instance, x)					\ +	do {	\ +		if (!(x))						\ +			dev_warn(&(instance)->usb_intf->dev,		\ +				 "failed assertion '%s' at line %d",	\ +				 __stringify(x), __LINE__);		\ +	} while(0)  #endif  #define usb_err(instance, format, arg...)	\ diff --git a/drivers/usb/atm/xusbatm.c b/drivers/usb/atm/xusbatm.c index 8472543eee8..17d167bbd2d 100644 --- a/drivers/usb/atm/xusbatm.c +++ b/drivers/usb/atm/xusbatm.c @@ -193,7 +193,7 @@ static int __init xusbatm_init(void)  	    num_vendor != num_product ||  	    num_vendor != num_rx_endpoint ||  	    num_vendor != num_tx_endpoint) { -		warn("malformed module parameters"); +		printk(KERN_WARNING "xusbatm: malformed module parameters\n");  		return -EINVAL;  	} diff --git a/drivers/usb/class/Kconfig b/drivers/usb/class/Kconfig index 66f17ed88cb..2519e320098 100644 --- a/drivers/usb/class/Kconfig +++ b/drivers/usb/class/Kconfig @@ -40,3 +40,13 @@ config USB_WDM  	  To compile this driver as a module, choose M here: the  	  module will be called cdc-wdm. +config USB_TMC +	tristate "USB Test and Measurement Class support" +	depends on USB +	help +	  Say Y here if you want to connect a USB device that follows +	  the USB.org specification for USB Test and Measurement devices +	  to your computer's USB port. + +	  To compile this driver as a module, choose M here: the +	  module will be called usbtmc. diff --git a/drivers/usb/class/Makefile b/drivers/usb/class/Makefile index 535d59a3060..32e85277b5c 100644 --- a/drivers/usb/class/Makefile +++ b/drivers/usb/class/Makefile @@ -6,3 +6,4 @@  obj-$(CONFIG_USB_ACM)		+= cdc-acm.o  obj-$(CONFIG_USB_PRINTER)	+= usblp.o  obj-$(CONFIG_USB_WDM)		+= cdc-wdm.o +obj-$(CONFIG_USB_TMC)		+= usbtmc.o diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index c257453fa9d..d50a99f70ae 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -158,16 +158,12 @@ static int acm_wb_is_avail(struct acm *acm)  }  /* - * Finish write. + * Finish write. Caller must hold acm->write_lock   */  static void acm_write_done(struct acm *acm, struct acm_wb *wb)  { -	unsigned long flags; - -	spin_lock_irqsave(&acm->write_lock, flags);  	wb->use = 0;  	acm->transmitting--; -	spin_unlock_irqrestore(&acm->write_lock, flags);  }  /* @@ -326,8 +322,8 @@ exit:  	usb_mark_last_busy(acm->dev);  	retval = usb_submit_urb (urb, GFP_ATOMIC);  	if (retval) -		err ("%s - usb_submit_urb failed with result %d", -		     __func__, retval); +		dev_err(&urb->dev->dev, "%s - usb_submit_urb failed with " +			"result %d", __func__, retval);  }  /* data interface returns incoming bytes, or we got unthrottled */ @@ -482,6 +478,7 @@ static void acm_write_bulk(struct urb *urb)  {  	struct acm_wb *wb = urb->context;  	struct acm *acm = wb->instance; +	unsigned long flags;  	if (verbose || urb->status  			|| (urb->actual_length != urb->transfer_buffer_length)) @@ -490,7 +487,9 @@ static void acm_write_bulk(struct urb *urb)  			urb->transfer_buffer_length,  			urb->status); +	spin_lock_irqsave(&acm->write_lock, flags);  	acm_write_done(acm, wb); +	spin_unlock_irqrestore(&acm->write_lock, flags);  	if (ACM_READY(acm))  		schedule_work(&acm->work);  	else @@ -514,7 +513,7 @@ static void acm_waker(struct work_struct *waker)  	rv = usb_autopm_get_interface(acm->control);  	if (rv < 0) { -		err("Autopm failure in %s", __func__); +		dev_err(&acm->dev->dev, "Autopm failure in %s\n", __func__);  		return;  	}  	if (acm->delayed_wb) { @@ -849,9 +848,10 @@ static void acm_write_buffers_free(struct acm *acm)  {  	int i;  	struct acm_wb *wb; +	struct usb_device *usb_dev = interface_to_usbdev(acm->control);  	for (wb = &acm->wb[0], i = 0; i < ACM_NW; i++, wb++) { -		usb_buffer_free(acm->dev, acm->writesize, wb->buf, wb->dmah); +		usb_buffer_free(usb_dev, acm->writesize, wb->buf, wb->dmah);  	}  } @@ -924,7 +924,7 @@ static int acm_probe (struct usb_interface *intf,  	/* normal probing*/  	if (!buffer) { -		err("Weird descriptor references\n"); +		dev_err(&intf->dev, "Weird descriptor references\n");  		return -EINVAL;  	} @@ -934,21 +934,24 @@ static int acm_probe (struct usb_interface *intf,  			buflen = intf->cur_altsetting->endpoint->extralen;  			buffer = intf->cur_altsetting->endpoint->extra;  		} else { -			err("Zero length descriptor references\n"); +			dev_err(&intf->dev, +				"Zero length descriptor references\n");  			return -EINVAL;  		}  	}  	while (buflen > 0) {  		if (buffer [1] != USB_DT_CS_INTERFACE) { -			err("skipping garbage\n"); +			dev_err(&intf->dev, "skipping garbage\n");  			goto next_desc;  		}  		switch (buffer [2]) {  			case USB_CDC_UNION_TYPE: /* we've found it */  				if (union_header) { -					err("More than one union descriptor, skipping ..."); +					dev_err(&intf->dev, "More than one " +						"union descriptor, " +						"skipping ...\n");  					goto next_desc;  				}  				union_header = (struct usb_cdc_union_desc *) @@ -966,7 +969,9 @@ static int acm_probe (struct usb_interface *intf,  				call_management_function = buffer[3];  				call_interface_num = buffer[4];  				if ((call_management_function & 3) != 3) -					err("This device cannot do calls on its own. It is no modem."); +					dev_err(&intf->dev, "This device " +						"cannot do calls on its own. " +						"It is no modem.\n");  				break;  			default:  				/* there are LOTS more CDC descriptors that @@ -1051,7 +1056,7 @@ skip_normal_probe:  	for (minor = 0; minor < ACM_TTY_MINORS && acm_table[minor]; minor++);  	if (minor == ACM_TTY_MINORS) { -		err("no more free acm devices"); +		dev_err(&intf->dev, "no more free acm devices\n");  		return -ENODEV;  	} @@ -1454,7 +1459,8 @@ static int __init acm_init(void)  		return retval;  	} -	info(DRIVER_VERSION ":" DRIVER_DESC); +	printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":" +	       DRIVER_DESC "\n");  	return 0;  } diff --git a/drivers/usb/class/cdc-wdm.c b/drivers/usb/class/cdc-wdm.c index 7e8e1235e4e..5a8ecc045e3 100644 --- a/drivers/usb/class/cdc-wdm.c +++ b/drivers/usb/class/cdc-wdm.c @@ -42,6 +42,8 @@ static struct usb_device_id wdm_ids[] = {  	{ }  }; +MODULE_DEVICE_TABLE (usb, wdm_ids); +  #define WDM_MINOR_BASE	176 @@ -132,10 +134,12 @@ static void wdm_in_callback(struct urb *urb)  				"nonzero urb status received: -ESHUTDOWN");  			break;  		case -EPIPE: -			err("nonzero urb status received: -EPIPE"); +			dev_err(&desc->intf->dev, +				"nonzero urb status received: -EPIPE\n");  			break;  		default: -			err("Unexpected error %d", status); +			dev_err(&desc->intf->dev, +				"Unexpected error %d\n", status);  			break;  		}  	} @@ -170,16 +174,18 @@ static void wdm_int_callback(struct urb *urb)  			return; /* unplug */  		case -EPIPE:  			set_bit(WDM_INT_STALL, &desc->flags); -			err("Stall on int endpoint"); +			dev_err(&desc->intf->dev, "Stall on int endpoint\n");  			goto sw; /* halt is cleared in work */  		default: -			err("nonzero urb status received: %d", status); +			dev_err(&desc->intf->dev, +				"nonzero urb status received: %d\n", status);  			break;  		}  	}  	if (urb->actual_length < sizeof(struct usb_cdc_notification)) { -		err("wdm_int_callback - %d bytes", urb->actual_length); +		dev_err(&desc->intf->dev, "wdm_int_callback - %d bytes\n", +			urb->actual_length);  		goto exit;  	} @@ -198,7 +204,8 @@ static void wdm_int_callback(struct urb *urb)  		goto exit;  	default:  		clear_bit(WDM_POLL_RUNNING, &desc->flags); -		err("unknown notification %d received: index %d len %d", +		dev_err(&desc->intf->dev, +			"unknown notification %d received: index %d len %d\n",  			dr->bNotificationType, dr->wIndex, dr->wLength);  		goto exit;  	} @@ -236,14 +243,16 @@ static void wdm_int_callback(struct urb *urb)  sw:  			rv = schedule_work(&desc->rxwork);  			if (rv) -				err("Cannot schedule work"); +				dev_err(&desc->intf->dev, +					"Cannot schedule work\n");  		}  	}  exit:  	rv = usb_submit_urb(urb, GFP_ATOMIC);  	if (rv) -		err("%s - usb_submit_urb failed with result %d", -		     __func__, rv); +		dev_err(&desc->intf->dev, +			"%s - usb_submit_urb failed with result %d\n", +			__func__, rv);  } @@ -353,7 +362,7 @@ static ssize_t wdm_write  	if (rv < 0) {  		kfree(buf);  		clear_bit(WDM_IN_USE, &desc->flags); -		err("Tx URB error: %d", rv); +		dev_err(&desc->intf->dev, "Tx URB error: %d\n", rv);  	} else {  		dev_dbg(&desc->intf->dev, "Tx URB has been submitted index=%d",  			req->wIndex); @@ -401,7 +410,8 @@ retry:  			int t = desc->rerr;  			desc->rerr = 0;  			spin_unlock_irq(&desc->iuspin); -			err("reading had resulted in %d", t); +			dev_err(&desc->intf->dev, +				"reading had resulted in %d\n", t);  			rv = -EIO;  			goto err;  		} @@ -440,7 +450,7 @@ retry:  err:  	mutex_unlock(&desc->rlock);  	if (rv < 0) -		err("wdm_read: exit error"); +		dev_err(&desc->intf->dev, "wdm_read: exit error\n");  	return rv;  } @@ -450,7 +460,8 @@ static int wdm_flush(struct file *file, fl_owner_t id)  	wait_event(desc->wait, !test_bit(WDM_IN_USE, &desc->flags));  	if (desc->werr < 0) -		err("Error in flush path: %d", desc->werr); +		dev_err(&desc->intf->dev, "Error in flush path: %d\n", +			desc->werr);  	return desc->werr;  } @@ -502,7 +513,7 @@ static int wdm_open(struct inode *inode, struct file *file)  	rv = usb_autopm_get_interface(desc->intf);  	if (rv < 0) { -		err("Error autopm - %d", rv); +		dev_err(&desc->intf->dev, "Error autopm - %d\n", rv);  		goto out;  	}  	intf->needs_remote_wakeup = 1; @@ -512,7 +523,8 @@ static int wdm_open(struct inode *inode, struct file *file)  		rv = usb_submit_urb(desc->validity, GFP_KERNEL);  		if (rv < 0) {  			desc->count--; -			err("Error submitting int urb - %d", rv); +			dev_err(&desc->intf->dev, +				"Error submitting int urb - %d\n", rv);  		}  	} else {  		rv = 0; @@ -600,7 +612,7 @@ static int wdm_probe(struct usb_interface *intf, const struct usb_device_id *id)  	while (buflen > 0) {  		if (buffer [1] != USB_DT_CS_INTERFACE) { -			err("skipping garbage"); +			dev_err(&intf->dev, "skipping garbage\n");  			goto next_desc;  		} @@ -614,7 +626,8 @@ static int wdm_probe(struct usb_interface *intf, const struct usb_device_id *id)  				"Finding maximum buffer length: %d", maxcom);  			break;  		default: -			err("Ignoring extra header, type %d, length %d", +			dev_err(&intf->dev, +				"Ignoring extra header, type %d, length %d\n",  				buffer[2], buffer[0]);  			break;  		} @@ -772,7 +785,8 @@ static int recover_from_urb_loss(struct wdm_device *desc)  	if (desc->count) {  		rv = usb_submit_urb(desc->validity, GFP_NOIO);  		if (rv < 0) -			err("Error resume submitting int urb - %d", rv); +			dev_err(&desc->intf->dev, +				"Error resume submitting int urb - %d\n", rv);  	}  	return rv;  } diff --git a/drivers/usb/class/usblp.c b/drivers/usb/class/usblp.c index 0647164d36d..b5775af3ba2 100644 --- a/drivers/usb/class/usblp.c +++ b/drivers/usb/class/usblp.c @@ -593,8 +593,9 @@ static long usblp_ioctl(struct file *file, unsigned int cmd, unsigned long arg)  				err = usblp_hp_channel_change_request(usblp,  					arg, &newChannel);  				if (err < 0) { -					err("usblp%d: error = %d setting " -						"HP channel", +					dev_err(&usblp->dev->dev, +						"usblp%d: error = %d setting " +						"HP channel\n",  						usblp->minor, err);  					retval = -EIO;  					goto done; @@ -1076,15 +1077,16 @@ static int usblp_probe(struct usb_interface *intf,  		       const struct usb_device_id *id)  {  	struct usb_device *dev = interface_to_usbdev (intf); -	struct usblp *usblp = NULL; +	struct usblp *usblp;  	int protocol;  	int retval;  	/* Malloc and start initializing usblp structure so we can use it  	 * directly. */ -	if (!(usblp = kzalloc(sizeof(struct usblp), GFP_KERNEL))) { +	usblp = kzalloc(sizeof(struct usblp), GFP_KERNEL); +	if (!usblp) {  		retval = -ENOMEM; -		goto abort; +		goto abort_ret;  	}  	usblp->dev = dev;  	mutex_init(&usblp->wmut); @@ -1179,12 +1181,11 @@ abort_intfdata:  	usb_set_intfdata (intf, NULL);  	device_remove_file(&intf->dev, &dev_attr_ieee1284_id);  abort: -	if (usblp) { -		kfree(usblp->readbuf); -		kfree(usblp->statusbuf); -		kfree(usblp->device_id_string); -		kfree(usblp); -	} +	kfree(usblp->readbuf); +	kfree(usblp->statusbuf); +	kfree(usblp->device_id_string); +	kfree(usblp); +abort_ret:  	return retval;  } @@ -1345,7 +1346,7 @@ static void usblp_disconnect(struct usb_interface *intf)  	usb_deregister_dev(intf, &usblp_class);  	if (!usblp || !usblp->dev) { -		err("bogus disconnect"); +		dev_err(&intf->dev, "bogus disconnect\n");  		BUG ();  	} diff --git a/drivers/usb/class/usbtmc.c b/drivers/usb/class/usbtmc.c new file mode 100644 index 00000000000..8e74657f106 --- /dev/null +++ b/drivers/usb/class/usbtmc.c @@ -0,0 +1,1087 @@ +/** + * drivers/usb/class/usbtmc.c - USB Test & Measurment class driver + * + * Copyright (C) 2007 Stefan Kopp, Gechingen, Germany + * Copyright (C) 2008 Novell, Inc. + * Copyright (C) 2008 Greg Kroah-Hartman <gregkh@suse.de> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * The GNU General Public License is available at + * http://www.gnu.org/copyleft/gpl.html. + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/fs.h> +#include <linux/uaccess.h> +#include <linux/kref.h> +#include <linux/mutex.h> +#include <linux/usb.h> +#include <linux/usb/tmc.h> + + +#define USBTMC_MINOR_BASE	176 + +/* + * Size of driver internal IO buffer. Must be multiple of 4 and at least as + * large as wMaxPacketSize (which is usually 512 bytes). + */ +#define USBTMC_SIZE_IOBUFFER	2048 + +/* Default USB timeout (in milliseconds) */ +#define USBTMC_TIMEOUT		10 + +/* + * Maximum number of read cycles to empty bulk in endpoint during CLEAR and + * ABORT_BULK_IN requests. Ends the loop if (for whatever reason) a short + * packet is never read. + */ +#define USBTMC_MAX_READS_TO_CLEAR_BULK_IN	100 + +static struct usb_device_id usbtmc_devices[] = { +	{ USB_INTERFACE_INFO(USB_CLASS_APP_SPEC, 3, 0), }, +	{ 0, } /* terminating entry */ +}; + +/* + * This structure is the capabilities for the device + * See section 4.2.1.8 of the USBTMC specification for details. + */ +struct usbtmc_dev_capabilities { +	__u8 interface_capabilities; +	__u8 device_capabilities; +	__u8 usb488_interface_capabilities; +	__u8 usb488_device_capabilities; +}; + +/* This structure holds private data for each USBTMC device. One copy is + * allocated for each USBTMC device in the driver's probe function. + */ +struct usbtmc_device_data { +	const struct usb_device_id *id; +	struct usb_device *usb_dev; +	struct usb_interface *intf; + +	unsigned int bulk_in; +	unsigned int bulk_out; + +	u8 bTag; +	u8 bTag_last_write;	/* needed for abort */ +	u8 bTag_last_read;	/* needed for abort */ + +	/* attributes from the USB TMC spec for this device */ +	u8 TermChar; +	bool TermCharEnabled; +	bool auto_abort; + +	struct usbtmc_dev_capabilities	capabilities; +	struct kref kref; +	struct mutex io_mutex;	/* only one i/o function running at a time */ +}; +#define to_usbtmc_data(d) container_of(d, struct usbtmc_device_data, kref) + +/* Forward declarations */ +static struct usb_driver usbtmc_driver; + +static void usbtmc_delete(struct kref *kref) +{ +	struct usbtmc_device_data *data = to_usbtmc_data(kref); + +	usb_put_dev(data->usb_dev); +	kfree(data); +} + +static int usbtmc_open(struct inode *inode, struct file *filp) +{ +	struct usb_interface *intf; +	struct usbtmc_device_data *data; +	int retval = -ENODEV; + +	intf = usb_find_interface(&usbtmc_driver, iminor(inode)); +	if (!intf) { +		printk(KERN_ERR KBUILD_MODNAME +		       ": can not find device for minor %d", iminor(inode)); +		goto exit; +	} + +	data = usb_get_intfdata(intf); +	kref_get(&data->kref); + +	/* Store pointer in file structure's private data field */ +	filp->private_data = data; + +exit: +	return retval; +} + +static int usbtmc_release(struct inode *inode, struct file *file) +{ +	struct usbtmc_device_data *data = file->private_data; + +	kref_put(&data->kref, usbtmc_delete); +	return 0; +} + +static int usbtmc_ioctl_abort_bulk_in(struct usbtmc_device_data *data) +{ +	u8 *buffer; +	struct device *dev; +	int rv; +	int n; +	int actual; +	struct usb_host_interface *current_setting; +	int max_size; + +	dev = &data->intf->dev; +	buffer = kmalloc(USBTMC_SIZE_IOBUFFER, GFP_KERNEL); +	if (!buffer) +		return -ENOMEM; + +	rv = usb_control_msg(data->usb_dev, +			     usb_rcvctrlpipe(data->usb_dev, 0), +			     USBTMC_REQUEST_INITIATE_ABORT_BULK_IN, +			     USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_ENDPOINT, +			     data->bTag_last_read, data->bulk_in, +			     buffer, 2, USBTMC_TIMEOUT); + +	if (rv < 0) { +		dev_err(dev, "usb_control_msg returned %d\n", rv); +		goto exit; +	} + +	dev_dbg(dev, "INITIATE_ABORT_BULK_IN returned %x\n", buffer[0]); + +	if (buffer[0] == USBTMC_STATUS_FAILED) { +		rv = 0; +		goto exit; +	} + +	if (buffer[0] != USBTMC_STATUS_SUCCESS) { +		dev_err(dev, "INITIATE_ABORT_BULK_IN returned %x\n", +			buffer[0]); +		rv = -EPERM; +		goto exit; +	} + +	max_size = 0; +	current_setting = data->intf->cur_altsetting; +	for (n = 0; n < current_setting->desc.bNumEndpoints; n++) +		if (current_setting->endpoint[n].desc.bEndpointAddress == +			data->bulk_in) +			max_size = le16_to_cpu(current_setting->endpoint[n]. +						desc.wMaxPacketSize); + +	if (max_size == 0) { +		dev_err(dev, "Couldn't get wMaxPacketSize\n"); +		rv = -EPERM; +		goto exit; +	} + +	dev_dbg(&data->intf->dev, "wMaxPacketSize is %d\n", max_size); + +	n = 0; + +	do { +		dev_dbg(dev, "Reading from bulk in EP\n"); + +		rv = usb_bulk_msg(data->usb_dev, +				  usb_rcvbulkpipe(data->usb_dev, +						  data->bulk_in), +				  buffer, USBTMC_SIZE_IOBUFFER, +				  &actual, USBTMC_TIMEOUT); + +		n++; + +		if (rv < 0) { +			dev_err(dev, "usb_bulk_msg returned %d\n", rv); +			goto exit; +		} +	} while ((actual == max_size) && +		 (n < USBTMC_MAX_READS_TO_CLEAR_BULK_IN)); + +	if (actual == max_size) { +		dev_err(dev, "Couldn't clear device buffer within %d cycles\n", +			USBTMC_MAX_READS_TO_CLEAR_BULK_IN); +		rv = -EPERM; +		goto exit; +	} + +	n = 0; + +usbtmc_abort_bulk_in_status: +	rv = usb_control_msg(data->usb_dev, +			     usb_rcvctrlpipe(data->usb_dev, 0), +			     USBTMC_REQUEST_CHECK_ABORT_BULK_IN_STATUS, +			     USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_ENDPOINT, +			     0, data->bulk_in, buffer, 0x08, +			     USBTMC_TIMEOUT); + +	if (rv < 0) { +		dev_err(dev, "usb_control_msg returned %d\n", rv); +		goto exit; +	} + +	dev_dbg(dev, "INITIATE_ABORT_BULK_IN returned %x\n", buffer[0]); + +	if (buffer[0] == USBTMC_STATUS_SUCCESS) { +		rv = 0; +		goto exit; +	} + +	if (buffer[0] != USBTMC_STATUS_PENDING) { +		dev_err(dev, "INITIATE_ABORT_BULK_IN returned %x\n", buffer[0]); +		rv = -EPERM; +		goto exit; +	} + +	if (buffer[1] == 1) +		do { +			dev_dbg(dev, "Reading from bulk in EP\n"); + +			rv = usb_bulk_msg(data->usb_dev, +					  usb_rcvbulkpipe(data->usb_dev, +							  data->bulk_in), +					  buffer, USBTMC_SIZE_IOBUFFER, +					  &actual, USBTMC_TIMEOUT); + +			n++; + +			if (rv < 0) { +				dev_err(dev, "usb_bulk_msg returned %d\n", rv); +				goto exit; +			} +		} while ((actual = max_size) && +			 (n < USBTMC_MAX_READS_TO_CLEAR_BULK_IN)); + +	if (actual == max_size) { +		dev_err(dev, "Couldn't clear device buffer within %d cycles\n", +			USBTMC_MAX_READS_TO_CLEAR_BULK_IN); +		rv = -EPERM; +		goto exit; +	} + +	goto usbtmc_abort_bulk_in_status; + +exit: +	kfree(buffer); +	return rv; + +} + +static int usbtmc_ioctl_abort_bulk_out(struct usbtmc_device_data *data) +{ +	struct device *dev; +	u8 *buffer; +	int rv; +	int n; + +	dev = &data->intf->dev; + +	buffer = kmalloc(8, GFP_KERNEL); +	if (!buffer) +		return -ENOMEM; + +	rv = usb_control_msg(data->usb_dev, +			     usb_rcvctrlpipe(data->usb_dev, 0), +			     USBTMC_REQUEST_INITIATE_ABORT_BULK_OUT, +			     USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_ENDPOINT, +			     data->bTag_last_write, data->bulk_out, +			     buffer, 2, USBTMC_TIMEOUT); + +	if (rv < 0) { +		dev_err(dev, "usb_control_msg returned %d\n", rv); +		goto exit; +	} + +	dev_dbg(dev, "INITIATE_ABORT_BULK_OUT returned %x\n", buffer[0]); + +	if (buffer[0] != USBTMC_STATUS_SUCCESS) { +		dev_err(dev, "INITIATE_ABORT_BULK_OUT returned %x\n", +			buffer[0]); +		rv = -EPERM; +		goto exit; +	} + +	n = 0; + +usbtmc_abort_bulk_out_check_status: +	rv = usb_control_msg(data->usb_dev, +			     usb_rcvctrlpipe(data->usb_dev, 0), +			     USBTMC_REQUEST_CHECK_ABORT_BULK_OUT_STATUS, +			     USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_ENDPOINT, +			     0, data->bulk_out, buffer, 0x08, +			     USBTMC_TIMEOUT); +	n++; +	if (rv < 0) { +		dev_err(dev, "usb_control_msg returned %d\n", rv); +		goto exit; +	} + +	dev_dbg(dev, "CHECK_ABORT_BULK_OUT returned %x\n", buffer[0]); + +	if (buffer[0] == USBTMC_STATUS_SUCCESS) +		goto usbtmc_abort_bulk_out_clear_halt; + +	if ((buffer[0] == USBTMC_STATUS_PENDING) && +	    (n < USBTMC_MAX_READS_TO_CLEAR_BULK_IN)) +		goto usbtmc_abort_bulk_out_check_status; + +	rv = -EPERM; +	goto exit; + +usbtmc_abort_bulk_out_clear_halt: +	rv = usb_control_msg(data->usb_dev, +			     usb_sndctrlpipe(data->usb_dev, 0), +			     USB_REQ_CLEAR_FEATURE, +			     USB_DIR_OUT | USB_TYPE_STANDARD | +			     USB_RECIP_ENDPOINT, +			     USB_ENDPOINT_HALT, data->bulk_out, buffer, +			     0, USBTMC_TIMEOUT); + +	if (rv < 0) { +		dev_err(dev, "usb_control_msg returned %d\n", rv); +		goto exit; +	} +	rv = 0; + +exit: +	kfree(buffer); +	return rv; +} + +static ssize_t usbtmc_read(struct file *filp, char __user *buf, +			   size_t count, loff_t *f_pos) +{ +	struct usbtmc_device_data *data; +	struct device *dev; +	unsigned long int n_characters; +	u8 *buffer; +	int actual; +	int done; +	int remaining; +	int retval; +	int this_part; + +	/* Get pointer to private data structure */ +	data = filp->private_data; +	dev = &data->intf->dev; + +	buffer = kmalloc(USBTMC_SIZE_IOBUFFER, GFP_KERNEL); +	if (!buffer) +		return -ENOMEM; + +	mutex_lock(&data->io_mutex); + +	remaining = count; +	done = 0; + +	while (remaining > 0) { +		if (remaining > USBTMC_SIZE_IOBUFFER - 12 - 3) +			this_part = USBTMC_SIZE_IOBUFFER - 12 - 3; +		else +			this_part = remaining; + +		/* Setup IO buffer for DEV_DEP_MSG_IN message +		 * Refer to class specs for details +		 */ +		buffer[0] = 2; +		buffer[1] = data->bTag; +		buffer[2] = ~(data->bTag); +		buffer[3] = 0; /* Reserved */ +		buffer[4] = (this_part - 12 - 3) & 255; +		buffer[5] = ((this_part - 12 - 3) >> 8) & 255; +		buffer[6] = ((this_part - 12 - 3) >> 16) & 255; +		buffer[7] = ((this_part - 12 - 3) >> 24) & 255; +		buffer[8] = data->TermCharEnabled * 2; +		/* Use term character? */ +		buffer[9] = data->TermChar; +		buffer[10] = 0; /* Reserved */ +		buffer[11] = 0; /* Reserved */ + +		/* Send bulk URB */ +		retval = usb_bulk_msg(data->usb_dev, +				      usb_sndbulkpipe(data->usb_dev, +						      data->bulk_out), +				      buffer, 12, &actual, USBTMC_TIMEOUT); + +		/* Store bTag (in case we need to abort) */ +		data->bTag_last_write = data->bTag; + +		/* Increment bTag -- and increment again if zero */ +		data->bTag++; +		if (!data->bTag) +			(data->bTag)++; + +		if (retval < 0) { +			dev_err(dev, "usb_bulk_msg returned %d\n", retval); +			if (data->auto_abort) +				usbtmc_ioctl_abort_bulk_out(data); +			goto exit; +		} + +		/* Send bulk URB */ +		retval = usb_bulk_msg(data->usb_dev, +				      usb_rcvbulkpipe(data->usb_dev, +						      data->bulk_in), +				      buffer, USBTMC_SIZE_IOBUFFER, &actual, +				      USBTMC_TIMEOUT); + +		/* Store bTag (in case we need to abort) */ +		data->bTag_last_read = data->bTag; + +		if (retval < 0) { +			dev_err(dev, "Unable to read data, error %d\n", retval); +			if (data->auto_abort) +				usbtmc_ioctl_abort_bulk_in(data); +			goto exit; +		} + +		/* How many characters did the instrument send? */ +		n_characters = buffer[4] + +			       (buffer[5] << 8) + +			       (buffer[6] << 16) + +			       (buffer[7] << 24); + +		/* Copy buffer to user space */ +		if (copy_to_user(buf + done, &buffer[12], n_characters)) { +			/* There must have been an addressing problem */ +			retval = -EFAULT; +			goto exit; +		} + +		done += n_characters; +		if (n_characters < USBTMC_SIZE_IOBUFFER) +			remaining = 0; +	} + +	/* Update file position value */ +	*f_pos = *f_pos + done; +	retval = done; + +exit: +	mutex_unlock(&data->io_mutex); +	kfree(buffer); +	return retval; +} + +static ssize_t usbtmc_write(struct file *filp, const char __user *buf, +			    size_t count, loff_t *f_pos) +{ +	struct usbtmc_device_data *data; +	u8 *buffer; +	int retval; +	int actual; +	unsigned long int n_bytes; +	int n; +	int remaining; +	int done; +	int this_part; + +	data = filp->private_data; + +	buffer = kmalloc(USBTMC_SIZE_IOBUFFER, GFP_KERNEL); +	if (!buffer) +		return -ENOMEM; + +	mutex_lock(&data->io_mutex); + +	remaining = count; +	done = 0; + +	while (remaining > 0) { +		if (remaining > USBTMC_SIZE_IOBUFFER - 12) { +			this_part = USBTMC_SIZE_IOBUFFER - 12; +			buffer[8] = 0; +		} else { +			this_part = remaining; +			buffer[8] = 1; +		} + +		/* Setup IO buffer for DEV_DEP_MSG_OUT message */ +		buffer[0] = 1; +		buffer[1] = data->bTag; +		buffer[2] = ~(data->bTag); +		buffer[3] = 0; /* Reserved */ +		buffer[4] = this_part & 255; +		buffer[5] = (this_part >> 8) & 255; +		buffer[6] = (this_part >> 16) & 255; +		buffer[7] = (this_part >> 24) & 255; +		/* buffer[8] is set above... */ +		buffer[9] = 0; /* Reserved */ +		buffer[10] = 0; /* Reserved */ +		buffer[11] = 0; /* Reserved */ + +		if (copy_from_user(&buffer[12], buf + done, this_part)) { +			retval = -EFAULT; +			goto exit; +		} + +		n_bytes = 12 + this_part; +		if (this_part % 4) +			n_bytes += 4 - this_part % 4; +			for (n = 12 + this_part; n < n_bytes; n++) +				buffer[n] = 0; + +		retval = usb_bulk_msg(data->usb_dev, +				      usb_sndbulkpipe(data->usb_dev, +						      data->bulk_out), +				      buffer, n_bytes, &actual, USBTMC_TIMEOUT); + +		data->bTag_last_write = data->bTag; +		data->bTag++; + +		if (!data->bTag) +			data->bTag++; + +		if (retval < 0) { +			dev_err(&data->intf->dev, +				"Unable to send data, error %d\n", retval); +			if (data->auto_abort) +				usbtmc_ioctl_abort_bulk_out(data); +			goto exit; +		} + +		remaining -= this_part; +		done += this_part; +	} + +	retval = count; +exit: +	mutex_unlock(&data->io_mutex); +	kfree(buffer); +	return retval; +} + +static int usbtmc_ioctl_clear(struct usbtmc_device_data *data) +{ +	struct usb_host_interface *current_setting; +	struct usb_endpoint_descriptor *desc; +	struct device *dev; +	u8 *buffer; +	int rv; +	int n; +	int actual; +	int max_size; + +	dev = &data->intf->dev; + +	dev_dbg(dev, "Sending INITIATE_CLEAR request\n"); + +	buffer = kmalloc(USBTMC_SIZE_IOBUFFER, GFP_KERNEL); +	if (!buffer) +		return -ENOMEM; + +	rv = usb_control_msg(data->usb_dev, +			     usb_rcvctrlpipe(data->usb_dev, 0), +			     USBTMC_REQUEST_INITIATE_CLEAR, +			     USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE, +			     0, 0, buffer, 1, USBTMC_TIMEOUT); +	if (rv < 0) { +		dev_err(dev, "usb_control_msg returned %d\n", rv); +		goto exit; +	} + +	dev_dbg(dev, "INITIATE_CLEAR returned %x\n", buffer[0]); + +	if (buffer[0] != USBTMC_STATUS_SUCCESS) { +		dev_err(dev, "INITIATE_CLEAR returned %x\n", buffer[0]); +		rv = -EPERM; +		goto exit; +	} + +	max_size = 0; +	current_setting = data->intf->cur_altsetting; +	for (n = 0; n < current_setting->desc.bNumEndpoints; n++) { +		desc = ¤t_setting->endpoint[n].desc; +		if (desc->bEndpointAddress == data->bulk_in) +			max_size = le16_to_cpu(desc->wMaxPacketSize); +	} + +	if (max_size == 0) { +		dev_err(dev, "Couldn't get wMaxPacketSize\n"); +		rv = -EPERM; +		goto exit; +	} + +	dev_dbg(dev, "wMaxPacketSize is %d\n", max_size); + +	n = 0; + +usbtmc_clear_check_status: + +	dev_dbg(dev, "Sending CHECK_CLEAR_STATUS request\n"); + +	rv = usb_control_msg(data->usb_dev, +			     usb_rcvctrlpipe(data->usb_dev, 0), +			     USBTMC_REQUEST_CHECK_CLEAR_STATUS, +			     USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE, +			     0, 0, buffer, 2, USBTMC_TIMEOUT); +	if (rv < 0) { +		dev_err(dev, "usb_control_msg returned %d\n", rv); +		goto exit; +	} + +	dev_dbg(dev, "CHECK_CLEAR_STATUS returned %x\n", buffer[0]); + +	if (buffer[0] == USBTMC_STATUS_SUCCESS) +		goto usbtmc_clear_bulk_out_halt; + +	if (buffer[0] != USBTMC_STATUS_PENDING) { +		dev_err(dev, "CHECK_CLEAR_STATUS returned %x\n", buffer[0]); +		rv = -EPERM; +		goto exit; +	} + +	if (buffer[1] == 1) +		do { +			dev_dbg(dev, "Reading from bulk in EP\n"); + +			rv = usb_bulk_msg(data->usb_dev, +					  usb_rcvbulkpipe(data->usb_dev, +							  data->bulk_in), +					  buffer, USBTMC_SIZE_IOBUFFER, +					  &actual, USBTMC_TIMEOUT); +			n++; + +			if (rv < 0) { +				dev_err(dev, "usb_control_msg returned %d\n", +					rv); +				goto exit; +			} +		} while ((actual == max_size) && +			  (n < USBTMC_MAX_READS_TO_CLEAR_BULK_IN)); + +	if (actual == max_size) { +		dev_err(dev, "Couldn't clear device buffer within %d cycles\n", +			USBTMC_MAX_READS_TO_CLEAR_BULK_IN); +		rv = -EPERM; +		goto exit; +	} + +	goto usbtmc_clear_check_status; + +usbtmc_clear_bulk_out_halt: + +	rv = usb_control_msg(data->usb_dev, +			     usb_sndctrlpipe(data->usb_dev, 0), +			     USB_REQ_CLEAR_FEATURE, +			     USB_DIR_OUT | USB_TYPE_STANDARD | +			     USB_RECIP_ENDPOINT, +			     USB_ENDPOINT_HALT, +			     data->bulk_out, buffer, 0, +			     USBTMC_TIMEOUT); +	if (rv < 0) { +		dev_err(dev, "usb_control_msg returned %d\n", rv); +		goto exit; +	} +	rv = 0; + +exit: +	kfree(buffer); +	return rv; +} + +static int usbtmc_ioctl_clear_out_halt(struct usbtmc_device_data *data) +{ +	u8 *buffer; +	int rv; + +	buffer = kmalloc(2, GFP_KERNEL); +	if (!buffer) +		return -ENOMEM; + +	rv = usb_control_msg(data->usb_dev, +			     usb_sndctrlpipe(data->usb_dev, 0), +			     USB_REQ_CLEAR_FEATURE, +			     USB_DIR_OUT | USB_TYPE_STANDARD | +			     USB_RECIP_ENDPOINT, +			     USB_ENDPOINT_HALT, data->bulk_out, +			     buffer, 0, USBTMC_TIMEOUT); + +	if (rv < 0) { +		dev_err(&data->usb_dev->dev, "usb_control_msg returned %d\n", +			rv); +		goto exit; +	} +	rv = 0; + +exit: +	kfree(buffer); +	return rv; +} + +static int usbtmc_ioctl_clear_in_halt(struct usbtmc_device_data *data) +{ +	u8 *buffer; +	int rv; + +	buffer = kmalloc(2, GFP_KERNEL); +	if (!buffer) +		return -ENOMEM; + +	rv = usb_control_msg(data->usb_dev, usb_sndctrlpipe(data->usb_dev, 0), +			     USB_REQ_CLEAR_FEATURE, +			     USB_DIR_OUT | USB_TYPE_STANDARD | +			     USB_RECIP_ENDPOINT, +			     USB_ENDPOINT_HALT, data->bulk_in, buffer, 0, +			     USBTMC_TIMEOUT); + +	if (rv < 0) { +		dev_err(&data->usb_dev->dev, "usb_control_msg returned %d\n", +			rv); +		goto exit; +	} +	rv = 0; + +exit: +	kfree(buffer); +	return rv; +} + +static int get_capabilities(struct usbtmc_device_data *data) +{ +	struct device *dev = &data->usb_dev->dev; +	char *buffer; +	int rv; + +	buffer = kmalloc(0x18, GFP_KERNEL); +	if (!buffer) +		return -ENOMEM; + +	rv = usb_control_msg(data->usb_dev, usb_rcvctrlpipe(data->usb_dev, 0), +			     USBTMC_REQUEST_GET_CAPABILITIES, +			     USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE, +			     0, 0, buffer, 0x18, USBTMC_TIMEOUT); +	if (rv < 0) { +		dev_err(dev, "usb_control_msg returned %d\n", rv); +		return rv; +	} + +	dev_dbg(dev, "GET_CAPABILITIES returned %x\n", buffer[0]); +	dev_dbg(dev, "Interface capabilities are %x\n", buffer[4]); +	dev_dbg(dev, "Device capabilities are %x\n", buffer[5]); +	dev_dbg(dev, "USB488 interface capabilities are %x\n", buffer[14]); +	dev_dbg(dev, "USB488 device capabilities are %x\n", buffer[15]); +	if (buffer[0] != USBTMC_STATUS_SUCCESS) { +		dev_err(dev, "GET_CAPABILITIES returned %x\n", buffer[0]); +		return -EPERM; +	} + +	data->capabilities.interface_capabilities = buffer[4]; +	data->capabilities.device_capabilities = buffer[5]; +	data->capabilities.usb488_interface_capabilities = buffer[14]; +	data->capabilities.usb488_device_capabilities = buffer[15]; + +	kfree(buffer); +	return 0; +} + +#define capability_attribute(name)					\ +static ssize_t show_##name(struct device *dev,				\ +			   struct device_attribute *attr, char *buf)	\ +{									\ +	struct usb_interface *intf = to_usb_interface(dev);		\ +	struct usbtmc_device_data *data = usb_get_intfdata(intf);	\ +									\ +	return sprintf(buf, "%d\n", data->capabilities.name);		\ +}									\ +static DEVICE_ATTR(name, S_IRUGO, show_##name, NULL) + +capability_attribute(interface_capabilities); +capability_attribute(device_capabilities); +capability_attribute(usb488_interface_capabilities); +capability_attribute(usb488_device_capabilities); + +static struct attribute *capability_attrs[] = { +	&dev_attr_interface_capabilities.attr, +	&dev_attr_device_capabilities.attr, +	&dev_attr_usb488_interface_capabilities.attr, +	&dev_attr_usb488_device_capabilities.attr, +	NULL, +}; + +static struct attribute_group capability_attr_grp = { +	.attrs = capability_attrs, +}; + +static ssize_t show_TermChar(struct device *dev, +			     struct device_attribute *attr, char *buf) +{ +	struct usb_interface *intf = to_usb_interface(dev); +	struct usbtmc_device_data *data = usb_get_intfdata(intf); + +	return sprintf(buf, "%c\n", data->TermChar); +} + +static ssize_t store_TermChar(struct device *dev, +			      struct device_attribute *attr, +			      const char *buf, size_t count) +{ +	struct usb_interface *intf = to_usb_interface(dev); +	struct usbtmc_device_data *data = usb_get_intfdata(intf); + +	if (count < 1) +		return -EINVAL; +	data->TermChar = buf[0]; +	return count; +} +static DEVICE_ATTR(TermChar, S_IRUGO, show_TermChar, store_TermChar); + +#define data_attribute(name)						\ +static ssize_t show_##name(struct device *dev,				\ +			   struct device_attribute *attr, char *buf)	\ +{									\ +	struct usb_interface *intf = to_usb_interface(dev);		\ +	struct usbtmc_device_data *data = usb_get_intfdata(intf);	\ +									\ +	return sprintf(buf, "%d\n", data->name);			\ +}									\ +static ssize_t store_##name(struct device *dev,				\ +			    struct device_attribute *attr,		\ +			    const char *buf, size_t count)		\ +{									\ +	struct usb_interface *intf = to_usb_interface(dev);		\ +	struct usbtmc_device_data *data = usb_get_intfdata(intf);	\ +	ssize_t result;							\ +	unsigned val;							\ +									\ +	result = sscanf(buf, "%u\n", &val);				\ +	if (result != 1)						\ +		result = -EINVAL;					\ +	data->name = val;						\ +	if (result < 0)							\ +		return result;						\ +	else								\ +		return count;						\ +}									\ +static DEVICE_ATTR(name, S_IRUGO, show_##name, store_##name) + +data_attribute(TermCharEnabled); +data_attribute(auto_abort); + +static struct attribute *data_attrs[] = { +	&dev_attr_TermChar.attr, +	&dev_attr_TermCharEnabled.attr, +	&dev_attr_auto_abort.attr, +	NULL, +}; + +static struct attribute_group data_attr_grp = { +	.attrs = data_attrs, +}; + +static int usbtmc_ioctl_indicator_pulse(struct usbtmc_device_data *data) +{ +	struct device *dev; +	u8 *buffer; +	int rv; + +	dev = &data->intf->dev; + +	buffer = kmalloc(2, GFP_KERNEL); +	if (!buffer) +		return -ENOMEM; + +	rv = usb_control_msg(data->usb_dev, +			     usb_rcvctrlpipe(data->usb_dev, 0), +			     USBTMC_REQUEST_INDICATOR_PULSE, +			     USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE, +			     0, 0, buffer, 0x01, USBTMC_TIMEOUT); + +	if (rv < 0) { +		dev_err(dev, "usb_control_msg returned %d\n", rv); +		goto exit; +	} + +	dev_dbg(dev, "INDICATOR_PULSE returned %x\n", buffer[0]); + +	if (buffer[0] != USBTMC_STATUS_SUCCESS) { +		dev_err(dev, "INDICATOR_PULSE returned %x\n", buffer[0]); +		rv = -EPERM; +		goto exit; +	} +	rv = 0; + +exit: +	kfree(buffer); +	return rv; +} + +static long usbtmc_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ +	struct usbtmc_device_data *data; +	int retval = -EBADRQC; + +	data = file->private_data; +	mutex_lock(&data->io_mutex); + +	switch (cmd) { +	case USBTMC_IOCTL_CLEAR_OUT_HALT: +		retval = usbtmc_ioctl_clear_out_halt(data); + +	case USBTMC_IOCTL_CLEAR_IN_HALT: +		retval = usbtmc_ioctl_clear_in_halt(data); + +	case USBTMC_IOCTL_INDICATOR_PULSE: +		retval = usbtmc_ioctl_indicator_pulse(data); + +	case USBTMC_IOCTL_CLEAR: +		retval = usbtmc_ioctl_clear(data); + +	case USBTMC_IOCTL_ABORT_BULK_OUT: +		retval = usbtmc_ioctl_abort_bulk_out(data); + +	case USBTMC_IOCTL_ABORT_BULK_IN: +		retval = usbtmc_ioctl_abort_bulk_in(data); +	} + +	mutex_unlock(&data->io_mutex); +	return retval; +} + +static struct file_operations fops = { +	.owner		= THIS_MODULE, +	.read		= usbtmc_read, +	.write		= usbtmc_write, +	.open		= usbtmc_open, +	.release	= usbtmc_release, +	.unlocked_ioctl	= usbtmc_ioctl, +}; + +static struct usb_class_driver usbtmc_class = { +	.name =		"usbtmc%d", +	.fops =		&fops, +	.minor_base =	USBTMC_MINOR_BASE, +}; + + +static int usbtmc_probe(struct usb_interface *intf, +			const struct usb_device_id *id) +{ +	struct usbtmc_device_data *data; +	struct usb_host_interface *iface_desc; +	struct usb_endpoint_descriptor *endpoint; +	int n; +	int retcode; + +	dev_dbg(&intf->dev, "%s called\n", __func__); + +	data = kmalloc(sizeof(struct usbtmc_device_data), GFP_KERNEL); +	if (!data) { +		dev_err(&intf->dev, "Unable to allocate kernel memory\n"); +		return -ENOMEM; +	} + +	data->intf = intf; +	data->id = id; +	data->usb_dev = usb_get_dev(interface_to_usbdev(intf)); +	usb_set_intfdata(intf, data); +	kref_init(&data->kref); +	mutex_init(&data->io_mutex); + +	/* Initialize USBTMC bTag and other fields */ +	data->bTag	= 1; +	data->TermCharEnabled = 0; +	data->TermChar = '\n'; + +	/* USBTMC devices have only one setting, so use that */ +	iface_desc = data->intf->cur_altsetting; + +	/* Find bulk in endpoint */ +	for (n = 0; n < iface_desc->desc.bNumEndpoints; n++) { +		endpoint = &iface_desc->endpoint[n].desc; + +		if (usb_endpoint_is_bulk_in(endpoint)) { +			data->bulk_in = endpoint->bEndpointAddress; +			dev_dbg(&intf->dev, "Found bulk in endpoint at %u\n", +				data->bulk_in); +			break; +		} +	} + +	/* Find bulk out endpoint */ +	for (n = 0; n < iface_desc->desc.bNumEndpoints; n++) { +		endpoint = &iface_desc->endpoint[n].desc; + +		if (usb_endpoint_is_bulk_out(endpoint)) { +			data->bulk_out = endpoint->bEndpointAddress; +			dev_dbg(&intf->dev, "Found Bulk out endpoint at %u\n", +				data->bulk_out); +			break; +		} +	} + +	retcode = get_capabilities(data); +	if (retcode) +		dev_err(&intf->dev, "can't read capabilities\n"); +	else +		retcode = sysfs_create_group(&intf->dev.kobj, +					     &capability_attr_grp); + +	retcode = sysfs_create_group(&intf->dev.kobj, &data_attr_grp); + +	retcode = usb_register_dev(intf, &usbtmc_class); +	if (retcode) { +		dev_err(&intf->dev, "Not able to get a minor" +			" (base %u, slice default): %d\n", USBTMC_MINOR_BASE, +			retcode); +		goto error_register; +	} +	dev_dbg(&intf->dev, "Using minor number %d\n", intf->minor); + +	return 0; + +error_register: +	sysfs_remove_group(&intf->dev.kobj, &capability_attr_grp); +	sysfs_remove_group(&intf->dev.kobj, &data_attr_grp); +	kref_put(&data->kref, usbtmc_delete); +	return retcode; +} + +static void usbtmc_disconnect(struct usb_interface *intf) +{ +	struct usbtmc_device_data *data; + +	dev_dbg(&intf->dev, "usbtmc_disconnect called\n"); + +	data = usb_get_intfdata(intf); +	usb_deregister_dev(intf, &usbtmc_class); +	sysfs_remove_group(&intf->dev.kobj, &capability_attr_grp); +	sysfs_remove_group(&intf->dev.kobj, &data_attr_grp); +	kref_put(&data->kref, usbtmc_delete); +} + +static struct usb_driver usbtmc_driver = { +	.name		= "usbtmc", +	.id_table	= usbtmc_devices, +	.probe		= usbtmc_probe, +	.disconnect	= usbtmc_disconnect +}; + +static int __init usbtmc_init(void) +{ +	int retcode; + +	retcode = usb_register(&usbtmc_driver); +	if (retcode) +		printk(KERN_ERR KBUILD_MODNAME": Unable to register driver\n"); +	return retcode; +} +module_init(usbtmc_init); + +static void __exit usbtmc_exit(void) +{ +	usb_deregister(&usbtmc_driver); +} +module_exit(usbtmc_exit); + +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/core/Kconfig b/drivers/usb/core/Kconfig index cc9f397e839..e1759d17ac5 100644 --- a/drivers/usb/core/Kconfig +++ b/drivers/usb/core/Kconfig @@ -134,5 +134,5 @@ config USB_OTG_BLACKLIST_HUB  	  If you say Y here, then Linux will refuse to enumerate  	  external hubs.  OTG hosts are allowed to reduce hardware  	  and software costs by not supporting external hubs.  So -	  are "Emedded Hosts" that don't offer OTG support. +	  are "Embedded Hosts" that don't offer OTG support. diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index 20290c5b156..2bccefebff1 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -413,7 +413,8 @@ static void driver_disconnect(struct usb_interface *intf)  	if (likely(ifnum < 8*sizeof(ps->ifclaimed)))  		clear_bit(ifnum, &ps->ifclaimed);  	else -		warn("interface number %u out of range", ifnum); +		dev_warn(&intf->dev, "interface number %u out of range\n", +			 ifnum);  	usb_set_intfdata(intf, NULL); @@ -624,6 +625,8 @@ static int usbdev_open(struct inode *inode, struct file *file)  	smp_wmb();  	list_add_tail(&ps->list, &dev->filelist);  	file->private_data = ps; +	snoop(&dev->dev, "opened by process %d: %s\n", task_pid_nr(current), +			current->comm);   out:  	if (ret) {  		kfree(ps); @@ -1729,9 +1732,9 @@ static int usb_classdev_add(struct usb_device *dev)  {  	struct device *cldev; -	cldev = device_create_drvdata(usb_classdev_class, &dev->dev, -				      dev->dev.devt, NULL, "usbdev%d.%d", -				      dev->bus->busnum, dev->devnum); +	cldev = device_create(usb_classdev_class, &dev->dev, dev->dev.devt, +			      NULL, "usbdev%d.%d", dev->bus->busnum, +			      dev->devnum);  	if (IS_ERR(cldev))  		return PTR_ERR(cldev);  	dev->usb_classdev = cldev; @@ -1774,19 +1777,20 @@ int __init usb_devio_init(void)  	retval = register_chrdev_region(USB_DEVICE_DEV, USB_DEVICE_MAX,  					"usb_device");  	if (retval) { -		err("unable to register minors for usb_device"); +		printk(KERN_ERR "Unable to register minors for usb_device\n");  		goto out;  	}  	cdev_init(&usb_device_cdev, &usbdev_file_operations);  	retval = cdev_add(&usb_device_cdev, USB_DEVICE_DEV, USB_DEVICE_MAX);  	if (retval) { -		err("unable to get usb_device major %d", USB_DEVICE_MAJOR); +		printk(KERN_ERR "Unable to get usb_device major %d\n", +		       USB_DEVICE_MAJOR);  		goto error_cdev;  	}  #ifdef CONFIG_USB_DEVICE_CLASS  	usb_classdev_class = class_create(THIS_MODULE, "usb_device");  	if (IS_ERR(usb_classdev_class)) { -		err("unable to register usb_device class"); +		printk(KERN_ERR "Unable to register usb_device class\n");  		retval = PTR_ERR(usb_classdev_class);  		cdev_del(&usb_device_cdev);  		usb_classdev_class = NULL; diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 5a7fa6f0995..3d7793d9303 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -1070,7 +1070,8 @@ static int autosuspend_check(struct usb_device *udev, int reschedule)  				struct usb_driver *driver;  				driver = to_usb_driver(intf->dev.driver); -				if (!driver->reset_resume) +				if (!driver->reset_resume || +				    intf->needs_remote_wakeup)  					return -EOPNOTSUPP;  			}  		} @@ -1609,7 +1610,8 @@ int usb_external_resume_device(struct usb_device *udev)  	status = usb_resume_both(udev);  	udev->last_busy = jiffies;  	usb_pm_unlock(udev); -	do_unbind_rebind(udev, DO_REBIND); +	if (status == 0) +		do_unbind_rebind(udev, DO_REBIND);  	/* Now that the device is awake, we can start trying to autosuspend  	 * it again. */ diff --git a/drivers/usb/core/endpoint.c b/drivers/usb/core/endpoint.c index 22912136fc1..946fae43d62 100644 --- a/drivers/usb/core/endpoint.c +++ b/drivers/usb/core/endpoint.c @@ -169,7 +169,8 @@ static int usb_endpoint_major_init(void)  	error = alloc_chrdev_region(&dev, 0, MAX_ENDPOINT_MINORS,  				    "usb_endpoint");  	if (error) { -		err("unable to get a dynamic major for usb endpoints"); +		printk(KERN_ERR "Unable to get a dynamic major for " +		       "usb endpoints.\n");  		return error;  	}  	usb_endpoint_major = MAJOR(dev); diff --git a/drivers/usb/core/file.c b/drivers/usb/core/file.c index 6b1b229e38c..997e659ff69 100644 --- a/drivers/usb/core/file.c +++ b/drivers/usb/core/file.c @@ -86,7 +86,7 @@ static int init_usb_class(void)  	usb_class->class = class_create(THIS_MODULE, "usb");  	if (IS_ERR(usb_class->class)) {  		result = IS_ERR(usb_class->class); -		err("class_create failed for usb devices"); +		printk(KERN_ERR "class_create failed for usb devices\n");  		kfree(usb_class);  		usb_class = NULL;  	} @@ -115,7 +115,8 @@ int usb_major_init(void)  	error = register_chrdev(USB_MAJOR, "usb", &usb_fops);  	if (error) -		err("unable to get major %d for usb devices", USB_MAJOR); +		printk(KERN_ERR "Unable to get major %d for usb devices\n", +		       USB_MAJOR);  	return error;  } @@ -196,9 +197,9 @@ int usb_register_dev(struct usb_interface *intf,  		++temp;  	else  		temp = name; -	intf->usb_dev = device_create_drvdata(usb_class->class, &intf->dev, -					      MKDEV(USB_MAJOR, minor), NULL, -					      "%s", temp); +	intf->usb_dev = device_create(usb_class->class, &intf->dev, +				      MKDEV(USB_MAJOR, minor), NULL, +				      "%s", temp);  	if (IS_ERR(intf->usb_dev)) {  		down_write(&minor_rwsem);  		usb_minors[intf->minor] = NULL; diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 8ab389dca2b..e1b42626d04 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -81,6 +81,10 @@  /*-------------------------------------------------------------------------*/ +/* Keep track of which host controller drivers are loaded */ +unsigned long usb_hcds_loaded; +EXPORT_SYMBOL_GPL(usb_hcds_loaded); +  /* host controllers we manage */  LIST_HEAD (usb_bus_list);  EXPORT_SYMBOL_GPL (usb_bus_list); @@ -102,6 +106,9 @@ static DEFINE_SPINLOCK(hcd_root_hub_lock);  /* used when updating an endpoint's URB list */  static DEFINE_SPINLOCK(hcd_urb_list_lock); +/* used to protect against unlinking URBs after the device is gone */ +static DEFINE_SPINLOCK(hcd_urb_unlink_lock); +  /* wait queue for synchronous unlinks */  DECLARE_WAIT_QUEUE_HEAD(usb_kill_urb_queue); @@ -818,9 +825,8 @@ static int usb_register_bus(struct usb_bus *bus)  	set_bit (busnum, busmap.busmap);  	bus->busnum = busnum; -	bus->dev = device_create_drvdata(usb_host_class, bus->controller, -					 MKDEV(0, 0), bus, -					 "usb_host%d", busnum); +	bus->dev = device_create(usb_host_class, bus->controller, MKDEV(0, 0), +				 bus, "usb_host%d", busnum);  	result = PTR_ERR(bus->dev);  	if (IS_ERR(bus->dev))  		goto error_create_class_dev; @@ -1373,10 +1379,25 @@ static int unlink1(struct usb_hcd *hcd, struct urb *urb, int status)  int usb_hcd_unlink_urb (struct urb *urb, int status)  {  	struct usb_hcd		*hcd; -	int			retval; +	int			retval = -EIDRM; +	unsigned long		flags; -	hcd = bus_to_hcd(urb->dev->bus); -	retval = unlink1(hcd, urb, status); +	/* Prevent the device and bus from going away while +	 * the unlink is carried out.  If they are already gone +	 * then urb->use_count must be 0, since disconnected +	 * devices can't have any active URBs. +	 */ +	spin_lock_irqsave(&hcd_urb_unlink_lock, flags); +	if (atomic_read(&urb->use_count) > 0) { +		retval = 0; +		usb_get_dev(urb->dev); +	} +	spin_unlock_irqrestore(&hcd_urb_unlink_lock, flags); +	if (retval == 0) { +		hcd = bus_to_hcd(urb->dev->bus); +		retval = unlink1(hcd, urb, status); +		usb_put_dev(urb->dev); +	}  	if (retval == 0)  		retval = -EINPROGRESS; @@ -1525,6 +1546,17 @@ void usb_hcd_disable_endpoint(struct usb_device *udev,  		hcd->driver->endpoint_disable(hcd, ep);  } +/* Protect against drivers that try to unlink URBs after the device + * is gone, by waiting until all unlinks for @udev are finished. + * Since we don't currently track URBs by device, simply wait until + * nothing is running in the locked region of usb_hcd_unlink_urb(). + */ +void usb_hcd_synchronize_unlinks(struct usb_device *udev) +{ +	spin_lock_irq(&hcd_urb_unlink_lock); +	spin_unlock_irq(&hcd_urb_unlink_lock); +} +  /*-------------------------------------------------------------------------*/  /* called in any context */ diff --git a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h index e710ce04e22..9465e70f4dd 100644 --- a/drivers/usb/core/hcd.h +++ b/drivers/usb/core/hcd.h @@ -232,6 +232,7 @@ extern void usb_hcd_flush_endpoint(struct usb_device *udev,  		struct usb_host_endpoint *ep);  extern void usb_hcd_disable_endpoint(struct usb_device *udev,  		struct usb_host_endpoint *ep); +extern void usb_hcd_synchronize_unlinks(struct usb_device *udev);  extern int usb_hcd_get_frame_number(struct usb_device *udev);  extern struct usb_hcd *usb_create_hcd(const struct hc_driver *driver, @@ -482,4 +483,10 @@ static inline void usbmon_urb_complete(struct usb_bus *bus, struct urb *urb,   */  extern struct rw_semaphore ehci_cf_port_reset_rwsem; +/* Keep track of which host controller drivers are loaded */ +#define USB_UHCI_LOADED		0 +#define USB_OHCI_LOADED		1 +#define USB_EHCI_LOADED		2 +extern unsigned long usb_hcds_loaded; +  #endif /* __KERNEL__ */ diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index d99963873e3..b19cbfcd51d 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -77,6 +77,7 @@ struct usb_hub {  	unsigned		has_indicators:1;  	u8			indicator[USB_MAXCHILDREN];  	struct delayed_work	leds; +	struct delayed_work	init_work;  }; @@ -100,6 +101,15 @@ module_param (blinkenlights, bool, S_IRUGO);  MODULE_PARM_DESC (blinkenlights, "true to cycle leds on hubs");  /* + * Device SATA8000 FW1.0 from DATAST0R Technology Corp requires about + * 10 seconds to send reply for the initial 64-byte descriptor request. + */ +/* define initial 64-byte descriptor request timeout in milliseconds */ +static int initial_descriptor_timeout = USB_CTRL_GET_TIMEOUT; +module_param(initial_descriptor_timeout, int, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(initial_descriptor_timeout, "initial 64-byte descriptor request timeout in milliseconds (default 5000 - 5.0 seconds)"); + +/*   * As of 2.6.10 we introduce a new USB device initialization scheme which   * closely resembles the way Windows works.  Hopefully it will be compatible   * with a wider range of devices than the old scheme.  However some previously @@ -515,10 +525,14 @@ void usb_hub_tt_clear_buffer (struct usb_device *udev, int pipe)  }  EXPORT_SYMBOL_GPL(usb_hub_tt_clear_buffer); -static void hub_power_on(struct usb_hub *hub) +/* If do_delay is false, return the number of milliseconds the caller + * needs to delay. + */ +static unsigned hub_power_on(struct usb_hub *hub, bool do_delay)  {  	int port1;  	unsigned pgood_delay = hub->descriptor->bPwrOn2PwrGood * 2; +	unsigned delay;  	u16 wHubCharacteristics =  			le16_to_cpu(hub->descriptor->wHubCharacteristics); @@ -537,7 +551,10 @@ static void hub_power_on(struct usb_hub *hub)  		set_port_feature(hub->hdev, port1, USB_PORT_FEAT_POWER);  	/* Wait at least 100 msec for power to become stable */ -	msleep(max(pgood_delay, (unsigned) 100)); +	delay = max(pgood_delay, (unsigned) 100); +	if (do_delay) +		msleep(delay); +	return delay;  }  static int hub_hub_status(struct usb_hub *hub, @@ -599,21 +616,58 @@ static void hub_port_logical_disconnect(struct usb_hub *hub, int port1)  }  enum hub_activation_type { -	HUB_INIT, HUB_POST_RESET, HUB_RESUME, HUB_RESET_RESUME +	HUB_INIT, HUB_INIT2, HUB_INIT3, +	HUB_POST_RESET, HUB_RESUME, HUB_RESET_RESUME,  }; +static void hub_init_func2(struct work_struct *ws); +static void hub_init_func3(struct work_struct *ws); +  static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)  {  	struct usb_device *hdev = hub->hdev;  	int port1;  	int status;  	bool need_debounce_delay = false; +	unsigned delay; + +	/* Continue a partial initialization */ +	if (type == HUB_INIT2) +		goto init2; +	if (type == HUB_INIT3) +		goto init3;  	/* After a resume, port power should still be on.  	 * For any other type of activation, turn it on.  	 */ -	if (type != HUB_RESUME) -		hub_power_on(hub); +	if (type != HUB_RESUME) { + +		/* Speed up system boot by using a delayed_work for the +		 * hub's initial power-up delays.  This is pretty awkward +		 * and the implementation looks like a home-brewed sort of +		 * setjmp/longjmp, but it saves at least 100 ms for each +		 * root hub (assuming usbcore is compiled into the kernel +		 * rather than as a module).  It adds up. +		 * +		 * This can't be done for HUB_RESUME or HUB_RESET_RESUME +		 * because for those activation types the ports have to be +		 * operational when we return.  In theory this could be done +		 * for HUB_POST_RESET, but it's easier not to. +		 */ +		if (type == HUB_INIT) { +			delay = hub_power_on(hub, false); +			PREPARE_DELAYED_WORK(&hub->init_work, hub_init_func2); +			schedule_delayed_work(&hub->init_work, +					msecs_to_jiffies(delay)); + +			/* Suppress autosuspend until init is done */ +			to_usb_interface(hub->intfdev)->pm_usage_cnt = 1; +			return;		/* Continues at init2: below */ +		} else { +			hub_power_on(hub, true); +		} +	} + init2:  	/* Check each port and set hub->change_bits to let khubd know  	 * which ports need attention. @@ -692,9 +746,20 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)  	 * If any port-status changes do occur during this delay, khubd  	 * will see them later and handle them normally.  	 */ -	if (need_debounce_delay) -		msleep(HUB_DEBOUNCE_STABLE); - +	if (need_debounce_delay) { +		delay = HUB_DEBOUNCE_STABLE; + +		/* Don't do a long sleep inside a workqueue routine */ +		if (type == HUB_INIT2) { +			PREPARE_DELAYED_WORK(&hub->init_work, hub_init_func3); +			schedule_delayed_work(&hub->init_work, +					msecs_to_jiffies(delay)); +			return;		/* Continues at init3: below */ +		} else { +			msleep(delay); +		} +	} + init3:  	hub->quiescing = 0;  	status = usb_submit_urb(hub->urb, GFP_NOIO); @@ -707,6 +772,21 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)  	kick_khubd(hub);  } +/* Implement the continuations for the delays above */ +static void hub_init_func2(struct work_struct *ws) +{ +	struct usb_hub *hub = container_of(ws, struct usb_hub, init_work.work); + +	hub_activate(hub, HUB_INIT2); +} + +static void hub_init_func3(struct work_struct *ws) +{ +	struct usb_hub *hub = container_of(ws, struct usb_hub, init_work.work); + +	hub_activate(hub, HUB_INIT3); +} +  enum hub_quiescing_type {  	HUB_DISCONNECT, HUB_PRE_RESET, HUB_SUSPEND  }; @@ -716,6 +796,8 @@ static void hub_quiesce(struct usb_hub *hub, enum hub_quiescing_type type)  	struct usb_device *hdev = hub->hdev;  	int i; +	cancel_delayed_work_sync(&hub->init_work); +  	/* khubd and related activity won't re-trigger */  	hub->quiescing = 1; @@ -1099,6 +1181,7 @@ descriptor_error:  	hub->intfdev = &intf->dev;  	hub->hdev = hdev;  	INIT_DELAYED_WORK(&hub->leds, led_work); +	INIT_DELAYED_WORK(&hub->init_work, NULL);  	usb_get_intf(intf);  	usb_set_intfdata (intf, hub); @@ -1349,6 +1432,7 @@ void usb_disconnect(struct usb_device **pdev)  	 */  	dev_dbg (&udev->dev, "unregistering device\n");  	usb_disable_device(udev, 0); +	usb_hcd_synchronize_unlinks(udev);  	usb_unlock_device(udev); @@ -2467,7 +2551,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,  					USB_REQ_GET_DESCRIPTOR, USB_DIR_IN,  					USB_DT_DEVICE << 8, 0,  					buf, GET_DESCRIPTOR_BUFSIZE, -					USB_CTRL_GET_TIMEOUT); +					initial_descriptor_timeout);  				switch (buf->bMaxPacketSize0) {  				case 8: case 16: case 32: case 64: case 255:  					if (buf->bDescriptorType == @@ -3035,7 +3119,7 @@ static void hub_events(void)  					i);  				clear_port_feature(hdev, i,  					USB_PORT_FEAT_C_OVER_CURRENT); -				hub_power_on(hub); +				hub_power_on(hub, true);  			}  			if (portchange & USB_PORT_STAT_C_RESET) { @@ -3070,7 +3154,7 @@ static void hub_events(void)  				dev_dbg (hub_dev, "overcurrent change\n");  				msleep(500);	/* Cool down */  				clear_hub_feature(hdev, C_HUB_OVER_CURRENT); -                        	hub_power_on(hub); +                        	hub_power_on(hub, true);  			}  		} @@ -3424,7 +3508,7 @@ int usb_reset_device(struct usb_device *udev)  						USB_INTERFACE_BOUND)  					rebind = 1;  			} -			if (rebind) +			if (ret == 0 && rebind)  				usb_rebind_intf(cintf);  		}  	} diff --git a/drivers/usb/core/inode.c b/drivers/usb/core/inode.c index db410e92c80..94632264dcc 100644 --- a/drivers/usb/core/inode.c +++ b/drivers/usb/core/inode.c @@ -97,7 +97,7 @@ enum {  	Opt_err,  }; -static match_table_t tokens = { +static const match_table_t tokens = {  	{Opt_devuid, "devuid=%u"},  	{Opt_devgid, "devgid=%u"},  	{Opt_devmode, "devmode=%o"}, @@ -180,8 +180,8 @@ static int parse_options(struct super_block *s, char *data)  			listmode = option & S_IRWXUGO;  			break;  		default: -			err("usbfs: unrecognised mount option \"%s\" " -			    "or missing value\n", p); +			printk(KERN_ERR "usbfs: unrecognised mount option " +			       "\"%s\" or missing value\n", p);  			return -EINVAL;  		}  	} @@ -240,7 +240,9 @@ static void update_sb(struct super_block *sb)  				update_special(bus);  				break;  			default: -				warn("Unknown node %s mode %x found on remount!\n",bus->d_name.name,bus->d_inode->i_mode); +				printk(KERN_WARNING "usbfs: Unknown node %s " +				       "mode %x found on remount!\n", +				       bus->d_name.name, bus->d_inode->i_mode);  				break;  			}  		} @@ -259,7 +261,7 @@ static int remount(struct super_block *sb, int *flags, char *data)  		return 0;  	if (parse_options(sb, data)) { -		warn("usbfs: mount parameter error:"); +		printk(KERN_WARNING "usbfs: mount parameter error.\n");  		return -EINVAL;  	} @@ -599,7 +601,7 @@ static int create_special_files (void)  	/* create the devices special file */  	retval = simple_pin_fs(&usb_fs_type, &usbfs_mount, &usbfs_mount_count);  	if (retval) { -		err ("Unable to get usbfs mount"); +		printk(KERN_ERR "Unable to get usbfs mount\n");  		goto exit;  	} @@ -611,7 +613,7 @@ static int create_special_files (void)  					       NULL, &usbfs_devices_fops,  					       listuid, listgid);  	if (devices_usbfs_dentry == NULL) { -		err ("Unable to create devices usbfs file"); +		printk(KERN_ERR "Unable to create devices usbfs file\n");  		retval = -ENODEV;  		goto error_clean_mounts;  	} @@ -663,7 +665,7 @@ static void usbfs_add_bus(struct usb_bus *bus)  	bus->usbfs_dentry = fs_create_file (name, busmode | S_IFDIR, parent,  					    bus, NULL, busuid, busgid);  	if (bus->usbfs_dentry == NULL) { -		err ("error creating usbfs bus entry"); +		printk(KERN_ERR "Error creating usbfs bus entry\n");  		return;  	}  } @@ -694,7 +696,7 @@ static void usbfs_add_device(struct usb_device *dev)  					    &usbdev_file_operations,  					    devuid, devgid);  	if (dev->usbfs_dentry == NULL) { -		err ("error creating usbfs device entry"); +		printk(KERN_ERR "Error creating usbfs device entry\n");  		return;  	} diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index 286b4431a09..6d1048faf08 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -1091,6 +1091,7 @@ void usb_disable_device(struct usb_device *dev, int skip_ep0)  				continue;  			dev_dbg(&dev->dev, "unregistering interface %s\n",  				dev_name(&interface->dev)); +			interface->unregistering = 1;  			usb_remove_sysfs_intf_files(interface);  			device_del(&interface->dev);  		} @@ -1204,7 +1205,8 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate)  	alt = usb_altnum_to_altsetting(iface, alternate);  	if (!alt) { -		warn("selecting invalid altsetting %d", alternate); +		dev_warn(&dev->dev, "selecting invalid altsetting %d", +			 alternate);  		return -EINVAL;  	} diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c index 5e1f5d55bf0..4fb65fdc9dc 100644 --- a/drivers/usb/core/sysfs.c +++ b/drivers/usb/core/sysfs.c @@ -743,6 +743,29 @@ static ssize_t show_modalias(struct device *dev,  }  static DEVICE_ATTR(modalias, S_IRUGO, show_modalias, NULL); +static ssize_t show_supports_autosuspend(struct device *dev, +		struct device_attribute *attr, char *buf) +{ +	struct usb_interface *intf; +	struct usb_device *udev; +	int ret; + +	intf = to_usb_interface(dev); +	udev = interface_to_usbdev(intf); + +	usb_lock_device(udev); +	/* Devices will be autosuspended even when an interface isn't claimed */ +	if (!intf->dev.driver || +			to_usb_driver(intf->dev.driver)->supports_autosuspend) +		ret = sprintf(buf, "%u\n", 1); +	else +		ret = sprintf(buf, "%u\n", 0); +	usb_unlock_device(udev); + +	return ret; +} +static DEVICE_ATTR(supports_autosuspend, S_IRUGO, show_supports_autosuspend, NULL); +  static struct attribute *intf_attrs[] = {  	&dev_attr_bInterfaceNumber.attr,  	&dev_attr_bAlternateSetting.attr, @@ -751,6 +774,7 @@ static struct attribute *intf_attrs[] = {  	&dev_attr_bInterfaceSubClass.attr,  	&dev_attr_bInterfaceProtocol.attr,  	&dev_attr_modalias.attr, +	&dev_attr_supports_autosuspend.attr,  	NULL,  };  static struct attribute_group intf_attr_grp = { @@ -816,7 +840,7 @@ int usb_create_sysfs_intf_files(struct usb_interface *intf)  	struct usb_host_interface *alt = intf->cur_altsetting;  	int retval; -	if (intf->sysfs_files_created) +	if (intf->sysfs_files_created || intf->unregistering)  		return 0;  	/* The interface string may be present in some altsettings diff --git a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c index 47111e88f79..1f68af9db3f 100644 --- a/drivers/usb/core/urb.c +++ b/drivers/usb/core/urb.c @@ -10,6 +10,8 @@  #define to_urb(d) container_of(d, struct urb, kref) +static DEFINE_SPINLOCK(usb_reject_lock); +  static void urb_destroy(struct kref *kref)  {  	struct urb *urb = to_urb(kref); @@ -68,7 +70,7 @@ struct urb *usb_alloc_urb(int iso_packets, gfp_t mem_flags)  		iso_packets * sizeof(struct usb_iso_packet_descriptor),  		mem_flags);  	if (!urb) { -		err("alloc_urb: kmalloc failed"); +		printk(KERN_ERR "alloc_urb: kmalloc failed\n");  		return NULL;  	}  	usb_init_urb(urb); @@ -83,8 +85,8 @@ EXPORT_SYMBOL_GPL(usb_alloc_urb);   * Must be called when a user of a urb is finished with it.  When the last user   * of the urb calls this function, the memory of the urb is freed.   * - * Note: The transfer buffer associated with the urb is not freed, that must be - * done elsewhere. + * Note: The transfer buffer associated with the urb is not freed unless the + * URB_FREE_BUFFER transfer flag is set.   */  void usb_free_urb(struct urb *urb)  { @@ -127,6 +129,13 @@ void usb_anchor_urb(struct urb *urb, struct usb_anchor *anchor)  	usb_get_urb(urb);  	list_add_tail(&urb->anchor_list, &anchor->urb_list);  	urb->anchor = anchor; + +	if (unlikely(anchor->poisoned)) { +		spin_lock(&usb_reject_lock); +		urb->reject++; +		spin_unlock(&usb_reject_lock); +	} +  	spin_unlock_irqrestore(&anchor->lock, flags);  }  EXPORT_SYMBOL_GPL(usb_anchor_urb); @@ -398,7 +407,7 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags)  	/* fail if submitter gave bogus flags */  	if (urb->transfer_flags != orig_flags) { -		err("BOGUS urb flags, %x --> %x", +		dev_err(&dev->dev, "BOGUS urb flags, %x --> %x\n",  			orig_flags, urb->transfer_flags);  		return -EINVAL;  	} @@ -465,6 +474,12 @@ EXPORT_SYMBOL_GPL(usb_submit_urb);   * indicating that the request has been canceled (rather than any other   * code).   * + * Drivers should not call this routine or related routines, such as + * usb_kill_urb() or usb_unlink_anchored_urbs(), after their disconnect + * method has returned.  The disconnect function should synchronize with + * a driver's I/O routines to insure that all URB-related activity has + * completed before it returns. + *   * This request is always asynchronous.  Success is indicated by   * returning -EINPROGRESS, at which time the URB will probably not yet   * have been given back to the device driver.  When it is eventually @@ -541,33 +556,87 @@ EXPORT_SYMBOL_GPL(usb_unlink_urb);   * This routine may not be used in an interrupt context (such as a bottom   * half or a completion handler), or when holding a spinlock, or in other   * situations where the caller can't schedule(). + * + * This routine should not be called by a driver after its disconnect + * method has returned.   */  void usb_kill_urb(struct urb *urb)  { -	static DEFINE_MUTEX(reject_mutex); -  	might_sleep();  	if (!(urb && urb->dev && urb->ep))  		return; -	mutex_lock(&reject_mutex); +	spin_lock_irq(&usb_reject_lock);  	++urb->reject; -	mutex_unlock(&reject_mutex); +	spin_unlock_irq(&usb_reject_lock);  	usb_hcd_unlink_urb(urb, -ENOENT);  	wait_event(usb_kill_urb_queue, atomic_read(&urb->use_count) == 0); -	mutex_lock(&reject_mutex); +	spin_lock_irq(&usb_reject_lock);  	--urb->reject; -	mutex_unlock(&reject_mutex); +	spin_unlock_irq(&usb_reject_lock);  }  EXPORT_SYMBOL_GPL(usb_kill_urb);  /** + * usb_poison_urb - reliably kill a transfer and prevent further use of an URB + * @urb: pointer to URB describing a previously submitted request, + *	may be NULL + * + * This routine cancels an in-progress request.  It is guaranteed that + * upon return all completion handlers will have finished and the URB + * will be totally idle and cannot be reused.  These features make + * this an ideal way to stop I/O in a disconnect() callback. + * If the request has not already finished or been unlinked + * the completion handler will see urb->status == -ENOENT. + * + * After and while the routine runs, attempts to resubmit the URB will fail + * with error -EPERM.  Thus even if the URB's completion handler always + * tries to resubmit, it will not succeed and the URB will become idle. + * + * This routine may not be used in an interrupt context (such as a bottom + * half or a completion handler), or when holding a spinlock, or in other + * situations where the caller can't schedule(). + * + * This routine should not be called by a driver after its disconnect + * method has returned. + */ +void usb_poison_urb(struct urb *urb) +{ +	might_sleep(); +	if (!(urb && urb->dev && urb->ep)) +		return; +	spin_lock_irq(&usb_reject_lock); +	++urb->reject; +	spin_unlock_irq(&usb_reject_lock); + +	usb_hcd_unlink_urb(urb, -ENOENT); +	wait_event(usb_kill_urb_queue, atomic_read(&urb->use_count) == 0); +} +EXPORT_SYMBOL_GPL(usb_poison_urb); + +void usb_unpoison_urb(struct urb *urb) +{ +	unsigned long flags; + +	if (!urb) +		return; + +	spin_lock_irqsave(&usb_reject_lock, flags); +	--urb->reject; +	spin_unlock_irqrestore(&usb_reject_lock, flags); +} +EXPORT_SYMBOL_GPL(usb_unpoison_urb); + +/**   * usb_kill_anchored_urbs - cancel transfer requests en masse   * @anchor: anchor the requests are bound to   *   * this allows all outstanding URBs to be killed starting   * from the back of the queue + * + * This routine should not be called by a driver after its disconnect + * method has returned.   */  void usb_kill_anchored_urbs(struct usb_anchor *anchor)  { @@ -589,6 +658,39 @@ void usb_kill_anchored_urbs(struct usb_anchor *anchor)  }  EXPORT_SYMBOL_GPL(usb_kill_anchored_urbs); + +/** + * usb_poison_anchored_urbs - cease all traffic from an anchor + * @anchor: anchor the requests are bound to + * + * this allows all outstanding URBs to be poisoned starting + * from the back of the queue. Newly added URBs will also be + * poisoned + * + * This routine should not be called by a driver after its disconnect + * method has returned. + */ +void usb_poison_anchored_urbs(struct usb_anchor *anchor) +{ +	struct urb *victim; + +	spin_lock_irq(&anchor->lock); +	anchor->poisoned = 1; +	while (!list_empty(&anchor->urb_list)) { +		victim = list_entry(anchor->urb_list.prev, struct urb, +				    anchor_list); +		/* we must make sure the URB isn't freed before we kill it*/ +		usb_get_urb(victim); +		spin_unlock_irq(&anchor->lock); +		/* this will unanchor the URB */ +		usb_poison_urb(victim); +		usb_put_urb(victim); +		spin_lock_irq(&anchor->lock); +	} +	spin_unlock_irq(&anchor->lock); +} +EXPORT_SYMBOL_GPL(usb_poison_anchored_urbs); +  /**   * usb_unlink_anchored_urbs - asynchronously cancel transfer requests en masse   * @anchor: anchor the requests are bound to @@ -597,6 +699,9 @@ EXPORT_SYMBOL_GPL(usb_kill_anchored_urbs);   * from the back of the queue. This function is asynchronous.   * The unlinking is just tiggered. It may happen after this   * function has returned. + * + * This routine should not be called by a driver after its disconnect + * method has returned.   */  void usb_unlink_anchored_urbs(struct usb_anchor *anchor)  { @@ -633,3 +738,73 @@ int usb_wait_anchor_empty_timeout(struct usb_anchor *anchor,  				  msecs_to_jiffies(timeout));  }  EXPORT_SYMBOL_GPL(usb_wait_anchor_empty_timeout); + +/** + * usb_get_from_anchor - get an anchor's oldest urb + * @anchor: the anchor whose urb you want + * + * this will take the oldest urb from an anchor, + * unanchor and return it + */ +struct urb *usb_get_from_anchor(struct usb_anchor *anchor) +{ +	struct urb *victim; +	unsigned long flags; + +	spin_lock_irqsave(&anchor->lock, flags); +	if (!list_empty(&anchor->urb_list)) { +		victim = list_entry(anchor->urb_list.next, struct urb, +				    anchor_list); +		usb_get_urb(victim); +		spin_unlock_irqrestore(&anchor->lock, flags); +		usb_unanchor_urb(victim); +	} else { +		spin_unlock_irqrestore(&anchor->lock, flags); +		victim = NULL; +	} + +	return victim; +} + +EXPORT_SYMBOL_GPL(usb_get_from_anchor); + +/** + * usb_scuttle_anchored_urbs - unanchor all an anchor's urbs + * @anchor: the anchor whose urbs you want to unanchor + * + * use this to get rid of all an anchor's urbs + */ +void usb_scuttle_anchored_urbs(struct usb_anchor *anchor) +{ +	struct urb *victim; +	unsigned long flags; + +	spin_lock_irqsave(&anchor->lock, flags); +	while (!list_empty(&anchor->urb_list)) { +		victim = list_entry(anchor->urb_list.prev, struct urb, +				    anchor_list); +		usb_get_urb(victim); +		spin_unlock_irqrestore(&anchor->lock, flags); +		/* this may free the URB */ +		usb_unanchor_urb(victim); +		usb_put_urb(victim); +		spin_lock_irqsave(&anchor->lock, flags); +	} +	spin_unlock_irqrestore(&anchor->lock, flags); +} + +EXPORT_SYMBOL_GPL(usb_scuttle_anchored_urbs); + +/** + * usb_anchor_empty - is an anchor empty + * @anchor: the anchor you want to query + * + * returns 1 if the anchor has no urbs associated with it + */ +int usb_anchor_empty(struct usb_anchor *anchor) +{ +	return list_empty(&anchor->urb_list); +} + +EXPORT_SYMBOL_GPL(usb_anchor_empty); + diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index acc95b2ac6f..dd4cd5a5137 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -45,7 +45,7 @@ if USB_GADGET  config USB_GADGET_DEBUG  	boolean "Debugging messages (DEVELOPMENT)" -	depends on USB_GADGET && DEBUG_KERNEL +	depends on DEBUG_KERNEL  	help  	   Many controller and gadget drivers will print some debugging  	   messages if you use this option to ask for those messages. @@ -59,7 +59,7 @@ config USB_GADGET_DEBUG  config USB_GADGET_DEBUG_FILES  	boolean "Debugging information files (DEVELOPMENT)" -	depends on USB_GADGET && PROC_FS +	depends on PROC_FS  	help  	   Some of the drivers in the "gadget" framework can expose  	   debugging information in files such as /proc/driver/udc @@ -70,7 +70,7 @@ config USB_GADGET_DEBUG_FILES  config USB_GADGET_DEBUG_FS  	boolean "Debugging information files in debugfs (DEVELOPMENT)" -	depends on USB_GADGET && DEBUG_FS +	depends on DEBUG_FS  	help  	   Some of the drivers in the "gadget" framework can expose  	   debugging information in files under /sys/kernel/debug/. @@ -79,12 +79,36 @@ config USB_GADGET_DEBUG_FS  	   Enable these files by choosing "Y" here.  If in doubt, or  	   to conserve kernel memory, say "N". +config USB_GADGET_VBUS_DRAW +	int "Maximum VBUS Power usage (2-500 mA)" +	range 2 500 +	default 2 +	help +	   Some devices need to draw power from USB when they are +	   configured, perhaps to operate circuitry or to recharge +	   batteries.  This is in addition to any local power supply, +	   such as an AC adapter or batteries. + +	   Enter the maximum power your device draws through USB, in +	   milliAmperes.  The permitted range of values is 2 - 500 mA; +	   0 mA would be legal, but can make some hosts misbehave. + +	   This value will be used except for system-specific gadget +	   drivers that have more specific information. +  config	USB_GADGET_SELECTED  	boolean  #  # USB Peripheral Controller Support  # +# The order here is alphabetical, except that integrated controllers go +# before discrete ones so they will be the initial/default value: +#   - integrated/SOC controllers first +#   - licensed IP used in both SOC and discrete versions +#   - discrete ones (including all PCI-only controllers) +#   - debug/dummy gadget+hcd is last. +#  choice  	prompt "USB Peripheral Controller"  	depends on USB_GADGET @@ -94,26 +118,27 @@ choice  	   Many controller drivers are platform-specific; these  	   often need board-specific hooks. -config USB_GADGET_AMD5536UDC -	boolean "AMD5536 UDC" -	depends on PCI -	select USB_GADGET_DUALSPEED +# +# Integrated controllers +# + +config USB_GADGET_AT91 +	boolean "Atmel AT91 USB Device Port" +	depends on ARCH_AT91 && !ARCH_AT91SAM9RL && !ARCH_AT91CAP9 +	select USB_GADGET_SELECTED  	help -	   The AMD5536 UDC is part of the AMD Geode CS5536, an x86 southbridge. -	   It is a USB Highspeed DMA capable USB device controller. Beside ep0 -	   it provides 4 IN and 4 OUT endpoints (bulk or interrupt type). -	   The UDC port supports OTG operation, and may be used as a host port -	   if it's not being used to implement peripheral or OTG roles. +	   Many Atmel AT91 processors (such as the AT91RM2000) have a +	   full speed USB Device Port with support for five configurable +	   endpoints (plus endpoint zero).  	   Say "y" to link the driver statically, or "m" to build a -	   dynamically linked module called "amd5536udc" and force all +	   dynamically linked module called "at91_udc" and force all  	   gadget drivers to also be dynamically linked. -config USB_AMD5536UDC +config USB_AT91  	tristate -	depends on USB_GADGET_AMD5536UDC +	depends on USB_GADGET_AT91  	default USB_GADGET -	select USB_GADGET_SELECTED  config USB_GADGET_ATMEL_USBA  	boolean "Atmel USBA" @@ -150,28 +175,50 @@ config USB_FSL_USB2  	default USB_GADGET  	select USB_GADGET_SELECTED -config USB_GADGET_NET2280 -	boolean "NetChip 228x" -	depends on PCI -	select USB_GADGET_DUALSPEED +config USB_GADGET_LH7A40X +	boolean "LH7A40X" +	depends on ARCH_LH7A40X  	help -	   NetChip 2280 / 2282 is a PCI based USB peripheral controller which -	   supports both full and high speed USB 2.0 data transfers.   -	    -	   It has six configurable endpoints, as well as endpoint zero -	   (for control transfers) and several endpoints with dedicated -	   functions. +	   This driver provides USB Device Controller driver for LH7A40x + +config USB_LH7A40X +	tristate +	depends on USB_GADGET_LH7A40X +	default USB_GADGET +	select USB_GADGET_SELECTED + +config USB_GADGET_OMAP +	boolean "OMAP USB Device Controller" +	depends on ARCH_OMAP +	select ISP1301_OMAP if MACH_OMAP_H2 || MACH_OMAP_H3 || MACH_OMAP_H4_OTG +	help +	   Many Texas Instruments OMAP processors have flexible full +	   speed USB device controllers, with support for up to 30 +	   endpoints (plus endpoint zero).  This driver supports the +	   controller in the OMAP 1611, and should work with controllers +	   in other OMAP processors too, given minor tweaks.  	   Say "y" to link the driver statically, or "m" to build a -	   dynamically linked module called "net2280" and force all +	   dynamically linked module called "omap_udc" and force all  	   gadget drivers to also be dynamically linked. -config USB_NET2280 +config USB_OMAP  	tristate -	depends on USB_GADGET_NET2280 +	depends on USB_GADGET_OMAP  	default USB_GADGET  	select USB_GADGET_SELECTED +config USB_OTG +	boolean "OTG Support" +	depends on USB_GADGET_OMAP && ARCH_OMAP_OTG && USB_OHCI_HCD +	help +	   The most notable feature of USB OTG is support for a +	   "Dual-Role" device, which can act as either a device +	   or a host.  The initial role choice can be changed +	   later, when two dual-role devices talk to each other. + +	   Select this only if your OMAP board has a Mini-AB connector. +  config USB_GADGET_PXA25X  	boolean "PXA 25x or IXP 4xx"  	depends on (ARCH_PXA && PXA25x) || ARCH_IXP4XX @@ -203,34 +250,6 @@ config USB_PXA25X_SMALL  	default y if USB_ETH  	default y if USB_G_SERIAL -config USB_GADGET_M66592 -	boolean "Renesas M66592 USB Peripheral Controller" -	select USB_GADGET_DUALSPEED -	help -	   M66592 is a discrete USB peripheral controller chip that -	   supports both full and high speed USB 2.0 data transfers. -	   It has seven configurable endpoints, and endpoint zero. - -	   Say "y" to link the driver statically, or "m" to build a -	   dynamically linked module called "m66592_udc" and force all -	   gadget drivers to also be dynamically linked. - -config USB_M66592 -	tristate -	depends on USB_GADGET_M66592 -	default USB_GADGET -	select USB_GADGET_SELECTED - -config SUPERH_BUILT_IN_M66592 -	boolean "Enable SuperH built-in USB like the M66592" -	depends on USB_GADGET_M66592 && CPU_SUBTYPE_SH7722 -	help -	   SH7722 has USB like the M66592. - -	   The transfer rate is very slow when use "Ethernet Gadget". -	   However, this problem is improved if change a value of -	   NET_IP_ALIGN to 4. -  config USB_GADGET_PXA27X  	boolean "PXA 27x"  	depends on ARCH_PXA && PXA27x @@ -251,40 +270,32 @@ config USB_PXA27X  	default USB_GADGET  	select USB_GADGET_SELECTED -config USB_GADGET_GOKU -	boolean "Toshiba TC86C001 'Goku-S'" -	depends on PCI +config USB_GADGET_S3C2410 +	boolean "S3C2410 USB Device Controller" +	depends on ARCH_S3C2410  	help -	   The Toshiba TC86C001 is a PCI device which includes controllers -	   for full speed USB devices, IDE, I2C, SIO, plus a USB host (OHCI). -	    -	   The device controller has three configurable (bulk or interrupt) -	   endpoints, plus endpoint zero (for control transfers). +	  Samsung's S3C2410 is an ARM-4 processor with an integrated +	  full speed USB 1.1 device controller.  It has 4 configurable +	  endpoints, as well as endpoint zero (for control transfers). -	   Say "y" to link the driver statically, or "m" to build a -	   dynamically linked module called "goku_udc" and to force all -	   gadget drivers to also be dynamically linked. +	  This driver has been tested on the S3C2410, S3C2412, and +	  S3C2440 processors. -config USB_GOKU +config USB_S3C2410  	tristate -	depends on USB_GADGET_GOKU +	depends on USB_GADGET_S3C2410  	default USB_GADGET  	select USB_GADGET_SELECTED +config USB_S3C2410_DEBUG +	boolean "S3C2410 udc debug messages" +	depends on USB_GADGET_S3C2410 -config USB_GADGET_LH7A40X -	boolean "LH7A40X" -	depends on ARCH_LH7A40X -	help -    This driver provides USB Device Controller driver for LH7A40x - -config USB_LH7A40X -	tristate -	depends on USB_GADGET_LH7A40X -	default USB_GADGET -	select USB_GADGET_SELECTED +# +# Controllers available in both integrated and discrete versions +# -# built in ../musb along with host support +# musb builds in ../musb along with host support  config USB_GADGET_MUSB_HDRC  	boolean "Inventra HDRC USB Peripheral (TI, ...)"  	depends on USB_MUSB_HDRC && (USB_MUSB_PERIPHERAL || USB_MUSB_OTG) @@ -294,76 +305,124 @@ config USB_GADGET_MUSB_HDRC  	  This OTG-capable silicon IP is used in dual designs including  	  the TI DaVinci, OMAP 243x, OMAP 343x, and TUSB 6010. -config USB_GADGET_OMAP -	boolean "OMAP USB Device Controller" -	depends on ARCH_OMAP -	select ISP1301_OMAP if MACH_OMAP_H2 || MACH_OMAP_H3 +config USB_GADGET_M66592 +	boolean "Renesas M66592 USB Peripheral Controller" +	select USB_GADGET_DUALSPEED  	help -	   Many Texas Instruments OMAP processors have flexible full -	   speed USB device controllers, with support for up to 30 -	   endpoints (plus endpoint zero).  This driver supports the -	   controller in the OMAP 1611, and should work with controllers -	   in other OMAP processors too, given minor tweaks. +	   M66592 is a discrete USB peripheral controller chip that +	   supports both full and high speed USB 2.0 data transfers. +	   It has seven configurable endpoints, and endpoint zero.  	   Say "y" to link the driver statically, or "m" to build a -	   dynamically linked module called "omap_udc" and force all +	   dynamically linked module called "m66592_udc" and force all  	   gadget drivers to also be dynamically linked. -config USB_OMAP +config USB_M66592  	tristate -	depends on USB_GADGET_OMAP +	depends on USB_GADGET_M66592  	default USB_GADGET  	select USB_GADGET_SELECTED -config USB_OTG -	boolean "OTG Support" -	depends on USB_GADGET_OMAP && ARCH_OMAP_OTG && USB_OHCI_HCD +config SUPERH_BUILT_IN_M66592 +	boolean "Enable SuperH built-in USB like the M66592" +	depends on USB_GADGET_M66592 && CPU_SUBTYPE_SH7722  	help -	   The most notable feature of USB OTG is support for a -	   "Dual-Role" device, which can act as either a device -	   or a host.  The initial role choice can be changed -	   later, when two dual-role devices talk to each other. +	   SH7722 has USB like the M66592. -	   Select this only if your OMAP board has a Mini-AB connector. +	   The transfer rate is very slow when use "Ethernet Gadget". +	   However, this problem is improved if change a value of +	   NET_IP_ALIGN to 4. -config USB_GADGET_S3C2410 -	boolean "S3C2410 USB Device Controller" -	depends on ARCH_S3C2410 +# +# Controllers available only in discrete form (and all PCI controllers) +# + +config USB_GADGET_AMD5536UDC +	boolean "AMD5536 UDC" +	depends on PCI +	select USB_GADGET_DUALSPEED  	help -	  Samsung's S3C2410 is an ARM-4 processor with an integrated -	  full speed USB 1.1 device controller.  It has 4 configurable -	  endpoints, as well as endpoint zero (for control transfers). +	   The AMD5536 UDC is part of the AMD Geode CS5536, an x86 southbridge. +	   It is a USB Highspeed DMA capable USB device controller. Beside ep0 +	   it provides 4 IN and 4 OUT endpoints (bulk or interrupt type). +	   The UDC port supports OTG operation, and may be used as a host port +	   if it's not being used to implement peripheral or OTG roles. -	  This driver has been tested on the S3C2410, S3C2412, and -	  S3C2440 processors. +	   Say "y" to link the driver statically, or "m" to build a +	   dynamically linked module called "amd5536udc" and force all +	   gadget drivers to also be dynamically linked. -config USB_S3C2410 +config USB_AMD5536UDC  	tristate -	depends on USB_GADGET_S3C2410 +	depends on USB_GADGET_AMD5536UDC  	default USB_GADGET  	select USB_GADGET_SELECTED -config USB_S3C2410_DEBUG -	boolean "S3C2410 udc debug messages" -	depends on USB_GADGET_S3C2410 +config USB_GADGET_FSL_QE +	boolean "Freescale QE/CPM USB Device Controller" +	depends on FSL_SOC && (QUICC_ENGINE || CPM) +	help +	   Some of Freescale PowerPC processors have a Full Speed +	   QE/CPM2 USB controller, which support device mode with 4 +	   programmable endpoints. This driver supports the +	   controller in the MPC8360 and MPC8272, and should work with +	   controllers having QE or CPM2, given minor tweaks. -config USB_GADGET_AT91 -	boolean "AT91 USB Device Port" -	depends on ARCH_AT91 && !ARCH_AT91SAM9RL && !ARCH_AT91CAP9 +	   Set CONFIG_USB_GADGET to "m" to build this driver as a +	   dynmically linked module called "fsl_qe_udc". + +config USB_FSL_QE +	tristate +	depends on USB_GADGET_FSL_QE +	default USB_GADGET  	select USB_GADGET_SELECTED + +config USB_GADGET_NET2280 +	boolean "NetChip 228x" +	depends on PCI +	select USB_GADGET_DUALSPEED  	help -	   Many Atmel AT91 processors (such as the AT91RM2000) have a -	   full speed USB Device Port with support for five configurable -	   endpoints (plus endpoint zero). +	   NetChip 2280 / 2282 is a PCI based USB peripheral controller which +	   supports both full and high speed USB 2.0 data transfers. + +	   It has six configurable endpoints, as well as endpoint zero +	   (for control transfers) and several endpoints with dedicated +	   functions.  	   Say "y" to link the driver statically, or "m" to build a -	   dynamically linked module called "at91_udc" and force all +	   dynamically linked module called "net2280" and force all  	   gadget drivers to also be dynamically linked. -config USB_AT91 +config USB_NET2280  	tristate -	depends on USB_GADGET_AT91 +	depends on USB_GADGET_NET2280 +	default USB_GADGET +	select USB_GADGET_SELECTED + +config USB_GADGET_GOKU +	boolean "Toshiba TC86C001 'Goku-S'" +	depends on PCI +	help +	   The Toshiba TC86C001 is a PCI device which includes controllers +	   for full speed USB devices, IDE, I2C, SIO, plus a USB host (OHCI). + +	   The device controller has three configurable (bulk or interrupt) +	   endpoints, plus endpoint zero (for control transfers). + +	   Say "y" to link the driver statically, or "m" to build a +	   dynamically linked module called "goku_udc" and to force all +	   gadget drivers to also be dynamically linked. + +config USB_GOKU +	tristate +	depends on USB_GADGET_GOKU  	default USB_GADGET +	select USB_GADGET_SELECTED + + +# +# LAST -- dummy/emulated controller +#  config USB_GADGET_DUMMY_HCD  	boolean "Dummy HCD (DEVELOPMENT)" @@ -553,19 +612,23 @@ config USB_FILE_STORAGE_TEST  	  normal operation.  config USB_G_SERIAL -	tristate "Serial Gadget (with CDC ACM support)" +	tristate "Serial Gadget (with CDC ACM and CDC OBEX support)"  	help  	  The Serial Gadget talks to the Linux-USB generic serial driver.  	  This driver supports a CDC-ACM module option, which can be used  	  to interoperate with MS-Windows hosts or with the Linux-USB  	  "cdc-acm" driver. +	  This driver also supports a CDC-OBEX option.  You will need a +	  user space OBEX server talking to /dev/ttyGS*, since the kernel +	  itself doesn't implement the OBEX protocol. +  	  Say "y" to link the driver statically, or "m" to build a  	  dynamically linked module called "g_serial".  	  For more information, see Documentation/usb/gadget_serial.txt  	  which includes instructions and a "driver info file" needed to -	  make MS-Windows work with this driver. +	  make MS-Windows work with CDC ACM.  config USB_MIDI_GADGET  	tristate "MIDI Gadget (EXPERIMENTAL)" diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index fcb5cb9094d..bd4041b47dc 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -18,28 +18,20 @@ obj-$(CONFIG_USB_AT91)		+= at91_udc.o  obj-$(CONFIG_USB_ATMEL_USBA)	+= atmel_usba_udc.o  obj-$(CONFIG_USB_FSL_USB2)	+= fsl_usb2_udc.o  obj-$(CONFIG_USB_M66592)	+= m66592-udc.o +obj-$(CONFIG_USB_FSL_QE)	+= fsl_qe_udc.o  #  # USB gadget drivers  # -C_UTILS =	composite.o usbstring.o config.o epautoconf.o - -g_zero-objs			:= zero.o f_sourcesink.o f_loopback.o $(C_UTILS) -g_ether-objs			:= ether.o u_ether.o f_subset.o f_ecm.o $(C_UTILS) -g_serial-objs			:= serial.o u_serial.o f_acm.o f_serial.o $(C_UTILS) -g_midi-objs			:= gmidi.o usbstring.o config.o epautoconf.o +g_zero-objs			:= zero.o +g_ether-objs			:= ether.o +g_serial-objs			:= serial.o +g_midi-objs			:= gmidi.o  gadgetfs-objs			:= inode.o -g_file_storage-objs		:= file_storage.o usbstring.o config.o \ -					epautoconf.o -g_printer-objs			:= printer.o usbstring.o config.o \ -					epautoconf.o -g_cdc-objs			:= cdc2.o u_ether.o f_ecm.o \ -					u_serial.o f_acm.o $(C_UTILS) +g_file_storage-objs		:= file_storage.o +g_printer-objs			:= printer.o +g_cdc-objs			:= cdc2.o -ifeq ($(CONFIG_USB_ETH_RNDIS),y) -	g_ether-objs		+= f_rndis.o rndis.o -endif -   obj-$(CONFIG_USB_ZERO)		+= g_zero.o  obj-$(CONFIG_USB_ETH)		+= g_ether.o  obj-$(CONFIG_USB_GADGETFS)	+= gadgetfs.o diff --git a/drivers/usb/gadget/cdc2.c b/drivers/usb/gadget/cdc2.c index a39a4b940c3..5495b171cf2 100644 --- a/drivers/usb/gadget/cdc2.c +++ b/drivers/usb/gadget/cdc2.c @@ -43,6 +43,25 @@  /*-------------------------------------------------------------------------*/ +/* + * Kbuild is not very cooperative with respect to linking separately + * compiled library objects into one module.  So for now we won't use + * separate compilation ... ensuring init/exit sections work to shrink + * the runtime footprint, and giving us at least some parts of what + * a "gcc --combine ... part1.c part2.c part3.c ... " build would. + */ + +#include "composite.c" +#include "usbstring.c" +#include "config.c" +#include "epautoconf.c" +#include "u_serial.c" +#include "f_acm.c" +#include "f_ecm.c" +#include "u_ether.c" + +/*-------------------------------------------------------------------------*/ +  static struct usb_device_descriptor device_desc = {  	.bLength =		sizeof device_desc,  	.bDescriptorType =	USB_DT_DEVICE, @@ -136,7 +155,6 @@ static struct usb_configuration cdc_config_driver = {  	.bConfigurationValue	= 1,  	/* .iConfiguration = DYNAMIC */  	.bmAttributes		= USB_CONFIG_ATT_SELFPOWER, -	.bMaxPower		= 1,	/* 2 mA, minimal */  };  /*-------------------------------------------------------------------------*/ @@ -148,7 +166,8 @@ static int __init cdc_bind(struct usb_composite_dev *cdev)  	int			status;  	if (!can_support_ecm(cdev->gadget)) { -		ERROR(cdev, "controller '%s' not usable\n", gadget->name); +		dev_err(&gadget->dev, "controller '%s' not usable\n", +				gadget->name);  		return -EINVAL;  	} @@ -203,7 +222,8 @@ static int __init cdc_bind(struct usb_composite_dev *cdev)  	if (status < 0)  		goto fail1; -	INFO(cdev, "%s, version: " DRIVER_VERSION "\n", DRIVER_DESC); +	dev_info(&gadget->dev, "%s, version: " DRIVER_VERSION "\n", +			DRIVER_DESC);  	return 0; diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index 85c876c1f15..f2da0269e1b 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -128,6 +128,70 @@ done:  }  /** + * usb_function_deactivate - prevent function and gadget enumeration + * @function: the function that isn't yet ready to respond + * + * Blocks response of the gadget driver to host enumeration by + * preventing the data line pullup from being activated.  This is + * normally called during @bind() processing to change from the + * initial "ready to respond" state, or when a required resource + * becomes available. + * + * For example, drivers that serve as a passthrough to a userspace + * daemon can block enumeration unless that daemon (such as an OBEX, + * MTP, or print server) is ready to handle host requests. + * + * Not all systems support software control of their USB peripheral + * data pullups. + * + * Returns zero on success, else negative errno. + */ +int usb_function_deactivate(struct usb_function *function) +{ +	struct usb_composite_dev	*cdev = function->config->cdev; +	int				status = 0; + +	spin_lock(&cdev->lock); + +	if (cdev->deactivations == 0) +		status = usb_gadget_disconnect(cdev->gadget); +	if (status == 0) +		cdev->deactivations++; + +	spin_unlock(&cdev->lock); +	return status; +} + +/** + * usb_function_activate - allow function and gadget enumeration + * @function: function on which usb_function_activate() was called + * + * Reverses effect of usb_function_deactivate().  If no more functions + * are delaying their activation, the gadget driver will respond to + * host enumeration procedures. + * + * Returns zero on success, else negative errno. + */ +int usb_function_activate(struct usb_function *function) +{ +	struct usb_composite_dev	*cdev = function->config->cdev; +	int				status = 0; + +	spin_lock(&cdev->lock); + +	if (WARN_ON(cdev->deactivations == 0)) +		status = -EINVAL; +	else { +		cdev->deactivations--; +		if (cdev->deactivations == 0) +			status = usb_gadget_connect(cdev->gadget); +	} + +	spin_unlock(&cdev->lock); +	return status; +} + +/**   * usb_interface_id() - allocate an unused interface ID   * @config: configuration associated with the interface   * @function: function handling the interface @@ -181,7 +245,7 @@ static int config_buf(struct usb_configuration *config,  	c->bConfigurationValue = config->bConfigurationValue;  	c->iConfiguration = config->iConfiguration;  	c->bmAttributes = USB_CONFIG_ATT_ONE | config->bmAttributes; -	c->bMaxPower = config->bMaxPower; +	c->bMaxPower = config->bMaxPower ? : (CONFIG_USB_GADGET_VBUS_DRAW / 2);  	/* There may be e.g. OTG descriptors */  	if (config->descriptors) { @@ -368,7 +432,7 @@ static int set_config(struct usb_composite_dev *cdev,  	}  	/* when we return, be sure our power usage is valid */ -	power = 2 * c->bMaxPower; +	power = c->bMaxPower ? (2 * c->bMaxPower) : CONFIG_USB_GADGET_VBUS_DRAW;  done:  	usb_gadget_vbus_draw(gadget, power);  	return result; diff --git a/drivers/usb/gadget/config.c b/drivers/usb/gadget/config.c index 1ca1c326392..e1191b9a316 100644 --- a/drivers/usb/gadget/config.c +++ b/drivers/usb/gadget/config.c @@ -168,7 +168,7 @@ usb_copy_descriptors(struct usb_descriptor_header **src)   * usb_find_endpoint - find a copy of an endpoint descriptor   * @src: original vector of descriptors   * @copy: copy of @src - * @ep: endpoint descriptor found in @src + * @match: endpoint descriptor found in @src   *   * This returns the copy of the @match descriptor made for @copy.  Its   * intended use is to help remembering the endpoint descriptor to use diff --git a/drivers/usb/gadget/dummy_hcd.c b/drivers/usb/gadget/dummy_hcd.c index 7600a0c7875..9064696636a 100644 --- a/drivers/usb/gadget/dummy_hcd.c +++ b/drivers/usb/gadget/dummy_hcd.c @@ -82,6 +82,7 @@ struct dummy_ep {  	const struct usb_endpoint_descriptor *desc;  	struct usb_ep			ep;  	unsigned			halted : 1; +	unsigned			wedged : 1;  	unsigned			already_seen : 1;  	unsigned			setup_stage : 1;  }; @@ -436,6 +437,7 @@ dummy_enable (struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)  	/* at this point real hardware should be NAKing transfers  	 * to that endpoint, until a buffer is queued to it.  	 */ +	ep->halted = ep->wedged = 0;  	retval = 0;  done:  	return retval; @@ -597,7 +599,7 @@ static int dummy_dequeue (struct usb_ep *_ep, struct usb_request *_req)  }  static int -dummy_set_halt (struct usb_ep *_ep, int value) +dummy_set_halt_and_wedge(struct usb_ep *_ep, int value, int wedged)  {  	struct dummy_ep		*ep;  	struct dummy		*dum; @@ -609,16 +611,32 @@ dummy_set_halt (struct usb_ep *_ep, int value)  	if (!dum->driver)  		return -ESHUTDOWN;  	if (!value) -		ep->halted = 0; +		ep->halted = ep->wedged = 0;  	else if (ep->desc && (ep->desc->bEndpointAddress & USB_DIR_IN) &&  			!list_empty (&ep->queue))  		return -EAGAIN; -	else +	else {  		ep->halted = 1; +		if (wedged) +			ep->wedged = 1; +	}  	/* FIXME clear emulated data toggle too */  	return 0;  } +static int +dummy_set_halt(struct usb_ep *_ep, int value) +{ +	return dummy_set_halt_and_wedge(_ep, value, 0); +} + +static int dummy_set_wedge(struct usb_ep *_ep) +{ +	if (!_ep || _ep->name == ep0name) +		return -EINVAL; +	return dummy_set_halt_and_wedge(_ep, 1, 1); +} +  static const struct usb_ep_ops dummy_ep_ops = {  	.enable		= dummy_enable,  	.disable	= dummy_disable, @@ -630,6 +648,7 @@ static const struct usb_ep_ops dummy_ep_ops = {  	.dequeue	= dummy_dequeue,  	.set_halt	= dummy_set_halt, +	.set_wedge	= dummy_set_wedge,  };  /*-------------------------------------------------------------------------*/ @@ -760,7 +779,8 @@ usb_gadget_register_driver (struct usb_gadget_driver *driver)  		ep->ep.name = ep_name [i];  		ep->ep.ops = &dummy_ep_ops;  		list_add_tail (&ep->ep.ep_list, &dum->gadget.ep_list); -		ep->halted = ep->already_seen = ep->setup_stage = 0; +		ep->halted = ep->wedged = ep->already_seen = +				ep->setup_stage = 0;  		ep->ep.maxpacket = ~0;  		ep->last_io = jiffies;  		ep->gadget = &dum->gadget; @@ -1351,7 +1371,7 @@ restart:  				} else if (setup.bRequestType == Ep_Request) {  					// endpoint halt  					ep2 = find_endpoint (dum, w_index); -					if (!ep2) { +					if (!ep2 || ep2->ep.name == ep0name) {  						value = -EOPNOTSUPP;  						break;  					} @@ -1380,7 +1400,8 @@ restart:  						value = -EOPNOTSUPP;  						break;  					} -					ep2->halted = 0; +					if (!ep2->wedged) +						ep2->halted = 0;  					value = 0;  					status = 0;  				} diff --git a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/ether.c index bcac2e68660..37252d0012a 100644 --- a/drivers/usb/gadget/ether.c +++ b/drivers/usb/gadget/ether.c @@ -96,6 +96,28 @@ static inline bool has_rndis(void)  /*-------------------------------------------------------------------------*/ +/* + * Kbuild is not very cooperative with respect to linking separately + * compiled library objects into one module.  So for now we won't use + * separate compilation ... ensuring init/exit sections work to shrink + * the runtime footprint, and giving us at least some parts of what + * a "gcc --combine ... part1.c part2.c part3.c ... " build would. + */ +#include "composite.c" +#include "usbstring.c" +#include "config.c" +#include "epautoconf.c" + +#include "f_ecm.c" +#include "f_subset.c" +#ifdef	CONFIG_USB_ETH_RNDIS +#include "f_rndis.c" +#include "rndis.c" +#endif +#include "u_ether.c" + +/*-------------------------------------------------------------------------*/ +  /* DO NOT REUSE THESE IDs with a protocol-incompatible driver!!  Ever!!   * Instead:  allocate your own, using normal USB-IF procedures.   */ @@ -220,7 +242,6 @@ static struct usb_configuration rndis_config_driver = {  	.bConfigurationValue	= 2,  	/* .iConfiguration = DYNAMIC */  	.bmAttributes		= USB_CONFIG_ATT_SELFPOWER, -	.bMaxPower		= 1,	/* 2 mA, minimal */  };  /*-------------------------------------------------------------------------*/ @@ -249,7 +270,6 @@ static struct usb_configuration eth_config_driver = {  	.bConfigurationValue	= 1,  	/* .iConfiguration = DYNAMIC */  	.bmAttributes		= USB_CONFIG_ATT_SELFPOWER, -	.bMaxPower		= 1,	/* 2 mA, minimal */  };  /*-------------------------------------------------------------------------*/ @@ -293,7 +313,8 @@ static int __init eth_bind(struct usb_composite_dev *cdev)  		 * but if the controller isn't recognized at all then  		 * that assumption is a bit more likely to be wrong.  		 */ -		WARNING(cdev, "controller '%s' not recognized; trying %s\n", +		dev_warn(&gadget->dev, +				"controller '%s' not recognized; trying %s\n",  				gadget->name,  				eth_config_driver.label);  		device_desc.bcdDevice = @@ -332,7 +353,8 @@ static int __init eth_bind(struct usb_composite_dev *cdev)  	if (status < 0)  		goto fail; -	INFO(cdev, "%s, version: " DRIVER_VERSION "\n", DRIVER_DESC); +	dev_info(&gadget->dev, "%s, version: " DRIVER_VERSION "\n", +			DRIVER_DESC);  	return 0; diff --git a/drivers/usb/gadget/f_acm.c b/drivers/usb/gadget/f_acm.c index 5ee1590b8e9..c1d34df0b15 100644 --- a/drivers/usb/gadget/f_acm.c +++ b/drivers/usb/gadget/f_acm.c @@ -463,7 +463,11 @@ static int acm_cdc_notify(struct f_acm *acm, u8 type, u16 value,  	notify->wLength = cpu_to_le16(length);  	memcpy(buf, data, length); +	/* ep_queue() can complete immediately if it fills the fifo... */ +	spin_unlock(&acm->lock);  	status = usb_ep_queue(ep, req, GFP_ATOMIC); +	spin_lock(&acm->lock); +  	if (status < 0) {  		ERROR(acm->port.func.config->cdev,  				"acm ttyGS%d can't notify serial state, %d\n", diff --git a/drivers/usb/gadget/f_ecm.c b/drivers/usb/gadget/f_ecm.c index a2b5c092bda..4ae579948e5 100644 --- a/drivers/usb/gadget/f_ecm.c +++ b/drivers/usb/gadget/f_ecm.c @@ -83,7 +83,7 @@ static inline struct f_ecm *func_to_ecm(struct usb_function *f)  }  /* peak (theoretical) bulk transfer rate in bits-per-second */ -static inline unsigned bitrate(struct usb_gadget *g) +static inline unsigned ecm_bitrate(struct usb_gadget *g)  {  	if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH)  		return 13 * 512 * 8 * 1000 * 8; @@ -107,7 +107,7 @@ static inline unsigned bitrate(struct usb_gadget *g)   */  #define LOG2_STATUS_INTERVAL_MSEC	5	/* 1 << 5 == 32 msec */ -#define STATUS_BYTECOUNT		16	/* 8 byte header + data */ +#define ECM_STATUS_BYTECOUNT		16	/* 8 byte header + data */  /* interface descriptor: */ @@ -125,8 +125,8 @@ static struct usb_interface_descriptor ecm_control_intf __initdata = {  	/* .iInterface = DYNAMIC */  }; -static struct usb_cdc_header_desc header_desc __initdata = { -	.bLength =		sizeof header_desc, +static struct usb_cdc_header_desc ecm_header_desc __initdata = { +	.bLength =		sizeof ecm_header_desc,  	.bDescriptorType =	USB_DT_CS_INTERFACE,  	.bDescriptorSubType =	USB_CDC_HEADER_TYPE, @@ -141,8 +141,8 @@ static struct usb_cdc_union_desc ecm_union_desc __initdata = {  	/* .bSlaveInterface0 =	DYNAMIC */  }; -static struct usb_cdc_ether_desc ether_desc __initdata = { -	.bLength =		sizeof ether_desc, +static struct usb_cdc_ether_desc ecm_desc __initdata = { +	.bLength =		sizeof ecm_desc,  	.bDescriptorType =	USB_DT_CS_INTERFACE,  	.bDescriptorSubType =	USB_CDC_ETHERNET_TYPE, @@ -186,17 +186,17 @@ static struct usb_interface_descriptor ecm_data_intf __initdata = {  /* full speed support: */ -static struct usb_endpoint_descriptor fs_notify_desc __initdata = { +static struct usb_endpoint_descriptor fs_ecm_notify_desc __initdata = {  	.bLength =		USB_DT_ENDPOINT_SIZE,  	.bDescriptorType =	USB_DT_ENDPOINT,  	.bEndpointAddress =	USB_DIR_IN,  	.bmAttributes =		USB_ENDPOINT_XFER_INT, -	.wMaxPacketSize =	__constant_cpu_to_le16(STATUS_BYTECOUNT), +	.wMaxPacketSize =	__constant_cpu_to_le16(ECM_STATUS_BYTECOUNT),  	.bInterval =		1 << LOG2_STATUS_INTERVAL_MSEC,  }; -static struct usb_endpoint_descriptor fs_in_desc __initdata = { +static struct usb_endpoint_descriptor fs_ecm_in_desc __initdata = {  	.bLength =		USB_DT_ENDPOINT_SIZE,  	.bDescriptorType =	USB_DT_ENDPOINT, @@ -204,7 +204,7 @@ static struct usb_endpoint_descriptor fs_in_desc __initdata = {  	.bmAttributes =		USB_ENDPOINT_XFER_BULK,  }; -static struct usb_endpoint_descriptor fs_out_desc __initdata = { +static struct usb_endpoint_descriptor fs_ecm_out_desc __initdata = {  	.bLength =		USB_DT_ENDPOINT_SIZE,  	.bDescriptorType =	USB_DT_ENDPOINT, @@ -212,34 +212,34 @@ static struct usb_endpoint_descriptor fs_out_desc __initdata = {  	.bmAttributes =		USB_ENDPOINT_XFER_BULK,  }; -static struct usb_descriptor_header *eth_fs_function[] __initdata = { +static struct usb_descriptor_header *ecm_fs_function[] __initdata = {  	/* CDC ECM control descriptors */  	(struct usb_descriptor_header *) &ecm_control_intf, -	(struct usb_descriptor_header *) &header_desc, +	(struct usb_descriptor_header *) &ecm_header_desc,  	(struct usb_descriptor_header *) &ecm_union_desc, -	(struct usb_descriptor_header *) ðer_desc, +	(struct usb_descriptor_header *) &ecm_desc,  	/* NOTE: status endpoint might need to be removed */ -	(struct usb_descriptor_header *) &fs_notify_desc, +	(struct usb_descriptor_header *) &fs_ecm_notify_desc,  	/* data interface, altsettings 0 and 1 */  	(struct usb_descriptor_header *) &ecm_data_nop_intf,  	(struct usb_descriptor_header *) &ecm_data_intf, -	(struct usb_descriptor_header *) &fs_in_desc, -	(struct usb_descriptor_header *) &fs_out_desc, +	(struct usb_descriptor_header *) &fs_ecm_in_desc, +	(struct usb_descriptor_header *) &fs_ecm_out_desc,  	NULL,  };  /* high speed support: */ -static struct usb_endpoint_descriptor hs_notify_desc __initdata = { +static struct usb_endpoint_descriptor hs_ecm_notify_desc __initdata = {  	.bLength =		USB_DT_ENDPOINT_SIZE,  	.bDescriptorType =	USB_DT_ENDPOINT,  	.bEndpointAddress =	USB_DIR_IN,  	.bmAttributes =		USB_ENDPOINT_XFER_INT, -	.wMaxPacketSize =	__constant_cpu_to_le16(STATUS_BYTECOUNT), +	.wMaxPacketSize =	__constant_cpu_to_le16(ECM_STATUS_BYTECOUNT),  	.bInterval =		LOG2_STATUS_INTERVAL_MSEC + 4,  }; -static struct usb_endpoint_descriptor hs_in_desc __initdata = { +static struct usb_endpoint_descriptor hs_ecm_in_desc __initdata = {  	.bLength =		USB_DT_ENDPOINT_SIZE,  	.bDescriptorType =	USB_DT_ENDPOINT, @@ -248,7 +248,7 @@ static struct usb_endpoint_descriptor hs_in_desc __initdata = {  	.wMaxPacketSize =	__constant_cpu_to_le16(512),  }; -static struct usb_endpoint_descriptor hs_out_desc __initdata = { +static struct usb_endpoint_descriptor hs_ecm_out_desc __initdata = {  	.bLength =		USB_DT_ENDPOINT_SIZE,  	.bDescriptorType =	USB_DT_ENDPOINT, @@ -257,19 +257,19 @@ static struct usb_endpoint_descriptor hs_out_desc __initdata = {  	.wMaxPacketSize =	__constant_cpu_to_le16(512),  }; -static struct usb_descriptor_header *eth_hs_function[] __initdata = { +static struct usb_descriptor_header *ecm_hs_function[] __initdata = {  	/* CDC ECM control descriptors */  	(struct usb_descriptor_header *) &ecm_control_intf, -	(struct usb_descriptor_header *) &header_desc, +	(struct usb_descriptor_header *) &ecm_header_desc,  	(struct usb_descriptor_header *) &ecm_union_desc, -	(struct usb_descriptor_header *) ðer_desc, +	(struct usb_descriptor_header *) &ecm_desc,  	/* NOTE: status endpoint might need to be removed */ -	(struct usb_descriptor_header *) &hs_notify_desc, +	(struct usb_descriptor_header *) &hs_ecm_notify_desc,  	/* data interface, altsettings 0 and 1 */  	(struct usb_descriptor_header *) &ecm_data_nop_intf,  	(struct usb_descriptor_header *) &ecm_data_intf, -	(struct usb_descriptor_header *) &hs_in_desc, -	(struct usb_descriptor_header *) &hs_out_desc, +	(struct usb_descriptor_header *) &hs_ecm_in_desc, +	(struct usb_descriptor_header *) &hs_ecm_out_desc,  	NULL,  }; @@ -329,14 +329,14 @@ static void ecm_do_notify(struct f_ecm *ecm)  		event->bNotificationType = USB_CDC_NOTIFY_SPEED_CHANGE;  		event->wValue = cpu_to_le16(0);  		event->wLength = cpu_to_le16(8); -		req->length = STATUS_BYTECOUNT; +		req->length = ECM_STATUS_BYTECOUNT;  		/* SPEED_CHANGE data is up/down speeds in bits/sec */  		data = req->buf + sizeof *event; -		data[0] = cpu_to_le32(bitrate(cdev->gadget)); +		data[0] = cpu_to_le32(ecm_bitrate(cdev->gadget));  		data[1] = data[0]; -		DBG(cdev, "notify speed %d\n", bitrate(cdev->gadget)); +		DBG(cdev, "notify speed %d\n", ecm_bitrate(cdev->gadget));  		ecm->notify_state = ECM_NOTIFY_NONE;  		break;  	} @@ -628,13 +628,13 @@ ecm_bind(struct usb_configuration *c, struct usb_function *f)  	status = -ENODEV;  	/* allocate instance-specific endpoints */ -	ep = usb_ep_autoconfig(cdev->gadget, &fs_in_desc); +	ep = usb_ep_autoconfig(cdev->gadget, &fs_ecm_in_desc);  	if (!ep)  		goto fail;  	ecm->port.in_ep = ep;  	ep->driver_data = cdev;	/* claim */ -	ep = usb_ep_autoconfig(cdev->gadget, &fs_out_desc); +	ep = usb_ep_autoconfig(cdev->gadget, &fs_ecm_out_desc);  	if (!ep)  		goto fail;  	ecm->port.out_ep = ep; @@ -644,7 +644,7 @@ ecm_bind(struct usb_configuration *c, struct usb_function *f)  	 * don't treat it that way.  It's simpler, and some newer CDC  	 * profiles (wireless handsets) no longer treat it as optional.  	 */ -	ep = usb_ep_autoconfig(cdev->gadget, &fs_notify_desc); +	ep = usb_ep_autoconfig(cdev->gadget, &fs_ecm_notify_desc);  	if (!ep)  		goto fail;  	ecm->notify = ep; @@ -656,47 +656,47 @@ ecm_bind(struct usb_configuration *c, struct usb_function *f)  	ecm->notify_req = usb_ep_alloc_request(ep, GFP_KERNEL);  	if (!ecm->notify_req)  		goto fail; -	ecm->notify_req->buf = kmalloc(STATUS_BYTECOUNT, GFP_KERNEL); +	ecm->notify_req->buf = kmalloc(ECM_STATUS_BYTECOUNT, GFP_KERNEL);  	if (!ecm->notify_req->buf)  		goto fail;  	ecm->notify_req->context = ecm;  	ecm->notify_req->complete = ecm_notify_complete;  	/* copy descriptors, and track endpoint copies */ -	f->descriptors = usb_copy_descriptors(eth_fs_function); +	f->descriptors = usb_copy_descriptors(ecm_fs_function);  	if (!f->descriptors)  		goto fail; -	ecm->fs.in = usb_find_endpoint(eth_fs_function, -			f->descriptors, &fs_in_desc); -	ecm->fs.out = usb_find_endpoint(eth_fs_function, -			f->descriptors, &fs_out_desc); -	ecm->fs.notify = usb_find_endpoint(eth_fs_function, -			f->descriptors, &fs_notify_desc); +	ecm->fs.in = usb_find_endpoint(ecm_fs_function, +			f->descriptors, &fs_ecm_in_desc); +	ecm->fs.out = usb_find_endpoint(ecm_fs_function, +			f->descriptors, &fs_ecm_out_desc); +	ecm->fs.notify = usb_find_endpoint(ecm_fs_function, +			f->descriptors, &fs_ecm_notify_desc);  	/* support all relevant hardware speeds... we expect that when  	 * hardware is dual speed, all bulk-capable endpoints work at  	 * both speeds  	 */  	if (gadget_is_dualspeed(c->cdev->gadget)) { -		hs_in_desc.bEndpointAddress = -				fs_in_desc.bEndpointAddress; -		hs_out_desc.bEndpointAddress = -				fs_out_desc.bEndpointAddress; -		hs_notify_desc.bEndpointAddress = -				fs_notify_desc.bEndpointAddress; +		hs_ecm_in_desc.bEndpointAddress = +				fs_ecm_in_desc.bEndpointAddress; +		hs_ecm_out_desc.bEndpointAddress = +				fs_ecm_out_desc.bEndpointAddress; +		hs_ecm_notify_desc.bEndpointAddress = +				fs_ecm_notify_desc.bEndpointAddress;  		/* copy descriptors, and track endpoint copies */ -		f->hs_descriptors = usb_copy_descriptors(eth_hs_function); +		f->hs_descriptors = usb_copy_descriptors(ecm_hs_function);  		if (!f->hs_descriptors)  			goto fail; -		ecm->hs.in = usb_find_endpoint(eth_hs_function, -				f->hs_descriptors, &hs_in_desc); -		ecm->hs.out = usb_find_endpoint(eth_hs_function, -				f->hs_descriptors, &hs_out_desc); -		ecm->hs.notify = usb_find_endpoint(eth_hs_function, -				f->hs_descriptors, &hs_notify_desc); +		ecm->hs.in = usb_find_endpoint(ecm_hs_function, +				f->hs_descriptors, &hs_ecm_in_desc); +		ecm->hs.out = usb_find_endpoint(ecm_hs_function, +				f->hs_descriptors, &hs_ecm_out_desc); +		ecm->hs.notify = usb_find_endpoint(ecm_hs_function, +				f->hs_descriptors, &hs_ecm_notify_desc);  	}  	/* NOTE:  all that is done without knowing or caring about @@ -795,7 +795,7 @@ int __init ecm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN])  		if (status < 0)  			return status;  		ecm_string_defs[1].id = status; -		ether_desc.iMACAddress = status; +		ecm_desc.iMACAddress = status;  	}  	/* allocate and initialize one new instance */ diff --git a/drivers/usb/gadget/f_loopback.c b/drivers/usb/gadget/f_loopback.c index eda4cde72c8..8affe1dfc2c 100644 --- a/drivers/usb/gadget/f_loopback.c +++ b/drivers/usb/gadget/f_loopback.c @@ -70,7 +70,7 @@ static struct usb_interface_descriptor loopback_intf = {  /* full speed support: */ -static struct usb_endpoint_descriptor fs_source_desc = { +static struct usb_endpoint_descriptor fs_loop_source_desc = {  	.bLength =		USB_DT_ENDPOINT_SIZE,  	.bDescriptorType =	USB_DT_ENDPOINT, @@ -78,7 +78,7 @@ static struct usb_endpoint_descriptor fs_source_desc = {  	.bmAttributes =		USB_ENDPOINT_XFER_BULK,  }; -static struct usb_endpoint_descriptor fs_sink_desc = { +static struct usb_endpoint_descriptor fs_loop_sink_desc = {  	.bLength =		USB_DT_ENDPOINT_SIZE,  	.bDescriptorType =	USB_DT_ENDPOINT, @@ -88,14 +88,14 @@ static struct usb_endpoint_descriptor fs_sink_desc = {  static struct usb_descriptor_header *fs_loopback_descs[] = {  	(struct usb_descriptor_header *) &loopback_intf, -	(struct usb_descriptor_header *) &fs_sink_desc, -	(struct usb_descriptor_header *) &fs_source_desc, +	(struct usb_descriptor_header *) &fs_loop_sink_desc, +	(struct usb_descriptor_header *) &fs_loop_source_desc,  	NULL,  };  /* high speed support: */ -static struct usb_endpoint_descriptor hs_source_desc = { +static struct usb_endpoint_descriptor hs_loop_source_desc = {  	.bLength =		USB_DT_ENDPOINT_SIZE,  	.bDescriptorType =	USB_DT_ENDPOINT, @@ -103,7 +103,7 @@ static struct usb_endpoint_descriptor hs_source_desc = {  	.wMaxPacketSize =	__constant_cpu_to_le16(512),  }; -static struct usb_endpoint_descriptor hs_sink_desc = { +static struct usb_endpoint_descriptor hs_loop_sink_desc = {  	.bLength =		USB_DT_ENDPOINT_SIZE,  	.bDescriptorType =	USB_DT_ENDPOINT, @@ -113,8 +113,8 @@ static struct usb_endpoint_descriptor hs_sink_desc = {  static struct usb_descriptor_header *hs_loopback_descs[] = {  	(struct usb_descriptor_header *) &loopback_intf, -	(struct usb_descriptor_header *) &hs_source_desc, -	(struct usb_descriptor_header *) &hs_sink_desc, +	(struct usb_descriptor_header *) &hs_loop_source_desc, +	(struct usb_descriptor_header *) &hs_loop_sink_desc,  	NULL,  }; @@ -152,7 +152,7 @@ loopback_bind(struct usb_configuration *c, struct usb_function *f)  	/* allocate endpoints */ -	loop->in_ep = usb_ep_autoconfig(cdev->gadget, &fs_source_desc); +	loop->in_ep = usb_ep_autoconfig(cdev->gadget, &fs_loop_source_desc);  	if (!loop->in_ep) {  autoconf_fail:  		ERROR(cdev, "%s: can't autoconfigure on %s\n", @@ -161,17 +161,17 @@ autoconf_fail:  	}  	loop->in_ep->driver_data = cdev;	/* claim */ -	loop->out_ep = usb_ep_autoconfig(cdev->gadget, &fs_sink_desc); +	loop->out_ep = usb_ep_autoconfig(cdev->gadget, &fs_loop_sink_desc);  	if (!loop->out_ep)  		goto autoconf_fail;  	loop->out_ep->driver_data = cdev;	/* claim */  	/* support high speed hardware */  	if (gadget_is_dualspeed(c->cdev->gadget)) { -		hs_source_desc.bEndpointAddress = -				fs_source_desc.bEndpointAddress; -		hs_sink_desc.bEndpointAddress = -				fs_sink_desc.bEndpointAddress; +		hs_loop_source_desc.bEndpointAddress = +				fs_loop_source_desc.bEndpointAddress; +		hs_loop_sink_desc.bEndpointAddress = +				fs_loop_sink_desc.bEndpointAddress;  		f->hs_descriptors = hs_loopback_descs;  	} @@ -255,8 +255,10 @@ enable_loopback(struct usb_composite_dev *cdev, struct f_loopback *loop)  	struct usb_request			*req;  	unsigned				i; -	src = ep_choose(cdev->gadget, &hs_source_desc, &fs_source_desc); -	sink = ep_choose(cdev->gadget, &hs_sink_desc, &fs_sink_desc); +	src = ep_choose(cdev->gadget, +			&hs_loop_source_desc, &fs_loop_source_desc); +	sink = ep_choose(cdev->gadget, +			&hs_loop_sink_desc, &fs_loop_sink_desc);  	/* one endpoint writes data back IN to the host */  	ep = loop->in_ep; @@ -350,7 +352,6 @@ static struct usb_configuration loopback_driver = {  	.bind		= loopback_bind_config,  	.bConfigurationValue = 2,  	.bmAttributes	= USB_CONFIG_ATT_SELFPOWER, -	.bMaxPower	= 1,	/* 2 mA, minimal */  	/* .iConfiguration = DYNAMIC */  }; diff --git a/drivers/usb/gadget/f_obex.c b/drivers/usb/gadget/f_obex.c new file mode 100644 index 00000000000..80c2e7e9622 --- /dev/null +++ b/drivers/usb/gadget/f_obex.c @@ -0,0 +1,493 @@ +/* + * f_obex.c -- USB CDC OBEX function driver + * + * Copyright (C) 2008 Nokia Corporation + * Contact: Felipe Balbi <felipe.balbi@nokia.com> + * + * Based on f_acm.c by Al Borchers and David Brownell. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* #define VERBOSE_DEBUG */ + +#include <linux/kernel.h> +#include <linux/utsname.h> +#include <linux/device.h> + +#include "u_serial.h" +#include "gadget_chips.h" + + +/* + * This CDC OBEX function support just packages a TTY-ish byte stream. + * A user mode server will put it into "raw" mode and handle all the + * relevant protocol details ... this is just a kernel passthrough. + * When possible, we prevent gadget enumeration until that server is + * ready to handle the commands. + */ + +struct obex_ep_descs { +	struct usb_endpoint_descriptor	*obex_in; +	struct usb_endpoint_descriptor	*obex_out; +}; + +struct f_obex { +	struct gserial			port; +	u8				ctrl_id; +	u8				data_id; +	u8				port_num; +	u8				can_activate; + +	struct obex_ep_descs		fs; +	struct obex_ep_descs		hs; +}; + +static inline struct f_obex *func_to_obex(struct usb_function *f) +{ +	return container_of(f, struct f_obex, port.func); +} + +static inline struct f_obex *port_to_obex(struct gserial *p) +{ +	return container_of(p, struct f_obex, port); +} + +/*-------------------------------------------------------------------------*/ + +#define OBEX_CTRL_IDX	0 +#define OBEX_DATA_IDX	1 + +static struct usb_string obex_string_defs[] = { +	[OBEX_CTRL_IDX].s	= "CDC Object Exchange (OBEX)", +	[OBEX_DATA_IDX].s	= "CDC OBEX Data", +	{  },	/* end of list */ +}; + +static struct usb_gadget_strings obex_string_table = { +	.language		= 0x0409,	/* en-US */ +	.strings		= obex_string_defs, +}; + +static struct usb_gadget_strings *obex_strings[] = { +	&obex_string_table, +	NULL, +}; + +/*-------------------------------------------------------------------------*/ + +static struct usb_interface_descriptor obex_control_intf __initdata = { +	.bLength		= sizeof(obex_control_intf), +	.bDescriptorType	= USB_DT_INTERFACE, +	.bInterfaceNumber	= 0, + +	.bAlternateSetting	= 0, +	.bNumEndpoints		= 0, +	.bInterfaceClass	= USB_CLASS_COMM, +	.bInterfaceSubClass	= USB_CDC_SUBCLASS_OBEX, +}; + +static struct usb_interface_descriptor obex_data_nop_intf __initdata = { +	.bLength		= sizeof(obex_data_nop_intf), +	.bDescriptorType	= USB_DT_INTERFACE, +	.bInterfaceNumber	= 1, + +	.bAlternateSetting	= 0, +	.bNumEndpoints		= 0, +	.bInterfaceClass	= USB_CLASS_CDC_DATA, +}; + +static struct usb_interface_descriptor obex_data_intf __initdata = { +	.bLength		= sizeof(obex_data_intf), +	.bDescriptorType	= USB_DT_INTERFACE, +	.bInterfaceNumber	= 2, + +	.bAlternateSetting	= 1, +	.bNumEndpoints		= 2, +	.bInterfaceClass	= USB_CLASS_CDC_DATA, +}; + +static struct usb_cdc_header_desc obex_cdc_header_desc __initdata = { +	.bLength		= sizeof(obex_cdc_header_desc), +	.bDescriptorType	= USB_DT_CS_INTERFACE, +	.bDescriptorSubType	= USB_CDC_HEADER_TYPE, +	.bcdCDC			= __constant_cpu_to_le16(0x0120), +}; + +static struct usb_cdc_union_desc obex_cdc_union_desc __initdata = { +	.bLength		= sizeof(obex_cdc_union_desc), +	.bDescriptorType	= USB_DT_CS_INTERFACE, +	.bDescriptorSubType	= USB_CDC_UNION_TYPE, +	.bMasterInterface0	= 1, +	.bSlaveInterface0	= 2, +}; + +static struct usb_cdc_obex_desc obex_desc __initdata = { +	.bLength		= sizeof(obex_desc), +	.bDescriptorType	= USB_DT_CS_INTERFACE, +	.bDescriptorSubType	= USB_CDC_OBEX_TYPE, +	.bcdVersion		= __constant_cpu_to_le16(0x0100), +}; + +/* High-Speed Support */ + +static struct usb_endpoint_descriptor obex_hs_ep_out_desc __initdata = { +	.bLength		= USB_DT_ENDPOINT_SIZE, +	.bDescriptorType	= USB_DT_ENDPOINT, + +	.bEndpointAddress	= USB_DIR_OUT, +	.bmAttributes		= USB_ENDPOINT_XFER_BULK, +	.wMaxPacketSize		= __constant_cpu_to_le16(512), +}; + +static struct usb_endpoint_descriptor obex_hs_ep_in_desc __initdata = { +	.bLength		= USB_DT_ENDPOINT_SIZE, +	.bDescriptorType	= USB_DT_ENDPOINT, + +	.bEndpointAddress	= USB_DIR_IN, +	.bmAttributes		= USB_ENDPOINT_XFER_BULK, +	.wMaxPacketSize		= __constant_cpu_to_le16(512), +}; + +static struct usb_descriptor_header *hs_function[] __initdata = { +	(struct usb_descriptor_header *) &obex_control_intf, +	(struct usb_descriptor_header *) &obex_cdc_header_desc, +	(struct usb_descriptor_header *) &obex_desc, +	(struct usb_descriptor_header *) &obex_cdc_union_desc, + +	(struct usb_descriptor_header *) &obex_data_nop_intf, +	(struct usb_descriptor_header *) &obex_data_intf, +	(struct usb_descriptor_header *) &obex_hs_ep_in_desc, +	(struct usb_descriptor_header *) &obex_hs_ep_out_desc, +	NULL, +}; + +/* Full-Speed Support */ + +static struct usb_endpoint_descriptor obex_fs_ep_in_desc __initdata = { +	.bLength		= USB_DT_ENDPOINT_SIZE, +	.bDescriptorType	= USB_DT_ENDPOINT, + +	.bEndpointAddress	= USB_DIR_IN, +	.bmAttributes		= USB_ENDPOINT_XFER_BULK, +}; + +static struct usb_endpoint_descriptor obex_fs_ep_out_desc __initdata = { +	.bLength		= USB_DT_ENDPOINT_SIZE, +	.bDescriptorType	= USB_DT_ENDPOINT, + +	.bEndpointAddress	= USB_DIR_OUT, +	.bmAttributes		= USB_ENDPOINT_XFER_BULK, +}; + +static struct usb_descriptor_header *fs_function[] __initdata = { +	(struct usb_descriptor_header *) &obex_control_intf, +	(struct usb_descriptor_header *) &obex_cdc_header_desc, +	(struct usb_descriptor_header *) &obex_desc, +	(struct usb_descriptor_header *) &obex_cdc_union_desc, + +	(struct usb_descriptor_header *) &obex_data_nop_intf, +	(struct usb_descriptor_header *) &obex_data_intf, +	(struct usb_descriptor_header *) &obex_fs_ep_in_desc, +	(struct usb_descriptor_header *) &obex_fs_ep_out_desc, +	NULL, +}; + +/*-------------------------------------------------------------------------*/ + +static int obex_set_alt(struct usb_function *f, unsigned intf, unsigned alt) +{ +	struct f_obex		*obex = func_to_obex(f); +	struct usb_composite_dev *cdev = f->config->cdev; + +	if (intf == obex->ctrl_id) { +		if (alt != 0) +			goto fail; +		/* NOP */ +		DBG(cdev, "reset obex ttyGS%d control\n", obex->port_num); + +	} else if (intf == obex->data_id) { +		if (alt > 1) +			goto fail; + +		if (obex->port.in->driver_data) { +			DBG(cdev, "reset obex ttyGS%d\n", obex->port_num); +			gserial_disconnect(&obex->port); +		} + +		if (!obex->port.in_desc) { +			DBG(cdev, "init obex ttyGS%d\n", obex->port_num); +			obex->port.in_desc = ep_choose(cdev->gadget, +					obex->hs.obex_in, obex->fs.obex_in); +			obex->port.out_desc = ep_choose(cdev->gadget, +					obex->hs.obex_out, obex->fs.obex_out); +		} + +		if (alt == 1) { +			DBG(cdev, "activate obex ttyGS%d\n", obex->port_num); +			gserial_connect(&obex->port, obex->port_num); +		} + +	} else +		goto fail; + +	return 0; + +fail: +	return -EINVAL; +} + +static int obex_get_alt(struct usb_function *f, unsigned intf) +{ +	struct f_obex		*obex = func_to_obex(f); + +	if (intf == obex->ctrl_id) +		return 0; + +	return obex->port.in->driver_data ? 1 : 0; +} + +static void obex_disable(struct usb_function *f) +{ +	struct f_obex	*obex = func_to_obex(f); +	struct usb_composite_dev *cdev = f->config->cdev; + +	DBG(cdev, "obex ttyGS%d disable\n", obex->port_num); +	gserial_disconnect(&obex->port); +} + +/*-------------------------------------------------------------------------*/ + +static void obex_connect(struct gserial *g) +{ +	struct f_obex		*obex = port_to_obex(g); +	struct usb_composite_dev *cdev = g->func.config->cdev; +	int			status; + +	if (!obex->can_activate) +		return; + +	status = usb_function_activate(&g->func); +	if (status) +		DBG(cdev, "obex ttyGS%d function activate --> %d\n", +			obex->port_num, status); +} + +static void obex_disconnect(struct gserial *g) +{ +	struct f_obex		*obex = port_to_obex(g); +	struct usb_composite_dev *cdev = g->func.config->cdev; +	int			status; + +	if (!obex->can_activate) +		return; + +	status = usb_function_deactivate(&g->func); +	if (status) +		DBG(cdev, "obex ttyGS%d function deactivate --> %d\n", +			obex->port_num, status); +} + +/*-------------------------------------------------------------------------*/ + +static int __init +obex_bind(struct usb_configuration *c, struct usb_function *f) +{ +	struct usb_composite_dev *cdev = c->cdev; +	struct f_obex		*obex = func_to_obex(f); +	int			status; +	struct usb_ep		*ep; + +	/* allocate instance-specific interface IDs, and patch descriptors */ + +	status = usb_interface_id(c, f); +	if (status < 0) +		goto fail; +	obex->ctrl_id = status; + +	obex_control_intf.bInterfaceNumber = status; +	obex_cdc_union_desc.bMasterInterface0 = status; + +	status = usb_interface_id(c, f); +	if (status < 0) +		goto fail; +	obex->data_id = status; + +	obex_data_nop_intf.bInterfaceNumber = status; +	obex_data_intf.bInterfaceNumber = status; +	obex_cdc_union_desc.bSlaveInterface0 = status; + +	/* allocate instance-specific endpoints */ + +	ep = usb_ep_autoconfig(cdev->gadget, &obex_fs_ep_in_desc); +	if (!ep) +		goto fail; +	obex->port.in = ep; +	ep->driver_data = cdev;	/* claim */ + +	ep = usb_ep_autoconfig(cdev->gadget, &obex_fs_ep_out_desc); +	if (!ep) +		goto fail; +	obex->port.out = ep; +	ep->driver_data = cdev;	/* claim */ + +	/* copy descriptors, and track endpoint copies */ +	f->descriptors = usb_copy_descriptors(fs_function); + +	obex->fs.obex_in = usb_find_endpoint(fs_function, +			f->descriptors, &obex_fs_ep_in_desc); +	obex->fs.obex_out = usb_find_endpoint(fs_function, +			f->descriptors, &obex_fs_ep_out_desc); + +	/* support all relevant hardware speeds... we expect that when +	 * hardware is dual speed, all bulk-capable endpoints work at +	 * both speeds +	 */ +	if (gadget_is_dualspeed(c->cdev->gadget)) { + +		obex_hs_ep_in_desc.bEndpointAddress = +				obex_fs_ep_in_desc.bEndpointAddress; +		obex_hs_ep_out_desc.bEndpointAddress = +				obex_fs_ep_out_desc.bEndpointAddress; + +		/* copy descriptors, and track endpoint copies */ +		f->hs_descriptors = usb_copy_descriptors(hs_function); + +		obex->hs.obex_in = usb_find_endpoint(hs_function, +				f->descriptors, &obex_hs_ep_in_desc); +		obex->hs.obex_out = usb_find_endpoint(hs_function, +				f->descriptors, &obex_hs_ep_out_desc); +	} + +	/* Avoid letting this gadget enumerate until the userspace +	 * OBEX server is active. +	 */ +	status = usb_function_deactivate(f); +	if (status < 0) +		WARNING(cdev, "obex ttyGS%d: can't prevent enumeration, %d\n", +			obex->port_num, status); +	else +		obex->can_activate = true; + + +	DBG(cdev, "obex ttyGS%d: %s speed IN/%s OUT/%s\n", +			obex->port_num, +			gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", +			obex->port.in->name, obex->port.out->name); + +	return 0; + +fail: +	/* we might as well release our claims on endpoints */ +	if (obex->port.out) +		obex->port.out->driver_data = NULL; +	if (obex->port.in) +		obex->port.in->driver_data = NULL; + +	ERROR(cdev, "%s/%p: can't bind, err %d\n", f->name, f, status); + +	return status; +} + +static void +obex_unbind(struct usb_configuration *c, struct usb_function *f) +{ +	if (gadget_is_dualspeed(c->cdev->gadget)) +		usb_free_descriptors(f->hs_descriptors); +	usb_free_descriptors(f->descriptors); +	kfree(func_to_obex(f)); +} + +/* Some controllers can't support CDC OBEX ... */ +static inline bool can_support_obex(struct usb_configuration *c) +{ +	/* Since the first interface is a NOP, we can ignore the +	 * issue of multi-interface support on most controllers. +	 * +	 * Altsettings are mandatory, however... +	 */ +	if (!gadget_supports_altsettings(c->cdev->gadget)) +		return false; + +	/* everything else is *probably* fine ... */ +	return true; +} + +/** + * obex_bind_config - add a CDC OBEX function to a configuration + * @c: the configuration to support the CDC OBEX instance + * @port_num: /dev/ttyGS* port this interface will use + * Context: single threaded during gadget setup + * + * Returns zero on success, else negative errno. + * + * Caller must have called @gserial_setup() with enough ports to + * handle all the ones it binds.  Caller is also responsible + * for calling @gserial_cleanup() before module unload. + */ +int __init obex_bind_config(struct usb_configuration *c, u8 port_num) +{ +	struct f_obex	*obex; +	int		status; + +	if (!can_support_obex(c)) +		return -EINVAL; + +	/* maybe allocate device-global string IDs, and patch descriptors */ +	if (obex_string_defs[OBEX_CTRL_IDX].id == 0) { +		status = usb_string_id(c->cdev); +		if (status < 0) +			return status; +		obex_string_defs[OBEX_CTRL_IDX].id = status; + +		obex_control_intf.iInterface = status; + +		status = usb_string_id(c->cdev); +		if (status < 0) +			return status; +		obex_string_defs[OBEX_DATA_IDX].id = status; + +		obex_data_nop_intf.iInterface = +			obex_data_intf.iInterface = status; +	} + +	/* allocate and initialize one new instance */ +	obex = kzalloc(sizeof *obex, GFP_KERNEL); +	if (!obex) +		return -ENOMEM; + +	obex->port_num = port_num; + +	obex->port.connect = obex_connect; +	obex->port.disconnect = obex_disconnect; + +	obex->port.func.name = "obex"; +	obex->port.func.strings = obex_strings; +	/* descriptors are per-instance copies */ +	obex->port.func.bind = obex_bind; +	obex->port.func.unbind = obex_unbind; +	obex->port.func.set_alt = obex_set_alt; +	obex->port.func.get_alt = obex_get_alt; +	obex->port.func.disable = obex_disable; + +	status = usb_add_function(c, &obex->port.func); +	if (status) +		kfree(obex); + +	return status; +} + +MODULE_AUTHOR("Felipe Balbi"); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/gadget/f_sourcesink.c b/drivers/usb/gadget/f_sourcesink.c index f18c3a14d72..dc84d26d283 100644 --- a/drivers/usb/gadget/f_sourcesink.c +++ b/drivers/usb/gadget/f_sourcesink.c @@ -552,7 +552,6 @@ static struct usb_configuration sourcesink_driver = {  	.setup		= sourcesink_setup,  	.bConfigurationValue = 3,  	.bmAttributes	= USB_CONFIG_ATT_SELFPOWER, -	.bMaxPower	= 1,	/* 2 mA, minimal */  	/* .iConfiguration = DYNAMIC */  }; diff --git a/drivers/usb/gadget/f_subset.c b/drivers/usb/gadget/f_subset.c index acb8d233aa1..fe183287577 100644 --- a/drivers/usb/gadget/f_subset.c +++ b/drivers/usb/gadget/f_subset.c @@ -103,8 +103,8 @@ static struct usb_interface_descriptor subset_data_intf __initdata = {  	/* .iInterface = DYNAMIC */  }; -static struct usb_cdc_header_desc header_desc __initdata = { -	.bLength =		sizeof header_desc, +static struct usb_cdc_header_desc mdlm_header_desc __initdata = { +	.bLength =		sizeof mdlm_header_desc,  	.bDescriptorType =	USB_DT_CS_INTERFACE,  	.bDescriptorSubType =	USB_CDC_HEADER_TYPE, @@ -152,7 +152,7 @@ static struct usb_cdc_ether_desc ether_desc __initdata = {  /* full speed support: */ -static struct usb_endpoint_descriptor fs_in_desc __initdata = { +static struct usb_endpoint_descriptor fs_subset_in_desc __initdata = {  	.bLength =		USB_DT_ENDPOINT_SIZE,  	.bDescriptorType =	USB_DT_ENDPOINT, @@ -160,7 +160,7 @@ static struct usb_endpoint_descriptor fs_in_desc __initdata = {  	.bmAttributes =		USB_ENDPOINT_XFER_BULK,  }; -static struct usb_endpoint_descriptor fs_out_desc __initdata = { +static struct usb_endpoint_descriptor fs_subset_out_desc __initdata = {  	.bLength =		USB_DT_ENDPOINT_SIZE,  	.bDescriptorType =	USB_DT_ENDPOINT, @@ -170,18 +170,18 @@ static struct usb_endpoint_descriptor fs_out_desc __initdata = {  static struct usb_descriptor_header *fs_eth_function[] __initdata = {  	(struct usb_descriptor_header *) &subset_data_intf, -	(struct usb_descriptor_header *) &header_desc, +	(struct usb_descriptor_header *) &mdlm_header_desc,  	(struct usb_descriptor_header *) &mdlm_desc,  	(struct usb_descriptor_header *) &mdlm_detail_desc,  	(struct usb_descriptor_header *) ðer_desc, -	(struct usb_descriptor_header *) &fs_in_desc, -	(struct usb_descriptor_header *) &fs_out_desc, +	(struct usb_descriptor_header *) &fs_subset_in_desc, +	(struct usb_descriptor_header *) &fs_subset_out_desc,  	NULL,  };  /* high speed support: */ -static struct usb_endpoint_descriptor hs_in_desc __initdata = { +static struct usb_endpoint_descriptor hs_subset_in_desc __initdata = {  	.bLength =		USB_DT_ENDPOINT_SIZE,  	.bDescriptorType =	USB_DT_ENDPOINT, @@ -189,7 +189,7 @@ static struct usb_endpoint_descriptor hs_in_desc __initdata = {  	.wMaxPacketSize =	__constant_cpu_to_le16(512),  }; -static struct usb_endpoint_descriptor hs_out_desc __initdata = { +static struct usb_endpoint_descriptor hs_subset_out_desc __initdata = {  	.bLength =		USB_DT_ENDPOINT_SIZE,  	.bDescriptorType =	USB_DT_ENDPOINT, @@ -199,12 +199,12 @@ static struct usb_endpoint_descriptor hs_out_desc __initdata = {  static struct usb_descriptor_header *hs_eth_function[] __initdata = {  	(struct usb_descriptor_header *) &subset_data_intf, -	(struct usb_descriptor_header *) &header_desc, +	(struct usb_descriptor_header *) &mdlm_header_desc,  	(struct usb_descriptor_header *) &mdlm_desc,  	(struct usb_descriptor_header *) &mdlm_detail_desc,  	(struct usb_descriptor_header *) ðer_desc, -	(struct usb_descriptor_header *) &hs_in_desc, -	(struct usb_descriptor_header *) &hs_out_desc, +	(struct usb_descriptor_header *) &hs_subset_in_desc, +	(struct usb_descriptor_header *) &hs_subset_out_desc,  	NULL,  }; @@ -281,13 +281,13 @@ geth_bind(struct usb_configuration *c, struct usb_function *f)  	status = -ENODEV;  	/* allocate instance-specific endpoints */ -	ep = usb_ep_autoconfig(cdev->gadget, &fs_in_desc); +	ep = usb_ep_autoconfig(cdev->gadget, &fs_subset_in_desc);  	if (!ep)  		goto fail;  	geth->port.in_ep = ep;  	ep->driver_data = cdev;	/* claim */ -	ep = usb_ep_autoconfig(cdev->gadget, &fs_out_desc); +	ep = usb_ep_autoconfig(cdev->gadget, &fs_subset_out_desc);  	if (!ep)  		goto fail;  	geth->port.out_ep = ep; @@ -297,9 +297,9 @@ geth_bind(struct usb_configuration *c, struct usb_function *f)  	f->descriptors = usb_copy_descriptors(fs_eth_function);  	geth->fs.in = usb_find_endpoint(fs_eth_function, -			f->descriptors, &fs_in_desc); +			f->descriptors, &fs_subset_in_desc);  	geth->fs.out = usb_find_endpoint(fs_eth_function, -			f->descriptors, &fs_out_desc); +			f->descriptors, &fs_subset_out_desc);  	/* support all relevant hardware speeds... we expect that when @@ -307,18 +307,18 @@ geth_bind(struct usb_configuration *c, struct usb_function *f)  	 * both speeds  	 */  	if (gadget_is_dualspeed(c->cdev->gadget)) { -		hs_in_desc.bEndpointAddress = -				fs_in_desc.bEndpointAddress; -		hs_out_desc.bEndpointAddress = -				fs_out_desc.bEndpointAddress; +		hs_subset_in_desc.bEndpointAddress = +				fs_subset_in_desc.bEndpointAddress; +		hs_subset_out_desc.bEndpointAddress = +				fs_subset_out_desc.bEndpointAddress;  		/* copy descriptors, and track endpoint copies */  		f->hs_descriptors = usb_copy_descriptors(hs_eth_function);  		geth->hs.in = usb_find_endpoint(hs_eth_function, -				f->hs_descriptors, &hs_in_desc); +				f->hs_descriptors, &hs_subset_in_desc);  		geth->hs.out = usb_find_endpoint(hs_eth_function, -				f->hs_descriptors, &hs_out_desc); +				f->hs_descriptors, &hs_subset_out_desc);  	}  	/* NOTE:  all that is done without knowing or caring about diff --git a/drivers/usb/gadget/file_storage.c b/drivers/usb/gadget/file_storage.c index ea2c31d1808..c4e62a6297d 100644 --- a/drivers/usb/gadget/file_storage.c +++ b/drivers/usb/gadget/file_storage.c @@ -245,6 +245,18 @@  #include "gadget_chips.h" + +/* + * Kbuild is not very cooperative with respect to linking separately + * compiled library objects into one module.  So for now we won't use + * separate compilation ... ensuring init/exit sections work to shrink + * the runtime footprint, and giving us at least some parts of what + * a "gcc --combine ... part1.c part2.c part3.c ... " build would. + */ +#include "usbstring.c" +#include "config.c" +#include "epautoconf.c" +  /*-------------------------------------------------------------------------*/  #define DRIVER_DESC		"File-backed Storage Gadget" @@ -839,7 +851,7 @@ config_desc = {  	.bConfigurationValue =	CONFIG_VALUE,  	.iConfiguration =	STRING_CONFIG,  	.bmAttributes =		USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER, -	.bMaxPower =		1,	// self-powered +	.bMaxPower =		CONFIG_USB_GADGET_VBUS_DRAW / 2,  };  static struct usb_otg_descriptor @@ -2664,11 +2676,24 @@ static int check_command(struct fsg_dev *fsg, int cmnd_size,  	/* Verify the length of the command itself */  	if (cmnd_size != fsg->cmnd_size) { -		/* Special case workaround: MS-Windows issues REQUEST SENSE -		 * with cbw->Length == 12 (it should be 6). */ -		if (fsg->cmnd[0] == SC_REQUEST_SENSE && fsg->cmnd_size == 12) +		/* Special case workaround: There are plenty of buggy SCSI +		 * implementations. Many have issues with cbw->Length +		 * field passing a wrong command size. For those cases we +		 * always try to work around the problem by using the length +		 * sent by the host side provided it is at least as large +		 * as the correct command length. +		 * Examples of such cases would be MS-Windows, which issues +		 * REQUEST SENSE with cbw->Length == 12 where it should +		 * be 6, and xbox360 issuing INQUIRY, TEST UNIT READY and +		 * REQUEST SENSE with cbw->Length == 10 where it should +		 * be 6 as well. +		 */ +		if (cmnd_size <= fsg->cmnd_size) { +			DBG(fsg, "%s is buggy! Expected length %d " +					"but we got %d\n", name, +					cmnd_size, fsg->cmnd_size);  			cmnd_size = fsg->cmnd_size; -		else { +		} else {  			fsg->phase_error = 1;  			return -EINVAL;  		} diff --git a/drivers/usb/gadget/fsl_qe_udc.c b/drivers/usb/gadget/fsl_qe_udc.c new file mode 100644 index 00000000000..1fe8b44787b --- /dev/null +++ b/drivers/usb/gadget/fsl_qe_udc.c @@ -0,0 +1,2760 @@ +/* + * driver/usb/gadget/fsl_qe_udc.c + * + * Copyright (c) 2006-2008 Freescale Semiconductor, Inc. All rights reserved. + * + * 	Xie Xiaobo <X.Xie@freescale.com> + * 	Li Yang <leoli@freescale.com> + * 	Based on bareboard code from Shlomi Gridish. + * + * Description: + * Freescle QE/CPM USB Pheripheral Controller Driver + * The controller can be found on MPC8360, MPC8272, and etc. + * MPC8360 Rev 1.1 may need QE mircocode update + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation;  either version 2 of the License, or (at your + * option) any later version. + */ + +#undef USB_TRACE + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/ioport.h> +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/slab.h> +#include <linux/list.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/moduleparam.h> +#include <linux/of_platform.h> +#include <linux/dma-mapping.h> +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> +#include <linux/usb/otg.h> +#include <asm/qe.h> +#include <asm/cpm.h> +#include <asm/dma.h> +#include <asm/reg.h> +#include "fsl_qe_udc.h" + +#define DRIVER_DESC     "Freescale QE/CPM USB Device Controller driver" +#define DRIVER_AUTHOR   "Xie XiaoBo" +#define DRIVER_VERSION  "1.0" + +#define DMA_ADDR_INVALID        (~(dma_addr_t)0) + +static const char driver_name[] = "fsl_qe_udc"; +static const char driver_desc[] = DRIVER_DESC; + +/*ep name is important in gadget, it should obey the convention of ep_match()*/ +static const char *const ep_name[] = { +	"ep0-control", /* everyone has ep0 */ +	/* 3 configurable endpoints */ +	"ep1", +	"ep2", +	"ep3", +}; + +static struct usb_endpoint_descriptor qe_ep0_desc = { +	.bLength =		USB_DT_ENDPOINT_SIZE, +	.bDescriptorType =	USB_DT_ENDPOINT, + +	.bEndpointAddress =	0, +	.bmAttributes =		USB_ENDPOINT_XFER_CONTROL, +	.wMaxPacketSize =	USB_MAX_CTRL_PAYLOAD, +}; + +/* it is initialized in probe()  */ +static struct qe_udc *udc_controller; + +/******************************************************************** + *      Internal Used Function Start +********************************************************************/ +/*----------------------------------------------------------------- + * done() - retire a request; caller blocked irqs + *--------------------------------------------------------------*/ +static void done(struct qe_ep *ep, struct qe_req *req, int status) +{ +	struct qe_udc *udc = ep->udc; +	unsigned char stopped = ep->stopped; + +	/* the req->queue pointer is used by ep_queue() func, in which +	 * the request will be added into a udc_ep->queue 'd tail +	 * so here the req will be dropped from the ep->queue +	 */ +	list_del_init(&req->queue); + +	/* req.status should be set as -EINPROGRESS in ep_queue() */ +	if (req->req.status == -EINPROGRESS) +		req->req.status = status; +	else +		status = req->req.status; + +	if (req->mapped) { +		dma_unmap_single(udc->gadget.dev.parent, +			req->req.dma, req->req.length, +			ep_is_in(ep) +				? DMA_TO_DEVICE +				: DMA_FROM_DEVICE); +		req->req.dma = DMA_ADDR_INVALID; +		req->mapped = 0; +	} else +		dma_sync_single_for_cpu(udc->gadget.dev.parent, +			req->req.dma, req->req.length, +			ep_is_in(ep) +				? DMA_TO_DEVICE +				: DMA_FROM_DEVICE); + +	if (status && (status != -ESHUTDOWN)) +		dev_vdbg(udc->dev, "complete %s req %p stat %d len %u/%u\n", +			ep->ep.name, &req->req, status, +			req->req.actual, req->req.length); + +	/* don't modify queue heads during completion callback */ +	ep->stopped = 1; +	spin_unlock(&udc->lock); + +	/* this complete() should a func implemented by gadget layer, +	 * eg fsg->bulk_in_complete() */ +	if (req->req.complete) +		req->req.complete(&ep->ep, &req->req); + +	spin_lock(&udc->lock); + +	ep->stopped = stopped; +} + +/*----------------------------------------------------------------- + * nuke(): delete all requests related to this ep + *--------------------------------------------------------------*/ +static void nuke(struct qe_ep *ep, int status) +{ +	/* Whether this eq has request linked */ +	while (!list_empty(&ep->queue)) { +		struct qe_req *req = NULL; +		req = list_entry(ep->queue.next, struct qe_req, queue); + +		done(ep, req, status); +	} +} + +/*---------------------------------------------------------------------------* + * USB and Endpoint manipulate process, include parameter and register       * + *---------------------------------------------------------------------------*/ +/* @value: 1--set stall 0--clean stall */ +static int qe_eprx_stall_change(struct qe_ep *ep, int value) +{ +	u16 tem_usep; +	u8 epnum = ep->epnum; +	struct qe_udc *udc = ep->udc; + +	tem_usep = in_be16(&udc->usb_regs->usb_usep[epnum]); +	tem_usep = tem_usep & ~USB_RHS_MASK; +	if (value == 1) +		tem_usep |= USB_RHS_STALL; +	else if (ep->dir == USB_DIR_IN) +		tem_usep |= USB_RHS_IGNORE_OUT; + +	out_be16(&udc->usb_regs->usb_usep[epnum], tem_usep); +	return 0; +} + +static int qe_eptx_stall_change(struct qe_ep *ep, int value) +{ +	u16 tem_usep; +	u8 epnum = ep->epnum; +	struct qe_udc *udc = ep->udc; + +	tem_usep = in_be16(&udc->usb_regs->usb_usep[epnum]); +	tem_usep = tem_usep & ~USB_THS_MASK; +	if (value == 1) +		tem_usep |= USB_THS_STALL; +	else if (ep->dir == USB_DIR_OUT) +		tem_usep |= USB_THS_IGNORE_IN; + +	out_be16(&udc->usb_regs->usb_usep[epnum], tem_usep); + +	return 0; +} + +static int qe_ep0_stall(struct qe_udc *udc) +{ +	qe_eptx_stall_change(&udc->eps[0], 1); +	qe_eprx_stall_change(&udc->eps[0], 1); +	udc_controller->ep0_state = WAIT_FOR_SETUP; +	udc_controller->ep0_dir = 0; +	return 0; +} + +static int qe_eprx_nack(struct qe_ep *ep) +{ +	u8 epnum = ep->epnum; +	struct qe_udc *udc = ep->udc; + +	if (ep->state == EP_STATE_IDLE) { +		/* Set the ep's nack */ +		clrsetbits_be16(&udc->usb_regs->usb_usep[epnum], +				USB_RHS_MASK, USB_RHS_NACK); + +		/* Mask Rx and Busy interrupts */ +		clrbits16(&udc->usb_regs->usb_usbmr, +				(USB_E_RXB_MASK | USB_E_BSY_MASK)); + +		ep->state = EP_STATE_NACK; +	} +	return 0; +} + +static int qe_eprx_normal(struct qe_ep *ep) +{ +	struct qe_udc *udc = ep->udc; + +	if (ep->state == EP_STATE_NACK) { +		clrsetbits_be16(&udc->usb_regs->usb_usep[ep->epnum], +				USB_RTHS_MASK, USB_THS_IGNORE_IN); + +		/* Unmask RX interrupts */ +		out_be16(&udc->usb_regs->usb_usber, +				USB_E_BSY_MASK | USB_E_RXB_MASK); +		setbits16(&udc->usb_regs->usb_usbmr, +				(USB_E_RXB_MASK | USB_E_BSY_MASK)); + +		ep->state = EP_STATE_IDLE; +		ep->has_data = 0; +	} + +	return 0; +} + +static int qe_ep_cmd_stoptx(struct qe_ep *ep) +{ +	if (ep->udc->soc_type == PORT_CPM) +		cpm_command(CPM_USB_STOP_TX | (ep->epnum << CPM_USB_EP_SHIFT), +				CPM_USB_STOP_TX_OPCODE); +	else +		qe_issue_cmd(QE_USB_STOP_TX, QE_CR_SUBBLOCK_USB, +				ep->epnum, 0); + +	return 0; +} + +static int qe_ep_cmd_restarttx(struct qe_ep *ep) +{ +	if (ep->udc->soc_type == PORT_CPM) +		cpm_command(CPM_USB_RESTART_TX | (ep->epnum << +				CPM_USB_EP_SHIFT), CPM_USB_RESTART_TX_OPCODE); +	else +		qe_issue_cmd(QE_USB_RESTART_TX, QE_CR_SUBBLOCK_USB, +				ep->epnum, 0); + +	return 0; +} + +static int qe_ep_flushtxfifo(struct qe_ep *ep) +{ +	struct qe_udc *udc = ep->udc; +	int i; + +	i = (int)ep->epnum; + +	qe_ep_cmd_stoptx(ep); +	out_8(&udc->usb_regs->usb_uscom, +		USB_CMD_FLUSH_FIFO | (USB_CMD_EP_MASK & (ep->epnum))); +	out_be16(&udc->ep_param[i]->tbptr, in_be16(&udc->ep_param[i]->tbase)); +	out_be32(&udc->ep_param[i]->tstate, 0); +	out_be16(&udc->ep_param[i]->tbcnt, 0); + +	ep->c_txbd = ep->txbase; +	ep->n_txbd = ep->txbase; +	qe_ep_cmd_restarttx(ep); +	return 0; +} + +static int qe_ep_filltxfifo(struct qe_ep *ep) +{ +	struct qe_udc *udc = ep->udc; + +	out_8(&udc->usb_regs->usb_uscom, +			USB_CMD_STR_FIFO | (USB_CMD_EP_MASK & (ep->epnum))); +	return 0; +} + +static int qe_epbds_reset(struct qe_udc *udc, int pipe_num) +{ +	struct qe_ep *ep; +	u32 bdring_len; +	struct qe_bd __iomem *bd; +	int i; + +	ep = &udc->eps[pipe_num]; + +	if (ep->dir == USB_DIR_OUT) +		bdring_len = USB_BDRING_LEN_RX; +	else +		bdring_len = USB_BDRING_LEN; + +	bd = ep->rxbase; +	for (i = 0; i < (bdring_len - 1); i++) { +		out_be32((u32 __iomem *)bd, R_E | R_I); +		bd++; +	} +	out_be32((u32 __iomem *)bd, R_E | R_I | R_W); + +	bd = ep->txbase; +	for (i = 0; i < USB_BDRING_LEN_TX - 1; i++) { +		out_be32(&bd->buf, 0); +		out_be32((u32 __iomem *)bd, 0); +		bd++; +	} +	out_be32((u32 __iomem *)bd, T_W); + +	return 0; +} + +static int qe_ep_reset(struct qe_udc *udc, int pipe_num) +{ +	struct qe_ep *ep; +	u16 tmpusep; + +	ep = &udc->eps[pipe_num]; +	tmpusep = in_be16(&udc->usb_regs->usb_usep[pipe_num]); +	tmpusep &= ~USB_RTHS_MASK; + +	switch (ep->dir) { +	case USB_DIR_BOTH: +		qe_ep_flushtxfifo(ep); +		break; +	case USB_DIR_OUT: +		tmpusep |= USB_THS_IGNORE_IN; +		break; +	case USB_DIR_IN: +		qe_ep_flushtxfifo(ep); +		tmpusep |= USB_RHS_IGNORE_OUT; +		break; +	default: +		break; +	} +	out_be16(&udc->usb_regs->usb_usep[pipe_num], tmpusep); + +	qe_epbds_reset(udc, pipe_num); + +	return 0; +} + +static int qe_ep_toggledata01(struct qe_ep *ep) +{ +	ep->data01 ^= 0x1; +	return 0; +} + +static int qe_ep_bd_init(struct qe_udc *udc, unsigned char pipe_num) +{ +	struct qe_ep *ep = &udc->eps[pipe_num]; +	unsigned long tmp_addr = 0; +	struct usb_ep_para __iomem *epparam; +	int i; +	struct qe_bd __iomem *bd; +	int bdring_len; + +	if (ep->dir == USB_DIR_OUT) +		bdring_len = USB_BDRING_LEN_RX; +	else +		bdring_len = USB_BDRING_LEN; + +	epparam = udc->ep_param[pipe_num]; +	/* alloc multi-ram for BD rings and set the ep parameters */ +	tmp_addr = cpm_muram_alloc(sizeof(struct qe_bd) * (bdring_len + +				USB_BDRING_LEN_TX), QE_ALIGNMENT_OF_BD); +	out_be16(&epparam->rbase, (u16)tmp_addr); +	out_be16(&epparam->tbase, (u16)(tmp_addr + +				(sizeof(struct qe_bd) * bdring_len))); + +	out_be16(&epparam->rbptr, in_be16(&epparam->rbase)); +	out_be16(&epparam->tbptr, in_be16(&epparam->tbase)); + +	ep->rxbase = cpm_muram_addr(tmp_addr); +	ep->txbase = cpm_muram_addr(tmp_addr + (sizeof(struct qe_bd) +				* bdring_len)); +	ep->n_rxbd = ep->rxbase; +	ep->e_rxbd = ep->rxbase; +	ep->n_txbd = ep->txbase; +	ep->c_txbd = ep->txbase; +	ep->data01 = 0; /* data0 */ + +	/* Init TX and RX bds */ +	bd = ep->rxbase; +	for (i = 0; i < bdring_len - 1; i++) { +		out_be32(&bd->buf, 0); +		out_be32((u32 __iomem *)bd, 0); +		bd++; +	} +	out_be32(&bd->buf, 0); +	out_be32((u32 __iomem *)bd, R_W); + +	bd = ep->txbase; +	for (i = 0; i < USB_BDRING_LEN_TX - 1; i++) { +		out_be32(&bd->buf, 0); +		out_be32((u32 __iomem *)bd, 0); +		bd++; +	} +	out_be32(&bd->buf, 0); +	out_be32((u32 __iomem *)bd, T_W); + +	return 0; +} + +static int qe_ep_rxbd_update(struct qe_ep *ep) +{ +	unsigned int size; +	int i; +	unsigned int tmp; +	struct qe_bd __iomem *bd; +	unsigned int bdring_len; + +	if (ep->rxbase == NULL) +		return -EINVAL; + +	bd = ep->rxbase; + +	ep->rxframe = kmalloc(sizeof(*ep->rxframe), GFP_ATOMIC); +	if (ep->rxframe == NULL) { +		dev_err(ep->udc->dev, "malloc rxframe failed\n"); +		return -ENOMEM; +	} + +	qe_frame_init(ep->rxframe); + +	if (ep->dir == USB_DIR_OUT) +		bdring_len = USB_BDRING_LEN_RX; +	else +		bdring_len = USB_BDRING_LEN; + +	size = (ep->ep.maxpacket + USB_CRC_SIZE + 2) * (bdring_len + 1); +	ep->rxbuffer = kzalloc(size, GFP_ATOMIC); +	if (ep->rxbuffer == NULL) { +		dev_err(ep->udc->dev, "malloc rxbuffer failed,size=%d\n", +				size); +		kfree(ep->rxframe); +		return -ENOMEM; +	} + +	ep->rxbuf_d = virt_to_phys((void *)ep->rxbuffer); +	if (ep->rxbuf_d == DMA_ADDR_INVALID) { +		ep->rxbuf_d = dma_map_single(udc_controller->gadget.dev.parent, +					ep->rxbuffer, +					size, +					DMA_FROM_DEVICE); +		ep->rxbufmap = 1; +	} else { +		dma_sync_single_for_device(udc_controller->gadget.dev.parent, +					ep->rxbuf_d, size, +					DMA_FROM_DEVICE); +		ep->rxbufmap = 0; +	} + +	size = ep->ep.maxpacket + USB_CRC_SIZE + 2; +	tmp = ep->rxbuf_d; +	tmp = (u32)(((tmp >> 2) << 2) + 4); + +	for (i = 0; i < bdring_len - 1; i++) { +		out_be32(&bd->buf, tmp); +		out_be32((u32 __iomem *)bd, (R_E | R_I)); +		tmp = tmp + size; +		bd++; +	} +	out_be32(&bd->buf, tmp); +	out_be32((u32 __iomem *)bd, (R_E | R_I | R_W)); + +	return 0; +} + +static int qe_ep_register_init(struct qe_udc *udc, unsigned char pipe_num) +{ +	struct qe_ep *ep = &udc->eps[pipe_num]; +	struct usb_ep_para __iomem *epparam; +	u16 usep, logepnum; +	u16 tmp; +	u8 rtfcr = 0; + +	epparam = udc->ep_param[pipe_num]; + +	usep = 0; +	logepnum = (ep->desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK); +	usep |= (logepnum << USB_EPNUM_SHIFT); + +	switch (ep->desc->bmAttributes & 0x03) { +	case USB_ENDPOINT_XFER_BULK: +		usep |= USB_TRANS_BULK; +		break; +	case USB_ENDPOINT_XFER_ISOC: +		usep |=  USB_TRANS_ISO; +		break; +	case USB_ENDPOINT_XFER_INT: +		usep |= USB_TRANS_INT; +		break; +	default: +		usep |= USB_TRANS_CTR; +		break; +	} + +	switch (ep->dir) { +	case USB_DIR_OUT: +		usep |= USB_THS_IGNORE_IN; +		break; +	case USB_DIR_IN: +		usep |= USB_RHS_IGNORE_OUT; +		break; +	default: +		break; +	} +	out_be16(&udc->usb_regs->usb_usep[pipe_num], usep); + +	rtfcr = 0x30; +	out_8(&epparam->rbmr, rtfcr); +	out_8(&epparam->tbmr, rtfcr); + +	tmp = (u16)(ep->ep.maxpacket + USB_CRC_SIZE); +	/* MRBLR must be divisble by 4 */ +	tmp = (u16)(((tmp >> 2) << 2) + 4); +	out_be16(&epparam->mrblr, tmp); + +	return 0; +} + +static int qe_ep_init(struct qe_udc *udc, +		      unsigned char pipe_num, +		      const struct usb_endpoint_descriptor *desc) +{ +	struct qe_ep *ep = &udc->eps[pipe_num]; +	unsigned long flags; +	int reval = 0; +	u16 max = 0; + +	max = le16_to_cpu(desc->wMaxPacketSize); + +	/* check the max package size validate for this endpoint */ +	/* Refer to USB2.0 spec table 9-13, +	*/ +	if (pipe_num != 0) { +		switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) { +		case USB_ENDPOINT_XFER_BULK: +			if (strstr(ep->ep.name, "-iso") +					|| strstr(ep->ep.name, "-int")) +				goto en_done; +			switch (udc->gadget.speed) { +			case USB_SPEED_HIGH: +			if ((max == 128) || (max == 256) || (max == 512)) +				break; +			default: +				switch (max) { +				case 4: +				case 8: +				case 16: +				case 32: +				case 64: +					break; +				default: +				case USB_SPEED_LOW: +					goto en_done; +				} +			} +			break; +		case USB_ENDPOINT_XFER_INT: +			if (strstr(ep->ep.name, "-iso"))	/* bulk is ok */ +				goto en_done; +			switch (udc->gadget.speed) { +			case USB_SPEED_HIGH: +				if (max <= 1024) +					break; +			case USB_SPEED_FULL: +				if (max <= 64) +					break; +			default: +				if (max <= 8) +					break; +				goto en_done; +			} +			break; +		case USB_ENDPOINT_XFER_ISOC: +			if (strstr(ep->ep.name, "-bulk") +				|| strstr(ep->ep.name, "-int")) +				goto en_done; +			switch (udc->gadget.speed) { +			case USB_SPEED_HIGH: +				if (max <= 1024) +					break; +			case USB_SPEED_FULL: +				if (max <= 1023) +					break; +			default: +				goto en_done; +			} +			break; +		case USB_ENDPOINT_XFER_CONTROL: +			if (strstr(ep->ep.name, "-iso") +				|| strstr(ep->ep.name, "-int")) +				goto en_done; +			switch (udc->gadget.speed) { +			case USB_SPEED_HIGH: +			case USB_SPEED_FULL: +				switch (max) { +				case 1: +				case 2: +				case 4: +				case 8: +				case 16: +				case 32: +				case 64: +					break; +				default: +					goto en_done; +				} +			case USB_SPEED_LOW: +				switch (max) { +				case 1: +				case 2: +				case 4: +				case 8: +					break; +				default: +					goto en_done; +				} +			default: +				goto en_done; +			} +			break; + +		default: +			goto en_done; +		} +	} /* if ep0*/ + +	spin_lock_irqsave(&udc->lock, flags); + +	/* initialize ep structure */ +	ep->ep.maxpacket = max; +	ep->tm = (u8)(desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK); +	ep->desc = desc; +	ep->stopped = 0; +	ep->init = 1; + +	if (pipe_num == 0) { +		ep->dir = USB_DIR_BOTH; +		udc->ep0_dir = USB_DIR_OUT; +		udc->ep0_state = WAIT_FOR_SETUP; +	} else	{ +		switch (desc->bEndpointAddress & USB_ENDPOINT_DIR_MASK) { +		case USB_DIR_OUT: +			ep->dir = USB_DIR_OUT; +			break; +		case USB_DIR_IN: +			ep->dir = USB_DIR_IN; +		default: +			break; +		} +	} + +	/* hardware special operation */ +	qe_ep_bd_init(udc, pipe_num); +	if ((ep->tm == USBP_TM_CTL) || (ep->dir == USB_DIR_OUT)) { +		reval = qe_ep_rxbd_update(ep); +		if (reval) +			goto en_done1; +	} + +	if ((ep->tm == USBP_TM_CTL) || (ep->dir == USB_DIR_IN)) { +		ep->txframe = kmalloc(sizeof(*ep->txframe), GFP_ATOMIC); +		if (ep->txframe == NULL) { +			dev_err(udc->dev, "malloc txframe failed\n"); +			goto en_done2; +		} +		qe_frame_init(ep->txframe); +	} + +	qe_ep_register_init(udc, pipe_num); + +	/* Now HW will be NAKing transfers to that EP, +	 * until a buffer is queued to it. */ +	spin_unlock_irqrestore(&udc->lock, flags); + +	return 0; +en_done2: +	kfree(ep->rxbuffer); +	kfree(ep->rxframe); +en_done1: +	spin_unlock_irqrestore(&udc->lock, flags); +en_done: +	dev_dbg(udc->dev, "failed to initialize %s\n", ep->ep.name); +	return -ENODEV; +} + +static inline void qe_usb_enable(void) +{ +	setbits8(&udc_controller->usb_regs->usb_usmod, USB_MODE_EN); +} + +static inline void qe_usb_disable(void) +{ +	clrbits8(&udc_controller->usb_regs->usb_usmod, USB_MODE_EN); +} + +/*----------------------------------------------------------------------------* + *		USB and EP basic manipulate function end		      * + *----------------------------------------------------------------------------*/ + + +/****************************************************************************** +		UDC transmit and receive process + ******************************************************************************/ +static void recycle_one_rxbd(struct qe_ep *ep) +{ +	u32 bdstatus; + +	bdstatus = in_be32((u32 __iomem *)ep->e_rxbd); +	bdstatus = R_I | R_E | (bdstatus & R_W); +	out_be32((u32 __iomem *)ep->e_rxbd, bdstatus); + +	if (bdstatus & R_W) +		ep->e_rxbd = ep->rxbase; +	else +		ep->e_rxbd++; +} + +static void recycle_rxbds(struct qe_ep *ep, unsigned char stopatnext) +{ +	u32 bdstatus; +	struct qe_bd __iomem *bd, *nextbd; +	unsigned char stop = 0; + +	nextbd = ep->n_rxbd; +	bd = ep->e_rxbd; +	bdstatus = in_be32((u32 __iomem *)bd); + +	while (!(bdstatus & R_E) && !(bdstatus & BD_LENGTH_MASK) && !stop) { +		bdstatus = R_E | R_I | (bdstatus & R_W); +		out_be32((u32 __iomem *)bd, bdstatus); + +		if (bdstatus & R_W) +			bd = ep->rxbase; +		else +			bd++; + +		bdstatus = in_be32((u32 __iomem *)bd); +		if (stopatnext && (bd == nextbd)) +			stop = 1; +	} + +	ep->e_rxbd = bd; +} + +static void ep_recycle_rxbds(struct qe_ep *ep) +{ +	struct qe_bd __iomem *bd = ep->n_rxbd; +	u32 bdstatus; +	u8 epnum = ep->epnum; +	struct qe_udc *udc = ep->udc; + +	bdstatus = in_be32((u32 __iomem *)bd); +	if (!(bdstatus & R_E) && !(bdstatus & BD_LENGTH_MASK)) { +		bd = ep->rxbase + +				((in_be16(&udc->ep_param[epnum]->rbptr) - +				  in_be16(&udc->ep_param[epnum]->rbase)) +				 >> 3); +		bdstatus = in_be32((u32 __iomem *)bd); + +		if (bdstatus & R_W) +			bd = ep->rxbase; +		else +			bd++; + +		ep->e_rxbd = bd; +		recycle_rxbds(ep, 0); +		ep->e_rxbd = ep->n_rxbd; +	} else +		recycle_rxbds(ep, 1); + +	if (in_be16(&udc->usb_regs->usb_usber) & USB_E_BSY_MASK) +		out_be16(&udc->usb_regs->usb_usber, USB_E_BSY_MASK); + +	if (ep->has_data <= 0 && (!list_empty(&ep->queue))) +		qe_eprx_normal(ep); + +	ep->localnack = 0; +} + +static void setup_received_handle(struct qe_udc *udc, +					struct usb_ctrlrequest *setup); +static int qe_ep_rxframe_handle(struct qe_ep *ep); +static void ep0_req_complete(struct qe_udc *udc, struct qe_req *req); +/* when BD PID is setup, handle the packet */ +static int ep0_setup_handle(struct qe_udc *udc) +{ +	struct qe_ep *ep = &udc->eps[0]; +	struct qe_frame *pframe; +	unsigned int fsize; +	u8 *cp; + +	pframe = ep->rxframe; +	if ((frame_get_info(pframe) & PID_SETUP) +			&& (udc->ep0_state == WAIT_FOR_SETUP)) { +		fsize = frame_get_length(pframe); +		if (unlikely(fsize != 8)) +			return -EINVAL; +		cp = (u8 *)&udc->local_setup_buff; +		memcpy(cp, pframe->data, fsize); +		ep->data01 = 1; + +		/* handle the usb command base on the usb_ctrlrequest */ +		setup_received_handle(udc, &udc->local_setup_buff); +		return 0; +	} +	return -EINVAL; +} + +static int qe_ep0_rx(struct qe_udc *udc) +{ +	struct qe_ep *ep = &udc->eps[0]; +	struct qe_frame *pframe; +	struct qe_bd __iomem *bd; +	u32 bdstatus, length; +	u32 vaddr; + +	pframe = ep->rxframe; + +	if (ep->dir == USB_DIR_IN) { +		dev_err(udc->dev, "ep0 not a control endpoint\n"); +		return -EINVAL; +	} + +	bd = ep->n_rxbd; +	bdstatus = in_be32((u32 __iomem *)bd); +	length = bdstatus & BD_LENGTH_MASK; + +	while (!(bdstatus & R_E) && length) { +		if ((bdstatus & R_F) && (bdstatus & R_L) +			&& !(bdstatus & R_ERROR)) { +			if (length == USB_CRC_SIZE) { +				udc->ep0_state = WAIT_FOR_SETUP; +				dev_vdbg(udc->dev, +					"receive a ZLP in status phase\n"); +			} else { +				qe_frame_clean(pframe); +				vaddr = (u32)phys_to_virt(in_be32(&bd->buf)); +				frame_set_data(pframe, (u8 *)vaddr); +				frame_set_length(pframe, +						(length - USB_CRC_SIZE)); +				frame_set_status(pframe, FRAME_OK); +				switch (bdstatus & R_PID) { +				case R_PID_SETUP: +					frame_set_info(pframe, PID_SETUP); +					break; +				case R_PID_DATA1: +					frame_set_info(pframe, PID_DATA1); +					break; +				default: +					frame_set_info(pframe, PID_DATA0); +					break; +				} + +				if ((bdstatus & R_PID) == R_PID_SETUP) +					ep0_setup_handle(udc); +				else +					qe_ep_rxframe_handle(ep); +			} +		} else { +			dev_err(udc->dev, "The receive frame with error!\n"); +		} + +		/* note: don't clear the rxbd's buffer address */ +		recycle_one_rxbd(ep); + +		/* Get next BD */ +		if (bdstatus & R_W) +			bd = ep->rxbase; +		else +			bd++; + +		bdstatus = in_be32((u32 __iomem *)bd); +		length = bdstatus & BD_LENGTH_MASK; + +	} + +	ep->n_rxbd = bd; + +	return 0; +} + +static int qe_ep_rxframe_handle(struct qe_ep *ep) +{ +	struct qe_frame *pframe; +	u8 framepid = 0; +	unsigned int fsize; +	u8 *cp; +	struct qe_req *req; + +	pframe = ep->rxframe; + +	if (frame_get_info(pframe) & PID_DATA1) +		framepid = 0x1; + +	if (framepid != ep->data01) { +		dev_err(ep->udc->dev, "the data01 error!\n"); +		return -EIO; +	} + +	fsize = frame_get_length(pframe); +	if (list_empty(&ep->queue)) { +		dev_err(ep->udc->dev, "the %s have no requeue!\n", ep->name); +	} else { +		req = list_entry(ep->queue.next, struct qe_req, queue); + +		cp = (u8 *)(req->req.buf) + req->req.actual; +		if (cp) { +			memcpy(cp, pframe->data, fsize); +			req->req.actual += fsize; +			if ((fsize < ep->ep.maxpacket) || +					(req->req.actual >= req->req.length)) { +				if (ep->epnum == 0) +					ep0_req_complete(ep->udc, req); +				else +					done(ep, req, 0); +				if (list_empty(&ep->queue) && ep->epnum != 0) +					qe_eprx_nack(ep); +			} +		} +	} + +	qe_ep_toggledata01(ep); + +	return 0; +} + +static void ep_rx_tasklet(unsigned long data) +{ +	struct qe_udc *udc = (struct qe_udc *)data; +	struct qe_ep *ep; +	struct qe_frame *pframe; +	struct qe_bd __iomem *bd; +	unsigned long flags; +	u32 bdstatus, length; +	u32 vaddr, i; + +	spin_lock_irqsave(&udc->lock, flags); + +	for (i = 1; i < USB_MAX_ENDPOINTS; i++) { +		ep = &udc->eps[i]; + +		if (ep->dir == USB_DIR_IN || ep->enable_tasklet == 0) { +			dev_dbg(udc->dev, +				"This is a transmit ep or disable tasklet!\n"); +			continue; +		} + +		pframe = ep->rxframe; +		bd = ep->n_rxbd; +		bdstatus = in_be32((u32 __iomem *)bd); +		length = bdstatus & BD_LENGTH_MASK; + +		while (!(bdstatus & R_E) && length) { +			if (list_empty(&ep->queue)) { +				qe_eprx_nack(ep); +				dev_dbg(udc->dev, +					"The rxep have noreq %d\n", +					ep->has_data); +				break; +			} + +			if ((bdstatus & R_F) && (bdstatus & R_L) +				&& !(bdstatus & R_ERROR)) { +				qe_frame_clean(pframe); +				vaddr = (u32)phys_to_virt(in_be32(&bd->buf)); +				frame_set_data(pframe, (u8 *)vaddr); +				frame_set_length(pframe, +						(length - USB_CRC_SIZE)); +				frame_set_status(pframe, FRAME_OK); +				switch (bdstatus & R_PID) { +				case R_PID_DATA1: +					frame_set_info(pframe, PID_DATA1); +					break; +				case R_PID_SETUP: +					frame_set_info(pframe, PID_SETUP); +					break; +				default: +					frame_set_info(pframe, PID_DATA0); +					break; +				} +				/* handle the rx frame */ +				qe_ep_rxframe_handle(ep); +			} else { +				dev_err(udc->dev, +					"error in received frame\n"); +			} +			/* note: don't clear the rxbd's buffer address */ +			/*clear the length */ +			out_be32((u32 __iomem *)bd, bdstatus & BD_STATUS_MASK); +			ep->has_data--; +			if (!(ep->localnack)) +				recycle_one_rxbd(ep); + +			/* Get next BD */ +			if (bdstatus & R_W) +				bd = ep->rxbase; +			else +				bd++; + +			bdstatus = in_be32((u32 __iomem *)bd); +			length = bdstatus & BD_LENGTH_MASK; +		} + +		ep->n_rxbd = bd; + +		if (ep->localnack) +			ep_recycle_rxbds(ep); + +		ep->enable_tasklet = 0; +	} /* for i=1 */ + +	spin_unlock_irqrestore(&udc->lock, flags); +} + +static int qe_ep_rx(struct qe_ep *ep) +{ +	struct qe_udc *udc; +	struct qe_frame *pframe; +	struct qe_bd __iomem *bd; +	u16 swoffs, ucoffs, emptybds; + +	udc = ep->udc; +	pframe = ep->rxframe; + +	if (ep->dir == USB_DIR_IN) { +		dev_err(udc->dev, "transmit ep in rx function\n"); +		return -EINVAL; +	} + +	bd = ep->n_rxbd; + +	swoffs = (u16)(bd - ep->rxbase); +	ucoffs = (u16)((in_be16(&udc->ep_param[ep->epnum]->rbptr) - +			in_be16(&udc->ep_param[ep->epnum]->rbase)) >> 3); +	if (swoffs < ucoffs) +		emptybds = USB_BDRING_LEN_RX - ucoffs + swoffs; +	else +		emptybds = swoffs - ucoffs; + +	if (emptybds < MIN_EMPTY_BDS) { +		qe_eprx_nack(ep); +		ep->localnack = 1; +		dev_vdbg(udc->dev, "%d empty bds, send NACK\n", emptybds); +	} +	ep->has_data = USB_BDRING_LEN_RX - emptybds; + +	if (list_empty(&ep->queue)) { +		qe_eprx_nack(ep); +		dev_vdbg(udc->dev, "The rxep have no req queued with %d BDs\n", +				ep->has_data); +		return 0; +	} + +	tasklet_schedule(&udc->rx_tasklet); +	ep->enable_tasklet = 1; + +	return 0; +} + +/* send data from a frame, no matter what tx_req */ +static int qe_ep_tx(struct qe_ep *ep, struct qe_frame *frame) +{ +	struct qe_udc *udc = ep->udc; +	struct qe_bd __iomem *bd; +	u16 saveusbmr; +	u32 bdstatus, pidmask; +	u32 paddr; + +	if (ep->dir == USB_DIR_OUT) { +		dev_err(udc->dev, "receive ep passed to tx function\n"); +		return -EINVAL; +	} + +	/* Disable the Tx interrupt */ +	saveusbmr = in_be16(&udc->usb_regs->usb_usbmr); +	out_be16(&udc->usb_regs->usb_usbmr, +			saveusbmr & ~(USB_E_TXB_MASK | USB_E_TXE_MASK)); + +	bd = ep->n_txbd; +	bdstatus = in_be32((u32 __iomem *)bd); + +	if (!(bdstatus & (T_R | BD_LENGTH_MASK))) { +		if (frame_get_length(frame) == 0) { +			frame_set_data(frame, udc->nullbuf); +			frame_set_length(frame, 2); +			frame->info |= (ZLP | NO_CRC); +			dev_vdbg(udc->dev, "the frame size = 0\n"); +		} +		paddr = virt_to_phys((void *)frame->data); +		out_be32(&bd->buf, paddr); +		bdstatus = (bdstatus&T_W); +		if (!(frame_get_info(frame) & NO_CRC)) +			bdstatus |= T_R | T_I | T_L | T_TC +					| frame_get_length(frame); +		else +			bdstatus |= T_R | T_I | T_L | frame_get_length(frame); + +		/* if the packet is a ZLP in status phase */ +		if ((ep->epnum == 0) && (udc->ep0_state == DATA_STATE_NEED_ZLP)) +			ep->data01 = 0x1; + +		if (ep->data01) { +			pidmask = T_PID_DATA1; +			frame->info |= PID_DATA1; +		} else { +			pidmask = T_PID_DATA0; +			frame->info |= PID_DATA0; +		} +		bdstatus |= T_CNF; +		bdstatus |= pidmask; +		out_be32((u32 __iomem *)bd, bdstatus); +		qe_ep_filltxfifo(ep); + +		/* enable the TX interrupt */ +		out_be16(&udc->usb_regs->usb_usbmr, saveusbmr); + +		qe_ep_toggledata01(ep); +		if (bdstatus & T_W) +			ep->n_txbd = ep->txbase; +		else +			ep->n_txbd++; + +		return 0; +	} else { +		out_be16(&udc->usb_regs->usb_usbmr, saveusbmr); +		dev_vdbg(udc->dev, "The tx bd is not ready!\n"); +		return -EBUSY; +	} +} + +/* when a bd was transmitted, the function can + * handle the tx_req, not include ep0           */ +static int txcomplete(struct qe_ep *ep, unsigned char restart) +{ +	if (ep->tx_req != NULL) { +		if (!restart) { +			int asent = ep->last; +			ep->sent += asent; +			ep->last -= asent; +		} else { +			ep->last = 0; +		} + +		/* a request already were transmitted completely */ +		if ((ep->tx_req->req.length - ep->sent) <= 0) { +			ep->tx_req->req.actual = (unsigned int)ep->sent; +			done(ep, ep->tx_req, 0); +			ep->tx_req = NULL; +			ep->last = 0; +			ep->sent = 0; +		} +	} + +	/* we should gain a new tx_req fot this endpoint */ +	if (ep->tx_req == NULL) { +		if (!list_empty(&ep->queue)) { +			ep->tx_req = list_entry(ep->queue.next,	struct qe_req, +							queue); +			ep->last = 0; +			ep->sent = 0; +		} +	} + +	return 0; +} + +/* give a frame and a tx_req, send some data */ +static int qe_usb_senddata(struct qe_ep *ep, struct qe_frame *frame) +{ +	unsigned int size; +	u8 *buf; + +	qe_frame_clean(frame); +	size = min_t(u32, (ep->tx_req->req.length - ep->sent), +				ep->ep.maxpacket); +	buf = (u8 *)ep->tx_req->req.buf + ep->sent; +	if (buf && size) { +		ep->last = size; +		frame_set_data(frame, buf); +		frame_set_length(frame, size); +		frame_set_status(frame, FRAME_OK); +		frame_set_info(frame, 0); +		return qe_ep_tx(ep, frame); +	} +	return -EIO; +} + +/* give a frame struct,send a ZLP */ +static int sendnulldata(struct qe_ep *ep, struct qe_frame *frame, uint infor) +{ +	struct qe_udc *udc = ep->udc; + +	if (frame == NULL) +		return -ENODEV; + +	qe_frame_clean(frame); +	frame_set_data(frame, (u8 *)udc->nullbuf); +	frame_set_length(frame, 2); +	frame_set_status(frame, FRAME_OK); +	frame_set_info(frame, (ZLP | NO_CRC | infor)); + +	return qe_ep_tx(ep, frame); +} + +static int frame_create_tx(struct qe_ep *ep, struct qe_frame *frame) +{ +	struct qe_req *req = ep->tx_req; +	int reval; + +	if (req == NULL) +		return -ENODEV; + +	if ((req->req.length - ep->sent) > 0) +		reval = qe_usb_senddata(ep, frame); +	else +		reval = sendnulldata(ep, frame, 0); + +	return reval; +} + +/* if direction is DIR_IN, the status is Device->Host + * if direction is DIR_OUT, the status transaction is Device<-Host + * in status phase, udc create a request and gain status */ +static int ep0_prime_status(struct qe_udc *udc, int direction) +{ + +	struct qe_ep *ep = &udc->eps[0]; + +	if (direction == USB_DIR_IN) { +		udc->ep0_state = DATA_STATE_NEED_ZLP; +		udc->ep0_dir = USB_DIR_IN; +		sendnulldata(ep, ep->txframe, SETUP_STATUS | NO_REQ); +	} else { +		udc->ep0_dir = USB_DIR_OUT; +		udc->ep0_state = WAIT_FOR_OUT_STATUS; +	} + +	return 0; +} + +/* a request complete in ep0, whether gadget request or udc request */ +static void ep0_req_complete(struct qe_udc *udc, struct qe_req *req) +{ +	struct qe_ep *ep = &udc->eps[0]; +	/* because usb and ep's status already been set in ch9setaddress() */ + +	switch (udc->ep0_state) { +	case DATA_STATE_XMIT: +		done(ep, req, 0); +		/* receive status phase */ +		if (ep0_prime_status(udc, USB_DIR_OUT)) +			qe_ep0_stall(udc); +		break; + +	case DATA_STATE_NEED_ZLP: +		done(ep, req, 0); +		udc->ep0_state = WAIT_FOR_SETUP; +		break; + +	case DATA_STATE_RECV: +		done(ep, req, 0); +		/* send status phase */ +		if (ep0_prime_status(udc, USB_DIR_IN)) +			qe_ep0_stall(udc); +		break; + +	case WAIT_FOR_OUT_STATUS: +		done(ep, req, 0); +		udc->ep0_state = WAIT_FOR_SETUP; +		break; + +	case WAIT_FOR_SETUP: +		dev_vdbg(udc->dev, "Unexpected interrupt\n"); +		break; + +	default: +		qe_ep0_stall(udc); +		break; +	} +} + +static int ep0_txcomplete(struct qe_ep *ep, unsigned char restart) +{ +	struct qe_req *tx_req = NULL; +	struct qe_frame *frame = ep->txframe; + +	if ((frame_get_info(frame) & (ZLP | NO_REQ)) == (ZLP | NO_REQ)) { +		if (!restart) +			ep->udc->ep0_state = WAIT_FOR_SETUP; +		else +			sendnulldata(ep, ep->txframe, SETUP_STATUS | NO_REQ); +		return 0; +	} + +	tx_req = ep->tx_req; +	if (tx_req != NULL) { +		if (!restart) { +			int asent = ep->last; +			ep->sent += asent; +			ep->last -= asent; +		} else { +			ep->last = 0; +		} + +		/* a request already were transmitted completely */ +		if ((ep->tx_req->req.length - ep->sent) <= 0) { +			ep->tx_req->req.actual = (unsigned int)ep->sent; +			ep0_req_complete(ep->udc, ep->tx_req); +			ep->tx_req = NULL; +			ep->last = 0; +			ep->sent = 0; +		} +	} else { +		dev_vdbg(ep->udc->dev, "the ep0_controller have no req\n"); +	} + +	return 0; +} + +static int ep0_txframe_handle(struct qe_ep *ep) +{ +	/* if have error, transmit again */ +	if (frame_get_status(ep->txframe) & FRAME_ERROR) { +		qe_ep_flushtxfifo(ep); +		dev_vdbg(ep->udc->dev, "The EP0 transmit data have error!\n"); +		if (frame_get_info(ep->txframe) & PID_DATA0) +			ep->data01 = 0; +		else +			ep->data01 = 1; + +		ep0_txcomplete(ep, 1); +	} else +		ep0_txcomplete(ep, 0); + +	frame_create_tx(ep, ep->txframe); +	return 0; +} + +static int qe_ep0_txconf(struct qe_ep *ep) +{ +	struct qe_bd __iomem *bd; +	struct qe_frame *pframe; +	u32 bdstatus; + +	bd = ep->c_txbd; +	bdstatus = in_be32((u32 __iomem *)bd); +	while (!(bdstatus & T_R) && (bdstatus & ~T_W)) { +		pframe = ep->txframe; + +		/* clear and recycle the BD */ +		out_be32((u32 __iomem *)bd, bdstatus & T_W); +		out_be32(&bd->buf, 0); +		if (bdstatus & T_W) +			ep->c_txbd = ep->txbase; +		else +			ep->c_txbd++; + +		if (ep->c_txbd == ep->n_txbd) { +			if (bdstatus & DEVICE_T_ERROR) { +				frame_set_status(pframe, FRAME_ERROR); +				if (bdstatus & T_TO) +					pframe->status |= TX_ER_TIMEOUT; +				if (bdstatus & T_UN) +					pframe->status |= TX_ER_UNDERUN; +			} +			ep0_txframe_handle(ep); +		} + +		bd = ep->c_txbd; +		bdstatus = in_be32((u32 __iomem *)bd); +	} + +	return 0; +} + +static int ep_txframe_handle(struct qe_ep *ep) +{ +	if (frame_get_status(ep->txframe) & FRAME_ERROR) { +		qe_ep_flushtxfifo(ep); +		dev_vdbg(ep->udc->dev, "The EP0 transmit data have error!\n"); +		if (frame_get_info(ep->txframe) & PID_DATA0) +			ep->data01 = 0; +		else +			ep->data01 = 1; + +		txcomplete(ep, 1); +	} else +		txcomplete(ep, 0); + +	frame_create_tx(ep, ep->txframe); /* send the data */ +	return 0; +} + +/* confirm the already trainsmited bd */ +static int qe_ep_txconf(struct qe_ep *ep) +{ +	struct qe_bd __iomem *bd; +	struct qe_frame *pframe = NULL; +	u32 bdstatus; +	unsigned char breakonrxinterrupt = 0; + +	bd = ep->c_txbd; +	bdstatus = in_be32((u32 __iomem *)bd); +	while (!(bdstatus & T_R) && (bdstatus & ~T_W)) { +		pframe = ep->txframe; +		if (bdstatus & DEVICE_T_ERROR) { +			frame_set_status(pframe, FRAME_ERROR); +			if (bdstatus & T_TO) +				pframe->status |= TX_ER_TIMEOUT; +			if (bdstatus & T_UN) +				pframe->status |= TX_ER_UNDERUN; +		} + +		/* clear and recycle the BD */ +		out_be32((u32 __iomem *)bd, bdstatus & T_W); +		out_be32(&bd->buf, 0); +		if (bdstatus & T_W) +			ep->c_txbd = ep->txbase; +		else +			ep->c_txbd++; + +		/* handle the tx frame */ +		ep_txframe_handle(ep); +		bd = ep->c_txbd; +		bdstatus = in_be32((u32 __iomem *)bd); +	} +	if (breakonrxinterrupt) +		return -EIO; +	else +		return 0; +} + +/* Add a request in queue, and try to transmit a packet */ +static int ep_req_send(struct qe_ep *ep, struct qe_req *req) +{ +	int reval = 0; + +	if (ep->tx_req == NULL) { +		ep->sent = 0; +		ep->last = 0; +		txcomplete(ep, 0); /* can gain a new tx_req */ +		reval = frame_create_tx(ep, ep->txframe); +	} +	return reval; +} + +/* Maybe this is a good ideal */ +static int ep_req_rx(struct qe_ep *ep, struct qe_req *req) +{ +	struct qe_udc *udc = ep->udc; +	struct qe_frame *pframe = NULL; +	struct qe_bd __iomem *bd; +	u32 bdstatus, length; +	u32 vaddr, fsize; +	u8 *cp; +	u8 finish_req = 0; +	u8 framepid; + +	if (list_empty(&ep->queue)) { +		dev_vdbg(udc->dev, "the req already finish!\n"); +		return 0; +	} +	pframe = ep->rxframe; + +	bd = ep->n_rxbd; +	bdstatus = in_be32((u32 __iomem *)bd); +	length = bdstatus & BD_LENGTH_MASK; + +	while (!(bdstatus & R_E) && length) { +		if (finish_req) +			break; +		if ((bdstatus & R_F) && (bdstatus & R_L) +					&& !(bdstatus & R_ERROR)) { +			qe_frame_clean(pframe); +			vaddr = (u32)phys_to_virt(in_be32(&bd->buf)); +			frame_set_data(pframe, (u8 *)vaddr); +			frame_set_length(pframe, (length - USB_CRC_SIZE)); +			frame_set_status(pframe, FRAME_OK); +			switch (bdstatus & R_PID) { +			case R_PID_DATA1: +				frame_set_info(pframe, PID_DATA1); break; +			default: +				frame_set_info(pframe, PID_DATA0); break; +			} +			/* handle the rx frame */ + +			if (frame_get_info(pframe) & PID_DATA1) +				framepid = 0x1; +			else +				framepid = 0; + +			if (framepid != ep->data01) { +				dev_vdbg(udc->dev, "the data01 error!\n"); +			} else { +				fsize = frame_get_length(pframe); + +				cp = (u8 *)(req->req.buf) + req->req.actual; +				if (cp) { +					memcpy(cp, pframe->data, fsize); +					req->req.actual += fsize; +					if ((fsize < ep->ep.maxpacket) +						|| (req->req.actual >= +							req->req.length)) { +						finish_req = 1; +						done(ep, req, 0); +						if (list_empty(&ep->queue)) +							qe_eprx_nack(ep); +					} +				} +				qe_ep_toggledata01(ep); +			} +		} else { +			dev_err(udc->dev, "The receive frame with error!\n"); +		} + +		/* note: don't clear the rxbd's buffer address * +		 * only Clear the length */ +		out_be32((u32 __iomem *)bd, (bdstatus & BD_STATUS_MASK)); +		ep->has_data--; + +		/* Get next BD */ +		if (bdstatus & R_W) +			bd = ep->rxbase; +		else +			bd++; + +		bdstatus = in_be32((u32 __iomem *)bd); +		length = bdstatus & BD_LENGTH_MASK; +	} + +	ep->n_rxbd = bd; +	ep_recycle_rxbds(ep); + +	return 0; +} + +/* only add the request in queue */ +static int ep_req_receive(struct qe_ep *ep, struct qe_req *req) +{ +	if (ep->state == EP_STATE_NACK) { +		if (ep->has_data <= 0) { +			/* Enable rx and unmask rx interrupt */ +			qe_eprx_normal(ep); +		} else { +			/* Copy the exist BD data */ +			ep_req_rx(ep, req); +		} +	} + +	return 0; +} + +/******************************************************************** +	Internal Used Function End +********************************************************************/ + +/*----------------------------------------------------------------------- +	Endpoint Management Functions For Gadget + -----------------------------------------------------------------------*/ +static int qe_ep_enable(struct usb_ep *_ep, +			 const struct usb_endpoint_descriptor *desc) +{ +	struct qe_udc *udc; +	struct qe_ep *ep; +	int retval = 0; +	unsigned char epnum; + +	ep = container_of(_ep, struct qe_ep, ep); + +	/* catch various bogus parameters */ +	if (!_ep || !desc || ep->desc || _ep->name == ep_name[0] || +			(desc->bDescriptorType != USB_DT_ENDPOINT)) +		return -EINVAL; + +	udc = ep->udc; +	if (!udc->driver || (udc->gadget.speed == USB_SPEED_UNKNOWN)) +		return -ESHUTDOWN; + +	epnum = (u8)desc->bEndpointAddress & 0xF; + +	retval = qe_ep_init(udc, epnum, desc); +	if (retval != 0) { +		cpm_muram_free(cpm_muram_offset(ep->rxbase)); +		dev_dbg(udc->dev, "enable ep%d failed\n", ep->epnum); +		return -EINVAL; +	} +	dev_dbg(udc->dev, "enable ep%d successful\n", ep->epnum); +	return 0; +} + +static int qe_ep_disable(struct usb_ep *_ep) +{ +	struct qe_udc *udc; +	struct qe_ep *ep; +	unsigned long flags; +	unsigned int size; + +	ep = container_of(_ep, struct qe_ep, ep); +	udc = ep->udc; + +	if (!_ep || !ep->desc) { +		dev_dbg(udc->dev, "%s not enabled\n", _ep ? ep->ep.name : NULL); +		return -EINVAL; +	} + +	spin_lock_irqsave(&udc->lock, flags); +	/* Nuke all pending requests (does flush) */ +	nuke(ep, -ESHUTDOWN); +	ep->desc = NULL; +	ep->stopped = 1; +	spin_unlock_irqrestore(&udc->lock, flags); + +	cpm_muram_free(cpm_muram_offset(ep->rxbase)); + +	if (ep->dir == USB_DIR_OUT) +		size = (ep->ep.maxpacket + USB_CRC_SIZE + 2) * +				(USB_BDRING_LEN_RX + 1); +	else +		size = (ep->ep.maxpacket + USB_CRC_SIZE + 2) * +				(USB_BDRING_LEN + 1); + +	if (ep->dir != USB_DIR_IN) { +		kfree(ep->rxframe); +		if (ep->rxbufmap) { +			dma_unmap_single(udc_controller->gadget.dev.parent, +					ep->rxbuf_d, size, +					DMA_FROM_DEVICE); +			ep->rxbuf_d = DMA_ADDR_INVALID; +		} else { +			dma_sync_single_for_cpu( +					udc_controller->gadget.dev.parent, +					ep->rxbuf_d, size, +					DMA_FROM_DEVICE); +		} +		kfree(ep->rxbuffer); +	} + +	if (ep->dir != USB_DIR_OUT) +		kfree(ep->txframe); + +	dev_dbg(udc->dev, "disabled %s OK\n", _ep->name); +	return 0; +} + +static struct usb_request *qe_alloc_request(struct usb_ep *_ep,	gfp_t gfp_flags) +{ +	struct qe_req *req; + +	req = kzalloc(sizeof(*req), gfp_flags); +	if (!req) +		return NULL; + +	req->req.dma = DMA_ADDR_INVALID; + +	INIT_LIST_HEAD(&req->queue); + +	return &req->req; +} + +static void qe_free_request(struct usb_ep *_ep, struct usb_request *_req) +{ +	struct qe_req *req; + +	req = container_of(_req, struct qe_req, req); + +	if (_req) +		kfree(req); +} + +/* queues (submits) an I/O request to an endpoint */ +static int qe_ep_queue(struct usb_ep *_ep, struct usb_request *_req, +				gfp_t gfp_flags) +{ +	struct qe_ep *ep = container_of(_ep, struct qe_ep, ep); +	struct qe_req *req = container_of(_req, struct qe_req, req); +	struct qe_udc *udc; +	unsigned long flags; +	int reval; + +	udc = ep->udc; +	/* catch various bogus parameters */ +	if (!_req || !req->req.complete || !req->req.buf +			|| !list_empty(&req->queue)) { +		dev_dbg(udc->dev, "bad params\n"); +		return -EINVAL; +	} +	if (!_ep || (!ep->desc && ep_index(ep))) { +		dev_dbg(udc->dev, "bad ep\n"); +		return -EINVAL; +	} + +	if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN) +		return -ESHUTDOWN; + +	req->ep = ep; + +	/* map virtual address to hardware */ +	if (req->req.dma == DMA_ADDR_INVALID) { +		req->req.dma = dma_map_single(ep->udc->gadget.dev.parent, +					req->req.buf, +					req->req.length, +					ep_is_in(ep) +					? DMA_TO_DEVICE : +					DMA_FROM_DEVICE); +		req->mapped = 1; +	} else { +		dma_sync_single_for_device(ep->udc->gadget.dev.parent, +					req->req.dma, req->req.length, +					ep_is_in(ep) +					? DMA_TO_DEVICE : +					DMA_FROM_DEVICE); +		req->mapped = 0; +	} + +	req->req.status = -EINPROGRESS; +	req->req.actual = 0; + +	list_add_tail(&req->queue, &ep->queue); +	dev_vdbg(udc->dev, "gadget have request in %s! %d\n", +			ep->name, req->req.length); +	spin_lock_irqsave(&udc->lock, flags); +	/* push the request to device */ +	if (ep_is_in(ep)) +		reval = ep_req_send(ep, req); + +	/* EP0 */ +	if (ep_index(ep) == 0 && req->req.length > 0) { +		if (ep_is_in(ep)) +			udc->ep0_state = DATA_STATE_XMIT; +		else +			udc->ep0_state = DATA_STATE_RECV; +	} + +	if (ep->dir == USB_DIR_OUT) +		reval = ep_req_receive(ep, req); + +	spin_unlock_irqrestore(&udc->lock, flags); + +	return 0; +} + +/* dequeues (cancels, unlinks) an I/O request from an endpoint */ +static int qe_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req) +{ +	struct qe_ep *ep = container_of(_ep, struct qe_ep, ep); +	struct qe_req *req; +	unsigned long flags; + +	if (!_ep || !_req) +		return -EINVAL; + +	spin_lock_irqsave(&ep->udc->lock, flags); + +	/* make sure it's actually queued on this endpoint */ +	list_for_each_entry(req, &ep->queue, queue) { +		if (&req->req == _req) +			break; +	} + +	if (&req->req != _req) { +		spin_unlock_irqrestore(&ep->udc->lock, flags); +		return -EINVAL; +	} + +	done(ep, req, -ECONNRESET); + +	spin_unlock_irqrestore(&ep->udc->lock, flags); +	return 0; +} + +/*----------------------------------------------------------------- + * modify the endpoint halt feature + * @ep: the non-isochronous endpoint being stalled + * @value: 1--set halt  0--clear halt + * Returns zero, or a negative error code. +*----------------------------------------------------------------*/ +static int qe_ep_set_halt(struct usb_ep *_ep, int value) +{ +	struct qe_ep *ep; +	unsigned long flags; +	int status = -EOPNOTSUPP; +	struct qe_udc *udc; + +	ep = container_of(_ep, struct qe_ep, ep); +	if (!_ep || !ep->desc) { +		status = -EINVAL; +		goto out; +	} + +	udc = ep->udc; +	/* Attempt to halt IN ep will fail if any transfer requests +	 * are still queue */ +	if (value && ep_is_in(ep) && !list_empty(&ep->queue)) { +		status = -EAGAIN; +		goto out; +	} + +	status = 0; +	spin_lock_irqsave(&ep->udc->lock, flags); +	qe_eptx_stall_change(ep, value); +	qe_eprx_stall_change(ep, value); +	spin_unlock_irqrestore(&ep->udc->lock, flags); + +	if (ep->epnum == 0) { +		udc->ep0_state = WAIT_FOR_SETUP; +		udc->ep0_dir = 0; +	} + +	/* set data toggle to DATA0 on clear halt */ +	if (value == 0) +		ep->data01 = 0; +out: +	dev_vdbg(udc->dev, "%s %s halt stat %d\n", ep->ep.name, +			value ?  "set" : "clear", status); + +	return status; +} + +static struct usb_ep_ops qe_ep_ops = { +	.enable = qe_ep_enable, +	.disable = qe_ep_disable, + +	.alloc_request = qe_alloc_request, +	.free_request = qe_free_request, + +	.queue = qe_ep_queue, +	.dequeue = qe_ep_dequeue, + +	.set_halt = qe_ep_set_halt, +}; + +/*------------------------------------------------------------------------ +	Gadget Driver Layer Operations + ------------------------------------------------------------------------*/ + +/* Get the current frame number */ +static int qe_get_frame(struct usb_gadget *gadget) +{ +	u16 tmp; + +	tmp = in_be16(&udc_controller->usb_param->frame_n); +	if (tmp & 0x8000) +		tmp = tmp & 0x07ff; +	else +		tmp = -EINVAL; + +	return (int)tmp; +} + +/* Tries to wake up the host connected to this gadget + * + * Return : 0-success + * Negative-this feature not enabled by host or not supported by device hw + */ +static int qe_wakeup(struct usb_gadget *gadget) +{ +	return -ENOTSUPP; +} + +/* Notify controller that VBUS is powered, Called by whatever +   detects VBUS sessions */ +static int qe_vbus_session(struct usb_gadget *gadget, int is_active) +{ +	return -ENOTSUPP; +} + +/* constrain controller's VBUS power usage + * This call is used by gadget drivers during SET_CONFIGURATION calls, + * reporting how much power the device may consume.  For example, this + * could affect how quickly batteries are recharged. + * + * Returns zero on success, else negative errno. + */ +static int qe_vbus_draw(struct usb_gadget *gadget, unsigned mA) +{ +	return -ENOTSUPP; +} + +/* Change Data+ pullup status + * this func is used by usb_gadget_connect/disconnect + */ +static int qe_pullup(struct usb_gadget *gadget, int is_on) +{ +	return -ENOTSUPP; +} + +/* defined in usb_gadget.h */ +static struct usb_gadget_ops qe_gadget_ops = { +	.get_frame = qe_get_frame, +	.wakeup = qe_wakeup, +/*	.set_selfpowered = qe_set_selfpowered,*/ /* always selfpowered */ +	.vbus_session = qe_vbus_session, +	.vbus_draw = qe_vbus_draw, +	.pullup = qe_pullup, +}; + +/*------------------------------------------------------------------------- +	USB ep0 Setup process in BUS Enumeration + -------------------------------------------------------------------------*/ +static int udc_reset_ep_queue(struct qe_udc *udc, u8 pipe) +{ +	struct qe_ep *ep = &udc->eps[pipe]; + +	nuke(ep, -ECONNRESET); +	ep->tx_req = NULL; +	return 0; +} + +static int reset_queues(struct qe_udc *udc) +{ +	u8 pipe; + +	for (pipe = 0; pipe < USB_MAX_ENDPOINTS; pipe++) +		udc_reset_ep_queue(udc, pipe); + +	/* report disconnect; the driver is already quiesced */ +	spin_unlock(&udc->lock); +	udc->driver->disconnect(&udc->gadget); +	spin_lock(&udc->lock); + +	return 0; +} + +static void ch9setaddress(struct qe_udc *udc, u16 value, u16 index, +			u16 length) +{ +	/* Save the new address to device struct */ +	udc->device_address = (u8) value; +	/* Update usb state */ +	udc->usb_state = USB_STATE_ADDRESS; + +	/* Status phase , send a ZLP */ +	if (ep0_prime_status(udc, USB_DIR_IN)) +		qe_ep0_stall(udc); +} + +static void ownercomplete(struct usb_ep *_ep, struct usb_request *_req) +{ +	struct qe_req *req = container_of(_req, struct qe_req, req); + +	req->req.buf = NULL; +	kfree(req); +} + +static void ch9getstatus(struct qe_udc *udc, u8 request_type, u16 value, +			u16 index, u16 length) +{ +	u16 usb_status = 0; +	struct qe_req *req; +	struct qe_ep *ep; +	int status = 0; + +	ep = &udc->eps[0]; +	if ((request_type & USB_RECIP_MASK) == USB_RECIP_DEVICE) { +		/* Get device status */ +		usb_status = 1 << USB_DEVICE_SELF_POWERED; +	} else if ((request_type & USB_RECIP_MASK) == USB_RECIP_INTERFACE) { +		/* Get interface status */ +		/* We don't have interface information in udc driver */ +		usb_status = 0; +	} else if ((request_type & USB_RECIP_MASK) == USB_RECIP_ENDPOINT) { +		/* Get endpoint status */ +		int pipe = index & USB_ENDPOINT_NUMBER_MASK; +		struct qe_ep *target_ep = &udc->eps[pipe]; +		u16 usep; + +		/* stall if endpoint doesn't exist */ +		if (!target_ep->desc) +			goto stall; + +		usep = in_be16(&udc->usb_regs->usb_usep[pipe]); +		if (index & USB_DIR_IN) { +			if (target_ep->dir != USB_DIR_IN) +				goto stall; +			if ((usep & USB_THS_MASK) == USB_THS_STALL) +				usb_status = 1 << USB_ENDPOINT_HALT; +		} else { +			if (target_ep->dir != USB_DIR_OUT) +				goto stall; +			if ((usep & USB_RHS_MASK) == USB_RHS_STALL) +				usb_status = 1 << USB_ENDPOINT_HALT; +		} +	} + +	req = container_of(qe_alloc_request(&ep->ep, GFP_KERNEL), +					struct qe_req, req); +	req->req.length = 2; +	req->req.buf = udc->statusbuf; +	*(u16 *)req->req.buf = cpu_to_le16(usb_status); +	req->req.status = -EINPROGRESS; +	req->req.actual = 0; +	req->req.complete = ownercomplete; + +	udc->ep0_dir = USB_DIR_IN; + +	/* data phase */ +	status = qe_ep_queue(&ep->ep, &req->req, GFP_ATOMIC); + +	if (status == 0) +		return; +stall: +	dev_err(udc->dev, "Can't respond to getstatus request \n"); +	qe_ep0_stall(udc); +} + +/* only handle the setup request, suppose the device in normal status */ +static void setup_received_handle(struct qe_udc *udc, +				struct usb_ctrlrequest *setup) +{ +	/* Fix Endian (udc->local_setup_buff is cpu Endian now)*/ +	u16 wValue = le16_to_cpu(setup->wValue); +	u16 wIndex = le16_to_cpu(setup->wIndex); +	u16 wLength = le16_to_cpu(setup->wLength); + +	/* clear the previous request in the ep0 */ +	udc_reset_ep_queue(udc, 0); + +	if (setup->bRequestType & USB_DIR_IN) +		udc->ep0_dir = USB_DIR_IN; +	else +		udc->ep0_dir = USB_DIR_OUT; + +	switch (setup->bRequest) { +	case USB_REQ_GET_STATUS: +		/* Data+Status phase form udc */ +		if ((setup->bRequestType & (USB_DIR_IN | USB_TYPE_MASK)) +					!= (USB_DIR_IN | USB_TYPE_STANDARD)) +			break; +		ch9getstatus(udc, setup->bRequestType, wValue, wIndex, +					wLength); +		return; + +	case USB_REQ_SET_ADDRESS: +		/* Status phase from udc */ +		if (setup->bRequestType != (USB_DIR_OUT | USB_TYPE_STANDARD | +						USB_RECIP_DEVICE)) +			break; +		ch9setaddress(udc, wValue, wIndex, wLength); +		return; + +	case USB_REQ_CLEAR_FEATURE: +	case USB_REQ_SET_FEATURE: +		/* Requests with no data phase, status phase from udc */ +		if ((setup->bRequestType & USB_TYPE_MASK) +					!= USB_TYPE_STANDARD) +			break; + +		if ((setup->bRequestType & USB_RECIP_MASK) +				== USB_RECIP_ENDPOINT) { +			int pipe = wIndex & USB_ENDPOINT_NUMBER_MASK; +			struct qe_ep *ep; + +			if (wValue != 0 || wLength != 0 +				|| pipe > USB_MAX_ENDPOINTS) +				break; +			ep = &udc->eps[pipe]; + +			spin_unlock(&udc->lock); +			qe_ep_set_halt(&ep->ep, +					(setup->bRequest == USB_REQ_SET_FEATURE) +						? 1 : 0); +			spin_lock(&udc->lock); +		} + +		ep0_prime_status(udc, USB_DIR_IN); + +		return; + +	default: +		break; +	} + +	if (wLength) { +		/* Data phase from gadget, status phase from udc */ +		if (setup->bRequestType & USB_DIR_IN) { +			udc->ep0_state = DATA_STATE_XMIT; +			udc->ep0_dir = USB_DIR_IN; +		} else { +			udc->ep0_state = DATA_STATE_RECV; +			udc->ep0_dir = USB_DIR_OUT; +		} +		spin_unlock(&udc->lock); +		if (udc->driver->setup(&udc->gadget, +					&udc->local_setup_buff) < 0) +			qe_ep0_stall(udc); +		spin_lock(&udc->lock); +	} else { +		/* No data phase, IN status from gadget */ +		udc->ep0_dir = USB_DIR_IN; +		spin_unlock(&udc->lock); +		if (udc->driver->setup(&udc->gadget, +					&udc->local_setup_buff) < 0) +			qe_ep0_stall(udc); +		spin_lock(&udc->lock); +		udc->ep0_state = DATA_STATE_NEED_ZLP; +	} +} + +/*------------------------------------------------------------------------- +	USB Interrupt handlers + -------------------------------------------------------------------------*/ +static void suspend_irq(struct qe_udc *udc) +{ +	udc->resume_state = udc->usb_state; +	udc->usb_state = USB_STATE_SUSPENDED; + +	/* report suspend to the driver ,serial.c not support this*/ +	if (udc->driver->suspend) +		udc->driver->suspend(&udc->gadget); +} + +static void resume_irq(struct qe_udc *udc) +{ +	udc->usb_state = udc->resume_state; +	udc->resume_state = 0; + +	/* report resume to the driver , serial.c not support this*/ +	if (udc->driver->resume) +		udc->driver->resume(&udc->gadget); +} + +static void idle_irq(struct qe_udc *udc) +{ +	u8 usbs; + +	usbs = in_8(&udc->usb_regs->usb_usbs); +	if (usbs & USB_IDLE_STATUS_MASK) { +		if ((udc->usb_state) != USB_STATE_SUSPENDED) +			suspend_irq(udc); +	} else { +		if (udc->usb_state == USB_STATE_SUSPENDED) +			resume_irq(udc); +	} +} + +static int reset_irq(struct qe_udc *udc) +{ +	unsigned char i; + +	qe_usb_disable(); +	out_8(&udc->usb_regs->usb_usadr, 0); + +	for (i = 0; i < USB_MAX_ENDPOINTS; i++) { +		if (udc->eps[i].init) +			qe_ep_reset(udc, i); +	} + +	reset_queues(udc); +	udc->usb_state = USB_STATE_DEFAULT; +	udc->ep0_state = WAIT_FOR_SETUP; +	udc->ep0_dir = USB_DIR_OUT; +	qe_usb_enable(); +	return 0; +} + +static int bsy_irq(struct qe_udc *udc) +{ +	return 0; +} + +static int txe_irq(struct qe_udc *udc) +{ +	return 0; +} + +/* ep0 tx interrupt also in here */ +static int tx_irq(struct qe_udc *udc) +{ +	struct qe_ep *ep; +	struct qe_bd __iomem *bd; +	int i, res = 0; + +	if ((udc->usb_state == USB_STATE_ADDRESS) +		&& (in_8(&udc->usb_regs->usb_usadr) == 0)) +		out_8(&udc->usb_regs->usb_usadr, udc->device_address); + +	for (i = (USB_MAX_ENDPOINTS-1); ((i >= 0) && (res == 0)); i--) { +		ep = &udc->eps[i]; +		if (ep && ep->init && (ep->dir != USB_DIR_OUT)) { +			bd = ep->c_txbd; +			if (!(in_be32((u32 __iomem *)bd) & T_R) +						&& (in_be32(&bd->buf))) { +				/* confirm the transmitted bd */ +				if (ep->epnum == 0) +					res = qe_ep0_txconf(ep); +				else +					res = qe_ep_txconf(ep); +			} +		} +	} +	return res; +} + + +/* setup packect's rx is handle in the function too */ +static void rx_irq(struct qe_udc *udc) +{ +	struct qe_ep *ep; +	struct qe_bd __iomem *bd; +	int i; + +	for (i = 0; i < USB_MAX_ENDPOINTS; i++) { +		ep = &udc->eps[i]; +		if (ep && ep->init && (ep->dir != USB_DIR_IN)) { +			bd = ep->n_rxbd; +			if (!(in_be32((u32 __iomem *)bd) & R_E) +						&& (in_be32(&bd->buf))) { +				if (ep->epnum == 0) { +					qe_ep0_rx(udc); +				} else { +					/*non-setup package receive*/ +					qe_ep_rx(ep); +				} +			} +		} +	} +} + +static irqreturn_t qe_udc_irq(int irq, void *_udc) +{ +	struct qe_udc *udc = (struct qe_udc *)_udc; +	u16 irq_src; +	irqreturn_t status = IRQ_NONE; +	unsigned long flags; + +	spin_lock_irqsave(&udc->lock, flags); + +	irq_src = in_be16(&udc->usb_regs->usb_usber) & +		in_be16(&udc->usb_regs->usb_usbmr); +	/* Clear notification bits */ +	out_be16(&udc->usb_regs->usb_usber, irq_src); +	/* USB Interrupt */ +	if (irq_src & USB_E_IDLE_MASK) { +		idle_irq(udc); +		irq_src &= ~USB_E_IDLE_MASK; +		status = IRQ_HANDLED; +	} + +	if (irq_src & USB_E_TXB_MASK) { +		tx_irq(udc); +		irq_src &= ~USB_E_TXB_MASK; +		status = IRQ_HANDLED; +	} + +	if (irq_src & USB_E_RXB_MASK) { +		rx_irq(udc); +		irq_src &= ~USB_E_RXB_MASK; +		status = IRQ_HANDLED; +	} + +	if (irq_src & USB_E_RESET_MASK) { +		reset_irq(udc); +		irq_src &= ~USB_E_RESET_MASK; +		status = IRQ_HANDLED; +	} + +	if (irq_src & USB_E_BSY_MASK) { +		bsy_irq(udc); +		irq_src &= ~USB_E_BSY_MASK; +		status = IRQ_HANDLED; +	} + +	if (irq_src & USB_E_TXE_MASK) { +		txe_irq(udc); +		irq_src &= ~USB_E_TXE_MASK; +		status = IRQ_HANDLED; +	} + +	spin_unlock_irqrestore(&udc->lock, flags); + +	return status; +} + +/*------------------------------------------------------------------------- +	Gadget driver register and unregister. + --------------------------------------------------------------------------*/ +int usb_gadget_register_driver(struct usb_gadget_driver *driver) +{ +	int retval; +	unsigned long flags = 0; + +	/* standard operations */ +	if (!udc_controller) +		return -ENODEV; + +	if (!driver || (driver->speed != USB_SPEED_FULL +			&& driver->speed != USB_SPEED_HIGH) +			|| !driver->bind || !driver->disconnect +			|| !driver->setup) +		return -EINVAL; + +	if (udc_controller->driver) +		return -EBUSY; + +	/* lock is needed but whether should use this lock or another */ +	spin_lock_irqsave(&udc_controller->lock, flags); + +	driver->driver.bus = NULL; +	/* hook up the driver */ +	udc_controller->driver = driver; +	udc_controller->gadget.dev.driver = &driver->driver; +	udc_controller->gadget.speed = (enum usb_device_speed)(driver->speed); +	spin_unlock_irqrestore(&udc_controller->lock, flags); + +	retval = driver->bind(&udc_controller->gadget); +	if (retval) { +		dev_err(udc_controller->dev, "bind to %s --> %d", +				driver->driver.name, retval); +		udc_controller->gadget.dev.driver = NULL; +		udc_controller->driver = NULL; +		return retval; +	} + +	/* Enable IRQ reg and Set usbcmd reg EN bit */ +	qe_usb_enable(); + +	out_be16(&udc_controller->usb_regs->usb_usber, 0xffff); +	out_be16(&udc_controller->usb_regs->usb_usbmr, USB_E_DEFAULT_DEVICE); +	udc_controller->usb_state = USB_STATE_ATTACHED; +	udc_controller->ep0_state = WAIT_FOR_SETUP; +	udc_controller->ep0_dir = USB_DIR_OUT; +	dev_info(udc_controller->dev, "%s bind to driver %s \n", +		udc_controller->gadget.name, driver->driver.name); +	return 0; +} +EXPORT_SYMBOL(usb_gadget_register_driver); + +int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) +{ +	struct qe_ep *loop_ep; +	unsigned long flags; + +	if (!udc_controller) +		return -ENODEV; + +	if (!driver || driver != udc_controller->driver) +		return -EINVAL; + +	/* stop usb controller, disable intr */ +	qe_usb_disable(); + +	/* in fact, no needed */ +	udc_controller->usb_state = USB_STATE_ATTACHED; +	udc_controller->ep0_state = WAIT_FOR_SETUP; +	udc_controller->ep0_dir = 0; + +	/* stand operation */ +	spin_lock_irqsave(&udc_controller->lock, flags); +	udc_controller->gadget.speed = USB_SPEED_UNKNOWN; +	nuke(&udc_controller->eps[0], -ESHUTDOWN); +	list_for_each_entry(loop_ep, &udc_controller->gadget.ep_list, +				ep.ep_list) +		nuke(loop_ep, -ESHUTDOWN); +	spin_unlock_irqrestore(&udc_controller->lock, flags); + +	/* unbind gadget and unhook driver. */ +	driver->unbind(&udc_controller->gadget); +	udc_controller->gadget.dev.driver = NULL; +	udc_controller->driver = NULL; + +	dev_info(udc_controller->dev, "unregistered gadget driver '%s'\r\n", +			driver->driver.name); +	return 0; +} +EXPORT_SYMBOL(usb_gadget_unregister_driver); + +/* udc structure's alloc and setup, include ep-param alloc */ +static struct qe_udc __devinit *qe_udc_config(struct of_device *ofdev) +{ +	struct qe_udc *udc; +	struct device_node *np = ofdev->node; +	unsigned int tmp_addr = 0; +	struct usb_device_para __iomem *usbpram; +	unsigned int i; +	u64 size; +	u32 offset; + +	udc = kzalloc(sizeof(*udc), GFP_KERNEL); +	if (udc == NULL) { +		dev_err(&ofdev->dev, "malloc udc failed\n"); +		goto cleanup; +	} + +	udc->dev = &ofdev->dev; + +	/* get default address of usb parameter in MURAM from device tree */ +	offset = *of_get_address(np, 1, &size, NULL); +	udc->usb_param = cpm_muram_addr(offset); +	memset_io(udc->usb_param, 0, size); + +	usbpram = udc->usb_param; +	out_be16(&usbpram->frame_n, 0); +	out_be32(&usbpram->rstate, 0); + +	tmp_addr = cpm_muram_alloc((USB_MAX_ENDPOINTS * +					sizeof(struct usb_ep_para)), +					   USB_EP_PARA_ALIGNMENT); + +	for (i = 0; i < USB_MAX_ENDPOINTS; i++) { +		out_be16(&usbpram->epptr[i], (u16)tmp_addr); +		udc->ep_param[i] = cpm_muram_addr(tmp_addr); +		tmp_addr += 32; +	} + +	memset_io(udc->ep_param[0], 0, +			USB_MAX_ENDPOINTS * sizeof(struct usb_ep_para)); + +	udc->resume_state = USB_STATE_NOTATTACHED; +	udc->usb_state = USB_STATE_POWERED; +	udc->ep0_dir = 0; + +	spin_lock_init(&udc->lock); +	return udc; + +cleanup: +	kfree(udc); +	return NULL; +} + +/* USB Controller register init */ +static int __devinit qe_udc_reg_init(struct qe_udc *udc) +{ +	struct usb_ctlr __iomem *qe_usbregs; +	qe_usbregs = udc->usb_regs; + +	/* Init the usb register */ +	out_8(&qe_usbregs->usb_usmod, 0x01); +	out_be16(&qe_usbregs->usb_usbmr, 0); +	out_8(&qe_usbregs->usb_uscom, 0); +	out_be16(&qe_usbregs->usb_usber, USBER_ALL_CLEAR); + +	return 0; +} + +static int __devinit qe_ep_config(struct qe_udc *udc, unsigned char pipe_num) +{ +	struct qe_ep *ep = &udc->eps[pipe_num]; + +	ep->udc = udc; +	strcpy(ep->name, ep_name[pipe_num]); +	ep->ep.name = ep_name[pipe_num]; + +	ep->ep.ops = &qe_ep_ops; +	ep->stopped = 1; +	ep->ep.maxpacket = (unsigned short) ~0; +	ep->desc = NULL; +	ep->dir = 0xff; +	ep->epnum = (u8)pipe_num; +	ep->sent = 0; +	ep->last = 0; +	ep->init = 0; +	ep->rxframe = NULL; +	ep->txframe = NULL; +	ep->tx_req = NULL; +	ep->state = EP_STATE_IDLE; +	ep->has_data = 0; + +	/* the queue lists any req for this ep */ +	INIT_LIST_HEAD(&ep->queue); + +	/* gagdet.ep_list used for ep_autoconfig so no ep0*/ +	if (pipe_num != 0) +		list_add_tail(&ep->ep.ep_list, &udc->gadget.ep_list); + +	ep->gadget = &udc->gadget; + +	return 0; +} + +/*----------------------------------------------------------------------- + *	UDC device Driver operation functions				* + *----------------------------------------------------------------------*/ +static void qe_udc_release(struct device *dev) +{ +	int i = 0; + +	complete(udc_controller->done); +	cpm_muram_free(cpm_muram_offset(udc_controller->ep_param[0])); +	for (i = 0; i < USB_MAX_ENDPOINTS; i++) +		udc_controller->ep_param[i] = NULL; + +	kfree(udc_controller); +	udc_controller = NULL; +} + +/* Driver probe functions */ +static int __devinit qe_udc_probe(struct of_device *ofdev, +			const struct of_device_id *match) +{ +	struct device_node *np = ofdev->node; +	struct qe_ep *ep; +	unsigned int ret = 0; +	unsigned int i; +	const void *prop; + +	prop = of_get_property(np, "mode", NULL); +	if (!prop || strcmp(prop, "peripheral")) +		return -ENODEV; + +	/* Initialize the udc structure including QH member and other member */ +	udc_controller = qe_udc_config(ofdev); +	if (!udc_controller) { +		dev_dbg(&ofdev->dev, "udc_controll is NULL\n"); +		return -ENOMEM; +	} + +	udc_controller->soc_type = (unsigned long)match->data; +	udc_controller->usb_regs = of_iomap(np, 0); +	if (!udc_controller->usb_regs) { +		ret = -ENOMEM; +		goto err1; +	} + +	/* initialize usb hw reg except for regs for EP, +	 * leave usbintr reg untouched*/ +	qe_udc_reg_init(udc_controller); + +	/* here comes the stand operations for probe +	 * set the qe_udc->gadget.xxx */ +	udc_controller->gadget.ops = &qe_gadget_ops; + +	/* gadget.ep0 is a pointer */ +	udc_controller->gadget.ep0 = &udc_controller->eps[0].ep; + +	INIT_LIST_HEAD(&udc_controller->gadget.ep_list); + +	/* modify in register gadget process */ +	udc_controller->gadget.speed = USB_SPEED_UNKNOWN; + +	/* name: Identifies the controller hardware type. */ +	udc_controller->gadget.name = driver_name; + +	device_initialize(&udc_controller->gadget.dev); + +	strcpy(udc_controller->gadget.dev.bus_id, "gadget"); + +	udc_controller->gadget.dev.release = qe_udc_release; +	udc_controller->gadget.dev.parent = &ofdev->dev; + +	/* initialize qe_ep struct */ +	for (i = 0; i < USB_MAX_ENDPOINTS ; i++) { +		/* because the ep type isn't decide here so +		 * qe_ep_init() should be called in ep_enable() */ + +		/* setup the qe_ep struct and link ep.ep.list +		 * into gadget.ep_list */ +		qe_ep_config(udc_controller, (unsigned char)i); +	} + +	/* ep0 initialization in here */ +	ret = qe_ep_init(udc_controller, 0, &qe_ep0_desc); +	if (ret) +		goto err2; + +	/* create a buf for ZLP send, need to remain zeroed */ +	udc_controller->nullbuf = kzalloc(256, GFP_KERNEL); +	if (udc_controller->nullbuf == NULL) { +		dev_dbg(udc_controller->dev, "cannot alloc nullbuf\n"); +		ret = -ENOMEM; +		goto err3; +	} + +	/* buffer for data of get_status request */ +	udc_controller->statusbuf = kzalloc(2, GFP_KERNEL); +	if (udc_controller->statusbuf == NULL) { +		ret = -ENOMEM; +		goto err4; +	} + +	udc_controller->nullp = virt_to_phys((void *)udc_controller->nullbuf); +	if (udc_controller->nullp == DMA_ADDR_INVALID) { +		udc_controller->nullp = dma_map_single( +					udc_controller->gadget.dev.parent, +					udc_controller->nullbuf, +					256, +					DMA_TO_DEVICE); +		udc_controller->nullmap = 1; +	} else { +		dma_sync_single_for_device(udc_controller->gadget.dev.parent, +					udc_controller->nullp, 256, +					DMA_TO_DEVICE); +	} + +	tasklet_init(&udc_controller->rx_tasklet, ep_rx_tasklet, +			(unsigned long)udc_controller); +	/* request irq and disable DR  */ +	udc_controller->usb_irq = irq_of_parse_and_map(np, 0); + +	ret = request_irq(udc_controller->usb_irq, qe_udc_irq, 0, +				driver_name, udc_controller); +	if (ret) { +		dev_err(udc_controller->dev, "cannot request irq %d err %d \n", +			udc_controller->usb_irq, ret); +		goto err5; +	} + +	ret = device_add(&udc_controller->gadget.dev); +	if (ret) +		goto err6; + +	dev_info(udc_controller->dev, +			"%s USB controller initialized as device\n", +			(udc_controller->soc_type == PORT_QE) ? "QE" : "CPM"); +	return 0; + +err6: +	free_irq(udc_controller->usb_irq, udc_controller); +err5: +	if (udc_controller->nullmap) { +		dma_unmap_single(udc_controller->gadget.dev.parent, +			udc_controller->nullp, 256, +				DMA_TO_DEVICE); +			udc_controller->nullp = DMA_ADDR_INVALID; +	} else { +		dma_sync_single_for_cpu(udc_controller->gadget.dev.parent, +			udc_controller->nullp, 256, +				DMA_TO_DEVICE); +	} +	kfree(udc_controller->statusbuf); +err4: +	kfree(udc_controller->nullbuf); +err3: +	ep = &udc_controller->eps[0]; +	cpm_muram_free(cpm_muram_offset(ep->rxbase)); +	kfree(ep->rxframe); +	kfree(ep->rxbuffer); +	kfree(ep->txframe); +err2: +	iounmap(udc_controller->usb_regs); +err1: +	kfree(udc_controller); + +	return ret; +} + +#ifdef CONFIG_PM +static int qe_udc_suspend(struct of_device *dev, pm_message_t state) +{ +	return -ENOTSUPP; +} + +static int qe_udc_resume(struct of_device *dev) +{ +	return -ENOTSUPP; +} +#endif + +static int __devexit qe_udc_remove(struct of_device *ofdev) +{ +	struct qe_ep *ep; +	unsigned int size; + +	DECLARE_COMPLETION(done); + +	if (!udc_controller) +		return -ENODEV; + +	udc_controller->done = &done; +	tasklet_disable(&udc_controller->rx_tasklet); + +	if (udc_controller->nullmap) { +		dma_unmap_single(udc_controller->gadget.dev.parent, +			udc_controller->nullp, 256, +				DMA_TO_DEVICE); +			udc_controller->nullp = DMA_ADDR_INVALID; +	} else { +		dma_sync_single_for_cpu(udc_controller->gadget.dev.parent, +			udc_controller->nullp, 256, +				DMA_TO_DEVICE); +	} +	kfree(udc_controller->statusbuf); +	kfree(udc_controller->nullbuf); + +	ep = &udc_controller->eps[0]; +	cpm_muram_free(cpm_muram_offset(ep->rxbase)); +	size = (ep->ep.maxpacket + USB_CRC_SIZE + 2) * (USB_BDRING_LEN + 1); + +	kfree(ep->rxframe); +	if (ep->rxbufmap) { +		dma_unmap_single(udc_controller->gadget.dev.parent, +				ep->rxbuf_d, size, +				DMA_FROM_DEVICE); +		ep->rxbuf_d = DMA_ADDR_INVALID; +	} else { +		dma_sync_single_for_cpu(udc_controller->gadget.dev.parent, +				ep->rxbuf_d, size, +				DMA_FROM_DEVICE); +	} + +	kfree(ep->rxbuffer); +	kfree(ep->txframe); + +	free_irq(udc_controller->usb_irq, udc_controller); + +	tasklet_kill(&udc_controller->rx_tasklet); + +	iounmap(udc_controller->usb_regs); + +	device_unregister(&udc_controller->gadget.dev); +	/* wait for release() of gadget.dev to free udc */ +	wait_for_completion(&done); + +	return 0; +} + +/*-------------------------------------------------------------------------*/ +static struct of_device_id __devinitdata qe_udc_match[] = { +	{ +		.compatible = "fsl,mpc8360-qe-usb", +		.data = (void *)PORT_QE, +	}, +	{ +		.compatible = "fsl,mpc8272-cpm-usb", +		.data = (void *)PORT_CPM, +	}, +	{}, +}; + +MODULE_DEVICE_TABLE(of, qe_udc_match); + +static struct of_platform_driver udc_driver = { +	.name           = (char *)driver_name, +	.match_table    = qe_udc_match, +	.probe          = qe_udc_probe, +	.remove         = __devexit_p(qe_udc_remove), +#ifdef CONFIG_PM +	.suspend        = qe_udc_suspend, +	.resume         = qe_udc_resume, +#endif +}; + +static int __init qe_udc_init(void) +{ +	printk(KERN_INFO "%s: %s, %s\n", driver_name, driver_desc, +			DRIVER_VERSION); +	return of_register_platform_driver(&udc_driver); +} + +static void __exit qe_udc_exit(void) +{ +	of_unregister_platform_driver(&udc_driver); +} + +module_init(qe_udc_init); +module_exit(qe_udc_exit); + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_LICENSE("GPL"); + diff --git a/drivers/usb/gadget/fsl_qe_udc.h b/drivers/usb/gadget/fsl_qe_udc.h new file mode 100644 index 00000000000..31b2710882e --- /dev/null +++ b/drivers/usb/gadget/fsl_qe_udc.h @@ -0,0 +1,437 @@ +/* + * drivers/usb/gadget/qe_udc.h + * + * Copyright (C) 2006-2008 Freescale Semiconductor, Inc. All rights reserved. + * + * 	Xiaobo Xie <X.Xie@freescale.com> + * 	Li Yang <leoli@freescale.com> + * + * Description: + * Freescale USB device/endpoint management registers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + */ + +#ifndef __FSL_QE_UDC_H +#define __FSL_QE_UDC_H + +/* SoC type */ +#define PORT_CPM	0 +#define PORT_QE		1 + +#define USB_MAX_ENDPOINTS               4 +#define USB_MAX_PIPES                   USB_MAX_ENDPOINTS +#define USB_EP0_MAX_SIZE		64 +#define USB_MAX_CTRL_PAYLOAD            0x4000 +#define USB_BDRING_LEN			16 +#define USB_BDRING_LEN_RX		256 +#define USB_BDRING_LEN_TX		16 +#define MIN_EMPTY_BDS			128 +#define MAX_DATA_BDS			8 +#define USB_CRC_SIZE			2 +#define USB_DIR_BOTH			0x88 +#define R_BUF_MAXSIZE			0x800 +#define USB_EP_PARA_ALIGNMENT		32 + +/* USB Mode Register bit define */ +#define USB_MODE_EN		0x01 +#define USB_MODE_HOST		0x02 +#define USB_MODE_TEST		0x04 +#define USB_MODE_SFTE		0x08 +#define USB_MODE_RESUME		0x40 +#define USB_MODE_LSS		0x80 + +/* USB Slave Address Register Mask */ +#define USB_SLVADDR_MASK	0x7F + +/* USB Endpoint register define */ +#define USB_EPNUM_MASK		0xF000 +#define USB_EPNUM_SHIFT		12 + +#define USB_TRANS_MODE_SHIFT	8 +#define USB_TRANS_CTR		0x0000 +#define USB_TRANS_INT		0x0100 +#define USB_TRANS_BULK		0x0200 +#define USB_TRANS_ISO		0x0300 + +#define USB_EP_MF		0x0020 +#define USB_EP_RTE		0x0010 + +#define USB_THS_SHIFT		2 +#define USB_THS_MASK		0x000c +#define USB_THS_NORMAL		0x0 +#define USB_THS_IGNORE_IN	0x0004 +#define USB_THS_NACK		0x0008 +#define USB_THS_STALL		0x000c + +#define USB_RHS_SHIFT   	0 +#define USB_RHS_MASK		0x0003 +#define USB_RHS_NORMAL  	0x0 +#define USB_RHS_IGNORE_OUT	0x0001 +#define USB_RHS_NACK		0x0002 +#define USB_RHS_STALL		0x0003 + +#define USB_RTHS_MASK		0x000f + +/* USB Command Register define */ +#define USB_CMD_STR_FIFO	0x80 +#define USB_CMD_FLUSH_FIFO	0x40 +#define USB_CMD_ISFT		0x20 +#define USB_CMD_DSFT		0x10 +#define USB_CMD_EP_MASK		0x03 + +/* USB Event and Mask Register define */ +#define USB_E_MSF_MASK		0x0800 +#define USB_E_SFT_MASK		0x0400 +#define USB_E_RESET_MASK	0x0200 +#define USB_E_IDLE_MASK		0x0100 +#define USB_E_TXE4_MASK		0x0080 +#define USB_E_TXE3_MASK		0x0040 +#define USB_E_TXE2_MASK		0x0020 +#define USB_E_TXE1_MASK		0x0010 +#define USB_E_SOF_MASK		0x0008 +#define USB_E_BSY_MASK		0x0004 +#define USB_E_TXB_MASK		0x0002 +#define USB_E_RXB_MASK		0x0001 +#define USBER_ALL_CLEAR 	0x0fff + +#define USB_E_DEFAULT_DEVICE   (USB_E_RESET_MASK | USB_E_TXE4_MASK | \ +				USB_E_TXE3_MASK | USB_E_TXE2_MASK | \ +				USB_E_TXE1_MASK | USB_E_BSY_MASK | \ +				USB_E_TXB_MASK | USB_E_RXB_MASK) + +#define USB_E_TXE_MASK         (USB_E_TXE4_MASK | USB_E_TXE3_MASK|\ +				 USB_E_TXE2_MASK | USB_E_TXE1_MASK) +/* USB Status Register define */ +#define USB_IDLE_STATUS_MASK	0x01 + +/* USB Start of Frame Timer */ +#define USB_USSFT_MASK		0x3FFF + +/* USB Frame Number Register */ +#define USB_USFRN_MASK		0xFFFF + +struct usb_device_para{ +	u16	epptr[4]; +	u32	rstate; +	u32	rptr; +	u16	frame_n; +	u16	rbcnt; +	u32	rtemp; +	u32	rxusb_data; +	u16	rxuptr; +	u8	reso[2]; +	u32	softbl; +	u8	sofucrctemp; +}; + +struct usb_ep_para{ +	u16	rbase; +	u16	tbase; +	u8	rbmr; +	u8	tbmr; +	u16	mrblr; +	u16	rbptr; +	u16	tbptr; +	u32	tstate; +	u32	tptr; +	u16	tcrc; +	u16	tbcnt; +	u32	ttemp; +	u16	txusbu_ptr; +	u8	reserve[2]; +}; + +#define USB_BUSMODE_GBL		0x20 +#define USB_BUSMODE_BO_MASK	0x18 +#define USB_BUSMODE_BO_SHIFT	0x3 +#define USB_BUSMODE_BE		0x2 +#define USB_BUSMODE_CETM	0x04 +#define USB_BUSMODE_DTB		0x02 + +/* Endpoint basic handle */ +#define ep_index(EP)		((EP)->desc->bEndpointAddress & 0xF) +#define ep_maxpacket(EP)	((EP)->ep.maxpacket) +#define ep_is_in(EP)	((ep_index(EP) == 0) ? (EP->udc->ep0_dir == \ +			USB_DIR_IN) : ((EP)->desc->bEndpointAddress \ +			& USB_DIR_IN) == USB_DIR_IN) + +/* ep0 transfer state */ +#define WAIT_FOR_SETUP          0 +#define DATA_STATE_XMIT         1 +#define DATA_STATE_NEED_ZLP     2 +#define WAIT_FOR_OUT_STATUS     3 +#define DATA_STATE_RECV         4 + +/* ep tramsfer mode */ +#define USBP_TM_CTL	0 +#define USBP_TM_ISO	1 +#define USBP_TM_BULK	2 +#define USBP_TM_INT	3 + +/*----------------------------------------------------------------------------- +	USB RX And TX DATA Frame + -----------------------------------------------------------------------------*/ +struct qe_frame{ +	u8 *data; +	u32 len; +	u32 status; +	u32 info; + +	void *privdata; +	struct list_head node; +}; + +/* Frame structure, info field. */ +#define PID_DATA0              0x80000000 /* Data toggle zero */ +#define PID_DATA1              0x40000000 /* Data toggle one  */ +#define PID_SETUP              0x20000000 /* setup bit */ +#define SETUP_STATUS           0x10000000 /* setup status bit */ +#define SETADDR_STATUS         0x08000000 /* setupup address status bit */ +#define NO_REQ                 0x04000000 /* Frame without request */ +#define HOST_DATA              0x02000000 /* Host data frame */ +#define FIRST_PACKET_IN_FRAME  0x01000000 /* first packet in the frame */ +#define TOKEN_FRAME            0x00800000 /* Host token frame */ +#define ZLP                    0x00400000 /* Zero length packet */ +#define IN_TOKEN_FRAME         0x00200000 /* In token package */ +#define OUT_TOKEN_FRAME        0x00100000 /* Out token package */ +#define SETUP_TOKEN_FRAME      0x00080000 /* Setup token package */ +#define STALL_FRAME            0x00040000 /* Stall handshake */ +#define NACK_FRAME             0x00020000 /* Nack handshake */ +#define NO_PID                 0x00010000 /* No send PID */ +#define NO_CRC                 0x00008000 /* No send CRC */ +#define HOST_COMMAND           0x00004000 /* Host command frame   */ + +/* Frame status field */ +/* Receive side */ +#define FRAME_OK               0x00000000 /* Frame tranmitted or received OK */ +#define FRAME_ERROR            0x80000000 /* Error occured on frame */ +#define START_FRAME_LOST       0x40000000 /* START_FRAME_LOST */ +#define END_FRAME_LOST         0x20000000 /* END_FRAME_LOST */ +#define RX_ER_NONOCT           0x10000000 /* Rx Non Octet Aligned Packet */ +#define RX_ER_BITSTUFF         0x08000000 /* Frame Aborted --Received packet +					     with bit stuff error */ +#define RX_ER_CRC              0x04000000 /* Received packet with CRC error */ +#define RX_ER_OVERUN           0x02000000 /* Over-run occured on reception */ +#define RX_ER_PID              0x01000000 /* Wrong PID received */ +/* Tranmit side */ +#define TX_ER_NAK              0x00800000 /* Received NAK handshake */ +#define TX_ER_STALL            0x00400000 /* Received STALL handshake */ +#define TX_ER_TIMEOUT          0x00200000 /* Transmit time out */ +#define TX_ER_UNDERUN          0x00100000 /* Transmit underrun */ +#define FRAME_INPROGRESS       0x00080000 /* Frame is being transmitted */ +#define ER_DATA_UNDERUN        0x00040000 /* Frame is shorter then expected */ +#define ER_DATA_OVERUN         0x00020000 /* Frame is longer then expected */ + +/* QE USB frame operation functions */ +#define frame_get_length(frm) (frm->len) +#define frame_set_length(frm, leng) (frm->len = leng) +#define frame_get_data(frm) (frm->data) +#define frame_set_data(frm, dat) (frm->data = dat) +#define frame_get_info(frm) (frm->info) +#define frame_set_info(frm, inf) (frm->info = inf) +#define frame_get_status(frm) (frm->status) +#define frame_set_status(frm, stat) (frm->status = stat) +#define frame_get_privdata(frm) (frm->privdata) +#define frame_set_privdata(frm, dat) (frm->privdata = dat) + +static inline void qe_frame_clean(struct qe_frame *frm) +{ +	frame_set_data(frm, NULL); +	frame_set_length(frm, 0); +	frame_set_status(frm, FRAME_OK); +	frame_set_info(frm, 0); +	frame_set_privdata(frm, NULL); +} + +static inline void qe_frame_init(struct qe_frame *frm) +{ +	qe_frame_clean(frm); +	INIT_LIST_HEAD(&(frm->node)); +} + +struct qe_req { +	struct usb_request req; +	struct list_head queue; +	/* ep_queue() func will add +	 a request->queue into a udc_ep->queue 'd tail */ +	struct qe_ep *ep; +	unsigned mapped:1; +}; + +struct qe_ep { +	struct usb_ep ep; +	struct list_head queue; +	struct qe_udc *udc; +	const struct usb_endpoint_descriptor *desc; +	struct usb_gadget *gadget; + +	u8 state; + +	struct qe_bd __iomem *rxbase; +	struct qe_bd __iomem *n_rxbd; +	struct qe_bd __iomem *e_rxbd; + +	struct qe_bd __iomem *txbase; +	struct qe_bd __iomem *n_txbd; +	struct qe_bd __iomem *c_txbd; + +	struct qe_frame *rxframe; +	u8 *rxbuffer; +	dma_addr_t rxbuf_d; +	u8 rxbufmap; +	unsigned char localnack; +	int has_data; + +	struct qe_frame *txframe; +	struct qe_req *tx_req; +	int sent;  /*data already sent */ +	int last;  /*data sent in the last time*/ + +	u8 dir; +	u8 epnum; +	u8 tm; /* transfer mode */ +	u8 data01; +	u8 init; + +	u8 already_seen; +	u8 enable_tasklet; +	u8 setup_stage; +	u32 last_io;            /* timestamp */ + +	char name[14]; + +	unsigned double_buf:1; +	unsigned stopped:1; +	unsigned fnf:1; +	unsigned has_dma:1; + +	u8 ackwait; +	u8 dma_channel; +	u16 dma_counter; +	int lch; + +	struct timer_list timer; +}; + +struct qe_udc { +	struct usb_gadget gadget; +	struct usb_gadget_driver *driver; +	struct device *dev; +	struct qe_ep eps[USB_MAX_ENDPOINTS]; +	struct usb_ctrlrequest local_setup_buff; +	spinlock_t lock;	/* lock for set/config qe_udc */ +	unsigned long soc_type;		/* QE or CPM soc */ + +	struct qe_req *status_req;	/* ep0 status request */ + +	/* USB and EP Parameter Block pointer */ +	struct usb_device_para __iomem *usb_param; +	struct usb_ep_para __iomem *ep_param[4]; + +	u32 max_pipes;          /* Device max pipes */ +	u32 max_use_endpts;     /* Max endpointes to be used */ +	u32 bus_reset;          /* Device is bus reseting */ +	u32 resume_state;       /* USB state to resume*/ +	u32 usb_state;          /* USB current state */ +	u32 usb_next_state;     /* USB next state */ +	u32 ep0_state;          /* Enpoint zero state */ +	u32 ep0_dir;            /* Enpoint zero direction: can be +				USB_DIR_IN or USB_DIR_OUT*/ +	u32 usb_sof_count;      /* SOF count */ +	u32 errors;             /* USB ERRORs count */ + +	u8 *tmpbuf; +	u32 c_start; +	u32 c_end; + +	u8 *nullbuf; +	u8 *statusbuf; +	dma_addr_t nullp; +	u8 nullmap; +	u8 device_address;	/* Device USB address */ + +	unsigned int usb_clock; +	unsigned int usb_irq; +	struct usb_ctlr __iomem *usb_regs; + +	struct tasklet_struct rx_tasklet; + +	struct completion *done;	/* to make sure release() is done */ +}; + +#define EP_STATE_IDLE	0 +#define EP_STATE_NACK	1 +#define EP_STATE_STALL	2 + +/* + * transmit BD's status + */ +#define T_R           0x80000000         /* ready bit */ +#define T_W           0x20000000         /* wrap bit */ +#define T_I           0x10000000         /* interrupt on completion */ +#define T_L           0x08000000         /* last */ +#define T_TC          0x04000000         /* transmit CRC */ +#define T_CNF         0x02000000         /* wait for  transmit confirm */ +#define T_LSP         0x01000000         /* Low-speed transaction */ +#define T_PID         0x00c00000         /* packet id */ +#define T_NAK         0x00100000         /* No ack. */ +#define T_STAL        0x00080000         /* Stall recieved */ +#define T_TO          0x00040000         /* time out */ +#define T_UN          0x00020000         /* underrun */ + +#define DEVICE_T_ERROR    (T_UN | T_TO) +#define HOST_T_ERROR      (T_UN | T_TO | T_NAK | T_STAL) +#define DEVICE_T_BD_MASK  DEVICE_T_ERROR +#define HOST_T_BD_MASK    HOST_T_ERROR + +#define T_PID_SHIFT   6 +#define T_PID_DATA0   0x00800000         /* Data 0 toggle */ +#define T_PID_DATA1   0x00c00000         /* Data 1 toggle */ + +/* + * receive BD's status + */ +#define R_E           0x80000000         /* buffer empty */ +#define R_W           0x20000000         /* wrap bit */ +#define R_I           0x10000000         /* interrupt on reception */ +#define R_L           0x08000000         /* last */ +#define R_F           0x04000000         /* first */ +#define R_PID         0x00c00000         /* packet id */ +#define R_NO          0x00100000         /* Rx Non Octet Aligned Packet */ +#define R_AB          0x00080000         /* Frame Aborted */ +#define R_CR          0x00040000         /* CRC Error */ +#define R_OV          0x00020000         /* Overrun */ + +#define R_ERROR       (R_NO | R_AB | R_CR | R_OV) +#define R_BD_MASK     R_ERROR + +#define R_PID_DATA0   0x00000000 +#define R_PID_DATA1   0x00400000 +#define R_PID_SETUP   0x00800000 + +#define CPM_USB_STOP_TX 0x2e600000 +#define CPM_USB_RESTART_TX 0x2e600000 +#define CPM_USB_STOP_TX_OPCODE 0x0a +#define CPM_USB_RESTART_TX_OPCODE 0x0b +#define CPM_USB_EP_SHIFT 5 + +#ifndef CONFIG_CPM +inline int cpm_command(u32 command, u8 opcode) +{ +	return -EOPNOTSUPP; +} +#endif + +#ifndef CONFIG_QUICC_ENGINE +inline int qe_issue_cmd(u32 cmd, u32 device, u8 mcn_protocol, +	u32 cmd_input) +{ +	return -EOPNOTSUPP; +} +#endif + +#endif  /* __FSL_QE_UDC_H */ diff --git a/drivers/usb/gadget/fsl_usb2_udc.c b/drivers/usb/gadget/fsl_usb2_udc.c index 45ad556169f..091bb55c9aa 100644 --- a/drivers/usb/gadget/fsl_usb2_udc.c +++ b/drivers/usb/gadget/fsl_usb2_udc.c @@ -23,11 +23,8 @@  #include <linux/ioport.h>  #include <linux/types.h>  #include <linux/errno.h> -#include <linux/delay.h> -#include <linux/sched.h>  #include <linux/slab.h>  #include <linux/init.h> -#include <linux/timer.h>  #include <linux/list.h>  #include <linux/interrupt.h>  #include <linux/proc_fs.h> @@ -44,11 +41,9 @@  #include <asm/byteorder.h>  #include <asm/io.h> -#include <asm/irq.h>  #include <asm/system.h>  #include <asm/unaligned.h>  #include <asm/dma.h> -#include <asm/cacheflush.h>  #include "fsl_usb2_udc.h" @@ -61,8 +56,8 @@  static const char driver_name[] = "fsl-usb2-udc";  static const char driver_desc[] = DRIVER_DESC; -volatile static struct usb_dr_device *dr_regs = NULL; -volatile static struct usb_sys_interface *usb_sys_regs = NULL; +static struct usb_dr_device *dr_regs; +static struct usb_sys_interface *usb_sys_regs;  /* it is initialized in probe()  */  static struct fsl_udc *udc_controller = NULL; @@ -76,16 +71,14 @@ fsl_ep0_desc = {  	.wMaxPacketSize =	USB_MAX_CTRL_PAYLOAD,  }; -static int fsl_udc_suspend(struct platform_device *pdev, pm_message_t state); -static int fsl_udc_resume(struct platform_device *pdev);  static void fsl_ep_fifo_flush(struct usb_ep *_ep);  #ifdef CONFIG_PPC32  #define fsl_readl(addr)		in_le32(addr) -#define fsl_writel(addr, val32) out_le32(val32, addr) +#define fsl_writel(val32, addr) out_le32(addr, val32)  #else  #define fsl_readl(addr)		readl(addr) -#define fsl_writel(addr, val32) writel(addr, val32) +#define fsl_writel(val32, addr) writel(val32, addr)  #endif  /******************************************************************** @@ -185,10 +178,6 @@ static int dr_controller_setup(struct fsl_udc *udc)  	unsigned long timeout;  #define FSL_UDC_RESET_TIMEOUT 1000 -	/* before here, make sure dr_regs has been initialized */ -	if (!udc) -		return -EINVAL; -  	/* Stop and reset the usb controller */  	tmp = fsl_readl(&dr_regs->usbcmd);  	tmp &= ~USB_CMD_RUN_STOP; @@ -202,7 +191,7 @@ static int dr_controller_setup(struct fsl_udc *udc)  	timeout = jiffies + FSL_UDC_RESET_TIMEOUT;  	while (fsl_readl(&dr_regs->usbcmd) & USB_CMD_CTRL_RESET) {  		if (time_after(jiffies, timeout)) { -			ERR("udc reset timeout! \n"); +			ERR("udc reset timeout!\n");  			return -ETIMEDOUT;  		}  		cpu_relax(); @@ -315,7 +304,8 @@ static void dr_controller_stop(struct fsl_udc *udc)  	return;  } -void dr_ep_setup(unsigned char ep_num, unsigned char dir, unsigned char ep_type) +static void dr_ep_setup(unsigned char ep_num, unsigned char dir, +			unsigned char ep_type)  {  	unsigned int tmp_epctrl = 0; @@ -563,7 +553,7 @@ static int fsl_ep_disable(struct usb_ep *_ep)  	/* nuke all pending requests (does flush) */  	nuke(ep, -ESHUTDOWN); -	ep->desc = 0; +	ep->desc = NULL;  	ep->stopped = 1;  	spin_unlock_irqrestore(&udc->lock, flags); @@ -602,7 +592,7 @@ static void fsl_free_request(struct usb_ep *_ep, struct usb_request *_req)  }  /*-------------------------------------------------------------------------*/ -static int fsl_queue_td(struct fsl_ep *ep, struct fsl_req *req) +static void fsl_queue_td(struct fsl_ep *ep, struct fsl_req *req)  {  	int i = ep_index(ep) * 2 + ep_is_in(ep);  	u32 temp, bitmask, tmp_stat; @@ -653,13 +643,16 @@ static int fsl_queue_td(struct fsl_ep *ep, struct fsl_req *req)  			| EP_QUEUE_HEAD_STATUS_HALT));  	dQH->size_ioc_int_sts &= temp; +	/* Ensure that updates to the QH will occure before priming. */ +	wmb(); +  	/* Prime endpoint by writing 1 to ENDPTPRIME */  	temp = ep_is_in(ep)  		? (1 << (ep_index(ep) + 16))  		: (1 << (ep_index(ep)));  	fsl_writel(temp, &dr_regs->endpointprime);  out: -	return 0; +	return;  }  /* Fill in the dTD structure @@ -710,7 +703,7 @@ static struct ep_td_struct *fsl_build_dtd(struct fsl_req *req, unsigned *length,  		*is_last = 0;  	if ((*is_last) == 0) -		VDBG("multi-dtd request!\n"); +		VDBG("multi-dtd request!");  	/* Fill in the transfer size; set active bit */  	swap_temp = ((*length << DTD_LENGTH_BIT_POS) | DTD_STATUS_ACTIVE); @@ -773,11 +766,11 @@ fsl_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)  	/* catch various bogus parameters */  	if (!_req || !req->req.complete || !req->req.buf  			|| !list_empty(&req->queue)) { -		VDBG("%s, bad params\n", __func__); +		VDBG("%s, bad params", __func__);  		return -EINVAL;  	}  	if (unlikely(!_ep || !ep->desc)) { -		VDBG("%s, bad ep\n", __func__); +		VDBG("%s, bad ep", __func__);  		return -EINVAL;  	}  	if (ep->desc->bmAttributes == USB_ENDPOINT_XFER_ISOC) { @@ -1069,7 +1062,7 @@ static int fsl_vbus_session(struct usb_gadget *gadget, int is_active)  	udc = container_of(gadget, struct fsl_udc, gadget);  	spin_lock_irqsave(&udc->lock, flags); -	VDBG("VBUS %s\n", is_active ? "on" : "off"); +	VDBG("VBUS %s", is_active ? "on" : "off");  	udc->vbus_active = (is_active != 0);  	if (can_pullup(udc))  		fsl_writel((fsl_readl(&dr_regs->usbcmd) | USB_CMD_RUN_STOP), @@ -1146,7 +1139,6 @@ static int ep0_prime_status(struct fsl_udc *udc, int direction)  {  	struct fsl_req *req = udc->status_req;  	struct fsl_ep *ep; -	int status = 0;  	if (direction == EP_DIR_IN)  		udc->ep0_dir = USB_DIR_IN; @@ -1164,27 +1156,21 @@ static int ep0_prime_status(struct fsl_udc *udc, int direction)  	req->dtd_count = 0;  	if (fsl_req_to_dtd(req) == 0) -		status = fsl_queue_td(ep, req); +		fsl_queue_td(ep, req);  	else  		return -ENOMEM; -	if (status) -		ERR("Can't queue ep0 status request \n");  	list_add_tail(&req->queue, &ep->queue); -	return status; +	return 0;  } -static inline int udc_reset_ep_queue(struct fsl_udc *udc, u8 pipe) +static void udc_reset_ep_queue(struct fsl_udc *udc, u8 pipe)  {  	struct fsl_ep *ep = get_ep_by_pipe(udc, pipe); -	if (!ep->name) -		return 0; - -	nuke(ep, -ESHUTDOWN); - -	return 0; +	if (ep->name) +		nuke(ep, -ESHUTDOWN);  }  /* @@ -1208,10 +1194,8 @@ static void ch9getstatus(struct fsl_udc *udc, u8 request_type, u16 value,  		u16 index, u16 length)  {  	u16 tmp = 0;		/* Status, cpu endian */ -  	struct fsl_req *req;  	struct fsl_ep *ep; -	int status = 0;  	ep = &udc->eps[0]; @@ -1250,14 +1234,10 @@ static void ch9getstatus(struct fsl_udc *udc, u8 request_type, u16 value,  	/* prime the data phase */  	if ((fsl_req_to_dtd(req) == 0)) -		status = fsl_queue_td(ep, req); +		fsl_queue_td(ep, req);  	else			/* no mem */  		goto stall; -	if (status) { -		ERR("Can't respond to getstatus request \n"); -		goto stall; -	}  	list_add_tail(&req->queue, &ep->queue);  	udc->ep0_state = DATA_STATE_XMIT;  	return; @@ -1397,7 +1377,7 @@ static void ep0_req_complete(struct fsl_udc *udc, struct fsl_ep *ep0,  		udc->ep0_state = WAIT_FOR_SETUP;  		break;  	case WAIT_FOR_SETUP: -		ERR("Unexpect ep0 packets \n"); +		ERR("Unexpect ep0 packets\n");  		break;  	default:  		ep0stall(udc); @@ -1476,7 +1456,7 @@ static int process_ep_req(struct fsl_udc *udc, int pipe,  				status = -EILSEQ;  				break;  			} else -				ERR("Unknown error has occured (0x%x)!\r\n", +				ERR("Unknown error has occured (0x%x)!\n",  					errors);  		} else if (le32_to_cpu(curr_td->size_ioc_sts) @@ -1495,7 +1475,7 @@ static int process_ep_req(struct fsl_udc *udc, int pipe,  			}  		} else {  			td_complete++; -			VDBG("dTD transmitted successful "); +			VDBG("dTD transmitted successful");  		}  		if (j != curr_req->dtd_count - 1) @@ -1568,9 +1548,6 @@ static void port_change_irq(struct fsl_udc *udc)  {  	u32 speed; -	if (udc->bus_reset) -		udc->bus_reset = 0; -  	/* Bus resetting is finished */  	if (!(fsl_readl(&dr_regs->portsc1) & PORTSCX_PORT_RESET)) {  		/* Get the speed */ @@ -1678,8 +1655,6 @@ static void reset_irq(struct fsl_udc *udc)  	if (fsl_readl(&dr_regs->portsc1) & PORTSCX_PORT_RESET) {  		VDBG("Bus reset"); -		/* Bus is reseting */ -		udc->bus_reset = 1;  		/* Reset all the queues, include XD, dTD, EP queue  		 * head and TR Queue */  		reset_queues(udc); @@ -1768,7 +1743,7 @@ static irqreturn_t fsl_udc_irq(int irq, void *_udc)  	}  	if (irq_src & (USB_STS_ERR | USB_STS_SYS_ERR)) { -		VDBG("Error IRQ %x ", irq_src); +		VDBG("Error IRQ %x", irq_src);  	}  	spin_unlock_irqrestore(&udc->lock, flags); @@ -1799,7 +1774,7 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver)  	/* lock is needed but whether should use this lock or another */  	spin_lock_irqsave(&udc_controller->lock, flags); -	driver->driver.bus = 0; +	driver->driver.bus = NULL;  	/* hook up the driver */  	udc_controller->driver = driver;  	udc_controller->gadget.dev.driver = &driver->driver; @@ -1809,8 +1784,8 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver)  	retval = driver->bind(&udc_controller->gadget);  	if (retval) {  		VDBG("bind to %s --> %d", driver->driver.name, retval); -		udc_controller->gadget.dev.driver = 0; -		udc_controller->driver = 0; +		udc_controller->gadget.dev.driver = NULL; +		udc_controller->driver = NULL;  		goto out;  	} @@ -1819,12 +1794,12 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver)  	udc_controller->usb_state = USB_STATE_ATTACHED;  	udc_controller->ep0_state = WAIT_FOR_SETUP;  	udc_controller->ep0_dir = 0; -	printk(KERN_INFO "%s: bind to driver %s \n", +	printk(KERN_INFO "%s: bind to driver %s\n",  			udc_controller->gadget.name, driver->driver.name);  out:  	if (retval) -		printk("retval %d \n", retval); +		printk("gadget driver register failed %d\n", retval);  	return retval;  }  EXPORT_SYMBOL(usb_gadget_register_driver); @@ -1842,7 +1817,7 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)  		return -EINVAL;  	if (udc_controller->transceiver) -		(void)otg_set_peripheral(udc_controller->transceiver, 0); +		otg_set_peripheral(udc_controller->transceiver, NULL);  	/* stop DR, disable intr */  	dr_controller_stop(udc_controller); @@ -1863,10 +1838,10 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)  	/* unbind gadget and unhook driver. */  	driver->unbind(&udc_controller->gadget); -	udc_controller->gadget.dev.driver = 0; -	udc_controller->driver = 0; +	udc_controller->gadget.dev.driver = NULL; +	udc_controller->driver = NULL; -	printk("unregistered gadget driver '%s'\r\n", driver->driver.name); +	printk("unregistered gadget driver '%s'\n", driver->driver.name);  	return 0;  }  EXPORT_SYMBOL(usb_gadget_unregister_driver); @@ -1922,7 +1897,7 @@ static int fsl_proc_read(char *page, char **start, off_t off, int count,  	tmp_reg = fsl_readl(&dr_regs->usbsts);  	t = scnprintf(next, size,  			"USB Status Reg:\n" -			"Dr Suspend: %d" "Reset Received: %d" "System Error: %s" +			"Dr Suspend: %d Reset Received: %d System Error: %s "  			"USB Error Interrupt: %s\n\n",  			(tmp_reg & USB_STS_SUSPEND) ? 1 : 0,  			(tmp_reg & USB_STS_RESET) ? 1 : 0, @@ -1934,11 +1909,11 @@ static int fsl_proc_read(char *page, char **start, off_t off, int count,  	tmp_reg = fsl_readl(&dr_regs->usbintr);  	t = scnprintf(next, size,  			"USB Intrrupt Enable Reg:\n" -			"Sleep Enable: %d" "SOF Received Enable: %d" +			"Sleep Enable: %d SOF Received Enable: %d "  			"Reset Enable: %d\n" -			"System Error Enable: %d" +			"System Error Enable: %d "  			"Port Change Dectected Enable: %d\n" -			"USB Error Intr Enable: %d" "USB Intr Enable: %d\n\n", +			"USB Error Intr Enable: %d USB Intr Enable: %d\n\n",  			(tmp_reg & USB_INTR_DEVICE_SUSPEND) ? 1 : 0,  			(tmp_reg & USB_INTR_SOF_EN) ? 1 : 0,  			(tmp_reg & USB_INTR_RESET_EN) ? 1 : 0, @@ -1951,21 +1926,21 @@ static int fsl_proc_read(char *page, char **start, off_t off, int count,  	tmp_reg = fsl_readl(&dr_regs->frindex);  	t = scnprintf(next, size, -			"USB Frame Index Reg:" "Frame Number is 0x%x\n\n", +			"USB Frame Index Reg: Frame Number is 0x%x\n\n",  			(tmp_reg & USB_FRINDEX_MASKS));  	size -= t;  	next += t;  	tmp_reg = fsl_readl(&dr_regs->deviceaddr);  	t = scnprintf(next, size, -			"USB Device Address Reg:" "Device Addr is 0x%x\n\n", +			"USB Device Address Reg: Device Addr is 0x%x\n\n",  			(tmp_reg & USB_DEVICE_ADDRESS_MASK));  	size -= t;  	next += t;  	tmp_reg = fsl_readl(&dr_regs->endpointlistaddr);  	t = scnprintf(next, size, -			"USB Endpoint List Address Reg:" +			"USB Endpoint List Address Reg: "  			"Device Addr is 0x%x\n\n",  			(tmp_reg & USB_EP_LIST_ADDRESS_MASK));  	size -= t; @@ -1974,11 +1949,12 @@ static int fsl_proc_read(char *page, char **start, off_t off, int count,  	tmp_reg = fsl_readl(&dr_regs->portsc1);  	t = scnprintf(next, size,  		"USB Port Status&Control Reg:\n" -		"Port Transceiver Type : %s" "Port Speed: %s \n" -		"PHY Low Power Suspend: %s" "Port Reset: %s" -		"Port Suspend Mode: %s \n" "Over-current Change: %s" +		"Port Transceiver Type : %s Port Speed: %s\n" +		"PHY Low Power Suspend: %s Port Reset: %s " +		"Port Suspend Mode: %s\n" +		"Over-current Change: %s "  		"Port Enable/Disable Change: %s\n" -		"Port Enabled/Disabled: %s" +		"Port Enabled/Disabled: %s "  		"Current Connect Status: %s\n\n", ( {  			char *s;  			switch (tmp_reg & PORTSCX_PTS_FSLS) { @@ -2023,7 +1999,7 @@ static int fsl_proc_read(char *page, char **start, off_t off, int count,  	tmp_reg = fsl_readl(&dr_regs->usbmode);  	t = scnprintf(next, size, -			"USB Mode Reg:" "Controller Mode is : %s\n\n", ( { +			"USB Mode Reg: Controller Mode is: %s\n\n", ( {  				char *s;  				switch (tmp_reg & USB_MODE_CTRL_MODE_HOST) {  				case USB_MODE_CTRL_MODE_IDLE: @@ -2042,7 +2018,7 @@ static int fsl_proc_read(char *page, char **start, off_t off, int count,  	tmp_reg = fsl_readl(&dr_regs->endptsetupstat);  	t = scnprintf(next, size, -			"Endpoint Setup Status Reg:" "SETUP on ep 0x%x\n\n", +			"Endpoint Setup Status Reg: SETUP on ep 0x%x\n\n",  			(tmp_reg & EP_SETUP_STATUS_MASK));  	size -= t;  	next += t; @@ -2055,12 +2031,12 @@ static int fsl_proc_read(char *page, char **start, off_t off, int count,  		next += t;  	}  	tmp_reg = fsl_readl(&dr_regs->endpointprime); -	t = scnprintf(next, size, "EP Prime Reg = [0x%x]\n", tmp_reg); +	t = scnprintf(next, size, "EP Prime Reg = [0x%x]\n\n", tmp_reg);  	size -= t;  	next += t;  	tmp_reg = usb_sys_regs->snoop1; -	t = scnprintf(next, size, "\nSnoop1 Reg : = [0x%x]\n\n", tmp_reg); +	t = scnprintf(next, size, "Snoop1 Reg : = [0x%x]\n\n", tmp_reg);  	size -= t;  	next += t; @@ -2084,7 +2060,7 @@ static int fsl_proc_read(char *page, char **start, off_t off, int count,  	} else {  		list_for_each_entry(req, &ep->queue, queue) {  			t = scnprintf(next, size, -				"req %p actual 0x%x length 0x%x  buf %p\n", +				"req %p actual 0x%x length 0x%x buf %p\n",  				&req->req, req->req.actual,  				req->req.length, req->req.buf);  			size -= t; @@ -2110,7 +2086,7 @@ static int fsl_proc_read(char *page, char **start, off_t off, int count,  			} else {  				list_for_each_entry(req, &ep->queue, queue) {  					t = scnprintf(next, size, -						"req %p actual 0x%x length" +						"req %p actual 0x%x length "  						"0x%x  buf %p\n",  						&req->req, req->req.actual,  						req->req.length, req->req.buf); @@ -2202,7 +2178,6 @@ static int __init struct_udc_setup(struct fsl_udc *udc,  	udc->usb_state = USB_STATE_POWERED;  	udc->ep0_dir = 0;  	udc->remote_wakeup = 0;	/* default to 0 on reset */ -	spin_lock_init(&udc->lock);  	return 0;  } @@ -2254,7 +2229,7 @@ static int __init fsl_udc_probe(struct platform_device *pdev)  	u32 dccparams;  	if (strcmp(pdev->name, driver_name)) { -		VDBG("Wrong device\n"); +		VDBG("Wrong device");  		return -ENODEV;  	} @@ -2264,23 +2239,26 @@ static int __init fsl_udc_probe(struct platform_device *pdev)  		return -ENOMEM;  	} +	spin_lock_init(&udc_controller->lock); +	udc_controller->stopped = 1; +  	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);  	if (!res) { -		kfree(udc_controller); -		return -ENXIO; +		ret = -ENXIO; +		goto err_kfree;  	}  	if (!request_mem_region(res->start, res->end - res->start + 1,  				driver_name)) { -		ERR("request mem region for %s failed \n", pdev->name); -		kfree(udc_controller); -		return -EBUSY; +		ERR("request mem region for %s failed\n", pdev->name); +		ret = -EBUSY; +		goto err_kfree;  	}  	dr_regs = ioremap(res->start, res->end - res->start + 1);  	if (!dr_regs) {  		ret = -ENOMEM; -		goto err1; +		goto err_release_mem_region;  	}  	usb_sys_regs = (struct usb_sys_interface *) @@ -2291,7 +2269,7 @@ static int __init fsl_udc_probe(struct platform_device *pdev)  	if (!(dccparams & DCCPARAMS_DC)) {  		ERR("This SOC doesn't support device role\n");  		ret = -ENODEV; -		goto err2; +		goto err_iounmap;  	}  	/* Get max device endpoints */  	/* DEN is bidirectional ep number, max_ep doubles the number */ @@ -2300,22 +2278,22 @@ static int __init fsl_udc_probe(struct platform_device *pdev)  	udc_controller->irq = platform_get_irq(pdev, 0);  	if (!udc_controller->irq) {  		ret = -ENODEV; -		goto err2; +		goto err_iounmap;  	}  	ret = request_irq(udc_controller->irq, fsl_udc_irq, IRQF_SHARED,  			driver_name, udc_controller);  	if (ret != 0) { -		ERR("cannot request irq %d err %d \n", +		ERR("cannot request irq %d err %d\n",  				udc_controller->irq, ret); -		goto err2; +		goto err_iounmap;  	}  	/* Initialize the udc structure including QH member and other member */  	if (struct_udc_setup(udc_controller, pdev)) {  		ERR("Can't initialize udc data structure\n");  		ret = -ENOMEM; -		goto err3; +		goto err_free_irq;  	}  	/* initialize usb hw reg except for regs for EP, @@ -2336,7 +2314,7 @@ static int __init fsl_udc_probe(struct platform_device *pdev)  	udc_controller->gadget.dev.parent = &pdev->dev;  	ret = device_register(&udc_controller->gadget.dev);  	if (ret < 0) -		goto err3; +		goto err_free_irq;  	/* setup QH and epctrl for ep0 */  	ep0_setup(udc_controller); @@ -2366,20 +2344,22 @@ static int __init fsl_udc_probe(struct platform_device *pdev)  			DTD_ALIGNMENT, UDC_DMA_BOUNDARY);  	if (udc_controller->td_pool == NULL) {  		ret = -ENOMEM; -		goto err4; +		goto err_unregister;  	}  	create_proc_file();  	return 0; -err4: +err_unregister:  	device_unregister(&udc_controller->gadget.dev); -err3: +err_free_irq:  	free_irq(udc_controller->irq, udc_controller); -err2: +err_iounmap:  	iounmap(dr_regs); -err1: +err_release_mem_region:  	release_mem_region(res->start, res->end - res->start + 1); +err_kfree:  	kfree(udc_controller); +	udc_controller = NULL;  	return ret;  } @@ -2469,7 +2449,7 @@ module_init(udc_init);  static void __exit udc_exit(void)  {  	platform_driver_unregister(&udc_driver); -	printk("%s unregistered \n", driver_desc); +	printk("%s unregistered\n", driver_desc);  }  module_exit(udc_exit); diff --git a/drivers/usb/gadget/fsl_usb2_udc.h b/drivers/usb/gadget/fsl_usb2_udc.h index 6131752a38b..e63ef12645f 100644 --- a/drivers/usb/gadget/fsl_usb2_udc.h +++ b/drivers/usb/gadget/fsl_usb2_udc.h @@ -424,16 +424,6 @@ struct ep_td_struct {  /* Controller dma boundary */  #define UDC_DMA_BOUNDARY			0x1000 -/* -----------------------------------------------------------------------*/ -/* ##### enum data -*/ -typedef enum { -	e_ULPI, -	e_UTMI_8BIT, -	e_UTMI_16BIT, -	e_SERIAL -} e_PhyInterface; -  /*-------------------------------------------------------------------------*/  /* ### driver private data @@ -469,9 +459,9 @@ struct fsl_ep {  #define EP_DIR_OUT	0  struct fsl_udc { -  	struct usb_gadget gadget;  	struct usb_gadget_driver *driver; +	struct completion *done;	/* to make sure release() is done */  	struct fsl_ep *eps;  	unsigned int max_ep;  	unsigned int irq; @@ -492,20 +482,13 @@ struct fsl_udc {  	size_t ep_qh_size;		/* size after alignment adjustment*/  	dma_addr_t ep_qh_dma;		/* dma address of QH */ -	u32 max_pipes;		/* Device max pipes */ -	u32 max_use_endpts;	/* Max endpointes to be used */ -	u32 bus_reset;		/* Device is bus reseting */ +	u32 max_pipes;          /* Device max pipes */  	u32 resume_state;	/* USB state to resume */  	u32 usb_state;		/* USB current state */ -	u32 usb_next_state;	/* USB next state */  	u32 ep0_state;		/* Endpoint zero state */  	u32 ep0_dir;		/* Endpoint zero direction: can be  				   USB_DIR_IN or USB_DIR_OUT */ -	u32 usb_sof_count;	/* SOF count */ -	u32 errors;		/* USB ERRORs count */  	u8 device_address;	/* Device USB address */ - -	struct completion *done;	/* to make sure release() is done */  };  /*-------------------------------------------------------------------------*/ diff --git a/drivers/usb/gadget/gadget_chips.h b/drivers/usb/gadget/gadget_chips.h index 17d9905101b..4e3107dd2f3 100644 --- a/drivers/usb/gadget/gadget_chips.h +++ b/drivers/usb/gadget/gadget_chips.h @@ -151,6 +151,13 @@  #define	gadget_is_m66592(g)	0  #endif +/* Freescale CPM/QE UDC SUPPORT */ +#ifdef CONFIG_USB_GADGET_FSL_QE +#define gadget_is_fsl_qe(g)	!strcmp("fsl_qe_udc", (g)->name) +#else +#define gadget_is_fsl_qe(g)	0 +#endif +  // CONFIG_USB_GADGET_SX2  // CONFIG_USB_GADGET_AU1X00 @@ -216,6 +223,8 @@ static inline int usb_gadget_controller_number(struct usb_gadget *gadget)  		return 0x20;  	else if (gadget_is_m66592(gadget))  		return 0x21; +	else if (gadget_is_fsl_qe(gadget)) +		return 0x22;  	return -ENOENT;  } diff --git a/drivers/usb/gadget/gmidi.c b/drivers/usb/gadget/gmidi.c index ea8651e3da1..60d3f9e9b51 100644 --- a/drivers/usb/gadget/gmidi.c +++ b/drivers/usb/gadget/gmidi.c @@ -35,6 +35,21 @@  #include "gadget_chips.h" + +/* + * Kbuild is not very cooperative with respect to linking separately + * compiled library objects into one module.  So for now we won't use + * separate compilation ... ensuring init/exit sections work to shrink + * the runtime footprint, and giving us at least some parts of what + * a "gcc --combine ... part1.c part2.c part3.c ... " build would. + */ +#include "usbstring.c" +#include "config.c" +#include "epautoconf.c" + +/*-------------------------------------------------------------------------*/ + +  MODULE_AUTHOR("Ben Williamson");  MODULE_LICENSE("GPL v2"); @@ -207,7 +222,7 @@ static struct usb_config_descriptor config_desc = {  	 * power properties of the device. Is it selfpowered?  	 */  	.bmAttributes =		USB_CONFIG_ATT_ONE, -	.bMaxPower =		1, +	.bMaxPower =		CONFIG_USB_GADGET_VBUS_DRAW / 2,  };  /* B.3.1  Standard AC Interface Descriptor */ diff --git a/drivers/usb/gadget/inode.c b/drivers/usb/gadget/inode.c index f4585d3e90d..eeb26c0f88e 100644 --- a/drivers/usb/gadget/inode.c +++ b/drivers/usb/gadget/inode.c @@ -1251,7 +1251,6 @@ dev_release (struct inode *inode, struct file *fd)  	 * alternatively, all host requests will time out.  	 */ -	fasync_helper (-1, fd, 0, &dev->fasync);  	kfree (dev->buf);  	dev->buf = NULL;  	put_dev (dev); diff --git a/drivers/usb/gadget/net2280.c b/drivers/usb/gadget/net2280.c index 5cfb5ebf388..8ae70de2c37 100644 --- a/drivers/usb/gadget/net2280.c +++ b/drivers/usb/gadget/net2280.c @@ -178,6 +178,7 @@ net2280_enable (struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)  	/* ep_reset() has already been called */  	ep->stopped = 0; +	ep->wedged = 0;  	ep->out_overflow = 0;  	/* set speed-dependent max packet; may kick in high bandwidth */ @@ -1218,7 +1219,7 @@ static int net2280_dequeue (struct usb_ep *_ep, struct usb_request *_req)  static int net2280_fifo_status (struct usb_ep *_ep);  static int -net2280_set_halt (struct usb_ep *_ep, int value) +net2280_set_halt_and_wedge(struct usb_ep *_ep, int value, int wedged)  {  	struct net2280_ep	*ep;  	unsigned long		flags; @@ -1239,16 +1240,21 @@ net2280_set_halt (struct usb_ep *_ep, int value)  	else if (ep->is_in && value && net2280_fifo_status (_ep) != 0)  		retval = -EAGAIN;  	else { -		VDEBUG (ep->dev, "%s %s halt\n", _ep->name, -				value ? "set" : "clear"); +		VDEBUG (ep->dev, "%s %s %s\n", _ep->name, +				value ? "set" : "clear", +				wedged ? "wedge" : "halt");  		/* set/clear, then synch memory views with the device */  		if (value) {  			if (ep->num == 0)  				ep->dev->protocol_stall = 1;  			else  				set_halt (ep); -		} else +			if (wedged) +				ep->wedged = 1; +		} else {  			clear_halt (ep); +			ep->wedged = 0; +		}  		(void) readl (&ep->regs->ep_rsp);  	}  	spin_unlock_irqrestore (&ep->dev->lock, flags); @@ -1257,6 +1263,20 @@ net2280_set_halt (struct usb_ep *_ep, int value)  }  static int +net2280_set_halt(struct usb_ep *_ep, int value) +{ +	return net2280_set_halt_and_wedge(_ep, value, 0); +} + +static int +net2280_set_wedge(struct usb_ep *_ep) +{ +	if (!_ep || _ep->name == ep0name) +		return -EINVAL; +	return net2280_set_halt_and_wedge(_ep, 1, 1); +} + +static int  net2280_fifo_status (struct usb_ep *_ep)  {  	struct net2280_ep	*ep; @@ -1302,6 +1322,7 @@ static const struct usb_ep_ops net2280_ep_ops = {  	.dequeue	= net2280_dequeue,  	.set_halt	= net2280_set_halt, +	.set_wedge	= net2280_set_wedge,  	.fifo_status	= net2280_fifo_status,  	.fifo_flush	= net2280_fifo_flush,  }; @@ -2410,9 +2431,14 @@ static void handle_stat0_irqs (struct net2280 *dev, u32 stat)  				goto do_stall;  			if ((e = get_ep_by_addr (dev, w_index)) == 0)  				goto do_stall; -			clear_halt (e); +			if (e->wedged) { +				VDEBUG(dev, "%s wedged, halt not cleared\n", +						ep->ep.name); +			} else { +				VDEBUG(dev, "%s clear halt\n", ep->ep.name); +				clear_halt(e); +			}  			allow_status (ep); -			VDEBUG (dev, "%s clear halt\n", ep->ep.name);  			goto next_endpoints;  			}  			break; @@ -2427,6 +2453,8 @@ static void handle_stat0_irqs (struct net2280 *dev, u32 stat)  				goto do_stall;  			if ((e = get_ep_by_addr (dev, w_index)) == 0)  				goto do_stall; +			if (e->ep.name == ep0name) +				goto do_stall;  			set_halt (e);  			allow_status (ep);  			VDEBUG (dev, "%s set halt\n", ep->ep.name); diff --git a/drivers/usb/gadget/net2280.h b/drivers/usb/gadget/net2280.h index 81a71dbdc2c..c36852263d9 100644 --- a/drivers/usb/gadget/net2280.h +++ b/drivers/usb/gadget/net2280.h @@ -109,6 +109,7 @@ struct net2280_ep {  						in_fifo_validate : 1,  						out_overflow : 1,  						stopped : 1, +						wedged : 1,  						is_in : 1,  						is_iso : 1,  						responded : 1; diff --git a/drivers/usb/gadget/omap_udc.c b/drivers/usb/gadget/omap_udc.c index bb54cca4c54..34e9e393f92 100644 --- a/drivers/usb/gadget/omap_udc.c +++ b/drivers/usb/gadget/omap_udc.c @@ -2313,6 +2313,13 @@ static int proc_otg_show(struct seq_file *s)  	tmp = omap_readl(OTG_REV);  	if (cpu_is_omap24xx()) { +		/* +		 * REVISIT: Not clear how this works on OMAP2.  trans +		 * is ANDed to produce bits 7 and 8, which might make +		 * sense for USB_TRANSCEIVER_CTRL on OMAP1, +		 * but with CONTROL_DEVCONF, these bits have something to +		 * do with the frame adjustment counter and McBSP2. +		 */  		ctrl_name = "control_devconf";  		trans = omap_ctrl_readl(OMAP2_CONTROL_DEVCONF0);  	} else { diff --git a/drivers/usb/gadget/printer.c b/drivers/usb/gadget/printer.c index e0090085b78..5a3034fdfe4 100644 --- a/drivers/usb/gadget/printer.c +++ b/drivers/usb/gadget/printer.c @@ -53,6 +53,20 @@  #include "gadget_chips.h" + +/* + * Kbuild is not very cooperative with respect to linking separately + * compiled library objects into one module.  So for now we won't use + * separate compilation ... ensuring init/exit sections work to shrink + * the runtime footprint, and giving us at least some parts of what + * a "gcc --combine ... part1.c part2.c part3.c ... " build would. + */ +#include "usbstring.c" +#include "config.c" +#include "epautoconf.c" + +/*-------------------------------------------------------------------------*/ +  #define DRIVER_DESC		"Printer Gadget"  #define DRIVER_VERSION		"2007 OCT 06" @@ -238,7 +252,7 @@ static struct usb_config_descriptor config_desc = {  	.bConfigurationValue =	DEV_CONFIG_VALUE,  	.iConfiguration =	0,  	.bmAttributes =		USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER, -	.bMaxPower =		1	/* Self-Powered */ +	.bMaxPower =		CONFIG_USB_GADGET_VBUS_DRAW / 2,  };  static struct usb_interface_descriptor intf_desc = { @@ -1264,8 +1278,7 @@ unknown:  	/* respond with data transfer before status phase? */  	if (value >= 0) {  		req->length = value; -		req->zero = value < wLength -				&& (value % gadget->ep0->maxpacket) == 0; +		req->zero = value < wLength;  		value = usb_ep_queue(gadget->ep0, req, GFP_ATOMIC);  		if (value < 0) {  			DBG(dev, "ep_queue --> %d\n", value); @@ -1360,8 +1373,8 @@ printer_bind(struct usb_gadget *gadget)  	/* Setup the sysfs files for the printer gadget. */ -	dev->pdev = device_create_drvdata(usb_gadget_class, NULL, -					  g_printer_devno, NULL, "g_printer"); +	dev->pdev = device_create(usb_gadget_class, NULL, g_printer_devno, +				  NULL, "g_printer");  	if (IS_ERR(dev->pdev)) {  		ERROR(dev, "Failed to create device: g_printer\n");  		goto fail; @@ -1463,7 +1476,6 @@ autoconf_fail:  	if (gadget->is_otg) {  		otg_desc.bmAttributes |= USB_OTG_HNP,  		config_desc.bmAttributes |= USB_CONFIG_ATT_WAKEUP; -		config_desc.bMaxPower = 4;  	}  	spin_lock_init(&dev->lock); diff --git a/drivers/usb/gadget/pxa27x_udc.c b/drivers/usb/gadget/pxa27x_udc.c index 7cbc78a6853..caa37c95802 100644 --- a/drivers/usb/gadget/pxa27x_udc.c +++ b/drivers/usb/gadget/pxa27x_udc.c @@ -22,7 +22,6 @@  #include <linux/module.h>  #include <linux/kernel.h>  #include <linux/types.h> -#include <linux/version.h>  #include <linux/errno.h>  #include <linux/platform_device.h>  #include <linux/delay.h> @@ -651,7 +650,7 @@ pxa_ep_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags)  	struct pxa27x_request *req;  	req = kzalloc(sizeof *req, gfp_flags); -	if (!req || !_ep) +	if (!req)  		return NULL;  	INIT_LIST_HEAD(&req->queue); diff --git a/drivers/usb/gadget/rndis.c b/drivers/usb/gadget/rndis.c index 7228e856223..8c26f5ea2b8 100644 --- a/drivers/usb/gadget/rndis.c +++ b/drivers/usb/gadget/rndis.c @@ -57,11 +57,6 @@ MODULE_PARM_DESC (rndis_debug, "enable debugging");  #define rndis_debug		0  #endif -#define DBG(str,args...) do { \ -	if (rndis_debug) \ -		pr_debug(str , ## args); \ -	} while (0) -  #define RNDIS_MAX_CONFIGS	1 @@ -183,9 +178,9 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len,  	if (!resp) return -ENOMEM;  	if (buf_len && rndis_debug > 1) { -		DBG("query OID %08x value, len %d:\n", OID, buf_len); +		pr_debug("query OID %08x value, len %d:\n", OID, buf_len);  		for (i = 0; i < buf_len; i += 16) { -			DBG("%03d: %08x %08x %08x %08x\n", i, +			pr_debug("%03d: %08x %08x %08x %08x\n", i,  				get_unaligned_le32(&buf[i]),  				get_unaligned_le32(&buf[i + 4]),  				get_unaligned_le32(&buf[i + 8]), @@ -209,7 +204,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len,  	/* mandatory */  	case OID_GEN_SUPPORTED_LIST: -		DBG("%s: OID_GEN_SUPPORTED_LIST\n", __func__); +		pr_debug("%s: OID_GEN_SUPPORTED_LIST\n", __func__);  		length = sizeof (oid_supported_list);  		count  = length / sizeof (u32);  		for (i = 0; i < count; i++) @@ -219,7 +214,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len,  	/* mandatory */  	case OID_GEN_HARDWARE_STATUS: -		DBG("%s: OID_GEN_HARDWARE_STATUS\n", __func__); +		pr_debug("%s: OID_GEN_HARDWARE_STATUS\n", __func__);  		/* Bogus question!  		 * Hardware must be ready to receive high level protocols.  		 * BTW: @@ -232,14 +227,14 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len,  	/* mandatory */  	case OID_GEN_MEDIA_SUPPORTED: -		DBG("%s: OID_GEN_MEDIA_SUPPORTED\n", __func__); +		pr_debug("%s: OID_GEN_MEDIA_SUPPORTED\n", __func__);  		*outbuf = cpu_to_le32 (rndis_per_dev_params [configNr].medium);  		retval = 0;  		break;  	/* mandatory */  	case OID_GEN_MEDIA_IN_USE: -		DBG("%s: OID_GEN_MEDIA_IN_USE\n", __func__); +		pr_debug("%s: OID_GEN_MEDIA_IN_USE\n", __func__);  		/* one medium, one transport... (maybe you do it better) */  		*outbuf = cpu_to_le32 (rndis_per_dev_params [configNr].medium);  		retval = 0; @@ -247,7 +242,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len,  	/* mandatory */  	case OID_GEN_MAXIMUM_FRAME_SIZE: -		DBG("%s: OID_GEN_MAXIMUM_FRAME_SIZE\n", __func__); +		pr_debug("%s: OID_GEN_MAXIMUM_FRAME_SIZE\n", __func__);  		if (rndis_per_dev_params [configNr].dev) {  			*outbuf = cpu_to_le32 (  				rndis_per_dev_params [configNr].dev->mtu); @@ -258,7 +253,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len,  	/* mandatory */  	case OID_GEN_LINK_SPEED:  		if (rndis_debug > 1) -			DBG("%s: OID_GEN_LINK_SPEED\n", __func__); +			pr_debug("%s: OID_GEN_LINK_SPEED\n", __func__);  		if (rndis_per_dev_params [configNr].media_state  				== NDIS_MEDIA_STATE_DISCONNECTED)  			*outbuf = __constant_cpu_to_le32 (0); @@ -270,7 +265,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len,  	/* mandatory */  	case OID_GEN_TRANSMIT_BLOCK_SIZE: -		DBG("%s: OID_GEN_TRANSMIT_BLOCK_SIZE\n", __func__); +		pr_debug("%s: OID_GEN_TRANSMIT_BLOCK_SIZE\n", __func__);  		if (rndis_per_dev_params [configNr].dev) {  			*outbuf = cpu_to_le32 (  				rndis_per_dev_params [configNr].dev->mtu); @@ -280,7 +275,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len,  	/* mandatory */  	case OID_GEN_RECEIVE_BLOCK_SIZE: -		DBG("%s: OID_GEN_RECEIVE_BLOCK_SIZE\n", __func__); +		pr_debug("%s: OID_GEN_RECEIVE_BLOCK_SIZE\n", __func__);  		if (rndis_per_dev_params [configNr].dev) {  			*outbuf = cpu_to_le32 (  				rndis_per_dev_params [configNr].dev->mtu); @@ -290,7 +285,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len,  	/* mandatory */  	case OID_GEN_VENDOR_ID: -		DBG("%s: OID_GEN_VENDOR_ID\n", __func__); +		pr_debug("%s: OID_GEN_VENDOR_ID\n", __func__);  		*outbuf = cpu_to_le32 (  			rndis_per_dev_params [configNr].vendorID);  		retval = 0; @@ -298,7 +293,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len,  	/* mandatory */  	case OID_GEN_VENDOR_DESCRIPTION: -		DBG("%s: OID_GEN_VENDOR_DESCRIPTION\n", __func__); +		pr_debug("%s: OID_GEN_VENDOR_DESCRIPTION\n", __func__);  		length = strlen (rndis_per_dev_params [configNr].vendorDescr);  		memcpy (outbuf,  			rndis_per_dev_params [configNr].vendorDescr, length); @@ -306,7 +301,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len,  		break;  	case OID_GEN_VENDOR_DRIVER_VERSION: -		DBG("%s: OID_GEN_VENDOR_DRIVER_VERSION\n", __func__); +		pr_debug("%s: OID_GEN_VENDOR_DRIVER_VERSION\n", __func__);  		/* Created as LE */  		*outbuf = rndis_driver_version;  		retval = 0; @@ -314,14 +309,14 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len,  	/* mandatory */  	case OID_GEN_CURRENT_PACKET_FILTER: -		DBG("%s: OID_GEN_CURRENT_PACKET_FILTER\n", __func__); +		pr_debug("%s: OID_GEN_CURRENT_PACKET_FILTER\n", __func__);  		*outbuf = cpu_to_le32 (*rndis_per_dev_params[configNr].filter);  		retval = 0;  		break;  	/* mandatory */  	case OID_GEN_MAXIMUM_TOTAL_SIZE: -		DBG("%s: OID_GEN_MAXIMUM_TOTAL_SIZE\n", __func__); +		pr_debug("%s: OID_GEN_MAXIMUM_TOTAL_SIZE\n", __func__);  		*outbuf = __constant_cpu_to_le32(RNDIS_MAX_TOTAL_SIZE);  		retval = 0;  		break; @@ -329,14 +324,14 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len,  	/* mandatory */  	case OID_GEN_MEDIA_CONNECT_STATUS:  		if (rndis_debug > 1) -			DBG("%s: OID_GEN_MEDIA_CONNECT_STATUS\n", __func__); +			pr_debug("%s: OID_GEN_MEDIA_CONNECT_STATUS\n", __func__);  		*outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]  						.media_state);  		retval = 0;  		break;  	case OID_GEN_PHYSICAL_MEDIUM: -		DBG("%s: OID_GEN_PHYSICAL_MEDIUM\n", __func__); +		pr_debug("%s: OID_GEN_PHYSICAL_MEDIUM\n", __func__);  		*outbuf = __constant_cpu_to_le32 (0);  		retval = 0;  		break; @@ -346,7 +341,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len,  	 * versions emit undefined RNDIS messages. DOCUMENT ALL THESE!  	 */  	case OID_GEN_MAC_OPTIONS:		/* from WinME */ -		DBG("%s: OID_GEN_MAC_OPTIONS\n", __func__); +		pr_debug("%s: OID_GEN_MAC_OPTIONS\n", __func__);  		*outbuf = __constant_cpu_to_le32(  			  NDIS_MAC_OPTION_RECEIVE_SERIALIZED  			| NDIS_MAC_OPTION_FULL_DUPLEX); @@ -358,7 +353,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len,  	/* mandatory */  	case OID_GEN_XMIT_OK:  		if (rndis_debug > 1) -			DBG("%s: OID_GEN_XMIT_OK\n", __func__); +			pr_debug("%s: OID_GEN_XMIT_OK\n", __func__);  		if (stats) {  			*outbuf = cpu_to_le32(stats->tx_packets  				- stats->tx_errors - stats->tx_dropped); @@ -369,7 +364,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len,  	/* mandatory */  	case OID_GEN_RCV_OK:  		if (rndis_debug > 1) -			DBG("%s: OID_GEN_RCV_OK\n", __func__); +			pr_debug("%s: OID_GEN_RCV_OK\n", __func__);  		if (stats) {  			*outbuf = cpu_to_le32(stats->rx_packets  				- stats->rx_errors - stats->rx_dropped); @@ -380,7 +375,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len,  	/* mandatory */  	case OID_GEN_XMIT_ERROR:  		if (rndis_debug > 1) -			DBG("%s: OID_GEN_XMIT_ERROR\n", __func__); +			pr_debug("%s: OID_GEN_XMIT_ERROR\n", __func__);  		if (stats) {  			*outbuf = cpu_to_le32(stats->tx_errors);  			retval = 0; @@ -390,7 +385,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len,  	/* mandatory */  	case OID_GEN_RCV_ERROR:  		if (rndis_debug > 1) -			DBG("%s: OID_GEN_RCV_ERROR\n", __func__); +			pr_debug("%s: OID_GEN_RCV_ERROR\n", __func__);  		if (stats) {  			*outbuf = cpu_to_le32(stats->rx_errors);  			retval = 0; @@ -399,7 +394,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len,  	/* mandatory */  	case OID_GEN_RCV_NO_BUFFER: -		DBG("%s: OID_GEN_RCV_NO_BUFFER\n", __func__); +		pr_debug("%s: OID_GEN_RCV_NO_BUFFER\n", __func__);  		if (stats) {  			*outbuf = cpu_to_le32(stats->rx_dropped);  			retval = 0; @@ -410,7 +405,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len,  	/* mandatory */  	case OID_802_3_PERMANENT_ADDRESS: -		DBG("%s: OID_802_3_PERMANENT_ADDRESS\n", __func__); +		pr_debug("%s: OID_802_3_PERMANENT_ADDRESS\n", __func__);  		if (rndis_per_dev_params [configNr].dev) {  			length = ETH_ALEN;  			memcpy (outbuf, @@ -422,7 +417,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len,  	/* mandatory */  	case OID_802_3_CURRENT_ADDRESS: -		DBG("%s: OID_802_3_CURRENT_ADDRESS\n", __func__); +		pr_debug("%s: OID_802_3_CURRENT_ADDRESS\n", __func__);  		if (rndis_per_dev_params [configNr].dev) {  			length = ETH_ALEN;  			memcpy (outbuf, @@ -434,7 +429,7 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len,  	/* mandatory */  	case OID_802_3_MULTICAST_LIST: -		DBG("%s: OID_802_3_MULTICAST_LIST\n", __func__); +		pr_debug("%s: OID_802_3_MULTICAST_LIST\n", __func__);  		/* Multicast base address only */  		*outbuf = __constant_cpu_to_le32 (0xE0000000);  		retval = 0; @@ -442,21 +437,21 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len,  	/* mandatory */  	case OID_802_3_MAXIMUM_LIST_SIZE: -		DBG("%s: OID_802_3_MAXIMUM_LIST_SIZE\n", __func__); +		pr_debug("%s: OID_802_3_MAXIMUM_LIST_SIZE\n", __func__);  		/* Multicast base address only */  		*outbuf = __constant_cpu_to_le32 (1);  		retval = 0;  		break;  	case OID_802_3_MAC_OPTIONS: -		DBG("%s: OID_802_3_MAC_OPTIONS\n", __func__); +		pr_debug("%s: OID_802_3_MAC_OPTIONS\n", __func__);  		break;  	/* ieee802.3 statistics OIDs (table 4-4) */  	/* mandatory */  	case OID_802_3_RCV_ERROR_ALIGNMENT: -		DBG("%s: OID_802_3_RCV_ERROR_ALIGNMENT\n", __func__); +		pr_debug("%s: OID_802_3_RCV_ERROR_ALIGNMENT\n", __func__);  		if (stats) {  			*outbuf = cpu_to_le32(stats->rx_frame_errors);  			retval = 0; @@ -465,14 +460,14 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len,  	/* mandatory */  	case OID_802_3_XMIT_ONE_COLLISION: -		DBG("%s: OID_802_3_XMIT_ONE_COLLISION\n", __func__); +		pr_debug("%s: OID_802_3_XMIT_ONE_COLLISION\n", __func__);  		*outbuf = __constant_cpu_to_le32 (0);  		retval = 0;  		break;  	/* mandatory */  	case OID_802_3_XMIT_MORE_COLLISIONS: -		DBG("%s: OID_802_3_XMIT_MORE_COLLISIONS\n", __func__); +		pr_debug("%s: OID_802_3_XMIT_MORE_COLLISIONS\n", __func__);  		*outbuf = __constant_cpu_to_le32 (0);  		retval = 0;  		break; @@ -504,9 +499,9 @@ static int gen_ndis_set_resp (u8 configNr, u32 OID, u8 *buf, u32 buf_len,  		return -ENOMEM;  	if (buf_len && rndis_debug > 1) { -		DBG("set OID %08x value, len %d:\n", OID, buf_len); +		pr_debug("set OID %08x value, len %d:\n", OID, buf_len);  		for (i = 0; i < buf_len; i += 16) { -			DBG("%03d: %08x %08x %08x %08x\n", i, +			pr_debug("%03d: %08x %08x %08x %08x\n", i,  				get_unaligned_le32(&buf[i]),  				get_unaligned_le32(&buf[i + 4]),  				get_unaligned_le32(&buf[i + 8]), @@ -525,7 +520,7 @@ static int gen_ndis_set_resp (u8 configNr, u32 OID, u8 *buf, u32 buf_len,  		 *	MULTICAST, ALL_MULTICAST, BROADCAST  		 */  		*params->filter = (u16)get_unaligned_le32(buf); -		DBG("%s: OID_GEN_CURRENT_PACKET_FILTER %08x\n", +		pr_debug("%s: OID_GEN_CURRENT_PACKET_FILTER %08x\n",  			__func__, *params->filter);  		/* this call has a significant side effect:  it's @@ -547,7 +542,7 @@ static int gen_ndis_set_resp (u8 configNr, u32 OID, u8 *buf, u32 buf_len,  	case OID_802_3_MULTICAST_LIST:  		/* I think we can ignore this */ -		DBG("%s: OID_802_3_MULTICAST_LIST\n", __func__); +		pr_debug("%s: OID_802_3_MULTICAST_LIST\n", __func__);  		retval = 0;  		break; @@ -606,7 +601,7 @@ static int rndis_query_response (int configNr, rndis_query_msg_type *buf)  	rndis_resp_t            *r;  	struct rndis_params	*params = rndis_per_dev_params + configNr; -	// DBG("%s: OID = %08X\n", __func__, cpu_to_le32(buf->OID)); +	/* pr_debug("%s: OID = %08X\n", __func__, cpu_to_le32(buf->OID)); */  	if (!params->dev)  		return -ENOTSUPP; @@ -659,15 +654,15 @@ static int rndis_set_response (int configNr, rndis_set_msg_type *buf)  	BufOffset = le32_to_cpu (buf->InformationBufferOffset);  #ifdef	VERBOSE_DEBUG -	DBG("%s: Length: %d\n", __func__, BufLength); -	DBG("%s: Offset: %d\n", __func__, BufOffset); -	DBG("%s: InfoBuffer: ", __func__); +	pr_debug("%s: Length: %d\n", __func__, BufLength); +	pr_debug("%s: Offset: %d\n", __func__, BufOffset); +	pr_debug("%s: InfoBuffer: ", __func__);  	for (i = 0; i < BufLength; i++) { -		DBG("%02x ", *(((u8 *) buf) + i + 8 + BufOffset)); +		pr_debug("%02x ", *(((u8 *) buf) + i + 8 + BufOffset));  	} -	DBG("\n"); +	pr_debug("\n");  #endif  	resp->MessageType = __constant_cpu_to_le32 (REMOTE_NDIS_SET_CMPLT); @@ -821,14 +816,14 @@ int rndis_msg_parser (u8 configNr, u8 *buf)  	/* For USB: responses may take up to 10 seconds */  	switch (MsgType) {  	case REMOTE_NDIS_INITIALIZE_MSG: -		DBG("%s: REMOTE_NDIS_INITIALIZE_MSG\n", +		pr_debug("%s: REMOTE_NDIS_INITIALIZE_MSG\n",  			__func__ );  		params->state = RNDIS_INITIALIZED;  		return  rndis_init_response (configNr,  					(rndis_init_msg_type *) buf);  	case REMOTE_NDIS_HALT_MSG: -		DBG("%s: REMOTE_NDIS_HALT_MSG\n", +		pr_debug("%s: REMOTE_NDIS_HALT_MSG\n",  			__func__ );  		params->state = RNDIS_UNINITIALIZED;  		if (params->dev) { @@ -846,7 +841,7 @@ int rndis_msg_parser (u8 configNr, u8 *buf)  					(rndis_set_msg_type *) buf);  	case REMOTE_NDIS_RESET_MSG: -		DBG("%s: REMOTE_NDIS_RESET_MSG\n", +		pr_debug("%s: REMOTE_NDIS_RESET_MSG\n",  			__func__ );  		return rndis_reset_response (configNr,  					(rndis_reset_msg_type *) buf); @@ -854,7 +849,7 @@ int rndis_msg_parser (u8 configNr, u8 *buf)  	case REMOTE_NDIS_KEEPALIVE_MSG:  		/* For USB: host does this every 5 seconds */  		if (rndis_debug > 1) -			DBG("%s: REMOTE_NDIS_KEEPALIVE_MSG\n", +			pr_debug("%s: REMOTE_NDIS_KEEPALIVE_MSG\n",  				__func__ );  		return rndis_keepalive_response (configNr,  						 (rndis_keepalive_msg_type *) @@ -870,7 +865,7 @@ int rndis_msg_parser (u8 configNr, u8 *buf)  		{  			unsigned i;  			for (i = 0; i < MsgLength; i += 16) { -				DBG("%03d: " +				pr_debug("%03d: "  					" %02x %02x %02x %02x"  					" %02x %02x %02x %02x"  					" %02x %02x %02x %02x" @@ -905,18 +900,18 @@ int rndis_register(void (*resp_avail)(void *v), void *v)  			rndis_per_dev_params [i].used = 1;  			rndis_per_dev_params [i].resp_avail = resp_avail;  			rndis_per_dev_params [i].v = v; -			DBG("%s: configNr = %d\n", __func__, i); +			pr_debug("%s: configNr = %d\n", __func__, i);  			return i;  		}  	} -	DBG("failed\n"); +	pr_debug("failed\n");  	return -ENODEV;  }  void rndis_deregister (int configNr)  { -	DBG("%s: \n", __func__ ); +	pr_debug("%s: \n", __func__);  	if (configNr >= RNDIS_MAX_CONFIGS) return;  	rndis_per_dev_params [configNr].used = 0; @@ -926,7 +921,7 @@ void rndis_deregister (int configNr)  int rndis_set_param_dev(u8 configNr, struct net_device *dev, u16 *cdc_filter)  { -	DBG("%s:\n", __func__ ); +	pr_debug("%s:\n", __func__);  	if (!dev)  		return -EINVAL;  	if (configNr >= RNDIS_MAX_CONFIGS) return -1; @@ -939,7 +934,7 @@ int rndis_set_param_dev(u8 configNr, struct net_device *dev, u16 *cdc_filter)  int rndis_set_param_vendor (u8 configNr, u32 vendorID, const char *vendorDescr)  { -	DBG("%s:\n", __func__ ); +	pr_debug("%s:\n", __func__);  	if (!vendorDescr) return -1;  	if (configNr >= RNDIS_MAX_CONFIGS) return -1; @@ -951,7 +946,7 @@ int rndis_set_param_vendor (u8 configNr, u32 vendorID, const char *vendorDescr)  int rndis_set_param_medium (u8 configNr, u32 medium, u32 speed)  { -	DBG("%s: %u %u\n", __func__, medium, speed); +	pr_debug("%s: %u %u\n", __func__, medium, speed);  	if (configNr >= RNDIS_MAX_CONFIGS) return -1;  	rndis_per_dev_params [configNr].medium = medium; @@ -1114,7 +1109,7 @@ static ssize_t rndis_proc_write(struct file *file, const char __user *buffer,  			break;  		default:  			if (fl_speed) p->speed = speed; -			else DBG("%c is not valid\n", c); +			else pr_debug("%c is not valid\n", c);  			break;  		} @@ -1159,12 +1154,12 @@ int __init rndis_init (void)  					&rndis_proc_fops,  					(void *)(rndis_per_dev_params + i))))  		{ -			DBG("%s :remove entries", __func__); +			pr_debug("%s :remove entries", __func__);  			while (i) {  				sprintf (name, NAME_TEMPLATE, --i);  				remove_proc_entry (name, NULL);  			} -			DBG("\n"); +			pr_debug("\n");  			return -EIO;  		}  #endif diff --git a/drivers/usb/gadget/s3c2410_udc.c b/drivers/usb/gadget/s3c2410_udc.c index 29d13ebe750..00ba06b4475 100644 --- a/drivers/usb/gadget/s3c2410_udc.c +++ b/drivers/usb/gadget/s3c2410_udc.c @@ -1651,7 +1651,7 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver)  		return -EBUSY;  	if (!driver->bind || !driver->setup -			|| driver->speed != USB_SPEED_FULL) { +			|| driver->speed < USB_SPEED_FULL) {  		printk(KERN_ERR "Invalid driver: bind %p setup %p speed %d\n",  			driver->bind, driver->setup, driver->speed);  		return -EINVAL; @@ -1894,11 +1894,8 @@ static int s3c2410_udc_probe(struct platform_device *pdev)  		udc->regs_info = debugfs_create_file("registers", S_IRUGO,  				s3c2410_udc_debugfs_root,  				udc, &s3c2410_udc_debugfs_fops); -		if (IS_ERR(udc->regs_info)) { -			dev_warn(dev, "debugfs file creation failed %ld\n", -				 PTR_ERR(udc->regs_info)); -			udc->regs_info = NULL; -		} +		if (!udc->regs_info) +			dev_warn(dev, "debugfs file creation failed\n");  	}  	dev_dbg(dev, "probe ok\n"); diff --git a/drivers/usb/gadget/serial.c b/drivers/usb/gadget/serial.c index b3699afff00..37879af1c43 100644 --- a/drivers/usb/gadget/serial.c +++ b/drivers/usb/gadget/serial.c @@ -30,6 +30,25 @@  /*-------------------------------------------------------------------------*/ +/* + * Kbuild is not very cooperative with respect to linking separately + * compiled library objects into one module.  So for now we won't use + * separate compilation ... ensuring init/exit sections work to shrink + * the runtime footprint, and giving us at least some parts of what + * a "gcc --combine ... part1.c part2.c part3.c ... " build would. + */ +#include "composite.c" +#include "usbstring.c" +#include "config.c" +#include "epautoconf.c" + +#include "f_acm.c" +#include "f_obex.c" +#include "f_serial.c" +#include "u_serial.c" + +/*-------------------------------------------------------------------------*/ +  /* Thanks to NetChip Technologies for donating this product ID.  *  * DO NOT REUSE THESE IDs with a protocol-incompatible driver!!  Ever!! @@ -38,6 +57,7 @@  #define GS_VENDOR_ID			0x0525	/* NetChip */  #define GS_PRODUCT_ID			0xa4a6	/* Linux-USB Serial Gadget */  #define GS_CDC_PRODUCT_ID		0xa4a7	/* ... as CDC-ACM */ +#define GS_CDC_OBEX_PRODUCT_ID		0xa4a9	/* ... as CDC-OBEX */  /* string IDs are assigned dynamically */ @@ -107,6 +127,10 @@ static int use_acm = true;  module_param(use_acm, bool, 0);  MODULE_PARM_DESC(use_acm, "Use CDC ACM, default=yes"); +static int use_obex = false; +module_param(use_obex, bool, 0); +MODULE_PARM_DESC(use_obex, "Use CDC OBEX, default=no"); +  static unsigned n_ports = 1;  module_param(n_ports, uint, 0);  MODULE_PARM_DESC(n_ports, "number of ports to create, default=1"); @@ -121,6 +145,8 @@ static int __init serial_bind_config(struct usb_configuration *c)  	for (i = 0; i < n_ports && status == 0; i++) {  		if (use_acm)  			status = acm_bind_config(c, i); +		else if (use_obex) +			status = obex_bind_config(c, i);  		else  			status = gser_bind_config(c, i);  	} @@ -133,7 +159,6 @@ static struct usb_configuration serial_config_driver = {  	/* .bConfigurationValue = f(use_acm) */  	/* .iConfiguration = DYNAMIC */  	.bmAttributes	= USB_CONFIG_ATT_SELFPOWER, -	.bMaxPower	= 1,	/* 2 mA, minimal */  };  static int __init gs_bind(struct usb_composite_dev *cdev) @@ -231,6 +256,12 @@ static int __init init(void)  		device_desc.bDeviceClass = USB_CLASS_COMM;  		device_desc.idProduct =  				__constant_cpu_to_le16(GS_CDC_PRODUCT_ID); +	} else if (use_obex) { +		serial_config_driver.label = "CDC OBEX config"; +		serial_config_driver.bConfigurationValue = 3; +		device_desc.bDeviceClass = USB_CLASS_COMM; +		device_desc.idProduct = +			__constant_cpu_to_le16(GS_CDC_OBEX_PRODUCT_ID);  	} else {  		serial_config_driver.label = "Generic Serial config";  		serial_config_driver.bConfigurationValue = 1; diff --git a/drivers/usb/gadget/u_ether.c b/drivers/usb/gadget/u_ether.c index 3791e627190..66948b72bb9 100644 --- a/drivers/usb/gadget/u_ether.c +++ b/drivers/usb/gadget/u_ether.c @@ -52,7 +52,7 @@   * this single "physical" link to be used by multiple virtual links.)   */ -#define DRIVER_VERSION	"29-May-2008" +#define UETH__VERSION	"29-May-2008"  struct eth_dev {  	/* lock is held while accessing port_usb @@ -170,7 +170,7 @@ static void eth_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *p)  	struct eth_dev	*dev = netdev_priv(net);  	strlcpy(p->driver, "g_ether", sizeof p->driver); -	strlcpy(p->version, DRIVER_VERSION, sizeof p->version); +	strlcpy(p->version, UETH__VERSION, sizeof p->version);  	strlcpy(p->fw_version, dev->gadget->name, sizeof p->fw_version);  	strlcpy(p->bus_info, dev_name(&dev->gadget->dev), sizeof p->bus_info);  } @@ -873,6 +873,13 @@ struct net_device *gether_connect(struct gether *link)  		spin_lock(&dev->lock);  		dev->port_usb = link;  		link->ioport = dev; +		if (netif_running(dev->net)) { +			if (link->open) +				link->open(link); +		} else { +			if (link->close) +				link->close(link); +		}  		spin_unlock(&dev->lock);  		netif_carrier_on(dev->net); diff --git a/drivers/usb/gadget/u_serial.h b/drivers/usb/gadget/u_serial.h index af3910d01ae..300f0ed9475 100644 --- a/drivers/usb/gadget/u_serial.h +++ b/drivers/usb/gadget/u_serial.h @@ -62,5 +62,6 @@ void gserial_disconnect(struct gserial *);  /* functions are bound to configurations by a config or gadget driver */  int acm_bind_config(struct usb_configuration *c, u8 port_num);  int gser_bind_config(struct usb_configuration *c, u8 port_num); +int obex_bind_config(struct usb_configuration *c, u8 port_num);  #endif /* __U_SERIAL_H */ diff --git a/drivers/usb/gadget/zero.c b/drivers/usb/gadget/zero.c index aa0bd4f126a..361d9659ac4 100644 --- a/drivers/usb/gadget/zero.c +++ b/drivers/usb/gadget/zero.c @@ -59,6 +59,23 @@  /*-------------------------------------------------------------------------*/ +/* + * Kbuild is not very cooperative with respect to linking separately + * compiled library objects into one module.  So for now we won't use + * separate compilation ... ensuring init/exit sections work to shrink + * the runtime footprint, and giving us at least some parts of what + * a "gcc --combine ... part1.c part2.c part3.c ... " build would. + */ +#include "composite.c" +#include "usbstring.c" +#include "config.c" +#include "epautoconf.c" + +#include "f_sourcesink.c" +#include "f_loopback.c" + +/*-------------------------------------------------------------------------*/ +  #define DRIVER_VERSION		"Cinco de Mayo 2008"  static const char longname[] = "Gadget Zero"; diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 228797e54f9..f3a75a929e0 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -110,35 +110,23 @@ config USB_ISP116X_HCD  config USB_ISP1760_HCD  	tristate "ISP 1760 HCD support" -	depends on USB && EXPERIMENTAL +	depends on USB && EXPERIMENTAL && (PCI || PPC_OF)  	---help---  	  The ISP1760 chip is a USB 2.0 host controller.  	  This driver does not support isochronous transfers or OTG. +	  This USB controller is usually attached to a non-DMA-Master +	  capable bus. NXP's eval kit brings this chip on PCI card +	  where the chip itself is behind a PLB to simulate such +	  a bus.  	  To compile this driver as a module, choose M here: the -	  module will be called isp1760-hcd. - -config USB_ISP1760_PCI -	bool "Support for the PCI bus" -	depends on USB_ISP1760_HCD && PCI -	---help--- -	  Enables support for the device present on the PCI bus. -	  This should only be required if you happen to have the eval kit from -	  NXP and you are going to test it. - -config USB_ISP1760_OF -	bool "Support for the OF platform bus" -	depends on USB_ISP1760_HCD && PPC_OF -	---help--- -	  Enables support for the device present on the PowerPC -	  OpenFirmware platform bus. +	  module will be called isp1760.  config USB_OHCI_HCD  	tristate "OHCI HCD support"  	depends on USB && USB_ARCH_HAS_OHCI  	select ISP1301_OMAP if MACH_OMAP_H2 || MACH_OMAP_H3 -	select I2C if ARCH_PNX4008  	---help---  	  The Open Host Controller Interface (OHCI) is a standard for accessing  	  USB 1.1 host controller hardware.  It does more in hardware than Intel's @@ -305,3 +293,31 @@ config SUPERH_ON_CHIP_R8A66597  	help  	   This driver enables support for the on-chip R8A66597 in the  	   SH7366 and SH7723 processors. + +config USB_WHCI_HCD +	tristate "Wireless USB Host Controller Interface (WHCI) driver (EXPERIMENTAL)" +	depends on EXPERIMENTAL +	depends on PCI && USB +	select USB_WUSB +	select UWB_WHCI +	help +	  A driver for PCI-based Wireless USB Host Controllers that are +	  compliant with the WHCI specification. + +	  To compile this driver a module, choose M here: the module +	  will be called "whci-hcd". + +config USB_HWA_HCD +	tristate "Host Wire Adapter (HWA) driver (EXPERIMENTAL)" +	depends on EXPERIMENTAL +	depends on USB +	select USB_WUSB +	select UWB_HWA +	help +	  This driver enables you to connect Wireless USB devices to +	  your system using a Host Wire Adaptor USB dongle. This is an +	  UWB Radio Controller and WUSB Host Controller connected to +	  your machine via USB (specified in WUSB1.0). + +	  To compile this driver a module, choose M here: the module +	  will be called "hwa-hc". diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index f1edda2dcfd..23be2222404 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -8,6 +8,8 @@ endif  isp1760-objs := isp1760-hcd.o isp1760-if.o +obj-$(CONFIG_USB_WHCI_HCD)	+= whci/ +  obj-$(CONFIG_PCI)		+= pci-quirks.o  obj-$(CONFIG_USB_EHCI_HCD)	+= ehci-hcd.o @@ -19,3 +21,4 @@ obj-$(CONFIG_USB_SL811_CS)	+= sl811_cs.o  obj-$(CONFIG_USB_U132_HCD)	+= u132-hcd.o  obj-$(CONFIG_USB_R8A66597_HCD)	+= r8a66597-hcd.o  obj-$(CONFIG_USB_ISP1760_HCD)	+= isp1760.o +obj-$(CONFIG_USB_HWA_HCD)	+= hwa-hc.o diff --git a/drivers/usb/host/ehci-dbg.c b/drivers/usb/host/ehci-dbg.c index b0f8ed5a7fb..0cb53ca8d34 100644 --- a/drivers/usb/host/ehci-dbg.c +++ b/drivers/usb/host/ehci-dbg.c @@ -358,7 +358,8 @@ struct debug_buffer {  	struct usb_bus *bus;  	struct mutex mutex;	/* protect filling of buffer */  	size_t count;		/* number of characters filled into buffer */ -	char *page; +	char *output_buf; +	size_t alloc_size;  };  #define speed_char(info1) ({ char tmp; \ @@ -488,8 +489,8 @@ static ssize_t fill_async_buffer(struct debug_buffer *buf)  	hcd = bus_to_hcd(buf->bus);  	ehci = hcd_to_ehci (hcd); -	next = buf->page; -	size = PAGE_SIZE; +	next = buf->output_buf; +	size = buf->alloc_size;  	*next = 0; @@ -510,7 +511,7 @@ static ssize_t fill_async_buffer(struct debug_buffer *buf)  	}  	spin_unlock_irqrestore (&ehci->lock, flags); -	return strlen(buf->page); +	return strlen(buf->output_buf);  }  #define DBG_SCHED_LIMIT 64 @@ -531,8 +532,8 @@ static ssize_t fill_periodic_buffer(struct debug_buffer *buf)  	hcd = bus_to_hcd(buf->bus);  	ehci = hcd_to_ehci (hcd); -	next = buf->page; -	size = PAGE_SIZE; +	next = buf->output_buf; +	size = buf->alloc_size;  	temp = scnprintf (next, size, "size = %d\n", ehci->periodic_size);  	size -= temp; @@ -568,14 +569,16 @@ static ssize_t fill_periodic_buffer(struct debug_buffer *buf)  				for (temp = 0; temp < seen_count; temp++) {  					if (seen [temp].ptr != p.ptr)  						continue; -					if (p.qh->qh_next.ptr) +					if (p.qh->qh_next.ptr) {  						temp = scnprintf (next, size,  							" ..."); -					p.ptr = NULL; +						size -= temp; +						next += temp; +					}  					break;  				}  				/* show more info the first time around */ -				if (temp == seen_count && p.ptr) { +				if (temp == seen_count) {  					u32	scratch = hc32_to_cpup(ehci,  							&p.qh->hw_info1);  					struct ehci_qtd	*qtd; @@ -649,7 +652,7 @@ static ssize_t fill_periodic_buffer(struct debug_buffer *buf)  	spin_unlock_irqrestore (&ehci->lock, flags);  	kfree (seen); -	return PAGE_SIZE - size; +	return buf->alloc_size - size;  }  #undef DBG_SCHED_LIMIT @@ -665,14 +668,14 @@ static ssize_t fill_registers_buffer(struct debug_buffer *buf)  	hcd = bus_to_hcd(buf->bus);  	ehci = hcd_to_ehci (hcd); -	next = buf->page; -	size = PAGE_SIZE; +	next = buf->output_buf; +	size = buf->alloc_size;  	spin_lock_irqsave (&ehci->lock, flags);  	if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) {  		size = scnprintf (next, size, -			"bus %s, device %s (driver " DRIVER_VERSION ")\n" +			"bus %s, device %s\n"  			"%s\n"  			"SUSPENDED (no register access)\n",  			hcd->self.controller->bus->name, @@ -684,7 +687,7 @@ static ssize_t fill_registers_buffer(struct debug_buffer *buf)  	/* Capability Registers */  	i = HC_VERSION(ehci_readl(ehci, &ehci->caps->hc_capbase));  	temp = scnprintf (next, size, -		"bus %s, device %s (driver " DRIVER_VERSION ")\n" +		"bus %s, device %s\n"  		"%s\n"  		"EHCI %x.%02x, hcd state %d\n",  		hcd->self.controller->bus->name, @@ -808,7 +811,7 @@ static ssize_t fill_registers_buffer(struct debug_buffer *buf)  done:  	spin_unlock_irqrestore (&ehci->lock, flags); -	return PAGE_SIZE - size; +	return buf->alloc_size - size;  }  static struct debug_buffer *alloc_buffer(struct usb_bus *bus, @@ -822,6 +825,7 @@ static struct debug_buffer *alloc_buffer(struct usb_bus *bus,  		buf->bus = bus;  		buf->fill_func = fill_func;  		mutex_init(&buf->mutex); +		buf->alloc_size = PAGE_SIZE;  	}  	return buf; @@ -831,10 +835,10 @@ static int fill_buffer(struct debug_buffer *buf)  {  	int ret = 0; -	if (!buf->page) -		buf->page = (char *)get_zeroed_page(GFP_KERNEL); +	if (!buf->output_buf) +		buf->output_buf = (char *)vmalloc(buf->alloc_size); -	if (!buf->page) { +	if (!buf->output_buf) {  		ret = -ENOMEM;  		goto out;  	} @@ -867,7 +871,7 @@ static ssize_t debug_output(struct file *file, char __user *user_buf,  	mutex_unlock(&buf->mutex);  	ret = simple_read_from_buffer(user_buf, len, offset, -				      buf->page, buf->count); +				      buf->output_buf, buf->count);  out:  	return ret; @@ -879,8 +883,8 @@ static int debug_close(struct inode *inode, struct file *file)  	struct debug_buffer *buf = file->private_data;  	if (buf) { -		if (buf->page) -			free_page((unsigned long)buf->page); +		if (buf->output_buf) +			vfree(buf->output_buf);  		kfree(buf);  	} @@ -895,10 +899,14 @@ static int debug_async_open(struct inode *inode, struct file *file)  static int debug_periodic_open(struct inode *inode, struct file *file)  { -	file->private_data = alloc_buffer(inode->i_private, -					  fill_periodic_buffer); +	struct debug_buffer *buf; +	buf = alloc_buffer(inode->i_private, fill_periodic_buffer); +	if (!buf) +		return -ENOMEM; -	return file->private_data ? 0 : -ENOMEM; +	buf->alloc_size = (sizeof(void *) == 4 ? 6 : 8)*PAGE_SIZE; +	file->private_data = buf; +	return 0;  }  static int debug_registers_open(struct inode *inode, struct file *file) diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 8409e0705d6..4725d15d096 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -24,6 +24,7 @@  #include <linux/ioport.h>  #include <linux/sched.h>  #include <linux/slab.h> +#include <linux/vmalloc.h>  #include <linux/errno.h>  #include <linux/init.h>  #include <linux/timer.h> @@ -59,7 +60,6 @@   * providing early devices for those host controllers to talk to!   */ -#define DRIVER_VERSION "10 Dec 2004"  #define DRIVER_AUTHOR "David Brownell"  #define DRIVER_DESC "USB 2.0 'Enhanced' Host Controller (EHCI) Driver" @@ -620,9 +620,9 @@ static int ehci_run (struct usb_hcd *hcd)  	temp = HC_VERSION(ehci_readl(ehci, &ehci->caps->hc_capbase));  	ehci_info (ehci, -		"USB %x.%x started, EHCI %x.%02x, driver %s%s\n", +		"USB %x.%x started, EHCI %x.%02x%s\n",  		((ehci->sbrn & 0xf0)>>4), (ehci->sbrn & 0x0f), -		temp >> 8, temp & 0xff, DRIVER_VERSION, +		temp >> 8, temp & 0xff,  		ignore_oc ? ", overcurrent ignored" : "");  	ehci_writel(ehci, INTR_MASK, @@ -643,7 +643,7 @@ static int ehci_run (struct usb_hcd *hcd)  static irqreturn_t ehci_irq (struct usb_hcd *hcd)  {  	struct ehci_hcd		*ehci = hcd_to_ehci (hcd); -	u32			status, pcd_status = 0, cmd; +	u32			status, masked_status, pcd_status = 0, cmd;  	int			bh;  	spin_lock (&ehci->lock); @@ -656,14 +656,14 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd)  		goto dead;  	} -	status &= INTR_MASK; -	if (!status) {			/* irq sharing? */ +	masked_status = status & INTR_MASK; +	if (!masked_status) {		/* irq sharing? */  		spin_unlock(&ehci->lock);  		return IRQ_NONE;  	}  	/* clear (just) interrupts */ -	ehci_writel(ehci, status, &ehci->regs->status); +	ehci_writel(ehci, masked_status, &ehci->regs->status);  	cmd = ehci_readl(ehci, &ehci->regs->command);  	bh = 0; @@ -706,7 +706,7 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd)  		pcd_status = status;  		/* resume root hub? */ -		if (!(ehci_readl(ehci, &ehci->regs->command) & CMD_RUN)) +		if (!(cmd & CMD_RUN))  			usb_hcd_resume_root_hub(hcd);  		while (i--) { @@ -715,8 +715,11 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd)  			if (pstatus & PORT_OWNER)  				continue; -			if (!(pstatus & PORT_RESUME) -					|| ehci->reset_done [i] != 0) +			if (!(test_bit(i, &ehci->suspended_ports) && +					((pstatus & PORT_RESUME) || +						!(pstatus & PORT_SUSPEND)) && +					(pstatus & PORT_PE) && +					ehci->reset_done[i] == 0))  				continue;  			/* start 20 msec resume signaling from this port, @@ -731,19 +734,17 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd)  	/* PCI errors [4.15.2.4] */  	if (unlikely ((status & STS_FATAL) != 0)) { -		dbg_cmd (ehci, "fatal", ehci_readl(ehci, -						   &ehci->regs->command)); -		dbg_status (ehci, "fatal", status); -		if (status & STS_HALT) { -			ehci_err (ehci, "fatal error\n"); +		ehci_err(ehci, "fatal error\n"); +		dbg_cmd(ehci, "fatal", cmd); +		dbg_status(ehci, "fatal", status); +		ehci_halt(ehci);  dead: -			ehci_reset (ehci); -			ehci_writel(ehci, 0, &ehci->regs->configured_flag); -			/* generic layer kills/unlinks all urbs, then -			 * uses ehci_stop to clean up the rest -			 */ -			bh = 1; -		} +		ehci_reset(ehci); +		ehci_writel(ehci, 0, &ehci->regs->configured_flag); +		/* generic layer kills/unlinks all urbs, then +		 * uses ehci_stop to clean up the rest +		 */ +		bh = 1;  	}  	if (bh) @@ -994,9 +995,7 @@ static int ehci_get_frame (struct usb_hcd *hcd)  /*-------------------------------------------------------------------------*/ -#define DRIVER_INFO DRIVER_VERSION " " DRIVER_DESC - -MODULE_DESCRIPTION (DRIVER_INFO); +MODULE_DESCRIPTION(DRIVER_DESC);  MODULE_AUTHOR (DRIVER_AUTHOR);  MODULE_LICENSE ("GPL"); @@ -1020,11 +1019,6 @@ MODULE_LICENSE ("GPL");  #define	PS3_SYSTEM_BUS_DRIVER	ps3_ehci_driver  #endif -#if defined(CONFIG_440EPX) && !defined(CONFIG_PPC_MERGE) -#include "ehci-ppc-soc.c" -#define	PLATFORM_DRIVER		ehci_ppc_soc_driver -#endif -  #ifdef CONFIG_USB_EHCI_HCD_PPC_OF  #include "ehci-ppc-of.c"  #define OF_PLATFORM_DRIVER	ehci_hcd_ppc_of_driver @@ -1049,6 +1043,16 @@ static int __init ehci_hcd_init(void)  {  	int retval = 0; +	if (usb_disabled()) +		return -ENODEV; + +	printk(KERN_INFO "%s: " DRIVER_DESC "\n", hcd_name); +	set_bit(USB_EHCI_LOADED, &usb_hcds_loaded); +	if (test_bit(USB_UHCI_LOADED, &usb_hcds_loaded) || +			test_bit(USB_OHCI_LOADED, &usb_hcds_loaded)) +		printk(KERN_WARNING "Warning! ehci_hcd should always be loaded" +				" before uhci_hcd and ohci_hcd, not after\n"); +  	pr_debug("%s: block sizes: qh %Zd qtd %Zd itd %Zd sitd %Zd\n",  		 hcd_name,  		 sizeof(struct ehci_qh), sizeof(struct ehci_qtd), @@ -1056,8 +1060,10 @@ static int __init ehci_hcd_init(void)  #ifdef DEBUG  	ehci_debug_root = debugfs_create_dir("ehci", NULL); -	if (!ehci_debug_root) -		return -ENOENT; +	if (!ehci_debug_root) { +		retval = -ENOENT; +		goto err_debug; +	}  #endif  #ifdef PLATFORM_DRIVER @@ -1104,7 +1110,9 @@ clean0:  #ifdef DEBUG  	debugfs_remove(ehci_debug_root);  	ehci_debug_root = NULL; +err_debug:  #endif +	clear_bit(USB_EHCI_LOADED, &usb_hcds_loaded);  	return retval;  }  module_init(ehci_hcd_init); @@ -1126,6 +1134,7 @@ static void __exit ehci_hcd_cleanup(void)  #ifdef DEBUG  	debugfs_remove(ehci_debug_root);  #endif +	clear_bit(USB_EHCI_LOADED, &usb_hcds_loaded);  }  module_exit(ehci_hcd_cleanup); diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index 740835bb857..218f9660d7e 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -236,10 +236,8 @@ static int ehci_bus_resume (struct usb_hcd *hcd)  		temp = ehci_readl(ehci, &ehci->regs->port_status [i]);  		temp &= ~(PORT_RWC_BITS | PORT_WAKE_BITS);  		if (test_bit(i, &ehci->bus_suspended) && -				(temp & PORT_SUSPEND)) { -			ehci->reset_done [i] = jiffies + msecs_to_jiffies (20); +				(temp & PORT_SUSPEND))  			temp |= PORT_RESUME; -		}  		ehci_writel(ehci, temp, &ehci->regs->port_status [i]);  	}  	i = HCS_N_PORTS (ehci->hcs_params); @@ -482,10 +480,9 @@ ehci_hub_status_data (struct usb_hcd *hcd, char *buf)  		 * controller by the user.  		 */ -		if ((temp & mask) != 0 -				|| ((temp & PORT_RESUME) != 0 -					&& time_after_eq(jiffies, -						ehci->reset_done[i]))) { +		if ((temp & mask) != 0 || test_bit(i, &ehci->port_c_suspend) +				|| (ehci->reset_done[i] && time_after_eq( +					jiffies, ehci->reset_done[i]))) {  			if (i < 7)  			    buf [0] |= 1 << (i + 1);  			else @@ -688,6 +685,7 @@ static int ehci_hub_control (  			/* resume completed? */  			else if (time_after_eq(jiffies,  					ehci->reset_done[wIndex])) { +				clear_bit(wIndex, &ehci->suspended_ports);  				set_bit(wIndex, &ehci->port_c_suspend);  				ehci->reset_done[wIndex] = 0; @@ -734,6 +732,9 @@ static int ehci_hub_control (  					ehci_readl(ehci, status_reg));  		} +		if (!(temp & (PORT_RESUME|PORT_RESET))) +			ehci->reset_done[wIndex] = 0; +  		/* transfer dedicated ports to the companion hc */  		if ((temp & PORT_CONNECT) &&  				test_bit(wIndex, &ehci->companion_ports)) { @@ -757,8 +758,17 @@ static int ehci_hub_control (  		}  		if (temp & PORT_PE)  			status |= 1 << USB_PORT_FEAT_ENABLE; -		if (temp & (PORT_SUSPEND|PORT_RESUME)) + +		/* maybe the port was unsuspended without our knowledge */ +		if (temp & (PORT_SUSPEND|PORT_RESUME)) {  			status |= 1 << USB_PORT_FEAT_SUSPEND; +		} else if (test_bit(wIndex, &ehci->suspended_ports)) { +			clear_bit(wIndex, &ehci->suspended_ports); +			ehci->reset_done[wIndex] = 0; +			if (temp & PORT_PE) +				set_bit(wIndex, &ehci->port_c_suspend); +		} +  		if (temp & PORT_OC)  			status |= 1 << USB_PORT_FEAT_OVER_CURRENT;  		if (temp & PORT_RESET) @@ -803,6 +813,7 @@ static int ehci_hub_control (  					|| (temp & PORT_RESET) != 0)  				goto error;  			ehci_writel(ehci, temp | PORT_SUSPEND, status_reg); +			set_bit(wIndex, &ehci->suspended_ports);  			break;  		case USB_PORT_FEAT_POWER:  			if (HCS_PPC (ehci->hcs_params)) diff --git a/drivers/usb/host/ehci-ppc-soc.c b/drivers/usb/host/ehci-ppc-soc.c deleted file mode 100644 index 529590eb403..00000000000 --- a/drivers/usb/host/ehci-ppc-soc.c +++ /dev/null @@ -1,201 +0,0 @@ -/* - * EHCI HCD (Host Controller Driver) for USB. - * - * (C) Copyright 2006-2007 Stefan Roese <sr@denx.de>, DENX Software Engineering - * - * Bus Glue for PPC On-Chip EHCI driver - * Tested on AMCC 440EPx - * - * Based on "ehci-au1xxx.c" by K.Boge <karsten.boge@amd.com> - * - * This file is licenced under the GPL. - */ - -#include <linux/platform_device.h> - -extern int usb_disabled(void); - -/* called during probe() after chip reset completes */ -static int ehci_ppc_soc_setup(struct usb_hcd *hcd) -{ -	struct ehci_hcd	*ehci = hcd_to_ehci(hcd); -	int		retval; - -	retval = ehci_halt(ehci); -	if (retval) -		return retval; - -	retval = ehci_init(hcd); -	if (retval) -		return retval; - -	ehci->sbrn = 0x20; -	return ehci_reset(ehci); -} - -/** - * usb_ehci_ppc_soc_probe - initialize PPC-SoC-based HCDs - * Context: !in_interrupt() - * - * Allocates basic resources for this USB host controller, and - * then invokes the start() method for the HCD associated with it - * through the hotplug entry's driver_data. - * - */ -int usb_ehci_ppc_soc_probe(const struct hc_driver *driver, -			   struct usb_hcd **hcd_out, -			   struct platform_device *dev) -{ -	int retval; -	struct usb_hcd *hcd; -	struct ehci_hcd *ehci; - -	if (dev->resource[1].flags != IORESOURCE_IRQ) { -		pr_debug("resource[1] is not IORESOURCE_IRQ"); -		retval = -ENOMEM; -	} -	hcd = usb_create_hcd(driver, &dev->dev, "PPC-SOC EHCI"); -	if (!hcd) -		return -ENOMEM; -	hcd->rsrc_start = dev->resource[0].start; -	hcd->rsrc_len = dev->resource[0].end - dev->resource[0].start + 1; - -	if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) { -		pr_debug("request_mem_region failed"); -		retval = -EBUSY; -		goto err1; -	} - -	hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len); -	if (!hcd->regs) { -		pr_debug("ioremap failed"); -		retval = -ENOMEM; -		goto err2; -	} - -	ehci = hcd_to_ehci(hcd); -	ehci->big_endian_mmio = 1; -	ehci->big_endian_desc = 1; -	ehci->caps = hcd->regs; -	ehci->regs = hcd->regs + HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase)); - -	/* cache this readonly data; minimize chip reads */ -	ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); - -#if defined(CONFIG_440EPX) -	/* -	 * 440EPx Errata USBH_3 -	 * Fix: Enable Break Memory Transfer (BMT) in INSNREG3 -	 */ -	out_be32((void *)((ulong)(&ehci->regs->command) + 0x8c), (1 << 0)); -	ehci_dbg(ehci, "Break Memory Transfer (BMT) has beed enabled!\n"); -#endif - -	retval = usb_add_hcd(hcd, dev->resource[1].start, IRQF_DISABLED); -	if (retval == 0) -		return retval; - -	iounmap(hcd->regs); -err2: -	release_mem_region(hcd->rsrc_start, hcd->rsrc_len); -err1: -	usb_put_hcd(hcd); -	return retval; -} - -/* may be called without controller electrically present */ -/* may be called with controller, bus, and devices active */ - -/** - * usb_ehci_hcd_ppc_soc_remove - shutdown processing for PPC-SoC-based HCDs - * @dev: USB Host Controller being removed - * Context: !in_interrupt() - * - * Reverses the effect of usb_ehci_hcd_ppc_soc_probe(), first invoking - * the HCD's stop() method.  It is always called from a thread - * context, normally "rmmod", "apmd", or something similar. - * - */ -void usb_ehci_ppc_soc_remove(struct usb_hcd *hcd, struct platform_device *dev) -{ -	usb_remove_hcd(hcd); -	iounmap(hcd->regs); -	release_mem_region(hcd->rsrc_start, hcd->rsrc_len); -	usb_put_hcd(hcd); -} - -static const struct hc_driver ehci_ppc_soc_hc_driver = { -	.description = hcd_name, -	.product_desc = "PPC-SOC EHCI", -	.hcd_priv_size = sizeof(struct ehci_hcd), - -	/* -	 * generic hardware linkage -	 */ -	.irq = ehci_irq, -	.flags = HCD_MEMORY | HCD_USB2, - -	/* -	 * basic lifecycle operations -	 */ -	.reset = ehci_ppc_soc_setup, -	.start = ehci_run, -	.stop = ehci_stop, -	.shutdown = ehci_shutdown, - -	/* -	 * managing i/o requests and associated device resources -	 */ -	.urb_enqueue = ehci_urb_enqueue, -	.urb_dequeue = ehci_urb_dequeue, -	.endpoint_disable = ehci_endpoint_disable, - -	/* -	 * scheduling support -	 */ -	.get_frame_number = ehci_get_frame, - -	/* -	 * root hub support -	 */ -	.hub_status_data = ehci_hub_status_data, -	.hub_control = ehci_hub_control, -	.bus_suspend = ehci_bus_suspend, -	.bus_resume = ehci_bus_resume, -	.relinquish_port = ehci_relinquish_port, -	.port_handed_over = ehci_port_handed_over, -}; - -static int ehci_hcd_ppc_soc_drv_probe(struct platform_device *pdev) -{ -	struct usb_hcd *hcd = NULL; -	int ret; - -	pr_debug("In ehci_hcd_ppc_soc_drv_probe\n"); - -	if (usb_disabled()) -		return -ENODEV; - -	/* FIXME we only want one one probe() not two */ -	ret = usb_ehci_ppc_soc_probe(&ehci_ppc_soc_hc_driver, &hcd, pdev); -	return ret; -} - -static int ehci_hcd_ppc_soc_drv_remove(struct platform_device *pdev) -{ -	struct usb_hcd *hcd = platform_get_drvdata(pdev); - -	/* FIXME we only want one one remove() not two */ -	usb_ehci_ppc_soc_remove(hcd, pdev); -	return 0; -} - -MODULE_ALIAS("platform:ppc-soc-ehci"); -static struct platform_driver ehci_ppc_soc_driver = { -	.probe = ehci_hcd_ppc_soc_drv_probe, -	.remove = ehci_hcd_ppc_soc_drv_remove, -	.shutdown = usb_hcd_platform_shutdown, -	.driver = { -		.name = "ppc-soc-ehci", -	} -}; diff --git a/drivers/usb/host/ehci-ps3.c b/drivers/usb/host/ehci-ps3.c index 0eba894bcb0..9c9da35abc6 100644 --- a/drivers/usb/host/ehci-ps3.c +++ b/drivers/usb/host/ehci-ps3.c @@ -205,6 +205,7 @@ static int ps3_ehci_remove(struct ps3_system_bus_device *dev)  	tmp = hcd->irq; +	ehci_shutdown(hcd);  	usb_remove_hcd(hcd);  	ps3_system_bus_set_driver_data(dev, NULL); diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index 4a0c5a78b2e..a081ee65bde 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c @@ -918,7 +918,7 @@ iso_stream_init (  		 */  		stream->usecs = HS_USECS_ISO (maxp);  		bandwidth = stream->usecs * 8; -		bandwidth /= 1 << (interval - 1); +		bandwidth /= interval;  	} else {  		u32		addr; @@ -951,7 +951,7 @@ iso_stream_init (  		} else  			stream->raw_mask = smask_out [hs_transfers - 1];  		bandwidth = stream->usecs + stream->c_usecs; -		bandwidth /= 1 << (interval + 2); +		bandwidth /= interval << 3;  		/* stream->splits gets created from raw_mask later */  		stream->address = cpu_to_hc32(ehci, addr); diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index 5799298364f..b11798d17ae 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -99,6 +99,8 @@ struct ehci_hcd {			/* one per controller */  			owned by the companion during a bus suspend */  	unsigned long		port_c_suspend;		/* which ports have  			the change-suspend feature turned on */ +	unsigned long		suspended_ports;	/* which ports are +			suspended */  	/* per-HC memory pools (could be per-bus, but ...) */  	struct dma_pool		*qh_pool;	/* qh per active urb */ @@ -181,14 +183,16 @@ timer_action (struct ehci_hcd *ehci, enum ehci_timer_action action)  	 * the async ring; just the I/O watchdog.  Note that if a  	 * SHRINK were pending, OFF would never be requested.  	 */ -	if (timer_pending(&ehci->watchdog) -			&& ((BIT(TIMER_ASYNC_SHRINK) | BIT(TIMER_ASYNC_OFF)) -				& ehci->actions)) -		return; +	enum ehci_timer_action oldactions = ehci->actions;  	if (!test_and_set_bit (action, &ehci->actions)) {  		unsigned long t; +		if (timer_pending(&ehci->watchdog) +			&& ((BIT(TIMER_ASYNC_SHRINK) | BIT(TIMER_ASYNC_OFF)) +				& oldactions)) +			return; +  		switch (action) {  		case TIMER_IO_WATCHDOG:  			t = EHCI_IO_JIFFIES; @@ -204,149 +208,13 @@ timer_action (struct ehci_hcd *ehci, enum ehci_timer_action action)  			t = DIV_ROUND_UP(EHCI_SHRINK_FRAMES * HZ, 1000) + 1;  			break;  		} -		mod_timer(&ehci->watchdog, t + jiffies); +		mod_timer(&ehci->watchdog, round_jiffies(t + jiffies));  	}  }  /*-------------------------------------------------------------------------*/ -/* EHCI register interface, corresponds to EHCI Revision 0.95 specification */ - -/* Section 2.2 Host Controller Capability Registers */ -struct ehci_caps { -	/* these fields are specified as 8 and 16 bit registers, -	 * but some hosts can't perform 8 or 16 bit PCI accesses. -	 */ -	u32		hc_capbase; -#define HC_LENGTH(p)		(((p)>>00)&0x00ff)	/* bits 7:0 */ -#define HC_VERSION(p)		(((p)>>16)&0xffff)	/* bits 31:16 */ -	u32		hcs_params;     /* HCSPARAMS - offset 0x4 */ -#define HCS_DEBUG_PORT(p)	(((p)>>20)&0xf)	/* bits 23:20, debug port? */ -#define HCS_INDICATOR(p)	((p)&(1 << 16))	/* true: has port indicators */ -#define HCS_N_CC(p)		(((p)>>12)&0xf)	/* bits 15:12, #companion HCs */ -#define HCS_N_PCC(p)		(((p)>>8)&0xf)	/* bits 11:8, ports per CC */ -#define HCS_PORTROUTED(p)	((p)&(1 << 7))	/* true: port routing */ -#define HCS_PPC(p)		((p)&(1 << 4))	/* true: port power control */ -#define HCS_N_PORTS(p)		(((p)>>0)&0xf)	/* bits 3:0, ports on HC */ - -	u32		hcc_params;      /* HCCPARAMS - offset 0x8 */ -#define HCC_EXT_CAPS(p)		(((p)>>8)&0xff)	/* for pci extended caps */ -#define HCC_ISOC_CACHE(p)       ((p)&(1 << 7))  /* true: can cache isoc frame */ -#define HCC_ISOC_THRES(p)       (((p)>>4)&0x7)  /* bits 6:4, uframes cached */ -#define HCC_CANPARK(p)		((p)&(1 << 2))  /* true: can park on async qh */ -#define HCC_PGM_FRAMELISTLEN(p) ((p)&(1 << 1))  /* true: periodic_size changes*/ -#define HCC_64BIT_ADDR(p)       ((p)&(1))       /* true: can use 64-bit addr */ -	u8		portroute [8];	 /* nibbles for routing - offset 0xC */ -} __attribute__ ((packed)); - - -/* Section 2.3 Host Controller Operational Registers */ -struct ehci_regs { - -	/* USBCMD: offset 0x00 */ -	u32		command; -/* 23:16 is r/w intr rate, in microframes; default "8" == 1/msec */ -#define CMD_PARK	(1<<11)		/* enable "park" on async qh */ -#define CMD_PARK_CNT(c)	(((c)>>8)&3)	/* how many transfers to park for */ -#define CMD_LRESET	(1<<7)		/* partial reset (no ports, etc) */ -#define CMD_IAAD	(1<<6)		/* "doorbell" interrupt async advance */ -#define CMD_ASE		(1<<5)		/* async schedule enable */ -#define CMD_PSE		(1<<4)		/* periodic schedule enable */ -/* 3:2 is periodic frame list size */ -#define CMD_RESET	(1<<1)		/* reset HC not bus */ -#define CMD_RUN		(1<<0)		/* start/stop HC */ - -	/* USBSTS: offset 0x04 */ -	u32		status; -#define STS_ASS		(1<<15)		/* Async Schedule Status */ -#define STS_PSS		(1<<14)		/* Periodic Schedule Status */ -#define STS_RECL	(1<<13)		/* Reclamation */ -#define STS_HALT	(1<<12)		/* Not running (any reason) */ -/* some bits reserved */ -	/* these STS_* flags are also intr_enable bits (USBINTR) */ -#define STS_IAA		(1<<5)		/* Interrupted on async advance */ -#define STS_FATAL	(1<<4)		/* such as some PCI access errors */ -#define STS_FLR		(1<<3)		/* frame list rolled over */ -#define STS_PCD		(1<<2)		/* port change detect */ -#define STS_ERR		(1<<1)		/* "error" completion (overflow, ...) */ -#define STS_INT		(1<<0)		/* "normal" completion (short, ...) */ - -	/* USBINTR: offset 0x08 */ -	u32		intr_enable; - -	/* FRINDEX: offset 0x0C */ -	u32		frame_index;	/* current microframe number */ -	/* CTRLDSSEGMENT: offset 0x10 */ -	u32		segment;	/* address bits 63:32 if needed */ -	/* PERIODICLISTBASE: offset 0x14 */ -	u32		frame_list;	/* points to periodic list */ -	/* ASYNCLISTADDR: offset 0x18 */ -	u32		async_next;	/* address of next async queue head */ - -	u32		reserved [9]; - -	/* CONFIGFLAG: offset 0x40 */ -	u32		configured_flag; -#define FLAG_CF		(1<<0)		/* true: we'll support "high speed" */ - -	/* PORTSC: offset 0x44 */ -	u32		port_status [0];	/* up to N_PORTS */ -/* 31:23 reserved */ -#define PORT_WKOC_E	(1<<22)		/* wake on overcurrent (enable) */ -#define PORT_WKDISC_E	(1<<21)		/* wake on disconnect (enable) */ -#define PORT_WKCONN_E	(1<<20)		/* wake on connect (enable) */ -/* 19:16 for port testing */ -#define PORT_LED_OFF	(0<<14) -#define PORT_LED_AMBER	(1<<14) -#define PORT_LED_GREEN	(2<<14) -#define PORT_LED_MASK	(3<<14) -#define PORT_OWNER	(1<<13)		/* true: companion hc owns this port */ -#define PORT_POWER	(1<<12)		/* true: has power (see PPC) */ -#define PORT_USB11(x) (((x)&(3<<10))==(1<<10))	/* USB 1.1 device */ -/* 11:10 for detecting lowspeed devices (reset vs release ownership) */ -/* 9 reserved */ -#define PORT_RESET	(1<<8)		/* reset port */ -#define PORT_SUSPEND	(1<<7)		/* suspend port */ -#define PORT_RESUME	(1<<6)		/* resume it */ -#define PORT_OCC	(1<<5)		/* over current change */ -#define PORT_OC		(1<<4)		/* over current active */ -#define PORT_PEC	(1<<3)		/* port enable change */ -#define PORT_PE		(1<<2)		/* port enable */ -#define PORT_CSC	(1<<1)		/* connect status change */ -#define PORT_CONNECT	(1<<0)		/* device connected */ -#define PORT_RWC_BITS   (PORT_CSC | PORT_PEC | PORT_OCC) -} __attribute__ ((packed)); - -#define USBMODE		0x68		/* USB Device mode */ -#define USBMODE_SDIS	(1<<3)		/* Stream disable */ -#define USBMODE_BE	(1<<2)		/* BE/LE endianness select */ -#define USBMODE_CM_HC	(3<<0)		/* host controller mode */ -#define USBMODE_CM_IDLE	(0<<0)		/* idle state */ - -/* Appendix C, Debug port ... intended for use with special "debug devices" - * that can help if there's no serial console.  (nonstandard enumeration.) - */ -struct ehci_dbg_port { -	u32	control; -#define DBGP_OWNER	(1<<30) -#define DBGP_ENABLED	(1<<28) -#define DBGP_DONE	(1<<16) -#define DBGP_INUSE	(1<<10) -#define DBGP_ERRCODE(x)	(((x)>>7)&0x07) -#	define DBGP_ERR_BAD	1 -#	define DBGP_ERR_SIGNAL	2 -#define DBGP_ERROR	(1<<6) -#define DBGP_GO		(1<<5) -#define DBGP_OUT	(1<<4) -#define DBGP_LEN(x)	(((x)>>0)&0x0f) -	u32	pids; -#define DBGP_PID_GET(x)		(((x)>>16)&0xff) -#define DBGP_PID_SET(data,tok)	(((data)<<8)|(tok)) -	u32	data03; -	u32	data47; -	u32	address; -#define DBGP_EPADDR(dev,ep)	(((dev)<<8)|(ep)) -} __attribute__ ((packed)); +#include <linux/usb/ehci_def.h>  /*-------------------------------------------------------------------------*/ @@ -740,16 +608,7 @@ ehci_port_speed(struct ehci_hcd *ehci, unsigned int portsc)  /*   * Big-endian read/write functions are arch-specific.   * Other arches can be added if/when they're needed. - * - * REVISIT: arch/powerpc now has readl/writel_be, so the - * definition below can die once the 4xx support is - * finally ported over.   */ -#if defined(CONFIG_PPC) && !defined(CONFIG_PPC_MERGE) -#define readl_be(addr)		in_be32((__force unsigned *)addr) -#define writel_be(val, addr)	out_be32((__force unsigned *)addr, val) -#endif -  #if defined(CONFIG_ARM) && defined(CONFIG_ARCH_IXP4XX)  #define readl_be(addr)		__raw_readl((__force unsigned *)addr)  #define writel_be(val, addr)	__raw_writel(val, (__force unsigned *)addr) diff --git a/drivers/usb/host/hwa-hc.c b/drivers/usb/host/hwa-hc.c new file mode 100644 index 00000000000..64be4d88df1 --- /dev/null +++ b/drivers/usb/host/hwa-hc.c @@ -0,0 +1,925 @@ +/* + * Host Wire Adapter: + * Driver glue, HWA-specific functions, bridges to WAHC and WUSBHC + * + * Copyright (C) 2005-2006 Intel Corporation + * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * + * The HWA driver is a simple layer that forwards requests to the WAHC + * (Wire Adater Host Controller) or WUSBHC (Wireless USB Host + * Controller) layers. + * + * Host Wire Adapter is the 'WUSB 1.0 standard' name for Wireless-USB + * Host Controller that is connected to your system via USB (a USB + * dongle that implements a USB host...). There is also a Device Wired + * Adaptor, DWA (Wireless USB hub) that uses the same mechanism for + * transferring data (it is after all a USB host connected via + * Wireless USB), we have a common layer called Wire Adapter Host + * Controller that does all the hard work. The WUSBHC (Wireless USB + * Host Controller) is the part common to WUSB Host Controllers, the + * HWA and the PCI-based one, that is implemented following the WHCI + * spec. All these layers are implemented in ../wusbcore. + * + * The main functions are hwahc_op_urb_{en,de}queue(), that pass the + * job of converting a URB to a Wire Adapter + * + * Entry points: + * + *   hwahc_driver_*()   Driver initialization, registration and + *                      teardown. + * + *   hwahc_probe()	New device came up, create an instance for + *                      it [from device enumeration]. + * + *   hwahc_disconnect()	Remove device instance [from device + *                      enumeration]. + * + *   [__]hwahc_op_*()   Host-Wire-Adaptor specific functions for + *                      starting/stopping/etc (some might be made also + *                      DWA). + */ +#include <linux/kernel.h> +#include <linux/version.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/workqueue.h> +#include <linux/wait.h> +#include <linux/completion.h> +#include "../wusbcore/wa-hc.h" +#include "../wusbcore/wusbhc.h" + +#define D_LOCAL 0 +#include <linux/uwb/debug.h> + +struct hwahc { +	struct wusbhc wusbhc;	/* has to be 1st */ +	struct wahc wa; +	u8 buffer[16];		/* for misc usb transactions */ +}; + +/** + * FIXME should be wusbhc + * + * NOTE: we need to cache the Cluster ID because later...there is no + *       way to get it :) + */ +static int __hwahc_set_cluster_id(struct hwahc *hwahc, u8 cluster_id) +{ +	int result; +	struct wusbhc *wusbhc = &hwahc->wusbhc; +	struct wahc *wa = &hwahc->wa; +	struct device *dev = &wa->usb_iface->dev; + +	result = usb_control_msg(wa->usb_dev, usb_sndctrlpipe(wa->usb_dev, 0), +			WUSB_REQ_SET_CLUSTER_ID, +			USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE, +			cluster_id, +			wa->usb_iface->cur_altsetting->desc.bInterfaceNumber, +			NULL, 0, 1000 /* FIXME: arbitrary */); +	if (result < 0) +		dev_err(dev, "Cannot set WUSB Cluster ID to 0x%02x: %d\n", +			cluster_id, result); +	else +		wusbhc->cluster_id = cluster_id; +	dev_info(dev, "Wireless USB Cluster ID set to 0x%02x\n", cluster_id); +	return result; +} + +static int __hwahc_op_set_num_dnts(struct wusbhc *wusbhc, u8 interval, u8 slots) +{ +	struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc); +	struct wahc *wa = &hwahc->wa; + +	return usb_control_msg(wa->usb_dev, usb_sndctrlpipe(wa->usb_dev, 0), +			WUSB_REQ_SET_NUM_DNTS, +			USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE, +			interval << 8 | slots, +			wa->usb_iface->cur_altsetting->desc.bInterfaceNumber, +			NULL, 0, 1000 /* FIXME: arbitrary */); +} + +/* + * Reset a WUSB host controller and wait for it to complete doing it. + * + * @usb_hcd:	Pointer to WUSB Host Controller instance. + * + */ +static int hwahc_op_reset(struct usb_hcd *usb_hcd) +{ +	int result; +	struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd); +	struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc); +	struct device *dev = &hwahc->wa.usb_iface->dev; + +	d_fnstart(4, dev, "(hwahc %p)\n", hwahc); +	mutex_lock(&wusbhc->mutex); +	wa_nep_disarm(&hwahc->wa); +	result = __wa_set_feature(&hwahc->wa, WA_RESET); +	if (result < 0) { +		dev_err(dev, "error commanding HC to reset: %d\n", result); +		goto error_unlock; +	} +	d_printf(3, dev, "reset: waiting for device to change state\n"); +	result = __wa_wait_status(&hwahc->wa, WA_STATUS_RESETTING, 0); +	if (result < 0) { +		dev_err(dev, "error waiting for HC to reset: %d\n", result); +		goto error_unlock; +	} +error_unlock: +	mutex_unlock(&wusbhc->mutex); +	d_fnend(4, dev, "(hwahc %p) = %d\n", hwahc, result); +	return result; +} + +/* + * FIXME: break this function up + */ +static int hwahc_op_start(struct usb_hcd *usb_hcd) +{ +	u8 addr; +	int result; +	struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd); +	struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc); +	struct device *dev = &hwahc->wa.usb_iface->dev; + +	/* Set up a Host Info WUSB Information Element */ +	d_fnstart(4, dev, "(hwahc %p)\n", hwahc); +	result = -ENOSPC; +	mutex_lock(&wusbhc->mutex); +	/* Start the numbering from the top so that the bottom +	 * range of the unauth addr space is used for devices, +	 * the top for HCs; use 0xfe - RC# */ +	addr = wusb_cluster_id_get(); +	if (addr == 0) +		goto error_cluster_id_get; +	result = __hwahc_set_cluster_id(hwahc, addr); +	if (result < 0) +		goto error_set_cluster_id; + +	result = wa_nep_arm(&hwahc->wa, GFP_KERNEL); +	if (result < 0) { +		dev_err(dev, "cannot listen to notifications: %d\n", result); +		goto error_stop; +	} +	usb_hcd->uses_new_polling = 1; +	usb_hcd->poll_rh = 1; +	usb_hcd->state = HC_STATE_RUNNING; +	result = 0; +out: +	mutex_unlock(&wusbhc->mutex); +	d_fnend(4, dev, "(hwahc %p) = %d\n", hwahc, result); +	return result; + +error_stop: +	__wa_stop(&hwahc->wa); +error_set_cluster_id: +	wusb_cluster_id_put(wusbhc->cluster_id); +error_cluster_id_get: +	goto out; + +} + +/* + * FIXME: break this function up + */ +static int __hwahc_op_wusbhc_start(struct wusbhc *wusbhc) +{ +	int result; +	struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc); +	struct device *dev = &hwahc->wa.usb_iface->dev; + +	/* Set up a Host Info WUSB Information Element */ +	d_fnstart(4, dev, "(hwahc %p)\n", hwahc); +	result = -ENOSPC; + +	result = __wa_set_feature(&hwahc->wa, WA_ENABLE); +	if (result < 0) { +		dev_err(dev, "error commanding HC to start: %d\n", result); +		goto error_stop; +	} +	result = __wa_wait_status(&hwahc->wa, WA_ENABLE, WA_ENABLE); +	if (result < 0) { +		dev_err(dev, "error waiting for HC to start: %d\n", result); +		goto error_stop; +	} +	result = 0; +out: +	d_fnend(4, dev, "(hwahc %p) = %d\n", hwahc, result); +	return result; + +error_stop: +	result = __wa_clear_feature(&hwahc->wa, WA_ENABLE); +	goto out; +} + +static int hwahc_op_suspend(struct usb_hcd *usb_hcd, pm_message_t msg) +{ +	struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd); +	struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc); +	dev_err(wusbhc->dev, "%s (%p [%p], 0x%lx) UNIMPLEMENTED\n", __func__, +		usb_hcd, hwahc, *(unsigned long *) &msg); +	return -ENOSYS; +} + +static int hwahc_op_resume(struct usb_hcd *usb_hcd) +{ +	struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd); +	struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc); + +	dev_err(wusbhc->dev, "%s (%p [%p]) UNIMPLEMENTED\n", __func__, +		usb_hcd, hwahc); +	return -ENOSYS; +} + +static void __hwahc_op_wusbhc_stop(struct wusbhc *wusbhc) +{ +	int result; +	struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc); +	struct device *dev = &hwahc->wa.usb_iface->dev; + +	d_fnstart(4, dev, "(hwahc %p)\n", hwahc); +	/* Nothing for now */ +	d_fnend(4, dev, "(hwahc %p) = %d\n", hwahc, result); +	return; +} + +/* + * No need to abort pipes, as when this is called, all the children + * has been disconnected and that has done it [through + * usb_disable_interface() -> usb_disable_endpoint() -> + * hwahc_op_ep_disable() - >rpipe_ep_disable()]. + */ +static void hwahc_op_stop(struct usb_hcd *usb_hcd) +{ +	int result; +	struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd); +	struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc); +	struct wahc *wa = &hwahc->wa; +	struct device *dev = &wa->usb_iface->dev; + +	d_fnstart(4, dev, "(hwahc %p)\n", hwahc); +	mutex_lock(&wusbhc->mutex); +	wusbhc_stop(wusbhc); +	wa_nep_disarm(&hwahc->wa); +	result = __wa_stop(&hwahc->wa); +	wusb_cluster_id_put(wusbhc->cluster_id); +	mutex_unlock(&wusbhc->mutex); +	d_fnend(4, dev, "(hwahc %p) = %d\n", hwahc, result); +	return; +} + +static int hwahc_op_get_frame_number(struct usb_hcd *usb_hcd) +{ +	struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd); +	struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc); + +	dev_err(wusbhc->dev, "%s (%p [%p]) UNIMPLEMENTED\n", __func__, +		usb_hcd, hwahc); +	return -ENOSYS; +} + +static int hwahc_op_urb_enqueue(struct usb_hcd *usb_hcd, struct urb *urb, +				gfp_t gfp) +{ +	struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd); +	struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc); + +	return wa_urb_enqueue(&hwahc->wa, urb->ep, urb, gfp); +} + +static int hwahc_op_urb_dequeue(struct usb_hcd *usb_hcd, struct urb *urb, +				int status) +{ +	struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd); +	struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc); + +	return wa_urb_dequeue(&hwahc->wa, urb); +} + +/* + * Release resources allocated for an endpoint + * + * If there is an associated rpipe to this endpoint, go ahead and put it. + */ +static void hwahc_op_endpoint_disable(struct usb_hcd *usb_hcd, +				      struct usb_host_endpoint *ep) +{ +	struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd); +	struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc); + +	rpipe_ep_disable(&hwahc->wa, ep); +} + +/* + * Set the UWB MAS allocation for the WUSB cluster + * + * @stream_index: stream to use (-1 for cancelling the allocation) + * @mas: mas bitmap to use + */ +static int __hwahc_op_bwa_set(struct wusbhc *wusbhc, s8 stream_index, +			      const struct uwb_mas_bm *mas) +{ +	int result; +	struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc); +	struct wahc *wa = &hwahc->wa; +	struct device *dev = &wa->usb_iface->dev; +	u8 mas_le[UWB_NUM_MAS/8]; + +	/* Set the stream index */ +	result = usb_control_msg(wa->usb_dev, usb_sndctrlpipe(wa->usb_dev, 0), +			WUSB_REQ_SET_STREAM_IDX, +			USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE, +			stream_index, +			wa->usb_iface->cur_altsetting->desc.bInterfaceNumber, +			NULL, 0, 1000 /* FIXME: arbitrary */); +	if (result < 0) { +		dev_err(dev, "Cannot set WUSB stream index: %d\n", result); +		goto out; +	} +	uwb_mas_bm_copy_le(mas_le, mas); +	/* Set the MAS allocation */ +	result = usb_control_msg(wa->usb_dev, usb_sndctrlpipe(wa->usb_dev, 0), +			WUSB_REQ_SET_WUSB_MAS, +			USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE, +			0, wa->usb_iface->cur_altsetting->desc.bInterfaceNumber, +			mas_le, 32, 1000 /* FIXME: arbitrary */); +	if (result < 0) +		dev_err(dev, "Cannot set WUSB MAS allocation: %d\n", result); +out: +	return result; +} + +/* + * Add an IE to the host's MMC + * + * @interval:    See WUSB1.0[8.5.3.1] + * @repeat_cnt:  See WUSB1.0[8.5.3.1] + * @handle:      See WUSB1.0[8.5.3.1] + * @wuie:        Pointer to the header of the WUSB IE data to add. + *               MUST BE allocated in a kmalloc buffer (no stack or + *               vmalloc). + * + * NOTE: the format of the WUSB IEs for MMCs are different to the + *       normal MBOA MAC IEs (IE Id + Length in MBOA MAC vs. Length + + *       Id in WUSB IEs). Standards...you gotta love'em. + */ +static int __hwahc_op_mmcie_add(struct wusbhc *wusbhc, u8 interval, +				u8 repeat_cnt, u8 handle, +				struct wuie_hdr *wuie) +{ +	struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc); +	struct wahc *wa = &hwahc->wa; +	u8 iface_no = wa->usb_iface->cur_altsetting->desc.bInterfaceNumber; + +	return usb_control_msg(wa->usb_dev, usb_sndctrlpipe(wa->usb_dev, 0), +			WUSB_REQ_ADD_MMC_IE, +			USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE, +			interval << 8 | repeat_cnt, +			handle << 8 | iface_no, +			wuie, wuie->bLength, 1000 /* FIXME: arbitrary */); +} + +/* + * Remove an IE to the host's MMC + * + * @handle:      See WUSB1.0[8.5.3.1] + */ +static int __hwahc_op_mmcie_rm(struct wusbhc *wusbhc, u8 handle) +{ +	struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc); +	struct wahc *wa = &hwahc->wa; +	u8 iface_no = wa->usb_iface->cur_altsetting->desc.bInterfaceNumber; +	return usb_control_msg(wa->usb_dev, usb_sndctrlpipe(wa->usb_dev, 0), +			WUSB_REQ_REMOVE_MMC_IE, +			USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE, +			0, handle << 8 | iface_no, +			NULL, 0, 1000 /* FIXME: arbitrary */); +} + +/* + * Update device information for a given fake port + * + * @port_idx: Fake port to which device is connected (wusbhc index, not + *            USB port number). + */ +static int __hwahc_op_dev_info_set(struct wusbhc *wusbhc, +				   struct wusb_dev *wusb_dev) +{ +	struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc); +	struct wahc *wa = &hwahc->wa; +	u8 iface_no = wa->usb_iface->cur_altsetting->desc.bInterfaceNumber; +	struct hwa_dev_info *dev_info; +	int ret; + +	/* fill out the Device Info buffer and send it */ +	dev_info = kzalloc(sizeof(struct hwa_dev_info), GFP_KERNEL); +	if (!dev_info) +		return -ENOMEM; +	uwb_mas_bm_copy_le(dev_info->bmDeviceAvailability, +			   &wusb_dev->availability); +	dev_info->bDeviceAddress = wusb_dev->addr; + +	/* +	 * If the descriptors haven't been read yet, use a default PHY +	 * rate of 53.3 Mbit/s only.  The correct value will be used +	 * when this will be called again as part of the +	 * authentication process (which occurs after the descriptors +	 * have been read). +	 */ +	if (wusb_dev->wusb_cap_descr) +		dev_info->wPHYRates = wusb_dev->wusb_cap_descr->wPHYRates; +	else +		dev_info->wPHYRates = cpu_to_le16(USB_WIRELESS_PHY_53); + +	ret = usb_control_msg(wa->usb_dev, usb_sndctrlpipe(wa->usb_dev, 0), +			WUSB_REQ_SET_DEV_INFO, +			USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE, +			0, wusb_dev->port_idx << 8 | iface_no, +			dev_info, sizeof(struct hwa_dev_info), +			1000 /* FIXME: arbitrary */); +	kfree(dev_info); +	return ret; +} + +/* + * Set host's idea of which encryption (and key) method to use when + * talking to ad evice on a given port. + * + * If key is NULL, it means disable encryption for that "virtual port" + * (used when we disconnect). + */ +static int __hwahc_dev_set_key(struct wusbhc *wusbhc, u8 port_idx, u32 tkid, +			       const void *key, size_t key_size, +			       u8 key_idx) +{ +	int result = -ENOMEM; +	struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc); +	struct wahc *wa = &hwahc->wa; +	u8 iface_no = wa->usb_iface->cur_altsetting->desc.bInterfaceNumber; +	struct usb_key_descriptor *keyd; +	size_t keyd_len; + +	keyd_len = sizeof(*keyd) + key_size; +	keyd = kzalloc(keyd_len, GFP_KERNEL); +	if (keyd == NULL) +		return -ENOMEM; + +	keyd->bLength = keyd_len; +	keyd->bDescriptorType = USB_DT_KEY; +	keyd->tTKID[0] = (tkid >>  0) & 0xff; +	keyd->tTKID[1] = (tkid >>  8) & 0xff; +	keyd->tTKID[2] = (tkid >> 16) & 0xff; +	memcpy(keyd->bKeyData, key, key_size); + +	result = usb_control_msg(wa->usb_dev, usb_sndctrlpipe(wa->usb_dev, 0), +			USB_REQ_SET_DESCRIPTOR, +			USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE, +			USB_DT_KEY << 8 | key_idx, +			port_idx << 8 | iface_no, +			keyd, keyd_len, 1000 /* FIXME: arbitrary */); + +	memset(keyd, 0, sizeof(*keyd));	/* clear keys etc. */ +	kfree(keyd); +	return result; +} + +/* + * Set host's idea of which encryption (and key) method to use when + * talking to ad evice on a given port. + * + * If key is NULL, it means disable encryption for that "virtual port" + * (used when we disconnect). + */ +static int __hwahc_op_set_ptk(struct wusbhc *wusbhc, u8 port_idx, u32 tkid, +			      const void *key, size_t key_size) +{ +	int result = -ENOMEM; +	struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc); +	struct wahc *wa = &hwahc->wa; +	u8 iface_no = wa->usb_iface->cur_altsetting->desc.bInterfaceNumber; +	u8 encryption_value; + +	/* Tell the host which key to use to talk to the device */ +	if (key) { +		u8 key_idx = wusb_key_index(0, WUSB_KEY_INDEX_TYPE_PTK, +					    WUSB_KEY_INDEX_ORIGINATOR_HOST); + +		result = __hwahc_dev_set_key(wusbhc, port_idx, tkid, +					     key, key_size, key_idx); +		if (result < 0) +			goto error_set_key; +		encryption_value = wusbhc->ccm1_etd->bEncryptionValue; +	} else { +		/* FIXME: this should come from wusbhc->etd[UNSECURE].value */ +		encryption_value = 0; +	} + +	/* Set the encryption type for commmunicating with the device */ +	result = usb_control_msg(wa->usb_dev, usb_sndctrlpipe(wa->usb_dev, 0), +			USB_REQ_SET_ENCRYPTION, +			USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE, +			encryption_value, port_idx << 8 | iface_no, +			NULL, 0, 1000 /* FIXME: arbitrary */); +	if (result < 0) +		dev_err(wusbhc->dev, "Can't set host's WUSB encryption for " +			"port index %u to %s (value %d): %d\n", port_idx, +			wusb_et_name(wusbhc->ccm1_etd->bEncryptionType), +			wusbhc->ccm1_etd->bEncryptionValue, result); +error_set_key: +	return result; +} + +/* + * Set host's GTK key + */ +static int __hwahc_op_set_gtk(struct wusbhc *wusbhc, u32 tkid, +			      const void *key, size_t key_size) +{ +	u8 key_idx = wusb_key_index(0, WUSB_KEY_INDEX_TYPE_GTK, +				    WUSB_KEY_INDEX_ORIGINATOR_HOST); + +	return __hwahc_dev_set_key(wusbhc, 0, tkid, key, key_size, key_idx); +} + +/* + * Get the Wire Adapter class-specific descriptor + * + * NOTE: this descriptor comes with the big bundled configuration + *       descriptor that includes the interfaces' and endpoints', so + *       we just look for it in the cached copy kept by the USB stack. + * + * NOTE2: We convert LE fields to CPU order. + */ +static int wa_fill_descr(struct wahc *wa) +{ +	int result; +	struct device *dev = &wa->usb_iface->dev; +	char *itr; +	struct usb_device *usb_dev = wa->usb_dev; +	struct usb_descriptor_header *hdr; +	struct usb_wa_descriptor *wa_descr; +	size_t itr_size, actconfig_idx; + +	actconfig_idx = (usb_dev->actconfig - usb_dev->config) / +			sizeof(usb_dev->config[0]); +	itr = usb_dev->rawdescriptors[actconfig_idx]; +	itr_size = le16_to_cpu(usb_dev->actconfig->desc.wTotalLength); +	while (itr_size >= sizeof(*hdr)) { +		hdr = (struct usb_descriptor_header *) itr; +		d_printf(3, dev, "Extra device descriptor: " +			 "type %02x/%u bytes @ %zu (%zu left)\n", +			 hdr->bDescriptorType, hdr->bLength, +			 (itr - usb_dev->rawdescriptors[actconfig_idx]), +			 itr_size); +		if (hdr->bDescriptorType == USB_DT_WIRE_ADAPTER) +			goto found; +		itr += hdr->bLength; +		itr_size -= hdr->bLength; +	} +	dev_err(dev, "cannot find Wire Adapter Class descriptor\n"); +	return -ENODEV; + +found: +	result = -EINVAL; +	if (hdr->bLength > itr_size) {	/* is it available? */ +		dev_err(dev, "incomplete Wire Adapter Class descriptor " +			"(%zu bytes left, %u needed)\n", +			itr_size, hdr->bLength); +		goto error; +	} +	if (hdr->bLength < sizeof(*wa->wa_descr)) { +		dev_err(dev, "short Wire Adapter Class descriptor\n"); +		goto error; +	} +	wa->wa_descr = wa_descr = (struct usb_wa_descriptor *) hdr; +	/* Make LE fields CPU order */ +	wa_descr->bcdWAVersion = le16_to_cpu(wa_descr->bcdWAVersion); +	wa_descr->wNumRPipes = le16_to_cpu(wa_descr->wNumRPipes); +	wa_descr->wRPipeMaxBlock = le16_to_cpu(wa_descr->wRPipeMaxBlock); +	if (wa_descr->bcdWAVersion > 0x0100) +		dev_warn(dev, "Wire Adapter v%d.%d newer than groked v1.0\n", +			 wa_descr->bcdWAVersion & 0xff00 >> 8, +			 wa_descr->bcdWAVersion & 0x00ff); +	result = 0; +error: +	return result; +} + +static struct hc_driver hwahc_hc_driver = { +	.description = "hwa-hcd", +	.product_desc = "Wireless USB HWA host controller", +	.hcd_priv_size = sizeof(struct hwahc) - sizeof(struct usb_hcd), +	.irq = NULL,			/* FIXME */ +	.flags = HCD_USB2,		/* FIXME */ +	.reset = hwahc_op_reset, +	.start = hwahc_op_start, +	.pci_suspend = hwahc_op_suspend, +	.pci_resume = hwahc_op_resume, +	.stop = hwahc_op_stop, +	.get_frame_number = hwahc_op_get_frame_number, +	.urb_enqueue = hwahc_op_urb_enqueue, +	.urb_dequeue = hwahc_op_urb_dequeue, +	.endpoint_disable = hwahc_op_endpoint_disable, + +	.hub_status_data = wusbhc_rh_status_data, +	.hub_control = wusbhc_rh_control, +	.bus_suspend = wusbhc_rh_suspend, +	.bus_resume = wusbhc_rh_resume, +	.start_port_reset = wusbhc_rh_start_port_reset, +}; + +static int hwahc_security_create(struct hwahc *hwahc) +{ +	int result; +	struct wusbhc *wusbhc = &hwahc->wusbhc; +	struct usb_device *usb_dev = hwahc->wa.usb_dev; +	struct device *dev = &usb_dev->dev; +	struct usb_security_descriptor *secd; +	struct usb_encryption_descriptor *etd; +	void *itr, *top; +	size_t itr_size, needed, bytes; +	u8 index; +	char buf[64]; + +	/* Find the host's security descriptors in the config descr bundle */ +	index = (usb_dev->actconfig - usb_dev->config) / +		sizeof(usb_dev->config[0]); +	itr = usb_dev->rawdescriptors[index]; +	itr_size = le16_to_cpu(usb_dev->actconfig->desc.wTotalLength); +	top = itr + itr_size; +	result = __usb_get_extra_descriptor(usb_dev->rawdescriptors[index], +			le16_to_cpu(usb_dev->actconfig->desc.wTotalLength), +			USB_DT_SECURITY, (void **) &secd); +	if (result == -1) { +		dev_warn(dev, "BUG? WUSB host has no security descriptors\n"); +		return 0; +	} +	needed = sizeof(*secd); +	if (top - (void *)secd < needed) { +		dev_err(dev, "BUG? Not enough data to process security " +			"descriptor header (%zu bytes left vs %zu needed)\n", +			top - (void *) secd, needed); +		return 0; +	} +	needed = le16_to_cpu(secd->wTotalLength); +	if (top - (void *)secd < needed) { +		dev_err(dev, "BUG? Not enough data to process security " +			"descriptors (%zu bytes left vs %zu needed)\n", +			top - (void *) secd, needed); +		return 0; +	} +	/* Walk over the sec descriptors and store CCM1's on wusbhc */ +	itr = (void *) secd + sizeof(*secd); +	top = (void *) secd + le16_to_cpu(secd->wTotalLength); +	index = 0; +	bytes = 0; +	while (itr < top) { +		etd = itr; +		if (top - itr < sizeof(*etd)) { +			dev_err(dev, "BUG: bad host security descriptor; " +				"not enough data (%zu vs %zu left)\n", +				top - itr, sizeof(*etd)); +			break; +		} +		if (etd->bLength < sizeof(*etd)) { +			dev_err(dev, "BUG: bad host encryption descriptor; " +				"descriptor is too short " +				"(%zu vs %zu needed)\n", +				(size_t)etd->bLength, sizeof(*etd)); +			break; +		} +		itr += etd->bLength; +		bytes += snprintf(buf + bytes, sizeof(buf) - bytes, +				  "%s (0x%02x) ", +				  wusb_et_name(etd->bEncryptionType), +				  etd->bEncryptionValue); +		wusbhc->ccm1_etd = etd; +	} +	dev_info(dev, "supported encryption types: %s\n", buf); +	if (wusbhc->ccm1_etd == NULL) { +		dev_err(dev, "E: host doesn't support CCM-1 crypto\n"); +		return 0; +	} +	/* Pretty print what we support */ +	return 0; +} + +static void hwahc_security_release(struct hwahc *hwahc) +{ +	/* nothing to do here so far... */ +} + +static int hwahc_create(struct hwahc *hwahc, struct usb_interface *iface) +{ +	int result; +	struct device *dev = &iface->dev; +	struct wusbhc *wusbhc = &hwahc->wusbhc; +	struct wahc *wa = &hwahc->wa; +	struct usb_device *usb_dev = interface_to_usbdev(iface); + +	wa->usb_dev = usb_get_dev(usb_dev);	/* bind the USB device */ +	wa->usb_iface = usb_get_intf(iface); +	wusbhc->dev = dev; +	wusbhc->uwb_rc = uwb_rc_get_by_grandpa(iface->dev.parent); +	if (wusbhc->uwb_rc == NULL) { +		result = -ENODEV; +		dev_err(dev, "Cannot get associated UWB Host Controller\n"); +		goto error_rc_get; +	} +	result = wa_fill_descr(wa);	/* Get the device descriptor */ +	if (result < 0) +		goto error_fill_descriptor; +	if (wa->wa_descr->bNumPorts > USB_MAXCHILDREN) { +		dev_err(dev, "FIXME: USB_MAXCHILDREN too low for WUSB " +			"adapter (%u ports)\n", wa->wa_descr->bNumPorts); +		wusbhc->ports_max = USB_MAXCHILDREN; +	} else { +		wusbhc->ports_max = wa->wa_descr->bNumPorts; +	} +	wusbhc->mmcies_max = wa->wa_descr->bNumMMCIEs; +	wusbhc->start = __hwahc_op_wusbhc_start; +	wusbhc->stop = __hwahc_op_wusbhc_stop; +	wusbhc->mmcie_add = __hwahc_op_mmcie_add; +	wusbhc->mmcie_rm = __hwahc_op_mmcie_rm; +	wusbhc->dev_info_set = __hwahc_op_dev_info_set; +	wusbhc->bwa_set = __hwahc_op_bwa_set; +	wusbhc->set_num_dnts = __hwahc_op_set_num_dnts; +	wusbhc->set_ptk = __hwahc_op_set_ptk; +	wusbhc->set_gtk = __hwahc_op_set_gtk; +	result = hwahc_security_create(hwahc); +	if (result < 0) { +		dev_err(dev, "Can't initialize security: %d\n", result); +		goto error_security_create; +	} +	wa->wusb = wusbhc;	/* FIXME: ugly, need to fix */ +	result = wusbhc_create(&hwahc->wusbhc); +	if (result < 0) { +		dev_err(dev, "Can't create WUSB HC structures: %d\n", result); +		goto error_wusbhc_create; +	} +	result = wa_create(&hwahc->wa, iface); +	if (result < 0) +		goto error_wa_create; +	return 0; + +error_wa_create: +	wusbhc_destroy(&hwahc->wusbhc); +error_wusbhc_create: +	/* WA Descr fill allocs no resources */ +error_security_create: +error_fill_descriptor: +	uwb_rc_put(wusbhc->uwb_rc); +error_rc_get: +	usb_put_intf(iface); +	usb_put_dev(usb_dev); +	return result; +} + +static void hwahc_destroy(struct hwahc *hwahc) +{ +	struct wusbhc *wusbhc = &hwahc->wusbhc; + +	d_fnstart(1, NULL, "(hwahc %p)\n", hwahc); +	mutex_lock(&wusbhc->mutex); +	__wa_destroy(&hwahc->wa); +	wusbhc_destroy(&hwahc->wusbhc); +	hwahc_security_release(hwahc); +	hwahc->wusbhc.dev = NULL; +	uwb_rc_put(wusbhc->uwb_rc); +	usb_put_intf(hwahc->wa.usb_iface); +	usb_put_dev(hwahc->wa.usb_dev); +	mutex_unlock(&wusbhc->mutex); +	d_fnend(1, NULL, "(hwahc %p) = void\n", hwahc); +} + +static void hwahc_init(struct hwahc *hwahc) +{ +	wa_init(&hwahc->wa); +} + +static int hwahc_probe(struct usb_interface *usb_iface, +		       const struct usb_device_id *id) +{ +	int result; +	struct usb_hcd *usb_hcd; +	struct wusbhc *wusbhc; +	struct hwahc *hwahc; +	struct device *dev = &usb_iface->dev; + +	d_fnstart(4, dev, "(%p, %p)\n", usb_iface, id); +	result = -ENOMEM; +	usb_hcd = usb_create_hcd(&hwahc_hc_driver, &usb_iface->dev, "wusb-hwa"); +	if (usb_hcd == NULL) { +		dev_err(dev, "unable to allocate instance\n"); +		goto error_alloc; +	} +	usb_hcd->wireless = 1; +	usb_hcd->flags |= HCD_FLAG_SAW_IRQ; +	wusbhc = usb_hcd_to_wusbhc(usb_hcd); +	hwahc = container_of(wusbhc, struct hwahc, wusbhc); +	hwahc_init(hwahc); +	result = hwahc_create(hwahc, usb_iface); +	if (result < 0) { +		dev_err(dev, "Cannot initialize internals: %d\n", result); +		goto error_hwahc_create; +	} +	result = usb_add_hcd(usb_hcd, 0, 0); +	if (result < 0) { +		dev_err(dev, "Cannot add HCD: %d\n", result); +		goto error_add_hcd; +	} +	result = wusbhc_b_create(&hwahc->wusbhc); +	if (result < 0) { +		dev_err(dev, "Cannot setup phase B of WUSBHC: %d\n", result); +		goto error_wusbhc_b_create; +	} +	d_fnend(4, dev, "(%p, %p) = 0\n", usb_iface, id); +	return 0; + +error_wusbhc_b_create: +	usb_remove_hcd(usb_hcd); +error_add_hcd: +	hwahc_destroy(hwahc); +error_hwahc_create: +	usb_put_hcd(usb_hcd); +error_alloc: +	d_fnend(4, dev, "(%p, %p) = %d\n", usb_iface, id, result); +	return result; +} + +static void hwahc_disconnect(struct usb_interface *usb_iface) +{ +	struct usb_hcd *usb_hcd; +	struct wusbhc *wusbhc; +	struct hwahc *hwahc; + +	usb_hcd = usb_get_intfdata(usb_iface); +	wusbhc = usb_hcd_to_wusbhc(usb_hcd); +	hwahc = container_of(wusbhc, struct hwahc, wusbhc); + +	d_fnstart(1, NULL, "(hwahc %p [usb_iface %p])\n", hwahc, usb_iface); +	wusbhc_b_destroy(&hwahc->wusbhc); +	usb_remove_hcd(usb_hcd); +	hwahc_destroy(hwahc); +	usb_put_hcd(usb_hcd); +	d_fnend(1, NULL, "(hwahc %p [usb_iface %p]) = void\n", hwahc, +		usb_iface); +} + +/** USB device ID's that we handle */ +static struct usb_device_id hwahc_id_table[] = { +	/* FIXME: use class labels for this */ +	{ USB_INTERFACE_INFO(0xe0, 0x02, 0x01), }, +	{}, +}; +MODULE_DEVICE_TABLE(usb, hwahc_id_table); + +static struct usb_driver hwahc_driver = { +	.name =		"hwa-hc", +	.probe =	hwahc_probe, +	.disconnect =	hwahc_disconnect, +	.id_table =	hwahc_id_table, +}; + +static int __init hwahc_driver_init(void) +{ +	int result; +	result = usb_register(&hwahc_driver); +	if (result < 0) { +		printk(KERN_ERR "WA-CDS: Cannot register USB driver: %d\n", +		       result); +		goto error_usb_register; +	} +	return 0; + +error_usb_register: +	return result; + +} +module_init(hwahc_driver_init); + +static void __exit hwahc_driver_exit(void) +{ +	usb_deregister(&hwahc_driver); +} +module_exit(hwahc_driver_exit); + + +MODULE_AUTHOR("Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>"); +MODULE_DESCRIPTION("Host Wired Adapter USB Host Control Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/host/isp116x-hcd.c b/drivers/usb/host/isp116x-hcd.c index ce1ca0ba051..4dda31b2689 100644 --- a/drivers/usb/host/isp116x-hcd.c +++ b/drivers/usb/host/isp116x-hcd.c @@ -1562,11 +1562,12 @@ static int __devinit isp116x_probe(struct platform_device *pdev)  {  	struct usb_hcd *hcd;  	struct isp116x *isp116x; -	struct resource *addr, *data; +	struct resource *addr, *data, *ires;  	void __iomem *addr_reg;  	void __iomem *data_reg;  	int irq;  	int ret = 0; +	unsigned long irqflags;  	if (pdev->num_resources < 3) {  		ret = -ENODEV; @@ -1575,12 +1576,16 @@ static int __devinit isp116x_probe(struct platform_device *pdev)  	data = platform_get_resource(pdev, IORESOURCE_MEM, 0);  	addr = platform_get_resource(pdev, IORESOURCE_MEM, 1); -	irq = platform_get_irq(pdev, 0); -	if (!addr || !data || irq < 0) { +	ires = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + +	if (!addr || !data || !ires) {  		ret = -ENODEV;  		goto err1;  	} +	irq = ires->start; +	irqflags = ires->flags & IRQF_TRIGGER_MASK; +  	if (pdev->dev.dma_mask) {  		DBG("DMA not supported\n");  		ret = -EINVAL; @@ -1634,7 +1639,7 @@ static int __devinit isp116x_probe(struct platform_device *pdev)  		goto err6;  	} -	ret = usb_add_hcd(hcd, irq, IRQF_DISABLED); +	ret = usb_add_hcd(hcd, irq, irqflags | IRQF_DISABLED);  	if (ret)  		goto err6; diff --git a/drivers/usb/host/isp1760-if.c b/drivers/usb/host/isp1760-if.c index 051ef7b6bdc..b87ca7cf4b3 100644 --- a/drivers/usb/host/isp1760-if.c +++ b/drivers/usb/host/isp1760-if.c @@ -14,16 +14,16 @@  #include "../core/hcd.h"  #include "isp1760-hcd.h" -#ifdef CONFIG_USB_ISP1760_OF +#ifdef CONFIG_PPC_OF  #include <linux/of.h>  #include <linux/of_platform.h>  #endif -#ifdef CONFIG_USB_ISP1760_PCI +#ifdef CONFIG_PCI  #include <linux/pci.h>  #endif -#ifdef CONFIG_USB_ISP1760_OF +#ifdef CONFIG_PPC_OF  static int of_isp1760_probe(struct of_device *dev,  		const struct of_device_id *match)  { @@ -128,7 +128,7 @@ static struct of_platform_driver isp1760_of_driver = {  };  #endif -#ifdef CONFIG_USB_ISP1760_PCI +#ifdef CONFIG_PCI  static u32 nxp_pci_io_base;  static u32 iolength;  static u32 pci_mem_phy0; @@ -218,7 +218,7 @@ static int __devinit isp1761_pci_probe(struct pci_dev *dev,  	 * and reading back and checking the contents are same or not  	 */  	if (reg_data != 0xFACE) { -		err("scratch register mismatch %x", reg_data); +		dev_err(&dev->dev, "scratch register mismatch %x\n", reg_data);  		goto clean;  	} @@ -232,9 +232,10 @@ static int __devinit isp1761_pci_probe(struct pci_dev *dev,  	hcd = isp1760_register(pci_mem_phy0, length, dev->irq,  		IRQF_SHARED | IRQF_DISABLED, &dev->dev, dev_name(&dev->dev),  		devflags); -	pci_set_drvdata(dev, hcd); -	if (!hcd) +	if (!IS_ERR(hcd)) { +		pci_set_drvdata(dev, hcd);  		return 0; +	}  clean:  	status = -ENODEV;  	iounmap(iobase); @@ -287,28 +288,28 @@ static struct pci_driver isp1761_pci_driver = {  static int __init isp1760_init(void)  { -	int ret = -ENODEV; +	int ret;  	init_kmem_once(); -#ifdef CONFIG_USB_ISP1760_OF +#ifdef CONFIG_PPC_OF  	ret = of_register_platform_driver(&isp1760_of_driver);  	if (ret) {  		deinit_kmem_cache();  		return ret;  	}  #endif -#ifdef CONFIG_USB_ISP1760_PCI +#ifdef CONFIG_PCI  	ret = pci_register_driver(&isp1761_pci_driver);  	if (ret)  		goto unreg_of;  #endif  	return ret; -#ifdef CONFIG_USB_ISP1760_PCI +#ifdef CONFIG_PCI  unreg_of:  #endif -#ifdef CONFIG_USB_ISP1760_OF +#ifdef CONFIG_PPC_OF  	of_unregister_platform_driver(&isp1760_of_driver);  #endif  	deinit_kmem_cache(); @@ -318,10 +319,10 @@ module_init(isp1760_init);  static void __exit isp1760_exit(void)  { -#ifdef CONFIG_USB_ISP1760_OF +#ifdef CONFIG_PPC_OF  	of_unregister_platform_driver(&isp1760_of_driver);  #endif -#ifdef CONFIG_USB_ISP1760_PCI +#ifdef CONFIG_PCI  	pci_unregister_driver(&isp1761_pci_driver);  #endif  	deinit_kmem_cache(); diff --git a/drivers/usb/host/ohci-dbg.c b/drivers/usb/host/ohci-dbg.c index 7cef1d2f7cc..d3269656aa4 100644 --- a/drivers/usb/host/ohci-dbg.c +++ b/drivers/usb/host/ohci-dbg.c @@ -649,7 +649,7 @@ static ssize_t fill_registers_buffer(struct debug_buffer *buf)  	ohci_dbg_sw (ohci, &next, &size,  		"bus %s, device %s\n"  		"%s\n" -		"%s version " DRIVER_VERSION "\n", +		"%s\n",  		hcd->self.controller->bus->name,  		dev_name(hcd->self.controller),  		hcd->product_desc, diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index 89901962cbf..8aa3f4556a3 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -46,7 +46,6 @@  #include "../core/hcd.h" -#define DRIVER_VERSION "2006 August 04"  #define DRIVER_AUTHOR "Roman Weissgaerber, David Brownell"  #define DRIVER_DESC "USB 1.1 'Open' Host Controller (OHCI) Driver" @@ -984,10 +983,8 @@ static int ohci_restart (struct ohci_hcd *ohci)  /*-------------------------------------------------------------------------*/ -#define DRIVER_INFO DRIVER_VERSION " " DRIVER_DESC -  MODULE_AUTHOR (DRIVER_AUTHOR); -MODULE_DESCRIPTION (DRIVER_INFO); +MODULE_DESCRIPTION(DRIVER_DESC);  MODULE_LICENSE ("GPL");  #ifdef CONFIG_PCI @@ -1078,12 +1075,18 @@ MODULE_LICENSE ("GPL");  #define SM501_OHCI_DRIVER	ohci_hcd_sm501_driver  #endif +#ifdef CONFIG_MFD_TC6393XB +#include "ohci-tmio.c" +#define TMIO_OHCI_DRIVER	ohci_hcd_tmio_driver +#endif +  #if	!defined(PCI_DRIVER) &&		\  	!defined(PLATFORM_DRIVER) &&	\  	!defined(OF_PLATFORM_DRIVER) &&	\  	!defined(SA1111_DRIVER) &&	\  	!defined(PS3_SYSTEM_BUS_DRIVER) && \  	!defined(SM501_OHCI_DRIVER) && \ +	!defined(TMIO_OHCI_DRIVER) && \  	!defined(SSB_OHCI_DRIVER)  #error "missing bus glue for ohci-hcd"  #endif @@ -1095,9 +1098,10 @@ static int __init ohci_hcd_mod_init(void)  	if (usb_disabled())  		return -ENODEV; -	printk (KERN_DEBUG "%s: " DRIVER_INFO "\n", hcd_name); +	printk(KERN_INFO "%s: " DRIVER_DESC "\n", hcd_name);  	pr_debug ("%s: block sizes: ed %Zd td %Zd\n", hcd_name,  		sizeof (struct ed), sizeof (struct td)); +	set_bit(USB_OHCI_LOADED, &usb_hcds_loaded);  #ifdef DEBUG  	ohci_debug_root = debugfs_create_dir("ohci", NULL); @@ -1149,13 +1153,25 @@ static int __init ohci_hcd_mod_init(void)  		goto error_sm501;  #endif +#ifdef TMIO_OHCI_DRIVER +	retval = platform_driver_register(&TMIO_OHCI_DRIVER); +	if (retval < 0) +		goto error_tmio; +#endif +  	return retval;  	/* Error path */ +#ifdef TMIO_OHCI_DRIVER +	platform_driver_unregister(&TMIO_OHCI_DRIVER); + error_tmio: +#endif  #ifdef SM501_OHCI_DRIVER +	platform_driver_unregister(&SM501_OHCI_DRIVER);   error_sm501:  #endif  #ifdef SSB_OHCI_DRIVER +	ssb_driver_unregister(&SSB_OHCI_DRIVER);   error_ssb:  #endif  #ifdef PCI_DRIVER @@ -1184,12 +1200,16 @@ static int __init ohci_hcd_mod_init(void)   error_debug:  #endif +	clear_bit(USB_OHCI_LOADED, &usb_hcds_loaded);  	return retval;  }  module_init(ohci_hcd_mod_init);  static void __exit ohci_hcd_mod_exit(void)  { +#ifdef TMIO_OHCI_DRIVER +	platform_driver_unregister(&TMIO_OHCI_DRIVER); +#endif  #ifdef SM501_OHCI_DRIVER  	platform_driver_unregister(&SM501_OHCI_DRIVER);  #endif @@ -1214,6 +1234,7 @@ static void __exit ohci_hcd_mod_exit(void)  #ifdef DEBUG  	debugfs_remove(ohci_debug_root);  #endif +	clear_bit(USB_OHCI_LOADED, &usb_hcds_loaded);  }  module_exit(ohci_hcd_mod_exit); diff --git a/drivers/usb/host/ohci-hub.c b/drivers/usb/host/ohci-hub.c index 7ea9a7b3115..32bbce9718f 100644 --- a/drivers/usb/host/ohci-hub.c +++ b/drivers/usb/host/ohci-hub.c @@ -359,21 +359,24 @@ static void ohci_finish_controller_resume(struct usb_hcd *hcd)  /* Carry out polling-, autostop-, and autoresume-related state changes */  static int ohci_root_hub_state_changes(struct ohci_hcd *ohci, int changed, -		int any_connected) +		int any_connected, int rhsc_status)  {  	int	poll_rh = 1; -	int	rhsc; +	int	rhsc_enable; -	rhsc = ohci_readl(ohci, &ohci->regs->intrenable) & OHCI_INTR_RHSC; -	switch (ohci->hc_control & OHCI_CTRL_HCFS) { +	/* Some broken controllers never turn off RHCS in the interrupt +	 * status register.  For their sake we won't re-enable RHSC +	 * interrupts if the interrupt bit is already active. +	 */ +	rhsc_enable = ohci_readl(ohci, &ohci->regs->intrenable) & +			OHCI_INTR_RHSC; +	switch (ohci->hc_control & OHCI_CTRL_HCFS) {  	case OHCI_USB_OPER: -		/* If no status changes are pending, enable status-change -		 * interrupts. -		 */ -		if (!rhsc && !changed) { -			rhsc = OHCI_INTR_RHSC; -			ohci_writel(ohci, rhsc, &ohci->regs->intrenable); +		/* If no status changes are pending, enable RHSC interrupts. */ +		if (!rhsc_enable && !rhsc_status && !changed) { +			rhsc_enable = OHCI_INTR_RHSC; +			ohci_writel(ohci, rhsc_enable, &ohci->regs->intrenable);  		}  		/* Keep on polling until we know a device is connected @@ -383,7 +386,7 @@ static int ohci_root_hub_state_changes(struct ohci_hcd *ohci, int changed,  			if (any_connected ||  					!device_may_wakeup(&ohci_to_hcd(ohci)  						->self.root_hub->dev)) { -				if (rhsc) +				if (rhsc_enable)  					poll_rh = 0;  			} else {  				ohci->autostop = 1; @@ -396,34 +399,45 @@ static int ohci_root_hub_state_changes(struct ohci_hcd *ohci, int changed,  				ohci->autostop = 0;  				ohci->next_statechange = jiffies +  						STATECHANGE_DELAY; -			} else if (rhsc && time_after_eq(jiffies, +			} else if (time_after_eq(jiffies,  						ohci->next_statechange)  					&& !ohci->ed_rm_list  					&& !(ohci->hc_control &  						OHCI_SCHED_ENABLES)) {  				ohci_rh_suspend(ohci, 1); -				poll_rh = 0; +				if (rhsc_enable) +					poll_rh = 0;  			}  		}  		break; -	/* if there is a port change, autostart or ask to be resumed */  	case OHCI_USB_SUSPEND:  	case OHCI_USB_RESUME: +		/* if there is a port change, autostart or ask to be resumed */  		if (changed) {  			if (ohci->autostop)  				ohci_rh_resume(ohci);  			else  				usb_hcd_resume_root_hub(ohci_to_hcd(ohci)); -		} else { -			if (!rhsc && (ohci->autostop || -					ohci_to_hcd(ohci)->self.root_hub-> -						do_remote_wakeup)) -				ohci_writel(ohci, OHCI_INTR_RHSC, -						&ohci->regs->intrenable); -			/* everything is idle, no need for polling */ +		/* If remote wakeup is disabled, stop polling */ +		} else if (!ohci->autostop && +				!ohci_to_hcd(ohci)->self.root_hub-> +					do_remote_wakeup) {  			poll_rh = 0; + +		} else { +			/* If no status changes are pending, +			 * enable RHSC interrupts +			 */ +			if (!rhsc_enable && !rhsc_status) { +				rhsc_enable = OHCI_INTR_RHSC; +				ohci_writel(ohci, rhsc_enable, +						&ohci->regs->intrenable); +			} +			/* Keep polling until RHSC is enabled */ +			if (rhsc_enable) +				poll_rh = 0;  		}  		break;  	} @@ -441,18 +455,22 @@ static inline int ohci_rh_resume(struct ohci_hcd *ohci)   * autostop isn't used when CONFIG_PM is turned off.   */  static int ohci_root_hub_state_changes(struct ohci_hcd *ohci, int changed, -		int any_connected) +		int any_connected, int rhsc_status)  {  	/* If RHSC is enabled, don't poll */  	if (ohci_readl(ohci, &ohci->regs->intrenable) & OHCI_INTR_RHSC)  		return 0; -	/* If no status changes are pending, enable status-change interrupts */ -	if (!changed) { -		ohci_writel(ohci, OHCI_INTR_RHSC, &ohci->regs->intrenable); -		return 0; -	} -	return 1; +	/* If status changes are pending, continue polling. +	 * Conversely, if no status changes are pending but the RHSC +	 * status bit was set, then RHSC may be broken so continue polling. +	 */ +	if (changed || rhsc_status) +		return 1; + +	/* It's safe to re-enable RHSC interrupts */ +	ohci_writel(ohci, OHCI_INTR_RHSC, &ohci->regs->intrenable); +	return 0;  }  #endif	/* CONFIG_PM */ @@ -467,6 +485,7 @@ ohci_hub_status_data (struct usb_hcd *hcd, char *buf)  	struct ohci_hcd	*ohci = hcd_to_ohci (hcd);  	int		i, changed = 0, length = 1;  	int		any_connected = 0; +	int		rhsc_status;  	unsigned long	flags;  	spin_lock_irqsave (&ohci->lock, flags); @@ -492,12 +511,10 @@ ohci_hub_status_data (struct usb_hcd *hcd, char *buf)  		length++;  	} -	/* Some broken controllers never turn off RHCS in the interrupt -	 * status register.  For their sake we won't re-enable RHSC -	 * interrupts if the flag is already set. -	 */ -	if (ohci_readl(ohci, &ohci->regs->intrstatus) & OHCI_INTR_RHSC) -		changed = 1; +	/* Clear the RHSC status flag before reading the port statuses */ +	ohci_writel(ohci, OHCI_INTR_RHSC, &ohci->regs->intrstatus); +	rhsc_status = ohci_readl(ohci, &ohci->regs->intrstatus) & +			OHCI_INTR_RHSC;  	/* look at each port */  	for (i = 0; i < ohci->num_ports; i++) { @@ -517,7 +534,7 @@ ohci_hub_status_data (struct usb_hcd *hcd, char *buf)  	}  	hcd->poll_rh = ohci_root_hub_state_changes(ohci, changed, -			any_connected); +			any_connected, rhsc_status);  done:  	spin_unlock_irqrestore (&ohci->lock, flags); diff --git a/drivers/usb/host/ohci-omap.c b/drivers/usb/host/ohci-omap.c index 95b3ec89c12..91697bdb399 100644 --- a/drivers/usb/host/ohci-omap.c +++ b/drivers/usb/host/ohci-omap.c @@ -231,7 +231,7 @@ static int ohci_omap_init(struct usb_hcd *hcd)  	omap_ohci_clock_power(1); -	if (cpu_is_omap1510()) { +	if (cpu_is_omap15xx()) {  		omap_1510_local_bus_power(1);  		omap_1510_local_bus_init();  	} @@ -319,7 +319,7 @@ static int usb_hcd_omap_probe (const struct hc_driver *driver,  	if (IS_ERR(usb_host_ck))  		return PTR_ERR(usb_host_ck); -	if (!cpu_is_omap1510()) +	if (!cpu_is_omap15xx())  		usb_dc_ck = clk_get(0, "usb_dc_ck");  	else  		usb_dc_ck = clk_get(0, "lb_ck"); @@ -344,7 +344,12 @@ static int usb_hcd_omap_probe (const struct hc_driver *driver,  		goto err1;  	} -	hcd->regs = (void __iomem *) (int) IO_ADDRESS(hcd->rsrc_start); +	hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len); +	if (!hcd->regs) { +		dev_err(&pdev->dev, "can't ioremap OHCI HCD\n"); +		retval = -ENOMEM; +		goto err2; +	}  	ohci = hcd_to_ohci(hcd);  	ohci_hcd_init(ohci); @@ -355,11 +360,11 @@ static int usb_hcd_omap_probe (const struct hc_driver *driver,  	irq = platform_get_irq(pdev, 0);  	if (irq < 0) {  		retval = -ENXIO; -		goto err2; +		goto err3;  	}  	retval = usb_add_hcd(hcd, irq, IRQF_DISABLED);  	if (retval) -		goto err2; +		goto err3;  	host_initialized = 1; @@ -367,6 +372,8 @@ static int usb_hcd_omap_probe (const struct hc_driver *driver,  		omap_ohci_clock_power(0);  	return 0; +err3: +	iounmap(hcd->regs);  err2:  	release_mem_region(hcd->rsrc_start, hcd->rsrc_len);  err1: @@ -401,6 +408,7 @@ usb_hcd_omap_remove (struct usb_hcd *hcd, struct platform_device *pdev)  	}  	if (machine_is_omap_osk())  		omap_free_gpio(9); +	iounmap(hcd->regs);  	release_mem_region(hcd->rsrc_start, hcd->rsrc_len);  	usb_put_hcd(hcd);  	clk_put(usb_dc_ck); diff --git a/drivers/usb/host/ohci-pnx4008.c b/drivers/usb/host/ohci-pnx4008.c index 658a2a978c3..e306ca6aef3 100644 --- a/drivers/usb/host/ohci-pnx4008.c +++ b/drivers/usb/host/ohci-pnx4008.c @@ -331,7 +331,7 @@ static int __devinit usb_hcd_pnx4008_probe(struct platform_device *pdev)  	int ret = 0, irq; -	dev_dbg(&pdev->dev, "%s: " DRIVER_INFO " (pnx4008)\n", hcd_name); +	dev_dbg(&pdev->dev, "%s: " DRIVER_DESC " (pnx4008)\n", hcd_name);  	if (usb_disabled()) {  		err("USB is disabled");  		ret = -ENODEV; diff --git a/drivers/usb/host/ohci-ps3.c b/drivers/usb/host/ohci-ps3.c index 2089d8a46c4..3c1a3b5f89f 100644 --- a/drivers/usb/host/ohci-ps3.c +++ b/drivers/usb/host/ohci-ps3.c @@ -192,7 +192,7 @@ fail_start:  	return result;  } -static int ps3_ohci_remove (struct ps3_system_bus_device *dev) +static int ps3_ohci_remove(struct ps3_system_bus_device *dev)  {  	unsigned int tmp;  	struct usb_hcd *hcd = @@ -205,6 +205,7 @@ static int ps3_ohci_remove (struct ps3_system_bus_device *dev)  	tmp = hcd->irq; +	ohci_shutdown(hcd);  	usb_remove_hcd(hcd);  	ps3_system_bus_set_driver_data(dev, NULL); diff --git a/drivers/usb/host/ohci-pxa27x.c b/drivers/usb/host/ohci-pxa27x.c index 7f0f35c7818..e294d430733 100644 --- a/drivers/usb/host/ohci-pxa27x.c +++ b/drivers/usb/host/ohci-pxa27x.c @@ -23,17 +23,90 @@  #include <linux/signal.h>  #include <linux/platform_device.h>  #include <linux/clk.h> - -#include <mach/hardware.h> -#include <mach/pxa-regs.h> -#include <mach/pxa2xx-regs.h> /* FIXME: for PSSR */  #include <mach/ohci.h> +/* + * UHC: USB Host Controller (OHCI-like) register definitions + */ +#define UHCREV		(0x0000) /* UHC HCI Spec Revision */ +#define UHCHCON		(0x0004) /* UHC Host Control Register */ +#define UHCCOMS		(0x0008) /* UHC Command Status Register */ +#define UHCINTS		(0x000C) /* UHC Interrupt Status Register */ +#define UHCINTE		(0x0010) /* UHC Interrupt Enable */ +#define UHCINTD		(0x0014) /* UHC Interrupt Disable */ +#define UHCHCCA		(0x0018) /* UHC Host Controller Comm. Area */ +#define UHCPCED		(0x001C) /* UHC Period Current Endpt Descr */ +#define UHCCHED		(0x0020) /* UHC Control Head Endpt Descr */ +#define UHCCCED		(0x0024) /* UHC Control Current Endpt Descr */ +#define UHCBHED		(0x0028) /* UHC Bulk Head Endpt Descr */ +#define UHCBCED		(0x002C) /* UHC Bulk Current Endpt Descr */ +#define UHCDHEAD	(0x0030) /* UHC Done Head */ +#define UHCFMI		(0x0034) /* UHC Frame Interval */ +#define UHCFMR		(0x0038) /* UHC Frame Remaining */ +#define UHCFMN		(0x003C) /* UHC Frame Number */ +#define UHCPERS		(0x0040) /* UHC Periodic Start */ +#define UHCLS		(0x0044) /* UHC Low Speed Threshold */ + +#define UHCRHDA		(0x0048) /* UHC Root Hub Descriptor A */ +#define UHCRHDA_NOCP	(1 << 12)	/* No over current protection */ +#define UHCRHDA_OCPM	(1 << 11)	/* Over Current Protection Mode */ +#define UHCRHDA_POTPGT(x) \ +			(((x) & 0xff) << 24) /* Power On To Power Good Time */ + +#define UHCRHDB		(0x004C) /* UHC Root Hub Descriptor B */ +#define UHCRHS		(0x0050) /* UHC Root Hub Status */ +#define UHCRHPS1	(0x0054) /* UHC Root Hub Port 1 Status */ +#define UHCRHPS2	(0x0058) /* UHC Root Hub Port 2 Status */ +#define UHCRHPS3	(0x005C) /* UHC Root Hub Port 3 Status */ + +#define UHCSTAT		(0x0060) /* UHC Status Register */ +#define UHCSTAT_UPS3	(1 << 16)	/* USB Power Sense Port3 */ +#define UHCSTAT_SBMAI	(1 << 15)	/* System Bus Master Abort Interrupt*/ +#define UHCSTAT_SBTAI	(1 << 14)	/* System Bus Target Abort Interrupt*/ +#define UHCSTAT_UPRI	(1 << 13)	/* USB Port Resume Interrupt */ +#define UHCSTAT_UPS2	(1 << 12)	/* USB Power Sense Port 2 */ +#define UHCSTAT_UPS1	(1 << 11)	/* USB Power Sense Port 1 */ +#define UHCSTAT_HTA	(1 << 10)	/* HCI Target Abort */ +#define UHCSTAT_HBA	(1 << 8)	/* HCI Buffer Active */ +#define UHCSTAT_RWUE	(1 << 7)	/* HCI Remote Wake Up Event */ + +#define UHCHR           (0x0064) /* UHC Reset Register */ +#define UHCHR_SSEP3	(1 << 11)	/* Sleep Standby Enable for Port3 */ +#define UHCHR_SSEP2	(1 << 10)	/* Sleep Standby Enable for Port2 */ +#define UHCHR_SSEP1	(1 << 9)	/* Sleep Standby Enable for Port1 */ +#define UHCHR_PCPL	(1 << 7)	/* Power control polarity low */ +#define UHCHR_PSPL	(1 << 6)	/* Power sense polarity low */ +#define UHCHR_SSE	(1 << 5)	/* Sleep Standby Enable */ +#define UHCHR_UIT	(1 << 4)	/* USB Interrupt Test */ +#define UHCHR_SSDC	(1 << 3)	/* Simulation Scale Down Clock */ +#define UHCHR_CGR	(1 << 2)	/* Clock Generation Reset */ +#define UHCHR_FHR	(1 << 1)	/* Force Host Controller Reset */ +#define UHCHR_FSBIR	(1 << 0)	/* Force System Bus Iface Reset */ + +#define UHCHIE          (0x0068) /* UHC Interrupt Enable Register*/ +#define UHCHIE_UPS3IE	(1 << 14)	/* Power Sense Port3 IntEn */ +#define UHCHIE_UPRIE	(1 << 13)	/* Port Resume IntEn */ +#define UHCHIE_UPS2IE	(1 << 12)	/* Power Sense Port2 IntEn */ +#define UHCHIE_UPS1IE	(1 << 11)	/* Power Sense Port1 IntEn */ +#define UHCHIE_TAIE	(1 << 10)	/* HCI Interface Transfer Abort +					   Interrupt Enable*/ +#define UHCHIE_HBAIE	(1 << 8)	/* HCI Buffer Active IntEn */ +#define UHCHIE_RWIE	(1 << 7)	/* Remote Wake-up IntEn */ + +#define UHCHIT          (0x006C) /* UHC Interrupt Test register */ +  #define PXA_UHC_MAX_PORTNUM    3 -#define UHCRHPS(x)              __REG2( 0x4C000050, (x)<<2 ) +struct pxa27x_ohci { +	/* must be 1st member here for hcd_to_ohci() to work */ +	struct ohci_hcd ohci; + +	struct device	*dev; +	struct clk	*clk; +	void __iomem	*mmio_base; +}; -static struct clk *usb_clk; +#define to_pxa27x_ohci(hcd)	(struct pxa27x_ohci *)hcd_to_ohci(hcd)  /*    PMM_NPS_MODE -- PMM Non-power switching mode @@ -45,30 +118,35 @@ static struct clk *usb_clk;    PMM_PERPORT_MODE -- PMM per port switching mode        Ports are powered individually.   */ -static int pxa27x_ohci_select_pmm( int mode ) +static int pxa27x_ohci_select_pmm(struct pxa27x_ohci *ohci, int mode)  { -	switch ( mode ) { +	uint32_t uhcrhda = __raw_readl(ohci->mmio_base + UHCRHDA); +	uint32_t uhcrhdb = __raw_readl(ohci->mmio_base + UHCRHDB); + +	switch (mode) {  	case PMM_NPS_MODE: -		UHCRHDA |= RH_A_NPS; +		uhcrhda |= RH_A_NPS;  		break;  	case PMM_GLOBAL_MODE: -		UHCRHDA &= ~(RH_A_NPS & RH_A_PSM); +		uhcrhda &= ~(RH_A_NPS & RH_A_PSM);  		break;  	case PMM_PERPORT_MODE: -		UHCRHDA &= ~(RH_A_NPS); -		UHCRHDA |= RH_A_PSM; +		uhcrhda &= ~(RH_A_NPS); +		uhcrhda |= RH_A_PSM;  		/* Set port power control mask bits, only 3 ports. */ -		UHCRHDB |= (0x7<<17); +		uhcrhdb |= (0x7<<17);  		break;  	default:  		printk( KERN_ERR  			"Invalid mode %d, set to non-power switch mode.\n",  			mode ); -		UHCRHDA |= RH_A_NPS; +		uhcrhda |= RH_A_NPS;  	} +	__raw_writel(uhcrhda, ohci->mmio_base + UHCRHDA); +	__raw_writel(uhcrhdb, ohci->mmio_base + UHCRHDB);  	return 0;  } @@ -76,57 +154,110 @@ extern int usb_disabled(void);  /*-------------------------------------------------------------------------*/ -static int pxa27x_start_hc(struct device *dev) +static inline void pxa27x_setup_hc(struct pxa27x_ohci *ohci, +				   struct pxaohci_platform_data *inf) +{ +	uint32_t uhchr = __raw_readl(ohci->mmio_base + UHCHR); +	uint32_t uhcrhda = __raw_readl(ohci->mmio_base + UHCRHDA); + +	if (inf->flags & ENABLE_PORT1) +		uhchr &= ~UHCHR_SSEP1; + +	if (inf->flags & ENABLE_PORT2) +		uhchr &= ~UHCHR_SSEP2; + +	if (inf->flags & ENABLE_PORT3) +		uhchr &= ~UHCHR_SSEP3; + +	if (inf->flags & POWER_CONTROL_LOW) +		uhchr |= UHCHR_PCPL; + +	if (inf->flags & POWER_SENSE_LOW) +		uhchr |= UHCHR_PSPL; + +	if (inf->flags & NO_OC_PROTECTION) +		uhcrhda |= UHCRHDA_NOCP; + +	if (inf->flags & OC_MODE_PERPORT) +		uhcrhda |= UHCRHDA_OCPM; + +	if (inf->power_on_delay) { +		uhcrhda &= ~UHCRHDA_POTPGT(0xff); +		uhcrhda |= UHCRHDA_POTPGT(inf->power_on_delay / 2); +	} + +	__raw_writel(uhchr, ohci->mmio_base + UHCHR); +	__raw_writel(uhcrhda, ohci->mmio_base + UHCRHDA); +} + +static inline void pxa27x_reset_hc(struct pxa27x_ohci *ohci) +{ +	uint32_t uhchr = __raw_readl(ohci->mmio_base + UHCHR); + +	__raw_writel(uhchr | UHCHR_FHR, ohci->mmio_base + UHCHR); +	udelay(11); +	__raw_writel(uhchr & ~UHCHR_FHR, ohci->mmio_base + UHCHR); +} + +#ifdef CONFIG_CPU_PXA27x +extern void pxa27x_clear_otgph(void); +#else +#define pxa27x_clear_otgph()	do {} while (0) +#endif + +static int pxa27x_start_hc(struct pxa27x_ohci *ohci, struct device *dev)  {  	int retval = 0;  	struct pxaohci_platform_data *inf; +	uint32_t uhchr;  	inf = dev->platform_data; -	clk_enable(usb_clk); +	clk_enable(ohci->clk); -	UHCHR |= UHCHR_FHR; -	udelay(11); -	UHCHR &= ~UHCHR_FHR; +	pxa27x_reset_hc(ohci); + +	uhchr = __raw_readl(ohci->mmio_base + UHCHR) | UHCHR_FSBIR; +	__raw_writel(uhchr, ohci->mmio_base + UHCHR); -	UHCHR |= UHCHR_FSBIR; -	while (UHCHR & UHCHR_FSBIR) +	while (__raw_readl(ohci->mmio_base + UHCHR) & UHCHR_FSBIR)  		cpu_relax(); +	pxa27x_setup_hc(ohci, inf); +  	if (inf->init)  		retval = inf->init(dev);  	if (retval < 0)  		return retval; -	UHCHR &= ~UHCHR_SSE; - -	UHCHIE = (UHCHIE_UPRIE | UHCHIE_RWIE); +	uhchr = __raw_readl(ohci->mmio_base + UHCHR) & ~UHCHR_SSE; +	__raw_writel(uhchr, ohci->mmio_base + UHCHR); +	__raw_writel(UHCHIE_UPRIE | UHCHIE_RWIE, ohci->mmio_base + UHCHIE);  	/* Clear any OTG Pin Hold */ -	if (cpu_is_pxa27x() && (PSSR & PSSR_OTGPH)) -		PSSR |= PSSR_OTGPH; - +	pxa27x_clear_otgph();  	return 0;  } -static void pxa27x_stop_hc(struct device *dev) +static void pxa27x_stop_hc(struct pxa27x_ohci *ohci, struct device *dev)  {  	struct pxaohci_platform_data *inf; +	uint32_t uhccoms;  	inf = dev->platform_data;  	if (inf->exit)  		inf->exit(dev); -	UHCHR |= UHCHR_FHR; -	udelay(11); -	UHCHR &= ~UHCHR_FHR; +	pxa27x_reset_hc(ohci); -	UHCCOMS |= 1; +	/* Host Controller Reset */ +	uhccoms = __raw_readl(ohci->mmio_base + UHCCOMS) | 0x01; +	__raw_writel(uhccoms, ohci->mmio_base + UHCCOMS);  	udelay(10); -	clk_disable(usb_clk); +	clk_disable(ohci->clk);  } @@ -147,18 +278,22 @@ static void pxa27x_stop_hc(struct device *dev)   */  int usb_hcd_pxa27x_probe (const struct hc_driver *driver, struct platform_device *pdev)  { -	int retval; +	int retval, irq;  	struct usb_hcd *hcd;  	struct pxaohci_platform_data *inf; +	struct pxa27x_ohci *ohci; +	struct resource *r; +	struct clk *usb_clk;  	inf = pdev->dev.platform_data;  	if (!inf)  		return -ENODEV; -	if (pdev->resource[1].flags != IORESOURCE_IRQ) { -		pr_debug ("resource[1] is not IORESOURCE_IRQ"); -		return -ENOMEM; +	irq = platform_get_irq(pdev, 0); +	if (irq < 0) { +		pr_err("no resource of IORESOURCE_IRQ"); +		return -ENXIO;  	}  	usb_clk = clk_get(&pdev->dev, "USBCLK"); @@ -168,8 +303,16 @@ int usb_hcd_pxa27x_probe (const struct hc_driver *driver, struct platform_device  	hcd = usb_create_hcd (driver, &pdev->dev, "pxa27x");  	if (!hcd)  		return -ENOMEM; -	hcd->rsrc_start = pdev->resource[0].start; -	hcd->rsrc_len = pdev->resource[0].end - pdev->resource[0].start + 1; + +	r = platform_get_resource(pdev, IORESOURCE_MEM, 0); +	if (!r) { +		pr_err("no resource of IORESOURCE_MEM"); +		retval = -ENXIO; +		goto err1; +	} + +	hcd->rsrc_start = r->start; +	hcd->rsrc_len = resource_size(r);  	if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {  		pr_debug("request_mem_region failed"); @@ -184,24 +327,30 @@ int usb_hcd_pxa27x_probe (const struct hc_driver *driver, struct platform_device  		goto err2;  	} -	if ((retval = pxa27x_start_hc(&pdev->dev)) < 0) { +	/* initialize "struct pxa27x_ohci" */ +	ohci = (struct pxa27x_ohci *)hcd_to_ohci(hcd); +	ohci->dev = &pdev->dev; +	ohci->clk = usb_clk; +	ohci->mmio_base = (void __iomem *)hcd->regs; + +	if ((retval = pxa27x_start_hc(ohci, &pdev->dev)) < 0) {  		pr_debug("pxa27x_start_hc failed");  		goto err3;  	}  	/* Select Power Management Mode */ -	pxa27x_ohci_select_pmm(inf->port_mode); +	pxa27x_ohci_select_pmm(ohci, inf->port_mode);  	if (inf->power_budget)  		hcd->power_budget = inf->power_budget;  	ohci_hcd_init(hcd_to_ohci(hcd)); -	retval = usb_add_hcd(hcd, pdev->resource[1].start, IRQF_DISABLED); +	retval = usb_add_hcd(hcd, irq, IRQF_DISABLED);  	if (retval == 0)  		return retval; -	pxa27x_stop_hc(&pdev->dev); +	pxa27x_stop_hc(ohci, &pdev->dev);   err3:  	iounmap(hcd->regs);   err2: @@ -228,12 +377,14 @@ int usb_hcd_pxa27x_probe (const struct hc_driver *driver, struct platform_device   */  void usb_hcd_pxa27x_remove (struct usb_hcd *hcd, struct platform_device *pdev)  { +	struct pxa27x_ohci *ohci = to_pxa27x_ohci(hcd); +  	usb_remove_hcd(hcd); -	pxa27x_stop_hc(&pdev->dev); +	pxa27x_stop_hc(ohci, &pdev->dev);  	iounmap(hcd->regs);  	release_mem_region(hcd->rsrc_start, hcd->rsrc_len);  	usb_put_hcd(hcd); -	clk_put(usb_clk); +	clk_put(ohci->clk);  }  /*-------------------------------------------------------------------------*/ @@ -266,7 +417,7 @@ ohci_pxa27x_start (struct usb_hcd *hcd)  static const struct hc_driver ohci_pxa27x_hc_driver = {  	.description =		hcd_name,  	.product_desc =		"PXA27x OHCI", -	.hcd_priv_size =	sizeof(struct ohci_hcd), +	.hcd_priv_size =	sizeof(struct pxa27x_ohci),  	/*  	 * generic hardware linkage @@ -330,13 +481,13 @@ static int ohci_hcd_pxa27x_drv_remove(struct platform_device *pdev)  static int ohci_hcd_pxa27x_drv_suspend(struct platform_device *pdev, pm_message_t state)  {  	struct usb_hcd *hcd = platform_get_drvdata(pdev); -	struct ohci_hcd *ohci = hcd_to_ohci(hcd); +	struct pxa27x_ohci *ohci = to_pxa27x_ohci(hcd); -	if (time_before(jiffies, ohci->next_statechange)) +	if (time_before(jiffies, ohci->ohci.next_statechange))  		msleep(5); -	ohci->next_statechange = jiffies; +	ohci->ohci.next_statechange = jiffies; -	pxa27x_stop_hc(&pdev->dev); +	pxa27x_stop_hc(ohci, &pdev->dev);  	hcd->state = HC_STATE_SUSPENDED;  	return 0; @@ -345,14 +496,14 @@ static int ohci_hcd_pxa27x_drv_suspend(struct platform_device *pdev, pm_message_  static int ohci_hcd_pxa27x_drv_resume(struct platform_device *pdev)  {  	struct usb_hcd *hcd = platform_get_drvdata(pdev); -	struct ohci_hcd *ohci = hcd_to_ohci(hcd); +	struct pxa27x_ohci *ohci = to_pxa27x_ohci(hcd);  	int status; -	if (time_before(jiffies, ohci->next_statechange)) +	if (time_before(jiffies, ohci->ohci.next_statechange))  		msleep(5); -	ohci->next_statechange = jiffies; +	ohci->ohci.next_statechange = jiffies; -	if ((status = pxa27x_start_hc(&pdev->dev)) < 0) +	if ((status = pxa27x_start_hc(ohci, &pdev->dev)) < 0)  		return status;  	ohci_finish_controller_resume(hcd); diff --git a/drivers/usb/host/ohci-tmio.c b/drivers/usb/host/ohci-tmio.c new file mode 100644 index 00000000000..f9f134af0bd --- /dev/null +++ b/drivers/usb/host/ohci-tmio.c @@ -0,0 +1,376 @@ +/* + * OHCI HCD(Host Controller Driver) for USB. + * + *(C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at> + *(C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net> + *(C) Copyright 2002 Hewlett-Packard Company + * + * Bus glue for Toshiba Mobile IO(TMIO) Controller's OHCI core + * (C) Copyright 2005 Chris Humbert <mahadri-usb@drigon.com> + * (C) Copyright 2007, 2008 Dmitry Baryshkov <dbaryshkov@gmail.com> + * + * This is known to work with the following variants: + *	TC6393XB revision 3	(32kB SRAM) + * + * The TMIO's OHCI core DMAs through a small internal buffer that + * is directly addressable by the CPU. + * + * Written from sparse documentation from Toshiba and Sharp's driver + * for the 2.4 kernel, + *	usb-ohci-tc6393.c(C) Copyright 2004 Lineo Solutions, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +/*#include <linux/fs.h> +#include <linux/mount.h> +#include <linux/pagemap.h> +#include <linux/init.h> +#include <linux/namei.h> +#include <linux/sched.h>*/ +#include <linux/platform_device.h> +#include <linux/mfd/core.h> +#include <linux/mfd/tmio.h> +#include <linux/dma-mapping.h> + +/*-------------------------------------------------------------------------*/ + +/* + * USB Host Controller Configuration Register + */ +#define CCR_REVID	0x08	/* b Revision ID				*/ +#define CCR_BASE	0x10	/* l USB Control Register Base Address Low	*/ +#define CCR_ILME	0x40	/* b Internal Local Memory Enable		*/ +#define CCR_PM		0x4c	/* w Power Management			*/ +#define CCR_INTC	0x50	/* b INT Control				*/ +#define CCR_LMW1L	0x54	/* w Local Memory Window 1 LMADRS Low	*/ +#define CCR_LMW1H	0x56	/* w Local Memory Window 1 LMADRS High	*/ +#define CCR_LMW1BL	0x58	/* w Local Memory Window 1 Base Address Low	*/ +#define CCR_LMW1BH	0x5A	/* w Local Memory Window 1 Base Address High	*/ +#define CCR_LMW2L	0x5C	/* w Local Memory Window 2 LMADRS Low	*/ +#define CCR_LMW2H	0x5E	/* w Local Memory Window 2 LMADRS High	*/ +#define CCR_LMW2BL	0x60	/* w Local Memory Window 2 Base Address Low	*/ +#define CCR_LMW2BH	0x62	/* w Local Memory Window 2 Base Address High	*/ +#define CCR_MISC	0xFC	/* b MISC					*/ + +#define CCR_PM_GKEN      0x0001 +#define CCR_PM_CKRNEN    0x0002 +#define CCR_PM_USBPW1    0x0004 +#define CCR_PM_USBPW2    0x0008 +#define CCR_PM_USBPW3    0x0008 +#define CCR_PM_PMEE      0x0100 +#define CCR_PM_PMES      0x8000 + +/*-------------------------------------------------------------------------*/ + +struct tmio_hcd { +	void __iomem		*ccr; +	spinlock_t		lock; /* protects RMW cycles */ +}; + +#define hcd_to_tmio(hcd)	((struct tmio_hcd *)(hcd_to_ohci(hcd) + 1)) + +/*-------------------------------------------------------------------------*/ + +static void tmio_write_pm(struct platform_device *dev) +{ +	struct usb_hcd *hcd = platform_get_drvdata(dev); +	struct tmio_hcd *tmio = hcd_to_tmio(hcd); +	u16 pm; +	unsigned long flags; + +	spin_lock_irqsave(&tmio->lock, flags); + +	pm = CCR_PM_GKEN | CCR_PM_CKRNEN | +	     CCR_PM_PMEE | CCR_PM_PMES; + +	tmio_iowrite16(pm, tmio->ccr + CCR_PM); +	spin_unlock_irqrestore(&tmio->lock, flags); +} + +static void tmio_stop_hc(struct platform_device *dev) +{ +	struct usb_hcd *hcd = platform_get_drvdata(dev); +	struct ohci_hcd *ohci = hcd_to_ohci(hcd); +	struct tmio_hcd *tmio = hcd_to_tmio(hcd); +	u16 pm; + +	pm = CCR_PM_GKEN | CCR_PM_CKRNEN; +	switch (ohci->num_ports) { +		default: +			dev_err(&dev->dev, "Unsupported amount of ports: %d\n", ohci->num_ports); +		case 3: +			pm |= CCR_PM_USBPW3; +		case 2: +			pm |= CCR_PM_USBPW2; +		case 1: +			pm |= CCR_PM_USBPW1; +	} +	tmio_iowrite8(0, tmio->ccr + CCR_INTC); +	tmio_iowrite8(0, tmio->ccr + CCR_ILME); +	tmio_iowrite16(0, tmio->ccr + CCR_BASE); +	tmio_iowrite16(0, tmio->ccr + CCR_BASE + 2); +	tmio_iowrite16(pm, tmio->ccr + CCR_PM); +} + +static void tmio_start_hc(struct platform_device *dev) +{ +	struct usb_hcd *hcd = platform_get_drvdata(dev); +	struct tmio_hcd *tmio = hcd_to_tmio(hcd); +	unsigned long base = hcd->rsrc_start; + +	tmio_write_pm(dev); +	tmio_iowrite16(base, tmio->ccr + CCR_BASE); +	tmio_iowrite16(base >> 16, tmio->ccr + CCR_BASE + 2); +	tmio_iowrite8(1, tmio->ccr + CCR_ILME); +	tmio_iowrite8(2, tmio->ccr + CCR_INTC); + +	dev_info(&dev->dev, "revision %d @ 0x%08llx, irq %d\n", +			tmio_ioread8(tmio->ccr + CCR_REVID), hcd->rsrc_start, hcd->irq); +} + +static int ohci_tmio_start(struct usb_hcd *hcd) +{ +	struct ohci_hcd *ohci = hcd_to_ohci(hcd); +	int ret; + +	if ((ret = ohci_init(ohci)) < 0) +		return ret; + +	if ((ret = ohci_run(ohci)) < 0) { +		err("can't start %s", hcd->self.bus_name); +		ohci_stop(hcd); +		return ret; +	} + +	return 0; +} + +static const struct hc_driver ohci_tmio_hc_driver = { +	.description =		hcd_name, +	.product_desc =		"TMIO OHCI USB Host Controller", +	.hcd_priv_size =	sizeof(struct ohci_hcd) + sizeof (struct tmio_hcd), + +	/* generic hardware linkage */ +	.irq =			ohci_irq, +	.flags =		HCD_USB11 | HCD_MEMORY | HCD_LOCAL_MEM, + +	/* basic lifecycle operations */ +	.start =		ohci_tmio_start, +	.stop =			ohci_stop, +	.shutdown =		ohci_shutdown, + +	/* managing i/o requests and associated device resources */ +	.urb_enqueue =		ohci_urb_enqueue, +	.urb_dequeue =		ohci_urb_dequeue, +	.endpoint_disable =	ohci_endpoint_disable, + +	/* scheduling support */ +	.get_frame_number =	ohci_get_frame, + +	/* root hub support */ +	.hub_status_data =	ohci_hub_status_data, +	.hub_control =		ohci_hub_control, +#ifdef	CONFIG_PM +	.bus_suspend =		ohci_bus_suspend, +	.bus_resume =		ohci_bus_resume, +#endif +	.start_port_reset =	ohci_start_port_reset, +}; + +/*-------------------------------------------------------------------------*/ +static struct platform_driver ohci_hcd_tmio_driver; + +static int __devinit ohci_hcd_tmio_drv_probe(struct platform_device *dev) +{ +	struct mfd_cell *cell = dev->dev.platform_data; +	struct resource *regs = platform_get_resource(dev, IORESOURCE_MEM, 0); +	struct resource *config = platform_get_resource(dev, IORESOURCE_MEM, 1); +	struct resource *sram = platform_get_resource(dev, IORESOURCE_MEM, 2); +	int irq = platform_get_irq(dev, 0); +	struct tmio_hcd *tmio; +	struct ohci_hcd *ohci; +	struct usb_hcd *hcd; +	int ret; + +	if (usb_disabled()) +		return -ENODEV; + +	if (!cell) +		return -EINVAL; + +	hcd = usb_create_hcd(&ohci_tmio_hc_driver, &dev->dev, dev->dev.bus_id); +	if (!hcd) { +		ret = -ENOMEM; +		goto err_usb_create_hcd; +	} + +	hcd->rsrc_start = regs->start; +	hcd->rsrc_len = regs->end - regs->start + 1; + +	tmio = hcd_to_tmio(hcd); + +	spin_lock_init(&tmio->lock); + +	tmio->ccr = ioremap(config->start, config->end - config->start + 1); +	if (!tmio->ccr) { +		ret = -ENOMEM; +		goto err_ioremap_ccr; +	} + +	hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len); +	if (!hcd->regs) { +		ret = -ENOMEM; +		goto err_ioremap_regs; +	} + +	if (!dma_declare_coherent_memory(&dev->dev, sram->start, +				sram->start, +				sram->end - sram->start + 1, +				DMA_MEMORY_MAP | DMA_MEMORY_EXCLUSIVE)) { +		ret = -EBUSY; +		goto err_dma_declare; +	} + +	if (cell->enable) { +		ret = cell->enable(dev); +		if (ret) +			goto err_enable; +	} + +	tmio_start_hc(dev); +	ohci = hcd_to_ohci(hcd); +	ohci_hcd_init(ohci); + +	ret = usb_add_hcd(hcd, irq, IRQF_DISABLED); +	if (ret) +		goto err_add_hcd; + +	if (ret == 0) +		return ret; + +	usb_remove_hcd(hcd); + +err_add_hcd: +	tmio_stop_hc(dev); +	if (cell->disable) +		cell->disable(dev); +err_enable: +	dma_release_declared_memory(&dev->dev); +err_dma_declare: +	iounmap(hcd->regs); +err_ioremap_regs: +	iounmap(tmio->ccr); +err_ioremap_ccr: +	usb_put_hcd(hcd); +err_usb_create_hcd: + +	return ret; +} + +static int __devexit ohci_hcd_tmio_drv_remove(struct platform_device *dev) +{ +	struct usb_hcd *hcd = platform_get_drvdata(dev); +	struct tmio_hcd *tmio = hcd_to_tmio(hcd); +	struct mfd_cell *cell = dev->dev.platform_data; + +	usb_remove_hcd(hcd); +	tmio_stop_hc(dev); +	if (cell->disable) +		cell->disable(dev); +	dma_release_declared_memory(&dev->dev); +	iounmap(hcd->regs); +	iounmap(tmio->ccr); +	usb_put_hcd(hcd); + +	platform_set_drvdata(dev, NULL); + +	return 0; +} + +#ifdef CONFIG_PM +static int ohci_hcd_tmio_drv_suspend(struct platform_device *dev, pm_message_t state) +{ +	struct mfd_cell *cell = dev->dev.platform_data; +	struct usb_hcd *hcd = platform_get_drvdata(dev); +	struct ohci_hcd *ohci = hcd_to_ohci(hcd); +	struct tmio_hcd *tmio = hcd_to_tmio(hcd); +	unsigned long flags; +	u8 misc; +	int ret; + +	if (time_before(jiffies, ohci->next_statechange)) +		msleep(5); +	ohci->next_statechange = jiffies; + +	spin_lock_irqsave(&tmio->lock, flags); + +	misc = tmio_ioread8(tmio->ccr + CCR_MISC); +	misc |= 1 << 3; /* USSUSP */ +	tmio_iowrite8(misc, tmio->ccr + CCR_MISC); + +	spin_unlock_irqrestore(&tmio->lock, flags); + +	if (cell->suspend) { +		ret = cell->suspend(dev); +		if (ret) +			return ret; +	} + +	hcd->state = HC_STATE_SUSPENDED; + +	return 0; +} + +static int ohci_hcd_tmio_drv_resume(struct platform_device *dev) +{ +	struct mfd_cell *cell = dev->dev.platform_data; +	struct usb_hcd *hcd = platform_get_drvdata(dev); +	struct ohci_hcd *ohci = hcd_to_ohci(hcd); +	struct tmio_hcd *tmio = hcd_to_tmio(hcd); +	unsigned long flags; +	u8 misc; +	int ret; + +	if (time_before(jiffies, ohci->next_statechange)) +		msleep(5); +	ohci->next_statechange = jiffies; + +	if (cell->resume) { +		ret = cell->resume(dev); +		if (ret) +			return ret; +	} + +	tmio_start_hc(dev); + +	spin_lock_irqsave(&tmio->lock, flags); + +	misc = tmio_ioread8(tmio->ccr + CCR_MISC); +	misc &= ~(1 << 3); /* USSUSP */ +	tmio_iowrite8(misc, tmio->ccr + CCR_MISC); + +	spin_unlock_irqrestore(&tmio->lock, flags); + +	ohci_finish_controller_resume(hcd); + +	return 0; +} +#else +#define ohci_hcd_tmio_drv_suspend NULL +#define ohci_hcd_tmio_drv_resume NULL +#endif + +static struct platform_driver ohci_hcd_tmio_driver = { +	.probe		= ohci_hcd_tmio_drv_probe, +	.remove		= __devexit_p(ohci_hcd_tmio_drv_remove), +	.shutdown	= usb_hcd_platform_shutdown, +	.suspend	= ohci_hcd_tmio_drv_suspend, +	.resume		= ohci_hcd_tmio_drv_resume, +	.driver		= { +		.name	= "tmio-ohci", +		.owner	= THIS_MODULE, +	}, +}; diff --git a/drivers/usb/host/ohci.h b/drivers/usb/host/ohci.h index faf622eafce..222011f6172 100644 --- a/drivers/usb/host/ohci.h +++ b/drivers/usb/host/ohci.h @@ -540,15 +540,7 @@ static inline struct usb_hcd *ohci_to_hcd (const struct ohci_hcd *ohci)   * Big-endian read/write functions are arch-specific.   * Other arches can be added if/when they're needed.   * - * REVISIT: arch/powerpc now has readl/writel_be, so the - * definition below can die once the STB04xxx support is - * finally ported over.   */ -#if defined(CONFIG_PPC) && !defined(CONFIG_PPC_MERGE) -#define readl_be(addr)		in_be32((__force unsigned *)addr) -#define writel_be(val, addr)	out_be32((__force unsigned *)addr, val) -#endif -  static inline unsigned int _ohci_readl (const struct ohci_hcd *ohci,  					__hc32 __iomem * regs)  { diff --git a/drivers/usb/host/r8a66597-hcd.c b/drivers/usb/host/r8a66597-hcd.c index ea7126f99ca..2376f24f3c8 100644 --- a/drivers/usb/host/r8a66597-hcd.c +++ b/drivers/usb/host/r8a66597-hcd.c @@ -66,7 +66,7 @@ static unsigned short endian;  module_param(endian, ushort, 0644);  MODULE_PARM_DESC(endian, "data endian: big=256, little=0 (default=0)"); -static unsigned short irq_sense = INTL; +static unsigned short irq_sense = 0xff;  module_param(irq_sense, ushort, 0644);  MODULE_PARM_DESC(irq_sense, "IRQ sense: low level=32, falling edge=0 "  		"(default=32)"); @@ -118,7 +118,7 @@ static int r8a66597_clock_enable(struct r8a66597 *r8a66597)  		r8a66597_write(r8a66597, SCKE, SYSCFG0);  		tmp = r8a66597_read(r8a66597, SYSCFG0);  		if (i++ > 1000) { -			err("register access fail."); +			printk(KERN_ERR "r8a66597: register access fail.\n");  			return -ENXIO;  		}  	} while ((tmp & SCKE) != SCKE); @@ -128,7 +128,7 @@ static int r8a66597_clock_enable(struct r8a66597 *r8a66597)  		r8a66597_write(r8a66597, USBE, SYSCFG0);  		tmp = r8a66597_read(r8a66597, SYSCFG0);  		if (i++ > 1000) { -			err("register access fail."); +			printk(KERN_ERR "r8a66597: register access fail.\n");  			return -ENXIO;  		}  	} while ((tmp & USBE) != USBE); @@ -141,7 +141,7 @@ static int r8a66597_clock_enable(struct r8a66597 *r8a66597)  		msleep(1);  		tmp = r8a66597_read(r8a66597, SYSCFG0);  		if (i++ > 500) { -			err("register access fail."); +			printk(KERN_ERR "r8a66597: register access fail.\n");  			return -ENXIO;  		}  	} while ((tmp & SCKE) != SCKE); @@ -265,7 +265,7 @@ static void get_port_number(char *devpath, u16 *root_port, u16 *hub_port)  	if (root_port) {  		*root_port = (devpath[0] & 0x0F) - 1;  		if (*root_port >= R8A66597_MAX_ROOT_HUB) -			err("illegal root port number"); +			printk(KERN_ERR "r8a66597: Illegal root port number.\n");  	}  	if (hub_port)  		*hub_port = devpath[2] & 0x0F; @@ -286,7 +286,7 @@ static u16 get_r8a66597_usb_speed(enum usb_device_speed speed)  		usbspd = HSMODE;  		break;  	default: -		err("unknown speed"); +		printk(KERN_ERR "r8a66597: unknown speed\n");  		break;  	} @@ -385,7 +385,7 @@ static u8 alloc_usb_address(struct r8a66597 *r8a66597, struct urb *urb)  	struct r8a66597_device *dev;  	if (is_hub_limit(urb->dev->devpath)) { -		err("Externel hub limit reached."); +		dev_err(&urb->dev->dev, "External hub limit reached.\n");  		return 0;  	} @@ -406,8 +406,9 @@ static u8 alloc_usb_address(struct r8a66597 *r8a66597, struct urb *urb)  		return addr;  	} -	err("cannot communicate with a USB device more than 10.(%x)", -	    r8a66597->address_map); +	dev_err(&urb->dev->dev, +		"cannot communicate with a USB device more than 10.(%x)\n", +		r8a66597->address_map);  	return 0;  } @@ -447,7 +448,8 @@ static void r8a66597_reg_wait(struct r8a66597 *r8a66597, unsigned long reg,  	do {  		tmp = r8a66597_read(r8a66597, reg);  		if (i++ > 1000000) { -			err("register%lx, loop %x is timeout", reg, loop); +			printk(KERN_ERR "r8a66597: register%lx, loop %x " +			       "is timeout\n", reg, loop);  			break;  		}  		ndelay(1); @@ -675,7 +677,7 @@ static u16 get_empty_pipenum(struct r8a66597 *r8a66597,  			array[i++] = 1;  		break;  	default: -		err("Illegal type"); +		printk(KERN_ERR "r8a66597: Illegal type\n");  		return 0;  	} @@ -705,7 +707,7 @@ static u16 get_r8a66597_type(__u8 type)  		r8a66597_type = R8A66597_ISO;  		break;  	default: -		err("Illegal type"); +		printk(KERN_ERR "r8a66597: Illegal type\n");  		r8a66597_type = 0x0000;  		break;  	} @@ -724,7 +726,7 @@ static u16 get_bufnum(u16 pipenum)  	else if (check_interrupt(pipenum))  		bufnum = 4 + (pipenum - 6);  	else -		err("Illegal pipenum (%d)", pipenum); +		printk(KERN_ERR "r8a66597: Illegal pipenum (%d)\n", pipenum);  	return bufnum;  } @@ -740,7 +742,7 @@ static u16 get_buf_bsize(u16 pipenum)  	else if (check_interrupt(pipenum))  		buf_bsize = 0;  	else -		err("Illegal pipenum (%d)", pipenum); +		printk(KERN_ERR "r8a66597: Illegal pipenum (%d)\n", pipenum);  	return buf_bsize;  } @@ -760,10 +762,12 @@ static void enable_r8a66597_pipe_dma(struct r8a66597 *r8a66597,  			if ((r8a66597->dma_map & (1 << i)) != 0)  				continue; -			info("address %d, EndpointAddress 0x%02x use DMA FIFO", -			     usb_pipedevice(urb->pipe), -			     info->dir_in ? USB_ENDPOINT_DIR_MASK + info->epnum -					    : info->epnum); +			dev_info(&dev->udev->dev, +				 "address %d, EndpointAddress 0x%02x use " +				 "DMA FIFO\n", usb_pipedevice(urb->pipe), +				 info->dir_in ? +				 	USB_ENDPOINT_DIR_MASK + info->epnum +					: info->epnum);  			r8a66597->dma_map |= 1 << i;  			dev->dma_map |= 1 << i; @@ -1187,7 +1191,7 @@ static int start_transfer(struct r8a66597 *r8a66597, struct r8a66597_td *td)  		prepare_status_packet(r8a66597, td);  		break;  	default: -		err("invalid type."); +		printk(KERN_ERR "r8a66597: invalid type.\n");  		break;  	} @@ -1295,7 +1299,7 @@ static void packet_read(struct r8a66597 *r8a66597, u16 pipenum)  	if (unlikely((tmp & FRDY) == 0)) {  		pipe_stop(r8a66597, td->pipe);  		pipe_irq_disable(r8a66597, pipenum); -		err("in fifo not ready (%d)", pipenum); +		printk(KERN_ERR "r8a66597: in fifo not ready (%d)\n", pipenum);  		finish_request(r8a66597, td, pipenum, td->urb, -EPIPE);  		return;  	} @@ -1370,7 +1374,7 @@ static void packet_write(struct r8a66597 *r8a66597, u16 pipenum)  	if (unlikely((tmp & FRDY) == 0)) {  		pipe_stop(r8a66597, td->pipe);  		pipe_irq_disable(r8a66597, pipenum); -		err("out write fifo not ready. (%d)", pipenum); +		printk(KERN_ERR "r8a66597: out fifo not ready (%d)\n", pipenum);  		finish_request(r8a66597, td, pipenum, urb, -EPIPE);  		return;  	} @@ -1759,11 +1763,12 @@ static void r8a66597_timer(unsigned long _r8a66597)  {  	struct r8a66597 *r8a66597 = (struct r8a66597 *)_r8a66597;  	unsigned long flags; +	int port;  	spin_lock_irqsave(&r8a66597->lock, flags); -	r8a66597_root_hub_control(r8a66597, 0); -	r8a66597_root_hub_control(r8a66597, 1); +	for (port = 0; port < R8A66597_MAX_ROOT_HUB; port++) +		r8a66597_root_hub_control(r8a66597, port);  	spin_unlock_irqrestore(&r8a66597->lock, flags);  } @@ -2005,7 +2010,7 @@ static struct r8a66597_device *get_r8a66597_device(struct r8a66597 *r8a66597,  		return dev;  	} -	err("get_r8a66597_device fail.(%d)\n", addr); +	printk(KERN_ERR "r8a66597: get_r8a66597_device fail.(%d)\n", addr);  	return NULL;  } @@ -2263,7 +2268,7 @@ static int __init_or_module r8a66597_remove(struct platform_device *pdev)  #define resource_len(r) (((r)->end - (r)->start) + 1)  static int __init r8a66597_probe(struct platform_device *pdev)  { -	struct resource *res = NULL; +	struct resource *res = NULL, *ires;  	int irq = -1;  	void __iomem *reg = NULL;  	struct usb_hcd *hcd = NULL; @@ -2274,7 +2279,7 @@ static int __init r8a66597_probe(struct platform_device *pdev)  	if (pdev->dev.dma_mask) {  		ret = -EINVAL; -		err("dma not support"); +		dev_err(&pdev->dev, "dma not supported\n");  		goto clean_up;  	} @@ -2282,21 +2287,25 @@ static int __init r8a66597_probe(struct platform_device *pdev)  					   (char *)hcd_name);  	if (!res) {  		ret = -ENODEV; -		err("platform_get_resource_byname error."); +		dev_err(&pdev->dev, "platform_get_resource_byname error.\n");  		goto clean_up;  	} -	irq = platform_get_irq(pdev, 0); -	if (irq < 0) { +	ires = platform_get_resource(pdev, IORESOURCE_IRQ, 0); +	if (!ires) {  		ret = -ENODEV; -		err("platform_get_irq error."); +		dev_err(&pdev->dev, +			"platform_get_resource IORESOURCE_IRQ error.\n");  		goto clean_up;  	} +	irq = ires->start; +	irq_trigger = ires->flags & IRQF_TRIGGER_MASK; +  	reg = ioremap(res->start, resource_len(res));  	if (reg == NULL) {  		ret = -ENOMEM; -		err("ioremap error."); +		dev_err(&pdev->dev, "ioremap error.\n");  		goto clean_up;  	} @@ -2304,7 +2313,7 @@ static int __init r8a66597_probe(struct platform_device *pdev)  	hcd = usb_create_hcd(&r8a66597_hc_driver, &pdev->dev, (char *)hcd_name);  	if (!hcd) {  		ret = -ENOMEM; -		err("Failed to create hcd"); +		dev_err(&pdev->dev, "Failed to create hcd\n");  		goto clean_up;  	}  	r8a66597 = hcd_to_r8a66597(hcd); @@ -2329,13 +2338,33 @@ static int __init r8a66597_probe(struct platform_device *pdev)  	INIT_LIST_HEAD(&r8a66597->child_device);  	hcd->rsrc_start = res->start; -	if (irq_sense == INTL) -		irq_trigger = IRQF_TRIGGER_LOW; -	else -		irq_trigger = IRQF_TRIGGER_FALLING; + +	/* irq_sense setting on cmdline takes precedence over resource +	 * settings, so the introduction of irqflags in IRQ resourse +	 * won't disturb existing setups */ +	switch (irq_sense) { +		case INTL: +			irq_trigger = IRQF_TRIGGER_LOW; +			break; +		case 0: +			irq_trigger = IRQF_TRIGGER_FALLING; +			break; +		case 0xff: +			if (irq_trigger) +				irq_sense = (irq_trigger & IRQF_TRIGGER_LOW) ? +					    INTL : 0; +			else { +				irq_sense = INTL; +				irq_trigger = IRQF_TRIGGER_LOW; +			} +			break; +		default: +			dev_err(&pdev->dev, "Unknown irq_sense value.\n"); +	} +  	ret = usb_add_hcd(hcd, irq, IRQF_DISABLED | irq_trigger);  	if (ret != 0) { -		err("Failed to add hcd"); +		dev_err(&pdev->dev, "Failed to add hcd\n");  		goto clean_up;  	} @@ -2364,7 +2393,8 @@ static int __init r8a66597_init(void)  	if (usb_disabled())  		return -ENODEV; -	info("driver %s, %s", hcd_name, DRIVER_VERSION); +	printk(KERN_INFO KBUILD_MODNAME ": driver %s, %s\n", hcd_name, +	       DRIVER_VERSION);  	return platform_driver_register(&r8a66597_driver);  }  module_init(r8a66597_init); diff --git a/drivers/usb/host/sl811-hcd.c b/drivers/usb/host/sl811-hcd.c index 8a74bbb57d0..e106e9d48d4 100644 --- a/drivers/usb/host/sl811-hcd.c +++ b/drivers/usb/host/sl811-hcd.c @@ -1620,22 +1620,26 @@ sl811h_probe(struct platform_device *dev)  {  	struct usb_hcd		*hcd;  	struct sl811		*sl811; -	struct resource		*addr, *data; +	struct resource		*addr, *data, *ires;  	int			irq;  	void __iomem		*addr_reg;  	void __iomem		*data_reg;  	int			retval;  	u8			tmp, ioaddr = 0; +	unsigned long		irqflags;  	/* basic sanity checks first.  board-specific init logic should  	 * have initialized these three resources and probably board  	 * specific platform_data.  we don't probe for IRQs, and do only  	 * minimal sanity checking.  	 */ -	irq = platform_get_irq(dev, 0); -	if (dev->num_resources < 3 || irq < 0) +	ires = platform_get_resource(dev, IORESOURCE_IRQ, 0); +	if (dev->num_resources < 3 || !ires)  		return -ENODEV; +	irq = ires->start; +	irqflags = ires->flags & IRQF_TRIGGER_MASK; +  	/* refuse to confuse usbcore */  	if (dev->dev.dma_mask) {  		DBG("no we won't dma\n"); @@ -1717,8 +1721,11 @@ sl811h_probe(struct platform_device *dev)  	 * triggers (e.g. most ARM CPUs).  Initial driver stress testing  	 * was on a system with single edge triggering, so most sorts of  	 * triggering arrangement should work. +	 * +	 * Use resource IRQ flags if set by platform device setup.  	 */ -	retval = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED); +	irqflags |= IRQF_SHARED; +	retval = usb_add_hcd(hcd, irq, IRQF_DISABLED | irqflags);  	if (retval != 0)  		goto err6; diff --git a/drivers/usb/host/sl811_cs.c b/drivers/usb/host/sl811_cs.c index 5da63f53500..516848dd9b4 100644 --- a/drivers/usb/host/sl811_cs.c +++ b/drivers/usb/host/sl811_cs.c @@ -112,7 +112,8 @@ static struct platform_device platform_dev = {  	.num_resources		= ARRAY_SIZE(resources),  }; -static int sl811_hc_init(struct device *parent, ioaddr_t base_addr, int irq) +static int sl811_hc_init(struct device *parent, resource_size_t base_addr, +			 int irq)  {  	if (platform_dev.dev.parent)  		return -EBUSY; @@ -155,97 +156,72 @@ static void sl811_cs_release(struct pcmcia_device * link)  	platform_device_unregister(&platform_dev);  } +static int sl811_cs_config_check(struct pcmcia_device *p_dev, +				 cistpl_cftable_entry_t *cfg, +				 cistpl_cftable_entry_t *dflt, +				 unsigned int vcc, +				 void *priv_data) +{ +	if (cfg->index == 0) +		return -ENODEV; + +	/* Use power settings for Vcc and Vpp if present */ +	/*  Note that the CIS values need to be rescaled */ +	if (cfg->vcc.present & (1<<CISTPL_POWER_VNOM)) { +		if (cfg->vcc.param[CISTPL_POWER_VNOM]/10000 != vcc) +			return -ENODEV; +	} else if (dflt->vcc.present & (1<<CISTPL_POWER_VNOM)) { +		if (dflt->vcc.param[CISTPL_POWER_VNOM]/10000 != vcc) +			return -ENODEV; +		} + +	if (cfg->vpp1.present & (1<<CISTPL_POWER_VNOM)) +		p_dev->conf.Vpp = +			cfg->vpp1.param[CISTPL_POWER_VNOM]/10000; +	else if (dflt->vpp1.present & (1<<CISTPL_POWER_VNOM)) +		p_dev->conf.Vpp = +			dflt->vpp1.param[CISTPL_POWER_VNOM]/10000; + +	/* we need an interrupt */ +	if (cfg->irq.IRQInfo1 || dflt->irq.IRQInfo1) +		p_dev->conf.Attributes |= CONF_ENABLE_IRQ; + +	/* IO window settings */ +	p_dev->io.NumPorts1 = p_dev->io.NumPorts2 = 0; +	if ((cfg->io.nwin > 0) || (dflt->io.nwin > 0)) { +		cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt->io; + +		p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_8; +		p_dev->io.IOAddrLines = io->flags & CISTPL_IO_LINES_MASK; +		p_dev->io.BasePort1 = io->win[0].base; +		p_dev->io.NumPorts1 = io->win[0].len; + +		return pcmcia_request_io(p_dev, &p_dev->io); +	} +	pcmcia_disable_device(p_dev); +	return -ENODEV; +} + +  static int sl811_cs_config(struct pcmcia_device *link)  {  	struct device		*parent = &handle_to_dev(link);  	local_info_t		*dev = link->priv; -	tuple_t			tuple; -	cisparse_t		parse;  	int			last_fn, last_ret; -	u_char			buf[64]; -	config_info_t		conf; -	cistpl_cftable_entry_t	dflt = { 0 };  	DBG(0, "sl811_cs_config(0x%p)\n", link); -	/* Look up the current Vcc */ -	CS_CHECK(GetConfigurationInfo, -			pcmcia_get_configuration_info(link, &conf)); - -	tuple.Attributes = 0; -	tuple.TupleData = buf; -	tuple.TupleDataMax = sizeof(buf); -	tuple.TupleOffset = 0; -	tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY; -	CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple)); -	while (1) { -		cistpl_cftable_entry_t	*cfg = &(parse.cftable_entry); - -		if (pcmcia_get_tuple_data(link, &tuple) != 0 -				|| pcmcia_parse_tuple(link, &tuple, &parse) -						!= 0) -			goto next_entry; - -		if (cfg->flags & CISTPL_CFTABLE_DEFAULT) { -			dflt = *cfg; -		} - -		if (cfg->index == 0) -			goto next_entry; - -		link->conf.ConfigIndex = cfg->index; - -		/* Use power settings for Vcc and Vpp if present */ -		/*  Note that the CIS values need to be rescaled */ -		if (cfg->vcc.present & (1<<CISTPL_POWER_VNOM)) { -			if (cfg->vcc.param[CISTPL_POWER_VNOM]/10000 -					!= conf.Vcc) -				goto next_entry; -		} else if (dflt.vcc.present & (1<<CISTPL_POWER_VNOM)) { -			if (dflt.vcc.param[CISTPL_POWER_VNOM]/10000 -					!= conf.Vcc) -				goto next_entry; -		} - -		if (cfg->vpp1.present & (1<<CISTPL_POWER_VNOM)) -			link->conf.Vpp = -				cfg->vpp1.param[CISTPL_POWER_VNOM]/10000; -		else if (dflt.vpp1.present & (1<<CISTPL_POWER_VNOM)) -			link->conf.Vpp = -				dflt.vpp1.param[CISTPL_POWER_VNOM]/10000; - -		/* we need an interrupt */ -		if (cfg->irq.IRQInfo1 || dflt.irq.IRQInfo1) -			link->conf.Attributes |= CONF_ENABLE_IRQ; - -		/* IO window settings */ -		link->io.NumPorts1 = link->io.NumPorts2 = 0; -		if ((cfg->io.nwin > 0) || (dflt.io.nwin > 0)) { -			cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt.io; - -			link->io.Attributes1 = IO_DATA_PATH_WIDTH_8; -			link->io.IOAddrLines = io->flags & CISTPL_IO_LINES_MASK; -			link->io.BasePort1 = io->win[0].base; -			link->io.NumPorts1 = io->win[0].len; - -			if (pcmcia_request_io(link, &link->io) != 0) -				goto next_entry; -		} -		break; - -next_entry: -		pcmcia_disable_device(link); -		last_ret = pcmcia_get_next_tuple(link, &tuple); -	} +	if (pcmcia_loop_config(link, sl811_cs_config_check, NULL)) +		goto failed;  	/* require an IRQ and two registers */  	if (!link->io.NumPorts1 || link->io.NumPorts1 < 2) -		goto cs_failed; +		goto failed;  	if (link->conf.Attributes & CONF_ENABLE_IRQ)  		CS_CHECK(RequestIRQ,  			pcmcia_request_irq(link, &link->irq));  	else -		goto cs_failed; +		goto failed;  	CS_CHECK(RequestConfiguration,  		pcmcia_request_configuration(link, &link->conf)); @@ -266,8 +242,9 @@ next_entry:  	if (sl811_hc_init(parent, link->io.BasePort1, link->irq.AssignedIRQ)  			< 0) {  cs_failed: -		printk("sl811_cs_config failed\n");  		cs_error(link, last_fn, last_ret); +failed: +		printk(KERN_WARNING "sl811_cs_config failed\n");  		sl811_cs_release(link);  		return  -ENODEV;  	} diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c index 3a7bfe7a887..cf5e4cf7ea4 100644 --- a/drivers/usb/host/uhci-hcd.c +++ b/drivers/usb/host/uhci-hcd.c @@ -53,7 +53,6 @@  /*   * Version Information   */ -#define DRIVER_VERSION "v3.0"  #define DRIVER_AUTHOR "Linus 'Frodo Rabbit' Torvalds, Johannes Erdfelt, \  Randy Dunlap, Georg Acher, Deti Fliegl, Thomas Sailer, Roman Weissgaerber, \  Alan Stern" @@ -951,12 +950,13 @@ static int __init uhci_hcd_init(void)  {  	int retval = -ENOMEM; -	printk(KERN_INFO DRIVER_DESC " " DRIVER_VERSION "%s\n", -			ignore_oc ? ", overcurrent ignored" : ""); -  	if (usb_disabled())  		return -ENODEV; +	printk(KERN_INFO "uhci_hcd: " DRIVER_DESC "%s\n", +			ignore_oc ? ", overcurrent ignored" : ""); +	set_bit(USB_UHCI_LOADED, &usb_hcds_loaded); +  	if (DEBUG_CONFIGURED) {  		errbuf = kmalloc(ERRBUF_LEN, GFP_KERNEL);  		if (!errbuf) @@ -988,6 +988,7 @@ debug_failed:  errbuf_failed: +	clear_bit(USB_UHCI_LOADED, &usb_hcds_loaded);  	return retval;  } @@ -997,6 +998,7 @@ static void __exit uhci_hcd_cleanup(void)  	kmem_cache_destroy(uhci_up_cachep);  	debugfs_remove(uhci_debugfs_root);  	kfree(errbuf); +	clear_bit(USB_UHCI_LOADED, &usb_hcds_loaded);  }  module_init(uhci_hcd_init); diff --git a/drivers/usb/host/uhci-q.c b/drivers/usb/host/uhci-q.c index db645936eed..5631d89c873 100644 --- a/drivers/usb/host/uhci-q.c +++ b/drivers/usb/host/uhci-q.c @@ -123,14 +123,10 @@ static struct uhci_td *uhci_alloc_td(struct uhci_hcd *uhci)  static void uhci_free_td(struct uhci_hcd *uhci, struct uhci_td *td)  { -	if (!list_empty(&td->list)) { -		dev_warn(uhci_dev(uhci), "td %p still in list!\n", td); -		WARN_ON(1); -	} -	if (!list_empty(&td->fl_list)) { -		dev_warn(uhci_dev(uhci), "td %p still in fl_list!\n", td); -		WARN_ON(1); -	} +	if (!list_empty(&td->list)) +		dev_WARN(uhci_dev(uhci), "td %p still in list!\n", td); +	if (!list_empty(&td->fl_list)) +		dev_WARN(uhci_dev(uhci), "td %p still in fl_list!\n", td);  	dma_pool_free(uhci->td_pool, td, td->dma_handle);  } @@ -295,10 +291,8 @@ static struct uhci_qh *uhci_alloc_qh(struct uhci_hcd *uhci,  static void uhci_free_qh(struct uhci_hcd *uhci, struct uhci_qh *qh)  {  	WARN_ON(qh->state != QH_STATE_IDLE && qh->udev); -	if (!list_empty(&qh->queue)) { -		dev_warn(uhci_dev(uhci), "qh %p list not empty!\n", qh); -		WARN_ON(1); -	} +	if (!list_empty(&qh->queue)) +		dev_WARN(uhci_dev(uhci), "qh %p list not empty!\n", qh);  	list_del(&qh->node);  	if (qh->udev) { @@ -746,11 +740,9 @@ static void uhci_free_urb_priv(struct uhci_hcd *uhci,  {  	struct uhci_td *td, *tmp; -	if (!list_empty(&urbp->node)) { -		dev_warn(uhci_dev(uhci), "urb %p still on QH's list!\n", +	if (!list_empty(&urbp->node)) +		dev_WARN(uhci_dev(uhci), "urb %p still on QH's list!\n",  				urbp->urb); -		WARN_ON(1); -	}  	list_for_each_entry_safe(td, tmp, &urbp->td_list, list) {  		uhci_remove_td_from_urbp(td); @@ -1073,13 +1065,18 @@ static int uhci_submit_interrupt(struct uhci_hcd *uhci, struct urb *urb,  		}  		if (exponent < 0)  			return -EINVAL; -		qh->period = 1 << exponent; -		qh->skel = SKEL_INDEX(exponent); -		/* For now, interrupt phase is fixed by the layout -		 * of the QH lists. */ -		qh->phase = (qh->period / 2) & (MAX_PHASE - 1); -		ret = uhci_check_bandwidth(uhci, qh); +		/* If the slot is full, try a lower period */ +		do { +			qh->period = 1 << exponent; +			qh->skel = SKEL_INDEX(exponent); + +			/* For now, interrupt phase is fixed by the layout +			 * of the QH lists. +			 */ +			qh->phase = (qh->period / 2) & (MAX_PHASE - 1); +			ret = uhci_check_bandwidth(uhci, qh); +		} while (ret != 0 && --exponent >= 0);  		if (ret)  			return ret;  	} else if (qh->period > urb->interval) diff --git a/drivers/usb/host/whci/Kbuild b/drivers/usb/host/whci/Kbuild new file mode 100644 index 00000000000..26a3871ea0f --- /dev/null +++ b/drivers/usb/host/whci/Kbuild @@ -0,0 +1,11 @@ +obj-$(CONFIG_USB_WHCI_HCD) += whci-hcd.o + +whci-hcd-y := \ +	asl.o	\ +	hcd.o 	\ +	hw.o	\ +	init.o	\ +	int.o	\ +	pzl.o	\ +	qset.o	\ +	wusb.o diff --git a/drivers/usb/host/whci/asl.c b/drivers/usb/host/whci/asl.c new file mode 100644 index 00000000000..4d7078e5057 --- /dev/null +++ b/drivers/usb/host/whci/asl.c @@ -0,0 +1,367 @@ +/* + * Wireless Host Controller (WHC) asynchronous schedule management. + * + * Copyright (C) 2007 Cambridge Silicon Radio Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ +#include <linux/kernel.h> +#include <linux/dma-mapping.h> +#include <linux/uwb/umc.h> +#include <linux/usb.h> +#define D_LOCAL 0 +#include <linux/uwb/debug.h> + +#include "../../wusbcore/wusbhc.h" + +#include "whcd.h" + +#if D_LOCAL >= 4 +static void dump_asl(struct whc *whc, const char *tag) +{ +	struct device *dev = &whc->umc->dev; +	struct whc_qset *qset; + +	d_printf(4, dev, "ASL %s\n", tag); + +	list_for_each_entry(qset, &whc->async_list, list_node) { +		dump_qset(qset, dev); +	} +} +#else +static inline void dump_asl(struct whc *whc, const char *tag) +{ +} +#endif + + +static void qset_get_next_prev(struct whc *whc, struct whc_qset *qset, +			       struct whc_qset **next, struct whc_qset **prev) +{ +	struct list_head *n, *p; + +	BUG_ON(list_empty(&whc->async_list)); + +	n = qset->list_node.next; +	if (n == &whc->async_list) +		n = n->next; +	p = qset->list_node.prev; +	if (p == &whc->async_list) +		p = p->prev; + +	*next = container_of(n, struct whc_qset, list_node); +	*prev = container_of(p, struct whc_qset, list_node); + +} + +static void asl_qset_insert_begin(struct whc *whc, struct whc_qset *qset) +{ +	list_move(&qset->list_node, &whc->async_list); +	qset->in_sw_list = true; +} + +static void asl_qset_insert(struct whc *whc, struct whc_qset *qset) +{ +	struct whc_qset *next, *prev; + +	qset_clear(whc, qset); + +	/* Link into ASL. */ +	qset_get_next_prev(whc, qset, &next, &prev); +	whc_qset_set_link_ptr(&qset->qh.link, next->qset_dma); +	whc_qset_set_link_ptr(&prev->qh.link, qset->qset_dma); +	qset->in_hw_list = true; +} + +static void asl_qset_remove(struct whc *whc, struct whc_qset *qset) +{ +	struct whc_qset *prev, *next; + +	qset_get_next_prev(whc, qset, &next, &prev); + +	list_move(&qset->list_node, &whc->async_removed_list); +	qset->in_sw_list = false; + +	/* +	 * No more qsets in the ASL?  The caller must stop the ASL as +	 * it's no longer valid. +	 */ +	if (list_empty(&whc->async_list)) +		return; + +	/* Remove from ASL. */ +	whc_qset_set_link_ptr(&prev->qh.link, next->qset_dma); +	qset->in_hw_list = false; +} + +/** + * process_qset - process any recently inactivated or halted qTDs in a + * qset. + * + * After inactive qTDs are removed, new qTDs can be added if the + * urb queue still contains URBs. + * + * Returns any additional WUSBCMD bits for the ASL sync command (i.e., + * WUSBCMD_ASYNC_QSET_RM if a halted qset was removed). + */ +static uint32_t process_qset(struct whc *whc, struct whc_qset *qset) +{ +	enum whc_update update = 0; +	uint32_t status = 0; + +	while (qset->ntds) { +		struct whc_qtd *td; +		int t; + +		t = qset->td_start; +		td = &qset->qtd[qset->td_start]; +		status = le32_to_cpu(td->status); + +		/* +		 * Nothing to do with a still active qTD. +		 */ +		if (status & QTD_STS_ACTIVE) +			break; + +		if (status & QTD_STS_HALTED) { +			/* Ug, an error. */ +			process_halted_qtd(whc, qset, td); +			goto done; +		} + +		/* Mmm, a completed qTD. */ +		process_inactive_qtd(whc, qset, td); +	} + +	update |= qset_add_qtds(whc, qset); + +done: +	/* +	 * Remove this qset from the ASL if requested, but only if has +	 * no qTDs. +	 */ +	if (qset->remove && qset->ntds == 0) { +		asl_qset_remove(whc, qset); +		update |= WHC_UPDATE_REMOVED; +	} +	return update; +} + +void asl_start(struct whc *whc) +{ +	struct whc_qset *qset; + +	qset = list_first_entry(&whc->async_list, struct whc_qset, list_node); + +	le_writeq(qset->qset_dma | QH_LINK_NTDS(8), whc->base + WUSBASYNCLISTADDR); + +	whc_write_wusbcmd(whc, WUSBCMD_ASYNC_EN, WUSBCMD_ASYNC_EN); +	whci_wait_for(&whc->umc->dev, whc->base + WUSBSTS, +		      WUSBSTS_ASYNC_SCHED, WUSBSTS_ASYNC_SCHED, +		      1000, "start ASL"); +} + +void asl_stop(struct whc *whc) +{ +	whc_write_wusbcmd(whc, WUSBCMD_ASYNC_EN, 0); +	whci_wait_for(&whc->umc->dev, whc->base + WUSBSTS, +		      WUSBSTS_ASYNC_SCHED, 0, +		      1000, "stop ASL"); +} + +void asl_update(struct whc *whc, uint32_t wusbcmd) +{ +	whc_write_wusbcmd(whc, wusbcmd, wusbcmd); +	wait_event(whc->async_list_wq, +		   (le_readl(whc->base + WUSBCMD) & WUSBCMD_ASYNC_UPDATED) == 0); +} + +/** + * scan_async_work - scan the ASL for qsets to process. + * + * Process each qset in the ASL in turn and then signal the WHC that + * the ASL has been updated. + * + * Then start, stop or update the asynchronous schedule as required. + */ +void scan_async_work(struct work_struct *work) +{ +	struct whc *whc = container_of(work, struct whc, async_work); +	struct whc_qset *qset, *t; +	enum whc_update update = 0; + +	spin_lock_irq(&whc->lock); + +	dump_asl(whc, "before processing"); + +	/* +	 * Transerve the software list backwards so new qsets can be +	 * safely inserted into the ASL without making it non-circular. +	 */ +	list_for_each_entry_safe_reverse(qset, t, &whc->async_list, list_node) { +		if (!qset->in_hw_list) { +			asl_qset_insert(whc, qset); +			update |= WHC_UPDATE_ADDED; +		} + +		update |= process_qset(whc, qset); +	} + +	dump_asl(whc, "after processing"); + +	spin_unlock_irq(&whc->lock); + +	if (update) { +		uint32_t wusbcmd = WUSBCMD_ASYNC_UPDATED | WUSBCMD_ASYNC_SYNCED_DB; +		if (update & WHC_UPDATE_REMOVED) +			wusbcmd |= WUSBCMD_ASYNC_QSET_RM; +		asl_update(whc, wusbcmd); +	} + +	/* +	 * Now that the ASL is updated, complete the removal of any +	 * removed qsets. +	 */ +	spin_lock(&whc->lock); + +	list_for_each_entry_safe(qset, t, &whc->async_removed_list, list_node) { +		qset_remove_complete(whc, qset); +	} + +	spin_unlock(&whc->lock); +} + +/** + * asl_urb_enqueue - queue an URB onto the asynchronous list (ASL). + * @whc: the WHCI host controller + * @urb: the URB to enqueue + * @mem_flags: flags for any memory allocations + * + * The qset for the endpoint is obtained and the urb queued on to it. + * + * Work is scheduled to update the hardware's view of the ASL. + */ +int asl_urb_enqueue(struct whc *whc, struct urb *urb, gfp_t mem_flags) +{ +	struct whc_qset *qset; +	int err; +	unsigned long flags; + +	spin_lock_irqsave(&whc->lock, flags); + +	qset = get_qset(whc, urb, GFP_ATOMIC); +	if (qset == NULL) +		err = -ENOMEM; +	else +		err = qset_add_urb(whc, qset, urb, GFP_ATOMIC); +	if (!err) { +		usb_hcd_link_urb_to_ep(&whc->wusbhc.usb_hcd, urb); +		if (!qset->in_sw_list) +			asl_qset_insert_begin(whc, qset); +	} + +	spin_unlock_irqrestore(&whc->lock, flags); + +	if (!err) +		queue_work(whc->workqueue, &whc->async_work); + +	return 0; +} + +/** + * asl_urb_dequeue - remove an URB (qset) from the async list. + * @whc: the WHCI host controller + * @urb: the URB to dequeue + * @status: the current status of the URB + * + * URBs that do yet have qTDs can simply be removed from the software + * queue, otherwise the qset must be removed from the ASL so the qTDs + * can be removed. + */ +int asl_urb_dequeue(struct whc *whc, struct urb *urb, int status) +{ +	struct whc_urb *wurb = urb->hcpriv; +	struct whc_qset *qset = wurb->qset; +	struct whc_std *std, *t; +	int ret; +	unsigned long flags; + +	spin_lock_irqsave(&whc->lock, flags); + +	ret = usb_hcd_check_unlink_urb(&whc->wusbhc.usb_hcd, urb, status); +	if (ret < 0) +		goto out; + +	list_for_each_entry_safe(std, t, &qset->stds, list_node) { +		if (std->urb == urb) +			qset_free_std(whc, std); +		else +			std->qtd = NULL; /* so this std is re-added when the qset is */ +	} + +	asl_qset_remove(whc, qset); +	wurb->status = status; +	wurb->is_async = true; +	queue_work(whc->workqueue, &wurb->dequeue_work); + +out: +	spin_unlock_irqrestore(&whc->lock, flags); + +	return ret; +} + +/** + * asl_qset_delete - delete a qset from the ASL + */ +void asl_qset_delete(struct whc *whc, struct whc_qset *qset) +{ +	qset->remove = 1; +	queue_work(whc->workqueue, &whc->async_work); +	qset_delete(whc, qset); +} + +/** + * asl_init - initialize the asynchronous schedule list + * + * A dummy qset with no qTDs is added to the ASL to simplify removing + * qsets (no need to stop the ASL when the last qset is removed). + */ +int asl_init(struct whc *whc) +{ +	struct whc_qset *qset; + +	qset = qset_alloc(whc, GFP_KERNEL); +	if (qset == NULL) +		return -ENOMEM; + +	asl_qset_insert_begin(whc, qset); +	asl_qset_insert(whc, qset); + +	return 0; +} + +/** + * asl_clean_up - free ASL resources + * + * The ASL is stopped and empty except for the dummy qset. + */ +void asl_clean_up(struct whc *whc) +{ +	struct whc_qset *qset; + +	if (!list_empty(&whc->async_list)) { +		qset = list_first_entry(&whc->async_list, struct whc_qset, list_node); +		list_del(&qset->list_node); +		qset_free(whc, qset); +	} +} diff --git a/drivers/usb/host/whci/hcd.c b/drivers/usb/host/whci/hcd.c new file mode 100644 index 00000000000..ef3ad4dca94 --- /dev/null +++ b/drivers/usb/host/whci/hcd.c @@ -0,0 +1,339 @@ +/* + * Wireless Host Controller (WHC) driver. + * + * Copyright (C) 2007 Cambridge Silicon Radio Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ +#include <linux/version.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/uwb/umc.h> + +#include "../../wusbcore/wusbhc.h" + +#include "whcd.h" + +/* + * One time initialization. + * + * Nothing to do here. + */ +static int whc_reset(struct usb_hcd *usb_hcd) +{ +	return 0; +} + +/* + * Start the wireless host controller. + * + * Start device notification. + * + * Put hc into run state, set DNTS parameters. + */ +static int whc_start(struct usb_hcd *usb_hcd) +{ +	struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd); +	struct whc *whc = wusbhc_to_whc(wusbhc); +	u8 bcid; +	int ret; + +	mutex_lock(&wusbhc->mutex); + +	le_writel(WUSBINTR_GEN_CMD_DONE +		  | WUSBINTR_HOST_ERR +		  | WUSBINTR_ASYNC_SCHED_SYNCED +		  | WUSBINTR_DNTS_INT +		  | WUSBINTR_ERR_INT +		  | WUSBINTR_INT, +		  whc->base + WUSBINTR); + +	/* set cluster ID */ +	bcid = wusb_cluster_id_get(); +	ret = whc_set_cluster_id(whc, bcid); +	if (ret < 0) +		goto out; +	wusbhc->cluster_id = bcid; + +	/* start HC */ +	whc_write_wusbcmd(whc, WUSBCMD_RUN, WUSBCMD_RUN); + +	usb_hcd->uses_new_polling = 1; +	usb_hcd->poll_rh = 1; +	usb_hcd->state = HC_STATE_RUNNING; + +out: +	mutex_unlock(&wusbhc->mutex); +	return ret; +} + + +/* + * Stop the wireless host controller. + * + * Stop device notification. + * + * Wait for pending transfer to stop? Put hc into stop state? + */ +static void whc_stop(struct usb_hcd *usb_hcd) +{ +	struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd); +	struct whc *whc = wusbhc_to_whc(wusbhc); + +	mutex_lock(&wusbhc->mutex); + +	wusbhc_stop(wusbhc); + +	/* stop HC */ +	le_writel(0, whc->base + WUSBINTR); +	whc_write_wusbcmd(whc, WUSBCMD_RUN, 0); +	whci_wait_for(&whc->umc->dev, whc->base + WUSBSTS, +		      WUSBSTS_HCHALTED, WUSBSTS_HCHALTED, +		      100, "HC to halt"); + +	wusb_cluster_id_put(wusbhc->cluster_id); + +	mutex_unlock(&wusbhc->mutex); +} + +static int whc_get_frame_number(struct usb_hcd *usb_hcd) +{ +	/* Frame numbers are not applicable to WUSB. */ +	return -ENOSYS; +} + + +/* + * Queue an URB to the ASL or PZL + */ +static int whc_urb_enqueue(struct usb_hcd *usb_hcd, struct urb *urb, +			   gfp_t mem_flags) +{ +	struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd); +	struct whc *whc = wusbhc_to_whc(wusbhc); +	int ret; + +	switch (usb_pipetype(urb->pipe)) { +	case PIPE_INTERRUPT: +		ret = pzl_urb_enqueue(whc, urb, mem_flags); +		break; +	case PIPE_ISOCHRONOUS: +		dev_err(&whc->umc->dev, "isochronous transfers unsupported\n"); +		ret = -ENOTSUPP; +		break; +	case PIPE_CONTROL: +	case PIPE_BULK: +	default: +		ret = asl_urb_enqueue(whc, urb, mem_flags); +		break; +	}; + +	return ret; +} + +/* + * Remove a queued URB from the ASL or PZL. + */ +static int whc_urb_dequeue(struct usb_hcd *usb_hcd, struct urb *urb, int status) +{ +	struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd); +	struct whc *whc = wusbhc_to_whc(wusbhc); +	int ret; + +	switch (usb_pipetype(urb->pipe)) { +	case PIPE_INTERRUPT: +		ret = pzl_urb_dequeue(whc, urb, status); +		break; +	case PIPE_ISOCHRONOUS: +		ret = -ENOTSUPP; +		break; +	case PIPE_CONTROL: +	case PIPE_BULK: +	default: +		ret = asl_urb_dequeue(whc, urb, status); +		break; +	}; + +	return ret; +} + +/* + * Wait for all URBs to the endpoint to be completed, then delete the + * qset. + */ +static void whc_endpoint_disable(struct usb_hcd *usb_hcd, +				 struct usb_host_endpoint *ep) +{ +	struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd); +	struct whc *whc = wusbhc_to_whc(wusbhc); +	struct whc_qset *qset; + +	qset = ep->hcpriv; +	if (qset) { +		ep->hcpriv = NULL; +		if (usb_endpoint_xfer_bulk(&ep->desc) +		    || usb_endpoint_xfer_control(&ep->desc)) +			asl_qset_delete(whc, qset); +		else +			pzl_qset_delete(whc, qset); +	} +} + +static struct hc_driver whc_hc_driver = { +	.description = "whci-hcd", +	.product_desc = "Wireless host controller", +	.hcd_priv_size = sizeof(struct whc) - sizeof(struct usb_hcd), +	.irq = whc_int_handler, +	.flags = HCD_USB2, + +	.reset = whc_reset, +	.start = whc_start, +	.stop = whc_stop, +	.get_frame_number = whc_get_frame_number, +	.urb_enqueue = whc_urb_enqueue, +	.urb_dequeue = whc_urb_dequeue, +	.endpoint_disable = whc_endpoint_disable, + +	.hub_status_data = wusbhc_rh_status_data, +	.hub_control = wusbhc_rh_control, +	.bus_suspend = wusbhc_rh_suspend, +	.bus_resume = wusbhc_rh_resume, +	.start_port_reset = wusbhc_rh_start_port_reset, +}; + +static int whc_probe(struct umc_dev *umc) +{ +	int ret = -ENOMEM; +	struct usb_hcd *usb_hcd; +	struct wusbhc *wusbhc = NULL; +	struct whc *whc = NULL; +	struct device *dev = &umc->dev; + +	usb_hcd = usb_create_hcd(&whc_hc_driver, dev, "whci"); +	if (usb_hcd == NULL) { +		dev_err(dev, "unable to create hcd\n"); +		goto error; +	} + +	usb_hcd->wireless = 1; + +	wusbhc = usb_hcd_to_wusbhc(usb_hcd); +	whc = wusbhc_to_whc(wusbhc); +	whc->umc = umc; + +	ret = whc_init(whc); +	if (ret) +		goto error; + +	wusbhc->dev = dev; +	wusbhc->uwb_rc = uwb_rc_get_by_grandpa(umc->dev.parent); +	if (!wusbhc->uwb_rc) { +		ret = -ENODEV; +		dev_err(dev, "cannot get radio controller\n"); +		goto error; +	} + +	if (whc->n_devices > USB_MAXCHILDREN) { +		dev_warn(dev, "USB_MAXCHILDREN too low for WUSB adapter (%u ports)\n", +			 whc->n_devices); +		wusbhc->ports_max = USB_MAXCHILDREN; +	} else +		wusbhc->ports_max = whc->n_devices; +	wusbhc->mmcies_max      = whc->n_mmc_ies; +	wusbhc->start           = whc_wusbhc_start; +	wusbhc->stop            = whc_wusbhc_stop; +	wusbhc->mmcie_add       = whc_mmcie_add; +	wusbhc->mmcie_rm        = whc_mmcie_rm; +	wusbhc->dev_info_set    = whc_dev_info_set; +	wusbhc->bwa_set         = whc_bwa_set; +	wusbhc->set_num_dnts    = whc_set_num_dnts; +	wusbhc->set_ptk         = whc_set_ptk; +	wusbhc->set_gtk         = whc_set_gtk; + +	ret = wusbhc_create(wusbhc); +	if (ret) +		goto error_wusbhc_create; + +	ret = usb_add_hcd(usb_hcd, whc->umc->irq, IRQF_SHARED); +	if (ret) { +		dev_err(dev, "cannot add HCD: %d\n", ret); +		goto error_usb_add_hcd; +	} + +	ret = wusbhc_b_create(wusbhc); +	if (ret) { +		dev_err(dev, "WUSBHC phase B setup failed: %d\n", ret); +		goto error_wusbhc_b_create; +	} + +	return 0; + +error_wusbhc_b_create: +	usb_remove_hcd(usb_hcd); +error_usb_add_hcd: +	wusbhc_destroy(wusbhc); +error_wusbhc_create: +	uwb_rc_put(wusbhc->uwb_rc); +error: +	whc_clean_up(whc); +	if (usb_hcd) +		usb_put_hcd(usb_hcd); +	return ret; +} + + +static void whc_remove(struct umc_dev *umc) +{ +	struct usb_hcd *usb_hcd = dev_get_drvdata(&umc->dev); +	struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd); +	struct whc *whc = wusbhc_to_whc(wusbhc); + +	if (usb_hcd) { +		wusbhc_b_destroy(wusbhc); +		usb_remove_hcd(usb_hcd); +		wusbhc_destroy(wusbhc); +		uwb_rc_put(wusbhc->uwb_rc); +		whc_clean_up(whc); +		usb_put_hcd(usb_hcd); +	} +} + +static struct umc_driver whci_hc_driver = { +	.name =		"whci-hcd", +	.cap_id =       UMC_CAP_ID_WHCI_WUSB_HC, +	.probe =	whc_probe, +	.remove =	whc_remove, +}; + +static int __init whci_hc_driver_init(void) +{ +	return umc_driver_register(&whci_hc_driver); +} +module_init(whci_hc_driver_init); + +static void __exit whci_hc_driver_exit(void) +{ +	umc_driver_unregister(&whci_hc_driver); +} +module_exit(whci_hc_driver_exit); + +/* PCI device ID's that we handle (so it gets loaded) */ +static struct pci_device_id whci_hcd_id_table[] = { +	{ PCI_DEVICE_CLASS(PCI_CLASS_WIRELESS_WHCI, ~0) }, +	{ /* empty last entry */ } +}; +MODULE_DEVICE_TABLE(pci, whci_hcd_id_table); + +MODULE_DESCRIPTION("WHCI Wireless USB host controller driver"); +MODULE_AUTHOR("Cambridge Silicon Radio Ltd."); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/host/whci/hw.c b/drivers/usb/host/whci/hw.c new file mode 100644 index 00000000000..ac86e59c122 --- /dev/null +++ b/drivers/usb/host/whci/hw.c @@ -0,0 +1,87 @@ +/* + * Wireless Host Controller (WHC) hardware access helpers. + * + * Copyright (C) 2007 Cambridge Silicon Radio Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ +#include <linux/kernel.h> +#include <linux/dma-mapping.h> +#include <linux/uwb/umc.h> + +#include "../../wusbcore/wusbhc.h" + +#include "whcd.h" + +void whc_write_wusbcmd(struct whc *whc, u32 mask, u32 val) +{ +	unsigned long flags; +	u32 cmd; + +	spin_lock_irqsave(&whc->lock, flags); + +	cmd = le_readl(whc->base + WUSBCMD); +	cmd = (cmd & ~mask) | val; +	le_writel(cmd, whc->base + WUSBCMD); + +	spin_unlock_irqrestore(&whc->lock, flags); +} + +/** + * whc_do_gencmd - start a generic command via the WUSBGENCMDSTS register + * @whc:    the WHCI HC + * @cmd:    command to start. + * @params: parameters for the command (the WUSBGENCMDPARAMS register value). + * @addr:   pointer to any data for the command (may be NULL). + * @len:    length of the data (if any). + */ +int whc_do_gencmd(struct whc *whc, u32 cmd, u32 params, void *addr, size_t len) +{ +	unsigned long flags; +	dma_addr_t dma_addr; +	int t; + +	mutex_lock(&whc->mutex); + +	/* Wait for previous command to complete. */ +	t = wait_event_timeout(whc->cmd_wq, +			       (le_readl(whc->base + WUSBGENCMDSTS) & WUSBGENCMDSTS_ACTIVE) == 0, +			       WHC_GENCMD_TIMEOUT_MS); +	if (t == 0) { +		dev_err(&whc->umc->dev, "generic command timeout (%04x/%04x)\n", +			le_readl(whc->base + WUSBGENCMDSTS), +			le_readl(whc->base + WUSBGENCMDPARAMS)); +		return -ETIMEDOUT; +	} + +	if (addr) { +		memcpy(whc->gen_cmd_buf, addr, len); +		dma_addr = whc->gen_cmd_buf_dma; +	} else +		dma_addr = 0; + +	/* Poke registers to start cmd. */ +	spin_lock_irqsave(&whc->lock, flags); + +	le_writel(params, whc->base + WUSBGENCMDPARAMS); +	le_writeq(dma_addr, whc->base + WUSBGENADDR); + +	le_writel(WUSBGENCMDSTS_ACTIVE | WUSBGENCMDSTS_IOC | cmd, +		  whc->base + WUSBGENCMDSTS); + +	spin_unlock_irqrestore(&whc->lock, flags); + +	mutex_unlock(&whc->mutex); + +	return 0; +} diff --git a/drivers/usb/host/whci/init.c b/drivers/usb/host/whci/init.c new file mode 100644 index 00000000000..34a783cb013 --- /dev/null +++ b/drivers/usb/host/whci/init.c @@ -0,0 +1,188 @@ +/* + * Wireless Host Controller (WHC) initialization. + * + * Copyright (C) 2007 Cambridge Silicon Radio Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ +#include <linux/kernel.h> +#include <linux/dma-mapping.h> +#include <linux/uwb/umc.h> + +#include "../../wusbcore/wusbhc.h" + +#include "whcd.h" + +/* + * Reset the host controller. + */ +static void whc_hw_reset(struct whc *whc) +{ +	le_writel(WUSBCMD_WHCRESET, whc->base + WUSBCMD); +	whci_wait_for(&whc->umc->dev, whc->base + WUSBCMD, WUSBCMD_WHCRESET, 0, +		      100, "reset"); +} + +static void whc_hw_init_di_buf(struct whc *whc) +{ +	int d; + +	/* Disable all entries in the Device Information buffer. */ +	for (d = 0; d < whc->n_devices; d++) +		whc->di_buf[d].addr_sec_info = WHC_DI_DISABLE; + +	le_writeq(whc->di_buf_dma, whc->base + WUSBDEVICEINFOADDR); +} + +static void whc_hw_init_dn_buf(struct whc *whc) +{ +	/* Clear the Device Notification buffer to ensure the V (valid) +	 * bits are clear.  */ +	memset(whc->dn_buf, 0, 4096); + +	le_writeq(whc->dn_buf_dma, whc->base + WUSBDNTSBUFADDR); +} + +int whc_init(struct whc *whc) +{ +	u32 whcsparams; +	int ret, i; +	resource_size_t start, len; + +	spin_lock_init(&whc->lock); +	mutex_init(&whc->mutex); +	init_waitqueue_head(&whc->cmd_wq); +	init_waitqueue_head(&whc->async_list_wq); +	init_waitqueue_head(&whc->periodic_list_wq); +	whc->workqueue = create_singlethread_workqueue(dev_name(&whc->umc->dev)); +	if (whc->workqueue == NULL) { +		ret = -ENOMEM; +		goto error; +	} +	INIT_WORK(&whc->dn_work, whc_dn_work); + +	INIT_WORK(&whc->async_work, scan_async_work); +	INIT_LIST_HEAD(&whc->async_list); +	INIT_LIST_HEAD(&whc->async_removed_list); + +	INIT_WORK(&whc->periodic_work, scan_periodic_work); +	for (i = 0; i < 5; i++) +		INIT_LIST_HEAD(&whc->periodic_list[i]); +	INIT_LIST_HEAD(&whc->periodic_removed_list); + +	/* Map HC registers. */ +	start = whc->umc->resource.start; +	len   = whc->umc->resource.end - start + 1; +	if (!request_mem_region(start, len, "whci-hc")) { +		dev_err(&whc->umc->dev, "can't request HC region\n"); +		ret = -EBUSY; +		goto error; +	} +	whc->base_phys = start; +	whc->base = ioremap(start, len); +	if (!whc->base) { +		dev_err(&whc->umc->dev, "ioremap\n"); +		ret = -ENOMEM; +		goto error; +	} + +	whc_hw_reset(whc); + +	/* Read maximum number of devices, keys and MMC IEs. */ +	whcsparams = le_readl(whc->base + WHCSPARAMS); +	whc->n_devices = WHCSPARAMS_TO_N_DEVICES(whcsparams); +	whc->n_keys    = WHCSPARAMS_TO_N_KEYS(whcsparams); +	whc->n_mmc_ies = WHCSPARAMS_TO_N_MMC_IES(whcsparams); + +	dev_dbg(&whc->umc->dev, "N_DEVICES = %d, N_KEYS = %d, N_MMC_IES = %d\n", +		whc->n_devices, whc->n_keys, whc->n_mmc_ies); + +	whc->qset_pool = dma_pool_create("qset", &whc->umc->dev, +					 sizeof(struct whc_qset), 64, 0); +	if (whc->qset_pool == NULL) { +		ret = -ENOMEM; +		goto error; +	} + +	ret = asl_init(whc); +	if (ret < 0) +		goto error; +	ret = pzl_init(whc); +	if (ret < 0) +		goto error; + +	/* Allocate and initialize a buffer for generic commands, the +	   Device Information buffer, and the Device Notification +	   buffer. */ + +	whc->gen_cmd_buf = dma_alloc_coherent(&whc->umc->dev, WHC_GEN_CMD_DATA_LEN, +					      &whc->gen_cmd_buf_dma, GFP_KERNEL); +	if (whc->gen_cmd_buf == NULL) { +		ret = -ENOMEM; +		goto error; +	} + +	whc->dn_buf = dma_alloc_coherent(&whc->umc->dev, +					 sizeof(struct dn_buf_entry) * WHC_N_DN_ENTRIES, +					 &whc->dn_buf_dma, GFP_KERNEL); +	if (!whc->dn_buf) { +		ret = -ENOMEM; +		goto error; +	} +	whc_hw_init_dn_buf(whc); + +	whc->di_buf = dma_alloc_coherent(&whc->umc->dev, +					 sizeof(struct di_buf_entry) * whc->n_devices, +					 &whc->di_buf_dma, GFP_KERNEL); +	if (!whc->di_buf) { +		ret = -ENOMEM; +		goto error; +	} +	whc_hw_init_di_buf(whc); + +	return 0; + +error: +	whc_clean_up(whc); +	return ret; +} + +void whc_clean_up(struct whc *whc) +{ +	resource_size_t len; + +	if (whc->di_buf) +		dma_free_coherent(&whc->umc->dev, sizeof(struct di_buf_entry) * whc->n_devices, +				  whc->di_buf, whc->di_buf_dma); +	if (whc->dn_buf) +		dma_free_coherent(&whc->umc->dev, sizeof(struct dn_buf_entry) * WHC_N_DN_ENTRIES, +				  whc->dn_buf, whc->dn_buf_dma); +	if (whc->gen_cmd_buf) +		dma_free_coherent(&whc->umc->dev, WHC_GEN_CMD_DATA_LEN, +				  whc->gen_cmd_buf, whc->gen_cmd_buf_dma); + +	pzl_clean_up(whc); +	asl_clean_up(whc); + +	if (whc->qset_pool) +		dma_pool_destroy(whc->qset_pool); + +	len   = whc->umc->resource.end - whc->umc->resource.start + 1; +	if (whc->base) +		iounmap(whc->base); +	if (whc->base_phys) +		release_mem_region(whc->base_phys, len); + +	if (whc->workqueue) +		destroy_workqueue(whc->workqueue); +} diff --git a/drivers/usb/host/whci/int.c b/drivers/usb/host/whci/int.c new file mode 100644 index 00000000000..fce01174aa9 --- /dev/null +++ b/drivers/usb/host/whci/int.c @@ -0,0 +1,95 @@ +/* + * Wireless Host Controller (WHC) interrupt handling. + * + * Copyright (C) 2007 Cambridge Silicon Radio Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ +#include <linux/version.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/uwb/umc.h> + +#include "../../wusbcore/wusbhc.h" + +#include "whcd.h" + +static void transfer_done(struct whc *whc) +{ +	queue_work(whc->workqueue, &whc->async_work); +	queue_work(whc->workqueue, &whc->periodic_work); +} + +irqreturn_t whc_int_handler(struct usb_hcd *hcd) +{ +	struct wusbhc *wusbhc = usb_hcd_to_wusbhc(hcd); +	struct whc *whc = wusbhc_to_whc(wusbhc); +	u32 sts; + +	sts = le_readl(whc->base + WUSBSTS); +	if (!(sts & WUSBSTS_INT_MASK)) +		return IRQ_NONE; +	le_writel(sts & WUSBSTS_INT_MASK, whc->base + WUSBSTS); + +	if (sts & WUSBSTS_GEN_CMD_DONE) +		wake_up(&whc->cmd_wq); + +	if (sts & WUSBSTS_HOST_ERR) +		dev_err(&whc->umc->dev, "FIXME: host system error\n"); + +	if (sts & WUSBSTS_ASYNC_SCHED_SYNCED) +		wake_up(&whc->async_list_wq); + +	if (sts & WUSBSTS_PERIODIC_SCHED_SYNCED) +		wake_up(&whc->periodic_list_wq); + +	if (sts & WUSBSTS_DNTS_INT) +		queue_work(whc->workqueue, &whc->dn_work); + +	/* +	 * A transfer completed (see [WHCI] section 4.7.1.2 for when +	 * this occurs). +	 */ +	if (sts & (WUSBSTS_INT | WUSBSTS_ERR_INT)) +		transfer_done(whc); + +	return IRQ_HANDLED; +} + +static int process_dn_buf(struct whc *whc) +{ +	struct wusbhc *wusbhc = &whc->wusbhc; +	struct dn_buf_entry *dn; +	int processed = 0; + +	for (dn = whc->dn_buf; dn < whc->dn_buf + WHC_N_DN_ENTRIES; dn++) { +		if (dn->status & WHC_DN_STATUS_VALID) { +			wusbhc_handle_dn(wusbhc, dn->src_addr, +					 (struct wusb_dn_hdr *)dn->dn_data, +					 dn->msg_size); +			dn->status &= ~WHC_DN_STATUS_VALID; +			processed++; +		} +	} +	return processed; +} + +void whc_dn_work(struct work_struct *work) +{ +	struct whc *whc = container_of(work, struct whc, dn_work); +	int processed; + +	do { +		processed = process_dn_buf(whc); +	} while (processed); +} diff --git a/drivers/usb/host/whci/pzl.c b/drivers/usb/host/whci/pzl.c new file mode 100644 index 00000000000..8d62df0c330 --- /dev/null +++ b/drivers/usb/host/whci/pzl.c @@ -0,0 +1,398 @@ +/* + * Wireless Host Controller (WHC) periodic schedule management. + * + * Copyright (C) 2007 Cambridge Silicon Radio Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ +#include <linux/kernel.h> +#include <linux/dma-mapping.h> +#include <linux/uwb/umc.h> +#include <linux/usb.h> +#define D_LOCAL 0 +#include <linux/uwb/debug.h> + +#include "../../wusbcore/wusbhc.h" + +#include "whcd.h" + +#if D_LOCAL >= 4 +static void dump_pzl(struct whc *whc, const char *tag) +{ +	struct device *dev = &whc->umc->dev; +	struct whc_qset *qset; +	int period = 0; + +	d_printf(4, dev, "PZL %s\n", tag); + +	for (period = 0; period < 5; period++) { +		d_printf(4, dev, "Period %d\n", period); +		list_for_each_entry(qset, &whc->periodic_list[period], list_node) { +			dump_qset(qset, dev); +		} +	} +} +#else +static inline void dump_pzl(struct whc *whc, const char *tag) +{ +} +#endif + +static void update_pzl_pointers(struct whc *whc, int period, u64 addr) +{ +	switch (period) { +	case 0: +		whc_qset_set_link_ptr(&whc->pz_list[0], addr); +		whc_qset_set_link_ptr(&whc->pz_list[2], addr); +		whc_qset_set_link_ptr(&whc->pz_list[4], addr); +		whc_qset_set_link_ptr(&whc->pz_list[6], addr); +		whc_qset_set_link_ptr(&whc->pz_list[8], addr); +		whc_qset_set_link_ptr(&whc->pz_list[10], addr); +		whc_qset_set_link_ptr(&whc->pz_list[12], addr); +		whc_qset_set_link_ptr(&whc->pz_list[14], addr); +		break; +	case 1: +		whc_qset_set_link_ptr(&whc->pz_list[1], addr); +		whc_qset_set_link_ptr(&whc->pz_list[5], addr); +		whc_qset_set_link_ptr(&whc->pz_list[9], addr); +		whc_qset_set_link_ptr(&whc->pz_list[13], addr); +		break; +	case 2: +		whc_qset_set_link_ptr(&whc->pz_list[3], addr); +		whc_qset_set_link_ptr(&whc->pz_list[11], addr); +		break; +	case 3: +		whc_qset_set_link_ptr(&whc->pz_list[7], addr); +		break; +	case 4: +		whc_qset_set_link_ptr(&whc->pz_list[15], addr); +		break; +	} +} + +/* + * Return the 'period' to use for this qset.  The minimum interval for + * the endpoint is used so whatever urbs are submitted the device is + * polled often enough. + */ +static int qset_get_period(struct whc *whc, struct whc_qset *qset) +{ +	uint8_t bInterval = qset->ep->desc.bInterval; + +	if (bInterval < 6) +		bInterval = 6; +	if (bInterval > 10) +		bInterval = 10; +	return bInterval - 6; +} + +static void qset_insert_in_sw_list(struct whc *whc, struct whc_qset *qset) +{ +	int period; + +	period = qset_get_period(whc, qset); + +	qset_clear(whc, qset); +	list_move(&qset->list_node, &whc->periodic_list[period]); +	qset->in_sw_list = true; +} + +static void pzl_qset_remove(struct whc *whc, struct whc_qset *qset) +{ +	list_move(&qset->list_node, &whc->periodic_removed_list); +	qset->in_hw_list = false; +	qset->in_sw_list = false; +} + +/** + * pzl_process_qset - process any recently inactivated or halted qTDs + * in a qset. + * + * After inactive qTDs are removed, new qTDs can be added if the + * urb queue still contains URBs. + * + * Returns the schedule updates required. + */ +static enum whc_update pzl_process_qset(struct whc *whc, struct whc_qset *qset) +{ +	enum whc_update update = 0; +	uint32_t status = 0; + +	while (qset->ntds) { +		struct whc_qtd *td; +		int t; + +		t = qset->td_start; +		td = &qset->qtd[qset->td_start]; +		status = le32_to_cpu(td->status); + +		/* +		 * Nothing to do with a still active qTD. +		 */ +		if (status & QTD_STS_ACTIVE) +			break; + +		if (status & QTD_STS_HALTED) { +			/* Ug, an error. */ +			process_halted_qtd(whc, qset, td); +			goto done; +		} + +		/* Mmm, a completed qTD. */ +		process_inactive_qtd(whc, qset, td); +	} + +	update |= qset_add_qtds(whc, qset); + +done: +	/* +	 * If there are no qTDs in this qset, remove it from the PZL. +	 */ +	if (qset->remove && qset->ntds == 0) { +		pzl_qset_remove(whc, qset); +		update |= WHC_UPDATE_REMOVED; +	} + +	return update; +} + +/** + * pzl_start - start the periodic schedule + * @whc: the WHCI host controller + * + * The PZL must be valid (e.g., all entries in the list should have + * the T bit set). + */ +void pzl_start(struct whc *whc) +{ +	le_writeq(whc->pz_list_dma, whc->base + WUSBPERIODICLISTBASE); + +	whc_write_wusbcmd(whc, WUSBCMD_PERIODIC_EN, WUSBCMD_PERIODIC_EN); +	whci_wait_for(&whc->umc->dev, whc->base + WUSBSTS, +		      WUSBSTS_PERIODIC_SCHED, WUSBSTS_PERIODIC_SCHED, +		      1000, "start PZL"); +} + +/** + * pzl_stop - stop the periodic schedule + * @whc: the WHCI host controller + */ +void pzl_stop(struct whc *whc) +{ +	whc_write_wusbcmd(whc, WUSBCMD_PERIODIC_EN, 0); +	whci_wait_for(&whc->umc->dev, whc->base + WUSBSTS, +		      WUSBSTS_PERIODIC_SCHED, 0, +		      1000, "stop PZL"); +} + +void pzl_update(struct whc *whc, uint32_t wusbcmd) +{ +	whc_write_wusbcmd(whc, wusbcmd, wusbcmd); +	wait_event(whc->periodic_list_wq, +		   (le_readl(whc->base + WUSBCMD) & WUSBCMD_PERIODIC_UPDATED) == 0); +} + +static void update_pzl_hw_view(struct whc *whc) +{ +	struct whc_qset *qset, *t; +	int period; +	u64 tmp_qh = 0; + +	for (period = 0; period < 5; period++) { +		list_for_each_entry_safe(qset, t, &whc->periodic_list[period], list_node) { +			whc_qset_set_link_ptr(&qset->qh.link, tmp_qh); +			tmp_qh = qset->qset_dma; +			qset->in_hw_list = true; +		} +		update_pzl_pointers(whc, period, tmp_qh); +	} +} + +/** + * scan_periodic_work - scan the PZL for qsets to process. + * + * Process each qset in the PZL in turn and then signal the WHC that + * the PZL has been updated. + * + * Then start, stop or update the periodic schedule as required. + */ +void scan_periodic_work(struct work_struct *work) +{ +	struct whc *whc = container_of(work, struct whc, periodic_work); +	struct whc_qset *qset, *t; +	enum whc_update update = 0; +	int period; + +	spin_lock_irq(&whc->lock); + +	dump_pzl(whc, "before processing"); + +	for (period = 4; period >= 0; period--) { +		list_for_each_entry_safe(qset, t, &whc->periodic_list[period], list_node) { +			if (!qset->in_hw_list) +				update |= WHC_UPDATE_ADDED; +			update |= pzl_process_qset(whc, qset); +		} +	} + +	if (update & (WHC_UPDATE_ADDED | WHC_UPDATE_REMOVED)) +		update_pzl_hw_view(whc); + +	dump_pzl(whc, "after processing"); + +	spin_unlock_irq(&whc->lock); + +	if (update) { +		uint32_t wusbcmd = WUSBCMD_PERIODIC_UPDATED | WUSBCMD_PERIODIC_SYNCED_DB; +		if (update & WHC_UPDATE_REMOVED) +			wusbcmd |= WUSBCMD_PERIODIC_QSET_RM; +		pzl_update(whc, wusbcmd); +	} + +	/* +	 * Now that the PZL is updated, complete the removal of any +	 * removed qsets. +	 */ +	spin_lock(&whc->lock); + +	list_for_each_entry_safe(qset, t, &whc->periodic_removed_list, list_node) { +		qset_remove_complete(whc, qset); +	} + +	spin_unlock(&whc->lock); +} + +/** + * pzl_urb_enqueue - queue an URB onto the periodic list (PZL) + * @whc: the WHCI host controller + * @urb: the URB to enqueue + * @mem_flags: flags for any memory allocations + * + * The qset for the endpoint is obtained and the urb queued on to it. + * + * Work is scheduled to update the hardware's view of the PZL. + */ +int pzl_urb_enqueue(struct whc *whc, struct urb *urb, gfp_t mem_flags) +{ +	struct whc_qset *qset; +	int err; +	unsigned long flags; + +	spin_lock_irqsave(&whc->lock, flags); + +	qset = get_qset(whc, urb, GFP_ATOMIC); +	if (qset == NULL) +		err = -ENOMEM; +	else +		err = qset_add_urb(whc, qset, urb, GFP_ATOMIC); +	if (!err) { +		usb_hcd_link_urb_to_ep(&whc->wusbhc.usb_hcd, urb); +		if (!qset->in_sw_list) +			qset_insert_in_sw_list(whc, qset); +	} + +	spin_unlock_irqrestore(&whc->lock, flags); + +	if (!err) +		queue_work(whc->workqueue, &whc->periodic_work); + +	return 0; +} + +/** + * pzl_urb_dequeue - remove an URB (qset) from the periodic list + * @whc: the WHCI host controller + * @urb: the URB to dequeue + * @status: the current status of the URB + * + * URBs that do yet have qTDs can simply be removed from the software + * queue, otherwise the qset must be removed so the qTDs can be safely + * removed. + */ +int pzl_urb_dequeue(struct whc *whc, struct urb *urb, int status) +{ +	struct whc_urb *wurb = urb->hcpriv; +	struct whc_qset *qset = wurb->qset; +	struct whc_std *std, *t; +	int ret; +	unsigned long flags; + +	spin_lock_irqsave(&whc->lock, flags); + +	ret = usb_hcd_check_unlink_urb(&whc->wusbhc.usb_hcd, urb, status); +	if (ret < 0) +		goto out; + +	list_for_each_entry_safe(std, t, &qset->stds, list_node) { +		if (std->urb == urb) +			qset_free_std(whc, std); +		else +			std->qtd = NULL; /* so this std is re-added when the qset is */ +	} + +	pzl_qset_remove(whc, qset); +	wurb->status = status; +	wurb->is_async = false; +	queue_work(whc->workqueue, &wurb->dequeue_work); + +out: +	spin_unlock_irqrestore(&whc->lock, flags); + +	return ret; +} + +/** + * pzl_qset_delete - delete a qset from the PZL + */ +void pzl_qset_delete(struct whc *whc, struct whc_qset *qset) +{ +	qset->remove = 1; +	queue_work(whc->workqueue, &whc->periodic_work); +	qset_delete(whc, qset); +} + + +/** + * pzl_init - initialize the periodic zone list + * @whc: the WHCI host controller + */ +int pzl_init(struct whc *whc) +{ +	int i; + +	whc->pz_list = dma_alloc_coherent(&whc->umc->dev, sizeof(u64) * 16, +					  &whc->pz_list_dma, GFP_KERNEL); +	if (whc->pz_list == NULL) +		return -ENOMEM; + +	/* Set T bit on all elements in PZL. */ +	for (i = 0; i < 16; i++) +		whc->pz_list[i] = cpu_to_le64(QH_LINK_NTDS(8) | QH_LINK_T); + +	le_writeq(whc->pz_list_dma, whc->base + WUSBPERIODICLISTBASE); + +	return 0; +} + +/** + * pzl_clean_up - free PZL resources + * @whc: the WHCI host controller + * + * The PZL is stopped and empty. + */ +void pzl_clean_up(struct whc *whc) +{ +	if (whc->pz_list) +		dma_free_coherent(&whc->umc->dev,  sizeof(u64) * 16, whc->pz_list, +				  whc->pz_list_dma); +} diff --git a/drivers/usb/host/whci/qset.c b/drivers/usb/host/whci/qset.c new file mode 100644 index 00000000000..0420037d2e1 --- /dev/null +++ b/drivers/usb/host/whci/qset.c @@ -0,0 +1,567 @@ +/* + * Wireless Host Controller (WHC) qset management. + * + * Copyright (C) 2007 Cambridge Silicon Radio Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ +#include <linux/kernel.h> +#include <linux/dma-mapping.h> +#include <linux/uwb/umc.h> +#include <linux/usb.h> + +#include "../../wusbcore/wusbhc.h" + +#include "whcd.h" + +void dump_qset(struct whc_qset *qset, struct device *dev) +{ +	struct whc_std *std; +	struct urb *urb = NULL; +	int i; + +	dev_dbg(dev, "qset %08x\n", (u32)qset->qset_dma); +	dev_dbg(dev, "  -> %08x\n", (u32)qset->qh.link); +	dev_dbg(dev, "  info: %08x %08x %08x\n", +		qset->qh.info1, qset->qh.info2,  qset->qh.info3); +	dev_dbg(dev, "  sts: %04x errs: %d\n", qset->qh.status, qset->qh.err_count); +	dev_dbg(dev, "  TD: sts: %08x opts: %08x\n", +		qset->qh.overlay.qtd.status, qset->qh.overlay.qtd.options); + +	for (i = 0; i < WHCI_QSET_TD_MAX; i++) { +		dev_dbg(dev, "  %c%c TD[%d]: sts: %08x opts: %08x ptr: %08x\n", +			i == qset->td_start ? 'S' : ' ', +			i == qset->td_end ? 'E' : ' ', +			i, qset->qtd[i].status, qset->qtd[i].options, +			(u32)qset->qtd[i].page_list_ptr); +	} +	dev_dbg(dev, "  ntds: %d\n", qset->ntds); +	list_for_each_entry(std, &qset->stds, list_node) { +		if (urb != std->urb) { +			urb = std->urb; +			dev_dbg(dev, "  urb %p transferred: %d bytes\n", urb, +				urb->actual_length); +		} +		if (std->qtd) +			dev_dbg(dev, "    sTD[%td]: %zu bytes @ %08x\n", +				std->qtd - &qset->qtd[0], +				std->len, std->num_pointers ? +				(u32)(std->pl_virt[0].buf_ptr) : (u32)std->dma_addr); +		else +			dev_dbg(dev, "    sTD[-]: %zd bytes @ %08x\n", +				std->len, std->num_pointers ? +				(u32)(std->pl_virt[0].buf_ptr) : (u32)std->dma_addr); +	} +} + +struct whc_qset *qset_alloc(struct whc *whc, gfp_t mem_flags) +{ +	struct whc_qset *qset; +	dma_addr_t dma; + +	qset = dma_pool_alloc(whc->qset_pool, mem_flags, &dma); +	if (qset == NULL) +		return NULL; +	memset(qset, 0, sizeof(struct whc_qset)); + +	qset->qset_dma = dma; +	qset->whc = whc; + +	INIT_LIST_HEAD(&qset->list_node); +	INIT_LIST_HEAD(&qset->stds); + +	return qset; +} + +/** + * qset_fill_qh - fill the static endpoint state in a qset's QHead + * @qset: the qset whose QH needs initializing with static endpoint + *        state + * @urb:  an urb for a transfer to this endpoint + */ +static void qset_fill_qh(struct whc_qset *qset, struct urb *urb) +{ +	struct usb_device *usb_dev = urb->dev; +	struct usb_wireless_ep_comp_descriptor *epcd; +	bool is_out; + +	is_out = usb_pipeout(urb->pipe); + +	epcd = (struct usb_wireless_ep_comp_descriptor *)qset->ep->extra; + +	if (epcd) { +		qset->max_seq = epcd->bMaxSequence; +		qset->max_burst = epcd->bMaxBurst; +	} else { +		qset->max_seq = 2; +		qset->max_burst = 1; +	} + +	qset->qh.info1 = cpu_to_le32( +		QH_INFO1_EP(usb_pipeendpoint(urb->pipe)) +		| (is_out ? QH_INFO1_DIR_OUT : QH_INFO1_DIR_IN) +		| usb_pipe_to_qh_type(urb->pipe) +		| QH_INFO1_DEV_INFO_IDX(wusb_port_no_to_idx(usb_dev->portnum)) +		| QH_INFO1_MAX_PKT_LEN(usb_maxpacket(urb->dev, urb->pipe, is_out)) +		); +	qset->qh.info2 = cpu_to_le32( +		QH_INFO2_BURST(qset->max_burst) +		| QH_INFO2_DBP(0) +		| QH_INFO2_MAX_COUNT(3) +		| QH_INFO2_MAX_RETRY(3) +		| QH_INFO2_MAX_SEQ(qset->max_seq - 1) +		); +	/* FIXME: where can we obtain these Tx parameters from?  Why +	 * doesn't the chip know what Tx power to use? It knows the Rx +	 * strength and can presumably guess the Tx power required +	 * from that? */ +	qset->qh.info3 = cpu_to_le32( +		QH_INFO3_TX_RATE_53_3 +		| QH_INFO3_TX_PWR(0) /* 0 == max power */ +		); +} + +/** + * qset_clear - clear fields in a qset so it may be reinserted into a + * schedule + */ +void qset_clear(struct whc *whc, struct whc_qset *qset) +{ +	qset->td_start = qset->td_end = qset->ntds = 0; +	qset->remove = 0; + +	qset->qh.link = cpu_to_le32(QH_LINK_NTDS(8) | QH_LINK_T); +	qset->qh.status = cpu_to_le16(QH_STATUS_ICUR(qset->td_start)); +	qset->qh.err_count = 0; +	qset->qh.cur_window = cpu_to_le32((1 << qset->max_burst) - 1); +	qset->qh.scratch[0] = 0; +	qset->qh.scratch[1] = 0; +	qset->qh.scratch[2] = 0; + +	memset(&qset->qh.overlay, 0, sizeof(qset->qh.overlay)); + +	init_completion(&qset->remove_complete); +} + +/** + * get_qset - get the qset for an async endpoint + * + * A new qset is created if one does not already exist. + */ +struct whc_qset *get_qset(struct whc *whc, struct urb *urb, +				 gfp_t mem_flags) +{ +	struct whc_qset *qset; + +	qset = urb->ep->hcpriv; +	if (qset == NULL) { +		qset = qset_alloc(whc, mem_flags); +		if (qset == NULL) +			return NULL; + +		qset->ep = urb->ep; +		urb->ep->hcpriv = qset; +		qset_fill_qh(qset, urb); +	} +	return qset; +} + +void qset_remove_complete(struct whc *whc, struct whc_qset *qset) +{ +	list_del_init(&qset->list_node); +	complete(&qset->remove_complete); +} + +/** + * qset_add_qtds - add qTDs for an URB to a qset + * + * Returns true if the list (ASL/PZL) must be updated because (for a + * WHCI 0.95 controller) an activated qTD was pointed to be iCur. + */ +enum whc_update qset_add_qtds(struct whc *whc, struct whc_qset *qset) +{ +	struct whc_std *std; +	enum whc_update update = 0; + +	list_for_each_entry(std, &qset->stds, list_node) { +		struct whc_qtd *qtd; +		uint32_t status; + +		if (qset->ntds >= WHCI_QSET_TD_MAX +		    || (qset->pause_after_urb && std->urb != qset->pause_after_urb)) +			break; + +		if (std->qtd) +			continue; /* already has a qTD */ + +		qtd = std->qtd = &qset->qtd[qset->td_end]; + +		/* Fill in setup bytes for control transfers. */ +		if (usb_pipecontrol(std->urb->pipe)) +			memcpy(qtd->setup, std->urb->setup_packet, 8); + +		status = QTD_STS_ACTIVE | QTD_STS_LEN(std->len); + +		if (whc_std_last(std) && usb_pipeout(std->urb->pipe)) +			status |= QTD_STS_LAST_PKT; + +		/* +		 * For an IN transfer the iAlt field should be set so +		 * the h/w will automatically advance to the next +		 * transfer. However, if there are 8 or more TDs +		 * remaining in this transfer then iAlt cannot be set +		 * as it could point to somewhere in this transfer. +		 */ +		if (std->ntds_remaining < WHCI_QSET_TD_MAX) { +			int ialt; +			ialt = (qset->td_end + std->ntds_remaining) % WHCI_QSET_TD_MAX; +			status |= QTD_STS_IALT(ialt); +		} else if (usb_pipein(std->urb->pipe)) +			qset->pause_after_urb = std->urb; + +		if (std->num_pointers) +			qtd->options = cpu_to_le32(QTD_OPT_IOC); +		else +			qtd->options = cpu_to_le32(QTD_OPT_IOC | QTD_OPT_SMALL); +		qtd->page_list_ptr = cpu_to_le64(std->dma_addr); + +		qtd->status = cpu_to_le32(status); + +		if (QH_STATUS_TO_ICUR(qset->qh.status) == qset->td_end) +			update = WHC_UPDATE_UPDATED; + +		if (++qset->td_end >= WHCI_QSET_TD_MAX) +			qset->td_end = 0; +		qset->ntds++; +	} + +	return update; +} + +/** + * qset_remove_qtd - remove the first qTD from a qset. + * + * The qTD might be still active (if it's part of a IN URB that + * resulted in a short read) so ensure it's deactivated. + */ +static void qset_remove_qtd(struct whc *whc, struct whc_qset *qset) +{ +	qset->qtd[qset->td_start].status = 0; + +	if (++qset->td_start >= WHCI_QSET_TD_MAX) +		qset->td_start = 0; +	qset->ntds--; +} + +/** + * qset_free_std - remove an sTD and free it. + * @whc: the WHCI host controller + * @std: the sTD to remove and free. + */ +void qset_free_std(struct whc *whc, struct whc_std *std) +{ +	list_del(&std->list_node); +	if (std->num_pointers) { +		dma_unmap_single(whc->wusbhc.dev, std->dma_addr, +				 std->num_pointers * sizeof(struct whc_page_list_entry), +				 DMA_TO_DEVICE); +		kfree(std->pl_virt); +	} + +	kfree(std); +} + +/** + * qset_remove_qtds - remove an URB's qTDs (and sTDs). + */ +static void qset_remove_qtds(struct whc *whc, struct whc_qset *qset, +			     struct urb *urb) +{ +	struct whc_std *std, *t; + +	list_for_each_entry_safe(std, t, &qset->stds, list_node) { +		if (std->urb != urb) +			break; +		if (std->qtd != NULL) +			qset_remove_qtd(whc, qset); +		qset_free_std(whc, std); +	} +} + +/** + * qset_free_stds - free any remaining sTDs for an URB. + */ +static void qset_free_stds(struct whc_qset *qset, struct urb *urb) +{ +	struct whc_std *std, *t; + +	list_for_each_entry_safe(std, t, &qset->stds, list_node) { +		if (std->urb == urb) +			qset_free_std(qset->whc, std); +	} +} + +static int qset_fill_page_list(struct whc *whc, struct whc_std *std, gfp_t mem_flags) +{ +	dma_addr_t dma_addr = std->dma_addr; +	dma_addr_t sp, ep; +	size_t std_len = std->len; +	size_t pl_len; +	int p; + +	sp = ALIGN(dma_addr, WHCI_PAGE_SIZE); +	ep = dma_addr + std_len; +	std->num_pointers = DIV_ROUND_UP(ep - sp, WHCI_PAGE_SIZE); + +	pl_len = std->num_pointers * sizeof(struct whc_page_list_entry); +	std->pl_virt = kmalloc(pl_len, mem_flags); +	if (std->pl_virt == NULL) +		return -ENOMEM; +	std->dma_addr = dma_map_single(whc->wusbhc.dev, std->pl_virt, pl_len, DMA_TO_DEVICE); + +	for (p = 0; p < std->num_pointers; p++) { +		std->pl_virt[p].buf_ptr = cpu_to_le64(dma_addr); +		dma_addr = ALIGN(dma_addr + WHCI_PAGE_SIZE, WHCI_PAGE_SIZE); +	} + +	return 0; +} + +/** + * urb_dequeue_work - executes asl/pzl update and gives back the urb to the system. + */ +static void urb_dequeue_work(struct work_struct *work) +{ +	struct whc_urb *wurb = container_of(work, struct whc_urb, dequeue_work); +	struct whc_qset *qset = wurb->qset; +	struct whc *whc = qset->whc; +	unsigned long flags; + +	if (wurb->is_async == true) +		asl_update(whc, WUSBCMD_ASYNC_UPDATED +			   | WUSBCMD_ASYNC_SYNCED_DB +			   | WUSBCMD_ASYNC_QSET_RM); +	else +		pzl_update(whc, WUSBCMD_PERIODIC_UPDATED +			   | WUSBCMD_PERIODIC_SYNCED_DB +			   | WUSBCMD_PERIODIC_QSET_RM); + +	spin_lock_irqsave(&whc->lock, flags); +	qset_remove_urb(whc, qset, wurb->urb, wurb->status); +	spin_unlock_irqrestore(&whc->lock, flags); +} + +/** + * qset_add_urb - add an urb to the qset's queue. + * + * The URB is chopped into sTDs, one for each qTD that will required. + * At least one qTD (and sTD) is required even if the transfer has no + * data (e.g., for some control transfers). + */ +int qset_add_urb(struct whc *whc, struct whc_qset *qset, struct urb *urb, +	gfp_t mem_flags) +{ +	struct whc_urb *wurb; +	int remaining = urb->transfer_buffer_length; +	u64 transfer_dma = urb->transfer_dma; +	int ntds_remaining; + +	ntds_remaining = DIV_ROUND_UP(remaining, QTD_MAX_XFER_SIZE); +	if (ntds_remaining == 0) +		ntds_remaining = 1; + +	wurb = kzalloc(sizeof(struct whc_urb), mem_flags); +	if (wurb == NULL) +		goto err_no_mem; +	urb->hcpriv = wurb; +	wurb->qset = qset; +	wurb->urb = urb; +	INIT_WORK(&wurb->dequeue_work, urb_dequeue_work); + +	while (ntds_remaining) { +		struct whc_std *std; +		size_t std_len; + +		std = kmalloc(sizeof(struct whc_std), mem_flags); +		if (std == NULL) +			goto err_no_mem; + +		std_len = remaining; +		if (std_len > QTD_MAX_XFER_SIZE) +			std_len = QTD_MAX_XFER_SIZE; + +		std->urb = urb; +		std->dma_addr = transfer_dma; +		std->len = std_len; +		std->ntds_remaining = ntds_remaining; +		std->qtd = NULL; + +		INIT_LIST_HEAD(&std->list_node); +		list_add_tail(&std->list_node, &qset->stds); + +		if (std_len > WHCI_PAGE_SIZE) { +			if (qset_fill_page_list(whc, std, mem_flags) < 0) +				goto err_no_mem; +		} else +			std->num_pointers = 0; + +		ntds_remaining--; +		remaining -= std_len; +		transfer_dma += std_len; +	} + +	return 0; + +err_no_mem: +	qset_free_stds(qset, urb); +	return -ENOMEM; +} + +/** + * qset_remove_urb - remove an URB from the urb queue. + * + * The URB is returned to the USB subsystem. + */ +void qset_remove_urb(struct whc *whc, struct whc_qset *qset, +			    struct urb *urb, int status) +{ +	struct wusbhc *wusbhc = &whc->wusbhc; +	struct whc_urb *wurb = urb->hcpriv; + +	usb_hcd_unlink_urb_from_ep(&wusbhc->usb_hcd, urb); +	/* Drop the lock as urb->complete() may enqueue another urb. */ +	spin_unlock(&whc->lock); +	wusbhc_giveback_urb(wusbhc, urb, status); +	spin_lock(&whc->lock); + +	kfree(wurb); +} + +/** + * get_urb_status_from_qtd - get the completed urb status from qTD status + * @urb:    completed urb + * @status: qTD status + */ +static int get_urb_status_from_qtd(struct urb *urb, u32 status) +{ +	if (status & QTD_STS_HALTED) { +		if (status & QTD_STS_DBE) +			return usb_pipein(urb->pipe) ? -ENOSR : -ECOMM; +		else if (status & QTD_STS_BABBLE) +			return -EOVERFLOW; +		else if (status & QTD_STS_RCE) +			return -ETIME; +		return -EPIPE; +	} +	if (usb_pipein(urb->pipe) +	    && (urb->transfer_flags & URB_SHORT_NOT_OK) +	    && urb->actual_length < urb->transfer_buffer_length) +		return -EREMOTEIO; +	return 0; +} + +/** + * process_inactive_qtd - process an inactive (but not halted) qTD. + * + * Update the urb with the transfer bytes from the qTD, if the urb is + * completely transfered or (in the case of an IN only) the LPF is + * set, then the transfer is complete and the urb should be returned + * to the system. + */ +void process_inactive_qtd(struct whc *whc, struct whc_qset *qset, +				 struct whc_qtd *qtd) +{ +	struct whc_std *std = list_first_entry(&qset->stds, struct whc_std, list_node); +	struct urb *urb = std->urb; +	uint32_t status; +	bool complete; + +	status = le32_to_cpu(qtd->status); + +	urb->actual_length += std->len - QTD_STS_TO_LEN(status); + +	if (usb_pipein(urb->pipe) && (status & QTD_STS_LAST_PKT)) +		complete = true; +	else +		complete = whc_std_last(std); + +	qset_remove_qtd(whc, qset); +	qset_free_std(whc, std); + +	/* +	 * Transfers for this URB are complete?  Then return it to the +	 * USB subsystem. +	 */ +	if (complete) { +		qset_remove_qtds(whc, qset, urb); +		qset_remove_urb(whc, qset, urb, get_urb_status_from_qtd(urb, status)); + +		/* +		 * If iAlt isn't valid then the hardware didn't +		 * advance iCur. Adjust the start and end pointers to +		 * match iCur. +		 */ +		if (!(status & QTD_STS_IALT_VALID)) +			qset->td_start = qset->td_end +				= QH_STATUS_TO_ICUR(le16_to_cpu(qset->qh.status)); +		qset->pause_after_urb = NULL; +	} +} + +/** + * process_halted_qtd - process a qset with a halted qtd + * + * Remove all the qTDs for the failed URB and return the failed URB to + * the USB subsystem.  Then remove all other qTDs so the qset can be + * removed. + * + * FIXME: this is the point where rate adaptation can be done.  If a + * transfer failed because it exceeded the maximum number of retries + * then it could be reactivated with a slower rate without having to + * remove the qset. + */ +void process_halted_qtd(struct whc *whc, struct whc_qset *qset, +			       struct whc_qtd *qtd) +{ +	struct whc_std *std = list_first_entry(&qset->stds, struct whc_std, list_node); +	struct urb *urb = std->urb; +	int urb_status; + +	urb_status = get_urb_status_from_qtd(urb, le32_to_cpu(qtd->status)); + +	qset_remove_qtds(whc, qset, urb); +	qset_remove_urb(whc, qset, urb, urb_status); + +	list_for_each_entry(std, &qset->stds, list_node) { +		if (qset->ntds == 0) +			break; +		qset_remove_qtd(whc, qset); +		std->qtd = NULL; +	} + +	qset->remove = 1; +} + +void qset_free(struct whc *whc, struct whc_qset *qset) +{ +	dma_pool_free(whc->qset_pool, qset, qset->qset_dma); +} + +/** + * qset_delete - wait for a qset to be unused, then free it. + */ +void qset_delete(struct whc *whc, struct whc_qset *qset) +{ +	wait_for_completion(&qset->remove_complete); +	qset_free(whc, qset); +} diff --git a/drivers/usb/host/whci/whcd.h b/drivers/usb/host/whci/whcd.h new file mode 100644 index 00000000000..1d2a53bd39f --- /dev/null +++ b/drivers/usb/host/whci/whcd.h @@ -0,0 +1,197 @@ +/* + * Wireless Host Controller (WHC) private header. + * + * Copyright (C) 2007 Cambridge Silicon Radio Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ +#ifndef __WHCD_H +#define __WHCD_H + +#include <linux/uwb/whci.h> +#include <linux/workqueue.h> + +#include "whci-hc.h" + +/* Generic command timeout. */ +#define WHC_GENCMD_TIMEOUT_MS 100 + + +struct whc { +	struct wusbhc wusbhc; +	struct umc_dev *umc; + +	resource_size_t base_phys; +	void __iomem *base; +	int irq; + +	u8 n_devices; +	u8 n_keys; +	u8 n_mmc_ies; + +	u64 *pz_list; +	struct dn_buf_entry *dn_buf; +	struct di_buf_entry *di_buf; +	dma_addr_t pz_list_dma; +	dma_addr_t dn_buf_dma; +	dma_addr_t di_buf_dma; + +	spinlock_t   lock; +	struct mutex mutex; + +	void *            gen_cmd_buf; +	dma_addr_t        gen_cmd_buf_dma; +	wait_queue_head_t cmd_wq; + +	struct workqueue_struct *workqueue; +	struct work_struct       dn_work; + +	struct dma_pool *qset_pool; + +	struct list_head async_list; +	struct list_head async_removed_list; +	wait_queue_head_t async_list_wq; +	struct work_struct async_work; + +	struct list_head periodic_list[5]; +	struct list_head periodic_removed_list; +	wait_queue_head_t periodic_list_wq; +	struct work_struct periodic_work; +}; + +#define wusbhc_to_whc(w) (container_of((w), struct whc, wusbhc)) + +/** + * struct whc_std - a software TD. + * @urb: the URB this sTD is for. + * @offset: start of the URB's data for this TD. + * @len: the length of data in the associated TD. + * @ntds_remaining: number of TDs (starting from this one) in this transfer. + * + * Queued URBs may require more TDs than are available in a qset so we + * use a list of these "software TDs" (sTDs) to hold per-TD data. + */ +struct whc_std { +	struct urb *urb; +	size_t len; +	int    ntds_remaining; +	struct whc_qtd *qtd; + +	struct list_head list_node; +	int num_pointers; +	dma_addr_t dma_addr; +	struct whc_page_list_entry *pl_virt; +}; + +/** + * struct whc_urb - per URB host controller structure. + * @urb: the URB this struct is for. + * @qset: the qset associated to the URB. + * @dequeue_work: the work to remove the URB when dequeued. + * @is_async: the URB belongs to async sheduler or not. + * @status: the status to be returned when calling wusbhc_giveback_urb. + */ +struct whc_urb { +	struct urb *urb; +	struct whc_qset *qset; +	struct work_struct dequeue_work; +	bool is_async; +	int status; +}; + +/** + * whc_std_last - is this sTD the URB's last? + * @std: the sTD to check. + */ +static inline bool whc_std_last(struct whc_std *std) +{ +	return std->ntds_remaining <= 1; +} + +enum whc_update { +	WHC_UPDATE_ADDED   = 0x01, +	WHC_UPDATE_REMOVED = 0x02, +	WHC_UPDATE_UPDATED = 0x04, +}; + +/* init.c */ +int whc_init(struct whc *whc); +void whc_clean_up(struct whc *whc); + +/* hw.c */ +void whc_write_wusbcmd(struct whc *whc, u32 mask, u32 val); +int whc_do_gencmd(struct whc *whc, u32 cmd, u32 params, void *addr, size_t len); + +/* wusb.c */ +int whc_wusbhc_start(struct wusbhc *wusbhc); +void whc_wusbhc_stop(struct wusbhc *wusbhc); +int whc_mmcie_add(struct wusbhc *wusbhc, u8 interval, u8 repeat_cnt, +		  u8 handle, struct wuie_hdr *wuie); +int whc_mmcie_rm(struct wusbhc *wusbhc, u8 handle); +int whc_bwa_set(struct wusbhc *wusbhc, s8 stream_index, const struct uwb_mas_bm *mas_bm); +int whc_dev_info_set(struct wusbhc *wusbhc, struct wusb_dev *wusb_dev); +int whc_set_num_dnts(struct wusbhc *wusbhc, u8 interval, u8 slots); +int whc_set_ptk(struct wusbhc *wusbhc, u8 port_idx, u32 tkid, +		const void *ptk, size_t key_size); +int whc_set_gtk(struct wusbhc *wusbhc, u32 tkid, +		const void *gtk, size_t key_size); +int whc_set_cluster_id(struct whc *whc, u8 bcid); + +/* int.c */ +irqreturn_t whc_int_handler(struct usb_hcd *hcd); +void whc_dn_work(struct work_struct *work); + +/* asl.c */ +void asl_start(struct whc *whc); +void asl_stop(struct whc *whc); +int  asl_init(struct whc *whc); +void asl_clean_up(struct whc *whc); +int  asl_urb_enqueue(struct whc *whc, struct urb *urb, gfp_t mem_flags); +int  asl_urb_dequeue(struct whc *whc, struct urb *urb, int status); +void asl_qset_delete(struct whc *whc, struct whc_qset *qset); +void scan_async_work(struct work_struct *work); + +/* pzl.c */ +int  pzl_init(struct whc *whc); +void pzl_clean_up(struct whc *whc); +void pzl_start(struct whc *whc); +void pzl_stop(struct whc *whc); +int  pzl_urb_enqueue(struct whc *whc, struct urb *urb, gfp_t mem_flags); +int  pzl_urb_dequeue(struct whc *whc, struct urb *urb, int status); +void pzl_qset_delete(struct whc *whc, struct whc_qset *qset); +void scan_periodic_work(struct work_struct *work); + +/* qset.c */ +struct whc_qset *qset_alloc(struct whc *whc, gfp_t mem_flags); +void qset_free(struct whc *whc, struct whc_qset *qset); +struct whc_qset *get_qset(struct whc *whc, struct urb *urb, gfp_t mem_flags); +void qset_delete(struct whc *whc, struct whc_qset *qset); +void qset_clear(struct whc *whc, struct whc_qset *qset); +int qset_add_urb(struct whc *whc, struct whc_qset *qset, struct urb *urb, +		 gfp_t mem_flags); +void qset_free_std(struct whc *whc, struct whc_std *std); +void qset_remove_urb(struct whc *whc, struct whc_qset *qset, +			    struct urb *urb, int status); +void process_halted_qtd(struct whc *whc, struct whc_qset *qset, +			       struct whc_qtd *qtd); +void process_inactive_qtd(struct whc *whc, struct whc_qset *qset, +				 struct whc_qtd *qtd); +enum whc_update qset_add_qtds(struct whc *whc, struct whc_qset *qset); +void qset_remove_complete(struct whc *whc, struct whc_qset *qset); +void dump_qset(struct whc_qset *qset, struct device *dev); +void pzl_update(struct whc *whc, uint32_t wusbcmd); +void asl_update(struct whc *whc, uint32_t wusbcmd); + +#endif /* #ifndef __WHCD_H */ diff --git a/drivers/usb/host/whci/whci-hc.h b/drivers/usb/host/whci/whci-hc.h new file mode 100644 index 00000000000..bff1eb7a35c --- /dev/null +++ b/drivers/usb/host/whci/whci-hc.h @@ -0,0 +1,416 @@ +/* + * Wireless Host Controller (WHC) data structures. + * + * Copyright (C) 2007 Cambridge Silicon Radio Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ +#ifndef _WHCI_WHCI_HC_H +#define _WHCI_WHCI_HC_H + +#include <linux/list.h> + +/** + * WHCI_PAGE_SIZE - page size use by WHCI + * + * WHCI assumes that host system uses pages of 4096 octets. + */ +#define WHCI_PAGE_SIZE 4096 + + +/** + * QTD_MAX_TXFER_SIZE - max number of bytes to transfer with a single + * qtd. + * + * This is 2^20 - 1. + */ +#define QTD_MAX_XFER_SIZE 1048575 + + +/** + * struct whc_qtd - Queue Element Transfer Descriptors (qTD) + * + * This describes the data for a bulk, control or interrupt transfer. + * + * [WHCI] section 3.2.4 + */ +struct whc_qtd { +	__le32 status; /*< remaining transfer len and transfer status */ +	__le32 options; +	__le64 page_list_ptr; /*< physical pointer to data buffer page list*/ +	__u8   setup[8];      /*< setup data for control transfers */ +} __attribute__((packed)); + +#define QTD_STS_ACTIVE     (1 << 31)  /* enable execution of transaction */ +#define QTD_STS_HALTED     (1 << 30)  /* transfer halted */ +#define QTD_STS_DBE        (1 << 29)  /* data buffer error */ +#define QTD_STS_BABBLE     (1 << 28)  /* babble detected */ +#define QTD_STS_RCE        (1 << 27)  /* retry count exceeded */ +#define QTD_STS_LAST_PKT   (1 << 26)  /* set Last Packet Flag in WUSB header */ +#define QTD_STS_INACTIVE   (1 << 25)  /* queue set is marked inactive */ +#define QTD_STS_IALT_VALID (1 << 23)                          /* iAlt field is valid */ +#define QTD_STS_IALT(i)    (QTD_STS_IALT_VALID | ((i) << 20)) /* iAlt field */ +#define QTD_STS_LEN(l)     ((l) << 0) /* transfer length */ +#define QTD_STS_TO_LEN(s)  ((s) & 0x000fffff) + +#define QTD_OPT_IOC      (1 << 1) /* page_list_ptr points to buffer directly */ +#define QTD_OPT_SMALL    (1 << 0) /* interrupt on complete */ + +/** + * struct whc_itd - Isochronous Queue Element Transfer Descriptors (iTD) + * + * This describes the data and other parameters for an isochronous + * transfer. + * + * [WHCI] section 3.2.5 + */ +struct whc_itd { +	__le16 presentation_time;    /*< presentation time for OUT transfers */ +	__u8   num_segments;         /*< number of data segments in segment list */ +	__u8   status;               /*< command execution status */ +	__le32 options;              /*< misc transfer options */ +	__le64 page_list_ptr;        /*< physical pointer to data buffer page list */ +	__le64 seg_list_ptr;         /*< physical pointer to segment list */ +} __attribute__((packed)); + +#define ITD_STS_ACTIVE   (1 << 7) /* enable execution of transaction */ +#define ITD_STS_DBE      (1 << 5) /* data buffer error */ +#define ITD_STS_BABBLE   (1 << 4) /* babble detected */ +#define ITD_STS_INACTIVE (1 << 1) /* queue set is marked inactive */ + +#define ITD_OPT_IOC      (1 << 1) /* interrupt on complete */ +#define ITD_OPT_SMALL    (1 << 0) /* page_list_ptr points to buffer directly */ + +/** + * Page list entry. + * + * A TD's page list must contain sufficient page list entries for the + * total data length in the TD. + * + * [WHCI] section 3.2.4.3 + */ +struct whc_page_list_entry { +	__le64 buf_ptr; /*< physical pointer to buffer */ +} __attribute__((packed)); + +/** + * struct whc_seg_list_entry - Segment list entry. + * + * Describes a portion of the data buffer described in the containing + * qTD's page list. + * + * seg_ptr = qtd->page_list_ptr[qtd->seg_list_ptr[seg].idx].buf_ptr + *           + qtd->seg_list_ptr[seg].offset; + * + * Segments can't cross page boundries. + * + * [WHCI] section 3.2.5.5 + */ +struct whc_seg_list_entry { +	__le16 len;    /*< segment length */ +	__u8   idx;    /*< index into page list */ +	__u8   status; /*< segment status */ +	__le16 offset; /*< 12 bit offset into page */ +} __attribute__((packed)); + +/** + * struct whc_qhead - endpoint and status information for a qset. + * + * [WHCI] section 3.2.6 + */ +struct whc_qhead { +	__le64 link; /*< next qset in list */ +	__le32 info1; +	__le32 info2; +	__le32 info3; +	__le16 status; +	__le16 err_count;  /*< transaction error count */ +	__le32 cur_window; +	__le32 scratch[3]; /*< h/w scratch area */ +	union { +		struct whc_qtd qtd; +		struct whc_itd itd; +	} overlay; +} __attribute__((packed)); + +#define QH_LINK_PTR_MASK (~0x03Full) +#define QH_LINK_PTR(ptr) ((ptr) & QH_LINK_PTR_MASK) +#define QH_LINK_IQS      (1 << 4) /* isochronous queue set */ +#define QH_LINK_NTDS(n)  (((n) - 1) << 1) /* number of TDs in queue set */ +#define QH_LINK_T        (1 << 0) /* last queue set in periodic schedule list */ + +#define QH_INFO1_EP(e)           ((e) << 0)  /* endpoint number */ +#define QH_INFO1_DIR_IN          (1 << 4)    /* IN transfer */ +#define QH_INFO1_DIR_OUT         (0 << 4)    /* OUT transfer */ +#define QH_INFO1_TR_TYPE_CTRL    (0x0 << 5)  /* control transfer */ +#define QH_INFO1_TR_TYPE_ISOC    (0x1 << 5)  /* isochronous transfer */ +#define QH_INFO1_TR_TYPE_BULK    (0x2 << 5)  /* bulk transfer */ +#define QH_INFO1_TR_TYPE_INT     (0x3 << 5)  /* interrupt */ +#define QH_INFO1_TR_TYPE_LP_INT  (0x7 << 5)  /* low power interrupt */ +#define QH_INFO1_DEV_INFO_IDX(i) ((i) << 8)  /* index into device info buffer */ +#define QH_INFO1_SET_INACTIVE    (1 << 15)   /* set inactive after transfer */ +#define QH_INFO1_MAX_PKT_LEN(l)  ((l) << 16) /* maximum packet length */ + +#define QH_INFO2_BURST(b)        ((b) << 0)  /* maximum burst length */ +#define QH_INFO2_DBP(p)          ((p) << 5)  /* data burst policy (see [WUSB] table 5-7) */ +#define QH_INFO2_MAX_COUNT(c)    ((c) << 8)  /* max isoc/int pkts per zone */ +#define QH_INFO2_RQS             (1 << 15)   /* reactivate queue set */ +#define QH_INFO2_MAX_RETRY(r)    ((r) << 16) /* maximum transaction retries */ +#define QH_INFO2_MAX_SEQ(s)      ((s) << 20) /* maximum sequence number */ +#define QH_INFO3_MAX_DELAY(d)    ((d) << 0)  /* maximum stream delay in 125 us units (isoc only) */ +#define QH_INFO3_INTERVAL(i)     ((i) << 16) /* segment interval in 125 us units (isoc only) */ + +#define QH_INFO3_TX_RATE_53_3    (0 << 24) +#define QH_INFO3_TX_RATE_80      (1 << 24) +#define QH_INFO3_TX_RATE_106_7   (2 << 24) +#define QH_INFO3_TX_RATE_160     (3 << 24) +#define QH_INFO3_TX_RATE_200     (4 << 24) +#define QH_INFO3_TX_RATE_320     (5 << 24) +#define QH_INFO3_TX_RATE_400     (6 << 24) +#define QH_INFO3_TX_RATE_480     (7 << 24) +#define QH_INFO3_TX_PWR(p)       ((p) << 29) /* transmit power (see [WUSB] section 5.2.1.2) */ + +#define QH_STATUS_FLOW_CTRL      (1 << 15) +#define QH_STATUS_ICUR(i)        ((i) << 5) +#define QH_STATUS_TO_ICUR(s)     (((s) >> 5) & 0x7) + +/** + * usb_pipe_to_qh_type - USB core pipe type to QH transfer type + * + * Returns the QH type field for a USB core pipe type. + */ +static inline unsigned usb_pipe_to_qh_type(unsigned pipe) +{ +	static const unsigned type[] = { +		[PIPE_ISOCHRONOUS] = QH_INFO1_TR_TYPE_ISOC, +		[PIPE_INTERRUPT]   = QH_INFO1_TR_TYPE_INT, +		[PIPE_CONTROL]     = QH_INFO1_TR_TYPE_CTRL, +		[PIPE_BULK]        = QH_INFO1_TR_TYPE_BULK, +	}; +	return type[usb_pipetype(pipe)]; +} + +/** + * Maxiumum number of TDs in a qset. + */ +#define WHCI_QSET_TD_MAX 8 + +/** + * struct whc_qset - WUSB data transfers to a specific endpoint + * @qh: the QHead of this qset + * @qtd: up to 8 qTDs (for qsets for control, bulk and interrupt + * transfers) + * @itd: up to 8 iTDs (for qsets for isochronous transfers) + * @qset_dma: DMA address for this qset + * @whc: WHCI HC this qset is for + * @ep: endpoint + * @stds: list of sTDs queued to this qset + * @ntds: number of qTDs queued (not necessarily the same as nTDs + * field in the QH) + * @td_start: index of the first qTD in the list + * @td_end: index of next free qTD in the list (provided + *          ntds < WHCI_QSET_TD_MAX) + * + * Queue Sets (qsets) are added to the asynchronous schedule list + * (ASL) or the periodic zone list (PZL). + * + * qsets may contain up to 8 TDs (either qTDs or iTDs as appropriate). + * Each TD may refer to at most 1 MiB of data. If a single transfer + * has > 8MiB of data, TDs can be reused as they are completed since + * the TD list is used as a circular buffer.  Similarly, several + * (smaller) transfers may be queued in a qset. + * + * WHCI controllers may cache portions of the qsets in the ASL and + * PZL, requiring the WHCD to inform the WHC that the lists have been + * updated (fields changed or qsets inserted or removed).  For safe + * insertion and removal of qsets from the lists the schedule must be + * stopped to avoid races in updating the QH link pointers. + * + * Since the HC is free to execute qsets in any order, all transfers + * to an endpoint should use the same qset to ensure transfers are + * executed in the order they're submitted. + * + * [WHCI] section 3.2.3 + */ +struct whc_qset { +	struct whc_qhead qh; +	union { +		struct whc_qtd qtd[WHCI_QSET_TD_MAX]; +		struct whc_itd itd[WHCI_QSET_TD_MAX]; +	}; + +	/* private data for WHCD */ +	dma_addr_t qset_dma; +	struct whc *whc; +	struct usb_host_endpoint *ep; +	struct list_head stds; +	int ntds; +	int td_start; +	int td_end; +	struct list_head list_node; +	unsigned in_sw_list:1; +	unsigned in_hw_list:1; +	unsigned remove:1; +	struct urb *pause_after_urb; +	struct completion remove_complete; +	int max_burst; +	int max_seq; +}; + +static inline void whc_qset_set_link_ptr(u64 *ptr, u64 target) +{ +	if (target) +		*ptr = (*ptr & ~(QH_LINK_PTR_MASK | QH_LINK_T)) | QH_LINK_PTR(target); +	else +		*ptr = QH_LINK_T; +} + +/** + * struct di_buf_entry - Device Information (DI) buffer entry. + * + * There's one of these per connected device. + */ +struct di_buf_entry { +	__le32 availability_info[8]; /*< MAS availability information, one MAS per bit */ +	__le32 addr_sec_info;        /*< addressing and security info */ +	__le32 reserved[7]; +} __attribute__((packed)); + +#define WHC_DI_SECURE           (1 << 31) +#define WHC_DI_DISABLE          (1 << 30) +#define WHC_DI_KEY_IDX(k)       ((k) << 8) +#define WHC_DI_KEY_IDX_MASK     0x0000ff00 +#define WHC_DI_DEV_ADDR(a)      ((a) << 0) +#define WHC_DI_DEV_ADDR_MASK    0x000000ff + +/** + * struct dn_buf_entry - Device Notification (DN) buffer entry. + * + * [WHCI] section 3.2.8 + */ +struct dn_buf_entry { +	__u8   msg_size;    /*< number of octets of valid DN data */ +	__u8   reserved1; +	__u8   src_addr;    /*< source address */ +	__u8   status;      /*< buffer entry status */ +	__le32 tkid;        /*< TKID for source device, valid if secure bit is set */ +	__u8   dn_data[56]; /*< up to 56 octets of DN data */ +} __attribute__((packed)); + +#define WHC_DN_STATUS_VALID  (1 << 7) /* buffer entry is valid */ +#define WHC_DN_STATUS_SECURE (1 << 6) /* notification received using secure frame */ + +#define WHC_N_DN_ENTRIES (4096 / sizeof(struct dn_buf_entry)) + +/* The Add MMC IE WUSB Generic Command may take up to 256 bytes of +   data. [WHCI] section 2.4.7. */ +#define WHC_GEN_CMD_DATA_LEN 256 + +/* + * HC registers. + * + * [WHCI] section 2.4 + */ + +#define WHCIVERSION          0x00 + +#define WHCSPARAMS           0x04 +#  define WHCSPARAMS_TO_N_MMC_IES(p) (((p) >> 16) & 0xff) +#  define WHCSPARAMS_TO_N_KEYS(p)    (((p) >> 8) & 0xff) +#  define WHCSPARAMS_TO_N_DEVICES(p) (((p) >> 0) & 0x7f) + +#define WUSBCMD              0x08 +#  define WUSBCMD_BCID(b)            ((b) << 16) +#  define WUSBCMD_BCID_MASK          (0xff << 16) +#  define WUSBCMD_ASYNC_QSET_RM      (1 << 12) +#  define WUSBCMD_PERIODIC_QSET_RM   (1 << 11) +#  define WUSBCMD_WUSBSI(s)          ((s) << 8) +#  define WUSBCMD_WUSBSI_MASK        (0x7 << 8) +#  define WUSBCMD_ASYNC_SYNCED_DB    (1 << 7) +#  define WUSBCMD_PERIODIC_SYNCED_DB (1 << 6) +#  define WUSBCMD_ASYNC_UPDATED      (1 << 5) +#  define WUSBCMD_PERIODIC_UPDATED   (1 << 4) +#  define WUSBCMD_ASYNC_EN           (1 << 3) +#  define WUSBCMD_PERIODIC_EN        (1 << 2) +#  define WUSBCMD_WHCRESET           (1 << 1) +#  define WUSBCMD_RUN                (1 << 0) + +#define WUSBSTS              0x0c +#  define WUSBSTS_ASYNC_SCHED             (1 << 15) +#  define WUSBSTS_PERIODIC_SCHED          (1 << 14) +#  define WUSBSTS_DNTS_SCHED              (1 << 13) +#  define WUSBSTS_HCHALTED                (1 << 12) +#  define WUSBSTS_GEN_CMD_DONE            (1 << 9) +#  define WUSBSTS_CHAN_TIME_ROLLOVER      (1 << 8) +#  define WUSBSTS_DNTS_OVERFLOW           (1 << 7) +#  define WUSBSTS_BPST_ADJUSTMENT_CHANGED (1 << 6) +#  define WUSBSTS_HOST_ERR                (1 << 5) +#  define WUSBSTS_ASYNC_SCHED_SYNCED      (1 << 4) +#  define WUSBSTS_PERIODIC_SCHED_SYNCED   (1 << 3) +#  define WUSBSTS_DNTS_INT                (1 << 2) +#  define WUSBSTS_ERR_INT                 (1 << 1) +#  define WUSBSTS_INT                     (1 << 0) +#  define WUSBSTS_INT_MASK                0x3ff + +#define WUSBINTR             0x10 +#  define WUSBINTR_GEN_CMD_DONE             (1 << 9) +#  define WUSBINTR_CHAN_TIME_ROLLOVER       (1 << 8) +#  define WUSBINTR_DNTS_OVERFLOW            (1 << 7) +#  define WUSBINTR_BPST_ADJUSTMENT_CHANGED  (1 << 6) +#  define WUSBINTR_HOST_ERR                 (1 << 5) +#  define WUSBINTR_ASYNC_SCHED_SYNCED       (1 << 4) +#  define WUSBINTR_PERIODIC_SCHED_SYNCED    (1 << 3) +#  define WUSBINTR_DNTS_INT                 (1 << 2) +#  define WUSBINTR_ERR_INT                  (1 << 1) +#  define WUSBINTR_INT                      (1 << 0) +#  define WUSBINTR_ALL 0x3ff + +#define WUSBGENCMDSTS        0x14 +#  define WUSBGENCMDSTS_ACTIVE (1 << 31) +#  define WUSBGENCMDSTS_ERROR  (1 << 24) +#  define WUSBGENCMDSTS_IOC    (1 << 23) +#  define WUSBGENCMDSTS_MMCIE_ADD 0x01 +#  define WUSBGENCMDSTS_MMCIE_RM  0x02 +#  define WUSBGENCMDSTS_SET_MAS   0x03 +#  define WUSBGENCMDSTS_CHAN_STOP 0x04 +#  define WUSBGENCMDSTS_RWP_EN    0x05 + +#define WUSBGENCMDPARAMS     0x18 +#define WUSBGENADDR          0x20 +#define WUSBASYNCLISTADDR    0x28 +#define WUSBDNTSBUFADDR      0x30 +#define WUSBDEVICEINFOADDR   0x38 + +#define WUSBSETSECKEYCMD     0x40 +#  define WUSBSETSECKEYCMD_SET    (1 << 31) +#  define WUSBSETSECKEYCMD_ERASE  (1 << 30) +#  define WUSBSETSECKEYCMD_GTK    (1 << 8) +#  define WUSBSETSECKEYCMD_IDX(i) ((i) << 0) + +#define WUSBTKID             0x44 +#define WUSBSECKEY           0x48 +#define WUSBPERIODICLISTBASE 0x58 +#define WUSBMASINDEX         0x60 + +#define WUSBDNTSCTRL         0x64 +#  define WUSBDNTSCTRL_ACTIVE      (1 << 31) +#  define WUSBDNTSCTRL_INTERVAL(i) ((i) << 8) +#  define WUSBDNTSCTRL_SLOTS(s)    ((s) << 0) + +#define WUSBTIME             0x68 +#define WUSBBPST             0x6c +#define WUSBDIBUPDATED       0x70 + +#endif /* #ifndef _WHCI_WHCI_HC_H */ diff --git a/drivers/usb/host/whci/wusb.c b/drivers/usb/host/whci/wusb.c new file mode 100644 index 00000000000..66e4ddcd961 --- /dev/null +++ b/drivers/usb/host/whci/wusb.c @@ -0,0 +1,241 @@ +/* + * Wireless Host Controller (WHC) WUSB operations. + * + * Copyright (C) 2007 Cambridge Silicon Radio Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ +#include <linux/version.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/uwb/umc.h> +#define D_LOCAL 1 +#include <linux/uwb/debug.h> + +#include "../../wusbcore/wusbhc.h" + +#include "whcd.h" + +#if D_LOCAL >= 1 +static void dump_di(struct whc *whc, int idx) +{ +	struct di_buf_entry *di = &whc->di_buf[idx]; +	struct device *dev = &whc->umc->dev; +	char buf[128]; + +	bitmap_scnprintf(buf, sizeof(buf), (unsigned long *)di->availability_info, UWB_NUM_MAS); + +	d_printf(1, dev, "DI[%d]\n", idx); +	d_printf(1, dev, "  availability: %s\n", buf); +	d_printf(1, dev, "  %c%c key idx: %d dev addr: %d\n", +		 (di->addr_sec_info & WHC_DI_SECURE) ? 'S' : ' ', +		 (di->addr_sec_info & WHC_DI_DISABLE) ? 'D' : ' ', +		 (di->addr_sec_info & WHC_DI_KEY_IDX_MASK) >> 8, +		 (di->addr_sec_info & WHC_DI_DEV_ADDR_MASK)); +} +#else +static inline void dump_di(struct whc *whc, int idx) +{ +} +#endif + +static int whc_update_di(struct whc *whc, int idx) +{ +	int offset = idx / 32; +	u32 bit = 1 << (idx % 32); + +	dump_di(whc, idx); + +	le_writel(bit, whc->base + WUSBDIBUPDATED + offset); + +	return whci_wait_for(&whc->umc->dev, +			     whc->base + WUSBDIBUPDATED + offset, bit, 0, +			     100, "DI update"); +} + +/* + * WHCI starts and stops MMCs based on there being a valid GTK so + * these need only start/stop the asynchronous and periodic schedules. + */ + +int whc_wusbhc_start(struct wusbhc *wusbhc) +{ +	struct whc *whc = wusbhc_to_whc(wusbhc); + +	asl_start(whc); +	pzl_start(whc); + +	return 0; +} + +void whc_wusbhc_stop(struct wusbhc *wusbhc) +{ +	struct whc *whc = wusbhc_to_whc(wusbhc); + +	pzl_stop(whc); +	asl_stop(whc); +} + +int whc_mmcie_add(struct wusbhc *wusbhc, u8 interval, u8 repeat_cnt, +		  u8 handle, struct wuie_hdr *wuie) +{ +	struct whc *whc = wusbhc_to_whc(wusbhc); +	u32 params; + +	params = (interval << 24) +		| (repeat_cnt << 16) +		| (wuie->bLength << 8) +		| handle; + +	return whc_do_gencmd(whc, WUSBGENCMDSTS_MMCIE_ADD, params, wuie, wuie->bLength); +} + +int whc_mmcie_rm(struct wusbhc *wusbhc, u8 handle) +{ +	struct whc *whc = wusbhc_to_whc(wusbhc); +	u32 params; + +	params = handle; + +	return whc_do_gencmd(whc, WUSBGENCMDSTS_MMCIE_RM, params, NULL, 0); +} + +int whc_bwa_set(struct wusbhc *wusbhc, s8 stream_index, const struct uwb_mas_bm *mas_bm) +{ +	struct whc *whc = wusbhc_to_whc(wusbhc); + +	if (stream_index >= 0) +		whc_write_wusbcmd(whc, WUSBCMD_WUSBSI_MASK, WUSBCMD_WUSBSI(stream_index)); + +	return whc_do_gencmd(whc, WUSBGENCMDSTS_SET_MAS, 0, (void *)mas_bm, sizeof(*mas_bm)); +} + +int whc_dev_info_set(struct wusbhc *wusbhc, struct wusb_dev *wusb_dev) +{ +	struct whc *whc = wusbhc_to_whc(wusbhc); +	int idx = wusb_dev->port_idx; +	struct di_buf_entry *di = &whc->di_buf[idx]; +	int ret; + +	mutex_lock(&whc->mutex); + +	uwb_mas_bm_copy_le(di->availability_info, &wusb_dev->availability); +	di->addr_sec_info &= ~(WHC_DI_DISABLE | WHC_DI_DEV_ADDR_MASK); +	di->addr_sec_info |= WHC_DI_DEV_ADDR(wusb_dev->addr); + +	ret = whc_update_di(whc, idx); + +	mutex_unlock(&whc->mutex); + +	return ret; +} + +/* + * Set the number of Device Notification Time Slots (DNTS) and enable + * device notifications. + */ +int whc_set_num_dnts(struct wusbhc *wusbhc, u8 interval, u8 slots) +{ +	struct whc *whc = wusbhc_to_whc(wusbhc); +	u32 dntsctrl; + +	dntsctrl = WUSBDNTSCTRL_ACTIVE +		| WUSBDNTSCTRL_INTERVAL(interval) +		| WUSBDNTSCTRL_SLOTS(slots); + +	le_writel(dntsctrl, whc->base + WUSBDNTSCTRL); + +	return 0; +} + +static int whc_set_key(struct whc *whc, u8 key_index, uint32_t tkid, +		       const void *key, size_t key_size, bool is_gtk) +{ +	uint32_t setkeycmd; +	uint32_t seckey[4]; +	int i; +	int ret; + +	memcpy(seckey, key, key_size); +	setkeycmd = WUSBSETSECKEYCMD_SET | WUSBSETSECKEYCMD_IDX(key_index); +	if (is_gtk) +		setkeycmd |= WUSBSETSECKEYCMD_GTK; + +	le_writel(tkid, whc->base + WUSBTKID); +	for (i = 0; i < 4; i++) +		le_writel(seckey[i], whc->base + WUSBSECKEY + 4*i); +	le_writel(setkeycmd, whc->base + WUSBSETSECKEYCMD); + +	ret = whci_wait_for(&whc->umc->dev, whc->base + WUSBSETSECKEYCMD, +			    WUSBSETSECKEYCMD_SET, 0, 100, "set key"); + +	return ret; +} + +/** + * whc_set_ptk - set the PTK to use for a device. + * + * The index into the key table for this PTK is the same as the + * device's port index. + */ +int whc_set_ptk(struct wusbhc *wusbhc, u8 port_idx, u32 tkid, +		const void *ptk, size_t key_size) +{ +	struct whc *whc = wusbhc_to_whc(wusbhc); +	struct di_buf_entry *di = &whc->di_buf[port_idx]; +	int ret; + +	mutex_lock(&whc->mutex); + +	if (ptk) { +		ret = whc_set_key(whc, port_idx, tkid, ptk, key_size, false); +		if (ret) +			goto out; + +		di->addr_sec_info &= ~WHC_DI_KEY_IDX_MASK; +		di->addr_sec_info |= WHC_DI_SECURE | WHC_DI_KEY_IDX(port_idx); +	} else +		di->addr_sec_info &= ~WHC_DI_SECURE; + +	ret = whc_update_di(whc, port_idx); +out: +	mutex_unlock(&whc->mutex); +	return ret; +} + +/** + * whc_set_gtk - set the GTK for subsequent broadcast packets + * + * The GTK is stored in the last entry in the key table (the previous + * N_DEVICES entries are for the per-device PTKs). + */ +int whc_set_gtk(struct wusbhc *wusbhc, u32 tkid, +		const void *gtk, size_t key_size) +{ +	struct whc *whc = wusbhc_to_whc(wusbhc); +	int ret; + +	mutex_lock(&whc->mutex); + +	ret = whc_set_key(whc, whc->n_devices, tkid, gtk, key_size, true); + +	mutex_unlock(&whc->mutex); + +	return ret; +} + +int whc_set_cluster_id(struct whc *whc, u8 bcid) +{ +	whc_write_wusbcmd(whc, WUSBCMD_BCID_MASK, WUSBCMD_BCID(bcid)); +	return 0; +} diff --git a/drivers/usb/image/mdc800.c b/drivers/usb/image/mdc800.c index 0fb114ca1eb..878c77ca086 100644 --- a/drivers/usb/image/mdc800.c +++ b/drivers/usb/image/mdc800.c @@ -355,13 +355,14 @@ static int mdc800_usb_waitForIRQ (int mode, int msec)  	if (mdc800->camera_request_ready>0)  	{  		mdc800->camera_request_ready=0; -		err ("timeout waiting for camera."); +		dev_err(&mdc800->dev->dev, "timeout waiting for camera.\n");  		return -1;  	}  	if (mdc800->state == NOT_CONNECTED)  	{ -		warn ("Camera gets disconnected during waiting for irq."); +		printk(KERN_WARNING "mdc800: Camera gets disconnected " +		       "during waiting for irq.\n");  		mdc800->camera_request_ready=0;  		return -2;  	} @@ -379,7 +380,8 @@ static void mdc800_usb_write_notify (struct urb *urb)  	int status = urb->status;  	if (status != 0) -		err ("writing command fails (status=%i)", status); +		dev_err(&mdc800->dev->dev, +			"writing command fails (status=%i)\n", status);  	else  		mdc800->state=READY;  	mdc800->written = 1; @@ -406,7 +408,8 @@ static void mdc800_usb_download_notify (struct urb *urb)  			mdc800->state=READY;  		}  	} else { -		err ("request bytes fails (status:%i)", status); +		dev_err(&mdc800->dev->dev, +			"request bytes fails (status:%i)\n", status);  	}  	mdc800->downloaded = 1;  	wake_up (&mdc800->download_wait); @@ -443,13 +446,14 @@ static int mdc800_usb_probe (struct usb_interface *intf,  	if (mdc800->dev != NULL)  	{ -		warn ("only one Mustek MDC800 is supported."); +		dev_warn(&intf->dev, "only one Mustek MDC800 is supported.\n");  		return -ENODEV;  	}  	if (dev->descriptor.bNumConfigurations != 1)  	{ -		err ("probe fails -> wrong Number of Configuration"); +		dev_err(&intf->dev, +			"probe fails -> wrong Number of Configuration\n");  		return -ENODEV;  	}  	intf_desc = intf->cur_altsetting; @@ -461,7 +465,7 @@ static int mdc800_usb_probe (struct usb_interface *intf,  		|| ( intf_desc->desc.bNumEndpoints != 4)  	)  	{ -		err ("probe fails -> wrong Interface"); +		dev_err(&intf->dev, "probe fails -> wrong Interface\n");  		return -ENODEV;  	} @@ -482,19 +486,19 @@ static int mdc800_usb_probe (struct usb_interface *intf,  		}  		if (mdc800->endpoint[i] == -1)  		{ -			err ("probe fails -> Wrong Endpoints."); +			dev_err(&intf->dev, "probe fails -> Wrong Endpoints.\n");  			return -ENODEV;  		}  	} -	info ("Found Mustek MDC800 on USB."); +	dev_info(&intf->dev, "Found Mustek MDC800 on USB.\n");  	mutex_lock(&mdc800->io_lock);  	retval = usb_register_dev(intf, &mdc800_class);  	if (retval) { -		err ("Not able to get a minor for this device."); +		dev_err(&intf->dev, "Not able to get a minor for this device.\n");  		return -ENODEV;  	} @@ -570,7 +574,7 @@ static void mdc800_usb_disconnect (struct usb_interface *intf)  		mdc800->dev = NULL;  		usb_set_intfdata(intf, NULL);  	} -	info ("Mustek MDC800 disconnected from USB."); +	dev_info(&intf->dev, "Mustek MDC800 disconnected from USB.\n");  } @@ -644,7 +648,8 @@ static int mdc800_device_open (struct inode* inode, struct file *file)  	mdc800->irq_urb->dev = mdc800->dev;  	retval = usb_submit_urb (mdc800->irq_urb, GFP_KERNEL);  	if (retval) { -		err ("request USB irq fails (submit_retval=%i).", retval); +		dev_err(&mdc800->dev->dev, +			"request USB irq fails (submit_retval=%i).\n", retval);  		errn = -EIO;  		goto error_out;  	} @@ -701,7 +706,8 @@ static ssize_t mdc800_device_read (struct file *file, char __user *buf, size_t l  	}  	if (mdc800->state == WORKING)  	{ -		warn ("Illegal State \"working\" reached during read ?!"); +		printk(KERN_WARNING "mdc800: Illegal State \"working\"" +		       "reached during read ?!\n");  		mutex_unlock(&mdc800->io_lock);  		return -EBUSY;  	} @@ -733,7 +739,9 @@ static ssize_t mdc800_device_read (struct file *file, char __user *buf, size_t l  				mdc800->download_urb->dev = mdc800->dev;  				retval = usb_submit_urb (mdc800->download_urb, GFP_KERNEL);  				if (retval) { -					err ("Can't submit download urb (retval=%i)",retval); +					dev_err(&mdc800->dev->dev, +						"Can't submit download urb " +						"(retval=%i)\n", retval);  					mutex_unlock(&mdc800->io_lock);  					return len-left;  				} @@ -742,7 +750,10 @@ static ssize_t mdc800_device_read (struct file *file, char __user *buf, size_t l  				mdc800->downloaded = 0;  				if (mdc800->download_urb->status != 0)  				{ -					err ("request download-bytes fails (status=%i)",mdc800->download_urb->status); +					dev_err(&mdc800->dev->dev, +						"request download-bytes fails " +						"(status=%i)\n", +						mdc800->download_urb->status);  					mutex_unlock(&mdc800->io_lock);  					return len-left;  				} @@ -839,7 +850,8 @@ static ssize_t mdc800_device_write (struct file *file, const char __user *buf, s  			if (mdc800_usb_waitForIRQ (0,TO_GET_READY))  			{ -				err ("Camera didn't get ready.\n"); +				dev_err(&mdc800->dev->dev, +					"Camera didn't get ready.\n");  				mutex_unlock(&mdc800->io_lock);  				return -EIO;  			} @@ -851,7 +863,9 @@ static ssize_t mdc800_device_write (struct file *file, const char __user *buf, s  			mdc800->write_urb->dev = mdc800->dev;  			retval = usb_submit_urb (mdc800->write_urb, GFP_KERNEL);  			if (retval) { -				err ("submitting write urb fails (retval=%i)", retval); +				dev_err(&mdc800->dev->dev, +					"submitting write urb fails " +					"(retval=%i)\n", retval);  				mutex_unlock(&mdc800->io_lock);  				return -EIO;  			} @@ -870,7 +884,9 @@ static ssize_t mdc800_device_write (struct file *file, const char __user *buf, s  				case 0x3e: /* Take shot in Fine Mode (WCam Mode) */  					if (mdc800->pic_len < 0)  					{ -						err ("call 0x07 before 0x05,0x3e"); +						dev_err(&mdc800->dev->dev, +							"call 0x07 before " +							"0x05,0x3e\n");  						mdc800->state=READY;  						mutex_unlock(&mdc800->io_lock);  						return -EIO; @@ -890,7 +906,7 @@ static ssize_t mdc800_device_write (struct file *file, const char __user *buf, s  						if (mdc800_usb_waitForIRQ (1,TO_READ_FROM_IRQ))  						{ -							err ("requesting answer from irq fails"); +							dev_err(&mdc800->dev->dev, "requesting answer from irq fails\n");  							mutex_unlock(&mdc800->io_lock);  							return -EIO;  						} @@ -918,7 +934,7 @@ static ssize_t mdc800_device_write (struct file *file, const char __user *buf, s  					{  						if (mdc800_usb_waitForIRQ (0,TO_DEFAULT_COMMAND))  						{ -							err ("Command Timeout."); +							dev_err(&mdc800->dev->dev, "Command Timeout.\n");  							mutex_unlock(&mdc800->io_lock);  							return -EIO;  						} @@ -1018,7 +1034,8 @@ static int __init usb_mdc800_init (void)  	if (retval)  		goto cleanup_on_fail; -	info (DRIVER_VERSION ":" DRIVER_DESC); +	printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":" +	       DRIVER_DESC "\n");  	return 0; @@ -1028,7 +1045,7 @@ cleanup_on_fail:  	if (mdc800 != NULL)  	{ -		err ("can't alloc memory!"); +		printk(KERN_ERR "mdc800: can't alloc memory!\n");  		kfree(mdc800->download_urb_buffer);  		kfree(mdc800->write_urb_buffer); diff --git a/drivers/usb/misc/Kconfig b/drivers/usb/misc/Kconfig index 4ea50e0abcb..e463db5d818 100644 --- a/drivers/usb/misc/Kconfig +++ b/drivers/usb/misc/Kconfig @@ -42,6 +42,15 @@ config USB_ADUTUX  	  To compile this driver as a module, choose M here.  The module  	  will be called adutux. +config USB_SEVSEG +	tristate "USB 7-Segment LED Display" +	depends on USB +	help +	  Say Y here if you have a USB 7-Segment Display by Delcom + +	  To compile this driver as a module, choose M here: the +	  module will be called usbsevseg. +  config USB_RIO500  	tristate "USB Diamond Rio500 support"  	depends on USB @@ -271,3 +280,18 @@ config USB_ISIGHTFW  	  The firmware for this driver must be extracted from the MacOS  	  driver beforehand. Tools for doing so are available at  	  http://bersace03.free.fr + +config USB_VST +	tristate "USB VST driver" +	depends on USB +	help +	  This driver is intended for Vernier Software Technologies +	  bulk usb devices such as their Ocean-Optics spectrometers or +	  Labquest. +	  It is a bulk channel driver with configurable read and write +	  timeouts. + +	  To compile this driver as a module, choose M here: the +	  module will be called vstusb. + + diff --git a/drivers/usb/misc/Makefile b/drivers/usb/misc/Makefile index 45b4e12afb0..1334f7bdd7b 100644 --- a/drivers/usb/misc/Makefile +++ b/drivers/usb/misc/Makefile @@ -26,6 +26,8 @@ obj-$(CONFIG_USB_RIO500)	+= rio500.o  obj-$(CONFIG_USB_TEST)		+= usbtest.o  obj-$(CONFIG_USB_TRANCEVIBRATOR)	+= trancevibrator.o  obj-$(CONFIG_USB_USS720)	+= uss720.o +obj-$(CONFIG_USB_SEVSEG)	+= usbsevseg.o +obj-$(CONFIG_USB_VST)		+= vstusb.o  obj-$(CONFIG_USB_SISUSBVGA)	+= sisusbvga/ diff --git a/drivers/usb/misc/adutux.c b/drivers/usb/misc/adutux.c index 965f6eaea6a..7b6922e08ed 100644 --- a/drivers/usb/misc/adutux.c +++ b/drivers/usb/misc/adutux.c @@ -283,8 +283,8 @@ static int adu_open(struct inode *inode, struct file *file)  	interface = usb_find_interface(&adu_driver, subminor);  	if (!interface) { -		err("%s - error, can't find device for minor %d", -		    __func__, subminor); +		printk(KERN_ERR "adutux: %s - error, can't find device for " +		       "minor %d\n", __func__, subminor);  		retval = -ENODEV;  		goto exit_no_device;  	} @@ -416,7 +416,8 @@ static ssize_t adu_read(struct file *file, __user char *buffer, size_t count,  	/* verify that the device wasn't unplugged */  	if (dev->udev == NULL) {  		retval = -ENODEV; -		err("No device or device unplugged %d", retval); +		printk(KERN_ERR "adutux: No device or device unplugged %d\n", +		       retval);  		goto exit;  	} @@ -576,7 +577,8 @@ static ssize_t adu_write(struct file *file, const __user char *buffer,  	/* verify that the device wasn't unplugged */  	if (dev->udev == NULL) {  		retval = -ENODEV; -		err("No device or device unplugged %d", retval); +		printk(KERN_ERR "adutux: No device or device unplugged %d\n", +		       retval);  		goto exit;  	} @@ -645,7 +647,8 @@ static ssize_t adu_write(struct file *file, const __user char *buffer,  			retval = usb_submit_urb(dev->interrupt_out_urb, GFP_KERNEL);  			if (retval < 0) {  				dev->out_urb_finished = 1; -				err("Couldn't submit interrupt_out_urb %d", retval); +				dev_err(&dev->udev->dev, "Couldn't submit " +					"interrupt_out_urb %d\n", retval);  				goto exit;  			} @@ -890,13 +893,14 @@ static int __init adu_init(void)  	/* register this driver with the USB subsystem */  	result = usb_register(&adu_driver);  	if (result < 0) { -		err("usb_register failed for the "__FILE__" driver. " -		    "Error number %d", result); +		printk(KERN_ERR "usb_register failed for the "__FILE__ +		       " driver. Error number %d\n", result);  		goto exit;  	} -	info("adutux " DRIVER_DESC " " DRIVER_VERSION); -	info("adutux is an experimental driver. Use at your own risk"); +	printk(KERN_INFO "adutux " DRIVER_DESC " " DRIVER_VERSION "\n"); +	printk(KERN_INFO "adutux is an experimental driver. " +	       "Use at your own risk\n");  exit:  	dbg(2," %s : leave, return value %d", __func__, result); diff --git a/drivers/usb/misc/appledisplay.c b/drivers/usb/misc/appledisplay.c index a076c24a312..1d8e39a557d 100644 --- a/drivers/usb/misc/appledisplay.c +++ b/drivers/usb/misc/appledisplay.c @@ -130,7 +130,8 @@ static void appledisplay_complete(struct urb *urb)  exit:  	retval = usb_submit_urb(pdata->urb, GFP_ATOMIC);  	if (retval) { -		err("%s - usb_submit_urb failed with result %d", +		dev_err(&pdata->udev->dev, +			"%s - usb_submit_urb failed with result %d\n",  			__func__, retval);  	}  } @@ -220,7 +221,7 @@ static int appledisplay_probe(struct usb_interface *iface,  		}  	}  	if (!int_in_endpointAddr) { -		err("Could not find int-in endpoint"); +		dev_err(&iface->dev, "Could not find int-in endpoint\n");  		return -EIO;  	} @@ -228,7 +229,7 @@ static int appledisplay_probe(struct usb_interface *iface,  	pdata = kzalloc(sizeof(struct appledisplay), GFP_KERNEL);  	if (!pdata) {  		retval = -ENOMEM; -		err("Out of memory"); +		dev_err(&iface->dev, "Out of memory\n");  		goto error;  	} @@ -241,8 +242,8 @@ static int appledisplay_probe(struct usb_interface *iface,  	pdata->msgdata = kmalloc(ACD_MSG_BUFFER_LEN, GFP_KERNEL);  	if (!pdata->msgdata) {  		retval = -ENOMEM; -		err("appledisplay: Allocating buffer for control messages " -			"failed"); +		dev_err(&iface->dev, +			"Allocating buffer for control messages failed\n");  		goto error;  	} @@ -250,7 +251,7 @@ static int appledisplay_probe(struct usb_interface *iface,  	pdata->urb = usb_alloc_urb(0, GFP_KERNEL);  	if (!pdata->urb) {  		retval = -ENOMEM; -		err("appledisplay: Allocating URB failed"); +		dev_err(&iface->dev, "Allocating URB failed\n");  		goto error;  	} @@ -259,7 +260,7 @@ static int appledisplay_probe(struct usb_interface *iface,  		GFP_KERNEL, &pdata->urb->transfer_dma);  	if (!pdata->urbdata) {  		retval = -ENOMEM; -		err("appledisplay: Allocating URB buffer failed"); +		dev_err(&iface->dev, "Allocating URB buffer failed\n");  		goto error;  	} @@ -270,7 +271,7 @@ static int appledisplay_probe(struct usb_interface *iface,  		pdata, 1);  	if (usb_submit_urb(pdata->urb, GFP_KERNEL)) {  		retval = -EIO; -		err("appledisplay: Submitting URB failed"); +		dev_err(&iface->dev, "Submitting URB failed\n");  		goto error;  	} @@ -280,7 +281,7 @@ static int appledisplay_probe(struct usb_interface *iface,  	pdata->bd = backlight_device_register(bl_name, NULL, pdata,  						&appledisplay_bl_data);  	if (IS_ERR(pdata->bd)) { -		err("appledisplay: Backlight registration failed"); +		dev_err(&iface->dev, "Backlight registration failed\n");  		goto error;  	} @@ -291,7 +292,8 @@ static int appledisplay_probe(struct usb_interface *iface,  	if (brightness < 0) {  		retval = brightness; -		err("appledisplay: Error while getting initial brightness: %d", retval); +		dev_err(&iface->dev, +			"Error while getting initial brightness: %d\n", retval);  		goto error;  	} @@ -314,7 +316,7 @@ error:  					pdata->urbdata, pdata->urb->transfer_dma);  			usb_free_urb(pdata->urb);  		} -		if (pdata->bd) +		if (pdata->bd && !IS_ERR(pdata->bd))  			backlight_device_unregister(pdata->bd);  		kfree(pdata->msgdata);  	} @@ -352,7 +354,7 @@ static int __init appledisplay_init(void)  {  	wq = create_singlethread_workqueue("appledisplay");  	if (!wq) { -		err("Could not create work queue\n"); +		printk(KERN_ERR "appledisplay: Could not create work queue\n");  		return -ENOMEM;  	} diff --git a/drivers/usb/misc/cypress_cy7c63.c b/drivers/usb/misc/cypress_cy7c63.c index 937940404b7..5720bfef6a3 100644 --- a/drivers/usb/misc/cypress_cy7c63.c +++ b/drivers/usb/misc/cypress_cy7c63.c @@ -278,9 +278,9 @@ static int __init cypress_init(void)  	/* register this driver with the USB subsystem */  	result = usb_register(&cypress_driver); -	if (result) { -		err("Function usb_register failed! Error number: %d\n", result); -	} +	if (result) +		printk(KERN_ERR KBUILD_MODNAME ": usb_register failed! " +		       "Error number: %d\n", result);  	return result;  } diff --git a/drivers/usb/misc/cytherm.c b/drivers/usb/misc/cytherm.c index 1cd9e7eba93..4fb3c38b924 100644 --- a/drivers/usb/misc/cytherm.c +++ b/drivers/usb/misc/cytherm.c @@ -422,13 +422,14 @@ static int __init usb_cytherm_init(void)  	int result;  	result = usb_register(&cytherm_driver); -	if (result)  -	{	 -		err("usb_register failed. Error number %d", result); +	if (result) { +		printk(KERN_ERR KBUILD_MODNAME ": usb_register failed! " +		       "Error number: %d\n", result);  		return result;  	} -	info(DRIVER_VERSION ":" DRIVER_DESC); +	printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":" +	       DRIVER_DESC "\n");  	return 0;  } diff --git a/drivers/usb/misc/emi26.c b/drivers/usb/misc/emi26.c index 4b994a0cd27..e762beb5f3c 100644 --- a/drivers/usb/misc/emi26.c +++ b/drivers/usb/misc/emi26.c @@ -50,7 +50,7 @@ static int emi26_writememory (struct usb_device *dev, int address,  	unsigned char *buffer =  kmemdup(data, length, GFP_KERNEL);  	if (!buffer) { -		err("emi26: kmalloc(%d) failed.", length); +		dev_err(&dev->dev, "kmalloc(%d) failed.\n", length);  		return -ENOMEM;  	}  	/* Note: usb_control_msg returns negative value on error or length of the @@ -64,11 +64,11 @@ static int emi26_writememory (struct usb_device *dev, int address,  static int emi26_set_reset (struct usb_device *dev, unsigned char reset_bit)  {  	int response; -	info("%s - %d", __func__, reset_bit); +	dev_info(&dev->dev, "%s - %d\n", __func__, reset_bit);  	/* printk(KERN_DEBUG "%s - %d", __func__, reset_bit); */  	response = emi26_writememory (dev, CPUCS_REG, &reset_bit, 1, 0xa0);  	if (response < 0) { -		err("emi26: set_reset (%d) failed", reset_bit); +		dev_err(&dev->dev, "set_reset (%d) failed\n", reset_bit);  	}  	return response;  } @@ -88,7 +88,8 @@ static int emi26_load_firmware (struct usb_device *dev)  	buf = kmalloc(FW_LOAD_SIZE, GFP_KERNEL);  	if (!buf) { -		err( "%s - error loading firmware: error = %d", __func__, -ENOMEM); +		dev_err(&dev->dev, "%s - error loading firmware: error = %d\n", +			__func__, -ENOMEM);  		err = -ENOMEM;  		goto wraperr;  	} @@ -106,14 +107,16 @@ static int emi26_load_firmware (struct usb_device *dev)  				    &dev->dev);  	if (err) {  	nofw: -		err( "%s - request_firmware() failed", __func__); +		dev_err(&dev->dev, "%s - request_firmware() failed\n", +			__func__);  		goto wraperr;  	}  	/* Assert reset (stop the CPU in the EMI) */  	err = emi26_set_reset(dev,1);  	if (err < 0) { -		err( "%s - error loading firmware: error = %d", __func__, err); +		dev_err(&dev->dev,"%s - error loading firmware: error = %d\n", +			__func__, err);  		goto wraperr;  	} @@ -254,7 +257,7 @@ static int emi26_probe(struct usb_interface *intf, const struct usb_device_id *i  {  	struct usb_device *dev = interface_to_usbdev(intf); -	info("%s start", __func__); +	dev_info(&intf->dev, "%s start\n", __func__);  	emi26_load_firmware(dev); diff --git a/drivers/usb/misc/emi62.c b/drivers/usb/misc/emi62.c index 5d859ded5bb..602ee05ba9f 100644 --- a/drivers/usb/misc/emi62.c +++ b/drivers/usb/misc/emi62.c @@ -73,7 +73,7 @@ static int emi62_writememory(struct usb_device *dev, int address,  static int emi62_set_reset (struct usb_device *dev, unsigned char reset_bit)  {  	int response; -	info("%s - %d", __func__, reset_bit); +	dev_info(&dev->dev, "%s - %d\n", __func__, reset_bit);  	response = emi62_writememory (dev, CPUCS_REG, &reset_bit, 1, 0xa0);  	if (response < 0) { @@ -271,7 +271,7 @@ static int emi62_probe(struct usb_interface *intf, const struct usb_device_id *i  	struct usb_device *dev = interface_to_usbdev(intf);  	dev_dbg(&intf->dev, "emi62_probe\n"); -	info("%s start", __func__); +	dev_info(&intf->dev, "%s start\n", __func__);  	emi62_load_firmware(dev); diff --git a/drivers/usb/misc/ftdi-elan.c b/drivers/usb/misc/ftdi-elan.c index 97c28097153..79a7668ef26 100644 --- a/drivers/usb/misc/ftdi-elan.c +++ b/drivers/usb/misc/ftdi-elan.c @@ -698,7 +698,7 @@ static ssize_t ftdi_elan_read(struct file *file, char __user *buffer,                  int retval = usb_bulk_msg(ftdi->udev,                          usb_rcvbulkpipe(ftdi->udev, ftdi->bulk_in_endpointAddr),                           ftdi->bulk_in_buffer, ftdi->bulk_in_size, -                        &packet_bytes, msecs_to_jiffies(50)); +                        &packet_bytes, 50);                  if (packet_bytes > 2) {                          ftdi->bulk_in_left = packet_bytes - 2;                          ftdi->bulk_in_last = 1; @@ -960,7 +960,7 @@ static int ftdi_elan_respond_engine(struct usb_ftdi *ftdi)                  int retval = usb_bulk_msg(ftdi->udev,                          usb_rcvbulkpipe(ftdi->udev, ftdi->bulk_in_endpointAddr),                           ftdi->bulk_in_buffer, ftdi->bulk_in_size, -                        &packet_bytes, msecs_to_jiffies(500)); +                        &packet_bytes, 500);                  char diag[30 *3 + 4];                  char *d = diag;                  int m = packet_bytes; @@ -1880,7 +1880,7 @@ static int ftdi_elan_flush_input_fifo(struct usb_ftdi *ftdi)                  int retval = usb_bulk_msg(ftdi->udev,                          usb_rcvbulkpipe(ftdi->udev, ftdi->bulk_in_endpointAddr),                           ftdi->bulk_in_buffer, ftdi->bulk_in_size, -                        &packet_bytes, msecs_to_jiffies(100)); +                        &packet_bytes, 100);                  if (packet_bytes > 2) {                          char diag[30 *3 + 4];                          char *d = diag; @@ -2067,7 +2067,7 @@ static int ftdi_elan_synchronize(struct usb_ftdi *ftdi)                                  usb_rcvbulkpipe(ftdi->udev,                                  ftdi->bulk_in_endpointAddr),                                  ftdi->bulk_in_buffer, ftdi->bulk_in_size, -                                &packet_bytes, msecs_to_jiffies(500)); +                                &packet_bytes, 500);                          if (packet_bytes > 2) {                                  char diag[30 *3 + 4];                                  char *d = diag; @@ -2176,7 +2176,7 @@ static int ftdi_elan_stuck_waiting(struct usb_ftdi *ftdi)                  int retval = usb_bulk_msg(ftdi->udev,                          usb_rcvbulkpipe(ftdi->udev, ftdi->bulk_in_endpointAddr),                           ftdi->bulk_in_buffer, ftdi->bulk_in_size, -                        &packet_bytes, msecs_to_jiffies(1000)); +                        &packet_bytes, 1000);                  if (packet_bytes > 2) {                          char diag[30 *3 + 4];                          char *d = diag; diff --git a/drivers/usb/misc/idmouse.c b/drivers/usb/misc/idmouse.c index 4bcf7fb4e5d..6da8887538c 100644 --- a/drivers/usb/misc/idmouse.c +++ b/drivers/usb/misc/idmouse.c @@ -403,14 +403,15 @@ static void idmouse_disconnect(struct usb_interface *interface)  		mutex_unlock(&dev->lock);  	} -	info("%s disconnected", DRIVER_DESC); +	dev_info(&interface->dev, "disconnected\n");  }  static int __init usb_idmouse_init(void)  {  	int result; -	info(DRIVER_DESC " " DRIVER_VERSION); +	printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":" +	       DRIVER_DESC "\n");  	/* register this driver with the USB subsystem */  	result = usb_register(&idmouse_driver); diff --git a/drivers/usb/misc/legousbtower.c b/drivers/usb/misc/legousbtower.c index 9370326a594..ab0f3226158 100644 --- a/drivers/usb/misc/legousbtower.c +++ b/drivers/usb/misc/legousbtower.c @@ -851,9 +851,8 @@ static int tower_probe (struct usb_interface *interface, const struct usb_device  	dbg(2, "%s: enter", __func__); -	if (udev == NULL) { -		info ("udev is NULL."); -	} +	if (udev == NULL) +		dev_info(&interface->dev, "udev is NULL.\n");  	/* allocate memory for our device state and initialize it */ @@ -954,7 +953,9 @@ static int tower_probe (struct usb_interface *interface, const struct usb_device  	dev->minor = interface->minor;  	/* 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); +	dev_info(&interface->dev, "LEGO USB Tower #%d now attached to major " +		 "%d minor %d\n", (dev->minor - LEGO_USB_TOWER_MINOR_BASE), +		 USB_MAJOR, dev->minor);  	/* get the firmware version and log it */  	result = usb_control_msg (udev, @@ -971,10 +972,10 @@ static int tower_probe (struct usb_interface *interface, const struct usb_device  		retval = result;  		goto error;  	} -	info("LEGO USB Tower firmware version is %d.%d build %d", -	     get_version_reply.major, -	     get_version_reply.minor, -	     le16_to_cpu(get_version_reply.build_no)); +	dev_info(&interface->dev, "LEGO USB Tower firmware version is %d.%d " +		 "build %d\n", get_version_reply.major, +		 get_version_reply.minor, +		 le16_to_cpu(get_version_reply.build_no));  exit: @@ -1021,7 +1022,8 @@ static void tower_disconnect (struct usb_interface *interface)  		mutex_unlock(&dev->lock);  	} -	info("LEGO USB Tower #%d now disconnected", (minor - LEGO_USB_TOWER_MINOR_BASE)); +	dev_info(&interface->dev, "LEGO USB Tower #%d now disconnected\n", +		 (minor - LEGO_USB_TOWER_MINOR_BASE));  	dbg(2, "%s: leave", __func__);  } @@ -1046,7 +1048,8 @@ static int __init lego_usb_tower_init(void)  		goto exit;  	} -	info(DRIVER_DESC " " DRIVER_VERSION); +	printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":" +	       DRIVER_DESC "\n");  exit:  	dbg(2, "%s: leave, return value %d", __func__, retval); diff --git a/drivers/usb/misc/phidgetkit.c b/drivers/usb/misc/phidgetkit.c index 4cfa25b0f44..cc8e0a926f9 100644 --- a/drivers/usb/misc/phidgetkit.c +++ b/drivers/usb/misc/phidgetkit.c @@ -595,9 +595,8 @@ static int interfacekit_probe(struct usb_interface *intf, const struct usb_devic          } while(value);          kit->dev_no = bit; -	kit->dev = device_create_drvdata(phidget_class, &kit->udev->dev, -					MKDEV(0, 0), kit, -					"interfacekit%d", kit->dev_no); +	kit->dev = device_create(phidget_class, &kit->udev->dev, MKDEV(0, 0), +				 kit, "interfacekit%d", kit->dev_no);          if (IS_ERR(kit->dev)) {                  rc = PTR_ERR(kit->dev);                  kit->dev = NULL; diff --git a/drivers/usb/misc/phidgetmotorcontrol.c b/drivers/usb/misc/phidgetmotorcontrol.c index 9b4696f21b2..38088b44349 100644 --- a/drivers/usb/misc/phidgetmotorcontrol.c +++ b/drivers/usb/misc/phidgetmotorcontrol.c @@ -365,9 +365,8 @@ static int motorcontrol_probe(struct usb_interface *intf, const struct usb_devic  	} while(value);  	mc->dev_no = bit; -	mc->dev = device_create_drvdata(phidget_class, &mc->udev->dev, -					MKDEV(0, 0), mc, -					"motorcontrol%d", mc->dev_no); +	mc->dev = device_create(phidget_class, &mc->udev->dev, MKDEV(0, 0), mc, +				"motorcontrol%d", mc->dev_no);  	if (IS_ERR(mc->dev)) {  		rc = PTR_ERR(mc->dev);  		mc->dev = NULL; diff --git a/drivers/usb/misc/phidgetservo.c b/drivers/usb/misc/phidgetservo.c index 1ca7ddb41d4..bef6fe16364 100644 --- a/drivers/usb/misc/phidgetservo.c +++ b/drivers/usb/misc/phidgetservo.c @@ -275,9 +275,8 @@ servo_probe(struct usb_interface *interface, const struct usb_device_id *id)          } while (value);  	dev->dev_no = bit; -	dev->dev = device_create_drvdata(phidget_class, &dev->udev->dev, -					 MKDEV(0, 0), dev, -					 "servo%d", dev->dev_no); +	dev->dev = device_create(phidget_class, &dev->udev->dev, MKDEV(0, 0), +				 dev, "servo%d", dev->dev_no);  	if (IS_ERR(dev->dev)) {  		rc = PTR_ERR(dev->dev);  		dev->dev = NULL; diff --git a/drivers/usb/misc/rio500.c b/drivers/usb/misc/rio500.c index 248a12aacef..deb95bb49fd 100644 --- a/drivers/usb/misc/rio500.c +++ b/drivers/usb/misc/rio500.c @@ -89,7 +89,7 @@ static int open_rio(struct inode *inode, struct file *file)  	mutex_unlock(&(rio->lock)); -	info("Rio opened."); +	dev_info(&rio->rio_dev->dev, "Rio opened.\n");  	return 0;  } @@ -100,7 +100,7 @@ static int close_rio(struct inode *inode, struct file *file)  	rio->isopen = 0; -	info("Rio closed."); +	dev_info(&rio->rio_dev->dev, "Rio closed.\n");  	return 0;  } @@ -451,7 +451,7 @@ static int probe_rio(struct usb_interface *intf,  	struct rio_usb_data *rio = &rio_instance;  	int retval; -	info("USB Rio found at address %d", dev->devnum); +	dev_info(&intf->dev, "USB Rio found at address %d\n", dev->devnum);  	retval = usb_register_dev(intf, &usb_rio_class);  	if (retval) { @@ -503,7 +503,7 @@ static void disconnect_rio(struct usb_interface *intf)  		kfree(rio->ibuf);  		kfree(rio->obuf); -		info("USB Rio disconnected."); +		dev_info(&intf->dev, "USB Rio disconnected.\n");  		rio->present = 0;  		mutex_unlock(&(rio->lock)); @@ -531,7 +531,8 @@ static int __init usb_rio_init(void)  	if (retval)  		goto out; -	info(DRIVER_VERSION ":" DRIVER_DESC); +	printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":" +	       DRIVER_DESC "\n");  out:  	return retval; diff --git a/drivers/usb/misc/sisusbvga/sisusb.c b/drivers/usb/misc/sisusbvga/sisusb.c index 69c34a58e20..b4ec716de7d 100644 --- a/drivers/usb/misc/sisusbvga/sisusb.c +++ b/drivers/usb/misc/sisusbvga/sisusb.c @@ -3270,6 +3270,7 @@ static struct usb_device_id sisusb_table [] = {  	{ USB_DEVICE(0x0711, 0x0900) },  	{ USB_DEVICE(0x0711, 0x0901) },  	{ USB_DEVICE(0x0711, 0x0902) }, +	{ USB_DEVICE(0x0711, 0x0903) },  	{ USB_DEVICE(0x0711, 0x0918) },  	{ USB_DEVICE(0x182d, 0x021c) },  	{ USB_DEVICE(0x182d, 0x0269) }, diff --git a/drivers/usb/misc/trancevibrator.c b/drivers/usb/misc/trancevibrator.c index 03368edf3f2..2e14102955c 100644 --- a/drivers/usb/misc/trancevibrator.c +++ b/drivers/usb/misc/trancevibrator.c @@ -144,7 +144,8 @@ static int __init tv_init(void)  		return retval;  	} -	info(DRIVER_VERSION ":" DRIVER_DESC); +	printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":" +	       DRIVER_DESC "\n");  	return 0;  } diff --git a/drivers/usb/misc/usblcd.c b/drivers/usb/misc/usblcd.c index 2db4228fbb0..e0ff9ccd866 100644 --- a/drivers/usb/misc/usblcd.c +++ b/drivers/usb/misc/usblcd.c @@ -311,7 +311,7 @@ static int lcd_probe(struct usb_interface *interface, const struct usb_device_id  	dev->interface = interface;  	if (le16_to_cpu(dev->udev->descriptor.idProduct) != 0x0001) { -		warn(KERN_INFO "USBLCD model not supported."); +		dev_warn(&interface->dev, "USBLCD model not supported.\n");  		return -ENODEV;  	} @@ -359,12 +359,13 @@ static int lcd_probe(struct usb_interface *interface, const struct usb_device_id  	i = le16_to_cpu(dev->udev->descriptor.bcdDevice); -	info("USBLCD Version %1d%1d.%1d%1d found at address %d", -		(i & 0xF000)>>12,(i & 0xF00)>>8,(i & 0xF0)>>4,(i & 0xF), -		dev->udev->devnum); +	dev_info(&interface->dev, "USBLCD Version %1d%1d.%1d%1d found " +		 "at address %d\n", (i & 0xF000)>>12, (i & 0xF00)>>8, +		 (i & 0xF0)>>4,(i & 0xF), dev->udev->devnum);  	/* let the user know what node this device is now attached to */ -	info("USB LCD device now attached to USBLCD-%d", interface->minor); +	dev_info(&interface->dev, "USB LCD device now attached to USBLCD-%d\n", +		 interface->minor);  	return 0;  error: @@ -413,7 +414,7 @@ static void lcd_disconnect(struct usb_interface *interface)  	/* decrement our usage count */  	kref_put(&dev->kref, lcd_delete); -	info("USB LCD #%d now disconnected", minor); +	dev_info(&interface->dev, "USB LCD #%d now disconnected\n", minor);  }  static struct usb_driver lcd_driver = { diff --git a/drivers/usb/misc/usbsevseg.c b/drivers/usb/misc/usbsevseg.c new file mode 100644 index 00000000000..28a6a3a0953 --- /dev/null +++ b/drivers/usb/misc/usbsevseg.c @@ -0,0 +1,394 @@ +/* + * USB 7 Segment Driver + * + * Copyright (C) 2008 Harrison Metzger <harrisonmetz@gmail.com> + * Based on usbled.c by Greg Kroah-Hartman (greg@kroah.com) + * + *	This program is free software; you can redistribute it and/or + *	modify it under the terms of the GNU General Public License as + *	published by the Free Software Foundation, version 2. + * + */ + +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/string.h> +#include <linux/usb.h> + + +#define DRIVER_AUTHOR "Harrison Metzger <harrisonmetz@gmail.com>" +#define DRIVER_DESC "USB 7 Segment Driver" + +#define VENDOR_ID	0x0fc5 +#define PRODUCT_ID	0x1227 +#define MAXLEN		6 + +/* table of devices that work with this driver */ +static struct usb_device_id id_table[] = { +	{ USB_DEVICE(VENDOR_ID, PRODUCT_ID) }, +	{ }, +}; +MODULE_DEVICE_TABLE(usb, id_table); + +/* the different text display modes the device is capable of */ +static char *display_textmodes[] = {"raw", "hex", "ascii", NULL}; + +struct usb_sevsegdev { +	struct usb_device *udev; + +	u8 powered; +	u8 mode_msb; +	u8 mode_lsb; +	u8 decimals[MAXLEN]; +	u8 textmode; +	u8 text[MAXLEN]; +	u16 textlength; +}; + +/* sysfs_streq can't replace this completely + * If the device was in hex mode, and the user wanted a 0, + * if str commands are used, we would assume the end of string + * so mem commands are used. + */ +inline size_t my_memlen(const char *buf, size_t count) +{ +	if (count > 0 && buf[count-1] == '\n') +		return count - 1; +	else +		return count; +} + +static void update_display_powered(struct usb_sevsegdev *mydev) +{ +	int rc; + +	rc = usb_control_msg(mydev->udev, +			usb_sndctrlpipe(mydev->udev, 0), +			0x12, +			0x48, +			(80 * 0x100) + 10, /*  (power mode) */ +			(0x00 * 0x100) + (mydev->powered ? 1 : 0), +			NULL, +			0, +			2000); +	if (rc < 0) +		dev_dbg(&mydev->udev->dev, "power retval = %d\n", rc); +} + +static void update_display_mode(struct usb_sevsegdev *mydev) +{ +	int rc; + +	rc = usb_control_msg(mydev->udev, +			usb_sndctrlpipe(mydev->udev, 0), +			0x12, +			0x48, +			(82 * 0x100) + 10, /* (set mode) */ +			(mydev->mode_msb * 0x100) + mydev->mode_lsb, +			NULL, +			0, +			2000); + +	if (rc < 0) +		dev_dbg(&mydev->udev->dev, "mode retval = %d\n", rc); +} + +static void update_display_visual(struct usb_sevsegdev *mydev) +{ +	int rc; +	int i; +	unsigned char *buffer; +	u8 decimals = 0; + +	buffer = kzalloc(MAXLEN, GFP_KERNEL); +	if (!buffer) { +		dev_err(&mydev->udev->dev, "out of memory\n"); +		return; +	} + +	/* The device is right to left, where as you write left to right */ +	for (i = 0; i < mydev->textlength; i++) +		buffer[i] = mydev->text[mydev->textlength-1-i]; + +	rc = usb_control_msg(mydev->udev, +			usb_sndctrlpipe(mydev->udev, 0), +			0x12, +			0x48, +			(85 * 0x100) + 10, /* (write text) */ +			(0 * 0x100) + mydev->textmode, /* mode  */ +			buffer, +			mydev->textlength, +			2000); + +	if (rc < 0) +		dev_dbg(&mydev->udev->dev, "write retval = %d\n", rc); + +	kfree(buffer); + +	/* The device is right to left, where as you write left to right */ +	for (i = 0; i < sizeof(mydev->decimals); i++) +		decimals |= mydev->decimals[i] << i; + +	rc = usb_control_msg(mydev->udev, +			usb_sndctrlpipe(mydev->udev, 0), +			0x12, +			0x48, +			(86 * 0x100) + 10, /* (set decimal) */ +			(0 * 0x100) + decimals, /* decimals */ +			NULL, +			0, +			2000); + +	if (rc < 0) +		dev_dbg(&mydev->udev->dev, "decimal retval = %d\n", rc); +} + +#define MYDEV_ATTR_SIMPLE_UNSIGNED(name, update_fcn)		\ +static ssize_t show_attr_##name(struct device *dev, 		\ +	struct device_attribute *attr, char *buf) 		\ +{								\ +	struct usb_interface *intf = to_usb_interface(dev);	\ +	struct usb_sevsegdev *mydev = usb_get_intfdata(intf);	\ +								\ +	return sprintf(buf, "%u\n", mydev->name);		\ +}								\ +								\ +static ssize_t set_attr_##name(struct device *dev, 		\ +	struct device_attribute *attr, const char *buf, size_t count) \ +{								\ +	struct usb_interface *intf = to_usb_interface(dev);	\ +	struct usb_sevsegdev *mydev = usb_get_intfdata(intf);	\ +								\ +	mydev->name = simple_strtoul(buf, NULL, 10);		\ +	update_fcn(mydev); \ +								\ +	return count;						\ +}								\ +static DEVICE_ATTR(name, S_IWUGO | S_IRUGO, show_attr_##name, set_attr_##name); + +static ssize_t show_attr_text(struct device *dev, +	struct device_attribute *attr, char *buf) +{ +	struct usb_interface *intf = to_usb_interface(dev); +	struct usb_sevsegdev *mydev = usb_get_intfdata(intf); + +	return snprintf(buf, mydev->textlength, "%s\n", mydev->text); +} + +static ssize_t set_attr_text(struct device *dev, +	struct device_attribute *attr, const char *buf, size_t count) +{ +	struct usb_interface *intf = to_usb_interface(dev); +	struct usb_sevsegdev *mydev = usb_get_intfdata(intf); +	size_t end = my_memlen(buf, count); + +	if (end > sizeof(mydev->text)) +		return -EINVAL; + +	memset(mydev->text, 0, sizeof(mydev->text)); +	mydev->textlength = end; + +	if (end > 0) +		memcpy(mydev->text, buf, end); + +	update_display_visual(mydev); +	return count; +} + +static DEVICE_ATTR(text, S_IWUGO | S_IRUGO, show_attr_text, set_attr_text); + +static ssize_t show_attr_decimals(struct device *dev, +	struct device_attribute *attr, char *buf) +{ +	struct usb_interface *intf = to_usb_interface(dev); +	struct usb_sevsegdev *mydev = usb_get_intfdata(intf); +	int i; +	int pos; + +	for (i = 0; i < sizeof(mydev->decimals); i++) { +		pos = sizeof(mydev->decimals) - 1 - i; +		if (mydev->decimals[i] == 0) +			buf[pos] = '0'; +		else if (mydev->decimals[i] == 1) +			buf[pos] = '1'; +		else +			buf[pos] = 'x'; +	} + +	buf[sizeof(mydev->decimals)] = '\n'; +	return sizeof(mydev->decimals) + 1; +} + +static ssize_t set_attr_decimals(struct device *dev, +	struct device_attribute *attr, const char *buf, size_t count) +{ +	struct usb_interface *intf = to_usb_interface(dev); +	struct usb_sevsegdev *mydev = usb_get_intfdata(intf); +	size_t end = my_memlen(buf, count); +	int i; + +	if (end > sizeof(mydev->decimals)) +		return -EINVAL; + +	for (i = 0; i < end; i++) +		if (buf[i] != '0' && buf[i] != '1') +			return -EINVAL; + +	memset(mydev->decimals, 0, sizeof(mydev->decimals)); +	for (i = 0; i < end; i++) +		if (buf[i] == '1') +			mydev->decimals[end-1-i] = 1; + +	update_display_visual(mydev); + +	return count; +} + +static DEVICE_ATTR(decimals, S_IWUGO | S_IRUGO, +	show_attr_decimals, set_attr_decimals); + +static ssize_t show_attr_textmode(struct device *dev, +	struct device_attribute *attr, char *buf) +{ +	struct usb_interface *intf = to_usb_interface(dev); +	struct usb_sevsegdev *mydev = usb_get_intfdata(intf); +	int i; + +	buf[0] = 0; + +	for (i = 0; display_textmodes[i]; i++) { +		if (mydev->textmode == i) { +			strcat(buf, " ["); +			strcat(buf, display_textmodes[i]); +			strcat(buf, "] "); +		} else { +			strcat(buf, " "); +			strcat(buf, display_textmodes[i]); +			strcat(buf, " "); +		} +	} +	strcat(buf, "\n"); + + +	return strlen(buf); +} + +static ssize_t set_attr_textmode(struct device *dev, +	struct device_attribute *attr, const char *buf, size_t count) +{ +	struct usb_interface *intf = to_usb_interface(dev); +	struct usb_sevsegdev *mydev = usb_get_intfdata(intf); +	int i; + +	for (i = 0; display_textmodes[i]; i++) { +		if (sysfs_streq(display_textmodes[i], buf)) { +			mydev->textmode = i; +			update_display_visual(mydev); +			return count; +		} +	} + +	return -EINVAL; +} + +static DEVICE_ATTR(textmode, S_IWUGO | S_IRUGO, +	show_attr_textmode, set_attr_textmode); + + +MYDEV_ATTR_SIMPLE_UNSIGNED(powered, update_display_powered); +MYDEV_ATTR_SIMPLE_UNSIGNED(mode_msb, update_display_mode); +MYDEV_ATTR_SIMPLE_UNSIGNED(mode_lsb, update_display_mode); + +static struct attribute *dev_attrs[] = { +	&dev_attr_powered.attr, +	&dev_attr_text.attr, +	&dev_attr_textmode.attr, +	&dev_attr_decimals.attr, +	&dev_attr_mode_msb.attr, +	&dev_attr_mode_lsb.attr, +	NULL +}; + +static struct attribute_group dev_attr_grp = { +	.attrs = dev_attrs, +}; + +static int sevseg_probe(struct usb_interface *interface, +	const struct usb_device_id *id) +{ +	struct usb_device *udev = interface_to_usbdev(interface); +	struct usb_sevsegdev *mydev = NULL; +	int rc = -ENOMEM; + +	mydev = kzalloc(sizeof(struct usb_sevsegdev), GFP_KERNEL); +	if (mydev == NULL) { +		dev_err(&interface->dev, "Out of memory\n"); +		goto error_mem; +	} + +	mydev->udev = usb_get_dev(udev); +	usb_set_intfdata(interface, mydev); + +	/*set defaults */ +	mydev->textmode = 0x02; /* ascii mode */ +	mydev->mode_msb = 0x06; /* 6 characters */ +	mydev->mode_lsb = 0x3f; /* scanmode for 6 chars */ + +	rc = sysfs_create_group(&interface->dev.kobj, &dev_attr_grp); +	if (rc) +		goto error; + +	dev_info(&interface->dev, "USB 7 Segment device now attached\n"); +	return 0; + +error: +	usb_set_intfdata(interface, NULL); +	usb_put_dev(mydev->udev); +	kfree(mydev); +error_mem: +	return rc; +} + +static void sevseg_disconnect(struct usb_interface *interface) +{ +	struct usb_sevsegdev *mydev; + +	mydev = usb_get_intfdata(interface); +	sysfs_remove_group(&interface->dev.kobj, &dev_attr_grp); +	usb_set_intfdata(interface, NULL); +	usb_put_dev(mydev->udev); +	kfree(mydev); +	dev_info(&interface->dev, "USB 7 Segment now disconnected\n"); +} + +static struct usb_driver sevseg_driver = { +	.name =		"usbsevseg", +	.probe =	sevseg_probe, +	.disconnect =	sevseg_disconnect, +	.id_table =	id_table, +}; + +static int __init usb_sevseg_init(void) +{ +	int rc = 0; + +	rc = usb_register(&sevseg_driver); +	if (rc) +		err("usb_register failed. Error number %d", rc); +	return rc; +} + +static void __exit usb_sevseg_exit(void) +{ +	usb_deregister(&sevseg_driver); +} + +module_init(usb_sevseg_init); +module_exit(usb_sevseg_exit); + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/misc/usbtest.c b/drivers/usb/misc/usbtest.c index b358c4e1cf2..444c69c447b 100644 --- a/drivers/usb/misc/usbtest.c +++ b/drivers/usb/misc/usbtest.c @@ -1561,8 +1561,7 @@ usbtest_ioctl (struct usb_interface *intf, unsigned int code, void *buf)  	if (code != USBTEST_REQUEST)  		return -EOPNOTSUPP; -	if (param->iterations <= 0 || param->length < 0 -			|| param->sglen < 0 || param->vary < 0) +	if (param->iterations <= 0)  		return -EINVAL;  	if (mutex_lock_interruptible(&dev->lock)) diff --git a/drivers/usb/misc/uss720.c b/drivers/usb/misc/uss720.c index f1255b0a182..9a6c27a0179 100644 --- a/drivers/usb/misc/uss720.c +++ b/drivers/usb/misc/uss720.c @@ -228,11 +228,12 @@ static int get_1284_register(struct parport *pp, unsigned char reg, unsigned cha  		ret = rq->urb->status;  		*val = priv->reg[(reg >= 9) ? 0 : regindex[reg]];  		if (ret) -			warn("get_1284_register: usb error %d", ret); +			printk(KERN_WARNING "get_1284_register: " +			       "usb error %d\n", ret);  		kref_put(&rq->ref_count, destroy_async);  		return ret;  	} -	warn("get_1284_register timeout"); +	printk(KERN_WARNING "get_1284_register timeout\n");  	kill_all_async_requests_priv(priv);  	return -EIO;  } @@ -716,7 +717,7 @@ static int uss720_probe(struct usb_interface *intf,  	spin_lock_init(&priv->asynclock);  	INIT_LIST_HEAD(&priv->asynclist);  	if (!(pp = parport_register_port(0, PARPORT_IRQ_NONE, PARPORT_DMA_NONE, &parport_uss720_ops))) { -		warn("could not register parport"); +		printk(KERN_WARNING "uss720: could not register parport\n");  		goto probe_abort;  	} @@ -800,10 +801,14 @@ static int __init uss720_init(void)  	if (retval)  		goto out; -	info(DRIVER_VERSION ":" DRIVER_DESC); -	info("NOTE: this is a special purpose driver to allow nonstandard"); -	info("protocols (eg. bitbang) over USS720 usb to parallel cables"); -	info("If you just want to connect to a printer, use usblp instead"); +	printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":" +	       DRIVER_DESC "\n"); +	printk(KERN_INFO KBUILD_MODNAME ": NOTE: this is a special purpose " +	       "driver to allow nonstandard\n"); +	printk(KERN_INFO KBUILD_MODNAME ": protocols (eg. bitbang) over " +	       "USS720 usb to parallel cables\n"); +	printk(KERN_INFO KBUILD_MODNAME ": If you just want to connect to a " +	       "printer, use usblp instead\n");  out:  	return retval;  } diff --git a/drivers/usb/misc/vstusb.c b/drivers/usb/misc/vstusb.c new file mode 100644 index 00000000000..63dff9ba73c --- /dev/null +++ b/drivers/usb/misc/vstusb.c @@ -0,0 +1,782 @@ +/***************************************************************************** + *  File: drivers/usb/misc/vstusb.c + * + *  Purpose: Support for the bulk USB Vernier Spectrophotometers + * + *  Author:     Johnnie Peters + *              Axian Consulting + *              Beaverton, OR, USA 97005 + * + *  Modified by:     EQware Engineering, Inc. + *                   Oregon City, OR, USA 97045 + * + *  Copyright:  2007, 2008 + *              Vernier Software & Technology + *              Beaverton, OR, USA 97005 + * + *  Web:        www.vernier.com + * + *  This program is free software; you can redistribute it and/or modify + *  it under the terms of the GNU General Public License version 2 as + *  published by the Free Software Foundation. + * + *****************************************************************************/ +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/uaccess.h> +#include <linux/usb.h> + +#include <linux/usb/vstusb.h> + +#define DRIVER_VERSION "VST USB Driver Version 1.5" +#define DRIVER_DESC "Vernier Software Technology Bulk USB Driver" + +#ifdef CONFIG_USB_DYNAMIC_MINORS +	#define VSTUSB_MINOR_BASE	0 +#else +	#define VSTUSB_MINOR_BASE	199 +#endif + +#define USB_VENDOR_OCEANOPTICS	0x2457 +#define USB_VENDOR_VERNIER	0x08F7	/* Vernier Software & Technology */ + +#define USB_PRODUCT_USB2000	0x1002 +#define USB_PRODUCT_ADC1000_FW	0x1003	/* firmware download (renumerates) */ +#define USB_PRODUCT_ADC1000	0x1004 +#define USB_PRODUCT_HR2000_FW	0x1009	/* firmware download (renumerates) */ +#define USB_PRODUCT_HR2000	0x100A +#define USB_PRODUCT_HR4000_FW	0x1011	/* firmware download (renumerates) */ +#define USB_PRODUCT_HR4000	0x1012 +#define USB_PRODUCT_USB650	0x1014	/* "Red Tide" */ +#define USB_PRODUCT_QE65000	0x1018 +#define USB_PRODUCT_USB4000	0x1022 +#define USB_PRODUCT_USB325	0x1024	/* "Vernier Spectrometer" */ + +#define USB_PRODUCT_LABPRO	0x0001 +#define USB_PRODUCT_LABQUEST	0x0005 + +#define VST_MAXBUFFER		(64*1024) + +static struct usb_device_id id_table[] = { +	{ USB_DEVICE(USB_VENDOR_OCEANOPTICS, USB_PRODUCT_USB2000)}, +	{ USB_DEVICE(USB_VENDOR_OCEANOPTICS, USB_PRODUCT_HR4000)}, +	{ USB_DEVICE(USB_VENDOR_OCEANOPTICS, USB_PRODUCT_USB650)}, +	{ USB_DEVICE(USB_VENDOR_OCEANOPTICS, USB_PRODUCT_USB4000)}, +	{ USB_DEVICE(USB_VENDOR_OCEANOPTICS, USB_PRODUCT_USB325)}, +	{ USB_DEVICE(USB_VENDOR_VERNIER, USB_PRODUCT_LABQUEST)}, +	{ USB_DEVICE(USB_VENDOR_VERNIER, USB_PRODUCT_LABPRO)}, +	{}, +}; + +MODULE_DEVICE_TABLE(usb, id_table); + +struct vstusb_device { +	struct kref				kref; +	struct mutex            lock; +	struct usb_device       *usb_dev; +	char                    present; +	char                    isopen; +	struct usb_anchor       submitted; +	int                     rd_pipe; +	int                     rd_timeout_ms; +	int                     wr_pipe; +	int                     wr_timeout_ms; +}; +#define to_vst_dev(d) container_of(d, struct vstusb_device, kref) + +static struct usb_driver vstusb_driver; + +static void vstusb_delete(struct kref *kref) +{ +	struct vstusb_device *vstdev = to_vst_dev(kref); + +	usb_put_dev(vstdev->usb_dev); +	kfree(vstdev); +} + +static int vstusb_open(struct inode *inode, struct file *file) +{ +	struct vstusb_device *vstdev; +	struct usb_interface *interface; + +	interface = usb_find_interface(&vstusb_driver, iminor(inode)); + +	if (!interface) { +		printk(KERN_ERR KBUILD_MODNAME +		       ": %s - error, can't find device for minor %d\n", +		       __func__, iminor(inode)); +		return -ENODEV; +	} + +	vstdev = usb_get_intfdata(interface); + +	if (!vstdev) +		return -ENODEV; + +	/* lock this device */ +	mutex_lock(&vstdev->lock); + +	/* can only open one time */ +	if ((!vstdev->present) || (vstdev->isopen)) { +		mutex_unlock(&vstdev->lock); +		return -EBUSY; +	} + +	/* increment our usage count */ +	kref_get(&vstdev->kref); + +	vstdev->isopen = 1; + +	/* save device in the file's private structure */ +	file->private_data = vstdev; + +	dev_dbg(&vstdev->usb_dev->dev, "%s: opened\n", __func__); + +	mutex_unlock(&vstdev->lock); + +	return 0; +} + +static int vstusb_release(struct inode *inode, struct file *file) +{ +	struct vstusb_device *vstdev; + +	vstdev = file->private_data; + +	if (vstdev == NULL) +		return -ENODEV; + +	mutex_lock(&vstdev->lock); + +	vstdev->isopen = 0; + +	dev_dbg(&vstdev->usb_dev->dev, "%s: released\n", __func__); + +	mutex_unlock(&vstdev->lock); + +	kref_put(&vstdev->kref, vstusb_delete); + +	return 0; +} + +static void usb_api_blocking_completion(struct urb *urb) +{ +	struct completion *completeit = urb->context; + +	complete(completeit); +} + +static int vstusb_fill_and_send_urb(struct urb *urb, +				    struct usb_device *usb_dev, +				    unsigned int pipe, void *data, +				    unsigned int len, struct completion *done) +{ +	struct usb_host_endpoint *ep; +	struct usb_host_endpoint **hostep; +	unsigned int pipend; + +	int status; + +	hostep = usb_pipein(pipe) ? usb_dev->ep_in : usb_dev->ep_out; +	pipend = usb_pipeendpoint(pipe); +	ep = hostep[pipend]; + +	if (!ep || (len == 0)) +		return -EINVAL; + +	if ((ep->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) +	    == USB_ENDPOINT_XFER_INT) { +		pipe = (pipe & ~(3 << 30)) | (PIPE_INTERRUPT << 30); +		usb_fill_int_urb(urb, usb_dev, pipe, data, len, +				 (usb_complete_t)usb_api_blocking_completion, +				 NULL, ep->desc.bInterval); +	} else +		usb_fill_bulk_urb(urb, usb_dev, pipe, data, len, +				  (usb_complete_t)usb_api_blocking_completion, +				  NULL); + +	init_completion(done); +	urb->context = done; +	urb->actual_length = 0; +	status = usb_submit_urb(urb, GFP_KERNEL); + +	return status; +} + +static int vstusb_complete_urb(struct urb *urb, struct completion *done, +			       int timeout, int *actual_length) +{ +	unsigned long expire; +	int status; + +	expire = timeout ? msecs_to_jiffies(timeout) : MAX_SCHEDULE_TIMEOUT; +	if (!wait_for_completion_interruptible_timeout(done, expire)) { +		usb_kill_urb(urb); +		status = urb->status == -ENOENT ? -ETIMEDOUT : urb->status; + +		dev_dbg(&urb->dev->dev, +			"%s timed out on ep%d%s len=%d/%d, urb status = %d\n", +			current->comm, +			usb_pipeendpoint(urb->pipe), +			usb_pipein(urb->pipe) ? "in" : "out", +			urb->actual_length, +			urb->transfer_buffer_length, +			urb->status); + +	} else { +		if (signal_pending(current)) { +			/* if really an error */ +			if (urb->status && !((urb->status == -ENOENT)     || +					     (urb->status == -ECONNRESET) || +					     (urb->status == -ESHUTDOWN))) { +				status = -EINTR; +				usb_kill_urb(urb); +			} else { +				status = 0; +			} + +			dev_dbg(&urb->dev->dev, +				"%s: signal pending on ep%d%s len=%d/%d," +				"urb status = %d\n", +				current->comm, +				usb_pipeendpoint(urb->pipe), +				usb_pipein(urb->pipe) ? "in" : "out", +				urb->actual_length, +				urb->transfer_buffer_length, +				urb->status); + +		} else { +			status = urb->status; +		} +	} + +	if (actual_length) +		*actual_length = urb->actual_length; + +	return status; +} + +static ssize_t vstusb_read(struct file *file, char __user *buffer, +			   size_t count, loff_t *ppos) +{ +	struct vstusb_device *vstdev; +	int cnt = -1; +	void *buf; +	int retval = 0; + +	struct urb              *urb; +	struct usb_device       *dev; +	unsigned int            pipe; +	int                     timeout; + +	DECLARE_COMPLETION_ONSTACK(done); + +	vstdev = file->private_data; + +	if (vstdev == NULL) +		return -ENODEV; + +	/* verify that we actually want to read some data */ +	if ((count == 0) || (count > VST_MAXBUFFER)) +		return -EINVAL; + +	/* lock this object */ +	if (mutex_lock_interruptible(&vstdev->lock)) +		return -ERESTARTSYS; + +	/* anyone home */ +	if (!vstdev->present) { +		mutex_unlock(&vstdev->lock); +		printk(KERN_ERR KBUILD_MODNAME +		       ": %s: device not present\n", __func__); +		return -ENODEV; +	} + +	/* pull out the necessary data */ +	dev =     vstdev->usb_dev; +	pipe =    usb_rcvbulkpipe(dev, vstdev->rd_pipe); +	timeout = vstdev->rd_timeout_ms; + +	buf = kmalloc(count, GFP_KERNEL); +	if (buf == NULL) { +		mutex_unlock(&vstdev->lock); +		return -ENOMEM; +	} + +	urb = usb_alloc_urb(0, GFP_KERNEL); +	if (!urb) { +		kfree(buf); +		mutex_unlock(&vstdev->lock); +		return -ENOMEM; +	} + +	usb_anchor_urb(urb, &vstdev->submitted); +	retval = vstusb_fill_and_send_urb(urb, dev, pipe, buf, count, &done); +	mutex_unlock(&vstdev->lock); +	if (retval) { +		usb_unanchor_urb(urb); +		dev_err(&dev->dev, "%s: error %d filling and sending urb %d\n", +			__func__, retval, pipe); +		goto exit; +	} + +	retval = vstusb_complete_urb(urb, &done, timeout, &cnt); +	if (retval) { +		dev_err(&dev->dev, "%s: error %d completing urb %d\n", +			__func__, retval, pipe); +		goto exit; +	} + +	if (copy_to_user(buffer, buf, cnt)) { +		dev_err(&dev->dev, "%s: can't copy_to_user\n", __func__); +		retval = -EFAULT; +	} else { +		retval = cnt; +		dev_dbg(&dev->dev, "%s: read %d bytes from pipe %d\n", +			__func__, cnt, pipe); +	} + +exit: +	usb_free_urb(urb); +	kfree(buf); +	return retval; +} + +static ssize_t vstusb_write(struct file *file, const char __user *buffer, +			    size_t count, loff_t *ppos) +{ +	struct vstusb_device *vstdev; +	int cnt = -1; +	void *buf; +	int retval = 0; + +	struct urb              *urb; +	struct usb_device       *dev; +	unsigned int            pipe; +	int                     timeout; + +	DECLARE_COMPLETION_ONSTACK(done); + +	vstdev = file->private_data; + +	if (vstdev == NULL) +		return -ENODEV; + +	/* verify that we actually have some data to write */ +	if ((count == 0) || (count > VST_MAXBUFFER)) +		return retval; + +	/* lock this object */ +	if (mutex_lock_interruptible(&vstdev->lock)) +		return -ERESTARTSYS; + +	/* anyone home */ +	if (!vstdev->present) { +		mutex_unlock(&vstdev->lock); +		printk(KERN_ERR KBUILD_MODNAME +		       ": %s: device not present\n", __func__); +		return -ENODEV; +	} + +	/* pull out the necessary data */ +	dev =     vstdev->usb_dev; +	pipe =    usb_sndbulkpipe(dev, vstdev->wr_pipe); +	timeout = vstdev->wr_timeout_ms; + +	buf = kmalloc(count, GFP_KERNEL); +	if (buf == NULL) { +		mutex_unlock(&vstdev->lock); +		return -ENOMEM; +	} + +	urb = usb_alloc_urb(0, GFP_KERNEL); +	if (!urb) { +		kfree(buf); +		mutex_unlock(&vstdev->lock); +		return -ENOMEM; +	} + +	if (copy_from_user(buf, buffer, count)) { +		dev_err(&dev->dev, "%s: can't copy_from_user\n", __func__); +		retval = -EFAULT; +		goto exit; +	} + +	usb_anchor_urb(urb, &vstdev->submitted); +	retval = vstusb_fill_and_send_urb(urb, dev, pipe, buf, count, &done); +	mutex_unlock(&vstdev->lock); +	if (retval) { +		usb_unanchor_urb(urb); +		dev_err(&dev->dev, "%s: error %d filling and sending urb %d\n", +			__func__, retval, pipe); +		goto exit; +	} + +	retval = vstusb_complete_urb(urb, &done, timeout, &cnt); +	if (retval) { +		dev_err(&dev->dev, "%s: error %d completing urb %d\n", +			__func__, retval, pipe); +		goto exit; +	} else { +		retval = cnt; +		dev_dbg(&dev->dev, "%s: sent %d bytes to pipe %d\n", +			__func__, cnt, pipe); +	} + +exit: +	usb_free_urb(urb); +	kfree(buf); +	return retval; +} + +static long vstusb_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ +	int retval = 0; +	int cnt = -1; +	void __user *data = (void __user *)arg; +	struct vstusb_args usb_data; + +	struct vstusb_device *vstdev; +	void *buffer = NULL; /* must be initialized. buffer is +			      *	referenced on exit but not all +			      * ioctls allocate it */ + +	struct urb              *urb = NULL; /* must be initialized. urb is +					      *	referenced on exit but not all +					      * ioctls allocate it */ +	struct usb_device       *dev; +	unsigned int            pipe; +	int                     timeout; + +	DECLARE_COMPLETION_ONSTACK(done); + +	vstdev = file->private_data; + +	if (_IOC_TYPE(cmd) != VST_IOC_MAGIC) { +		dev_warn(&vstdev->usb_dev->dev, +			 "%s: ioctl command %x, bad ioctl magic %x, " +			 "expected %x\n", __func__, cmd, +			 _IOC_TYPE(cmd), VST_IOC_MAGIC); +		return -EINVAL; +	} + +	if (vstdev == NULL) +		return -ENODEV; + +	if (copy_from_user(&usb_data, data, sizeof(struct vstusb_args))) { +		dev_err(&vstdev->usb_dev->dev, "%s: can't copy_from_user\n", +			__func__); +		return -EFAULT; +	} + +	/* lock this object */ +	if (mutex_lock_interruptible(&vstdev->lock)) { +		retval = -ERESTARTSYS; +		goto exit; +	} + +	/* anyone home */ +	if (!vstdev->present) { +		mutex_unlock(&vstdev->lock); +		dev_err(&vstdev->usb_dev->dev, "%s: device not present\n", +			__func__); +		retval = -ENODEV; +		goto exit; +	} + +	/* pull out the necessary data */ +	dev = vstdev->usb_dev; + +	switch (cmd) { + +	case IOCTL_VSTUSB_CONFIG_RW: + +		vstdev->rd_pipe = usb_data.rd_pipe; +		vstdev->rd_timeout_ms = usb_data.rd_timeout_ms; +		vstdev->wr_pipe = usb_data.wr_pipe; +		vstdev->wr_timeout_ms = usb_data.wr_timeout_ms; + +		mutex_unlock(&vstdev->lock); + +		dev_dbg(&dev->dev, "%s: setting pipes/timeouts, " +			"rdpipe = %d, rdtimeout = %d, " +			"wrpipe = %d, wrtimeout = %d\n", __func__, +			vstdev->rd_pipe, vstdev->rd_timeout_ms, +			vstdev->wr_pipe, vstdev->wr_timeout_ms); +		break; + +	case IOCTL_VSTUSB_SEND_PIPE: + +		if ((usb_data.count == 0) || (usb_data.count > VST_MAXBUFFER)) { +			mutex_unlock(&vstdev->lock); +			retval = -EINVAL; +			goto exit; +		} + +		buffer = kmalloc(usb_data.count, GFP_KERNEL); +		if (buffer == NULL) { +			mutex_unlock(&vstdev->lock); +			retval = -ENOMEM; +			goto exit; +		} + +		urb = usb_alloc_urb(0, GFP_KERNEL); +		if (!urb) { +			mutex_unlock(&vstdev->lock); +			retval = -ENOMEM; +			goto exit; +		} + +		timeout = usb_data.timeout_ms; + +		pipe = usb_sndbulkpipe(dev, usb_data.pipe); + +		if (copy_from_user(buffer, usb_data.buffer, usb_data.count)) { +			dev_err(&dev->dev, "%s: can't copy_from_user\n", +				__func__); +			mutex_unlock(&vstdev->lock); +			retval = -EFAULT; +			goto exit; +		} + +		usb_anchor_urb(urb, &vstdev->submitted); +		retval = vstusb_fill_and_send_urb(urb, dev, pipe, buffer, +						  usb_data.count, &done); +		mutex_unlock(&vstdev->lock); +		if (retval) { +			usb_unanchor_urb(urb); +			dev_err(&dev->dev, +				"%s: error %d filling and sending urb %d\n", +				__func__, retval, pipe); +			goto exit; +		} + +		retval = vstusb_complete_urb(urb, &done, timeout, &cnt); +		if (retval) { +			dev_err(&dev->dev, "%s: error %d completing urb %d\n", +				__func__, retval, pipe); +		} + +		break; +	case IOCTL_VSTUSB_RECV_PIPE: + +		if ((usb_data.count == 0) || (usb_data.count > VST_MAXBUFFER)) { +			mutex_unlock(&vstdev->lock); +			retval = -EINVAL; +			goto exit; +		} + +		buffer = kmalloc(usb_data.count, GFP_KERNEL); +		if (buffer == NULL) { +			mutex_unlock(&vstdev->lock); +			retval = -ENOMEM; +			goto exit; +		} + +		urb = usb_alloc_urb(0, GFP_KERNEL); +		if (!urb) { +			mutex_unlock(&vstdev->lock); +			retval = -ENOMEM; +			goto exit; +		} + +		timeout = usb_data.timeout_ms; + +		pipe = usb_rcvbulkpipe(dev, usb_data.pipe); + +		usb_anchor_urb(urb, &vstdev->submitted); +		retval = vstusb_fill_and_send_urb(urb, dev, pipe, buffer, +						  usb_data.count, &done); +		mutex_unlock(&vstdev->lock); +		if (retval) { +			usb_unanchor_urb(urb); +			dev_err(&dev->dev, +				"%s: error %d filling and sending urb %d\n", +				__func__, retval, pipe); +			goto exit; +		} + +		retval = vstusb_complete_urb(urb, &done, timeout, &cnt); +		if (retval) { +			dev_err(&dev->dev, "%s: error %d completing urb %d\n", +				__func__, retval, pipe); +			goto exit; +		} + +		if (copy_to_user(usb_data.buffer, buffer, cnt)) { +			dev_err(&dev->dev, "%s: can't copy_to_user\n", +				__func__); +			retval = -EFAULT; +			goto exit; +		} + +		usb_data.count = cnt; +		if (copy_to_user(data, &usb_data, sizeof(struct vstusb_args))) { +			dev_err(&dev->dev, "%s: can't copy_to_user\n", +				__func__); +			retval = -EFAULT; +		} else { +			dev_dbg(&dev->dev, "%s: recv %zd bytes from pipe %d\n", +				__func__, usb_data.count, usb_data.pipe); +		} + +		break; + +	default: +		mutex_unlock(&vstdev->lock); +		dev_warn(&dev->dev, "ioctl_vstusb: invalid ioctl cmd %x\n", +			 cmd); +		return -EINVAL; +		break; +	} +exit: +	usb_free_urb(urb); +	kfree(buffer); +	return retval; +} + +static const struct file_operations vstusb_fops = { +	.owner =                THIS_MODULE, +	.read =                 vstusb_read, +	.write =                vstusb_write, +	.unlocked_ioctl =       vstusb_ioctl, +	.compat_ioctl =         vstusb_ioctl, +	.open =                 vstusb_open, +	.release =              vstusb_release, +}; + +static struct usb_class_driver usb_vstusb_class = { +	.name =         "usb/vstusb%d", +	.fops =         &vstusb_fops, +	.minor_base =   VSTUSB_MINOR_BASE, +}; + +static int vstusb_probe(struct usb_interface *intf, +			const struct usb_device_id *id) +{ +	struct usb_device *dev = interface_to_usbdev(intf); +	struct vstusb_device *vstdev; +	int i; +	int retval = 0; + +	/* allocate memory for our device state and intialize it */ + +	vstdev = kzalloc(sizeof(*vstdev), GFP_KERNEL); +	if (vstdev == NULL) +		return -ENOMEM; + +	/* must do usb_get_dev() prior to kref_init() since the kref_put() +	 * release function will do a usb_put_dev() */ +	usb_get_dev(dev); +	kref_init(&vstdev->kref); +	mutex_init(&vstdev->lock); + +	i = dev->descriptor.bcdDevice; + +	dev_dbg(&intf->dev, "Version %1d%1d.%1d%1d found at address %d\n", +		(i & 0xF000) >> 12, (i & 0xF00) >> 8, +		(i & 0xF0) >> 4, (i & 0xF), dev->devnum); + +	vstdev->present = 1; +	vstdev->isopen = 0; +	vstdev->usb_dev = dev; +	init_usb_anchor(&vstdev->submitted); + +	usb_set_intfdata(intf, vstdev); +	retval = usb_register_dev(intf, &usb_vstusb_class); +	if (retval) { +		dev_err(&intf->dev, +			"%s: Not able to get a minor for this device.\n", +			__func__); +		usb_set_intfdata(intf, NULL); +		kref_put(&vstdev->kref, vstusb_delete); +		return retval; +	} + +	/* let the user know what node this device is now attached to */ +	dev_info(&intf->dev, +		 "VST USB Device #%d now attached to major %d minor %d\n", +		 (intf->minor - VSTUSB_MINOR_BASE), USB_MAJOR, intf->minor); + +	dev_info(&intf->dev, "%s, %s\n", DRIVER_DESC, DRIVER_VERSION); + +	return retval; +} + +static void vstusb_disconnect(struct usb_interface *intf) +{ +	struct vstusb_device *vstdev = usb_get_intfdata(intf); + +	usb_deregister_dev(intf, &usb_vstusb_class); +	usb_set_intfdata(intf, NULL); + +	if (vstdev) { + +		mutex_lock(&vstdev->lock); +		vstdev->present = 0; + +		usb_kill_anchored_urbs(&vstdev->submitted); + +		mutex_unlock(&vstdev->lock); + +		kref_put(&vstdev->kref, vstusb_delete); +	} + +} + +static int vstusb_suspend(struct usb_interface *intf, pm_message_t message) +{ +	struct vstusb_device *vstdev = usb_get_intfdata(intf); +	int time; +	if (!vstdev) +		return 0; + +	mutex_lock(&vstdev->lock); +	time = usb_wait_anchor_empty_timeout(&vstdev->submitted, 1000); +	if (!time) +		usb_kill_anchored_urbs(&vstdev->submitted); +	mutex_unlock(&vstdev->lock); + +	return 0; +} + +static int vstusb_resume(struct usb_interface *intf) +{ +	return 0; +} + +static struct usb_driver vstusb_driver = { +	.name =         "vstusb", +	.probe =        vstusb_probe, +	.disconnect =   vstusb_disconnect, +	.suspend =      vstusb_suspend, +	.resume =       vstusb_resume, +	.id_table = id_table, +}; + +static int __init vstusb_init(void) +{ +	int rc; + +	rc = usb_register(&vstusb_driver); +	if (rc) +		printk(KERN_ERR "%s: failed to register (%d)", __func__, rc); + +	return rc; +} + +static void __exit vstusb_exit(void) +{ +	usb_deregister(&vstusb_driver); +} + +module_init(vstusb_init); +module_exit(vstusb_exit); + +MODULE_AUTHOR("Dennis O'Brien/Stephen Ware"); +MODULE_DESCRIPTION(DRIVER_VERSION); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/mon/mon_bin.c b/drivers/usb/mon/mon_bin.c index 6566fc0a322..c9de3f027aa 100644 --- a/drivers/usb/mon/mon_bin.c +++ b/drivers/usb/mon/mon_bin.c @@ -1162,9 +1162,9 @@ int mon_bin_add(struct mon_bus *mbus, const struct usb_bus *ubus)  	if (minor >= MON_BIN_MAX_MINOR)  		return 0; -	dev = device_create_drvdata(mon_bin_class, ubus? ubus->controller: NULL, -				    MKDEV(MAJOR(mon_bin_dev0), minor), NULL, -				    "usbmon%d", minor); +	dev = device_create(mon_bin_class, ubus ? ubus->controller : NULL, +			    MKDEV(MAJOR(mon_bin_dev0), minor), NULL, +			    "usbmon%d", minor);  	if (IS_ERR(dev))  		return 0; diff --git a/drivers/usb/mon/mon_main.c b/drivers/usb/mon/mon_main.c index 442d8076b20..5e0ab4201c0 100644 --- a/drivers/usb/mon/mon_main.c +++ b/drivers/usb/mon/mon_main.c @@ -361,12 +361,12 @@ static int __init mon_init(void)  	}  	// MOD_INC_USE_COUNT(which_module?); -	usb_register_notify(&mon_nb);  	mutex_lock(&usb_bus_list_lock);  	list_for_each_entry (ubus, &usb_bus_list, bus_list) {  		mon_bus_init(ubus);  	} +	usb_register_notify(&mon_nb);  	mutex_unlock(&usb_bus_list_lock);  	return 0; diff --git a/drivers/usb/musb/Kconfig b/drivers/usb/musb/Kconfig index 58b2b8fc943..4b9542bbb35 100644 --- a/drivers/usb/musb/Kconfig +++ b/drivers/usb/musb/Kconfig @@ -33,10 +33,6 @@ config USB_MUSB_SOC  	default y if ARCH_DAVINCI  	default y if ARCH_OMAP2430  	default y if ARCH_OMAP34XX -	help -	  Use a static <asm/arch/hdrc_cnf.h> file to describe how the -	  controller is configured (endpoints, mechanisms, etc) on the -	  current iteration of a given system-on-chip.  comment "DaVinci 644x USB support"  	depends on USB_MUSB_HDRC && ARCH_DAVINCI diff --git a/drivers/usb/musb/cppi_dma.h b/drivers/usb/musb/cppi_dma.h index fc5216b5d2c..729b4071787 100644 --- a/drivers/usb/musb/cppi_dma.h +++ b/drivers/usb/musb/cppi_dma.h @@ -119,8 +119,8 @@ struct cppi {  	void __iomem			*mregs;		/* Mentor regs */  	void __iomem			*tibase;	/* TI/CPPI regs */ -	struct cppi_channel		tx[MUSB_C_NUM_EPT - 1]; -	struct cppi_channel		rx[MUSB_C_NUM_EPR - 1]; +	struct cppi_channel		tx[4]; +	struct cppi_channel		rx[4];  	struct dma_pool			*pool; diff --git a/drivers/usb/musb/davinci.c b/drivers/usb/musb/davinci.c index 75baf181a8c..dfb3bcbe00f 100644 --- a/drivers/usb/musb/davinci.c +++ b/drivers/usb/musb/davinci.c @@ -30,6 +30,7 @@  #include <linux/delay.h>  #include <linux/clk.h>  #include <linux/io.h> +#include <linux/gpio.h>  #include <asm/arch/hardware.h>  #include <asm/arch/memory.h> @@ -39,7 +40,7 @@  #include "musb_core.h"  #ifdef CONFIG_MACH_DAVINCI_EVM -#include <asm/arch/i2c-client.h> +#define GPIO_nVBUS_DRV		87  #endif  #include "davinci.h" @@ -138,7 +139,6 @@ static int vbus_state = -1;  /* VBUS SWITCHING IS BOARD-SPECIFIC */  #ifdef CONFIG_MACH_DAVINCI_EVM -#ifndef CONFIG_MACH_DAVINCI_EVM_OTG  /* I2C operations are always synchronous, and require a task context.   * With unloaded systems, using the shared workqueue seems to suffice @@ -146,12 +146,11 @@ static int vbus_state = -1;   */  static void evm_deferred_drvvbus(struct work_struct *ignored)  { -	davinci_i2c_expander_op(0x3a, USB_DRVVBUS, vbus_state); +	gpio_set_value_cansleep(GPIO_nVBUS_DRV, vbus_state);  	vbus_state = !vbus_state;  }  static DECLARE_WORK(evm_vbus_work, evm_deferred_drvvbus); -#endif	/* modified board */  #endif	/* EVM */  static void davinci_source_power(struct musb *musb, int is_on, int immediate) @@ -165,21 +164,10 @@ static void davinci_source_power(struct musb *musb, int is_on, int immediate)  #ifdef CONFIG_MACH_DAVINCI_EVM  	if (machine_is_davinci_evm()) { -#ifdef CONFIG_MACH_DAVINCI_EVM_OTG -		/* modified EVM board switching VBUS with GPIO(6) not I2C -		 * NOTE:  PINMUX0.RGB888 (bit23) must be clear -		 */ -		if (is_on) -			gpio_set(GPIO(6)); -		else -			gpio_clear(GPIO(6)); -		immediate = 1; -#else  		if (immediate) -			davinci_i2c_expander_op(0x3a, USB_DRVVBUS, !is_on); +			gpio_set_value_cansleep(GPIO_nVBUS_DRV, vbus_state);  		else  			schedule_work(&evm_vbus_work); -#endif  	}  #endif  	if (immediate) diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index 128e949db47..5280dba9b1f 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -82,9 +82,9 @@  /*   * This gets many kinds of configuration information:   *	- Kconfig for everything user-configurable - *	- <asm/arch/hdrc_cnf.h> for SOC or family details   *	- platform_device for addressing, irq, and platform_data   *	- platform_data is mostly for board-specific informarion + *	  (plus recentrly, SOC or family details)   *   * Most of the conditional compilation will (someday) vanish.   */ @@ -114,8 +114,8 @@ -unsigned debug; -module_param(debug, uint, S_IRUGO | S_IWUSR); +unsigned musb_debug; +module_param(musb_debug, uint, S_IRUGO | S_IWUSR);  MODULE_PARM_DESC(debug, "Debug message level. Default = 0");  #define DRIVER_AUTHOR "Mentor Graphics, Texas Instruments, Nokia" @@ -974,9 +974,9 @@ static void musb_shutdown(struct platform_device *pdev)  /*   * The silicon either has hard-wired endpoint configurations, or else   * "dynamic fifo" sizing.  The driver has support for both, though at this - * writing only the dynamic sizing is very well tested.   We use normal - * idioms to so both modes are compile-tested, but dead code elimination - * leaves only the relevant one in the object file. + * writing only the dynamic sizing is very well tested.   Since we switched + * away from compile-time hardware parameters, we can no longer rely on + * dead code elimination to leave only the relevant one in the object file.   *   * We don't currently use dynamic fifo setup capability to do anything   * more than selecting one of a bunch of predefined configurations. @@ -1806,6 +1806,7 @@ allocate_instance(struct device *dev,  	musb->ctrl_base = mbase;  	musb->nIrq = -ENODEV;  	musb->config = config; +	BUG_ON(musb->config->num_eps > MUSB_C_NUM_EPS);  	for (epnum = 0, ep = musb->endpoints;  			epnum < musb->config->num_eps;  			epnum++, ep++) { @@ -2054,15 +2055,6 @@ bad_config:  	} -	return 0; - -fail: -	if (musb->clock) -		clk_put(musb->clock); -	device_init_wakeup(dev, 0); -	musb_free(musb); -	return status; -  #ifdef CONFIG_SYSFS  	status = device_create_file(dev, &dev_attr_mode);  	status = device_create_file(dev, &dev_attr_vbus); @@ -2071,12 +2063,31 @@ fail:  #endif /* CONFIG_USB_GADGET_MUSB_HDRC */  	status = 0;  #endif +	if (status) +		goto fail2; -	return status; +	return 0;  fail2: +#ifdef CONFIG_SYSFS +	device_remove_file(musb->controller, &dev_attr_mode); +	device_remove_file(musb->controller, &dev_attr_vbus); +#ifdef CONFIG_USB_MUSB_OTG +	device_remove_file(musb->controller, &dev_attr_srp); +#endif +#endif  	musb_platform_exit(musb); -	goto fail; +fail: +	dev_err(musb->controller, +		"musb_init_controller failed with status %d\n", status); + +	if (musb->clock) +		clk_put(musb->clock); +	device_init_wakeup(dev, 0); +	musb_free(musb); + +	return status; +  }  /*-------------------------------------------------------------------------*/ @@ -2237,7 +2248,7 @@ static int __init musb_init(void)  		"host"  #endif  		", debug=%d\n", -		musb_driver_name, debug); +		musb_driver_name, musb_debug);  	return platform_driver_probe(&musb_driver, musb_probe);  } diff --git a/drivers/usb/musb/musb_debug.h b/drivers/usb/musb/musb_debug.h index 4d2794441b1..9fc1db44c72 100644 --- a/drivers/usb/musb/musb_debug.h +++ b/drivers/usb/musb/musb_debug.h @@ -48,11 +48,11 @@  				__func__, __LINE__ , ## args); \  	} } while (0) -extern unsigned debug; +extern unsigned musb_debug;  static inline int _dbg_level(unsigned l)  { -	return debug >= l; +	return musb_debug >= l;  }  #define DBG(level, fmt, args...) xprintk(level, KERN_DEBUG, fmt, ## args) diff --git a/drivers/usb/musb/musb_gadget_ep0.c b/drivers/usb/musb/musb_gadget_ep0.c index a57652fff39..3f5e30ddfa2 100644 --- a/drivers/usb/musb/musb_gadget_ep0.c +++ b/drivers/usb/musb/musb_gadget_ep0.c @@ -437,7 +437,7 @@ static void ep0_rxstate(struct musb *musb)  {  	void __iomem		*regs = musb->control_ep->regs;  	struct usb_request	*req; -	u16			tmp; +	u16			count, csr;  	req = next_ep0_request(musb); @@ -449,35 +449,35 @@ static void ep0_rxstate(struct musb *musb)  		unsigned	len = req->length - req->actual;  		/* read the buffer */ -		tmp = musb_readb(regs, MUSB_COUNT0); -		if (tmp > len) { +		count = musb_readb(regs, MUSB_COUNT0); +		if (count > len) {  			req->status = -EOVERFLOW; -			tmp = len; +			count = len;  		} -		musb_read_fifo(&musb->endpoints[0], tmp, buf); -		req->actual += tmp; -		tmp = MUSB_CSR0_P_SVDRXPKTRDY; -		if (tmp < 64 || req->actual == req->length) { +		musb_read_fifo(&musb->endpoints[0], count, buf); +		req->actual += count; +		csr = MUSB_CSR0_P_SVDRXPKTRDY; +		if (count < 64 || req->actual == req->length) {  			musb->ep0_state = MUSB_EP0_STAGE_STATUSIN; -			tmp |= MUSB_CSR0_P_DATAEND; +			csr |= MUSB_CSR0_P_DATAEND;  		} else  			req = NULL;  	} else -		tmp = MUSB_CSR0_P_SVDRXPKTRDY | MUSB_CSR0_P_SENDSTALL; +		csr = MUSB_CSR0_P_SVDRXPKTRDY | MUSB_CSR0_P_SENDSTALL;  	/* Completion handler may choose to stall, e.g. because the  	 * message just received holds invalid data.  	 */  	if (req) { -		musb->ackpend = tmp; +		musb->ackpend = csr;  		musb_g_ep0_giveback(musb, req);  		if (!musb->ackpend)  			return;  		musb->ackpend = 0;  	}  	musb_ep_select(musb->mregs, 0); -	musb_writew(regs, MUSB_CSR0, tmp); +	musb_writew(regs, MUSB_CSR0, csr);  }  /* diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c index 8b4be012669..e45e70bcc5e 100644 --- a/drivers/usb/musb/musb_host.c +++ b/drivers/usb/musb/musb_host.c @@ -108,7 +108,7 @@ static void musb_ep_program(struct musb *musb, u8 epnum,  /*   * Clear TX fifo. Needed to avoid BABBLE errors.   */ -static inline void musb_h_tx_flush_fifo(struct musb_hw_ep *ep) +static void musb_h_tx_flush_fifo(struct musb_hw_ep *ep)  {  	void __iomem	*epio = ep->regs;  	u16		csr; @@ -291,6 +291,7 @@ __acquires(musb->lock)  			urb->actual_length, urb->transfer_buffer_length  			); +	usb_hcd_unlink_urb_from_ep(musb_to_hcd(musb), urb);  	spin_unlock(&musb->lock);  	usb_hcd_giveback_urb(musb_to_hcd(musb), urb, status);  	spin_lock(&musb->lock); @@ -353,8 +354,6 @@ musb_giveback(struct musb_qh *qh, struct urb *urb, int status)  		break;  	} -	usb_hcd_unlink_urb_from_ep(musb_to_hcd(musb), urb); -  	qh->is_ready = 0;  	__musb_giveback(musb, urb, status);  	qh->is_ready = ready; @@ -379,6 +378,19 @@ musb_giveback(struct musb_qh *qh, struct urb *urb, int status)  		switch (qh->type) { +		case USB_ENDPOINT_XFER_CONTROL: +		case USB_ENDPOINT_XFER_BULK: +			/* fifo policy for these lists, except that NAKing +			 * should rotate a qh to the end (for fairness). +			 */ +			if (qh->mux == 1) { +				head = qh->ring.prev; +				list_del(&qh->ring); +				kfree(qh); +				qh = first_qh(head); +				break; +			} +  		case USB_ENDPOINT_XFER_ISOC:  		case USB_ENDPOINT_XFER_INT:  			/* this is where periodic bandwidth should be @@ -389,17 +401,6 @@ musb_giveback(struct musb_qh *qh, struct urb *urb, int status)  			kfree(qh);  			qh = NULL;  			break; - -		case USB_ENDPOINT_XFER_CONTROL: -		case USB_ENDPOINT_XFER_BULK: -			/* fifo policy for these lists, except that NAKing -			 * should rotate a qh to the end (for fairness). -			 */ -			head = qh->ring.prev; -			list_del(&qh->ring); -			kfree(qh); -			qh = first_qh(head); -			break;  		}  	}  	return qh; @@ -436,7 +437,7 @@ musb_advance_schedule(struct musb *musb, struct urb *urb,  	}  } -static inline u16 musb_h_flush_rxfifo(struct musb_hw_ep *hw_ep, u16 csr) +static u16 musb_h_flush_rxfifo(struct musb_hw_ep *hw_ep, u16 csr)  {  	/* we don't want fifo to fill itself again;  	 * ignore dma (various models), @@ -1005,7 +1006,7 @@ static bool musb_h_ep0_continue(struct musb *musb, u16 len, struct urb *urb)  /*   * Handle default endpoint interrupt as host. Only called in IRQ time - * from the LinuxIsr() interrupt service routine. + * from musb_interrupt().   *   * called with controller irqlocked   */ @@ -1508,10 +1509,29 @@ void musb_host_rx(struct musb *musb, u8 epnum)  		musb_writew(hw_ep->regs, MUSB_RXCSR, val);  #ifdef CONFIG_USB_INVENTRA_DMA +		if (usb_pipeisoc(pipe)) { +			struct usb_iso_packet_descriptor *d; + +			d = urb->iso_frame_desc + qh->iso_idx; +			d->actual_length = xfer_len; + +			/* even if there was an error, we did the dma +			 * for iso_frame_desc->length +			 */ +			if (d->status != EILSEQ && d->status != -EOVERFLOW) +				d->status = 0; + +			if (++qh->iso_idx >= urb->number_of_packets) +				done = true; +			else +				done = false; + +		} else  {  		/* done if urb buffer is full or short packet is recd */  		done = (urb->actual_length + xfer_len >=  				urb->transfer_buffer_length  			|| dma->actual_len < qh->maxpacket); +		}  		/* send IN token for next packet, without AUTOREQ */  		if (!done) { @@ -1548,7 +1568,8 @@ void musb_host_rx(struct musb *musb, u8 epnum)  		if (dma) {  			struct dma_controller	*c;  			u16			rx_count; -			int			ret; +			int			ret, length; +			dma_addr_t		buf;  			rx_count = musb_readw(epio, MUSB_RXCOUNT); @@ -1561,6 +1582,35 @@ void musb_host_rx(struct musb *musb, u8 epnum)  			c = musb->dma_controller; +			if (usb_pipeisoc(pipe)) { +				int status = 0; +				struct usb_iso_packet_descriptor *d; + +				d = urb->iso_frame_desc + qh->iso_idx; + +				if (iso_err) { +					status = -EILSEQ; +					urb->error_count++; +				} +				if (rx_count > d->length) { +					if (status == 0) { +						status = -EOVERFLOW; +						urb->error_count++; +					} +					DBG(2, "** OVERFLOW %d into %d\n",\ +					    rx_count, d->length); + +					length = d->length; +				} else +					length = rx_count; +				d->status = status; +				buf = urb->transfer_dma + d->offset; +			} else { +				length = rx_count; +				buf = urb->transfer_dma + +						urb->actual_length; +			} +  			dma->desired_mode = 0;  #ifdef USE_MODE1  			/* because of the issue below, mode 1 will @@ -1572,6 +1622,12 @@ void musb_host_rx(struct musb *musb, u8 epnum)  						urb->actual_length)  					> qh->maxpacket)  				dma->desired_mode = 1; +			if (rx_count < hw_ep->max_packet_sz_rx) { +				length = rx_count; +				dma->bDesiredMode = 0; +			} else { +				length = urb->transfer_buffer_length; +			}  #endif  /* Disadvantage of using mode 1: @@ -1609,12 +1665,7 @@ void musb_host_rx(struct musb *musb, u8 epnum)  			 */  			ret = c->channel_program(  				dma, qh->maxpacket, -				dma->desired_mode, -				urb->transfer_dma -					+ urb->actual_length, -				(dma->desired_mode == 0) -					? rx_count -					: urb->transfer_buffer_length); +				dma->desired_mode, buf, length);  			if (!ret) {  				c->channel_release(dma); @@ -1632,19 +1683,6 @@ void musb_host_rx(struct musb *musb, u8 epnum)  		}  	} -	if (dma && usb_pipeisoc(pipe)) { -		struct usb_iso_packet_descriptor	*d; -		int					iso_stat = status; - -		d = urb->iso_frame_desc + qh->iso_idx; -		d->actual_length += xfer_len; -		if (iso_err) { -			iso_stat = -EILSEQ; -			urb->error_count++; -		} -		d->status = iso_stat; -	} -  finish:  	urb->actual_length += xfer_len;  	qh->offset += xfer_len; @@ -1672,22 +1710,9 @@ static int musb_schedule(  	struct list_head	*head = NULL;  	/* use fixed hardware for control and bulk */ -	switch (qh->type) { -	case USB_ENDPOINT_XFER_CONTROL: +	if (qh->type == USB_ENDPOINT_XFER_CONTROL) {  		head = &musb->control;  		hw_ep = musb->control_ep; -		break; -	case USB_ENDPOINT_XFER_BULK: -		hw_ep = musb->bulk_ep; -		if (is_in) -			head = &musb->in_bulk; -		else -			head = &musb->out_bulk; -		break; -	} -	if (head) { -		idle = list_empty(head); -		list_add_tail(&qh->ring, head);  		goto success;  	} @@ -1726,19 +1751,34 @@ static int musb_schedule(  		else  			diff = hw_ep->max_packet_sz_tx - qh->maxpacket; -		if (diff > 0 && best_diff > diff) { +		if (diff >= 0 && best_diff > diff) {  			best_diff = diff;  			best_end = epnum;  		}  	} -	if (best_end < 0) +	/* use bulk reserved ep1 if no other ep is free */ +	if (best_end > 0 && qh->type == USB_ENDPOINT_XFER_BULK) { +		hw_ep = musb->bulk_ep; +		if (is_in) +			head = &musb->in_bulk; +		else +			head = &musb->out_bulk; +		goto success; +	} else if (best_end < 0) {  		return -ENOSPC; +	}  	idle = 1; +	qh->mux = 0;  	hw_ep = musb->endpoints + best_end;  	musb->periodic[best_end] = qh;  	DBG(4, "qh %p periodic slot %d\n", qh, best_end);  success: +	if (head) { +		idle = list_empty(head); +		list_add_tail(&qh->ring, head); +		qh->mux = 1; +	}  	qh->hw_ep = hw_ep;  	qh->hep->hcpriv = qh;  	if (idle) @@ -1791,7 +1831,9 @@ static int musb_urb_enqueue(  	 */  	qh = kzalloc(sizeof *qh, mem_flags);  	if (!qh) { +		spin_lock_irqsave(&musb->lock, flags);  		usb_hcd_unlink_urb_from_ep(hcd, urb); +		spin_unlock_irqrestore(&musb->lock, flags);  		return -ENOMEM;  	} @@ -1873,7 +1915,11 @@ static int musb_urb_enqueue(  			/* set up tt info if needed */  			if (urb->dev->tt) {  				qh->h_port_reg = (u8) urb->dev->ttport; -				qh->h_addr_reg |= 0x80; +				if (urb->dev->tt->hub) +					qh->h_addr_reg = +						(u8) urb->dev->tt->hub->devnum; +				if (urb->dev->tt->multi) +					qh->h_addr_reg |= 0x80;  			}  		}  	} @@ -1903,7 +1949,9 @@ static int musb_urb_enqueue(  done:  	if (ret != 0) { +		spin_lock_irqsave(&musb->lock, flags);  		usb_hcd_unlink_urb_from_ep(hcd, urb); +		spin_unlock_irqrestore(&musb->lock, flags);  		kfree(qh);  	}  	return ret; @@ -2008,11 +2056,13 @@ static int musb_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)  			sched = &musb->control;  			break;  		case USB_ENDPOINT_XFER_BULK: -			if (usb_pipein(urb->pipe)) -				sched = &musb->in_bulk; -			else -				sched = &musb->out_bulk; -			break; +			if (qh->mux == 1) { +				if (usb_pipein(urb->pipe)) +					sched = &musb->in_bulk; +				else +					sched = &musb->out_bulk; +				break; +			}  		default:  			/* REVISIT when we get a schedule tree, periodic  			 * transfers won't always be at the head of a @@ -2060,11 +2110,13 @@ musb_h_disable(struct usb_hcd *hcd, struct usb_host_endpoint *hep)  		sched = &musb->control;  		break;  	case USB_ENDPOINT_XFER_BULK: -		if (is_in) -			sched = &musb->in_bulk; -		else -			sched = &musb->out_bulk; -		break; +		if (qh->mux == 1) { +			if (is_in) +				sched = &musb->in_bulk; +			else +				sched = &musb->out_bulk; +			break; +		}  	default:  		/* REVISIT when we get a schedule tree, periodic transfers  		 * won't always be at the head of a singleton queue... diff --git a/drivers/usb/musb/musb_host.h b/drivers/usb/musb/musb_host.h index 77bcdb9d5b3..0b7fbcd2196 100644 --- a/drivers/usb/musb/musb_host.h +++ b/drivers/usb/musb/musb_host.h @@ -53,6 +53,7 @@ struct musb_qh {  	struct list_head	ring;		/* of musb_qh */  	/* struct musb_qh		*next; */	/* for periodic tree */ +	u8			mux;		/* qh multiplexed to hw_ep */  	unsigned		offset;		/* in urb->transfer_buffer */  	unsigned		segsize;	/* current xfer fragment */ diff --git a/drivers/usb/musb/musb_io.h b/drivers/usb/musb/musb_io.h index 6bbedae83af..223f0a51409 100644 --- a/drivers/usb/musb/musb_io.h +++ b/drivers/usb/musb/musb_io.h @@ -37,7 +37,9 @@  #include <linux/io.h> -#ifndef	CONFIG_ARM +#if !defined(CONFIG_ARM) && !defined(CONFIG_SUPERH) \ +	&& !defined(CONFIG_AVR32) && !defined(CONFIG_PPC32) \ +	&& !defined(CONFIG_PPC64)  static inline void readsl(const void __iomem *addr, void *buf, int len)  	{ insl((unsigned long)addr, buf, len); }  static inline void readsw(const void __iomem *addr, void *buf, int len) diff --git a/drivers/usb/musb/musbhsdma.c b/drivers/usb/musb/musbhsdma.c index 9ba8fb7fcd2..8c734ef2c1e 100644 --- a/drivers/usb/musb/musbhsdma.c +++ b/drivers/usb/musb/musbhsdma.c @@ -45,8 +45,8 @@  #define MUSB_HSDMA_ADDRESS		0x8  #define MUSB_HSDMA_COUNT		0xc -#define MUSB_HSDMA_CHANNEL_OFFSET(_bChannel, _offset)		\ -		(MUSB_HSDMA_BASE + (_bChannel << 4) + _offset) +#define MUSB_HSDMA_CHANNEL_OFFSET(_bchannel, _offset)		\ +		(MUSB_HSDMA_BASE + (_bchannel << 4) + _offset)  /* control register (16-bit): */  #define MUSB_HSDMA_ENABLE_SHIFT		0 @@ -67,23 +67,23 @@  struct musb_dma_controller;  struct musb_dma_channel { -	struct dma_channel		Channel; +	struct dma_channel		channel;  	struct musb_dma_controller	*controller; -	u32				dwStartAddress; +	u32				start_addr;  	u32				len; -	u16				wMaxPacketSize; -	u8				bIndex; +	u16				max_packet_sz; +	u8				idx;  	u8				epnum;  	u8				transmit;  };  struct musb_dma_controller { -	struct dma_controller		Controller; -	struct musb_dma_channel		aChannel[MUSB_HSDMA_CHANNELS]; -	void				*pDmaPrivate; -	void __iomem			*pCoreBase; -	u8				bChannelCount; -	u8				bmUsedChannels; +	struct dma_controller		controller; +	struct musb_dma_channel		channel[MUSB_HSDMA_CHANNELS]; +	void				*private_data; +	void __iomem			*base; +	u8				channel_count; +	u8				used_channels;  	u8				irq;  }; @@ -93,91 +93,91 @@ static int dma_controller_start(struct dma_controller *c)  	return 0;  } -static void dma_channel_release(struct dma_channel *pChannel); +static void dma_channel_release(struct dma_channel *channel);  static int dma_controller_stop(struct dma_controller *c)  { -	struct musb_dma_controller *controller = -		container_of(c, struct musb_dma_controller, Controller); -	struct musb *musb = (struct musb *) controller->pDmaPrivate; -	struct dma_channel *pChannel; -	u8 bBit; +	struct musb_dma_controller *controller = container_of(c, +			struct musb_dma_controller, controller); +	struct musb *musb = controller->private_data; +	struct dma_channel *channel; +	u8 bit; -	if (controller->bmUsedChannels != 0) { +	if (controller->used_channels != 0) {  		dev_err(musb->controller,  			"Stopping DMA controller while channel active\n"); -		for (bBit = 0; bBit < MUSB_HSDMA_CHANNELS; bBit++) { -			if (controller->bmUsedChannels & (1 << bBit)) { -				pChannel = &controller->aChannel[bBit].Channel; -				dma_channel_release(pChannel); +		for (bit = 0; bit < MUSB_HSDMA_CHANNELS; bit++) { +			if (controller->used_channels & (1 << bit)) { +				channel = &controller->channel[bit].channel; +				dma_channel_release(channel); -				if (!controller->bmUsedChannels) +				if (!controller->used_channels)  					break;  			}  		}  	} +  	return 0;  }  static struct dma_channel *dma_channel_allocate(struct dma_controller *c,  				struct musb_hw_ep *hw_ep, u8 transmit)  { -	u8 bBit; -	struct dma_channel *pChannel = NULL; -	struct musb_dma_channel *pImplChannel = NULL; -	struct musb_dma_controller *controller = -			container_of(c, struct musb_dma_controller, Controller); - -	for (bBit = 0; bBit < MUSB_HSDMA_CHANNELS; bBit++) { -		if (!(controller->bmUsedChannels & (1 << bBit))) { -			controller->bmUsedChannels |= (1 << bBit); -			pImplChannel = &(controller->aChannel[bBit]); -			pImplChannel->controller = controller; -			pImplChannel->bIndex = bBit; -			pImplChannel->epnum = hw_ep->epnum; -			pImplChannel->transmit = transmit; -			pChannel = &(pImplChannel->Channel); -			pChannel->private_data = pImplChannel; -			pChannel->status = MUSB_DMA_STATUS_FREE; -			pChannel->max_len = 0x10000; +	struct musb_dma_controller *controller = container_of(c, +			struct musb_dma_controller, controller); +	struct musb_dma_channel *musb_channel = NULL; +	struct dma_channel *channel = NULL; +	u8 bit; + +	for (bit = 0; bit < MUSB_HSDMA_CHANNELS; bit++) { +		if (!(controller->used_channels & (1 << bit))) { +			controller->used_channels |= (1 << bit); +			musb_channel = &(controller->channel[bit]); +			musb_channel->controller = controller; +			musb_channel->idx = bit; +			musb_channel->epnum = hw_ep->epnum; +			musb_channel->transmit = transmit; +			channel = &(musb_channel->channel); +			channel->private_data = musb_channel; +			channel->status = MUSB_DMA_STATUS_FREE; +			channel->max_len = 0x10000;  			/* Tx => mode 1; Rx => mode 0 */ -			pChannel->desired_mode = transmit; -			pChannel->actual_len = 0; +			channel->desired_mode = transmit; +			channel->actual_len = 0;  			break;  		}  	} -	return pChannel; + +	return channel;  } -static void dma_channel_release(struct dma_channel *pChannel) +static void dma_channel_release(struct dma_channel *channel)  { -	struct musb_dma_channel *pImplChannel = -		(struct musb_dma_channel *) pChannel->private_data; +	struct musb_dma_channel *musb_channel = channel->private_data; -	pChannel->actual_len = 0; -	pImplChannel->dwStartAddress = 0; -	pImplChannel->len = 0; +	channel->actual_len = 0; +	musb_channel->start_addr = 0; +	musb_channel->len = 0; -	pImplChannel->controller->bmUsedChannels &= -		~(1 << pImplChannel->bIndex); +	musb_channel->controller->used_channels &= +		~(1 << musb_channel->idx); -	pChannel->status = MUSB_DMA_STATUS_UNKNOWN; +	channel->status = MUSB_DMA_STATUS_UNKNOWN;  } -static void configure_channel(struct dma_channel *pChannel, +static void configure_channel(struct dma_channel *channel,  				u16 packet_sz, u8 mode,  				dma_addr_t dma_addr, u32 len)  { -	struct musb_dma_channel *pImplChannel = -		(struct musb_dma_channel *) pChannel->private_data; -	struct musb_dma_controller *controller = pImplChannel->controller; -	void __iomem *mbase = controller->pCoreBase; -	u8 bChannel = pImplChannel->bIndex; +	struct musb_dma_channel *musb_channel = channel->private_data; +	struct musb_dma_controller *controller = musb_channel->controller; +	void __iomem *mbase = controller->base; +	u8 bchannel = musb_channel->idx;  	u16 csr = 0;  	DBG(4, "%p, pkt_sz %d, addr 0x%x, len %d, mode %d\n", -			pChannel, packet_sz, dma_addr, len, mode); +			channel, packet_sz, dma_addr, len, mode);  	if (mode) {  		csr |= 1 << MUSB_HSDMA_MODE1_SHIFT; @@ -195,180 +195,183 @@ static void configure_channel(struct dma_channel *pChannel,  		}  	} -	csr |= (pImplChannel->epnum << MUSB_HSDMA_ENDPOINT_SHIFT) +	csr |= (musb_channel->epnum << MUSB_HSDMA_ENDPOINT_SHIFT)  		| (1 << MUSB_HSDMA_ENABLE_SHIFT)  		| (1 << MUSB_HSDMA_IRQENABLE_SHIFT) -		| (pImplChannel->transmit +		| (musb_channel->transmit  				? (1 << MUSB_HSDMA_TRANSMIT_SHIFT)  				: 0);  	/* address/count */  	musb_writel(mbase, -		MUSB_HSDMA_CHANNEL_OFFSET(bChannel, MUSB_HSDMA_ADDRESS), +		MUSB_HSDMA_CHANNEL_OFFSET(bchannel, MUSB_HSDMA_ADDRESS),  		dma_addr);  	musb_writel(mbase, -		MUSB_HSDMA_CHANNEL_OFFSET(bChannel, MUSB_HSDMA_COUNT), +		MUSB_HSDMA_CHANNEL_OFFSET(bchannel, MUSB_HSDMA_COUNT),  		len);  	/* control (this should start things) */  	musb_writew(mbase, -		MUSB_HSDMA_CHANNEL_OFFSET(bChannel, MUSB_HSDMA_CONTROL), +		MUSB_HSDMA_CHANNEL_OFFSET(bchannel, MUSB_HSDMA_CONTROL),  		csr);  } -static int dma_channel_program(struct dma_channel *pChannel, +static int dma_channel_program(struct dma_channel *channel,  				u16 packet_sz, u8 mode,  				dma_addr_t dma_addr, u32 len)  { -	struct musb_dma_channel *pImplChannel = -			(struct musb_dma_channel *) pChannel->private_data; +	struct musb_dma_channel *musb_channel = channel->private_data;  	DBG(2, "ep%d-%s pkt_sz %d, dma_addr 0x%x length %d, mode %d\n", -		pImplChannel->epnum, -		pImplChannel->transmit ? "Tx" : "Rx", +		musb_channel->epnum, +		musb_channel->transmit ? "Tx" : "Rx",  		packet_sz, dma_addr, len, mode); -	BUG_ON(pChannel->status == MUSB_DMA_STATUS_UNKNOWN || -		pChannel->status == MUSB_DMA_STATUS_BUSY); +	BUG_ON(channel->status == MUSB_DMA_STATUS_UNKNOWN || +		channel->status == MUSB_DMA_STATUS_BUSY); -	pChannel->actual_len = 0; -	pImplChannel->dwStartAddress = dma_addr; -	pImplChannel->len = len; -	pImplChannel->wMaxPacketSize = packet_sz; -	pChannel->status = MUSB_DMA_STATUS_BUSY; +	channel->actual_len = 0; +	musb_channel->start_addr = dma_addr; +	musb_channel->len = len; +	musb_channel->max_packet_sz = packet_sz; +	channel->status = MUSB_DMA_STATUS_BUSY;  	if ((mode == 1) && (len >= packet_sz)) -		configure_channel(pChannel, packet_sz, 1, dma_addr, len); +		configure_channel(channel, packet_sz, 1, dma_addr, len);  	else -		configure_channel(pChannel, packet_sz, 0, dma_addr, len); +		configure_channel(channel, packet_sz, 0, dma_addr, len);  	return true;  } -static int dma_channel_abort(struct dma_channel *pChannel) +static int dma_channel_abort(struct dma_channel *channel)  { -	struct musb_dma_channel *pImplChannel = -		(struct musb_dma_channel *) pChannel->private_data; -	u8 bChannel = pImplChannel->bIndex; -	void __iomem *mbase = pImplChannel->controller->pCoreBase; +	struct musb_dma_channel *musb_channel = channel->private_data; +	void __iomem *mbase = musb_channel->controller->base; + +	u8 bchannel = musb_channel->idx;  	u16 csr; -	if (pChannel->status == MUSB_DMA_STATUS_BUSY) { -		if (pImplChannel->transmit) { +	if (channel->status == MUSB_DMA_STATUS_BUSY) { +		if (musb_channel->transmit) {  			csr = musb_readw(mbase, -				MUSB_EP_OFFSET(pImplChannel->epnum, +				MUSB_EP_OFFSET(musb_channel->epnum,  						MUSB_TXCSR));  			csr &= ~(MUSB_TXCSR_AUTOSET |  				 MUSB_TXCSR_DMAENAB |  				 MUSB_TXCSR_DMAMODE);  			musb_writew(mbase, -				MUSB_EP_OFFSET(pImplChannel->epnum, -						MUSB_TXCSR), +				MUSB_EP_OFFSET(musb_channel->epnum, MUSB_TXCSR),  				csr);  		} else {  			csr = musb_readw(mbase, -				MUSB_EP_OFFSET(pImplChannel->epnum, +				MUSB_EP_OFFSET(musb_channel->epnum,  						MUSB_RXCSR));  			csr &= ~(MUSB_RXCSR_AUTOCLEAR |  				 MUSB_RXCSR_DMAENAB |  				 MUSB_RXCSR_DMAMODE);  			musb_writew(mbase, -				MUSB_EP_OFFSET(pImplChannel->epnum, -						MUSB_RXCSR), +				MUSB_EP_OFFSET(musb_channel->epnum, MUSB_RXCSR),  				csr);  		}  		musb_writew(mbase, -			MUSB_HSDMA_CHANNEL_OFFSET(bChannel, MUSB_HSDMA_CONTROL), +			MUSB_HSDMA_CHANNEL_OFFSET(bchannel, MUSB_HSDMA_CONTROL),  			0);  		musb_writel(mbase, -			MUSB_HSDMA_CHANNEL_OFFSET(bChannel, MUSB_HSDMA_ADDRESS), +			MUSB_HSDMA_CHANNEL_OFFSET(bchannel, MUSB_HSDMA_ADDRESS),  			0);  		musb_writel(mbase, -			MUSB_HSDMA_CHANNEL_OFFSET(bChannel, MUSB_HSDMA_COUNT), +			MUSB_HSDMA_CHANNEL_OFFSET(bchannel, MUSB_HSDMA_COUNT),  			0); -		pChannel->status = MUSB_DMA_STATUS_FREE; +		channel->status = MUSB_DMA_STATUS_FREE;  	} +  	return 0;  }  static irqreturn_t dma_controller_irq(int irq, void *private_data)  { -	struct musb_dma_controller *controller = -		(struct musb_dma_controller *)private_data; -	struct musb_dma_channel *pImplChannel; -	struct musb *musb = controller->pDmaPrivate; -	void __iomem *mbase = controller->pCoreBase; -	struct dma_channel *pChannel; -	u8 bChannel; -	u16 csr; -	u32 dwAddress; -	u8 int_hsdma; +	struct musb_dma_controller *controller = private_data; +	struct musb *musb = controller->private_data; +	struct musb_dma_channel *musb_channel; +	struct dma_channel *channel; + +	void __iomem *mbase = controller->base; +  	irqreturn_t retval = IRQ_NONE; +  	unsigned long flags; +	u8 bchannel; +	u8 int_hsdma; + +	u32 addr; +	u16 csr; +  	spin_lock_irqsave(&musb->lock, flags);  	int_hsdma = musb_readb(mbase, MUSB_HSDMA_INTR);  	if (!int_hsdma)  		goto done; -	for (bChannel = 0; bChannel < MUSB_HSDMA_CHANNELS; bChannel++) { -		if (int_hsdma & (1 << bChannel)) { -			pImplChannel = (struct musb_dma_channel *) -					&(controller->aChannel[bChannel]); -			pChannel = &pImplChannel->Channel; +	for (bchannel = 0; bchannel < MUSB_HSDMA_CHANNELS; bchannel++) { +		if (int_hsdma & (1 << bchannel)) { +			musb_channel = (struct musb_dma_channel *) +					&(controller->channel[bchannel]); +			channel = &musb_channel->channel;  			csr = musb_readw(mbase, -					MUSB_HSDMA_CHANNEL_OFFSET(bChannel, +					MUSB_HSDMA_CHANNEL_OFFSET(bchannel,  							MUSB_HSDMA_CONTROL)); -			if (csr & (1 << MUSB_HSDMA_BUSERROR_SHIFT)) -				pImplChannel->Channel.status = +			if (csr & (1 << MUSB_HSDMA_BUSERROR_SHIFT)) { +				musb_channel->channel.status =  					MUSB_DMA_STATUS_BUS_ABORT; -			else { +			} else {  				u8 devctl; -				dwAddress = musb_readl(mbase, +				addr = musb_readl(mbase,  						MUSB_HSDMA_CHANNEL_OFFSET( -							bChannel, +							bchannel,  							MUSB_HSDMA_ADDRESS)); -				pChannel->actual_len = dwAddress -					- pImplChannel->dwStartAddress; +				channel->actual_len = addr +					- musb_channel->start_addr;  				DBG(2, "ch %p, 0x%x -> 0x%x (%d / %d) %s\n", -					pChannel, pImplChannel->dwStartAddress, -					dwAddress, pChannel->actual_len, -					pImplChannel->len, -					(pChannel->actual_len -						< pImplChannel->len) ? +					channel, musb_channel->start_addr, +					addr, channel->actual_len, +					musb_channel->len, +					(channel->actual_len +						< musb_channel->len) ?  					"=> reconfig 0" : "=> complete");  				devctl = musb_readb(mbase, MUSB_DEVCTL); -				pChannel->status = MUSB_DMA_STATUS_FREE; +				channel->status = MUSB_DMA_STATUS_FREE;  				/* completed */  				if ((devctl & MUSB_DEVCTL_HM) -					&& (pImplChannel->transmit) -					&& ((pChannel->desired_mode == 0) -					    || (pChannel->actual_len & -					    (pImplChannel->wMaxPacketSize - 1))) +					&& (musb_channel->transmit) +					&& ((channel->desired_mode == 0) +					    || (channel->actual_len & +					    (musb_channel->max_packet_sz - 1)))  					 ) {  					/* Send out the packet */  					musb_ep_select(mbase, -						pImplChannel->epnum); +						musb_channel->epnum);  					musb_writew(mbase, MUSB_EP_OFFSET( -							pImplChannel->epnum, +							musb_channel->epnum,  							MUSB_TXCSR),  						MUSB_TXCSR_TXPKTRDY); -				} else +				} else {  					musb_dma_completion(  						musb, -						pImplChannel->epnum, -						pImplChannel->transmit); +						musb_channel->epnum, +						musb_channel->transmit); +				}  			}  		}  	} @@ -380,9 +383,9 @@ done:  void dma_controller_destroy(struct dma_controller *c)  { -	struct musb_dma_controller *controller; +	struct musb_dma_controller *controller = container_of(c, +			struct musb_dma_controller, controller); -	controller = container_of(c, struct musb_dma_controller, Controller);  	if (!controller)  		return; @@ -393,7 +396,7 @@ void dma_controller_destroy(struct dma_controller *c)  }  struct dma_controller *__init -dma_controller_create(struct musb *musb, void __iomem *pCoreBase) +dma_controller_create(struct musb *musb, void __iomem *base)  {  	struct musb_dma_controller *controller;  	struct device *dev = musb->controller; @@ -405,29 +408,30 @@ dma_controller_create(struct musb *musb, void __iomem *pCoreBase)  		return NULL;  	} -	controller = kzalloc(sizeof(struct musb_dma_controller), GFP_KERNEL); +	controller = kzalloc(sizeof(*controller), GFP_KERNEL);  	if (!controller)  		return NULL; -	controller->bChannelCount = MUSB_HSDMA_CHANNELS; -	controller->pDmaPrivate = musb; -	controller->pCoreBase = pCoreBase; +	controller->channel_count = MUSB_HSDMA_CHANNELS; +	controller->private_data = musb; +	controller->base = base; -	controller->Controller.start = dma_controller_start; -	controller->Controller.stop = dma_controller_stop; -	controller->Controller.channel_alloc = dma_channel_allocate; -	controller->Controller.channel_release = dma_channel_release; -	controller->Controller.channel_program = dma_channel_program; -	controller->Controller.channel_abort = dma_channel_abort; +	controller->controller.start = dma_controller_start; +	controller->controller.stop = dma_controller_stop; +	controller->controller.channel_alloc = dma_channel_allocate; +	controller->controller.channel_release = dma_channel_release; +	controller->controller.channel_program = dma_channel_program; +	controller->controller.channel_abort = dma_channel_abort;  	if (request_irq(irq, dma_controller_irq, IRQF_DISABLED, -			musb->controller->bus_id, &controller->Controller)) { +			musb->controller->bus_id, &controller->controller)) {  		dev_err(dev, "request_irq %d failed!\n", irq); -		dma_controller_destroy(&controller->Controller); +		dma_controller_destroy(&controller->controller); +  		return NULL;  	}  	controller->irq = irq; -	return &controller->Controller; +	return &controller->controller;  } diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c index 9d2dcb121c5..ce6c162920f 100644 --- a/drivers/usb/musb/omap2430.c +++ b/drivers/usb/musb/omap2430.c @@ -53,7 +53,9 @@ static void musb_do_idle(unsigned long _musb)  {  	struct musb	*musb = (void *)_musb;  	unsigned long	flags; +#ifdef CONFIG_USB_MUSB_HDRC_HCD  	u8	power; +#endif  	u8	devctl;  	devctl = musb_readb(musb->mregs, MUSB_DEVCTL); diff --git a/drivers/usb/musb/tusb6010.c b/drivers/usb/musb/tusb6010.c index b73b036f3d7..ee8fca92a4a 100644 --- a/drivers/usb/musb/tusb6010.c +++ b/drivers/usb/musb/tusb6010.c @@ -605,7 +605,7 @@ void musb_platform_set_mode(struct musb *musb, u8 musb_mode)  	if (musb->board_mode != MUSB_OTG) {  		ERR("Changing mode currently only supported in OTG mode\n"); -		return; +		return -EINVAL;  	}  	otg_stat = musb_readl(tbase, TUSB_DEV_OTG_STAT); diff --git a/drivers/usb/serial/aircable.c b/drivers/usb/serial/aircable.c index 79ea98c66fa..537f953bd7f 100644 --- a/drivers/usb/serial/aircable.c +++ b/drivers/usb/serial/aircable.c @@ -220,8 +220,8 @@ static void aircable_send(struct usb_serial_port *port)  	buf = kzalloc(count + HCI_HEADER_LENGTH, GFP_ATOMIC);  	if (!buf) { -		err("%s- kzalloc(%d) failed.", __func__, -		    count + HCI_HEADER_LENGTH); +		dev_err(&port->dev, "%s- kzalloc(%d) failed.\n", +			__func__, count + HCI_HEADER_LENGTH);  		return;  	} @@ -272,23 +272,24 @@ static void aircable_read(struct work_struct *work)  	 * 64 bytes, to ensure I do not get throttled.  	 * Ask USB mailing list for better aproach.  	 */ -	tty = port->port.tty; +	tty = tty_port_tty_get(&port->port);  	if (!tty) {  		schedule_work(&priv->rx_work); -		err("%s - No tty available", __func__); +		dev_err(&port->dev, "%s - No tty available\n", __func__);  		return ;  	}  	count = min(64, serial_buf_data_avail(priv->rx_buf));  	if (count <= 0) -		return; /* We have finished sending everything. */ +		goto out; /* We have finished sending everything. */  	tty_prepare_flip_string(tty, &data, count);  	if (!data) { -		err("%s- kzalloc(%d) failed.", __func__, count); -		return; +		dev_err(&port->dev, "%s- kzalloc(%d) failed.", +							__func__, count); +		goto out;  	}  	serial_buf_get(priv->rx_buf, data, count); @@ -297,7 +298,8 @@ static void aircable_read(struct work_struct *work)  	if (serial_buf_data_avail(priv->rx_buf))  		schedule_work(&priv->rx_work); - +out:		 +	tty_kref_put(tty);  	return;  }  /* End of private methods */ @@ -334,7 +336,7 @@ static int aircable_attach(struct usb_serial *serial)  	priv = kzalloc(sizeof(struct aircable_private), GFP_KERNEL);  	if (!priv) { -		err("%s- kmalloc(%Zd) failed.", __func__, +		dev_err(&port->dev, "%s- kmalloc(%Zd) failed.\n", __func__,  			sizeof(struct aircable_private));  		return -ENOMEM;  	} @@ -495,7 +497,7 @@ static void aircable_read_bulk_callback(struct urb *urb)  	usb_serial_debug_data(debug, &port->dev, __func__,  				urb->actual_length, urb->transfer_buffer); -	tty = port->port.tty; +	tty = tty_port_tty_get(&port->port);  	if (tty && urb->actual_length) {  		if (urb->actual_length <= 2) {  			/* This is an incomplete package */ @@ -527,6 +529,7 @@ static void aircable_read_bulk_callback(struct urb *urb)  		}  		aircable_read(&priv->rx_work);  	} +	tty_kref_put(tty);  	/* Schedule the next read _if_ we are still open */  	if (port->port.count) { diff --git a/drivers/usb/serial/belkin_sa.c b/drivers/usb/serial/belkin_sa.c index 2ebe06c3405..b7eacad4d48 100644 --- a/drivers/usb/serial/belkin_sa.c +++ b/drivers/usb/serial/belkin_sa.c @@ -187,7 +187,7 @@ static int belkin_sa_startup(struct usb_serial *serial)  	/* see comments at top of file */  	priv->bad_flow_control =  		(le16_to_cpu(dev->descriptor.bcdDevice) <= 0x0206) ? 1 : 0; -	info("bcdDevice: %04x, bfc: %d", +	dev_info(&dev->dev, "bcdDevice: %04x, bfc: %d\n",  					le16_to_cpu(dev->descriptor.bcdDevice),  					priv->bad_flow_control); @@ -228,7 +228,7 @@ static int  belkin_sa_open(struct tty_struct *tty,  	port->read_urb->dev = port->serial->dev;  	retval = usb_submit_urb(port->read_urb, GFP_KERNEL);  	if (retval) { -		err("usb_submit_urb(read bulk) failed"); +		dev_err(&port->dev, "usb_submit_urb(read bulk) failed\n");  		goto exit;  	} @@ -236,7 +236,7 @@ static int  belkin_sa_open(struct tty_struct *tty,  	retval = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);  	if (retval) {  		usb_kill_urb(port->read_urb); -		err(" usb_submit_urb(read int) failed"); +		dev_err(&port->dev, "usb_submit_urb(read int) failed\n");  	}  exit: @@ -322,7 +322,7 @@ static void belkin_sa_read_int_callback(struct urb *urb)  	 * to look in to this before committing any code.  	 */  	if (priv->last_lsr & BELKIN_SA_LSR_ERR) { -		tty = port->port.tty; +		tty = tty_port_tty_get(&port->port);  		/* Overrun Error */  		if (priv->last_lsr & BELKIN_SA_LSR_OE) {  		} @@ -335,14 +335,15 @@ static void belkin_sa_read_int_callback(struct urb *urb)  		/* Break Indicator */  		if (priv->last_lsr & BELKIN_SA_LSR_BI) {  		} +		tty_kref_put(tty);  	}  #endif  	spin_unlock_irqrestore(&priv->lock, flags);  exit:  	retval = usb_submit_urb(urb, GFP_ATOMIC);  	if (retval) -		err("%s - usb_submit_urb failed with result %d", -		     __func__, retval); +		dev_err(&port->dev, "%s - usb_submit_urb failed with " +			"result %d\n", __func__, retval);  }  static void belkin_sa_set_termios(struct tty_struct *tty, @@ -381,12 +382,12 @@ static void belkin_sa_set_termios(struct tty_struct *tty,  		if ((old_cflag & CBAUD) == B0) {  			control_state |= (TIOCM_DTR|TIOCM_RTS);  			if (BSA_USB_CMD(BELKIN_SA_SET_DTR_REQUEST, 1) < 0) -				err("Set DTR error"); +				dev_err(&port->dev, "Set DTR error\n");  			/* don't set RTS if using hardware flow control */  			if (!(old_cflag & CRTSCTS))  				if (BSA_USB_CMD(BELKIN_SA_SET_RTS_REQUEST  								, 1) < 0) -					err("Set RTS error"); +					dev_err(&port->dev, "Set RTS error\n");  		}  	} @@ -402,18 +403,18 @@ static void belkin_sa_set_termios(struct tty_struct *tty,  		/* Report the actual baud rate back to the caller */  		tty_encode_baud_rate(tty, baud, baud);  		if (BSA_USB_CMD(BELKIN_SA_SET_BAUDRATE_REQUEST, urb_value) < 0) -			err("Set baudrate error"); +			dev_err(&port->dev, "Set baudrate error\n");  	} else {  		/* Disable flow control */  		if (BSA_USB_CMD(BELKIN_SA_SET_FLOW_CTRL_REQUEST,  						BELKIN_SA_FLOW_NONE) < 0) -			err("Disable flowcontrol error"); +			dev_err(&port->dev, "Disable flowcontrol error\n");  		/* Drop RTS and DTR */  		control_state &= ~(TIOCM_DTR | TIOCM_RTS);  		if (BSA_USB_CMD(BELKIN_SA_SET_DTR_REQUEST, 0) < 0) -			err("DTR LOW error"); +			dev_err(&port->dev, "DTR LOW error\n");  		if (BSA_USB_CMD(BELKIN_SA_SET_RTS_REQUEST, 0) < 0) -			err("RTS LOW error"); +			dev_err(&port->dev, "RTS LOW error\n");  	}  	/* set the parity */ @@ -424,7 +425,7 @@ static void belkin_sa_set_termios(struct tty_struct *tty,  		else  			urb_value = BELKIN_SA_PARITY_NONE;  		if (BSA_USB_CMD(BELKIN_SA_SET_PARITY_REQUEST, urb_value) < 0) -			err("Set parity error"); +			dev_err(&port->dev, "Set parity error\n");  	}  	/* set the number of data bits */ @@ -447,7 +448,7 @@ static void belkin_sa_set_termios(struct tty_struct *tty,  			break;  		}  		if (BSA_USB_CMD(BELKIN_SA_SET_DATA_BITS_REQUEST, urb_value) < 0) -			err("Set data bits error"); +			dev_err(&port->dev, "Set data bits error\n");  	}  	/* set the number of stop bits */ @@ -456,7 +457,7 @@ static void belkin_sa_set_termios(struct tty_struct *tty,  						: BELKIN_SA_STOP_BITS(1);  		if (BSA_USB_CMD(BELKIN_SA_SET_STOP_BITS_REQUEST,  							urb_value) < 0) -			err("Set stop bits error"); +			dev_err(&port->dev, "Set stop bits error\n");  	}  	/* Set flow control */ @@ -477,7 +478,7 @@ static void belkin_sa_set_termios(struct tty_struct *tty,  			urb_value &= ~(BELKIN_SA_FLOW_IRTS);  		if (BSA_USB_CMD(BELKIN_SA_SET_FLOW_CTRL_REQUEST, urb_value) < 0) -			err("Set flow control error"); +			dev_err(&port->dev, "Set flow control error\n");  	}  	/* save off the modified port settings */ @@ -493,7 +494,7 @@ static void belkin_sa_break_ctl(struct tty_struct *tty, int break_state)  	struct usb_serial *serial = port->serial;  	if (BSA_USB_CMD(BELKIN_SA_SET_BREAK_REQUEST, break_state ? 1 : 0) < 0) -		err("Set break_ctl %d", break_state); +		dev_err(&port->dev, "Set break_ctl %d\n", break_state);  } @@ -553,13 +554,13 @@ static int belkin_sa_tiocmset(struct tty_struct *tty, struct file *file,  	retval = BSA_USB_CMD(BELKIN_SA_SET_RTS_REQUEST, rts);  	if (retval < 0) { -		err("Set RTS error %d", retval); +		dev_err(&port->dev, "Set RTS error %d\n", retval);  		goto exit;  	}  	retval = BSA_USB_CMD(BELKIN_SA_SET_DTR_REQUEST, dtr);  	if (retval < 0) { -		err("Set DTR error %d", retval); +		dev_err(&port->dev, "Set DTR error %d\n", retval);  		goto exit;  	}  exit: @@ -576,7 +577,8 @@ static int __init belkin_sa_init(void)  	retval = usb_register(&belkin_driver);  	if (retval)  		goto failed_usb_register; -	info(DRIVER_DESC " " DRIVER_VERSION); +	printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":" +	       DRIVER_DESC "\n");  	return 0;  failed_usb_register:  	usb_serial_deregister(&belkin_device); diff --git a/drivers/usb/serial/console.c b/drivers/usb/serial/console.c index e980766bb84..5b20de130e0 100644 --- a/drivers/usb/serial/console.c +++ b/drivers/usb/serial/console.c @@ -117,7 +117,7 @@ static int usb_console_setup(struct console *co, char *options)  	}  	port = serial->port[0]; -	port->port.tty = NULL; +	tty_port_tty_set(&port->port, NULL);  	info->port = port; @@ -143,7 +143,7 @@ static int usb_console_setup(struct console *co, char *options)  			}  			memset(&dummy, 0, sizeof(struct ktermios));  			tty->termios = termios; -			port->port.tty = tty; +			tty_port_tty_set(&port->port, tty);  		}  		/* only call the device specific open if this @@ -163,7 +163,7 @@ static int usb_console_setup(struct console *co, char *options)  			tty_termios_encode_baud_rate(termios, baud, baud);  			serial->type->set_termios(tty, port, &dummy); -			port->port.tty = NULL; +			tty_port_tty_set(&port->port, NULL);  			kfree(termios);  			kfree(tty);  		} @@ -176,7 +176,7 @@ out:  	return retval;  free_termios:  	kfree(termios); -	port->port.tty = NULL; +	tty_port_tty_set(&port->port, NULL);  free_tty:  	kfree(tty);  reset_open_count: diff --git a/drivers/usb/serial/cp2101.c b/drivers/usb/serial/cp2101.c index 1279553381e..9035d7256b0 100644 --- a/drivers/usb/serial/cp2101.c +++ b/drivers/usb/serial/cp2101.c @@ -67,6 +67,7 @@ static struct usb_device_id id_table [] = {  	{ USB_DEVICE(0x10C4, 0x800A) }, /* SPORTident BSM7-D-USB main station */  	{ USB_DEVICE(0x10C4, 0x803B) }, /* Pololu USB-serial converter */  	{ USB_DEVICE(0x10C4, 0x8053) }, /* Enfora EDG1228 */ +	{ USB_DEVICE(0x10C4, 0x8054) }, /* Enfora GSM2228 */  	{ USB_DEVICE(0x10C4, 0x8066) }, /* Argussoft In-System Programmer */  	{ USB_DEVICE(0x10C4, 0x807A) }, /* Crumb128 board */  	{ USB_DEVICE(0x10C4, 0x80CA) }, /* Degree Controls Inc */ @@ -85,6 +86,7 @@ static struct usb_device_id id_table [] = {  	{ USB_DEVICE(0x10C4, 0x8218) }, /* Lipowsky Industrie Elektronik GmbH, HARP-1 */  	{ USB_DEVICE(0x10c4, 0x8293) }, /* Telegesys ETRX2USB */  	{ USB_DEVICE(0x10C4, 0x8341) }, /* Siemens MC35PU GPRS Modem */ +	{ USB_DEVICE(0x10C4, 0x83A8) }, /* Amber Wireless AMB2560 */  	{ USB_DEVICE(0x10C4, 0xEA60) }, /* Silicon Labs factory default */  	{ USB_DEVICE(0x10C4, 0xEA61) }, /* Silicon Labs factory default */  	{ USB_DEVICE(0x10C4, 0xF001) }, /* Elan Digital Systems USBscope50 */ @@ -753,7 +755,8 @@ static int __init cp2101_init(void)  	}  	/* Success */ -	info(DRIVER_DESC " " DRIVER_VERSION); +	printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":" +	       DRIVER_DESC "\n");  	return 0;  } diff --git a/drivers/usb/serial/cyberjack.c b/drivers/usb/serial/cyberjack.c index b4d72351cb9..858bdd038fb 100644 --- a/drivers/usb/serial/cyberjack.c +++ b/drivers/usb/serial/cyberjack.c @@ -141,7 +141,8 @@ static int cyberjack_startup(struct usb_serial *serial)  		result = usb_submit_urb(serial->port[i]->interrupt_in_urb,  					GFP_KERNEL);  		if (result) -			err(" usb_submit_urb(read int) failed"); +			dev_err(&serial->dev->dev, +				"usb_submit_urb(read int) failed\n");  		dbg("%s - usb_submit_urb(int urb)", __func__);  	} @@ -274,8 +275,9 @@ static int cyberjack_write(struct tty_struct *tty,  		/* send the data out the bulk port */  		result = usb_submit_urb(port->write_urb, GFP_ATOMIC);  		if (result) { -			err("%s - failed submitting write urb, error %d", -							__func__, result); +			dev_err(&port->dev, +				"%s - failed submitting write urb, error %d", +				__func__, result);  			/* Throw away data. No better idea what to do with it. */  			priv->wrfilled = 0;  			priv->wrsent = 0; @@ -351,7 +353,9 @@ static void cyberjack_read_int_callback(struct urb *urb)  			port->read_urb->dev = port->serial->dev;  			result = usb_submit_urb(port->read_urb, GFP_ATOMIC);  			if (result) -				err("%s - failed resubmitting read urb, error %d", __func__, result); +				dev_err(&port->dev, "%s - failed resubmitting " +					"read urb, error %d\n", +					__func__, result);  			dbg("%s - usb_submit_urb(read urb)", __func__);  		}  	} @@ -360,7 +364,7 @@ resubmit:  	port->interrupt_in_urb->dev = port->serial->dev;  	result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC);  	if (result) -		err(" usb_submit_urb(read int) failed"); +		dev_err(&port->dev, "usb_submit_urb(read int) failed\n");  	dbg("%s - usb_submit_urb(int urb)", __func__);  } @@ -384,7 +388,7 @@ static void cyberjack_read_bulk_callback(struct urb *urb)  		return;  	} -	tty = port->port.tty; +	tty = tty_port_tty_get(&port->port);  	if (!tty) {  		dbg("%s - ignoring since device not open\n", __func__);  		return; @@ -394,6 +398,7 @@ static void cyberjack_read_bulk_callback(struct urb *urb)  		tty_insert_flip_string(tty, data, urb->actual_length);  		tty_flip_buffer_push(tty);  	} +	tty_kref_put(tty);  	spin_lock(&priv->lock); @@ -413,8 +418,8 @@ static void cyberjack_read_bulk_callback(struct urb *urb)  		port->read_urb->dev = port->serial->dev;  		result = usb_submit_urb(port->read_urb, GFP_ATOMIC);  		if (result) -			err("%s - failed resubmitting read urb, error %d", -				__func__, result); +			dev_err(&port->dev, "%s - failed resubmitting read " +				"urb, error %d\n", __func__, result);  		dbg("%s - usb_submit_urb(read urb)", __func__);  	}  } @@ -461,8 +466,9 @@ static void cyberjack_write_bulk_callback(struct urb *urb)  		/* send the data out the bulk port */  		result = usb_submit_urb(port->write_urb, GFP_ATOMIC);  		if (result) { -			err("%s - failed submitting write urb, error %d", -								__func__, result); +			dev_err(&port->dev, +				"%s - failed submitting write urb, error %d\n", +				__func__, result);  			/* Throw away data. No better idea what to do with it. */  			priv->wrfilled = 0;  			priv->wrsent = 0; @@ -498,8 +504,9 @@ static int __init cyberjack_init(void)  	if (retval)  		goto failed_usb_register; -	info(DRIVER_VERSION " " DRIVER_AUTHOR); -	info(DRIVER_DESC); +	printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION " " +	       DRIVER_AUTHOR "\n"); +	printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_DESC "\n");  	return 0;  failed_usb_register: diff --git a/drivers/usb/serial/cypress_m8.c b/drivers/usb/serial/cypress_m8.c index 22837a3f2f8..eae4740d448 100644 --- a/drivers/usb/serial/cypress_m8.c +++ b/drivers/usb/serial/cypress_m8.c @@ -404,8 +404,8 @@ static int cypress_serial_control(struct tty_struct *tty,  			 retval != -ENODEV);  		if (retval != sizeof(feature_buffer)) { -			err("%s - failed sending serial line settings - %d", -							__func__, retval); +			dev_err(&port->dev, "%s - failed sending serial " +				"line settings - %d\n", __func__, retval);  			cypress_set_dead(port);  		} else {  			spin_lock_irqsave(&priv->lock, flags); @@ -443,7 +443,8 @@ static int cypress_serial_control(struct tty_struct *tty,  						&& retval != -ENODEV);  		if (retval != sizeof(feature_buffer)) { -			err("%s - failed to retrieve serial line settings - %d", __func__, retval); +			dev_err(&port->dev, "%s - failed to retrieve serial " +				"line settings - %d\n", __func__, retval);  			cypress_set_dead(port);  			return retval;  		} else { @@ -476,8 +477,8 @@ static void cypress_set_dead(struct usb_serial_port *port)  	priv->comm_is_ok = 0;  	spin_unlock_irqrestore(&priv->lock, flags); -	err("cypress_m8 suspending failing port %d - interval might be too short", -	    port->number); +	dev_err(&port->dev, "cypress_m8 suspending failing port %d - " +		"interval might be too short\n", port->number);  } @@ -679,7 +680,8 @@ static int cypress_open(struct tty_struct *tty,  	/* setup the port and start reading from the device */  	if (!port->interrupt_in_urb) { -		err("%s - interrupt_in_urb is empty!", __func__); +		dev_err(&port->dev, "%s - interrupt_in_urb is empty!\n", +			__func__);  		return -1;  	} @@ -1107,8 +1109,8 @@ static void cypress_set_termios(struct tty_struct *tty,  		data_bits = 3;  		break;  	default: -		err("%s - CSIZE was set, but not CS5-CS8", -				__func__); +		dev_err(&port->dev, "%s - CSIZE was set, but not CS5-CS8\n", +			__func__);  		data_bits = 3;  	}  	spin_lock_irqsave(&priv->lock, flags); @@ -1286,7 +1288,7 @@ static void cypress_read_int_callback(struct urb *urb)  	}  	spin_unlock_irqrestore(&priv->lock, flags); -	tty = port->port.tty; +	tty = tty_port_tty_get(&port->port);  	if (!tty) {  		dbg("%s - bad tty pointer - exiting", __func__);  		return; @@ -1362,7 +1364,7 @@ static void cypress_read_int_callback(struct urb *urb)  					data[i]);  			tty_insert_flip_char(tty, data[i], tty_flag);  		} -		tty_flip_buffer_push(port->port.tty); +		tty_flip_buffer_push(tty);  	}  	spin_lock_irqsave(&priv->lock, flags); @@ -1371,6 +1373,7 @@ static void cypress_read_int_callback(struct urb *urb)  	spin_unlock_irqrestore(&priv->lock, flags);  continue_read: +	tty_kref_put(tty);  	/* Continue trying to always read... unless the port has closed. */ @@ -1657,7 +1660,8 @@ static int __init cypress_init(void)  	if (retval)  		goto failed_usb_register; -	info(DRIVER_DESC " " DRIVER_VERSION); +	printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":" +	       DRIVER_DESC "\n");  	return 0;  failed_usb_register: diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c index 240aad1acaa..69f84f0ea6f 100644 --- a/drivers/usb/serial/digi_acceleport.c +++ b/drivers/usb/serial/digi_acceleport.c @@ -604,7 +604,9 @@ static void digi_wakeup_write_lock(struct work_struct *work)  static void digi_wakeup_write(struct usb_serial_port *port)  { -	tty_wakeup(port->port.tty); +	struct tty_struct *tty = tty_port_tty_get(&port->port); +	tty_wakeup(tty); +	tty_kref_put(tty);  } @@ -659,7 +661,8 @@ static int digi_write_oob_command(struct usb_serial_port *port,  	}  	spin_unlock_irqrestore(&oob_priv->dp_port_lock, flags);  	if (ret) -		err("%s: usb_submit_urb failed, ret=%d", __func__, ret); +		dev_err(&port->dev, "%s: usb_submit_urb failed, ret=%d\n", +			__func__, ret);  	return ret;  } @@ -741,7 +744,8 @@ static int digi_write_inb_command(struct usb_serial_port *port,  	spin_unlock_irqrestore(&priv->dp_port_lock, flags);  	if (ret) -		err("%s: usb_submit_urb failed, ret=%d, port=%d", +		dev_err(&port->dev, +			"%s: usb_submit_urb failed, ret=%d, port=%d\n",  			__func__, ret, priv->dp_port_num);  	return ret;  } @@ -810,7 +814,8 @@ static int digi_set_modem_signals(struct usb_serial_port *port,  	spin_unlock(&port_priv->dp_port_lock);  	spin_unlock_irqrestore(&oob_priv->dp_port_lock, flags);  	if (ret) -		err("%s: usb_submit_urb failed, ret=%d", __func__, ret); +		dev_err(&port->dev, "%s: usb_submit_urb failed, ret=%d\n", +			__func__, ret);  	return ret;  } @@ -905,7 +910,8 @@ static void digi_rx_unthrottle(struct tty_struct *tty)  	spin_unlock_irqrestore(&priv->dp_port_lock, flags);  	if (ret) -		err("%s: usb_submit_urb failed, ret=%d, port=%d", +		dev_err(&port->dev, +			"%s: usb_submit_urb failed, ret=%d, port=%d\n",  			__func__, ret, priv->dp_port_num);  } @@ -1212,7 +1218,8 @@ static int digi_write(struct tty_struct *tty, struct usb_serial_port *port,  	/* return length of new data written, or error */  	spin_unlock_irqrestore(&priv->dp_port_lock, flags);  	if (ret < 0) -		err("%s: usb_submit_urb failed, ret=%d, port=%d", +		dev_err(&port->dev, +			"%s: usb_submit_urb failed, ret=%d, port=%d\n",  			__func__, ret, priv->dp_port_num);  	dbg("digi_write: returning %d", ret);  	return ret; @@ -1233,14 +1240,16 @@ static void digi_write_bulk_callback(struct urb *urb)  	/* port and serial sanity check */  	if (port == NULL || (priv = usb_get_serial_port_data(port)) == NULL) { -		err("%s: port or port->private is NULL, status=%d", -		    __func__, status); +		dev_err(&port->dev, +			"%s: port or port->private is NULL, status=%d\n", +			__func__, status);  		return;  	}  	serial = port->serial;  	if (serial == NULL || (serial_priv = usb_get_serial_data(serial)) == NULL) { -		err("%s: serial or serial->private is NULL, status=%d", -		    __func__, status); +		dev_err(&port->dev, +			"%s: serial or serial->private is NULL, status=%d\n", +			__func__, status);  		return;  	} @@ -1282,7 +1291,8 @@ static void digi_write_bulk_callback(struct urb *urb)  	spin_unlock(&priv->dp_port_lock);  	if (ret) -		err("%s: usb_submit_urb failed, ret=%d, port=%d", +		dev_err(&port->dev, +			"%s: usb_submit_urb failed, ret=%d, port=%d\n",  			__func__, ret, priv->dp_port_num);  } @@ -1516,8 +1526,9 @@ static int digi_startup_device(struct usb_serial *serial)  		port->write_urb->dev = port->serial->dev;  		ret = usb_submit_urb(port->read_urb, GFP_KERNEL);  		if (ret != 0) { -			err("%s: usb_submit_urb failed, ret=%d, port=%d", -					__func__, ret, i); +			dev_err(&port->dev, +				"%s: usb_submit_urb failed, ret=%d, port=%d\n", +				__func__, ret, i);  			break;  		}  	} @@ -1616,22 +1627,26 @@ static void digi_read_bulk_callback(struct urb *urb)  	dbg("digi_read_bulk_callback: TOP");  	/* port sanity check, do not resubmit if port is not valid */ -	if (port == NULL || (priv = usb_get_serial_port_data(port)) == NULL) { -		err("%s: port or port->private is NULL, status=%d", -		    __func__, status); +	if (port == NULL) +		return; +	priv = usb_get_serial_port_data(port); +	if (priv == NULL) { +		dev_err(&port->dev, "%s: port->private is NULL, status=%d\n", +			__func__, status);  		return;  	}  	if (port->serial == NULL ||  		(serial_priv = usb_get_serial_data(port->serial)) == NULL) { -		err("%s: serial is bad or serial->private is NULL, status=%d", -			__func__, status); +		dev_err(&port->dev, "%s: serial is bad or serial->private " +			"is NULL, status=%d\n", __func__, status);  		return;  	}  	/* do not resubmit urb if it has any status error */  	if (status) { -		err("%s: nonzero read bulk status: status=%d, port=%d", -		    __func__, status, priv->dp_port_num); +		dev_err(&port->dev, +			"%s: nonzero read bulk status: status=%d, port=%d\n", +			__func__, status, priv->dp_port_num);  		return;  	} @@ -1648,8 +1663,9 @@ static void digi_read_bulk_callback(struct urb *urb)  	urb->dev = port->serial->dev;  	ret = usb_submit_urb(urb, GFP_ATOMIC);  	if (ret != 0) { -		err("%s: failed resubmitting urb, ret=%d, port=%d", -		    __func__, ret, priv->dp_port_num); +		dev_err(&port->dev, +			"%s: failed resubmitting urb, ret=%d, port=%d\n", +			__func__, ret, priv->dp_port_num);  	}  } @@ -1668,7 +1684,7 @@ static int digi_read_inb_callback(struct urb *urb)  {  	struct usb_serial_port *port = urb->context; -	struct tty_struct *tty = port->port.tty; +	struct tty_struct *tty;  	struct digi_port *priv = usb_get_serial_port_data(port);  	int opcode = ((unsigned char *)urb->transfer_buffer)[0];  	int len = ((unsigned char *)urb->transfer_buffer)[1]; @@ -1685,13 +1701,15 @@ static int digi_read_inb_callback(struct urb *urb)  	/* short/multiple packet check */  	if (urb->actual_length != len + 2) { -		err("%s: INCOMPLETE OR MULTIPLE PACKET, urb->status=%d, " -		    "port=%d, opcode=%d, len=%d, actual_length=%d, " -		    "status=%d", __func__, status, priv->dp_port_num, -		    opcode, len, urb->actual_length, port_status); +		dev_err(&port->dev, "%s: INCOMPLETE OR MULTIPLE PACKET, " +			"urb->status=%d, port=%d, opcode=%d, len=%d, " +			"actual_length=%d, status=%d\n", __func__, status, +			priv->dp_port_num, opcode, len, urb->actual_length, +			port_status);  		return -1;  	} +	tty = tty_port_tty_get(&port->port);  	spin_lock(&priv->dp_port_lock);  	/* check for throttle; if set, do not resubmit read urb */ @@ -1735,6 +1753,7 @@ static int digi_read_inb_callback(struct urb *urb)  		}  	}  	spin_unlock(&priv->dp_port_lock); +	tty_kref_put(tty);  	if (opcode == DIGI_CMD_RECEIVE_DISABLE)  		dbg("%s: got RECEIVE_DISABLE", __func__); @@ -1760,6 +1779,7 @@ static int digi_read_oob_callback(struct urb *urb)  	struct usb_serial_port *port = urb->context;  	struct usb_serial *serial = port->serial; +	struct tty_struct *tty;  	struct digi_port *priv = usb_get_serial_port_data(port);  	int opcode, line, status, val;  	int i; @@ -1787,10 +1807,11 @@ static int digi_read_oob_callback(struct urb *urb)  		if (priv == NULL)  			return -1; +		tty = tty_port_tty_get(&port->port);  		rts = 0;  		if (port->port.count) -			rts = port->port.tty->termios->c_cflag & CRTSCTS; - +			rts = tty->termios->c_cflag & CRTSCTS; +		  		if (opcode == DIGI_CMD_READ_INPUT_SIGNALS) {  			spin_lock(&priv->dp_port_lock);  			/* convert from digi flags to termiox flags */ @@ -1798,14 +1819,14 @@ static int digi_read_oob_callback(struct urb *urb)  				priv->dp_modem_signals |= TIOCM_CTS;  				/* port must be open to use tty struct */  				if (rts) { -					port->port.tty->hw_stopped = 0; +					tty->hw_stopped = 0;  					digi_wakeup_write(port);  				}  			} else {  				priv->dp_modem_signals &= ~TIOCM_CTS;  				/* port must be open to use tty struct */  				if (rts) -					port->port.tty->hw_stopped = 1; +					tty->hw_stopped = 1;  			}  			if (val & DIGI_READ_INPUT_SIGNALS_DSR)  				priv->dp_modem_signals |= TIOCM_DSR; @@ -1830,6 +1851,7 @@ static int digi_read_oob_callback(struct urb *urb)  		} else if (opcode == DIGI_CMD_IFLUSH_FIFO) {  			wake_up_interruptible(&priv->dp_flush_wait);  		} +		tty_kref_put(tty);  	}  	return 0; @@ -1847,7 +1869,8 @@ static int __init digi_init(void)  	retval = usb_register(&digi_driver);  	if (retval)  		goto failed_usb_register; -	info(DRIVER_VERSION ":" DRIVER_DESC); +	printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":" +	       DRIVER_DESC "\n");  	return 0;  failed_usb_register:  	usb_serial_deregister(&digi_acceleport_4_device); diff --git a/drivers/usb/serial/empeg.c b/drivers/usb/serial/empeg.c index a6ab5b58d9c..8a69cce40b6 100644 --- a/drivers/usb/serial/empeg.c +++ b/drivers/usb/serial/empeg.c @@ -33,9 +33,8 @@   *	Moved MOD_DEC_USE_COUNT to end of empeg_close().   *   * (12/03/2000) gb - *	Added port->port.tty->ldisc.set_termios(port->port.tty, NULL) to - *	empeg_open(). This notifies the tty driver that the termios have - *	changed. + *	Added tty->ldisc.set_termios(port, tty, NULL) to empeg_open(). + *	This notifies the tty driver that the termios have changed.   *   * (11/13/2000) gb   *	Moved tty->low_latency = 1 from empeg_read_bulk_callback() to @@ -354,7 +353,7 @@ static void empeg_read_bulk_callback(struct urb *urb)  	usb_serial_debug_data(debug, &port->dev, __func__,  						urb->actual_length, data); -	tty = port->port.tty; +	tty = tty_port_tty_get(&port->port);  	if (urb->actual_length) {  		tty_buffer_request_room(tty, urb->actual_length); @@ -362,6 +361,7 @@ static void empeg_read_bulk_callback(struct urb *urb)  		tty_flip_buffer_push(tty);  		bytes_in += urb->actual_length;  	} +	tty_kref_put(tty);  	/* Continue trying to always read  */  	usb_fill_bulk_urb( @@ -416,7 +416,7 @@ static int  empeg_startup(struct usb_serial *serial)  	dbg("%s", __func__);  	if (serial->dev->actconfig->desc.bConfigurationValue != 1) { -		err("active config #%d != 1 ??", +		dev_err(&serial->dev->dev, "active config #%d != 1 ??\n",  			serial->dev->actconfig->desc.bConfigurationValue);  		return -ENODEV;  	} @@ -499,15 +499,15 @@ static int __init empeg_init(void)  		urb = usb_alloc_urb(0, GFP_KERNEL);  		write_urb_pool[i] = urb;  		if (urb == NULL) { -			err("No more urbs???"); +			printk(KERN_ERR "empeg: No more urbs???\n");  			continue;  		}  		urb->transfer_buffer = kmalloc(URB_TRANSFER_BUFFER_SIZE,  								GFP_KERNEL);  		if (!urb->transfer_buffer) { -			err("%s - out of memory for urb buffers.", -			    __func__); +			printk(KERN_ERR "empeg: %s - out of memory for urb " +			       "buffers.", __func__);  			continue;  		}  	} @@ -519,7 +519,8 @@ static int __init empeg_init(void)  	if (retval)  		goto failed_usb_register; -	info(DRIVER_VERSION ":" DRIVER_DESC); +	printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":" +	       DRIVER_DESC "\n");  	return 0;  failed_usb_register: diff --git a/drivers/usb/serial/ezusb.c b/drivers/usb/serial/ezusb.c index 711e84f6ed8..3cfc762f505 100644 --- a/drivers/usb/serial/ezusb.c +++ b/drivers/usb/serial/ezusb.c @@ -28,7 +28,8 @@ int ezusb_writememory(struct usb_serial *serial, int address,  	/* dbg("ezusb_writememory %x, %d", address, length); */  	if (!serial->dev) { -		err("%s - no physical device present, failing.", __func__); +		printk(KERN_ERR "ezusb: %s - no physical device present, " +		       "failing.\n", __func__);  		return -ENODEV;  	} diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index 3dc93b542b3..51d7bdea286 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -578,6 +578,7 @@ static struct usb_device_id id_table_combined [] = {  	{ USB_DEVICE(FALCOM_VID, FALCOM_TWIST_PID) },  	{ USB_DEVICE(FALCOM_VID, FALCOM_SAMBA_PID) },  	{ USB_DEVICE(FTDI_VID, FTDI_SUUNTO_SPORTS_PID) }, +	{ USB_DEVICE(FTDI_VID, FTDI_OCEANIC_PID) },  	{ USB_DEVICE(TTI_VID, TTI_QL355P_PID) },  	{ USB_DEVICE(FTDI_VID, FTDI_RM_CANVIEW_PID) },  	{ USB_DEVICE(BANDB_VID, BANDB_USOTL4_PID) }, @@ -860,7 +861,7 @@ static int update_mctrl(struct usb_serial_port *port, unsigned int set,  	kfree(buf);  	if (rv < 0) { -		err("%s Error from MODEM_CTRL urb: DTR %s, RTS %s", +		dbg("%s Error from MODEM_CTRL urb: DTR %s, RTS %s",  				__func__,  				(set & TIOCM_DTR) ? "HIGH" :  				(clear & TIOCM_DTR) ? "LOW" : "unchanged", @@ -1153,7 +1154,7 @@ static void ftdi_determine_type(struct usb_serial_port *port)  		/* Assume its an FT232R  */  		priv->chip_type = FT232RL;  	} -	info("Detected %s", ftdi_chip_name[priv->chip_type]); +	dev_info(&udev->dev, "Detected %s\n", ftdi_chip_name[priv->chip_type]);  } @@ -1326,7 +1327,7 @@ static int ftdi_sio_port_probe(struct usb_serial_port *port)  	priv = kzalloc(sizeof(struct ftdi_private), GFP_KERNEL);  	if (!priv) { -		err("%s- kmalloc(%Zd) failed.", __func__, +		dev_err(&port->dev, "%s- kmalloc(%Zd) failed.\n", __func__,  					sizeof(struct ftdi_private));  		return -ENOMEM;  	} @@ -1409,7 +1410,8 @@ static int ftdi_jtag_probe(struct usb_serial *serial)  	dbg("%s", __func__);  	if (interface == udev->actconfig->interface[0]) { -		info("Ignoring serial port reserved for JTAG"); +		dev_info(&udev->dev, +			 "Ignoring serial port reserved for JTAG\n");  		return -ENODEV;  	} @@ -1427,7 +1429,8 @@ static int ftdi_mtxorb_hack_setup(struct usb_serial *serial)  	if (ep->enabled && ep_desc->wMaxPacketSize == 0) {  		ep_desc->wMaxPacketSize = cpu_to_le16(0x40); -		info("Fixing invalid wMaxPacketSize on read pipe"); +		dev_info(&serial->dev->dev, +			 "Fixing invalid wMaxPacketSize on read pipe\n");  	}  	return 0; @@ -1521,8 +1524,9 @@ static int ftdi_open(struct tty_struct *tty,  			ftdi_read_bulk_callback, port);  	result = usb_submit_urb(port->read_urb, GFP_KERNEL);  	if (result) -		err("%s - failed submitting read urb, error %d", -							__func__, result); +		dev_err(&port->dev, +			"%s - failed submitting read urb, error %d\n", +			__func__, result);  	return result; @@ -1556,7 +1560,7 @@ static void ftdi_close(struct tty_struct *tty,  				    FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE,  				    0, priv->interface, buf, 0,  				    WDR_TIMEOUT) < 0) { -			err("error from flowcontrol urb"); +			dev_err(&port->dev, "error from flowcontrol urb\n");  		}  		/* drop RTS and DTR */ @@ -1621,14 +1625,15 @@ static int ftdi_write(struct tty_struct *tty, struct usb_serial_port *port,  	buffer = kmalloc(transfer_size, GFP_ATOMIC);  	if (!buffer) { -		err("%s ran out of kernel memory for urb ...", __func__); +		dev_err(&port->dev, +			"%s ran out of kernel memory for urb ...\n", __func__);  		count = -ENOMEM;  		goto error_no_buffer;  	}  	urb = usb_alloc_urb(0, GFP_ATOMIC);  	if (!urb) { -		err("%s - no more free urbs", __func__); +		dev_err(&port->dev, "%s - no more free urbs\n", __func__);  		count = -ENOMEM;  		goto error_no_urb;  	} @@ -1672,8 +1677,9 @@ static int ftdi_write(struct tty_struct *tty, struct usb_serial_port *port,  	status = usb_submit_urb(urb, GFP_ATOMIC);  	if (status) { -		err("%s - failed submitting write urb, error %d", -							__func__, status); +		dev_err(&port->dev, +			"%s - failed submitting write urb, error %d\n", +			__func__, status);  		count = status;  		goto error;  	} else { @@ -1780,7 +1786,8 @@ static int ftdi_chars_in_buffer(struct tty_struct *tty)  	buffered = (int)priv->tx_outstanding_bytes;  	spin_unlock_irqrestore(&priv->tx_lock, flags);  	if (buffered < 0) { -		err("%s outstanding tx bytes is negative!", __func__); +		dev_err(&port->dev, "%s outstanding tx bytes is negative!\n", +			__func__);  		buffered = 0;  	}  	return buffered; @@ -1796,11 +1803,12 @@ static void ftdi_read_bulk_callback(struct urb *urb)  	int status = urb->status;  	if (urb->number_of_packets > 0) { -		err("%s transfer_buffer_length %d actual_length %d number of packets %d", -				__func__, -				urb->transfer_buffer_length, -				urb->actual_length, urb->number_of_packets); -		err("%s transfer_flags %x ", __func__, urb->transfer_flags); +		dev_err(&port->dev, "%s transfer_buffer_length %d " +			"actual_length %d number of packets %d\n", __func__, +			urb->transfer_buffer_length, +			urb->actual_length, urb->number_of_packets); +		dev_err(&port->dev, "%s transfer_flags %x\n", __func__, +			urb->transfer_flags);  	}  	dbg("%s - port %d", __func__, port->number); @@ -1808,7 +1816,7 @@ static void ftdi_read_bulk_callback(struct urb *urb)  	if (port->port.count <= 0)  		return; -	tty = port->port.tty; +	tty = tty_port_tty_get(&port->port);  	if (!tty) {  		dbg("%s - bad tty pointer - exiting", __func__);  		return; @@ -1817,17 +1825,17 @@ static void ftdi_read_bulk_callback(struct urb *urb)  	priv = usb_get_serial_port_data(port);  	if (!priv) {  		dbg("%s - bad port private data pointer - exiting", __func__); -		return; +		goto out;  	}  	if (urb != port->read_urb) -		err("%s - Not my urb!", __func__); +		dev_err(&port->dev, "%s - Not my urb!\n", __func__);  	if (status) {  		/* This will happen at close every time so it is a dbg not an  		   err */  		dbg("(this is ok on close) nonzero read bulk status received: %d", status); -		return; +		goto out;  	}  	/* count data bytes, but not status bytes */ @@ -1838,7 +1846,8 @@ static void ftdi_read_bulk_callback(struct urb *urb)  	spin_unlock_irqrestore(&priv->rx_lock, flags);  	ftdi_process_read(&priv->rx_work.work); - +out: +	tty_kref_put(tty);  } /* ftdi_read_bulk_callback */ @@ -1863,7 +1872,7 @@ static void ftdi_process_read(struct work_struct *work)  	if (port->port.count <= 0)  		return; -	tty = port->port.tty; +	tty = tty_port_tty_get(&port->port);  	if (!tty) {  		dbg("%s - bad tty pointer - exiting", __func__);  		return; @@ -1872,13 +1881,13 @@ static void ftdi_process_read(struct work_struct *work)  	priv = usb_get_serial_port_data(port);  	if (!priv) {  		dbg("%s - bad port private data pointer - exiting", __func__); -		return; +		goto out;  	}  	urb = port->read_urb;  	if (!urb) {  		dbg("%s - bad read_urb pointer - exiting", __func__); -		return; +		goto out;  	}  	data = urb->transfer_buffer; @@ -1923,7 +1932,8 @@ static void ftdi_process_read(struct work_struct *work)  		length = min(PKTSZ, urb->actual_length-packet_offset)-2;  		if (length < 0) { -			err("%s - bad packet length: %d", __func__, length+2); +			dev_err(&port->dev, "%s - bad packet length: %d\n", +				__func__, length+2);  			length = 0;  		} @@ -2020,7 +2030,7 @@ static void ftdi_process_read(struct work_struct *work)  			schedule_delayed_work(&priv->rx_work, 1);  		else  			dbg("%s - port is closed", __func__); -		return; +		goto out;  	}  	/* urb is completely processed */ @@ -2038,9 +2048,12 @@ static void ftdi_process_read(struct work_struct *work)  		result = usb_submit_urb(port->read_urb, GFP_ATOMIC);  		if (result) -			err("%s - failed resubmitting read urb, error %d", -							__func__, result); +			dev_err(&port->dev, +				"%s - failed resubmitting read urb, error %d\n", +				__func__, result);  	} +out: +	tty_kref_put(tty);  } /* ftdi_process_read */ @@ -2066,8 +2079,8 @@ static void ftdi_break_ctl(struct tty_struct *tty, int break_state)  			FTDI_SIO_SET_DATA_REQUEST_TYPE,  			urb_value , priv->interface,  			buf, 0, WDR_TIMEOUT) < 0) { -		err("%s FAILED to enable/disable break state (state was %d)", -							__func__, break_state); +		dev_err(&port->dev, "%s FAILED to enable/disable break state " +			"(state was %d)\n", __func__, break_state);  	}  	dbg("%s break state is %d - urb is %d", __func__, @@ -2139,7 +2152,7 @@ static void ftdi_set_termios(struct tty_struct *tty,  		case CS7: urb_value |= 7; dbg("Setting CS7"); break;  		case CS8: urb_value |= 8; dbg("Setting CS8"); break;  		default: -			err("CSIZE was set but not CS5-CS8"); +			dev_err(&port->dev, "CSIZE was set but not CS5-CS8\n");  		}  	} @@ -2152,7 +2165,8 @@ static void ftdi_set_termios(struct tty_struct *tty,  			    FTDI_SIO_SET_DATA_REQUEST_TYPE,  			    urb_value , priv->interface,  			    buf, 0, WDR_SHORT_TIMEOUT) < 0) { -		err("%s FAILED to set databits/stopbits/parity", __func__); +		dev_err(&port->dev, "%s FAILED to set " +			"databits/stopbits/parity\n", __func__);  	}  	/* Now do the baudrate */ @@ -2163,14 +2177,17 @@ static void ftdi_set_termios(struct tty_struct *tty,  				    FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE,  				    0, priv->interface,  				    buf, 0, WDR_TIMEOUT) < 0) { -			err("%s error from disable flowcontrol urb", __func__); +			dev_err(&port->dev, +				"%s error from disable flowcontrol urb\n", +				__func__);  		}  		/* Drop RTS and DTR */  		clear_mctrl(port, TIOCM_DTR | TIOCM_RTS);  	} else {  		/* set the baudrate determined before */  		if (change_speed(tty, port)) -			err("%s urb failed to set baudrate", __func__); +			dev_err(&port->dev, "%s urb failed to set baudrate\n", +				__func__);  		/* Ensure RTS and DTR are raised when baudrate changed from 0 */  		if (!old_termios || (old_termios->c_cflag & CBAUD) == B0)  			set_mctrl(port, TIOCM_DTR | TIOCM_RTS); @@ -2186,7 +2203,8 @@ static void ftdi_set_termios(struct tty_struct *tty,  				    FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE,  				    0 , (FTDI_SIO_RTS_CTS_HS | priv->interface),  				    buf, 0, WDR_TIMEOUT) < 0) { -			err("urb failed to set to rts/cts flow control"); +			dev_err(&port->dev, +				"urb failed to set to rts/cts flow control\n");  		}  	} else { @@ -2217,7 +2235,8 @@ static void ftdi_set_termios(struct tty_struct *tty,  					    urb_value , (FTDI_SIO_XON_XOFF_HS  							 | priv->interface),  					    buf, 0, WDR_TIMEOUT) < 0) { -				err("urb failed to set to xon/xoff flow control"); +				dev_err(&port->dev, "urb failed to set to " +					"xon/xoff flow control\n");  			}  		} else {  			/* else clause to only run if cflag ! CRTSCTS and iflag @@ -2230,7 +2249,8 @@ static void ftdi_set_termios(struct tty_struct *tty,  					    FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE,  					    0, priv->interface,  					    buf, 0, WDR_TIMEOUT) < 0) { -				err("urb failed to clear flow control"); +				dev_err(&port->dev, +					"urb failed to clear flow control\n");  			}  		} @@ -2256,7 +2276,7 @@ static int ftdi_tiocmget(struct tty_struct *tty, struct file *file)  			   0, 0,  			   buf, 1, WDR_TIMEOUT);  		if (ret < 0) { -			err("%s Could not get modem status of device - err: %d", __func__, +			dbg("%s Could not get modem status of device - err: %d", __func__,  			    ret);  			return ret;  		} @@ -2275,7 +2295,7 @@ static int ftdi_tiocmget(struct tty_struct *tty, struct file *file)  				   0, priv->interface,  				   buf, 2, WDR_TIMEOUT);  		if (ret < 0) { -			err("%s Could not get modem status of device - err: %d", __func__, +			dbg("%s Could not get modem status of device - err: %d", __func__,  			    ret);  			return ret;  		} @@ -2422,7 +2442,8 @@ static int __init ftdi_init(void)  	if (retval)  		goto failed_usb_register; -	info(DRIVER_VERSION ":" DRIVER_DESC); +	printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":" +	       DRIVER_DESC "\n");  	return 0;  failed_usb_register:  	usb_serial_deregister(&ftdi_sio_device); @@ -2455,5 +2476,5 @@ module_param(vendor, ushort, 0);  MODULE_PARM_DESC(vendor, "User specified vendor ID (default="  		__MODULE_STRING(FTDI_VID)")");  module_param(product, ushort, 0); -MODULE_PARM_DESC(vendor, "User specified product ID"); +MODULE_PARM_DESC(product, "User specified product ID"); diff --git a/drivers/usb/serial/ftdi_sio.h b/drivers/usb/serial/ftdi_sio.h index 8a5b6df3a97..07a3992abad 100644 --- a/drivers/usb/serial/ftdi_sio.h +++ b/drivers/usb/serial/ftdi_sio.h @@ -628,6 +628,11 @@  #define FTDI_SUUNTO_SPORTS_PID	0xF680	/* Suunto Sports instrument */  /* + * Oceanic product ids + */ +#define FTDI_OCEANIC_PID	0xF460  /* Oceanic dive instrument */ + +/*   * TTi (Thurlby Thandar Instruments)   */  #define TTI_VID			0x103E	/* Vendor Id */ diff --git a/drivers/usb/serial/garmin_gps.c b/drivers/usb/serial/garmin_gps.c index d9538208807..8e6a66e38db 100644 --- a/drivers/usb/serial/garmin_gps.c +++ b/drivers/usb/serial/garmin_gps.c @@ -276,7 +276,7 @@ static inline int isAbortTrfCmnd(const unsigned char *buf)  static void send_to_tty(struct usb_serial_port *port,  			char *data, unsigned int actual_length)  { -	struct tty_struct *tty = port->port.tty; +	struct tty_struct *tty = tty_port_tty_get(&port->port);  	if (tty && actual_length) { @@ -287,6 +287,7 @@ static void send_to_tty(struct usb_serial_port *port,  		tty_insert_flip_string(tty, data, actual_length);  		tty_flip_buffer_push(tty);  	} +	tty_kref_put(tty);  } @@ -1584,7 +1585,8 @@ static int __init garmin_init(void)  	retval = usb_register(&garmin_driver);  	if (retval)  		goto failed_usb_register; -	info(DRIVER_DESC " " DRIVER_VERSION); +	printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":" +	       DRIVER_DESC "\n");  	return 0;  failed_usb_register: diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c index fe84c88ec20..814909f1ee6 100644 --- a/drivers/usb/serial/generic.c +++ b/drivers/usb/serial/generic.c @@ -330,7 +330,7 @@ static void resubmit_read_urb(struct usb_serial_port *port, gfp_t mem_flags)  static void flush_and_resubmit_read_urb(struct usb_serial_port *port)  {  	struct urb *urb = port->read_urb; -	struct tty_struct *tty = port->port.tty; +	struct tty_struct *tty = tty_port_tty_get(&port->port);  	int room;  	/* Push data to tty */ @@ -341,6 +341,7 @@ static void flush_and_resubmit_read_urb(struct usb_serial_port *port)  			tty_flip_buffer_push(tty);  		}  	} +	tty_kref_put(tty);  	resubmit_read_urb(port, GFP_ATOMIC);  } diff --git a/drivers/usb/serial/hp4x.c b/drivers/usb/serial/hp4x.c index ab905869e95..43132927513 100644 --- a/drivers/usb/serial/hp4x.c +++ b/drivers/usb/serial/hp4x.c @@ -63,7 +63,8 @@ static int __init hp49gp_init(void)  	retval = usb_register(&hp49gp_driver);  	if (retval)  		goto failed_usb_register; -	info(DRIVER_DESC " " DRIVER_VERSION); +	printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":" +	       DRIVER_DESC "\n");  	return 0;  failed_usb_register:  	usb_serial_deregister(&hp49gp_device); diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c index bfa508ddb0f..e85c8c0d1ad 100644 --- a/drivers/usb/serial/io_edgeport.c +++ b/drivers/usb/serial/io_edgeport.c @@ -600,6 +600,7 @@ static void edge_interrupt_callback(struct urb *urb)  	struct edgeport_serial	*edge_serial = urb->context;  	struct edgeport_port *edge_port;  	struct usb_serial_port *port; +	struct tty_struct *tty;  	unsigned char *data = urb->transfer_buffer;  	int length = urb->actual_length;  	int bytes_avail; @@ -675,9 +676,12 @@ static void edge_interrupt_callback(struct urb *urb)  					/* tell the tty driver that something  					   has changed */ -					if (edge_port->port->port.tty) -						tty_wakeup(edge_port->port->port.tty); - +					tty = tty_port_tty_get( +						&edge_port->port->port); +					if (tty) { +						tty_wakeup(tty); +						tty_kref_put(tty); +					}  					/* Since we have more credit, check  					   if more data can be sent */  					send_more_port_data(edge_serial, @@ -778,13 +782,14 @@ static void edge_bulk_out_data_callback(struct urb *urb)  		    __func__, status);  	} -	tty = edge_port->port->port.tty; +	tty = tty_port_tty_get(&edge_port->port->port);  	if (tty && edge_port->open) {  		/* let the tty driver wakeup if it has a special  		   write_wakeup function */  		tty_wakeup(tty);  	} +	tty_kref_put(tty);  	/* Release the Write URB */  	edge_port->write_in_progress = false; @@ -826,11 +831,12 @@ static void edge_bulk_out_cmd_callback(struct urb *urb)  	}  	/* Get pointer to tty */ -	tty = edge_port->port->port.tty; +	tty = tty_port_tty_get(&edge_port->port->port);  	/* tell the tty driver that something has changed */  	if (tty && edge_port->open)  		tty_wakeup(tty); +	tty_kref_put(tty);  	/* we have completed the command */  	edge_port->commandPending = false; @@ -1932,11 +1938,13 @@ static void process_rcvd_data(struct edgeport_serial *edge_serial,  							edge_serial->rxPort];  				edge_port = usb_get_serial_port_data(port);  				if (edge_port->open) { -					tty = edge_port->port->port.tty; +					tty = tty_port_tty_get( +						&edge_port->port->port);  					if (tty) {  						dbg("%s - Sending %d bytes to TTY for port %d",  							__func__, rxLen, edge_serial->rxPort);  						edge_tty_recv(&edge_serial->serial->dev->dev, tty, buffer, rxLen); +						tty_kref_put(tty);  					}  					edge_port->icount.rx += rxLen;  				} @@ -1971,6 +1979,7 @@ static void process_rcvd_status(struct edgeport_serial *edge_serial,  {  	struct usb_serial_port *port;  	struct edgeport_port *edge_port; +	struct tty_struct *tty;  	__u8 code = edge_serial->rxStatusCode;  	/* switch the port pointer to the one being currently talked about */ @@ -2020,10 +2029,12 @@ static void process_rcvd_status(struct edgeport_serial *edge_serial,  		/* send the current line settings to the port so we are  		   in sync with any further termios calls */ -		/* FIXME: locking on tty */ -		if (edge_port->port->port.tty) -			change_port_settings(edge_port->port->port.tty, -				edge_port, edge_port->port->port.tty->termios); +		tty = tty_port_tty_get(&edge_port->port->port); +		if (tty) { +			change_port_settings(tty, +				edge_port, tty->termios); +			tty_kref_put(tty); +		}  		/* we have completed the open */  		edge_port->openPending = false; @@ -2163,10 +2174,14 @@ static void handle_new_lsr(struct edgeport_port *edge_port, __u8 lsrData,  	}  	/* Place LSR data byte into Rx buffer */ -	if (lsrData && edge_port->port->port.tty) -		edge_tty_recv(&edge_port->port->dev, -					edge_port->port->port.tty, &data, 1); - +	if (lsrData) { +		struct tty_struct *tty = +				tty_port_tty_get(&edge_port->port->port); +		if (tty) { +			edge_tty_recv(&edge_port->port->dev, tty, &data, 1); +			tty_kref_put(tty); +		} +	}  	/* update input line counters */  	icount = &edge_port->icount;  	if (newLsr & LSR_BREAK) @@ -3094,13 +3109,13 @@ static int edge_startup(struct usb_serial *serial)  				edge_serial->interrupt_read_urb =  						usb_alloc_urb(0, GFP_KERNEL);  				if (!edge_serial->interrupt_read_urb) { -					err("out of memory"); +					dev_err(&dev->dev, "out of memory\n");  					return -ENOMEM;  				}  				edge_serial->interrupt_in_buffer =  					kmalloc(buffer_size, GFP_KERNEL);  				if (!edge_serial->interrupt_in_buffer) { -					err("out of memory"); +					dev_err(&dev->dev, "out of memory\n");  					usb_free_urb(edge_serial->interrupt_read_urb);  					return -ENOMEM;  				} @@ -3131,13 +3146,13 @@ static int edge_startup(struct usb_serial *serial)  				edge_serial->read_urb =  						usb_alloc_urb(0, GFP_KERNEL);  				if (!edge_serial->read_urb) { -					err("out of memory"); +					dev_err(&dev->dev, "out of memory\n");  					return -ENOMEM;  				}  				edge_serial->bulk_in_buffer =  					kmalloc(buffer_size, GFP_KERNEL);  				if (!edge_serial->bulk_in_buffer) { -					err("out of memory"); +					dev_err(&dev->dev, "out of memory\n");  					usb_free_urb(edge_serial->read_urb);  					return -ENOMEM;  				} @@ -3166,7 +3181,8 @@ static int edge_startup(struct usb_serial *serial)  		}  		if (!interrupt_in_found || !bulk_in_found || !bulk_out_found) { -			err("Error - the proper endpoints were not found!"); +			dev_err(&dev->dev, "Error - the proper endpoints " +				"were not found!\n");  			return -ENODEV;  		} @@ -3175,8 +3191,9 @@ static int edge_startup(struct usb_serial *serial)  		response = usb_submit_urb(edge_serial->interrupt_read_urb,  								GFP_KERNEL);  		if (response) -			err("%s - Error %d submitting control urb", -							__func__, response); +			dev_err(&dev->dev, +				"%s - Error %d submitting control urb\n", +				__func__, response);  	}  	return response;  } @@ -3238,7 +3255,8 @@ static int __init edgeport_init(void)  	if (retval)  		goto failed_usb_register;  	atomic_set(&CmdUrbs, 0); -	info(DRIVER_DESC " " DRIVER_VERSION); +	printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":" +	       DRIVER_DESC "\n");  	return 0;  failed_usb_register: diff --git a/drivers/usb/serial/io_ti.c b/drivers/usb/serial/io_ti.c index cb4c54316cf..c3cdd00ddc4 100644 --- a/drivers/usb/serial/io_ti.c +++ b/drivers/usb/serial/io_ti.c @@ -572,7 +572,7 @@ static void chase_port(struct edgeport_port *port, unsigned long timeout,  								int flush)  {  	int baud_rate; -	struct tty_struct *tty = port->port->port.tty; +	struct tty_struct *tty = tty_port_tty_get(&port->port->port);  	wait_queue_t wait;  	unsigned long flags; @@ -599,6 +599,7 @@ static void chase_port(struct edgeport_port *port, unsigned long timeout,  	if (flush)  		edge_buf_clear(port->ep_out_buf);  	spin_unlock_irqrestore(&port->ep_lock, flags); +	tty_kref_put(tty);  	/* wait for data to drain from the device */  	timeout += jiffies; @@ -1554,7 +1555,7 @@ static void handle_new_msr(struct edgeport_port *edge_port, __u8 msr)  	/* Save the new modem status */  	edge_port->shadow_msr = msr & 0xf0; -	tty = edge_port->port->port.tty; +	tty = tty_port_tty_get(&edge_port->port->port);  	/* handle CTS flow control */  	if (tty && C_CRTSCTS(tty)) {  		if (msr & EDGEPORT_MSR_CTS) { @@ -1564,6 +1565,7 @@ static void handle_new_msr(struct edgeport_port *edge_port, __u8 msr)  			tty->hw_stopped = 1;  		}  	} +	tty_kref_put(tty);  	return;  } @@ -1574,6 +1576,7 @@ static void handle_new_lsr(struct edgeport_port *edge_port, int lsr_data,  	struct async_icount *icount;  	__u8 new_lsr = (__u8)(lsr & (__u8)(LSR_OVER_ERR | LSR_PAR_ERR |  						LSR_FRM_ERR | LSR_BREAK)); +	struct tty_struct *tty;  	dbg("%s - %02x", __func__, new_lsr); @@ -1587,8 +1590,13 @@ static void handle_new_lsr(struct edgeport_port *edge_port, int lsr_data,  		new_lsr &= (__u8)(LSR_OVER_ERR | LSR_BREAK);  	/* Place LSR data byte into Rx buffer */ -	if (lsr_data && edge_port->port->port.tty) -		edge_tty_recv(&edge_port->port->dev, edge_port->port->port.tty, &data, 1); +	if (lsr_data) { +		tty = tty_port_tty_get(&edge_port->port->port); +		if (tty) { +			edge_tty_recv(&edge_port->port->dev, tty, &data, 1); +			tty_kref_put(tty); +		} +	}  	/* update input line counters */  	icount = &edge_port->icount; @@ -1749,7 +1757,7 @@ static void edge_bulk_in_callback(struct urb *urb)  		++data;  	} -	tty = edge_port->port->port.tty; +	tty = tty_port_tty_get(&edge_port->port->port);  	if (tty && urb->actual_length) {  		usb_serial_debug_data(debug, &edge_port->port->dev,  					__func__, urb->actual_length, data); @@ -1761,6 +1769,7 @@ static void edge_bulk_in_callback(struct urb *urb)  							urb->actual_length);  		edge_port->icount.rx += urb->actual_length;  	} +	tty_kref_put(tty);  exit:  	/* continue read unless stopped */ @@ -1796,6 +1805,7 @@ static void edge_bulk_out_callback(struct urb *urb)  	struct usb_serial_port *port = urb->context;  	struct edgeport_port *edge_port = usb_get_serial_port_data(port);  	int status = urb->status; +	struct tty_struct *tty;  	dbg("%s - port %d", __func__, port->number); @@ -1818,7 +1828,9 @@ static void edge_bulk_out_callback(struct urb *urb)  	}  	/* send any buffered data */ -	edge_send(port->port.tty); +	tty = tty_port_tty_get(&port->port); +	edge_send(tty); +	tty_kref_put(tty);  }  static int edge_open(struct tty_struct *tty, @@ -1876,7 +1888,7 @@ static int edge_open(struct tty_struct *tty,  	/* set up the port settings */  	if (tty) -		edge_set_termios(tty, port, port->port.tty->termios); +		edge_set_termios(tty, port, tty->termios);  	/* open up the port */ @@ -2966,7 +2978,8 @@ static int __init edgeport_init(void)  	retval = usb_register(&io_driver);  	if (retval)  		goto failed_usb_register; -	info(DRIVER_DESC " " DRIVER_VERSION); +	printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":" +	       DRIVER_DESC "\n");  	return 0;  failed_usb_register:  	usb_serial_deregister(&edgeport_2port_device); diff --git a/drivers/usb/serial/ipaq.c b/drivers/usb/serial/ipaq.c index cd9a2e138c8..132be74d2b8 100644 --- a/drivers/usb/serial/ipaq.c +++ b/drivers/usb/serial/ipaq.c @@ -608,7 +608,7 @@ static int ipaq_open(struct tty_struct *tty,  	bytes_out = 0;  	priv = kmalloc(sizeof(struct ipaq_private), GFP_KERNEL);  	if (priv == NULL) { -		err("%s - Out of memory", __func__); +		dev_err(&port->dev, "%s - Out of memory\n", __func__);  		return -ENOMEM;  	}  	usb_set_serial_port_data(port, priv); @@ -693,8 +693,7 @@ static int ipaq_open(struct tty_struct *tty,  	}  	if (!retries && result) { -		err("%s - failed doing control urb, error %d", __func__, -		    result); +		dev_err(&port->dev, "%s - failed doing control urb, error %d\n",			__func__, result);  		goto error;  	} @@ -707,8 +706,9 @@ static int ipaq_open(struct tty_struct *tty,  	result = usb_submit_urb(port->read_urb, GFP_KERNEL);  	if (result) { -		err("%s - failed submitting read urb, error %d", -						__func__, result); +		dev_err(&port->dev, +			"%s - failed submitting read urb, error %d\n", +			__func__, result);  		goto error;  	} @@ -716,7 +716,7 @@ static int ipaq_open(struct tty_struct *tty,  enomem:  	result = -ENOMEM; -	err("%s - Out of memory", __func__); +	dev_err(&port->dev, "%s - Out of memory\n", __func__);  error:  	ipaq_destroy_lists(port);  	kfree(priv); @@ -764,13 +764,14 @@ static void ipaq_read_bulk_callback(struct urb *urb)  	usb_serial_debug_data(debug, &port->dev, __func__,  						urb->actual_length, data); -	tty = port->port.tty; +	tty = tty_port_tty_get(&port->port);  	if (tty && urb->actual_length) {  		tty_buffer_request_room(tty, urb->actual_length);  		tty_insert_flip_string(tty, data, urb->actual_length);  		tty_flip_buffer_push(tty);  		bytes_in += urb->actual_length;  	} +	tty_kref_put(tty);  	/* Continue trying to always read  */  	usb_fill_bulk_urb(port->read_urb, port->serial->dev, @@ -780,8 +781,9 @@ static void ipaq_read_bulk_callback(struct urb *urb)  	    ipaq_read_bulk_callback, port);  	result = usb_submit_urb(port->read_urb, GFP_ATOMIC);  	if (result) -		err("%s - failed resubmitting read urb, error %d", -							__func__, result); +		dev_err(&port->dev, +			"%s - failed resubmitting read urb, error %d\n", +			__func__, result);  	return;  } @@ -846,7 +848,8 @@ static int ipaq_write_bulk(struct usb_serial_port *port,  		spin_unlock_irqrestore(&write_list_lock, flags);  		result = usb_submit_urb(port->write_urb, GFP_ATOMIC);  		if (result) -			err("%s - failed submitting write urb, error %d", +			dev_err(&port->dev, +				"%s - failed submitting write urb, error %d\n",  				__func__, result);  	} else {  		spin_unlock_irqrestore(&write_list_lock, flags); @@ -908,8 +911,9 @@ static void ipaq_write_bulk_callback(struct urb *urb)  		spin_unlock_irqrestore(&write_list_lock, flags);  		result = usb_submit_urb(port->write_urb, GFP_ATOMIC);  		if (result) -			err("%s - failed submitting write urb, error %d", -					__func__, result); +			dev_err(&port->dev, +				"%s - failed submitting write urb, error %d\n", +				__func__, result);  	} else {  		priv->active = 0;  		spin_unlock_irqrestore(&write_list_lock, flags); @@ -956,7 +960,7 @@ static int ipaq_startup(struct usb_serial *serial)  {  	dbg("%s", __func__);  	if (serial->dev->actconfig->desc.bConfigurationValue != 1) { -		err("active config #%d != 1 ??", +		dev_err(&serial->dev->dev, "active config #%d != 1 ??\n",  			serial->dev->actconfig->desc.bConfigurationValue);  		return -ENODEV;  	} @@ -975,7 +979,6 @@ static int __init ipaq_init(void)  	retval = usb_serial_register(&ipaq_device);  	if (retval)  		goto failed_usb_serial_register; -	info(DRIVER_DESC " " DRIVER_VERSION);  	if (vendor) {  		ipaq_id_table[0].idVendor = vendor;  		ipaq_id_table[0].idProduct = product; @@ -984,6 +987,8 @@ static int __init ipaq_init(void)  	if (retval)  		goto failed_usb_register; +	printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":" +	       DRIVER_DESC "\n");  	return 0;  failed_usb_register:  	usb_serial_deregister(&ipaq_device); diff --git a/drivers/usb/serial/ipw.c b/drivers/usb/serial/ipw.c index a842025b9b5..3ac59a8a980 100644 --- a/drivers/usb/serial/ipw.c +++ b/drivers/usb/serial/ipw.c @@ -170,12 +170,13 @@ static void ipw_read_bulk_callback(struct urb *urb)  	usb_serial_debug_data(debug, &port->dev, __func__,  					urb->actual_length, data); -	tty = port->port.tty; +	tty = tty_port_tty_get(&port->port);  	if (tty && urb->actual_length) {  		tty_buffer_request_room(tty, urb->actual_length);  		tty_insert_flip_string(tty, data, urb->actual_length);  		tty_flip_buffer_push(tty);  	} +	tty_kref_put(tty);  	/* Continue trying to always read  */  	usb_fill_bulk_urb(port->read_urb, port->serial->dev, @@ -484,7 +485,8 @@ static int usb_ipw_init(void)  		usb_serial_deregister(&ipw_device);  		return retval;  	} -	info(DRIVER_DESC " " DRIVER_VERSION); +	printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":" +	       DRIVER_DESC "\n");  	return 0;  } diff --git a/drivers/usb/serial/ir-usb.c b/drivers/usb/serial/ir-usb.c index e59155c6607..4e2cda93da5 100644 --- a/drivers/usb/serial/ir-usb.c +++ b/drivers/usb/serial/ir-usb.c @@ -26,7 +26,7 @@   *	Introduced common header to be used also in USB Gadget Framework.   *	Still needs some other style fixes.   * - * 2007_Jun_21  Alan Cox <alan@redhat.com> + * 2007_Jun_21  Alan Cox <alan@lxorguk.ukuu.org.uk>   *	Minimal cleanups for some of the driver problens and tty layer abuse.   *	Still needs fixing to allow multiple dongles.   * @@ -465,11 +465,12 @@ static void ir_read_bulk_callback(struct urb *urb)  			ir_baud = *data & 0x0f;  		usb_serial_debug_data(debug, &port->dev, __func__,  						urb->actual_length, data); - 		tty = port->port.tty; +		tty = tty_port_tty_get(&port->port);  		if (tty_buffer_request_room(tty, urb->actual_length - 1)) {  			tty_insert_flip_string(tty, data+1, urb->actual_length - 1);  			tty_flip_buffer_push(tty);  		} +		tty_kref_put(tty);  		/*  		 * No break here. @@ -601,7 +602,8 @@ static int __init ir_init(void)  	if (retval)  		goto failed_usb_register; -	info(DRIVER_DESC " " DRIVER_VERSION); +	printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":" +	       DRIVER_DESC "\n");  	return 0; diff --git a/drivers/usb/serial/iuu_phoenix.c b/drivers/usb/serial/iuu_phoenix.c index ddff37fa633..e320972cb22 100644 --- a/drivers/usb/serial/iuu_phoenix.c +++ b/drivers/usb/serial/iuu_phoenix.c @@ -629,13 +629,14 @@ static void read_buf_callback(struct urb *urb)  	}  	dbg("%s - %i chars to write", __func__, urb->actual_length); -	tty = port->port.tty; +	tty = tty_port_tty_get(&port->port);  	if (data == NULL)  		dbg("%s - data is NULL !!!", __func__);  	if (tty && urb->actual_length && data) {  		tty_insert_flip_string(tty, data, urb->actual_length);  		tty_flip_buffer_push(tty);  	} +	tty_kref_put(tty);  	iuu_led_activity_on(urb);  } @@ -1184,7 +1185,8 @@ static int __init iuu_init(void)  	retval = usb_register(&iuu_driver);  	if (retval)  		goto failed_usb_register; -	info(DRIVER_DESC " " DRIVER_VERSION); +	printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":" +	       DRIVER_DESC "\n");  	return 0;  failed_usb_register:  	usb_serial_deregister(&iuu_device); diff --git a/drivers/usb/serial/keyspan.c b/drivers/usb/serial/keyspan.c index 704716f6f6d..9878c0fb385 100644 --- a/drivers/usb/serial/keyspan.c +++ b/drivers/usb/serial/keyspan.c @@ -217,7 +217,8 @@ static int __init keyspan_init(void)  	if (retval)  		goto failed_usb_register; -	info(DRIVER_VERSION ":" DRIVER_DESC); +	printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":" +	       DRIVER_DESC "\n");  	return 0;  failed_usb_register: @@ -430,7 +431,7 @@ static void	usa26_indat_callback(struct urb *urb)  	}  	port =  urb->context; -	tty = port->port.tty; +	tty = tty_port_tty_get(&port->port);  	if (tty && urb->actual_length) {  		/* 0x80 bit is error flag */  		if ((data[0] & 0x80) == 0) { @@ -459,6 +460,7 @@ static void	usa26_indat_callback(struct urb *urb)  		}  		tty_flip_buffer_push(tty);  	} +	tty_kref_put(tty);  	/* Resubmit urb so we continue receiving */  	urb->dev = port->serial->dev; @@ -513,6 +515,7 @@ static void	usa26_instat_callback(struct urb *urb)  	struct usb_serial			*serial;  	struct usb_serial_port			*port;  	struct keyspan_port_private	 	*p_priv; +	struct tty_struct			*tty;  	int old_dcd_state, err;  	int status = urb->status; @@ -553,12 +556,11 @@ static void	usa26_instat_callback(struct urb *urb)  	p_priv->dcd_state = ((msg->gpia_dcd) ? 1 : 0);  	p_priv->ri_state = ((msg->ri) ? 1 : 0); -	if (port->port.tty && !C_CLOCAL(port->port.tty) -	    && old_dcd_state != p_priv->dcd_state) { -		if (old_dcd_state) -			tty_hangup(port->port.tty); -		/*  else */ -		/*	wake_up_interruptible(&p_priv->open_wait); */ +	if (old_dcd_state != p_priv->dcd_state) { +		tty = tty_port_tty_get(&port->port); +		if (tty && !C_CLOCAL(tty)) +			tty_hangup(tty); +		tty_kref_put(tty);  	}  	/* Resubmit urb so we continue receiving */ @@ -604,11 +606,12 @@ static void usa28_indat_callback(struct urb *urb)  		p_priv = usb_get_serial_port_data(port);  		data = urb->transfer_buffer; -		tty = port->port.tty; -		if (urb->actual_length) { +		tty =tty_port_tty_get(&port->port); +		if (tty && urb->actual_length) {  			tty_insert_flip_string(tty, data, urb->actual_length);  			tty_flip_buffer_push(tty);  		} +		tty_kref_put(tty);  		/* Resubmit urb so we continue receiving */  		urb->dev = port->serial->dev; @@ -652,6 +655,7 @@ static void	usa28_instat_callback(struct urb *urb)  	struct usb_serial			*serial;  	struct usb_serial_port			*port;  	struct keyspan_port_private	 	*p_priv; +	struct tty_struct			*tty;  	int old_dcd_state;  	int status = urb->status; @@ -689,12 +693,11 @@ static void	usa28_instat_callback(struct urb *urb)  	p_priv->dcd_state = ((msg->dcd) ? 1 : 0);  	p_priv->ri_state = ((msg->ri) ? 1 : 0); -	if (port->port.tty && !C_CLOCAL(port->port.tty) -	    && old_dcd_state != p_priv->dcd_state) { -		if (old_dcd_state) -			tty_hangup(port->port.tty); -		/*  else */ -		/*	wake_up_interruptible(&p_priv->open_wait); */ +	if( old_dcd_state != p_priv->dcd_state && old_dcd_state) { +		tty = tty_port_tty_get(&port->port); +		if (tty && !C_CLOCAL(tty))  +			tty_hangup(tty); +		tty_kref_put(tty);  	}  		/* Resubmit urb so we continue receiving */ @@ -785,12 +788,11 @@ static void	usa49_instat_callback(struct urb *urb)  	p_priv->dcd_state = ((msg->dcd) ? 1 : 0);  	p_priv->ri_state = ((msg->ri) ? 1 : 0); -	if (port->port.tty && !C_CLOCAL(port->port.tty) -	    && old_dcd_state != p_priv->dcd_state) { -		if (old_dcd_state) -			tty_hangup(port->port.tty); -		/*  else */ -		/*	wake_up_interruptible(&p_priv->open_wait); */ +	if (old_dcd_state != p_priv->dcd_state && old_dcd_state) { +		struct tty_struct *tty = tty_port_tty_get(&port->port); +		if (tty && !C_CLOCAL(tty)) +			tty_hangup(tty); +		tty_kref_put(tty);  	}  	/* Resubmit urb so we continue receiving */ @@ -827,7 +829,7 @@ static void	usa49_indat_callback(struct urb *urb)  	}  	port =  urb->context; -	tty = port->port.tty; +	tty = tty_port_tty_get(&port->port);  	if (tty && urb->actual_length) {  		/* 0x80 bit is error flag */  		if ((data[0] & 0x80) == 0) { @@ -850,6 +852,7 @@ static void	usa49_indat_callback(struct urb *urb)  		}  		tty_flip_buffer_push(tty);  	} +	tty_kref_put(tty);  	/* Resubmit urb so we continue receiving */  	urb->dev = port->serial->dev; @@ -893,7 +896,7 @@ static void usa49wg_indat_callback(struct urb *urb)  				return;  			}  			port = serial->port[data[i++]]; -			tty = port->port.tty; +			tty = tty_port_tty_get(&port->port);  			len = data[i++];  			/* 0x80 bit is error flag */ @@ -927,6 +930,7 @@ static void usa49wg_indat_callback(struct urb *urb)  			}  			if (port->port.count)  				tty_flip_buffer_push(tty); +			tty_kref_put(tty);  		}  	} @@ -967,8 +971,8 @@ static void usa90_indat_callback(struct urb *urb)  	port =  urb->context;  	p_priv = usb_get_serial_port_data(port); -	tty = port->port.tty;  	if (urb->actual_length) { +		tty = tty_port_tty_get(&port->port);  		/* if current mode is DMA, looks like usa28 format  		   otherwise looks like usa26 data format */ @@ -1004,6 +1008,7 @@ static void usa90_indat_callback(struct urb *urb)  			}  		}  		tty_flip_buffer_push(tty); +		tty_kref_put(tty);  	}  	/* Resubmit urb so we continue receiving */ @@ -1025,6 +1030,7 @@ static void	usa90_instat_callback(struct urb *urb)  	struct usb_serial			*serial;  	struct usb_serial_port			*port;  	struct keyspan_port_private	 	*p_priv; +	struct tty_struct			*tty;  	int old_dcd_state, err;  	int status = urb->status; @@ -1053,12 +1059,11 @@ static void	usa90_instat_callback(struct urb *urb)  	p_priv->dcd_state = ((msg->dcd) ? 1 : 0);  	p_priv->ri_state = ((msg->ri) ? 1 : 0); -	if (port->port.tty && !C_CLOCAL(port->port.tty) -	    && old_dcd_state != p_priv->dcd_state) { -		if (old_dcd_state) -			tty_hangup(port->port.tty); -		/*  else */ -		/*	wake_up_interruptible(&p_priv->open_wait); */ +	if (old_dcd_state != p_priv->dcd_state && old_dcd_state) { +		tty = tty_port_tty_get(&port->port); +		if (tty && !C_CLOCAL(tty)) +			tty_hangup(tty); +		tty_kref_put(tty);  	}  	/* Resubmit urb so we continue receiving */ @@ -1130,12 +1135,11 @@ static void	usa67_instat_callback(struct urb *urb)  	p_priv->cts_state = ((msg->hskia_cts) ? 1 : 0);  	p_priv->dcd_state = ((msg->gpia_dcd) ? 1 : 0); -	if (port->port.tty && !C_CLOCAL(port->port.tty) -	    && old_dcd_state != p_priv->dcd_state) { -		if (old_dcd_state) -			tty_hangup(port->port.tty); -		/*  else */ -		/*	wake_up_interruptible(&p_priv->open_wait); */ +	if (old_dcd_state != p_priv->dcd_state && old_dcd_state) { +		struct tty_struct *tty = tty_port_tty_get(&port->port); +		if (tty && !C_CLOCAL(tty)) +			tty_hangup(tty); +		tty_kref_put(tty);  	}  	/* Resubmit urb so we continue receiving */ @@ -1332,7 +1336,7 @@ static void keyspan_close(struct tty_struct *tty,  			stop_urb(p_priv->out_urbs[i]);  		}  	} -	port->port.tty = NULL; +	tty_port_tty_set(&port->port, NULL);  }  /* download the firmware to a pre-renumeration device */ diff --git a/drivers/usb/serial/keyspan_pda.c b/drivers/usb/serial/keyspan_pda.c index 040040a267d..bf1ae247da6 100644 --- a/drivers/usb/serial/keyspan_pda.c +++ b/drivers/usb/serial/keyspan_pda.c @@ -172,8 +172,9 @@ static void keyspan_pda_wakeup_write(struct work_struct *work)  	struct keyspan_pda_private *priv =  		container_of(work, struct keyspan_pda_private, wakeup_work);  	struct usb_serial_port *port = priv->port; - -	tty_wakeup(port->port.tty); +	struct tty_struct *tty = tty_port_tty_get(&port->port); +	tty_wakeup(tty); +	tty_kref_put(tty);  }  static void keyspan_pda_request_unthrottle(struct work_struct *work) @@ -205,7 +206,7 @@ static void keyspan_pda_request_unthrottle(struct work_struct *work)  static void keyspan_pda_rx_interrupt(struct urb *urb)  {  	struct usb_serial_port *port = urb->context; -	struct tty_struct *tty = port->port.tty; +	struct tty_struct *tty = tty_port_tty_get(&port->port);  	unsigned char *data = urb->transfer_buffer;  	int retval;  	int status = urb->status; @@ -222,7 +223,7 @@ static void keyspan_pda_rx_interrupt(struct urb *urb)  		/* this urb is terminated, clean up */  		dbg("%s - urb shutting down with status: %d",  		    __func__, status); -		return; +		goto out;  	default:  		dbg("%s - nonzero urb status received: %d",  		    __func__, status); @@ -261,8 +262,11 @@ static void keyspan_pda_rx_interrupt(struct urb *urb)  exit:  	retval = usb_submit_urb(urb, GFP_ATOMIC);  	if (retval) -		err("%s - usb_submit_urb failed with result %d", -		     __func__, retval); +		dev_err(&port->dev, +			"%s - usb_submit_urb failed with result %d", +			__func__, retval); +out: +	tty_kref_put(tty);		       } @@ -738,11 +742,13 @@ static int keyspan_pda_fake_startup(struct usb_serial *serial)  		fw_name = "keyspan_pda/xircom_pgs.fw";  #endif  	else { -		err("%s: unknown vendor, aborting.", __func__); +		dev_err(&serial->dev->dev, "%s: unknown vendor, aborting.\n", +			__func__);  		return -ENODEV;  	}  	if (request_ihex_firmware(&fw, fw_name, &serial->dev->dev)) { -		err("failed to load firmware \"%s\"\n", fw_name); +		dev_err(&serial->dev->dev, "failed to load firmware \"%s\"\n", +			fw_name);  		return -ENOENT;  	}  	record = (const struct ihex_binrec *)fw->data; @@ -752,10 +758,10 @@ static int keyspan_pda_fake_startup(struct usb_serial *serial)  					     (unsigned char *)record->data,  					     be16_to_cpu(record->len), 0xa0);  		if (response < 0) { -			err("ezusb_writememory failed for Keyspan PDA " -			    "firmware (%d %04X %p %d)", -			    response, be32_to_cpu(record->addr), -			    record->data, be16_to_cpu(record->len)); +			dev_err(&serial->dev->dev, "ezusb_writememory failed " +				"for Keyspan PDA firmware (%d %04X %p %d)\n", +				response, be32_to_cpu(record->addr), +				record->data, be16_to_cpu(record->len));  			break;  		}  		record = ihex_next_binrec(record); @@ -870,7 +876,8 @@ static int __init keyspan_pda_init(void)  	retval = usb_register(&keyspan_pda_driver);  	if (retval)  		goto failed_usb_register; -	info(DRIVER_DESC " " DRIVER_VERSION); +	printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":" +	       DRIVER_DESC "\n");  	return 0;  failed_usb_register:  #ifdef XIRCOM diff --git a/drivers/usb/serial/kl5kusb105.c b/drivers/usb/serial/kl5kusb105.c index b84dddc7112..dc36a052766 100644 --- a/drivers/usb/serial/kl5kusb105.c +++ b/drivers/usb/serial/kl5kusb105.c @@ -182,12 +182,12 @@ static int klsi_105_chg_port_settings(struct usb_serial_port *port,  			sizeof(struct klsi_105_port_settings),  			KLSI_TIMEOUT);  	if (rc < 0) -		err("Change port settings failed (error = %d)", rc); -	info("%s - %d byte block, baudrate %x, databits %d, u1 %d, u2 %d", -	    __func__, -	    settings->pktlen, -	    settings->baudrate, settings->databits, -	    settings->unknown1, settings->unknown2); +		dev_err(&port->dev, +			"Change port settings failed (error = %d)\n", rc); +	dev_info(&port->serial->dev->dev, +		 "%d byte block, baudrate %x, databits %d, u1 %d, u2 %d\n", +		 settings->pktlen, settings->baudrate, settings->databits, +		 settings->unknown1, settings->unknown2);  	return rc;  } /* klsi_105_chg_port_settings */ @@ -215,7 +215,7 @@ static int klsi_105_get_line_state(struct usb_serial_port *port,  	__u8 status_buf[KLSI_STATUSBUF_LEN] = { -1, -1};  	__u16 status; -	info("%s - sending SIO Poll request", __func__); +	dev_info(&port->serial->dev->dev, "sending SIO Poll request\n");  	rc = usb_control_msg(port->serial->dev,  			     usb_rcvctrlpipe(port->serial->dev, 0),  			     KL5KUSB105A_SIO_POLL, @@ -226,12 +226,13 @@ static int klsi_105_get_line_state(struct usb_serial_port *port,  			     10000  			     );  	if (rc < 0) -		err("Reading line status failed (error = %d)", rc); +		dev_err(&port->dev, "Reading line status failed (error = %d)\n", +			rc);  	else {  		status = get_unaligned_le16(status_buf); -		info("%s - read status %x %x", __func__, -		     status_buf[0], status_buf[1]); +		dev_info(&port->serial->dev->dev, "read status %x %x", +			 status_buf[0], status_buf[1]);  		*line_state_p = klsi_105_status2linestate(status);  	} @@ -280,15 +281,16 @@ static int klsi_105_startup(struct usb_serial *serial)  			priv->write_urb_pool[j] = urb;  			if (urb == NULL) { -				err("No more urbs???"); +				dev_err(&serial->dev->dev, "No more urbs???\n");  				goto err_cleanup;  			}  			urb->transfer_buffer =  				kmalloc(URB_TRANSFER_BUFFER_SIZE, GFP_KERNEL);  			if (!urb->transfer_buffer) { -				err("%s - out of memory for urb buffers.", -								__func__); +				dev_err(&serial->dev->dev, +					"%s - out of memory for urb buffers.\n", +					__func__);  				goto err_cleanup;  			}  		} @@ -409,7 +411,8 @@ static int  klsi_105_open(struct tty_struct *tty,  	rc = usb_submit_urb(port->read_urb, GFP_KERNEL);  	if (rc) { -		err("%s - failed submitting read urb, error %d", __func__, rc); +		dev_err(&port->dev, "%s - failed submitting read urb, " +			"error %d\n", __func__, rc);  		retval = rc;  		goto exit;  	} @@ -424,7 +427,7 @@ static int  klsi_105_open(struct tty_struct *tty,  			     0,  			     KLSI_TIMEOUT);  	if (rc < 0) { -		err("Enabling read failed (error = %d)", rc); +		dev_err(&port->dev, "Enabling read failed (error = %d)\n", rc);  		retval = rc;  	} else  		dbg("%s - enabled reading", __func__); @@ -464,7 +467,8 @@ static void klsi_105_close(struct tty_struct *tty,  				     NULL, 0,  				     KLSI_TIMEOUT);  		if (rc < 0) -			err("Disabling read failed (error = %d)", rc); +			dev_err(&port->dev, +				"Disabling read failed (error = %d)\n", rc);  	}  	mutex_unlock(&port->serial->disc_mutex); @@ -475,8 +479,9 @@ static void klsi_105_close(struct tty_struct *tty,  	/* FIXME */  	/* wgg - do I need this? I think so. */  	usb_kill_urb(port->interrupt_in_urb); -	info("kl5kusb105 port stats: %ld bytes in, %ld bytes out", -					priv->bytes_in, priv->bytes_out); +	dev_info(&port->serial->dev->dev, +		 "port stats: %ld bytes in, %ld bytes out\n", +		 priv->bytes_in, priv->bytes_out);  } /* klsi_105_close */ @@ -522,7 +527,9 @@ static int klsi_105_write(struct tty_struct *tty,  			urb->transfer_buffer =  				kmalloc(URB_TRANSFER_BUFFER_SIZE, GFP_ATOMIC);  			if (urb->transfer_buffer == NULL) { -				err("%s - no more kernel memory...", __func__); +				dev_err(&port->dev, +					"%s - no more kernel memory...\n", +					__func__);  				goto exit;  			}  		} @@ -549,8 +556,9 @@ static int klsi_105_write(struct tty_struct *tty,  		/* send the data out the bulk port */  		result = usb_submit_urb(urb, GFP_ATOMIC);  		if (result) { -			err("%s - failed submitting write urb, error %d", -							__func__, result); +			dev_err(&port->dev, +				"%s - failed submitting write urb, error %d\n", +				__func__, result);  			goto exit;  		}  		buf += size; @@ -658,7 +666,7 @@ static void klsi_105_read_bulk_callback(struct urb *urb)  	} else {  		int bytes_sent = ((__u8 *) data)[0] +  				 ((unsigned int) ((__u8 *) data)[1] << 8); -		tty = port->port.tty; +		tty = tty_port_tty_get(&port->port);  		/* we should immediately resubmit the URB, before attempting  		 * to pass the data on to the tty layer. But that needs locking  		 * against re-entry an then mixed-up data because of @@ -679,6 +687,7 @@ static void klsi_105_read_bulk_callback(struct urb *urb)  		tty_buffer_request_room(tty, bytes_sent);  		tty_insert_flip_string(tty, data + 2, bytes_sent);  		tty_flip_buffer_push(tty); +		tty_kref_put(tty);  		/* again lockless, but debug info only */  		priv->bytes_in += bytes_sent; @@ -693,8 +702,9 @@ static void klsi_105_read_bulk_callback(struct urb *urb)  		      port);  	rc = usb_submit_urb(port->read_urb, GFP_ATOMIC);  	if (rc) -		err("%s - failed resubmitting read urb, error %d", -							__func__, rc); +		dev_err(&port->dev, +			"%s - failed resubmitting read urb, error %d\n", +			__func__, rc);  } /* klsi_105_read_bulk_callback */ @@ -798,7 +808,8 @@ static void klsi_105_set_termios(struct tty_struct *tty,  			priv->cfg.databits = kl5kusb105a_dtb_8;  			break;  		default: -			err("CSIZE was not CS5-CS8, using default of 8"); +			dev_err(&port->dev, +				"CSIZE was not CS5-CS8, using default of 8\n");  			priv->cfg.databits = kl5kusb105a_dtb_8;  			break;  		} @@ -885,7 +896,8 @@ static int klsi_105_tiocmget(struct tty_struct *tty, struct file *file)  	rc = klsi_105_get_line_state(port, &line_state);  	if (rc < 0) { -		err("Reading line control failed (error = %d)", rc); +		dev_err(&port->dev, +			"Reading line control failed (error = %d)\n", rc);  		/* better return value? EAGAIN? */  		return rc;  	} @@ -943,8 +955,9 @@ static void klsi_105_unthrottle(struct tty_struct *tty)  	port->read_urb->dev = port->serial->dev;  	result = usb_submit_urb(port->read_urb, GFP_ATOMIC);  	if (result) -		err("%s - failed submitting read urb, error %d", __func__, -		    result); +		dev_err(&port->dev, +			"%s - failed submitting read urb, error %d\n", +			__func__, result);  } @@ -959,7 +972,8 @@ static int __init klsi_105_init(void)  	if (retval)  		goto failed_usb_register; -	info(DRIVER_DESC " " DRIVER_VERSION); +	printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":" +	       DRIVER_DESC "\n");  	return 0;  failed_usb_register:  	usb_serial_deregister(&kl5kusb105d_device); diff --git a/drivers/usb/serial/kobil_sct.c b/drivers/usb/serial/kobil_sct.c index deba28ec77e..6286baad939 100644 --- a/drivers/usb/serial/kobil_sct.c +++ b/drivers/usb/serial/kobil_sct.c @@ -383,7 +383,7 @@ static void kobil_read_int_callback(struct urb *urb)  		return;  	} -	tty = port->port.tty; +	tty = tty_port_tty_get(&port->port);  	if (urb->actual_length) {  		/* BEGIN DEBUG */ @@ -405,6 +405,7 @@ static void kobil_read_int_callback(struct urb *urb)  		tty_insert_flip_string(tty, data, urb->actual_length);  		tty_flip_buffer_push(tty);  	} +	tty_kref_put(tty);  	/* someone sets the dev to 0 if the close method has been called */  	port->interrupt_in_urb->dev = port->serial->dev; @@ -743,8 +744,8 @@ static int __init kobil_init(void)  	if (retval)  		goto failed_usb_register; -	info(DRIVER_VERSION " " DRIVER_AUTHOR); -	info(DRIVER_DESC); +	printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":" +	       DRIVER_DESC "\n");  	return 0;  failed_usb_register: diff --git a/drivers/usb/serial/mct_u232.c b/drivers/usb/serial/mct_u232.c index 0ded8bd6ec8..07710cf31d0 100644 --- a/drivers/usb/serial/mct_u232.c +++ b/drivers/usb/serial/mct_u232.c @@ -246,7 +246,8 @@ static int mct_u232_set_baud_rate(struct tty_struct *tty,  				0, 0, &divisor, MCT_U232_SET_BAUD_RATE_SIZE,  				WDR_TIMEOUT);  	if (rc < 0)	/*FIXME: What value speed results */ -		err("Set BAUD RATE %d failed (error = %d)", value, rc); +		dev_err(&port->dev, "Set BAUD RATE %d failed (error = %d)\n", +			value, rc);  	else  		tty_encode_baud_rate(tty, speed, speed);  	dbg("set_baud_rate: value: 0x%x, divisor: 0x%x", value, divisor); @@ -274,8 +275,9 @@ static int mct_u232_set_baud_rate(struct tty_struct *tty,  				0, 0, &zero_byte, MCT_U232_SET_UNKNOWN1_SIZE,  				WDR_TIMEOUT);  	if (rc < 0) -		err("Sending USB device request code %d failed (error = %d)", -		    MCT_U232_SET_UNKNOWN1_REQUEST, rc); +		dev_err(&port->dev, "Sending USB device request code %d " +			"failed (error = %d)\n", MCT_U232_SET_UNKNOWN1_REQUEST, +			rc);  	if (port && C_CRTSCTS(tty))  	   cts_enable_byte = 1; @@ -288,8 +290,8 @@ static int mct_u232_set_baud_rate(struct tty_struct *tty,  			0, 0, &cts_enable_byte, MCT_U232_SET_CTS_SIZE,  			WDR_TIMEOUT);  	if (rc < 0) -		err("Sending USB device request code %d failed (error = %d)", -					MCT_U232_SET_CTS_REQUEST, rc); +		dev_err(&port->dev, "Sending USB device request code %d " +			"failed (error = %d)\n", MCT_U232_SET_CTS_REQUEST, rc);  	return rc;  } /* mct_u232_set_baud_rate */ @@ -303,7 +305,8 @@ static int mct_u232_set_line_ctrl(struct usb_serial *serial, unsigned char lcr)  			0, 0, &lcr, MCT_U232_SET_LINE_CTRL_SIZE,  			WDR_TIMEOUT);  	if (rc < 0) -		err("Set LINE CTRL 0x%x failed (error = %d)", lcr, rc); +		dev_err(&serial->dev->dev, +			"Set LINE CTRL 0x%x failed (error = %d)\n", lcr, rc);  	dbg("set_line_ctrl: 0x%x", lcr);  	return rc;  } /* mct_u232_set_line_ctrl */ @@ -325,7 +328,8 @@ static int mct_u232_set_modem_ctrl(struct usb_serial *serial,  			0, 0, &mcr, MCT_U232_SET_MODEM_CTRL_SIZE,  			WDR_TIMEOUT);  	if (rc < 0) -		err("Set MODEM CTRL 0x%x failed (error = %d)", mcr, rc); +		dev_err(&serial->dev->dev, +			"Set MODEM CTRL 0x%x failed (error = %d)\n", mcr, rc);  	dbg("set_modem_ctrl: state=0x%x ==> mcr=0x%x", control_state, mcr);  	return rc; @@ -341,7 +345,8 @@ static int mct_u232_get_modem_stat(struct usb_serial *serial,  			0, 0, msr, MCT_U232_GET_MODEM_STAT_SIZE,  			WDR_TIMEOUT);  	if (rc < 0) { -		err("Get MODEM STATus failed (error = %d)", rc); +		dev_err(&serial->dev->dev, +			"Get MODEM STATus failed (error = %d)\n", rc);  		*msr = 0;  	}  	dbg("get_modem_stat: 0x%x", *msr); @@ -470,8 +475,9 @@ static int  mct_u232_open(struct tty_struct *tty,  	port->read_urb->dev = port->serial->dev;  	retval = usb_submit_urb(port->read_urb, GFP_KERNEL);  	if (retval) { -		err("usb_submit_urb(read bulk) failed pipe 0x%x err %d", -		    port->read_urb->pipe, retval); +		dev_err(&port->dev, +			"usb_submit_urb(read bulk) failed pipe 0x%x err %d\n", +			port->read_urb->pipe, retval);  		goto error;  	} @@ -479,8 +485,9 @@ static int  mct_u232_open(struct tty_struct *tty,  	retval = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);  	if (retval) {  		usb_kill_urb(port->read_urb); -		err(" usb_submit_urb(read int) failed pipe 0x%x err %d", -		    port->interrupt_in_urb->pipe, retval); +		dev_err(&port->dev, +			"usb_submit_urb(read int) failed pipe 0x%x err %d", +			port->interrupt_in_urb->pipe, retval);  		goto error;  	}  	return 0; @@ -563,10 +570,11 @@ static void mct_u232_read_int_callback(struct urb *urb)  	 * Work-a-round: handle the 'usual' bulk-in pipe here  	 */  	if (urb->transfer_buffer_length > 2) { -		tty = port->port.tty; +		tty = tty_port_tty_get(&port->port);  		if (urb->actual_length) {  			tty_insert_flip_string(tty, data, urb->actual_length);  			tty_flip_buffer_push(tty); +			tty_kref_put(tty);  		}  		goto exit;  	} @@ -591,7 +599,7 @@ static void mct_u232_read_int_callback(struct urb *urb)  	 * to look in to this before committing any code.  	 */  	if (priv->last_lsr & MCT_U232_LSR_ERR) { -		tty = port->port.tty; +		tty = tty_port_tty_get(&port->port);  		/* Overrun Error */  		if (priv->last_lsr & MCT_U232_LSR_OE) {  		} @@ -604,14 +612,16 @@ static void mct_u232_read_int_callback(struct urb *urb)  		/* Break Indicator */  		if (priv->last_lsr & MCT_U232_LSR_BI) {  		} +		tty_kref_put(tty);  	}  #endif  	spin_unlock_irqrestore(&priv->lock, flags);  exit:  	retval = usb_submit_urb(urb, GFP_ATOMIC);  	if (retval) -		err("%s - usb_submit_urb failed with result %d", -		     __func__, retval); +		dev_err(&port->dev, +			"%s - usb_submit_urb failed with result %d\n", +			__func__, retval);  } /* mct_u232_read_int_callback */  static void mct_u232_set_termios(struct tty_struct *tty, @@ -678,7 +688,8 @@ static void mct_u232_set_termios(struct tty_struct *tty,  	case CS8:  		last_lcr |= MCT_U232_DATA_BITS_8; break;  	default: -		err("CSIZE was not CS5-CS8, using default of 8"); +		dev_err(&port->dev, +			"CSIZE was not CS5-CS8, using default of 8\n");  		last_lcr |= MCT_U232_DATA_BITS_8;  		break;  	} @@ -815,7 +826,8 @@ static int __init mct_u232_init(void)  	retval = usb_register(&mct_u232_driver);  	if (retval)  		goto failed_usb_register; -	info(DRIVER_DESC " " DRIVER_VERSION); +	printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":" +	       DRIVER_DESC "\n");  	return 0;  failed_usb_register:  	usb_serial_deregister(&mct_u232_device); diff --git a/drivers/usb/serial/mos7720.c b/drivers/usb/serial/mos7720.c index 7c4917d77c0..e772cc0a97f 100644 --- a/drivers/usb/serial/mos7720.c +++ b/drivers/usb/serial/mos7720.c @@ -216,12 +216,13 @@ static void mos7720_bulk_in_callback(struct urb *urb)  	data = urb->transfer_buffer; -	tty = port->port.tty; +	tty = tty_port_tty_get(&port->port);  	if (tty && urb->actual_length) {  		tty_buffer_request_room(tty, urb->actual_length);  		tty_insert_flip_string(tty, data, urb->actual_length);  		tty_flip_buffer_push(tty);  	} +	tty_kref_put(tty);  	if (!port->read_urb) {  		dbg("URB KILLED !!!"); @@ -262,10 +263,11 @@ static void mos7720_bulk_out_data_callback(struct urb *urb)  	dbg("Entering ........."); -	tty = mos7720_port->port->port.tty; +	tty = tty_port_tty_get(&mos7720_port->port->port);  	if (tty && mos7720_port->open)  		tty_wakeup(tty); +	tty_kref_put(tty);  }  /* @@ -353,14 +355,16 @@ static int mos7720_open(struct tty_struct *tty,  		mos7720_port->write_urb_pool[j] = urb;  		if (urb == NULL) { -			err("No more urbs???"); +			dev_err(&port->dev, "No more urbs???\n");  			continue;  		}  		urb->transfer_buffer = kmalloc(URB_TRANSFER_BUFFER_SIZE,  					       GFP_KERNEL);  		if (!urb->transfer_buffer) { -			err("%s-out of memory for urb buffers.", __func__); +			dev_err(&port->dev, +				"%s-out of memory for urb buffers.\n", +				__func__);  			usb_free_urb(mos7720_port->write_urb_pool[j]);  			mos7720_port->write_urb_pool[j] = NULL;  			continue; @@ -692,7 +696,8 @@ static int mos7720_write(struct tty_struct *tty, struct usb_serial_port *port,  		urb->transfer_buffer = kmalloc(URB_TRANSFER_BUFFER_SIZE,  					       GFP_KERNEL);  		if (urb->transfer_buffer == NULL) { -			err("%s no more kernel memory...", __func__); +			dev_err(&port->dev, "%s no more kernel memory...\n", +				__func__);  			goto exit;  		}  	} @@ -712,8 +717,8 @@ static int mos7720_write(struct tty_struct *tty, struct usb_serial_port *port,  	/* send it down the pipe */  	status = usb_submit_urb(urb, GFP_ATOMIC);  	if (status) { -		err("%s - usb_submit_urb(write bulk) failed with status = %d", -		    __func__, status); +		dev_err(&port->dev, "%s - usb_submit_urb(write bulk) failed " +			"with status = %d\n", __func__, status);  		bytes_sent = status;  		goto exit;  	} @@ -973,7 +978,7 @@ static int send_cmd_write_baud_rate(struct moschip_port *mos7720_port,  	/* Calculate the Divisor */  	status = calc_baud_rate_divisor(baudrate, &divisor);  	if (status) { -		err("%s - bad baud rate", __func__); +		dev_err(&port->dev, "%s - bad baud rate\n", __func__);  		return status;  	} @@ -1267,29 +1272,6 @@ static int get_lsr_info(struct tty_struct *tty,  	return 0;  } -/* - * get_number_bytes_avail - get number of bytes available - * - * Purpose: Let user call ioctl to get the count of number of bytes available. - */ -static int get_number_bytes_avail(struct moschip_port *mos7720_port, -				  unsigned int __user *value) -{ -	unsigned int result = 0; -	struct tty_struct *tty = mos7720_port->port->port.tty; - -	if (!tty) -		return -ENOIOCTLCMD; - -	result = tty->read_cnt; - -	dbg("%s(%d) = %d", __func__,  mos7720_port->port->number, result); -	if (copy_to_user(value, &result, sizeof(int))) -		return -EFAULT; - -	return -ENOIOCTLCMD; -} -  static int set_modem_info(struct moschip_port *mos7720_port, unsigned int cmd,  			  unsigned int __user *value)  { @@ -1409,13 +1391,6 @@ static int mos7720_ioctl(struct tty_struct *tty, struct file *file,  	dbg("%s - port %d, cmd = 0x%x", __func__, port->number, cmd);  	switch (cmd) { -	case TIOCINQ: -		/* return number of bytes available */ -		dbg("%s (%d) TIOCINQ", __func__,  port->number); -		return get_number_bytes_avail(mos7720_port, -					      (unsigned int __user *)arg); -		break; -  	case TIOCSERGETLSR:  		dbg("%s (%d) TIOCSERGETLSR", __func__,  port->number);  		return get_lsr_info(tty, mos7720_port, @@ -1506,7 +1481,7 @@ static int mos7720_startup(struct usb_serial *serial)  	/* create our private serial structure */  	mos7720_serial = kzalloc(sizeof(struct moschip_serial), GFP_KERNEL);  	if (mos7720_serial == NULL) { -		err("%s - Out of memory", __func__); +		dev_err(&dev->dev, "%s - Out of memory\n", __func__);  		return -ENOMEM;  	} @@ -1519,7 +1494,7 @@ static int mos7720_startup(struct usb_serial *serial)  	for (i = 0; i < serial->num_ports; ++i) {  		mos7720_port = kzalloc(sizeof(struct moschip_port), GFP_KERNEL);  		if (mos7720_port == NULL) { -			err("%s - Out of memory", __func__); +			dev_err(&dev->dev, "%s - Out of memory\n", __func__);  			usb_set_serial_data(serial, NULL);  			kfree(mos7720_serial);  			return -ENOMEM; @@ -1613,7 +1588,8 @@ static int __init moschip7720_init(void)  	if (retval)  		goto failed_port_device_register; -	info(DRIVER_DESC " " DRIVER_VERSION); +	printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":" +	       DRIVER_DESC "\n");  	/* Register with the usb */  	retval = usb_register(&usb_driver); diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c index 09d82062b97..fda4a6421c4 100644 --- a/drivers/usb/serial/mos7840.c +++ b/drivers/usb/serial/mos7840.c @@ -709,12 +709,13 @@ static void mos7840_bulk_in_callback(struct urb *urb)  	dbg("%s", "Entering ........... \n");  	if (urb->actual_length) { -		tty = mos7840_port->port->port.tty; +		tty = tty_port_tty_get(&mos7840_port->port->port);  		if (tty) {  			tty_buffer_request_room(tty, urb->actual_length);  			tty_insert_flip_string(tty, data, urb->actual_length);  			dbg(" %s \n", data);  			tty_flip_buffer_push(tty); +			tty_kref_put(tty);  		}  		mos7840_port->icount.rx += urb->actual_length;  		smp_wmb(); @@ -773,10 +774,10 @@ static void mos7840_bulk_out_data_callback(struct urb *urb)  	dbg("%s \n", "Entering ........."); -	tty = mos7840_port->port->port.tty; - +	tty = tty_port_tty_get(&mos7840_port->port->port);  	if (tty && mos7840_port->open)  		tty_wakeup(tty); +	tty_kref_put(tty);  } @@ -843,7 +844,7 @@ static int mos7840_open(struct tty_struct *tty,  		mos7840_port->write_urb_pool[j] = urb;  		if (urb == NULL) { -			err("No more urbs???"); +			dev_err(&port->dev, "No more urbs???\n");  			continue;  		} @@ -852,7 +853,9 @@ static int mos7840_open(struct tty_struct *tty,  		if (!urb->transfer_buffer) {  			usb_free_urb(urb);  			mos7840_port->write_urb_pool[j] = NULL; -			err("%s-out of memory for urb buffers.", __func__); +			dev_err(&port->dev, +				"%s-out of memory for urb buffers.\n", +				__func__);  			continue;  		}  	} @@ -1020,8 +1023,8 @@ static int mos7840_open(struct tty_struct *tty,  			    usb_submit_urb(serial->port[0]->interrupt_in_urb,  					   GFP_KERNEL);  			if (response) { -				err("%s - Error %d submitting interrupt urb", -				    __func__, response); +				dev_err(&port->dev, "%s - Error %d submitting " +					"interrupt urb\n", __func__, response);  			}  		} @@ -1054,8 +1057,8 @@ static int mos7840_open(struct tty_struct *tty,  	    port->bulk_in_endpointAddress);  	response = usb_submit_urb(mos7840_port->read_urb, GFP_KERNEL);  	if (response) { -		err("%s - Error %d submitting control urb", __func__, -		    response); +		dev_err(&port->dev, "%s - Error %d submitting control urb\n", +			__func__, response);  	}  	/* initialize our wait queues */ @@ -1491,7 +1494,8 @@ static int mos7840_write(struct tty_struct *tty, struct usb_serial_port *port,  		    kmalloc(URB_TRANSFER_BUFFER_SIZE, GFP_KERNEL);  		if (urb->transfer_buffer == NULL) { -			err("%s no more kernel memory...", __func__); +			dev_err(&port->dev, "%s no more kernel memory...\n", +				__func__);  			goto exit;  		}  	} @@ -1516,8 +1520,8 @@ static int mos7840_write(struct tty_struct *tty, struct usb_serial_port *port,  	if (status) {  		mos7840_port->busy[i] = 0; -		err("%s - usb_submit_urb(write bulk) failed with status = %d", -		    __func__, status); +		dev_err(&port->dev, "%s - usb_submit_urb(write bulk) failed " +			"with status = %d\n", __func__, status);  		bytes_sent = status;  		goto exit;  	} @@ -1855,8 +1859,7 @@ static int mos7840_send_cmd_write_baud_rate(struct moschip_port *mos7840_port,  		/* Calculate the Divisor */  		if (status) { -			err("%s - bad baud rate", __func__); -			dbg("%s\n", "bad baud rate"); +			dev_err(&port->dev, "%s - bad baud rate\n", __func__);  			return status;  		}  		/* Enable access to divisor latch */ @@ -2445,7 +2448,7 @@ static int mos7840_startup(struct usb_serial *serial)  	for (i = 0; i < serial->num_ports; ++i) {  		mos7840_port = kzalloc(sizeof(struct moschip_port), GFP_KERNEL);  		if (mos7840_port == NULL) { -			err("%s - Out of memory", __func__); +			dev_err(&dev->dev, "%s - Out of memory\n", __func__);  			status = -ENOMEM;  			i--; /* don't follow NULL pointer cleaning up */  			goto error; @@ -2742,7 +2745,8 @@ static int __init moschip7840_init(void)  		goto failed_port_device_register;  	dbg("%s\n", "Entring..."); -	info(DRIVER_DESC " " DRIVER_VERSION); +	printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":" +	       DRIVER_DESC "\n");  	/* Register with the usb */  	retval = usb_register(&io_driver); diff --git a/drivers/usb/serial/navman.c b/drivers/usb/serial/navman.c index d6736531a0f..bcdcbb82270 100644 --- a/drivers/usb/serial/navman.c +++ b/drivers/usb/serial/navman.c @@ -64,12 +64,13 @@ static void navman_read_int_callback(struct urb *urb)  	usb_serial_debug_data(debug, &port->dev, __func__,  			      urb->actual_length, data); -	tty = port->port.tty; +	tty = tty_port_tty_get(&port->port);  	if (tty && urb->actual_length) {  		tty_buffer_request_room(tty, urb->actual_length);  		tty_insert_flip_string(tty, data, urb->actual_length);  		tty_flip_buffer_push(tty);  	} +	tty_kref_put(tty);  exit:  	result = usb_submit_urb(urb, GFP_ATOMIC); diff --git a/drivers/usb/serial/omninet.c b/drivers/usb/serial/omninet.c index ae8e227f3db..df653971272 100644 --- a/drivers/usb/serial/omninet.c +++ b/drivers/usb/serial/omninet.c @@ -154,8 +154,8 @@ static int omninet_attach(struct usb_serial *serial)  	od = kmalloc(sizeof(struct omninet_data), GFP_KERNEL);  	if (!od) { -		err("%s- kmalloc(%Zd) failed.", -				__func__, sizeof(struct omninet_data)); +		dev_err(&port->dev, "%s- kmalloc(%Zd) failed.\n", +			__func__, sizeof(struct omninet_data));  		return -ENOMEM;  	}  	usb_set_serial_port_data(port, od); @@ -172,7 +172,7 @@ static int omninet_open(struct tty_struct *tty,  	dbg("%s - port %d", __func__, port->number);  	wport = serial->port[1]; -	wport->port.tty = tty;		/* FIXME */ +	tty_port_tty_set(&wport->port, tty);  	/* Start reading from the device */  	usb_fill_bulk_urb(port->read_urb, serial->dev, @@ -183,8 +183,9 @@ static int omninet_open(struct tty_struct *tty,  			omninet_read_bulk_callback, port);  	result = usb_submit_urb(port->read_urb, GFP_KERNEL);  	if (result) -		err("%s - failed submitting read urb, error %d", -							__func__, result); +		dev_err(&port->dev, +			"%s - failed submitting read urb, error %d\n", +			__func__, result);  	return result;  } @@ -229,9 +230,11 @@ static void omninet_read_bulk_callback(struct urb *urb)  	}  	if (urb->actual_length && header->oh_len) { -		tty_insert_flip_string(port->port.tty, -			data + OMNINET_DATAOFFSET, header->oh_len); -		tty_flip_buffer_push(port->port.tty); +		struct tty_struct *tty = tty_port_tty_get(&port->port); +		tty_insert_flip_string(tty, data + OMNINET_DATAOFFSET, +							header->oh_len); +		tty_flip_buffer_push(tty); +		tty_kref_put(tty);  	}  	/* Continue trying to always read  */ @@ -242,8 +245,9 @@ static void omninet_read_bulk_callback(struct urb *urb)  			omninet_read_bulk_callback, port);  	result = usb_submit_urb(urb, GFP_ATOMIC);  	if (result) -		err("%s - failed resubmitting read urb, error %d", -						__func__, result); +		dev_err(&port->dev, +			"%s - failed resubmitting read urb, error %d\n", +			__func__, result);  	return;  } @@ -296,8 +300,9 @@ static int omninet_write(struct tty_struct *tty, struct usb_serial_port *port,  	result = usb_submit_urb(wport->write_urb, GFP_ATOMIC);  	if (result) {  		wport->write_urb_busy = 0; -		err("%s - failed submitting write urb, error %d", -							__func__, result); +		dev_err(&port->dev, +			"%s - failed submitting write urb, error %d\n", +			__func__, result);  	} else  		result = count; @@ -362,7 +367,8 @@ static int __init omninet_init(void)  	retval = usb_register(&omninet_driver);  	if (retval)  		goto failed_usb_register; -	info(DRIVER_VERSION ":" DRIVER_DESC); +	printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":" +	       DRIVER_DESC "\n");  	return 0;  failed_usb_register:  	usb_serial_deregister(&zyxel_omninet_device); diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 73f8277f88f..6fa1ec441b6 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -79,38 +79,36 @@ static int  option_send_setup(struct tty_struct *tty, struct usb_serial_port *po  #define OPTION_PRODUCT_VIPER			0x6600  #define OPTION_PRODUCT_VIPER_BUS		0x6601  #define OPTION_PRODUCT_GT_MAX_READY		0x6701 -#define OPTION_PRODUCT_GT_MAX			0x6711  #define OPTION_PRODUCT_FUJI_MODEM_LIGHT		0x6721  #define OPTION_PRODUCT_FUJI_MODEM_GT		0x6741  #define OPTION_PRODUCT_FUJI_MODEM_EX		0x6761 -#define OPTION_PRODUCT_FUJI_NETWORK_LIGHT	0x6731 -#define OPTION_PRODUCT_FUJI_NETWORK_GT		0x6751 -#define OPTION_PRODUCT_FUJI_NETWORK_EX		0x6771  #define OPTION_PRODUCT_KOI_MODEM		0x6800 -#define OPTION_PRODUCT_KOI_NETWORK		0x6811  #define OPTION_PRODUCT_SCORPION_MODEM		0x6901 -#define OPTION_PRODUCT_SCORPION_NETWORK		0x6911  #define OPTION_PRODUCT_ETNA_MODEM		0x7001 -#define OPTION_PRODUCT_ETNA_NETWORK		0x7011  #define OPTION_PRODUCT_ETNA_MODEM_LITE		0x7021  #define OPTION_PRODUCT_ETNA_MODEM_GT		0x7041  #define OPTION_PRODUCT_ETNA_MODEM_EX		0x7061 -#define OPTION_PRODUCT_ETNA_NETWORK_LITE	0x7031 -#define OPTION_PRODUCT_ETNA_NETWORK_GT		0x7051 -#define OPTION_PRODUCT_ETNA_NETWORK_EX		0x7071  #define OPTION_PRODUCT_ETNA_KOI_MODEM		0x7100 -#define OPTION_PRODUCT_ETNA_KOI_NETWORK		0x7111  #define HUAWEI_VENDOR_ID			0x12D1  #define HUAWEI_PRODUCT_E600			0x1001  #define HUAWEI_PRODUCT_E220			0x1003  #define HUAWEI_PRODUCT_E220BIS			0x1004  #define HUAWEI_PRODUCT_E1401			0x1401 +#define HUAWEI_PRODUCT_E1402			0x1402  #define HUAWEI_PRODUCT_E1403			0x1403 +#define HUAWEI_PRODUCT_E1404			0x1404  #define HUAWEI_PRODUCT_E1405			0x1405  #define HUAWEI_PRODUCT_E1406			0x1406 +#define HUAWEI_PRODUCT_E1407			0x1407  #define HUAWEI_PRODUCT_E1408			0x1408  #define HUAWEI_PRODUCT_E1409			0x1409 +#define HUAWEI_PRODUCT_E140A			0x140A +#define HUAWEI_PRODUCT_E140B			0x140B +#define HUAWEI_PRODUCT_E140C			0x140C +#define HUAWEI_PRODUCT_E140D			0x140D +#define HUAWEI_PRODUCT_E140E			0x140E +#define HUAWEI_PRODUCT_E140F			0x140F  #define HUAWEI_PRODUCT_E1410			0x1410  #define HUAWEI_PRODUCT_E1411			0x1411  #define HUAWEI_PRODUCT_E1412			0x1412 @@ -121,9 +119,52 @@ static int  option_send_setup(struct tty_struct *tty, struct usb_serial_port *po  #define HUAWEI_PRODUCT_E1417			0x1417  #define HUAWEI_PRODUCT_E1418			0x1418  #define HUAWEI_PRODUCT_E1419			0x1419 +#define HUAWEI_PRODUCT_E141A			0x141A +#define HUAWEI_PRODUCT_E141B			0x141B +#define HUAWEI_PRODUCT_E141C			0x141C +#define HUAWEI_PRODUCT_E141D			0x141D +#define HUAWEI_PRODUCT_E141E			0x141E +#define HUAWEI_PRODUCT_E141F			0x141F +#define HUAWEI_PRODUCT_E1420			0x1420 +#define HUAWEI_PRODUCT_E1421			0x1421 +#define HUAWEI_PRODUCT_E1422			0x1422 +#define HUAWEI_PRODUCT_E1423			0x1423 +#define HUAWEI_PRODUCT_E1424			0x1424 +#define HUAWEI_PRODUCT_E1425			0x1425 +#define HUAWEI_PRODUCT_E1426			0x1426 +#define HUAWEI_PRODUCT_E1427			0x1427 +#define HUAWEI_PRODUCT_E1428			0x1428 +#define HUAWEI_PRODUCT_E1429			0x1429 +#define HUAWEI_PRODUCT_E142A			0x142A +#define HUAWEI_PRODUCT_E142B			0x142B +#define HUAWEI_PRODUCT_E142C			0x142C +#define HUAWEI_PRODUCT_E142D			0x142D +#define HUAWEI_PRODUCT_E142E			0x142E +#define HUAWEI_PRODUCT_E142F			0x142F +#define HUAWEI_PRODUCT_E1430			0x1430 +#define HUAWEI_PRODUCT_E1431			0x1431 +#define HUAWEI_PRODUCT_E1432			0x1432 +#define HUAWEI_PRODUCT_E1433			0x1433 +#define HUAWEI_PRODUCT_E1434			0x1434 +#define HUAWEI_PRODUCT_E1435			0x1435 +#define HUAWEI_PRODUCT_E1436			0x1436 +#define HUAWEI_PRODUCT_E1437			0x1437 +#define HUAWEI_PRODUCT_E1438			0x1438 +#define HUAWEI_PRODUCT_E1439			0x1439 +#define HUAWEI_PRODUCT_E143A			0x143A +#define HUAWEI_PRODUCT_E143B			0x143B +#define HUAWEI_PRODUCT_E143C			0x143C +#define HUAWEI_PRODUCT_E143D			0x143D +#define HUAWEI_PRODUCT_E143E			0x143E +#define HUAWEI_PRODUCT_E143F			0x143F  #define NOVATELWIRELESS_VENDOR_ID		0x1410 +/* YISO PRODUCTS */ + +#define YISO_VENDOR_ID				0x0EAB +#define YISO_PRODUCT_U893			0xC893 +  /* MERLIN EVDO PRODUCTS */  #define NOVATELWIRELESS_PRODUCT_V640		0x1100  #define NOVATELWIRELESS_PRODUCT_V620		0x1110 @@ -218,8 +259,19 @@ static int  option_send_setup(struct tty_struct *tty, struct usb_serial_port *po  /* ZTE PRODUCTS */  #define ZTE_VENDOR_ID				0x19d2  #define ZTE_PRODUCT_MF628			0x0015 +#define ZTE_PRODUCT_MF626			0x0031  #define ZTE_PRODUCT_CDMA_TECH			0xfffe +/* Ericsson products */ +#define ERICSSON_VENDOR_ID			0x0bdb +#define ERICSSON_PRODUCT_F3507G			0x1900 + +/* Pantech products */ +#define PANTECH_VENDOR_ID			0x106c +#define PANTECH_PRODUCT_PC5740			0x3701 +#define PANTECH_PRODUCT_PC5750			0x3702  /* PX-500 */ +#define PANTECH_PRODUCT_UM150			0x3711 +  static struct usb_device_id option_ids[] = {  	{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COLT) },  	{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA) }, @@ -235,36 +287,34 @@ static struct usb_device_id option_ids[] = {  	{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_VIPER) },  	{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_VIPER_BUS) },  	{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_GT_MAX_READY) }, -	{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_GT_MAX) },  	{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUJI_MODEM_LIGHT) },  	{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUJI_MODEM_GT) },  	{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUJI_MODEM_EX) }, -	{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUJI_NETWORK_LIGHT) }, -	{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUJI_NETWORK_GT) }, -	{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUJI_NETWORK_EX) },  	{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_KOI_MODEM) }, -	{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_KOI_NETWORK) },  	{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_SCORPION_MODEM) }, -	{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_SCORPION_NETWORK) },  	{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_ETNA_MODEM) }, -	{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_ETNA_NETWORK) },  	{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_ETNA_MODEM_LITE) },  	{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_ETNA_MODEM_GT) },  	{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_ETNA_MODEM_EX) }, -	{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_ETNA_NETWORK_LITE) }, -	{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_ETNA_NETWORK_GT) }, -	{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_ETNA_NETWORK_EX) },  	{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_ETNA_KOI_MODEM) }, -	{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_ETNA_KOI_NETWORK) },  	{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E600, 0xff, 0xff, 0xff) },  	{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E220, 0xff, 0xff, 0xff) },  	{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E220BIS, 0xff, 0xff, 0xff) },  	{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1401, 0xff, 0xff, 0xff) }, +	{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1402, 0xff, 0xff, 0xff) },  	{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1403, 0xff, 0xff, 0xff) }, +	{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1404, 0xff, 0xff, 0xff) },  	{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1405, 0xff, 0xff, 0xff) },  	{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1406, 0xff, 0xff, 0xff) }, +	{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1407, 0xff, 0xff, 0xff) },  	{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1408, 0xff, 0xff, 0xff) },  	{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1409, 0xff, 0xff, 0xff) }, +	{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E140A, 0xff, 0xff, 0xff) }, +	{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E140B, 0xff, 0xff, 0xff) }, +	{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E140C, 0xff, 0xff, 0xff) }, +	{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E140D, 0xff, 0xff, 0xff) }, +	{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E140E, 0xff, 0xff, 0xff) }, +	{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E140F, 0xff, 0xff, 0xff) },  	{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1410, 0xff, 0xff, 0xff) },  	{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1411, 0xff, 0xff, 0xff) },  	{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1412, 0xff, 0xff, 0xff) }, @@ -275,6 +325,44 @@ static struct usb_device_id option_ids[] = {  	{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1417, 0xff, 0xff, 0xff) },  	{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1418, 0xff, 0xff, 0xff) },  	{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1419, 0xff, 0xff, 0xff) }, +	{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E141A, 0xff, 0xff, 0xff) }, +	{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E141B, 0xff, 0xff, 0xff) }, +	{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E141C, 0xff, 0xff, 0xff) }, +	{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E141D, 0xff, 0xff, 0xff) }, +	{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E141E, 0xff, 0xff, 0xff) }, +	{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E141F, 0xff, 0xff, 0xff) }, +	{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1420, 0xff, 0xff, 0xff) }, +	{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1421, 0xff, 0xff, 0xff) }, +	{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1422, 0xff, 0xff, 0xff) }, +	{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1423, 0xff, 0xff, 0xff) }, +	{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1424, 0xff, 0xff, 0xff) }, +	{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1425, 0xff, 0xff, 0xff) }, +	{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1426, 0xff, 0xff, 0xff) }, +	{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1427, 0xff, 0xff, 0xff) }, +	{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1428, 0xff, 0xff, 0xff) }, +	{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1429, 0xff, 0xff, 0xff) }, +	{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E142A, 0xff, 0xff, 0xff) }, +	{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E142B, 0xff, 0xff, 0xff) }, +	{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E142C, 0xff, 0xff, 0xff) }, +	{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E142D, 0xff, 0xff, 0xff) }, +	{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E142E, 0xff, 0xff, 0xff) }, +	{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E142F, 0xff, 0xff, 0xff) }, +	{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1430, 0xff, 0xff, 0xff) }, +	{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1431, 0xff, 0xff, 0xff) }, +	{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1432, 0xff, 0xff, 0xff) }, +	{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1433, 0xff, 0xff, 0xff) }, +	{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1434, 0xff, 0xff, 0xff) }, +	{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1435, 0xff, 0xff, 0xff) }, +	{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1436, 0xff, 0xff, 0xff) }, +	{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1437, 0xff, 0xff, 0xff) }, +	{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1438, 0xff, 0xff, 0xff) }, +	{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1439, 0xff, 0xff, 0xff) }, +	{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E143A, 0xff, 0xff, 0xff) }, +	{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E143B, 0xff, 0xff, 0xff) }, +	{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E143C, 0xff, 0xff, 0xff) }, +	{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E143D, 0xff, 0xff, 0xff) }, +	{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E143E, 0xff, 0xff, 0xff) }, +	{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E143F, 0xff, 0xff, 0xff) },  	{ USB_DEVICE(AMOI_VENDOR_ID, AMOI_PRODUCT_9508) },  	{ USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_V640) }, /* Novatel Merlin V640/XV620 */  	{ USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_V620) }, /* Novatel Merlin V620/S620 */ @@ -318,12 +406,14 @@ static struct usb_device_id option_ids[] = {  	{ USB_DEVICE(DELL_VENDOR_ID, 0x8136) },	/* Dell Wireless HSDPA 5520 == Novatel Expedite EU860D */  	{ USB_DEVICE(DELL_VENDOR_ID, 0x8137) },	/* Dell Wireless HSDPA 5520 */  	{ USB_DEVICE(DELL_VENDOR_ID, 0x8138) },	/* Dell Wireless 5520 Voda I Mobile Broadband (3G HSDPA) Minicard */ -	{ USB_DEVICE(ANYDATA_VENDOR_ID, ANYDATA_PRODUCT_ADU_E100A) }, +	{ USB_DEVICE(DELL_VENDOR_ID, 0x8147) },	/* Dell Wireless 5530 Mobile Broadband (3G HSPA) Mini-Card */ +	{ USB_DEVICE(ANYDATA_VENDOR_ID, ANYDATA_PRODUCT_ADU_E100A) },	/* ADU-E100, ADU-310 */  	{ USB_DEVICE(ANYDATA_VENDOR_ID, ANYDATA_PRODUCT_ADU_500A) },  	{ USB_DEVICE(ANYDATA_VENDOR_ID, ANYDATA_PRODUCT_ADU_620UW) },  	{ USB_DEVICE(AXESSTEL_VENDOR_ID, AXESSTEL_PRODUCT_MV110H) },  	{ USB_DEVICE(ONDA_VENDOR_ID, ONDA_PRODUCT_MSA501HS) },  	{ USB_DEVICE(ONDA_VENDOR_ID, ONDA_PRODUCT_ET502HS) }, +	{ USB_DEVICE(YISO_VENDOR_ID, YISO_PRODUCT_U893) },  	{ USB_DEVICE(BANDRICH_VENDOR_ID, BANDRICH_PRODUCT_C100_1) },  	{ USB_DEVICE(BANDRICH_VENDOR_ID, BANDRICH_PRODUCT_C100_2) },  	{ USB_DEVICE(BANDRICH_VENDOR_ID, BANDRICH_PRODUCT_1004) }, @@ -347,8 +437,13 @@ static struct usb_device_id option_ids[] = {  	{ USB_DEVICE(QUALCOMM_VENDOR_ID, 0x6613)}, /* Onda H600/ZTE MF330 */  	{ USB_DEVICE(MAXON_VENDOR_ID, 0x6280) }, /* BP3-USB & BP3-EXT HSDPA */  	{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_UC864E) }, +	{ USB_DEVICE(ZTE_VENDOR_ID, ZTE_PRODUCT_MF626) },  	{ USB_DEVICE(ZTE_VENDOR_ID, ZTE_PRODUCT_MF628) },  	{ USB_DEVICE(ZTE_VENDOR_ID, ZTE_PRODUCT_CDMA_TECH) }, +	{ USB_DEVICE(ERICSSON_VENDOR_ID, ERICSSON_PRODUCT_F3507G) }, +	{ USB_DEVICE(PANTECH_VENDOR_ID, PANTECH_PRODUCT_PC5740) }, +	{ USB_DEVICE(PANTECH_VENDOR_ID, PANTECH_PRODUCT_PC5750) }, +	{ USB_DEVICE(PANTECH_VENDOR_ID, PANTECH_PRODUCT_UM150) },  	{ } /* Terminating entry */  };  MODULE_DEVICE_TABLE(usb, option_ids); @@ -427,7 +522,8 @@ static int __init option_init(void)  	if (retval)  		goto failed_driver_register; -	info(DRIVER_DESC ": " DRIVER_VERSION); +	printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":" +	       DRIVER_DESC "\n");  	return 0; @@ -571,14 +667,14 @@ static void option_indat_callback(struct urb *urb)  		dbg("%s: nonzero status: %d on endpoint %02x.",  		    __func__, status, endpoint);  	} else { -		tty = port->port.tty; +		tty = tty_port_tty_get(&port->port);  		if (urb->actual_length) {  			tty_buffer_request_room(tty, urb->actual_length);  			tty_insert_flip_string(tty, data, urb->actual_length);  			tty_flip_buffer_push(tty); -		} else { +		} else   			dbg("%s: empty read urb received", __func__); -		} +		tty_kref_put(tty);  		/* Resubmit urb so we continue receiving */  		if (port->port.count && status != -ESHUTDOWN) { @@ -647,9 +743,13 @@ static void option_instat_callback(struct urb *urb)  			portdata->dsr_state = ((signals & 0x02) ? 1 : 0);  			portdata->ri_state = ((signals & 0x08) ? 1 : 0); -			if (port->port.tty && !C_CLOCAL(port->port.tty) && -					old_dcd_state && !portdata->dcd_state) -				tty_hangup(port->port.tty); +			if (old_dcd_state && !portdata->dcd_state) { +				struct tty_struct *tty = +						tty_port_tty_get(&port->port); +				if (tty && !C_CLOCAL(tty)) +					tty_hangup(tty); +				tty_kref_put(tty); +			}  		} else {  			dbg("%s: type %x req %x", __func__,  				req_pkt->bRequestType, req_pkt->bRequest); @@ -793,7 +893,7 @@ static void option_close(struct tty_struct *tty,  		for (i = 0; i < N_OUT_URB; i++)  			usb_kill_urb(portdata->out_urbs[i]);  	} -	port->port.tty = NULL;	/* FIXME */ +	tty_port_tty_set(&port->port, NULL);  }  /* Helper functions used by option_setup_urbs */ diff --git a/drivers/usb/serial/oti6858.c b/drivers/usb/serial/oti6858.c index 81db5715ee2..ba551f00f16 100644 --- a/drivers/usb/serial/oti6858.c +++ b/drivers/usb/serial/oti6858.c @@ -224,10 +224,6 @@ struct oti6858_private {  	struct usb_serial_port *port;   /* USB port with which associated */  }; -#undef dbg -/* #define dbg(format, arg...) printk(KERN_INFO "%s: " format "\n", __FILE__, ## arg) */ -#define dbg(format, arg...) printk(KERN_INFO "" format "\n", ## arg) -  static void setup_line(struct work_struct *work)  {  	struct oti6858_private *priv = container_of(work, @@ -1002,11 +998,12 @@ static void oti6858_read_bulk_callback(struct urb *urb)  		return;  	} -	tty = port->port.tty; +	tty = tty_port_tty_get(&port->port);  	if (tty != NULL && urb->actual_length > 0) {  		tty_insert_flip_string(tty, data, urb->actual_length);  		tty_flip_buffer_push(tty);  	} +	tty_kref_put(tty);  	/* schedule the interrupt urb if we are still open */  	if (port->port.count != 0) { diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index 1ede1441cb1..491c8857b64 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -154,7 +154,6 @@ struct pl2303_private {  	wait_queue_head_t delta_msr_wait;  	u8 line_control;  	u8 line_status; -	u8 termios_initialized;  	enum pl2303_type type;  }; @@ -526,16 +525,6 @@ static void pl2303_set_termios(struct tty_struct *tty,  	dbg("%s -  port %d", __func__, port->number); -	spin_lock_irqsave(&priv->lock, flags); -	if (!priv->termios_initialized) { -		*(tty->termios) = tty_std_termios; -		tty->termios->c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; -		tty->termios->c_ispeed = 9600; -		tty->termios->c_ospeed = 9600; -		priv->termios_initialized = 1; -	} -	spin_unlock_irqrestore(&priv->lock, flags); -  	/* The PL2303 is reported to lose bytes if you change  	   serial settings even to the same values as before. Thus  	   we actually need to filter in this specific case */ @@ -1057,7 +1046,7 @@ static void pl2303_read_bulk_callback(struct urb *urb)  		tty_flag = TTY_FRAME;  	dbg("%s - tty_flag = %d", __func__, tty_flag); -	tty = port->port.tty; +	tty = tty_port_tty_get(&port->port);  	if (tty && urb->actual_length) {  		tty_buffer_request_room(tty, urb->actual_length + 1);  		/* overrun is special, not associated with a char */ @@ -1067,7 +1056,7 @@ static void pl2303_read_bulk_callback(struct urb *urb)  			tty_insert_flip_char(tty, data[i], tty_flag);  		tty_flip_buffer_push(tty);  	} - +	tty_kref_put(tty);  	/* Schedule the next read _if_ we are still open */  	if (port->port.count) {  		urb->dev = port->serial->dev; @@ -1158,7 +1147,7 @@ static int __init pl2303_init(void)  	retval = usb_register(&pl2303_driver);  	if (retval)  		goto failed_usb_register; -	info(DRIVER_DESC); +	printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_DESC "\n");  	return 0;  failed_usb_register:  	usb_serial_deregister(&pl2303_device); diff --git a/drivers/usb/serial/safe_serial.c b/drivers/usb/serial/safe_serial.c index def52d07a4e..4b463cd140e 100644 --- a/drivers/usb/serial/safe_serial.c +++ b/drivers/usb/serial/safe_serial.c @@ -217,6 +217,7 @@ static void safe_read_bulk_callback(struct urb *urb)  	struct usb_serial_port *port =  urb->context;  	unsigned char *data = urb->transfer_buffer;  	unsigned char length = urb->actual_length; +	struct tty_struct *tty;  	int result;  	int status = urb->status; @@ -242,28 +243,31 @@ static void safe_read_bulk_callback(struct urb *urb)  		printk("\n");  	}  #endif +	tty = tty_port_tty_get(&port->port);  	if (safe) {  		__u16 fcs;  		fcs = fcs_compute10(data, length, CRC10_INITFCS);  		if (!fcs) {  			int actual_length = data[length - 2] >> 2;  			if (actual_length <= (length - 2)) { -				info("%s - actual: %d", __func__, -							actual_length); -				tty_insert_flip_string(port->port.tty, +				dev_info(&urb->dev->dev, "%s - actual: %d\n", +					 __func__, actual_length); +				tty_insert_flip_string(tty,  							data, actual_length); -				tty_flip_buffer_push(port->port.tty); +				tty_flip_buffer_push(tty);  			} else { -				err("%s - inconsistent lengths %d:%d", +				dev_err(&port->dev, +					"%s - inconsistent lengths %d:%d\n",  					__func__, actual_length, length);  			}  		} else { -			err("%s - bad CRC %x", __func__, fcs); +			dev_err(&port->dev, "%s - bad CRC %x\n", __func__, fcs);  		}  	} else { -		tty_insert_flip_string(port->port.tty, data, length); -		tty_flip_buffer_push(port->port.tty); +		tty_insert_flip_string(tty, data, length); +		tty_flip_buffer_push(tty);  	} +	tty_kref_put(tty);  	/* Continue trying to always read  */  	usb_fill_bulk_urb(urb, port->serial->dev, @@ -274,8 +278,9 @@ static void safe_read_bulk_callback(struct urb *urb)  	result = usb_submit_urb(urb, GFP_ATOMIC);  	if (result) -		err("%s - failed resubmitting read urb, error %d", -							__func__, result); +		dev_err(&port->dev, +			"%s - failed resubmitting read urb, error %d\n", +			__func__, result);  		/* FIXME: Need a mechanism to retry later if this happens */  } @@ -366,8 +371,9 @@ static int safe_write(struct tty_struct *tty, struct usb_serial_port *port,  	result = usb_submit_urb(port->write_urb, GFP_KERNEL);  	if (result) {  		port->write_urb_busy = 0; -		err("%s - failed submitting write urb, error %d", -							__func__, result); +		dev_err(&port->dev, +			"%s - failed submitting write urb, error %d\n", +			__func__, result);  		return 0;  	}  	dbg("%s urb: %p submitted", __func__, port->write_urb); @@ -425,14 +431,13 @@ static int __init safe_init(void)  {  	int i, retval; -	info(DRIVER_VERSION " " DRIVER_AUTHOR); -	info(DRIVER_DESC); -	info("vendor: %x product: %x safe: %d padded: %d\n", -					vendor, product, safe, padded); +	printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":" +	       DRIVER_DESC "\n");  	/* if we have vendor / product parameters patch them into id list */  	if (vendor || product) { -		info("vendor: %x product: %x\n", vendor, product); +		printk(KERN_INFO KBUILD_MODNAME ": vendor: %x product: %x\n", +		       vendor, product);  		for (i = 0; i < ARRAY_SIZE(id_table); i++) {  			if (!id_table[i].idVendor && !id_table[i].idProduct) { diff --git a/drivers/usb/serial/sierra.c b/drivers/usb/serial/sierra.c index ea1a103c99b..0f2b67244af 100644 --- a/drivers/usb/serial/sierra.c +++ b/drivers/usb/serial/sierra.c @@ -247,7 +247,7 @@ static int sierra_send_setup(struct tty_struct *tty,  	struct sierra_port_private *portdata;  	__u16 interface = 0; -	dbg("%s", __func__); +	dev_dbg(&port->dev, "%s", __func__);  	portdata = usb_get_serial_port_data(port); @@ -284,7 +284,7 @@ static int sierra_send_setup(struct tty_struct *tty,  static void sierra_set_termios(struct tty_struct *tty,  		struct usb_serial_port *port, struct ktermios *old_termios)  { -	dbg("%s", __func__); +	dev_dbg(&port->dev, "%s", __func__);  	tty_termios_copy_hw(tty->termios, old_termios);  	sierra_send_setup(tty, port);  } @@ -295,6 +295,7 @@ static int sierra_tiocmget(struct tty_struct *tty, struct file *file)  	unsigned int value;  	struct sierra_port_private *portdata; +	dev_dbg(&port->dev, "%s", __func__);  	portdata = usb_get_serial_port_data(port);  	value = ((portdata->rts_state) ? TIOCM_RTS : 0) | @@ -334,14 +335,14 @@ static void sierra_outdat_callback(struct urb *urb)  	int status = urb->status;  	unsigned long flags; -	dbg("%s - port %d", __func__, port->number); +	dev_dbg(&port->dev, "%s - port %d", __func__, port->number);  	/* free up the transfer buffer, as usb_free_urb() does not do this */  	kfree(urb->transfer_buffer);  	if (status) -		dbg("%s - nonzero write bulk status received: %d", -		    __func__, status); +		dev_dbg(&port->dev, "%s - nonzero write bulk status " +		    "received: %d", __func__, status);  	spin_lock_irqsave(&portdata->lock, flags);  	--portdata->outstanding_urbs; @@ -363,12 +364,12 @@ static int sierra_write(struct tty_struct *tty, struct usb_serial_port *port,  	portdata = usb_get_serial_port_data(port); -	dbg("%s: write (%d chars)", __func__, count); +	dev_dbg(&port->dev, "%s: write (%d chars)", __func__, count);  	spin_lock_irqsave(&portdata->lock, flags);  	if (portdata->outstanding_urbs > N_OUT_URB) {  		spin_unlock_irqrestore(&portdata->lock, flags); -		dbg("%s - write limit hit\n", __func__); +		dev_dbg(&port->dev, "%s - write limit hit\n", __func__);  		return 0;  	}  	portdata->outstanding_urbs++; @@ -437,17 +438,18 @@ static void sierra_indat_callback(struct urb *urb)  	port =  urb->context;  	if (status) { -		dbg("%s: nonzero status: %d on endpoint %02x.", -		    __func__, status, endpoint); +		dev_dbg(&port->dev, "%s: nonzero status: %d on" +		    " endpoint %02x.", __func__, status, endpoint);  	} else { -		tty = port->port.tty;  		if (urb->actual_length) { +		tty = tty_port_tty_get(&port->port);  			tty_buffer_request_room(tty, urb->actual_length);  			tty_insert_flip_string(tty, data, urb->actual_length);  			tty_flip_buffer_push(tty); -		} else { -			dbg("%s: empty read urb received", __func__); -		} +			tty_kref_put(tty); +		} else +			dev_dbg(&port->dev, "%s: empty read urb" +				" received", __func__);  		/* Resubmit urb so we continue receiving */  		if (port->port.count && status != -ESHUTDOWN) { @@ -468,15 +470,17 @@ static void sierra_instat_callback(struct urb *urb)  	struct sierra_port_private *portdata = usb_get_serial_port_data(port);  	struct usb_serial *serial = port->serial; -	dbg("%s", __func__); -	dbg("%s: urb %p port %p has data %p", __func__, urb, port, portdata); +	dev_dbg(&port->dev, "%s", __func__); +	dev_dbg(&port->dev, "%s: urb %p port %p has data %p", __func__, +		urb, port, portdata);  	if (status == 0) {  		struct usb_ctrlrequest *req_pkt =  				(struct usb_ctrlrequest *)urb->transfer_buffer;  		if (!req_pkt) { -			dbg("%s: NULL req_pkt\n", __func__); +			dev_dbg(&port->dev, "%s: NULL req_pkt\n", +				__func__);  			return;  		}  		if ((req_pkt->bRequestType == 0xA1) && @@ -485,8 +489,10 @@ static void sierra_instat_callback(struct urb *urb)  			unsigned char signals = *((unsigned char *)  					urb->transfer_buffer +  					sizeof(struct usb_ctrlrequest)); +			struct tty_struct *tty; -			dbg("%s: signal x%x", __func__, signals); +			dev_dbg(&port->dev, "%s: signal x%x", __func__, +				signals);  			old_dcd_state = portdata->dcd_state;  			portdata->cts_state = 1; @@ -494,23 +500,26 @@ static void sierra_instat_callback(struct urb *urb)  			portdata->dsr_state = ((signals & 0x02) ? 1 : 0);  			portdata->ri_state = ((signals & 0x08) ? 1 : 0); -			if (port->port.tty && !C_CLOCAL(port->port.tty) && +			tty = tty_port_tty_get(&port->port); +			if (tty && !C_CLOCAL(tty) &&  					old_dcd_state && !portdata->dcd_state) -				tty_hangup(port->port.tty); +				tty_hangup(tty); +			tty_kref_put(tty);  		} else { -			dbg("%s: type %x req %x", __func__, -				req_pkt->bRequestType, req_pkt->bRequest); +			dev_dbg(&port->dev, "%s: type %x req %x", +				__func__, req_pkt->bRequestType, +				req_pkt->bRequest);  		}  	} else -		dbg("%s: error %d", __func__, status); +		dev_dbg(&port->dev, "%s: error %d", __func__, status);  	/* Resubmit urb so we continue receiving IRQ data */  	if (status != -ESHUTDOWN) {  		urb->dev = serial->dev;  		err = usb_submit_urb(urb, GFP_ATOMIC);  		if (err) -			dbg("%s: resubmit intr urb failed. (%d)", -				__func__, err); +			dev_dbg(&port->dev, "%s: resubmit intr urb " +				"failed. (%d)",	__func__, err);  	}  } @@ -520,14 +529,14 @@ static int sierra_write_room(struct tty_struct *tty)  	struct sierra_port_private *portdata = usb_get_serial_port_data(port);  	unsigned long flags; -	dbg("%s - port %d", __func__, port->number); +	dev_dbg(&port->dev, "%s - port %d", __func__, port->number);  	/* try to give a good number back based on if we have any free urbs at  	 * this point in time */  	spin_lock_irqsave(&portdata->lock, flags);  	if (portdata->outstanding_urbs > N_OUT_URB * 2 / 3) {  		spin_unlock_irqrestore(&portdata->lock, flags); -		dbg("%s - write limit hit\n", __func__); +		dev_dbg(&port->dev, "%s - write limit hit\n", __func__);  		return 0;  	}  	spin_unlock_irqrestore(&portdata->lock, flags); @@ -546,7 +555,7 @@ static int sierra_open(struct tty_struct *tty,  	portdata = usb_get_serial_port_data(port); -	dbg("%s", __func__); +	dev_dbg(&port->dev, "%s", __func__);  	/* Set some sane defaults */  	portdata->rts_state = 1; @@ -558,8 +567,8 @@ static int sierra_open(struct tty_struct *tty,  		if (!urb)  			continue;  		if (urb->dev != serial->dev) { -			dbg("%s: dev %p != %p", __func__, -				urb->dev, serial->dev); +			dev_dbg(&port->dev, "%s: dev %p != %p", +				 __func__, urb->dev, serial->dev);  			continue;  		} @@ -598,7 +607,7 @@ static void sierra_close(struct tty_struct *tty,  	struct usb_serial *serial = port->serial;  	struct sierra_port_private *portdata; -	dbg("%s", __func__); +	dev_dbg(&port->dev, "%s", __func__);  	portdata = usb_get_serial_port_data(port);  	portdata->rts_state = 0; @@ -616,8 +625,7 @@ static void sierra_close(struct tty_struct *tty,  	}  	usb_kill_urb(port->interrupt_in_urb); - -	port->port.tty = NULL;	/* FIXME */ +	tty_port_tty_set(&port->port, NULL);  }  static int sierra_startup(struct usb_serial *serial) @@ -628,7 +636,7 @@ static int sierra_startup(struct usb_serial *serial)  	int i;  	int j; -	dbg("%s", __func__); +	dev_dbg(&serial->dev->dev, "%s", __func__);  	/* Set Device mode to D0 */  	sierra_set_power_state(serial->dev, 0x0000); @@ -642,8 +650,9 @@ static int sierra_startup(struct usb_serial *serial)  		port = serial->port[i];  		portdata = kzalloc(sizeof(*portdata), GFP_KERNEL);  		if (!portdata) { -			dbg("%s: kmalloc for sierra_port_private (%d) failed!.", -					__func__, i); +			dev_dbg(&port->dev, "%s: kmalloc for " +				"sierra_port_private (%d) failed!.", +				__func__, i);  			return -ENOMEM;  		}  		spin_lock_init(&portdata->lock); @@ -663,8 +672,8 @@ static int sierra_startup(struct usb_serial *serial)  		for (j = 0; j < N_IN_URB; ++j) {  			urb = usb_alloc_urb(0, GFP_KERNEL);  			if (urb == NULL) { -				dbg("%s: alloc for in port failed.", -				    __func__); +				dev_dbg(&port->dev, "%s: alloc for in " +					"port failed.", __func__);  				continue;  			}  			/* Fill URB using supplied data. */ @@ -686,7 +695,7 @@ static void sierra_shutdown(struct usb_serial *serial)  	struct usb_serial_port *port;  	struct sierra_port_private *portdata; -	dbg("%s", __func__); +	dev_dbg(&serial->dev->dev, "%s", __func__);  	for (i = 0; i < serial->num_ports; ++i) {  		port = serial->port[i]; @@ -741,7 +750,8 @@ static int __init sierra_init(void)  	if (retval)  		goto failed_driver_register; -	info(DRIVER_DESC ": " DRIVER_VERSION); +	printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":" +	       DRIVER_DESC "\n");  	return 0; diff --git a/drivers/usb/serial/spcp8x5.c b/drivers/usb/serial/spcp8x5.c index 283cf6b36b2..a65bc2bd8e7 100644 --- a/drivers/usb/serial/spcp8x5.c +++ b/drivers/usb/serial/spcp8x5.c @@ -589,8 +589,8 @@ static void spcp8x5_set_termios(struct tty_struct *tty,  	case 1000000:  			buf[0] = 0x0b;	break;  	default: -		err("spcp825 driver does not support the baudrate " -		    "requested, using default of 9600."); +		dev_err(&port->dev, "spcp825 driver does not support the " +			"baudrate requested, using default of 9600.\n");  	}  	/* Set Data Length : 00:5bit, 01:6bit, 10:7bit, 11:8bit */ @@ -629,7 +629,8 @@ static void spcp8x5_set_termios(struct tty_struct *tty,  			    SET_UART_FORMAT_TYPE, SET_UART_FORMAT,  			    uartdata, 0, NULL, 0, 100);  	if (i < 0) -		err("Set UART format %#x failed (error = %d)", uartdata, i); +		dev_err(&port->dev, "Set UART format %#x failed (error = %d)\n", +			uartdata, i);  	dbg("0x21:0x40:0:0  %d\n", i);  	if (cflag & CRTSCTS) { @@ -755,7 +756,7 @@ static void spcp8x5_read_bulk_callback(struct urb *urb)  		tty_flag = TTY_FRAME;  	dev_dbg(&port->dev, "tty_flag = %d\n", tty_flag); -	tty = port->port.tty; +	tty = tty_port_tty_get(&port->port);  	if (tty && urb->actual_length) {  		tty_buffer_request_room(tty, urb->actual_length + 1);  		/* overrun is special, not associated with a char */ @@ -765,6 +766,7 @@ static void spcp8x5_read_bulk_callback(struct urb *urb)  			tty_insert_flip_char(tty, data[i], tty_flag);  		tty_flip_buffer_push(tty);  	} +	tty_kref_put(tty);  	/* Schedule the next read _if_ we are still open */  	if (port->port.count) { @@ -1053,7 +1055,8 @@ static int __init spcp8x5_init(void)  	retval = usb_register(&spcp8x5_driver);  	if (retval)  		goto failed_usb_register; -	info(DRIVER_DESC " " DRIVER_VERSION); +	printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":" +	       DRIVER_DESC "\n");  	return 0;  failed_usb_register:  	usb_serial_deregister(&spcp8x5_device); diff --git a/drivers/usb/serial/ti_usb_3410_5052.c b/drivers/usb/serial/ti_usb_3410_5052.c index 9a3e495c769..31c42d1cae1 100644 --- a/drivers/usb/serial/ti_usb_3410_5052.c +++ b/drivers/usb/serial/ti_usb_3410_5052.c @@ -85,7 +85,6 @@  #include <linux/uaccess.h>  #include <linux/usb.h>  #include <linux/usb/serial.h> -#include <linux/firmware.h>  #include "ti_usb_3410_5052.h" @@ -179,7 +178,7 @@ static int ti_set_mcr(struct ti_port *tport, unsigned int mcr);  static int ti_get_lsr(struct ti_port *tport);  static int ti_get_serial_info(struct ti_port *tport,  	struct serial_struct __user *ret_arg); -static int ti_set_serial_info(struct ti_port *tport, +static int ti_set_serial_info(struct tty_struct *tty, struct ti_port *tport,  	struct serial_struct __user *new_arg);  static void ti_handle_new_msr(struct ti_port *tport, __u8 msr); @@ -383,7 +382,8 @@ static int __init ti_init(void)  	if (ret)  		goto failed_usb; -	info(TI_DRIVER_DESC " " TI_DRIVER_VERSION); +	printk(KERN_INFO KBUILD_MODNAME ": " TI_DRIVER_VERSION ":" +	       TI_DRIVER_DESC "\n");  	return 0; @@ -857,8 +857,8 @@ static int ti_ioctl(struct tty_struct *tty, struct file *file,  				(struct serial_struct __user *)arg);  	case TIOCSSERIAL:  		dbg("%s - (%d) TIOCSSERIAL", __func__, port->number); -		return ti_set_serial_info(tport, -					(struct serial_struct __user *)arg); +		return ti_set_serial_info(tty, tport, +				(struct serial_struct __user *)arg);  	case TIOCMIWAIT:  		dbg("%s - (%d) TIOCMIWAIT", __func__, port->number);  		cprev = tport->tp_icount; @@ -1211,6 +1211,7 @@ static void ti_bulk_in_callback(struct urb *urb)  	struct device *dev = &urb->dev->dev;  	int status = urb->status;  	int retval = 0; +	struct tty_struct *tty;  	dbg("%s", __func__); @@ -1239,20 +1240,22 @@ static void ti_bulk_in_callback(struct urb *urb)  		return;  	} -	if (port->port.tty && urb->actual_length) { +	tty = tty_port_tty_get(&port->port); +	if (tty && urb->actual_length) {  		usb_serial_debug_data(debug, dev, __func__,  			urb->actual_length, urb->transfer_buffer);  		if (!tport->tp_is_open)  			dbg("%s - port closed, dropping data", __func__);  		else -			ti_recv(&urb->dev->dev, port->port.tty, +			ti_recv(&urb->dev->dev, tty,  						urb->transfer_buffer,  						urb->actual_length);  		spin_lock(&tport->tp_lock);  		tport->tp_icount.rx += urb->actual_length;  		spin_unlock(&tport->tp_lock); +		tty_kref_put(tty);  	}  exit: @@ -1330,7 +1333,7 @@ static void ti_send(struct ti_port *tport)  {  	int count, result;  	struct usb_serial_port *port = tport->tp_port; -	struct tty_struct *tty = port->port.tty;	/* FIXME */ +	struct tty_struct *tty = tty_port_tty_get(&port->port);	/* FIXME */  	unsigned long flags; @@ -1338,19 +1341,15 @@ static void ti_send(struct ti_port *tport)  	spin_lock_irqsave(&tport->tp_lock, flags); -	if (tport->tp_write_urb_in_use) { -		spin_unlock_irqrestore(&tport->tp_lock, flags); -		return; -	} +	if (tport->tp_write_urb_in_use) +		goto unlock;  	count = ti_buf_get(tport->tp_write_buf,  				port->write_urb->transfer_buffer,  				port->bulk_out_size); -	if (count == 0) { -		spin_unlock_irqrestore(&tport->tp_lock, flags); -		return; -	} +	if (count == 0) +		goto unlock;  	tport->tp_write_urb_in_use = 1; @@ -1380,7 +1379,13 @@ static void ti_send(struct ti_port *tport)  	/* more room in the buffer for new writes, wakeup */  	if (tty)  		tty_wakeup(tty); +	tty_kref_put(tty);  	wake_up_interruptible(&tport->tp_write_wait); +	return; +unlock: +	spin_unlock_irqrestore(&tport->tp_lock, flags); +	tty_kref_put(tty); +	return;  } @@ -1464,20 +1469,16 @@ static int ti_get_serial_info(struct ti_port *tport,  } -static int ti_set_serial_info(struct ti_port *tport, +static int ti_set_serial_info(struct tty_struct *tty, struct ti_port *tport,  	struct serial_struct __user *new_arg)  { -	struct usb_serial_port *port = tport->tp_port;  	struct serial_struct new_serial;  	if (copy_from_user(&new_serial, new_arg, sizeof(new_serial)))  		return -EFAULT;  	tport->tp_flags = new_serial.flags & TI_SET_SERIAL_FLAGS; -	/* FIXME */ -	if (port->port.tty) -		port->port.tty->low_latency = -			(tport->tp_flags & ASYNC_LOW_LATENCY) ? 1 : 0; +	tty->low_latency = (tport->tp_flags & ASYNC_LOW_LATENCY) ? 1 : 0;  	tport->tp_closing_wait = new_serial.closing_wait;  	return 0; @@ -1510,7 +1511,7 @@ static void ti_handle_new_msr(struct ti_port *tport, __u8 msr)  	tport->tp_msr = msr & TI_MSR_MASK;  	/* handle CTS flow control */ -	tty = tport->tp_port->port.tty; +	tty = tty_port_tty_get(&tport->tp_port->port);  	if (tty && C_CRTSCTS(tty)) {  		if (msr & TI_MSR_CTS) {  			tty->hw_stopped = 0; @@ -1519,6 +1520,7 @@ static void ti_handle_new_msr(struct ti_port *tport, __u8 msr)  			tty->hw_stopped = 1;  		}  	} +	tty_kref_put(tty);  } diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index 4f7f9e3ae0a..794b5ffe439 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -214,7 +214,7 @@ static int serial_open (struct tty_struct *tty, struct file *filp)  	/* set up our port structure making the tty driver  	 * remember our port object, and us it */  	tty->driver_data = port; -	port->port.tty = tty; +	tty_port_tty_set(&port->port, tty);  	if (port->port.count == 1) { @@ -246,7 +246,7 @@ bailout_module_put:  bailout_mutex_unlock:  	port->port.count = 0;  	tty->driver_data = NULL; -	port->port.tty = NULL; +	tty_port_tty_set(&port->port, NULL);  	mutex_unlock(&port->mutex);  bailout_kref_put:  	usb_serial_put(serial); @@ -276,10 +276,12 @@ static void serial_close(struct tty_struct *tty, struct file *filp)  		port->serial->type->close(tty, port, filp);  	if (port->port.count == (port->console? 1 : 0)) { -		if (port->port.tty) { -			if (port->port.tty->driver_data) -				port->port.tty->driver_data = NULL; -			port->port.tty = NULL; +		struct tty_struct *tty = tty_port_tty_get(&port->port); +		if (tty) { +			if (tty->driver_data) +				tty->driver_data = NULL; +			tty_port_tty_set(&port->port, NULL); +			tty_kref_put(tty);  		}  	} @@ -508,11 +510,12 @@ static void usb_serial_port_work(struct work_struct *work)  	if (!port)  		return; -	tty = port->port.tty; +	tty = tty_port_tty_get(&port->port);  	if (!tty)  		return;  	tty_wakeup(tty); +	tty_kref_put(tty);  }  static void port_release(struct device *dev) @@ -819,6 +822,7 @@ int usb_serial_probe(struct usb_interface *interface,  		port = kzalloc(sizeof(struct usb_serial_port), GFP_KERNEL);  		if (!port)  			goto probe_error; +		tty_port_init(&port->port);  		port->serial = serial;  		spin_lock_init(&port->lock);  		mutex_init(&port->mutex); @@ -1040,8 +1044,11 @@ void usb_serial_disconnect(struct usb_interface *interface)  	for (i = 0; i < serial->num_ports; ++i) {  		port = serial->port[i];  		if (port) { -			if (port->port.tty) -				tty_hangup(port->port.tty); +			struct tty_struct *tty = tty_port_tty_get(&port->port); +			if (tty) { +				tty_hangup(tty); +				tty_kref_put(tty); +			}  			kill_traffic(port);  		}  	} @@ -1115,7 +1122,8 @@ static int __init usb_serial_init(void)  	result = bus_register(&usb_serial_bus_type);  	if (result) { -		err("%s - registering bus driver failed", __func__); +		printk(KERN_ERR "usb-serial: %s - registering bus driver " +		       "failed\n", __func__);  		goto exit_bus;  	} @@ -1136,25 +1144,28 @@ static int __init usb_serial_init(void)  	tty_set_operations(usb_serial_tty_driver, &serial_ops);  	result = tty_register_driver(usb_serial_tty_driver);  	if (result) { -		err("%s - tty_register_driver failed", __func__); +		printk(KERN_ERR "usb-serial: %s - tty_register_driver failed\n", +		       __func__);  		goto exit_reg_driver;  	}  	/* register the USB driver */  	result = usb_register(&usb_serial_driver);  	if (result < 0) { -		err("%s - usb_register failed", __func__); +		printk(KERN_ERR "usb-serial: %s - usb_register failed\n", +		       __func__);  		goto exit_tty;  	}  	/* register the generic driver, if we should */  	result = usb_serial_generic_register(debug);  	if (result < 0) { -		err("%s - registering generic driver failed", __func__); +		printk(KERN_ERR "usb-serial: %s - registering generic " +		       "driver failed\n", __func__);  		goto exit_generic;  	} -	info(DRIVER_DESC); +	printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_DESC "\n");  	return result; @@ -1168,7 +1179,8 @@ exit_reg_driver:  	bus_unregister(&usb_serial_bus_type);  exit_bus: -	err("%s - returning with error %d", __func__, result); +	printk(KERN_ERR "usb-serial: %s - returning with error %d\n", +	       __func__, result);  	put_tty_driver(usb_serial_tty_driver);  	return result;  } @@ -1227,11 +1239,11 @@ int usb_serial_register(struct usb_serial_driver *driver)  	retval = usb_serial_bus_register(driver);  	if (retval) { -		err("problem %d when registering driver %s", -						retval, driver->description); +		printk(KERN_ERR "usb-serial: problem %d when registering " +		       "driver %s\n", retval, driver->description);  		list_del(&driver->driver_list);  	} else -		info("USB Serial support registered for %s", +		printk(KERN_INFO "USB Serial support registered for %s\n",  						driver->description);  	return retval; @@ -1242,7 +1254,8 @@ EXPORT_SYMBOL_GPL(usb_serial_register);  void usb_serial_deregister(struct usb_serial_driver *device)  {  	/* must be called with BKL held */ -	info("USB Serial deregistering driver %s", device->description); +	printk(KERN_INFO "USB Serial deregistering driver %s\n", +	       device->description);  	list_del(&device->driver_list);  	usb_serial_bus_deregister(device);  } diff --git a/drivers/usb/serial/visor.c b/drivers/usb/serial/visor.c index cf8924f9a2c..4facce3d936 100644 --- a/drivers/usb/serial/visor.c +++ b/drivers/usb/serial/visor.c @@ -499,7 +499,7 @@ static void visor_read_bulk_callback(struct urb *urb)  	int status = urb->status;  	struct tty_struct *tty;  	int result; -	int available_room; +	int available_room = 0;  	dbg("%s - port %d", __func__, port->number); @@ -512,13 +512,17 @@ static void visor_read_bulk_callback(struct urb *urb)  	usb_serial_debug_data(debug, &port->dev, __func__,  						urb->actual_length, data); -	tty = port->port.tty; -	if (tty && urb->actual_length) { -		available_room = tty_buffer_request_room(tty, +	if (urb->actual_length) { +		tty = tty_port_tty_get(&port->port); +		if (tty) { +			available_room = tty_buffer_request_room(tty,  							urb->actual_length); -		if (available_room) { -			tty_insert_flip_string(tty, data, available_room); -			tty_flip_buffer_push(tty); +			if (available_room) { +				tty_insert_flip_string(tty, data, +							available_room); +				tty_flip_buffer_push(tty); +			} +			tty_kref_put(tty);  		}  		spin_lock(&priv->lock);  		priv->bytes_in += available_room; @@ -764,7 +768,7 @@ static int visor_probe(struct usb_serial *serial,  	dbg("%s", __func__);  	if (serial->dev->actconfig->desc.bConfigurationValue != 1) { -		err("active config #%d != 1 ??", +		dev_err(&serial->dev->dev, "active config #%d != 1 ??\n",  			serial->dev->actconfig->desc.bConfigurationValue);  		return -ENODEV;  	} @@ -967,11 +971,14 @@ static int __init visor_init(void)  				break;  			}  		} -		info( -		  "Untested USB device specified at time of module insertion"); -		info("Warning: This is not guaranteed to work"); -		info("Using a newer kernel is preferred to this method"); -		info("Adding Palm OS protocol 4.x support for unknown device: 0x%x/0x%x", +		printk(KERN_INFO KBUILD_MODNAME +		       ": Untested USB device specified at time of module insertion\n"); +		printk(KERN_INFO KBUILD_MODNAME +		       ": Warning: This is not guaranteed to work\n"); +		printk(KERN_INFO KBUILD_MODNAME +		       ": Using a newer kernel is preferred to this method\n"); +		printk(KERN_INFO KBUILD_MODNAME +		       ": Adding Palm OS protocol 4.x support for unknown device: 0x%x/0x%x\n",  			vendor, product);  	}  	retval = usb_serial_register(&handspring_device); @@ -986,7 +993,7 @@ static int __init visor_init(void)  	retval = usb_register(&visor_driver);  	if (retval)  		goto failed_usb_register; -	info(DRIVER_DESC); +	printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_DESC "\n");  	return 0;  failed_usb_register: diff --git a/drivers/usb/serial/whiteheat.c b/drivers/usb/serial/whiteheat.c index 3a9d14384a4..5335d3211c0 100644 --- a/drivers/usb/serial/whiteheat.c +++ b/drivers/usb/serial/whiteheat.c @@ -303,12 +303,15 @@ static int whiteheat_firmware_download(struct usb_serial *serial,  	if (request_ihex_firmware(&firmware_fw, "whiteheat.fw",  				  &serial->dev->dev)) { -		err("%s - request \"whiteheat.fw\" failed", __func__); +		dev_err(&serial->dev->dev, +			"%s - request \"whiteheat.fw\" failed\n", __func__);  		goto out;  	}  	if (request_ihex_firmware(&loader_fw, "whiteheat_loader.fw",  			     &serial->dev->dev)) { -		err("%s - request \"whiteheat_loader.fw\" failed", __func__); +		dev_err(&serial->dev->dev, +			"%s - request \"whiteheat_loader.fw\" failed\n", +			__func__);  		goto out;  	}  	ret = 0; @@ -320,9 +323,10 @@ static int whiteheat_firmware_download(struct usb_serial *serial,  					      (unsigned char *)record->data,  					      be16_to_cpu(record->len), 0xa0);  		if (response < 0) { -			err("%s - ezusb_writememory failed for loader (%d %04X %p %d)", -			    __func__, response, be32_to_cpu(record->addr), -			    record->data, be16_to_cpu(record->len)); +			dev_err(&serial->dev->dev, "%s - ezusb_writememory " +				"failed for loader (%d %04X %p %d)\n", +				__func__, response, be32_to_cpu(record->addr), +				record->data, be16_to_cpu(record->len));  			break;  		}  		record = ihex_next_binrec(record); @@ -338,9 +342,11 @@ static int whiteheat_firmware_download(struct usb_serial *serial,  					      (unsigned char *)record->data,  					      be16_to_cpu(record->len), 0xa3);  		if (response < 0) { -			err("%s - ezusb_writememory failed for first firmware step (%d %04X %p %d)",  -			    __func__, response, be32_to_cpu(record->addr), -			    record->data, be16_to_cpu(record->len)); +			dev_err(&serial->dev->dev, "%s - ezusb_writememory " +				"failed for first firmware step " +				"(%d %04X %p %d)\n", __func__, response, +				be32_to_cpu(record->addr), record->data, +				be16_to_cpu(record->len));  			break;  		}  		++record; @@ -354,9 +360,11 @@ static int whiteheat_firmware_download(struct usb_serial *serial,  					      (unsigned char *)record->data,  					      be16_to_cpu(record->len), 0xa0);  		if (response < 0) { -			err("%s - ezusb_writememory failed for second firmware step (%d %04X %p %d)",  -			    __func__, response, be32_to_cpu(record->addr), -			    record->data, be16_to_cpu(record->len)); +			dev_err(&serial->dev->dev, "%s - ezusb_writememory " +				"failed for second firmware step " +				"(%d %04X %p %d)\n", __func__, response, +				be32_to_cpu(record->addr), record->data, +				be16_to_cpu(record->len));  			break;  		}  		++record; @@ -421,12 +429,12 @@ static int whiteheat_attach(struct usb_serial *serial)  	ret = usb_bulk_msg(serial->dev, pipe, command, 2,  						&alen, COMMAND_TIMEOUT_MS);  	if (ret) { -		err("%s: Couldn't send command [%d]", -				serial->type->description, ret); +		dev_err(&serial->dev->dev, "%s: Couldn't send command [%d]\n", +			serial->type->description, ret);  		goto no_firmware;  	} else if (alen != 2) { -		err("%s: Send command incomplete [%d]", -				serial->type->description, alen); +		dev_err(&serial->dev->dev, "%s: Send command incomplete [%d]\n", +			serial->type->description, alen);  		goto no_firmware;  	} @@ -437,31 +445,33 @@ static int whiteheat_attach(struct usb_serial *serial)  	ret = usb_bulk_msg(serial->dev, pipe, result,  			sizeof(*hw_info) + 1, &alen, COMMAND_TIMEOUT_MS);  	if (ret) { -		err("%s: Couldn't get results [%d]", -				serial->type->description, ret); +		dev_err(&serial->dev->dev, "%s: Couldn't get results [%d]\n", +			serial->type->description, ret);  		goto no_firmware;  	} else if (alen != sizeof(*hw_info) + 1) { -		err("%s: Get results incomplete [%d]", -				serial->type->description, alen); +		dev_err(&serial->dev->dev, "%s: Get results incomplete [%d]\n", +			serial->type->description, alen);  		goto no_firmware;  	} else if (result[0] != command[0]) { -		err("%s: Command failed [%d]", -				serial->type->description, result[0]); +		dev_err(&serial->dev->dev, "%s: Command failed [%d]\n", +			serial->type->description, result[0]);  		goto no_firmware;  	}  	hw_info = (struct whiteheat_hw_info *)&result[1]; -	info("%s: Driver %s: Firmware v%d.%02d", serial->type->description, -	     DRIVER_VERSION, hw_info->sw_major_rev, hw_info->sw_minor_rev); +	dev_info(&serial->dev->dev, "%s: Driver %s: Firmware v%d.%02d\n", +		 serial->type->description, DRIVER_VERSION, +		 hw_info->sw_major_rev, hw_info->sw_minor_rev);  	for (i = 0; i < serial->num_ports; i++) {  		port = serial->port[i];  		info = kmalloc(sizeof(struct whiteheat_private), GFP_KERNEL);  		if (info == NULL) { -			err("%s: Out of memory for port structures\n", -					serial->type->description); +			dev_err(&port->dev, +				"%s: Out of memory for port structures\n", +				serial->type->description);  			goto no_private;  		} @@ -481,18 +491,20 @@ static int whiteheat_attach(struct usb_serial *serial)  		for (j = 0; j < urb_pool_size; j++) {  			urb = usb_alloc_urb(0, GFP_KERNEL);  			if (!urb) { -				err("No free urbs available"); +				dev_err(&port->dev, "No free urbs available\n");  				goto no_rx_urb;  			}  			buf_size = port->read_urb->transfer_buffer_length;  			urb->transfer_buffer = kmalloc(buf_size, GFP_KERNEL);  			if (!urb->transfer_buffer) { -				err("Couldn't allocate urb buffer"); +				dev_err(&port->dev, +					"Couldn't allocate urb buffer\n");  				goto no_rx_buf;  			}  			wrap = kmalloc(sizeof(*wrap), GFP_KERNEL);  			if (!wrap) { -				err("Couldn't allocate urb wrapper"); +				dev_err(&port->dev, +					"Couldn't allocate urb wrapper\n");  				goto no_rx_wrap;  			}  			usb_fill_bulk_urb(urb, serial->dev, @@ -505,18 +517,20 @@ static int whiteheat_attach(struct usb_serial *serial)  			urb = usb_alloc_urb(0, GFP_KERNEL);  			if (!urb) { -				err("No free urbs available"); +				dev_err(&port->dev, "No free urbs available\n");  				goto no_tx_urb;  			}  			buf_size = port->write_urb->transfer_buffer_length;  			urb->transfer_buffer = kmalloc(buf_size, GFP_KERNEL);  			if (!urb->transfer_buffer) { -				err("Couldn't allocate urb buffer"); +				dev_err(&port->dev, +					"Couldn't allocate urb buffer\n");  				goto no_tx_buf;  			}  			wrap = kmalloc(sizeof(*wrap), GFP_KERNEL);  			if (!wrap) { -				err("Couldn't allocate urb wrapper"); +				dev_err(&port->dev, +					"Couldn't allocate urb wrapper\n");  				goto no_tx_wrap;  			}  			usb_fill_bulk_urb(urb, serial->dev, @@ -534,8 +548,9 @@ static int whiteheat_attach(struct usb_serial *serial)  	command_info = kmalloc(sizeof(struct whiteheat_command_private),  								GFP_KERNEL);  	if (command_info == NULL) { -		err("%s: Out of memory for port structures\n", -					serial->type->description); +		dev_err(&serial->dev->dev, +			"%s: Out of memory for port structures\n", +			serial->type->description);  		goto no_command_private;  	} @@ -552,12 +567,15 @@ static int whiteheat_attach(struct usb_serial *serial)  no_firmware:  	/* Firmware likely not running */ -	err("%s: Unable to retrieve firmware version, try replugging\n", -					serial->type->description); -	err("%s: If the firmware is not running (status led not blinking)\n", -					serial->type->description); -	err("%s: please contact support@connecttech.com\n", -					serial->type->description); +	dev_err(&serial->dev->dev, +		"%s: Unable to retrieve firmware version, try replugging\n", +		serial->type->description); +	dev_err(&serial->dev->dev, +		"%s: If the firmware is not running (status led not blinking)\n", +		serial->type->description); +	dev_err(&serial->dev->dev, +		"%s: please contact support@connecttech.com\n", +		serial->type->description);  	kfree(result);  	return -ENODEV; @@ -680,8 +698,9 @@ static int whiteheat_open(struct tty_struct *tty,  	/* Start reading from the device */  	retval = start_port_read(port);  	if (retval) { -		err("%s - failed submitting read urb, error %d", -				__func__, retval); +		dev_err(&port->dev, +			"%s - failed submitting read urb, error %d\n", +			__func__, retval);  		firm_close(port);  		stop_command_port(port->serial);  		goto exit; @@ -806,8 +825,9 @@ static int whiteheat_write(struct tty_struct *tty,  		urb->transfer_buffer_length = bytes;  		result = usb_submit_urb(urb, GFP_ATOMIC);  		if (result) { -			err("%s - failed submitting write urb, error %d", -							__func__, result); +			dev_err(&port->dev, +				"%s - failed submitting write urb, error %d\n", +				__func__, result);  			sent = result;  			spin_lock_irqsave(&info->lock, flags);  			list_add(tmp, &info->tx_urbs_free); @@ -1075,7 +1095,7 @@ static void whiteheat_read_callback(struct urb *urb)  	wrap = urb_to_wrap(urb, &info->rx_urbs_submitted);  	if (!wrap) {  		spin_unlock(&info->lock); -		err("%s - Not my urb!", __func__); +		dev_err(&port->dev, "%s - Not my urb!\n", __func__);  		return;  	}  	list_del(&wrap->list); @@ -1119,7 +1139,7 @@ static void whiteheat_write_callback(struct urb *urb)  	wrap = urb_to_wrap(urb, &info->tx_urbs_submitted);  	if (!wrap) {  		spin_unlock(&info->lock); -		err("%s - Not my urb!", __func__); +		dev_err(&port->dev, "%s - Not my urb!\n", __func__);  		return;  	}  	list_move(&wrap->list, &info->tx_urbs_free); @@ -1383,8 +1403,9 @@ static int start_command_port(struct usb_serial *serial)  		command_port->read_urb->dev = serial->dev;  		retval = usb_submit_urb(command_port->read_urb, GFP_KERNEL);  		if (retval) { -			err("%s - failed submitting read urb, error %d", -							__func__, retval); +			dev_err(&serial->dev->dev, +				"%s - failed submitting read urb, error %d\n", +				__func__, retval);  			goto exit;  		}  	} @@ -1481,7 +1502,7 @@ static void rx_data_softint(struct work_struct *work)  	struct whiteheat_private *info =  		container_of(work, struct whiteheat_private, rx_work);  	struct usb_serial_port *port = info->port; -	struct tty_struct *tty = port->port.tty; +	struct tty_struct *tty = tty_port_tty_get(&port->port);  	struct whiteheat_urb_wrap *wrap;  	struct urb *urb;  	unsigned long flags; @@ -1493,7 +1514,7 @@ static void rx_data_softint(struct work_struct *work)  	spin_lock_irqsave(&info->lock, flags);  	if (info->flags & THROTTLED) {  		spin_unlock_irqrestore(&info->lock, flags); -		return; +		goto out;  	}  	list_for_each_safe(tmp, tmp2, &info->rx_urb_q) { @@ -1513,7 +1534,7 @@ static void rx_data_softint(struct work_struct *work)  				spin_unlock_irqrestore(&info->lock, flags);  				tty_flip_buffer_push(tty);  				schedule_work(&info->rx_work); -				return; +				goto out;  			}  			tty_insert_flip_string(tty, urb->transfer_buffer, len);  			sent += len; @@ -1522,7 +1543,8 @@ static void rx_data_softint(struct work_struct *work)  		urb->dev = port->serial->dev;  		result = usb_submit_urb(urb, GFP_ATOMIC);  		if (result) { -			err("%s - failed resubmitting read urb, error %d", +			dev_err(&port->dev, +				"%s - failed resubmitting read urb, error %d\n",  				__func__, result);  			spin_lock_irqsave(&info->lock, flags);  			list_add(tmp, &info->rx_urbs_free); @@ -1536,6 +1558,8 @@ static void rx_data_softint(struct work_struct *work)  	if (sent)  		tty_flip_buffer_push(tty); +out: +	tty_kref_put(tty);  } @@ -1554,7 +1578,8 @@ static int __init whiteheat_init(void)  	retval = usb_register(&whiteheat_driver);  	if (retval)  		goto failed_usb_register; -	info(DRIVER_DESC " " DRIVER_VERSION); +	printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":" +	       DRIVER_DESC "\n");  	return 0;  failed_usb_register:  	usb_serial_deregister(&whiteheat_device); diff --git a/drivers/usb/storage/Kconfig b/drivers/usb/storage/Kconfig index 3d9249632ae..c68b738900b 100644 --- a/drivers/usb/storage/Kconfig +++ b/drivers/usb/storage/Kconfig @@ -2,8 +2,8 @@  # USB Storage driver configuration  # -comment "NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support'" -comment "may also be needed; see USB_STORAGE Help for more information" +comment "NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may also be needed;" +comment "see USB_STORAGE Help for more information"  	depends on USB  config USB_STORAGE diff --git a/drivers/usb/storage/initializers.c b/drivers/usb/storage/initializers.c index 4995bb595ae..2dd9bd4bff5 100644 --- a/drivers/usb/storage/initializers.c +++ b/drivers/usb/storage/initializers.c @@ -95,11 +95,10 @@ int usb_stor_huawei_e220_init(struct us_data *us)  {  	int result; -	us->iobuf[0] = 0x1;  	result = usb_stor_control_msg(us, us->send_ctrl_pipe,  				      USB_REQ_SET_FEATURE,  				      USB_TYPE_STANDARD | USB_RECIP_DEVICE, -				      0x01, 0x0, us->iobuf, 0x1, 1000); +				      0x01, 0x0, NULL, 0x0, 1000);  	US_DEBUGP("usb_control_msg performing result is %d\n", result);  	return (result ? 0 : -1);  } diff --git a/drivers/usb/storage/onetouch.c b/drivers/usb/storage/onetouch.c index 98b89ea9e31..c7bf8954b4e 100644 --- a/drivers/usb/storage/onetouch.c +++ b/drivers/usb/storage/onetouch.c @@ -78,8 +78,8 @@ static void usb_onetouch_irq(struct urb *urb)  resubmit:  	retval = usb_submit_urb (urb, GFP_ATOMIC);  	if (retval) -		err ("can't resubmit intr, %s-%s/input0, retval %d", -			onetouch->udev->bus->bus_name, +		dev_err(&dev->dev, "can't resubmit intr, %s-%s/input0, " +			"retval %d\n", onetouch->udev->bus->bus_name,  			onetouch->udev->devpath, retval);  } @@ -90,7 +90,7 @@ static int usb_onetouch_open(struct input_dev *dev)  	onetouch->is_open = 1;  	onetouch->irq->dev = onetouch->udev;  	if (usb_submit_urb(onetouch->irq, GFP_KERNEL)) { -		err("usb_submit_urb failed"); +		dev_err(&dev->dev, "usb_submit_urb failed\n");  		return -EIO;  	} @@ -117,7 +117,8 @@ static void usb_onetouch_pm_hook(struct us_data *us, int action)  			break;  		case US_RESUME:  			if (usb_submit_urb(onetouch->irq, GFP_KERNEL) != 0) -				err("usb_submit_urb failed"); +				dev_err(&onetouch->irq->dev->dev, +					"usb_submit_urb failed\n");  			break;  		default:  			break; diff --git a/drivers/usb/storage/transport.c b/drivers/usb/storage/transport.c index 3523a0bfa0f..79108d5d317 100644 --- a/drivers/usb/storage/transport.c +++ b/drivers/usb/storage/transport.c @@ -663,7 +663,7 @@ void usb_stor_invoke_transport(struct scsi_cmnd *srb, struct us_data *us)  	}  	/* Did we transfer less than the minimum amount required? */ -	if (srb->result == SAM_STAT_GOOD && +	if ((srb->result == SAM_STAT_GOOD || srb->sense_buffer[2] == 0) &&  			scsi_bufflen(srb) - scsi_get_resid(srb) < srb->underflow)  		srb->result = (DID_ERROR << 16) | (SUGGEST_RETRY << 24); diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h index cd155475cb6..d4e5fc86e43 100644 --- a/drivers/usb/storage/unusual_devs.h +++ b/drivers/usb/storage/unusual_devs.h @@ -253,6 +253,14 @@ UNUSUAL_DEV(  0x0421, 0x006a, 0x0000, 0x0591,  	US_SC_DEVICE, US_PR_DEVICE, NULL,  	US_FL_FIX_CAPACITY ), +/* Submitted by Ricky Wong Yung Fei <evilbladewarrior@gmail.com> */ +/* Nokia 7610 Supernova - Too many sectors reported in usb storage mode */ +UNUSUAL_DEV(  0x0421, 0x00f5, 0x0000, 0x0470, +	"Nokia", +	"7610 Supernova", +	US_SC_DEVICE, US_PR_DEVICE, NULL, +	US_FL_FIX_CAPACITY ), +  /* Reported by Olaf Hering <olh@suse.de> from novell bug #105878 */  UNUSUAL_DEV(  0x0424, 0x0fdc, 0x0210, 0x0210,  		"SMSC", @@ -333,6 +341,13 @@ UNUSUAL_DEV(  0x0482, 0x0103, 0x0100, 0x0100,  		"Finecam S5",  		US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_FIX_INQUIRY), +/* Patch submitted by Jens Taprogge <jens.taprogge@taprogge.org> */ +UNUSUAL_DEV(  0x0482, 0x0107, 0x0100, 0x0100, +		"Kyocera", +		"CONTAX SL300R T*", +		US_SC_DEVICE, US_PR_DEVICE, NULL, +		US_FL_FIX_CAPACITY | US_FL_NOT_LOCKABLE), +  /* Reported by Paul Stewart <stewart@wetlogic.net>   * This entry is needed because the device reports Sub=ff */  UNUSUAL_DEV(  0x04a4, 0x0004, 0x0001, 0x0001, @@ -411,6 +426,13 @@ UNUSUAL_DEV(  0x04b0, 0x0417, 0x0100, 0x0100,  		US_SC_DEVICE, US_PR_DEVICE, NULL,  		US_FL_FIX_CAPACITY), +/* Reported by paul ready <lxtwin@homecall.co.uk> */ +UNUSUAL_DEV(  0x04b0, 0x0419, 0x0100, 0x0200, +		"NIKON", +		"NIKON DSC D300", +		US_SC_DEVICE, US_PR_DEVICE, NULL, +		US_FL_FIX_CAPACITY), +  /* Reported by Doug Maxey (dwm@austin.ibm.com) */  UNUSUAL_DEV(  0x04b3, 0x4001, 0x0110, 0x0110,  		"IBM", @@ -1251,6 +1273,13 @@ UNUSUAL_DEV( 0x0839, 0x000a, 0x0001, 0x0001,  		US_SC_DEVICE, US_PR_DEVICE, NULL,  		US_FL_FIX_INQUIRY), +/* Reported by Luciano Rocha <luciano@eurotux.com> */ +UNUSUAL_DEV( 0x0840, 0x0082, 0x0001, 0x0001, +		"Argosy", +		"Storage", +		US_SC_DEVICE, US_PR_DEVICE, NULL, +		US_FL_FIX_CAPACITY), +  /* Entry and supporting patch by Theodore Kilgore <kilgota@auburn.edu>.   * Flag will support Bulk devices which use a standards-violating 32-byte   * Command Block Wrapper. Here, the "DC2MEGA" cameras (several brands) with @@ -1628,97 +1657,332 @@ UNUSUAL_DEV(  0x1210, 0x0003, 0x0100, 0x0100,  /* Reported by fangxiaozhi <huananhu@huawei.com>   * This brings the HUAWEI data card devices into multi-port mode   */ -UNUSUAL_DEV( 0x12d1, 0x1001, 0x0000, 0x0000, +UNUSUAL_DEV(  0x12d1, 0x1001, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		US_SC_DEVICE, US_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x1003, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		US_SC_DEVICE, US_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x1004, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		US_SC_DEVICE, US_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x1401, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		US_SC_DEVICE, US_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x1402, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		US_SC_DEVICE, US_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x1403, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		US_SC_DEVICE, US_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x1404, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		US_SC_DEVICE, US_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x1405, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		US_SC_DEVICE, US_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x1406, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		US_SC_DEVICE, US_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x1407, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		US_SC_DEVICE, US_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x1408, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		US_SC_DEVICE, US_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x1409, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		US_SC_DEVICE, US_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x140A, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		US_SC_DEVICE, US_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x140B, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		US_SC_DEVICE, US_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x140C, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		US_SC_DEVICE, US_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x140D, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		US_SC_DEVICE, US_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x140E, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		US_SC_DEVICE, US_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x140F, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		US_SC_DEVICE, US_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x1410, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		US_SC_DEVICE, US_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x1411, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		US_SC_DEVICE, US_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x1412, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		US_SC_DEVICE, US_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x1413, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		US_SC_DEVICE, US_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x1414, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		US_SC_DEVICE, US_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x1415, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		US_SC_DEVICE, US_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x1416, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		US_SC_DEVICE, US_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x1417, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		US_SC_DEVICE, US_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x1418, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		US_SC_DEVICE, US_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x1419, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		US_SC_DEVICE, US_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x141A, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		US_SC_DEVICE, US_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x141B, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		US_SC_DEVICE, US_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x141C, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		US_SC_DEVICE, US_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x141D, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		US_SC_DEVICE, US_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x141E, 0x0000, 0x0000,  		"HUAWEI MOBILE",  		"Mass Storage",  		US_SC_DEVICE, US_PR_DEVICE, usb_stor_huawei_e220_init,  		0), -UNUSUAL_DEV( 0x12d1, 0x1003, 0x0000, 0x0000, +UNUSUAL_DEV(  0x12d1, 0x141F, 0x0000, 0x0000,  		"HUAWEI MOBILE",  		"Mass Storage",  		US_SC_DEVICE, US_PR_DEVICE, usb_stor_huawei_e220_init,  		0), -UNUSUAL_DEV( 0x12d1, 0x1004, 0x0000, 0x0000, +UNUSUAL_DEV(  0x12d1, 0x1420, 0x0000, 0x0000,  		"HUAWEI MOBILE",  		"Mass Storage",  		US_SC_DEVICE, US_PR_DEVICE, usb_stor_huawei_e220_init,  		0), -UNUSUAL_DEV( 0x12d1, 0x1401, 0x0000, 0x0000, +UNUSUAL_DEV(  0x12d1, 0x1421, 0x0000, 0x0000,  		"HUAWEI MOBILE",  		"Mass Storage",  		US_SC_DEVICE, US_PR_DEVICE, usb_stor_huawei_e220_init,  		0), -UNUSUAL_DEV( 0x12d1, 0x1403, 0x0000, 0x0000, +UNUSUAL_DEV(  0x12d1, 0x1422, 0x0000, 0x0000,  		"HUAWEI MOBILE",  		"Mass Storage",  		US_SC_DEVICE, US_PR_DEVICE, usb_stor_huawei_e220_init,  		0), -UNUSUAL_DEV( 0x12d1, 0x1405, 0x0000, 0x0000, +UNUSUAL_DEV(  0x12d1, 0x1423, 0x0000, 0x0000,  		"HUAWEI MOBILE",  		"Mass Storage",  		US_SC_DEVICE, US_PR_DEVICE, usb_stor_huawei_e220_init,  		0), -UNUSUAL_DEV( 0x12d1, 0x1406, 0x0000, 0x0000, +UNUSUAL_DEV(  0x12d1, 0x1424, 0x0000, 0x0000,  		"HUAWEI MOBILE",  		"Mass Storage",  		US_SC_DEVICE, US_PR_DEVICE, usb_stor_huawei_e220_init,  		0), -UNUSUAL_DEV( 0x12d1, 0x1408, 0x0000, 0x0000, +UNUSUAL_DEV(  0x12d1, 0x1425, 0x0000, 0x0000,  		"HUAWEI MOBILE",  		"Mass Storage",  		US_SC_DEVICE, US_PR_DEVICE, usb_stor_huawei_e220_init,  		0), -UNUSUAL_DEV( 0x12d1, 0x1409, 0x0000, 0x0000, +UNUSUAL_DEV(  0x12d1, 0x1426, 0x0000, 0x0000,  		"HUAWEI MOBILE",  		"Mass Storage",  		US_SC_DEVICE, US_PR_DEVICE, usb_stor_huawei_e220_init,  		0), -UNUSUAL_DEV( 0x12d1, 0x1410, 0x0000, 0x0000, +UNUSUAL_DEV(  0x12d1, 0x1427, 0x0000, 0x0000,  		"HUAWEI MOBILE",  		"Mass Storage",  		US_SC_DEVICE, US_PR_DEVICE, usb_stor_huawei_e220_init,  		0), -UNUSUAL_DEV( 0x12d1, 0x1411, 0x0000, 0x0000, +UNUSUAL_DEV(  0x12d1, 0x1428, 0x0000, 0x0000,  		"HUAWEI MOBILE",  		"Mass Storage",  		US_SC_DEVICE, US_PR_DEVICE, usb_stor_huawei_e220_init,  		0), -UNUSUAL_DEV( 0x12d1, 0x1412, 0x0000, 0x0000, +UNUSUAL_DEV(  0x12d1, 0x1429, 0x0000, 0x0000,  		"HUAWEI MOBILE",  		"Mass Storage",  		US_SC_DEVICE, US_PR_DEVICE, usb_stor_huawei_e220_init,  		0), -UNUSUAL_DEV( 0x12d1, 0x1413, 0x0000, 0x0000, +UNUSUAL_DEV(  0x12d1, 0x142A, 0x0000, 0x0000,  		"HUAWEI MOBILE",  		"Mass Storage",  		US_SC_DEVICE, US_PR_DEVICE, usb_stor_huawei_e220_init,  		0), -UNUSUAL_DEV( 0x12d1, 0x1414, 0x0000, 0x0000, +UNUSUAL_DEV(  0x12d1, 0x142B, 0x0000, 0x0000,  		"HUAWEI MOBILE",  		"Mass Storage",  		US_SC_DEVICE, US_PR_DEVICE, usb_stor_huawei_e220_init,  		0), -UNUSUAL_DEV( 0x12d1, 0x1415, 0x0000, 0x0000, +UNUSUAL_DEV(  0x12d1, 0x142C, 0x0000, 0x0000,  		"HUAWEI MOBILE",  		"Mass Storage",  		US_SC_DEVICE, US_PR_DEVICE, usb_stor_huawei_e220_init,  		0), -UNUSUAL_DEV( 0x12d1, 0x1416, 0x0000, 0x0000, +UNUSUAL_DEV(  0x12d1, 0x142D, 0x0000, 0x0000,  		"HUAWEI MOBILE",  		"Mass Storage",  		US_SC_DEVICE, US_PR_DEVICE, usb_stor_huawei_e220_init,  		0), -UNUSUAL_DEV( 0x12d1, 0x1417, 0x0000, 0x0000, +UNUSUAL_DEV(  0x12d1, 0x142E, 0x0000, 0x0000,  		"HUAWEI MOBILE",  		"Mass Storage",  		US_SC_DEVICE, US_PR_DEVICE, usb_stor_huawei_e220_init,  		0), -UNUSUAL_DEV( 0x12d1, 0x1418, 0x0000, 0x0000, +UNUSUAL_DEV(  0x12d1, 0x142F, 0x0000, 0x0000,  		"HUAWEI MOBILE",  		"Mass Storage",  		US_SC_DEVICE, US_PR_DEVICE, usb_stor_huawei_e220_init,  		0), -UNUSUAL_DEV( 0x12d1, 0x1419, 0x0000, 0x0000, +UNUSUAL_DEV(  0x12d1, 0x1430, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		US_SC_DEVICE, US_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x1431, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		US_SC_DEVICE, US_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x1432, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		US_SC_DEVICE, US_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x1433, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		US_SC_DEVICE, US_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x1434, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		US_SC_DEVICE, US_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x1435, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		US_SC_DEVICE, US_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x1436, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		US_SC_DEVICE, US_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x1437, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		US_SC_DEVICE, US_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x1438, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		US_SC_DEVICE, US_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x1439, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		US_SC_DEVICE, US_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x143A, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		US_SC_DEVICE, US_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x143B, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		US_SC_DEVICE, US_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x143C, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		US_SC_DEVICE, US_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x143D, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		US_SC_DEVICE, US_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x143E, 0x0000, 0x0000, +		"HUAWEI MOBILE", +		"Mass Storage", +		US_SC_DEVICE, US_PR_DEVICE, usb_stor_huawei_e220_init, +		0), +UNUSUAL_DEV(  0x12d1, 0x143F, 0x0000, 0x0000,  		"HUAWEI MOBILE",  		"Mass Storage",  		US_SC_DEVICE, US_PR_DEVICE, usb_stor_huawei_e220_init, @@ -1745,6 +2009,15 @@ UNUSUAL_DEV(  0x14cd, 0x6600, 0x0201, 0x0201,  		US_SC_DEVICE, US_PR_DEVICE, NULL,  		US_FL_IGNORE_RESIDUE ), +/* Reported by Alexandre Oliva <oliva@lsd.ic.unicamp.br> + * JMicron responds to USN and several other SCSI ioctls with a + * residue that causes subsequent I/O requests to fail.  */ +UNUSUAL_DEV(  0x152d, 0x2329, 0x0100, 0x0100, +	        "JMicron", +	        "USB to ATA/ATAPI Bridge", +	        US_SC_DEVICE, US_PR_DEVICE, NULL, +	        US_FL_IGNORE_RESIDUE ), +  /* Reported by Robert Schedel <r.schedel@yahoo.de>   * Note: this is a 'super top' device like the above 14cd/6600 device */  UNUSUAL_DEV(  0x1652, 0x6600, 0x0201, 0x0201, @@ -1818,6 +2091,15 @@ UNUSUAL_DEV(  0x2770, 0x915d, 0x0010, 0x0010,  		US_SC_DEVICE, US_PR_DEVICE, NULL,  		US_FL_FIX_CAPACITY ), +/* Reported by Frederic Marchal <frederic.marchal@wowcompany.com> + * Mio Moov 330 + */ +UNUSUAL_DEV(  0x3340, 0xffff, 0x0000, 0x0000, +		"Mitac", +		"Mio DigiWalker USB Sync", +		US_SC_DEVICE,US_PR_DEVICE,NULL, +		US_FL_MAX_SECTORS_64 ), +  /* Reported by Andrey Rahmatullin <wrar@altlinux.org> */  UNUSUAL_DEV(  0x4102, 0x1020, 0x0100,  0x0100,  		"iRiver", diff --git a/drivers/usb/wusbcore/Kconfig b/drivers/usb/wusbcore/Kconfig new file mode 100644 index 00000000000..eb09a0a14a8 --- /dev/null +++ b/drivers/usb/wusbcore/Kconfig @@ -0,0 +1,41 @@ +# +# Wireless USB Core configuration +# +config USB_WUSB +	tristate "Enable Wireless USB extensions (EXPERIMENTAL)" +	depends on EXPERIMENTAL +	depends on USB +        select UWB +        select CRYPTO +        select CRYPTO_BLKCIPHER +        select CRYPTO_CBC +        select CRYPTO_MANAGER +        select CRYPTO_AES +	help +	  Enable the host-side support for Wireless USB. + +          To compile this support select Y (built in). It is safe to +	  select even if you don't have the hardware. + +config USB_WUSB_CBAF +	tristate "Support WUSB Cable Based Association (CBA)" +	depends on USB +	help +	  Some WUSB devices support Cable Based Association. It's used to +	  enable the secure communication between the host and the +	  device. + +	  Enable this option if your WUSB device must to be connected +	  via wired USB before establishing a wireless link. + +	  It is safe to select even if you don't have a compatible +	  hardware. + +config USB_WUSB_CBAF_DEBUG +	bool "Enable CBA debug messages" +	depends on USB_WUSB_CBAF +	help +	  Say Y here if you want the CBA to produce a bunch of debug messages +	  to the system log. Select this if you are having a problem with +	  CBA support and want to see more of what is going on. + diff --git a/drivers/usb/wusbcore/Makefile b/drivers/usb/wusbcore/Makefile new file mode 100644 index 00000000000..75f1ade6625 --- /dev/null +++ b/drivers/usb/wusbcore/Makefile @@ -0,0 +1,26 @@ +obj-$(CONFIG_USB_WUSB)		+= wusbcore.o +obj-$(CONFIG_USB_HWA_HCD)	+= wusb-wa.o +obj-$(CONFIG_USB_WUSB_CBAF)	+= wusb-cbaf.o + + +wusbcore-objs := 	\ +	crypto.o	\ +	devconnect.o	\ +	dev-sysfs.o	\ +	mmc.o		\ +	pal.o		\ +	rh.o		\ +	reservation.o	\ +	security.o	\ +	wusbhc.o + +wusb-cbaf-objs := cbaf.o + +wusb-wa-objs :=	wa-hc.o		\ +		wa-nep.o	\ +		wa-rpipe.o	\ +		wa-xfer.o + +ifeq ($(CONFIG_USB_WUSB_CBAF_DEBUG),y) +EXTRA_CFLAGS += -DDEBUG +endif diff --git a/drivers/usb/wusbcore/cbaf.c b/drivers/usb/wusbcore/cbaf.c new file mode 100644 index 00000000000..ab4788d1785 --- /dev/null +++ b/drivers/usb/wusbcore/cbaf.c @@ -0,0 +1,673 @@ +/* + * Wireless USB - Cable Based Association + * + * + * Copyright (C) 2006 Intel Corporation + * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> + * Copyright (C) 2008 Cambridge Silicon Radio Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * + * WUSB devices have to be paired (associated in WUSB lingo) so + * that they can connect to the system. + * + * One way of pairing is using CBA-Cable Based Association. First + * time you plug the device with a cable, association is done between + * host and device and subsequent times, you can connect wirelessly + * without having to associate again. That's the idea. + * + * This driver does nothing Earth shattering. It just provides an + * interface to chat with the wire-connected device so we can get a + * CDID (device ID) that might have been previously associated to a + * CHID (host ID) and to set up a new <CHID,CDID,CK> triplet + * (connection context), with the CK being the secret, or connection + * key. This is the pairing data. + * + * When a device with the CBA capability connects, the probe routine + * just creates a bunch of sysfs files that a user space enumeration + * manager uses to allow it to connect wirelessly to the system or not. + * + * The process goes like this: + * + * 1. Device plugs, cbaf is loaded, notifications happen. + * + * 2. The connection manager (CM) sees a device with CBAF capability + *    (the wusb_chid etc. files in /sys/devices/blah/OURDEVICE). + * + * 3. The CM writes the host name, supported band groups, and the CHID + *    (host ID) into the wusb_host_name, wusb_host_band_groups and + *    wusb_chid files. These get sent to the device and the CDID (if + *    any) for this host is requested. + * + * 4. The CM can verify that the device's supported band groups + *    (wusb_device_band_groups) are compatible with the host. + * + * 5. The CM reads the wusb_cdid file. + * + * 6. The CM looks up its database + * + * 6.1 If it has a matching CHID,CDID entry, the device has been + *     authorized before (paired) and nothing further needs to be + *     done. + * + * 6.2 If the CDID is zero (or the CM doesn't find a matching CDID in + *     its database), the device is assumed to be not known.  The CM + *     may associate the host with device by: writing a randomly + *     generated CDID to wusb_cdid and then a random CK to wusb_ck + *     (this uploads the new CC to the device). + * + *     CMD may choose to prompt the user before associating with a new + *     device. + * + * 7. Device is unplugged. + * + * When the device tries to connect wirelessly, it will present its + * CDID to the WUSB host controller.  The CM will query the + * database. If the CHID/CDID pair found, it will (with a 4-way + * handshake) challenge the device to demonstrate it has the CK secret + * key (from our database) without actually exchanging it. Once + * satisfied, crypto keys are derived from the CK, the device is + * connected and all communication is encrypted. + * + * References: + *   [WUSB-AM] Association Models Supplement to the Certified Wireless + *             Universal Serial Bus Specification, version 1.0. + */ +#include <linux/module.h> +#include <linux/ctype.h> +#include <linux/version.h> +#include <linux/usb.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/random.h> +#include <linux/mutex.h> +#include <linux/uwb.h> +#include <linux/usb/wusb.h> +#include <linux/usb/association.h> + +#define CBA_NAME_LEN 0x40 /* [WUSB-AM] table 4-7 */ + +/* An instance of a Cable-Based-Association-Framework device */ +struct cbaf { +	struct usb_device *usb_dev; +	struct usb_interface *usb_iface; +	void *buffer; +	size_t buffer_size; + +	struct wusb_ckhdid chid; +	char host_name[CBA_NAME_LEN]; +	u16 host_band_groups; + +	struct wusb_ckhdid cdid; +	char device_name[CBA_NAME_LEN]; +	u16 device_band_groups; + +	struct wusb_ckhdid ck; +}; + +/* + * Verify that a CBAF USB-interface has what we need + * + * According to [WUSB-AM], CBA devices should provide at least two + * interfaces: + *  - RETRIEVE_HOST_INFO + *  - ASSOCIATE + * + * If the device doesn't provide these interfaces, we do not know how + * to deal with it. + */ +static int cbaf_check(struct cbaf *cbaf) +{ +	int result; +	struct device *dev = &cbaf->usb_iface->dev; +	struct wusb_cbaf_assoc_info *assoc_info; +	struct wusb_cbaf_assoc_request *assoc_request; +	size_t assoc_size; +	void *itr, *top; +	int ar_rhi = 0, ar_assoc = 0; + +	result = usb_control_msg( +		cbaf->usb_dev, usb_rcvctrlpipe(cbaf->usb_dev, 0), +		CBAF_REQ_GET_ASSOCIATION_INFORMATION, +		USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE, +		0, cbaf->usb_iface->cur_altsetting->desc.bInterfaceNumber, +		cbaf->buffer, cbaf->buffer_size, 1000 /* FIXME: arbitrary */); +	if (result < 0) { +		dev_err(dev, "Cannot get available association types: %d\n", +			result); +		return result; +	} + +	assoc_info = cbaf->buffer; +	if (result < sizeof(*assoc_info)) { +		dev_err(dev, "Not enough data to decode association info " +			"header (%zu vs %zu bytes required)\n", +			(size_t)result, sizeof(*assoc_info)); +		return result; +	} + +	assoc_size = le16_to_cpu(assoc_info->Length); +	if (result < assoc_size) { +		dev_err(dev, "Not enough data to decode association info " +			"(%zu vs %zu bytes required)\n", +			(size_t)assoc_size, sizeof(*assoc_info)); +		return result; +	} +	/* +	 * From now on, we just verify, but won't error out unless we +	 * don't find the AR_TYPE_WUSB_{RETRIEVE_HOST_INFO,ASSOCIATE} +	 * types. +	 */ +	itr = cbaf->buffer + sizeof(*assoc_info); +	top = cbaf->buffer + assoc_size; +	dev_dbg(dev, "Found %u association requests (%zu bytes)\n", +		 assoc_info->NumAssociationRequests, assoc_size); + +	while (itr < top) { +		u16 ar_type, ar_subtype; +		u32 ar_size; +		const char *ar_name; + +		assoc_request = itr; + +		if (top - itr < sizeof(*assoc_request)) { +			dev_err(dev, "Not enough data to decode associaton " +				"request (%zu vs %zu bytes needed)\n", +				top - itr, sizeof(*assoc_request)); +			break; +		} + +		ar_type = le16_to_cpu(assoc_request->AssociationTypeId); +		ar_subtype = le16_to_cpu(assoc_request->AssociationSubTypeId); +		ar_size = le32_to_cpu(assoc_request->AssociationTypeInfoSize); +		ar_name = "unknown"; + +		switch (ar_type) { +		case AR_TYPE_WUSB: +			/* Verify we have what is mandated by [WUSB-AM]. */ +			switch (ar_subtype) { +			case AR_TYPE_WUSB_RETRIEVE_HOST_INFO: +				ar_name = "RETRIEVE_HOST_INFO"; +				ar_rhi = 1; +				break; +			case AR_TYPE_WUSB_ASSOCIATE: +				/* send assoc data */ +				ar_name = "ASSOCIATE"; +				ar_assoc = 1; +				break; +			}; +			break; +		}; + +		dev_dbg(dev, "Association request #%02u: 0x%04x/%04x " +			 "(%zu bytes): %s\n", +			 assoc_request->AssociationDataIndex, ar_type, +			 ar_subtype, (size_t)ar_size, ar_name); + +		itr += sizeof(*assoc_request); +	} + +	if (!ar_rhi) { +		dev_err(dev, "Missing RETRIEVE_HOST_INFO association " +			"request\n"); +		return -EINVAL; +	} +	if (!ar_assoc) { +		dev_err(dev, "Missing ASSOCIATE association request\n"); +		return -EINVAL; +	} + +	return 0; +} + +static const struct wusb_cbaf_host_info cbaf_host_info_defaults = { +	.AssociationTypeId_hdr    = WUSB_AR_AssociationTypeId, +	.AssociationTypeId    	  = cpu_to_le16(AR_TYPE_WUSB), +	.AssociationSubTypeId_hdr = WUSB_AR_AssociationSubTypeId, +	.AssociationSubTypeId = cpu_to_le16(AR_TYPE_WUSB_RETRIEVE_HOST_INFO), +	.CHID_hdr                 = WUSB_AR_CHID, +	.LangID_hdr               = WUSB_AR_LangID, +	.HostFriendlyName_hdr     = WUSB_AR_HostFriendlyName, +}; + +/* Send WUSB host information (CHID and name) to a CBAF device */ +static int cbaf_send_host_info(struct cbaf *cbaf) +{ +	struct wusb_cbaf_host_info *hi; +	size_t name_len; +	size_t hi_size; + +	hi = cbaf->buffer; +	memset(hi, 0, sizeof(*hi)); +	*hi = cbaf_host_info_defaults; +	hi->CHID = cbaf->chid; +	hi->LangID = 0;	/* FIXME: I guess... */ +	strlcpy(hi->HostFriendlyName, cbaf->host_name, CBA_NAME_LEN); +	name_len = strlen(cbaf->host_name); +	hi->HostFriendlyName_hdr.len = cpu_to_le16(name_len); +	hi_size = sizeof(*hi) + name_len; + +	return usb_control_msg(cbaf->usb_dev, usb_sndctrlpipe(cbaf->usb_dev, 0), +			CBAF_REQ_SET_ASSOCIATION_RESPONSE, +			USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE, +			0x0101, +			cbaf->usb_iface->cur_altsetting->desc.bInterfaceNumber, +			hi, hi_size, 1000 /* FIXME: arbitrary */); +} + +/* + * Get device's information (CDID) associated to CHID + * + * The device will return it's information (CDID, name, bandgroups) + * associated to the CHID we have set before, or 0 CDID and default + * name and bandgroup if no CHID set or unknown. + */ +static int cbaf_cdid_get(struct cbaf *cbaf) +{ +	int result; +	struct device *dev = &cbaf->usb_iface->dev; +	struct wusb_cbaf_device_info *di; +	size_t needed; + +	di = cbaf->buffer; +	result = usb_control_msg( +		cbaf->usb_dev, usb_rcvctrlpipe(cbaf->usb_dev, 0), +		CBAF_REQ_GET_ASSOCIATION_REQUEST, +		USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE, +		0x0200, cbaf->usb_iface->cur_altsetting->desc.bInterfaceNumber, +		di, cbaf->buffer_size, 1000 /* FIXME: arbitrary */); +	if (result < 0) { +		dev_err(dev, "Cannot request device information: %d\n", result); +		return result; +	} + +	needed = result < sizeof(*di) ? sizeof(*di) : le32_to_cpu(di->Length); +	if (result < needed) { +		dev_err(dev, "Not enough data in DEVICE_INFO reply (%zu vs " +			"%zu bytes needed)\n", (size_t)result, needed); +		return result; +	} + +	strlcpy(cbaf->device_name, di->DeviceFriendlyName, CBA_NAME_LEN); +	cbaf->cdid = di->CDID; +	cbaf->device_band_groups = le16_to_cpu(di->BandGroups); + +	return 0; +} + +static ssize_t cbaf_wusb_chid_show(struct device *dev, +					struct device_attribute *attr, +					char *buf) +{ +	struct usb_interface *iface = to_usb_interface(dev); +	struct cbaf *cbaf = usb_get_intfdata(iface); +	char pr_chid[WUSB_CKHDID_STRSIZE]; + +	ckhdid_printf(pr_chid, sizeof(pr_chid), &cbaf->chid); +	return scnprintf(buf, PAGE_SIZE, "%s\n", pr_chid); +} + +static ssize_t cbaf_wusb_chid_store(struct device *dev, +					 struct device_attribute *attr, +					 const char *buf, size_t size) +{ +	ssize_t result; +	struct usb_interface *iface = to_usb_interface(dev); +	struct cbaf *cbaf = usb_get_intfdata(iface); + +	result = sscanf(buf, +			"%02hhx %02hhx %02hhx %02hhx " +			"%02hhx %02hhx %02hhx %02hhx " +			"%02hhx %02hhx %02hhx %02hhx " +			"%02hhx %02hhx %02hhx %02hhx", +			&cbaf->chid.data[0] , &cbaf->chid.data[1], +			&cbaf->chid.data[2] , &cbaf->chid.data[3], +			&cbaf->chid.data[4] , &cbaf->chid.data[5], +			&cbaf->chid.data[6] , &cbaf->chid.data[7], +			&cbaf->chid.data[8] , &cbaf->chid.data[9], +			&cbaf->chid.data[10], &cbaf->chid.data[11], +			&cbaf->chid.data[12], &cbaf->chid.data[13], +			&cbaf->chid.data[14], &cbaf->chid.data[15]); + +	if (result != 16) +		return -EINVAL; + +	result = cbaf_send_host_info(cbaf); +	if (result < 0) +		return result; +	result = cbaf_cdid_get(cbaf); +	if (result < 0) +		return -result; +	return size; +} +static DEVICE_ATTR(wusb_chid, 0600, cbaf_wusb_chid_show, cbaf_wusb_chid_store); + +static ssize_t cbaf_wusb_host_name_show(struct device *dev, +					struct device_attribute *attr, +					char *buf) +{ +	struct usb_interface *iface = to_usb_interface(dev); +	struct cbaf *cbaf = usb_get_intfdata(iface); + +	return scnprintf(buf, PAGE_SIZE, "%s\n", cbaf->host_name); +} + +static ssize_t cbaf_wusb_host_name_store(struct device *dev, +					 struct device_attribute *attr, +					 const char *buf, size_t size) +{ +	ssize_t result; +	struct usb_interface *iface = to_usb_interface(dev); +	struct cbaf *cbaf = usb_get_intfdata(iface); + +	result = sscanf(buf, "%63s", cbaf->host_name); +	if (result != 1) +		return -EINVAL; + +	return size; +} +static DEVICE_ATTR(wusb_host_name, 0600, cbaf_wusb_host_name_show, +					 cbaf_wusb_host_name_store); + +static ssize_t cbaf_wusb_host_band_groups_show(struct device *dev, +					       struct device_attribute *attr, +					       char *buf) +{ +	struct usb_interface *iface = to_usb_interface(dev); +	struct cbaf *cbaf = usb_get_intfdata(iface); + +	return scnprintf(buf, PAGE_SIZE, "0x%04x\n", cbaf->host_band_groups); +} + +static ssize_t cbaf_wusb_host_band_groups_store(struct device *dev, +						struct device_attribute *attr, +						const char *buf, size_t size) +{ +	ssize_t result; +	struct usb_interface *iface = to_usb_interface(dev); +	struct cbaf *cbaf = usb_get_intfdata(iface); +	u16 band_groups = 0; + +	result = sscanf(buf, "%04hx", &band_groups); +	if (result != 1) +		return -EINVAL; + +	cbaf->host_band_groups = band_groups; + +	return size; +} + +static DEVICE_ATTR(wusb_host_band_groups, 0600, +		   cbaf_wusb_host_band_groups_show, +		   cbaf_wusb_host_band_groups_store); + +static const struct wusb_cbaf_device_info cbaf_device_info_defaults = { +	.Length_hdr               = WUSB_AR_Length, +	.CDID_hdr                 = WUSB_AR_CDID, +	.BandGroups_hdr           = WUSB_AR_BandGroups, +	.LangID_hdr               = WUSB_AR_LangID, +	.DeviceFriendlyName_hdr   = WUSB_AR_DeviceFriendlyName, +}; + +static ssize_t cbaf_wusb_cdid_show(struct device *dev, +				   struct device_attribute *attr, char *buf) +{ +	struct usb_interface *iface = to_usb_interface(dev); +	struct cbaf *cbaf = usb_get_intfdata(iface); +	char pr_cdid[WUSB_CKHDID_STRSIZE]; + +	ckhdid_printf(pr_cdid, sizeof(pr_cdid), &cbaf->cdid); +	return scnprintf(buf, PAGE_SIZE, "%s\n", pr_cdid); +} + +static ssize_t cbaf_wusb_cdid_store(struct device *dev, +				struct device_attribute *attr, +				const char *buf, size_t size) +{ +	ssize_t result; +	struct usb_interface *iface = to_usb_interface(dev); +	struct cbaf *cbaf = usb_get_intfdata(iface); +	struct wusb_ckhdid cdid; + +	result = sscanf(buf, +			"%02hhx %02hhx %02hhx %02hhx " +			"%02hhx %02hhx %02hhx %02hhx " +			"%02hhx %02hhx %02hhx %02hhx " +			"%02hhx %02hhx %02hhx %02hhx", +			&cdid.data[0] , &cdid.data[1], +			&cdid.data[2] , &cdid.data[3], +			&cdid.data[4] , &cdid.data[5], +			&cdid.data[6] , &cdid.data[7], +			&cdid.data[8] , &cdid.data[9], +			&cdid.data[10], &cdid.data[11], +			&cdid.data[12], &cdid.data[13], +			&cdid.data[14], &cdid.data[15]); +	if (result != 16) +		return -EINVAL; + +	cbaf->cdid = cdid; + +	return size; +} +static DEVICE_ATTR(wusb_cdid, 0600, cbaf_wusb_cdid_show, cbaf_wusb_cdid_store); + +static ssize_t cbaf_wusb_device_band_groups_show(struct device *dev, +						 struct device_attribute *attr, +						 char *buf) +{ +	struct usb_interface *iface = to_usb_interface(dev); +	struct cbaf *cbaf = usb_get_intfdata(iface); + +	return scnprintf(buf, PAGE_SIZE, "0x%04x\n", cbaf->device_band_groups); +} + +static DEVICE_ATTR(wusb_device_band_groups, 0600, +		   cbaf_wusb_device_band_groups_show, +		   NULL); + +static ssize_t cbaf_wusb_device_name_show(struct device *dev, +					struct device_attribute *attr, +					char *buf) +{ +	struct usb_interface *iface = to_usb_interface(dev); +	struct cbaf *cbaf = usb_get_intfdata(iface); + +	return scnprintf(buf, PAGE_SIZE, "%s\n", cbaf->device_name); +} +static DEVICE_ATTR(wusb_device_name, 0600, cbaf_wusb_device_name_show, NULL); + +static const struct wusb_cbaf_cc_data cbaf_cc_data_defaults = { +	.AssociationTypeId_hdr    = WUSB_AR_AssociationTypeId, +	.AssociationTypeId    	  = cpu_to_le16(AR_TYPE_WUSB), +	.AssociationSubTypeId_hdr = WUSB_AR_AssociationSubTypeId, +	.AssociationSubTypeId     = cpu_to_le16(AR_TYPE_WUSB_ASSOCIATE), +	.Length_hdr               = WUSB_AR_Length, +	.Length               	  = cpu_to_le32(sizeof(struct wusb_cbaf_cc_data)), +	.ConnectionContext_hdr    = WUSB_AR_ConnectionContext, +	.BandGroups_hdr           = WUSB_AR_BandGroups, +}; + +static const struct wusb_cbaf_cc_data_fail cbaf_cc_data_fail_defaults = { +	.AssociationTypeId_hdr    = WUSB_AR_AssociationTypeId, +	.AssociationSubTypeId_hdr = WUSB_AR_AssociationSubTypeId, +	.Length_hdr               = WUSB_AR_Length, +	.AssociationStatus_hdr    = WUSB_AR_AssociationStatus, +}; + +/* + * Send a new CC to the device. + */ +static int cbaf_cc_upload(struct cbaf *cbaf) +{ +	int result; +	struct device *dev = &cbaf->usb_iface->dev; +	struct wusb_cbaf_cc_data *ccd; +	char pr_cdid[WUSB_CKHDID_STRSIZE]; + +	ccd =  cbaf->buffer; +	*ccd = cbaf_cc_data_defaults; +	ccd->CHID = cbaf->chid; +	ccd->CDID = cbaf->cdid; +	ccd->CK = cbaf->ck; +	ccd->BandGroups = cpu_to_le16(cbaf->host_band_groups); + +	dev_dbg(dev, "Trying to upload CC:\n"); +	ckhdid_printf(pr_cdid, sizeof(pr_cdid), &ccd->CHID); +	dev_dbg(dev, "  CHID       %s\n", pr_cdid); +	ckhdid_printf(pr_cdid, sizeof(pr_cdid), &ccd->CDID); +	dev_dbg(dev, "  CDID       %s\n", pr_cdid); +	dev_dbg(dev, "  Bandgroups 0x%04x\n", cbaf->host_band_groups); + +	result = usb_control_msg( +		cbaf->usb_dev, usb_sndctrlpipe(cbaf->usb_dev, 0), +		CBAF_REQ_SET_ASSOCIATION_RESPONSE, +		USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE, +		0x0201, cbaf->usb_iface->cur_altsetting->desc.bInterfaceNumber, +		ccd, sizeof(*ccd), 1000 /* FIXME: arbitrary */); + +	return result; +} + +static ssize_t cbaf_wusb_ck_store(struct device *dev, +				  struct device_attribute *attr, +				  const char *buf, size_t size) +{ +	ssize_t result; +	struct usb_interface *iface = to_usb_interface(dev); +	struct cbaf *cbaf = usb_get_intfdata(iface); + +	result = sscanf(buf, +			"%02hhx %02hhx %02hhx %02hhx " +			"%02hhx %02hhx %02hhx %02hhx " +			"%02hhx %02hhx %02hhx %02hhx " +			"%02hhx %02hhx %02hhx %02hhx", +			&cbaf->ck.data[0] , &cbaf->ck.data[1], +			&cbaf->ck.data[2] , &cbaf->ck.data[3], +			&cbaf->ck.data[4] , &cbaf->ck.data[5], +			&cbaf->ck.data[6] , &cbaf->ck.data[7], +			&cbaf->ck.data[8] , &cbaf->ck.data[9], +			&cbaf->ck.data[10], &cbaf->ck.data[11], +			&cbaf->ck.data[12], &cbaf->ck.data[13], +			&cbaf->ck.data[14], &cbaf->ck.data[15]); +	if (result != 16) +		return -EINVAL; + +	result = cbaf_cc_upload(cbaf); +	if (result < 0) +		return result; + +	return size; +} +static DEVICE_ATTR(wusb_ck, 0600, NULL, cbaf_wusb_ck_store); + +static struct attribute *cbaf_dev_attrs[] = { +	&dev_attr_wusb_host_name.attr, +	&dev_attr_wusb_host_band_groups.attr, +	&dev_attr_wusb_chid.attr, +	&dev_attr_wusb_cdid.attr, +	&dev_attr_wusb_device_name.attr, +	&dev_attr_wusb_device_band_groups.attr, +	&dev_attr_wusb_ck.attr, +	NULL, +}; + +static struct attribute_group cbaf_dev_attr_group = { +	.name = NULL,	/* we want them in the same directory */ +	.attrs = cbaf_dev_attrs, +}; + +static int cbaf_probe(struct usb_interface *iface, +		      const struct usb_device_id *id) +{ +	struct cbaf *cbaf; +	struct device *dev = &iface->dev; +	int result = -ENOMEM; + +	cbaf = kzalloc(sizeof(*cbaf), GFP_KERNEL); +	if (cbaf == NULL) +		goto error_kzalloc; +	cbaf->buffer = kmalloc(512, GFP_KERNEL); +	if (cbaf->buffer == NULL) +		goto error_kmalloc_buffer; + +	cbaf->buffer_size = 512; +	cbaf->usb_dev = usb_get_dev(interface_to_usbdev(iface)); +	cbaf->usb_iface = usb_get_intf(iface); +	result = cbaf_check(cbaf); +	if (result < 0) { +		dev_err(dev, "This device is not WUSB-CBAF compliant" +			"and is not supported yet.\n"); +		goto error_check; +	} + +	result = sysfs_create_group(&dev->kobj, &cbaf_dev_attr_group); +	if (result < 0) { +		dev_err(dev, "Can't register sysfs attr group: %d\n", result); +		goto error_create_group; +	} +	usb_set_intfdata(iface, cbaf); +	return 0; + +error_create_group: +error_check: +	kfree(cbaf->buffer); +error_kmalloc_buffer: +	kfree(cbaf); +error_kzalloc: +	return result; +} + +static void cbaf_disconnect(struct usb_interface *iface) +{ +	struct cbaf *cbaf = usb_get_intfdata(iface); +	struct device *dev = &iface->dev; +	sysfs_remove_group(&dev->kobj, &cbaf_dev_attr_group); +	usb_set_intfdata(iface, NULL); +	usb_put_intf(iface); +	kfree(cbaf->buffer); +	/* paranoia: clean up crypto keys */ +	memset(cbaf, 0, sizeof(*cbaf)); +	kfree(cbaf); +} + +static struct usb_device_id cbaf_id_table[] = { +	{ USB_INTERFACE_INFO(0xef, 0x03, 0x01), }, +	{ }, +}; +MODULE_DEVICE_TABLE(usb, cbaf_id_table); + +static struct usb_driver cbaf_driver = { +	.name =		"wusb-cbaf", +	.id_table =	cbaf_id_table, +	.probe =	cbaf_probe, +	.disconnect =	cbaf_disconnect, +}; + +static int __init cbaf_driver_init(void) +{ +	return usb_register(&cbaf_driver); +} +module_init(cbaf_driver_init); + +static void __exit cbaf_driver_exit(void) +{ +	usb_deregister(&cbaf_driver); +} +module_exit(cbaf_driver_exit); + +MODULE_AUTHOR("Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>"); +MODULE_DESCRIPTION("Wireless USB Cable Based Association"); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/wusbcore/crypto.c b/drivers/usb/wusbcore/crypto.c new file mode 100644 index 00000000000..c36c4389baa --- /dev/null +++ b/drivers/usb/wusbcore/crypto.c @@ -0,0 +1,538 @@ +/* + * Ultra Wide Band + * AES-128 CCM Encryption + * + * Copyright (C) 2007 Intel Corporation + * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * + * We don't do any encryption here; we use the Linux Kernel's AES-128 + * crypto modules to construct keys and payload blocks in a way + * defined by WUSB1.0[6]. Check the erratas, as typos are are patched + * there. + * + * Thanks a zillion to John Keys for his help and clarifications over + * the designed-by-a-committee text. + * + * So the idea is that there is this basic Pseudo-Random-Function + * defined in WUSB1.0[6.5] which is the core of everything. It works + * by tweaking some blocks, AES crypting them and then xoring + * something else with them (this seems to be called CBC(AES) -- can + * you tell I know jack about crypto?). So we just funnel it into the + * Linux Crypto API. + * + * We leave a crypto test module so we can verify that vectors match, + * every now and then. + * + * Block size: 16 bytes -- AES seems to do things in 'block sizes'. I + *             am learning a lot... + * + *             Conveniently, some data structures that need to be + *             funneled through AES are...16 bytes in size! + */ + +#include <linux/crypto.h> +#include <linux/module.h> +#include <linux/err.h> +#include <linux/uwb.h> +#include <linux/usb/wusb.h> +#include <linux/scatterlist.h> +#define D_LOCAL 0 +#include <linux/uwb/debug.h> + + +/* + * Block of data, as understood by AES-CCM + * + * The code assumes this structure is nothing but a 16 byte array + * (packed in a struct to avoid common mess ups that I usually do with + * arrays and enforcing type checking). + */ +struct aes_ccm_block { +	u8 data[16]; +} __attribute__((packed)); + +/* + * Counter-mode Blocks (WUSB1.0[6.4]) + * + * According to CCM (or so it seems), for the purpose of calculating + * the MIC, the message is broken in N counter-mode blocks, B0, B1, + * ... BN. + * + * B0 contains flags, the CCM nonce and l(m). + * + * B1 contains l(a), the MAC header, the encryption offset and padding. + * + * If EO is nonzero, additional blocks are built from payload bytes + * until EO is exahusted (FIXME: padding to 16 bytes, I guess). The + * padding is not xmitted. + */ + +/* WUSB1.0[T6.4] */ +struct aes_ccm_b0 { +	u8 flags;	/* 0x59, per CCM spec */ +	struct aes_ccm_nonce ccm_nonce; +	__be16 lm; +} __attribute__((packed)); + +/* WUSB1.0[T6.5] */ +struct aes_ccm_b1 { +	__be16 la; +	u8 mac_header[10]; +	__le16 eo; +	u8 security_reserved;	/* This is always zero */ +	u8 padding;		/* 0 */ +} __attribute__((packed)); + +/* + * Encryption Blocks (WUSB1.0[6.4.4]) + * + * CCM uses Ax blocks to generate a keystream with which the MIC and + * the message's payload are encoded. A0 always encrypts/decrypts the + * MIC. Ax (x>0) are used for the sucesive payload blocks. + * + * The x is the counter, and is increased for each block. + */ +struct aes_ccm_a { +	u8 flags;	/* 0x01, per CCM spec */ +	struct aes_ccm_nonce ccm_nonce; +	__be16 counter;	/* Value of x */ +} __attribute__((packed)); + +static void bytewise_xor(void *_bo, const void *_bi1, const void *_bi2, +			 size_t size) +{ +	u8 *bo = _bo; +	const u8 *bi1 = _bi1, *bi2 = _bi2; +	size_t itr; +	for (itr = 0; itr < size; itr++) +		bo[itr] = bi1[itr] ^ bi2[itr]; +} + +/* + * CC-MAC function WUSB1.0[6.5] + * + * Take a data string and produce the encrypted CBC Counter-mode MIC + * + * Note the names for most function arguments are made to (more or + * less) match those used in the pseudo-function definition given in + * WUSB1.0[6.5]. + * + * @tfm_cbc: CBC(AES) blkcipher handle (initialized) + * + * @tfm_aes: AES cipher handle (initialized) + * + * @mic: buffer for placing the computed MIC (Message Integrity + *       Code). This is exactly 8 bytes, and we expect the buffer to + *       be at least eight bytes in length. + * + * @key: 128 bit symmetric key + * + * @n: CCM nonce + * + * @a: ASCII string, 14 bytes long (I guess zero padded if needed; + *     we use exactly 14 bytes). + * + * @b: data stream to be processed; cannot be a global or const local + *     (will confuse the scatterlists) + * + * @blen: size of b... + * + * Still not very clear how this is done, but looks like this: we + * create block B0 (as WUSB1.0[6.5] says), then we AES-crypt it with + * @key. We bytewise xor B0 with B1 (1) and AES-crypt that. Then we + * take the payload and divide it in blocks (16 bytes), xor them with + * the previous crypto result (16 bytes) and crypt it, repeat the next + * block with the output of the previous one, rinse wash (I guess this + * is what AES CBC mode means...but I truly have no idea). So we use + * the CBC(AES) blkcipher, that does precisely that. The IV (Initial + * Vector) is 16 bytes and is set to zero, so + * + * See rfc3610. Linux crypto has a CBC implementation, but the + * documentation is scarce, to say the least, and the example code is + * so intricated that is difficult to understand how things work. Most + * of this is guess work -- bite me. + * + * (1) Created as 6.5 says, again, using as l(a) 'Blen + 14', and + *     using the 14 bytes of @a to fill up + *     b1.{mac_header,e0,security_reserved,padding}. + * + * NOTE: The definiton of l(a) in WUSB1.0[6.5] vs the definition of + *       l(m) is orthogonal, they bear no relationship, so it is not + *       in conflict with the parameter's relation that + *       WUSB1.0[6.4.2]) defines. + * + * NOTE: WUSB1.0[A.1]: Host Nonce is missing a nibble? (1e); fixed in + *       first errata released on 2005/07. + * + * NOTE: we need to clean IV to zero at each invocation to make sure + *       we start with a fresh empty Initial Vector, so that the CBC + *       works ok. + * + * NOTE: blen is not aligned to a block size, we'll pad zeros, that's + *       what sg[4] is for. Maybe there is a smarter way to do this. + */ +static int wusb_ccm_mac(struct crypto_blkcipher *tfm_cbc, +			struct crypto_cipher *tfm_aes, void *mic, +			const struct aes_ccm_nonce *n, +			const struct aes_ccm_label *a, const void *b, +			size_t blen) +{ +	int result = 0; +	struct blkcipher_desc desc; +	struct aes_ccm_b0 b0; +	struct aes_ccm_b1 b1; +	struct aes_ccm_a ax; +	struct scatterlist sg[4], sg_dst; +	void *iv, *dst_buf; +	size_t ivsize, dst_size; +	const u8 bzero[16] = { 0 }; +	size_t zero_padding; + +	d_fnstart(3, NULL, "(tfm_cbc %p, tfm_aes %p, mic %p, " +		  "n %p, a %p, b %p, blen %zu)\n", +		  tfm_cbc, tfm_aes, mic, n, a, b, blen); +	/* +	 * These checks should be compile time optimized out +	 * ensure @a fills b1's mac_header and following fields +	 */ +	WARN_ON(sizeof(*a) != sizeof(b1) - sizeof(b1.la)); +	WARN_ON(sizeof(b0) != sizeof(struct aes_ccm_block)); +	WARN_ON(sizeof(b1) != sizeof(struct aes_ccm_block)); +	WARN_ON(sizeof(ax) != sizeof(struct aes_ccm_block)); + +	result = -ENOMEM; +	zero_padding = sizeof(struct aes_ccm_block) +		- blen % sizeof(struct aes_ccm_block); +	zero_padding = blen % sizeof(struct aes_ccm_block); +	if (zero_padding) +		zero_padding = sizeof(struct aes_ccm_block) - zero_padding; +	dst_size = blen + sizeof(b0) + sizeof(b1) + zero_padding; +	dst_buf = kzalloc(dst_size, GFP_KERNEL); +	if (dst_buf == NULL) { +		printk(KERN_ERR "E: can't alloc destination buffer\n"); +		goto error_dst_buf; +	} + +	iv = crypto_blkcipher_crt(tfm_cbc)->iv; +	ivsize = crypto_blkcipher_ivsize(tfm_cbc); +	memset(iv, 0, ivsize); + +	/* Setup B0 */ +	b0.flags = 0x59;	/* Format B0 */ +	b0.ccm_nonce = *n; +	b0.lm = cpu_to_be16(0);	/* WUSB1.0[6.5] sez l(m) is 0 */ + +	/* Setup B1 +	 * +	 * The WUSB spec is anything but clear! WUSB1.0[6.5] +	 * says that to initialize B1 from A with 'l(a) = blen + +	 * 14'--after clarification, it means to use A's contents +	 * for MAC Header, EO, sec reserved and padding. +	 */ +	b1.la = cpu_to_be16(blen + 14); +	memcpy(&b1.mac_header, a, sizeof(*a)); + +	d_printf(4, NULL, "I: B0 (%zu bytes)\n", sizeof(b0)); +	d_dump(4, NULL, &b0, sizeof(b0)); +	d_printf(4, NULL, "I: B1 (%zu bytes)\n", sizeof(b1)); +	d_dump(4, NULL, &b1, sizeof(b1)); +	d_printf(4, NULL, "I: B (%zu bytes)\n", blen); +	d_dump(4, NULL, b, blen); +	d_printf(4, NULL, "I: B 0-padding (%zu bytes)\n", zero_padding); +	d_printf(4, NULL, "D: IV before crypto (%zu)\n", ivsize); +	d_dump(4, NULL, iv, ivsize); + +	sg_init_table(sg, ARRAY_SIZE(sg)); +	sg_set_buf(&sg[0], &b0, sizeof(b0)); +	sg_set_buf(&sg[1], &b1, sizeof(b1)); +	sg_set_buf(&sg[2], b, blen); +	/* 0 if well behaved :) */ +	sg_set_buf(&sg[3], bzero, zero_padding); +	sg_init_one(&sg_dst, dst_buf, dst_size); + +	desc.tfm = tfm_cbc; +	desc.flags = 0; +	result = crypto_blkcipher_encrypt(&desc, &sg_dst, sg, dst_size); +	if (result < 0) { +		printk(KERN_ERR "E: can't compute CBC-MAC tag (MIC): %d\n", +		       result); +		goto error_cbc_crypt; +	} +	d_printf(4, NULL, "D: MIC tag\n"); +	d_dump(4, NULL, iv, ivsize); + +	/* Now we crypt the MIC Tag (*iv) with Ax -- values per WUSB1.0[6.5] +	 * The procedure is to AES crypt the A0 block and XOR the MIC +	 * Tag agains it; we only do the first 8 bytes and place it +	 * directly in the destination buffer. +	 * +	 * POS Crypto API: size is assumed to be AES's block size. +	 * Thanks for documenting it -- tip taken from airo.c +	 */ +	ax.flags = 0x01;		/* as per WUSB 1.0 spec */ +	ax.ccm_nonce = *n; +	ax.counter = 0; +	crypto_cipher_encrypt_one(tfm_aes, (void *)&ax, (void *)&ax); +	bytewise_xor(mic, &ax, iv, 8); +	d_printf(4, NULL, "D: CTR[MIC]\n"); +	d_dump(4, NULL, &ax, 8); +	d_printf(4, NULL, "D: CCM-MIC tag\n"); +	d_dump(4, NULL, mic, 8); +	result = 8; +error_cbc_crypt: +	kfree(dst_buf); +error_dst_buf: +	d_fnend(3, NULL, "(tfm_cbc %p, tfm_aes %p, mic %p, " +		"n %p, a %p, b %p, blen %zu)\n", +		tfm_cbc, tfm_aes, mic, n, a, b, blen); +	return result; +} + +/* + * WUSB Pseudo Random Function (WUSB1.0[6.5]) + * + * @b: buffer to the source data; cannot be a global or const local + *     (will confuse the scatterlists) + */ +ssize_t wusb_prf(void *out, size_t out_size, +		 const u8 key[16], const struct aes_ccm_nonce *_n, +		 const struct aes_ccm_label *a, +		 const void *b, size_t blen, size_t len) +{ +	ssize_t result, bytes = 0, bitr; +	struct aes_ccm_nonce n = *_n; +	struct crypto_blkcipher *tfm_cbc; +	struct crypto_cipher *tfm_aes; +	u64 sfn = 0; +	__le64 sfn_le; + +	d_fnstart(3, NULL, "(out %p, out_size %zu, key %p, _n %p, " +		  "a %p, b %p, blen %zu, len %zu)\n", out, out_size, +		  key, _n, a, b, blen, len); + +	tfm_cbc = crypto_alloc_blkcipher("cbc(aes)", 0, CRYPTO_ALG_ASYNC); +	if (IS_ERR(tfm_cbc)) { +		result = PTR_ERR(tfm_cbc); +		printk(KERN_ERR "E: can't load CBC(AES): %d\n", (int)result); +		goto error_alloc_cbc; +	} +	result = crypto_blkcipher_setkey(tfm_cbc, key, 16); +	if (result < 0) { +		printk(KERN_ERR "E: can't set CBC key: %d\n", (int)result); +		goto error_setkey_cbc; +	} + +	tfm_aes = crypto_alloc_cipher("aes", 0, CRYPTO_ALG_ASYNC); +	if (IS_ERR(tfm_aes)) { +		result = PTR_ERR(tfm_aes); +		printk(KERN_ERR "E: can't load AES: %d\n", (int)result); +		goto error_alloc_aes; +	} +	result = crypto_cipher_setkey(tfm_aes, key, 16); +	if (result < 0) { +		printk(KERN_ERR "E: can't set AES key: %d\n", (int)result); +		goto error_setkey_aes; +	} + +	for (bitr = 0; bitr < (len + 63) / 64; bitr++) { +		sfn_le = cpu_to_le64(sfn++); +		memcpy(&n.sfn, &sfn_le, sizeof(n.sfn));	/* n.sfn++... */ +		result = wusb_ccm_mac(tfm_cbc, tfm_aes, out + bytes, +				      &n, a, b, blen); +		if (result < 0) +			goto error_ccm_mac; +		bytes += result; +	} +	result = bytes; +error_ccm_mac: +error_setkey_aes: +	crypto_free_cipher(tfm_aes); +error_alloc_aes: +error_setkey_cbc: +	crypto_free_blkcipher(tfm_cbc); +error_alloc_cbc: +	d_fnend(3, NULL, "(out %p, out_size %zu, key %p, _n %p, " +		"a %p, b %p, blen %zu, len %zu) = %d\n", out, out_size, +		key, _n, a, b, blen, len, (int)bytes); +	return result; +} + +/* WUSB1.0[A.2] test vectors */ +static const u8 stv_hsmic_key[16] = { +	0x4b, 0x79, 0xa3, 0xcf, 0xe5, 0x53, 0x23, 0x9d, +	0xd7, 0xc1, 0x6d, 0x1c, 0x2d, 0xab, 0x6d, 0x3f +}; + +static const struct aes_ccm_nonce stv_hsmic_n = { +	.sfn = { 0 }, +	.tkid = { 0x76, 0x98, 0x01,  }, +	.dest_addr = { .data = { 0xbe, 0x00 } }, +		.src_addr = { .data = { 0x76, 0x98 } }, +}; + +/* + * Out-of-band MIC Generation verification code + * + */ +static int wusb_oob_mic_verify(void) +{ +	int result; +	u8 mic[8]; +	/* WUSB1.0[A.2] test vectors +	 * +	 * Need to keep it in the local stack as GCC 4.1.3something +	 * messes up and generates noise. +	 */ +	struct usb_handshake stv_hsmic_hs = { +		.bMessageNumber = 2, +		.bStatus 	= 00, +		.tTKID 		= { 0x76, 0x98, 0x01 }, +		.bReserved 	= 00, +		.CDID 		= { 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, +				    0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, +				    0x3c, 0x3d, 0x3e, 0x3f }, +		.nonce	 	= { 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, +				    0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, +				    0x2c, 0x2d, 0x2e, 0x2f }, +		.MIC	 	= { 0x75, 0x6a, 0x97, 0x51, 0x0c, 0x8c, +				    0x14, 0x7b } , +	}; +	size_t hs_size; + +	result = wusb_oob_mic(mic, stv_hsmic_key, &stv_hsmic_n, &stv_hsmic_hs); +	if (result < 0) +		printk(KERN_ERR "E: WUSB OOB MIC test: failed: %d\n", result); +	else if (memcmp(stv_hsmic_hs.MIC, mic, sizeof(mic))) { +		printk(KERN_ERR "E: OOB MIC test: " +		       "mismatch between MIC result and WUSB1.0[A2]\n"); +		hs_size = sizeof(stv_hsmic_hs) - sizeof(stv_hsmic_hs.MIC); +		printk(KERN_ERR "E: Handshake2 in: (%zu bytes)\n", hs_size); +		dump_bytes(NULL, &stv_hsmic_hs, hs_size); +		printk(KERN_ERR "E: CCM Nonce in: (%zu bytes)\n", +		       sizeof(stv_hsmic_n)); +		dump_bytes(NULL, &stv_hsmic_n, sizeof(stv_hsmic_n)); +		printk(KERN_ERR "E: MIC out:\n"); +		dump_bytes(NULL, mic, sizeof(mic)); +		printk(KERN_ERR "E: MIC out (from WUSB1.0[A.2]):\n"); +		dump_bytes(NULL, stv_hsmic_hs.MIC, sizeof(stv_hsmic_hs.MIC)); +		result = -EINVAL; +	} else +		result = 0; +	return result; +} + +/* + * Test vectors for Key derivation + * + * These come from WUSB1.0[6.5.1], the vectors in WUSB1.0[A.1] + * (errata corrected in 2005/07). + */ +static const u8 stv_key_a1[16] __attribute__ ((__aligned__(4))) = { +	0xf0, 0xe1, 0xd2, 0xc3, 0xb4, 0xa5, 0x96, 0x87, +	0x78, 0x69, 0x5a, 0x4b, 0x3c, 0x2d, 0x1e, 0x0f +}; + +static const struct aes_ccm_nonce stv_keydvt_n_a1 = { +	.sfn = { 0 }, +	.tkid = { 0x76, 0x98, 0x01,  }, +	.dest_addr = { .data = { 0xbe, 0x00 } }, +	.src_addr = { .data = { 0x76, 0x98 } }, +}; + +static const struct wusb_keydvt_out stv_keydvt_out_a1 = { +	.kck = { +		0x4b, 0x79, 0xa3, 0xcf, 0xe5, 0x53, 0x23, 0x9d, +		0xd7, 0xc1, 0x6d, 0x1c, 0x2d, 0xab, 0x6d, 0x3f +	}, +	.ptk = { +		0xc8, 0x70, 0x62, 0x82, 0xb6, 0x7c, 0xe9, 0x06, +		0x7b, 0xc5, 0x25, 0x69, 0xf2, 0x36, 0x61, 0x2d +	} +}; + +/* + * Performa a test to make sure we match the vectors defined in + * WUSB1.0[A.1](Errata2006/12) + */ +static int wusb_key_derive_verify(void) +{ +	int result = 0; +	struct wusb_keydvt_out keydvt_out; +	/* These come from WUSB1.0[A.1] + 2006/12 errata +	 * NOTE: can't make this const or global -- somehow it seems +	 *       the scatterlists for crypto get confused and we get +	 *       bad data. There is no doc on this... */ +	struct wusb_keydvt_in stv_keydvt_in_a1 = { +		.hnonce = { +			0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, +			0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f +		}, +		.dnonce = { +			0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, +			0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f +		} +	}; + +	result = wusb_key_derive(&keydvt_out, stv_key_a1, &stv_keydvt_n_a1, +				 &stv_keydvt_in_a1); +	if (result < 0) +		printk(KERN_ERR "E: WUSB key derivation test: " +		       "derivation failed: %d\n", result); +	if (memcmp(&stv_keydvt_out_a1, &keydvt_out, sizeof(keydvt_out))) { +		printk(KERN_ERR "E: WUSB key derivation test: " +		       "mismatch between key derivation result " +		       "and WUSB1.0[A1] Errata 2006/12\n"); +		printk(KERN_ERR "E: keydvt in: key (%zu bytes)\n", +		       sizeof(stv_key_a1)); +		dump_bytes(NULL, stv_key_a1, sizeof(stv_key_a1)); +		printk(KERN_ERR "E: keydvt in: nonce (%zu bytes)\n", +		       sizeof(stv_keydvt_n_a1)); +		dump_bytes(NULL, &stv_keydvt_n_a1, sizeof(stv_keydvt_n_a1)); +		printk(KERN_ERR "E: keydvt in: hnonce & dnonce (%zu bytes)\n", +		       sizeof(stv_keydvt_in_a1)); +		dump_bytes(NULL, &stv_keydvt_in_a1, sizeof(stv_keydvt_in_a1)); +		printk(KERN_ERR "E: keydvt out: KCK\n"); +		dump_bytes(NULL, &keydvt_out.kck, sizeof(keydvt_out.kck)); +		printk(KERN_ERR "E: keydvt out: PTK\n"); +		dump_bytes(NULL, &keydvt_out.ptk, sizeof(keydvt_out.ptk)); +		result = -EINVAL; +	} else +		result = 0; +	return result; +} + +/* + * Initialize crypto system + * + * FIXME: we do nothing now, other than verifying. Later on we'll + * cache the encryption stuff, so that's why we have a separate init. + */ +int wusb_crypto_init(void) +{ +	int result; + +	result = wusb_key_derive_verify(); +	if (result < 0) +		return result; +	return wusb_oob_mic_verify(); +} + +void wusb_crypto_exit(void) +{ +	/* FIXME: free cached crypto transforms */ +} diff --git a/drivers/usb/wusbcore/dev-sysfs.c b/drivers/usb/wusbcore/dev-sysfs.c new file mode 100644 index 00000000000..7897a19652e --- /dev/null +++ b/drivers/usb/wusbcore/dev-sysfs.c @@ -0,0 +1,143 @@ +/* + * WUSB devices + * sysfs bindings + * + * Copyright (C) 2007 Intel Corporation + * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * + * Get them out of the way... + */ + +#include <linux/jiffies.h> +#include <linux/ctype.h> +#include <linux/workqueue.h> +#include "wusbhc.h" + +#undef D_LOCAL +#define D_LOCAL 4 +#include <linux/uwb/debug.h> + +static ssize_t wusb_disconnect_store(struct device *dev, +				     struct device_attribute *attr, +				     const char *buf, size_t size) +{ +	struct usb_device *usb_dev; +	struct wusbhc *wusbhc; +	unsigned command; +	u8 port_idx; + +	if (sscanf(buf, "%u", &command) != 1) +		return -EINVAL; +	if (command == 0) +		return size; +	usb_dev = to_usb_device(dev); +	wusbhc = wusbhc_get_by_usb_dev(usb_dev); +	if (wusbhc == NULL) +		return -ENODEV; + +	mutex_lock(&wusbhc->mutex); +	port_idx = wusb_port_no_to_idx(usb_dev->portnum); +	__wusbhc_dev_disable(wusbhc, port_idx); +	mutex_unlock(&wusbhc->mutex); +	wusbhc_put(wusbhc); +	return size; +} +static DEVICE_ATTR(wusb_disconnect, 0200, NULL, wusb_disconnect_store); + +static ssize_t wusb_cdid_show(struct device *dev, +			      struct device_attribute *attr, char *buf) +{ +	ssize_t result; +	struct wusb_dev *wusb_dev; + +	wusb_dev = wusb_dev_get_by_usb_dev(to_usb_device(dev)); +	if (wusb_dev == NULL) +		return -ENODEV; +	result = ckhdid_printf(buf, PAGE_SIZE, &wusb_dev->cdid); +	strcat(buf, "\n"); +	wusb_dev_put(wusb_dev); +	return result + 1; +} +static DEVICE_ATTR(wusb_cdid, 0444, wusb_cdid_show, NULL); + +static ssize_t wusb_ck_store(struct device *dev, +			     struct device_attribute *attr, +			     const char *buf, size_t size) +{ +	int result; +	struct usb_device *usb_dev; +	struct wusbhc *wusbhc; +	struct wusb_ckhdid ck; + +	result = sscanf(buf, +			"%02hhx %02hhx %02hhx %02hhx " +			"%02hhx %02hhx %02hhx %02hhx " +			"%02hhx %02hhx %02hhx %02hhx " +			"%02hhx %02hhx %02hhx %02hhx\n", +			&ck.data[0] , &ck.data[1], +			&ck.data[2] , &ck.data[3], +			&ck.data[4] , &ck.data[5], +			&ck.data[6] , &ck.data[7], +			&ck.data[8] , &ck.data[9], +			&ck.data[10], &ck.data[11], +			&ck.data[12], &ck.data[13], +			&ck.data[14], &ck.data[15]); +	if (result != 16) +		return -EINVAL; + +	usb_dev = to_usb_device(dev); +	wusbhc = wusbhc_get_by_usb_dev(usb_dev); +	if (wusbhc == NULL) +		return -ENODEV; +	result = wusb_dev_4way_handshake(wusbhc, usb_dev->wusb_dev, &ck); +	memset(&ck, 0, sizeof(ck)); +	wusbhc_put(wusbhc); +	return result < 0 ? result : size; +} +static DEVICE_ATTR(wusb_ck, 0200, NULL, wusb_ck_store); + +static struct attribute *wusb_dev_attrs[] = { +		&dev_attr_wusb_disconnect.attr, +		&dev_attr_wusb_cdid.attr, +		&dev_attr_wusb_ck.attr, +		NULL, +}; + +static struct attribute_group wusb_dev_attr_group = { +	.name = NULL,	/* we want them in the same directory */ +	.attrs = wusb_dev_attrs, +}; + +int wusb_dev_sysfs_add(struct wusbhc *wusbhc, struct usb_device *usb_dev, +		       struct wusb_dev *wusb_dev) +{ +	int result = sysfs_create_group(&usb_dev->dev.kobj, +					&wusb_dev_attr_group); +	struct device *dev = &usb_dev->dev; +	if (result < 0) +		dev_err(dev, "Cannot register WUSB-dev attributes: %d\n", +			result); +	return result; +} + +void wusb_dev_sysfs_rm(struct wusb_dev *wusb_dev) +{ +	struct usb_device *usb_dev = wusb_dev->usb_dev; +	if (usb_dev) +		sysfs_remove_group(&usb_dev->dev.kobj, &wusb_dev_attr_group); +} diff --git a/drivers/usb/wusbcore/devconnect.c b/drivers/usb/wusbcore/devconnect.c new file mode 100644 index 00000000000..f45d777bef3 --- /dev/null +++ b/drivers/usb/wusbcore/devconnect.c @@ -0,0 +1,1297 @@ +/* + * WUSB Wire Adapter: Control/Data Streaming Interface (WUSB[8]) + * Device Connect handling + * + * Copyright (C) 2006 Intel Corporation + * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * + * FIXME: docs + * FIXME: this file needs to be broken up, it's grown too big + * + * + * WUSB1.0[7.1, 7.5.1, ] + * + * WUSB device connection is kind of messy. Some background: + * + *     When a device wants to connect it scans the UWB radio channels + *     looking for a WUSB Channel; a WUSB channel is defined by MMCs + *     (Micro Managed Commands or something like that) [see + *     Design-overview for more on this] . + * + * So, device scans the radio, finds MMCs and thus a host and checks + * when the next DNTS is. It sends a Device Notification Connect + * (DN_Connect); the host picks it up (through nep.c and notif.c, ends + * up in wusb_devconnect_ack(), which creates a wusb_dev structure in + * wusbhc->port[port_number].wusb_dev), assigns an unauth address + * to the device (this means from 0x80 to 0xfe) and sends, in the MMC + * a Connect Ack Information Element (ConnAck IE). + * + * So now the device now has a WUSB address. From now on, we use + * that to talk to it in the RPipes. + * + * ASSUMPTIONS: + * + *  - We use the the as device address the port number where it is + *    connected (port 0 doesn't exist). For unauth, it is 128 + that. + * + * ROADMAP: + * + *   This file contains the logic for doing that--entry points: + * + *   wusb_devconnect_ack()      Ack a device until _acked() called. + *                              Called by notif.c:wusb_handle_dn_connect() + *                              when a DN_Connect is received. + * + *   wusbhc_devconnect_auth()   Called by rh.c:wusbhc_rh_port_reset() when + *                              doing the device connect sequence. + * + *     wusb_devconnect_acked()  Ack done, release resources. + * + *   wusb_handle_dn_alive()     Called by notif.c:wusb_handle_dn() + *                              for processing a DN_Alive pong from a device. + * + *   wusb_handle_dn_disconnect()Called by notif.c:wusb_handle_dn() to + *                              process a disconenct request from a + *                              device. + * + *   wusb_dev_reset()           Called by rh.c:wusbhc_rh_port_reset() when + *                              resetting a device. + * + *   __wusb_dev_disable()       Called by rh.c:wusbhc_rh_clear_port_feat() when + *                              disabling a port. + * + *   wusb_devconnect_create()   Called when creating the host by + *                              lc.c:wusbhc_create(). + * + *   wusb_devconnect_destroy()  Cleanup called removing the host. Called + *                              by lc.c:wusbhc_destroy(). + * + *   Each Wireless USB host maintains a list of DN_Connect requests + *   (actually we maintain a list of pending Connect Acks, the + *   wusbhc->ca_list). + * + * LIFE CYCLE OF port->wusb_dev + * + *   Before the @wusbhc structure put()s the reference it owns for + *   port->wusb_dev [and clean the wusb_dev pointer], it needs to + *   lock @wusbhc->mutex. + */ + +#include <linux/jiffies.h> +#include <linux/ctype.h> +#include <linux/workqueue.h> +#include "wusbhc.h" + +#undef D_LOCAL +#define D_LOCAL 1 +#include <linux/uwb/debug.h> + +static void wusbhc_devconnect_acked_work(struct work_struct *work); + +static void wusb_dev_free(struct wusb_dev *wusb_dev) +{ +	if (wusb_dev) { +		kfree(wusb_dev->set_gtk_req); +		usb_free_urb(wusb_dev->set_gtk_urb); +		kfree(wusb_dev); +	} +} + +static struct wusb_dev *wusb_dev_alloc(struct wusbhc *wusbhc) +{ +	struct wusb_dev *wusb_dev; +	struct urb *urb; +	struct usb_ctrlrequest *req; + +	wusb_dev = kzalloc(sizeof(*wusb_dev), GFP_KERNEL); +	if (wusb_dev == NULL) +		goto err; + +	wusb_dev->wusbhc = wusbhc; + +	INIT_WORK(&wusb_dev->devconnect_acked_work, wusbhc_devconnect_acked_work); + +	urb = usb_alloc_urb(0, GFP_KERNEL); +	if (urb == NULL) +		goto err; + +	req = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL); +	if (req == NULL) +		goto err; + +	req->bRequestType = USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE; +	req->bRequest = USB_REQ_SET_DESCRIPTOR; +	req->wValue = cpu_to_le16(USB_DT_KEY << 8 | wusbhc->gtk_index); +	req->wIndex = 0; +	req->wLength = cpu_to_le16(wusbhc->gtk.descr.bLength); + +	wusb_dev->set_gtk_urb = urb; +	wusb_dev->set_gtk_req = req; + +	return wusb_dev; +err: +	wusb_dev_free(wusb_dev); +	return NULL; +} + + +/* + * Using the Connect-Ack list, fill out the @wusbhc Connect-Ack WUSB IE + * properly so that it can be added to the MMC. + * + * We just get the @wusbhc->ca_list and fill out the first four ones or + * less (per-spec WUSB1.0[7.5, before T7-38). If the ConnectAck WUSB + * IE is not allocated, we alloc it. + * + * @wusbhc->mutex must be taken + */ +static void wusbhc_fill_cack_ie(struct wusbhc *wusbhc) +{ +	unsigned cnt; +	struct wusb_dev *dev_itr; +	struct wuie_connect_ack *cack_ie; + +	cack_ie = &wusbhc->cack_ie; +	cnt = 0; +	list_for_each_entry(dev_itr, &wusbhc->cack_list, cack_node) { +		cack_ie->blk[cnt].CDID = dev_itr->cdid; +		cack_ie->blk[cnt].bDeviceAddress = dev_itr->addr; +		if (++cnt >= WUIE_ELT_MAX) +			break; +	} +	cack_ie->hdr.bLength = sizeof(cack_ie->hdr) +		+ cnt * sizeof(cack_ie->blk[0]); +} + +/* + * Register a new device that wants to connect + * + * A new device wants to connect, so we add it to the Connect-Ack + * list. We give it an address in the unauthorized range (bit 8 set); + * user space will have to drive authorization further on. + * + * @dev_addr: address to use for the device (which is also the port + *            number). + * + * @wusbhc->mutex must be taken + */ +static struct wusb_dev *wusbhc_cack_add(struct wusbhc *wusbhc, +					struct wusb_dn_connect *dnc, +					const char *pr_cdid, u8 port_idx) +{ +	struct device *dev = wusbhc->dev; +	struct wusb_dev *wusb_dev; +	int new_connection = wusb_dn_connect_new_connection(dnc); +	u8 dev_addr; +	int result; + +	/* Is it registered already? */ +	list_for_each_entry(wusb_dev, &wusbhc->cack_list, cack_node) +		if (!memcmp(&wusb_dev->cdid, &dnc->CDID, +			    sizeof(wusb_dev->cdid))) +			return wusb_dev; +	/* We don't have it, create an entry, register it */ +	wusb_dev = wusb_dev_alloc(wusbhc); +	if (wusb_dev == NULL) +		return NULL; +	wusb_dev_init(wusb_dev); +	wusb_dev->cdid = dnc->CDID; +	wusb_dev->port_idx = port_idx; + +	/* +	 * Devices are always available within the cluster reservation +	 * and since the hardware will take the intersection of the +	 * per-device availability and the cluster reservation, the +	 * per-device availability can simply be set to always +	 * available. +	 */ +	bitmap_fill(wusb_dev->availability.bm, UWB_NUM_MAS); + +	/* FIXME: handle reconnects instead of assuming connects are +	   always new. */ +	if (1 && new_connection == 0) +		new_connection = 1; +	if (new_connection) { +		dev_addr = (port_idx + 2) | WUSB_DEV_ADDR_UNAUTH; + +		dev_info(dev, "Connecting new WUSB device to address %u, " +			"port %u\n", dev_addr, port_idx); + +		result = wusb_set_dev_addr(wusbhc, wusb_dev, dev_addr); +		if (result < 0) +			return NULL; +	} +	wusb_dev->entry_ts = jiffies; +	list_add_tail(&wusb_dev->cack_node, &wusbhc->cack_list); +	wusbhc->cack_count++; +	wusbhc_fill_cack_ie(wusbhc); +	return wusb_dev; +} + +/* + * Remove a Connect-Ack context entry from the HCs view + * + * @wusbhc->mutex must be taken + */ +static void wusbhc_cack_rm(struct wusbhc *wusbhc, struct wusb_dev *wusb_dev) +{ +	struct device *dev = wusbhc->dev; +	d_fnstart(3, dev, "(wusbhc %p wusb_dev %p)\n", wusbhc, wusb_dev); +	list_del_init(&wusb_dev->cack_node); +	wusbhc->cack_count--; +	wusbhc_fill_cack_ie(wusbhc); +	d_fnend(3, dev, "(wusbhc %p wusb_dev %p) = void\n", wusbhc, wusb_dev); +} + +/* + * @wusbhc->mutex must be taken */ +static +void wusbhc_devconnect_acked(struct wusbhc *wusbhc, struct wusb_dev *wusb_dev) +{ +	struct device *dev = wusbhc->dev; +	d_fnstart(3, dev, "(wusbhc %p wusb_dev %p)\n", wusbhc, wusb_dev); +	wusbhc_cack_rm(wusbhc, wusb_dev); +	if (wusbhc->cack_count) +		wusbhc_mmcie_set(wusbhc, 0, 0, &wusbhc->cack_ie.hdr); +	else +		wusbhc_mmcie_rm(wusbhc, &wusbhc->cack_ie.hdr); +	d_fnend(3, dev, "(wusbhc %p wusb_dev %p) = void\n", wusbhc, wusb_dev); +} + +static void wusbhc_devconnect_acked_work(struct work_struct *work) +{ +	struct wusb_dev *wusb_dev = container_of(work, struct wusb_dev, +						 devconnect_acked_work); +	struct wusbhc *wusbhc = wusb_dev->wusbhc; + +	mutex_lock(&wusbhc->mutex); +	wusbhc_devconnect_acked(wusbhc, wusb_dev); +	mutex_unlock(&wusbhc->mutex); +} + +/* + * Ack a device for connection + * + * FIXME: docs + * + * @pr_cdid:	Printable CDID...hex Use @dnc->cdid for the real deal. + * + * So we get the connect ack IE (may have been allocated already), + * find an empty connect block, an empty virtual port, create an + * address with it (see below), make it an unauth addr [bit 7 set] and + * set the MMC. + * + * Addresses: because WUSB hosts have no downstream hubs, we can do a + *            1:1 mapping between 'port number' and device + *            address. This simplifies many things, as during this + *            initial connect phase the USB stack has no knoledge of + *            the device and hasn't assigned an address yet--we know + *            USB's choose_address() will use the same euristics we + *            use here, so we can assume which address will be assigned. + * + *            USB stack always assigns address 1 to the root hub, so + *            to the port number we add 2 (thus virtual port #0 is + *            addr #2). + * + * @wusbhc shall be referenced + */ +static +void wusbhc_devconnect_ack(struct wusbhc *wusbhc, struct wusb_dn_connect *dnc, +			   const char *pr_cdid) +{ +	int result; +	struct device *dev = wusbhc->dev; +	struct wusb_dev *wusb_dev; +	struct wusb_port *port; +	unsigned idx, devnum; + +	d_fnstart(3, dev, "(%p, %p, %s)\n", wusbhc, dnc, pr_cdid); +	mutex_lock(&wusbhc->mutex); + +	/* Check we are not handling it already */ +	for (idx = 0; idx < wusbhc->ports_max; idx++) { +		port = wusb_port_by_idx(wusbhc, idx); +		if (port->wusb_dev +		    && memcmp(&dnc->CDID, &port->wusb_dev->cdid, sizeof(dnc->CDID)) == 0) +			goto error_unlock; +	} +	/* Look up those fake ports we have for a free one */ +	for (idx = 0; idx < wusbhc->ports_max; idx++) { +		port = wusb_port_by_idx(wusbhc, idx); +		if ((port->status & USB_PORT_STAT_POWER) +		    && !(port->status & USB_PORT_STAT_CONNECTION)) +			break; +	} +	if (idx >= wusbhc->ports_max) { +		dev_err(dev, "Host controller can't connect more devices " +			"(%u already connected); device %s rejected\n", +			wusbhc->ports_max, pr_cdid); +		/* NOTE: we could send a WUIE_Disconnect here, but we haven't +		 *       event acked, so the device will eventually timeout the +		 *       connection, right? */ +		goto error_unlock; +	} + +	devnum = idx + 2; + +	/* Make sure we are using no crypto on that "virtual port" */ +	wusbhc->set_ptk(wusbhc, idx, 0, NULL, 0); + +	/* Grab a filled in Connect-Ack context, fill out the +	 * Connect-Ack Wireless USB IE, set the MMC */ +	wusb_dev = wusbhc_cack_add(wusbhc, dnc, pr_cdid, idx); +	if (wusb_dev == NULL) +		goto error_unlock; +	result = wusbhc_mmcie_set(wusbhc, 0, 0, &wusbhc->cack_ie.hdr); +	if (result < 0) +		goto error_unlock; +	/* Give the device at least 2ms (WUSB1.0[7.5.1p3]), let's do +	 * three for a good measure */ +	msleep(3); +	port->wusb_dev = wusb_dev; +	port->status |= USB_PORT_STAT_CONNECTION; +	port->change |= USB_PORT_STAT_C_CONNECTION; +	port->reset_count = 0; +	/* Now the port status changed to connected; khubd will +	 * pick the change up and try to reset the port to bring it to +	 * the enabled state--so this process returns up to the stack +	 * and it calls back into wusbhc_rh_port_reset() who will call +	 * devconnect_auth(). +	 */ +error_unlock: +	mutex_unlock(&wusbhc->mutex); +	d_fnend(3, dev, "(%p, %p, %s) = void\n", wusbhc, dnc, pr_cdid); +	return; + +} + +/* + * Disconnect a Wireless USB device from its fake port + * + * Marks the port as disconnected so that khubd can pick up the change + * and drops our knowledge about the device. + * + * Assumes there is a device connected + * + * @port_index: zero based port number + * + * NOTE: @wusbhc->mutex is locked + * + * WARNING: From here it is not very safe to access anything hanging off + *	    wusb_dev + */ +static void __wusbhc_dev_disconnect(struct wusbhc *wusbhc, +				    struct wusb_port *port) +{ +	struct device *dev = wusbhc->dev; +	struct wusb_dev *wusb_dev = port->wusb_dev; + +	d_fnstart(3, dev, "(wusbhc %p, port %p)\n", wusbhc, port); +	port->status &= ~(USB_PORT_STAT_CONNECTION | USB_PORT_STAT_ENABLE +			  | USB_PORT_STAT_SUSPEND | USB_PORT_STAT_RESET +			  | USB_PORT_STAT_LOW_SPEED | USB_PORT_STAT_HIGH_SPEED); +	port->change |= USB_PORT_STAT_C_CONNECTION | USB_PORT_STAT_C_ENABLE; +	if (wusb_dev) { +		if (!list_empty(&wusb_dev->cack_node)) +			list_del_init(&wusb_dev->cack_node); +		/* For the one in cack_add() */ +		wusb_dev_put(wusb_dev); +	} +	port->wusb_dev = NULL; +	/* don't reset the reset_count to zero or wusbhc_rh_port_reset will get +	 * confused! We only reset to zero when we connect a new device. +	 */ + +	/* After a device disconnects, change the GTK (see [WUSB] +	 * section 6.2.11.2). */ +	wusbhc_gtk_rekey(wusbhc); + +	d_fnend(3, dev, "(wusbhc %p, port %p) = void\n", wusbhc, port); +	/* The Wireless USB part has forgotten about the device already; now +	 * khubd's timer will pick up the disconnection and remove the USB +	 * device from the system +	 */ +} + +/* + * Authenticate a device into the WUSB Cluster + * + * Called from the Root Hub code (rh.c:wusbhc_rh_port_reset()) when + * asking for a reset on a port that is not enabled (ie: first connect + * on the port). + * + * Performs the 4way handshake to allow the device to comunicate w/ the + * WUSB Cluster securely; once done, issue a request to the device for + * it to change to address 0. + * + * This mimics the reset step of Wired USB that once resetting a + * device, leaves the port in enabled state and the dev with the + * default address (0). + * + * WUSB1.0[7.1.2] + * + * @port_idx: port where the change happened--This is the index into + *            the wusbhc port array, not the USB port number. + */ +int wusbhc_devconnect_auth(struct wusbhc *wusbhc, u8 port_idx) +{ +	struct device *dev = wusbhc->dev; +	struct wusb_port *port = wusb_port_by_idx(wusbhc, port_idx); + +	d_fnstart(3, dev, "(%p, %u)\n", wusbhc, port_idx); +	port->status &= ~USB_PORT_STAT_RESET; +	port->status |= USB_PORT_STAT_ENABLE; +	port->change |= USB_PORT_STAT_C_RESET | USB_PORT_STAT_C_ENABLE; +	d_fnend(3, dev, "(%p, %u) = 0\n", wusbhc, port_idx); +	return 0; +} + +/* + * Refresh the list of keep alives to emit in the MMC + * + * Some devices don't respond to keep alives unless they've been + * authenticated, so skip unauthenticated devices. + * + * We only publish the first four devices that have a coming timeout + * condition. Then when we are done processing those, we go for the + * next ones. We ignore the ones that have timed out already (they'll + * be purged). + * + * This might cause the first devices to timeout the last devices in + * the port array...FIXME: come up with a better algorithm? + * + * Note we can't do much about MMC's ops errors; we hope next refresh + * will kind of handle it. + * + * NOTE: @wusbhc->mutex is locked + */ +static void __wusbhc_keep_alive(struct wusbhc *wusbhc) +{ +	struct device *dev = wusbhc->dev; +	unsigned cnt; +	struct wusb_dev *wusb_dev; +	struct wusb_port *wusb_port; +	struct wuie_keep_alive *ie = &wusbhc->keep_alive_ie; +	unsigned keep_alives, old_keep_alives; + +	old_keep_alives = ie->hdr.bLength - sizeof(ie->hdr); +	keep_alives = 0; +	for (cnt = 0; +	     keep_alives <= WUIE_ELT_MAX && cnt < wusbhc->ports_max; +	     cnt++) { +		unsigned tt = msecs_to_jiffies(wusbhc->trust_timeout); + +		wusb_port = wusb_port_by_idx(wusbhc, cnt); +		wusb_dev = wusb_port->wusb_dev; + +		if (wusb_dev == NULL) +			continue; +		if (wusb_dev->usb_dev == NULL || !wusb_dev->usb_dev->authenticated) +			continue; + +		if (time_after(jiffies, wusb_dev->entry_ts + tt)) { +			dev_err(dev, "KEEPALIVE: device %u timed out\n", +				wusb_dev->addr); +			__wusbhc_dev_disconnect(wusbhc, wusb_port); +		} else if (time_after(jiffies, wusb_dev->entry_ts + tt/2)) { +			/* Approaching timeout cut out, need to refresh */ +			ie->bDeviceAddress[keep_alives++] = wusb_dev->addr; +		} +	} +	if (keep_alives & 0x1)	/* pad to even number ([WUSB] section 7.5.9) */ +		ie->bDeviceAddress[keep_alives++] = 0x7f; +	ie->hdr.bLength = sizeof(ie->hdr) + +		keep_alives*sizeof(ie->bDeviceAddress[0]); +	if (keep_alives > 0) +		wusbhc_mmcie_set(wusbhc, 10, 5, &ie->hdr); +	else if (old_keep_alives != 0) +		wusbhc_mmcie_rm(wusbhc, &ie->hdr); +} + +/* + * Do a run through all devices checking for timeouts + */ +static void wusbhc_keep_alive_run(struct work_struct *ws) +{ +	struct delayed_work *dw = +		container_of(ws, struct delayed_work, work); +	struct wusbhc *wusbhc = +		container_of(dw, struct wusbhc, keep_alive_timer); + +	d_fnstart(5, wusbhc->dev, "(wusbhc %p)\n", wusbhc); +	if (wusbhc->active) { +		mutex_lock(&wusbhc->mutex); +		__wusbhc_keep_alive(wusbhc); +		mutex_unlock(&wusbhc->mutex); +		queue_delayed_work(wusbd, &wusbhc->keep_alive_timer, +				   (wusbhc->trust_timeout * CONFIG_HZ)/1000/2); +	} +	d_fnend(5, wusbhc->dev, "(wusbhc %p) = void\n", wusbhc); +	return; +} + +/* + * Find the wusb_dev from its device address. + * + * The device can be found directly from the address (see + * wusb_cack_add() for where the device address is set to port_idx + * +2), except when the address is zero. + */ +static struct wusb_dev *wusbhc_find_dev_by_addr(struct wusbhc *wusbhc, u8 addr) +{ +	int p; + +	if (addr == 0xff) /* unconnected */ +		return NULL; + +	if (addr > 0) { +		int port = (addr & ~0x80) - 2; +		if (port < 0 || port >= wusbhc->ports_max) +			return NULL; +		return wusb_port_by_idx(wusbhc, port)->wusb_dev; +	} + +	/* Look for the device with address 0. */ +	for (p = 0; p < wusbhc->ports_max; p++) { +		struct wusb_dev *wusb_dev = wusb_port_by_idx(wusbhc, p)->wusb_dev; +		if (wusb_dev && wusb_dev->addr == addr) +			return wusb_dev; +	} +	return NULL; +} + +/* + * Handle a DN_Alive notification (WUSB1.0[7.6.1]) + * + * This just updates the device activity timestamp and then refreshes + * the keep alive IE. + * + * @wusbhc shall be referenced and unlocked + */ +static void wusbhc_handle_dn_alive(struct wusbhc *wusbhc, struct wusb_dev *wusb_dev) +{ +	struct device *dev = wusbhc->dev; + +	d_printf(2, dev, "DN ALIVE: device 0x%02x pong\n", wusb_dev->addr); + +	mutex_lock(&wusbhc->mutex); +	wusb_dev->entry_ts = jiffies; +	__wusbhc_keep_alive(wusbhc); +	mutex_unlock(&wusbhc->mutex); +} + +/* + * Handle a DN_Connect notification (WUSB1.0[7.6.1]) + * + * @wusbhc + * @pkt_hdr + * @size:    Size of the buffer where the notification resides; if the + *           notification data suggests there should be more data than + *           available, an error will be signaled and the whole buffer + *           consumed. + * + * @wusbhc->mutex shall be held + */ +static void wusbhc_handle_dn_connect(struct wusbhc *wusbhc, +				     struct wusb_dn_hdr *dn_hdr, +				     size_t size) +{ +	struct device *dev = wusbhc->dev; +	struct wusb_dn_connect *dnc; +	char pr_cdid[WUSB_CKHDID_STRSIZE]; +	static const char *beacon_behaviour[] = { +		"reserved", +		"self-beacon", +		"directed-beacon", +		"no-beacon" +	}; + +	d_fnstart(3, dev, "(%p, %p, %zu)\n", wusbhc, dn_hdr, size); +	if (size < sizeof(*dnc)) { +		dev_err(dev, "DN CONNECT: short notification (%zu < %zu)\n", +			size, sizeof(*dnc)); +		goto out; +	} + +	dnc = container_of(dn_hdr, struct wusb_dn_connect, hdr); +	ckhdid_printf(pr_cdid, sizeof(pr_cdid), &dnc->CDID); +	dev_info(dev, "DN CONNECT: device %s @ %x (%s) wants to %s\n", +		 pr_cdid, +		 wusb_dn_connect_prev_dev_addr(dnc), +		 beacon_behaviour[wusb_dn_connect_beacon_behavior(dnc)], +		 wusb_dn_connect_new_connection(dnc) ? "connect" : "reconnect"); +	/* ACK the connect */ +	wusbhc_devconnect_ack(wusbhc, dnc, pr_cdid); +out: +	d_fnend(3, dev, "(%p, %p, %zu) = void\n", +		wusbhc, dn_hdr, size); +	return; +} + +/* + * Handle a DN_Disconnect notification (WUSB1.0[7.6.1]) + * + * Device is going down -- do the disconnect. + * + * @wusbhc shall be referenced and unlocked + */ +static void wusbhc_handle_dn_disconnect(struct wusbhc *wusbhc, struct wusb_dev *wusb_dev) +{ +	struct device *dev = wusbhc->dev; + +	dev_info(dev, "DN DISCONNECT: device 0x%02x going down\n", wusb_dev->addr); + +	mutex_lock(&wusbhc->mutex); +	__wusbhc_dev_disconnect(wusbhc, wusb_port_by_idx(wusbhc, wusb_dev->port_idx)); +	mutex_unlock(&wusbhc->mutex); +} + +/* + * Reset a WUSB device on a HWA + * + * @wusbhc + * @port_idx   Index of the port where the device is + * + * In Wireless USB, a reset is more or less equivalent to a full + * disconnect; so we just do a full disconnect and send the device a + * Device Reset IE (WUSB1.0[7.5.11]) giving it a few millisecs (6 MMCs). + * + * @wusbhc should be refcounted and unlocked + */ +int wusbhc_dev_reset(struct wusbhc *wusbhc, u8 port_idx) +{ +	int result; +	struct device *dev = wusbhc->dev; +	struct wusb_dev *wusb_dev; +	struct wuie_reset *ie; + +	d_fnstart(3, dev, "(%p, %u)\n", wusbhc, port_idx); +	mutex_lock(&wusbhc->mutex); +	result = 0; +	wusb_dev = wusb_port_by_idx(wusbhc, port_idx)->wusb_dev; +	if (wusb_dev == NULL) { +		/* reset no device? ignore */ +		dev_dbg(dev, "RESET: no device at port %u, ignoring\n", +			port_idx); +		goto error_unlock; +	} +	result = -ENOMEM; +	ie = kzalloc(sizeof(*ie), GFP_KERNEL); +	if (ie == NULL) +		goto error_unlock; +	ie->hdr.bLength = sizeof(ie->hdr) + sizeof(ie->CDID); +	ie->hdr.bIEIdentifier = WUIE_ID_RESET_DEVICE; +	ie->CDID = wusb_dev->cdid; +	result = wusbhc_mmcie_set(wusbhc, 0xff, 6, &ie->hdr); +	if (result < 0) { +		dev_err(dev, "RESET: cant's set MMC: %d\n", result); +		goto error_kfree; +	} +	__wusbhc_dev_disconnect(wusbhc, wusb_port_by_idx(wusbhc, port_idx)); + +	/* 120ms, hopefully 6 MMCs (FIXME) */ +	msleep(120); +	wusbhc_mmcie_rm(wusbhc, &ie->hdr); +error_kfree: +	kfree(ie); +error_unlock: +	mutex_unlock(&wusbhc->mutex); +	d_fnend(3, dev, "(%p, %u) = %d\n", wusbhc, port_idx, result); +	return result; +} + +/* + * Handle a Device Notification coming a host + * + * The Device Notification comes from a host (HWA, DWA or WHCI) + * wrapped in a set of headers. Somebody else has peeled off those + * headers for us and we just get one Device Notifications. + * + * Invalid DNs (e.g., too short) are discarded. + * + * @wusbhc shall be referenced + * + * FIXMES: + *  - implement priorities as in WUSB1.0[Table 7-55]? + */ +void wusbhc_handle_dn(struct wusbhc *wusbhc, u8 srcaddr, +		      struct wusb_dn_hdr *dn_hdr, size_t size) +{ +	struct device *dev = wusbhc->dev; +	struct wusb_dev *wusb_dev; + +	d_fnstart(3, dev, "(%p, %p)\n", wusbhc, dn_hdr); + +	if (size < sizeof(struct wusb_dn_hdr)) { +		dev_err(dev, "DN data shorter than DN header (%d < %d)\n", +			(int)size, (int)sizeof(struct wusb_dn_hdr)); +		goto out; +	} + +	wusb_dev = wusbhc_find_dev_by_addr(wusbhc, srcaddr); +	if (wusb_dev == NULL && dn_hdr->bType != WUSB_DN_CONNECT) { +		dev_dbg(dev, "ignoring DN %d from unconnected device %02x\n", +			dn_hdr->bType, srcaddr); +		goto out; +	} + +	switch (dn_hdr->bType) { +	case WUSB_DN_CONNECT: +		wusbhc_handle_dn_connect(wusbhc, dn_hdr, size); +		break; +	case WUSB_DN_ALIVE: +		wusbhc_handle_dn_alive(wusbhc, wusb_dev); +		break; +	case WUSB_DN_DISCONNECT: +		wusbhc_handle_dn_disconnect(wusbhc, wusb_dev); +		break; +	case WUSB_DN_MASAVAILCHANGED: +	case WUSB_DN_RWAKE: +	case WUSB_DN_SLEEP: +		/* FIXME: handle these DNs. */ +		break; +	case WUSB_DN_EPRDY: +		/* The hardware handles these. */ +		break; +	default: +		dev_warn(dev, "unknown DN %u (%d octets) from %u\n", +			 dn_hdr->bType, (int)size, srcaddr); +	} +out: +	d_fnend(3, dev, "(%p, %p) = void\n", wusbhc, dn_hdr); +	return; +} +EXPORT_SYMBOL_GPL(wusbhc_handle_dn); + +/* + * Disconnect a WUSB device from a the cluster + * + * @wusbhc + * @port     Fake port where the device is (wusbhc index, not USB port number). + * + * In Wireless USB, a disconnect is basically telling the device he is + * being disconnected and forgetting about him. + * + * We send the device a Device Disconnect IE (WUSB1.0[7.5.11]) for 100 + * ms and then keep going. + * + * We don't do much in case of error; we always pretend we disabled + * the port and disconnected the device. If physically the request + * didn't get there (many things can fail in the way there), the stack + * will reject the device's communication attempts. + * + * @wusbhc should be refcounted and locked + */ +void __wusbhc_dev_disable(struct wusbhc *wusbhc, u8 port_idx) +{ +	int result; +	struct device *dev = wusbhc->dev; +	struct wusb_dev *wusb_dev; +	struct wuie_disconnect *ie; + +	d_fnstart(3, dev, "(%p, %u)\n", wusbhc, port_idx); +	result = 0; +	wusb_dev = wusb_port_by_idx(wusbhc, port_idx)->wusb_dev; +	if (wusb_dev == NULL) { +		/* reset no device? ignore */ +		dev_dbg(dev, "DISCONNECT: no device at port %u, ignoring\n", +			port_idx); +		goto error; +	} +	__wusbhc_dev_disconnect(wusbhc, wusb_port_by_idx(wusbhc, port_idx)); + +	result = -ENOMEM; +	ie = kzalloc(sizeof(*ie), GFP_KERNEL); +	if (ie == NULL) +		goto error; +	ie->hdr.bLength = sizeof(*ie); +	ie->hdr.bIEIdentifier = WUIE_ID_DEVICE_DISCONNECT; +	ie->bDeviceAddress = wusb_dev->addr; +	result = wusbhc_mmcie_set(wusbhc, 0, 0, &ie->hdr); +	if (result < 0) { +		dev_err(dev, "DISCONNECT: can't set MMC: %d\n", result); +		goto error_kfree; +	} + +	/* 120ms, hopefully 6 MMCs */ +	msleep(100); +	wusbhc_mmcie_rm(wusbhc, &ie->hdr); +error_kfree: +	kfree(ie); +error: +	d_fnend(3, dev, "(%p, %u) = %d\n", wusbhc, port_idx, result); +	return; +} + +static void wusb_cap_descr_printf(const unsigned level, struct device *dev, +				  const struct usb_wireless_cap_descriptor *wcd) +{ +	d_printf(level, dev, +		 "WUSB Capability Descriptor\n" +		 "  bDevCapabilityType          0x%02x\n" +		 "  bmAttributes                0x%02x\n" +		 "  wPhyRates                   0x%04x\n" +		 "  bmTFITXPowerInfo            0x%02x\n" +		 "  bmFFITXPowerInfo            0x%02x\n" +		 "  bmBandGroup                 0x%04x\n" +		 "  bReserved                   0x%02x\n", +		 wcd->bDevCapabilityType, +		 wcd->bmAttributes, +		 le16_to_cpu(wcd->wPHYRates), +		 wcd->bmTFITXPowerInfo, +		 wcd->bmFFITXPowerInfo, +		 wcd->bmBandGroup, +		 wcd->bReserved); +} + +/* + * Walk over the BOS descriptor, verify and grok it + * + * @usb_dev: referenced + * @wusb_dev: referenced and unlocked + * + * The BOS descriptor is defined at WUSB1.0[7.4.1], and it defines a + * "flexible" way to wrap all kinds of descriptors inside an standard + * descriptor (wonder why they didn't use normal descriptors, + * btw). Not like they lack code. + * + * At the end we go to look for the WUSB Device Capabilities + * (WUSB1.0[7.4.1.1]) that is wrapped in a device capability descriptor + * that is part of the BOS descriptor set. That tells us what does the + * device support (dual role, beacon type, UWB PHY rates). + */ +static int wusb_dev_bos_grok(struct usb_device *usb_dev, +			     struct wusb_dev *wusb_dev, +			     struct usb_bos_descriptor *bos, size_t desc_size) +{ +	ssize_t result; +	struct device *dev = &usb_dev->dev; +	void *itr, *top; + +	/* Walk over BOS capabilities, verify them */ +	itr = (void *)bos + sizeof(*bos); +	top = itr + desc_size - sizeof(*bos); +	while (itr < top) { +		struct usb_dev_cap_header *cap_hdr = itr; +		size_t cap_size; +		u8 cap_type; +		if (top - itr < sizeof(*cap_hdr)) { +			dev_err(dev, "Device BUG? premature end of BOS header " +				"data [offset 0x%02x]: only %zu bytes left\n", +				(int)(itr - (void *)bos), top - itr); +			result = -ENOSPC; +			goto error_bad_cap; +		} +		cap_size = cap_hdr->bLength; +		cap_type = cap_hdr->bDevCapabilityType; +		d_printf(4, dev, "BOS Capability: 0x%02x (%zu bytes)\n", +			 cap_type, cap_size); +		if (cap_size == 0) +			break; +		if (cap_size > top - itr) { +			dev_err(dev, "Device BUG? premature end of BOS data " +				"[offset 0x%02x cap %02x %zu bytes]: " +				"only %zu bytes left\n", +				(int)(itr - (void *)bos), +				cap_type, cap_size, top - itr); +			result = -EBADF; +			goto error_bad_cap; +		} +		d_dump(3, dev, itr, cap_size); +		switch (cap_type) { +		case USB_CAP_TYPE_WIRELESS_USB: +			if (cap_size != sizeof(*wusb_dev->wusb_cap_descr)) +				dev_err(dev, "Device BUG? WUSB Capability " +					"descriptor is %zu bytes vs %zu " +					"needed\n", cap_size, +					sizeof(*wusb_dev->wusb_cap_descr)); +			else { +				wusb_dev->wusb_cap_descr = itr; +				wusb_cap_descr_printf(3, dev, itr); +			} +			break; +		default: +			dev_err(dev, "BUG? Unknown BOS capability 0x%02x " +				"(%zu bytes) at offset 0x%02x\n", cap_type, +				cap_size, (int)(itr - (void *)bos)); +		} +		itr += cap_size; +	} +	result = 0; +error_bad_cap: +	return result; +} + +/* + * Add information from the BOS descriptors to the device + * + * @usb_dev: referenced + * @wusb_dev: referenced and unlocked + * + * So what we do is we alloc a space for the BOS descriptor of 64 + * bytes; read the first four bytes which include the wTotalLength + * field (WUSB1.0[T7-26]) and if it fits in those 64 bytes, read the + * whole thing. If not we realloc to that size. + * + * Then we call the groking function, that will fill up + * wusb_dev->wusb_cap_descr, which is what we'll need later on. + */ +static int wusb_dev_bos_add(struct usb_device *usb_dev, +			    struct wusb_dev *wusb_dev) +{ +	ssize_t result; +	struct device *dev = &usb_dev->dev; +	struct usb_bos_descriptor *bos; +	size_t alloc_size = 32, desc_size = 4; + +	bos = kmalloc(alloc_size, GFP_KERNEL); +	if (bos == NULL) +		return -ENOMEM; +	result = usb_get_descriptor(usb_dev, USB_DT_BOS, 0, bos, desc_size); +	if (result < 4) { +		dev_err(dev, "Can't get BOS descriptor or too short: %zd\n", +			result); +		goto error_get_descriptor; +	} +	desc_size = le16_to_cpu(bos->wTotalLength); +	if (desc_size >= alloc_size) { +		kfree(bos); +		alloc_size = desc_size; +		bos = kmalloc(alloc_size, GFP_KERNEL); +		if (bos == NULL) +			return -ENOMEM; +	} +	result = usb_get_descriptor(usb_dev, USB_DT_BOS, 0, bos, desc_size); +	if (result < 0 || result != desc_size) { +		dev_err(dev, "Can't get  BOS descriptor or too short (need " +			"%zu bytes): %zd\n", desc_size, result); +		goto error_get_descriptor; +	} +	if (result < sizeof(*bos) +	    || le16_to_cpu(bos->wTotalLength) != desc_size) { +		dev_err(dev, "Can't get  BOS descriptor or too short (need " +			"%zu bytes): %zd\n", desc_size, result); +		goto error_get_descriptor; +	} +	d_printf(2, dev, "Got BOS descriptor %zd bytes, %u capabilities\n", +		 result, bos->bNumDeviceCaps); +	d_dump(2, dev, bos, result); +	result = wusb_dev_bos_grok(usb_dev, wusb_dev, bos, result); +	if (result < 0) +		goto error_bad_bos; +	wusb_dev->bos = bos; +	return 0; + +error_bad_bos: +error_get_descriptor: +	kfree(bos); +	wusb_dev->wusb_cap_descr = NULL; +	return result; +} + +static void wusb_dev_bos_rm(struct wusb_dev *wusb_dev) +{ +	kfree(wusb_dev->bos); +	wusb_dev->wusb_cap_descr = NULL; +}; + +static struct usb_wireless_cap_descriptor wusb_cap_descr_default = { +	.bLength = sizeof(wusb_cap_descr_default), +	.bDescriptorType = USB_DT_DEVICE_CAPABILITY, +	.bDevCapabilityType = USB_CAP_TYPE_WIRELESS_USB, + +	.bmAttributes = USB_WIRELESS_BEACON_NONE, +	.wPHYRates = cpu_to_le16(USB_WIRELESS_PHY_53), +	.bmTFITXPowerInfo = 0, +	.bmFFITXPowerInfo = 0, +	.bmBandGroup = cpu_to_le16(0x0001),	/* WUSB1.0[7.4.1] bottom */ +	.bReserved = 0 +}; + +/* + * USB stack's device addition Notifier Callback + * + * Called from drivers/usb/core/hub.c when a new device is added; we + * use this hook to perform certain WUSB specific setup work on the + * new device. As well, it is the first time we can connect the + * wusb_dev and the usb_dev. So we note it down in wusb_dev and take a + * reference that we'll drop. + * + * First we need to determine if the device is a WUSB device (else we + * ignore it). For that we use the speed setting (USB_SPEED_VARIABLE) + * [FIXME: maybe we'd need something more definitive]. If so, we track + * it's usb_busd and from there, the WUSB HC. + * + * Because all WUSB HCs are contained in a 'struct wusbhc', voila, we + * get the wusbhc for the device. + * + * We have a reference on @usb_dev (as we are called at the end of its + * enumeration). + * + * NOTE: @usb_dev locked + */ +static void wusb_dev_add_ncb(struct usb_device *usb_dev) +{ +	int result = 0; +	struct wusb_dev *wusb_dev; +	struct wusbhc *wusbhc; +	struct device *dev = &usb_dev->dev; +	u8 port_idx; + +	if (usb_dev->wusb == 0 || usb_dev->devnum == 1) +		return;		/* skip non wusb and wusb RHs */ + +	d_fnstart(3, dev, "(usb_dev %p)\n", usb_dev); + +	wusbhc = wusbhc_get_by_usb_dev(usb_dev); +	if (wusbhc == NULL) +		goto error_nodev; +	mutex_lock(&wusbhc->mutex); +	wusb_dev = __wusb_dev_get_by_usb_dev(wusbhc, usb_dev); +	port_idx = wusb_port_no_to_idx(usb_dev->portnum); +	mutex_unlock(&wusbhc->mutex); +	if (wusb_dev == NULL) +		goto error_nodev; +	wusb_dev->usb_dev = usb_get_dev(usb_dev); +	usb_dev->wusb_dev = wusb_dev_get(wusb_dev); +	result = wusb_dev_sec_add(wusbhc, usb_dev, wusb_dev); +	if (result < 0) { +		dev_err(dev, "Cannot enable security: %d\n", result); +		goto error_sec_add; +	} +	/* Now query the device for it's BOS and attach it to wusb_dev */ +	result = wusb_dev_bos_add(usb_dev, wusb_dev); +	if (result < 0) { +		dev_err(dev, "Cannot get BOS descriptors: %d\n", result); +		goto error_bos_add; +	} +	result = wusb_dev_sysfs_add(wusbhc, usb_dev, wusb_dev); +	if (result < 0) +		goto error_add_sysfs; +out: +	wusb_dev_put(wusb_dev); +	wusbhc_put(wusbhc); +error_nodev: +	d_fnend(3, dev, "(usb_dev %p) = void\n", usb_dev); +	return; + +	wusb_dev_sysfs_rm(wusb_dev); +error_add_sysfs: +	wusb_dev_bos_rm(wusb_dev); +error_bos_add: +	wusb_dev_sec_rm(wusb_dev); +error_sec_add: +	mutex_lock(&wusbhc->mutex); +	__wusbhc_dev_disconnect(wusbhc, wusb_port_by_idx(wusbhc, port_idx)); +	mutex_unlock(&wusbhc->mutex); +	goto out; +} + +/* + * Undo all the steps done at connection by the notifier callback + * + * NOTE: @usb_dev locked + */ +static void wusb_dev_rm_ncb(struct usb_device *usb_dev) +{ +	struct wusb_dev *wusb_dev = usb_dev->wusb_dev; + +	if (usb_dev->wusb == 0 || usb_dev->devnum == 1) +		return;		/* skip non wusb and wusb RHs */ + +	wusb_dev_sysfs_rm(wusb_dev); +	wusb_dev_bos_rm(wusb_dev); +	wusb_dev_sec_rm(wusb_dev); +	wusb_dev->usb_dev = NULL; +	usb_dev->wusb_dev = NULL; +	wusb_dev_put(wusb_dev); +	usb_put_dev(usb_dev); +} + +/* + * Handle notifications from the USB stack (notifier call back) + * + * This is called when the USB stack does a + * usb_{bus,device}_{add,remove}() so we can do WUSB specific + * handling. It is called with [for the case of + * USB_DEVICE_{ADD,REMOVE} with the usb_dev locked. + */ +int wusb_usb_ncb(struct notifier_block *nb, unsigned long val, +		 void *priv) +{ +	int result = NOTIFY_OK; + +	switch (val) { +	case USB_DEVICE_ADD: +		wusb_dev_add_ncb(priv); +		break; +	case USB_DEVICE_REMOVE: +		wusb_dev_rm_ncb(priv); +		break; +	case USB_BUS_ADD: +		/* ignore (for now) */ +	case USB_BUS_REMOVE: +		break; +	default: +		WARN_ON(1); +		result = NOTIFY_BAD; +	}; +	return result; +} + +/* + * Return a referenced wusb_dev given a @wusbhc and @usb_dev + */ +struct wusb_dev *__wusb_dev_get_by_usb_dev(struct wusbhc *wusbhc, +					   struct usb_device *usb_dev) +{ +	struct wusb_dev *wusb_dev; +	u8 port_idx; + +	port_idx = wusb_port_no_to_idx(usb_dev->portnum); +	BUG_ON(port_idx > wusbhc->ports_max); +	wusb_dev = wusb_port_by_idx(wusbhc, port_idx)->wusb_dev; +	if (wusb_dev != NULL)		/* ops, device is gone */ +		wusb_dev_get(wusb_dev); +	return wusb_dev; +} +EXPORT_SYMBOL_GPL(__wusb_dev_get_by_usb_dev); + +void wusb_dev_destroy(struct kref *_wusb_dev) +{ +	struct wusb_dev *wusb_dev +		= container_of(_wusb_dev, struct wusb_dev, refcnt); +	list_del_init(&wusb_dev->cack_node); +	wusb_dev_free(wusb_dev); +	d_fnend(1, NULL, "%s (wusb_dev %p) = void\n", __func__, wusb_dev); +} +EXPORT_SYMBOL_GPL(wusb_dev_destroy); + +/* + * Create all the device connect handling infrastructure + * + * This is basically the device info array, Connect Acknowledgement + * (cack) lists, keep-alive timers (and delayed work thread). + */ +int wusbhc_devconnect_create(struct wusbhc *wusbhc) +{ +	d_fnstart(3, wusbhc->dev, "(wusbhc %p)\n", wusbhc); + +	wusbhc->keep_alive_ie.hdr.bIEIdentifier = WUIE_ID_KEEP_ALIVE; +	wusbhc->keep_alive_ie.hdr.bLength = sizeof(wusbhc->keep_alive_ie.hdr); +	INIT_DELAYED_WORK(&wusbhc->keep_alive_timer, wusbhc_keep_alive_run); + +	wusbhc->cack_ie.hdr.bIEIdentifier = WUIE_ID_CONNECTACK; +	wusbhc->cack_ie.hdr.bLength = sizeof(wusbhc->cack_ie.hdr); +	INIT_LIST_HEAD(&wusbhc->cack_list); + +	d_fnend(3, wusbhc->dev, "(wusbhc %p) = void\n", wusbhc); +	return 0; +} + +/* + * Release all resources taken by the devconnect stuff + */ +void wusbhc_devconnect_destroy(struct wusbhc *wusbhc) +{ +	d_fnstart(3, wusbhc->dev, "(wusbhc %p)\n", wusbhc); +	d_fnend(3, wusbhc->dev, "(wusbhc %p) = void\n", wusbhc); +} + +/* + * wusbhc_devconnect_start - start accepting device connections + * @wusbhc: the WUSB HC + * + * Sets the Host Info IE to accept all new connections. + * + * FIXME: This also enables the keep alives but this is not necessary + * until there are connected and authenticated devices. + */ +int wusbhc_devconnect_start(struct wusbhc *wusbhc, +			    const struct wusb_ckhdid *chid) +{ +	struct device *dev = wusbhc->dev; +	struct wuie_host_info *hi; +	int result; + +	hi = kzalloc(sizeof(*hi), GFP_KERNEL); +	if (hi == NULL) +		return -ENOMEM; + +	hi->hdr.bLength       = sizeof(*hi); +	hi->hdr.bIEIdentifier = WUIE_ID_HOST_INFO; +	hi->attributes        = cpu_to_le16((wusbhc->rsv->stream << 3) | WUIE_HI_CAP_ALL); +	hi->CHID              = *chid; +	result = wusbhc_mmcie_set(wusbhc, 0, 0, &hi->hdr); +	if (result < 0) { +		dev_err(dev, "Cannot add Host Info MMCIE: %d\n", result); +		goto error_mmcie_set; +	} +	wusbhc->wuie_host_info = hi; + +	queue_delayed_work(wusbd, &wusbhc->keep_alive_timer, +			   (wusbhc->trust_timeout*CONFIG_HZ)/1000/2); + +	return 0; + +error_mmcie_set: +	kfree(hi); +	return result; +} + +/* + * wusbhc_devconnect_stop - stop managing connected devices + * @wusbhc: the WUSB HC + * + * Removes the Host Info IE and stops the keep alives. + * + * FIXME: should this disconnect all devices? + */ +void wusbhc_devconnect_stop(struct wusbhc *wusbhc) +{ +	cancel_delayed_work_sync(&wusbhc->keep_alive_timer); +	WARN_ON(!list_empty(&wusbhc->cack_list)); + +	wusbhc_mmcie_rm(wusbhc, &wusbhc->wuie_host_info->hdr); +	kfree(wusbhc->wuie_host_info); +	wusbhc->wuie_host_info = NULL; +} + +/* + * wusb_set_dev_addr - set the WUSB device address used by the host + * @wusbhc: the WUSB HC the device is connect to + * @wusb_dev: the WUSB device + * @addr: new device address + */ +int wusb_set_dev_addr(struct wusbhc *wusbhc, struct wusb_dev *wusb_dev, u8 addr) +{ +	int result; + +	wusb_dev->addr = addr; +	result = wusbhc->dev_info_set(wusbhc, wusb_dev); +	if (result < 0) +		dev_err(wusbhc->dev, "device %d: failed to set device " +			"address\n", wusb_dev->port_idx); +	else +		dev_info(wusbhc->dev, "device %d: %s addr %u\n", +			 wusb_dev->port_idx, +			 (addr & WUSB_DEV_ADDR_UNAUTH) ? "unauth" : "auth", +			 wusb_dev->addr); + +	return result; +} diff --git a/drivers/usb/wusbcore/mmc.c b/drivers/usb/wusbcore/mmc.c new file mode 100644 index 00000000000..cfa77a01ceb --- /dev/null +++ b/drivers/usb/wusbcore/mmc.c @@ -0,0 +1,321 @@ +/* + * WUSB Wire Adapter: Control/Data Streaming Interface (WUSB[8]) + * MMC (Microscheduled Management Command) handling + * + * Copyright (C) 2005-2006 Intel Corporation + * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * + * WUIEs and MMC IEs...well, they are almost the same at the end. MMC + * IEs are Wireless USB IEs that go into the MMC period...[what is + * that? look in Design-overview.txt]. + * + * + * This is a simple subsystem to keep track of which IEs are being + * sent by the host in the MMC period. + * + * For each WUIE we ask to send, we keep it in an array, so we can + * request its removal later, or replace the content. They are tracked + * by pointer, so be sure to use the same pointer if you want to + * remove it or update the contents. + * + * FIXME: + *  - add timers that autoremove intervalled IEs? + */ +#include <linux/usb/wusb.h> +#include "wusbhc.h" + +/* Initialize the MMCIEs handling mechanism */ +int wusbhc_mmcie_create(struct wusbhc *wusbhc) +{ +	u8 mmcies = wusbhc->mmcies_max; +	wusbhc->mmcie = kcalloc(mmcies, sizeof(wusbhc->mmcie[0]), GFP_KERNEL); +	if (wusbhc->mmcie == NULL) +		return -ENOMEM; +	mutex_init(&wusbhc->mmcie_mutex); +	return 0; +} + +/* Release resources used by the MMCIEs handling mechanism */ +void wusbhc_mmcie_destroy(struct wusbhc *wusbhc) +{ +	kfree(wusbhc->mmcie); +} + +/* + * Add or replace an MMC Wireless USB IE. + * + * @interval:    See WUSB1.0[8.5.3.1] + * @repeat_cnt:  See WUSB1.0[8.5.3.1] + * @handle:      See WUSB1.0[8.5.3.1] + * @wuie:        Pointer to the header of the WUSB IE data to add. + *               MUST BE allocated in a kmalloc buffer (no stack or + *               vmalloc). + *               THE CALLER ALWAYS OWNS THE POINTER (we don't free it + *               on remove, we just forget about it). + * @returns:     0 if ok, < 0 errno code on error. + * + * Goes over the *whole* @wusbhc->mmcie array looking for (a) the + * first free spot and (b) if @wuie is already in the array (aka: + * transmitted in the MMCs) the spot were it is. + * + * If present, we "overwrite it" (update). + * + * + * NOTE: Need special ordering rules -- see below WUSB1.0 Table 7-38. + *       The host uses the handle as the 'sort' index. We + *       allocate the last one always for the WUIE_ID_HOST_INFO, and + *       the rest, first come first serve in inverse order. + * + *       Host software must make sure that it adds the other IEs in + *       the right order... the host hardware is responsible for + *       placing the WCTA IEs in the right place with the other IEs + *       set by host software. + * + * NOTE: we can access wusbhc->wa_descr without locking because it is + *       read only. + */ +int wusbhc_mmcie_set(struct wusbhc *wusbhc, u8 interval, u8 repeat_cnt, +		     struct wuie_hdr *wuie) +{ +	int result = -ENOBUFS; +	unsigned handle, itr; + +	/* Search a handle, taking into account the ordering */ +	mutex_lock(&wusbhc->mmcie_mutex); +	switch (wuie->bIEIdentifier) { +	case WUIE_ID_HOST_INFO: +		/* Always last */ +		handle = wusbhc->mmcies_max - 1; +		break; +	case WUIE_ID_ISOCH_DISCARD: +		dev_err(wusbhc->dev, "Special ordering case for WUIE ID 0x%x " +			"unimplemented\n", wuie->bIEIdentifier); +		result = -ENOSYS; +		goto error_unlock; +	default: +		/* search for it or find the last empty slot */ +		handle = ~0; +		for (itr = 0; itr < wusbhc->mmcies_max - 1; itr++) { +			if (wusbhc->mmcie[itr] == wuie) { +				handle = itr; +				break; +			} +			if (wusbhc->mmcie[itr] == NULL) +				handle = itr; +		} +		if (handle == ~0) +			goto error_unlock; +	} +	result = (wusbhc->mmcie_add)(wusbhc, interval, repeat_cnt, handle, +				     wuie); +	if (result >= 0) +		wusbhc->mmcie[handle] = wuie; +error_unlock: +	mutex_unlock(&wusbhc->mmcie_mutex); +	return result; +} +EXPORT_SYMBOL_GPL(wusbhc_mmcie_set); + +/* + * Remove an MMC IE previously added with wusbhc_mmcie_set() + * + * @wuie	Pointer used to add the WUIE + */ +void wusbhc_mmcie_rm(struct wusbhc *wusbhc, struct wuie_hdr *wuie) +{ +	int result; +	unsigned handle, itr; + +	mutex_lock(&wusbhc->mmcie_mutex); +	for (itr = 0; itr < wusbhc->mmcies_max; itr++) { +		if (wusbhc->mmcie[itr] == wuie) { +			handle = itr; +			goto found; +		} +	} +	mutex_unlock(&wusbhc->mmcie_mutex); +	return; + +found: +	result = (wusbhc->mmcie_rm)(wusbhc, handle); +	if (result == 0) +		wusbhc->mmcie[itr] = NULL; +	mutex_unlock(&wusbhc->mmcie_mutex); +} +EXPORT_SYMBOL_GPL(wusbhc_mmcie_rm); + +/* + * wusbhc_start - start transmitting MMCs and accepting connections + * @wusbhc: the HC to start + * @chid: the CHID to use for this host + * + * Establishes a cluster reservation, enables device connections, and + * starts MMCs with appropriate DNTS parameters. + */ +int wusbhc_start(struct wusbhc *wusbhc, const struct wusb_ckhdid *chid) +{ +	int result; +	struct device *dev = wusbhc->dev; + +	WARN_ON(wusbhc->wuie_host_info != NULL); + +	result = wusbhc_rsv_establish(wusbhc); +	if (result < 0) { +		dev_err(dev, "cannot establish cluster reservation: %d\n", +			result); +		goto error_rsv_establish; +	} + +	result = wusbhc_devconnect_start(wusbhc, chid); +	if (result < 0) { +		dev_err(dev, "error enabling device connections: %d\n", result); +		goto error_devconnect_start; +	} + +	result = wusbhc_sec_start(wusbhc); +	if (result < 0) { +		dev_err(dev, "error starting security in the HC: %d\n", result); +		goto error_sec_start; +	} +	/* FIXME: the choice of the DNTS parameters is somewhat +	 * arbitrary */ +	result = wusbhc->set_num_dnts(wusbhc, 0, 15); +	if (result < 0) { +		dev_err(dev, "Cannot set DNTS parameters: %d\n", result); +		goto error_set_num_dnts; +	} +	result = wusbhc->start(wusbhc); +	if (result < 0) { +		dev_err(dev, "error starting wusbch: %d\n", result); +		goto error_wusbhc_start; +	} +	wusbhc->active = 1; +	return 0; + +error_wusbhc_start: +	wusbhc_sec_stop(wusbhc); +error_set_num_dnts: +error_sec_start: +	wusbhc_devconnect_stop(wusbhc); +error_devconnect_start: +	wusbhc_rsv_terminate(wusbhc); +error_rsv_establish: +	return result; +} + +/* + * Disconnect all from the WUSB Channel + * + * Send a Host Disconnect IE in the MMC, wait, don't send it any more + */ +static int __wusbhc_host_disconnect_ie(struct wusbhc *wusbhc) +{ +	int result = -ENOMEM; +	struct wuie_host_disconnect *host_disconnect_ie; +	might_sleep(); +	host_disconnect_ie = kmalloc(sizeof(*host_disconnect_ie), GFP_KERNEL); +	if (host_disconnect_ie == NULL) +		goto error_alloc; +	host_disconnect_ie->hdr.bLength       = sizeof(*host_disconnect_ie); +	host_disconnect_ie->hdr.bIEIdentifier = WUIE_ID_HOST_DISCONNECT; +	result = wusbhc_mmcie_set(wusbhc, 0, 0, &host_disconnect_ie->hdr); +	if (result < 0) +		goto error_mmcie_set; + +	/* WUSB1.0[8.5.3.1 & 7.5.2] */ +	msleep(100); +	wusbhc_mmcie_rm(wusbhc, &host_disconnect_ie->hdr); +error_mmcie_set: +	kfree(host_disconnect_ie); +error_alloc: +	return result; +} + +/* + * wusbhc_stop - stop transmitting MMCs + * @wusbhc: the HC to stop + * + * Send a Host Disconnect IE, wait, remove all the MMCs (stop sending MMCs). + * + * If we can't allocate a Host Stop IE, screw it, we don't notify the + * devices we are disconnecting... + */ +void wusbhc_stop(struct wusbhc *wusbhc) +{ +	if (wusbhc->active) { +		wusbhc->active = 0; +		wusbhc->stop(wusbhc); +		wusbhc_sec_stop(wusbhc); +		__wusbhc_host_disconnect_ie(wusbhc); +		wusbhc_devconnect_stop(wusbhc); +		wusbhc_rsv_terminate(wusbhc); +	} +} +EXPORT_SYMBOL_GPL(wusbhc_stop); + +/* + * Change the CHID in a WUSB Channel + * + * If it is just a new CHID, send a Host Disconnect IE and then change + * the CHID IE. + */ +static int __wusbhc_chid_change(struct wusbhc *wusbhc, +				const struct wusb_ckhdid *chid) +{ +	int result = -ENOSYS; +	struct device *dev = wusbhc->dev; +	dev_err(dev, "%s() not implemented yet\n", __func__); +	return result; + +	BUG_ON(wusbhc->wuie_host_info == NULL); +	__wusbhc_host_disconnect_ie(wusbhc); +	wusbhc->wuie_host_info->CHID = *chid; +	result = wusbhc_mmcie_set(wusbhc, 0, 0, &wusbhc->wuie_host_info->hdr); +	if (result < 0) +		dev_err(dev, "Can't update Host Info WUSB IE: %d\n", result); +	return result; +} + +/* + * Set/reset/update a new CHID + * + * Depending on the previous state of the MMCs, start, stop or change + * the sent MMC. This effectively switches the host controller on and + * off (radio wise). + */ +int wusbhc_chid_set(struct wusbhc *wusbhc, const struct wusb_ckhdid *chid) +{ +	int result = 0; + +	if (memcmp(chid, &wusb_ckhdid_zero, sizeof(chid)) == 0) +		chid = NULL; + +	mutex_lock(&wusbhc->mutex); +	if (wusbhc->active) { +		if (chid) +			result = __wusbhc_chid_change(wusbhc, chid); +		else +			wusbhc_stop(wusbhc); +	} else { +		if (chid) +			wusbhc_start(wusbhc, chid); +	} +	mutex_unlock(&wusbhc->mutex); +	return result; +} +EXPORT_SYMBOL_GPL(wusbhc_chid_set); diff --git a/drivers/usb/wusbcore/pal.c b/drivers/usb/wusbcore/pal.c new file mode 100644 index 00000000000..7cc51e9905c --- /dev/null +++ b/drivers/usb/wusbcore/pal.c @@ -0,0 +1,42 @@ +/* + * Wireless USB Host Controller + * UWB Protocol Adaptation Layer (PAL) glue. + * + * Copyright (C) 2008 Cambridge Silicon Radio Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ +#include "wusbhc.h" + +/** + * wusbhc_pal_register - register the WUSB HC as a UWB PAL + * @wusbhc: the WUSB HC + */ +int wusbhc_pal_register(struct wusbhc *wusbhc) +{ +	uwb_pal_init(&wusbhc->pal); + +	wusbhc->pal.name   = "wusbhc"; +	wusbhc->pal.device = wusbhc->usb_hcd.self.controller; + +	return uwb_pal_register(wusbhc->uwb_rc, &wusbhc->pal); +} + +/** + * wusbhc_pal_register - unregister the WUSB HC as a UWB PAL + * @wusbhc: the WUSB HC + */ +void wusbhc_pal_unregister(struct wusbhc *wusbhc) +{ +	uwb_pal_unregister(wusbhc->uwb_rc, &wusbhc->pal); +} diff --git a/drivers/usb/wusbcore/reservation.c b/drivers/usb/wusbcore/reservation.c new file mode 100644 index 00000000000..fc63e77ded2 --- /dev/null +++ b/drivers/usb/wusbcore/reservation.c @@ -0,0 +1,115 @@ +/* + * WUSB cluster reservation management + * + * Copyright (C) 2007 Cambridge Silicon Radio Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ +#include <linux/kernel.h> +#include <linux/uwb.h> + +#include "wusbhc.h" + +/* + * WUSB cluster reservations are multicast reservations with the + * broadcast cluster ID (BCID) as the target DevAddr. + * + * FIXME: consider adjusting the reservation depending on what devices + * are attached. + */ + +static int wusbhc_bwa_set(struct wusbhc *wusbhc, u8 stream, +	const struct uwb_mas_bm *mas) +{ +	if (mas == NULL) +		mas = &uwb_mas_bm_zero; +	return wusbhc->bwa_set(wusbhc, stream, mas); +} + +/** + * wusbhc_rsv_complete_cb - WUSB HC reservation complete callback + * @rsv:    the reservation + * + * Either set or clear the HC's view of the reservation. + * + * FIXME: when a reservation is denied the HC should be stopped. + */ +static void wusbhc_rsv_complete_cb(struct uwb_rsv *rsv) +{ +	struct wusbhc *wusbhc = rsv->pal_priv; +	struct device *dev = wusbhc->dev; +	char buf[72]; + +	switch (rsv->state) { +	case UWB_RSV_STATE_O_ESTABLISHED: +		bitmap_scnprintf(buf, sizeof(buf), rsv->mas.bm, UWB_NUM_MAS); +		dev_dbg(dev, "established reservation: %s\n", buf); +		wusbhc_bwa_set(wusbhc, rsv->stream, &rsv->mas); +		break; +	case UWB_RSV_STATE_NONE: +		dev_dbg(dev, "removed reservation\n"); +		wusbhc_bwa_set(wusbhc, 0, NULL); +		wusbhc->rsv = NULL; +		break; +	default: +		dev_dbg(dev, "unexpected reservation state: %d\n", rsv->state); +		break; +	} +} + + +/** + * wusbhc_rsv_establish - establish a reservation for the cluster + * @wusbhc: the WUSB HC requesting a bandwith reservation + */ +int wusbhc_rsv_establish(struct wusbhc *wusbhc) +{ +	struct uwb_rc *rc = wusbhc->uwb_rc; +	struct uwb_rsv *rsv; +	struct uwb_dev_addr bcid; +	int ret; + +	rsv = uwb_rsv_create(rc, wusbhc_rsv_complete_cb, wusbhc); +	if (rsv == NULL) +		return -ENOMEM; + +	bcid.data[0] = wusbhc->cluster_id; +	bcid.data[1] = 0; + +	rsv->owner = &rc->uwb_dev; +	rsv->target.type = UWB_RSV_TARGET_DEVADDR; +	rsv->target.devaddr = bcid; +	rsv->type = UWB_DRP_TYPE_PRIVATE; +	rsv->max_mas = 256; +	rsv->min_mas = 16;  /* one MAS per zone? */ +	rsv->sparsity = 16; /* at least one MAS in each zone? */ +	rsv->is_multicast = true; + +	ret = uwb_rsv_establish(rsv); +	if (ret == 0) +		wusbhc->rsv = rsv; +	else +		uwb_rsv_destroy(rsv); +	return ret; +} + + +/** + * wusbhc_rsv_terminate - terminate any cluster reservation + * @wusbhc: the WUSB host whose reservation is to be terminated + */ +void wusbhc_rsv_terminate(struct wusbhc *wusbhc) +{ +	if (wusbhc->rsv) +		uwb_rsv_terminate(wusbhc->rsv); +} diff --git a/drivers/usb/wusbcore/rh.c b/drivers/usb/wusbcore/rh.c new file mode 100644 index 00000000000..267a6432510 --- /dev/null +++ b/drivers/usb/wusbcore/rh.c @@ -0,0 +1,477 @@ +/* + * Wireless USB Host Controller + * Root Hub operations + * + * + * Copyright (C) 2005-2006 Intel Corporation + * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * + * We fake a root hub that has fake ports (as many as simultaneous + * devices the Wireless USB Host Controller can deal with). For each + * port we keep an state in @wusbhc->port[index] identical to the one + * specified in the USB2.0[ch11] spec and some extra device + * information that complements the one in 'struct usb_device' (as + * this lacs a hcpriv pointer). + * + * Note this is common to WHCI and HWA host controllers. + * + * Through here we enable most of the state changes that the USB stack + * will use to connect or disconnect devices. We need to do some + * forced adaptation of Wireless USB device states vs. wired: + * + *        USB:                 WUSB: + * + * Port   Powered-off          port slot n/a + *        Powered-on           port slot available + *        Disconnected         port slot available + *        Connected            port slot assigned device + *        		       device sent DN_Connect + *                             device was authenticated + *        Enabled              device is authenticated, transitioned + *                             from unauth -> auth -> default address + *                             -> enabled + *        Reset                disconnect + *        Disable              disconnect + * + * This maps the standard USB port states with the WUSB device states + * so we can fake ports without having to modify the USB stack. + * + * FIXME: this process will change in the future + * + * + * ENTRY POINTS + * + * Our entry points into here are, as in hcd.c, the USB stack root hub + * ops defined in the usb_hcd struct: + * + * wusbhc_rh_status_data()	Provide hub and port status data bitmap + * + * wusbhc_rh_control()          Execution of all the major requests + *                              you can do to a hub (Set|Clear + *                              features, get descriptors, status, etc). + * + * wusbhc_rh_[suspend|resume]() That + * + * wusbhc_rh_start_port_reset() ??? unimplemented + */ +#include "wusbhc.h" + +#define D_LOCAL 0 +#include <linux/uwb/debug.h> + +/* + * Reset a fake port + * + * This can be called to reset a port from any other state or to reset + * it when connecting. In Wireless USB they are different; when doing + * a new connect that involves going over the authentication. When + * just reseting, its a different story. + * + * The Linux USB stack resets a port twice before it considers it + * enabled, so we have to detect and ignore that. + * + * @wusbhc is assumed referenced and @wusbhc->mutex unlocked. + * + * Supposedly we are the only thread accesing @wusbhc->port; in any + * case, maybe we should move the mutex locking from + * wusbhc_devconnect_auth() to here. + * + * @port_idx refers to the wusbhc's port index, not the USB port number + */ +static int wusbhc_rh_port_reset(struct wusbhc *wusbhc, u8 port_idx) +{ +	int result = 0; +	struct wusb_port *port = wusb_port_by_idx(wusbhc, port_idx); + +	d_fnstart(3, wusbhc->dev, "(wusbhc %p port_idx %u)\n", +		  wusbhc, port_idx); +	if (port->reset_count == 0) { +		wusbhc_devconnect_auth(wusbhc, port_idx); +		port->reset_count++; +	} else if (port->reset_count == 1) +		/* see header */ +		d_printf(2, wusbhc->dev, "Ignoring second reset on port_idx " +			"%u\n", port_idx); +	else +		result = wusbhc_dev_reset(wusbhc, port_idx); +	d_fnend(3, wusbhc->dev, "(wusbhc %p port_idx %u) = %d\n", +		wusbhc, port_idx, result); +	return result; +} + +/* + * Return the hub change status bitmap + * + * The bits in the change status bitmap are cleared when a + * ClearPortFeature request is issued (USB2.0[11.12.3,11.12.4]. + * + * @wusbhc is assumed referenced and @wusbhc->mutex unlocked. + * + * WARNING!! This gets called from atomic context; we cannot get the + *           mutex--the only race condition we can find is some bit + *           changing just after we copy it, which shouldn't be too + *           big of a problem [and we can't make it an spinlock + *           because other parts need to take it and sleep] . + * + *           @usb_hcd is refcounted, so it won't dissapear under us + *           and before killing a host, the polling of the root hub + *           would be stopped anyway. + */ +int wusbhc_rh_status_data(struct usb_hcd *usb_hcd, char *_buf) +{ +	struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd); +	size_t cnt, size; +	unsigned long *buf = (unsigned long *) _buf; + +	d_fnstart(1, wusbhc->dev, "(wusbhc %p)\n", wusbhc); +	/* WE DON'T LOCK, see comment */ +	size = wusbhc->ports_max + 1 /* hub bit */; +	size = (size + 8 - 1) / 8;	/* round to bytes */ +	for (cnt = 0; cnt < wusbhc->ports_max; cnt++) +		if (wusb_port_by_idx(wusbhc, cnt)->change) +			set_bit(cnt + 1, buf); +		else +			clear_bit(cnt + 1, buf); +	d_fnend(1, wusbhc->dev, "(wusbhc %p) %u, buffer:\n", wusbhc, (int)size); +	d_dump(1, wusbhc->dev, _buf, size); +	return size; +} +EXPORT_SYMBOL_GPL(wusbhc_rh_status_data); + +/* + * Return the hub's desciptor + * + * NOTE: almost cut and paste from ehci-hub.c + * + * @wusbhc is assumed referenced and @wusbhc->mutex unlocked + */ +static int wusbhc_rh_get_hub_descr(struct wusbhc *wusbhc, u16 wValue, +				   u16 wIndex, +				   struct usb_hub_descriptor *descr, +				   u16 wLength) +{ +	u16 temp = 1 + (wusbhc->ports_max / 8); +	u8 length = 7 + 2 * temp; + +	if (wLength < length) +		return -ENOSPC; +	descr->bDescLength = 7 + 2 * temp; +	descr->bDescriptorType = 0x29;	/* HUB type */ +	descr->bNbrPorts = wusbhc->ports_max; +	descr->wHubCharacteristics = cpu_to_le16( +		0x00			/* All ports power at once */ +		| 0x00			/* not part of compound device */ +		| 0x10			/* No overcurrent protection */ +		| 0x00			/* 8 FS think time FIXME ?? */ +		| 0x00);		/* No port indicators */ +	descr->bPwrOn2PwrGood = 0; +	descr->bHubContrCurrent = 0; +	/* two bitmaps:  ports removable, and usb 1.0 legacy PortPwrCtrlMask */ +	memset(&descr->bitmap[0], 0, temp); +	memset(&descr->bitmap[temp], 0xff, temp); +	return 0; +} + +/* + * Clear a hub feature + * + * @wusbhc is assumed referenced and @wusbhc->mutex unlocked. + * + * Nothing to do, so no locking needed ;) + */ +static int wusbhc_rh_clear_hub_feat(struct wusbhc *wusbhc, u16 feature) +{ +	int result; +	struct device *dev = wusbhc->dev; + +	d_fnstart(4, dev, "(%p, feature 0x%04u)\n", wusbhc, feature); +	switch (feature) { +	case C_HUB_LOCAL_POWER: +		/* FIXME: maybe plug bit 0 to the power input status, +		 * if any? +		 * see wusbhc_rh_get_hub_status() */ +	case C_HUB_OVER_CURRENT: +		result = 0; +		break; +	default: +		result = -EPIPE; +	} +	d_fnend(4, dev, "(%p, feature 0x%04u), %d\n", wusbhc, feature, result); +	return result; +} + +/* + * Return hub status (it is always zero...) + * + * @wusbhc is assumed referenced and @wusbhc->mutex unlocked. + * + * Nothing to do, so no locking needed ;) + */ +static int wusbhc_rh_get_hub_status(struct wusbhc *wusbhc, u32 *buf, +				    u16 wLength) +{ +	/* FIXME: maybe plug bit 0 to the power input status (if any)? */ +	*buf = 0; +	return 0; +} + +/* + * Set a port feature + * + * @wusbhc is assumed referenced and @wusbhc->mutex unlocked. + */ +static int wusbhc_rh_set_port_feat(struct wusbhc *wusbhc, u16 feature, +				   u8 selector, u8 port_idx) +{ +	int result = -EINVAL; +	struct device *dev = wusbhc->dev; + +	d_fnstart(4, dev, "(feat 0x%04u, selector 0x%u, port_idx %d)\n", +		  feature, selector, port_idx); + +	if (port_idx > wusbhc->ports_max) +		goto error; + +	switch (feature) { +		/* According to USB2.0[11.24.2.13]p2, these features +		 * are not required to be implemented. */ +	case USB_PORT_FEAT_C_OVER_CURRENT: +	case USB_PORT_FEAT_C_ENABLE: +	case USB_PORT_FEAT_C_SUSPEND: +	case USB_PORT_FEAT_C_CONNECTION: +	case USB_PORT_FEAT_C_RESET: +		result = 0; +		break; + +	case USB_PORT_FEAT_POWER: +		/* No such thing, but we fake it works */ +		mutex_lock(&wusbhc->mutex); +		wusb_port_by_idx(wusbhc, port_idx)->status |= USB_PORT_STAT_POWER; +		mutex_unlock(&wusbhc->mutex); +		result = 0; +		break; +	case USB_PORT_FEAT_RESET: +		result = wusbhc_rh_port_reset(wusbhc, port_idx); +		break; +	case USB_PORT_FEAT_ENABLE: +	case USB_PORT_FEAT_SUSPEND: +		dev_err(dev, "(port_idx %d) set feat %d/%d UNIMPLEMENTED\n", +			port_idx, feature, selector); +		result = -ENOSYS; +		break; +	default: +		dev_err(dev, "(port_idx %d) set feat %d/%d UNKNOWN\n", +			port_idx, feature, selector); +		result = -EPIPE; +		break; +	} +error: +	d_fnend(4, dev, "(feat 0x%04u, selector 0x%u, port_idx %d) = %d\n", +		feature, selector, port_idx, result); +	return result; +} + +/* + * Clear a port feature... + * + * @wusbhc is assumed referenced and @wusbhc->mutex unlocked. + */ +static int wusbhc_rh_clear_port_feat(struct wusbhc *wusbhc, u16 feature, +				     u8 selector, u8 port_idx) +{ +	int result = -EINVAL; +	struct device *dev = wusbhc->dev; + +	d_fnstart(4, dev, "(wusbhc %p feat 0x%04x selector %d port_idx %d)\n", +		  wusbhc, feature, selector, port_idx); + +	if (port_idx > wusbhc->ports_max) +		goto error; + +	mutex_lock(&wusbhc->mutex); +	result = 0; +	switch (feature) { +	case USB_PORT_FEAT_POWER:	/* fake port always on */ +		/* According to USB2.0[11.24.2.7.1.4], no need to implement? */ +	case USB_PORT_FEAT_C_OVER_CURRENT: +		break; +	case USB_PORT_FEAT_C_RESET: +		wusb_port_by_idx(wusbhc, port_idx)->change &= ~USB_PORT_STAT_C_RESET; +		break; +	case USB_PORT_FEAT_C_CONNECTION: +		wusb_port_by_idx(wusbhc, port_idx)->change &= ~USB_PORT_STAT_C_CONNECTION; +		break; +	case USB_PORT_FEAT_ENABLE: +		__wusbhc_dev_disable(wusbhc, port_idx); +		break; +	case USB_PORT_FEAT_C_ENABLE: +		wusb_port_by_idx(wusbhc, port_idx)->change &= ~USB_PORT_STAT_C_ENABLE; +		break; +	case USB_PORT_FEAT_SUSPEND: +	case USB_PORT_FEAT_C_SUSPEND: +	case 0xffff:		/* ??? FIXME */ +		dev_err(dev, "(port_idx %d) Clear feat %d/%d UNIMPLEMENTED\n", +			port_idx, feature, selector); +		/* dump_stack(); */ +		result = -ENOSYS; +		break; +	default: +		dev_err(dev, "(port_idx %d) Clear feat %d/%d UNKNOWN\n", +			port_idx, feature, selector); +		result = -EPIPE; +		break; +	} +	mutex_unlock(&wusbhc->mutex); +error: +	d_fnend(4, dev, "(wusbhc %p feat 0x%04x selector %d port_idx %d) = " +		"%d\n", wusbhc, feature, selector, port_idx, result); +	return result; +} + +/* + * Return the port's status + * + * @wusbhc is assumed referenced and @wusbhc->mutex unlocked. + */ +static int wusbhc_rh_get_port_status(struct wusbhc *wusbhc, u16 port_idx, +				     u32 *_buf, u16 wLength) +{ +	int result = -EINVAL; +	u16 *buf = (u16 *) _buf; + +	d_fnstart(1, wusbhc->dev, "(wusbhc %p port_idx %u wLength %u)\n", +		  wusbhc, port_idx, wLength); +	if (port_idx > wusbhc->ports_max) +		goto error; +	mutex_lock(&wusbhc->mutex); +	buf[0] = cpu_to_le16(wusb_port_by_idx(wusbhc, port_idx)->status); +	buf[1] = cpu_to_le16(wusb_port_by_idx(wusbhc, port_idx)->change); +	result = 0; +	mutex_unlock(&wusbhc->mutex); +error: +	d_fnend(1, wusbhc->dev, "(wusbhc %p) = %d, buffer:\n", wusbhc, result); +	d_dump(1, wusbhc->dev, _buf, wLength); +	return result; +} + +/* + * Entry point for Root Hub operations + * + * @wusbhc is assumed referenced and @wusbhc->mutex unlocked. + */ +int wusbhc_rh_control(struct usb_hcd *usb_hcd, u16 reqntype, u16 wValue, +		      u16 wIndex, char *buf, u16 wLength) +{ +	int result = -ENOSYS; +	struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd); + +	switch (reqntype) { +	case GetHubDescriptor: +		result = wusbhc_rh_get_hub_descr( +			wusbhc, wValue, wIndex, +			(struct usb_hub_descriptor *) buf, wLength); +		break; +	case ClearHubFeature: +		result = wusbhc_rh_clear_hub_feat(wusbhc, wValue); +		break; +	case GetHubStatus: +		result = wusbhc_rh_get_hub_status(wusbhc, (u32 *)buf, wLength); +		break; + +	case SetPortFeature: +		result = wusbhc_rh_set_port_feat(wusbhc, wValue, wIndex >> 8, +						 (wIndex & 0xff) - 1); +		break; +	case ClearPortFeature: +		result = wusbhc_rh_clear_port_feat(wusbhc, wValue, wIndex >> 8, +						   (wIndex & 0xff) - 1); +		break; +	case GetPortStatus: +		result = wusbhc_rh_get_port_status(wusbhc, wIndex - 1, +						   (u32 *)buf, wLength); +		break; + +	case SetHubFeature: +	default: +		dev_err(wusbhc->dev, "%s (%p [%p], %x, %x, %x, %p, %x) " +			"UNIMPLEMENTED\n", __func__, usb_hcd, wusbhc, reqntype, +			wValue, wIndex, buf, wLength); +		/* dump_stack(); */ +		result = -ENOSYS; +	} +	return result; +} +EXPORT_SYMBOL_GPL(wusbhc_rh_control); + +int wusbhc_rh_suspend(struct usb_hcd *usb_hcd) +{ +	struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd); +	dev_err(wusbhc->dev, "%s (%p [%p]) UNIMPLEMENTED\n", __func__, +		usb_hcd, wusbhc); +	/* dump_stack(); */ +	return -ENOSYS; +} +EXPORT_SYMBOL_GPL(wusbhc_rh_suspend); + +int wusbhc_rh_resume(struct usb_hcd *usb_hcd) +{ +	struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd); +	dev_err(wusbhc->dev, "%s (%p [%p]) UNIMPLEMENTED\n", __func__, +		usb_hcd, wusbhc); +	/* dump_stack(); */ +	return -ENOSYS; +} +EXPORT_SYMBOL_GPL(wusbhc_rh_resume); + +int wusbhc_rh_start_port_reset(struct usb_hcd *usb_hcd, unsigned port_idx) +{ +	struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd); +	dev_err(wusbhc->dev, "%s (%p [%p], port_idx %u) UNIMPLEMENTED\n", +		__func__, usb_hcd, wusbhc, port_idx); +	WARN_ON(1); +	return -ENOSYS; +} +EXPORT_SYMBOL_GPL(wusbhc_rh_start_port_reset); + +static void wusb_port_init(struct wusb_port *port) +{ +	port->status |= USB_PORT_STAT_HIGH_SPEED; +} + +/* + * Alloc fake port specific fields and status. + */ +int wusbhc_rh_create(struct wusbhc *wusbhc) +{ +	int result = -ENOMEM; +	size_t port_size, itr; +	port_size = wusbhc->ports_max * sizeof(wusbhc->port[0]); +	wusbhc->port = kzalloc(port_size, GFP_KERNEL); +	if (wusbhc->port == NULL) +		goto error_port_alloc; +	for (itr = 0; itr < wusbhc->ports_max; itr++) +		wusb_port_init(&wusbhc->port[itr]); +	result = 0; +error_port_alloc: +	return result; +} + +void wusbhc_rh_destroy(struct wusbhc *wusbhc) +{ +	kfree(wusbhc->port); +} diff --git a/drivers/usb/wusbcore/security.c b/drivers/usb/wusbcore/security.c new file mode 100644 index 00000000000..a101cad6a8d --- /dev/null +++ b/drivers/usb/wusbcore/security.c @@ -0,0 +1,642 @@ +/* + * Wireless USB Host Controller + * Security support: encryption enablement, etc + * + * Copyright (C) 2006 Intel Corporation + * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * + * FIXME: docs + */ +#include <linux/types.h> +#include <linux/usb/ch9.h> +#include <linux/random.h> +#include "wusbhc.h" + +/* + * DEBUG & SECURITY WARNING!!!! + * + * If you enable this past 1, the debug code will weaken the + * cryptographic safety of the system (on purpose, for debugging). + * + * Weaken means: + *   we print secret keys and intermediate values all the way, + */ +#undef D_LOCAL +#define D_LOCAL 2 +#include <linux/uwb/debug.h> + +static void wusbhc_set_gtk_callback(struct urb *urb); +static void wusbhc_gtk_rekey_done_work(struct work_struct *work); + +int wusbhc_sec_create(struct wusbhc *wusbhc) +{ +	wusbhc->gtk.descr.bLength = sizeof(wusbhc->gtk.descr) + sizeof(wusbhc->gtk.data); +	wusbhc->gtk.descr.bDescriptorType = USB_DT_KEY; +	wusbhc->gtk.descr.bReserved = 0; + +	wusbhc->gtk_index = wusb_key_index(0, WUSB_KEY_INDEX_TYPE_GTK, +					   WUSB_KEY_INDEX_ORIGINATOR_HOST); + +	INIT_WORK(&wusbhc->gtk_rekey_done_work, wusbhc_gtk_rekey_done_work); + +	return 0; +} + + +/* Called when the HC is destroyed */ +void wusbhc_sec_destroy(struct wusbhc *wusbhc) +{ +} + + +/** + * wusbhc_next_tkid - generate a new, currently unused, TKID + * @wusbhc:   the WUSB host controller + * @wusb_dev: the device whose PTK the TKID is for + *            (or NULL for a TKID for a GTK) + * + * The generated TKID consist of two parts: the device's authenicated + * address (or 0 or a GTK); and an incrementing number.  This ensures + * that TKIDs cannot be shared between devices and by the time the + * incrementing number wraps around the older TKIDs will no longer be + * in use (a maximum of two keys may be active at any one time). + */ +static u32 wusbhc_next_tkid(struct wusbhc *wusbhc, struct wusb_dev *wusb_dev) +{ +	u32 *tkid; +	u32 addr; + +	if (wusb_dev == NULL) { +		tkid = &wusbhc->gtk_tkid; +		addr = 0; +	} else { +		tkid = &wusb_port_by_idx(wusbhc, wusb_dev->port_idx)->ptk_tkid; +		addr = wusb_dev->addr & 0x7f; +	} + +	*tkid = (addr << 8) | ((*tkid + 1) & 0xff); + +	return *tkid; +} + +static void wusbhc_generate_gtk(struct wusbhc *wusbhc) +{ +	const size_t key_size = sizeof(wusbhc->gtk.data); +	u32 tkid; + +	tkid = wusbhc_next_tkid(wusbhc, NULL); + +	wusbhc->gtk.descr.tTKID[0] = (tkid >>  0) & 0xff; +	wusbhc->gtk.descr.tTKID[1] = (tkid >>  8) & 0xff; +	wusbhc->gtk.descr.tTKID[2] = (tkid >> 16) & 0xff; + +	get_random_bytes(wusbhc->gtk.descr.bKeyData, key_size); +} + +/** + * wusbhc_sec_start - start the security management process + * @wusbhc: the WUSB host controller + * + * Generate and set an initial GTK on the host controller. + * + * Called when the HC is started. + */ +int wusbhc_sec_start(struct wusbhc *wusbhc) +{ +	const size_t key_size = sizeof(wusbhc->gtk.data); +	int result; + +	wusbhc_generate_gtk(wusbhc); + +	result = wusbhc->set_gtk(wusbhc, wusbhc->gtk_tkid, +				 &wusbhc->gtk.descr.bKeyData, key_size); +	if (result < 0) +		dev_err(wusbhc->dev, "cannot set GTK for the host: %d\n", +			result); + +	return result; +} + +/** + * wusbhc_sec_stop - stop the security management process + * @wusbhc: the WUSB host controller + * + * Wait for any pending GTK rekeys to stop. + */ +void wusbhc_sec_stop(struct wusbhc *wusbhc) +{ +	cancel_work_sync(&wusbhc->gtk_rekey_done_work); +} + + +/** @returns encryption type name */ +const char *wusb_et_name(u8 x) +{ +	switch (x) { +	case USB_ENC_TYPE_UNSECURE:	return "unsecure"; +	case USB_ENC_TYPE_WIRED:	return "wired"; +	case USB_ENC_TYPE_CCM_1:	return "CCM-1"; +	case USB_ENC_TYPE_RSA_1:	return "RSA-1"; +	default: 			return "unknown"; +	} +} +EXPORT_SYMBOL_GPL(wusb_et_name); + +/* + * Set the device encryption method + * + * We tell the device which encryption method to use; we do this when + * setting up the device's security. + */ +static int wusb_dev_set_encryption(struct usb_device *usb_dev, int value) +{ +	int result; +	struct device *dev = &usb_dev->dev; +	struct wusb_dev *wusb_dev = usb_dev->wusb_dev; + +	if (value) { +		value = wusb_dev->ccm1_etd.bEncryptionValue; +	} else { +		/* FIXME: should be wusb_dev->etd[UNSECURE].bEncryptionValue */ +		value = 0; +	} +	/* Set device's */ +	result = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0), +			USB_REQ_SET_ENCRYPTION, +			USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE, +			value, 0, NULL, 0, 1000 /* FIXME: arbitrary */); +	if (result < 0) +		dev_err(dev, "Can't set device's WUSB encryption to " +			"%s (value %d): %d\n", +			wusb_et_name(wusb_dev->ccm1_etd.bEncryptionType), +			wusb_dev->ccm1_etd.bEncryptionValue,  result); +	return result; +} + +/* + * Set the GTK to be used by a device. + * + * The device must be authenticated. + */ +static int wusb_dev_set_gtk(struct wusbhc *wusbhc, struct wusb_dev *wusb_dev) +{ +	struct usb_device *usb_dev = wusb_dev->usb_dev; + +	return usb_control_msg( +		usb_dev, usb_sndctrlpipe(usb_dev, 0), +		USB_REQ_SET_DESCRIPTOR, +		USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE, +		USB_DT_KEY << 8 | wusbhc->gtk_index, 0, +		&wusbhc->gtk.descr, wusbhc->gtk.descr.bLength, +		1000); +} + + +/* FIXME: prototype for adding security */ +int wusb_dev_sec_add(struct wusbhc *wusbhc, +		     struct usb_device *usb_dev, struct wusb_dev *wusb_dev) +{ +	int result, bytes, secd_size; +	struct device *dev = &usb_dev->dev; +	struct usb_security_descriptor secd; +	const struct usb_encryption_descriptor *etd, *ccm1_etd = NULL; +	void *secd_buf; +	const void *itr, *top; +	char buf[64]; + +	d_fnstart(3, dev, "(usb_dev %p, wusb_dev %p)\n", usb_dev, wusb_dev); +	result = usb_get_descriptor(usb_dev, USB_DT_SECURITY, +				    0, &secd, sizeof(secd)); +	if (result < sizeof(secd)) { +		dev_err(dev, "Can't read security descriptor or " +			"not enough data: %d\n", result); +		goto error_secd; +	} +	secd_size = le16_to_cpu(secd.wTotalLength); +	d_printf(5, dev, "got %d bytes of sec descriptor, total is %d\n", +		 result, secd_size); +	secd_buf = kmalloc(secd_size, GFP_KERNEL); +	if (secd_buf == NULL) { +		dev_err(dev, "Can't allocate space for security descriptors\n"); +		goto error_secd_alloc; +	} +	result = usb_get_descriptor(usb_dev, USB_DT_SECURITY, +				    0, secd_buf, secd_size); +	if (result < secd_size) { +		dev_err(dev, "Can't read security descriptor or " +			"not enough data: %d\n", result); +		goto error_secd_all; +	} +	d_printf(5, dev, "got %d bytes of sec descriptors\n", result); +	bytes = 0; +	itr = secd_buf + sizeof(secd); +	top = secd_buf + result; +	while (itr < top) { +		etd = itr; +		if (top - itr < sizeof(*etd)) { +			dev_err(dev, "BUG: bad device security descriptor; " +				"not enough data (%zu vs %zu bytes left)\n", +				top - itr, sizeof(*etd)); +			break; +		} +		if (etd->bLength < sizeof(*etd)) { +			dev_err(dev, "BUG: bad device encryption descriptor; " +				"descriptor is too short " +				"(%u vs %zu needed)\n", +				etd->bLength, sizeof(*etd)); +			break; +		} +		itr += etd->bLength; +		bytes += snprintf(buf + bytes, sizeof(buf) - bytes, +				  "%s (0x%02x/%02x) ", +				  wusb_et_name(etd->bEncryptionType), +				  etd->bEncryptionValue, etd->bAuthKeyIndex); +		if (etd->bEncryptionType == USB_ENC_TYPE_CCM_1) +			ccm1_etd = etd; +	} +	/* This code only supports CCM1 as of now. */ +	/* FIXME: user has to choose which sec mode to use? +	 * In theory we want CCM */ +	if (ccm1_etd == NULL) { +		dev_err(dev, "WUSB device doesn't support CCM1 encryption, " +			"can't use!\n"); +		result = -EINVAL; +		goto error_no_ccm1; +	} +	wusb_dev->ccm1_etd = *ccm1_etd; +	dev_info(dev, "supported encryption: %s; using %s (0x%02x/%02x)\n", +		 buf, wusb_et_name(ccm1_etd->bEncryptionType), +		 ccm1_etd->bEncryptionValue, ccm1_etd->bAuthKeyIndex); +	result = 0; +	kfree(secd_buf); +out: +	d_fnend(3, dev, "(usb_dev %p, wusb_dev %p) = %d\n", +		usb_dev, wusb_dev, result); +	return result; + + +error_no_ccm1: +error_secd_all: +	kfree(secd_buf); +error_secd_alloc: +error_secd: +	goto out; +} + +void wusb_dev_sec_rm(struct wusb_dev *wusb_dev) +{ +	/* Nothing so far */ +} + +static void hs_printk(unsigned level, struct device *dev, +		      struct usb_handshake *hs) +{ +	d_printf(level, dev, +		 "  bMessageNumber: %u\n" +		 "  bStatus:        %u\n" +		 "  tTKID:          %02x %02x %02x\n" +		 "  CDID:           %02x %02x %02x %02x %02x %02x %02x %02x\n" +		 "                  %02x %02x %02x %02x %02x %02x %02x %02x\n" +		 "  nonce:          %02x %02x %02x %02x %02x %02x %02x %02x\n" +		 "                  %02x %02x %02x %02x %02x %02x %02x %02x\n" +		 "  MIC:            %02x %02x %02x %02x %02x %02x %02x %02x\n", +		 hs->bMessageNumber, hs->bStatus, +		 hs->tTKID[2], hs->tTKID[1], hs->tTKID[0], +		 hs->CDID[0], hs->CDID[1], hs->CDID[2], hs->CDID[3], +		 hs->CDID[4], hs->CDID[5], hs->CDID[6], hs->CDID[7], +		 hs->CDID[8], hs->CDID[9], hs->CDID[10], hs->CDID[11], +		 hs->CDID[12], hs->CDID[13], hs->CDID[14], hs->CDID[15], +		 hs->nonce[0], hs->nonce[1], hs->nonce[2], hs->nonce[3], +		 hs->nonce[4], hs->nonce[5], hs->nonce[6], hs->nonce[7], +		 hs->nonce[8], hs->nonce[9], hs->nonce[10], hs->nonce[11], +		 hs->nonce[12], hs->nonce[13], hs->nonce[14], hs->nonce[15], +		 hs->MIC[0], hs->MIC[1], hs->MIC[2], hs->MIC[3], +		 hs->MIC[4], hs->MIC[5], hs->MIC[6], hs->MIC[7]); +} + +/** + * Update the address of an unauthenticated WUSB device + * + * Once we have successfully authenticated, we take it to addr0 state + * and then to a normal address. + * + * Before the device's address (as known by it) was usb_dev->devnum | + * 0x80 (unauthenticated address). With this we update it to usb_dev->devnum. + */ +static int wusb_dev_update_address(struct wusbhc *wusbhc, +				   struct wusb_dev *wusb_dev) +{ +	int result = -ENOMEM; +	struct usb_device *usb_dev = wusb_dev->usb_dev; +	struct device *dev = &usb_dev->dev; +	u8 new_address = wusb_dev->addr & 0x7F; + +	/* Set address 0 */ +	result = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0), +				 USB_REQ_SET_ADDRESS, 0, +				 0, 0, NULL, 0, 1000 /* FIXME: arbitrary */); +	if (result < 0) { +		dev_err(dev, "auth failed: can't set address 0: %d\n", +			result); +		goto error_addr0; +	} +	result = wusb_set_dev_addr(wusbhc, wusb_dev, 0); +	if (result < 0) +		goto error_addr0; +	usb_ep0_reinit(usb_dev); + +	/* Set new (authenticated) address. */ +	result = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0), +				 USB_REQ_SET_ADDRESS, 0, +				 new_address, 0, NULL, 0, +				 1000 /* FIXME: arbitrary */); +	if (result < 0) { +		dev_err(dev, "auth failed: can't set address %u: %d\n", +			new_address, result); +		goto error_addr; +	} +	result = wusb_set_dev_addr(wusbhc, wusb_dev, new_address); +	if (result < 0) +		goto error_addr; +	usb_ep0_reinit(usb_dev); +	usb_dev->authenticated = 1; +error_addr: +error_addr0: +	return result; +} + +/* + * + * + */ +/* FIXME: split and cleanup */ +int wusb_dev_4way_handshake(struct wusbhc *wusbhc, struct wusb_dev *wusb_dev, +			    struct wusb_ckhdid *ck) +{ +	int result = -ENOMEM; +	struct usb_device *usb_dev = wusb_dev->usb_dev; +	struct device *dev = &usb_dev->dev; +	u32 tkid; +	__le32 tkid_le; +	struct usb_handshake *hs; +	struct aes_ccm_nonce ccm_n; +	u8 mic[8]; +	struct wusb_keydvt_in keydvt_in; +	struct wusb_keydvt_out keydvt_out; + +	hs = kzalloc(3*sizeof(hs[0]), GFP_KERNEL); +	if (hs == NULL) { +		dev_err(dev, "can't allocate handshake data\n"); +		goto error_kzalloc; +	} + +	/* We need to turn encryption before beginning the 4way +	 * hshake (WUSB1.0[.3.2.2]) */ +	result = wusb_dev_set_encryption(usb_dev, 1); +	if (result < 0) +		goto error_dev_set_encryption; + +	tkid = wusbhc_next_tkid(wusbhc, wusb_dev); +	tkid_le = cpu_to_le32(tkid); + +	hs[0].bMessageNumber = 1; +	hs[0].bStatus = 0; +	memcpy(hs[0].tTKID, &tkid_le, sizeof(hs[0].tTKID)); +	hs[0].bReserved = 0; +	memcpy(hs[0].CDID, &wusb_dev->cdid, sizeof(hs[0].CDID)); +	get_random_bytes(&hs[0].nonce, sizeof(hs[0].nonce)); +	memset(hs[0].MIC, 0, sizeof(hs[0].MIC));	/* Per WUSB1.0[T7-22] */ + +	d_printf(1, dev, "I: sending hs1:\n"); +	hs_printk(2, dev, &hs[0]); + +	result = usb_control_msg( +		usb_dev, usb_sndctrlpipe(usb_dev, 0), +		USB_REQ_SET_HANDSHAKE, +		USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE, +		1, 0, &hs[0], sizeof(hs[0]), 1000 /* FIXME: arbitrary */); +	if (result < 0) { +		dev_err(dev, "Handshake1: request failed: %d\n", result); +		goto error_hs1; +	} + +	/* Handshake 2, from the device -- need to verify fields */ +	result = usb_control_msg( +		usb_dev, usb_rcvctrlpipe(usb_dev, 0), +		USB_REQ_GET_HANDSHAKE, +		USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE, +		2, 0, &hs[1], sizeof(hs[1]), 1000 /* FIXME: arbitrary */); +	if (result < 0) { +		dev_err(dev, "Handshake2: request failed: %d\n", result); +		goto error_hs2; +	} +	d_printf(1, dev, "got HS2:\n"); +	hs_printk(2, dev, &hs[1]); + +	result = -EINVAL; +	if (hs[1].bMessageNumber != 2) { +		dev_err(dev, "Handshake2 failed: bad message number %u\n", +			hs[1].bMessageNumber); +		goto error_hs2; +	} +	if (hs[1].bStatus != 0) { +		dev_err(dev, "Handshake2 failed: bad status %u\n", +			hs[1].bStatus); +		goto error_hs2; +	} +	if (memcmp(hs[0].tTKID, hs[1].tTKID, sizeof(hs[0].tTKID))) { +		dev_err(dev, "Handshake2 failed: TKID mismatch " +			"(#1 0x%02x%02x%02x vs #2 0x%02x%02x%02x)\n", +			hs[0].tTKID[0], hs[0].tTKID[1], hs[0].tTKID[2], +			hs[1].tTKID[0], hs[1].tTKID[1], hs[1].tTKID[2]); +		goto error_hs2; +	} +	if (memcmp(hs[0].CDID, hs[1].CDID, sizeof(hs[0].CDID))) { +		dev_err(dev, "Handshake2 failed: CDID mismatch\n"); +		goto error_hs2; +	} + +	/* Setup the CCM nonce */ +	memset(&ccm_n.sfn, 0, sizeof(ccm_n.sfn));	/* Per WUSB1.0[6.5.2] */ +	memcpy(ccm_n.tkid, &tkid_le, sizeof(ccm_n.tkid)); +	ccm_n.src_addr = wusbhc->uwb_rc->uwb_dev.dev_addr; +	ccm_n.dest_addr.data[0] = wusb_dev->addr; +	ccm_n.dest_addr.data[1] = 0; + +	/* Derive the KCK and PTK from CK, the CCM, H and D nonces */ +	memcpy(keydvt_in.hnonce, hs[0].nonce, sizeof(keydvt_in.hnonce)); +	memcpy(keydvt_in.dnonce, hs[1].nonce, sizeof(keydvt_in.dnonce)); +	result = wusb_key_derive(&keydvt_out, ck->data, &ccm_n, &keydvt_in); +	if (result < 0) { +		dev_err(dev, "Handshake2 failed: cannot derive keys: %d\n", +			result); +		goto error_hs2; +	} +	d_printf(2, dev, "KCK:\n"); +	d_dump(2, dev, keydvt_out.kck, sizeof(keydvt_out.kck)); +	d_printf(2, dev, "PTK:\n"); +	d_dump(2, dev, keydvt_out.ptk, sizeof(keydvt_out.ptk)); + +	/* Compute MIC and verify it */ +	result = wusb_oob_mic(mic, keydvt_out.kck, &ccm_n, &hs[1]); +	if (result < 0) { +		dev_err(dev, "Handshake2 failed: cannot compute MIC: %d\n", +			result); +		goto error_hs2; +	} + +	d_printf(2, dev, "MIC:\n"); +	d_dump(2, dev, mic, sizeof(mic)); +	if (memcmp(hs[1].MIC, mic, sizeof(hs[1].MIC))) { +		dev_err(dev, "Handshake2 failed: MIC mismatch\n"); +		goto error_hs2; +	} + +	/* Send Handshake3 */ +	hs[2].bMessageNumber = 3; +	hs[2].bStatus = 0; +	memcpy(hs[2].tTKID, &tkid_le, sizeof(hs[2].tTKID)); +	hs[2].bReserved = 0; +	memcpy(hs[2].CDID, &wusb_dev->cdid, sizeof(hs[2].CDID)); +	memcpy(hs[2].nonce, hs[0].nonce, sizeof(hs[2].nonce)); +	result = wusb_oob_mic(hs[2].MIC, keydvt_out.kck, &ccm_n, &hs[2]); +	if (result < 0) { +		dev_err(dev, "Handshake3 failed: cannot compute MIC: %d\n", +			result); +		goto error_hs2; +	} + +	d_printf(1, dev, "I: sending hs3:\n"); +	hs_printk(2, dev, &hs[2]); + +	result = usb_control_msg( +		usb_dev, usb_sndctrlpipe(usb_dev, 0), +		USB_REQ_SET_HANDSHAKE, +		USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE, +		3, 0, &hs[2], sizeof(hs[2]), 1000 /* FIXME: arbitrary */); +	if (result < 0) { +		dev_err(dev, "Handshake3: request failed: %d\n", result); +		goto error_hs3; +	} + +	d_printf(1, dev, "I: turning on encryption on host for device\n"); +	d_dump(2, dev, keydvt_out.ptk, sizeof(keydvt_out.ptk)); +	result = wusbhc->set_ptk(wusbhc, wusb_dev->port_idx, tkid, +				 keydvt_out.ptk, sizeof(keydvt_out.ptk)); +	if (result < 0) +		goto error_wusbhc_set_ptk; + +	d_printf(1, dev, "I: setting a GTK\n"); +	result = wusb_dev_set_gtk(wusbhc, wusb_dev); +	if (result < 0) { +		dev_err(dev, "Set GTK for device: request failed: %d\n", +			result); +		goto error_wusbhc_set_gtk; +	} + +	/* Update the device's address from unauth to auth */ +	if (usb_dev->authenticated == 0) { +		d_printf(1, dev, "I: updating addres to auth from non-auth\n"); +		result = wusb_dev_update_address(wusbhc, wusb_dev); +		if (result < 0) +			goto error_dev_update_address; +	} +	result = 0; +	d_printf(1, dev, "I: 4way handshke done, device authenticated\n"); + +error_dev_update_address: +error_wusbhc_set_gtk: +error_wusbhc_set_ptk: +error_hs3: +error_hs2: +error_hs1: +	memset(hs, 0, 3*sizeof(hs[0])); +	memset(&keydvt_out, 0, sizeof(keydvt_out)); +	memset(&keydvt_in, 0, sizeof(keydvt_in)); +	memset(&ccm_n, 0, sizeof(ccm_n)); +	memset(mic, 0, sizeof(mic)); +	if (result < 0) { +		/* error path */ +		wusb_dev_set_encryption(usb_dev, 0); +	} +error_dev_set_encryption: +	kfree(hs); +error_kzalloc: +	return result; +} + +/* + * Once all connected and authenticated devices have received the new + * GTK, switch the host to using it. + */ +static void wusbhc_gtk_rekey_done_work(struct work_struct *work) +{ +	struct wusbhc *wusbhc = container_of(work, struct wusbhc, gtk_rekey_done_work); +	size_t key_size = sizeof(wusbhc->gtk.data); + +	mutex_lock(&wusbhc->mutex); + +	if (--wusbhc->pending_set_gtks == 0) +		wusbhc->set_gtk(wusbhc, wusbhc->gtk_tkid, &wusbhc->gtk.descr.bKeyData, key_size); + +	mutex_unlock(&wusbhc->mutex); +} + +static void wusbhc_set_gtk_callback(struct urb *urb) +{ +	struct wusbhc *wusbhc = urb->context; + +	queue_work(wusbd, &wusbhc->gtk_rekey_done_work); +} + +/** + * wusbhc_gtk_rekey - generate and distribute a new GTK + * @wusbhc: the WUSB host controller + * + * Generate a new GTK and distribute it to all connected and + * authenticated devices.  When all devices have the new GTK, the host + * starts using it. + * + * This must be called after every device disconnect (see [WUSB] + * section 6.2.11.2). + */ +void wusbhc_gtk_rekey(struct wusbhc *wusbhc) +{ +	static const size_t key_size = sizeof(wusbhc->gtk.data); +	int p; + +	wusbhc_generate_gtk(wusbhc); + +	for (p = 0; p < wusbhc->ports_max; p++) { +		struct wusb_dev *wusb_dev; + +		wusb_dev = wusbhc->port[p].wusb_dev; +		if (!wusb_dev || !wusb_dev->usb_dev | !wusb_dev->usb_dev->authenticated) +			continue; + +		usb_fill_control_urb(wusb_dev->set_gtk_urb, wusb_dev->usb_dev, +				     usb_sndctrlpipe(wusb_dev->usb_dev, 0), +				     (void *)wusb_dev->set_gtk_req, +				     &wusbhc->gtk.descr, wusbhc->gtk.descr.bLength, +				     wusbhc_set_gtk_callback, wusbhc); +		if (usb_submit_urb(wusb_dev->set_gtk_urb, GFP_KERNEL) == 0) +			wusbhc->pending_set_gtks++; +	} +	if (wusbhc->pending_set_gtks == 0) +		wusbhc->set_gtk(wusbhc, wusbhc->gtk_tkid, &wusbhc->gtk.descr.bKeyData, key_size); +} diff --git a/drivers/usb/wusbcore/wa-hc.c b/drivers/usb/wusbcore/wa-hc.c new file mode 100644 index 00000000000..9d04722415b --- /dev/null +++ b/drivers/usb/wusbcore/wa-hc.c @@ -0,0 +1,95 @@ +/* + * Wire Adapter Host Controller Driver + * Common items to HWA and DWA based HCDs + * + * Copyright (C) 2005-2006 Intel Corporation + * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * + * FIXME: docs + */ +#include "wusbhc.h" +#include "wa-hc.h" + +/** + * Assumes + * + * wa->usb_dev and wa->usb_iface initialized and refcounted, + * wa->wa_descr initialized. + */ +int wa_create(struct wahc *wa, struct usb_interface *iface) +{ +	int result; +	struct device *dev = &iface->dev; + +	result = wa_rpipes_create(wa); +	if (result < 0) +		goto error_rpipes_create; +	/* Fill up Data Transfer EP pointers */ +	wa->dti_epd = &iface->cur_altsetting->endpoint[1].desc; +	wa->dto_epd = &iface->cur_altsetting->endpoint[2].desc; +	wa->xfer_result_size = le16_to_cpu(wa->dti_epd->wMaxPacketSize); +	wa->xfer_result = kmalloc(wa->xfer_result_size, GFP_KERNEL); +	if (wa->xfer_result == NULL) +		goto error_xfer_result_alloc; +	result = wa_nep_create(wa, iface); +	if (result < 0) { +		dev_err(dev, "WA-CDS: can't initialize notif endpoint: %d\n", +			result); +		goto error_nep_create; +	} +	return 0; + +error_nep_create: +	kfree(wa->xfer_result); +error_xfer_result_alloc: +	wa_rpipes_destroy(wa); +error_rpipes_create: +	return result; +} +EXPORT_SYMBOL_GPL(wa_create); + + +void __wa_destroy(struct wahc *wa) +{ +	if (wa->dti_urb) { +		usb_kill_urb(wa->dti_urb); +		usb_put_urb(wa->dti_urb); +		usb_kill_urb(wa->buf_in_urb); +		usb_put_urb(wa->buf_in_urb); +	} +	kfree(wa->xfer_result); +	wa_nep_destroy(wa); +	wa_rpipes_destroy(wa); +} +EXPORT_SYMBOL_GPL(__wa_destroy); + +/** + * wa_reset_all - reset the WA device + * @wa: the WA to be reset + * + * For HWAs the radio controller and all other PALs are also reset. + */ +void wa_reset_all(struct wahc *wa) +{ +	/* FIXME: assuming HWA. */ +	wusbhc_reset_all(wa->wusb); +} + +MODULE_AUTHOR("Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>"); +MODULE_DESCRIPTION("Wireless USB Wire Adapter core"); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/wusbcore/wa-hc.h b/drivers/usb/wusbcore/wa-hc.h new file mode 100644 index 00000000000..586d350cdb4 --- /dev/null +++ b/drivers/usb/wusbcore/wa-hc.h @@ -0,0 +1,417 @@ +/* + * HWA Host Controller Driver + * Wire Adapter Control/Data Streaming Iface (WUSB1.0[8]) + * + * Copyright (C) 2005-2006 Intel Corporation + * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * + * This driver implements a USB Host Controller (struct usb_hcd) for a + * Wireless USB Host Controller based on the Wireless USB 1.0 + * Host-Wire-Adapter specification (in layman terms, a USB-dongle that + * implements a Wireless USB host). + * + * Check out the Design-overview.txt file in the source documentation + * for other details on the implementation. + * + * Main blocks: + * + *  driver     glue with the driver API, workqueue daemon + * + *  lc         RC instance life cycle management (create, destroy...) + * + *  hcd        glue with the USB API Host Controller Interface API. + * + *  nep        Notification EndPoint managent: collect notifications + *             and queue them with the workqueue daemon. + * + *             Handle notifications as coming from the NEP. Sends them + *             off others to their respective modules (eg: connect, + *             disconnect and reset go to devconnect). + * + *  rpipe      Remote Pipe management; rpipe is what we use to write + *             to an endpoint on a WUSB device that is connected to a + *             HWA RC. + * + *  xfer       Transfer managment -- this is all the code that gets a + *             buffer and pushes it to a device (or viceversa). * + * + * Some day a lot of this code will be shared between this driver and + * the drivers for DWA (xfer, rpipe). + * + * All starts at driver.c:hwahc_probe(), when one of this guys is + * connected. hwahc_disconnect() stops it. + * + * During operation, the main driver is devices connecting or + * disconnecting. They cause the HWA RC to send notifications into + * nep.c:hwahc_nep_cb() that will dispatch them to + * notif.c:wa_notif_dispatch(). From there they will fan to cause + * device connects, disconnects, etc. + * + * Note much of the activity is difficult to follow. For example a + * device connect goes to devconnect, which will cause the "fake" root + * hub port to show a connect and stop there. Then khubd will notice + * and call into the rh.c:hwahc_rc_port_reset() code to authenticate + * the device (and this might require user intervention) and enable + * the port. + * + * We also have a timer workqueue going from devconnect.c that + * schedules in hwahc_devconnect_create(). + * + * The rest of the traffic is in the usual entry points of a USB HCD, + * which are hooked up in driver.c:hwahc_rc_driver, and defined in + * hcd.c. + */ + +#ifndef __HWAHC_INTERNAL_H__ +#define __HWAHC_INTERNAL_H__ + +#include <linux/completion.h> +#include <linux/usb.h> +#include <linux/mutex.h> +#include <linux/spinlock.h> +#include <linux/uwb.h> +#include <linux/usb/wusb.h> +#include <linux/usb/wusb-wa.h> + +struct wusbhc; +struct wahc; +extern void wa_urb_enqueue_run(struct work_struct *ws); + +/** + * RPipe instance + * + * @descr's fields are kept in LE, as we need to send it back and + * forth. + * + * @wa is referenced when set + * + * @segs_available is the number of requests segments that still can + *                 be submitted to the controller without overloading + *                 it. It is initialized to descr->wRequests when + *                 aiming. + * + * A rpipe supports a max of descr->wRequests at the same time; before + * submitting seg_lock has to be taken. If segs_avail > 0, then we can + * submit; if not, we have to queue them. + */ +struct wa_rpipe { +	struct kref refcnt; +	struct usb_rpipe_descriptor descr; +	struct usb_host_endpoint *ep; +	struct wahc *wa; +	spinlock_t seg_lock; +	struct list_head seg_list; +	atomic_t segs_available; +	u8 buffer[1];	/* For reads/writes on USB */ +}; + + +/** + * Instance of a HWA Host Controller + * + * Except where a more specific lock/mutex applies or atomic, all + * fields protected by @mutex. + * + * @wa_descr  Can be accessed without locking because it is in + *            the same area where the device descriptors were + *            read, so it is guaranteed to exist umodified while + *            the device exists. + * + *            Endianess has been converted to CPU's. + * + * @nep_* can be accessed without locking as its processing is + *        serialized; we submit a NEP URB and it comes to + *        hwahc_nep_cb(), which won't issue another URB until it is + *        done processing it. + * + * @xfer_list: + * + *   List of active transfers to verify existence from a xfer id + *   gotten from the xfer result message. Can't use urb->list because + *   it goes by endpoint, and we don't know the endpoint at the time + *   when we get the xfer result message. We can't really rely on the + *   pointer (will have to change for 64 bits) as the xfer id is 32 bits. + * + * @xfer_delayed_list:   List of transfers that need to be started + *                       (with a workqueue, because they were + *                       submitted from an atomic context). + * + * FIXME: this needs to be layered up: a wusbhc layer (for sharing + *        comonalities with WHCI), a wa layer (for sharing + *        comonalities with DWA-RC). + */ +struct wahc { +	struct usb_device *usb_dev; +	struct usb_interface *usb_iface; + +	/* HC to deliver notifications */ +	union { +		struct wusbhc *wusb; +		struct dwahc *dwa; +	}; + +	const struct usb_endpoint_descriptor *dto_epd, *dti_epd; +	const struct usb_wa_descriptor *wa_descr; + +	struct urb *nep_urb;		/* Notification EndPoint [lockless] */ +	struct edc nep_edc; +	void *nep_buffer; +	size_t nep_buffer_size; + +	atomic_t notifs_queued; + +	u16 rpipes; +	unsigned long *rpipe_bm;	/* rpipe usage bitmap */ +	spinlock_t rpipe_bm_lock;	/* protect rpipe_bm */ +	struct mutex rpipe_mutex;	/* assigning resources to endpoints */ + +	struct urb *dti_urb;		/* URB for reading xfer results */ +	struct urb *buf_in_urb;		/* URB for reading data in */ +	struct edc dti_edc;		/* DTI error density counter */ +	struct wa_xfer_result *xfer_result; /* real size = dti_ep maxpktsize */ +	size_t xfer_result_size; + +	s32 status;			/* For reading status */ + +	struct list_head xfer_list; +	struct list_head xfer_delayed_list; +	spinlock_t xfer_list_lock; +	struct work_struct xfer_work; +	atomic_t xfer_id_count; +}; + + +extern int wa_create(struct wahc *wa, struct usb_interface *iface); +extern void __wa_destroy(struct wahc *wa); +void wa_reset_all(struct wahc *wa); + + +/* Miscellaneous constants */ +enum { +	/** Max number of EPROTO errors we tolerate on the NEP in a +	 * period of time */ +	HWAHC_EPROTO_MAX = 16, +	/** Period of time for EPROTO errors (in jiffies) */ +	HWAHC_EPROTO_PERIOD = 4 * HZ, +}; + + +/* Notification endpoint handling */ +extern int wa_nep_create(struct wahc *, struct usb_interface *); +extern void wa_nep_destroy(struct wahc *); + +static inline int wa_nep_arm(struct wahc *wa, gfp_t gfp_mask) +{ +	struct urb *urb = wa->nep_urb; +	urb->transfer_buffer = wa->nep_buffer; +	urb->transfer_buffer_length = wa->nep_buffer_size; +	return usb_submit_urb(urb, gfp_mask); +} + +static inline void wa_nep_disarm(struct wahc *wa) +{ +	usb_kill_urb(wa->nep_urb); +} + + +/* RPipes */ +static inline void wa_rpipe_init(struct wahc *wa) +{ +	spin_lock_init(&wa->rpipe_bm_lock); +	mutex_init(&wa->rpipe_mutex); +} + +static inline void wa_init(struct wahc *wa) +{ +	edc_init(&wa->nep_edc); +	atomic_set(&wa->notifs_queued, 0); +	wa_rpipe_init(wa); +	edc_init(&wa->dti_edc); +	INIT_LIST_HEAD(&wa->xfer_list); +	INIT_LIST_HEAD(&wa->xfer_delayed_list); +	spin_lock_init(&wa->xfer_list_lock); +	INIT_WORK(&wa->xfer_work, wa_urb_enqueue_run); +	atomic_set(&wa->xfer_id_count, 1); +} + +/** + * Destroy a pipe (when refcount drops to zero) + * + * Assumes it has been moved to the "QUIESCING" state. + */ +struct wa_xfer; +extern void rpipe_destroy(struct kref *_rpipe); +static inline +void __rpipe_get(struct wa_rpipe *rpipe) +{ +	kref_get(&rpipe->refcnt); +} +extern int rpipe_get_by_ep(struct wahc *, struct usb_host_endpoint *, +			   struct urb *, gfp_t); +static inline void rpipe_put(struct wa_rpipe *rpipe) +{ +	kref_put(&rpipe->refcnt, rpipe_destroy); + +} +extern void rpipe_ep_disable(struct wahc *, struct usb_host_endpoint *); +extern int wa_rpipes_create(struct wahc *); +extern void wa_rpipes_destroy(struct wahc *); +static inline void rpipe_avail_dec(struct wa_rpipe *rpipe) +{ +	atomic_dec(&rpipe->segs_available); +} + +/** + * Returns true if the rpipe is ready to submit more segments. + */ +static inline int rpipe_avail_inc(struct wa_rpipe *rpipe) +{ +	return atomic_inc_return(&rpipe->segs_available) > 0 +		&& !list_empty(&rpipe->seg_list); +} + + +/* Transferring data */ +extern int wa_urb_enqueue(struct wahc *, struct usb_host_endpoint *, +			  struct urb *, gfp_t); +extern int wa_urb_dequeue(struct wahc *, struct urb *); +extern void wa_handle_notif_xfer(struct wahc *, struct wa_notif_hdr *); + + +/* Misc + * + * FIXME: Refcounting for the actual @hwahc object is not correct; I + *        mean, this should be refcounting on the HCD underneath, but + *        it is not. In any case, the semantics for HCD refcounting + *        are *weird*...on refcount reaching zero it just frees + *        it...no RC specific function is called...unless I miss + *        something. + * + * FIXME: has to go away in favour of an 'struct' hcd based sollution + */ +static inline struct wahc *wa_get(struct wahc *wa) +{ +	usb_get_intf(wa->usb_iface); +	return wa; +} + +static inline void wa_put(struct wahc *wa) +{ +	usb_put_intf(wa->usb_iface); +} + + +static inline int __wa_feature(struct wahc *wa, unsigned op, u16 feature) +{ +	return usb_control_msg(wa->usb_dev, usb_sndctrlpipe(wa->usb_dev, 0), +			op ? USB_REQ_SET_FEATURE : USB_REQ_CLEAR_FEATURE, +			USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE, +			feature, +			wa->usb_iface->cur_altsetting->desc.bInterfaceNumber, +			NULL, 0, 1000 /* FIXME: arbitrary */); +} + + +static inline int __wa_set_feature(struct wahc *wa, u16 feature) +{ +	return  __wa_feature(wa, 1, feature); +} + + +static inline int __wa_clear_feature(struct wahc *wa, u16 feature) +{ +	return __wa_feature(wa, 0, feature); +} + + +/** + * Return the status of a Wire Adapter + * + * @wa:		Wire Adapter instance + * @returns     < 0 errno code on error, or status bitmap as described + *              in WUSB1.0[8.3.1.6]. + * + * NOTE: need malloc, some arches don't take USB from the stack + */ +static inline +s32 __wa_get_status(struct wahc *wa) +{ +	s32 result; +	result = usb_control_msg( +		wa->usb_dev, usb_rcvctrlpipe(wa->usb_dev, 0), +		USB_REQ_GET_STATUS, +		USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE, +		0, wa->usb_iface->cur_altsetting->desc.bInterfaceNumber, +		&wa->status, sizeof(wa->status), +		1000 /* FIXME: arbitrary */); +	if (result >= 0) +		result = wa->status; +	return result; +} + + +/** + * Waits until the Wire Adapter's status matches @mask/@value + * + * @wa:		Wire Adapter instance. + * @returns     < 0 errno code on error, otherwise status. + * + * Loop until the WAs status matches the mask and value (status & mask + * == value). Timeout if it doesn't happen. + * + * FIXME: is there an official specification on how long status + *        changes can take? + */ +static inline s32 __wa_wait_status(struct wahc *wa, u32 mask, u32 value) +{ +	s32 result; +	unsigned loops = 10; +	do { +		msleep(50); +		result = __wa_get_status(wa); +		if ((result & mask) == value) +			break; +		if (loops-- == 0) { +			result = -ETIMEDOUT; +			break; +		} +	} while (result >= 0); +	return result; +} + + +/** Command @hwahc to stop, @returns 0 if ok, < 0 errno code on error */ +static inline int __wa_stop(struct wahc *wa) +{ +	int result; +	struct device *dev = &wa->usb_iface->dev; + +	result = __wa_clear_feature(wa, WA_ENABLE); +	if (result < 0 && result != -ENODEV) { +		dev_err(dev, "error commanding HC to stop: %d\n", result); +		goto out; +	} +	result = __wa_wait_status(wa, WA_ENABLE, 0); +	if (result < 0 && result != -ENODEV) +		dev_err(dev, "error waiting for HC to stop: %d\n", result); +out: +	return 0; +} + + +#endif /* #ifndef __HWAHC_INTERNAL_H__ */ diff --git a/drivers/usb/wusbcore/wa-nep.c b/drivers/usb/wusbcore/wa-nep.c new file mode 100644 index 00000000000..3f542990c73 --- /dev/null +++ b/drivers/usb/wusbcore/wa-nep.c @@ -0,0 +1,310 @@ +/* + * WUSB Wire Adapter: Control/Data Streaming Interface (WUSB[8]) + * Notification EndPoint support + * + * Copyright (C) 2006 Intel Corporation + * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * + * This part takes care of getting the notification from the hw + * only and dispatching through wusbwad into + * wa_notif_dispatch. Handling is done there. + * + * WA notifications are limited in size; most of them are three or + * four bytes long, and the longest is the HWA Device Notification, + * which would not exceed 38 bytes (DNs are limited in payload to 32 + * bytes plus 3 bytes header (WUSB1.0[7.6p2]), plus 3 bytes HWA + * header (WUSB1.0[8.5.4.2]). + * + * It is not clear if more than one Device Notification can be packed + * in a HWA Notification, I assume no because of the wording in + * WUSB1.0[8.5.4.2]. In any case, the bigger any notification could + * get is 256 bytes (as the bLength field is a byte). + * + * So what we do is we have this buffer and read into it; when a + * notification arrives we schedule work to a specific, single thread + * workqueue (so notifications are serialized) and copy the + * notification data. After scheduling the work, we rearm the read from + * the notification endpoint. + * + * Entry points here are: + * + * wa_nep_[create|destroy]()   To initialize/release this subsystem + * + * wa_nep_cb()                 Callback for the notification + *                                endpoint; when data is ready, this + *                                does the dispatching. + */ +#include <linux/workqueue.h> +#include <linux/ctype.h> +#include <linux/uwb/debug.h> +#include "wa-hc.h" +#include "wusbhc.h" + +/* Structure for queueing notifications to the workqueue */ +struct wa_notif_work { +	struct work_struct work; +	struct wahc *wa; +	size_t size; +	u8 data[]; +}; + +/* + * Process incoming notifications from the WA's Notification EndPoint + * [the wuswad daemon, basically] + * + * @_nw:	Pointer to a descriptor which has the pointer to the + * 		@wa, the size of the buffer and the work queue + * 		structure (so we can free all when done). + * @returns     0 if ok, < 0 errno code on error. + * + * All notifications follow the same format; they need to start with a + * 'struct wa_notif_hdr' header, so it is easy to parse through + * them. We just break the buffer in individual notifications (the + * standard doesn't say if it can be done or is forbidden, so we are + * cautious) and dispatch each. + * + * So the handling layers are is: + * + *   WA specific notification (from NEP) + *      Device Notification Received -> wa_handle_notif_dn() + *        WUSB Device notification generic handling + *      BPST Adjustment -> wa_handle_notif_bpst_adj() + *      ... -> ... + * + * @wa has to be referenced + */ +static void wa_notif_dispatch(struct work_struct *ws) +{ +	void *itr; +	u8 missing = 0; +	struct wa_notif_work *nw = container_of(ws, struct wa_notif_work, work); +	struct wahc *wa = nw->wa; +	struct wa_notif_hdr *notif_hdr; +	size_t size; + +	struct device *dev = &wa->usb_iface->dev; + +#if 0 +	/* FIXME: need to check for this??? */ +	if (usb_hcd->state == HC_STATE_QUIESCING)	/* Going down? */ +		goto out;				/* screw it */ +#endif +	atomic_dec(&wa->notifs_queued);		/* Throttling ctl */ +	dev = &wa->usb_iface->dev; +	size = nw->size; +	itr = nw->data; + +	while (size) { +		if (size < sizeof(*notif_hdr)) { +			missing = sizeof(*notif_hdr) - size; +			goto exhausted_buffer; +		} +		notif_hdr = itr; +		if (size < notif_hdr->bLength) +			goto exhausted_buffer; +		itr += notif_hdr->bLength; +		size -= notif_hdr->bLength; +		/* Dispatch the notification [don't use itr or size!] */ +		switch (notif_hdr->bNotifyType) { +		case HWA_NOTIF_DN: { +			struct hwa_notif_dn *hwa_dn; +			hwa_dn = container_of(notif_hdr, struct hwa_notif_dn, +					      hdr); +			wusbhc_handle_dn(wa->wusb, hwa_dn->bSourceDeviceAddr, +					 hwa_dn->dndata, +					 notif_hdr->bLength - sizeof(*hwa_dn)); +			break; +		} +		case WA_NOTIF_TRANSFER: +			wa_handle_notif_xfer(wa, notif_hdr); +			break; +		case DWA_NOTIF_RWAKE: +		case DWA_NOTIF_PORTSTATUS: +		case HWA_NOTIF_BPST_ADJ: +			/* FIXME: unimplemented WA NOTIFs */ +			/* fallthru */ +		default: +			if (printk_ratelimit()) { +				dev_err(dev, "HWA: unknown notification 0x%x, " +					"%zu bytes; discarding\n", +					notif_hdr->bNotifyType, +					(size_t)notif_hdr->bLength); +				dump_bytes(dev, notif_hdr, 16); +			} +			break; +		} +	} +out: +	wa_put(wa); +	kfree(nw); +	return; + +	/* THIS SHOULD NOT HAPPEN +	 * +	 * Buffer exahusted with partial data remaining; just warn and +	 * discard the data, as this should not happen. +	 */ +exhausted_buffer: +	if (!printk_ratelimit()) +		goto out; +	dev_warn(dev, "HWA: device sent short notification, " +		 "%d bytes missing; discarding %d bytes.\n", +		 missing, (int)size); +	dump_bytes(dev, itr, size); +	goto out; +} + +/* + * Deliver incoming WA notifications to the wusbwa workqueue + * + * @wa:	Pointer the Wire Adapter Controller Data Streaming + *              instance (part of an 'struct usb_hcd'). + * @size:       Size of the received buffer + * @returns     0 if ok, < 0 errno code on error. + * + * The input buffer is @wa->nep_buffer, with @size bytes + * (guaranteed to fit in the allocated space, + * @wa->nep_buffer_size). + */ +static int wa_nep_queue(struct wahc *wa, size_t size) +{ +	int result = 0; +	struct device *dev = &wa->usb_iface->dev; +	struct wa_notif_work *nw; + +	/* dev_fnstart(dev, "(wa %p, size %zu)\n", wa, size); */ +	BUG_ON(size > wa->nep_buffer_size); +	if (size == 0) +		goto out; +	if (atomic_read(&wa->notifs_queued) > 200) { +		if (printk_ratelimit()) +			dev_err(dev, "Too many notifications queued, " +				"throttling back\n"); +		goto out; +	} +	nw = kzalloc(sizeof(*nw) + size, GFP_ATOMIC); +	if (nw == NULL) { +		if (printk_ratelimit()) +			dev_err(dev, "No memory to queue notification\n"); +		goto out; +	} +	INIT_WORK(&nw->work, wa_notif_dispatch); +	nw->wa = wa_get(wa); +	nw->size = size; +	memcpy(nw->data, wa->nep_buffer, size); +	atomic_inc(&wa->notifs_queued);		/* Throttling ctl */ +	queue_work(wusbd, &nw->work); +out: +	/* dev_fnend(dev, "(wa %p, size %zu) = result\n", wa, size, result); */ +	return result; +} + +/* + * Callback for the notification event endpoint + * + * Check's that everything is fine and then passes the data to be + * queued to the workqueue. + */ +static void wa_nep_cb(struct urb *urb) +{ +	int result; +	struct wahc *wa = urb->context; +	struct device *dev = &wa->usb_iface->dev; + +	switch (result = urb->status) { +	case 0: +		result = wa_nep_queue(wa, urb->actual_length); +		if (result < 0) +			dev_err(dev, "NEP: unable to process notification(s): " +				"%d\n", result); +		break; +	case -ECONNRESET:	/* Not an error, but a controlled situation; */ +	case -ENOENT:		/* (we killed the URB)...so, no broadcast */ +	case -ESHUTDOWN: +		dev_dbg(dev, "NEP: going down %d\n", urb->status); +		goto out; +	default:	/* On general errors, we retry unless it gets ugly */ +		if (edc_inc(&wa->nep_edc, EDC_MAX_ERRORS, +			    EDC_ERROR_TIMEFRAME)) { +			dev_err(dev, "NEP: URB max acceptable errors " +				"exceeded, resetting device\n"); +			wa_reset_all(wa); +			goto out; +		} +		dev_err(dev, "NEP: URB error %d\n", urb->status); +	} +	result = wa_nep_arm(wa, GFP_ATOMIC); +	if (result < 0) { +		dev_err(dev, "NEP: cannot submit URB: %d\n", result); +		wa_reset_all(wa); +	} +out: +	return; +} + +/* + * Initialize @wa's notification and event's endpoint stuff + * + * This includes the allocating the read buffer, the context ID + * allocation bitmap, the URB and submitting the URB. + */ +int wa_nep_create(struct wahc *wa, struct usb_interface *iface) +{ +	int result; +	struct usb_endpoint_descriptor *epd; +	struct usb_device *usb_dev = interface_to_usbdev(iface); +	struct device *dev = &iface->dev; + +	edc_init(&wa->nep_edc); +	epd = &iface->cur_altsetting->endpoint[0].desc; +	wa->nep_buffer_size = 1024; +	wa->nep_buffer = kmalloc(wa->nep_buffer_size, GFP_KERNEL); +	if (wa->nep_buffer == NULL) { +		dev_err(dev, "Unable to allocate notification's read buffer\n"); +		goto error_nep_buffer; +	} +	wa->nep_urb = usb_alloc_urb(0, GFP_KERNEL); +	if (wa->nep_urb == NULL) { +		dev_err(dev, "Unable to allocate notification URB\n"); +		goto error_urb_alloc; +	} +	usb_fill_int_urb(wa->nep_urb, usb_dev, +			 usb_rcvintpipe(usb_dev, epd->bEndpointAddress), +			 wa->nep_buffer, wa->nep_buffer_size, +			 wa_nep_cb, wa, epd->bInterval); +	result = wa_nep_arm(wa, GFP_KERNEL); +	if (result < 0) { +		dev_err(dev, "Cannot submit notification URB: %d\n", result); +		goto error_nep_arm; +	} +	return 0; + +error_nep_arm: +	usb_free_urb(wa->nep_urb); +error_urb_alloc: +	kfree(wa->nep_buffer); +error_nep_buffer: +	return -ENOMEM; +} + +void wa_nep_destroy(struct wahc *wa) +{ +	wa_nep_disarm(wa); +	usb_free_urb(wa->nep_urb); +	kfree(wa->nep_buffer); +} diff --git a/drivers/usb/wusbcore/wa-rpipe.c b/drivers/usb/wusbcore/wa-rpipe.c new file mode 100644 index 00000000000..f18e4aae66e --- /dev/null +++ b/drivers/usb/wusbcore/wa-rpipe.c @@ -0,0 +1,562 @@ +/* + * WUSB Wire Adapter + * rpipe management + * + * Copyright (C) 2005-2006 Intel Corporation + * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * + * FIXME: docs + * + * RPIPE + * + *   Targetted at different downstream endpoints + * + *   Descriptor: use to config the remote pipe. + * + *   The number of blocks could be dynamic (wBlocks in descriptor is + *   0)--need to schedule them then. + * + * Each bit in wa->rpipe_bm represents if an rpipe is being used or + * not. Rpipes are represented with a 'struct wa_rpipe' that is + * attached to the hcpriv member of a 'struct usb_host_endpoint'. + * + * When you need to xfer data to an endpoint, you get an rpipe for it + * with wa_ep_rpipe_get(), which gives you a reference to the rpipe + * and keeps a single one (the first one) with the endpoint. When you + * are done transferring, you drop that reference. At the end the + * rpipe is always allocated and bound to the endpoint. There it might + * be recycled when not used. + * + * Addresses: + * + *  We use a 1:1 mapping mechanism between port address (0 based + *  index, actually) and the address. The USB stack knows about this. + * + *  USB Stack port number    4 (1 based) + *  WUSB code port index     3 (0 based) + *  USB Addresss             5 (2 based -- 0 is for default, 1 for root hub) + * + *  Now, because we don't use the concept as default address exactly + *  like the (wired) USB code does, we need to kind of skip it. So we + *  never take addresses from the urb->pipe, but from the + *  urb->dev->devnum, to make sure that we always have the right + *  destination address. + */ +#include <linux/init.h> +#include <asm/atomic.h> +#include <linux/bitmap.h> +#include "wusbhc.h" +#include "wa-hc.h" + +#define D_LOCAL 0 +#include <linux/uwb/debug.h> + + +static int __rpipe_get_descr(struct wahc *wa, +			     struct usb_rpipe_descriptor *descr, u16 index) +{ +	ssize_t result; +	struct device *dev = &wa->usb_iface->dev; + +	/* Get the RPIPE descriptor -- we cannot use the usb_get_descriptor() +	 * function because the arguments are different. +	 */ +	d_printf(1, dev, "rpipe %u: get descr\n", index); +	result = usb_control_msg( +		wa->usb_dev, usb_rcvctrlpipe(wa->usb_dev, 0), +		USB_REQ_GET_DESCRIPTOR, +		USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_RPIPE, +		USB_DT_RPIPE<<8, index, descr, sizeof(*descr), +		1000 /* FIXME: arbitrary */); +	if (result < 0) { +		dev_err(dev, "rpipe %u: get descriptor failed: %d\n", +			index, (int)result); +		goto error; +	} +	if (result < sizeof(*descr)) { +		dev_err(dev, "rpipe %u: got short descriptor " +			"(%zd vs %zd bytes needed)\n", +			index, result, sizeof(*descr)); +		result = -EINVAL; +		goto error; +	} +	result = 0; + +error: +	return result; +} + +/* + * + * The descriptor is assumed to be properly initialized (ie: you got + * it through __rpipe_get_descr()). + */ +static int __rpipe_set_descr(struct wahc *wa, +			     struct usb_rpipe_descriptor *descr, u16 index) +{ +	ssize_t result; +	struct device *dev = &wa->usb_iface->dev; + +	/* we cannot use the usb_get_descriptor() function because the +	 * arguments are different. +	 */ +	d_printf(1, dev, "rpipe %u: set descr\n", index); +	result = usb_control_msg( +		wa->usb_dev, usb_sndctrlpipe(wa->usb_dev, 0), +		USB_REQ_SET_DESCRIPTOR, +		USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_RPIPE, +		USB_DT_RPIPE<<8, index, descr, sizeof(*descr), +		HZ / 10); +	if (result < 0) { +		dev_err(dev, "rpipe %u: set descriptor failed: %d\n", +			index, (int)result); +		goto error; +	} +	if (result < sizeof(*descr)) { +		dev_err(dev, "rpipe %u: sent short descriptor " +			"(%zd vs %zd bytes required)\n", +			index, result, sizeof(*descr)); +		result = -EINVAL; +		goto error; +	} +	result = 0; + +error: +	return result; + +} + +static void rpipe_init(struct wa_rpipe *rpipe) +{ +	kref_init(&rpipe->refcnt); +	spin_lock_init(&rpipe->seg_lock); +	INIT_LIST_HEAD(&rpipe->seg_list); +} + +static unsigned rpipe_get_idx(struct wahc *wa, unsigned rpipe_idx) +{ +	unsigned long flags; + +	spin_lock_irqsave(&wa->rpipe_bm_lock, flags); +	rpipe_idx = find_next_zero_bit(wa->rpipe_bm, wa->rpipes, rpipe_idx); +	if (rpipe_idx < wa->rpipes) +		set_bit(rpipe_idx, wa->rpipe_bm); +	spin_unlock_irqrestore(&wa->rpipe_bm_lock, flags); + +	return rpipe_idx; +} + +static void rpipe_put_idx(struct wahc *wa, unsigned rpipe_idx) +{ +	unsigned long flags; + +	spin_lock_irqsave(&wa->rpipe_bm_lock, flags); +	clear_bit(rpipe_idx, wa->rpipe_bm); +	spin_unlock_irqrestore(&wa->rpipe_bm_lock, flags); +} + +void rpipe_destroy(struct kref *_rpipe) +{ +	struct wa_rpipe *rpipe = container_of(_rpipe, struct wa_rpipe, refcnt); +	u8 index = le16_to_cpu(rpipe->descr.wRPipeIndex); +	d_fnstart(1, NULL, "(rpipe %p %u)\n", rpipe, index); +	if (rpipe->ep) +		rpipe->ep->hcpriv = NULL; +	rpipe_put_idx(rpipe->wa, index); +	wa_put(rpipe->wa); +	kfree(rpipe); +	d_fnend(1, NULL, "(rpipe %p %u)\n", rpipe, index); +} +EXPORT_SYMBOL_GPL(rpipe_destroy); + +/* + * Locate an idle rpipe, create an structure for it and return it + * + * @wa 	  is referenced and unlocked + * @crs   enum rpipe_attr, required endpoint characteristics + * + * The rpipe can be used only sequentially (not in parallel). + * + * The rpipe is moved into the "ready" state. + */ +static int rpipe_get_idle(struct wa_rpipe **prpipe, struct wahc *wa, u8 crs, +			  gfp_t gfp) +{ +	int result; +	unsigned rpipe_idx; +	struct wa_rpipe *rpipe; +	struct device *dev = &wa->usb_iface->dev; + +	d_fnstart(3, dev, "(wa %p crs 0x%02x)\n", wa, crs); +	rpipe = kzalloc(sizeof(*rpipe), gfp); +	if (rpipe == NULL) +		return -ENOMEM; +	rpipe_init(rpipe); + +	/* Look for an idle pipe */ +	for (rpipe_idx = 0; rpipe_idx < wa->rpipes; rpipe_idx++) { +		rpipe_idx = rpipe_get_idx(wa, rpipe_idx); +		if (rpipe_idx >= wa->rpipes)	/* no more pipes :( */ +			break; +		result =  __rpipe_get_descr(wa, &rpipe->descr, rpipe_idx); +		if (result < 0) +			dev_err(dev, "Can't get descriptor for rpipe %u: %d\n", +				rpipe_idx, result); +		else if ((rpipe->descr.bmCharacteristics & crs) != 0) +			goto found; +		rpipe_put_idx(wa, rpipe_idx); +	} +	*prpipe = NULL; +	kfree(rpipe); +	d_fnend(3, dev, "(wa %p crs 0x%02x) = -ENXIO\n", wa, crs); +	return -ENXIO; + +found: +	set_bit(rpipe_idx, wa->rpipe_bm); +	rpipe->wa = wa_get(wa); +	*prpipe = rpipe; +	d_fnstart(3, dev, "(wa %p crs 0x%02x) = 0\n", wa, crs); +	return 0; +} + +static int __rpipe_reset(struct wahc *wa, unsigned index) +{ +	int result; +	struct device *dev = &wa->usb_iface->dev; + +	d_printf(1, dev, "rpipe %u: reset\n", index); +	result = usb_control_msg( +		wa->usb_dev, usb_sndctrlpipe(wa->usb_dev, 0), +		USB_REQ_RPIPE_RESET, +		USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_RPIPE, +		0, index, NULL, 0, 1000 /* FIXME: arbitrary */); +	if (result < 0) +		dev_err(dev, "rpipe %u: reset failed: %d\n", +			index, result); +	return result; +} + +/* + * Fake companion descriptor for ep0 + * + * See WUSB1.0[7.4.4], most of this is zero for bulk/int/ctl + */ +static struct usb_wireless_ep_comp_descriptor epc0 = { +	.bLength = sizeof(epc0), +	.bDescriptorType = USB_DT_WIRELESS_ENDPOINT_COMP, +/*	.bMaxBurst = 1, */ +	.bMaxSequence = 31, +}; + +/* + * Look for EP companion descriptor + * + * Get there, look for Inara in the endpoint's extra descriptors + */ +static struct usb_wireless_ep_comp_descriptor *rpipe_epc_find( +		struct device *dev, struct usb_host_endpoint *ep) +{ +	void *itr; +	size_t itr_size; +	struct usb_descriptor_header *hdr; +	struct usb_wireless_ep_comp_descriptor *epcd; + +	d_fnstart(3, dev, "(ep %p)\n", ep); +	if (ep->desc.bEndpointAddress == 0) { +		epcd = &epc0; +		goto out; +	} +	itr = ep->extra; +	itr_size = ep->extralen; +	epcd = NULL; +	while (itr_size > 0) { +		if (itr_size < sizeof(*hdr)) { +			dev_err(dev, "HW Bug? ep 0x%02x: extra descriptors " +				"at offset %zu: only %zu bytes left\n", +				ep->desc.bEndpointAddress, +				itr - (void *) ep->extra, itr_size); +			break; +		} +		hdr = itr; +		if (hdr->bDescriptorType == USB_DT_WIRELESS_ENDPOINT_COMP) { +			epcd = itr; +			break; +		} +		if (hdr->bLength > itr_size) { +			dev_err(dev, "HW Bug? ep 0x%02x: extra descriptor " +				"at offset %zu (type 0x%02x) " +				"length %d but only %zu bytes left\n", +				ep->desc.bEndpointAddress, +				itr - (void *) ep->extra, hdr->bDescriptorType, +				hdr->bLength, itr_size); +			break; +		} +		itr += hdr->bLength; +		itr_size -= hdr->bDescriptorType; +	} +out: +	d_fnend(3, dev, "(ep %p) = %p\n", ep, epcd); +	return epcd; +} + +/* + * Aim an rpipe to its device & endpoint destination + * + * Make sure we change the address to unauthenticathed if the device + * is WUSB and it is not authenticated. + */ +static int rpipe_aim(struct wa_rpipe *rpipe, struct wahc *wa, +		     struct usb_host_endpoint *ep, struct urb *urb, gfp_t gfp) +{ +	int result = -ENOMSG;	/* better code for lack of companion? */ +	struct device *dev = &wa->usb_iface->dev; +	struct usb_device *usb_dev = urb->dev; +	struct usb_wireless_ep_comp_descriptor *epcd; +	u8 unauth; + +	d_fnstart(3, dev, "(rpipe %p wa %p ep %p, urb %p)\n", +		    rpipe, wa, ep, urb); +	epcd = rpipe_epc_find(dev, ep); +	if (epcd == NULL) { +		dev_err(dev, "ep 0x%02x: can't find companion descriptor\n", +			ep->desc.bEndpointAddress); +		goto error; +	} +	unauth = usb_dev->wusb && !usb_dev->authenticated ? 0x80 : 0; +	__rpipe_reset(wa, le16_to_cpu(rpipe->descr.wRPipeIndex)); +	atomic_set(&rpipe->segs_available, le16_to_cpu(rpipe->descr.wRequests)); +	/* FIXME: block allocation system; request with queuing and timeout */ +	/* FIXME: compute so seg_size > ep->maxpktsize */ +	rpipe->descr.wBlocks = cpu_to_le16(16);		/* given */ +	/* ep0 maxpktsize is 0x200 (WUSB1.0[4.8.1]) */ +	rpipe->descr.wMaxPacketSize = cpu_to_le16(ep->desc.wMaxPacketSize); +	rpipe->descr.bHSHubAddress = 0;			/* reserved: zero */ +	rpipe->descr.bHSHubPort = wusb_port_no_to_idx(urb->dev->portnum); +	/* FIXME: use maximum speed as supported or recommended by device */ +	rpipe->descr.bSpeed = usb_pipeendpoint(urb->pipe) == 0 ? +		UWB_PHY_RATE_53 : UWB_PHY_RATE_200; +	d_printf(2, dev, "addr %u (0x%02x) rpipe #%u ep# %u speed %d\n", +		 urb->dev->devnum, urb->dev->devnum | unauth, +		 le16_to_cpu(rpipe->descr.wRPipeIndex), +		 usb_pipeendpoint(urb->pipe), rpipe->descr.bSpeed); +	/* see security.c:wusb_update_address() */ +	if (unlikely(urb->dev->devnum == 0x80)) +		rpipe->descr.bDeviceAddress = 0; +	else +		rpipe->descr.bDeviceAddress = urb->dev->devnum | unauth; +	rpipe->descr.bEndpointAddress = ep->desc.bEndpointAddress; +	/* FIXME: bDataSequence */ +	rpipe->descr.bDataSequence = 0; +	/* FIXME: dwCurrentWindow */ +	rpipe->descr.dwCurrentWindow = cpu_to_le32(1); +	/* FIXME: bMaxDataSequence */ +	rpipe->descr.bMaxDataSequence = epcd->bMaxSequence - 1; +	rpipe->descr.bInterval = ep->desc.bInterval; +	/* FIXME: bOverTheAirInterval */ +	rpipe->descr.bOverTheAirInterval = 0;	/* 0 if not isoc */ +	/* FIXME: xmit power & preamble blah blah */ +	rpipe->descr.bmAttribute = ep->desc.bmAttributes & 0x03; +	/* rpipe->descr.bmCharacteristics RO */ +	/* FIXME: bmRetryOptions */ +	rpipe->descr.bmRetryOptions = 15; +	/* FIXME: use for assessing link quality? */ +	rpipe->descr.wNumTransactionErrors = 0; +	result = __rpipe_set_descr(wa, &rpipe->descr, +				   le16_to_cpu(rpipe->descr.wRPipeIndex)); +	if (result < 0) { +		dev_err(dev, "Cannot aim rpipe: %d\n", result); +		goto error; +	} +	result = 0; +error: +	d_fnend(3, dev, "(rpipe %p wa %p ep %p urb %p) = %d\n", +		  rpipe, wa, ep, urb, result); +	return result; +} + +/* + * Check an aimed rpipe to make sure it points to where we want + * + * We use bit 19 of the Linux USB pipe bitmap for unauth vs auth + * space; when it is like that, we or 0x80 to make an unauth address. + */ +static int rpipe_check_aim(const struct wa_rpipe *rpipe, const struct wahc *wa, +			   const struct usb_host_endpoint *ep, +			   const struct urb *urb, gfp_t gfp) +{ +	int result = 0;		/* better code for lack of companion? */ +	struct device *dev = &wa->usb_iface->dev; +	struct usb_device *usb_dev = urb->dev; +	u8 unauth = (usb_dev->wusb && !usb_dev->authenticated) ? 0x80 : 0; +	u8 portnum = wusb_port_no_to_idx(urb->dev->portnum); + +	d_fnstart(3, dev, "(rpipe %p wa %p ep %p, urb %p)\n", +		    rpipe, wa, ep, urb); +#define AIM_CHECK(rdf, val, text)					\ +	do {								\ +		if (rpipe->descr.rdf != (val)) {			\ +			dev_err(dev,					\ +				"rpipe aim discrepancy: " #rdf " " text "\n", \ +				rpipe->descr.rdf, (val));		\ +			result = -EINVAL;				\ +			WARN_ON(1);					\ +		}							\ +	} while (0) +	AIM_CHECK(wMaxPacketSize, cpu_to_le16(ep->desc.wMaxPacketSize), +		  "(%u vs %u)"); +	AIM_CHECK(bHSHubPort, portnum, "(%u vs %u)"); +	AIM_CHECK(bSpeed, usb_pipeendpoint(urb->pipe) == 0 ? +			UWB_PHY_RATE_53 : UWB_PHY_RATE_200, +		  "(%u vs %u)"); +	AIM_CHECK(bDeviceAddress, urb->dev->devnum | unauth, "(%u vs %u)"); +	AIM_CHECK(bEndpointAddress, ep->desc.bEndpointAddress, "(%u vs %u)"); +	AIM_CHECK(bInterval, ep->desc.bInterval, "(%u vs %u)"); +	AIM_CHECK(bmAttribute, ep->desc.bmAttributes & 0x03, "(%u vs %u)"); +#undef AIM_CHECK +	return result; +} + +#ifndef CONFIG_BUG +#define CONFIG_BUG 0 +#endif + +/* + * Make sure there is an rpipe allocated for an endpoint + * + * If already allocated, we just refcount it; if not, we get an + * idle one, aim it to the right location and take it. + * + * Attaches to ep->hcpriv and rpipe->ep to ep. + */ +int rpipe_get_by_ep(struct wahc *wa, struct usb_host_endpoint *ep, +		    struct urb *urb, gfp_t gfp) +{ +	int result = 0; +	struct device *dev = &wa->usb_iface->dev; +	struct wa_rpipe *rpipe; +	u8 eptype; + +	d_fnstart(3, dev, "(wa %p ep %p urb %p gfp 0x%08x)\n", wa, ep, urb, +		  gfp); +	mutex_lock(&wa->rpipe_mutex); +	rpipe = ep->hcpriv; +	if (rpipe != NULL) { +		if (CONFIG_BUG == 1) { +			result = rpipe_check_aim(rpipe, wa, ep, urb, gfp); +			if (result < 0) +				goto error; +		} +		__rpipe_get(rpipe); +		d_printf(2, dev, "ep 0x%02x: reusing rpipe %u\n", +			 ep->desc.bEndpointAddress, +			 le16_to_cpu(rpipe->descr.wRPipeIndex)); +	} else { +		/* hmm, assign idle rpipe, aim it */ +		result = -ENOBUFS; +		eptype = ep->desc.bmAttributes & 0x03; +		result = rpipe_get_idle(&rpipe, wa, 1 << eptype, gfp); +		if (result < 0) +			goto error; +		result = rpipe_aim(rpipe, wa, ep, urb, gfp); +		if (result < 0) { +			rpipe_put(rpipe); +			goto error; +		} +		ep->hcpriv = rpipe; +		rpipe->ep = ep; +		__rpipe_get(rpipe);	/* for caching into ep->hcpriv */ +		d_printf(2, dev, "ep 0x%02x: using rpipe %u\n", +			 ep->desc.bEndpointAddress, +			 le16_to_cpu(rpipe->descr.wRPipeIndex)); +	} +	d_dump(4, dev, &rpipe->descr, sizeof(rpipe->descr)); +error: +	mutex_unlock(&wa->rpipe_mutex); +	d_fnend(3, dev, "(wa %p ep %p urb %p gfp 0x%08x)\n", wa, ep, urb, gfp); +	return result; +} + +/* + * Allocate the bitmap for each rpipe. + */ +int wa_rpipes_create(struct wahc *wa) +{ +	wa->rpipes = wa->wa_descr->wNumRPipes; +	wa->rpipe_bm = kzalloc(BITS_TO_LONGS(wa->rpipes)*sizeof(unsigned long), +			       GFP_KERNEL); +	if (wa->rpipe_bm == NULL) +		return -ENOMEM; +	return 0; +} + +void wa_rpipes_destroy(struct wahc *wa) +{ +	struct device *dev = &wa->usb_iface->dev; +	d_fnstart(3, dev, "(wa %p)\n", wa); +	if (!bitmap_empty(wa->rpipe_bm, wa->rpipes)) { +		char buf[256]; +		WARN_ON(1); +		bitmap_scnprintf(buf, sizeof(buf), wa->rpipe_bm, wa->rpipes); +		dev_err(dev, "BUG: pipes not released on exit: %s\n", buf); +	} +	kfree(wa->rpipe_bm); +	d_fnend(3, dev, "(wa %p)\n", wa); +} + +/* + * Release resources allocated for an endpoint + * + * If there is an associated rpipe to this endpoint, Abort any pending + * transfers and put it. If the rpipe ends up being destroyed, + * __rpipe_destroy() will cleanup ep->hcpriv. + * + * This is called before calling hcd->stop(), so you don't need to do + * anything else in there. + */ +void rpipe_ep_disable(struct wahc *wa, struct usb_host_endpoint *ep) +{ +	struct device *dev = &wa->usb_iface->dev; +	struct wa_rpipe *rpipe; +	d_fnstart(2, dev, "(wa %p ep %p)\n", wa, ep); +	mutex_lock(&wa->rpipe_mutex); +	rpipe = ep->hcpriv; +	if (rpipe != NULL) { +		unsigned rc = atomic_read(&rpipe->refcnt.refcount); +		int result; +		u16 index = le16_to_cpu(rpipe->descr.wRPipeIndex); + +		if (rc != 1) +			d_printf(1, dev, "(wa %p ep %p) rpipe %p refcnt %u\n", +				 wa, ep, rpipe, rc); + +		d_printf(1, dev, "rpipe %u: abort\n", index); +		result = usb_control_msg( +			wa->usb_dev, usb_rcvctrlpipe(wa->usb_dev, 0), +			USB_REQ_RPIPE_ABORT, +			USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_RPIPE, +			0, index, NULL, 0, 1000 /* FIXME: arbitrary */); +		if (result < 0 && result != -ENODEV /* dev is gone */) +			d_printf(1, dev, "(wa %p rpipe %u): abort failed: %d\n", +				 wa, index, result); +		rpipe_put(rpipe); +	} +	mutex_unlock(&wa->rpipe_mutex); +	d_fnend(2, dev, "(wa %p ep %p)\n", wa, ep); +	return; +} +EXPORT_SYMBOL_GPL(rpipe_ep_disable); diff --git a/drivers/usb/wusbcore/wa-xfer.c b/drivers/usb/wusbcore/wa-xfer.c new file mode 100644 index 00000000000..c038635d1c6 --- /dev/null +++ b/drivers/usb/wusbcore/wa-xfer.c @@ -0,0 +1,1709 @@ +/* + * WUSB Wire Adapter + * Data transfer and URB enqueing + * + * Copyright (C) 2005-2006 Intel Corporation + * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * + * How transfers work: get a buffer, break it up in segments (segment + * size is a multiple of the maxpacket size). For each segment issue a + * segment request (struct wa_xfer_*), then send the data buffer if + * out or nothing if in (all over the DTO endpoint). + * + * For each submitted segment request, a notification will come over + * the NEP endpoint and a transfer result (struct xfer_result) will + * arrive in the DTI URB. Read it, get the xfer ID, see if there is + * data coming (inbound transfer), schedule a read and handle it. + * + * Sounds simple, it is a pain to implement. + * + * + * ENTRY POINTS + * + *   FIXME + * + * LIFE CYCLE / STATE DIAGRAM + * + *   FIXME + * + * THIS CODE IS DISGUSTING + * + *   Warned you are; it's my second try and still not happy with it. + * + * NOTES: + * + *   - No iso + * + *   - Supports DMA xfers, control, bulk and maybe interrupt + * + *   - Does not recycle unused rpipes + * + *     An rpipe is assigned to an endpoint the first time it is used, + *     and then it's there, assigned, until the endpoint is disabled + *     (destroyed [{h,d}wahc_op_ep_disable()]. The assignment of the + *     rpipe to the endpoint is done under the wa->rpipe_sem semaphore + *     (should be a mutex). + * + *     Two methods it could be done: + * + *     (a) set up a timer everytime an rpipe's use count drops to 1 + *         (which means unused) or when a transfer ends. Reset the + *         timer when a xfer is queued. If the timer expires, release + *         the rpipe [see rpipe_ep_disable()]. + * + *     (b) when looking for free rpipes to attach [rpipe_get_by_ep()], + *         when none are found go over the list, check their endpoint + *         and their activity record (if no last-xfer-done-ts in the + *         last x seconds) take it + * + *     However, due to the fact that we have a set of limited + *     resources (max-segments-at-the-same-time per xfer, + *     xfers-per-ripe, blocks-per-rpipe, rpipes-per-host), at the end + *     we are going to have to rebuild all this based on an scheduler, + *     to where we have a list of transactions to do and based on the + *     availability of the different requried components (blocks, + *     rpipes, segment slots, etc), we go scheduling them. Painful. + */ +#include <linux/init.h> +#include <linux/spinlock.h> +#include <linux/hash.h> +#include "wa-hc.h" +#include "wusbhc.h" + +#undef D_LOCAL +#define D_LOCAL 0 /* 0 disabled, > 0 different levels... */ +#include <linux/uwb/debug.h> + +enum { +	WA_SEGS_MAX = 255, +}; + +enum wa_seg_status { +	WA_SEG_NOTREADY, +	WA_SEG_READY, +	WA_SEG_DELAYED, +	WA_SEG_SUBMITTED, +	WA_SEG_PENDING, +	WA_SEG_DTI_PENDING, +	WA_SEG_DONE, +	WA_SEG_ERROR, +	WA_SEG_ABORTED, +}; + +static void wa_xfer_delayed_run(struct wa_rpipe *); + +/* + * Life cycle governed by 'struct urb' (the refcount of the struct is + * that of the 'struct urb' and usb_free_urb() would free the whole + * struct). + */ +struct wa_seg { +	struct urb urb; +	struct urb *dto_urb;		/* for data output? */ +	struct list_head list_node;	/* for rpipe->req_list */ +	struct wa_xfer *xfer;		/* out xfer */ +	u8 index;			/* which segment we are */ +	enum wa_seg_status status; +	ssize_t result;			/* bytes xfered or error */ +	struct wa_xfer_hdr xfer_hdr; +	u8 xfer_extra[];		/* xtra space for xfer_hdr_ctl */ +}; + +static void wa_seg_init(struct wa_seg *seg) +{ +	/* usb_init_urb() repeats a lot of work, so we do it here */ +	kref_init(&seg->urb.kref); +} + +/* + * Protected by xfer->lock + * + */ +struct wa_xfer { +	struct kref refcnt; +	struct list_head list_node; +	spinlock_t lock; +	u32 id; + +	struct wahc *wa;		/* Wire adapter we are plugged to */ +	struct usb_host_endpoint *ep; +	struct urb *urb;		/* URB we are transfering for */ +	struct wa_seg **seg;		/* transfer segments */ +	u8 segs, segs_submitted, segs_done; +	unsigned is_inbound:1; +	unsigned is_dma:1; +	size_t seg_size; +	int result; + +	gfp_t gfp;			/* allocation mask */ + +	struct wusb_dev *wusb_dev;	/* for activity timestamps */ +}; + +static inline void wa_xfer_init(struct wa_xfer *xfer) +{ +	kref_init(&xfer->refcnt); +	INIT_LIST_HEAD(&xfer->list_node); +	spin_lock_init(&xfer->lock); +} + +/* + * Destory a transfer structure + * + * Note that the xfer->seg[index] thingies follow the URB life cycle, + * so we need to put them, not free them. + */ +static void wa_xfer_destroy(struct kref *_xfer) +{ +	struct wa_xfer *xfer = container_of(_xfer, struct wa_xfer, refcnt); +	if (xfer->seg) { +		unsigned cnt; +		for (cnt = 0; cnt < xfer->segs; cnt++) { +			if (xfer->is_inbound) +				usb_put_urb(xfer->seg[cnt]->dto_urb); +			usb_put_urb(&xfer->seg[cnt]->urb); +		} +	} +	kfree(xfer); +	d_printf(2, NULL, "xfer %p destroyed\n", xfer); +} + +static void wa_xfer_get(struct wa_xfer *xfer) +{ +	kref_get(&xfer->refcnt); +} + +static void wa_xfer_put(struct wa_xfer *xfer) +{ +	d_fnstart(3, NULL, "(xfer %p) -- ref count bef put %d\n", +		    xfer, atomic_read(&xfer->refcnt.refcount)); +	kref_put(&xfer->refcnt, wa_xfer_destroy); +	d_fnend(3, NULL, "(xfer %p) = void\n", xfer); +} + +/* + * xfer is referenced + * + * xfer->lock has to be unlocked + * + * We take xfer->lock for setting the result; this is a barrier + * against drivers/usb/core/hcd.c:unlink1() being called after we call + * usb_hcd_giveback_urb() and wa_urb_dequeue() trying to get a + * reference to the transfer. + */ +static void wa_xfer_giveback(struct wa_xfer *xfer) +{ +	unsigned long flags; +	d_fnstart(3, NULL, "(xfer %p)\n", xfer); +	spin_lock_irqsave(&xfer->wa->xfer_list_lock, flags); +	list_del_init(&xfer->list_node); +	spin_unlock_irqrestore(&xfer->wa->xfer_list_lock, flags); +	/* FIXME: segmentation broken -- kills DWA */ +	wusbhc_giveback_urb(xfer->wa->wusb, xfer->urb, xfer->result); +	wa_put(xfer->wa); +	wa_xfer_put(xfer); +	d_fnend(3, NULL, "(xfer %p) = void\n", xfer); +} + +/* + * xfer is referenced + * + * xfer->lock has to be unlocked + */ +static void wa_xfer_completion(struct wa_xfer *xfer) +{ +	d_fnstart(3, NULL, "(xfer %p)\n", xfer); +	if (xfer->wusb_dev) +		wusb_dev_put(xfer->wusb_dev); +	rpipe_put(xfer->ep->hcpriv); +	wa_xfer_giveback(xfer); +	d_fnend(3, NULL, "(xfer %p) = void\n", xfer); +	return; +} + +/* + * If transfer is done, wrap it up and return true + * + * xfer->lock has to be locked + */ +static unsigned __wa_xfer_is_done(struct wa_xfer *xfer) +{ +	unsigned result, cnt; +	struct wa_seg *seg; +	struct urb *urb = xfer->urb; +	unsigned found_short = 0; + +	d_fnstart(3, NULL, "(xfer %p)\n", xfer); +	result = xfer->segs_done == xfer->segs_submitted; +	if (result == 0) +		goto out; +	urb->actual_length = 0; +	for (cnt = 0; cnt < xfer->segs; cnt++) { +		seg = xfer->seg[cnt]; +		switch (seg->status) { +		case WA_SEG_DONE: +			if (found_short && seg->result > 0) { +				if (printk_ratelimit()) +					printk(KERN_ERR "xfer %p#%u: bad short " +					       "segments (%zu)\n", xfer, cnt, +					       seg->result); +				urb->status = -EINVAL; +				goto out; +			} +			urb->actual_length += seg->result; +			if (seg->result < xfer->seg_size +			    && cnt != xfer->segs-1) +				found_short = 1; +			d_printf(2, NULL, "xfer %p#%u: DONE short %d " +				 "result %zu urb->actual_length %d\n", +				 xfer, seg->index, found_short, seg->result, +				 urb->actual_length); +			break; +		case WA_SEG_ERROR: +			xfer->result = seg->result; +			d_printf(2, NULL, "xfer %p#%u: ERROR result %zu\n", +				 xfer, seg->index, seg->result); +			goto out; +		case WA_SEG_ABORTED: +			WARN_ON(urb->status != -ECONNRESET +				&& urb->status != -ENOENT); +			d_printf(2, NULL, "xfer %p#%u ABORTED: result %d\n", +				 xfer, seg->index, urb->status); +			xfer->result = urb->status; +			goto out; +		default: +			/* if (printk_ratelimit()) */ +				printk(KERN_ERR "xfer %p#%u: " +				       "is_done bad state %d\n", +				       xfer, cnt, seg->status); +			xfer->result = -EINVAL; +			WARN_ON(1); +			goto out; +		} +	} +	xfer->result = 0; +out: +	d_fnend(3, NULL, "(xfer %p) = void\n", xfer); +	return result; +} + +/* + * Initialize a transfer's ID + * + * We need to use a sequential number; if we use the pointer or the + * hash of the pointer, it can repeat over sequential transfers and + * then it will confuse the HWA....wonder why in hell they put a 32 + * bit handle in there then. + */ +static void wa_xfer_id_init(struct wa_xfer *xfer) +{ +	xfer->id = atomic_add_return(1, &xfer->wa->xfer_id_count); +} + +/* + * Return the xfer's ID associated with xfer + * + * Need to generate a + */ +static u32 wa_xfer_id(struct wa_xfer *xfer) +{ +	return xfer->id; +} + +/* + * Search for a transfer list ID on the HCD's URB list + * + * For 32 bit architectures, we use the pointer itself; for 64 bits, a + * 32-bit hash of the pointer. + * + * @returns NULL if not found. + */ +static struct wa_xfer *wa_xfer_get_by_id(struct wahc *wa, u32 id) +{ +	unsigned long flags; +	struct wa_xfer *xfer_itr; +	spin_lock_irqsave(&wa->xfer_list_lock, flags); +	list_for_each_entry(xfer_itr, &wa->xfer_list, list_node) { +		if (id == xfer_itr->id) { +			wa_xfer_get(xfer_itr); +			goto out; +		} +	} +	xfer_itr = NULL; +out: +	spin_unlock_irqrestore(&wa->xfer_list_lock, flags); +	return xfer_itr; +} + +struct wa_xfer_abort_buffer { +	struct urb urb; +	struct wa_xfer_abort cmd; +}; + +static void __wa_xfer_abort_cb(struct urb *urb) +{ +	struct wa_xfer_abort_buffer *b = urb->context; +	usb_put_urb(&b->urb); +} + +/* + * Aborts an ongoing transaction + * + * Assumes the transfer is referenced and locked and in a submitted + * state (mainly that there is an endpoint/rpipe assigned). + * + * The callback (see above) does nothing but freeing up the data by + * putting the URB. Because the URB is allocated at the head of the + * struct, the whole space we allocated is kfreed. + * + * We'll get an 'aborted transaction' xfer result on DTI, that'll + * politely ignore because at this point the transaction has been + * marked as aborted already. + */ +static void __wa_xfer_abort(struct wa_xfer *xfer) +{ +	int result; +	struct device *dev = &xfer->wa->usb_iface->dev; +	struct wa_xfer_abort_buffer *b; +	struct wa_rpipe *rpipe = xfer->ep->hcpriv; + +	b = kmalloc(sizeof(*b), GFP_ATOMIC); +	if (b == NULL) +		goto error_kmalloc; +	b->cmd.bLength =  sizeof(b->cmd); +	b->cmd.bRequestType = WA_XFER_ABORT; +	b->cmd.wRPipe = rpipe->descr.wRPipeIndex; +	b->cmd.dwTransferID = wa_xfer_id(xfer); + +	usb_init_urb(&b->urb); +	usb_fill_bulk_urb(&b->urb, xfer->wa->usb_dev, +		usb_sndbulkpipe(xfer->wa->usb_dev, +				xfer->wa->dto_epd->bEndpointAddress), +		&b->cmd, sizeof(b->cmd), __wa_xfer_abort_cb, b); +	result = usb_submit_urb(&b->urb, GFP_ATOMIC); +	if (result < 0) +		goto error_submit; +	return;				/* callback frees! */ + + +error_submit: +	if (printk_ratelimit()) +		dev_err(dev, "xfer %p: Can't submit abort request: %d\n", +			xfer, result); +	kfree(b); +error_kmalloc: +	return; + +} + +/* + * + * @returns < 0 on error, transfer segment request size if ok + */ +static ssize_t __wa_xfer_setup_sizes(struct wa_xfer *xfer, +				     enum wa_xfer_type *pxfer_type) +{ +	ssize_t result; +	struct device *dev = &xfer->wa->usb_iface->dev; +	size_t maxpktsize; +	struct urb *urb = xfer->urb; +	struct wa_rpipe *rpipe = xfer->ep->hcpriv; + +	d_fnstart(3, dev, "(xfer %p [rpipe %p] urb %p)\n", +		  xfer, rpipe, urb); +	switch (rpipe->descr.bmAttribute & 0x3) { +	case USB_ENDPOINT_XFER_CONTROL: +		*pxfer_type = WA_XFER_TYPE_CTL; +		result = sizeof(struct wa_xfer_ctl); +		break; +	case USB_ENDPOINT_XFER_INT: +	case USB_ENDPOINT_XFER_BULK: +		*pxfer_type = WA_XFER_TYPE_BI; +		result = sizeof(struct wa_xfer_bi); +		break; +	case USB_ENDPOINT_XFER_ISOC: +		dev_err(dev, "FIXME: ISOC not implemented\n"); +		result = -ENOSYS; +		goto error; +	default: +		/* never happens */ +		BUG(); +		result = -EINVAL;	/* shut gcc up */ +	}; +	xfer->is_inbound = urb->pipe & USB_DIR_IN ? 1 : 0; +	xfer->is_dma = urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP ? 1 : 0; +	xfer->seg_size = le16_to_cpu(rpipe->descr.wBlocks) +		* 1 << (xfer->wa->wa_descr->bRPipeBlockSize - 1); +	/* Compute the segment size and make sure it is a multiple of +	 * the maxpktsize (WUSB1.0[8.3.3.1])...not really too much of +	 * a check (FIXME) */ +	maxpktsize = le16_to_cpu(rpipe->descr.wMaxPacketSize); +	if (xfer->seg_size < maxpktsize) { +		dev_err(dev, "HW BUG? seg_size %zu smaller than maxpktsize " +			"%zu\n", xfer->seg_size, maxpktsize); +		result = -EINVAL; +		goto error; +	} +	xfer->seg_size = (xfer->seg_size / maxpktsize) * maxpktsize; +	xfer->segs = (urb->transfer_buffer_length + xfer->seg_size - 1) +		/ xfer->seg_size; +	if (xfer->segs >= WA_SEGS_MAX) { +		dev_err(dev, "BUG? ops, number of segments %d bigger than %d\n", +			(int)(urb->transfer_buffer_length / xfer->seg_size), +			WA_SEGS_MAX); +		result = -EINVAL; +		goto error; +	} +	if (xfer->segs == 0 && *pxfer_type == WA_XFER_TYPE_CTL) +		xfer->segs = 1; +error: +	d_fnend(3, dev, "(xfer %p [rpipe %p] urb %p) = %d\n", +		xfer, rpipe, urb, (int)result); +	return result; +} + +/** Fill in the common request header and xfer-type specific data. */ +static void __wa_xfer_setup_hdr0(struct wa_xfer *xfer, +				 struct wa_xfer_hdr *xfer_hdr0, +				 enum wa_xfer_type xfer_type, +				 size_t xfer_hdr_size) +{ +	struct wa_rpipe *rpipe = xfer->ep->hcpriv; + +	xfer_hdr0 = &xfer->seg[0]->xfer_hdr; +	xfer_hdr0->bLength = xfer_hdr_size; +	xfer_hdr0->bRequestType = xfer_type; +	xfer_hdr0->wRPipe = rpipe->descr.wRPipeIndex; +	xfer_hdr0->dwTransferID = wa_xfer_id(xfer); +	xfer_hdr0->bTransferSegment = 0; +	switch (xfer_type) { +	case WA_XFER_TYPE_CTL: { +		struct wa_xfer_ctl *xfer_ctl = +			container_of(xfer_hdr0, struct wa_xfer_ctl, hdr); +		xfer_ctl->bmAttribute = xfer->is_inbound ? 1 : 0; +		BUG_ON(xfer->urb->transfer_flags & URB_NO_SETUP_DMA_MAP +		       && xfer->urb->setup_packet == NULL); +		memcpy(&xfer_ctl->baSetupData, xfer->urb->setup_packet, +		       sizeof(xfer_ctl->baSetupData)); +		break; +	} +	case WA_XFER_TYPE_BI: +		break; +	case WA_XFER_TYPE_ISO: +		printk(KERN_ERR "FIXME: ISOC not implemented\n"); +	default: +		BUG(); +	}; +} + +/* + * Callback for the OUT data phase of the segment request + * + * Check wa_seg_cb(); most comments also apply here because this + * function does almost the same thing and they work closely + * together. + * + * If the seg request has failed but this DTO phase has suceeded, + * wa_seg_cb() has already failed the segment and moved the + * status to WA_SEG_ERROR, so this will go through 'case 0' and + * effectively do nothing. + */ +static void wa_seg_dto_cb(struct urb *urb) +{ +	struct wa_seg *seg = urb->context; +	struct wa_xfer *xfer = seg->xfer; +	struct wahc *wa; +	struct device *dev; +	struct wa_rpipe *rpipe; +	unsigned long flags; +	unsigned rpipe_ready = 0; +	u8 done = 0; + +	d_fnstart(3, NULL, "(urb %p [%d])\n", urb, urb->status); +	switch (urb->status) { +	case 0: +		spin_lock_irqsave(&xfer->lock, flags); +		wa = xfer->wa; +		dev = &wa->usb_iface->dev; +		d_printf(2, dev, "xfer %p#%u: data out done (%d bytes)\n", +			   xfer, seg->index, urb->actual_length); +		if (seg->status < WA_SEG_PENDING) +			seg->status = WA_SEG_PENDING; +		seg->result = urb->actual_length; +		spin_unlock_irqrestore(&xfer->lock, flags); +		break; +	case -ECONNRESET:	/* URB unlinked; no need to do anything */ +	case -ENOENT:		/* as it was done by the who unlinked us */ +		break; +	default:		/* Other errors ... */ +		spin_lock_irqsave(&xfer->lock, flags); +		wa = xfer->wa; +		dev = &wa->usb_iface->dev; +		rpipe = xfer->ep->hcpriv; +		if (printk_ratelimit()) +			dev_err(dev, "xfer %p#%u: data out error %d\n", +				xfer, seg->index, urb->status); +		if (edc_inc(&wa->nep_edc, EDC_MAX_ERRORS, +			    EDC_ERROR_TIMEFRAME)){ +			dev_err(dev, "DTO: URB max acceptable errors " +				"exceeded, resetting device\n"); +			wa_reset_all(wa); +		} +		if (seg->status != WA_SEG_ERROR) { +			seg->status = WA_SEG_ERROR; +			seg->result = urb->status; +			xfer->segs_done++; +			__wa_xfer_abort(xfer); +			rpipe_ready = rpipe_avail_inc(rpipe); +			done = __wa_xfer_is_done(xfer); +		} +		spin_unlock_irqrestore(&xfer->lock, flags); +		if (done) +			wa_xfer_completion(xfer); +		if (rpipe_ready) +			wa_xfer_delayed_run(rpipe); +	} +	d_fnend(3, NULL, "(urb %p [%d]) = void\n", urb, urb->status); +} + +/* + * Callback for the segment request + * + * If succesful transition state (unless already transitioned or + * outbound transfer); otherwise, take a note of the error, mark this + * segment done and try completion. + * + * Note we don't access until we are sure that the transfer hasn't + * been cancelled (ECONNRESET, ENOENT), which could mean that + * seg->xfer could be already gone. + * + * We have to check before setting the status to WA_SEG_PENDING + * because sometimes the xfer result callback arrives before this + * callback (geeeeeeze), so it might happen that we are already in + * another state. As well, we don't set it if the transfer is inbound, + * as in that case, wa_seg_dto_cb will do it when the OUT data phase + * finishes. + */ +static void wa_seg_cb(struct urb *urb) +{ +	struct wa_seg *seg = urb->context; +	struct wa_xfer *xfer = seg->xfer; +	struct wahc *wa; +	struct device *dev; +	struct wa_rpipe *rpipe; +	unsigned long flags; +	unsigned rpipe_ready; +	u8 done = 0; + +	d_fnstart(3, NULL, "(urb %p [%d])\n", urb, urb->status); +	switch (urb->status) { +	case 0: +		spin_lock_irqsave(&xfer->lock, flags); +		wa = xfer->wa; +		dev = &wa->usb_iface->dev; +		d_printf(2, dev, "xfer %p#%u: request done\n", +			   xfer, seg->index); +		if (xfer->is_inbound && seg->status < WA_SEG_PENDING) +			seg->status = WA_SEG_PENDING; +		spin_unlock_irqrestore(&xfer->lock, flags); +		break; +	case -ECONNRESET:	/* URB unlinked; no need to do anything */ +	case -ENOENT:		/* as it was done by the who unlinked us */ +		break; +	default:		/* Other errors ... */ +		spin_lock_irqsave(&xfer->lock, flags); +		wa = xfer->wa; +		dev = &wa->usb_iface->dev; +		rpipe = xfer->ep->hcpriv; +		if (printk_ratelimit()) +			dev_err(dev, "xfer %p#%u: request error %d\n", +				xfer, seg->index, urb->status); +		if (edc_inc(&wa->nep_edc, EDC_MAX_ERRORS, +			    EDC_ERROR_TIMEFRAME)){ +			dev_err(dev, "DTO: URB max acceptable errors " +				"exceeded, resetting device\n"); +			wa_reset_all(wa); +		} +		usb_unlink_urb(seg->dto_urb); +		seg->status = WA_SEG_ERROR; +		seg->result = urb->status; +		xfer->segs_done++; +		__wa_xfer_abort(xfer); +		rpipe_ready = rpipe_avail_inc(rpipe); +		done = __wa_xfer_is_done(xfer); +		spin_unlock_irqrestore(&xfer->lock, flags); +		if (done) +			wa_xfer_completion(xfer); +		if (rpipe_ready) +			wa_xfer_delayed_run(rpipe); +	} +	d_fnend(3, NULL, "(urb %p [%d]) = void\n", urb, urb->status); +} + +/* + * Allocate the segs array and initialize each of them + * + * The segments are freed by wa_xfer_destroy() when the xfer use count + * drops to zero; however, because each segment is given the same life + * cycle as the USB URB it contains, it is actually freed by + * usb_put_urb() on the contained USB URB (twisted, eh?). + */ +static int __wa_xfer_setup_segs(struct wa_xfer *xfer, size_t xfer_hdr_size) +{ +	int result, cnt; +	size_t alloc_size = sizeof(*xfer->seg[0]) +		- sizeof(xfer->seg[0]->xfer_hdr) + xfer_hdr_size; +	struct usb_device *usb_dev = xfer->wa->usb_dev; +	const struct usb_endpoint_descriptor *dto_epd = xfer->wa->dto_epd; +	struct wa_seg *seg; +	size_t buf_itr, buf_size, buf_itr_size; + +	result = -ENOMEM; +	xfer->seg = kcalloc(xfer->segs, sizeof(xfer->seg[0]), GFP_ATOMIC); +	if (xfer->seg == NULL) +		goto error_segs_kzalloc; +	buf_itr = 0; +	buf_size = xfer->urb->transfer_buffer_length; +	for (cnt = 0; cnt < xfer->segs; cnt++) { +		seg = xfer->seg[cnt] = kzalloc(alloc_size, GFP_ATOMIC); +		if (seg == NULL) +			goto error_seg_kzalloc; +		wa_seg_init(seg); +		seg->xfer = xfer; +		seg->index = cnt; +		usb_fill_bulk_urb(&seg->urb, usb_dev, +				  usb_sndbulkpipe(usb_dev, +						  dto_epd->bEndpointAddress), +				  &seg->xfer_hdr, xfer_hdr_size, +				  wa_seg_cb, seg); +		buf_itr_size = buf_size > xfer->seg_size ? +			xfer->seg_size : buf_size; +		if (xfer->is_inbound == 0 && buf_size > 0) { +			seg->dto_urb = usb_alloc_urb(0, GFP_ATOMIC); +			if (seg->dto_urb == NULL) +				goto error_dto_alloc; +			usb_fill_bulk_urb( +				seg->dto_urb, usb_dev, +				usb_sndbulkpipe(usb_dev, +						dto_epd->bEndpointAddress), +				NULL, 0, wa_seg_dto_cb, seg); +			if (xfer->is_dma) { +				seg->dto_urb->transfer_dma = +					xfer->urb->transfer_dma + buf_itr; +				seg->dto_urb->transfer_flags |= +					URB_NO_TRANSFER_DMA_MAP; +			} else +				seg->dto_urb->transfer_buffer = +					xfer->urb->transfer_buffer + buf_itr; +			seg->dto_urb->transfer_buffer_length = buf_itr_size; +		} +		seg->status = WA_SEG_READY; +		buf_itr += buf_itr_size; +		buf_size -= buf_itr_size; +	} +	return 0; + +error_dto_alloc: +	kfree(xfer->seg[cnt]); +	cnt--; +error_seg_kzalloc: +	/* use the fact that cnt is left at were it failed */ +	for (; cnt > 0; cnt--) { +		if (xfer->is_inbound == 0) +			kfree(xfer->seg[cnt]->dto_urb); +		kfree(xfer->seg[cnt]); +	} +error_segs_kzalloc: +	return result; +} + +/* + * Allocates all the stuff needed to submit a transfer + * + * Breaks the whole data buffer in a list of segments, each one has a + * structure allocated to it and linked in xfer->seg[index] + * + * FIXME: merge setup_segs() and the last part of this function, no + *        need to do two for loops when we could run everything in a + *        single one + */ +static int __wa_xfer_setup(struct wa_xfer *xfer, struct urb *urb) +{ +	int result; +	struct device *dev = &xfer->wa->usb_iface->dev; +	enum wa_xfer_type xfer_type = 0; /* shut up GCC */ +	size_t xfer_hdr_size, cnt, transfer_size; +	struct wa_xfer_hdr *xfer_hdr0, *xfer_hdr; + +	d_fnstart(3, dev, "(xfer %p [rpipe %p] urb %p)\n", +		  xfer, xfer->ep->hcpriv, urb); + +	result = __wa_xfer_setup_sizes(xfer, &xfer_type); +	if (result < 0) +		goto error_setup_sizes; +	xfer_hdr_size = result; +	result = __wa_xfer_setup_segs(xfer, xfer_hdr_size); +	if (result < 0) { +		dev_err(dev, "xfer %p: Failed to allocate %d segments: %d\n", +			xfer, xfer->segs, result); +		goto error_setup_segs; +	} +	/* Fill the first header */ +	xfer_hdr0 = &xfer->seg[0]->xfer_hdr; +	wa_xfer_id_init(xfer); +	__wa_xfer_setup_hdr0(xfer, xfer_hdr0, xfer_type, xfer_hdr_size); + +	/* Fill remainig headers */ +	xfer_hdr = xfer_hdr0; +	transfer_size = urb->transfer_buffer_length; +	xfer_hdr0->dwTransferLength = transfer_size > xfer->seg_size ? +		xfer->seg_size : transfer_size; +	transfer_size -=  xfer->seg_size; +	for (cnt = 1; cnt < xfer->segs; cnt++) { +		xfer_hdr = &xfer->seg[cnt]->xfer_hdr; +		memcpy(xfer_hdr, xfer_hdr0, xfer_hdr_size); +		xfer_hdr->bTransferSegment = cnt; +		xfer_hdr->dwTransferLength = transfer_size > xfer->seg_size ? +			cpu_to_le32(xfer->seg_size) +			: cpu_to_le32(transfer_size); +		xfer->seg[cnt]->status = WA_SEG_READY; +		transfer_size -=  xfer->seg_size; +	} +	xfer_hdr->bTransferSegment |= 0x80;	/* this is the last segment */ +	result = 0; +error_setup_segs: +error_setup_sizes: +	d_fnend(3, dev, "(xfer %p [rpipe %p] urb %p) = %d\n", +		xfer, xfer->ep->hcpriv, urb, result); +	return result; +} + +/* + * + * + * rpipe->seg_lock is held! + */ +static int __wa_seg_submit(struct wa_rpipe *rpipe, struct wa_xfer *xfer, +			   struct wa_seg *seg) +{ +	int result; +	result = usb_submit_urb(&seg->urb, GFP_ATOMIC); +	if (result < 0) { +		printk(KERN_ERR "xfer %p#%u: REQ submit failed: %d\n", +		       xfer, seg->index, result); +		goto error_seg_submit; +	} +	if (seg->dto_urb) { +		result = usb_submit_urb(seg->dto_urb, GFP_ATOMIC); +		if (result < 0) { +			printk(KERN_ERR "xfer %p#%u: DTO submit failed: %d\n", +			       xfer, seg->index, result); +			goto error_dto_submit; +		} +	} +	seg->status = WA_SEG_SUBMITTED; +	rpipe_avail_dec(rpipe); +	return 0; + +error_dto_submit: +	usb_unlink_urb(&seg->urb); +error_seg_submit: +	seg->status = WA_SEG_ERROR; +	seg->result = result; +	return result; +} + +/* + * Execute more queued request segments until the maximum concurrent allowed + * + * The ugly unlock/lock sequence on the error path is needed as the + * xfer->lock normally nests the seg_lock and not viceversa. + * + */ +static void wa_xfer_delayed_run(struct wa_rpipe *rpipe) +{ +	int result; +	struct device *dev = &rpipe->wa->usb_iface->dev; +	struct wa_seg *seg; +	struct wa_xfer *xfer; +	unsigned long flags; + +	d_fnstart(1, dev, "(rpipe #%d) %d segments available\n", +		  le16_to_cpu(rpipe->descr.wRPipeIndex), +		  atomic_read(&rpipe->segs_available)); +	spin_lock_irqsave(&rpipe->seg_lock, flags); +	while (atomic_read(&rpipe->segs_available) > 0 +	      && !list_empty(&rpipe->seg_list)) { +		seg = list_entry(rpipe->seg_list.next, struct wa_seg, +				 list_node); +		list_del(&seg->list_node); +		xfer = seg->xfer; +		result = __wa_seg_submit(rpipe, xfer, seg); +		d_printf(1, dev, "xfer %p#%u submitted from delayed " +			 "[%d segments available] %d\n", +			 xfer, seg->index, +			 atomic_read(&rpipe->segs_available), result); +		if (unlikely(result < 0)) { +			spin_unlock_irqrestore(&rpipe->seg_lock, flags); +			spin_lock_irqsave(&xfer->lock, flags); +			__wa_xfer_abort(xfer); +			xfer->segs_done++; +			spin_unlock_irqrestore(&xfer->lock, flags); +			spin_lock_irqsave(&rpipe->seg_lock, flags); +		} +	} +	spin_unlock_irqrestore(&rpipe->seg_lock, flags); +	d_fnend(1, dev, "(rpipe #%d) = void, %d segments available\n", +		le16_to_cpu(rpipe->descr.wRPipeIndex), +		atomic_read(&rpipe->segs_available)); + +} + +/* + * + * xfer->lock is taken + * + * On failure submitting we just stop submitting and return error; + * wa_urb_enqueue_b() will execute the completion path + */ +static int __wa_xfer_submit(struct wa_xfer *xfer) +{ +	int result; +	struct wahc *wa = xfer->wa; +	struct device *dev = &wa->usb_iface->dev; +	unsigned cnt; +	struct wa_seg *seg; +	unsigned long flags; +	struct wa_rpipe *rpipe = xfer->ep->hcpriv; +	size_t maxrequests = le16_to_cpu(rpipe->descr.wRequests); +	u8 available; +	u8 empty; + +	d_fnstart(3, dev, "(xfer %p [rpipe %p])\n", +		  xfer, xfer->ep->hcpriv); + +	spin_lock_irqsave(&wa->xfer_list_lock, flags); +	list_add_tail(&xfer->list_node, &wa->xfer_list); +	spin_unlock_irqrestore(&wa->xfer_list_lock, flags); + +	BUG_ON(atomic_read(&rpipe->segs_available) > maxrequests); +	result = 0; +	spin_lock_irqsave(&rpipe->seg_lock, flags); +	for (cnt = 0; cnt < xfer->segs; cnt++) { +		available = atomic_read(&rpipe->segs_available); +		empty = list_empty(&rpipe->seg_list); +		seg = xfer->seg[cnt]; +		d_printf(2, dev, "xfer %p#%u: available %u empty %u (%s)\n", +			 xfer, cnt, available, empty, +			 available == 0 || !empty ? "delayed" : "submitted"); +		if (available == 0 || !empty) { +			d_printf(1, dev, "xfer %p#%u: delayed\n", xfer, cnt); +			seg->status = WA_SEG_DELAYED; +			list_add_tail(&seg->list_node, &rpipe->seg_list); +		} else { +			result = __wa_seg_submit(rpipe, xfer, seg); +			if (result < 0) +				goto error_seg_submit; +		} +		xfer->segs_submitted++; +	} +	spin_unlock_irqrestore(&rpipe->seg_lock, flags); +	d_fnend(3, dev, "(xfer %p [rpipe %p]) = void\n", xfer, +		xfer->ep->hcpriv); +	return result; + +error_seg_submit: +	__wa_xfer_abort(xfer); +	spin_unlock_irqrestore(&rpipe->seg_lock, flags); +	d_fnend(3, dev, "(xfer %p [rpipe %p]) = void\n", xfer, +		xfer->ep->hcpriv); +	return result; +} + +/* + * Second part of a URB/transfer enqueuement + * + * Assumes this comes from wa_urb_enqueue() [maybe through + * wa_urb_enqueue_run()]. At this point: + * + * xfer->wa	filled and refcounted + * xfer->ep	filled with rpipe refcounted if + *              delayed == 0 + * xfer->urb 	filled and refcounted (this is the case when called + *              from wa_urb_enqueue() as we come from usb_submit_urb() + *              and when called by wa_urb_enqueue_run(), as we took an + *              extra ref dropped by _run() after we return). + * xfer->gfp	filled + * + * If we fail at __wa_xfer_submit(), then we just check if we are done + * and if so, we run the completion procedure. However, if we are not + * yet done, we do nothing and wait for the completion handlers from + * the submitted URBs or from the xfer-result path to kick in. If xfer + * result never kicks in, the xfer will timeout from the USB code and + * dequeue() will be called. + */ +static void wa_urb_enqueue_b(struct wa_xfer *xfer) +{ +	int result; +	unsigned long flags; +	struct urb *urb = xfer->urb; +	struct wahc *wa = xfer->wa; +	struct wusbhc *wusbhc = wa->wusb; +	struct device *dev = &wa->usb_iface->dev; +	struct wusb_dev *wusb_dev; +	unsigned done; + +	d_fnstart(3, dev, "(wa %p urb %p)\n", wa, urb); +	result = rpipe_get_by_ep(wa, xfer->ep, urb, xfer->gfp); +	if (result < 0) +		goto error_rpipe_get; +	result = -ENODEV; +	/* FIXME: segmentation broken -- kills DWA */ +	mutex_lock(&wusbhc->mutex);		/* get a WUSB dev */ +	if (urb->dev == NULL) +		goto error_dev_gone; +	wusb_dev = __wusb_dev_get_by_usb_dev(wusbhc, urb->dev); +	if (wusb_dev == NULL) { +		mutex_unlock(&wusbhc->mutex); +		goto error_dev_gone; +	} +	mutex_unlock(&wusbhc->mutex); + +	spin_lock_irqsave(&xfer->lock, flags); +	xfer->wusb_dev = wusb_dev; +	result = urb->status; +	if (urb->status != -EINPROGRESS) +		goto error_dequeued; + +	result = __wa_xfer_setup(xfer, urb); +	if (result < 0) +		goto error_xfer_setup; +	result = __wa_xfer_submit(xfer); +	if (result < 0) +		goto error_xfer_submit; +	spin_unlock_irqrestore(&xfer->lock, flags); +	d_fnend(3, dev, "(wa %p urb %p) = void\n", wa, urb); +	return; + +	/* this is basically wa_xfer_completion() broken up wa_xfer_giveback() +	 * does a wa_xfer_put() that will call wa_xfer_destroy() and clean +	 * upundo setup(). +	 */ +error_xfer_setup: +error_dequeued: +	spin_unlock_irqrestore(&xfer->lock, flags); +	/* FIXME: segmentation broken, kills DWA */ +	if (wusb_dev) +		wusb_dev_put(wusb_dev); +error_dev_gone: +	rpipe_put(xfer->ep->hcpriv); +error_rpipe_get: +	xfer->result = result; +	wa_xfer_giveback(xfer); +	d_fnend(3, dev, "(wa %p urb %p) = (void) %d\n", wa, urb, result); +	return; + +error_xfer_submit: +	done = __wa_xfer_is_done(xfer); +	xfer->result = result; +	spin_unlock_irqrestore(&xfer->lock, flags); +	if (done) +		wa_xfer_completion(xfer); +	d_fnend(3, dev, "(wa %p urb %p) = (void) %d\n", wa, urb, result); +	return; +} + +/* + * Execute the delayed transfers in the Wire Adapter @wa + * + * We need to be careful here, as dequeue() could be called in the + * middle.  That's why we do the whole thing under the + * wa->xfer_list_lock. If dequeue() jumps in, it first locks urb->lock + * and then checks the list -- so as we would be acquiring in inverse + * order, we just drop the lock once we have the xfer and reacquire it + * later. + */ +void wa_urb_enqueue_run(struct work_struct *ws) +{ +	struct wahc *wa = container_of(ws, struct wahc, xfer_work); +	struct device *dev = &wa->usb_iface->dev; +	struct wa_xfer *xfer, *next; +	struct urb *urb; + +	d_fnstart(3, dev, "(wa %p)\n", wa); +	spin_lock_irq(&wa->xfer_list_lock); +	list_for_each_entry_safe(xfer, next, &wa->xfer_delayed_list, +				 list_node) { +		list_del_init(&xfer->list_node); +		spin_unlock_irq(&wa->xfer_list_lock); + +		urb = xfer->urb; +		wa_urb_enqueue_b(xfer); +		usb_put_urb(urb);	/* taken when queuing */ + +		spin_lock_irq(&wa->xfer_list_lock); +	} +	spin_unlock_irq(&wa->xfer_list_lock); +	d_fnend(3, dev, "(wa %p) = void\n", wa); +} +EXPORT_SYMBOL_GPL(wa_urb_enqueue_run); + +/* + * Submit a transfer to the Wire Adapter in a delayed way + * + * The process of enqueuing involves possible sleeps() [see + * enqueue_b(), for the rpipe_get() and the mutex_lock()]. If we are + * in an atomic section, we defer the enqueue_b() call--else we call direct. + * + * @urb: We own a reference to it done by the HCI Linux USB stack that + *       will be given up by calling usb_hcd_giveback_urb() or by + *       returning error from this function -> ergo we don't have to + *       refcount it. + */ +int wa_urb_enqueue(struct wahc *wa, struct usb_host_endpoint *ep, +		   struct urb *urb, gfp_t gfp) +{ +	int result; +	struct device *dev = &wa->usb_iface->dev; +	struct wa_xfer *xfer; +	unsigned long my_flags; +	unsigned cant_sleep = irqs_disabled() | in_atomic(); + +	d_fnstart(3, dev, "(wa %p ep %p urb %p [%d] gfp 0x%x)\n", +		  wa, ep, urb, urb->transfer_buffer_length, gfp); + +	if (urb->transfer_buffer == NULL +	    && !(urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP) +	    && urb->transfer_buffer_length != 0) { +		dev_err(dev, "BUG? urb %p: NULL xfer buffer & NODMA\n", urb); +		dump_stack(); +	} + +	result = -ENOMEM; +	xfer = kzalloc(sizeof(*xfer), gfp); +	if (xfer == NULL) +		goto error_kmalloc; + +	result = -ENOENT; +	if (urb->status != -EINPROGRESS)	/* cancelled */ +		goto error_dequeued;		/* before starting? */ +	wa_xfer_init(xfer); +	xfer->wa = wa_get(wa); +	xfer->urb = urb; +	xfer->gfp = gfp; +	xfer->ep = ep; +	urb->hcpriv = xfer; +	d_printf(2, dev, "xfer %p urb %p pipe 0x%02x [%d bytes] %s %s %s\n", +		 xfer, urb, urb->pipe, urb->transfer_buffer_length, +		 urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP ? "dma" : "nodma", +		 urb->pipe & USB_DIR_IN ? "inbound" : "outbound", +		 cant_sleep ? "deferred" : "inline"); +	if (cant_sleep) { +		usb_get_urb(urb); +		spin_lock_irqsave(&wa->xfer_list_lock, my_flags); +		list_add_tail(&xfer->list_node, &wa->xfer_delayed_list); +		spin_unlock_irqrestore(&wa->xfer_list_lock, my_flags); +		queue_work(wusbd, &wa->xfer_work); +	} else { +		wa_urb_enqueue_b(xfer); +	} +	d_fnend(3, dev, "(wa %p ep %p urb %p [%d] gfp 0x%x) = 0\n", +		wa, ep, urb, urb->transfer_buffer_length, gfp); +	return 0; + +error_dequeued: +	kfree(xfer); +error_kmalloc: +	d_fnend(3, dev, "(wa %p ep %p urb %p [%d] gfp 0x%x) = %d\n", +		wa, ep, urb, urb->transfer_buffer_length, gfp, result); +	return result; +} +EXPORT_SYMBOL_GPL(wa_urb_enqueue); + +/* + * Dequeue a URB and make sure uwb_hcd_giveback_urb() [completion + * handler] is called. + * + * Until a transfer goes successfully through wa_urb_enqueue() it + * needs to be dequeued with completion calling; when stuck in delayed + * or before wa_xfer_setup() is called, we need to do completion. + * + *  not setup  If there is no hcpriv yet, that means that that enqueue + *             still had no time to set the xfer up. Because + *             urb->status should be other than -EINPROGRESS, + *             enqueue() will catch that and bail out. + * + * If the transfer has gone through setup, we just need to clean it + * up. If it has gone through submit(), we have to abort it [with an + * asynch request] and then make sure we cancel each segment. + * + */ +int wa_urb_dequeue(struct wahc *wa, struct urb *urb) +{ +	struct device *dev = &wa->usb_iface->dev; +	unsigned long flags, flags2; +	struct wa_xfer *xfer; +	struct wa_seg *seg; +	struct wa_rpipe *rpipe; +	unsigned cnt; +	unsigned rpipe_ready = 0; + +	d_fnstart(3, dev, "(wa %p, urb %p)\n", wa, urb); + +	d_printf(1, dev, "xfer %p urb %p: aborting\n", urb->hcpriv, urb); +	xfer = urb->hcpriv; +	if (xfer == NULL) { +		/* NOthing setup yet enqueue will see urb->status != +		 * -EINPROGRESS (by hcd layer) and bail out with +		 * error, no need to do completion +		 */ +		BUG_ON(urb->status == -EINPROGRESS); +		goto out; +	} +	spin_lock_irqsave(&xfer->lock, flags); +	rpipe = xfer->ep->hcpriv; +	/* Check the delayed list -> if there, release and complete */ +	spin_lock_irqsave(&wa->xfer_list_lock, flags2); +	if (!list_empty(&xfer->list_node) && xfer->seg == NULL) +		goto dequeue_delayed; +	spin_unlock_irqrestore(&wa->xfer_list_lock, flags2); +	if (xfer->seg == NULL)  	/* still hasn't reached */ +		goto out_unlock;	/* setup(), enqueue_b() completes */ +	/* Ok, the xfer is in flight already, it's been setup and submitted.*/ +	__wa_xfer_abort(xfer); +	for (cnt = 0; cnt < xfer->segs; cnt++) { +		seg = xfer->seg[cnt]; +		switch (seg->status) { +		case WA_SEG_NOTREADY: +		case WA_SEG_READY: +			printk(KERN_ERR "xfer %p#%u: dequeue bad state %u\n", +			       xfer, cnt, seg->status); +			WARN_ON(1); +			break; +		case WA_SEG_DELAYED: +			seg->status = WA_SEG_ABORTED; +			spin_lock_irqsave(&rpipe->seg_lock, flags2); +			list_del(&seg->list_node); +			xfer->segs_done++; +			rpipe_ready = rpipe_avail_inc(rpipe); +			spin_unlock_irqrestore(&rpipe->seg_lock, flags2); +			break; +		case WA_SEG_SUBMITTED: +			seg->status = WA_SEG_ABORTED; +			usb_unlink_urb(&seg->urb); +			if (xfer->is_inbound == 0) +				usb_unlink_urb(seg->dto_urb); +			xfer->segs_done++; +			rpipe_ready = rpipe_avail_inc(rpipe); +			break; +		case WA_SEG_PENDING: +			seg->status = WA_SEG_ABORTED; +			xfer->segs_done++; +			rpipe_ready = rpipe_avail_inc(rpipe); +			break; +		case WA_SEG_DTI_PENDING: +			usb_unlink_urb(wa->dti_urb); +			seg->status = WA_SEG_ABORTED; +			xfer->segs_done++; +			rpipe_ready = rpipe_avail_inc(rpipe); +			break; +		case WA_SEG_DONE: +		case WA_SEG_ERROR: +		case WA_SEG_ABORTED: +			break; +		} +	} +	xfer->result = urb->status;	/* -ENOENT or -ECONNRESET */ +	__wa_xfer_is_done(xfer); +	spin_unlock_irqrestore(&xfer->lock, flags); +	wa_xfer_completion(xfer); +	if (rpipe_ready) +		wa_xfer_delayed_run(rpipe); +	d_fnend(3, dev, "(wa %p, urb %p) = 0\n", wa, urb); +	return 0; + +out_unlock: +	spin_unlock_irqrestore(&xfer->lock, flags); +out: +	d_fnend(3, dev, "(wa %p, urb %p) = 0\n", wa, urb); +	return 0; + +dequeue_delayed: +	list_del_init(&xfer->list_node); +	spin_unlock_irqrestore(&wa->xfer_list_lock, flags2); +	xfer->result = urb->status; +	spin_unlock_irqrestore(&xfer->lock, flags); +	wa_xfer_giveback(xfer); +	usb_put_urb(urb);		/* we got a ref in enqueue() */ +	d_fnend(3, dev, "(wa %p, urb %p) = 0\n", wa, urb); +	return 0; +} +EXPORT_SYMBOL_GPL(wa_urb_dequeue); + +/* + * Translation from WA status codes (WUSB1.0 Table 8.15) to errno + * codes + * + * Positive errno values are internal inconsistencies and should be + * flagged louder. Negative are to be passed up to the user in the + * normal way. + * + * @status: USB WA status code -- high two bits are stripped. + */ +static int wa_xfer_status_to_errno(u8 status) +{ +	int errno; +	u8 real_status = status; +	static int xlat[] = { +		[WA_XFER_STATUS_SUCCESS] = 		0, +		[WA_XFER_STATUS_HALTED] = 		-EPIPE, +		[WA_XFER_STATUS_DATA_BUFFER_ERROR] = 	-ENOBUFS, +		[WA_XFER_STATUS_BABBLE] = 		-EOVERFLOW, +		[WA_XFER_RESERVED] = 			EINVAL, +		[WA_XFER_STATUS_NOT_FOUND] =		0, +		[WA_XFER_STATUS_INSUFFICIENT_RESOURCE] = -ENOMEM, +		[WA_XFER_STATUS_TRANSACTION_ERROR] = 	-EILSEQ, +		[WA_XFER_STATUS_ABORTED] = 		-EINTR, +		[WA_XFER_STATUS_RPIPE_NOT_READY] = 	EINVAL, +		[WA_XFER_INVALID_FORMAT] = 		EINVAL, +		[WA_XFER_UNEXPECTED_SEGMENT_NUMBER] = 	EINVAL, +		[WA_XFER_STATUS_RPIPE_TYPE_MISMATCH] = 	EINVAL, +	}; +	status &= 0x3f; + +	if (status == 0) +		return 0; +	if (status >= ARRAY_SIZE(xlat)) { +		if (printk_ratelimit()) +			printk(KERN_ERR "%s(): BUG? " +			       "Unknown WA transfer status 0x%02x\n", +			       __func__, real_status); +		return -EINVAL; +	} +	errno = xlat[status]; +	if (unlikely(errno > 0)) { +		if (printk_ratelimit()) +			printk(KERN_ERR "%s(): BUG? " +			       "Inconsistent WA status: 0x%02x\n", +			       __func__, real_status); +		errno = -errno; +	} +	return errno; +} + +/* + * Process a xfer result completion message + * + * inbound transfers: need to schedule a DTI read + * + * FIXME: this functio needs to be broken up in parts + */ +static void wa_xfer_result_chew(struct wahc *wa, struct wa_xfer *xfer) +{ +	int result; +	struct device *dev = &wa->usb_iface->dev; +	unsigned long flags; +	u8 seg_idx; +	struct wa_seg *seg; +	struct wa_rpipe *rpipe; +	struct wa_xfer_result *xfer_result = wa->xfer_result; +	u8 done = 0; +	u8 usb_status; +	unsigned rpipe_ready = 0; + +	d_fnstart(3, dev, "(wa %p xfer %p)\n", wa, xfer); +	spin_lock_irqsave(&xfer->lock, flags); +	seg_idx = xfer_result->bTransferSegment & 0x7f; +	if (unlikely(seg_idx >= xfer->segs)) +		goto error_bad_seg; +	seg = xfer->seg[seg_idx]; +	rpipe = xfer->ep->hcpriv; +	usb_status = xfer_result->bTransferStatus; +	d_printf(2, dev, "xfer %p#%u: bTransferStatus 0x%02x (seg %u)\n", +		 xfer, seg_idx, usb_status, seg->status); +	if (seg->status == WA_SEG_ABORTED +	    || seg->status == WA_SEG_ERROR)	/* already handled */ +		goto segment_aborted; +	if (seg->status == WA_SEG_SUBMITTED)	/* ops, got here */ +		seg->status = WA_SEG_PENDING;	/* before wa_seg{_dto}_cb() */ +	if (seg->status != WA_SEG_PENDING) { +		if (printk_ratelimit()) +			dev_err(dev, "xfer %p#%u: Bad segment state %u\n", +				xfer, seg_idx, seg->status); +		seg->status = WA_SEG_PENDING;	/* workaround/"fix" it */ +	} +	if (usb_status & 0x80) { +		seg->result = wa_xfer_status_to_errno(usb_status); +		dev_err(dev, "DTI: xfer %p#%u failed (0x%02x)\n", +			xfer, seg->index, usb_status); +		goto error_complete; +	} +	/* FIXME: we ignore warnings, tally them for stats */ +	if (usb_status & 0x40) 		/* Warning?... */ +		usb_status = 0;		/* ... pass */ +	if (xfer->is_inbound) {	/* IN data phase: read to buffer */ +		seg->status = WA_SEG_DTI_PENDING; +		BUG_ON(wa->buf_in_urb->status == -EINPROGRESS); +		if (xfer->is_dma) { +			wa->buf_in_urb->transfer_dma = +				xfer->urb->transfer_dma +				+ seg_idx * xfer->seg_size; +			wa->buf_in_urb->transfer_flags +				|= URB_NO_TRANSFER_DMA_MAP; +		} else { +			wa->buf_in_urb->transfer_buffer = +				xfer->urb->transfer_buffer +				+ seg_idx * xfer->seg_size; +			wa->buf_in_urb->transfer_flags +				&= ~URB_NO_TRANSFER_DMA_MAP; +		} +		wa->buf_in_urb->transfer_buffer_length = +			le32_to_cpu(xfer_result->dwTransferLength); +		wa->buf_in_urb->context = seg; +		result = usb_submit_urb(wa->buf_in_urb, GFP_ATOMIC); +		if (result < 0) +			goto error_submit_buf_in; +	} else { +		/* OUT data phase, complete it -- */ +		seg->status = WA_SEG_DONE; +		seg->result = le32_to_cpu(xfer_result->dwTransferLength); +		xfer->segs_done++; +		rpipe_ready = rpipe_avail_inc(rpipe); +		done = __wa_xfer_is_done(xfer); +	} +	spin_unlock_irqrestore(&xfer->lock, flags); +	if (done) +		wa_xfer_completion(xfer); +	if (rpipe_ready) +		wa_xfer_delayed_run(rpipe); +	d_fnend(3, dev, "(wa %p xfer %p) = void\n", wa, xfer); +	return; + + +error_submit_buf_in: +	if (edc_inc(&wa->dti_edc, EDC_MAX_ERRORS, EDC_ERROR_TIMEFRAME)) { +		dev_err(dev, "DTI: URB max acceptable errors " +			"exceeded, resetting device\n"); +		wa_reset_all(wa); +	} +	if (printk_ratelimit()) +		dev_err(dev, "xfer %p#%u: can't submit DTI data phase: %d\n", +			xfer, seg_idx, result); +	seg->result = result; +error_complete: +	seg->status = WA_SEG_ERROR; +	xfer->segs_done++; +	rpipe_ready = rpipe_avail_inc(rpipe); +	__wa_xfer_abort(xfer); +	done = __wa_xfer_is_done(xfer); +	spin_unlock_irqrestore(&xfer->lock, flags); +	if (done) +		wa_xfer_completion(xfer); +	if (rpipe_ready) +		wa_xfer_delayed_run(rpipe); +	d_fnend(3, dev, "(wa %p xfer %p) = void [segment/DTI-submit error]\n", +		wa, xfer); +	return; + + +error_bad_seg: +	spin_unlock_irqrestore(&xfer->lock, flags); +	wa_urb_dequeue(wa, xfer->urb); +	if (printk_ratelimit()) +		dev_err(dev, "xfer %p#%u: bad segment\n", xfer, seg_idx); +	if (edc_inc(&wa->dti_edc, EDC_MAX_ERRORS, EDC_ERROR_TIMEFRAME)) { +		dev_err(dev, "DTI: URB max acceptable errors " +			"exceeded, resetting device\n"); +		wa_reset_all(wa); +	} +	d_fnend(3, dev, "(wa %p xfer %p) = void [bad seg]\n", wa, xfer); +	return; + + +segment_aborted: +	/* nothing to do, as the aborter did the completion */ +	spin_unlock_irqrestore(&xfer->lock, flags); +	d_fnend(3, dev, "(wa %p xfer %p) = void [segment aborted]\n", +		wa, xfer); +	return; + +} + +/* + * Callback for the IN data phase + * + * If succesful transition state; otherwise, take a note of the + * error, mark this segment done and try completion. + * + * Note we don't access until we are sure that the transfer hasn't + * been cancelled (ECONNRESET, ENOENT), which could mean that + * seg->xfer could be already gone. + */ +static void wa_buf_in_cb(struct urb *urb) +{ +	struct wa_seg *seg = urb->context; +	struct wa_xfer *xfer = seg->xfer; +	struct wahc *wa; +	struct device *dev; +	struct wa_rpipe *rpipe; +	unsigned rpipe_ready; +	unsigned long flags; +	u8 done = 0; + +	d_fnstart(3, NULL, "(urb %p [%d])\n", urb, urb->status); +	switch (urb->status) { +	case 0: +		spin_lock_irqsave(&xfer->lock, flags); +		wa = xfer->wa; +		dev = &wa->usb_iface->dev; +		rpipe = xfer->ep->hcpriv; +		d_printf(2, dev, "xfer %p#%u: data in done (%zu bytes)\n", +			   xfer, seg->index, (size_t)urb->actual_length); +		seg->status = WA_SEG_DONE; +		seg->result = urb->actual_length; +		xfer->segs_done++; +		rpipe_ready = rpipe_avail_inc(rpipe); +		done = __wa_xfer_is_done(xfer); +		spin_unlock_irqrestore(&xfer->lock, flags); +		if (done) +			wa_xfer_completion(xfer); +		if (rpipe_ready) +			wa_xfer_delayed_run(rpipe); +		break; +	case -ECONNRESET:	/* URB unlinked; no need to do anything */ +	case -ENOENT:		/* as it was done by the who unlinked us */ +		break; +	default:		/* Other errors ... */ +		spin_lock_irqsave(&xfer->lock, flags); +		wa = xfer->wa; +		dev = &wa->usb_iface->dev; +		rpipe = xfer->ep->hcpriv; +		if (printk_ratelimit()) +			dev_err(dev, "xfer %p#%u: data in error %d\n", +				xfer, seg->index, urb->status); +		if (edc_inc(&wa->nep_edc, EDC_MAX_ERRORS, +			    EDC_ERROR_TIMEFRAME)){ +			dev_err(dev, "DTO: URB max acceptable errors " +				"exceeded, resetting device\n"); +			wa_reset_all(wa); +		} +		seg->status = WA_SEG_ERROR; +		seg->result = urb->status; +		xfer->segs_done++; +		rpipe_ready = rpipe_avail_inc(rpipe); +		__wa_xfer_abort(xfer); +		done = __wa_xfer_is_done(xfer); +		spin_unlock_irqrestore(&xfer->lock, flags); +		if (done) +			wa_xfer_completion(xfer); +		if (rpipe_ready) +			wa_xfer_delayed_run(rpipe); +	} +	d_fnend(3, NULL, "(urb %p [%d]) = void\n", urb, urb->status); +} + +/* + * Handle an incoming transfer result buffer + * + * Given a transfer result buffer, it completes the transfer (possibly + * scheduling and buffer in read) and then resubmits the DTI URB for a + * new transfer result read. + * + * + * The xfer_result DTI URB state machine + * + * States: OFF | RXR (Read-Xfer-Result) | RBI (Read-Buffer-In) + * + * We start in OFF mode, the first xfer_result notification [through + * wa_handle_notif_xfer()] moves us to RXR by posting the DTI-URB to + * read. + * + * We receive a buffer -- if it is not a xfer_result, we complain and + * repost the DTI-URB. If it is a xfer_result then do the xfer seg + * request accounting. If it is an IN segment, we move to RBI and post + * a BUF-IN-URB to the right buffer. The BUF-IN-URB callback will + * repost the DTI-URB and move to RXR state. if there was no IN + * segment, it will repost the DTI-URB. + * + * We go back to OFF when we detect a ENOENT or ESHUTDOWN (or too many + * errors) in the URBs. + */ +static void wa_xfer_result_cb(struct urb *urb) +{ +	int result; +	struct wahc *wa = urb->context; +	struct device *dev = &wa->usb_iface->dev; +	struct wa_xfer_result *xfer_result; +	u32 xfer_id; +	struct wa_xfer *xfer; +	u8 usb_status; + +	d_fnstart(3, dev, "(%p)\n", wa); +	BUG_ON(wa->dti_urb != urb); +	switch (wa->dti_urb->status) { +	case 0: +		/* We have a xfer result buffer; check it */ +		d_printf(2, dev, "DTI: xfer result %d bytes at %p\n", +			   urb->actual_length, urb->transfer_buffer); +		d_dump(3, dev, urb->transfer_buffer, urb->actual_length); +		if (wa->dti_urb->actual_length != sizeof(*xfer_result)) { +			dev_err(dev, "DTI Error: xfer result--bad size " +				"xfer result (%d bytes vs %zu needed)\n", +				urb->actual_length, sizeof(*xfer_result)); +			break; +		} +		xfer_result = wa->xfer_result; +		if (xfer_result->hdr.bLength != sizeof(*xfer_result)) { +			dev_err(dev, "DTI Error: xfer result--" +				"bad header length %u\n", +				xfer_result->hdr.bLength); +			break; +		} +		if (xfer_result->hdr.bNotifyType != WA_XFER_RESULT) { +			dev_err(dev, "DTI Error: xfer result--" +				"bad header type 0x%02x\n", +				xfer_result->hdr.bNotifyType); +			break; +		} +		usb_status = xfer_result->bTransferStatus & 0x3f; +		if (usb_status == WA_XFER_STATUS_ABORTED +		    || usb_status == WA_XFER_STATUS_NOT_FOUND) +			/* taken care of already */ +			break; +		xfer_id = xfer_result->dwTransferID; +		xfer = wa_xfer_get_by_id(wa, xfer_id); +		if (xfer == NULL) { +			/* FIXME: transaction might have been cancelled */ +			dev_err(dev, "DTI Error: xfer result--" +				"unknown xfer 0x%08x (status 0x%02x)\n", +				xfer_id, usb_status); +			break; +		} +		wa_xfer_result_chew(wa, xfer); +		wa_xfer_put(xfer); +		break; +	case -ENOENT:		/* (we killed the URB)...so, no broadcast */ +	case -ESHUTDOWN:	/* going away! */ +		dev_dbg(dev, "DTI: going down! %d\n", urb->status); +		goto out; +	default: +		/* Unknown error */ +		if (edc_inc(&wa->dti_edc, EDC_MAX_ERRORS, +			    EDC_ERROR_TIMEFRAME)) { +			dev_err(dev, "DTI: URB max acceptable errors " +				"exceeded, resetting device\n"); +			wa_reset_all(wa); +			goto out; +		} +		if (printk_ratelimit()) +			dev_err(dev, "DTI: URB error %d\n", urb->status); +		break; +	} +	/* Resubmit the DTI URB */ +	result = usb_submit_urb(wa->dti_urb, GFP_ATOMIC); +	if (result < 0) { +		dev_err(dev, "DTI Error: Could not submit DTI URB (%d), " +			"resetting\n", result); +		wa_reset_all(wa); +	} +out: +	d_fnend(3, dev, "(%p) = void\n", wa); +	return; +} + +/* + * Transfer complete notification + * + * Called from the notif.c code. We get a notification on EP2 saying + * that some endpoint has some transfer result data available. We are + * about to read it. + * + * To speed up things, we always have a URB reading the DTI URB; we + * don't really set it up and start it until the first xfer complete + * notification arrives, which is what we do here. + * + * Follow up in wa_xfer_result_cb(), as that's where the whole state + * machine starts. + * + * So here we just initialize the DTI URB for reading transfer result + * notifications and also the buffer-in URB, for reading buffers. Then + * we just submit the DTI URB. + * + * @wa shall be referenced + */ +void wa_handle_notif_xfer(struct wahc *wa, struct wa_notif_hdr *notif_hdr) +{ +	int result; +	struct device *dev = &wa->usb_iface->dev; +	struct wa_notif_xfer *notif_xfer; +	const struct usb_endpoint_descriptor *dti_epd = wa->dti_epd; + +	d_fnstart(4, dev, "(%p, %p)\n", wa, notif_hdr); +	notif_xfer = container_of(notif_hdr, struct wa_notif_xfer, hdr); +	BUG_ON(notif_hdr->bNotifyType != WA_NOTIF_TRANSFER); + +	if ((0x80 | notif_xfer->bEndpoint) != dti_epd->bEndpointAddress) { +		/* FIXME: hardcoded limitation, adapt */ +		dev_err(dev, "BUG: DTI ep is %u, not %u (hack me)\n", +			notif_xfer->bEndpoint, dti_epd->bEndpointAddress); +		goto error; +	} +	if (wa->dti_urb != NULL)	/* DTI URB already started */ +		goto out; + +	wa->dti_urb = usb_alloc_urb(0, GFP_KERNEL); +	if (wa->dti_urb == NULL) { +		dev_err(dev, "Can't allocate DTI URB\n"); +		goto error_dti_urb_alloc; +	} +	usb_fill_bulk_urb( +		wa->dti_urb, wa->usb_dev, +		usb_rcvbulkpipe(wa->usb_dev, 0x80 | notif_xfer->bEndpoint), +		wa->xfer_result, wa->xfer_result_size, +		wa_xfer_result_cb, wa); + +	wa->buf_in_urb = usb_alloc_urb(0, GFP_KERNEL); +	if (wa->buf_in_urb == NULL) { +		dev_err(dev, "Can't allocate BUF-IN URB\n"); +		goto error_buf_in_urb_alloc; +	} +	usb_fill_bulk_urb( +		wa->buf_in_urb, wa->usb_dev, +		usb_rcvbulkpipe(wa->usb_dev, 0x80 | notif_xfer->bEndpoint), +		NULL, 0, wa_buf_in_cb, wa); +	result = usb_submit_urb(wa->dti_urb, GFP_KERNEL); +	if (result < 0) { +		dev_err(dev, "DTI Error: Could not submit DTI URB (%d), " +			"resetting\n", result); +		goto error_dti_urb_submit; +	} +out: +	d_fnend(4, dev, "(%p, %p) = void\n", wa, notif_hdr); +	return; + +error_dti_urb_submit: +	usb_put_urb(wa->buf_in_urb); +error_buf_in_urb_alloc: +	usb_put_urb(wa->dti_urb); +	wa->dti_urb = NULL; +error_dti_urb_alloc: +error: +	wa_reset_all(wa); +	d_fnend(4, dev, "(%p, %p) = void\n", wa, notif_hdr); +	return; +} diff --git a/drivers/usb/wusbcore/wusbhc.c b/drivers/usb/wusbcore/wusbhc.c new file mode 100644 index 00000000000..07c63a31c79 --- /dev/null +++ b/drivers/usb/wusbcore/wusbhc.c @@ -0,0 +1,418 @@ +/* + * Wireless USB Host Controller + * sysfs glue, wusbcore module support and life cycle management + * + * + * Copyright (C) 2005-2006 Intel Corporation + * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * + * Creation/destruction of wusbhc is split in two parts; that that + * doesn't require the HCD to be added (wusbhc_{create,destroy}) and + * the one that requires (phase B, wusbhc_b_{create,destroy}). + * + * This is so because usb_add_hcd() will start the HC, and thus, all + * the HC specific stuff has to be already initialiazed (like sysfs + * thingies). + */ +#include <linux/device.h> +#include <linux/module.h> +#include "wusbhc.h" + +/** + * Extract the wusbhc that corresponds to a USB Host Controller class device + * + * WARNING! Apply only if @dev is that of a + *          wusbhc.usb_hcd.self->class_dev; otherwise, you loose. + */ +static struct wusbhc *usbhc_dev_to_wusbhc(struct device *dev) +{ +	struct usb_bus *usb_bus = dev_get_drvdata(dev); +	struct usb_hcd *usb_hcd = bus_to_hcd(usb_bus); +	return usb_hcd_to_wusbhc(usb_hcd); +} + +/* + * Show & store the current WUSB trust timeout + * + * We don't do locking--it is an 'atomic' value. + * + * The units that we store/show are always MILLISECONDS. However, the + * value of trust_timeout is jiffies. + */ +static ssize_t wusb_trust_timeout_show(struct device *dev, +				       struct device_attribute *attr, char *buf) +{ +	struct wusbhc *wusbhc = usbhc_dev_to_wusbhc(dev); + +	return scnprintf(buf, PAGE_SIZE, "%u\n", wusbhc->trust_timeout); +} + +static ssize_t wusb_trust_timeout_store(struct device *dev, +					struct device_attribute *attr, +					const char *buf, size_t size) +{ +	struct wusbhc *wusbhc = usbhc_dev_to_wusbhc(dev); +	ssize_t result = -ENOSYS; +	unsigned trust_timeout; + +	result = sscanf(buf, "%u", &trust_timeout); +	if (result != 1) { +		result = -EINVAL; +		goto out; +	} +	/* FIXME: maybe we should check for range validity? */ +	wusbhc->trust_timeout = trust_timeout; +	cancel_delayed_work(&wusbhc->keep_alive_timer); +	flush_workqueue(wusbd); +	queue_delayed_work(wusbd, &wusbhc->keep_alive_timer, +			   (trust_timeout * CONFIG_HZ)/1000/2); +out: +	return result < 0 ? result : size; +} +static DEVICE_ATTR(wusb_trust_timeout, 0644, wusb_trust_timeout_show, +					     wusb_trust_timeout_store); + +/* + * Show & store the current WUSB CHID + */ +static ssize_t wusb_chid_show(struct device *dev, +			      struct device_attribute *attr, char *buf) +{ +	struct wusbhc *wusbhc = usbhc_dev_to_wusbhc(dev); +	ssize_t result = 0; + +	if (wusbhc->wuie_host_info != NULL) +		result += ckhdid_printf(buf, PAGE_SIZE, +					&wusbhc->wuie_host_info->CHID); +	return result; +} + +/* + * Store a new CHID + * + * This will (FIXME) trigger many changes. + * + * - Send an all zeros CHID and it will stop the controller + * - Send a non-zero CHID and it will start it + *   (unless it was started, it will just change the CHID, + *   diconnecting all devices first). + * + * So first we scan the MMC we are sent and then we act on it.  We + * read it in the same format as we print it, an ASCII string of 16 + * hex bytes. + * + * See wusbhc_chid_set() for more info. + */ +static ssize_t wusb_chid_store(struct device *dev, +			       struct device_attribute *attr, +			       const char *buf, size_t size) +{ +	struct wusbhc *wusbhc = usbhc_dev_to_wusbhc(dev); +	struct wusb_ckhdid chid; +	ssize_t result; + +	result = sscanf(buf, +			"%02hhx %02hhx %02hhx %02hhx " +			"%02hhx %02hhx %02hhx %02hhx " +			"%02hhx %02hhx %02hhx %02hhx " +			"%02hhx %02hhx %02hhx %02hhx\n", +			&chid.data[0] , &chid.data[1] , +			&chid.data[2] , &chid.data[3] , +			&chid.data[4] , &chid.data[5] , +			&chid.data[6] , &chid.data[7] , +			&chid.data[8] , &chid.data[9] , +			&chid.data[10], &chid.data[11], +			&chid.data[12], &chid.data[13], +			&chid.data[14], &chid.data[15]); +	if (result != 16) { +		dev_err(dev, "Unrecognized CHID (need 16 8-bit hex digits): " +			"%d\n", (int)result); +		return -EINVAL; +	} +	result = wusbhc_chid_set(wusbhc, &chid); +	return result < 0 ? result : size; +} +static DEVICE_ATTR(wusb_chid, 0644, wusb_chid_show, wusb_chid_store); + +/* Group all the WUSBHC attributes */ +static struct attribute *wusbhc_attrs[] = { +		&dev_attr_wusb_trust_timeout.attr, +		&dev_attr_wusb_chid.attr, +		NULL, +}; + +static struct attribute_group wusbhc_attr_group = { +	.name = NULL,	/* we want them in the same directory */ +	.attrs = wusbhc_attrs, +}; + +/* + * Create a wusbhc instance + * + * NOTEs: + * + *  - assumes *wusbhc has been zeroed and wusbhc->usb_hcd has been + *    initialized but not added. + * + *  - fill out ports_max, mmcies_max and mmcie_{add,rm} before calling. + * + *  - fill out wusbhc->uwb_rc and refcount it before calling + *  - fill out the wusbhc->sec_modes array + */ +int wusbhc_create(struct wusbhc *wusbhc) +{ +	int result = 0; + +	wusbhc->trust_timeout = WUSB_TRUST_TIMEOUT_MS; +	mutex_init(&wusbhc->mutex); +	result = wusbhc_mmcie_create(wusbhc); +	if (result < 0) +		goto error_mmcie_create; +	result = wusbhc_devconnect_create(wusbhc); +	if (result < 0) +		goto error_devconnect_create; +	result = wusbhc_rh_create(wusbhc); +	if (result < 0) +		goto error_rh_create; +	result = wusbhc_sec_create(wusbhc); +	if (result < 0) +		goto error_sec_create; +	return 0; + +error_sec_create: +	wusbhc_rh_destroy(wusbhc); +error_rh_create: +	wusbhc_devconnect_destroy(wusbhc); +error_devconnect_create: +	wusbhc_mmcie_destroy(wusbhc); +error_mmcie_create: +	return result; +} +EXPORT_SYMBOL_GPL(wusbhc_create); + +static inline struct kobject *wusbhc_kobj(struct wusbhc *wusbhc) +{ +	return &wusbhc->usb_hcd.self.controller->kobj; +} + +/* + * Phase B of a wusbhc instance creation + * + * Creates fields that depend on wusbhc->usb_hcd having been + * added. This is where we create the sysfs files in + * /sys/class/usb_host/usb_hostX/. + * + * NOTE: Assumes wusbhc->usb_hcd has been already added by the upper + *       layer (hwahc or whci) + */ +int wusbhc_b_create(struct wusbhc *wusbhc) +{ +	int result = 0; +	struct device *dev = wusbhc->usb_hcd.self.controller; + +	result = sysfs_create_group(wusbhc_kobj(wusbhc), &wusbhc_attr_group); +	if (result < 0) { +		dev_err(dev, "Cannot register WUSBHC attributes: %d\n", result); +		goto error_create_attr_group; +	} + +	result = wusbhc_pal_register(wusbhc); +	if (result < 0) +		goto error_pal_register; +	return 0; + +error_pal_register: +	sysfs_remove_group(wusbhc_kobj(wusbhc), &wusbhc_attr_group); +error_create_attr_group: +	return result; +} +EXPORT_SYMBOL_GPL(wusbhc_b_create); + +void wusbhc_b_destroy(struct wusbhc *wusbhc) +{ +	wusbhc_pal_unregister(wusbhc); +	sysfs_remove_group(wusbhc_kobj(wusbhc), &wusbhc_attr_group); +} +EXPORT_SYMBOL_GPL(wusbhc_b_destroy); + +void wusbhc_destroy(struct wusbhc *wusbhc) +{ +	wusbhc_sec_destroy(wusbhc); +	wusbhc_rh_destroy(wusbhc); +	wusbhc_devconnect_destroy(wusbhc); +	wusbhc_mmcie_destroy(wusbhc); +} +EXPORT_SYMBOL_GPL(wusbhc_destroy); + +struct workqueue_struct *wusbd; +EXPORT_SYMBOL_GPL(wusbd); + +/* + * WUSB Cluster ID allocation map + * + * Each WUSB bus in a channel is identified with a Cluster Id in the + * unauth address pace (WUSB1.0[4.3]). We take the range 0xe0 to 0xff + * (that's space for 31 WUSB controllers, as 0xff can't be taken). We + * start taking from 0xff, 0xfe, 0xfd... (hence the += or -= 0xff). + * + * For each one we taken, we pin it in the bitap + */ +#define CLUSTER_IDS 32 +static DECLARE_BITMAP(wusb_cluster_id_table, CLUSTER_IDS); +static DEFINE_SPINLOCK(wusb_cluster_ids_lock); + +/* + * Get a WUSB Cluster ID + * + * Need to release with wusb_cluster_id_put() when done w/ it. + */ +/* FIXME: coordinate with the choose_addres() from the USB stack */ +/* we want to leave the top of the 128 range for cluster addresses and + * the bottom for device addresses (as we map them one on one with + * ports). */ +u8 wusb_cluster_id_get(void) +{ +	u8 id; +	spin_lock(&wusb_cluster_ids_lock); +	id = find_first_zero_bit(wusb_cluster_id_table, CLUSTER_IDS); +	if (id > CLUSTER_IDS) { +		id = 0; +		goto out; +	} +	set_bit(id, wusb_cluster_id_table); +	id = (u8) 0xff - id; +out: +	spin_unlock(&wusb_cluster_ids_lock); +	return id; + +} +EXPORT_SYMBOL_GPL(wusb_cluster_id_get); + +/* + * Release a WUSB Cluster ID + * + * Obtained it with wusb_cluster_id_get() + */ +void wusb_cluster_id_put(u8 id) +{ +	id = 0xff - id; +	BUG_ON(id >= CLUSTER_IDS); +	spin_lock(&wusb_cluster_ids_lock); +	WARN_ON(!test_bit(id, wusb_cluster_id_table)); +	clear_bit(id, wusb_cluster_id_table); +	spin_unlock(&wusb_cluster_ids_lock); +} +EXPORT_SYMBOL_GPL(wusb_cluster_id_put); + +/** + * wusbhc_giveback_urb - return an URB to the USB core + * @wusbhc: the host controller the URB is from. + * @urb:    the URB. + * @status: the URB's status. + * + * Return an URB to the USB core doing some additional WUSB specific + * processing. + * + *  - After a successful transfer, update the trust timeout timestamp + *    for the WUSB device. + * + *  - [WUSB] sections 4.13 and 7.5.1 specifies the stop retrasmittion + *    condition for the WCONNECTACK_IE is that the host has observed + *    the associated device responding to a control transfer. + */ +void wusbhc_giveback_urb(struct wusbhc *wusbhc, struct urb *urb, int status) +{ +	struct wusb_dev *wusb_dev = __wusb_dev_get_by_usb_dev(wusbhc, urb->dev); + +	if (status == 0) { +		wusb_dev->entry_ts = jiffies; + +		/* wusbhc_devconnect_acked() can't be called from from +		   atomic context so defer it to a work queue. */ +		if (!list_empty(&wusb_dev->cack_node)) +			queue_work(wusbd, &wusb_dev->devconnect_acked_work); +	} + +	usb_hcd_giveback_urb(&wusbhc->usb_hcd, urb, status); +} +EXPORT_SYMBOL_GPL(wusbhc_giveback_urb); + +/** + * wusbhc_reset_all - reset the HC hardware + * @wusbhc: the host controller to reset. + * + * Request a full hardware reset of the chip.  This will also reset + * the radio controller and any other PALs. + */ +void wusbhc_reset_all(struct wusbhc *wusbhc) +{ +	uwb_rc_reset_all(wusbhc->uwb_rc); +} +EXPORT_SYMBOL_GPL(wusbhc_reset_all); + +static struct notifier_block wusb_usb_notifier = { +	.notifier_call = wusb_usb_ncb, +	.priority = INT_MAX	/* Need to be called first of all */ +}; + +static int __init wusbcore_init(void) +{ +	int result; +	result = wusb_crypto_init(); +	if (result < 0) +		goto error_crypto_init; +	/* WQ is singlethread because we need to serialize notifications */ +	wusbd = create_singlethread_workqueue("wusbd"); +	if (wusbd == NULL) { +		result = -ENOMEM; +		printk(KERN_ERR "WUSB-core: Cannot create wusbd workqueue\n"); +		goto error_wusbd_create; +	} +	usb_register_notify(&wusb_usb_notifier); +	bitmap_zero(wusb_cluster_id_table, CLUSTER_IDS); +	set_bit(0, wusb_cluster_id_table);	/* reserve Cluster ID 0xff */ +	return 0; + +error_wusbd_create: +	wusb_crypto_exit(); +error_crypto_init: +	return result; + +} +module_init(wusbcore_init); + +static void __exit wusbcore_exit(void) +{ +	clear_bit(0, wusb_cluster_id_table); +	if (!bitmap_empty(wusb_cluster_id_table, CLUSTER_IDS)) { +		char buf[256]; +		bitmap_scnprintf(buf, sizeof(buf), wusb_cluster_id_table, +				 CLUSTER_IDS); +		printk(KERN_ERR "BUG: WUSB Cluster IDs not released " +		       "on exit: %s\n", buf); +		WARN_ON(1); +	} +	usb_unregister_notify(&wusb_usb_notifier); +	destroy_workqueue(wusbd); +	wusb_crypto_exit(); +} +module_exit(wusbcore_exit); + +MODULE_AUTHOR("Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>"); +MODULE_DESCRIPTION("Wireless USB core"); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/wusbcore/wusbhc.h b/drivers/usb/wusbcore/wusbhc.h new file mode 100644 index 00000000000..d0c132434f1 --- /dev/null +++ b/drivers/usb/wusbcore/wusbhc.h @@ -0,0 +1,495 @@ +/* + * Wireless USB Host Controller + * Common infrastructure for WHCI and HWA WUSB-HC drivers + * + * + * Copyright (C) 2005-2006 Intel Corporation + * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * + * This driver implements parts common to all Wireless USB Host + * Controllers (struct wusbhc, embedding a struct usb_hcd) and is used + * by: + * + *   - hwahc: HWA, USB-dongle that implements a Wireless USB host + *     controller, (Wireless USB 1.0 Host-Wire-Adapter specification). + * + *   - whci: WHCI, a PCI card with a wireless host controller + *     (Wireless Host Controller Interface 1.0 specification). + * + * Check out the Design-overview.txt file in the source documentation + * for other details on the implementation. + * + * Main blocks: + * + *  rh         Root Hub emulation (part of the HCD glue) + * + *  devconnect Handle all the issues related to device connection, + *             authentication, disconnection, timeout, reseting, + *             keepalives, etc. + * + *  mmc        MMC IE broadcasting handling + * + * A host controller driver just initializes its stuff and as part of + * that, creates a 'struct wusbhc' instance that handles all the + * common WUSB mechanisms. Links in the function ops that are specific + * to it and then registers the host controller. Ready to run. + */ + +#ifndef __WUSBHC_H__ +#define __WUSBHC_H__ + +#include <linux/usb.h> +#include <linux/list.h> +#include <linux/mutex.h> +#include <linux/kref.h> +#include <linux/workqueue.h> +/* FIXME: Yes, I know: BAD--it's not my fault the USB HC iface is not + *        public */ +#include <linux/../../drivers/usb/core/hcd.h> +#include <linux/uwb.h> +#include <linux/usb/wusb.h> + + +/** + * Wireless USB device + * + * Describe a WUSB device connected to the cluster. This struct + * belongs to the 'struct wusb_port' it is attached to and it is + * responsible for putting and clearing the pointer to it. + * + * Note this "complements" the 'struct usb_device' that the usb_hcd + * keeps for each connected USB device. However, it extends some + * information that is not available (there is no hcpriv ptr in it!) + * *and* most importantly, it's life cycle is different. It is created + * as soon as we get a DN_Connect (connect request notification) from + * the device through the WUSB host controller; the USB stack doesn't + * create the device until we authenticate it. FIXME: this will + * change. + * + * @bos:    This is allocated when the BOS descriptors are read from + *          the device and freed upon the wusb_dev struct dying. + * @wusb_cap_descr: points into @bos, and has been verified to be size + *                  safe. + */ +struct wusb_dev { +	struct kref refcnt; +	struct wusbhc *wusbhc; +	struct list_head cack_node;	/* Connect-Ack list */ +	u8 port_idx; +	u8 addr; +	u8 beacon_type:4; +	struct usb_encryption_descriptor ccm1_etd; +	struct wusb_ckhdid cdid; +	unsigned long entry_ts; +	struct usb_bos_descriptor *bos; +	struct usb_wireless_cap_descriptor *wusb_cap_descr; +	struct uwb_mas_bm availability; +	struct work_struct devconnect_acked_work; +	struct urb *set_gtk_urb; +	struct usb_ctrlrequest *set_gtk_req; +	struct usb_device *usb_dev; +}; + +#define WUSB_DEV_ADDR_UNAUTH 0x80 + +static inline void wusb_dev_init(struct wusb_dev *wusb_dev) +{ +	kref_init(&wusb_dev->refcnt); +	/* no need to init the cack_node */ +} + +extern void wusb_dev_destroy(struct kref *_wusb_dev); + +static inline struct wusb_dev *wusb_dev_get(struct wusb_dev *wusb_dev) +{ +	kref_get(&wusb_dev->refcnt); +	return wusb_dev; +} + +static inline void wusb_dev_put(struct wusb_dev *wusb_dev) +{ +	kref_put(&wusb_dev->refcnt, wusb_dev_destroy); +} + +/** + * Wireless USB Host Controlller root hub "fake" ports + * (state and device information) + * + * Wireless USB is wireless, so there are no ports; but we + * fake'em. Each RC can connect a max of devices at the same time + * (given in the Wireless Adapter descriptor, bNumPorts or WHCI's + * caps), referred to in wusbhc->ports_max. + * + * See rh.c for more information. + * + * The @status and @change use the same bits as in USB2.0[11.24.2.7], + * so we don't have to do much when getting the port's status. + * + * WUSB1.0[7.1], USB2.0[11.24.2.7.1,fig 11-10], + * include/linux/usb_ch9.h (#define USB_PORT_STAT_*) + */ +struct wusb_port { +	u16 status; +	u16 change; +	struct wusb_dev *wusb_dev;	/* connected device's info */ +	unsigned reset_count; +	u32 ptk_tkid; +}; + +/** + * WUSB Host Controller specifics + * + * All fields that are common to all Wireless USB controller types + * (HWA and WHCI) are grouped here. Host Controller + * functions/operations that only deal with general Wireless USB HC + * issues use this data type to refer to the host. + * + * @usb_hcd 	   Instantiation of a USB host controller + *                 (initialized by upper layer [HWA=HC or WHCI]. + * + * @dev		   Device that implements this; initialized by the + *                 upper layer (HWA-HC, WHCI...); this device should + *                 have a refcount. + * + * @trust_timeout  After this time without hearing for device + *                 activity, we consider the device gone and we have to + *                 re-authenticate. + * + *                 Can be accessed w/o locking--however, read to a + *                 local variable then use. + * + * @chid           WUSB Cluster Host ID: this is supposed to be a + *                 unique value that doesn't change across reboots (so + *                 that your devices do not require re-association). + * + *                 Read/Write protected by @mutex + * + * @dev_info       This array has ports_max elements. It is used to + *                 give the HC information about the WUSB devices (see + *                 'struct wusb_dev_info'). + * + *	           For HWA we need to allocate it in heap; for WHCI it + *                 needs to be permanently mapped, so we keep it for + *                 both and make it easy. Call wusbhc->dev_info_set() + *                 to update an entry. + * + * @ports_max	   Number of simultaneous device connections (fake + *                 ports) this HC will take. Read-only. + * + * @port      	   Array of port status for each fake root port. Guaranteed to + *                 always be the same lenght during device existence + *                 [this allows for some unlocked but referenced reading]. + * + * @mmcies_max	   Max number of Information Elements this HC can send + *                 in its MMC. Read-only. + * + * @mmcie_add	   HC specific operation (WHCI or HWA) for adding an + *                 MMCIE. + * + * @mmcie_rm	   HC specific operation (WHCI or HWA) for removing an + *                 MMCIE. + * + * @enc_types	   Array which describes the encryptions methods + *                 supported by the host as described in WUSB1.0 -- + *                 one entry per supported method. As of WUSB1.0 there + *                 is only four methods, we make space for eight just in + *                 case they decide to add some more (and pray they do + *                 it in sequential order). if 'enc_types[enc_method] + *                 != 0', then it is supported by the host. enc_method + *                 is USB_ENC_TYPE*. + * + * @set_ptk:       Set the PTK and enable encryption for a device. Or, if + *                 the supplied key is NULL, disable encryption for that + *                 device. + * + * @set_gtk:       Set the GTK to be used for all future broadcast packets + *                 (i.e., MMCs).  With some hardware, setting the GTK may start + *                 MMC transmission. + * + * NOTE: + * + *  - If wusb_dev->usb_dev is not NULL, then usb_dev is valid + *    (wusb_dev has a refcount on it). Likewise, if usb_dev->wusb_dev + *    is not NULL, usb_dev->wusb_dev is valid (usb_dev keeps a + *    refcount on it). + * + *    Most of the times when you need to use it, it will be non-NULL, + *    so there is no real need to check for it (wusb_dev will + *    dissapear before usb_dev). + * + *  - The following fields need to be filled out before calling + *    wusbhc_create(): ports_max, mmcies_max, mmcie_{add,rm}. + * + *  - there is no wusbhc_init() method, we do everything in + *    wusbhc_create(). + * + *  - Creation is done in two phases, wusbhc_create() and + *    wusbhc_create_b(); b are the parts that need to be called after + *    calling usb_hcd_add(&wusbhc->usb_hcd). + */ +struct wusbhc { +	struct usb_hcd usb_hcd;		/* HAS TO BE 1st */ +	struct device *dev; +	struct uwb_rc *uwb_rc; +	struct uwb_pal pal; + +	unsigned trust_timeout;			/* in jiffies */ +	struct wuie_host_info *wuie_host_info;	/* Includes CHID */ + +	struct mutex mutex;			/* locks everything else */ +	u16 cluster_id;				/* Wireless USB Cluster ID */ +	struct wusb_port *port;			/* Fake port status handling */ +	struct wusb_dev_info *dev_info;		/* for Set Device Info mgmt */ +	u8 ports_max; +	unsigned active:1;			/* currently xmit'ing MMCs */ +	struct wuie_keep_alive keep_alive_ie;	/* protected by mutex */ +	struct delayed_work keep_alive_timer; +	struct list_head cack_list;		/* Connect acknowledging */ +	size_t cack_count;			/* protected by 'mutex' */ +	struct wuie_connect_ack cack_ie; +	struct uwb_rsv *rsv;		/* cluster bandwidth reservation */ + +	struct mutex mmcie_mutex;		/* MMC WUIE handling */ +	struct wuie_hdr **mmcie;		/* WUIE array */ +	u8 mmcies_max; +	/* FIXME: make wusbhc_ops? */ +	int (*start)(struct wusbhc *wusbhc); +	void (*stop)(struct wusbhc *wusbhc); +	int (*mmcie_add)(struct wusbhc *wusbhc, u8 interval, u8 repeat_cnt, +			 u8 handle, struct wuie_hdr *wuie); +	int (*mmcie_rm)(struct wusbhc *wusbhc, u8 handle); +	int (*dev_info_set)(struct wusbhc *, struct wusb_dev *wusb_dev); +	int (*bwa_set)(struct wusbhc *wusbhc, s8 stream_index, +		       const struct uwb_mas_bm *); +	int (*set_ptk)(struct wusbhc *wusbhc, u8 port_idx, +		       u32 tkid, const void *key, size_t key_size); +	int (*set_gtk)(struct wusbhc *wusbhc, +		       u32 tkid, const void *key, size_t key_size); +	int (*set_num_dnts)(struct wusbhc *wusbhc, u8 interval, u8 slots); + +	struct { +		struct usb_key_descriptor descr; +		u8 data[16];				/* GTK key data */ +	} __attribute__((packed)) gtk; +	u8 gtk_index; +	u32 gtk_tkid; +	struct work_struct gtk_rekey_done_work; +	int pending_set_gtks; + +	struct usb_encryption_descriptor *ccm1_etd; +}; + +#define usb_hcd_to_wusbhc(u) container_of((u), struct wusbhc, usb_hcd) + + +extern int wusbhc_create(struct wusbhc *); +extern int wusbhc_b_create(struct wusbhc *); +extern void wusbhc_b_destroy(struct wusbhc *); +extern void wusbhc_destroy(struct wusbhc *); +extern int wusb_dev_sysfs_add(struct wusbhc *, struct usb_device *, +			      struct wusb_dev *); +extern void wusb_dev_sysfs_rm(struct wusb_dev *); +extern int wusbhc_sec_create(struct wusbhc *); +extern int wusbhc_sec_start(struct wusbhc *); +extern void wusbhc_sec_stop(struct wusbhc *); +extern void wusbhc_sec_destroy(struct wusbhc *); +extern void wusbhc_giveback_urb(struct wusbhc *wusbhc, struct urb *urb, +				int status); +void wusbhc_reset_all(struct wusbhc *wusbhc); + +int wusbhc_pal_register(struct wusbhc *wusbhc); +void wusbhc_pal_unregister(struct wusbhc *wusbhc); + +/* + * Return @usb_dev's @usb_hcd (properly referenced) or NULL if gone + * + * @usb_dev: USB device, UNLOCKED and referenced (or otherwise, safe ptr) + * + * This is a safe assumption as @usb_dev->bus is referenced all the + * time during the @usb_dev life cycle. + */ +static inline struct usb_hcd *usb_hcd_get_by_usb_dev(struct usb_device *usb_dev) +{ +	struct usb_hcd *usb_hcd; +	usb_hcd = container_of(usb_dev->bus, struct usb_hcd, self); +	return usb_get_hcd(usb_hcd); +} + +/* + * Increment the reference count on a wusbhc. + * + * @wusbhc's life cycle is identical to that of the underlying usb_hcd. + */ +static inline struct wusbhc *wusbhc_get(struct wusbhc *wusbhc) +{ +	return usb_get_hcd(&wusbhc->usb_hcd) ? wusbhc : NULL; +} + +/* + * Return the wusbhc associated to a @usb_dev + * + * @usb_dev: USB device, UNLOCKED and referenced (or otherwise, safe ptr) + * + * @returns: wusbhc for @usb_dev; NULL if the @usb_dev is being torn down. + *           WARNING: referenced at the usb_hcd level, unlocked + * + * FIXME: move offline + */ +static inline struct wusbhc *wusbhc_get_by_usb_dev(struct usb_device *usb_dev) +{ +	struct wusbhc *wusbhc = NULL; +	struct usb_hcd *usb_hcd; +	if (usb_dev->devnum > 1 && !usb_dev->wusb) { +		/* but root hubs */ +		dev_err(&usb_dev->dev, "devnum %d wusb %d\n", usb_dev->devnum, +			usb_dev->wusb); +		BUG_ON(usb_dev->devnum > 1 && !usb_dev->wusb); +	} +	usb_hcd = usb_hcd_get_by_usb_dev(usb_dev); +	if (usb_hcd == NULL) +		return NULL; +	BUG_ON(usb_hcd->wireless == 0); +	return wusbhc = usb_hcd_to_wusbhc(usb_hcd); +} + + +static inline void wusbhc_put(struct wusbhc *wusbhc) +{ +	usb_put_hcd(&wusbhc->usb_hcd); +} + +int wusbhc_start(struct wusbhc *wusbhc, const struct wusb_ckhdid *chid); +void wusbhc_stop(struct wusbhc *wusbhc); +extern int wusbhc_chid_set(struct wusbhc *, const struct wusb_ckhdid *); + +/* Device connect handling */ +extern int wusbhc_devconnect_create(struct wusbhc *); +extern void wusbhc_devconnect_destroy(struct wusbhc *); +extern int wusbhc_devconnect_start(struct wusbhc *wusbhc, +				   const struct wusb_ckhdid *chid); +extern void wusbhc_devconnect_stop(struct wusbhc *wusbhc); +extern int wusbhc_devconnect_auth(struct wusbhc *, u8); +extern void wusbhc_handle_dn(struct wusbhc *, u8 srcaddr, +			     struct wusb_dn_hdr *dn_hdr, size_t size); +extern int wusbhc_dev_reset(struct wusbhc *wusbhc, u8 port); +extern void __wusbhc_dev_disable(struct wusbhc *wusbhc, u8 port); +extern int wusb_usb_ncb(struct notifier_block *nb, unsigned long val, +			void *priv); +extern int wusb_set_dev_addr(struct wusbhc *wusbhc, struct wusb_dev *wusb_dev, +			     u8 addr); + +/* Wireless USB fake Root Hub methods */ +extern int wusbhc_rh_create(struct wusbhc *); +extern void wusbhc_rh_destroy(struct wusbhc *); + +extern int wusbhc_rh_status_data(struct usb_hcd *, char *); +extern int wusbhc_rh_control(struct usb_hcd *, u16, u16, u16, char *, u16); +extern int wusbhc_rh_suspend(struct usb_hcd *); +extern int wusbhc_rh_resume(struct usb_hcd *); +extern int wusbhc_rh_start_port_reset(struct usb_hcd *, unsigned); + +/* MMC handling */ +extern int wusbhc_mmcie_create(struct wusbhc *); +extern void wusbhc_mmcie_destroy(struct wusbhc *); +extern int wusbhc_mmcie_set(struct wusbhc *, u8 interval, u8 repeat_cnt, +			    struct wuie_hdr *); +extern void wusbhc_mmcie_rm(struct wusbhc *, struct wuie_hdr *); + +/* Bandwidth reservation */ +int wusbhc_rsv_establish(struct wusbhc *wusbhc); +void wusbhc_rsv_terminate(struct wusbhc *wusbhc); + +/* + * I've always said + * I wanted a wedding in a church... + * + * but lately I've been thinking about + * the Botanical Gardens. + * + * We could do it by the tulips. + * It'll be beautiful + * + * --Security! + */ +extern int wusb_dev_sec_add(struct wusbhc *, struct usb_device *, +				struct wusb_dev *); +extern void wusb_dev_sec_rm(struct wusb_dev *) ; +extern int wusb_dev_4way_handshake(struct wusbhc *, struct wusb_dev *, +				   struct wusb_ckhdid *ck); +void wusbhc_gtk_rekey(struct wusbhc *wusbhc); + + +/* WUSB Cluster ID handling */ +extern u8 wusb_cluster_id_get(void); +extern void wusb_cluster_id_put(u8); + +/* + * wusb_port_by_idx - return the port associated to a zero-based port index + * + * NOTE: valid without locking as long as wusbhc is referenced (as the + *       number of ports doesn't change). The data pointed to has to + *       be verified though :) + */ +static inline struct wusb_port *wusb_port_by_idx(struct wusbhc *wusbhc, +						 u8 port_idx) +{ +	return &wusbhc->port[port_idx]; +} + +/* + * wusb_port_no_to_idx - Convert port number (per usb_dev->portnum) to + * a port_idx. + * + * USB stack USB ports are 1 based!! + * + * NOTE: only valid for WUSB devices!!! + */ +static inline u8 wusb_port_no_to_idx(u8 port_no) +{ +	return port_no - 1; +} + +extern struct wusb_dev *__wusb_dev_get_by_usb_dev(struct wusbhc *, +						  struct usb_device *); + +/* + * Return a referenced wusb_dev given a @usb_dev + * + * Returns NULL if the usb_dev is being torn down. + * + * FIXME: move offline + */ +static inline +struct wusb_dev *wusb_dev_get_by_usb_dev(struct usb_device *usb_dev) +{ +	struct wusbhc *wusbhc; +	struct wusb_dev *wusb_dev; +	wusbhc = wusbhc_get_by_usb_dev(usb_dev); +	if (wusbhc == NULL) +		return NULL; +	mutex_lock(&wusbhc->mutex); +	wusb_dev = __wusb_dev_get_by_usb_dev(wusbhc, usb_dev); +	mutex_unlock(&wusbhc->mutex); +	wusbhc_put(wusbhc); +	return wusb_dev; +} + +/* Misc */ + +extern struct workqueue_struct *wusbd; +#endif /* #ifndef __WUSBHC_H__ */  | 
