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