Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/torvalds/linux.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb')
-rw-r--r--drivers/usb/dwc2/core.h2
-rw-r--r--drivers/usb/dwc2/gadget.c28
-rw-r--r--drivers/usb/dwc3/core.c15
-rw-r--r--drivers/usb/dwc3/core.h16
-rw-r--r--drivers/usb/dwc3/dwc3-qcom.c23
-rw-r--r--drivers/usb/dwc3/ep0.c2
-rw-r--r--drivers/usb/dwc3/gadget.c232
-rw-r--r--drivers/usb/gadget/composite.c8
-rw-r--r--drivers/usb/gadget/configfs.c4
-rw-r--r--drivers/usb/gadget/function/f_ncm.c50
-rw-r--r--drivers/usb/gadget/function/f_uac1.c674
-rw-r--r--drivers/usb/gadget/function/f_uac2.c656
-rw-r--r--drivers/usb/gadget/function/u_audio.c369
-rw-r--r--drivers/usb/gadget/function/u_audio.h22
-rw-r--r--drivers/usb/gadget/function/u_ether.c5
-rw-r--r--drivers/usb/gadget/function/u_uac1.h20
-rw-r--r--drivers/usb/gadget/function/u_uac2.h23
-rw-r--r--drivers/usb/gadget/udc/core.c19
-rw-r--r--drivers/usb/host/ehci-mv.c23
-rw-r--r--drivers/usb/host/fotg210-hcd.c48
-rw-r--r--drivers/usb/host/fotg210.h5
-rw-r--r--drivers/usb/host/ohci-spear.c2
-rw-r--r--drivers/usb/host/xhci-pci-renesas.c7
-rw-r--r--drivers/usb/host/xhci-pci.c2
-rw-r--r--drivers/usb/host/xhci-pci.h3
-rw-r--r--drivers/usb/phy/phy-isp1301-omap.c2
26 files changed, 2074 insertions, 186 deletions
diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h
index 483de2bbfaab..cb9059a8444b 100644
--- a/drivers/usb/dwc2/core.h
+++ b/drivers/usb/dwc2/core.h
@@ -122,6 +122,7 @@ struct dwc2_hsotg_req;
* @periodic: Set if this is a periodic ep, such as Interrupt
* @isochronous: Set if this is a isochronous ep
* @send_zlp: Set if we need to send a zero-length packet.
+ * @wedged: Set if ep is wedged.
* @desc_list_dma: The DMA address of descriptor chain currently in use.
* @desc_list: Pointer to descriptor DMA chain head currently in use.
* @desc_count: Count of entries within the DMA descriptor chain of EP.
@@ -172,6 +173,7 @@ struct dwc2_hsotg_ep {
unsigned int periodic:1;
unsigned int isochronous:1;
unsigned int send_zlp:1;
+ unsigned int wedged:1;
unsigned int target_frame;
#define TARGET_FRAME_INITIAL 0xFFFFFFFF
bool frame_overrun;
diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c
index 3146df6e6510..985b272f53d5 100644
--- a/drivers/usb/dwc2/gadget.c
+++ b/drivers/usb/dwc2/gadget.c
@@ -1806,7 +1806,8 @@ static int dwc2_hsotg_process_req_feature(struct dwc2_hsotg *hsotg,
case USB_ENDPOINT_HALT:
halted = ep->halted;
- dwc2_hsotg_ep_sethalt(&ep->ep, set, true);
+ if (!ep->wedged)
+ dwc2_hsotg_ep_sethalt(&ep->ep, set, true);
ret = dwc2_hsotg_send_reply(hsotg, ep0, NULL, 0);
if (ret) {
@@ -4066,6 +4067,7 @@ static int dwc2_hsotg_ep_enable(struct usb_ep *ep,
hs_ep->isochronous = 0;
hs_ep->periodic = 0;
hs_ep->halted = 0;
+ hs_ep->wedged = 0;
hs_ep->interval = desc->bInterval;
switch (ep_type) {
@@ -4307,6 +4309,27 @@ static int dwc2_hsotg_ep_dequeue(struct usb_ep *ep, struct usb_request *req)
}
/**
+ * dwc2_gadget_ep_set_wedge - set wedge on a given endpoint
+ * @ep: The endpoint to be wedged.
+ *
+ */
+static int dwc2_gadget_ep_set_wedge(struct usb_ep *ep)
+{
+ struct dwc2_hsotg_ep *hs_ep = our_ep(ep);
+ struct dwc2_hsotg *hs = hs_ep->parent;
+
+ unsigned long flags;
+ int ret;
+
+ spin_lock_irqsave(&hs->lock, flags);
+ hs_ep->wedged = 1;
+ ret = dwc2_hsotg_ep_sethalt(ep, 1, false);
+ spin_unlock_irqrestore(&hs->lock, flags);
+
+ return ret;
+}
+
+/**
* dwc2_hsotg_ep_sethalt - set halt on a given endpoint
* @ep: The endpoint to set halt.
* @value: Set or unset the halt.
@@ -4357,6 +4380,7 @@ static int dwc2_hsotg_ep_sethalt(struct usb_ep *ep, int value, bool now)
epctl |= DXEPCTL_EPDIS;
} else {
epctl &= ~DXEPCTL_STALL;
+ hs_ep->wedged = 0;
xfertype = epctl & DXEPCTL_EPTYPE_MASK;
if (xfertype == DXEPCTL_EPTYPE_BULK ||
xfertype == DXEPCTL_EPTYPE_INTERRUPT)
@@ -4376,6 +4400,7 @@ static int dwc2_hsotg_ep_sethalt(struct usb_ep *ep, int value, bool now)
// STALL bit will be set in GOUTNAKEFF interrupt handler
} else {
epctl &= ~DXEPCTL_STALL;
+ hs_ep->wedged = 0;
xfertype = epctl & DXEPCTL_EPTYPE_MASK;
if (xfertype == DXEPCTL_EPTYPE_BULK ||
xfertype == DXEPCTL_EPTYPE_INTERRUPT)
@@ -4415,6 +4440,7 @@ static const struct usb_ep_ops dwc2_hsotg_ep_ops = {
.queue = dwc2_hsotg_ep_queue_lock,
.dequeue = dwc2_hsotg_ep_dequeue,
.set_halt = dwc2_hsotg_ep_sethalt_lock,
+ .set_wedge = dwc2_gadget_ep_set_wedge,
/* note, don't believe we have any call for the fifo routines */
};
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index ba74ad7f6995..b194aecd2202 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -1267,6 +1267,7 @@ static void dwc3_get_properties(struct dwc3 *dwc)
u8 rx_max_burst_prd;
u8 tx_thr_num_pkt_prd;
u8 tx_max_burst_prd;
+ u8 tx_fifo_resize_max_num;
const char *usb_psy_name;
int ret;
@@ -1282,6 +1283,13 @@ static void dwc3_get_properties(struct dwc3 *dwc)
*/
hird_threshold = 12;
+ /*
+ * default to a TXFIFO size large enough to fit 6 max packets. This
+ * allows for systems with larger bus latencies to have some headroom
+ * for endpoints that have a large bMaxBurst value.
+ */
+ tx_fifo_resize_max_num = 6;
+
dwc->maximum_speed = usb_get_maximum_speed(dev);
dwc->max_ssp_rate = usb_get_maximum_ssp_rate(dev);
dwc->dr_mode = usb_get_dr_mode(dev);
@@ -1325,6 +1333,11 @@ static void dwc3_get_properties(struct dwc3 *dwc)
&tx_thr_num_pkt_prd);
device_property_read_u8(dev, "snps,tx-max-burst-prd",
&tx_max_burst_prd);
+ dwc->do_fifo_resize = device_property_read_bool(dev,
+ "tx-fifo-resize");
+ if (dwc->do_fifo_resize)
+ device_property_read_u8(dev, "tx-fifo-max-num",
+ &tx_fifo_resize_max_num);
dwc->disable_scramble_quirk = device_property_read_bool(dev,
"snps,disable_scramble_quirk");
@@ -1390,6 +1403,8 @@ static void dwc3_get_properties(struct dwc3 *dwc)
dwc->tx_max_burst_prd = tx_max_burst_prd;
dwc->imod_interval = 0;
+
+ dwc->tx_fifo_resize_max_num = tx_fifo_resize_max_num;
}
/* check whether the core supports IMOD */
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 5991766239ba..bcfeadc862b6 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -1023,6 +1023,7 @@ struct dwc3_scratchpad_array {
* @rx_max_burst_prd: max periodic ESS receive burst size
* @tx_thr_num_pkt_prd: periodic ESS transmit packet count
* @tx_max_burst_prd: max periodic ESS transmit burst size
+ * @tx_fifo_resize_max_num: max number of fifos allocated during txfifo resize
* @hsphy_interface: "utmi" or "ulpi"
* @connected: true when we're connected to a host, false otherwise
* @delayed_status: true when gadget driver asks for delayed status
@@ -1037,6 +1038,7 @@ struct dwc3_scratchpad_array {
* 1 - utmi_l1_suspend_n
* @is_fpga: true when we are using the FPGA board
* @pending_events: true when we have pending IRQs to be handled
+ * @do_fifo_resize: true when txfifo resizing is enabled for dwc3 endpoints
* @pullups_connected: true when Run/Stop bit is set
* @setup_packet_pending: true when there's a Setup Packet in FIFO. Workaround
* @three_stage_setup: set if we perform a three phase setup
@@ -1079,6 +1081,11 @@ struct dwc3_scratchpad_array {
* @dis_split_quirk: set to disable split boundary.
* @imod_interval: set the interrupt moderation interval in 250ns
* increments or 0 to disable.
+ * @max_cfg_eps: current max number of IN eps used across all USB configs.
+ * @last_fifo_depth: last fifo depth used to determine next fifo ram start
+ * address.
+ * @num_ep_resized: carries the current number endpoints which have had its tx
+ * fifo resized.
*/
struct dwc3 {
struct work_struct drd_work;
@@ -1233,6 +1240,7 @@ struct dwc3 {
u8 rx_max_burst_prd;
u8 tx_thr_num_pkt_prd;
u8 tx_max_burst_prd;
+ u8 tx_fifo_resize_max_num;
const char *hsphy_interface;
@@ -1246,6 +1254,7 @@ struct dwc3 {
unsigned is_utmi_l1_suspend:1;
unsigned is_fpga:1;
unsigned pending_events:1;
+ unsigned do_fifo_resize:1;
unsigned pullups_connected:1;
unsigned setup_packet_pending:1;
unsigned three_stage_setup:1;
@@ -1282,6 +1291,10 @@ struct dwc3 {
unsigned async_callbacks:1;
u16 imod_interval;
+
+ int max_cfg_eps;
+ int last_fifo_depth;
+ int num_ep_resized;
};
#define INCRX_BURST_MODE 0
@@ -1513,6 +1526,7 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned int cmd,
struct dwc3_gadget_ep_cmd_params *params);
int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned int cmd,
u32 param);
+void dwc3_gadget_clear_tx_fifos(struct dwc3 *dwc);
#else
static inline int dwc3_gadget_init(struct dwc3 *dwc)
{ return 0; }
@@ -1532,6 +1546,8 @@ static inline int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned int cmd,
static inline int dwc3_send_gadget_generic_command(struct dwc3 *dwc,
int cmd, u32 param)
{ return 0; }
+static inline void dwc3_gadget_clear_tx_fifos(struct dwc3 *dwc)
+{ }
#endif
#if IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE)
diff --git a/drivers/usb/dwc3/dwc3-qcom.c b/drivers/usb/dwc3/dwc3-qcom.c
index 49e6ca94486d..25cd123e441a 100644
--- a/drivers/usb/dwc3/dwc3-qcom.c
+++ b/drivers/usb/dwc3/dwc3-qcom.c
@@ -115,7 +115,7 @@ static inline void dwc3_qcom_clrbits(void __iomem *base, u32 offset, u32 val)
readl(base + offset);
}
-static void dwc3_qcom_vbus_overrride_enable(struct dwc3_qcom *qcom, bool enable)
+static void dwc3_qcom_vbus_override_enable(struct dwc3_qcom *qcom, bool enable)
{
if (enable) {
dwc3_qcom_setbits(qcom->qscratch_base, QSCRATCH_SS_PHY_CTRL,
@@ -136,7 +136,7 @@ static int dwc3_qcom_vbus_notifier(struct notifier_block *nb,
struct dwc3_qcom *qcom = container_of(nb, struct dwc3_qcom, vbus_nb);
/* enable vbus override for device mode */
- dwc3_qcom_vbus_overrride_enable(qcom, event);
+ dwc3_qcom_vbus_override_enable(qcom, event);
qcom->mode = event ? USB_DR_MODE_PERIPHERAL : USB_DR_MODE_HOST;
return NOTIFY_DONE;
@@ -148,7 +148,7 @@ static int dwc3_qcom_host_notifier(struct notifier_block *nb,
struct dwc3_qcom *qcom = container_of(nb, struct dwc3_qcom, host_nb);
/* disable vbus override in host mode */
- dwc3_qcom_vbus_overrride_enable(qcom, !event);
+ dwc3_qcom_vbus_override_enable(qcom, !event);
qcom->mode = event ? USB_DR_MODE_HOST : USB_DR_MODE_PERIPHERAL;
return NOTIFY_DONE;
@@ -645,6 +645,7 @@ static int dwc3_qcom_of_register_core(struct platform_device *pdev)
struct dwc3_qcom *qcom = platform_get_drvdata(pdev);
struct device_node *np = pdev->dev.of_node, *dwc3_np;
struct device *dev = &pdev->dev;
+ struct property *prop;
int ret;
dwc3_np = of_get_compatible_child(np, "snps,dwc3");
@@ -653,6 +654,20 @@ static int dwc3_qcom_of_register_core(struct platform_device *pdev)
return -ENODEV;
}
+ prop = devm_kzalloc(dev, sizeof(*prop), GFP_KERNEL);
+ if (!prop) {
+ ret = -ENOMEM;
+ dev_err(dev, "unable to allocate memory for property\n");
+ goto node_put;
+ }
+
+ prop->name = "tx-fifo-resize";
+ ret = of_add_property(dwc3_np, prop);
+ if (ret) {
+ dev_err(dev, "unable to add property\n");
+ goto node_put;
+ }
+
ret = of_platform_populate(np, NULL, NULL, dev);
if (ret) {
dev_err(dev, "failed to register dwc3 core - %d\n", ret);
@@ -811,7 +826,7 @@ static int dwc3_qcom_probe(struct platform_device *pdev)
/* enable vbus override for device mode */
if (qcom->mode == USB_DR_MODE_PERIPHERAL)
- dwc3_qcom_vbus_overrride_enable(qcom, true);
+ dwc3_qcom_vbus_override_enable(qcom, true);
/* register extcon to override sw_vbus on Vbus change later */
ret = dwc3_qcom_register_extcon(qcom);
diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
index 2f9e45eed228..658739410992 100644
--- a/drivers/usb/dwc3/ep0.c
+++ b/drivers/usb/dwc3/ep0.c
@@ -621,6 +621,8 @@ static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
return -EINVAL;
case USB_STATE_ADDRESS:
+ dwc3_gadget_clear_tx_fifos(dwc);
+
ret = dwc3_ep0_delegate_req(dwc, ctrl);
/* if the cfg matches and the cfg is non zero */
if (cfg && (!ret || (ret == USB_GADGET_DELAYED_STATUS))) {
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 45f2bc0807e8..fb5a09ffebb0 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -632,6 +632,187 @@ static void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force,
bool interrupt);
/**
+ * dwc3_gadget_calc_tx_fifo_size - calculates the txfifo size value
+ * @dwc: pointer to the DWC3 context
+ * @nfifos: number of fifos to calculate for
+ *
+ * Calculates the size value based on the equation below:
+ *
+ * DWC3 revision 280A and prior:
+ * fifo_size = mult * (max_packet / mdwidth) + 1;
+ *
+ * DWC3 revision 290A and onwards:
+ * fifo_size = mult * ((max_packet + mdwidth)/mdwidth + 1) + 1
+ *
+ * The max packet size is set to 1024, as the txfifo requirements mainly apply
+ * to super speed USB use cases. However, it is safe to overestimate the fifo
+ * allocations for other scenarios, i.e. high speed USB.
+ */
+static int dwc3_gadget_calc_tx_fifo_size(struct dwc3 *dwc, int mult)
+{
+ int max_packet = 1024;
+ int fifo_size;
+ int mdwidth;
+
+ mdwidth = dwc3_mdwidth(dwc);
+
+ /* MDWIDTH is represented in bits, we need it in bytes */
+ mdwidth >>= 3;
+
+ if (DWC3_VER_IS_PRIOR(DWC3, 290A))
+ fifo_size = mult * (max_packet / mdwidth) + 1;
+ else
+ fifo_size = mult * ((max_packet + mdwidth) / mdwidth) + 1;
+ return fifo_size;
+}
+
+/**
+ * dwc3_gadget_clear_tx_fifo_size - Clears txfifo allocation
+ * @dwc: pointer to the DWC3 context
+ *
+ * Iterates through all the endpoint registers and clears the previous txfifo
+ * allocations.
+ */
+void dwc3_gadget_clear_tx_fifos(struct dwc3 *dwc)
+{
+ struct dwc3_ep *dep;
+ int fifo_depth;
+ int size;
+ int num;
+
+ if (!dwc->do_fifo_resize)
+ return;
+
+ /* Read ep0IN related TXFIFO size */
+ dep = dwc->eps[1];
+ size = dwc3_readl(dwc->regs, DWC3_GTXFIFOSIZ(0));
+ if (DWC3_IP_IS(DWC3))
+ fifo_depth = DWC3_GTXFIFOSIZ_TXFDEP(size);
+ else
+ fifo_depth = DWC31_GTXFIFOSIZ_TXFDEP(size);
+
+ dwc->last_fifo_depth = fifo_depth;
+ /* Clear existing TXFIFO for all IN eps except ep0 */
+ for (num = 3; num < min_t(int, dwc->num_eps, DWC3_ENDPOINTS_NUM);
+ num += 2) {
+ dep = dwc->eps[num];
+ /* Don't change TXFRAMNUM on usb31 version */
+ size = DWC3_IP_IS(DWC3) ? 0 :
+ dwc3_readl(dwc->regs, DWC3_GTXFIFOSIZ(num >> 1)) &
+ DWC31_GTXFIFOSIZ_TXFRAMNUM;
+
+ dwc3_writel(dwc->regs, DWC3_GTXFIFOSIZ(num >> 1), size);
+ }
+ dwc->num_ep_resized = 0;
+}
+
+/*
+ * dwc3_gadget_resize_tx_fifos - reallocate fifo spaces for current use-case
+ * @dwc: pointer to our context structure
+ *
+ * This function will a best effort FIFO allocation in order
+ * to improve FIFO usage and throughput, while still allowing
+ * us to enable as many endpoints as possible.
+ *
+ * Keep in mind that this operation will be highly dependent
+ * on the configured size for RAM1 - which contains TxFifo -,
+ * the amount of endpoints enabled on coreConsultant tool, and
+ * the width of the Master Bus.
+ *
+ * In general, FIFO depths are represented with the following equation:
+ *
+ * fifo_size = mult * ((max_packet + mdwidth)/mdwidth + 1) + 1
+ *
+ * In conjunction with dwc3_gadget_check_config(), this resizing logic will
+ * ensure that all endpoints will have enough internal memory for one max
+ * packet per endpoint.
+ */
+static int dwc3_gadget_resize_tx_fifos(struct dwc3_ep *dep)
+{
+ struct dwc3 *dwc = dep->dwc;
+ int fifo_0_start;
+ int ram1_depth;
+ int fifo_size;
+ int min_depth;
+ int num_in_ep;
+ int remaining;
+ int num_fifos = 1;
+ int fifo;
+ int tmp;
+
+ if (!dwc->do_fifo_resize)
+ return 0;
+
+ /* resize IN endpoints except ep0 */
+ if (!usb_endpoint_dir_in(dep->endpoint.desc) || dep->number <= 1)
+ return 0;
+
+ ram1_depth = DWC3_RAM1_DEPTH(dwc->hwparams.hwparams7);
+
+ if ((dep->endpoint.maxburst > 1 &&
+ usb_endpoint_xfer_bulk(dep->endpoint.desc)) ||
+ usb_endpoint_xfer_isoc(dep->endpoint.desc))
+ num_fifos = 3;
+
+ if (dep->endpoint.maxburst > 6 &&
+ usb_endpoint_xfer_bulk(dep->endpoint.desc) && DWC3_IP_IS(DWC31))
+ num_fifos = dwc->tx_fifo_resize_max_num;
+
+ /* FIFO size for a single buffer */
+ fifo = dwc3_gadget_calc_tx_fifo_size(dwc, 1);
+
+ /* Calculate the number of remaining EPs w/o any FIFO */
+ num_in_ep = dwc->max_cfg_eps;
+ num_in_ep -= dwc->num_ep_resized;
+
+ /* Reserve at least one FIFO for the number of IN EPs */
+ min_depth = num_in_ep * (fifo + 1);
+ remaining = ram1_depth - min_depth - dwc->last_fifo_depth;
+ remaining = max_t(int, 0, remaining);
+ /*
+ * We've already reserved 1 FIFO per EP, so check what we can fit in
+ * addition to it. If there is not enough remaining space, allocate
+ * all the remaining space to the EP.
+ */
+ fifo_size = (num_fifos - 1) * fifo;
+ if (remaining < fifo_size)
+ fifo_size = remaining;
+
+ fifo_size += fifo;
+ /* Last increment according to the TX FIFO size equation */
+ fifo_size++;
+
+ /* Check if TXFIFOs start at non-zero addr */
+ tmp = dwc3_readl(dwc->regs, DWC3_GTXFIFOSIZ(0));
+ fifo_0_start = DWC3_GTXFIFOSIZ_TXFSTADDR(tmp);
+
+ fifo_size |= (fifo_0_start + (dwc->last_fifo_depth << 16));
+ if (DWC3_IP_IS(DWC3))
+ dwc->last_fifo_depth += DWC3_GTXFIFOSIZ_TXFDEP(fifo_size);
+ else
+ dwc->last_fifo_depth += DWC31_GTXFIFOSIZ_TXFDEP(fifo_size);
+
+ /* Check fifo size allocation doesn't exceed available RAM size. */
+ if (dwc->last_fifo_depth >= ram1_depth) {
+ dev_err(dwc->dev, "Fifosize(%d) > RAM size(%d) %s depth:%d\n",
+ dwc->last_fifo_depth, ram1_depth,
+ dep->endpoint.name, fifo_size);
+ if (DWC3_IP_IS(DWC3))
+ fifo_size = DWC3_GTXFIFOSIZ_TXFDEP(fifo_size);
+ else
+ fifo_size = DWC31_GTXFIFOSIZ_TXFDEP(fifo_size);
+
+ dwc->last_fifo_depth -= fifo_size;
+ return -ENOMEM;
+ }
+
+ dwc3_writel(dwc->regs, DWC3_GTXFIFOSIZ(dep->number >> 1), fifo_size);
+ dwc->num_ep_resized++;
+
+ return 0;
+}
+
+/**
* __dwc3_gadget_ep_enable - initializes a hw endpoint
* @dep: endpoint to be initialized
* @action: one of INIT, MODIFY or RESTORE
@@ -648,6 +829,10 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, unsigned int action)
int ret;
if (!(dep->flags & DWC3_EP_ENABLED)) {
+ ret = dwc3_gadget_resize_tx_fifos(dep);
+ if (ret)
+ return ret;
+
ret = dwc3_gadget_start_config(dep);
if (ret)
return ret;
@@ -2498,6 +2683,7 @@ static int dwc3_gadget_stop(struct usb_gadget *g)
spin_lock_irqsave(&dwc->lock, flags);
dwc->gadget_driver = NULL;
+ dwc->max_cfg_eps = 0;
spin_unlock_irqrestore(&dwc->lock, flags);
free_irq(dwc->irq_gadget, dwc->ev_buf);
@@ -2585,6 +2771,51 @@ static int dwc3_gadget_vbus_draw(struct usb_gadget *g, unsigned int mA)
return ret;
}
+/**
+ * dwc3_gadget_check_config - ensure dwc3 can support the USB configuration
+ * @g: pointer to the USB gadget
+ *
+ * Used to record the maximum number of endpoints being used in a USB composite
+ * device. (across all configurations) This is to be used in the calculation
+ * of the TXFIFO sizes when resizing internal memory for individual endpoints.
+ * It will help ensured that the resizing logic reserves enough space for at
+ * least one max packet.
+ */
+static int dwc3_gadget_check_config(struct usb_gadget *g)
+{
+ struct dwc3 *dwc = gadget_to_dwc(g);
+ struct usb_ep *ep;
+ int fifo_size = 0;
+ int ram1_depth;
+ int ep_num = 0;
+
+ if (!dwc->do_fifo_resize)
+ return 0;
+
+ list_for_each_entry(ep, &g->ep_list, ep_list) {
+ /* Only interested in the IN endpoints */
+ if (ep->claimed && (ep->address & USB_DIR_IN))
+ ep_num++;
+ }
+
+ if (ep_num <= dwc->max_cfg_eps)
+ return 0;
+
+ /* Update the max number of eps in the composition */
+ dwc->max_cfg_eps = ep_num;
+
+ fifo_size = dwc3_gadget_calc_tx_fifo_size(dwc, dwc->max_cfg_eps);
+ /* Based on the equation, increment by one for every ep */
+ fifo_size += dwc->max_cfg_eps;
+
+ /* Check if we can fit a single fifo per endpoint */
+ ram1_depth = DWC3_RAM1_DEPTH(dwc->hwparams.hwparams7);
+ if (fifo_size > ram1_depth)
+ return -ENOMEM;
+
+ return 0;
+}
+
static void dwc3_gadget_async_callbacks(struct usb_gadget *g, bool enable)
{
struct dwc3 *dwc = gadget_to_dwc(g);
@@ -2606,6 +2837,7 @@ static const struct usb_gadget_ops dwc3_gadget_ops = {
.udc_set_ssp_rate = dwc3_gadget_set_ssp_rate,
.get_config_params = dwc3_gadget_config_params,
.vbus_draw = dwc3_gadget_vbus_draw,
+ .check_config = dwc3_gadget_check_config,
.udc_async_callbacks = dwc3_gadget_async_callbacks,
};
diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
index 72a9797dbbae..504c1cbc255d 100644
--- a/drivers/usb/gadget/composite.c
+++ b/drivers/usb/gadget/composite.c
@@ -482,7 +482,7 @@ static u8 encode_bMaxPower(enum usb_device_speed speed,
{
unsigned val;
- if (c->MaxPower)
+ if (c->MaxPower || (c->bmAttributes & USB_CONFIG_ATT_SELFPOWER))
val = c->MaxPower;
else
val = CONFIG_USB_GADGET_VBUS_DRAW;
@@ -936,7 +936,11 @@ static int set_config(struct usb_composite_dev *cdev,
}
/* when we return, be sure our power usage is valid */
- power = c->MaxPower ? c->MaxPower : CONFIG_USB_GADGET_VBUS_DRAW;
+ if (c->MaxPower || (c->bmAttributes & USB_CONFIG_ATT_SELFPOWER))
+ power = c->MaxPower;
+ else
+ power = CONFIG_USB_GADGET_VBUS_DRAW;
+
if (gadget->speed < USB_SPEED_SUPER)
power = min(power, 500U);
else
diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c
index 15a607ccef8a..f4c7c8241fe2 100644
--- a/drivers/usb/gadget/configfs.c
+++ b/drivers/usb/gadget/configfs.c
@@ -1404,6 +1404,10 @@ static int configfs_composite_bind(struct usb_gadget *gadget,
goto err_purge_funcs;
}
}
+ ret = usb_gadget_check_config(cdev->gadget);
+ if (ret)
+ goto err_purge_funcs;
+
usb_ep_autoconfig_reset(cdev->gadget);
}
if (cdev->use_os_string) {
diff --git a/drivers/usb/gadget/function/f_ncm.c b/drivers/usb/gadget/function/f_ncm.c
index 855127249f24..dc8f078f918c 100644
--- a/drivers/usb/gadget/function/f_ncm.c
+++ b/drivers/usb/gadget/function/f_ncm.c
@@ -72,9 +72,7 @@ struct f_ncm {
struct sk_buff *skb_tx_data;
struct sk_buff *skb_tx_ndp;
u16 ndp_dgram_count;
- bool timer_force_tx;
struct hrtimer task_timer;
- bool timer_stopping;
};
static inline struct f_ncm *func_to_ncm(struct usb_function *f)
@@ -890,7 +888,6 @@ static int ncm_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
if (ncm->port.in_ep->enabled) {
DBG(cdev, "reset ncm\n");
- ncm->timer_stopping = true;
ncm->netdev = NULL;
gether_disconnect(&ncm->port);
ncm_reset_values(ncm);
@@ -928,7 +925,6 @@ static int ncm_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
if (IS_ERR(net))
return PTR_ERR(net);
ncm->netdev = net;
- ncm->timer_stopping = false;
}
spin_lock(&ncm->lock);
@@ -1017,22 +1013,20 @@ static struct sk_buff *ncm_wrap_ntb(struct gether *port,
{
struct f_ncm *ncm = func_to_ncm(&port->func);
struct sk_buff *skb2 = NULL;
- int ncb_len = 0;
- __le16 *ntb_data;
- __le16 *ntb_ndp;
- int dgram_pad;
-
- unsigned max_size = ncm->port.fixed_in_len;
- const struct ndp_parser_opts *opts = ncm->parser_opts;
- const int ndp_align = le16_to_cpu(ntb_parameters.wNdpInAlignment);
- const int div = le16_to_cpu(ntb_parameters.wNdpInDivisor);
- const int rem = le16_to_cpu(ntb_parameters.wNdpInPayloadRemainder);
- const int dgram_idx_len = 2 * 2 * opts->dgram_item_len;
-
- if (!skb && !ncm->skb_tx_data)
- return NULL;
if (skb) {
+ int ncb_len = 0;
+ __le16 *ntb_data;
+ __le16 *ntb_ndp;
+ int dgram_pad;
+
+ unsigned max_size = ncm->port.fixed_in_len;
+ const struct ndp_parser_opts *opts = ncm->parser_opts;
+ const int ndp_align = le16_to_cpu(ntb_parameters.wNdpInAlignment);
+ const int div = le16_to_cpu(ntb_parameters.wNdpInDivisor);
+ const int rem = le16_to_cpu(ntb_parameters.wNdpInPayloadRemainder);
+ const int dgram_idx_len = 2 * 2 * opts->dgram_item_len;
+
/* Add the CRC if required up front */
if (ncm->is_crc) {
uint32_t crc;
@@ -1126,8 +1120,11 @@ static struct sk_buff *ncm_wrap_ntb(struct gether *port,
dev_consume_skb_any(skb);
skb = NULL;
- } else if (ncm->skb_tx_data && ncm->timer_force_tx) {
- /* If the tx was requested because of a timeout then send */
+ } else if (ncm->skb_tx_data) {
+ /* If we get here ncm_wrap_ntb() was called with NULL skb,
+ * because eth_start_xmit() was called with NULL skb by
+ * ncm_tx_timeout() - hence, this is our signal to flush/send.
+ */
skb2 = package_for_tx(ncm);
if (!skb2)
goto err;
@@ -1155,20 +1152,18 @@ err:
static enum hrtimer_restart ncm_tx_timeout(struct hrtimer *data)
{
struct f_ncm *ncm = container_of(data, struct f_ncm, task_timer);
+ struct net_device *netdev = READ_ONCE(ncm->netdev);
- /* Only send if data is available. */
- if (!ncm->timer_stopping && ncm->skb_tx_data) {
- ncm->timer_force_tx = true;
-
+ if (netdev) {
/* XXX This allowance of a NULL skb argument to ndo_start_xmit
* XXX is not sane. The gadget layer should be redesigned so
* XXX that the dev->wrap() invocations to build SKBs is transparent
* XXX and performed in some way outside of the ndo_start_xmit
* XXX interface.
+ *
+ * This will call directly into u_ether's eth_start_xmit()
*/
- ncm->netdev->netdev_ops->ndo_start_xmit(NULL, ncm->netdev);
-
- ncm->timer_force_tx = false;
+ netdev->netdev_ops->ndo_start_xmit(NULL, netdev);
}
return HRTIMER_NORESTART;
}
@@ -1357,7 +1352,6 @@ static void ncm_disable(struct usb_function *f)
DBG(cdev, "ncm deactivated\n");
if (ncm->port.in_ep->enabled) {
- ncm->timer_stopping = true;
ncm->netdev = NULL;
gether_disconnect(&ncm->port);
}
diff --git a/drivers/usb/gadget/function/f_uac1.c b/drivers/usb/gadget/function/f_uac1.c
index d04707580068..3b3db1a8df75 100644
--- a/drivers/usb/gadget/function/f_uac1.c
+++ b/drivers/usb/gadget/function/f_uac1.c
@@ -22,13 +22,26 @@
/* UAC1 spec: 3.7.2.3 Audio Channel Cluster Format */
#define UAC1_CHANNEL_MASK 0x0FFF
+#define USB_OUT_FU_ID (out_feature_unit_desc->bUnitID)
+#define USB_IN_FU_ID (in_feature_unit_desc->bUnitID)
+
#define EPIN_EN(_opts) ((_opts)->p_chmask != 0)
#define EPOUT_EN(_opts) ((_opts)->c_chmask != 0)
+#define FUIN_EN(_opts) ((_opts)->p_mute_present \
+ || (_opts)->p_volume_present)
+#define FUOUT_EN(_opts) ((_opts)->c_mute_present \
+ || (_opts)->c_volume_present)
struct f_uac1 {
struct g_audio g_audio;
u8 ac_intf, as_in_intf, as_out_intf;
u8 ac_alt, as_in_alt, as_out_alt; /* needed for get_alt() */
+
+ struct usb_ctrlrequest setup_cr; /* will be used in data stage */
+
+ /* Interrupt IN endpoint of AC interface */
+ struct usb_ep *int_ep;
+ atomic_t int_count;
};
static inline struct f_uac1 *func_to_uac1(struct usb_function *f)
@@ -58,7 +71,7 @@ static inline struct f_uac1_opts *g_audio_to_uac1_opts(struct g_audio *audio)
static struct usb_interface_descriptor ac_interface_desc = {
.bLength = USB_DT_INTERFACE_SIZE,
.bDescriptorType = USB_DT_INTERFACE,
- .bNumEndpoints = 0,
+ /* .bNumEndpoints = DYNAMIC */
.bInterfaceClass = USB_CLASS_AUDIO,
.bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL,
};
@@ -106,6 +119,19 @@ static struct uac1_output_terminal_descriptor usb_in_ot_desc = {
/* .bSourceID = DYNAMIC */
};
+static struct uac_feature_unit_descriptor *in_feature_unit_desc;
+static struct uac_feature_unit_descriptor *out_feature_unit_desc;
+
+/* AC IN Interrupt Endpoint */
+static struct usb_endpoint_descriptor ac_int_ep_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .wMaxPacketSize = cpu_to_le16(2),
+ .bInterval = 4,
+};
+
/* B.4.1 Standard AS Interface Descriptor */
static struct usb_interface_descriptor as_out_interface_alt_0_desc = {
.bLength = USB_DT_INTERFACE_SIZE,
@@ -232,8 +258,13 @@ static struct usb_descriptor_header *f_audio_desc[] = {
(struct usb_descriptor_header *)&usb_out_it_desc,
(struct usb_descriptor_header *)&io_out_ot_desc,
+ (struct usb_descriptor_header *)&out_feature_unit_desc,
+
(struct usb_descriptor_header *)&io_in_it_desc,
(struct usb_descriptor_header *)&usb_in_ot_desc,
+ (struct usb_descriptor_header *)&in_feature_unit_desc,
+
+ (struct usb_descriptor_header *)&ac_int_ep_desc,
(struct usb_descriptor_header *)&as_out_interface_alt_0_desc,
(struct usb_descriptor_header *)&as_out_interface_alt_1_desc,
@@ -263,6 +294,8 @@ enum {
STR_IO_IN_IT,
STR_IO_IN_IT_CH_NAMES,
STR_USB_IN_OT,
+ STR_FU_IN,
+ STR_FU_OUT,
STR_AS_OUT_IF_ALT0,
STR_AS_OUT_IF_ALT1,
STR_AS_IN_IF_ALT0,
@@ -277,6 +310,8 @@ static struct usb_string strings_uac1[] = {
[STR_IO_IN_IT].s = "Capture Input terminal",
[STR_IO_IN_IT_CH_NAMES].s = "Capture Channels",
[STR_USB_IN_OT].s = "Capture Output terminal",
+ [STR_FU_IN].s = "Capture Volume",
+ [STR_FU_OUT].s = "Playback Volume",
[STR_AS_OUT_IF_ALT0].s = "Playback Inactive",
[STR_AS_OUT_IF_ALT1].s = "Playback Active",
[STR_AS_IN_IF_ALT0].s = "Capture Inactive",
@@ -298,6 +333,376 @@ static struct usb_gadget_strings *uac1_strings[] = {
* This function is an ALSA sound card following USB Audio Class Spec 1.0.
*/
+static void audio_notify_complete(struct usb_ep *_ep, struct usb_request *req)
+{
+ struct g_audio *audio = req->context;
+ struct f_uac1 *uac1 = func_to_uac1(&audio->func);
+
+ atomic_dec(&uac1->int_count);
+ kfree(req->buf);
+ usb_ep_free_request(_ep, req);
+}
+
+static int audio_notify(struct g_audio *audio, int unit_id, int cs)
+{
+ struct f_uac1 *uac1 = func_to_uac1(&audio->func);
+ struct usb_request *req;
+ struct uac1_status_word *msg;
+ int ret;
+
+ if (!uac1->int_ep->enabled)
+ return 0;
+
+ if (atomic_inc_return(&uac1->int_count) > UAC1_DEF_INT_REQ_NUM) {
+ atomic_dec(&uac1->int_count);
+ return 0;
+ }
+
+ req = usb_ep_alloc_request(uac1->int_ep, GFP_ATOMIC);
+ if (req == NULL) {
+ ret = -ENOMEM;
+ goto err_dec_int_count;
+ }
+
+ msg = kmalloc(sizeof(*msg), GFP_ATOMIC);
+ if (msg == NULL) {
+ ret = -ENOMEM;
+ goto err_free_request;
+ }
+
+ msg->bStatusType = UAC1_STATUS_TYPE_IRQ_PENDING
+ | UAC1_STATUS_TYPE_ORIG_AUDIO_CONTROL_IF;
+ msg->bOriginator = unit_id;
+
+ req->length = sizeof(*msg);
+ req->buf = msg;
+ req->context = audio;
+ req->complete = audio_notify_complete;
+
+ ret = usb_ep_queue(uac1->int_ep, req, GFP_ATOMIC);
+
+ if (ret)
+ goto err_free_msg;
+
+ return 0;
+
+err_free_msg:
+ kfree(msg);
+err_free_request:
+ usb_ep_free_request(uac1->int_ep, req);
+err_dec_int_count:
+ atomic_dec(&uac1->int_count);
+
+ return ret;
+}
+
+static int
+in_rq_cur(struct usb_function *fn, const struct usb_ctrlrequest *cr)
+{
+ struct usb_request *req = fn->config->cdev->req;
+ struct g_audio *audio = func_to_g_audio(fn);
+ struct f_uac1_opts *opts = g_audio_to_uac1_opts(audio);
+ u16 w_length = le16_to_cpu(cr->wLength);
+ u16 w_index = le16_to_cpu(cr->wIndex);
+ u16 w_value = le16_to_cpu(cr->wValue);
+ u8 entity_id = (w_index >> 8) & 0xff;
+ u8 control_selector = w_value >> 8;
+ int value = -EOPNOTSUPP;
+
+ if ((FUIN_EN(opts) && (entity_id == USB_IN_FU_ID)) ||
+ (FUOUT_EN(opts) && (entity_id == USB_OUT_FU_ID))) {
+ unsigned int is_playback = 0;
+
+ if (FUIN_EN(opts) && (entity_id == USB_IN_FU_ID))
+ is_playback = 1;
+
+ if (control_selector == UAC_FU_MUTE) {
+ unsigned int mute;
+
+ u_audio_get_mute(audio, is_playback, &mute);
+
+ *(u8 *)req->buf = mute;
+ value = min_t(unsigned int, w_length, 1);
+ } else if (control_selector == UAC_FU_VOLUME) {
+ __le16 c;
+ s16 volume;
+
+ u_audio_get_volume(audio, is_playback, &volume);
+
+ c = cpu_to_le16(volume);
+
+ value = min_t(unsigned int, w_length, sizeof(c));
+ memcpy(req->buf, &c, value);
+ } else {
+ dev_err(&audio->gadget->dev,
+ "%s:%d control_selector=%d TODO!\n",
+ __func__, __LINE__, control_selector);
+ }
+ } else {
+ dev_err(&audio->gadget->dev,
+ "%s:%d entity_id=%d control_selector=%d TODO!\n",
+ __func__, __LINE__, entity_id, control_selector);
+ }
+
+ return value;
+}
+
+static int
+in_rq_min(struct usb_function *fn, const struct usb_ctrlrequest *cr)
+{
+ struct usb_request *req = fn->config->cdev->req;
+ struct g_audio *audio = func_to_g_audio(fn);
+ struct f_uac1_opts *opts = g_audio_to_uac1_opts(audio);
+ u16 w_length = le16_to_cpu(cr->wLength);
+ u16 w_index = le16_to_cpu(cr->wIndex);
+ u16 w_value = le16_to_cpu(cr->wValue);
+ u8 entity_id = (w_index >> 8) & 0xff;
+ u8 control_selector = w_value >> 8;
+ int value = -EOPNOTSUPP;
+
+ if ((FUIN_EN(opts) && (entity_id == USB_IN_FU_ID)) ||
+ (FUOUT_EN(opts) && (entity_id == USB_OUT_FU_ID))) {
+ unsigned int is_playback = 0;
+
+ if (FUIN_EN(opts) && (entity_id == USB_IN_FU_ID))
+ is_playback = 1;
+
+ if (control_selector == UAC_FU_VOLUME) {
+ __le16 r;
+ s16 min_db;
+
+ if (is_playback)
+ min_db = opts->p_volume_min;
+ else
+ min_db = opts->c_volume_min;
+
+ r = cpu_to_le16(min_db);
+
+ value = min_t(unsigned int, w_length, sizeof(r));
+ memcpy(req->buf, &r, value);
+ } else {
+ dev_err(&audio->gadget->dev,
+ "%s:%d control_selector=%d TODO!\n",
+ __func__, __LINE__, control_selector);
+ }
+ } else {
+ dev_err(&audio->gadget->dev,
+ "%s:%d entity_id=%d control_selector=%d TODO!\n",
+ __func__, __LINE__, entity_id, control_selector);
+ }
+
+ return value;
+}
+
+static int
+in_rq_max(struct usb_function *fn, const struct usb_ctrlrequest *cr)
+{
+ struct usb_request *req = fn->config->cdev->req;
+ struct g_audio *audio = func_to_g_audio(fn);
+ struct f_uac1_opts *opts = g_audio_to_uac1_opts(audio);
+ u16 w_length = le16_to_cpu(cr->wLength);
+ u16 w_index = le16_to_cpu(cr->wIndex);
+ u16 w_value = le16_to_cpu(cr->wValue);
+ u8 entity_id = (w_index >> 8) & 0xff;
+ u8 control_selector = w_value >> 8;
+ int value = -EOPNOTSUPP;
+
+ if ((FUIN_EN(opts) && (entity_id == USB_IN_FU_ID)) ||
+ (FUOUT_EN(opts) && (entity_id == USB_OUT_FU_ID))) {
+ unsigned int is_playback = 0;
+
+ if (FUIN_EN(opts) && (entity_id == USB_IN_FU_ID))
+ is_playback = 1;
+
+ if (control_selector == UAC_FU_VOLUME) {
+ __le16 r;
+ s16 max_db;
+
+ if (is_playback)
+ max_db = opts->p_volume_max;
+ else
+ max_db = opts->c_volume_max;
+
+ r = cpu_to_le16(max_db);
+
+ value = min_t(unsigned int, w_length, sizeof(r));
+ memcpy(req->buf, &r, value);
+ } else {
+ dev_err(&audio->gadget->dev,
+ "%s:%d control_selector=%d TODO!\n",
+ __func__, __LINE__, control_selector);
+ }
+ } else {
+ dev_err(&audio->gadget->dev,
+ "%s:%d entity_id=%d control_selector=%d TODO!\n",
+ __func__, __LINE__, entity_id, control_selector);
+ }
+
+ return value;
+}
+
+static int
+in_rq_res(struct usb_function *fn, const struct usb_ctrlrequest *cr)
+{
+ struct usb_request *req = fn->config->cdev->req;
+ struct g_audio *audio = func_to_g_audio(fn);
+ struct f_uac1_opts *opts = g_audio_to_uac1_opts(audio);
+ u16 w_length = le16_to_cpu(cr->wLength);
+ u16 w_index = le16_to_cpu(cr->wIndex);
+ u16 w_value = le16_to_cpu(cr->wValue);
+ u8 entity_id = (w_index >> 8) & 0xff;
+ u8 control_selector = w_value >> 8;
+ int value = -EOPNOTSUPP;
+
+ if ((FUIN_EN(opts) && (entity_id == USB_IN_FU_ID)) ||
+ (FUOUT_EN(opts) && (entity_id == USB_OUT_FU_ID))) {
+ unsigned int is_playback = 0;
+
+ if (FUIN_EN(opts) && (entity_id == USB_IN_FU_ID))
+ is_playback = 1;
+
+ if (control_selector == UAC_FU_VOLUME) {
+ __le16 r;
+ s16 res_db;
+
+ if (is_playback)
+ res_db = opts->p_volume_res;
+ else
+ res_db = opts->c_volume_res;
+
+ r = cpu_to_le16(res_db);
+
+ value = min_t(unsigned int, w_length, sizeof(r));
+ memcpy(req->buf, &r, value);
+ } else {
+ dev_err(&audio->gadget->dev,
+ "%s:%d control_selector=%d TODO!\n",
+ __func__, __LINE__, control_selector);
+ }
+ } else {
+ dev_err(&audio->gadget->dev,
+ "%s:%d entity_id=%d control_selector=%d TODO!\n",
+ __func__, __LINE__, entity_id, control_selector);
+ }
+
+ return value;
+}
+
+static void
+out_rq_cur_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ struct g_audio *audio = req->context;
+ struct usb_composite_dev *cdev = audio->func.config->cdev;
+ struct f_uac1_opts *opts = g_audio_to_uac1_opts(audio);
+ struct f_uac1 *uac1 = func_to_uac1(&audio->func);
+ struct usb_ctrlrequest *cr = &uac1->setup_cr;
+ u16 w_index = le16_to_cpu(cr->wIndex);
+ u16 w_value = le16_to_cpu(cr->wValue);
+ u8 entity_id = (w_index >> 8) & 0xff;
+ u8 control_selector = w_value >> 8;
+
+ if (req->status != 0) {
+ dev_dbg(&cdev->gadget->dev, "completion err %d\n", req->status);
+ return;
+ }
+
+ if ((FUIN_EN(opts) && (entity_id == USB_IN_FU_ID)) ||
+ (FUOUT_EN(opts) && (entity_id == USB_OUT_FU_ID))) {
+ unsigned int is_playback = 0;
+
+ if (FUIN_EN(opts) && (entity_id == USB_IN_FU_ID))
+ is_playback = 1;
+
+ if (control_selector == UAC_FU_MUTE) {
+ u8 mute = *(u8 *)req->buf;
+
+ u_audio_set_mute(audio, is_playback, mute);
+
+ return;
+ } else if (control_selector == UAC_FU_VOLUME) {
+ __le16 *c = req->buf;
+ s16 volume;
+
+ volume = le16_to_cpu(*c);
+ u_audio_set_volume(audio, is_playback, volume);
+
+ return;
+ } else {
+ dev_err(&audio->gadget->dev,
+ "%s:%d control_selector=%d TODO!\n",
+ __func__, __LINE__, control_selector);
+ usb_ep_set_halt(ep);
+ }
+ } else {
+ dev_err(&audio->gadget->dev,
+ "%s:%d entity_id=%d control_selector=%d TODO!\n",
+ __func__, __LINE__, entity_id, control_selector);
+ usb_ep_set_halt(ep);
+
+ }
+}
+
+static int
+out_rq_cur(struct usb_function *fn, const struct usb_ctrlrequest *cr)
+{
+ struct usb_request *req = fn->config->cdev->req;
+ struct g_audio *audio = func_to_g_audio(fn);
+ struct f_uac1_opts *opts = g_audio_to_uac1_opts(audio);
+ struct f_uac1 *uac1 = func_to_uac1(&audio->func);
+ u16 w_length = le16_to_cpu(cr->wLength);
+ u16 w_index = le16_to_cpu(cr->wIndex);
+ u16 w_value = le16_to_cpu(cr->wValue);
+ u8 entity_id = (w_index >> 8) & 0xff;
+ u8 control_selector = w_value >> 8;
+
+ if ((FUIN_EN(opts) && (entity_id == USB_IN_FU_ID)) ||
+ (FUOUT_EN(opts) && (entity_id == USB_OUT_FU_ID))) {
+ memcpy(&uac1->setup_cr, cr, sizeof(*cr));
+ req->context = audio;
+ req->complete = out_rq_cur_complete;
+
+ return w_length;
+ } else {
+ dev_err(&audio->gadget->dev,
+ "%s:%d entity_id=%d control_selector=%d TODO!\n",
+ __func__, __LINE__, entity_id, control_selector);
+ }
+ return -EOPNOTSUPP;
+}
+
+static int ac_rq_in(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);
+
+ DBG(cdev, "bRequest 0x%x, w_value 0x%04x, len %d, endpoint %d\n",
+ ctrl->bRequest, w_value, len, ep);
+
+ switch (ctrl->bRequest) {
+ case UAC_GET_CUR:
+ return in_rq_cur(f, ctrl);
+ case UAC_GET_MIN:
+ return in_rq_min(f, ctrl);
+ case UAC_GET_MAX:
+ return in_rq_max(f, ctrl);
+ case UAC_GET_RES:
+ return in_rq_res(f, ctrl);
+ case UAC_GET_MEM:
+ break;
+ case UAC_GET_STAT:
+ value = len;
+ break;
+ default:
+ break;
+ }
+
+ return value;
+}
+
static int audio_set_endpoint_req(struct usb_function *f,
const struct usb_ctrlrequest *ctrl)
{
@@ -383,7 +788,13 @@ f_audio_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
case USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_ENDPOINT:
value = audio_get_endpoint_req(f, ctrl);
break;
-
+ case USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE:
+ if (ctrl->bRequest == UAC_SET_CUR)
+ value = out_rq_cur(f, ctrl);
+ break;
+ case USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE:
+ value = ac_rq_in(f, ctrl);
+ break;
default:
ERROR(cdev, "invalid control req%02x.%02x v%04x i%04x l%d\n",
ctrl->bRequestType, ctrl->bRequest,
@@ -411,6 +822,7 @@ static int f_audio_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
struct usb_composite_dev *cdev = f->config->cdev;
struct usb_gadget *gadget = cdev->gadget;
struct device *dev = &gadget->dev;
+ struct g_audio *audio = func_to_g_audio(f);
struct f_uac1 *uac1 = func_to_uac1(f);
int ret = 0;
@@ -426,6 +838,14 @@ static int f_audio_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
return -EINVAL;
}
+
+ /* restart interrupt endpoint */
+ if (uac1->int_ep) {
+ usb_ep_disable(uac1->int_ep);
+ config_ep_by_speed(gadget, &audio->func, uac1->int_ep);
+ usb_ep_enable(uac1->int_ep);
+ }
+
return 0;
}
@@ -481,10 +901,33 @@ static void f_audio_disable(struct usb_function *f)
u_audio_stop_playback(&uac1->g_audio);
u_audio_stop_capture(&uac1->g_audio);
+ if (uac1->int_ep)
+ usb_ep_disable(uac1->int_ep);
}
/*-------------------------------------------------------------------------*/
+static struct uac_feature_unit_descriptor *build_fu_desc(int chmask)
+{
+ struct uac_feature_unit_descriptor *fu_desc;
+ int channels = num_channels(chmask);
+ int fu_desc_size = UAC_DT_FEATURE_UNIT_SIZE(channels);
+
+ fu_desc = kzalloc(fu_desc_size, GFP_KERNEL);
+ if (!fu_desc)
+ return NULL;
+
+ fu_desc->bLength = fu_desc_size;
+ fu_desc->bDescriptorType = USB_DT_CS_INTERFACE;
+
+ fu_desc->bDescriptorSubtype = UAC_FEATURE_UNIT;
+ fu_desc->bControlSize = 2;
+ /* bUnitID, bSourceID and bmaControls will be defined later */
+
+ return fu_desc;
+}
+
+/* B.3.2 Class-Specific AC Interface Descriptor */
static struct
uac1_ac_header_descriptor *build_ac_header_desc(struct f_uac1_opts *opts)
{
@@ -530,9 +973,23 @@ static void setup_descriptor(struct f_uac1_opts *opts)
io_out_ot_desc.bTerminalID = i++;
if (EPIN_EN(opts))
usb_in_ot_desc.bTerminalID = i++;
-
- usb_in_ot_desc.bSourceID = io_in_it_desc.bTerminalID;
- io_out_ot_desc.bSourceID = usb_out_it_desc.bTerminalID;
+ if (FUOUT_EN(opts))
+ out_feature_unit_desc->bUnitID = i++;
+ if (FUIN_EN(opts))
+ in_feature_unit_desc->bUnitID = i++;
+
+ if (FUIN_EN(opts)) {
+ usb_in_ot_desc.bSourceID = in_feature_unit_desc->bUnitID;
+ in_feature_unit_desc->bSourceID = io_in_it_desc.bTerminalID;
+ } else {
+ usb_in_ot_desc.bSourceID = io_in_it_desc.bTerminalID;
+ }
+ if (FUOUT_EN(opts)) {
+ io_out_ot_desc.bSourceID = out_feature_unit_desc->bUnitID;
+ out_feature_unit_desc->bSourceID = usb_out_it_desc.bTerminalID;
+ } else {
+ io_out_ot_desc.bSourceID = usb_out_it_desc.bTerminalID;
+ }
as_out_header_desc.bTerminalLink = usb_out_it_desc.bTerminalID;
as_in_header_desc.bTerminalLink = usb_in_ot_desc.bTerminalID;
@@ -544,6 +1001,8 @@ static void setup_descriptor(struct f_uac1_opts *opts)
len += sizeof(usb_in_ot_desc);
len += sizeof(io_in_it_desc);
+ if (FUIN_EN(opts))
+ len += in_feature_unit_desc->bLength;
ac_header_desc->wTotalLength = cpu_to_le16(len);
}
if (EPOUT_EN(opts)) {
@@ -551,6 +1010,8 @@ static void setup_descriptor(struct f_uac1_opts *opts)
len += sizeof(usb_out_it_desc);
len += sizeof(io_out_ot_desc);
+ if (FUOUT_EN(opts))
+ len += out_feature_unit_desc->bLength;
ac_header_desc->wTotalLength = cpu_to_le16(len);
}
@@ -561,13 +1022,20 @@ static void setup_descriptor(struct f_uac1_opts *opts)
if (EPOUT_EN(opts)) {
f_audio_desc[i++] = USBDHDR(&usb_out_it_desc);
f_audio_desc[i++] = USBDHDR(&io_out_ot_desc);
+ if (FUOUT_EN(opts))
+ f_audio_desc[i++] = USBDHDR(out_feature_unit_desc);
}
if (EPIN_EN(opts)) {
f_audio_desc[i++] = USBDHDR(&io_in_it_desc);
f_audio_desc[i++] = USBDHDR(&usb_in_ot_desc);
+ if (FUIN_EN(opts))
+ f_audio_desc[i++] = USBDHDR(in_feature_unit_desc);
}
+ if (FUOUT_EN(opts) || FUIN_EN(opts))
+ f_audio_desc[i++] = USBDHDR(&ac_int_ep_desc);
+
if (EPOUT_EN(opts)) {
f_audio_desc[i++] = USBDHDR(&as_out_interface_alt_0_desc);
f_audio_desc[i++] = USBDHDR(&as_out_interface_alt_1_desc);
@@ -614,6 +1082,28 @@ static int f_audio_validate_opts(struct g_audio *audio, struct device *dev)
return -EINVAL;
}
+ if (opts->p_volume_max <= opts->p_volume_min) {
+ dev_err(dev, "Error: incorrect playback volume max/min\n");
+ return -EINVAL;
+ } else if (opts->c_volume_max <= opts->c_volume_min) {
+ dev_err(dev, "Error: incorrect capture volume max/min\n");
+ return -EINVAL;
+ } else if (opts->p_volume_res <= 0) {
+ dev_err(dev, "Error: negative/zero playback volume resolution\n");
+ return -EINVAL;
+ } else if (opts->c_volume_res <= 0) {
+ dev_err(dev, "Error: negative/zero capture volume resolution\n");
+ return -EINVAL;
+ }
+
+ if ((opts->p_volume_max - opts->p_volume_min) % opts->p_volume_res) {
+ dev_err(dev, "Error: incorrect playback volume resolution\n");
+ return -EINVAL;
+ } else if ((opts->c_volume_max - opts->c_volume_min) % opts->c_volume_res) {
+ dev_err(dev, "Error: incorrect capture volume resolution\n");
+ return -EINVAL;
+ }
+
return 0;
}
@@ -647,6 +1137,21 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
if (!ac_header_desc)
return -ENOMEM;
+ if (FUOUT_EN(audio_opts)) {
+ out_feature_unit_desc = build_fu_desc(audio_opts->c_chmask);
+ if (!out_feature_unit_desc) {
+ status = -ENOMEM;
+ goto fail;
+ }
+ }
+ if (FUIN_EN(audio_opts)) {
+ in_feature_unit_desc = build_fu_desc(audio_opts->p_chmask);
+ if (!in_feature_unit_desc) {
+ status = -ENOMEM;
+ goto err_free_fu;
+ }
+ }
+
ac_interface_desc.iInterface = us[STR_AC_IF].id;
usb_out_it_desc.iTerminal = us[STR_USB_OUT_IT].id;
usb_out_it_desc.iChannelNames = us[STR_USB_OUT_IT_CH_NAMES].id;
@@ -659,6 +1164,21 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
as_in_interface_alt_0_desc.iInterface = us[STR_AS_IN_IF_ALT0].id;
as_in_interface_alt_1_desc.iInterface = us[STR_AS_IN_IF_ALT1].id;
+ if (FUOUT_EN(audio_opts)) {
+ u8 *i_feature;
+
+ i_feature = (u8 *)out_feature_unit_desc +
+ out_feature_unit_desc->bLength - 1;
+ *i_feature = us[STR_FU_OUT].id;
+ }
+ if (FUIN_EN(audio_opts)) {
+ u8 *i_feature;
+
+ i_feature = (u8 *)in_feature_unit_desc +
+ in_feature_unit_desc->bLength - 1;
+ *i_feature = us[STR_FU_IN].id;
+ }
+
/* Set channel numbers */
usb_out_it_desc.bNrChannels = num_channels(audio_opts->c_chmask);
usb_out_it_desc.wChannelConfig = cpu_to_le16(audio_opts->c_chmask);
@@ -671,6 +1191,27 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
as_in_type_i_desc.bSubframeSize = audio_opts->p_ssize;
as_in_type_i_desc.bBitResolution = audio_opts->p_ssize * 8;
+ if (FUOUT_EN(audio_opts)) {
+ __le16 *bma = (__le16 *)&out_feature_unit_desc->bmaControls[0];
+ u32 control = 0;
+
+ if (audio_opts->c_mute_present)
+ control |= UAC_FU_MUTE;
+ if (audio_opts->c_volume_present)
+ control |= UAC_FU_VOLUME;
+ *bma = cpu_to_le16(control);
+ }
+ if (FUIN_EN(audio_opts)) {
+ __le16 *bma = (__le16 *)&in_feature_unit_desc->bmaControls[0];
+ u32 control = 0;
+
+ if (audio_opts->p_mute_present)
+ control |= UAC_FU_MUTE;
+ if (audio_opts->p_volume_present)
+ control |= UAC_FU_VOLUME;
+ *bma = cpu_to_le16(control);
+ }
+
/* Set sample rates */
rate = audio_opts->c_srate;
sam_freq = as_out_type_i_desc.tSamFreq[0];
@@ -682,7 +1223,7 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
/* allocate instance-specific interface IDs, and patch descriptors */
status = usb_interface_id(c, f);
if (status < 0)
- goto fail;
+ goto err_free_fu;
ac_interface_desc.bInterfaceNumber = status;
uac1->ac_intf = status;
uac1->ac_alt = 0;
@@ -692,7 +1233,7 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
if (EPOUT_EN(audio_opts)) {
status = usb_interface_id(c, f);
if (status < 0)
- goto fail;
+ goto err_free_fu;
as_out_interface_alt_0_desc.bInterfaceNumber = status;
as_out_interface_alt_1_desc.bInterfaceNumber = status;
ac_header_desc->baInterfaceNr[ba_iface_id++] = status;
@@ -703,7 +1244,7 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
if (EPIN_EN(audio_opts)) {
status = usb_interface_id(c, f);
if (status < 0)
- goto fail;
+ goto err_free_fu;
as_in_interface_alt_0_desc.bInterfaceNumber = status;
as_in_interface_alt_1_desc.bInterfaceNumber = status;
ac_header_desc->baInterfaceNr[ba_iface_id++] = status;
@@ -715,11 +1256,24 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
status = -ENODEV;
+ ac_interface_desc.bNumEndpoints = 0;
+
+ /* allocate AC interrupt endpoint */
+ if (FUOUT_EN(audio_opts) || FUIN_EN(audio_opts)) {
+ ep = usb_ep_autoconfig(cdev->gadget, &ac_int_ep_desc);
+ if (!ep)
+ goto err_free_fu;
+ uac1->int_ep = ep;
+ uac1->int_ep->desc = &ac_int_ep_desc;
+
+ ac_interface_desc.bNumEndpoints = 1;
+ }
+
/* allocate instance-specific endpoints */
if (EPOUT_EN(audio_opts)) {
ep = usb_ep_autoconfig(cdev->gadget, &as_out_ep_desc);
if (!ep)
- goto fail;
+ goto err_free_fu;
audio->out_ep = ep;
audio->out_ep->desc = &as_out_ep_desc;
}
@@ -727,7 +1281,7 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
if (EPIN_EN(audio_opts)) {
ep = usb_ep_autoconfig(cdev->gadget, &as_in_ep_desc);
if (!ep)
- goto fail;
+ goto err_free_fu;
audio->in_ep = ep;
audio->in_ep->desc = &as_in_ep_desc;
}
@@ -738,17 +1292,37 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
status = usb_assign_descriptors(f, f_audio_desc, f_audio_desc, NULL,
NULL);
if (status)
- goto fail;
+ goto err_free_fu;
audio->out_ep_maxpsize = le16_to_cpu(as_out_ep_desc.wMaxPacketSize);
audio->in_ep_maxpsize = le16_to_cpu(as_in_ep_desc.wMaxPacketSize);
audio->params.c_chmask = audio_opts->c_chmask;
audio->params.c_srate = audio_opts->c_srate;
audio->params.c_ssize = audio_opts->c_ssize;
+ if (FUIN_EN(audio_opts)) {
+ audio->params.p_fu.id = USB_IN_FU_ID;
+ audio->params.p_fu.mute_present = audio_opts->p_mute_present;
+ audio->params.p_fu.volume_present =
+ audio_opts->p_volume_present;
+ audio->params.p_fu.volume_min = audio_opts->p_volume_min;
+ audio->params.p_fu.volume_max = audio_opts->p_volume_max;
+ audio->params.p_fu.volume_res = audio_opts->p_volume_res;
+ }
audio->params.p_chmask = audio_opts->p_chmask;
audio->params.p_srate = audio_opts->p_srate;
audio->params.p_ssize = audio_opts->p_ssize;
+ if (FUOUT_EN(audio_opts)) {
+ audio->params.c_fu.id = USB_OUT_FU_ID;
+ audio->params.c_fu.mute_present = audio_opts->c_mute_present;
+ audio->params.c_fu.volume_present =
+ audio_opts->c_volume_present;
+ audio->params.c_fu.volume_min = audio_opts->c_volume_min;
+ audio->params.c_fu.volume_max = audio_opts->c_volume_max;
+ audio->params.c_fu.volume_res = audio_opts->c_volume_res;
+ }
audio->params.req_number = audio_opts->req_number;
+ if (FUOUT_EN(audio_opts) || FUIN_EN(audio_opts))
+ audio->notify = audio_notify;
status = g_audio_setup(audio, "UAC1_PCM", "UAC1_Gadget");
if (status)
@@ -758,6 +1332,11 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
err_card_register:
usb_free_all_descriptors(f);
+err_free_fu:
+ kfree(out_feature_unit_desc);
+ out_feature_unit_desc = NULL;
+ kfree(in_feature_unit_desc);
+ in_feature_unit_desc = NULL;
fail:
kfree(ac_header_desc);
ac_header_desc = NULL;
@@ -783,7 +1362,15 @@ static struct configfs_item_operations f_uac1_item_ops = {
.release = f_uac1_attr_release,
};
-#define UAC1_ATTRIBUTE(name) \
+#define uac1_kstrtou32 kstrtou32
+#define uac1_kstrtos16 kstrtos16
+#define uac1_kstrtobool(s, base, res) kstrtobool((s), (res))
+
+static const char *u32_fmt = "%u\n";
+static const char *s16_fmt = "%hd\n";
+static const char *bool_fmt = "%u\n";
+
+#define UAC1_ATTRIBUTE(type, name) \
static ssize_t f_uac1_opts_##name##_show( \
struct config_item *item, \
char *page) \
@@ -792,7 +1379,7 @@ static ssize_t f_uac1_opts_##name##_show( \
int result; \
\
mutex_lock(&opts->lock); \
- result = sprintf(page, "%u\n", opts->name); \
+ result = sprintf(page, type##_fmt, opts->name); \
mutex_unlock(&opts->lock); \
\
return result; \
@@ -804,7 +1391,7 @@ static ssize_t f_uac1_opts_##name##_store( \
{ \
struct f_uac1_opts *opts = to_f_uac1_opts(item); \
int ret; \
- u32 num; \
+ type num; \
\
mutex_lock(&opts->lock); \
if (opts->refcnt) { \
@@ -812,7 +1399,7 @@ static ssize_t f_uac1_opts_##name##_store( \
goto end; \
} \
\
- ret = kstrtou32(page, 0, &num); \
+ ret = uac1_kstrto##type(page, 0, &num); \
if (ret) \
goto end; \
\
@@ -826,13 +1413,25 @@ end: \
\
CONFIGFS_ATTR(f_uac1_opts_, name)
-UAC1_ATTRIBUTE(c_chmask);
-UAC1_ATTRIBUTE(c_srate);
-UAC1_ATTRIBUTE(c_ssize);
-UAC1_ATTRIBUTE(p_chmask);
-UAC1_ATTRIBUTE(p_srate);
-UAC1_ATTRIBUTE(p_ssize);
-UAC1_ATTRIBUTE(req_number);
+UAC1_ATTRIBUTE(u32, c_chmask);
+UAC1_ATTRIBUTE(u32, c_srate);
+UAC1_ATTRIBUTE(u32, c_ssize);
+UAC1_ATTRIBUTE(u32, p_chmask);
+UAC1_ATTRIBUTE(u32, p_srate);
+UAC1_ATTRIBUTE(u32, p_ssize);
+UAC1_ATTRIBUTE(u32, req_number);
+
+UAC1_ATTRIBUTE(bool, p_mute_present);
+UAC1_ATTRIBUTE(bool, p_volume_present);
+UAC1_ATTRIBUTE(s16, p_volume_min);
+UAC1_ATTRIBUTE(s16, p_volume_max);
+UAC1_ATTRIBUTE(s16, p_volume_res);
+
+UAC1_ATTRIBUTE(bool, c_mute_present);
+UAC1_ATTRIBUTE(bool, c_volume_present);
+UAC1_ATTRIBUTE(s16, c_volume_min);
+UAC1_ATTRIBUTE(s16, c_volume_max);
+UAC1_ATTRIBUTE(s16, c_volume_res);
static struct configfs_attribute *f_uac1_attrs[] = {
&f_uac1_opts_attr_c_chmask,
@@ -842,6 +1441,19 @@ static struct configfs_attribute *f_uac1_attrs[] = {
&f_uac1_opts_attr_p_srate,
&f_uac1_opts_attr_p_ssize,
&f_uac1_opts_attr_req_number,
+
+ &f_uac1_opts_attr_p_mute_present,
+ &f_uac1_opts_attr_p_volume_present,
+ &f_uac1_opts_attr_p_volume_min,
+ &f_uac1_opts_attr_p_volume_max,
+ &f_uac1_opts_attr_p_volume_res,
+
+ &f_uac1_opts_attr_c_mute_present,
+ &f_uac1_opts_attr_c_volume_present,
+ &f_uac1_opts_attr_c_volume_min,
+ &f_uac1_opts_attr_c_volume_max,
+ &f_uac1_opts_attr_c_volume_res,
+
NULL,
};
@@ -879,6 +1491,19 @@ static struct usb_function_instance *f_audio_alloc_inst(void)
opts->p_chmask = UAC1_DEF_PCHMASK;
opts->p_srate = UAC1_DEF_PSRATE;
opts->p_ssize = UAC1_DEF_PSSIZE;
+
+ opts->p_mute_present = UAC1_DEF_MUTE_PRESENT;
+ opts->p_volume_present = UAC1_DEF_VOLUME_PRESENT;
+ opts->p_volume_min = UAC1_DEF_MIN_DB;
+ opts->p_volume_max = UAC1_DEF_MAX_DB;
+ opts->p_volume_res = UAC1_DEF_RES_DB;
+
+ opts->c_mute_present = UAC1_DEF_MUTE_PRESENT;
+ opts->c_volume_present = UAC1_DEF_VOLUME_PRESENT;
+ opts->c_volume_min = UAC1_DEF_MIN_DB;
+ opts->c_volume_max = UAC1_DEF_MAX_DB;
+ opts->c_volume_res = UAC1_DEF_RES_DB;
+
opts->req_number = UAC1_DEF_REQ_NUM;
return &opts->func_inst;
}
@@ -903,6 +1528,11 @@ static void f_audio_unbind(struct usb_configuration *c, struct usb_function *f)
g_audio_cleanup(audio);
usb_free_all_descriptors(f);
+ kfree(out_feature_unit_desc);
+ out_feature_unit_desc = NULL;
+ kfree(in_feature_unit_desc);
+ in_feature_unit_desc = NULL;
+
kfree(ac_header_desc);
ac_header_desc = NULL;
diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c
index ae29ff2b2b68..b9edc6787f79 100644
--- a/drivers/usb/gadget/function/f_uac2.c
+++ b/drivers/usb/gadget/function/f_uac2.c
@@ -5,6 +5,9 @@
* Copyright (C) 2011
* Yadwinder Singh (yadi.brar01@gmail.com)
* Jaswinder Singh (jaswinder.singh@linaro.org)
+ *
+ * Copyright (C) 2020
+ * Ruslan Bilovol (ruslan.bilovol@gmail.com)
*/
#include <linux/usb/audio.h>
@@ -19,14 +22,16 @@
/*
* The driver implements a simple UAC_2 topology.
- * USB-OUT -> IT_1 -> OT_3 -> ALSA_Capture
- * ALSA_Playback -> IT_2 -> OT_4 -> USB-IN
+ * USB-OUT -> IT_1 -> FU -> OT_3 -> ALSA_Capture
+ * ALSA_Playback -> IT_2 -> FU -> OT_4 -> USB-IN
* Capture and Playback sampling rates are independently
* controlled by two clock sources :
* CLK_5 := c_srate, and CLK_6 := p_srate
*/
#define USB_OUT_CLK_ID (out_clk_src_desc.bClockID)
#define USB_IN_CLK_ID (in_clk_src_desc.bClockID)
+#define USB_OUT_FU_ID (out_feature_unit_desc->bUnitID)
+#define USB_IN_FU_ID (in_feature_unit_desc->bUnitID)
#define CONTROL_ABSENT 0
#define CONTROL_RDONLY 1
@@ -34,6 +39,8 @@
#define CLK_FREQ_CTRL 0
#define CLK_VLD_CTRL 2
+#define FU_MUTE_CTRL 0
+#define FU_VOL_CTRL 2
#define COPY_CTRL 0
#define CONN_CTRL 2
@@ -44,12 +51,24 @@
#define EPIN_EN(_opts) ((_opts)->p_chmask != 0)
#define EPOUT_EN(_opts) ((_opts)->c_chmask != 0)
+#define FUIN_EN(_opts) (EPIN_EN(_opts) \
+ && ((_opts)->p_mute_present \
+ || (_opts)->p_volume_present))
+#define FUOUT_EN(_opts) (EPOUT_EN(_opts) \
+ && ((_opts)->c_mute_present \
+ || (_opts)->c_volume_present))
#define EPOUT_FBACK_IN_EN(_opts) ((_opts)->c_sync == USB_ENDPOINT_SYNC_ASYNC)
struct f_uac2 {
struct g_audio g_audio;
u8 ac_intf, as_in_intf, as_out_intf;
u8 ac_alt, as_in_alt, as_out_alt; /* needed for get_alt() */
+
+ struct usb_ctrlrequest setup_cr; /* will be used in data stage */
+
+ /* Interrupt IN endpoint of AC interface */
+ struct usb_ep *int_ep;
+ atomic_t int_count;
};
static inline struct f_uac2 *func_to_uac2(struct usb_function *f)
@@ -63,6 +82,8 @@ struct f_uac2_opts *g_audio_to_uac2_opts(struct g_audio *agdev)
return container_of(agdev->func.fi, struct f_uac2_opts, func_inst);
}
+static int afunc_notify(struct g_audio *agdev, int unit_id, int cs);
+
/* --------- USB Function Interface ------------- */
enum {
@@ -74,6 +95,8 @@ enum {
STR_IO_IT,
STR_USB_OT,
STR_IO_OT,
+ STR_FU_IN,
+ STR_FU_OUT,
STR_AS_OUT_ALT0,
STR_AS_OUT_ALT1,
STR_AS_IN_ALT0,
@@ -92,6 +115,8 @@ static struct usb_string strings_fn[] = {
[STR_IO_IT].s = "USBD Out",
[STR_USB_OT].s = "USBH In",
[STR_IO_OT].s = "USBD In",
+ [STR_FU_IN].s = "Capture Volume",
+ [STR_FU_OUT].s = "Playback Volume",
[STR_AS_OUT_ALT0].s = "Playback Inactive",
[STR_AS_OUT_ALT1].s = "Playback Active",
[STR_AS_IN_ALT0].s = "Capture Inactive",
@@ -126,7 +151,7 @@ static struct usb_interface_descriptor std_ac_if_desc = {
.bDescriptorType = USB_DT_INTERFACE,
.bAlternateSetting = 0,
- .bNumEndpoints = 0,
+ /* .bNumEndpoints = DYNAMIC */
.bInterfaceClass = USB_CLASS_AUDIO,
.bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL,
.bInterfaceProtocol = UAC_VERSION_2,
@@ -212,6 +237,9 @@ static struct uac2_output_terminal_descriptor io_out_ot_desc = {
.bmControls = cpu_to_le16(CONTROL_RDWR << COPY_CTRL),
};
+static struct uac2_feature_unit_descriptor *in_feature_unit_desc;
+static struct uac2_feature_unit_descriptor *out_feature_unit_desc;
+
static struct uac2_ac_header_descriptor ac_hdr_desc = {
.bLength = sizeof ac_hdr_desc,
.bDescriptorType = USB_DT_CS_INTERFACE,
@@ -223,6 +251,36 @@ static struct uac2_ac_header_descriptor ac_hdr_desc = {
.bmControls = 0,
};
+/* AC IN Interrupt Endpoint */
+static struct usb_endpoint_descriptor fs_ep_int_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .wMaxPacketSize = cpu_to_le16(6),
+ .bInterval = 1,
+};
+
+static struct usb_endpoint_descriptor hs_ep_int_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .wMaxPacketSize = cpu_to_le16(6),
+ .bInterval = 4,
+};
+
+static struct usb_endpoint_descriptor ss_ep_int_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .wMaxPacketSize = cpu_to_le16(6),
+ .bInterval = 4,
+};
+
/* Audio Streaming OUT Interface - Alt0 */
static struct usb_interface_descriptor std_as_out_if0_desc = {
.bLength = sizeof std_as_out_if0_desc,
@@ -452,10 +510,14 @@ static struct usb_descriptor_header *fs_audio_desc[] = {
(struct usb_descriptor_header *)&in_clk_src_desc,
(struct usb_descriptor_header *)&out_clk_src_desc,
(struct usb_descriptor_header *)&usb_out_it_desc,
+ (struct usb_descriptor_header *)&out_feature_unit_desc,
(struct usb_descriptor_header *)&io_in_it_desc,
(struct usb_descriptor_header *)&usb_in_ot_desc,
+ (struct usb_descriptor_header *)&in_feature_unit_desc,
(struct usb_descriptor_header *)&io_out_ot_desc,
+ (struct usb_descriptor_header *)&fs_ep_int_desc,
+
(struct usb_descriptor_header *)&std_as_out_if0_desc,
(struct usb_descriptor_header *)&std_as_out_if1_desc,
@@ -483,10 +545,14 @@ static struct usb_descriptor_header *hs_audio_desc[] = {
(struct usb_descriptor_header *)&in_clk_src_desc,
(struct usb_descriptor_header *)&out_clk_src_desc,
(struct usb_descriptor_header *)&usb_out_it_desc,
+ (struct usb_descriptor_header *)&out_feature_unit_desc,
(struct usb_descriptor_header *)&io_in_it_desc,
(struct usb_descriptor_header *)&usb_in_ot_desc,
+ (struct usb_descriptor_header *)&in_feature_unit_desc,
(struct usb_descriptor_header *)&io_out_ot_desc,
+ (struct usb_descriptor_header *)&hs_ep_int_desc,
+
(struct usb_descriptor_header *)&std_as_out_if0_desc,
(struct usb_descriptor_header *)&std_as_out_if1_desc,
@@ -514,10 +580,14 @@ static struct usb_descriptor_header *ss_audio_desc[] = {
(struct usb_descriptor_header *)&in_clk_src_desc,
(struct usb_descriptor_header *)&out_clk_src_desc,
(struct usb_descriptor_header *)&usb_out_it_desc,
+ (struct usb_descriptor_header *)&out_feature_unit_desc,
(struct usb_descriptor_header *)&io_in_it_desc,
(struct usb_descriptor_header *)&usb_in_ot_desc,
+ (struct usb_descriptor_header *)&in_feature_unit_desc,
(struct usb_descriptor_header *)&io_out_ot_desc,
+ (struct usb_descriptor_header *)&ss_ep_int_desc,
+
(struct usb_descriptor_header *)&std_as_out_if0_desc,
(struct usb_descriptor_header *)&std_as_out_if1_desc,
@@ -539,6 +609,17 @@ static struct usb_descriptor_header *ss_audio_desc[] = {
NULL,
};
+struct cntrl_cur_lay2 {
+ __le16 wCUR;
+};
+
+struct cntrl_range_lay2 {
+ __le16 wNumSubRanges;
+ __le16 wMIN;
+ __le16 wMAX;
+ __le16 wRES;
+} __packed;
+
struct cntrl_cur_lay3 {
__le32 dCUR;
};
@@ -595,6 +676,26 @@ static int set_ep_max_packet_size(const struct f_uac2_opts *uac2_opts,
return 0;
}
+static struct uac2_feature_unit_descriptor *build_fu_desc(int chmask)
+{
+ struct uac2_feature_unit_descriptor *fu_desc;
+ int channels = num_channels(chmask);
+ int fu_desc_size = UAC2_DT_FEATURE_UNIT_SIZE(channels);
+
+ fu_desc = kzalloc(fu_desc_size, GFP_KERNEL);
+ if (!fu_desc)
+ return NULL;
+
+ fu_desc->bLength = fu_desc_size;
+ fu_desc->bDescriptorType = USB_DT_CS_INTERFACE;
+
+ fu_desc->bDescriptorSubtype = UAC_FEATURE_UNIT;
+
+ /* bUnitID, bSourceID and bmaControls will be defined later */
+
+ return fu_desc;
+}
+
/* Use macro to overcome line length limitation */
#define USBDHDR(p) (struct usb_descriptor_header *)(p)
@@ -607,6 +708,7 @@ static void setup_headers(struct f_uac2_opts *opts,
struct usb_endpoint_descriptor *epout_desc;
struct usb_endpoint_descriptor *epin_desc;
struct usb_endpoint_descriptor *epin_fback_desc;
+ struct usb_endpoint_descriptor *ep_int_desc;
int i;
switch (speed) {
@@ -614,11 +716,13 @@ static void setup_headers(struct f_uac2_opts *opts,
epout_desc = &fs_epout_desc;
epin_desc = &fs_epin_desc;
epin_fback_desc = &fs_epin_fback_desc;
+ ep_int_desc = &fs_ep_int_desc;
break;
case USB_SPEED_HIGH:
epout_desc = &hs_epout_desc;
epin_desc = &hs_epin_desc;
epin_fback_desc = &hs_epin_fback_desc;
+ ep_int_desc = &hs_ep_int_desc;
break;
default:
epout_desc = &ss_epout_desc;
@@ -626,6 +730,7 @@ static void setup_headers(struct f_uac2_opts *opts,
epout_desc_comp = &ss_epout_desc_comp;
epin_desc_comp = &ss_epin_desc_comp;
epin_fback_desc = &ss_epin_fback_desc;
+ ep_int_desc = &ss_ep_int_desc;
}
i = 0;
@@ -637,13 +742,27 @@ static void setup_headers(struct f_uac2_opts *opts,
if (EPOUT_EN(opts)) {
headers[i++] = USBDHDR(&out_clk_src_desc);
headers[i++] = USBDHDR(&usb_out_it_desc);
- }
+
+ if (FUOUT_EN(opts))
+ headers[i++] = USBDHDR(out_feature_unit_desc);
+ }
+
if (EPIN_EN(opts)) {
headers[i++] = USBDHDR(&io_in_it_desc);
+
+ if (FUIN_EN(opts))
+ headers[i++] = USBDHDR(in_feature_unit_desc);
+
headers[i++] = USBDHDR(&usb_in_ot_desc);
}
- if (EPOUT_EN(opts)) {
+
+ if (EPOUT_EN(opts))
headers[i++] = USBDHDR(&io_out_ot_desc);
+
+ if (FUOUT_EN(opts) || FUIN_EN(opts))
+ headers[i++] = USBDHDR(ep_int_desc);
+
+ if (EPOUT_EN(opts)) {
headers[i++] = USBDHDR(&std_as_out_if0_desc);
headers[i++] = USBDHDR(&std_as_out_if1_desc);
headers[i++] = USBDHDR(&as_out_hdr_desc);
@@ -657,6 +776,7 @@ static void setup_headers(struct f_uac2_opts *opts,
if (EPOUT_FBACK_IN_EN(opts))
headers[i++] = USBDHDR(epin_fback_desc);
}
+
if (EPIN_EN(opts)) {
headers[i++] = USBDHDR(&std_as_in_if0_desc);
headers[i++] = USBDHDR(&std_as_in_if1_desc);
@@ -684,17 +804,35 @@ static void setup_descriptor(struct f_uac2_opts *opts)
io_out_ot_desc.bTerminalID = i++;
if (EPIN_EN(opts))
usb_in_ot_desc.bTerminalID = i++;
+ if (FUOUT_EN(opts))
+ out_feature_unit_desc->bUnitID = i++;
+ if (FUIN_EN(opts))
+ in_feature_unit_desc->bUnitID = i++;
if (EPOUT_EN(opts))
out_clk_src_desc.bClockID = i++;
if (EPIN_EN(opts))
in_clk_src_desc.bClockID = i++;
usb_out_it_desc.bCSourceID = out_clk_src_desc.bClockID;
- usb_in_ot_desc.bSourceID = io_in_it_desc.bTerminalID;
+
+ if (FUIN_EN(opts)) {
+ usb_in_ot_desc.bSourceID = in_feature_unit_desc->bUnitID;
+ in_feature_unit_desc->bSourceID = io_in_it_desc.bTerminalID;
+ } else {
+ usb_in_ot_desc.bSourceID = io_in_it_desc.bTerminalID;
+ }
+
usb_in_ot_desc.bCSourceID = in_clk_src_desc.bClockID;
io_in_it_desc.bCSourceID = in_clk_src_desc.bClockID;
io_out_ot_desc.bCSourceID = out_clk_src_desc.bClockID;
- io_out_ot_desc.bSourceID = usb_out_it_desc.bTerminalID;
+
+ if (FUOUT_EN(opts)) {
+ io_out_ot_desc.bSourceID = out_feature_unit_desc->bUnitID;
+ out_feature_unit_desc->bSourceID = usb_out_it_desc.bTerminalID;
+ } else {
+ io_out_ot_desc.bSourceID = usb_out_it_desc.bTerminalID;
+ }
+
as_out_hdr_desc.bTerminalLink = usb_out_it_desc.bTerminalID;
as_in_hdr_desc.bTerminalLink = usb_in_ot_desc.bTerminalID;
@@ -706,6 +844,10 @@ static void setup_descriptor(struct f_uac2_opts *opts)
len += sizeof(in_clk_src_desc);
len += sizeof(usb_in_ot_desc);
+
+ if (FUIN_EN(opts))
+ len += in_feature_unit_desc->bLength;
+
len += sizeof(io_in_it_desc);
ac_hdr_desc.wTotalLength = cpu_to_le16(len);
iad_desc.bInterfaceCount++;
@@ -715,6 +857,10 @@ static void setup_descriptor(struct f_uac2_opts *opts)
len += sizeof(out_clk_src_desc);
len += sizeof(usb_out_it_desc);
+
+ if (FUOUT_EN(opts))
+ len += out_feature_unit_desc->bLength;
+
len += sizeof(io_out_ot_desc);
ac_hdr_desc.wTotalLength = cpu_to_le16(len);
iad_desc.bInterfaceCount++;
@@ -752,6 +898,28 @@ static int afunc_validate_opts(struct g_audio *agdev, struct device *dev)
return -EINVAL;
}
+ if (opts->p_volume_max <= opts->p_volume_min) {
+ dev_err(dev, "Error: incorrect playback volume max/min\n");
+ return -EINVAL;
+ } else if (opts->c_volume_max <= opts->c_volume_min) {
+ dev_err(dev, "Error: incorrect capture volume max/min\n");
+ return -EINVAL;
+ } else if (opts->p_volume_res <= 0) {
+ dev_err(dev, "Error: negative/zero playback volume resolution\n");
+ return -EINVAL;
+ } else if (opts->c_volume_res <= 0) {
+ dev_err(dev, "Error: negative/zero capture volume resolution\n");
+ return -EINVAL;
+ }
+
+ if ((opts->p_volume_max - opts->p_volume_min) % opts->p_volume_res) {
+ dev_err(dev, "Error: incorrect playback volume resolution\n");
+ return -EINVAL;
+ } else if ((opts->c_volume_max - opts->c_volume_min) % opts->c_volume_res) {
+ dev_err(dev, "Error: incorrect capture volume resolution\n");
+ return -EINVAL;
+ }
+
return 0;
}
@@ -774,6 +942,20 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
us = usb_gstrings_attach(cdev, fn_strings, ARRAY_SIZE(strings_fn));
if (IS_ERR(us))
return PTR_ERR(us);
+
+ if (FUOUT_EN(uac2_opts)) {
+ out_feature_unit_desc = build_fu_desc(uac2_opts->c_chmask);
+ if (!out_feature_unit_desc)
+ return -ENOMEM;
+ }
+ if (FUIN_EN(uac2_opts)) {
+ in_feature_unit_desc = build_fu_desc(uac2_opts->p_chmask);
+ if (!in_feature_unit_desc) {
+ ret = -ENOMEM;
+ goto err_free_fu;
+ }
+ }
+
iad_desc.iFunction = us[STR_ASSOC].id;
std_ac_if_desc.iInterface = us[STR_IF_CTRL].id;
in_clk_src_desc.iClockSource = us[STR_CLKSRC_IN].id;
@@ -787,6 +969,21 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
std_as_in_if0_desc.iInterface = us[STR_AS_IN_ALT0].id;
std_as_in_if1_desc.iInterface = us[STR_AS_IN_ALT1].id;
+ if (FUOUT_EN(uac2_opts)) {
+ u8 *i_feature = (u8 *)out_feature_unit_desc;
+
+ i_feature = (u8 *)out_feature_unit_desc +
+ out_feature_unit_desc->bLength - 1;
+ *i_feature = us[STR_FU_OUT].id;
+ }
+ if (FUIN_EN(uac2_opts)) {
+ u8 *i_feature = (u8 *)in_feature_unit_desc;
+
+ i_feature = (u8 *)in_feature_unit_desc +
+ in_feature_unit_desc->bLength - 1;
+ *i_feature = us[STR_FU_IN].id;
+ }
+
/* Initialize the configurable parameters */
usb_out_it_desc.bNrChannels = num_channels(uac2_opts->c_chmask);
@@ -801,6 +998,26 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
as_out_fmt1_desc.bBitResolution = uac2_opts->c_ssize * 8;
as_in_fmt1_desc.bSubslotSize = uac2_opts->p_ssize;
as_in_fmt1_desc.bBitResolution = uac2_opts->p_ssize * 8;
+ if (FUOUT_EN(uac2_opts)) {
+ __le32 *bma = (__le32 *)&out_feature_unit_desc->bmaControls[0];
+ u32 control = 0;
+
+ if (uac2_opts->c_mute_present)
+ control |= CONTROL_RDWR << FU_MUTE_CTRL;
+ if (uac2_opts->c_volume_present)
+ control |= CONTROL_RDWR << FU_VOL_CTRL;
+ *bma = cpu_to_le32(control);
+ }
+ if (FUIN_EN(uac2_opts)) {
+ __le32 *bma = (__le32 *)&in_feature_unit_desc->bmaControls[0];
+ u32 control = 0;
+
+ if (uac2_opts->p_mute_present)
+ control |= CONTROL_RDWR << FU_MUTE_CTRL;
+ if (uac2_opts->p_volume_present)
+ control |= CONTROL_RDWR << FU_VOL_CTRL;
+ *bma = cpu_to_le32(control);
+ }
snprintf(clksrc_in, sizeof(clksrc_in), "%uHz", uac2_opts->p_srate);
snprintf(clksrc_out, sizeof(clksrc_out), "%uHz", uac2_opts->c_srate);
@@ -808,7 +1025,7 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
ret = usb_interface_id(cfg, fn);
if (ret < 0) {
dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
- return ret;
+ goto err_free_fu;
}
iad_desc.bFirstInterface = ret;
@@ -820,7 +1037,7 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
ret = usb_interface_id(cfg, fn);
if (ret < 0) {
dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
- return ret;
+ goto err_free_fu;
}
std_as_out_if0_desc.bInterfaceNumber = ret;
std_as_out_if1_desc.bInterfaceNumber = ret;
@@ -849,7 +1066,7 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
ret = usb_interface_id(cfg, fn);
if (ret < 0) {
dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
- return ret;
+ goto err_free_fu;
}
std_as_in_if0_desc.bInterfaceNumber = ret;
std_as_in_if1_desc.bInterfaceNumber = ret;
@@ -857,6 +1074,17 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
uac2->as_in_alt = 0;
}
+ if (FUOUT_EN(uac2_opts) || FUIN_EN(uac2_opts)) {
+ uac2->int_ep = usb_ep_autoconfig(gadget, &fs_ep_int_desc);
+ if (!uac2->int_ep) {
+ dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
+ ret = -ENODEV;
+ goto err_free_fu;
+ }
+
+ std_ac_if_desc.bNumEndpoints = 1;
+ }
+
/* Calculate wMaxPacketSize according to audio bandwidth */
ret = set_ep_max_packet_size(uac2_opts, &fs_epin_desc, USB_SPEED_FULL,
true);
@@ -904,7 +1132,8 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
agdev->out_ep = usb_ep_autoconfig(gadget, &fs_epout_desc);
if (!agdev->out_ep) {
dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
- return -ENODEV;
+ ret = -ENODEV;
+ goto err_free_fu;
}
if (EPOUT_FBACK_IN_EN(uac2_opts)) {
agdev->in_ep_fback = usb_ep_autoconfig(gadget,
@@ -912,7 +1141,8 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
if (!agdev->in_ep_fback) {
dev_err(dev, "%s:%d Error!\n",
__func__, __LINE__);
- return -ENODEV;
+ ret = -ENODEV;
+ goto err_free_fu;
}
}
}
@@ -921,7 +1151,8 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
agdev->in_ep = usb_ep_autoconfig(gadget, &fs_epin_desc);
if (!agdev->in_ep) {
dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
- return -ENODEV;
+ ret = -ENODEV;
+ goto err_free_fu;
}
}
@@ -937,38 +1168,137 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
agdev->out_ep_maxpsize = max_t(u16, agdev->out_ep_maxpsize,
le16_to_cpu(ss_epout_desc.wMaxPacketSize));
+ // HS and SS endpoint addresses are copied from autoconfigured FS descriptors
+ hs_ep_int_desc.bEndpointAddress = fs_ep_int_desc.bEndpointAddress;
hs_epout_desc.bEndpointAddress = fs_epout_desc.bEndpointAddress;
hs_epin_fback_desc.bEndpointAddress = fs_epin_fback_desc.bEndpointAddress;
hs_epin_desc.bEndpointAddress = fs_epin_desc.bEndpointAddress;
ss_epout_desc.bEndpointAddress = fs_epout_desc.bEndpointAddress;
ss_epin_fback_desc.bEndpointAddress = fs_epin_fback_desc.bEndpointAddress;
ss_epin_desc.bEndpointAddress = fs_epin_desc.bEndpointAddress;
+ ss_ep_int_desc.bEndpointAddress = fs_ep_int_desc.bEndpointAddress;
setup_descriptor(uac2_opts);
ret = usb_assign_descriptors(fn, fs_audio_desc, hs_audio_desc, ss_audio_desc,
ss_audio_desc);
if (ret)
- return ret;
+ goto err_free_fu;
agdev->gadget = gadget;
agdev->params.p_chmask = uac2_opts->p_chmask;
agdev->params.p_srate = uac2_opts->p_srate;
agdev->params.p_ssize = uac2_opts->p_ssize;
+ if (FUIN_EN(uac2_opts)) {
+ agdev->params.p_fu.id = USB_IN_FU_ID;
+ agdev->params.p_fu.mute_present = uac2_opts->p_mute_present;
+ agdev->params.p_fu.volume_present = uac2_opts->p_volume_present;
+ agdev->params.p_fu.volume_min = uac2_opts->p_volume_min;
+ agdev->params.p_fu.volume_max = uac2_opts->p_volume_max;
+ agdev->params.p_fu.volume_res = uac2_opts->p_volume_res;
+ }
agdev->params.c_chmask = uac2_opts->c_chmask;
agdev->params.c_srate = uac2_opts->c_srate;
agdev->params.c_ssize = uac2_opts->c_ssize;
+ if (FUOUT_EN(uac2_opts)) {
+ agdev->params.c_fu.id = USB_OUT_FU_ID;
+ agdev->params.c_fu.mute_present = uac2_opts->c_mute_present;
+ agdev->params.c_fu.volume_present = uac2_opts->c_volume_present;
+ agdev->params.c_fu.volume_min = uac2_opts->c_volume_min;
+ agdev->params.c_fu.volume_max = uac2_opts->c_volume_max;
+ agdev->params.c_fu.volume_res = uac2_opts->c_volume_res;
+ }
agdev->params.req_number = uac2_opts->req_number;
agdev->params.fb_max = uac2_opts->fb_max;
+
+ if (FUOUT_EN(uac2_opts) || FUIN_EN(uac2_opts))
+ agdev->notify = afunc_notify;
+
ret = g_audio_setup(agdev, "UAC2 PCM", "UAC2_Gadget");
if (ret)
goto err_free_descs;
+
return 0;
err_free_descs:
usb_free_all_descriptors(fn);
agdev->gadget = NULL;
+err_free_fu:
+ kfree(out_feature_unit_desc);
+ out_feature_unit_desc = NULL;
+ kfree(in_feature_unit_desc);
+ in_feature_unit_desc = NULL;
+ return ret;
+}
+
+static void
+afunc_notify_complete(struct usb_ep *_ep, struct usb_request *req)
+{
+ struct g_audio *agdev = req->context;
+ struct f_uac2 *uac2 = func_to_uac2(&agdev->func);
+
+ atomic_dec(&uac2->int_count);
+ kfree(req->buf);
+ usb_ep_free_request(_ep, req);
+}
+
+static int
+afunc_notify(struct g_audio *agdev, int unit_id, int cs)
+{
+ struct f_uac2 *uac2 = func_to_uac2(&agdev->func);
+ struct usb_request *req;
+ struct uac2_interrupt_data_msg *msg;
+ u16 w_index, w_value;
+ int ret;
+
+ if (!uac2->int_ep->enabled)
+ return 0;
+
+ if (atomic_inc_return(&uac2->int_count) > UAC2_DEF_INT_REQ_NUM) {
+ atomic_dec(&uac2->int_count);
+ return 0;
+ }
+
+ req = usb_ep_alloc_request(uac2->int_ep, GFP_ATOMIC);
+ if (req == NULL) {
+ ret = -ENOMEM;
+ goto err_dec_int_count;
+ }
+
+ msg = kzalloc(sizeof(*msg), GFP_ATOMIC);
+ if (msg == NULL) {
+ ret = -ENOMEM;
+ goto err_free_request;
+ }
+
+ w_index = unit_id << 8 | uac2->ac_intf;
+ w_value = cs << 8;
+
+ msg->bInfo = 0; /* Non-vendor, interface interrupt */
+ msg->bAttribute = UAC2_CS_CUR;
+ msg->wIndex = cpu_to_le16(w_index);
+ msg->wValue = cpu_to_le16(w_value);
+
+ req->length = sizeof(*msg);
+ req->buf = msg;
+ req->context = agdev;
+ req->complete = afunc_notify_complete;
+
+ ret = usb_ep_queue(uac2->int_ep, req, GFP_ATOMIC);
+
+ if (ret)
+ goto err_free_msg;
+
+ return 0;
+
+err_free_msg:
+ kfree(msg);
+err_free_request:
+ usb_ep_free_request(uac2->int_ep, req);
+err_dec_int_count:
+ atomic_dec(&uac2->int_count);
+
return ret;
}
@@ -977,6 +1307,7 @@ afunc_set_alt(struct usb_function *fn, unsigned intf, unsigned alt)
{
struct usb_composite_dev *cdev = fn->config->cdev;
struct f_uac2 *uac2 = func_to_uac2(fn);
+ struct g_audio *agdev = func_to_g_audio(fn);
struct usb_gadget *gadget = cdev->gadget;
struct device *dev = &gadget->dev;
int ret = 0;
@@ -993,6 +1324,14 @@ afunc_set_alt(struct usb_function *fn, unsigned intf, unsigned alt)
dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
return -EINVAL;
}
+
+ /* restart interrupt endpoint */
+ if (uac2->int_ep) {
+ usb_ep_disable(uac2->int_ep);
+ config_ep_by_speed(gadget, &agdev->func, uac2->int_ep);
+ usb_ep_enable(uac2->int_ep);
+ }
+
return 0;
}
@@ -1047,6 +1386,8 @@ afunc_disable(struct usb_function *fn)
uac2->as_out_alt = 0;
u_audio_stop_capture(&uac2->g_audio);
u_audio_stop_playback(&uac2->g_audio);
+ if (uac2->int_ep)
+ usb_ep_disable(uac2->int_ep);
}
static int
@@ -1054,7 +1395,7 @@ in_rq_cur(struct usb_function *fn, const struct usb_ctrlrequest *cr)
{
struct usb_request *req = fn->config->cdev->req;
struct g_audio *agdev = func_to_g_audio(fn);
- struct f_uac2_opts *opts;
+ struct f_uac2_opts *opts = g_audio_to_uac2_opts(agdev);
u16 w_length = le16_to_cpu(cr->wLength);
u16 w_index = le16_to_cpu(cr->wIndex);
u16 w_value = le16_to_cpu(cr->wValue);
@@ -1063,28 +1404,64 @@ in_rq_cur(struct usb_function *fn, const struct usb_ctrlrequest *cr)
int value = -EOPNOTSUPP;
int p_srate, c_srate;
- opts = g_audio_to_uac2_opts(agdev);
p_srate = opts->p_srate;
c_srate = opts->c_srate;
- if (control_selector == UAC2_CS_CONTROL_SAM_FREQ) {
- struct cntrl_cur_lay3 c;
- memset(&c, 0, sizeof(struct cntrl_cur_lay3));
+ if ((entity_id == USB_IN_CLK_ID) || (entity_id == USB_OUT_CLK_ID)) {
+ if (control_selector == UAC2_CS_CONTROL_SAM_FREQ) {
+ struct cntrl_cur_lay3 c;
+
+ memset(&c, 0, sizeof(struct cntrl_cur_lay3));
+
+ if (entity_id == USB_IN_CLK_ID)
+ c.dCUR = cpu_to_le32(p_srate);
+ else if (entity_id == USB_OUT_CLK_ID)
+ c.dCUR = cpu_to_le32(c_srate);
+
+ value = min_t(unsigned int, w_length, sizeof(c));
+ memcpy(req->buf, &c, value);
+ } else if (control_selector == UAC2_CS_CONTROL_CLOCK_VALID) {
+ *(u8 *)req->buf = 1;
+ value = min_t(unsigned int, w_length, 1);
+ } else {
+ dev_err(&agdev->gadget->dev,
+ "%s:%d control_selector=%d TODO!\n",
+ __func__, __LINE__, control_selector);
+ }
+ } else if ((FUIN_EN(opts) && (entity_id == USB_IN_FU_ID)) ||
+ (FUOUT_EN(opts) && (entity_id == USB_OUT_FU_ID))) {
+ unsigned int is_playback = 0;
- if (entity_id == USB_IN_CLK_ID)
- c.dCUR = cpu_to_le32(p_srate);
- else if (entity_id == USB_OUT_CLK_ID)
- c.dCUR = cpu_to_le32(c_srate);
+ if (FUIN_EN(opts) && (entity_id == USB_IN_FU_ID))
+ is_playback = 1;
- value = min_t(unsigned, w_length, sizeof c);
- memcpy(req->buf, &c, value);
- } else if (control_selector == UAC2_CS_CONTROL_CLOCK_VALID) {
- *(u8 *)req->buf = 1;
- value = min_t(unsigned, w_length, 1);
+ if (control_selector == UAC_FU_MUTE) {
+ unsigned int mute;
+
+ u_audio_get_mute(agdev, is_playback, &mute);
+
+ *(u8 *)req->buf = mute;
+ value = min_t(unsigned int, w_length, 1);
+ } else if (control_selector == UAC_FU_VOLUME) {
+ struct cntrl_cur_lay2 c;
+ s16 volume;
+
+ memset(&c, 0, sizeof(struct cntrl_cur_lay2));
+
+ u_audio_get_volume(agdev, is_playback, &volume);
+ c.wCUR = cpu_to_le16(volume);
+
+ value = min_t(unsigned int, w_length, sizeof(c));
+ memcpy(req->buf, &c, value);
+ } else {
+ dev_err(&agdev->gadget->dev,
+ "%s:%d control_selector=%d TODO!\n",
+ __func__, __LINE__, control_selector);
+ }
} else {
dev_err(&agdev->gadget->dev,
- "%s:%d control_selector=%d TODO!\n",
- __func__, __LINE__, control_selector);
+ "%s:%d entity_id=%d control_selector=%d TODO!\n",
+ __func__, __LINE__, entity_id, control_selector);
}
return value;
@@ -1095,38 +1472,77 @@ in_rq_range(struct usb_function *fn, const struct usb_ctrlrequest *cr)
{
struct usb_request *req = fn->config->cdev->req;
struct g_audio *agdev = func_to_g_audio(fn);
- struct f_uac2_opts *opts;
+ struct f_uac2_opts *opts = g_audio_to_uac2_opts(agdev);
u16 w_length = le16_to_cpu(cr->wLength);
u16 w_index = le16_to_cpu(cr->wIndex);
u16 w_value = le16_to_cpu(cr->wValue);
u8 entity_id = (w_index >> 8) & 0xff;
u8 control_selector = w_value >> 8;
- struct cntrl_range_lay3 r;
int value = -EOPNOTSUPP;
int p_srate, c_srate;
- opts = g_audio_to_uac2_opts(agdev);
p_srate = opts->p_srate;
c_srate = opts->c_srate;
- if (control_selector == UAC2_CS_CONTROL_SAM_FREQ) {
- if (entity_id == USB_IN_CLK_ID)
- r.dMIN = cpu_to_le32(p_srate);
- else if (entity_id == USB_OUT_CLK_ID)
- r.dMIN = cpu_to_le32(c_srate);
- else
- return -EOPNOTSUPP;
+ if ((entity_id == USB_IN_CLK_ID) || (entity_id == USB_OUT_CLK_ID)) {
+ if (control_selector == UAC2_CS_CONTROL_SAM_FREQ) {
+ struct cntrl_range_lay3 r;
+
+ if (entity_id == USB_IN_CLK_ID)
+ r.dMIN = cpu_to_le32(p_srate);
+ else if (entity_id == USB_OUT_CLK_ID)
+ r.dMIN = cpu_to_le32(c_srate);
+ else
+ return -EOPNOTSUPP;
- r.dMAX = r.dMIN;
- r.dRES = 0;
- r.wNumSubRanges = cpu_to_le16(1);
+ r.dMAX = r.dMIN;
+ r.dRES = 0;
+ r.wNumSubRanges = cpu_to_le16(1);
- value = min_t(unsigned, w_length, sizeof r);
- memcpy(req->buf, &r, value);
+ value = min_t(unsigned int, w_length, sizeof(r));
+ memcpy(req->buf, &r, value);
+ } else {
+ dev_err(&agdev->gadget->dev,
+ "%s:%d control_selector=%d TODO!\n",
+ __func__, __LINE__, control_selector);
+ }
+ } else if ((FUIN_EN(opts) && (entity_id == USB_IN_FU_ID)) ||
+ (FUOUT_EN(opts) && (entity_id == USB_OUT_FU_ID))) {
+ unsigned int is_playback = 0;
+
+ if (FUIN_EN(opts) && (entity_id == USB_IN_FU_ID))
+ is_playback = 1;
+
+ if (control_selector == UAC_FU_VOLUME) {
+ struct cntrl_range_lay2 r;
+ s16 max_db, min_db, res_db;
+
+ if (is_playback) {
+ max_db = opts->p_volume_max;
+ min_db = opts->p_volume_min;
+ res_db = opts->p_volume_res;
+ } else {
+ max_db = opts->c_volume_max;
+ min_db = opts->c_volume_min;
+ res_db = opts->c_volume_res;
+ }
+
+ r.wMAX = cpu_to_le16(max_db);
+ r.wMIN = cpu_to_le16(min_db);
+ r.wRES = cpu_to_le16(res_db);
+ r.wNumSubRanges = cpu_to_le16(1);
+
+ value = min_t(unsigned int, w_length, sizeof(r));
+ memcpy(req->buf, &r, value);
+ } else {
+ dev_err(&agdev->gadget->dev,
+ "%s:%d control_selector=%d TODO!\n",
+ __func__, __LINE__, control_selector);
+ }
} else {
dev_err(&agdev->gadget->dev,
- "%s:%d control_selector=%d TODO!\n",
- __func__, __LINE__, control_selector);
+ "%s:%d entity_id=%d control_selector=%d TODO!\n",
+ __func__, __LINE__, entity_id, control_selector);
}
return value;
@@ -1143,16 +1559,82 @@ ac_rq_in(struct usb_function *fn, const struct usb_ctrlrequest *cr)
return -EOPNOTSUPP;
}
+static void
+out_rq_cur_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ struct g_audio *agdev = req->context;
+ struct usb_composite_dev *cdev = agdev->func.config->cdev;
+ struct f_uac2_opts *opts = g_audio_to_uac2_opts(agdev);
+ struct f_uac2 *uac2 = func_to_uac2(&agdev->func);
+ struct usb_ctrlrequest *cr = &uac2->setup_cr;
+ u16 w_index = le16_to_cpu(cr->wIndex);
+ u16 w_value = le16_to_cpu(cr->wValue);
+ u8 entity_id = (w_index >> 8) & 0xff;
+ u8 control_selector = w_value >> 8;
+
+ if (req->status != 0) {
+ dev_dbg(&cdev->gadget->dev, "completion err %d\n", req->status);
+ return;
+ }
+
+ if ((FUIN_EN(opts) && (entity_id == USB_IN_FU_ID)) ||
+ (FUOUT_EN(opts) && (entity_id == USB_OUT_FU_ID))) {
+ unsigned int is_playback = 0;
+
+ if (FUIN_EN(opts) && (entity_id == USB_IN_FU_ID))
+ is_playback = 1;
+
+ if (control_selector == UAC_FU_MUTE) {
+ u8 mute = *(u8 *)req->buf;
+
+ u_audio_set_mute(agdev, is_playback, mute);
+
+ return;
+ } else if (control_selector == UAC_FU_VOLUME) {
+ struct cntrl_cur_lay2 *c = req->buf;
+ s16 volume;
+
+ volume = le16_to_cpu(c->wCUR);
+ u_audio_set_volume(agdev, is_playback, volume);
+
+ return;
+ } else {
+ dev_err(&agdev->gadget->dev,
+ "%s:%d control_selector=%d TODO!\n",
+ __func__, __LINE__, control_selector);
+ usb_ep_set_halt(ep);
+ }
+ }
+}
+
static int
out_rq_cur(struct usb_function *fn, const struct usb_ctrlrequest *cr)
{
+ struct usb_request *req = fn->config->cdev->req;
+ struct g_audio *agdev = func_to_g_audio(fn);
+ struct f_uac2_opts *opts = g_audio_to_uac2_opts(agdev);
+ struct f_uac2 *uac2 = func_to_uac2(fn);
u16 w_length = le16_to_cpu(cr->wLength);
+ u16 w_index = le16_to_cpu(cr->wIndex);
u16 w_value = le16_to_cpu(cr->wValue);
+ u8 entity_id = (w_index >> 8) & 0xff;
u8 control_selector = w_value >> 8;
- if (control_selector == UAC2_CS_CONTROL_SAM_FREQ)
- return w_length;
+ if ((entity_id == USB_IN_CLK_ID) || (entity_id == USB_OUT_CLK_ID)) {
+ if (control_selector == UAC2_CS_CONTROL_SAM_FREQ)
+ return w_length;
+ } else if ((FUIN_EN(opts) && (entity_id == USB_IN_FU_ID)) ||
+ (FUOUT_EN(opts) && (entity_id == USB_OUT_FU_ID))) {
+ memcpy(&uac2->setup_cr, cr, sizeof(*cr));
+ req->context = agdev;
+ req->complete = out_rq_cur_complete;
+ return w_length;
+ } else {
+ dev_err(&agdev->gadget->dev,
+ "%s:%d entity_id=%d control_selector=%d TODO!\n",
+ __func__, __LINE__, entity_id, control_selector);
+ }
return -EOPNOTSUPP;
}
@@ -1228,7 +1710,15 @@ static struct configfs_item_operations f_uac2_item_ops = {
.release = f_uac2_attr_release,
};
-#define UAC2_ATTRIBUTE(name) \
+#define uac2_kstrtou32 kstrtou32
+#define uac2_kstrtos16 kstrtos16
+#define uac2_kstrtobool(s, base, res) kstrtobool((s), (res))
+
+static const char *u32_fmt = "%u\n";
+static const char *s16_fmt = "%hd\n";
+static const char *bool_fmt = "%u\n";
+
+#define UAC2_ATTRIBUTE(type, name) \
static ssize_t f_uac2_opts_##name##_show(struct config_item *item, \
char *page) \
{ \
@@ -1236,7 +1726,7 @@ static ssize_t f_uac2_opts_##name##_show(struct config_item *item, \
int result; \
\
mutex_lock(&opts->lock); \
- result = sprintf(page, "%u\n", opts->name); \
+ result = sprintf(page, type##_fmt, opts->name); \
mutex_unlock(&opts->lock); \
\
return result; \
@@ -1247,7 +1737,7 @@ static ssize_t f_uac2_opts_##name##_store(struct config_item *item, \
{ \
struct f_uac2_opts *opts = to_f_uac2_opts(item); \
int ret; \
- u32 num; \
+ type num; \
\
mutex_lock(&opts->lock); \
if (opts->refcnt) { \
@@ -1255,7 +1745,7 @@ static ssize_t f_uac2_opts_##name##_store(struct config_item *item, \
goto end; \
} \
\
- ret = kstrtou32(page, 0, &num); \
+ ret = uac2_kstrto##type(page, 0, &num); \
if (ret) \
goto end; \
\
@@ -1325,15 +1815,27 @@ end: \
\
CONFIGFS_ATTR(f_uac2_opts_, name)
-UAC2_ATTRIBUTE(p_chmask);
-UAC2_ATTRIBUTE(p_srate);
-UAC2_ATTRIBUTE(p_ssize);
-UAC2_ATTRIBUTE(c_chmask);
-UAC2_ATTRIBUTE(c_srate);
+UAC2_ATTRIBUTE(u32, p_chmask);
+UAC2_ATTRIBUTE(u32, p_srate);
+UAC2_ATTRIBUTE(u32, p_ssize);
+UAC2_ATTRIBUTE(u32, c_chmask);
+UAC2_ATTRIBUTE(u32, c_srate);
UAC2_ATTRIBUTE_SYNC(c_sync);
-UAC2_ATTRIBUTE(c_ssize);
-UAC2_ATTRIBUTE(req_number);
-UAC2_ATTRIBUTE(fb_max);
+UAC2_ATTRIBUTE(u32, c_ssize);
+UAC2_ATTRIBUTE(u32, req_number);
+
+UAC2_ATTRIBUTE(bool, p_mute_present);
+UAC2_ATTRIBUTE(bool, p_volume_present);
+UAC2_ATTRIBUTE(s16, p_volume_min);
+UAC2_ATTRIBUTE(s16, p_volume_max);
+UAC2_ATTRIBUTE(s16, p_volume_res);
+
+UAC2_ATTRIBUTE(bool, c_mute_present);
+UAC2_ATTRIBUTE(bool, c_volume_present);
+UAC2_ATTRIBUTE(s16, c_volume_min);
+UAC2_ATTRIBUTE(s16, c_volume_max);
+UAC2_ATTRIBUTE(s16, c_volume_res);
+UAC2_ATTRIBUTE(u32, fb_max);
static struct configfs_attribute *f_uac2_attrs[] = {
&f_uac2_opts_attr_p_chmask,
@@ -1345,6 +1847,19 @@ static struct configfs_attribute *f_uac2_attrs[] = {
&f_uac2_opts_attr_c_sync,
&f_uac2_opts_attr_req_number,
&f_uac2_opts_attr_fb_max,
+
+ &f_uac2_opts_attr_p_mute_present,
+ &f_uac2_opts_attr_p_volume_present,
+ &f_uac2_opts_attr_p_volume_min,
+ &f_uac2_opts_attr_p_volume_max,
+ &f_uac2_opts_attr_p_volume_res,
+
+ &f_uac2_opts_attr_c_mute_present,
+ &f_uac2_opts_attr_c_volume_present,
+ &f_uac2_opts_attr_c_volume_min,
+ &f_uac2_opts_attr_c_volume_max,
+ &f_uac2_opts_attr_c_volume_res,
+
NULL,
};
@@ -1383,6 +1898,19 @@ static struct usb_function_instance *afunc_alloc_inst(void)
opts->c_srate = UAC2_DEF_CSRATE;
opts->c_ssize = UAC2_DEF_CSSIZE;
opts->c_sync = UAC2_DEF_CSYNC;
+
+ opts->p_mute_present = UAC2_DEF_MUTE_PRESENT;
+ opts->p_volume_present = UAC2_DEF_VOLUME_PRESENT;
+ opts->p_volume_min = UAC2_DEF_MIN_DB;
+ opts->p_volume_max = UAC2_DEF_MAX_DB;
+ opts->p_volume_res = UAC2_DEF_RES_DB;
+
+ opts->c_mute_present = UAC2_DEF_MUTE_PRESENT;
+ opts->c_volume_present = UAC2_DEF_VOLUME_PRESENT;
+ opts->c_volume_min = UAC2_DEF_MIN_DB;
+ opts->c_volume_max = UAC2_DEF_MAX_DB;
+ opts->c_volume_res = UAC2_DEF_RES_DB;
+
opts->req_number = UAC2_DEF_REQ_NUM;
opts->fb_max = UAC2_DEF_FB_MAX;
return &opts->func_inst;
@@ -1409,6 +1937,11 @@ static void afunc_unbind(struct usb_configuration *c, struct usb_function *f)
usb_free_all_descriptors(f);
agdev->gadget = NULL;
+
+ kfree(out_feature_unit_desc);
+ out_feature_unit_desc = NULL;
+ kfree(in_feature_unit_desc);
+ in_feature_unit_desc = NULL;
}
static struct usb_function *afunc_alloc(struct usb_function_instance *fi)
@@ -1441,3 +1974,4 @@ DECLARE_USB_FUNCTION_INIT(uac2, afunc_alloc_inst, afunc_alloc);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Yadwinder Singh");
MODULE_AUTHOR("Jaswinder Singh");
+MODULE_AUTHOR("Ruslan Bilovol");
diff --git a/drivers/usb/gadget/function/u_audio.c b/drivers/usb/gadget/function/u_audio.c
index 018dd0978995..f6b5b9547236 100644
--- a/drivers/usb/gadget/function/u_audio.c
+++ b/drivers/usb/gadget/function/u_audio.c
@@ -12,11 +12,14 @@
* Jaswinder Singh (jaswinder.singh@linaro.org)
*/
+#include <linux/kernel.h>
#include <linux/module.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/control.h>
+#include <sound/tlv.h>
+#include <linux/usb/audio.h>
#include "u_audio.h"
@@ -24,6 +27,12 @@
#define PRD_SIZE_MAX PAGE_SIZE
#define MIN_PERIODS 4
+enum {
+ UAC_FBACK_CTRL,
+ UAC_MUTE_CTRL,
+ UAC_VOLUME_CTRL,
+};
+
/* Runtime data params for one stream */
struct uac_rtd_params {
struct snd_uac_chip *uac; /* parent chip */
@@ -43,6 +52,17 @@ struct uac_rtd_params {
struct usb_request *req_fback; /* Feedback endpoint request */
bool fb_ep_enabled; /* if the ep is enabled */
+
+ /* Volume/Mute controls and their state */
+ int fu_id; /* Feature Unit ID */
+ struct snd_kcontrol *snd_kctl_volume;
+ struct snd_kcontrol *snd_kctl_mute;
+ s16 volume_min, volume_max, volume_res;
+ s16 volume;
+ int mute;
+
+ spinlock_t lock; /* lock for control transfers */
+
};
struct snd_uac_chip {
@@ -597,6 +617,103 @@ void u_audio_stop_playback(struct g_audio *audio_dev)
}
EXPORT_SYMBOL_GPL(u_audio_stop_playback);
+int u_audio_get_volume(struct g_audio *audio_dev, int playback, s16 *val)
+{
+ struct snd_uac_chip *uac = audio_dev->uac;
+ struct uac_rtd_params *prm;
+ unsigned long flags;
+
+ if (playback)
+ prm = &uac->p_prm;
+ else
+ prm = &uac->c_prm;
+
+ spin_lock_irqsave(&prm->lock, flags);
+ *val = prm->volume;
+ spin_unlock_irqrestore(&prm->lock, flags);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(u_audio_get_volume);
+
+int u_audio_set_volume(struct g_audio *audio_dev, int playback, s16 val)
+{
+ struct snd_uac_chip *uac = audio_dev->uac;
+ struct uac_rtd_params *prm;
+ unsigned long flags;
+ int change = 0;
+
+ if (playback)
+ prm = &uac->p_prm;
+ else
+ prm = &uac->c_prm;
+
+ spin_lock_irqsave(&prm->lock, flags);
+ val = clamp(val, prm->volume_min, prm->volume_max);
+ if (prm->volume != val) {
+ prm->volume = val;
+ change = 1;
+ }
+ spin_unlock_irqrestore(&prm->lock, flags);
+
+ if (change)
+ snd_ctl_notify(uac->card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &prm->snd_kctl_volume->id);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(u_audio_set_volume);
+
+int u_audio_get_mute(struct g_audio *audio_dev, int playback, int *val)
+{
+ struct snd_uac_chip *uac = audio_dev->uac;
+ struct uac_rtd_params *prm;
+ unsigned long flags;
+
+ if (playback)
+ prm = &uac->p_prm;
+ else
+ prm = &uac->c_prm;
+
+ spin_lock_irqsave(&prm->lock, flags);
+ *val = prm->mute;
+ spin_unlock_irqrestore(&prm->lock, flags);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(u_audio_get_mute);
+
+int u_audio_set_mute(struct g_audio *audio_dev, int playback, int val)
+{
+ struct snd_uac_chip *uac = audio_dev->uac;
+ struct uac_rtd_params *prm;
+ unsigned long flags;
+ int change = 0;
+ int mute;
+
+ if (playback)
+ prm = &uac->p_prm;
+ else
+ prm = &uac->c_prm;
+
+ mute = val ? 1 : 0;
+
+ spin_lock_irqsave(&prm->lock, flags);
+ if (prm->mute != mute) {
+ prm->mute = mute;
+ change = 1;
+ }
+ spin_unlock_irqrestore(&prm->lock, flags);
+
+ if (change)
+ snd_ctl_notify(uac->card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &prm->snd_kctl_mute->id);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(u_audio_set_mute);
+
+
static int u_audio_pitch_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
@@ -656,14 +773,158 @@ static int u_audio_pitch_put(struct snd_kcontrol *kcontrol,
return change;
}
-static const struct snd_kcontrol_new u_audio_controls[] = {
+static int u_audio_mute_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
{
- .iface = SNDRV_CTL_ELEM_IFACE_PCM,
- .name = "Capture Pitch 1000000",
- .info = u_audio_pitch_info,
- .get = u_audio_pitch_get,
- .put = u_audio_pitch_put,
-},
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ uinfo->value.integer.step = 1;
+
+ return 0;
+}
+
+static int u_audio_mute_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct uac_rtd_params *prm = snd_kcontrol_chip(kcontrol);
+ unsigned long flags;
+
+ spin_lock_irqsave(&prm->lock, flags);
+ ucontrol->value.integer.value[0] = !prm->mute;
+ spin_unlock_irqrestore(&prm->lock, flags);
+
+ return 0;
+}
+
+static int u_audio_mute_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct uac_rtd_params *prm = snd_kcontrol_chip(kcontrol);
+ struct snd_uac_chip *uac = prm->uac;
+ struct g_audio *audio_dev = uac->audio_dev;
+ unsigned int val;
+ unsigned long flags;
+ int change = 0;
+
+ val = !ucontrol->value.integer.value[0];
+
+ spin_lock_irqsave(&prm->lock, flags);
+ if (val != prm->mute) {
+ prm->mute = val;
+ change = 1;
+ }
+ spin_unlock_irqrestore(&prm->lock, flags);
+
+ if (change && audio_dev->notify)
+ audio_dev->notify(audio_dev, prm->fu_id, UAC_FU_MUTE);
+
+ return change;
+}
+
+/*
+ * TLV callback for mixer volume controls
+ */
+static int u_audio_volume_tlv(struct snd_kcontrol *kcontrol, int op_flag,
+ unsigned int size, unsigned int __user *_tlv)
+{
+ struct uac_rtd_params *prm = snd_kcontrol_chip(kcontrol);
+ DECLARE_TLV_DB_MINMAX(scale, 0, 0);
+
+ if (size < sizeof(scale))
+ return -ENOMEM;
+
+ /* UAC volume resolution is 1/256 dB, TLV is 1/100 dB */
+ scale[2] = (prm->volume_min * 100) / 256;
+ scale[3] = (prm->volume_max * 100) / 256;
+ if (copy_to_user(_tlv, scale, sizeof(scale)))
+ return -EFAULT;
+
+ return 0;
+}
+
+static int u_audio_volume_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct uac_rtd_params *prm = snd_kcontrol_chip(kcontrol);
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max =
+ (prm->volume_max - prm->volume_min + prm->volume_res - 1)
+ / prm->volume_res;
+ uinfo->value.integer.step = 1;
+
+ return 0;
+}
+
+static int u_audio_volume_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct uac_rtd_params *prm = snd_kcontrol_chip(kcontrol);
+ unsigned long flags;
+
+ spin_lock_irqsave(&prm->lock, flags);
+ ucontrol->value.integer.value[0] =
+ (prm->volume - prm->volume_min) / prm->volume_res;
+ spin_unlock_irqrestore(&prm->lock, flags);
+
+ return 0;
+}
+
+static int u_audio_volume_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct uac_rtd_params *prm = snd_kcontrol_chip(kcontrol);
+ struct snd_uac_chip *uac = prm->uac;
+ struct g_audio *audio_dev = uac->audio_dev;
+ unsigned int val;
+ s16 volume;
+ unsigned long flags;
+ int change = 0;
+
+ val = ucontrol->value.integer.value[0];
+
+ spin_lock_irqsave(&prm->lock, flags);
+ volume = (val * prm->volume_res) + prm->volume_min;
+ volume = clamp(volume, prm->volume_min, prm->volume_max);
+ if (volume != prm->volume) {
+ prm->volume = volume;
+ change = 1;
+ }
+ spin_unlock_irqrestore(&prm->lock, flags);
+
+ if (change && audio_dev->notify)
+ audio_dev->notify(audio_dev, prm->fu_id, UAC_FU_VOLUME);
+
+ return change;
+}
+
+
+static struct snd_kcontrol_new u_audio_controls[] = {
+ [UAC_FBACK_CTRL] {
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "Capture Pitch 1000000",
+ .info = u_audio_pitch_info,
+ .get = u_audio_pitch_get,
+ .put = u_audio_pitch_put,
+ },
+ [UAC_MUTE_CTRL] {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "", /* will be filled later */
+ .info = u_audio_mute_info,
+ .get = u_audio_mute_get,
+ .put = u_audio_mute_put,
+ },
+ [UAC_VOLUME_CTRL] {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "", /* will be filled later */
+ .info = u_audio_volume_info,
+ .get = u_audio_volume_get,
+ .put = u_audio_volume_put,
+ },
};
int g_audio_setup(struct g_audio *g_audio, const char *pcm_name,
@@ -675,7 +936,7 @@ int g_audio_setup(struct g_audio *g_audio, const char *pcm_name,
struct snd_kcontrol *kctl;
struct uac_params *params;
int p_chmask, c_chmask;
- int err;
+ int i, err;
if (!g_audio)
return -EINVAL;
@@ -693,7 +954,8 @@ int g_audio_setup(struct g_audio *g_audio, const char *pcm_name,
if (c_chmask) {
struct uac_rtd_params *prm = &uac->c_prm;
- uac->c_prm.uac = uac;
+ spin_lock_init(&prm->lock);
+ uac->c_prm.uac = uac;
prm->max_psize = g_audio->out_ep_maxpsize;
prm->reqs = kcalloc(params->req_number,
@@ -716,6 +978,7 @@ int g_audio_setup(struct g_audio *g_audio, const char *pcm_name,
if (p_chmask) {
struct uac_rtd_params *prm = &uac->p_prm;
+ spin_lock_init(&prm->lock);
uac->p_prm.uac = uac;
prm->max_psize = g_audio->in_ep_maxpsize;
@@ -760,10 +1023,18 @@ int g_audio_setup(struct g_audio *g_audio, const char *pcm_name,
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &uac_pcm_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &uac_pcm_ops);
- if (c_chmask && g_audio->in_ep_fback) {
+ /*
+ * Create mixer and controls
+ * Create only if it's required on USB side
+ */
+ if ((c_chmask && g_audio->in_ep_fback)
+ || (p_chmask && params->p_fu.id)
+ || (c_chmask && params->c_fu.id))
strscpy(card->mixername, card_name, sizeof(card->driver));
- kctl = snd_ctl_new1(&u_audio_controls[0], &uac->c_prm);
+ if (c_chmask && g_audio->in_ep_fback) {
+ kctl = snd_ctl_new1(&u_audio_controls[UAC_FBACK_CTRL],
+ &uac->c_prm);
if (!kctl) {
err = -ENOMEM;
goto snd_fail;
@@ -777,6 +1048,82 @@ int g_audio_setup(struct g_audio *g_audio, const char *pcm_name,
goto snd_fail;
}
+ for (i = 0; i <= SNDRV_PCM_STREAM_LAST; i++) {
+ struct uac_rtd_params *prm;
+ struct uac_fu_params *fu;
+ char ctrl_name[24];
+ char *direction;
+
+ if (!pcm->streams[i].substream_count)
+ continue;
+
+ if (i == SNDRV_PCM_STREAM_PLAYBACK) {
+ prm = &uac->p_prm;
+ fu = &params->p_fu;
+ direction = "Playback";
+ } else {
+ prm = &uac->c_prm;
+ fu = &params->c_fu;
+ direction = "Capture";
+ }
+
+ prm->fu_id = fu->id;
+
+ if (fu->mute_present) {
+ snprintf(ctrl_name, sizeof(ctrl_name),
+ "PCM %s Switch", direction);
+
+ u_audio_controls[UAC_MUTE_CTRL].name = ctrl_name;
+
+ kctl = snd_ctl_new1(&u_audio_controls[UAC_MUTE_CTRL],
+ prm);
+ if (!kctl) {
+ err = -ENOMEM;
+ goto snd_fail;
+ }
+
+ kctl->id.device = pcm->device;
+ kctl->id.subdevice = i;
+
+ err = snd_ctl_add(card, kctl);
+ if (err < 0)
+ goto snd_fail;
+ prm->snd_kctl_mute = kctl;
+ prm->mute = 0;
+ }
+
+ if (fu->volume_present) {
+ snprintf(ctrl_name, sizeof(ctrl_name),
+ "PCM %s Volume", direction);
+
+ u_audio_controls[UAC_VOLUME_CTRL].name = ctrl_name;
+
+ kctl = snd_ctl_new1(&u_audio_controls[UAC_VOLUME_CTRL],
+ prm);
+ if (!kctl) {
+ err = -ENOMEM;
+ goto snd_fail;
+ }
+
+ kctl->id.device = pcm->device;
+ kctl->id.subdevice = i;
+
+
+ kctl->tlv.c = u_audio_volume_tlv;
+ kctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ |
+ SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK;
+
+ err = snd_ctl_add(card, kctl);
+ if (err < 0)
+ goto snd_fail;
+ prm->snd_kctl_volume = kctl;
+ prm->volume = fu->volume_max;
+ prm->volume_max = fu->volume_max;
+ prm->volume_min = fu->volume_min;
+ prm->volume_res = fu->volume_res;
+ }
+ }
+
strscpy(card->driver, card_name, sizeof(card->driver));
strscpy(card->shortname, card_name, sizeof(card->shortname));
sprintf(card->longname, "%s %i", card_name, card->dev->id);
diff --git a/drivers/usb/gadget/function/u_audio.h b/drivers/usb/gadget/function/u_audio.h
index a218cdf771fe..001a79a46022 100644
--- a/drivers/usb/gadget/function/u_audio.h
+++ b/drivers/usb/gadget/function/u_audio.h
@@ -19,16 +19,30 @@
*/
#define FBACK_SLOW_MAX 250
+/* Feature Unit parameters */
+struct uac_fu_params {
+ int id; /* Feature Unit ID */
+
+ bool mute_present; /* mute control enable */
+
+ bool volume_present; /* volume control enable */
+ s16 volume_min; /* min volume in 1/256 dB */
+ s16 volume_max; /* max volume in 1/256 dB */
+ s16 volume_res; /* volume resolution in 1/256 dB */
+};
+
struct uac_params {
/* playback */
int p_chmask; /* channel mask */
int p_srate; /* rate in Hz */
int p_ssize; /* sample size */
+ struct uac_fu_params p_fu; /* Feature Unit parameters */
/* capture */
int c_chmask; /* channel mask */
int c_srate; /* rate in Hz */
int c_ssize; /* sample size */
+ struct uac_fu_params c_fu; /* Feature Unit parameters */
int req_number; /* number of preallocated requests */
int fb_max; /* upper frequency drift feedback limit per-mil */
@@ -49,6 +63,9 @@ struct g_audio {
/* Max packet size for all out_ep possible speeds */
unsigned int out_ep_maxpsize;
+ /* Notify UAC driver about control change */
+ int (*notify)(struct g_audio *g_audio, int unit_id, int cs);
+
/* The ALSA Sound Card it represents on the USB-Client side */
struct snd_uac_chip *uac;
@@ -94,4 +111,9 @@ void u_audio_stop_capture(struct g_audio *g_audio);
int u_audio_start_playback(struct g_audio *g_audio);
void u_audio_stop_playback(struct g_audio *g_audio);
+int u_audio_get_volume(struct g_audio *g_audio, int playback, s16 *val);
+int u_audio_set_volume(struct g_audio *g_audio, int playback, s16 val);
+int u_audio_get_mute(struct g_audio *g_audio, int playback, int *val);
+int u_audio_set_mute(struct g_audio *g_audio, int playback, int val);
+
#endif /* __U_AUDIO_H */
diff --git a/drivers/usb/gadget/function/u_ether.c b/drivers/usb/gadget/function/u_ether.c
index d1d044d9f859..85a3f6d4b5af 100644
--- a/drivers/usb/gadget/function/u_ether.c
+++ b/drivers/usb/gadget/function/u_ether.c
@@ -492,8 +492,9 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb,
}
spin_unlock_irqrestore(&dev->lock, flags);
- if (skb && !in) {
- dev_kfree_skb_any(skb);
+ if (!in) {
+ if (skb)
+ dev_kfree_skb_any(skb);
return NETDEV_TX_OK;
}
diff --git a/drivers/usb/gadget/function/u_uac1.h b/drivers/usb/gadget/function/u_uac1.h
index 39c0e29e1b46..589fae861141 100644
--- a/drivers/usb/gadget/function/u_uac1.h
+++ b/drivers/usb/gadget/function/u_uac1.h
@@ -18,6 +18,13 @@
#define UAC1_DEF_PSRATE 48000
#define UAC1_DEF_PSSIZE 2
#define UAC1_DEF_REQ_NUM 2
+#define UAC1_DEF_INT_REQ_NUM 10
+
+#define UAC1_DEF_MUTE_PRESENT 1
+#define UAC1_DEF_VOLUME_PRESENT 1
+#define UAC1_DEF_MIN_DB (-100*256) /* -100 dB */
+#define UAC1_DEF_MAX_DB 0 /* 0 dB */
+#define UAC1_DEF_RES_DB (1*256) /* 1 dB */
struct f_uac1_opts {
@@ -28,6 +35,19 @@ struct f_uac1_opts {
int p_chmask;
int p_srate;
int p_ssize;
+
+ bool p_mute_present;
+ bool p_volume_present;
+ s16 p_volume_min;
+ s16 p_volume_max;
+ s16 p_volume_res;
+
+ bool c_mute_present;
+ bool c_volume_present;
+ s16 c_volume_min;
+ s16 c_volume_max;
+ s16 c_volume_res;
+
int req_number;
unsigned bound:1;
diff --git a/drivers/usb/gadget/function/u_uac2.h b/drivers/usb/gadget/function/u_uac2.h
index 179d3ef6a195..a73b35774c44 100644
--- a/drivers/usb/gadget/function/u_uac2.h
+++ b/drivers/usb/gadget/function/u_uac2.h
@@ -22,8 +22,16 @@
#define UAC2_DEF_CSRATE 64000
#define UAC2_DEF_CSSIZE 2
#define UAC2_DEF_CSYNC USB_ENDPOINT_SYNC_ASYNC
+
+#define UAC2_DEF_MUTE_PRESENT 1
+#define UAC2_DEF_VOLUME_PRESENT 1
+#define UAC2_DEF_MIN_DB (-100*256) /* -100 dB */
+#define UAC2_DEF_MAX_DB 0 /* 0 dB */
+#define UAC2_DEF_RES_DB (1*256) /* 1 dB */
+
#define UAC2_DEF_REQ_NUM 2
#define UAC2_DEF_FB_MAX 5
+#define UAC2_DEF_INT_REQ_NUM 10
struct f_uac2_opts {
struct usb_function_instance func_inst;
@@ -34,9 +42,22 @@ struct f_uac2_opts {
int c_srate;
int c_ssize;
int c_sync;
+
+ bool p_mute_present;
+ bool p_volume_present;
+ s16 p_volume_min;
+ s16 p_volume_max;
+ s16 p_volume_res;
+
+ bool c_mute_present;
+ bool c_volume_present;
+ s16 c_volume_min;
+ s16 c_volume_max;
+ s16 c_volume_res;
+
int req_number;
int fb_max;
- bool bound;
+ bool bound;
struct mutex lock;
int refcnt;
diff --git a/drivers/usb/gadget/udc/core.c b/drivers/usb/gadget/udc/core.c
index b7f0b1ebaaa8..14fdf918ecfe 100644
--- a/drivers/usb/gadget/udc/core.c
+++ b/drivers/usb/gadget/udc/core.c
@@ -1003,6 +1003,25 @@ int usb_gadget_ep_match_desc(struct usb_gadget *gadget,
}
EXPORT_SYMBOL_GPL(usb_gadget_ep_match_desc);
+/**
+ * usb_gadget_check_config - checks if the UDC can support the binded
+ * configuration
+ * @gadget: controller to check the USB configuration
+ *
+ * Ensure that a UDC is able to support the requested resources by a
+ * configuration, and that there are no resource limitations, such as
+ * internal memory allocated to all requested endpoints.
+ *
+ * Returns zero on success, else a negative errno.
+ */
+int usb_gadget_check_config(struct usb_gadget *gadget)
+{
+ if (gadget->ops->check_config)
+ return gadget->ops->check_config(gadget);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(usb_gadget_check_config);
+
/* ------------------------------------------------------------------------- */
static void usb_gadget_state_work(struct work_struct *work)
diff --git a/drivers/usb/host/ehci-mv.c b/drivers/usb/host/ehci-mv.c
index cffdc8d01b2a..8fd27249ad25 100644
--- a/drivers/usb/host/ehci-mv.c
+++ b/drivers/usb/host/ehci-mv.c
@@ -42,26 +42,25 @@ struct ehci_hcd_mv {
int (*set_vbus)(unsigned int vbus);
};
-static void ehci_clock_enable(struct ehci_hcd_mv *ehci_mv)
+static int mv_ehci_enable(struct ehci_hcd_mv *ehci_mv)
{
- clk_prepare_enable(ehci_mv->clk);
-}
+ int retval;
-static void ehci_clock_disable(struct ehci_hcd_mv *ehci_mv)
-{
- clk_disable_unprepare(ehci_mv->clk);
-}
+ retval = clk_prepare_enable(ehci_mv->clk);
+ if (retval)
+ return retval;
-static int mv_ehci_enable(struct ehci_hcd_mv *ehci_mv)
-{
- ehci_clock_enable(ehci_mv);
- return phy_init(ehci_mv->phy);
+ retval = phy_init(ehci_mv->phy);
+ if (retval)
+ clk_disable_unprepare(ehci_mv->clk);
+
+ return retval;
}
static void mv_ehci_disable(struct ehci_hcd_mv *ehci_mv)
{
phy_exit(ehci_mv->phy);
- ehci_clock_disable(ehci_mv);
+ clk_disable_unprepare(ehci_mv->clk);
}
static int mv_ehci_reset(struct usb_hcd *hcd)
diff --git a/drivers/usb/host/fotg210-hcd.c b/drivers/usb/host/fotg210-hcd.c
index 05fb8d97cf02..4b02ace09f3d 100644
--- a/drivers/usb/host/fotg210-hcd.c
+++ b/drivers/usb/host/fotg210-hcd.c
@@ -1858,9 +1858,11 @@ static struct fotg210_qh *fotg210_qh_alloc(struct fotg210_hcd *fotg210,
qh = kzalloc(sizeof(*qh), GFP_ATOMIC);
if (!qh)
goto done;
- qh->hw = dma_pool_zalloc(fotg210->qh_pool, flags, &dma);
+ qh->hw = (struct fotg210_qh_hw *)
+ dma_pool_alloc(fotg210->qh_pool, flags, &dma);
if (!qh->hw)
goto fail;
+ memset(qh->hw, 0, sizeof(*qh->hw));
qh->qh_dma = dma;
INIT_LIST_HEAD(&qh->qtd_list);
@@ -2510,11 +2512,6 @@ retry_xacterr:
return count;
}
-/* high bandwidth multiplier, as encoded in highspeed endpoint descriptors */
-#define hb_mult(wMaxPacketSize) (1 + (((wMaxPacketSize) >> 11) & 0x03))
-/* ... and packet size, for any kind of endpoint descriptor */
-#define max_packet(wMaxPacketSize) ((wMaxPacketSize) & 0x07ff)
-
/* reverse of qh_urb_transaction: free a list of TDs.
* used for cleanup after errors, before HC sees an URB's TDs.
*/
@@ -2600,7 +2597,7 @@ static struct list_head *qh_urb_transaction(struct fotg210_hcd *fotg210,
token |= (1 /* "in" */ << 8);
/* else it's already initted to "out" pid (0 << 8) */
- maxpacket = max_packet(usb_maxpacket(urb->dev, urb->pipe, !is_input));
+ maxpacket = usb_maxpacket(urb->dev, urb->pipe, !is_input);
/*
* buffer gets wrapped in one or more qtds;
@@ -2714,9 +2711,11 @@ static struct fotg210_qh *qh_make(struct fotg210_hcd *fotg210, struct urb *urb,
gfp_t flags)
{
struct fotg210_qh *qh = fotg210_qh_alloc(fotg210, flags);
+ struct usb_host_endpoint *ep;
u32 info1 = 0, info2 = 0;
int is_input, type;
int maxp = 0;
+ int mult;
struct usb_tt *tt = urb->dev->tt;
struct fotg210_qh_hw *hw;
@@ -2731,14 +2730,15 @@ static struct fotg210_qh *qh_make(struct fotg210_hcd *fotg210, struct urb *urb,
is_input = usb_pipein(urb->pipe);
type = usb_pipetype(urb->pipe);
- maxp = usb_maxpacket(urb->dev, urb->pipe, !is_input);
+ ep = usb_pipe_endpoint(urb->dev, urb->pipe);
+ maxp = usb_endpoint_maxp(&ep->desc);
+ mult = usb_endpoint_maxp_mult(&ep->desc);
/* 1024 byte maxpacket is a hardware ceiling. High bandwidth
* acts like up to 3KB, but is built from smaller packets.
*/
- if (max_packet(maxp) > 1024) {
- fotg210_dbg(fotg210, "bogus qh maxpacket %d\n",
- max_packet(maxp));
+ if (maxp > 1024) {
+ fotg210_dbg(fotg210, "bogus qh maxpacket %d\n", maxp);
goto done;
}
@@ -2752,8 +2752,7 @@ static struct fotg210_qh *qh_make(struct fotg210_hcd *fotg210, struct urb *urb,
*/
if (type == PIPE_INTERRUPT) {
qh->usecs = NS_TO_US(usb_calc_bus_time(USB_SPEED_HIGH,
- is_input, 0,
- hb_mult(maxp) * max_packet(maxp)));
+ is_input, 0, mult * maxp));
qh->start = NO_FRAME;
if (urb->dev->speed == USB_SPEED_HIGH) {
@@ -2790,7 +2789,7 @@ static struct fotg210_qh *qh_make(struct fotg210_hcd *fotg210, struct urb *urb,
think_time = tt ? tt->think_time : 0;
qh->tt_usecs = NS_TO_US(think_time +
usb_calc_bus_time(urb->dev->speed,
- is_input, 0, max_packet(maxp)));
+ is_input, 0, maxp));
qh->period = urb->interval;
if (qh->period > fotg210->periodic_size) {
qh->period = fotg210->periodic_size;
@@ -2853,11 +2852,11 @@ static struct fotg210_qh *qh_make(struct fotg210_hcd *fotg210, struct urb *urb,
* to help them do so. So now people expect to use
* such nonconformant devices with Linux too; sigh.
*/
- info1 |= max_packet(maxp) << 16;
+ info1 |= maxp << 16;
info2 |= (FOTG210_TUNE_MULT_HS << 30);
} else { /* PIPE_INTERRUPT */
- info1 |= max_packet(maxp) << 16;
- info2 |= hb_mult(maxp) << 30;
+ info1 |= maxp << 16;
+ info2 |= mult << 30;
}
break;
default:
@@ -3927,6 +3926,7 @@ static void iso_stream_init(struct fotg210_hcd *fotg210,
int is_input;
long bandwidth;
unsigned multi;
+ struct usb_host_endpoint *ep;
/*
* this might be a "high bandwidth" highspeed endpoint,
@@ -3934,14 +3934,14 @@ static void iso_stream_init(struct fotg210_hcd *fotg210,
*/
epnum = usb_pipeendpoint(pipe);
is_input = usb_pipein(pipe) ? USB_DIR_IN : 0;
- maxp = usb_maxpacket(dev, pipe, !is_input);
+ ep = usb_pipe_endpoint(dev, pipe);
+ maxp = usb_endpoint_maxp(&ep->desc);
if (is_input)
buf1 = (1 << 11);
else
buf1 = 0;
- maxp = max_packet(maxp);
- multi = hb_mult(maxp);
+ multi = usb_endpoint_maxp_mult(&ep->desc);
buf1 |= maxp;
maxp *= multi;
@@ -4112,7 +4112,7 @@ static int itd_urb_transaction(struct fotg210_iso_stream *stream,
} else {
alloc_itd:
spin_unlock_irqrestore(&fotg210->lock, flags);
- itd = dma_pool_zalloc(fotg210->itd_pool, mem_flags,
+ itd = dma_pool_alloc(fotg210->itd_pool, mem_flags,
&itd_dma);
spin_lock_irqsave(&fotg210->lock, flags);
if (!itd) {
@@ -4122,6 +4122,7 @@ alloc_itd:
}
}
+ memset(itd, 0, sizeof(*itd));
itd->itd_dma = itd_dma;
list_add(&itd->itd_list, &sched->td_list);
}
@@ -4462,13 +4463,12 @@ static bool itd_complete(struct fotg210_hcd *fotg210, struct fotg210_itd *itd)
/* HC need not update length with this error */
if (!(t & FOTG210_ISOC_BABBLE)) {
- desc->actual_length =
- fotg210_itdlen(urb, desc, t);
+ desc->actual_length = FOTG210_ITD_LENGTH(t);
urb->actual_length += desc->actual_length;
}
} else if (likely((t & FOTG210_ISOC_ACTIVE) == 0)) {
desc->status = 0;
- desc->actual_length = fotg210_itdlen(urb, desc, t);
+ desc->actual_length = FOTG210_ITD_LENGTH(t);
urb->actual_length += desc->actual_length;
} else {
/* URB was too late */
diff --git a/drivers/usb/host/fotg210.h b/drivers/usb/host/fotg210.h
index 0a91061a0551..0781442b7a24 100644
--- a/drivers/usb/host/fotg210.h
+++ b/drivers/usb/host/fotg210.h
@@ -683,11 +683,6 @@ static inline unsigned fotg210_read_frame_index(struct fotg210_hcd *fotg210)
return fotg210_readl(fotg210, &fotg210->regs->frame_index);
}
-#define fotg210_itdlen(urb, desc, t) ({ \
- usb_pipein((urb)->pipe) ? \
- (desc)->length - FOTG210_ITD_LENGTH(t) : \
- FOTG210_ITD_LENGTH(t); \
-})
/*-------------------------------------------------------------------------*/
#endif /* __LINUX_FOTG210_H */
diff --git a/drivers/usb/host/ohci-spear.c b/drivers/usb/host/ohci-spear.c
index 5cc05449281c..b4cd9e6c72fd 100644
--- a/drivers/usb/host/ohci-spear.c
+++ b/drivers/usb/host/ohci-spear.c
@@ -84,7 +84,7 @@ static int spear_ohci_hcd_drv_probe(struct platform_device *pdev)
clk_prepare_enable(sohci_p->clk);
- retval = usb_add_hcd(hcd, platform_get_irq(pdev, 0), 0);
+ retval = usb_add_hcd(hcd, irq, 0);
if (retval == 0) {
device_wakeup_enable(hcd->self.controller);
return retval;
diff --git a/drivers/usb/host/xhci-pci-renesas.c b/drivers/usb/host/xhci-pci-renesas.c
index 5923844ed821..aa88e57649a9 100644
--- a/drivers/usb/host/xhci-pci-renesas.c
+++ b/drivers/usb/host/xhci-pci-renesas.c
@@ -595,7 +595,7 @@ int renesas_xhci_check_request_fw(struct pci_dev *pdev,
err = renesas_fw_check_running(pdev);
/* Continue ahead, if the firmware is already running. */
- if (err == 0)
+ if (!err)
return 0;
if (err != 1)
@@ -620,9 +620,4 @@ exit:
}
EXPORT_SYMBOL_GPL(renesas_xhci_check_request_fw);
-void renesas_xhci_pci_exit(struct pci_dev *dev)
-{
-}
-EXPORT_SYMBOL_GPL(renesas_xhci_pci_exit);
-
MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c
index 1c9a7957c45c..2c9f25ca8edd 100644
--- a/drivers/usb/host/xhci-pci.c
+++ b/drivers/usb/host/xhci-pci.c
@@ -449,8 +449,6 @@ static void xhci_pci_remove(struct pci_dev *dev)
struct xhci_hcd *xhci;
xhci = hcd_to_xhci(pci_get_drvdata(dev));
- if (xhci->quirks & XHCI_RENESAS_FW_QUIRK)
- renesas_xhci_pci_exit(dev);
xhci->xhc_state |= XHCI_STATE_REMOVING;
diff --git a/drivers/usb/host/xhci-pci.h b/drivers/usb/host/xhci-pci.h
index acd7cf0a1706..cb9a8f331a44 100644
--- a/drivers/usb/host/xhci-pci.h
+++ b/drivers/usb/host/xhci-pci.h
@@ -7,7 +7,6 @@
#if IS_ENABLED(CONFIG_USB_XHCI_PCI_RENESAS)
int renesas_xhci_check_request_fw(struct pci_dev *dev,
const struct pci_device_id *id);
-void renesas_xhci_pci_exit(struct pci_dev *dev);
#else
static int renesas_xhci_check_request_fw(struct pci_dev *dev,
@@ -16,8 +15,6 @@ static int renesas_xhci_check_request_fw(struct pci_dev *dev,
return 0;
}
-static void renesas_xhci_pci_exit(struct pci_dev *dev) { };
-
#endif
struct xhci_driver_data {
diff --git a/drivers/usb/phy/phy-isp1301-omap.c b/drivers/usb/phy/phy-isp1301-omap.c
index f3e9b3b6ac3e..190699b38b41 100644
--- a/drivers/usb/phy/phy-isp1301-omap.c
+++ b/drivers/usb/phy/phy-isp1301-omap.c
@@ -12,7 +12,7 @@
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
-#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
#include <linux/usb.h>