USB总线是一种典型的热插拔的总线标准,由于其优异的性能几乎成为了当下大小设备中的标配。

USB的驱动可以分为3类:SoC的USB控制器的驱动,主机端USB设备的驱动,设备上的USB Gadget驱动,通常,对于USB这种标准化的设备,内核已经将主机控制器的驱动编写好了,设备上的Gadget驱动通常只运行固件程序而不是基于Linux, 所以驱动工程师的主要工作就是编写主机端的USB设备驱动。

USB子系统框架

下图表示了Linux中USB子系统的框架结构,和i2c一样,USB子系统也可分为三层:**设备驱动层--USB核心--控制器驱动层*

作为热插拔总线, USB和非热插拔总线最大的区别就是总线无法事前获知设备的信息以及设备何时被插入或拔出,所以也就不能使用任意一种形式将设备信息事前写入内核。

为了解决由于热插拔引起的设备识别问题,USB总线通过枚举的方式来获取一个接入总线的USB设备的设备信息——一个由device->config->interface->endpoint逐级描述的设备,基于分离的思想,USB子系统中设计了一组结构来描述这几个维度的设备信息,相比之下,i2c总线只要一个i2c_client即可描述一个设备.

USB总线上的所有通信都是由主机发起的,所以本质上,USB都是采用轮询的方式进行的。USB总线会使用轮询的方式不断检测总线上是否有设备接入,如果有设备接入相应的D+D-就会有电平变化。然后总线就会按照USB规定的协议与设备进行通信,设备将存储在自身的设备信息依次交给主机,主机将这些信息按照4层模型组织起来。上报到内核,内核中的USB子系统再去匹配相应的驱动,USB设备驱动是面向interface这一层次的信息的

作为一种高度标准化的设备, 虽然USB本身十分复杂, 但是内核已经为我们完成了相当多的工作, 下述的常用设备驱动在内核中已经实现了。很多时候, 驱动的难度不是看设备的复杂程度, 而是看标准化程度

音频设备类

通信设备类

HID设备类

显示设备类

海量存储设备类

电源设备类

打印设备类

集线器设备类

核心结构和方法简述

核心结构

基于分离的思想,USB子系统也提供了描述一个USB设备的结构,只不过基于USB协议,完整描述一个USB设备信息需要9个结构,这些结构中,前4个用来描述一个USB设备的硬件信息,即设备本身的信息,这些信息是写入到设备的eeprom的,在任何USB主机中看到的都一样,这些信息可以使用lsusb -v命令来查看; 后5个描述一个USB设备的软件信息,即除了硬件信息之外,Linux为了管理一个USB设备还要封装一些信息,是OS-specific的信息; USB设备硬件信息和软件信息的关系类似于中断子系统中的硬件中断和内核中断,只不过更复杂一点。

usb_device_descriptor来描述一个USB设备的device信息

usb_config_descriptor来描述一个device的config信息

usb_interface_descriptor来描述一个config的interface信息

usb_endpoint_descriptor来描述一个interface的endpoint信息

usb_device描述一个USB的device的软件信息,包括usb_device_descriptor

urb_host_config描述一个USB设备config的软件信息,包括usb_config_descriptor

usb_interface描述一个接口信息

usb_host_interface描述一个interface的设置信息,包括usb_interface_descriptor,我们编写驱动就是针对这一层次的

usb_host_endpoint描述一个interdace的endpoint信息,包括usb_endpoint_descriptor,这是USB通信的最小单位,我们读写一个设备就是针对一个endpoint

usb_driver描述一个usb设备驱动, 也就是USB设备驱动开发的核心结构

usb_driver_id用来标识一个usb设备, 其实例id_table就是usb_driver中的一个域, 由于usb总线中描述一个设备的复杂性, 构造这样一个对象的方法也多种多样

urb (usb request block)是在USB通信过程中的数据载体, 相当于i2c子系统中的i2c_msg, 网络设备驱动中的sk_buff

usb_hcd描述一个SoC中的USB控制器驱动

核心方法

usb_fill_int_urb是注册urb的API, 是整个USB通信的核心数据封装

