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

Commit e468c048 authored by Tilman Schmidt's avatar Tilman Schmidt Committed by Linus Torvalds
Browse files

Gigaset: permit module unload



Fix the initialization and reference counting of the Gigaset driver modules
so that they can be unloaded when they are not actually in use.

Signed-off-by: default avatarTilman Schmidt <tilman@imap.cc>
Cc: Hansjoerg Lipp <hjlipp@web.de>
Cc: Greg KH <gregkh@suse.de>
Cc: Karsten Keil <kkeil@suse.de>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 9d4bee2b
Loading
Loading
Loading
Loading
+39 −41
Original line number Diff line number Diff line
@@ -134,7 +134,6 @@ struct bas_cardstate {


static struct gigaset_driver *driver = NULL;
static struct cardstate *cardstate = NULL;

/* usb specific object needed to register this driver with the usb subsystem */
static struct usb_driver gigaset_usb_driver = {
@@ -2247,11 +2246,11 @@ static int gigaset_probe(struct usb_interface *interface,
		 __func__, le16_to_cpu(udev->descriptor.idVendor),
		 le16_to_cpu(udev->descriptor.idProduct));

	cs = gigaset_getunassignedcs(driver);
	if (!cs) {
		dev_err(&udev->dev, "no free cardstate\n");
	/* allocate memory for our device state and intialize it */
	cs = gigaset_initcs(driver, BAS_CHANNELS, 0, 0, cidmode,
			    GIGASET_MODULENAME);
	if (!cs)
		return -ENODEV;
	}
	ucs = cs->hw.bas;

	/* save off device structure ptrs for later use */
@@ -2320,7 +2319,7 @@ static int gigaset_probe(struct usb_interface *interface,
error:
	freeurbs(cs);
	usb_set_intfdata(interface, NULL);
	gigaset_unassign(cs);
	gigaset_freecs(cs);
	return -ENODEV;
}

@@ -2362,7 +2361,7 @@ static void gigaset_disconnect(struct usb_interface *interface)
	ucs->interface = NULL;
	ucs->udev = NULL;
	cs->dev = NULL;
	gigaset_unassign(cs);
	gigaset_freecs(cs);
}

/* gigaset_suspend
@@ -2501,12 +2500,6 @@ static int __init bas_gigaset_init(void)
				       &gigops, THIS_MODULE)) == NULL)
		goto error;

	/* allocate memory for our device state and intialize it */
	cardstate = gigaset_initcs(driver, BAS_CHANNELS, 0, 0, cidmode,
				   GIGASET_MODULENAME);
	if (!cardstate)
		goto error;

	/* register this driver with the USB subsystem */
	result = usb_register(&gigaset_usb_driver);
	if (result < 0) {
@@ -2518,9 +2511,7 @@ static int __init bas_gigaset_init(void)
	info(DRIVER_DESC);
	return 0;

error:	if (cardstate)
		gigaset_freecs(cardstate);
	cardstate = NULL;
error:
	if (driver)
		gigaset_freedriver(driver);
	driver = NULL;
@@ -2532,43 +2523,50 @@ error: if (cardstate)
 */
static void __exit bas_gigaset_exit(void)
{
	struct bas_cardstate *ucs = cardstate->hw.bas;
	struct bas_cardstate *ucs;
	int i;

	gigaset_blockdriver(driver); /* => probe will fail
				      * => no gigaset_start any more
				      */

	gigaset_shutdown(cardstate);
	/* stop all connected devices */
	for (i = 0; i < driver->minors; i++) {
		if (gigaset_shutdown(driver->cs + i) < 0)
			continue;		/* no device */
		/* from now on, no isdn callback should be possible */

		/* close all still open channels */
		ucs = driver->cs[i].hw.bas;
		if (ucs->basstate & BS_B1OPEN) {
			gig_dbg(DEBUG_INIT, "closing B1 channel");
		usb_control_msg(ucs->udev, usb_sndctrlpipe(ucs->udev, 0),
				HD_CLOSE_B1CHANNEL, OUT_VENDOR_REQ, 0, 0,
				NULL, 0, BAS_TIMEOUT);
			usb_control_msg(ucs->udev,
					usb_sndctrlpipe(ucs->udev, 0),
					HD_CLOSE_B1CHANNEL, OUT_VENDOR_REQ,
					0, 0, NULL, 0, BAS_TIMEOUT);
		}
		if (ucs->basstate & BS_B2OPEN) {
			gig_dbg(DEBUG_INIT, "closing B2 channel");
		usb_control_msg(ucs->udev, usb_sndctrlpipe(ucs->udev, 0),
				HD_CLOSE_B2CHANNEL, OUT_VENDOR_REQ, 0, 0,
				NULL, 0, BAS_TIMEOUT);
			usb_control_msg(ucs->udev,
					usb_sndctrlpipe(ucs->udev, 0),
					HD_CLOSE_B2CHANNEL, OUT_VENDOR_REQ,
					0, 0, NULL, 0, BAS_TIMEOUT);
		}
		if (ucs->basstate & BS_ATOPEN) {
			gig_dbg(DEBUG_INIT, "closing AT channel");
		usb_control_msg(ucs->udev, usb_sndctrlpipe(ucs->udev, 0),
				HD_CLOSE_ATCHANNEL, OUT_VENDOR_REQ, 0, 0,
				NULL, 0, BAS_TIMEOUT);
			usb_control_msg(ucs->udev,
					usb_sndctrlpipe(ucs->udev, 0),
					HD_CLOSE_ATCHANNEL, OUT_VENDOR_REQ,
					0, 0, NULL, 0, BAS_TIMEOUT);
		}
		ucs->basstate = 0;
	}

	/* deregister this driver with the USB subsystem */
	usb_deregister(&gigaset_usb_driver);
	/* this will call the disconnect-callback */
	/* from now on, no disconnect/probe callback should be running */

	gigaset_freecs(cardstate);
	cardstate = NULL;
	gigaset_freedriver(driver);
	driver = NULL;
}
+29 −76
Original line number Diff line number Diff line
@@ -31,7 +31,6 @@ MODULE_PARM_DESC(debug, "debug level");
/* driver state flags */
#define VALID_MINOR	0x01
#define VALID_ID	0x02
#define ASSIGNED	0x04

void gigaset_dbg_buffer(enum debuglevel level, const unsigned char *msg,
			size_t len, const unsigned char *buf)
@@ -178,7 +177,7 @@ int gigaset_get_channel(struct bc_state *bcs)
	unsigned long flags;

	spin_lock_irqsave(&bcs->cs->lock, flags);
	if (bcs->use_count) {
	if (bcs->use_count || !try_module_get(bcs->cs->driver->owner)) {
		gig_dbg(DEBUG_ANY, "could not allocate channel %d",
			bcs->channel);
		spin_unlock_irqrestore(&bcs->cs->lock, flags);
@@ -203,6 +202,7 @@ void gigaset_free_channel(struct bc_state *bcs)
	}
	--bcs->use_count;
	bcs->busy = 0;
	module_put(bcs->cs->driver->owner);
	gig_dbg(DEBUG_ANY, "freed channel %d", bcs->channel);
	spin_unlock_irqrestore(&bcs->cs->lock, flags);
}
@@ -356,31 +356,28 @@ static struct cardstate *alloc_cs(struct gigaset_driver *drv)
{
	unsigned long flags;
	unsigned i;
	struct cardstate *cs;
	struct cardstate *ret = NULL;

	spin_lock_irqsave(&drv->lock, flags);
	if (drv->blocked)
		goto exit;
	for (i = 0; i < drv->minors; ++i) {
		if (!(drv->flags[i] & VALID_MINOR)) {
			if (try_module_get(drv->owner)) {
				drv->flags[i] = VALID_MINOR;
				ret = drv->cs + i;
			}
		cs = drv->cs + i;
		if (!(cs->flags & VALID_MINOR)) {
			cs->flags = VALID_MINOR;
			ret = cs;
			break;
		}
	}
exit:
	spin_unlock_irqrestore(&drv->lock, flags);
	return ret;
}

static void free_cs(struct cardstate *cs)
{
	unsigned long flags;
	struct gigaset_driver *drv = cs->driver;
	spin_lock_irqsave(&drv->lock, flags);
	if (drv->flags[cs->minor_index] & VALID_MINOR)
		module_put(drv->owner);
	drv->flags[cs->minor_index] = 0;
	spin_unlock_irqrestore(&drv->lock, flags);
	cs->flags = 0;
}

static void make_valid(struct cardstate *cs, unsigned mask)
@@ -388,7 +385,7 @@ static void make_valid(struct cardstate *cs, unsigned mask)
	unsigned long flags;
	struct gigaset_driver *drv = cs->driver;
	spin_lock_irqsave(&drv->lock, flags);
	drv->flags[cs->minor_index] |= mask;
	cs->flags |= mask;
	spin_unlock_irqrestore(&drv->lock, flags);
}

@@ -397,7 +394,7 @@ static void make_invalid(struct cardstate *cs, unsigned mask)
	unsigned long flags;
	struct gigaset_driver *drv = cs->driver;
	spin_lock_irqsave(&drv->lock, flags);
	drv->flags[cs->minor_index] &= ~mask;
	cs->flags &= ~mask;
	spin_unlock_irqrestore(&drv->lock, flags);
}

@@ -893,10 +890,17 @@ int gigaset_start(struct cardstate *cs)
}
EXPORT_SYMBOL_GPL(gigaset_start);

