usb-serial.c

这两周都在调GPS, 用的是usb转串口的pl2303芯片,所以涉及到了usb-serial驱动,见解如下。

int usb_serial_register(struct usb_serial_driver *driver)
{
    /* must be called with BKL held */
    int retval;

    if (usb_disabled())
        return -ENODEV;

    fixup_generic(driver);

    if (!driver->description)
        driver->description = driver->driver.name;

    /* Add this device to our list of devices */
    list_add(&driver->driver_list, &usb_serial_driver_list);

    retval = usb_serial_bus_register(driver);
    if (retval) {
        printk(KERN_ERR "usb-serial: problem %d when registering "
         "driver %s\n", retval, driver->description);
        list_del(&driver->driver_list);
    } else
        printk(KERN_INFO "USB Serial support registered for %s\n",
                        driver->description);

    return retval;
}
EXPORT_SYMBOL_GPL(usb_serial_register);


void usb_serial_deregister(struct usb_serial_driver *device)
{
    /* must be called with BKL held */
    printk(KERN_INFO "USB Serial deregistering driver %s\n",
     device->description);
    list_del(&device->driver_list);
    usb_serial_bus_deregister(device);
}
EXPORT_SYMBOL_GPL(usb_serial_deregister);

/* Module information */
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");

module_param(debug, bool, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(debug, "Debug enabled or not");
static int __init usb_serial_init(void)
{
    int i;
    int result;

    usb_serial_tty_driver = alloc_tty_driver(SERIAL_TTY_MINORS);
    if (!usb_serial_tty_driver)
        return -ENOMEM;

    /* Initialize our global data */
    for (i = 0; i < SERIAL_TTY_MINORS; ++i)
        serial_table[i] = NULL;

    result = bus_register(&usb_serial_bus_type);
    if (result) {
        printk(KERN_ERR "usb-serial: %s - registering bus driver "
         "failed\n", __func__);
        goto exit_bus;
    }

    usb_serial_tty_driver->owner = THIS_MODULE;
    usb_serial_tty_driver->driver_name = "usbserial";
    usb_serial_tty_driver->name =     "ttyUSB";
    usb_serial_tty_driver->major = SERIAL_TTY_MAJOR;
    usb_serial_tty_driver->minor_start = 0;
    usb_serial_tty_driver->type = TTY_DRIVER_TYPE_SERIAL;
    usb_serial_tty_driver->subtype = SERIAL_TYPE_NORMAL;
    usb_serial_tty_driver->flags = TTY_DRIVER_REAL_RAW |
                        TTY_DRIVER_DYNAMIC_DEV;
    usb_serial_tty_driver->init_termios = tty_std_termios;
    usb_serial_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD
                            | HUPCL | CLOCAL;
    usb_serial_tty_driver->init_termios.c_ispeed = 9600;
    usb_serial_tty_driver->init_termios.c_ospeed = 9600;
    tty_set_operations(usb_serial_tty_driver, &serial_ops);
    result = tty_register_driver(usb_serial_tty_driver);
    if (result) {
        printk(KERN_ERR "usb-serial: %s - tty_register_driver failed\n",
         __func__);
        goto exit_reg_driver;
    }

    /* register the USB driver */
    result = usb_register(&usb_serial_driver);
    if (result < 0) {
        printk(KERN_ERR "usb-serial: %s - usb_register failed\n",
         __func__);
        goto exit_tty;
    }

    /* register the generic driver, if we should */
    result = usb_serial_generic_register(debug);
    if (result < 0) {
        printk(KERN_ERR "usb-serial: %s - registering generic "
         "driver failed\n", __func__);
        goto exit_generic;
    }

    printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_DESC "\n");

    return result;

exit_generic:
    usb_deregister(&usb_serial_driver);

exit_tty:
    tty_unregister_driver(usb_serial_tty_driver);

exit_reg_driver:
    bus_unregister(&usb_serial_bus_type);

exit_bus:
    printk(KERN_ERR "usb-serial: %s - returning with error %d\n",
     __func__, result);
    put_tty_driver(usb_serial_tty_driver);
    return result;
}


static void __exit usb_serial_exit(void)
{
    usb_serial_console_exit();

    usb_serial_generic_deregister();

    usb_deregister(&usb_serial_driver);
    tty_unregister_driver(usb_serial_tty_driver);
    put_tty_driver(usb_serial_tty_driver);
    bus_unregister(&usb_serial_bus_type);
}


module_init(usb_serial_init);
module_exit(usb_serial_exit);
static const struct tty_operations serial_ops = {
    .open =            serial_open,
    .close =        serial_close,
    .write =        serial_write,
    .hangup =         serial_hangup,
    .write_room =        serial_write_room,
    .ioctl =        serial_ioctl,
    .set_termios =        serial_set_termios,
    .throttle =        serial_throttle,
    .unthrottle =        serial_unthrottle,
    .break_ctl =        serial_break,
    .chars_in_buffer =    serial_chars_in_buffer,
    .tiocmget =        serial_tiocmget,
    .tiocmset =        serial_tiocmset,
    .shutdown =         serial_release,
    .install =         serial_install,
    .proc_fops =        &serial_proc_fops,
};


