您的位置:首页 > 其它

从零写USB摄像头驱动之实现数据传输1_框架

2017-12-13 17:35 603 查看
dmesg命令缺点:

1 每次都去要输入查看

2 驱动程序有问题导致内核崩溃,无法使用dmesg命令

A. 设置ubuntu让它从串口0输出printk信息

a. 设置vmware添加serial port, 使用文件作为串口

01.



02.



03.



b. 启动ubuntu,修改/etc/default/grub如下:

vi  /etc/default/grub

GRUB_CMDLINE_LINUX_DEFAULT=""
GRUB_CMDLINE_LINUX="console=tty0 console=ttyS0,115200n8" //可以从屏幕、串口0上输出信息


完成后

sudo update-grub //更新
sudo reboot //重启


sudo insmod myuvc.ko //安装驱动

dmesg //查看信息



kernel_log.txt中并没有输出装载驱动的信息。

原因分析:cat /proc/sys/kernel/printk



c.修改打印级别

echo "8 4 1 7" > /proc/sys/kernel/printk


问题:



d. ubuntu禁止root用户登录

先修改root密码:
sudo passwd root


然后执行”su root”就可以用root登录了

rmmod  myuvc.ko
insmod  myuvc.ko
dmesg




B. 写代码:

usb摄像头驱动程序框架:

1.构造一个usb_driver
2.
.id_table:
.probe:
2.1. 分配video_device:video_device_alloc
2.2. 设置
.fops
.ioctl_ops (里面需要设置11项)
如果要用内核提供的缓冲区操作函数,还需要构造一个videobuf_queue_ops
2.3. 注册: video_register_device
3.注册: usb_register


注:id_table: 表示支持哪些USB设备

关于2.2参考myvivi.c

static const struct v4l2_ioctl_ops myvivi_ioctl_ops = {
// 表示它是一个摄像头设备
.vidioc_querycap      = myvivi_vidioc_querycap,

/* 用于列举、获得、测试、设置摄像头的数据的格式 */
.vidioc_enum_fmt_vid_cap  = myvivi_vidioc_enum_fmt_vid_cap,
.vidioc_g_fmt_vid_cap     = myvivi_vidioc_g_fmt_vid_cap,
.vidioc_try_fmt_vid_cap   = myvivi_vidioc_try_fmt_vid_cap,
.vidioc_s_fmt_vid_cap     = myvivi_vidioc_s_fmt_vid_cap,

/* 缓冲区操作: 申请/查询/放入队列/取出队列 */
.vidioc_reqbufs       = myvivi_vidioc_reqbufs,
.vidioc_querybuf      = myvivi_vidioc_querybuf,
.vidioc_qbuf          = myvivi_vidioc_qbuf,
.vidioc_dqbuf         = myvivi_vidioc_dqbuf,

// 启动/停止
.vidioc_streamon      = myvivi_vidioc_streamon,
.vidioc_streamoff     = myvivi_vidioc_streamoff,
};


参考 myvivi.c写myuvc.c

static int myuvc_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
...
if(cnt == 2) //myuvc_ids有VideoControl Interface、VideoStreaming Interface,在第二个注册
{
/*1.分配一个video_device结构体*/
myuvc_vdev =video_device_alloc();

/*2.设置*/
/*2.1*/
myuvc_vdev->release =myuvc_release;
/*2.2*/
myuvc_vdev->fops =&myuvc_fops;
/*2.3*/
myuvc_vdev->ioctl_ops =&myuvc_ioctl_ops;

/*3.注册*/
video_register_device(myuvc_vdev,VFL_TYPE_GRABBER, -1);
}
}


注:缺少myuvc_release将无法注册

video_register_device(myuvc_vdev, VFL_TYPE_GRABBER, -1);//在myuvc.c中
video_register_device_index
/* the release callback MUST be present */
if (WARN_ON(!vdev->release))
return -EINVAL;


这里写一个myuvc_release空函数:

/**写一个myuvc_release空函数/
static void myuvc_release(struct video_device *vdev)
{
}


static void myuvc_disconnect(struct usb_interface *intf)
{
static int cnt = 0;
printk("myuvc_disconnect : cnt = %d\n", cnt++);

if (cnt == 2)
{
video_unregister_device(myuvc_vdev);
video_device_release(myuvc_vdev);
}
}


linux驱动之–fops的关联

http://blog.csdn.net/zssmcu/article/details/6746770

static const struct v4l2_file_operations myuvc_fops = {
.owner      = THIS_MODULE,
.open       = myuvc_open,
.release    = myuvc_close,
.mmap       = myuvc_mmap,
.ioctl      = video_ioctl2, /* V4L2 ioctl handler */
.poll       = myuvc_poll,
};


