A series to enable ioctl usbfs in linux-user
-----BEGIN PGP SIGNATURE----- iQIcBAABAgAGBQJbycutAAoJEPMMOL0/L748BOwQAK43yZuxNOMoWA4+jnEFk8Cu rBBZWpkT3MEGrM2KC2VmsVEtZemBgSKSOccZzdIs0rT0kQUdHsANoS9MeAbZtAzR 5AcnUw9Vr9tXRsOooEy4IM4gwsyHKKFsyEpDHisZW1qNL4I66OKyctVTcA3ONyT/ /ozVuY4ldBb/TyaJSGOH5yTJ21O5PTqBL1LJMcFhedEuGScWbVXrDDhswbSC1koe tc2OthMusgjiQnF+PQUMoBTCdLu35hvTjFvb/Utl+abf+iz5BIA+4WKFo6Uqmvte auSIWrXN7oCy1AccHNrHOgotyqopPeJIplnEpu+vBP2NOPPl/V911XC666nEcpYL fLr9iS2f1j+l+QO2BPcna6up0oZDHZ7+ppKBU2fYOcaE+XV13Jx1DClU6T5V87tY NmXolNKBGjF6zjt/Psbd5xBY23wRcQ5zxlyPCE0dcuC2FfXjZN4w/cW3MvfRlDCc g4B/lxoZJehCGAU0yB1W6zbb08ybJoo07C8AjkiPQFuouMXZ9XPNREBunKjXubSg UcYEJxwyn+t4qCtE2pi90XRLGle2d/hwMIiYh61iKnjtQ3JLBup5VfCM7lhEyzNk bsSGxpGHYkAm8vYP8JWjAgijy/u/bmg7oAX1BJ8cBzIAE1L1ICG0M2/rqzuDQ+f3 wcQu19rXuG8FsnOzKjwi =l80Q -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/vivier2/tags/linux-user-for-3.1-pull-request' into staging A series to enable ioctl usbfs in linux-user # gpg: Signature made Fri 19 Oct 2018 13:18:53 BST # gpg: using RSA key F30C38BD3F2FBE3C # gpg: Good signature from "Laurent Vivier <lvivier@redhat.com>" # gpg: aka "Laurent Vivier <laurent@vivier.eu>" # gpg: aka "Laurent Vivier (Red Hat) <lvivier@redhat.com>" # Primary key fingerprint: CD2F 75DD C8E3 A4DC 2E4F 5173 F30C 38BD 3F2F BE3C * remotes/vivier2/tags/linux-user-for-3.1-pull-request: linux-user: Implement special usbfs ioctls. linux-user: Define ordinary usbfs ioctls. linux-user: Check for Linux USBFS in configure Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
99e2487e00
12
configure
vendored
12
configure
vendored
@ -4202,7 +4202,14 @@ if compile_prog "" "" ; then
|
||||
memfd=yes
|
||||
fi
|
||||
|
||||
|
||||
# check for usbfs
|
||||
have_usbfs=no
|
||||
if test "$linux_user" = "yes"; then
|
||||
if check_include linux/usbdevice_fs.h; then
|
||||
have_usbfs=yes
|
||||
fi
|
||||
have_usbfs=yes
|
||||
fi
|
||||
|
||||
# check for fallocate
|
||||
fallocate=no
|
||||
@ -6325,6 +6332,9 @@ fi
|
||||
if test "$memfd" = "yes" ; then
|
||||
echo "CONFIG_MEMFD=y" >> $config_host_mak
|
||||
fi
|
||||
if test "$have_usbfs" = "yes" ; then
|
||||
echo "CONFIG_USBFS=y" >> $config_host_mak
|
||||
fi
|
||||
if test "$fallocate" = "yes" ; then
|
||||
echo "CONFIG_FALLOCATE=y" >> $config_host_mak
|
||||
fi
|
||||
|
@ -131,6 +131,52 @@
|
||||
IOCTL(FS_IOC_GETFLAGS, IOC_R, MK_PTR(TYPE_INT))
|
||||
IOCTL(FS_IOC_SETFLAGS, IOC_W, MK_PTR(TYPE_INT))
|
||||
|
||||
#ifdef CONFIG_USBFS
|
||||
/* USB ioctls */
|
||||
IOCTL(USBDEVFS_CONTROL, IOC_RW,
|
||||
MK_PTR(MK_STRUCT(STRUCT_usbdevfs_ctrltransfer)))
|
||||
IOCTL(USBDEVFS_BULK, IOC_RW,
|
||||
MK_PTR(MK_STRUCT(STRUCT_usbdevfs_bulktransfer)))
|
||||
IOCTL(USBDEVFS_RESETEP, IOC_W, MK_PTR(TYPE_INT))
|
||||
IOCTL(USBDEVFS_SETINTERFACE, IOC_W,
|
||||
MK_PTR(MK_STRUCT(STRUCT_usbdevfs_setinterface)))
|
||||
IOCTL(USBDEVFS_SETCONFIGURATION, IOC_W, MK_PTR(TYPE_INT))
|
||||
IOCTL(USBDEVFS_GETDRIVER, IOC_R,
|
||||
MK_PTR(MK_STRUCT(STRUCT_usbdevfs_getdriver)))
|
||||
IOCTL_SPECIAL(USBDEVFS_SUBMITURB, IOC_W, do_ioctl_usbdevfs_submiturb,
|
||||
MK_PTR(MK_STRUCT(STRUCT_usbdevfs_urb)))
|
||||
IOCTL_SPECIAL(USBDEVFS_DISCARDURB, IOC_RW, do_ioctl_usbdevfs_discardurb,
|
||||
MK_PTR(MK_STRUCT(STRUCT_usbdevfs_urb)))
|
||||
IOCTL_SPECIAL(USBDEVFS_REAPURB, IOC_R, do_ioctl_usbdevfs_reapurb,
|
||||
MK_PTR(TYPE_PTRVOID))
|
||||
IOCTL_SPECIAL(USBDEVFS_REAPURBNDELAY, IOC_R, do_ioctl_usbdevfs_reapurb,
|
||||
MK_PTR(TYPE_PTRVOID))
|
||||
IOCTL(USBDEVFS_DISCSIGNAL, IOC_W,
|
||||
MK_PTR(MK_STRUCT(STRUCT_usbdevfs_disconnectsignal)))
|
||||
IOCTL(USBDEVFS_CLAIMINTERFACE, IOC_W, MK_PTR(TYPE_INT))
|
||||
IOCTL(USBDEVFS_RELEASEINTERFACE, IOC_W, MK_PTR(TYPE_INT))
|
||||
IOCTL(USBDEVFS_CONNECTINFO, IOC_R,
|
||||
MK_PTR(MK_STRUCT(STRUCT_usbdevfs_connectinfo)))
|
||||
IOCTL(USBDEVFS_IOCTL, IOC_RW, MK_PTR(MK_STRUCT(STRUCT_usbdevfs_ioctl)))
|
||||
IOCTL(USBDEVFS_HUB_PORTINFO, IOC_R,
|
||||
MK_PTR(MK_STRUCT(STRUCT_usbdevfs_hub_portinfo)))
|
||||
IOCTL(USBDEVFS_RESET, 0, TYPE_NULL)
|
||||
IOCTL(USBDEVFS_CLEAR_HALT, IOC_W, MK_PTR(TYPE_INT))
|
||||
IOCTL(USBDEVFS_DISCONNECT, 0, TYPE_NULL)
|
||||
IOCTL(USBDEVFS_CONNECT, 0, TYPE_NULL)
|
||||
IOCTL(USBDEVFS_CLAIM_PORT, IOC_W, MK_PTR(TYPE_INT))
|
||||
IOCTL(USBDEVFS_RELEASE_PORT, IOC_W, MK_PTR(TYPE_INT))
|
||||
IOCTL(USBDEVFS_GET_CAPABILITIES, IOC_R, MK_PTR(TYPE_INT))
|
||||
IOCTL(USBDEVFS_DISCONNECT_CLAIM, IOC_W,
|
||||
MK_PTR(MK_STRUCT(STRUCT_usbdevfs_disconnect_claim)))
|
||||
#ifdef USBDEVFS_DROP_PRIVILEGES
|
||||
IOCTL(USBDEVFS_DROP_PRIVILEGES, IOC_W, MK_PTR(TYPE_INT))
|
||||
#endif
|
||||
#ifdef USBDEVFS_GET_SPEED
|
||||
IOCTL(USBDEVFS_GET_SPEED, 0, TYPE_NULL)
|
||||
#endif
|
||||
#endif /* CONFIG_USBFS */
|
||||
|
||||
IOCTL(SIOCATMARK, IOC_R, MK_PTR(TYPE_INT))
|
||||
IOCTL(SIOCGIFNAME, IOC_RW, MK_PTR(TYPE_INT))
|
||||
IOCTL(SIOCGIFFLAGS, IOC_W | IOC_R, MK_PTR(MK_STRUCT(STRUCT_short_ifreq)))
|
||||
|
@ -94,6 +94,10 @@
|
||||
#include <linux/fiemap.h>
|
||||
#endif
|
||||
#include <linux/fb.h>
|
||||
#if defined(CONFIG_USBFS)
|
||||
#include <linux/usbdevice_fs.h>
|
||||
#include <linux/usb/ch9.h>
|
||||
#endif
|
||||
#include <linux/vt.h>
|
||||
#include <linux/dm-ioctl.h>
|
||||
#include <linux/reboot.h>
|
||||
@ -4196,6 +4200,182 @@ static abi_long do_ioctl_ifconf(const IOCTLEntry *ie, uint8_t *buf_temp,
|
||||
return ret;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_USBFS)
|
||||
#if HOST_LONG_BITS > 64
|
||||
#error USBDEVFS thunks do not support >64 bit hosts yet.
|
||||
#endif
|
||||
struct live_urb {
|
||||
uint64_t target_urb_adr;
|
||||
uint64_t target_buf_adr;
|
||||
char *target_buf_ptr;
|
||||
struct usbdevfs_urb host_urb;
|
||||
};
|
||||
|
||||
static GHashTable *usbdevfs_urb_hashtable(void)
|
||||
{
|
||||
static GHashTable *urb_hashtable;
|
||||
|
||||
if (!urb_hashtable) {
|
||||
urb_hashtable = g_hash_table_new(g_int64_hash, g_int64_equal);
|
||||
}
|
||||
return urb_hashtable;
|
||||
}
|
||||
|
||||
static void urb_hashtable_insert(struct live_urb *urb)
|
||||
{
|
||||
GHashTable *urb_hashtable = usbdevfs_urb_hashtable();
|
||||
g_hash_table_insert(urb_hashtable, urb, urb);
|
||||
}
|
||||
|
||||
static struct live_urb *urb_hashtable_lookup(uint64_t target_urb_adr)
|
||||
{
|
||||
GHashTable *urb_hashtable = usbdevfs_urb_hashtable();
|
||||
return g_hash_table_lookup(urb_hashtable, &target_urb_adr);
|
||||
}
|
||||
|
||||
static void urb_hashtable_remove(struct live_urb *urb)
|
||||
{
|
||||
GHashTable *urb_hashtable = usbdevfs_urb_hashtable();
|
||||
g_hash_table_remove(urb_hashtable, urb);
|
||||
}
|
||||
|
||||
static abi_long
|
||||
do_ioctl_usbdevfs_reapurb(const IOCTLEntry *ie, uint8_t *buf_temp,
|
||||
int fd, int cmd, abi_long arg)
|
||||
{
|
||||
const argtype usbfsurb_arg_type[] = { MK_STRUCT(STRUCT_usbdevfs_urb) };
|
||||
const argtype ptrvoid_arg_type[] = { TYPE_PTRVOID, 0, 0 };
|
||||
struct live_urb *lurb;
|
||||
void *argptr;
|
||||
uint64_t hurb;
|
||||
int target_size;
|
||||
uintptr_t target_urb_adr;
|
||||
abi_long ret;
|
||||
|
||||
target_size = thunk_type_size(usbfsurb_arg_type, THUNK_TARGET);
|
||||
|
||||
memset(buf_temp, 0, sizeof(uint64_t));
|
||||
ret = get_errno(safe_ioctl(fd, ie->host_cmd, buf_temp));
|
||||
if (is_error(ret)) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
memcpy(&hurb, buf_temp, sizeof(uint64_t));
|
||||
lurb = (void *)((uintptr_t)hurb - offsetof(struct live_urb, host_urb));
|
||||
if (!lurb->target_urb_adr) {
|
||||
return -TARGET_EFAULT;
|
||||
}
|
||||
urb_hashtable_remove(lurb);
|
||||
unlock_user(lurb->target_buf_ptr, lurb->target_buf_adr,
|
||||
lurb->host_urb.buffer_length);
|
||||
lurb->target_buf_ptr = NULL;
|
||||
|
||||
/* restore the guest buffer pointer */
|
||||
lurb->host_urb.buffer = (void *)(uintptr_t)lurb->target_buf_adr;
|
||||
|
||||
/* update the guest urb struct */
|
||||
argptr = lock_user(VERIFY_WRITE, lurb->target_urb_adr, target_size, 0);
|
||||
if (!argptr) {
|
||||
g_free(lurb);
|
||||
return -TARGET_EFAULT;
|
||||
}
|
||||
thunk_convert(argptr, &lurb->host_urb, usbfsurb_arg_type, THUNK_TARGET);
|
||||
unlock_user(argptr, lurb->target_urb_adr, target_size);
|
||||
|
||||
target_size = thunk_type_size(ptrvoid_arg_type, THUNK_TARGET);
|
||||
/* write back the urb handle */
|
||||
argptr = lock_user(VERIFY_WRITE, arg, target_size, 0);
|
||||
if (!argptr) {
|
||||
g_free(lurb);
|
||||
return -TARGET_EFAULT;
|
||||
}
|
||||
|
||||
/* GHashTable uses 64-bit keys but thunk_convert expects uintptr_t */
|
||||
target_urb_adr = lurb->target_urb_adr;
|
||||
thunk_convert(argptr, &target_urb_adr, ptrvoid_arg_type, THUNK_TARGET);
|
||||
unlock_user(argptr, arg, target_size);
|
||||
|
||||
g_free(lurb);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static abi_long
|
||||
do_ioctl_usbdevfs_discardurb(const IOCTLEntry *ie,
|
||||
uint8_t *buf_temp __attribute__((unused)),
|
||||
int fd, int cmd, abi_long arg)
|
||||
{
|
||||
struct live_urb *lurb;
|
||||
|
||||
/* map target address back to host URB with metadata. */
|
||||
lurb = urb_hashtable_lookup(arg);
|
||||
if (!lurb) {
|
||||
return -TARGET_EFAULT;
|
||||
}
|
||||
return get_errno(safe_ioctl(fd, ie->host_cmd, &lurb->host_urb));
|
||||
}
|
||||
|
||||
static abi_long
|
||||
do_ioctl_usbdevfs_submiturb(const IOCTLEntry *ie, uint8_t *buf_temp,
|
||||
int fd, int cmd, abi_long arg)
|
||||
{
|
||||
const argtype *arg_type = ie->arg_type;
|
||||
int target_size;
|
||||
abi_long ret;
|
||||
void *argptr;
|
||||
int rw_dir;
|
||||
struct live_urb *lurb;
|
||||
|
||||
/*
|
||||
* each submitted URB needs to map to a unique ID for the
|
||||
* kernel, and that unique ID needs to be a pointer to
|
||||
* host memory. hence, we need to malloc for each URB.
|
||||
* isochronous transfers have a variable length struct.
|
||||
*/
|
||||
arg_type++;
|
||||
target_size = thunk_type_size(arg_type, THUNK_TARGET);
|
||||
|
||||
/* construct host copy of urb and metadata */
|
||||
lurb = g_try_malloc0(sizeof(struct live_urb));
|
||||
if (!lurb) {
|
||||
return -TARGET_ENOMEM;
|
||||
}
|
||||
|
||||
argptr = lock_user(VERIFY_READ, arg, target_size, 1);
|
||||
if (!argptr) {
|
||||
g_free(lurb);
|
||||
return -TARGET_EFAULT;
|
||||
}
|
||||
thunk_convert(&lurb->host_urb, argptr, arg_type, THUNK_HOST);
|
||||
unlock_user(argptr, arg, 0);
|
||||
|
||||
lurb->target_urb_adr = arg;
|
||||
lurb->target_buf_adr = (uintptr_t)lurb->host_urb.buffer;
|
||||
|
||||
/* buffer space used depends on endpoint type so lock the entire buffer */
|
||||
/* control type urbs should check the buffer contents for true direction */
|
||||
rw_dir = lurb->host_urb.endpoint & USB_DIR_IN ? VERIFY_WRITE : VERIFY_READ;
|
||||
lurb->target_buf_ptr = lock_user(rw_dir, lurb->target_buf_adr,
|
||||
lurb->host_urb.buffer_length, 1);
|
||||
if (lurb->target_buf_ptr == NULL) {
|
||||
g_free(lurb);
|
||||
return -TARGET_EFAULT;
|
||||
}
|
||||
|
||||
/* update buffer pointer in host copy */
|
||||
lurb->host_urb.buffer = lurb->target_buf_ptr;
|
||||
|
||||
ret = get_errno(safe_ioctl(fd, ie->host_cmd, &lurb->host_urb));
|
||||
if (is_error(ret)) {
|
||||
unlock_user(lurb->target_buf_ptr, lurb->target_buf_adr, 0);
|
||||
g_free(lurb);
|
||||
} else {
|
||||
urb_hashtable_insert(lurb);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif /* CONFIG_USBFS */
|
||||
|
||||
static abi_long do_ioctl_dm(const IOCTLEntry *ie, uint8_t *buf_temp, int fd,
|
||||
int cmd, abi_long arg)
|
||||
{
|
||||
|
@ -863,6 +863,34 @@ struct target_pollfd {
|
||||
|
||||
#define TARGET_FS_IOC_FIEMAP TARGET_IOWR('f',11,struct fiemap)
|
||||
|
||||
/* usb ioctls */
|
||||
#define TARGET_USBDEVFS_CONTROL TARGET_IOWRU('U', 0)
|
||||
#define TARGET_USBDEVFS_BULK TARGET_IOWRU('U', 2)
|
||||
#define TARGET_USBDEVFS_RESETEP TARGET_IORU('U', 3)
|
||||
#define TARGET_USBDEVFS_SETINTERFACE TARGET_IORU('U', 4)
|
||||
#define TARGET_USBDEVFS_SETCONFIGURATION TARGET_IORU('U', 5)
|
||||
#define TARGET_USBDEVFS_GETDRIVER TARGET_IOWU('U', 8)
|
||||
#define TARGET_USBDEVFS_SUBMITURB TARGET_IORU('U', 10)
|
||||
#define TARGET_USBDEVFS_DISCARDURB TARGET_IO('U', 11)
|
||||
#define TARGET_USBDEVFS_REAPURB TARGET_IOWU('U', 12)
|
||||
#define TARGET_USBDEVFS_REAPURBNDELAY TARGET_IOWU('U', 13)
|
||||
#define TARGET_USBDEVFS_DISCSIGNAL TARGET_IORU('U', 14)
|
||||
#define TARGET_USBDEVFS_CLAIMINTERFACE TARGET_IORU('U', 15)
|
||||
#define TARGET_USBDEVFS_RELEASEINTERFACE TARGET_IORU('U', 16)
|
||||
#define TARGET_USBDEVFS_CONNECTINFO TARGET_IOWU('U', 17)
|
||||
#define TARGET_USBDEVFS_IOCTL TARGET_IOWRU('U', 18)
|
||||
#define TARGET_USBDEVFS_HUB_PORTINFO TARGET_IORU('U', 19)
|
||||
#define TARGET_USBDEVFS_RESET TARGET_IO('U', 20)
|
||||
#define TARGET_USBDEVFS_CLEAR_HALT TARGET_IORU('U', 21)
|
||||
#define TARGET_USBDEVFS_DISCONNECT TARGET_IO('U', 22)
|
||||
#define TARGET_USBDEVFS_CONNECT TARGET_IO('U', 23)
|
||||
#define TARGET_USBDEVFS_CLAIM_PORT TARGET_IORU('U', 24)
|
||||
#define TARGET_USBDEVFS_RELEASE_PORT TARGET_IORU('U', 25)
|
||||
#define TARGET_USBDEVFS_GET_CAPABILITIES TARGET_IORU('U', 26)
|
||||
#define TARGET_USBDEVFS_DISCONNECT_CLAIM TARGET_IORU('U', 27)
|
||||
#define TARGET_USBDEVFS_DROP_PRIVILEGES TARGET_IOWU('U', 30)
|
||||
#define TARGET_USBDEVFS_GET_SPEED TARGET_IO('U', 31)
|
||||
|
||||
/* cdrom commands */
|
||||
#define TARGET_CDROMPAUSE 0x5301 /* Pause Audio Operation */
|
||||
#define TARGET_CDROMRESUME 0x5302 /* Resume paused Audio Operation */
|
||||
|
@ -266,3 +266,71 @@ STRUCT(blkpg_ioctl_arg,
|
||||
TYPE_INT, /* flags */
|
||||
TYPE_INT, /* datalen */
|
||||
TYPE_PTRVOID) /* data */
|
||||
|
||||
#if defined(CONFIG_USBFS)
|
||||
/* usb device ioctls */
|
||||
STRUCT(usbdevfs_ctrltransfer,
|
||||
TYPE_CHAR, /* bRequestType */
|
||||
TYPE_CHAR, /* bRequest */
|
||||
TYPE_SHORT, /* wValue */
|
||||
TYPE_SHORT, /* wIndex */
|
||||
TYPE_SHORT, /* wLength */
|
||||
TYPE_INT, /* timeout */
|
||||
TYPE_PTRVOID) /* data */
|
||||
|
||||
STRUCT(usbdevfs_bulktransfer,
|
||||
TYPE_INT, /* ep */
|
||||
TYPE_INT, /* len */
|
||||
TYPE_INT, /* timeout */
|
||||
TYPE_PTRVOID) /* data */
|
||||
|
||||
STRUCT(usbdevfs_setinterface,
|
||||
TYPE_INT, /* interface */
|
||||
TYPE_INT) /* altsetting */
|
||||
|
||||
STRUCT(usbdevfs_disconnectsignal,
|
||||
TYPE_INT, /* signr */
|
||||
TYPE_PTRVOID) /* context */
|
||||
|
||||
STRUCT(usbdevfs_getdriver,
|
||||
TYPE_INT, /* interface */
|
||||
MK_ARRAY(TYPE_CHAR, USBDEVFS_MAXDRIVERNAME + 1)) /* driver */
|
||||
|
||||
STRUCT(usbdevfs_connectinfo,
|
||||
TYPE_INT, /* devnum */
|
||||
TYPE_CHAR) /* slow */
|
||||
|
||||
STRUCT(usbdevfs_iso_packet_desc,
|
||||
TYPE_INT, /* length */
|
||||
TYPE_INT, /* actual_length */
|
||||
TYPE_INT) /* status */
|
||||
|
||||
STRUCT(usbdevfs_urb,
|
||||
TYPE_CHAR, /* type */
|
||||
TYPE_CHAR, /* endpoint */
|
||||
TYPE_INT, /* status */
|
||||
TYPE_INT, /* flags */
|
||||
TYPE_PTRVOID, /* buffer */
|
||||
TYPE_INT, /* buffer_length */
|
||||
TYPE_INT, /* actual_length */
|
||||
TYPE_INT, /* start_frame */
|
||||
TYPE_INT, /* union number_of_packets stream_id */
|
||||
TYPE_INT, /* error_count */
|
||||
TYPE_INT, /* signr */
|
||||
TYPE_PTRVOID, /* usercontext */
|
||||
MK_ARRAY(MK_STRUCT(STRUCT_usbdevfs_iso_packet_desc), 0)) /* desc */
|
||||
|
||||
STRUCT(usbdevfs_ioctl,
|
||||
TYPE_INT, /* ifno */
|
||||
TYPE_INT, /* ioctl_code */
|
||||
TYPE_PTRVOID) /* data */
|
||||
|
||||
STRUCT(usbdevfs_hub_portinfo,
|
||||
TYPE_CHAR, /* nports */
|
||||
MK_ARRAY(TYPE_CHAR, 127)) /* port */
|
||||
|
||||
STRUCT(usbdevfs_disconnect_claim,
|
||||
TYPE_INT, /* interface */
|
||||
TYPE_INT, /* flags */
|
||||
MK_ARRAY(TYPE_CHAR, USBDEVFS_MAXDRIVERNAME + 1)) /* driver */
|
||||
#endif /* CONFIG_USBFS */
|
||||
|
Loading…
Reference in New Issue
Block a user