2 * Copyright 2010, Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
6 * Philippe Houdoin, <phoudoin %at% haiku-os %dot% org>
10 #include <net_buffer.h>
11 #include <net_device.h>
12 #include <net_stack.h>
14 #include <KernelExport.h>
18 #include <net/if_dl.h>
19 #include <net/if_media.h>
20 #include <net/if_types.h>
29 #define HDLC_FLAG_SEQUENCE 0x7e
30 #define HDLC_CONTROL_ESCAPE 0x7d
32 #define HDLC_ALL_STATIONS 0xff
35 #define HDLC_HEADER_LENGTH 4
45 struct dialup_device
: net_device
{
47 struct termios line_config
;
49 bigtime_t last_closing_flag_sequence_time
;
53 char escape_string
[8];
54 bigtime_t escape_silence
;
55 char hangup_string
[16];
56 bigtime_t tx_flag_timeout
;
61 net_buffer_module_info
* gBufferModule
;
62 static net_stack_module_info
* sStackModule
;
69 switch_to_command_mode(dialup_device
* device
)
71 if (device
->state
!= UP
)
74 if (!device
->data_mode
)
77 snooze(device
->escape_silence
);
79 ssize_t size
= write(device
->fd
, device
->escape_string
,
80 strlen(device
->escape_string
));
81 if (size
!= (ssize_t
)strlen(device
->escape_string
))
84 snooze(device
->escape_silence
);
85 device
->data_mode
= false;
92 switch_to_data_mode(dialup_device
* device
)
94 if (device
->state
!= UP
)
97 if (device
->data_mode
)
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);
107 device
->data_mode
= true;
114 send_command(dialup_device
* device
, const char* command
)
117 if (device
->data_mode
) {
118 status
= switch_to_command_mode(device
);
123 ssize_t bytesWritten
= write(device
->fd
, command
, strlen(command
));
124 if (bytesWritten
!= (ssize_t
)strlen(command
))
127 if (write(device
->fd
, "\r", 1) != 1)
135 read_command_reply(dialup_device
* device
, const char* command
,
136 char* reply
, int replyMaxSize
)
138 if (device
->data_mode
)
142 while (i
< replyMaxSize
) {
144 ssize_t bytesRead
= read(device
->fd
, &reply
[i
], 1);
148 if (reply
[i
] == '\n') {
149 // filter linefeed char
153 if (reply
[i
] == '\r') {
156 // is command reply or command echo (if any) ?
157 if (!strcasecmp(reply
, command
))
160 // It's command echo line. Just ignore it.
167 // replyMaxSize not large enough to store the full reply line.
173 hangup(dialup_device
* device
)
175 if (device
->state
!= UP
)
178 // TODO: turn device's DTR down instead. Or do that too after sending command
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"))
187 device
->state
= DOWN
;
196 dialup_init(const char* name
, net_device
** _device
)
198 // make sure this is a device in /dev/ports
199 if (strncmp(name
, "/dev/ports/", 11))
202 status_t status
= get_module(NET_BUFFER_MODULE_NAME
, (module_info
**)&gBufferModule
);
206 dialup_device
* device
= new (std::nothrow
)dialup_device
;
207 if (device
== NULL
) {
208 put_module(NET_BUFFER_MODULE_NAME
);
212 memset(device
, 0, sizeof(dialup_device
));
214 strcpy(device
->name
, name
);
215 device
->flags
= IFF_POINTOPOINT
;
216 device
->type
= IFT_PPP
; // this device handle RFC 1331 frame format only
219 device
->header_length
= HDLC_HEADER_LENGTH
;
222 device
->state
= DOWN
;
223 device
->data_mode
= false;
224 device
->last_closing_flag_sequence_time
= 0;
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
));
231 strncpy(device
->escape_string
, "+++", sizeof(device
->escape_string
));
232 device
->escape_silence
= 1000000;
234 device
->tx_flag_timeout
= 1000000;
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
));
246 dialup_uninit(net_device
* _device
)
248 dialup_device
* device
= (dialup_device
*)_device
;
251 put_module(NET_BUFFER_MODULE_NAME
);
252 gBufferModule
= NULL
;
258 dialup_up(net_device
* _device
)
260 dialup_device
* device
= (dialup_device
*)_device
;
262 device
->fd
= open(device
->name
, O_RDWR
);
266 device
->media
= IFM_ACTIVE
;
269 if (ioctl(device
->fd
, TCGETA
, &device
->line_config
,
270 sizeof(device
->line_config
)) < 0)
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;
285 if(ioctl(device
->fd
, TCSETA
, &device
->line_config
,
286 sizeof(device
->line_config
)) < 0)
289 // init modem & start dialing phase
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")) {
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)) {
319 device
->data_mode
= true;
321 device
->media
|= IFM_FULL_DUPLEX
;
322 device
->flags
|= IFF_LINK
;
324 device
->link_quality
= 1000;
325 if (strlen(reply
) > 7) {
326 // get speed from "CONNECTxxxx" reply
327 device
->link_speed
= atoi(&reply
[8]);
329 // Set default speed (theorically, it could be 300 bits/s even)
330 device
->link_speed
= 19200;
345 dialup_down(net_device
* _device
)
347 dialup_device
* device
= (dialup_device
*)_device
;
349 if (device
->flags
& IFF_LINK
350 && hangup(device
) == B_OK
)
351 device
->flags
&= ~IFF_LINK
;
360 dialup_control(net_device
* _device
, int32 op
, void* argument
,
363 dialup_device
* device
= (dialup_device
*)_device
;
364 return ioctl(device
->fd
, op
, argument
, length
);
369 dialup_send_data(net_device
* _device
, net_buffer
* buffer
)
371 dialup_device
* device
= (dialup_device
*)_device
;
373 if (device
->fd
== -1)
376 dprintf("try to send HDLC packet of %" B_PRIu32
" bytes "
377 "(flags 0x%" B_PRIx32
"):\n", buffer
->size
, buffer
->flags
);
379 if (buffer
->size
< HDLC_HEADER_LENGTH
)
382 iovec
* ioVectors
= NULL
;
384 uint8
* packet
= NULL
;
387 ssize_t bytesWritten
;
389 uint32 vectorCount
= gBufferModule
->count_iovecs(buffer
);
390 if (vectorCount
< 1) {
391 status
= B_BAD_VALUE
;
395 ioVectors
= (iovec
*)malloc(sizeof(iovec
)*vectorCount
);
396 if (ioVectors
== NULL
) {
397 status
= B_NO_MEMORY
;
400 gBufferModule
->get_iovecs(buffer
, ioVectors
, vectorCount
);
402 // encode HDLC packet
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
;
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
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
;
422 ioVector
= ioVectors
;
423 while (vectorCount
--) {
424 uint8
* data
= (uint8
*) ioVector
->iov_base
;
425 for (unsigned int i
= 0; i
< ioVector
->iov_len
; i
++) {
427 || data
[i
] == HDLC_FLAG_SEQUENCE
428 || data
[i
] == HDLC_CONTROL_ESCAPE
) {
430 packet
[packetSize
++] = HDLC_CONTROL_ESCAPE
;
431 packet
[packetSize
++] = data
[i
] ^ 0x20;
433 packet
[packetSize
++] = data
[i
];
440 packet
[packetSize
++] = HDLC_FLAG_SEQUENCE
;
444 bytesWritten
= write(device
->fd
, packet
, packetSize
);
445 if (bytesWritten
< 0) {
449 device
->last_closing_flag_sequence_time
= system_time();
451 device
->stats
.send
.packets
++;
452 device
->stats
.send
.bytes
+= bytesWritten
;
457 device
->stats
.send
.errors
++;
467 dialup_receive_data(net_device
* _device
, net_buffer
** _buffer
)
469 dialup_device
* device
= (dialup_device
*)_device
;
471 if (device
->fd
== -1)
474 net_buffer
* buffer
= gBufferModule
->create(256);
481 uint8
* packet
= (uint8
*)malloc(2 + 2 * buffer
->size
);
482 if (packet
== NULL
) {
483 status
= B_NO_MEMORY
;
487 status
= gBufferModule
->append_size(buffer
,
488 device
->mtu
+ HDLC_HEADER_LENGTH
, (void**)&data
);
489 if (status
== B_OK
&& data
== NULL
) {
490 dprintf("scattered I/O is not yet supported by dialup device.\n");
491 status
= B_NOT_SUPPORTED
;
497 bytesRead
= read(device
->fd
, data
, device
->mtu
+ HDLC_HEADER_LENGTH
);
503 status
= gBufferModule
->trim(buffer
, bytesRead
);
505 device
->stats
.receive
.dropped
++;
509 device
->stats
.receive
.bytes
+= bytesRead
;
510 device
->stats
.receive
.packets
++;
517 gBufferModule
->free(buffer
);
518 device
->stats
.receive
.errors
++;
527 dialup_set_mtu(net_device
* _device
, size_t mtu
)
529 dialup_device
* device
= (dialup_device
*)_device
;
537 dialup_set_promiscuous(net_device
* _device
, bool promiscuous
)
539 return B_NOT_SUPPORTED
;
544 dialup_set_media(net_device
* device
, uint32 media
)
546 return B_NOT_SUPPORTED
;
551 dialup_add_multicast(struct net_device
* _device
, const sockaddr
* _address
)
553 return B_NOT_SUPPORTED
;
558 dialup_remove_multicast(struct net_device
* _device
, const sockaddr
* _address
)
560 return B_NOT_SUPPORTED
;
565 dialup_std_ops(int32 op
, ...)
570 status_t status
= get_module(NET_STACK_MODULE_NAME
,
571 (module_info
**)&sStackModule
);
578 case B_MODULE_UNINIT
:
580 put_module(NET_STACK_MODULE_NAME
);
590 net_device_module_info sDialUpModule
= {
592 "network/devices/dialup/v1",
604 dialup_set_promiscuous
,
606 dialup_add_multicast
,
607 dialup_remove_multicast
,
610 module_info
* modules
[] = {
611 (module_info
*)&sDialUpModule
,