Linux usb子系统(二) _usb-skeleton.c精析

2023-10-29

"./drivers/usb/usb-skeleton.c"是内核提供给usb设备驱动开发者的海量存储usb设备的模板程序, 程序不长, 通用性却很强,十分经典, 深入理解这个文件可以帮助我们更好的理解usb子系统以及usb设备驱动框架, 写出更好的usb海量存储设备驱动。

匹配前

既然是一个usb设备驱动的模板,那么就少不了构造一个usb_driver对象并将其注册到内核中,

650 static struct usb_driver skel_driver = {
651         .name =         "skeleton",
652         .probe =        skel_probe,
653         .disconnect =   skel_disconnect,
654         .suspend =      skel_suspend,
655         .resume =       skel_resume,
656         .pre_reset =    skel_pre_reset,
657         .post_reset =   skel_post_reset,
658         .id_table =     skel_table,
659         .supports_autosuspend = 1,
660 };
661 
662 module_usb_driver(skel_driver);

关于这个对象的域,在上一篇已经解释了,这里,我们主要关心的是skel_table,它决定了这个驱动匹配到哪个设备,从下面的定义可以看出,这个驱动是按照device进行匹配的,

 30 static const struct usb_device_id skel_table[] = {
 31         { USB_DEVICE(USB_SKEL_VENDOR_ID, USB_SKEL_PRODUCT_ID) },
 32         { }                                     /* Terminating entry */
 33 };
 34 MODULE_DEVICE_TABLE(usb, skel_table);

匹配后

资源类

接下来,看一下这个驱动对于资源类的定义,这可是整个驱动程序的纽带,管理着整个驱动程序各个函数与接口共用的资源, 不得不说这个注释真的是内核中少有的详细, skeleton主要是针对海量存储设备的,所以其资源对象中封装了很多缓冲区的信息VS中断设备只要一个urb即可搞定数据传输问题

 49 struct usb_skel {
 50         struct usb_device       *udev;                  /* the usb device for this device */
 51         struct usb_interface    *interface;             /* the interface for this device */
 52         struct semaphore        limit_sem;              /* limiting the number of writes in progress
 53         struct usb_anchor       submitted;              /* in case we need to retract our submission
 54         struct urb              *bulk_in_urb;           /* the urb to read data with */
 55         unsigned char           *bulk_in_buffer;        /* the buffer to receive data */
 56         size_t                  bulk_in_size;           /* the size of the receive buffer */
 57         size_t                  bulk_in_filled;         /* number of bytes in the buffer */
 58         size_t                  bulk_in_copied;         /* already copied to user space */
 59         __u8                    bulk_in_endpointAddr;   /* the address of the bulk in endpoint */
 60         __u8                    bulk_out_endpointAddr;  /* the address of the bulk out endpoint */
 61         int                     errors;                 /* the last request tanked */
 62         bool                    ongoing_read;           /* a read is going on */
 63         spinlock_t              err_lock;               /* lock for errors */
 64         struct kref             kref;
 65         struct mutex            io_mutex;               /* synchronize I/O with disconnect */
 66         wait_queue_head_t       bulk_in_wait;           /* to wait for an ongoing read */
 67 };

struct usb_skel
--50-->驱动操作的usb_device对象
--51-->驱动操作的usb_interface对象, 这两个都是设备信息, VS i2c-s3c2410.c 通过将设备信息在probe中拷贝出来以保存到驱动资源对象中, 这里也是同样的思路. struct usb_interface->dev域之于usb_skel以及其他接口函数, 相当于struct device域之于s3c24xx_i2c以及其他接口函数, 都是在各个接口函数中流动的
--54-->使用的urb对象
--55-->用于接收数据的buf指针
--56-->标识要接收数据长度的域
--57-->标识当前缓冲区有多少有效数据的域
--58-->标识当前缓冲区已经被拷贝走多少数据的域,skeleton不会清空缓冲区,而是使用各种长度表示来决定已经占用了多少,超出长度的部分,是否被清零无所谓。他们之间的关系见下图
--59-->bulk设备的输入端点
--60-->bulk设备的输出端点
--62-->设备可读标志位,0表示可读,1表示不可读
--64-->kref供内核引用计数用

