Inaky Perez-Gonzalez | 90ff96f | 2008-09-17 16:34:23 +0100 | [diff] [blame] | 1 | /* |
| 2 | * WUSB devices |
| 3 | * sysfs bindings |
| 4 | * |
| 5 | * Copyright (C) 2007 Intel Corporation |
| 6 | * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> |
| 7 | * |
| 8 | * This program is free software; you can redistribute it and/or |
| 9 | * modify it under the terms of the GNU General Public License version |
| 10 | * 2 as published by the Free Software Foundation. |
| 11 | * |
| 12 | * This program is distributed in the hope that it will be useful, |
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 15 | * GNU General Public License for more details. |
| 16 | * |
| 17 | * You should have received a copy of the GNU General Public License |
| 18 | * along with this program; if not, write to the Free Software |
| 19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
| 20 | * 02110-1301, USA. |
| 21 | * |
| 22 | * |
| 23 | * Get them out of the way... |
| 24 | */ |
| 25 | |
| 26 | #include <linux/jiffies.h> |
| 27 | #include <linux/ctype.h> |
| 28 | #include <linux/workqueue.h> |
| 29 | #include "wusbhc.h" |
| 30 | |
Inaky Perez-Gonzalez | 90ff96f | 2008-09-17 16:34:23 +0100 | [diff] [blame] | 31 | static ssize_t wusb_disconnect_store(struct device *dev, |
| 32 | struct device_attribute *attr, |
| 33 | const char *buf, size_t size) |
| 34 | { |
| 35 | struct usb_device *usb_dev; |
| 36 | struct wusbhc *wusbhc; |
| 37 | unsigned command; |
| 38 | u8 port_idx; |
| 39 | |
| 40 | if (sscanf(buf, "%u", &command) != 1) |
| 41 | return -EINVAL; |
| 42 | if (command == 0) |
| 43 | return size; |
| 44 | usb_dev = to_usb_device(dev); |
| 45 | wusbhc = wusbhc_get_by_usb_dev(usb_dev); |
| 46 | if (wusbhc == NULL) |
| 47 | return -ENODEV; |
| 48 | |
| 49 | mutex_lock(&wusbhc->mutex); |
| 50 | port_idx = wusb_port_no_to_idx(usb_dev->portnum); |
| 51 | __wusbhc_dev_disable(wusbhc, port_idx); |
| 52 | mutex_unlock(&wusbhc->mutex); |
| 53 | wusbhc_put(wusbhc); |
| 54 | return size; |
| 55 | } |
| 56 | static DEVICE_ATTR(wusb_disconnect, 0200, NULL, wusb_disconnect_store); |
| 57 | |
| 58 | static ssize_t wusb_cdid_show(struct device *dev, |
| 59 | struct device_attribute *attr, char *buf) |
| 60 | { |
| 61 | ssize_t result; |
| 62 | struct wusb_dev *wusb_dev; |
| 63 | |
| 64 | wusb_dev = wusb_dev_get_by_usb_dev(to_usb_device(dev)); |
| 65 | if (wusb_dev == NULL) |
| 66 | return -ENODEV; |
| 67 | result = ckhdid_printf(buf, PAGE_SIZE, &wusb_dev->cdid); |
| 68 | strcat(buf, "\n"); |
| 69 | wusb_dev_put(wusb_dev); |
| 70 | return result + 1; |
| 71 | } |
| 72 | static DEVICE_ATTR(wusb_cdid, 0444, wusb_cdid_show, NULL); |
| 73 | |
| 74 | static ssize_t wusb_ck_store(struct device *dev, |
| 75 | struct device_attribute *attr, |
| 76 | const char *buf, size_t size) |
| 77 | { |
| 78 | int result; |
| 79 | struct usb_device *usb_dev; |
| 80 | struct wusbhc *wusbhc; |
| 81 | struct wusb_ckhdid ck; |
| 82 | |
| 83 | result = sscanf(buf, |
| 84 | "%02hhx %02hhx %02hhx %02hhx " |
| 85 | "%02hhx %02hhx %02hhx %02hhx " |
| 86 | "%02hhx %02hhx %02hhx %02hhx " |
| 87 | "%02hhx %02hhx %02hhx %02hhx\n", |
| 88 | &ck.data[0] , &ck.data[1], |
| 89 | &ck.data[2] , &ck.data[3], |
| 90 | &ck.data[4] , &ck.data[5], |
| 91 | &ck.data[6] , &ck.data[7], |
| 92 | &ck.data[8] , &ck.data[9], |
| 93 | &ck.data[10], &ck.data[11], |
| 94 | &ck.data[12], &ck.data[13], |
| 95 | &ck.data[14], &ck.data[15]); |
| 96 | if (result != 16) |
| 97 | return -EINVAL; |
| 98 | |
| 99 | usb_dev = to_usb_device(dev); |
| 100 | wusbhc = wusbhc_get_by_usb_dev(usb_dev); |
| 101 | if (wusbhc == NULL) |
| 102 | return -ENODEV; |
| 103 | result = wusb_dev_4way_handshake(wusbhc, usb_dev->wusb_dev, &ck); |
| 104 | memset(&ck, 0, sizeof(ck)); |
| 105 | wusbhc_put(wusbhc); |
| 106 | return result < 0 ? result : size; |
| 107 | } |
| 108 | static DEVICE_ATTR(wusb_ck, 0200, NULL, wusb_ck_store); |
| 109 | |
| 110 | static struct attribute *wusb_dev_attrs[] = { |
| 111 | &dev_attr_wusb_disconnect.attr, |
| 112 | &dev_attr_wusb_cdid.attr, |
| 113 | &dev_attr_wusb_ck.attr, |
| 114 | NULL, |
| 115 | }; |
| 116 | |
| 117 | static struct attribute_group wusb_dev_attr_group = { |
| 118 | .name = NULL, /* we want them in the same directory */ |
| 119 | .attrs = wusb_dev_attrs, |
| 120 | }; |
| 121 | |
| 122 | int wusb_dev_sysfs_add(struct wusbhc *wusbhc, struct usb_device *usb_dev, |
| 123 | struct wusb_dev *wusb_dev) |
| 124 | { |
| 125 | int result = sysfs_create_group(&usb_dev->dev.kobj, |
| 126 | &wusb_dev_attr_group); |
| 127 | struct device *dev = &usb_dev->dev; |
| 128 | if (result < 0) |
| 129 | dev_err(dev, "Cannot register WUSB-dev attributes: %d\n", |
| 130 | result); |
| 131 | return result; |
| 132 | } |
| 133 | |
| 134 | void wusb_dev_sysfs_rm(struct wusb_dev *wusb_dev) |
| 135 | { |
| 136 | struct usb_device *usb_dev = wusb_dev->usb_dev; |
| 137 | if (usb_dev) |
| 138 | sysfs_remove_group(&usb_dev->dev.kobj, &wusb_dev_attr_group); |
| 139 | } |