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