aboutsummaryrefslogtreecommitdiffstats
path: root/sound/usb/6fire
diff options
context:
space:
mode:
authorGlenn Elliott <gelliott@cs.unc.edu>2012-03-04 19:47:13 -0500
committerGlenn Elliott <gelliott@cs.unc.edu>2012-03-04 19:47:13 -0500
commitc71c03bda1e86c9d5198c5d83f712e695c4f2a1e (patch)
treeecb166cb3e2b7e2adb3b5e292245fefd23381ac8 /sound/usb/6fire
parentea53c912f8a86a8567697115b6a0d8152beee5c8 (diff)
parent6a00f206debf8a5c8899055726ad127dbeeed098 (diff)
Merge branch 'mpi-master' into wip-k-fmlpwip-k-fmlp
Conflicts: litmus/sched_cedf.c
Diffstat (limited to 'sound/usb/6fire')
-rw-r--r--sound/usb/6fire/Makefile3
-rw-r--r--sound/usb/6fire/chip.c232
-rw-r--r--sound/usb/6fire/chip.h32
-rw-r--r--sound/usb/6fire/comm.c176
-rw-r--r--sound/usb/6fire/comm.h44
-rw-r--r--sound/usb/6fire/common.h30
-rw-r--r--sound/usb/6fire/control.c380
-rw-r--r--sound/usb/6fire/control.h54
-rw-r--r--sound/usb/6fire/firmware.c419
-rw-r--r--sound/usb/6fire/firmware.h27
-rw-r--r--sound/usb/6fire/midi.c203
-rw-r--r--sound/usb/6fire/midi.h46
-rw-r--r--sound/usb/6fire/pcm.c667
-rw-r--r--sound/usb/6fire/pcm.h76
14 files changed, 2389 insertions, 0 deletions
diff --git a/sound/usb/6fire/Makefile b/sound/usb/6fire/Makefile
new file mode 100644
index 000000000000..dfce6ec53513
--- /dev/null
+++ b/sound/usb/6fire/Makefile
@@ -0,0 +1,3 @@
1snd-usb-6fire-objs += chip.o comm.o midi.o control.o firmware.o pcm.o
2obj-$(CONFIG_SND_USB_6FIRE) += snd-usb-6fire.o
3
diff --git a/sound/usb/6fire/chip.c b/sound/usb/6fire/chip.c
new file mode 100644
index 000000000000..c7dca7b0b9fe
--- /dev/null
+++ b/sound/usb/6fire/chip.c
@@ -0,0 +1,232 @@
1/*
2 * Linux driver for TerraTec DMX 6Fire USB
3 *
4 * Main routines and module definitions.
5 *
6 * Author: Torsten Schenk <torsten.schenk@zoho.com>
7 * Created: Jan 01, 2011
8 * Version: 0.3.0
9 * Copyright: (C) Torsten Schenk
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 */
16
17#include "chip.h"
18#include "firmware.h"
19#include "pcm.h"
20#include "control.h"
21#include "comm.h"
22#include "midi.h"
23
24#include <linux/moduleparam.h>
25#include <linux/interrupt.h>
26#include <linux/module.h>
27#include <linux/init.h>
28#include <linux/gfp.h>
29#include <sound/initval.h>
30
31MODULE_AUTHOR("Torsten Schenk <torsten.schenk@zoho.com>");
32MODULE_DESCRIPTION("TerraTec DMX 6Fire USB audio driver, version 0.3.0");
33MODULE_LICENSE("GPL v2");
34MODULE_SUPPORTED_DEVICE("{{TerraTec, DMX 6Fire USB}}");
35
36static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-max */
37static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* Id for card */
38static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable card */
39static struct sfire_chip *chips[SNDRV_CARDS] = SNDRV_DEFAULT_PTR;
40static struct usb_device *devices[SNDRV_CARDS] = SNDRV_DEFAULT_PTR;
41
42module_param_array(index, int, NULL, 0444);
43MODULE_PARM_DESC(index, "Index value for the 6fire sound device");
44module_param_array(id, charp, NULL, 0444);
45MODULE_PARM_DESC(id, "ID string for the 6fire sound device.");
46module_param_array(enable, bool, NULL, 0444);
47MODULE_PARM_DESC(enable, "Enable the 6fire sound device.");
48
49static DEFINE_MUTEX(register_mutex);
50
51static void usb6fire_chip_abort(struct sfire_chip *chip)
52{
53 if (chip) {
54 if (chip->pcm)
55 usb6fire_pcm_abort(chip);
56 if (chip->midi)
57 usb6fire_midi_abort(chip);
58 if (chip->comm)
59 usb6fire_comm_abort(chip);
60 if (chip->control)
61 usb6fire_control_abort(chip);
62 if (chip->card) {
63 snd_card_disconnect(chip->card);
64 snd_card_free_when_closed(chip->card);
65 chip->card = NULL;
66 }
67 }
68}
69
70static void usb6fire_chip_destroy(struct sfire_chip *chip)
71{
72 if (chip) {
73 if (chip->pcm)
74 usb6fire_pcm_destroy(chip);
75 if (chip->midi)
76 usb6fire_midi_destroy(chip);
77 if (chip->comm)
78 usb6fire_comm_destroy(chip);
79 if (chip->control)
80 usb6fire_control_destroy(chip);
81 if (chip->card)
82 snd_card_free(chip->card);
83 }
84}
85
86static int __devinit usb6fire_chip_probe(struct usb_interface *intf,
87 const struct usb_device_id *usb_id)
88{
89 int ret;
90 int i;
91 struct sfire_chip *chip = NULL;
92 struct usb_device *device = interface_to_usbdev(intf);
93 int regidx = -1; /* index in module parameter array */
94 struct snd_card *card = NULL;
95
96 /* look if we already serve this card and return if so */
97 mutex_lock(&register_mutex);
98 for (i = 0; i < SNDRV_CARDS; i++) {
99 if (devices[i] == device) {
100 if (chips[i])
101 chips[i]->intf_count++;
102 usb_set_intfdata(intf, chips[i]);
103 mutex_unlock(&register_mutex);
104 return 0;
105 } else if (regidx < 0)
106 regidx = i;
107 }
108 if (regidx < 0) {
109 mutex_unlock(&register_mutex);
110 snd_printk(KERN_ERR PREFIX "too many cards registered.\n");
111 return -ENODEV;
112 }
113 devices[regidx] = device;
114 mutex_unlock(&register_mutex);
115
116 /* check, if firmware is present on device, upload it if not */
117 ret = usb6fire_fw_init(intf);
118 if (ret < 0)
119 return ret;
120 else if (ret == FW_NOT_READY) /* firmware update performed */
121 return 0;
122
123 /* if we are here, card can be registered in alsa. */
124 if (usb_set_interface(device, 0, 0) != 0) {
125 snd_printk(KERN_ERR PREFIX "can't set first interface.\n");
126 return -EIO;
127 }
128 ret = snd_card_create(index[regidx], id[regidx], THIS_MODULE,
129 sizeof(struct sfire_chip), &card);
130 if (ret < 0) {
131 snd_printk(KERN_ERR PREFIX "cannot create alsa card.\n");
132 return ret;
133 }
134 strcpy(card->driver, "6FireUSB");
135 strcpy(card->shortname, "TerraTec DMX6FireUSB");
136 sprintf(card->longname, "%s at %d:%d", card->shortname,
137 device->bus->busnum, device->devnum);
138 snd_card_set_dev(card, &intf->dev);
139
140 chip = card->private_data;
141 chips[regidx] = chip;
142 chip->dev = device;
143 chip->regidx = regidx;
144 chip->intf_count = 1;
145 chip->card = card;
146
147 ret = usb6fire_comm_init(chip);
148 if (ret < 0) {
149 usb6fire_chip_destroy(chip);
150 return ret;
151 }
152
153 ret = usb6fire_midi_init(chip);
154 if (ret < 0) {
155 usb6fire_chip_destroy(chip);
156 return ret;
157 }
158
159 ret = usb6fire_pcm_init(chip);
160 if (ret < 0) {
161 usb6fire_chip_destroy(chip);
162 return ret;
163 }
164
165 ret = usb6fire_control_init(chip);
166 if (ret < 0) {
167 usb6fire_chip_destroy(chip);
168 return ret;
169 }
170
171 ret = snd_card_register(card);
172 if (ret < 0) {
173 snd_printk(KERN_ERR PREFIX "cannot register card.");
174 usb6fire_chip_destroy(chip);
175 return ret;
176 }
177 usb_set_intfdata(intf, chip);
178 return 0;
179}
180
181static void usb6fire_chip_disconnect(struct usb_interface *intf)
182{
183 struct sfire_chip *chip;
184 struct snd_card *card;
185
186 chip = usb_get_intfdata(intf);
187 if (chip) { /* if !chip, fw upload has been performed */
188 card = chip->card;
189 chip->intf_count--;
190 if (!chip->intf_count) {
191 mutex_lock(&register_mutex);
192 devices[chip->regidx] = NULL;
193 chips[chip->regidx] = NULL;
194 mutex_unlock(&register_mutex);
195
196 chip->shutdown = true;
197 usb6fire_chip_abort(chip);
198 usb6fire_chip_destroy(chip);
199 }
200 }
201}
202
203static struct usb_device_id device_table[] = {
204 {
205 .match_flags = USB_DEVICE_ID_MATCH_DEVICE,
206 .idVendor = 0x0ccd,
207 .idProduct = 0x0080
208 },
209 {}
210};
211
212MODULE_DEVICE_TABLE(usb, device_table);
213
214static struct usb_driver driver = {
215 .name = "snd-usb-6fire",
216 .probe = usb6fire_chip_probe,
217 .disconnect = usb6fire_chip_disconnect,
218 .id_table = device_table,
219};
220
221static int __init usb6fire_chip_init(void)
222{
223 return usb_register(&driver);
224}
225
226static void __exit usb6fire_chip_cleanup(void)
227{
228 usb_deregister(&driver);
229}
230
231module_init(usb6fire_chip_init);
232module_exit(usb6fire_chip_cleanup);
diff --git a/sound/usb/6fire/chip.h b/sound/usb/6fire/chip.h
new file mode 100644
index 000000000000..d11e5cb520f0
--- /dev/null
+++ b/sound/usb/6fire/chip.h
@@ -0,0 +1,32 @@
1/*
2 * Linux driver for TerraTec DMX 6Fire USB
3 *
4 * Author: Torsten Schenk <torsten.schenk@zoho.com>
5 * Created: Jan 01, 2011
6 * Version: 0.3.0
7 * Copyright: (C) Torsten Schenk
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 */
14#ifndef USB6FIRE_CHIP_H
15#define USB6FIRE_CHIP_H
16
17#include "common.h"
18
19struct sfire_chip {
20 struct usb_device *dev;
21 struct snd_card *card;
22 int intf_count; /* number of registered interfaces */
23 int regidx; /* index in module parameter arrays */
24 bool shutdown;
25
26 struct midi_runtime *midi;
27 struct pcm_runtime *pcm;
28 struct control_runtime *control;
29 struct comm_runtime *comm;
30};
31#endif /* USB6FIRE_CHIP_H */
32
diff --git a/sound/usb/6fire/comm.c b/sound/usb/6fire/comm.c
new file mode 100644
index 000000000000..c994daa57af2
--- /dev/null
+++ b/sound/usb/6fire/comm.c
@@ -0,0 +1,176 @@
1/*
2 * Linux driver for TerraTec DMX 6Fire USB
3 *
4 * Device communications
5 *
6 * Author: Torsten Schenk <torsten.schenk@zoho.com>
7 * Created: Jan 01, 2011
8 * Version: 0.3.0
9 * Copyright: (C) Torsten Schenk
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 */
16
17#include "comm.h"
18#include "chip.h"
19#include "midi.h"
20
21enum {
22 COMM_EP = 1,
23 COMM_FPGA_EP = 2
24};
25
26static void usb6fire_comm_init_urb(struct comm_runtime *rt, struct urb *urb,
27 u8 *buffer, void *context, void(*handler)(struct urb *urb))
28{
29 usb_init_urb(urb);
30 urb->transfer_buffer = buffer;
31 urb->pipe = usb_sndintpipe(rt->chip->dev, COMM_EP);
32 urb->complete = handler;
33 urb->context = context;
34 urb->interval = 1;
35 urb->dev = rt->chip->dev;
36}
37
38static void usb6fire_comm_receiver_handler(struct urb *urb)
39{
40 struct comm_runtime *rt = urb->context;
41 struct midi_runtime *midi_rt = rt->chip->midi;
42
43 if (!urb->status) {
44 if (rt->receiver_buffer[0] == 0x10) /* midi in event */
45 if (midi_rt)
46 midi_rt->in_received(midi_rt,
47 rt->receiver_buffer + 2,
48 rt->receiver_buffer[1]);
49 }
50
51 if (!rt->chip->shutdown) {
52 urb->status = 0;
53 urb->actual_length = 0;
54 if (usb_submit_urb(urb, GFP_ATOMIC) < 0)
55 snd_printk(KERN_WARNING PREFIX
56 "comm data receiver aborted.\n");
57 }
58}
59
60static void usb6fire_comm_init_buffer(u8 *buffer, u8 id, u8 request,
61 u8 reg, u8 vl, u8 vh)
62{
63 buffer[0] = 0x01;
64 buffer[2] = request;
65 buffer[3] = id;
66 switch (request) {
67 case 0x02:
68 buffer[1] = 0x05; /* length (starting at buffer[2]) */
69 buffer[4] = reg;
70 buffer[5] = vl;
71 buffer[6] = vh;
72 break;
73
74 case 0x12:
75 buffer[1] = 0x0b; /* length (starting at buffer[2]) */
76 buffer[4] = 0x00;
77 buffer[5] = 0x18;
78 buffer[6] = 0x05;
79 buffer[7] = 0x00;
80 buffer[8] = 0x01;
81 buffer[9] = 0x00;
82 buffer[10] = 0x9e;
83 buffer[11] = reg;
84 buffer[12] = vl;
85 break;
86
87 case 0x20:
88 case 0x21:
89 case 0x22:
90 buffer[1] = 0x04;
91 buffer[4] = reg;
92 buffer[5] = vl;
93 break;
94 }
95}
96
97static int usb6fire_comm_send_buffer(u8 *buffer, struct usb_device *dev)
98{
99 int ret;
100 int actual_len;
101
102 ret = usb_interrupt_msg(dev, usb_sndintpipe(dev, COMM_EP),
103 buffer, buffer[1] + 2, &actual_len, HZ);
104 if (ret < 0)
105 return ret;
106 else if (actual_len != buffer[1] + 2)
107 return -EIO;
108 return 0;
109}
110
111static int usb6fire_comm_write8(struct comm_runtime *rt, u8 request,
112 u8 reg, u8 value)
113{
114 u8 buffer[13]; /* 13: maximum length of message */
115
116 usb6fire_comm_init_buffer(buffer, 0x00, request, reg, value, 0x00);
117 return usb6fire_comm_send_buffer(buffer, rt->chip->dev);
118}
119
120static int usb6fire_comm_write16(struct comm_runtime *rt, u8 request,
121 u8 reg, u8 vl, u8 vh)
122{
123 u8 buffer[13]; /* 13: maximum length of message */
124
125 usb6fire_comm_init_buffer(buffer, 0x00, request, reg, vl, vh);
126 return usb6fire_comm_send_buffer(buffer, rt->chip->dev);
127}
128
129int __devinit usb6fire_comm_init(struct sfire_chip *chip)
130{
131 struct comm_runtime *rt = kzalloc(sizeof(struct comm_runtime),
132 GFP_KERNEL);
133 struct urb *urb = &rt->receiver;
134 int ret;
135
136 if (!rt)
137 return -ENOMEM;
138
139 rt->serial = 1;
140 rt->chip = chip;
141 usb_init_urb(urb);
142 rt->init_urb = usb6fire_comm_init_urb;
143 rt->write8 = usb6fire_comm_write8;
144 rt->write16 = usb6fire_comm_write16;
145
146 /* submit an urb that receives communication data from device */
147 urb->transfer_buffer = rt->receiver_buffer;
148 urb->transfer_buffer_length = COMM_RECEIVER_BUFSIZE;
149 urb->pipe = usb_rcvintpipe(chip->dev, COMM_EP);
150 urb->dev = chip->dev;
151 urb->complete = usb6fire_comm_receiver_handler;
152 urb->context = rt;
153 urb->interval = 1;
154 ret = usb_submit_urb(urb, GFP_KERNEL);
155 if (ret < 0) {
156 kfree(rt);
157 snd_printk(KERN_ERR PREFIX "cannot create comm data receiver.");
158 return ret;
159 }
160 chip->comm = rt;
161 return 0;
162}
163
164void usb6fire_comm_abort(struct sfire_chip *chip)
165{
166 struct comm_runtime *rt = chip->comm;
167
168 if (rt)
169 usb_poison_urb(&rt->receiver);
170}
171
172void usb6fire_comm_destroy(struct sfire_chip *chip)
173{
174 kfree(chip->comm);
175 chip->comm = NULL;
176}
diff --git a/sound/usb/6fire/comm.h b/sound/usb/6fire/comm.h
new file mode 100644
index 000000000000..edc5dc84b888
--- /dev/null
+++ b/sound/usb/6fire/comm.h
@@ -0,0 +1,44 @@
1/*
2 * Linux driver for TerraTec DMX 6Fire USB
3 *
4 * Author: Torsten Schenk <torsten.schenk@zoho.com>
5 * Created: Jan 01, 2011
6 * Version: 0.3.0
7 * Copyright: (C) Torsten Schenk
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 */
14#ifndef USB6FIRE_COMM_H
15#define USB6FIRE_COMM_H
16
17#include "common.h"
18
19enum /* settings for comm */
20{
21 COMM_RECEIVER_BUFSIZE = 64,
22};
23
24struct comm_runtime {
25 struct sfire_chip *chip;
26
27 struct urb receiver;
28 u8 receiver_buffer[COMM_RECEIVER_BUFSIZE];
29
30 u8 serial; /* urb serial */
31
32 void (*init_urb)(struct comm_runtime *rt, struct urb *urb, u8 *buffer,
33 void *context, void(*handler)(struct urb *urb));
34 /* writes control data to the device */
35 int (*write8)(struct comm_runtime *rt, u8 request, u8 reg, u8 value);
36 int (*write16)(struct comm_runtime *rt, u8 request, u8 reg,
37 u8 vh, u8 vl);
38};
39
40int __devinit usb6fire_comm_init(struct sfire_chip *chip);
41void usb6fire_comm_abort(struct sfire_chip *chip);
42void usb6fire_comm_destroy(struct sfire_chip *chip);
43#endif /* USB6FIRE_COMM_H */
44
diff --git a/sound/usb/6fire/common.h b/sound/usb/6fire/common.h
new file mode 100644
index 000000000000..7dbeb4a37831
--- /dev/null
+++ b/sound/usb/6fire/common.h
@@ -0,0 +1,30 @@
1/*
2 * Linux driver for TerraTec DMX 6Fire USB
3 *
4 * Author: Torsten Schenk <torsten.schenk@zoho.com>
5 * Created: Jan 01, 2011
6 * Version: 0.3.0
7 * Copyright: (C) Torsten Schenk
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 */
14
15#ifndef USB6FIRE_COMMON_H
16#define USB6FIRE_COMMON_H
17
18#include <linux/slab.h>
19#include <linux/usb.h>
20#include <sound/core.h>
21
22#define PREFIX "6fire: "
23
24struct sfire_chip;
25struct midi_runtime;
26struct pcm_runtime;
27struct control_runtime;
28struct comm_runtime;
29#endif /* USB6FIRE_COMMON_H */
30
diff --git a/sound/usb/6fire/control.c b/sound/usb/6fire/control.c
new file mode 100644
index 000000000000..ac828eff1a63
--- /dev/null
+++ b/sound/usb/6fire/control.c
@@ -0,0 +1,380 @@
1/*
2 * Linux driver for TerraTec DMX 6Fire USB
3 *
4 * Mixer control
5 *
6 * Author: Torsten Schenk <torsten.schenk@zoho.com>
7 * Created: Jan 01, 2011
8 * Version: 0.3.0
9 * Copyright: (C) Torsten Schenk
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 */
16
17#include <linux/interrupt.h>
18#include <sound/control.h>
19
20#include "control.h"
21#include "comm.h"
22#include "chip.h"
23
24static char *opt_coax_texts[2] = { "Optical", "Coax" };
25static char *line_phono_texts[2] = { "Line", "Phono" };
26
27/*
28 * calculated with $value\[i\] = 128 \cdot sqrt[3]{\frac{i}{128}}$
29 * this is done because the linear values cause rapid degredation
30 * of volume in the uppermost region.
31 */
32static const u8 log_volume_table[128] = {
33 0x00, 0x19, 0x20, 0x24, 0x28, 0x2b, 0x2e, 0x30, 0x32, 0x34,
34 0x36, 0x38, 0x3a, 0x3b, 0x3d, 0x3e, 0x40, 0x41, 0x42, 0x43,
35 0x44, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e,
36 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x53, 0x54, 0x55, 0x56,
37 0x56, 0x57, 0x58, 0x58, 0x59, 0x5a, 0x5b, 0x5b, 0x5c, 0x5c,
38 0x5d, 0x5e, 0x5e, 0x5f, 0x60, 0x60, 0x61, 0x61, 0x62, 0x62,
39 0x63, 0x63, 0x64, 0x65, 0x65, 0x66, 0x66, 0x67, 0x67, 0x68,
40 0x68, 0x69, 0x69, 0x6a, 0x6a, 0x6b, 0x6b, 0x6c, 0x6c, 0x6c,
41 0x6d, 0x6d, 0x6e, 0x6e, 0x6f, 0x6f, 0x70, 0x70, 0x70, 0x71,
42 0x71, 0x72, 0x72, 0x73, 0x73, 0x73, 0x74, 0x74, 0x75, 0x75,
43 0x75, 0x76, 0x76, 0x77, 0x77, 0x77, 0x78, 0x78, 0x78, 0x79,
44 0x79, 0x7a, 0x7a, 0x7a, 0x7b, 0x7b, 0x7b, 0x7c, 0x7c, 0x7c,
45 0x7d, 0x7d, 0x7d, 0x7e, 0x7e, 0x7e, 0x7f, 0x7f };
46
47/*
48 * data that needs to be sent to device. sets up card internal stuff.
49 * values dumped from windows driver and filtered by trial'n'error.
50 */
51static const struct {
52 u8 type;
53 u8 reg;
54 u8 value;
55}
56init_data[] = {
57 { 0x22, 0x00, 0x00 }, { 0x20, 0x00, 0x08 }, { 0x22, 0x01, 0x01 },
58 { 0x20, 0x01, 0x08 }, { 0x22, 0x02, 0x00 }, { 0x20, 0x02, 0x08 },
59 { 0x22, 0x03, 0x00 }, { 0x20, 0x03, 0x08 }, { 0x22, 0x04, 0x00 },
60 { 0x20, 0x04, 0x08 }, { 0x22, 0x05, 0x01 }, { 0x20, 0x05, 0x08 },
61 { 0x22, 0x04, 0x01 }, { 0x12, 0x04, 0x00 }, { 0x12, 0x05, 0x00 },
62 { 0x12, 0x0d, 0x78 }, { 0x12, 0x21, 0x82 }, { 0x12, 0x22, 0x80 },
63 { 0x12, 0x23, 0x00 }, { 0x12, 0x06, 0x02 }, { 0x12, 0x03, 0x00 },
64 { 0x12, 0x02, 0x00 }, { 0x22, 0x03, 0x01 },
65 { 0 } /* TERMINATING ENTRY */
66};
67
68static const int rates_altsetting[] = { 1, 1, 2, 2, 3, 3 };
69/* values to write to soundcard register for all samplerates */
70static const u16 rates_6fire_vl[] = {0x00, 0x01, 0x00, 0x01, 0x00, 0x01};
71static const u16 rates_6fire_vh[] = {0x11, 0x11, 0x10, 0x10, 0x00, 0x00};
72
73enum {
74 DIGITAL_THRU_ONLY_SAMPLERATE = 3
75};
76
77static void usb6fire_control_master_vol_update(struct control_runtime *rt)
78{
79 struct comm_runtime *comm_rt = rt->chip->comm;
80 if (comm_rt) {
81 /* set volume */
82 comm_rt->write8(comm_rt, 0x12, 0x0f, 0x7f -
83 log_volume_table[rt->master_vol]);
84 /* unmute */
85 comm_rt->write8(comm_rt, 0x12, 0x0e, 0x00);
86 }
87}
88
89static void usb6fire_control_line_phono_update(struct control_runtime *rt)
90{
91 struct comm_runtime *comm_rt = rt->chip->comm;
92 if (comm_rt) {
93 comm_rt->write8(comm_rt, 0x22, 0x02, rt->line_phono_switch);
94 comm_rt->write8(comm_rt, 0x21, 0x02, rt->line_phono_switch);
95 }
96}
97
98static void usb6fire_control_opt_coax_update(struct control_runtime *rt)
99{
100 struct comm_runtime *comm_rt = rt->chip->comm;
101 if (comm_rt) {
102 comm_rt->write8(comm_rt, 0x22, 0x00, rt->opt_coax_switch);
103 comm_rt->write8(comm_rt, 0x21, 0x00, rt->opt_coax_switch);
104 }
105}
106
107static int usb6fire_control_set_rate(struct control_runtime *rt, int rate)
108{
109 int ret;
110 struct usb_device *device = rt->chip->dev;
111 struct comm_runtime *comm_rt = rt->chip->comm;
112
113 if (rate < 0 || rate >= CONTROL_N_RATES)
114 return -EINVAL;
115
116 ret = usb_set_interface(device, 1, rates_altsetting[rate]);
117 if (ret < 0)
118 return ret;
119
120 /* set soundcard clock */
121 ret = comm_rt->write16(comm_rt, 0x02, 0x01, rates_6fire_vl[rate],
122 rates_6fire_vh[rate]);
123 if (ret < 0)
124 return ret;
125
126 return 0;
127}
128
129static int usb6fire_control_set_channels(
130 struct control_runtime *rt, int n_analog_out,
131 int n_analog_in, bool spdif_out, bool spdif_in)
132{
133 int ret;
134 struct comm_runtime *comm_rt = rt->chip->comm;
135
136 /* enable analog inputs and outputs
137 * (one bit per stereo-channel) */
138 ret = comm_rt->write16(comm_rt, 0x02, 0x02,
139 (1 << (n_analog_out / 2)) - 1,
140 (1 << (n_analog_in / 2)) - 1);
141 if (ret < 0)
142 return ret;
143
144 /* disable digital inputs and outputs */
145 /* TODO: use spdif_x to enable/disable digital channels */
146 ret = comm_rt->write16(comm_rt, 0x02, 0x03, 0x00, 0x00);
147 if (ret < 0)
148 return ret;
149
150 return 0;
151}
152
153static int usb6fire_control_streaming_update(struct control_runtime *rt)
154{
155 struct comm_runtime *comm_rt = rt->chip->comm;
156
157 if (comm_rt) {
158 if (!rt->usb_streaming && rt->digital_thru_switch)
159 usb6fire_control_set_rate(rt,
160 DIGITAL_THRU_ONLY_SAMPLERATE);
161 return comm_rt->write16(comm_rt, 0x02, 0x00, 0x00,
162 (rt->usb_streaming ? 0x01 : 0x00) |
163 (rt->digital_thru_switch ? 0x08 : 0x00));
164 }
165 return -EINVAL;
166}
167
168static int usb6fire_control_master_vol_info(struct snd_kcontrol *kcontrol,
169 struct snd_ctl_elem_info *uinfo)
170{
171 uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
172 uinfo->count = 1;
173 uinfo->value.integer.min = 0;
174 uinfo->value.integer.max = 127;
175 return 0;
176}
177
178static int usb6fire_control_master_vol_put(struct snd_kcontrol *kcontrol,
179 struct snd_ctl_elem_value *ucontrol)
180{
181 struct control_runtime *rt = snd_kcontrol_chip(kcontrol);
182 int changed = 0;
183 if (rt->master_vol != ucontrol->value.integer.value[0]) {
184 rt->master_vol = ucontrol->value.integer.value[0];
185 usb6fire_control_master_vol_update(rt);
186 changed = 1;
187 }
188 return changed;
189}
190
191static int usb6fire_control_master_vol_get(struct snd_kcontrol *kcontrol,
192 struct snd_ctl_elem_value *ucontrol)
193{
194 struct control_runtime *rt = snd_kcontrol_chip(kcontrol);
195 ucontrol->value.integer.value[0] = rt->master_vol;
196 return 0;
197}
198
199static int usb6fire_control_line_phono_info(struct snd_kcontrol *kcontrol,
200 struct snd_ctl_elem_info *uinfo)
201{
202 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
203 uinfo->count = 1;
204 uinfo->value.enumerated.items = 2;
205 if (uinfo->value.enumerated.item > 1)
206 uinfo->value.enumerated.item = 1;
207 strcpy(uinfo->value.enumerated.name,
208 line_phono_texts[uinfo->value.enumerated.item]);
209 return 0;
210}
211
212static int usb6fire_control_line_phono_put(struct snd_kcontrol *kcontrol,
213 struct snd_ctl_elem_value *ucontrol)
214{
215 struct control_runtime *rt = snd_kcontrol_chip(kcontrol);
216 int changed = 0;
217 if (rt->line_phono_switch != ucontrol->value.integer.value[0]) {
218 rt->line_phono_switch = ucontrol->value.integer.value[0];
219 usb6fire_control_line_phono_update(rt);
220 changed = 1;
221 }
222 return changed;
223}
224
225static int usb6fire_control_line_phono_get(struct snd_kcontrol *kcontrol,
226 struct snd_ctl_elem_value *ucontrol)
227{
228 struct control_runtime *rt = snd_kcontrol_chip(kcontrol);
229 ucontrol->value.integer.value[0] = rt->line_phono_switch;
230 return 0;
231}
232
233static int usb6fire_control_opt_coax_info(struct snd_kcontrol *kcontrol,
234 struct snd_ctl_elem_info *uinfo)
235{
236 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
237 uinfo->count = 1;
238 uinfo->value.enumerated.items = 2;
239 if (uinfo->value.enumerated.item > 1)
240 uinfo->value.enumerated.item = 1;
241 strcpy(uinfo->value.enumerated.name,
242 opt_coax_texts[uinfo->value.enumerated.item]);
243 return 0;
244}
245
246static int usb6fire_control_opt_coax_put(struct snd_kcontrol *kcontrol,
247 struct snd_ctl_elem_value *ucontrol)
248{
249 struct control_runtime *rt = snd_kcontrol_chip(kcontrol);
250 int changed = 0;
251
252 if (rt->opt_coax_switch != ucontrol->value.enumerated.item[0]) {
253 rt->opt_coax_switch = ucontrol->value.enumerated.item[0];
254 usb6fire_control_opt_coax_update(rt);
255 changed = 1;
256 }
257 return changed;
258}
259
260static int usb6fire_control_opt_coax_get(struct snd_kcontrol *kcontrol,
261 struct snd_ctl_elem_value *ucontrol)
262{
263 struct control_runtime *rt = snd_kcontrol_chip(kcontrol);
264 ucontrol->value.enumerated.item[0] = rt->opt_coax_switch;
265 return 0;
266}
267
268static int usb6fire_control_digital_thru_put(struct snd_kcontrol *kcontrol,
269 struct snd_ctl_elem_value *ucontrol)
270{
271 struct control_runtime *rt = snd_kcontrol_chip(kcontrol);
272 int changed = 0;
273
274 if (rt->digital_thru_switch != ucontrol->value.integer.value[0]) {
275 rt->digital_thru_switch = ucontrol->value.integer.value[0];
276 usb6fire_control_streaming_update(rt);
277 changed = 1;
278 }
279 return changed;
280}
281
282static int usb6fire_control_digital_thru_get(struct snd_kcontrol *kcontrol,
283 struct snd_ctl_elem_value *ucontrol)
284{
285 struct control_runtime *rt = snd_kcontrol_chip(kcontrol);
286 ucontrol->value.integer.value[0] = rt->digital_thru_switch;
287 return 0;
288}
289
290static struct __devinitdata snd_kcontrol_new elements[] = {
291 {
292 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
293 .name = "Master Playback Volume",
294 .index = 0,
295 .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
296 .info = usb6fire_control_master_vol_info,
297 .get = usb6fire_control_master_vol_get,
298 .put = usb6fire_control_master_vol_put
299 },
300 {
301 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
302 .name = "Line/Phono Capture Route",
303 .index = 0,
304 .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
305 .info = usb6fire_control_line_phono_info,
306 .get = usb6fire_control_line_phono_get,
307 .put = usb6fire_control_line_phono_put
308 },
309 {
310 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
311 .name = "Opt/Coax Capture Route",
312 .index = 0,
313 .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
314 .info = usb6fire_control_opt_coax_info,
315 .get = usb6fire_control_opt_coax_get,
316 .put = usb6fire_control_opt_coax_put
317 },
318 {
319 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
320 .name = "Digital Thru Playback Route",
321 .index = 0,
322 .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
323 .info = snd_ctl_boolean_mono_info,
324 .get = usb6fire_control_digital_thru_get,
325 .put = usb6fire_control_digital_thru_put
326 },
327 {}
328};
329
330int __devinit usb6fire_control_init(struct sfire_chip *chip)
331{
332 int i;
333 int ret;
334 struct control_runtime *rt = kzalloc(sizeof(struct control_runtime),
335 GFP_KERNEL);
336 struct comm_runtime *comm_rt = chip->comm;
337
338 if (!rt)
339 return -ENOMEM;
340
341 rt->chip = chip;
342 rt->update_streaming = usb6fire_control_streaming_update;
343 rt->set_rate = usb6fire_control_set_rate;
344 rt->set_channels = usb6fire_control_set_channels;
345
346 i = 0;
347 while (init_data[i].type) {
348 comm_rt->write8(comm_rt, init_data[i].type, init_data[i].reg,
349 init_data[i].value);
350 i++;
351 }
352
353 usb6fire_control_opt_coax_update(rt);
354 usb6fire_control_line_phono_update(rt);
355 usb6fire_control_master_vol_update(rt);
356 usb6fire_control_streaming_update(rt);
357
358 i = 0;
359 while (elements[i].name) {
360 ret = snd_ctl_add(chip->card, snd_ctl_new1(&elements[i], rt));
361 if (ret < 0) {
362 kfree(rt);
363 snd_printk(KERN_ERR PREFIX "cannot add control.\n");
364 return ret;
365 }
366 i++;
367 }
368
369 chip->control = rt;
370 return 0;
371}
372
373void usb6fire_control_abort(struct sfire_chip *chip)
374{}
375
376void usb6fire_control_destroy(struct sfire_chip *chip)
377{
378 kfree(chip->control);
379 chip->control = NULL;
380}
diff --git a/sound/usb/6fire/control.h b/sound/usb/6fire/control.h
new file mode 100644
index 000000000000..8f5aeead2e3d
--- /dev/null
+++ b/sound/usb/6fire/control.h
@@ -0,0 +1,54 @@
1/*
2 * Linux driver for TerraTec DMX 6Fire USB
3 *
4 * Author: Torsten Schenk <torsten.schenk@zoho.com>
5 * Created: Jan 01, 2011
6 * Version: 0.3.0
7 * Copyright: (C) Torsten Schenk
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 */
14
15#ifndef USB6FIRE_CONTROL_H
16#define USB6FIRE_CONTROL_H
17
18#include "common.h"
19
20enum {
21 CONTROL_MAX_ELEMENTS = 32
22};
23
24enum {
25 CONTROL_RATE_44KHZ,
26 CONTROL_RATE_48KHZ,
27 CONTROL_RATE_88KHZ,
28 CONTROL_RATE_96KHZ,
29 CONTROL_RATE_176KHZ,
30 CONTROL_RATE_192KHZ,
31 CONTROL_N_RATES
32};
33
34struct control_runtime {
35 int (*update_streaming)(struct control_runtime *rt);
36 int (*set_rate)(struct control_runtime *rt, int rate);
37 int (*set_channels)(struct control_runtime *rt, int n_analog_out,
38 int n_analog_in, bool spdif_out, bool spdif_in);
39
40 struct sfire_chip *chip;
41
42 struct snd_kcontrol *element[CONTROL_MAX_ELEMENTS];
43 bool opt_coax_switch;
44 bool line_phono_switch;
45 bool digital_thru_switch;
46 bool usb_streaming;
47 u8 master_vol;
48};
49
50int __devinit usb6fire_control_init(struct sfire_chip *chip);
51void usb6fire_control_abort(struct sfire_chip *chip);
52void usb6fire_control_destroy(struct sfire_chip *chip);
53#endif /* USB6FIRE_CONTROL_H */
54
diff --git a/sound/usb/6fire/firmware.c b/sound/usb/6fire/firmware.c
new file mode 100644
index 000000000000..1e3ae3327dd3
--- /dev/null
+++ b/sound/usb/6fire/firmware.c
@@ -0,0 +1,419 @@
1/*
2 * Linux driver for TerraTec DMX 6Fire USB
3 *
4 * Firmware loader
5 *
6 * Author: Torsten Schenk <torsten.schenk@zoho.com>
7 * Created: Jan 01, 2011
8 * Version: 0.3.0
9 * Copyright: (C) Torsten Schenk
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 */
16
17#include <linux/firmware.h>
18#include <linux/bitrev.h>
19
20#include "firmware.h"
21#include "chip.h"
22
23MODULE_FIRMWARE("6fire/dmx6firel2.ihx");
24MODULE_FIRMWARE("6fire/dmx6fireap.ihx");
25MODULE_FIRMWARE("6fire/dmx6firecf.bin");
26
27enum {
28 FPGA_BUFSIZE = 512, FPGA_EP = 2
29};
30
31/*
32 * wMaxPacketSize of pcm endpoints.
33 * keep synced with rates_in_packet_size and rates_out_packet_size in pcm.c
34 * fpp: frames per isopacket
35 *
36 * CAUTION: keep sizeof <= buffer[] in usb6fire_fw_init
37 */
38static const u8 ep_w_max_packet_size[] = {
39 0xe4, 0x00, 0xe4, 0x00, /* alt 1: 228 EP2 and EP6 (7 fpp) */
40 0xa4, 0x01, 0xa4, 0x01, /* alt 2: 420 EP2 and EP6 (13 fpp)*/
41 0x94, 0x01, 0x5c, 0x02 /* alt 3: 404 EP2 and 604 EP6 (25 fpp) */
42};
43
44static const u8 known_fw_versions[][4] = {
45 { 0x03, 0x01, 0x0b, 0x00 }
46};
47
48struct ihex_record {
49 u16 address;
50 u8 len;
51 u8 data[256];
52 char error; /* true if an error occurred parsing this record */
53
54 u8 max_len; /* maximum record length in whole ihex */
55
56 /* private */
57 const char *txt_data;
58 unsigned int txt_length;
59 unsigned int txt_offset; /* current position in txt_data */
60};
61
62static u8 usb6fire_fw_ihex_nibble(const u8 n)
63{
64 if (n >= '0' && n <= '9')
65 return n - '0';
66 else if (n >= 'A' && n <= 'F')
67 return n - ('A' - 10);
68 else if (n >= 'a' && n <= 'f')
69 return n - ('a' - 10);
70 return 0;
71}
72
73static u8 usb6fire_fw_ihex_hex(const u8 *data, u8 *crc)
74{
75 u8 val = (usb6fire_fw_ihex_nibble(data[0]) << 4) |
76 usb6fire_fw_ihex_nibble(data[1]);
77 *crc += val;
78 return val;
79}
80
81/*
82 * returns true if record is available, false otherwise.
83 * iff an error occurred, false will be returned and record->error will be true.
84 */
85static bool usb6fire_fw_ihex_next_record(struct ihex_record *record)
86{
87 u8 crc = 0;
88 u8 type;
89 int i;
90
91 record->error = false;
92
93 /* find begin of record (marked by a colon) */
94 while (record->txt_offset < record->txt_length
95 && record->txt_data[record->txt_offset] != ':')
96 record->txt_offset++;
97 if (record->txt_offset == record->txt_length)
98 return false;
99
100 /* number of characters needed for len, addr and type entries */
101 record->txt_offset++;
102 if (record->txt_offset + 8 > record->txt_length) {
103 record->error = true;
104 return false;
105 }
106
107 record->len = usb6fire_fw_ihex_hex(record->txt_data +
108 record->txt_offset, &crc);
109 record->txt_offset += 2;
110 record->address = usb6fire_fw_ihex_hex(record->txt_data +
111 record->txt_offset, &crc) << 8;
112 record->txt_offset += 2;
113 record->address |= usb6fire_fw_ihex_hex(record->txt_data +
114 record->txt_offset, &crc);
115 record->txt_offset += 2;
116 type = usb6fire_fw_ihex_hex(record->txt_data +
117 record->txt_offset, &crc);
118 record->txt_offset += 2;
119
120 /* number of characters needed for data and crc entries */
121 if (record->txt_offset + 2 * (record->len + 1) > record->txt_length) {
122 record->error = true;
123 return false;
124 }
125 for (i = 0; i < record->len; i++) {
126 record->data[i] = usb6fire_fw_ihex_hex(record->txt_data
127 + record->txt_offset, &crc);
128 record->txt_offset += 2;
129 }
130 usb6fire_fw_ihex_hex(record->txt_data + record->txt_offset, &crc);
131 if (crc) {
132 record->error = true;
133 return false;
134 }
135
136 if (type == 1 || !record->len) /* eof */
137 return false;
138 else if (type == 0)
139 return true;
140 else {
141 record->error = true;
142 return false;
143 }
144}
145
146static int usb6fire_fw_ihex_init(const struct firmware *fw,
147 struct ihex_record *record)
148{
149 record->txt_data = fw->data;
150 record->txt_length = fw->size;
151 record->txt_offset = 0;
152 record->max_len = 0;
153 /* read all records, if loop ends, record->error indicates,
154 * whether ihex is valid. */
155 while (usb6fire_fw_ihex_next_record(record))
156 record->max_len = max(record->len, record->max_len);
157 if (record->error)
158 return -EINVAL;
159 record->txt_offset = 0;
160 return 0;
161}
162
163static int usb6fire_fw_ezusb_write(struct usb_device *device,
164 int type, int value, char *data, int len)
165{
166 int ret;
167
168 ret = usb_control_msg(device, usb_sndctrlpipe(device, 0), type,
169 USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
170 value, 0, data, len, HZ);
171 if (ret < 0)
172 return ret;
173 else if (ret != len)
174 return -EIO;
175 return 0;
176}
177
178static int usb6fire_fw_ezusb_read(struct usb_device *device,
179 int type, int value, char *data, int len)
180{
181 int ret = usb_control_msg(device, usb_rcvctrlpipe(device, 0), type,
182 USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, value,
183 0, data, len, HZ);
184 if (ret < 0)
185 return ret;
186 else if (ret != len)
187 return -EIO;
188 return 0;
189}
190
191static int usb6fire_fw_fpga_write(struct usb_device *device,
192 char *data, int len)
193{
194 int actual_len;
195 int ret;
196
197 ret = usb_bulk_msg(device, usb_sndbulkpipe(device, FPGA_EP), data, len,
198 &actual_len, HZ);
199 if (ret < 0)
200 return ret;
201 else if (actual_len != len)
202 return -EIO;
203 return 0;
204}
205
206static int usb6fire_fw_ezusb_upload(
207 struct usb_interface *intf, const char *fwname,
208 unsigned int postaddr, u8 *postdata, unsigned int postlen)
209{
210 int ret;
211 u8 data;
212 struct usb_device *device = interface_to_usbdev(intf);
213 const struct firmware *fw = 0;
214 struct ihex_record *rec = kmalloc(sizeof(struct ihex_record),
215 GFP_KERNEL);
216
217 if (!rec)
218 return -ENOMEM;
219
220 ret = request_firmware(&fw, fwname, &device->dev);
221 if (ret < 0) {
222 kfree(rec);
223 snd_printk(KERN_ERR PREFIX "error requesting ezusb "
224 "firmware %s.\n", fwname);
225 return ret;
226 }
227 ret = usb6fire_fw_ihex_init(fw, rec);
228 if (ret < 0) {
229 kfree(rec);
230 release_firmware(fw);
231 snd_printk(KERN_ERR PREFIX "error validating ezusb "
232 "firmware %s.\n", fwname);
233 return ret;
234 }
235 /* upload firmware image */
236 data = 0x01; /* stop ezusb cpu */
237 ret = usb6fire_fw_ezusb_write(device, 0xa0, 0xe600, &data, 1);
238 if (ret < 0) {
239 kfree(rec);
240 release_firmware(fw);
241 snd_printk(KERN_ERR PREFIX "unable to upload ezusb "
242 "firmware %s: begin message.\n", fwname);
243 return ret;
244 }
245
246 while (usb6fire_fw_ihex_next_record(rec)) { /* write firmware */
247 ret = usb6fire_fw_ezusb_write(device, 0xa0, rec->address,
248 rec->data, rec->len);
249 if (ret < 0) {
250 kfree(rec);
251 release_firmware(fw);
252 snd_printk(KERN_ERR PREFIX "unable to upload ezusb "
253 "firmware %s: data urb.\n", fwname);
254 return ret;
255 }
256 }
257
258 release_firmware(fw);
259 kfree(rec);
260 if (postdata) { /* write data after firmware has been uploaded */
261 ret = usb6fire_fw_ezusb_write(device, 0xa0, postaddr,
262 postdata, postlen);
263 if (ret < 0) {
264 snd_printk(KERN_ERR PREFIX "unable to upload ezusb "
265 "firmware %s: post urb.\n", fwname);
266 return ret;
267 }
268 }
269
270 data = 0x00; /* resume ezusb cpu */
271 ret = usb6fire_fw_ezusb_write(device, 0xa0, 0xe600, &data, 1);
272 if (ret < 0) {
273 snd_printk(KERN_ERR PREFIX "unable to upload ezusb "
274 "firmware %s: end message.\n", fwname);
275 return ret;
276 }
277 return 0;
278}
279
280static int usb6fire_fw_fpga_upload(
281 struct usb_interface *intf, const char *fwname)
282{
283 int ret;
284 int i;
285 struct usb_device *device = interface_to_usbdev(intf);
286 u8 *buffer = kmalloc(FPGA_BUFSIZE, GFP_KERNEL);
287 const char *c;
288 const char *end;
289 const struct firmware *fw;
290
291 if (!buffer)
292 return -ENOMEM;
293
294 ret = request_firmware(&fw, fwname, &device->dev);
295 if (ret < 0) {
296 snd_printk(KERN_ERR PREFIX "unable to get fpga firmware %s.\n",
297 fwname);
298 kfree(buffer);
299 return -EIO;
300 }
301
302 c = fw->data;
303 end = fw->data + fw->size;
304
305 ret = usb6fire_fw_ezusb_write(device, 8, 0, NULL, 0);
306 if (ret < 0) {
307 kfree(buffer);
308 release_firmware(fw);
309 snd_printk(KERN_ERR PREFIX "unable to upload fpga firmware: "
310 "begin urb.\n");
311 return ret;
312 }
313
314 while (c != end) {
315 for (i = 0; c != end && i < FPGA_BUFSIZE; i++, c++)
316 buffer[i] = byte_rev_table[(u8) *c];
317
318 ret = usb6fire_fw_fpga_write(device, buffer, i);
319 if (ret < 0) {
320 release_firmware(fw);
321 kfree(buffer);
322 snd_printk(KERN_ERR PREFIX "unable to upload fpga "
323 "firmware: fw urb.\n");
324 return ret;
325 }
326 }
327 release_firmware(fw);
328 kfree(buffer);
329
330 ret = usb6fire_fw_ezusb_write(device, 9, 0, NULL, 0);
331 if (ret < 0) {
332 snd_printk(KERN_ERR PREFIX "unable to upload fpga firmware: "
333 "end urb.\n");
334 return ret;
335 }
336 return 0;
337}
338
339/* check, if the firmware version the devices has currently loaded
340 * is known by this driver. 'version' needs to have 4 bytes version
341 * info data. */
342static int usb6fire_fw_check(u8 *version)
343{
344 int i;
345
346 for (i = 0; i < ARRAY_SIZE(known_fw_versions); i++)
347 if (!memcmp(version, known_fw_versions + i, 4))
348 return 0;
349
350 snd_printk(KERN_ERR PREFIX "invalid fimware version in device: "
351 "%02x %02x %02x %02x. "
352 "please reconnect to power. if this failure "
353 "still happens, check your firmware installation.",
354 version[0], version[1], version[2], version[3]);
355 return -EINVAL;
356}
357
358int usb6fire_fw_init(struct usb_interface *intf)
359{
360 int i;
361 int ret;
362 struct usb_device *device = interface_to_usbdev(intf);
363 /* buffer: 8 receiving bytes from device and
364 * sizeof(EP_W_MAX_PACKET_SIZE) bytes for non-const copy */
365 u8 buffer[12];
366
367 ret = usb6fire_fw_ezusb_read(device, 1, 0, buffer, 8);
368 if (ret < 0) {
369 snd_printk(KERN_ERR PREFIX "unable to receive device "
370 "firmware state.\n");
371 return ret;
372 }
373 if (buffer[0] != 0xeb || buffer[1] != 0xaa || buffer[2] != 0x55) {
374 snd_printk(KERN_ERR PREFIX "unknown device firmware state "
375 "received from device: ");
376 for (i = 0; i < 8; i++)
377 snd_printk("%02x ", buffer[i]);
378 snd_printk("\n");
379 return -EIO;
380 }
381 /* do we need fpga loader ezusb firmware? */
382 if (buffer[3] == 0x01) {
383 ret = usb6fire_fw_ezusb_upload(intf,
384 "6fire/dmx6firel2.ihx", 0, NULL, 0);
385 if (ret < 0)
386 return ret;
387 return FW_NOT_READY;
388 }
389 /* do we need fpga firmware and application ezusb firmware? */
390 else if (buffer[3] == 0x02) {
391 ret = usb6fire_fw_check(buffer + 4);
392 if (ret < 0)
393 return ret;
394 ret = usb6fire_fw_fpga_upload(intf, "6fire/dmx6firecf.bin");
395 if (ret < 0)
396 return ret;
397 memcpy(buffer, ep_w_max_packet_size,
398 sizeof(ep_w_max_packet_size));
399 ret = usb6fire_fw_ezusb_upload(intf, "6fire/dmx6fireap.ihx",
400 0x0003, buffer, sizeof(ep_w_max_packet_size));
401 if (ret < 0)
402 return ret;
403 return FW_NOT_READY;
404 }
405 /* all fw loaded? */
406 else if (buffer[3] == 0x03)
407 return usb6fire_fw_check(buffer + 4);
408 /* unknown data? */
409 else {
410 snd_printk(KERN_ERR PREFIX "unknown device firmware state "
411 "received from device: ");
412 for (i = 0; i < 8; i++)
413 snd_printk("%02x ", buffer[i]);
414 snd_printk("\n");
415 return -EIO;
416 }
417 return 0;
418}
419
diff --git a/sound/usb/6fire/firmware.h b/sound/usb/6fire/firmware.h
new file mode 100644
index 000000000000..008569895381
--- /dev/null
+++ b/sound/usb/6fire/firmware.h
@@ -0,0 +1,27 @@
1/*
2 * Linux driver for TerraTec DMX 6Fire USB
3 *
4 * Author: Torsten Schenk
5 * Created: Jan 01, 2011
6 * Copyright: (C) Torsten Schenk
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 */
13
14#ifndef USB6FIRE_FIRMWARE_H
15#define USB6FIRE_FIRMWARE_H
16
17#include "common.h"
18
19enum /* firmware state of device */
20{
21 FW_READY = 0,
22 FW_NOT_READY = 1
23};
24
25int __devinit usb6fire_fw_init(struct usb_interface *intf);
26#endif /* USB6FIRE_FIRMWARE_H */
27
diff --git a/sound/usb/6fire/midi.c b/sound/usb/6fire/midi.c
new file mode 100644
index 000000000000..13f4509dce2b
--- /dev/null
+++ b/sound/usb/6fire/midi.c
@@ -0,0 +1,203 @@
1/*
2 * Linux driver for TerraTec DMX 6Fire USB
3 *
4 * Rawmidi driver
5 *
6 * Author: Torsten Schenk <torsten.schenk@zoho.com>
7 * Created: Jan 01, 2011
8 * Version: 0.3.0
9 * Copyright: (C) Torsten Schenk
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 */
16
17#include <sound/rawmidi.h>
18
19#include "midi.h"
20#include "chip.h"
21#include "comm.h"
22
23static void usb6fire_midi_out_handler(struct urb *urb)
24{
25 struct midi_runtime *rt = urb->context;
26 int ret;
27 unsigned long flags;
28
29 spin_lock_irqsave(&rt->out_lock, flags);
30
31 if (rt->out) {
32 ret = snd_rawmidi_transmit(rt->out, rt->out_buffer + 4,
33 MIDI_BUFSIZE - 4);
34 if (ret > 0) { /* more data available, send next packet */
35 rt->out_buffer[1] = ret + 2;
36 rt->out_buffer[3] = rt->out_serial++;
37 urb->transfer_buffer_length = ret + 4;
38
39 ret = usb_submit_urb(urb, GFP_ATOMIC);
40 if (ret < 0)
41 snd_printk(KERN_ERR PREFIX "midi out urb "
42 "submit failed: %d\n", ret);
43 } else /* no more data to transmit */
44 rt->out = NULL;
45 }
46 spin_unlock_irqrestore(&rt->out_lock, flags);
47}
48
49static void usb6fire_midi_in_received(
50 struct midi_runtime *rt, u8 *data, int length)
51{
52 unsigned long flags;
53
54 spin_lock_irqsave(&rt->in_lock, flags);
55 if (rt->in)
56 snd_rawmidi_receive(rt->in, data, length);
57 spin_unlock_irqrestore(&rt->in_lock, flags);
58}
59
60static int usb6fire_midi_out_open(struct snd_rawmidi_substream *alsa_sub)
61{
62 return 0;
63}
64
65static int usb6fire_midi_out_close(struct snd_rawmidi_substream *alsa_sub)
66{
67 return 0;
68}
69
70static void usb6fire_midi_out_trigger(
71 struct snd_rawmidi_substream *alsa_sub, int up)
72{
73 struct midi_runtime *rt = alsa_sub->rmidi->private_data;
74 struct urb *urb = &rt->out_urb;
75 __s8 ret;
76 unsigned long flags;
77
78 spin_lock_irqsave(&rt->out_lock, flags);
79 if (up) { /* start transfer */
80 if (rt->out) { /* we are already transmitting so just return */
81 spin_unlock_irqrestore(&rt->out_lock, flags);
82 return;
83 }
84
85 ret = snd_rawmidi_transmit(alsa_sub, rt->out_buffer + 4,
86 MIDI_BUFSIZE - 4);
87 if (ret > 0) {
88 rt->out_buffer[1] = ret + 2;
89 rt->out_buffer[3] = rt->out_serial++;
90 urb->transfer_buffer_length = ret + 4;
91
92 ret = usb_submit_urb(urb, GFP_ATOMIC);
93 if (ret < 0)
94 snd_printk(KERN_ERR PREFIX "midi out urb "
95 "submit failed: %d\n", ret);
96 else
97 rt->out = alsa_sub;
98 }
99 } else if (rt->out == alsa_sub)
100 rt->out = NULL;
101 spin_unlock_irqrestore(&rt->out_lock, flags);
102}
103
104static void usb6fire_midi_out_drain(struct snd_rawmidi_substream *alsa_sub)
105{
106 struct midi_runtime *rt = alsa_sub->rmidi->private_data;
107 int retry = 0;
108
109 while (rt->out && retry++ < 100)
110 msleep(10);
111}
112
113static int usb6fire_midi_in_open(struct snd_rawmidi_substream *alsa_sub)
114{
115 return 0;
116}
117
118static int usb6fire_midi_in_close(struct snd_rawmidi_substream *alsa_sub)
119{
120 return 0;
121}
122
123static void usb6fire_midi_in_trigger(
124 struct snd_rawmidi_substream *alsa_sub, int up)
125{
126 struct midi_runtime *rt = alsa_sub->rmidi->private_data;
127 unsigned long flags;
128
129 spin_lock_irqsave(&rt->in_lock, flags);
130 if (up)
131 rt->in = alsa_sub;
132 else
133 rt->in = NULL;
134 spin_unlock_irqrestore(&rt->in_lock, flags);
135}
136
137static struct snd_rawmidi_ops out_ops = {
138 .open = usb6fire_midi_out_open,
139 .close = usb6fire_midi_out_close,
140 .trigger = usb6fire_midi_out_trigger,
141 .drain = usb6fire_midi_out_drain
142};
143
144static struct snd_rawmidi_ops in_ops = {
145 .open = usb6fire_midi_in_open,
146 .close = usb6fire_midi_in_close,
147 .trigger = usb6fire_midi_in_trigger
148};
149
150int __devinit usb6fire_midi_init(struct sfire_chip *chip)
151{
152 int ret;
153 struct midi_runtime *rt = kzalloc(sizeof(struct midi_runtime),
154 GFP_KERNEL);
155 struct comm_runtime *comm_rt = chip->comm;
156
157 if (!rt)
158 return -ENOMEM;
159
160 rt->chip = chip;
161 rt->in_received = usb6fire_midi_in_received;
162 rt->out_buffer[0] = 0x80; /* 'send midi' command */
163 rt->out_buffer[1] = 0x00; /* size of data */
164 rt->out_buffer[2] = 0x00; /* always 0 */
165 spin_lock_init(&rt->in_lock);
166 spin_lock_init(&rt->out_lock);
167
168 comm_rt->init_urb(comm_rt, &rt->out_urb, rt->out_buffer, rt,
169 usb6fire_midi_out_handler);
170
171 ret = snd_rawmidi_new(chip->card, "6FireUSB", 0, 1, 1, &rt->instance);
172 if (ret < 0) {
173 kfree(rt);
174 snd_printk(KERN_ERR PREFIX "unable to create midi.\n");
175 return ret;
176 }
177 rt->instance->private_data = rt;
178 strcpy(rt->instance->name, "DMX6FireUSB MIDI");
179 rt->instance->info_flags = SNDRV_RAWMIDI_INFO_OUTPUT |
180 SNDRV_RAWMIDI_INFO_INPUT |
181 SNDRV_RAWMIDI_INFO_DUPLEX;
182 snd_rawmidi_set_ops(rt->instance, SNDRV_RAWMIDI_STREAM_OUTPUT,
183 &out_ops);
184 snd_rawmidi_set_ops(rt->instance, SNDRV_RAWMIDI_STREAM_INPUT,
185 &in_ops);
186
187 chip->midi = rt;
188 return 0;
189}
190
191void usb6fire_midi_abort(struct sfire_chip *chip)
192{
193 struct midi_runtime *rt = chip->midi;
194
195 if (rt)
196 usb_poison_urb(&rt->out_urb);
197}
198
199void usb6fire_midi_destroy(struct sfire_chip *chip)
200{
201 kfree(chip->midi);
202 chip->midi = NULL;
203}
diff --git a/sound/usb/6fire/midi.h b/sound/usb/6fire/midi.h
new file mode 100644
index 000000000000..97a7bf669135
--- /dev/null
+++ b/sound/usb/6fire/midi.h
@@ -0,0 +1,46 @@
1/*
2 * Linux driver for TerraTec DMX 6Fire USB
3 *
4 * Author: Torsten Schenk <torsten.schenk@zoho.com>
5 * Created: Jan 01, 2011
6 * Version: 0.3.0
7 * Copyright: (C) Torsten Schenk
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 */
14
15#ifndef USB6FIRE_MIDI_H
16#define USB6FIRE_MIDI_H
17
18#include "common.h"
19
20enum {
21 MIDI_BUFSIZE = 64
22};
23
24struct midi_runtime {
25 struct sfire_chip *chip;
26 struct snd_rawmidi *instance;
27
28 struct snd_rawmidi_substream *in;
29 char in_active;
30
31 spinlock_t in_lock;
32 spinlock_t out_lock;
33 struct snd_rawmidi_substream *out;
34 struct urb out_urb;
35 u8 out_serial; /* serial number of out packet */
36 u8 out_buffer[MIDI_BUFSIZE];
37 int buffer_offset;
38
39 void (*in_received)(struct midi_runtime *rt, u8 *data, int length);
40};
41
42int __devinit usb6fire_midi_init(struct sfire_chip *chip);
43void usb6fire_midi_abort(struct sfire_chip *chip);
44void usb6fire_midi_destroy(struct sfire_chip *chip);
45#endif /* USB6FIRE_MIDI_H */
46
diff --git a/sound/usb/6fire/pcm.c b/sound/usb/6fire/pcm.c
new file mode 100644
index 000000000000..d144cdb2f159
--- /dev/null
+++ b/sound/usb/6fire/pcm.c
@@ -0,0 +1,667 @@
1/*
2 * Linux driver for TerraTec DMX 6Fire USB
3 *
4 * PCM driver
5 *
6 * Author: Torsten Schenk <torsten.schenk@zoho.com>
7 * Created: Jan 01, 2011
8 * Version: 0.3.0
9 * Copyright: (C) Torsten Schenk
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 */
16
17#include "pcm.h"
18#include "chip.h"
19#include "comm.h"
20#include "control.h"
21
22enum {
23 OUT_N_CHANNELS = 6, IN_N_CHANNELS = 4
24};
25
26/* keep next two synced with
27 * FW_EP_W_MAX_PACKET_SIZE[] and RATES_MAX_PACKET_SIZE
28 * and CONTROL_RATE_XXX in control.h */
29static const int rates_in_packet_size[] = { 228, 228, 420, 420, 404, 404 };
30static const int rates_out_packet_size[] = { 228, 228, 420, 420, 604, 604 };
31static const int rates[] = { 44100, 48000, 88200, 96000, 176400, 192000 };
32static const int rates_alsaid[] = {
33 SNDRV_PCM_RATE_44100, SNDRV_PCM_RATE_48000,
34 SNDRV_PCM_RATE_88200, SNDRV_PCM_RATE_96000,
35 SNDRV_PCM_RATE_176400, SNDRV_PCM_RATE_192000 };
36
37enum { /* settings for pcm */
38 OUT_EP = 6, IN_EP = 2, MAX_BUFSIZE = 128 * 1024
39};
40
41enum { /* pcm streaming states */
42 STREAM_DISABLED, /* no pcm streaming */
43 STREAM_STARTING, /* pcm streaming requested, waiting to become ready */
44 STREAM_RUNNING, /* pcm streaming running */
45 STREAM_STOPPING
46};
47
48static const struct snd_pcm_hardware pcm_hw = {
49 .info = SNDRV_PCM_INFO_MMAP |
50 SNDRV_PCM_INFO_INTERLEAVED |
51 SNDRV_PCM_INFO_BLOCK_TRANSFER |
52 SNDRV_PCM_INFO_MMAP_VALID |
53 SNDRV_PCM_INFO_BATCH,
54
55 .formats = SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE,
56
57 .rates = SNDRV_PCM_RATE_44100 |
58 SNDRV_PCM_RATE_48000 |
59 SNDRV_PCM_RATE_88200 |
60 SNDRV_PCM_RATE_96000 |
61 SNDRV_PCM_RATE_176400 |
62 SNDRV_PCM_RATE_192000,
63
64 .rate_min = 44100,
65 .rate_max = 192000,
66 .channels_min = 1,
67 .channels_max = 0, /* set in pcm_open, depending on capture/playback */
68 .buffer_bytes_max = MAX_BUFSIZE,
69 .period_bytes_min = PCM_N_PACKETS_PER_URB * (PCM_MAX_PACKET_SIZE - 4),
70 .period_bytes_max = MAX_BUFSIZE,
71 .periods_min = 2,
72 .periods_max = 1024
73};
74
75static int usb6fire_pcm_set_rate(struct pcm_runtime *rt)
76{
77 int ret;
78 struct control_runtime *ctrl_rt = rt->chip->control;
79
80 ctrl_rt->usb_streaming = false;
81 ret = ctrl_rt->update_streaming(ctrl_rt);
82 if (ret < 0) {
83 snd_printk(KERN_ERR PREFIX "error stopping streaming while "
84 "setting samplerate %d.\n", rates[rt->rate]);
85 return ret;
86 }
87
88 ret = ctrl_rt->set_rate(ctrl_rt, rt->rate);
89 if (ret < 0) {
90 snd_printk(KERN_ERR PREFIX "error setting samplerate %d.\n",
91 rates[rt->rate]);
92 return ret;
93 }
94
95 ret = ctrl_rt->set_channels(ctrl_rt, OUT_N_CHANNELS, IN_N_CHANNELS,
96 false, false);
97 if (ret < 0) {
98 snd_printk(KERN_ERR PREFIX "error initializing channels "
99 "while setting samplerate %d.\n",
100 rates[rt->rate]);
101 return ret;
102 }
103
104 ctrl_rt->usb_streaming = true;
105 ret = ctrl_rt->update_streaming(ctrl_rt);
106 if (ret < 0) {
107 snd_printk(KERN_ERR PREFIX "error starting streaming while "
108 "setting samplerate %d.\n", rates[rt->rate]);
109 return ret;
110 }
111
112 rt->in_n_analog = IN_N_CHANNELS;
113 rt->out_n_analog = OUT_N_CHANNELS;
114 rt->in_packet_size = rates_in_packet_size[rt->rate];
115 rt->out_packet_size = rates_out_packet_size[rt->rate];
116 return 0;
117}
118
119static struct pcm_substream *usb6fire_pcm_get_substream(
120 struct snd_pcm_substream *alsa_sub)
121{
122 struct pcm_runtime *rt = snd_pcm_substream_chip(alsa_sub);
123
124 if (alsa_sub->stream == SNDRV_PCM_STREAM_PLAYBACK)
125 return &rt->playback;
126 else if (alsa_sub->stream == SNDRV_PCM_STREAM_CAPTURE)
127 return &rt->capture;
128 snd_printk(KERN_ERR PREFIX "error getting pcm substream slot.\n");
129 return NULL;
130}
131
132/* call with stream_mutex locked */
133static void usb6fire_pcm_stream_stop(struct pcm_runtime *rt)
134{
135 int i;
136 struct control_runtime *ctrl_rt = rt->chip->control;
137
138 if (rt->stream_state != STREAM_DISABLED) {
139 for (i = 0; i < PCM_N_URBS; i++) {
140 usb_kill_urb(&rt->in_urbs[i].instance);
141 usb_kill_urb(&rt->out_urbs[i].instance);
142 }
143 ctrl_rt->usb_streaming = false;
144 ctrl_rt->update_streaming(ctrl_rt);
145 rt->stream_state = STREAM_DISABLED;
146 }
147}
148
149/* call with stream_mutex locked */
150static int usb6fire_pcm_stream_start(struct pcm_runtime *rt)
151{
152 int ret;
153 int i;
154 int k;
155 struct usb_iso_packet_descriptor *packet;
156
157 if (rt->stream_state == STREAM_DISABLED) {
158 /* submit our in urbs */
159 rt->stream_wait_cond = false;
160 rt->stream_state = STREAM_STARTING;
161 for (i = 0; i < PCM_N_URBS; i++) {
162 for (k = 0; k < PCM_N_PACKETS_PER_URB; k++) {
163 packet = &rt->in_urbs[i].packets[k];
164 packet->offset = k * rt->in_packet_size;
165 packet->length = rt->in_packet_size;
166 packet->actual_length = 0;
167 packet->status = 0;
168 }
169 ret = usb_submit_urb(&rt->in_urbs[i].instance,
170 GFP_ATOMIC);
171 if (ret) {
172 usb6fire_pcm_stream_stop(rt);
173 return ret;
174 }
175 }
176
177 /* wait for first out urb to return (sent in in urb handler) */
178 wait_event_timeout(rt->stream_wait_queue, rt->stream_wait_cond,
179 HZ);
180 if (rt->stream_wait_cond)
181 rt->stream_state = STREAM_RUNNING;
182 else {
183 usb6fire_pcm_stream_stop(rt);
184 return -EIO;
185 }
186 }
187 return 0;
188}
189
190/* call with substream locked */
191static void usb6fire_pcm_capture(struct pcm_substream *sub, struct pcm_urb *urb)
192{
193 int i;
194 int frame;
195 int frame_count;
196 unsigned int total_length = 0;
197 struct pcm_runtime *rt = snd_pcm_substream_chip(sub->instance);
198 struct snd_pcm_runtime *alsa_rt = sub->instance->runtime;
199 u32 *src = NULL;
200 u32 *dest = (u32 *) (alsa_rt->dma_area + sub->dma_off
201 * (alsa_rt->frame_bits >> 3));
202 u32 *dest_end = (u32 *) (alsa_rt->dma_area + alsa_rt->buffer_size
203 * (alsa_rt->frame_bits >> 3));
204 int bytes_per_frame = alsa_rt->channels << 2;
205
206 for (i = 0; i < PCM_N_PACKETS_PER_URB; i++) {
207 /* at least 4 header bytes for valid packet.
208 * after that: 32 bits per sample for analog channels */
209 if (urb->packets[i].actual_length > 4)
210 frame_count = (urb->packets[i].actual_length - 4)
211 / (rt->in_n_analog << 2);
212 else
213 frame_count = 0;
214
215 if (alsa_rt->format == SNDRV_PCM_FORMAT_S24_LE)
216 src = (u32 *) (urb->buffer + total_length);
217 else if (alsa_rt->format == SNDRV_PCM_FORMAT_S32_LE)
218 src = (u32 *) (urb->buffer - 1 + total_length);
219 else
220 return;
221 src++; /* skip leading 4 bytes of every packet */
222 total_length += urb->packets[i].length;
223 for (frame = 0; frame < frame_count; frame++) {
224 memcpy(dest, src, bytes_per_frame);
225 dest += alsa_rt->channels;
226 src += rt->in_n_analog;
227 sub->dma_off++;
228 sub->period_off++;
229 if (dest == dest_end) {
230 sub->dma_off = 0;
231 dest = (u32 *) alsa_rt->dma_area;
232 }
233 }
234 }
235}
236
237/* call with substream locked */
238static void usb6fire_pcm_playback(struct pcm_substream *sub,
239 struct pcm_urb *urb)
240{
241 int i;
242 int frame;
243 int frame_count;
244 struct pcm_runtime *rt = snd_pcm_substream_chip(sub->instance);
245 struct snd_pcm_runtime *alsa_rt = sub->instance->runtime;
246 u32 *src = (u32 *) (alsa_rt->dma_area + sub->dma_off
247 * (alsa_rt->frame_bits >> 3));
248 u32 *src_end = (u32 *) (alsa_rt->dma_area + alsa_rt->buffer_size
249 * (alsa_rt->frame_bits >> 3));
250 u32 *dest;
251 int bytes_per_frame = alsa_rt->channels << 2;
252
253 if (alsa_rt->format == SNDRV_PCM_FORMAT_S32_LE)
254 dest = (u32 *) (urb->buffer - 1);
255 else if (alsa_rt->format == SNDRV_PCM_FORMAT_S24_LE)
256 dest = (u32 *) (urb->buffer);
257 else {
258 snd_printk(KERN_ERR PREFIX "Unknown sample format.");
259 return;
260 }
261
262 for (i = 0; i < PCM_N_PACKETS_PER_URB; i++) {
263 /* at least 4 header bytes for valid packet.
264 * after that: 32 bits per sample for analog channels */
265 if (urb->packets[i].length > 4)
266 frame_count = (urb->packets[i].length - 4)
267 / (rt->out_n_analog << 2);
268 else
269 frame_count = 0;
270 dest++; /* skip leading 4 bytes of every frame */
271 for (frame = 0; frame < frame_count; frame++) {
272 memcpy(dest, src, bytes_per_frame);
273 src += alsa_rt->channels;
274 dest += rt->out_n_analog;
275 sub->dma_off++;
276 sub->period_off++;
277 if (src == src_end) {
278 src = (u32 *) alsa_rt->dma_area;
279 sub->dma_off = 0;
280 }
281 }
282 }
283}
284
285static void usb6fire_pcm_in_urb_handler(struct urb *usb_urb)
286{
287 struct pcm_urb *in_urb = usb_urb->context;
288 struct pcm_urb *out_urb = in_urb->peer;
289 struct pcm_runtime *rt = in_urb->chip->pcm;
290 struct pcm_substream *sub;
291 unsigned long flags;
292 int total_length = 0;
293 int frame_count;
294 int frame;
295 int channel;
296 int i;
297 u8 *dest;
298
299 if (usb_urb->status || rt->panic || rt->stream_state == STREAM_STOPPING)
300 return;
301 for (i = 0; i < PCM_N_PACKETS_PER_URB; i++)
302 if (in_urb->packets[i].status) {
303 rt->panic = true;
304 return;
305 }
306
307 if (rt->stream_state == STREAM_DISABLED) {
308 snd_printk(KERN_ERR PREFIX "internal error: "
309 "stream disabled in in-urb handler.\n");
310 return;
311 }
312
313 /* receive our capture data */
314 sub = &rt->capture;
315 spin_lock_irqsave(&sub->lock, flags);
316 if (sub->active) {
317 usb6fire_pcm_capture(sub, in_urb);
318 if (sub->period_off >= sub->instance->runtime->period_size) {
319 sub->period_off %= sub->instance->runtime->period_size;
320 spin_unlock_irqrestore(&sub->lock, flags);
321 snd_pcm_period_elapsed(sub->instance);
322 } else
323 spin_unlock_irqrestore(&sub->lock, flags);
324 } else
325 spin_unlock_irqrestore(&sub->lock, flags);
326
327 /* setup out urb structure */
328 for (i = 0; i < PCM_N_PACKETS_PER_URB; i++) {
329 out_urb->packets[i].offset = total_length;
330 out_urb->packets[i].length = (in_urb->packets[i].actual_length
331 - 4) / (rt->in_n_analog << 2)
332 * (rt->out_n_analog << 2) + 4;
333 out_urb->packets[i].status = 0;
334 total_length += out_urb->packets[i].length;
335 }
336 memset(out_urb->buffer, 0, total_length);
337
338 /* now send our playback data (if a free out urb was found) */
339 sub = &rt->playback;
340 spin_lock_irqsave(&sub->lock, flags);
341 if (sub->active) {
342 usb6fire_pcm_playback(sub, out_urb);
343 if (sub->period_off >= sub->instance->runtime->period_size) {
344 sub->period_off %= sub->instance->runtime->period_size;
345 spin_unlock_irqrestore(&sub->lock, flags);
346 snd_pcm_period_elapsed(sub->instance);
347 } else
348 spin_unlock_irqrestore(&sub->lock, flags);
349 } else
350 spin_unlock_irqrestore(&sub->lock, flags);
351
352 /* setup the 4th byte of each sample (0x40 for analog channels) */
353 dest = out_urb->buffer;
354 for (i = 0; i < PCM_N_PACKETS_PER_URB; i++)
355 if (out_urb->packets[i].length >= 4) {
356 frame_count = (out_urb->packets[i].length - 4)
357 / (rt->out_n_analog << 2);
358 *(dest++) = 0xaa;
359 *(dest++) = 0xaa;
360 *(dest++) = frame_count;
361 *(dest++) = 0x00;
362 for (frame = 0; frame < frame_count; frame++)
363 for (channel = 0;
364 channel < rt->out_n_analog;
365 channel++) {
366 dest += 3; /* skip sample data */
367 *(dest++) = 0x40;
368 }
369 }
370 usb_submit_urb(&out_urb->instance, GFP_ATOMIC);
371 usb_submit_urb(&in_urb->instance, GFP_ATOMIC);
372}
373
374static void usb6fire_pcm_out_urb_handler(struct urb *usb_urb)
375{
376 struct pcm_urb *urb = usb_urb->context;
377 struct pcm_runtime *rt = urb->chip->pcm;
378
379 if (rt->stream_state == STREAM_STARTING) {
380 rt->stream_wait_cond = true;
381 wake_up(&rt->stream_wait_queue);
382 }
383}
384
385static int usb6fire_pcm_open(struct snd_pcm_substream *alsa_sub)
386{
387 struct pcm_runtime *rt = snd_pcm_substream_chip(alsa_sub);
388 struct pcm_substream *sub = NULL;
389 struct snd_pcm_runtime *alsa_rt = alsa_sub->runtime;
390
391 if (rt->panic)
392 return -EPIPE;
393
394 mutex_lock(&rt->stream_mutex);
395 alsa_rt->hw = pcm_hw;
396
397 if (alsa_sub->stream == SNDRV_PCM_STREAM_PLAYBACK) {
398 if (rt->rate < ARRAY_SIZE(rates))
399 alsa_rt->hw.rates = rates_alsaid[rt->rate];
400 alsa_rt->hw.channels_max = OUT_N_CHANNELS;
401 sub = &rt->playback;
402 } else if (alsa_sub->stream == SNDRV_PCM_STREAM_CAPTURE) {
403 if (rt->rate < ARRAY_SIZE(rates))
404 alsa_rt->hw.rates = rates_alsaid[rt->rate];
405 alsa_rt->hw.channels_max = IN_N_CHANNELS;
406 sub = &rt->capture;
407 }
408
409 if (!sub) {
410 mutex_unlock(&rt->stream_mutex);
411 snd_printk(KERN_ERR PREFIX "invalid stream type.\n");
412 return -EINVAL;
413 }
414
415 sub->instance = alsa_sub;
416 sub->active = false;
417 mutex_unlock(&rt->stream_mutex);
418 return 0;
419}
420
421static int usb6fire_pcm_close(struct snd_pcm_substream *alsa_sub)
422{
423 struct pcm_runtime *rt = snd_pcm_substream_chip(alsa_sub);
424 struct pcm_substream *sub = usb6fire_pcm_get_substream(alsa_sub);
425 unsigned long flags;
426
427 if (rt->panic)
428 return 0;
429
430 mutex_lock(&rt->stream_mutex);
431 if (sub) {
432 /* deactivate substream */
433 spin_lock_irqsave(&sub->lock, flags);
434 sub->instance = NULL;
435 sub->active = false;
436 spin_unlock_irqrestore(&sub->lock, flags);
437
438 /* all substreams closed? if so, stop streaming */
439 if (!rt->playback.instance && !rt->capture.instance) {
440 usb6fire_pcm_stream_stop(rt);
441 rt->rate = ARRAY_SIZE(rates);
442 }
443 }
444 mutex_unlock(&rt->stream_mutex);
445 return 0;
446}
447
448static int usb6fire_pcm_hw_params(struct snd_pcm_substream *alsa_sub,
449 struct snd_pcm_hw_params *hw_params)
450{
451 return snd_pcm_lib_malloc_pages(alsa_sub,
452 params_buffer_bytes(hw_params));
453}
454
455static int usb6fire_pcm_hw_free(struct snd_pcm_substream *alsa_sub)
456{
457 return snd_pcm_lib_free_pages(alsa_sub);
458}
459
460static int usb6fire_pcm_prepare(struct snd_pcm_substream *alsa_sub)
461{
462 struct pcm_runtime *rt = snd_pcm_substream_chip(alsa_sub);
463 struct pcm_substream *sub = usb6fire_pcm_get_substream(alsa_sub);
464 struct snd_pcm_runtime *alsa_rt = alsa_sub->runtime;
465 int ret;
466
467 if (rt->panic)
468 return -EPIPE;
469 if (!sub)
470 return -ENODEV;
471
472 mutex_lock(&rt->stream_mutex);
473 sub->dma_off = 0;
474 sub->period_off = 0;
475
476 if (rt->stream_state == STREAM_DISABLED) {
477 for (rt->rate = 0; rt->rate < ARRAY_SIZE(rates); rt->rate++)
478 if (alsa_rt->rate == rates[rt->rate])
479 break;
480 if (rt->rate == ARRAY_SIZE(rates)) {
481 mutex_unlock(&rt->stream_mutex);
482 snd_printk("invalid rate %d in prepare.\n",
483 alsa_rt->rate);
484 return -EINVAL;
485 }
486
487 ret = usb6fire_pcm_set_rate(rt);
488 if (ret) {
489 mutex_unlock(&rt->stream_mutex);
490 return ret;
491 }
492 ret = usb6fire_pcm_stream_start(rt);
493 if (ret) {
494 mutex_unlock(&rt->stream_mutex);
495 snd_printk(KERN_ERR PREFIX
496 "could not start pcm stream.\n");
497 return ret;
498 }
499 }
500 mutex_unlock(&rt->stream_mutex);
501 return 0;
502}
503
504static int usb6fire_pcm_trigger(struct snd_pcm_substream *alsa_sub, int cmd)
505{
506 struct pcm_substream *sub = usb6fire_pcm_get_substream(alsa_sub);
507 struct pcm_runtime *rt = snd_pcm_substream_chip(alsa_sub);
508 unsigned long flags;
509
510 if (rt->panic)
511 return -EPIPE;
512 if (!sub)
513 return -ENODEV;
514
515 switch (cmd) {
516 case SNDRV_PCM_TRIGGER_START:
517 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
518 spin_lock_irqsave(&sub->lock, flags);
519 sub->active = true;
520 spin_unlock_irqrestore(&sub->lock, flags);
521 return 0;
522
523 case SNDRV_PCM_TRIGGER_STOP:
524 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
525 spin_lock_irqsave(&sub->lock, flags);
526 sub->active = false;
527 spin_unlock_irqrestore(&sub->lock, flags);
528 return 0;
529
530 default:
531 return -EINVAL;
532 }
533}
534
535static snd_pcm_uframes_t usb6fire_pcm_pointer(
536 struct snd_pcm_substream *alsa_sub)
537{
538 struct pcm_substream *sub = usb6fire_pcm_get_substream(alsa_sub);
539 struct pcm_runtime *rt = snd_pcm_substream_chip(alsa_sub);
540 unsigned long flags;
541 snd_pcm_uframes_t ret;
542
543 if (rt->panic || !sub)
544 return SNDRV_PCM_STATE_XRUN;
545
546 spin_lock_irqsave(&sub->lock, flags);
547 ret = sub->dma_off;
548 spin_unlock_irqrestore(&sub->lock, flags);
549 return ret;
550}
551
552static struct snd_pcm_ops pcm_ops = {
553 .open = usb6fire_pcm_open,
554 .close = usb6fire_pcm_close,
555 .ioctl = snd_pcm_lib_ioctl,
556 .hw_params = usb6fire_pcm_hw_params,
557 .hw_free = usb6fire_pcm_hw_free,
558 .prepare = usb6fire_pcm_prepare,
559 .trigger = usb6fire_pcm_trigger,
560 .pointer = usb6fire_pcm_pointer,
561};
562
563static void __devinit usb6fire_pcm_init_urb(struct pcm_urb *urb,
564 struct sfire_chip *chip, bool in, int ep,
565 void (*handler)(struct urb *))
566{
567 urb->chip = chip;
568 usb_init_urb(&urb->instance);
569 urb->instance.transfer_buffer = urb->buffer;
570 urb->instance.transfer_buffer_length =
571 PCM_N_PACKETS_PER_URB * PCM_MAX_PACKET_SIZE;
572 urb->instance.dev = chip->dev;
573 urb->instance.pipe = in ? usb_rcvisocpipe(chip->dev, ep)
574 : usb_sndisocpipe(chip->dev, ep);
575 urb->instance.interval = 1;
576 urb->instance.transfer_flags = URB_ISO_ASAP;
577 urb->instance.complete = handler;
578 urb->instance.context = urb;
579 urb->instance.number_of_packets = PCM_N_PACKETS_PER_URB;
580}
581
582int __devinit usb6fire_pcm_init(struct sfire_chip *chip)
583{
584 int i;
585 int ret;
586 struct snd_pcm *pcm;
587 struct pcm_runtime *rt =
588 kzalloc(sizeof(struct pcm_runtime), GFP_KERNEL);
589
590 if (!rt)
591 return -ENOMEM;
592
593 rt->chip = chip;
594 rt->stream_state = STREAM_DISABLED;
595 rt->rate = ARRAY_SIZE(rates);
596 init_waitqueue_head(&rt->stream_wait_queue);
597 mutex_init(&rt->stream_mutex);
598
599 spin_lock_init(&rt->playback.lock);
600 spin_lock_init(&rt->capture.lock);
601
602 for (i = 0; i < PCM_N_URBS; i++) {
603 usb6fire_pcm_init_urb(&rt->in_urbs[i], chip, true, IN_EP,
604 usb6fire_pcm_in_urb_handler);
605 usb6fire_pcm_init_urb(&rt->out_urbs[i], chip, false, OUT_EP,
606 usb6fire_pcm_out_urb_handler);
607
608 rt->in_urbs[i].peer = &rt->out_urbs[i];
609 rt->out_urbs[i].peer = &rt->in_urbs[i];
610 }
611
612 ret = snd_pcm_new(chip->card, "DMX6FireUSB", 0, 1, 1, &pcm);
613 if (ret < 0) {
614 kfree(rt);
615 snd_printk(KERN_ERR PREFIX "cannot create pcm instance.\n");
616 return ret;
617 }
618
619 pcm->private_data = rt;
620 strcpy(pcm->name, "DMX 6Fire USB");
621 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &pcm_ops);
622 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pcm_ops);
623
624 ret = snd_pcm_lib_preallocate_pages_for_all(pcm,
625 SNDRV_DMA_TYPE_CONTINUOUS,
626 snd_dma_continuous_data(GFP_KERNEL),
627 MAX_BUFSIZE, MAX_BUFSIZE);
628 if (ret) {
629 kfree(rt);
630 snd_printk(KERN_ERR PREFIX
631 "error preallocating pcm buffers.\n");
632 return ret;
633 }
634 rt->instance = pcm;
635
636 chip->pcm = rt;
637 return 0;
638}
639
640void usb6fire_pcm_abort(struct sfire_chip *chip)
641{
642 struct pcm_runtime *rt = chip->pcm;
643 int i;
644
645 if (rt) {
646 rt->panic = true;
647
648 if (rt->playback.instance)
649 snd_pcm_stop(rt->playback.instance,
650 SNDRV_PCM_STATE_XRUN);
651 if (rt->capture.instance)
652 snd_pcm_stop(rt->capture.instance,
653 SNDRV_PCM_STATE_XRUN);
654
655 for (i = 0; i < PCM_N_URBS; i++) {
656 usb_poison_urb(&rt->in_urbs[i].instance);
657 usb_poison_urb(&rt->out_urbs[i].instance);
658 }
659
660 }
661}
662
663void usb6fire_pcm_destroy(struct sfire_chip *chip)
664{
665 kfree(chip->pcm);
666 chip->pcm = NULL;
667}
diff --git a/sound/usb/6fire/pcm.h b/sound/usb/6fire/pcm.h
new file mode 100644
index 000000000000..2bee81374002
--- /dev/null
+++ b/sound/usb/6fire/pcm.h
@@ -0,0 +1,76 @@
1/*
2 * Linux driver for TerraTec DMX 6Fire USB
3 *
4 * Author: Torsten Schenk <torsten.schenk@zoho.com>
5 * Created: Jan 01, 2011
6 * Version: 0.3.0
7 * Copyright: (C) Torsten Schenk
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 */
14
15#ifndef USB6FIRE_PCM_H
16#define USB6FIRE_PCM_H
17
18#include <sound/pcm.h>
19#include <linux/mutex.h>
20
21#include "common.h"
22
23enum /* settings for pcm */
24{
25 /* maximum of EP_W_MAX_PACKET_SIZE[] (see firmware.c) */
26 PCM_N_URBS = 16, PCM_N_PACKETS_PER_URB = 8, PCM_MAX_PACKET_SIZE = 604
27};
28
29struct pcm_urb {
30 struct sfire_chip *chip;
31
32 /* BEGIN DO NOT SEPARATE */
33 struct urb instance;
34 struct usb_iso_packet_descriptor packets[PCM_N_PACKETS_PER_URB];
35 /* END DO NOT SEPARATE */
36 u8 buffer[PCM_N_PACKETS_PER_URB * PCM_MAX_PACKET_SIZE];
37
38 struct pcm_urb *peer;
39};
40
41struct pcm_substream {
42 spinlock_t lock;
43 struct snd_pcm_substream *instance;
44
45 bool active;
46
47 snd_pcm_uframes_t dma_off; /* current position in alsa dma_area */
48 snd_pcm_uframes_t period_off; /* current position in current period */
49};
50
51struct pcm_runtime {
52 struct sfire_chip *chip;
53 struct snd_pcm *instance;
54
55 struct pcm_substream playback;
56 struct pcm_substream capture;
57 bool panic; /* if set driver won't do anymore pcm on device */
58
59 struct pcm_urb in_urbs[PCM_N_URBS];
60 struct pcm_urb out_urbs[PCM_N_URBS];
61 int in_packet_size;
62 int out_packet_size;
63 int in_n_analog; /* number of analog channels soundcard sends */
64 int out_n_analog; /* number of analog channels soundcard receives */
65
66 struct mutex stream_mutex;
67 u8 stream_state; /* one of STREAM_XXX (pcm.c) */
68 u8 rate; /* one of PCM_RATE_XXX */
69 wait_queue_head_t stream_wait_queue;
70 bool stream_wait_cond;
71};
72
73int __devinit usb6fire_pcm_init(struct sfire_chip *chip);
74void usb6fire_pcm_abort(struct sfire_chip *chip);
75void usb6fire_pcm_destroy(struct sfire_chip *chip);
76#endif /* USB6FIRE_PCM_H */