#define URB_NO_FSBR 0x0020 /* UHCI-specific */

#define URB_ZERO_PACKET 0x0040 /* 完成大块分解成小包输出

#define URB_NO_INTERRUPT 0x0080 /* HINT: no non-error interrupt needed */

struct usb_iso_packet_descriptor {

unsigned int offset;

unsigned int length; /* expected length */

unsigned int actual_length;

unsigned int status;

};

struct urb;

struct pt_regs;

typedef void (*usb_complete_t)(struct urb *, struct pt_regs *);

/**

struct urb - USB 请求块

◎usb_list 给当前的urb拥有者使用

◎pipe 保持端点的数字,方向,类型,或更多。创建这些值要同时对八个macros可访问的。usb_{snd,rcv}TYPEpipe(dev,endpoint中的TYPE是"ctrl" (control), "bulk", "int" (interrupt), or "iso" (isochronous).同步。例如usb_sndbulkpipe() or usb_rcvintpipe().

端点的数字从0到15。注意到in端点2和out端点2是不同的端点(管道)。当前的配置控制任何给定端点的存在,类型和最大的包大小

◎dev usb设备执行请求的标识符

◎status 在不同步的完成函数中被读取以获得特殊请求的状态。同步完成中只用它来区分urb是否被断开。每个帧的详细的状态在iso_freame-desc的域中

◎transfer_flag 一个变化的标志可能会被用在影响URB怎样提交,断开,或操作。不同的URB能用不同的flag

◎transfer_buffer 这个标识I/O请求从哪里的缓存开始执行,或者到那里完成。(除非URB_NO_TRANSFER_DMA_MAP 被置位)这个缓存不许对DMA有效的,它和kmalloc()或其等价物一起分配。这缓存被用来控制传输的数据进程。

◎trancfer_dma 当transfer_buffer包含有URB_NO_TRANSFER_DMA_MAP时,设备的驱动程序已经表示提供给DMA地址,那么主机端的主控制器的驱动程序会优先使用transfer_buffe作缓存

◎trancfer_lengeh 在不同步的完成函数中被读取,它显示有多少bytes被传输了。一般它和请求的是一样的,除非出现错误或者发生short read 。这个The URB_SHORT_NOT_OK 传输标志被用来把这些报告的short read作为错误。

◎transfer_buffer_lengeh transfer_buffer的大小。传输可能会根据端点最大包的大小来把数据变成一个个包。(包的大小在配置中完成,并在管道中编码。当长度为零时transfer_buffer 和 trancfer_dma多无效。

◎setup_packet 只在控制传输中使用,setup数据包包含八个比特控制传输总是发送这个数据包到设备,然后对transfer_buffer进行必要的读写(如果需要的话)

◎setup_dma 和URB_NO_TRANSFER_DMA_MAP设置一起在控制传输中使用。设备的驱动程序为setup_packet提供DMA地址。主机控制器驱动程序应当优先使用它(相对于setup_packet包)

◎start_frame 为同步传输返回返回原始的帧

◎number_of_packets 列出同步传输的缓存数量

◎interval 为中断或同步传输指定polling间隔。对于全速或低速设备它的单元是帧,对于高速,他的单元的微帧。

◎error_count 返回同步传输中报告的错误数

◎context 在完成状态函数中使用,它一般指向request-specific 驱动的上下

◎Complete 完成训练者。URB传递参数给完成状态函数。完成状态函数然后就会对URB进行操作,包括重新提交或释放它。

@iso_frame_desc: 用来给ISO transfer buffer提供数组,并收集每个缓存的传输状态

@timeout: 如果设置为零的话,那么URB将永远不会超时,否则这就是URB超时的时间。

这个结构表示usb的传输请求URB必须被calling usb_alloc_urb() 函数分配,用 usb_free_urb().函数来释放。用各种usb_fill_*_urb() 函数来做初始化. URBs使用usb_submit_urb()来提交请求, 并通过函数unlink_urb() or usb_kill_urb().来结束那些还没完成的的请求 。

数据传输缓存

一般来说驱动程序用函数kmalloc()来提供I/O缓存,否则就从一般的页池中得到。那是由tranfer_buffer提供的(控制请求也使用setup_packet),主机控制器的驱动为每个用于传输的缓存执行一个dma对应操作(或不对应),这些对应的操作在某些平台上可能会花费巨大(可能使用一个dmabounce buffer或同一个IOMMU对话)

作为选择的,驱动程序可能传递URB_NO_xxx_DMA_MAP传输标志,这个传输标志将会告诉主机控制器的驱动程序没有相应的DMA被需要,因为这个设备的驱动程序是知道DMA的。例如

一个设备驱动程序用usb_buffer_alloc() or call usb_buffer_map()分配一个DMA缓存。当这些传输标志被提供了,主机驱动程序将会试着用DMA地址在transfer_dma 或(和)setup_dma域中去发现,而不是他们自己决定一个DMA地址。(注意到transfer_dma 或(和)setup_dma必须仍然被设置因为不是所有的主机控制器使用DMA,实际的根hub也不是多使用DMA)

初始化

所有的URBs提交必须初始化设备,管道,传输标志(可能是零),完成标志,超时标志(可能是零)。The URB_ASYNC_UNLINK 传输标志影响以后的 the usb_unlink_urb()路由。注意:如果用usb_unlink_urb()设置 URB_ASYNC_UNLINK 失败这被忽略。因为同步的断开连接使用

usb_kill_urb() 代替 。

所有的URBs也必须初始化transfer_buffer 和transfer_buffer_length他们可能提供 URB_SHORT_NOT_OK 传输标志,他表明shor他 read 将会被认为是错误的,那个标志在写请求中是无效的。

Bulk URBs may

使用 URB_ZERO_PACKET传输标志,表明大的OUT传输应当总是以一个小的包作为结束。 即便是加上一个额外的零长度的包。

控制URBs必须提供一个setup_packet。这个setup_packet 和transfer_buffer可能和 DMA一一对应或者不是,他们是相互独立的。这个传输标志 URB_NO_TRANSFER_DMA_MAP和 URB_NO_SETUP_DMA_MAP 表明已经被对应上了。对于不是URBs控制请求 URB_NO_SETUP_DMA_MAP 将被忽略。

中断URBs必须提供一个时间间隔,表示间隔多久给传输投票。在URB提交提交以后,这个interval域实际反映了传输是如何被列成进度表的。这个投票间隔可能比请求更加的频繁。例如:某些控制器有最大的间隔为32微秒,而其他的可能为1024微秒。同步的UEBs也有传输间隔。(注意到对于同步端点和告诉的中断端点,在端点标识符的传输编码是对数的。设备驱动必须通过他们自己把这些值转化为线性单元。

同步URBs一般使用URB_ISO_ASAP 传输标志,他表明主机控制器把传输列成表只要带宽利用允许,然后设置start_frame 以反映实际在提交期被选中的帧。否则这个驱动程序必须指定start_frame然后处理传输不能开始的情况。然而驱动程序不知道当前带宽是如何分配的,当他们用usb_get_current_frame_number () 找到当前帧,他们就不知道这个帧数字的变化(帧计数器值的变化范围从256―――65535)

同步URBs有一个不同的数据传输模型,因为他的服务只是尽最大努力。发话方用iso_frame_desc 结构中的number_of_packets的值提供特殊的分配URBs,每个这样的包是独立的ISO传输。同步URBs一般被排成队列,驱动程序保证传输具有两倍的缓存后提交请求,然后在and then explicitly resubmitted in completion handlers 。所以那些视频(音频)数据流以恒定比特率传输因为主机控制器支持队列。

Completion Callbacks:

这个Completion Callbacks在in_interrupt() 中产生,其中完成处理的第一件事应该事检查状态域。

这个状态域为所有的URBs提供。他被用来报告断开的urbs,和所有non-ISO 传输的状态。他不应该在URb被返回到完成处理(ompletion handler.)前被检验。

这个context域一般用来连接URBs并返回相关的驱动和请求状态

当这个Completion Callbacks: 为non-isochronous URB而被调用。这个actual_length 域表明有多少比特被传输。这个域被更新即使URb因为错误或断开终止。

同步传输的状态在status and actual_length fields (包含在 iso_frame_desc array 数组中)。错误数包含在error_count 。而这个Completion callbacks 则是通过提交urb来保证一个恒定的传输比特率。

struct urb

{

/* private, usb core and host controller only fields in the urb */

