diff options
author | Tilman Schmidt <tilman@imap.cc> | 2008-02-06 04:38:29 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2008-02-06 13:41:12 -0500 |
commit | e468c04894f36045cf93d1384183a461014b6840 (patch) | |
tree | b734bbc4ee65f8282de5299dc200f47ea466067a /drivers/isdn/gigaset/common.c | |
parent | 9d4bee2b9de9e30057a860d2d6794f874caffc5e (diff) |
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: Tilman 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: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers/isdn/gigaset/common.c')
-rw-r--r-- | drivers/isdn/gigaset/common.c | 105 |
1 files changed, 29 insertions, 76 deletions
diff --git a/drivers/isdn/gigaset/common.c b/drivers/isdn/gigaset/common.c index f8f7d7e553bf..aacedec4986f 100644 --- a/drivers/isdn/gigaset/common.c +++ b/drivers/isdn/gigaset/common.c | |||
@@ -31,7 +31,6 @@ MODULE_PARM_DESC(debug, "debug level"); | |||
31 | /* driver state flags */ | 31 | /* driver state flags */ |
32 | #define VALID_MINOR 0x01 | 32 | #define VALID_MINOR 0x01 |
33 | #define VALID_ID 0x02 | 33 | #define VALID_ID 0x02 |
34 | #define ASSIGNED 0x04 | ||
35 | 34 | ||
36 | void gigaset_dbg_buffer(enum debuglevel level, const unsigned char *msg, | 35 | void gigaset_dbg_buffer(enum debuglevel level, const unsigned char *msg, |
37 | size_t len, const unsigned char *buf) | 36 | size_t len, const unsigned char *buf) |
@@ -178,7 +177,7 @@ int gigaset_get_channel(struct bc_state *bcs) | |||
178 | unsigned long flags; | 177 | unsigned long flags; |
179 | 178 | ||
180 | spin_lock_irqsave(&bcs->cs->lock, flags); | 179 | spin_lock_irqsave(&bcs->cs->lock, flags); |
181 | if (bcs->use_count) { | 180 | if (bcs->use_count || !try_module_get(bcs->cs->driver->owner)) { |
182 | gig_dbg(DEBUG_ANY, "could not allocate channel %d", | 181 | gig_dbg(DEBUG_ANY, "could not allocate channel %d", |
183 | bcs->channel); | 182 | bcs->channel); |
184 | spin_unlock_irqrestore(&bcs->cs->lock, flags); | 183 | spin_unlock_irqrestore(&bcs->cs->lock, flags); |
@@ -203,6 +202,7 @@ void gigaset_free_channel(struct bc_state *bcs) | |||
203 | } | 202 | } |
204 | --bcs->use_count; | 203 | --bcs->use_count; |
205 | bcs->busy = 0; | 204 | bcs->busy = 0; |
205 | module_put(bcs->cs->driver->owner); | ||
206 | gig_dbg(DEBUG_ANY, "freed channel %d", bcs->channel); | 206 | gig_dbg(DEBUG_ANY, "freed channel %d", bcs->channel); |
207 | spin_unlock_irqrestore(&bcs->cs->lock, flags); | 207 | spin_unlock_irqrestore(&bcs->cs->lock, flags); |
208 | } | 208 | } |
@@ -356,31 +356,28 @@ static struct cardstate *alloc_cs(struct gigaset_driver *drv) | |||
356 | { | 356 | { |
357 | unsigned long flags; | 357 | unsigned long flags; |
358 | unsigned i; | 358 | unsigned i; |
359 | struct cardstate *cs; | ||
359 | struct cardstate *ret = NULL; | 360 | struct cardstate *ret = NULL; |
360 | 361 | ||
361 | spin_lock_irqsave(&drv->lock, flags); | 362 | spin_lock_irqsave(&drv->lock, flags); |
363 | if (drv->blocked) | ||
364 | goto exit; | ||
362 | for (i = 0; i < drv->minors; ++i) { | 365 | for (i = 0; i < drv->minors; ++i) { |
363 | if (!(drv->flags[i] & VALID_MINOR)) { | 366 | cs = drv->cs + i; |
364 | if (try_module_get(drv->owner)) { | 367 | if (!(cs->flags & VALID_MINOR)) { |
365 | drv->flags[i] = VALID_MINOR; | 368 | cs->flags = VALID_MINOR; |
366 | ret = drv->cs + i; | 369 | ret = cs; |
367 | } | ||
368 | break; | 370 | break; |
369 | } | 371 | } |
370 | } | 372 | } |
373 | exit: | ||
371 | spin_unlock_irqrestore(&drv->lock, flags); | 374 | spin_unlock_irqrestore(&drv->lock, flags); |
372 | return ret; | 375 | return ret; |
373 | } | 376 | } |
374 | 377 | ||
375 | static void free_cs(struct cardstate *cs) | 378 | static void free_cs(struct cardstate *cs) |
376 | { | 379 | { |
377 | unsigned long flags; | 380 | cs->flags = 0; |
378 | struct gigaset_driver *drv = cs->driver; | ||
379 | spin_lock_irqsave(&drv->lock, flags); | ||
380 | if (drv->flags[cs->minor_index] & VALID_MINOR) | ||
381 | module_put(drv->owner); | ||
382 | drv->flags[cs->minor_index] = 0; | ||
383 | spin_unlock_irqrestore(&drv->lock, flags); | ||
384 | } | 381 | } |
385 | 382 | ||
386 | static void make_valid(struct cardstate *cs, unsigned mask) | 383 | static void make_valid(struct cardstate *cs, unsigned mask) |
@@ -388,7 +385,7 @@ static void make_valid(struct cardstate *cs, unsigned mask) | |||
388 | unsigned long flags; | 385 | unsigned long flags; |
389 | struct gigaset_driver *drv = cs->driver; | 386 | struct gigaset_driver *drv = cs->driver; |
390 | spin_lock_irqsave(&drv->lock, flags); | 387 | spin_lock_irqsave(&drv->lock, flags); |
391 | drv->flags[cs->minor_index] |= mask; | 388 | cs->flags |= mask; |
392 | spin_unlock_irqrestore(&drv->lock, flags); | 389 | spin_unlock_irqrestore(&drv->lock, flags); |
393 | } | 390 | } |
394 | 391 | ||
@@ -397,7 +394,7 @@ static void make_invalid(struct cardstate *cs, unsigned mask) | |||
397 | unsigned long flags; | 394 | unsigned long flags; |
398 | struct gigaset_driver *drv = cs->driver; | 395 | struct gigaset_driver *drv = cs->driver; |
399 | spin_lock_irqsave(&drv->lock, flags); | 396 | spin_lock_irqsave(&drv->lock, flags); |
400 | drv->flags[cs->minor_index] &= ~mask; | 397 | cs->flags &= ~mask; |
401 | spin_unlock_irqrestore(&drv->lock, flags); | 398 | spin_unlock_irqrestore(&drv->lock, flags); |
402 | } | 399 | } |
403 | 400 | ||
@@ -893,10 +890,17 @@ error: | |||
893 | } | 890 | } |
894 | EXPORT_SYMBOL_GPL(gigaset_start); | 891 | EXPORT_SYMBOL_GPL(gigaset_start); |
895 | 892 | ||
896 | void gigaset_shutdown(struct cardstate *cs) | 893 | /* gigaset_shutdown |
894 | * check if a device is associated to the cardstate structure and stop it | ||
895 | * return value: 0 if ok, -1 if no device was associated | ||
896 | */ | ||
897 | int gigaset_shutdown(struct cardstate *cs) | ||
897 | { | 898 | { |
898 | mutex_lock(&cs->mutex); | 899 | mutex_lock(&cs->mutex); |
899 | 900 | ||
901 | if (!(cs->flags & VALID_MINOR)) | ||
902 | return -1; | ||
903 | |||
900 | cs->waiting = 1; | 904 | cs->waiting = 1; |
901 | 905 | ||
902 | if (!gigaset_add_event(cs, &cs->at_state, EV_SHUTDOWN, NULL, 0, NULL)) { | 906 | if (!gigaset_add_event(cs, &cs->at_state, EV_SHUTDOWN, NULL, 0, NULL)) { |
@@ -913,6 +917,7 @@ void gigaset_shutdown(struct cardstate *cs) | |||
913 | 917 | ||
914 | exit: | 918 | exit: |
915 | mutex_unlock(&cs->mutex); | 919 | mutex_unlock(&cs->mutex); |
920 | return 0; | ||
916 | } | 921 | } |
917 | EXPORT_SYMBOL_GPL(gigaset_shutdown); | 922 | EXPORT_SYMBOL_GPL(gigaset_shutdown); |
918 | 923 | ||
@@ -954,13 +959,11 @@ struct cardstate *gigaset_get_cs_by_id(int id) | |||
954 | list_for_each_entry(drv, &drivers, list) { | 959 | list_for_each_entry(drv, &drivers, list) { |
955 | spin_lock(&drv->lock); | 960 | spin_lock(&drv->lock); |
956 | for (i = 0; i < drv->minors; ++i) { | 961 | for (i = 0; i < drv->minors; ++i) { |
957 | if (drv->flags[i] & VALID_ID) { | 962 | cs = drv->cs + i; |
958 | cs = drv->cs + i; | 963 | if ((cs->flags & VALID_ID) && cs->myid == id) { |
959 | if (cs->myid == id) | 964 | ret = cs; |
960 | ret = cs; | ||
961 | } | ||
962 | if (ret) | ||
963 | break; | 965 | break; |
966 | } | ||
964 | } | 967 | } |
965 | spin_unlock(&drv->lock); | 968 | spin_unlock(&drv->lock); |
966 | if (ret) | 969 | if (ret) |
@@ -983,10 +986,9 @@ void gigaset_debugdrivers(void) | |||
983 | spin_lock(&drv->lock); | 986 | spin_lock(&drv->lock); |
984 | for (i = 0; i < drv->minors; ++i) { | 987 | for (i = 0; i < drv->minors; ++i) { |
985 | gig_dbg(DEBUG_DRIVER, " index %u", i); | 988 | gig_dbg(DEBUG_DRIVER, " index %u", i); |
986 | gig_dbg(DEBUG_DRIVER, " flags 0x%02x", | ||
987 | drv->flags[i]); | ||
988 | cs = drv->cs + i; | 989 | cs = drv->cs + i; |
989 | gig_dbg(DEBUG_DRIVER, " cardstate %p", cs); | 990 | gig_dbg(DEBUG_DRIVER, " cardstate %p", cs); |
991 | gig_dbg(DEBUG_DRIVER, " flags 0x%02x", cs->flags); | ||
990 | gig_dbg(DEBUG_DRIVER, " minor_index %u", | 992 | gig_dbg(DEBUG_DRIVER, " minor_index %u", |
991 | cs->minor_index); | 993 | cs->minor_index); |
992 | gig_dbg(DEBUG_DRIVER, " driver %p", cs->driver); | 994 | gig_dbg(DEBUG_DRIVER, " driver %p", cs->driver); |
@@ -1010,7 +1012,7 @@ static struct cardstate *gigaset_get_cs_by_minor(unsigned minor) | |||
1010 | continue; | 1012 | continue; |
1011 | index = minor - drv->minor; | 1013 | index = minor - drv->minor; |
1012 | spin_lock(&drv->lock); | 1014 | spin_lock(&drv->lock); |
1013 | if (drv->flags[index] & VALID_MINOR) | 1015 | if (drv->cs[index].flags & VALID_MINOR) |
1014 | ret = drv->cs + index; | 1016 | ret = drv->cs + index; |
1015 | spin_unlock(&drv->lock); | 1017 | spin_unlock(&drv->lock); |
1016 | if (ret) | 1018 | if (ret) |
@@ -1038,7 +1040,6 @@ void gigaset_freedriver(struct gigaset_driver *drv) | |||
1038 | gigaset_if_freedriver(drv); | 1040 | gigaset_if_freedriver(drv); |
1039 | 1041 | ||
1040 | kfree(drv->cs); | 1042 | kfree(drv->cs); |
1041 | kfree(drv->flags); | ||
1042 | kfree(drv); | 1043 | kfree(drv); |
1043 | } | 1044 | } |
1044 | EXPORT_SYMBOL_GPL(gigaset_freedriver); | 1045 | EXPORT_SYMBOL_GPL(gigaset_freedriver); |
@@ -1080,12 +1081,8 @@ struct gigaset_driver *gigaset_initdriver(unsigned minor, unsigned minors, | |||
1080 | if (!drv->cs) | 1081 | if (!drv->cs) |
1081 | goto error; | 1082 | goto error; |
1082 | 1083 | ||
1083 | drv->flags = kmalloc(minors * sizeof *drv->flags, GFP_KERNEL); | ||
1084 | if (!drv->flags) | ||
1085 | goto error; | ||
1086 | |||
1087 | for (i = 0; i < minors; ++i) { | 1084 | for (i = 0; i < minors; ++i) { |
1088 | drv->flags[i] = 0; | 1085 | drv->cs[i].flags = 0; |
1089 | drv->cs[i].driver = drv; | 1086 | drv->cs[i].driver = drv; |
1090 | drv->cs[i].ops = drv->ops; | 1087 | drv->cs[i].ops = drv->ops; |
1091 | drv->cs[i].minor_index = i; | 1088 | drv->cs[i].minor_index = i; |
@@ -1106,53 +1103,9 @@ error: | |||
1106 | } | 1103 | } |
1107 | EXPORT_SYMBOL_GPL(gigaset_initdriver); | 1104 | EXPORT_SYMBOL_GPL(gigaset_initdriver); |
1108 | 1105 | ||
1109 | /* For drivers without fixed assignment device<->cardstate (usb) */ | ||
1110 | struct cardstate *gigaset_getunassignedcs(struct gigaset_driver *drv) | ||
1111 | { | ||
1112 | unsigned long flags; | ||
1113 | struct cardstate *cs = NULL; | ||
1114 | unsigned i; | ||
1115 | |||
1116 | spin_lock_irqsave(&drv->lock, flags); | ||
1117 | if (drv->blocked) | ||
1118 | goto exit; | ||
1119 | for (i = 0; i < drv->minors; ++i) { | ||
1120 | if ((drv->flags[i] & VALID_MINOR) && | ||
1121 | !(drv->flags[i] & ASSIGNED)) { | ||
1122 | drv->flags[i] |= ASSIGNED; | ||
1123 | cs = drv->cs + i; | ||
1124 | break; | ||
1125 | } | ||
1126 | } | ||
1127 | exit: | ||
1128 | spin_unlock_irqrestore(&drv->lock, flags); | ||
1129 | return cs; | ||
1130 | } | ||
1131 | EXPORT_SYMBOL_GPL(gigaset_getunassignedcs); | ||
1132 | |||
1133 | void gigaset_unassign(struct cardstate *cs) | ||
1134 | { | ||
1135 | unsigned long flags; | ||
1136 | unsigned *minor_flags; | ||
1137 | struct gigaset_driver *drv; | ||
1138 | |||
1139 | if (!cs) | ||
1140 | return; | ||
1141 | drv = cs->driver; | ||
1142 | spin_lock_irqsave(&drv->lock, flags); | ||
1143 | minor_flags = drv->flags + cs->minor_index; | ||
1144 | if (*minor_flags & VALID_MINOR) | ||
1145 | *minor_flags &= ~ASSIGNED; | ||
1146 | spin_unlock_irqrestore(&drv->lock, flags); | ||
1147 | } | ||
1148 | EXPORT_SYMBOL_GPL(gigaset_unassign); | ||
1149 | |||
1150 | void gigaset_blockdriver(struct gigaset_driver *drv) | 1106 | void gigaset_blockdriver(struct gigaset_driver *drv) |
1151 | { | 1107 | { |
1152 | unsigned long flags; | ||
1153 | spin_lock_irqsave(&drv->lock, flags); | ||
1154 | drv->blocked = 1; | 1108 | drv->blocked = 1; |
1155 | spin_unlock_irqrestore(&drv->lock, flags); | ||
1156 | } | 1109 | } |
1157 | EXPORT_SYMBOL_GPL(gigaset_blockdriver); | 1110 | EXPORT_SYMBOL_GPL(gigaset_blockdriver); |
1158 | 1111 | ||