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