1、USB插入时,创建设备

[plain] view plaincopy
  1. DriverObject->DriverExtension->AddDevice = USB2COM_PnPAddDevice;
步一、调用USB2COM_CreateDeviceObject创建功能设备对象(FDO)

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

[plain] view plaincopy
  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. // this event is triggered when self-requested power irps complete
  4. KeInitializeEvent(&deviceExtension->SelfRequestedPowerIrpEvent, NotificationEvent, FALSE);
  5. // this event is triggered when there is no pending io  (pending io count == 1 )
  6. KeInitializeEvent(&deviceExtension->NoPendingIoEvent, NotificationEvent, FALSE);
  7. // spinlock used to protect inc/dec iocount logic
  8. KeInitializeSpinLock (&deviceExtension->IoCountSpinLock);
  9. deviceExtension->BaudRate = 19200;
  10. /* Set line control */
  11. deviceExtension->SerialLineControl.StopBits = STOP_BIT_1;
  12. deviceExtension->SerialLineControl.Parity = NO_PARITY;
  13. deviceExtension->SerialLineControl.WordLength = 8;
  14. deviceExtension->SpecialChars.XonChar = SERIAL_DEF_XON;
  15. deviceExtension->SpecialChars.XoffChar = SERIAL_DEF_XOFF;
  16. deviceExtension->HandFlow.ControlHandShake = SERIAL_DTR_CONTROL;
  17. deviceExtension->HandFlow.FlowReplace      = SERIAL_RTS_CONTROL;
  18. deviceExtension->HandFlow.XoffLimit    = 300;
  19. deviceExtension->HandFlow.XonLimit     = 100;
  20. InitializeCircularBuffer(&deviceExtension->InputBuffer, 512);
  21. InitializeCircularBuffer(&deviceExtension->OutputBuffer, 512);
  22. KeInitializeSpinLock(&deviceExtension->InputBufferLock);
  23. KeInitializeSpinLock(&deviceExtension->OutputBufferLock);
  24. InitializeListHead(&deviceExtension->ReadQueue);
  25. KeInitializeSpinLock(&deviceExtension->ReadQueueSpinLock);
  26. InitializeListHead(&deviceExtension->WriteQueue);
  27. KeInitializeSpinLock(&deviceExtension->WriteQueueSpinLock);
  28. InitializeListHead(&deviceExtension->PurgeQueue);
  29. 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. //Set this flag causes the driver to not receive a IRP_MN_STOP_DEVICE
  5. //during suspend and also not get an IRP_MN_START_DEVICE during resume.
  6. //This is neccesary because during the start device call,
  7. // the GetDescriptors() call  will be failed by the USB stack.
  8. 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. USB2COM_KdPrint( DBGLVL_MEDIUM,(" USB2COM_QueryCapabilities() ntStatus from IoCallDriver to PCI = 0x%x\n", ntStatus));
  4. if (ntStatus == STATUS_PENDING) {
  5. // wait for irp to complete
  6. KeWaitForSingleObject(
  7. &event,
  8. Suspended,
  9. KernelMode,
  10. FALSE,
  11. 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. USB2COM_KdPrint( DBGLVL_DEFAULT,("ABORTING USB2COM_Create\n"));
  4. goto done;
  5. }

在以下的条件不能接收一个新的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. urb->UrbBulkOrInterruptTransfer.Hdr.Length = (USHORT) siz;
  14. urb->UrbBulkOrInterruptTransfer.Hdr.Function =
  15. URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER;
  16. urb->UrbBulkOrInterruptTransfer.PipeHandle = PipeHandle;
  17. urb->UrbBulkOrInterruptTransfer.TransferFlags =
  18. Read ? USBD_TRANSFER_DIRECTION_IN : 0;
  19. // short packet is not treated as an error.
  20. urb->UrbBulkOrInterruptTransfer.TransferFlags |=
  21. USBD_SHORT_TRANSFER_OK;
  22. //
  23. // not using linked urb's
  24. //
  25. urb->UrbBulkOrInterruptTransfer.UrbLink = NULL;
  26. urb->UrbBulkOrInterruptTransfer.TransferBufferMDL = NULL;
  27. urb->UrbBulkOrInterruptTransfer.TransferBuffer = TransferBuffer;
  28. urb->UrbBulkOrInterruptTransfer.TransferBufferLength = length;
  29. }
  30. }

