导航:首页 > 操作系统 > linuxusb从设备驱动程序

linuxusb从设备驱动程序

发布时间:2022-10-02 19:43:17

A. linux下usb驱动程序开发有哪些背景及其意义

在Linux kernel源码目录中driver/usb/usb-skeleton.c为我们提供了一个最基础的USB驱动程序。我们称为USB骨架。通过它我们仅需要修改极少的部分,就可以完成一个USB设备的驱动。我们的USB驱动开发也是从她开始的。

B. 如何在Linux开发工具中启动USB驱动程序

USB驱动程序支持在原生Linux提供了。

方法要求启用驱动,但是,取决于你所使用的分布和内核版本。

哪个发行版支持USBFS?

已知提供USBFS支持:

Ubuntu 9.04或以上,
Ubuntu 9.10的内核2.6.31-19服务器,
CentOS的4.8,
CentOS的5.4,
一般情况下,任何分布用内核版本<2.6.32

已知不提供USBFS支持:

Ubuntu 10.04,
一般情况下,任何分布用内核版本> = 2.6.32

方法1:USBFS支持

如果你的发行版提供了USBFS支持,那么下面的命令将工作:

mount -t usbfs none /proc/bus/usb -o devmode=0666

为了使这种更改永久,确保下面一行是在/ etc / fstab文件中:

none /proc/bus/usb usbfs defaults,devmode=0666 0 0

这将自动安装在系统启动。

一旦 /etc/fstab添加已经做完,一个简单的命令应该挂载USBFS文件系统:

mount /proc/bus/usb

无论是FTDI / XTAG和XTAG-2的调试适配器现在应该工作。

方法2:没有USBFS支持

为了确保在任何一个FTDI / XTAG或XTAG-2调试适配器插入时的权限是在设备上是正确的,您需要配置“udev”来识别这个设备。

创建一个文件“/etc/udev/rules.d/99-xmos.rules”,其内容如下:

SUBSYSTEM!="usb|usb_device", GOTO="xmos_rules_end"
ACTION!="add", GOTO="xmos_rules_end"

# 20b1:f7d1 for xmos xtag2
ATTRS{idVendor}=="20b1", ATTRS{idProct}=="f7d1", MODE="0666", SYMLINK+="xtag2-%n"

# 20b1:f7d3 for xmos startkit
ATTRS{idVendor}=="20b1", ATTRS{idProct}=="f7d3", MODE="0666", SYMLINK+="startkit-%n"

# 0403:6010 for XC-1 with FTDI al-uart chip
ATTRS{idVendor}=="0403", ATTRS{idProct}=="6010", MODE="0666", SYMLINK+="xc1-%n"

LABEL="xmos_rules_end"

注意:本ATTRS,MODE和SYMLINK节必须全部在同一行,因为每个规则只能在一行上。

现在告诉udev进行重新加载,以确保文件添加新的规则:

service udev reload

还必须拔下并重新插上USB线,让udev的识别设备的新规则。或者触发重新插上的udev类型为“udevadm触发”或“udevtrigger”命令,取决于在你的Linux分布上udev的版本。

FTDI / XTAG其他注意事项

FTDI的库需要USB设备的文件都可以从/ proc /bus/ USB,不支持的/dev/bus/usb作为文件的位置。

但是,如果你的发行版不支持USBFS(以上方法1),但确实有一个的/proc/bus/usb空目录,你可以使用下面的绑定mount命令的解决方法:

mount --bind /dev/bus/usb /proc/bus/usb

如果你的发行版不支持USBFS,也没有一个的/proc/bus/usb目录(这是较新的内核的情况下,大约从2.6.32开始),然后联系XMOS的rthe设备库的非官方补丁的版本; 发送XMOS的支持标签,其中包括“Linux的FTDI库请求”的主题。

C. 怎样写linux下的USB设备驱动程序

USB驱动程序基础
在动手写USB驱动程序这前,让我们先看看写的USB驱动程序在内核中的结构,如下图:



