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, ¬ifyHooks); 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