USB: gadget: f_accessory: Misc improvements and cleanup:
- Add URI string - Replace type string with a description string - Add a control call to retrieve accessory protocol version (currently 1) - Driver read() and write() calls now fail after USB disconnect until driver file is closed and reopened. - Misc cleanup work Signed-off-by: Mike Lockwood <lockwood@android.com>
This commit is contained in:
parent
68c3ec37af
commit
5dcd55b2e9
2 changed files with 107 additions and 85 deletions
|
|
@ -41,6 +41,8 @@
|
|||
#define BULK_BUFFER_SIZE 16384
|
||||
#define ACC_STRING_SIZE 256
|
||||
|
||||
#define PROTOCOL_VERSION 1
|
||||
|
||||
/* String IDs */
|
||||
#define INTERFACE_STRING_INDEX 0
|
||||
|
||||
|
|
@ -56,13 +58,19 @@ struct acc_dev {
|
|||
struct usb_ep *ep_in;
|
||||
struct usb_ep *ep_out;
|
||||
|
||||
/* set to 1 when we connect */
|
||||
int online:1;
|
||||
/* Set to 1 when we disconnect.
|
||||
* Not cleared until our file is closed.
|
||||
*/
|
||||
int disconnected:1;
|
||||
|
||||
/* strings sent by the host */
|
||||
char manufacturer[ACC_STRING_SIZE];
|
||||
char model[ACC_STRING_SIZE];
|
||||
char type[ACC_STRING_SIZE];
|
||||
char description[ACC_STRING_SIZE];
|
||||
char version[ACC_STRING_SIZE];
|
||||
char uri[ACC_STRING_SIZE];
|
||||
|
||||
/* for acc_complete_set_string */
|
||||
int string_index;
|
||||
|
|
@ -180,21 +188,6 @@ static void acc_request_free(struct usb_request *req, struct usb_ep *ep)
|
|||
}
|
||||
}
|
||||
|
||||
static inline int _lock(atomic_t *excl)
|
||||
{
|
||||
if (atomic_inc_return(excl) == 1) {
|
||||
return 0;
|
||||
} else {
|
||||
atomic_dec(excl);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void _unlock(atomic_t *excl)
|
||||
{
|
||||
atomic_dec(excl);
|
||||
}
|
||||
|
||||
/* add a request to the tail of a list */
|
||||
static void req_put(struct acc_dev *dev, struct list_head *head,
|
||||
struct usb_request *req)
|
||||
|
|
@ -227,8 +220,10 @@ static void acc_complete_in(struct usb_ep *ep, struct usb_request *req)
|
|||
{
|
||||
struct acc_dev *dev = _acc_dev;
|
||||
|
||||
if (req->status != 0)
|
||||
if (req->status != 0) {
|
||||
dev->online = 0;
|
||||
dev->disconnected = 1;
|
||||
}
|
||||
|
||||
req_put(dev, &dev->tx_idle, req);
|
||||
|
||||
|
|
@ -240,8 +235,10 @@ static void acc_complete_out(struct usb_ep *ep, struct usb_request *req)
|
|||
struct acc_dev *dev = _acc_dev;
|
||||
|
||||
dev->rx_done = 1;
|
||||
if (req->status != 0)
|
||||
if (req->status != 0) {
|
||||
dev->online = 0;
|
||||
dev->disconnected = 1;
|
||||
}
|
||||
|
||||
wake_up(&dev->read_wq);
|
||||
}
|
||||
|
|
@ -258,11 +255,6 @@ static void acc_complete_set_string(struct usb_ep *ep, struct usb_request *req)
|
|||
return;
|
||||
}
|
||||
|
||||
if (length > ACC_STRING_SIZE) {
|
||||
DBG(cdev, "accessory string too long (length %d)\n", length);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (dev->string_index) {
|
||||
case ACCESSORY_STRING_MANUFACTURER:
|
||||
string_dest = dev->manufacturer;
|
||||
|
|
@ -270,19 +262,26 @@ static void acc_complete_set_string(struct usb_ep *ep, struct usb_request *req)
|
|||
case ACCESSORY_STRING_MODEL:
|
||||
string_dest = dev->model;
|
||||
break;
|
||||
case ACCESSORY_STRING_TYPE:
|
||||
string_dest = dev->type;
|
||||
case ACCESSORY_STRING_DESCRIPTION:
|
||||
string_dest = dev->description;
|
||||
break;
|
||||
case ACCESSORY_STRING_VERSION:
|
||||
string_dest = dev->version;
|
||||
break;
|
||||
case ACCESSORY_STRING_URI:
|
||||
string_dest = dev->uri;
|
||||
break;
|
||||
}
|
||||
if (string_dest) {
|
||||
unsigned long flags;
|
||||
|
||||
if (length >= ACC_STRING_SIZE)
|
||||
length = ACC_STRING_SIZE - 1;
|
||||
|
||||
spin_lock_irqsave(&dev->lock, flags);
|
||||
if (string_dest)
|
||||
memcpy(string_dest, cdev->req->buf, length);
|
||||
memcpy(string_dest, cdev->req->buf, length);
|
||||
/* ensure zero termination */
|
||||
string_dest[length] = 0;
|
||||
spin_unlock_irqrestore(&dev->lock, flags);
|
||||
} else {
|
||||
DBG(cdev, "unknown accessory string index %d\n",
|
||||
|
|
@ -348,6 +347,10 @@ static int __init create_bulk_endpoints(struct acc_dev *dev,
|
|||
|
||||
fail:
|
||||
printk(KERN_ERR "acc_bind() could not allocate requests\n");
|
||||
while ((req = req_get(dev, &dev->tx_idle)))
|
||||
acc_request_free(req, dev->ep_in);
|
||||
for (i = 0; i < RX_REQ_MAX; i++)
|
||||
acc_request_free(dev->rx_req[i], dev->ep_out);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
|
@ -362,6 +365,9 @@ static ssize_t acc_read(struct file *fp, char __user *buf,
|
|||
|
||||
DBG(cdev, "acc_read(%d)\n", count);
|
||||
|
||||
if (dev->disconnected)
|
||||
return -ENODEV;
|
||||
|
||||
if (count > BULK_BUFFER_SIZE)
|
||||
count = BULK_BUFFER_SIZE;
|
||||
|
||||
|
|
@ -422,12 +428,8 @@ static ssize_t acc_write(struct file *fp, const char __user *buf,
|
|||
|
||||
DBG(cdev, "acc_write(%d)\n", count);
|
||||
|
||||
spin_lock_irq(&dev->lock);
|
||||
if (!dev->online) {
|
||||
spin_unlock_irq(&dev->lock);
|
||||
if (!dev->online || dev->disconnected)
|
||||
return -ENODEV;
|
||||
}
|
||||
spin_unlock_irq(&dev->lock);
|
||||
|
||||
while (count > 0) {
|
||||
if (!dev->online) {
|
||||
|
|
@ -479,52 +481,42 @@ static ssize_t acc_write(struct file *fp, const char __user *buf,
|
|||
static long acc_ioctl(struct file *fp, unsigned code, unsigned long value)
|
||||
{
|
||||
struct acc_dev *dev = fp->private_data;
|
||||
int ret = -EINVAL;
|
||||
char *src = NULL;
|
||||
int ret;
|
||||
|
||||
if (dev->function.disabled)
|
||||
return -ENODEV;
|
||||
|
||||
switch (code) {
|
||||
case ACCESSORY_GET_STRING_MANUFACTURER:
|
||||
spin_lock_irq(&dev->lock);
|
||||
ret = strlen(dev->manufacturer) + 1;
|
||||
if (copy_to_user((void __user *)value, dev->manufacturer, ret))
|
||||
ret = -EFAULT;
|
||||
spin_unlock_irq(&dev->lock);
|
||||
break;
|
||||
|
||||
src = dev->manufacturer;
|
||||
break;
|
||||
case ACCESSORY_GET_STRING_MODEL:
|
||||
spin_lock_irq(&dev->lock);
|
||||
ret = strlen(dev->model) + 1;
|
||||
if (copy_to_user((void __user *)value, dev->model, ret))
|
||||
ret = -EFAULT;
|
||||
spin_unlock_irq(&dev->lock);
|
||||
break;
|
||||
|
||||
case ACCESSORY_GET_STRING_TYPE:
|
||||
spin_lock_irq(&dev->lock);
|
||||
ret = strlen(dev->type) + 1;
|
||||
if (copy_to_user((void __user *)value, dev->type, ret))
|
||||
ret = -EFAULT;
|
||||
spin_unlock_irq(&dev->lock);
|
||||
break;
|
||||
|
||||
src = dev->model;
|
||||
break;
|
||||
case ACCESSORY_GET_STRING_DESCRIPTION:
|
||||
src = dev->description;
|
||||
break;
|
||||
case ACCESSORY_GET_STRING_VERSION:
|
||||
spin_lock_irq(&dev->lock);
|
||||
ret = strlen(dev->version) + 1;
|
||||
if (copy_to_user((void __user *)value, dev->version, ret))
|
||||
ret = -EFAULT;
|
||||
spin_unlock_irq(&dev->lock);
|
||||
break;
|
||||
src = dev->version;
|
||||
break;
|
||||
case ACCESSORY_GET_STRING_URI:
|
||||
src = dev->uri;
|
||||
break;
|
||||
}
|
||||
if (!src)
|
||||
return -EINVAL;
|
||||
|
||||
ret = strlen(src) + 1;
|
||||
if (copy_to_user((void __user *)value, src, ret))
|
||||
ret = -EFAULT;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int acc_open(struct inode *ip, struct file *fp)
|
||||
{
|
||||
printk(KERN_INFO "acc_open\n");
|
||||
if (_lock(&_acc_dev->open_excl))
|
||||
if (atomic_xchg(&_acc_dev->open_excl, 1))
|
||||
return -EBUSY;
|
||||
|
||||
fp->private_data = _acc_dev;
|
||||
|
|
@ -535,7 +527,8 @@ static int acc_release(struct inode *ip, struct file *fp)
|
|||
{
|
||||
printk(KERN_INFO "acc_release\n");
|
||||
|
||||
_unlock(&_acc_dev->open_excl);
|
||||
WARN_ON(!atomic_xchg(&_acc_dev->open_excl, 0));
|
||||
_acc_dev->disconnected = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -626,6 +619,8 @@ static int acc_function_setup(struct usb_function *f,
|
|||
struct acc_dev *dev = func_to_dev(f);
|
||||
struct usb_composite_dev *cdev = dev->cdev;
|
||||
int value = -EOPNOTSUPP;
|
||||
u8 b_requestType = ctrl->bRequestType;
|
||||
u8 b_request = ctrl->bRequest;
|
||||
u16 w_index = le16_to_cpu(ctrl->wIndex);
|
||||
u16 w_value = le16_to_cpu(ctrl->wValue);
|
||||
u16 w_length = le16_to_cpu(ctrl->wLength);
|
||||
|
|
@ -633,20 +628,27 @@ static int acc_function_setup(struct usb_function *f,
|
|||
/*
|
||||
printk(KERN_INFO "acc_function_setup "
|
||||
"%02x.%02x v%04x i%04x l%u\n",
|
||||
ctrl->bRequestType, ctrl->bRequest,
|
||||
b_requestType, b_request,
|
||||
w_value, w_index, w_length);
|
||||
*/
|
||||
|
||||
if (dev->function.disabled && ctrl->bRequestType ==
|
||||
(USB_DIR_OUT | USB_TYPE_VENDOR)) {
|
||||
if (ctrl->bRequest == ACCESSORY_START) {
|
||||
schedule_delayed_work(&dev->work, msecs_to_jiffies(10));
|
||||
value = 0;
|
||||
} else if (ctrl->bRequest == ACCESSORY_SEND_STRING) {
|
||||
dev->string_index = w_index;
|
||||
cdev->gadget->ep0->driver_data = dev;
|
||||
cdev->req->complete = acc_complete_set_string;
|
||||
value = w_length;
|
||||
if (dev->function.disabled) {
|
||||
if (b_requestType == (USB_DIR_OUT | USB_TYPE_VENDOR)) {
|
||||
if (b_request == ACCESSORY_START) {
|
||||
schedule_delayed_work(
|
||||
&dev->work, msecs_to_jiffies(10));
|
||||
value = 0;
|
||||
} else if (b_request == ACCESSORY_SEND_STRING) {
|
||||
dev->string_index = w_index;
|
||||
cdev->gadget->ep0->driver_data = dev;
|
||||
cdev->req->complete = acc_complete_set_string;
|
||||
value = w_length;
|
||||
}
|
||||
} else if (b_requestType == (USB_DIR_IN | USB_TYPE_VENDOR)) {
|
||||
if (b_request == ACCESSORY_GET_PROTOCOL) {
|
||||
*((u16 *)cdev->req->buf) = PROTOCOL_VERSION;
|
||||
value = sizeof(u16);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -28,9 +28,24 @@
|
|||
/* Product ID to use when in accessory mode and adb is enabled */
|
||||
#define USB_ACCESSORY_ADB_PRODUCT_ID 0x2D01
|
||||
|
||||
/*
|
||||
* Indexes for strings sent by the host to identify the accessory.
|
||||
* The host sends these as vendor requests:
|
||||
/* Indexes for strings sent by the host via ACCESSORY_SEND_STRING */
|
||||
#define ACCESSORY_STRING_MANUFACTURER 0
|
||||
#define ACCESSORY_STRING_MODEL 1
|
||||
#define ACCESSORY_STRING_DESCRIPTION 2
|
||||
#define ACCESSORY_STRING_VERSION 3
|
||||
#define ACCESSORY_STRING_URI 4
|
||||
|
||||
/* Control request for retrieving device's protocol version (currently 1)
|
||||
*
|
||||
* requestType: USB_DIR_IN | USB_TYPE_VENDOR
|
||||
* request: ACCESSORY_GET_PROTOCOL
|
||||
* value: 0
|
||||
* index: 0
|
||||
* data version number (16 bits little endian)
|
||||
*/
|
||||
#define ACCESSORY_GET_PROTOCOL 51
|
||||
|
||||
/* Control request for host to send a string to the device
|
||||
*
|
||||
* requestType: USB_DIR_OUT | USB_TYPE_VENDOR
|
||||
* request: ACCESSORY_SEND_STRING
|
||||
|
|
@ -41,19 +56,24 @@
|
|||
* The device can later retrieve these strings via the
|
||||
* ACCESSORY_GET_STRING_* ioctls
|
||||
*/
|
||||
#define ACCESSORY_STRING_MANUFACTURER 0
|
||||
#define ACCESSORY_STRING_MODEL 1
|
||||
#define ACCESSORY_STRING_TYPE 2
|
||||
#define ACCESSORY_STRING_VERSION 3
|
||||
|
||||
/* control requests */
|
||||
#define ACCESSORY_SEND_STRING 52
|
||||
|
||||
/* Control request for starting device in accessory mode.
|
||||
* The host sends this after setting all its strings to the device.
|
||||
*
|
||||
* requestType: USB_DIR_OUT | USB_TYPE_VENDOR
|
||||
* request: ACCESSORY_START
|
||||
* value: 0
|
||||
* index: 0
|
||||
* data none
|
||||
*/
|
||||
#define ACCESSORY_START 53
|
||||
|
||||
/* Sends an event to the accessory via the interrupt endpoint */
|
||||
/* ioctls for retrieving strings set by the host */
|
||||
#define ACCESSORY_GET_STRING_MANUFACTURER _IOW('M', 1, char[256])
|
||||
#define ACCESSORY_GET_STRING_MODEL _IOW('M', 2, char[256])
|
||||
#define ACCESSORY_GET_STRING_TYPE _IOW('M', 3, char[256])
|
||||
#define ACCESSORY_GET_STRING_DESCRIPTION _IOW('M', 3, char[256])
|
||||
#define ACCESSORY_GET_STRING_VERSION _IOW('M', 4, char[256])
|
||||
#define ACCESSORY_GET_STRING_URI _IOW('M', 5, char[256])
|
||||
|
||||
#endif /* __LINUX_USB_F_ACCESSORY_H */
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue