USB应用程序调用驱..

这是一个很简单的例子, 大家不要见笑,给入门者一点启发吧!!#include <windows.h>
#include <conio.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <time.h>

#include <devioctl.h>
#include <winioctl.h>

#include <setupapi.h>
#include <basetyps.h>
#include "..Ioctl.h"
#include "..GuidUDK.h"

ddk的例子bulkusb里有


#include <usbdi.h>


#define NOISY(_x_) printf _x_ ;
#define BUF_SIZE (32)
#define DATA_SIZE (10*1024*1024)

char inPipe[32] = "PIPE00"; // pipe name for bulk input pipe on our test board
char outPipe[32] = "PIPE01"; // pipe name for bulk output pipe on our test board
char completeDeviceName[256] = ""; //generated from the GUID registered by the driver itself

BOOL fDumpUsbConfig = FALSE; // flags set in response to console command line switches
BOOL fDumpReadData = FALSE;
BOOL fRead = FALSE;
BOOL fWrite = FALSE;


int gDebugLevel = 1; // higher == more verbose, default is 1, 0 turns off all

ULONG IterationCount = 1; //count of iterations of the test we are to perform
int WriteLen = 0; // #bytes to write
int ReadLen = 0; // #bytes to read

// functions


HANDLE
OpenOneDevice (
IN HDEVINFO HardwareDeviceInfo,
IN PSP_INTERFACE_DEVICE_DATA DeviceInfoData,
IN char *devName
)
/*++
Routine Description:

Given the HardwareDeviceInfo, representing a handle to the plug and
play information, and deviceInfoData, representing a specific usb device,
open that device and fill in all the relevant information in the given
USB_DEVICE_DESCRIPTOR structure.

Arguments:

HardwareDeviceInfo: handle to info obtained from Pnp mgr via SetupDiGetClassDevs()
DeviceInfoData: ptr to info obtained via SetupDiEnumInterfaceDevice()

Return Value:

return HANDLE if the open and initialization was successfull,
else INVLAID_HANDLE_VALUE.

--*/
{
PSP_INTERFACE_DEVICE_DETAIL_DATA functionClassDeviceData = NULL;
ULONG predictedLength = 0;
ULONG requiredLength = 0;
HANDLE hOut = INVALID_HANDLE_VALUE;

//
// allocate a function class device data structure to receive the
// goods about this particular device.
//
SetupDiGetDeviceInterfaceDetail (
HardwareDeviceInfo,
DeviceInfoData,
NULL, // probing so no output buffer yet
0, // probing so output buffer length of zero
&requiredLength,
NULL); // not interested in the specific dev-node


predictedLength = requiredLength;
// sizeof (SP_FNCLASS_DEVICE_DATA) + 512;

functionClassDeviceData = malloc (predictedLength);
functionClassDeviceData->cbSize = sizeof (SP_INTERFACE_DEVICE_DETAIL_DATA);

//
// Retrieve the information from Plug and Play.
//
if (! SetupDiGetDeviceInterfaceDetail (
HardwareDeviceInfo,
DeviceInfoData,
functionClassDeviceData,
predictedLength,
&requiredLength,
NULL)) {
free( functionClassDeviceData );
return INVALID_HANDLE_VALUE;
}

strcpy( devName,functionClassDeviceData->DevicePath) ;
printf( "Attempting to open %sn", devName );

hOut = CreateFile (
functionClassDeviceData->DevicePath,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, // no SECURITY_ATTRIBUTES structure
OPEN_EXISTING, // No special create flags
0, // No special attributes
NULL); // No template file

if (INVALID_HANDLE_VALUE == hOut) {
printf( "FAILED to open %sn", devName );
}
free( functionClassDeviceData );
return hOut;
}


HANDLE
OpenUsbDevice( LPGUID pGuid, char *outNameBuf)
/*++
Routine Description:

Do the required PnP things in order to find
the next available proper device in the system at this time.

Arguments:

pGuid: ptr to GUID registered by the driver itself
outNameBuf: the generated name for this device

Return Value:

return HANDLE if the open and initialization was successful,
else INVLAID_HANDLE_VALUE.
--*/
{
ULONG NumberDevices;
HANDLE hOut = INVALID_HANDLE_VALUE;
HDEVINFO hardwareDeviceInfo;
SP_INTERFACE_DEVICE_DATA deviceInfoData;
ULONG i;
BOOLEAN done;
PUSB_DEVICE_DESCRIPTOR usbDeviceInst;
PUSB_DEVICE_DESCRIPTOR *UsbDevices = &usbDeviceInst;

*UsbDevices = NULL;
NumberDevices = 0;

//
// Open a handle to the plug and play dev node.
// SetupDiGetClassDevs() returns a device information set that contains info on all
// installed devices of a specified class.
//
printf("entering the open_usb_device()n");

hardwareDeviceInfo = SetupDiGetClassDevs (
pGuid,
NULL, // Define no enumerator (global)
NULL, // Define no
(DIGCF_PRESENT | // Only Devices present
DIGCF_INTERFACEDEVICE)); // Function class devices.

//
// Take a wild guess at the number of devices we have;
// Be prepared to realloc and retry if there are more than we guessed
//
NumberDevices = 4;
done = FALSE;
deviceInfoData.cbSize = sizeof (SP_INTERFACE_DEVICE_DATA);

i=0;
while (!done) {
NumberDevices *= 2;

if (*UsbDevices) {
*UsbDevices =
realloc (*UsbDevices, (NumberDevices * sizeof (USB_DEVICE_DESCRIPTOR)));
} else {
*UsbDevices = calloc (NumberDevices, sizeof (USB_DEVICE_DESCRIPTOR));
}

if (NULL == *UsbDevices) {

// SetupDiDestroyDeviceInfoList destroys a device information set
// and frees all associated memory.

SetupDiDestroyDeviceInfoList (hardwareDeviceInfo);
return INVALID_HANDLE_VALUE;
}

usbDeviceInst = *UsbDevices + i;

for (; i < NumberDevices; i++) {

// SetupDiEnumDeviceInterfaces() returns information about device interfaces
// exposed by one or more devices. Each call returns information about one interface;
// the routine can be called repeatedly to get information about several interfaces
// exposed by one or more devices.

if (SetupDiEnumDeviceInterfaces (hardwareDeviceInfo,
0, // We don't care about specific PDOs
pGuid,
i,
&deviceInfoData)) {
printf("entering the openonedevice:n");
hOut = OpenOneDevice (hardwareDeviceInfo, &deviceInfoData, outNameBuf);
if ( hOut != INVALID_HANDLE_VALUE ) {
printf("openonedevice is success!!!n");
done = TRUE;
break;
}
} else {
if (ERROR_NO_MORE_ITEMS == GetLastError()) {
printf("openonedevice is error!!!n");
done = TRUE;
break;
}
}
}
}

NumberDevices = i;

// SetupDiDestroyDeviceInfoList() destroys a device information set
// and frees all associated memory.

SetupDiDestroyDeviceInfoList (hardwareDeviceInfo);
free ( *UsbDevices );
return hOut;
}

 


BOOL
GetUsbDeviceFileName( LPGUID pGuid, char *outNameBuf)
/*++
Routine Description:

Given a ptr to a driver-registered GUID, give us a string with the device name
that can be used in a CreateFile() call.
Actually briefly opens and closes the device and sets outBuf if successfull;
returns FALSE if not

Arguments:

pGuid: ptr to GUID registered by the driver itself
outNameBuf: the generated zero-terminated name for this device

Return Value:

TRUE on success else FALSE

--*/
{
HANDLE hDev = OpenUsbDevice( pGuid, outNameBuf );
if ( hDev != INVALID_HANDLE_VALUE )
{
CloseHandle( hDev );
return TRUE;
}
return FALSE;

}

HANDLE
open_dev()
/*++
Routine Description:

Called by dumpUsbConfig() to open an instance of our device

Arguments:

None

Return Value:

Device handle on success else NULL

--*/
{

HANDLE hDEV = OpenUsbDevice( (LPGUID)&USB_HD_DRIVER, completeDeviceName);


if (hDEV == INVALID_HANDLE_VALUE) {
printf("Failed to open (%s) = %d", completeDeviceName, GetLastError());
} else {
printf("DeviceName = (%s)n", completeDeviceName);
}

return hDEV;
}


HANDLE
open_file( char *filename)
/*++
Routine Description:

Called by main() to open an instance of our device after obtaining its name

Arguments:

None

Return Value:

Device handle on success else NULL

--*/
{

int success = 1;
HANDLE h;

if ( !GetUsbDeviceFileName(
(LPGUID) &USB_HD_DRIVER,
completeDeviceName) )
{
NOISY(("Failed to GetUsbDeviceFileNamen", GetLastError()));
return INVALID_HANDLE_VALUE;
}

strcat (completeDeviceName,
"/"
);

strcat (completeDeviceName,
filename
);

printf("completeDeviceName = (%s)n", completeDeviceName);

h = CreateFile(completeDeviceName,
GENERIC_WRITE | GENERIC_READ,
FILE_SHARE_WRITE | FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
0,
NULL);

if (h == INVALID_HANDLE_VALUE) {
NOISY(("Failed to open (%s) = %d", completeDeviceName, GetLastError()));
success = 0;
} else {
NOISY(("Opened successfully.n"));
}

return h;
}

void
usage()
/*++
Routine Description:

Called by main() to dump usage info to the console when
the app is called with no parms or with an invalid parm

Arguments:

None

Return Value:

None

--*/
{
static int i=1;

if (i) {
printf("Usage for Read/Write test:n");
// printf("-r [n] where n is number of bytes to readn");
// printf("-w [n] where n is number of bytes to writen");
// printf("-c [n] where n is number of iterations (default = 1)n");
// printf("-i [s] where s is the input pipen");
// printf("-o [s] where s is the output pipen");
// printf("-v verbose -- dumps read datan");
printf("-r read the file test.vn");
printf("-w write the file test.v to disk n");
printf("nUsage for USB and Endpoint info:n");
printf("-u to dump USB configuration and pipe info n");
i = 0;
}
}


void
parse(
int argc,
char *argv[] )
/*++
Routine Description:
Called by main() to parse command line parms
Arguments:
argc and argv that was passed to main()
Return Value:
Sets global flags as per user function request
--*/
{
int i;

if ( argc != 2 ) // give usage if invoked with no parms
usage();

for (i=0; i<argc; i++) {
if (argv[0] == '-' ||
argv[0] == '/') {
switch(argv[1]) {
case 'r':
case 'R':
//ReadLen = atoi(&argv[i+1][0]);
fRead = TRUE;
i++;
break;
case 'w':
case 'W':
// WriteLen = atoi(&argv[i+1][0]);
fWrite = TRUE;

i++;
break;
case 'c':
case 'C':
IterationCount = atoi(&argv[i+1][0]);
i++;
break;
case 'i':
case 'I':
strcpy(inPipe, &argv[i+1][0]);
i++;
break;
case 'u':
case 'U':
fDumpUsbConfig = TRUE;
i++;
break;
case 'v':
case 'V':
fDumpReadData = TRUE;
i++;
break;
case 'o':
case 'O':
strcpy(outPipe, &argv[i+1][0]);
i++;
break;
default:
usage();
}
}
}
}

BOOL
compare_buffs(char *buff1, char *buff2, int length)
/*++
Routine Description:

Called to verify read and write buffers match for loopback test

Arguments:

buffers to compare and length

Return Value:

TRUE if buffers match, else FALSE

--*/
{
int ok = 1;

if (memcmp(buff1, buff2, length )) {

// Edi, and Esi point to the mismatching char and ecx indicates the
// remaining length.
ok = 0;
}


return ok;
}

#define NPERLN 8

void
dump(
UCHAR *b,
int len
)
/*++
Routine Description:

Called to do formatted ascii dump to console of the io buffer

Arguments:

buffer and length

Return Value:

none

--*/
{
ULONG i;
ULONG longLen = (ULONG)len / sizeof( ULONG );
PULONG pBuf = (PULONG) b;

// dump an ordinal ULONG for each sizeof(ULONG)'th byte
printf("n****** BEGIN DUMP LEN decimal %d, 0x%xn", len,len);
for (i=0; i<longLen; i++) {
printf("%04X ", *pBuf++);
if (i % NPERLN == (NPERLN - 1)) {
printf("n");
}
}
if (i % NPERLN != 0) {
printf("n");
}
printf("n****** END DUMP LEN decimal %d, 0x%xn", len,len);
}

 

 

