xref: /haiku/src/add-ons/kernel/network/devices/dialup/dialup.cpp (revision d374a27286b8a52974a97dba0d5966ea026a665d)
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 #include <sys/uio.h>
27 
28 
29 #define HDLC_FLAG_SEQUENCE	0x7e
30 #define HDLC_CONTROL_ESCAPE	0x7d
31 
32 #define HDLC_ALL_STATIONS	0xff
33 #define HDLC_UI				0x03
34 
35 #define HDLC_HEADER_LENGTH	4
36 
37 
38 enum dialup_state {
39 	DOWN,
40 	DIALING,
41 	UP,
42 	HANGINGUP
43 };
44 
45 struct dialup_device : net_device {
46 	int				fd;
47 	struct termios	line_config;
48 	dialup_state 	state;
49 	bigtime_t		last_closing_flag_sequence_time;
50 	bool			data_mode;
51 	char			init_string[64];
52 	char			dial_string[64];
53 	char			escape_string[8];
54 	bigtime_t		escape_silence;
55 	char			hangup_string[16];
56 	bigtime_t		tx_flag_timeout;
57 	uint32			rx_accm;
58 	uint32			tx_accm[8];
59 };
60 
61 net_buffer_module_info* gBufferModule;
62 static net_stack_module_info* sStackModule;
63 
64 
65 //	#pragma mark -
66 
67 
68 static status_t
69 switch_to_command_mode(dialup_device* device)
70 {
71 	if (device->state != UP || !device->data_mode)
72 		return B_ERROR;
73 
74 	snooze(device->escape_silence);
75 
76 	ssize_t size = write(device->fd, device->escape_string,
77 			strlen(device->escape_string));
78 	if (size != (ssize_t)strlen(device->escape_string))
79 		return B_IO_ERROR;
80 
81 	snooze(device->escape_silence);
82 	device->data_mode = false;
83 	return B_OK;
84 }
85 
86 #if 0
87 static status_t
88 switch_to_data_mode(dialup_device* device)
89 {
90 	if (device->state != UP)
91 		return B_OK;
92 
93 	// TODO: check if it's needed, as these days any
94 	// escaped AT commands switch back to data mode automatically
95 	// after their completion...
96 	ssize_t size = write(device->fd, "ATO", 3);
97 	if (size != 3)
98 		return B_IO_ERROR;
99 
100 	device->data_mode = true;
101 	return B_OK;
102 }
103 #endif
104 
105 static status_t
106 send_command(dialup_device* device, const char* command)
107 {
108 	status_t status;
109 	if (device->data_mode) {
110 		status = switch_to_command_mode(device);
111 		if (status != B_OK)
112 			return status;
113 	}
114 
115 	ssize_t bytesWritten = write(device->fd, command, strlen(command));
116 	if (bytesWritten != (ssize_t)strlen(command))
117 		return B_IO_ERROR;
118 
119 	if (write(device->fd, "\r", 1) != 1)
120 		return B_IO_ERROR;
121 
122 	return B_OK;
123 }
124 
125 
126 static status_t
127 read_command_reply(dialup_device* device, const char* command,
128 	char* reply, int replyMaxSize)
129 {
130 	if (device->data_mode)
131 		return B_ERROR;
132 
133 	int i = 0;
134 	while (i < replyMaxSize) {
135 
136 		ssize_t bytesRead = read(device->fd, &reply[i], 1);
137 		if (bytesRead != 1)
138 			return B_IO_ERROR;
139 
140 		if (reply[i] == '\n') {
141 			// filter linefeed char
142 			continue;
143 		}
144 
145 		if (reply[i] == '\r') {
146 			reply[i] = '\0';
147 
148 			// is command reply or command echo (if any) ?
149 			if (!strcasecmp(reply, command))
150 				return B_OK;
151 
152 			// It's command echo line. Just ignore it.
153 			i = 0;
154 			continue;
155 		}
156 		i++;
157 	}
158 
159 	// replyMaxSize not large enough to store the full reply line.
160 	return B_NO_MEMORY;
161 }
162 
163 
164 static status_t
165 hangup(dialup_device* device)
166 {
167 	if (device->state != UP)
168 		return B_ERROR;
169 
170 	// TODO: turn device's DTR down instead. Or do that too after sending command
171 	char reply[8];
172 
173 	if (send_command(device, device->hangup_string) != B_OK
174 		|| read_command_reply(device, device->hangup_string,
175 			reply, sizeof(reply)) != B_OK
176 		|| strcmp(reply, "OK"))
177 		return B_ERROR;
178 
179 	device->state = DOWN;
180 	return B_OK;
181 }
182 
183 
184 //	#pragma mark -
185 
186 
187 status_t
188 dialup_init(const char* name, net_device** _device)
189 {
190 	// make sure this is a device in /dev/ports
191 	if (strncmp(name, "/dev/ports/", 11))
192 		return B_BAD_VALUE;
193 
194 	status_t status = get_module(NET_BUFFER_MODULE_NAME, (module_info**)&gBufferModule);
195 	if (status < B_OK)
196 		return status;
197 
198 	dialup_device* device = new (std::nothrow)dialup_device;
199 	if (device == NULL) {
200 		put_module(NET_BUFFER_MODULE_NAME);
201 		return B_NO_MEMORY;
202 	}
203 
204 	memset(device, 0, sizeof(dialup_device));
205 
206 	strcpy(device->name, name);
207 	device->flags = IFF_POINTOPOINT;
208 	device->type = IFT_PPP; // this device handle RFC 1331 frame format only
209 	device->mtu = 1500;
210 	device->media = 0;
211 	device->header_length = HDLC_HEADER_LENGTH;
212 
213 	device->fd = -1;
214 	device->state = DOWN;
215 	device->data_mode = false;
216 	device->last_closing_flag_sequence_time = 0;
217 
218 	// default AT strings
219 	strncpy(device->init_string, "ATZ", sizeof(device->init_string));
220 	strncpy(device->dial_string, "ATDT", sizeof(device->dial_string));
221 	strncpy(device->hangup_string, "ATH0", sizeof(device->hangup_string));
222 
223 	strncpy(device->escape_string, "+++", sizeof(device->escape_string));
224 	device->escape_silence = 1000000;
225 
226 	device->tx_flag_timeout = 1000000;
227 
228 	// default rx & tx Async-Control-Character-Map
229 	memset(&device->rx_accm, 0xFF, sizeof(device->rx_accm));
230 	memset(&device->tx_accm, 0xFF, sizeof(device->tx_accm));
231 
232 	*_device = device;
233 	return B_OK;
234 }
235 
236 
237 status_t
238 dialup_uninit(net_device* _device)
239 {
240 	dialup_device* device = (dialup_device*)_device;
241 	delete device;
242 
243 	put_module(NET_BUFFER_MODULE_NAME);
244 	gBufferModule = NULL;
245 	return B_OK;
246 }
247 
248 
249 status_t
250 dialup_up(net_device* _device)
251 {
252 	dialup_device* device = (dialup_device*)_device;
253 
254 	device->fd = open(device->name, O_RDWR);
255 	if (device->fd < 0)
256 		return errno;
257 
258 	device->media = IFM_ACTIVE;
259 
260 	// init port
261 	if (ioctl(device->fd, TCGETA, &device->line_config,
262 		sizeof(device->line_config)) < 0)
263 		goto err;
264 
265 	// adjust options
266 	device->line_config.c_cflag &= ~CBAUD;
267 	device->line_config.c_cflag &= CSIZE;
268 	device->line_config.c_cflag &= CS8;
269 	device->line_config.c_cflag |= B115200;	// TODO: make this configurable too...
270 	device->line_config.c_cflag |= (CLOCAL | CREAD);
271 	device->line_config.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
272 	device->line_config.c_oflag &= ~OPOST;
273 	device->line_config.c_cc[VMIN] = 0;
274 	device->line_config.c_cc[VTIME] = 10;
275 
276 	// set new options
277 	if(ioctl(device->fd, TCSETA, &device->line_config,
278 		sizeof(device->line_config)) < 0)
279 		goto err;
280 
281 	// init modem & start dialing phase
282 
283 	char reply[32];
284 
285 	if (strlen(device->init_string) > 0) {
286 		// Send modem init string
287 		if (send_command(device, device->init_string) != B_OK
288 			|| read_command_reply(device, device->init_string,
289 				reply, sizeof(reply)) != B_OK
290 			|| strcmp(reply, "OK")) {
291 			errno = B_IO_ERROR;
292 			goto err;
293 		}
294 	}
295 
296 	if (strlen(device->dial_string) > 0) {
297 		// Send dialing string
298 		device->state = DIALING;
299 		if (send_command(device, device->dial_string) != B_OK
300 			|| read_command_reply(device, device->dial_string,
301 				reply, sizeof(reply)) != B_OK
302 			|| strncmp(reply, "CONNECT", 7)) {
303 			errno = B_IO_ERROR;
304 			goto err;
305 		}
306 	}
307 
308 	device->state = UP;
309 	device->data_mode = true;
310 
311 	device->media |= IFM_FULL_DUPLEX;
312 	device->flags |= IFF_LINK;
313 
314 	device->link_quality = 1000;
315 	if (strlen(reply) > 7) {
316 		// get speed from "CONNECTxxxx" reply
317 		device->link_speed = atoi(&reply[8]);
318 	} else {
319 		// Set default speed (theorically, it could be 300 bits/s even)
320 		device->link_speed = 19200;
321 	}
322 
323 	return B_OK;
324 
325 err:
326 	close(device->fd);
327 	device->fd = -1;
328 	device->media = 0;
329 
330 	return errno;
331 }
332 
333 
334 void
335 dialup_down(net_device* _device)
336 {
337 	dialup_device* device = (dialup_device*)_device;
338 
339 	if (device->flags & IFF_LINK
340 		&& hangup(device) == B_OK)
341 		device->flags &= ~IFF_LINK;
342 
343 	close(device->fd);
344 	device->fd = -1;
345 	device->media = 0;
346 }
347 
348 
349 status_t
350 dialup_control(net_device* _device, int32 op, void* argument,
351 	size_t length)
352 {
353 	dialup_device* device = (dialup_device*)_device;
354 	return ioctl(device->fd, op, argument, length);
355 }
356 
357 
358 status_t
359 dialup_send_data(net_device* _device, net_buffer* buffer)
360 {
361 	dialup_device* device = (dialup_device*)_device;
362 
363 	if (device->fd == -1)
364 		return B_FILE_ERROR;
365 
366 	dprintf("try to send HDLC packet of %lu bytes (flags %ld):\n", buffer->size, buffer->flags);
367 
368 	if (buffer->size < HDLC_HEADER_LENGTH)
369 		return B_BAD_VALUE;
370 
371 	iovec* ioVectors = NULL;
372 	iovec* ioVector;
373 	uint8* packet = NULL;
374 	int packetSize = 0;
375 	status_t status;
376 	ssize_t bytesWritten;
377 
378 	uint32 vectorCount = gBufferModule->count_iovecs(buffer);
379 	if (vectorCount < 1) {
380 		status = B_BAD_VALUE;
381 		goto err;
382 	}
383 
384 	ioVectors = (iovec*)malloc(sizeof(iovec)*vectorCount);
385 	if (ioVectors == NULL) {
386 		status = B_NO_MEMORY;
387 		goto err;
388 	}
389 	gBufferModule->get_iovecs(buffer, ioVectors, vectorCount);
390 
391 	// encode HDLC packet
392 
393 	// worst case: begin and end sequence flags and each byte needing escape
394 	packet = (uint8*)malloc(2 + 2 * buffer->size);
395 	if (packet == NULL) {
396 		status = B_NO_MEMORY;
397 		goto err;
398 	}
399 
400 	// Mark frame start if the prior frame closing flag was sent
401 	// more than a second ago.
402 	// Otherwise, the prior closing flag sequence is the open flag of this
403 	// frame
404 	if (device->tx_flag_timeout
405 		&& system_time() - device->last_closing_flag_sequence_time
406 			> device->tx_flag_timeout) {
407 		packet[packetSize++] = HDLC_FLAG_SEQUENCE;
408 	}
409 
410 	// encode frame data
411 	ioVector = ioVectors;
412 	while (vectorCount--) {
413 		uint8* data = (uint8*) ioVector->iov_base;
414 		for (unsigned int i = 0; i < ioVector->iov_len; i++) {
415 			if (data[i] < 0x20
416 				|| data[i] == HDLC_FLAG_SEQUENCE
417 				|| data[i] == HDLC_CONTROL_ESCAPE) {
418 				// needs escape
419 				packet[packetSize++] = HDLC_CONTROL_ESCAPE;
420 				packet[packetSize++] = data[i] ^ 0x20;
421 			} else
422 				packet[packetSize++] = data[i];
423 		}
424 		// next io vector
425 		ioVector++;
426 	}
427 
428 	// mark frame end
429 	packet[packetSize++] = HDLC_FLAG_SEQUENCE;
430 
431 	// send HDLC packet
432 
433 	bytesWritten = write(device->fd, packet, packetSize);
434 	if (bytesWritten < 0) {
435 		status = errno;
436 		goto err;
437 	}
438 	device->last_closing_flag_sequence_time = system_time();
439 
440 	device->stats.send.packets++;
441 	device->stats.send.bytes += bytesWritten;
442 	status = B_OK;
443 	goto done;
444 
445 err:
446 	device->stats.send.errors++;
447 
448 done:
449 	free(ioVectors);
450 	free(packet);
451 	return status;
452 }
453 
454 
455 status_t
456 dialup_receive_data(net_device* _device, net_buffer** _buffer)
457 {
458 	dialup_device* device = (dialup_device*)_device;
459 
460 	if (device->fd == -1)
461 		return B_FILE_ERROR;
462 
463 	net_buffer* buffer = gBufferModule->create(256);
464 	if (buffer == NULL)
465 		return ENOBUFS;
466 
467 	status_t status;
468 	ssize_t bytesRead;
469 	uint8* data = NULL;
470 	uint8* packet = (uint8*)malloc(2 + 2 * buffer->size);
471 	if (packet == NULL) {
472 		status = B_NO_MEMORY;
473 		goto err;
474 	}
475 
476 	status = gBufferModule->append_size(buffer,
477 		device->mtu + HDLC_HEADER_LENGTH, (void**)&data);
478 	if (status == B_OK && data == NULL) {
479 		dprintf("scattered I/O is not yet supported by dialup device.\n");
480 		status = B_NOT_SUPPORTED;
481 	}
482 	if (status < B_OK)
483 		goto err;
484 
485 	while (true) {
486 		bytesRead = read(device->fd, data, device->mtu + HDLC_HEADER_LENGTH);
487 		if (bytesRead < 0) {
488 			// TODO
489 		}
490 	}
491 
492 	status = gBufferModule->trim(buffer, bytesRead);
493 	if (status < B_OK) {
494 		device->stats.receive.dropped++;
495 		goto err;
496 	}
497 
498 	device->stats.receive.bytes += bytesRead;
499 	device->stats.receive.packets++;
500 
501 	*_buffer = buffer;
502 	status = B_OK;
503 	goto done;
504 
505 err:
506 	gBufferModule->free(buffer);
507 	device->stats.receive.errors++;
508 
509 done:
510 	free(packet);
511 	return status;
512 }
513 
514 
515 status_t
516 dialup_set_mtu(net_device* _device, size_t mtu)
517 {
518 	dialup_device* device = (dialup_device*)_device;
519 
520 	device->mtu = mtu;
521 	return B_OK;
522 }
523 
524 
525 status_t
526 dialup_set_promiscuous(net_device* _device, bool promiscuous)
527 {
528 	return B_NOT_SUPPORTED;
529 }
530 
531 
532 status_t
533 dialup_set_media(net_device* device, uint32 media)
534 {
535 	return B_NOT_SUPPORTED;
536 }
537 
538 
539 status_t
540 dialup_add_multicast(struct net_device* _device, const sockaddr* _address)
541 {
542 	return B_NOT_SUPPORTED;
543 }
544 
545 
546 status_t
547 dialup_remove_multicast(struct net_device* _device, const sockaddr* _address)
548 {
549 	return B_NOT_SUPPORTED;
550 }
551 
552 
553 static status_t
554 dialup_std_ops(int32 op, ...)
555 {
556 	switch (op) {
557 		case B_MODULE_INIT:
558 		{
559 			status_t status = get_module(NET_STACK_MODULE_NAME,
560 				(module_info**)&sStackModule);
561 			if (status < B_OK)
562 				return status;
563 
564 			return B_OK;
565 		}
566 
567 		case B_MODULE_UNINIT:
568 		{
569 			put_module(NET_STACK_MODULE_NAME);
570 			return B_OK;
571 		}
572 
573 		default:
574 			return B_ERROR;
575 	}
576 }
577 
578 
579 net_device_module_info sDialUpModule = {
580 	{
581 		"network/devices/dialup/v1",
582 		0,
583 		dialup_std_ops
584 	},
585 	dialup_init,
586 	dialup_uninit,
587 	dialup_up,
588 	dialup_down,
589 	dialup_control,
590 	dialup_send_data,
591 	dialup_receive_data,
592 	dialup_set_mtu,
593 	dialup_set_promiscuous,
594 	dialup_set_media,
595 	dialup_add_multicast,
596 	dialup_remove_multicast,
597 };
598 
599 module_info* modules[] = {
600 	(module_info*)&sDialUpModule,
601 	NULL
602 };
603