当前位置:   article > 正文

Linux usb驱动开发(基础)_usb char devpath [16]

usb char devpath [16]

最近想学几个基于linux的驱动开发:(想想还是从usb驱动开始记录,毕竟USB的驱动的开发的讲解比较多,学习比较快)

(做个笔记,忘了就进来看看)

参考文档:文档写的比较基础:

https://blog.csdn.net/zqixiao_09/article/details/50984074

https://www.cnblogs.com/general001/articles/2319552.html

基础知识:

USB体系:USB接口标准支持主机和外部设备之间的数据传输,主机预定了各种类型外部设备的总线带宽,,外部设备和主机正在运行时,可以添加,添加设置,使用外部设备。可以分为USB互联,USB主机,USB设备,USB互联是usb设备和USB主机之间的通信操作。USB系统中,有唯一的主机,USB和主机的接口称为主机控制器,主机控制器负责主机和设备的通信,USB设备包括usb集线器和功能设备,usb集线器的作用向总线提供多个连接点。(包括usb主机<控制插入其中的usb设备>和usb设备<控制该设备如何作为从设备与主机通信>)每条总线有一个主机控制器,负责协调主机和设备间的通信,设备不能主动向主机发送任何消息。

设备层的3个层次:UDC层直接访问硬件,控制usb设备和主机间的底层通信。向上层提供与硬件相关操作的回调函数,

Gadget Function API是UDC驱动程序回调函数的简单包装,

Gadget Function驱动程序具体控制USB设备功能的实现,使其实现USB特性

Gadget Function API把下层的UDC驱动程序和上层Gadget Function驱动程序隔离,是的USB设备侧驱动程序吧功能的实现和底层的通信分离开来。

主机控制器和设备控制器之间的usb核心非常重要,向上为设备驱动提供编程接口,向下为usb主机控制器提供编程接口,维护usb的设备信息,完成热插拔和总线数据传输控制等。

USB工作流程:采用轮询方式控制,,主机控制设置初始化所有的数据传输,USB总线执行的传输动作最多传输3个数据包,每次传输开始:主机控制器发送一个描述符描述传输侗族偶尔的种类和方向,,这个数据包称为标志数据包,,usb设备收到数据包后,解析数据包。USB传输的方式:主机到设备,设备到主机,在一个数据传输开始,数据包标识传输方向,,发送端发送包含信息的数据,接收端发送一个握手数据标识是否传输成功。

USB主机控制器:提供OHCI,EHCI,UHCI总线接口,,usb核心部分连接了USB控制器和设备驱动,

两种类型的USB驱动:1:宿主上的驱动(host)2:设备上的驱动(device)

宿主USB是指:控制插入其中的USB设备,设备驱动:表示该设备作为一个USB设备和主机通信。

USB期间驱动“USB gadget drivers”

觉得该图像对USB设备的结构描述很适合,就借用过来了。

一:USB设备的基础知识

设备,配置,接口,端点。

我们从上往下看:(具体参数可查看上面链接)

1.设备描述符:一个usb只有一个设备描述符,说明usb的总体的信息,并指明配置的个数,设备的通用信息,供应商ID,产品ID,修订ID,支持的设备的类子类和适应的协议,默认的端点的最大包大小

结构体描述:struct usb_device_descriptor,(好多结构都调用该基础的设备描述符)

2.配置描述符:作用:接口本身被捆绑为配置,可以在配置之间切换改变设备的状态,主要用来说明配置特性和所支持的接口个数,每个USB设备提供不同级别的配置信息,不同的配置表现不同的功能组合,配置由多个接口组成。功能此配置中的接口数,支持的挂起和恢复能力以及功率要求

结构体:struct usb_config_config(内核中使用usb_host_config)

3.接口描述符:(相对比较重要)通俗的说是逻辑上的设备,接口备用配置的数目和接口使用端点数,接口类型,接口子类型eg:usb摄像头:就将各种不同的分辨率设置为不同的接口,

将最终的端点捆绑为接口,

结构体:usb_interface_descriptor(内核中使用interface_descriptor)

