File: /usr/src/linux/drivers/isdn/tpam/tpam_nco.c
1 /* $Id: tpam_nco.c,v 1.1.2.1 2001/06/05 19:45:37 kai Exp $
2 *
3 * Turbo PAM ISDN driver for Linux. (Kernel Driver - Low Level NCO Manipulation)
4 *
5 * Copyright 2001 Stelian Pop <stelian.pop@fr.alcove.com>, Alcôve
6 *
7 * For all support questions please contact: <support@auvertech.fr>
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2, or (at your option)
12 * any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 *
23 */
24 #include <linux/config.h>
25 #include <linux/pci.h>
26 #include <linux/sched.h>
27 #include <linux/tqueue.h>
28 #include <linux/interrupt.h>
29 #include <asm/io.h>
30
31 #include "tpam.h"
32
33 /* Local function prototypes */
34 static struct sk_buff *build_NCOpacket(u16, u16, u16, u16, u16);
35 static int extract_NCOParameter(struct sk_buff *, u8, void *, u16);
36
37 /*
38 * Build a NCO packet (PCI message).
39 *
40 * messageID: the message type (ID_*)
41 * size: size of the TLV block
42 * data_size: size of the data block
43 * ack: packet needs to send ack upon send
44 * ack_size: size of data to be acknowledged upon send
45 *
46 * Return: the sk_buff filled with the NCO packet, or NULL if error.
47 */
48 static struct sk_buff *build_NCOpacket(u16 messageID, u16 size,
49 u16 data_size, u16 ack,
50 u16 ack_size) {
51 struct sk_buff *skb;
52 skb_header *h;
53 pci_mpb *p;
54 u16 finalsize;
55
56 /* reserve enough space for the sk_buff header, the pci * header,
57 * size bytes for the TLV block, size bytes for the data and 4 more
58 * bytes in order to make sure we can write dwords to the board. */
59 finalsize = sizeof(skb_header) + sizeof(pci_mpb) + size + data_size + 4;
60
61 /* allocate the sk_buff */
62 if (!(skb = alloc_skb(finalsize, GFP_ATOMIC))) {
63 printk(KERN_ERR "TurboPAM(make_NCOpacket): alloc_skb failed\n");
64 return NULL;
65 }
66
67 /* construct the skb_header */
68 h = (skb_header *)skb_put(skb, sizeof(skb_header));
69 h->size = sizeof(pci_mpb) + size;
70 h->data_size = data_size;
71 h->ack = ack;
72 h->ack_size = ack_size;
73
74 /* construct the pci_mpb */
75 p = (pci_mpb *)skb_put(skb, sizeof(pci_mpb));
76 p->exID = 0;
77 p->flags = 0;
78 p->errorCode = 0;
79 p->messageID = messageID;
80 p->maximumBlockTLVSize = MPB_MAXIMUMBLOCKTLVSIZE;
81 p->actualBlockTLVSize = size;
82 p->maximumDataSize = MPB_MAXIMUMDATASIZE;
83 p->actualDataSize = data_size;
84 return skb;
85 }
86
87 /*
88 * Build a ACreateNCOReq message.
89 *
90 * phone: the local phone number.
91 *
92 * Return: the sk_buff filled with the NCO packet, or NULL if error.
93 */
94 struct sk_buff *build_ACreateNCOReq(const u8 *phone) {
95 struct sk_buff *skb;
96 u8 *tlv;
97
98 dprintk("TurboPAM(build_ACreateNCOReq): phone=%s\n", phone);
99
100 /* build the NCO packet */
101 if (!(skb = build_NCOpacket(ID_ACreateNCOReq, 23 + strlen(phone), 0, 0, 0)))
102 return NULL;
103
104 /* add the parameters */
105 tlv = (u8 *)skb_put(skb, 3);
106 *tlv = PAR_NCOType;
107 *(tlv+1) = 1;
108 *(tlv+2) = 5; /* mistery value... */
109
110 tlv = (u8 *)skb_put(skb, 4);
111 *tlv = PAR_U3Protocol;
112 *(tlv+1) = 2;
113 *(tlv+2) = 4; /* no level 3 protocol */
114 *(tlv+3) = 1; /* HDLC in level 2 */
115
116 tlv = (u8 *)skb_put(skb, 3);
117 *tlv = PAR_Cdirection;
118 *(tlv+1) = 1;
119 *(tlv+2) = 3; /* PCI_DIRECTION_BOTH */
120
121 tlv = (u8 *)skb_put(skb, 3);
122 *tlv = PAR_Udirection;
123 *(tlv+1) = 1;
124 *(tlv+2) = 3; /* PCI_DIRECTION_BOTH */
125
126 tlv = (u8 *)skb_put(skb, 4);
127 *tlv = PAR_BearerCap;
128 *(tlv+1) = 2;
129 *(tlv+2) = 0x88;
130 *(tlv+3) = 0x90;
131
132 tlv = (u8 *)skb_put(skb, 6 + strlen(phone));
133 *tlv = PAR_CallingNumber;
134 *(tlv+1) = strlen(phone) + 4;
135 *(tlv+2) = 0x01; /* international */
136 *(tlv+3) = 0x01; /* isdn */
137 *(tlv+4) = 0x00;
138 *(tlv+5) = 0x00;
139 memcpy(tlv + 6, phone, strlen(phone));
140
141 return skb;
142 }
143
144 /*
145 * Build a ADestroyNCOReq message.
146 *
147 * ncoid: the NCO id.
148 *
149 * Return: the sk_buff filled with the NCO packet, or NULL if error.
150 */
151 struct sk_buff *build_ADestroyNCOReq(u32 ncoid) {
152 struct sk_buff *skb;
153 u8 *tlv;
154
155 dprintk("TurboPAM(build_ADestroyNCOReq): ncoid=%lu\n",
156 (unsigned long)ncoid);
157
158 /* build the NCO packet */
159 if (!(skb = build_NCOpacket(ID_ADestroyNCOReq, 6, 0, 0, 0)))
160 return NULL;
161
162 /* add the parameters */
163 tlv = (u8 *)skb_put(skb, 6);
164 *tlv = PAR_NCOID;
165 *(tlv+1) = 4;
166 *((u32 *)(tlv+2)) = ncoid;
167
168 return skb;
169 }
170
171 /*
172 * Build a CConnectReq message.
173 *
174 * ncoid: the NCO id.
175 * called: the destination phone number
176 * hdlc: type of connection: 1 (HDLC) or 0(modem)
177 *
178 * Return: the sk_buff filled with the NCO packet, or NULL if error.
179 */
180 struct sk_buff *build_CConnectReq(u32 ncoid, const u8 *called, u8 hdlc) {
181 struct sk_buff *skb;
182 u8 *tlv;
183
184 dprintk("TurboPAM(build_CConnectReq): ncoid=%lu, called=%s, hdlc=%d\n",
185 (unsigned long)ncoid, called, hdlc);
186
187 /* build the NCO packet */
188 if (!(skb = build_NCOpacket(ID_CConnectReq, 20 + strlen(called), 0, 0, 0)))
189 return NULL;
190
191 /* add the parameters */
192 tlv = (u8 *)skb_put(skb, 6);
193 *tlv = PAR_NCOID;
194 *(tlv+1) = 4;
195 *((u32 *)(tlv+2)) = ncoid;
196
197 tlv = (u8 *)skb_put(skb, 4 + strlen(called));
198 *tlv = PAR_CalledNumber;
199 *(tlv+1) = strlen(called) + 2;
200 *(tlv+2) = 0x01; /* international */
201 *(tlv+3) = 0x01; /* isdn */
202 memcpy(tlv + 4, called, strlen(called));
203
204 tlv = (u8 *)skb_put(skb, 3);
205 *tlv = PAR_BearerCap;
206 *(tlv+1) = 1;
207 *(tlv+2) = hdlc ? 0x88 /* HDLC */ : 0x80 /* MODEM */;
208
209 tlv = (u8 *)skb_put(skb, 4);
210 *tlv = PAR_HLC;
211 *(tlv+1) = 2;
212 *(tlv+2) = 0x2;
213 *(tlv+3) = 0x7f;
214
215 tlv = (u8 *)skb_put(skb, 3);
216 *tlv = PAR_Facility;
217 *(tlv+1) = 1;
218 *(tlv+2) = 2;
219
220 return skb;
221 }
222
223 /*
224 * Build a CConnectRsp message.
225 *
226 * ncoid: the NCO id.
227 *
228 * Return: the sk_buff filled with the NCO packet, or NULL if error.
229 */
230 struct sk_buff *build_CConnectRsp(u32 ncoid) {
231 struct sk_buff *skb;
232 u8 *tlv;
233
234 dprintk("TurboPAM(build_CConnectRsp): ncoid=%lu\n",
235 (unsigned long)ncoid);
236
237 /* build the NCO packet */
238 if (!(skb = build_NCOpacket(ID_CConnectRsp, 6, 0, 0, 0)))
239 return NULL;
240
241 /* add the parameters */
242 tlv = (u8 *)skb_put(skb, 6);
243 *tlv = PAR_NCOID;
244 *(tlv+1) = 4;
245 *((u32 *)(tlv+2)) = ncoid;
246
247 return skb;
248 }
249
250 /*
251 * Build a CDisconnectReq message.
252 *
253 * ncoid: the NCO id.
254 *
255 * Return: the sk_buff filled with the NCO packet, or NULL if error.
256 */
257 struct sk_buff *build_CDisconnectReq(u32 ncoid) {
258 struct sk_buff *skb;
259 u8 *tlv;
260
261 dprintk("TurboPAM(build_CDisconnectReq): ncoid=%lu\n",
262 (unsigned long)ncoid);
263
264 /* build the NCO packet */
265 if (!(skb = build_NCOpacket(ID_CDisconnectReq, 6, 0, 0, 0)))
266 return NULL;
267
268 /* add the parameters */
269 tlv = (u8 *)skb_put(skb, 6);
270 *tlv = PAR_NCOID;
271 *(tlv+1) = 4;
272 *((u32 *)(tlv+2)) = ncoid;
273
274 return skb;
275 }
276
277 /*
278 * Build a CDisconnectRsp message.
279 *
280 * ncoid: the NCO id.
281 *
282 * Return: the sk_buff filled with the NCO packet, or NULL if error.
283 */
284 struct sk_buff *build_CDisconnectRsp(u32 ncoid) {
285 struct sk_buff *skb;
286 u8 *tlv;
287
288 dprintk("TurboPAM(build_CDisconnectRsp): ncoid=%lu\n",
289 (unsigned long)ncoid);
290
291 /* build the NCO packet */
292 if (!(skb = build_NCOpacket(ID_CDisconnectRsp, 6, 0, 0, 0)))
293 return NULL;
294
295 /* add the parameters */
296 tlv = (u8 *)skb_put(skb, 6);
297 *tlv = PAR_NCOID;
298 *(tlv+1) = 4;
299 *((u32 *)(tlv+2)) = ncoid;
300
301 return skb;
302 }
303
304 /*
305 * Build a U3DataReq message.
306 *
307 * ncoid: the NCO id.
308 * data: the data to be send
309 * len: length of the data
310 * ack: send ack upon send
311 * ack_size: size of data to be acknowledged upon send
312 *
313 * Return: the sk_buff filled with the NCO packet, or NULL if error.
314 */
315 struct sk_buff *build_U3DataReq(u32 ncoid, void *data, u16 len,
316 u16 ack, u16 ack_size) {
317 struct sk_buff *skb;
318 u8 *tlv;
319 void *p;
320
321 dprintk("TurboPAM(build_U3DataReq): "
322 "ncoid=%lu, len=%d, ack=%d, ack_size=%d\n",
323 (unsigned long)ncoid, len, ack, ack_size);
324
325 /* build the NCO packet */
326 if (!(skb = build_NCOpacket(ID_U3DataReq, 6, len, ack, ack_size)))
327 return NULL;
328
329 /* add the parameters */
330 tlv = (u8 *)skb_put(skb, 6);
331 *tlv = PAR_NCOID;
332 *(tlv+1) = 4;
333 *((u32 *)(tlv+2)) = ncoid;
334
335 p = skb_put(skb, len);
336 memcpy(p, data, len);
337
338 return skb;
339 }
340
341 /*
342 * Extract a parameter from a TLV block.
343 *
344 * skb: sk_buff containing the PCI message
345 * type: parameter to search for (PARAM_*)
346 * value: to be filled with the value of the parameter
347 * len: maximum length of the parameter value
348 *
349 * Return: 0 if OK, <0 if error.
350 */
351 static int extract_NCOParameter(struct sk_buff *skb, u8 type,
352 void *value, u16 len) {
353 void *buffer = (void *)skb->data;
354 pci_mpb *p;
355 void * bufferend;
356 u8 valtype;
357 u16 vallen;
358
359 /* calculate the start and end of the TLV block */
360 buffer += sizeof(skb_header);
361 p = (pci_mpb *)buffer;
362 buffer += sizeof(pci_mpb);
363 bufferend = buffer + p->actualBlockTLVSize;
364
365 /* walk through the parameters */
366 while (buffer < bufferend) {
367
368 /* parameter type */
369 valtype = *((u8 *)buffer++);
370 /* parameter length */
371 vallen = *((u8 *)buffer++);
372 if (vallen == 0xff) {
373 /* parameter length is on 2 bytes */
374 vallen = *((u8 *)buffer++);
375 vallen <<= 8;
376 vallen |= *((u8 *)buffer++);
377 }
378 /* got the right parameter */
379 if (valtype == type) {
380 /* not enough space for returning the value */
381 if (vallen > len)
382 return -1;
383 /* OK, return it */
384 memcpy(value, buffer, vallen);
385 return 0;
386 }
387 buffer += vallen;
388 }
389 return -1;
390 }
391
392 /*
393 * Parse a ACreateNCOCnf message.
394 *
395 * skb: the sk_buff containing the message
396 * status: to be filled with the status field value
397 * ncoid: to be filled with the ncoid field value
398 *
399 * Return: 0 if OK, <0 if error.
400 */
401 int parse_ACreateNCOCnf(struct sk_buff *skb, u8 *status, u32 *ncoid) {
402
403 /* extract the status */
404 if (extract_NCOParameter(skb, PAR_CompletionStatus, status, 1)) {
405 printk(KERN_ERR "TurboPAM(parse_ACreateNCOCnf): "
406 "CompletionStatus not found\n");
407 return -1;
408 }
409
410 if (*status) {
411 dprintk("TurboPAM(parse_ACreateNCOCnf): status=%d\n", *status);
412 return 0;
413 }
414
415 /* extract the ncoid */
416 if (extract_NCOParameter(skb, PAR_NCOID, ncoid, 4)) {
417 printk(KERN_ERR "TurboPAM(parse_ACreateNCOCnf): "
418 "NCOID not found\n");
419 return -1;
420 }
421
422 dprintk("TurboPAM(parse_ACreateNCOCnf): ncoid=%lu, status=%d\n",
423 (unsigned long)*ncoid, *status);
424 return 0;
425 }
426
427 /*
428 * Parse a ADestroyNCOCnf message. Not used in the driver.
429 *
430 * skb: the sk_buff containing the message
431 * status: to be filled with the status field value
432 * ncoid: to be filled with the ncoid field value
433 *
434 * Return: 0 if OK, <0 if error.
435 */
436 int parse_ADestroyNCOCnf(struct sk_buff *skb, u8 *status, u32 *ncoid) {
437
438 /* extract the status */
439 if (extract_NCOParameter(skb, PAR_CompletionStatus, status, 1)) {
440 printk(KERN_ERR "TurboPAM(parse_ADestroyNCOCnf): "
441 "CompletionStatus not found\n");
442 return -1;
443 }
444
445 if (*status) {
446 dprintk("TurboPAM(parse_ADestroyNCOCnf): status=%d\n", *status);
447 return 0;
448 }
449
450 /* extract the ncoid */
451 if (extract_NCOParameter(skb, PAR_NCOID, ncoid, 4)) {
452 printk(KERN_ERR "TurboPAM(parse_ADestroyNCOCnf): "
453 "NCOID not found\n");
454 return -1;
455 }
456
457 dprintk("TurboPAM(parse_ADestroyNCOCnf): ncoid=%lu, status=%d\n",
458 (unsigned long)*ncoid, *status);
459 return 0;
460 }
461
462 /*
463 * Parse a CConnectCnf message.
464 *
465 * skb: the sk_buff containing the message
466 * ncoid: to be filled with the ncoid field value
467 *
468 * Return: 0 if OK, <0 if error.
469 */
470 int parse_CConnectCnf(struct sk_buff *skb, u32 *ncoid) {
471
472 /* extract the ncoid */
473 if (extract_NCOParameter(skb, PAR_NCOID, ncoid, 4)) {
474 printk(KERN_ERR "TurboPAM(parse_CConnectCnf): "
475 "NCOID not found\n");
476 return -1;
477 }
478 dprintk("TurboPAM(parse_CConnectCnf): ncoid=%lu\n",
479 (unsigned long)*ncoid);
480 return 0;
481 }
482
483 /*
484 * Parse a CConnectInd message.
485 *
486 * skb: the sk_buff containing the message
487 * ncoid: to be filled with the ncoid field value
488 * hdlc: to be filled with 1 if the incoming connection is a HDLC one,
489 * with 0 if the incoming connection is a modem one
490 * calling: to be filled with the calling phone number value
491 * called: to be filled with the called phone number value
492 * plan: to be filled with the plan value
493 * screen: to be filled with the screen value
494 *
495 * Return: 0 if OK, <0 if error.
496 */
497 int parse_CConnectInd(struct sk_buff *skb, u32 *ncoid, u8 *hdlc,
498 u8 *calling, u8 *called, u8 *plan, u8 *screen) {
499 u8 phone[PHONE_MAXIMUMSIZE + 4];
500
501 /* extract the ncoid */
502 if (extract_NCOParameter(skb, PAR_NCOID, ncoid, 4)) {
503 printk(KERN_ERR "TurboPAM(parse_CConnectInd): "
504 "NCOID not found\n");
505 return -1;
506 }
507
508 /* extract the bearer capability field */
509 if (extract_NCOParameter(skb, PAR_BearerCap, hdlc, 1)) {
510 printk(KERN_ERR "TurboPAM(parse_CConnectInd): "
511 "BearerCap not found\n");
512 return -1;
513 }
514 *hdlc = (*hdlc == 0x88) ? 1 : 0;
515
516 /* extract the calling number / plan / screen */
517 if (extract_NCOParameter(skb, PAR_CallingNumber, phone,
518 PHONE_MAXIMUMSIZE + 4)) {
519 printk(KERN_ERR "TurboPAM(parse_CConnectInd): "
520 "CallingNumber not found\n");
521 return -1;
522 }
523 memcpy(calling, phone + 4, PHONE_MAXIMUMSIZE);
524 *plan = phone[1];
525 *screen = phone[3];
526
527 /* extract the called number */
528 if (extract_NCOParameter(skb, PAR_CalledNumber, phone,
529 PHONE_MAXIMUMSIZE + 2)) {
530 printk(KERN_ERR "TurboPAM(parse_CConnectInd): "
531 "CalledNumber not found\n");
532 return -1;
533 }
534 memcpy(called, phone + 2, PHONE_MAXIMUMSIZE);
535
536 dprintk("TurboPAM(parse_CConnectInd): "
537 "ncoid=%lu, hdlc=%d, plan=%d, scr=%d, calling=%s, called=%s\n",
538 (unsigned long)*ncoid, *hdlc, *plan, *screen, calling, called);
539 return 0;
540 }
541
542 /*
543 * Parse a CDisconnectCnf message.
544 *
545 * skb: the sk_buff containing the message
546 * ncoid: to be filled with the ncoid field value
547 * causetopuf: to be filled with the cause field value
548 *
549 * Return: 0 if OK, <0 if error.
550 */
551 int parse_CDisconnectCnf(struct sk_buff *skb, u32 *ncoid, u32 *causetopuf) {
552
553 /* extract the ncoid */
554 if (extract_NCOParameter(skb, PAR_NCOID, ncoid, 4)) {
555 printk(KERN_ERR "TurboPAM(parse_CDisconnectCnf): "
556 "NCOID not found\n");
557 return -1;
558 }
559
560 /* extract the cause of disconnection */
561 if (extract_NCOParameter(skb, PAR_CauseToPUF, causetopuf, 4)) {
562 printk(KERN_ERR "TurboPAM(parse_CDisconnectCnf): "
563 "CauseToPUF not found\n");
564 return -1;
565 }
566
567 dprintk("TurboPAM(parse_CDisconnectCnf): ncoid=%lu, causetopuf=%lu\n",
568 (unsigned long)*ncoid, (unsigned long)*causetopuf);
569 return 0;
570 }
571
572 /*
573 * Parse a CDisconnectInd message.
574 *
575 * skb: the sk_buff containing the message
576 * ncoid: to be filled with the ncoid field value
577 * causetopuf: to be filled with the cause field value
578 *
579 * Return: 0 if OK, <0 if error.
580 */
581 int parse_CDisconnectInd(struct sk_buff *skb, u32 *ncoid, u32 *causetopuf) {
582
583 /* extract the ncoid */
584 if (extract_NCOParameter(skb, PAR_NCOID, ncoid, 4)) {
585 printk(KERN_ERR "TurboPAM(parse_CDisconnectInd): "
586 "NCOID not found\n");
587 return -1;
588 }
589
590 /* extract the cause of disconnection */
591 if (extract_NCOParameter(skb, PAR_CauseToPUF, causetopuf, 4)) {
592 printk(KERN_ERR "TurboPAM(parse_CDisconnectInd): "
593 "CauseToPUF not found\n");
594 return -1;
595 }
596
597 dprintk("TurboPAM(parse_CDisconnectInd): ncoid=%lu, causetopuf=%lu\n",
598 (unsigned long)*ncoid, (unsigned long)*causetopuf);
599 return 0;
600 }
601
602 /*
603 * Parse a U3ReadyToReceiveInd message.
604 *
605 * skb: the sk_buff containing the message
606 * ncoid: to be filled with the ncoid field value
607 * ready: to be filled with the ready field value
608 *
609 * Return: 0 if OK, <0 if error.
610 */
611 int parse_U3ReadyToReceiveInd(struct sk_buff *skb, u32 *ncoid, u8 *ready) {
612
613 /* extract the ncoid */
614 if (extract_NCOParameter(skb, PAR_NCOID, ncoid, 4)) {
615 printk(KERN_ERR "TurboPAM(parse_U3ReadyToReceiveInd): "
616 "NCOID not found\n");
617 return -1;
618 }
619
620 /* extract the ready flag */
621 if (extract_NCOParameter(skb, PAR_ReadyFlag, ready, 1)) {
622 printk(KERN_ERR "TurboPAM(parse_U3ReadyToReceiveInd): "
623 "ReadyFlag not found\n");
624 return -1;
625 }
626
627 dprintk("TurboPAM(parse_U3ReadyToReceiveInd): ncoid=%lu, ready=%d\n",
628 (unsigned long)*ncoid, *ready);
629 return 0;
630 }
631
632 /*
633 * Parse a U3DataInd message.
634 *
635 * skb: the sk_buff containing the message + data
636 * ncoid: to be filled with the ncoid field value
637 * data: to be filled with the data
638 * ready: to be filled with the data length
639 *
640 * Return: 0 if OK, <0 if error.
641 */
642 int parse_U3DataInd(struct sk_buff *skb, u32 *ncoid, u8 **data, u16 *len) {
643 pci_mpb *p;
644
645 /* extract the ncoid */
646 if (extract_NCOParameter(skb, PAR_NCOID, ncoid, 4) == -1) {
647 printk(KERN_ERR "TurboPAM(parse_U3DataInd): NCOID not found\n");
648 return -1;
649 }
650
651 /* get a pointer to the beginning of the data block and its length */
652 p = (pci_mpb *)(skb->data + sizeof(skb_header));
653 *len = p->actualDataSize;
654 skb_pull(skb,
655 sizeof(skb_header) + sizeof(pci_mpb) + p->actualBlockTLVSize);
656 *data = skb->data;
657
658 dprintk("TurboPAM(parse_U3DataInd): ncoid=%lu, datalen=%d\n",
659 (unsigned long)*ncoid, *len);
660 return 0;
661 }
662
663