Linux驱动开发、22-USB子系统

本文深入解析USB系统的硬件结构、工作原理及软件架构,涵盖USB各版本速率、拓扑结构、描述符概念、数据通信机制等内容。

USB子系统

USB(universal serial bus)总线:通用串行总线,是一种外部总线标准,用于规范电脑与外部设备的连接和通讯。

 

USB1.0:1.5MB/S

USB1.1(full speed):12MB/S

USB2.0(high speed):480MB/S

USB3.0(supper sped):4800MB/S

 

USB硬件结构(4线):电源(5V,500mA),地线,D+,D-

工作原理:

USB端口的D+、D-数据线上有15K左右的“高值”下拉电阻,从而使USB的数据线悬空电平为低。USB设备的D+(高速High Speed或全速Full Speed)或D-(低速Low Speed)上具有1.5K左右的“低值”上拉电阻,而USB端口的VCC和GND引出线长于数据线,这保证了USB设备先上电后挂线,如此上拉电阻能可靠地将USB端口的相应数据线拉高,这样即可判别USB设备的接入及其速度了。通过改变D+和D- 两根数据线之间的电压差来表示0/1;

 

拓普结构:

\

\

 

对于每个USB系统来说,都有一个称为主机控制器的设备,该控制器和一个根Hub作为
一个整体。这个根Hub下可以接多级的Hub,每个子Hub又可以接子Hub。每个USB设备
作为一个节点接在不同级别的Hub上。 每条USB总线上最多可以接127个设备。

 

常见的USB主控制器规格有:
OHCI(Open HCI 开放主机接口):主要是非PC系统上的USB芯片
UHCI:大多是Intel和Via主板上的USB控制器芯片。他们都是由USB1.1规格的。
EHCI(Enhanced Host Connective Interface 增强主机控制器接口):由Intel等几个厂商研发,兼容OHCI,UHCI ,遵循USB2.0规范

 

USB OTG(on the go)控制器:这类控制器在嵌入式微控制器领域备受欢迎,采用otg 控制器,每个通信终端能充当DRD(Dual-Role Device,双重角色设备)。用HNP(Host Negotiation Protocol,主机沟通协议)初始化设备连接后,这样的设备可以根据功能需要在主机模式和设备模式之间任意切换。

 

HCD主控制器驱动:Host Control Driver

 

USB设备逻辑结构

\

在USB设备的逻辑组织中,包含设备、配置、接口和端点4个层次。设备通常有一个或多个配置,配置通常有一个或多个接口,接口有零或多个端点(端点可以比喻成寄存器)。

 

USB设备中的唯一可寻址的部分是设备端点,端点的作用类似于寄存器。每个端点在设备内部有唯一的端点号,这个端点号是在设备设计时给定的。主机和设备的通信最终都作用于设备上的各个端点。每个端点所支持的操作都是单向的,要么只读,要么只写。

 

主机能自动设备USB设备的原因:

 

在每一个USB设备内部,包含了固定格式的数据,通过这些数据,USB主机就可以获取USB设备的类型、生产厂商等信息。这些固定格式的数据,我们就称之为USB描述符。标准的USB设备有5种USB描述符:设备描述符,配置描述符,接口描述符,端点描述符,字符串描述符。

格式查看:《USB specification :Table-9.8》

 

设备描述符:一个USB设备只有一个设备描述符,设备描述符长度为18个字节。

\

 

 

配置描述符:

 

\

接口描述符:

\

 

端点描述符

\\

 

USB数据通讯:

USB的数据通讯首先是基于传输(Transfer)的,传输的类型有:中断传输、批量传输、同步传输、控制传输

 

一次传输由一个或多个事务(transaction)构成,事务可分为:In事务,Out事务,Setup事务

 

一个事务由一个或多个包(packet)构成,包可分为:令牌包(setup)、数据包(data)、握手包(ACK)和特殊包

 

一个包由多个域构成,域可分为:同步域(SYNC),标识域(PID),地址域(ADDR),端点域(ENDP),帧号域(FRAM),数据域(DATA),校验域(CRC)

\

 

USB枚举:

USB设备在正常工作以前, 第一件要做的事就是枚举。枚举是让主机认得这个USB设备, 并且为该设备准备资源,建立好主机和设备之间的数据传递通道。

