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 %" 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 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 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 529 dialup_set_promiscuous(net_device* _device, bool promiscuous) 530 { 531 return B_NOT_SUPPORTED; 532 } 533 534 535 status_t 536 dialup_set_media(net_device* device, uint32 media) 537 { 538 return B_NOT_SUPPORTED; 539 } 540 541 542 status_t 543 dialup_add_multicast(struct net_device* _device, const sockaddr* _address) 544 { 545 return B_NOT_SUPPORTED; 546 } 547 548 549 status_t 550 dialup_remove_multicast(struct net_device* _device, const sockaddr* _address) 551 { 552 return B_NOT_SUPPORTED; 553 } 554 555 556 static status_t 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