Linux kernel U盘识别流程

用U盘在电脑端启动linux内核(kernel)最小系统 本文记录我制作u盘驱动的过程,按照这篇教程,你可以得到一个装有kernel 版本linux-5.8.9操作系统的u盘,可以启动x86架构的主板,文件系统使用busybox制作。 o制作u盘启动linux系统 o所需工具 o第一步为u盘分区 o第二步为u盘安装grub o第三步编译kernel o第四步制作文件系统 所需工具 高质量u盘一个,容量大于4G linux-5.8.9.tar.xz源码 busybox-1.32.0.tar.bz2源码 装有ubuntu系统pc一台 注意: u盘不能使用那 阅读详情
AI摘要

本文详述了USB设备从插入到驱动加载的全过程,包括USB枚举机制、内核线程检测、hub_events函数解析等内容。重点介绍了hub_port_init函数如何实现设备复位、地址分配和获取设备描述符等功能。

一、正常USB枚举及断开的log

正常U盘插入和拔掉Kernel log的打印消息如下:
U盘插入

[  220.836836] usb 2-1: new high-speed USB device number 4 using xxx-ehci
[  220.984187] usb-storage 2-1:1.0: USB Mass Storage device detected
[  220.990977] scsi1 : usb-storage 2-1:1.0
[  220.998948] scsi 1:0:0:0: Direct-Access     Kingston DataTraveler 3.0 PMAP PQ: 0 ANSI: 6
[  221.010354] sd 1:0:0:0: [sda] 30277632 512-byte logical blocks: (15.5 GB/14.4 GiB)
[  221.018712] sd 1:0:0:0: [sda] Write Protect is off
[  221.023549] sd 1:0:0:0: [sda] Mode Sense: 45 00 00 00
[  221.029224] sd 1:0:0:0: [sda] Write cache: disabled, read cache: enabled, doesn't support DPO or FUA
[  221.055488]  sda: sda1
[  221.068303] sd 1:0:0:0: [sda] Attached SCSI removable disk

U盘拔掉

[  226.396127] usb 2-1: USB disconnect, device number 4

下面的USB设备的枚举过程将根据 http://blog.youkuaiyun.com/myarrow/article/details/8270029 这篇文章进行梳理。


二、内核线程检测Hub端口的变化

在初始化USB subsystem(usb.c 中的usb_init())的时候,会去做usb hub的初始化usb_hub_init(),在那里面会去开启一个内核线程hub_thread() ,在该函数中会去循环判断链表是否非空以及是否唤醒等待队列。如果条件都满足,就会去执行 hub_events() 的内容。

static int hub_thread(void *__unused)
{
    /* khubd needs to be freezable to avoid interfering with USB-PERSIST
     * port handover.  Otherwise it might see that a full-speed device
     * was gone before the EHCI controller had handed its port over to
     * the companion full-speed controller.
     */
    set_freezable();

    do {
        hub_events();
        wait_event_freezable(khubd_wait,
                !list_empty(&hub_event_list) ||
                kthread_should_stop());
    } while (!kthread_should_stop() || !list_empty(&hub_event_list));

    pr_debug("%s: khubd exiting\n", usbcore_name);
    return 0;
}

在hub_irq() 检测到设备插入唤醒队列:
对于4.15版本的Kernel来说,在hub_irq()函数中会调用 kick_hub_wq() 中让 hub_wq 的工作队列开始 hub_event() 工作。

if (queue_work(hub_wq, &hub->events))
    return;

INIT_WORK(&hub->events, hub_event);

这里的hub_event()函数执行的内容就跟我们经常见的hub_events()的内容是一致的。只是名字变了。

对于3.14版本的Kernel来说,在 kick_khubd() 中往 hub->event_list的链表中添加一个 hub_event_list 的事件,并且唤醒 khubd_wait 等待队列。

// kick_khubd()

if (!hub->disconnected && list_empty(&hub->event_list)) {
        list_add_tail(&hub->event_list, &hub_event_list);

        /* Suppress autosuspend until khubd runs */
        usb_autopm_get_interface_no_resume(
                to_usb_interface(hub->intfdev));
        wake_up(&khubd_wait);
    }

后面的内容都是基于3.14版本的Kernel 中的hub_events()来说明。


三、hub_events()函数

所有有关hub端口的变化最终都会在hub_events()中执行,以此来做相应的动作。比如枚举连接上来的USB设备,在 hub_events() 中通过调用 hub_port_status() 来获得 portchange 和 portstatus 的状态并设置 connect_change变量。connect_change 为1表示Hub上的端口状态有变化(设备插入或拔出),进而执行 hub_port_connect_change() 的内容。

hub_port_connect_change()中调用usb_alloc_dev()为新的USB设备申请资源,并进行一些初始化。设置设备的状态为 USB_STATE_POWERED,接下来会枚举USB设备(复位、握手、获取描述符),当USB设备枚举完成后就会触发加载与之对应的USB接口驱动。

for (i = 1; i <= hdev->maxchild; i++) {
    if (test_bit(i, hub->busy_bits))
        continue;
    connect_change = test_bit(i, hub->change_bits);
    wakeup_change = test_and_clear_bit(i, hub->wakeup_bits);
    …….

    if (connect_change)
        hub_port_connect_change(hub, i,
            portstatus, portchange);
}

在这里依次判断hub上的端口状态变化,如果发生状态变化,那么就设置connect_change=1,并调用hub_port_connect_change()函数去处理该端口。这个函数里面更确切的说主要来处理枚举过程。


四、hub_port_connect_change()函数

/* Handle physical or logical connection change events.
 * This routine is called when:
 *  a port connection-change occurs;
 *  a port enable-change occurs (often caused by EMI);
 *  usb_reset_and_verify_device() encounters changed descriptors (as from
 *      a firmware download)
 * caller already locked the hub
 */
