Newer
Older
11001
11002
11003
11004
11005
11006
11007
11008
11009
11010
11011
11012
11013
11014
11015
11016
11017
11018
11019
11020
11021
11022
11023
11024
11025
11026
11027
11028
11029
11030
11031
11032
11033
11034
11035
11036
11037
11038
11039
11040
11041
11042
11043
11044
11045
11046
11047
11048
11049
11050
11051
11052
11053
11054
11055
11056
11057
11058
11059
11060
11061
11062
11063
11064
11065
11066
11067
11068
11069
11070
11071
11072
11073
11074
11075
11076
11077
11078
11079
11080
11081
11082
11083
11084
11085
11086
11087
11088
11089
11090
11091
11092
11093
11094
11095
11096
11097
11098
11099
11100
11101
11102
11103
11104
11105
11106
11107
11108
11109
11110
11111
11112
11113
11114
11115
11116
11117
11118
11119
11120
11121
11122
11123
11124
11125
11126
11127
11128
11129
11130
11131
11132
11133
11134
11135
11136
11137
11138
11139
11140
11141
11142
11143
11144
11145
11146
11147
11148
11149
11150
11151
11152
11153
11154
11155
11156
11157
11158
11159
11160
11161
11162
11163
11164
11165
11166
11167
11168
11169
11170
11171
11172
11173
11174
11175
11176
11177
11178
11179
11180
11181
11182
11183
11184
11185
11186
11187
11188
11189
11190
11191
11192
11193
11194
11195
11196
11197
11198
11199
11200
11201
11202
11203
11204
11205
11206
11207
11208
11209
11210
11211
11212
11213
11214
11215
11216
11217
11218
11219
11220
11221
11222
11223
11224
11225
11226
11227
11228
11229
11230
11231
11232
11233
11234
11235
11236
11237
11238
11239
11240
11241
11242
11243
11244
11245
11246
11247
11248
11249
11250
11251
11252
11253
11254
11255
11256
11257
11258
11259
11260
11261
11262
11263
11264
11265
11266
11267
11268
11269
11270
11271
11272
11273
11274
11275
11276
11277
11278
11279
11280
11281
11282
11283
11284
11285
11286
11287
11288
11289
11290
11291
11292
11293
11294
11295
11296
11297
11298
11299
11300
11301
11302
11303
11304
11305
11306
11307
11308
11309
11310
11311
11312
11313
11314
11315
11316
11317
11318
11319
11320
11321
11322
11323
11324
11325
11326
11327
11328
11329
11330
11331
11332
11333
11334
11335
11336
11337
11338
11339
11340
11341
11342
11343
11344
11345
11346
11347
11348
11349
11350
11351
11352
11353
11354
11355
11356
11357
11358
11359
11360
11361
11362
11363
11364
11365
11366
11367
11368
11369
11370
11371
11372
11373
11374
11375
11376
11377
11378
11379
11380
11381
11382
11383
11384
11385
11386
11387
11388
11389
11390
11391
11392
11393
11394
11395
11396
11397
11398
11399
11400
11401
11402
11403
11404
11405
11406
11407
11408
11409
11410
11411
11412
11413
11414
11415
11416
11417
11418
11419
11420
11421
11422
11423
11424
11425
11426
11427
11428
11429
11430
11431
11432
11433
11434
11435
+ break;
+ }
+
+ if (ret == USBG_ERROR_NOT_FOUND)
+ ret = 0;
+
+ return ret;
+}
+
+static int uvc_export_frame_attrs(struct usbg_f_uvc *uvcf, const char *format,
+ int frame_id, config_setting_t *root)
+{
+ union usbg_f_uvc_frame_attr_val val;
+ int ret = 0;
+ int i;
+
+ for (i = USBG_F_UVC_FRAME_ATTR_MIN; i < USBG_F_UVC_FRAME_ATTR_MAX; ++i) {
+ ret = usbg_f_uvc_get_frame_attr_val(uvcf, format, frame_id, i, &val);
+ if (ret)
+ break;
+
+ ret = uvc_frame_attr[i].export(root, uvc_frame_attr[i].name, &val);
+ if (ret)
+ break;
+ }
+
+ if (ret == USBG_ERROR_NOT_FOUND)
+ ret = 0;
+
+ return ret;
+}
+
+static int uvc_export_frames(struct usbg_f_uvc *uvcf, const char *format,
+ bool *frames, config_setting_t *root)
+{
+ config_setting_t *frames_node, *node;
+ int i, nframes;
+ int ret = 0;
+
+ frames_node = config_setting_add(root, "frames", CONFIG_TYPE_LIST);
+ if (!frames_node) {
+ ret = USBG_ERROR_INVALID_FORMAT;
+ goto out;
+ }
+
+ nframes = usbg_f_uvc_get_nframes(frames);
+ if (nframes < 0)
+ return nframes;
+ for (i = 0; i < nframes; ++i) {
+ node = config_setting_add(frames_node, "", CONFIG_TYPE_GROUP);
+ if (!node)
+ goto out;
+
+ ret = uvc_export_frame_attrs(uvcf, format, i, node);
+ if (ret)
+ goto out;
+ }
+
+out:
+ return ret;
+};
+
+static int uvc_libconfig_export(struct usbg_function *f, config_setting_t *root)
+{
+ struct usbg_f_uvc *uvcf = usbg_to_uvc_function(f);
+ config_setting_t *formats_node, *node;
+ char fp[USBG_MAX_PATH_LENGTH];
+ struct formats *formats;
+ int i, ret = 0;
+ char *p;
+
+ formats_node = config_setting_add(root, "formats", CONFIG_TYPE_GROUP);
+ if (!formats_node) {
+ ret = USBG_ERROR_INVALID_PARAM;
+ goto out;
+ }
+
+ formats = get_formats_mask(uvcf);
+ for (i = 0; i < MAX_FORMATS; ++i) {
+
+ /* remove trailing /m and /u from format */
+ strcpy(fp, format_names[i]);
+ p = fp + strlen(fp) - 2; *p = '\0';
+
+ node = config_setting_add(formats_node, fp, CONFIG_TYPE_GROUP);
+ if (!node)
+ goto out;
+
+ ret = uvc_export_frames(uvcf, format_names[i], formats[i].frames, node);
+ if (ret)
+ goto out;
+
+ ret = uvc_export_format_attrs(uvcf, format_names[i], node);
+ if (ret)
+ goto out;
+ }
+
+ ret = uvc_export_config(uvcf, root);
+
+out:
+ return ret;
+}
+
+#endif
+
+static int uvc_set_format(char *format_path, const char *format, const struct usbg_f_uvc_format_attrs *attrs)
+{
+ return usbg_write_dec(format_path, format, "bDefaultFrameIndex", attrs->bDefaultFrameIndex);
+}
+
+static int uvc_set_frame(char *format_path, const char *format, const struct usbg_f_uvc_frame_attrs *attrs)
+{
+ char frame_path[USBG_MAX_PATH_LENGTH];
+ char full_frame_path[USBG_MAX_PATH_LENGTH];
+ char frame_name[32];
+ int nmb, ret;
+
+ nmb = snprintf(frame_name, sizeof(frame_name), "frame.%d", attrs->bFrameIndex);
+ if (nmb >= sizeof(frame_name))
+ return USBG_ERROR_PATH_TOO_LONG;
+
+ nmb = snprintf(frame_path, sizeof(frame_path), "%s/%s", format_path, format);
+ if (nmb >= sizeof(frame_path))
+ return USBG_ERROR_PATH_TOO_LONG;
+
+ nmb = snprintf(full_frame_path, sizeof(frame_path), "%s/%s", frame_path, frame_name);
+ if (nmb >= sizeof(full_frame_path))
+ return USBG_ERROR_PATH_TOO_LONG;
+
+ ret = uvc_create_dir(full_frame_path);
+ if (ret != USBG_SUCCESS)
+ return ret;
+
+ ret = usbg_write_dec(frame_path, frame_name, "dwFrameInterval", attrs->dwFrameInterval);
+ if (ret != USBG_SUCCESS)
+ return ret;
+
+ ret = usbg_write_dec(frame_path, frame_name, "dwMaxVideoFrameBufferSize", attrs->wHeight * attrs->wWidth);
+ if (ret != USBG_SUCCESS)
+ return ret;
+
+ ret = usbg_write_dec(frame_path, frame_name, "wHeight", attrs->wHeight);
+ if (ret != USBG_SUCCESS)
+ return ret;
+
+ return usbg_write_dec(frame_path, frame_name, "wWidth", attrs->wWidth);
+}
+
+static int uvc_set_streaming(char *func_path, const char *format, const struct usbg_f_uvc_format_attrs *attrs)
+{
+ struct usbg_f_uvc_frame_attrs **frame_attrs;
+ char streaming_path[USBG_MAX_PATH_LENGTH];
+ int nmb, ret, i;
+
+ nmb = snprintf(streaming_path, sizeof(streaming_path), "%s/" UVC_PATH_STREAMING, func_path);
+ if (nmb >= sizeof(streaming_path))
+ return USBG_ERROR_PATH_TOO_LONG;
+
+ for(frame_attrs = attrs->frames, i = 0; frame_attrs[i]; ++i) {
+ if (frame_attrs[i]) {
+ ret = uvc_set_frame(streaming_path, format, frame_attrs[i]);
+ if(ret != USBG_SUCCESS)
+ ERROR("Error: %d", ret);
+ }
+ }
+
+ ret = uvc_set_format(streaming_path, format, attrs);
+ if(ret != USBG_SUCCESS)
+ ERROR("Error: %d", ret);
+
+ return ret;
+}
+
+static int dir_nftw_cb(const char *pathname, const struct stat *sbuf, int type, struct FTW *ftwb)
+{
+ (void) sbuf;
+ (void) type;
+ (void) ftwb;
+ int ret;
+
+ ret = remove(pathname);
+ if (ret < -1)
+ ERROR("failed to remove %s - %s", pathname, strerror(ret));
+
+ return 0;
+}
+
+int remove_dir(const char *dirpath)
+{
+ const int max_open_descs = 8;
+ int ret;
+
+ ret = nftw(dirpath, dir_nftw_cb, max_open_descs, FTW_DEPTH | FTW_MOUNT | FTW_PHYS);
+ if (ret < 0) {
+ ERROR("nftw failed");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int content_nftw_cb(const char *pathname, const struct stat *sbuf, int type, struct FTW *ftwb)
+{
+ (void) sbuf;
+ (void) type;
+ (void) ftwb;
+ int ret;
+
+ if(ftwb->level == 0)
+ return 0;
+
+ ret = remove(pathname);
+ if(ret < -1)
+ ERROR("failed to remove %s - %s", pathname, strerror(ret));
+
+ return 0;
+}
+
+int remove_dir_content(const char *dirpath)
+{
+ const int max_open_descs = 8;
+ int ret;
+
+ /* traverse in reverse order (handle directory after it's content), stay within the same file system and do not follow symbolic links */
+ ret = nftw(dirpath, content_nftw_cb, max_open_descs, FTW_DEPTH | FTW_MOUNT | FTW_PHYS);
+ if (ret < 0) {
+ ERROR("nftw failed");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int uvc_remove(struct usbg_function *f, int opts)
+{
+ usbg_f_uvc *uvcf = usbg_to_uvc_function(f);
+ char streaming_path[USBG_MAX_PATH_LENGTH];
+ char control_path[USBG_MAX_PATH_LENGTH];
+ char path[USBG_MAX_PATH_LENGTH];
+ int nmb, ret = USBG_SUCCESS;
+
+ nmb = snprintf(path, sizeof(path), "%s/%s", uvcf->func.path, uvcf->func.name);
+ if (nmb >= sizeof(path))
+ return USBG_ERROR_PATH_TOO_LONG;
+
+ nmb = snprintf(streaming_path, sizeof(streaming_path), "%s/streaming", path);
+ if (nmb >= sizeof(streaming_path))
+ return USBG_ERROR_PATH_TOO_LONG;
+
+ nmb = snprintf(control_path, sizeof(control_path), "%s/control", path);
+ if (nmb >= sizeof(control_path))
+ return USBG_ERROR_PATH_TOO_LONG;
+
+ if(remove_dir_content(streaming_path) < 0)
+ return USBG_ERROR_PATH_TOO_LONG;
+
+ if(remove_dir_content(control_path) < 0)
+ return USBG_ERROR_PATH_TOO_LONG;
+
+ if(remove_dir(streaming_path) < 0)
+ return USBG_ERROR_PATH_TOO_LONG;
+
+ if(remove_dir(control_path) < 0)
+ return USBG_ERROR_PATH_TOO_LONG;
+
+ return ret;
+};
+
+struct usbg_function_type usbg_f_type_uvc = {
+ .name = "uvc",
+ .alloc_inst = uvc_alloc_inst,
+ .free_inst = uvc_free_inst,
+ .set_attrs = uvc_set_attrs,
+ .get_attrs = uvc_get_attrs,
+ .cleanup_attrs = uvc_cleanup_attrs,
+#ifdef HAS_GADGET_SCHEMES
+ .import = uvc_libconfig_import,
+ .export = uvc_libconfig_export,
+#endif
+ .remove = uvc_remove,
+};
+
+/* API implementation */
+
+usbg_f_uvc *usbg_to_uvc_function(usbg_function *f)
+{
+ return f->ops == &usbg_f_type_uvc ?
+ container_of(f, struct usbg_f_uvc, func) : NULL;
+}
+
+usbg_function *usbg_from_uvc_function(usbg_f_uvc *ff)
+{
+ return &ff->func;
+}
+
+int usbg_f_uvc_get_attrs(usbg_f_uvc *uvcf, struct usbg_f_uvc_attrs *attrs)
+{
+ return USBG_SUCCESS;
+}
+
+int usbg_f_uvc_set_attrs(usbg_f_uvc *uvcf, const struct usbg_f_uvc_attrs *attrs)
+{
+ char path[USBG_MAX_PATH_LENGTH];
+ struct usbg_f_uvc_format_attrs **format_attrs;
+ int ret = USBG_SUCCESS;
+ int nmb, i;
+
+ if (!attrs)
+ return USBG_ERROR_INVALID_PARAM;
+
+ nmb = snprintf(path, sizeof(path), "%s/%s", uvcf->func.path, uvcf->func.name);
+ if (nmb >= sizeof(path))
+ return USBG_ERROR_PATH_TOO_LONG;
+
+ for(format_attrs = attrs->formats, i = 0; format_attrs[i]; ++i) {
+ ret = uvc_set_streaming(path, format_attrs[i]->format, format_attrs[i]);
+ if(ret != USBG_SUCCESS)
+ ERROR("Error: %d", ret);
+ }
+
+ ret = uvc_set_class(uvcf, UVC_PATH_CONTROL);
+ if (ret != USBG_SUCCESS)
+ return ret;
+
+ ret = uvc_set_class(uvcf, UVC_PATH_STREAMING);
+ if (ret != USBG_SUCCESS)
+ return ret;
+
+ return ret;
+}
diff --git a/src/usbg.c b/src/usbg.c
index d2bf381..68bded3 100644
--- a/src/usbg.c
+++ b/src/usbg.c
@@ -52,6 +52,8 @@ extern struct usbg_function_type usbg_f_type_phonet;
extern struct usbg_function_type usbg_f_type_loopback;
extern struct usbg_function_type usbg_f_type_hid;
extern struct usbg_function_type usbg_f_type_uac2;
+extern struct usbg_function_type usbg_f_type_uvc;
+extern struct usbg_function_type usbg_f_type_printer;
/**
* @var function_types
@@ -73,6 +75,8 @@ struct usbg_function_type* function_types[] = {
[USBG_F_LOOPBACK] = &usbg_f_type_loopback,
[USBG_F_HID] = &usbg_f_type_hid,
[USBG_F_UAC2] = &usbg_f_type_uac2,
+ [USBG_F_UVC] = &usbg_f_type_uvc,
+ [USBG_F_PRINTER] = &usbg_f_type_printer,
};
ARRAY_SIZE_SENTINEL(function_types, USBG_FUNCTION_TYPE_MAX);
@@ -747,8 +751,7 @@ static int usbg_parse_config(const char *path, const char *name,
goto free_config;
TAILQ_INSERT_TAIL(&g->configs, c, cnode);
-
- return USBG_SUCCESS;
+ goto out;
free_config:
usbg_free_config(c);
diff --git a/src/usbg_common.c b/src/usbg_common.c
index f8822fc..5f7f4e5 100644
--- a/src/usbg_common.c
+++ b/src/usbg_common.c
@@ -15,11 +15,13 @@
#include <errno.h>
#include <sys/stat.h>
+#include <sys/sysmacros.h>
#include <sys/types.h>
#include <malloc.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
+#include <sys/sysmacros.h>
int usbg_read_buf_limited(const char *path, const char *name,
const char *file, char *buf, int len)
diff --git a/tests/usbg-test.c b/tests/usbg-test.c
index abe9bff..549667b 100644
--- a/tests/usbg-test.c
+++ b/tests/usbg-test.c
@@ -1067,6 +1067,18 @@ static void push_net_attrs(struct test_function *func,
safe_asprintf(&path, "%s/%s/qmult", func->path, func->name);
safe_asprintf(&content, "%d\n", attrs->qmult);
PUSH_FILE_STR(path, content);
+
+ safe_asprintf(&path, "%s/%s/class", func->path, func->name);
+ safe_asprintf(&content, "%d\n", attrs->class_);
+ PUSH_FILE_STR(path, content);
+
+ safe_asprintf(&path, "%s/%s/subclass", func->path, func->name);
+ safe_asprintf(&content, "%d\n", attrs->subclass);
+ PUSH_FILE_STR(path, content);
+
+ safe_asprintf(&path, "%s/%s/protocol", func->path, func->name);
+ safe_asprintf(&content, "%d\n", attrs->protocol);
+ PUSH_FILE_STR(path, content);
}
static void push_phonet_attrs(struct test_function *func,
@@ -1128,6 +1140,18 @@ static void pull_function_net_attrs(struct test_function *func,
safe_asprintf(&path, "%s/%s/qmult", func->path, func->name);
safe_asprintf(&content, "%d\n", attrs->qmult);
EXPECT_WRITE_STR(path, content);
+
+ safe_asprintf(&path, "%s/%s/class", func->path, func->name);
+ safe_asprintf(&content, "%d\n", attrs->class_);
+ EXPECT_WRITE_STR(path, content);
+
+ safe_asprintf(&path, "%s/%s/subclass", func->path, func->name);
+ safe_asprintf(&content, "%d\n", attrs->subclass);
+ EXPECT_WRITE_STR(path, content);
+
+ safe_asprintf(&path, "%s/%s/protocol", func->path, func->name);
+ safe_asprintf(&content, "%d\n", attrs->protocol);
+ EXPECT_WRITE_STR(path, content);
}
void pull_function_attrs(struct test_function *func, void *attrs)
@@ -1317,6 +1341,9 @@ void assert_f_net_attrs_equal(struct usbg_f_net_attrs *actual,
assert_ether_addrs_equal(&actual->host_addr, &expected->host_addr);
assert_string_equal(actual->ifname, expected->ifname);
assert_int_equal(actual->qmult, expected->qmult);
+ assert_int_equal(actual->class_, expected->class_);
+ assert_int_equal(actual->subclass, expected->subclass);
+ assert_int_equal(actual->protocol, expected->protocol);
}
void assert_f_phonet_attrs_equal(char **actual, char **expected)
--
2.39.2