4.端点描述符:主机和设备之间的物理传输usb_endpoint_descriptor,usb中的每个端点都有唯一的端点号,传输方向也确定(或主机传输到设备,设备传输到主机,端点传输时单方向的)。设备地址,端点号,传输方向就可以传输一个地址,主机只能通过端点与设备通信。端点是usb通信的基本形式。端点参数包含:端点属性都可以被读到:00表示控制属性,01:表示同步属性,02表示批量属性,03表示中断属性,(端点属性比较重要)。本端点接收或者发送的最大数据信息包,轮询的间隔时间,

在这里提出0端点,0端点比较特殊(不用配置就可以传输数据),只能用于传输控制单元,首先利用0端点与设备进行通信,得到usb的基本信息,配置其他的端点。端点的端点属性:传输方式,总线访问频率,带宽,端点号,数据包的最大容量

*************************************

注意通过lsusb可以读出与usb相关的设备信息,包括设备描述符,配置描述符,接口描述符,端点描述符

在/sys/kernel/debug/usb/devices中可以看到类似的设备信息                /sys/bus/usb树形结构实例

**************************************

端点的传输方式:1:控制:用于配置设备,获取设备信息,发送命令到设备,usb协议保证有足够的带宽传输数据到设备,

2:中断:中断端点以一个固定的速录传输少量的数据,,3:批量:传输大量数据(可确保传输数据,不确保传输时间,eg:存储设备),4:等时:传输大量数据(确保传输时间,不确保传输内容,eg:摄像头)。

结构体:usb_endpoint_descriptor(该结构体由设备自己定义:具体内容,可参考文档。内核中使用usb_host_endpoint描述)

5.最后一个基本概念:管道:usb主机软件和usb设备之间的连接,,是在usb配置过程中建立,是usb主机和设备之间数据流的抽象,usb主机的数据缓冲区域usb设备的端点之间存在着逻辑数据传输,实际的数据传输由总线接口完成,管道中的数据都是相互独立的。

以上为最基础的结构体:

接下来我们要说的是比较上层的数据结构体:也就是包含以上的基础结构体,却有增加了新的比较重要的功能(优化后的结构体):(从端点道接口的主机配置描述符)

1:端点描述:usb_host_endpoint

其中有一个比较重要的结构体 :struct list_head urb_list //此端点表示urb队列

当usb设备调用usb_submit_urb提交urb请求时将此urb添加到该链表的末尾。

2:接口描述:struct usb_host_interface :主机端的接口包装器,用于解析接口设置描述符。用于描述接口本身的信息,一个接口由多个设置,以及所使用的端点

struct usb_interface 其中有个结构体usb_host_interface *cur_altsetting,用于表示当前的世界设置接口。

3:配置描述:struct usb_host_config:配置所有的接口以及接口缓存,配置描述符等参数。

4:通信描述符:struct usb_skeleton是一个局部的结构体:用于端点的通信。

其中包括设备信息,接口描述,块输入端点,块输出端点。

以上为基本配置参数,

二:基本设置函数以及基本的结构体解析:

1:struct usb_device(基于总线驱动设备描述符的:device)内核usb设备的表示

https://blog.csdn.net/u013989284/article/details/78998083           (usb_device的解释)

