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