static int usb_serial_probe()

in serial/usb-serial.c [961:1173]


static int usb_serial_probe(struct usb_interface *interface,
			       const struct usb_device_id *id)
{
	struct device *ddev = &interface->dev;
	struct usb_device *dev = interface_to_usbdev(interface);
	struct usb_serial *serial = NULL;
	struct usb_serial_port *port;
	struct usb_serial_endpoints *epds;
	struct usb_serial_driver *type = NULL;
	int retval;
	int i;
	int num_ports = 0;
	unsigned char max_endpoints;

	mutex_lock(&table_lock);
	type = search_serial_device(interface);
	if (!type) {
		mutex_unlock(&table_lock);
		dev_dbg(ddev, "none matched\n");
		return -ENODEV;
	}

	if (!try_module_get(type->driver.owner)) {
		mutex_unlock(&table_lock);
		dev_err(ddev, "module get failed, exiting\n");
		return -EIO;
	}
	mutex_unlock(&table_lock);

	serial = create_serial(dev, interface, type);
	if (!serial) {
		retval = -ENOMEM;
		goto err_put_module;
	}

	/* if this device type has a probe function, call it */
	if (type->probe) {
		const struct usb_device_id *id;

		id = get_iface_id(type, interface);
		retval = type->probe(serial, id);

		if (retval) {
			dev_dbg(ddev, "sub driver rejected device\n");
			goto err_release_sibling;
		}
	}

	/* descriptor matches, let's find the endpoints needed */
	epds = kzalloc(sizeof(*epds), GFP_KERNEL);
	if (!epds) {
		retval = -ENOMEM;
		goto err_release_sibling;
	}

	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 ||
			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;
	}

	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 = (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);

	/* 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(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);
	for (i = 0; i < max_endpoints; ++i) {
		port = kzalloc(sizeof(struct usb_serial_port), GFP_KERNEL);
		if (!port) {
			retval = -ENOMEM;
			goto err_free_epds;
		}
		tty_port_init(&port->port);
		port->port.ops = &serial_port_ops;
		port->serial = serial;
		spin_lock_init(&port->lock);
		/* Keep this for private driver use for the moment but
		   should probably go away */
		INIT_WORK(&port->work, usb_serial_port_work);
		serial->port[i] = port;
		port->dev.parent = &interface->dev;
		port->dev.driver = NULL;
		port->dev.bus = &usb_serial_bus_type;
		port->dev.release = &usb_serial_port_release;
		port->dev.groups = usb_serial_port_groups;
		device_initialize(&port->dev);
	}

	/* set up the endpoint information */
	for (i = 0; i < epds->num_bulk_in; ++i) {
		retval = setup_port_bulk_in(serial->port[i], epds->bulk_in[i]);
		if (retval)
			goto err_free_epds;
	}

	for (i = 0; i < epds->num_bulk_out; ++i) {
		retval = setup_port_bulk_out(serial->port[i],
				epds->bulk_out[i]);
		if (retval)
			goto err_free_epds;
	}

	if (serial->type->read_int_callback) {
		for (i = 0; i < epds->num_interrupt_in; ++i) {
			retval = setup_port_interrupt_in(serial->port[i],
					epds->interrupt_in[i]);
			if (retval)
				goto err_free_epds;
		}
	} 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 < epds->num_interrupt_out; ++i) {
			retval = setup_port_interrupt_out(serial->port[i],
					epds->interrupt_out[i]);
			if (retval)
				goto err_free_epds;
		}
	} 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");
	}

	usb_set_intfdata(interface, serial);

	/* if this device type has an attach function, call it */
	if (type->attach) {
		retval = type->attach(serial);
		if (retval < 0)
			goto err_free_epds;
		serial->attached = 1;
		if (retval > 0) {
			/* quietly accept this device, but don't bind to a
			   serial port as it's about to disappear */
			serial->num_ports = 0;
			goto exit;
		}
	} else {
		serial->attached = 1;
	}

	retval = allocate_minors(serial, num_ports);
	if (retval) {
		dev_err(ddev, "No more free serial minor numbers\n");
		goto err_free_epds;
	}

	/* register all of the individual ports with the driver core */
	for (i = 0; i < num_ports; ++i) {
		port = serial->port[i];
		dev_set_name(&port->dev, "ttyUSB%d", port->minor);
		dev_dbg(ddev, "registering %s\n", dev_name(&port->dev));
		device_enable_async_suspend(&port->dev);

		retval = device_add(&port->dev);
		if (retval)
			dev_err(ddev, "Error registering port device, continuing\n");
	}

	if (num_ports > 0)
		usb_serial_console_init(serial->port[0]->minor);
exit:
	kfree(epds);
	module_put(type->driver.owner);
	return 0;

err_free_epds:
	kfree(epds);
err_release_sibling:
	release_sibling(serial, interface);
	usb_serial_put(serial);
err_put_module:
	module_put(type->driver.owner);

	return retval;
}