由myuvc_fops 结构,接下里逐个完成 myuvc_open,video_ioctl2, myuvc_poll。

这里完成框架性的东西,不写内容

static int myuvc_open(struct file *file)
{
return 0;
}


v4l2_ioctl_ops结构体调用过程:

myuvc_fops
video_ioctl2
__video_do_ioctl
v4l2_ioctl_ops
找到v4l2_ioctl_ops结构体


static const struct v4l2_ioctl_ops myuvc_ioctl_ops = {
// 表示它是一个摄像头设备
.vidioc_querycap      = myuvc_vidioc_querycap,//查询性能

/* 用于列举、获得、测试、设置摄像头的数据的格式 */
.vidioc_enum_fmt_vid_cap  = myuvc_vidioc_enum_fmt_vid_cap,
.vidioc_g_fmt_vid_cap     = myuvc_vidioc_g_fmt_vid_cap,
.vidioc_try_fmt_vid_cap   = myuvc_vidioc_try_fmt_vid_cap,
.vidioc_s_fmt_vid_cap     = myuvc_vidioc_s_fmt_vid_cap,

/* 缓冲区操作: 申请/查询/放入队列/取出队列 */
.vidioc_reqbufs       = myuvc_vidioc_reqbufs,
.vidioc_querybuf      = myuvc_vidioc_querybuf,
.vidioc_qbuf          = myuvc_vidioc_qbuf,
.vidioc_dqbuf         = myuvc_vidioc_dqbuf,

// 启动/停止
.vidioc_streamon      = myuvc_vidioc_streamon,
.vidioc_streamoff     = myuvc_vidioc_streamoff,
};


注:整体流程

应用程程序(app)打开设备(调用myuvc_open),确定(调用myuvc_vidioc_querycap)它是视频设备,获取格式信息,申请缓冲区,查询缓冲区信息,将缓冲区映射到用户空间,把缓冲区放入队列,启动传输。

下面是关于myuvc_ioctl_ops结构体中各个单元的实现

/* A2 查询性能*/
static int myuvc_vidioc_querycap(struct file *file, void  *priv,
struct v4l2_capability *cap)
{
return 0;
}

/* A3 列举支持哪种格式 */
static int myuvc_vidioc_enum_fmt_vid_cap(struct file *file, void  *priv,
struct v4l2_fmtdesc *f)
{
return 0;
}

/* A4 返回当前所使用的格式 */
static int myuvc_vidioc_g_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
return (0);
}

/* A5 测试驱动程序是否支持某种格式 */
static int myuvc_vidioc_try_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
return 0;
}
/* A6 */
static int myuvc_vidioc_s_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
return 0;
}

/* A7 APP调用该ioctl让驱动程序分配若干个缓存, APP将从这些缓存中读到视频数据 */
static int myuvc_vidioc_reqbufs(struct file *file, void *priv,
struct v4l2_requestbuffers *p)
{
return 0;
}

/* A8 查询缓存状态, 比如地址信息(APP可以用mmap进行映射)*/
static int myuvc_vidioc_querybuf(struct file *file, void *priv, struct v4l2_buffer *p)
{
return 0;
}

/* A10 把缓冲区放入队列, 底层的硬件操作函数将会把数据放入这个队列的缓存 */
static int myuvc_vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *p)
{
return 0;
}

/* A11 启动传输 */
static int myuvc_vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
{
return 0;
}

/* A13 APP通过poll/select确定有数据后, 把缓存从队列中取出来
*/
static int myuvc_vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p)
{
return 0;
}

/*
* A14 之前已经通过mmap映射了缓存, APP可以直接读数据
* A15 再次调用myuvc_vidioc_qbuf把缓存放入队列
* A16 poll...
*/

/* A17 停止 */
static int myuvc_vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
{
return 0;
}


rmmod myuvc.ko //删除以前的驱动,将usb摄像头插入电脑,可以看到usb设备。



ls /dev/vid*



linux系统自动装载了摄像头驱动,将它删除,装入myuvc.ko

sudo rmmod  uvcvideo


su root
insmod myuvc.ko
echo "8 4 1 7" > /proc/sys/kernel/printk


可以在输出文件看到内核的信息

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: