Bluez hid Analysis (2)

Source: Internet
Author: User
Document directory
  • 2.1 btd_register_adapter_driver
  • 2.2 btd_register_device_driver

This article analyzes the implementation of the hid protocol in the Bluetooth bluez protocol stack.

1. Basic Concepts

The hid protocol is used for human-machine input devices. The implementation code of hid in bluez is in the input directory under its root directory. The Bluetooth specification contains the profile about hid, which reuse some protocol specifications about hid in USB.

The routing interface is used between the bluez protocol stack and upper-layer applications.

Bluez and kernel use the socket communication of the af_bluetooth protocol family, and use the glib library in GTK +.

2. Initialization

The initialization of HID is in Main. C of the input directory, and the input_manager_init function. This function calls input_manager_init. In input_manager_init, three operations are performed:

Btd_register_adapter_driver (& input_server_driver );

Btd_register_device_driver (& input_hid_driver );

Btd_register_device_driver (& input_headset_driver );

The following are discussed respectively.

2.1 btd_register_adapter_driver

Btd_register_adapter_driver (& input_server_driver );

Static struct btd_adapter_driver input_server_driver = {

. Name = "input-server ",

. Probe = hid_server_probe,

. Remove = hid_server_remove,

};

This call registers an Adapter Driver. After the system is started, the probe function in each local Bluetooth hardware instance, that is, each HCI device, will be called hid_server_probe.

Static int hid_server_probe (struct btd_adapter * adapter)

// Obtain the local Bluetooth address of the HCI Device

Adapter_get_address (adapter, & SRC );

// Start the hid Service

Server_start (& SRC );

...

 

Int server_start (const bdaddr_t * SRC)

Struct input_server * Server = g_new0 (struct input_server, 1 );

// Listen on the CTRL channel (l2cap_psm_hidp_ctrl), the callback function connect_event_cb

Server-> CTRL = bt_io_listen (bt_io_l2cap, connect_event_cb, null,

Server, null, & err,

Bt_io_opt_source_bdaddr, SRC,

Bt_io_opt_psm, l2cap_psm_hidp_ctrl,

Bt_io_opt_sec_level, bt_io_sec_low,

Bt_io_opt_invalid );

// In the intr channel (l2cap_psm_hidp_intr) Listen, the callback function confirm_event_cb

Server-> intr = bt_io_listen (bt_io_l2cap, null, confirm_event_cb,

Server, null, & err,

Bt_io_opt_source_bdaddr, SRC,

Bt_io_opt_psm, l2cap_psm_hidp_intr,

Bt_io_opt_sec_level, bt_io_sec_low,

Bt_io_opt_invalid );

Both the CTRL channel and intr channel are defined by the Bluetooth hid spec.

For the control channel, when the device actively connects to the local machine, glib calls the callback function connect_event_cb:

Static void connect_event_cb (giochannel * Chan, gerror * err, gpointer data)

// Obtain the source address and destination address of the device, such as SMS.

Bt_io_get (Chan, bt_io_l2cap, & Gerr, bt_io_opt_source_bdaddr, & SRC,

Bt_io_opt_dest_bdaddr, & DST, bt_io_opt_psm, & PSM,

Bt_io_opt_invalid );

// Set input_device

Input_device_set_channel (& SRC, & DST, SMS, Chan );

// If the device is an illegal device and is currently a control channel, the "unplug virtual cable" message needs to be sent to the other party based on the hid protocol.

If (ret =-enoent & PSM = l2cap_psm_hidp_ctrl ){

Unsigned char unplug = 0x15;

Int err, SK = g_io_channel_unix_get_fd (Chan );

Err = write (SK, & unplug, sizeof (unplug ));

}

Next we will continue to study the input_device_set_channel function.

Int input_device_set_channel (const bdaddr_t * SRC, const bdaddr_t * DST, int PSM, giochannel * IO)

