15
15
#include <linux/kdev_t.h>
16
16
#include <linux/module.h>
17
17
#include <linux/mutex.h>
18
+ #include <linux/slab.h>
18
19
#include <linux/types.h>
19
20
#include <linux/wait.h>
20
21
24
25
/* Provides a unique ID for each counter device */
25
26
static DEFINE_IDA (counter_ida );
26
27
28
+ struct counter_device_allochelper {
29
+ struct counter_device counter ;
30
+
31
+ /*
32
+ * This is cache line aligned to ensure private data behaves like if it
33
+ * were kmalloced separately.
34
+ */
35
+ unsigned long privdata [] ____cacheline_aligned ;
36
+ };
37
+
27
38
static void counter_device_release (struct device * dev )
28
39
{
29
40
struct counter_device * const counter =
30
41
container_of (dev , struct counter_device , dev );
31
42
32
43
counter_chrdev_remove (counter );
33
44
ida_free (& counter_ida , dev -> id );
45
+
46
+ if (!counter -> legacy_device )
47
+ kfree (container_of (counter , struct counter_device_allochelper , counter ));
34
48
}
35
49
36
50
static struct device_type counter_device_type = {
@@ -53,7 +67,14 @@ static dev_t counter_devt;
53
67
*/
54
68
void * counter_priv (const struct counter_device * const counter )
55
69
{
56
- return counter -> priv ;
70
+ if (counter -> legacy_device ) {
71
+ return counter -> priv ;
72
+ } else {
73
+ struct counter_device_allochelper * ch =
74
+ container_of (counter , struct counter_device_allochelper , counter );
75
+
76
+ return & ch -> privdata ;
77
+ }
57
78
}
58
79
EXPORT_SYMBOL_GPL (counter_priv );
59
80
@@ -74,6 +95,8 @@ int counter_register(struct counter_device *const counter)
74
95
int id ;
75
96
int err ;
76
97
98
+ counter -> legacy_device = true;
99
+
77
100
/* Acquire unique ID */
78
101
id = ida_alloc (& counter_ida , GFP_KERNEL );
79
102
if (id < 0 )
@@ -114,6 +137,95 @@ int counter_register(struct counter_device *const counter)
114
137
}
115
138
EXPORT_SYMBOL_GPL (counter_register );
116
139
140
+ /**
141
+ * counter_alloc - allocate a counter_device
142
+ * @sizeof_priv: size of the driver private data
143
+ *
144
+ * This is part one of counter registration. The structure is allocated
145
+ * dynamically to ensure the right lifetime for the embedded struct device.
146
+ *
147
+ * If this succeeds, call counter_put() to get rid of the counter_device again.
148
+ */
149
+ struct counter_device * counter_alloc (size_t sizeof_priv )
150
+ {
151
+ struct counter_device_allochelper * ch ;
152
+ struct counter_device * counter ;
153
+ struct device * dev ;
154
+ int err ;
155
+
156
+ ch = kzalloc (sizeof (* ch ) + sizeof_priv , GFP_KERNEL );
157
+ if (!ch ) {
158
+ err = - ENOMEM ;
159
+ goto err_alloc_ch ;
160
+ }
161
+
162
+ counter = & ch -> counter ;
163
+ dev = & counter -> dev ;
164
+
165
+ /* Acquire unique ID */
166
+ err = ida_alloc (& counter_ida , GFP_KERNEL );
167
+ if (err < 0 )
168
+ goto err_ida_alloc ;
169
+ dev -> id = err ;
170
+
171
+ mutex_init (& counter -> ops_exist_lock );
172
+ dev -> type = & counter_device_type ;
173
+ dev -> bus = & counter_bus_type ;
174
+ dev -> devt = MKDEV (MAJOR (counter_devt ), dev -> id );
175
+
176
+ err = counter_chrdev_add (counter );
177
+ if (err < 0 )
178
+ goto err_chrdev_add ;
179
+
180
+ device_initialize (dev );
181
+
182
+ return counter ;
183
+
184
+ err_chrdev_add :
185
+
186
+ ida_free (& counter_ida , dev -> id );
187
+ err_ida_alloc :
188
+
189
+ kfree (ch );
190
+ err_alloc_ch :
191
+
192
+ return ERR_PTR (err );
193
+ }
194
+ EXPORT_SYMBOL_GPL (counter_alloc );
195
+
196
+ void counter_put (struct counter_device * counter )
197
+ {
198
+ put_device (& counter -> dev );
199
+ }
200
+ EXPORT_SYMBOL_GPL (counter_put );
201
+
202
+ /**
203
+ * counter_add - complete registration of a counter
204
+ * @counter: the counter to add
205
+ *
206
+ * This is part two of counter registration.
207
+ *
208
+ * If this succeeds, call counter_unregister() to get rid of the counter_device again.
209
+ */
210
+ int counter_add (struct counter_device * counter )
211
+ {
212
+ int err ;
213
+ struct device * dev = & counter -> dev ;
214
+
215
+ if (counter -> parent ) {
216
+ dev -> parent = counter -> parent ;
217
+ dev -> of_node = counter -> parent -> of_node ;
218
+ }
219
+
220
+ err = counter_sysfs_add (counter );
221
+ if (err < 0 )
222
+ return err ;
223
+
224
+ /* implies device_add(dev) */
225
+ return cdev_device_add (& counter -> chrdev , dev );
226
+ }
227
+ EXPORT_SYMBOL_GPL (counter_add );
228
+
117
229
/**
118
230
* counter_unregister - unregister Counter from the system
119
231
* @counter: pointer to Counter to unregister
@@ -134,7 +246,8 @@ void counter_unregister(struct counter_device *const counter)
134
246
135
247
mutex_unlock (& counter -> ops_exist_lock );
136
248
137
- put_device (& counter -> dev );
249
+ if (counter -> legacy_device )
250
+ put_device (& counter -> dev );
138
251
}
139
252
EXPORT_SYMBOL_GPL (counter_unregister );
140
253
@@ -168,6 +281,57 @@ int devm_counter_register(struct device *dev,
168
281
}
169
282
EXPORT_SYMBOL_GPL (devm_counter_register );
170
283
284
+ static void devm_counter_put (void * counter )
285
+ {
286
+ counter_put (counter );
287
+ }
288
+
289
+ /**
290
+ * devm_counter_alloc - allocate a counter_device
291
+ * @dev: the device to register the release callback for
292
+ * @sizeof_priv: size of the driver private data
293
+ *
294
+ * This is the device managed version of counter_add(). It registers a cleanup
295
+ * callback to care for calling counter_put().
296
+ */
297
+ struct counter_device * devm_counter_alloc (struct device * dev , size_t sizeof_priv )
298
+ {
299
+ struct counter_device * counter ;
300
+ int err ;
301
+
302
+ counter = counter_alloc (sizeof_priv );
303
+ if (IS_ERR (counter ))
304
+ return counter ;
305
+
306
+ err = devm_add_action_or_reset (dev , devm_counter_put , counter );
307
+ if (err < 0 )
308
+ return ERR_PTR (err );
309
+
310
+ return counter ;
311
+ }
312
+ EXPORT_SYMBOL_GPL (devm_counter_alloc );
313
+
314
+ /**
315
+ * devm_counter_add - complete registration of a counter
316
+ * @dev: the device to register the release callback for
317
+ * @counter: the counter to add
318
+ *
319
+ * This is the device managed version of counter_add(). It registers a cleanup
320
+ * callback to care for calling counter_unregister().
321
+ */
322
+ int devm_counter_add (struct device * dev ,
323
+ struct counter_device * const counter )
324
+ {
325
+ int err ;
326
+
327
+ err = counter_add (counter );
328
+ if (err < 0 )
329
+ return err ;
330
+
331
+ return devm_add_action_or_reset (dev , devm_counter_release , counter );
332
+ }
333
+ EXPORT_SYMBOL_GPL (devm_counter_add );
334
+
171
335
#define COUNTER_DEV_MAX 256
172
336
173
337
static int __init counter_init (void )
0 commit comments