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