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