\

USB 寻址

USB设备里的每个寻址单元称作端点。每个端点分配的地址称作端点地址。每个端点地址都有与之相关的传输模式。如果一个端点的数据传输模式时批量传输模式,该端点叫做批量端点。地址为0的端点专门用来配置设备。控制管道和它相连,完成设备枚举过程.

USB设备地址和I2C一样,并不占用CPU可寻址的空间,它们的地址空间是私有的,同样采用主从结构..

 

LINUX USB子系统驱动架构

开发板作为主机,挂载U盘

\

 

开发板作为从设备,PC机为主机

 

 

\

 

 

 

 

 

 

URB(USB Request Block,USB请求块)通讯模型:USB数据传输机制使用的核心数据结构

 

 

 

dnw下载线执行流程

\

 

驱动程序设计

#include

#include

#include

#include

#include

#include

#include

#include

#include

#define USB_VID_TQ210 0x04e8

#define USB_PID_TQ210 0X1234

#define DES_BUF_SIZE 512

 

unsigned char bulk_out_endaddr; /*目标端点地址*/

char *des_buffer;

struct usb_device *usb_dev; /*指向USB设备*/

 

/***********************************

当USB核心检测到某个设备的属性和某个驱动程序

的ID匹配时(既枚举过程完成),

这个驱动程序的prob函数就被khubd执行。

 

查看从设备时先让从设备进入下载状态,在PC终端

使用 lsusb 查看

***********************************/

static struct usb_device_id dnw_table [] = {

{ USB_DEVICE(USB_VID_TQ210, USB_PID_TQ210) },

{ },

};

 

/***********************************

文件操作:

 

************************************/

static int dnw_open(struct inode *inode, struct file *file)

{

/*分配内核空间*/

des_buffer = kmalloc(DES_BUF_SIZE,GFP_KERNEL);

return 0;

}

 

static ssize_t dnw_write(struct file *file, const __user char *buffer,

size_t count, loff_t *ppos)

{

 

size_t toWrite=0,totalshift=0;

int actual_length;

unsigned long ret = 0;

while(count > 0)

{

/*获取用户传输下来的数据*/

/*获取较小值*/

toWrite = min(count,(size_t)DES_BUF_SIZE);

ret = copy_from_user(des_buffer,buffer+totalshift,toWrite);

 

/*将数据提交给USB主控制器*/

/*

usb_dev:指向需要操作的usb设备

管道操作:

usb_sndbulkpipe(usb_dev,bulk_out_endaddr):建立usb设备与端点的批量传输管道

 

1)管道包括:端点地址

数据传输方向(IN/OUT)

数据传输模式:控制,中断,批量,等时

1)函数格式:usb_[rcv|snd][ctrl|int|bulk|isoc]pipe

 

actual_length:实际传输的字节数存放在这里

3*HZ:等待超时

 

*/

usb_bulk_msg(usb_dev,usb_sndbulkpipe(usb_dev,bulk_out_endaddr),des_buffer,toWrite,&actual_length,3*HZ);

 

count -= toWrite;

totalshift+=toWrite;

}

return 0;

}

 

static int dnw_release(struct inode *inode, struct file *file)

{

kfree(des_buffer);

return 0;

}

 

/* file operations needed when we register this driver */

static const struct file_operations dnw_fops = {

.owner = THIS_MODULE,

.write = dnw_write,

.open = dnw_open,

.release = dnw_release,

};

 

 

 

struct usb_class_driver dnw_class = {

.name = "dnw%d",

.fops = &dnw_fops,

.minor_base = 100,

};

 

/****************************************************************************

设备捕获函数需要做的工作分析:

 

1、USB是围绕URB数据传输机制展开的,所以开始应该初始化URB,URB使用步骤:

1)分配内存:usb_alloc_urb():这个和网络设备差不多

2)初始化:usb_fill_[control|int|bulk]_urb

3)异步提交:usb_sumit_urb():这项工作在读写操作函数中进行

 

2、同步提交URB接口函数:既使用一下函数就能完成1中三步的工作,这个函数适合在读写操作中进行

1)usb_[control|int|bulk]_msg()

 

 

3、批量传输属于字符设备操作,既要初始化字符操作函数集

1)usb_register_dev():该函数能将字符设备和USB总线关联在一起

 

综合以上分析:

prob只需做的就是第三步;关联字符设备

其他地方需要用到那种数据结构在来这里初始化就好了

****************************************************************************/

