]> rtime.felk.cvut.cz Git - linux-imx.git/blob - sound/usb/6fire/comm.c
9e6e3ffd86bbbc4212e72eb3d0c7cd69e6d44c8e
[linux-imx.git] / sound / usb / 6fire / comm.c
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  * Copyright:   (C) Torsten Schenk
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  */
15
16 #include "comm.h"
17 #include "chip.h"
18 #include "midi.h"
19
20 enum {
21         COMM_EP = 1,
22         COMM_FPGA_EP = 2
23 };
24
25 static void usb6fire_comm_init_urb(struct comm_runtime *rt, struct urb *urb,
26                 u8 *buffer, void *context, void(*handler)(struct urb *urb))
27 {
28         usb_init_urb(urb);
29         urb->transfer_buffer = buffer;
30         urb->pipe = usb_sndintpipe(rt->chip->dev, COMM_EP);
31         urb->complete = handler;
32         urb->context = context;
33         urb->interval = 1;
34         urb->dev = rt->chip->dev;
35 }
36
37 static void usb6fire_comm_receiver_handler(struct urb *urb)
38 {
39         struct comm_runtime *rt = urb->context;
40         struct midi_runtime *midi_rt = rt->chip->midi;
41
42         if (!urb->status) {
43                 if (rt->receiver_buffer[0] == 0x10) /* midi in event */
44                         if (midi_rt)
45                                 midi_rt->in_received(midi_rt,
46                                                 rt->receiver_buffer + 2,
47                                                 rt->receiver_buffer[1]);
48         }
49
50         if (!rt->chip->shutdown) {
51                 urb->status = 0;
52                 urb->actual_length = 0;
53                 if (usb_submit_urb(urb, GFP_ATOMIC) < 0)
54                         snd_printk(KERN_WARNING PREFIX
55                                         "comm data receiver aborted.\n");
56         }
57 }
58
59 static void usb6fire_comm_init_buffer(u8 *buffer, u8 id, u8 request,
60                 u8 reg, u8 vl, u8 vh)
61 {
62         buffer[0] = 0x01;
63         buffer[2] = request;
64         buffer[3] = id;
65         switch (request) {
66         case 0x02:
67                 buffer[1] = 0x05; /* length (starting at buffer[2]) */
68                 buffer[4] = reg;
69                 buffer[5] = vl;
70                 buffer[6] = vh;
71                 break;
72
73         case 0x12:
74                 buffer[1] = 0x0b; /* length (starting at buffer[2]) */
75                 buffer[4] = 0x00;
76                 buffer[5] = 0x18;
77                 buffer[6] = 0x05;
78                 buffer[7] = 0x00;
79                 buffer[8] = 0x01;
80                 buffer[9] = 0x00;
81                 buffer[10] = 0x9e;
82                 buffer[11] = reg;
83                 buffer[12] = vl;
84                 break;
85
86         case 0x20:
87         case 0x21:
88         case 0x22:
89                 buffer[1] = 0x04;
90                 buffer[4] = reg;
91                 buffer[5] = vl;
92                 break;
93         }
94 }
95
96 static int usb6fire_comm_send_buffer(u8 *buffer, struct usb_device *dev)
97 {
98         int ret;
99         int actual_len;
100
101         ret = usb_interrupt_msg(dev, usb_sndintpipe(dev, COMM_EP),
102                         buffer, buffer[1] + 2, &actual_len, HZ);
103         if (ret < 0)
104                 return ret;
105         else if (actual_len != buffer[1] + 2)
106                 return -EIO;
107         return 0;
108 }
109
110 static int usb6fire_comm_write8(struct comm_runtime *rt, u8 request,
111                 u8 reg, u8 value)
112 {
113         u8 buffer[13]; /* 13: maximum length of message */
114
115         usb6fire_comm_init_buffer(buffer, 0x00, request, reg, value, 0x00);
116         return usb6fire_comm_send_buffer(buffer, rt->chip->dev);
117 }
118
119 static int usb6fire_comm_write16(struct comm_runtime *rt, u8 request,
120                 u8 reg, u8 vl, u8 vh)
121 {
122         u8 buffer[13]; /* 13: maximum length of message */
123
124         usb6fire_comm_init_buffer(buffer, 0x00, request, reg, vl, vh);
125         return usb6fire_comm_send_buffer(buffer, rt->chip->dev);
126 }
127
128 int usb6fire_comm_init(struct sfire_chip *chip)
129 {
130         struct comm_runtime *rt = kzalloc(sizeof(struct comm_runtime),
131                         GFP_KERNEL);
132         struct urb *urb;
133         int ret;
134
135         if (!rt)
136                 return -ENOMEM;
137
138         urb = &rt->receiver;
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
164 void 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
172 void usb6fire_comm_destroy(struct sfire_chip *chip)
173 {
174         kfree(chip->comm);
175         chip->comm = NULL;
176 }