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