精华内容
下载资源
问答
  • LinuxUSB转串口代码

    2012-09-21 15:59:19
    LinuxUSB转串口代码
  • USB转串口驱动代码

    2009-05-10 12:30:52
    一个简单和USB转串口驱动源代码。。。。。。。。很好用的。。。。。。。。
  • Linux 串口和USB转串口操作代码,能用于at指令测试, 可以测试ESP32和ESP8266
  • 1、对应的博客地址:https://blog.csdn.net/mm13420109325/article/details/103379077 2、该程序通过安卓手机的【USB口实现串口通信】,搭配CH340usb转串口模块使用。
  • win10 usb转串口驱动是一个电脑端的驱动程序,由于近来...其实这一切都是因为系统升级之后,PL2303USB转串口线程序出错无法工作导致的,本次带来的win10 usb转串口驱动主要解决这个问题,只要是错误代码为10的情况,...

    win10 usb转串口驱动是一个电脑端的驱动程序,由于近来WIN10系统市场占有率越来越高,大部分网友反映在升级系统以后USB接口无法使用了,经常遇到的情况是无法连接U盘、手机、移动盘,甚至更严重的还有出现无法识别免驱设备比如鼠标,其实这一切都是因为系统升级之后,PL2303USB转串口线程序出错无法工作导致的,本次带来的win10 usb转串口驱动主要解决这个问题,只要是错误代码为10的情况,通通手到病除,目前经过测试,除了安装这一个驱动之外,没有别的任何办法讷讷个解决,当您的电脑出现以上情况时,请来本站下载解决吧!

    4902caa122bd8868ad0ccb4ea6563353.png

    功能特色

    1、本次以供提供三个驱动,分别为windows7-10 32-64位驱动.rar、win-me-2000-XP驱动.rar、YH-340 USB转串口驱动.rar,前两个世界解决各自系统问题的,第三个YH-340就是前两个的辅助,

    2、解决错误代码为10的所有USB驱动问题,

    3、速度极快,一次处理永不再犯。

    安装方法

    下载并解压,由于此次一共3个驱动程序,不便一一讲解,以下直接进入使用方法,针对具体问题具体解决。

    f80a4478e1545104616e22434c617334.png

    使用方法

    一、升级 Win10 后,PL2303 USB 转串口线不能正常工作,查看设备管理器,显示如下:

    db1b95976bb118e8a974160808147fdf.png

    升级后串口不能正常工作,单击右键显示如下,代码 10.

    4902caa122bd8868ad0ccb4ea6563353.png

    设备无法启动代码

    二、正确的驱动程序安装步骤

    选择驱动程序。经过测试选择 2009.11.19发行,版本 3.3.10.140的驱动程序能够在 Win10

    下正常工作。

    步骤 1 安装驱动程序

    步骤 2 在计算机管理页面,单击右键选择“更新驱动程序软件”

    步骤 3 选择“浏览计算机以查找驱动程序软件”

    步骤 4 选择“从计算机的驱动程序列表中选取”

    步骤 5 选择“ Prolific ... 版本 3.3.10.140【2009/11/19】”,点击“下一步”

    提示重新启动,测试工作正常。

    展开全文
  • USB转串口驱动源代码及单片机程序

    热门讨论 2009-09-07 11:16:21
    USB转串口驱动源代码,在WDK中编译通过,能够安装,单片机程序是实现USB转串口芯片必不可少的,二者结合才能实现USB转串口功能,都是代码,而且有编译环境,很有参考价值。
  • USB转串口驱动代码分析

    千次阅读 2015-06-10 18:16:49
    1、USB插入时,创建设备[plain] view plaincopyDriverObject->DriverExtension->AddDevice = USB2COM_PnPAddDevice; 步一、调用USB2COM_CreateDeviceObject创建功能设备对象(FDO)(1) IoCreateDevice系统API...


    1、USB插入时,创建设备

    1. DriverObject->DriverExtension->AddDevice = USB2COM_PnPAddDevice;  

    步一、调用USB2COM_CreateDeviceObject创建功能设备对象(FDO)

    (1) IoCreateDevice系统API的原理为:

    1. NTKERNELAPI  
    2. NTSTATUS  
    3. IoCreateDevice(  
    4.     IN PDRIVER_OBJECT DriverObject,  
    5.     IN ULONG DeviceExtensionSize,  
    6.     IN PUNICODE_STRING DeviceName OPTIONAL,  
    7.     IN DEVICE_TYPE DeviceType,  
    8.     IN ULONG DeviceCharacteristics,  
    9.     IN BOOLEAN Reserved,  
    10.     OUT PDEVICE_OBJECT *DeviceObject  
    11.     );  


    在之前真实的USB驱动中我们是这样创建的:

    1. ntStatus = IoCreateDevice(  
    2.                     DriverObject,                   // our driver object  
    3.                     sizeof(DEVICE_EXTENSION),       // extension size for us  
    4.                     NULL,                           // name for this device  
    5.                     FILE_DEVICE_UNKNOWN,  
    6.                     FILE_AUTOGENERATED_DEVICE_NAME, // device characteristics  
    7.                     FALSE,                          // Not exclusive  
    8.                     &deviceObject);                 // Our device object  


    就是第三个参数为NULL, 第四个参数为FILE_DEVICE_UNKNOWN,意味着我们驱动想附加的设备是空的,且未知。

    由于我们是创建虚拟串口驱动,因此调用IoCreateDevice创建时在指定串口设备的名字,且指定设备类型

    1. ntStatus = IoCreateDevice(DriverObject, sizeof(DEVICE_EXTENSION),  
    2.                            &deviceObjName, FILE_DEVICE_SERIAL_PORT,  
    3.                            FILE_DEVICE_SECURE_OPEN, TRUE, DeviceObject);  

    (2)为自定义的扩展设备中的设备名字段指定设备名

    1. // deviceExtension->DeviceName为UNICODE_STRING类型  
    2. RtlZeroMemory(&deviceExtension->DeviceName, sizeof(UNICODE_STRING));  
    3. deviceExtension->DeviceName.MaximumLength = deviceObjName.Length + sizeof(WCHAR);  
    4. // Buffer重新分配  
    5. deviceExtension->DeviceName.Buffer = USB2COM_ExAllocatePool(NonPagedPool, deviceObjName.Length + sizeof(WCHAR));  
    6. RtlZeroMemory(deviceExtension->DeviceName.Buffer,  
    7.         deviceObjName.Length+sizeof(WCHAR));  
    8. RtlAppendUnicodeStringToString(&deviceExtension->DeviceName, &deviceObjName);  


    (3)初始化事件、串口控件对象、关键代码段、自旋锁、读写队列链表。

    1.  // this event is triggered when there is no pending io of any kind and device is removed  
    2. KeInitializeEvent(&deviceExtension->RemoveEvent, NotificationEvent, FALSE);  
    3.   
    4. // this event is triggered when self-requested power irps complete  
    5. KeInitializeEvent(&deviceExtension->SelfRequestedPowerIrpEvent, NotificationEvent, FALSE);  
    6.   
    7. // this event is triggered when there is no pending io  (pending io count == 1 )  
    8. KeInitializeEvent(&deviceExtension->NoPendingIoEvent, NotificationEvent, FALSE);  
    9.   
    10. // spinlock used to protect inc/dec iocount logic  
    11. KeInitializeSpinLock (&deviceExtension->IoCountSpinLock);  
    12.       
    13.     deviceExtension->BaudRate = 19200;  
    14. /* Set line control */  
    15. deviceExtension->SerialLineControl.StopBits = STOP_BIT_1;  
    16. deviceExtension->SerialLineControl.Parity = NO_PARITY;  
    17. deviceExtension->SerialLineControl.WordLength = 8;  
    18.   
    19. deviceExtension->SpecialChars.XonChar = SERIAL_DEF_XON;  
    20. deviceExtension->SpecialChars.XoffChar = SERIAL_DEF_XOFF;  
    21.   
    22. deviceExtension->HandFlow.ControlHandShake = SERIAL_DTR_CONTROL;  
    23. deviceExtension->HandFlow.FlowReplace      = SERIAL_RTS_CONTROL;  
    24. deviceExtension->HandFlow.XoffLimit    = 300;  
    25. deviceExtension->HandFlow.XonLimit     = 100;  
    26. InitializeCircularBuffer(&deviceExtension->InputBuffer, 512);  
    27. InitializeCircularBuffer(&deviceExtension->OutputBuffer, 512);  
    28. KeInitializeSpinLock(&deviceExtension->InputBufferLock);  
    29. KeInitializeSpinLock(&deviceExtension->OutputBufferLock);  
    30. InitializeListHead(&deviceExtension->ReadQueue);  
    31. KeInitializeSpinLock(&deviceExtension->ReadQueueSpinLock);  
    32. InitializeListHead(&deviceExtension->WriteQueue);  
    33. KeInitializeSpinLock(&deviceExtension->WriteQueueSpinLock);  
    34. InitializeListHead(&deviceExtension->PurgeQueue);  
    35. KeInitializeSpinLock(&deviceExtension->PurgeQueueSpinLock);  


     

    步二、让设备对象支持直接读写IO和并设置DO_POWER_PAGABLE

     设置DO_POWER_PAGABLE的目的是在suspend期间不接收一个IRP_MN_STOP_DEVICE,

    在resume时不接收一个IRP_MN_START_DEVICE消息。

    1. // we support direct io for read/write  
    2.        //  
    3.        deviceObject->Flags |= DO_DIRECT_IO;  
    4.   
    5.   
    6.        //Set this flag causes the driver to not receive a IRP_MN_STOP_DEVICE  
    7.        //during suspend and also not get an IRP_MN_START_DEVICE during resume.  
    8.        //This is neccesary because during the start device call,  
    9.        // the GetDescriptors() call  will be failed by the USB stack.  
    10.        deviceObject->Flags |= DO_POWER_PAGABLE;  

    步三、附加FDO到物理设备对象上,并创建SymbolLink,并通过IoRegisterDeviceInterface来设备绑定让设备可见。

    1. deviceExtension->TopOfStackDeviceObject =  
    2.             IoAttachDeviceToDeviceStack(deviceObject, PhysicalDeviceObject);  


     

    1. status = IoRegisterDeviceInterface(PDevExt->PhysicalDeviceObject, (LPGUID)&GUID_CLASS_COMPORT,  
    2.                                       NULL, &PDevExt->DeviceClassSymbolicName);  

    步四、获得物理设备的性能的一份复制,保存在extension中,以此来获得电源级别

     

     (1)建立IRP来产生一个发往FDO的内部查询请求;

    1. irp = IoAllocateIrp(LowerDeviceObject->StackSize, FALSE);  


    (2)设置IRP要发往的设备栈Location(是更低层的设备,在这里就是它附加下的USB)的信息,

    eg: MajorFunction、MinorFunction、Parameters.DeviceCapabilities.Capabilities

    1. nextStack = IoGetNextIrpStackLocation(irp);  
    2. nextStack->MajorFunction= IRP_MJ_PNP;  
    3. nextStack->MinorFunction= IRP_MN_QUERY_CAPABILITIES;  


    在以上代码中的IoGetNextIrpStackLocation是一个宏,它的定义如下:

    1. #define IoGetNextIrpStackLocation( Irp ) (\  
    2.     (Irp)->Tail.Overlay.CurrentStackLocation - 1 )  

    从以上宏可以看出:IRP结构体中存有当前的栈Location CurrentStackLoctation,而我们的IRP要发往的栈Location的获得方法就是原有栈的地址 - 1


     

     (3)设置IRP的完成例程;(在完成全程中就是把事件激活,这样KeWaitForSingleObject就能走下来)

    (4)把IRP发送下去,并等待完成;

     

    1. ntStatus = IoCallDriver(LowerDeviceObject,  
    2.                            irp);  
    3.   
    4.    USB2COM_KdPrint( DBGLVL_MEDIUM,(" USB2COM_QueryCapabilities() ntStatus from IoCallDriver to PCI = 0x%x\n", ntStatus));  
    5.   
    6.    if (ntStatus == STATUS_PENDING) {  
    7.       // wait for irp to complete  
    8.   
    9.       KeWaitForSingleObject(  
    10.            &event,  
    11.            Suspended,  
    12.            KernelMode,  
    13.            FALSE,  
    14.            NULL);  

    当完成全程返回时, nextStack->Parameters.DeviceCapabilities.Capabilities = DeviceCapabilities;指针就存着我们的性能信息。


    步五、获得USB的版本信息;

    直接调用系统API:

    1. USBD_GetUSBDIVersion(&versionInformation);  

     

    2、处理系统PNP和电源管理请求的派遣函数

    1. DriverObject->MajorFunction[IRP_MJ_PNP] = USB2COM_ProcessPnPIrp;  

    在USB2COM_ProcessPnPIrp里case了以下几个消息:

    IRP_MN_START_DEVICE 、IRP_MN_QUERY_STOP_DEVICE、IRP_MN_CANCEL_STOP_DEVICE、IRP_MN_STOP_DEVICE

    IRP_MN_QUERY_REMOVE_DEVICE、IRP_MN_CANCEL_REMOVE_DEVICE、IRP_MN_SURPRISE_REMOVAL、IRP_MN_REMOVE_DEVICE
    文章<<Window XP驱动开发(九) USB WDM驱动开发实例 bulkusb >>中讲到的同类派遣函数,它只case 了以下:

    IRP_MN_START_DEVICE、

    IRP_MN_STOP_DEVICE 、IRP_MN_EJECT和IRP_MN_SURPRISE_REMOVAL

    所以一比较,觉得USB2COM考虑得更全面。

    (1)IRP_MN_START_DEVICE

    与文章<<Window XP驱动开发(九) USB WDM驱动开发实例 bulkusb >>中的处理基本类似;

    (2)IRP_MN_QUERY_STOP_DEVICE

    与文章<<Window XP驱动开发(九) USB WDM驱动开发实例 bulkusb >>中的处理基本类似;

    (3)IRP_MN_CANCEL_STOP_DEVICE

     

    (4)IRP_MN_STOP_DEVICE、

    (5)IRP_MN_QUERY_REMOVE_DEVICE、

    (6)IRP_MN_CANCEL_REMOVE_DEVICE、

    (7)IRP_MN_SURPRISE_REMOVAL、

    (8)IRP_MN_REMOVE_DEVICE

     

    3、当应用程序CreateFile时,调用USB2COM_Create

     DriverObject->MajorFunction[IRP_MJ_CREATE] = USB2COM_Create;

    步一、判断是否能接收一个新的IO请求

    如果不能接收一个新的IO请求,那么直接返回。

    1. if ( !USB2COM_CanAcceptIoRequests( DeviceObject ) ) {  
    2.       ntStatus = STATUS_DELETE_PENDING;  
    3.   
    4. USB2COM_KdPrint( DBGLVL_DEFAULT,("ABORTING USB2COM_Create\n"));  
    5.       goto done;  
    6.   }  

     

    在以下的条件不能接收一个新的IO(判断的标志是我们自己标记的):

    1) 设备已经被移除了,
    2) 从来没有被启动过,,
    3) 已经停止了,
    4) 有一个移除的请求还没处理,
    5) 有一个停止的请求还没处理。

    1. //flag set when processing IRP_MN_REMOVE_DEVICE  
    2.     if ( !deviceExtension->DeviceRemoved &&  
    3.          // device must be started( enabled )  
    4.          deviceExtension->DeviceStarted &&  
    5.          // flag set when driver has answered success to IRP_MN_QUERY_REMOVE_DEVICE  
    6.          !deviceExtension->RemoveDeviceRequested &&  
    7.          // flag set when driver has answered success to IRP_MN_QUERY_STOP_DEVICE  
    8.          !deviceExtension->StopDeviceRequested ){  
    9.             fCan = TRUE;  
    10.     }  

    步二:开始从interface中的Pipe[0]中读(说明Interface的获取是在之前就完成的,是在USB2COM_ProcessPnPIrp
                                                                   里case IRP_MN_START_DEVICE:完成的)
    1. StartReadIntUrb(  
    2.         DeviceObject,  
    3.         &interface->Pipes[0]  
    4.         );  


    (1)如果判断现在不能接收IO请求,那么就记录在extension中的IRP置为完成状态,然后返回;

    (2)分配IRP、URB空间,并存到Extension->ReadIntUrbs数组中,再把Pipe句柄、Buffer、传送标志填入到URB结构体成员;

    1. irp = IoAllocateIrp(stackSize, FALSE);  
    2.         if(irp == NULL)   
    3.         {  
    4.                 return STATUS_INSUFFICIENT_RESOURCES;  
    5.         }  
    6.         urb = USB2COM_ExAllocatePool(NonPagedPool,   
    7.                         sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER));  
    8.         if(urb == NULL)  
    9.         {  
    10.             IoFreeIrp(irp);  
    11.         return STATUS_INSUFFICIENT_RESOURCES;  
    12.         }  
    13.         deviceExtension->ReadIntUrbs[i].Irp = irp;  
    14.         deviceExtension->ReadIntUrbs[i].Urb = urb;  
    15.         deviceExtension->ReadIntUrbs[i].deviceObject = DeviceObject;  
    16.         deviceExtension->ReadIntUrbs[i].PipeInfo = PipeInfo;  
    17.         InitIntUrb(urb,  
    18.                 PipeInfo->PipeHandle,  
    19.                 deviceExtension->ReadIntUrbs[i].TransferBuffer,  
    20.                 sizeof(deviceExtension->ReadIntUrbs[i].TransferBuffer),  
    21.                 TRUE);  

     

    InitIntUrb为自己封装的函数,很简单,就是把数据填入到urb结构体中:

    1. VOID  
    2. InitIntUrb(  
    3.     IN PURB urb,  
    4.     IN USBD_PIPE_HANDLE  PipeHandle,  
    5.     IN PUCHAR TransferBuffer,  
    6.     IN ULONG length,  
    7.     IN BOOLEAN Read  
    8.     )  
    9. {  
    10.     USHORT siz = sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER);  
    11.     if (urb) {  
    12.         RtlZeroMemory(urb, siz);  
    13.   
    14.         urb->UrbBulkOrInterruptTransfer.Hdr.Length = (USHORT) siz;  
    15.         urb->UrbBulkOrInterruptTransfer.Hdr.Function =  
    16.                     URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER;  
    17.         urb->UrbBulkOrInterruptTransfer.PipeHandle = PipeHandle;  
    18.         urb->UrbBulkOrInterruptTransfer.TransferFlags =  
    19.             Read ? USBD_TRANSFER_DIRECTION_IN : 0;  
    20.         // short packet is not treated as an error.  
    21.         urb->UrbBulkOrInterruptTransfer.TransferFlags |=   
    22.             USBD_SHORT_TRANSFER_OK;              
    23.                   
    24.         //  
    25.         // not using linked urb's  
    26.         //  
    27.         urb->UrbBulkOrInterruptTransfer.UrbLink = NULL;  
    28.         urb->UrbBulkOrInterruptTransfer.TransferBufferMDL = NULL;  
    29.         urb->UrbBulkOrInterruptTransfer.TransferBuffer = TransferBuffer;  
    30.         urb->UrbBulkOrInterruptTransfer.TransferBufferLength = length;  
    31.     }  
    32. }  


    (3)初始化IRP的栈Location及它的完成例程ReadIntUrbComplete,并把当前ReadIntUrbs的内存作为完成例程的Context;

    (4)完成例程ReadIntUrbComplete的处理;

    A、判断返回执行后返回的IRP、URB的状态,如果为未连接或取消那么就释放IRP及URB内存,并完成把extension中的IRP置为完成状态,然后直接返回;否则往下执行;

    B、把IRP之前绑定的Buffer数据拷到InputBuffer中(现在的Buffer就是我们的结果数据);

    1. KeAcquireSpinLock(&deviceExtension->InputBufferLock, &oldIrql);  
    2.             PushCircularBufferEntry(  
    3.                 &deviceExtension->InputBuffer,  
    4.                 &pIntUrbs->TransferBuffer[1],  
    5.                 pIntUrbs->TransferBuffer[0]);  
    6.             KeReleaseSpinLock(&deviceExtension->InputBufferLock, oldIrql);  

    PushCircularBufferEntry为自己封装的函数:

    把data内存中的len个数据拷到pBuffer中

    1. NTSTATUS  
    2. PushCircularBufferEntry(  
    3.     IN PCIRCULAR_BUFFER pBuffer,  
    4.     IN PUCHAR data,  
    5.     IN ULONG len)  
    6. {  
    7.     ULONG NextPosition;  
    8.     DbgPrint("Serial: PushCircularBufferEntry(data %p, len %d)\n", data, len);  
    9.     ASSERT(pBuffer);  
    10.     ASSERT(pBuffer->Length);  
    11.   
    12.     if ((data == NULL) || (len == 0))  
    13.         return STATUS_INVALID_PARAMETER;  
    14.     do{  
    15.         NextPosition = (pBuffer->WritePosition + 1) % pBuffer->Length;  
    16.         if (NextPosition == pBuffer->ReadPosition)  
    17.             return STATUS_BUFFER_TOO_SMALL;  
    18.         pBuffer->Buffer[pBuffer->WritePosition] = *data++;  
    19.         pBuffer->DataLen++;  
    20.         pBuffer->WritePosition = NextPosition;  
    21.     }while(--len);  
    22.       
    23.     return STATUS_SUCCESS;  
    24. }  

    C、如果extension中有等待的IRP,且等待的事件中有SERIAL_EV_RXCHAR,那么通过SerialCompleteCurrentWait完成等待串口的等待IRP。

    C、1   把当前IRP的取消完成例程置为NULL,根据包是否已经被取消标志pIrp->Cancel 及之前的IRP取消例程是否被执行过了,那么调用SerialCancelCurrentWait来取消。

    注意SerialCancelCurrentWait中很重要的是要通过调用IoReleaseCancelSpinLock来释放系统的删除自旋锁

    1. void  
    2. SerialCancelCurrentWait( PDEVICE_OBJECT DeviceObject, PIRP pIrp )  
    3. {  
    4.     PIO_STACK_LOCATION irpStack;  
    5.     PDEVICE_EXTENSION deviceExtension;  
    6.       
    7.     DbgPrint("SerialCancelCurrentWait Enter Irp = %p\n",pIrp);  
    8.     ASSERT(pIrp);  
    9.     irpStack = IoGetCurrentIrpStackLocation(pIrp);  
    10.     deviceExtension = irpStack->DeviceObject->DeviceExtension;  
    11.     deviceExtension->CurrentWaitIrp = NULL;  
    12.     /* 
    13.     *All Cancel routines must follow these guidelines: 
    14.     * 1. Call IoReleaseCancelSpinLock to release the system's cancel spin lock 
    15.     * 2. ... 
    16.     */  
    17.     IoReleaseCancelSpinLock(pIrp->CancelIrql);  
    18.     pIrp->IoStatus.Status = STATUS_CANCELLED;  
    19.     pIrp->IoStatus.Information = 0;  
    20.     IoCompleteRequest(pIrp, IO_NO_INCREMENT);  
    21.     DbgPrint("SerialCancelCurrentWait Exit\n");  
    22. }  

    C、2    如果没有被取消,那么把当前我们的事件返回给应用层,并完成IRP。

    1. deviceExtension->CurrentWaitIrp = NULL;  
    2. deviceExtension->HistoryMask &= ~events;  
    3. IoReleaseCancelSpinLock(OldIrql);  
    4. pIrp->IoStatus.Information = sizeof(ULONG);  
    5. pIrp->IoStatus.Status = ntStatus;  
    6. *((ULONG *)pIrp->AssociatedIrp.SystemBuffer) = events;  
    7. IoCompleteRequest (pIrp,IO_NO_INCREMENT);  

    D、完成当前的读IRP;

    D、1   如果有当前读的Irp,那么把第(4)B、里得到的结果数据弹出到当前的读IRP中(通过当前读的IRP中的MdlAddress地址访问到内存)。

    1. if(deviceExtension->CurrentReadIrp)  
    2. {  
    3.     ULONG       haveLen;  
    4.     BOOLEAN     returnWhatsPresent = FALSE;  
    5.       
    6.     if(deviceExtension->SerialTimeOuts.ReadIntervalTimeout &&  
    7.         ( deviceExtension->SerialTimeOuts.ReadTotalTimeoutMultiplier == 0) &&  
    8.         ( deviceExtension->SerialTimeOuts.ReadTotalTimeoutConstant == 0)  
    9.     )  
    10.     {  
    11.         returnWhatsPresent = TRUE;  
    12.     }  
    13.     ioBuffer = MmGetSystemAddressForMdlSafe(deviceExtension->CurrentReadIrp->MdlAddress,NormalPagePriority );  
    14.     ioLength = MmGetMdlByteCount(deviceExtension->CurrentReadIrp->MdlAddress);  
    15.     KeAcquireSpinLock(&deviceExtension->InputBufferLock, &oldIrql);  
    16.     haveLen = CircularBufferDataLen(&deviceExtension->InputBuffer);  
    17.     if( (ioLength <= haveLen) || (returnWhatsPresent && (haveLen > 0)))  
    18.     {  
    19.         ioLength = (ioLength < haveLen) ? ioLength : haveLen;  
    20.         DbgPrint("Complete CurrentReadIrp ioLength = %d\n",ioLength);  
    21.         ntStatus = PopCircularBufferEntry(&deviceExtension->InputBuffer,ioBuffer,ioLength);  
    22.         KeReleaseSpinLock(&deviceExtension->InputBufferLock, oldIrql);  
    23.         deviceExtension->CurrentReadIrp->IoStatus.Information = ioLength;  
    24.         deviceExtension->CurrentReadIrp->IoStatus.Status = ntStatus;   
    25.         IoCompleteRequest(deviceExtension->CurrentReadIrp,IO_NO_INCREMENT);  
    26.         deviceExtension->CurrentReadIrp = DequeueReadIrp(deviceExtension);  
    27.     }  
    28.     else  
    29.         KeReleaseSpinLock(&deviceExtension->InputBufferLock, oldIrql);  
    30. }  

    以上代码中MmGetSystemAddressForMdlSafe

    1. // 函数说明:  
    2. //     此函数返回MDL映射的地址,如果此MDL还没有被映射,那么它将会被映射  
    3. // 参数:  
    4. //     MemoryDescriptorList - 指向MDL的指针  
    5. //     Priority - 指向一个标志,该标记表明它是如何的重要,以至于这个请求在低的可用PTE条件下也成功了  
    6. // 返回值:  
    7. //     返回映射页的基地址,这个基地址和MDL的虚拟地址有同样的偏移地址.  
    8. //     与MmGetSystemAddressForMdl不同,在失败时它会返回NULL,而不是bugchecking the system.  
    9. // 版本说明:  
    10. //     此宏在WDM 1.0中不同用,在WDM1.0的驱动中实现此功能是通过提供同步并set/reset MDL_MAPPING_CAN_FAIL bit.  
    11. #define MmGetSystemAddressForMdlSafe(MDL, PRIORITY)                    \  
    12.      (((MDL)->MdlFlags & (MDL_MAPPED_TO_SYSTEM_VA |                    \  
    13.                         MDL_SOURCE_IS_NONPAGED_POOL)) ?                \  
    14.                              ((MDL)->MappedSystemVa) :                 \  
    15.                              (MmMapLockedPagesSpecifyCache((MDL),      \  
    16.                                                            KernelMode, \  
    17.                                                            MmCached,   \  
    18.                                                            NULL,       \  
    19.                                                            FALSE,      \  
    20.                                                            (PRIORITY))))  


     

    第一个参数是deviceExtension->CurrentReadIrp->MdlAddress,我们从wdm.h可以看到它的定义:

    1. // 定义一个指向这个I/O请求的内存描述符(MDL)的指针,  
    2. // 此域仅在I/O是“direct I/O"时被用  
    3. PMDL MdlAddress;  


    D、2   判断是否能接收一个新的IO请求

    如果不能接收一个新的IO请求或者Pipe[0]关闭了,那么直接返回。


    D、3   循环发送IRP,并置完成例程为ReadIntUrbComplete。(这样,就又回到了(4)),何时结束呢?

    结束条件:直至设备不能接收一个新的IO请求或未连接、取消。

    所以处理当前读IRP是一直进行的。

    步三:准备写interface中的Pipe[1] (说明Interface的获取是在之前就完成的)
    1. PrepareWriteIntUrb(  
    2.     IN PDEVICE_OBJECT DeviceObject,  
    3.     IN PUSBD_PIPE_INFORMATION PipeInfo  
    4.     )  

    A、分配写的Irb 与 Urb空间。

    B、保存Pipe[1]地址为deviceExtension->WriteIntUrb.PipeInfo存在extension中。

    步四、用应用程序传进来的Pipe名字来查找Pipe;(从extension中保存的PipeInfo数组中获得,说明PipeInfo在之前就获得了)

    应用程序的名字从当前IRP中的fileObject得到。

    1. ourPipeInfo = USB2COM_PipeWithName( DeviceObject, &fileObject->FileName );  

    步五、给应用程序返回步四中查找到的Pipe对应的Pipe信息,且如果设备不是D0状态那么就给设备上电;
    1. for (i=0; i<interface->NumberOfPipes; i++) {  
    2.   
    3.         PipeInfo =  &interface->Pipes[i]; // PUSBD_PIPE_INFORMATION  PipeInfo;  
    4.   
    5.         if ( ourPipeInfo == &deviceExtension->PipeInfo[i] ) {  
    6.   
    7.             //  
    8.             // found a match  
    9.             //  
    10.             USB2COM_KdPrint( DBGLVL_DEFAULT,("open pipe %d\n", i));  
    11.             fileObject->FsContext = PipeInfo;  
    12.             ourPipeInfo->fPipeOpened = TRUE; // set flag for opened  
    13.             ntStatus = STATUS_SUCCESS;  
    14.   
    15.             deviceExtension->OpenPipeCount++;  
    16.   
    17.             // try to power up device if its not in D0  
    18.             actStat = USB2COM_SelfSuspendOrActivate( DeviceObject, FALSE );  
    19.             break;  
    20.         }  
    21.     }  


     

    1、USB插入时,创建设备

    1. DriverObject->DriverExtension->AddDevice = USB2COM_PnPAddDevice;  

    步一、调用USB2COM_CreateDeviceObject创建功能设备对象(FDO)

    (1) IoCreateDevice系统API的原理为:

    1. NTKERNELAPI  
    2. NTSTATUS  
    3. IoCreateDevice(  
    4.     IN PDRIVER_OBJECT DriverObject,  
    5.     IN ULONG DeviceExtensionSize,  
    6.     IN PUNICODE_STRING DeviceName OPTIONAL,  
    7.     IN DEVICE_TYPE DeviceType,  
    8.     IN ULONG DeviceCharacteristics,  
    9.     IN BOOLEAN Reserved,  
    10.     OUT PDEVICE_OBJECT *DeviceObject  
    11.     );  


    在之前真实的USB驱动中我们是这样创建的:

    1. ntStatus = IoCreateDevice(  
    2.                     DriverObject,                   // our driver object  
    3.                     sizeof(DEVICE_EXTENSION),       // extension size for us  
    4.                     NULL,                           // name for this device  
    5.                     FILE_DEVICE_UNKNOWN,  
    6.                     FILE_AUTOGENERATED_DEVICE_NAME, // device characteristics  
    7.                     FALSE,                          // Not exclusive  
    8.                     &deviceObject);                 // Our device object  


    就是第三个参数为NULL, 第四个参数为FILE_DEVICE_UNKNOWN,意味着我们驱动想附加的设备是空的,且未知。

    由于我们是创建虚拟串口驱动,因此调用IoCreateDevice创建时在指定串口设备的名字,且指定设备类型

    1. ntStatus = IoCreateDevice(DriverObject, sizeof(DEVICE_EXTENSION),  
    2.                            &deviceObjName, FILE_DEVICE_SERIAL_PORT,  
    3.                            FILE_DEVICE_SECURE_OPEN, TRUE, DeviceObject);  

    (2)为自定义的扩展设备中的设备名字段指定设备名

    1. // deviceExtension->DeviceName为UNICODE_STRING类型  
    2. RtlZeroMemory(&deviceExtension->DeviceName, sizeof(UNICODE_STRING));  
    3. deviceExtension->DeviceName.MaximumLength = deviceObjName.Length + sizeof(WCHAR);  
    4. // Buffer重新分配  
    5. deviceExtension->DeviceName.Buffer = USB2COM_ExAllocatePool(NonPagedPool, deviceObjName.Length + sizeof(WCHAR));  
    6. RtlZeroMemory(deviceExtension->DeviceName.Buffer,  
    7.         deviceObjName.Length+sizeof(WCHAR));  
    8. RtlAppendUnicodeStringToString(&deviceExtension->DeviceName, &deviceObjName);  


    (3)初始化事件、串口控件对象、关键代码段、自旋锁、读写队列链表。

    1.  // this event is triggered when there is no pending io of any kind and device is removed  
    2. KeInitializeEvent(&deviceExtension->RemoveEvent, NotificationEvent, FALSE);  
    3.   
    4. // this event is triggered when self-requested power irps complete  
    5. KeInitializeEvent(&deviceExtension->SelfRequestedPowerIrpEvent, NotificationEvent, FALSE);  
    6.   
    7. // this event is triggered when there is no pending io  (pending io count == 1 )  
    8. KeInitializeEvent(&deviceExtension->NoPendingIoEvent, NotificationEvent, FALSE);  
    9.   
    10. // spinlock used to protect inc/dec iocount logic  
    11. KeInitializeSpinLock (&deviceExtension->IoCountSpinLock);  
    12.       
    13.     deviceExtension->BaudRate = 19200;  
    14. /* Set line control */  
    15. deviceExtension->SerialLineControl.StopBits = STOP_BIT_1;  
    16. deviceExtension->SerialLineControl.Parity = NO_PARITY;  
    17. deviceExtension->SerialLineControl.WordLength = 8;  
    18.   
    19. deviceExtension->SpecialChars.XonChar = SERIAL_DEF_XON;  
    20. deviceExtension->SpecialChars.XoffChar = SERIAL_DEF_XOFF;  
    21.   
    22. deviceExtension->HandFlow.ControlHandShake = SERIAL_DTR_CONTROL;  
    23. deviceExtension->HandFlow.FlowReplace      = SERIAL_RTS_CONTROL;  
    24. deviceExtension->HandFlow.XoffLimit    = 300;  
    25. deviceExtension->HandFlow.XonLimit     = 100;  
    26. InitializeCircularBuffer(&deviceExtension->InputBuffer, 512);  
    27. InitializeCircularBuffer(&deviceExtension->OutputBuffer, 512);  
    28. KeInitializeSpinLock(&deviceExtension->InputBufferLock);  
    29. KeInitializeSpinLock(&deviceExtension->OutputBufferLock);  
    30. InitializeListHead(&deviceExtension->ReadQueue);  
    31. KeInitializeSpinLock(&deviceExtension->ReadQueueSpinLock);  
    32. InitializeListHead(&deviceExtension->WriteQueue);  
    33. KeInitializeSpinLock(&deviceExtension->WriteQueueSpinLock);  
    34. InitializeListHead(&deviceExtension->PurgeQueue);  
    35. KeInitializeSpinLock(&deviceExtension->PurgeQueueSpinLock);  


     

    步二、让设备对象支持直接读写IO和并设置DO_POWER_PAGABLE

     设置DO_POWER_PAGABLE的目的是在suspend期间不接收一个IRP_MN_STOP_DEVICE,

    在resume时不接收一个IRP_MN_START_DEVICE消息。

    1. // we support direct io for read/write  
    2.        //  
    3.        deviceObject->Flags |= DO_DIRECT_IO;  
    4.   
    5.   
    6.        //Set this flag causes the driver to not receive a IRP_MN_STOP_DEVICE  
    7.        //during suspend and also not get an IRP_MN_START_DEVICE during resume.  
    8.        //This is neccesary because during the start device call,  
    9.        // the GetDescriptors() call  will be failed by the USB stack.  
    10.        deviceObject->Flags |= DO_POWER_PAGABLE;  

    步三、附加FDO到物理设备对象上,并创建SymbolLink,并通过IoRegisterDeviceInterface来设备绑定让设备可见。

    1. deviceExtension->TopOfStackDeviceObject =  
    2.             IoAttachDeviceToDeviceStack(deviceObject, PhysicalDeviceObject);  


     

    1. status = IoRegisterDeviceInterface(PDevExt->PhysicalDeviceObject, (LPGUID)&GUID_CLASS_COMPORT,  
    2.                                       NULL, &PDevExt->DeviceClassSymbolicName);  

    步四、获得物理设备的性能的一份复制,保存在extension中,以此来获得电源级别

     

     (1)建立IRP来产生一个发往FDO的内部查询请求;

    1. irp = IoAllocateIrp(LowerDeviceObject->StackSize, FALSE);  


    (2)设置IRP要发往的设备栈Location(是更低层的设备,在这里就是它附加下的USB)的信息,

    eg: MajorFunction、MinorFunction、Parameters.DeviceCapabilities.Capabilities

    1. nextStack = IoGetNextIrpStackLocation(irp);  
    2. nextStack->MajorFunction= IRP_MJ_PNP;  
    3. nextStack->MinorFunction= IRP_MN_QUERY_CAPABILITIES;  


    在以上代码中的IoGetNextIrpStackLocation是一个宏,它的定义如下:

    1. #define IoGetNextIrpStackLocation( Irp ) (\  
    2.     (Irp)->Tail.Overlay.CurrentStackLocation - 1 )  

    从以上宏可以看出:IRP结构体中存有当前的栈Location CurrentStackLoctation,而我们的IRP要发往的栈Location的获得方法就是原有栈的地址 - 1


     

     (3)设置IRP的完成例程;(在完成全程中就是把事件激活,这样KeWaitForSingleObject就能走下来)

    (4)把IRP发送下去,并等待完成;

     

    1. ntStatus = IoCallDriver(LowerDeviceObject,  
    2.                            irp);  
    3.   
    4.    USB2COM_KdPrint( DBGLVL_MEDIUM,(" USB2COM_QueryCapabilities() ntStatus from IoCallDriver to PCI = 0x%x\n", ntStatus));  
    5.   
    6.    if (ntStatus == STATUS_PENDING) {  
    7.       // wait for irp to complete  
    8.   
    9.       KeWaitForSingleObject(  
    10.            &event,  
    11.            Suspended,  
    12.            KernelMode,  
    13.            FALSE,  
    14.            NULL);  

    当完成全程返回时, nextStack->Parameters.DeviceCapabilities.Capabilities = DeviceCapabilities;指针就存着我们的性能信息。


    步五、获得USB的版本信息;

    直接调用系统API:

    1. USBD_GetUSBDIVersion(&versionInformation);  

     

    2、处理系统PNP和电源管理请求的派遣函数

    1. DriverObject->MajorFunction[IRP_MJ_PNP] = USB2COM_ProcessPnPIrp;  

    在USB2COM_ProcessPnPIrp里case了以下几个消息:

    IRP_MN_START_DEVICE 、IRP_MN_QUERY_STOP_DEVICE、IRP_MN_CANCEL_STOP_DEVICE、IRP_MN_STOP_DEVICE

    IRP_MN_QUERY_REMOVE_DEVICE、IRP_MN_CANCEL_REMOVE_DEVICE、IRP_MN_SURPRISE_REMOVAL、IRP_MN_REMOVE_DEVICE
    文章<<Window XP驱动开发(九) USB WDM驱动开发实例 bulkusb >>中讲到的同类派遣函数,它只case 了以下:

    IRP_MN_START_DEVICE、

    IRP_MN_STOP_DEVICE 、IRP_MN_EJECT和IRP_MN_SURPRISE_REMOVAL

    所以一比较,觉得USB2COM考虑得更全面。

    (1)IRP_MN_START_DEVICE

    与文章<<Window XP驱动开发(九) USB WDM驱动开发实例 bulkusb >>中的处理基本类似;

    (2)IRP_MN_QUERY_STOP_DEVICE

    与文章<<Window XP驱动开发(九) USB WDM驱动开发实例 bulkusb >>中的处理基本类似;

    (3)IRP_MN_CANCEL_STOP_DEVICE

     

    (4)IRP_MN_STOP_DEVICE、

    (5)IRP_MN_QUERY_REMOVE_DEVICE、

    (6)IRP_MN_CANCEL_REMOVE_DEVICE、

    (7)IRP_MN_SURPRISE_REMOVAL、

    (8)IRP_MN_REMOVE_DEVICE

     

    3、当应用程序CreateFile时,调用USB2COM_Create

     DriverObject->MajorFunction[IRP_MJ_CREATE] = USB2COM_Create;

    步一、判断是否能接收一个新的IO请求

    如果不能接收一个新的IO请求,那么直接返回。

    1. if ( !USB2COM_CanAcceptIoRequests( DeviceObject ) ) {  
    2.       ntStatus = STATUS_DELETE_PENDING;  
    3.   
    4. USB2COM_KdPrint( DBGLVL_DEFAULT,("ABORTING USB2COM_Create\n"));  
    5.       goto done;  
    6.   }  

     

    在以下的条件不能接收一个新的IO(判断的标志是我们自己标记的):

    1) 设备已经被移除了,
    2) 从来没有被启动过,,
    3) 已经停止了,
    4) 有一个移除的请求还没处理,
    5) 有一个停止的请求还没处理。

    1. //flag set when processing IRP_MN_REMOVE_DEVICE  
    2.     if ( !deviceExtension->DeviceRemoved &&  
    3.          // device must be started( enabled )  
    4.          deviceExtension->DeviceStarted &&  
    5.          // flag set when driver has answered success to IRP_MN_QUERY_REMOVE_DEVICE  
    6.          !deviceExtension->RemoveDeviceRequested &&  
    7.          // flag set when driver has answered success to IRP_MN_QUERY_STOP_DEVICE  
    8.          !deviceExtension->StopDeviceRequested ){  
    9.             fCan = TRUE;  
    10.     }  

    步二:开始从interface中的Pipe[0]中读(说明Interface的获取是在之前就完成的,是在USB2COM_ProcessPnPIrp
                                                                   里case IRP_MN_START_DEVICE:完成的)
    1. StartReadIntUrb(  
    2.         DeviceObject,  
    3.         &interface->Pipes[0]  
    4.         );  


    (1)如果判断现在不能接收IO请求,那么就记录在extension中的IRP置为完成状态,然后返回;

    (2)分配IRP、URB空间,并存到Extension->ReadIntUrbs数组中,再把Pipe句柄、Buffer、传送标志填入到URB结构体成员;

    1. irp = IoAllocateIrp(stackSize, FALSE);  
    2.         if(irp == NULL)   
    3.         {  
    4.                 return STATUS_INSUFFICIENT_RESOURCES;  
    5.         }  
    6.         urb = USB2COM_ExAllocatePool(NonPagedPool,   
    7.                         sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER));  
    8.         if(urb == NULL)  
    9.         {  
    10.             IoFreeIrp(irp);  
    11.         return STATUS_INSUFFICIENT_RESOURCES;  
    12.         }  
    13.         deviceExtension->ReadIntUrbs[i].Irp = irp;  
    14.         deviceExtension->ReadIntUrbs[i].Urb = urb;  
    15.         deviceExtension->ReadIntUrbs[i].deviceObject = DeviceObject;  
    16.         deviceExtension->ReadIntUrbs[i].PipeInfo = PipeInfo;  
    17.         InitIntUrb(urb,  
    18.                 PipeInfo->PipeHandle,  
    19.                 deviceExtension->ReadIntUrbs[i].TransferBuffer,  
    20.                 sizeof(deviceExtension->ReadIntUrbs[i].TransferBuffer),  
    21.                 TRUE);  

     

    InitIntUrb为自己封装的函数,很简单,就是把数据填入到urb结构体中:

    1. VOID  
    2. InitIntUrb(  
    3.     IN PURB urb,  
    4.     IN USBD_PIPE_HANDLE  PipeHandle,  
    5.     IN PUCHAR TransferBuffer,  
    6.     IN ULONG length,  
    7.     IN BOOLEAN Read  
    8.     )  
    9. {  
    10.     USHORT siz = sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER);  
    11.     if (urb) {  
    12.         RtlZeroMemory(urb, siz);  
    13.   
    14.         urb->UrbBulkOrInterruptTransfer.Hdr.Length = (USHORT) siz;  
    15.         urb->UrbBulkOrInterruptTransfer.Hdr.Function =  
    16.                     URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER;  
    17.         urb->UrbBulkOrInterruptTransfer.PipeHandle = PipeHandle;  
    18.         urb->UrbBulkOrInterruptTransfer.TransferFlags =  
    19.             Read ? USBD_TRANSFER_DIRECTION_IN : 0;  
    20.         // short packet is not treated as an error.  
    21.         urb->UrbBulkOrInterruptTransfer.TransferFlags |=   
    22.             USBD_SHORT_TRANSFER_OK;              
    23.                   
    24.         //  
    25.         // not using linked urb's  
    26.         //  
    27.         urb->UrbBulkOrInterruptTransfer.UrbLink = NULL;  
    28.         urb->UrbBulkOrInterruptTransfer.TransferBufferMDL = NULL;  
    29.         urb->UrbBulkOrInterruptTransfer.TransferBuffer = TransferBuffer;  
    30.         urb->UrbBulkOrInterruptTransfer.TransferBufferLength = length;  
    31.     }  
    32. }  


    (3)初始化IRP的栈Location及它的完成例程ReadIntUrbComplete,并把当前ReadIntUrbs的内存作为完成例程的Context;

    (4)完成例程ReadIntUrbComplete的处理;

    A、判断返回执行后返回的IRP、URB的状态,如果为未连接或取消那么就释放IRP及URB内存,并完成把extension中的IRP置为完成状态,然后直接返回;否则往下执行;

    B、把IRP之前绑定的Buffer数据拷到InputBuffer中(现在的Buffer就是我们的结果数据);

    1. KeAcquireSpinLock(&deviceExtension->InputBufferLock, &oldIrql);  
    2.             PushCircularBufferEntry(  
    3.                 &deviceExtension->InputBuffer,  
    4.                 &pIntUrbs->TransferBuffer[1],  
    5.                 pIntUrbs->TransferBuffer[0]);  
    6.             KeReleaseSpinLock(&deviceExtension->InputBufferLock, oldIrql);  

    PushCircularBufferEntry为自己封装的函数:

    把data内存中的len个数据拷到pBuffer中

    1. NTSTATUS  
    2. PushCircularBufferEntry(  
    3.     IN PCIRCULAR_BUFFER pBuffer,  
    4.     IN PUCHAR data,  
    5.     IN ULONG len)  
    6. {  
    7.     ULONG NextPosition;  
    8.     DbgPrint("Serial: PushCircularBufferEntry(data %p, len %d)\n", data, len);  
    9.     ASSERT(pBuffer);  
    10.     ASSERT(pBuffer->Length);  
    11.   
    12.     if ((data == NULL) || (len == 0))  
    13.         return STATUS_INVALID_PARAMETER;  
    14.     do{  
    15.         NextPosition = (pBuffer->WritePosition + 1) % pBuffer->Length;  
    16.         if (NextPosition == pBuffer->ReadPosition)  
    17.             return STATUS_BUFFER_TOO_SMALL;  
    18.         pBuffer->Buffer[pBuffer->WritePosition] = *data++;  
    19.         pBuffer->DataLen++;  
    20.         pBuffer->WritePosition = NextPosition;  
    21.     }while(--len);  
    22.       
    23.     return STATUS_SUCCESS;  
    24. }  

    C、如果extension中有等待的IRP,且等待的事件中有SERIAL_EV_RXCHAR,那么通过SerialCompleteCurrentWait完成等待串口的等待IRP。

    C、1   把当前IRP的取消完成例程置为NULL,根据包是否已经被取消标志pIrp->Cancel 及之前的IRP取消例程是否被执行过了,那么调用SerialCancelCurrentWait来取消。

    注意SerialCancelCurrentWait中很重要的是要通过调用IoReleaseCancelSpinLock来释放系统的删除自旋锁

    1. void  
    2. SerialCancelCurrentWait( PDEVICE_OBJECT DeviceObject, PIRP pIrp )  
    3. {  
    4.     PIO_STACK_LOCATION irpStack;  
    5.     PDEVICE_EXTENSION deviceExtension;  
    6.       
    7.     DbgPrint("SerialCancelCurrentWait Enter Irp = %p\n",pIrp);  
    8.     ASSERT(pIrp);  
    9.     irpStack = IoGetCurrentIrpStackLocation(pIrp);  
    10.     deviceExtension = irpStack->DeviceObject->DeviceExtension;  
    11.     deviceExtension->CurrentWaitIrp = NULL;  
    12.     /* 
    13.     *All Cancel routines must follow these guidelines: 
    14.     * 1. Call IoReleaseCancelSpinLock to release the system's cancel spin lock 
    15.     * 2. ... 
    16.     */  
    17.     IoReleaseCancelSpinLock(pIrp->CancelIrql);  
    18.     pIrp->IoStatus.Status = STATUS_CANCELLED;  
    19.     pIrp->IoStatus.Information = 0;  
    20.     IoCompleteRequest(pIrp, IO_NO_INCREMENT);  
    21.     DbgPrint("SerialCancelCurrentWait Exit\n");  
    22. }  

    C、2    如果没有被取消,那么把当前我们的事件返回给应用层,并完成IRP。

    1. deviceExtension->CurrentWaitIrp = NULL;  
    2. deviceExtension->HistoryMask &= ~events;  
    3. IoReleaseCancelSpinLock(OldIrql);  
    4. pIrp->IoStatus.Information = sizeof(ULONG);  
    5. pIrp->IoStatus.Status = ntStatus;  
    6. *((ULONG *)pIrp->AssociatedIrp.SystemBuffer) = events;  
    7. IoCompleteRequest (pIrp,IO_NO_INCREMENT);  

    D、完成当前的读IRP;

    D、1   如果有当前读的Irp,那么把第(4)B、里得到的结果数据弹出到当前的读IRP中(通过当前读的IRP中的MdlAddress地址访问到内存)。

    1. if(deviceExtension->CurrentReadIrp)  
    2. {  
    3.     ULONG       haveLen;  
    4.     BOOLEAN     returnWhatsPresent = FALSE;  
    5.       
    6.     if(deviceExtension->SerialTimeOuts.ReadIntervalTimeout &&  
    7.         ( deviceExtension->SerialTimeOuts.ReadTotalTimeoutMultiplier == 0) &&  
    8.         ( deviceExtension->SerialTimeOuts.ReadTotalTimeoutConstant == 0)  
    9.     )  
    10.     {  
    11.         returnWhatsPresent = TRUE;  
    12.     }  
    13.     ioBuffer = MmGetSystemAddressForMdlSafe(deviceExtension->CurrentReadIrp->MdlAddress,NormalPagePriority );  
    14.     ioLength = MmGetMdlByteCount(deviceExtension->CurrentReadIrp->MdlAddress);  
    15.     KeAcquireSpinLock(&deviceExtension->InputBufferLock, &oldIrql);  
    16.     haveLen = CircularBufferDataLen(&deviceExtension->InputBuffer);  
    17.     if( (ioLength <= haveLen) || (returnWhatsPresent && (haveLen > 0)))  
    18.     {  
    19.         ioLength = (ioLength < haveLen) ? ioLength : haveLen;  
    20.         DbgPrint("Complete CurrentReadIrp ioLength = %d\n",ioLength);  
    21.         ntStatus = PopCircularBufferEntry(&deviceExtension->InputBuffer,ioBuffer,ioLength);  
    22.         KeReleaseSpinLock(&deviceExtension->InputBufferLock, oldIrql);  
    23.         deviceExtension->CurrentReadIrp->IoStatus.Information = ioLength;  
    24.         deviceExtension->CurrentReadIrp->IoStatus.Status = ntStatus;   
    25.         IoCompleteRequest(deviceExtension->CurrentReadIrp,IO_NO_INCREMENT);  
    26.         deviceExtension->CurrentReadIrp = DequeueReadIrp(deviceExtension);  
    27.     }  
    28.     else  
    29.         KeReleaseSpinLock(&deviceExtension->InputBufferLock, oldIrql);  
    30. }  

    以上代码中MmGetSystemAddressForMdlSafe

    1. // 函数说明:  
    2. //     此函数返回MDL映射的地址,如果此MDL还没有被映射,那么它将会被映射  
    3. // 参数:  
    4. //     MemoryDescriptorList - 指向MDL的指针  
    5. //     Priority - 指向一个标志,该标记表明它是如何的重要,以至于这个请求在低的可用PTE条件下也成功了  
    6. // 返回值:  
    7. //     返回映射页的基地址,这个基地址和MDL的虚拟地址有同样的偏移地址.  
    8. //     与MmGetSystemAddressForMdl不同,在失败时它会返回NULL,而不是bugchecking the system.  
    9. // 版本说明:  
    10. //     此宏在WDM 1.0中不同用,在WDM1.0的驱动中实现此功能是通过提供同步并set/reset MDL_MAPPING_CAN_FAIL bit.  
    11. #define MmGetSystemAddressForMdlSafe(MDL, PRIORITY)                    \  
    12.      (((MDL)->MdlFlags & (MDL_MAPPED_TO_SYSTEM_VA |                    \  
    13.                         MDL_SOURCE_IS_NONPAGED_POOL)) ?                \  
    14.                              ((MDL)->MappedSystemVa) :                 \  
    15.                              (MmMapLockedPagesSpecifyCache((MDL),      \  
    16.                                                            KernelMode, \  
    17.                                                            MmCached,   \  
    18.                                                            NULL,       \  
    19.                                                            FALSE,      \  
    20.                                                            (PRIORITY))))  


     

    第一个参数是deviceExtension->CurrentReadIrp->MdlAddress,我们从wdm.h可以看到它的定义:

    1. // 定义一个指向这个I/O请求的内存描述符(MDL)的指针,  
    2. // 此域仅在I/O是“direct I/O"时被用  
    3. PMDL MdlAddress;  


    D、2   判断是否能接收一个新的IO请求

    如果不能接收一个新的IO请求或者Pipe[0]关闭了,那么直接返回。


    D、3   循环发送IRP,并置完成例程为ReadIntUrbComplete。(这样,就又回到了(4)),何时结束呢?

    结束条件:直至设备不能接收一个新的IO请求或未连接、取消。

    所以处理当前读IRP是一直进行的。

    步三:准备写interface中的Pipe[1] (说明Interface的获取是在之前就完成的)
    1. PrepareWriteIntUrb(  
    2.     IN PDEVICE_OBJECT DeviceObject,  
    3.     IN PUSBD_PIPE_INFORMATION PipeInfo  
    4.     )  

    A、分配写的Irb 与 Urb空间。

    B、保存Pipe[1]地址为deviceExtension->WriteIntUrb.PipeInfo存在extension中。

    步四、用应用程序传进来的Pipe名字来查找Pipe;(从extension中保存的PipeInfo数组中获得,说明PipeInfo在之前就获得了)

    应用程序的名字从当前IRP中的fileObject得到。

    1. ourPipeInfo = USB2COM_PipeWithName( DeviceObject, &fileObject->FileName );  

    步五、给应用程序返回步四中查找到的Pipe对应的Pipe信息,且如果设备不是D0状态那么就给设备上电;
    1. for (i=0; i<interface->NumberOfPipes; i++) {  
    2.   
    3.         PipeInfo =  &interface->Pipes[i]; // PUSBD_PIPE_INFORMATION  PipeInfo;  
    4.   
    5.         if ( ourPipeInfo == &deviceExtension->PipeInfo[i] ) {  
    6.   
    7.             //  
    8.             // found a match  
    9.             //  
    10.             USB2COM_KdPrint( DBGLVL_DEFAULT,("open pipe %d\n", i));  
    11.             fileObject->FsContext = PipeInfo;  
    12.             ourPipeInfo->fPipeOpened = TRUE; // set flag for opened  
    13.             ntStatus = STATUS_SUCCESS;  
    14.   
    15.             deviceExtension->OpenPipeCount++;  
    16.   
    17.             // try to power up device if its not in D0  
    18.             actStat = USB2COM_SelfSuspendOrActivate( DeviceObject, FALSE );  
    19.             break;  
    20.         }  
    21.     }  


     

    展开全文
  • 这里需要注意的是,当usb转串口模块插到usb上面/dev下面才能有设备,拔了设备就没了,所以操作的时候要注意。 #include <stdio.h> //标准输入输出,如printf、scanf以及文件操作 #include <stdlib.h> /...

     这里需要注意的是,当usb转串口模块插到usb上面/dev下面才能有设备,拔了设备就没了,所以操作的时候要注意。

    #include <stdio.h>        //标准输入输出,如printf、scanf以及文件操作
    #include <stdlib.h>        //标准库头文件,定义了五种类型、一些宏和通用工具函数
    #include <unistd.h>        //定义 read write close lseek 等Unix标准函数
    #include <sys/types.h>    //定义数据类型,如 ssiz e_t off_t 等
    #include <sys/stat.h>    //文件状态
    #include <fcntl.h>        //文件控制定义
    #include <termios.h>    //终端I/O
    #include <errno.h>        //与全局变量 errno 相关的定义
    #include <getopt.h>        //处理命令行参数
    #include <string.h>        //字符串操作
    #include <time.h>        //时间
    #include <sys/select.h>    //select函数
    
    
    #include <sys/ioctl.h>
    #include <net/if.h>
    #include <pthread.h>
    
    
    int usb2uartfd;
    
    
    int setOpt(int fd, int nSpeed, int nBits, int nParity, int nStop)
    {
        struct termios newtio, oldtio;
    
        // 保存测试现有串口参数设置,在这里如果串口号等出错,会有相关的出错信息
        if (tcgetattr(fd, &oldtio) != 0)
        {
            perror("SetupSerial 1");
            return -1;
        }
    
        bzero(&newtio, sizeof(newtio));        //新termios参数清零
        newtio.c_cflag |= CLOCAL | CREAD;    //CLOCAL--忽略 modem 控制线,本地连线, 不具数据机控制功能, CREAD--使能接收标志
        // 设置数据位数
        newtio.c_cflag &= ~CSIZE;    //清数据位标志
        switch (nBits)
        {
            case 7:
                newtio.c_cflag |= CS7;
            break;
            case 8:
                newtio.c_cflag |= CS8;
            break;
            default:
                fprintf(stderr, "Unsupported data size\n");
                return -1;
        }
        // 设置校验位
        switch (nParity)
        {
            case 'o':
            case 'O':                     //奇校验
                newtio.c_cflag |= PARENB;
                newtio.c_cflag |= PARODD;
                newtio.c_iflag |= (INPCK | ISTRIP);
                break;
            case 'e':
            case 'E':                     //偶校验
                newtio.c_iflag |= (INPCK | ISTRIP);
                newtio.c_cflag |= PARENB;
                newtio.c_cflag &= ~PARODD;
                break;
            case 'n':
            case 'N':                    //无校验
                newtio.c_cflag &= ~PARENB;
                break;
            default:
                fprintf(stderr, "Unsupported parity\n");
                return -1;
        }
        // 设置停止位
        switch (nStop)
        {
            case 1:
                newtio.c_cflag &= ~CSTOPB;
            break;
            case 2:
                newtio.c_cflag |= CSTOPB;
            break;
            default:
                fprintf(stderr,"Unsupported stop bits\n");
                return -1;
        }
        // 设置波特率 2400/4800/9600/19200/38400/57600/115200/230400
        switch (nSpeed)
        {
            case 2400:
                cfsetispeed(&newtio, B2400);
                cfsetospeed(&newtio, B2400);
                break;
            case 4800:
                cfsetispeed(&newtio, B4800);
                cfsetospeed(&newtio, B4800);
                break;
            case 9600:
                cfsetispeed(&newtio, B9600);
                cfsetospeed(&newtio, B9600);
                break;
            case 19200:
                cfsetispeed(&newtio, B19200);
                cfsetospeed(&newtio, B19200);
                break;
            case 38400:
                cfsetispeed(&newtio, B38400);
                cfsetospeed(&newtio, B38400);
                break;
            case 57600:
                cfsetispeed(&newtio, B57600);
                cfsetospeed(&newtio, B57600);
                break;
            case 115200:
                cfsetispeed(&newtio, B115200);
                cfsetospeed(&newtio, B115200);
                break;
            case 230400:
                cfsetispeed(&newtio, B230400);
                cfsetospeed(&newtio, B230400);
                break;
            default:
                printf("\tSorry, Unsupported baud rate, set default 9600!\n\n");
                cfsetispeed(&newtio, B9600);
                cfsetospeed(&newtio, B9600);
                break;
        }
        // 设置read读取最小字节数和超时时间
        newtio.c_cc[VTIME] = 1;     // 读取一个字符等待1*(1/10)s
        newtio.c_cc[VMIN] = 1;        // 读取字符的最少个数为1
    
    	tcflush(fd,TCIFLUSH);         //清空缓冲区
    	if (tcsetattr(fd, TCSANOW, &newtio) != 0)    //激活新设置
    	{
    		perror("SetupSerial 3");
    		return -1;
    	}
    	printf("Serial set done!\n");
        return 0;
    }
    
    
    
    void usb2uartfdCreate()
    {
    	int ret = 0;
    	
    	usb2uartfd = open("/dev/ttyUSB0", O_RDWR | O_NOCTTY | O_NDELAY);
    	if(usb2uartfd < 0)
    	{
    		printf("usb uart open failed ret = %d\r\n",usb2uartfd);
    		usb2uartfd = -1;
    		return;
    	}
    	
    	ret = fcntl(usb2uartfd, F_SETFL, 0);
    	if(ret < 0)
    	{
    		printf("fcntl failed ret = %d\r\n",ret);
    		close(usb2uartfd);
    		usb2uartfd = -1;
    		return;
    	}
    	
    	if (isatty(usb2uartfd) == 0)
    	{
    		printf("standard input is not a terminal device\r\n");
    		close(usb2uartfd);
    		usb2uartfd = -1;
    		return;	
    	}
    	
    	if (setOpt(usb2uartfd, 115200, 8, 'N', 1)== -1)    //设置8位数据位、1位停止位、无校验
    	{
    		printf("Set opt Error\r\n");
    		close(usb2uartfd);
    		usb2uartfd = -1;
    		return ;				
    	}
    	
    	tcflush(usb2uartfd, TCIOFLUSH);    //清掉串口缓存
    	fcntl(usb2uartfd, F_SETFL, 0);    //串口阻塞
    	
    	printf("uart fd create ok\r\n");
    }
    
    void *uartrecv(void *arg)
    {
    	unsigned int maxfd = 0;
    	unsigned int ret = 0;
    	struct timeval waittime;
    	fd_set rfd;
    	char buf[128];
    	
    	while(1)
    	{
    		FD_ZERO(&rfd);
    		
    		if(usb2uartfd == -1)
    			return NULL;
    		
    		FD_SET(usb2uartfd,&rfd);
    		if(maxfd < usb2uartfd)
    		{
    			maxfd = usb2uartfd;
    		}
    		
    		waittime.tv_sec = 1;
    		waittime.tv_usec = 0;
    		ret = select(maxfd + 1,&rfd,NULL,NULL,&waittime);
    		if(0 >= ret)
    		{
    			continue;
    		}
    		
    		if(FD_ISSET(usb2uartfd,&rfd))
    		{
    			memset(buf,0,sizeof(buf));
    			read(usb2uartfd,buf,sizeof(buf) - 1);
    			printf("usb uart recv buf = %s\r\n",buf);
    		}
    	}
    }
    
    
    
    
    void main()
    {
    	char buf[128];
    	pthread_t ntid;
    	
    	usb2uartfdCreate();
    		
    	pthread_create(&ntid, NULL, uartrecv, NULL);
    	
    	while(1)
    	{
    		if(usb2uartfd != -1)
    		{
    			memset(buf,0,sizeof(buf));
    			sprintf(buf,"this is usb uart send date");
    			write(usb2uartfd,buf,strlen(buf));
    			tcflush(usb2uartfd,TCOFLUSH);
    		}
    			
    		sleep(5);
    	}
    }

     

    展开全文
  • 【实例简介】通过USB与PLC设备进行通信,从而控制PLC设备。...【实例截图】【核心代码】USBToSerialPort├── __MACOSX│ └── USBToSerialPort│ └── usb-serial-for-android-master│ ├── arduino│...

    【实例简介】

    通过USB与PLC设备进行通信,从而控制PLC设备。sdk是在前人的基础上进行定制封装,可重复利用。目前已在某洗车设备上进行使用。

    【实例截图】

    【核心代码】

    USBToSerialPort

    ├── __MACOSX

    │   └── USBToSerialPort

    │   └── usb-serial-for-android-master

    │   ├── arduino

    │   ├── build

    │   ├── gradle

    │   │   └── wrapper

    │   ├── usbSerialExamples

    │   │   ├── build

    │   │   └── src

    │   │   └── main

    │   │   ├── java

    │   │   │   └── com

    │   │   │   └── hoho

    │   │   │   └── android

    │   │   │   └── usbserial

    │   │   └── res

    │   │   ├── drawable-hdpi

    │   │   ├── drawable-ldpi

    │   │   ├── drawable-mdpi

    │   │   ├── layout

    │   │   ├── values

    │   │   └── xml

    │   └── usbSerialForAndroid

    │   ├── build

    │   └── src

    │   └── main

    │   └── java

    │   └── com

    │   └── hoho

    │   └── android

    │   └── usbserial

    │   ├── driver

    │   └── util

    └── USBToSerialPort

    ├── proguard-usb_serialport_20180318b0.jar

    └── usb-serial-for-android-master

    ├── arduino

    │   └── serial_test.ino

    ├── build

    ├── build.gradle

    ├── CHANGELOG.txt

    ├── gradle

    │   └── wrapper

    │   ├── gradle-wrapper.jar

    │   └── gradle-wrapper.properties

    ├── gradlew

    ├── gradlew.bat

    ├── LICENSE.txt

    ├── local.properties

    ├── README.md

    ├── settings.gradle

    ├── usbSerialExamples

    │   ├── build

    │   │   └── outputs

    │   │   ├── apk

    │   │   │   └── usbSerialExamples-debug.apk

    │   │   ├── DeviceListActivity.java

    │   │   ├── logs

    │   │   │   └── manifest-merger-debug-report.txt

    │   │   └── SerialConsoleActivity.java

    │   ├── build.gradle

    │   ├── src

    │   │   └── main

    │   │   ├── AndroidManifest.xml

    │   │   ├── java

    │   │   │   ├── com

    │   │   │   │   └── hoho

    │   │   │   │   └── android

    │   │   │   │   └── usbserial

    │   │   │   │   └── examples

    │   │   │   │   ├── AppConfig.java

    │   │   │   │   ├── AppException.java

    │   │   │   │   ├── DataUtils.java

    │   │   │   │   ├── DateUtils.java

    │   │   │   │   ├── DeviceListActivity.java

    │   │   │   │   ├── FileUtils.java

    │   │   │   │   ├── LogInfoManager.java

    │   │   │   │   ├── LogUtils.java

    │   │   │   │   ├── MainApplication.java

    │   │   │   │   ├── SerialConsoleActivity.java

    │   │   │   │   └── USBManager.java

    │   │   │   └── test

    │   │   │   └── usb

    │   │   │   └── serialport

    │   │   │   ├── DeviceListActivityI.java

    │   │   │   ├── DeviceListActivity.java

    │   │   │   ├── SerialConsoleActivityI.java

    │   │   │   └── SerialConsoleActivity.java

    │   │   └── res

    │   │   ├── drawable-hdpi

    │   │   │   └── ic_launcher.png

    │   │   ├── drawable-ldpi

    │   │   │   └── ic_launcher.png

    │   │   ├── drawable-mdpi

    │   │   │   └── ic_launcher.png

    │   │   ├── layout

    │   │   │   ├── main.xml

    │   │   │   ├── serial_console.xml

    │   │   │   ├── usbserial_console_activity.xml

    │   │   │   └── usbserial_list_activity.xml

    │   │   ├── values

    │   │   │   └── strings.xml

    │   │   └── xml

    │   │   └── device_filter.xml

    │   └── usbSerialExamples.iml

    ├── usbSerialForAndroid

    │   ├── build

    │   │   └── libs

    │   │   ├── proguard-usb_serialport_20180318b02.jar

    │   │   └── usb_serialport_20180318b02.jar

    │   ├── build.gradle

    │   ├── proguard.map

    │   ├── proguard-rules.pro

    │   ├── src

    │   │   └── main

    │   │   ├── AndroidManifest.xml

    │   │   └── java

    │   │   └── com

    │   │   ├── android

    │   │   │   └── usbport

    │   │   │   ├── ModbusTransferImpl.java

    │   │   │   ├── ModbusTransfer.java

    │   │   │   ├── USBDevice.java

    │   │   │   ├── USBManagerImpl.java

    │   │   │   ├── USBManager.java

    │   │   │   └── USBParams.java

    │   │   └── hoho

    │   │   └── android

    │   │   └── usbserial

    │   │   ├── BuildInfo.java

    │   │   ├── driver

    │   │   │   ├── CdcAcmSerialDriver.java

    │   │   │   ├── Ch34xSerialDriver.java

    │   │   │   ├── CommonUsbSerialPort.java

    │   │   │   ├── Cp21xxSerialDriver.java

    │   │   │   ├── FtdiSerialDriver.java

    │   │   │   ├── ProbeTable.java

    │   │   │   ├── ProlificSerialDriver.java

    │   │   │   ├── UsbId.java

    │   │   │   ├── UsbSerialDriver.java

    │   │   │   ├── UsbSerialPort.java

    │   │   │   ├── UsbSerialProber.java

    │   │   │   └── UsbSerialRuntimeException.java

    │   │   └── util

    │   │   ├── HexDump.java

    │   │   ├── SerialInputOutputManager.java

    │   │   └── StringUtil.java

    │   └── usbSerialForAndroid.iml

    └── usb-serial-for-android-master.iml

    77 directories, 73 files

    展开全文
  • android usb转串口数据通信示例

    热门讨论 2017-09-14 15:50:37
    android usb转串口数据通信示例。物联网开发中也会经常用到usb转串口,对android手机进行通信。一般都会用otc线进行转换。我在GitHub下来一份代码,亲测可用。并进行了修改封装。我的博客地址:...
  • 安卓usb转串口通讯

    千次阅读 2017-08-09 16:10:43
    我是参考了这篇博客http://blog.csdn.net/tianruxishui/article/details/38338087,里面讲解的很清楚,其实安卓串口转usb就是usb接收的数据还要经过那个usb转串口芯片的处理,也就需要数据处理的代码,相当于是安卓...
  • 文件名称: usb-serial-for-android-master下载 收藏√ [5 4 3 2 1]开发工具: Java文件大小: 126 KB上传时间: 2017-02-20下载次数: 0详细说明:android usb转串口驱动及应用,希望对大家有用,谢谢!-Android USB ...
  • Android手机的USB转串口开发Android手机的USB接口在各个领域都得到了广泛的应用和推广。笔者前几天接手一个项目,主要就是解决工业现场数据采集的问题,该项目是由深圳宏进科技委托笔者开发的。现场的总线是RS485和...
  • PL-2303 USB转串口驱动

    2018-08-11 10:28:17
    还有一个USB转串口。 使用注意: 如若win10之前对pl2303支持较好,可是使用,那就不必要进行一下操作了,否则,你需要先卸载之前相关的一切pl2303驱动,安装本驱动(pl2303),并进行如下设置,以保证重启后驱动正常...
  • USB转串口

    千次阅读 2016-03-25 18:03:26
    刚开始我也是这样的,因为只是看了串口的代码,但是对串口这个硬件的东西完全不了解,今天来扫盲。 先来说说TTL电平和232电平这两个东西。 TTL电平:一般用作数字芯片的电平,例如芯片的供电电压是5V,那么高电平...
  • usb转串口万能驱动.RAR

    2011-09-19 13:19:28
    1.usb转串口万能驱动 2.串口技术的分析 3.源需要源代码的可以找我 4.串口测试工具
  • 在win10系统下,在更新USB转串口驱动后,出现黄色叹号,无法识别设备,然后串口无法正常使用,下载安装USB转串口驱动后,每次插USB转串口线,电脑就会蓝屏,终止代码:IRQL NOT LESS OR EQUAL 下载此驱动,拔下电脑...
  • C# USB 转串口突然拔出检测解决方案 最近做串口通讯程序由于是 USB 设备所以在通讯过程中有可能把串口拔出程序需要实 时检测到串口...再查找串口是否已经不存在代码如下 /// <summary> /// USB转串口拔出处理 /// </sum
  • 因为stm32的usb转串口demo的枚举过程是基于状态的,分析起来不明了,改为基于ucos2的基于过程,代码直接明了 分析起来
  • 这里写自定义目录标题欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、...
  • 在win10操作系统中使用usb转串口工具时有可能遇到以下错误:USB-to-serial 设备无法启动 代码(10)。这是因为在win10上需要使用较老版本的pl2303驱动。安装方法可参考...
  • usb转串口异步读取数据

    千次阅读 2017-08-30 17:35:29
    该实验是通过usb转串口线连接了开发板的 uart3和pc。 在pc上编译下面代码并执行 #include #include #include #include #include #include #define MODEMDEVICE "/dev/ttyUSB0" #define FALSE 0 ...
  • PL2303 USB转串口 com

    2018-05-01 13:20:00
    PL2303 USB转串口 com PL-2303 XA/HXA chip http://www.prolific.com.tw/US/ShowProduct.aspx?p_id=225&pcid=41 该设备无法启动 代码10 Ser2pl64.sys win10 OK Prolific 3.8.18.0 2017-10-17 ...
  • 解决win10,64位系统下 PL2303 USB转串口驱动问题

    万次阅读 多人点赞 2018-06-14 15:06:17
    或者下载安装USB转串口驱动后,每次插USB转串口线,电脑就会蓝屏,终止代码:IRQL NOT LESS OR EQUAL解决办法: 1、下载驱动下载地址:http://file5.mydrivers.com/2016/others/pl2303_3321...
  • ubuntu添加USB转串口 sudo /sbin/modprobe usbserial echo 067b 23c3 > /sys/bus/usb-serial/drivers/generic/new_id 如果是脚本运行用户只要插上USB后,./autoInstall.sh就可以一键加载usb驱动 参考脚本如下: ...

空空如也

空空如也

1 2 3 4 5 ... 15
收藏数 281
精华内容 112
关键字:

usb转串口代码