usb声卡驱动(三):内核对usb操作的支持

usb声卡驱动

前面的笔记,介绍了,USB。接下来,就是,看看linux内核中,如何操作usb

在linux内核中,加入了USB core。大部分的工作都已经放入了这个USB core中,我们现在只需要知道,怎么跟USB core进行互动即可。

USB core互动的关键数据结构是urb(USB 请求块)

它的生命周期,概述如下:

  1. 由usb设备驱动程序创建
  2. 分配给一个特定usb设备的特定端点
  3. 由usb设备驱动程序,递交给USB core
  4. 由usb核心递交到特定设备的特定usb主控制器驱动程序
  5. usb主控制器驱动程序,处理具体的传输
  6. 当传输完成,usb主控制器驱动程序通知usb设备驱动程序。

urb


struct urb {
	/* private: usb core and host controller only fields in the urb */
	struct kref kref;		/* URB引用计数 */
	void *hcpriv;			/* 给host controller的私有数据 */
	atomic_t use_count;		/* 并发提交计数器 */
	atomic_t reject;		/* 提交失败 */
	int unlinked;			/* unlink 错误码 */

	/* public: documented fields in the urb that can be used by drivers */
	struct list_head urb_list;	/*  给当前用户使用的列表头 */
	struct list_head anchor_list;	/* the URB may be anchored */
	struct usb_anchor *anchor;
	struct usb_device *dev;		/* 指向相关设备的指针 */
	struct usb_host_endpoint *ep;	/* 指向端点的指针*/
	unsigned int pipe;		/* 管道信息 */
	unsigned int stream_id;		/* 流ID*/
	int status;			/* 非标准状态的返回 */
	unsigned int transfer_flags;	/* 传输标志*/
	void *transfer_buffer;		/* buffer指针 */
	dma_addr_t transfer_dma;	/* DMA缓冲区 */
	struct scatterlist *sg;		/* (in) scatter gather buffer list */
	int num_mapped_sgs;		/* (internal) mapped sg entries */
	int num_sgs;			/* (in) number of entries in the sg list */
	u32 transfer_buffer_length;	/* (in) 数据缓冲区长度 */
	u32 actual_length;		/* 返回实际传输的长度 */
	unsigned char *setup_packet;	/* 控制传输用的缓冲区 */
	dma_addr_t setup_dma;		/* 控制传输用的dma缓冲区 */
	int start_frame;		/* 等时传输开始的帧 */
	int number_of_packets;		/* 等时传输的包的个数 */
	int interval;			/* 传输间隔 */
	int error_count;		/* 等时传输错误个数 */
	void *context;			/* 给传输完成函数complete使用的上下文 */
	usb_complete_t complete;	/* 传输完成函数 */
	struct usb_iso_packet_descriptor iso_frame_desc[0];
					/* 只有等时传输会用到 */
	void *priv_data;                /* 私有数据 */
};

kref:指的是当前urb的引用计数。

hcpriv:当提交给主机控制器时,主机控制器可以从这里提取它自己的私有数据

use_count:当这个urb被HCD使用时,该值加1。返还驱动时,该值减1.

reject:表示提交将不会真正的执行,由usb_kill_urb修改

unlinked:unlink错误码

unlinked:将所有提交到这个端点的urb串成一串,这个就是这个串儿的头

anchor_list:不懂,这个是干嘛的

anchor:依然不懂

dev:指向usb设备的指针

ep:指向端点的指针

pipe:pipe信息

stream_id:stream ID

status:urb的状态

transfer_flags:传输的一些标志,可以用的值有:

#define URB_SHORT_NOT_OK	0x0001	/* report short reads as errors */
#define URB_ISO_ASAP		0x0002	/* iso-only; use the first unexpired
					 * slot in the schedule */
#define URB_NO_TRANSFER_DMA_MAP	0x0004	/* urb->transfer_dma valid on submit */
#define URB_NO_FSBR		0x0020	/* UHCI-specific */
#define URB_ZERO_PACKET		0x0040	/* Finish bulk OUT with short packet */
#define URB_NO_INTERRUPT	0x0080	/* HINT: no non-error interrupt
					 * needed */
#define URB_FREE_BUFFER		0x0100	/* Free transfer buffer with the URB */

URB_SHORT_NOT_OK:字面意思就是,短的urb就不OK。详细点就是,从IN端点读取到的数据包,短于wMaxPacketSize的数据,就认为是错误的。

URB_ISO_ASAP:字面意思就是,尽快等时传输,ASAP(as soon as possible).再详细点就是,等时传输,需要告诉主机控制器,从第几帧开始传输。该标志就是告诉主机控制器,尽可能快的传输,我就不用写第几帧开始了。

URB_NO_TRANSFER_DMA_MAP:字面意思就是,不要映射DMA。详细点就是,驱动已经做好了DMA映射了,直接使用就行了,不用在映射了。

URB_NO_FSBR:这个看样子是给UHCI使用的,不懂。遇到之后,再去深究吧

URB_ZERO_PACKET:字面意思0数据包。详细点就是,当发送数据的时候,使用短包结束。当传输数据刚好是wMaxPacketSize的整数倍时,在发一个0长度数据包,作为结束。如果不是整数倍,最后一个包,就是一个短包。不用再做任何操作。

URB_NO_INTERRUPT:字面意思是不要中断。详细点就是,urb传输完成之后,不要请求一个中断,这就意味着,urb完成之后,complete函数并不会马上被调用。而是在之后被调用。USB core会保证每个complete都被调用

URB_FREE_BUFFER:字面意思就是释放buffer,详细点就是,随urb一起释放buffer

