123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905 |
- /*
- * Gadget Function Driver for USB audio source device
- *
- * Copyright (C) 2012 Google, Inc.
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- */
- #include <asm/dma.h>
- #include <linux/dma-mapping.h>
- #include <linux/device.h>
- #include <linux/usb/audio.h>
- #include <linux/wait.h>
- #include <sound/core.h>
- #include <sound/initval.h>
- #include <sound/pcm.h>
- #define SAMPLE_RATE 44100
- #define FRAMES_PER_MSEC (SAMPLE_RATE / 1000)
- #define IN_EP_MAX_PACKET_SIZE 256
- /* Number of requests to allocate */
- #define IN_EP_REQ_COUNT 4
- #define AUDIO_AC_INTERFACE 0
- #define AUDIO_AS_INTERFACE 1
- #define AUDIO_NUM_INTERFACES 2
- /* B.3.1 Standard AC Interface Descriptor */
- static struct usb_interface_descriptor audio_source_ac_interface_desc = {
- .bLength = USB_DT_INTERFACE_SIZE,
- .bDescriptorType = USB_DT_INTERFACE,
- .bNumEndpoints = 0,
- .bInterfaceClass = USB_CLASS_AUDIO,
- .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL,
- };
- #define UAC_DT_AC_HEADER_LENGTH UAC_DT_AC_HEADER_SIZE(AUDIO_NUM_INTERFACES)
- /* 1 input terminal, 1 output terminal and 1 feature unit */
- #define UAC_DT_TOTAL_LENGTH (UAC_DT_AC_HEADER_LENGTH \
- + UAC_DT_INPUT_TERMINAL_SIZE + UAC_DT_OUTPUT_TERMINAL_SIZE \
- + UAC_DT_FEATURE_UNIT_SIZE(0))
- /* B.3.2 Class-Specific AC Interface Descriptor */
- static struct uac1_ac_header_descriptor_2 audio_source_ac_header_desc = {
- .bLength = UAC_DT_AC_HEADER_LENGTH,
- .bDescriptorType = USB_DT_CS_INTERFACE,
- .bDescriptorSubtype = UAC_HEADER,
- .bcdADC = __constant_cpu_to_le16(0x0100),
- .wTotalLength = __constant_cpu_to_le16(UAC_DT_TOTAL_LENGTH),
- .bInCollection = AUDIO_NUM_INTERFACES,
- .baInterfaceNr = {
- [0] = AUDIO_AC_INTERFACE,
- [1] = AUDIO_AS_INTERFACE,
- }
- };
- #define INPUT_TERMINAL_ID 1
- static struct uac_input_terminal_descriptor input_terminal_desc = {
- .bLength = UAC_DT_INPUT_TERMINAL_SIZE,
- .bDescriptorType = USB_DT_CS_INTERFACE,
- .bDescriptorSubtype = UAC_INPUT_TERMINAL,
- .bTerminalID = INPUT_TERMINAL_ID,
- .wTerminalType = UAC_INPUT_TERMINAL_MICROPHONE,
- .bAssocTerminal = 0,
- .wChannelConfig = 0x3,
- };
- DECLARE_UAC_FEATURE_UNIT_DESCRIPTOR(0);
- #define FEATURE_UNIT_ID 2
- static struct uac_feature_unit_descriptor_0 feature_unit_desc = {
- .bLength = UAC_DT_FEATURE_UNIT_SIZE(0),
- .bDescriptorType = USB_DT_CS_INTERFACE,
- .bDescriptorSubtype = UAC_FEATURE_UNIT,
- .bUnitID = FEATURE_UNIT_ID,
- .bSourceID = INPUT_TERMINAL_ID,
- .bControlSize = 2,
- };
- #define OUTPUT_TERMINAL_ID 3
- static struct uac1_output_terminal_descriptor output_terminal_desc = {
- .bLength = UAC_DT_OUTPUT_TERMINAL_SIZE,
- .bDescriptorType = USB_DT_CS_INTERFACE,
- .bDescriptorSubtype = UAC_OUTPUT_TERMINAL,
- .bTerminalID = OUTPUT_TERMINAL_ID,
- .wTerminalType = UAC_TERMINAL_STREAMING,
- .bAssocTerminal = FEATURE_UNIT_ID,
- .bSourceID = FEATURE_UNIT_ID,
- };
- /* B.4.1 Standard AS Interface Descriptor */
- static struct usb_interface_descriptor as_interface_alt_0_desc = {
- .bLength = USB_DT_INTERFACE_SIZE,
- .bDescriptorType = USB_DT_INTERFACE,
- .bAlternateSetting = 0,
- .bNumEndpoints = 0,
- .bInterfaceClass = USB_CLASS_AUDIO,
- .bInterfaceSubClass = USB_SUBCLASS_AUDIOSTREAMING,
- };
- static struct usb_interface_descriptor as_interface_alt_1_desc = {
- .bLength = USB_DT_INTERFACE_SIZE,
- .bDescriptorType = USB_DT_INTERFACE,
- .bAlternateSetting = 1,
- .bNumEndpoints = 1,
- .bInterfaceClass = USB_CLASS_AUDIO,
- .bInterfaceSubClass = USB_SUBCLASS_AUDIOSTREAMING,
- };
- /* B.4.2 Class-Specific AS Interface Descriptor */
- static struct uac1_as_header_descriptor as_header_desc = {
- .bLength = UAC_DT_AS_HEADER_SIZE,
- .bDescriptorType = USB_DT_CS_INTERFACE,
- .bDescriptorSubtype = UAC_AS_GENERAL,
- .bTerminalLink = INPUT_TERMINAL_ID,
- .bDelay = 1,
- .wFormatTag = UAC_FORMAT_TYPE_I_PCM,
- };
- static struct uac_format_type_i_discrete_descriptor_1 as_type_i_desc = {
- .bLength = UAC_FORMAT_TYPE_I_DISCRETE_DESC_SIZE(1),
- .bDescriptorType = USB_DT_CS_INTERFACE,
- .bDescriptorSubtype = UAC_FORMAT_TYPE,
- .bFormatType = UAC_FORMAT_TYPE_I,
- .bSubframeSize = 2,
- .bBitResolution = 16,
- .bSamFreqType = 1,
- };
- /* Standard ISO IN Endpoint Descriptor for highspeed */
- static struct usb_endpoint_descriptor hs_as_in_ep_desc = {
- .bLength = USB_DT_ENDPOINT_AUDIO_SIZE,
- .bDescriptorType = USB_DT_ENDPOINT,
- .bEndpointAddress = USB_DIR_IN,
- .bmAttributes = USB_ENDPOINT_SYNC_SYNC
- | USB_ENDPOINT_XFER_ISOC,
- .wMaxPacketSize = __constant_cpu_to_le16(IN_EP_MAX_PACKET_SIZE),
- .bInterval = 4, /* poll 1 per millisecond */
- };
- /* Standard ISO IN Endpoint Descriptor for highspeed */
- static struct usb_endpoint_descriptor fs_as_in_ep_desc = {
- .bLength = USB_DT_ENDPOINT_AUDIO_SIZE,
- .bDescriptorType = USB_DT_ENDPOINT,
- .bEndpointAddress = USB_DIR_IN,
- .bmAttributes = USB_ENDPOINT_SYNC_SYNC
- | USB_ENDPOINT_XFER_ISOC,
- .wMaxPacketSize = __constant_cpu_to_le16(IN_EP_MAX_PACKET_SIZE),
- .bInterval = 1, /* poll 1 per millisecond */
- };
- /* Class-specific AS ISO OUT Endpoint Descriptor */
- static struct uac_iso_endpoint_descriptor as_iso_in_desc = {
- .bLength = UAC_ISO_ENDPOINT_DESC_SIZE,
- .bDescriptorType = USB_DT_CS_ENDPOINT,
- .bDescriptorSubtype = UAC_EP_GENERAL,
- .bmAttributes = 1,
- .bLockDelayUnits = 1,
- .wLockDelay = __constant_cpu_to_le16(1),
- };
- static struct usb_descriptor_header *hs_audio_desc[] = {
- (struct usb_descriptor_header *)&audio_source_ac_interface_desc,
- (struct usb_descriptor_header *)&audio_source_ac_header_desc,
- (struct usb_descriptor_header *)&input_terminal_desc,
- (struct usb_descriptor_header *)&output_terminal_desc,
- (struct usb_descriptor_header *)&feature_unit_desc,
- (struct usb_descriptor_header *)&as_interface_alt_0_desc,
- (struct usb_descriptor_header *)&as_interface_alt_1_desc,
- (struct usb_descriptor_header *)&as_header_desc,
- (struct usb_descriptor_header *)&as_type_i_desc,
- (struct usb_descriptor_header *)&hs_as_in_ep_desc,
- (struct usb_descriptor_header *)&as_iso_in_desc,
- NULL,
- };
- static struct usb_descriptor_header *fs_audio_desc[] = {
- (struct usb_descriptor_header *)&audio_source_ac_interface_desc,
- (struct usb_descriptor_header *)&audio_source_ac_header_desc,
- (struct usb_descriptor_header *)&input_terminal_desc,
- (struct usb_descriptor_header *)&output_terminal_desc,
- (struct usb_descriptor_header *)&feature_unit_desc,
- (struct usb_descriptor_header *)&as_interface_alt_0_desc,
- (struct usb_descriptor_header *)&as_interface_alt_1_desc,
- (struct usb_descriptor_header *)&as_header_desc,
- (struct usb_descriptor_header *)&as_type_i_desc,
- (struct usb_descriptor_header *)&fs_as_in_ep_desc,
- (struct usb_descriptor_header *)&as_iso_in_desc,
- NULL,
- };
- static struct snd_pcm_hardware audio_hw_info = {
- .info = SNDRV_PCM_INFO_MMAP |
- SNDRV_PCM_INFO_MMAP_VALID |
- SNDRV_PCM_INFO_BATCH |
- SNDRV_PCM_INFO_INTERLEAVED |
- SNDRV_PCM_INFO_BLOCK_TRANSFER,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
- .channels_min = 2,
- .channels_max = 2,
- .rate_min = SAMPLE_RATE,
- .rate_max = SAMPLE_RATE,
- .buffer_bytes_max = 1024 * 1024,
- .period_bytes_min = 64,
- .period_bytes_max = 512 * 1024,
- .periods_min = 2,
- .periods_max = 1024,
- };
- /*-------------------------------------------------------------------------*/
- struct audio_source_config {
- int card;
- int device;
- };
- struct audio_dev {
- struct usb_function func;
- struct snd_card *card;
- struct snd_pcm *pcm;
- struct snd_pcm_substream *substream;
- struct list_head idle_reqs;
- struct usb_ep *in_ep;
- spinlock_t lock;
- /* beginning, end and current position in our buffer */
- void *buffer_start;
- void *buffer_end;
- void *buffer_pos;
- /* byte size of a "period" */
- unsigned int period;
- /* bytes sent since last call to snd_pcm_period_elapsed */
- unsigned int period_offset;
- /* time we started playing */
- ktime_t start_time;
- /* number of frames sent since start_time */
- s64 frames_sent;
- bool audio_ep_enabled;
- };
- static inline struct audio_dev *func_to_audio_source(struct usb_function *f)
- {
- return container_of(f, struct audio_dev, func);
- }
- /*-------------------------------------------------------------------------*/
- static struct usb_request *audio_request_new(struct usb_ep *ep, int buffer_size)
- {
- struct usb_request *req = usb_ep_alloc_request(ep, GFP_KERNEL);
- if (!req)
- return NULL;
- req->buf = kmalloc(buffer_size, GFP_KERNEL);
- if (!req->buf) {
- usb_ep_free_request(ep, req);
- return NULL;
- }
- req->length = buffer_size;
- return req;
- }
- static void audio_request_free(struct usb_request *req, struct usb_ep *ep)
- {
- if (req) {
- kfree(req->buf);
- usb_ep_free_request(ep, req);
- }
- }
- static void audio_req_put(struct audio_dev *audio, struct usb_request *req)
- {
- unsigned long flags;
- spin_lock_irqsave(&audio->lock, flags);
- list_add_tail(&req->list, &audio->idle_reqs);
- spin_unlock_irqrestore(&audio->lock, flags);
- }
- static struct usb_request *audio_req_get(struct audio_dev *audio)
- {
- unsigned long flags;
- struct usb_request *req;
- spin_lock_irqsave(&audio->lock, flags);
- if (list_empty(&audio->idle_reqs)) {
- req = 0;
- } else {
- req = list_first_entry(&audio->idle_reqs, struct usb_request,
- list);
- list_del(&req->list);
- }
- spin_unlock_irqrestore(&audio->lock, flags);
- return req;
- }
- /* send the appropriate number of packets to match our bitrate */
- static void audio_send(struct audio_dev *audio)
- {
- struct snd_pcm_runtime *runtime;
- struct usb_request *req;
- int length, length1, length2, ret;
- s64 msecs;
- s64 frames;
- ktime_t now;
- unsigned long flags;
- spin_lock_irqsave(&audio->lock, flags);
- /* audio->substream will be null if we have been closed */
- if (!audio->substream) {
- spin_unlock_irqrestore(&audio->lock, flags);
- return;
- }
- /* audio->buffer_pos will be null if we have been stopped */
- if (!audio->buffer_pos) {
- spin_unlock_irqrestore(&audio->lock, flags);
- return;
- }
- runtime = audio->substream->runtime;
- spin_unlock_irqrestore(&audio->lock, flags);
- /* compute number of frames to send */
- now = ktime_get();
- msecs = ktime_to_ns(now) - ktime_to_ns(audio->start_time);
- do_div(msecs, 1000000);
- frames = msecs * SAMPLE_RATE;
- do_div(frames, 1000);
- /* Readjust our frames_sent if we fall too far behind.
- * If we get too far behind it is better to drop some frames than
- * to keep sending data too fast in an attempt to catch up.
- */
- if (frames - audio->frames_sent > 10 * FRAMES_PER_MSEC)
- audio->frames_sent = frames - FRAMES_PER_MSEC;
- frames -= audio->frames_sent;
- /* We need to send something to keep the pipeline going */
- if (frames <= 0)
- frames = FRAMES_PER_MSEC;
- while (frames > 0) {
- req = audio_req_get(audio);
- spin_lock_irqsave(&audio->lock, flags);
- /* audio->substream will be null if we have been closed */
- if (!audio->substream) {
- spin_unlock_irqrestore(&audio->lock, flags);
- return;
- }
- /* audio->buffer_pos will be null if we have been stopped */
- if (!audio->buffer_pos) {
- spin_unlock_irqrestore(&audio->lock, flags);
- return;
- }
- if (!req) {
- spin_unlock_irqrestore(&audio->lock, flags);
- break;
- }
- length = frames_to_bytes(runtime, frames);
- if (length > IN_EP_MAX_PACKET_SIZE)
- length = IN_EP_MAX_PACKET_SIZE;
- if (audio->buffer_pos + length > audio->buffer_end)
- length1 = audio->buffer_end - audio->buffer_pos;
- else
- length1 = length;
- memcpy(req->buf, audio->buffer_pos, length1);
- if (length1 < length) {
- /* Wrap around and copy remaining length
- * at beginning of buffer.
- */
- length2 = length - length1;
- memcpy(req->buf + length1, audio->buffer_start,
- length2);
- audio->buffer_pos = audio->buffer_start + length2;
- } else {
- audio->buffer_pos += length1;
- if (audio->buffer_pos >= audio->buffer_end)
- audio->buffer_pos = audio->buffer_start;
- }
- req->length = length;
- spin_unlock_irqrestore(&audio->lock, flags);
- ret = usb_ep_queue(audio->in_ep, req, GFP_ATOMIC);
- if (ret < 0) {
- pr_err("usb_ep_queue failed ret: %d\n", ret);
- audio_req_put(audio, req);
- break;
- }
- frames -= bytes_to_frames(runtime, length);
- audio->frames_sent += bytes_to_frames(runtime, length);
- }
- }
- static void audio_control_complete(struct usb_ep *ep, struct usb_request *req)
- {
- /* nothing to do here */
- }
- static void audio_data_complete(struct usb_ep *ep, struct usb_request *req)
- {
- struct audio_dev *audio = req->context;
- pr_debug("audio_data_complete req->status %d req->actual %d\n",
- req->status, req->actual);
- audio_req_put(audio, req);
- if (!audio->buffer_start || req->status)
- return;
- audio->period_offset += req->actual;
- if (audio->period_offset >= audio->period) {
- snd_pcm_period_elapsed(audio->substream);
- audio->period_offset = 0;
- }
- audio_send(audio);
- }
- static int audio_source_set_endpoint_req(struct usb_function *f,
- const struct usb_ctrlrequest *ctrl)
- {
- int value = -EOPNOTSUPP;
- u16 ep = le16_to_cpu(ctrl->wIndex);
- u16 len = le16_to_cpu(ctrl->wLength);
- u16 w_value = le16_to_cpu(ctrl->wValue);
- pr_debug("bRequest 0x%x, w_value 0x%04x, len %d, endpoint %d\n",
- ctrl->bRequest, w_value, len, ep);
- switch (ctrl->bRequest) {
- case UAC_SET_CUR:
- case UAC_SET_MIN:
- case UAC_SET_MAX:
- case UAC_SET_RES:
- value = len;
- break;
- default:
- break;
- }
- return value;
- }
- static int audio_source_get_endpoint_req(struct usb_function *f,
- const struct usb_ctrlrequest *ctrl)
- {
- struct usb_composite_dev *cdev = f->config->cdev;
- int value = -EOPNOTSUPP;
- u8 ep = ((le16_to_cpu(ctrl->wIndex) >> 8) & 0xFF);
- u16 len = le16_to_cpu(ctrl->wLength);
- u16 w_value = le16_to_cpu(ctrl->wValue);
- u8 *buf = cdev->req->buf;
- pr_debug("bRequest 0x%x, w_value 0x%04x, len %d, endpoint %d\n",
- ctrl->bRequest, w_value, len, ep);
- if (w_value == UAC_EP_CS_ATTR_SAMPLE_RATE << 8) {
- switch (ctrl->bRequest) {
- case UAC_GET_CUR:
- case UAC_GET_MIN:
- case UAC_GET_MAX:
- case UAC_GET_RES:
- /* return our sample rate */
- buf[0] = (u8)SAMPLE_RATE;
- buf[1] = (u8)(SAMPLE_RATE >> 8);
- buf[2] = (u8)(SAMPLE_RATE >> 16);
- value = 3;
- break;
- default:
- break;
- }
- }
- return value;
- }
- static int
- audio_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
- {
- struct usb_composite_dev *cdev = f->config->cdev;
- struct usb_request *req = cdev->req;
- int value = -EOPNOTSUPP;
- u16 w_index = le16_to_cpu(ctrl->wIndex);
- u16 w_value = le16_to_cpu(ctrl->wValue);
- u16 w_length = le16_to_cpu(ctrl->wLength);
- /* composite driver infrastructure handles everything; interface
- * activation uses set_alt().
- */
- switch (ctrl->bRequestType) {
- case USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_ENDPOINT:
- value = audio_source_set_endpoint_req(f, ctrl);
- break;
- case USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_ENDPOINT:
- value = audio_source_get_endpoint_req(f, ctrl);
- break;
- }
- /* respond with data transfer or status phase? */
- if (value >= 0) {
- pr_debug("audio req%02x.%02x v%04x i%04x l%d\n",
- ctrl->bRequestType, ctrl->bRequest,
- w_value, w_index, w_length);
- req->zero = 0;
- req->length = value;
- req->complete = audio_control_complete;
- value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
- if (value < 0)
- pr_err("audio response on err %d\n", value);
- }
- /* device either stalls (value < 0) or reports success */
- return value;
- }
- static int audio_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
- {
- struct audio_dev *audio = func_to_audio_source(f);
- struct usb_composite_dev *cdev = f->config->cdev;
- int ret;
- pr_debug("audio_set_alt intf %d, alt %d\n", intf, alt);
- if (intf == as_interface_alt_1_desc.bInterfaceNumber) {
- if (alt && !audio->audio_ep_enabled) {
- ret = config_ep_by_speed(cdev->gadget, f, audio->in_ep);
- if (ret) {
- audio->in_ep->desc = NULL;
- ERROR(cdev, "config_ep fail ep %s, result %d\n",
- audio->in_ep->name, ret);
- return ret;
- }
- ret = usb_ep_enable(audio->in_ep);
- if (ret) {
- ERROR(cdev, "failedto enable ep%s, result %d\n",
- audio->in_ep->name, ret);
- return ret;
- }
- audio->audio_ep_enabled = true;
- } else if (!alt && audio->audio_ep_enabled) {
- usb_ep_disable(audio->in_ep);
- audio->audio_ep_enabled = false;
- }
- }
- return 0;
- }
- static void audio_disable(struct usb_function *f)
- {
- struct audio_dev *audio = func_to_audio_source(f);
- pr_debug("audio_disable\n");
- if (audio->audio_ep_enabled) {
- usb_ep_disable(audio->in_ep);
- audio->audio_ep_enabled = false;
- }
- }
- /*-------------------------------------------------------------------------*/
- static void audio_build_desc(struct audio_dev *audio)
- {
- u8 *sam_freq;
- int rate;
- /* Set channel numbers */
- input_terminal_desc.bNrChannels = 2;
- as_type_i_desc.bNrChannels = 2;
- /* Set sample rates */
- rate = SAMPLE_RATE;
- sam_freq = as_type_i_desc.tSamFreq[0];
- memcpy(sam_freq, &rate, 3);
- }
- /* audio function driver setup/binding */
- static int
- audio_bind(struct usb_configuration *c, struct usb_function *f)
- {
- struct usb_composite_dev *cdev = c->cdev;
- struct audio_dev *audio = func_to_audio_source(f);
- int status;
- struct usb_ep *ep;
- struct usb_request *req;
- int i;
- audio_build_desc(audio);
- /* allocate instance-specific interface IDs, and patch descriptors */
- status = usb_interface_id(c, f);
- if (status < 0)
- goto fail;
- audio_source_ac_interface_desc.bInterfaceNumber = status;
- status = usb_interface_id(c, f);
- if (status < 0)
- goto fail;
- as_interface_alt_0_desc.bInterfaceNumber = status;
- as_interface_alt_1_desc.bInterfaceNumber = status;
- status = -ENODEV;
- /* allocate our endpoint */
- ep = usb_ep_autoconfig(cdev->gadget, &fs_as_in_ep_desc);
- if (!ep)
- goto fail;
- audio->in_ep = ep;
- ep->driver_data = audio; /* claim */
- if (gadget_is_dualspeed(c->cdev->gadget))
- hs_as_in_ep_desc.bEndpointAddress =
- fs_as_in_ep_desc.bEndpointAddress;
- for (i = 0, status = 0; i < IN_EP_REQ_COUNT && status == 0; i++) {
- req = audio_request_new(ep, IN_EP_MAX_PACKET_SIZE);
- if (req) {
- req->context = audio;
- req->complete = audio_data_complete;
- audio_req_put(audio, req);
- } else
- status = -ENOMEM;
- }
- fail:
- return status;
- }
- static void
- audio_unbind(struct usb_configuration *c, struct usb_function *f)
- {
- struct audio_dev *audio = func_to_audio_source(f);
- struct usb_request *req;
- while ((req = audio_req_get(audio)))
- audio_request_free(req, audio->in_ep);
- snd_card_free_when_closed(audio->card);
- audio->card = NULL;
- audio->pcm = NULL;
- audio->substream = NULL;
- audio->in_ep = NULL;
- }
- static void audio_pcm_playback_start(struct audio_dev *audio)
- {
- audio->start_time = ktime_get();
- audio->frames_sent = 0;
- audio_send(audio);
- }
- static void audio_pcm_playback_stop(struct audio_dev *audio)
- {
- unsigned long flags;
- spin_lock_irqsave(&audio->lock, flags);
- audio->buffer_start = 0;
- audio->buffer_end = 0;
- audio->buffer_pos = 0;
- spin_unlock_irqrestore(&audio->lock, flags);
- }
- static int audio_pcm_open(struct snd_pcm_substream *substream)
- {
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct audio_dev *audio = substream->private_data;
- runtime->private_data = audio;
- runtime->hw = audio_hw_info;
- snd_pcm_limit_hw_rates(runtime);
- runtime->hw.channels_max = 2;
- audio->substream = substream;
- return 0;
- }
- static int audio_pcm_close(struct snd_pcm_substream *substream)
- {
- struct audio_dev *audio = substream->private_data;
- unsigned long flags;
- spin_lock_irqsave(&audio->lock, flags);
- audio->substream = NULL;
- spin_unlock_irqrestore(&audio->lock, flags);
- return 0;
- }
- static int audio_pcm_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
- {
- struct snd_dma_buffer *buf = &substream->dma_buffer;
- unsigned int channels = params_channels(params);
- unsigned int rate = params_rate(params);
- if (rate != SAMPLE_RATE)
- return -EINVAL;
- if (channels != 2)
- return -EINVAL;
- if (!substream->pcm->card->dev->coherent_dma_mask)
- substream->pcm->card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
- buf->dev.type = SNDRV_DMA_TYPE_DEV;
- buf->dev.dev = substream->pcm->card->dev;
- buf->private_data = NULL;
- buf->area = dma_alloc_coherent(substream->pcm->card->dev,
- params_buffer_bytes(params),
- &buf->addr, GFP_KERNEL);
- if (!buf->area)
- return -ENOMEM;
- buf->bytes = params_buffer_bytes(params);
- snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
- return 0;
- }
- static int audio_pcm_hw_free(struct snd_pcm_substream *substream)
- {
- struct snd_dma_buffer *buf = &substream->dma_buffer;
- if (buf->area != NULL)
- dma_free_coherent(substream->pcm->card->dev, buf->bytes,
- buf->area, buf->addr);
- buf->area = NULL;
- return 0;
- }
- static int audio_pcm_prepare(struct snd_pcm_substream *substream)
- {
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct audio_dev *audio = runtime->private_data;
- audio->period = snd_pcm_lib_period_bytes(substream);
- audio->period_offset = 0;
- audio->buffer_start = runtime->dma_area;
- audio->buffer_end = audio->buffer_start
- + snd_pcm_lib_buffer_bytes(substream);
- audio->buffer_pos = audio->buffer_start;
- return 0;
- }
- static snd_pcm_uframes_t audio_pcm_pointer(struct snd_pcm_substream *substream)
- {
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct audio_dev *audio = runtime->private_data;
- ssize_t bytes = audio->buffer_pos - audio->buffer_start;
- /* return offset of next frame to fill in our buffer */
- return bytes_to_frames(runtime, bytes);
- }
- static int audio_pcm_playback_trigger(struct snd_pcm_substream *substream,
- int cmd)
- {
- struct audio_dev *audio = substream->runtime->private_data;
- int ret = 0;
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
- case SNDRV_PCM_TRIGGER_RESUME:
- audio_pcm_playback_start(audio);
- break;
- case SNDRV_PCM_TRIGGER_STOP:
- case SNDRV_PCM_TRIGGER_SUSPEND:
- audio_pcm_playback_stop(audio);
- break;
- default:
- ret = -EINVAL;
- }
- return ret;
- }
- static int audio_pcm_mmap(struct snd_pcm_substream *substream,
- struct vm_area_struct *vma)
- {
- struct snd_pcm_runtime *runtime = substream->runtime;
- if (runtime->dma_addr && runtime->dma_bytes) {
- return dma_mmap_coherent(substream->pcm->card->dev, vma,
- runtime->dma_area,
- runtime->dma_addr,
- runtime->dma_bytes);
- } else {
- pr_err("Physical address or size of buf is NULL");
- return -EINVAL;
- }
- }
- static struct audio_dev _audio_dev = {
- .func = {
- .name = "audio_source",
- .bind = audio_bind,
- .unbind = audio_unbind,
- .set_alt = audio_set_alt,
- .setup = audio_setup,
- .disable = audio_disable,
- .fs_descriptors = fs_audio_desc,
- .hs_descriptors = hs_audio_desc,
- },
- .lock = __SPIN_LOCK_UNLOCKED(_audio_dev.lock),
- .idle_reqs = LIST_HEAD_INIT(_audio_dev.idle_reqs),
- };
- static struct snd_pcm_ops audio_playback_ops = {
- .open = audio_pcm_open,
- .close = audio_pcm_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = audio_pcm_hw_params,
- .hw_free = audio_pcm_hw_free,
- .prepare = audio_pcm_prepare,
- .trigger = audio_pcm_playback_trigger,
- .pointer = audio_pcm_pointer,
- .mmap = audio_pcm_mmap,
- };
- int audio_source_bind_config(struct usb_configuration *c,
- struct audio_source_config *config)
- {
- struct audio_dev *audio;
- struct snd_card *card;
- struct snd_pcm *pcm;
- int err;
- config->card = -1;
- config->device = -1;
- audio = &_audio_dev;
- err = snd_card_create(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
- THIS_MODULE, 0, &card);
- if (err)
- return err;
- snd_card_set_dev(card, &c->cdev->gadget->dev);
- err = snd_pcm_new(card, "USB audio source", 0, 1, 0, &pcm);
- if (err)
- goto pcm_fail;
- pcm->private_data = audio;
- pcm->info_flags = 0;
- audio->pcm = pcm;
- strlcpy(pcm->name, "USB gadget audio", sizeof(pcm->name));
- snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &audio_playback_ops);
- snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
- NULL, 0, 64 * 1024);
- strlcpy(card->driver, "audio_source", sizeof(card->driver));
- strlcpy(card->shortname, card->driver, sizeof(card->shortname));
- strlcpy(card->longname, "USB accessory audio source",
- sizeof(card->longname));
- err = snd_card_register(card);
- if (err)
- goto register_fail;
- err = usb_add_function(c, &audio->func);
- if (err)
- goto add_fail;
- config->card = pcm->card->number;
- config->device = pcm->device;
- audio->card = card;
- return 0;
- add_fail:
- register_fail:
- pcm_fail:
- snd_card_free(audio->card);
- return err;
- }
|