struct kref kref; /* reference涉及count of the URB */

spinlock_t lock; /* lock for the URB */

void *hcpriv; /* private data for host controller */

struct list_head urb_list; /* list pointer to all active urbs */

int bandwidth; /* bandwidth for INT/ISO request */

atomic_t use_count; /* concurrent submissions counter */

u8 reject; /* submissions will fail */

/* public, documented备有证明文件的fields in the urb that can be used by drivers *

struct usb_device *dev; /* (in) pointer to associated关联的device */

unsigned int pipe; /* (in) pipe information */

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 */

int transfer_buffer_length; /* (in) data buffer length */

int 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 */

int timeout; /* (in) timeout, in jiffies */

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_fill_control_urb – 初始化一个控制URB

* @urb: 指向要初始化的URB的指针

* @dev: 指向对应这个URB的usb_devicce的结构的指针

* @pipe: the endpoint pipe

* @setup_packet: 指向setup_packet buffer的指针

* @transfer_buffer: 指向 the transfer buffer 的指针

* @buffer_length: the transfer buffer 的长度

* @complete:指向 usb_complete_t 的指针

* @context: what to set the urb context to.

*

* 初始化这个控制URBs进行适当的信息提交

* it to a device.

*/

static inline 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)

{

spin_lock_init(&urb->lock);

urb->dev = dev;

urb->pipe = pipe;

urb->setup_packet = setup_packet;

urb->transfer_buffer = transfer_buffer;

urb->transfer_buffer_length = buffer_length;

urb->complete = complete;

urb->context = context;

}