核心结构和方法详述

首先说的是那9个描述设备信息的结构, 其中的硬件信息是相互独立的, 分别使用 这些结构在内核"include/uapi/linux/usbch9.h"有定义, 我就不贴代码了

usb_device_descriptor

//include/uapi/linux/usbch9.h

258 struct usb_device_descriptor{

259 __u8 bLength;

260 __u8 bDescriptorType;

262 __le16 bcdUSB;

263 __u8 bDeviceClass;

264 __u8 bDeviceSubClass;

265 __u8 bDeviceProtocol;

266 __u8 bMaxPacketSize0;

267 __le16 idVendor;

268 __le16 idProduct;

269 __le16 bcdDevice;

270 __u8 iManufacturer;

271 __u8 iProduct;

272 __u8 iSerialNumber;

273 __u8 bNumConfigurations;

274 } __attribute__ ((packed));

struct usb_device_descriptor

--263-->设备类别

--264-->设备子类

--265-->通信协议

--267-->销售商

--268-->产品ID

--272-->序列号

usb_config_descriptor

//include/uapi/linux/usbch9.h

314 struct usb_config_descriptor{

315 __u8 bLength;

316 __u8 bDescriptorType;

317

318 __le16 wTotalLength;

319 __u8 bNumInterfaces;

320 __u8 bConfigurationValue;

321 __u8 iConfiguration;

322 __u8 bmAttributes;

323 __u8 bMaxPower;

324 } __attribute__ ((packed));

usb_interface_descriptor

//include/uapi/linux/usbch9.h

351 struct usb_interface_descriptor{

352 __u8 bLength;

353 __u8 bDescriptorType;

354

355 __u8 bInterfaceNumber;

356 __u8 bAlternateSetting;

357 __u8 bNumEndpoints;

358 __u8 bInterfaceClass;

359 __u8 bInterfaceSubClass;

360 __u8 bInterfaceProtocol;

361 __u8 iInterface;

362 } __attribute__ ((packed));

usb_endpoint_descriptor

//include/uapi/linux/usbch9.h

369 struct usb_endpoint_descriptor{

370 __u8 bLength;

371 __u8 bDescriptorType;

372

373 __u8 bEndpointAddress;

374 __u8 bmAttributes;

375 __le16 wMaxPacketSize;

376 __u8 bInterval;

377

378 /* NOTE: these two are _only_ in audio endpoints. */

379 /* use USB_DT_ENDPOINT*_SIZE in bLength, not sizeof. */

380 __u8 bRefresh;

381 __u8 bSynchAddress;

382 } __attribute__ ((packed));

usb_device

//include/linux/usb.h

510 struct usb_device {

511 int devnum;

512 char devpath[16];

513 u32 route;

522 struct usb_device *parent;

523 struct usb_bus *bus;

524 struct usb_host_endpoint ep0;

526 struct device dev;

528 struct usb_device_descriptor descriptor;

529 struct usb_host_bos *bos;

530 struct usb_host_config *config;

532 struct usb_host_config *actconfig;

557 char *product;

558 char *manufacturer;

559 char *serial;

561 struct list_head filelist;

563 int maxchild;

568 unsigned long active_duration;

569

584 };

struct usb_device

--522-->这个设备的父设备, 通常就是usb塔形结构的上一个节点设备

--523-->所属的总线是usb总线

--526-->这是一个device, 会挂接到相应的链表

--528-->这个软件device结构包含的硬件device对象

--530-->软件device拥有的所有软件config对象, 对应硬件device拥有的所有硬件config

--531-->当下, 这个device正在使用的config

--557-->产品名

--558-->产品制造商

--559-->产品序列号

--561-->在这个设备上打开的usbfs文件的链表节点

--563-->子设备的最大数量

urb_host_config

//include/linux/usb.h

275 struct usb_host_config {

276 struct usb_config_descriptor desc;

278 char *string; /* iConfiguration string, if present */

282 struct usb_interface_assoc_descriptor *intf_assoc[USB_MAXIADS];

286 struct usb_interface *interface[USB_MAXINTERFACES];

290 struct usb_interface_cache *intf_cache[USB_MAXINTERFACES];

292 unsigned char *extra; /* Extra descriptors */

293 int extralen;

294 };