(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. if ((data == NULL) || (len == 0))
  12. return STATUS_INVALID_PARAMETER;
  13. do{
  14. NextPosition = (pBuffer->WritePosition + 1) % pBuffer->Length;
  15. if (NextPosition == pBuffer->ReadPosition)
  16. return STATUS_BUFFER_TOO_SMALL;
  17. pBuffer->Buffer[pBuffer->WritePosition] = *data++;
  18. pBuffer->DataLen++;
  19. pBuffer->WritePosition = NextPosition;
  20. }while(--len);
  21. return STATUS_SUCCESS;
  22. }

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

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

以上代码中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. PipeInfo =  &interface->Pipes[i]; // PUSBD_PIPE_INFORMATION  PipeInfo;
  3. if ( ourPipeInfo == &deviceExtension->PipeInfo[i] ) {
  4. //
  5. // found a match
  6. //
  7. USB2COM_KdPrint( DBGLVL_DEFAULT,("open pipe %d\n", i));
  8. fileObject->FsContext = PipeInfo;
  9. ourPipeInfo->fPipeOpened = TRUE; // set flag for opened
  10. ntStatus = STATUS_SUCCESS;
  11. deviceExtension->OpenPipeCount++;
  12. // try to power up device if its not in D0
  13. actStat = USB2COM_SelfSuspendOrActivate( DeviceObject, FALSE );
  14. break;
  15. }
  16. }

1、USB插入时,创建设备

[plain] view plaincopy
  1. DriverObject->DriverExtension->AddDevice = USB2COM_PnPAddDevice;
步一、调用USB2COM_CreateDeviceObject创建功能设备对象(FDO)

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

[plain] view plaincopy
  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. // this event is triggered when self-requested power irps complete
  4. KeInitializeEvent(&deviceExtension->SelfRequestedPowerIrpEvent, NotificationEvent, FALSE);
  5. // this event is triggered when there is no pending io  (pending io count == 1 )
  6. KeInitializeEvent(&deviceExtension->NoPendingIoEvent, NotificationEvent, FALSE);
  7. // spinlock used to protect inc/dec iocount logic
  8. KeInitializeSpinLock (&deviceExtension->IoCountSpinLock);
  9. deviceExtension->BaudRate = 19200;
  10. /* Set line control */
  11. deviceExtension->SerialLineControl.StopBits = STOP_BIT_1;
  12. deviceExtension->SerialLineControl.Parity = NO_PARITY;
  13. deviceExtension->SerialLineControl.WordLength = 8;
  14. deviceExtension->SpecialChars.XonChar = SERIAL_DEF_XON;
  15. deviceExtension->SpecialChars.XoffChar = SERIAL_DEF_XOFF;
  16. deviceExtension->HandFlow.ControlHandShake = SERIAL_DTR_CONTROL;
  17. deviceExtension->HandFlow.FlowReplace      = SERIAL_RTS_CONTROL;
  18. deviceExtension->HandFlow.XoffLimit    = 300;
  19. deviceExtension->HandFlow.XonLimit     = 100;
  20. InitializeCircularBuffer(&deviceExtension->InputBuffer, 512);
  21. InitializeCircularBuffer(&deviceExtension->OutputBuffer, 512);
  22. KeInitializeSpinLock(&deviceExtension->InputBufferLock);
  23. KeInitializeSpinLock(&deviceExtension->OutputBufferLock);
  24. InitializeListHead(&deviceExtension->ReadQueue);
  25. KeInitializeSpinLock(&deviceExtension->ReadQueueSpinLock);
  26. InitializeListHead(&deviceExtension->WriteQueue);
  27. KeInitializeSpinLock(&deviceExtension->WriteQueueSpinLock);
  28. InitializeListHead(&deviceExtension->PurgeQueue);
  29. 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. //Set this flag causes the driver to not receive a IRP_MN_STOP_DEVICE
  5. //during suspend and also not get an IRP_MN_START_DEVICE during resume.
  6. //This is neccesary because during the start device call,
  7. // the GetDescriptors() call  will be failed by the USB stack.
  8. 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. USB2COM_KdPrint( DBGLVL_MEDIUM,(" USB2COM_QueryCapabilities() ntStatus from IoCallDriver to PCI = 0x%x\n", ntStatus));
  4. if (ntStatus == STATUS_PENDING) {
  5. // wait for irp to complete
  6. KeWaitForSingleObject(
  7. &event,
  8. Suspended,
  9. KernelMode,
  10. FALSE,
  11. 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. USB2COM_KdPrint( DBGLVL_DEFAULT,("ABORTING USB2COM_Create\n"));
  4. goto done;
  5. }

在以下的条件不能接收一个新的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. urb->UrbBulkOrInterruptTransfer.Hdr.Length = (USHORT) siz;
  14. urb->UrbBulkOrInterruptTransfer.Hdr.Function =
  15. URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER;
  16. urb->UrbBulkOrInterruptTransfer.PipeHandle = PipeHandle;
  17. urb->UrbBulkOrInterruptTransfer.TransferFlags =
  18. Read ? USBD_TRANSFER_DIRECTION_IN : 0;
  19. // short packet is not treated as an error.
  20. urb->UrbBulkOrInterruptTransfer.TransferFlags |=
  21. USBD_SHORT_TRANSFER_OK;
  22. //
  23. // not using linked urb's
  24. //
  25. urb->UrbBulkOrInterruptTransfer.UrbLink = NULL;
  26. urb->UrbBulkOrInterruptTransfer.TransferBufferMDL = NULL;
  27. urb->UrbBulkOrInterruptTransfer.TransferBuffer = TransferBuffer;
  28. urb->UrbBulkOrInterruptTransfer.TransferBufferLength = length;
  29. }
  30. }

