Linux下USB转串驱动源码分析(1)

时间:2022-08-15 09:23:54
   一、总线和驱动的注册,和别的驱动一样从module_init(ftdi_init);函数开始, ftdi_init------->
1. 先添加指定的VID/PID到 id_table_combined
if (vendor > 0 && product > 0) {
		<span style="color:#ff0000;">/* Add user specified VID/PID to reserved element of table. */
		int i;
		for (i = 0; id_table_combined[i].idVendor; i++)
			;
		id_table_combined[i].match_flags = USB_DEVICE_ID_MATCH_DEVICE;
		id_table_combined[i].idVendor = vendor;
		id_table_combined[i].idProduct = product;</span>
	}

2.  usb_serial_register(&ftdi_sio_device) 在USB转串口总线上注册驱动,它只是想USB总线注册,并不想kernel注册
-------------------------------------------------------------ftdi_sio_device-----------------------------------------------------------------------------
static struct usb_serial_driver ftdi_sio_device = {
.driver = {
.owner = THIS_MODULE,
.name = "ftdi_sio",
},
.description = "FTDI USB Serial Device",
.usb_driver = &ftdi_driver,
.id_table = id_table_combined,
.num_ports = 1,
.bulk_in_size = 512,
.bulk_out_size = 256,
.probe = ftdi_sio_probe,
.port_probe = ftdi_sio_port_probe,
.port_remove = ftdi_sio_port_remove,
.open = ftdi_open,
.close = ftdi_close,
.dtr_rts = ftdi_dtr_rts,
.throttle = usb_serial_generic_throttle,
.unthrottle = usb_serial_generic_unthrottle,
.process_read_urb = ftdi_process_read_urb,
.prepare_write_buffer = ftdi_prepare_write_buffer,
.tiocmget = ftdi_tiocmget,
.tiocmset = ftdi_tiocmset,
.ioctl = ftdi_ioctl,
.set_termios = ftdi_set_termios,
.break_ctl = ftdi_break_ctl,
};
-------------------------------------------------------------------------------------------------------------------------------------------

3. usb_register(&ftdi_driver) 注册一条USB总线
----------------------------------------------------------------------ftdi_driver--------------------------------------------------------------------
static struct usb_driver ftdi_driver = {
.name = "ftdi_sio",
.probe = usb_serial_probe,
.disconnect = usb_serial_disconnect,
.id_table = id_table_combined,
.no_dynamic_id = 1,
};
-----------------------------------------------------------------------------------------------------------------------------------------------------
这时总线和驱动都已经准备就绪,只需要设备接入了。

二、当设备接入时,kernel将去调用 usb_serial_probe 函数。
  1. 调用search_serial_device来收索设备,并获得 ftdi_sio_device 这个结构体 , 如果失败将返回错误信息。
   2. 有设备与之匹配后,调用creat_serial函数去创建usb_serial结构体,同时对该结构体赋值,serial->type = driver;(即 ftdi_sio_device  ), serial->interface = interface;------》此函数使用了kzalloc为设备分配usb_serial空间。
  3. 如果创建成功,系统将调用usb serial 的探测函数probe了,即ftdi_sio_probe, 这个上面结构体中已经编写好了。
<span style="color:#333333;">static int ftdi_sio_probe(struct usb_serial *serial,
					const struct usb_device_id *id)
{
	struct ftdi_sio_quirk *quirk =
				(struct ftdi_sio_quirk *)id->driver_info;
	if (quirk && quirk->probe) {
		int ret = quirk->probe(serial);
		if (ret != 0)
			return ret;
	}
	usb_set_serial_data(serial, (void *)id->driver_info);  //将serial->private = </span><span style="font-family: Arial;"><span style="color:#333333;">id->driver_info, </span><span style="color:#3333ff;">此处的</span></span><span style="font-family: Arial;"><span style="color:#3333ff;">id->driver_info这个东西还没有看懂怎么来的,主要是interface还没有看是怎么出来的,兴许随着分析的深入就能找到答案。</span></span><span style="color: rgb(51, 51, 51); font-family: Arial;">
</span><span style="color:#333333;">	return 0;
}</span>
  4. 接下来循环检测端点类型
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;
		}
	}
   5. 接着讲检测处理的结果赋值给serial,之后算出最大端点 max_endpoints
   
    6. 对每一个端点进行设置和相关的初始化工作
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);//<span style="color:#3333ff;">初始化tty端口(z主要是这两个port->close_delay, port->closing_wait)</span>
		port->port.ops = &<span style="color:#ff0000;">serial_port_ops</span>; //⑴<span style="background-color: rgb(255, 255, 255);"><span style="color:#3333ff;">这个之后用着的时候进行详细分析</span></span>
		port->serial = serial;
		spin_lock_init(&port->lock);
		/* Keep this for private driver use for the moment but
		   should probably go away */
		INIT_WORK(&port->work, <span style="color:#ff0000;">usb_serial_port_work</span>);//⑵<span style="color:#3333ff;">创建以个工作队列,仍然是在使用到时在进行分析</span>
		serial->port[i] = port;
		port->dev.parent = &interface->dev;
		port->dev.driver = NULL;
		port->dev.bus = &<span style="color:#ff0000;">usb_serial_bus_type</span>;//⑶
		port->dev.release = &<span style="color:#ff0000;">port_release</span>;//⑷
		device_initialize(&port->dev);
	}
    7. 建立端点信息
for (i = 0; i < num_bulk_in; ++i) {
		endpoint = bulk_in_endpoint[i];
		port = serial->port[i];
		<span style="color:#ff0000;">port->read_urb = usb_alloc_urb(0, GFP_KERNEL)</span>;//分配<span style="color:#3366ff;">URB</span>
		if (!port->read_urb) {
			dev_err(&interface->dev, "No free urbs available\n");
			goto probe_error;
		}
		buffer_size = serial->type->bulk_in_size;
		if (!buffer_size)
			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);
	}
<span style="font-size: 14px; line-height: 26px;">for (i = 0; i < num_bulk_out; ++i) {
		int j;

		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;
		}
		if (kfifo_alloc(&port->write_fifo, PAGE_SIZE, GFP_KERNEL))
			goto probe_error;
		buffer_size = serial->type->bulk_out_size;
		if (!buffer_size)
			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);
		for (j = 0; j < ARRAY_SIZE(port->write_urbs); ++j) {
			set_bit(j, &port->write_urbs_free);
			port->write_urbs[j] = usb_alloc_urb(0, GFP_KERNEL);
			if (!port->write_urbs[j]) {
				dev_err(&interface->dev,
						"No free urbs available\n");
				goto probe_error;
			}
			port->bulk_out_buffers[j] = kmalloc(buffer_size,
								GFP_KERNEL);
			if (!port->bulk_out_buffers[j]) {
				dev_err(&interface->dev,
					"Couldn't allocate bulk_out_buffer\n");
				goto probe_error;
			}
			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) {
			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");
	}</span>