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_audio/"; 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 driverLock; 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 driverLock; 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, ¬ifyHooks); 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 driverLock; 177 178 *cookie = NULL; 179 status_t status = ENODEV; 180 int32 index = strtol(name + strlen(sDeviceBaseName), NULL, 10) - 1; 181 if (index >= 0 && index < MAX_DEVICES && gDevices[index]) { 182 status = gDevices[index]->Open(flags); 183 *cookie = gDevices[index]; 184 } 185 186 return status; 187 } 188 189 190 static status_t 191 usb_audio_read(void* cookie, off_t position, void* buffer, size_t* numBytes) 192 { 193 Device* device = (Device*)cookie; 194 return device->Read((uint8*)buffer, numBytes); 195 } 196 197 198 static status_t 199 usb_audio_write(void* cookie, off_t position, const void* buffer, 200 size_t* numBytes) 201 { 202 Device* device = (Device*)cookie; 203 return device->Write((const uint8*)buffer, numBytes); 204 } 205 206 207 static status_t 208 usb_audio_control(void* cookie, uint32 op, void* buffer, size_t length) 209 { 210 Device* device = (Device*)cookie; 211 return device->Control(op, buffer, length); 212 } 213 214 215 static status_t 216 usb_audio_close(void* cookie) 217 { 218 Device* device = (Device*)cookie; 219 return device->Close(); 220 } 221 222 223 static status_t 224 usb_audio_free(void* cookie) 225 { 226 Device* device = (Device*)cookie; 227 228 MutexLocker driverLock; 229 230 status_t status = device->Free(); 231 for (int32 i = 0; i < MAX_DEVICES; i++) { 232 if (gDevices[i] == device) { 233 // the device is removed already but as it was open the 234 // removed hook has not deleted the object 235 gDevices[i] = NULL; 236 delete device; 237 TRACE(INF, "Device at %ld deleted.\n", i); 238 break; 239 } 240 } 241 242 return status; 243 } 244 245 246 const char** 247 publish_devices() 248 { 249 for (int32 i = 0; gDeviceNames[i]; i++) { 250 free(gDeviceNames[i]); 251 gDeviceNames[i] = NULL; 252 } 253 254 MutexLocker driverLock; 255 256 int32 deviceCount = 0; 257 for (size_t i = 0; i < MAX_DEVICES; i++) { 258 if (gDevices[i] == NULL) 259 continue; 260 261 gDeviceNames[deviceCount] = (char*)malloc(strlen(sDeviceBaseName) + 4); 262 if (gDeviceNames[deviceCount]) { 263 sprintf(gDeviceNames[deviceCount], "%s%ld", sDeviceBaseName, i + 1); 264 TRACE(INF, "publishing %s\n", gDeviceNames[deviceCount]); 265 deviceCount++; 266 } else 267 TRACE(ERR, "Error: out of memory during allocating device name.\n"); 268 } 269 270 gDeviceNames[deviceCount] = NULL; 271 return (const char**)&gDeviceNames[0]; 272 } 273 274 275 device_hooks* 276 find_device(const char* name) 277 { 278 static device_hooks deviceHooks = { 279 usb_audio_open, 280 usb_audio_close, 281 usb_audio_free, 282 usb_audio_control, 283 usb_audio_read, 284 usb_audio_write, 285 NULL, // select 286 NULL // deselect 287 }; 288 289 return &deviceHooks; 290 } 291 292