#define BULK_BUFFER_SIZE 16384
#define ACC_STRING_SIZE 256
+#define PROTOCOL_VERSION 1
+
/* String IDs */
#define INTERFACE_STRING_INDEX 0
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;
}
}
-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)
{
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);
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);
}
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;
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",
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;
}
DBG(cdev, "acc_read(%d)\n", count);
+ if (dev->disconnected)
+ return -ENODEV;
+
if (count > BULK_BUFFER_SIZE)
count = BULK_BUFFER_SIZE;
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) {
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;
{
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;
}
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);
/*
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);
+ }
}
}
/* 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
* 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 */