/**

* usb_fill_bulk_urb – 帮助初始化一个bulk urb(((((同上)))))

* @urb: 指向要初始化的URB的指针

* @dev: pointer to the struct usb_device for this urb.

* @pipe: the endpoint pipe

* @transfer_buffer: pointer to the transfer buffer

* @buffer_length: length of the transfer buffer

* @complete: pointer to the usb_complete_t function

* @context: what to set the urb context to.

*

* Initializes a bulk urb with the proper information needed to submit it

* to a device.

*/

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,

void *context)

{

spin_lock_init(&urb->lock);

urb->dev = dev;

urb->pipe = pipe;

urb->transfer_buffer = transfer_buffer;

urb->transfer_buffer_length = buffer_length;

urb->complete = complete;

urb->context = context;

}

/**

* usb_fill_int_urb – 帮助初始化interrupt urb

* @urb: pointer to the urb to initialize.

* @dev: pointer to the struct usb_device for this urb.

* @pipe: the endpoint pipe

* @transfer_buffer: pointer to the transfer buffer

* @buffer_length: length of the transfer buffer

* @complete: pointer to the usb_complete_t function

* @context: what to set the urb context to.

* @interval: what to set the urb interval to, encoded like

通过把适当的信息递交给设备来初始化一个interrupt urb

注意到高速中断端点对端点间隔进行对数编码,在微帧中表达polling intervals (每毫秒8微帧)而在帧中为一帧每毫秒

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,

void *context,

int interval)

{

spin_lock_init(&urb->lock);

urb->dev = dev;

urb->pipe = pipe;

urb->transfer_buffer = transfer_buffer;

urb->transfer_buffer_length = buffer_length;

urb->complete = complete;

urb->context = context;

if (dev->speed == USB_SPEED_HIGH)

urb->interval = 1 << (interval - 1);

else

urb->interval = interval;

urb->start_frame = -1;

}

extern void usb_init_urb(struct urb *urb);

extern struct urb *usb_alloc_urb(int iso_packets, int mem_flags);

extern void usb_free_urb(struct urb *urb);

#define usb_put_urb usb_free_urb

extern struct urb *usb_get_urb(struct urb *urb);

extern int usb_submit_urb(struct urb *urb, int mem_flags);

extern int usb_unlink_urb(struct urb *urb);

extern void usb_kill_urb(struct urb *urb);

#define HAVE_USB_BUFFERS

void *usb_buffer_alloc (struct usb_device *dev, size_t size,

int mem_flags, dma_addr_t *dma);

void usb_buffer_free (struct usb_device *dev, size_t size,

void *addr, dma_addr_t dma);

struct urb *usb_buffer_map (struct urb *urb);

#if 0

void usb_buffer_dmasync (struct urb *urb);

#endif

void usb_buffer_unmap (struct urb *urb);

struct scatterlist;

int usb_buffer_map_sg (struct usb_device *dev, unsigned pipe,

struct scatterlist *sg, int nents);

#if 0

void usb_buffer_dmasync_sg (struct usb_device *dev, unsigned pipe,

struct scatterlist *sg, int n_hw_ents);

#endif

/*-------------------------------------------------------------------*

* SYNCHRONOUS CALL SUPPORT *

*-------------------------------------------------------------------*/

extern int usb_control_msg(struct usb_device *dev, unsigned int pipe,

__u8 request, __u8 requesttype, __u16 value, __u16 index,

void *data, __u16 size, int timeout);