struct tty_driver *usb_serial_tty_driver;
int usb_serial_probe(struct usb_interface *interface,
             const struct usb_device_id *id)
{
    struct usb_device *dev = interface_to_usbdev(interface);
    struct usb_serial *serial = NULL;
    struct usb_serial_port *port;
    struct usb_host_interface *iface_desc;
    struct usb_endpoint_descriptor *endpoint;
    struct usb_endpoint_descriptor *interrupt_in_endpoint[MAX_NUM_PORTS];
    struct usb_endpoint_descriptor *interrupt_out_endpoint[MAX_NUM_PORTS];
    struct usb_endpoint_descriptor *bulk_in_endpoint[MAX_NUM_PORTS];
    struct usb_endpoint_descriptor *bulk_out_endpoint[MAX_NUM_PORTS];
    struct usb_serial_driver *type = NULL;
    int retval;
    unsigned int minor;
    int buffer_size;
    int i;
    int num_interrupt_in = 0;
    int num_interrupt_out = 0;
    int num_bulk_in = 0;
    int num_bulk_out = 0;
    int num_ports = 0;
    int max_endpoints;

    lock_kernel(); /* guard against unloading a serial driver module */
    type = search_serial_device(interface);
    if (!type) {
        unlock_kernel();
        dbg("none matched");
        return -ENODEV;
    }

    serial = create_serial(dev, interface, type);
    if (!serial) {
        unlock_kernel();
        dev_err(&interface->dev, "%s - out of memory\n", __func__);
        return -ENOMEM;
    }

    /* if this device type has a probe function, call it */
    if (type->probe) {
        const struct usb_device_id *id;

        if (!try_module_get(type->driver.owner)) {
            unlock_kernel();
            dev_err(&interface->dev,
                "module get failed, exiting\n");
            kfree(serial);
            return -EIO;
        }

        id = get_iface_id(type, interface);
        retval = type->probe(serial, id);
        module_put(type->driver.owner);

        if (retval) {
            unlock_kernel();
            dbg("sub driver rejected device");
            kfree(serial);
            return retval;
        }
    }

    /* descriptor matches, let's find the endpoints needed */
    /* check out the endpoints */
    iface_desc = interface->cur_altsetting;
    for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
        endpoint = &iface_desc->endpoint[i].desc;

        if (usb_endpoint_is_bulk_in(endpoint)) {
            /* we found a bulk in endpoint */
            dbg("found bulk in on endpoint %d", i);
            bulk_in_endpoint[num_bulk_in] = endpoint;
            ++num_bulk_in;
        }

        if (usb_endpoint_is_bulk_out(endpoint)) {
            /* we found a bulk out endpoint */
            dbg("found bulk out on endpoint %d", i);
            bulk_out_endpoint[num_bulk_out] = endpoint;
            ++num_bulk_out;
        }

        if (usb_endpoint_is_int_in(endpoint)) {
            /* we found a interrupt in endpoint */
            dbg("found interrupt in on endpoint %d", i);
            interrupt_in_endpoint[num_interrupt_in] = endpoint;
            ++num_interrupt_in;
        }

        if (usb_endpoint_is_int_out(endpoint)) {
            /* we found an interrupt out endpoint */
            dbg("found interrupt out on endpoint %d", i);
            interrupt_out_endpoint[num_interrupt_out] = endpoint;
            ++num_interrupt_out;
        }
    }

#if defined(CONFIG_USB_SERIAL_PL2303) || defined(CONFIG_USB_SERIAL_PL2303_MODULE)
    /* BEGIN HORRIBLE HACK FOR PL2303 */
    /* this is needed due to the looney way its endpoints are set up */
    if (((le16_to_cpu(dev->descriptor.idVendor) == PL2303_VENDOR_ID) &&
     (le16_to_cpu(dev->descriptor.idProduct) == PL2303_PRODUCT_ID)) ||
     ((le16_to_cpu(dev->descriptor.idVendor) == ATEN_VENDOR_ID) &&
     (le16_to_cpu(dev->descriptor.idProduct) == ATEN_PRODUCT_ID)) ||
     ((le16_to_cpu(dev->descriptor.idVendor) == ALCOR_VENDOR_ID) &&
     (le16_to_cpu(dev->descriptor.idProduct) == ALCOR_PRODUCT_ID)) ||
     ((le16_to_cpu(dev->descriptor.idVendor) == SIEMENS_VENDOR_ID) &&
     (le16_to_cpu(dev->descriptor.idProduct) == SIEMENS_PRODUCT_ID_EF81))) {
        if (interface != dev->actconfig->interface[0]) {
            /* check out the endpoints of the other interface*/
            iface_desc = dev->actconfig->interface[0]->cur_altsetting;
            for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
                endpoint = &iface_desc->endpoint[i].desc;
                if (usb_endpoint_is_int_in(endpoint)) {
                    /* we found a interrupt in endpoint */
                    dbg("found interrupt in for Prolific device on separate interface");
                    interrupt_in_endpoint[num_interrupt_in] = endpoint;
                    ++num_interrupt_in;
                }
            }
        }

        /* Now make sure the PL-2303 is configured correctly.
         * If not, give up now and hope this hack will work
         * properly during a later invocation of usb_serial_probe
         */
        if (num_bulk_in == 0 || num_bulk_out == 0) {
            unlock_kernel();
            dev_info(&interface->dev, "PL-2303 hack: descriptors matched but endpoints did not\n");
            kfree(serial);
            return -ENODEV;
        }
    }
    /* END HORRIBLE HACK FOR PL2303 */
