Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* Bluetooth HCI driver model support. */ |
2 | ||
3a9a231d | 3 | #include <linux/module.h> |
1da177e4 LT |
4 | |
5 | #include <net/bluetooth/bluetooth.h> | |
6 | #include <net/bluetooth/hci_core.h> | |
7 | ||
aef7d97c | 8 | static struct class *bt_class; |
90855d7b | 9 | |
90855d7b MH |
10 | static inline char *link_typetostr(int type) |
11 | { | |
12 | switch (type) { | |
13 | case ACL_LINK: | |
14 | return "ACL"; | |
15 | case SCO_LINK: | |
16 | return "SCO"; | |
17 | case ESCO_LINK: | |
18 | return "eSCO"; | |
21061df3 PH |
19 | case LE_LINK: |
20 | return "LE"; | |
90855d7b MH |
21 | default: |
22 | return "UNKNOWN"; | |
23 | } | |
24 | } | |
25 | ||
b80f021f GP |
26 | static ssize_t show_link_type(struct device *dev, |
27 | struct device_attribute *attr, char *buf) | |
90855d7b | 28 | { |
3dc07322 | 29 | struct hci_conn *conn = to_hci_conn(dev); |
90855d7b MH |
30 | return sprintf(buf, "%s\n", link_typetostr(conn->type)); |
31 | } | |
32 | ||
b80f021f GP |
33 | static ssize_t show_link_address(struct device *dev, |
34 | struct device_attribute *attr, char *buf) | |
90855d7b | 35 | { |
3dc07322 | 36 | struct hci_conn *conn = to_hci_conn(dev); |
fcb73338 | 37 | return sprintf(buf, "%pMR\n", &conn->dst); |
90855d7b MH |
38 | } |
39 | ||
602f9887 GP |
40 | #define LINK_ATTR(_name, _mode, _show, _store) \ |
41 | struct device_attribute link_attr_##_name = __ATTR(_name, _mode, _show, _store) | |
90855d7b MH |
42 | |
43 | static LINK_ATTR(type, S_IRUGO, show_link_type, NULL); | |
44 | static LINK_ATTR(address, S_IRUGO, show_link_address, NULL); | |
90855d7b MH |
45 | |
46 | static struct attribute *bt_link_attrs[] = { | |
47 | &link_attr_type.attr, | |
48 | &link_attr_address.attr, | |
90855d7b MH |
49 | NULL |
50 | }; | |
51 | ||
52 | static struct attribute_group bt_link_group = { | |
53 | .attrs = bt_link_attrs, | |
54 | }; | |
55 | ||
a4dbd674 | 56 | static const struct attribute_group *bt_link_groups[] = { |
90855d7b MH |
57 | &bt_link_group, |
58 | NULL | |
59 | }; | |
60 | ||
61 | static void bt_link_release(struct device *dev) | |
62 | { | |
2dd10688 DH |
63 | struct hci_conn *conn = to_hci_conn(dev); |
64 | kfree(conn); | |
90855d7b MH |
65 | } |
66 | ||
67 | static struct device_type bt_link = { | |
68 | .name = "link", | |
69 | .groups = bt_link_groups, | |
70 | .release = bt_link_release, | |
71 | }; | |
72 | ||
6d438e33 GP |
73 | /* |
74 | * The rfcomm tty device will possibly retain even when conn | |
75 | * is down, and sysfs doesn't support move zombie device, | |
76 | * so we should move the device before conn device is destroyed. | |
77 | */ | |
78 | static int __match_tty(struct device *dev, void *data) | |
79 | { | |
80 | return !strncmp(dev_name(dev), "rfcomm", 6); | |
81 | } | |
82 | ||
83 | void hci_conn_init_sysfs(struct hci_conn *conn) | |
84 | { | |
85 | struct hci_dev *hdev = conn->hdev; | |
86 | ||
87 | BT_DBG("conn %p", conn); | |
88 | ||
89 | conn->dev.type = &bt_link; | |
90 | conn->dev.class = bt_class; | |
91 | conn->dev.parent = &hdev->dev; | |
92 | ||
93 | device_initialize(&conn->dev); | |
94 | } | |
95 | ||
96 | void hci_conn_add_sysfs(struct hci_conn *conn) | |
90855d7b | 97 | { |
457ca7bb | 98 | struct hci_dev *hdev = conn->hdev; |
90855d7b | 99 | |
6d438e33 GP |
100 | BT_DBG("conn %p", conn); |
101 | ||
457ca7bb MH |
102 | dev_set_name(&conn->dev, "%s:%d", hdev->name, conn->handle); |
103 | ||
90855d7b MH |
104 | if (device_add(&conn->dev) < 0) { |
105 | BT_ERR("Failed to register connection device"); | |
106 | return; | |
107 | } | |
384943ec MH |
108 | |
109 | hci_dev_hold(hdev); | |
90855d7b MH |
110 | } |
111 | ||
6d438e33 | 112 | void hci_conn_del_sysfs(struct hci_conn *conn) |
90855d7b | 113 | { |
90855d7b MH |
114 | struct hci_dev *hdev = conn->hdev; |
115 | ||
a67e899c MH |
116 | if (!device_is_registered(&conn->dev)) |
117 | return; | |
f3784d83 | 118 | |
90855d7b MH |
119 | while (1) { |
120 | struct device *dev; | |
121 | ||
122 | dev = device_find_child(&conn->dev, NULL, __match_tty); | |
123 | if (!dev) | |
124 | break; | |
ffa6a705 | 125 | device_move(dev, NULL, DPM_ORDER_DEV_LAST); |
90855d7b MH |
126 | put_device(dev); |
127 | } | |
128 | ||
129 | device_del(&conn->dev); | |
384943ec | 130 | |
90855d7b MH |
131 | hci_dev_put(hdev); |
132 | } | |
133 | ||
943da25d MH |
134 | static inline char *host_typetostr(int type) |
135 | { | |
136 | switch (type) { | |
137 | case HCI_BREDR: | |
138 | return "BR/EDR"; | |
8f1e1742 DV |
139 | case HCI_AMP: |
140 | return "AMP"; | |
943da25d MH |
141 | default: |
142 | return "UNKNOWN"; | |
143 | } | |
144 | } | |
145 | ||
b80f021f GP |
146 | static ssize_t show_type(struct device *dev, |
147 | struct device_attribute *attr, char *buf) | |
943da25d | 148 | { |
aa2b86d7 | 149 | struct hci_dev *hdev = to_hci_dev(dev); |
943da25d MH |
150 | return sprintf(buf, "%s\n", host_typetostr(hdev->dev_type)); |
151 | } | |
152 | ||
b80f021f GP |
153 | static ssize_t show_name(struct device *dev, |
154 | struct device_attribute *attr, char *buf) | |
a9de9248 | 155 | { |
aa2b86d7 | 156 | struct hci_dev *hdev = to_hci_dev(dev); |
1f6c6378 | 157 | char name[HCI_MAX_NAME_LENGTH + 1]; |
a9de9248 MH |
158 | int i; |
159 | ||
1f6c6378 | 160 | for (i = 0; i < HCI_MAX_NAME_LENGTH; i++) |
a9de9248 MH |
161 | name[i] = hdev->dev_name[i]; |
162 | ||
1f6c6378 | 163 | name[HCI_MAX_NAME_LENGTH] = '\0'; |
a9de9248 MH |
164 | return sprintf(buf, "%s\n", name); |
165 | } | |
166 | ||
b80f021f GP |
167 | static ssize_t show_address(struct device *dev, |
168 | struct device_attribute *attr, char *buf) | |
1da177e4 | 169 | { |
aa2b86d7 | 170 | struct hci_dev *hdev = to_hci_dev(dev); |
fcb73338 | 171 | return sprintf(buf, "%pMR\n", &hdev->bdaddr); |
1da177e4 LT |
172 | } |
173 | ||
943da25d | 174 | static DEVICE_ATTR(type, S_IRUGO, show_type, NULL); |
a9de9248 | 175 | static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); |
a91f2e39 | 176 | static DEVICE_ATTR(address, S_IRUGO, show_address, NULL); |
1da177e4 | 177 | |
90855d7b | 178 | static struct attribute *bt_host_attrs[] = { |
943da25d | 179 | &dev_attr_type.attr, |
90855d7b | 180 | &dev_attr_name.attr, |
90855d7b | 181 | &dev_attr_address.attr, |
1da177e4 LT |
182 | NULL |
183 | }; | |
184 | ||
90855d7b MH |
185 | static struct attribute_group bt_host_group = { |
186 | .attrs = bt_host_attrs, | |
b219e3ac MH |
187 | }; |
188 | ||
a4dbd674 | 189 | static const struct attribute_group *bt_host_groups[] = { |
90855d7b MH |
190 | &bt_host_group, |
191 | NULL | |
27d35284 MH |
192 | }; |
193 | ||
90855d7b | 194 | static void bt_host_release(struct device *dev) |
a91f2e39 | 195 | { |
2dd10688 DH |
196 | struct hci_dev *hdev = to_hci_dev(dev); |
197 | kfree(hdev); | |
46e06531 | 198 | module_put(THIS_MODULE); |
b219e3ac MH |
199 | } |
200 | ||
90855d7b MH |
201 | static struct device_type bt_host = { |
202 | .name = "host", | |
203 | .groups = bt_host_groups, | |
204 | .release = bt_host_release, | |
205 | }; | |
a91f2e39 | 206 | |
0ac7e700 DH |
207 | void hci_init_sysfs(struct hci_dev *hdev) |
208 | { | |
209 | struct device *dev = &hdev->dev; | |
210 | ||
211 | dev->type = &bt_host; | |
212 | dev->class = bt_class; | |
213 | ||
46e06531 | 214 | __module_get(THIS_MODULE); |
0ac7e700 DH |
215 | device_initialize(dev); |
216 | } | |
217 | ||
1da177e4 LT |
218 | int __init bt_sysfs_init(void) |
219 | { | |
a91f2e39 | 220 | bt_class = class_create(THIS_MODULE, "bluetooth"); |
27d35284 | 221 | |
8c6ffba0 | 222 | return PTR_ERR_OR_ZERO(bt_class); |
1da177e4 LT |
223 | } |
224 | ||
860e13b5 | 225 | void bt_sysfs_cleanup(void) |
1da177e4 | 226 | { |
a91f2e39 | 227 | class_destroy(bt_class); |
1da177e4 | 228 | } |