(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. if ((data == NULL) || (len == 0))
  12. return STATUS_INVALID_PARAMETER;
  13. do{
  14. NextPosition = (pBuffer->WritePosition + 1) % pBuffer->Length;
  15. if (NextPosition == pBuffer->ReadPosition)
  16. return STATUS_BUFFER_TOO_SMALL;
  17. pBuffer->Buffer[pBuffer->WritePosition] = *data++;
  18. pBuffer->DataLen++;
  19. pBuffer->WritePosition = NextPosition;
  20. }while(--len);
  21. return STATUS_SUCCESS;
  22. }

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

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

以上代码中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. PipeInfo =  &interface->Pipes[i]; // PUSBD_PIPE_INFORMATION  PipeInfo;
  3. if ( ourPipeInfo == &deviceExtension->PipeInfo[i] ) {
  4. //
  5. // found a match
  6. //
  7. USB2COM_KdPrint( DBGLVL_DEFAULT,("open pipe %d\n", i));
  8. fileObject->FsContext = PipeInfo;
  9. ourPipeInfo->fPipeOpened = TRUE; // set flag for opened
  10. ntStatus = STATUS_SUCCESS;
  11. deviceExtension->OpenPipeCount++;
  12. // try to power up device if its not in D0
  13. actStat = USB2COM_SelfSuspendOrActivate( DeviceObject, FALSE );
  14. break;
  15. }
  16. }