transfer_buffer:传输用的缓冲区。这个缓冲区使用kmalloc进行分配的。使用这个缓冲区时,不应该有URB_NO_TRANSFER_DMA_MAP标志

transfer_dma:传输使用的缓冲区。这个缓冲器来自于DMA区域,使用这个缓冲区时,需要设置URB_NO_TRANSFER_DMA_MAP标志

sg:sg列表

num_mapped_sgs:已经映射的sg

num_sgs:sg列表的个数

transfer_buffer_length:缓冲区长度

actual_length:传输完成之后,实际的长度

setup_packet:控制传输使用的缓冲区,同样的它由kmalloc分配,且不能使用URB_NO_TRANSFER_DMA_MAP标志。

setup_dma:控制传传输使用的缓冲区,同样的它是DMA内存。使用这个缓冲区时,需要设置URB_NO_TRANSFER_DMA_MAP标志

start_frame:如果没有使用URB_ISO_ASAP标志,则应该设置这个值,以告诉主机控制器,应该在哪一个帧开始传输。如果使用了URB_ISO_ASAP标志,这个值返回,实际从哪一个帧开始传输。

number_of_packets:等时传输的包的个数

interval:等时和中断传输专用。表示轮询的间隔时间。对于中断传输来说,全速范围为1-255ms,低速为10-255ms,高速为1-16.而高速下的时间需要换算一下为2的(bInterval-1)次方乘以125us。

对于等时传输,该值的范围为1-16,换算方式为2的(bInterval-1)次方乘以125us。

error_count:等时传输出错的个数

context:为complete函数设置的上下文保存地方

complete:urb传输完成之后的处理函数

iso_frame_desc:等时传输的各个包的描述

priv_data:私有数据

urb的创建和销毁

因为urb提交之后,是异步执行的,所以,urb的空间不能在栈区。USB core提供了函数用于分配urb。即usb_alloc_urb

如果需要释放,urb。也需要调用USB core提供的函数,即usb_free_urb.

接下来,需要对创建的urb进行必要的初始化。针对不同的传输类型,USB core 提供了不同的初始化函数。

中断传输:usb_fill_int_urb

批量传输:usb_fill_bulk_urb

控制传输:usb_fill_control_urb

等时传输:哦豁!,非常不幸,需要手动填写必要的字段,并没有对应的函数。

在上面四个初始化中,必须要有一下的数据:发送的目标USB设备;发送的目标USB设备的端点;发送的数据;发送完成之后的处理回调;

具体的值,可以直接参考各个初始化函数的说明。下面列出,等时传输的一个初始化例子(因为没有标准初始化函数):

//目标USB设备
urb->dev = dev;
//为complete函数设置的context
urb->context = uvd;
//目标pipe
urb->pipe = usb_rcvisocpipe(dev,uvd->video_endp-1);
//期望的间隔
urb->interval = 1;
//设置传输的flag,为ASAP,这样就不用设置start_frame
urb->transfer_flags = URB_ISO_ASAP;
//传输的数据位置
urb->transfer_buffer = cam->sts_buf[i];
//传输完成,处理函数
urb->complete=konicawc_isoc_irq;
//等时传输的包的个数
urb->number_of_packets = FRAME_PER_DESC;
//buffer长度
urb->transfer_buffer_length = FRAMES_PER_DESC;
//接下来赋值iso_frame_desc数组
for(j=0;j<FRAMES_PER_DESC;j++>){
    urb->iso_frame_desc[j].offset = j;
    urb->iso_frame_desc[j].length = 1;
}

urb的提交和取消

一旦一个urb被分配并初始化完成,就可以提交给USB core了。提交给USB core的函数为usb_submit_urb.该函数将urb放入对应的端点的urb队列中,等待传输。

当urb被传输完成之后,将会调用complete函数。

在complete函数调用之前,urb中的数据,不能随意更改和访问。因为主机控制器调用urb的时间是不确定的。

提交之后,后悔了怎么办,USB Core依然提供了两个函数用来做此操作:usb_kill_urb,usb_unlink_urb

前者会等到,所有的处理逻辑都处理完成之后,返回。所以,它不能用在中断上下文中,持有自旋锁的上下文中,无法调度的上下文中。

后者就不会等到所有的处理逻辑处理完之后再返回。所以,它的使用范围更广。

对于这两个函数的区别和使用注意。直接参考官方文档:
http://epic-alfa.kavli.tudelft.nl/share/doc/kernel-doc-2.6.32/Documentation/DocBook/usb/re51.html

urb完成

当urb传输完成后,主机控制器驱动将urb的处理权交给usb设备驱动。调用urb中的complete处理函数。

此时会有三种情况

  1. urb被成功的传输。status值为0
  2. 传输数据,出现了问题。此时urb的status会有错误状态。
  3. 传输被取消。此时urb的status也会又不同的值。

额外的usb 传输函数

USB core除了上面的传输方式以外,还提供了三种分别对于控制,批量,中断传输的简便函数,这些函数是同步的,只有等到传输完成才会返回。

可以在驱动的早期,获取少量信息时,使用这些简便函数。

int usb_bulk_msg(struct usb_device * usb_dev, unsigned int pipe, void * data, int len, int * actual_length, int timeout);

int usb_control_msg(struct usb_device * dev, unsigned int pipe, __u8 request, __u8 requesttype, __u16 value, __u16 index, void * data, __u16 size, int timeout);

int usb_interrupt_msg(struct usb_device * usb_dev, unsigned int pipe, void * data, int len, int * actual_length, int timeout);

现在关于usb数据传输的操作就记录完成。下一篇是关于ALSA 驱动的操作。然后在是两者的结合,即我的终极目标USB Audio 驱动。

本篇完

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值