struct usb_host_config

--276-->软件config对象包含的硬件config对象

--278-->config的名称

--282-->这个config上关联的Interface Association Descriptor

--283-->这个config上关联的下一级的软件interface数组,

usb_interface

下面这个就是与驱动直接匹配的描述

160 struct usb_interface {

163 struct usb_host_interface *altsetting;

165 struct usb_host_interface *cur_altsetting;

167 unsigned num_altsetting; /* number of alternate settings */

171 struct usb_interface_assoc_descriptor *intf_assoc;

173 int minor;

175 enum usb_interface_condition condition; /* state of binding */

176 unsigned sysfs_files_created:1; /* the sysfs attributes exist */

177 unsigned ep_devs_created:1; /* endpoint "devices" exist */

178 unsigned unregistering:1; /* unregistration is in progress */

179 unsigned needs_remote_wakeup:1; /* driver requires remote wakeup */

180 unsigned needs_altsetting0:1; /* switch to altsetting 0 is pending */

181 unsigned needs_binding:1; /* needs delayed unbind/rebind */

182 unsigned reset_running:1;

183 unsigned resetting_device:1; /* true: bandwidth alloc after reset */

185 struct device dev; /* interface specific device info */

186 struct device *usb_dev;

187 atomic_t pm_usage_cnt; /* usage counter for autosuspend */

188 struct work_struct reset_ws; /* for resets in atomic context */

189 };

struct usb_interface

--163-->这个interface包含的所有的setting

--164-->这个interface当前正在使用的setting

--165-->如果这个interface与一个使用了主设备号的驱动绑定了, 这个域就是interface的次设备号; 反之则没用. 驱动应该在probe中设置这个参数

usb_host_interface

77 struct usb_host_interface {

78 struct usb_interface_descriptor desc;

80 int extralen;

81 unsigned char *extra; /* Extra descriptors */

86 struct usb_host_endpoint *endpoint;

88 char *string; /* iInterface string, if present */

89 };

struct usb_host_interface

--78-->这个interface对应的硬件interface对象

--86-->拥有的描述软件endpoint信息的usb_host_endpoint数组

--88-->interface名称

usb_host_endpoint

endpoint是USB设备IO的基本单元

64 struct usb_host_endpoint {

65 struct usb_endpoint_descriptor desc;

66 struct usb_ss_ep_comp_descriptor ss_ep_comp;

67 struct list_head urb_list;

68 void *hcpriv;

69 struct ep_device *ep_dev; /* For sysfs info */

71 unsigned char *extra; /* Extra descriptors */

72 int extralen;

73 int enabled;

74 };

struct usb_host_endpoint

--65-->这个usb_host_endpoint对应的硬件endpoint信息

--67-->读写这个endpoint的usb链表, 由usb核心层(drivers/usb/core/file.c)维护

--73-->这个endpoint是否被使能了

每一个硬件信息对象都包含在一个软件信息对象中, 而软件信息对象是层层包含的, 所以虽然驱动是基于interface描述的, 但是我们可以使用"list_entry()"很容易的向上找到config和device描述, 使用其中的域很容易的找到endpoint描述, 这9个描述设备的结构关系如下图所示:

urb

与platform或i2c总线不同的是,usb总线不允许设备发起通信,所以作为设备驱动, 只有将需要的"材料"准备好经由核心层提交到usb控制器驱动,让控制器驱动带着这些"材料"去轮询设备并将应答带回。这些"材料"就是urb和相应的注册参数。当usb_driver和usb设备匹配上后,我们准备一个urb对象通过usb_fill_int_urb()/_bulk_/_control_注册到总线,并在合适的时机通过usb_submit_urb向控制器驱动发出发送这个urb对象的命令,总线的控制器驱动收到我们的发送命令之后,会根据我们注册时设置的周期不断向匹配的设备发送请求,如果子设备响应了我们的请求,控制器驱动就会将我们注册的urb对象填充好,并回调我们的注册函数。

