diff options
author | Bryan Wu <cooloney@kernel.org> | 2009-06-03 09:17:58 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2009-06-16 00:44:47 -0400 |
commit | c6994e6f067cf0fc4c6cca3d164018b1150916f8 (patch) | |
tree | d971c529cbd4684e5c1c5fcb67f3f09d1cca4295 /drivers/usb/gadget/audio.c | |
parent | c47d7b09891abb4f8b222317c89c7469bed8db3a (diff) |
USB: gadget: add USB Audio Gadget driver
Funtions added:
- setup all the USB audio class device descriptors
- handle class specific setup request
- receive data from USB host by ISO transfer
- play audio data by ALSA sound card
- open and setup playback PCM interface
- set default playback PCM parameters
- provide playback functions for USB audio driver
- provide PCM parameters set/get functions
Test on:
- Host: Ubuntu 8.10, kernel 2.6.27
- Gadget: EZKIT-BF548 with ASoC AD1980 codec
Todo:
- add real Mute control code
- add real Volume control code
- maybe find another way to replace dynamic buffer handling
with static buffer allocation
- test on Windows system
- provide control interface to handle mute/volume control
- provide capture interface in the future
- test on BF527, other USB device controler and other audio codec
Signed-off-by: Bryan Wu <cooloney@kernel.org>
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb/gadget/audio.c')
-rw-r--r-- | drivers/usb/gadget/audio.c | 302 |
1 files changed, 302 insertions, 0 deletions
diff --git a/drivers/usb/gadget/audio.c b/drivers/usb/gadget/audio.c new file mode 100644 index 000000000000..94de7e864614 --- /dev/null +++ b/drivers/usb/gadget/audio.c | |||
@@ -0,0 +1,302 @@ | |||
1 | /* | ||
2 | * audio.c -- Audio gadget driver | ||
3 | * | ||
4 | * Copyright (C) 2008 Bryan Wu <cooloney@kernel.org> | ||
5 | * Copyright (C) 2008 Analog Devices, Inc | ||
6 | * | ||
7 | * Enter bugs at http://blackfin.uclinux.org/ | ||
8 | * | ||
9 | * Licensed under the GPL-2 or later. | ||
10 | */ | ||
11 | |||
12 | /* #define VERBOSE_DEBUG */ | ||
13 | |||
14 | #include <linux/kernel.h> | ||
15 | #include <linux/utsname.h> | ||
16 | |||
17 | #include "u_audio.h" | ||
18 | |||
19 | #define DRIVER_DESC "Linux USB Audio Gadget" | ||
20 | #define DRIVER_VERSION "Dec 18, 2008" | ||
21 | |||
22 | /*-------------------------------------------------------------------------*/ | ||
23 | |||
24 | /* | ||
25 | * Kbuild is not very cooperative with respect to linking separately | ||
26 | * compiled library objects into one module. So for now we won't use | ||
27 | * separate compilation ... ensuring init/exit sections work to shrink | ||
28 | * the runtime footprint, and giving us at least some parts of what | ||
29 | * a "gcc --combine ... part1.c part2.c part3.c ... " build would. | ||
30 | */ | ||
31 | #include "composite.c" | ||
32 | #include "usbstring.c" | ||
33 | #include "config.c" | ||
34 | #include "epautoconf.c" | ||
35 | |||
36 | #include "u_audio.c" | ||
37 | #include "f_audio.c" | ||
38 | |||
39 | /*-------------------------------------------------------------------------*/ | ||
40 | |||
41 | /* DO NOT REUSE THESE IDs with a protocol-incompatible driver!! Ever!! | ||
42 | * Instead: allocate your own, using normal USB-IF procedures. | ||
43 | */ | ||
44 | |||
45 | /* Thanks to NetChip Technologies for donating this product ID. */ | ||
46 | #define AUDIO_VENDOR_NUM 0x0525 /* NetChip */ | ||
47 | #define AUDIO_PRODUCT_NUM 0xa4a1 /* Linux-USB Audio Gadget */ | ||
48 | |||
49 | /*-------------------------------------------------------------------------*/ | ||
50 | |||
51 | static struct usb_device_descriptor device_desc = { | ||
52 | .bLength = sizeof device_desc, | ||
53 | .bDescriptorType = USB_DT_DEVICE, | ||
54 | |||
55 | .bcdUSB = __constant_cpu_to_le16(0x200), | ||
56 | |||
57 | .bDeviceClass = USB_CLASS_PER_INTERFACE, | ||
58 | .bDeviceSubClass = 0, | ||
59 | .bDeviceProtocol = 0, | ||
60 | /* .bMaxPacketSize0 = f(hardware) */ | ||
61 | |||
62 | /* Vendor and product id defaults change according to what configs | ||
63 | * we support. (As does bNumConfigurations.) These values can | ||
64 | * also be overridden by module parameters. | ||
65 | */ | ||
66 | .idVendor = __constant_cpu_to_le16(AUDIO_VENDOR_NUM), | ||
67 | .idProduct = __constant_cpu_to_le16(AUDIO_PRODUCT_NUM), | ||
68 | /* .bcdDevice = f(hardware) */ | ||
69 | /* .iManufacturer = DYNAMIC */ | ||
70 | /* .iProduct = DYNAMIC */ | ||
71 | /* NO SERIAL NUMBER */ | ||
72 | .bNumConfigurations = 1, | ||
73 | }; | ||
74 | |||
75 | static struct usb_otg_descriptor otg_descriptor = { | ||
76 | .bLength = sizeof otg_descriptor, | ||
77 | .bDescriptorType = USB_DT_OTG, | ||
78 | |||
79 | /* REVISIT SRP-only hardware is possible, although | ||
80 | * it would not be called "OTG" ... | ||
81 | */ | ||
82 | .bmAttributes = USB_OTG_SRP | USB_OTG_HNP, | ||
83 | }; | ||
84 | |||
85 | static const struct usb_descriptor_header *otg_desc[] = { | ||
86 | (struct usb_descriptor_header *) &otg_descriptor, | ||
87 | NULL, | ||
88 | }; | ||
89 | |||
90 | /*-------------------------------------------------------------------------*/ | ||
91 | |||
92 | /** | ||
93 | * Handle USB audio endpoint set/get command in setup class request | ||
94 | */ | ||
95 | |||
96 | static int audio_set_endpoint_req(struct usb_configuration *c, | ||
97 | const struct usb_ctrlrequest *ctrl) | ||
98 | { | ||
99 | struct usb_composite_dev *cdev = c->cdev; | ||
100 | int value = -EOPNOTSUPP; | ||
101 | u16 ep = le16_to_cpu(ctrl->wIndex); | ||
102 | u16 len = le16_to_cpu(ctrl->wLength); | ||
103 | u16 w_value = le16_to_cpu(ctrl->wValue); | ||
104 | |||
105 | DBG(cdev, "bRequest 0x%x, w_value 0x%04x, len %d, endpoint %d\n", | ||
106 | ctrl->bRequest, w_value, len, ep); | ||
107 | |||
108 | switch (ctrl->bRequest) { | ||
109 | case SET_CUR: | ||
110 | value = 0; | ||
111 | break; | ||
112 | |||
113 | case SET_MIN: | ||
114 | break; | ||
115 | |||
116 | case SET_MAX: | ||
117 | break; | ||
118 | |||
119 | case SET_RES: | ||
120 | break; | ||
121 | |||
122 | case SET_MEM: | ||
123 | break; | ||
124 | |||
125 | default: | ||
126 | break; | ||
127 | } | ||
128 | |||
129 | return value; | ||
130 | } | ||
131 | |||
132 | static int audio_get_endpoint_req(struct usb_configuration *c, | ||
133 | const struct usb_ctrlrequest *ctrl) | ||
134 | { | ||
135 | struct usb_composite_dev *cdev = c->cdev; | ||
136 | int value = -EOPNOTSUPP; | ||
137 | u8 ep = ((le16_to_cpu(ctrl->wIndex) >> 8) & 0xFF); | ||
138 | u16 len = le16_to_cpu(ctrl->wLength); | ||
139 | u16 w_value = le16_to_cpu(ctrl->wValue); | ||
140 | |||
141 | DBG(cdev, "bRequest 0x%x, w_value 0x%04x, len %d, endpoint %d\n", | ||
142 | ctrl->bRequest, w_value, len, ep); | ||
143 | |||
144 | switch (ctrl->bRequest) { | ||
145 | case GET_CUR: | ||
146 | case GET_MIN: | ||
147 | case GET_MAX: | ||
148 | case GET_RES: | ||
149 | value = 3; | ||
150 | break; | ||
151 | case GET_MEM: | ||
152 | break; | ||
153 | default: | ||
154 | break; | ||
155 | } | ||
156 | |||
157 | return value; | ||
158 | } | ||
159 | |||
160 | static int | ||
161 | audio_setup(struct usb_configuration *c, const struct usb_ctrlrequest *ctrl) | ||
162 | { | ||
163 | struct usb_composite_dev *cdev = c->cdev; | ||
164 | struct usb_request *req = cdev->req; | ||
165 | int value = -EOPNOTSUPP; | ||
166 | u16 w_index = le16_to_cpu(ctrl->wIndex); | ||
167 | u16 w_value = le16_to_cpu(ctrl->wValue); | ||
168 | u16 w_length = le16_to_cpu(ctrl->wLength); | ||
169 | |||
170 | /* composite driver infrastructure handles everything except | ||
171 | * Audio class messages; interface activation uses set_alt(). | ||
172 | */ | ||
173 | switch (ctrl->bRequestType) { | ||
174 | case USB_AUDIO_SET_ENDPOINT: | ||
175 | value = audio_set_endpoint_req(c, ctrl); | ||
176 | break; | ||
177 | |||
178 | case USB_AUDIO_GET_ENDPOINT: | ||
179 | value = audio_get_endpoint_req(c, ctrl); | ||
180 | break; | ||
181 | |||
182 | default: | ||
183 | ERROR(cdev, "Invalid control req%02x.%02x v%04x i%04x l%d\n", | ||
184 | ctrl->bRequestType, ctrl->bRequest, | ||
185 | w_value, w_index, w_length); | ||
186 | } | ||
187 | |||
188 | /* respond with data transfer or status phase? */ | ||
189 | if (value >= 0) { | ||
190 | DBG(cdev, "Audio req%02x.%02x v%04x i%04x l%d\n", | ||
191 | ctrl->bRequestType, ctrl->bRequest, | ||
192 | w_value, w_index, w_length); | ||
193 | req->zero = 0; | ||
194 | req->length = value; | ||
195 | value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC); | ||
196 | if (value < 0) | ||
197 | ERROR(cdev, "Audio response on err %d\n", value); | ||
198 | } | ||
199 | |||
200 | /* device either stalls (value < 0) or reports success */ | ||
201 | return value; | ||
202 | } | ||
203 | |||
204 | /*-------------------------------------------------------------------------*/ | ||
205 | |||
206 | static int __init audio_do_config(struct usb_configuration *c) | ||
207 | { | ||
208 | /* FIXME alloc iConfiguration string, set it in c->strings */ | ||
209 | |||
210 | if (gadget_is_otg(c->cdev->gadget)) { | ||
211 | c->descriptors = otg_desc; | ||
212 | c->bmAttributes |= USB_CONFIG_ATT_WAKEUP; | ||
213 | } | ||
214 | |||
215 | audio_bind_config(c); | ||
216 | |||
217 | return 0; | ||
218 | } | ||
219 | |||
220 | static struct usb_configuration audio_config_driver = { | ||
221 | .label = DRIVER_DESC, | ||
222 | .bind = audio_do_config, | ||
223 | .setup = audio_setup, | ||
224 | .bConfigurationValue = 1, | ||
225 | /* .iConfiguration = DYNAMIC */ | ||
226 | .bmAttributes = USB_CONFIG_ATT_SELFPOWER, | ||
227 | }; | ||
228 | |||
229 | /*-------------------------------------------------------------------------*/ | ||
230 | |||
231 | static int __init audio_bind(struct usb_composite_dev *cdev) | ||
232 | { | ||
233 | int gcnum; | ||
234 | int status; | ||
235 | |||
236 | gcnum = usb_gadget_controller_number(cdev->gadget); | ||
237 | if (gcnum >= 0) | ||
238 | device_desc.bcdDevice = cpu_to_le16(0x0300 | gcnum); | ||
239 | else { | ||
240 | ERROR(cdev, "controller '%s' not recognized; trying %s\n", | ||
241 | cdev->gadget->name, | ||
242 | audio_config_driver.label); | ||
243 | device_desc.bcdDevice = | ||
244 | __constant_cpu_to_le16(0x0300 | 0x0099); | ||
245 | } | ||
246 | |||
247 | /* device descriptor strings: manufacturer, product */ | ||
248 | snprintf(manufacturer, sizeof manufacturer, "%s %s with %s", | ||
249 | init_utsname()->sysname, init_utsname()->release, | ||
250 | cdev->gadget->name); | ||
251 | status = usb_string_id(cdev); | ||
252 | if (status < 0) | ||
253 | goto fail; | ||
254 | strings_dev[STRING_MANUFACTURER_IDX].id = status; | ||
255 | device_desc.iManufacturer = status; | ||
256 | |||
257 | status = usb_string_id(cdev); | ||
258 | if (status < 0) | ||
259 | goto fail; | ||
260 | strings_dev[STRING_PRODUCT_IDX].id = status; | ||
261 | device_desc.iProduct = status; | ||
262 | |||
263 | status = usb_add_config(cdev, &audio_config_driver); | ||
264 | if (status < 0) | ||
265 | goto fail; | ||
266 | |||
267 | INFO(cdev, "%s, version: %s\n", DRIVER_DESC, DRIVER_VERSION); | ||
268 | return 0; | ||
269 | |||
270 | fail: | ||
271 | return status; | ||
272 | } | ||
273 | |||
274 | static int __exit audio_unbind(struct usb_composite_dev *cdev) | ||
275 | { | ||
276 | return 0; | ||
277 | } | ||
278 | |||
279 | static struct usb_composite_driver audio_driver = { | ||
280 | .name = "g_audio", | ||
281 | .dev = &device_desc, | ||
282 | .strings = audio_strings, | ||
283 | .bind = audio_bind, | ||
284 | .unbind = __exit_p(audio_unbind), | ||
285 | }; | ||
286 | |||
287 | static int __init init(void) | ||
288 | { | ||
289 | return usb_composite_register(&audio_driver); | ||
290 | } | ||
291 | module_init(init); | ||
292 | |||
293 | static void __exit cleanup(void) | ||
294 | { | ||
295 | usb_composite_unregister(&audio_driver); | ||
296 | } | ||
297 | module_exit(cleanup); | ||
298 | |||
299 | MODULE_DESCRIPTION(DRIVER_DESC); | ||
300 | MODULE_AUTHOR("Bryan Wu <cooloney@kernel.org>"); | ||
301 | MODULE_LICENSE("GPL"); | ||
302 | |||