hci_sysfs.c 12 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1
2
3
4
/* Bluetooth HCI driver model support. */

#include <linux/kernel.h>
#include <linux/init.h>
5
#include <linux/debugfs.h>
Linus Torvalds's avatar
Linus Torvalds committed
6
7
8
9

#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>

10
11
12
struct class *bt_class = NULL;
EXPORT_SYMBOL_GPL(bt_class);

13
14
15
struct dentry *bt_debugfs = NULL;
EXPORT_SYMBOL_GPL(bt_debugfs);

16
static struct workqueue_struct *bt_workq;
Linus Torvalds's avatar
Linus Torvalds committed
17

18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
static inline char *link_typetostr(int type)
{
	switch (type) {
	case ACL_LINK:
		return "ACL";
	case SCO_LINK:
		return "SCO";
	case ESCO_LINK:
		return "eSCO";
	default:
		return "UNKNOWN";
	}
}

static ssize_t show_link_type(struct device *dev, struct device_attribute *attr, char *buf)
{
	struct hci_conn *conn = dev_get_drvdata(dev);
	return sprintf(buf, "%s\n", link_typetostr(conn->type));
}

static ssize_t show_link_address(struct device *dev, struct device_attribute *attr, char *buf)
{
	struct hci_conn *conn = dev_get_drvdata(dev);
	bdaddr_t bdaddr;
	baswap(&bdaddr, &conn->dst);
	return sprintf(buf, "%s\n", batostr(&bdaddr));
}

static ssize_t show_link_features(struct device *dev, struct device_attribute *attr, char *buf)
{
	struct hci_conn *conn = dev_get_drvdata(dev);

	return sprintf(buf, "0x%02x%02x%02x%02x%02x%02x%02x%02x\n",
				conn->features[0], conn->features[1],
				conn->features[2], conn->features[3],
				conn->features[4], conn->features[5],
				conn->features[6], conn->features[7]);
}

#define LINK_ATTR(_name,_mode,_show,_store) \
struct device_attribute link_attr_##_name = __ATTR(_name,_mode,_show,_store)

static LINK_ATTR(type, S_IRUGO, show_link_type, NULL);
static LINK_ATTR(address, S_IRUGO, show_link_address, NULL);
static LINK_ATTR(features, S_IRUGO, show_link_features, NULL);

static struct attribute *bt_link_attrs[] = {
	&link_attr_type.attr,
	&link_attr_address.attr,
	&link_attr_features.attr,
	NULL
};

static struct attribute_group bt_link_group = {
	.attrs = bt_link_attrs,
};

75
static const struct attribute_group *bt_link_groups[] = {
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
	&bt_link_group,
	NULL
};

static void bt_link_release(struct device *dev)
{
	void *data = dev_get_drvdata(dev);
	kfree(data);
}

static struct device_type bt_link = {
	.name    = "link",
	.groups  = bt_link_groups,
	.release = bt_link_release,
};

static void add_conn(struct work_struct *work)
{
94
	struct hci_conn *conn = container_of(work, struct hci_conn, work_add);
95
	struct hci_dev *hdev = conn->hdev;
96

97
98
	dev_set_name(&conn->dev, "%s:%d", hdev->name, conn->handle);

99
100
	dev_set_drvdata(&conn->dev, conn);

101
102
103
104
	if (device_add(&conn->dev) < 0) {
		BT_ERR("Failed to register connection device");
		return;
	}
105
106

	hci_dev_hold(hdev);
107
108
109
110
111
112
113
114
115
}

/*
 * The rfcomm tty device will possibly retain even when conn
 * is down, and sysfs doesn't support move zombie device,
 * so we should move the device before conn device is destroyed.
 */
static int __match_tty(struct device *dev, void *data)
{
116
	return !strncmp(dev_name(dev), "rfcomm", 6);
117
118
119
120
}

static void del_conn(struct work_struct *work)
{
121
	struct hci_conn *conn = container_of(work, struct hci_conn, work_del);
122
123
	struct hci_dev *hdev = conn->hdev;

124
125
	if (!device_is_registered(&conn->dev))
		return;
126

127
128
129
130
131
132
	while (1) {
		struct device *dev;

		dev = device_find_child(&conn->dev, NULL, __match_tty);
		if (!dev)
			break;
133
		device_move(dev, NULL, DPM_ORDER_DEV_LAST);
134
135
136
137
138
		put_device(dev);
	}

	device_del(&conn->dev);
	put_device(&conn->dev);
139

140
141
142
	hci_dev_put(hdev);
}