extern int usb_bulk_msg(struct usb_device *usb_dev, unsigned int pipe,

void *data, int len, int *actual_length,

int timeout);

/* selective suspend/resume */

extern int usb_suspend_device(struct usb_device *dev, u32 state);

extern int usb_resume_device(struct usb_device *dev);

/* wrappers around usb_control_msg() for the most common standard requests */

extern int usb_get_descriptor(struct usb_device *dev, unsigned char desctype,

unsigned char descindex, void *buf, int size);

extern int usb_get_status(struct usb_device *dev,

int type, int target, void *data);

extern int usb_get_string(struct usb_device *dev,

unsigned short langid, unsigned char index, void *buf, int size);

extern int usb_string(struct usb_device *dev, int index,

char *buf, size_t size);

/* wrappers that also update important state inside usbcore */

extern int usb_clear_halt(struct usb_device *dev, int pipe);

extern int usb_reset_configuration(struct usb_device *dev);

extern int usb_set_interface(struct usb_device *dev, int ifnum, int alternate);

timeouts, in seconds,被用来发送或接收控制信息。具有代表性的他们一般在他们被声明后几个帧后完成。Usb标识5秒为超时,或者在某些时候多一点

#define USB_CTRL_GET_TIMEOUT 5

#define USB_CTRL_SET_TIMEOUT 5

struct usb_sg_request 对分散/集中的I/O的支持

* @status:0代表正确,其他代表错误

* @bytes: 计算传输的比特

这些请求被usb_sg_init() 初始化,然后被用作请求操作传递给usb_sg_wait() or usb_sg_cancel()。大多数请求目标的成员不是给驱动程序作为入口

这个状态和比特数的值只在usb_sg_wait() 返回后有效。如果这status是零的话,那么bytecount就符合请求的总数。

在一个错误完成以后,驱动程序就能够在端点处清除一个暂停的情形

struct usb_sg_request {

int status;

size_t bytes;

一以下的成员对usb核私有不对外开放

spinlock_t lock;

struct usb_device *dev;

int pipe;

struct scatterlist *sg;

int nents;

int entries;

struct urb **urbs;

int count;

struct completion complete;

};

int usb_sg_init (

struct usb_sg_request *io,

struct usb_device *dev,

unsigned pipe,

unsigned period,

struct scatterlist *sg,

int nents,

size_t length,

int mem_flags

);

void usb_sg_cancel (struct usb_sg_request *io);

void usb_sg_wait (struct usb_sg_request *io);

/* -------------------------------------------------------------------------- */

称这个实体为管道是形象化的说法,一个usb管道就是把一些阻碍简单化,它基本具备一以下条件:

设备数字(7bit)

端点数字(4bit)

当前的Data0/1 的状态(1bit)在usb1.1时使用,现在没有了

方向(1bit)

速度(1bit)(历史的,在usb1.1时明确规定,现在没有了)

最大的包尺寸(2 bits: 8, 16, 32 or 64) 在usb1.1时使用,现在没有了

管道类型(2bit:控制,中断,块,同步)

那是18比特,实际上,没有更多。Usb开发人员把这18个bit作为”光荣“的数据结构

我们不要进入这个圈套,我们只要简单的认为他是一个unsigned int类型。它的编码就是

* - max size: bits 0-1 [Historical; now gone.]

* - direction: bit 7 (0 = Host-to-Device [Out],

* 1 = Device-to-Host [In] ...

* like endpoint bEndpointAddress)

* - device: bits 8-14 ... bit positions known to uhci-hcd

* - endpoint: bits 15-18 ... bit positions known to uhci-hcd

* - Data0/1: bit 19 [Historical; now gone. ]

* - lowspeed: bit 26 [Historical; now gone. ]

* - pipe type: bits 30-31 (00 = isochronous, 01 = interrupt,

* 10 = control, 11 = bulk)

为什么呢?因为这是任意的,我们选择无论什么编码,多取决域我们自己。这个和那个UHCI规格有很多共享的比特,因此很多的UHCI驱动程序能够适当的掩盖比特位。

注:这些不是标准的USB_ENDPOINT_XFER_*值

#define PIPE_ISOCHRONOUS 0

#define PIPE_INTERRUPT 1

#define PIPE_CONTROL 2

#define PIPE_BULK 3

#define usb_maxpacket(dev, pipe, out) (out

? (dev)->epmaxpacketout[usb_pipeendpoint(pipe)]

: (dev)->epmaxpacketin [usb_pipeendpoint(pipe)] )

