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