18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Driver for Tascam US-X2Y USB soundcards 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * FPGA Loader + ALSA Startup 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Copyright (c) 2003 by Karsten Wiese <annabellesgarden@yahoo.de> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 118c2ecf20Sopenharmony_ci#include <linux/slab.h> 128c2ecf20Sopenharmony_ci#include <linux/usb.h> 138c2ecf20Sopenharmony_ci#include <sound/core.h> 148c2ecf20Sopenharmony_ci#include <sound/memalloc.h> 158c2ecf20Sopenharmony_ci#include <sound/pcm.h> 168c2ecf20Sopenharmony_ci#include <sound/hwdep.h> 178c2ecf20Sopenharmony_ci#include "usx2y.h" 188c2ecf20Sopenharmony_ci#include "usbusx2y.h" 198c2ecf20Sopenharmony_ci#include "usX2Yhwdep.h" 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_cistatic vm_fault_t snd_us428ctls_vm_fault(struct vm_fault *vmf) 228c2ecf20Sopenharmony_ci{ 238c2ecf20Sopenharmony_ci unsigned long offset; 248c2ecf20Sopenharmony_ci struct page * page; 258c2ecf20Sopenharmony_ci void *vaddr; 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci snd_printdd("ENTER, start %lXh, pgoff %ld\n", 288c2ecf20Sopenharmony_ci vmf->vma->vm_start, 298c2ecf20Sopenharmony_ci vmf->pgoff); 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci offset = vmf->pgoff << PAGE_SHIFT; 328c2ecf20Sopenharmony_ci vaddr = (char *)((struct usx2ydev *)vmf->vma->vm_private_data)->us428ctls_sharedmem + offset; 338c2ecf20Sopenharmony_ci page = virt_to_page(vaddr); 348c2ecf20Sopenharmony_ci get_page(page); 358c2ecf20Sopenharmony_ci vmf->page = page; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci snd_printdd("vaddr=%p made us428ctls_vm_fault() page %p\n", 388c2ecf20Sopenharmony_ci vaddr, page); 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci return 0; 418c2ecf20Sopenharmony_ci} 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_cistatic const struct vm_operations_struct us428ctls_vm_ops = { 448c2ecf20Sopenharmony_ci .fault = snd_us428ctls_vm_fault, 458c2ecf20Sopenharmony_ci}; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_cistatic int snd_us428ctls_mmap(struct snd_hwdep * hw, struct file *filp, struct vm_area_struct *area) 488c2ecf20Sopenharmony_ci{ 498c2ecf20Sopenharmony_ci unsigned long size = (unsigned long)(area->vm_end - area->vm_start); 508c2ecf20Sopenharmony_ci struct usx2ydev *us428 = hw->private_data; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci // FIXME this hwdep interface is used twice: fpga download and mmap for controlling Lights etc. Maybe better using 2 hwdep devs? 538c2ecf20Sopenharmony_ci // so as long as the device isn't fully initialised yet we return -EBUSY here. 548c2ecf20Sopenharmony_ci if (!(us428->chip_status & USX2Y_STAT_CHIP_INIT)) 558c2ecf20Sopenharmony_ci return -EBUSY; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci /* if userspace tries to mmap beyond end of our buffer, fail */ 588c2ecf20Sopenharmony_ci if (size > PAGE_ALIGN(sizeof(struct us428ctls_sharedmem))) { 598c2ecf20Sopenharmony_ci snd_printd( "%lu > %lu\n", size, (unsigned long)sizeof(struct us428ctls_sharedmem)); 608c2ecf20Sopenharmony_ci return -EINVAL; 618c2ecf20Sopenharmony_ci } 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci if (!us428->us428ctls_sharedmem) { 648c2ecf20Sopenharmony_ci init_waitqueue_head(&us428->us428ctls_wait_queue_head); 658c2ecf20Sopenharmony_ci us428->us428ctls_sharedmem = alloc_pages_exact(sizeof(struct us428ctls_sharedmem), GFP_KERNEL); 668c2ecf20Sopenharmony_ci if (!us428->us428ctls_sharedmem) 678c2ecf20Sopenharmony_ci return -ENOMEM; 688c2ecf20Sopenharmony_ci memset(us428->us428ctls_sharedmem, -1, sizeof(struct us428ctls_sharedmem)); 698c2ecf20Sopenharmony_ci us428->us428ctls_sharedmem->ctl_snapshot_last = -2; 708c2ecf20Sopenharmony_ci } 718c2ecf20Sopenharmony_ci area->vm_ops = &us428ctls_vm_ops; 728c2ecf20Sopenharmony_ci area->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP; 738c2ecf20Sopenharmony_ci area->vm_private_data = hw->private_data; 748c2ecf20Sopenharmony_ci return 0; 758c2ecf20Sopenharmony_ci} 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_cistatic __poll_t snd_us428ctls_poll(struct snd_hwdep *hw, struct file *file, poll_table *wait) 788c2ecf20Sopenharmony_ci{ 798c2ecf20Sopenharmony_ci __poll_t mask = 0; 808c2ecf20Sopenharmony_ci struct usx2ydev *us428 = hw->private_data; 818c2ecf20Sopenharmony_ci struct us428ctls_sharedmem *shm = us428->us428ctls_sharedmem; 828c2ecf20Sopenharmony_ci if (us428->chip_status & USX2Y_STAT_CHIP_HUP) 838c2ecf20Sopenharmony_ci return EPOLLHUP; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci poll_wait(file, &us428->us428ctls_wait_queue_head, wait); 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci if (shm != NULL && shm->ctl_snapshot_last != shm->ctl_snapshot_red) 888c2ecf20Sopenharmony_ci mask |= EPOLLIN; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci return mask; 918c2ecf20Sopenharmony_ci} 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_cistatic int snd_usx2y_hwdep_dsp_status(struct snd_hwdep *hw, 958c2ecf20Sopenharmony_ci struct snd_hwdep_dsp_status *info) 968c2ecf20Sopenharmony_ci{ 978c2ecf20Sopenharmony_ci static const char * const type_ids[USX2Y_TYPE_NUMS] = { 988c2ecf20Sopenharmony_ci [USX2Y_TYPE_122] = "us122", 998c2ecf20Sopenharmony_ci [USX2Y_TYPE_224] = "us224", 1008c2ecf20Sopenharmony_ci [USX2Y_TYPE_428] = "us428", 1018c2ecf20Sopenharmony_ci }; 1028c2ecf20Sopenharmony_ci struct usx2ydev *us428 = hw->private_data; 1038c2ecf20Sopenharmony_ci int id = -1; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci switch (le16_to_cpu(us428->dev->descriptor.idProduct)) { 1068c2ecf20Sopenharmony_ci case USB_ID_US122: 1078c2ecf20Sopenharmony_ci id = USX2Y_TYPE_122; 1088c2ecf20Sopenharmony_ci break; 1098c2ecf20Sopenharmony_ci case USB_ID_US224: 1108c2ecf20Sopenharmony_ci id = USX2Y_TYPE_224; 1118c2ecf20Sopenharmony_ci break; 1128c2ecf20Sopenharmony_ci case USB_ID_US428: 1138c2ecf20Sopenharmony_ci id = USX2Y_TYPE_428; 1148c2ecf20Sopenharmony_ci break; 1158c2ecf20Sopenharmony_ci } 1168c2ecf20Sopenharmony_ci if (0 > id) 1178c2ecf20Sopenharmony_ci return -ENODEV; 1188c2ecf20Sopenharmony_ci strcpy(info->id, type_ids[id]); 1198c2ecf20Sopenharmony_ci info->num_dsps = 2; // 0: Prepad Data, 1: FPGA Code 1208c2ecf20Sopenharmony_ci if (us428->chip_status & USX2Y_STAT_CHIP_INIT) 1218c2ecf20Sopenharmony_ci info->chip_ready = 1; 1228c2ecf20Sopenharmony_ci info->version = USX2Y_DRIVER_VERSION; 1238c2ecf20Sopenharmony_ci return 0; 1248c2ecf20Sopenharmony_ci} 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_cistatic int usx2y_create_usbmidi(struct snd_card *card) 1288c2ecf20Sopenharmony_ci{ 1298c2ecf20Sopenharmony_ci static const struct snd_usb_midi_endpoint_info quirk_data_1 = { 1308c2ecf20Sopenharmony_ci .out_ep = 0x06, 1318c2ecf20Sopenharmony_ci .in_ep = 0x06, 1328c2ecf20Sopenharmony_ci .out_cables = 0x001, 1338c2ecf20Sopenharmony_ci .in_cables = 0x001 1348c2ecf20Sopenharmony_ci }; 1358c2ecf20Sopenharmony_ci static const struct snd_usb_audio_quirk quirk_1 = { 1368c2ecf20Sopenharmony_ci .vendor_name = "TASCAM", 1378c2ecf20Sopenharmony_ci .product_name = NAME_ALLCAPS, 1388c2ecf20Sopenharmony_ci .ifnum = 0, 1398c2ecf20Sopenharmony_ci .type = QUIRK_MIDI_FIXED_ENDPOINT, 1408c2ecf20Sopenharmony_ci .data = &quirk_data_1 1418c2ecf20Sopenharmony_ci }; 1428c2ecf20Sopenharmony_ci static const struct snd_usb_midi_endpoint_info quirk_data_2 = { 1438c2ecf20Sopenharmony_ci .out_ep = 0x06, 1448c2ecf20Sopenharmony_ci .in_ep = 0x06, 1458c2ecf20Sopenharmony_ci .out_cables = 0x003, 1468c2ecf20Sopenharmony_ci .in_cables = 0x003 1478c2ecf20Sopenharmony_ci }; 1488c2ecf20Sopenharmony_ci static const struct snd_usb_audio_quirk quirk_2 = { 1498c2ecf20Sopenharmony_ci .vendor_name = "TASCAM", 1508c2ecf20Sopenharmony_ci .product_name = "US428", 1518c2ecf20Sopenharmony_ci .ifnum = 0, 1528c2ecf20Sopenharmony_ci .type = QUIRK_MIDI_FIXED_ENDPOINT, 1538c2ecf20Sopenharmony_ci .data = &quirk_data_2 1548c2ecf20Sopenharmony_ci }; 1558c2ecf20Sopenharmony_ci struct usb_device *dev = usx2y(card)->dev; 1568c2ecf20Sopenharmony_ci struct usb_interface *iface = usb_ifnum_to_if(dev, 0); 1578c2ecf20Sopenharmony_ci const struct snd_usb_audio_quirk *quirk = 1588c2ecf20Sopenharmony_ci le16_to_cpu(dev->descriptor.idProduct) == USB_ID_US428 ? 1598c2ecf20Sopenharmony_ci &quirk_2 : &quirk_1; 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci snd_printdd("usx2y_create_usbmidi \n"); 1628c2ecf20Sopenharmony_ci return snd_usbmidi_create(card, iface, &usx2y(card)->midi_list, quirk); 1638c2ecf20Sopenharmony_ci} 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_cistatic int usx2y_create_alsa_devices(struct snd_card *card) 1668c2ecf20Sopenharmony_ci{ 1678c2ecf20Sopenharmony_ci int err; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci do { 1708c2ecf20Sopenharmony_ci if ((err = usx2y_create_usbmidi(card)) < 0) { 1718c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "usx2y_create_alsa_devices: usx2y_create_usbmidi error %i \n", err); 1728c2ecf20Sopenharmony_ci break; 1738c2ecf20Sopenharmony_ci } 1748c2ecf20Sopenharmony_ci if ((err = usx2y_audio_create(card)) < 0) 1758c2ecf20Sopenharmony_ci break; 1768c2ecf20Sopenharmony_ci if ((err = usx2y_hwdep_pcm_new(card)) < 0) 1778c2ecf20Sopenharmony_ci break; 1788c2ecf20Sopenharmony_ci if ((err = snd_card_register(card)) < 0) 1798c2ecf20Sopenharmony_ci break; 1808c2ecf20Sopenharmony_ci } while (0); 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci return err; 1838c2ecf20Sopenharmony_ci} 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_cistatic int snd_usx2y_hwdep_dsp_load(struct snd_hwdep *hw, 1868c2ecf20Sopenharmony_ci struct snd_hwdep_dsp_image *dsp) 1878c2ecf20Sopenharmony_ci{ 1888c2ecf20Sopenharmony_ci struct usx2ydev *priv = hw->private_data; 1898c2ecf20Sopenharmony_ci struct usb_device* dev = priv->dev; 1908c2ecf20Sopenharmony_ci int lret, err; 1918c2ecf20Sopenharmony_ci char *buf; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci snd_printdd( "dsp_load %s\n", dsp->name); 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci buf = memdup_user(dsp->image, dsp->length); 1968c2ecf20Sopenharmony_ci if (IS_ERR(buf)) 1978c2ecf20Sopenharmony_ci return PTR_ERR(buf); 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci err = usb_set_interface(dev, 0, 1); 2008c2ecf20Sopenharmony_ci if (err) 2018c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "usb_set_interface error \n"); 2028c2ecf20Sopenharmony_ci else 2038c2ecf20Sopenharmony_ci err = usb_bulk_msg(dev, usb_sndbulkpipe(dev, 2), buf, dsp->length, &lret, 6000); 2048c2ecf20Sopenharmony_ci kfree(buf); 2058c2ecf20Sopenharmony_ci if (err) 2068c2ecf20Sopenharmony_ci return err; 2078c2ecf20Sopenharmony_ci if (dsp->index == 1) { 2088c2ecf20Sopenharmony_ci msleep(250); // give the device some time 2098c2ecf20Sopenharmony_ci err = usx2y_async_seq04_init(priv); 2108c2ecf20Sopenharmony_ci if (err) { 2118c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "usx2y_async_seq04_init error \n"); 2128c2ecf20Sopenharmony_ci return err; 2138c2ecf20Sopenharmony_ci } 2148c2ecf20Sopenharmony_ci err = usx2y_in04_init(priv); 2158c2ecf20Sopenharmony_ci if (err) { 2168c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "usx2y_in04_init error \n"); 2178c2ecf20Sopenharmony_ci return err; 2188c2ecf20Sopenharmony_ci } 2198c2ecf20Sopenharmony_ci err = usx2y_create_alsa_devices(hw->card); 2208c2ecf20Sopenharmony_ci if (err) { 2218c2ecf20Sopenharmony_ci snd_printk(KERN_ERR "usx2y_create_alsa_devices error %i \n", err); 2228c2ecf20Sopenharmony_ci snd_card_free(hw->card); 2238c2ecf20Sopenharmony_ci return err; 2248c2ecf20Sopenharmony_ci } 2258c2ecf20Sopenharmony_ci priv->chip_status |= USX2Y_STAT_CHIP_INIT; 2268c2ecf20Sopenharmony_ci snd_printdd("%s: alsa all started\n", hw->name); 2278c2ecf20Sopenharmony_ci } 2288c2ecf20Sopenharmony_ci return err; 2298c2ecf20Sopenharmony_ci} 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ciint usx2y_hwdep_new(struct snd_card *card, struct usb_device* device) 2338c2ecf20Sopenharmony_ci{ 2348c2ecf20Sopenharmony_ci int err; 2358c2ecf20Sopenharmony_ci struct snd_hwdep *hw; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci if ((err = snd_hwdep_new(card, SND_USX2Y_LOADER_ID, 0, &hw)) < 0) 2388c2ecf20Sopenharmony_ci return err; 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci hw->iface = SNDRV_HWDEP_IFACE_USX2Y; 2418c2ecf20Sopenharmony_ci hw->private_data = usx2y(card); 2428c2ecf20Sopenharmony_ci hw->ops.dsp_status = snd_usx2y_hwdep_dsp_status; 2438c2ecf20Sopenharmony_ci hw->ops.dsp_load = snd_usx2y_hwdep_dsp_load; 2448c2ecf20Sopenharmony_ci hw->ops.mmap = snd_us428ctls_mmap; 2458c2ecf20Sopenharmony_ci hw->ops.poll = snd_us428ctls_poll; 2468c2ecf20Sopenharmony_ci hw->exclusive = 1; 2478c2ecf20Sopenharmony_ci sprintf(hw->name, "/dev/bus/usb/%03d/%03d", device->bus->busnum, device->devnum); 2488c2ecf20Sopenharmony_ci return 0; 2498c2ecf20Sopenharmony_ci} 2508c2ecf20Sopenharmony_ci 251