static void hub_port_connect_change(struct usb_hub *hub, int port1,
                    u16 portstatus, u16 portchange)

其中portstatus和portchange有如下几种状态,这些状态都在USB协议中第11章做了明确的规定。

portstatus各个bit的意义如下:

/*
 * wPortStatus bit field
 * See USB 2.0 spec Table 11-21
 */
#define USB_PORT_STAT_CONNECTION    0x0001
#define USB_PORT_STAT_ENABLE        0x0002
#define USB_PORT_STAT_SUSPEND       0x0004
#define USB_PORT_STAT_OVERCURRENT   0x0008
#define USB_PORT_STAT_RESET     0x0010
#define USB_PORT_STAT_L1        0x0020
/* bits 6 to 7 are reserved */
#define USB_PORT_STAT_POWER     0x0100
#define USB_PORT_STAT_LOW_SPEED     0x0200
#define USB_PORT_STAT_HIGH_SPEED        0x0400
#define USB_PORT_STAT_TEST              0x0800
#define USB_PORT_STAT_INDICATOR         0x1000
/* bits 13 to 15 are reserved */

这里写图片描述
这里写图片描述


portchange各个bit的意义如下:

/*
 * wPortChange bit field
 * See USB 2.0 spec Table 11-22 and USB 2.0 LPM ECN Table-4.10
 * Bits 0 to 5 shown, bits 6 to 15 are reserved
 */
#define USB_PORT_STAT_C_CONNECTION  0x0001
#define USB_PORT_STAT_C_ENABLE      0x0002
#define USB_PORT_STAT_C_SUSPEND     0x0004
#define USB_PORT_STAT_C_OVERCURRENT 0x0008
#define USB_PORT_STAT_C_RESET       0x0010
#define USB_PORT_STAT_C_L1      0x0020
/*
 * USB 3.0 wPortChange bit fields
 * See USB 3.0 spec Table 10-11
 */
#define USB_PORT_STAT_C_BH_RESET    0x0020
#define USB_PORT_STAT_C_LINK_STATE  0x0040
#define USB_PORT_STAT_C_CONFIG_ERROR    0x0080

这里写图片描述

这个函数主要处理hub上某个端口的变化,通过判断portstatus和portchange来判断当前hub上端口的状态,如果有设备连接上,要为其分配struct usb_devices并将其添加到USB总线上,之后会匹配对应的USB接口驱动。接下来说说这个函数内主要的做的事情。


1、USB设备连接去抖动

if (portchange & (USB_PORT_STAT_C_CONNECTION |
                USB_PORT_STAT_C_ENABLE)) {
        status = hub_port_debounce_be_stable(hub, port1);
        if (status < 0) {
            if (status != -ENODEV && printk_ratelimit())
                dev_err(hub_dev, "connect-debounce failed, "
                        "port %d disabled\n", port1);
            portstatus &= ~USB_PORT_STAT_CONNECTION;
        } else {
            portstatus = status;
        }
    }

如果判断portchanged的状态是USB_PORT_STAT_C_CONNECTION或USB_PORT_STAT_C_ENABLE表示有新的USB设备连接。之后会调用hub_port_debounce_be_stable()不间断检测端口的状态,实际上就是一个去抖动的过程,保证USB设备完全连接上。在USB协议上规定至少100ms的去抖动时间。

static inline int hub_port_debounce_be_stable(struct usb_hub *hub,
        int port1)
{
    return hub_port_debounce(hub, port1, false);
}

/* USB 2.0 spec, 7.1.7.3 / fig 7-29:
 *
 * Between connect detection and reset signaling there must be a delay
 * of 100ms at least for debounce and power-settling.  The corresponding
 * timer shall restart whenever the downstream port detects a disconnect.
 *
 * Apparently there are some bluetooth and irda-dongles and a number of
 * low-speed devices for which this debounce period may last over a second.
 * Not covered by the spec - but easy to deal with.
 *
 * This implementation uses a 1500ms total debounce timeout; if the
 * connection isn't stable by then it returns -ETIMEDOUT.  It checks
 * every 25ms for transient disconnects.  When the port status has been
 * unchanged for 100ms it returns the port status.
 */

int hub_port_debounce(struct usb_hub *hub, int port1, bool must_be_connected)

2、枚举USB设备

在这个for循环中会循环SET_CONFIG_TRIES次去枚举USB设备,一旦枚举成功就跳出循环,如果SET_CONFIG_TRIES次都失败,那么就会报告枚举失败,USB设备无法识别。关于这部分代码的内容,详见下面的分析:

