You need to sign in or sign up before continuing.
hci_sysfs.c 11.1 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8
/* Bluetooth HCI driver model support. */

#include <linux/kernel.h>
#include <linux/init.h>

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

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

12
static struct workqueue_struct *bt_workq;
L
Linus Torvalds 已提交
13

14 15 16 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 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
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,
};

static struct attribute_group *bt_link_groups[] = {
	&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)
{
90
	struct hci_conn *conn = container_of(work, struct hci_conn, work_add);
91

92 93
	/* ensure previous del is complete */
	flush_work(&conn->work_del);
94 95 96 97 98 99 100 101 102 103 104 105 106 107

	if (device_add(&conn->dev) < 0) {
		BT_ERR("Failed to register connection device");
		return;
	}
}

/*
 * 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)
{
108
	return !strncmp(dev_name(dev), "rfcomm", 6);
109 110 111 112
}

static void del_conn(struct work_struct *work)
{
113
	struct hci_conn *conn = container_of(work, struct hci_conn, work_del);
114 115
	struct hci_dev *hdev = conn->hdev;

116 117 118 119 120
	/* ensure previous add is complete */
	flush_work(&conn->work_add);

	if (!device_is_registered(&conn->dev))
		return;
121

122 123 124 125 126 127
	while (1) {
		struct device *dev;

		dev = device_find_child(&conn->dev, NULL, __match_tty);
		if (!dev)
			break;
128
		device_move(dev, NULL, DPM_ORDER_DEV_LAST);
129 130 131 132 133 134 135 136
		put_device(dev);
	}

	device_del(&conn->dev);
	put_device(&conn->dev);
	hci_dev_put(hdev);
}

137
void hci_conn_init_sysfs(struct hci_conn *conn)
138
{
139 140
	struct hci_dev *hdev = conn->hdev;

141 142
	BT_DBG("conn %p", conn);

143 144 145 146 147
	conn->dev.type = &bt_link;
	conn->dev.class = bt_class;
	conn->dev.parent = &hdev->dev;

	dev_set_drvdata(&conn->dev, conn);
148

149 150 151
	device_initialize(&conn->dev);

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

void hci_conn_add_sysfs(struct hci_conn *conn)
{
	struct hci_dev *hdev = conn->hdev;

	BT_DBG("conn %p", conn);

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

	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_typetostr(int type)
L
Linus Torvalds 已提交
174
{
175
	switch (type) {
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";
	}
L
Linus Torvalds 已提交
193 194
}

195
static ssize_t show_type(struct device *dev, struct device_attribute *attr, char *buf)
L
Linus Torvalds 已提交
196
{
197
	struct hci_dev *hdev = dev_get_drvdata(dev);
198
	return sprintf(buf, "%s\n", host_typetostr(hdev->type));
L
Linus Torvalds 已提交
199 200
}

201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220
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]);
}

221
static ssize_t show_address(struct device *dev, struct device_attribute *attr, char *buf)
L
Linus Torvalds 已提交
222
{
223
	struct hci_dev *hdev = dev_get_drvdata(dev);
L
Linus Torvalds 已提交
224 225 226 227 228
	bdaddr_t bdaddr;
	baswap(&bdaddr, &hdev->bdaddr);
	return sprintf(buf, "%s\n", batostr(&bdaddr));
}

229 230 231 232 233 234 235 236 237 238 239
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]);
}

240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257
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);
}

258
static ssize_t show_inquiry_cache(struct device *dev, struct device_attribute *attr, char *buf)
L
Linus Torvalds 已提交
259
{
260
	struct hci_dev *hdev = dev_get_drvdata(dev);
L
Linus Torvalds 已提交
261 262 263 264 265 266 267 268 269 270
	struct inquiry_cache *cache = &hdev->inq_cache;
	struct inquiry_entry *e;
	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);
271
		n += sprintf(buf + n, "%s %d %d %d 0x%.2x%.2x%.2x 0x%.4x %d %d %u\n",
L
Linus Torvalds 已提交
272
				batostr(&bdaddr),
273 274 275 276 277
				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);
L
Linus Torvalds 已提交
278 279 280 281 282 283
	}

	hci_dev_unlock_bh(hdev);
	return n;
}

284
static ssize_t show_idle_timeout(struct device *dev, struct device_attribute *attr, char *buf)
285
{
286
	struct hci_dev *hdev = dev_get_drvdata(dev);
287 288 289
	return sprintf(buf, "%d\n", hdev->idle_timeout);
}

