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