for (i = 0; i < SET_CONFIG_TRIES; i++) {

        /* reallocate for each attempt, since references
         * to the previous one can escape in various ways
         */
        udev = usb_alloc_dev(hdev, hdev->bus, port1);
        if (!udev) {
            dev_err (hub_dev,
                "couldn't allocate port %d usb_device\n",
                port1);
            goto done;
        }

        usb_set_device_state(udev, USB_STATE_POWERED);
        udev->bus_mA = hub->mA_per_port;
        udev->level = hdev->level + 1;
        udev->wusb = hub_is_wusb(hub);

        /* Only USB 3.0 devices are connected to SuperSpeed hubs. */
        if (hub_is_superspeed(hub->hdev))
            udev->speed = USB_SPEED_SUPER;
        else
            udev->speed = USB_SPEED_UNKNOWN;

        choose_devnum(udev);
        if (udev->devnum <= 0) {
            status = -ENOTCONN; /* Don't retry */
            goto loop;
        }

        /* reset (non-USB 3.0 devices) and get descriptor */
        status = hub_port_init(hub, udev, port1, i);
        if (status < 0)
            goto loop;

        usb_detect_quirks(udev);
        if (udev->quirks & USB_QUIRK_DELAY_INIT)
            msleep(1000);

        /* consecutive bus-powered hubs aren't reliable; they can
         * violate the voltage drop budget.  if the new child has
         * a "powered" LED, users should notice we didn't enable it
         * (without reading syslog), even without per-port LEDs
         * on the parent.
         */
        if (udev->descriptor.bDeviceClass == USB_CLASS_HUB
                && udev->bus_mA <= unit_load) {
            u16 devstat;

            status = usb_get_status(udev, USB_RECIP_DEVICE, 0,
                    &devstat);
            if (status) {
                dev_dbg(&udev->dev, "get status %d ?\n", status);
                goto loop_disable;
            }
            if ((devstat & (1 << USB_DEVICE_SELF_POWERED)) == 0) {
                dev_err(&udev->dev,
                    "can't connect bus-powered hub "
                    "to this port\n");
                if (hub->has_indicators) {
                    hub->indicator[port1-1] =
                        INDICATOR_AMBER_BLINK;
                    schedule_delayed_work (&hub->leds, 0);
                }
                status = -ENOTCONN; /* Don't retry */
                goto loop_disable;
            }
        }

        /* check for devices running slower than they could */
        if (le16_to_cpu(udev->descriptor.bcdUSB) >= 0x0200
                && udev->speed == USB_SPEED_FULL
                && highspeed_hubs != 0)
            check_highspeed (hub, udev, port1);

        /* Store the parent's children[] pointer.  At this point
         * udev becomes globally accessible, although presumably
         * no one will look at it until hdev is unlocked.
         */
        status = 0;

        /* We mustn't add new devices if the parent hub has
         * been disconnected; we would race with the
         * recursively_mark_NOTATTACHED() routine.
         */
        spin_lock_irq(&device_state_lock);
        if (hdev->state == USB_STATE_NOTATTACHED)
            status = -ENOTCONN;
        else
            hub->ports[port1 - 1]->child = udev;
        spin_unlock_irq(&device_state_lock);

        /* Run it through the hoops (find a driver, etc) */
        if (!status) {
            status = usb_new_device(udev);
            if (status) {
                spin_lock_irq(&device_state_lock);
                hub->ports[port1 - 1]->child = NULL;
                spin_unlock_irq(&device_state_lock);
            }
        }

        if (status)
            goto loop_disable;

        status = hub_power_remaining(hub);
        if (status)
            dev_dbg(hub_dev, "%dmA power budget left\n", status);

        return;

loop_disable:
        hub_port_disable(hub, port1, 1);
loop:
        usb_ep0_reinit(udev);
        release_devnum(udev);
        hub_free_dev(udev);
        usb_put_dev(udev);
        if ((status == -ENOTCONN) || (status == -ENOTSUPP))
            break;
    }

3、为端口上的USB设备分配struct usb_device

/* reallocate for each attempt, since references
* to the previous one can escape in various ways
*/
udev = usb_alloc_dev(hdev, hdev->bus, port1);
if (!udev) {
    dev_err (hub_dev,
        "couldn't allocate port %d usb_device\n",
        port1);
    goto done;
}

执行usb_alloc_dev()会将这个USB设备挂到USB总线上,并将USB设备的连接状态置为USB_STATE_ATTACHED。USB的总线是为了USB设备和USB接口驱动匹配用的。usb_alloc_dev()大概内容如下:

device_initialize(&dev->dev);
dev->dev.bus = &usb_bus_type;
dev->dev.type = &usb_device_type;
dev->dev.groups = usb_device_groups;
dev->dev.dma_mask = bus->controller->dma_mask;
set_dev_node(&dev->dev, dev_to_node(bus->controller));
dev->state = USB_STATE_ATTACHED;

4、设置USB设备的状态为USB_STATE_POWERED

usb_set_device_state(udev, USB_STATE_POWERED);
udev->bus_mA = hub->mA_per_port;
udev->level = hdev->level + 1;
udev->wusb = hub_is_wusb(hub);

前面在usb_alloc_dev()函数中设置USB设备的状态为USB_STATE_ATTACHED,之后设置USB设备从总线上汲取的电流大小以及设置USB设备的状态为USB_STATE_POWERED。这个时序满足USB协议第9章规定的USB设备状态迁移。也可以参照我的上一篇文章USB设备状态设置– usb_gadget_set_state()


5、为USB设备分配编号

调用 choose_devnum() 为设备分配一个小于128的编号,下面打印的编号4就是这个函数产生的。

[  220.836836] usb 2-1: new high-speed USB device number 4 using xxx-ehci
choose_devnum(udev);
if (udev->devnum <= 0) {
    status = -ENOTCONN; /* Don't retry */
    goto loop;
}

6、调用 hub_port_init() 对插入的USB设备进行复位、分配地址、获取默认管道的最大包长度、获取设备描述符。

在后面将着重讲hub_port_init()函数的内容。


7、调用usb_new_device()

该函数的定义如下:

/**
 * usb_new_device - perform initial device setup (usbcore-internal)
 * @udev: newly addressed device (in ADDRESS state)
 *
 * This is called with devices which have been detected but not fully
 * enumerated.  The device descriptor is available, but not descriptors
 * for any device configuration.  The caller must have locked either
 * the parent hub (if udev is a normal device) or else the
 * usb_bus_list_lock (if udev is a root hub).  The parent's pointer to
 * udev has already been installed, but udev is not yet visible through
 * sysfs or other filesystem code.
 *
 * This call is synchronous, and may not be used in an interrupt context.
 *
 * Only the hub driver or root-hub registrar should ever call this.
 *
 * Return: Whether the device is configured properly or not. Zero if the
 * interface was registered with the driver core; else a negative errno
 * value.
 *
 */
int usb_new_device(struct usb_device *udev)

该函数主要做了以下几件事:

  • 调用usb_enumerate_device()获取USB设备的描述符(配置描述符、接口描述符);

  • 调用announce_device()告诉世界新的USB设备添加了;

