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