| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /* $Id: capidrv.c,v 1.1.2.2 2004/01/12 23:17:24 keil Exp $ | 
 | 2 |  * | 
 | 3 |  * ISDN4Linux Driver, using capi20 interface (kernelcapi) | 
 | 4 |  * | 
 | 5 |  * Copyright 1997 by Carsten Paeth <calle@calle.de> | 
 | 6 |  * | 
 | 7 |  * This software may be used and distributed according to the terms | 
 | 8 |  * of the GNU General Public License, incorporated herein by reference. | 
 | 9 |  * | 
 | 10 |  */ | 
 | 11 |  | 
 | 12 | #include <linux/module.h> | 
 | 13 | #include <linux/errno.h> | 
 | 14 | #include <linux/kernel.h> | 
 | 15 | #include <linux/major.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 16 | #include <linux/slab.h> | 
 | 17 | #include <linux/fcntl.h> | 
 | 18 | #include <linux/fs.h> | 
 | 19 | #include <linux/signal.h> | 
 | 20 | #include <linux/mm.h> | 
 | 21 | #include <linux/timer.h> | 
 | 22 | #include <linux/wait.h> | 
 | 23 | #include <linux/skbuff.h> | 
 | 24 | #include <linux/isdn.h> | 
 | 25 | #include <linux/isdnif.h> | 
 | 26 | #include <linux/proc_fs.h> | 
 | 27 | #include <linux/capi.h> | 
 | 28 | #include <linux/kernelcapi.h> | 
 | 29 | #include <linux/ctype.h> | 
 | 30 | #include <linux/init.h> | 
 | 31 | #include <linux/moduleparam.h> | 
 | 32 |  | 
 | 33 | #include <linux/isdn/capiutil.h> | 
 | 34 | #include <linux/isdn/capicmd.h> | 
 | 35 | #include "capidrv.h" | 
 | 36 |  | 
 | 37 | static char *revision = "$Revision: 1.1.2.2 $"; | 
 | 38 | static int debugmode = 0; | 
 | 39 |  | 
 | 40 | MODULE_DESCRIPTION("CAPI4Linux: Interface to ISDN4Linux"); | 
 | 41 | MODULE_AUTHOR("Carsten Paeth"); | 
 | 42 | MODULE_LICENSE("GPL"); | 
 | 43 | module_param(debugmode, uint, 0); | 
 | 44 |  | 
 | 45 | /* -------- type definitions ----------------------------------------- */ | 
 | 46 |  | 
 | 47 |  | 
 | 48 | struct capidrv_contr { | 
 | 49 |  | 
 | 50 | 	struct capidrv_contr *next; | 
 | 51 | 	struct module *owner; | 
 | 52 | 	u32 contrnr; | 
 | 53 | 	char name[20]; | 
 | 54 |  | 
 | 55 | 	/* | 
 | 56 | 	 * for isdn4linux | 
 | 57 | 	 */ | 
 | 58 | 	isdn_if interface; | 
 | 59 | 	int myid; | 
 | 60 |  | 
 | 61 | 	/* | 
 | 62 | 	 * LISTEN state | 
 | 63 | 	 */ | 
 | 64 | 	int state; | 
 | 65 | 	u32 cipmask; | 
 | 66 | 	u32 cipmask2; | 
 | 67 |         struct timer_list listentimer; | 
 | 68 |  | 
 | 69 | 	/* | 
 | 70 | 	 * ID of capi message sent | 
 | 71 | 	 */ | 
 | 72 | 	u16 msgid; | 
 | 73 |  | 
 | 74 | 	/* | 
 | 75 | 	 * B-Channels | 
 | 76 | 	 */ | 
 | 77 | 	int nbchan; | 
 | 78 | 	struct capidrv_bchan { | 
 | 79 | 		struct capidrv_contr *contr; | 
 | 80 | 		u8 msn[ISDN_MSNLEN]; | 
 | 81 | 		int l2; | 
 | 82 | 		int l3; | 
 | 83 | 		u8 num[ISDN_MSNLEN]; | 
 | 84 | 		u8 mynum[ISDN_MSNLEN]; | 
 | 85 | 		int si1; | 
 | 86 | 		int si2; | 
 | 87 | 		int incoming; | 
 | 88 | 		int disconnecting; | 
 | 89 | 		struct capidrv_plci { | 
 | 90 | 			struct capidrv_plci *next; | 
 | 91 | 			u32 plci; | 
 | 92 | 			u32 ncci;	/* ncci for CONNECT_ACTIVE_IND */ | 
 | 93 | 			u16 msgid;	/* to identfy CONNECT_CONF */ | 
 | 94 | 			int chan; | 
 | 95 | 			int state; | 
 | 96 | 			int leasedline; | 
 | 97 | 			struct capidrv_ncci { | 
 | 98 | 				struct capidrv_ncci *next; | 
 | 99 | 				struct capidrv_plci *plcip; | 
 | 100 | 				u32 ncci; | 
 | 101 | 				u16 msgid;	/* to identfy CONNECT_B3_CONF */ | 
 | 102 | 				int chan; | 
 | 103 | 				int state; | 
 | 104 | 				int oldstate; | 
 | 105 | 				/* */ | 
 | 106 | 				u16 datahandle; | 
 | 107 | 				struct ncci_datahandle_queue { | 
 | 108 | 				    struct ncci_datahandle_queue *next; | 
 | 109 | 				    u16                         datahandle; | 
 | 110 | 				    int                           len; | 
 | 111 | 				} *ackqueue; | 
 | 112 | 			} *ncci_list; | 
 | 113 | 		} *plcip; | 
 | 114 | 		struct capidrv_ncci *nccip; | 
 | 115 | 	} *bchans; | 
 | 116 |  | 
 | 117 | 	struct capidrv_plci *plci_list; | 
 | 118 |  | 
 | 119 | 	/* for q931 data */ | 
 | 120 | 	u8  q931_buf[4096]; | 
 | 121 | 	u8 *q931_read; | 
 | 122 | 	u8 *q931_write; | 
 | 123 | 	u8 *q931_end; | 
 | 124 | }; | 
 | 125 |  | 
 | 126 |  | 
 | 127 | struct capidrv_data { | 
 | 128 | 	struct capi20_appl ap; | 
 | 129 | 	int ncontr; | 
 | 130 | 	struct capidrv_contr *contr_list; | 
 | 131 | }; | 
 | 132 |  | 
 | 133 | typedef struct capidrv_plci capidrv_plci; | 
 | 134 | typedef struct capidrv_ncci capidrv_ncci; | 
 | 135 | typedef struct capidrv_contr capidrv_contr; | 
 | 136 | typedef struct capidrv_data capidrv_data; | 
 | 137 | typedef struct capidrv_bchan capidrv_bchan; | 
 | 138 |  | 
 | 139 | /* -------- data definitions ----------------------------------------- */ | 
 | 140 |  | 
 | 141 | static capidrv_data global; | 
 | 142 | static DEFINE_SPINLOCK(global_lock); | 
 | 143 |  | 
 | 144 | static void handle_dtrace_data(capidrv_contr *card, | 
 | 145 | 	int send, int level2, u8 *data, u16 len); | 
 | 146 |  | 
 | 147 | /* -------- convert functions ---------------------------------------- */ | 
 | 148 |  | 
 | 149 | static inline u32 b1prot(int l2, int l3) | 
 | 150 | { | 
 | 151 | 	switch (l2) { | 
 | 152 | 	case ISDN_PROTO_L2_X75I: | 
 | 153 | 	case ISDN_PROTO_L2_X75UI: | 
 | 154 | 	case ISDN_PROTO_L2_X75BUI: | 
 | 155 | 		return 0; | 
 | 156 | 	case ISDN_PROTO_L2_HDLC: | 
 | 157 | 	default: | 
 | 158 | 		return 0; | 
 | 159 | 	case ISDN_PROTO_L2_TRANS: | 
 | 160 | 		return 1; | 
 | 161 |         case ISDN_PROTO_L2_V11096: | 
 | 162 |         case ISDN_PROTO_L2_V11019: | 
 | 163 |         case ISDN_PROTO_L2_V11038: | 
 | 164 | 		return 2; | 
 | 165 |         case ISDN_PROTO_L2_FAX: | 
 | 166 | 		return 4; | 
 | 167 | 	case ISDN_PROTO_L2_MODEM: | 
 | 168 | 		return 8; | 
 | 169 | 	} | 
 | 170 | } | 
 | 171 |  | 
 | 172 | static inline u32 b2prot(int l2, int l3) | 
 | 173 | { | 
 | 174 | 	switch (l2) { | 
 | 175 | 	case ISDN_PROTO_L2_X75I: | 
 | 176 | 	case ISDN_PROTO_L2_X75UI: | 
 | 177 | 	case ISDN_PROTO_L2_X75BUI: | 
 | 178 | 	default: | 
 | 179 | 		return 0; | 
 | 180 | 	case ISDN_PROTO_L2_HDLC: | 
 | 181 | 	case ISDN_PROTO_L2_TRANS: | 
 | 182 |         case ISDN_PROTO_L2_V11096: | 
 | 183 |         case ISDN_PROTO_L2_V11019: | 
 | 184 |         case ISDN_PROTO_L2_V11038: | 
 | 185 | 	case ISDN_PROTO_L2_MODEM: | 
 | 186 | 		return 1; | 
 | 187 |         case ISDN_PROTO_L2_FAX: | 
 | 188 | 		return 4; | 
 | 189 | 	} | 
 | 190 | } | 
 | 191 |  | 
 | 192 | static inline u32 b3prot(int l2, int l3) | 
 | 193 | { | 
 | 194 | 	switch (l2) { | 
 | 195 | 	case ISDN_PROTO_L2_X75I: | 
 | 196 | 	case ISDN_PROTO_L2_X75UI: | 
 | 197 | 	case ISDN_PROTO_L2_X75BUI: | 
 | 198 | 	case ISDN_PROTO_L2_HDLC: | 
 | 199 | 	case ISDN_PROTO_L2_TRANS: | 
 | 200 |         case ISDN_PROTO_L2_V11096: | 
 | 201 |         case ISDN_PROTO_L2_V11019: | 
 | 202 |         case ISDN_PROTO_L2_V11038: | 
 | 203 | 	case ISDN_PROTO_L2_MODEM: | 
 | 204 | 	default: | 
 | 205 | 		return 0; | 
 | 206 |         case ISDN_PROTO_L2_FAX: | 
 | 207 | 		return 4; | 
 | 208 | 	} | 
 | 209 | } | 
 | 210 |  | 
 | 211 | static _cstruct b1config_async_v110(u16 rate) | 
 | 212 | { | 
 | 213 | 	/* CAPI-Spec "B1 Configuration" */ | 
 | 214 | 	static unsigned char buf[9]; | 
 | 215 | 	buf[0] = 8; /* len */ | 
 | 216 | 	/* maximum bitrate */ | 
 | 217 | 	buf[1] = rate & 0xff; buf[2] = (rate >> 8) & 0xff; | 
 | 218 | 	buf[3] = 8; buf[4] = 0; /* 8 bits per character */ | 
 | 219 | 	buf[5] = 0; buf[6] = 0; /* parity none */ | 
 | 220 | 	buf[7] = 0; buf[8] = 0; /* 1 stop bit */ | 
 | 221 | 	return buf; | 
 | 222 | } | 
 | 223 |  | 
 | 224 | static _cstruct b1config(int l2, int l3) | 
 | 225 | { | 
 | 226 | 	switch (l2) { | 
 | 227 | 	case ISDN_PROTO_L2_X75I: | 
 | 228 | 	case ISDN_PROTO_L2_X75UI: | 
 | 229 | 	case ISDN_PROTO_L2_X75BUI: | 
 | 230 | 	case ISDN_PROTO_L2_HDLC: | 
 | 231 | 	case ISDN_PROTO_L2_TRANS: | 
 | 232 | 	default: | 
 | 233 | 		return NULL; | 
 | 234 |         case ISDN_PROTO_L2_V11096: | 
 | 235 | 	    return b1config_async_v110(9600); | 
 | 236 |         case ISDN_PROTO_L2_V11019: | 
 | 237 | 	    return b1config_async_v110(19200); | 
 | 238 |         case ISDN_PROTO_L2_V11038: | 
 | 239 | 	    return b1config_async_v110(38400); | 
 | 240 | 	} | 
 | 241 | } | 
 | 242 |  | 
 | 243 | static inline u16 si2cip(u8 si1, u8 si2) | 
 | 244 | { | 
 | 245 | 	static const u8 cip[17][5] = | 
 | 246 | 	{ | 
 | 247 | 	/*  0  1  2  3  4  */ | 
 | 248 | 		{0, 0, 0, 0, 0},	/*0 */ | 
 | 249 | 		{16, 16, 4, 26, 16},	/*1 */ | 
 | 250 | 		{17, 17, 17, 4, 4},	/*2 */ | 
 | 251 | 		{2, 2, 2, 2, 2},	/*3 */ | 
 | 252 | 		{18, 18, 18, 18, 18},	/*4 */ | 
 | 253 | 		{2, 2, 2, 2, 2},	/*5 */ | 
 | 254 | 		{0, 0, 0, 0, 0},	/*6 */ | 
 | 255 | 		{2, 2, 2, 2, 2},	/*7 */ | 
 | 256 | 		{2, 2, 2, 2, 2},	/*8 */ | 
 | 257 | 		{21, 21, 21, 21, 21},	/*9 */ | 
 | 258 | 		{19, 19, 19, 19, 19},	/*10 */ | 
 | 259 | 		{0, 0, 0, 0, 0},	/*11 */ | 
 | 260 | 		{0, 0, 0, 0, 0},	/*12 */ | 
 | 261 | 		{0, 0, 0, 0, 0},	/*13 */ | 
 | 262 | 		{0, 0, 0, 0, 0},	/*14 */ | 
 | 263 | 		{22, 22, 22, 22, 22},	/*15 */ | 
 | 264 | 		{27, 27, 27, 28, 27}	/*16 */ | 
 | 265 | 	}; | 
 | 266 | 	if (si1 > 16) | 
 | 267 | 		si1 = 0; | 
 | 268 | 	if (si2 > 4) | 
 | 269 | 		si2 = 0; | 
 | 270 |  | 
 | 271 | 	return (u16) cip[si1][si2]; | 
 | 272 | } | 
 | 273 |  | 
 | 274 | static inline u8 cip2si1(u16 cipval) | 
 | 275 | { | 
 | 276 | 	static const u8 si[32] = | 
 | 277 | 	{7, 1, 7, 7, 1, 1, 7, 7,	/*0-7 */ | 
 | 278 | 	 7, 1, 0, 0, 0, 0, 0, 0,	/*8-15 */ | 
 | 279 | 	 1, 2, 4, 10, 9, 9, 15, 7,	/*16-23 */ | 
 | 280 | 	 7, 7, 1, 16, 16, 0, 0, 0};	/*24-31 */ | 
 | 281 |  | 
 | 282 | 	if (cipval > 31) | 
 | 283 | 		cipval = 0;	/* .... */ | 
 | 284 | 	return si[cipval]; | 
 | 285 | } | 
 | 286 |  | 
 | 287 | static inline u8 cip2si2(u16 cipval) | 
 | 288 | { | 
 | 289 | 	static const u8 si[32] = | 
 | 290 | 	{0, 0, 0, 0, 2, 3, 0, 0,	/*0-7 */ | 
 | 291 | 	 0, 3, 0, 0, 0, 0, 0, 0,	/*8-15 */ | 
 | 292 | 	 1, 2, 0, 0, 9, 0, 0, 0,	/*16-23 */ | 
 | 293 | 	 0, 0, 3, 2, 3, 0, 0, 0};	/*24-31 */ | 
 | 294 |  | 
 | 295 | 	if (cipval > 31) | 
 | 296 | 		cipval = 0;	/* .... */ | 
 | 297 | 	return si[cipval]; | 
 | 298 | } | 
 | 299 |  | 
 | 300 |  | 
 | 301 | /* -------- controller management ------------------------------------- */ | 
 | 302 |  | 
 | 303 | static inline capidrv_contr *findcontrbydriverid(int driverid) | 
 | 304 | { | 
 | 305 |     	unsigned long flags; | 
 | 306 | 	capidrv_contr *p; | 
 | 307 |  | 
 | 308 | 	spin_lock_irqsave(&global_lock, flags); | 
 | 309 | 	for (p = global.contr_list; p; p = p->next) | 
 | 310 | 		if (p->myid == driverid) | 
 | 311 | 			break; | 
 | 312 | 	spin_unlock_irqrestore(&global_lock, flags); | 
 | 313 | 	return p; | 
 | 314 | } | 
 | 315 |  | 
 | 316 | static capidrv_contr *findcontrbynumber(u32 contr) | 
 | 317 | { | 
 | 318 | 	unsigned long flags; | 
 | 319 | 	capidrv_contr *p = global.contr_list; | 
 | 320 |  | 
 | 321 | 	spin_lock_irqsave(&global_lock, flags); | 
 | 322 | 	for (p = global.contr_list; p; p = p->next) | 
 | 323 | 		if (p->contrnr == contr) | 
 | 324 | 			break; | 
 | 325 | 	spin_unlock_irqrestore(&global_lock, flags); | 
 | 326 | 	return p; | 
 | 327 | } | 
 | 328 |  | 
 | 329 |  | 
 | 330 | /* -------- plci management ------------------------------------------ */ | 
 | 331 |  | 
 | 332 | static capidrv_plci *new_plci(capidrv_contr * card, int chan) | 
 | 333 | { | 
 | 334 | 	capidrv_plci *plcip; | 
 | 335 |  | 
| Burman Yan | 41f9693 | 2006-12-08 02:39:35 -0800 | [diff] [blame] | 336 | 	plcip = kzalloc(sizeof(capidrv_plci), GFP_ATOMIC); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 337 |  | 
| Harvey Harrison | 2f9e9b6 | 2008-04-28 02:14:37 -0700 | [diff] [blame] | 338 | 	if (plcip == NULL) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 339 | 		return NULL; | 
 | 340 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 341 | 	plcip->state = ST_PLCI_NONE; | 
 | 342 | 	plcip->plci = 0; | 
 | 343 | 	plcip->msgid = 0; | 
 | 344 | 	plcip->chan = chan; | 
 | 345 | 	plcip->next = card->plci_list; | 
 | 346 | 	card->plci_list = plcip; | 
 | 347 | 	card->bchans[chan].plcip = plcip; | 
 | 348 |  | 
 | 349 | 	return plcip; | 
 | 350 | } | 
 | 351 |  | 
 | 352 | static capidrv_plci *find_plci_by_plci(capidrv_contr * card, u32 plci) | 
 | 353 | { | 
 | 354 | 	capidrv_plci *p; | 
 | 355 | 	for (p = card->plci_list; p; p = p->next) | 
 | 356 | 		if (p->plci == plci) | 
 | 357 | 			return p; | 
 | 358 | 	return NULL; | 
 | 359 | } | 
 | 360 |  | 
 | 361 | static capidrv_plci *find_plci_by_msgid(capidrv_contr * card, u16 msgid) | 
 | 362 | { | 
 | 363 | 	capidrv_plci *p; | 
 | 364 | 	for (p = card->plci_list; p; p = p->next) | 
 | 365 | 		if (p->msgid == msgid) | 
 | 366 | 			return p; | 
 | 367 | 	return NULL; | 
 | 368 | } | 
 | 369 |  | 
 | 370 | static capidrv_plci *find_plci_by_ncci(capidrv_contr * card, u32 ncci) | 
 | 371 | { | 
 | 372 | 	capidrv_plci *p; | 
 | 373 | 	for (p = card->plci_list; p; p = p->next) | 
 | 374 | 		if (p->plci == (ncci & 0xffff)) | 
 | 375 | 			return p; | 
 | 376 | 	return NULL; | 
 | 377 | } | 
 | 378 |  | 
 | 379 | static void free_plci(capidrv_contr * card, capidrv_plci * plcip) | 
 | 380 | { | 
 | 381 | 	capidrv_plci **pp; | 
 | 382 |  | 
 | 383 | 	for (pp = &card->plci_list; *pp; pp = &(*pp)->next) { | 
 | 384 | 		if (*pp == plcip) { | 
 | 385 | 			*pp = (*pp)->next; | 
 | 386 | 			card->bchans[plcip->chan].plcip = NULL; | 
 | 387 | 			card->bchans[plcip->chan].disconnecting = 0; | 
 | 388 | 			card->bchans[plcip->chan].incoming = 0; | 
 | 389 | 			kfree(plcip); | 
 | 390 | 			return; | 
 | 391 | 		} | 
 | 392 | 	} | 
 | 393 | 	printk(KERN_ERR "capidrv-%d: free_plci %p (0x%x) not found, Huh?\n", | 
 | 394 | 	       card->contrnr, plcip, plcip->plci); | 
 | 395 | } | 
 | 396 |  | 
 | 397 | /* -------- ncci management ------------------------------------------ */ | 
 | 398 |  | 
 | 399 | static inline capidrv_ncci *new_ncci(capidrv_contr * card, | 
 | 400 | 				     capidrv_plci * plcip, | 
 | 401 | 				     u32 ncci) | 
 | 402 | { | 
 | 403 | 	capidrv_ncci *nccip; | 
 | 404 |  | 
| Burman Yan | 41f9693 | 2006-12-08 02:39:35 -0800 | [diff] [blame] | 405 | 	nccip = kzalloc(sizeof(capidrv_ncci), GFP_ATOMIC); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 406 |  | 
| Harvey Harrison | 2f9e9b6 | 2008-04-28 02:14:37 -0700 | [diff] [blame] | 407 | 	if (nccip == NULL) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 408 | 		return NULL; | 
 | 409 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 410 | 	nccip->ncci = ncci; | 
 | 411 | 	nccip->state = ST_NCCI_NONE; | 
 | 412 | 	nccip->plcip = plcip; | 
 | 413 | 	nccip->chan = plcip->chan; | 
 | 414 | 	nccip->datahandle = 0; | 
 | 415 |  | 
 | 416 | 	nccip->next = plcip->ncci_list; | 
 | 417 | 	plcip->ncci_list = nccip; | 
 | 418 |  | 
 | 419 | 	card->bchans[plcip->chan].nccip = nccip; | 
 | 420 |  | 
 | 421 | 	return nccip; | 
 | 422 | } | 
 | 423 |  | 
 | 424 | static inline capidrv_ncci *find_ncci(capidrv_contr * card, u32 ncci) | 
 | 425 | { | 
 | 426 | 	capidrv_plci *plcip; | 
 | 427 | 	capidrv_ncci *p; | 
 | 428 |  | 
| Harvey Harrison | 2f9e9b6 | 2008-04-28 02:14:37 -0700 | [diff] [blame] | 429 | 	if ((plcip = find_plci_by_ncci(card, ncci)) == NULL) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 430 | 		return NULL; | 
 | 431 |  | 
 | 432 | 	for (p = plcip->ncci_list; p; p = p->next) | 
 | 433 | 		if (p->ncci == ncci) | 
 | 434 | 			return p; | 
 | 435 | 	return NULL; | 
 | 436 | } | 
 | 437 |  | 
 | 438 | static inline capidrv_ncci *find_ncci_by_msgid(capidrv_contr * card, | 
 | 439 | 					       u32 ncci, u16 msgid) | 
 | 440 | { | 
 | 441 | 	capidrv_plci *plcip; | 
 | 442 | 	capidrv_ncci *p; | 
 | 443 |  | 
| Harvey Harrison | 2f9e9b6 | 2008-04-28 02:14:37 -0700 | [diff] [blame] | 444 | 	if ((plcip = find_plci_by_ncci(card, ncci)) == NULL) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 445 | 		return NULL; | 
 | 446 |  | 
 | 447 | 	for (p = plcip->ncci_list; p; p = p->next) | 
 | 448 | 		if (p->msgid == msgid) | 
 | 449 | 			return p; | 
 | 450 | 	return NULL; | 
 | 451 | } | 
 | 452 |  | 
 | 453 | static void free_ncci(capidrv_contr * card, struct capidrv_ncci *nccip) | 
 | 454 | { | 
 | 455 | 	struct capidrv_ncci **pp; | 
 | 456 |  | 
 | 457 | 	for (pp = &(nccip->plcip->ncci_list); *pp; pp = &(*pp)->next) { | 
 | 458 | 		if (*pp == nccip) { | 
 | 459 | 			*pp = (*pp)->next; | 
 | 460 | 			break; | 
 | 461 | 		} | 
 | 462 | 	} | 
 | 463 | 	card->bchans[nccip->chan].nccip = NULL; | 
 | 464 | 	kfree(nccip); | 
 | 465 | } | 
 | 466 |  | 
 | 467 | static int capidrv_add_ack(struct capidrv_ncci *nccip, | 
 | 468 | 		           u16 datahandle, int len) | 
 | 469 | { | 
 | 470 | 	struct ncci_datahandle_queue *n, **pp; | 
 | 471 |  | 
 | 472 | 	n = (struct ncci_datahandle_queue *) | 
 | 473 | 		kmalloc(sizeof(struct ncci_datahandle_queue), GFP_ATOMIC); | 
 | 474 | 	if (!n) { | 
 | 475 | 	   printk(KERN_ERR "capidrv: kmalloc ncci_datahandle failed\n"); | 
 | 476 | 	   return -1; | 
 | 477 | 	} | 
 | 478 | 	n->next = NULL; | 
 | 479 | 	n->datahandle = datahandle; | 
 | 480 | 	n->len = len; | 
 | 481 | 	for (pp = &nccip->ackqueue; *pp; pp = &(*pp)->next) ; | 
 | 482 | 	*pp = n; | 
 | 483 | 	return 0; | 
 | 484 | } | 
 | 485 |  | 
 | 486 | static int capidrv_del_ack(struct capidrv_ncci *nccip, u16 datahandle) | 
 | 487 | { | 
 | 488 | 	struct ncci_datahandle_queue **pp, *p; | 
 | 489 | 	int len; | 
 | 490 |  | 
 | 491 | 	for (pp = &nccip->ackqueue; *pp; pp = &(*pp)->next) { | 
 | 492 |  		if ((*pp)->datahandle == datahandle) { | 
 | 493 | 			p = *pp; | 
 | 494 | 			len = p->len; | 
 | 495 | 			*pp = (*pp)->next; | 
 | 496 | 		        kfree(p); | 
 | 497 | 			return len; | 
 | 498 | 		} | 
 | 499 | 	} | 
 | 500 | 	return -1; | 
 | 501 | } | 
 | 502 |  | 
 | 503 | /* -------- convert and send capi message ---------------------------- */ | 
 | 504 |  | 
 | 505 | static void send_message(capidrv_contr * card, _cmsg * cmsg) | 
 | 506 | { | 
 | 507 | 	struct sk_buff *skb; | 
 | 508 | 	size_t len; | 
| Jesper Juhl | b1b2e7c | 2007-10-16 01:27:51 -0700 | [diff] [blame] | 509 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 510 | 	capi_cmsg2message(cmsg, cmsg->buf); | 
 | 511 | 	len = CAPIMSG_LEN(cmsg->buf); | 
 | 512 | 	skb = alloc_skb(len, GFP_ATOMIC); | 
| Jesper Juhl | b1b2e7c | 2007-10-16 01:27:51 -0700 | [diff] [blame] | 513 | 	if (!skb) { | 
 | 514 | 		printk(KERN_ERR "capidrv::send_message: can't allocate mem\n"); | 
 | 515 | 		return; | 
 | 516 | 	} | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 517 | 	memcpy(skb_put(skb, len), cmsg->buf, len); | 
 | 518 | 	if (capi20_put_message(&global.ap, skb) != CAPI_NOERROR) | 
 | 519 | 		kfree_skb(skb); | 
 | 520 | } | 
 | 521 |  | 
 | 522 | /* -------- state machine -------------------------------------------- */ | 
 | 523 |  | 
 | 524 | struct listenstatechange { | 
 | 525 | 	int actstate; | 
 | 526 | 	int nextstate; | 
 | 527 | 	int event; | 
 | 528 | }; | 
 | 529 |  | 
 | 530 | static struct listenstatechange listentable[] = | 
 | 531 | { | 
 | 532 |   {ST_LISTEN_NONE, ST_LISTEN_WAIT_CONF, EV_LISTEN_REQ}, | 
 | 533 |   {ST_LISTEN_ACTIVE, ST_LISTEN_ACTIVE_WAIT_CONF, EV_LISTEN_REQ}, | 
 | 534 |   {ST_LISTEN_WAIT_CONF, ST_LISTEN_NONE, EV_LISTEN_CONF_ERROR}, | 
 | 535 |   {ST_LISTEN_ACTIVE_WAIT_CONF, ST_LISTEN_ACTIVE, EV_LISTEN_CONF_ERROR}, | 
 | 536 |   {ST_LISTEN_WAIT_CONF, ST_LISTEN_NONE, EV_LISTEN_CONF_EMPTY}, | 
 | 537 |   {ST_LISTEN_ACTIVE_WAIT_CONF, ST_LISTEN_NONE, EV_LISTEN_CONF_EMPTY}, | 
 | 538 |   {ST_LISTEN_WAIT_CONF, ST_LISTEN_ACTIVE, EV_LISTEN_CONF_OK}, | 
 | 539 |   {ST_LISTEN_ACTIVE_WAIT_CONF, ST_LISTEN_ACTIVE, EV_LISTEN_CONF_OK}, | 
 | 540 |   {}, | 
 | 541 | }; | 
 | 542 |  | 
 | 543 | static void listen_change_state(capidrv_contr * card, int event) | 
 | 544 | { | 
 | 545 | 	struct listenstatechange *p = listentable; | 
 | 546 | 	while (p->event) { | 
 | 547 | 		if (card->state == p->actstate && p->event == event) { | 
 | 548 | 			if (debugmode) | 
 | 549 | 				printk(KERN_DEBUG "capidrv-%d: listen_change_state %d -> %d\n", | 
 | 550 | 				       card->contrnr, card->state, p->nextstate); | 
 | 551 | 			card->state = p->nextstate; | 
 | 552 | 			return; | 
 | 553 | 		} | 
 | 554 | 		p++; | 
 | 555 | 	} | 
 | 556 | 	printk(KERN_ERR "capidrv-%d: listen_change_state state=%d event=%d ????\n", | 
 | 557 | 	       card->contrnr, card->state, event); | 
 | 558 |  | 
 | 559 | } | 
 | 560 |  | 
 | 561 | /* ------------------------------------------------------------------ */ | 
 | 562 |  | 
 | 563 | static void p0(capidrv_contr * card, capidrv_plci * plci) | 
 | 564 | { | 
 | 565 | 	isdn_ctrl cmd; | 
 | 566 |  | 
 | 567 | 	card->bchans[plci->chan].contr = NULL; | 
 | 568 | 	cmd.command = ISDN_STAT_DHUP; | 
 | 569 | 	cmd.driver = card->myid; | 
 | 570 | 	cmd.arg = plci->chan; | 
 | 571 | 	card->interface.statcallb(&cmd); | 
 | 572 | 	free_plci(card, plci); | 
 | 573 | } | 
 | 574 |  | 
 | 575 | /* ------------------------------------------------------------------ */ | 
 | 576 |  | 
 | 577 | struct plcistatechange { | 
 | 578 | 	int actstate; | 
 | 579 | 	int nextstate; | 
 | 580 | 	int event; | 
 | 581 | 	void (*changefunc) (capidrv_contr * card, capidrv_plci * plci); | 
 | 582 | }; | 
 | 583 |  | 
 | 584 | static struct plcistatechange plcitable[] = | 
 | 585 | { | 
 | 586 |   /* P-0 */ | 
 | 587 |   {ST_PLCI_NONE, ST_PLCI_OUTGOING, EV_PLCI_CONNECT_REQ, NULL}, | 
 | 588 |   {ST_PLCI_NONE, ST_PLCI_ALLOCATED, EV_PLCI_FACILITY_IND_UP, NULL}, | 
 | 589 |   {ST_PLCI_NONE, ST_PLCI_INCOMING, EV_PLCI_CONNECT_IND, NULL}, | 
 | 590 |   {ST_PLCI_NONE, ST_PLCI_RESUMEING, EV_PLCI_RESUME_REQ, NULL}, | 
 | 591 |   /* P-0.1 */ | 
 | 592 |   {ST_PLCI_OUTGOING, ST_PLCI_NONE, EV_PLCI_CONNECT_CONF_ERROR, p0}, | 
 | 593 |   {ST_PLCI_OUTGOING, ST_PLCI_ALLOCATED, EV_PLCI_CONNECT_CONF_OK, NULL}, | 
 | 594 |   /* P-1 */ | 
 | 595 |   {ST_PLCI_ALLOCATED, ST_PLCI_ACTIVE, EV_PLCI_CONNECT_ACTIVE_IND, NULL}, | 
 | 596 |   {ST_PLCI_ALLOCATED, ST_PLCI_DISCONNECTING, EV_PLCI_DISCONNECT_REQ, NULL}, | 
 | 597 |   {ST_PLCI_ALLOCATED, ST_PLCI_DISCONNECTING, EV_PLCI_FACILITY_IND_DOWN, NULL}, | 
 | 598 |   {ST_PLCI_ALLOCATED, ST_PLCI_DISCONNECTED, EV_PLCI_DISCONNECT_IND, NULL}, | 
 | 599 |   /* P-ACT */ | 
 | 600 |   {ST_PLCI_ACTIVE, ST_PLCI_DISCONNECTING, EV_PLCI_DISCONNECT_REQ, NULL}, | 
 | 601 |   {ST_PLCI_ACTIVE, ST_PLCI_DISCONNECTING, EV_PLCI_FACILITY_IND_DOWN, NULL}, | 
 | 602 |   {ST_PLCI_ACTIVE, ST_PLCI_DISCONNECTED, EV_PLCI_DISCONNECT_IND, NULL}, | 
 | 603 |   {ST_PLCI_ACTIVE, ST_PLCI_HELD, EV_PLCI_HOLD_IND, NULL}, | 
 | 604 |   {ST_PLCI_ACTIVE, ST_PLCI_DISCONNECTING, EV_PLCI_SUSPEND_IND, NULL}, | 
 | 605 |   /* P-2 */ | 
 | 606 |   {ST_PLCI_INCOMING, ST_PLCI_DISCONNECTING, EV_PLCI_CONNECT_REJECT, NULL}, | 
 | 607 |   {ST_PLCI_INCOMING, ST_PLCI_FACILITY_IND, EV_PLCI_FACILITY_IND_UP, NULL}, | 
 | 608 |   {ST_PLCI_INCOMING, ST_PLCI_ACCEPTING, EV_PLCI_CONNECT_RESP, NULL}, | 
 | 609 |   {ST_PLCI_INCOMING, ST_PLCI_DISCONNECTING, EV_PLCI_DISCONNECT_REQ, NULL}, | 
 | 610 |   {ST_PLCI_INCOMING, ST_PLCI_DISCONNECTING, EV_PLCI_FACILITY_IND_DOWN, NULL}, | 
 | 611 |   {ST_PLCI_INCOMING, ST_PLCI_DISCONNECTED, EV_PLCI_DISCONNECT_IND, NULL}, | 
 | 612 |   {ST_PLCI_INCOMING, ST_PLCI_DISCONNECTING, EV_PLCI_CD_IND, NULL}, | 
 | 613 |   /* P-3 */ | 
 | 614 |   {ST_PLCI_FACILITY_IND, ST_PLCI_DISCONNECTING, EV_PLCI_CONNECT_REJECT, NULL}, | 
 | 615 |   {ST_PLCI_FACILITY_IND, ST_PLCI_ACCEPTING, EV_PLCI_CONNECT_ACTIVE_IND, NULL}, | 
 | 616 |   {ST_PLCI_FACILITY_IND, ST_PLCI_DISCONNECTING, EV_PLCI_DISCONNECT_REQ, NULL}, | 
 | 617 |   {ST_PLCI_FACILITY_IND, ST_PLCI_DISCONNECTING, EV_PLCI_FACILITY_IND_DOWN, NULL}, | 
 | 618 |   {ST_PLCI_FACILITY_IND, ST_PLCI_DISCONNECTED, EV_PLCI_DISCONNECT_IND, NULL}, | 
 | 619 |   /* P-4 */ | 
 | 620 |   {ST_PLCI_ACCEPTING, ST_PLCI_ACTIVE, EV_PLCI_CONNECT_ACTIVE_IND, NULL}, | 
 | 621 |   {ST_PLCI_ACCEPTING, ST_PLCI_DISCONNECTING, EV_PLCI_DISCONNECT_REQ, NULL}, | 
 | 622 |   {ST_PLCI_ACCEPTING, ST_PLCI_DISCONNECTING, EV_PLCI_FACILITY_IND_DOWN, NULL}, | 
 | 623 |   {ST_PLCI_ACCEPTING, ST_PLCI_DISCONNECTED, EV_PLCI_DISCONNECT_IND, NULL}, | 
 | 624 |   /* P-5 */ | 
 | 625 |   {ST_PLCI_DISCONNECTING, ST_PLCI_DISCONNECTED, EV_PLCI_DISCONNECT_IND, NULL}, | 
 | 626 |   /* P-6 */ | 
 | 627 |   {ST_PLCI_DISCONNECTED, ST_PLCI_NONE, EV_PLCI_DISCONNECT_RESP, p0}, | 
 | 628 |   /* P-0.Res */ | 
 | 629 |   {ST_PLCI_RESUMEING, ST_PLCI_NONE, EV_PLCI_RESUME_CONF_ERROR, p0}, | 
 | 630 |   {ST_PLCI_RESUMEING, ST_PLCI_RESUME, EV_PLCI_RESUME_CONF_OK, NULL}, | 
 | 631 |   /* P-RES */ | 
 | 632 |   {ST_PLCI_RESUME, ST_PLCI_ACTIVE, EV_PLCI_RESUME_IND, NULL}, | 
 | 633 |   /* P-HELD */ | 
 | 634 |   {ST_PLCI_HELD, ST_PLCI_ACTIVE, EV_PLCI_RETRIEVE_IND, NULL}, | 
 | 635 |   {}, | 
 | 636 | }; | 
 | 637 |  | 
 | 638 | static void plci_change_state(capidrv_contr * card, capidrv_plci * plci, int event) | 
 | 639 | { | 
 | 640 | 	struct plcistatechange *p = plcitable; | 
 | 641 | 	while (p->event) { | 
 | 642 | 		if (plci->state == p->actstate && p->event == event) { | 
 | 643 | 			if (debugmode) | 
 | 644 | 				printk(KERN_DEBUG "capidrv-%d: plci_change_state:0x%x %d -> %d\n", | 
 | 645 | 				  card->contrnr, plci->plci, plci->state, p->nextstate); | 
 | 646 | 			plci->state = p->nextstate; | 
 | 647 | 			if (p->changefunc) | 
 | 648 | 				p->changefunc(card, plci); | 
 | 649 | 			return; | 
 | 650 | 		} | 
 | 651 | 		p++; | 
 | 652 | 	} | 
 | 653 | 	printk(KERN_ERR "capidrv-%d: plci_change_state:0x%x state=%d event=%d ????\n", | 
 | 654 | 	       card->contrnr, plci->plci, plci->state, event); | 
 | 655 | } | 
 | 656 |  | 
 | 657 | /* ------------------------------------------------------------------ */ | 
 | 658 |  | 
 | 659 | static _cmsg cmsg; | 
 | 660 |  | 
 | 661 | static void n0(capidrv_contr * card, capidrv_ncci * ncci) | 
 | 662 | { | 
 | 663 | 	isdn_ctrl cmd; | 
 | 664 |  | 
 | 665 | 	capi_fill_DISCONNECT_REQ(&cmsg, | 
 | 666 | 				 global.ap.applid, | 
 | 667 | 				 card->msgid++, | 
 | 668 | 				 ncci->plcip->plci, | 
 | 669 | 				 NULL,	/* BChannelinformation */ | 
 | 670 | 				 NULL,	/* Keypadfacility */ | 
 | 671 | 				 NULL,	/* Useruserdata */   /* $$$$ */ | 
 | 672 | 				 NULL	/* Facilitydataarray */ | 
 | 673 | 	); | 
 | 674 | 	send_message(card, &cmsg); | 
 | 675 | 	plci_change_state(card, ncci->plcip, EV_PLCI_DISCONNECT_REQ); | 
 | 676 |  | 
 | 677 | 	cmd.command = ISDN_STAT_BHUP; | 
 | 678 | 	cmd.driver = card->myid; | 
 | 679 | 	cmd.arg = ncci->chan; | 
 | 680 | 	card->interface.statcallb(&cmd); | 
 | 681 | 	free_ncci(card, ncci); | 
 | 682 | } | 
 | 683 |  | 
 | 684 | /* ------------------------------------------------------------------ */ | 
 | 685 |  | 
 | 686 | struct nccistatechange { | 
 | 687 | 	int actstate; | 
 | 688 | 	int nextstate; | 
 | 689 | 	int event; | 
 | 690 | 	void (*changefunc) (capidrv_contr * card, capidrv_ncci * ncci); | 
 | 691 | }; | 
 | 692 |  | 
 | 693 | static struct nccistatechange nccitable[] = | 
 | 694 | { | 
 | 695 |   /* N-0 */ | 
 | 696 |   {ST_NCCI_NONE, ST_NCCI_OUTGOING, EV_NCCI_CONNECT_B3_REQ, NULL}, | 
 | 697 |   {ST_NCCI_NONE, ST_NCCI_INCOMING, EV_NCCI_CONNECT_B3_IND, NULL}, | 
 | 698 |   /* N-0.1 */ | 
 | 699 |   {ST_NCCI_OUTGOING, ST_NCCI_ALLOCATED, EV_NCCI_CONNECT_B3_CONF_OK, NULL}, | 
 | 700 |   {ST_NCCI_OUTGOING, ST_NCCI_NONE, EV_NCCI_CONNECT_B3_CONF_ERROR, n0}, | 
 | 701 |   /* N-1 */ | 
 | 702 |   {ST_NCCI_INCOMING, ST_NCCI_DISCONNECTING, EV_NCCI_CONNECT_B3_REJECT, NULL}, | 
 | 703 |   {ST_NCCI_INCOMING, ST_NCCI_ALLOCATED, EV_NCCI_CONNECT_B3_RESP, NULL}, | 
 | 704 |   {ST_NCCI_INCOMING, ST_NCCI_DISCONNECTED, EV_NCCI_DISCONNECT_B3_IND, NULL}, | 
 | 705 |   {ST_NCCI_INCOMING, ST_NCCI_DISCONNECTING, EV_NCCI_DISCONNECT_B3_REQ, NULL}, | 
 | 706 |   /* N-2 */ | 
 | 707 |   {ST_NCCI_ALLOCATED, ST_NCCI_ACTIVE, EV_NCCI_CONNECT_B3_ACTIVE_IND, NULL}, | 
 | 708 |   {ST_NCCI_ALLOCATED, ST_NCCI_DISCONNECTED, EV_NCCI_DISCONNECT_B3_IND, NULL}, | 
 | 709 |   {ST_NCCI_ALLOCATED, ST_NCCI_DISCONNECTING, EV_NCCI_DISCONNECT_B3_REQ, NULL}, | 
 | 710 |   /* N-ACT */ | 
 | 711 |   {ST_NCCI_ACTIVE, ST_NCCI_ACTIVE, EV_NCCI_RESET_B3_IND, NULL}, | 
 | 712 |   {ST_NCCI_ACTIVE, ST_NCCI_RESETING, EV_NCCI_RESET_B3_REQ, NULL}, | 
 | 713 |   {ST_NCCI_ACTIVE, ST_NCCI_DISCONNECTED, EV_NCCI_DISCONNECT_B3_IND, NULL}, | 
 | 714 |   {ST_NCCI_ACTIVE, ST_NCCI_DISCONNECTING, EV_NCCI_DISCONNECT_B3_REQ, NULL}, | 
 | 715 |   /* N-3 */ | 
 | 716 |   {ST_NCCI_RESETING, ST_NCCI_ACTIVE, EV_NCCI_RESET_B3_IND, NULL}, | 
 | 717 |   {ST_NCCI_RESETING, ST_NCCI_DISCONNECTED, EV_NCCI_DISCONNECT_B3_IND, NULL}, | 
 | 718 |   {ST_NCCI_RESETING, ST_NCCI_DISCONNECTING, EV_NCCI_DISCONNECT_B3_REQ, NULL}, | 
 | 719 |   /* N-4 */ | 
 | 720 |   {ST_NCCI_DISCONNECTING, ST_NCCI_DISCONNECTED, EV_NCCI_DISCONNECT_B3_IND, NULL}, | 
 | 721 |   {ST_NCCI_DISCONNECTING, ST_NCCI_PREVIOUS, EV_NCCI_DISCONNECT_B3_CONF_ERROR,NULL}, | 
 | 722 |   /* N-5 */ | 
 | 723 |   {ST_NCCI_DISCONNECTED, ST_NCCI_NONE, EV_NCCI_DISCONNECT_B3_RESP, n0}, | 
 | 724 |   {}, | 
 | 725 | }; | 
 | 726 |  | 
 | 727 | static void ncci_change_state(capidrv_contr * card, capidrv_ncci * ncci, int event) | 
 | 728 | { | 
 | 729 | 	struct nccistatechange *p = nccitable; | 
 | 730 | 	while (p->event) { | 
 | 731 | 		if (ncci->state == p->actstate && p->event == event) { | 
 | 732 | 			if (debugmode) | 
 | 733 | 				printk(KERN_DEBUG "capidrv-%d: ncci_change_state:0x%x %d -> %d\n", | 
 | 734 | 				  card->contrnr, ncci->ncci, ncci->state, p->nextstate); | 
 | 735 | 			if (p->nextstate == ST_NCCI_PREVIOUS) { | 
 | 736 | 				ncci->state = ncci->oldstate; | 
 | 737 | 				ncci->oldstate = p->actstate; | 
 | 738 | 			} else { | 
 | 739 | 				ncci->oldstate = p->actstate; | 
 | 740 | 				ncci->state = p->nextstate; | 
 | 741 | 			} | 
 | 742 | 			if (p->changefunc) | 
 | 743 | 				p->changefunc(card, ncci); | 
 | 744 | 			return; | 
 | 745 | 		} | 
 | 746 | 		p++; | 
 | 747 | 	} | 
 | 748 | 	printk(KERN_ERR "capidrv-%d: ncci_change_state:0x%x state=%d event=%d ????\n", | 
 | 749 | 	       card->contrnr, ncci->ncci, ncci->state, event); | 
 | 750 | } | 
 | 751 |  | 
 | 752 | /* ------------------------------------------------------------------- */ | 
 | 753 |  | 
 | 754 | static inline int new_bchan(capidrv_contr * card) | 
 | 755 | { | 
 | 756 | 	int i; | 
 | 757 | 	for (i = 0; i < card->nbchan; i++) { | 
| Harvey Harrison | 2f9e9b6 | 2008-04-28 02:14:37 -0700 | [diff] [blame] | 758 | 		if (card->bchans[i].plcip == NULL) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 759 | 			card->bchans[i].disconnecting = 0; | 
 | 760 | 			return i; | 
 | 761 | 		} | 
 | 762 | 	} | 
 | 763 | 	return -1; | 
 | 764 | } | 
 | 765 |  | 
 | 766 | /* ------------------------------------------------------------------- */ | 
 | 767 |  | 
 | 768 | static void handle_controller(_cmsg * cmsg) | 
 | 769 | { | 
 | 770 | 	capidrv_contr *card = findcontrbynumber(cmsg->adr.adrController & 0x7f); | 
 | 771 |  | 
 | 772 | 	if (!card) { | 
 | 773 | 		printk(KERN_ERR "capidrv: %s from unknown controller 0x%x\n", | 
 | 774 | 		       capi_cmd2str(cmsg->Command, cmsg->Subcommand), | 
 | 775 | 		       cmsg->adr.adrController & 0x7f); | 
 | 776 | 		return; | 
 | 777 | 	} | 
 | 778 | 	switch (CAPICMD(cmsg->Command, cmsg->Subcommand)) { | 
 | 779 |  | 
 | 780 | 	case CAPI_LISTEN_CONF:	/* Controller */ | 
 | 781 | 		if (debugmode) | 
 | 782 | 			printk(KERN_DEBUG "capidrv-%d: listenconf Info=0x%4x (%s) cipmask=0x%x\n", | 
 | 783 | 			       card->contrnr, cmsg->Info, capi_info2str(cmsg->Info), card->cipmask); | 
 | 784 | 		if (cmsg->Info) { | 
 | 785 | 			listen_change_state(card, EV_LISTEN_CONF_ERROR); | 
 | 786 | 		} else if (card->cipmask == 0) { | 
 | 787 | 			listen_change_state(card, EV_LISTEN_CONF_EMPTY); | 
 | 788 | 		} else { | 
 | 789 | 			listen_change_state(card, EV_LISTEN_CONF_OK); | 
 | 790 | 		} | 
 | 791 | 		break; | 
 | 792 |  | 
 | 793 | 	case CAPI_MANUFACTURER_IND:	/* Controller */ | 
 | 794 | 		if (   cmsg->ManuID == 0x214D5641 | 
 | 795 | 		    && cmsg->Class == 0 | 
 | 796 | 		    && cmsg->Function == 1) { | 
 | 797 | 		   u8  *data = cmsg->ManuData+3; | 
 | 798 | 		   u16  len = cmsg->ManuData[0]; | 
 | 799 | 		   u16 layer; | 
 | 800 | 		   int direction; | 
 | 801 | 		   if (len == 255) { | 
 | 802 | 		      len = (cmsg->ManuData[1] | (cmsg->ManuData[2] << 8)); | 
 | 803 | 		      data += 2; | 
 | 804 | 		   } | 
 | 805 | 		   len -= 2; | 
 | 806 | 		   layer = ((*(data-1)) << 8) | *(data-2); | 
 | 807 | 		   if (layer & 0x300) | 
 | 808 | 			direction = (layer & 0x200) ? 0 : 1; | 
 | 809 | 		   else direction = (layer & 0x800) ? 0 : 1; | 
 | 810 | 		   if (layer & 0x0C00) { | 
 | 811 | 		   	if ((layer & 0xff) == 0x80) { | 
 | 812 | 		           handle_dtrace_data(card, direction, 1, data, len); | 
 | 813 | 		           break; | 
 | 814 | 		   	} | 
 | 815 | 		   } else if ((layer & 0xff) < 0x80) { | 
 | 816 | 		      handle_dtrace_data(card, direction, 0, data, len); | 
 | 817 | 		      break; | 
 | 818 | 		   } | 
 | 819 | 	           printk(KERN_INFO "capidrv-%d: %s from controller 0x%x layer 0x%x, ignored\n", | 
 | 820 |                         card->contrnr,  | 
 | 821 | 			capi_cmd2str(cmsg->Command, cmsg->Subcommand), | 
 | 822 | 			cmsg->adr.adrController, layer); | 
 | 823 |                    break; | 
 | 824 | 		} | 
 | 825 | 		goto ignored; | 
 | 826 | 	case CAPI_MANUFACTURER_CONF:	/* Controller */ | 
 | 827 | 		if (cmsg->ManuID == 0x214D5641) { | 
 | 828 | 		   char *s = NULL; | 
 | 829 | 		   switch (cmsg->Class) { | 
 | 830 | 		      case 0: break; | 
 | 831 | 		      case 1: s = "unknown class"; break; | 
 | 832 | 		      case 2: s = "unknown function"; break; | 
 | 833 | 		      default: s = "unkown error"; break; | 
 | 834 | 		   } | 
 | 835 | 		   if (s) | 
 | 836 | 	           printk(KERN_INFO "capidrv-%d: %s from controller 0x%x function %d: %s\n", | 
 | 837 | 			card->contrnr, | 
 | 838 | 			capi_cmd2str(cmsg->Command, cmsg->Subcommand), | 
 | 839 | 			cmsg->adr.adrController, | 
 | 840 | 			cmsg->Function, s); | 
 | 841 | 		   break; | 
 | 842 | 		} | 
 | 843 | 		goto ignored; | 
 | 844 | 	case CAPI_FACILITY_IND:	/* Controller/plci/ncci */ | 
 | 845 | 		goto ignored; | 
 | 846 | 	case CAPI_FACILITY_CONF:	/* Controller/plci/ncci */ | 
 | 847 | 		goto ignored; | 
 | 848 | 	case CAPI_INFO_IND:	/* Controller/plci */ | 
 | 849 | 		goto ignored; | 
 | 850 | 	case CAPI_INFO_CONF:	/* Controller/plci */ | 
 | 851 | 		goto ignored; | 
 | 852 |  | 
 | 853 | 	default: | 
 | 854 | 		printk(KERN_ERR "capidrv-%d: got %s from controller 0x%x ???", | 
 | 855 | 		       card->contrnr, | 
 | 856 | 		       capi_cmd2str(cmsg->Command, cmsg->Subcommand), | 
 | 857 | 		       cmsg->adr.adrController); | 
 | 858 | 	} | 
 | 859 | 	return; | 
 | 860 |  | 
 | 861 |       ignored: | 
 | 862 | 	printk(KERN_INFO "capidrv-%d: %s from controller 0x%x ignored\n", | 
 | 863 | 	       card->contrnr, | 
 | 864 | 	       capi_cmd2str(cmsg->Command, cmsg->Subcommand), | 
 | 865 | 	       cmsg->adr.adrController); | 
 | 866 | } | 
 | 867 |  | 
 | 868 | static void handle_incoming_call(capidrv_contr * card, _cmsg * cmsg) | 
 | 869 | { | 
 | 870 | 	capidrv_plci *plcip; | 
 | 871 | 	capidrv_bchan *bchan; | 
 | 872 | 	isdn_ctrl cmd; | 
 | 873 | 	int chan; | 
 | 874 |  | 
 | 875 | 	if ((chan = new_bchan(card)) == -1) { | 
 | 876 | 		printk(KERN_ERR "capidrv-%d: incoming call on not existing bchan ?\n", card->contrnr); | 
 | 877 | 		return; | 
 | 878 | 	} | 
 | 879 | 	bchan = &card->bchans[chan]; | 
| Harvey Harrison | 2f9e9b6 | 2008-04-28 02:14:37 -0700 | [diff] [blame] | 880 | 	if ((plcip = new_plci(card, chan)) == NULL) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 881 | 		printk(KERN_ERR "capidrv-%d: incoming call: no memory, sorry.\n", card->contrnr); | 
 | 882 | 		return; | 
 | 883 | 	} | 
 | 884 | 	bchan->incoming = 1; | 
 | 885 | 	plcip->plci = cmsg->adr.adrPLCI; | 
 | 886 | 	plci_change_state(card, plcip, EV_PLCI_CONNECT_IND); | 
 | 887 |  | 
 | 888 | 	cmd.command = ISDN_STAT_ICALL; | 
 | 889 | 	cmd.driver = card->myid; | 
 | 890 | 	cmd.arg = chan; | 
 | 891 | 	memset(&cmd.parm.setup, 0, sizeof(cmd.parm.setup)); | 
 | 892 | 	strncpy(cmd.parm.setup.phone, | 
 | 893 | 	        cmsg->CallingPartyNumber + 3, | 
 | 894 | 		cmsg->CallingPartyNumber[0] - 2); | 
 | 895 | 	strncpy(cmd.parm.setup.eazmsn, | 
 | 896 | 	        cmsg->CalledPartyNumber + 2, | 
 | 897 | 		cmsg->CalledPartyNumber[0] - 1); | 
 | 898 | 	cmd.parm.setup.si1 = cip2si1(cmsg->CIPValue); | 
 | 899 | 	cmd.parm.setup.si2 = cip2si2(cmsg->CIPValue); | 
 | 900 | 	cmd.parm.setup.plan = cmsg->CallingPartyNumber[1]; | 
 | 901 | 	cmd.parm.setup.screen = cmsg->CallingPartyNumber[2]; | 
 | 902 |  | 
 | 903 | 	printk(KERN_INFO "capidrv-%d: incoming call %s,%d,%d,%s\n",  | 
 | 904 | 			card->contrnr, | 
 | 905 | 			cmd.parm.setup.phone, | 
 | 906 | 			cmd.parm.setup.si1, | 
 | 907 | 			cmd.parm.setup.si2, | 
 | 908 | 			cmd.parm.setup.eazmsn); | 
 | 909 |  | 
 | 910 | 	if (cmd.parm.setup.si1 == 1 && cmd.parm.setup.si2 != 0) { | 
 | 911 | 		printk(KERN_INFO "capidrv-%d: patching si2=%d to 0 for VBOX\n",  | 
 | 912 | 			card->contrnr, | 
 | 913 | 			cmd.parm.setup.si2); | 
 | 914 | 		cmd.parm.setup.si2 = 0; | 
 | 915 | 	} | 
 | 916 |  | 
 | 917 | 	switch (card->interface.statcallb(&cmd)) { | 
 | 918 | 	case 0: | 
 | 919 | 	case 3: | 
 | 920 | 		/* No device matching this call. | 
 | 921 | 		 * and isdn_common.c has send a HANGUP command | 
 | 922 | 		 * which is ignored in state ST_PLCI_INCOMING, | 
 | 923 | 		 * so we send RESP to ignore the call | 
 | 924 | 		 */ | 
 | 925 | 		capi_cmsg_answer(cmsg); | 
 | 926 | 		cmsg->Reject = 1;	/* ignore */ | 
 | 927 | 		send_message(card, cmsg); | 
 | 928 | 		plci_change_state(card, plcip, EV_PLCI_CONNECT_REJECT); | 
 | 929 | 		printk(KERN_INFO "capidrv-%d: incoming call %s,%d,%d,%s ignored\n", | 
 | 930 | 			card->contrnr, | 
 | 931 | 			cmd.parm.setup.phone, | 
 | 932 | 			cmd.parm.setup.si1, | 
 | 933 | 			cmd.parm.setup.si2, | 
 | 934 | 			cmd.parm.setup.eazmsn); | 
 | 935 | 		break; | 
 | 936 | 	case 1: | 
 | 937 | 		/* At least one device matching this call (RING on ttyI) | 
 | 938 | 		 * HL-driver may send ALERTING on the D-channel in this | 
 | 939 | 		 * case. | 
 | 940 | 		 * really means: RING on ttyI or a net interface | 
 | 941 | 		 * accepted this call already. | 
 | 942 | 		 * | 
 | 943 | 		 * If the call was accepted, state has already changed, | 
 | 944 | 		 * and CONNECT_RESP already sent. | 
 | 945 | 		 */ | 
 | 946 | 		if (plcip->state == ST_PLCI_INCOMING) { | 
 | 947 | 			printk(KERN_INFO "capidrv-%d: incoming call %s,%d,%d,%s tty alerting\n", | 
 | 948 | 				card->contrnr, | 
 | 949 | 				cmd.parm.setup.phone, | 
 | 950 | 				cmd.parm.setup.si1, | 
 | 951 | 				cmd.parm.setup.si2, | 
 | 952 | 				cmd.parm.setup.eazmsn); | 
 | 953 | 			capi_fill_ALERT_REQ(cmsg, | 
 | 954 | 					    global.ap.applid, | 
 | 955 | 					    card->msgid++, | 
 | 956 | 					    plcip->plci,	/* adr */ | 
 | 957 | 					    NULL,/* BChannelinformation */ | 
 | 958 | 					    NULL,/* Keypadfacility */ | 
 | 959 | 					    NULL,/* Useruserdata */ | 
 | 960 | 					    NULL /* Facilitydataarray */ | 
 | 961 | 			); | 
 | 962 | 			plcip->msgid = cmsg->Messagenumber; | 
 | 963 | 			send_message(card, cmsg); | 
 | 964 | 		} else { | 
 | 965 | 			printk(KERN_INFO "capidrv-%d: incoming call %s,%d,%d,%s on netdev\n", | 
 | 966 | 				card->contrnr, | 
 | 967 | 				cmd.parm.setup.phone, | 
 | 968 | 				cmd.parm.setup.si1, | 
 | 969 | 				cmd.parm.setup.si2, | 
 | 970 | 				cmd.parm.setup.eazmsn); | 
 | 971 | 		} | 
 | 972 | 		break; | 
 | 973 |  | 
 | 974 | 	case 2:		/* Call will be rejected. */ | 
 | 975 | 		capi_cmsg_answer(cmsg); | 
 | 976 | 		cmsg->Reject = 2;	/* reject call, normal call clearing */ | 
 | 977 | 		send_message(card, cmsg); | 
 | 978 | 		plci_change_state(card, plcip, EV_PLCI_CONNECT_REJECT); | 
 | 979 | 		break; | 
 | 980 |  | 
 | 981 | 	default: | 
 | 982 | 		/* An error happened. (Invalid parameters for example.) */ | 
 | 983 | 		capi_cmsg_answer(cmsg); | 
 | 984 | 		cmsg->Reject = 8;	/* reject call, | 
 | 985 | 					   destination out of order */ | 
 | 986 | 		send_message(card, cmsg); | 
 | 987 | 		plci_change_state(card, plcip, EV_PLCI_CONNECT_REJECT); | 
 | 988 | 		break; | 
 | 989 | 	} | 
 | 990 | 	return; | 
 | 991 | } | 
 | 992 |  | 
 | 993 | static void handle_plci(_cmsg * cmsg) | 
 | 994 | { | 
 | 995 | 	capidrv_contr *card = findcontrbynumber(cmsg->adr.adrController & 0x7f); | 
 | 996 | 	capidrv_plci *plcip; | 
 | 997 | 	isdn_ctrl cmd; | 
| Karsten Keil | 17f0cd2 | 2007-02-28 20:13:50 -0800 | [diff] [blame] | 998 | 	_cdebbuf *cdb; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 999 |  | 
 | 1000 | 	if (!card) { | 
 | 1001 | 		printk(KERN_ERR "capidrv: %s from unknown controller 0x%x\n", | 
 | 1002 | 		       capi_cmd2str(cmsg->Command, cmsg->Subcommand), | 
 | 1003 | 		       cmsg->adr.adrController & 0x7f); | 
 | 1004 | 		return; | 
 | 1005 | 	} | 
 | 1006 | 	switch (CAPICMD(cmsg->Command, cmsg->Subcommand)) { | 
 | 1007 |  | 
 | 1008 | 	case CAPI_DISCONNECT_IND:	/* plci */ | 
 | 1009 | 		if (cmsg->Reason) { | 
 | 1010 | 			printk(KERN_INFO "capidrv-%d: %s reason 0x%x (%s) for plci 0x%x\n", | 
 | 1011 | 			   card->contrnr, | 
 | 1012 | 			   capi_cmd2str(cmsg->Command, cmsg->Subcommand), | 
 | 1013 | 			       cmsg->Reason, capi_info2str(cmsg->Reason), cmsg->adr.adrPLCI); | 
 | 1014 | 		} | 
 | 1015 | 		if (!(plcip = find_plci_by_plci(card, cmsg->adr.adrPLCI))) { | 
 | 1016 | 			capi_cmsg_answer(cmsg); | 
 | 1017 | 			send_message(card, cmsg); | 
 | 1018 | 			goto notfound; | 
 | 1019 | 		} | 
 | 1020 | 		card->bchans[plcip->chan].disconnecting = 1; | 
 | 1021 | 		plci_change_state(card, plcip, EV_PLCI_DISCONNECT_IND); | 
 | 1022 | 		capi_cmsg_answer(cmsg); | 
 | 1023 | 		send_message(card, cmsg); | 
 | 1024 | 		plci_change_state(card, plcip, EV_PLCI_DISCONNECT_RESP); | 
 | 1025 | 		break; | 
 | 1026 |  | 
 | 1027 | 	case CAPI_DISCONNECT_CONF:	/* plci */ | 
 | 1028 | 		if (cmsg->Info) { | 
 | 1029 | 			printk(KERN_INFO "capidrv-%d: %s info 0x%x (%s) for plci 0x%x\n", | 
 | 1030 | 			   card->contrnr, | 
 | 1031 | 			   capi_cmd2str(cmsg->Command, cmsg->Subcommand), | 
 | 1032 | 			       cmsg->Info, capi_info2str(cmsg->Info),  | 
 | 1033 | 			       cmsg->adr.adrPLCI); | 
 | 1034 | 		} | 
 | 1035 | 		if (!(plcip = find_plci_by_plci(card, cmsg->adr.adrPLCI))) | 
 | 1036 | 			goto notfound; | 
 | 1037 |  | 
 | 1038 | 		card->bchans[plcip->chan].disconnecting = 1; | 
 | 1039 | 		break; | 
 | 1040 |  | 
 | 1041 | 	case CAPI_ALERT_CONF:	/* plci */ | 
 | 1042 | 		if (cmsg->Info) { | 
 | 1043 | 			printk(KERN_INFO "capidrv-%d: %s info 0x%x (%s) for plci 0x%x\n", | 
 | 1044 | 			   card->contrnr, | 
 | 1045 | 			   capi_cmd2str(cmsg->Command, cmsg->Subcommand), | 
 | 1046 | 			       cmsg->Info, capi_info2str(cmsg->Info),  | 
 | 1047 | 			       cmsg->adr.adrPLCI); | 
 | 1048 | 		} | 
 | 1049 | 		break; | 
 | 1050 |  | 
 | 1051 | 	case CAPI_CONNECT_IND:	/* plci */ | 
 | 1052 | 		handle_incoming_call(card, cmsg); | 
 | 1053 | 		break; | 
 | 1054 |  | 
 | 1055 | 	case CAPI_CONNECT_CONF:	/* plci */ | 
 | 1056 | 		if (cmsg->Info) { | 
 | 1057 | 			printk(KERN_INFO "capidrv-%d: %s info 0x%x (%s) for plci 0x%x\n", | 
 | 1058 | 			   card->contrnr, | 
 | 1059 | 			   capi_cmd2str(cmsg->Command, cmsg->Subcommand), | 
 | 1060 | 			       cmsg->Info, capi_info2str(cmsg->Info),  | 
 | 1061 | 			       cmsg->adr.adrPLCI); | 
 | 1062 | 		} | 
 | 1063 | 		if (!(plcip = find_plci_by_msgid(card, cmsg->Messagenumber))) | 
 | 1064 | 			goto notfound; | 
 | 1065 |  | 
 | 1066 | 		plcip->plci = cmsg->adr.adrPLCI; | 
 | 1067 | 		if (cmsg->Info) { | 
 | 1068 | 			plci_change_state(card, plcip, EV_PLCI_CONNECT_CONF_ERROR); | 
 | 1069 | 		} else { | 
 | 1070 | 			plci_change_state(card, plcip, EV_PLCI_CONNECT_CONF_OK); | 
 | 1071 | 		} | 
 | 1072 | 		break; | 
 | 1073 |  | 
 | 1074 | 	case CAPI_CONNECT_ACTIVE_IND:	/* plci */ | 
 | 1075 |  | 
 | 1076 | 		if (!(plcip = find_plci_by_plci(card, cmsg->adr.adrPLCI))) | 
 | 1077 | 			goto notfound; | 
 | 1078 |  | 
 | 1079 | 		if (card->bchans[plcip->chan].incoming) { | 
 | 1080 | 			capi_cmsg_answer(cmsg); | 
 | 1081 | 			send_message(card, cmsg); | 
 | 1082 | 			plci_change_state(card, plcip, EV_PLCI_CONNECT_ACTIVE_IND); | 
 | 1083 | 		} else { | 
 | 1084 | 			capidrv_ncci *nccip; | 
 | 1085 | 			capi_cmsg_answer(cmsg); | 
 | 1086 | 			send_message(card, cmsg); | 
 | 1087 |  | 
 | 1088 | 			nccip = new_ncci(card, plcip, cmsg->adr.adrPLCI); | 
 | 1089 |  | 
 | 1090 | 			if (!nccip) { | 
 | 1091 | 				printk(KERN_ERR "capidrv-%d: no mem for ncci, sorry\n", card->contrnr); | 
 | 1092 | 				break;	/* $$$$ */ | 
 | 1093 | 			} | 
 | 1094 | 			capi_fill_CONNECT_B3_REQ(cmsg, | 
 | 1095 | 						 global.ap.applid, | 
 | 1096 | 						 card->msgid++, | 
 | 1097 | 						 plcip->plci,	/* adr */ | 
 | 1098 | 						 NULL	/* NCPI */ | 
 | 1099 | 			); | 
 | 1100 | 			nccip->msgid = cmsg->Messagenumber; | 
 | 1101 | 			send_message(card, cmsg); | 
 | 1102 | 			cmd.command = ISDN_STAT_DCONN; | 
 | 1103 | 			cmd.driver = card->myid; | 
 | 1104 | 			cmd.arg = plcip->chan; | 
 | 1105 | 			card->interface.statcallb(&cmd); | 
 | 1106 | 			plci_change_state(card, plcip, EV_PLCI_CONNECT_ACTIVE_IND); | 
 | 1107 | 			ncci_change_state(card, nccip, EV_NCCI_CONNECT_B3_REQ); | 
 | 1108 | 		} | 
 | 1109 | 		break; | 
 | 1110 |  | 
 | 1111 | 	case CAPI_INFO_IND:	/* Controller/plci */ | 
 | 1112 |  | 
 | 1113 | 		if (!(plcip = find_plci_by_plci(card, cmsg->adr.adrPLCI))) | 
 | 1114 | 			goto notfound; | 
 | 1115 |  | 
 | 1116 | 		if (cmsg->InfoNumber == 0x4000) { | 
 | 1117 | 			if (cmsg->InfoElement[0] == 4) { | 
 | 1118 | 				cmd.command = ISDN_STAT_CINF; | 
 | 1119 | 				cmd.driver = card->myid; | 
 | 1120 | 				cmd.arg = plcip->chan; | 
 | 1121 | 				sprintf(cmd.parm.num, "%lu", | 
 | 1122 | 					(unsigned long) | 
 | 1123 | 					((u32) cmsg->InfoElement[1] | 
 | 1124 | 				  | ((u32) (cmsg->InfoElement[2]) << 8) | 
 | 1125 | 				 | ((u32) (cmsg->InfoElement[3]) << 16) | 
 | 1126 | 					 | ((u32) (cmsg->InfoElement[4]) << 24))); | 
 | 1127 | 				card->interface.statcallb(&cmd); | 
 | 1128 | 				break; | 
 | 1129 | 			} | 
 | 1130 | 		} | 
| Karsten Keil | 17f0cd2 | 2007-02-28 20:13:50 -0800 | [diff] [blame] | 1131 | 		cdb = capi_cmsg2str(cmsg); | 
 | 1132 | 		if (cdb) { | 
 | 1133 | 			printk(KERN_WARNING "capidrv-%d: %s\n", | 
 | 1134 | 				card->contrnr, cdb->buf); | 
 | 1135 | 			cdebbuf_free(cdb); | 
 | 1136 | 		} else | 
 | 1137 | 			printk(KERN_WARNING "capidrv-%d: CAPI_INFO_IND InfoNumber %x not handled\n", | 
 | 1138 | 				card->contrnr, cmsg->InfoNumber); | 
 | 1139 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1140 | 		break; | 
 | 1141 |  | 
 | 1142 | 	case CAPI_CONNECT_ACTIVE_CONF:		/* plci */ | 
 | 1143 | 		goto ignored; | 
 | 1144 | 	case CAPI_SELECT_B_PROTOCOL_CONF:	/* plci */ | 
 | 1145 | 		goto ignored; | 
 | 1146 | 	case CAPI_FACILITY_IND:	/* Controller/plci/ncci */ | 
 | 1147 | 		goto ignored; | 
 | 1148 | 	case CAPI_FACILITY_CONF:	/* Controller/plci/ncci */ | 
 | 1149 | 		goto ignored; | 
 | 1150 |  | 
 | 1151 | 	case CAPI_INFO_CONF:	/* Controller/plci */ | 
 | 1152 | 		goto ignored; | 
 | 1153 |  | 
 | 1154 | 	default: | 
 | 1155 | 		printk(KERN_ERR "capidrv-%d: got %s for plci 0x%x ???", | 
 | 1156 | 		       card->contrnr, | 
 | 1157 | 		       capi_cmd2str(cmsg->Command, cmsg->Subcommand), | 
 | 1158 | 		       cmsg->adr.adrPLCI); | 
 | 1159 | 	} | 
 | 1160 | 	return; | 
 | 1161 |       ignored: | 
 | 1162 | 	printk(KERN_INFO "capidrv-%d: %s for plci 0x%x ignored\n", | 
 | 1163 | 	       card->contrnr, | 
 | 1164 | 	       capi_cmd2str(cmsg->Command, cmsg->Subcommand), | 
 | 1165 | 	       cmsg->adr.adrPLCI); | 
 | 1166 | 	return; | 
 | 1167 |       notfound: | 
 | 1168 | 	printk(KERN_ERR "capidrv-%d: %s: plci 0x%x not found\n", | 
 | 1169 | 	       card->contrnr, | 
 | 1170 | 	       capi_cmd2str(cmsg->Command, cmsg->Subcommand), | 
 | 1171 | 	       cmsg->adr.adrPLCI); | 
 | 1172 | 	return; | 
 | 1173 | } | 
 | 1174 |  | 
 | 1175 | static void handle_ncci(_cmsg * cmsg) | 
 | 1176 | { | 
 | 1177 | 	capidrv_contr *card = findcontrbynumber(cmsg->adr.adrController & 0x7f); | 
 | 1178 | 	capidrv_plci *plcip; | 
 | 1179 | 	capidrv_ncci *nccip; | 
 | 1180 | 	isdn_ctrl cmd; | 
 | 1181 | 	int len; | 
 | 1182 |  | 
 | 1183 | 	if (!card) { | 
 | 1184 | 		printk(KERN_ERR "capidrv: %s from unknown controller 0x%x\n", | 
 | 1185 | 		       capi_cmd2str(cmsg->Command, cmsg->Subcommand), | 
 | 1186 | 		       cmsg->adr.adrController & 0x7f); | 
 | 1187 | 		return; | 
 | 1188 | 	} | 
 | 1189 | 	switch (CAPICMD(cmsg->Command, cmsg->Subcommand)) { | 
 | 1190 |  | 
 | 1191 | 	case CAPI_CONNECT_B3_ACTIVE_IND:	/* ncci */ | 
 | 1192 | 		if (!(nccip = find_ncci(card, cmsg->adr.adrNCCI))) | 
 | 1193 | 			goto notfound; | 
 | 1194 |  | 
 | 1195 | 		capi_cmsg_answer(cmsg); | 
 | 1196 | 		send_message(card, cmsg); | 
 | 1197 | 		ncci_change_state(card, nccip, EV_NCCI_CONNECT_B3_ACTIVE_IND); | 
 | 1198 |  | 
 | 1199 | 		cmd.command = ISDN_STAT_BCONN; | 
 | 1200 | 		cmd.driver = card->myid; | 
 | 1201 | 		cmd.arg = nccip->chan; | 
 | 1202 | 		card->interface.statcallb(&cmd); | 
 | 1203 |  | 
 | 1204 | 		printk(KERN_INFO "capidrv-%d: chan %d up with ncci 0x%x\n", | 
 | 1205 | 		       card->contrnr, nccip->chan, nccip->ncci); | 
 | 1206 | 		break; | 
 | 1207 |  | 
 | 1208 | 	case CAPI_CONNECT_B3_ACTIVE_CONF:	/* ncci */ | 
 | 1209 | 		goto ignored; | 
 | 1210 |  | 
 | 1211 | 	case CAPI_CONNECT_B3_IND:	/* ncci */ | 
 | 1212 |  | 
 | 1213 | 		plcip = find_plci_by_ncci(card, cmsg->adr.adrNCCI); | 
 | 1214 | 		if (plcip) { | 
 | 1215 | 			nccip = new_ncci(card, plcip, cmsg->adr.adrNCCI); | 
 | 1216 | 			if (nccip) { | 
 | 1217 | 				ncci_change_state(card, nccip, EV_NCCI_CONNECT_B3_IND); | 
 | 1218 | 				capi_fill_CONNECT_B3_RESP(cmsg, | 
 | 1219 | 							  global.ap.applid, | 
 | 1220 | 							  card->msgid++, | 
 | 1221 | 							  nccip->ncci,	/* adr */ | 
 | 1222 | 							  0,	/* Reject */ | 
 | 1223 | 							  NULL	/* NCPI */ | 
 | 1224 | 				); | 
 | 1225 | 				send_message(card, cmsg); | 
 | 1226 | 				ncci_change_state(card, nccip, EV_NCCI_CONNECT_B3_RESP); | 
 | 1227 | 				break; | 
 | 1228 | 			} | 
 | 1229 | 			printk(KERN_ERR "capidrv-%d: no mem for ncci, sorry\n",							card->contrnr); | 
 | 1230 | 		} else { | 
 | 1231 | 			printk(KERN_ERR "capidrv-%d: %s: plci for ncci 0x%x not found\n", | 
 | 1232 | 			   card->contrnr, | 
 | 1233 | 			   capi_cmd2str(cmsg->Command, cmsg->Subcommand), | 
 | 1234 | 			       cmsg->adr.adrNCCI); | 
 | 1235 | 		} | 
 | 1236 | 		capi_fill_CONNECT_B3_RESP(cmsg, | 
 | 1237 | 					  global.ap.applid, | 
 | 1238 | 					  card->msgid++, | 
 | 1239 | 					  cmsg->adr.adrNCCI, | 
 | 1240 | 					  2,	/* Reject */ | 
 | 1241 | 					  NULL	/* NCPI */ | 
 | 1242 | 		); | 
 | 1243 | 		send_message(card, cmsg); | 
 | 1244 | 		break; | 
 | 1245 |  | 
 | 1246 | 	case CAPI_CONNECT_B3_CONF:	/* ncci */ | 
 | 1247 |  | 
 | 1248 | 		if (!(nccip = find_ncci_by_msgid(card, | 
 | 1249 | 						 cmsg->adr.adrNCCI, | 
 | 1250 | 						 cmsg->Messagenumber))) | 
 | 1251 | 			goto notfound; | 
 | 1252 |  | 
 | 1253 | 		nccip->ncci = cmsg->adr.adrNCCI; | 
 | 1254 | 		if (cmsg->Info) { | 
 | 1255 | 			printk(KERN_INFO "capidrv-%d: %s info 0x%x (%s) for ncci 0x%x\n", | 
 | 1256 | 			   card->contrnr, | 
 | 1257 | 			   capi_cmd2str(cmsg->Command, cmsg->Subcommand), | 
 | 1258 | 			       cmsg->Info, capi_info2str(cmsg->Info),  | 
 | 1259 | 			       cmsg->adr.adrNCCI); | 
 | 1260 | 		} | 
 | 1261 |  | 
 | 1262 | 		if (cmsg->Info) | 
 | 1263 | 			ncci_change_state(card, nccip, EV_NCCI_CONNECT_B3_CONF_ERROR); | 
 | 1264 | 		else | 
 | 1265 | 			ncci_change_state(card, nccip, EV_NCCI_CONNECT_B3_CONF_OK); | 
 | 1266 | 		break; | 
 | 1267 |  | 
 | 1268 | 	case CAPI_CONNECT_B3_T90_ACTIVE_IND:	/* ncci */ | 
 | 1269 | 		capi_cmsg_answer(cmsg); | 
 | 1270 | 		send_message(card, cmsg); | 
 | 1271 | 		break; | 
 | 1272 |  | 
 | 1273 | 	case CAPI_DATA_B3_IND:	/* ncci */ | 
 | 1274 | 		/* handled in handle_data() */ | 
 | 1275 | 		goto ignored; | 
 | 1276 |  | 
 | 1277 | 	case CAPI_DATA_B3_CONF:	/* ncci */ | 
 | 1278 | 		if (cmsg->Info) { | 
 | 1279 | 			printk(KERN_WARNING "CAPI_DATA_B3_CONF: Info %x - %s\n", | 
 | 1280 | 				cmsg->Info, capi_info2str(cmsg->Info)); | 
 | 1281 | 		} | 
 | 1282 | 		if (!(nccip = find_ncci(card, cmsg->adr.adrNCCI))) | 
 | 1283 | 			goto notfound; | 
 | 1284 |  | 
 | 1285 | 		len = capidrv_del_ack(nccip, cmsg->DataHandle); | 
 | 1286 | 		if (len < 0) | 
 | 1287 | 			break; | 
 | 1288 | 	        cmd.command = ISDN_STAT_BSENT; | 
 | 1289 | 	        cmd.driver = card->myid; | 
 | 1290 | 	        cmd.arg = nccip->chan; | 
 | 1291 | 		cmd.parm.length = len; | 
 | 1292 | 	        card->interface.statcallb(&cmd); | 
 | 1293 | 		break; | 
 | 1294 |  | 
 | 1295 | 	case CAPI_DISCONNECT_B3_IND:	/* ncci */ | 
 | 1296 | 		if (!(nccip = find_ncci(card, cmsg->adr.adrNCCI))) | 
 | 1297 | 			goto notfound; | 
 | 1298 |  | 
 | 1299 | 		card->bchans[nccip->chan].disconnecting = 1; | 
 | 1300 | 		ncci_change_state(card, nccip, EV_NCCI_DISCONNECT_B3_IND); | 
 | 1301 | 		capi_cmsg_answer(cmsg); | 
 | 1302 | 		send_message(card, cmsg); | 
 | 1303 | 		ncci_change_state(card, nccip, EV_NCCI_DISCONNECT_B3_RESP); | 
 | 1304 | 		break; | 
 | 1305 |  | 
 | 1306 | 	case CAPI_DISCONNECT_B3_CONF:	/* ncci */ | 
 | 1307 | 		if (!(nccip = find_ncci(card, cmsg->adr.adrNCCI))) | 
 | 1308 | 			goto notfound; | 
 | 1309 | 		if (cmsg->Info) { | 
 | 1310 | 			printk(KERN_INFO "capidrv-%d: %s info 0x%x (%s) for ncci 0x%x\n", | 
 | 1311 | 			   card->contrnr, | 
 | 1312 | 			   capi_cmd2str(cmsg->Command, cmsg->Subcommand), | 
 | 1313 | 			       cmsg->Info, capi_info2str(cmsg->Info),  | 
 | 1314 | 			       cmsg->adr.adrNCCI); | 
 | 1315 | 			ncci_change_state(card, nccip, EV_NCCI_DISCONNECT_B3_CONF_ERROR); | 
 | 1316 | 		} | 
 | 1317 | 		break; | 
 | 1318 |  | 
 | 1319 | 	case CAPI_RESET_B3_IND:	/* ncci */ | 
 | 1320 | 		if (!(nccip = find_ncci(card, cmsg->adr.adrNCCI))) | 
 | 1321 | 			goto notfound; | 
 | 1322 | 		ncci_change_state(card, nccip, EV_NCCI_RESET_B3_IND); | 
 | 1323 | 		capi_cmsg_answer(cmsg); | 
 | 1324 | 		send_message(card, cmsg); | 
 | 1325 | 		break; | 
 | 1326 |  | 
 | 1327 | 	case CAPI_RESET_B3_CONF:	/* ncci */ | 
 | 1328 | 		goto ignored;	/* $$$$ */ | 
 | 1329 |  | 
 | 1330 | 	case CAPI_FACILITY_IND:	/* Controller/plci/ncci */ | 
 | 1331 | 		goto ignored; | 
 | 1332 | 	case CAPI_FACILITY_CONF:	/* Controller/plci/ncci */ | 
 | 1333 | 		goto ignored; | 
 | 1334 |  | 
 | 1335 | 	default: | 
 | 1336 | 		printk(KERN_ERR "capidrv-%d: got %s for ncci 0x%x ???", | 
 | 1337 | 		       card->contrnr, | 
 | 1338 | 		       capi_cmd2str(cmsg->Command, cmsg->Subcommand), | 
 | 1339 | 		       cmsg->adr.adrNCCI); | 
 | 1340 | 	} | 
 | 1341 | 	return; | 
 | 1342 |       ignored: | 
 | 1343 | 	printk(KERN_INFO "capidrv-%d: %s for ncci 0x%x ignored\n", | 
 | 1344 | 	       card->contrnr, | 
 | 1345 | 	       capi_cmd2str(cmsg->Command, cmsg->Subcommand), | 
 | 1346 | 	       cmsg->adr.adrNCCI); | 
 | 1347 | 	return; | 
 | 1348 |       notfound: | 
 | 1349 | 	printk(KERN_ERR "capidrv-%d: %s: ncci 0x%x not found\n", | 
 | 1350 | 	       card->contrnr, | 
 | 1351 | 	       capi_cmd2str(cmsg->Command, cmsg->Subcommand), | 
 | 1352 | 	       cmsg->adr.adrNCCI); | 
 | 1353 | } | 
 | 1354 |  | 
 | 1355 |  | 
 | 1356 | static void handle_data(_cmsg * cmsg, struct sk_buff *skb) | 
 | 1357 | { | 
 | 1358 | 	capidrv_contr *card = findcontrbynumber(cmsg->adr.adrController & 0x7f); | 
 | 1359 | 	capidrv_ncci *nccip; | 
 | 1360 |  | 
 | 1361 | 	if (!card) { | 
 | 1362 | 		printk(KERN_ERR "capidrv: %s from unknown controller 0x%x\n", | 
 | 1363 | 		       capi_cmd2str(cmsg->Command, cmsg->Subcommand), | 
 | 1364 | 		       cmsg->adr.adrController & 0x7f); | 
 | 1365 | 		kfree_skb(skb); | 
 | 1366 | 		return; | 
 | 1367 | 	} | 
 | 1368 | 	if (!(nccip = find_ncci(card, cmsg->adr.adrNCCI))) { | 
 | 1369 | 		printk(KERN_ERR "capidrv-%d: %s: ncci 0x%x not found\n", | 
 | 1370 | 		       card->contrnr, | 
 | 1371 | 		       capi_cmd2str(cmsg->Command, cmsg->Subcommand), | 
 | 1372 | 		       cmsg->adr.adrNCCI); | 
 | 1373 | 		kfree_skb(skb); | 
 | 1374 | 		return; | 
 | 1375 | 	} | 
 | 1376 | 	(void) skb_pull(skb, CAPIMSG_LEN(skb->data)); | 
 | 1377 | 	card->interface.rcvcallb_skb(card->myid, nccip->chan, skb); | 
 | 1378 | 	capi_cmsg_answer(cmsg); | 
 | 1379 | 	send_message(card, cmsg); | 
 | 1380 | } | 
 | 1381 |  | 
 | 1382 | static _cmsg s_cmsg; | 
 | 1383 |  | 
 | 1384 | static void capidrv_recv_message(struct capi20_appl *ap, struct sk_buff *skb) | 
 | 1385 | { | 
 | 1386 | 	capi_message2cmsg(&s_cmsg, skb->data); | 
| Karsten Keil | 17f0cd2 | 2007-02-28 20:13:50 -0800 | [diff] [blame] | 1387 | 	if (debugmode > 3) { | 
 | 1388 | 		_cdebbuf *cdb = capi_cmsg2str(&s_cmsg); | 
 | 1389 |  | 
 | 1390 | 		if (cdb) { | 
| Harvey Harrison | 156f1ed | 2008-04-28 02:14:40 -0700 | [diff] [blame] | 1391 | 			printk(KERN_DEBUG "%s: applid=%d %s\n", __func__, | 
| Karsten Keil | 17f0cd2 | 2007-02-28 20:13:50 -0800 | [diff] [blame] | 1392 | 				ap->applid, cdb->buf); | 
 | 1393 | 			cdebbuf_free(cdb); | 
 | 1394 | 		} else | 
 | 1395 | 			printk(KERN_DEBUG "%s: applid=%d %s not traced\n", | 
| Harvey Harrison | 156f1ed | 2008-04-28 02:14:40 -0700 | [diff] [blame] | 1396 | 				__func__, ap->applid, | 
| Karsten Keil | 17f0cd2 | 2007-02-28 20:13:50 -0800 | [diff] [blame] | 1397 | 				capi_cmd2str(s_cmsg.Command, s_cmsg.Subcommand)); | 
 | 1398 | 	} | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1399 | 	if (s_cmsg.Command == CAPI_DATA_B3 | 
 | 1400 | 	    && s_cmsg.Subcommand == CAPI_IND) { | 
 | 1401 | 		handle_data(&s_cmsg, skb); | 
 | 1402 | 		return; | 
 | 1403 | 	} | 
 | 1404 | 	if ((s_cmsg.adr.adrController & 0xffffff00) == 0) | 
 | 1405 | 		handle_controller(&s_cmsg); | 
 | 1406 | 	else if ((s_cmsg.adr.adrPLCI & 0xffff0000) == 0) | 
 | 1407 | 		handle_plci(&s_cmsg); | 
 | 1408 | 	else | 
 | 1409 | 		handle_ncci(&s_cmsg); | 
 | 1410 | 	/* | 
 | 1411 | 	 * data of skb used in s_cmsg, | 
 | 1412 | 	 * free data when s_cmsg is not used again | 
 | 1413 | 	 * thanks to Lars Heete <hel@admin.de> | 
 | 1414 | 	 */ | 
 | 1415 | 	kfree_skb(skb); | 
 | 1416 | } | 
 | 1417 |  | 
 | 1418 | /* ------------------------------------------------------------------- */ | 
 | 1419 |  | 
 | 1420 | #define PUTBYTE_TO_STATUS(card, byte) \ | 
 | 1421 | 	do { \ | 
 | 1422 | 		*(card)->q931_write++ = (byte); \ | 
 | 1423 |         	if ((card)->q931_write > (card)->q931_end) \ | 
 | 1424 | 	  		(card)->q931_write = (card)->q931_buf; \ | 
 | 1425 | 	} while (0) | 
 | 1426 |  | 
 | 1427 | static void handle_dtrace_data(capidrv_contr *card, | 
 | 1428 | 			     int send, int level2, u8 *data, u16 len) | 
 | 1429 | { | 
 | 1430 |     	u8 *p, *end; | 
 | 1431 |     	isdn_ctrl cmd; | 
 | 1432 |  | 
 | 1433 |     	if (!len) { | 
 | 1434 | 		printk(KERN_DEBUG "capidrv-%d: avmb1_q931_data: len == %d\n", | 
 | 1435 | 				card->contrnr, len); | 
 | 1436 | 		return; | 
 | 1437 | 	} | 
 | 1438 |  | 
 | 1439 | 	if (level2) { | 
 | 1440 | 		PUTBYTE_TO_STATUS(card, 'D'); | 
 | 1441 | 		PUTBYTE_TO_STATUS(card, '2'); | 
 | 1442 |         	PUTBYTE_TO_STATUS(card, send ? '>' : '<'); | 
 | 1443 |         	PUTBYTE_TO_STATUS(card, ':'); | 
 | 1444 | 	} else { | 
 | 1445 |         	PUTBYTE_TO_STATUS(card, 'D'); | 
 | 1446 |         	PUTBYTE_TO_STATUS(card, '3'); | 
 | 1447 |         	PUTBYTE_TO_STATUS(card, send ? '>' : '<'); | 
 | 1448 |         	PUTBYTE_TO_STATUS(card, ':'); | 
 | 1449 |     	} | 
 | 1450 |  | 
 | 1451 | 	for (p = data, end = data+len; p < end; p++) { | 
 | 1452 | 		u8 w; | 
 | 1453 | 		PUTBYTE_TO_STATUS(card, ' '); | 
 | 1454 | 		w = (*p >> 4) & 0xf; | 
 | 1455 | 		PUTBYTE_TO_STATUS(card, (w < 10) ? '0'+w : 'A'-10+w); | 
 | 1456 | 		w = *p & 0xf; | 
 | 1457 | 		PUTBYTE_TO_STATUS(card, (w < 10) ? '0'+w : 'A'-10+w); | 
 | 1458 | 	} | 
 | 1459 | 	PUTBYTE_TO_STATUS(card, '\n'); | 
 | 1460 |  | 
 | 1461 | 	cmd.command = ISDN_STAT_STAVAIL; | 
 | 1462 | 	cmd.driver = card->myid; | 
 | 1463 | 	cmd.arg = len*3+5; | 
 | 1464 | 	card->interface.statcallb(&cmd); | 
 | 1465 | } | 
 | 1466 |  | 
 | 1467 | /* ------------------------------------------------------------------- */ | 
 | 1468 |  | 
 | 1469 | static _cmsg cmdcmsg; | 
 | 1470 |  | 
 | 1471 | static int capidrv_ioctl(isdn_ctrl * c, capidrv_contr * card) | 
 | 1472 | { | 
 | 1473 | 	switch (c->arg) { | 
 | 1474 | 	case 1: | 
 | 1475 | 		debugmode = (int)(*((unsigned int *)c->parm.num)); | 
 | 1476 | 		printk(KERN_DEBUG "capidrv-%d: debugmode=%d\n", | 
 | 1477 | 				card->contrnr, debugmode); | 
 | 1478 | 		return 0; | 
 | 1479 | 	default: | 
 | 1480 | 		printk(KERN_DEBUG "capidrv-%d: capidrv_ioctl(%ld) called ??\n", | 
 | 1481 | 				card->contrnr, c->arg); | 
 | 1482 | 		return -EINVAL; | 
 | 1483 | 	} | 
 | 1484 | 	return -EINVAL; | 
 | 1485 | } | 
 | 1486 |  | 
 | 1487 | /* | 
 | 1488 |  * Handle leased lines (CAPI-Bundling) | 
 | 1489 |  */ | 
 | 1490 |  | 
 | 1491 | struct internal_bchannelinfo { | 
 | 1492 |    unsigned short channelalloc; | 
 | 1493 |    unsigned short operation; | 
 | 1494 |    unsigned char  cmask[31]; | 
 | 1495 | }; | 
 | 1496 |  | 
 | 1497 | static int decodeFVteln(char *teln, unsigned long *bmaskp, int *activep) | 
 | 1498 | { | 
 | 1499 | 	unsigned long bmask = 0; | 
 | 1500 | 	int active = !0; | 
 | 1501 | 	char *s; | 
 | 1502 | 	int i; | 
 | 1503 |  | 
 | 1504 | 	if (strncmp(teln, "FV:", 3) != 0) | 
 | 1505 | 		return 1; | 
 | 1506 | 	s = teln + 3; | 
 | 1507 | 	while (*s && *s == ' ') s++; | 
 | 1508 | 	if (!*s) return -2; | 
 | 1509 | 	if (*s == 'p' || *s == 'P') { | 
 | 1510 | 		active = 0; | 
 | 1511 | 		s++; | 
 | 1512 | 	} | 
 | 1513 | 	if (*s == 'a' || *s == 'A') { | 
 | 1514 | 		active = !0; | 
 | 1515 | 		s++; | 
 | 1516 | 	} | 
 | 1517 | 	while (*s) { | 
 | 1518 | 		int digit1 = 0; | 
 | 1519 | 		int digit2 = 0; | 
 | 1520 | 		if (!isdigit(*s)) return -3; | 
 | 1521 | 		while (isdigit(*s)) { digit1 = digit1*10 + (*s - '0'); s++; } | 
| Roel Kluin | fecc703 | 2009-01-04 16:22:04 -0800 | [diff] [blame] | 1522 | 		if (digit1 <= 0 || digit1 > 30) return -4; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1523 | 		if (*s == 0 || *s == ',' || *s == ' ') { | 
 | 1524 | 			bmask |= (1 << digit1); | 
 | 1525 | 			digit1 = 0; | 
 | 1526 | 			if (*s) s++; | 
 | 1527 | 			continue; | 
 | 1528 | 		} | 
 | 1529 | 		if (*s != '-') return -5; | 
 | 1530 | 		s++; | 
 | 1531 | 		if (!isdigit(*s)) return -3; | 
 | 1532 | 		while (isdigit(*s)) { digit2 = digit2*10 + (*s - '0'); s++; } | 
| Roel Kluin | fecc703 | 2009-01-04 16:22:04 -0800 | [diff] [blame] | 1533 | 		if (digit2 <= 0 || digit2 > 30) return -4; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1534 | 		if (*s == 0 || *s == ',' || *s == ' ') { | 
 | 1535 | 			if (digit1 > digit2) | 
 | 1536 | 				for (i = digit2; i <= digit1 ; i++) | 
 | 1537 | 					bmask |= (1 << i); | 
 | 1538 | 			else  | 
 | 1539 | 				for (i = digit1; i <= digit2 ; i++) | 
 | 1540 | 					bmask |= (1 << i); | 
 | 1541 | 			digit1 = digit2 = 0; | 
 | 1542 | 			if (*s) s++; | 
 | 1543 | 			continue; | 
 | 1544 | 		} | 
 | 1545 | 		return -6; | 
 | 1546 | 	} | 
 | 1547 | 	if (activep) *activep = active; | 
 | 1548 | 	if (bmaskp) *bmaskp = bmask; | 
 | 1549 | 	return 0; | 
 | 1550 | } | 
 | 1551 |  | 
 | 1552 | static int FVteln2capi20(char *teln, u8 AdditionalInfo[1+2+2+31]) | 
 | 1553 | { | 
 | 1554 | 	unsigned long bmask; | 
 | 1555 | 	int active; | 
 | 1556 | 	int rc, i; | 
 | 1557 |     | 
 | 1558 | 	rc = decodeFVteln(teln, &bmask, &active); | 
 | 1559 | 	if (rc) return rc; | 
 | 1560 | 	/* Length */ | 
 | 1561 | 	AdditionalInfo[0] = 2+2+31; | 
 | 1562 |         /* Channel: 3 => use channel allocation */ | 
 | 1563 |         AdditionalInfo[1] = 3; AdditionalInfo[2] = 0; | 
 | 1564 | 	/* Operation: 0 => DTE mode, 1 => DCE mode */ | 
 | 1565 |         if (active) { | 
 | 1566 |    		AdditionalInfo[3] = 0; AdditionalInfo[4] = 0; | 
 | 1567 |    	} else { | 
 | 1568 |    		AdditionalInfo[3] = 1; AdditionalInfo[4] = 0; | 
 | 1569 | 	} | 
 | 1570 | 	/* Channel mask array */ | 
 | 1571 | 	AdditionalInfo[5] = 0; /* no D-Channel */ | 
 | 1572 | 	for (i=1; i <= 30; i++) | 
 | 1573 | 		AdditionalInfo[5+i] = (bmask & (1 << i)) ? 0xff : 0; | 
 | 1574 | 	return 0; | 
 | 1575 | } | 
 | 1576 |  | 
 | 1577 | static int capidrv_command(isdn_ctrl * c, capidrv_contr * card) | 
 | 1578 | { | 
 | 1579 | 	isdn_ctrl cmd; | 
 | 1580 | 	struct capidrv_bchan *bchan; | 
 | 1581 | 	struct capidrv_plci *plcip; | 
 | 1582 | 	u8 AdditionalInfo[1+2+2+31]; | 
 | 1583 |         int rc, isleasedline = 0; | 
 | 1584 |  | 
 | 1585 | 	if (c->command == ISDN_CMD_IOCTL) | 
 | 1586 | 		return capidrv_ioctl(c, card); | 
 | 1587 |  | 
 | 1588 | 	switch (c->command) { | 
 | 1589 | 	case ISDN_CMD_DIAL:{ | 
 | 1590 | 			u8 calling[ISDN_MSNLEN + 3]; | 
 | 1591 | 			u8 called[ISDN_MSNLEN + 2]; | 
 | 1592 |  | 
 | 1593 | 			if (debugmode) | 
 | 1594 | 				printk(KERN_DEBUG "capidrv-%d: ISDN_CMD_DIAL(ch=%ld,\"%s,%d,%d,%s\")\n", | 
 | 1595 | 					card->contrnr, | 
 | 1596 | 					c->arg, | 
 | 1597 | 				        c->parm.setup.phone, | 
 | 1598 | 				        c->parm.setup.si1, | 
 | 1599 | 				        c->parm.setup.si2, | 
 | 1600 | 				        c->parm.setup.eazmsn); | 
 | 1601 |  | 
 | 1602 | 			bchan = &card->bchans[c->arg % card->nbchan]; | 
 | 1603 |  | 
 | 1604 | 			if (bchan->plcip) { | 
 | 1605 | 				printk(KERN_ERR "capidrv-%d: dail ch=%ld,\"%s,%d,%d,%s\" in use (plci=0x%x)\n", | 
 | 1606 | 					card->contrnr, | 
 | 1607 | 			        	c->arg,  | 
 | 1608 | 				        c->parm.setup.phone, | 
 | 1609 | 				        c->parm.setup.si1, | 
 | 1610 | 				        c->parm.setup.si2, | 
 | 1611 | 				        c->parm.setup.eazmsn, | 
 | 1612 | 				        bchan->plcip->plci); | 
 | 1613 | 				return 0; | 
 | 1614 | 			} | 
 | 1615 | 			bchan->si1 = c->parm.setup.si1; | 
 | 1616 | 			bchan->si2 = c->parm.setup.si2; | 
 | 1617 |  | 
 | 1618 | 			strncpy(bchan->num, c->parm.setup.phone, sizeof(bchan->num)); | 
 | 1619 | 			strncpy(bchan->mynum, c->parm.setup.eazmsn, sizeof(bchan->mynum)); | 
 | 1620 |                         rc = FVteln2capi20(bchan->num, AdditionalInfo); | 
 | 1621 | 			isleasedline = (rc == 0); | 
 | 1622 | 			if (rc < 0) | 
 | 1623 | 				printk(KERN_ERR "capidrv-%d: WARNING: invalid leased linedefinition \"%s\"\n", card->contrnr, bchan->num); | 
 | 1624 |  | 
 | 1625 | 			if (isleasedline) { | 
 | 1626 | 				calling[0] = 0; | 
 | 1627 | 				called[0] = 0; | 
 | 1628 | 			        if (debugmode) | 
 | 1629 | 					printk(KERN_DEBUG "capidrv-%d: connecting leased line\n", card->contrnr); | 
 | 1630 | 			} else { | 
 | 1631 | 		        	calling[0] = strlen(bchan->mynum) + 2; | 
 | 1632 | 		        	calling[1] = 0; | 
 | 1633 | 		     		calling[2] = 0x80; | 
 | 1634 | 			   	strncpy(calling + 3, bchan->mynum, ISDN_MSNLEN); | 
 | 1635 | 				called[0] = strlen(bchan->num) + 1; | 
 | 1636 | 				called[1] = 0x80; | 
 | 1637 | 				strncpy(called + 2, bchan->num, ISDN_MSNLEN); | 
 | 1638 | 			} | 
 | 1639 |  | 
 | 1640 | 			capi_fill_CONNECT_REQ(&cmdcmsg, | 
 | 1641 | 					      global.ap.applid, | 
 | 1642 | 					      card->msgid++, | 
 | 1643 | 					      card->contrnr,	/* adr */ | 
 | 1644 | 					  si2cip(bchan->si1, bchan->si2),	/* cipvalue */ | 
 | 1645 | 					      called,	/* CalledPartyNumber */ | 
 | 1646 | 					      calling,	/* CallingPartyNumber */ | 
 | 1647 | 					      NULL,	/* CalledPartySubaddress */ | 
 | 1648 | 					      NULL,	/* CallingPartySubaddress */ | 
 | 1649 | 					    b1prot(bchan->l2, bchan->l3),	/* B1protocol */ | 
 | 1650 | 					    b2prot(bchan->l2, bchan->l3),	/* B2protocol */ | 
 | 1651 | 					    b3prot(bchan->l2, bchan->l3),	/* B3protocol */ | 
 | 1652 | 					    b1config(bchan->l2, bchan->l3),	/* B1configuration */ | 
 | 1653 | 					      NULL,	/* B2configuration */ | 
 | 1654 | 					      NULL,	/* B3configuration */ | 
 | 1655 | 					      NULL,	/* BC */ | 
 | 1656 | 					      NULL,	/* LLC */ | 
 | 1657 | 					      NULL,	/* HLC */ | 
 | 1658 | 					      /* BChannelinformation */ | 
 | 1659 | 					      isleasedline ? AdditionalInfo : NULL, | 
 | 1660 | 					      NULL,	/* Keypadfacility */ | 
 | 1661 | 					      NULL,	/* Useruserdata */ | 
 | 1662 | 					      NULL	/* Facilitydataarray */ | 
 | 1663 | 			    ); | 
| Harvey Harrison | 2f9e9b6 | 2008-04-28 02:14:37 -0700 | [diff] [blame] | 1664 | 			if ((plcip = new_plci(card, (c->arg % card->nbchan))) == NULL) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1665 | 				cmd.command = ISDN_STAT_DHUP; | 
 | 1666 | 				cmd.driver = card->myid; | 
 | 1667 | 				cmd.arg = (c->arg % card->nbchan); | 
 | 1668 | 				card->interface.statcallb(&cmd); | 
 | 1669 | 				return -1; | 
 | 1670 | 			} | 
 | 1671 | 			plcip->msgid = cmdcmsg.Messagenumber; | 
 | 1672 | 			plcip->leasedline = isleasedline; | 
 | 1673 | 			plci_change_state(card, plcip, EV_PLCI_CONNECT_REQ); | 
 | 1674 | 			send_message(card, &cmdcmsg); | 
 | 1675 | 			return 0; | 
 | 1676 | 		} | 
 | 1677 |  | 
 | 1678 | 	case ISDN_CMD_ACCEPTD: | 
 | 1679 |  | 
 | 1680 | 		bchan = &card->bchans[c->arg % card->nbchan]; | 
 | 1681 | 		if (debugmode) | 
 | 1682 | 			printk(KERN_DEBUG "capidrv-%d: ISDN_CMD_ACCEPTD(ch=%ld) l2=%d l3=%d\n", | 
 | 1683 | 			       card->contrnr, | 
 | 1684 | 			       c->arg, bchan->l2, bchan->l3); | 
 | 1685 |  | 
 | 1686 | 		capi_fill_CONNECT_RESP(&cmdcmsg, | 
 | 1687 | 				       global.ap.applid, | 
 | 1688 | 				       card->msgid++, | 
 | 1689 | 				       bchan->plcip->plci,	/* adr */ | 
 | 1690 | 				       0,	/* Reject */ | 
 | 1691 | 				       b1prot(bchan->l2, bchan->l3),	/* B1protocol */ | 
 | 1692 | 				       b2prot(bchan->l2, bchan->l3),	/* B2protocol */ | 
 | 1693 | 				       b3prot(bchan->l2, bchan->l3),	/* B3protocol */ | 
 | 1694 | 				       b1config(bchan->l2, bchan->l3),	/* B1configuration */ | 
 | 1695 | 				       NULL,	/* B2configuration */ | 
 | 1696 | 				       NULL,	/* B3configuration */ | 
 | 1697 | 				       NULL,	/* ConnectedNumber */ | 
 | 1698 | 				       NULL,	/* ConnectedSubaddress */ | 
 | 1699 | 				       NULL,	/* LLC */ | 
 | 1700 | 				       NULL,	/* BChannelinformation */ | 
 | 1701 | 				       NULL,	/* Keypadfacility */ | 
 | 1702 | 				       NULL,	/* Useruserdata */ | 
 | 1703 | 				       NULL	/* Facilitydataarray */ | 
 | 1704 | 		); | 
 | 1705 | 		capi_cmsg2message(&cmdcmsg, cmdcmsg.buf); | 
 | 1706 | 		plci_change_state(card, bchan->plcip, EV_PLCI_CONNECT_RESP); | 
 | 1707 | 		send_message(card, &cmdcmsg); | 
 | 1708 | 		return 0; | 
 | 1709 |  | 
 | 1710 | 	case ISDN_CMD_ACCEPTB: | 
 | 1711 | 		if (debugmode) | 
 | 1712 | 			printk(KERN_DEBUG "capidrv-%d: ISDN_CMD_ACCEPTB(ch=%ld)\n", | 
 | 1713 | 			       card->contrnr, | 
 | 1714 | 			       c->arg); | 
 | 1715 | 		return -ENOSYS; | 
 | 1716 |  | 
 | 1717 | 	case ISDN_CMD_HANGUP: | 
 | 1718 | 		if (debugmode) | 
 | 1719 | 			printk(KERN_DEBUG "capidrv-%d: ISDN_CMD_HANGUP(ch=%ld)\n", | 
 | 1720 | 			       card->contrnr, | 
 | 1721 | 			       c->arg); | 
 | 1722 | 		bchan = &card->bchans[c->arg % card->nbchan]; | 
 | 1723 |  | 
 | 1724 | 		if (bchan->disconnecting) { | 
 | 1725 | 			if (debugmode) | 
 | 1726 | 				printk(KERN_DEBUG "capidrv-%d: chan %ld already disconnecting ...\n", | 
 | 1727 | 				       card->contrnr, | 
 | 1728 | 				       c->arg); | 
 | 1729 | 			return 0; | 
 | 1730 | 		} | 
 | 1731 | 		if (bchan->nccip) { | 
 | 1732 | 			bchan->disconnecting = 1; | 
 | 1733 | 			capi_fill_DISCONNECT_B3_REQ(&cmdcmsg, | 
 | 1734 | 						    global.ap.applid, | 
 | 1735 | 						    card->msgid++, | 
 | 1736 | 						    bchan->nccip->ncci, | 
 | 1737 | 						    NULL	/* NCPI */ | 
 | 1738 | 			); | 
 | 1739 | 			ncci_change_state(card, bchan->nccip, EV_NCCI_DISCONNECT_B3_REQ); | 
 | 1740 | 			send_message(card, &cmdcmsg); | 
 | 1741 | 			return 0; | 
 | 1742 | 		} else if (bchan->plcip) { | 
 | 1743 | 			if (bchan->plcip->state == ST_PLCI_INCOMING) { | 
 | 1744 | 				/* | 
 | 1745 | 				 * just ignore, we a called from | 
 | 1746 | 				 * isdn_status_callback(), | 
 | 1747 | 				 * which will return 0 or 2, this is handled | 
 | 1748 | 				 * by the CONNECT_IND handler | 
 | 1749 | 				 */ | 
 | 1750 | 				bchan->disconnecting = 1; | 
 | 1751 | 				return 0; | 
 | 1752 | 			} else if (bchan->plcip->plci) { | 
 | 1753 | 				bchan->disconnecting = 1; | 
 | 1754 | 				capi_fill_DISCONNECT_REQ(&cmdcmsg, | 
 | 1755 | 							 global.ap.applid, | 
 | 1756 | 							 card->msgid++, | 
 | 1757 | 						      bchan->plcip->plci, | 
 | 1758 | 							 NULL,	/* BChannelinformation */ | 
 | 1759 | 							 NULL,	/* Keypadfacility */ | 
 | 1760 | 							 NULL,	/* Useruserdata */ | 
 | 1761 | 							 NULL	/* Facilitydataarray */ | 
 | 1762 | 				); | 
 | 1763 | 				plci_change_state(card, bchan->plcip, EV_PLCI_DISCONNECT_REQ); | 
 | 1764 | 				send_message(card, &cmdcmsg); | 
 | 1765 | 				return 0; | 
 | 1766 | 			} else { | 
 | 1767 | 				printk(KERN_ERR "capidrv-%d: chan %ld disconnect request while waiting for CONNECT_CONF\n", | 
 | 1768 | 				       card->contrnr, | 
 | 1769 | 				       c->arg); | 
 | 1770 | 				return -EINVAL; | 
 | 1771 | 			} | 
 | 1772 | 		} | 
 | 1773 | 		printk(KERN_ERR "capidrv-%d: chan %ld disconnect request on free channel\n", | 
 | 1774 | 				       card->contrnr, | 
 | 1775 | 				       c->arg); | 
 | 1776 | 		return -EINVAL; | 
 | 1777 | /* ready */ | 
 | 1778 |  | 
 | 1779 | 	case ISDN_CMD_SETL2: | 
 | 1780 | 		if (debugmode) | 
 | 1781 | 			printk(KERN_DEBUG "capidrv-%d: set L2 on chan %ld to %ld\n", | 
 | 1782 | 			       card->contrnr, | 
 | 1783 | 			       (c->arg & 0xff), (c->arg >> 8)); | 
 | 1784 | 		bchan = &card->bchans[(c->arg & 0xff) % card->nbchan]; | 
 | 1785 | 		bchan->l2 = (c->arg >> 8); | 
 | 1786 | 		return 0; | 
 | 1787 |  | 
 | 1788 | 	case ISDN_CMD_SETL3: | 
 | 1789 | 		if (debugmode) | 
 | 1790 | 			printk(KERN_DEBUG "capidrv-%d: set L3 on chan %ld to %ld\n", | 
 | 1791 | 			       card->contrnr, | 
 | 1792 | 			       (c->arg & 0xff), (c->arg >> 8)); | 
 | 1793 | 		bchan = &card->bchans[(c->arg & 0xff) % card->nbchan]; | 
 | 1794 | 		bchan->l3 = (c->arg >> 8); | 
 | 1795 | 		return 0; | 
 | 1796 |  | 
 | 1797 | 	case ISDN_CMD_SETEAZ: | 
 | 1798 | 		if (debugmode) | 
 | 1799 | 			printk(KERN_DEBUG "capidrv-%d: set EAZ \"%s\" on chan %ld\n", | 
 | 1800 | 			       card->contrnr, | 
 | 1801 | 			       c->parm.num, c->arg); | 
 | 1802 | 		bchan = &card->bchans[c->arg % card->nbchan]; | 
 | 1803 | 		strncpy(bchan->msn, c->parm.num, ISDN_MSNLEN); | 
 | 1804 | 		return 0; | 
 | 1805 |  | 
 | 1806 | 	case ISDN_CMD_CLREAZ: | 
 | 1807 | 		if (debugmode) | 
 | 1808 | 			printk(KERN_DEBUG "capidrv-%d: clearing EAZ on chan %ld\n", | 
 | 1809 | 					card->contrnr, c->arg); | 
 | 1810 | 		bchan = &card->bchans[c->arg % card->nbchan]; | 
 | 1811 | 		bchan->msn[0] = 0; | 
 | 1812 | 		return 0; | 
 | 1813 |  | 
 | 1814 | 	default: | 
 | 1815 | 		printk(KERN_ERR "capidrv-%d: ISDN_CMD_%d, Huh?\n", | 
 | 1816 | 					card->contrnr, c->command); | 
 | 1817 | 		return -EINVAL; | 
 | 1818 | 	} | 
 | 1819 | 	return 0; | 
 | 1820 | } | 
 | 1821 |  | 
 | 1822 | static int if_command(isdn_ctrl * c) | 
 | 1823 | { | 
 | 1824 | 	capidrv_contr *card = findcontrbydriverid(c->driver); | 
 | 1825 |  | 
 | 1826 | 	if (card) | 
 | 1827 | 		return capidrv_command(c, card); | 
 | 1828 |  | 
 | 1829 | 	printk(KERN_ERR | 
 | 1830 | 	     "capidrv: if_command %d called with invalid driverId %d!\n", | 
 | 1831 | 						c->command, c->driver); | 
 | 1832 | 	return -ENODEV; | 
 | 1833 | } | 
 | 1834 |  | 
 | 1835 | static _cmsg sendcmsg; | 
 | 1836 |  | 
 | 1837 | static int if_sendbuf(int id, int channel, int doack, struct sk_buff *skb) | 
 | 1838 | { | 
 | 1839 | 	capidrv_contr *card = findcontrbydriverid(id); | 
 | 1840 | 	capidrv_bchan *bchan; | 
 | 1841 | 	capidrv_ncci *nccip; | 
 | 1842 | 	int len = skb->len; | 
 | 1843 | 	int msglen; | 
 | 1844 | 	u16 errcode; | 
 | 1845 | 	u16 datahandle; | 
| Jeff Garzik | b393243 | 2007-10-25 23:02:14 -0400 | [diff] [blame] | 1846 | 	u32 data; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1847 |  | 
 | 1848 | 	if (!card) { | 
 | 1849 | 		printk(KERN_ERR "capidrv: if_sendbuf called with invalid driverId %d!\n", | 
 | 1850 | 		       id); | 
 | 1851 | 		return 0; | 
 | 1852 | 	} | 
 | 1853 | 	if (debugmode > 4) | 
 | 1854 | 		printk(KERN_DEBUG "capidrv-%d: sendbuf len=%d skb=%p doack=%d\n", | 
 | 1855 | 					card->contrnr, len, skb, doack); | 
 | 1856 | 	bchan = &card->bchans[channel % card->nbchan]; | 
 | 1857 | 	nccip = bchan->nccip; | 
 | 1858 | 	if (!nccip || nccip->state != ST_NCCI_ACTIVE) { | 
 | 1859 | 		printk(KERN_ERR "capidrv-%d: if_sendbuf: %s:%d: chan not up!\n", | 
 | 1860 | 		       card->contrnr, card->name, channel); | 
 | 1861 | 		return 0; | 
 | 1862 | 	} | 
 | 1863 | 	datahandle = nccip->datahandle; | 
| Jeff Garzik | b393243 | 2007-10-25 23:02:14 -0400 | [diff] [blame] | 1864 |  | 
 | 1865 | 	/* | 
 | 1866 | 	 * Here we copy pointer skb->data into the 32-bit 'Data' field. | 
 | 1867 | 	 * The 'Data' field is not used in practice in linux kernel | 
 | 1868 | 	 * (neither in 32 or 64 bit), but should have some value, | 
 | 1869 | 	 * since a CAPI message trace will display it. | 
 | 1870 | 	 * | 
 | 1871 | 	 * The correct value in the 32 bit case is the address of the | 
 | 1872 | 	 * data, in 64 bit it makes no sense, we use 0 there. | 
 | 1873 | 	 */ | 
 | 1874 |  | 
 | 1875 | #ifdef CONFIG_64BIT | 
 | 1876 | 	data = 0; | 
 | 1877 | #else | 
 | 1878 | 	data = (unsigned long) skb->data; | 
 | 1879 | #endif | 
 | 1880 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1881 | 	capi_fill_DATA_B3_REQ(&sendcmsg, global.ap.applid, card->msgid++, | 
 | 1882 | 			      nccip->ncci,	/* adr */ | 
| Jeff Garzik | b393243 | 2007-10-25 23:02:14 -0400 | [diff] [blame] | 1883 | 			      data,		/* Data */ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1884 | 			      skb->len,		/* DataLength */ | 
 | 1885 | 			      datahandle,	/* DataHandle */ | 
 | 1886 | 			      0	/* Flags */ | 
 | 1887 | 	    ); | 
 | 1888 |  | 
 | 1889 | 	if (capidrv_add_ack(nccip, datahandle, doack ? (int)skb->len : -1) < 0) | 
 | 1890 | 	   return 0; | 
 | 1891 |  | 
 | 1892 | 	capi_cmsg2message(&sendcmsg, sendcmsg.buf); | 
 | 1893 | 	msglen = CAPIMSG_LEN(sendcmsg.buf); | 
 | 1894 | 	if (skb_headroom(skb) < msglen) { | 
 | 1895 | 		struct sk_buff *nskb = skb_realloc_headroom(skb, msglen); | 
 | 1896 | 		if (!nskb) { | 
 | 1897 | 			printk(KERN_ERR "capidrv-%d: if_sendbuf: no memory\n", | 
 | 1898 | 				card->contrnr); | 
 | 1899 | 		        (void)capidrv_del_ack(nccip, datahandle); | 
 | 1900 | 			return 0; | 
 | 1901 | 		} | 
 | 1902 | 		printk(KERN_DEBUG "capidrv-%d: only %d bytes headroom, need %d\n", | 
 | 1903 | 		       card->contrnr, skb_headroom(skb), msglen); | 
 | 1904 | 		memcpy(skb_push(nskb, msglen), sendcmsg.buf, msglen); | 
 | 1905 | 		errcode = capi20_put_message(&global.ap, nskb); | 
 | 1906 | 		if (errcode == CAPI_NOERROR) { | 
 | 1907 | 			dev_kfree_skb(skb); | 
 | 1908 | 			nccip->datahandle++; | 
 | 1909 | 			return len; | 
 | 1910 | 		} | 
 | 1911 | 		if (debugmode > 3) | 
 | 1912 | 			printk(KERN_DEBUG "capidrv-%d: sendbuf putmsg ret(%x) - %s\n", | 
 | 1913 | 				card->contrnr, errcode, capi_info2str(errcode)); | 
 | 1914 | 	        (void)capidrv_del_ack(nccip, datahandle); | 
 | 1915 | 	        dev_kfree_skb(nskb); | 
 | 1916 | 		return errcode == CAPI_SENDQUEUEFULL ? 0 : -1; | 
 | 1917 | 	} else { | 
 | 1918 | 		memcpy(skb_push(skb, msglen), sendcmsg.buf, msglen); | 
 | 1919 | 		errcode = capi20_put_message(&global.ap, skb); | 
 | 1920 | 		if (errcode == CAPI_NOERROR) { | 
 | 1921 | 			nccip->datahandle++; | 
 | 1922 | 			return len; | 
 | 1923 | 		} | 
 | 1924 | 		if (debugmode > 3) | 
 | 1925 | 			printk(KERN_DEBUG "capidrv-%d: sendbuf putmsg ret(%x) - %s\n", | 
 | 1926 | 				card->contrnr, errcode, capi_info2str(errcode)); | 
 | 1927 | 		skb_pull(skb, msglen); | 
 | 1928 | 	        (void)capidrv_del_ack(nccip, datahandle); | 
 | 1929 | 		return errcode == CAPI_SENDQUEUEFULL ? 0 : -1; | 
 | 1930 | 	} | 
 | 1931 | } | 
 | 1932 |  | 
 | 1933 | static int if_readstat(u8 __user *buf, int len, int id, int channel) | 
 | 1934 | { | 
 | 1935 | 	capidrv_contr *card = findcontrbydriverid(id); | 
 | 1936 | 	int count; | 
 | 1937 | 	u8 __user *p; | 
 | 1938 |  | 
 | 1939 | 	if (!card) { | 
 | 1940 | 		printk(KERN_ERR "capidrv: if_readstat called with invalid driverId %d!\n", | 
 | 1941 | 		       id); | 
 | 1942 | 		return -ENODEV; | 
 | 1943 | 	} | 
 | 1944 |  | 
 | 1945 | 	for (p=buf, count=0; count < len; p++, count++) { | 
| Jeff Garzik | 7786ce1 | 2006-10-17 00:10:40 -0700 | [diff] [blame] | 1946 | 		if (put_user(*card->q931_read++, p)) | 
 | 1947 | 			return -EFAULT; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1948 | 	        if (card->q931_read > card->q931_end) | 
 | 1949 | 	                card->q931_read = card->q931_buf; | 
 | 1950 | 	} | 
 | 1951 | 	return count; | 
 | 1952 |  | 
 | 1953 | } | 
 | 1954 |  | 
 | 1955 | static void enable_dchannel_trace(capidrv_contr *card) | 
 | 1956 | { | 
 | 1957 |         u8 manufacturer[CAPI_MANUFACTURER_LEN]; | 
 | 1958 |         capi_version version; | 
 | 1959 | 	u16 contr = card->contrnr; | 
 | 1960 | 	u16 errcode; | 
 | 1961 | 	u16 avmversion[3]; | 
 | 1962 |  | 
 | 1963 |         errcode = capi20_get_manufacturer(contr, manufacturer); | 
 | 1964 |         if (errcode != CAPI_NOERROR) { | 
 | 1965 | 	   printk(KERN_ERR "%s: can't get manufacturer (0x%x)\n", | 
 | 1966 | 			card->name, errcode); | 
 | 1967 | 	   return; | 
 | 1968 | 	} | 
| Harvey Harrison | 2f9e9b6 | 2008-04-28 02:14:37 -0700 | [diff] [blame] | 1969 | 	if (strstr(manufacturer, "AVM") == NULL) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1970 | 	   printk(KERN_ERR "%s: not from AVM, no d-channel trace possible (%s)\n", | 
 | 1971 | 			card->name, manufacturer); | 
 | 1972 | 	   return; | 
 | 1973 | 	} | 
 | 1974 |         errcode = capi20_get_version(contr, &version); | 
 | 1975 |         if (errcode != CAPI_NOERROR) { | 
 | 1976 | 	   printk(KERN_ERR "%s: can't get version (0x%x)\n", | 
 | 1977 | 			card->name, errcode); | 
 | 1978 | 	   return; | 
 | 1979 | 	} | 
 | 1980 | 	avmversion[0] = (version.majormanuversion >> 4) & 0x0f; | 
 | 1981 | 	avmversion[1] = (version.majormanuversion << 4) & 0xf0; | 
 | 1982 | 	avmversion[1] |= (version.minormanuversion >> 4) & 0x0f; | 
 | 1983 | 	avmversion[2] |= version.minormanuversion & 0x0f; | 
 | 1984 |  | 
 | 1985 |         if (avmversion[0] > 3 || (avmversion[0] == 3 && avmversion[1] > 5)) { | 
 | 1986 | 		printk(KERN_INFO "%s: D2 trace enabled\n", card->name); | 
 | 1987 | 		capi_fill_MANUFACTURER_REQ(&cmdcmsg, global.ap.applid, | 
 | 1988 | 					   card->msgid++, | 
 | 1989 | 					   contr, | 
 | 1990 | 					   0x214D5641,  /* ManuID */ | 
 | 1991 | 					   0,           /* Class */ | 
 | 1992 | 					   1,           /* Function */ | 
 | 1993 | 					   (_cstruct)"\004\200\014\000\000"); | 
 | 1994 | 	} else { | 
 | 1995 | 		printk(KERN_INFO "%s: D3 trace enabled\n", card->name); | 
 | 1996 | 		capi_fill_MANUFACTURER_REQ(&cmdcmsg, global.ap.applid, | 
 | 1997 | 					   card->msgid++, | 
 | 1998 | 					   contr, | 
 | 1999 | 					   0x214D5641,  /* ManuID */ | 
 | 2000 | 					   0,           /* Class */ | 
 | 2001 | 					   1,           /* Function */ | 
 | 2002 | 					   (_cstruct)"\004\002\003\000\000"); | 
 | 2003 | 	} | 
 | 2004 | 	send_message(card, &cmdcmsg); | 
 | 2005 | } | 
 | 2006 |  | 
 | 2007 |  | 
 | 2008 | static void send_listen(capidrv_contr *card) | 
 | 2009 | { | 
 | 2010 | 	capi_fill_LISTEN_REQ(&cmdcmsg, global.ap.applid, | 
 | 2011 | 			     card->msgid++, | 
 | 2012 | 			     card->contrnr, /* controller */ | 
 | 2013 | 			     1 << 6,	/* Infomask */ | 
 | 2014 | 			     card->cipmask, | 
 | 2015 | 			     card->cipmask2, | 
 | 2016 | 			     NULL, NULL); | 
 | 2017 | 	send_message(card, &cmdcmsg); | 
 | 2018 | 	listen_change_state(card, EV_LISTEN_REQ); | 
 | 2019 | } | 
 | 2020 |  | 
 | 2021 | static void listentimerfunc(unsigned long x) | 
 | 2022 | { | 
 | 2023 | 	capidrv_contr *card = (capidrv_contr *)x; | 
 | 2024 | 	if (card->state != ST_LISTEN_NONE && card->state != ST_LISTEN_ACTIVE) | 
 | 2025 | 		printk(KERN_ERR "%s: controller dead ??\n", card->name); | 
 | 2026 |         send_listen(card); | 
 | 2027 | 	mod_timer(&card->listentimer, jiffies + 60*HZ); | 
 | 2028 | } | 
 | 2029 |  | 
 | 2030 |  | 
 | 2031 | static int capidrv_addcontr(u16 contr, struct capi_profile *profp) | 
 | 2032 | { | 
 | 2033 | 	capidrv_contr *card; | 
 | 2034 | 	unsigned long flags; | 
 | 2035 | 	isdn_ctrl cmd; | 
 | 2036 | 	char id[20]; | 
 | 2037 | 	int i; | 
 | 2038 |  | 
 | 2039 | 	sprintf(id, "capidrv-%d", contr); | 
 | 2040 | 	if (!try_module_get(THIS_MODULE)) { | 
 | 2041 | 		printk(KERN_WARNING "capidrv: (%s) Could not reserve module\n", id); | 
 | 2042 | 		return -1; | 
 | 2043 | 	} | 
| Burman Yan | 41f9693 | 2006-12-08 02:39:35 -0800 | [diff] [blame] | 2044 | 	if (!(card = kzalloc(sizeof(capidrv_contr), GFP_ATOMIC))) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2045 | 		printk(KERN_WARNING | 
 | 2046 | 		 "capidrv: (%s) Could not allocate contr-struct.\n", id); | 
 | 2047 | 		return -1; | 
 | 2048 | 	} | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2049 | 	card->owner = THIS_MODULE; | 
 | 2050 | 	init_timer(&card->listentimer); | 
 | 2051 | 	strcpy(card->name, id); | 
 | 2052 | 	card->contrnr = contr; | 
 | 2053 | 	card->nbchan = profp->nbchannel; | 