{
    int        devnum; //设备号,没插入一个新的设备有新的设备号
    char        devpath[16];  //设备路径 /sys/device/003:005/usb1
    u32        route;
    enum usb_device_state    state; //设备状态
    enum usb_device_speed    speed; //速度级别

    struct usb_tt    *tt;
    int        ttport;

    unsigned int toggle[2]; //数据0,数据1交替进行,toggle就是标识0端点in和out的datax的状态

    struct usb_device *parent;
    struct usb_bus *bus; //设备所在的总线
    struct usb_host_endpoint ep0; //端点0 被特别提出

    struct device dev;   //嵌入到usb_device的device

    struct usb_device_descriptor descriptor; //设备描述符
    struct usb_host_bos *bos;
    struct usb_host_config *config;  //配置描述符

    struct usb_host_config *actconfig; //当前活跃的信息
    struct usb_host_endpoint *ep_in[16];//设备的输入端点
    struct usb_host_endpoint *ep_out[16]; //输出端点

    char **rawdescriptors;

    unsigned short bus_mA;
    u8 portnum;
    u8 level;

    unsigned can_submit:1;
    unsigned persist_enabled:1;
    unsigned have_langid:1;
    unsigned authorized:1;
    unsigned authenticated:1;
    unsigned wusb:1;
    unsigned lpm_capable:1;
    unsigned usb2_hw_lpm_capable:1;
    unsigned usb2_hw_lpm_enabled:1;
    unsigned usb3_lpm_enabled:1;
    int string_langid;

    /* static strings from the device */
    char *product;
    char *manufacturer;
    char *serial;

    struct list_head filelist;

    int maxchild;

    u32 quirks;
    atomic_t urbnum;

    unsigned long active_duration;

#ifdef CONFIG_PM
    unsigned long connect_time;

    unsigned do_remote_wakeup:1;
    unsigned reset_resume:1;
    unsigned port_is_suspended:1;
#endif
    struct wusb_dev *wusb_dev;
    int slot_id;
    enum usb_device_removable removable;
    struct usb3_lpm_parameters u1_params;
    struct usb3_lpm_parameters u2_params;
    unsigned lpm_disable_count;
}

其中:struct usb_device *parent//表示设备从root hub开始一个个往外连,每个口连一个usb设备,一个hub可以有多个口,一级级的往下连。

struct usb_interface {  //usb的接口信息
    struct usb_host_interface *altsetting;   //包含所有可用于该接口的选项设置,

    struct usb_host_interface *cur_altsetting; //当前激活的接口配置
    unsigned num_altsetting;    /* 可选择的接口设置*/
    struct usb_interface_assoc_descriptor *intf_assoc;

    int minor;        
    enum usb_interface_condition condition;        /* state of binding */
    unsigned sysfs_files_created:1;    /* the sysfs attributes exist */
    unsigned ep_devs_created:1;    /* endpoint "devices" exist */
    unsigned unregistering:1;    /* unregistration is in progress */
    unsigned needs_remote_wakeup:1;    /* driver requires remote wakeup */
    unsigned needs_altsetting0:1;    /* switch to altsetting 0 is pending */
    unsigned needs_binding:1;    /* needs delayed unbind/rebind */
    unsigned reset_running:1;
    unsigned resetting_device:1;    /* true: bandwidth alloc after reset */

    struct device dev;        /* interface specific device info */
    struct device *usb_dev;
    atomic_t pm_usage_cnt;        /* usage counter for autosuspend */
    struct work_struct reset_ws;    /* for resets in atomic context */
};

其中发现有两处调用usb_device :usb_alloc_dev(usb.c分配一个dev usb_hcd主机控制器驱动)usb_set_configuaration(message.c设置一个特定设备为当前设备)

在hub.c的hub_thread。。hub_events。。hub_port_connect_change (处理物理逻辑和连接更改事件)。。时间中分配和创建一个dev结构体

2:struct usb_driver所有的usb驱动程序必须要创建的主结构体。,向usb平台代码描述了usb驱动程序,(一般我们开发填充修改的是这个结构体)(作用形象描述:树根为主控制器,树叶比作具体的USB设备,树干和树枝就是usb总线,树叶和树枝本身通过usb_driver连接,树叶本身的驱动主要通过本身所属类设备驱动来完成,树根和树叶之间的通信,依靠树枝里面的URB来完成)。

采用usb_register注册usb_driver结构体。

2.1:我们先谈该结构体的id_table

其中包含的结构体:struct usb_device_id结构体,描述了这个usb驱动所支持的USB设备列表,(制造商ID,产品ID,产品版本,设备类等信息,需要标明那些成员要被匹配,匹配之后就可以调用设备的porble函数了)包含了该驱动可以支持的所有不同类型的usb的设备,没有该变量,usb驱动程序的探测函数将不会被调用,(*****一个驱动usb_driver可以对应多个设备usb_device)热插拔脚本使用它来确定一个特定的设备插入到系统时该自动挂载那一个驱动程序。

初始化usb_device_id结构体的宏:USB_DEVICE(),和指定的制造商和ID匹配,根据制造商ID和产品ID生成usb_device_id结构体,意味着该驱动可支持制造商ID和产品ID匹配的设备。