static void announce_device(struct usb_device *udev)
{
    dev_info(&udev->dev, "New USB device found, idVendor=%04x, idProduct=%04x\n",
        le16_to_cpu(udev->descriptor.idVendor),
        le16_to_cpu(udev->descriptor.idProduct));
    dev_info(&udev->dev,
        "New USB device strings: Mfr=%d, Product=%d, SerialNumber=%d\n",
        udev->descriptor.iManufacturer,
        udev->descriptor.iProduct,
        udev->descriptor.iSerialNumber);
    show_string(udev, "Product", udev->product);
    show_string(udev, "Manufacturer", udev->manufacturer);
    show_string(udev, "SerialNumber", udev->serial);
}
  • 将已枚举完成且USB状态为USB_STATE_ADDRESS的USB设备通过设备模型提供的接口device_add()进行设备的注册;

  • 为该USB设备创建对应的sysfs接口;

至此,当调用usb_new_device()成功之后,那么表明连接到Hub上的某个端口的USB设备已经完全的被枚举了,USB Core已经完全知道该USB设备的所有特性(通过设备描述符知道)。那么之后就是根据该USB设备的配置描述符会匹配一个或多个与之对应的USB接口驱动。在讲USB接口驱动之前,我们再详细的说明一下hub_port_init()这个函数。


五、hub_port_init()函数

这个函数也是异常的庞大,先看看它的介绍:

/* Reset device, (re)assign address, get device descriptor.
 * Device connection must be stable, no more debouncing needed.
 * Returns device in USB_STATE_ADDRESS, except on error.
 *
 * If this is called for an already-existing device (as part of
 * usb_reset_and_verify_device), the caller must own the device lock.  For a
 * newly detected device that is not accessible through any global
 * pointers, it's not necessary to lock the device.
 */
static int
hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
        int retry_counter)

该函数的主要功能是复位设备、分配地址、获取设备描述符。如果成功,那么返回USB设备的状态USB_STATE_ADDRESS。接下来讲讲这个函数中的具体细节吧。


1、调用 hub_port_reset() 对设备复位

/* Reset the device; full speed may morph to high speed */
    /* FIXME a USB 2.0 device may morph into SuperSpeed on reset. */
    retval = hub_port_reset(hub, port1, udev, delay, false);
    if (retval < 0)     /* error or disconnect */
        goto fail;
    /* success, speed is known */

复位之后,USB Host就可以确定该USB设备的速度是Full Speed或者是High Speed了。然后设置USB设备状态为USB_STATE_DEFAULT(这个是在hub_port_finish_reset()函数中设置的)。

具体速度的判断是Hub通过与设备之间的握手信号(JK序列)来判断此USB设备是全速设备(Full Speed)还是高速设备(High Speed)。当hub撤销了复位信号,设备就处于默认/空闲状态(Default state),准备接收主机发来的请求。设备和主机之间的通信通过控制传输,默认地址0,端点号0进行。此时,设备能从总线上得到的最大电流是100mA。(所有的USB设备在总线复位后其地址都为0,这样主机就可以跟那些刚刚插入的设备通过地址0通信)。

关于USB设备的识别可以参照之前的文章:USB 全速/高速设备识别信号分析


2、获取默认管道的最大包长度

/* USB 2.0 section 5.5.3 talks about ep0 maxpacket ...
 * it's fixed size except for full speed devices.
 * For Wireless USB devices, ep0 max packet is always 512 (tho
 * reported as 0xff in the device descriptor). WUSB1.0[4.8.1].
 */
switch (udev->speed) {
case USB_SPEED_SUPER:
case USB_SPEED_WIRELESS:    /* fixed at 512 */
    udev->ep0.desc.wMaxPacketSize = cpu_to_le16(512);
    break;
case USB_SPEED_HIGH:        /* fixed at 64 */
    udev->ep0.desc.wMaxPacketSize = cpu_to_le16(64);
    break;
case USB_SPEED_FULL:        /* 8, 16, 32, or 64 */
    /* to determine the ep0 maxpacket size, try to read
     * the device descriptor to get bMaxPacketSize0 and
     * then correct our initial guess.
     */
    udev->ep0.desc.wMaxPacketSize = cpu_to_le16(64);
    break;
case USB_SPEED_LOW:     /* fixed at 8 */
    udev->ep0.desc.wMaxPacketSize = cpu_to_le16(8);
    break;
default:
    goto fail;
}

3、打印该USB设备的提示语句

接下来就是打印如下的log,标记哪个总线上的哪个端口上连接了哪种速度的USB设备,分配的设备编号是多少,与其对应的USB主控制器名称是什么。

[  220.836836] usb 2-1: new high-speed USB device number 4 using xxx-ehci
// hub.c   hub_port_init()
if (udev->speed != USB_SPEED_SUPER)
    dev_info(&udev->dev,
        "%s %s USB device number %d using %s\n",
         (udev->config) ? "reset" : "new", speed,
        devnum, udev->bus->controller->driver->name);

在 hub_port_init() 中会打印这句话,走到这一步,表示已经识别上一个高速的USB设备。


4、调用 hub_set_address() 给USB设备分配一个地址

为USB设置分配一个地址并配置设备的状态为 USB_STATE_ADDRESS。之后就启用新地址继续与主机通信。这个地址对于设备来说是终生制的,设备在,地址在;设备消失(被拔出,复位,系统重启),地址被收回。同一个设备当再次被枚举后得到的地址不一定是上次那个了。


5、调用 usb_get_device_descriptor() 获取设备描述符

获取设备描述符之后USB Host就知道连接到Hub上端口的USB设备是属于哪一类了。主机发送 Get_Descriptor请求到新地址读取设备描述符,这次主机发送Get_Descriptor请求可算是诚心,它会认真解析设备描述符的内容。设备描述符内信息包括端点0的最大包长度,设备所支持的配置(Configuration)个数,设备类型,VID(Vendor ID,由USB-IF分配), PID(Product ID,由厂商自己定制)等信息。此时设备的状态还是USB_STATE_ADDRESS。