USB通信最基本的形式是通过端点(USB端点分中断、批量、等时、控制四种,每种用途不同),USB端点只能往一个方向传送数据,从主机到设备或者从设备到主机,端点可以看作是单向的管道(pipe)。所以我们可以这样认为:设备通常具有一个或者更多的配置,配置经常具有一个或者更多的接口,接口通常具有一个或者更多的设置,接口没有或具有一个以上的端点。驱动程序把驱动程序对象注册到USB子系统中,稍后再使用制造商和设备标识来判断是否已经安装了硬件。USB核心使用一个列表(是一个包含制造商ID和设备号ID的一个结构体)来判断对于一个设备该使用哪一个驱动程序,热插拨脚本使用它来确定当一个特定的设备插入到系统时该自动装载哪一个驱动程序。
上面我们简要说明了驱动程序的基本理论,在写一个设备驱动程序之前,我们还要了解以下两个概念:模块和设备文件。
模块:是在内核空间运行的程序,实际上是一种目标对象文件,没有链接,不能独立运行,但是可以装载到系统中作为内核的一部分运行,从而可以动态扩充内核的功能。模块最主要的用处就是用来实现设备驱动程序。Linux下对于一个硬件的驱动,可以有两种方式:直接加载到内核代码中,启动内核时就会驱动此硬件设备。另一种就是以模块方式,编译生成一个.ko文件(在2.4以下内核中是用.o作模块文件,我们以2.6的内核为准,以下同)。当应用程序需要时再加载到内核空间运行。所以我们所说的一个硬件的驱动程序,通常指的就是一个驱动模块。
设备文件:对于一个设备,它可以在/dev下面存在一个对应的逻辑设备节点,这个节点以文件的形式存在,但它不是普通意义上的文件,它是设备文件,更确切的说,它是设备节点。这个节点是通过mknod命令建立的,其中指定了主设备号和次设备号。主设备号表明了某一类设备,一般对应着确定的驱动程序;次设备号一般是区分不同属性,例如不同的使用方法,不同的位置,不同的操作。这个设备号是从/proc/devices文件中获得的,所以一般是先有驱动程序在内核中,才有设备节点在目录中。这个设备号(特指主设备号)的主要作用,就是声明设备所使用的驱动程序。驱动程序和设备号是一一对应的,当你打开一个设备文件时,操作系统就已经知道这个设备所对应的驱动程序。对于一个硬件,Linux是这样来进行驱动的:首先,我们必须提供一个.ko的驱动模块文件。我们要使用这个驱动程序,首先要加载它,我们可以用insmod
xxx.ko,这样驱动就会根据自己的类型(字符设备类型或块设备类型,例如鼠标就是字符设备而硬盘就是块设备)向系统注册,注册成功系统会反馈一个主设备号,这个主设备号就是系统对它的唯一标识。驱动就是根据此主设备号来创建一个一般放置在/dev目录下的设备文件。在我们要访问此硬件时,就可以对设备文件通过open、read、write、close等命令进行。而驱动就会接收到相应的read、write操作而根据自己的模块中的相应函数进行操作了。

USB驱动程序实践
了解了上述理论后,我们就可以动手写驱动程序,如果你基本功好,而且写过linux下的硬件驱动,USB的硬件驱动和pci_driver很类似,那么写USB的驱动就比较简单了,如果你只是大体了解了linux的硬件驱动,那也不要紧,因为在linux的内核源码中有一个框架程序可以拿来借用一下,这个框架程序在/usr/src/~(你的内核版本,以下同)/drivers/usb下,文件名为usb-skeleton.c。写一个USB的驱动程序最基本的要做四件事:驱动程序要支持的设备、注册USB驱动程序、探测和断开、提交和控制urb(USB请求块)(当然也可以不用urb来传输数据,下文我们会说到)。
驱动程序支持的设备:有一个结构体struct
usb_device_id,这个结构体提供了一列不同类型的该驱动程序支持的USB设备,对于一个只控制一个特定的USB设备的驱动程序来说,struct
usb_device_id表被定义为:
/* 驱动程序支持的设备列表 */
static struct usb_device_id
skel_table [] = {
{ USB_DEVICE(USB_SKEL_VENDOR_ID, USB_SKEL_PRODUCT_ID)
},
{ } /* 终止入口 */
};
MODULE_DEVICE_TABLE (usb,
skel_table);
对于PC驱动程序,MODULE_DEVICE_TABLE是必需的,而且usb必需为该宏的第一个值,而USB_SKEL_VENDOR_ID和USB_SKEL_PRODUCT_ID就是这个特殊设备的制造商和产品的ID了,我们在程序中把定义的值改为我们这款USB的,如:
/*
定义制造商和产品的ID号 */
#define USB_SKEL_VENDOR_ID 0x1234
#define
USB_SKEL_PRODUCT_ID
0x2345
这两个值可以通过命令lsusb,当然你得先把USB设备先插到主机上了。或者查看厂商的USB设备的手册也能得到,在我机器上运行lsusb是这样的结果:
Bus
004 Device 001: ID 0000:0000
Bus 003 Device 002: ID 1234:2345 Abc Corp.

