Driver for USB RNDIS Network devices
Copyright (C) 2022 Adrien Destugues <pulkomandy@pulkomandy.tk>
Distributed under the terms of the MIT license.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <lock.h>
#include "Driver.h"
#include "RNDISDevice.h"
int32 api_version = B_CUR_DRIVER_API_VERSION;
static const char *sDeviceBaseName = "net/usb_rndis/";
RNDISDevice *gRNDISDevices[MAX_DEVICES];
char *gDeviceNames[MAX_DEVICES + 1];
usb_module_info *gUSBModule = NULL;
mutex gDriverLock;
status_t
usb_rndis_device_added(usb_device device, void **cookie)
{
*cookie = NULL;
mutex_lock(&gDriverLock);
RNDISDevice *rndisDevice = new RNDISDevice(device);
status_t status = rndisDevice->InitCheck();
if (status < B_OK) {
delete rndisDevice;
mutex_unlock(&gDriverLock);
return status;
}
for (int32 i = 0; i < MAX_DEVICES; i++) {
if (gRNDISDevices[i] != NULL)
continue;
gRNDISDevices[i] = rndisDevice;
*cookie = rndisDevice;
TRACE_ALWAYS("rndis device %" B_PRId32 " added\n", i);
mutex_unlock(&gDriverLock);
return B_OK;
}
delete rndisDevice;
mutex_unlock(&gDriverLock);
return B_ERROR;
}
status_t
usb_rndis_device_removed(void *cookie)
{
mutex_lock(&gDriverLock);
RNDISDevice *device = (RNDISDevice *)cookie;
for (int32 i = 0; i < MAX_DEVICES; i++) {
if (gRNDISDevices[i] == device) {
if (device->IsOpen()) {
device->Removed();
} else {
gRNDISDevices[i] = NULL;
delete device;
}
break;
}
}
mutex_unlock(&gDriverLock);
return B_OK;
}
status_t
init_hardware()
{
TRACE("init_hardware()\n");
return B_OK;
}
status_t
init_driver()
{
TRACE("init_driver()\n");
status_t status = get_module(B_USB_MODULE_NAME,
(module_info **)&gUSBModule);
if (status < B_OK)
return status;
for (int32 i = 0; i < MAX_DEVICES; i++)
gRNDISDevices[i] = NULL;
gDeviceNames[0] = NULL;
mutex_init(&gDriverLock, DRIVER_NAME"_devices");
static usb_notify_hooks notifyHooks = {
&usb_rndis_device_added,
&usb_rndis_device_removed
};
static usb_support_descriptor supportDescriptor = {
0xE0,
0x01, 0x03,
0, 0
};
gUSBModule->register_driver(DRIVER_NAME, &supportDescriptor, 1, NULL);
gUSBModule->install_notify(DRIVER_NAME, ¬ifyHooks);
return B_OK;
}
void
uninit_driver()
{
TRACE("uninit_driver()\n");
gUSBModule->uninstall_notify(DRIVER_NAME);
mutex_lock(&gDriverLock);
for (int32 i = 0; i < MAX_DEVICES; i++) {
if (gRNDISDevices[i] != NULL) {
delete gRNDISDevices[i];
gRNDISDevices[i] = NULL;
}
}
for (int32 i = 0; gDeviceNames[i]; i++) {
free(gDeviceNames[i]);
gDeviceNames[i] = NULL;
}
mutex_destroy(&gDriverLock);
put_module(B_USB_MODULE_NAME);
}
static status_t
usb_rndis_open(const char *name, uint32 flags, void **cookie)
{
TRACE("open(%s, %" B_PRIu32 ", %p)\n", name, flags, cookie);
mutex_lock(&gDriverLock);
*cookie = NULL;
status_t status = ENODEV;
int32 index = strtol(name + strlen(sDeviceBaseName), NULL, 10);
if (index >= 0 && index < MAX_DEVICES && gRNDISDevices[index] != NULL) {
status = gRNDISDevices[index]->Open();
if (status == B_OK)
*cookie = gRNDISDevices[index];
}
mutex_unlock(&gDriverLock);
return status;
}
static status_t
usb_rndis_read(void *cookie, off_t position, void *buffer, size_t *numBytes)
{
TRACE("read(%p, %" B_PRIdOFF ", %p, %lu)\n", cookie, position, buffer, *numBytes);
RNDISDevice *device = (RNDISDevice *)cookie;
return device->Read((uint8 *)buffer, numBytes);
}
static status_t
usb_rndis_write(void *cookie, off_t position, const void *buffer,
size_t *numBytes)
{
TRACE("write(%p, %" B_PRIdOFF ", %p, %lu)\n", cookie, position, buffer, *numBytes);
RNDISDevice *device = (RNDISDevice *)cookie;
return device->Write((const uint8 *)buffer, numBytes);
}
static status_t
usb_rndis_control(void *cookie, uint32 op, void *buffer, size_t length)
{
TRACE("control(%p, %" B_PRIu32 ", %p, %lu)\n", cookie, op, buffer, length);
RNDISDevice *device = (RNDISDevice *)cookie;
return device->Control(op, buffer, length);
}
static status_t
usb_rndis_close(void *cookie)
{
TRACE("close(%p)\n", cookie);
RNDISDevice *device = (RNDISDevice *)cookie;
return device->Close();
}
static status_t
usb_rndis_free(void *cookie)
{
TRACE("free(%p)\n", cookie);
RNDISDevice *device = (RNDISDevice *)cookie;
mutex_lock(&gDriverLock);
status_t status = device->Free();
for (int32 i = 0; i < MAX_DEVICES; i++) {
if (gRNDISDevices[i] == device) {
gRNDISDevices[i] = NULL;
delete device;
break;
}
}
mutex_unlock(&gDriverLock);
return status;
}
const char **
publish_devices()
{
TRACE("publish_devices()\n");
for (int32 i = 0; gDeviceNames[i]; i++) {
free(gDeviceNames[i]);
gDeviceNames[i] = NULL;
}
int32 deviceCount = 0;
mutex_lock(&gDriverLock);
for (int32 i = 0; i < MAX_DEVICES; i++) {
if (gRNDISDevices[i] == NULL)
continue;
gDeviceNames[deviceCount] = (char *)malloc(strlen(sDeviceBaseName) + 4);
if (gDeviceNames[deviceCount] != NULL) {
sprintf(gDeviceNames[deviceCount], "%s%" B_PRId32, sDeviceBaseName,
i);
TRACE("publishing %s\n", gDeviceNames[deviceCount]);
deviceCount++;
} else
TRACE_ALWAYS("publish_devices - no memory to allocate device name\n");
}
gDeviceNames[deviceCount] = NULL;
mutex_unlock(&gDriverLock);
return (const char **)&gDeviceNames[0];
}
device_hooks *
find_device(const char *name)
{
TRACE("find_device(%s)\n", name);
static device_hooks deviceHooks = {
usb_rndis_open,
usb_rndis_close,
usb_rndis_free,
usb_rndis_control,
usb_rndis_read,
usb_rndis_write,
NULL,
NULL
};
return &deviceHooks;
}