#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
typedef unsigned char UI_08;
typedef unsigned short UI_16;
typedef unsigned int UI_32;
typedef char SI_08;
typedef short SI_16;
typedef int SI_32;
static field_uvc_streaming_control
{
__u16 bmHint;
__u8 bFormatIndex;
__u8 bFrameIndex;
__u32 dwFrameInterval;
__u16 wKeyFrameRate;
__u16 wPFrameRate;
__u16 wCompQuality;
__u16 wCompWindowSize;
__u16 wDelay;
__u32 dwMaxVideoFrameSize;
__u32 dwMaxPayloadTransferSize;
__u32 dwClockFrequency;
__u8 bmFramingInfo;
__u8 bPreferedVersion;
__u8 bMinVersion;
__u8 bMaxVersion;
};
struct frame_desc
{
int width;
int height;
};
struct field_uvc_buffer
{
struct v4l2_buffer buf;
int state;
int vma_use_count;
wait_queue_head_t wait;
struct list_head stream;
struct list_head irq;
};
struct field_uvc_queue
{
void *mem;
int count;
int buf_size;
struct field_uvc_buffer buffer[32];
struct urb *urb[32];
char *urb_buffer[32];
dma_addr_t urb_dma[32];
unsigned int urb_size;
struct list_head mainqueue;
struct list_head irqqueue;
};
static struct field_uvc_queue field_uvc_queue;
static struct video_device *field_uvc_vdev;
static struct usb_device *field_uvc_udev;
static struct v4l2_format field_uvc_format;
static int field_uvc_video_querycap(struct file *file, void *priv, struct v4l2_capability *cap)
{
memset(cap,0,sizeof(*cap));
strcpy(cap->driver,"fieldUVC");
strcpy(cap->card,"fieldUVC");
cap->version=1;
cap->capabilities=V4L2_CAP_VIDEO_CAPTURE|V4L2_CAP_STREAMING;
return 0;
}
static int field_uvc_videoc_enum_fmt_vid_cap(struct file *file, void *priv, struct v4l2_fmtdesc *f)
{
if(f->index>=1)
return -EINVAL;
strcpy(f->description,"4:2:2,packed,YUYV");
f->pixelformat=V4L2_PIX_FMT_YUYV;
return 0;
}
static int field_uvc_videoc_get_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f)
{
memcpy(f,&field_uvc_format,sizeof(field_uvc_format));
return 0;
}
static int field_uvc_videoc_reqbufs(struct file *file, void *priv, struct v4l2_requestbuffers *p)
{
int nbuffers=p->count;
int bufsize=PAGE_ALIGN(field_uvc_format.fmt.pix.sizeimage);
unsigned int i;
void *mem=NULL;
int ret;
if((ret=field_uvc_free_buffers())<0)
goto done;
if(nbuffers==0)
goto done;
for(;nbuffers>0;--nbuffers)
{
mem=vmalloc_32(nbuffers*bufsize);
if(mem!=NULL)
break;
}
if(mem==NULL)
{
ret=-ENOMEM;
goto done;
}
memset(&field_uvc_queue,0,sizeof(field_uvc_queue));
INIT_LIST_HEAD(&field_uvc_queue.mainqueue);
INIT_LIST_HEAD(&field_uvc_queue.irqqueue);
for(i=0;i
{
field_uvc_queue.buffer[i].buf.index=i;
field_uvc_queue.buffer[i].buf.m.offset=i*bufsize;
field_uvc_queue.buffer[i].buf.length=field_uvc_format.fmt.pix.sizeiamge;
field_uvc_queue.buffer[i].buf.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
field_uvc_queue.buffer[i].buf.sequence=0;
}
}
static int field_uvc_videoc_qbuf(struct file *file, void *priv, struct v4l2_buffer *v4l2_buf)
{
struct field_uvc_buffer *buf;
if(v4l2_buf->type!=V4L2_BUF_TYPE_VIDEO_CAPTURE||v4l2_buf->memory!=V4L2_MEMORY_MMAP)
return -EINVAL;
if(v4l2_buf->index>=field_uvc_queue.count)
return -EINVAL;
buf=&field_uvc_queue.buffer[v4l2_buf->index];
if(buf->state!=VIDEOBUF_IDLE)
return -EINVAL;
buf->state=VIDEOBUF_QUEUED;
buf->buf.bytesused=0;
list_add_tail(&buf->stream,&field_uvc_queue.mainqueue);
list_add_tail(&buf->irq,&field_uvc_queue.irqqueue);
return 0;
}
static int field_uvc_set_streaming_params(struct field_uvc_streaming_contrl *ctrl)
{
__u8 *data;
__u16 size;
int ret;
__8 type=USB_TYPE_CLASS|USB_RECIP_INTERFACE;
unsigned int pipe;
size=uvc_version >=0x0110?34:26;
data=kzalloc(size,GFP_KERNEL);
if(data==NULL)
return -ENOMEM;
*(__le16 *)&data[0]=cpu_to_le16(ctrl->bmHint);
data[2]=ctrl->bFormatIndex;
data[3]=ctrl->bFrameIndex;
*(__le32 *)&data[4]=cpu_to_le32(ctrl->dwFrameInterval);
*(__le16 *)&data[8]=cpu_to_le16(ctrl->wKeyFrameRate);
*(__le16 *)&data[10]=cpu_to_le16(ctrl->wPFrameRate);
*(__le16 *)&data[12]=cpu_to_le16(ctrl->wCompQuality);
*(__le16 *)&data[14]=cpu_to_le16(ctrl->wCompWindowSize);
*(__le16 *)&data[16]=cpu_to_le16(ctrl->wDelay);
put_unaligned_le32(ctrl->dwMaxVideoFrameSize,&data[18]);
put_unaligned_le32(ctrl->dwMaxPayloadTransferSize,&data[22]);
pipe=(SET_CUR&0x80)?usb_rcvctrlpipe(field_uvc_udev,0):usb_sndctrlpipe(field_uvc_udev,0);
type|=(SET_CUR&0x80)?USB_DIR_IN:USB_DIR_OUT;
ret=usb_contrl_msg(field_uvc_udev,pipe,SET_CUR,type,VS_COMMIT_CONTROL<<8,0<<8|field_uvc_streaming_intf,data,size,5000);
kfree(data);
return (ret<0)?ret:0;
}
static void field_uvc_video_complete(struct urb *urb)
{
u8 *src;
u8 *dest;
int ret,i;
int len;
int maxlen;
int nbytes;
struct field_uvc_buffer *buf;
switch(urb->status)
{
case 0:
break;
default:
printk("Non-zero status %d in video completion handler.\n",urb->status);
}
//从irqqueue队列中取出第1个缓冲区
if(!list_empty(&field_uvc_queue.irqqueue))
{
buf=list_first_entry(&field_uvc_queue.irqqueue,struct field_uvc_buffer,irq);
for(i=0;inumber_of_packets;++i)
{
if(urb->iso_frame_desc[i].status<0)
{
printk("USB isochronous frame lost %d\n",urb->iso_frame_desc[i].status);
continue;
}
src=urb->transfer_buffer+urb->iso_frame_desc[i].offset;
dest=field_uvc_queue.mem+buf->buf.m.offset+buf->buf.bytesused;
len=urb->iso_frame_desc[i].actual_length;
//URB数据含义: data[0]-头部长度,data[1]-错误状态
if(len<2||src[0]<2||src[0]>len)
continue;
//skip paylaod marked with the error bitops
if(src[1]&UVC_STREAM_ERR)
{
printk("Dropping payload error bit set");
continue;
}
//除去头部后的数据长度
len-=src[0];
//缓冲区{BANNED}最佳多还能存多少数据
maxlen=buf->buf.length-buf->buf.bytesused;
nbytes=min(len,maxlen);
//copy data
memcpy(desc,src+src[0],nbytes);
buf->buf.bytesused+=nbytes;
//mark the buffer as done if the EOF marker is set
if(src[1]&UVC_STREAM_EOF&&buf->buf.bytesused!=0)
{
printk("Frame complete eof fount\n");
buf->state=VIDEOBUF_DONE;
}
}
//wake up the waiting process
if(buf->state==VIDEOBUF_DONE||buf->state==VIDEOBUF_ERROR)
{
list_del(&buf->irq);
wake_up(&buf->wait);
}
}
if((ret=usb_submit_urb(urb,GFP_ATOMIC))<0)
printk("Failed to resubmit urb %d\n",ret);
}
static int field_uvc_alloc_init_urbs(void)
{
u16 psize;
u32 size;
int npackets;
int i,j;
struct urb *urb;
psize=wMaxPacketSize; //实际传输端点一次能传输的{BANNED}最佳大字节数
size=field_uvc_params.dwMaxVideoFrameSize;
for(i=0;i
{
field_uvc_queue.urb_buffer[i]=usb_buffer_alloc(field_uvc_udev,size,GFP_KERNEL|__GFP_NOWARN,&field_uvc_queue.urb_dma[i]);
field_uvc_queue.urb[i]=usb_alloc_urb(npackets,GFP_KERNEL);
if(!field_uvc_queue.urb_buffer[i]||!field_uvc_queue.urb[i])
{
field_uvc_uninit_urbs();
return -ENOMEM;
}
}
for(i=0;i
{
urb=field_uvc_queue.urb[i];
urb->dev=field_uvc_udev;
urb->context=NULL;
urb->pipe=usb_rcvisocpipe(field_uvc_udev,field_uvc_bEndpointAddress);
urb->transfer_flags=URB_ISO_ASAP|URB_NO_TRANSFER_DMA_MAP;
urb->interval=1;
urb->transfer_buffer=field_uvc_queue.urb_buffer[i];
urb->transfer_dma=field_uvc_queue.urb_dma[i];
urb->complete=field_uvc_video_complete;
urb->number_of_packets=npackets; //要传输的数据次数
urb->transfer_buffer_length=size; //总共的数据量
for(j=0;j
{
urb->iso_frame_desc[j].offset=j*psize; //存放每次传输的数据
urb->iso_frame_desc[j].length=psize;
}
}
return 0;
}
static void field_uvc_uninit_urbs(void)
{
int i;
for(i=0;i
{
if(field_uvc_queue.urb_buffer[i])
{
usb_buffer_free(field_uvc_udev,field_uvc_queue.urb_size,field_uvc_queue.urb_buffer[i],field_uvc_queue.urb_dma[i]);
field_uvc_queue.urb_buffer[i]=NULL;
}
if(field_uvc_queue.urb[i])
{
usb_free_urb(field_uvc_queue.urb[i]);
field_uvc_queue.urb[i]=NULL;
}
}
}
static int field_uvc_videoc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *v4l2_buf)
{
struct field_uvc_buffer *buf;
int ret=0;
if(list_empty(&field_uvc_queue.mainqueue))
{
ret=-EINVAL;
goto done;
}
buf=list_first_entry(&field_uvc_queue.mainqueue,struct field_uvc_buffer,stream);
}
static int field_uvc_videoc_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
{
int ret;
ret=field_uvc_try_streaming_params(&field_uvc_params);
}
static int fiedl_uvc_videoc_streamoff(struct file *file, void *priv, enum v4l2_buf_type t)
{
struct urb *urb;
unsigned int i;
for(i=0;i
{
if(urb=field_uvc_queue.urb[i]==NULL)
continue;
usb_kill_urb(urb);
}
field_uvc_uinit_urbs();
usb_set_interface(field_uvc_udev,field_uvc_streaming_intf,0);
return 0;
}
static __s32 field_uvc_get_le_value(const __u8 *data)
{
int bits=16;
int offset=0;
__s32 value=0;
__u8 mask;
data+=offset/8;
offset&=7;
mask=((1LL<
for(;bits>0;data++)
{
__u8 byte=*data&mask;
value|=offset>0?(byte>>offset):(byte<<(-offset));
bits-=8-(offset>0?offset:0);
offset-=8;
mask=(1<
}
value|=-(value&(1<<(16-1)));
return value;
}
static void field_uvc_set_le_value(__s32 value, __u8 *data)
{
int bits=16;
int offset=0;
__u8 mask;
data+=offset/8;
offset&=7;
for(;bits>0;data++)
{
mask=((1LL<
*data=(*data&~mask)|((value<
value>>=offset?offset:8;
bits-=8-offset;
offset=0;
}
}
int field_uvc_videoc_queryctrl(struct file *file, void *fn, struct v4l2_queryctrl *ctrl)
{
__u8 type=USB_TYPE_CLASS|USB_RECIP_INTERFACE;
unsigned int pipe;
int ret;
u8 data[2];
if(ctrl->id!=V4L2_CID_BRIGHTNESS)
return -EINVAL;
memset(ctrl,0,sizeof(*ctrl));
ctrl->id=V4L2_CID_BRIGHTNESS;
ctrl->type=V4L2_CTRL_TYPE_INTEGER;
strcpy(ctrl->name,"field_uvc_brightness");
ctrl->flags=0;
pipe=usb_rcvctrlpipe(field_uvc_udev,0);
type|=USB_DIR_IN;
ret=usb_contrl_msg(field_uvc_udev,pipe,GET_MIN,type,PU_BRIGHTNESS_CONTROL<<8,
ProcessingUnitID<<8|field_uvc_control_intf,data,2,5000);
if(ret!=2)
return -EIO;
ctrl->minimum=field_uvc_get_le_value(data);
ret=usb_contrl_msg(field_uvc_udev,pipe,GET_MAX,type,PU_BRIGHTNESS_CONTROL<<8,
ProcessingUnitID<<8|field_uvc_control_intf,data,2,5000);
if(ret!=2)
return -EIO;
ctrl->maximum=field_uvc_get_le_value(data);
ret=usb_contrl_msg(field_uvc_udev,pipe,GET_RES,type,PU_BRIGHTNESS_CONTROL<<8,
ProcessingUnitID<<8|field_uvc_control_intf,data,2,5000);
if(ret!=2)
return -EIO;
ctrl->step=field_uvc_get_le_value(data);
ret=usb_control_msg(field_uvc_udev,pipe,GET_DEF,type,PU_BRIGHTNESS_CONTROL<<8,
ProcessingUnitID<<8|field_uvc_control_intf,data,2,5000);
if(ret!=2)
return -EIO;
ctrl->default_value=field_uvc_get_le_value(data);
return 0;
}
int field_uvc_videoc_g_ctrl(struct file *file, void fn, struct v4l2_control *ctrl)
{
__u8 type=USB_TYPE_CLASS|USB_RECIP_INTERFACE;
unsigned int pipe;
int ret;
u8 data[2];
if(ctrl->id!=V4L2_CID_BRIGHTNESS)
return -EINVAL;
pipe=usb_rcvctrlpipe(field_uvc_udev,0);
type|=USB_DIR_IN;
ret=usb_control_msg(field_uvc_udev,pipe,GET_CUR,type,PU_BRIGHTNESS_CONTROL<<8,
ProcessingUnitID<<8|field_uvc_control_intf,data,2,5000);
if(ret!=2)
return -EIO;
ctrl->value=field_uvc_get_le_value(data);
return 0;
}
int field_uvc_videoc_s_ctrl(struct file *file, void *fn, struct v4l2_conctrol *ctrl)
{
__u8 type=USB_TYPE_CLASS|USB_RECIP_INTERFACE;
unsigned int pipe;
int ret;
u8 data[2];
if(ctrl->id!=V4L2_CID_BRIGHTNESS)
return -EINVAL;
field_uvc_set_le_value(ctrl->value,data);
pipe=usb_sndctrlpipe(field_uvc_udev,0);
type|=USB_DIR_OUT;
ret=usb_contrl_msg(field_uvc_udev,pipe,SET_CUR,type,PU_BRIGHTNESS_CONTROL<<8,
ProcessingUnitID<<8|field_uvc_control_intf,data,2,5000);
if(ret!=2)
return -EIO;
return 0;
}
static const struct v4l2_ioctl_ops field_uvc_ioctl_ops = {
.videoc_querycap=field_uvc_videoc_querycap,
.videoc_enum_fmt_vid_cap=field_uvc_videoc_enum_fmt_vid_cap,
.videoc_g_fmt_vid_cap=field_uvc_videoc_get_fmt_vid_cap,
};
static void field_uvc_wm_open(struct vm_area_struct *vma)
{
struct field_uvc_buffer *buffer=vma->vm_private_data;
buffer->vma_use_count++;
}
static void field_uvc_vm_close(struct vm_area_struct *vma)
{
struct field_uvc_buffer *buffer=vma->vm_private_data;
buffer->vma_use_count--;
}
static struct vm_operations_struct field_uvc_vm_ops = {
.open=field_uvc_vm_open,
.close=field_uvc_vm_close,
};
static int field_uvc_mmap(struct file *file, struct vm_area_struct *wma)
{
struct field_uvc_buffer *buffer;
struct page *page;
unsigned long addr,start,size;
unsigned int i;
int ret=0;
start=vma->vm_start;
size=vma->vm_end-vma->vm_start;
for(i=0;i
{
buffer=&field_uvc_queue.buffer[i];
if((buffer->buf.m.offset>>PAGE_SHIFT)==vma_pgoff)
break;
}
if(i==field_uvc_queue.count||size!=field_uvc_queue.buf_size)
{
ret=-EINVAL;
goto done;
}
wma->vm_flags|=VM_IO;
addr=(unsigned long)field_uvc_queue.mem+buffer->buf.m.offset;
while(size>0)
{
page=vmalloc_to_page((void*)addr);
if((ret=vm_insert_page(vma,start,page))<0)
goto done;
start+=PAGE_SIZE;
addr+=PAGE_SIZE;
size-=PAGE_SIZE;
}
vma->vm_ops=&field_uvc_vm_ops;
vma->vm_private_data=buffer;
field_uvc_vm_open(vma);
done:
return ret;
}
static unsigned int field_uvc_poll(struct file *file, struct poll_table_struct *wait)
{
struct field_uvc_buffer *buf;
unsigned int mask=0;
if(list_empty(&field_uvc_queue.mainqueue))
{
mask|=POLLERR;
goto done;
}
buf=list_first_entry(&field_uvc_queue.mainqueue,struct field uvc_buffer,stream);
poll_wait(file,&buf->wait,wait);
if(buf->state==VIDEOBUF_DONE||buf->state==VIDEOBUF_ERROR)
mask|=POLLIN|POLLRDNORM;
done:
return mask;
}
static int field_uvc_close(struct file *file)
{
}
static const struct v4l2_field_operations field_uvc_fops = {
.owner=THIS_MODULE,
.open=field_uvc_open,
.release=field_uvc_close,
.mmap=field_uvc_mmap,
.ioctl=field_uvc_ioctl,
.poll=field_uvc_poll,
};
static void field_uvc_release(struct video_device *vdev)
{
}
static int field_uvc_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
static int cnt;
//根据interface结构体获得usb_device结构体,其中包含了设备描述符
struct usb_device *dev=interface_to_usbdev(intf);
//设备描述符结构体
struct usb_device_descriptor *descriptor=&dev->descriptor;
//配置描述符
struct usb_host_config *host_config;
struct usb_config_descriptor *config;
//接口联合体描述符,获得IAD接口
struct usb_interface_assoc_descriptor *assoc_desc;
//接口描述符
struct usb_interface_descriptor *interface;
//端点描述符
struct usb_endpoint_descriptor *endpoint;
field_uvc_udev=dev;
if(cnt==1)
field_uvc_control_intf=intf->cur_altsetting->desc.bInterfaceNumber;
else if(cnt==2)
field_uvc_streaming_intf=intf->cur_altsetting->desc.bInterfaceNumber;
if(cnt==2)
{
field_uvc_udev=video_device_alloc();
field_uvc_udev->release=field_uvc_release;
field_uvc_udev->fops=&field_uvc_fops;
field_uvc_udev->ioctl_ops=&field_uvc_ioctl_ops;
video_register_device(field_uvc_udev,VFL_TYPE_GRABBER,-1);
}
}
static int uvc_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
struct usb_device *udev=interface_to_usbdev(intf);
struct uvc_device *uvc_dev;
if(id->idVendor && id->idProduct)
printk("Probing known UVC device %s (%04x:%04x)\n",udev->devpath,id->idVendor,id->idProduct);
else
printk("Probing generic UVC device %s\n",udev->devpath);
if((uvc_dev=kzalloc(sizeof(*dev),GFP_KERNEL)) == NULL)
return -ENOMEM;
INIT_LIST_HEAD(&uvc_dev->entities);
INIT_LIST_HEAD(&uvc_dev->chains);
INIT_LIST_HEAD(&uvc_dev->streams);
atomic_set(&uvc_dev->nstreams,0);
atomic_set(&uvc_dev->nmappings,0);
mutex_init(&uvc_dev->lock);
uvc_dev->udev=usb_get_dev(udev);
uvc_dev->intf=usb_get_intf(intf);
uvc_dev->intfnum=intf->cur_altsetting->desc.bInterfaceNumber;
uvc_dev->quirks=(uvc_quirks_param == -1)? id->driver_info : uvc_quirks_param;
if(udev->product!=NULL)
strlcpy(uvc_dev->name,udev->product,sizeof uvc_dev->name);
else
snprintf(uvc_dev->name,sizeof dev->name,"UVC Camera (%04x:%04x)",le16_to_cpu(udev->descriptor.idVendor),le16_to_cpu(udev->descriptor.idProduct));
if(uvc_parse_control(uvc_dev) < 0)
{
printk("Unable to parse UVC descriptors\n");
goto error;
}
printk("Found UVC %u.%02x device %s (%04x:%04x)\n",uvc_dev->uvc_version>>8,uvc_dev->uvc_version&0xff,
udev->product?udev->product:"unnamed",le16_to_cpu(udev->descriptor.idVendor),le16_to_cpu(udev->descriptor.idProduct));
if(uvc_dev->quirks != id->driver_info)
{
printk("Forcing device quirks to 0x%x by module parameter for testing purpose\n",uvc_dev->quirks);
printk("Please report required quirks to the linux-uvc-devel mailing list\n");
}
if(v4l2_device_register(&intf->dev,&uvc_dev->udev) < 0)
goto error;
}
static void field_uvc_disconnect(struct usb_interface *intf)
{
video_unregister_device(field_uvc_udev);
video_device_release(field_uvc_udev);
}
static struct usb_device_id field_uvc_ids[] = {
{USB_INTERFACE_INFO(USB_CLASS_VIDEO,1,0)},
{USB_INTERFACE_INFO(USB_CLASS_VIDEO,2,0)},
{}
};
static struct usb_driver field_uvc_driver = {
.name = "field_uvc",
.probe = field_uvc_probe,
.disconnect = field_uvc_disconnect,
.id_table = field_uvc_ids,
};
static int __init field_uvc_init(void)
{
usb_register(&field_uvc_driver);
return 0;
}
static void __exit field_uvc_exit(void)
{
usb_deregister(&field_uvc_driver);
}
module_init(field_uvc_init);
module_exit(field_uvc_exit);
MODULE_LICENSE("GPL");
阅读(858) | 评论(0) | 转发(0) |