I've been working with bluez 4.101 recently bringing up a GATT server, you'll want to take a look at "linkloss.c" located (in bluez 4.101 at least) in the "proximity" directory. Or here I guess:
https://github.com/pauloborges/bluez/blob/master/profiles/proximity/linkloss.c
The link loss service is registered with writable callbacks like:
svc_added = gatt_service_add(adapter,
GATT_PRIM_SVC_UUID, &uuid,
/* Alert level characteristic */
GATT_OPT_CHR_UUID16, ALERT_LEVEL_CHR_UUID,
GATT_OPT_CHR_PROPS,
ATT_CHAR_PROPER_READ | ATT_CHAR_PROPER_WRITE,
GATT_OPT_CHR_VALUE_CB, ATTRIB_READ,
link_loss_alert_lvl_read, lladapter,
GATT_OPT_CHR_VALUE_CB, ATTRIB_WRITE,
link_loss_alert_lvl_write, lladapter,
GATT_OPT_CHR_VALUE_GET_HANDLE,
&lladapter->alert_lvl_value_handle,
GATT_OPT_INVALID);
Where the callback structure is like:
static uint8_t link_loss_alert_lvl_write(struct attribute *a, struct btd_device *device, gpointer user_data)
{
/*stuff*/
}
The "attribute" struct contains the data passed to the writable callback.