diff options
Diffstat (limited to 'drivers/usb/serial/usb-serial.c')
| -rw-r--r-- | drivers/usb/serial/usb-serial.c | 226 | 
1 files changed, 168 insertions, 58 deletions
| diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index 27e3bb58c872..98b33b1b5357 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -121,6 +121,44 @@ static void release_minors(struct usb_serial *serial)  	serial->minors_reserved = 0;  } +int usb_serial_claim_interface(struct usb_serial *serial, struct usb_interface *intf) +{ +	struct usb_driver *driver = serial->type->usb_driver; +	int ret; + +	if (serial->sibling) +		return -EBUSY; + +	ret = usb_driver_claim_interface(driver, intf, serial); +	if (ret) { +		dev_err(&serial->interface->dev, +				"failed to claim sibling interface: %d\n", ret); +		return ret; +	} + +	serial->sibling = intf; + +	return 0; +} +EXPORT_SYMBOL_GPL(usb_serial_claim_interface); + +static void release_sibling(struct usb_serial *serial, struct usb_interface *intf) +{ +	struct usb_driver *driver = serial->type->usb_driver; +	struct usb_interface *sibling; + +	if (!serial->sibling) +		return; + +	if (intf == serial->sibling) +		sibling = serial->interface; +	else +		sibling = serial->sibling; + +	usb_set_intfdata(sibling, NULL); +	usb_driver_release_interface(driver, sibling); +} +  static void destroy_serial(struct kref *kref)  {  	struct usb_serial *serial; @@ -243,7 +281,7 @@ static int serial_open(struct tty_struct *tty, struct file *filp)  {  	struct usb_serial_port *port = tty->driver_data; -	dev_dbg(tty->dev, "%s\n", __func__); +	dev_dbg(&port->dev, "%s\n", __func__);  	return tty_port_open(&port->port, tty, filp);  } @@ -272,7 +310,7 @@ static void serial_hangup(struct tty_struct *tty)  {  	struct usb_serial_port *port = tty->driver_data; -	dev_dbg(tty->dev, "%s\n", __func__); +	dev_dbg(&port->dev, "%s\n", __func__);  	tty_port_hangup(&port->port);  } @@ -281,7 +319,7 @@ static void serial_close(struct tty_struct *tty, struct file *filp)  {  	struct usb_serial_port *port = tty->driver_data; -	dev_dbg(tty->dev, "%s\n", __func__); +	dev_dbg(&port->dev, "%s\n", __func__);  	tty_port_close(&port->port, tty, filp);  } @@ -301,7 +339,7 @@ static void serial_cleanup(struct tty_struct *tty)  	struct usb_serial *serial;  	struct module *owner; -	dev_dbg(tty->dev, "%s\n", __func__); +	dev_dbg(&port->dev, "%s\n", __func__);  	/* The console is magical.  Do not hang up the console hardware  	 * or there will be tears. @@ -329,7 +367,7 @@ static int serial_write(struct tty_struct *tty, const unsigned char *buf,  	if (port->serial->dev->state == USB_STATE_NOTATTACHED)  		goto exit; -	dev_dbg(tty->dev, "%s - %d byte(s)\n", __func__, count); +	dev_dbg(&port->dev, "%s - %d byte(s)\n", __func__, count);  	retval = port->serial->type->write(tty, port, buf, count);  	if (retval < 0) @@ -342,7 +380,7 @@ static int serial_write_room(struct tty_struct *tty)  {  	struct usb_serial_port *port = tty->driver_data; -	dev_dbg(tty->dev, "%s\n", __func__); +	dev_dbg(&port->dev, "%s\n", __func__);  	return port->serial->type->write_room(tty);  } @@ -352,7 +390,7 @@ static int serial_chars_in_buffer(struct tty_struct *tty)  	struct usb_serial_port *port = tty->driver_data;  	struct usb_serial *serial = port->serial; -	dev_dbg(tty->dev, "%s\n", __func__); +	dev_dbg(&port->dev, "%s\n", __func__);  	if (serial->disconnected)  		return 0; @@ -365,7 +403,7 @@ static void serial_wait_until_sent(struct tty_struct *tty, int timeout)  	struct usb_serial_port *port = tty->driver_data;  	struct usb_serial *serial = port->serial; -	dev_dbg(tty->dev, "%s\n", __func__); +	dev_dbg(&port->dev, "%s\n", __func__);  	if (!port->serial->type->wait_until_sent)  		return; @@ -380,7 +418,7 @@ static void serial_throttle(struct tty_struct *tty)  {  	struct usb_serial_port *port = tty->driver_data; -	dev_dbg(tty->dev, "%s\n", __func__); +	dev_dbg(&port->dev, "%s\n", __func__);  	if (port->serial->type->throttle)  		port->serial->type->throttle(tty); @@ -390,7 +428,7 @@ static void serial_unthrottle(struct tty_struct *tty)  {  	struct usb_serial_port *port = tty->driver_data; -	dev_dbg(tty->dev, "%s\n", __func__); +	dev_dbg(&port->dev, "%s\n", __func__);  	if (port->serial->type->unthrottle)  		port->serial->type->unthrottle(tty); @@ -399,19 +437,62 @@ static void serial_unthrottle(struct tty_struct *tty)  static int serial_get_serial(struct tty_struct *tty, struct serial_struct *ss)  {  	struct usb_serial_port *port = tty->driver_data; +	struct tty_port *tport = &port->port; +	unsigned int close_delay, closing_wait; + +	mutex_lock(&tport->mutex); + +	close_delay = jiffies_to_msecs(tport->close_delay) / 10; +	closing_wait = tport->closing_wait; +	if (closing_wait != ASYNC_CLOSING_WAIT_NONE) +		closing_wait = jiffies_to_msecs(closing_wait) / 10; + +	ss->line = port->minor; +	ss->close_delay = close_delay; +	ss->closing_wait = closing_wait;  	if (port->serial->type->get_serial) -		return port->serial->type->get_serial(tty, ss); -	return -ENOTTY; +		port->serial->type->get_serial(tty, ss); + +	mutex_unlock(&tport->mutex); + +	return 0;  }  static int serial_set_serial(struct tty_struct *tty, struct serial_struct *ss)  {  	struct usb_serial_port *port = tty->driver_data; +	struct tty_port *tport = &port->port; +	unsigned int close_delay, closing_wait; +	int ret = 0; + +	close_delay = msecs_to_jiffies(ss->close_delay * 10); +	closing_wait = ss->closing_wait; +	if (closing_wait != ASYNC_CLOSING_WAIT_NONE) +		closing_wait = msecs_to_jiffies(closing_wait * 10); + +	mutex_lock(&tport->mutex); + +	if (!capable(CAP_SYS_ADMIN)) { +		if (close_delay != tport->close_delay || +				closing_wait != tport->closing_wait) { +			ret = -EPERM; +			goto out_unlock; +		} +	} -	if (port->serial->type->set_serial) -		return port->serial->type->set_serial(tty, ss); -	return -ENOTTY; +	if (port->serial->type->set_serial) { +		ret = port->serial->type->set_serial(tty, ss); +		if (ret) +			goto out_unlock; +	} + +	tport->close_delay = close_delay; +	tport->closing_wait = closing_wait; +out_unlock: +	mutex_unlock(&tport->mutex); + +	return ret;  }  static int serial_ioctl(struct tty_struct *tty, @@ -420,7 +501,7 @@ static int serial_ioctl(struct tty_struct *tty,  	struct usb_serial_port *port = tty->driver_data;  	int retval = -ENOIOCTLCMD; -	dev_dbg(tty->dev, "%s - cmd 0x%04x\n", __func__, cmd); +	dev_dbg(&port->dev, "%s - cmd 0x%04x\n", __func__, cmd);  	switch (cmd) {  	case TIOCMIWAIT: @@ -439,7 +520,7 @@ static void serial_set_termios(struct tty_struct *tty, struct ktermios *old)  {  	struct usb_serial_port *port = tty->driver_data; -	dev_dbg(tty->dev, "%s\n", __func__); +	dev_dbg(&port->dev, "%s\n", __func__);  	if (port->serial->type->set_termios)  		port->serial->type->set_termios(tty, port, old); @@ -451,7 +532,7 @@ static int serial_break(struct tty_struct *tty, int break_state)  {  	struct usb_serial_port *port = tty->driver_data; -	dev_dbg(tty->dev, "%s\n", __func__); +	dev_dbg(&port->dev, "%s\n", __func__);  	if (port->serial->type->break_ctl)  		port->serial->type->break_ctl(tty, break_state); @@ -498,11 +579,11 @@ static int serial_tiocmget(struct tty_struct *tty)  {  	struct usb_serial_port *port = tty->driver_data; -	dev_dbg(tty->dev, "%s\n", __func__); +	dev_dbg(&port->dev, "%s\n", __func__);  	if (port->serial->type->tiocmget)  		return port->serial->type->tiocmget(tty); -	return -EINVAL; +	return -ENOTTY;  }  static int serial_tiocmset(struct tty_struct *tty, @@ -510,11 +591,11 @@ static int serial_tiocmset(struct tty_struct *tty,  {  	struct usb_serial_port *port = tty->driver_data; -	dev_dbg(tty->dev, "%s\n", __func__); +	dev_dbg(&port->dev, "%s\n", __func__);  	if (port->serial->type->tiocmset)  		return port->serial->type->tiocmset(tty, set, clear); -	return -EINVAL; +	return -ENOTTY;  }  static int serial_get_icount(struct tty_struct *tty, @@ -522,11 +603,11 @@ static int serial_get_icount(struct tty_struct *tty,  {  	struct usb_serial_port *port = tty->driver_data; -	dev_dbg(tty->dev, "%s\n", __func__); +	dev_dbg(&port->dev, "%s\n", __func__);  	if (port->serial->type->get_icount)  		return port->serial->type->get_icount(tty, icount); -	return -EINVAL; +	return -ENOTTY;  }  /* @@ -711,36 +792,48 @@ static const struct tty_port_operations serial_port_ops = {  	.shutdown		= serial_port_shutdown,  }; -static void find_endpoints(struct usb_serial *serial, -					struct usb_serial_endpoints *epds) +static void store_endpoint(struct usb_serial *serial, +					struct usb_serial_endpoints *epds, +					struct usb_endpoint_descriptor *epd)  {  	struct device *dev = &serial->interface->dev; +	u8 addr = epd->bEndpointAddress; + +	if (usb_endpoint_is_bulk_in(epd)) { +		if (epds->num_bulk_in == ARRAY_SIZE(epds->bulk_in)) +			return; +		dev_dbg(dev, "found bulk in endpoint %02x\n", addr); +		epds->bulk_in[epds->num_bulk_in++] = epd; +	} else if (usb_endpoint_is_bulk_out(epd)) { +		if (epds->num_bulk_out == ARRAY_SIZE(epds->bulk_out)) +			return; +		dev_dbg(dev, "found bulk out endpoint %02x\n", addr); +		epds->bulk_out[epds->num_bulk_out++] = epd; +	} else if (usb_endpoint_is_int_in(epd)) { +		if (epds->num_interrupt_in == ARRAY_SIZE(epds->interrupt_in)) +			return; +		dev_dbg(dev, "found interrupt in endpoint %02x\n", addr); +		epds->interrupt_in[epds->num_interrupt_in++] = epd; +	} else if (usb_endpoint_is_int_out(epd)) { +		if (epds->num_interrupt_out == ARRAY_SIZE(epds->interrupt_out)) +			return; +		dev_dbg(dev, "found interrupt out endpoint %02x\n", addr); +		epds->interrupt_out[epds->num_interrupt_out++] = epd; +	} +} + +static void find_endpoints(struct usb_serial *serial, +					struct usb_serial_endpoints *epds, +					struct usb_interface *intf) +{  	struct usb_host_interface *iface_desc;  	struct usb_endpoint_descriptor *epd;  	unsigned int i; -	BUILD_BUG_ON(ARRAY_SIZE(epds->bulk_in) < USB_MAXENDPOINTS / 2); -	BUILD_BUG_ON(ARRAY_SIZE(epds->bulk_out) < USB_MAXENDPOINTS / 2); -	BUILD_BUG_ON(ARRAY_SIZE(epds->interrupt_in) < USB_MAXENDPOINTS / 2); -	BUILD_BUG_ON(ARRAY_SIZE(epds->interrupt_out) < USB_MAXENDPOINTS / 2); - -	iface_desc = serial->interface->cur_altsetting; +	iface_desc = intf->cur_altsetting;  	for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {  		epd = &iface_desc->endpoint[i].desc; - -		if (usb_endpoint_is_bulk_in(epd)) { -			dev_dbg(dev, "found bulk in on endpoint %u\n", i); -			epds->bulk_in[epds->num_bulk_in++] = epd; -		} else if (usb_endpoint_is_bulk_out(epd)) { -			dev_dbg(dev, "found bulk out on endpoint %u\n", i); -			epds->bulk_out[epds->num_bulk_out++] = epd; -		} else if (usb_endpoint_is_int_in(epd)) { -			dev_dbg(dev, "found interrupt in on endpoint %u\n", i); -			epds->interrupt_in[epds->num_interrupt_in++] = epd; -		} else if (usb_endpoint_is_int_out(epd)) { -			dev_dbg(dev, "found interrupt out on endpoint %u\n", i); -			epds->interrupt_out[epds->num_interrupt_out++] = epd; -		} +		store_endpoint(serial, epds, epd);  	}  } @@ -906,7 +999,7 @@ static int usb_serial_probe(struct usb_interface *interface,  		if (retval) {  			dev_dbg(ddev, "sub driver rejected device\n"); -			goto err_put_serial; +			goto err_release_sibling;  		}  	} @@ -914,10 +1007,12 @@ static int usb_serial_probe(struct usb_interface *interface,  	epds = kzalloc(sizeof(*epds), GFP_KERNEL);  	if (!epds) {  		retval = -ENOMEM; -		goto err_put_serial; +		goto err_release_sibling;  	} -	find_endpoints(serial, epds); +	find_endpoints(serial, epds, interface); +	if (serial->sibling) +		find_endpoints(serial, epds, serial->sibling);  	if (epds->num_bulk_in < type->num_bulk_in ||  			epds->num_bulk_out < type->num_bulk_out || @@ -1065,7 +1160,8 @@ exit:  err_free_epds:  	kfree(epds); -err_put_serial: +err_release_sibling: +	release_sibling(serial, interface);  	usb_serial_put(serial);  err_put_module:  	module_put(type->driver.owner); @@ -1081,6 +1177,10 @@ static void usb_serial_disconnect(struct usb_interface *interface)  	struct usb_serial_port *port;  	struct tty_struct *tty; +	/* sibling interface is cleaning up */ +	if (!serial) +		return; +  	usb_serial_console_disconnect(serial);  	mutex_lock(&serial->disc_mutex); @@ -1104,6 +1204,8 @@ static void usb_serial_disconnect(struct usb_interface *interface)  	if (serial->type->disconnect)  		serial->type->disconnect(serial); +	release_sibling(serial, interface); +  	/* let the last holder of this object cause it to be cleaned up */  	usb_serial_put(serial);  	dev_info(dev, "device disconnected\n"); @@ -1112,9 +1214,11 @@ static void usb_serial_disconnect(struct usb_interface *interface)  int usb_serial_suspend(struct usb_interface *intf, pm_message_t message)  {  	struct usb_serial *serial = usb_get_intfdata(intf); -	int i, r = 0; +	int i, r; -	serial->suspending = 1; +	/* suspend when called for first sibling interface */ +	if (serial->suspend_count++) +		return 0;  	/*  	 * serial->type->suspend() MUST return 0 in system sleep context, @@ -1124,15 +1228,15 @@ int usb_serial_suspend(struct usb_interface *intf, pm_message_t message)  	if (serial->type->suspend) {  		r = serial->type->suspend(serial, message);  		if (r < 0) { -			serial->suspending = 0; -			goto err_out; +			serial->suspend_count--; +			return r;  		}  	}  	for (i = 0; i < serial->num_ports; ++i)  		usb_serial_port_poison_urbs(serial->port[i]); -err_out: -	return r; + +	return 0;  }  EXPORT_SYMBOL(usb_serial_suspend); @@ -1149,9 +1253,12 @@ int usb_serial_resume(struct usb_interface *intf)  	struct usb_serial *serial = usb_get_intfdata(intf);  	int rv; +	/* resume when called for last sibling interface */ +	if (--serial->suspend_count) +		return 0; +  	usb_serial_unpoison_port_urbs(serial); -	serial->suspending = 0;  	if (serial->type->resume)  		rv = serial->type->resume(serial);  	else @@ -1166,9 +1273,12 @@ static int usb_serial_reset_resume(struct usb_interface *intf)  	struct usb_serial *serial = usb_get_intfdata(intf);  	int rv; +	/* resume when called for last sibling interface */ +	if (--serial->suspend_count) +		return 0; +  	usb_serial_unpoison_port_urbs(serial); -	serial->suspending = 0;  	if (serial->type->reset_resume) {  		rv = serial->type->reset_resume(serial);  	} else { | 
