0001
0002
0003
0004
0005
0006
0007
0008
0009 #include <linux/module.h>
0010 #include <linux/kernel.h>
0011 #include <net/genetlink.h>
0012 #include <uapi/linux/thermal.h>
0013
0014 #include "thermal_core.h"
0015
0016 static const struct genl_multicast_group thermal_genl_mcgrps[] = {
0017 { .name = THERMAL_GENL_SAMPLING_GROUP_NAME, },
0018 { .name = THERMAL_GENL_EVENT_GROUP_NAME, },
0019 };
0020
0021 static const struct nla_policy thermal_genl_policy[THERMAL_GENL_ATTR_MAX + 1] = {
0022
0023 [THERMAL_GENL_ATTR_TZ] = { .type = NLA_NESTED },
0024 [THERMAL_GENL_ATTR_TZ_ID] = { .type = NLA_U32 },
0025 [THERMAL_GENL_ATTR_TZ_TEMP] = { .type = NLA_U32 },
0026 [THERMAL_GENL_ATTR_TZ_TRIP] = { .type = NLA_NESTED },
0027 [THERMAL_GENL_ATTR_TZ_TRIP_ID] = { .type = NLA_U32 },
0028 [THERMAL_GENL_ATTR_TZ_TRIP_TEMP] = { .type = NLA_U32 },
0029 [THERMAL_GENL_ATTR_TZ_TRIP_TYPE] = { .type = NLA_U32 },
0030 [THERMAL_GENL_ATTR_TZ_TRIP_HYST] = { .type = NLA_U32 },
0031 [THERMAL_GENL_ATTR_TZ_MODE] = { .type = NLA_U32 },
0032 [THERMAL_GENL_ATTR_TZ_CDEV_WEIGHT] = { .type = NLA_U32 },
0033 [THERMAL_GENL_ATTR_TZ_NAME] = { .type = NLA_STRING,
0034 .len = THERMAL_NAME_LENGTH },
0035
0036 [THERMAL_GENL_ATTR_TZ_GOV] = { .type = NLA_NESTED },
0037 [THERMAL_GENL_ATTR_TZ_GOV_NAME] = { .type = NLA_STRING,
0038 .len = THERMAL_NAME_LENGTH },
0039
0040 [THERMAL_GENL_ATTR_CDEV] = { .type = NLA_NESTED },
0041 [THERMAL_GENL_ATTR_CDEV_ID] = { .type = NLA_U32 },
0042 [THERMAL_GENL_ATTR_CDEV_CUR_STATE] = { .type = NLA_U32 },
0043 [THERMAL_GENL_ATTR_CDEV_MAX_STATE] = { .type = NLA_U32 },
0044 [THERMAL_GENL_ATTR_CDEV_NAME] = { .type = NLA_STRING,
0045 .len = THERMAL_NAME_LENGTH },
0046
0047 [THERMAL_GENL_ATTR_CPU_CAPABILITY] = { .type = NLA_NESTED },
0048 [THERMAL_GENL_ATTR_CPU_CAPABILITY_ID] = { .type = NLA_U32 },
0049 [THERMAL_GENL_ATTR_CPU_CAPABILITY_PERFORMANCE] = { .type = NLA_U32 },
0050 [THERMAL_GENL_ATTR_CPU_CAPABILITY_EFFICIENCY] = { .type = NLA_U32 },
0051 };
0052
0053 struct param {
0054 struct nlattr **attrs;
0055 struct sk_buff *msg;
0056 const char *name;
0057 int tz_id;
0058 int cdev_id;
0059 int trip_id;
0060 int trip_temp;
0061 int trip_type;
0062 int trip_hyst;
0063 int temp;
0064 int cdev_state;
0065 int cdev_max_state;
0066 struct thermal_genl_cpu_caps *cpu_capabilities;
0067 int cpu_capabilities_count;
0068 };
0069
0070 typedef int (*cb_t)(struct param *);
0071
0072 static struct genl_family thermal_gnl_family;
0073
0074
0075
0076 int thermal_genl_sampling_temp(int id, int temp)
0077 {
0078 struct sk_buff *skb;
0079 void *hdr;
0080
0081 skb = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
0082 if (!skb)
0083 return -ENOMEM;
0084
0085 hdr = genlmsg_put(skb, 0, 0, &thermal_gnl_family, 0,
0086 THERMAL_GENL_SAMPLING_TEMP);
0087 if (!hdr)
0088 goto out_free;
0089
0090 if (nla_put_u32(skb, THERMAL_GENL_ATTR_TZ_ID, id))
0091 goto out_cancel;
0092
0093 if (nla_put_u32(skb, THERMAL_GENL_ATTR_TZ_TEMP, temp))
0094 goto out_cancel;
0095
0096 genlmsg_end(skb, hdr);
0097
0098 genlmsg_multicast(&thermal_gnl_family, skb, 0, 0, GFP_KERNEL);
0099
0100 return 0;
0101 out_cancel:
0102 genlmsg_cancel(skb, hdr);
0103 out_free:
0104 nlmsg_free(skb);
0105
0106 return -EMSGSIZE;
0107 }
0108
0109
0110
0111 static int thermal_genl_event_tz_create(struct param *p)
0112 {
0113 if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_ID, p->tz_id) ||
0114 nla_put_string(p->msg, THERMAL_GENL_ATTR_TZ_NAME, p->name))
0115 return -EMSGSIZE;
0116
0117 return 0;
0118 }
0119
0120 static int thermal_genl_event_tz(struct param *p)
0121 {
0122 if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_ID, p->tz_id))
0123 return -EMSGSIZE;
0124
0125 return 0;
0126 }
0127
0128 static int thermal_genl_event_tz_trip_up(struct param *p)
0129 {
0130 if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_ID, p->tz_id) ||
0131 nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_TRIP_ID, p->trip_id) ||
0132 nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_TEMP, p->temp))
0133 return -EMSGSIZE;
0134
0135 return 0;
0136 }
0137
0138 static int thermal_genl_event_tz_trip_add(struct param *p)
0139 {
0140 if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_ID, p->tz_id) ||
0141 nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_TRIP_ID, p->trip_id) ||
0142 nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_TRIP_TYPE, p->trip_type) ||
0143 nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_TRIP_TEMP, p->trip_temp) ||
0144 nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_TRIP_HYST, p->trip_hyst))
0145 return -EMSGSIZE;
0146
0147 return 0;
0148 }
0149
0150 static int thermal_genl_event_tz_trip_delete(struct param *p)
0151 {
0152 if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_ID, p->tz_id) ||
0153 nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_TRIP_ID, p->trip_id))
0154 return -EMSGSIZE;
0155
0156 return 0;
0157 }
0158
0159 static int thermal_genl_event_cdev_add(struct param *p)
0160 {
0161 if (nla_put_string(p->msg, THERMAL_GENL_ATTR_CDEV_NAME,
0162 p->name) ||
0163 nla_put_u32(p->msg, THERMAL_GENL_ATTR_CDEV_ID,
0164 p->cdev_id) ||
0165 nla_put_u32(p->msg, THERMAL_GENL_ATTR_CDEV_MAX_STATE,
0166 p->cdev_max_state))
0167 return -EMSGSIZE;
0168
0169 return 0;
0170 }
0171
0172 static int thermal_genl_event_cdev_delete(struct param *p)
0173 {
0174 if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_CDEV_ID, p->cdev_id))
0175 return -EMSGSIZE;
0176
0177 return 0;
0178 }
0179
0180 static int thermal_genl_event_cdev_state_update(struct param *p)
0181 {
0182 if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_CDEV_ID,
0183 p->cdev_id) ||
0184 nla_put_u32(p->msg, THERMAL_GENL_ATTR_CDEV_CUR_STATE,
0185 p->cdev_state))
0186 return -EMSGSIZE;
0187
0188 return 0;
0189 }
0190
0191 static int thermal_genl_event_gov_change(struct param *p)
0192 {
0193 if (nla_put_u32(p->msg, THERMAL_GENL_ATTR_TZ_ID, p->tz_id) ||
0194 nla_put_string(p->msg, THERMAL_GENL_ATTR_GOV_NAME, p->name))
0195 return -EMSGSIZE;
0196
0197 return 0;
0198 }
0199
0200 static int thermal_genl_event_cpu_capability_change(struct param *p)
0201 {
0202 struct thermal_genl_cpu_caps *cpu_cap = p->cpu_capabilities;
0203 struct sk_buff *msg = p->msg;
0204 struct nlattr *start_cap;
0205 int i;
0206
0207 start_cap = nla_nest_start(msg, THERMAL_GENL_ATTR_CPU_CAPABILITY);
0208 if (!start_cap)
0209 return -EMSGSIZE;
0210
0211 for (i = 0; i < p->cpu_capabilities_count; ++i) {
0212 if (nla_put_u32(msg, THERMAL_GENL_ATTR_CPU_CAPABILITY_ID,
0213 cpu_cap->cpu))
0214 goto out_cancel_nest;
0215
0216 if (nla_put_u32(msg, THERMAL_GENL_ATTR_CPU_CAPABILITY_PERFORMANCE,
0217 cpu_cap->performance))
0218 goto out_cancel_nest;
0219
0220 if (nla_put_u32(msg, THERMAL_GENL_ATTR_CPU_CAPABILITY_EFFICIENCY,
0221 cpu_cap->efficiency))
0222 goto out_cancel_nest;
0223
0224 ++cpu_cap;
0225 }
0226
0227 nla_nest_end(msg, start_cap);
0228
0229 return 0;
0230 out_cancel_nest:
0231 nla_nest_cancel(msg, start_cap);
0232
0233 return -EMSGSIZE;
0234 }
0235
0236 int thermal_genl_event_tz_delete(struct param *p)
0237 __attribute__((alias("thermal_genl_event_tz")));
0238
0239 int thermal_genl_event_tz_enable(struct param *p)
0240 __attribute__((alias("thermal_genl_event_tz")));
0241
0242 int thermal_genl_event_tz_disable(struct param *p)
0243 __attribute__((alias("thermal_genl_event_tz")));
0244
0245 int thermal_genl_event_tz_trip_down(struct param *p)
0246 __attribute__((alias("thermal_genl_event_tz_trip_up")));
0247
0248 int thermal_genl_event_tz_trip_change(struct param *p)
0249 __attribute__((alias("thermal_genl_event_tz_trip_add")));
0250
0251 static cb_t event_cb[] = {
0252 [THERMAL_GENL_EVENT_TZ_CREATE] = thermal_genl_event_tz_create,
0253 [THERMAL_GENL_EVENT_TZ_DELETE] = thermal_genl_event_tz_delete,
0254 [THERMAL_GENL_EVENT_TZ_ENABLE] = thermal_genl_event_tz_enable,
0255 [THERMAL_GENL_EVENT_TZ_DISABLE] = thermal_genl_event_tz_disable,
0256 [THERMAL_GENL_EVENT_TZ_TRIP_UP] = thermal_genl_event_tz_trip_up,
0257 [THERMAL_GENL_EVENT_TZ_TRIP_DOWN] = thermal_genl_event_tz_trip_down,
0258 [THERMAL_GENL_EVENT_TZ_TRIP_CHANGE] = thermal_genl_event_tz_trip_change,
0259 [THERMAL_GENL_EVENT_TZ_TRIP_ADD] = thermal_genl_event_tz_trip_add,
0260 [THERMAL_GENL_EVENT_TZ_TRIP_DELETE] = thermal_genl_event_tz_trip_delete,
0261 [THERMAL_GENL_EVENT_CDEV_ADD] = thermal_genl_event_cdev_add,
0262 [THERMAL_GENL_EVENT_CDEV_DELETE] = thermal_genl_event_cdev_delete,
0263 [THERMAL_GENL_EVENT_CDEV_STATE_UPDATE] = thermal_genl_event_cdev_state_update,
0264 [THERMAL_GENL_EVENT_TZ_GOV_CHANGE] = thermal_genl_event_gov_change,
0265 [THERMAL_GENL_EVENT_CPU_CAPABILITY_CHANGE] = thermal_genl_event_cpu_capability_change,
0266 };
0267
0268
0269
0270
0271 static int thermal_genl_send_event(enum thermal_genl_event event,
0272 struct param *p)
0273 {
0274 struct sk_buff *msg;
0275 int ret = -EMSGSIZE;
0276 void *hdr;
0277
0278 msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
0279 if (!msg)
0280 return -ENOMEM;
0281 p->msg = msg;
0282
0283 hdr = genlmsg_put(msg, 0, 0, &thermal_gnl_family, 0, event);
0284 if (!hdr)
0285 goto out_free_msg;
0286
0287 ret = event_cb[event](p);
0288 if (ret)
0289 goto out_cancel_msg;
0290
0291 genlmsg_end(msg, hdr);
0292
0293 genlmsg_multicast(&thermal_gnl_family, msg, 0, 1, GFP_KERNEL);
0294
0295 return 0;
0296
0297 out_cancel_msg:
0298 genlmsg_cancel(msg, hdr);
0299 out_free_msg:
0300 nlmsg_free(msg);
0301
0302 return ret;
0303 }
0304
0305 int thermal_notify_tz_create(int tz_id, const char *name)
0306 {
0307 struct param p = { .tz_id = tz_id, .name = name };
0308
0309 return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_CREATE, &p);
0310 }
0311
0312 int thermal_notify_tz_delete(int tz_id)
0313 {
0314 struct param p = { .tz_id = tz_id };
0315
0316 return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_DELETE, &p);
0317 }
0318
0319 int thermal_notify_tz_enable(int tz_id)
0320 {
0321 struct param p = { .tz_id = tz_id };
0322
0323 return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_ENABLE, &p);
0324 }
0325
0326 int thermal_notify_tz_disable(int tz_id)
0327 {
0328 struct param p = { .tz_id = tz_id };
0329
0330 return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_DISABLE, &p);
0331 }
0332
0333 int thermal_notify_tz_trip_down(int tz_id, int trip_id, int temp)
0334 {
0335 struct param p = { .tz_id = tz_id, .trip_id = trip_id, .temp = temp };
0336
0337 return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_TRIP_DOWN, &p);
0338 }
0339
0340 int thermal_notify_tz_trip_up(int tz_id, int trip_id, int temp)
0341 {
0342 struct param p = { .tz_id = tz_id, .trip_id = trip_id, .temp = temp };
0343
0344 return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_TRIP_UP, &p);
0345 }
0346
0347 int thermal_notify_tz_trip_add(int tz_id, int trip_id, int trip_type,
0348 int trip_temp, int trip_hyst)
0349 {
0350 struct param p = { .tz_id = tz_id, .trip_id = trip_id,
0351 .trip_type = trip_type, .trip_temp = trip_temp,
0352 .trip_hyst = trip_hyst };
0353
0354 return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_TRIP_ADD, &p);
0355 }
0356
0357 int thermal_notify_tz_trip_delete(int tz_id, int trip_id)
0358 {
0359 struct param p = { .tz_id = tz_id, .trip_id = trip_id };
0360
0361 return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_TRIP_DELETE, &p);
0362 }
0363
0364 int thermal_notify_tz_trip_change(int tz_id, int trip_id, int trip_type,
0365 int trip_temp, int trip_hyst)
0366 {
0367 struct param p = { .tz_id = tz_id, .trip_id = trip_id,
0368 .trip_type = trip_type, .trip_temp = trip_temp,
0369 .trip_hyst = trip_hyst };
0370
0371 return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_TRIP_CHANGE, &p);
0372 }
0373
0374 int thermal_notify_cdev_state_update(int cdev_id, int cdev_state)
0375 {
0376 struct param p = { .cdev_id = cdev_id, .cdev_state = cdev_state };
0377
0378 return thermal_genl_send_event(THERMAL_GENL_EVENT_CDEV_STATE_UPDATE, &p);
0379 }
0380
0381 int thermal_notify_cdev_add(int cdev_id, const char *name, int cdev_max_state)
0382 {
0383 struct param p = { .cdev_id = cdev_id, .name = name,
0384 .cdev_max_state = cdev_max_state };
0385
0386 return thermal_genl_send_event(THERMAL_GENL_EVENT_CDEV_ADD, &p);
0387 }
0388
0389 int thermal_notify_cdev_delete(int cdev_id)
0390 {
0391 struct param p = { .cdev_id = cdev_id };
0392
0393 return thermal_genl_send_event(THERMAL_GENL_EVENT_CDEV_DELETE, &p);
0394 }
0395
0396 int thermal_notify_tz_gov_change(int tz_id, const char *name)
0397 {
0398 struct param p = { .tz_id = tz_id, .name = name };
0399
0400 return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_GOV_CHANGE, &p);
0401 }
0402
0403 int thermal_genl_cpu_capability_event(int count,
0404 struct thermal_genl_cpu_caps *caps)
0405 {
0406 struct param p = { .cpu_capabilities_count = count, .cpu_capabilities = caps };
0407
0408 return thermal_genl_send_event(THERMAL_GENL_EVENT_CPU_CAPABILITY_CHANGE, &p);
0409 }
0410 EXPORT_SYMBOL_GPL(thermal_genl_cpu_capability_event);
0411
0412
0413
0414 static int __thermal_genl_cmd_tz_get_id(struct thermal_zone_device *tz,
0415 void *data)
0416 {
0417 struct sk_buff *msg = data;
0418
0419 if (nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_ID, tz->id) ||
0420 nla_put_string(msg, THERMAL_GENL_ATTR_TZ_NAME, tz->type))
0421 return -EMSGSIZE;
0422
0423 return 0;
0424 }
0425
0426 static int thermal_genl_cmd_tz_get_id(struct param *p)
0427 {
0428 struct sk_buff *msg = p->msg;
0429 struct nlattr *start_tz;
0430 int ret;
0431
0432 start_tz = nla_nest_start(msg, THERMAL_GENL_ATTR_TZ);
0433 if (!start_tz)
0434 return -EMSGSIZE;
0435
0436 ret = for_each_thermal_zone(__thermal_genl_cmd_tz_get_id, msg);
0437 if (ret)
0438 goto out_cancel_nest;
0439
0440 nla_nest_end(msg, start_tz);
0441
0442 return 0;
0443
0444 out_cancel_nest:
0445 nla_nest_cancel(msg, start_tz);
0446
0447 return ret;
0448 }
0449
0450 static int thermal_genl_cmd_tz_get_trip(struct param *p)
0451 {
0452 struct sk_buff *msg = p->msg;
0453 struct thermal_zone_device *tz;
0454 struct nlattr *start_trip;
0455 int i, id;
0456
0457 if (!p->attrs[THERMAL_GENL_ATTR_TZ_ID])
0458 return -EINVAL;
0459
0460 id = nla_get_u32(p->attrs[THERMAL_GENL_ATTR_TZ_ID]);
0461
0462 tz = thermal_zone_get_by_id(id);
0463 if (!tz)
0464 return -EINVAL;
0465
0466 start_trip = nla_nest_start(msg, THERMAL_GENL_ATTR_TZ_TRIP);
0467 if (!start_trip)
0468 return -EMSGSIZE;
0469
0470 mutex_lock(&tz->lock);
0471
0472 for (i = 0; i < tz->num_trips; i++) {
0473
0474 enum thermal_trip_type type;
0475 int temp, hyst = 0;
0476
0477 tz->ops->get_trip_type(tz, i, &type);
0478 tz->ops->get_trip_temp(tz, i, &temp);
0479 if (tz->ops->get_trip_hyst)
0480 tz->ops->get_trip_hyst(tz, i, &hyst);
0481
0482 if (nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_TRIP_ID, i) ||
0483 nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_TRIP_TYPE, type) ||
0484 nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_TRIP_TEMP, temp) ||
0485 nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_TRIP_HYST, hyst))
0486 goto out_cancel_nest;
0487 }
0488
0489 mutex_unlock(&tz->lock);
0490
0491 nla_nest_end(msg, start_trip);
0492
0493 return 0;
0494
0495 out_cancel_nest:
0496 mutex_unlock(&tz->lock);
0497
0498 return -EMSGSIZE;
0499 }
0500
0501 static int thermal_genl_cmd_tz_get_temp(struct param *p)
0502 {
0503 struct sk_buff *msg = p->msg;
0504 struct thermal_zone_device *tz;
0505 int temp, ret, id;
0506
0507 if (!p->attrs[THERMAL_GENL_ATTR_TZ_ID])
0508 return -EINVAL;
0509
0510 id = nla_get_u32(p->attrs[THERMAL_GENL_ATTR_TZ_ID]);
0511
0512 tz = thermal_zone_get_by_id(id);
0513 if (!tz)
0514 return -EINVAL;
0515
0516 ret = thermal_zone_get_temp(tz, &temp);
0517 if (ret)
0518 return ret;
0519
0520 if (nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_ID, id) ||
0521 nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_TEMP, temp))
0522 return -EMSGSIZE;
0523
0524 return 0;
0525 }
0526
0527 static int thermal_genl_cmd_tz_get_gov(struct param *p)
0528 {
0529 struct sk_buff *msg = p->msg;
0530 struct thermal_zone_device *tz;
0531 int id, ret = 0;
0532
0533 if (!p->attrs[THERMAL_GENL_ATTR_TZ_ID])
0534 return -EINVAL;
0535
0536 id = nla_get_u32(p->attrs[THERMAL_GENL_ATTR_TZ_ID]);
0537
0538 tz = thermal_zone_get_by_id(id);
0539 if (!tz)
0540 return -EINVAL;
0541
0542 mutex_lock(&tz->lock);
0543
0544 if (nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_ID, id) ||
0545 nla_put_string(msg, THERMAL_GENL_ATTR_TZ_GOV_NAME,
0546 tz->governor->name))
0547 ret = -EMSGSIZE;
0548
0549 mutex_unlock(&tz->lock);
0550
0551 return ret;
0552 }
0553
0554 static int __thermal_genl_cmd_cdev_get(struct thermal_cooling_device *cdev,
0555 void *data)
0556 {
0557 struct sk_buff *msg = data;
0558
0559 if (nla_put_u32(msg, THERMAL_GENL_ATTR_CDEV_ID, cdev->id))
0560 return -EMSGSIZE;
0561
0562 if (nla_put_string(msg, THERMAL_GENL_ATTR_CDEV_NAME, cdev->type))
0563 return -EMSGSIZE;
0564
0565 return 0;
0566 }
0567
0568 static int thermal_genl_cmd_cdev_get(struct param *p)
0569 {
0570 struct sk_buff *msg = p->msg;
0571 struct nlattr *start_cdev;
0572 int ret;
0573
0574 start_cdev = nla_nest_start(msg, THERMAL_GENL_ATTR_CDEV);
0575 if (!start_cdev)
0576 return -EMSGSIZE;
0577
0578 ret = for_each_thermal_cooling_device(__thermal_genl_cmd_cdev_get, msg);
0579 if (ret)
0580 goto out_cancel_nest;
0581
0582 nla_nest_end(msg, start_cdev);
0583
0584 return 0;
0585 out_cancel_nest:
0586 nla_nest_cancel(msg, start_cdev);
0587
0588 return ret;
0589 }
0590
0591 static cb_t cmd_cb[] = {
0592 [THERMAL_GENL_CMD_TZ_GET_ID] = thermal_genl_cmd_tz_get_id,
0593 [THERMAL_GENL_CMD_TZ_GET_TRIP] = thermal_genl_cmd_tz_get_trip,
0594 [THERMAL_GENL_CMD_TZ_GET_TEMP] = thermal_genl_cmd_tz_get_temp,
0595 [THERMAL_GENL_CMD_TZ_GET_GOV] = thermal_genl_cmd_tz_get_gov,
0596 [THERMAL_GENL_CMD_CDEV_GET] = thermal_genl_cmd_cdev_get,
0597 };
0598
0599 static int thermal_genl_cmd_dumpit(struct sk_buff *skb,
0600 struct netlink_callback *cb)
0601 {
0602 struct param p = { .msg = skb };
0603 const struct genl_dumpit_info *info = genl_dumpit_info(cb);
0604 int cmd = info->op.cmd;
0605 int ret;
0606 void *hdr;
0607
0608 hdr = genlmsg_put(skb, 0, 0, &thermal_gnl_family, 0, cmd);
0609 if (!hdr)
0610 return -EMSGSIZE;
0611
0612 ret = cmd_cb[cmd](&p);
0613 if (ret)
0614 goto out_cancel_msg;
0615
0616 genlmsg_end(skb, hdr);
0617
0618 return 0;
0619
0620 out_cancel_msg:
0621 genlmsg_cancel(skb, hdr);
0622
0623 return ret;
0624 }
0625
0626 static int thermal_genl_cmd_doit(struct sk_buff *skb,
0627 struct genl_info *info)
0628 {
0629 struct param p = { .attrs = info->attrs };
0630 struct sk_buff *msg;
0631 void *hdr;
0632 int cmd = info->genlhdr->cmd;
0633 int ret = -EMSGSIZE;
0634
0635 msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
0636 if (!msg)
0637 return -ENOMEM;
0638 p.msg = msg;
0639
0640 hdr = genlmsg_put_reply(msg, info, &thermal_gnl_family, 0, cmd);
0641 if (!hdr)
0642 goto out_free_msg;
0643
0644 ret = cmd_cb[cmd](&p);
0645 if (ret)
0646 goto out_cancel_msg;
0647
0648 genlmsg_end(msg, hdr);
0649
0650 return genlmsg_reply(msg, info);
0651
0652 out_cancel_msg:
0653 genlmsg_cancel(msg, hdr);
0654 out_free_msg:
0655 nlmsg_free(msg);
0656
0657 return ret;
0658 }
0659
0660 static const struct genl_small_ops thermal_genl_ops[] = {
0661 {
0662 .cmd = THERMAL_GENL_CMD_TZ_GET_ID,
0663 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
0664 .dumpit = thermal_genl_cmd_dumpit,
0665 },
0666 {
0667 .cmd = THERMAL_GENL_CMD_TZ_GET_TRIP,
0668 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
0669 .doit = thermal_genl_cmd_doit,
0670 },
0671 {
0672 .cmd = THERMAL_GENL_CMD_TZ_GET_TEMP,
0673 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
0674 .doit = thermal_genl_cmd_doit,
0675 },
0676 {
0677 .cmd = THERMAL_GENL_CMD_TZ_GET_GOV,
0678 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
0679 .doit = thermal_genl_cmd_doit,
0680 },
0681 {
0682 .cmd = THERMAL_GENL_CMD_CDEV_GET,
0683 .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
0684 .dumpit = thermal_genl_cmd_dumpit,
0685 },
0686 };
0687
0688 static struct genl_family thermal_gnl_family __ro_after_init = {
0689 .hdrsize = 0,
0690 .name = THERMAL_GENL_FAMILY_NAME,
0691 .version = THERMAL_GENL_VERSION,
0692 .maxattr = THERMAL_GENL_ATTR_MAX,
0693 .policy = thermal_genl_policy,
0694 .small_ops = thermal_genl_ops,
0695 .n_small_ops = ARRAY_SIZE(thermal_genl_ops),
0696 .mcgrps = thermal_genl_mcgrps,
0697 .n_mcgrps = ARRAY_SIZE(thermal_genl_mcgrps),
0698 };
0699
0700 int __init thermal_netlink_init(void)
0701 {
0702 return genl_register_family(&thermal_gnl_family);
0703 }