Bus 002 Device 001: ID 0000:0000
Bus 001 Device 001: ID
0000:0000
得到这两个值后把它定义到程序里就可以了。
注册USB驱动程序:所有的USB驱动程序都必须创建的结构体是struct
usb_driver。这个结构体必须由USB驱动程序来填写,包括许多回调函数和变量,它们向USB核心代码描述USB驱动程序。创建一个有效的struct
usb_driver结构体,只须要初始化五个字段就可以了,在框架程序中是这样的:
static struct usb_driver skel_driver
= {
.owner = THIS_MODULE,
.name = "skeleton",

.probe = skel_probe,
.disconnect = skel_disconnect,

.id_table = skel_table,
};

D. linux usb驱动在哪个文件夹

内核自带,不需要启动。添加新设备号,只需要增加udev配置文件,一行一个设备。

E. linux下usb驱动程序开发有哪些背景及其意义

在Linux kernel源码目录中driver/usb/usb-skeleton.c为我们提供了一个最基础的USB驱动程序。我们称为USB骨架。通过它我们仅需要修改极少的部分,就可以完成一个USB设备的驱动。我们的USB驱动开发也是从她开始的。
那些linux下不支持的USB设备几乎都是生产厂商特定的产品。如果生产厂商在他们的产品中使用自己定义的协议,他们就需要为此设备创建特定的驱动程序。当然我们知道,有些生产厂商公开他们的USB协议,并帮助Linux驱动程序的开发,然而有些生产厂商却根本不公开他们的USB协议。因为每一个不同的协议都会产生一个新的驱动程序,所以就有了这个通用的USB驱动骨架程序, 它是以pci 骨架为模板的。
如果你准备写一个linux驱动程序,首先要熟悉USB协议规范。USB主页上有它的帮助。一些比较典型的驱动可以在上面发现,同时还介绍了USB urbs的概念,而这个是usb驱动程序中最基本的。
Linux USB 驱动程序需要做的第一件事情就是在Linux USB 子系统里注册,并提供一些相关信息,例如这个驱动程序支持哪种设备,当被支持的设备从系统插入或拔出时,会有哪些动作。所有这些信息都传送到USB 子系统中。
楼主如果想学习如何使用Linux系统,可以网络《Linux就该这么学》,里面有详细的介绍。

F. linux USB设备驱动的问题!

usb驱动加载过程,
usb驱动模块insmod之后,会在usbcore中注册自己的probe函数,当usb控制器发现有设备接入时,就会根据PID和VID在usbcore中查找注册的驱动模块,找到之后,调用对应的probe函数,probe函数将实现设备的接入节点创建等等。
你先插入设备,此时usbcore内没有模块注册的probe,则无法识别设备。

G. linux主机侧与设备侧USB驱动