对于设备描述符的相关信息,可以参照USB 2.0协议第9章的内容。Host之所以要获取USB设备的描述符,是想要对该USB设备有足够的了解,知道它是属于哪个类,然后为其加载对应的驱动程序。有可能读出来的bDeviceClass、bDeviceSubClass、bDeviceProtocol字段都为0,表示USB类的信息被保存在接口描述符中。
USB Host发送的获取描述符命令的格式如下:

这里写图片描述


USB 接收到USB设备描述符的格式如下:

这里写图片描述
这里写图片描述


六、USB接口驱动与USB设备匹配

一般在嵌入式系统中,都会根据需要,预先加载USB接口驱动,等与之对应的USB设备插上去枚举成功并与USB接口驱动匹配。接下来的工作就是USB接口驱动的事了。在上面我们接入了一个U盘,属于usb-storage 类。通过获取设备描述符或者接口描述符解析来判断属于哪种类型的USB设备。

在 driver.c 中的 usb_device_match() 函数,每次有新的USB设备添加到USB总线上都会执行该函数去与检查与系统中预先加载的USB接口驱动是否匹配。在该函数中会取得USB设备的接口,通过接口描述符来判定与 USB 接口驱动是否匹配。最终通过 usb_match_one_id_intf() 函数来判断是否匹配。


七、USB 接口驱动

USB 接口驱动通过 struct usb_driver 来定义,然后通过 module_usb_driver() 函数进行注册。比如我们常见的 usb-storage 的USB 接口驱动定义和声明如下:

// <kernel_dir>/drivers/usb/storage/usb.c

static struct usb_driver usb_storage_driver = {
    .name =     "usb-storage",
    .probe =    storage_probe,
    .disconnect =   usb_stor_disconnect,
    .suspend =  usb_stor_suspend,
    .resume =   usb_stor_resume,
    .reset_resume = usb_stor_reset_resume,
    .pre_reset =    usb_stor_pre_reset,
    .post_reset =   usb_stor_post_reset,
    .id_table = usb_storage_usb_ids,
    .supports_autosuspend = 1,
    .soft_unbind =  1,
};

module_usb_driver(usb_storage_driver);

其中对 usb_storage_usb_ids 的定义如下:

// <kernel_dir>/drivers/usb/storage/usual-tables.c

struct usb_device_id usb_storage_usb_ids[] = {
#   include "unusual_devs.h"
    { }     /* Terminating entry */
};

unusual_devs.h 文件包含了所有支持usb-storage类的USB设备的定义。分成常见(USUAL_DEV)和不常见(UNUSUAL_DEV)。比如说,常见的设备定义如下:

// <kernel_dir>/drivers/usb/storage/usual-tables.c

#define USUAL_DEV(useProto, useTrans) \
{ USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, useProto, useTrans) }

这里将所有的USB设备的 bInterfaceClass 值设置为 USB_CLASS_MASS_STORAGE(8)。这里与USB-IF规定的usb-storage类的值是一致的。USB_INTERFACE_INFO 的定义如下:

// <kernel_dir>/include/linux/usb.h

/**
 * USB_INTERFACE_INFO - macro used to describe a class of usb interfaces
 * @cl: bInterfaceClass value
 * @sc: bInterfaceSubClass value
 * @pr: bInterfaceProtocol value
 *
 * This macro is used to create a struct usb_device_id that matches a
 * specific class of interfaces.
 */
#define USB_INTERFACE_INFO(cl, sc, pr) \
    .match_flags = USB_DEVICE_ID_MATCH_INT_INFO, \
    .bInterfaceClass = (cl), \
    .bInterfaceSubClass = (sc), \
    .bInterfaceProtocol = (pr)

加入代码调试发现,当插入U盘,在 usb_match_id() 中匹配上了USB接口驱动usb_storage_usb_ids[] 中的 USUAL_DEV(USB_SC_SCSI, USB_PR_BULK)。


注:下面的内容还不是完全理解usb-storage驱动以及scsci驱动的相关内容,只是列出了简单的调用关系。

八、usb-storage 接口驱动中USB 识别并添加设备

usb_stor_probe1() 会打印如下提示信息:

[  220.984187] usb-storage 2-1:1.0: USB Mass Storage device detected

usb_stor_probe2() -> scsi_add_host () -> scsi_add_host_with_dma()。在这个函数会打印如下的提示信息:

[  220.990977] scsi1 : usb-storage 2-1:1.0

九、scsi驱动中获取U盘信息的内容

以下内容主要是在<Kernel_Dir>/driver/scsi/sd.c 文件中实现:

1、注册一个块设备驱动

在 static int __init init_sd(void) 中注册一个modules,调用关系如下:

  • 调用register_blkdev()注册 SD_MAJORS 个以“sd”命名的块设备。可通过 cat /proc/devices 查看到;

  • 注册名为“scsi_disk”的类;

  • 调用 scsi_register_driver(&sd_template.gendrv)注册驱动程序;


2、调用sd_probe()函数

当 scsi 设备接入到系统之后会调用 sd_probe()进行设备与驱动匹配。

struct scsi_device;
struct scsi_disk;
struct gendisk;
struct hd_struct;//用于标记disk的头部信息
  • 调用 alloc_disk()-> alloc_disk_node() 分配一个 struct gendisk 的指针,该指针标记通用的disk;

  • struct gendisk 中有个 struct hd_struct 类型的成员part0,将 disk_type 和 block_class 赋值给struct device中的成员type和class;

disk_to_dev(disk)->class = &block_class;
disk_to_dev(disk)->type = &disk_type;

static struct device_type disk_type = {
    .name       = "disk",
    .groups     = disk_attr_groups,
    .release    = disk_release,
    .devnode    = block_devnode,
    .uevent         = disk_uevent,
};
  • 调用 sd_format_disk_name() 重新设置 gendisk 中的 disk_name 为”sdx”,表示disk的个数;

  • 调用执行 sd_probe_async()


