]> Pileus Git - ~andy/linux/blob - sound/usb/6fire/comm.c
Merge tag 'v3.0' of git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux...
[~andy/linux] / 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  * 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
21 enum {
22         COMM_EP = 1,
23         COMM_FPGA_EP = 2
24 };
25
26 static 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
38 static 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
60 static 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
97 static 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
111 static 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
120 static 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
129 int __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
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 }