[Bluetooth] Integrate low-level connections into the driver model
This patch integrates the low-level connections (ACL and SCO) into the driver model. Every connection is presented as device with the parent set to its host controller device. Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
This commit is contained in:
parent
4d0eb0049c
commit
b219e3ac66
|
@ -165,6 +165,10 @@ struct hci_conn {
|
||||||
struct timer_list disc_timer;
|
struct timer_list disc_timer;
|
||||||
struct timer_list idle_timer;
|
struct timer_list idle_timer;
|
||||||
|
|
||||||
|
struct work_struct work;
|
||||||
|
|
||||||
|
struct device dev;
|
||||||
|
|
||||||
struct hci_dev *hdev;
|
struct hci_dev *hdev;
|
||||||
void *l2cap_data;
|
void *l2cap_data;
|
||||||
void *sco_data;
|
void *sco_data;
|
||||||
|
@ -412,6 +416,8 @@ static inline int hci_recv_frame(struct sk_buff *skb)
|
||||||
|
|
||||||
int hci_register_sysfs(struct hci_dev *hdev);
|
int hci_register_sysfs(struct hci_dev *hdev);
|
||||||
void hci_unregister_sysfs(struct hci_dev *hdev);
|
void hci_unregister_sysfs(struct hci_dev *hdev);
|
||||||
|
void hci_conn_add_sysfs(struct hci_conn *conn);
|
||||||
|
void hci_conn_del_sysfs(struct hci_conn *conn);
|
||||||
|
|
||||||
#define SET_HCIDEV_DEV(hdev, pdev) ((hdev)->parent = (pdev))
|
#define SET_HCIDEV_DEV(hdev, pdev) ((hdev)->parent = (pdev))
|
||||||
|
|
||||||
|
|
|
@ -179,6 +179,8 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst)
|
||||||
if (hdev->notify)
|
if (hdev->notify)
|
||||||
hdev->notify(hdev, HCI_NOTIFY_CONN_ADD);
|
hdev->notify(hdev, HCI_NOTIFY_CONN_ADD);
|
||||||
|
|
||||||
|
hci_conn_add_sysfs(conn);
|
||||||
|
|
||||||
tasklet_enable(&hdev->tx_task);
|
tasklet_enable(&hdev->tx_task);
|
||||||
|
|
||||||
return conn;
|
return conn;
|
||||||
|
@ -211,6 +213,8 @@ int hci_conn_del(struct hci_conn *conn)
|
||||||
|
|
||||||
tasklet_disable(&hdev->tx_task);
|
tasklet_disable(&hdev->tx_task);
|
||||||
|
|
||||||
|
hci_conn_del_sysfs(conn);
|
||||||
|
|
||||||
hci_conn_hash_del(hdev, conn);
|
hci_conn_hash_del(hdev, conn);
|
||||||
if (hdev->notify)
|
if (hdev->notify)
|
||||||
hdev->notify(hdev, HCI_NOTIFY_CONN_DEL);
|
hdev->notify(hdev, HCI_NOTIFY_CONN_DEL);
|
||||||
|
@ -221,7 +225,9 @@ int hci_conn_del(struct hci_conn *conn)
|
||||||
|
|
||||||
hci_dev_put(hdev);
|
hci_dev_put(hdev);
|
||||||
|
|
||||||
kfree(conn);
|
/* will free via device release */
|
||||||
|
put_device(&conn->dev);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -170,6 +170,32 @@ static struct device_attribute *bt_attrs[] = {
|
||||||
NULL
|
NULL
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static ssize_t show_conn_type(struct device *dev, struct device_attribute *attr, char *buf)
|
||||||
|
{
|
||||||
|
struct hci_conn *conn = dev_get_drvdata(dev);
|
||||||
|
return sprintf(buf, "%s\n", conn->type == ACL_LINK ? "ACL" : "SCO");
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t show_conn_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));
|
||||||
|
}
|
||||||
|
|
||||||
|
#define CONN_ATTR(_name,_mode,_show,_store) \
|
||||||
|
struct device_attribute conn_attr_##_name = __ATTR(_name,_mode,_show,_store)
|
||||||
|
|
||||||
|
static CONN_ATTR(type, S_IRUGO, show_conn_type, NULL);
|
||||||
|
static CONN_ATTR(address, S_IRUGO, show_conn_address, NULL);
|
||||||
|
|
||||||
|
static struct device_attribute *conn_attrs[] = {
|
||||||
|
&conn_attr_type,
|
||||||
|
&conn_attr_address,
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
struct class *bt_class = NULL;
|
struct class *bt_class = NULL;
|
||||||
EXPORT_SYMBOL_GPL(bt_class);
|
EXPORT_SYMBOL_GPL(bt_class);
|
||||||
|
|
||||||
|
@ -181,8 +207,57 @@ static struct platform_device *bt_platform;
|
||||||
|
|
||||||
static void bt_release(struct device *dev)
|
static void bt_release(struct device *dev)
|
||||||
{
|
{
|
||||||
struct hci_dev *hdev = dev_get_drvdata(dev);
|
void *data = dev_get_drvdata(dev);
|
||||||
kfree(hdev);
|
kfree(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void add_conn(void *data)
|
||||||
|
{
|
||||||
|
struct hci_conn *conn = data;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
device_register(&conn->dev);
|
||||||
|
|
||||||
|
for (i = 0; conn_attrs[i]; i++)
|
||||||
|
device_create_file(&conn->dev, conn_attrs[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void hci_conn_add_sysfs(struct hci_conn *conn)
|
||||||
|
{
|
||||||
|
struct hci_dev *hdev = conn->hdev;
|
||||||
|
bdaddr_t *ba = &conn->dst;
|
||||||
|
|
||||||
|
BT_DBG("conn %p", conn);
|
||||||
|
|
||||||
|
conn->dev.parent = &hdev->dev;
|
||||||
|
conn->dev.release = bt_release;
|
||||||
|
|
||||||
|
snprintf(conn->dev.bus_id, BUS_ID_SIZE,
|
||||||
|
"%s%2.2X%2.2X%2.2X%2.2X%2.2X%2.2X",
|
||||||
|
conn->type == ACL_LINK ? "acl" : "sco",
|
||||||
|
ba->b[5], ba->b[4], ba->b[3],
|
||||||
|
ba->b[2], ba->b[1], ba->b[0]);
|
||||||
|
|
||||||
|
dev_set_drvdata(&conn->dev, conn);
|
||||||
|
|
||||||
|
INIT_WORK(&conn->work, add_conn, (void *) conn);
|
||||||
|
|
||||||
|
schedule_work(&conn->work);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void del_conn(void *data)
|
||||||
|
{
|
||||||
|
struct hci_conn *conn = data;
|
||||||
|
device_del(&conn->dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
void hci_conn_del_sysfs(struct hci_conn *conn)
|
||||||
|
{
|
||||||
|
BT_DBG("conn %p", conn);
|
||||||
|
|
||||||
|
INIT_WORK(&conn->work, del_conn, (void *) conn);
|
||||||
|
|
||||||
|
schedule_work(&conn->work);
|
||||||
}
|
}
|
||||||
|
|
||||||
int hci_register_sysfs(struct hci_dev *hdev)
|
int hci_register_sysfs(struct hci_dev *hdev)
|
||||||
|
|
Loading…
Reference in New Issue