本节主要分析虚拟串口的tty设备的注册、创建/dev/ttyGSx设备节点、tty相关接口的实现。

tty的申请与注册

源码:drivers/usb/gadget/function/u_serial.c

static const struct tty_operations gs_tty_ops = {
.open = gs_open,
.close = gs_close,
.write = gs_write,
.put_char = gs_put_char,
.flush_chars = gs_flush_chars,
.write_room = gs_write_room,
.chars_in_buffer = gs_chars_in_buffer,
.unthrottle = gs_unthrottle,
.break_ctl = gs_break_ctl,
}; static int userial_init(void)
{
unsigned i;
int status; //申请tty_driver驱动
gs_tty_driver = alloc_tty_driver(MAX_U_SERIAL_PORTS);
if (!gs_tty_driver)
return -ENOMEM; //初始化tty_driver,设备节点名
gs_tty_driver->driver_name = "g_serial";
gs_tty_driver->name = "ttyGS";
/* uses dynamically assigned dev_t values */ gs_tty_driver->type = TTY_DRIVER_TYPE_SERIAL;
gs_tty_driver->subtype = SERIAL_TYPE_NORMAL;
gs_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
gs_tty_driver->init_termios = tty_std_termios; /* 9600-8-N-1 ... matches defaults expected by "usbser.sys" on
* MS-Windows. Otherwise, most of these flags shouldn't affect
* anything unless we were to actually hook up to a serial line.
*/
gs_tty_driver->init_termios.c_cflag =
B9600 | CS8 | CREAD | HUPCL | CLOCAL;
gs_tty_driver->init_termios.c_ispeed = 9600;
gs_tty_driver->init_termios.c_ospeed = 9600; //tty_driver的tty_operations初始化
tty_set_operations(gs_tty_driver, &gs_tty_ops);
for (i = 0; i < MAX_U_SERIAL_PORTS; i++)
mutex_init(&ports[i].lock); /* export the driver ... */
status = tty_register_driver(gs_tty_driver);
if (status) {
pr_err("%s: cannot register, err %d\n",
__func__, status);
goto fail;
} pr_debug("%s: registered %d ttyGS* device%s\n", __func__,
MAX_U_SERIAL_PORTS,
(MAX_U_SERIAL_PORTS == 1) ? "" : "s"); return status;
fail:
put_tty_driver(gs_tty_driver);
gs_tty_driver = NULL;
return status;
}
module_init(userial_init); static void userial_cleanup(void)
{
tty_unregister_driver(gs_tty_driver);
put_tty_driver(gs_tty_driver);
gs_tty_driver = NULL;
}
module_exit(userial_cleanup);

connect与disconnect

conect将会使能数据传输用的endpoint,同时启动数据数据的功能。