#define usb_pipein(pipe) ((pipe) & USB_DIR_IN)

#define usb_pipeout(pipe) (!usb_pipein(pipe))

#define usb_pipedevice(pipe) (((pipe) >> 8) & 0x7f)

#define usb_pipeendpoint(pipe) (((pipe) >> 15) & 0xf)

#define usb_pipetype(pipe) (((pipe) >> 30) & 3)

#define usb_pipeisoc(pipe) (usb_pipetype((pipe)) == PIPE_ISOCHRONOUS)

#define usb_pipeint(pipe) (usb_pipetype((pipe)) == PIPE_INTERRUPT)

#define usb_pipecontrol(pipe) (usb_pipetype((pipe)) == PIPE_CONTROL)

#define usb_pipebulk(pipe) (usb_pipetype((pipe)) == PIPE_BULK)

/* The D0/D1 toggle bits ... USE WITH CAUTION (they're almost hcd-internal) */

#define usb_gettoggle(dev, ep, out) (((dev)->toggle[out] >> (ep)) & 1)

#define usb_dotoggle(dev, ep, out) ((dev)->toggle[out] ^= (1 << (ep)))

#define usb_settoggle(dev, ep, out, bit) ((dev)->toggle[out] = ((dev)->toggle[out] & ~(1 << (ep))) | ((bit) << (ep)))

/* Endpoint halt control/status ... likewise USE WITH CAUTION */

端点暂停 控制和状态 和USE WITH CAUTION 相似

#define usb_endpoint_running(dev, ep, out) ((dev)->halted[out] &= ~(1 << (ep)))

#define usb_endpoint_halted(dev, ep, out) ((dev)->halted[out] & (1 << (ep)))

static inline unsigned int __create_pipe(struct usb_device *dev, unsigned int endpoint)

{

return (dev->devnum << 8) | (endpoint << 15);

}

/* Create various pipes... */

#define usb_sndctrlpipe(dev,endpoint) ((PIPE_CONTROL << 30) | __create_pipe(dev,endpoint))

#define usb_rcvctrlpipe(dev,endpoint) ((PIPE_CONTROL << 30) | __create_pipe(dev,endpoint) | USB_DIR_IN)

#define usb_sndisocpipe(dev,endpoint) ((PIPE_ISOCHRONOUS << 30) | __create_pipe(dev,endpoint))

#define usb_rcvisocpipe(dev,endpoint) ((PIPE_ISOCHRONOUS << 30) | __create_pipe(dev,endpoint) | USB_DIR_IN)

#define usb_sndbulkpipe(dev,endpoint) ((PIPE_BULK << 30) | __create_pipe(dev,endpoint))

#define usb_rcvbulkpipe(dev,endpoint) ((PIPE_BULK << 30) | __create_pipe(dev,endpoint) | USB_DIR_IN)

#define usb_sndintpipe(dev,endpoint) ((PIPE_INTERRUPT << 30) | __create_pipe(dev,endpoint))

#define usb_rcvintpipe(dev,endpoint) ((PIPE_INTERRUPT << 30) | __create_pipe(dev,endpoint) | USB_DIR_IN)

/* -------------------------------------

#ifdef DEBUG

#define dbg(format, arg...) printk(KERN_DEBUG "%s: " format "n" , __FILE__ , ## arg)

#else

#define dbg(format, arg...) do {} while (0)

#endif

#define err(format, arg...) printk(KERN_ERR "%s: " format "n" , __FILE__ , ## arg)

#define info(format, arg...) printk(KERN_INFO "%s: " format "n" , __FILE__ , ## arg)

#define warn(format, arg...) printk(KERN_WARNING "%s: " format "n" , __FILE__ , ## arg)

#endif /* __KERNEL__ */

#endif

