162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Driver for Tascam US-X2Y USB soundcards 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * FPGA Loader + ALSA Startup 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Copyright (c) 2003 by Karsten Wiese <annabellesgarden@yahoo.de> 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/interrupt.h> 1162306a36Sopenharmony_ci#include <linux/slab.h> 1262306a36Sopenharmony_ci#include <linux/usb.h> 1362306a36Sopenharmony_ci#include <sound/core.h> 1462306a36Sopenharmony_ci#include <sound/memalloc.h> 1562306a36Sopenharmony_ci#include <sound/pcm.h> 1662306a36Sopenharmony_ci#include <sound/hwdep.h> 1762306a36Sopenharmony_ci#include "usx2y.h" 1862306a36Sopenharmony_ci#include "usbusx2y.h" 1962306a36Sopenharmony_ci#include "usX2Yhwdep.h" 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_cistatic vm_fault_t snd_us428ctls_vm_fault(struct vm_fault *vmf) 2262306a36Sopenharmony_ci{ 2362306a36Sopenharmony_ci unsigned long offset; 2462306a36Sopenharmony_ci struct page *page; 2562306a36Sopenharmony_ci void *vaddr; 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci snd_printdd("ENTER, start %lXh, pgoff %ld\n", 2862306a36Sopenharmony_ci vmf->vma->vm_start, 2962306a36Sopenharmony_ci vmf->pgoff); 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci offset = vmf->pgoff << PAGE_SHIFT; 3262306a36Sopenharmony_ci vaddr = (char *)((struct usx2ydev *)vmf->vma->vm_private_data)->us428ctls_sharedmem + offset; 3362306a36Sopenharmony_ci page = virt_to_page(vaddr); 3462306a36Sopenharmony_ci get_page(page); 3562306a36Sopenharmony_ci vmf->page = page; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci snd_printdd("vaddr=%p made us428ctls_vm_fault() page %p\n", 3862306a36Sopenharmony_ci vaddr, page); 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci return 0; 4162306a36Sopenharmony_ci} 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistatic const struct vm_operations_struct us428ctls_vm_ops = { 4462306a36Sopenharmony_ci .fault = snd_us428ctls_vm_fault, 4562306a36Sopenharmony_ci}; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_cistatic int snd_us428ctls_mmap(struct snd_hwdep *hw, struct file *filp, struct vm_area_struct *area) 4862306a36Sopenharmony_ci{ 4962306a36Sopenharmony_ci unsigned long size = (unsigned long)(area->vm_end - area->vm_start); 5062306a36Sopenharmony_ci struct usx2ydev *us428 = hw->private_data; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci // FIXME this hwdep interface is used twice: fpga download and mmap for controlling Lights etc. Maybe better using 2 hwdep devs? 5362306a36Sopenharmony_ci // so as long as the device isn't fully initialised yet we return -EBUSY here. 5462306a36Sopenharmony_ci if (!(us428->chip_status & USX2Y_STAT_CHIP_INIT)) 5562306a36Sopenharmony_ci return -EBUSY; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci /* if userspace tries to mmap beyond end of our buffer, fail */ 5862306a36Sopenharmony_ci if (size > US428_SHAREDMEM_PAGES) { 5962306a36Sopenharmony_ci snd_printd("%lu > %lu\n", size, (unsigned long)US428_SHAREDMEM_PAGES); 6062306a36Sopenharmony_ci return -EINVAL; 6162306a36Sopenharmony_ci } 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci area->vm_ops = &us428ctls_vm_ops; 6462306a36Sopenharmony_ci vm_flags_set(area, VM_DONTEXPAND | VM_DONTDUMP); 6562306a36Sopenharmony_ci area->vm_private_data = hw->private_data; 6662306a36Sopenharmony_ci return 0; 6762306a36Sopenharmony_ci} 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_cistatic __poll_t snd_us428ctls_poll(struct snd_hwdep *hw, struct file *file, poll_table *wait) 7062306a36Sopenharmony_ci{ 7162306a36Sopenharmony_ci __poll_t mask = 0; 7262306a36Sopenharmony_ci struct usx2ydev *us428 = hw->private_data; 7362306a36Sopenharmony_ci struct us428ctls_sharedmem *shm = us428->us428ctls_sharedmem; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci if (us428->chip_status & USX2Y_STAT_CHIP_HUP) 7662306a36Sopenharmony_ci return EPOLLHUP; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci poll_wait(file, &us428->us428ctls_wait_queue_head, wait); 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci if (shm && shm->ctl_snapshot_last != shm->ctl_snapshot_red) 8162306a36Sopenharmony_ci mask |= EPOLLIN; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci return mask; 8462306a36Sopenharmony_ci} 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_cistatic int snd_usx2y_hwdep_dsp_status(struct snd_hwdep *hw, 8862306a36Sopenharmony_ci struct snd_hwdep_dsp_status *info) 8962306a36Sopenharmony_ci{ 9062306a36Sopenharmony_ci static const char * const type_ids[USX2Y_TYPE_NUMS] = { 9162306a36Sopenharmony_ci [USX2Y_TYPE_122] = "us122", 9262306a36Sopenharmony_ci [USX2Y_TYPE_224] = "us224", 9362306a36Sopenharmony_ci [USX2Y_TYPE_428] = "us428", 9462306a36Sopenharmony_ci }; 9562306a36Sopenharmony_ci struct usx2ydev *us428 = hw->private_data; 9662306a36Sopenharmony_ci int id = -1; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci switch (le16_to_cpu(us428->dev->descriptor.idProduct)) { 9962306a36Sopenharmony_ci case USB_ID_US122: 10062306a36Sopenharmony_ci id = USX2Y_TYPE_122; 10162306a36Sopenharmony_ci break; 10262306a36Sopenharmony_ci case USB_ID_US224: 10362306a36Sopenharmony_ci id = USX2Y_TYPE_224; 10462306a36Sopenharmony_ci break; 10562306a36Sopenharmony_ci case USB_ID_US428: 10662306a36Sopenharmony_ci id = USX2Y_TYPE_428; 10762306a36Sopenharmony_ci break; 10862306a36Sopenharmony_ci } 10962306a36Sopenharmony_ci if (id < 0) 11062306a36Sopenharmony_ci return -ENODEV; 11162306a36Sopenharmony_ci strcpy(info->id, type_ids[id]); 11262306a36Sopenharmony_ci info->num_dsps = 2; // 0: Prepad Data, 1: FPGA Code 11362306a36Sopenharmony_ci if (us428->chip_status & USX2Y_STAT_CHIP_INIT) 11462306a36Sopenharmony_ci info->chip_ready = 1; 11562306a36Sopenharmony_ci info->version = USX2Y_DRIVER_VERSION; 11662306a36Sopenharmony_ci return 0; 11762306a36Sopenharmony_ci} 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_cistatic int usx2y_create_usbmidi(struct snd_card *card) 12062306a36Sopenharmony_ci{ 12162306a36Sopenharmony_ci static const struct snd_usb_midi_endpoint_info quirk_data_1 = { 12262306a36Sopenharmony_ci .out_ep = 0x06, 12362306a36Sopenharmony_ci .in_ep = 0x06, 12462306a36Sopenharmony_ci .out_cables = 0x001, 12562306a36Sopenharmony_ci .in_cables = 0x001 12662306a36Sopenharmony_ci }; 12762306a36Sopenharmony_ci static const struct snd_usb_audio_quirk quirk_1 = { 12862306a36Sopenharmony_ci .vendor_name = "TASCAM", 12962306a36Sopenharmony_ci .product_name = NAME_ALLCAPS, 13062306a36Sopenharmony_ci .ifnum = 0, 13162306a36Sopenharmony_ci .type = QUIRK_MIDI_FIXED_ENDPOINT, 13262306a36Sopenharmony_ci .data = &quirk_data_1 13362306a36Sopenharmony_ci }; 13462306a36Sopenharmony_ci static const struct snd_usb_midi_endpoint_info quirk_data_2 = { 13562306a36Sopenharmony_ci .out_ep = 0x06, 13662306a36Sopenharmony_ci .in_ep = 0x06, 13762306a36Sopenharmony_ci .out_cables = 0x003, 13862306a36Sopenharmony_ci .in_cables = 0x003 13962306a36Sopenharmony_ci }; 14062306a36Sopenharmony_ci static const struct snd_usb_audio_quirk quirk_2 = { 14162306a36Sopenharmony_ci .vendor_name = "TASCAM", 14262306a36Sopenharmony_ci .product_name = "US428", 14362306a36Sopenharmony_ci .ifnum = 0, 14462306a36Sopenharmony_ci .type = QUIRK_MIDI_FIXED_ENDPOINT, 14562306a36Sopenharmony_ci .data = &quirk_data_2 14662306a36Sopenharmony_ci }; 14762306a36Sopenharmony_ci struct usb_device *dev = usx2y(card)->dev; 14862306a36Sopenharmony_ci struct usb_interface *iface = usb_ifnum_to_if(dev, 0); 14962306a36Sopenharmony_ci const struct snd_usb_audio_quirk *quirk = 15062306a36Sopenharmony_ci le16_to_cpu(dev->descriptor.idProduct) == USB_ID_US428 ? 15162306a36Sopenharmony_ci &quirk_2 : &quirk_1; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci snd_printdd("%s\n", __func__); 15462306a36Sopenharmony_ci return snd_usbmidi_create(card, iface, &usx2y(card)->midi_list, quirk); 15562306a36Sopenharmony_ci} 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_cistatic int usx2y_create_alsa_devices(struct snd_card *card) 15862306a36Sopenharmony_ci{ 15962306a36Sopenharmony_ci int err; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci err = usx2y_create_usbmidi(card); 16262306a36Sopenharmony_ci if (err < 0) { 16362306a36Sopenharmony_ci snd_printk(KERN_ERR "%s: usx2y_create_usbmidi error %i\n", __func__, err); 16462306a36Sopenharmony_ci return err; 16562306a36Sopenharmony_ci } 16662306a36Sopenharmony_ci err = usx2y_audio_create(card); 16762306a36Sopenharmony_ci if (err < 0) 16862306a36Sopenharmony_ci return err; 16962306a36Sopenharmony_ci err = usx2y_hwdep_pcm_new(card); 17062306a36Sopenharmony_ci if (err < 0) 17162306a36Sopenharmony_ci return err; 17262306a36Sopenharmony_ci err = snd_card_register(card); 17362306a36Sopenharmony_ci if (err < 0) 17462306a36Sopenharmony_ci return err; 17562306a36Sopenharmony_ci return 0; 17662306a36Sopenharmony_ci} 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_cistatic int snd_usx2y_hwdep_dsp_load(struct snd_hwdep *hw, 17962306a36Sopenharmony_ci struct snd_hwdep_dsp_image *dsp) 18062306a36Sopenharmony_ci{ 18162306a36Sopenharmony_ci struct usx2ydev *priv = hw->private_data; 18262306a36Sopenharmony_ci struct usb_device *dev = priv->dev; 18362306a36Sopenharmony_ci int lret, err; 18462306a36Sopenharmony_ci char *buf; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci snd_printdd("dsp_load %s\n", dsp->name); 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci buf = memdup_user(dsp->image, dsp->length); 18962306a36Sopenharmony_ci if (IS_ERR(buf)) 19062306a36Sopenharmony_ci return PTR_ERR(buf); 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci err = usb_set_interface(dev, 0, 1); 19362306a36Sopenharmony_ci if (err) 19462306a36Sopenharmony_ci snd_printk(KERN_ERR "usb_set_interface error\n"); 19562306a36Sopenharmony_ci else 19662306a36Sopenharmony_ci err = usb_bulk_msg(dev, usb_sndbulkpipe(dev, 2), buf, dsp->length, &lret, 6000); 19762306a36Sopenharmony_ci kfree(buf); 19862306a36Sopenharmony_ci if (err) 19962306a36Sopenharmony_ci return err; 20062306a36Sopenharmony_ci if (dsp->index == 1) { 20162306a36Sopenharmony_ci msleep(250); // give the device some time 20262306a36Sopenharmony_ci err = usx2y_async_seq04_init(priv); 20362306a36Sopenharmony_ci if (err) { 20462306a36Sopenharmony_ci snd_printk(KERN_ERR "usx2y_async_seq04_init error\n"); 20562306a36Sopenharmony_ci return err; 20662306a36Sopenharmony_ci } 20762306a36Sopenharmony_ci err = usx2y_in04_init(priv); 20862306a36Sopenharmony_ci if (err) { 20962306a36Sopenharmony_ci snd_printk(KERN_ERR "usx2y_in04_init error\n"); 21062306a36Sopenharmony_ci return err; 21162306a36Sopenharmony_ci } 21262306a36Sopenharmony_ci err = usx2y_create_alsa_devices(hw->card); 21362306a36Sopenharmony_ci if (err) { 21462306a36Sopenharmony_ci snd_printk(KERN_ERR "usx2y_create_alsa_devices error %i\n", err); 21562306a36Sopenharmony_ci return err; 21662306a36Sopenharmony_ci } 21762306a36Sopenharmony_ci priv->chip_status |= USX2Y_STAT_CHIP_INIT; 21862306a36Sopenharmony_ci snd_printdd("%s: alsa all started\n", hw->name); 21962306a36Sopenharmony_ci } 22062306a36Sopenharmony_ci return err; 22162306a36Sopenharmony_ci} 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ciint usx2y_hwdep_new(struct snd_card *card, struct usb_device *device) 22462306a36Sopenharmony_ci{ 22562306a36Sopenharmony_ci int err; 22662306a36Sopenharmony_ci struct snd_hwdep *hw; 22762306a36Sopenharmony_ci struct usx2ydev *us428 = usx2y(card); 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci err = snd_hwdep_new(card, SND_USX2Y_LOADER_ID, 0, &hw); 23062306a36Sopenharmony_ci if (err < 0) 23162306a36Sopenharmony_ci return err; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci hw->iface = SNDRV_HWDEP_IFACE_USX2Y; 23462306a36Sopenharmony_ci hw->private_data = us428; 23562306a36Sopenharmony_ci hw->ops.dsp_status = snd_usx2y_hwdep_dsp_status; 23662306a36Sopenharmony_ci hw->ops.dsp_load = snd_usx2y_hwdep_dsp_load; 23762306a36Sopenharmony_ci hw->ops.mmap = snd_us428ctls_mmap; 23862306a36Sopenharmony_ci hw->ops.poll = snd_us428ctls_poll; 23962306a36Sopenharmony_ci hw->exclusive = 1; 24062306a36Sopenharmony_ci sprintf(hw->name, "/dev/bus/usb/%03d/%03d", device->bus->busnum, device->devnum); 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci us428->us428ctls_sharedmem = alloc_pages_exact(US428_SHAREDMEM_PAGES, GFP_KERNEL); 24362306a36Sopenharmony_ci if (!us428->us428ctls_sharedmem) 24462306a36Sopenharmony_ci return -ENOMEM; 24562306a36Sopenharmony_ci memset(us428->us428ctls_sharedmem, -1, US428_SHAREDMEM_PAGES); 24662306a36Sopenharmony_ci us428->us428ctls_sharedmem->ctl_snapshot_last = -2; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci return 0; 24962306a36Sopenharmony_ci} 250