Chinaunix首页 | 论坛 | 博客
  • 博客访问: 52761
  • 博文数量: 17
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 70
  • 用 户 组: 普通用户
  • 注册时间: 2014-11-09 22:49
文章分类
文章存档

2015年(9)

2014年(8)

我的朋友

分类: LINUX

2015-01-30 16:27:13

原文地址:linux usb 鼠标(2) 作者:steven_miao

linux usb 鼠标
 
谨以此文纪念过往的岁月
 
4.2.1 usb_endpoint_is_int_in
判断端点是否为中断in类型
static inline int usb_endpoint_is_int_in(const struct usb_endpoint_descriptor *epd)
{
 return (usb_endpoint_xfer_int(epd) && usb_endpoint_dir_in(epd));
}
static inline int usb_endpoint_xfer_int(const struct usb_endpoint_descriptor *epd)
{
 return ((epd->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==USB_ENDPOINT_XFER_INT); 
}
端点描述符中bmAttributes的低两位用于标示端点传输类型,分为四种:
#define USB_ENDPOINT_XFERTYPE_MASK 0x03 /* in bmAttributes */
#define USB_ENDPOINT_XFER_CONTROL 0     --控制传输
#define USB_ENDPOINT_XFER_ISOC  1     --等时传输
#define USB_ENDPOINT_XFER_BULK  2     --块传输
#define USB_ENDPOINT_XFER_INT    3     --中断传输  
static inline int usb_endpoint_dir_in(const struct usb_endpoint_descriptor *epd)
{
 return ((epd->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN);
}
对于bEndpointAddress的最高位用于标示传输方向,以host定位,in只device to host ,而out指host to device。而低四位用于标示设备地址。所以每一个hub上最多
挂31个器件。
同时对于其他端点类型的检测类似,如检测端点是否为块in类型:usb_endpoint_is_bulk_in。
4.2.2 usb_rcvintpipe
设置端点信息,其实pipe是一个int类型的数据。urb所发送的特定目标struct usb_device的端点信息。
#define usb_rcvintpipe(dev,endpoint) ((PIPE_INTERRUPT << 30) | __create_pipe(dev, endpoint) | USB_DIR_IN)
static inline unsigned int __create_pipe(struct usb_device *dev,unsigned int endpoint)
{
 return (dev->devnum << 8) | (endpoint << 15);
}
pipe由四个部分组成,最高2位用于标示PIPE类型,第15到23的bit位用于标示endpoint->bEndpointaddress信息,而第8到第15位用于标示设备的number号,第7bit位
用于标示数据流方向。
个人以为这里的endpoint极容易引起误解,如果写成endpointaddress会较好理解,这样在看到该API时直接就知道传入参数是什么。
创建各种管道:
#define usb_sndctrlpipe(dev,endpoint)   --把指定usb设备的指定端口设置为一个控制OUT端点
#define usb_rcvctrlpipe(dev,endpoint)   --把指定usb设备的指定端口设置为一个控制IN端点
#define usb_sndisocpipe(dev,endpoint)   --把指定usb设备的指定端口设置为一个等时OUT端点
#define usb_rcvisocpipe(dev,endpoint)   --把指定usb设备的指定端口设置为一个等时IN端点
#define usb_sndbulkpipe(dev,endpoint)   --把指定usb设备的指定端口设置为一个块OUT端点
#define usb_rcvbulkpipe(dev,endpoint)   --把指定usb设备的指定端口设置为一个块IN端点
#define usb_sndintpipe(dev,endpoint)    --把指定usb设备的指定端口设置为一个中断OUT端点
#define usb_rcvintpipe(dev,endpoint)    --把指定usb设备的指定端口设置为一个中断IN端点
pipe的含义就很好理解了,其就是将指定usb设备的指定端口设置为某一类型的端口,即是将端点的信息保存。
4.2.3 usb_maxpacket
取得端点所支持的最大数据数
static inline __u16 usb_maxpacket(struct usb_device *udev, int pipe, int is_out)
{
 struct usb_host_endpoint *ep;
 unsigned   epnum = usb_pipeendpoint(pipe);    --获取设备number (pipe>>15)&&0x0F
 
 if (is_out) {
  WARN_ON(usb_pipein(pipe));
  ep = udev->ep_out[epnum];           --获取端点
 } else {
  WARN_ON(usb_pipeout(pipe));
  ep = udev->ep_in[epnum];
 }
 if (!ep)
  return 0;
 /* NOTE:  only 0x07ff bits are for packet size... */
 return le16_to_cpu(ep->desc.wMaxPacketSize);    --获取端点信息
}
4.2.4 usb_buffer_alloc
从字面含义去理解开辟一段缓冲区。参数很好理解,一般的mem_flags为GFP_KERNEL,不过在中断上下文中不能阻塞,设置为GFP_ATOMIC
void *usb_buffer_alloc(struct usb_device *dev, size_t size, gfp_t mem_flags, dma_addr_t *dma)
{
 if (!dev || !dev->bus)
  return NULL;
 return hcd_buffer_alloc(dev->bus, size, mem_flags, dma);
}
void *hcd_buffer_alloc(struct usb_bus *bus,size_t size,gfp_t mem_flags,dma_addr_t *dma)
{
 struct usb_hcd  *hcd = bus_to_hcd(bus);
 int    i;
 /* some USB hosts just use PIO */
 if (!bus->controller->dma_mask &&!(hcd->driver->flags & HCD_LOCAL_MEM)) {
  *dma = ~(dma_addr_t) 0;
  return kmalloc(size, mem_flags);
 }
 for (i = 0; i < HCD_BUFFER_POOLS; i++) {
  if (size <= pool_max [i])           --当size在HCD_BUFFER_POOLS之内时,直接在pool中分配
   return dma_pool_alloc(hcd->pool [i], mem_flags, dma);
 }
 return dma_alloc_coherent(hcd->self.controller, size, dma, 0);   --分配DMA空间
}
4.2.5 usb_alloc_urb
在usb系统中urb很重要,其内核通过urb与usb设备通信。这个类型一个联络官一样,将驱动和设备连接。
urb是一个很巨大的结构体,不过对于驱动工程师而言,需要理解下面几个成员:
struct usb_device *dev;   urb所发送的目标usb device的指针。
unsigned int pipe;        这个就不多说了,这个就是所发送特定目标的特定端点信息。至于pipe怎么设置在上面讲过。
unsigned int transfer_flags; 传输标识符
     URB_SHORT_NOT_OK         --对任何可能发生的对IN端点的读取都应该是err
   URB_ISO_ASAP           --如果urb是等时的,这个在以后等时urb时再看
     URB_NO_TRANSFER_DMA_MAP --当urb包含一个即将传输DMA缓冲区时应该设置该位,usb core使用transfer_dma指向的缓冲区,而不是transfer_buffer指向地址
     URB_NO_SETUP_DMA_MAP     --类似于上面。usb core使用setup_dma变量指向的缓冲区,而非setup_packet.s
     URB_NO_FSBR  
     URB_ZERO_PACKET  
     URB_NO_INTERRUPT 
void *transfer_buffer 指向用于发送数据到设备(OUT urb)或者从设备接收数据(IN urb)的缓冲区的指针。该缓冲区必须要使用kmalloc来创建,而非在栈中或静态内存中。
dma_addr_t transfer_dma 用于以DMA方式传输数据到usb设备的缓冲区。
int transfer_buffer_length 上述两个参数指向的缓冲区的大小
unsigned char *setup_packet 指向控制urb的设置数据包的指针
dma_addr_t setup_dma
usb_complete_t complete  结束处理函数指针,当urb完全传输或发生错误时调用。
void *context  类似于一个urb的私有数据
int actual_length urb结束后,urb所发送或接收的实际长度。
int status urb结束或被调用时的状态
int start_frame 设置或返回初始的帧数量。
int interval urb被轮询的时间间隔
上面几个成员比较重要。
struct urb *usb_alloc_urb(int iso_packets, gfp_t mem_flags)
{
 struct urb *urb;
 urb = kmalloc(sizeof(struct urb) +iso_packets * sizeof(struct usb_iso_packet_descriptor),mem_flags);
 if (!urb) {
  printk(KERN_ERR "alloc_urb: kmalloc failed\n");
  return NULL;
 }
 usb_init_urb(urb);
 return urb;
}
void usb_init_urb(struct urb *urb)
{
 if (urb) {
  memset(urb, 0, sizeof(*urb));
  kref_init(&urb->kref);
  INIT_LIST_HEAD(&urb->anchor_list);
 }
}
4.2.6 usb_fill_int_urb
在urb申请后,需要对urb的参数进行赋值。对于中断,控制,以及批量urb都有对urb的初始化函数。
以usb_fill_int_urb为例:
static inline void usb_fill_int_urb(struct urb *urb,struct usb_device *dev,unsigned int pipe,void *transfer_buffer,int buffer_length,
                                    usb_complete_t complete_fn,void *context,int interval)
{
 urb->dev = dev;
 urb->pipe = pipe;
 urb->transfer_buffer = transfer_buffer;
 urb->transfer_buffer_length = buffer_length;
 urb->complete = complete_fn;
 urb->context = context;
 if (dev->speed == USB_SPEED_HIGH)
  urb->interval = 1 << (interval - 1);
 else
  urb->interval = interval;
 urb->start_frame = -1;
}
对于控制urb和批量urb都有类似的初始化函数
static inline void usb_fill_bulk_urb(struct urb *urb,struct usb_device *dev,unsigned int pipe,void *transfer_buffer,int buffer_length,usb_complete_t complete_fn,void *context) 
static inline void usb_fill_control_urb(struct urb *urb,struct usb_device *dev,unsigned int pipe,unsigned char *setup_packet,void *transfer_buffer,int buffer_length,usb_complete_t complete_fn,void *context)
对于urb的填充均是对urb的部分成员进行赋值。
4.2.7 usb_submit_urb
int usb_submit_urb(struct urb *urb, gfp_t mem_flags) 
参数 urb 就是前面的urb
mem_flags 有三种值可以使用
GFP_ATOMIC
   调用者在一个urb结束处理函数中,中断处理函数,底半部。tasklet或定时器的回调函数中。
   调用者正持有一个自旋锁或读写锁
   其实所白了这个标识符的设置其实是避免睡眠。
GFP_NOIO
   驱动程序处于块IO路径中应使用该值
GFP_KERNEL
   在上述情况之外使用
五.总结
对于linux的usb设备的驱动,一般是针对设备的某一接口做的驱动。在其中主要是urb的申请和提交。
阅读(885) | 评论(0) | 转发(0) |
0

上一篇:linux usb 鼠标(1)

下一篇:input子系统(1)

给主人留下些什么吧!~~