| Robert P. J. Day | 5cbded5 | 2006-12-13 00:35:56 -0800 | [diff] [blame] | 2054 | 	card->bchans = kmalloc(sizeof(capidrv_bchan) * card->nbchan, GFP_ATOMIC); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2055 | 	if (!card->bchans) { | 
 | 2056 | 		printk(KERN_WARNING | 
 | 2057 | 		"capidrv: (%s) Could not allocate bchan-structs.\n", id); | 
 | 2058 | 		module_put(card->owner); | 
 | 2059 | 		kfree(card); | 
 | 2060 | 		return -1; | 
 | 2061 | 	} | 
 | 2062 | 	card->interface.channels = profp->nbchannel; | 
 | 2063 | 	card->interface.maxbufsize = 2048; | 
 | 2064 | 	card->interface.command = if_command; | 
 | 2065 | 	card->interface.writebuf_skb = if_sendbuf; | 
 | 2066 | 	card->interface.writecmd = NULL; | 
 | 2067 | 	card->interface.readstat = if_readstat; | 
 | 2068 | 	card->interface.features = ISDN_FEATURE_L2_HDLC | | 
 | 2069 | 	    			   ISDN_FEATURE_L2_TRANS | | 
 | 2070 | 	    			   ISDN_FEATURE_L3_TRANS | | 
 | 2071 | 				   ISDN_FEATURE_P_UNKNOWN | | 
 | 2072 | 				   ISDN_FEATURE_L2_X75I | | 
 | 2073 | 				   ISDN_FEATURE_L2_X75UI | | 
 | 2074 | 				   ISDN_FEATURE_L2_X75BUI; | 
 | 2075 | 	if (profp->support1 & (1<<2)) | 
 | 2076 | 		card->interface.features |= ISDN_FEATURE_L2_V11096 | | 
 | 2077 | 	    				    ISDN_FEATURE_L2_V11019 | | 
 | 2078 | 	    				    ISDN_FEATURE_L2_V11038; | 
 | 2079 | 	if (profp->support1 & (1<<8)) | 
 | 2080 | 		card->interface.features |= ISDN_FEATURE_L2_MODEM; | 
 | 2081 | 	card->interface.hl_hdrlen = 22; /* len of DATA_B3_REQ */ | 
 | 2082 | 	strncpy(card->interface.id, id, sizeof(card->interface.id) - 1); | 
 | 2083 |  | 
 | 2084 |  | 
 | 2085 | 	card->q931_read = card->q931_buf; | 
 | 2086 | 	card->q931_write = card->q931_buf; | 
 | 2087 | 	card->q931_end = card->q931_buf + sizeof(card->q931_buf) - 1; | 
 | 2088 |  | 
 | 2089 | 	if (!register_isdn(&card->interface)) { | 
 | 2090 | 		printk(KERN_ERR "capidrv: Unable to register contr %s\n", id); | 
 | 2091 | 		kfree(card->bchans); | 
 | 2092 | 		module_put(card->owner); | 
 | 2093 | 		kfree(card); | 
 | 2094 | 		return -1; | 
 | 2095 | 	} | 
 | 2096 | 	card->myid = card->interface.channels; | 
 | 2097 | 	memset(card->bchans, 0, sizeof(capidrv_bchan) * card->nbchan); | 
 | 2098 | 	for (i = 0; i < card->nbchan; i++) { | 
 | 2099 | 		card->bchans[i].contr = card; | 
 | 2100 | 	} | 
 | 2101 |  | 
 | 2102 | 	spin_lock_irqsave(&global_lock, flags); | 
 | 2103 | 	card->next = global.contr_list; | 
 | 2104 | 	global.contr_list = card; | 
 | 2105 | 	global.ncontr++; | 
 | 2106 | 	spin_unlock_irqrestore(&global_lock, flags); | 
 | 2107 |  | 
 | 2108 | 	cmd.command = ISDN_STAT_RUN; | 
 | 2109 | 	cmd.driver = card->myid; | 
 | 2110 | 	card->interface.statcallb(&cmd); | 
 | 2111 |  | 
 | 2112 | 	card->cipmask = 0x1FFF03FF;	/* any */ | 
 | 2113 | 	card->cipmask2 = 0; | 
 | 2114 |  | 
 | 2115 | 	card->listentimer.data = (unsigned long)card; | 
 | 2116 | 	card->listentimer.function = listentimerfunc; | 
 | 2117 | 	send_listen(card); | 
 | 2118 | 	mod_timer(&card->listentimer, jiffies + 60*HZ); | 
 | 2119 |  | 
 | 2120 | 	printk(KERN_INFO "%s: now up (%d B channels)\n", | 
 | 2121 | 		card->name, card->nbchan); | 
 | 2122 |  | 
 | 2123 | 	enable_dchannel_trace(card); | 
 | 2124 |  | 
 | 2125 | 	return 0; | 
 | 2126 | } | 
 | 2127 |  | 
 | 2128 | static int capidrv_delcontr(u16 contr) | 
 | 2129 | { | 
 | 2130 | 	capidrv_contr **pp, *card; | 
 | 2131 | 	unsigned long flags; | 
 | 2132 | 	isdn_ctrl cmd; | 
 | 2133 |  | 
 | 2134 | 	spin_lock_irqsave(&global_lock, flags); | 
 | 2135 | 	for (card = global.contr_list; card; card = card->next) { | 
 | 2136 | 		if (card->contrnr == contr) | 
 | 2137 | 			break; | 
 | 2138 | 	} | 
 | 2139 | 	if (!card) { | 
 | 2140 | 		spin_unlock_irqrestore(&global_lock, flags); | 
 | 2141 | 		printk(KERN_ERR "capidrv: delcontr: no contr %u\n", contr); | 
 | 2142 | 		return -1; | 
 | 2143 | 	} | 
| Jeff Garzik | b393243 | 2007-10-25 23:02:14 -0400 | [diff] [blame] | 2144 |  | 
 | 2145 | 	/* FIXME: maybe a race condition the card should be removed | 
 | 2146 | 	 * here from global list /kkeil | 
 | 2147 | 	 */ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2148 | 	spin_unlock_irqrestore(&global_lock, flags); | 
 | 2149 |  | 
 | 2150 | 	del_timer(&card->listentimer); | 
 | 2151 |  | 
 | 2152 | 	if (debugmode) | 
 | 2153 | 		printk(KERN_DEBUG "capidrv-%d: id=%d unloading\n", | 
 | 2154 | 					card->contrnr, card->myid); | 
 | 2155 |  | 
 | 2156 | 	cmd.command = ISDN_STAT_STOP; | 
 | 2157 | 	cmd.driver = card->myid; | 
 | 2158 | 	card->interface.statcallb(&cmd); | 
 | 2159 |  | 
 | 2160 | 	while (card->nbchan) { | 
 | 2161 |  | 
 | 2162 | 		cmd.command = ISDN_STAT_DISCH; | 
 | 2163 | 		cmd.driver = card->myid; | 
 | 2164 | 		cmd.arg = card->nbchan-1; | 
 | 2165 | 	        cmd.parm.num[0] = 0; | 
 | 2166 | 		if (debugmode) | 
 | 2167 | 			printk(KERN_DEBUG "capidrv-%d: id=%d disable chan=%ld\n", | 
 | 2168 | 					card->contrnr, card->myid, cmd.arg); | 
 | 2169 | 		card->interface.statcallb(&cmd); | 
 | 2170 |  | 
 | 2171 | 		if (card->bchans[card->nbchan-1].nccip) | 
 | 2172 | 			free_ncci(card, card->bchans[card->nbchan-1].nccip); | 
 | 2173 | 		if (card->bchans[card->nbchan-1].plcip) | 
 | 2174 | 			free_plci(card, card->bchans[card->nbchan-1].plcip); | 
 | 2175 | 		if (card->plci_list) | 
 | 2176 | 			printk(KERN_ERR "capidrv: bug in free_plci()\n"); | 
 | 2177 | 		card->nbchan--; | 
 | 2178 | 	} | 
 | 2179 | 	kfree(card->bchans); | 
 | 2180 | 	card->bchans = NULL; | 
 | 2181 |  | 
 | 2182 | 	if (debugmode) | 
 | 2183 | 		printk(KERN_DEBUG "capidrv-%d: id=%d isdn unload\n", | 
 | 2184 | 					card->contrnr, card->myid); | 
 | 2185 |  | 
 | 2186 | 	cmd.command = ISDN_STAT_UNLOAD; | 
 | 2187 | 	cmd.driver = card->myid; | 
 | 2188 | 	card->interface.statcallb(&cmd); | 
 | 2189 |  | 
 | 2190 | 	if (debugmode) | 
 | 2191 | 		printk(KERN_DEBUG "capidrv-%d: id=%d remove contr from list\n", | 
 | 2192 | 					card->contrnr, card->myid); | 
 | 2193 |  | 
 | 2194 | 	spin_lock_irqsave(&global_lock, flags); | 
 | 2195 | 	for (pp = &global.contr_list; *pp; pp = &(*pp)->next) { | 
 | 2196 | 		if (*pp == card) { | 
 | 2197 | 			*pp = (*pp)->next; | 
 | 2198 | 			card->next = NULL; | 
 | 2199 | 			global.ncontr--; | 
 | 2200 | 			break; | 
 | 2201 | 		} | 
 | 2202 | 	} | 
 | 2203 | 	spin_unlock_irqrestore(&global_lock, flags); | 
 | 2204 |  | 
 | 2205 | 	module_put(card->owner); | 
 | 2206 | 	printk(KERN_INFO "%s: now down.\n", card->name); | 
 | 2207 | 	kfree(card); | 
 | 2208 | 	return 0; | 
 | 2209 | } | 
 | 2210 |  | 
 | 2211 |  | 
 | 2212 | static void lower_callback(unsigned int cmd, u32 contr, void *data) | 
 | 2213 | { | 
 | 2214 |  | 
 | 2215 | 	switch (cmd) { | 
 | 2216 | 	case KCI_CONTRUP: | 
 | 2217 | 		printk(KERN_INFO "capidrv: controller %hu up\n", contr); | 
 | 2218 | 		(void) capidrv_addcontr(contr, (capi_profile *) data); | 
 | 2219 | 		break; | 
 | 2220 | 	case KCI_CONTRDOWN: | 
 | 2221 | 		printk(KERN_INFO "capidrv: controller %hu down\n", contr); | 
 | 2222 | 		(void) capidrv_delcontr(contr); | 
 | 2223 | 		break; | 
 | 2224 | 	} | 
 | 2225 | } | 
 | 2226 |  | 
 | 2227 | /* | 
 | 2228 |  * /proc/capi/capidrv: | 
 | 2229 |  * nrecvctlpkt nrecvdatapkt nsendctlpkt nsenddatapkt | 
 | 2230 |  */ | 
 | 2231 | static int proc_capidrv_read_proc(char *page, char **start, off_t off, | 
 | 2232 |                                        int count, int *eof, void *data) | 
 | 2233 | { | 
 | 2234 | 	int len = 0; | 
 | 2235 |  | 
 | 2236 | 	len += sprintf(page+len, "%lu %lu %lu %lu\n", | 
 | 2237 | 			global.ap.nrecvctlpkt, | 
 | 2238 | 			global.ap.nrecvdatapkt, | 
 | 2239 | 			global.ap.nsentctlpkt, | 
 | 2240 | 			global.ap.nsentdatapkt); | 
 | 2241 | 	if (off+count >= len) | 
 | 2242 | 	   *eof = 1; | 
 | 2243 | 	if (len < off) | 
 | 2244 |            return 0; | 
 | 2245 | 	*start = page + off; | 
 | 2246 | 	return ((count < len-off) ? count : len-off); | 
 | 2247 | } | 
 | 2248 |  | 
 | 2249 | static struct procfsentries { | 
 | 2250 |   char *name; | 
 | 2251 |   mode_t mode; | 
 | 2252 |   int (*read_proc)(char *page, char **start, off_t off, | 
 | 2253 |                                        int count, int *eof, void *data); | 
 | 2254 |   struct proc_dir_entry *procent; | 
 | 2255 | } procfsentries[] = { | 
 | 2256 |    /* { "capi",		  S_IFDIR, 0 }, */ | 
 | 2257 |    { "capi/capidrv", 	  0	 , proc_capidrv_read_proc }, | 
 | 2258 | }; | 
 | 2259 |  | 
 | 2260 | static void __init proc_init(void) | 
 | 2261 | { | 
| Ahmed S. Darwish | fd863db | 2007-02-12 00:53:19 -0800 | [diff] [blame] | 2262 |     int nelem = ARRAY_SIZE(procfsentries); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2263 |     int i; | 
 | 2264 |  | 
 | 2265 |     for (i=0; i < nelem; i++) { | 
 | 2266 |         struct procfsentries *p = procfsentries + i; | 
 | 2267 | 	p->procent = create_proc_entry(p->name, p->mode, NULL); | 
 | 2268 | 	if (p->procent) p->procent->read_proc = p->read_proc; | 
 | 2269 |     } | 
 | 2270 | } | 
 | 2271 |  | 
 | 2272 | static void __exit proc_exit(void) | 
 | 2273 | { | 
| Ahmed S. Darwish | fd863db | 2007-02-12 00:53:19 -0800 | [diff] [blame] | 2274 |     int nelem = ARRAY_SIZE(procfsentries); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2275 |     int i; | 
 | 2276 |  | 
 | 2277 |     for (i=nelem-1; i >= 0; i--) { | 
 | 2278 |         struct procfsentries *p = procfsentries + i; | 
 | 2279 | 	if (p->procent) { | 
 | 2280 | 	   remove_proc_entry(p->name, NULL); | 
 | 2281 | 	   p->procent = NULL; | 
 | 2282 | 	} | 
 | 2283 |     } | 
 | 2284 | } | 
 | 2285 |  | 
 | 2286 | static int __init capidrv_init(void) | 
 | 2287 | { | 
 | 2288 | 	capi_profile profile; | 
 | 2289 | 	char rev[32]; | 
 | 2290 | 	char *p; | 
 | 2291 | 	u32 ncontr, contr; | 
 | 2292 | 	u16 errcode; | 
 | 2293 |  | 
| Harvey Harrison | 2f9e9b6 | 2008-04-28 02:14:37 -0700 | [diff] [blame] | 2294 | 	if ((p = strchr(revision, ':')) != NULL && p[1]) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2295 | 		strncpy(rev, p + 2, sizeof(rev)); | 
 | 2296 | 		rev[sizeof(rev)-1] = 0; | 
| Harvey Harrison | 2f9e9b6 | 2008-04-28 02:14:37 -0700 | [diff] [blame] | 2297 | 		if ((p = strchr(rev, '$')) != NULL && p > rev) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2298 | 		   *(p-1) = 0; | 
 | 2299 | 	} else | 
 | 2300 | 		strcpy(rev, "1.0"); | 
 | 2301 |  | 
 | 2302 | 	global.ap.rparam.level3cnt = -2;  /* number of bchannels twice */ | 
 | 2303 | 	global.ap.rparam.datablkcnt = 16; | 
 | 2304 | 	global.ap.rparam.datablklen = 2048; | 
 | 2305 |  | 
 | 2306 | 	global.ap.recv_message = capidrv_recv_message; | 
 | 2307 | 	errcode = capi20_register(&global.ap); | 
 | 2308 | 	if (errcode) { | 
 | 2309 | 		return -EIO; | 
 | 2310 | 	} | 
 | 2311 |  | 
 | 2312 | 	capi20_set_callback(&global.ap, lower_callback); | 
 | 2313 |  | 
 | 2314 | 	errcode = capi20_get_profile(0, &profile); | 
 | 2315 | 	if (errcode != CAPI_NOERROR) { | 
 | 2316 | 		capi20_release(&global.ap); | 
 | 2317 | 		return -EIO; | 
 | 2318 | 	} | 
 | 2319 |  | 
 | 2320 | 	ncontr = profile.ncontroller; | 
 | 2321 | 	for (contr = 1; contr <= ncontr; contr++) { | 
 | 2322 | 		errcode = capi20_get_profile(contr, &profile); | 
 | 2323 | 		if (errcode != CAPI_NOERROR) | 
 | 2324 | 			continue; | 
 | 2325 | 		(void) capidrv_addcontr(contr, &profile); | 
 | 2326 | 	} | 
 | 2327 | 	proc_init(); | 
 | 2328 |  | 
 | 2329 | 	printk(KERN_NOTICE "capidrv: Rev %s: loaded\n", rev); | 
 | 2330 | 	return 0; | 
 | 2331 | } | 
 | 2332 |  | 
 | 2333 | static void __exit capidrv_exit(void) | 
 | 2334 | { | 
| Karsten Keil | eb36f4f | 2008-01-25 11:55:28 +0100 | [diff] [blame] | 2335 | 	char rev[32]; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2336 | 	char *p; | 
 | 2337 |  | 
| Harvey Harrison | 2f9e9b6 | 2008-04-28 02:14:37 -0700 | [diff] [blame] | 2338 | 	if ((p = strchr(revision, ':')) != NULL) { | 
| Karsten Keil | eb36f4f | 2008-01-25 11:55:28 +0100 | [diff] [blame] | 2339 | 		strncpy(rev, p + 1, sizeof(rev)); | 
 | 2340 | 		rev[sizeof(rev)-1] = 0; | 
| Harvey Harrison | 2f9e9b6 | 2008-04-28 02:14:37 -0700 | [diff] [blame] | 2341 | 		if ((p = strchr(rev, '$')) != NULL) | 
| Karsten Keil | eb36f4f | 2008-01-25 11:55:28 +0100 | [diff] [blame] | 2342 | 			*p = 0; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2343 | 	} else { | 
 | 2344 | 		strcpy(rev, " ??? "); | 
 | 2345 | 	} | 
 | 2346 |  | 
 | 2347 | 	capi20_release(&global.ap); | 
 | 2348 |  | 
 | 2349 | 	proc_exit(); | 
 | 2350 |  | 
 | 2351 | 	printk(KERN_NOTICE "capidrv: Rev%s: unloaded\n", rev); | 
 | 2352 | } | 
 | 2353 |  | 
 | 2354 | module_init(capidrv_init); | 
 | 2355 | module_exit(capidrv_exit); |