对于海量存储USB设备,如果要让设备正常工作, 除了这些流程,还需要加上对缓存区的管理,这部分我们下篇说。

usb_fill_int_urb

初始化并注册一个中断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表示我们要注册的urb对象; dev表示这个urb对象的目的设备; pipe表示读写管道, 使用usb_sndintpipe()和usb_rcvintpipe()获取; transfer_buffer表示传递数据的缓冲区首地址;buffer_length表示缓冲区长度;complete_fn表示如果我们发出的urb有了回应, 就回调这个函数; context是回调函数的参数, 由用户定义, 相当于request_irq中的void *dev; interval就是发送周期, 核心层会以这个参数为周期通过usb控制器驱动轮询设备,

usb_alloc()

urb和xxx一样,要用内核分配函数,其中会做一些初始化的工作

usb_fill_bulk_urb

初始化并注册一个海量存储urb

usb_fill_control_urb

初始化并注册一个控制urb

usb_submit_urb

通知内核发送urb对象

usb_driver

1048struct usb_driver {

1049 const char *name;

1051 int (*probe) (struct usb_interface *intf,

1052 const struct usb_device_id *id);

1054 void (*disconnect) (struct usb_interface *intf);

1056 int (*unlocked_ioctl) (struct usb_interface *intf, unsigned int code,

1057 void *buf);

1059 int (*suspend) (struct usb_interface *intf, pm_message_t message);

1060 int (*resume) (struct usb_interface *intf);

1061 int (*reset_resume)(struct usb_interface *intf);

1063 int (*pre_reset)(struct usb_interface *intf);

1064 int (*post_reset)(struct usb_interface *intf);

1066 const struct usb_device_id *id_table;

1068 struct usb_dynids dynids;

1069 struct usbdrv_wrap drvwrap;

1070 unsigned int no_dynamic_id:1;

1071 unsigned int supports_autosuspend:1;

1072 unsigned int disable_hub_initiated_lpm:1;

1073 unsigned int soft_unbind:1;

1074};

struct usb_driver

--1049-->usb设备的名字

--1051-->探测函数, 当usb_driver的id_table和usb设备信息匹配的时候会执行, 主要的工作是申请资源, 初始化, 提供接口

--1054-->当驱动模块被卸载时或设备被拔出时会被执行

--1066-->功能依然是匹配一样, 只是usb的设备信息由4个维度描述, 所以id_table可以填充的内容也多种多样

usb_register

注册一个usb_driver到内核

usb_deregister

注销一个usb_driver

id_table

内核提供了如下的宏来构造一个usb_device_id对象, 其实也就是对usb_device_id中的不同域进行了填充, 由于设备的差异性, 不同的USB设备会上报不同的设备信息, 但无论上报哪些信息, 一定属于下面这些宏的一种封装. 可以先使用lsusb -v查看设备的硬件信息, 再根据其提供的硬件信息确定id_table编写相应的驱动

USB_DEVICE

811 #define USB_DEVICE(vend, prod) \

812 .match_flags = USB_DEVICE_ID_MATCH_DEVICE, \

813 .idVendor = (vend), \

814 .idProduct = (prod)

USB_DEVICE_VER

825 #define USB_DEVICE_VER(vend, prod, lo, hi) \

826 .match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION, \

827 .idVendor = (vend), \

828 .idProduct = (prod), \

829 .bcdDevice_lo = (lo), \

830 .bcdDevice_hi = (hi)

USB_DEVICE_INTERFACE_CLASS

841 #define USB_DEVICE_INTERFACE_CLASS(vend, prod, cl) \

842 .match_flags = USB_DEVICE_ID_MATCH_DEVICE | \

843 USB_DEVICE_ID_MATCH_INT_CLASS, \

844 .idVendor = (vend), \

845 .idProduct = (prod), \

846 .bInterfaceClass = (cl)

USB_DEVICE_INTERFACE_PROTOCOL

857 #define USB_DEVICE_INTERFACE_PROTOCOL(vend, prod, pr) \

