[ 3257.804953] usb 5-1: New USB device found, idVendor=0756, idProduct=0527, bcdDevice= 1.00
[ 3257.805008] usb 5-1: New USB device strings: Mfr=2, Product=1, SerialNumber=3
[ 3257.805018] usb 5-1: Product: MV-Medical
[ 3257.805027] usb 5-1: Manufacturer: MV
[ 3257.805035] usb 5-1: SerialNumber: MV0001
[ 3257.813216] uvcvideo: Found UVC 1.00 device USB-CAMERA
[ 3257.983766] usb 5-1: USB disconnect, device number 35
[ 3264.659381] Unable to handle kernel NULL pointer dereference at virtual address 0000000000000000
[ 3264.672181] Mem abort info:
[ 3264.674998] ESR = 0x96000006
[ 3264.678077] Exception class = DABT (current EL), IL = 32 bits
[ 3264.684042] SET = 0, FnV = 0
[ 3264.687125] EA = 0, S1PTW = 0
[ 3264.690289] Data abort info:
[ 3264.693254] ISV = 0, ISS = 0x00000006
[ 3264.697136] CM = 0, WnR = 0
[ 3264.700203] user pgtable: 4k pages, 39-bit VAs, pgdp = 00000000a561b7d1
[ 3264.706827] [0000000000000000] pgd=000000006f92f003, pud=000000006f92f003, pmd=0000000000000000
[ 3264.715556] Internal error: Oops: 96000006 [#1] SMP
[ 3264.720431] Modules linked in: rknpu(O) bcmdhd
[ 3264.724886] Process C100A_RK3568 (pid: 620, stack limit = 0x00000000de471811)
[ 3264.732020] CPU: 0 PID: 620 Comm: C100A_RK3568 Tainted: G O 4.19.172 #41
[ 3264.740018] Hardware name: evm-rk3568 base on Rockchip RK3568 EVB1 DDR4 V10 Board (DT)
[ 3264.748364] pstate: 80400009 (Nzcv daif +PAN -UAO)
[ 3264.753158] pc : usb_ifnum_to_if+0x48/0x64
[ 3264.757256] lr : usb_hcd_alloc_bandwidth+0x214/0x2c0
[ 3264.762219] sp : ffffff800b84b8b0
[ 3264.765532] x29: ffffff800b84b8b0 x28: ffffffc063f38018
[ 3264.770844] x27: 00000000ffffffb9 x26: ffffffc0703f4808
[ 3264.776157] x25: 0000000000000000 x24: ffffffc071c72000
[ 3264.781469] x23: 0000000000000001 x22: ffffffc0703f4808
[ 3264.786782] x21: 00000000ffffffb9 x20: ffffffc06f835800
[ 3264.792094] x19: 0000000000000001 x18: 000000000000000a
[ 3264.797406] x17: 0000000000000000 x16: 0000000000000000
[ 3264.802717] x15: 0000000000000c50 x14: 30763a6273753d53
[ 3264.808028] x13: 0000000000000196 x12: 0000000000000024
[ 3264.813340] x11: ffffffc07eecb268 x10: 0000000000000a40
[ 3264.818652] x9 : ffffff800b84b620 x8 : ffffffc06f8d6e60
[ 3264.823963] x7 : 000000000000089b x6 : 0000000000049b80
[ 3264.829274] x5 : ffffffc0702d3200 x4 : ffffffc07eecf3f0
[ 3264.834585] x3 : ffffffc0703e90a0 x2 : ffffffc0703e90a8
read ReadKeyVoltage error: Connection timed out
[ 3264.844139] x1 : 0000000000000001 x0 : 0000000000000000
//中间省略一部分打印信息
[ 3265.989113] Call trace:
[ 3265.991567] usb_ifnum_to_if+0x48/0x64
[ 3265.995318] usb_hcd_alloc_bandwidth+0x214/0x2c0
[ 3265.999933] usb_set_interface+0x1d0/0x324
[ 3266.004032] uvc_video_enable+0x50/0x13c
[ 3266.007951] uvc_start_streaming+0x30/0x6c
[ 3266.012051] vb2_start_streaming+0x8c/0x150
[ 3266.016232] vb2_core_streamon+0x168/0x174
[ 3266.020330] vb2_streamon+0x6c/0x74
[ 3266.023813] uvc_queue_streamon+0x38/0x58
[ 3266.027826] uvc_ioctl_streamon+0x48/0x70
[ 3266.031839] v4l_streamon+0x3c/0x4c
[ 3266.035324] __video_do_ioctl+0x2d0/0x3f8
[ 3266.039336] video_usercopy+0x3fc/0x668
[ 3266.043168] video_ioctl2+0x3c/0x4c
[ 3266.046651] v4l2_ioctl+0x50/0x74
[ 3266.049968] vfs_ioctl+0x58/0x68
[ 3266.053198] do_vfs_ioctl+0xb4/0x9d4
[ 3266.056777] ksys_ioctl+0x50/0x80
[ 3266.060091] __arm64_sys_ioctl+0x28/0x38
[ 3266.064010] el0_svc_common.constprop.0+0xe8/0x168
[ 3266.068794] el0_svc_handler+0x70/0x8c
[ 3266.072543] el0_svc+0x8/0xc
[ 3266.075428] Code: 54000061 d2800000 14000006 f8408460 (f9400001)
[ 3266.081513] ---[ end trace d992e9bf5856231b ]---
很明显这个问题是由于出现访问空指针导致了内核出现崩溃
Unable to handle kernel NULL pointer dereference at virtual address 0000000000000000
查看打印信息,定位到了这个函数
[ 3264.753158] pc : usb_ifnum_to_if+0x48/0x64
[ 3264.757256] lr : usb_hcd_alloc_bandwidth+0x214/0x2c0
这个是内核中的调用顺序,可能是尝试启用摄像头设备的视频流时出现问题(是由于分配 USB 带宽失败或设置 USB 接口时出现问题导致的。)
[ 3265.989113] Call trace:
[ 3265.991567] usb_ifnum_to_if+0x48/0x64
[ 3265.995318] usb_hcd_alloc_bandwidth+0x214/0x2c0
[ 3265.999933] usb_set_interface+0x1d0/0x324
[ 3266.004032] uvc_video_enable+0x50/0x13c
[ 3266.007951] uvc_start_streaming+0x30/0x6c
[ 3266.012051] vb2_start_streaming+0x8c/0x150
[ 3266.016232] vb2_core_streamon+0x168/0x174
[ 3266.020330] vb2_streamon+0x6c/0x74
[ 3266.023813] uvc_queue_streamon+0x38/0x58
[ 3266.027826] uvc_ioctl_streamon+0x48/0x70
[ 3266.031839] v4l_streamon+0x3c/0x4c
[ 3266.035324] __video_do_ioctl+0x2d0/0x3f8
[ 3266.039336] video_usercopy+0x3fc/0x668
[ 3266.043168] video_ioctl2+0x3c/0x4c
[ 3266.046651] v4l2_ioctl+0x50/0x74
[ 3266.049968] vfs_ioctl+0x58/0x68
[ 3266.053198] do_vfs_ioctl+0xb4/0x9d4
[ 3266.056777] ksys_ioctl+0x50/0x80
[ 3266.060091] __arm64_sys_ioctl+0x28/0x38
[ 3266.064010] el0_svc_common.constprop.0+0xe8/0x168
[ 3266.068794] el0_svc_handler+0x70/0x8c
[ 3266.072543] el0_svc+0x8/0xc
通过查看打印信息反馈的信息,相关源码如下所示
相关路径:
kernel/drivers/media/usb/uvc/uvc_driver.c
kernel/drivers/media/v4l2-core/v4l2-dev.c
kernel/drivers/usb/core/message.c
int usb_set_interface(struct usb_device *dev, int interface, int alternate)
{
struct usb_interface *iface;
struct usb_host_interface *alt;
struct usb_hcd *hcd = bus_to_hcd(dev->bus);
int i, ret, manual = 0;
unsigned int epaddr;
unsigned int pipe;
if (dev->state == USB_STATE_SUSPENDED)
return -EHOSTUNREACH;
iface = usb_ifnum_to_if(dev, interface);
if (!iface) {
dev_dbg(&dev->dev, "selecting invalid interface %d\n",
interface);
return -EINVAL;
}
if (iface->unregistering)
return -ENODEV;
alt = usb_altnum_to_altsetting(iface, alternate);
if (!alt) {
dev_warn(&dev->dev, "selecting invalid altsetting %d\n",
alternate);
return -EINVAL;
}
/*
* usb3 hosts configure the interface in usb_hcd_alloc_bandwidth,
* including freeing dropped endpoint ring buffers.
* Make sure the interface endpoints are flushed before that
*/
usb_disable_interface(dev, iface, false);
/* Make sure we have enough bandwidth for this alternate interface.
* Remove the current alt setting and add the new alt setting.
*/
mutex_lock(hcd->bandwidth_mutex);
/* Disable LPM, and re-enable it once the new alt setting is installed,
* so that the xHCI driver can recalculate the U1/U2 timeouts.
*/
if (usb_disable_lpm(dev)) {
dev_err(&iface->dev, "%s Failed to disable LPM\n", __func__);
mutex_unlock(hcd->bandwidth_mutex);
return -ENOMEM;
}
/* Changing alt-setting also frees any allocated streams */
for (i = 0; i < iface->cur_altsetting->desc.bNumEndpoints; i++)
iface->cur_altsetting->endpoint[i].streams = 0;
ret = usb_hcd_alloc_bandwidth(dev, NULL, iface->cur_altsetting, alt);
if (ret < 0) {
dev_info(&dev->dev, "Not enough bandwidth for altsetting %d\n",
alternate);
usb_enable_lpm(dev);
mutex_unlock(hcd->bandwidth_mutex);
return ret;
}
if (dev->quirks & USB_QUIRK_NO_SET_INTF)
ret = -EPIPE;
else
ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
USB_REQ_SET_INTERFACE, USB_RECIP_INTERFACE,
alternate, interface, NULL, 0, 5000);
/* 9.4.10 says devices don't need this and are free to STALL the
* request if the interface only has one alternate setting.
*/
if (ret == -EPIPE && iface->num_altsetting == 1) {
dev_dbg(&dev->dev,
"manual set_interface for iface %d, alt %d\n",
interface, alternate);
manual = 1;
} else if (ret < 0) {
/* Re-instate the old alt setting */
usb_hcd_alloc_bandwidth(dev, NULL, alt, iface->cur_altsetting);
usb_enable_lpm(dev);
mutex_unlock(hcd->bandwidth_mutex);
return ret;
}
mutex_unlock(hcd->bandwidth_mutex);
/* FIXME drivers shouldn't need to replicate/bugfix the logic here
* when they implement async or easily-killable versions of this or
* other "should-be-internal" functions (like clear_halt).
* should hcd+usbcore postprocess control requests?
*/
/* prevent submissions using previous endpoint settings */
if (iface->cur_altsetting != alt) {
remove_intf_ep_devs(iface);
usb_remove_sysfs_intf_files(iface);
}
usb_disable_interface(dev, iface, true);
iface->cur_altsetting = alt;
/* Now that the interface is installed, re-enable LPM. */
usb_unlocked_enable_lpm(dev);
/* If the interface only has one altsetting and the device didn't
* accept the request, we attempt to carry out the equivalent action
* by manually clearing the HALT feature for each endpoint in the
* new altsetting.
*/
if (manual) {
for (i = 0; i < alt->desc.bNumEndpoints; i++) {
epaddr = alt->endpoint[i].desc.bEndpointAddress;
pipe = __create_pipe(dev,
USB_ENDPOINT_NUMBER_MASK & epaddr) |
(usb_endpoint_out(epaddr) ?
USB_DIR_OUT : USB_DIR_IN);
usb_clear_halt(dev, pipe);
}
}
/* 9.1.1.5: reset toggles for all endpoints in the new altsetting
*
* Note:
* Despite EP0 is always present in all interfaces/AS, the list of
* endpoints from the descriptor does not contain EP0. Due to its
* omnipresence one might expect EP0 being considered "affected" by
* any SetInterface request and hence assume toggles need to be reset.
* However, EP0 toggles are re-synced for every individual transfer
* during the SETUP stage - hence EP0 toggles are "don't care" here.
* (Likewise, EP0 never "halts" on well designed devices.)
*/
usb_enable_interface(dev, iface, true);
if (device_is_registered(&iface->dev)) {
usb_create_sysfs_intf_files(iface);
create_intf_ep_devs(iface);
}
return 0;
}
EXPORT_SYMBOL_GPL(usb_set_interface);
kernel/drivers/usb/core/usb.c
struct usb_interface *usb_ifnum_to_if(const struct usb_device *dev,
unsigned ifnum)
{
struct usb_host_config *config = dev->actconfig;
int i;
if (!config)
{
return NULL;
}
for (i = 0; i < config->desc.bNumInterfaces; i++)
{
if (config->interface[i]->altsetting[0].desc.bInterfaceNumber == ifnum)
{
return config->interface[i];
}
}
return NULL;
}
EXPORT_SYMBOL_GPL(usb_ifnum_to_if);
kernel/include/linux/usb.h
struct usb_host_config {
struct usb_config_descriptor desc;
char *string; /* iConfiguration string, if present */
/* List of any Interface Association Descriptors in this
* configuration. */
struct usb_interface_assoc_descriptor *intf_assoc[USB_MAXIADS];
/* the interfaces associated with this configuration,
* stored in no particular order */
struct usb_interface *interface[USB_MAXINTERFACES];
/* Interface information available even when this is not the
* active configuration */
struct usb_interface_cache *intf_cache[USB_MAXINTERFACES];
unsigned char *extra; /* Extra descriptors */
int extralen;
};
适当的在怀疑会出现报错的地方添加一些打印信息,我个人比较喜欢使用“printk()”,
然后经过反复测试,最终在usb_ifnum_to_if(const struct usb_device *dev,
unsigned ifnum)函数上添加以下语句。
struct usb_interface *usb_ifnum_to_if(const struct usb_device *dev,
unsigned ifnum)
{
struct usb_host_config *config = dev->actconfig;
int i;
if (!config)
{
return NULL;
}
for (i = 0; i < config->desc.bNumInterfaces; i++)
{
+ if (!config->interface[i])
+ {
+ printk("......................................................\n");
+ return NULL;
+ }
if (config->interface[i]->altsetting[0].desc.bInterfaceNumber == ifnum)
{
return config->interface[i];
}
}
return NULL;
}