|
@@ -34,13 +34,6 @@
|
|
|
* plus two that support control-OUT tests. If the optional "autoresume"
|
|
|
* mode is enabled, it provides good functional coverage for the "USBCV"
|
|
|
* test harness from USB-IF.
|
|
|
- *
|
|
|
- * Note that because this doesn't queue more than one request at a time,
|
|
|
- * some other function must be used to test queueing logic. The network
|
|
|
- * link (g_ether) is the best overall option for that, since its TX and RX
|
|
|
- * queues are relatively independent, will receive a range of packet sizes,
|
|
|
- * and can often be made to run out completely. Those issues are important
|
|
|
- * when stress testing peripheral controller drivers.
|
|
|
*/
|
|
|
struct f_sourcesink {
|
|
|
struct usb_function function;
|
|
@@ -57,6 +50,8 @@ struct f_sourcesink {
|
|
|
unsigned isoc_mult;
|
|
|
unsigned isoc_maxburst;
|
|
|
unsigned buflen;
|
|
|
+ unsigned bulk_qlen;
|
|
|
+ unsigned iso_qlen;
|
|
|
};
|
|
|
|
|
|
static inline struct f_sourcesink *func_to_ss(struct usb_function *f)
|
|
@@ -595,31 +590,33 @@ static int source_sink_start_ep(struct f_sourcesink *ss, bool is_in,
|
|
|
{
|
|
|
struct usb_ep *ep;
|
|
|
struct usb_request *req;
|
|
|
- int i, size, status;
|
|
|
-
|
|
|
- for (i = 0; i < 8; i++) {
|
|
|
- if (is_iso) {
|
|
|
- switch (speed) {
|
|
|
- case USB_SPEED_SUPER:
|
|
|
- size = ss->isoc_maxpacket *
|
|
|
- (ss->isoc_mult + 1) *
|
|
|
- (ss->isoc_maxburst + 1);
|
|
|
- break;
|
|
|
- case USB_SPEED_HIGH:
|
|
|
- size = ss->isoc_maxpacket * (ss->isoc_mult + 1);
|
|
|
- break;
|
|
|
- default:
|
|
|
- size = ss->isoc_maxpacket > 1023 ?
|
|
|
- 1023 : ss->isoc_maxpacket;
|
|
|
- break;
|
|
|
- }
|
|
|
- ep = is_in ? ss->iso_in_ep : ss->iso_out_ep;
|
|
|
- req = ss_alloc_ep_req(ep, size);
|
|
|
- } else {
|
|
|
- ep = is_in ? ss->in_ep : ss->out_ep;
|
|
|
- req = ss_alloc_ep_req(ep, 0);
|
|
|
+ int i, size, qlen, status = 0;
|
|
|
+
|
|
|
+ if (is_iso) {
|
|
|
+ switch (speed) {
|
|
|
+ case USB_SPEED_SUPER:
|
|
|
+ size = ss->isoc_maxpacket *
|
|
|
+ (ss->isoc_mult + 1) *
|
|
|
+ (ss->isoc_maxburst + 1);
|
|
|
+ break;
|
|
|
+ case USB_SPEED_HIGH:
|
|
|
+ size = ss->isoc_maxpacket * (ss->isoc_mult + 1);
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ size = ss->isoc_maxpacket > 1023 ?
|
|
|
+ 1023 : ss->isoc_maxpacket;
|
|
|
+ break;
|
|
|
}
|
|
|
+ ep = is_in ? ss->iso_in_ep : ss->iso_out_ep;
|
|
|
+ qlen = ss->iso_qlen;
|
|
|
+ } else {
|
|
|
+ ep = is_in ? ss->in_ep : ss->out_ep;
|
|
|
+ qlen = ss->bulk_qlen;
|
|
|
+ size = 0;
|
|
|
+ }
|
|
|
|
|
|
+ for (i = 0; i < qlen; i++) {
|
|
|
+ req = ss_alloc_ep_req(ep, size);
|
|
|
if (!req)
|
|
|
return -ENOMEM;
|
|
|
|
|
@@ -639,9 +636,6 @@ static int source_sink_start_ep(struct f_sourcesink *ss, bool is_in,
|
|
|
ep->name, status);
|
|
|
free_ep_req(ep, req);
|
|
|
}
|
|
|
-
|
|
|
- if (!is_iso)
|
|
|
- break;
|
|
|
}
|
|
|
|
|
|
return status;
|
|
@@ -869,6 +863,8 @@ static struct usb_function *source_sink_alloc_func(
|
|
|
ss->isoc_mult = ss_opts->isoc_mult;
|
|
|
ss->isoc_maxburst = ss_opts->isoc_maxburst;
|
|
|
ss->buflen = ss_opts->bulk_buflen;
|
|
|
+ ss->bulk_qlen = ss_opts->bulk_qlen;
|
|
|
+ ss->iso_qlen = ss_opts->iso_qlen;
|
|
|
|
|
|
ss->function.name = "source/sink";
|
|
|
ss->function.bind = sourcesink_bind;
|
|
@@ -1153,6 +1149,82 @@ end:
|
|
|
|
|
|
CONFIGFS_ATTR(f_ss_opts_, bulk_buflen);
|
|
|
|
|
|
+static ssize_t f_ss_opts_bulk_qlen_show(struct config_item *item, char *page)
|
|
|
+{
|
|
|
+ struct f_ss_opts *opts = to_f_ss_opts(item);
|
|
|
+ int result;
|
|
|
+
|
|
|
+ mutex_lock(&opts->lock);
|
|
|
+ result = sprintf(page, "%u\n", opts->bulk_qlen);
|
|
|
+ mutex_unlock(&opts->lock);
|
|
|
+
|
|
|
+ return result;
|
|
|
+}
|
|
|
+
|
|
|
+static ssize_t f_ss_opts_bulk_qlen_store(struct config_item *item,
|
|
|
+ const char *page, size_t len)
|
|
|
+{
|
|
|
+ struct f_ss_opts *opts = to_f_ss_opts(item);
|
|
|
+ int ret;
|
|
|
+ u32 num;
|
|
|
+
|
|
|
+ mutex_lock(&opts->lock);
|
|
|
+ if (opts->refcnt) {
|
|
|
+ ret = -EBUSY;
|
|
|
+ goto end;
|
|
|
+ }
|
|
|
+
|
|
|
+ ret = kstrtou32(page, 0, &num);
|
|
|
+ if (ret)
|
|
|
+ goto end;
|
|
|
+
|
|
|
+ opts->bulk_qlen = num;
|
|
|
+ ret = len;
|
|
|
+end:
|
|
|
+ mutex_unlock(&opts->lock);
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+CONFIGFS_ATTR(f_ss_opts_, bulk_qlen);
|
|
|
+
|
|
|
+static ssize_t f_ss_opts_iso_qlen_show(struct config_item *item, char *page)
|
|
|
+{
|
|
|
+ struct f_ss_opts *opts = to_f_ss_opts(item);
|
|
|
+ int result;
|
|
|
+
|
|
|
+ mutex_lock(&opts->lock);
|
|
|
+ result = sprintf(page, "%u\n", opts->iso_qlen);
|
|
|
+ mutex_unlock(&opts->lock);
|
|
|
+
|
|
|
+ return result;
|
|
|
+}
|
|
|
+
|
|
|
+static ssize_t f_ss_opts_iso_qlen_store(struct config_item *item,
|
|
|
+ const char *page, size_t len)
|
|
|
+{
|
|
|
+ struct f_ss_opts *opts = to_f_ss_opts(item);
|
|
|
+ int ret;
|
|
|
+ u32 num;
|
|
|
+
|
|
|
+ mutex_lock(&opts->lock);
|
|
|
+ if (opts->refcnt) {
|
|
|
+ ret = -EBUSY;
|
|
|
+ goto end;
|
|
|
+ }
|
|
|
+
|
|
|
+ ret = kstrtou32(page, 0, &num);
|
|
|
+ if (ret)
|
|
|
+ goto end;
|
|
|
+
|
|
|
+ opts->iso_qlen = num;
|
|
|
+ ret = len;
|
|
|
+end:
|
|
|
+ mutex_unlock(&opts->lock);
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+CONFIGFS_ATTR(f_ss_opts_, iso_qlen);
|
|
|
+
|
|
|
static struct configfs_attribute *ss_attrs[] = {
|
|
|
&f_ss_opts_attr_pattern,
|
|
|
&f_ss_opts_attr_isoc_interval,
|
|
@@ -1160,6 +1232,8 @@ static struct configfs_attribute *ss_attrs[] = {
|
|
|
&f_ss_opts_attr_isoc_mult,
|
|
|
&f_ss_opts_attr_isoc_maxburst,
|
|
|
&f_ss_opts_attr_bulk_buflen,
|
|
|
+ &f_ss_opts_attr_bulk_qlen,
|
|
|
+ &f_ss_opts_attr_iso_qlen,
|
|
|
NULL,
|
|
|
};
|
|
|
|
|
@@ -1189,6 +1263,8 @@ static struct usb_function_instance *source_sink_alloc_inst(void)
|
|
|
ss_opts->isoc_interval = GZERO_ISOC_INTERVAL;
|
|
|
ss_opts->isoc_maxpacket = GZERO_ISOC_MAXPACKET;
|
|
|
ss_opts->bulk_buflen = GZERO_BULK_BUFLEN;
|
|
|
+ ss_opts->bulk_qlen = GZERO_SS_BULK_QLEN;
|
|
|
+ ss_opts->iso_qlen = GZERO_SS_ISO_QLEN;
|
|
|
|
|
|
config_group_init_type_name(&ss_opts->func_inst.group, "",
|
|
|
&ss_func_type);
|