// Begin, routines for USB configuration dump (Cmdline "rwbulk -u" )


char
*usbDescriptorTypeString(UCHAR bDescriptorType )
/*++
Routine Description:

Called to get ascii string of USB descriptor

Arguments:

PUSB_ENDPOINT_DESCRIPTOR->bDescriptorType or
PUSB_DEVICE_DESCRIPTOR->bDescriptorType or
PUSB_INTERFACE_DESCRIPTOR->bDescriptorType or
PUSB_STRING_DESCRIPTOR->bDescriptorType or
PUSB_POWER_DESCRIPTOR->bDescriptorType or
PUSB_CONFIGURATION_DESCRIPTOR->bDescriptorType

Return Value:

ptr to string

--*/{

switch(bDescriptorType) {

case USB_DEVICE_DESCRIPTOR_TYPE:
return "USB_DEVICE_DESCRIPTOR_TYPE";

case USB_CONFIGURATION_DESCRIPTOR_TYPE:
return "USB_CONFIGURATION_DESCRIPTOR_TYPE";


case USB_STRING_DESCRIPTOR_TYPE:
return "USB_STRING_DESCRIPTOR_TYPE";


case USB_INTERFACE_DESCRIPTOR_TYPE:
return "USB_INTERFACE_DESCRIPTOR_TYPE";


case USB_ENDPOINT_DESCRIPTOR_TYPE:
return "USB_ENDPOINT_DESCRIPTOR_TYPE";


#ifdef USB_POWER_DESCRIPTOR_TYPE // this is the older definintion which is actually obsolete
// workaround for temporary bug in 98ddk, older USB100.h file
case USB_POWER_DESCRIPTOR_TYPE:
return "USB_POWER_DESCRIPTOR_TYPE";
#endif

#ifdef USB_RESERVED_DESCRIPTOR_TYPE // this is the current version of USB100.h as in NT5DDK

case USB_RESERVED_DESCRIPTOR_TYPE:
return "USB_RESERVED_DESCRIPTOR_TYPE";

case USB_CONFIG_POWER_DESCRIPTOR_TYPE:
return "USB_CONFIG_POWER_DESCRIPTOR_TYPE";

case USB_INTERFACE_POWER_DESCRIPTOR_TYPE:
return "USB_INTERFACE_POWER_DESCRIPTOR_TYPE";
#endif // for current nt5ddk version of USB100.h

default:
return "??? UNKNOWN!!";
}
}


char
*usbEndPointTypeString(UCHAR bmAttributes)
/*++
Routine Description:

Called to get ascii string of endpt descriptor type

Arguments:

PUSB_ENDPOINT_DESCRIPTOR->bmAttributes

Return Value:

ptr to string

--*/
{
UINT typ = bmAttributes & USB_ENDPOINT_TYPE_MASK;


switch( typ) {
case USB_ENDPOINT_TYPE_INTERRUPT:
return "USB_ENDPOINT_TYPE_INTERRUPT";

case USB_ENDPOINT_TYPE_BULK:
return "USB_ENDPOINT_TYPE_BULK";

case USB_ENDPOINT_TYPE_ISOCHRONOUS:
return "USB_ENDPOINT_TYPE_ISOCHRONOUS";

case USB_ENDPOINT_TYPE_CONTROL:
return "USB_ENDPOINT_TYPE_CONTROL";

default:
return "??? UNKNOWN!!";
}
}


char
*usbConfigAttributesString(UCHAR bmAttributes)
/*++
Routine Description:

Called to get ascii string of USB_CONFIGURATION_DESCRIPTOR attributes

Arguments:

PUSB_CONFIGURATION_DESCRIPTOR->bmAttributes

Return Value:

ptr to string

--*/
{
UINT typ = bmAttributes & USB_CONFIG_POWERED_MASK;


switch( typ) {

case USB_CONFIG_BUS_POWERED:
return "USB_CONFIG_BUS_POWERED";

case USB_CONFIG_SELF_POWERED:
return "USB_CONFIG_SELF_POWERED";

case USB_CONFIG_REMOTE_WAKEUP:
return "USB_CONFIG_REMOTE_WAKEUP";


default:
return "??? UNKNOWN!!";
}
}