/**
* gserial_connect - notify TTY I/O glue that USB link is active
* @gser: the function, set up with endpoints and descriptors
* @port_num: which port is active
* Context: any (usually from irq)
*
* This is called activate endpoints and let the TTY layer know that
* the connection is active ... not unlike "carrier detect". It won't
* necessarily start I/O queues; unless the TTY is held open by any
* task, there would be no point. However, the endpoints will be
* activated so the USB host can perform I/O, subject to basic USB
* hardware flow control.
*
* Caller needs to have set up the endpoints and USB function in @dev
* before calling this, as well as the appropriate (speed-specific)
* endpoint descriptors, and also have allocate @port_num by calling
* @gserial_alloc_line().
*
* Returns negative errno or zero.
* On success, ep->driver_data will be overwritten.
*/
int gserial_connect(struct gserial *gser, u8 port_num)
{
struct gs_port *port;
unsigned long flags;
int status; if (port_num >= MAX_U_SERIAL_PORTS)
return -ENXIO; port = ports[port_num].port;
if (!port) {
pr_err("serial line %d not allocated.\n", port_num);
return -EINVAL;
}
if (port->port_usb) {
pr_err("serial line %d is in use.\n", port_num);
return -EBUSY;
} /* activate the endpoints */
//使能in_endpoint,可用于in数据传输
status = usb_ep_enable(gser->in);
if (status < 0)
return status;
gser->in->driver_data = port; //使能out_endpoint,可用于out数据传输
status = usb_ep_enable(gser->out);
if (status < 0)
goto fail_out;
gser->out->driver_data = port; /* then tell the tty glue that I/O can work */
spin_lock_irqsave(&port->port_lock, flags);
gser->ioport = port;
port->port_usb = gser; /* REVISIT unclear how best to handle this state...
* we don't really couple it with the Linux TTY.
*/
gser->port_line_coding = port->port_line_coding; /* REVISIT if waiting on "carrier detect", signal. */ /* if it's already open, start I/O ... and notify the serial
* protocol about open/close status (connect/disconnect).
*/
//若当前TTYGSx设备节点已打开,则启动tx/rx的功能
//并调用gserial的connect功能,详见drivers/usb/gadget/function/f_acm.c的acm_connect与acm_disconnect
if (port->port.count) {
pr_debug("gserial_connect: start ttyGS%d\n", port->port_num);
gs_start_io(port); if (gser->connect)
gser->connect(gser);
} else {
if (gser->disconnect)
gser->disconnect(gser);
} status = gs_console_connect(port_num);
spin_unlock_irqrestore(&port->port_lock, flags); return status; fail_out:
usb_ep_disable(gser->in);
return status;
}
EXPORT_SYMBOL_GPL(gserial_connect);
/**
* gserial_disconnect - notify TTY I/O glue that USB link is inactive
* @gser: the function, on which gserial_connect() was called
* Context: any (usually from irq)
*
* This is called to deactivate endpoints and let the TTY layer know
* that the connection went inactive ... not unlike "hangup".
*
* On return, the state is as if gserial_connect() had never been called;
* there is no active USB I/O on these endpoints.
*/
void gserial_disconnect(struct gserial *gser)
{
struct gs_port *port = gser->ioport;
unsigned long flags; if (!port)
return; /* tell the TTY glue not to do I/O here any more */
spin_lock_irqsave(&port->port_lock, flags); /* REVISIT as above: how best to track this? */
port->port_line_coding = gser->port_line_coding; port->port_usb = NULL;
gser->ioport = NULL;
if (port->port.count > 0 || port->openclose) {
wake_up_interruptible(&port->drain_wait);
if (port->port.tty)
tty_hangup(port->port.tty);
}
spin_unlock_irqrestore(&port->port_lock, flags); /* disable endpoints, aborting down any active I/O */
usb_ep_disable(gser->out);
usb_ep_disable(gser->in); /* finally, free any unused/unusable I/O buffers */
spin_lock_irqsave(&port->port_lock, flags);
if (port->port.count == 0 && !port->openclose)
kfifo_free(&port->port_write_buf);
gs_free_requests(gser->out, &port->read_pool, NULL);
gs_free_requests(gser->out, &port->read_queue, NULL);
gs_free_requests(gser->in, &port->write_pool, NULL); port->read_allocated = port->read_started =
port->write_allocated = port->write_started = 0; gs_console_disconnect(gser->in);
spin_unlock_irqrestore(&port->port_lock, flags);
}
EXPORT_SYMBOL_GPL(gserial_disconnect);

gs_start_io

