歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux基礎 >> 關於Linux >> Linux驅動開發、22-USB子系統

Linux驅動開發、22-USB子系統

日期:2017/3/1 11:48:58   编辑:關於Linux

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);


Copyright © Linux教程網 All Rights Reserved