Linux如何重新枚举USB设备
USB(通用串行总线)设备在现代计算机中广泛使用。在linux系统中,USB设备会自动识别并枚举,以便可以在系统中使用。但是,有时设备可能无法正常枚举或者需要重新枚举才能重新识别,例如USB驱动程序异常、硬件升级或更换等。在这种情况下,重新枚举USB设备是一个常见的解决方案。
重新枚举USB设备可以通过多种方式完成,包括使用系统命令、使用udev工具和重载USB内核模块。下面我们将详细介绍这些方法。
方法1:使用系统命令
您可以使用系统命令来重新枚举USB设备。请按以下步骤操作:
1.打开终端并输入以下命令:
sudo systemctl stop systemd-udevd.service
该命令将停止udev守护程序。
2.卸载所有挂载的USB存储设备,以防止数据损坏。使用以下命令弹出所有已挂载的USB存储驱动器:
sudo eject /dev/sdX
其中“X”是USB存储设备的字母。
3.使用以下命令卸载已连接但未挂载的USB存储设备:
sudo umount /dev/sdX
4.断开所有的USB设备连接。
5.重新插入USB设备,您的计算机应该能够重新枚举它们。
6.使用以下命令重启udev守护程序:
sudo systemctl start systemd-udevd.service
方法2:使用udev工具
udev工具可以帮助管理和控制Linux系统中的设备。使用该工具重新枚举USB设备,只需要使用以下命令:
sudo udevadm trigger
运行此命令将强制udev重新扫描您的系统设备,包括USB设备。udev将通过检查系统设备目录的变化来更新设备信息,因此当您重新插入USB设备时,udev会自动重新枚举它们。
方法3:重载USB内核模块
最后一种解决方案是通过重新加载USB内核模块来重新枚举USB设备。使用以下命令:
sudo rmmod u-storage
sudo modprobe u-storage
之一个命令将卸载USB存储内核模块,第二个命令将重新加载该模块。在重新加载USB内核模块后,您应该能够重新枚举USB设备。
结论
重新枚举USB设备是解决在Linux系统中使用USB设备问题的有效方法。您可以使用系统命令、udev工具和重新加载USB内核模块这三种方法中的任何一种。
不过,在执行这些方法之前,请务必备份您的数据并谨慎处理USB设备,以防止数据丢失或设备损坏。同时,如果您不确定该如何操作,请先咨询专业人士。
相关问题拓展阅读:
- 怎么能将linux下自动分配的u0改为u1
怎么能将linux下自动分配的u0改为u1
你好,方法如下:
写一个USB的驱动程序最 基本的要做四件事:
驱动程序要支持的设备、注册USB驱动程序、探测和断开、提交和控制urb(USB请求块)
驱动程序支持的设备:有一个结构体struct u_device_id,这个结构体提供了一列不同类型的该驱动程序支持的USB设备,对于一个只控制一个特定的USB设备的驱动程序来说,struct u_device_id表被定义为:
/* 驱动程序支持的设备列表 */
static struct u_device_id skel_table = {
{ USB_DEVICE(USB_SKEL_VENDOR_ID, USB_SKEL_PRODUCT_ID) },
{ } /* 终止入口 */
};
MODULE_DEVICE_TABLE (u, skel_table);
对 于PC驱动程序,MODULE_DEVICE_TABLE是必需的,而且u必需为该宏的之一个值,而USB_SKEL_VENDOR_ID和 USB_SKEL_PRODUCT_ID就是这个特殊设备的制造商和产品的ID了,我们在程序中把定义的值改为我们这款USB的,如:
/* 定义制造商和产品的ID号 */
#define USB_SKEL_VENDOR_ID 0x1234
#define USB_SKEL_PRODUCT_ID 0x2345
这两个值可以通过命令lsu,当然你得先把USB设备先插到主机上了。或者查看厂商的USB设备的手册也能得到,在我机器上运行lsu是这样的结果:
Bus 004 Device 001: ID 0000:0000
Bus 003 Device 002: ID 1234:2345 Abc Corp.
Bus 002 Device 001: ID 0000:0000
Bus 001 Device 001: ID 0000:0000
得到这两个值后把它定义到程序李卜迅里就可以了。
注册USB驱动程序:所 有的USB驱动程序都必须创建的结构体是struct u_driver。这个结构体必须由USB驱动程序来填写,包括许多回调函数和变量,它们向USB核心代码描述USB驱动程序。创建一个有效的 struct u_driver结构体,只须要初始化五个字段就可以了,在框架程序中是这样的:
static struct u_driver skel_driver = {
.owner = THIS_MODULE,
.name =”skeleton”,
.probe = skel_probe,
.disconnect = skel_disconnect,
.id_table = skel_table,
};
探测和断开:当 一个设备被安装而USB核心认为该驱动程序应该处理时,探测函数被调用,探测函数检查传递给它的设备信息,确定驱动程序是否真的适合该设备。当驱动程序因 为某种原因不应该控制设备时,断开函数被调用,它可以做一些清理工作。探测回调函数中,USB驱动程序初始化任何可能用于控制USB设备的局部结构体,它 还把所需的任何设备相关信息保存到一个局部结构体弊仔中,
提交和控制urb:当驱动程序有数据要发送到USB设备时(大多数情况是在驱动程序的写函数中),要分配一个urb来把数据传输给设备:
/* 创建一个urb,并且给它分配一个缓存*/
urb = u_alloc_urb(0, GFP_KERNEL);
if (!urb) {
retval = -ENOMEM;
goto error;
}
当urb被成功分配后,还要创建一个DMA缓冲区来以高效的方式发送数据到设备,传递给驱动程序的数据要复制到这块缓冲中去:
buf = u_buffer_alloc(dev->udev, count, GFP_KERNEL, &urb->transfer_dma);
if (!buf) {
retval = -ENOMEM;
goto error;
}
if (copy_from_user(buf, user_buffer, count)) {
retval = -EFAULT;
goto error;
}
当数据从用户空间正确复制到局部哪此缓冲区后,urb必须在可以被提交给USB核心之前被正确初始化:
/* 初始化urb */
u_fill_bulk_urb(urb, dev->udev,
u_sndbulkpipe(dev->udev, dev->bulk_out_endpointAddr),
buf, count, skel_write_bulk_callback, dev);
urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
然后urb就可以被提交给USB核心以传输到设备了:
/* 把数据从批量OUT端口发出 */
retval = u_submit_urb(urb, GFP_KERNEL);
if (retval) {
err(“%s – failed submitting write urb, error %d”, __FUNCTION__, retval);
goto error;
}
当urb被成功传输到USB设备之后,urb回调函数将被USB核心调用,在我们的例子中,我们初始化urb,使它指向skel_write_bulk_callback函数,以下就是该函数:
static void skel_write_bulk_callback(struct urb *urb, struct pt_regs *regs)
{
struct u_skel *dev;
dev = (struct u_skel *)urb->context;
if (urb->status &&
!(urb->status == -ENOENT ||
urb->status == -ECONNRESET ||
urb->status == -ESHUTDOWN)) {
dbg(“%s – nonzero write bulk status received: %d”,
__FUNCTION__, urb->status);
}
/* 释放已分配的缓冲区 */
u_buffer_free(urb->dev, urb->transfer_buffer_length,
urb->transfer_buffer, urb->transfer_dma);
}
有时候USB驱动程序只是要发送或者接收一些简单的数据,驱动程序也可以不用urb来进行数据的传输,这是里涉及到两个简单的接口函数:u_bulk_msg和u_control_msg ,在这个USB框架程序里读操作就是这样的一个应用:
/* 进行阻塞的批量读以从设备获取数据 */
retval = u_bulk_msg(dev->udev,
u_rcvbulkpipe(dev->udev, dev->bulk_in_endpointAddr),
dev->bulk_in_buffer,
min(dev->bulk_in_size, count),
&count, HZ*10);
/*如果读成功,复制到用户空间 */
if (!retval) {
if (copy_to_user(buffer, dev->bulk_in_buffer, count))
retval = -EFAULT;
else
retval = count;
}
u_bulk_msg接口函数的定义如下:
int u_bulk_msg(struct u_device *u_dev,unsigned int pipe,
void *data,int len,int *actual_length,int timeout);
其参数为:
struct u_device *u_dev:指向批量消息所发送的目标USB设备指针。
unsigned int pipe:批量消息所发送目标USB设备的特定端点,此值是调用u_sndbulkpipe或者u_rcvbulkpipe来创建的。
void *data:如果是一个OUT端点,它是指向即将发送到设备的数据的指针。如果是IN端点,它是指向从设备读取的数据应该存放的位置的指针。
int len:data参数所指缓冲区的大小。
int *actual_length:指向保存实际传输字节数的位置的指针,至于是传输到设备还是从设备接收取决于端点的方向。
int timeout:以Jiffies为单位的等待的超时时间,如果该值为0,该函数一直等待消息的结束。
如果该接口函数调用成功,返回值为0,否则返回一个负的错误值。
u_control_msg接口函数定义如下:
int u_control_msg(struct u_device *dev,unsigned int pipe,__u8 request,__u8requesttype,__u16 value,__u16 index,void *data,__u16 size,int timeout)
除了允许驱动程序发送和接收USB控制消息之外,u_control_msg函数的运作和u_bulk_msg函数类似,其参数和u_bulk_msg的参数有几个重要区别:
struct u_device *dev:指向控制消息所发送的目标USB设备的指针。
unsigned int pipe:控制消息所发送的目标USB设备的特定端点,该值是调用u_sndctrlpipe或u_rcvctrlpipe来创建的。
__u8 request:控制消息的USB请求值。
__u8 requesttype:控制消息的USB请求类型值。
__u16 value:控制消息的USB消息值。
__u16 index:控制消息的USB消息索引值。
void *data:如果是一个OUT端点,它是指身即将发送到设备的数据的指针。如果是一个IN端点,它是指向从设备读取的数据应该存放的位置的指针。
__u16 size:data参数所指缓冲区的大小。
int timeout:以Jiffies为单位的应该等待的超时时间,如果为0,该函数将一直等待消息结束。
如果该接口函数调用成功,返回传输到设备或者从设备读取的字节数;如果不成功它返回一个负的错误值。
这两个接口函数都不能在一个中断上下文中或者持有自旋锁的情况下调用,同样,该函数也不能被任何其它函数取消,使用时要谨慎。
我们要给未知的USB设备写驱动程序,只需要把这个框架程序稍做修改就可以用了,前面我们已经说过要修改制造商和产品的ID号,把0xfff0这两个值改为未知USB的ID号。
#define USB_SKEL_VENDOR_IDxfff0
#define USB_SKEL_PRODUCT_ID 0xfff0
还 有就是在探测函数中把需要探测的接口端点类型写好,在这个框架程序中只探测了批量(USB_ENDPOINT_XFER_BULK)IN和OUT端点,可 以在此处使用掩码(USB_ENDPOINT_XFERTYPE_MASK)让其探测其它的端点类型,驱动程序会对USB设备的每一个接口进行一次探测, 当探测成功后,驱动程序就被绑定到这个接口上。再有就是urb的初始化问题,如果你只写简单的USB驱动,这块不用多加考虑,框架程序里的东西已经够用 了,这里我们简单介绍三个初始化urb的辅助函数:
u_fill_int_urb :它的函数原型是这样的:
void u_fill_int_urb(struct urb *urb,struct u_device *dev,
unsigned int pipe,void *transfer_buff,
int buffer_length,u_complete_t complete,
void *context,int interval);
这个函数用来正确的初始化即将被发送到USB设备的中断端点的urb。
u_fill_bulk_urb :它的函数原型是这样的:
void u_fill_bulk_urb(struct urb *urb,struct u_device *dev,
unsigned int pipe,void *transfer_buffer,
int buffer_length,u_complete_t complete)
这个函数是用来正确的初始化批量urb端点的。
u_fill_control_urb :它的函数原型是这样的:
void u_fill_control_urb(struct urb *urb,struct u_device *dev,unsigned int pipe,unsigned char *setup_packet,void *transfer_buffer,int buffer_length,u_complete_t complete,void *context);
这个函数是用来正确初始化控制urb端点的。
还有一个初始化等时urb的,它现在还没有初始化函数,所以它们在被提交到USB核心前,必须在驱动程序中手工地进行初始化,可以参考内核源代码树下的/usr/src/~/drivers/u/media下的konicawc.c文件。
linux 重新枚举u设备的介绍就聊到这里吧,感谢你花时间阅读本站内容,更多关于linux 重新枚举u设备,「Linux如何重新枚举USB设备」,怎么能将linux下自动分配的u0改为u1的信息别忘了在本站进行查找喔。