查看linux驱动使用的头文件,[转载]linux下usb驱动头文件的usb.h(二)相关推荐

  1. Linux驱动开发常用头文件

    头文件目录中总共有32个.h头文件.其中主目录下有13个,asm子目录中有4个,linux子目录中有10个,sys子目录中有5个.这些头文件各自的功能如下: 1.主目录 <a.out.h> ...

  2. linux打开core文件,[转载]linux下core文件设置与查看

    程序异常推出时,内核会生成一个core文件(是内存映像以及调试信息).可以通过使用gdb来查看core文件,指示出导致程序出错的代码所在的文件和行数. 1.查看系统中core文件生成的开关是否打开 1 ...

  3. enc28j60 linux 驱动_enc28j60网卡驱动模块添加进Linux内核,Kconfig,Makefile配置过程...

    这里是要把 http://www.linuxidc.com/Linux/2017-02/140819.htm 中的enc28j60网卡驱动模块,添加到2.6.22.6内核中,这个模块代码不需要任何修改 ...

  4. ac3165 linux驱动_一文读懂Linux系统启动流程

    Linux启动管理 11.1 CentOS 6.x系统启动过程详解 CentOS 6.x系统启动过程发生了较大的变化,使用Upstart启动服务取代了原先的System V init启动服务.Upst ...

  5. Linux 驱动开发 四十八:Linux INPUT 子系统实验

    一.input 子系统简介 input 就是输入的意思,因此 input 子系统就是管理输入的子系统,是 Linux 内核针对某一类设备而创建的框架. 比如按键输入.键盘.鼠标.触摸屏等等这些都属于输 ...

  6. Linux驱动视频教程推荐,隆重推荐:linux驱动基础开发系列免费教程独家版本

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 相信大家经常网上闲逛,会经常碰到很多的linux免费教程之类得,但是今天我推荐的这个linux驱动基础开发系列免费教程可不是网上可以随意找到得.废话少说: ...

  7. h3c linux驱动 wn612_H3C iNode智能客户端安装指导(Linux)-7.3-5PW102

    如果系统中已经存在老版本的iNode客户端,需要先卸载老版本的客户端,然后再安装新版本的客户端. Linux iNode支持在主流的Linux操作系统中安装,比较常用的Linux操作系统包括: ·   ...

  8. Linux 驱动开发 四十六:Linux MISC驱动实验

    misc 的意思是混合.杂项的,因此MISC 驱动也叫做杂项驱动,也就是当我们板子上的某些外设无法进行分类的时候就可以使用 MISC 驱动. MISC 驱动其实就是最简单的字符设备驱动,通常嵌套在 p ...

  9. Linux 驱动开发 三十五:Linux 内核时钟管理

    参考: linux时间管理,时钟中断,系统节拍_u010936265的博客-CSDN博客_系统节拍时钟中断 Linux内核时钟系统和定时器实现_anonymalias的专栏-CSDN博客_linux内 ...

  10. linux删除文件_【Linux实战】Vim编辑器和恢复ext4下误删除的文件

    学神IT教育:XueGod-IT 良心教育 贴心服务 1.  vim主要模式介绍,vim命令模式. 确保系统已经安装了VIM工具 [root@panda ~]# rpm -qf `which vim` ...

最新文章

  1. 使用Python,OpenCV+OCR检测护照图像中的机器可读区域(MRZ Machine-Readable Zones)
  2. 【学习笔记】矩阵树定理(Matrix-Tree)
  3. SAP MM ME57界面看到的供应源跟Source List主数据不一致?
  4. Django1.6 用Form实现注册登录注销修改密码(含代码!)
  5. Linux下RabbitMQ服务器搭建
  6. IIS中“使用 XSL 样式表无法查看 XML 输入”问题的解决
  7. 浏览器缓存问题原理以及解决方案
  8. 让IE浏览器提示下载或直接打开word文档
  9. 玩转SpringCloud Security OAuth2资源授权动态权限扩展
  10. 华为手机下拉菜单变大_手机投屏到电视最好的方法和手机可同屏也可电视当电脑手机当鼠标...
  11. StanfordDB class自学笔记 (5) JSON Data
  12. 线性代数及其应用(part2)--特征方程
  13. 嵌入式linux应用开发完全手册 第2版面市
  14. 从未在一起更让人遗憾_我们从未在一起更遗憾
  15. 算法实现- 数字转金额大写
  16. opencv半透明填充不规则区域
  17. django之admin调整页面展示
  18. 企业邮箱的反垃圾邮件功能是怎么实现的?
  19. winhex万能恢复磁盘数据
  20. python nacos注册中心_使用Nacos作为注册中心和配置中心

热门文章

  1. VISTA下载全集(上)
  2. Java实现 LeetCode 24 两两交换链表中的节点
  3. UNIX文件表示(四章)
  4. 怎么把cad的图导入ps_如何把CAD图导入PS
  5. java ice spring_卩大王叫我来巡山-【Springboot + Ice 系列文章】1.搭建开发环境
  6. 怎样成为一名优秀的科学家?
  7. hdu4864 Task
  8. 紫外测试的常见问题及解答
  9. 非零基础自学Java (老师:韩顺平) 第10章 面向对象编程(高级部分) 10.6 抽象类
  10. 1399. 坐船旅行