USB_DEVICE_INFO用于匹配设备指定类型的的usb_device_id结构体实例

USB_INTERFACE_INFO只和usb的制定类型相匹配,

使用初始化函数:MODULE_DEVICE_TABLE()容许用户空间的工具判断该驱动可以控制什么设备。

2.2:当USB检测到某个设备和某个驱动的usb_device_id结构体所携带的信息一致时,则驱动程序的proble函数将被执行,还用proble函数:usb驱动程序中的探测函数的指针,当usb核心认为有一个usb_interface可以由该驱动处理时,它将调用该函数,,usb核心用来判断的指向usb_device_id的指针也被传给该函数。探测设备的端点地址,缓冲区大小,初始化任何可用于控制usb设备的数据结构体,把已经初始化的数据结构体的指针保存到接口设备中(一个设备被安装而usb核心认为该驱动应该被处理)

disconnect函数:usb_interface被从系统中移除或者驱动程序从usb核心中卸载时被调用。(驱动程序因为某些原因不应该控制设备时)。

proble和disconnect(探测和断开的细节):(编写新的usb时主要需要写的两个驱动,初始化和释放硬件资源)。

都是在USB集线器(usb_hub)内核线程的上下文被调用,其中的休眠合法(其中可以看hub.c的程序)(所以我们应该将探测函数时间减少),系统传递给探测函数两个结构体:1:usb_interface Usb的接口描述符,2:usb_device_id作为参数,

注意:USB驱动程序应该初始化任何用于控制USB设备的局部结构体,并把任何设备的相关信息保存到结构体中,:USB驱动程序需要探测设备的端点地址和缓冲区大小,根据这些然后建立与usb驱动的通信。(探测批量的IN和OUT端点),用于探测设备的端点地址,缓冲区大小,初始化可能用于控制usb设备的数据结构体,(eg:Usb_skeleton.c中的skel_probe函数中做了探测全部的IN和out端点的例程)

在probe成员函数中,会根据usb_interface的成员结构寻找第一个批量输入和输出的端点,将端点地址和缓冲区信息存入结构体中,并且传入usb_set_intfdata中以作为usb接口的私有数据,

过程:1:循环访问改接口中的每个端点,赋予该端点局部指针,一遍稍后访问:结构体参数

struct usb_host_interface *iface_desc;

usb_interface *interface

    iface_desc = interface->cur_altsetting;
    for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
        endpoint = &iface_desc->endpoint[i].desc;

查看端点是输入还是输出:

        if (!dev->bulk_in_endpointAddr &&
            usb_endpoint_is_bulk_in(endpoint)) {}

        if (!dev->bulk_out_endpointAddr &&
            usb_endpoint_is_bulk_out(endpoint)) {

然后把该端点的相关信息保存到局部结构体中,以便稍后的端点通信。

把初始化的数据保存到接口设备中:usb_set_intfdata(interface,dev).用来注册一个data,这个data的结构是任意的,这个程序向内核注册了一个usb_skel结构

得到设备中的接口信息:usb_get_intfdata()

基于上面的两个函数,以上为我们的接口函数,

还有一种注册usb设备的方法:usb_register_dev(interface,&skel_class)USB驱动程序没有和处理设备与用户交互的子系统,驱动程序可以使用USB主设备号,一遍用户空间使用传统的字符驱动接口,(具体查看上面的连接)。

三:urb

USB请求块urb是USB设备驱动中用来描述与usb设备通信所用的基本载体和核心数据结构体,类似于网络驱动的sk_buff。

调试过程:在debugfs下/sys/kernel/debug/usb/devices包含了USB的设备信息,可以查看各种类似的信息。

struct urb {
    /* private: usb core and host controller only fields in the urb */
    struct kref kref;        /* reference count of the URB */
    void *hcpriv;            /* private data for host controller */
    atomic_t use_count;        /* concurrent submissions counter */
    atomic_t reject;        /* submissions will fail */
    int unlinked;            /* unlink error code */

    /* public: documented fields in the urb that can be used by drivers */
    struct list_head urb_list;    /* list head for use by the urb's
                     * current owner */
    struct list_head anchor_list;    /* the URB may be anchored */
    struct usb_anchor *anchor;
    struct usb_device *dev;        /* (in) pointer to associated device */
    struct usb_host_endpoint *ep;    /* (internal) pointer to endpoint */
    unsigned int pipe;        /* (in) pipe information */
    unsigned int stream_id;        /* (in) stream ID */
    int status;            /* (return) non-ISO status */
    unsigned int transfer_flags;    /* (in) URB_SHORT_NOT_OK | ...*/
    void *transfer_buffer;        /* (in) associated data buffer */
    dma_addr_t transfer_dma;    /* (in) dma addr for transfer_buffer */
    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) data buffer length */
    u32 actual_length;        /* (return) actual transfer length */
    unsigned char *setup_packet;    /* (in) setup packet (control only) */
    dma_addr_t setup_dma;        /* (in) dma addr for setup_packet */
    int start_frame;        /* (modify) start frame (ISO) */
    int number_of_packets;        /* (in) number of ISO packets */
    int interval;            /* (modify) transfer interval
                     * (INT/ISO) */
    int error_count;        /* (return) number of ISO errors */
    void *context;            /* (in) context for completion */
    usb_complete_t complete;    /* (in) completion routine */
    struct usb_iso_packet_descriptor iso_frame_desc[0];
                    /* (in) ISO ONLY */
};