/**
* gs_start_io - start USB I/O streams
* @dev: encapsulates endpoints to use
* Context: holding port_lock; port_tty and port_usb are non-null
*
* We only start I/O when something is connected to both sides of
* this port. If nothing is listening on the host side, we may
* be pointlessly filling up our TX buffers and FIFO.
*/
static int gs_start_io(struct gs_port *port)
{
struct list_head *head = &port->read_pool;
struct usb_ep *ep = port->port_usb->out;
int status;
unsigned started; /* Allocate RX and TX I/O buffers. We can't easily do this much
* earlier (with GFP_KERNEL) because the requests are coupled to
* endpoints, as are the packet sizes we'll be using. Different
* configurations may use different endpoints with a given port;
* and high speed vs full speed changes packet sizes too.
*/
//申请out endpoint的req,最终会回调到udc驱动中usb_ep_ops的alloc_request
//详见:drivers/usb/gadget/udc/s3c2410_udc.c
status = gs_alloc_requests(ep, head, gs_read_complete,
&port->read_allocated);
if (status)
return status; //申请in endpoint的req,最终会回调到udc驱动中usb_ep_ops的alloc_request
//详见:drivers/usb/gadget/udc/s3c2410_udc.c
status = gs_alloc_requests(port->port_usb->in, &port->write_pool,
gs_write_complete, &port->write_allocated);
if (status) {
gs_free_requests(ep, head, &port->read_allocated);
return status;
} /* queue read requests */
port->n_read = 0;
started = gs_start_rx(port); if (started) {
gs_start_tx(port);
/* Unblock any pending writes into our circular buffer, in case
* we didn't in gs_start_tx() */
tty_wakeup(port->port.tty);
} else {
gs_free_requests(ep, head, &port->read_allocated);
gs_free_requests(port->port_usb->in, &port->write_pool,
&port->write_allocated);
status = -EIO;
} return status;
}
static void gs_free_requests(struct usb_ep *ep, struct list_head *head,
int *allocated)
{
struct usb_request *req; while (!list_empty(head)) {
req = list_entry(head->next, struct usb_request, list);
list_del(&req->list);
gs_free_req(ep, req);
if (allocated)
(*allocated)--;
}
} static int gs_alloc_requests(struct usb_ep *ep, struct list_head *head,
void (*fn)(struct usb_ep *, struct usb_request *),
int *allocated)
{
int i;
struct usb_request *req;
//由于*allocated初始化为0,则默认申请req的数量为QUEUE_SIZE=16
int n = allocated ? QUEUE_SIZE - *allocated : QUEUE_SIZE; /* Pre-allocate up to QUEUE_SIZE transfers, but if we can't
* do quite that many this time, don't fail ... we just won't
* be as speedy as we might otherwise be.
*/
for (i = 0; i < n; i++) {
req = gs_alloc_req(ep, ep->maxpacket, GFP_ATOMIC);
if (!req)
return list_empty(head) ? -ENOMEM : 0;
req->complete = fn;
list_add_tail(&req->list, head);
if (allocated)
(*allocated)++;
}
return 0;
}

gs_start_rx与gs_start_tx

/*
* Context: caller owns port_lock, and port_usb is set
*/
static unsigned gs_start_rx(struct gs_port *port)
/*
__releases(&port->port_lock)
__acquires(&port->port_lock)
*/
{
struct list_head *pool = &port->read_pool;
struct usb_ep *out = port->port_usb->out; //read_pool不为空
while (!list_empty(pool)) {
struct usb_request *req;
int status;
struct tty_struct *tty; /* no more rx if closed */
tty = port->port.tty;
if (!tty)
break; if (port->read_started >= QUEUE_SIZE)
break; //从list链表中取出空闲的req
req = list_entry(pool->next, struct usb_request, list);
list_del(&req->list);
req->length = out->maxpacket; /* drop lock while we call out; the controller driver
* may need to call us back (e.g. for disconnect)
*/
spin_unlock(&port->port_lock);
//将取出的req插入到out endpoint 的queue中
status = usb_ep_queue(out, req, GFP_ATOMIC);
spin_lock(&port->port_lock); if (status) {
pr_debug("%s: %s %s err %d\n",
__func__, "queue", out->name, status);
list_add(&req->list, pool);
break;
}
port->read_started++; /* abort immediately after disconnect */
if (!port->port_usb)
break;
}
return port->read_started;
}
/*
* gs_start_tx
*
* This function finds available write requests, calls
* gs_send_packet to fill these packets with data, and
* continues until either there are no more write requests
* available or no more data to send. This function is
* run whenever data arrives or write requests are available.
*
* Context: caller owns port_lock; port_usb is non-null.
*/
static int gs_start_tx(struct gs_port *port)
/*
__releases(&port->port_lock)
__acquires(&port->port_lock)
*/
{
struct list_head *pool = &port->write_pool;
struct usb_ep *in;
int status = 0;
bool do_tty_wake = false; if (!port->port_usb)
return status; in = port->port_usb->in; while (!port->write_busy && !list_empty(pool)) {
struct usb_request *req;
int len; if (port->write_started >= QUEUE_SIZE)
break; //从list中取出空闲的req
req = list_entry(pool->next, struct usb_request, list);
//将需发送的数据填充到req->buf中
len = gs_send_packet(port, req->buf, in->maxpacket);
if (len == 0) {
wake_up_interruptible(&port->drain_wait);
break;
}
do_tty_wake = true; req->length = len;
list_del(&req->list);
req->zero = kfifo_is_empty(&port->port_write_buf); pr_vdebug("ttyGS%d: tx len=%d, 0x%02x 0x%02x 0x%02x ...\n",
port->port_num, len, *((u8 *)req->buf),
*((u8 *)req->buf+1), *((u8 *)req->buf+2)); /* Drop lock while we call out of driver; completions
* could be issued while we do so. Disconnection may
* happen too; maybe immediately before we queue this!
*
* NOTE that we may keep sending data for a while after
* the TTY closed (dev->ioport->port_tty is NULL).
*/
port->write_busy = true;
spin_unlock(&port->port_lock);
//将取出的req插入到in endpoint的queue尾部
status = usb_ep_queue(in, req, GFP_ATOMIC);
spin_lock(&port->port_lock);
port->write_busy = false; if (status) {
pr_debug("%s: %s %s err %d\n",
__func__, "queue", in->name, status);
list_add(&req->list, pool);
break;
} port->write_started++; /* abort immediately after disconnect */
if (!port->port_usb)
break;
} if (do_tty_wake && port->port.tty)
tty_wakeup(port->port.tty);
return status;
}