usb_skeleton还参考内核中已有的to_platform_device等结构封装了一个to_skel_dev, 这种写法值得借鉴

 68 #define to_skel_dev(d) container_of(d, struct usb_skel, kref)

probe

匹配成功后,按照套路就该请probe上场了

490 static int skel_probe(struct usb_interface *interface,
491                       const struct usb_device_id *id)
492 {
493         struct usb_skel *dev;
494         struct usb_host_interface *iface_desc;
495         struct usb_endpoint_descriptor *endpoint;
496         size_t buffer_size;
497         int i;
498         int retval = -ENOMEM;
499 
500         /* allocate memory for our device state and initialize it */
501         dev = kzalloc(sizeof(*dev), GFP_KERNEL);
506         kref_init(&dev->kref);
507         sema_init(&dev->limit_sem, WRITES_IN_FLIGHT);
508         mutex_init(&dev->io_mutex);
509         spin_lock_init(&dev->err_lock);
510         init_usb_anchor(&dev->submitted);
511         init_waitqueue_head(&dev->bulk_in_wait);
512 
513         dev->udev = usb_get_dev(interface_to_usbdev(interface));
514         dev->interface = interface;
515 
516         /* set up the endpoint information */
517         /* use only the first bulk-in and bulk-out endpoints */
518         iface_desc = interface->cur_altsetting;
519         for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
520                 endpoint = &iface_desc->endpoint[i].desc;
521 
522                 if (!dev->bulk_in_endpointAddr &&
523                     usb_endpoint_is_bulk_in(endpoint)) {
524                         /* we found a bulk in endpoint */
525                         buffer_size = usb_endpoint_maxp(endpoint);
526                         dev->bulk_in_size = buffer_size;
527                         dev->bulk_in_endpointAddr = endpoint->bEndpointAddress;
528                         dev->bulk_in_buffer = kmalloc(buffer_size, GFP_KERNEL);
534                         dev->bulk_in_urb = usb_alloc_urb(0, GFP_KERNEL);
540                 }
542                 if (!dev->bulk_out_endpointAddr &&
543                     usb_endpoint_is_bulk_out(endpoint)) {
544                         /* we found a bulk out endpoint */
545                         dev->bulk_out_endpointAddr = endpoint->bEndpointAddress;
546                 }
547         }
553 
554         /* save our data pointer in this interface device */
555         usb_set_intfdata(interface, dev);
556 
557         /* we can register the device now, as it is ready */
558         retval = usb_register_dev(interface, &skel_class);
566 
567         /* let the user know what node this device is now attached to */
568         dev_info(&interface->dev,
569                  "USB Skeleton device now attached to USBSkel-%d",
570                  interface->minor);
571         return 0;
578 }
579 

skel_probe
--501-->为资源对象申请空间, 注意这里的写法: dev = kzalloc(sizeof(*dev), GFP_KERNEL);
--506-->初始化usb_skel->kref
--507-->初始化usb_skel->limit_sem
--508-->初始化usb_skel->io_mutex);
--509-->初始化usb_skel->err_lock);
--510-->初始化usb_skel->submitted);
--511-->初始化usb_skel->bulk_in_wait
--513-->初始化usb_skel->udev,将匹配到的usb_device地址存储下来
--514-519-->初始化usb_skel对象其他域.
--555-->将我们的资源对象藏到interface->dev->p->driver_data中
--558-->注册一个usb_device对象到内核、申请一个次设备号并创建设备文件, ==>intf->usb_dev = device_create(usb_class->class, &intf->dev,MKDEV(USB_MAJOR, minor), class_driver,"%s", temp);

