USB协议分析(pl2303)

在hub.c的hub_port_connect_change中,检测到有USB设备插进来后执行该代码。

1. USB协议中规定,插入设备后,主机要至少等待100ms,让设备完成插入以及上电动作;hub_port_debounce(hub, port1)就是起到这个作用。

2. 接着在hub_port_init函数中,USB协议规定,上电后,HUB要响USB设备发送持续10ms的复位信号(D+、D-都拉低), hub_port_reset(hub, port1, udev, delay)完成该动作。

这样,当信号完成后,端口就有效了,设备处于缺省状态(Default state),并且获得主机提供的100mA的电流;主机可以和设备地址0,端点0(即default pipe)通过控制传输进行通讯,端点0比较特殊,可以写入,可以读出;其他端点都是单向的,比如U盘有两个断电,从端点1写入数据,端点2读出数据。

要注意,USB术语中端点的IN和OUT,是从主机端的立场来看的,比如USB鼠标输入数据到电脑,对应的端点即为输入端点。

3. 

#define GET_DESCRIPTOR_BUFSIZE  64

           for (j = 0; j < 3; ++j) {
                buf->bMaxPacketSize0 = 0;
                r = usb_control_msg(udev, usb_rcvaddr0pipe(),
                    USB_REQ_GET_DESCRIPTOR, USB_DIR_IN,
                    USB_DT_DEVICE << 8, 0,
                    buf, GET_DESCRIPTOR_BUFSIZE,
                    initial_descriptor_timeout);
                switch (buf->bMaxPacketSize0) {
                case 8: case 16: case 32: case 64: case 255:
                    if (buf->bDescriptorType ==
                            USB_DT_DEVICE) {
                        r = 0;
                        break;
                    }
                    /* FALL THROUGH */
                default:
                    if (r == 0)
                        r = -EPROTO;
                    break;
                }
                if (r == 0)
                    break;
            }

获取设备描述符(device descriptor),请求的长度是64,虽然一般的设备描述符长度只有18,但主机并不在乎,从switch语句看出,它更在乎的是描述符的总长度信息。
4. 获取完设备描述符后,接着hub_port_reset(hub, port1, udev, delay)再次对设备进行复位(USB协议中并没有这一步的要求),这次复位的目的是使设备进入一个确定的状态。

5. 往下,hub_set_address(udev, devnum)给设备分配一个唯一的新地址,设备处于编址状态(Address state).

6.  有了新地址后,再次获取设备描述符,用usb_get_device_descriptor(udev, USB_DT_DEVICE_SIZE)来获得,USB_DT_DEVICE_SIZE 为18。这次,主机会认真解析设备描述符的信息,包括断电0的最大包长度、设备所支持的配置个数、设备类型、VID、PID等。hub_port_init函数就完成了。


7. 回到hub_port_connect_change函数继续往下,usb_new_device(udev)函数:

int usb_new_device(struct usb_device *udev)
{
。。。。。。
    err = usb_enumerate_device(udev);   /* Read descriptors */
。。。。。。
    /* Tell the world! */
    announce_device(udev);
。。。。。。
    /* Register the device.  The device driver is responsible
     * for configuring the device and invoking the add-device
     * notifier chain (used by usbfs and possibly others).
     */
    err = device_add(&udev->dev);
。。。。。。
}


usb_enumerate_device(udev)称为枚举过程,就是读取配置描述符(configuration descriptor)并解析的过程:

static int usb_enumerate_device(struct usb_device *udev)                                                                                                       
{
    int err; 

    if (udev->config == NULL) {
        err = usb_get_configuration(udev);
        if (err < 0) { 
            dev_err(&udev->dev, "can't read configurations, error %d\n",
                err);
            goto fail;
        }    
    }    
    if (udev->wusb == 1 && udev->authorized == 0) { 
        udev->product = kstrdup("n/a (unauthorized)", GFP_KERNEL);
        udev->manufacturer = kstrdup("n/a (unauthorized)", GFP_KERNEL);
        udev->serial = kstrdup("n/a (unauthorized)", GFP_KERNEL);
    }    
    else {
        /* read the standard strings and cache them if present */
        udev->product = usb_cache_string(udev, udev->descriptor.iProduct);
        udev->manufacturer = usb_cache_string(udev,
                              udev->descriptor.iManufacturer);
        udev->serial = usb_cache_string(udev, udev->descriptor.iSerialNumber);
    }    
    err = usb_enumerate_device_otg(udev);
fail:
    return err; 
}