290
static ssize_t store_idle_timeout(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
291
{
292
	struct hci_dev *hdev = dev_get_drvdata(dev);
293 294 295 296 297 298 299 300 301 302 303 304 305 306 307
	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;
}

308
static ssize_t show_sniff_max_interval(struct device *dev, struct device_attribute *attr, char *buf)
309
{
310
	struct hci_dev *hdev = dev_get_drvdata(dev);
311 312 313
	return sprintf(buf, "%d\n", hdev->sniff_max_interval);
}

314
static ssize_t store_sniff_max_interval(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
315
{
316
	struct hci_dev *hdev = dev_get_drvdata(dev);
317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334
	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;
}

335
static ssize_t show_sniff_min_interval(struct device *dev, struct device_attribute *attr, char *buf)
336
{
337
	struct hci_dev *hdev = dev_get_drvdata(dev);
338 339 340
	return sprintf(buf, "%d\n", hdev->sniff_min_interval);
}

341
static ssize_t store_sniff_min_interval(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
342
{
343
	struct hci_dev *hdev = dev_get_drvdata(dev);
344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361
	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;
}

362
static DEVICE_ATTR(type, S_IRUGO, show_type, NULL);
363 364
static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
static DEVICE_ATTR(class, S_IRUGO, show_class, NULL);
365
static DEVICE_ATTR(address, S_IRUGO, show_address, NULL);
366
static DEVICE_ATTR(features, S_IRUGO, show_features, NULL);
367 368 369
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);
370
static DEVICE_ATTR(inquiry_cache, S_IRUGO, show_inquiry_cache, NULL);
L
Linus Torvalds 已提交
371

372
static DEVICE_ATTR(idle_timeout, S_IRUGO | S_IWUSR,
373
				show_idle_timeout, store_idle_timeout);
374
static DEVICE_ATTR(sniff_max_interval, S_IRUGO | S_IWUSR,
375
				show_sniff_max_interval, store_sniff_max_interval);
376
static DEVICE_ATTR(sniff_min_interval, S_IRUGO | S_IWUSR,
377 378
				show_sniff_min_interval, store_sniff_min_interval);

379 380 381 382 383 384 385 386 387 388 389 390 391
static struct attribute *bt_host_attrs[] = {
	&dev_attr_type.attr,
	&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_inquiry_cache.attr,
	&dev_attr_idle_timeout.attr,
	&dev_attr_sniff_max_interval.attr,
	&dev_attr_sniff_min_interval.attr,
L
Linus Torvalds 已提交
392 393 394
	NULL
};

395 396
static struct attribute_group bt_host_group = {
	.attrs = bt_host_attrs,
397 398
};

399 400 401
static struct attribute_group *bt_host_groups[] = {
	&bt_host_group,
	NULL
402 403
};

404
static void bt_host_release(struct device *dev)
405
{
406 407 408 409
	void *data = dev_get_drvdata(dev);
	kfree(data);
}

410 411 412 413 414
static struct device_type bt_host = {
	.name    = "host",
	.groups  = bt_host_groups,
	.release = bt_host_release,
};
415

L
Linus Torvalds 已提交
416 417
int hci_register_sysfs(struct hci_dev *hdev)
{
418
	struct device *dev = &hdev->dev;
L
Linus Torvalds 已提交
419 420 421 422
	int err;

	BT_DBG("%p name %s type %d", hdev, hdev->name, hdev->type);

423 424
	dev->type = &bt_host;
	dev->class = bt_class;
425
	dev->parent = hdev->parent;
426

427
	dev_set_name(dev, "%s", hdev->name);
L
Linus Torvalds 已提交
428

429
	dev_set_drvdata(dev, hdev);
430

431
	err = device_register(dev);
L
Linus Torvalds 已提交
432 433 434 435 436 437 438 439 440 441
	if (err < 0)
		return err;

	return 0;
}

void hci_unregister_sysfs(struct hci_dev *hdev)
{
	BT_DBG("%p name %s type %d", hdev, hdev->name, hdev->type);

442
	device_del(&hdev->dev);
L
Linus Torvalds 已提交
443 444 445 446
}

int __init bt_sysfs_init(void)
{
447 448
	bt_workq = create_singlethread_workqueue("bluetooth");
	if (!bt_workq)
449
		return -ENOMEM;
450

451 452
	bt_class = class_create(THIS_MODULE, "bluetooth");
	if (IS_ERR(bt_class)) {
453
		destroy_workqueue(bt_workq);
454
		return PTR_ERR(bt_class);
455 456 457
	}

	return 0;
L
Linus Torvalds 已提交
458 459
}

460
void bt_sysfs_cleanup(void)
L
Linus Torvalds 已提交
461
{
462
	destroy_workqueue(bt_workq);
463

464
	class_destroy(bt_class);
L
Linus Torvalds 已提交
465
}