本节主要分析虚拟串口的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. 七天.NET 8操作SQLite入门到实战 - (3)第七天Blazor学生管理页面编写和接口对接

    前言 本章节的主要内容是完善Blazor学生管理页面的编写和接口对接. 七天.NET 8 操作 SQLite 入门到实战详细教程 第一天 SQLite 简介 第二天 在 Windows 上配置 SQL ...

  2. RHCA rh442 002 监控工具 脏页 块设备名 缓存

    sar 看某一个时间的数据 sar -d 1 5 与iostat类似 计算机识别设备按编号识别 0-15预留出 8 为iscsi设备 做一个块设备名 名字不重要是给人看的,重要的是编号 8 17(主编 ...

  3. linux常用命令(每日积累)

    linux查看应用程序的进程号和端口号 lsof -i :port,查看指定端口运行的程序,同时还有当前连接. netstat -nupl  (UDP类型的端口)netstat -ntpl  (TCP ...

  4. 网友开放的开源项目:网页版的A*算法可视化演示程序

    相关项目: https://xueqiaoxu.me/#projects 项目介绍: A JavaScript path-finding library for grid based games. I ...

  5. 海豚调度调优 | 正在运行的工作流(DAG)如何重新拉起失败的任务(Task)

     本系列文章是DolphinScheduler由浅入深的教程,涵盖搭建.二开迭代.核心原理解读.运维和管理等一系列内容.适用于想对 DolphinScheduler了解或想要加深理解的读者. **祝开 ...

  6. 开关资源新方法:Try- with-resources

    JDK7新特性:Try- with-resources try-with-resources 是 JDK 7中引入的一种新的异常处理机制,它主要用于自动管理资源,能够很容易地关闭在 try-catch ...

  7. 代码随想录Day15

    110.平衡二叉树 (优先掌握递归) 给定一个二叉树,判断它是否是 平衡二叉树 平衡二叉树 是指该树所有节点的左右子树的深度相差不超过 1. 示例 1: 输入:root = [3,9,20,null, ...

  8. 微服务全链路跟踪:grpc集成jaeger

    微服务全链路跟踪:grpc集成zipkin 微服务全链路跟踪:grpc集成jaeger 微服务全链路跟踪:springcloud集成jaeger 微服务全链路跟踪:jaeger集成istio,并兼容u ...

  9. PyJWT 和 python-jose 在处理JWT令牌处理的时候的差异和具体使用

    PyJWT 和 python-jose 是两个用于处理 JSON Web Tokens (JWT) 的 Python 库.它们都有助于生成.解码.验证和管理 JWT,但它们在功能范围和设计哲学上有一些 ...

  10. 在使用Nacos作为统一配置中心的时候,项目中使用@Value注解从Nacos获取值,一直报错Could not resolve placeholder 'blog.website' in value "${blog.website}".如下图:

    在使用Nacos作为统一配置中心的时候,项目中使用@Value注解从Nacos获取值,一直报错Could not resolve placeholder 'blog.website' in value ...