USB协议规定设备的配置个数不能大于8个,至少有1个;从前面的设备描述符中主机可以知道设备有多少中配置:

 ncfg = dev->descriptor.bNumConfigurations

所以usb_get_configuration(udev)作用就是用for循环依次读取配置描述符信息并解析保存起来,要注意,在这里已经获取到了接口描述符和字符串描述符(如果有的话),所以一并解析保存在一个usb_host_config(每个配置描述符创建一个该结构)的结构体中:

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;
};

announce_device(udev)作用是打印信息,包括PID、VID、SN等告诉我们有新设备连接上来了;

device_add(&udev->dev)就把该设备添加到USB总线上了,因为这时候主机已经从设备出获取了足够的信息,USB HUB只能做到这里了,后续的事情就要由具体的设备驱动来完成了。


根据前一篇文章的分析,接着往下走就来到了generic.c的generic_probe方法中:

static int generic_probe(struct usb_device *udev)                                                                                                              
{
    int err, c;

    /* Choose and set the configuration.  This registers the interfaces
     * with the driver core and lets interface drivers bind to them.
     */
    if (usb_device_is_owned(udev))
        ;       /* Don't configure if the device is owned */
    else if (udev->authorized == 0)
        dev_err(&udev->dev, "Device is not authorized for usage\n");
    else {
        c = usb_choose_configuration(udev);
        if (c >= 0) {
            err = usb_set_configuration(udev, c); 
            if (err) {
                dev_err(&udev->dev, "can't set config #%d, error %d\n",
                    c, err);
                /* This need not be fatal.  The user can try to
                 * set other configurations. */
            }   
        }   
    }   
    /* USB device state == configured ... usable */
    usb_notify_add_device(udev);

    return 0;
}

usb_choose_configuration(udev)会根据一些条件来选择一个配置,比如判断该配置下描述的电流是否过大,然后LINUX根据自己喜好,选择了一个不是USB_CLASS_VENDOR_SPEC的配置,代码片段如下:

        /* From the remaining configs, choose the first one whose
         * first interface is for a non-vendor-specific class.
         * Reason: Linux is more likely to have a class driver
         * than a vendor-specific driver. */
        else if (udev->descriptor.bDeviceClass !=
                        USB_CLASS_VENDOR_SPEC &&                                                                                                               
                (desc && desc->bInterfaceClass !=
                        USB_CLASS_VENDOR_SPEC)) {
            best = c;
            break;
        }  

#define USB_CLASS_VENDOR_SPEC       0xff 


USB协议中规定,在 usb_device_descriptor类中bDeviceClass代表的是设备的类型代码,如果是0x01 ~ 0xfe,代表标准设备,如果是0xff代表厂商自定义设备;usb_interface_descriptor的bInterfaceClass也一样代表设备类型。

其实大多数设备只有一种配置而已,这样就返回了配置编号

接着usb_set_configuration(udev, c)把选择的配置写入设备。在该函数中完成配置后用 usb_enable_interface(dev, intf, true)使接口生效;创建cp->desc.bNumInterfaces个代表该配置下接口的device并用device_add(&intf->dev)添加到USB核心中去;可见,一个接口对应一个驱动,bNumInterfaces个接口就要bNumInterfaces个驱动了;不过由于一个device_driver可以对应多个device,所以像pl2303就是一个driver,但是可以处理多个接口。

下边通过具体的例子来分析这个接口。

把pl2303线插入平板上,在/sys/bus/usb/devices多出了两个设备

3-1
3-1:1.0

3-1代表的就是usb_device结构体中的device,这是在hub_port_connect_change中创建的,名字的由来如下:

dev_set_name(&dev->dev, "%d-%s", bus->busnum, dev->devpath);

3代表这个pl2303挂在第三个控制器上,1代表root hub下的一个控制器,如果pl2303再通过一个外接hub连接到平板上,则为2

