1.lsusb
lsusb -v -d 0x2bdf: 查看usb描述符等信息 (Ubuntu中使用)2bdf为ID号
2.usb驱动程序框架
#include <linux/atomic.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/usb.h>
#include <linux/videodev2.h>
#include <linux/vmalloc.h>
#include <linux/wait.h>
#include <linux/version.h>
#include <asm/unaligned.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/font.h>
#include <linux/mutex.h>
#include <linux/platform_device.h>
#include <linux/v4l2-dv-timings.h>
#include <media/videobuf2-vmalloc.h>
#include <media/v4l2-dv-timings.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-fh.h>
#include <media/v4l2-event.h>
#include <media/v4l2-common.h>
static struct video_device *myuvc_vdev;
static struct usb_device_id myuvc_ids[] = {
/* Generic USB Video Class */
{ USB_INTERFACE_INFO(USB_CLASS_VIDEO, 1, 0) },/*vdieocontral interface*/
{ USB_INTERFACE_INFO(USB_CLASS_VIDEO, 2, 0) },
{}
};
/*A2 确定他是否是视频设备*/
static int vidioc_querycap(struct file *file, void *priv,
struct v4l2_capability *v)
{
return 0;
}
/* A3 列举支持哪种格式*/
static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_fmtdesc *f)
{
return 0;
}
/*A4 返回当前所使用的格式*/
static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
return 0;
}
/*A5 测试驱动程序是否支持某种格式*/
static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
return 0;
}
/*A6*/
static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
return 0;
}
/*A7 APP调用该ioctl让驱动分配若干个缓存,APP将从这些缓存里面读取视频数据*/
int vb2_ioctl_reqbufs(struct file *file, void *priv,
struct v4l2_requestbuffers *p)
{
return 0;
}
/*A8 查询缓存状态,比如地址信息(APP可以用)*/
int vb2_ioctl_querybuf(struct file *file, void *priv, struct v4l2_buffer *p)
{
return 0;
}
/*A10 把缓冲区放入队列,底层硬件操作函数将会把数据放入这个队列的缓存*/
int vb2_ioctl_qbuf(struct file *file, void *priv, struct v4l2_buffer *p)
{
return 0;
}
/*A13 APP通过poll/select确定有数据之后,把缓存从队列中取出来*/
/*A14 之前已经通过mmap映射了缓存,APP可以直接读取数据*/
/*A15 在此调用vb2_ioctl_qbuf把缓存放入队列*/
/*A16 调用poll*/
/*又进入A13,开始进行循环,直到vb2_ioctl_streamoff 退出*/
int vb2_ioctl_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p)
{
return 0;
}
/*A11 启动传输*/
int vb2_ioctl_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
{
return 0;
}
/*A17 停止*/
int vb2_ioctl_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
{
return 0;
}
static const struct v4l2_ioctl_ops myuvc_fops ={
// 表示他是一个摄像头设备
.vidioc_querycap = vidioc_querycap,
/*用于列举,获得,测试,设置摄像头的数据的格式*/
.vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
.vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
.vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
.vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
/*缓冲区操作:申请,查询,放入队列,取出队列*/
.vidioc_reqbufs = vb2_ioctl_reqbufs,
.vidioc_querybuf = vb2_ioctl_querybuf,
.vidioc_qbuf = vb2_ioctl_qbuf,
.vidioc_dqbuf = vb2_ioctl_dqbuf,
/*启动,停止*/
.vidioc_streamon = vb2_ioctl_streamon,
.vidioc_streamoff = vb2_ioctl_streamoff,
};
static int myuvc_release(struct video_device *vdev)
{
return 0;
}
/*A1*/
static int myuvc_open(struct file *filp)
{
return 0;
}
/*A18 关闭*/
static int myuvc_close(struct file *filp)
{
return 0;
}
/*A9 把缓存映射到APP的空间,以后APP就可以直接操作了*/
static int myuvc_mmap(struct file *file, struct vm_area_struct *vma)
{
return 0;
}
/*A12 APP调用poll/select 来确定缓存是有就绪(有数据)*/
unsigned int myuvc_poll(struct file *file, poll_table *wait)
{
return 0;
}
static const struct v4l2_file_operations myuvc_fops = {
.owner = THIS_MODULE,
.open = myuvc_open,
.close = myuvc_close,
.release = myuvc_release,
.ioctl = video_ioctl2,
.mmap = myuvc_mmap,
.poll = myuvc_poll,
};
static int myuvc_probe(struct usb_interface *intf,const struct usb_device_id *id) //usb_interface->usb_host_interface
{
int m,j
struct usb_device *udev = interface_to_usbdev(intf); /*可在结构体usb_device获得设备描述符:struct usb_device_descriptor descriptor;*/
struct usb_device_descriptor *descriptor = &udev->descriptor;
/*获取IAD*/
struct usb_interface_assoc_descriptor *assoc_desc;
assoc_desc = udev->actconfig->intf_assoc[0];
/*视频流接口中包括一个接口和与其对应的多个转换设置接口(Alternate Setting)。打印接口描述符*/
struct usb_interface_descriptor *interface;
struct usb_endpoint_descriptor *endpoint;
static int count = 0;
printk("disconnect %d\n",count++);
if (count==2) /*因为ids里面有两个 probe会被调用两次*/
{
/*1. 分配一个 video_device结构体*/
myuvc_vdev = video_device_alloc();
/*2. 设置*/
/*3. 注册*/
video_register_device(myuvc_vdev, VFL_TYPE_GRABBER,-1)/*-1则为自动分配次设备号*/;
}
return 0;
};
static void myuvc_disconnect(struct usb_interface *intf)
{
static int count = 0;
printk("disconnect %d\n",count++);
if (count==2) /*因为ids里面有两个 probe会被调用两次*/
{
video_unregister_device(myuvc_vdev);
myuvc_vdev = video_device_release();
}
};
struct usb_driver myuvc_driver = {
.name = "my_uvcvideo",
.probe = myuvc_probe,
.disconnect = myuvc_disconnect,
//.suspend = uvc_suspend,
//.resume = uvc_resume,
//.reset_resume = uvc_reset_resume,
.id_table = myuvc_ids,
//.supports_autosuspend = 1,
};
static int myuvc_init(void)
{
int ret ;
ret = usb_register(&myuvc_driver);
if (ret < 0) {
return ret;
}
return 0;
}
static void myuvc_cleanup(void)
{
usb_deregister(&myuvc_driver);
}
module_init(myuvc_init);
module_exit(myuvc_cleanup);
MODULE_LICENSE("GPL");