]>
Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* Bluetooth HCI driver model support. */ |
2 | ||
1da177e4 LT |
3 | #include <linux/kernel.h> |
4 | #include <linux/init.h> | |
5 | ||
27d35284 MH |
6 | #include <linux/platform_device.h> |
7 | ||
1da177e4 LT |
8 | #include <net/bluetooth/bluetooth.h> |
9 | #include <net/bluetooth/hci_core.h> | |
10 | ||
11 | #ifndef CONFIG_BT_HCI_CORE_DEBUG | |
12 | #undef BT_DBG | |
13 | #define BT_DBG(D...) | |
14 | #endif | |
15 | ||
4d0eb004 | 16 | static inline char *typetostr(int type) |
1da177e4 | 17 | { |
4d0eb004 MH |
18 | switch (type) { |
19 | case HCI_VHCI: | |
20 | return "VIRTUAL"; | |
21 | case HCI_USB: | |
22 | return "USB"; | |
23 | case HCI_PCCARD: | |
24 | return "PCCARD"; | |
25 | case HCI_UART: | |
26 | return "UART"; | |
27 | case HCI_RS232: | |
28 | return "RS232"; | |
29 | case HCI_PCI: | |
30 | return "PCI"; | |
31 | default: | |
32 | return "UNKNOWN"; | |
33 | } | |
1da177e4 LT |
34 | } |
35 | ||
a91f2e39 | 36 | static ssize_t show_type(struct device *dev, struct device_attribute *attr, char *buf) |
1da177e4 | 37 | { |
a91f2e39 | 38 | struct hci_dev *hdev = dev_get_drvdata(dev); |
4d0eb004 | 39 | return sprintf(buf, "%s\n", typetostr(hdev->type)); |
1da177e4 LT |
40 | } |
41 | ||
a91f2e39 | 42 | static ssize_t show_address(struct device *dev, struct device_attribute *attr, char *buf) |
1da177e4 | 43 | { |
a91f2e39 | 44 | struct hci_dev *hdev = dev_get_drvdata(dev); |
1da177e4 LT |
45 | bdaddr_t bdaddr; |
46 | baswap(&bdaddr, &hdev->bdaddr); | |
47 | return sprintf(buf, "%s\n", batostr(&bdaddr)); | |
48 | } | |
49 | ||
a91f2e39 | 50 | static ssize_t show_inquiry_cache(struct device *dev, struct device_attribute *attr, char *buf) |
1da177e4 | 51 | { |
a91f2e39 | 52 | struct hci_dev *hdev = dev_get_drvdata(dev); |
1da177e4 LT |
53 | struct inquiry_cache *cache = &hdev->inq_cache; |
54 | struct inquiry_entry *e; | |
55 | int n = 0; | |
56 | ||
57 | hci_dev_lock_bh(hdev); | |
58 | ||
59 | for (e = cache->list; e; e = e->next) { | |
60 | struct inquiry_data *data = &e->data; | |
61 | bdaddr_t bdaddr; | |
62 | baswap(&bdaddr, &data->bdaddr); | |
63 | n += sprintf(buf + n, "%s %d %d %d 0x%.2x%.2x%.2x 0x%.4x %d %u\n", | |
64 | batostr(&bdaddr), | |
65 | data->pscan_rep_mode, data->pscan_period_mode, data->pscan_mode, | |
66 | data->dev_class[2], data->dev_class[1], data->dev_class[0], | |
67 | __le16_to_cpu(data->clock_offset), data->rssi, e->timestamp); | |
68 | } | |
69 | ||
70 | hci_dev_unlock_bh(hdev); | |
71 | return n; | |
72 | } | |
73 | ||
a91f2e39 | 74 | static ssize_t show_idle_timeout(struct device *dev, struct device_attribute *attr, char *buf) |
04837f64 | 75 | { |
a91f2e39 | 76 | struct hci_dev *hdev = dev_get_drvdata(dev); |
04837f64 MH |
77 | return sprintf(buf, "%d\n", hdev->idle_timeout); |
78 | } | |
79 | ||
a91f2e39 | 80 | static ssize_t store_idle_timeout(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) |
04837f64 | 81 | { |
a91f2e39 | 82 | struct hci_dev *hdev = dev_get_drvdata(dev); |
04837f64 MH |
83 | char *ptr; |
84 | __u32 val; | |
85 | ||
86 | val = simple_strtoul(buf, &ptr, 10); | |
87 | if (ptr == buf) | |
88 | return -EINVAL; | |
89 | ||
90 | if (val != 0 && (val < 500 || val > 3600000)) | |
91 | return -EINVAL; | |
92 | ||
93 | hdev->idle_timeout = val; | |
94 | ||
95 | return count; | |
96 | } | |
97 | ||
a91f2e39 | 98 | static ssize_t show_sniff_max_interval(struct device *dev, struct device_attribute *attr, char *buf) |
04837f64 | 99 | { |
a91f2e39 | 100 | struct hci_dev *hdev = dev_get_drvdata(dev); |
04837f64 MH |
101 | return sprintf(buf, "%d\n", hdev->sniff_max_interval); |
102 | } | |
103 | ||
a91f2e39 | 104 | static ssize_t store_sniff_max_interval(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) |
04837f64 | 105 | { |
a91f2e39 | 106 | struct hci_dev *hdev = dev_get_drvdata(dev); |
04837f64 MH |
107 | char *ptr; |
108 | __u16 val; | |
109 | ||
110 | val = simple_strtoul(buf, &ptr, 10); | |
111 | if (ptr == buf) | |
112 | return -EINVAL; | |
113 | ||
114 | if (val < 0x0002 || val > 0xFFFE || val % 2) | |
115 | return -EINVAL; | |
116 | ||
117 | if (val < hdev->sniff_min_interval) | |
118 | return -EINVAL; | |
119 | ||
120 | hdev->sniff_max_interval = val; | |
121 | ||
122 | return count; | |
123 | } | |
124 | ||
a91f2e39 | 125 | static ssize_t show_sniff_min_interval(struct device *dev, struct device_attribute *attr, char *buf) |
04837f64 | 126 | { |
a91f2e39 | 127 | struct hci_dev *hdev = dev_get_drvdata(dev); |
04837f64 MH |
128 | return sprintf(buf, "%d\n", hdev->sniff_min_interval); |
129 | } | |
130 | ||
a91f2e39 | 131 | static ssize_t store_sniff_min_interval(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) |
04837f64 | 132 | { |
a91f2e39 | 133 | struct hci_dev *hdev = dev_get_drvdata(dev); |
04837f64 MH |
134 | char *ptr; |
135 | __u16 val; | |
136 | ||
137 | val = simple_strtoul(buf, &ptr, 10); | |
138 | if (ptr == buf) | |
139 | return -EINVAL; | |
140 | ||
141 | if (val < 0x0002 || val > 0xFFFE || val % 2) | |
142 | return -EINVAL; | |
143 | ||
144 | if (val > hdev->sniff_max_interval) | |
145 | return -EINVAL; | |
146 | ||
147 | hdev->sniff_min_interval = val; | |
148 | ||
149 | return count; | |
150 | } | |
151 | ||
a91f2e39 MH |
152 | static DEVICE_ATTR(type, S_IRUGO, show_type, NULL); |
153 | static DEVICE_ATTR(address, S_IRUGO, show_address, NULL); | |
a91f2e39 | 154 | static DEVICE_ATTR(inquiry_cache, S_IRUGO, show_inquiry_cache, NULL); |
1da177e4 | 155 | |
a91f2e39 | 156 | static DEVICE_ATTR(idle_timeout, S_IRUGO | S_IWUSR, |
04837f64 | 157 | show_idle_timeout, store_idle_timeout); |
a91f2e39 | 158 | static DEVICE_ATTR(sniff_max_interval, S_IRUGO | S_IWUSR, |
04837f64 | 159 | show_sniff_max_interval, store_sniff_max_interval); |
a91f2e39 | 160 | static DEVICE_ATTR(sniff_min_interval, S_IRUGO | S_IWUSR, |
04837f64 MH |
161 | show_sniff_min_interval, store_sniff_min_interval); |
162 | ||
a91f2e39 | 163 | static struct device_attribute *bt_attrs[] = { |
a91f2e39 MH |
164 | &dev_attr_type, |
165 | &dev_attr_address, | |
a91f2e39 MH |
166 | &dev_attr_inquiry_cache, |
167 | &dev_attr_idle_timeout, | |
168 | &dev_attr_sniff_max_interval, | |
169 | &dev_attr_sniff_min_interval, | |
1da177e4 LT |
170 | NULL |
171 | }; | |
172 | ||
a91f2e39 | 173 | struct class *bt_class = NULL; |
be9d1227 MH |
174 | EXPORT_SYMBOL_GPL(bt_class); |
175 | ||
27d35284 MH |
176 | static struct bus_type bt_bus = { |
177 | .name = "bluetooth", | |
178 | }; | |
179 | ||
180 | static struct platform_device *bt_platform; | |
181 | ||
a91f2e39 MH |
182 | static void bt_release(struct device *dev) |
183 | { | |
184 | struct hci_dev *hdev = dev_get_drvdata(dev); | |
185 | kfree(hdev); | |
186 | } | |
187 | ||
1da177e4 LT |
188 | int hci_register_sysfs(struct hci_dev *hdev) |
189 | { | |
a91f2e39 | 190 | struct device *dev = &hdev->dev; |
1da177e4 LT |
191 | unsigned int i; |
192 | int err; | |
193 | ||
194 | BT_DBG("%p name %s type %d", hdev, hdev->name, hdev->type); | |
195 | ||
a91f2e39 MH |
196 | dev->class = bt_class; |
197 | ||
198 | if (hdev->parent) | |
199 | dev->parent = hdev->parent; | |
200 | else | |
201 | dev->parent = &bt_platform->dev; | |
202 | ||
203 | strlcpy(dev->bus_id, hdev->name, BUS_ID_SIZE); | |
1da177e4 | 204 | |
a91f2e39 | 205 | dev->release = bt_release; |
27d35284 | 206 | |
a91f2e39 | 207 | dev_set_drvdata(dev, hdev); |
27d35284 | 208 | |
a91f2e39 | 209 | err = device_register(dev); |
1da177e4 LT |
210 | if (err < 0) |
211 | return err; | |
212 | ||
213 | for (i = 0; bt_attrs[i]; i++) | |
a91f2e39 | 214 | device_create_file(dev, bt_attrs[i]); |
1da177e4 LT |
215 | |
216 | return 0; | |
217 | } | |
218 | ||
219 | void hci_unregister_sysfs(struct hci_dev *hdev) | |
220 | { | |
1da177e4 LT |
221 | BT_DBG("%p name %s type %d", hdev, hdev->name, hdev->type); |
222 | ||
4d0eb004 | 223 | device_del(&hdev->dev); |
1da177e4 LT |
224 | } |
225 | ||
226 | int __init bt_sysfs_init(void) | |
227 | { | |
27d35284 MH |
228 | int err; |
229 | ||
230 | bt_platform = platform_device_register_simple("bluetooth", -1, NULL, 0); | |
231 | if (IS_ERR(bt_platform)) | |
232 | return PTR_ERR(bt_platform); | |
233 | ||
234 | err = bus_register(&bt_bus); | |
235 | if (err < 0) { | |
236 | platform_device_unregister(bt_platform); | |
237 | return err; | |
238 | } | |
239 | ||
a91f2e39 MH |
240 | bt_class = class_create(THIS_MODULE, "bluetooth"); |
241 | if (IS_ERR(bt_class)) { | |
27d35284 MH |
242 | bus_unregister(&bt_bus); |
243 | platform_device_unregister(bt_platform); | |
a91f2e39 | 244 | return PTR_ERR(bt_class); |
27d35284 MH |
245 | } |
246 | ||
247 | return 0; | |
1da177e4 LT |
248 | } |
249 | ||
250 | void __exit bt_sysfs_cleanup(void) | |
251 | { | |
a91f2e39 | 252 | class_destroy(bt_class); |
27d35284 MH |
253 | |
254 | bus_unregister(&bt_bus); | |
255 | ||
256 | platform_device_unregister(bt_platform); | |
1da177e4 | 257 | } |