void
print_USB_CONFIGURATION_DESCRIPTOR(PUSB_CONFIGURATION_DESCRIPTOR cd)
/*++
Routine Description:

Called to do formatted ascii dump to console of a USB config descriptor

Arguments:

ptr to USB configuration descriptor

Return Value:

none

--*/
{
printf("n===================nUSB_CONFIGURATION_DESCRIPTORn");

printf(
"bLength = 0x%x, decimal %dn", cd->bLength, cd->bLength
);

printf(
"bDescriptorType = 0x%x ( %s )n", cd->bDescriptorType, usbDescriptorTypeString( cd->bDescriptorType )
);

printf(
"wTotalLength = 0x%x, decimal %dn", cd->wTotalLength, cd->wTotalLength
);

printf(
"bNumInterfaces = 0x%x, decimal %dn", cd->bNumInterfaces, cd->bNumInterfaces
);

printf(
"bConfigurationValue = 0x%x, decimal %dn", cd->bConfigurationValue, cd->bConfigurationValue
);

printf(
"iConfiguration = 0x%x, decimal %dn", cd->iConfiguration, cd->iConfiguration
);

printf(
"bmAttributes = 0x%x ( %s )n", cd->bmAttributes, usbConfigAttributesString( cd->bmAttributes )
);

printf(
"MaxPower = 0x%x, decimal %dn", cd->MaxPower, cd->MaxPower
);
}


void
print_USB_INTERFACE_DESCRIPTOR(PUSB_INTERFACE_DESCRIPTOR id, UINT ix)
/*++
Routine Description:

Called to do formatted ascii dump to console of a USB interface descriptor

Arguments:

ptr to USB interface descriptor

Return Value:

none

--*/
{
printf("n-----------------------------nUSB_INTERFACE_DESCRIPTOR #%dn", ix);


printf(
"bLength = 0x%xn", id->bLength
);


printf(
"bDescriptorType = 0x%x ( %s )n", id->bDescriptorType, usbDescriptorTypeString( id->bDescriptorType )
);


printf(
"bInterfaceNumber = 0x%xn", id->bInterfaceNumber
);
printf(
"bAlternateSetting = 0x%xn", id->bAlternateSetting
);
printf(
"bNumEndpoints = 0x%xn", id->bNumEndpoints
);
printf(
"bInterfaceClass = 0x%xn", id->bInterfaceClass
);
printf(
"bInterfaceSubClass = 0x%xn", id->bInterfaceSubClass
);
printf(
"bInterfaceProtocol = 0x%xn", id->bInterfaceProtocol
);
printf(
"bInterface = 0x%xn", id->iInterface
);
}


void
print_USB_ENDPOINT_DESCRIPTOR(PUSB_ENDPOINT_DESCRIPTOR ed, int i)
/*++
Routine Description:

Called to do formatted ascii dump to console of a USB endpoint descriptor

Arguments:

ptr to USB endpoint descriptor,
index of this endpt in interface desc

Return Value:

none

--*/
{
printf(
"------------------------------nUSB_ENDPOINT_DESCRIPTOR for Pipe%02dn", i
);

printf(
"bLength = 0x%xn", ed->bLength
);

printf(
"bDescriptorType = 0x%x ( %s )n", ed->bDescriptorType, usbDescriptorTypeString( ed->bDescriptorType )
);


if ( USB_ENDPOINT_DIRECTION_IN( ed->bEndpointAddress ) ) {
printf(
"bEndpointAddress= 0x%x ( INPUT )n", ed->bEndpointAddress
);
} else {
printf(
"bEndpointAddress= 0x%x ( OUTPUT )n", ed->bEndpointAddress
);
}

printf(
"bmAttributes= 0x%x ( %s )n", ed->bmAttributes, usbEndPointTypeString ( ed->bmAttributes )
);


printf(
"wMaxPacketSize= 0x%x, decimal %dn", ed->wMaxPacketSize, ed->wMaxPacketSize
);
printf(
"bInterval = 0x%x, decimal %dn", ed->bInterval, ed->bInterval
);
}