143
void hci_conn_init_sysfs(struct hci_conn *conn)
144
{
145
146
	struct hci_dev *hdev = conn->hdev;

147
148
	BT_DBG("conn %p", conn);

149
150
151
152
153
154
155
	conn->dev.type = &bt_link;
	conn->dev.class = bt_class;
	conn->dev.parent = &hdev->dev;

	device_initialize(&conn->dev);

	INIT_WORK(&conn->work_add, add_conn);
156
	INIT_WORK(&conn->work_del, del_conn);
157
158
159
160
161
162
163
164
165
166
167
168
}

void hci_conn_add_sysfs(struct hci_conn *conn)
{
	BT_DBG("conn %p", conn);

	queue_work(bt_workq, &conn->work_add);
}

void hci_conn_del_sysfs(struct hci_conn *conn)
{
	BT_DBG("conn %p", conn);
169

170
	queue_work(bt_workq, &conn->work_del);
171
172
}

173
static inline char *host_bustostr(int bus)
Linus Torvalds's avatar
Linus Torvalds committed
174
{
175
	switch (bus) {
176
	case HCI_VIRTUAL:
177
178
179
180
181
182
183
184
185
186
187
		return "VIRTUAL";
	case HCI_USB:
		return "USB";
	case HCI_PCCARD:
		return "PCCARD";
	case HCI_UART:
		return "UART";
	case HCI_RS232:
		return "RS232";
	case HCI_PCI:
		return "PCI";
188
189
	case HCI_SDIO:
		return "SDIO";
190
191
192
	default:
		return "UNKNOWN";
	}
Linus Torvalds's avatar
Linus Torvalds committed
193
194
}

195
196
197
198
199
200
201
202
203
204
205
206
static inline char *host_typetostr(int type)
{
	switch (type) {
	case HCI_BREDR:
		return "BR/EDR";
	case HCI_80211:
		return "802.11";
	default:
		return "UNKNOWN";
	}
}

207
static ssize_t show_bus(struct device *dev, struct device_attribute *attr, char *buf)
Linus Torvalds's avatar
Linus Torvalds committed
208
{
209
	struct hci_dev *hdev = dev_get_drvdata(dev);
210
	return sprintf(buf, "%s\n", host_bustostr(hdev->bus));
Linus Torvalds's avatar
Linus Torvalds committed
211
212
}

213
214
215
216
217
218
static ssize_t show_type(struct device *dev, struct device_attribute *attr, char *buf)
{
	struct hci_dev *hdev = dev_get_drvdata(dev);
	return sprintf(buf, "%s\n", host_typetostr(hdev->dev_type));
}

219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
static ssize_t show_name(struct device *dev, struct device_attribute *attr, char *buf)
{
	struct hci_dev *hdev = dev_get_drvdata(dev);
	char name[249];
	int i;

	for (i = 0; i < 248; i++)
		name[i] = hdev->dev_name[i];

	name[248] = '\0';
	return sprintf(buf, "%s\n", name);
}

static ssize_t show_class(struct device *dev, struct device_attribute *attr, char *buf)
{
	struct hci_dev *hdev = dev_get_drvdata(dev);
	return sprintf(buf, "0x%.2x%.2x%.2x\n",
			hdev->dev_class[2], hdev->dev_class[1], hdev->dev_class[0]);
}

239
static ssize_t show_address(struct device *dev, struct device_attribute *attr, char *buf)
Linus Torvalds's avatar
Linus Torvalds committed
240
{
241
	struct hci_dev *hdev = dev_get_drvdata(dev);
Linus Torvalds's avatar
Linus Torvalds committed
242
243
244
245
246
	bdaddr_t bdaddr;
	baswap(&bdaddr, &hdev->bdaddr);
	return sprintf(buf, "%s\n", batostr(&bdaddr));
}

247
248
249
250
251
252
253
254
255
256
257
static ssize_t show_features(struct device *dev, struct device_attribute *attr, char *buf)
{
	struct hci_dev *hdev = dev_get_drvdata(dev);

	return sprintf(buf, "0x%02x%02x%02x%02x%02x%02x%02x%02x\n",
				hdev->features[0], hdev->features[1],
				hdev->features[2], hdev->features[3],
				hdev->features[4], hdev->features[5],
				hdev->features[6], hdev->features[7]);
}

258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
static ssize_t show_manufacturer(struct device *dev, struct device_attribute *attr, char *buf)
{
	struct hci_dev *hdev = dev_get_drvdata(dev);
	return sprintf(buf, "%d\n", hdev->manufacturer);
}

static ssize_t show_hci_version(struct device *dev, struct device_attribute *attr, char *buf)
{
	struct hci_dev *hdev = dev_get_drvdata(dev);
	return sprintf(buf, "%d\n", hdev->hci_ver);
}