USB采用树形拓扑结构,主机侧和设备侧的USB控制器分别称为主机控制器((Host Controller)和USB设备控制器(UDC),每条总线上只有一个主机控制器,负责协调主机和设备间的通信,而设备不能主动向主机发送任何消息。

在Linux系统中,USB驱动可以从两个角度去观察,一个角度是主机侧,一个角度是设备侧。从上图主机侧去看,在Linux驱动中,处于USB驱动最底层的是USB主机控制器硬件,在其上运行的是USB主机控制器驱动,在主机控制器上的为USB核心层,再上层为USB设备驱动层(插入主机上的U盘、鼠标、USB转串口等设备驱动)。因此,在主机侧的层次结构中,要实现的USB驱动包括两类:USB主机控制器驱动和USB设备驱动,前者控制插入其中的USB设备,后者控制USB设备如何与主机通信。Linux内核中的USB核心负责USB驱动管理和协议处理的主要工作。主机控制器驱动和设备驱动之间的USB核心非常重要,其功能包括:通过定义一些数据结构、宏和功能函数,向上为设备驱动提供编程接口,向下为USB主机控制器驱动提供编程接口;维护整个系统的USB设备信息;完成设备热插拔控制、总线数据传输控制等。

H. 怎样写linux下的USB设备驱动程序

你好,方法如下:
写一个USB的驱动程序最 基本的要做四件事:
驱动程序要支持的设备、注册USB驱动程序、探测和断开、提交和控制urb(USB请求块)
驱动程序支持的设备:有一个结构体struct usb_device_id,这个结构体提供了一列不同类型的该驱动程序支持的USB设备,对于一个只控制一个特定的USB设备的驱动程序来说,struct usb_device_id表被定义为:
/* 驱动程序支持的设备列表 */
static struct usb_device_id skel_table [] = {
{ USB_DEVICE(USB_SKEL_VENDOR_ID, USB_SKEL_PRODUCT_ID) },
{ } /* 终止入口 */
};
MODULE_DEVICE_TABLE (usb, skel_table);
对 于PC驱动程序,MODULE_DEVICE_TABLE是必需的,而且usb必需为该宏的第一个值,而USB_SKEL_VENDOR_ID和 USB_SKEL_PRODUCT_ID就是这个特殊设备的制造商和产品的ID了,我们在程序中把定义的值改为我们这款USB的,如:
/* 定义制造商和产品的ID号 */
#define USB_SKEL_VENDOR_ID 0x1234
#define USB_SKEL_PRODUCT_ID 0x2345
这两个值可以通过命令lsusb,当然你得先把USB设备先插到主机上了。或者查看厂商的USB设备的手册也能得到,在我机器上运行lsusb是这样的结果:
Bus 004 Device 001: ID 0000:0000
Bus 003 Device 002: ID 1234:2345 Abc Corp.
Bus 002 Device 001: ID 0000:0000
Bus 001 Device 001: ID 0000:0000
得到这两个值后把它定义到程序里就可以了。
注册USB驱动程序:所 有的USB驱动程序都必须创建的结构体是struct usb_driver。这个结构体必须由USB驱动程序来填写,包括许多回调函数和变量,它们向USB核心代码描述USB驱动程序。创建一个有效的 struct usb_driver结构体,只须要初始化五个字段就可以了,在框架程序中是这样的:
static struct usb_driver skel_driver = {
.owner = THIS_MODULE,
.name = "skeleton",
.probe = skel_probe,
.disconnect = skel_disconnect,
.id_table = skel_table,
};
探测和断开:当 一个设备被安装而USB核心认为该驱动程序应该处理时,探测函数被调用,探测函数检查传递给它的设备信息,确定驱动程序是否真的适合该设备。当驱动程序因 为某种原因不应该控制设备时,断开函数被调用,它可以做一些清理工作。探测回调函数中,USB驱动程序初始化任何可能用于控制USB设备的局部结构体,它 还把所需的任何设备相关信息保存到一个局部结构体中,
提交和控制urb:当驱动程序有数据要发送到USB设备时(大多数情况是在驱动程序的写函数中),要分配一个urb来把数据传输给设备:
/* 创建一个urb,并且给它分配一个缓存*/
urb = usb_alloc_urb(0, GFP_KERNEL);
if (!urb) {
retval = -ENOMEM;
goto error;
}
当urb被成功分配后,还要创建一个DMA缓冲区来以高效的方式发送数据到设备,传递给驱动程序的数据要复制到这块缓冲中去:
buf = usb_buffer_alloc(dev->udev, count, GFP_KERNEL, &urb->transfer_dma);
if (!buf) {
retval = -ENOMEM;
goto error;
}

if (_from_user(buf, user_buffer, count)) {
retval = -EFAULT;
goto error;
}
当数据从用户空间正确复制到局部缓冲区后,urb必须在可以被提交给USB核心之前被正确初始化:
/* 初始化urb */
usb_fill_bulk_urb(urb, dev->udev,
usb_sndbulkpipe(dev->udev, dev->bulk_out_endpointAddr),
buf, count, skel_write_bulk_callback, dev);
urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
然后urb就可以被提交给USB核心以传输到设备了:
/* 把数据从批量OUT端口发出 */
retval = usb_submit_urb(urb, GFP_KERNEL);
if (retval) {
err("%s - failed submitting write urb, error %d", __FUNCTION__, retval);
goto error;
}
当urb被成功传输到USB设备之后,urb回调函数将被USB核心调用,在我们的例子中,我们初始化urb,使它指向skel_write_bulk_callback函数,以下就是该函数:
static void skel_write_bulk_callback(struct urb *urb, struct pt_regs *regs)
{
struct usb_skel *dev;

dev = (struct usb_skel *)urb->context;

if (urb->status &&
!(urb->status == -ENOENT ||
urb->status == -ECONNRESET ||
urb->status == -ESHUTDOWN)) {
dbg("%s - nonzero write bulk status received: %d",
__FUNCTION__, urb->status);
}

/* 释放已分配的缓冲区 */
usb_buffer_free(urb->dev, urb->transfer_buffer_length,
urb->transfer_buffer, urb->transfer_dma);
}
有时候USB驱动程序只是要发送或者接收一些简单的数据,驱动程序也可以不用urb来进行数据的传输,这是里涉及到两个简单的接口函数:usb_bulk_msg和usb_control_msg ,在这个USB框架程序里读操作就是这样的一个应用:
/* 进行阻塞的批量读以从设备获取数据 */
retval = usb_bulk_msg(dev->udev,
usb_rcvbulkpipe(dev->udev, dev->bulk_in_endpointAddr),
dev->bulk_in_buffer,
min(dev->bulk_in_size, count),
&count, HZ*10);

/*如果读成功,复制到用户空间 */
if (!retval) {
if (_to_user(buffer, dev->bulk_in_buffer, count))
retval = -EFAULT;
else
retval = count;
}
usb_bulk_msg接口函数的定义如下:
int usb_bulk_msg(struct usb_device *usb_dev,unsigned int pipe,
void *data,int len,int *actual_length,int timeout);
其参数为:
struct usb_device *usb_dev:指向批量消息所发送的目标USB设备指针。
unsigned int pipe:批量消息所发送目标USB设备的特定端点,此值是调用usb_sndbulkpipe或者usb_rcvbulkpipe来创建的。
void *data:如果是一个OUT端点,它是指向即将发送到设备的数据的指针。如果是IN端点,它是指向从设备读取的数据应该存放的位置的指针。
int len:data参数所指缓冲区的大小。
int *actual_length:指向保存实际传输字节数的位置的指针,至于是传输到设备还是从设备接收取决于端点的方向。
int timeout:以Jiffies为单位的等待的超时时间,如果该值为0,该函数一直等待消息的结束。
如果该接口函数调用成功,返回值为0,否则返回一个负的错误值。
usb_control_msg接口函数定义如下:
int usb_control_msg(struct usb_device *dev,unsigned int pipe,__u8 request,__u8requesttype,__u16 value,__u16 index,void *data,__u16 size,int timeout)
除了允许驱动程序发送和接收USB控制消息之外,usb_control_msg函数的运作和usb_bulk_msg函数类似,其参数和usb_bulk_msg的参数有几个重要区别:
struct usb_device *dev:指向控制消息所发送的目标USB设备的指针。
unsigned int pipe:控制消息所发送的目标USB设备的特定端点,该值是调用usb_sndctrlpipe或usb_rcvctrlpipe来创建的。
__u8 request:控制消息的USB请求值。
__u8 requesttype:控制消息的USB请求类型值。
__u16 value:控制消息的USB消息值。
__u16 index:控制消息的USB消息索引值。
void *data:如果是一个OUT端点,它是指身即将发送到设备的数据的指针。如果是一个IN端点,它是指向从设备读取的数据应该存放的位置的指针。
__u16 size:data参数所指缓冲区的大小。
int timeout:以Jiffies为单位的应该等待的超时时间,如果为0,该函数将一直等待消息结束。
如果该接口函数调用成功,返回传输到设备或者从设备读取的字节数;如果不成功它返回一个负的错误值。
这两个接口函数都不能在一个中断上下文中或者持有自旋锁的情况下调用,同样,该函数也不能被任何其它函数取消,使用时要谨慎。
我们要给未知的USB设备写驱动程序,只需要把这个框架程序稍做修改就可以用了,前面我们已经说过要修改制造商和产品的ID号,把0xfff0这两个值改为未知USB的ID号。
#define USB_SKEL_VENDOR_ID 0xfff0
#define USB_SKEL_PRODUCT_ID 0xfff0
还 有就是在探测函数中把需要探测的接口端点类型写好,在这个框架程序中只探测了批量(USB_ENDPOINT_XFER_BULK)IN和OUT端点,可 以在此处使用掩码(USB_ENDPOINT_XFERTYPE_MASK)让其探测其它的端点类型,驱动程序会对USB设备的每一个接口进行一次探测, 当探测成功后,驱动程序就被绑定到这个接口上。再有就是urb的初始化问题,如果你只写简单的USB驱动,这块不用多加考虑,框架程序里的东西已经够用 了,这里我们简单介绍三个初始化urb的辅助函数:
usb_fill_int_urb :它的函数原型是这样的:
void usb_fill_int_urb(struct urb *urb,struct usb_device *dev,
unsigned int pipe,void *transfer_buff,
int buffer_length,usb_complete_t complete,
void *context,int interval);
这个函数用来正确的初始化即将被发送到USB设备的中断端点的urb。
usb_fill_bulk_urb :它的函数原型是这样的:
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)
这个函数是用来正确的初始化批量urb端点的。
usb_fill_control_urb :它的函数原型是这样的:
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,void *context);
这个函数是用来正确初始化控制urb端点的。
还有一个初始化等时urb的,它现在还没有初始化函数,所以它们在被提交到USB核心前,必须在驱动程序中手工地进行初始化,可以参考内核源代码树下的/usr/src/~/drivers/usb/media下的konicawc.c文件。