// Find the corresponding input_dev device from the list of HID devices based on the device address of the other device. There is a problem here, that is, when the corresponding input_dev device was registered to the linked list. This will be discussed later.

Struct input_device * IDEV = find_device (SRC, DST );

// Find the connection named "hid" in the device

Struct input_conn * iconn = find_connection (IDEV-> connections, "hid ");

Switch (PSM ){

Case l2cap_psm_hidp_ctrl:

If (iconn-> ctrl_io)

Return-ealready;

Iconn-> ctrl_io = g_io_channel_ref (IO );

Break;

Case l2cap_psm_hidp_intr:

If (iconn-> intr_io)

Return-ealready;

Iconn-> intr_io = g_io_channel_ref (IO );

Break;

}

// When both the CTRL channel and the intr channel are set, the system enters input_device_connadd. Currently, we can see this in the callback function connect_event_cb of l2cap_psm_hidp_ctrl, so we will not study it further.

If (iconn-> intr_io & iconn-> ctrl_io)

Input_device_connadd (IDEV, iconn );

...

Next, let's take a look at the l2cap_psm_hidp_intr in the server_start function, and call the confirm_event_cb function. For the differences between the connect and confirm functions in bt_io_listen, you can view the glib documentation or the source code of bluez on your own.

Static void confirm_event_cb (giochannel * Chan, gpointer user_data)

Bt_io_get (Chan, bt_io_l2cap, & err, bt_io_opt_source_bdaddr, & SRC,

Bt_io_opt_dest_bdaddr, & DST, bt_io_opt_invalid );

Server-> confirm = g_io_channel_ref (Chan );

// Request the authorization operation and specify the completed callback function as auth_callback

Btd_request_authorization (& SRC, & DST, hid_uuid, auth_callback, server );

Static void auth_callback (dbuserror * Derr, void * user_data)

Bt_io_get (server-> confirm, bt_io_l2cap, & err,

Bt_io_opt_source_bdaddr, & SRC,

Bt_io_opt_dest_bdaddr, & DST,

Bt_io_opt_invalid );

Bt_io_accept (server-> confirm, connect_event_cb, server, null, & ERR)

It can be seen that after authorization, bt_io_accept will be called and the callback function is also specified as connect_event_cb. In this case, connect_event_cb sets the intr channel and calls the input_device_connadd function.

Static int input_device_connadd (struct input_device * IDEV, struct input_conn * iconn)

Input_device_connected (IDEV, iconn)

...

Static int input_device_connected (struct input_device * IDEV, struct input_conn * iconn)

Hidp_add_connection (IDEV, iconn)

...

Connected = true;

// Send the connected signal through the signal

Emit_property_changed (IDEV-> Conn, IDEV-> path, input_device_interface,

"Connected", dbus_type_boolean, & connected );

...

Static int hidp_add_connection (const struct input_device * IDEV, const struct input_conn * iconn)

Struct hidp_connadd_req * req;

Sdp_record_t * REC;

Req = g_new0 (struct hidp_connadd_req, 1 );

REQ-> ctrl_sock = g_io_channel_unix_get_fd (iconn-> ctrl_io );

REQ-> intr_sock = g_io_channel_unix_get_fd (iconn-> intr_io );

REQ-> flags = 0;

REQ-> idle_to = iconn-> timeout;

Ba2str (& IDEV-> SRC, src_addr );

Ba2str (& IDEV-> DST, dst_addr );

// Find the SDP corresponding to the device

Rec = fetch_record (src_addr, dst_addr, IDEV-> handle );

// Obtain some attributes from SDP record to set some fields in Req. The specific code can be viewed, including the HID device descriptor.

Extract_hid_record (REC, req );

Sdp_record_free (REC );

// Obtain information about the device, such as the device, based on SDP.

Read_device_id (src_addr, dst_addr, null,

& Req-> vendor, & req-> product, & req-> version );

// The following Code supports fakehid. Currently, this code is only supported by the PS3 device.

