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
17static const char* sDeviceBaseName = "audio/hmulti/usb/";
18
19usb_module_info* gUSBModule = NULL;
20
21Device* gDevices[MAX_DEVICES];
22char* gDeviceNames[MAX_DEVICES + 1];
23
24mutex gDriverLock;
25int32 api_version = B_CUR_DRIVER_API_VERSION;
26
27
28status_t
29usb_audio_device_added(usb_device device, void** cookie)
30{
31	*cookie = NULL;
32
33	MutexLocker _(gDriverLock);
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
84status_t
85usb_audio_device_removed(void* cookie)
86{
87	MutexLocker _(gDriverLock);
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
108status_t
109init_hardware()
110{
111	return B_OK;
112}
113
114
115status_t
116init_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
148void
149uninit_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
173static status_t
174usb_audio_open(const char* name, uint32 flags, void** cookie)
175{
176	MutexLocker _(gDriverLock);
177
178	*cookie = NULL;
179	status_t status = ENODEV;
180	for (int32 i = 0; i < MAX_DEVICES && gDevices[i] != NULL; i++) {
181		if (strcmp(gDeviceNames[i], name) == 0) {
182			status = gDevices[i]->Open(flags);
183			*cookie = gDevices[i];
184			break;
185		}
186	}
187
188	return status;
189}
190
191
192static status_t
193usb_audio_read(void* cookie, off_t position, void* buffer, size_t* numBytes)
194{
195	Device* device = (Device*)cookie;
196	return device->Read((uint8*)buffer, numBytes);
197}
198
199
200static status_t
201usb_audio_write(void* cookie, off_t position, const void* buffer,
202	size_t* numBytes)
203{
204	Device* device = (Device*)cookie;
205	return device->Write((const uint8*)buffer, numBytes);
206}
207
208
209static status_t
210usb_audio_control(void* cookie, uint32 op, void* buffer, size_t length)
211{
212	Device* device = (Device*)cookie;
213	return device->Control(op, buffer, length);
214}
215
216
217static status_t
218usb_audio_close(void* cookie)
219{
220	Device* device = (Device*)cookie;
221	return device->Close();
222}
223
224
225static status_t
226usb_audio_free(void* cookie)
227{
228	Device* device = (Device*)cookie;
229	return device->Free();
230}
231
232
233const char**
234publish_devices()
235{
236	MutexLocker _(gDriverLock);
237
238	for (int32 i = 0; gDeviceNames[i]; i++) {
239		free(gDeviceNames[i]);
240		gDeviceNames[i] = NULL;
241	}
242
243	int32 deviceCount = 0;
244	for (size_t i = 0; i < MAX_DEVICES; i++) {
245		if (gDevices[i] == NULL)
246			continue;
247
248		gDeviceNames[deviceCount] = (char*)malloc(strlen(sDeviceBaseName) + 4);
249		if (gDeviceNames[deviceCount]) {
250			sprintf(gDeviceNames[deviceCount], "%s%ld", sDeviceBaseName, i + 1);
251			TRACE(INF, "publishing %s\n", gDeviceNames[deviceCount]);
252			deviceCount++;
253		} else
254			TRACE(ERR, "Error: out of memory during allocating device name.\n");
255	}
256
257	gDeviceNames[deviceCount] = NULL;
258	return (const char**)&gDeviceNames[0];
259}
260
261
262device_hooks*
263find_device(const char* name)
264{
265	static device_hooks deviceHooks = {
266		usb_audio_open,
267		usb_audio_close,
268		usb_audio_free,
269		usb_audio_control,
270		usb_audio_read,
271		usb_audio_write,
272		NULL,				// select
273		NULL				// deselect
274	};
275
276	return &deviceHooks;
277}
278
279