I. 怎样写linux下的USB设备驱动程序

usb分为host驱动和device驱动,看你说的是哪一种。我就是写过usb host和device驱动的,一般来说看spec和研究linux代码,仔细阅读下linux 下usb的子系统代码吧,不是一朝一夕就能说的清楚的。我过一阵子会出点usb host和device开发的总结性文章。

还有usb2.0和usb3.0的host驱动是不一样的,device也多了点描述符,而且不同版本的传输协议基本上很大不同了。

可参考:http://wenku..com/view/c1d60c175f0e7cd1842536c2.html

J. linux下usb驱动,求教,该怎么处理

系统中没有USB驱动,只能自己安装了。安装具体如下:事先需要插入一张系统光盘(因为所有的系统光盘中都集成了大部分的各种各样的驱动)到光驱。再把USB移动硬件插入USB接口,也就是先把USB插入电脑上。之后右下角会提示新硬件被发现,但些此时没有驱动肯定是用不起的。在弹出的对话框中选择更新驱动的路径,这个路径当然就是刚才放入光驱中的光盘了。此时电脑会自动搜索光盘中符合USB硬件的驱动。如果没有自动弹出更新驱动对话框:右击“我的电脑”-属性-硬件-设备管理器。刚插入的新硬件因为没有驱动所以在此对话框中就会显示一个黄色的问号。再在黄色问号的上边右击-选择更新驱动此时电脑就会自动侦测电脑中符合USB的驱动(当然这个自动就是电脑自动到插入光盘中去搜索)。

阅读全文

与linuxusb从设备驱动程序相关的资料

热点内容
工厂哪里用压缩空气 浏览:328
centos如何安装php 浏览:110
丹凤服务器是什么 浏览:148
分析html5源码 浏览:994
资治通鉴中华书局pdf 浏览:212
如何搭建服务器和数据库 浏览:537
论文app怎么用 浏览:466
开源文件服务器加密 浏览:588
哈利波特游戏选什么服务器 浏览:424
云服务器怎样格式化 浏览:16
框架柱顶部箍筋加密区规范 浏览:169
pythonjson文件读取 浏览:105
梦幻西游源码架设 浏览:123
抽烟有解压效果吗 浏览:827
由于加密数据错误这个会话将结束 浏览:832
javaproject运行 浏览:255
0经验转行单片机 浏览:638
什么叫解压缩下载的文件 浏览:862
什么牌子的手机加密狗好用 浏览:536
程序员编程学习笔记 浏览:864