static ssize_t show_hci_revision(struct device *dev, struct device_attribute *attr, char *buf)
{
	struct hci_dev *hdev = dev_get_drvdata(dev);
	return sprintf(buf, "%d\n", hdev->hci_rev);
}

276
static ssize_t show_idle_timeout(struct device *dev, struct device_attribute *attr, char *buf)
277
{
278
	struct hci_dev *hdev = dev_get_drvdata(dev);
279
280
281
	return sprintf(buf, "%d\n", hdev->idle_timeout);
}

282
static ssize_t store_idle_timeout(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
283
{
284
	struct hci_dev *hdev = dev_get_drvdata(dev);
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
	char *ptr;
	__u32 val;

	val = simple_strtoul(buf, &ptr, 10);
	if (ptr == buf)
		return -EINVAL;

	if (val != 0 && (val < 500 || val > 3600000))
		return -EINVAL;

	hdev->idle_timeout = val;

	return count;
}

300
static ssize_t show_sniff_max_interval(struct device *dev, struct device_attribute *attr, char *buf)
301
{
302
	struct hci_dev *hdev = dev_get_drvdata(dev);
303
304
305
	return sprintf(buf, "%d\n", hdev->sniff_max_interval);
}

306
static ssize_t store_sniff_max_interval(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
307
{
308
	struct hci_dev *hdev = dev_get_drvdata(dev);
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
	char *ptr;
	__u16 val;

	val = simple_strtoul(buf, &ptr, 10);
	if (ptr == buf)
		return -EINVAL;

	if (val < 0x0002 || val > 0xFFFE || val % 2)
		return -EINVAL;

	if (val < hdev->sniff_min_interval)
		return -EINVAL;

	hdev->sniff_max_interval = val;

	return count;
}

327
static ssize_t show_sniff_min_interval(struct device *dev, struct device_attribute *attr, char *buf)
328
{
329
	struct hci_dev *hdev = dev_get_drvdata(dev);
330
331
332
	return sprintf(buf, "%d\n", hdev->sniff_min_interval);
}

333
static ssize_t store_sniff_min_interval(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
334
{
335
	struct hci_dev *hdev = dev_get_drvdata(dev);
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
	char *ptr;
	__u16 val;

	val = simple_strtoul(buf, &ptr, 10);
	if (ptr == buf)
		return -EINVAL;

	if (val < 0x0002 || val > 0xFFFE || val % 2)
		return -EINVAL;

	if (val > hdev->sniff_max_interval)
		return -EINVAL;

	hdev->sniff_min_interval = val;

	return count;
}

354
static DEVICE_ATTR(bus, S_IRUGO, show_bus, NULL);
355
static DEVICE_ATTR(type, S_IRUGO, show_type, NULL);
356
357
static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
static DEVICE_ATTR(class, S_IRUGO, show_class, NULL);
358
static DEVICE_ATTR(address, S_IRUGO, show_address, NULL);
359
static DEVICE_ATTR(features, S_IRUGO, show_features, NULL);
360
361
362
static DEVICE_ATTR(manufacturer, S_IRUGO, show_manufacturer, NULL);
static DEVICE_ATTR(hci_version, S_IRUGO, show_hci_version, NULL);
static DEVICE_ATTR(hci_revision, S_IRUGO, show_hci_revision, NULL);
Linus Torvalds's avatar
Linus Torvalds committed
363

364
static DEVICE_ATTR(idle_timeout, S_IRUGO | S_IWUSR,
365
				show_idle_timeout, store_idle_timeout);
366
static DEVICE_ATTR(sniff_max_interval, S_IRUGO | S_IWUSR,
367
				show_sniff_max_interval, store_sniff_max_interval);
368
static DEVICE_ATTR(sniff_min_interval, S_IRUGO | S_IWUSR,
369
370
				show_sniff_min_interval, store_sniff_min_interval);

371
static struct attribute *bt_host_attrs[] = {
372
	&dev_attr_bus.attr,
373
	&dev_attr_type.attr,
374
375
376
377
378
379
380
381
382
383
	&dev_attr_name.attr,
	&dev_attr_class.attr,
	&dev_attr_address.attr,
	&dev_attr_features.attr,
	&dev_attr_manufacturer.attr,
	&dev_attr_hci_version.attr,
	&dev_attr_hci_revision.attr,
	&dev_attr_idle_timeout.attr,
	&dev_attr_sniff_max_interval.attr,
	&dev_attr_sniff_min_interval.attr,
Linus Torvalds's avatar
Linus Torvalds committed
384
385
386
	NULL
};

387
388
static struct attribute_group bt_host_group = {
	.attrs = bt_host_attrs,
389
390
};

391
static const struct attribute_group *bt_host_groups[] = {
392
393
	&bt_host_group,
	NULL
394
395
};

396
static void bt_host_release(struct device *dev)
397
{
398
399
400
401
	void *data = dev_get_drvdata(dev);
	kfree(data);
}

402
403
404
405
406
static struct device_type bt_host = {
	.name    = "host",
	.groups  = bt_host_groups,
	.release = bt_host_release,
};
407

408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
static int inquiry_cache_open(struct inode *inode, struct file *file)
{
	file->private_data = inode->i_private;
	return 0;
}

static ssize_t inquiry_cache_read(struct file *file, char __user *userbuf,
						size_t count, loff_t *ppos)
{
	struct hci_dev *hdev = file->private_data;
	struct inquiry_cache *cache = &hdev->inq_cache;
	struct inquiry_entry *e;
	char buf[4096];
	int n = 0;

	hci_dev_lock_bh(hdev);

	for (e = cache->list; e; e = e->next) {
		struct inquiry_data *data = &e->data;
		bdaddr_t bdaddr;
		baswap(&bdaddr, &data->bdaddr);
		n += sprintf(buf + n, "%s %d %d %d 0x%.2x%.2x%.2x 0x%.4x %d %d %u\n",
				batostr(&bdaddr),
				data->pscan_rep_mode, data->pscan_period_mode,
				data->pscan_mode, data->dev_class[2],
				data->dev_class[1], data->dev_class[0],
				__le16_to_cpu(data->clock_offset),
				data->rssi, data->ssp_mode, e->timestamp);
	}

	hci_dev_unlock_bh(hdev);

	return simple_read_from_buffer(userbuf, count, ppos, buf, n);
}

static const struct file_operations inquiry_cache_fops = {
	.open = inquiry_cache_open,
	.read = inquiry_cache_read,
};

Linus Torvalds's avatar
Linus Torvalds committed
448
449
int hci_register_sysfs(struct hci_dev *hdev)
{
450
	struct device *dev = &hdev->dev;
Linus Torvalds's avatar
Linus Torvalds committed
451
452
	int err;

453
	BT_DBG("%p name %s bus %d", hdev, hdev->name, hdev->bus);
Linus Torvalds's avatar
Linus Torvalds committed
454

455
456
	dev->type = &bt_host;
	dev->class = bt_class;
457
	dev->parent = hdev->parent;
458

459
	dev_set_name(dev, "%s", hdev->name);
Linus Torvalds's avatar
Linus Torvalds committed
460

461
	dev_set_drvdata(dev, hdev);
462

463
	err = device_register(dev);
Linus Torvalds's avatar
Linus Torvalds committed
464
465
466
	if (err < 0)
		return err;

467
468
469
470
471
472
473
474
475
476
	if (!bt_debugfs)
		return 0;

	hdev->debugfs = debugfs_create_dir(hdev->name, bt_debugfs);
	if (!hdev->debugfs)
		return 0;

	debugfs_create_file("inquiry_cache", 0444, hdev->debugfs,
						hdev, &inquiry_cache_fops);

Linus Torvalds's avatar
Linus Torvalds committed
477
478
479
480
481
	return 0;
}

void hci_unregister_sysfs(struct hci_dev *hdev)
{
482
	BT_DBG("%p name %s bus %d", hdev, hdev->name, hdev->bus);
Linus Torvalds's avatar
Linus Torvalds committed
483

484
485
	debugfs_remove_recursive(hdev->debugfs);

486
	device_del(&hdev->dev);
Linus Torvalds's avatar
Linus Torvalds committed
487
488
489
490
}

int __init bt_sysfs_init(void)
{
491
492
	bt_workq = create_singlethread_workqueue("bluetooth");
	if (!bt_workq)
493
		return -ENOMEM;
494

495
496
	bt_debugfs = debugfs_create_dir("bluetooth", NULL);

497
498
	bt_class = class_create(THIS_MODULE, "bluetooth");
	if (IS_ERR(bt_class)) {
499
		destroy_workqueue(bt_workq);
500
		return PTR_ERR(bt_class);
501
502
503
	}

	return 0;
Linus Torvalds's avatar
Linus Torvalds committed
504
505
}

506
void bt_sysfs_cleanup(void)
Linus Torvalds's avatar
Linus Torvalds committed
507
{
508
	class_destroy(bt_class);
509
510
511
512

	debugfs_remove_recursive(bt_debugfs);

	destroy_workqueue(bt_workq);
Linus Torvalds's avatar
Linus Torvalds committed
513
}