#endif

#ifdef CONFIG_USB_SERIAL_GENERIC
    if (type == &usb_serial_generic_device) {
        num_ports = num_bulk_out;
        if (num_ports == 0) {
            unlock_kernel();
            dev_err(&interface->dev,
             "Generic device with no bulk out, not allowed.\n");
            kfree(serial);
            return -EIO;
        }
    }
#endif
    if (!num_ports) {
        /* if this device type has a calc_num_ports function, call it */
        if (type->calc_num_ports) {
            if (!try_module_get(type->driver.owner)) {
                unlock_kernel();
                dev_err(&interface->dev,
                    "module get failed, exiting\n");
                kfree(serial);
                return -EIO;
            }
            num_ports = type->calc_num_ports(serial);
            module_put(type->driver.owner);
        }
        if (!num_ports)
            num_ports = type->num_ports;
    }

    serial->num_ports = num_ports;
    serial->num_bulk_in = num_bulk_in;
    serial->num_bulk_out = num_bulk_out;
    serial->num_interrupt_in = num_interrupt_in;
    serial->num_interrupt_out = num_interrupt_out;

    /* found all that we need */
    dev_info(&interface->dev, "%s converter detected\n",
            type->description);

    /* create our ports, we need as many as the max endpoints */
    /* we don't use num_ports here because some devices have more
     endpoint pairs than ports */
    max_endpoints = max(num_bulk_in, num_bulk_out);
    max_endpoints = max(max_endpoints, num_interrupt_in);
    max_endpoints = max(max_endpoints, num_interrupt_out);
    max_endpoints = max(max_endpoints, (int)serial->num_ports);
    serial->num_port_pointers = max_endpoints;
    unlock_kernel();

    dbg("%s - setting up %d port structures for this device",
                        __func__, max_endpoints);
    for (i = 0; i < max_endpoints; ++i) {
        port = kzalloc(sizeof(struct usb_serial_port), GFP_KERNEL);
        if (!port)
            goto probe_error;
        tty_port_init(&port->port);
        port->port.ops = &serial_port_ops;
        port->serial = serial;
        spin_lock_init(&port->lock);
        mutex_init(&port->mutex);
        INIT_WORK(&port->work, usb_serial_port_work);
        serial->port[i] = port;
        port->dev.parent = &interface->dev;
        port->dev.driver = NULL;
        port->dev.bus = &usb_serial_bus_type;
        port->dev.release = &port_release;
        device_initialize(&port->dev);
    }

    /* set up the endpoint information */
    for (i = 0; i < num_bulk_in; ++i) {
        endpoint = bulk_in_endpoint[i];
        port = serial->port[i];
        port->read_urb = usb_alloc_urb(0, GFP_KERNEL);
        if (!port->read_urb) {
            dev_err(&interface->dev, "No free urbs available\n");
            goto probe_error;
        }
        buffer_size = le16_to_cpu(endpoint->wMaxPacketSize);
        port->bulk_in_size = buffer_size;
        port->bulk_in_endpointAddress = endpoint->bEndpointAddress;
        port->bulk_in_buffer = kmalloc(buffer_size, GFP_KERNEL);
        if (!port->bulk_in_buffer) {
            dev_err(&interface->dev,
                    "Couldn't allocate bulk_in_buffer\n");
            goto probe_error;
        }
        usb_fill_bulk_urb(port->read_urb, dev,
                usb_rcvbulkpipe(dev,
                        endpoint->bEndpointAddress),
                port->bulk_in_buffer, buffer_size,
                serial->type->read_bulk_callback, port);
    }

    for (i = 0; i < num_bulk_out; ++i) {
        endpoint = bulk_out_endpoint[i];
        port = serial->port[i];
        port->write_urb = usb_alloc_urb(0, GFP_KERNEL);
        if (!port->write_urb) {
            dev_err(&interface->dev, "No free urbs available\n");
            goto probe_error;
        }
        buffer_size = le16_to_cpu(endpoint->wMaxPacketSize);
        port->bulk_out_size = buffer_size;
        port->bulk_out_endpointAddress = endpoint->bEndpointAddress;
        port->bulk_out_buffer = kmalloc(buffer_size, GFP_KERNEL);
        if (!port->bulk_out_buffer) {
            dev_err(&interface->dev,
                    "Couldn't allocate bulk_out_buffer\n");
            goto probe_error;
        }
        usb_fill_bulk_urb(port->write_urb, dev,
                usb_sndbulkpipe(dev,
                    endpoint->bEndpointAddress),
                port->bulk_out_buffer, buffer_size,
                serial->type->write_bulk_callback, port);
    }

    if (serial->type->read_int_callback) {
        for (i = 0; i < num_interrupt_in; ++i) {
            endpoint = interrupt_in_endpoint[i];
            port = serial->port[i];
            port->interrupt_in_urb = usb_alloc_urb(0, GFP_KERNEL);
            if (!port->interrupt_in_urb) {
                dev_err(&interface->dev,
                        "No free urbs available\n");
                goto probe_error;
            }
            buffer_size = le16_to_cpu(endpoint->wMaxPacketSize);
            port->interrupt_in_endpointAddress =
                        endpoint->bEndpointAddress;
            port->interrupt_in_buffer = kmalloc(buffer_size,
                                GFP_KERNEL);
            if (!port->interrupt_in_buffer) {
                dev_err(&interface->dev,
                 "Couldn't allocate interrupt_in_buffer\n");
                goto probe_error;
            }
            usb_fill_int_urb(port->interrupt_in_urb, dev,
                usb_rcvintpipe(dev,
                        endpoint->bEndpointAddress),
                port->interrupt_in_buffer, buffer_size,
                serial->type->read_int_callback, port,
                endpoint->bInterval);
        }
    } else if (num_interrupt_in) {
        dbg("the device claims to support interrupt in transfers, but read_int_callback is not defined");
    }

    if (serial->type->write_int_callback) {
        for (i = 0; i < num_interrupt_out; ++i) {
            endpoint = interrupt_out_endpoint[i];
            port = serial->port[i];
            port->interrupt_out_urb = usb_alloc_urb(0, GFP_KERNEL);
            if (!port->interrupt_out_urb) {
                dev_err(&interface->dev,
                        "No free urbs available\n");
                goto probe_error;
            }
            buffer_size = le16_to_cpu(endpoint->wMaxPacketSize);
            port->interrupt_out_size = buffer_size;
            port->interrupt_out_endpointAddress =
                        endpoint->bEndpointAddress;
            port->interrupt_out_buffer = kmalloc(buffer_size,
                                GFP_KERNEL);
            if (!port->interrupt_out_buffer) {
                dev_err(&interface->dev,
                 "Couldn't allocate interrupt_out_buffer\n");
                goto probe_error;
            }
            usb_fill_int_urb(port->interrupt_out_urb, dev,
                usb_sndintpipe(dev,
                         endpoint->bEndpointAddress),
                port->interrupt_out_buffer, buffer_size,
                serial->type->write_int_callback, port,
                endpoint->bInterval);
        }
    } else if (num_interrupt_out) {
        dbg("the device claims to support interrupt out transfers, but write_int_callback is not defined");
    }

    /* if this device type has an attach function, call it */
    if (type->attach) {
        if (!try_module_get(type->driver.owner)) {
            dev_err(&interface->dev,
                    "module get failed, exiting\n");
            goto probe_error;
        }
        retval = type->attach(serial);
        module_put(type->driver.owner);
        if (retval < 0)
            goto probe_error;
        if (retval > 0) {
            /* quietly accept this device, but don't bind to a
             serial port as it's about to disappear */
            serial->num_ports = 0;
            goto exit;
        }
    }

    if (get_free_serial(serial, num_ports, &minor) == NULL) {
        dev_err(&interface->dev, "No more free serial devices\n");
        goto probe_error;
    }
    serial->minor = minor;

    /* register all of the individual ports with the driver core */
    for (i = 0; i < num_ports; ++i) {
        port = serial->port[i];
        dev_set_name(&port->dev, "ttyUSB%d", port->number);
        dbg ("%s - registering %s", __func__, dev_name(&port->dev));
        port->dev_state = PORT_REGISTERING;
        retval = device_add(&port->dev);
        if (retval) {
            dev_err(&port->dev, "Error registering port device, "
                "continuing\n");
            port->dev_state = PORT_UNREGISTERED;
        } else {
            port->dev_state = PORT_REGISTERED;
        }
    }

    usb_serial_console_init(debug, minor);