3-1:1.0代表的就是pl2303的接口,可见该设备只有一个接口,名字的创建如下:

        dev_set_name(&intf->dev, "%d-%s:%d.%d",
            dev->bus->busnum, dev->devpath,
            configuration, alt->desc.bInterfaceNumber);
用的是1配置,0接口。进入3-1:1.0目录看到如下信息:

bAlternateSetting
bInterfaceClass
bInterfaceNumber
bInterfaceProtocol
bInterfaceSubClass
bNumEndpoints
driver
ep_02
ep_81
ep_83
modalias
power
subsystem
supports_autosuspend
ttyUSB0
uevent
看到有三个端点,再进入端点目录发现ep_02为bulk传输,方向为out;ep_81为Interrupt传输,方向为in;ep_83为Bulk传输,方向为in; bInterfaceClass为ff,表示是厂商自定义的设备。

而进入3-1目录看到如下信息(有设备描述符和配置描述符的信息):

3-1:1.0
authorized
avoid_reset_quirk
bConfigurationValue
bDeviceClass
bDeviceProtocol
bDeviceSubClass
bMaxPacketSize0
bMaxPower
bNumConfigurations
bNumInterfaces
bcdDevice
bmAttributes
busnum
configuration
descriptors
dev
devnum
devpath
driver
ep_00
idProduct
idVendor
manufacturer
maxchild
power
product
quirks
removable
remove
speed
subsystem
uevent
urbnum
usb_device
version

bNumConfigurations为1,只有一种配置;bNumInterfaces为1,只有一个接口;ep_00代表控制端点0,因为这是每个usb设备都必须有的,作为共性放到了这里。


最后进入pl2303驱动看usb_serial_probe函数的实现。

int usb_serial_probe(struct usb_interface *interface,
                   const struct usb_device_id *id) 
{
.........................
    type = search_serial_device(interface);
........................
    serial = create_serial(dev, interface, type);
.........................
    for (i = 0; i < num_bulk_in; ++i) {
            usb_fill_bulk_urb(port->read_urbs[j], dev,
                    usb_rcvbulkpipe(dev,
                        endpoint->bEndpointAddress),
                    port->bulk_in_buffers[j], buffer_size,
                    serial->type->read_bulk_callback,
                    port);
        }
}
............................................
    for (i = 0; i < num_bulk_out; ++i) {
            usb_fill_bulk_urb(port->write_urbs[j], dev,
                    usb_sndbulkpipe(dev,
                        endpoint->bEndpointAddress),
                    port->bulk_out_buffers[j], buffer_size,
                    serial->type->write_bulk_callback,
                    port);
}
.....................................
    if (serial->type->read_int_callback) {
        for (i = 0; i < num_interrupt_in; ++i) { 
            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);
}
...............................................
    if (serial->type->write_int_callback) {
        for (i = 0; i < num_interrupt_out; ++i) {
            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);
}
...................................
}
其实就是用usb_fill_bulk_urb或者usb_fill_int_urb函数来注册中断或者批量传输函数,以后有数据到来或者有数据需要发送,调用相应的函数来处理。

不过要注意,这里还用到serial/generic.c中的相应函数,该文件提供了通用的访问接口,比如pl2303.c中没有定义read_bulk_callback函数,但是在usb-serical.c中有:

#define set_to_generic_if_null(type, function)              \
    do {                                \
        if (!type->function) {                  \
            type->function = usb_serial_generic_##function; \
            dbg("Had to override the " #function        \
                " usb serial operation with the generic one.");\
            }                       \
    } while (0)

static void fixup_generic(struct usb_serial_driver *device)
{
    set_to_generic_if_null(device, open);
    set_to_generic_if_null(device, write);
    set_to_generic_if_null(device, close);
    set_to_generic_if_null(device, write_room);
    set_to_generic_if_null(device, chars_in_buffer);
    set_to_generic_if_null(device, read_bulk_callback);
    set_to_generic_if_null(device, write_bulk_callback);                                                                                                       
    set_to_generic_if_null(device, disconnect);
    set_to_generic_if_null(device, release);
    set_to_generic_if_null(device, process_read_urb);
    set_to_generic_if_null(device, prepare_write_buffer);
}