通过上面的分析, 我们发现了一个skeleton和usbmouse不一样的地方:skeleton构造了usb_class_driver对象并使用usb_register_dev注册一个usb设备, 而usbmouse作为input子系统, 仅需要input_register(input_dev)即可, 不用usb设备的注册问题, 产生这个差别的原因是skeleton是针对bulk urb设备的, 而usbmouse是针对interrupt urb设备的。对于bulk设备,我们会对设备进行读写操作,而不仅仅是读操作,所以在bulk urb设备驱动中要实现相应的操作方法集并绑定到设备文件一起注册到内核,这个工作就是由usb_register_dev来完成。为了使用这个函数,我们需要构造一个usb_class_driver对象,其中最重要的就是本节要讨论的skel_fops了。这个域也是struct file_operations类型的,所有的读写方法的实现都要注册到这个域中。
既然提到fops,我们主要关心的就三个方法的实现:open, read和write,考虑到read和write在操作逻辑类似,所以本文只讨论open和read

open

 83 static int skel_open(struct inode *inode, struct file *file)
 84 {
 85         struct usb_skel *dev;
 86         struct usb_interface *interface;
 87         int subminor;
 88         int retval = 0;
 89 
 90         subminor = iminor(inode);
 91 
 92         interface = usb_find_interface(&skel_driver, subminor);
100         dev = usb_get_intfdata(interface);
106         retval = usb_autopm_get_interface(interface);
110         /* increment our usage count for the device */
111         kref_get(&dev->kref);
112 
113         /* save our object in the file's private structure */
114         file->private_data = dev;
115 
117         return retval;
118 }

skel_open()
--90-->从inode中获取次设备号
--92-->根据skel_driver对象和次设备号获取usb_interface对象,至此就找到了设备
--100-->从interface->dev->p->driver_data中获取资源对象的地址,这个地址是在probe--555--中藏到这的
--110-->引用计数加一
--114-->关键,将之前获得的资源对象的地址藏在file->private_data中,这样在所有的cdev接口之间都可以使用资源对象了,和将资源对象地址藏到interface中以便在usb_driver的接口函数之间流动的思想是一样的。

read

打开了设备,接下来就可以读写了,skeleton中对于读操作的关键函数调用关系如下,我们依照这个调用树依次分析

skel_read()
skel_do_read_io(dev, count)
usb_fill_bulk_urb(...);
usb_submit_urb(dev->bulk_in_urb, GFP_KERNEL);

首先是skel_read(),这个函数是应用层读设备时回调的函数,它试图实现这样一个功能: 如果内核缓冲区有数据就将适当的数据拷贝给应用层, 如果没有就调用skel_do_read_io来向设备请求数据

226 static ssize_t skel_read(struct file *file, char *buffer, size_t count,
227                          loff_t *ppos)
228 {
229         struct usb_skel *dev;
230         int rv;
231         bool ongoing_io;
232 
233         dev = file->private_data;
255         if (ongoing_io) {
256                 /* nonblocking IO shall not wait */
257                 if (file->f_flags & O_NONBLOCK) {
258                         rv = -EAGAIN;
259                         goto exit;
260                 }
265                 rv = wait_event_interruptible(dev->bulk_in_wait, (!dev->ongoing_read));
266                 if (rv < 0)
267                         goto exit;
268         }
269 
270         /* errors must be reported */
271         rv = dev->errors;
272         if (rv < 0) {
273                 /* any error is reported once */
274                 dev->errors = 0;
275                 /* to preserve notifications about reset */
276                 rv = (rv == -EPIPE) ? rv : -EIO;
277                 /* report it */
278                 goto exit;
279         }
286         if (dev->bulk_in_filled) {
287                 /* we had read data */
288                 size_t available = dev->bulk_in_filled - dev->bulk_in_copied;
289                 size_t chunk = min(available, count);
290 
291                 if (!available) {
296                         rv = skel_do_read_io(dev, count);
297                         if (rv < 0)
298                                 goto exit;
299                         else
300                                 goto retry;
301                 }
307                 if (copy_to_user(buffer,
308                                  dev->bulk_in_buffer + dev->bulk_in_copied,
309                                  chunk))
310                         rv = -EFAULT;
311                 else
312                         rv = chunk;
313 
314                 dev->bulk_in_copied += chunk;
320                 if (available < count)
321                         skel_do_read_io(dev, count - chunk);
322         } else {
323                 /* no data in the buffer */
324                 rv = skel_do_read_io(dev, count);
325                 if (rv < 0)
326                         goto exit;
327                 else
328                         goto retry;
329         }
330 exit:
331         mutex_unlock(&dev->io_mutex);
332         return rv;
333 }

