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