1. init clue:
/* 1]create data structure: adb_dev
* 2]调用misc_register(&adb_device);
**/
struct adb_data {
bool opened;
bool enabled;
};
struct adb_dev {
struct usb_function function;
struct usb_composite_dev *cdev;
spinlock_t lock;
struct usb_ep *ep_in;
struct usb_ep *ep_out;
int online;
int error;
atomic_t read_excl;
atomic_t write_excl;
atomic_t open_excl;
struct list_head tx_idle;
wait_queue_head_t read_wq;
wait_queue_head_t write_wq;
struct usb_request *rx_req;
int rx_done;
};
static int
adb_function_init(struct android_usb_function *f,
struct usb_composite_dev *cdev)
{
f->config = kzalloc(sizeof(struct adb_data), GFP_KERNEL);
if (!f->config)
return -ENOMEM;
return adb_setup();
}
static int adb_setup(void)
{
struct adb_dev *dev;
int ret;
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
spin_lock_init(&dev->lock);
init_waitqueue_head(&dev->read_wq);
init_waitqueue_head(&dev->write_wq);
atomic_set(&dev->open_excl, 0);
atomic_set(&dev->read_excl, 0);
atomic_set(&dev->write_excl, 0);
INIT_LIST_HEAD(&dev->tx_idle);
_adb_dev = dev;
ret = misc_register(&adb_device);
return 0;
}
/* file operations for ADB device /dev/android_adb */
static const struct file_operations adb_fops = {
.owner = THIS_MODULE,
.read = adb_read,
.write = adb_write,
.open = adb_open,
.release = adb_release,
};
static struct miscdevice adb_device = {
.minor = MISC_DYNAMIC_MINOR,
.name = adb_shortname,
.fops = &adb_fops,
};
/*************************************************/
2. enable clue:
adb_function_bind_config -> adb_bind_config(c);static int adb_bind_config(struct usb_configuration *c)
{
struct adb_dev *dev = _adb_dev;
printk(KERN_INFO "adb_bind_config\n");
dev->cdev = c->cdev;
dev->function.name = "adb";
dev->function.descriptors = fs_adb_descs;
dev->function.hs_descriptors = hs_adb_descs;
dev->function.bind = adb_function_bind;
dev->function.unbind = adb_function_unbind;
dev->function.set_alt = adb_function_set_alt;
dev->function.disable = adb_function_disable;
return usb_add_function(c, &dev->function);
}
adb_function_bind(struct usb_configuration *c, struct usb_function *f)
{
struct usb_composite_dev *cdev = c->cdev;
struct adb_dev *dev = func_to_adb(f);
int id;
int ret;
dev->cdev = cdev;
adb_interface_desc.bInterfaceNumber = id;
/* allocate endpoints */
ret = adb_create_bulk_endpoints(dev, &adb_fullspeed_in_desc,
&adb_fullspeed_out_desc);
}
static int adb_create_bulk_endpoints(struct adb_dev *dev,
struct usb_endpoint_descriptor *in_desc,
struct usb_endpoint_descriptor *out_desc)
{
struct usb_composite_dev *cdev = dev->cdev;
struct usb_request *req;
struct usb_ep *ep;
ep = usb_ep_autoconfig(cdev->gadget, in_desc);
ep->driver_data = dev; /* claim the endpoint */
dev->ep_in = ep
ep = usb_ep_autoconfig(cdev->gadget, out_desc);
ep->driver_data = dev; /* claim the endpoint */
dev->ep_out = ep;
req = adb_request_new(dev->ep_out, ADB_BULK_BUFFER_SIZE);
req->complete = adb_complete_out;
dev->rx_req = req;
for (i = 0; i < TX_REQ_MAX; i++) {
req = adb_request_new(dev->ep_in, ADB_BULK_BUFFER_SIZE);
if (!req)
goto fail;
req->complete = adb_complete_in;
adb_req_put(dev, &dev->tx_idle, req);
}
return 0;
}
/* add a request to the tail of a list */
void adb_req_put(struct adb_dev *dev, struct list_head *head,
struct usb_request *req)
{
unsigned long flags;
spin_lock_irqsave(&dev->lock, flags);
list_add_tail(&req->list, head);
spin_unlock_irqrestore(&dev->lock, flags);
}
/*************************************************/
3. set interface
static int adb_function_set_alt(struct usb_function *f,
unsigned intf, unsigned alt)
{
struct adb_dev *dev = func_to_adb(f);
struct usb_composite_dev *cdev = f->config->cdev;
int ret;
DBG(cdev, "adb_function_set_alt intf: %d alt: %d\n", intf, alt);
ret = config_ep_by_speed(cdev->gadget, f, dev->ep_in);
ret = usb_ep_enable(dev->ep_in);
ret = config_ep_by_speed(cdev->gadget, f, dev->ep_out);
ret = usb_ep_enable(dev->ep_out);
dev->online = 1;
/* readers may be blocked waiting for us to go online */
wake_up(&dev->read_wq);
return 0;
}
/*************************************************/
4. adb_open/read/write
static int adb_open(struct inode *ip, struct file *fp){
fp->private_data = _adb_dev;
return 0;
}
static ssize_t adb_write(struct file *fp, const char __user *buf,
size_t count, loff_t *pos)
{
struct adb_dev *dev = fp->private_data;
struct usb_request *req = 0;
while (count > 0) {
/* get an idle tx request to use */
req = 0;
/*什么时候睡?当后面的参数condition 不成立的时候?
*什么时候醒?当后面的参数condition 成立的时候?
**/
ret = wait_event_interruptible(dev->write_wq,
(req = adb_req_get(dev, &dev->tx_idle)) || dev->error);
if (req != 0) {
if (count > ADB_BULK_BUFFER_SIZE)
xfer = ADB_BULK_BUFFER_SIZE;
else
xfer = count;
if (copy_from_user(req->buf, buf, xfer)) {
r = -EFAULT;
break;
}
req->length = xfer;
ret = usb_ep_queue(dev->ep_in, req, GFP_ATOMIC);
buf += xfer;
count -= xfer;
/* zero this so we don't try to free it on error exit */
req = 0;
}
}
return;
}
刚开始的时候条件:req = adb_req_get(dev, &dev->tx_idle)) || dev->error成立,thread肯定不睡。当
dev->tx_idle没有时,系统睡眠。当endpoint发送数据完毕后,调用函数:
static void adb_complete_in(struct usb_ep *ep, struct usb_request *req)
{
struct adb_dev *dev = _adb_dev;
adb_req_put(dev, &dev->tx_idle, req);
wake_up(&dev->write_wq);
/*注意和wake_up_thread的区别,两者有什么的不同的用途?*/
}
static ssize_t adb_read(struct file *fp, char __user *buf,
size_t count, loff_t *pos)
{
struct adb_dev *dev = fp->private_data;
struct usb_request *req;
/* we will block until we're online */
while (!(dev->online || dev->error)) {
pr_debug("adb_read: waiting for online state\n");
ret = wait_event_interruptible(dev->read_wq,
(dev->online || dev->error));
if (ret < 0) {
adb_unlock(&dev->read_excl);
return ret;
}
}
/* queue a request */
req = dev->rx_req;
req->length = count;
dev->rx_done = 0;
ret = usb_ep_queue(dev->ep_out, req, GFP_ATOMIC);
/* wait for a request to complete */
ret = wait_event_interruptible(dev->read_wq, dev->rx_done);
if (!dev->error) {
/* If we got a 0-len packet, throw it back and try again. */
xfer = (req->actual < count) ? req->actual : count;
if (copy_to_user(buf, req->buf, xfer))
r = -EFAULT;
}
}
static void adb_complete_out(struct usb_ep *ep, struct usb_request *req)
{
struct adb_dev *dev = _adb_dev;
dev->rx_done = 1;
if (req->status != 0 && req->status != -ECONNRESET)
dev->error = 1;
wake_up(&dev->read_wq);
}
ADB driver 看上去比较简单:使用一对 bulk IN/OUT端点,向外导出file operation open/write/read函数,当需要写数据时使用 in endpoint, 从用户空间得到数据
通过in end point 输出; 当读数据时提交一个 usb request, 然后等待接收到数据,最后把接收到的数据copy 到用户空间。