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