/***************************************************************** >file name : uvc_dev.c >author : lichao >create time : Tue 13 Jun 2017 04:20:02 PM HKT *****************************************************************/ #include "device/usb_stack.h" /* #include "asm/dma.h" */ #include "uvc_device.h" #include "usb_config.h" #include "host_uvc.h" #include "video_ioctl.h" /* #include "video/video.h" */ #include "videobuf.h" #include "generic/list.h" #include "os/os_api.h" #include "os/os_type.h" #include "app_config.h" #if TCFG_HOST_UVC_ENABLE #define LOG_TAG_CONST USB #define LOG_TAG "[UVC_HOST]" #define LOG_ERROR_ENABLE #define LOG_DEBUG_ENABLE #define LOG_INFO_ENABLE /* #define LOG_DUMP_ENABLE */ #define LOG_CLI_ENABLE #include "debug.h" #define UVC_CHANNEL_MAX 2 #define UVC_REC_JPG_HEAD_SIZE 8 #define UVC_REC_JPG_ALIGN 512 #define UVC_JPEG_HEAD 0xE0FFD8FF #define UVC_JPEG_HEAD1 0xC0FFD8FF #define USB_DMA_CP_ENABLE (0)//注意:使用硬件dma copy需要异步接收推送数据,否则出现速度快数据错乱的问题 #ifdef CONFIG_UI_ENABLE #define UVC_RECV_BUFF_SHARE 0 //1:UVC接收数据不受应用层缓存影响,使用共享帧单独给UVC一直接收数据,使用uvc_host_jpg_callback_register注册进行数据获取,应用在屏显等 #else #define UVC_RECV_BUFF_SHARE 0 #endif #define UVC_RECV_BUFF_SHARE_SIZE (100*1024) //UVC一帧JPEG大小,建议100K #define ADDR_ALIGNE(addr,align) ((addr + (align - 1)) / align * align) static void (*uvc_host_jpg_cb)(char *jpg_buf, u32 jpg_len); void uvc_host_jpg_callback_register(void (*cb)(char *jpg_buf, u32 jpg_len)) { uvc_host_jpg_cb = cb; } static int uvc_jpeg_head_check(u32 head) { if (uvc_host_get_fmt()) { return true; } if (head == UVC_JPEG_HEAD) { return true; } else if (head == UVC_JPEG_HEAD1) { return true; } return 0; } struct uvc_dev_control { OS_SEM sem; struct list_head dev_list; }; struct mjpg_uvc_image { u8 *buf; u32 size; }; struct uvc_fh { u8 id; u8 eof; u8 fps; u8 real_fps; u8 drop_frame; u8 streamon; volatile u8 streamoff; u8 uvc_out; u8 channel; u8 alloc_channel; u8 open[UVC_CHANNEL_MAX]; u8 streamon_ch[UVC_CHANNEL_MAX]; u32 frame_cnt; u32 drop_frame_cnt; struct list_head entry; int free_size; u8 *buf; u8 *share_buf; int b_offset; /* dma_user_task_t dma_list[16]; */ u8 bfmode; u8 uvc_video_q; void *ppbuf; struct videobuf_queue video_q; struct video_device *video_dev[UVC_CHANNEL_MAX]; struct device device[UVC_CHANNEL_MAX]; void *private_data; OS_SEM sem; OS_SEM img_sem; struct mjpg_uvc_image img; volatile u8 image_req; }; static struct uvc_dev_control uvc_dev; #define __this (&uvc_dev) #define list_for_each_uvc(fh) \ list_for_each_entry(fh, &__this->dev_list, entry) #define list_add_uvc(fh) \ list_add(&fh->entry, &__this->dev_list) #define list_del_uvc(fh) \ list_del(&fh->entry); static int uvc_img_cap(void *_fh, u32 arg) { struct uvc_fh *fh = (struct uvc_fh *)_fh; struct video_image_capture *icap = (struct video_image_capture *)arg; u32 *head; int err = 0; if (!icap) { return -EINVAL; } if (fh) { fh->img.buf = icap->baddr; fh->img.size = icap->size; os_sem_create(&fh->img_sem, 0); err = os_sem_pend(&fh->img_sem, 500); if (err) { log_error("image err"); } fh->image_req = false; head = (u32 *)(fh->img.buf + 8); if (uvc_jpeg_head_check(*head)) { fh->img.size -= 8; memcpy(icap->baddr, icap->baddr + 8, fh->img.size); } os_sem_del(&fh->img_sem, OS_DEL_ALWAYS); if (icap->size < fh->img.size) { log_error("image capture size is too small !!!"); } else if (!err) { log_info("image capture : %d.%dKB", fh->img.size / 1024, (fh->img.size % 1024) * 10 / 1024); } icap->size = fh->img.size; return err; } else { log_error("image err in on open uvc"); } return err; } void *uvc_buf_malloc(struct uvc_fh *fh, u32 size, u8 channel)//uvc视频流内存申请 { void *b; if (fh->uvc_video_q) { struct videobuf_buffer *vb; vb = videobuf_stream_alloc(&fh->video_q, size); if (!vb) { return NULL; } vb->len = size; // printf("video_buf: %x\n", vb, vb->data); return vb->data; } if (!channel) { for (int i = 0; i < UVC_CHANNEL_MAX; i++) { if (fh->open[i] && fh->streamon_ch[i]) { channel = i + 1; break; } } if (!channel) { return NULL; } } fh->alloc_channel = channel; if (!fh->open[channel - 1] || !fh->streamon_ch[channel - 1]) { return NULL; } struct video_device *video_dev = (struct video_device *)fh->video_dev[fh->alloc_channel - 1]; if (!video_dev) { return NULL; } b = video_buf_malloc(video_dev, size); return b; } void *uvc_buf_realloc(struct uvc_fh *fh, void *buf, int size, u8 channel)//uvc视频流内存重新申请 { if (fh->uvc_video_q) { struct videobuf_buffer *b = container_of(buf, struct videobuf_buffer, data); b->len = size; videobuf_stream_realloc(&fh->video_q, b, size); return b->data; } if (!channel || !fh->open[channel - 1] || !fh->streamon_ch[channel - 1]) { return NULL; } struct video_device *video_dev = (struct video_device *)fh->video_dev[channel - 1]; return video_buf_realloc(video_dev, buf, size); } void uvc_buf_free(struct uvc_fh *fh, void *buf, u8 channel)//uvc视频流内存释放 { if (fh->uvc_video_q) { struct videobuf_buffer *b = container_of(buf, struct videobuf_buffer, data); ASSERT(buf != NULL, "uvc_buf_free\n"); videobuf_stream_free(&fh->video_q, b); return; } if (!channel || !fh->open[channel - 1] || !fh->streamon_ch[channel - 1]) { return; } struct video_device *video_dev = (struct video_device *)fh->video_dev[channel - 1]; if (!video_dev) { return ; } return video_buf_free(video_dev, buf); } u32 uvc_buf_free_space(struct uvc_fh *fh, u8 channel)//uvc视频流内存剩余空间 { if (fh->uvc_video_q) { return videobuf_stream_free_space(&fh->video_q); } if (!channel) { find_open: for (int i = 0; i < UVC_CHANNEL_MAX; i++) { if (fh->open[i] && fh->streamon_ch[i]) { channel = i + 1; break; } } if (!channel) { return 0; } fh->alloc_channel = channel; } if (!fh->open[channel - 1] || !fh->streamon_ch[channel - 1]) { return 0; } struct video_device *video_dev = (struct video_device *)fh->video_dev[channel - 1]; if (!video_dev) { channel = 0; goto find_open; } return video_buf_free_space(video_dev); } void uvc_buf_stream_finish(struct uvc_fh *fh, void *buf, u8 channel)//uvc视频流推流 { if (fh->uvc_video_q) { struct videobuf_buffer *b = container_of(buf, struct videobuf_buffer, data); videobuf_stream_finish(&fh->video_q, b); return; } if (!channel || !buf || !fh->open[channel - 1] || !fh->streamon_ch[channel - 1]) { return; } struct video_device *video_dev = (struct video_device *)fh->video_dev[channel - 1]; if (!video_dev) { return; } u32 size; if (os_sem_valid(&fh->img_sem) && fh->image_req) {//有拍照申请则复制数据到拍照缓存 size = video_buf_size(buf); size = MIN(size, fh->img.size); memcpy(fh->img.buf, buf, size); fh->img.size = size; os_sem_post(&fh->img_sem); fh->image_req = false; } video_buf_stream_finish(video_dev, buf);//lbuf推流 } static int uvc_dev_reqbufs(void *_fh, struct uvc_reqbufs *b) { struct uvc_fh *fh = (struct uvc_fh *)_fh; struct video_reqbufs vb_req = {0}; vb_req.buf = b->buf; vb_req.size = b->size; videobuf_queue_init(&fh->video_q, 32, "uvc_dev"); fh->uvc_video_q = 1; return videobuf_reqbufs(&fh->video_q, (struct video_reqbufs *)&vb_req); /* return 0; */ } static int uvc_dev_qbuf(void *_fh, struct video_buffer *b) { struct uvc_fh *fh = (struct uvc_fh *)_fh; return videobuf_qbuf(&fh->video_q, b); /* return 0; */ } static int uvc_dev_dqbuf(void *_fh, struct video_buffer *b) { struct uvc_fh *fh = (struct uvc_fh *)_fh; if (fh->uvc_out) { return -ENODEV; } if (!fh->streamon) { return -ENODEV; } return videobuf_dqbuf(&fh->video_q, b); /* return 0; */ } int uvc_mjpg_stream_out(void *fd, int cnt, void *stream_list, int state) { struct uvc_fh *fh = (struct uvc_fh *)fd; struct uvc_stream_list *list = (struct uvc_stream_list *)stream_list; int i; int err = 0; u32 tmp_jiffies = 0, req_size; if ((cnt < 0) || !list) { /*putchar('E');*/ if (fh->buf) { if (fh->buf != fh->share_buf) {//没有多通道共享内存才释放 uvc_buf_free(fh, fh->buf, fh->alloc_channel); } fh->buf = NULL; if (state != STREAM_SOF) { // eof == 2 next frame start fh->drop_frame = 1; } err = -EINVAL; } else if (state == STREAM_SOF) { fh->drop_frame = 0; } goto _exit; } if (fh->drop_frame) { if (state == STREAM_EOF) { fh->drop_frame = 0; } goto _exit; } if (!fh->buf) { if (fh->share_buf) {//有共享内存则使用共享内存大小(一般打开两个通道或者开启UI) fh->buf = fh->share_buf; fh->free_size = uvc_host_get_fmt() ? UVC_RECV_BUFF_SHARE_SIZE : UVC_RECV_BUFF_SHARE_SIZE - UVC_REC_JPG_ALIGN; fh->alloc_channel = 0; } else {//没有使用共享内存,则请求剩余空间足够再申请内存 fh->free_size = uvc_buf_free_space(fh, fh->alloc_channel); if (fh->free_size > 1024) { fh->buf = uvc_buf_malloc(fh, fh->free_size, fh->alloc_channel); if (!uvc_host_get_fmt()) { fh->free_size -= UVC_REC_JPG_ALIGN;//减512防止realloc时512对齐断言 } } } if (!fh->buf) { err = -ENOMEM; goto _exit; } fh->b_offset = 0; } for (i = 0; i < cnt; i++) { if ((fh->b_offset + list[i].length + UVC_REC_JPG_HEAD_SIZE) > fh->free_size) {//数据超过当前可用的内存空间则释放当前帧 if (fh->buf != fh->share_buf) { uvc_buf_free(fh, fh->buf, fh->alloc_channel); } fh->buf = NULL; fh->drop_frame = 1; putchar('d'); err = -EFAULT; goto _exit; } //复制数据到buf缓存 #if USB_DMA_CP_ENABLE fh->dma_list[i].src_addr = list[i].addr; fh->dma_list[i].dst_addr = fh->buf + UVC_REC_JPG_HEAD_SIZE + fh->b_offset; fh->dma_list[i].len = list[i].length; #else memcpy(fh->buf + UVC_REC_JPG_HEAD_SIZE + fh->b_offset, list[i].addr, list[i].length); #endif fh->b_offset += list[i].length; } #if USB_DMA_CP_ENABLE dma_task_list_copy(fh->dma_list, cnt);//注意:使用硬件dma copy需要异步接收推送数据,否则出现速度快数据错乱的问题 #endif if (state == STREAM_EOF) {//USB结束帧则表明一帧图像数据接收完成 fh->frame_cnt++; #ifdef CONFIG_UVC_DROP_FRAME_ENABLE if (fh->real_fps && fh->fps && fh->real_fps < fh->fps) {//检测USB的帧率和当前应用层请求的帧率是否一直是否需要丢帧处理 u32 drop = fh->frame_cnt * (fh->fps - fh->real_fps) / fh->fps; if (fh->drop_frame_cnt != drop) {//不一致需要丢帧处理 fh->drop_frame_cnt = drop; if (fh->buf != fh->share_buf) {//丢帧 uvc_buf_free(fh, fh->buf, fh->alloc_channel); } fh->buf = NULL; goto _exit; } } #endif if (uvc_host_get_fmt()) { req_size = fh->b_offset + UVC_REC_JPG_HEAD_SIZE; } else { req_size = ADDR_ALIGNE(fh->b_offset + UVC_REC_JPG_HEAD_SIZE, UVC_REC_JPG_ALIGN);//JPEG图像数据需要512对齐 } if (uvc_host_jpg_cb) {//有注册回调则先进入回调函数 uvc_host_jpg_cb((char *)fh->buf + UVC_REC_JPG_HEAD_SIZE, fh->b_offset); } if (fh->buf == fh->share_buf && fh->share_buf) {//共享数据,则为多通道 for (int ch = 1; ch <= UVC_CHANNEL_MAX; ch++) {//多个通道检测 if (uvc_buf_free_space(fh, ch) > 1024) {//检测剩余空间足够用再申请复制数据 u8 *pbuf = uvc_buf_malloc(fh, req_size, ch); if (pbuf) { memcpy(pbuf + UVC_REC_JPG_HEAD_SIZE, fh->buf + UVC_REC_JPG_HEAD_SIZE, fh->b_offset); fh->buf = pbuf; if (fh->buf) { uvc_buf_stream_finish(fh, fh->buf, ch);//推流往应用层 } } } } } else {//单通道 fh->buf = uvc_buf_realloc(fh, fh->buf, req_size, fh->alloc_channel);//重新申请内存,会释放多余的空间 if (fh->buf) { uvc_buf_stream_finish(fh, fh->buf, fh->alloc_channel);//推流往应用层 } } fh->buf = NULL; } _exit: fh->streamoff = 0; return err; } int uvc_h264_stream_out(void *fd, int cnt, void *stream_list, int state) { struct uvc_fh *fh = (struct uvc_fh *)fd; struct uvc_stream_list *list = (struct uvc_stream_list *)stream_list; int i; int err = 0; u32 tmp_jiffies = 0; if ((cnt < 0) || !list) { /* putchar('E'); */ if (fh->buf) { if (fh->buf != fh->share_buf) {//没有多通道共享内存才释放 uvc_buf_free(fh, fh->buf, 0); } fh->buf = NULL; if (state != STREAM_SOF) { // eof == 2 next frame start fh->drop_frame = 1; } err = -EINVAL; } else if (state == STREAM_SOF) { fh->drop_frame = 0; } goto _exit; } if (fh->drop_frame) { /* puts("drop\n"); */ if (state == STREAM_EOF) { fh->drop_frame = 0; } goto _exit; } if (!fh->buf) { fh->free_size = uvc_buf_free_space(fh, 0); if (fh->free_size > 1024) { fh->buf = uvc_buf_malloc(fh, fh->free_size, 0); } if (!fh->buf) { /* puts("no mem\n"); */ err = -ENOMEM; goto _exit; } fh->b_offset = 0; } for (i = 0; i < cnt; i++) { if ((fh->b_offset + list[i].length) > fh->free_size) { uvc_buf_free(fh, fh->buf, 0); fh->buf = NULL; fh->drop_frame = 1; putchar('d'); /* printf("%d", fh->id); */ err = -EFAULT; goto _exit; } #if USB_DMA_CP_ENABLE fh->dma_list[i].src_addr = list[i].addr; fh->dma_list[i].dst_addr = fh->buf + fh->b_offset; fh->dma_list[i].len = list[i].length; #else memcpy(fh->buf + fh->b_offset, list[i].addr, list[i].length); #endif fh->b_offset += list[i].length; } #if USB_DMA_CP_ENABLE dma_task_list_copy(fh->dma_list, cnt); #endif if (state == STREAM_EOF) { /* puts("h264\n"); */ fh->buf = uvc_buf_realloc(fh, fh->buf, fh->b_offset, 0); uvc_buf_stream_finish(fh, fh->buf, 0); fh->buf = NULL; } _exit: fh->streamoff = 0; return err; } static int uvc_stream_on(void *device, void *_fh, int index) { int err = 0; u8 channel = 0; struct uvc_fh *fh = (struct uvc_fh *)_fh; os_sem_pend(&fh->sem, 0); for (int i = 0; i < UVC_CHANNEL_MAX; i++) { if ((u32)(&fh->device[i]) == (u32)device) { channel = i + 1; break; } } ASSERT(channel, "err int uvc_stream_on\n"); if (fh->uvc_out) { os_sem_post(&fh->sem); return -EINVAL; } fh->eof = 1; fh->drop_frame = 0; if (fh->streamon) { fh->streamon++; fh->streamon_ch[channel - 1]++; os_sem_post(&fh->sem); return 0; } err = uvc_host_open_camera(fh->private_data);//打开USB,并配置UVC的视频流 if (err) { log_error("uvc_stream_on err"); os_sem_post(&fh->sem); return err; } fh->streamon++; fh->streamon_ch[channel - 1]++; if (fh->uvc_video_q) { videobuf_streamon(&fh->video_q, (u8 *)&channel); } os_sem_post(&fh->sem); return err; } static int uvc_set_real_fps(void *_fh, u8 fp) { struct uvc_fh *fh = (struct uvc_fh *)_fh; fh->real_fps = fp; return 0; } static int uvc_stream_off(void *device, void *_fh, int index) { int err = 0; struct uvc_fh *fh = (struct uvc_fh *)_fh; u32 time = jiffies + msecs_to_jiffies(10); u8 channel = 0; os_sem_pend(&fh->sem, 0); for (int i = 0; i < UVC_CHANNEL_MAX; i++) { if ((u32)(&fh->device[i]) == (u32)device) { channel = i + 1; break; } } ASSERT(channel, "err int uvc_stream_off\n"); fh->streamon_ch[channel - 1]--; if (fh->streamon && --fh->streamon) { os_sem_post(&fh->sem); return 0; } if (!fh->uvc_out) { uvc_host_close_camera(fh->private_data);//关闭UVC视频流 } fh->streamoff = 1; //cppcheck-suppress knownConditionTrueFalse while (fh->streamoff) { if (time_after(jiffies, time)) { break; } } if (fh->buf) { if (fh->buf != fh->share_buf) { uvc_buf_free(fh, fh->buf, fh->alloc_channel); } fh->buf = NULL; fh->b_offset = 0; } if (fh->uvc_video_q) { err = videobuf_streamoff(&fh->video_q, 0); } os_sem_post(&fh->sem); return err; } static int uvc_dev_init(const struct dev_node *node, void *_data) { //struct uvc_platform_data *data = (struct uvc_platform_data *)_data; os_sem_create(&__this->sem, 1); INIT_LIST_HEAD(&__this->dev_list); return 0; } static bool uvc_dev_online(const struct dev_node *node) { int id; id = uvc_host_online(); log_debug("[msg]>>>>>>>>>>>id=%d", id); if (id < 0) { return false; } return true; } static int uvc_dev_out(void *fd) { struct uvc_fh *fh = (struct uvc_fh *)fd; os_sem_pend(&fh->sem, 0); fh->uvc_out = 1; os_sem_post(&fh->sem); return 0; } static int uvc_dev_open(const char *_name, struct device **device, void *arg) { struct uvc_fh *fh = NULL; struct uvc_host_param param = {0}; char name[8]; int id, i; u8 channel = 0; struct video_var_param_info *info = (struct video_var_param_info *)arg; id = info->f->uvc_id; list_for_each_uvc(fh) { if (fh->id == id) {//已经被打开过则使用另外通道 for (i = 0; i < UVC_CHANNEL_MAX; i++) { if (!fh->open[i]) { channel = i; break; } } if (i >= UVC_CHANNEL_MAX) { log_error("uvc no channel"); os_sem_post(&__this->sem); return -EINVAL; } *device = &fh->device[channel]; (*device)->private_data = fh; fh->video_dev[channel] = (struct video_device *)info->priv; fh->open[channel] = 1; fh->channel++; os_sem_post(&__this->sem); return 0; } } fh = (struct uvc_fh *)zalloc(sizeof(*fh)); if (!fh) { os_sem_post(&__this->sem); return -ENOMEM; } sprintf(name, "uvc%d", id); param.name = name;//node->name; if (info->f->pixelformat & VIDEO_PIX_FMT_H264) { param.uvc_stream_out = uvc_h264_stream_out; } else { param.uvc_stream_out = uvc_mjpg_stream_out; } param.uvc_out = uvc_dev_out; param.priv = fh; log_info("open uvc name = %s", name); fh->private_data = uvc_host_open(¶m);//初始化UVC主机 if (!fh->private_data) { free(fh); os_sem_post(&__this->sem); return -EINVAL; } if (info->priv) { fh->video_dev[channel] = (struct video_device *)info->priv; } #if UVC_RECV_BUFF_SHARE if (!fh->share_buf) { fh->share_buf = malloc(UVC_RECV_BUFF_SHARE_SIZE); if (!fh->share_buf) { log_error("uvc share_buf no men"); free(fh); os_sem_post(&__this->sem); return -ENOMEM; } } #endif os_sem_create(&fh->sem, 0); list_add_uvc(fh);//打开后添加到链表,在多通道打开则需要查找链表 os_sem_post(&fh->sem); *device = &fh->device[channel]; (*device)->private_data = fh; fh->id = id; fh->open[channel] = 1; fh->channel++; os_sem_post(&__this->sem); return 0; } static int uvc_querycap(struct uvc_fh *fh, struct uvc_capability *cap)//获取分辨率 { int num; struct uvc_frame_info *reso_table; os_sem_pend(&fh->sem, 0); num = uvc_host_get_pix_table(fh->private_data, &reso_table); if (num > (sizeof(cap->reso) / sizeof(cap->reso[0]))) { num = sizeof(cap->reso) / sizeof(cap->reso[0]); } memcpy(cap->reso, reso_table, sizeof(cap->reso[0]) * num); cap->fmt = UVC_CAMERA_FMT_MJPG; cap->reso_num = num; cap->fps = uvc_host_get_fps(fh->private_data); fh->fps = cap->fps; //printf("defualt : uvc_querycap fps = %d, %d*%d\n", cap->fps, cap->reso[cap->reso_num - 1].width, cap->reso[cap->reso_num - 1].height); os_sem_post(&fh->sem); return 0; } static int uvc_req_processing_unit(struct uvc_fh *fh, struct uvc_processing_unit *pu) { int err = 0; os_sem_pend(&fh->sem, 0); if (!fh->uvc_out) { err = uvc_host_req_processing_unit(fh->private_data, pu); } os_sem_post(&fh->sem); return err; } static int uvc_dev_reset(struct uvc_fh *fh) { int err = 0; os_sem_pend(&fh->sem, 0); if (!fh->uvc_out) { err = uvc_force_reset(fh->private_data); } os_sem_post(&fh->sem); return err; } static int usb_ioctl(struct uvc_fh *fh, u32 cmd, void *arg) { int err = 0; if (!fh) { return -EINVAL; } os_sem_pend(&fh->sem, 0); if (!fh->uvc_out) { err = uvc2usb_ioctl(fh->private_data, cmd, arg); } os_sem_post(&fh->sem); return err; } static int uvc_dev_ioctl(struct device *device, u32 cmd, u32 arg) { int ret = 0; struct uvc_fh *fh = (struct uvc_fh *)device->private_data; switch (cmd) { case UVCIOC_QUERYCAP: ret = uvc_querycap(fh, (struct uvc_capability *)arg); break; case UVCIOC_SET_CAP_SIZE: ret = uvc_host_set_pix(fh->private_data, (int)arg); break; case UVCIOC_REQBUFS: ret = uvc_dev_reqbufs(fh, (struct uvc_reqbufs *)arg); break; case UVCIOC_DQBUF: ret = uvc_dev_dqbuf(fh, (struct video_buffer *)arg); break; case UVCIOC_QBUF: ret = uvc_dev_qbuf(fh, (struct video_buffer *)arg); break; case UVCIOC_STREAM_ON: ret = uvc_stream_on(device, fh, arg); break; case UVCIOC_STREAM_OFF: ret = uvc_stream_off(device, fh, arg); break; case UVCIOC_GET_IMAMGE: ret = uvc_img_cap(fh, arg); break; case UVCIOC_RESET: ret = uvc_dev_reset(fh); break; case UVCIOC_REQ_PROCESSING_UNIT: ret = uvc_req_processing_unit(fh, (struct uvc_processing_unit *)arg); break; case UVCIOC_SET_CUR_GRAY: /*set_uvc_gray(arg);*/ break; case UVCIOC_SET_CUR_FPS: uvc_set_real_fps(fh, (u8)arg); break; default: ret = usb_ioctl(fh, cmd, (void *)arg); break; } return ret; } static int uvc_dev_close(struct device *device) { struct uvc_fh *fh = (struct uvc_fh *)device->private_data; if (fh) { if (fh->channel) { fh->channel--; } } if (fh && fh->channel == 0) {//通道都关闭了 os_sem_pend(&fh->sem, 0); uvc_host_close(fh->private_data); for (int i = 0; i < UVC_CHANNEL_MAX; i++) {//多通道关闭 if (&fh->device[i] == device) {//关闭是当前通道 fh->open[i] = 0; fh->video_dev[i] = NULL; memset(&fh->device[i], 0, sizeof(struct device)); } } if (fh->uvc_video_q) { videobuf_queue_release(&fh->video_q); } list_del_uvc(fh); if (fh->share_buf) { free(fh->share_buf); fh->share_buf = NULL; } os_sem_post(&fh->sem); free(fh); } return 0; } const struct device_operations uvc_dev_ops = { .init = uvc_dev_init, .open = uvc_dev_open, .ioctl = uvc_dev_ioctl, .close = uvc_dev_close, .online = uvc_dev_online, }; #endif