USB转串口驱动代码分析的更多相关文章

  1. Linux 串口、usb转串口驱动分析(2-2) 【转】

    转自:http://blog.chinaunix.net/xmlrpc.php?r=blog/article&uid=26807463&id=4186852 Linux 串口.usb转 ...

  2. linux驱动基础系列--Linux 串口、usb转串口驱动分析

    前言 主要是想对Linux 串口.usb转串口驱动框架有一个整体的把控,因此会忽略某些细节,同时里面涉及到的一些驱动基础,比如字符设备驱动.平台驱动等也不进行详细说明原理.如果有任何错误地方,请指出, ...

  3. Linux 串口、usb转串口驱动分析(2-1) 【转】

    转自:http://blog.chinaunix.net/xmlrpc.php?r=blog/article&uid=26807463&id=4186851 Linux 串口.usb转 ...

  4. usb转串口驱动时会出现“文件的哈希值不在指定的目录”这样的提示

    一般在安装一些usb转串口驱动时会出现“文件的哈希值不在指定的目录”这样的提示,那么怎么解决呢?知道的别喷我哦,我只是再普及一下,嘿嘿1.鼠标移到右下角,点击“设置”,再点击“更改电脑设置”2.点击最 ...

  5. Linux时间子系统之(十七):ARM generic timer驱动代码分析

    专题文档汇总目录 Notes:ARM平台Clock/Timer架构:System counter.Timer以及两者之间关系:Per cpu timer通过CP15访问,System counter通 ...

  6. TI 开发板安装USB转串口驱动

    使用TI开发板的时候,USB转串口驱动没有,显示,无法识别设备.搜了好久才搜到相关驱动. 做个记录. 链接: https://pan.baidu.com/s/1ZT5zzVcU727jrYacKVoT ...

  7. STM32 USB转串口驱动安装不成功出现黄色感叹号解决方法!

    相信很多人在做USB转串口时出现过串口驱动安装不成功,出现黄色感叹号问题, 出现这种问题一般是驱动安装不成功造成的. 这里我就这个问题总结几个简单的方法. 方法1: 插上USB,利用驱动人生安装驱动. ...

  8. debian下使用dynamic printk分析usb转串口驱动执行流程

    看了一篇文章<debug by printing>,文中提到了多种通过printk来调试驱动的方法,其中最有用的就是"Dynamic debugging". “Dyna ...

  9. linux下usb转串口驱动分析【转】

    转自:http://blog.csdn.net/txxm520/article/details/8934706 首先说一下linux的风格,个人理解 1. linux大小结构体其实是面向对象的方法,( ...

随机推荐

  1. java重定向

    package com.sn.servlet; import java.io.IOException; import javax.servlet.ServletException; import ja ...

  2. Java对正则表达式的支持(二)

    正则表达式的主要用途: a.在目标字符串中找出匹配正则表达式的部分 b.校验目标字符串是否符合正则表达式,例如校验邮箱地址 c.在目标字符串中替换符合正则表达式的部分为其他的字符串 Scanner类是 ...

  3. Python基础篇(九)

    Key Words: 文件迭代器,标准输入,GUI工具包,数据库操作SQLlite,socket编程 文件迭代器 >>> f= open("some.txt",& ...

  4. 济南清北学堂游记 Day 4.

    不知不觉,行程已经过半了啊. 基本上已经熟悉了这里的环境,这其实也意味着我应该很快就要走了. 明天和后天还有最后四场模拟赛,虽然以我的实力拿不到奖,但我也会尽力做好我自己. 我大概反思了一下这几天,其 ...

  5. 夏令营讲课内容整理 Day 5.

    DP专场.. 动态规划是运筹学的一个分支, 求解决策过程最优化的数学方法. 我们一般把动态规划简称为DP(Dynamic Programming)   1.动态规划的背包问题 有一个容量为m的背包,有 ...

  6. 自己动手实践 spring retry 重试框架

    前序 马上过年了,预祝大家,新年快乐,少写bug 什么是spring retry? spring retry是从spring batch独立出来的一个能功能,主要实现了重试和熔断. 什么时候用? 远程 ...

  7. 福建百度seo和推广,关键词排名优化,网络营销推广培训

    福建百度seo和推广,关键词排名优化,网络营销推广培训 福建百度seo和推广,关键词排名优化,网络营销推广培训,那么如何才能够让自己的文章信息被百度收录呢?只要说自己的文章能够被百度收录,那么你的信息 ...

  8. python脚本0b文件处理

    要处理的文件: 此处处理将00的数据干掉. 处理python脚本: dir_fd = open('abc.yuv','rb+') tmp_fd = open('tmp.yuv','wb+') whil ...

  9. PHP实现session对象封装

    <?php class Session { private $db; // 设置数据库变量 private $expiry = 3600; // 设置Session失效时间 public fun ...

  10. CSS预处理器之Less详解

    本文最初发表于博客园,并在GitHub上持续更新前端的系列文章.欢迎在GitHub上关注我,一起入门和进阶前端. 以下是正文. CSS 预处理器 为什么要有 CSS 预处理器 CSS基本上是设计师的工 ...