xref: /haiku/src/add-ons/kernel/drivers/network/ether/usb_ecm/Driver.cpp (revision 4c8e85b316c35a9161f5a1c50ad70bc91c83a76f)
1 /*
2 	Driver for USB Ethernet Control Model devices
3 	Copyright (C) 2008 Michael Lotz <mmlr@mlotz.ch>
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 "ECMDevice.h"
13 
14 int32 api_version = B_CUR_DRIVER_API_VERSION;
15 static const char *sDeviceBaseName = "net/usb_ecm/";
16 ECMDevice *gECMDevices[MAX_DEVICES];
17 char *gDeviceNames[MAX_DEVICES + 1];
18 usb_module_info *gUSBModule = NULL;
19 mutex gDriverLock;
20 
21 
22 status_t
23 usb_ecm_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 (gECMDevices[i] == NULL)
31 			continue;
32 
33 		if (gECMDevices[i]->CompareAndReattach(device) != B_OK)
34 			continue;
35 
36 		TRACE_ALWAYS("ecm device %" B_PRId32 " replugged\n", i);
37 		*cookie = gECMDevices[i];
38 		mutex_unlock(&gDriverLock);
39 		return B_OK;
40 	}
41 
42 	// no such device yet, create a new one
43 	ECMDevice *ecmDevice = new ECMDevice(device);
44 	status_t status = ecmDevice->InitCheck();
45 	if (status < B_OK) {
46 		delete ecmDevice;
47 		mutex_unlock(&gDriverLock);
48 		return status;
49 	}
50 
51 	for (int32 i = 0; i < MAX_DEVICES; i++) {
52 		if (gECMDevices[i] != NULL)
53 			continue;
54 
55 		gECMDevices[i] = ecmDevice;
56 		*cookie = ecmDevice;
57 
58 		TRACE_ALWAYS("ecm device %" B_PRId32 " added\n", i);
59 		mutex_unlock(&gDriverLock);
60 		return B_OK;
61 	}
62 
63 	// no space for the device
64 	delete ecmDevice;
65 	mutex_unlock(&gDriverLock);
66 	return B_ERROR;
67 }
68 
69 
70 status_t
71 usb_ecm_device_removed(void *cookie)
72 {
73 	mutex_lock(&gDriverLock);
74 
75 	ECMDevice *device = (ECMDevice *)cookie;
76 	for (int32 i = 0; i < MAX_DEVICES; i++) {
77 		if (gECMDevices[i] == device) {
78 			if (device->IsOpen()) {
79 				// the device will be deleted upon being freed
80 				device->Removed();
81 			} else {
82 				gECMDevices[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 
97 status_t
98 init_hardware()
99 {
100 	TRACE("init_hardware()\n");
101 	return B_OK;
102 }
103 
104 
105 status_t
106 init_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 		gECMDevices[i] = NULL;
116 
117 	gDeviceNames[0] = NULL;
118 	mutex_init(&gDriverLock, DRIVER_NAME"_devices");
119 
120 	static usb_notify_hooks notifyHooks = {
121 		&usb_ecm_device_added,
122 		&usb_ecm_device_removed
123 	};
124 
125 	static usb_support_descriptor supportDescriptor = {
126 		USB_INTERFACE_CLASS_CDC, /* CDC - Communication Device Class */
127 		USB_INTERFACE_SUBCLASS_ECM, /* ECM - Ethernet Control Model */
128 		0, 0, 0 /* no protocol, 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 
137 void
138 uninit_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 (gECMDevices[i]) {
146 			delete gECMDevices[i];
147 			gECMDevices[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 
161 static status_t
162 usb_ecm_open(const char *name, uint32 flags, void **cookie)
163 {
164 	TRACE("open(%s, %lu, %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 && gECMDevices[index]) {
171 		status = gECMDevices[index]->Open();
172 		*cookie = gECMDevices[index];
173 	}
174 
175 	mutex_unlock(&gDriverLock);
176 	return status;
177 }
178 
179 
180 static status_t
181 usb_ecm_read(void *cookie, off_t position, void *buffer, size_t *numBytes)
182 {
183 	TRACE("read(%p, %Ld, %p, %lu)\n", cookie, position, buffer, *numBytes);
184 	ECMDevice *device = (ECMDevice *)cookie;
185 	return device->Read((uint8 *)buffer, numBytes);
186 }
187 
188 
189 static status_t
190 usb_ecm_write(void *cookie, off_t position, const void *buffer,
191 	size_t *numBytes)
192 {
193 	TRACE("write(%p, %Ld, %p, %lu)\n", cookie, position, buffer, *numBytes);
194 	ECMDevice *device = (ECMDevice *)cookie;
195 	return device->Write((const uint8 *)buffer, numBytes);
196 }
197 
198 
199 static status_t
200 usb_ecm_control(void *cookie, uint32 op, void *buffer, size_t length)
201 {
202 	TRACE("control(%p, %lu, %p, %lu)\n", cookie, op, buffer, length);
203 	ECMDevice *device = (ECMDevice *)cookie;
204 	return device->Control(op, buffer, length);
205 }
206 
207 
208 static status_t
209 usb_ecm_close(void *cookie)
210 {
211 	TRACE("close(%p)\n", cookie);
212 	ECMDevice *device = (ECMDevice *)cookie;
213 	return device->Close();
214 }
215 
216 
217 static status_t
218 usb_ecm_free(void *cookie)
219 {
220 	TRACE("free(%p)\n", cookie);
221 	ECMDevice *device = (ECMDevice *)cookie;
222 	mutex_lock(&gDriverLock);
223 	status_t status = device->Free();
224 	for (int32 i = 0; i < MAX_DEVICES; i++) {
225 		if (gECMDevices[i] == device) {
226 			// the device is removed already but as it was open the
227 			// removed hook has not deleted the object
228 			gECMDevices[i] = NULL;
229 			delete device;
230 			break;
231 		}
232 	}
233 
234 	mutex_unlock(&gDriverLock);
235 	return status;
236 }
237 
238 
239 const char **
240 publish_devices()
241 {
242 	TRACE("publish_devices()\n");
243 	for (int32 i = 0; gDeviceNames[i]; i++) {
244 		free(gDeviceNames[i]);
245 		gDeviceNames[i] = NULL;
246 	}
247 
248 	int32 deviceCount = 0;
249 	mutex_lock(&gDriverLock);
250 	for (int32 i = 0; i < MAX_DEVICES; i++) {
251 		if (gECMDevices[i] == NULL)
252 			continue;
253 
254 		gDeviceNames[deviceCount] = (char *)malloc(strlen(sDeviceBaseName) + 4);
255 		if (gDeviceNames[deviceCount]) {
256 			sprintf(gDeviceNames[deviceCount], "%s%" B_PRId32, sDeviceBaseName,
257 				i);
258 			TRACE("publishing %s\n", gDeviceNames[deviceCount]);
259 			deviceCount++;
260 		} else
261 			TRACE_ALWAYS("publish_devices - no memory to allocate device name\n");
262 	}
263 
264 	gDeviceNames[deviceCount] = NULL;
265 	mutex_unlock(&gDriverLock);
266 	return (const char **)&gDeviceNames[0];
267 }
268 
269 
270 device_hooks *
271 find_device(const char *name)
272 {
273 	TRACE("find_device(%s)\n", name);
274 	static device_hooks deviceHooks = {
275 		usb_ecm_open,
276 		usb_ecm_close,
277 		usb_ecm_free,
278 		usb_ecm_control,
279 		usb_ecm_read,
280 		usb_ecm_write,
281 		NULL,				/* select */
282 		NULL				/* deselect */
283 	};
284 
285 	return &deviceHooks;
286 }
287