summaryrefslogtreecommitdiffstats
path: root/loop.c
blob: cbd82e5e2ccbe8013f278c197683e6967ffb674d (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
/*****************************************************************************\
**                                                                           **
** LCR                                                                       **
**                                                                           **
**---------------------------------------------------------------------------**
** Copyright: Andreas Eversberg                                              **
**                                                                           **
** loopback interface functions                                              **
**                                                                           **
\*****************************************************************************/ 

#include "main.h"

struct mISDNloop mISDNloop = { -1, 0 };

void mISDNloop_close(void)
{
	if (mISDNloop.sock > -1)
		close(mISDNloop.sock);
	mISDNloop.sock = -1;
}

int mISDNloop_open()
{
	int ret;
	int cnt;
	unsigned long on = 1;
	struct sockaddr_mISDN addr;
	struct mISDN_devinfo devinfo;
	int pri, bri;

	/* already open */
	if (mISDNloop.sock > -1)
		return 0;

	PDEBUG(DEBUG_PORT, "Open external interface of loopback.\n");

	/* check port counts */
	ret = ioctl(mISDNsocket, IMGETCOUNT, &cnt);
	if (ret < 0) {
		fprintf(stderr, "Cannot get number of mISDN devices. (ioctl IMGETCOUNT failed ret=%d)\n", ret);
		return(ret);
	}

	if (cnt <= 0) {
		PERROR_RUNTIME("Found no card. Please be sure to load card drivers.\n");
		return -EIO;
	}
	mISDNloop.port = mISDN_getportbyname(mISDNsocket, cnt, options.loopback_ext);
	if (mISDNloop.port < 0) {
		PERROR_RUNTIME("Port name '%s' not found, did you load loopback interface?.\n", options.loopback_ext);
		return mISDNloop.port;
	}
	/* get protocol */
	bri = pri = 0;
	devinfo.id = mISDNloop.port;
	ret = ioctl(mISDNsocket, IMGETDEVINFO, &devinfo);
	if (ret < 0) {
		PERROR_RUNTIME("Cannot get device information for port %d. (ioctl IMGETDEVINFO failed ret=%d)\n", mISDNloop.port, ret);
		return ret;
	}
	if (devinfo.Dprotocols & (1 << ISDN_P_TE_S0)) {
		bri = 1;
	}
	if (devinfo.Dprotocols & (1 << ISDN_P_TE_E1)) {
		pri = 1;
	}
	if (!bri && !pri) {
		PERROR_RUNTIME("loop port %d does not support TE PRI or TE BRI.\n", mISDNloop.port);
	}
	/* open socket */
	if ((mISDNloop.sock = socket(PF_ISDN, SOCK_DGRAM, (pri)?ISDN_P_TE_E1:ISDN_P_TE_S0)) < 0) {
		PERROR_RUNTIME("loop port %d failed to open socket.\n", mISDNloop.port);
		mISDNloop_close();
		return mISDNloop.sock;
	}
	/* set nonblocking io */
	if ((ret = ioctl(mISDNloop.sock, FIONBIO, &on)) < 0) {
		PERROR_RUNTIME("loop port %d failed to set socket into nonblocking io.\n", mISDNloop.port);
		mISDNloop_close();
		return ret;
	}
	/* bind socket to dchannel */
	memset(&addr, 0, sizeof(addr));
	addr.family = AF_ISDN;
	addr.dev = mISDNloop.port;
	addr.channel = 0;
	if ((ret = bind(mISDNloop.sock, (struct sockaddr *)&addr, sizeof(addr))) < 0) {
		PERROR_RUNTIME("loop port %d failed to bind socket. (name = %s errno=%d)\n", mISDNloop.port, options.loopback_ext, errno);
		mISDNloop_close();
		return (ret);
	}

	return 0;
}

int loop_hunt_bchannel(class PmISDN *port, struct mISDNport *mISDNport)
{
	int channel;
	int i;
	char map[mISDNport->b_num];
	struct interface *interface;
	struct interface_port *ifport;

	chan_trace_header(mISDNport, port, "CHANNEL SELECTION (setup)", DIRECTION_NONE);
	add_trace("channel", "reserved", "%d", mISDNport->b_reserved);
	if (mISDNport->b_reserved >= mISDNport->b_num) { // of out chan..
		add_trace("conclusion", NULL, "all channels are reserved");
		end_trace();
		return(-34); // no channel
	}

	/* map all used ports of shared loopback interface */
	memset(map, 0, sizeof(map));
	interface = interface_first;
	while(interface) {
		ifport = interface->ifport;
		while(ifport) {
			if (!strcmp(ifport->portname, options.loopback_lcr)) {
				i = 0;
				while(i < mISDNport->b_num) {
					if (mISDNport->b_port[i])
						map[i] = 1;
					i++;
				}
			}
			ifport = ifport->next;
		}
		interface = interface->next;
	}

	/* find channel */
	i = 0;
	channel = 0;
	while(i < mISDNport->b_num) {
		if (!map[i]) {
			channel = i+1+(i>=15);
			break;
		}
		i++;
	}
	if (!channel) {
		add_trace("conclusion", NULL, "no channel available");
		end_trace();
		return(-6); // channel unacceptable
	}
	add_trace("conclusion", NULL, "channel available");
	add_trace("connect", "channel", "%d", channel);
	end_trace();
	return(channel);
}