3、调用sd_probe_async()函数

sd_probe_async() 承接执行上一步 sd_probe() 的内容

  • 获取 (major, first_minor, minors)给struct gendisk,minors的值固定为SD_MINORS;

  • 设置 struct gendisk的成员 fops 为 sd_fops,为操作scsi相关的file operations;

  • 调用 sd_revalidate_disk() 读取磁盘的相关信息;

  • 调用 add_disk() 注册该磁盘的分区信息到内核中去;

  • 再次调用 sd_revalidate_disk();


4、static int sd_revalidate_disk(struct gendisk *disk)

  • 调用 sd_spinup_disk()让磁盘转起来,以此可以获取磁盘相关信息;

  • 调用 sd_read_capacity() 读取磁盘容量大小;

  • 调用 sd_read_write_protect_flag() 判断磁盘是否有些保护;

  • 读取磁盘的其他信息;

下面的内核打印信息都是在这个函数中打印的,获取到磁盘的相关信息并打印出来。

[  221.010354] sd 1:0:0:0: [sda] 30277632 512-byte logical blocks: (15.5 GB/14.4 GiB)
[  221.018712] sd 1:0:0:0: [sda] Write Protect is off
[  221.023549] sd 1:0:0:0: [sda] Mode Sense: 45 00 00 00
[  221.029224] sd 1:0:0:0: [sda] Write cache: disabled, read cache: enabled, doesn't support DPO or FUA

5、void add_disk(struct gendisk *disk)

  • 调用 blk_alloc_devt() 为 struct gendisk中的成员 part0 分配设备号赋值给 disk->major 和 disk->first_minor;

  • 调用 disk_alloc_events() 为 disk 分配 struct disk_events的空间;

  • 调用 blk_register_region() 来管理设备号;

  • 调用 register_disk() 注册磁盘分区信息;


6、static void register_disk(struct gendisk *disk)

  • 调用 disk_part_scan_enabled() 判断分区数不能大于 DISK_MAX_PARTS;

  • 调用 get_capacity() 获取磁盘容量大小,即struct gendisk disk->part0.nr_sects;

  • 调用 bdget_disk() -> disk_get_part() 查找分区号指定的分区;

  • 调用 blkdev_get() 打开一个block 设备;

  • kobject_uevent(&ddev->kobj, KOBJ_ADD);将会执行发送uevent的内容给用户空间,就是在这里会调用
    static int disk_uevent(struct device *dev, struct kobj_uevent_env *env) 的内容将 NPARTS 的参数传递上去。

详细的内容可以参照:http://blog.youkuaiyun.com/zjujoe/article/details/2986634


7、int blkdev_get(struct block_device *bdev, fmode_t mode, void *holder)

  • 检测block设备的有效性,并claiming;

  • 调用 __blkdev_get();

  • 做一些claiming的操作;


8、static int __blkdev_get(struct block_device *bdev, fmode_t mode, int for_part)

  • 调用 get_gendisk() 通过已知的设备号返回分区号,并返回 struct gendisk;

  • 调用 disk_block_events() block and flush disk event checking;

  • 调用 disk_get_part() 通过 struct gendisk 和分区号来返回分区信息存放在 struct hd_struct;

  • disk->fops->open,执行 sd_open() 的动作;

  • blk_get_backing_dev_info() 和 bdev_inode_switch_bdi();

  • 调用rescan_partitions() 检查分区信息;

  • 如果是无效的分区,调用 invalidate_partitions() 设置其无效;


9、int rescan_partitions(struct gendisk *disk, struct block_device *bdev)

  • 调用 drop_partitions() 判断分区信息是否有效,如果无效需调用 delete_partition();

  • check_disk_size_change() 检查disk的大小是否改变;

  • get_capacity() 获取disk大小;

  • 判断分区code是否超出 EOD;

  • kobject_uevent(&disk_to_dev(disk)->kobj, KOBJ_CHANGE);告诉用户空间分区表改变;

  • disk_expand_part_tbl() 展开 disk_expand_part_tbl 的内容;

  • 判断是否添加分区信息add partitions,并保存在 struct partition_meta_info;

  • 如果获取到的分区size不为0,那么调用add_partition()添加分区;