static int dnw_probe(struct usb_interface *intf,const struct usb_device_id *id)

{

int ret = -ENOMEM;

int i=0;

 

printk("Device prob!\n");

 

 

/* 接口设置描述 ,主机对每个接口的描述*/

struct usb_host_interface *interface;

struct usb_endpoint_descriptor *endpoint;

 

 

/*获取USB设备,在初始化URB中使用*/

usb_dev = usb_get_dev(interface_to_usbdev(intf));

 

/*获取接口*/

interface = intf->cur_altsetting;

/*获取目标端点*/

for(i=0;idesc.bNumEndpoints;i++)

{

endpoint = &interface->endpoint[i].desc;

if(usb_endpoint_is_bulk_out(endpoint))

{

bulk_out_endaddr = endpoint->bEndpointAddress;

break;

}

}

 

 

/*把字符设备和usb设备关联起来*/

if((ret = usb_register_dev(intf,&dnw_class)) < 0)

{

printk("usb_register_dev err!\n");

}

 

 

return ret;

}

 

void dnw_disconnect (struct usb_interface *intf)

{

usb_deregister_dev(intf,&dnw_class);

}

 

static struct usb_driver dnw_driver = {

.name = "dnw",

.probe = dnw_probe,

.disconnect = dnw_disconnect,

.id_table = dnw_table,

};

 

 

/***********************************

USB 同样是一种总线协议,所以初始化一般是向

其总线注册

************************************/

static int dnw_init(void)

{

/*1、向usb核心注册USB设备*/

int result;

if ((result = usb_register(&dnw_driver))) {

err("usb_register failed. Error number %d",result);

return result;

}

return 0;

}

 

static void dnw_exit(void)

{

/* deregister this driver from the USB subsystem */

usb_deregister(&dnw_driver);

 

}

 

 

MODULE_LICENSE("GPL");

MODULE_AUTHOR("Hntea");

module_init(dnw_init);

module_exit(dnw_exit);


说明:本文转载自:http://www.2cto.com/os/201605/513150.html

下载方式:https://pan.quark.cn/s/a4b39357ea24 布线问题(分支限界算法)是计算机科学和电子工程领域中一个广为人知的议题,它主要探讨如何在印刷电路板上定位两个节点间最短的连接路径。 在这一议题中,电路板被构建为一个包含 n×m 个方格的矩阵,每个方格能够被界定为可通行或不可通行,其核心任务是定位从初始点到最终点的最短路径。 分支限界算法是处理布线问题的一种常用策略。 该算法与回溯法有相似之处,但存在差异,分支限界法仅需获取满足约束条件的一个最优路径,并按照广度优先或最小成本优先的原则来探索解空间树。 树 T 被构建为子集树或排列树,在探索过程中,每个节点仅被赋予一次成为扩展节点的机会,且会一次性生成其全部子节点。 针对布线问题的解决,队列式分支限界法可以被采用。 从起始位置 a 出发,将其设定为首个扩展节点,并将与该扩展节点相邻且可通行的方格加入至活跃节点队列中,将这些方格标记为 1,即从起始方格 a 到这些方格的距离为 1。 随后,从活跃节点队列中提取队首节点作为下一个扩展节点,并将与当前扩展节点相邻且未标记的方格标记为 2,随后将这些方格存入活跃节点队列。 这一过程将持续进行,直至算法探测到目标方格 b 或活跃节点队列为空。 在实现上述算法时,必须定义一个类 Position 来表征电路板上方格的位置,其成员 row 和 col 分别指示方格所在的行和列。 在方格位置上,布线能够沿右、下、左、上四个方向展开。 这四个方向的移动分别被记为 0、1、2、3。 下述表格中,offset[i].row 和 offset[i].col(i=0,1,2,3)分别提供了沿这四个方向前进 1 步相对于当前方格的相对位移。 在 Java 编程语言中,可以使用二维数组...
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值