xref: /haiku/src/add-ons/kernel/drivers/network/ether/usb_ecm/Driver.cpp (revision 6f80a9801fedbe7355c4360bd204ba746ec3ec2d)
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] != NULL) {
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] != NULL) {
171 		status = gECMDevices[index]->Open();
172 		if (status == B_OK)
173 			*cookie = gECMDevices[index];
174 	}
175 
176 	mutex_unlock(&gDriverLock);
177 	return status;
178 }
179 
180 
181 static status_t
182 usb_ecm_read(void *cookie, off_t position, void *buffer, size_t *numBytes)
183 {
184 	TRACE("read(%p, %Ld, %p, %lu)\n", cookie, position, buffer, *numBytes);
185 	ECMDevice *device = (ECMDevice *)cookie;
186 	return device->Read((uint8 *)buffer, numBytes);
187 }
188 
189 
190 static status_t
191 usb_ecm_write(void *cookie, off_t position, const void *buffer,
192 	size_t *numBytes)
193 {
194 	TRACE("write(%p, %Ld, %p, %lu)\n", cookie, position, buffer, *numBytes);
195 	ECMDevice *device = (ECMDevice *)cookie;
196 	return device->Write((const uint8 *)buffer, numBytes);
197 }
198 
199 
200 static status_t
201 usb_ecm_control(void *cookie, uint32 op, void *buffer, size_t length)
202 {
203 	TRACE("control(%p, %lu, %p, %lu)\n", cookie, op, buffer, length);
204 	ECMDevice *device = (ECMDevice *)cookie;
205 	return device->Control(op, buffer, length);
206 }
207 
208 
209 static status_t
210 usb_ecm_close(void *cookie)
211 {
212 	TRACE("close(%p)\n", cookie);
213 	ECMDevice *device = (ECMDevice *)cookie;
214 	return device->Close();
215 }
216 
217 
218 static status_t
219 usb_ecm_free(void *cookie)
220 {
221 	TRACE("free(%p)\n", cookie);
222 	ECMDevice *device = (ECMDevice *)cookie;
223 	mutex_lock(&gDriverLock);
224 	status_t status = device->Free();
225 	for (int32 i = 0; i < MAX_DEVICES; i++) {
226 		if (gECMDevices[i] == device) {
227 			// the device is removed already but as it was open the
228 			// removed hook has not deleted the object
229 			gECMDevices[i] = NULL;
230 			delete device;
231 			break;
232 		}
233 	}
234 
235 	mutex_unlock(&gDriverLock);
236 	return status;
237 }
238 
239 
240 const char **
241 publish_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 (gECMDevices[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 
271 device_hooks *
272 find_device(const char *name)
273 {
274 	TRACE("find_device(%s)\n", name);
275 	static device_hooks deviceHooks = {
276 		usb_ecm_open,
277 		usb_ecm_close,
278 		usb_ecm_free,
279 		usb_ecm_control,
280 		usb_ecm_read,
281 		usb_ecm_write,
282 		NULL,				/* select */
283 		NULL				/* deselect */
284 	};
285 
286 	return &deviceHooks;
287 }
288