本文主要是介绍USB gadget driver: ACM,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
有关 acm的代码在文件: android.c/ f_acm.c and u_serial.c中。
有关的数据结构主要包括:tty_struct/ gs_port and gserial.
三者之间的关系及建立:
/*
*gserial is the lifecycle interface, used by USB functions
*gs_port is the I/O nexus, used by the tty driver
*tty_struct links to the tty/filesystem framework
*
*gserial <---> gs_port ... links will be null when the USB linkis
*inactive; managed by gserial_{connect,disconnect}(). each gserial
*instance can wrap its own USB control protocol.
* gserial->ioport== usb_ep->driver_data ... gs_port
* gs_port->port_usb... gserial
*
*gs_port <---> tty_struct ... links will be null when the TTYfile
*isn't opened; managed by gs_open()/gs_close()
* gserial->port_tty... tty_struct
* tty_struct->driver_data... gserial
*/
何时创建的gserial?
当创建f_acm是创建的gserial
structf_acm {
structgserial port;
---
}
intacm_bind_config(struct usb_configuration *c, u8 port_num)
{
structf_acm *acm;
int status;
/*allocate and initialize one new instance */
acm= kzalloc(sizeof *acm, GFP_KERNEL);
acm->port.connect= acm_connect;
acm->port.disconnect= acm_disconnect;
acm->port.send_break= acm_send_break;
acm->port.func.name= "acm";
acm->port.func.strings= acm_strings;
/*descriptors are per-instance copies */
acm->port.func.bind= acm_bind;
acm->port.func.unbind= acm_unbind;
acm->port.func.set_alt= acm_set_alt;
acm->port.func.setup= acm_setup;
acm->port.func.disable= acm_disable;
status= usb_add_function(c, &acm->port.func);
}
gs_port又是何时创建的?
gserial_setup->gs_port_alloc
tty_struct/gs_port/ gserial是何时关联上的?
intgserial_connect(struct gserial *gser, u8 port_num)
{
structgs_port *port;
port= ports[port_num].port;
/*activate the endpoints */
status= usb_ep_enable(gser->in);
gser->in->driver_data= port;
status= usb_ep_enable(gser->out);
gser->out->driver_data= port;
/*then tell the tty glue that I/O can work */
gser->ioport= port;
port->port_usb= gser;
}
staticint gs_open(struct tty_struct *tty, struct file *file)
{
int port_num= tty->index;
structgs_port *port;
port= ports[port_num].port;
tty->driver_data= port;
port->port_tty= tty;
}
以 init为线索:
acm_function_init -> gserial_setup(cdev->gadget, MAX_ACM_INSTANCES);
{
struct usb_cdc_line_coding coding;
for (i = 0; i < count; i++)
gs_port_alloc(i, &coding);
gs_tty_driver = alloc_tty_driver(count);
tty_set_operations(gs_tty_driver, &gs_tty_ops);
tty_register_driver(gs_tty_driver);
tty_register_device(gs_tty_driver, i, &g->dev);
}
/*分为两部分:
*1] register tty_driver: gs_tty_driver;这是就可看到 ttyGS0-3
*2] 创建 gs_port,关联usb_cdc_line_coding,并赋值给全局变量ports[N_PORTS]
**/
tty_operations gs_tty_ops 使用gs_port这个结构体
spinlock_t port_lock; /* guard port_* access */
struct gserial *port_usb;
struct tty_struct *port_tty;
unsigned open_count;
bool openclose; /* open/close in progress */
u8 port_num;
wait_queue_head_t close_wait; /* wait for last close */
struct list_head read_pool;
int read_started;
int read_allocated;
struct list_head read_queue;
unsigned n_read;
struct tasklet_struct push;
struct list_head write_pool;
int write_started;
int write_allocated;
struct gs_buf port_write_buf;
wait_queue_head_t drain_wait; /* wait while writes drain */
/* REVISIT this state ... */
struct usb_cdc_line_coding port_line_coding; /* 8-N-1 etc */
};
以enable function为线索:
acm_function_bind_config -> acm_bind_config
int acm_bind_config(struct usb_configuration *c, u8 port_num)
{
struct f_acm *acm;
/* allocate and initialize one new instance */
acm = kzalloc(sizeof *acm, GFP_KERNEL);
acm->port_num = port_num;
acm->port.connect = acm_connect;
acm->port.disconnect = acm_disconnect;
acm->port.send_break = acm_send_break;
acm->port.func.name = "acm";
acm->port.func.strings = acm_strings;
/* descriptors are per-instance copies */
acm->port.func.bind = acm_bind;
acm->port.func.unbind = acm_unbind;
acm->port.func.set_alt = acm_set_alt;
acm->port.func.setup = acm_setup;
acm->port.func.disable = acm_disable;
status = usb_add_function(c, &acm->port.func);
return status;
}
/*
*这里创建了f_acm:主要成员有gserial/通知端点
*gserial 和 gs_port有什么不同?
**/
struct f_acm {
struct gserial port;
u8 ctrl_id, data_id;
u8 port_num;
u8 pending;
spinlock_t lock;
struct usb_ep *notify;
struct usb_request *notify_req;
struct usb_cdc_line_coding port_line_coding; /* 8-N-1 etc */
/* SetControlLineState request -- CDC 1.1 section 6.2.14 (INPUT) */
u16 port_handshake_bits;
/* SerialState notification -- CDC 1.1 section 6.3.5 (OUTPUT) */
u16 serial_state;
};
/*gserial主要包含:in/out endpoit; notification callbacks; 和gs_port关联;
*composite需要的usb_function
**/
struct gserial {
struct usb_function func;
/* port is managed by gserial_{connect,disconnect} */
struct gs_port *ioport;
struct usb_ep *in;
struct usb_ep *out;
/* REVISIT avoid this CDC-ACM support harder ... */
struct usb_cdc_line_coding port_line_coding; /* 9600-8-N-1 etc */
/* notification callbacks */
void (*connect)(struct gserial *p);
void (*disconnect)(struct gserial *p);
int (*send_break)(struct gserial *p, int duration);
};
usb_add_function -> function->bind();
acm_bind(struct usb_configuration *c, struct usb_function *f)
{
struct usb_composite_dev *cdev = c->cdev;
struct f_acm *acm = func_to_acm(f);
struct usb_ep *ep;
/* allocate instance-specific endpoints */
ep = usb_ep_autoconfig(cdev->gadget, &acm_fs_in_desc);
acm->port.in = ep;
ep->driver_data = cdev; /* claim */
ep = usb_ep_autoconfig(cdev->gadget, &acm_fs_out_desc);
acm->port.out = ep;
ep->driver_data = cdev; /* claim */
ep = usb_ep_autoconfig(cdev->gadget, &acm_fs_notify_desc);
acm->notify = ep;
ep->driver_data = cdev; /* claim */
/* allocate notification */
acm->notify_req = gs_alloc_req(ep,
sizeof(struct usb_cdc_notification) + 2,
GFP_KERNEL);
acm->notify_req->complete = acm_cdc_notify_complete;
acm->notify_req->context = acm;
}
以gserail_connect为线索
/* After initialization (gserial_setup), these TTY port devices stay
* available until they are removed (gserial_cleanup). Each one may be
* connected to a USB function (gserial_connect), or disconnected (with
* gserial_disconnect) when the USB host issues a config change event.
* Data can only flow when the port is connected to the host.
**/
composite_setup{
case USB_REQ_SET_INTERFACE:
value = f->set_alt(f, w_index, w_value);
break;
}
acm->port.func.set_alt = acm_set_alt;
acm_set_alt-> gserial_connect(&acm->port, acm->port_num);
tty 使用的数据结构是 gs_port
USB 相关使用的结构体为 f_acm and gserial
在枚举过程中set interface时,这些数据结构关联,tty 层和USB 关联。
int gserial_connect(struct gserial *gser, u8 port_num)
{
struct gs_port *port;
unsigned long flags;
int status;
/* we "know" gserial_cleanup() hasn't been called */
port = ports[port_num].port;
/* activate the endpoints */
status = usb_ep_enable(gser->in);
gser->in->driver_data = port;
status = usb_ep_enable(gser->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;
gser->port_line_coding = port->port_line_coding;
/* if it's already open, start I/O ... and notify the serial
* protocol about open/close status (connect/disconnect).
*/
/*哪里改变这个数?gs_open*/
if (port->open_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);
}
spin_unlock_irqrestore(&port->port_lock, flags);
return status;
}
/* connect == the TTY link is open */
static void acm_connect(struct gserial *port)
{
struct f_acm *acm = port_to_acm(port);
acm->serial_state |= ACM_CTRL_DSR | ACM_CTRL_DCD;
acm_notify_serial_state(acm);
}
acm_notify_serial_state -> acm_cdc_notify -> usb_ep_queue;
以gs_open为线索
static int gs_open(struct tty_struct *tty, struct file *file)
{
int port_num = tty->index;
struct gs_port *port;
int status;
/* allocate circular buffer on first open */
if (port->port_write_buf.buf_buf == NULL) {
spin_unlock_irq(&port->port_lock);
status = gs_buf_alloc(&port->port_write_buf, WRITE_BUF_SIZE);
spin_lock_irq(&port->port_lock);
}
tty->driver_data = port;
port->port_tty = tty;
port->open_count = 1;
port->openclose = false;
/* if connected, start the I/O stream */
if (port->port_usb) {
struct gserial *gser = port->port_usb;
gs_start_io(port);
if (gser->connect)
gser->connect(gser);
}
}
static int gs_start_io(struct gs_port *port)
{
struct list_head *head = &port->read_pool;
struct usb_ep *ep = port->port_usb->out;
/*read pool and out endpoit*/
gs_alloc_requests(ep, head, gs_read_complete,&port->read_allocated);
gs_alloc_requests(port->port_usb->in, &port->write_pool,
gs_write_complete, &port->write_allocated);
/* queue read requests */
port->n_read = 0;
started = gs_start_rx(port);
}
有关gs_write/read
/******************************************************************************/
static int gs_write(struct tty_struct *tty, const unsigned char *buf, int count)
{
struct gs_port *port = tty->driver_data;
unsigned long flags;
int status;
spin_lock_irqsave(&port->port_lock, flags);
if (count)
count = gs_buf_put(&port->port_write_buf, buf, count);
if (port->port_usb)
status = gs_start_tx(port);
spin_unlock_irqrestore(&port->port_lock, flags);
return count;
}
/* circular buffer
* 分配了一个大小为8K的write_buffer
*/
struct gs_buf {
unsigned buf_size;
char *buf_buf;
char *buf_get;
char *buf_put;
};
#define WRITE_BUF_SIZE 8192
gs_open -> gs_buf_alloc(&port->port_write_buf, WRITE_BUF_SIZE)
gs_write -> gs_buf_put(&port->port_write_buf, buf, count);
/*调用函数gs_buf_put(&port->port_write_buf, buf, count);
*把来自用户空间的数据copy到port_write_buf,并更新buf_put指针
**/
gs_start_io ->
gs_alloc_requests(port->port_usb->in, &port->write_pool,
gs_write_complete, &port->write_allocated);
/*
*分配usb_request, 并把usb_request add到port->write_pool中.
* 形成了一个write_pool指向的usb_requset双向链表,元素的个数为
* #define QUEUE_SIZE 16
**/
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;
int n = allocated ? QUEUE_SIZE - *allocated : QUEUE_SIZE;
for (i = 0; i < n; i++) {
req = gs_alloc_req(ep, ep->maxpacket, GFP_ATOMIC);
req->complete = fn;
list_add_tail(&req->list, head);
if (allocated)
(*allocated)++;
}
return 0;
}
/*调用端点具体的方法得到usb_requset,把并分配memory for usb_request*/
struct usb_request *
gs_alloc_req(struct usb_ep *ep, unsigned len, gfp_t kmalloc_flags)
{
struct usb_request *req;
req = usb_ep_alloc_request(ep, kmalloc_flags);
if (req != NULL) {
req->length = len;
req->buf = kmalloc(len, kmalloc_flags);
if (req->buf == NULL) {
usb_ep_free_request(ep, req);
return NULL;
}
}
return req;
}
static inline struct usb_request *usb_ep_alloc_request(struct usb_ep *ep,
gfp_t gfp_flags)
{
return ep->ops->alloc_request(ep, gfp_flags);
}
/*
* 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)
{
struct list_head *pool = &port->write_pool;
struct usb_ep *in = port->port_usb->in;
while (!list_empty(pool)) {
struct usb_request *req;
int len;
/*判断是否还有没有使用的 usb_request*/
if (port->write_started >= QUEUE_SIZE)
break;
/*从链表中得到一个usb_request*/
req = list_entry(pool->next, struct usb_request, list);
/*把数据从circular buffer中copy到usb_request的buffer中 */
len = gs_send_packet(port, req->buf, in->maxpacket);
/*如果从circular buffer中copy到usb_request的buffer中数据大小是0,
*则退出发送过程
**/
if (len == 0) {/*this variable used to check write finished ?*/
wake_up_interruptible(&port->drain_wait);
break;
}
do_tty_wake = true;
/*根据实际的大小,赋值usb_request.length*/
req->length = len;
/*把usb_request从list中删除*/
list_del(&req->list);
/*根据剩余的数据设置zero标志位*/
req->zero = (gs_buf_data_avail(&port->port_write_buf) == 0);
/*为什么使用port_lock?
*把usb_request提交到usb_endpoit queue上
**/
/* 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).
*/
spin_unlock(&port->port_lock);
status = usb_ep_queue(in, req, GFP_ATOMIC);
spin_lock(&port->port_lock);
/*更新使用的usb_request个数*/
port->write_started++;
/* abort immediately after disconnect */
if (!port->port_usb)
break;
}
return status;
}
/*
*write usb_reqeust处理完成后,调用注册的gs_write_complete
**/
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);
/*把使用完的usb_request放入pool中,减少使用的个数*/
list_add(&req->list, &port->write_pool);
port->write_started--;
/*根据使用后的 usb_request的状态,分别处理*/
switch (req->status) {
default:
/* presumably a transient fault */
pr_warning("%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);
}
最后总是调到定义在 gadget.h文件中的gadget_API:usb_ep_queue(in, req, GFP_ATOMIC);
当通过Out endpoint收到数据后,调用注册的回调函数gs_read_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);
list_add_tail(&req->list, &port->read_queue);
tasklet_schedule(&port->push);
spin_unlock(&port->port_lock);
}
tasklet_init(&port->push, gs_rx_push, (unsigned long) port);
gs_rx_push(unsigned long _port) -> tty_insert_flip_string(tty, packet, size);
下面分析几种情况:
1. 在插入USB之前,向ttyGSx write data:
static int gs_write(struct tty_struct *tty, const unsigned char *buf, int count)
{
struct gs_port *port = tty->driver_data;
unsigned long flags;
int status;
pr_vdebug("gs_write: ttyGS%d (%p) writing %d bytes\n",
port->port_num, tty, count);
spin_lock_irqsave(&port->port_lock, flags);
if (count)
count = gs_buf_put(&port->port_write_buf, buf, count);
/* treat count == 0 as flush_chars() */
if (port->port_usb)
status = gs_start_tx(port);
spin_unlock_irqrestore(&port->port_lock, flags);
return count;
}
/*何时分配的port_write_buf?*/
gs_open -> gs_buf_alloc(&port->port_write_buf, 8K);
/*write buffer共8K,如果没有及时传输 buffer满的话,avail space == 0, return 0
*不会有什么严重的后果
**/
gs_buf_put(struct gs_buf *gb, const char *buf, unsigned count)
{
unsigned len;
len = gs_buf_space_avail(gb);
if (count > len)
count = len;
if (count == 0)
return 0;
----
}
2. write failed?
--
这篇关于USB gadget driver: ACM的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!