rx/tx的complete

在udc驱动中endpoint对应的req完成后,将会回调其complete函数,完成后续的动作。

static void gs_read_complete(struct usb_ep *ep, struct usb_request *req)
{
struct gs_port *port = ep->driver_data; /* Queue all received data until the tty layer is ready for it. */
spin_lock(&port->port_lock);
//将当前接收到数据req,插入到read_queue的尾部
list_add_tail(&req->list, &port->read_queue); //唤醒gs_rx_push处理数据
tasklet_schedule(&port->push);
spin_unlock(&port->port_lock);
}
static void gs_write_complete(struct usb_ep *ep, struct usb_request *req)
{
struct gs_port *port = ep->driver_data; spin_lock(&port->port_lock);
list_add(&req->list, &port->write_pool);
port->write_started--; switch (req->status) {
default:
/* presumably a transient fault */
pr_warn("%s: unexpected %s status %d\n",
__func__, ep->name, req->status);
/* FALL THROUGH */
case 0:
/* normal completion */
gs_start_tx(port);
break; case -ESHUTDOWN:
/* disconnect */
pr_vdebug("%s: %s shutdown\n", __func__, ep->name);
break;
} spin_unlock(&port->port_lock);
}

USB gadget驱动框架(五)的更多相关文章

  1. USB摄像头驱动框架分析(五)

    一.USB摄像头驱动框架如下所示:1.构造一个usb_driver2.设置   probe:        2.1. 分配video_device:video_device_alloc        ...

  2. USB gadget 驱动 printer.c 分析

    1. modprobe g_printer idVendor=0x0525 idProduct=0xa4a8 modprobe后面也可以加模块参数 2. prn_example从stdout获取数据然 ...

  3. USB摄像头驱动框架分析

    usb摄像头驱动程序,里面涉及硬件的操作.比如说,想设置亮度的时候,需要把亮度的参数发给硬件.去得到真正视频数据的时候,需要访问硬件得到数据.usb摄像头驱动程序框架与虚拟摄像头驱动程序的框架是一样的 ...

  4. I2C驱动框架(五)

    参考:I2C子系统之 adapter driver注册——I2C_dev_init() i2c的操作在内核中是当做字符设备来操作的,相关初始化在由i2c_dev_init函数来初始化. static ...

  5. Linux USB ECM Gadget 驱动介绍

    ​1 USB ECM介绍 USB ECM,属于USB-IF定义的CDC(Communication Device Class)下的一个子类:Ethernet Networking Control Mo ...

  6. Linux下USB驱动框架分析【转】

    转自:http://blog.csdn.net/brucexu1978/article/details/17583407 版权声明:本文为博主原创文章,未经博主允许不得转载. http://www.c ...

  7. Linux usb子系统(一) _写一个usb鼠标驱动

    USB总线是一种典型的热插拔的总线标准,由于其优异的性能几乎成为了当下大小设备中的标配. USB的驱动可以分为3类:SoC的USB控制器的驱动,主机端USB设备的驱动,设备上的USB Gadget驱动 ...

  8. linux usb总线驱动(一)

    目录 linux usb总线驱动框架 USB 介绍 传输类型 控制器接口 2440接口 基本流程 alloc_dev choose_address hub_port_init usb_get_devi ...

  9. Linux 串口、usb转串口驱动分析(2-2) 【转】

    转自:http://blog.chinaunix.net/xmlrpc.php?r=blog/article&uid=26807463&id=4186852 Linux 串口.usb转 ...

  10. Linux 串口、usb转串口驱动分析(2-1) 【转】

    转自:http://blog.chinaunix.net/xmlrpc.php?r=blog/article&uid=26807463&id=4186851 Linux 串口.usb转 ...