858 .match_flags = USB_DEVICE_ID_MATCH_DEVICE | \

859 USB_DEVICE_ID_MATCH_INT_PROTOCOL, \

860 .idVendor = (vend), \

861 .idProduct = (prod), \

862 .bInterfaceProtocol = (pr)

USB_DEVICE_INTERFACE_NUMBER

873 #define USB_DEVICE_INTERFACE_NUMBER(vend, prod, num) \

874 .match_flags = USB_DEVICE_ID_MATCH_DEVICE | \

875 USB_DEVICE_ID_MATCH_INT_NUMBER, \

876 .idVendor = (vend), \

877 .idProduct = (prod), \

878 .bInterfaceNumber = (num)

USB_DEVICE_INFO

889 #define USB_DEVICE_INFO(cl, sc, pr) \

890 .match_flags = USB_DEVICE_ID_MATCH_DEV_INFO, \

891 .bDeviceClass = (cl), \

892 .bDeviceSubClass = (sc), \

893 .bDeviceProtocol = (pr)

894

USB_INTERFACE_INFO

904 #define USB_INTERFACE_INFO(cl, sc, pr) \

905 .match_flags = USB_DEVICE_ID_MATCH_INT_INFO, \

906 .bInterfaceClass = (cl), \

907 .bInterfaceSubClass = (sc), \

908 .bInterfaceProtocol = (pr)

USB_DEVICE_AND_INTERFACE_INFO

924 #define USB_DEVICE_AND_INTERFACE_INFO(vend, prod, cl, sc, pr) \

925 .match_flags = USB_DEVICE_ID_MATCH_INT_INFO \

926 | USB_DEVICE_ID_MATCH_DEVICE, \

927 .idVendor = (vend), \

928 .idProduct = (prod), \

929 .bInterfaceClass = (cl), \

930 .bInterfaceSubClass = (sc), \

931 .bInterfaceProtocol = (pr)

USB_VENDOR_AND_INTERFACE_INFO

946 #define USB_VENDOR_AND_INTERFACE_INFO(vend, cl, sc, pr) \

947 .match_flags = USB_DEVICE_ID_MATCH_INT_INFO \

948 | USB_DEVICE_ID_MATCH_VENDOR, \

949 .idVendor = (vend), \

950 .bInterfaceClass = (cl), \

951 .bInterfaceSubClass = (sc), \

952 .bInterfaceProtocol = (pr)

953

id_table实例

下面是内核"drdrivers/hid/usbhid/usbmouse.c"中的ib_table填写方式, 可以看出, 不仅构造使用了宏, 由于USB鼠标是标准设备, 它的属性值也有标准的宏来标识

230static struct usb_device_id usb_mouse_id_table [] = {

231 { USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,

232 USB_INTERFACE_PROTOCOL_MOUSE) },

233 { } /* Terminating entry */

234};

USB鼠标实例

内核"drivers/hid/usbhid/usbmouse.c"就是一个usb鼠标的驱动, 这里我根据自己的理解写了一个, 采用的是"usb中断设备驱动+input子系统"框架

#define BUF_SIZE 8

MODULE_LICENSE("GPL");

//面向对象, 根据需求封装类

struct xj_mouse {

char name[128];

struct usb_device *dev;

struct urb *msg;

struct input_dev *input;

signed char *buf;

};

struct xj_mouse *mouse;

static int usb_mouse_open(struct input_dev *dev){

struct xj_mouse *mouse = input_get_drvdata(dev);

mouse->msg->dev = mouse->dev;

if (usb_submit_urb(mouse->msg, GFP_KERNEL))

return -EIO;

return 0;

}

static void usb_mouse_close(struct input_dev *dev){

struct xj_mouse *mouse = input_get_drvdata(dev);

usb_kill_urb(mouse->msg);

}

static int init_input(struct usb_interface * intf){

int err=0;

struct usb_device *dev = mouse->dev;

struct input_dev *input_dev = mouse->input;

input_dev->name = mouse->name;

usb_to_input_id(dev, &input_dev->id);

input_dev->dev.parent = &intf->dev;

input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);

