xref: /haiku/src/add-ons/kernel/network/devices/dialup/dialup.cpp (revision 87ada8acb14ef371e1d8c813378230efe59645e4)
1 /*
2  * Copyright 2010, Haiku, Inc. All Rights Reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Philippe Houdoin, <phoudoin %at% haiku-os %dot% org>
7  */
8 
9 
10 #include <net_buffer.h>
11 #include <net_device.h>
12 #include <net_stack.h>
13 
14 #include <KernelExport.h>
15 
16 #include <errno.h>
17 #include <net/if.h>
18 #include <net/if_dl.h>
19 #include <net/if_media.h>
20 #include <net/if_types.h>
21 #include <new>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <unistd.h>
25 #include <termios.h>
26 
27 #define HDLC_CONTROL_ESCAPE	0x7d
28 #define HDLC_FLAG_SEQUENCE	0x7e
29 #define HDLC_ALL_STATIONS	0xff
30 #define HDLC_UI				0x03
31 
32 
33 enum dialup_state {
34 	DOWN,
35 	DIALING,
36 	UP,
37 	HANGINGUP
38 };
39 
40 struct dialup_device : net_device {
41 	int				fd;
42 	dialup_state 	state;
43 	char*			init_string;
44 	char*			dial_string;
45 	char*			escape_string;
46 	bigtime_t		escape_silence;
47 	char*			hangup_string;
48 };
49 
50 net_buffer_module_info* gBufferModule;
51 static net_stack_module_info* sStackModule;
52 
53 //	#pragma mark -
54 
55 
56 status_t
57 dialup_init(const char* name, net_device** _device)
58 {
59 	// make sure this is a device in /dev/ports
60 	if (strncmp(name, "/dev/ports/", 11))
61 		return B_BAD_VALUE;
62 
63 	status_t status = get_module(NET_BUFFER_MODULE_NAME, (module_info**)&gBufferModule);
64 	if (status < B_OK)
65 		return status;
66 
67 	dialup_device* device = new (std::nothrow) dialup_device;
68 	if (device == NULL) {
69 		put_module(NET_BUFFER_MODULE_NAME);
70 		return B_NO_MEMORY;
71 	}
72 
73 	memset(device, 0, sizeof(dialup_device));
74 
75 	strcpy(device->name, name);
76 	device->flags = IFF_POINTOPOINT;
77 	device->type = IFT_PPP;
78 	device->mtu = 1502;
79 	device->media = 0;
80 	device->header_length = 8; // HDLC_HEADER_LENGTH;
81 	device->fd = -1;
82 	device->state = DOWN;
83 
84 	// default AT strings
85 	device->init_string = strdup("ATZ");
86 	device->dial_string = strdup("ATDT");
87 	device->escape_string = strdup("+++");
88 	device->escape_silence  = 1000000;
89 	device->hangup_string = strdup("ATH0");
90 
91 	*_device = device;
92 	return B_OK;
93 }
94 
95 
96 status_t
97 dialup_uninit(net_device* _device)
98 {
99 	dialup_device* device = (dialup_device*)_device;
100 	free(device->init_string);
101 	free(device->dial_string);
102 	free(device->escape_string);
103 	free(device->hangup_string);
104 
105 	delete device;
106 
107 	put_module(NET_BUFFER_MODULE_NAME);
108 
109 	return B_OK;
110 }
111 
112 
113 status_t
114 dialup_up(net_device* _device)
115 {
116 	dialup_device* device = (dialup_device*)_device;
117 
118 	device->fd = open(device->name, O_RDWR);
119 	if (device->fd < 0)
120 		return errno;
121 
122 	// init port
123 	struct termios options;
124 	if (ioctl(device->fd, TCGETA, &options, sizeof(options)) < 0)
125 		goto err;
126 
127 	// adjust options
128 	options.c_cflag &= ~CBAUD;
129 	options.c_cflag |= B115200;	// TODO: make this configurable too...
130 	options.c_cflag |= (CLOCAL | CREAD);
131 	options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
132 	options.c_oflag &= ~OPOST;
133 	options.c_cc[VMIN] = 0;
134 	options.c_cc[VTIME] = 10;
135 
136 	// set new options
137 	if(ioctl(device->fd, TCSETA, &options) < 0)
138 		goto err;
139 
140 	// TODO: init modem & start dialing phase
141 	device->state = DIALING;
142 	return B_OK;
143 
144 err:
145 	close(device->fd);
146 	device->fd = -1;
147 	return errno;
148 }
149 
150 
151 void
152 dialup_down(net_device* _device)
153 {
154 	dialup_device* device = (dialup_device*)_device;
155 
156 	// TODO: hangup if needed
157 	if (device->flags & IFF_LINK) {
158 		snooze(device->escape_silence);
159 		if (write(device->fd, device->escape_string,
160 			strlen(device->escape_string)) > 0) {
161 			snooze(device->escape_silence);
162 			// TODO: send hangup string and check for OK ack
163 		}
164 		device->flags &= ~IFF_LINK;
165 	}
166 
167 	close(device->fd);
168 	device->fd = -1;
169 }
170 
171 
172 status_t
173 dialup_control(net_device* _device, int32 op, void* argument,
174 	size_t length)
175 {
176 	dialup_device* device = (dialup_device*)_device;
177 	return ioctl(device->fd, op, argument, length);
178 }
179 
180 
181 status_t
182 dialup_send_data(net_device* _device, net_buffer* buffer)
183 {
184 	return B_NOT_SUPPORTED;
185 }
186 
187 
188 status_t
189 dialup_receive_data(net_device* _device, net_buffer** _buffer)
190 {
191 	dialup_device* device = (dialup_device*)_device;
192 
193 	if (device->fd == -1)
194 		return B_FILE_ERROR;
195 
196 	return ENOBUFS;
197 }
198 
199 
200 status_t
201 dialup_set_mtu(net_device* _device, size_t mtu)
202 {
203 	dialup_device* device = (dialup_device*)_device;
204 
205 	device->mtu = mtu;
206 	return B_OK;
207 }
208 
209 
210 status_t
211 dialup_set_promiscuous(net_device* _device, bool promiscuous)
212 {
213 	return B_NOT_SUPPORTED;
214 }
215 
216 
217 status_t
218 dialup_set_media(net_device* device, uint32 media)
219 {
220 	return B_NOT_SUPPORTED;
221 }
222 
223 
224 status_t
225 dialup_add_multicast(struct net_device* _device, const sockaddr* _address)
226 {
227 	return B_NOT_SUPPORTED;
228 }
229 
230 
231 status_t
232 dialup_remove_multicast(struct net_device* _device, const sockaddr* _address)
233 {
234 	return B_NOT_SUPPORTED;
235 }
236 
237 
238 static status_t
239 dialup_std_ops(int32 op, ...)
240 {
241 	switch (op) {
242 		case B_MODULE_INIT:
243 		{
244 			status_t status = get_module(NET_STACK_MODULE_NAME,
245 				(module_info**)&sStackModule);
246 			if (status < B_OK)
247 				return status;
248 
249 			return B_OK;
250 		}
251 
252 		case B_MODULE_UNINIT:
253 		{
254 			put_module(NET_STACK_MODULE_NAME);
255 			return B_OK;
256 		}
257 
258 		default:
259 			return B_ERROR;
260 	}
261 }
262 
263 
264 net_device_module_info sDialUpModule = {
265 	{
266 		"network/devices/dialup/v1",
267 		0,
268 		dialup_std_ops
269 	},
270 	dialup_init,
271 	dialup_uninit,
272 	dialup_up,
273 	dialup_down,
274 	dialup_control,
275 	dialup_send_data,
276 	dialup_receive_data,
277 	dialup_set_mtu,
278 	dialup_set_promiscuous,
279 	dialup_set_media,
280 	dialup_add_multicast,
281 	dialup_remove_multicast,
282 };
283 
284 module_info* modules[] = {
285 	(module_info*)&sDialUpModule,
286 	NULL
287 };
288