Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* $Id: kcapi.c,v 1.1.2.8 2004/03/26 19:57:20 armin Exp $ |
2 | * | |
3 | * Kernel CAPI 2.0 Module | |
4 | * | |
5 | * Copyright 1999 by Carsten Paeth <calle@calle.de> | |
6 | * Copyright 2002 by Kai Germaschewski <kai@germaschewski.name> | |
7 | * | |
8 | * This software may be used and distributed according to the terms | |
9 | * of the GNU General Public License, incorporated herein by reference. | |
10 | * | |
11 | */ | |
12 | ||
37772ac0 | 13 | #define AVMB1_COMPAT |
1da177e4 LT |
14 | |
15 | #include "kcapi.h" | |
16 | #include <linux/module.h> | |
17 | #include <linux/mm.h> | |
18 | #include <linux/interrupt.h> | |
19 | #include <linux/ioport.h> | |
20 | #include <linux/proc_fs.h> | |
d43c36dc | 21 | #include <linux/sched.h> |
1da177e4 LT |
22 | #include <linux/seq_file.h> |
23 | #include <linux/skbuff.h> | |
24 | #include <linux/workqueue.h> | |
25 | #include <linux/capi.h> | |
26 | #include <linux/kernelcapi.h> | |
27 | #include <linux/init.h> | |
28 | #include <linux/moduleparam.h> | |
29 | #include <linux/delay.h> | |
5a0e3ad6 | 30 | #include <linux/slab.h> |
1da177e4 LT |
31 | #include <asm/uaccess.h> |
32 | #include <linux/isdn/capicmd.h> | |
33 | #include <linux/isdn/capiutil.h> | |
37772ac0 | 34 | #ifdef AVMB1_COMPAT |
1da177e4 LT |
35 | #include <linux/b1lli.h> |
36 | #endif | |
9cdf1827 | 37 | #include <linux/mutex.h> |
88c896ef | 38 | #include <linux/rcupdate.h> |
1da177e4 | 39 | |
1da177e4 LT |
40 | static int showcapimsgs = 0; |
41 | ||
42 | MODULE_DESCRIPTION("CAPI4Linux: kernel CAPI layer"); | |
43 | MODULE_AUTHOR("Carsten Paeth"); | |
44 | MODULE_LICENSE("GPL"); | |
45 | module_param(showcapimsgs, uint, 0); | |
46 | ||
47 | /* ------------------------------------------------------------- */ | |
48 | ||
ef69bb2e | 49 | struct capictr_event { |
1da177e4 | 50 | struct work_struct work; |
ef69bb2e | 51 | unsigned int type; |
1da177e4 | 52 | u32 controller; |
1da177e4 LT |
53 | }; |
54 | ||
55 | /* ------------------------------------------------------------- */ | |
56 | ||
57 | static struct capi_version driver_version = {2, 0, 1, 1<<4}; | |
58 | static char driver_serial[CAPI_SERIAL_LEN] = "0004711"; | |
59 | static char capi_manufakturer[64] = "AVM Berlin"; | |
60 | ||
61 | #define NCCI2CTRL(ncci) (((ncci) >> 24) & 0x7f) | |
62 | ||
63 | LIST_HEAD(capi_drivers); | |
9717fb8b | 64 | DEFINE_MUTEX(capi_drivers_lock); |
1da177e4 | 65 | |
0ca3a017 JK |
66 | struct capi_ctr *capi_controller[CAPI_MAXCONTR]; |
67 | DEFINE_MUTEX(capi_controller_lock); | |
68 | ||
1da177e4 | 69 | struct capi20_appl *capi_applications[CAPI_MAXAPPL]; |
1da177e4 | 70 | |
52253031 | 71 | static int ncontrollers; |
1da177e4 | 72 | |
ef69bb2e JK |
73 | static BLOCKING_NOTIFIER_HEAD(ctr_notifier_list); |
74 | ||
1da177e4 LT |
75 | /* -------- controller ref counting -------------------------------------- */ |
76 | ||
77 | static inline struct capi_ctr * | |
52253031 | 78 | capi_ctr_get(struct capi_ctr *ctr) |
1da177e4 | 79 | { |
52253031 | 80 | if (!try_module_get(ctr->owner)) |
1da177e4 | 81 | return NULL; |
52253031 | 82 | return ctr; |
1da177e4 LT |
83 | } |
84 | ||
85 | static inline void | |
52253031 | 86 | capi_ctr_put(struct capi_ctr *ctr) |
1da177e4 | 87 | { |
52253031 | 88 | module_put(ctr->owner); |
1da177e4 LT |
89 | } |
90 | ||
91 | /* ------------------------------------------------------------- */ | |
92 | ||
93 | static inline struct capi_ctr *get_capi_ctr_by_nr(u16 contr) | |
94 | { | |
95 | if (contr - 1 >= CAPI_MAXCONTR) | |
96 | return NULL; | |
97 | ||
52253031 | 98 | return capi_controller[contr - 1]; |
1da177e4 LT |
99 | } |
100 | ||
101 | static inline struct capi20_appl *get_capi_appl_by_nr(u16 applid) | |
102 | { | |
103 | if (applid - 1 >= CAPI_MAXAPPL) | |
104 | return NULL; | |
105 | ||
88c896ef | 106 | return rcu_dereference(capi_applications[applid - 1]); |
1da177e4 LT |
107 | } |
108 | ||
109 | /* -------- util functions ------------------------------------ */ | |
110 | ||
111 | static inline int capi_cmd_valid(u8 cmd) | |
112 | { | |
113 | switch (cmd) { | |
114 | case CAPI_ALERT: | |
115 | case CAPI_CONNECT: | |
116 | case CAPI_CONNECT_ACTIVE: | |
117 | case CAPI_CONNECT_B3_ACTIVE: | |
118 | case CAPI_CONNECT_B3: | |
119 | case CAPI_CONNECT_B3_T90_ACTIVE: | |
120 | case CAPI_DATA_B3: | |
121 | case CAPI_DISCONNECT_B3: | |
122 | case CAPI_DISCONNECT: | |
123 | case CAPI_FACILITY: | |
124 | case CAPI_INFO: | |
125 | case CAPI_LISTEN: | |
126 | case CAPI_MANUFACTURER: | |
127 | case CAPI_RESET_B3: | |
128 | case CAPI_SELECT_B_PROTOCOL: | |
129 | return 1; | |
130 | } | |
131 | return 0; | |
132 | } | |
133 | ||
134 | static inline int capi_subcmd_valid(u8 subcmd) | |
135 | { | |
136 | switch (subcmd) { | |
137 | case CAPI_REQ: | |
138 | case CAPI_CONF: | |
139 | case CAPI_IND: | |
140 | case CAPI_RESP: | |
141 | return 1; | |
142 | } | |
143 | return 0; | |
144 | } | |
145 | ||
146 | /* ------------------------------------------------------------ */ | |
147 | ||
52253031 JK |
148 | static void |
149 | register_appl(struct capi_ctr *ctr, u16 applid, capi_register_params *rparam) | |
1da177e4 | 150 | { |
52253031 | 151 | ctr = capi_ctr_get(ctr); |
1da177e4 | 152 | |
52253031 JK |
153 | if (ctr) |
154 | ctr->register_appl(ctr, applid, rparam); | |
1da177e4 | 155 | else |
52253031 JK |
156 | printk(KERN_WARNING "%s: cannot get controller resources\n", |
157 | __func__); | |
1da177e4 LT |
158 | } |
159 | ||
160 | ||
52253031 | 161 | static void release_appl(struct capi_ctr *ctr, u16 applid) |
1da177e4 LT |
162 | { |
163 | DBG("applid %#x", applid); | |
164 | ||
52253031 JK |
165 | ctr->release_appl(ctr, applid); |
166 | capi_ctr_put(ctr); | |
1da177e4 LT |
167 | } |
168 | ||
1da177e4 LT |
169 | static void notify_up(u32 contr) |
170 | { | |
1da177e4 | 171 | struct capi20_appl *ap; |
3efecf7a | 172 | struct capi_ctr *ctr; |
1da177e4 LT |
173 | u16 applid; |
174 | ||
0ca3a017 JK |
175 | mutex_lock(&capi_controller_lock); |
176 | ||
3efecf7a | 177 | if (showcapimsgs & 1) |
1da177e4 | 178 | printk(KERN_DEBUG "kcapi: notify up contr %d\n", contr); |
3efecf7a JK |
179 | |
180 | ctr = get_capi_ctr_by_nr(contr); | |
181 | if (ctr) { | |
182 | if (ctr->state == CAPI_CTR_RUNNING) | |
0ca3a017 | 183 | goto unlock_out; |
3efecf7a JK |
184 | |
185 | ctr->state = CAPI_CTR_RUNNING; | |
186 | ||
187 | for (applid = 1; applid <= CAPI_MAXAPPL; applid++) { | |
188 | ap = get_capi_appl_by_nr(applid); | |
88c896ef | 189 | if (!ap) |
3efecf7a JK |
190 | continue; |
191 | register_appl(ctr, applid, &ap->rparam); | |
3efecf7a | 192 | } |
0ca3a017 JK |
193 | |
194 | wake_up_interruptible_all(&ctr->state_wait_queue); | |
3efecf7a | 195 | } else |
156f1ed6 | 196 | printk(KERN_WARNING "%s: invalid contr %d\n", __func__, contr); |
0ca3a017 JK |
197 | |
198 | unlock_out: | |
199 | mutex_unlock(&capi_controller_lock); | |
1da177e4 LT |
200 | } |
201 | ||
0ca3a017 | 202 | static void ctr_down(struct capi_ctr *ctr, int new_state) |
1da177e4 LT |
203 | { |
204 | struct capi20_appl *ap; | |
205 | u16 applid; | |
206 | ||
0ca3a017 | 207 | if (ctr->state == CAPI_CTR_DETECTED || ctr->state == CAPI_CTR_DETACHED) |
3efecf7a JK |
208 | return; |
209 | ||
0ca3a017 | 210 | ctr->state = new_state; |
3efecf7a JK |
211 | |
212 | memset(ctr->manu, 0, sizeof(ctr->manu)); | |
213 | memset(&ctr->version, 0, sizeof(ctr->version)); | |
214 | memset(&ctr->profile, 0, sizeof(ctr->profile)); | |
215 | memset(ctr->serial, 0, sizeof(ctr->serial)); | |
1da177e4 LT |
216 | |
217 | for (applid = 1; applid <= CAPI_MAXAPPL; applid++) { | |
218 | ap = get_capi_appl_by_nr(applid); | |
88c896ef | 219 | if (ap) |
3efecf7a | 220 | capi_ctr_put(ctr); |
1da177e4 | 221 | } |
0ca3a017 JK |
222 | |
223 | wake_up_interruptible_all(&ctr->state_wait_queue); | |
1da177e4 LT |
224 | } |
225 | ||
3efecf7a JK |
226 | static void notify_down(u32 contr) |
227 | { | |
228 | struct capi_ctr *ctr; | |
229 | ||
0ca3a017 JK |
230 | mutex_lock(&capi_controller_lock); |
231 | ||
3efecf7a JK |
232 | if (showcapimsgs & 1) |
233 | printk(KERN_DEBUG "kcapi: notify down contr %d\n", contr); | |
234 | ||
235 | ctr = get_capi_ctr_by_nr(contr); | |
236 | if (ctr) | |
0ca3a017 | 237 | ctr_down(ctr, CAPI_CTR_DETECTED); |
3efecf7a JK |
238 | else |
239 | printk(KERN_WARNING "%s: invalid contr %d\n", __func__, contr); | |
0ca3a017 JK |
240 | |
241 | mutex_unlock(&capi_controller_lock); | |
3efecf7a JK |
242 | } |
243 | ||
ef69bb2e JK |
244 | static int |
245 | notify_handler(struct notifier_block *nb, unsigned long val, void *v) | |
1da177e4 | 246 | { |
ef69bb2e | 247 | u32 contr = (long)v; |
1da177e4 | 248 | |
ef69bb2e JK |
249 | switch (val) { |
250 | case CAPICTR_UP: | |
251 | notify_up(contr); | |
1da177e4 | 252 | break; |
ef69bb2e JK |
253 | case CAPICTR_DOWN: |
254 | notify_down(contr); | |
1da177e4 LT |
255 | break; |
256 | } | |
ef69bb2e JK |
257 | return NOTIFY_OK; |
258 | } | |
259 | ||
260 | static void do_notify_work(struct work_struct *work) | |
261 | { | |
262 | struct capictr_event *event = | |
263 | container_of(work, struct capictr_event, work); | |
1da177e4 | 264 | |
ef69bb2e JK |
265 | blocking_notifier_call_chain(&ctr_notifier_list, event->type, |
266 | (void *)(long)event->controller); | |
267 | kfree(event); | |
1da177e4 LT |
268 | } |
269 | ||
270 | /* | |
271 | * The notifier will result in adding/deleteing of devices. Devices can | |
272 | * only removed in user process, not in bh. | |
273 | */ | |
ef69bb2e | 274 | static int notify_push(unsigned int event_type, u32 controller) |
1da177e4 | 275 | { |
ef69bb2e | 276 | struct capictr_event *event = kmalloc(sizeof(*event), GFP_ATOMIC); |
1da177e4 | 277 | |
ef69bb2e | 278 | if (!event) |
1da177e4 LT |
279 | return -ENOMEM; |
280 | ||
ef69bb2e JK |
281 | INIT_WORK(&event->work, do_notify_work); |
282 | event->type = event_type; | |
283 | event->controller = controller; | |
1da177e4 | 284 | |
ef69bb2e | 285 | schedule_work(&event->work); |
1da177e4 LT |
286 | return 0; |
287 | } | |
288 | ||
ef69bb2e JK |
289 | int register_capictr_notifier(struct notifier_block *nb) |
290 | { | |
291 | return blocking_notifier_chain_register(&ctr_notifier_list, nb); | |
292 | } | |
293 | EXPORT_SYMBOL_GPL(register_capictr_notifier); | |
294 | ||
295 | int unregister_capictr_notifier(struct notifier_block *nb) | |
296 | { | |
297 | return blocking_notifier_chain_unregister(&ctr_notifier_list, nb); | |
298 | } | |
299 | EXPORT_SYMBOL_GPL(unregister_capictr_notifier); | |
300 | ||
1da177e4 LT |
301 | /* -------- Receiver ------------------------------------------ */ |
302 | ||
c4028958 | 303 | static void recv_handler(struct work_struct *work) |
1da177e4 LT |
304 | { |
305 | struct sk_buff *skb; | |
c4028958 DH |
306 | struct capi20_appl *ap = |
307 | container_of(work, struct capi20_appl, recv_work); | |
1da177e4 LT |
308 | |
309 | if ((!ap) || (ap->release_in_progress)) | |
310 | return; | |
311 | ||
67837f23 | 312 | mutex_lock(&ap->recv_mtx); |
1da177e4 LT |
313 | while ((skb = skb_dequeue(&ap->recv_queue))) { |
314 | if (CAPIMSG_CMD(skb->data) == CAPI_DATA_B3_IND) | |
315 | ap->nrecvdatapkt++; | |
316 | else | |
317 | ap->nrecvctlpkt++; | |
318 | ||
319 | ap->recv_message(ap, skb); | |
320 | } | |
67837f23 | 321 | mutex_unlock(&ap->recv_mtx); |
1da177e4 LT |
322 | } |
323 | ||
554f200e TS |
324 | /** |
325 | * capi_ctr_handle_message() - handle incoming CAPI message | |
52253031 | 326 | * @ctr: controller descriptor structure. |
554f200e TS |
327 | * @appl: application ID. |
328 | * @skb: message. | |
329 | * | |
330 | * Called by hardware driver to pass a CAPI message to the application. | |
331 | */ | |
332 | ||
52253031 JK |
333 | void capi_ctr_handle_message(struct capi_ctr *ctr, u16 appl, |
334 | struct sk_buff *skb) | |
1da177e4 LT |
335 | { |
336 | struct capi20_appl *ap; | |
337 | int showctl = 0; | |
338 | u8 cmd, subcmd; | |
17f0cd2f | 339 | _cdebbuf *cdb; |
1da177e4 | 340 | |
52253031 | 341 | if (ctr->state != CAPI_CTR_RUNNING) { |
17f0cd2f KK |
342 | cdb = capi_message2str(skb->data); |
343 | if (cdb) { | |
344 | printk(KERN_INFO "kcapi: controller [%03d] not active, got: %s", | |
52253031 | 345 | ctr->cnr, cdb->buf); |
17f0cd2f KK |
346 | cdebbuf_free(cdb); |
347 | } else | |
348 | printk(KERN_INFO "kcapi: controller [%03d] not active, cannot trace\n", | |
52253031 | 349 | ctr->cnr); |
1da177e4 LT |
350 | goto error; |
351 | } | |
352 | ||
353 | cmd = CAPIMSG_COMMAND(skb->data); | |
354 | subcmd = CAPIMSG_SUBCOMMAND(skb->data); | |
355 | if (cmd == CAPI_DATA_B3 && subcmd == CAPI_IND) { | |
52253031 JK |
356 | ctr->nrecvdatapkt++; |
357 | if (ctr->traceflag > 2) | |
358 | showctl |= 2; | |
1da177e4 | 359 | } else { |
52253031 JK |
360 | ctr->nrecvctlpkt++; |
361 | if (ctr->traceflag) | |
362 | showctl |= 2; | |
1da177e4 | 363 | } |
52253031 | 364 | showctl |= (ctr->traceflag & 1); |
1da177e4 LT |
365 | if (showctl & 2) { |
366 | if (showctl & 1) { | |
17f0cd2f | 367 | printk(KERN_DEBUG "kcapi: got [%03d] id#%d %s len=%u\n", |
52253031 | 368 | ctr->cnr, CAPIMSG_APPID(skb->data), |
1da177e4 LT |
369 | capi_cmd2str(cmd, subcmd), |
370 | CAPIMSG_LEN(skb->data)); | |
371 | } else { | |
17f0cd2f KK |
372 | cdb = capi_message2str(skb->data); |
373 | if (cdb) { | |
374 | printk(KERN_DEBUG "kcapi: got [%03d] %s\n", | |
52253031 | 375 | ctr->cnr, cdb->buf); |
17f0cd2f KK |
376 | cdebbuf_free(cdb); |
377 | } else | |
378 | printk(KERN_DEBUG "kcapi: got [%03d] id#%d %s len=%u, cannot trace\n", | |
52253031 | 379 | ctr->cnr, CAPIMSG_APPID(skb->data), |
17f0cd2f KK |
380 | capi_cmd2str(cmd, subcmd), |
381 | CAPIMSG_LEN(skb->data)); | |
1da177e4 LT |
382 | } |
383 | ||
384 | } | |
385 | ||
88c896ef | 386 | rcu_read_lock(); |
1da177e4 | 387 | ap = get_capi_appl_by_nr(CAPIMSG_APPID(skb->data)); |
88c896ef JK |
388 | if (!ap) { |
389 | rcu_read_unlock(); | |
17f0cd2f KK |
390 | cdb = capi_message2str(skb->data); |
391 | if (cdb) { | |
392 | printk(KERN_ERR "kcapi: handle_message: applid %d state released (%s)\n", | |
393 | CAPIMSG_APPID(skb->data), cdb->buf); | |
394 | cdebbuf_free(cdb); | |
395 | } else | |
396 | printk(KERN_ERR "kcapi: handle_message: applid %d state released (%s) cannot trace\n", | |
397 | CAPIMSG_APPID(skb->data), | |
398 | capi_cmd2str(cmd, subcmd)); | |
1da177e4 LT |
399 | goto error; |
400 | } | |
401 | skb_queue_tail(&ap->recv_queue, skb); | |
402 | schedule_work(&ap->recv_work); | |
88c896ef | 403 | rcu_read_unlock(); |
1da177e4 LT |
404 | |
405 | return; | |
406 | ||
407 | error: | |
408 | kfree_skb(skb); | |
409 | } | |
410 | ||
411 | EXPORT_SYMBOL(capi_ctr_handle_message); | |
412 | ||
554f200e TS |
413 | /** |
414 | * capi_ctr_ready() - signal CAPI controller ready | |
52253031 | 415 | * @ctr: controller descriptor structure. |
554f200e TS |
416 | * |
417 | * Called by hardware driver to signal that the controller is up and running. | |
418 | */ | |
419 | ||
52253031 | 420 | void capi_ctr_ready(struct capi_ctr *ctr) |
1da177e4 | 421 | { |
52253031 JK |
422 | printk(KERN_NOTICE "kcapi: controller [%03d] \"%s\" ready.\n", |
423 | ctr->cnr, ctr->name); | |
1da177e4 | 424 | |
ef69bb2e | 425 | notify_push(CAPICTR_UP, ctr->cnr); |
1da177e4 LT |
426 | } |
427 | ||
428 | EXPORT_SYMBOL(capi_ctr_ready); | |
429 | ||
554f200e | 430 | /** |
4e329972 | 431 | * capi_ctr_down() - signal CAPI controller not ready |
52253031 | 432 | * @ctr: controller descriptor structure. |
554f200e TS |
433 | * |
434 | * Called by hardware driver to signal that the controller is down and | |
435 | * unavailable for use. | |
436 | */ | |
437 | ||
52253031 | 438 | void capi_ctr_down(struct capi_ctr *ctr) |
1da177e4 | 439 | { |
52253031 | 440 | printk(KERN_NOTICE "kcapi: controller [%03d] down.\n", ctr->cnr); |
1da177e4 | 441 | |
ef69bb2e | 442 | notify_push(CAPICTR_DOWN, ctr->cnr); |
1da177e4 LT |
443 | } |
444 | ||
4e329972 | 445 | EXPORT_SYMBOL(capi_ctr_down); |
1da177e4 | 446 | |
554f200e TS |
447 | /** |
448 | * capi_ctr_suspend_output() - suspend controller | |
52253031 | 449 | * @ctr: controller descriptor structure. |
554f200e TS |
450 | * |
451 | * Called by hardware driver to stop data flow. | |
0ca3a017 JK |
452 | * |
453 | * Note: The caller is responsible for synchronizing concurrent state changes | |
454 | * as well as invocations of capi_ctr_handle_message. | |
554f200e TS |
455 | */ |
456 | ||
52253031 | 457 | void capi_ctr_suspend_output(struct capi_ctr *ctr) |
1da177e4 | 458 | { |
52253031 JK |
459 | if (!ctr->blocked) { |
460 | printk(KERN_DEBUG "kcapi: controller [%03d] suspend\n", | |
461 | ctr->cnr); | |
462 | ctr->blocked = 1; | |
1da177e4 LT |
463 | } |
464 | } | |
465 | ||
466 | EXPORT_SYMBOL(capi_ctr_suspend_output); | |
467 | ||
554f200e TS |
468 | /** |
469 | * capi_ctr_resume_output() - resume controller | |
52253031 | 470 | * @ctr: controller descriptor structure. |
554f200e TS |
471 | * |
472 | * Called by hardware driver to resume data flow. | |
0ca3a017 JK |
473 | * |
474 | * Note: The caller is responsible for synchronizing concurrent state changes | |
475 | * as well as invocations of capi_ctr_handle_message. | |
554f200e TS |
476 | */ |
477 | ||
52253031 | 478 | void capi_ctr_resume_output(struct capi_ctr *ctr) |
1da177e4 | 479 | { |
52253031 JK |
480 | if (ctr->blocked) { |
481 | printk(KERN_DEBUG "kcapi: controller [%03d] resumed\n", | |
482 | ctr->cnr); | |
483 | ctr->blocked = 0; | |
1da177e4 LT |
484 | } |
485 | } | |
486 | ||
487 | EXPORT_SYMBOL(capi_ctr_resume_output); | |
488 | ||
489 | /* ------------------------------------------------------------- */ | |
490 | ||
554f200e TS |
491 | /** |
492 | * attach_capi_ctr() - register CAPI controller | |
52253031 | 493 | * @ctr: controller descriptor structure. |
554f200e TS |
494 | * |
495 | * Called by hardware driver to register a controller with the CAPI subsystem. | |
496 | * Return value: 0 on success, error code < 0 on error | |
497 | */ | |
498 | ||
52253031 | 499 | int attach_capi_ctr(struct capi_ctr *ctr) |
1da177e4 LT |
500 | { |
501 | int i; | |
502 | ||
0ca3a017 | 503 | mutex_lock(&capi_controller_lock); |
1da177e4 LT |
504 | |
505 | for (i = 0; i < CAPI_MAXCONTR; i++) { | |
52253031 | 506 | if (!capi_controller[i]) |
1da177e4 LT |
507 | break; |
508 | } | |
509 | if (i == CAPI_MAXCONTR) { | |
0ca3a017 | 510 | mutex_unlock(&capi_controller_lock); |
1da177e4 LT |
511 | printk(KERN_ERR "kcapi: out of controller slots\n"); |
512 | return -EBUSY; | |
513 | } | |
52253031 | 514 | capi_controller[i] = ctr; |
1da177e4 | 515 | |
52253031 JK |
516 | ctr->nrecvctlpkt = 0; |
517 | ctr->nrecvdatapkt = 0; | |
518 | ctr->nsentctlpkt = 0; | |
519 | ctr->nsentdatapkt = 0; | |
520 | ctr->cnr = i + 1; | |
521 | ctr->state = CAPI_CTR_DETECTED; | |
522 | ctr->blocked = 0; | |
523 | ctr->traceflag = showcapimsgs; | |
0ca3a017 | 524 | init_waitqueue_head(&ctr->state_wait_queue); |
52253031 JK |
525 | |
526 | sprintf(ctr->procfn, "capi/controllers/%d", ctr->cnr); | |
527 | ctr->procent = proc_create_data(ctr->procfn, 0, NULL, ctr->proc_fops, ctr); | |
528 | ||
529 | ncontrollers++; | |
0ca3a017 JK |
530 | |
531 | mutex_unlock(&capi_controller_lock); | |
532 | ||
52253031 JK |
533 | printk(KERN_NOTICE "kcapi: controller [%03d]: %s attached\n", |
534 | ctr->cnr, ctr->name); | |
1da177e4 LT |
535 | return 0; |
536 | } | |
537 | ||
538 | EXPORT_SYMBOL(attach_capi_ctr); | |
539 | ||
554f200e TS |
540 | /** |
541 | * detach_capi_ctr() - unregister CAPI controller | |
52253031 | 542 | * @ctr: controller descriptor structure. |
554f200e TS |
543 | * |
544 | * Called by hardware driver to remove the registration of a controller | |
545 | * with the CAPI subsystem. | |
546 | * Return value: 0 on success, error code < 0 on error | |
547 | */ | |
548 | ||
52253031 | 549 | int detach_capi_ctr(struct capi_ctr *ctr) |
1da177e4 | 550 | { |
0ca3a017 | 551 | int err = 0; |
1da177e4 | 552 | |
0ca3a017 | 553 | mutex_lock(&capi_controller_lock); |
1da177e4 | 554 | |
0ca3a017 JK |
555 | ctr_down(ctr, CAPI_CTR_DETACHED); |
556 | ||
557 | if (capi_controller[ctr->cnr - 1] != ctr) { | |
558 | err = -EINVAL; | |
559 | goto unlock_out; | |
1da177e4 | 560 | } |
52253031 | 561 | capi_controller[ctr->cnr - 1] = NULL; |
0ca3a017 JK |
562 | ncontrollers--; |
563 | ||
564 | if (ctr->procent) | |
565 | remove_proc_entry(ctr->procfn, NULL); | |
566 | ||
52253031 JK |
567 | printk(KERN_NOTICE "kcapi: controller [%03d]: %s unregistered\n", |
568 | ctr->cnr, ctr->name); | |
1da177e4 | 569 | |
0ca3a017 JK |
570 | unlock_out: |
571 | mutex_unlock(&capi_controller_lock); | |
572 | ||
573 | return err; | |
1da177e4 LT |
574 | } |
575 | ||
576 | EXPORT_SYMBOL(detach_capi_ctr); | |
577 | ||
554f200e TS |
578 | /** |
579 | * register_capi_driver() - register CAPI driver | |
580 | * @driver: driver descriptor structure. | |
581 | * | |
582 | * Called by hardware driver to register itself with the CAPI subsystem. | |
583 | */ | |
584 | ||
1da177e4 LT |
585 | void register_capi_driver(struct capi_driver *driver) |
586 | { | |
9717fb8b | 587 | mutex_lock(&capi_drivers_lock); |
1da177e4 | 588 | list_add_tail(&driver->list, &capi_drivers); |
9717fb8b | 589 | mutex_unlock(&capi_drivers_lock); |
1da177e4 LT |
590 | } |
591 | ||
592 | EXPORT_SYMBOL(register_capi_driver); | |
593 | ||
554f200e TS |
594 | /** |
595 | * unregister_capi_driver() - unregister CAPI driver | |
596 | * @driver: driver descriptor structure. | |
597 | * | |
598 | * Called by hardware driver to unregister itself from the CAPI subsystem. | |
599 | */ | |
600 | ||
1da177e4 LT |
601 | void unregister_capi_driver(struct capi_driver *driver) |
602 | { | |
9717fb8b | 603 | mutex_lock(&capi_drivers_lock); |
1da177e4 | 604 | list_del(&driver->list); |
9717fb8b | 605 | mutex_unlock(&capi_drivers_lock); |
1da177e4 LT |
606 | } |
607 | ||
608 | EXPORT_SYMBOL(unregister_capi_driver); | |
609 | ||
610 | /* ------------------------------------------------------------- */ | |
611 | /* -------- CAPI2.0 Interface ---------------------------------- */ | |
612 | /* ------------------------------------------------------------- */ | |
613 | ||
554f200e TS |
614 | /** |
615 | * capi20_isinstalled() - CAPI 2.0 operation CAPI_INSTALLED | |
616 | * | |
617 | * Return value: CAPI result code (CAPI_NOERROR if at least one ISDN controller | |
618 | * is ready for use, CAPI_REGNOTINSTALLED otherwise) | |
619 | */ | |
620 | ||
1da177e4 LT |
621 | u16 capi20_isinstalled(void) |
622 | { | |
0ca3a017 | 623 | u16 ret = CAPI_REGNOTINSTALLED; |
1da177e4 | 624 | int i; |
0ca3a017 JK |
625 | |
626 | mutex_lock(&capi_controller_lock); | |
627 | ||
628 | for (i = 0; i < CAPI_MAXCONTR; i++) | |
52253031 | 629 | if (capi_controller[i] && |
0ca3a017 JK |
630 | capi_controller[i]->state == CAPI_CTR_RUNNING) { |
631 | ret = CAPI_NOERROR; | |
632 | break; | |
633 | } | |
634 | ||
635 | mutex_unlock(&capi_controller_lock); | |
636 | ||
637 | return ret; | |
1da177e4 LT |
638 | } |
639 | ||
640 | EXPORT_SYMBOL(capi20_isinstalled); | |
641 | ||
554f200e TS |
642 | /** |
643 | * capi20_register() - CAPI 2.0 operation CAPI_REGISTER | |
644 | * @ap: CAPI application descriptor structure. | |
645 | * | |
646 | * Register an application's presence with CAPI. | |
647 | * A unique application ID is assigned and stored in @ap->applid. | |
648 | * After this function returns successfully, the message receive | |
649 | * callback function @ap->recv_message() may be called at any time | |
650 | * until capi20_release() has been called for the same @ap. | |
651 | * Return value: CAPI result code | |
652 | */ | |
653 | ||
1da177e4 LT |
654 | u16 capi20_register(struct capi20_appl *ap) |
655 | { | |
656 | int i; | |
657 | u16 applid; | |
1da177e4 LT |
658 | |
659 | DBG(""); | |
660 | ||
661 | if (ap->rparam.datablklen < 128) | |
662 | return CAPI_LOGBLKSIZETOSMALL; | |
663 | ||
88c896ef JK |
664 | ap->nrecvctlpkt = 0; |
665 | ap->nrecvdatapkt = 0; | |
666 | ap->nsentctlpkt = 0; | |
667 | ap->nsentdatapkt = 0; | |
668 | mutex_init(&ap->recv_mtx); | |
669 | skb_queue_head_init(&ap->recv_queue); | |
670 | INIT_WORK(&ap->recv_work, recv_handler); | |
671 | ap->release_in_progress = 0; | |
672 | ||
673 | mutex_lock(&capi_controller_lock); | |
1da177e4 LT |
674 | |
675 | for (applid = 1; applid <= CAPI_MAXAPPL; applid++) { | |
676 | if (capi_applications[applid - 1] == NULL) | |
677 | break; | |
678 | } | |
679 | if (applid > CAPI_MAXAPPL) { | |
88c896ef | 680 | mutex_unlock(&capi_controller_lock); |
1da177e4 LT |
681 | return CAPI_TOOMANYAPPLS; |
682 | } | |
683 | ||
684 | ap->applid = applid; | |
685 | capi_applications[applid - 1] = ap; | |
686 | ||
1da177e4 | 687 | for (i = 0; i < CAPI_MAXCONTR; i++) { |
52253031 JK |
688 | if (!capi_controller[i] || |
689 | capi_controller[i]->state != CAPI_CTR_RUNNING) | |
1da177e4 | 690 | continue; |
52253031 | 691 | register_appl(capi_controller[i], applid, &ap->rparam); |
1da177e4 | 692 | } |
0ca3a017 JK |
693 | |
694 | mutex_unlock(&capi_controller_lock); | |
1da177e4 LT |
695 | |
696 | if (showcapimsgs & 1) { | |
697 | printk(KERN_DEBUG "kcapi: appl %d up\n", applid); | |
698 | } | |
699 | ||
700 | return CAPI_NOERROR; | |
701 | } | |
702 | ||
703 | EXPORT_SYMBOL(capi20_register); | |
704 | ||
554f200e TS |
705 | /** |
706 | * capi20_release() - CAPI 2.0 operation CAPI_RELEASE | |
707 | * @ap: CAPI application descriptor structure. | |
708 | * | |
709 | * Terminate an application's registration with CAPI. | |
710 | * After this function returns successfully, the message receive | |
711 | * callback function @ap->recv_message() will no longer be called. | |
712 | * Return value: CAPI result code | |
713 | */ | |
714 | ||
1da177e4 LT |
715 | u16 capi20_release(struct capi20_appl *ap) |
716 | { | |
717 | int i; | |
1da177e4 LT |
718 | |
719 | DBG("applid %#x", ap->applid); | |
720 | ||
88c896ef JK |
721 | mutex_lock(&capi_controller_lock); |
722 | ||
1da177e4 LT |
723 | ap->release_in_progress = 1; |
724 | capi_applications[ap->applid - 1] = NULL; | |
1da177e4 | 725 | |
88c896ef | 726 | synchronize_rcu(); |
0ca3a017 | 727 | |
1da177e4 | 728 | for (i = 0; i < CAPI_MAXCONTR; i++) { |
52253031 JK |
729 | if (!capi_controller[i] || |
730 | capi_controller[i]->state != CAPI_CTR_RUNNING) | |
1da177e4 | 731 | continue; |
52253031 | 732 | release_appl(capi_controller[i], ap->applid); |
1da177e4 | 733 | } |
0ca3a017 JK |
734 | |
735 | mutex_unlock(&capi_controller_lock); | |
1da177e4 LT |
736 | |
737 | flush_scheduled_work(); | |
738 | skb_queue_purge(&ap->recv_queue); | |
739 | ||
740 | if (showcapimsgs & 1) { | |
741 | printk(KERN_DEBUG "kcapi: appl %d down\n", ap->applid); | |
742 | } | |
743 | ||
744 | return CAPI_NOERROR; | |
745 | } | |
746 | ||
747 | EXPORT_SYMBOL(capi20_release); | |
748 | ||
554f200e TS |
749 | /** |
750 | * capi20_put_message() - CAPI 2.0 operation CAPI_PUT_MESSAGE | |
751 | * @ap: CAPI application descriptor structure. | |
752 | * @skb: CAPI message. | |
753 | * | |
754 | * Transfer a single message to CAPI. | |
755 | * Return value: CAPI result code | |
756 | */ | |
757 | ||
1da177e4 LT |
758 | u16 capi20_put_message(struct capi20_appl *ap, struct sk_buff *skb) |
759 | { | |
52253031 | 760 | struct capi_ctr *ctr; |
1da177e4 LT |
761 | int showctl = 0; |
762 | u8 cmd, subcmd; | |
763 | ||
764 | DBG("applid %#x", ap->applid); | |
765 | ||
52253031 | 766 | if (ncontrollers == 0) |
1da177e4 LT |
767 | return CAPI_REGNOTINSTALLED; |
768 | if ((ap->applid == 0) || ap->release_in_progress) | |
769 | return CAPI_ILLAPPNR; | |
770 | if (skb->len < 12 | |
771 | || !capi_cmd_valid(CAPIMSG_COMMAND(skb->data)) | |
772 | || !capi_subcmd_valid(CAPIMSG_SUBCOMMAND(skb->data))) | |
773 | return CAPI_ILLCMDORSUBCMDORMSGTOSMALL; | |
0ca3a017 JK |
774 | |
775 | /* | |
776 | * The controller reference is protected by the existence of the | |
777 | * application passed to us. We assume that the caller properly | |
778 | * synchronizes this service with capi20_release. | |
779 | */ | |
52253031 | 780 | ctr = get_capi_ctr_by_nr(CAPIMSG_CONTROLLER(skb->data)); |
c6af0437 JK |
781 | if (!ctr || ctr->state != CAPI_CTR_RUNNING) |
782 | return CAPI_REGNOTINSTALLED; | |
52253031 | 783 | if (ctr->blocked) |
1da177e4 LT |
784 | return CAPI_SENDQUEUEFULL; |
785 | ||
786 | cmd = CAPIMSG_COMMAND(skb->data); | |
787 | subcmd = CAPIMSG_SUBCOMMAND(skb->data); | |
788 | ||
789 | if (cmd == CAPI_DATA_B3 && subcmd== CAPI_REQ) { | |
52253031 | 790 | ctr->nsentdatapkt++; |
1da177e4 | 791 | ap->nsentdatapkt++; |
52253031 JK |
792 | if (ctr->traceflag > 2) |
793 | showctl |= 2; | |
1da177e4 | 794 | } else { |
52253031 | 795 | ctr->nsentctlpkt++; |
1da177e4 | 796 | ap->nsentctlpkt++; |
52253031 JK |
797 | if (ctr->traceflag) |
798 | showctl |= 2; | |
1da177e4 | 799 | } |
52253031 | 800 | showctl |= (ctr->traceflag & 1); |
1da177e4 LT |
801 | if (showctl & 2) { |
802 | if (showctl & 1) { | |
17f0cd2f | 803 | printk(KERN_DEBUG "kcapi: put [%03d] id#%d %s len=%u\n", |
1da177e4 LT |
804 | CAPIMSG_CONTROLLER(skb->data), |
805 | CAPIMSG_APPID(skb->data), | |
806 | capi_cmd2str(cmd, subcmd), | |
807 | CAPIMSG_LEN(skb->data)); | |
808 | } else { | |
17f0cd2f KK |
809 | _cdebbuf *cdb = capi_message2str(skb->data); |
810 | if (cdb) { | |
811 | printk(KERN_DEBUG "kcapi: put [%03d] %s\n", | |
812 | CAPIMSG_CONTROLLER(skb->data), | |
813 | cdb->buf); | |
814 | cdebbuf_free(cdb); | |
815 | } else | |
816 | printk(KERN_DEBUG "kcapi: put [%03d] id#%d %s len=%u cannot trace\n", | |
817 | CAPIMSG_CONTROLLER(skb->data), | |
818 | CAPIMSG_APPID(skb->data), | |
819 | capi_cmd2str(cmd, subcmd), | |
820 | CAPIMSG_LEN(skb->data)); | |
1da177e4 | 821 | } |
1da177e4 | 822 | } |
52253031 | 823 | return ctr->send_message(ctr, skb); |
1da177e4 LT |
824 | } |
825 | ||
826 | EXPORT_SYMBOL(capi20_put_message); | |
827 | ||
554f200e TS |
828 | /** |
829 | * capi20_get_manufacturer() - CAPI 2.0 operation CAPI_GET_MANUFACTURER | |
830 | * @contr: controller number. | |
831 | * @buf: result buffer (64 bytes). | |
832 | * | |
833 | * Retrieve information about the manufacturer of the specified ISDN controller | |
834 | * or (for @contr == 0) the driver itself. | |
835 | * Return value: CAPI result code | |
836 | */ | |
837 | ||
1da177e4 LT |
838 | u16 capi20_get_manufacturer(u32 contr, u8 *buf) |
839 | { | |
52253031 | 840 | struct capi_ctr *ctr; |
0ca3a017 | 841 | u16 ret; |
1da177e4 LT |
842 | |
843 | if (contr == 0) { | |
844 | strlcpy(buf, capi_manufakturer, CAPI_MANUFACTURER_LEN); | |
845 | return CAPI_NOERROR; | |
846 | } | |
0ca3a017 JK |
847 | |
848 | mutex_lock(&capi_controller_lock); | |
849 | ||
52253031 | 850 | ctr = get_capi_ctr_by_nr(contr); |
0ca3a017 JK |
851 | if (ctr && ctr->state == CAPI_CTR_RUNNING) { |
852 | strlcpy(buf, ctr->manu, CAPI_MANUFACTURER_LEN); | |
853 | ret = CAPI_NOERROR; | |
854 | } else | |
855 | ret = CAPI_REGNOTINSTALLED; | |
856 | ||
857 | mutex_unlock(&capi_controller_lock); | |
858 | return ret; | |
1da177e4 LT |
859 | } |
860 | ||
861 | EXPORT_SYMBOL(capi20_get_manufacturer); | |
862 | ||
554f200e TS |
863 | /** |
864 | * capi20_get_version() - CAPI 2.0 operation CAPI_GET_VERSION | |
865 | * @contr: controller number. | |
866 | * @verp: result structure. | |
867 | * | |
868 | * Retrieve version information for the specified ISDN controller | |
869 | * or (for @contr == 0) the driver itself. | |
870 | * Return value: CAPI result code | |
871 | */ | |
872 | ||
1da177e4 LT |
873 | u16 capi20_get_version(u32 contr, struct capi_version *verp) |
874 | { | |
52253031 | 875 | struct capi_ctr *ctr; |
0ca3a017 | 876 | u16 ret; |
1da177e4 LT |
877 | |
878 | if (contr == 0) { | |
879 | *verp = driver_version; | |
880 | return CAPI_NOERROR; | |
881 | } | |
0ca3a017 JK |
882 | |
883 | mutex_lock(&capi_controller_lock); | |
884 | ||
52253031 | 885 | ctr = get_capi_ctr_by_nr(contr); |
0ca3a017 JK |
886 | if (ctr && ctr->state == CAPI_CTR_RUNNING) { |
887 | memcpy(verp, &ctr->version, sizeof(capi_version)); | |
888 | ret = CAPI_NOERROR; | |
889 | } else | |
890 | ret = CAPI_REGNOTINSTALLED; | |
1da177e4 | 891 | |
0ca3a017 JK |
892 | mutex_unlock(&capi_controller_lock); |
893 | return ret; | |
1da177e4 LT |
894 | } |
895 | ||
896 | EXPORT_SYMBOL(capi20_get_version); | |
897 | ||
554f200e TS |
898 | /** |
899 | * capi20_get_serial() - CAPI 2.0 operation CAPI_GET_SERIAL_NUMBER | |
900 | * @contr: controller number. | |
901 | * @serial: result buffer (8 bytes). | |
902 | * | |
903 | * Retrieve the serial number of the specified ISDN controller | |
904 | * or (for @contr == 0) the driver itself. | |
905 | * Return value: CAPI result code | |
906 | */ | |
907 | ||
1da177e4 LT |
908 | u16 capi20_get_serial(u32 contr, u8 *serial) |
909 | { | |
52253031 | 910 | struct capi_ctr *ctr; |
0ca3a017 | 911 | u16 ret; |
1da177e4 LT |
912 | |
913 | if (contr == 0) { | |
914 | strlcpy(serial, driver_serial, CAPI_SERIAL_LEN); | |
915 | return CAPI_NOERROR; | |
916 | } | |
0ca3a017 JK |
917 | |
918 | mutex_lock(&capi_controller_lock); | |
919 | ||
52253031 | 920 | ctr = get_capi_ctr_by_nr(contr); |
0ca3a017 JK |
921 | if (ctr && ctr->state == CAPI_CTR_RUNNING) { |
922 | strlcpy(serial, ctr->serial, CAPI_SERIAL_LEN); | |
923 | ret = CAPI_NOERROR; | |
924 | } else | |
925 | ret = CAPI_REGNOTINSTALLED; | |
1da177e4 | 926 | |
0ca3a017 JK |
927 | mutex_unlock(&capi_controller_lock); |
928 | return ret; | |
1da177e4 LT |
929 | } |
930 | ||
931 | EXPORT_SYMBOL(capi20_get_serial); | |
932 | ||
554f200e TS |
933 | /** |
934 | * capi20_get_profile() - CAPI 2.0 operation CAPI_GET_PROFILE | |
935 | * @contr: controller number. | |
936 | * @profp: result structure. | |
937 | * | |
938 | * Retrieve capability information for the specified ISDN controller | |
939 | * or (for @contr == 0) the number of installed controllers. | |
940 | * Return value: CAPI result code | |
941 | */ | |
942 | ||
1da177e4 LT |
943 | u16 capi20_get_profile(u32 contr, struct capi_profile *profp) |
944 | { | |
52253031 | 945 | struct capi_ctr *ctr; |
0ca3a017 | 946 | u16 ret; |
1da177e4 LT |
947 | |
948 | if (contr == 0) { | |
52253031 | 949 | profp->ncontroller = ncontrollers; |
1da177e4 LT |
950 | return CAPI_NOERROR; |
951 | } | |
0ca3a017 JK |
952 | |
953 | mutex_lock(&capi_controller_lock); | |
954 | ||
52253031 | 955 | ctr = get_capi_ctr_by_nr(contr); |
0ca3a017 JK |
956 | if (ctr && ctr->state == CAPI_CTR_RUNNING) { |
957 | memcpy(profp, &ctr->profile, sizeof(struct capi_profile)); | |
958 | ret = CAPI_NOERROR; | |
959 | } else | |
960 | ret = CAPI_REGNOTINSTALLED; | |
1da177e4 | 961 | |
0ca3a017 JK |
962 | mutex_unlock(&capi_controller_lock); |
963 | return ret; | |
1da177e4 LT |
964 | } |
965 | ||
966 | EXPORT_SYMBOL(capi20_get_profile); | |
967 | ||
0ca3a017 JK |
968 | /* Must be called with capi_controller_lock held. */ |
969 | static int wait_on_ctr_state(struct capi_ctr *ctr, unsigned int state) | |
970 | { | |
971 | DEFINE_WAIT(wait); | |
972 | int retval = 0; | |
973 | ||
974 | ctr = capi_ctr_get(ctr); | |
975 | if (!ctr) | |
976 | return -ESRCH; | |
977 | ||
978 | for (;;) { | |
979 | prepare_to_wait(&ctr->state_wait_queue, &wait, | |
980 | TASK_INTERRUPTIBLE); | |
981 | ||
982 | if (ctr->state == state) | |
983 | break; | |
984 | if (ctr->state == CAPI_CTR_DETACHED) { | |
985 | retval = -ESRCH; | |
986 | break; | |
987 | } | |
988 | if (signal_pending(current)) { | |
989 | retval = -EINTR; | |
990 | break; | |
991 | } | |
992 | ||
993 | mutex_unlock(&capi_controller_lock); | |
994 | schedule(); | |
995 | mutex_lock(&capi_controller_lock); | |
996 | } | |
997 | finish_wait(&ctr->state_wait_queue, &wait); | |
998 | ||
999 | capi_ctr_put(ctr); | |
1000 | ||
1001 | return retval; | |
1002 | } | |
1003 | ||
37772ac0 | 1004 | #ifdef AVMB1_COMPAT |
1da177e4 LT |
1005 | static int old_capi_manufacturer(unsigned int cmd, void __user *data) |
1006 | { | |
1007 | avmb1_loadandconfigdef ldef; | |
1008 | avmb1_extcarddef cdef; | |
1009 | avmb1_resetdef rdef; | |
1010 | capicardparams cparams; | |
52253031 | 1011 | struct capi_ctr *ctr; |
1da177e4 LT |
1012 | struct capi_driver *driver = NULL; |
1013 | capiloaddata ldata; | |
1014 | struct list_head *l; | |
1da177e4 LT |
1015 | int retval; |
1016 | ||
1017 | switch (cmd) { | |
1018 | case AVMB1_ADDCARD: | |
1019 | case AVMB1_ADDCARD_WITH_TYPE: | |
1020 | if (cmd == AVMB1_ADDCARD) { | |
1021 | if ((retval = copy_from_user(&cdef, data, | |
1022 | sizeof(avmb1_carddef)))) | |
1023 | return retval; | |
1024 | cdef.cardtype = AVM_CARDTYPE_B1; | |
1025 | } else { | |
1026 | if ((retval = copy_from_user(&cdef, data, | |
1027 | sizeof(avmb1_extcarddef)))) | |
1028 | return retval; | |
1029 | } | |
1030 | cparams.port = cdef.port; | |
1031 | cparams.irq = cdef.irq; | |
1032 | cparams.cardnr = cdef.cardnr; | |
1033 | ||
9717fb8b JK |
1034 | mutex_lock(&capi_drivers_lock); |
1035 | ||
1da177e4 LT |
1036 | switch (cdef.cardtype) { |
1037 | case AVM_CARDTYPE_B1: | |
1038 | list_for_each(l, &capi_drivers) { | |
1039 | driver = list_entry(l, struct capi_driver, list); | |
1040 | if (strcmp(driver->name, "b1isa") == 0) | |
1041 | break; | |
1042 | } | |
1043 | break; | |
1044 | case AVM_CARDTYPE_T1: | |
1045 | list_for_each(l, &capi_drivers) { | |
1046 | driver = list_entry(l, struct capi_driver, list); | |
1047 | if (strcmp(driver->name, "t1isa") == 0) | |
1048 | break; | |
1049 | } | |
1050 | break; | |
1051 | default: | |
1052 | driver = NULL; | |
1053 | break; | |
1054 | } | |
1055 | if (!driver) { | |
1da177e4 | 1056 | printk(KERN_ERR "kcapi: driver not loaded.\n"); |
9717fb8b JK |
1057 | retval = -EIO; |
1058 | } else if (!driver->add_card) { | |
1da177e4 | 1059 | printk(KERN_ERR "kcapi: driver has no add card function.\n"); |
9717fb8b JK |
1060 | retval = -EIO; |
1061 | } else | |
1062 | retval = driver->add_card(driver, &cparams); | |
1da177e4 | 1063 | |
9717fb8b | 1064 | mutex_unlock(&capi_drivers_lock); |
1da177e4 LT |
1065 | return retval; |
1066 | ||
1067 | case AVMB1_LOAD: | |
1068 | case AVMB1_LOAD_AND_CONFIG: | |
1069 | ||
1070 | if (cmd == AVMB1_LOAD) { | |
1071 | if (copy_from_user(&ldef, data, | |
1072 | sizeof(avmb1_loaddef))) | |
1073 | return -EFAULT; | |
1074 | ldef.t4config.len = 0; | |
1075 | ldef.t4config.data = NULL; | |
1076 | } else { | |
1077 | if (copy_from_user(&ldef, data, | |
1078 | sizeof(avmb1_loadandconfigdef))) | |
1079 | return -EFAULT; | |
1080 | } | |
0ca3a017 JK |
1081 | |
1082 | mutex_lock(&capi_controller_lock); | |
1083 | ||
52253031 | 1084 | ctr = get_capi_ctr_by_nr(ldef.contr); |
0ca3a017 JK |
1085 | if (!ctr) { |
1086 | retval = -EINVAL; | |
1087 | goto load_unlock_out; | |
1088 | } | |
1089 | ||
52253031 | 1090 | if (ctr->load_firmware == NULL) { |
1da177e4 | 1091 | printk(KERN_DEBUG "kcapi: load: no load function\n"); |
0ca3a017 JK |
1092 | retval = -ESRCH; |
1093 | goto load_unlock_out; | |
1da177e4 LT |
1094 | } |
1095 | ||
1096 | if (ldef.t4file.len <= 0) { | |
1097 | printk(KERN_DEBUG "kcapi: load: invalid parameter: length of t4file is %d ?\n", ldef.t4file.len); | |
0ca3a017 JK |
1098 | retval = -EINVAL; |
1099 | goto load_unlock_out; | |
1da177e4 | 1100 | } |
2f9e9b6d | 1101 | if (ldef.t4file.data == NULL) { |
1da177e4 | 1102 | printk(KERN_DEBUG "kcapi: load: invalid parameter: dataptr is 0\n"); |
0ca3a017 JK |
1103 | retval = -EINVAL; |
1104 | goto load_unlock_out; | |
1da177e4 LT |
1105 | } |
1106 | ||
1107 | ldata.firmware.user = 1; | |
1108 | ldata.firmware.data = ldef.t4file.data; | |
1109 | ldata.firmware.len = ldef.t4file.len; | |
1110 | ldata.configuration.user = 1; | |
1111 | ldata.configuration.data = ldef.t4config.data; | |
1112 | ldata.configuration.len = ldef.t4config.len; | |
1113 | ||
52253031 | 1114 | if (ctr->state != CAPI_CTR_DETECTED) { |
1da177e4 | 1115 | printk(KERN_INFO "kcapi: load: contr=%d not in detect state\n", ldef.contr); |
0ca3a017 JK |
1116 | retval = -EBUSY; |
1117 | goto load_unlock_out; | |
1da177e4 | 1118 | } |
52253031 | 1119 | ctr->state = CAPI_CTR_LOADING; |
1da177e4 | 1120 | |
52253031 | 1121 | retval = ctr->load_firmware(ctr, &ldata); |
1da177e4 | 1122 | if (retval) { |
52253031 | 1123 | ctr->state = CAPI_CTR_DETECTED; |
0ca3a017 | 1124 | goto load_unlock_out; |
1da177e4 LT |
1125 | } |
1126 | ||
0ca3a017 | 1127 | retval = wait_on_ctr_state(ctr, CAPI_CTR_RUNNING); |
1da177e4 | 1128 | |
0ca3a017 JK |
1129 | load_unlock_out: |
1130 | mutex_unlock(&capi_controller_lock); | |
1131 | return retval; | |
1da177e4 LT |
1132 | |
1133 | case AVMB1_RESETCARD: | |
1134 | if (copy_from_user(&rdef, data, sizeof(avmb1_resetdef))) | |
1135 | return -EFAULT; | |
0ca3a017 JK |
1136 | |
1137 | retval = 0; | |
1138 | ||
1139 | mutex_lock(&capi_controller_lock); | |
1140 | ||
52253031 | 1141 | ctr = get_capi_ctr_by_nr(rdef.contr); |
0ca3a017 JK |
1142 | if (!ctr) { |
1143 | retval = -ESRCH; | |
1144 | goto reset_unlock_out; | |
1145 | } | |
1da177e4 | 1146 | |
52253031 | 1147 | if (ctr->state == CAPI_CTR_DETECTED) |
0ca3a017 | 1148 | goto reset_unlock_out; |
1da177e4 | 1149 | |
52253031 | 1150 | ctr->reset_ctr(ctr); |
1da177e4 | 1151 | |
0ca3a017 | 1152 | retval = wait_on_ctr_state(ctr, CAPI_CTR_DETECTED); |
1da177e4 | 1153 | |
0ca3a017 JK |
1154 | reset_unlock_out: |
1155 | mutex_unlock(&capi_controller_lock); | |
1156 | return retval; | |
1da177e4 LT |
1157 | } |
1158 | return -EINVAL; | |
1159 | } | |
1160 | #endif | |
1161 | ||
554f200e TS |
1162 | /** |
1163 | * capi20_manufacturer() - CAPI 2.0 operation CAPI_MANUFACTURER | |
1164 | * @cmd: command. | |
1165 | * @data: parameter. | |
1166 | * | |
1167 | * Perform manufacturer specific command. | |
1168 | * Return value: CAPI result code | |
1169 | */ | |
1170 | ||
1da177e4 LT |
1171 | int capi20_manufacturer(unsigned int cmd, void __user *data) |
1172 | { | |
52253031 | 1173 | struct capi_ctr *ctr; |
0ca3a017 | 1174 | int retval; |
1da177e4 LT |
1175 | |
1176 | switch (cmd) { | |
37772ac0 | 1177 | #ifdef AVMB1_COMPAT |
1da177e4 LT |
1178 | case AVMB1_LOAD: |
1179 | case AVMB1_LOAD_AND_CONFIG: | |
1180 | case AVMB1_RESETCARD: | |
1181 | case AVMB1_GET_CARDINFO: | |
1182 | case AVMB1_REMOVECARD: | |
1183 | return old_capi_manufacturer(cmd, data); | |
1184 | #endif | |
1185 | case KCAPI_CMD_TRACE: | |
1186 | { | |
1187 | kcapi_flagdef fdef; | |
1188 | ||
1189 | if (copy_from_user(&fdef, data, sizeof(kcapi_flagdef))) | |
1190 | return -EFAULT; | |
1191 | ||
0ca3a017 JK |
1192 | mutex_lock(&capi_controller_lock); |
1193 | ||
52253031 | 1194 | ctr = get_capi_ctr_by_nr(fdef.contr); |
0ca3a017 JK |
1195 | if (ctr) { |
1196 | ctr->traceflag = fdef.flag; | |
1197 | printk(KERN_INFO "kcapi: contr [%03d] set trace=%d\n", | |
1198 | ctr->cnr, ctr->traceflag); | |
1199 | retval = 0; | |
1200 | } else | |
1201 | retval = -ESRCH; | |
1202 | ||
1203 | mutex_unlock(&capi_controller_lock); | |
1da177e4 | 1204 | |
0ca3a017 | 1205 | return retval; |
1da177e4 LT |
1206 | } |
1207 | case KCAPI_CMD_ADDCARD: | |
1208 | { | |
1209 | struct list_head *l; | |
1210 | struct capi_driver *driver = NULL; | |
1211 | capicardparams cparams; | |
1212 | kcapi_carddef cdef; | |
1da177e4 LT |
1213 | |
1214 | if ((retval = copy_from_user(&cdef, data, sizeof(cdef)))) | |
1215 | return retval; | |
1216 | ||
1217 | cparams.port = cdef.port; | |
1218 | cparams.irq = cdef.irq; | |
1219 | cparams.membase = cdef.membase; | |
1220 | cparams.cardnr = cdef.cardnr; | |
1221 | cparams.cardtype = 0; | |
1222 | cdef.driver[sizeof(cdef.driver)-1] = 0; | |
1223 | ||
9717fb8b JK |
1224 | mutex_lock(&capi_drivers_lock); |
1225 | ||
1da177e4 LT |
1226 | list_for_each(l, &capi_drivers) { |
1227 | driver = list_entry(l, struct capi_driver, list); | |
1228 | if (strcmp(driver->name, cdef.driver) == 0) | |
1229 | break; | |
1230 | } | |
2f9e9b6d | 1231 | if (driver == NULL) { |
1da177e4 LT |
1232 | printk(KERN_ERR "kcapi: driver \"%s\" not loaded.\n", |
1233 | cdef.driver); | |
9717fb8b JK |
1234 | retval = -ESRCH; |
1235 | } else if (!driver->add_card) { | |
1da177e4 | 1236 | printk(KERN_ERR "kcapi: driver \"%s\" has no add card function.\n", cdef.driver); |
9717fb8b JK |
1237 | retval = -EIO; |
1238 | } else | |
1239 | retval = driver->add_card(driver, &cparams); | |
1da177e4 | 1240 | |
9717fb8b JK |
1241 | mutex_unlock(&capi_drivers_lock); |
1242 | return retval; | |
1da177e4 LT |
1243 | } |
1244 | ||
1245 | default: | |
1246 | printk(KERN_ERR "kcapi: manufacturer command %d unknown.\n", | |
1247 | cmd); | |
1248 | break; | |
1249 | ||
1250 | } | |
1251 | return -EINVAL; | |
1252 | } | |
1253 | ||
1254 | EXPORT_SYMBOL(capi20_manufacturer); | |
1255 | ||
1da177e4 LT |
1256 | /* ------------------------------------------------------------- */ |
1257 | /* -------- Init & Cleanup ------------------------------------- */ | |
1258 | /* ------------------------------------------------------------- */ | |
1259 | ||
1260 | /* | |
1261 | * init / exit functions | |
1262 | */ | |
1263 | ||
ef69bb2e JK |
1264 | static struct notifier_block capictr_nb = { |
1265 | .notifier_call = notify_handler, | |
1266 | .priority = INT_MAX, | |
1267 | }; | |
1268 | ||
1da177e4 LT |
1269 | static int __init kcapi_init(void) |
1270 | { | |
88549d6b | 1271 | int err; |
1da177e4 | 1272 | |
ef69bb2e JK |
1273 | register_capictr_notifier(&capictr_nb); |
1274 | ||
88549d6b JK |
1275 | err = cdebug_init(); |
1276 | if (!err) | |
1277 | kcapi_proc_init(); | |
1278 | return err; | |
1da177e4 LT |
1279 | } |
1280 | ||
1281 | static void __exit kcapi_exit(void) | |
1282 | { | |
1283 | kcapi_proc_exit(); | |
1284 | ||
1285 | /* make sure all notifiers are finished */ | |
1286 | flush_scheduled_work(); | |
17f0cd2f | 1287 | cdebug_exit(); |
1da177e4 LT |
1288 | } |
1289 | ||
1290 | module_init(kcapi_init); | |
1291 | module_exit(kcapi_exit); |