USB设备中的每个端点处理一个URB队列,以下为一个典型的urb生命周期。

创建URB;struct urb *usb_alloc_urb(int iso_packets, gfp_t mem_flags)//iso_packets包含等时数据包的数目,返回一个urb结构体

void *usb_alloc_urb(struct urb)//释放

中断urb的创建:static inline void usb_fill_int_urb(struct urb *urb, //urb指针
                    struct usb_device *dev, //要被发送到的USB设备结构
                    unsigned int pipe, //特定端点
                    void *transfer_buffer, //指向发送数据或接受数据的缓冲区的指针,必须使用kmalloc分配,
                    int buffer_length,  //缓冲区的大小
                    usb_complete_t complete_fn, //urb完成时被调用的函数
                    void *context,
                    int interval)//调度的间隔

函数的pipe使用通过usb_sndintpipe()或者usb_rcvintpipe()创建。

批量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)

函数的pipe使用通过usb_sndbulkpipe()或者usb_rcvbulkpipe()创建

控制urb:static inline void usb_fill_control_urb(struct urb *urb,//urb指针
                    struct usb_device *dev, //要被发送到的USB设备结构
                    unsigned int pipe,//特定端点
                    unsigned char *setup_packet, //指向即将被发送到端点的设置数据包
                    void *transfer_buffer,
                    int buffer_length,
                    usb_complete_t complete_fn,
                    void *context)()

函数的pipe使用通过usb_sndcontrolpipe()或者usb_rcvcontrolpipe()创建

等时urb,没有初始化函数的接口,我们只能手动初始化urb,然后提交给usb核心。

eg:后期添加:

usb提交usb_submit_urb()函数

在提交urb到usb的核心后,知道完成函数被调用之前,不要访问URB中的任何成员。如果提交成功,即urb的控制权被移交给USB的核心。被主机控制器处理,进行一次到usb设备的传送,URB完成,usb主机驱动控制器通知usb设备驱动。urb结束后,通过urb->status得到发送状态

usb_kill_urb取消已近提交的urb,会终止urb的生命周期,通常在设备的disconnect()中调用

四:简单的批量传输

1:从usb设备上接收或者想usb设备发送一些简单的数据则调用:usb_bulk_msg:创建一个usb批量urb并将它发送到特定设备,这个函数是异步的

2:usb_control_msg()他提供给驱动发送和结束USB控制信息,

以上两个函数使用是同步的,因此补鞥呢在中断上下文或者持有自旋锁的状态下使用,该函数也不能被其他函数取消,因此钥匙的驱动的disconnect函数掌握足够的信息 ,一判断内核等待该调用的结束。

 

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家自动化/article/detail/246978
推荐阅读
相关标签
  

闽ICP备14008679号