1 /* $Id: tpam_nco.c,v 1.1.2.2 2001/09/23 22:25:03 kai Exp $
3 * Turbo PAM ISDN driver for Linux.
4 * (Kernel Driver - Low Level NCO Manipulation)
6 * Copyright 2001 Stelian Pop <stelian.pop@fr.alcove.com>, AlcĂ´ve
8 * This software may be used and distributed according to the terms
9 * of the GNU General Public License, incorporated herein by reference.
11 * For all support questions please contact: <support@auvertech.fr>
15 #include <linux/pci.h>
16 #include <linux/sched.h>
17 #include <linux/interrupt.h>
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);
27 * Build a NCO packet (PCI message).
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
35 * Return: the sk_buff filled with the NCO packet, or NULL if error.
37 static struct sk_buff *build_NCOpacket(u16 messageID, u16 size,
38 u16 data_size, u16 ack,
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;
50 /* allocate the sk_buff */
51 if (!(skb = alloc_skb(finalsize, GFP_ATOMIC))) {
52 printk(KERN_ERR "TurboPAM(make_NCOpacket): alloc_skb failed\n");
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;
61 h->ack_size = ack_size;
63 /* construct the pci_mpb */
64 p = (pci_mpb *)skb_put(skb, sizeof(pci_mpb));
68 p->messageID = messageID;
69 p->maximumBlockTLVSize = MPB_MAXIMUMBLOCKTLVSIZE;
70 p->actualBlockTLVSize = size;
71 p->maximumDataSize = MPB_MAXIMUMDATASIZE;
72 p->actualDataSize = data_size;
77 * Build a ACreateNCOReq message.
79 * phone: the local phone number.
81 * Return: the sk_buff filled with the NCO packet, or NULL if error.
83 struct sk_buff *build_ACreateNCOReq(const u8 *phone) {
87 pr_debug("TurboPAM(build_ACreateNCOReq): phone=%s\n", phone);
89 /* build the NCO packet */
90 if (!(skb = build_NCOpacket(ID_ACreateNCOReq, 23 + strlen(phone), 0, 0, 0)))
93 /* add the parameters */
94 tlv = (u8 *)skb_put(skb, 3);
97 *(tlv+2) = 5; /* mistery value... */
99 tlv = (u8 *)skb_put(skb, 4);
100 *tlv = PAR_U3Protocol;
102 *(tlv+2) = 4; /* no level 3 protocol */
103 *(tlv+3) = 1; /* HDLC in level 2 */
105 tlv = (u8 *)skb_put(skb, 3);
106 *tlv = PAR_Cdirection;
108 *(tlv+2) = 3; /* PCI_DIRECTION_BOTH */
110 tlv = (u8 *)skb_put(skb, 3);
111 *tlv = PAR_Udirection;
113 *(tlv+2) = 3; /* PCI_DIRECTION_BOTH */
115 tlv = (u8 *)skb_put(skb, 4);
116 *tlv = PAR_BearerCap;
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 */
128 memcpy(tlv + 6, phone, strlen(phone));
134 * Build a ADestroyNCOReq message.
138 * Return: the sk_buff filled with the NCO packet, or NULL if error.
140 struct sk_buff *build_ADestroyNCOReq(u32 ncoid) {
144 pr_debug("TurboPAM(build_ADestroyNCOReq): ncoid=%lu\n",
145 (unsigned long)ncoid);
147 /* build the NCO packet */
148 if (!(skb = build_NCOpacket(ID_ADestroyNCOReq, 6, 0, 0, 0)))
151 /* add the parameters */
152 tlv = (u8 *)skb_put(skb, 6);
155 *((u32 *)(tlv+2)) = ncoid;
161 * Build a CConnectReq message.
164 * called: the destination phone number
165 * hdlc: type of connection: 1 (HDLC) or 0(modem)
167 * Return: the sk_buff filled with the NCO packet, or NULL if error.
169 struct sk_buff *build_CConnectReq(u32 ncoid, const u8 *called, u8 hdlc) {
173 pr_debug("TurboPAM(build_CConnectReq): ncoid=%lu, called=%s, hdlc=%d\n",
174 (unsigned long)ncoid, called, hdlc);
176 /* build the NCO packet */
177 if (!(skb = build_NCOpacket(ID_CConnectReq, 20 + strlen(called), 0, 0, 0)))
180 /* add the parameters */
181 tlv = (u8 *)skb_put(skb, 6);
184 *((u32 *)(tlv+2)) = ncoid;
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));
193 tlv = (u8 *)skb_put(skb, 3);
194 *tlv = PAR_BearerCap;
196 *(tlv+2) = hdlc ? 0x88 /* HDLC */ : 0x80 /* MODEM */;
198 tlv = (u8 *)skb_put(skb, 4);
204 tlv = (u8 *)skb_put(skb, 3);
213 * Build a CConnectRsp message.
217 * Return: the sk_buff filled with the NCO packet, or NULL if error.
219 struct sk_buff *build_CConnectRsp(u32 ncoid) {
223 pr_debug("TurboPAM(build_CConnectRsp): ncoid=%lu\n",
224 (unsigned long)ncoid);
226 /* build the NCO packet */
227 if (!(skb = build_NCOpacket(ID_CConnectRsp, 6, 0, 0, 0)))
230 /* add the parameters */
231 tlv = (u8 *)skb_put(skb, 6);
234 *((u32 *)(tlv+2)) = ncoid;
240 * Build a CDisconnectReq message.
244 * Return: the sk_buff filled with the NCO packet, or NULL if error.
246 struct sk_buff *build_CDisconnectReq(u32 ncoid) {
250 pr_debug("TurboPAM(build_CDisconnectReq): ncoid=%lu\n",
251 (unsigned long)ncoid);
253 /* build the NCO packet */
254 if (!(skb = build_NCOpacket(ID_CDisconnectReq, 6, 0, 0, 0)))
257 /* add the parameters */
258 tlv = (u8 *)skb_put(skb, 6);
261 *((u32 *)(tlv+2)) = ncoid;
267 * Build a CDisconnectRsp message.
271 * Return: the sk_buff filled with the NCO packet, or NULL if error.
273 struct sk_buff *build_CDisconnectRsp(u32 ncoid) {
277 pr_debug("TurboPAM(build_CDisconnectRsp): ncoid=%lu\n",
278 (unsigned long)ncoid);
280 /* build the NCO packet */
281 if (!(skb = build_NCOpacket(ID_CDisconnectRsp, 6, 0, 0, 0)))
284 /* add the parameters */
285 tlv = (u8 *)skb_put(skb, 6);
288 *((u32 *)(tlv+2)) = ncoid;
294 * Build a U3DataReq message.
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
302 * Return: the sk_buff filled with the NCO packet, or NULL if error.
304 struct sk_buff *build_U3DataReq(u32 ncoid, void *data, u16 len,
305 u16 ack, u16 ack_size) {
310 pr_debug("TurboPAM(build_U3DataReq): "
311 "ncoid=%lu, len=%d, ack=%d, ack_size=%d\n",
312 (unsigned long)ncoid, len, ack, ack_size);
314 /* build the NCO packet */
315 if (!(skb = build_NCOpacket(ID_U3DataReq, 6, len, ack, ack_size)))
318 /* add the parameters */
319 tlv = (u8 *)skb_put(skb, 6);
322 *((u32 *)(tlv+2)) = ncoid;
324 p = skb_put(skb, len);
325 memcpy(p, data, len);
331 * Extract a parameter from a TLV block.
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
338 * Return: 0 if OK, <0 if error.
340 static int extract_NCOParameter(struct sk_buff *skb, u8 type,
341 void *value, u16 len) {
342 void *buffer = (void *)skb->data;
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;
354 /* walk through the parameters */
355 while (buffer < bufferend) {
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++);
365 vallen |= *((u8 *)buffer++);
367 /* got the right parameter */
368 if (valtype == type) {
369 /* not enough space for returning the value */
373 memcpy(value, buffer, vallen);
382 * Parse a ACreateNCOCnf message.
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
388 * Return: 0 if OK, <0 if error.
390 int parse_ACreateNCOCnf(struct sk_buff *skb, u8 *status, u32 *ncoid) {
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");
400 pr_debug("TurboPAM(parse_ACreateNCOCnf): status=%d\n", *status);
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");
411 pr_debug("TurboPAM(parse_ACreateNCOCnf): ncoid=%lu, status=%d\n",
412 (unsigned long)*ncoid, *status);
417 * Parse a ADestroyNCOCnf message. Not used in the driver.
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
423 * Return: 0 if OK, <0 if error.
425 int parse_ADestroyNCOCnf(struct sk_buff *skb, u8 *status, u32 *ncoid) {
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");
435 pr_debug("TurboPAM(parse_ADestroyNCOCnf): status=%d\n", *status);
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");
446 pr_debug("TurboPAM(parse_ADestroyNCOCnf): ncoid=%lu, status=%d\n",
447 (unsigned long)*ncoid, *status);
452 * Parse a CConnectCnf message.
454 * skb: the sk_buff containing the message
455 * ncoid: to be filled with the ncoid field value
457 * Return: 0 if OK, <0 if error.
459 int parse_CConnectCnf(struct sk_buff *skb, u32 *ncoid) {
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");
467 pr_debug("TurboPAM(parse_CConnectCnf): ncoid=%lu\n",
468 (unsigned long)*ncoid);
473 * Parse a CConnectInd message.
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
484 * Return: 0 if OK, <0 if error.
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];
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");
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");
503 *hdlc = (*hdlc == 0x88) ? 1 : 0;
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");
512 memcpy(calling, phone + 4, PHONE_MAXIMUMSIZE);
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");
523 memcpy(called, phone + 2, PHONE_MAXIMUMSIZE);
525 pr_debug("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);
532 * Parse a CDisconnectCnf message.
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
538 * Return: 0 if OK, <0 if error.
540 int parse_CDisconnectCnf(struct sk_buff *skb, u32 *ncoid, u32 *causetopuf) {
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");
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");
556 pr_debug("TurboPAM(parse_CDisconnectCnf): ncoid=%lu, causetopuf=%lu\n",
557 (unsigned long)*ncoid, (unsigned long)*causetopuf);
562 * Parse a CDisconnectInd message.
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
568 * Return: 0 if OK, <0 if error.
570 int parse_CDisconnectInd(struct sk_buff *skb, u32 *ncoid, u32 *causetopuf) {
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");
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");
586 pr_debug("TurboPAM(parse_CDisconnectInd): ncoid=%lu, causetopuf=%lu\n",
587 (unsigned long)*ncoid, (unsigned long)*causetopuf);
592 * Parse a U3ReadyToReceiveInd message.
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
598 * Return: 0 if OK, <0 if error.
600 int parse_U3ReadyToReceiveInd(struct sk_buff *skb, u32 *ncoid, u8 *ready) {
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");
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");
616 pr_debug("TurboPAM(parse_U3ReadyToReceiveInd): ncoid=%lu, ready=%d\n",
617 (unsigned long)*ncoid, *ready);
622 * Parse a U3DataInd message.
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
629 * Return: 0 if OK, <0 if error.
631 int parse_U3DataInd(struct sk_buff *skb, u32 *ncoid, u8 **data, u16 *len) {
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");
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;
644 sizeof(skb_header) + sizeof(pci_mpb) + p->actualBlockTLVSize);
647 pr_debug("TurboPAM(parse_U3DataInd): ncoid=%lu, datalen=%d\n",
648 (unsigned long)*ncoid, *len);