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