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