void gigaset_shutdown(struct cardstate *cs)
/* gigaset_shutdown
 * check if a device is associated to the cardstate structure and stop it
 * return value: 0 if ok, -1 if no device was associated
 */
int gigaset_shutdown(struct cardstate *cs)
{
	mutex_lock(&cs->mutex);

	if (!(cs->flags & VALID_MINOR))
		return -1;

	cs->waiting = 1;

	if (!gigaset_add_event(cs, &cs->at_state, EV_SHUTDOWN, NULL, 0, NULL)) {
@@ -913,6 +917,7 @@ void gigaset_shutdown(struct cardstate *cs)

exit:
	mutex_unlock(&cs->mutex);
	return 0;
}
EXPORT_SYMBOL_GPL(gigaset_shutdown);

@@ -954,14 +959,12 @@ struct cardstate *gigaset_get_cs_by_id(int id)
	list_for_each_entry(drv, &drivers, list) {
		spin_lock(&drv->lock);
		for (i = 0; i < drv->minors; ++i) {
			if (drv->flags[i] & VALID_ID) {
			cs = drv->cs + i;
				if (cs->myid == id)
			if ((cs->flags & VALID_ID) && cs->myid == id) {
				ret = cs;
			}
			if (ret)
				break;
			}
		}
		spin_unlock(&drv->lock);
		if (ret)
			break;
@@ -983,10 +986,9 @@ void gigaset_debugdrivers(void)
		spin_lock(&drv->lock);
		for (i = 0; i < drv->minors; ++i) {
			gig_dbg(DEBUG_DRIVER, "  index %u", i);
			gig_dbg(DEBUG_DRIVER, "    flags 0x%02x",
				drv->flags[i]);
			cs = drv->cs + i;
			gig_dbg(DEBUG_DRIVER, "    cardstate %p", cs);
			gig_dbg(DEBUG_DRIVER, "    flags 0x%02x", cs->flags);
			gig_dbg(DEBUG_DRIVER, "    minor_index %u",
				cs->minor_index);
			gig_dbg(DEBUG_DRIVER, "    driver %p", cs->driver);
@@ -1010,7 +1012,7 @@ static struct cardstate *gigaset_get_cs_by_minor(unsigned minor)
			continue;
		index = minor - drv->minor;
		spin_lock(&drv->lock);
		if (drv->flags[index] & VALID_MINOR)
		if (drv->cs[index].flags & VALID_MINOR)
			ret = drv->cs + index;
		spin_unlock(&drv->lock);
		if (ret)
@@ -1038,7 +1040,6 @@ void gigaset_freedriver(struct gigaset_driver *drv)
	gigaset_if_freedriver(drv);

	kfree(drv->cs);
	kfree(drv->flags);
	kfree(drv);
}
EXPORT_SYMBOL_GPL(gigaset_freedriver);
@@ -1080,12 +1081,8 @@ struct gigaset_driver *gigaset_initdriver(unsigned minor, unsigned minors,
	if (!drv->cs)
		goto error;

	drv->flags = kmalloc(minors * sizeof *drv->flags, GFP_KERNEL);
	if (!drv->flags)
		goto error;

	for (i = 0; i < minors; ++i) {
		drv->flags[i] = 0;
		drv->cs[i].flags = 0;
		drv->cs[i].driver = drv;
		drv->cs[i].ops = drv->ops;
		drv->cs[i].minor_index = i;
@@ -1106,53 +1103,9 @@ struct gigaset_driver *gigaset_initdriver(unsigned minor, unsigned minors,
}
EXPORT_SYMBOL_GPL(gigaset_initdriver);

/* For drivers without fixed assignment device<->cardstate (usb) */
struct cardstate *gigaset_getunassignedcs(struct gigaset_driver *drv)
{
	unsigned long flags;
	struct cardstate *cs = NULL;
	unsigned i;

	spin_lock_irqsave(&drv->lock, flags);
	if (drv->blocked)
		goto exit;
	for (i = 0; i < drv->minors; ++i) {
		if ((drv->flags[i] & VALID_MINOR) &&
		    !(drv->flags[i] & ASSIGNED)) {
			drv->flags[i] |= ASSIGNED;
			cs = drv->cs + i;
			break;
		}
	}
exit:
	spin_unlock_irqrestore(&drv->lock, flags);
	return cs;
}
EXPORT_SYMBOL_GPL(gigaset_getunassignedcs);

void gigaset_unassign(struct cardstate *cs)
{
	unsigned long flags;
	unsigned *minor_flags;
	struct gigaset_driver *drv;

	if (!cs)
		return;
	drv = cs->driver;
	spin_lock_irqsave(&drv->lock, flags);
	minor_flags = drv->flags + cs->minor_index;
	if (*minor_flags & VALID_MINOR)
		*minor_flags &= ~ASSIGNED;
	spin_unlock_irqrestore(&drv->lock, flags);
}
EXPORT_SYMBOL_GPL(gigaset_unassign);

void gigaset_blockdriver(struct gigaset_driver *drv)
{
	unsigned long flags;
	spin_lock_irqsave(&drv->lock, flags);
	drv->blocked = 1;
	spin_unlock_irqrestore(&drv->lock, flags);
}
EXPORT_SYMBOL_GPL(gigaset_blockdriver);

+2 −6
Original line number Diff line number Diff line
@@ -435,6 +435,7 @@ struct cardstate {
	unsigned minor_index;
	struct device *dev;
	struct device *tty_dev;
	unsigned flags;

	const struct gigaset_ops *ops;

@@ -539,7 +540,6 @@ struct gigaset_driver {
	unsigned minor;
	unsigned minors;
	struct cardstate *cs;
	unsigned *flags;
	int blocked;

	const struct gigaset_ops *ops;
@@ -767,10 +767,6 @@ void gigaset_freedriver(struct gigaset_driver *drv);
void gigaset_debugdrivers(void);
struct cardstate *gigaset_get_cs_by_tty(struct tty_struct *tty);
struct cardstate *gigaset_get_cs_by_id(int id);

/* For drivers without fixed assignment device<->cardstate (usb) */
struct cardstate *gigaset_getunassignedcs(struct gigaset_driver *drv);
void gigaset_unassign(struct cardstate *cs);
void gigaset_blockdriver(struct gigaset_driver *drv);

/* Allocate and initialize card state. Calls hardware dependent
@@ -789,7 +785,7 @@ int gigaset_start(struct cardstate *cs);
void gigaset_stop(struct cardstate *cs);

/* Tell common.c that the driver is being unloaded. */
void gigaset_shutdown(struct cardstate *cs);
int gigaset_shutdown(struct cardstate *cs);

/* Tell common.c that an skb has been sent. */
void gigaset_skb_sent(struct bc_state *bcs, struct sk_buff *skb);
+3 −1
Original line number Diff line number Diff line
@@ -161,7 +161,7 @@ static int if_open(struct tty_struct *tty, struct file *filp)
	tty->driver_data = NULL;

	cs = gigaset_get_cs_by_tty(tty);
	if (!cs)
	if (!cs || !try_module_get(cs->driver->owner))
		return -ENODEV;

	if (mutex_lock_interruptible(&cs->mutex))
@@ -207,6 +207,8 @@ static void if_close(struct tty_struct *tty, struct file *filp)
	}

	mutex_unlock(&cs->mutex);

	module_put(cs->driver->owner);
}

static int if_ioctl(struct tty_struct *tty, struct file *file,
+12 −18
Original line number Diff line number Diff line
@@ -115,7 +115,6 @@ static int gigaset_resume(struct usb_interface *intf);
static int gigaset_pre_reset(struct usb_interface *intf);

static struct gigaset_driver *driver = NULL;
static struct cardstate *cardstate = NULL;

/* usb specific object needed to register this driver with the usb subsystem */
static struct usb_driver gigaset_usb_driver = {
@@ -727,11 +726,10 @@ static int gigaset_probe(struct usb_interface *interface,

	dev_info(&udev->dev, "%s: Device matched ... !\n", __func__);

	cs = gigaset_getunassignedcs(driver);
	if (!cs) {
		dev_warn(&udev->dev, "no free cardstate\n");
	/* allocate memory for our device state and intialize it */
	cs = gigaset_initcs(driver, 1, 1, 0, cidmode, GIGASET_MODULENAME);
	if (!cs)
		return -ENODEV;
	}
	ucs = cs->hw.usb;

	/* save off device structure ptrs for later use */
@@ -818,7 +816,7 @@ static int gigaset_probe(struct usb_interface *interface,
	usb_put_dev(ucs->udev);
	ucs->udev = NULL;
	ucs->interface = NULL;
	gigaset_unassign(cs);
	gigaset_freecs(cs);
	return retval;
}

@@ -852,7 +850,7 @@ static void gigaset_disconnect(struct usb_interface *interface)
	ucs->interface = NULL;
	ucs->udev = NULL;
	cs->dev = NULL;
	gigaset_unassign(cs);
	gigaset_freecs(cs);
}

/* gigaset_suspend
@@ -934,11 +932,6 @@ static int __init usb_gigaset_init(void)
				       &ops, THIS_MODULE)) == NULL)
		goto error;

	/* allocate memory for our device state and intialize it */
	cardstate = gigaset_initcs(driver, 1, 1, 0, cidmode, GIGASET_MODULENAME);
	if (!cardstate)
		goto error;

	/* register this driver with the USB subsystem */
	result = usb_register(&gigaset_usb_driver);
	if (result < 0) {
@@ -951,9 +944,7 @@ static int __init usb_gigaset_init(void)
	info(DRIVER_DESC);
	return 0;

error:	if (cardstate)
		gigaset_freecs(cardstate);
	cardstate = NULL;
error:
	if (driver)
		gigaset_freedriver(driver);
	driver = NULL;
@@ -967,11 +958,16 @@ error: if (cardstate)
 */
static void __exit usb_gigaset_exit(void)
{
	int i;

	gigaset_blockdriver(driver); /* => probe will fail
				      * => no gigaset_start any more
				      */

	gigaset_shutdown(cardstate);
	/* stop all connected devices */
	for (i = 0; i < driver->minors; i++)
		gigaset_shutdown(driver->cs + i);

	/* from now on, no isdn callback should be possible */

	/* deregister this driver with the USB subsystem */
@@ -979,8 +975,6 @@ static void __exit usb_gigaset_exit(void)
	/* this will call the disconnect-callback */
	/* from now on, no disconnect/probe callback should be running */

	gigaset_freecs(cardstate);
	cardstate = NULL;
	gigaset_freedriver(driver);
	driver = NULL;
}