1*b2489546SPulkoMandy /*
2*b2489546SPulkoMandy Driver for USB RNDIS Network devices
3*b2489546SPulkoMandy Copyright (C) 2022 Adrien Destugues <pulkomandy@pulkomandy.tk>
4*b2489546SPulkoMandy Distributed under the terms of the MIT license.
5*b2489546SPulkoMandy */
6*b2489546SPulkoMandy #include <stdio.h>
7*b2489546SPulkoMandy #include <stdlib.h>
8*b2489546SPulkoMandy #include <string.h>
9*b2489546SPulkoMandy #include <lock.h>
10*b2489546SPulkoMandy
11*b2489546SPulkoMandy #include "Driver.h"
12*b2489546SPulkoMandy #include "RNDISDevice.h"
13*b2489546SPulkoMandy
14*b2489546SPulkoMandy int32 api_version = B_CUR_DRIVER_API_VERSION;
15*b2489546SPulkoMandy static const char *sDeviceBaseName = "net/usb_rndis/";
16*b2489546SPulkoMandy RNDISDevice *gRNDISDevices[MAX_DEVICES];
17*b2489546SPulkoMandy char *gDeviceNames[MAX_DEVICES + 1];
18*b2489546SPulkoMandy usb_module_info *gUSBModule = NULL;
19*b2489546SPulkoMandy mutex gDriverLock;
20*b2489546SPulkoMandy
21*b2489546SPulkoMandy
22*b2489546SPulkoMandy status_t
usb_rndis_device_added(usb_device device,void ** cookie)23*b2489546SPulkoMandy usb_rndis_device_added(usb_device device, void **cookie)
24*b2489546SPulkoMandy {
25*b2489546SPulkoMandy *cookie = NULL;
26*b2489546SPulkoMandy
27*b2489546SPulkoMandy // check if this is a replug of an existing device first
28*b2489546SPulkoMandy mutex_lock(&gDriverLock);
29*b2489546SPulkoMandy
30*b2489546SPulkoMandy // no such device yet, create a new one
31*b2489546SPulkoMandy RNDISDevice *rndisDevice = new RNDISDevice(device);
32*b2489546SPulkoMandy status_t status = rndisDevice->InitCheck();
33*b2489546SPulkoMandy if (status < B_OK) {
34*b2489546SPulkoMandy delete rndisDevice;
35*b2489546SPulkoMandy mutex_unlock(&gDriverLock);
36*b2489546SPulkoMandy return status;
37*b2489546SPulkoMandy }
38*b2489546SPulkoMandy
39*b2489546SPulkoMandy for (int32 i = 0; i < MAX_DEVICES; i++) {
40*b2489546SPulkoMandy if (gRNDISDevices[i] != NULL)
41*b2489546SPulkoMandy continue;
42*b2489546SPulkoMandy
43*b2489546SPulkoMandy gRNDISDevices[i] = rndisDevice;
44*b2489546SPulkoMandy *cookie = rndisDevice;
45*b2489546SPulkoMandy
46*b2489546SPulkoMandy TRACE_ALWAYS("rndis device %" B_PRId32 " added\n", i);
47*b2489546SPulkoMandy mutex_unlock(&gDriverLock);
48*b2489546SPulkoMandy return B_OK;
49*b2489546SPulkoMandy }
50*b2489546SPulkoMandy
51*b2489546SPulkoMandy // no space for the device
52*b2489546SPulkoMandy delete rndisDevice;
53*b2489546SPulkoMandy mutex_unlock(&gDriverLock);
54*b2489546SPulkoMandy return B_ERROR;
55*b2489546SPulkoMandy }
56*b2489546SPulkoMandy
57*b2489546SPulkoMandy
58*b2489546SPulkoMandy status_t
usb_rndis_device_removed(void * cookie)59*b2489546SPulkoMandy usb_rndis_device_removed(void *cookie)
60*b2489546SPulkoMandy {
61*b2489546SPulkoMandy mutex_lock(&gDriverLock);
62*b2489546SPulkoMandy
63*b2489546SPulkoMandy RNDISDevice *device = (RNDISDevice *)cookie;
64*b2489546SPulkoMandy for (int32 i = 0; i < MAX_DEVICES; i++) {
65*b2489546SPulkoMandy if (gRNDISDevices[i] == device) {
66*b2489546SPulkoMandy if (device->IsOpen()) {
67*b2489546SPulkoMandy // the device will be deleted upon being freed
68*b2489546SPulkoMandy device->Removed();
69*b2489546SPulkoMandy } else {
70*b2489546SPulkoMandy gRNDISDevices[i] = NULL;
71*b2489546SPulkoMandy delete device;
72*b2489546SPulkoMandy }
73*b2489546SPulkoMandy break;
74*b2489546SPulkoMandy }
75*b2489546SPulkoMandy }
76*b2489546SPulkoMandy
77*b2489546SPulkoMandy mutex_unlock(&gDriverLock);
78*b2489546SPulkoMandy return B_OK;
79*b2489546SPulkoMandy }
80*b2489546SPulkoMandy
81*b2489546SPulkoMandy
82*b2489546SPulkoMandy //#pragma mark -
83*b2489546SPulkoMandy
84*b2489546SPulkoMandy
85*b2489546SPulkoMandy status_t
init_hardware()86*b2489546SPulkoMandy init_hardware()
87*b2489546SPulkoMandy {
88*b2489546SPulkoMandy TRACE("init_hardware()\n");
89*b2489546SPulkoMandy return B_OK;
90*b2489546SPulkoMandy }
91*b2489546SPulkoMandy
92*b2489546SPulkoMandy
93*b2489546SPulkoMandy status_t
init_driver()94*b2489546SPulkoMandy init_driver()
95*b2489546SPulkoMandy {
96*b2489546SPulkoMandy TRACE("init_driver()\n");
97*b2489546SPulkoMandy status_t status = get_module(B_USB_MODULE_NAME,
98*b2489546SPulkoMandy (module_info **)&gUSBModule);
99*b2489546SPulkoMandy if (status < B_OK)
100*b2489546SPulkoMandy return status;
101*b2489546SPulkoMandy
102*b2489546SPulkoMandy for (int32 i = 0; i < MAX_DEVICES; i++)
103*b2489546SPulkoMandy gRNDISDevices[i] = NULL;
104*b2489546SPulkoMandy
105*b2489546SPulkoMandy gDeviceNames[0] = NULL;
106*b2489546SPulkoMandy mutex_init(&gDriverLock, DRIVER_NAME"_devices");
107*b2489546SPulkoMandy
108*b2489546SPulkoMandy static usb_notify_hooks notifyHooks = {
109*b2489546SPulkoMandy &usb_rndis_device_added,
110*b2489546SPulkoMandy &usb_rndis_device_removed
111*b2489546SPulkoMandy };
112*b2489546SPulkoMandy
113*b2489546SPulkoMandy static usb_support_descriptor supportDescriptor = {
114*b2489546SPulkoMandy 0xE0, /* CDC - Wireless device */
115*b2489546SPulkoMandy 0x01, 0x03, /* RNDIS subclass/protocol */
116*b2489546SPulkoMandy 0, 0 /* no specific vendor or device */
117*b2489546SPulkoMandy };
118*b2489546SPulkoMandy
119*b2489546SPulkoMandy gUSBModule->register_driver(DRIVER_NAME, &supportDescriptor, 1, NULL);
120*b2489546SPulkoMandy gUSBModule->install_notify(DRIVER_NAME, ¬ifyHooks);
121*b2489546SPulkoMandy return B_OK;
122*b2489546SPulkoMandy }
123*b2489546SPulkoMandy
124*b2489546SPulkoMandy
125*b2489546SPulkoMandy void
uninit_driver()126*b2489546SPulkoMandy uninit_driver()
127*b2489546SPulkoMandy {
128*b2489546SPulkoMandy TRACE("uninit_driver()\n");
129*b2489546SPulkoMandy gUSBModule->uninstall_notify(DRIVER_NAME);
130*b2489546SPulkoMandy mutex_lock(&gDriverLock);
131*b2489546SPulkoMandy
132*b2489546SPulkoMandy for (int32 i = 0; i < MAX_DEVICES; i++) {
133*b2489546SPulkoMandy if (gRNDISDevices[i] != NULL) {
134*b2489546SPulkoMandy delete gRNDISDevices[i];
135*b2489546SPulkoMandy gRNDISDevices[i] = NULL;
136*b2489546SPulkoMandy }
137*b2489546SPulkoMandy }
138*b2489546SPulkoMandy
139*b2489546SPulkoMandy for (int32 i = 0; gDeviceNames[i]; i++) {
140*b2489546SPulkoMandy free(gDeviceNames[i]);
141*b2489546SPulkoMandy gDeviceNames[i] = NULL;
142*b2489546SPulkoMandy }
143*b2489546SPulkoMandy
144*b2489546SPulkoMandy mutex_destroy(&gDriverLock);
145*b2489546SPulkoMandy put_module(B_USB_MODULE_NAME);
146*b2489546SPulkoMandy }
147*b2489546SPulkoMandy
148*b2489546SPulkoMandy
149*b2489546SPulkoMandy static status_t
usb_rndis_open(const char * name,uint32 flags,void ** cookie)150*b2489546SPulkoMandy usb_rndis_open(const char *name, uint32 flags, void **cookie)
151*b2489546SPulkoMandy {
152*b2489546SPulkoMandy TRACE("open(%s, %" B_PRIu32 ", %p)\n", name, flags, cookie);
153*b2489546SPulkoMandy mutex_lock(&gDriverLock);
154*b2489546SPulkoMandy
155*b2489546SPulkoMandy *cookie = NULL;
156*b2489546SPulkoMandy status_t status = ENODEV;
157*b2489546SPulkoMandy int32 index = strtol(name + strlen(sDeviceBaseName), NULL, 10);
158*b2489546SPulkoMandy if (index >= 0 && index < MAX_DEVICES && gRNDISDevices[index] != NULL) {
159*b2489546SPulkoMandy status = gRNDISDevices[index]->Open();
160*b2489546SPulkoMandy if (status == B_OK)
161*b2489546SPulkoMandy *cookie = gRNDISDevices[index];
162*b2489546SPulkoMandy }
163*b2489546SPulkoMandy
164*b2489546SPulkoMandy mutex_unlock(&gDriverLock);
165*b2489546SPulkoMandy return status;
166*b2489546SPulkoMandy }
167*b2489546SPulkoMandy
168*b2489546SPulkoMandy
169*b2489546SPulkoMandy static status_t
usb_rndis_read(void * cookie,off_t position,void * buffer,size_t * numBytes)170*b2489546SPulkoMandy usb_rndis_read(void *cookie, off_t position, void *buffer, size_t *numBytes)
171*b2489546SPulkoMandy {
172*b2489546SPulkoMandy TRACE("read(%p, %" B_PRIdOFF ", %p, %lu)\n", cookie, position, buffer, *numBytes);
173*b2489546SPulkoMandy RNDISDevice *device = (RNDISDevice *)cookie;
174*b2489546SPulkoMandy return device->Read((uint8 *)buffer, numBytes);
175*b2489546SPulkoMandy }
176*b2489546SPulkoMandy
177*b2489546SPulkoMandy
178*b2489546SPulkoMandy static status_t
usb_rndis_write(void * cookie,off_t position,const void * buffer,size_t * numBytes)179*b2489546SPulkoMandy usb_rndis_write(void *cookie, off_t position, const void *buffer,
180*b2489546SPulkoMandy size_t *numBytes)
181*b2489546SPulkoMandy {
182*b2489546SPulkoMandy TRACE("write(%p, %" B_PRIdOFF ", %p, %lu)\n", cookie, position, buffer, *numBytes);
183*b2489546SPulkoMandy RNDISDevice *device = (RNDISDevice *)cookie;
184*b2489546SPulkoMandy return device->Write((const uint8 *)buffer, numBytes);
185*b2489546SPulkoMandy }
186*b2489546SPulkoMandy
187*b2489546SPulkoMandy
188*b2489546SPulkoMandy static status_t
usb_rndis_control(void * cookie,uint32 op,void * buffer,size_t length)189*b2489546SPulkoMandy usb_rndis_control(void *cookie, uint32 op, void *buffer, size_t length)
190*b2489546SPulkoMandy {
191*b2489546SPulkoMandy TRACE("control(%p, %" B_PRIu32 ", %p, %lu)\n", cookie, op, buffer, length);
192*b2489546SPulkoMandy RNDISDevice *device = (RNDISDevice *)cookie;
193*b2489546SPulkoMandy return device->Control(op, buffer, length);
194*b2489546SPulkoMandy }
195*b2489546SPulkoMandy
196*b2489546SPulkoMandy
197*b2489546SPulkoMandy static status_t
usb_rndis_close(void * cookie)198*b2489546SPulkoMandy usb_rndis_close(void *cookie)
199*b2489546SPulkoMandy {
200*b2489546SPulkoMandy TRACE("close(%p)\n", cookie);
201*b2489546SPulkoMandy RNDISDevice *device = (RNDISDevice *)cookie;
202*b2489546SPulkoMandy return device->Close();
203*b2489546SPulkoMandy }
204*b2489546SPulkoMandy
205*b2489546SPulkoMandy
206*b2489546SPulkoMandy static status_t
usb_rndis_free(void * cookie)207*b2489546SPulkoMandy usb_rndis_free(void *cookie)
208*b2489546SPulkoMandy {
209*b2489546SPulkoMandy TRACE("free(%p)\n", cookie);
210*b2489546SPulkoMandy RNDISDevice *device = (RNDISDevice *)cookie;
211*b2489546SPulkoMandy mutex_lock(&gDriverLock);
212*b2489546SPulkoMandy status_t status = device->Free();
213*b2489546SPulkoMandy for (int32 i = 0; i < MAX_DEVICES; i++) {
214*b2489546SPulkoMandy if (gRNDISDevices[i] == device) {
215*b2489546SPulkoMandy // the device is removed already but as it was open the
216*b2489546SPulkoMandy // removed hook has not deleted the object
217*b2489546SPulkoMandy gRNDISDevices[i] = NULL;
218*b2489546SPulkoMandy delete device;
219*b2489546SPulkoMandy break;
220*b2489546SPulkoMandy }
221*b2489546SPulkoMandy }
222*b2489546SPulkoMandy
223*b2489546SPulkoMandy mutex_unlock(&gDriverLock);
224*b2489546SPulkoMandy return status;
225*b2489546SPulkoMandy }
226*b2489546SPulkoMandy
227*b2489546SPulkoMandy
228*b2489546SPulkoMandy const char **
publish_devices()229*b2489546SPulkoMandy publish_devices()
230*b2489546SPulkoMandy {
231*b2489546SPulkoMandy TRACE("publish_devices()\n");
232*b2489546SPulkoMandy for (int32 i = 0; gDeviceNames[i]; i++) {
233*b2489546SPulkoMandy free(gDeviceNames[i]);
234*b2489546SPulkoMandy gDeviceNames[i] = NULL;
235*b2489546SPulkoMandy }
236*b2489546SPulkoMandy
237*b2489546SPulkoMandy int32 deviceCount = 0;
238*b2489546SPulkoMandy mutex_lock(&gDriverLock);
239*b2489546SPulkoMandy for (int32 i = 0; i < MAX_DEVICES; i++) {
240*b2489546SPulkoMandy if (gRNDISDevices[i] == NULL)
241*b2489546SPulkoMandy continue;
242*b2489546SPulkoMandy
243*b2489546SPulkoMandy gDeviceNames[deviceCount] = (char *)malloc(strlen(sDeviceBaseName) + 4);
244*b2489546SPulkoMandy if (gDeviceNames[deviceCount] != NULL) {
245*b2489546SPulkoMandy sprintf(gDeviceNames[deviceCount], "%s%" B_PRId32, sDeviceBaseName,
246*b2489546SPulkoMandy i);
247*b2489546SPulkoMandy TRACE("publishing %s\n", gDeviceNames[deviceCount]);
248*b2489546SPulkoMandy deviceCount++;
249*b2489546SPulkoMandy } else
250*b2489546SPulkoMandy TRACE_ALWAYS("publish_devices - no memory to allocate device name\n");
251*b2489546SPulkoMandy }
252*b2489546SPulkoMandy
253*b2489546SPulkoMandy gDeviceNames[deviceCount] = NULL;
254*b2489546SPulkoMandy mutex_unlock(&gDriverLock);
255*b2489546SPulkoMandy return (const char **)&gDeviceNames[0];
256*b2489546SPulkoMandy }
257*b2489546SPulkoMandy
258*b2489546SPulkoMandy
259*b2489546SPulkoMandy device_hooks *
find_device(const char * name)260*b2489546SPulkoMandy find_device(const char *name)
261*b2489546SPulkoMandy {
262*b2489546SPulkoMandy TRACE("find_device(%s)\n", name);
263*b2489546SPulkoMandy static device_hooks deviceHooks = {
264*b2489546SPulkoMandy usb_rndis_open,
265*b2489546SPulkoMandy usb_rndis_close,
266*b2489546SPulkoMandy usb_rndis_free,
267*b2489546SPulkoMandy usb_rndis_control,
268*b2489546SPulkoMandy usb_rndis_read,
269*b2489546SPulkoMandy usb_rndis_write,
270*b2489546SPulkoMandy NULL, /* select */
271*b2489546SPulkoMandy NULL /* deselect */
272*b2489546SPulkoMandy };
273*b2489546SPulkoMandy
274*b2489546SPulkoMandy return &deviceHooks;
275*b2489546SPulkoMandy }
276