12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178 |
- /*
- BlueZ - Bluetooth protocol stack for Linux
- Copyright (C) 2014 Intel Corporation
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License version 2 as
- published by the Free Software Foundation;
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
- OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
- IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
- CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
- COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
- SOFTWARE IS DISCLAIMED.
- */
- #include <linux/debugfs.h>
- #include <net/bluetooth/bluetooth.h>
- #include <net/bluetooth/hci_core.h>
- #include "hci_debugfs.h"
- #define DEFINE_QUIRK_ATTRIBUTE(__name, __quirk) \
- static ssize_t __name ## _read(struct file *file, \
- char __user *user_buf, \
- size_t count, loff_t *ppos) \
- { \
- struct hci_dev *hdev = file->private_data; \
- char buf[3]; \
- \
- buf[0] = test_bit(__quirk, &hdev->quirks) ? 'Y' : 'N'; \
- buf[1] = '\n'; \
- buf[2] = '\0'; \
- return simple_read_from_buffer(user_buf, count, ppos, buf, 2); \
- } \
- \
- static ssize_t __name ## _write(struct file *file, \
- const char __user *user_buf, \
- size_t count, loff_t *ppos) \
- { \
- struct hci_dev *hdev = file->private_data; \
- char buf[32]; \
- size_t buf_size = min(count, (sizeof(buf) - 1)); \
- bool enable; \
- \
- if (test_bit(HCI_UP, &hdev->flags)) \
- return -EBUSY; \
- \
- if (copy_from_user(buf, user_buf, buf_size)) \
- return -EFAULT; \
- \
- buf[buf_size] = '\0'; \
- if (strtobool(buf, &enable)) \
- return -EINVAL; \
- \
- if (enable == test_bit(__quirk, &hdev->quirks)) \
- return -EALREADY; \
- \
- change_bit(__quirk, &hdev->quirks); \
- \
- return count; \
- } \
- \
- static const struct file_operations __name ## _fops = { \
- .open = simple_open, \
- .read = __name ## _read, \
- .write = __name ## _write, \
- .llseek = default_llseek, \
- } \
- #define DEFINE_INFO_ATTRIBUTE(__name, __field) \
- static int __name ## _show(struct seq_file *f, void *ptr) \
- { \
- struct hci_dev *hdev = f->private; \
- \
- hci_dev_lock(hdev); \
- seq_printf(f, "%s\n", hdev->__field ? : ""); \
- hci_dev_unlock(hdev); \
- \
- return 0; \
- } \
- \
- static int __name ## _open(struct inode *inode, struct file *file) \
- { \
- return single_open(file, __name ## _show, inode->i_private); \
- } \
- \
- static const struct file_operations __name ## _fops = { \
- .open = __name ## _open, \
- .read = seq_read, \
- .llseek = seq_lseek, \
- .release = single_release, \
- } \
- static int features_show(struct seq_file *f, void *ptr)
- {
- struct hci_dev *hdev = f->private;
- u8 p;
- hci_dev_lock(hdev);
- for (p = 0; p < HCI_MAX_PAGES && p <= hdev->max_page; p++) {
- seq_printf(f, "%2u: 0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x "
- "0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x\n", p,
- hdev->features[p][0], hdev->features[p][1],
- hdev->features[p][2], hdev->features[p][3],
- hdev->features[p][4], hdev->features[p][5],
- hdev->features[p][6], hdev->features[p][7]);
- }
- if (lmp_le_capable(hdev))
- seq_printf(f, "LE: 0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x "
- "0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x\n",
- hdev->le_features[0], hdev->le_features[1],
- hdev->le_features[2], hdev->le_features[3],
- hdev->le_features[4], hdev->le_features[5],
- hdev->le_features[6], hdev->le_features[7]);
- hci_dev_unlock(hdev);
- return 0;
- }
- static int features_open(struct inode *inode, struct file *file)
- {
- return single_open(file, features_show, inode->i_private);
- }
- static const struct file_operations features_fops = {
- .open = features_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
- };
- static int device_id_show(struct seq_file *f, void *ptr)
- {
- struct hci_dev *hdev = f->private;
- hci_dev_lock(hdev);
- seq_printf(f, "%4.4x:%4.4x:%4.4x:%4.4x\n", hdev->devid_source,
- hdev->devid_vendor, hdev->devid_product, hdev->devid_version);
- hci_dev_unlock(hdev);
- return 0;
- }
- static int device_id_open(struct inode *inode, struct file *file)
- {
- return single_open(file, device_id_show, inode->i_private);
- }
- static const struct file_operations device_id_fops = {
- .open = device_id_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
- };
- static int device_list_show(struct seq_file *f, void *ptr)
- {
- struct hci_dev *hdev = f->private;
- struct hci_conn_params *p;
- struct bdaddr_list *b;
- hci_dev_lock(hdev);
- list_for_each_entry(b, &hdev->whitelist, list)
- seq_printf(f, "%pMR (type %u)\n", &b->bdaddr, b->bdaddr_type);
- list_for_each_entry(p, &hdev->le_conn_params, list) {
- seq_printf(f, "%pMR (type %u) %u\n", &p->addr, p->addr_type,
- p->auto_connect);
- }
- hci_dev_unlock(hdev);
- return 0;
- }
- static int device_list_open(struct inode *inode, struct file *file)
- {
- return single_open(file, device_list_show, inode->i_private);
- }
- static const struct file_operations device_list_fops = {
- .open = device_list_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
- };
- static int blacklist_show(struct seq_file *f, void *p)
- {
- struct hci_dev *hdev = f->private;
- struct bdaddr_list *b;
- hci_dev_lock(hdev);
- list_for_each_entry(b, &hdev->blacklist, list)
- seq_printf(f, "%pMR (type %u)\n", &b->bdaddr, b->bdaddr_type);
- hci_dev_unlock(hdev);
- return 0;
- }
- static int blacklist_open(struct inode *inode, struct file *file)
- {
- return single_open(file, blacklist_show, inode->i_private);
- }
- static const struct file_operations blacklist_fops = {
- .open = blacklist_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
- };
- static int uuids_show(struct seq_file *f, void *p)
- {
- struct hci_dev *hdev = f->private;
- struct bt_uuid *uuid;
- hci_dev_lock(hdev);
- list_for_each_entry(uuid, &hdev->uuids, list) {
- u8 i, val[16];
- /* The Bluetooth UUID values are stored in big endian,
- * but with reversed byte order. So convert them into
- * the right order for the %pUb modifier.
- */
- for (i = 0; i < 16; i++)
- val[i] = uuid->uuid[15 - i];
- seq_printf(f, "%pUb\n", val);
- }
- hci_dev_unlock(hdev);
- return 0;
- }
- static int uuids_open(struct inode *inode, struct file *file)
- {
- return single_open(file, uuids_show, inode->i_private);
- }
- static const struct file_operations uuids_fops = {
- .open = uuids_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
- };
- static int remote_oob_show(struct seq_file *f, void *ptr)
- {
- struct hci_dev *hdev = f->private;
- struct oob_data *data;
- hci_dev_lock(hdev);
- list_for_each_entry(data, &hdev->remote_oob_data, list) {
- seq_printf(f, "%pMR (type %u) %u %*phN %*phN %*phN %*phN\n",
- &data->bdaddr, data->bdaddr_type, data->present,
- 16, data->hash192, 16, data->rand192,
- 16, data->hash256, 16, data->rand256);
- }
- hci_dev_unlock(hdev);
- return 0;
- }
- static int remote_oob_open(struct inode *inode, struct file *file)
- {
- return single_open(file, remote_oob_show, inode->i_private);
- }
- static const struct file_operations remote_oob_fops = {
- .open = remote_oob_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
- };
- static int conn_info_min_age_set(void *data, u64 val)
- {
- struct hci_dev *hdev = data;
- if (val == 0 || val > hdev->conn_info_max_age)
- return -EINVAL;
- hci_dev_lock(hdev);
- hdev->conn_info_min_age = val;
- hci_dev_unlock(hdev);
- return 0;
- }
- static int conn_info_min_age_get(void *data, u64 *val)
- {
- struct hci_dev *hdev = data;
- hci_dev_lock(hdev);
- *val = hdev->conn_info_min_age;
- hci_dev_unlock(hdev);
- return 0;
- }
- DEFINE_SIMPLE_ATTRIBUTE(conn_info_min_age_fops, conn_info_min_age_get,
- conn_info_min_age_set, "%llu\n");
- static int conn_info_max_age_set(void *data, u64 val)
- {
- struct hci_dev *hdev = data;
- if (val == 0 || val < hdev->conn_info_min_age)
- return -EINVAL;
- hci_dev_lock(hdev);
- hdev->conn_info_max_age = val;
- hci_dev_unlock(hdev);
- return 0;
- }
- static int conn_info_max_age_get(void *data, u64 *val)
- {
- struct hci_dev *hdev = data;
- hci_dev_lock(hdev);
- *val = hdev->conn_info_max_age;
- hci_dev_unlock(hdev);
- return 0;
- }
- DEFINE_SIMPLE_ATTRIBUTE(conn_info_max_age_fops, conn_info_max_age_get,
- conn_info_max_age_set, "%llu\n");
- static ssize_t use_debug_keys_read(struct file *file, char __user *user_buf,
- size_t count, loff_t *ppos)
- {
- struct hci_dev *hdev = file->private_data;
- char buf[3];
- buf[0] = hci_dev_test_flag(hdev, HCI_USE_DEBUG_KEYS) ? 'Y': 'N';
- buf[1] = '\n';
- buf[2] = '\0';
- return simple_read_from_buffer(user_buf, count, ppos, buf, 2);
- }
- static const struct file_operations use_debug_keys_fops = {
- .open = simple_open,
- .read = use_debug_keys_read,
- .llseek = default_llseek,
- };
- static ssize_t sc_only_mode_read(struct file *file, char __user *user_buf,
- size_t count, loff_t *ppos)
- {
- struct hci_dev *hdev = file->private_data;
- char buf[3];
- buf[0] = hci_dev_test_flag(hdev, HCI_SC_ONLY) ? 'Y': 'N';
- buf[1] = '\n';
- buf[2] = '\0';
- return simple_read_from_buffer(user_buf, count, ppos, buf, 2);
- }
- static const struct file_operations sc_only_mode_fops = {
- .open = simple_open,
- .read = sc_only_mode_read,
- .llseek = default_llseek,
- };
- DEFINE_INFO_ATTRIBUTE(hardware_info, hw_info);
- DEFINE_INFO_ATTRIBUTE(firmware_info, fw_info);
- void hci_debugfs_create_common(struct hci_dev *hdev)
- {
- debugfs_create_file("features", 0444, hdev->debugfs, hdev,
- &features_fops);
- debugfs_create_u16("manufacturer", 0444, hdev->debugfs,
- &hdev->manufacturer);
- debugfs_create_u8("hci_version", 0444, hdev->debugfs, &hdev->hci_ver);
- debugfs_create_u16("hci_revision", 0444, hdev->debugfs, &hdev->hci_rev);
- debugfs_create_u8("hardware_error", 0444, hdev->debugfs,
- &hdev->hw_error_code);
- debugfs_create_file("device_id", 0444, hdev->debugfs, hdev,
- &device_id_fops);
- debugfs_create_file("device_list", 0444, hdev->debugfs, hdev,
- &device_list_fops);
- debugfs_create_file("blacklist", 0444, hdev->debugfs, hdev,
- &blacklist_fops);
- debugfs_create_file("uuids", 0444, hdev->debugfs, hdev, &uuids_fops);
- debugfs_create_file("remote_oob", 0400, hdev->debugfs, hdev,
- &remote_oob_fops);
- debugfs_create_file("conn_info_min_age", 0644, hdev->debugfs, hdev,
- &conn_info_min_age_fops);
- debugfs_create_file("conn_info_max_age", 0644, hdev->debugfs, hdev,
- &conn_info_max_age_fops);
- if (lmp_ssp_capable(hdev) || lmp_le_capable(hdev))
- debugfs_create_file("use_debug_keys", 0444, hdev->debugfs,
- hdev, &use_debug_keys_fops);
- if (lmp_sc_capable(hdev) || lmp_le_capable(hdev))
- debugfs_create_file("sc_only_mode", 0444, hdev->debugfs,
- hdev, &sc_only_mode_fops);
- if (hdev->hw_info)
- debugfs_create_file("hardware_info", 0444, hdev->debugfs,
- hdev, &hardware_info_fops);
- if (hdev->fw_info)
- debugfs_create_file("firmware_info", 0444, hdev->debugfs,
- hdev, &firmware_info_fops);
- }
- static int inquiry_cache_show(struct seq_file *f, void *p)
- {
- struct hci_dev *hdev = f->private;
- struct discovery_state *cache = &hdev->discovery;
- struct inquiry_entry *e;
- hci_dev_lock(hdev);
- list_for_each_entry(e, &cache->all, all) {
- struct inquiry_data *data = &e->data;
- seq_printf(f, "%pMR %d %d %d 0x%.2x%.2x%.2x 0x%.4x %d %d %u\n",
- &data->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(hdev);
- return 0;
- }
- static int inquiry_cache_open(struct inode *inode, struct file *file)
- {
- return single_open(file, inquiry_cache_show, inode->i_private);
- }
- static const struct file_operations inquiry_cache_fops = {
- .open = inquiry_cache_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
- };
- static int link_keys_show(struct seq_file *f, void *ptr)
- {
- struct hci_dev *hdev = f->private;
- struct link_key *key;
- rcu_read_lock();
- list_for_each_entry_rcu(key, &hdev->link_keys, list)
- seq_printf(f, "%pMR %u %*phN %u\n", &key->bdaddr, key->type,
- HCI_LINK_KEY_SIZE, key->val, key->pin_len);
- rcu_read_unlock();
- return 0;
- }
- static int link_keys_open(struct inode *inode, struct file *file)
- {
- return single_open(file, link_keys_show, inode->i_private);
- }
- static const struct file_operations link_keys_fops = {
- .open = link_keys_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
- };
- static int dev_class_show(struct seq_file *f, void *ptr)
- {
- struct hci_dev *hdev = f->private;
- hci_dev_lock(hdev);
- seq_printf(f, "0x%.2x%.2x%.2x\n", hdev->dev_class[2],
- hdev->dev_class[1], hdev->dev_class[0]);
- hci_dev_unlock(hdev);
- return 0;
- }
- static int dev_class_open(struct inode *inode, struct file *file)
- {
- return single_open(file, dev_class_show, inode->i_private);
- }
- static const struct file_operations dev_class_fops = {
- .open = dev_class_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
- };
- static int voice_setting_get(void *data, u64 *val)
- {
- struct hci_dev *hdev = data;
- hci_dev_lock(hdev);
- *val = hdev->voice_setting;
- hci_dev_unlock(hdev);
- return 0;
- }
- DEFINE_SIMPLE_ATTRIBUTE(voice_setting_fops, voice_setting_get,
- NULL, "0x%4.4llx\n");
- static ssize_t ssp_debug_mode_read(struct file *file, char __user *user_buf,
- size_t count, loff_t *ppos)
- {
- struct hci_dev *hdev = file->private_data;
- char buf[3];
- buf[0] = hdev->ssp_debug_mode ? 'Y': 'N';
- buf[1] = '\n';
- buf[2] = '\0';
- return simple_read_from_buffer(user_buf, count, ppos, buf, 2);
- }
- static const struct file_operations ssp_debug_mode_fops = {
- .open = simple_open,
- .read = ssp_debug_mode_read,
- .llseek = default_llseek,
- };
- static int auto_accept_delay_set(void *data, u64 val)
- {
- struct hci_dev *hdev = data;
- hci_dev_lock(hdev);
- hdev->auto_accept_delay = val;
- hci_dev_unlock(hdev);
- return 0;
- }
- static int auto_accept_delay_get(void *data, u64 *val)
- {
- struct hci_dev *hdev = data;
- hci_dev_lock(hdev);
- *val = hdev->auto_accept_delay;
- hci_dev_unlock(hdev);
- return 0;
- }
- DEFINE_SIMPLE_ATTRIBUTE(auto_accept_delay_fops, auto_accept_delay_get,
- auto_accept_delay_set, "%llu\n");
- static int idle_timeout_set(void *data, u64 val)
- {
- struct hci_dev *hdev = data;
- if (val != 0 && (val < 500 || val > 3600000))
- return -EINVAL;
- hci_dev_lock(hdev);
- hdev->idle_timeout = val;
- hci_dev_unlock(hdev);
- return 0;
- }
- static int idle_timeout_get(void *data, u64 *val)
- {
- struct hci_dev *hdev = data;
- hci_dev_lock(hdev);
- *val = hdev->idle_timeout;
- hci_dev_unlock(hdev);
- return 0;
- }
- DEFINE_SIMPLE_ATTRIBUTE(idle_timeout_fops, idle_timeout_get,
- idle_timeout_set, "%llu\n");
- static int sniff_min_interval_set(void *data, u64 val)
- {
- struct hci_dev *hdev = data;
- if (val == 0 || val % 2 || val > hdev->sniff_max_interval)
- return -EINVAL;
- hci_dev_lock(hdev);
- hdev->sniff_min_interval = val;
- hci_dev_unlock(hdev);
- return 0;
- }
- static int sniff_min_interval_get(void *data, u64 *val)
- {
- struct hci_dev *hdev = data;
- hci_dev_lock(hdev);
- *val = hdev->sniff_min_interval;
- hci_dev_unlock(hdev);
- return 0;
- }
- DEFINE_SIMPLE_ATTRIBUTE(sniff_min_interval_fops, sniff_min_interval_get,
- sniff_min_interval_set, "%llu\n");
- static int sniff_max_interval_set(void *data, u64 val)
- {
- struct hci_dev *hdev = data;
- if (val == 0 || val % 2 || val < hdev->sniff_min_interval)
- return -EINVAL;
- hci_dev_lock(hdev);
- hdev->sniff_max_interval = val;
- hci_dev_unlock(hdev);
- return 0;
- }
- static int sniff_max_interval_get(void *data, u64 *val)
- {
- struct hci_dev *hdev = data;
- hci_dev_lock(hdev);
- *val = hdev->sniff_max_interval;
- hci_dev_unlock(hdev);
- return 0;
- }
- DEFINE_SIMPLE_ATTRIBUTE(sniff_max_interval_fops, sniff_max_interval_get,
- sniff_max_interval_set, "%llu\n");
- void hci_debugfs_create_bredr(struct hci_dev *hdev)
- {
- debugfs_create_file("inquiry_cache", 0444, hdev->debugfs, hdev,
- &inquiry_cache_fops);
- debugfs_create_file("link_keys", 0400, hdev->debugfs, hdev,
- &link_keys_fops);
- debugfs_create_file("dev_class", 0444, hdev->debugfs, hdev,
- &dev_class_fops);
- debugfs_create_file("voice_setting", 0444, hdev->debugfs, hdev,
- &voice_setting_fops);
- if (lmp_ssp_capable(hdev)) {
- debugfs_create_file("ssp_debug_mode", 0444, hdev->debugfs,
- hdev, &ssp_debug_mode_fops);
- debugfs_create_file("auto_accept_delay", 0644, hdev->debugfs,
- hdev, &auto_accept_delay_fops);
- }
- if (lmp_sniff_capable(hdev)) {
- debugfs_create_file("idle_timeout", 0644, hdev->debugfs,
- hdev, &idle_timeout_fops);
- debugfs_create_file("sniff_min_interval", 0644, hdev->debugfs,
- hdev, &sniff_min_interval_fops);
- debugfs_create_file("sniff_max_interval", 0644, hdev->debugfs,
- hdev, &sniff_max_interval_fops);
- }
- }
- static int identity_show(struct seq_file *f, void *p)
- {
- struct hci_dev *hdev = f->private;
- bdaddr_t addr;
- u8 addr_type;
- hci_dev_lock(hdev);
- hci_copy_identity_address(hdev, &addr, &addr_type);
- seq_printf(f, "%pMR (type %u) %*phN %pMR\n", &addr, addr_type,
- 16, hdev->irk, &hdev->rpa);
- hci_dev_unlock(hdev);
- return 0;
- }
- static int identity_open(struct inode *inode, struct file *file)
- {
- return single_open(file, identity_show, inode->i_private);
- }
- static const struct file_operations identity_fops = {
- .open = identity_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
- };
- static int rpa_timeout_set(void *data, u64 val)
- {
- struct hci_dev *hdev = data;
- /* Require the RPA timeout to be at least 30 seconds and at most
- * 24 hours.
- */
- if (val < 30 || val > (60 * 60 * 24))
- return -EINVAL;
- hci_dev_lock(hdev);
- hdev->rpa_timeout = val;
- hci_dev_unlock(hdev);
- return 0;
- }
- static int rpa_timeout_get(void *data, u64 *val)
- {
- struct hci_dev *hdev = data;
- hci_dev_lock(hdev);
- *val = hdev->rpa_timeout;
- hci_dev_unlock(hdev);
- return 0;
- }
- DEFINE_SIMPLE_ATTRIBUTE(rpa_timeout_fops, rpa_timeout_get,
- rpa_timeout_set, "%llu\n");
- static int random_address_show(struct seq_file *f, void *p)
- {
- struct hci_dev *hdev = f->private;
- hci_dev_lock(hdev);
- seq_printf(f, "%pMR\n", &hdev->random_addr);
- hci_dev_unlock(hdev);
- return 0;
- }
- static int random_address_open(struct inode *inode, struct file *file)
- {
- return single_open(file, random_address_show, inode->i_private);
- }
- static const struct file_operations random_address_fops = {
- .open = random_address_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
- };
- static int static_address_show(struct seq_file *f, void *p)
- {
- struct hci_dev *hdev = f->private;
- hci_dev_lock(hdev);
- seq_printf(f, "%pMR\n", &hdev->static_addr);
- hci_dev_unlock(hdev);
- return 0;
- }
- static int static_address_open(struct inode *inode, struct file *file)
- {
- return single_open(file, static_address_show, inode->i_private);
- }
- static const struct file_operations static_address_fops = {
- .open = static_address_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
- };
- static ssize_t force_static_address_read(struct file *file,
- char __user *user_buf,
- size_t count, loff_t *ppos)
- {
- struct hci_dev *hdev = file->private_data;
- char buf[3];
- buf[0] = hci_dev_test_flag(hdev, HCI_FORCE_STATIC_ADDR) ? 'Y': 'N';
- buf[1] = '\n';
- buf[2] = '\0';
- return simple_read_from_buffer(user_buf, count, ppos, buf, 2);
- }
- static ssize_t force_static_address_write(struct file *file,
- const char __user *user_buf,
- size_t count, loff_t *ppos)
- {
- struct hci_dev *hdev = file->private_data;
- char buf[32];
- size_t buf_size = min(count, (sizeof(buf)-1));
- bool enable;
- if (test_bit(HCI_UP, &hdev->flags))
- return -EBUSY;
- if (copy_from_user(buf, user_buf, buf_size))
- return -EFAULT;
- buf[buf_size] = '\0';
- if (strtobool(buf, &enable))
- return -EINVAL;
- if (enable == hci_dev_test_flag(hdev, HCI_FORCE_STATIC_ADDR))
- return -EALREADY;
- hci_dev_change_flag(hdev, HCI_FORCE_STATIC_ADDR);
- return count;
- }
- static const struct file_operations force_static_address_fops = {
- .open = simple_open,
- .read = force_static_address_read,
- .write = force_static_address_write,
- .llseek = default_llseek,
- };
- static int white_list_show(struct seq_file *f, void *ptr)
- {
- struct hci_dev *hdev = f->private;
- struct bdaddr_list *b;
- hci_dev_lock(hdev);
- list_for_each_entry(b, &hdev->le_white_list, list)
- seq_printf(f, "%pMR (type %u)\n", &b->bdaddr, b->bdaddr_type);
- hci_dev_unlock(hdev);
- return 0;
- }
- static int white_list_open(struct inode *inode, struct file *file)
- {
- return single_open(file, white_list_show, inode->i_private);
- }
- static const struct file_operations white_list_fops = {
- .open = white_list_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
- };
- static int identity_resolving_keys_show(struct seq_file *f, void *ptr)
- {
- struct hci_dev *hdev = f->private;
- struct smp_irk *irk;
- rcu_read_lock();
- list_for_each_entry_rcu(irk, &hdev->identity_resolving_keys, list) {
- seq_printf(f, "%pMR (type %u) %*phN %pMR\n",
- &irk->bdaddr, irk->addr_type,
- 16, irk->val, &irk->rpa);
- }
- rcu_read_unlock();
- return 0;
- }
- static int identity_resolving_keys_open(struct inode *inode, struct file *file)
- {
- return single_open(file, identity_resolving_keys_show,
- inode->i_private);
- }
- static const struct file_operations identity_resolving_keys_fops = {
- .open = identity_resolving_keys_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
- };
- static int long_term_keys_show(struct seq_file *f, void *ptr)
- {
- struct hci_dev *hdev = f->private;
- struct smp_ltk *ltk;
- rcu_read_lock();
- list_for_each_entry_rcu(ltk, &hdev->long_term_keys, list)
- seq_printf(f, "%pMR (type %u) %u 0x%02x %u %.4x %.16llx %*phN\n",
- <k->bdaddr, ltk->bdaddr_type, ltk->authenticated,
- ltk->type, ltk->enc_size, __le16_to_cpu(ltk->ediv),
- __le64_to_cpu(ltk->rand), 16, ltk->val);
- rcu_read_unlock();
- return 0;
- }
- static int long_term_keys_open(struct inode *inode, struct file *file)
- {
- return single_open(file, long_term_keys_show, inode->i_private);
- }
- static const struct file_operations long_term_keys_fops = {
- .open = long_term_keys_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
- };
- static int conn_min_interval_set(void *data, u64 val)
- {
- struct hci_dev *hdev = data;
- if (val < 0x0006 || val > 0x0c80 || val > hdev->le_conn_max_interval)
- return -EINVAL;
- hci_dev_lock(hdev);
- hdev->le_conn_min_interval = val;
- hci_dev_unlock(hdev);
- return 0;
- }
- static int conn_min_interval_get(void *data, u64 *val)
- {
- struct hci_dev *hdev = data;
- hci_dev_lock(hdev);
- *val = hdev->le_conn_min_interval;
- hci_dev_unlock(hdev);
- return 0;
- }
- DEFINE_SIMPLE_ATTRIBUTE(conn_min_interval_fops, conn_min_interval_get,
- conn_min_interval_set, "%llu\n");
- static int conn_max_interval_set(void *data, u64 val)
- {
- struct hci_dev *hdev = data;
- if (val < 0x0006 || val > 0x0c80 || val < hdev->le_conn_min_interval)
- return -EINVAL;
- hci_dev_lock(hdev);
- hdev->le_conn_max_interval = val;
- hci_dev_unlock(hdev);
- return 0;
- }
- static int conn_max_interval_get(void *data, u64 *val)
- {
- struct hci_dev *hdev = data;
- hci_dev_lock(hdev);
- *val = hdev->le_conn_max_interval;
- hci_dev_unlock(hdev);
- return 0;
- }
- DEFINE_SIMPLE_ATTRIBUTE(conn_max_interval_fops, conn_max_interval_get,
- conn_max_interval_set, "%llu\n");
- static int conn_latency_set(void *data, u64 val)
- {
- struct hci_dev *hdev = data;
- if (val > 0x01f3)
- return -EINVAL;
- hci_dev_lock(hdev);
- hdev->le_conn_latency = val;
- hci_dev_unlock(hdev);
- return 0;
- }
- static int conn_latency_get(void *data, u64 *val)
- {
- struct hci_dev *hdev = data;
- hci_dev_lock(hdev);
- *val = hdev->le_conn_latency;
- hci_dev_unlock(hdev);
- return 0;
- }
- DEFINE_SIMPLE_ATTRIBUTE(conn_latency_fops, conn_latency_get,
- conn_latency_set, "%llu\n");
- static int supervision_timeout_set(void *data, u64 val)
- {
- struct hci_dev *hdev = data;
- if (val < 0x000a || val > 0x0c80)
- return -EINVAL;
- hci_dev_lock(hdev);
- hdev->le_supv_timeout = val;
- hci_dev_unlock(hdev);
- return 0;
- }
- static int supervision_timeout_get(void *data, u64 *val)
- {
- struct hci_dev *hdev = data;
- hci_dev_lock(hdev);
- *val = hdev->le_supv_timeout;
- hci_dev_unlock(hdev);
- return 0;
- }
- DEFINE_SIMPLE_ATTRIBUTE(supervision_timeout_fops, supervision_timeout_get,
- supervision_timeout_set, "%llu\n");
- static int adv_channel_map_set(void *data, u64 val)
- {
- struct hci_dev *hdev = data;
- if (val < 0x01 || val > 0x07)
- return -EINVAL;
- hci_dev_lock(hdev);
- hdev->le_adv_channel_map = val;
- hci_dev_unlock(hdev);
- return 0;
- }
- static int adv_channel_map_get(void *data, u64 *val)
- {
- struct hci_dev *hdev = data;
- hci_dev_lock(hdev);
- *val = hdev->le_adv_channel_map;
- hci_dev_unlock(hdev);
- return 0;
- }
- DEFINE_SIMPLE_ATTRIBUTE(adv_channel_map_fops, adv_channel_map_get,
- adv_channel_map_set, "%llu\n");
- static int adv_min_interval_set(void *data, u64 val)
- {
- struct hci_dev *hdev = data;
- if (val < 0x0020 || val > 0x4000 || val > hdev->le_adv_max_interval)
- return -EINVAL;
- hci_dev_lock(hdev);
- hdev->le_adv_min_interval = val;
- hci_dev_unlock(hdev);
- return 0;
- }
- static int adv_min_interval_get(void *data, u64 *val)
- {
- struct hci_dev *hdev = data;
- hci_dev_lock(hdev);
- *val = hdev->le_adv_min_interval;
- hci_dev_unlock(hdev);
- return 0;
- }
- DEFINE_SIMPLE_ATTRIBUTE(adv_min_interval_fops, adv_min_interval_get,
- adv_min_interval_set, "%llu\n");
- static int adv_max_interval_set(void *data, u64 val)
- {
- struct hci_dev *hdev = data;
- if (val < 0x0020 || val > 0x4000 || val < hdev->le_adv_min_interval)
- return -EINVAL;
- hci_dev_lock(hdev);
- hdev->le_adv_max_interval = val;
- hci_dev_unlock(hdev);
- return 0;
- }
- static int adv_max_interval_get(void *data, u64 *val)
- {
- struct hci_dev *hdev = data;
- hci_dev_lock(hdev);
- *val = hdev->le_adv_max_interval;
- hci_dev_unlock(hdev);
- return 0;
- }
- DEFINE_SIMPLE_ATTRIBUTE(adv_max_interval_fops, adv_max_interval_get,
- adv_max_interval_set, "%llu\n");
- DEFINE_QUIRK_ATTRIBUTE(quirk_strict_duplicate_filter,
- HCI_QUIRK_STRICT_DUPLICATE_FILTER);
- DEFINE_QUIRK_ATTRIBUTE(quirk_simultaneous_discovery,
- HCI_QUIRK_SIMULTANEOUS_DISCOVERY);
- void hci_debugfs_create_le(struct hci_dev *hdev)
- {
- debugfs_create_file("identity", 0400, hdev->debugfs, hdev,
- &identity_fops);
- debugfs_create_file("rpa_timeout", 0644, hdev->debugfs, hdev,
- &rpa_timeout_fops);
- debugfs_create_file("random_address", 0444, hdev->debugfs, hdev,
- &random_address_fops);
- debugfs_create_file("static_address", 0444, hdev->debugfs, hdev,
- &static_address_fops);
- /* For controllers with a public address, provide a debug
- * option to force the usage of the configured static
- * address. By default the public address is used.
- */
- if (bacmp(&hdev->bdaddr, BDADDR_ANY))
- debugfs_create_file("force_static_address", 0644,
- hdev->debugfs, hdev,
- &force_static_address_fops);
- debugfs_create_u8("white_list_size", 0444, hdev->debugfs,
- &hdev->le_white_list_size);
- debugfs_create_file("white_list", 0444, hdev->debugfs, hdev,
- &white_list_fops);
- debugfs_create_file("identity_resolving_keys", 0400, hdev->debugfs,
- hdev, &identity_resolving_keys_fops);
- debugfs_create_file("long_term_keys", 0400, hdev->debugfs, hdev,
- &long_term_keys_fops);
- debugfs_create_file("conn_min_interval", 0644, hdev->debugfs, hdev,
- &conn_min_interval_fops);
- debugfs_create_file("conn_max_interval", 0644, hdev->debugfs, hdev,
- &conn_max_interval_fops);
- debugfs_create_file("conn_latency", 0644, hdev->debugfs, hdev,
- &conn_latency_fops);
- debugfs_create_file("supervision_timeout", 0644, hdev->debugfs, hdev,
- &supervision_timeout_fops);
- debugfs_create_file("adv_channel_map", 0644, hdev->debugfs, hdev,
- &adv_channel_map_fops);
- debugfs_create_file("adv_min_interval", 0644, hdev->debugfs, hdev,
- &adv_min_interval_fops);
- debugfs_create_file("adv_max_interval", 0644, hdev->debugfs, hdev,
- &adv_max_interval_fops);
- debugfs_create_u16("discov_interleaved_timeout", 0644, hdev->debugfs,
- &hdev->discov_interleaved_timeout);
- debugfs_create_file("quirk_strict_duplicate_filter", 0644,
- hdev->debugfs, hdev,
- &quirk_strict_duplicate_filter_fops);
- debugfs_create_file("quirk_simultaneous_discovery", 0644,
- hdev->debugfs, hdev,
- &quirk_simultaneous_discovery_fops);
- }
- void hci_debugfs_create_conn(struct hci_conn *conn)
- {
- struct hci_dev *hdev = conn->hdev;
- char name[6];
- if (IS_ERR_OR_NULL(hdev->debugfs))
- return;
- snprintf(name, sizeof(name), "%u", conn->handle);
- conn->debugfs = debugfs_create_dir(name, hdev->debugfs);
- }
|