随机推荐

  1. httpclient,轻量级idea集成测试工具

    优点:不用新开一个网页,具有测试数据保存功能,不需要配置即用(对比swagger)     不会特别占内存(对比postman) 使用方法:idea中安装插件 controller方法中点击 选择对应 ...

  2. Java还是C#?我该如何选择?给年轻人的建议...

    一.年轻人应该通吃 其实这不应该是我们真正的主题,而且入了行的也很少会java还是c#这么比,但初学的,java和c#往往就代表了两大流派,java代替了j2ee,c#代替了.net,ok,没有关系, ...

  3. 【Java】Input,Output,Stream I/O流 05 RandomAccessFile 随机访问文件类

    RandomAccessFile 随机访问文件类 直接继承java.lang.Object 实现DataInput & DataOutput 接口 即是输入流,也是输出流 public cla ...

  4. 【DataBase】MySQL 08 SQL函数 单行函数其二 数值函数

    数值函数 # 数值函数 -- ROUND() 四舍五入 SELECT ROUND(3.14),ROUND(-3.14); -- 重载,保留指定参数的小数位数 SELECT ROUND(-3.14,3) ...

  5. 《Python数据可视化之matplotlib实践》 源码 第四篇 扩展 第十二章

    图  12.1 import matplotlib.pyplot as plt import numpy as np barSlices=12 theta=np.linspace(0.0, 2*np. ...

  6. Ubuntu18.04系统下网络文件系统nfs的安装及简单配置

    硬件环境: 两台Ubuntu18.04服务器使用局域网连接,IP分别为192.168.11.66 和 192.168.11.206. ================================= ...

  7. C# 反射以及实际场景使用

    1 什么是反射 首先要复习一下C#的编译过程,可以解释为下图 其中dll/exe中,包括元数据(metadata)和IL(中间语言Intermediate Language) 另外还出现的其他名词:C ...

  8. java中sleep与 yield 区别

    1.背景 在多线程的使用中你会看到这个两个方法sleep()与yield()这两方法有什么作用呢? 请看下面案例演示 2.测试 package com.ldp.demo01; import com.c ...

  9. OpenTiny HUICharts开源发布,带你了解一个简单、易上手的图表组件库

    摘要:目前 OpenTiny HUICharts 已经成功落地在华为内部100多个产品中,持续提升了用户的可视化体验. 本文分享自华为云社区<OpenTiny HUICharts 正式开源发布, ...

  10. Java学习笔记1--JDK,JRE和JVM

    1.Java开发环境 Java开发环境是指Java程序员开发.编写.测试和调试Java程序所使用的所有工具和技术.Java开发环境通常由以下几个部分组成: JDK(Java Development K ...