diff options
-rw-r--r-- | drivers/media/video/uvc/uvc_debugfs.c | 63 | ||||
-rw-r--r-- | drivers/media/video/uvc/uvc_video.c | 109 | ||||
-rw-r--r-- | drivers/media/video/uvc/uvcvideo.h | 32 |
3 files changed, 199 insertions, 5 deletions
diff --git a/drivers/media/video/uvc/uvc_debugfs.c b/drivers/media/video/uvc/uvc_debugfs.c index 39fd43b3e1d2..14561a5abb79 100644 --- a/drivers/media/video/uvc/uvc_debugfs.c +++ b/drivers/media/video/uvc/uvc_debugfs.c | |||
@@ -19,6 +19,57 @@ | |||
19 | #include "uvcvideo.h" | 19 | #include "uvcvideo.h" |
20 | 20 | ||
21 | /* ----------------------------------------------------------------------------- | 21 | /* ----------------------------------------------------------------------------- |
22 | * Statistics | ||
23 | */ | ||
24 | |||
25 | #define UVC_DEBUGFS_BUF_SIZE 1024 | ||
26 | |||
27 | struct uvc_debugfs_buffer { | ||
28 | size_t count; | ||
29 | char data[UVC_DEBUGFS_BUF_SIZE]; | ||
30 | }; | ||
31 | |||
32 | static int uvc_debugfs_stats_open(struct inode *inode, struct file *file) | ||
33 | { | ||
34 | struct uvc_streaming *stream = inode->i_private; | ||
35 | struct uvc_debugfs_buffer *buf; | ||
36 | |||
37 | buf = kmalloc(sizeof(*buf), GFP_KERNEL); | ||
38 | if (buf == NULL) | ||
39 | return -ENOMEM; | ||
40 | |||
41 | buf->count = uvc_video_stats_dump(stream, buf->data, sizeof(buf->data)); | ||
42 | |||
43 | file->private_data = buf; | ||
44 | return 0; | ||
45 | } | ||
46 | |||
47 | static ssize_t uvc_debugfs_stats_read(struct file *file, char __user *user_buf, | ||
48 | size_t nbytes, loff_t *ppos) | ||
49 | { | ||
50 | struct uvc_debugfs_buffer *buf = file->private_data; | ||
51 | |||
52 | return simple_read_from_buffer(user_buf, nbytes, ppos, buf->data, | ||
53 | buf->count); | ||
54 | } | ||
55 | |||
56 | static int uvc_debugfs_stats_release(struct inode *inode, struct file *file) | ||
57 | { | ||
58 | kfree(file->private_data); | ||
59 | file->private_data = NULL; | ||
60 | |||
61 | return 0; | ||
62 | } | ||
63 | |||
64 | static const struct file_operations uvc_debugfs_stats_fops = { | ||
65 | .owner = THIS_MODULE, | ||
66 | .open = uvc_debugfs_stats_open, | ||
67 | .llseek = no_llseek, | ||
68 | .read = uvc_debugfs_stats_read, | ||
69 | .release = uvc_debugfs_stats_release, | ||
70 | }; | ||
71 | |||
72 | /* ----------------------------------------------------------------------------- | ||
22 | * Global and stream initialization/cleanup | 73 | * Global and stream initialization/cleanup |
23 | */ | 74 | */ |
24 | 75 | ||
@@ -37,13 +88,21 @@ int uvc_debugfs_init_stream(struct uvc_streaming *stream) | |||
37 | 88 | ||
38 | dent = debugfs_create_dir(dir_name, uvc_debugfs_root_dir); | 89 | dent = debugfs_create_dir(dir_name, uvc_debugfs_root_dir); |
39 | if (IS_ERR_OR_NULL(dent)) { | 90 | if (IS_ERR_OR_NULL(dent)) { |
40 | uvc_printk(KERN_INFO, "Unable to create debugfs %s directory.\n", | 91 | uvc_printk(KERN_INFO, "Unable to create debugfs %s " |
41 | dir_name); | 92 | "directory.\n", dir_name); |
42 | return -ENODEV; | 93 | return -ENODEV; |
43 | } | 94 | } |
44 | 95 | ||
45 | stream->debugfs_dir = dent; | 96 | stream->debugfs_dir = dent; |
46 | 97 | ||
98 | dent = debugfs_create_file("stats", 0444, stream->debugfs_dir, | ||
99 | stream, &uvc_debugfs_stats_fops); | ||
100 | if (IS_ERR_OR_NULL(dent)) { | ||
101 | uvc_printk(KERN_INFO, "Unable to create debugfs stats file.\n"); | ||
102 | uvc_debugfs_cleanup_stream(stream); | ||
103 | return -ENODEV; | ||
104 | } | ||
105 | |||
47 | return 0; | 106 | return 0; |
48 | } | 107 | } |
49 | 108 | ||
diff --git a/drivers/media/video/uvc/uvc_video.c b/drivers/media/video/uvc/uvc_video.c index a57f81341849..1908dd859433 100644 --- a/drivers/media/video/uvc/uvc_video.c +++ b/drivers/media/video/uvc/uvc_video.c | |||
@@ -358,6 +358,100 @@ static int uvc_commit_video(struct uvc_streaming *stream, | |||
358 | } | 358 | } |
359 | 359 | ||
360 | /* ------------------------------------------------------------------------ | 360 | /* ------------------------------------------------------------------------ |
361 | * Stream statistics | ||
362 | */ | ||
363 | |||
364 | static void uvc_video_stats_decode(struct uvc_streaming *stream, | ||
365 | const __u8 *data, int len) | ||
366 | { | ||
367 | unsigned int header_size; | ||
368 | |||
369 | if (stream->stats.stream.nb_frames == 0 && | ||
370 | stream->stats.frame.nb_packets == 0) | ||
371 | ktime_get_ts(&stream->stats.stream.start_ts); | ||
372 | |||
373 | switch (data[1] & (UVC_STREAM_PTS | UVC_STREAM_SCR)) { | ||
374 | case UVC_STREAM_PTS | UVC_STREAM_SCR: | ||
375 | header_size = 12; | ||
376 | break; | ||
377 | case UVC_STREAM_PTS: | ||
378 | header_size = 6; | ||
379 | break; | ||
380 | case UVC_STREAM_SCR: | ||
381 | header_size = 8; | ||
382 | break; | ||
383 | default: | ||
384 | header_size = 2; | ||
385 | break; | ||
386 | } | ||
387 | |||
388 | /* Check for invalid headers. */ | ||
389 | if (len < header_size || data[0] < header_size) { | ||
390 | stream->stats.frame.nb_invalid++; | ||
391 | return; | ||
392 | } | ||
393 | |||
394 | /* Record the first non-empty packet number. */ | ||
395 | if (stream->stats.frame.size == 0 && len > header_size) | ||
396 | stream->stats.frame.first_data = stream->stats.frame.nb_packets; | ||
397 | |||
398 | /* Update the frame size. */ | ||
399 | stream->stats.frame.size += len - header_size; | ||
400 | |||
401 | /* Update the packets counters. */ | ||
402 | stream->stats.frame.nb_packets++; | ||
403 | if (len > header_size) | ||
404 | stream->stats.frame.nb_empty++; | ||
405 | |||
406 | if (data[1] & UVC_STREAM_ERR) | ||
407 | stream->stats.frame.nb_errors++; | ||
408 | } | ||
409 | |||
410 | static void uvc_video_stats_update(struct uvc_streaming *stream) | ||
411 | { | ||
412 | struct uvc_stats_frame *frame = &stream->stats.frame; | ||
413 | |||
414 | uvc_trace(UVC_TRACE_STATS, "frame %u stats: %u/%u/%u packets\n", | ||
415 | stream->sequence, frame->first_data, | ||
416 | frame->nb_packets - frame->nb_empty, frame->nb_packets); | ||
417 | |||
418 | stream->stats.stream.nb_frames++; | ||
419 | stream->stats.stream.nb_packets += stream->stats.frame.nb_packets; | ||
420 | stream->stats.stream.nb_empty += stream->stats.frame.nb_empty; | ||
421 | stream->stats.stream.nb_errors += stream->stats.frame.nb_errors; | ||
422 | stream->stats.stream.nb_invalid += stream->stats.frame.nb_invalid; | ||
423 | |||
424 | memset(&stream->stats.frame, 0, sizeof(stream->stats.frame)); | ||
425 | } | ||
426 | |||
427 | size_t uvc_video_stats_dump(struct uvc_streaming *stream, char *buf, | ||
428 | size_t size) | ||
429 | { | ||
430 | size_t count = 0; | ||
431 | |||
432 | count += scnprintf(buf + count, size - count, | ||
433 | "frames: %u\npackets: %u\nempty: %u\n" | ||
434 | "errors: %u\ninvalid: %u\n", | ||
435 | stream->stats.stream.nb_frames, | ||
436 | stream->stats.stream.nb_packets, | ||
437 | stream->stats.stream.nb_empty, | ||
438 | stream->stats.stream.nb_errors, | ||
439 | stream->stats.stream.nb_invalid); | ||
440 | |||
441 | return count; | ||
442 | } | ||
443 | |||
444 | static void uvc_video_stats_start(struct uvc_streaming *stream) | ||
445 | { | ||
446 | memset(&stream->stats, 0, sizeof(stream->stats)); | ||
447 | } | ||
448 | |||
449 | static void uvc_video_stats_stop(struct uvc_streaming *stream) | ||
450 | { | ||
451 | ktime_get_ts(&stream->stats.stream.stop_ts); | ||
452 | } | ||
453 | |||
454 | /* ------------------------------------------------------------------------ | ||
361 | * Video codecs | 455 | * Video codecs |
362 | */ | 456 | */ |
363 | 457 | ||
@@ -406,16 +500,23 @@ static int uvc_video_decode_start(struct uvc_streaming *stream, | |||
406 | * - bHeaderLength value must be at least 2 bytes (see above) | 500 | * - bHeaderLength value must be at least 2 bytes (see above) |
407 | * - bHeaderLength value can't be larger than the packet size. | 501 | * - bHeaderLength value can't be larger than the packet size. |
408 | */ | 502 | */ |
409 | if (len < 2 || data[0] < 2 || data[0] > len) | 503 | if (len < 2 || data[0] < 2 || data[0] > len) { |
504 | stream->stats.frame.nb_invalid++; | ||
410 | return -EINVAL; | 505 | return -EINVAL; |
506 | } | ||
411 | 507 | ||
412 | fid = data[1] & UVC_STREAM_FID; | 508 | fid = data[1] & UVC_STREAM_FID; |
413 | 509 | ||
414 | /* Increase the sequence number regardless of any buffer states, so | 510 | /* Increase the sequence number regardless of any buffer states, so |
415 | * that discontinuous sequence numbers always indicate lost frames. | 511 | * that discontinuous sequence numbers always indicate lost frames. |
416 | */ | 512 | */ |
417 | if (stream->last_fid != fid) | 513 | if (stream->last_fid != fid) { |
418 | stream->sequence++; | 514 | stream->sequence++; |
515 | if (stream->sequence) | ||
516 | uvc_video_stats_update(stream); | ||
517 | } | ||
518 | |||
519 | uvc_video_stats_decode(stream, data, len); | ||
419 | 520 | ||
420 | /* Store the payload FID bit and return immediately when the buffer is | 521 | /* Store the payload FID bit and return immediately when the buffer is |
421 | * NULL. | 522 | * NULL. |
@@ -860,6 +961,8 @@ static void uvc_uninit_video(struct uvc_streaming *stream, int free_buffers) | |||
860 | struct urb *urb; | 961 | struct urb *urb; |
861 | unsigned int i; | 962 | unsigned int i; |
862 | 963 | ||
964 | uvc_video_stats_stop(stream); | ||
965 | |||
863 | for (i = 0; i < UVC_URBS; ++i) { | 966 | for (i = 0; i < UVC_URBS; ++i) { |
864 | urb = stream->urb[i]; | 967 | urb = stream->urb[i]; |
865 | if (urb == NULL) | 968 | if (urb == NULL) |
@@ -999,6 +1102,8 @@ static int uvc_init_video(struct uvc_streaming *stream, gfp_t gfp_flags) | |||
999 | stream->bulk.skip_payload = 0; | 1102 | stream->bulk.skip_payload = 0; |
1000 | stream->bulk.payload_size = 0; | 1103 | stream->bulk.payload_size = 0; |
1001 | 1104 | ||
1105 | uvc_video_stats_start(stream); | ||
1106 | |||
1002 | if (intf->num_altsetting > 1) { | 1107 | if (intf->num_altsetting > 1) { |
1003 | struct usb_host_endpoint *best_ep = NULL; | 1108 | struct usb_host_endpoint *best_ep = NULL; |
1004 | unsigned int best_psize = 3 * 1024; | 1109 | unsigned int best_psize = 3 * 1024; |
diff --git a/drivers/media/video/uvc/uvcvideo.h b/drivers/media/video/uvc/uvcvideo.h index d975636cbb10..f9ee62ed0150 100644 --- a/drivers/media/video/uvc/uvcvideo.h +++ b/drivers/media/video/uvc/uvcvideo.h | |||
@@ -356,6 +356,28 @@ struct uvc_video_chain { | |||
356 | struct mutex ctrl_mutex; /* Protects ctrl.info */ | 356 | struct mutex ctrl_mutex; /* Protects ctrl.info */ |
357 | }; | 357 | }; |
358 | 358 | ||
359 | struct uvc_stats_frame { | ||
360 | unsigned int size; /* Number of bytes captured */ | ||
361 | unsigned int first_data; /* Index of the first non-empty packet */ | ||
362 | |||
363 | unsigned int nb_packets; /* Number of packets */ | ||
364 | unsigned int nb_empty; /* Number of empty packets */ | ||
365 | unsigned int nb_invalid; /* Number of packets with an invalid header */ | ||
366 | unsigned int nb_errors; /* Number of packets with the error bit set */ | ||
367 | }; | ||
368 | |||
369 | struct uvc_stats_stream { | ||
370 | struct timespec start_ts; /* Stream start timestamp */ | ||
371 | struct timespec stop_ts; /* Stream stop timestamp */ | ||
372 | |||
373 | unsigned int nb_frames; /* Number of frames */ | ||
374 | |||
375 | unsigned int nb_packets; /* Number of packets */ | ||
376 | unsigned int nb_empty; /* Number of empty packets */ | ||
377 | unsigned int nb_invalid; /* Number of packets with an invalid header */ | ||
378 | unsigned int nb_errors; /* Number of packets with the error bit set */ | ||
379 | }; | ||
380 | |||
359 | struct uvc_streaming { | 381 | struct uvc_streaming { |
360 | struct list_head list; | 382 | struct list_head list; |
361 | struct uvc_device *dev; | 383 | struct uvc_device *dev; |
@@ -406,6 +428,10 @@ struct uvc_streaming { | |||
406 | 428 | ||
407 | /* debugfs */ | 429 | /* debugfs */ |
408 | struct dentry *debugfs_dir; | 430 | struct dentry *debugfs_dir; |
431 | struct { | ||
432 | struct uvc_stats_frame frame; | ||
433 | struct uvc_stats_stream stream; | ||
434 | } stats; | ||
409 | }; | 435 | }; |
410 | 436 | ||
411 | enum uvc_device_state { | 437 | enum uvc_device_state { |
@@ -477,6 +503,7 @@ struct uvc_driver { | |||
477 | #define UVC_TRACE_SUSPEND (1 << 8) | 503 | #define UVC_TRACE_SUSPEND (1 << 8) |
478 | #define UVC_TRACE_STATUS (1 << 9) | 504 | #define UVC_TRACE_STATUS (1 << 9) |
479 | #define UVC_TRACE_VIDEO (1 << 10) | 505 | #define UVC_TRACE_VIDEO (1 << 10) |
506 | #define UVC_TRACE_STATS (1 << 11) | ||
480 | 507 | ||
481 | #define UVC_WARN_MINMAX 0 | 508 | #define UVC_WARN_MINMAX 0 |
482 | #define UVC_WARN_PROBE_DEF 1 | 509 | #define UVC_WARN_PROBE_DEF 1 |
@@ -609,10 +636,13 @@ extern struct usb_host_endpoint *uvc_find_endpoint( | |||
609 | void uvc_video_decode_isight(struct urb *urb, struct uvc_streaming *stream, | 636 | void uvc_video_decode_isight(struct urb *urb, struct uvc_streaming *stream, |
610 | struct uvc_buffer *buf); | 637 | struct uvc_buffer *buf); |
611 | 638 | ||
612 | /* debugfs */ | 639 | /* debugfs and statistics */ |
613 | int uvc_debugfs_init(void); | 640 | int uvc_debugfs_init(void); |
614 | void uvc_debugfs_cleanup(void); | 641 | void uvc_debugfs_cleanup(void); |
615 | int uvc_debugfs_init_stream(struct uvc_streaming *stream); | 642 | int uvc_debugfs_init_stream(struct uvc_streaming *stream); |
616 | void uvc_debugfs_cleanup_stream(struct uvc_streaming *stream); | 643 | void uvc_debugfs_cleanup_stream(struct uvc_streaming *stream); |
617 | 644 | ||
645 | size_t uvc_video_stats_dump(struct uvc_streaming *stream, char *buf, | ||
646 | size_t size); | ||
647 | |||
618 | #endif | 648 | #endif |