五、从usb的插入开始 当usb设备插入接口,电压变化会通知到usb主控器, 触发主控器中断,如果主控器不支持中断,那么会使用rh_timer方法,轮询接口 其结果都是调用usb_hcd_poll_rh_status void usb_hcd_poll_rh_status(struct usb_hcd *hcd) { struct urb *urb; int length; unsigned lon 阅读详情

相关推荐

内核报reet ehci 问题分析

问题环境: a:硬件环境:3C5000L 双路服务器 b:软件内核版本:linux-4.19.167-next 问题现象: 系统启动过程中和启动之后,bmc kvm 没有连接的情况下,内核不断的打印 usb 1-3: reset high-speed USB device number 2 using ehci-pci 如下图所示: 连接上以后,就不再报了。 问题定位: 根据以上现象,查看内核代码得知,这个打印是在函数hub_port_init 内打印出来的,代码如下: 追踪函数的调用关系得知,调用关系

太初有道 5130

USB驱动之U盘驱动

U盘使用的是drivers/usb/storage/usb.c驱动 #define DRV_NAME "usb-storage" //插入U盘时一般会打印usb-storage 1-1:1.0: USB Mass Storage device detected static struct usb_driver usb_storage_driver = { .name = DRV_NAME, .probe = storage_probe, .disconnect = usb_sto...

四季帆的博客 4057

USB 设备常见问题

当涉及到 USB(通用串行总线)的使用时遇到一些常见问题。

weixin_35723192的博客 3424

AR9331出现connect-debounce failed,port 1 disabled解决方法备忘

基于AR9331的路由器,自己画的pcb板子,竟然出现这个错误,百度下,貌似有不少人遇见过这个错误,但是在修改板子前我的固件用的是没问题的,USB完美使用 修改过板子后出现这个问题! hub 1-0:1.0 connect-debounce failed,port 1 disabled 后台串口不停的报这个错误。 最后发现是USB的数据线反了,哈哈,跳线反过来试试! 成功识别

jk110333的专栏 6964

Linux 2.5G网卡驱动整合包:从芯片识别到编译加载的完整指南

操作系统与硬件交互时,网卡驱动本质上是一个内核模块,负责让系统正确识别并控制PCI或USB接口的网络设备。在Linux环境下,2.5G有线网卡常因芯片厂商驱动未进入主线内核而无法正常工作,尤其在Intel、MediaTek、Realtek方案混用的主板上更为常见。理解驱动源码编译、模块加载、firmware放置和黑名单机制,是解决网卡识别失败的关键。实际工程中,通过lspci确认芯片型号,再针对RTL8125、I225等芯片编译对应驱动,配合ethtool验证协商速率,可快速恢复2.5G网络能力。该思路同样

weixin_32050773的博客 173

Linux下U盘自动识别与文件拷贝系统实现

只处理块设备中的分区,且来自USB控制器该函数通过多重条件判断确保仅响应真正的U盘分区事件,排除本地硬盘或光驱干扰。现代U盘常用于传输文档、图片、视频等用户生成内容。因此,在设计自动拷贝系统时,首要步骤是明确哪些类型的文件属于“有效载荷”。通常可通过文件扩展名进行分类管理,例如.jpg.png.pdf.docx.mp4等。以下是一个典型的允许列表定义方式:# 支持的文件扩展名白名单此数组可用于后续find命令的匹配条件构造。

weixin_42588555的博客 891

linux系统rocky的安装+显卡驱动的安装

安装的时候,在u盘里copy一个镜像用的Rocky的iso文件,安装源选择U盘的这个文件,否则下载时候有可能特别慢或者连不上。注意驱动的版本号和后面的kernels地址,一定要指定,否则安装不成功。用lspci | grep -i nvidia命令看一下是什么,如果没有安装lspci命令,就用yum install pciutils安装一下。用lspci | grep -i nvidia命令看一下是什么,如果没有安装lspci命令,就用yum install pciutils安装一下。

u013015301的博客 544

Linux下实现U盘自动挂载到指定目录的步骤详解

自动挂载通常是通过udev规则来实现的。udev是Linux内核的设备管理器,负责在系统启动时和运行时自动创建设备文件,并且响应设备的热插拔事件。通过udev规则,我们可以配置系统,在识别到新设备时自动执行挂载命令。这一过程将在后续章节中详细探讨。

weixin_42588672的博客 1330

Android12 U盘插拔链路源码全解析(二):内核层USB驱动与uevent机制

本文深入解析了Android系统中USB设备插入时的内核层处理机制。通过分析Linux Kernel 5.10源码,揭示了从硬件中断到用户态通知的完整流程

AfinalStone的专栏 374

ADS RFpro 射频阻抗仿真步骤

导入器件后,在库里面选取器件的对应模型,如图所示我们选取10pf 系统自带的村田模型。选取网络上的器件,拖入到下面component Models。导入成功后,截取需要仿真的区域(不截取仿真时间会非常长),可以生成对应如下原理图,通过原理图继续调整仿真。图上找到需要仿真链路上的网络,右键设置为信号。的叠层信息,按照PCB板厂提供信息填写完整。选择仿真的频率范围和频点数,生成如下图的RFPro仿真界。,然后再把GND也添加到下面。按图设置截取需要仿真的部分。添加成功后 网络就有了,15 双击run开始仿真。

m0_72954480的博客 3217

Origin画图——获取论文图中数据

有时候需要已发表论文的图的数据,这里使用Origin软件取值,并且可以实现对图形再次画图。 Step 1. 打开origin软件,点击工具,图形数字化工具。 Step 2. 打开要获取数据的图片。拉动红蓝坐标,与图的坐标轴所贴合。 Step 3. 打开要获取数据的图片。拉动红蓝坐标,与图的坐标轴所贴合。 Step 3. 设置参数横轴起始坐标为300-900,纵轴为1.8-2.6. Step 4. 点击第五个手动取点,取出图中数据点。 Step 5. 依次对原图进行取点。点击项目资源查看图。

qq_42278015的博客 5183

linux 配置如何识别U盘

1. kernel 中需要的配置 [python] view plaincopy Device Drivers   (U盘属于存储设备,linux将其定义为 SCSI设备)       SCSI device support  --->        SCSI device support                           

harhy的专栏 5969

Faster Planner——Kinodynamic Astar详解

实际成本和启发式成本 实际成本 Actual Cost   每条轨迹的真实代价函数定义如下: J(T)=∫0T∥u(t)∥2dt+ρT \mathcal{J}(T)=\int_{0}^{T}\|\mathbf{u}(t)\|^{2} d t+\rho T J(T)=∫0T​∥u(t)∥2dt+ρT 离散形式下: 启发成本 Heuristic Cost pμ∗(t)=16αμt3+12βμt2+vμc+pμc=pμc+vμc+T2∗((2∗vμc−2∗vμg)/(2∗T)−(6∗pμc−6∗pμg+6∗T∗v

漫长IT路 4834

linux为了识别U盘该如何配置

1. 查询是否有U盘识别,可以用如下命令: 一般插上U盘,如果识别出来了: # tail -f /var/log/messages     //会有一些usb register的log. # fdisk -l      //可以查看可以格式化的盘符 # mount      // 查看是否有U盘自动mount上可以用,命令查看 2. mount 各种格式的 U盘,命令: m

844

Android 的端智能学习路径

基础层:先补齐 Android 进阶(JNI / 性能优化)和 AI 基础概念,无需深入算法开发;核心层:掌握 TFLite 的模型加载、推理、量化,能完成基础端侧推理;落地层:聚焦实战场景(图像 / OCR / 语音),解决工程化问题(模型管理、异常处理);优化层:通过模型量化、硬件加速、工程优化,提升推理性能和用户体验。

hqiangtai的博客 344

炸金花游戏(3)--基于EV(期望收益)的简单AI模型

前言:   炸金花这款游戏, 从技术的角度来说, 比德州差了很多. 所以他的AI模型也相对简单一些. 本文从EV(期望收益)的角度, 来尝试构建一个简单的炸金花AI. 相关文章:  德州扑克AI--Programming Poker AI(译).  系列文章说来惭愧, 之前一直叫嚷着写德州AI, 不过可惜懒癌晚期, 一直没去实践, T_T. 相比而言,***简单很多, 也更偏重于运...

weixin_30377461的博客 2341

u盘启动linux kernel,u盘启动linux

proc /proc proc defaults 0 0#/dev/hda2 / ext3 defaults,errors=remount-ro 0 1UUID=4f1a87c0-b065-44d8-a6bf-2bb3d4a0c63a / ext3 de...

weixin_36000501的博客 395

单相变换器的二次纹波分析

1. 单相PWM整流器的二次纹波产生原因   单相变换器的二次纹波产生的原因是功率含有二次脉动分量。设电网电压 us=Vmsin(wt),电网电流IL=Imsin(wt−θ)I_L=I_msin(wt-\theta)IL​=Im​sin(wt−θ)。其中w是电网电压披绿,θ\thetaθ是电流之后电压相位。保证输入功率和输出功率相等,就要保证两者的直流量和交流量对应相等。   uDu_DuD​为 PWM 整流器直流电容电压平均值,IoI_oIo​ 为负载电流平均值 ,uDC^\widehat {u_{DC}

qq_38847810的博客 4973

Linux上用udev自动识别并挂载U盘

最近想在U盘中放置一个配置文件,当U盘插入时自动读取配置文件。发现插上U盘后,在/dev下产生sda1、adb1、sdc1等不定设备,需要手动挂载才行。根据网上了解,可以通过udev来实现U盘的自动识别和挂载,操作方法如下:   1. 在/etc/udev/rules目录下新建11-add-usb.rules和11-add-remove.rules,负责设备监测。 root@am

Micheal 3556

光学相干断层扫描(OCT)在 妇科诊断 中的革命性应用

尽管OCT在妇科的应用提供了许多潜在的优势,但它仍然是一个相对新的技术,并不是所有医疗机构都有能力提供这种类型的成像服务。随着技术的发展和医生对OCT的熟悉程度的提高,预计OCT将在妇科和其他医学领域中扮演越来越重要的角色。早期发现和准确的分类是提高治疗成功率的关键。虽然这是一个相对较新的应用领域,但OCT的非侵入性和高分辨率成像能力可能提供有价值的信息,以提高辅助生殖技术的成功率。:光学相干断层扫描(OCT)在阴道和外阴组织的评估中的应用正在逐渐展开,提供了一种新的方法来诊断和监测各种妇科疾病。

LiRongLu_的博客 1720

【Bug已解决】Auto mode cannot determine the safety of an action — Claude Code 自动模式安全判断失败解决方案

将已知安全的操作添加到白名单,分类器不再拦截这些操作。# 步骤 1:编辑 Claude Code 配置"Read","Write"EOF# 步骤 2:白名单中的操作将自动通过# 不在白名单中的操作仍需分类器判断# 步骤 3:重启 Claude Codeclaude方案适用场景推荐指数难度方案一:手动审批偶发回退,安全优先⭐⭐⭐⭐⭐低方案二:/compact 压缩上下文超限导致⭐⭐⭐⭐低方案三:拆分步骤复杂操作触发⭐⭐⭐⭐低方案四:跳过审批隔离环境,CI/CD。

博客 1万+

ARM-Linux下实现U盘自动挂载

记录使用ARM-Linux下实现U盘自动挂载实践。自动挂载U盘在项目中很便捷,不用手动挂载。

ZML的博客 4441

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

嵌入式Linux系统中U盘挂载

USB一般分为主机(Host)和从机(Slave),数据传输是建立在Host和Slave的正常连接基础上,比如电脑上的USB口是Host,U盘是Slave;还有一种协议OTG(On The Go),这是一种USB2.0引入的工作模式,可以进行主从协商,什么时候做Host或者Slave由一个ID决定,这个ID是硬件上的管脚来检测的,原理图: 本章内容是有关USBHost进行U盘的挂载。 1 配置选项说明 在目录~/share/patchs/udisk-config中,有一个配置文件,我们已经把.

HomeByte的博客 1965

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

Ubuntu 自动挂载u盘(arm等其他linux环境也可)

1. 手动挂载 cat /proc/partitions 识别U盘在系统中的设备号 mount /dev/sdb1 /mnt/udisk 在/mnt/udisk目录下挂载sdb1,如果没有udisk则手动创建一个 umount /mnt/udisk 卸载u盘 2. 自动挂载 禁止自动挂载: $ gsettings set org.gnome.de...

qq_37730663的博客 4522

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

Linux udev挂载U盘

关于udev请自行科普,网上N多关于linux 挂载U盘文章,但是基本是说一个U盘的挂靠,这里是2个U盘的挂载 一、首先要增加规则文件 /etc/udev/rules.d/11-usbmount.rules #11-usbmount.rules #文件名前加11是为了把规则排名靠前,让系统优先处理 KERNEL=="sd[a-z][0-9]", RUN+="/etc/udev/mount_u...

silno的专栏 957
上一篇: USB设备插入和拔出的波形
下一篇: 怎么查看调用栈关系
www.wowothink.com
博客等级 码龄16年 381粉丝 66原创
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值