1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Driver for Tascam US-X2Y USB soundcards 4 * 5 * FPGA Loader + ALSA Startup 6 * 7 * Copyright (c) 2003 by Karsten Wiese <annabellesgarden@yahoo.de> 8 */ 9 10 #include <linux/interrupt.h> 11 #include <linux/slab.h> 12 #include <linux/usb.h> 13 #include <sound/core.h> 14 #include <sound/memalloc.h> 15 #include <sound/pcm.h> 16 #include <sound/hwdep.h> 17 #include "usx2y.h" 18 #include "usbusx2y.h" 19 #include "usX2Yhwdep.h" 20 21 static vm_fault_t snd_us428ctls_vm_fault(struct vm_fault *vmf) 22 { 23 unsigned long offset; 24 struct page *page; 25 void *vaddr; 26 27 snd_printdd("ENTER, start %lXh, pgoff %ld\n", 28 vmf->vma->vm_start, 29 vmf->pgoff); 30 31 offset = vmf->pgoff << PAGE_SHIFT; 32 vaddr = (char *)((struct usx2ydev *)vmf->vma->vm_private_data)->us428ctls_sharedmem + offset; 33 page = virt_to_page(vaddr); 34 get_page(page); 35 vmf->page = page; 36 37 snd_printdd("vaddr=%p made us428ctls_vm_fault() page %p\n", 38 vaddr, page); 39 40 return 0; 41 } 42 43 static const struct vm_operations_struct us428ctls_vm_ops = { 44 .fault = snd_us428ctls_vm_fault, 45 }; 46 47 static int snd_us428ctls_mmap(struct snd_hwdep *hw, struct file *filp, struct vm_area_struct *area) 48 { 49 unsigned long size = (unsigned long)(area->vm_end - area->vm_start); 50 struct usx2ydev *us428 = hw->private_data; 51 52 // FIXME this hwdep interface is used twice: fpga download and mmap for controlling Lights etc. Maybe better using 2 hwdep devs? 53 // so as long as the device isn't fully initialised yet we return -EBUSY here. 54 if (!(us428->chip_status & USX2Y_STAT_CHIP_INIT)) 55 return -EBUSY; 56 57 /* if userspace tries to mmap beyond end of our buffer, fail */ 58 if (size > US428_SHAREDMEM_PAGES) { 59 snd_printd("%lu > %lu\n", size, (unsigned long)US428_SHAREDMEM_PAGES); 60 return -EINVAL; 61 } 62 63 area->vm_ops = &us428ctls_vm_ops; 64 vm_flags_set(area, VM_DONTEXPAND | VM_DONTDUMP); 65 area->vm_private_data = hw->private_data; 66 return 0; 67 } 68 69 static __poll_t snd_us428ctls_poll(struct snd_hwdep *hw, struct file *file, poll_table *wait) 70 { 71 __poll_t mask = 0; 72 struct usx2ydev *us428 = hw->private_data; 73 struct us428ctls_sharedmem *shm = us428->us428ctls_sharedmem; 74 75 if (us428->chip_status & USX2Y_STAT_CHIP_HUP) 76 return EPOLLHUP; 77 78 poll_wait(file, &us428->us428ctls_wait_queue_head, wait); 79 80 if (shm && shm->ctl_snapshot_last != shm->ctl_snapshot_red) 81 mask |= EPOLLIN; 82 83 return mask; 84 } 85 86 87 static int snd_usx2y_hwdep_dsp_status(struct snd_hwdep *hw, 88 struct snd_hwdep_dsp_status *info) 89 { 90 static const char * const type_ids[USX2Y_TYPE_NUMS] = { 91 [USX2Y_TYPE_122] = "us122", 92 [USX2Y_TYPE_224] = "us224", 93 [USX2Y_TYPE_428] = "us428", 94 }; 95 struct usx2ydev *us428 = hw->private_data; 96 int id = -1; 97 98 switch (le16_to_cpu(us428->dev->descriptor.idProduct)) { 99 case USB_ID_US122: 100 id = USX2Y_TYPE_122; 101 break; 102 case USB_ID_US224: 103 id = USX2Y_TYPE_224; 104 break; 105 case USB_ID_US428: 106 id = USX2Y_TYPE_428; 107 break; 108 } 109 if (id < 0) 110 return -ENODEV; 111 strcpy(info->id, type_ids[id]); 112 info->num_dsps = 2; // 0: Prepad Data, 1: FPGA Code 113 if (us428->chip_status & USX2Y_STAT_CHIP_INIT) 114 info->chip_ready = 1; 115 info->version = USX2Y_DRIVER_VERSION; 116 return 0; 117 } 118 119 static int usx2y_create_usbmidi(struct snd_card *card) 120 { 121 static const struct snd_usb_midi_endpoint_info quirk_data_1 = { 122 .out_ep = 0x06, 123 .in_ep = 0x06, 124 .out_cables = 0x001, 125 .in_cables = 0x001 126 }; 127 static const struct snd_usb_audio_quirk quirk_1 = { 128 .vendor_name = "TASCAM", 129 .product_name = NAME_ALLCAPS, 130 .ifnum = 0, 131 .type = QUIRK_MIDI_FIXED_ENDPOINT, 132 .data = &quirk_data_1 133 }; 134 static const struct snd_usb_midi_endpoint_info quirk_data_2 = { 135 .out_ep = 0x06, 136 .in_ep = 0x06, 137 .out_cables = 0x003, 138 .in_cables = 0x003 139 }; 140 static const struct snd_usb_audio_quirk quirk_2 = { 141 .vendor_name = "TASCAM", 142 .product_name = "US428", 143 .ifnum = 0, 144 .type = QUIRK_MIDI_FIXED_ENDPOINT, 145 .data = &quirk_data_2 146 }; 147 struct usb_device *dev = usx2y(card)->dev; 148 struct usb_interface *iface = usb_ifnum_to_if(dev, 0); 149 const struct snd_usb_audio_quirk *quirk = 150 le16_to_cpu(dev->descriptor.idProduct) == USB_ID_US428 ? 151 &quirk_2 : &quirk_1; 152 153 snd_printdd("%s\n", __func__); 154 return snd_usbmidi_create(card, iface, &usx2y(card)->midi_list, quirk); 155 } 156 157 static int usx2y_create_alsa_devices(struct snd_card *card) 158 { 159 int err; 160 161 err = usx2y_create_usbmidi(card); 162 if (err < 0) { 163 snd_printk(KERN_ERR "%s: usx2y_create_usbmidi error %i\n", __func__, err); 164 return err; 165 } 166 err = usx2y_audio_create(card); 167 if (err < 0) 168 return err; 169 err = usx2y_hwdep_pcm_new(card); 170 if (err < 0) 171 return err; 172 err = snd_card_register(card); 173 if (err < 0) 174 return err; 175 return 0; 176 } 177 178 static int snd_usx2y_hwdep_dsp_load(struct snd_hwdep *hw, 179 struct snd_hwdep_dsp_image *dsp) 180 { 181 struct usx2ydev *priv = hw->private_data; 182 struct usb_device *dev = priv->dev; 183 int lret, err; 184 char *buf; 185 186 snd_printdd("dsp_load %s\n", dsp->name); 187 188 buf = memdup_user(dsp->image, dsp->length); 189 if (IS_ERR(buf)) 190 return PTR_ERR(buf); 191 192 err = usb_set_interface(dev, 0, 1); 193 if (err) 194 snd_printk(KERN_ERR "usb_set_interface error\n"); 195 else 196 err = usb_bulk_msg(dev, usb_sndbulkpipe(dev, 2), buf, dsp->length, &lret, 6000); 197 kfree(buf); 198 if (err) 199 return err; 200 if (dsp->index == 1) { 201 msleep(250); // give the device some time 202 err = usx2y_async_seq04_init(priv); 203 if (err) { 204 snd_printk(KERN_ERR "usx2y_async_seq04_init error\n"); 205 return err; 206 } 207 err = usx2y_in04_init(priv); 208 if (err) { 209 snd_printk(KERN_ERR "usx2y_in04_init error\n"); 210 return err; 211 } 212 err = usx2y_create_alsa_devices(hw->card); 213 if (err) { 214 snd_printk(KERN_ERR "usx2y_create_alsa_devices error %i\n", err); 215 return err; 216 } 217 priv->chip_status |= USX2Y_STAT_CHIP_INIT; 218 snd_printdd("%s: alsa all started\n", hw->name); 219 } 220 return err; 221 } 222 223 int usx2y_hwdep_new(struct snd_card *card, struct usb_device *device) 224 { 225 int err; 226 struct snd_hwdep *hw; 227 struct usx2ydev *us428 = usx2y(card); 228 229 err = snd_hwdep_new(card, SND_USX2Y_LOADER_ID, 0, &hw); 230 if (err < 0) 231 return err; 232 233 hw->iface = SNDRV_HWDEP_IFACE_USX2Y; 234 hw->private_data = us428; 235 hw->ops.dsp_status = snd_usx2y_hwdep_dsp_status; 236 hw->ops.dsp_load = snd_usx2y_hwdep_dsp_load; 237 hw->ops.mmap = snd_us428ctls_mmap; 238 hw->ops.poll = snd_us428ctls_poll; 239 hw->exclusive = 1; 240 sprintf(hw->name, "/dev/bus/usb/%03d/%03d", device->bus->busnum, device->devnum); 241 242 us428->us428ctls_sharedmem = alloc_pages_exact(US428_SHAREDMEM_PAGES, GFP_KERNEL); 243 if (!us428->us428ctls_sharedmem) 244 return -ENOMEM; 245 memset(us428->us428ctls_sharedmem, -1, US428_SHAREDMEM_PAGES); 246 us428->us428ctls_sharedmem->ctl_snapshot_last = -2; 247 248 return 0; 249 } 250
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.