input_dev->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) |BIT_MASK(BTN_RIGHT) | BIT_MASK(BTN_MIDDLE);

input_dev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y);

input_dev->keybit[BIT_WORD(BTN_MOUSE)] |= BIT_MASK(BTN_SIDE) |BIT_MASK(BTN_EXTRA);

input_dev->relbit[0] |= BIT_MASK(REL_WHEEL);

input_set_drvdata(input_dev, mouse);

input_dev->open = usb_mouse_open;

input_dev->close = usb_mouse_close;

err = input_register_device(mouse->input);

return 0;

}

static void completion(struct urb * msg){

int status;

signed char *buf = mouse->buf;

struct input_dev *input = mouse->input;

input_report_key(input, BTN_LEFT, buf[0] & 0x01);

input_report_key(input, BTN_RIGHT, buf[0] & 0x02);

input_report_key(input, BTN_MIDDLE, buf[0] & 0x04);

input_report_key(input, BTN_SIDE, buf[0] & 0x08);

input_report_key(input, BTN_EXTRA, buf[0] & 0x10);

input_report_rel(input, REL_X, buf[1]);

input_report_rel(input, REL_Y, buf[2]);

input_report_rel(input, REL_WHEEL, buf[3]);

input_sync(input);

status = usb_submit_urb (msg, GFP_ATOMIC);

}

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

int pipe;

struct usb_host_interface *interface;

struct usb_endpoint_descriptor *endpoint;

//分配、初始化个性结构

mouse = (struct xj_mouse *)kzalloc(sizeof(struct xj_mouse),GFP_KERNEL);

mouse->dev=interface_to_usbdev(intf);

mouse->msg=usb_alloc_urb(0,GFP_KERNEL);

mouse->input=input_allocate_device();

mouse->buf=(void *)kzalloc(BUF_SIZE,GFP_KERNEL);

if (mouse->dev->manufacturer){

strlcpy(mouse->name, mouse->dev->manufacturer, sizeof(mouse->name));

mouse->input->name = mouse->name;

}

//初始化input设备

init_input(intf);

//获取pipe

interface=intf->cur_altsetting;

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

/* 使用dev和endpoint获取端点地址 */

pipe = usb_rcvintpipe(mouse->dev,endpoint->bEndpointAddress);

//注册usb驱动

usb_fill_int_urb(mouse->msg,mouse->dev,pipe,mouse->buf,BUF_SIZE,completion,mouse->msg,endpoint->bInterval);

return 0;

}

static void disconnect(struct usb_interface *intf){

struct xj_mouse *tmp_mouse = usb_get_intfdata (intf);

usb_set_intfdata(intf, NULL);

if (tmp_mouse) {

usb_kill_urb(tmp_mouse->msg);

input_unregister_device(tmp_mouse->input);

usb_free_urb(tmp_mouse->msg);

kfree(tmp_mouse->buf);

kfree(tmp_mouse);

}

}

static struct usb_device_id id_table [] ={

{ USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,USB_INTERFACE_PROTOCOL_MOUSE) },

{},

};

struct usb_driver mouse_drv = {

.name = "xj_mouse_drv",

.probe = probe,

.disconnect = disconnect,

.id_table = id_table,

};

module_usb_driver(mouse_drv);