skel_read()
--233-->都是套路,先将藏在file_private_data中的资源对象拿出来
--255-268-->资源对象中的可读标志位,不可读的时候,判断IO是否允许阻塞,如果不允许就直接返回,允许阻塞就使用资源对象中的等待队列头,将进程加入等待队列,使用的是interruptible版本的wait,如果睡眠中的进程是被中断唤醒的,那么rv==-1,函数直接返回。
--286-->执行到这一行只有一个情况:设备可读了!如果缓冲区满执行第一个语句块,否则执行下面的语句块
--288-->缓冲区满时, 获取可拷贝的数据的大小.
--289-->在可拷贝的大小和期望拷贝的大小中取小者给chunk
--291-->可拷贝的数据为0, 而usb_skel->bulk_in_filled被置位才能进入这里, 所以只有一种情况: 缓冲区的数据已经拷贝完了
--292-->既然数据已经拷贝完毕, 调用skel_do_read_io发起请求
--300-->请求了数据,设备也反馈了,但是什么数据都没有,重试
307-->从内核缓冲区usb_skel->bulk_in_buffer + usb_skel->bulk_in_copied开始(就是剩余未拷贝数据的首地址)拷贝chunk byte的数据到应用层
--314-->更新usb_skel->bulk_in_copied的值
--320-->如果可拷贝数据的大小<期望拷贝的大小, 那么显然刚才chunk=availible, 已经将所有的数据拷贝到应用层, 但是还不能满足应用层的需求, 调用skel_do_read_io来继续向设备索取数据, 当然, 索取的大小是没满足的部分, 即count-chunk
--324-->usb_skel->bulk_in_filled没有被置位, 表示内核缓冲区没有数据, 调用skel_do_read_io索取数据, 当然, 索取的大小是全部数据, 即count

刚才也说了, 如果缓冲区不能满足应用层需求的时候, 就会调用下面这个函数向bulk usb设备请求数据, 得到数据后将数据放到缓冲区并将相应的标志位置1/置0

189 static int skel_do_read_io(struct usb_skel *dev, size_t count)
190 {
191         int rv;
193         /* prepare a read */
194         usb_fill_bulk_urb(dev->bulk_in_urb,dev->udev,usb_rcvbulkpipe(dev->udev,dev->bulk_in_endpointAddr),dev->bulk_in_buffer, min(dev->bulk_in_size, count),skel_read_bulk_callback,dev);
204         dev->ongoing_read = 1;
206 
207         /* submit bulk in urb, which means no data to deliver */
208         dev->bulk_in_filled = 0;
209         dev->bulk_in_copied = 0;
210 
211         /* do it */
212         rv = usb_submit_urb(dev->bulk_in_urb, GFP_KERNEL);
223         return rv;
224 }