exit:
    /* success */
    usb_set_intfdata(interface, serial);
    return 0;

probe_error:
    usb_serial_put(serial);
    return -EIO;
}
EXPORT_SYMBOL_GPL(usb_serial_probe);

void usb_serial_disconnect(struct usb_interface *interface)
{
    int i;
    struct usb_serial *serial = usb_get_intfdata(interface);
    struct device *dev = &interface->dev;
    struct usb_serial_port *port;

    usb_serial_console_disconnect(serial);
    dbg("%s", __func__);

    mutex_lock(&serial->disc_mutex);
    usb_set_intfdata(interface, NULL);
    /* must set a flag, to signal subdrivers */
    serial->disconnected = 1;
    mutex_unlock(&serial->disc_mutex);

    for (i = 0; i < serial->num_ports; ++i) {
        port = serial->port[i];
        if (port) {
            struct tty_struct *tty = tty_port_tty_get(&port->port);
            if (tty) {
                tty_vhangup(tty);
                tty_kref_put(tty);
            }
            kill_traffic(port);
            cancel_work_sync(&port->work);
            if (port->dev_state == PORT_REGISTERED) {

                /* Make sure the port is bound so that the
                 * driver's port_remove method is called.
                 */
                if (!port->dev.driver) {
                    int rc;

                    port->dev.driver =
                            &serial->type->driver;
                    rc = device_bind_driver(&port->dev);
                }
                port->dev_state = PORT_UNREGISTERING;
                device_del(&port->dev);
                port->dev_state = PORT_UNREGISTERED;
            }
        }
    }
    serial->type->disconnect(serial);

    /* let the last holder of this object cause it to be cleaned up */
    usb_serial_put(serial);
    dev_info(dev, "device disconnected\n");
}
EXPORT_SYMBOL_GPL(usb_serial_disconnect);

