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