skel_do_read_io()
--194-->向usb核心提交一个urb, 将资源对象dev藏在urb->context中随着urb实参传入回调函数, 和usb_fill_int_urb不同, usb_fill_bulk_urb注册的时候需要将缓冲区首地址和请求数据的大小和urb绑定到一起一同提交, 这样才知道向bulk设备请求的数据的大小, bulk设备有数据返回的时候才知道放哪.
--204-->将usb_skel->ongoing_read置1, 表示没有数据可读
--208-->将usb_skel->bulk_in_filled置0, 表示内核缓冲区没有数据可读
--209-->将usb_skel->bulk_in_copied置0, 表示没有任何数据已被拷贝
--212-->做好准备工作之后, 命令usb核心发送urb

请求被发出后, usb总线就会静待设备的反馈, 设备有反馈后就会回调urb的注册函数, 我们看看这个回调函数都做了什么

163 static void skel_read_bulk_callback(struct urb *urb)
164 {
165         struct usb_skel *dev;
166 
167         dev = urb->context;
168 
169         spin_lock(&dev->err_lock);
170         /* sync/async unlink faults aren't errors */
181                 dev->bulk_in_filled = urb->actual_length;
183         dev->ongoing_read = 0;
184         spin_unlock(&dev->err_lock);
185 
186         wake_up_interruptible(&dev->bulk_in_wait);
187 }

skel_read_bulk_callback
--167-->套路, 先把资源对象拿出来
--181-->将表示设备反馈的数据长度urb->actual_length赋值给usb_skel->bulk_in_filled, 表示缓冲区有数据了
--183-->将usb_skel->ongoing_read置0, 表示可读了!
--186-->唤醒因为没有数据可读而陷入睡眠的进程

分析到这里, 应用层就可以通过usb_skeleton驱动从USB海量存储设备中获取数据了!!!写入数据的思路是一样的, 我这里就不罗嗦了.

锁的使用

除了对缓冲区管理的巧妙, usb_skeleton.c中对于并发控制技术的使用也值得学习, 在构造资源对象usb_skel的时候, 这个驱动使用了semaphore ,spinlock,mutex三种常用的并发控制锁机制, 接下来我们讨论一下内核大牛们是如何在不同应用场景中使用这些技术的.

semaphore

semaphore是以进程为单位的, 其典型特点就是当一个进程不能获取信号量的时候, 会进陷入睡眠让出CPU, 所以中断上下文不能使用semaphore。在usb_skeleton.c中,semaphore在如下场景中被使用

335 static void skel_write_bulk_callback(struct urb *urb)
336 {
358         up(&dev->limit_sem);                                                                        
359 }

361 static ssize_t skel_write(struct file *file, const char *user_buffer,
362                           size_t count, loff_t *ppos)
363 {
376         /*
377          * limit the number of URBs in flight to stop a user from using up all
378          * RAM
379          */
380         if (!(file->f_flags & O_NONBLOCK)) {
381                 if (down_interruptible(&dev->limit_sem)) {
382                         retval = -ERESTARTSYS;
383                         goto exit;
384                 }
385         } else {
386                 if (down_trylock(&dev->limit_sem)) {
387                         retval = -EAGAIN;
388                         goto exit;
389                 }
390         }
467         return retval;
468 }

spinlock

当不能获取临界资源时,使用spinlock的进程不会陷入睡眠, 而是忙等,所以spinlock可以用在中断上下文,但是如果不能获取资源又不出让CPU,会浪费系统资源,所以被spinlock保护的临界区不能太长。usb_skeleton主要在以下场景中使用了spinlock

226 static ssize_t skel_read(struct file *file, char *buffer, size_t count,
227                          loff_t *ppos)
228 {
250 retry:
251         spin_lock_irq(&dev->err_lock);
252         ongoing_io = dev->ongoing_read;
253         spin_unlock_irq(&dev->err_lock);
332         return rv;
333 }

mutex

mutex只是用来保证互斥,在不使用trylock的时候,和semaphore一样会在得不到锁的时候进入睡眠。usb_skeleton在以下场景中使用mutex

