summaryrefslogtreecommitdiffstats
path: root/drivers/usb/serial/usb-serial.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/serial/usb-serial.c')
-rw-r--r--drivers/usb/serial/usb-serial.c230
1 files changed, 86 insertions, 144 deletions
diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c
index 4a037b4a79cf..c7ca95f64edc 100644
--- a/drivers/usb/serial/usb-serial.c
+++ b/drivers/usb/serial/usb-serial.c
@@ -38,7 +38,6 @@
#include <linux/usb/serial.h>
#include <linux/kfifo.h>
#include <linux/idr.h>
-#include "pl2303.h"
#define DRIVER_AUTHOR "Greg Kroah-Hartman <gregkh@linuxfoundation.org>"
#define DRIVER_DESC "USB Serial Driver core"
@@ -710,6 +709,39 @@ 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)
+{
+ struct device *dev = &serial->interface->dev;
+ 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;
+ 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;
+ }
+ }
+}
+
static int usb_serial_probe(struct usb_interface *interface,
const struct usb_device_id *id)
{
@@ -717,23 +749,15 @@ static int usb_serial_probe(struct usb_interface *interface,
struct usb_device *dev = interface_to_usbdev(interface);
struct usb_serial *serial = NULL;
struct usb_serial_port *port;
- struct usb_host_interface *iface_desc;
struct usb_endpoint_descriptor *endpoint;
- struct usb_endpoint_descriptor *interrupt_in_endpoint[MAX_NUM_PORTS];
- struct usb_endpoint_descriptor *interrupt_out_endpoint[MAX_NUM_PORTS];
- struct usb_endpoint_descriptor *bulk_in_endpoint[MAX_NUM_PORTS];
- struct usb_endpoint_descriptor *bulk_out_endpoint[MAX_NUM_PORTS];
+ struct usb_serial_endpoints *epds;
struct usb_serial_driver *type = NULL;
int retval;
int buffer_size;
int i;
int j;
- int num_interrupt_in = 0;
- int num_interrupt_out = 0;
- int num_bulk_in = 0;
- int num_bulk_out = 0;
int num_ports = 0;
- int max_endpoints;
+ unsigned char max_endpoints;
mutex_lock(&table_lock);
type = search_serial_device(interface);
@@ -752,8 +776,8 @@ static int usb_serial_probe(struct usb_interface *interface,
serial = create_serial(dev, interface, type);
if (!serial) {
- module_put(type->driver.owner);
- return -ENOMEM;
+ retval = -ENOMEM;
+ goto err_put_module;
}
/* if this device type has a probe function, call it */
@@ -765,129 +789,48 @@ static int usb_serial_probe(struct usb_interface *interface,
if (retval) {
dev_dbg(ddev, "sub driver rejected device\n");
- usb_serial_put(serial);
- module_put(type->driver.owner);
- return retval;
+ goto err_put_serial;
}
}
/* descriptor matches, let's find the endpoints needed */
- /* check out the endpoints */
- iface_desc = interface->cur_altsetting;
- for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
- endpoint = &iface_desc->endpoint[i].desc;
-
- if (usb_endpoint_is_bulk_in(endpoint)) {
- /* we found a bulk in endpoint */
- dev_dbg(ddev, "found bulk in on endpoint %d\n", i);
- if (num_bulk_in < MAX_NUM_PORTS) {
- bulk_in_endpoint[num_bulk_in] = endpoint;
- ++num_bulk_in;
- }
- }
-
- if (usb_endpoint_is_bulk_out(endpoint)) {
- /* we found a bulk out endpoint */
- dev_dbg(ddev, "found bulk out on endpoint %d\n", i);
- if (num_bulk_out < MAX_NUM_PORTS) {
- bulk_out_endpoint[num_bulk_out] = endpoint;
- ++num_bulk_out;
- }
- }
-
- if (usb_endpoint_is_int_in(endpoint)) {
- /* we found a interrupt in endpoint */
- dev_dbg(ddev, "found interrupt in on endpoint %d\n", i);
- if (num_interrupt_in < MAX_NUM_PORTS) {
- interrupt_in_endpoint[num_interrupt_in] =
- endpoint;
- ++num_interrupt_in;
- }
- }
-
- if (usb_endpoint_is_int_out(endpoint)) {
- /* we found an interrupt out endpoint */
- dev_dbg(ddev, "found interrupt out on endpoint %d\n", i);
- if (num_interrupt_out < MAX_NUM_PORTS) {
- interrupt_out_endpoint[num_interrupt_out] =
- endpoint;
- ++num_interrupt_out;
- }
- }
+ epds = kzalloc(sizeof(*epds), GFP_KERNEL);
+ if (!epds) {
+ retval = -ENOMEM;
+ goto err_put_serial;
}
-#if IS_ENABLED(CONFIG_USB_SERIAL_PL2303)
- /* BEGIN HORRIBLE HACK FOR PL2303 */
- /* this is needed due to the looney way its endpoints are set up */
- if (((le16_to_cpu(dev->descriptor.idVendor) == PL2303_VENDOR_ID) &&
- (le16_to_cpu(dev->descriptor.idProduct) == PL2303_PRODUCT_ID)) ||
- ((le16_to_cpu(dev->descriptor.idVendor) == ATEN_VENDOR_ID) &&
- (le16_to_cpu(dev->descriptor.idProduct) == ATEN_PRODUCT_ID)) ||
- ((le16_to_cpu(dev->descriptor.idVendor) == ALCOR_VENDOR_ID) &&
- (le16_to_cpu(dev->descriptor.idProduct) == ALCOR_PRODUCT_ID)) ||
- ((le16_to_cpu(dev->descriptor.idVendor) == SIEMENS_VENDOR_ID) &&
- (le16_to_cpu(dev->descriptor.idProduct) == SIEMENS_PRODUCT_ID_EF81))) {
- if (interface != dev->actconfig->interface[0]) {
- /* check out the endpoints of the other interface*/
- iface_desc = dev->actconfig->interface[0]->cur_altsetting;
- for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
- endpoint = &iface_desc->endpoint[i].desc;
- if (usb_endpoint_is_int_in(endpoint)) {
- /* we found a interrupt in endpoint */
- dev_dbg(ddev, "found interrupt in for Prolific device on separate interface\n");
- if (num_interrupt_in < MAX_NUM_PORTS) {
- interrupt_in_endpoint[num_interrupt_in] = endpoint;
- ++num_interrupt_in;
- }
- }
- }
- }
+ find_endpoints(serial, epds);
- /* Now make sure the PL-2303 is configured correctly.
- * If not, give up now and hope this hack will work
- * properly during a later invocation of usb_serial_probe
- */
- if (num_bulk_in == 0 || num_bulk_out == 0) {
- dev_info(ddev, "PL-2303 hack: descriptors matched but endpoints did not\n");
- usb_serial_put(serial);
- module_put(type->driver.owner);
- return -ENODEV;
- }
- }
- /* END HORRIBLE HACK FOR PL2303 */
-#endif
-
-#ifdef CONFIG_USB_SERIAL_GENERIC
- if (type == &usb_serial_generic_device) {
- num_ports = num_bulk_out;
- if (num_ports == 0) {
- dev_err(ddev, "Generic device with no bulk out, not allowed.\n");
- usb_serial_put(serial);
- module_put(type->driver.owner);
- return -EIO;
- }
- dev_info(ddev, "The \"generic\" usb-serial driver is only for testing and one-off prototypes.\n");
- dev_info(ddev, "Tell linux-usb@vger.kernel.org to add your device to a proper driver.\n");
+ if (epds->num_bulk_in < type->num_bulk_in ||
+ epds->num_bulk_out < type->num_bulk_out ||
+ epds->num_interrupt_in < type->num_interrupt_in ||
+ epds->num_interrupt_out < type->num_interrupt_out) {
+ dev_err(ddev, "required endpoints missing\n");
+ retval = -ENODEV;
+ goto err_free_epds;
}
-#endif
- if (!num_ports) {
- /* if this device type has a calc_num_ports function, call it */
- if (type->calc_num_ports)
- num_ports = type->calc_num_ports(serial);
- if (!num_ports)
- num_ports = type->num_ports;
+
+ if (type->calc_num_ports) {
+ retval = type->calc_num_ports(serial, epds);
+ if (retval < 0)
+ goto err_free_epds;
+ num_ports = retval;
}
+ if (!num_ports)
+ num_ports = type->num_ports;
+
if (num_ports > MAX_NUM_PORTS) {
dev_warn(ddev, "too many ports requested: %d\n", num_ports);
num_ports = MAX_NUM_PORTS;
}
- serial->num_ports = num_ports;
- serial->num_bulk_in = num_bulk_in;
- serial->num_bulk_out = num_bulk_out;
- serial->num_interrupt_in = num_interrupt_in;
- serial->num_interrupt_out = num_interrupt_out;
+ serial->num_ports = (unsigned char)num_ports;
+ serial->num_bulk_in = epds->num_bulk_in;
+ serial->num_bulk_out = epds->num_bulk_out;
+ serial->num_interrupt_in = epds->num_interrupt_in;
+ serial->num_interrupt_out = epds->num_interrupt_out;
/* found all that we need */
dev_info(ddev, "%s converter detected\n", type->description);
@@ -895,10 +838,10 @@ static int usb_serial_probe(struct usb_interface *interface,
/* create our ports, we need as many as the max endpoints */
/* we don't use num_ports here because some devices have more
endpoint pairs than ports */
- max_endpoints = max(num_bulk_in, num_bulk_out);
- max_endpoints = max(max_endpoints, num_interrupt_in);
- max_endpoints = max(max_endpoints, num_interrupt_out);
- max_endpoints = max(max_endpoints, (int)serial->num_ports);
+ max_endpoints = max(epds->num_bulk_in, epds->num_bulk_out);
+ max_endpoints = max(max_endpoints, epds->num_interrupt_in);
+ max_endpoints = max(max_endpoints, epds->num_interrupt_out);
+ max_endpoints = max(max_endpoints, serial->num_ports);
serial->num_port_pointers = max_endpoints;
dev_dbg(ddev, "setting up %d port structure(s)\n", max_endpoints);
@@ -923,8 +866,8 @@ static int usb_serial_probe(struct usb_interface *interface,
}
/* set up the endpoint information */
- for (i = 0; i < num_bulk_in; ++i) {
- endpoint = bulk_in_endpoint[i];
+ for (i = 0; i < epds->num_bulk_in; ++i) {
+ endpoint = epds->bulk_in[i];
port = serial->port[i];
buffer_size = max_t(int, serial->type->bulk_in_size,
usb_endpoint_maxp(endpoint));
@@ -952,8 +895,8 @@ static int usb_serial_probe(struct usb_interface *interface,
port->bulk_in_buffer = port->bulk_in_buffers[0];
}
- for (i = 0; i < num_bulk_out; ++i) {
- endpoint = bulk_out_endpoint[i];
+ for (i = 0; i < epds->num_bulk_out; ++i) {
+ endpoint = epds->bulk_out[i];
port = serial->port[i];
if (kfifo_alloc(&port->write_fifo, PAGE_SIZE, GFP_KERNEL))
goto probe_error;
@@ -985,8 +928,8 @@ static int usb_serial_probe(struct usb_interface *interface,
}
if (serial->type->read_int_callback) {
- for (i = 0; i < num_interrupt_in; ++i) {
- endpoint = interrupt_in_endpoint[i];
+ for (i = 0; i < epds->num_interrupt_in; ++i) {
+ endpoint = epds->interrupt_in[i];
port = serial->port[i];
port->interrupt_in_urb = usb_alloc_urb(0, GFP_KERNEL);
if (!port->interrupt_in_urb)
@@ -1005,13 +948,13 @@ static int usb_serial_probe(struct usb_interface *interface,
serial->type->read_int_callback, port,
endpoint->bInterval);
}
- } else if (num_interrupt_in) {
+ } else if (epds->num_interrupt_in) {
dev_dbg(ddev, "The device claims to support interrupt in transfers, but read_int_callback is not defined\n");
}
if (serial->type->write_int_callback) {
- for (i = 0; i < num_interrupt_out; ++i) {
- endpoint = interrupt_out_endpoint[i];
+ for (i = 0; i < epds->num_interrupt_out; ++i) {
+ endpoint = epds->interrupt_out[i];
port = serial->port[i];
port->interrupt_out_urb = usb_alloc_urb(0, GFP_KERNEL);
if (!port->interrupt_out_urb)
@@ -1031,7 +974,7 @@ static int usb_serial_probe(struct usb_interface *interface,
serial->type->write_int_callback, port,
endpoint->bInterval);
}
- } else if (num_interrupt_out) {
+ } else if (epds->num_interrupt_out) {
dev_dbg(ddev, "The device claims to support interrupt out transfers, but write_int_callback is not defined\n");
}
@@ -1053,12 +996,6 @@ static int usb_serial_probe(struct usb_interface *interface,
serial->attached = 1;
}
- /* Avoid race with tty_open and serial_install by setting the
- * disconnected flag and not clearing it until all ports have been
- * registered.
- */
- serial->disconnected = 1;
-
if (allocate_minors(serial, num_ports)) {
dev_err(ddev, "No more free serial minor numbers\n");
goto probe_error;
@@ -1076,18 +1013,23 @@ static int usb_serial_probe(struct usb_interface *interface,
dev_err(ddev, "Error registering port device, continuing\n");
}
- serial->disconnected = 0;
-
if (num_ports > 0)
usb_serial_console_init(serial->port[0]->minor);
exit:
+ kfree(epds);
module_put(type->driver.owner);
return 0;
probe_error:
+ retval = -EIO;
+err_free_epds:
+ kfree(epds);
+err_put_serial:
usb_serial_put(serial);
+err_put_module:
module_put(type->driver.owner);
- return -EIO;
+
+ return retval;
}
static void usb_serial_disconnect(struct usb_interface *interface)
OpenPOWER on IntegriCloud