2 * Copyright 2003-2006, Waldemar Kornewald <wkornew@gmx.net>
3 * Distributed under the terms of the MIT License.
8 #include "ModemDevice.h"
9 #include "ACFCHandler.h"
17 #include <settings_tools.h>
21 static char sDigits
[] = "0123456789ABCDEF";
23 dump_packet(net_buffer
*packet
)
28 uint8
*data
= mtod(packet
, uint8
*);
30 uint8 bufferIndex
= 0;
32 TRACE("Dumping packet;len=%ld;pkthdr.len=%d\n", packet
->m_len
,
33 packet
->m_flags
& M_PKTHDR
? packet
->m_pkthdr
.len
: -1);
35 for (uint32 index
= 0; index
< packet
->m_len
; index
++) {
36 buffer
[bufferIndex
++] = sDigits
[data
[index
] >> 4];
37 buffer
[bufferIndex
++] = sDigits
[data
[index
] & 0x0F];
38 if (bufferIndex
== 32 || index
== packet
->m_len
- 1) {
39 buffer
[bufferIndex
] = 0;
40 TRACE("%s\n", buffer
);
49 modem_put_line(int32 handle
, const char *string
, int32 length
)
55 sprintf(line
, "%s\r", string
);
56 return write(handle
, line
, length
+ 1);
61 modem_get_line(int32 handle
, char *string
, int32 length
, const char *echo
)
63 if (!string
|| length
< 40)
66 int32 result
, position
= 0;
68 while(position
< length
) {
69 result
= read(handle
, string
+ position
, 1);
72 else if (result
== 1) {
73 if (string
[position
] == '\r') {
75 if (!strcasecmp(string
, echo
)) {
93 worker_thread(void *data
)
95 ModemDevice
*device
= (ModemDevice
*) data
;
96 int32 handle
= device
->Handle();
97 uint8 buffer
[MODEM_MTU
];
100 if (modem_put_line(handle
, device
->InitString(), strlen(device
->InitString())) < 0
101 || modem_get_line(handle
, (char*) buffer
, sizeof(buffer
),
102 device
->InitString()) < 0
103 || strcmp((char*) buffer
, "OK")) {
104 device
->FailedDialing();
109 if (modem_put_line(handle
, device
->DialString(), strlen(device
->DialString())) < 0
110 || modem_get_line(handle
, (char*) buffer
, sizeof(buffer
),
111 device
->DialString()) < 0
112 || strncmp((char*) buffer
, "CONNECT", 7)) {
113 device
->FailedDialing();
117 if (strlen((char*) buffer
) > 8)
118 device
->SetSpeed(atoi((char*) buffer
+ 8));
120 device
->SetSpeed(19200);
122 // TODO: authenticate if needed
124 device
->FinishedDialing();
127 int32 length
= 0, position
= 0;
128 bool inPacket
= true, needsEscape
= false;
131 // ignore data if buffer is full
132 if (position
== MODEM_MTU
)
135 length
= read(handle
, buffer
+ position
, MODEM_MTU
- position
);
137 if (length
< 0 || !device
->IsUp()) {
138 device
->ConnectionLost();
143 for (int32 index
= 0; index
< length
; ) {
144 if (buffer
[position
] == FLAG_SEQUENCE
) {
145 if (inPacket
&& position
> 0)
146 device
->DataReceived(buffer
, position
);
147 // DataReceived() will check FCS
149 length
= length
- index
- 1;
150 // remaining data length
151 memmove(buffer
, buffer
+ position
+ 1, length
);
152 position
= index
= 0;
159 if (buffer
[position
+ index
] < 0x20) {
165 buffer
[position
] = buffer
[position
+ index
] ^ 0x20;
168 } else if (buffer
[position
+ index
] == CONTROL_ESCAPE
) {
172 buffer
[position
] = buffer
[position
+ index
];
180 ModemDevice::ModemDevice(KPPPInterface
& interface
, driver_parameter
*settings
)
181 : KPPPDevice("Modem", 0, interface
, settings
),
189 TRACE("ModemDevice: Constructor\n");
190 if (!settings
|| !settings
->parameters
)
191 TRACE("ModemDevice::ctor: No settings!\n");
194 fACFC
= new ACFCHandler(REQUEST_ACFC
| ALLOW_ACFC
, interface
);
195 if (!interface
.LCP().AddOptionHandler(fACFC
)) {
196 fInitStatus
= B_ERROR
;
200 interface
.SetPFCOptions(PPP_REQUEST_PFC
| PPP_ALLOW_PFC
);
204 // MTU size does not contain PPP header
206 fPortName
= get_parameter_value(MODEM_PORT_KEY
, settings
);
207 fInitString
= get_parameter_value(MODEM_INIT_KEY
, settings
);
208 fDialString
= get_parameter_value(MODEM_DIAL_KEY
, settings
);
210 TRACE("ModemDevice::ctor: interfaceName: %s\n", fPortName
);
214 ModemDevice::~ModemDevice()
216 TRACE("ModemDevice: Destructor\n");
221 ModemDevice::InitCheck() const
223 if (fState
!= INITIAL
&& Handle() == -1)
226 return PortName() && InitString() && DialString()
227 && KPPPDevice::InitCheck() == B_OK
? B_OK
: B_ERROR
;
234 TRACE("ModemDevice: Up()\n");
236 if (InitCheck() != B_OK
)
245 // check if we are allowed to go up now (user intervention might disallow that)
250 // there was no error
257 if (fWorkerThread
== -1) {
258 fWorkerThread
= spawn_kernel_thread(worker_thread
, "Modem: worker_thread",
259 B_NORMAL_PRIORITY
, this);
260 resume_thread(fWorkerThread
);
270 TRACE("ModemDevice: Down()\n");
272 if (InitCheck() != B_OK
)
275 fState
= TERMINATING
;
285 // this tells StateMachine that DownEvent() does not mean we lost connection
287 // worker_thread will notice that we are terminating (IsUp() == false)
288 // ConnectionLost() will be called so we can terminate the connection there.
290 wait_for_thread(fWorkerThread
, &tmp
);
299 ModemDevice::SetSpeed(uint32 bps
)
301 fInputTransferRate
= bps
/ 8;
302 fOutputTransferRate
= (fInputTransferRate
* 60) / 100;
303 // 60% of input transfer rate
308 ModemDevice::InputTransferRate() const
310 return fInputTransferRate
;
315 ModemDevice::OutputTransferRate() const
317 return fOutputTransferRate
;
322 ModemDevice::CountOutputBytes() const
329 ModemDevice::OpenModem()
334 fHandle
= open(PortName(), O_RDWR
);
337 struct termios options
;
338 if (ioctl(fHandle
, TCGETA
, &options
) != B_OK
) {
339 ERROR("ModemDevice: Could not retrieve port options!\n");
344 options
.c_cflag
&= ~CBAUD
;
345 options
.c_cflag
|= B115200
;
346 options
.c_cflag
|= (CLOCAL
| CREAD
);
347 options
.c_lflag
&= ~(ICANON
| ECHO
| ECHOE
| ISIG
);
348 options
.c_oflag
&= ~OPOST
;
349 options
.c_cc
[VMIN
] = 0;
350 options
.c_cc
[VTIME
] = 10;
353 if (ioctl(fHandle
, TCSETA
, &options
) != B_OK
) {
354 ERROR("ModemDevice: Could not init port!\n");
361 ModemDevice::CloseModem()
371 ModemDevice::FinishedDialing()
380 ModemDevice::FailedDialing()
390 ModemDevice::ConnectionLost()
392 // switch to command mode and disconnect
395 snooze(ESCAPE_DELAY
);
396 if (write(Handle(), ESCAPE_SEQUENCE
, strlen(ESCAPE_SEQUENCE
)) < 0)
398 snooze(ESCAPE_DELAY
);
400 modem_put_line(Handle(), AT_HANG_UP
, strlen(AT_HANG_UP
));
406 ModemDevice::Send(net_buffer
*packet
, uint16 protocolNumber
)
409 TRACE("ModemDevice: Send()\n");
415 else if (InitCheck() != B_OK
|| protocolNumber
!= 0) {
416 gBufferModule
->free(packet
);
418 } else if (!IsUp()) {
419 gBufferModule
->free(packet
);
420 return PPP_NO_CONNECTION
;
423 uint8 buffer
[2 * (MODEM_MTU
+ PACKET_OVERHEAD
)];
426 if (fACFC
->LocalState() != ACFC_ACCEPTED
) {
427 NetBufferPrepend
<uint8
> bufferHeader(packet
, 2);
428 uint8
* data
= bufferHeader
.operator->();
429 data
[0] = ALL_STATIONS
;
434 int32 length
= packet
->size
;
435 int32 offset
= (fACFC
->LocalState() != ACFC_ACCEPTED
) ? 2 : 0;
437 if (gBufferModule
->direct_access(packet
, offset
, length
, (void**)&data
) != B_OK
) {
438 ERROR("ModemDevice: Failed to access buffer!\n");
444 fcs
= pppfcs16(fcs
, data
, length
);
446 data
[length
++] = fcs
& 0x00ff;
447 data
[length
++] = (fcs
& 0xff00) >> 8;
450 buffer
[position
++] = FLAG_SEQUENCE
;
451 // mark beginning of packet
452 for (int32 index
= 0; index
< length
; index
++) {
453 if (data
[index
] < 0x20 || data
[index
] == FLAG_SEQUENCE
454 || data
[index
] == CONTROL_ESCAPE
) {
455 buffer
[position
++] = CONTROL_ESCAPE
;
456 buffer
[position
++] = data
[index
] ^ 0x20;
458 buffer
[position
++] = data
[index
];
460 buffer
[position
++] = FLAG_SEQUENCE
;
461 // mark end of packet
463 gBufferModule
->free(packet
);
467 atomic_add((int32
*) &fOutputBytes
, position
);
468 if (write(Handle(), buffer
, position
) < 0)
469 return PPP_NO_CONNECTION
;
470 atomic_add((int32
*) &fOutputBytes
, -position
);
477 ModemDevice::DataReceived(uint8
*buffer
, uint32 length
)
479 // TODO: report corrupted packets to KPPPInterface
485 fcs
= pppfcs16(fcs
, buffer
, length
- 2);
487 if (buffer
[length
- 2] != (fcs
& 0x00ff)
488 || buffer
[length
- 1] != (fcs
& 0xff00) >> 8) {
489 ERROR("ModemDevice: Incorrect FCS!\n");
493 if (buffer
[0] == ALL_STATIONS
&& buffer
[1] == UI
)
496 net_buffer
* packet
= gBufferModule
->create(length
- 2);
497 if (gBufferModule
->write(packet
, 0, buffer
, length
- 2) != B_OK
) {
498 ERROR("ModemDevice: Failed to write into packet!\n");
502 return Receive(packet
);
507 ModemDevice::Receive(net_buffer
*packet
, uint16 protocolNumber
)
509 // we do not need to lock because only the worker_thread calls this method
513 else if (InitCheck() != B_OK
|| !IsUp()) {
514 gBufferModule
->free(packet
);
518 return Interface().ReceiveFromDevice(packet
);