1/* $Id: tpam_nco.c,v 1.1.1.1 2008/10/15 03:26:34 james26_jang 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 */
24static struct sk_buff *build_NCOpacket(u16, u16, u16, u16, u16);
25static 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 */
38static 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 */
84struct 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 */
141struct 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 */
170struct 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 */
220struct 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 */
247struct 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 */
274struct 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 */
305struct 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 */
341static 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 */
391int 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 */
426int 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 */
460int 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 */
487int 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 */
541int 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 */
571int 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 */
601int 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 */
632int 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