从宏定义可以看出,如果没有定义的话,就用generic.c的来代替。

这样,当串口有数据到来时候,调用read_bulk_callback:

void usb_serial_generic_read_bulk_callback(struct urb *urb)                                                                                                    
{
    struct usb_serial_port *port = urb->context;
    unsigned char *data = urb->transfer_buffer;
    unsigned long flags;
    int i;

    for (i = 0; i < ARRAY_SIZE(port->read_urbs); ++i) {
        if (urb == port->read_urbs[i])
            break;
    }   
    set_bit(i, &port->read_urbs_free);

    dbg("%s - port %d, urb %d, len %d\n", __func__, port->number, i,
                            urb->actual_length);
    if (urb->status) {
        dbg("%s - non-zero urb status: %d\n", __func__, urb->status);
        return;
    }   

    usb_serial_debug_data(debug, &port->dev, __func__,
                        urb->actual_length, data);
    port->serial->type->process_read_urb(urb);

    /* Throttle the device if requested by tty */
    spin_lock_irqsave(&port->lock, flags);
    port->throttled = port->throttle_req;
    if (!port->throttled) {
        spin_unlock_irqrestore(&port->lock, flags);
        usb_serial_generic_submit_read_urb(port, i, GFP_ATOMIC);
    } else
        spin_unlock_irqrestore(&port->lock, flags);
}
EXPORT_SYMBOL_GPL(usb_serial_generic_read_bulk_callback);

这时候数据已经在urb结构中了,接着调用pl2303.c的process_read_urb方法:

static void pl2303_process_read_urb(struct urb *urb)
{
。。。。。。。。。。
    if (line_status & UART_OVERRUN_ERROR)
        tty_insert_flip_char(tty, 0, TTY_OVERRUN);

    if (port->port.console && port->sysrq) {
        for (i = 0; i < urb->actual_length; ++i)
            if (!usb_serial_handle_sysrq_char(port, data[i]))
                tty_insert_flip_char(tty, data[i], tty_flag);
    } else {
        tty_insert_flip_string_fixed_flag(tty, data, tty_flag,
                            urb->actual_length);
    }

    tty_flip_buffer_push(tty);
    tty_kref_put(tty);
}

根据前面tty驱动的分析可知,tty_flip_buffer_push函数就把数据通过链路规程提交给tty 核心层处理了。













  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
usb-serial驱动是Linux系统中用于支持USB串口设备的一个内核模块。pl2303.ko是其中一个常见的驱动模块,用于支持使用Prolific PL2303芯片的USB串口设备。 PL2303芯片是Prolific公司生产的一种常见的USB转串口芯片。它可以将USB接口转换为标准的串口通信接口,从而实现计算机与其他串口设备(如串口打印机、串口调试工具等)的连接和通信。 在Ubuntu 16.04操作系统中使用PL2303芯片的USB串口设备时,需要加载pl2303.ko内核模块来支持该设备的使用。内核模块是一个可插拔的代码,可以被Linux内核动态加载,并加入内核运行的过程中。 为了加载pl2303.ko内核模块,首先需要确认系统已经安装了相关的驱动程序。如果没有安装,可以通过命令行输入以下命令进行安装: sudo apt-get install linux-headers-$(uname -r) build-essential sudo apt-get install git sudo git clone https://github.com/jeremyb31/PL2303_Tablet.git cd PL2303_Tablet sudo make sudo cp pl2303.ko /lib/modules/$(uname -r)/kernel/drivers/usb/serial sudo depmod -a 之后,通过命令行输入以下命令来加载pl2303.ko内核模块: sudo modprobe pl2303 成功加载了pl2303.ko内核模块后,系统会自动识别和初始化连接到计算机上的PL2303芯片的USB串口设备。我们可以使用相应的设备节点(例如/dev/ttyUSB0)来进行串口通信的配置和操作。 总结而言,usb-serial驱动的pl2303.ko模块是用于支持PL2303芯片的USB串口设备在Ubuntu 16.04操作系统中的使用。通过加载该内核模块,我们可以使用USB串口设备与其他串口设备进行通信。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值