1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright 2008 by Karsten Keil <kkeil@novell.com> |
4 | */ |
5 | |
6 | #include <linux/slab.h> |
7 | #include <linux/types.h> |
8 | #include <linux/stddef.h> |
9 | #include <linux/module.h> |
10 | #include <linux/spinlock.h> |
11 | #include <linux/mISDNif.h> |
12 | #include "core.h" |
13 | |
14 | static u_int debug; |
15 | |
16 | MODULE_AUTHOR("Karsten Keil" ); |
17 | MODULE_LICENSE("GPL" ); |
18 | module_param(debug, uint, S_IRUGO | S_IWUSR); |
19 | |
20 | static u64 device_ids; |
21 | #define MAX_DEVICE_ID 63 |
22 | |
23 | static LIST_HEAD(Bprotocols); |
24 | static DEFINE_RWLOCK(bp_lock); |
25 | |
26 | static void mISDN_dev_release(struct device *dev) |
27 | { |
28 | /* nothing to do: the device is part of its parent's data structure */ |
29 | } |
30 | |
31 | static ssize_t id_show(struct device *dev, |
32 | struct device_attribute *attr, char *buf) |
33 | { |
34 | struct mISDNdevice *mdev = dev_to_mISDN(dev); |
35 | |
36 | if (!mdev) |
37 | return -ENODEV; |
38 | return sprintf(buf, fmt: "%d\n" , mdev->id); |
39 | } |
40 | static DEVICE_ATTR_RO(id); |
41 | |
42 | static ssize_t nrbchan_show(struct device *dev, |
43 | struct device_attribute *attr, char *buf) |
44 | { |
45 | struct mISDNdevice *mdev = dev_to_mISDN(dev); |
46 | |
47 | if (!mdev) |
48 | return -ENODEV; |
49 | return sprintf(buf, fmt: "%d\n" , mdev->nrbchan); |
50 | } |
51 | static DEVICE_ATTR_RO(nrbchan); |
52 | |
53 | static ssize_t d_protocols_show(struct device *dev, |
54 | struct device_attribute *attr, char *buf) |
55 | { |
56 | struct mISDNdevice *mdev = dev_to_mISDN(dev); |
57 | |
58 | if (!mdev) |
59 | return -ENODEV; |
60 | return sprintf(buf, fmt: "%d\n" , mdev->Dprotocols); |
61 | } |
62 | static DEVICE_ATTR_RO(d_protocols); |
63 | |
64 | static ssize_t b_protocols_show(struct device *dev, |
65 | struct device_attribute *attr, char *buf) |
66 | { |
67 | struct mISDNdevice *mdev = dev_to_mISDN(dev); |
68 | |
69 | if (!mdev) |
70 | return -ENODEV; |
71 | return sprintf(buf, fmt: "%d\n" , mdev->Bprotocols | get_all_Bprotocols()); |
72 | } |
73 | static DEVICE_ATTR_RO(b_protocols); |
74 | |
75 | static ssize_t protocol_show(struct device *dev, |
76 | struct device_attribute *attr, char *buf) |
77 | { |
78 | struct mISDNdevice *mdev = dev_to_mISDN(dev); |
79 | |
80 | if (!mdev) |
81 | return -ENODEV; |
82 | return sprintf(buf, fmt: "%d\n" , mdev->D.protocol); |
83 | } |
84 | static DEVICE_ATTR_RO(protocol); |
85 | |
86 | static ssize_t name_show(struct device *dev, |
87 | struct device_attribute *attr, char *buf) |
88 | { |
89 | strcpy(p: buf, q: dev_name(dev)); |
90 | return strlen(buf); |
91 | } |
92 | static DEVICE_ATTR_RO(name); |
93 | |
94 | #if 0 /* hangs */ |
95 | static ssize_t name_set(struct device *dev, struct device_attribute *attr, |
96 | const char *buf, size_t count) |
97 | { |
98 | int err = 0; |
99 | char *out = kmalloc(count + 1, GFP_KERNEL); |
100 | |
101 | if (!out) |
102 | return -ENOMEM; |
103 | |
104 | memcpy(out, buf, count); |
105 | if (count && out[count - 1] == '\n') |
106 | out[--count] = 0; |
107 | if (count) |
108 | err = device_rename(dev, out); |
109 | kfree(out); |
110 | |
111 | return (err < 0) ? err : count; |
112 | } |
113 | static DEVICE_ATTR_RW(name); |
114 | #endif |
115 | |
116 | static ssize_t channelmap_show(struct device *dev, |
117 | struct device_attribute *attr, char *buf) |
118 | { |
119 | struct mISDNdevice *mdev = dev_to_mISDN(dev); |
120 | char *bp = buf; |
121 | int i; |
122 | |
123 | for (i = 0; i <= mdev->nrbchan; i++) |
124 | *bp++ = test_channelmap(nr: i, map: mdev->channelmap) ? '1' : '0'; |
125 | |
126 | return bp - buf; |
127 | } |
128 | static DEVICE_ATTR_RO(channelmap); |
129 | |
130 | static struct attribute *mISDN_attrs[] = { |
131 | &dev_attr_id.attr, |
132 | &dev_attr_d_protocols.attr, |
133 | &dev_attr_b_protocols.attr, |
134 | &dev_attr_protocol.attr, |
135 | &dev_attr_channelmap.attr, |
136 | &dev_attr_nrbchan.attr, |
137 | &dev_attr_name.attr, |
138 | NULL, |
139 | }; |
140 | ATTRIBUTE_GROUPS(mISDN); |
141 | |
142 | static int mISDN_uevent(const struct device *dev, struct kobj_uevent_env *env) |
143 | { |
144 | const struct mISDNdevice *mdev = dev_to_mISDN(dev); |
145 | |
146 | if (!mdev) |
147 | return 0; |
148 | |
149 | if (add_uevent_var(env, format: "nchans=%d" , mdev->nrbchan)) |
150 | return -ENOMEM; |
151 | |
152 | return 0; |
153 | } |
154 | |
155 | static struct class mISDN_class = { |
156 | .name = "mISDN" , |
157 | .dev_uevent = mISDN_uevent, |
158 | .dev_groups = mISDN_groups, |
159 | .dev_release = mISDN_dev_release, |
160 | }; |
161 | |
162 | static int |
163 | _get_mdevice(struct device *dev, const void *id) |
164 | { |
165 | struct mISDNdevice *mdev = dev_to_mISDN(dev); |
166 | |
167 | if (!mdev) |
168 | return 0; |
169 | if (mdev->id != *(const u_int *)id) |
170 | return 0; |
171 | return 1; |
172 | } |
173 | |
174 | struct mISDNdevice |
175 | *get_mdevice(u_int id) |
176 | { |
177 | return dev_to_mISDN(dev: class_find_device(class: &mISDN_class, NULL, data: &id, |
178 | match: _get_mdevice)); |
179 | } |
180 | |
181 | static int |
182 | _get_mdevice_count(struct device *dev, void *cnt) |
183 | { |
184 | *(int *)cnt += 1; |
185 | return 0; |
186 | } |
187 | |
188 | int |
189 | get_mdevice_count(void) |
190 | { |
191 | int cnt = 0; |
192 | |
193 | class_for_each_device(class: &mISDN_class, NULL, data: &cnt, fn: _get_mdevice_count); |
194 | return cnt; |
195 | } |
196 | |
197 | static int |
198 | get_free_devid(void) |
199 | { |
200 | u_int i; |
201 | |
202 | for (i = 0; i <= MAX_DEVICE_ID; i++) |
203 | if (!test_and_set_bit(nr: i, addr: (u_long *)&device_ids)) |
204 | break; |
205 | if (i > MAX_DEVICE_ID) |
206 | return -EBUSY; |
207 | return i; |
208 | } |
209 | |
210 | int |
211 | mISDN_register_device(struct mISDNdevice *dev, |
212 | struct device *parent, char *name) |
213 | { |
214 | int err; |
215 | |
216 | err = get_free_devid(); |
217 | if (err < 0) |
218 | return err; |
219 | dev->id = err; |
220 | |
221 | device_initialize(dev: &dev->dev); |
222 | if (name && name[0]) |
223 | dev_set_name(dev: &dev->dev, name: "%s" , name); |
224 | else |
225 | dev_set_name(dev: &dev->dev, name: "mISDN%d" , dev->id); |
226 | if (debug & DEBUG_CORE) |
227 | printk(KERN_DEBUG "mISDN_register %s %d\n" , |
228 | dev_name(&dev->dev), dev->id); |
229 | dev->dev.class = &mISDN_class; |
230 | |
231 | err = create_stack(dev); |
232 | if (err) |
233 | goto error1; |
234 | |
235 | dev->dev.platform_data = dev; |
236 | dev->dev.parent = parent; |
237 | dev_set_drvdata(dev: &dev->dev, data: dev); |
238 | |
239 | err = device_add(dev: &dev->dev); |
240 | if (err) |
241 | goto error3; |
242 | return 0; |
243 | |
244 | error3: |
245 | delete_stack(dev); |
246 | error1: |
247 | put_device(dev: &dev->dev); |
248 | return err; |
249 | |
250 | } |
251 | EXPORT_SYMBOL(mISDN_register_device); |
252 | |
253 | void |
254 | mISDN_unregister_device(struct mISDNdevice *dev) { |
255 | if (debug & DEBUG_CORE) |
256 | printk(KERN_DEBUG "mISDN_unregister %s %d\n" , |
257 | dev_name(&dev->dev), dev->id); |
258 | /* sysfs_remove_link(&dev->dev.kobj, "device"); */ |
259 | device_del(dev: &dev->dev); |
260 | dev_set_drvdata(dev: &dev->dev, NULL); |
261 | |
262 | test_and_clear_bit(nr: dev->id, addr: (u_long *)&device_ids); |
263 | delete_stack(dev); |
264 | put_device(dev: &dev->dev); |
265 | } |
266 | EXPORT_SYMBOL(mISDN_unregister_device); |
267 | |
268 | u_int |
269 | get_all_Bprotocols(void) |
270 | { |
271 | struct Bprotocol *bp; |
272 | u_int m = 0; |
273 | |
274 | read_lock(&bp_lock); |
275 | list_for_each_entry(bp, &Bprotocols, list) |
276 | m |= bp->Bprotocols; |
277 | read_unlock(&bp_lock); |
278 | return m; |
279 | } |
280 | |
281 | struct Bprotocol * |
282 | get_Bprotocol4mask(u_int m) |
283 | { |
284 | struct Bprotocol *bp; |
285 | |
286 | read_lock(&bp_lock); |
287 | list_for_each_entry(bp, &Bprotocols, list) |
288 | if (bp->Bprotocols & m) { |
289 | read_unlock(&bp_lock); |
290 | return bp; |
291 | } |
292 | read_unlock(&bp_lock); |
293 | return NULL; |
294 | } |
295 | |
296 | struct Bprotocol * |
297 | get_Bprotocol4id(u_int id) |
298 | { |
299 | u_int m; |
300 | |
301 | if (id < ISDN_P_B_START || id > 63) { |
302 | printk(KERN_WARNING "%s id not in range %d\n" , |
303 | __func__, id); |
304 | return NULL; |
305 | } |
306 | m = 1 << (id & ISDN_P_B_MASK); |
307 | return get_Bprotocol4mask(m); |
308 | } |
309 | |
310 | int |
311 | mISDN_register_Bprotocol(struct Bprotocol *bp) |
312 | { |
313 | u_long flags; |
314 | struct Bprotocol *old; |
315 | |
316 | if (debug & DEBUG_CORE) |
317 | printk(KERN_DEBUG "%s: %s/%x\n" , __func__, |
318 | bp->name, bp->Bprotocols); |
319 | old = get_Bprotocol4mask(m: bp->Bprotocols); |
320 | if (old) { |
321 | printk(KERN_WARNING |
322 | "register duplicate protocol old %s/%x new %s/%x\n" , |
323 | old->name, old->Bprotocols, bp->name, bp->Bprotocols); |
324 | return -EBUSY; |
325 | } |
326 | write_lock_irqsave(&bp_lock, flags); |
327 | list_add_tail(new: &bp->list, head: &Bprotocols); |
328 | write_unlock_irqrestore(&bp_lock, flags); |
329 | return 0; |
330 | } |
331 | EXPORT_SYMBOL(mISDN_register_Bprotocol); |
332 | |
333 | void |
334 | mISDN_unregister_Bprotocol(struct Bprotocol *bp) |
335 | { |
336 | u_long flags; |
337 | |
338 | if (debug & DEBUG_CORE) |
339 | printk(KERN_DEBUG "%s: %s/%x\n" , __func__, bp->name, |
340 | bp->Bprotocols); |
341 | write_lock_irqsave(&bp_lock, flags); |
342 | list_del(entry: &bp->list); |
343 | write_unlock_irqrestore(&bp_lock, flags); |
344 | } |
345 | EXPORT_SYMBOL(mISDN_unregister_Bprotocol); |
346 | |
347 | static const char *msg_no_channel = "<no channel>" ; |
348 | static const char *msg_no_stack = "<no stack>" ; |
349 | static const char *msg_no_stackdev = "<no stack device>" ; |
350 | |
351 | const char *mISDNDevName4ch(struct mISDNchannel *ch) |
352 | { |
353 | if (!ch) |
354 | return msg_no_channel; |
355 | if (!ch->st) |
356 | return msg_no_stack; |
357 | if (!ch->st->dev) |
358 | return msg_no_stackdev; |
359 | return dev_name(dev: &ch->st->dev->dev); |
360 | }; |
361 | EXPORT_SYMBOL(mISDNDevName4ch); |
362 | |
363 | static int |
364 | mISDNInit(void) |
365 | { |
366 | int err; |
367 | |
368 | printk(KERN_INFO "Modular ISDN core version %d.%d.%d\n" , |
369 | MISDN_MAJOR_VERSION, MISDN_MINOR_VERSION, MISDN_RELEASE); |
370 | mISDN_init_clock(&debug); |
371 | mISDN_initstack(&debug); |
372 | err = class_register(class: &mISDN_class); |
373 | if (err) |
374 | goto error1; |
375 | err = mISDN_inittimer(&debug); |
376 | if (err) |
377 | goto error2; |
378 | err = Isdnl1_Init(&debug); |
379 | if (err) |
380 | goto error3; |
381 | err = Isdnl2_Init(&debug); |
382 | if (err) |
383 | goto error4; |
384 | err = misdn_sock_init(&debug); |
385 | if (err) |
386 | goto error5; |
387 | return 0; |
388 | |
389 | error5: |
390 | Isdnl2_cleanup(); |
391 | error4: |
392 | Isdnl1_cleanup(); |
393 | error3: |
394 | mISDN_timer_cleanup(); |
395 | error2: |
396 | class_unregister(class: &mISDN_class); |
397 | error1: |
398 | return err; |
399 | } |
400 | |
401 | static void mISDN_cleanup(void) |
402 | { |
403 | misdn_sock_cleanup(); |
404 | Isdnl2_cleanup(); |
405 | Isdnl1_cleanup(); |
406 | mISDN_timer_cleanup(); |
407 | class_unregister(class: &mISDN_class); |
408 | |
409 | printk(KERN_DEBUG "mISDNcore unloaded\n" ); |
410 | } |
411 | |
412 | module_init(mISDNInit); |
413 | module_exit(mISDN_cleanup); |
414 | |