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
switch_to_command_mode(dialup_device * device)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
send_command(dialup_device * device,const char * command)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
read_command_reply(dialup_device * device,const char * command,char * reply,int replyMaxSize)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
hangup(dialup_device * device)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
dialup_init(const char * name,net_device ** _device)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
dialup_uninit(net_device * _device)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
dialup_up(net_device * _device)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
dialup_down(net_device * _device)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
dialup_control(net_device * _device,int32 op,void * argument,size_t length)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
dialup_send_data(net_device * _device,net_buffer * buffer)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 %" B_PRIu32 " bytes "
377 "(flags 0x%" B_PRIx32 "):\n", buffer->size, buffer->flags);
378
379 if (buffer->size < HDLC_HEADER_LENGTH)
380 return B_BAD_VALUE;
381
382 iovec* ioVectors = NULL;
383 iovec* ioVector;
384 uint8* packet = NULL;
385 int packetSize = 0;
386 status_t status;
387 ssize_t bytesWritten;
388
389 uint32 vectorCount = gBufferModule->count_iovecs(buffer);
390 if (vectorCount < 1) {
391 status = B_BAD_VALUE;
392 goto err;
393 }
394
395 ioVectors = (iovec*)malloc(sizeof(iovec)*vectorCount);
396 if (ioVectors == NULL) {
397 status = B_NO_MEMORY;
398 goto err;
399 }
400 gBufferModule->get_iovecs(buffer, ioVectors, vectorCount);
401
402 // encode HDLC packet
403
404 // worst case: begin and end sequence flags plus each payload byte escaped
405 packet = (uint8*)malloc(2 + 2 * buffer->size);
406 if (packet == NULL) {
407 status = B_NO_MEMORY;
408 goto err;
409 }
410
411 // Mark frame start if the prior frame closing flag was sent
412 // more than a second ago.
413 // Otherwise, the prior closing flag sequence is the open flag of this
414 // frame
415 if (device->tx_flag_timeout
416 && system_time() - device->last_closing_flag_sequence_time
417 > device->tx_flag_timeout) {
418 packet[packetSize++] = HDLC_FLAG_SEQUENCE;
419 }
420
421 // encode frame data
422 ioVector = ioVectors;
423 while (vectorCount--) {
424 uint8* data = (uint8*) ioVector->iov_base;
425 for (unsigned int i = 0; i < ioVector->iov_len; i++) {
426 if (data[i] < 0x20
427 || data[i] == HDLC_FLAG_SEQUENCE
428 || data[i] == HDLC_CONTROL_ESCAPE) {
429 // needs escape
430 packet[packetSize++] = HDLC_CONTROL_ESCAPE;
431 packet[packetSize++] = data[i] ^ 0x20;
432 } else
433 packet[packetSize++] = data[i];
434 }
435 // next io vector
436 ioVector++;
437 }
438
439 // mark frame end
440 packet[packetSize++] = HDLC_FLAG_SEQUENCE;
441
442 // send HDLC packet
443
444 bytesWritten = write(device->fd, packet, packetSize);
445 if (bytesWritten < 0) {
446 status = errno;
447 goto err;
448 }
449 device->last_closing_flag_sequence_time = system_time();
450
451 status = B_OK;
452 goto done;
453
454 err:
455 done:
456 free(ioVectors);
457 free(packet);
458 return status;
459 }
460
461
462 status_t
dialup_receive_data(net_device * _device,net_buffer ** _buffer)463 dialup_receive_data(net_device* _device, net_buffer** _buffer)
464 {
465 dialup_device* device = (dialup_device*)_device;
466
467 if (device->fd == -1)
468 return B_FILE_ERROR;
469
470 net_buffer* buffer = gBufferModule->create(256);
471 if (buffer == NULL)
472 return ENOBUFS;
473
474 status_t status;
475 ssize_t bytesRead;
476 uint8* data = NULL;
477 uint8* packet = (uint8*)malloc(2 + 2 * buffer->size);
478 if (packet == NULL) {
479 status = B_NO_MEMORY;
480 goto err;
481 }
482
483 status = gBufferModule->append_size(buffer,
484 device->mtu + HDLC_HEADER_LENGTH, (void**)&data);
485 if (status == B_OK && data == NULL) {
486 dprintf("scattered I/O is not yet supported by dialup device.\n");
487 status = B_NOT_SUPPORTED;
488 }
489 if (status < B_OK)
490 goto err;
491
492 while (true) {
493 bytesRead = read(device->fd, data, device->mtu + HDLC_HEADER_LENGTH);
494 if (bytesRead < 0) {
495 // TODO
496 }
497 }
498
499 status = gBufferModule->trim(buffer, bytesRead);
500 if (status < B_OK) {
501 atomic_add((int32*)&device->stats.receive.dropped, 1);
502 goto err;
503 }
504
505 *_buffer = buffer;
506 status = B_OK;
507 goto done;
508
509 err:
510 gBufferModule->free(buffer);
511
512 done:
513 free(packet);
514 return status;
515 }
516
517
518 status_t
dialup_set_mtu(net_device * _device,size_t mtu)519 dialup_set_mtu(net_device* _device, size_t mtu)
520 {
521 dialup_device* device = (dialup_device*)_device;
522
523 device->mtu = mtu;
524 return B_OK;
525 }
526
527
528 status_t
dialup_set_promiscuous(net_device * _device,bool promiscuous)529 dialup_set_promiscuous(net_device* _device, bool promiscuous)
530 {
531 return B_NOT_SUPPORTED;
532 }
533
534
535 status_t
dialup_set_media(net_device * device,uint32 media)536 dialup_set_media(net_device* device, uint32 media)
537 {
538 return B_NOT_SUPPORTED;
539 }
540
541
542 status_t
dialup_add_multicast(struct net_device * _device,const sockaddr * _address)543 dialup_add_multicast(struct net_device* _device, const sockaddr* _address)
544 {
545 return B_NOT_SUPPORTED;
546 }
547
548
549 status_t
dialup_remove_multicast(struct net_device * _device,const sockaddr * _address)550 dialup_remove_multicast(struct net_device* _device, const sockaddr* _address)
551 {
552 return B_NOT_SUPPORTED;
553 }
554
555
556 static status_t
dialup_std_ops(int32 op,...)557 dialup_std_ops(int32 op, ...)
558 {
559 switch (op) {
560 case B_MODULE_INIT:
561 {
562 status_t status = get_module(NET_STACK_MODULE_NAME,
563 (module_info**)&sStackModule);
564 if (status < B_OK)
565 return status;
566
567 return B_OK;
568 }
569
570 case B_MODULE_UNINIT:
571 {
572 put_module(NET_STACK_MODULE_NAME);
573 return B_OK;
574 }
575
576 default:
577 return B_ERROR;
578 }
579 }
580
581
582 net_device_module_info sDialUpModule = {
583 {
584 "network/devices/dialup/v1",
585 0,
586 dialup_std_ops
587 },
588 dialup_init,
589 dialup_uninit,
590 dialup_up,
591 dialup_down,
592 dialup_control,
593 dialup_send_data,
594 dialup_receive_data,
595 dialup_set_mtu,
596 dialup_set_promiscuous,
597 dialup_set_media,
598 dialup_add_multicast,
599 dialup_remove_multicast,
600 };
601
602 module_info* modules[] = {
603 (module_info*)&sDialUpModule,
604 NULL
605 };
606