Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 765e0ba6 authored by Alan Stern's avatar Alan Stern Committed by Greg Kroah-Hartman
Browse files

usb-serial: new API for driver registration



This patch (as1522) adds two new routines to the usb-serial core, for
registering and unregistering serial drivers.  Instead of registering
the usb_driver and usb_serial_drivers separately, with error checking
for each one, the drivers can all be registered and unregistered by a
single function call.  This reduces duplicated code.

More importantly, the new core routines change the order in which the
drivers are registered.  Currently the usb-serial drivers are all
registered first and the usb_driver is done last, which leaves a
window for problems.  A udev script may quickly add a new dynamic-ID
for a usb-serial driver, causing the corresponding usb_driver to be
probed.  If the usb_driver hasn't been registered yet then an oops
will occur.

The new routine prevents such problems by registering the usb_driver
first.  To insure that it gets probed properly for already-attached
serial devices, we call driver_attach() after all the usb-serial
drivers have been registered.

Along with adding the new routines, the patch modifies the "generic"
serial driver to use them.  Further patches will similarly modify all
the other in-tree USB serial drivers.

Signed-off-by: default avatarAlan Stern <stern@rowland.harvard.edu>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent c69263c6
Loading
Loading
Loading
Loading
+6 −11
Original line number Diff line number Diff line
@@ -54,7 +54,6 @@ static struct usb_driver generic_driver = {
	.probe =	generic_probe,
	.disconnect =	usb_serial_disconnect,
	.id_table =	generic_serial_ids,
	.no_dynamic_id =	1,
};

/* All of the device info needed for the Generic Serial Converter */
@@ -64,7 +63,6 @@ struct usb_serial_driver usb_serial_generic_device = {
		.name =		"generic",
	},
	.id_table =		generic_device_ids,
	.usb_driver = 		&generic_driver,
	.num_ports =		1,
	.disconnect =		usb_serial_generic_disconnect,
	.release =		usb_serial_generic_release,
@@ -73,6 +71,10 @@ struct usb_serial_driver usb_serial_generic_device = {
	.resume =		usb_serial_generic_resume,
};

static struct usb_serial_driver * const serial_drivers[] = {
	&usb_serial_generic_device, NULL
};

static int generic_probe(struct usb_interface *interface,
			       const struct usb_device_id *id)
{
@@ -97,13 +99,7 @@ int usb_serial_generic_register(int _debug)
		USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_PRODUCT;

	/* register our generic driver with ourselves */
	retval = usb_serial_register(&usb_serial_generic_device);
	if (retval)
		goto exit;
	retval = usb_register(&generic_driver);
	if (retval)
		usb_serial_deregister(&usb_serial_generic_device);
exit:
	retval = usb_serial_register_drivers(&generic_driver, serial_drivers);
#endif
	return retval;
}
@@ -112,8 +108,7 @@ void usb_serial_generic_deregister(void)
{
#ifdef CONFIG_USB_SERIAL_GENERIC
	/* remove our generic driver */
	usb_deregister(&generic_driver);
	usb_serial_deregister(&usb_serial_generic_device);
	usb_serial_deregister_drivers(&generic_driver, serial_drivers);
#endif
}

+75 −0
Original line number Diff line number Diff line
@@ -1338,6 +1338,11 @@ static void fixup_generic(struct usb_serial_driver *device)
	set_to_generic_if_null(device, prepare_write_buffer);
}

/*
 * The next two routines are mainly for internal use.
 * They are exported only for out-of-tree modules.
 * New drivers should call usb_serial_{de}register_drivers() instead.
 */
int usb_serial_register(struct usb_serial_driver *driver)
{
	int retval;
@@ -1386,6 +1391,76 @@ void usb_serial_deregister(struct usb_serial_driver *device)
}
EXPORT_SYMBOL_GPL(usb_serial_deregister);

/**
 * usb_serial_register_drivers - register drivers for a usb-serial module
 * @udriver: usb_driver used for matching devices/interfaces
 * @serial_drivers: NULL-terminated array of pointers to drivers to be registered
 *
 * Registers @udriver and all the drivers in the @serial_drivers array.
 * Automatically fills in the .no_dynamic_id field in @udriver and
 * the .usb_driver field in each serial driver.
 */
int usb_serial_register_drivers(struct usb_driver *udriver,
		struct usb_serial_driver * const serial_drivers[])
{
	int rc;
	const struct usb_device_id *saved_id_table;
	struct usb_serial_driver * const *sd;

	/*
	 * udriver must be registered before any of the serial drivers,
	 * because the store_new_id() routine for the serial drivers (in
	 * bus.c) probes udriver.
	 *
	 * Performance hack: We don't want udriver to be probed until
	 * the serial drivers are registered, because the probe would
	 * simply fail for lack of a matching serial driver.
	 * Therefore save off udriver's id_table until we are all set.
	 */
	saved_id_table = udriver->id_table;
	udriver->id_table = NULL;

	udriver->no_dynamic_id = 1;
	rc = usb_register(udriver);
	if (rc)
		return rc;

	for (sd = serial_drivers; *sd; ++sd) {
		(*sd)->usb_driver = udriver;
		rc = usb_serial_register(*sd);
		if (rc)
			goto failed;
	}

	/* Now restore udriver's id_table and look for matches */
	udriver->id_table = saved_id_table;
	rc = driver_attach(&udriver->drvwrap.driver);
	return 0;

 failed:
	while (sd-- > serial_drivers)
		usb_serial_deregister(*sd);
	usb_deregister(udriver);
	return rc;
}
EXPORT_SYMBOL_GPL(usb_serial_register_drivers);

/**
 * usb_serial_deregister_drivers - deregister drivers for a usb-serial module
 * @udriver: usb_driver to unregister
 * @serial_drivers: NULL-terminated array of pointers to drivers to be deregistered
 *
 * Deregisters @udriver and all the drivers in the @serial_drivers array.
 */
void usb_serial_deregister_drivers(struct usb_driver *udriver,
		struct usb_serial_driver * const serial_drivers[])
{
	for (; *serial_drivers; ++serial_drivers)
		usb_serial_deregister(*serial_drivers);
	usb_deregister(udriver);
}
EXPORT_SYMBOL_GPL(usb_serial_deregister_drivers);

/* Module information */
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
+9 −0
Original line number Diff line number Diff line
@@ -300,8 +300,17 @@ struct usb_serial_driver {
#define to_usb_serial_driver(d) \
	container_of(d, struct usb_serial_driver, driver)

/*
 * These two routines are kept only for backward compatibility.
 * Don't use them; call usb_serial_{de}register_drivers() instead.
 */
extern int  usb_serial_register(struct usb_serial_driver *driver);
extern void usb_serial_deregister(struct usb_serial_driver *driver);

extern int usb_serial_register_drivers(struct usb_driver *udriver,
		struct usb_serial_driver * const serial_drivers[]);
extern void usb_serial_deregister_drivers(struct usb_driver *udriver,
		struct usb_serial_driver * const serial_drivers[]);
extern void usb_serial_port_softint(struct usb_serial_port *port);

extern int usb_serial_probe(struct usb_interface *iface,