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. Trusted Execution Technology (TXT) --- 启动控制策略(LCP)篇

    版权声明:本文为博主原创文章,未经博主允许不得转载.http://www.cnblogs.com/tsec/p/8428631.html 在TXT平台中,启动控制策略(Launch Control P ...

  2. RAID知识总结[转]

    原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任.http://815632410.blog.51cto.com/1544685/1394306 ...

  3. BZOJ 3168: [Heoi2013]钙铁锌硒维生素 [线性基 Hungary 矩阵求逆]

    3168: [Heoi2013]钙铁锌硒维生素 题意:给一个线性无关组A,再给一个B,要为A中每个向量在B中选一个可以代替的向量,替换后仍然线性无关.判断可行和求字典序最小的解 PoPoQQQ orz ...

  4. BZOJ 2006: [NOI2010]超级钢琴 [ST表+堆 | 主席树]

    题意: 一个序列,求k个不相同的长度属于\([L,R]\)的区间使得和最大 前缀和,对于每个r找最小的a[l] 然后我yy了一个可持久化线段树做法...也许会T 实际上主席树就可以了,区间k小值 然后 ...

  5. laravel5.4+vue+vux+element的环境搭配

    最近因为项目的需要,需要搭配一个这样的环境.之前做过的东西没有这样用过,在网上找了半天不是过于简单就是根本行不通,自己踩了半天的坑,终于搭配成功. 首先下载laravel5.4,直接去官网一键安装包或 ...

  6. css页面布局之左侧定宽,右侧自适应

    二列布局的特征是侧栏固定宽度,主栏自适应宽度.三列布局的特征是两侧两列固定宽度,中间列自适应宽度. 之所以将二列布局和三列布局写在一起,是因为二列布局可以看做去掉一个侧栏的三列布局,其布局的思想有异曲 ...

  7. CentOS 7 安装 Nginx 反向代理 node

    安装 nginx yum install epel-release yum install nginx 配置 nginx sudo vim /etc/nginx/nginx.conf, 改成下面配置: ...

  8. centos7下安装vsftpd

    安装步骤: 创建ftp目录 cd / mkdir ftpfile 创建指定登陆用户并不让他拥有登陆系统的权限(设置指定登陆shell) useradd ftpuser -d /ftpfile/ -s ...

  9. Shiro的原理及Web搭建

    shiro(java安全框架) 以下都是综合之前的人加上自己的一些小总结 Apache Shiro是一个强大且易用的Java安全框架,执行身份验证.授权.密码学和会话管理.使用Shiro的易于理解的A ...

  10. mysql数据库外部无法访问

    有以下两种情况: 1.mysql未分配访问权限 格式:grant 权限 on 数据库名.表名 用户@登录主机 identified by "用户密码"; grant select, ...