linux鼠标驱动程序,Linux usb子系统(一) _写一个usb鼠标驱动相关推荐

  1. Linux驱动开发--写一个块设备驱动

    原文地址:[原创] 写一个块设备驱动 http://bbs.chinaunix.net/forum.php?mod=viewthread&tid=2017377&fromuid=288 ...

  2. linux文件控制驱动程序,Linux设备驱动程序学习(6)-高级字符驱动程序操作[(3)设备文件的访问控制]...

    Linux设备驱动程序学习(6) -高级字符驱动程序操作[(3)设备文件的访问控制] 提供访问控制对于一个设备节点来的可靠性来说有时是至关重要的.这部分的内容只是在open和release方法上做些修 ...

  3. linux提取驱动程序,linux驱动程序调试常用方法

    驱动程序开发的一个重大难点就是不易调试.本文目的就是介绍驱动开发中常用的几种直接和间接的调试手段,它们是: 利用printk 查看OOP消息 利用strace 利用内核内置的hacking选项 利用i ...

  4. linux网络设备驱动程序,Linux网络设备驱动程序

    Linux网络设备驱动程序体系结构 -------------------------------------- |        数据包发送    |    数据包接收      |    ---- ...

  5. python制作u盘病毒_十行代码--用Python写一个USB病毒!

    大家好,我又回来了. 昨天在上厕所的时候突发奇想,当你把usb插进去的时候,能不能自动执行usb上的程序.查了一下,发现只有windows上可以,具体的大家也可以搜索(搜索关键词usb autorun ...

  6. Linux打包软件版本带时间,带你写一个 linux 下的打包软件 tar

    相信你对 linux 的 .tar.gz 有点熟悉,这就是先 tar 打包(.tar 后缀),再对此 tar 文件用 gzip 压缩(.tar.gz)的后缀名. 值得注意的是, tar 不是压缩软件, ...

  7. usb禁止重定向_一种USB重定向处理方法和系统与流程

    本发明涉及桌面虚拟化的USB重定向处理技术领域,具体而言,涉及一种USB重定向处理方法和系统. 背景技术: 随着科学技术的不断进步,网络技术日益发达,虚拟化的应用逐渐普及.桌面虚拟化是指在数据中心的服 ...

  8. python物体跟着鼠标走_用Python写一个跟随鼠标运动的自定义窗口

    背景:因为项目需要,要开发一个在PC上运行的应用程序,生成一个跟随鼠标运动的窗口,并且监听鼠标的点击事件,并在窗口上做相应的显示. 平台:Win7 64位 + Python27 64位 支持库:PyH ...

  9. python写一个服务_写一个Python的windows服务

    1. 安装pywin32和pyinstaller pip install pywin32 pip install pyinstaller 2.写一个服务Demo # -*- coding: utf-8 ...

最新文章

  1. 使用qwt作曲线图——有网格线背景的画法
  2. 【Win32汇编】五种寻址方式
  3. Maven构建Struts2框架的注意事项
  4. 支付通道接口异常统计上报
  5. 想不明白的时候可以干的十件事情
  6. zabbix自动发现(Discovery)功能使用
  7. 为什么python除法结果会有小数点0_为什么Python返回0进行简单的除法计算?
  8. apache配置php后访问localhost出现403错误
  9. 怎样给Spark传递函数—怎样让你的Spark应用更高效更健壮
  10. Mac自带的实用功能
  11. 网页长截图工具_Mac系统如何轻松实现网页长截图功能
  12. ipython 安装_IPYTHON安装.DOC
  13. 虚拟机VMware安装windows7 64位操作系统(图文版详解版)
  14. 泛型:泛型类与泛型方法
  15. python窗口显示表格_python表格界面
  16. R语言入门——rep函数
  17. 计算机cpu天体图,台式电脑CPU天梯图2019年1月版更新 多款新九代酷睿处理器了解下...
  18. 从360首席科学家到区块链创业者,苦钻代码、强迫自己看白皮书,原来这个圈子都是这么努力的 | 人物志...
  19. 海洋cms(海洋视频内容管理系统) v12.5
  20. C++数字三角形问题(动态规划)

热门文章

  1. qemu debug linux内核,在QEMU环境中使用GDB调试Linux内核
  2. 从0到1带你手撸一个请求重试组件,不信你学不会!
  3. 2020最新,100道电商大厂必问面试题:附完整答案
  4. Java多线程基础学习一:线程的6种状态和相互转化
  5. Spring框架对JDBC的简单封装。提供了一个JDBCTemplate对象简化JDBC的开发
  6. 【转贴】龙芯内核发展策略 已经支持k8s
  7. 基于QGraphics的简易画板1
  8. 原生JS替代jQuery的各种方法汇总
  9. 团队编程项目作业1-成员简介及分工
  10. oracle 自定义函数 返回一个表类型