xref: /haiku/src/add-ons/kernel/drivers/audio/usb/Driver.cpp (revision 5ffbe7d778424c9c59f00b37a3baff5c4c648790)
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/";
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 _(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 
84 status_t
85 usb_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 
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 _(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 
192 static status_t
193 usb_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 
200 static status_t
201 usb_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 
209 static status_t
210 usb_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 
217 static status_t
218 usb_audio_close(void* cookie)
219 {
220 	Device* device = (Device*)cookie;
221 	return device->Close();
222 }
223 
224 
225 static status_t
226 usb_audio_free(void* cookie)
227 {
228 	Device* device = (Device*)cookie;
229 	return device->Free();
230 }
231 
232 
233 const char**
234 publish_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 
262 device_hooks*
263 find_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