void
rw_dev( HANDLE hDEV )
/*++
Routine Description:

Called to do formatted ascii dump to console of USB
configuration, interface, and endpoint descriptors
(Cmdline "rwbulk -u" )

Arguments:

handle to device

Return Value:

none

--*/
{
UINT success;
int siz, nBytes;
char buf[256];
PUSB_CONFIGURATION_DESCRIPTOR cd;
PUSB_INTERFACE_DESCRIPTOR id;
PUSB_ENDPOINT_DESCRIPTOR ed;

siz = sizeof(buf);

if (hDEV == INVALID_HANDLE_VALUE) {
NOISY(("DEV not open"));
return;
}

success = DeviceIoControl(hDEV,
IOCTL_UDK_GET_CONFIG_DESCRIPTOR,
buf,
siz,
buf,
siz,
&nBytes,
NULL);

NOISY(("request complete, success = %d nBytes = %dn", success, nBytes));

if (success) {
ULONG i;
UINT j, n;
char *pch;

pch = buf;
n = 0;

cd = (PUSB_CONFIGURATION_DESCRIPTOR) pch;

print_USB_CONFIGURATION_DESCRIPTOR( cd );

pch += cd->bLength;

do {

id = (PUSB_INTERFACE_DESCRIPTOR) pch;

print_USB_INTERFACE_DESCRIPTOR(id, n++);

pch += id->bLength;
for (j=0; j<id->bNumEndpoints; j++) {

ed = (PUSB_ENDPOINT_DESCRIPTOR) pch;

print_USB_ENDPOINT_DESCRIPTOR(ed,j);

pch += ed->bLength;
}
i = (ULONG)(pch - buf);
} while (i<cd->wTotalLength);

}

return;

}


int dumpUsbConfig()
/*++
Routine Description:

Called to do formatted ascii dump to console of USB
configuration, interface, and endpoint descriptors
(Cmdline "rwbulk -u" )

Arguments:

none

Return Value:

none

--*/
{

HANDLE hDEV = open_dev();

if ( hDEV )
{
rw_dev( hDEV );
CloseHandle(hDEV);
}

return 0;
}
// End, routines for USB configuration and pipe info dump (Cmdline "rwbulk -u" )

void doWrite()
{
char file_name[8];
char file_txt[4];
char cmd[16] ;
char *buf;
ULONG nWritten;
FILE *fp,*fp1;
ULONG n,i,j;
HANDLE hwrite = INVALID_HANDLE_VALUE;

buf = (char *) malloc(BUF_SIZE);
if (buf == NULL)
{
printf("Failed to allocate buffer for write");
return;
//Exit(1);
}

// Write data to driver
printf("Writing to device - ");
cmd[0]=16;
cmd[1]=15;
cmd[2]=0;
cmd[3]=8;
cmd[4]='t';
cmd[5]='x';
cmd[6]='t';
cmd[7]='.';
cmd[8]='t';
cmd[9]='s';
cmd[10] = 'e';
cmd[11] = 't';
cmd[12]=0;
cmd[13]=0;
cmd[14]=192;
cmd[15]=3;
printf("writing the write cmd: n");
n=16;
strcpy(outPipe,"PIPE02");
hwrite = open_file(outPipe);
printf("send the cmd packet??n");
getchar();

WriteFile(hwrite, cmd,n, &nWritten, NULL);


if((fp=fopen("c:/tmp/test.txt","rb"))==NULL)
{printf("can't open this filen");
exit(0);
}

n=BUF_SIZE;
printf(" begin to write the file:n");

strcpy(outPipe,"PIPE01");
hwrite = open_file(outPipe);

printf("please press anykey: n");
getchar();

for (j=0;j<DATA_SIZE/BUF_SIZE;j++)
{
fread(buf,n,1,fp);
WriteFile(hwrite, buf,n, &nWritten, NULL);
/*
//printf (" the j = %d:n",j);
//getchar();
//printf("write the bb1? n");
//getchar();
for (i=0;i<64;i++)
{
fread(buf,n,1,fp);
WriteFile(hwrite, buf,n, &nWritten, NUL

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值