226 static ssize_t skel_read(struct file *file, char *buffer, size_t count,
227                          loff_t *ppos)
228 {
239         /* no concurrent readers */
240         rv = mutex_lock_interruptible(&dev->io_mutex);
330 exit:
331         mutex_unlock(&dev->io_mutex);
332         return rv;
333 }
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Linux usb子系统(二) _usb-skeleton.c精析 的相关文章

  • 在c++中接收USB设备插入的通知/事件/信号

    我已经寻找了一段时间 但一直无法找到这个问题的答案 我正在尝试接收特定 USB 设备的连接通知 这是我所知道的 我运行的是 linux 内核版本 3 2 我有识别 USB 设备所需的 ID 我创建了一个用于读取和写入设备的内核模块 内核模块
  • 新画图不好用?『 图层困扰?』『 剪切板拷贝失败?』旧版画图软件yyds

    樊梓慕 个人主页 个人专栏 C语言
  • AS3 USB 游戏控制器支持

    如何让 USB 控制器控制我的 AS3 游戏 我现在正在使用键盘 我考虑过映射按键 但我必须为此使用一个插件 我只是希望用户能够购买一个不需要任何驱动程序下载 配置的 简单控制器 他们应该能够插入 USB 控制器并通过浏览器中的 Flash
  • macOS下如何查找串口的父USB设备?

    以下是我尝试枚举 Mac 上找到的所有串行端口 并遍历设备节点树以查找 USB 串行适配器的父 USB 设备的方法 import
  • 如何检测Android平板电脑中充当USB Host的USB设备?

    我尝试在代码的帮助下开发一个示例应用程序开发者 android com http developer android com guide topics connectivity usb host html 我的代码看起来像这样 public
  • USB 控制传输能否保证交付?

    USB 2 0 指定 http www usb org developers docs usb20 docs 4 种传输类型 第 5 4 节传输类型 控制转移 同步传输 中断传输 批量转账 第 5 8 节规定批量转账提供 在带宽可用的基础上
  • 如何拦截并翻译USB事件

    我想使用飞利浦 LFH 2330 查看图像 使用 Windows Image Viewer 或其他查看器 如 IrfanView 等 LFH 2330 是一款带有四个踏板的脚踏控制器 参见here http www aaaaudio net
  • USB 传输数据

    我正在尝试通过 USB 发送和接收数据 我的设备 Acer Iconia A500 拥有连接到设备所需的一切 一切都很好并且工作正常 但是当我尝试发送和接收数据时 它的行为并不像预期的 这是我的代码 for this is the main
  • adb 未检测到 micromax a111

    最近我购买了 micromax a111 Android 设备 我是安卓开发者 因此尝试使用该设备进行调试选项 但 adb 无法检测到该设备 我也更改了设置 但没有成功 如果有人有解决方案 请帮助我解决这个问题 micromax没有官方的P
  • 在 Mac 上通过 USB 访问 iOS 设备

    我有一个移动应用程序需要将文件传输到数据库 然而 其规范之一是 如果用户没有设置无线网络 则应该能够将其插入 Mac 并将文件传输到数据库 然而 我遇到的问题是如何将上述文件从 iPad 上通过 USB 传输到计算机上 上周我研究了各种解决
  • Ubuntu 10.1下如何识别多个USB串口适配器

    我正在 Ubuntu 10 1 下从多个相同的 USB 串行适配器读取数据 有时 它们的 dev tty 路径会发生变化 例如 如果在启动时连接了其他 USB 设备 我需要一种通过任何此类更改重复引用同一适配器的方法 据 udevadm 称
  • libv4l2:打开流时出错:设备上没有剩余空间

    我尝试为 opencv 获取立体声对 我将 Logitech B910 和 Logitech C910 网络摄像头连接到 USB 但有这个错误 我玩弄了怪癖参数并设置outfmt mjpeg在mplayer中 但又出现此错误 在哪里可以找到
  • 如何对 USB/HID/蓝牙适配器进行逆向工程。使用什么工具?

    需要对蓝牙USB鼠标适配器进行逆向工程 并使用鼠标板测量距离 并用绘图库 http matplotlib org 我发现这些工具可以帮助完成此任务 Linux lsusb usbmon usbhid转储 rfcomm hidrd 转换 xx
  • 从网络浏览器中检测和访问 USB 设备 - 即使用插件

    是否可以通过插件 activeX Java 或 Flex 编写一个能够检测从 USB 设备通道读取数据流的浏览器插件 我做了一些研究 但没有找到答案 http support microsoft com kb 832678 http sup
  • 如何查找USB盘符?

    我正在编写一个安装程序来将应用程序安装到 USB 驱动器 该应用程序只能从 USB 驱动器使用 因此它可以通过自动选择要安装的 USB 驱动器来为用户节省额外的步骤 我可能会尝试使用 Nullsoft 或 MSI 进行安装 但由于我最熟悉
  • 当端点和 PMA 地址均更改时,CubeMX 生成的 USB HID 设备发送错误数据

    我正在调试我正在创建的复合设备的问题 并在新生成的仅 CubeMX 代码中重新创建了该问题 以使其更容易解决 我添加了少量代码main 让我发送 USB HID 鼠标点击 并在按下蓝色按钮时使 LED 闪烁 uint8 t click re
  • USB 端口速度 Linux [关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 如何以编程方式确定运行 Linux 内核的嵌入式设备中的 USB 端口速度 你可以阅读 sys bus usb devices usb s
  • LibUsb 声明接口访问被拒绝 Java

    我希望能够从 USB 计步器读取数据 我正在 Java 中尝试此操作 并且使用 LibUsb 和 Usb4Java 库 我似乎无法认领 USB 管道或类似的东西 我正在使用的代码 final Context context new Cont
  • STM32 传输结束时,循环 DMA 外设到存储器的行为如何?

    我想问一下 在以下情况下 STM32 中的 DMA SPI rx 会如何表现 我有一个指定的 例如 96 字节数组 名为 A 用于存储从 SPI 接收到的数据 我打开循环 SPI DMA 它对每个字节进行操作 配置为 96 字节 是否有可能
  • Delphi - 如何获取 USB 可移动硬盘和记忆棒的列表?

    在我的应用程序 Delphi 中 我需要列出所有 USB 存储设备 这些可以是闪存棒or外部存储驱动器 有一个Jvcl成分JvDriveCombo 并且它有DriveType属性 问题是我是否选择DriveType Fixed那么除了外部驱

随机推荐

  • linux部署vue项目

    命令行进入配置文件 vi usr local nginx conf nginx conf 输入i进行修改端口号和文件路径 按ESC保存后输入 wq退出 进入sbin启动nginx cd usr local nginx sbin nginx
  • FMC164-基于JESD204B的4路1Gsps AD 4路1.25Gsps DA FMC子卡

    板卡介绍 FMC164子卡集成4通道1Gsps采样率 16位AD 4通道1 25Gsps 16位DA 板载时钟芯片HMC7044 可以提供JESD204B所需要的各种时钟 具有同步 触发功能 模拟信号采用SSMC射频连接器输入和输出 板卡可
  • es 修改mappings字段结构

    es不支持直接修改mappings字段结构 可以通过 reindex 重建索引 方式修改 POST reindex source index old index dest index new index op type create Ela
  • 记录:Qt Creator 10配置安卓开发环境

    Qt Creator 现在的安卓开发环境配置相比老版本方便了不少 本文以目前在线安装版的 Qt Creator 10 0 2 Qt 5 15 Qt 6 5 为例做演示 有些文件可能会因为网络问题需要科学上网才能下载 1 下载 JDK htt
  • 【css】css动画实现的3种方式

    css实现动画主要有3种方式 transition实现过渡动画 transform转变动画 animation实现自定义动画 一 transition过渡动画 1 语法 transition property duration timing
  • UnityAPI.Transform变换(Yanlz+Unity+API+Transform+)

    UnityAPI Transform变换 版本 作者 参与者 完成日期 备注 UnityAPI Transform V01 1 0 严立钻 2018 08 21 UnityAPI Transform变换 发布说明 UnityAPI Tran
  • Linux·C++多线程基础知识

    目录 1 多线程 1 1 多进程与多线程 1 2 多线程理解 1 3 创建线程 1 4 join与detach方式 1 join举例 2 detach举例 1 5 this thread 2 mutex 2 1 lock与unlock 2
  • 【Tensorflow】tf.nn.depthwise_conv2d如何实现深度卷积?

    实验环境 tensorflow版本1 2 0 python2 7 介绍 depthwise conv2d来源于深度可分离卷积 Xception Deep Learning with Depthwise Separable Convoluti
  • C#时间字符串转换

    class Program static void Main string args DateTime datetime DateTime Now 打印当前时间 Console WriteLine 时间为 n datetime n 方法1
  • Generator 函数

    Generator 函数 基本概念 与 Iterator 接口的关系 next 方法的参数 for of 循环 Generator prototype throw Generator prototype return next throw
  • docker quick start

    文章目录 安装 验证 设置阿里云加速代理 测试 常用命令 镜像和容器存储位置 容器生命周期 Dockerfile 构建镜像 部署tomcat 基础指令 构建redis镜像 容器间Link单向通信 容器间Bridge网桥双向通信 volume
  • 【Node.js实战】一文带你开发博客项目之日志(文件读写、stream流、写日志)

    个人简介 个人主页 前端杂货铺 学习方向 主攻前端方向 也会涉及到服务端 个人状态 在校大学生一枚 已拿多个前端 offer 秋招 未来打算 为中国的工业软件事业效力n年 推荐学习 前端面试宝典 Vue2 Vue3 Vue2 Vue3项目实
  • memcache清除缓存

    打开cmd 输入 telnet localhost 11211 flush all
  • 为博客添加樱花飘落的效果

    偶然看见别人博客中樱花飘落的效果 感觉很棒 于是我也给博客加上了樱花 外链图片转存失败 源站可能有防盗链机制 建议将图片保存下来直接上传 新建 sakura js 新建一个 sakura js 文件 在里面填入下面的代码 代码来自于 折月
  • 快速排序的优化

    1 快速排序的基本思想 快速排序使用分治的思想 通过一趟排序将待排序列分割成两部分 其中一部分记录的关键字均比另一部分记录的关键字小 之后分别对这两部分记录继续进行排序 以达到整个序列有序的目的 2 快速排序的三个步骤 1 选择基准 在待排
  • 24. 在QML界面中嵌入QWidget窗口

    1 说明 目前QT官方主推的界面开发方式是采用QML进行界面设计 但在QML未流行之前 很多的项目都是采用QWidget开发的 把之前的代码全部转换为QML代码显然工作量非常大 如果能将QWidget窗口嵌入到QML界面中 那么开发效率将提
  • WTL 界面设计篇(CImageEx)

    头文件声明 CImageEx h pragma once include
  • chrony实现同步阿里云时间

    1 安装chrony yum install chrony y vim etc chrony conf 修改chrony配置文件 替换默认的centos server ntp1 aliyun com iburst server ntp2 a
  • 【Winform Vlc.DotNet Fill拉伸控件实现全屏】

    其实也有WPF得实现 但是这个效率更高 cpu占用在2 以内 基本都素在1 上下浮动 但是WPF基本就是3 起步了 想看WPF实现得右转 https blog csdn net weixin 38138153 article details
  • Linux usb子系统(二) _usb-skeleton.c精析

    drivers usb usb skeleton c 是内核提供给usb设备驱动开发者的海量存储usb设备的模板程序 程序不长 通用性却很强 十分经典 深入理解这个文件可以帮助我们更好的理解usb子系统以及usb设备驱动框架 写出更好的us