xref: /haiku/src/add-ons/kernel/drivers/audio/usb/Driver.cpp (revision e81a954787e50e56a7f06f72705b7859b6ab06d1)
1 /*
2  *	Driver for USB Audio Device Class devices.
3  *	Copyright (c) 2009-13 S.Zharski <imker@gmx.li>
4  *	Distributed under the terms of the MIT license.
5  *
6  */
7 
8 #include "Driver.h"
9 
10 #include <AutoLock.h>
11 #include <usb/USB_audio.h>
12 
13 #include "Device.h"
14 #include "Settings.h"
15 
16 
17 static const char* sDeviceBaseName = "audio/hmulti/usb_audio/";
18 
19 usb_module_info* gUSBModule = NULL;
20 
21 Device* gDevices[MAX_DEVICES];
22 char* gDeviceNames[MAX_DEVICES + 1];
23 
24 mutex gDriverLock;
25 int32 api_version = B_CUR_DRIVER_API_VERSION;
26 
27 
28 status_t
29 usb_audio_device_added(usb_device device, void** cookie)
30 {
31 	*cookie = NULL;
32 
33 	MutexLocker driverLock;
34 
35 	// check if this is a replug of an existing device first
36 	for (int32 i = 0; i < MAX_DEVICES; i++) {
37 		if (gDevices[i] == NULL)
38 			continue;
39 
40 		if (gDevices[i]->CompareAndReattach(device) != B_OK)
41 			continue;
42 
43 		TRACE(INF, "The device is plugged back. Use entry at %ld.\n", i);
44 		*cookie = gDevices[i];
45 		return B_OK;
46 	}
47 
48 	// no such device yet, create a new one
49 	Device* audioDevice = new(std::nothrow) Device(device);
50 	if (audioDevice == 0)
51 		return ENODEV;
52 
53 	status_t status = audioDevice->InitCheck();
54 	if (status < B_OK) {
55 		delete audioDevice;
56 		return status;
57 	}
58 
59 	status = audioDevice->SetupDevice(false);
60 	if (status < B_OK) {
61 		delete audioDevice;
62 		return status;
63 	}
64 
65 	for (int32 i = 0; i < MAX_DEVICES; i++) {
66 		if (gDevices[i] != NULL)
67 			continue;
68 
69 		gDevices[i] = audioDevice;
70 		*cookie = audioDevice;
71 
72 		TRACE(INF, "New device is added at %ld.\n", i);
73 		return B_OK;
74 	}
75 
76 	// no space for the device
77 	TRACE(ERR, "Error: no more device entries availble.\n");
78 
79 	delete audioDevice;
80 	return B_ERROR;
81 }
82 
83 
84 status_t
85 usb_audio_device_removed(void* cookie)
86 {
87 	MutexLocker driverLock;
88 
89 	Device* device = (Device*)cookie;
90 	for (int32 i = 0; i < MAX_DEVICES; i++) {
91 		if (gDevices[i] == device) {
92 			if (device->IsOpen()) {
93 				// the device will be deleted upon being freed
94 				device->Removed();
95 			} else {
96 				gDevices[i] = NULL;
97 				delete device;
98 				TRACE(INF, "Device at %ld deleted.\n", i);
99 			}
100 			break;
101 		}
102 	}
103 
104 	return B_OK;
105 }
106 
107 
108 status_t
109 init_hardware()
110 {
111 	return B_OK;
112 }
113 
114 
115 status_t
116 init_driver()
117 {
118 	status_t status = get_module(B_USB_MODULE_NAME,
119 		(module_info**)&gUSBModule);
120 	if (status < B_OK)
121 		return status;
122 
123 	load_settings();
124 
125 	TRACE(ERR, "%s\n", kVersion); // TODO: always???
126 
127 	for (int32 i = 0; i < MAX_DEVICES; i++)
128 		gDevices[i] = NULL;
129 
130 	gDeviceNames[0] = NULL;
131 	mutex_init(&gDriverLock, DRIVER_NAME"_devices");
132 
133 	static usb_notify_hooks notifyHooks = {
134 		&usb_audio_device_added,
135 		&usb_audio_device_removed
136 	};
137 
138 	static usb_support_descriptor supportedDevices[] = {
139 		{ USB_AUDIO_INTERFACE_AUDIO_CLASS, 0, 0, 0, 0 }
140 	};
141 
142 	gUSBModule->register_driver(DRIVER_NAME, supportedDevices, 0, NULL);
143 	gUSBModule->install_notify(DRIVER_NAME, &notifyHooks);
144 	return B_OK;
145 }
146 
147 
148 void
149 uninit_driver()
150 {
151 	gUSBModule->uninstall_notify(DRIVER_NAME);
152 	mutex_lock(&gDriverLock);
153 
154 	for (int32 i = 0; i < MAX_DEVICES; i++) {
155 		if (gDevices[i]) {
156 			delete gDevices[i];
157 			gDevices[i] = NULL;
158 		}
159 	}
160 
161 	for (int32 i = 0; gDeviceNames[i]; i++) {
162 		free(gDeviceNames[i]);
163 		gDeviceNames[i] = NULL;
164 	}
165 
166 	mutex_destroy(&gDriverLock);
167 	put_module(B_USB_MODULE_NAME);
168 
169 	release_settings();
170 }
171 
172 
173 static status_t
174 usb_audio_open(const char* name, uint32 flags, void** cookie)
175 {
176 	MutexLocker driverLock;
177 
178 	*cookie = NULL;
179 	status_t status = ENODEV;
180 	int32 index = strtol(name + strlen(sDeviceBaseName), NULL, 10) - 1;
181 	if (index >= 0 && index < MAX_DEVICES && gDevices[index]) {
182 		status = gDevices[index]->Open(flags);
183 		*cookie = gDevices[index];
184 	}
185 
186 	return status;
187 }
188 
189 
190 static status_t
191 usb_audio_read(void* cookie, off_t position, void* buffer, size_t* numBytes)
192 {
193 	Device* device = (Device*)cookie;
194 	return device->Read((uint8*)buffer, numBytes);
195 }
196 
197 
198 static status_t
199 usb_audio_write(void* cookie, off_t position, const void* buffer,
200 	size_t* numBytes)
201 {
202 	Device* device = (Device*)cookie;
203 	return device->Write((const uint8*)buffer, numBytes);
204 }
205 
206 
207 static status_t
208 usb_audio_control(void* cookie, uint32 op, void* buffer, size_t length)
209 {
210 	Device* device = (Device*)cookie;
211 	return device->Control(op, buffer, length);
212 }
213 
214 
215 static status_t
216 usb_audio_close(void* cookie)
217 {
218 	Device* device = (Device*)cookie;
219 	return device->Close();
220 }
221 
222 
223 static status_t
224 usb_audio_free(void* cookie)
225 {
226 	Device* device = (Device*)cookie;
227 
228 	MutexLocker driverLock;
229 
230 	status_t status = device->Free();
231 	for (int32 i = 0; i < MAX_DEVICES; i++) {
232 		if (gDevices[i] == device) {
233 			// the device is removed already but as it was open the
234 			// removed hook has not deleted the object
235 			gDevices[i] = NULL;
236 			delete device;
237 			TRACE(INF, "Device at %ld deleted.\n", i);
238 			break;
239 		}
240 	}
241 
242 	return status;
243 }
244 
245 
246 const char**
247 publish_devices()
248 {
249 	for (int32 i = 0; gDeviceNames[i]; i++) {
250 		free(gDeviceNames[i]);
251 		gDeviceNames[i] = NULL;
252 	}
253 
254 	MutexLocker driverLock;
255 
256 	int32 deviceCount = 0;
257 	for (size_t i = 0; i < MAX_DEVICES; i++) {
258 		if (gDevices[i] == NULL)
259 			continue;
260 
261 		gDeviceNames[deviceCount] = (char*)malloc(strlen(sDeviceBaseName) + 4);
262 		if (gDeviceNames[deviceCount]) {
263 			sprintf(gDeviceNames[deviceCount], "%s%ld", sDeviceBaseName, i + 1);
264 			TRACE(INF, "publishing %s\n", gDeviceNames[deviceCount]);
265 			deviceCount++;
266 		} else
267 			TRACE(ERR, "Error: out of memory during allocating device name.\n");
268 	}
269 
270 	gDeviceNames[deviceCount] = NULL;
271 	return (const char**)&gDeviceNames[0];
272 }
273 
274 
275 device_hooks*
276 find_device(const char* name)
277 {
278 	static device_hooks deviceHooks = {
279 		usb_audio_open,
280 		usb_audio_close,
281 		usb_audio_free,
282 		usb_audio_control,
283 		usb_audio_read,
284 		usb_audio_write,
285 		NULL,				// select
286 		NULL				// deselect
287 	};
288 
289 	return &deviceHooks;
290 }
291 
292