mirror of
https://github.com/torvalds/linux.git
synced 2026-07-30 19:21:28 +02:00
Merge "arm64: deconfig: Enable support for QC specific function drivers"
This commit is contained in:
commit
42549563e8
|
|
@ -57,6 +57,13 @@ CONFIG_SM_DISPCC_PINEAPPLE=m
|
|||
CONFIG_SM_GCC_PINEAPPLE=m
|
||||
CONFIG_SM_TCSRCC_PINEAPPLE=m
|
||||
CONFIG_SM_VIDEOCC_PINEAPPLE=m
|
||||
CONFIG_USB_CONFIGFS_F_CCID=m
|
||||
CONFIG_USB_CONFIGFS_F_CDEV=m
|
||||
# CONFIG_USB_CONFIGFS_F_GSI is not set
|
||||
CONFIG_USB_CONFIGFS_F_QDSS=m
|
||||
CONFIG_USB_DWC3_MSM=m
|
||||
CONFIG_USB_F_CCID=m
|
||||
CONFIG_USB_F_CDEV=m
|
||||
CONFIG_USB_F_QDSS=m
|
||||
CONFIG_USB_QCOM_EMU_PHY=m
|
||||
# CONFIG_VIDEO_QCOM_VENUS is not set
|
||||
|
|
|
|||
|
|
@ -222,6 +222,30 @@ config USB_F_ACC
|
|||
config USB_F_AUDIO_SRC
|
||||
tristate
|
||||
|
||||
config USB_F_CDEV
|
||||
tristate
|
||||
|
||||
config USB_F_CCID
|
||||
tristate
|
||||
|
||||
config USB_F_QDSS
|
||||
tristate
|
||||
|
||||
config USB_F_GSI
|
||||
tristate
|
||||
|
||||
config USB_F_FS_IPC_LOGGING
|
||||
tristate "Enable IPC logging for FunctionFS via f_fs_ipc_log"
|
||||
depends on IPC_LOGGING
|
||||
depends on ARM64
|
||||
help
|
||||
Enables additional debug messages for FunctionFS driver with the help
|
||||
of f_fs_ipc_log module and output via IPC Logging mechanism. This can
|
||||
be useful when troubleshooting transfer stalls or other general
|
||||
failures and determine if the issue is in the kernel gadget or the
|
||||
userspace client. Separate IPC log contexts are created for each
|
||||
function instance at mount time.
|
||||
|
||||
# this first set of drivers all depend on bulk-capable hardware.
|
||||
|
||||
config USB_CONFIGFS
|
||||
|
|
@ -517,6 +541,50 @@ config USB_CONFIGFS_F_TCM
|
|||
Both protocols can work on USB2.0 and USB3.0.
|
||||
UAS utilizes the USB 3.0 feature called streams support.
|
||||
|
||||
config USB_CONFIGFS_F_CDEV
|
||||
tristate "USB Serial Character function"
|
||||
select USB_F_CDEV
|
||||
depends on USB_CONFIGFS
|
||||
help
|
||||
The serial character function is a generic function driver that
|
||||
exposes a pair of bulk IN and OUT endpoints which are backed by
|
||||
a character device and mapped to its read/write routines. The
|
||||
function also supports a single interrupt IN endpoint for
|
||||
asynchronous notification to the host. This driver is typically
|
||||
used to support DUN/NMEA functions.
|
||||
|
||||
config USB_CONFIGFS_F_CCID
|
||||
tristate "USB CCID function"
|
||||
select USB_F_CCID
|
||||
depends on USB_CONFIGFS
|
||||
help
|
||||
The Chip Card Interface Device (CCID) function implements a USB
|
||||
interface that exposes a standard CSCID class that consists of a
|
||||
pair of bulk IN and OUT endpoints and a single interrupt IN
|
||||
endpoint. This driver provides a character device interface
|
||||
allowing a userspace component to be able to provide the
|
||||
implementation necessary to interface with the smartcard.
|
||||
|
||||
config USB_CONFIGFS_F_QDSS
|
||||
tristate "USB QDSS function"
|
||||
select USB_F_QDSS
|
||||
depends on USB_CONFIGFS
|
||||
help
|
||||
USB QDSS function driver to get hwtracing related data over
|
||||
USB. USB QDSS function driver which allows communication
|
||||
between USB BAM and QDSS BAM for QDSS debug functionality
|
||||
over USB.
|
||||
|
||||
config USB_CONFIGFS_F_GSI
|
||||
tristate "USB GSI function"
|
||||
select USB_F_GSI
|
||||
depends on USB_CONFIGFS
|
||||
help
|
||||
Generic function driver to support h/w acceleration to IPA
|
||||
over GSI. This driver provides USB RMNET/RNDIS/ECM/MBIM/DPL
|
||||
related functionalities using GSI hardware accelerated data
|
||||
path and control path.
|
||||
|
||||
source "drivers/usb/gadget/legacy/Kconfig"
|
||||
|
||||
endif # USB_GADGET
|
||||
|
|
|
|||
|
|
@ -54,3 +54,15 @@ usb_f_accessory-y := f_accessory.o
|
|||
obj-$(CONFIG_USB_F_ACC) += usb_f_accessory.o
|
||||
usb_f_audio_source-y := f_audio_source.o
|
||||
obj-$(CONFIG_USB_F_AUDIO_SRC) += usb_f_audio_source.o
|
||||
obj-$(CONFIG_USB_F_FS_IPC_LOGGING) += f_fs_ipc_log.o
|
||||
usb_f_cdev-y := f_cdev.o
|
||||
obj-$(CONFIG_USB_F_CDEV) += usb_f_cdev.o
|
||||
usb_f_ccid-y := f_ccid.o
|
||||
obj-$(CONFIG_USB_F_CCID) += usb_f_ccid.o
|
||||
usb_f_qdss-y := f_qdss.o u_qdss.o
|
||||
obj-$(CONFIG_USB_F_QDSS) += usb_f_qdss.o
|
||||
usb_f_gsi-y := f_gsi.o
|
||||
ifeq ($(CONFIG_USB_F_RNDIS),)
|
||||
usb_f_gsi-y += rndis.o
|
||||
endif
|
||||
obj-$(CONFIG_USB_F_GSI) += usb_f_gsi.o
|
||||
|
|
|
|||
1239
drivers/usb/gadget/function/f_ccid.c
Normal file
1239
drivers/usb/gadget/function/f_ccid.c
Normal file
File diff suppressed because it is too large
Load Diff
76
drivers/usb/gadget/function/f_ccid.h
Normal file
76
drivers/usb/gadget/function/f_ccid.h
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (c) 2011, 2017 The Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __F_CCID_H
|
||||
#define __F_CCID_H
|
||||
|
||||
#define PROTOCOL_TO 0x01
|
||||
#define PROTOCOL_T1 0x02
|
||||
#define ABDATA_SIZE 512
|
||||
|
||||
/* define for dwFeatures for Smart Card Device Class Descriptors */
|
||||
/* No special characteristics */
|
||||
#define CCID_FEATURES_NADA 0x00000000
|
||||
/* Automatic parameter configuration based on ATR data */
|
||||
#define CCID_FEATURES_AUTO_PCONF 0x00000002
|
||||
/* Automatic activation of ICC on inserting */
|
||||
#define CCID_FEATURES_AUTO_ACTIV 0x00000004
|
||||
/* Automatic ICC voltage selection */
|
||||
#define CCID_FEATURES_AUTO_VOLT 0x00000008
|
||||
/* Automatic ICC clock frequency change */
|
||||
#define CCID_FEATURES_AUTO_CLOCK 0x00000010
|
||||
/* Automatic baud rate change */
|
||||
#define CCID_FEATURES_AUTO_BAUD 0x00000020
|
||||
/*Automatic parameters negotiation made by the CCID */
|
||||
#define CCID_FEATURES_AUTO_PNEGO 0x00000040
|
||||
/* Automatic PPS made by the CCID according to the active parameters */
|
||||
#define CCID_FEATURES_AUTO_PPS 0x00000080
|
||||
/* CCID can set ICC in clock stop mode */
|
||||
#define CCID_FEATURES_ICCSTOP 0x00000100
|
||||
/* NAD value other than 00 accepted (T=1 protocol in use) */
|
||||
#define CCID_FEATURES_NAD 0x00000200
|
||||
/* Automatic IFSD exchange as first exchange (T=1 protocol in use) */
|
||||
#define CCID_FEATURES_AUTO_IFSD 0x00000400
|
||||
/* TPDU level exchanges with CCID */
|
||||
#define CCID_FEATURES_EXC_TPDU 0x00010000
|
||||
/* Short APDU level exchange with CCID */
|
||||
#define CCID_FEATURES_EXC_SAPDU 0x00020000
|
||||
/* Short and Extended APDU level exchange with CCID */
|
||||
#define CCID_FEATURES_EXC_APDU 0x00040000
|
||||
/* USB Wake up signaling supported on card insertion and removal */
|
||||
#define CCID_FEATURES_WAKEUP 0x00100000
|
||||
|
||||
#define CCID_NOTIFY_CARD _IOW('C', 1, struct usb_ccid_notification)
|
||||
#define CCID_NOTIFY_HWERROR _IOW('C', 2, struct usb_ccid_notification)
|
||||
#define CCID_READ_DTR _IOR('C', 3, int)
|
||||
|
||||
struct usb_ccid_notification {
|
||||
__u8 buf[4];
|
||||
} __packed;
|
||||
|
||||
struct ccid_bulk_in_header {
|
||||
__u8 bMessageType;
|
||||
__u32 wLength;
|
||||
__u8 bSlot;
|
||||
__u8 bSeq;
|
||||
__u8 bStatus;
|
||||
__u8 bError;
|
||||
__u8 bSpecific;
|
||||
__u8 abData[ABDATA_SIZE];
|
||||
__u8 bSizeToSend;
|
||||
} __packed;
|
||||
|
||||
struct ccid_bulk_out_header {
|
||||
__u8 bMessageType;
|
||||
__u32 wLength;
|
||||
__u8 bSlot;
|
||||
__u8 bSeq;
|
||||
__u8 bSpecific_0;
|
||||
__u8 bSpecific_1;
|
||||
__u8 bSpecific_2;
|
||||
__u8 APDU[ABDATA_SIZE];
|
||||
} __packed;
|
||||
#endif
|
||||
2028
drivers/usb/gadget/function/f_cdev.c
Normal file
2028
drivers/usb/gadget/function/f_cdev.c
Normal file
File diff suppressed because it is too large
Load Diff
904
drivers/usb/gadget/function/f_fs_ipc_log.c
Normal file
904
drivers/usb/gadget/function/f_fs_ipc_log.c
Normal file
|
|
@ -0,0 +1,904 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2021, The Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kprobes.h>
|
||||
#include <linux/fs_parser.h>
|
||||
#include <linux/ipc_logging.h>
|
||||
#include <linux/usb/functionfs.h>
|
||||
|
||||
#include "u_fs.h"
|
||||
|
||||
/* Copied from f_fs.c */
|
||||
struct ffs_io_data {
|
||||
bool aio;
|
||||
bool read;
|
||||
|
||||
struct kiocb *kiocb;
|
||||
struct iov_iter data;
|
||||
const void *to_free;
|
||||
char *buf;
|
||||
|
||||
struct mm_struct *mm;
|
||||
struct work_struct work;
|
||||
|
||||
struct usb_ep *ep;
|
||||
struct usb_request *req;
|
||||
struct sg_table sgt;
|
||||
bool use_sg;
|
||||
|
||||
struct ffs_data *ffs;
|
||||
};
|
||||
|
||||
/* Copied from f_fs.c */
|
||||
struct ffs_epfile {
|
||||
struct mutex mutex;
|
||||
struct ffs_data *ffs;
|
||||
struct ffs_ep *ep; /* P: ffs->eps_lock */
|
||||
struct dentry *dentry;
|
||||
struct ffs_buffer *read_buffer;
|
||||
#define READ_BUFFER_DROP ((struct ffs_buffer *)ERR_PTR(-ESHUTDOWN))
|
||||
char name[5];
|
||||
unsigned char in; /* P: ffs->eps_lock */
|
||||
unsigned char isoc; /* P: ffs->eps_lock */
|
||||
unsigned char _pad;
|
||||
};
|
||||
|
||||
/* Copied from f_fs.c */
|
||||
struct ffs_ep {
|
||||
struct usb_ep *ep; /* P: ffs->eps_lock */
|
||||
struct usb_request *req; /* P: epfile->mutex */
|
||||
/* [0]: full speed, [1]: high speed, [2]: super speed */
|
||||
struct usb_endpoint_descriptor *descs[3];
|
||||
|
||||
u8 num;
|
||||
|
||||
int status; /* P: epfile->mutex */
|
||||
};
|
||||
|
||||
/* Copied from f_fs.c */
|
||||
struct ffs_sb_fill_data {
|
||||
struct ffs_file_perms perms;
|
||||
umode_t root_mode;
|
||||
const char *dev_name;
|
||||
bool no_disconnect;
|
||||
struct ffs_data *ffs_data;
|
||||
};
|
||||
|
||||
/* Copied from f_fs.c */
|
||||
struct ffs_function {
|
||||
struct usb_configuration *conf;
|
||||
struct usb_gadget *gadget;
|
||||
struct ffs_data *ffs;
|
||||
struct ffs_ep *eps;
|
||||
u8 eps_revmap[16];
|
||||
short *interfaces_nums;
|
||||
struct usb_function function;
|
||||
};
|
||||
|
||||
/* Copied from f_fs.c */
|
||||
enum ffs_os_desc_type {
|
||||
FFS_OS_DESC, FFS_OS_DESC_EXT_COMPAT, FFS_OS_DESC_EXT_PROP
|
||||
};
|
||||
|
||||
#define kprobe_log(context, fmt, ...) \
|
||||
ipc_log_string(context, "%s: " fmt, \
|
||||
get_kretprobe(ri)->kp.symbol_name, ##__VA_ARGS__)
|
||||
|
||||
#define MAX_IPC_INSTANCES 9
|
||||
|
||||
/* per-probe private data */
|
||||
struct kprobe_data {
|
||||
void *x0;
|
||||
void *x1;
|
||||
void *x2;
|
||||
};
|
||||
|
||||
struct ipc_log_work {
|
||||
struct ffs_data *ffs;
|
||||
const char *dev_name;
|
||||
struct work_struct ctxt_work;
|
||||
};
|
||||
|
||||
/* per-device IPC log data */
|
||||
struct ipc_log {
|
||||
void *context;
|
||||
struct ffs_data *ffs;
|
||||
};
|
||||
|
||||
static struct workqueue_struct *ipc_wq;
|
||||
static struct ipc_log ipc_log_s[MAX_IPC_INSTANCES];
|
||||
|
||||
/* Number of devices for f_fs driver */
|
||||
static int num_devices;
|
||||
|
||||
static int ipc_inst_exists(struct ffs_data *ffs)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
for (i = 0; i < num_devices; i++)
|
||||
if (ipc_log_s[i].ffs == ffs)
|
||||
return i;
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static void create_ipc_context_work(struct work_struct *w)
|
||||
{
|
||||
struct ipc_log_work *ipc_w = container_of(w, struct ipc_log_work,
|
||||
ctxt_work);
|
||||
char ipcname[24] = "usb_ffs_";
|
||||
void *ctx;
|
||||
|
||||
if (num_devices >= MAX_IPC_INSTANCES) {
|
||||
pr_err("Can't create any more FFS log contexts\n");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (ipc_inst_exists(ipc_w->ffs) >= 0)
|
||||
goto exit;
|
||||
|
||||
strlcat(ipcname, ipc_w->dev_name, sizeof(ipcname));
|
||||
ctx = ipc_log_context_create(10, ipcname, 0);
|
||||
if (IS_ERR_OR_NULL(ctx)) {
|
||||
pr_err("%s: Could not create IPC log context for device %s\n",
|
||||
__func__, ipc_w->dev_name);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
ipc_log_s[num_devices].context = ctx;
|
||||
ipc_log_s[num_devices].ffs = ipc_w->ffs;
|
||||
num_devices++;
|
||||
|
||||
exit:
|
||||
kfree(ipc_w);
|
||||
}
|
||||
|
||||
static void create_ipc_context(const char *dev_name, struct ffs_data *ffs)
|
||||
{
|
||||
struct ipc_log_work *ipc_w;
|
||||
|
||||
ipc_w = kzalloc(sizeof(*ipc_w), GFP_ATOMIC);
|
||||
if (!ipc_w)
|
||||
return;
|
||||
|
||||
INIT_WORK(&ipc_w->ctxt_work, create_ipc_context_work);
|
||||
ipc_w->dev_name = ffs->dev_name;
|
||||
ipc_w->ffs = ffs;
|
||||
|
||||
queue_work(ipc_wq, &ipc_w->ctxt_work);
|
||||
}
|
||||
|
||||
static void *get_ipc_context(struct ffs_data *ffs)
|
||||
{
|
||||
int idx = 0;
|
||||
|
||||
idx = ipc_inst_exists(ffs);
|
||||
if (idx >= 0)
|
||||
return ipc_log_s[idx].context;
|
||||
|
||||
/* not found, create a new one now */
|
||||
create_ipc_context(ffs->dev_name, ffs);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int entry_ffs_user_copy_worker(struct kretprobe_instance *ri,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
struct kprobe_data *data = (struct kprobe_data *)ri->data;
|
||||
struct work_struct *work = (struct work_struct *)regs->regs[0];
|
||||
struct ffs_io_data *io_data = container_of(work, struct ffs_io_data, work);
|
||||
int ret = io_data->req->status ? io_data->req->status :
|
||||
io_data->req->actual;
|
||||
struct ffs_data *ffs = io_data->ffs;
|
||||
void *context = get_ipc_context(ffs);
|
||||
|
||||
data->x0 = work;
|
||||
kprobe_log(context, "enter: ret %d for %s",
|
||||
ret, io_data->read ? "read" : "write");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int entry_ffs_epfile_io(struct kretprobe_instance *ri, struct pt_regs *regs)
|
||||
{
|
||||
struct kprobe_data *data = (struct kprobe_data *)ri->data;
|
||||
struct file *file = (struct file *)regs->regs[0];
|
||||
struct ffs_io_data *io_data = (struct ffs_io_data *)regs->regs[1];
|
||||
struct ffs_epfile *epfile = file->private_data;
|
||||
void *context = get_ipc_context(epfile->ffs);
|
||||
|
||||
data->x0 = file;
|
||||
data->x1 = io_data;
|
||||
kprobe_log(context, "enter: %s about to queue %zd bytes",
|
||||
epfile->name, iov_iter_count(&io_data->data));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int exit_ffs_epfile_io(struct kretprobe_instance *ri, struct pt_regs *regs)
|
||||
{
|
||||
struct kprobe_data *data = (struct kprobe_data *)ri->data;
|
||||
struct file *file = data->x0;
|
||||
struct ffs_epfile *epfile = file->private_data;
|
||||
unsigned long ret = regs_return_value(regs);
|
||||
void *context = get_ipc_context(epfile->ffs);
|
||||
|
||||
kprobe_log(context, "exit: %s ret %zd", epfile->name, ret);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int entry_ffs_epfile_async_io_complete(struct kretprobe_instance *ri,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
struct usb_request *req = (struct usb_request *)regs->regs[1];
|
||||
struct ffs_io_data *io_data = req->context;
|
||||
void *context = get_ipc_context(io_data->ffs);
|
||||
|
||||
kprobe_log(context, "enter");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int entry_ffs_epfile_write_iter(struct kretprobe_instance *ri,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
struct kprobe_data *data = (struct kprobe_data *)ri->data;
|
||||
struct kiocb *kiocb = (struct kiocb *)regs->regs[0];
|
||||
struct ffs_epfile *epfile = kiocb->ki_filp->private_data;
|
||||
void *context = get_ipc_context(epfile->ffs);
|
||||
|
||||
data->x0 = kiocb;
|
||||
kprobe_log(context, "enter");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int exit_ffs_epfile_write_iter(struct kretprobe_instance *ri,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
struct kprobe_data *data = (struct kprobe_data *)ri->data;
|
||||
struct kiocb *kiocb = data->x0;
|
||||
struct ffs_epfile *epfile = kiocb->ki_filp->private_data;
|
||||
void *context = get_ipc_context(epfile->ffs);
|
||||
unsigned long ret = regs_return_value(regs);
|
||||
|
||||
if (ret != -ENOMEM && ret != -EIOCBQUEUED)
|
||||
kprobe_log(context, "exit: ret %zd", ret);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int entry_ffs_epfile_read_iter(struct kretprobe_instance *ri,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
struct kprobe_data *data = (struct kprobe_data *)ri->data;
|
||||
struct kiocb *kiocb = (struct kiocb *)regs->regs[0];
|
||||
struct ffs_epfile *epfile = kiocb->ki_filp->private_data;
|
||||
void *context = get_ipc_context(epfile->ffs);
|
||||
|
||||
data->x0 = kiocb;
|
||||
kprobe_log(context, "enter");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int exit_ffs_epfile_read_iter(struct kretprobe_instance *ri,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
struct kprobe_data *data = (struct kprobe_data *)ri->data;
|
||||
struct kiocb *kiocb = data->x0;
|
||||
struct ffs_epfile *epfile = kiocb->ki_filp->private_data;
|
||||
void *context = get_ipc_context(epfile->ffs);
|
||||
unsigned long ret = regs_return_value(regs);
|
||||
|
||||
kprobe_log(context, "exit: ret %zd", ret);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int entry_ffs_data_put(struct kretprobe_instance *ri,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
struct ffs_data *ffs = (struct ffs_data *)regs->regs[0];
|
||||
void *context = get_ipc_context(ffs);
|
||||
unsigned int refcount = refcount_read(&ffs->ref);
|
||||
|
||||
kprobe_log(context, "ref %u", refcount);
|
||||
|
||||
if (refcount == 1) {
|
||||
ipc_log_context_destroy(context);
|
||||
context = NULL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int entry_ffs_sb_fill(struct kretprobe_instance *ri, struct pt_regs *regs)
|
||||
{
|
||||
struct fs_context *fc = (struct fs_context *)regs->regs[1];
|
||||
struct ffs_sb_fill_data *data = fc->fs_private;
|
||||
|
||||
create_ipc_context(fc->source, data->ffs_data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int entry___ffs_ep0_queue_wait(struct kretprobe_instance *ri,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
struct kprobe_data *data = (struct kprobe_data *)ri->data;
|
||||
struct ffs_data *ffs = (struct ffs_data *)regs->regs[0];
|
||||
void *context = get_ipc_context(ffs);
|
||||
|
||||
data->x0 = ffs;
|
||||
kprobe_log(context, "enter: state %d setup_state %d flags %lu",
|
||||
ffs->state, ffs->setup_state, ffs->flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int exit___ffs_ep0_queue_wait(struct kretprobe_instance *ri,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
struct kprobe_data *data = (struct kprobe_data *)ri->data;
|
||||
struct ffs_data *ffs = data->x0;
|
||||
void *context = get_ipc_context(ffs);
|
||||
|
||||
kprobe_log(context, "exit: state %d setup_state %d flags %lu",
|
||||
ffs->state, ffs->setup_state, ffs->flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int entry_ffs_ep0_write(struct kretprobe_instance *ri,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
struct kprobe_data *data = (struct kprobe_data *)ri->data;
|
||||
struct file *file = (struct file *)regs->regs[0];
|
||||
struct ffs_data *ffs = file->private_data;
|
||||
unsigned int len = (unsigned int)regs->regs[2];
|
||||
void *context = get_ipc_context(ffs);
|
||||
|
||||
data->x0 = file;
|
||||
kprobe_log(context, "enter:len %zu state %d setup_state %d flags %lu",
|
||||
len, ffs->state, ffs->setup_state, ffs->flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int exit_ffs_ep0_write(struct kretprobe_instance *ri,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
|
||||
struct kprobe_data *data = (struct kprobe_data *)ri->data;
|
||||
struct file *file = data->x0;
|
||||
struct ffs_data *ffs = file->private_data;
|
||||
void *context = get_ipc_context(ffs);
|
||||
unsigned long ret = regs_return_value(regs);
|
||||
|
||||
if (ret != -EIDRM && ret != -EINVAL)
|
||||
kprobe_log(context,
|
||||
"exit:ret %zd state %d setup_state %d flags %lu",
|
||||
ret, ffs->state, ffs->setup_state, ffs->flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int entry_ffs_ep0_read(struct kretprobe_instance *ri,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
struct kprobe_data *data = (struct kprobe_data *)ri->data;
|
||||
struct file *file = (struct file *)regs->regs[0];
|
||||
size_t len = (size_t)regs->regs[2];
|
||||
struct ffs_data *ffs = file->private_data;
|
||||
void *context = get_ipc_context(ffs);
|
||||
size_t n;
|
||||
|
||||
data->x0 = file;
|
||||
n = min((len / sizeof(struct usb_functionfs_event)), (size_t)ffs->ev.count);
|
||||
kprobe_log(context, "enter:len %zu state %d setup_state %d flags %lu n %zu",
|
||||
len, ffs->state, ffs->setup_state, ffs->flags, n);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int exit_ffs_ep0_read(struct kretprobe_instance *ri,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
struct kprobe_data *data = (struct kprobe_data *)ri->data;
|
||||
struct file *file = data->x0;
|
||||
struct ffs_data *ffs = file->private_data;
|
||||
void *context = get_ipc_context(ffs);
|
||||
unsigned long ret = regs_return_value(regs);
|
||||
|
||||
kprobe_log(context, "exit:ret %d state %d setup_state %d flags %lu",
|
||||
ret, ffs->state, ffs->setup_state, ffs->flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int entry_ffs_ep0_open(struct kretprobe_instance *ri,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
struct inode *inode = (struct inode *)regs->regs[0];
|
||||
struct ffs_data *ffs = inode->i_private;
|
||||
void *context = get_ipc_context(ffs);
|
||||
|
||||
kprobe_log(context, "state %d setup_state %d flags %lu opened %d",
|
||||
ffs->state, ffs->setup_state, ffs->flags,
|
||||
atomic_read(&ffs->opened));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int entry_ffs_ep0_release(struct kretprobe_instance *ri,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
struct file *file = (struct file *)regs->regs[1];
|
||||
struct ffs_data *ffs = file->private_data;
|
||||
void *context = get_ipc_context(ffs);
|
||||
|
||||
kprobe_log(context, "state %d setup_state %d flags %lu opened %d",
|
||||
ffs->state, ffs->setup_state, ffs->flags,
|
||||
atomic_read(&ffs->opened));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int entry_ffs_ep0_ioctl(struct kretprobe_instance *ri,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
struct file *file = (struct file *)regs->regs[0];
|
||||
struct ffs_data *ffs = file->private_data;
|
||||
void *context = get_ipc_context(ffs);
|
||||
|
||||
kprobe_log(context, "state %d setup_state %d flags %lu opened %d",
|
||||
ffs->state, ffs->setup_state, ffs->flags,
|
||||
atomic_read(&ffs->opened));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int entry_ffs_ep0_poll(struct kretprobe_instance *ri,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
struct kprobe_data *data = (struct kprobe_data *)ri->data;
|
||||
struct file *file = (struct file *)regs->regs[0];
|
||||
struct ffs_data *ffs = file->private_data;
|
||||
void *context = get_ipc_context(ffs);
|
||||
|
||||
data->x0 = file;
|
||||
kprobe_log(context, "state %d setup_state %d flags %lu opened %d",
|
||||
ffs->state, ffs->setup_state, ffs->flags,
|
||||
atomic_read(&ffs->opened));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int exit_ffs_ep0_poll(struct kretprobe_instance *ri,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
struct kprobe_data *data = (struct kprobe_data *)ri->data;
|
||||
unsigned long ret = regs_return_value(regs);
|
||||
struct file *file = data->x0;
|
||||
struct ffs_data *ffs = file->private_data;
|
||||
void *context = get_ipc_context(ffs);
|
||||
|
||||
kprobe_log(context, "exit: mask %u", ret);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int entry_ffs_epfile_open(struct kretprobe_instance *ri,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
struct inode *inode = (struct inode *)regs->regs[0];
|
||||
struct ffs_epfile *epfile = inode->i_private;
|
||||
void *context = get_ipc_context(epfile->ffs);
|
||||
|
||||
kprobe_log(context, "%s: state %d setup_state %d flag %lu opened %u",
|
||||
epfile->name, epfile->ffs->state, epfile->ffs->setup_state,
|
||||
epfile->ffs->flags, atomic_read(&epfile->ffs->opened));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int entry_ffs_aio_cancel(struct kretprobe_instance *ri,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
struct kprobe_data *data = (struct kprobe_data *)ri->data;
|
||||
struct kiocb *kiocb = (struct kiocb *)regs->regs[0];
|
||||
struct ffs_epfile *epfile = kiocb->ki_filp->private_data;
|
||||
void *context = get_ipc_context(epfile->ffs);
|
||||
|
||||
data->x0 = kiocb;
|
||||
kprobe_log(context, "enter:state %d setup_state %d flag %lu",
|
||||
epfile->ffs->state, epfile->ffs->setup_state,
|
||||
epfile->ffs->flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int exit_ffs_aio_cancel(struct kretprobe_instance *ri,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
struct kprobe_data *data = (struct kprobe_data *)ri->data;
|
||||
unsigned long ret = regs_return_value(regs);
|
||||
struct kiocb *kiocb = data->x0;
|
||||
struct ffs_epfile *epfile = kiocb->ki_filp->private_data;
|
||||
void *context = get_ipc_context(epfile->ffs);
|
||||
|
||||
kprobe_log(context, "exit: mask %u", ret);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int entry_ffs_epfile_release(struct kretprobe_instance *ri,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
struct inode *inode = (struct inode *)regs->regs[0];
|
||||
struct ffs_epfile *epfile = inode->i_private;
|
||||
void *context = get_ipc_context(epfile->ffs);
|
||||
|
||||
kprobe_log(context, "%s: state %d setup_state %d flag %lu opened %u",
|
||||
epfile->name, epfile->ffs->state, epfile->ffs->setup_state,
|
||||
epfile->ffs->flags, atomic_read(&epfile->ffs->opened));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int entry_ffs_epfile_ioctl(struct kretprobe_instance *ri,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
struct kprobe_data *data = (struct kprobe_data *)ri->data;
|
||||
struct file *file = (struct file *)regs->regs[0];
|
||||
unsigned int code = (unsigned int)regs->regs[1];
|
||||
unsigned long value = (unsigned long)regs->regs[2];
|
||||
struct ffs_epfile *epfile = file->private_data;
|
||||
void *context = get_ipc_context(epfile->ffs);
|
||||
|
||||
data->x0 = file;
|
||||
kprobe_log(context,
|
||||
"%s: code 0x%08x value %#lx state %d setup_state %d flag %lu",
|
||||
epfile->name, code, value, epfile->ffs->state,
|
||||
epfile->ffs->setup_state, epfile->ffs->flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int exit_ffs_epfile_ioctl(struct kretprobe_instance *ri,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
struct kprobe_data *data = (struct kprobe_data *)ri->data;
|
||||
unsigned long ret = regs_return_value(regs);
|
||||
struct file *file = data->x0;
|
||||
struct ffs_epfile *epfile = file->private_data;
|
||||
void *context = get_ipc_context(epfile->ffs);
|
||||
|
||||
kprobe_log(context, "exit: %s: ret %d\n", epfile->name, ret);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int entry_ffs_data_opened(struct kretprobe_instance *ri,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
struct ffs_data *ffs = (struct ffs_data *)regs->regs[0];
|
||||
void *context = get_ipc_context(ffs);
|
||||
|
||||
kprobe_log(context,
|
||||
"enter: state %d setup_state %d flag %lu opened %d ref %d",
|
||||
ffs->state, ffs->setup_state, ffs->flags,
|
||||
atomic_read(&ffs->opened), refcount_read(&ffs->ref));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int entry_ffs_data_closed(struct kretprobe_instance *ri,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
struct ffs_data *ffs = (struct ffs_data *)regs->regs[0];
|
||||
void *context = get_ipc_context(ffs);
|
||||
|
||||
kprobe_log(context, "state %d setup_state %d flag %lu opened %d",
|
||||
ffs->state, ffs->setup_state, ffs->flags,
|
||||
atomic_read(&ffs->opened));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int entry_ffs_data_clear(struct kretprobe_instance *ri,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
struct ffs_data *ffs = (struct ffs_data *)regs->regs[0];
|
||||
void *context = get_ipc_context(ffs);
|
||||
|
||||
kprobe_log(context, "enter: state %d setup_state %d flag %lu",
|
||||
ffs->state, ffs->setup_state, ffs->flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int entry_functionfs_bind(struct kretprobe_instance *ri,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
struct kprobe_data *data = (struct kprobe_data *)ri->data;
|
||||
struct ffs_data *ffs = (struct ffs_data *)regs->regs[0];
|
||||
void *context = get_ipc_context(ffs);
|
||||
|
||||
data->x0 = ffs;
|
||||
kprobe_log(context, "enter: state %d setup_state %d flag %lu",
|
||||
ffs->state, ffs->setup_state, ffs->flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int exit_functionfs_bind(struct kretprobe_instance *ri,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
struct kprobe_data *data = (struct kprobe_data *)ri->data;
|
||||
struct ffs_data *ffs = data->x0;
|
||||
void *context = get_ipc_context(ffs);
|
||||
unsigned long ret = regs_return_value(regs);
|
||||
|
||||
kprobe_log(context, "functionfs_bind returned %d", ret);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int entry_ffs_func_eps_disable(struct kretprobe_instance *ri,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
struct ffs_function *func = (struct ffs_function *)regs->regs[0];
|
||||
void *context = get_ipc_context(func->ffs);
|
||||
|
||||
kprobe_log(context, "enter: state %d setup_state %d flag %lu",
|
||||
func->ffs->state, func->ffs->setup_state, func->ffs->flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int entry_ffs_func_bind(struct kretprobe_instance *ri,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
struct kprobe_data *data = (struct kprobe_data *)ri->data;
|
||||
struct usb_function *f = (struct usb_function *)regs->regs[1];
|
||||
struct f_fs_opts *ffs_opts =
|
||||
container_of(f->fi, struct f_fs_opts, func_inst);
|
||||
/* func->ffs not set yet; get ffs_data via ffs_opts instead */
|
||||
struct ffs_data *ffs = ffs_opts->dev->ffs_data;
|
||||
void *context;
|
||||
|
||||
if (!ffs)
|
||||
return 0;
|
||||
|
||||
context = get_ipc_context(ffs);
|
||||
data->x0 = ffs;
|
||||
kprobe_log(context, "enter");
|
||||
kprobe_log(context, "_ffs_func_bind",
|
||||
"enter: state %d setup_state %d flag %lu", ffs->state,
|
||||
ffs->setup_state, ffs->flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int exit_ffs_func_bind(struct kretprobe_instance *ri,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
struct kprobe_data *data = (struct kprobe_data *)ri->data;
|
||||
int ret = (int)regs_return_value(regs);
|
||||
struct ffs_data *ffs = data->x0;
|
||||
void *context;
|
||||
|
||||
if (!ffs)
|
||||
return 0;
|
||||
|
||||
context = get_ipc_context(ffs);
|
||||
if (ret < 0)
|
||||
kprobe_log(context, "exit: ret %d", ret);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int entry_ffs_reset_work(struct kretprobe_instance *ri,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
struct work_struct *work = (struct work_struct *)regs->regs[0];
|
||||
struct ffs_data *ffs = container_of(work, struct ffs_data, reset_work);
|
||||
void *context = get_ipc_context(ffs);
|
||||
|
||||
kprobe_log(context, "enter");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int entry_ffs_func_set_alt(struct kretprobe_instance *ri,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
struct kprobe_data *data = (struct kprobe_data *)ri->data;
|
||||
struct usb_function *f = (struct usb_function *)regs->regs[0];
|
||||
struct ffs_function *func = container_of(f, struct ffs_function, function);
|
||||
unsigned int alt = (unsigned int)regs->regs[2];
|
||||
void *context = get_ipc_context(func->ffs);
|
||||
|
||||
data->x0 = func;
|
||||
kprobe_log(context, "enter: alt %d", (int)alt);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int entry_ffs_func_disable(struct kretprobe_instance *ri,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
struct usb_function *f = (struct usb_function *)regs->regs[0];
|
||||
struct ffs_function *func = container_of(f, struct ffs_function, function);
|
||||
void *context = get_ipc_context(func->ffs);
|
||||
|
||||
kprobe_log(context, "enter");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int entry_ffs_func_setup(struct kretprobe_instance *ri,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
struct usb_function *f = (struct usb_function *)regs->regs[0];
|
||||
struct ffs_function *func = container_of(f, struct ffs_function, function);
|
||||
struct usb_ctrlrequest *creq = (struct usb_ctrlrequest *)regs->regs[1];
|
||||
struct ffs_data *ffs = func->ffs;
|
||||
void *context = get_ipc_context(func->ffs);
|
||||
|
||||
kprobe_log(context,
|
||||
"enter: state %d reqtype=%02x req=%02x wv=%04x wi=%04x wl=%04x",
|
||||
ffs->state, creq->bRequestType, creq->bRequest,
|
||||
le16_to_cpu(creq->wValue), le16_to_cpu(creq->wIndex),
|
||||
le16_to_cpu(creq->wLength));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int entry_ffs_func_suspend(struct kretprobe_instance *ri,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
struct usb_function *f = (struct usb_function *)regs->regs[0];
|
||||
struct ffs_function *func = container_of(f, struct ffs_function, function);
|
||||
void *context = get_ipc_context(func->ffs);
|
||||
|
||||
kprobe_log(context, "enter");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int entry_ffs_func_resume(struct kretprobe_instance *ri,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
struct usb_function *f = (struct usb_function *)regs->regs[0];
|
||||
struct ffs_function *func = container_of(f, struct ffs_function, function);
|
||||
void *context = get_ipc_context(func->ffs);
|
||||
|
||||
kprobe_log(context, "enter");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int entry_ffs_func_unbind(struct kretprobe_instance *ri,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
struct kprobe_data *data = (struct kprobe_data *)ri->data;
|
||||
struct usb_function *f = (struct usb_function *)regs->regs[1];
|
||||
struct ffs_function *func = container_of(f, struct ffs_function, function);
|
||||
struct ffs_data *ffs = func->ffs;
|
||||
struct f_fs_opts *opts =
|
||||
container_of(f->fi, struct f_fs_opts, func_inst);
|
||||
void *context = get_ipc_context(ffs);
|
||||
|
||||
data->x1 = ffs;
|
||||
kprobe_log(context, "enter: state %d setup_state %d flag %lu refcnt %u",
|
||||
ffs->state, ffs->setup_state, ffs->flags, opts->refcnt);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int exit_ffs_func_unbind(struct kretprobe_instance *ri,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
struct kprobe_data *data = (struct kprobe_data *)ri->data;
|
||||
struct ffs_data *ffs = data->x1;
|
||||
void *context = get_ipc_context(ffs);
|
||||
|
||||
kprobe_log(context, "exit: state %d setup_state %d flag %lu",
|
||||
ffs->state, ffs->setup_state, ffs->flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int entry_ffs_closed(struct kretprobe_instance *ri,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
struct kprobe_data *data = (struct kprobe_data *)ri->data;
|
||||
struct ffs_data *ffs = (struct ffs_data *)regs->regs[0];
|
||||
void *context = get_ipc_context(ffs);
|
||||
|
||||
data->x0 = ffs;
|
||||
kprobe_log(context, "enter");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int exit_ffs_closed(struct kretprobe_instance *ri,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
struct kprobe_data *data = (struct kprobe_data *)ri->data;
|
||||
struct ffs_data *ffs = data->x0;
|
||||
struct ffs_dev *ffs_obj = ffs->private_data;
|
||||
void *context = get_ipc_context(ffs);
|
||||
|
||||
if (test_bit(FFS_FL_BOUND, &ffs->flags))
|
||||
kprobe_log(context, "unreg gadget done");
|
||||
else if (!ffs_obj || !ffs_obj->opts || ffs_obj->opts->no_configfs ||
|
||||
!ffs_obj->opts->func_inst.group.cg_item.ci_parent
|
||||
|| !kref_read(&ffs_obj->opts->func_inst.group.cg_item.ci_kref))
|
||||
kprobe_log(context, "exit error");
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define ENTRY_EXIT(name) {\
|
||||
.handler = exit_##name,\
|
||||
.entry_handler = entry_##name,\
|
||||
.data_size = sizeof(struct kprobe_data),\
|
||||
.maxactive = MAX_IPC_INSTANCES,\
|
||||
.kp.symbol_name = #name,\
|
||||
}
|
||||
|
||||
#define ENTRY(name) {\
|
||||
.entry_handler = entry_##name,\
|
||||
.data_size = sizeof(struct kprobe_data),\
|
||||
.maxactive = MAX_IPC_INSTANCES,\
|
||||
.kp.symbol_name = #name,\
|
||||
}
|
||||
|
||||
static struct kretprobe ffsprobes[] = {
|
||||
ENTRY(ffs_user_copy_worker),
|
||||
ENTRY_EXIT(ffs_epfile_io),
|
||||
ENTRY(ffs_epfile_async_io_complete),
|
||||
ENTRY_EXIT(ffs_epfile_write_iter),
|
||||
ENTRY_EXIT(ffs_epfile_read_iter),
|
||||
ENTRY(ffs_data_put),
|
||||
ENTRY(ffs_sb_fill),
|
||||
ENTRY_EXIT(__ffs_ep0_queue_wait),
|
||||
ENTRY_EXIT(ffs_ep0_write),
|
||||
ENTRY_EXIT(ffs_ep0_read),
|
||||
ENTRY(ffs_ep0_open),
|
||||
ENTRY(ffs_ep0_release),
|
||||
ENTRY(ffs_ep0_ioctl),
|
||||
ENTRY_EXIT(ffs_ep0_poll),
|
||||
ENTRY(ffs_epfile_open),
|
||||
ENTRY_EXIT(ffs_aio_cancel),
|
||||
ENTRY(ffs_epfile_release),
|
||||
ENTRY_EXIT(ffs_epfile_ioctl),
|
||||
ENTRY(ffs_data_opened),
|
||||
ENTRY(ffs_data_closed),
|
||||
ENTRY(ffs_data_clear),
|
||||
ENTRY_EXIT(functionfs_bind),
|
||||
ENTRY(ffs_func_eps_disable),
|
||||
ENTRY_EXIT(ffs_func_bind),
|
||||
ENTRY(ffs_reset_work),
|
||||
ENTRY(ffs_func_set_alt),
|
||||
ENTRY(ffs_func_disable),
|
||||
ENTRY(ffs_func_setup),
|
||||
ENTRY(ffs_func_suspend),
|
||||
ENTRY(ffs_func_resume),
|
||||
ENTRY_EXIT(ffs_func_unbind),
|
||||
ENTRY_EXIT(ffs_closed)
|
||||
};
|
||||
|
||||
static int __init kretprobe_init(void)
|
||||
{
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
ipc_wq = alloc_ordered_workqueue("ipc_wq", 0);
|
||||
if (!ipc_wq) {
|
||||
pr_err("%s: Unable to create workqueue ipc_wq\n", __func__);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(ffsprobes); i++) {
|
||||
ret = register_kretprobe(&ffsprobes[i]);
|
||||
if (ret < 0) {
|
||||
pr_err("register_kretprobe failed at %s, returned %d\n",
|
||||
ffsprobes[i].kp.symbol_name, ret);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit kretprobe_exit(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
destroy_workqueue(ipc_wq);
|
||||
for (i = 0; i < num_devices; i++) {
|
||||
if (ipc_log_s[i].ffs) {
|
||||
ipc_log_context_destroy(ipc_log_s[i].context);
|
||||
ipc_log_s[i].context = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(ffsprobes); i++) {
|
||||
unregister_kretprobe(&ffsprobes[i]);
|
||||
|
||||
/* nmissed > 0 suggests that maxactive was set too low. */
|
||||
if (ffsprobes[i].nmissed > 0)
|
||||
pr_info("Missed probing %d instances of %s\n",
|
||||
ffsprobes[i].nmissed,
|
||||
ffsprobes[i].kp.symbol_name);
|
||||
}
|
||||
}
|
||||
|
||||
module_init(kretprobe_init)
|
||||
module_exit(kretprobe_exit)
|
||||
MODULE_LICENSE("GPL");
|
||||
3740
drivers/usb/gadget/function/f_gsi.c
Normal file
3740
drivers/usb/gadget/function/f_gsi.c
Normal file
File diff suppressed because it is too large
Load Diff
1433
drivers/usb/gadget/function/f_gsi.h
Normal file
1433
drivers/usb/gadget/function/f_gsi.h
Normal file
File diff suppressed because it is too large
Load Diff
1236
drivers/usb/gadget/function/f_qdss.c
Normal file
1236
drivers/usb/gadget/function/f_qdss.c
Normal file
File diff suppressed because it is too large
Load Diff
88
drivers/usb/gadget/function/f_qdss.h
Normal file
88
drivers/usb/gadget/function/f_qdss.h
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (c) 2012-2021, The Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef _F_QDSS_H
|
||||
#define _F_QDSS_H
|
||||
|
||||
#include <linux/completion.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/ipc_logging.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/usb/ch9.h>
|
||||
#include <linux/usb/gadget.h>
|
||||
#include <linux/usb/composite.h>
|
||||
#include <linux/usb/usb_qdss.h>
|
||||
|
||||
enum qti_port_type {
|
||||
QTI_PORT_RMNET,
|
||||
QTI_PORT_DPL,
|
||||
QTI_NUM_PORTS
|
||||
};
|
||||
|
||||
struct usb_qdss_ch {
|
||||
const char *name;
|
||||
struct list_head list;
|
||||
void (*notify)(void *priv, unsigned int event,
|
||||
struct qdss_request *d_req, struct usb_qdss_ch *ch);
|
||||
void *priv;
|
||||
int ch_type;
|
||||
};
|
||||
|
||||
struct gqdss {
|
||||
struct usb_function function;
|
||||
struct usb_ep *ctrl_out;
|
||||
struct usb_ep *ctrl_in;
|
||||
struct usb_ep *data;
|
||||
int (*send_encap_cmd)(enum qti_port_type qport, void *buf, size_t len);
|
||||
void (*notify_modem)(void *g, enum qti_port_type qport, int cbits);
|
||||
};
|
||||
|
||||
/* struct f_qdss - USB qdss function driver private structure */
|
||||
struct f_qdss {
|
||||
struct gqdss port;
|
||||
struct usb_gadget *gadget;
|
||||
short int port_num;
|
||||
u8 ctrl_iface_id;
|
||||
u8 data_iface_id;
|
||||
int usb_connected;
|
||||
bool debug_inface_enabled;
|
||||
struct usb_request *endless_req;
|
||||
struct usb_qdss_ch ch;
|
||||
|
||||
/* for mdm channel SW path */
|
||||
struct list_head data_write_pool;
|
||||
struct list_head queued_data_pool;
|
||||
struct list_head dequeued_data_pool;
|
||||
|
||||
struct work_struct connect_w;
|
||||
struct work_struct disconnect_w;
|
||||
spinlock_t lock;
|
||||
unsigned int data_enabled:1;
|
||||
unsigned int ctrl_in_enabled:1;
|
||||
unsigned int ctrl_out_enabled:1;
|
||||
struct workqueue_struct *wq;
|
||||
|
||||
struct mutex mutex;
|
||||
bool opened; /* protected by 'mutex' */
|
||||
struct completion dequeue_done;
|
||||
};
|
||||
|
||||
struct usb_qdss_opts {
|
||||
struct usb_function_instance func_inst;
|
||||
struct f_qdss *usb_qdss;
|
||||
char *channel_name;
|
||||
};
|
||||
|
||||
struct qdss_req {
|
||||
struct usb_request *usb_req;
|
||||
struct qdss_request *qdss_req;
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
int set_qdss_data_connection(struct f_qdss *qdss, int enable);
|
||||
int alloc_hw_req(struct usb_ep *data_ep);
|
||||
#endif
|
||||
70
drivers/usb/gadget/function/u_qdss.c
Normal file
70
drivers/usb/gadget/function/u_qdss.c
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2012-2021, The Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/usb/dwc3-msm.h>
|
||||
|
||||
#include "f_qdss.h"
|
||||
|
||||
#define NUM_EBC_IN_BUF 2
|
||||
|
||||
int alloc_hw_req(struct usb_ep *data_ep)
|
||||
{
|
||||
struct usb_request *req = NULL;
|
||||
struct f_qdss *qdss = data_ep->driver_data;
|
||||
|
||||
pr_debug("allocating EBC request\n");
|
||||
|
||||
req = usb_ep_alloc_request(data_ep, GFP_ATOMIC);
|
||||
if (!req) {
|
||||
pr_err("usb_ep_alloc_request failed\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
req->length = NUM_EBC_IN_BUF * EBC_TRB_SIZE;
|
||||
qdss->endless_req = req;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int enable_qdss_ebc_data_connection(struct f_qdss *qdss)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = msm_ep_config(qdss->port.data, qdss->endless_req, 1);
|
||||
if (ret)
|
||||
pr_err("msm_ep_config failed\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int set_qdss_data_connection(struct f_qdss *qdss, int enable)
|
||||
{
|
||||
struct usb_gadget *gadget;
|
||||
struct device *dev;
|
||||
int ret = 0;
|
||||
|
||||
if (!qdss) {
|
||||
pr_err("%s: qdss ptr is NULL\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
gadget = qdss->gadget;
|
||||
dev = gadget->dev.parent;
|
||||
|
||||
pr_debug("%s ch_type:%d\n", __func__, qdss->ch.ch_type);
|
||||
if (enable) {
|
||||
ret = enable_qdss_ebc_data_connection(qdss);
|
||||
} else {
|
||||
ret = msm_ep_unconfig(qdss->port.data);
|
||||
if (ret)
|
||||
pr_err("msm_ep_unconfig failed\n");
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
107
include/linux/usb/ccid_desc.h
Normal file
107
include/linux/usb/ccid_desc.h
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (c) 2011, 2017 The Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __LINUX_USB_CCID_DESC_H
|
||||
#define __LINUX_USB_CCID_DESC_H
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
/*CCID specification version 1.10*/
|
||||
#define CCID1_10 0x0110
|
||||
|
||||
#define SMART_CARD_DEVICE_CLASS 0x0B
|
||||
/* Smart Card Device Class Descriptor Type */
|
||||
#define CCID_DECRIPTOR_TYPE 0x21
|
||||
|
||||
/* Table 5.3-1 Summary of CCID Class Specific Request */
|
||||
#define CCIDGENERICREQ_ABORT 0x01
|
||||
#define CCIDGENERICREQ_GET_CLOCK_FREQUENCIES 0x02
|
||||
#define CCIDGENERICREQ_GET_DATA_RATES 0x03
|
||||
|
||||
/* 6.1 Command Pipe, Bulk-OUT Messages */
|
||||
#define PC_TO_RDR_ICCPOWERON 0x62
|
||||
#define PC_TO_RDR_ICCPOWEROFF 0x63
|
||||
#define PC_TO_RDR_GETSLOTSTATUS 0x65
|
||||
#define PC_TO_RDR_XFRBLOCK 0x6F
|
||||
#define PC_TO_RDR_GETPARAMETERS 0x6C
|
||||
#define PC_TO_RDR_RESETPARAMETERS 0x6D
|
||||
#define PC_TO_RDR_SETPARAMETERS 0x61
|
||||
#define PC_TO_RDR_ESCAPE 0x6B
|
||||
#define PC_TO_RDR_ICCCLOCK 0x6E
|
||||
#define PC_TO_RDR_T0APDU 0x6A
|
||||
#define PC_TO_RDR_SECURE 0x69
|
||||
#define PC_TO_RDR_MECHANICAL 0x71
|
||||
#define PC_TO_RDR_ABORT 0x72
|
||||
#define PC_TO_RDR_SETDATARATEANDCLOCKFREQUENCY 0x73
|
||||
|
||||
/* 6.2 Response Pipe, Bulk-IN Messages */
|
||||
#define RDR_TO_PC_DATABLOCK 0x80
|
||||
#define RDR_TO_PC_SLOTSTATUS 0x81
|
||||
#define RDR_TO_PC_PARAMETERS 0x82
|
||||
#define RDR_TO_PC_ESCAPE 0x83
|
||||
#define RDR_TO_PC_DATARATEANDCLOCKFREQUENCY 0x84
|
||||
|
||||
/* 6.3 Interrupt-IN Messages */
|
||||
#define RDR_TO_PC_NOTIFYSLOTCHANGE 0x50
|
||||
#define RDR_TO_PC_HARDWAREERROR 0x51
|
||||
|
||||
/* Table 6.2-2 Slot error register when bmCommandStatus = 1 */
|
||||
#define CMD_ABORTED 0xFF
|
||||
#define ICC_MUTE 0xFE
|
||||
#define XFR_PARITY_ERROR 0xFD
|
||||
#define XFR_OVERRUN 0xFC
|
||||
#define HW_ERROR 0xFB
|
||||
#define BAD_ATR_TS 0xF8
|
||||
#define BAD_ATR_TCK 0xF7
|
||||
#define ICC_PROTOCOL_NOT_SUPPORTED 0xF6
|
||||
#define ICC_CLASS_NOT_SUPPORTED 0xF5
|
||||
#define PROCEDURE_BYTE_CONFLICT 0xF4
|
||||
#define DEACTIVATED_PROTOCOL 0xF3
|
||||
#define BUSY_WITH_AUTO_SEQUENCE 0xF2
|
||||
#define PIN_TIMEOUT 0xF0
|
||||
#define PIN_CANCELLED 0xEF
|
||||
#define CMD_SLOT_BUSY 0xE0
|
||||
|
||||
/* CCID rev 1.1, p.27 */
|
||||
#define VOLTS_AUTO 0x00
|
||||
#define VOLTS_5_0 0x01
|
||||
#define VOLTS_3_0 0x02
|
||||
#define VOLTS_1_8 0x03
|
||||
|
||||
/* 6.3.1 RDR_to_PC_NotifySlotChange */
|
||||
#define ICC_NOT_PRESENT 0x00
|
||||
#define ICC_PRESENT 0x01
|
||||
#define ICC_CHANGE 0x02
|
||||
#define ICC_INSERTED_EVENT (ICC_PRESENT+ICC_CHANGE)
|
||||
|
||||
/* Identifies the length of type of subordinate descriptors of a CCID device
|
||||
* Table 5.1-1 Smart Card Device Class descriptors
|
||||
*/
|
||||
struct usb_ccid_class_descriptor {
|
||||
__u8 bLength;
|
||||
__u8 bDescriptorType;
|
||||
__u16 bcdCCID;
|
||||
__u8 bMaxSlotIndex;
|
||||
__u8 bVoltageSupport;
|
||||
__u32 dwProtocols;
|
||||
__u32 dwDefaultClock;
|
||||
__u32 dwMaximumClock;
|
||||
__u8 bNumClockSupported;
|
||||
__u32 dwDataRate;
|
||||
__u32 dwMaxDataRate;
|
||||
__u8 bNumDataRatesSupported;
|
||||
__u32 dwMaxIFSD;
|
||||
__u32 dwSynchProtocols;
|
||||
__u32 dwMechanical;
|
||||
__u32 dwFeatures;
|
||||
__u32 dwMaxCCIDMessageLength;
|
||||
__u8 bClassGetResponse;
|
||||
__u8 bClassEnvelope;
|
||||
__u16 wLcdLayout;
|
||||
__u8 bPINSupport;
|
||||
__u8 bMaxCCIDBusySlots;
|
||||
} __packed;
|
||||
#endif
|
||||
69
include/linux/usb/usb_qdss.h
Normal file
69
include/linux/usb/usb_qdss.h
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (c) 2012-2013, 2017-2020 The Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __LINUX_USB_QDSS_H
|
||||
#define __LINUX_USB_QDSS_H
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/scatterlist.h>
|
||||
|
||||
#define USB_QDSS_CH_EBC "qdss_ebc"
|
||||
#define USB_QDSS_CH_MDM "qdss_mdm"
|
||||
#define USB_QDSS_CH_SW "qdss_sw"
|
||||
|
||||
struct qdss_request {
|
||||
char *buf;
|
||||
int length;
|
||||
int actual;
|
||||
int status;
|
||||
void *context;
|
||||
struct scatterlist *sg;
|
||||
unsigned int num_sgs;
|
||||
};
|
||||
|
||||
struct usb_qdss_ch;
|
||||
|
||||
enum qdss_state {
|
||||
USB_QDSS_CONNECT,
|
||||
USB_QDSS_DISCONNECT,
|
||||
USB_QDSS_CTRL_READ_DONE,
|
||||
USB_QDSS_DATA_WRITE_DONE,
|
||||
};
|
||||
|
||||
#if IS_ENABLED(CONFIG_USB_F_QDSS)
|
||||
struct usb_qdss_ch *usb_qdss_open(const char *name, void *priv,
|
||||
void (*notify)(void *priv, unsigned int event,
|
||||
struct qdss_request *d_req, struct usb_qdss_ch *ch));
|
||||
void usb_qdss_close(struct usb_qdss_ch *ch);
|
||||
int usb_qdss_alloc_req(struct usb_qdss_ch *ch, int n_write);
|
||||
void usb_qdss_free_req(struct usb_qdss_ch *ch);
|
||||
int usb_qdss_write(struct usb_qdss_ch *ch, struct qdss_request *d_req);
|
||||
#else
|
||||
static inline struct usb_qdss_ch *usb_qdss_open(const char *name, void *priv,
|
||||
void (*n)(void *, unsigned int event,
|
||||
struct qdss_request *d, struct usb_qdss_ch *c))
|
||||
{
|
||||
return ERR_PTR(-ENODEV);
|
||||
}
|
||||
|
||||
static inline int usb_qdss_write(struct usb_qdss_ch *c, struct qdss_request *d)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static inline int usb_qdss_alloc_req(struct usb_qdss_ch *c, int n_wr, int n_rd)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
|
||||
static inline void usb_qdss_close(struct usb_qdss_ch *ch) { }
|
||||
|
||||
static inline void usb_qdss_free_req(struct usb_qdss_ch *ch) { }
|
||||
#endif /* CONFIG_USB_F_QDSS */
|
||||
|
||||
#endif
|
||||
Loading…
Reference in New Issue
Block a user