int usb_serial_suspend(struct usb_interface *intf, pm_message_t message)
{
    struct usb_serial *serial = usb_get_intfdata(intf);
    struct usb_serial_port *port;
    int i, r = 0;

    serial->suspending = 1;

    for (i = 0; i < serial->num_ports; ++i) {
        port = serial->port[i];
        if (port)
            kill_traffic(port);
    }

    if (serial->type->suspend)
        r = serial->type->suspend(serial, message);

    return r;
}
EXPORT_SYMBOL(usb_serial_suspend);

int usb_serial_resume(struct usb_interface *intf)
{
    struct usb_serial *serial = usb_get_intfdata(intf);
    int rv;

    serial->suspending = 0;
    if (serial->type->resume)
        rv = serial->type->resume(serial);
    else
        rv = usb_serial_generic_resume(serial);

    return rv;
}
EXPORT_SYMBOL(usb_serial_resume);

<think> 我们面临的任务是在Android 11(内核版本5.10)中修改usb-serial.c驱动,以强制将特定的USB转串口设备(位于USB总线2-1.3)绑定到ttyUSB7,并且不使用udev规则。 分析: 在Linux内核中,USB串口设备由usb-serial驱动管理。每个连接的USB设备在内核中都有一个对应的usb_device结构,并且通过总线号、设备号(如2-1.3)来标识。通常,设备节点(如ttyUSB0, ttyUSB1等)的分配是动态的,由内核按检测顺序分配。为了固定设备节点名称,传统方法是使用udev规则,但在Android系统中,尤其是我们不想使用udev规则,则需要在内核驱动层面进行修改。 思路: 我们可以修改usb-serial驱动,在创建设备节点时,根据设备的物理位置(总线号、设备号)来指定设备号(minor number),从而固定设备节点名称(如ttyUSB7对应次设备号为7)。 具体步骤: 1. 确定目标设备在usb-serial驱动中创建tty设备时使用的次设备号(minor number)。我们知道ttyUSBX的X就是次设备号减去usb-serial驱动分配的第一个次设备号(通常是从0开始,但实际驱动中可能有偏移)。 2.usb-serial驱动中,每个串口设备对应一个struct usb_serial_port,而每个port在创建时都会分配一个次设备号。 3. 我们需要在驱动中检测当前设备的总线位置(例如总线号2,设备路径1.3),如果匹配,则强制指定其端口(port)的次设备号为7(对应ttyUSB7)。 注意:在usb-serial驱动中,一个USB转串口设备可能包含多个串口(端口),但大多数常见的USB转串口芯片(如PL2303)通常只提供一个串口。因此,我们通常只需处理第一个端口。 实现: 我们将修改usb-serial驱动的代码(通常位于drivers/usb/serial/usb-serial.c)。具体修改点可能是在端口创建函数中(例如在usb_serial_probe函数中创建端口时)。 步骤: 1. 获取当前usb_device的总线位置信息,并判断是否为目标设备(总线2-1.3)。 2. 如果是目标设备,则强制为其端口分配次设备号7。 关键点:如何获取设备的总线位置? 在usb_device结构中,我们可以通过devpath成员获取设备路径(例如“2-1.3”),但注意内核中表示设备路径的方式可能有差异。另外,我们还可以通过总线号(bus->busnum)和设备地址(dev->devnum)以及父设备的信息来构建完整的路径。 然而,更简单的方法是使用usb_device的devpath,它是一个字符串,表示设备在总线上的位置。例如,设备2-1.3的devpath可能为“1.3”(因为总线号在上级总线中)。但注意,整个设备的标识需要包括总线号。实际上,完整的设备位置可以表示为:usb_device->bus->busnum和usb_device->devpath。 在usb-serial驱动中,我们可以通过usb_serial_port *port的port->serial->dev成员获取usb_device结构。 修改代码: 我们可以在创建端口(port)时,在调用tty_port_register_device函数之前,判断设备的总线位置,然后动态修改该端口应该使用的次设备号。 具体修改函数:我们可以在usb_serial_probe函数中,遍历串行设备的每个端口(port)时,对每个端口进行判断。但注意,通常一个设备只有一个端口,所以我们可以只修改第一个端口。 伪代码: 在usb_serial_probe函数中,在调用usb_serial_probe_interface函数后,会为每个端口创建设备。我们可以在此过程中修改特定端口的次设备号。 但是,实际上端口设备的创建是在usb_serial_device_register函数中完成的,而这个函数在usb_serial_probe中调用。我们可能需要在端口设备创建之前就设置好次设备号。 另一个关键点:次设备号是动态分配还是静态指定? 在usb-serial驱动中,默认是动态分配次设备号(使用alloc_tty_driver和tty_register_driver时指定动态分配)。但我们可以强制修改某个端口的次设备号。 然而,强制指定次设备号需要确保该次设备号尚未被使用。因此,我们需要在驱动初始化时保留这个次设备号(例如,通过tty_register_driver时指定允许的次设备号范围,并确保7没有被占用)。但这种方法较为复杂。 替代方案:我们可以在usb_serial_port_register函数中,在调用tty_port_register_device之前,判断设备的总线位置,然后调用tty_port_register_device_attr函数时,指定一个我们想要的次设备号(即设备号)。但是,tty_port_register_device系列函数并不允许直接指定次设备号,它是动态分配的。 因此,我们需要改变思路:修改usb-serial驱动,使其在分配次设备号时,对特定设备进行特殊处理。 在usb-serial.c中,有一个全局的tty_driver结构(serial_tty_driver),它管理着所有的usb串口设备。当我们调用tty_port_register_device时,实际上是调用了tty_register_device(serial_tty_driver, ...),这个函数会动态分配一个次设备号(如果我们传递的index参数为-1)。我们可以改为传递固定的index(即7),但是这样会忽略动态分配,而且我们必须确保7是空闲的。 但是,我们无法保证7没有被占用。因此,我们需要在驱动中记录哪些次设备号已经被分配。通常,内核的tty驱动会使用一个位图来管理次设备号。我们可以在驱动初始化时预留次设备号7(例如,在alloc_tty_driver时设置num为256,然后通过位图标记7为已使用?这样不行,因为动态分配是自动的)。 另一种方法:在usb-serial驱动中,有一个结构管理每个端口的次设备号。实际上,在usb_serial_port结构中有一个minor成员,它记录了分配给这个端口的次设备号。在usb_serial_probe函数中,调用port_alloc时,会初始化minor为-1。然后,在usb_serial_device_register函数中,调用usb_serial_port_register函数,该函数中会设置minor: port->minor = usb_serial_port_minor++; if (port->minor >= serial_tty_driver->num) { port->minor = 0; usb_serial_port_minor = 0; } 这是动态分配的一个简单循环。因此,我们可以修改这个分配过程:在分配minor之前,判断是否是目标设备,如果是,则直接指定minor=7(但需要确保7没有被占用)。同时,我们需要跳过7,防止其他设备使用该minor。 具体步骤: 1.usb-serial.c中,定义一个静态变量,用于记录次设备号7是否已被占用。 2. 修改usb_serial_port_register函数(或者修改usb_serial_device_register函数中调用usb_serial_port_register之前的部分),在分配minor时,先判断当前设备是否是目标设备(总线2-1.3),如果是,则检查7是否已被占用。如果未被占用,则设置port->minor=7,并将标记置为已占用;如果已被占用,则分配失败(或者分配其他minor,但这样就不能固定为7了,所以最好在初始化时保留7)。 3. 如果不是目标设备,则按原方式分配。 但是,我们需要确保在目标设备被拔掉后释放次设备号7,否则下次插入就无法再使用7了。因此,我们需要在设备断开时(在usb_serial_disconnect函数中)重置占用的次设备号。 实现: 在usb-serial.c中: - 定义一个静态变量:static bool minor7_used = false; -usb_serial_port_register函数中,添加: struct usb_device *dev = port->serial->dev; char bus_dev[32]; // 构造总线位置字符串:总线号-设备路径 snprintf(bus_dev, sizeof(bus_dev), "%d-%s", dev->bus->busnum, dev->devpath); // 判断是否是目标设备 if (strcmp(bus_dev, "2-1.3") == 0) { if (!minor7_used) { port->minor = 7; minor7_used = true; dev_info(&port->dev, "Assigned fixed minor 7 for device %s\n", bus_dev); } else { dev_err(&port->dev, "Minor 7 is already used, cannot assign to device %s\n", bus_dev); // 按正常分配,但这样就不是ttyUSB7了 port->minor = usb_serial_port_minor++; if (usb_serial_port_minor >= serial_tty_driver->num) usb_serial_port_minor = 0; } } else { // 正常分配 port->minor = usb_serial_port_minor++; if (usb_serial_port_minor >= serial_tty_driver->num) usb_serial_port_minor = 0; } 但是,上述代码有并发问题,因为多个设备可能同时调用usb_serial_port_register。因此,需要使用锁保护。我们可以使用已有的serial_tty_driver->driver_lock(或者usb_serial_port_minor原本使用的锁?实际上,在usb_serial_port_register函数中,修改usb_serial_port_minor时没有加锁,这是不安全的。实际上,在较早的内核版本中,这个分配是在一个互斥区中进行的(例如serial_mutex),但我们需要查看当前内核版本中的实现。 在Linux 5.10中,usb_serial_port_register函数是在usb_serial_device_register函数中调用的,而usb_serial_device_register是在usb_serial_probe中调用的。由于usb_serial_probe是由USB核心在探测设备时调用的,并且使用USB总线的锁,所以通常不会并发。但是,为了安全,我们仍然需要加锁。我们可以使用一个自旋锁来保护minor7_used和usb_serial_port_minor。 然而,在5.10内核中,usb_serial_port_minor的分配并没有使用锁,因此我们需要修改这里,增加锁机制。我们可以定义一个自旋锁,用于保护usb_serial_port_minor和minor7_used。 修改: 1. 定义自旋锁: static DEFINE_SPINLOCK(minor_lock); 2. 在分配minor时加锁: unsigned long flags; spin_lock_irqsave(&minor_lock, flags); // 分配逻辑 spin_unlock_irqrestore(&minor_lock, flags); 此外,在usb_serial_port_release函数(当端口被释放时)中,我们需要回收minor。对于强制分配的7,我们需要将minor7_used置为false;对于动态分配的,则需要将usb_serial_port_minor回退?但是动态分配的不需要回退,因为它是递增的。但是动态分配的我们不需要记录,只有7需要回收。 修改usb_serial_port_release函数(该函数在端口被注销时调用): if (port->minor == 7) { spin_lock_irqsave(&minor_lock, flags); minor7_used = false; spin_unlock_irqrestore(&minor_lock, flags); } 这样,当设备断开时,次设备号7就会被释放。 注意:我们需要确保在设备断开时,这个端口确实注销了。在usb_serial_disconnect函数中,会调用usb_serial_deregister_ports,进而调用usb_serial_port_release。 代码修改总结: 在usb-serial.c文件中: 1. 在文件开头定义静态变量: static bool minor7_used = false; static DEFINE_SPINLOCK(minor_lock); 2. 修改usb_serial_port_register函数(或者找到分配minor的代码位置,通常在usb_serial_port_register中),将原来的动态分配minor的代码替换为: unsigned long flags; struct usb_device *dev = port->serial->dev; char bus_dev[32]; snprintf(bus_dev, sizeof(bus_dev), "%d-%s", dev->bus->busnum, dev->devpath); spin_lock_irqsave(&minor_lock, flags); if (strcmp(bus_dev, "2-1.3") == 0) { if (!minor7_used) { port->minor = 7; minor7_used = true; spin_unlock_irqrestore(&minor_lock, flags); dev_info(&port->dev, "Assigned fixed minor 7 for device %s\n", bus_dev); } else { spin_unlock_irqrestore(&minor_lock, flags); dev_err(&port->dev, "Minor 7 is already used, cannot assign to device %s\n", bus_dev); // 分配一个动态的minor port->minor = usb_serial_port_minor++; if (usb_serial_port_minor >= serial_tty_driver->num) usb_serial_port_minor = 0; } } else { port->minor = usb_serial_port_minor++; if (usb_serial_port_minor >= serial_tty_driver->num) usb_serial_port_minor = 0; spin_unlock_irqrestore(&minor_lock, flags); } 注意:原本的动态分配代码是没有加锁的,现在我们加了锁,并且将动态分配部分移到了锁内(针对非目标设备)。对于目标设备,如果7已被占用,则在锁外做动态分配(因为动态分配部分需要操作usb_serial_port_minor,所以也要在锁内?)。因此,我们需要将动态分配部分也放在锁内。 为了避免重复代码,我们将动态分配提取出来。修改如下: unsigned long flags; struct usb_device *dev = port->serial->dev; char bus_dev[32]; snprintf(bus_dev, sizeof(bus_dev), "%d-%s", dev->bus->busnum, dev->devpath); bool is_target = (strcmp(bus_dev, "2-1.3") == 0); spin_lock_irqsave(&minor_lock, flags); if (is_target && !minor7_used) { port->minor = 7; minor7_used = true; spin_unlock_irqrestore(&minor_lock, flags); dev_info(&port->dev, "Assigned fixed minor 7 for device %s\n", bus_dev); } else { if (is_target) { dev_err(&port->dev, "Minor 7 is already used, cannot assign to device %s. Falling back to dynamic minor.\n", bus_dev); } port->minor = usb_serial_port_minor++; if (usb_serial_port_minor >= serial_tty_driver->num) usb_serial_port_minor = 0; spin_unlock_irqrestore(&minor_lock, flags); } 3. 修改usb_serial_port_release函数,在释放端口时,如果是次设备号7,则释放标记: if (port->minor == 7) { unsigned long flags; spin_lock_irqsave(&minor_lock, flags); minor7_used = false; spin_unlock_irqrestore(&minor_lock, flags); } 注意事项: - 字符串“2-1.3”需要与实际设备的路径匹配。但注意,dev->devpath可能不包含总线号,所以我们用dev->bus->busnum和dev->devpath组合。在设备路径中,devpath是一个类似于“1.3”的字符串,所以组合后就是“2-1.3”。 - 但是,实际设备路径可能会有所不同,我们需要确认目标设备的路径。可以在内核日志中打印设备的总线位置信息,或者使用usb-devices命令查看。 另外,在Android环境下,需要编译内核模块并集成到系统中。这可能需要重新编译内核或构建内核模块。 由于Android的内核通常使用设备树(Device Tree)和特定的配置,我们需要确保修改的内核模块能够被正确编译和加载。 总结: 通过修改usb-serial.c驱动,在端口注册时根据设备的总线位置强制分配次设备号7,并在端口释放时回收,可以实现将特定USB转串口设备固定绑定到ttyUSB7,而不依赖于udev规则。 但是,这种方法有一定的局限性: - 只能固定一个设备到ttyUSB7,如果有多个设备需要固定,需要扩展代码。 - 修改内核驱动需要重新编译内核或模块,并且需要系统支持。 最后,请在实际修改前确认目标设备的总线位置字符串是否正确,并确保在修改代码后测试设备连接和断开时次设备号的分配和释放是否正常。 相关问题: §§相关问题§§ 1. 如何获取USB设备在Linux内核中的总线位置信息? 2. USB串口驱动中次设备号分配机制是怎样的? 3. 在Android系统中,如何重新编译内核模块并替换? </think> ### 在Android 11 Kernel 5.10中修改usb-serial.c实现设备绑定 要在Android 11(内核5.10)中通过修改`usb-serial.c`驱动将特定USB转串口设备(总线位置2-1.3)绑定到`ttyUSB7`节点,且不使用udev规则,需按以下步骤操作: --- #### **1. 修改驱动逻辑核心思路** 在内核驱动层面强制指定次设备号(minor number),通过设备物理位置(总线号+端口路径)识别目标设备: ```c // 示例修改位置:drivers/usb/serial/usb-serial.c static DEFINE_SPINLOCK(minor_lock); // 新增自旋锁 static bool minor7_used = false; // 次设备号7占用标志 int usb_serial_register_port(struct usb_serial *serial, struct usb_serial_port *port) { struct usb_device *udev = serial->dev; unsigned long flags; // 构造设备物理位置标识 (格式: 总线号-端口路径) char bus_path[32]; snprintf(bus_path, sizeof(bus_path), "%d-%s", udev->bus->busnum, udev->devpath); spin_lock_irqsave(&minor_lock, flags); // 检测目标设备 (2-1.3) 并分配次设备号7 if (strcmp(bus_path, "2-1.3") == 0 && !minor7_used) { port->minor = 7; // 强制指定次设备号 minor7_used = true; // 标记已占用 pr_info("USB-SERIAL: Fixed minor=7 for %s\n", bus_path); } else { // 其他设备动态分配 port->minor = usb_serial_port_minor++; if (usb_serial_port_minor >= SERIAL_TTY_MINORS) usb_serial_port_minor = 0; } spin_unlock_irqrestore(&minor_lock, flags); ... } ``` #### **2. 释放设备号逻辑** 在设备断开时释放占用的次设备号: ```c void usb_serial_deregister_port(struct usb_serial_port *port) { unsigned long flags; ... if (port->minor == 7) { // 释放次设备号7 spin_lock_irqsave(&minor_lock, flags); minor7_used = false; spin_unlock_irqrestore(&minor_lock, flags); } ... } ``` --- #### **3. 关键实现细节** | **组件** | **作用** | **内核参考** | |------------------|--------------------------------------------------------------------------|---------------------------| | `dev->bus->busnum` | 获取USB总线编号 (如总线2) | `struct usb_device` | | `dev->devpath` | 获取设备端口路径 (如1.3) | `struct usb_device` | | `port->minor` | 次设备号,直接对应`ttyUSBX`末位数字 | `struct usb_serial_port` | | 自旋锁 | 防止多设备并发操作导致次设备号冲突 | `DEFINE_SPINLOCK()` | --- #### **4. 验证与调试** 1. **查看设备位置**:通过`lsusb -t`命令确认设备拓扑: ``` /: Bus 02.Port 1: Dev 1, ... |__ Port 3: Dev 27, ID 067b:2303 // 目标设备:总线2-1.3 ``` 2. **内核日志**:插入设备后检查dmesg: ``` [ 125.477] USB-SERIAL: Fixed minor=7 for 2-1.3 [ 125.478] usb 2-1.3: pl2303 converter now attached to ttyUSB7 ``` --- #### **5. Android系统集成** 1. **编译内核模块**: ```bash cd /path/to/android-kernel make drivers/usb/serial/usb-serial.ko ``` 2. **替换驱动**: ```bash adb push usb-serial.ko /vendor/lib/modules/ insmod /vendor/lib/modules/usb-serial.ko ``` 3. **关闭udev**:在Android设备树(DTB)或内核配置中禁用`CONFIG_HOTPLUG`。 --- #### **技术原理** - **设备节点生成**:USB转串口设备节点命名由`usb-serial`驱动管理,次设备号直接决定`ttyUSBX`编号[^1]。 - **物理路径绑定**:USB设备的`busnum+devpath`在系统运行时唯一稳定,不受枚举顺序影响[^2]。 - **内核安全机制**:自旋锁确保多设备竞争场景下资源安全分配。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值