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 | ||
1da177e4 LT |
13 | #include "kcapi.h" |
14 | #include <linux/module.h> | |
15 | #include <linux/mm.h> | |
16 | #include <linux/interrupt.h> | |
17 | #include <linux/ioport.h> | |
18 | #include <linux/proc_fs.h> | |
174cd4b1 | 19 | #include <linux/sched/signal.h> |
1da177e4 LT |
20 | #include <linux/seq_file.h> |
21 | #include <linux/skbuff.h> | |
22 | #include <linux/workqueue.h> | |
23 | #include <linux/capi.h> | |
24 | #include <linux/kernelcapi.h> | |
25 | #include <linux/init.h> | |
26 | #include <linux/moduleparam.h> | |
27 | #include <linux/delay.h> | |
5a0e3ad6 | 28 | #include <linux/slab.h> |
7c0f6ba6 | 29 | #include <linux/uaccess.h> |
1da177e4 LT |
30 | #include <linux/isdn/capicmd.h> |
31 | #include <linux/isdn/capiutil.h> | |
9cdf1827 | 32 | #include <linux/mutex.h> |
88c896ef | 33 | #include <linux/rcupdate.h> |
1da177e4 | 34 | |
2cd24a2e | 35 | static int showcapimsgs; |
158fa677 | 36 | static struct workqueue_struct *kcapi_wq; |
1da177e4 | 37 | |
1da177e4 LT |
38 | module_param(showcapimsgs, uint, 0); |
39 | ||
40 | /* ------------------------------------------------------------- */ | |
41 | ||
ef69bb2e | 42 | struct capictr_event { |
1da177e4 | 43 | struct work_struct work; |
ef69bb2e | 44 | unsigned int type; |
1da177e4 | 45 | u32 controller; |
1da177e4 LT |
46 | }; |
47 | ||
48 | /* ------------------------------------------------------------- */ | |
49 | ||
733a707d | 50 | static const struct capi_version driver_version = {2, 0, 1, 1 << 4}; |
1da177e4 LT |
51 | static char driver_serial[CAPI_SERIAL_LEN] = "0004711"; |
52 | static char capi_manufakturer[64] = "AVM Berlin"; | |
53 | ||
54 | #define NCCI2CTRL(ncci) (((ncci) >> 24) & 0x7f) | |
55 | ||
0ca3a017 JK |
56 | struct capi_ctr *capi_controller[CAPI_MAXCONTR]; |
57 | DEFINE_MUTEX(capi_controller_lock); | |
58 | ||
1da177e4 | 59 | struct capi20_appl *capi_applications[CAPI_MAXAPPL]; |
1da177e4 | 60 | |
52253031 | 61 | static int ncontrollers; |
1da177e4 LT |
62 | |
63 | /* -------- controller ref counting -------------------------------------- */ | |
64 | ||
65 | static inline struct capi_ctr * | |
52253031 | 66 | capi_ctr_get(struct capi_ctr *ctr) |
1da177e4 | 67 | { |
52253031 | 68 | if (!try_module_get(ctr->owner)) |
1da177e4 | 69 | return NULL; |
52253031 | 70 | return ctr; |
1da177e4 LT |
71 | } |
72 | ||
73 | static inline void | |
52253031 | 74 | capi_ctr_put(struct capi_ctr *ctr) |
1da177e4 | 75 | { |
52253031 | 76 | module_put(ctr->owner); |
1da177e4 LT |
77 | } |
78 | ||
79 | /* ------------------------------------------------------------- */ | |
80 | ||
81 | static inline struct capi_ctr *get_capi_ctr_by_nr(u16 contr) | |
82 | { | |
25dff94f | 83 | if (contr < 1 || contr - 1 >= CAPI_MAXCONTR) |
1da177e4 LT |
84 | return NULL; |
85 | ||
52253031 | 86 | return capi_controller[contr - 1]; |
1da177e4 LT |
87 | } |
88 | ||
b003f4e1 JK |
89 | static inline struct capi20_appl *__get_capi_appl_by_nr(u16 applid) |
90 | { | |
91 | lockdep_assert_held(&capi_controller_lock); | |
92 | ||
25dff94f | 93 | if (applid < 1 || applid - 1 >= CAPI_MAXAPPL) |
b003f4e1 JK |
94 | return NULL; |
95 | ||
96 | return capi_applications[applid - 1]; | |
97 | } | |
98 | ||
1da177e4 LT |
99 | static inline struct capi20_appl *get_capi_appl_by_nr(u16 applid) |
100 | { | |
25dff94f | 101 | if (applid < 1 || applid - 1 >= CAPI_MAXAPPL) |
1da177e4 LT |
102 | return NULL; |
103 | ||
88c896ef | 104 | return rcu_dereference(capi_applications[applid - 1]); |
1da177e4 LT |
105 | } |
106 | ||
107 | /* -------- util functions ------------------------------------ */ | |
108 | ||
109 | static inline int capi_cmd_valid(u8 cmd) | |
110 | { | |
111 | switch (cmd) { | |
112 | case CAPI_ALERT: | |
113 | case CAPI_CONNECT: | |
114 | case CAPI_CONNECT_ACTIVE: | |
115 | case CAPI_CONNECT_B3_ACTIVE: | |
116 | case CAPI_CONNECT_B3: | |
117 | case CAPI_CONNECT_B3_T90_ACTIVE: | |
118 | case CAPI_DATA_B3: | |
119 | case CAPI_DISCONNECT_B3: | |
120 | case CAPI_DISCONNECT: | |
121 | case CAPI_FACILITY: | |
122 | case CAPI_INFO: | |
123 | case CAPI_LISTEN: | |
124 | case CAPI_MANUFACTURER: | |
125 | case CAPI_RESET_B3: | |
126 | case CAPI_SELECT_B_PROTOCOL: | |
127 | return 1; | |
128 | } | |
129 | return 0; | |
130 | } | |
131 | ||
132 | static inline int capi_subcmd_valid(u8 subcmd) | |
133 | { | |
134 | switch (subcmd) { | |
135 | case CAPI_REQ: | |
136 | case CAPI_CONF: | |
137 | case CAPI_IND: | |
138 | case CAPI_RESP: | |
139 | return 1; | |
140 | } | |
141 | return 0; | |
142 | } | |
143 | ||
144 | /* ------------------------------------------------------------ */ | |
145 | ||
52253031 JK |
146 | static void |
147 | register_appl(struct capi_ctr *ctr, u16 applid, capi_register_params *rparam) | |
1da177e4 | 148 | { |
52253031 | 149 | ctr = capi_ctr_get(ctr); |
1da177e4 | 150 | |
52253031 JK |
151 | if (ctr) |
152 | ctr->register_appl(ctr, applid, rparam); | |
1da177e4 | 153 | else |
52253031 JK |
154 | printk(KERN_WARNING "%s: cannot get controller resources\n", |
155 | __func__); | |
1da177e4 LT |
156 | } |
157 | ||
158 | ||
52253031 | 159 | static void release_appl(struct capi_ctr *ctr, u16 applid) |
1da177e4 LT |
160 | { |
161 | DBG("applid %#x", applid); | |
475be4d8 | 162 | |
52253031 JK |
163 | ctr->release_appl(ctr, applid); |
164 | capi_ctr_put(ctr); | |
1da177e4 LT |
165 | } |
166 | ||
1da177e4 LT |
167 | static void notify_up(u32 contr) |
168 | { | |
1da177e4 | 169 | struct capi20_appl *ap; |
3efecf7a | 170 | struct capi_ctr *ctr; |
1da177e4 LT |
171 | u16 applid; |
172 | ||
0ca3a017 JK |
173 | mutex_lock(&capi_controller_lock); |
174 | ||
3efecf7a | 175 | if (showcapimsgs & 1) |
475be4d8 | 176 | printk(KERN_DEBUG "kcapi: notify up contr %d\n", contr); |
3efecf7a JK |
177 | |
178 | ctr = get_capi_ctr_by_nr(contr); | |
179 | if (ctr) { | |
180 | if (ctr->state == CAPI_CTR_RUNNING) | |
0ca3a017 | 181 | goto unlock_out; |
3efecf7a JK |
182 | |
183 | ctr->state = CAPI_CTR_RUNNING; | |
184 | ||
185 | for (applid = 1; applid <= CAPI_MAXAPPL; applid++) { | |
b003f4e1 JK |
186 | ap = __get_capi_appl_by_nr(applid); |
187 | if (ap) | |
188 | register_appl(ctr, applid, &ap->rparam); | |
3efecf7a JK |
189 | } |
190 | } else | |
156f1ed6 | 191 | printk(KERN_WARNING "%s: invalid contr %d\n", __func__, contr); |
0ca3a017 JK |
192 | |
193 | unlock_out: | |
194 | mutex_unlock(&capi_controller_lock); | |
1da177e4 LT |
195 | } |
196 | ||
0ca3a017 | 197 | static void ctr_down(struct capi_ctr *ctr, int new_state) |
1da177e4 LT |
198 | { |
199 | struct capi20_appl *ap; | |
200 | u16 applid; | |
201 | ||
0ca3a017 | 202 | if (ctr->state == CAPI_CTR_DETECTED || ctr->state == CAPI_CTR_DETACHED) |
3efecf7a JK |
203 | return; |
204 | ||
0ca3a017 | 205 | ctr->state = new_state; |
3efecf7a JK |
206 | |
207 | memset(ctr->manu, 0, sizeof(ctr->manu)); | |
208 | memset(&ctr->version, 0, sizeof(ctr->version)); | |
209 | memset(&ctr->profile, 0, sizeof(ctr->profile)); | |
210 | memset(ctr->serial, 0, sizeof(ctr->serial)); | |
1da177e4 LT |
211 | |
212 | for (applid = 1; applid <= CAPI_MAXAPPL; applid++) { | |
b003f4e1 | 213 | ap = __get_capi_appl_by_nr(applid); |
88c896ef | 214 | if (ap) |
3efecf7a | 215 | capi_ctr_put(ctr); |
1da177e4 LT |
216 | } |
217 | } | |
218 | ||
3efecf7a JK |
219 | static void notify_down(u32 contr) |
220 | { | |
221 | struct capi_ctr *ctr; | |
222 | ||
0ca3a017 JK |
223 | mutex_lock(&capi_controller_lock); |
224 | ||
3efecf7a JK |
225 | if (showcapimsgs & 1) |
226 | printk(KERN_DEBUG "kcapi: notify down contr %d\n", contr); | |
227 | ||
228 | ctr = get_capi_ctr_by_nr(contr); | |
229 | if (ctr) | |
0ca3a017 | 230 | ctr_down(ctr, CAPI_CTR_DETECTED); |
3efecf7a JK |
231 | else |
232 | printk(KERN_WARNING "%s: invalid contr %d\n", __func__, contr); | |
0ca3a017 JK |
233 | |
234 | mutex_unlock(&capi_controller_lock); | |
3efecf7a JK |
235 | } |
236 | ||
f59aba2f | 237 | static void do_notify_work(struct work_struct *work) |
1da177e4 | 238 | { |
f59aba2f AB |
239 | struct capictr_event *event = |
240 | container_of(work, struct capictr_event, work); | |
1da177e4 | 241 | |
f59aba2f | 242 | switch (event->type) { |
ef69bb2e | 243 | case CAPICTR_UP: |
f59aba2f | 244 | notify_up(event->controller); |
1da177e4 | 245 | break; |
ef69bb2e | 246 | case CAPICTR_DOWN: |
f59aba2f | 247 | notify_down(event->controller); |
1da177e4 LT |
248 | break; |
249 | } | |
ef69bb2e | 250 | |
ef69bb2e | 251 | kfree(event); |
1da177e4 LT |
252 | } |
253 | ||
ef69bb2e | 254 | static int notify_push(unsigned int event_type, u32 controller) |
1da177e4 | 255 | { |
ef69bb2e | 256 | struct capictr_event *event = kmalloc(sizeof(*event), GFP_ATOMIC); |
1da177e4 | 257 | |
ef69bb2e | 258 | if (!event) |
1da177e4 LT |
259 | return -ENOMEM; |
260 | ||
ef69bb2e JK |
261 | INIT_WORK(&event->work, do_notify_work); |
262 | event->type = event_type; | |
263 | event->controller = controller; | |
1da177e4 | 264 | |
158fa677 | 265 | queue_work(kcapi_wq, &event->work); |
1da177e4 LT |
266 | return 0; |
267 | } | |
268 | ||
1da177e4 LT |
269 | /* -------- Receiver ------------------------------------------ */ |
270 | ||
c4028958 | 271 | static void recv_handler(struct work_struct *work) |
1da177e4 LT |
272 | { |
273 | struct sk_buff *skb; | |
c4028958 DH |
274 | struct capi20_appl *ap = |
275 | container_of(work, struct capi20_appl, recv_work); | |
1da177e4 LT |
276 | |
277 | if ((!ap) || (ap->release_in_progress)) | |
278 | return; | |
279 | ||
67837f23 | 280 | mutex_lock(&ap->recv_mtx); |
1da177e4 LT |
281 | while ((skb = skb_dequeue(&ap->recv_queue))) { |
282 | if (CAPIMSG_CMD(skb->data) == CAPI_DATA_B3_IND) | |
283 | ap->nrecvdatapkt++; | |
284 | else | |
285 | ap->nrecvctlpkt++; | |
286 | ||
287 | ap->recv_message(ap, skb); | |
288 | } | |
67837f23 | 289 | mutex_unlock(&ap->recv_mtx); |
1da177e4 LT |
290 | } |
291 | ||
554f200e TS |
292 | /** |
293 | * capi_ctr_handle_message() - handle incoming CAPI message | |
52253031 | 294 | * @ctr: controller descriptor structure. |
554f200e TS |
295 | * @appl: application ID. |
296 | * @skb: message. | |
297 | * | |
298 | * Called by hardware driver to pass a CAPI message to the application. | |
299 | */ | |
300 | ||
52253031 JK |
301 | void capi_ctr_handle_message(struct capi_ctr *ctr, u16 appl, |
302 | struct sk_buff *skb) | |
1da177e4 LT |
303 | { |
304 | struct capi20_appl *ap; | |
305 | int showctl = 0; | |
306 | u8 cmd, subcmd; | |
17f0cd2f | 307 | _cdebbuf *cdb; |
1da177e4 | 308 | |
52253031 | 309 | if (ctr->state != CAPI_CTR_RUNNING) { |
17f0cd2f KK |
310 | cdb = capi_message2str(skb->data); |
311 | if (cdb) { | |
312 | printk(KERN_INFO "kcapi: controller [%03d] not active, got: %s", | |
475be4d8 | 313 | ctr->cnr, cdb->buf); |
17f0cd2f KK |
314 | cdebbuf_free(cdb); |
315 | } else | |
316 | printk(KERN_INFO "kcapi: controller [%03d] not active, cannot trace\n", | |
475be4d8 | 317 | ctr->cnr); |
1da177e4 LT |
318 | goto error; |
319 | } | |
320 | ||
321 | cmd = CAPIMSG_COMMAND(skb->data); | |
475be4d8 | 322 | subcmd = CAPIMSG_SUBCOMMAND(skb->data); |
1da177e4 | 323 | if (cmd == CAPI_DATA_B3 && subcmd == CAPI_IND) { |
52253031 JK |
324 | ctr->nrecvdatapkt++; |
325 | if (ctr->traceflag > 2) | |
326 | showctl |= 2; | |
1da177e4 | 327 | } else { |
52253031 JK |
328 | ctr->nrecvctlpkt++; |
329 | if (ctr->traceflag) | |
330 | showctl |= 2; | |
1da177e4 | 331 | } |
52253031 | 332 | showctl |= (ctr->traceflag & 1); |
1da177e4 LT |
333 | if (showctl & 2) { |
334 | if (showctl & 1) { | |
17f0cd2f | 335 | printk(KERN_DEBUG "kcapi: got [%03d] id#%d %s len=%u\n", |
52253031 | 336 | ctr->cnr, CAPIMSG_APPID(skb->data), |
1da177e4 LT |
337 | capi_cmd2str(cmd, subcmd), |
338 | CAPIMSG_LEN(skb->data)); | |
339 | } else { | |
17f0cd2f KK |
340 | cdb = capi_message2str(skb->data); |
341 | if (cdb) { | |
342 | printk(KERN_DEBUG "kcapi: got [%03d] %s\n", | |
475be4d8 | 343 | ctr->cnr, cdb->buf); |
17f0cd2f KK |
344 | cdebbuf_free(cdb); |
345 | } else | |
346 | printk(KERN_DEBUG "kcapi: got [%03d] id#%d %s len=%u, cannot trace\n", | |
475be4d8 JP |
347 | ctr->cnr, CAPIMSG_APPID(skb->data), |
348 | capi_cmd2str(cmd, subcmd), | |
349 | CAPIMSG_LEN(skb->data)); | |
1da177e4 LT |
350 | } |
351 | ||
352 | } | |
353 | ||
88c896ef | 354 | rcu_read_lock(); |
1da177e4 | 355 | ap = get_capi_appl_by_nr(CAPIMSG_APPID(skb->data)); |
88c896ef JK |
356 | if (!ap) { |
357 | rcu_read_unlock(); | |
17f0cd2f KK |
358 | cdb = capi_message2str(skb->data); |
359 | if (cdb) { | |
360 | printk(KERN_ERR "kcapi: handle_message: applid %d state released (%s)\n", | |
475be4d8 | 361 | CAPIMSG_APPID(skb->data), cdb->buf); |
17f0cd2f KK |
362 | cdebbuf_free(cdb); |
363 | } else | |
364 | printk(KERN_ERR "kcapi: handle_message: applid %d state released (%s) cannot trace\n", | |
475be4d8 JP |
365 | CAPIMSG_APPID(skb->data), |
366 | capi_cmd2str(cmd, subcmd)); | |
1da177e4 LT |
367 | goto error; |
368 | } | |
369 | skb_queue_tail(&ap->recv_queue, skb); | |
158fa677 | 370 | queue_work(kcapi_wq, &ap->recv_work); |
88c896ef | 371 | rcu_read_unlock(); |
1da177e4 LT |
372 | |
373 | return; | |
374 | ||
375 | error: | |
376 | kfree_skb(skb); | |
377 | } | |
378 | ||
379 | EXPORT_SYMBOL(capi_ctr_handle_message); | |
380 | ||
554f200e TS |
381 | /** |
382 | * capi_ctr_ready() - signal CAPI controller ready | |
52253031 | 383 | * @ctr: controller descriptor structure. |
554f200e TS |
384 | * |
385 | * Called by hardware driver to signal that the controller is up and running. | |
386 | */ | |
387 | ||
52253031 | 388 | void capi_ctr_ready(struct capi_ctr *ctr) |
1da177e4 | 389 | { |
52253031 JK |
390 | printk(KERN_NOTICE "kcapi: controller [%03d] \"%s\" ready.\n", |
391 | ctr->cnr, ctr->name); | |
1da177e4 | 392 | |
ef69bb2e | 393 | notify_push(CAPICTR_UP, ctr->cnr); |
1da177e4 LT |
394 | } |
395 | ||
396 | EXPORT_SYMBOL(capi_ctr_ready); | |
397 | ||
554f200e | 398 | /** |
4e329972 | 399 | * capi_ctr_down() - signal CAPI controller not ready |
52253031 | 400 | * @ctr: controller descriptor structure. |
554f200e TS |
401 | * |
402 | * Called by hardware driver to signal that the controller is down and | |
403 | * unavailable for use. | |
404 | */ | |
405 | ||
52253031 | 406 | void capi_ctr_down(struct capi_ctr *ctr) |
1da177e4 | 407 | { |
52253031 | 408 | printk(KERN_NOTICE "kcapi: controller [%03d] down.\n", ctr->cnr); |
1da177e4 | 409 | |
ef69bb2e | 410 | notify_push(CAPICTR_DOWN, ctr->cnr); |
1da177e4 LT |
411 | } |
412 | ||
4e329972 | 413 | EXPORT_SYMBOL(capi_ctr_down); |
1da177e4 | 414 | |
1da177e4 LT |
415 | /* ------------------------------------------------------------- */ |
416 | ||
554f200e TS |
417 | /** |
418 | * attach_capi_ctr() - register CAPI controller | |
52253031 | 419 | * @ctr: controller descriptor structure. |
554f200e TS |
420 | * |
421 | * Called by hardware driver to register a controller with the CAPI subsystem. | |
422 | * Return value: 0 on success, error code < 0 on error | |
423 | */ | |
424 | ||
52253031 | 425 | int attach_capi_ctr(struct capi_ctr *ctr) |
1da177e4 LT |
426 | { |
427 | int i; | |
428 | ||
0ca3a017 | 429 | mutex_lock(&capi_controller_lock); |
1da177e4 LT |
430 | |
431 | for (i = 0; i < CAPI_MAXCONTR; i++) { | |
52253031 | 432 | if (!capi_controller[i]) |
1da177e4 LT |
433 | break; |
434 | } | |
435 | if (i == CAPI_MAXCONTR) { | |
0ca3a017 | 436 | mutex_unlock(&capi_controller_lock); |
1da177e4 | 437 | printk(KERN_ERR "kcapi: out of controller slots\n"); |
475be4d8 | 438 | return -EBUSY; |
1da177e4 | 439 | } |
52253031 | 440 | capi_controller[i] = ctr; |
1da177e4 | 441 | |
52253031 JK |
442 | ctr->nrecvctlpkt = 0; |
443 | ctr->nrecvdatapkt = 0; | |
444 | ctr->nsentctlpkt = 0; | |
445 | ctr->nsentdatapkt = 0; | |
446 | ctr->cnr = i + 1; | |
447 | ctr->state = CAPI_CTR_DETECTED; | |
448 | ctr->blocked = 0; | |
449 | ctr->traceflag = showcapimsgs; | |
450 | ||
451 | sprintf(ctr->procfn, "capi/controllers/%d", ctr->cnr); | |
2cd1f0dd CH |
452 | ctr->procent = proc_create_single_data(ctr->procfn, 0, NULL, |
453 | ctr->proc_show, ctr); | |
52253031 JK |
454 | |
455 | ncontrollers++; | |
0ca3a017 JK |
456 | |
457 | mutex_unlock(&capi_controller_lock); | |
458 | ||
52253031 | 459 | printk(KERN_NOTICE "kcapi: controller [%03d]: %s attached\n", |
475be4d8 | 460 | ctr->cnr, ctr->name); |
1da177e4 LT |
461 | return 0; |
462 | } | |
463 | ||
464 | EXPORT_SYMBOL(attach_capi_ctr); | |
465 | ||
554f200e TS |
466 | /** |
467 | * detach_capi_ctr() - unregister CAPI controller | |
52253031 | 468 | * @ctr: controller descriptor structure. |
554f200e TS |
469 | * |
470 | * Called by hardware driver to remove the registration of a controller | |
471 | * with the CAPI subsystem. | |
472 | * Return value: 0 on success, error code < 0 on error | |
473 | */ | |
474 | ||
52253031 | 475 | int detach_capi_ctr(struct capi_ctr *ctr) |
1da177e4 | 476 | { |
0ca3a017 | 477 | int err = 0; |
1da177e4 | 478 | |
0ca3a017 | 479 | mutex_lock(&capi_controller_lock); |
1da177e4 | 480 | |
0ca3a017 JK |
481 | ctr_down(ctr, CAPI_CTR_DETACHED); |
482 | ||
1f3e2e97 XH |
483 | if (ctr->cnr < 1 || ctr->cnr - 1 >= CAPI_MAXCONTR) { |
484 | err = -EINVAL; | |
485 | goto unlock_out; | |
486 | } | |
487 | ||
0ca3a017 JK |
488 | if (capi_controller[ctr->cnr - 1] != ctr) { |
489 | err = -EINVAL; | |
490 | goto unlock_out; | |
1da177e4 | 491 | } |
52253031 | 492 | capi_controller[ctr->cnr - 1] = NULL; |
0ca3a017 JK |
493 | ncontrollers--; |
494 | ||
495 | if (ctr->procent) | |
496 | remove_proc_entry(ctr->procfn, NULL); | |
497 | ||
52253031 JK |
498 | printk(KERN_NOTICE "kcapi: controller [%03d]: %s unregistered\n", |
499 | ctr->cnr, ctr->name); | |
1da177e4 | 500 | |
0ca3a017 JK |
501 | unlock_out: |
502 | mutex_unlock(&capi_controller_lock); | |
503 | ||
504 | return err; | |
1da177e4 LT |
505 | } |
506 | ||
507 | EXPORT_SYMBOL(detach_capi_ctr); | |
508 | ||
1da177e4 LT |
509 | /* ------------------------------------------------------------- */ |
510 | /* -------- CAPI2.0 Interface ---------------------------------- */ | |
511 | /* ------------------------------------------------------------- */ | |
512 | ||
554f200e TS |
513 | /** |
514 | * capi20_isinstalled() - CAPI 2.0 operation CAPI_INSTALLED | |
515 | * | |
516 | * Return value: CAPI result code (CAPI_NOERROR if at least one ISDN controller | |
517 | * is ready for use, CAPI_REGNOTINSTALLED otherwise) | |
518 | */ | |
519 | ||
1da177e4 LT |
520 | u16 capi20_isinstalled(void) |
521 | { | |
0ca3a017 | 522 | u16 ret = CAPI_REGNOTINSTALLED; |
1da177e4 | 523 | int i; |
0ca3a017 JK |
524 | |
525 | mutex_lock(&capi_controller_lock); | |
526 | ||
527 | for (i = 0; i < CAPI_MAXCONTR; i++) | |
52253031 | 528 | if (capi_controller[i] && |
0ca3a017 JK |
529 | capi_controller[i]->state == CAPI_CTR_RUNNING) { |
530 | ret = CAPI_NOERROR; | |
531 | break; | |
532 | } | |
533 | ||
534 | mutex_unlock(&capi_controller_lock); | |
535 | ||
536 | return ret; | |
1da177e4 LT |
537 | } |
538 | ||
554f200e TS |
539 | /** |
540 | * capi20_register() - CAPI 2.0 operation CAPI_REGISTER | |
541 | * @ap: CAPI application descriptor structure. | |
542 | * | |
543 | * Register an application's presence with CAPI. | |
544 | * A unique application ID is assigned and stored in @ap->applid. | |
545 | * After this function returns successfully, the message receive | |
546 | * callback function @ap->recv_message() may be called at any time | |
547 | * until capi20_release() has been called for the same @ap. | |
548 | * Return value: CAPI result code | |
549 | */ | |
550 | ||
1da177e4 LT |
551 | u16 capi20_register(struct capi20_appl *ap) |
552 | { | |
553 | int i; | |
554 | u16 applid; | |
1da177e4 LT |
555 | |
556 | DBG(""); | |
557 | ||
558 | if (ap->rparam.datablklen < 128) | |
559 | return CAPI_LOGBLKSIZETOSMALL; | |
560 | ||
88c896ef JK |
561 | ap->nrecvctlpkt = 0; |
562 | ap->nrecvdatapkt = 0; | |
563 | ap->nsentctlpkt = 0; | |
564 | ap->nsentdatapkt = 0; | |
565 | mutex_init(&ap->recv_mtx); | |
566 | skb_queue_head_init(&ap->recv_queue); | |
567 | INIT_WORK(&ap->recv_work, recv_handler); | |
568 | ap->release_in_progress = 0; | |
569 | ||
570 | mutex_lock(&capi_controller_lock); | |
1da177e4 LT |
571 | |
572 | for (applid = 1; applid <= CAPI_MAXAPPL; applid++) { | |
573 | if (capi_applications[applid - 1] == NULL) | |
574 | break; | |
575 | } | |
576 | if (applid > CAPI_MAXAPPL) { | |
88c896ef | 577 | mutex_unlock(&capi_controller_lock); |
1da177e4 LT |
578 | return CAPI_TOOMANYAPPLS; |
579 | } | |
580 | ||
581 | ap->applid = applid; | |
582 | capi_applications[applid - 1] = ap; | |
583 | ||
1da177e4 | 584 | for (i = 0; i < CAPI_MAXCONTR; i++) { |
52253031 JK |
585 | if (!capi_controller[i] || |
586 | capi_controller[i]->state != CAPI_CTR_RUNNING) | |
1da177e4 | 587 | continue; |
52253031 | 588 | register_appl(capi_controller[i], applid, &ap->rparam); |
1da177e4 | 589 | } |
0ca3a017 JK |
590 | |
591 | mutex_unlock(&capi_controller_lock); | |
1da177e4 LT |
592 | |
593 | if (showcapimsgs & 1) { | |
594 | printk(KERN_DEBUG "kcapi: appl %d up\n", applid); | |
595 | } | |
596 | ||
597 | return CAPI_NOERROR; | |
598 | } | |
599 | ||
554f200e TS |
600 | /** |
601 | * capi20_release() - CAPI 2.0 operation CAPI_RELEASE | |
602 | * @ap: CAPI application descriptor structure. | |
603 | * | |
604 | * Terminate an application's registration with CAPI. | |
605 | * After this function returns successfully, the message receive | |
606 | * callback function @ap->recv_message() will no longer be called. | |
607 | * Return value: CAPI result code | |
608 | */ | |
609 | ||
1da177e4 LT |
610 | u16 capi20_release(struct capi20_appl *ap) |
611 | { | |
612 | int i; | |
1da177e4 LT |
613 | |
614 | DBG("applid %#x", ap->applid); | |
615 | ||
88c896ef JK |
616 | mutex_lock(&capi_controller_lock); |
617 | ||
1da177e4 LT |
618 | ap->release_in_progress = 1; |
619 | capi_applications[ap->applid - 1] = NULL; | |
1da177e4 | 620 | |
88c896ef | 621 | synchronize_rcu(); |
0ca3a017 | 622 | |
1da177e4 | 623 | for (i = 0; i < CAPI_MAXCONTR; i++) { |
52253031 JK |
624 | if (!capi_controller[i] || |
625 | capi_controller[i]->state != CAPI_CTR_RUNNING) | |
1da177e4 | 626 | continue; |
52253031 | 627 | release_appl(capi_controller[i], ap->applid); |
1da177e4 | 628 | } |
0ca3a017 JK |
629 | |
630 | mutex_unlock(&capi_controller_lock); | |
1da177e4 | 631 | |
158fa677 | 632 | flush_workqueue(kcapi_wq); |
1da177e4 LT |
633 | skb_queue_purge(&ap->recv_queue); |
634 | ||
635 | if (showcapimsgs & 1) { | |
636 | printk(KERN_DEBUG "kcapi: appl %d down\n", ap->applid); | |
637 | } | |
638 | ||
639 | return CAPI_NOERROR; | |
640 | } | |
641 | ||
554f200e TS |
642 | /** |
643 | * capi20_put_message() - CAPI 2.0 operation CAPI_PUT_MESSAGE | |
644 | * @ap: CAPI application descriptor structure. | |
645 | * @skb: CAPI message. | |
646 | * | |
647 | * Transfer a single message to CAPI. | |
648 | * Return value: CAPI result code | |
649 | */ | |
650 | ||
1da177e4 LT |
651 | u16 capi20_put_message(struct capi20_appl *ap, struct sk_buff *skb) |
652 | { | |
52253031 | 653 | struct capi_ctr *ctr; |
1da177e4 LT |
654 | int showctl = 0; |
655 | u8 cmd, subcmd; | |
656 | ||
657 | DBG("applid %#x", ap->applid); | |
475be4d8 | 658 | |
52253031 | 659 | if (ncontrollers == 0) |
1da177e4 LT |
660 | return CAPI_REGNOTINSTALLED; |
661 | if ((ap->applid == 0) || ap->release_in_progress) | |
662 | return CAPI_ILLAPPNR; | |
663 | if (skb->len < 12 | |
664 | || !capi_cmd_valid(CAPIMSG_COMMAND(skb->data)) | |
665 | || !capi_subcmd_valid(CAPIMSG_SUBCOMMAND(skb->data))) | |
666 | return CAPI_ILLCMDORSUBCMDORMSGTOSMALL; | |
0ca3a017 JK |
667 | |
668 | /* | |
669 | * The controller reference is protected by the existence of the | |
670 | * application passed to us. We assume that the caller properly | |
671 | * synchronizes this service with capi20_release. | |
672 | */ | |
52253031 | 673 | ctr = get_capi_ctr_by_nr(CAPIMSG_CONTROLLER(skb->data)); |
c6af0437 JK |
674 | if (!ctr || ctr->state != CAPI_CTR_RUNNING) |
675 | return CAPI_REGNOTINSTALLED; | |
52253031 | 676 | if (ctr->blocked) |
1da177e4 LT |
677 | return CAPI_SENDQUEUEFULL; |
678 | ||
679 | cmd = CAPIMSG_COMMAND(skb->data); | |
475be4d8 | 680 | subcmd = CAPIMSG_SUBCOMMAND(skb->data); |
1da177e4 | 681 | |
475be4d8 | 682 | if (cmd == CAPI_DATA_B3 && subcmd == CAPI_REQ) { |
52253031 | 683 | ctr->nsentdatapkt++; |
1da177e4 | 684 | ap->nsentdatapkt++; |
52253031 JK |
685 | if (ctr->traceflag > 2) |
686 | showctl |= 2; | |
1da177e4 | 687 | } else { |
52253031 | 688 | ctr->nsentctlpkt++; |
1da177e4 | 689 | ap->nsentctlpkt++; |
52253031 JK |
690 | if (ctr->traceflag) |
691 | showctl |= 2; | |
1da177e4 | 692 | } |
52253031 | 693 | showctl |= (ctr->traceflag & 1); |
1da177e4 LT |
694 | if (showctl & 2) { |
695 | if (showctl & 1) { | |
17f0cd2f | 696 | printk(KERN_DEBUG "kcapi: put [%03d] id#%d %s len=%u\n", |
1da177e4 LT |
697 | CAPIMSG_CONTROLLER(skb->data), |
698 | CAPIMSG_APPID(skb->data), | |
699 | capi_cmd2str(cmd, subcmd), | |
700 | CAPIMSG_LEN(skb->data)); | |
701 | } else { | |
17f0cd2f KK |
702 | _cdebbuf *cdb = capi_message2str(skb->data); |
703 | if (cdb) { | |
704 | printk(KERN_DEBUG "kcapi: put [%03d] %s\n", | |
475be4d8 JP |
705 | CAPIMSG_CONTROLLER(skb->data), |
706 | cdb->buf); | |
17f0cd2f KK |
707 | cdebbuf_free(cdb); |
708 | } else | |
709 | printk(KERN_DEBUG "kcapi: put [%03d] id#%d %s len=%u cannot trace\n", | |
475be4d8 JP |
710 | CAPIMSG_CONTROLLER(skb->data), |
711 | CAPIMSG_APPID(skb->data), | |
712 | capi_cmd2str(cmd, subcmd), | |
713 | CAPIMSG_LEN(skb->data)); | |
1da177e4 | 714 | } |
1da177e4 | 715 | } |
52253031 | 716 | return ctr->send_message(ctr, skb); |
1da177e4 LT |
717 | } |
718 | ||
554f200e TS |
719 | /** |
720 | * capi20_get_manufacturer() - CAPI 2.0 operation CAPI_GET_MANUFACTURER | |
721 | * @contr: controller number. | |
722 | * @buf: result buffer (64 bytes). | |
723 | * | |
724 | * Retrieve information about the manufacturer of the specified ISDN controller | |
725 | * or (for @contr == 0) the driver itself. | |
726 | * Return value: CAPI result code | |
727 | */ | |
728 | ||
5ee7d4c7 | 729 | u16 capi20_get_manufacturer(u32 contr, u8 buf[CAPI_MANUFACTURER_LEN]) |
1da177e4 | 730 | { |
52253031 | 731 | struct capi_ctr *ctr; |
0ca3a017 | 732 | u16 ret; |
1da177e4 LT |
733 | |
734 | if (contr == 0) { | |
d63967e4 | 735 | strncpy(buf, capi_manufakturer, CAPI_MANUFACTURER_LEN); |
1da177e4 LT |
736 | return CAPI_NOERROR; |
737 | } | |
0ca3a017 JK |
738 | |
739 | mutex_lock(&capi_controller_lock); | |
740 | ||
52253031 | 741 | ctr = get_capi_ctr_by_nr(contr); |
0ca3a017 | 742 | if (ctr && ctr->state == CAPI_CTR_RUNNING) { |
d63967e4 | 743 | strncpy(buf, ctr->manu, CAPI_MANUFACTURER_LEN); |
0ca3a017 JK |
744 | ret = CAPI_NOERROR; |
745 | } else | |
746 | ret = CAPI_REGNOTINSTALLED; | |
747 | ||
748 | mutex_unlock(&capi_controller_lock); | |
749 | return ret; | |
1da177e4 LT |
750 | } |
751 | ||
554f200e TS |
752 | /** |
753 | * capi20_get_version() - CAPI 2.0 operation CAPI_GET_VERSION | |
754 | * @contr: controller number. | |
755 | * @verp: result structure. | |
756 | * | |
757 | * Retrieve version information for the specified ISDN controller | |
758 | * or (for @contr == 0) the driver itself. | |
759 | * Return value: CAPI result code | |
760 | */ | |
761 | ||
1da177e4 LT |
762 | u16 capi20_get_version(u32 contr, struct capi_version *verp) |
763 | { | |
52253031 | 764 | struct capi_ctr *ctr; |
0ca3a017 | 765 | u16 ret; |
1da177e4 LT |
766 | |
767 | if (contr == 0) { | |
768 | *verp = driver_version; | |
769 | return CAPI_NOERROR; | |
770 | } | |
0ca3a017 JK |
771 | |
772 | mutex_lock(&capi_controller_lock); | |
773 | ||
52253031 | 774 | ctr = get_capi_ctr_by_nr(contr); |
0ca3a017 JK |
775 | if (ctr && ctr->state == CAPI_CTR_RUNNING) { |
776 | memcpy(verp, &ctr->version, sizeof(capi_version)); | |
777 | ret = CAPI_NOERROR; | |
778 | } else | |
779 | ret = CAPI_REGNOTINSTALLED; | |
1da177e4 | 780 | |
0ca3a017 JK |
781 | mutex_unlock(&capi_controller_lock); |
782 | return ret; | |
1da177e4 LT |
783 | } |
784 | ||
554f200e TS |
785 | /** |
786 | * capi20_get_serial() - CAPI 2.0 operation CAPI_GET_SERIAL_NUMBER | |
787 | * @contr: controller number. | |
788 | * @serial: result buffer (8 bytes). | |
789 | * | |
790 | * Retrieve the serial number of the specified ISDN controller | |
791 | * or (for @contr == 0) the driver itself. | |
792 | * Return value: CAPI result code | |
793 | */ | |
794 | ||
5ee7d4c7 | 795 | u16 capi20_get_serial(u32 contr, u8 serial[CAPI_SERIAL_LEN]) |
1da177e4 | 796 | { |
52253031 | 797 | struct capi_ctr *ctr; |
0ca3a017 | 798 | u16 ret; |
1da177e4 LT |
799 | |
800 | if (contr == 0) { | |
cdb27b7b | 801 | strscpy(serial, driver_serial, CAPI_SERIAL_LEN); |
1da177e4 LT |
802 | return CAPI_NOERROR; |
803 | } | |
0ca3a017 JK |
804 | |
805 | mutex_lock(&capi_controller_lock); | |
806 | ||
52253031 | 807 | ctr = get_capi_ctr_by_nr(contr); |
0ca3a017 | 808 | if (ctr && ctr->state == CAPI_CTR_RUNNING) { |
cdb27b7b | 809 | strscpy(serial, ctr->serial, CAPI_SERIAL_LEN); |
0ca3a017 JK |
810 | ret = CAPI_NOERROR; |
811 | } else | |
812 | ret = CAPI_REGNOTINSTALLED; | |
1da177e4 | 813 | |
0ca3a017 JK |
814 | mutex_unlock(&capi_controller_lock); |
815 | return ret; | |
1da177e4 LT |
816 | } |
817 | ||
554f200e TS |
818 | /** |
819 | * capi20_get_profile() - CAPI 2.0 operation CAPI_GET_PROFILE | |
820 | * @contr: controller number. | |
821 | * @profp: result structure. | |
822 | * | |
823 | * Retrieve capability information for the specified ISDN controller | |
824 | * or (for @contr == 0) the number of installed controllers. | |
825 | * Return value: CAPI result code | |
826 | */ | |
827 | ||
1da177e4 LT |
828 | u16 capi20_get_profile(u32 contr, struct capi_profile *profp) |
829 | { | |
52253031 | 830 | struct capi_ctr *ctr; |
0ca3a017 | 831 | u16 ret; |
1da177e4 LT |
832 | |
833 | if (contr == 0) { | |
52253031 | 834 | profp->ncontroller = ncontrollers; |
1da177e4 LT |
835 | return CAPI_NOERROR; |
836 | } | |
0ca3a017 JK |
837 | |
838 | mutex_lock(&capi_controller_lock); | |
839 | ||
52253031 | 840 | ctr = get_capi_ctr_by_nr(contr); |
0ca3a017 JK |
841 | if (ctr && ctr->state == CAPI_CTR_RUNNING) { |
842 | memcpy(profp, &ctr->profile, sizeof(struct capi_profile)); | |
843 | ret = CAPI_NOERROR; | |
844 | } else | |
845 | ret = CAPI_REGNOTINSTALLED; | |
1da177e4 | 846 | |
0ca3a017 JK |
847 | mutex_unlock(&capi_controller_lock); |
848 | return ret; | |
1da177e4 LT |
849 | } |
850 | ||
554f200e TS |
851 | /** |
852 | * capi20_manufacturer() - CAPI 2.0 operation CAPI_MANUFACTURER | |
853 | * @cmd: command. | |
854 | * @data: parameter. | |
855 | * | |
856 | * Perform manufacturer specific command. | |
857 | * Return value: CAPI result code | |
858 | */ | |
859 | ||
9ea8aa8d | 860 | int capi20_manufacturer(unsigned long cmd, void __user *data) |
1da177e4 | 861 | { |
52253031 | 862 | struct capi_ctr *ctr; |
0ca3a017 | 863 | int retval; |
1da177e4 LT |
864 | |
865 | switch (cmd) { | |
1da177e4 LT |
866 | case KCAPI_CMD_TRACE: |
867 | { | |
868 | kcapi_flagdef fdef; | |
869 | ||
870 | if (copy_from_user(&fdef, data, sizeof(kcapi_flagdef))) | |
871 | return -EFAULT; | |
872 | ||
0ca3a017 JK |
873 | mutex_lock(&capi_controller_lock); |
874 | ||
52253031 | 875 | ctr = get_capi_ctr_by_nr(fdef.contr); |
0ca3a017 JK |
876 | if (ctr) { |
877 | ctr->traceflag = fdef.flag; | |
878 | printk(KERN_INFO "kcapi: contr [%03d] set trace=%d\n", | |
879 | ctr->cnr, ctr->traceflag); | |
880 | retval = 0; | |
881 | } else | |
882 | retval = -ESRCH; | |
883 | ||
884 | mutex_unlock(&capi_controller_lock); | |
1da177e4 | 885 | |
0ca3a017 | 886 | return retval; |
1da177e4 | 887 | } |
1da177e4 LT |
888 | |
889 | default: | |
9ea8aa8d | 890 | printk(KERN_ERR "kcapi: manufacturer command %lu unknown.\n", |
475be4d8 | 891 | cmd); |
1da177e4 LT |
892 | break; |
893 | ||
894 | } | |
895 | return -EINVAL; | |
896 | } | |
897 | ||
1da177e4 LT |
898 | /* ------------------------------------------------------------- */ |
899 | /* -------- Init & Cleanup ------------------------------------- */ | |
900 | /* ------------------------------------------------------------- */ | |
901 | ||
902 | /* | |
903 | * init / exit functions | |
904 | */ | |
905 | ||
f59aba2f | 906 | int __init kcapi_init(void) |
1da177e4 | 907 | { |
88549d6b | 908 | int err; |
1da177e4 | 909 | |
158fa677 TH |
910 | kcapi_wq = alloc_workqueue("kcapi", 0, 0); |
911 | if (!kcapi_wq) | |
912 | return -ENOMEM; | |
913 | ||
88549d6b | 914 | err = cdebug_init(); |
158fa677 | 915 | if (err) { |
158fa677 TH |
916 | destroy_workqueue(kcapi_wq); |
917 | return err; | |
918 | } | |
919 | ||
920 | kcapi_proc_init(); | |
921 | return 0; | |
1da177e4 LT |
922 | } |
923 | ||
f59aba2f | 924 | void kcapi_exit(void) |
1da177e4 | 925 | { |
475be4d8 | 926 | kcapi_proc_exit(); |
1da177e4 | 927 | |
17f0cd2f | 928 | cdebug_exit(); |
158fa677 | 929 | destroy_workqueue(kcapi_wq); |
1da177e4 | 930 | } |