Struct fake_hid * fake_hid = get_fake_hid (req-> vendor, req-> product );

...

If (req-> subclass & 0x40) // if it is a keyboard, enable Encryption

Bt_acl_encrypt (& IDEV-> SRC, & IDEV-> DST, encrypt_completed, req );

...

// A btproto_hidp socket will be created in ioctl_connadd, and a new connection will be created by calling hidpconnadd. Now, the connection to the remote device is established. After the establishment, the kernel establishes a HID device, which interacts with bluez through CTRL sock and intr sock.

Ioctl_connadd (req );

 

2.2 btd_register_device_driver

Btd_register_device_driver is used to register the device driver. Two devices registered using this function in bluez are input_headset_driver and input_hid_driver.

Input-headset is related to Bluetooth headsets, and input-hid is used for normal hid devices.

Next let's take a look at the input_hid_driver device.

Input_hid_driver

Static struct btd_device_driver input_hid_driver = {

. Name = "input-hid ",

. Uuids = btd_uuids (hid_uuid ),

. Probe = hid_device_probe,

. Remove = hid_device_remove,

};

When bluez detects that there is a HID device, that is, a device with a uuid containing the hid_uuid is connected, it calls the probe function.

Static int hid_device_probe (struct btd_device * device, gslist * uuuids)

...

Input_device_register (connection, device, path, & SRC, & DST,

Hid_uuid, rec-> handle, idle_timeout * 60 );

 

Int input_device_register (dbusconnection * Conn, struct btd_device * device,

Const char * path, const bdaddr_t * SRC,

Const bdaddr_t * DST, const char * UUID,

Uint32_t handle, int timeout)

...

// Assign a new input_device struct and add it to the global linked list devices.

// When analyzing the input_device_set_channel function in the previous article, add IDEV here.

IDEV = input_device_new (Conn, device, path, SRC, DST, handle );

Devices = g_slist_append (devices, IDEV );

...

// Add a connection named "hid"

Iconn = input_conn_new (IDEV, UUID, "hid", timeout );

IDEV-> connections = g_slist_append (IDEV-> connections, iconn );

In the input_device_new function, in addition to creating a device, a producer interface is added:

G_dbus_register_interface (Conn, IDEV-> path, input_device_interface,

Device_methods, device_signals, null,

IDEV, device_unregister)

Static gdbusmethodtable device_methods [] = {

{"Connect", "", "", input_device_connect,

G_dbus_method_flag_async },

{"Disconnect", "", "", input_device_disconnect },

{"Virtualunplug", "", "", input_device_unplug },

{"Getproperties", "", "A {SV}", input_device_get_properties },

{}

};

The above analysis shows that when the hid connection is established, the local machine serves as the server and waits for the remote device to connect. With this disconnect interface, the local application can actively connect to the remote device. You only need to call the connect method. This method will be linked to the input_device_connect function.

The process in input_device_connect is basically the same as that in the previous article when the local machine acts as a server. In this function, the CTRL channel connection is established first, and then the intr channel connection is established. In the end, the hidp_add_connection function is called to notify the kernel to establish a HID device or input device (hid Boot Protocol device ).

Input-Headset

Static struct btd_device_driver input_headset_driver = {

. Name = "input-headset ",

. Uuids = btd_uuids (hsp_hs_uuid ),

. Probe = headset_probe,

. Remove = headset_remove,

};

The process of input-headset is special. There are at least the following differences with input-hid:

1. The connection of the HID device is established on L2CAP, and the connection of the headset is established on RFCOMM.

2. The HID device notifies the kernel to establish a HID device or input device. The headset device only instantiates a uinput device.

3. the CTRL channel and intr channel mentioned above cannot apply to headset, because they are all connections on L2CAP.

If the remote headset device is actively connected locally, the application also calls the "Connect" method to start the connection process. For specific implementation, you can view the code.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.