#include <errno.h>
#include <stdlib.h>
#include <net/if_dl.h>
#include <net/if_media.h>
#include <net/if_types.h>
#include <new>
#include <ethernet.h>
#include <ether_driver.h>
#include <net_buffer.h>
#include <net_datalink_protocol.h>
#include <NetBufferUtilities.h>
#include <net_stack.h>
#include <KernelExport.h>
#include <KPPPManager.h>
#include <ppp_device.h>
#define PPP_HEADER_LENGTH (8 + ETHER_HEADER_LENGTH)
net_buffer* create_buffer_for_frame(uint8 *frame, uint16 frame_size);
static const bigtime_t kLinkCheckInterval = 1000000;
net_buffer_module_info *gBufferModule;
ppp_interface_module_info* gPPPInterfaceModule;
static net_stack_module_info *sStackModule;
static mutex sListLock;
static DoublyLinkedList<ppp_device> sCheckList;
static sem_id sLinkChangeSemaphore;
static thread_id sLinkCheckerThread;
status_t
ppp_init(const char *name, net_device **_device)
{
dprintf("%s: entering!\n", __func__);
if (strncmp(name, "ppp", 3)) {
dprintf("[%s] not ppp device\n", name);
return B_BAD_VALUE;
}
dprintf("[%s] is ppp device\n", name);
status_t status = get_module(NET_BUFFER_MODULE_NAME, (module_info **)&gBufferModule);
if (status < B_OK)
return status;
ppp_interface_id idInterface = gPPPInterfaceModule->CreateInterfaceWithName(name, 0);
KPPPInterface * pppInterface = gPPPInterfaceModule->GetInterface(idInterface);
if (idInterface <= 0 || pppInterface == NULL) {
if (idInterface <= 0)
dprintf("%s: idInterface: %" B_PRIu32 "\n", __func__, idInterface);
else
dprintf("%s: pppInterface == NULL %" B_PRIu32 "\n", __func__, idInterface);
put_module(NET_BUFFER_MODULE_NAME);
return B_NO_MEMORY;
}
ppp_device *device = (ppp_device *)pppInterface->Ifnet();
if (device == NULL) {
dprintf("%s: can not get ppp_device\n", __func__);
put_module(NET_BUFFER_MODULE_NAME);
return B_NO_MEMORY;
}
strcpy(device->name, name);
device->flags = (IFF_BROADCAST | IFF_LINK) & (~IFF_UP);
device->type = IFT_PPP;
device->mtu = 1492;
device->frame_size = 1500;
device->media = IFM_ACTIVE | IFM_ETHER;
device->header_length = PPP_HEADER_LENGTH;
status =sStackModule->init_fifo(&(device->ppp_fifo), "ppp_fifo", 10 * 1500);
if (status < B_OK)
return(status);
TRACE("[%s] finish\n", "allocate ppp_device");
*_device = device;
return B_OK;
}
status_t
ppp_uninit(net_device *device)
{
dprintf("%s: uninit ppp\n", __func__);
ppp_device *ppp_dev=(ppp_device *)device;
sStackModule->uninit_fifo(&(ppp_dev->ppp_fifo));
put_module(NET_BUFFER_MODULE_NAME);
delete ppp_dev;
return B_OK;
}
status_t
ppp_up(net_device *_device)
{
dprintf("ppp_up\n");
ppp_device *device = (ppp_device *)_device;
if (device->KPPP_Interface == NULL) {
dprintf("%s: warning! can not find pppinterface KPPP_Interface\n", __func__);
return B_ERROR;
}
if (device->KPPP_Interface->Up() != true) {
dprintf("%s: warning! KPPP_Interface->Up() failure\n", __func__);
return B_ERROR;
}
device->mtu = device->frame_size - device->header_length;
dprintf("%s: congratulations! Find pppinterface (device->KPPP_Interface)\n", __func__);
return B_OK;
}
void
ppp_down(net_device *_device)
{
dprintf("%s\n", __func__);
ppp_device *device = (ppp_device *)_device;
device->KPPP_Interface->Down();
MutexLocker _(sListLock);
if (device->GetDoublyLinkedListLink()->next != NULL
|| device->GetDoublyLinkedListLink()->previous != NULL
|| device == sCheckList.Head())
sCheckList.Remove(device);
return;
}
status_t
ppp_control(net_device *_device, int32 op, void *argument,
size_t length)
{
TRACE("%s op:%" B_PRId32 "\n", __func__, op);
ppp_device *device = (ppp_device *)_device;
if (device->KPPP_Interface == NULL) {
dprintf("%s: can not find KPPP_Interface for ppp\n", __func__);
return B_OK;
}
return device->KPPP_Interface->Control(op, argument, length);
return B_OK;
}
status_t
ppp_send_data(net_device *_device, net_buffer *buffer)
{
TRACE("%s\n", __func__);
ppp_device *device = (ppp_device *)_device;
if (buffer->size > device->frame_size || buffer->size < device->header_length) {
dprintf("sorry! fail send ppp packet, size wrong!\n");
return EMSGSIZE;
}
if (device->KPPP_Interface == NULL) {
dprintf("Fail send ppp packet, no eth for ppp!\n");
return B_BAD_VALUE;
}
size_t net_buffer_size = buffer->size;
status_t status = device->KPPP_Interface->Send(buffer, 0x0021);
if (status != B_OK) {
dprintf("KPPP_Interface->Send(buffer, 0x0021 IP) fail\n");
return B_BAD_VALUE;
}
return B_OK;
}
status_t
ppp_receive_data(net_device *_device, net_buffer **_buffer)
{
TRACE("%s\n", __func__);
ppp_device *device = (ppp_device *)_device;
if (device->KPPP_Interface == NULL)
return B_FILE_ERROR;
TRACE("%s: trying fifo_dequeue_buffer\n", __func__);
status_t status = sStackModule->fifo_dequeue_buffer(&(device->ppp_fifo), 0, 10000000, _buffer);
if (status < B_OK) {
TRACE("sorry! can not fifo_dequeue_buffer!\n");
return status;
}
return B_OK;
}
status_t
ppp_set_mtu(net_device *_device, size_t mtu)
{
ppp_device *device = (ppp_device *)_device;
if (mtu > device->frame_size - ETHER_HEADER_LENGTH - 8
|| mtu <= ETHER_HEADER_LENGTH + 8 + 10)
return B_BAD_VALUE;
device->mtu = mtu;
return B_OK;
}
status_t
ppp_set_promiscuous(net_device *_device, bool promiscuous)
{
return B_NOT_SUPPORTED;
}
status_t
ppp_set_media(net_device *device, uint32 media)
{
return B_NOT_SUPPORTED;
}
status_t
ppp_add_multicast(struct net_device *_device, const sockaddr *_address)
{
if (_address->sa_family != AF_LINK)
return B_BAD_VALUE;
const sockaddr_dl *address = (const sockaddr_dl *)_address;
if (address->sdl_type != IFT_ETHER)
return B_BAD_VALUE;
return B_NOT_SUPPORTED;
}
status_t
ppp_remove_multicast(struct net_device *_device, const sockaddr *_address)
{
if (_address->sa_family != AF_LINK)
return B_BAD_VALUE;
const sockaddr_dl *address = (const sockaddr_dl *)_address;
if (address->sdl_type != IFT_ETHER)
return B_BAD_VALUE;
return B_NOT_SUPPORTED;
}
static status_t
ppp_std_ops(int32 op, ...)
{
switch (op) {
case B_MODULE_INIT:
{
status_t status = get_module(NET_STACK_MODULE_NAME,
(module_info **)&sStackModule);
if (status < B_OK)
return status;
status = get_module(PPP_INTERFACE_MODULE_NAME,
(module_info**)&gPPPInterfaceModule);
if (status < B_OK) {
put_module(NET_STACK_MODULE_NAME);
return status;
}
new (&sCheckList) DoublyLinkedList<ppp_device>;
sLinkCheckerThread = -1;
sLinkChangeSemaphore = create_sem(0, "ppp link change");
if (sLinkChangeSemaphore < B_OK) {
put_module(PPP_INTERFACE_MODULE_NAME);
put_module(NET_STACK_MODULE_NAME);
return sLinkChangeSemaphore;
}
mutex_init(&sListLock, "ppp devices");
return B_OK;
}
case B_MODULE_UNINIT:
{
delete_sem(sLinkChangeSemaphore);
status_t status;
wait_for_thread(sLinkCheckerThread, &status);
mutex_destroy(&sListLock);
put_module(PPP_INTERFACE_MODULE_NAME);
put_module(NET_STACK_MODULE_NAME);
return B_OK;
}
default:
return B_ERROR;
}
}
net_device_module_info spppModule = {
{
"network/devices/ppp/v1",
0,
ppp_std_ops
},
ppp_init,
ppp_uninit,
ppp_up,
ppp_down,
ppp_control,
ppp_send_data,
ppp_receive_data,
ppp_set_mtu,
ppp_set_promiscuous,
ppp_set_media,
ppp_add_multicast,
ppp_remove_multicast,
};
module_info *modules[] = {
(module_info *)&spppModule,
NULL
};