1626 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Generic netlink for DPLL management framework4 *5 * Copyright (c) 2023 Meta Platforms, Inc. and affiliates6 * Copyright (c) 2023 Intel and affiliates7 *8 */9#include <linux/module.h>10#include <linux/kernel.h>11#include <linux/netdevice.h>12#include <net/genetlink.h>13#include "dpll_core.h"14#include "dpll_netlink.h"15#include "dpll_nl.h"16#include <uapi/linux/dpll.h>17 18#define ASSERT_NOT_NULL(ptr) (WARN_ON(!ptr))19 20#define xa_for_each_marked_start(xa, index, entry, filter, start) \21 for (index = start, entry = xa_find(xa, &index, ULONG_MAX, filter); \22 entry; entry = xa_find_after(xa, &index, ULONG_MAX, filter))23 24struct dpll_dump_ctx {25 unsigned long idx;26};27 28static struct dpll_dump_ctx *dpll_dump_context(struct netlink_callback *cb)29{30 return (struct dpll_dump_ctx *)cb->ctx;31}32 33static int34dpll_msg_add_dev_handle(struct sk_buff *msg, struct dpll_device *dpll)35{36 if (nla_put_u32(msg, DPLL_A_ID, dpll->id))37 return -EMSGSIZE;38 39 return 0;40}41 42static int43dpll_msg_add_dev_parent_handle(struct sk_buff *msg, u32 id)44{45 if (nla_put_u32(msg, DPLL_A_PIN_PARENT_ID, id))46 return -EMSGSIZE;47 48 return 0;49}50 51/**52 * dpll_msg_add_pin_handle - attach pin handle attribute to a given message53 * @msg: pointer to sk_buff message to attach a pin handle54 * @pin: pin pointer55 *56 * Return:57 * * 0 - success58 * * -EMSGSIZE - no space in message to attach pin handle59 */60static int dpll_msg_add_pin_handle(struct sk_buff *msg, struct dpll_pin *pin)61{62 if (!pin)63 return 0;64 if (nla_put_u32(msg, DPLL_A_PIN_ID, pin->id))65 return -EMSGSIZE;66 return 0;67}68 69static struct dpll_pin *dpll_netdev_pin(const struct net_device *dev)70{71 return rcu_dereference_rtnl(dev->dpll_pin);72}73 74/**75 * dpll_netdev_pin_handle_size - get size of pin handle attribute of a netdev76 * @dev: netdev from which to get the pin77 *78 * Return: byte size of pin handle attribute, or 0 if @dev has no pin.79 */80size_t dpll_netdev_pin_handle_size(const struct net_device *dev)81{82 return dpll_netdev_pin(dev) ? nla_total_size(4) : 0; /* DPLL_A_PIN_ID */83}84 85int dpll_netdev_add_pin_handle(struct sk_buff *msg,86 const struct net_device *dev)87{88 return dpll_msg_add_pin_handle(msg, dpll_netdev_pin(dev));89}90 91static int92dpll_msg_add_mode(struct sk_buff *msg, struct dpll_device *dpll,93 struct netlink_ext_ack *extack)94{95 const struct dpll_device_ops *ops = dpll_device_ops(dpll);96 enum dpll_mode mode;97 int ret;98 99 ret = ops->mode_get(dpll, dpll_priv(dpll), &mode, extack);100 if (ret)101 return ret;102 if (nla_put_u32(msg, DPLL_A_MODE, mode))103 return -EMSGSIZE;104 105 return 0;106}107 108static int109dpll_msg_add_mode_supported(struct sk_buff *msg, struct dpll_device *dpll,110 struct netlink_ext_ack *extack)111{112 const struct dpll_device_ops *ops = dpll_device_ops(dpll);113 enum dpll_mode mode;114 int ret;115 116 /* No mode change is supported now, so the only supported mode is the117 * one obtained by mode_get().118 */119 120 ret = ops->mode_get(dpll, dpll_priv(dpll), &mode, extack);121 if (ret)122 return ret;123 if (nla_put_u32(msg, DPLL_A_MODE_SUPPORTED, mode))124 return -EMSGSIZE;125 126 return 0;127}128 129static int130dpll_msg_add_lock_status(struct sk_buff *msg, struct dpll_device *dpll,131 struct netlink_ext_ack *extack)132{133 const struct dpll_device_ops *ops = dpll_device_ops(dpll);134 enum dpll_lock_status_error status_error = 0;135 enum dpll_lock_status status;136 int ret;137 138 ret = ops->lock_status_get(dpll, dpll_priv(dpll), &status,139 &status_error, extack);140 if (ret)141 return ret;142 if (nla_put_u32(msg, DPLL_A_LOCK_STATUS, status))143 return -EMSGSIZE;144 if (status_error &&145 (status == DPLL_LOCK_STATUS_UNLOCKED ||146 status == DPLL_LOCK_STATUS_HOLDOVER) &&147 nla_put_u32(msg, DPLL_A_LOCK_STATUS_ERROR, status_error))148 return -EMSGSIZE;149 150 return 0;151}152 153static int154dpll_msg_add_temp(struct sk_buff *msg, struct dpll_device *dpll,155 struct netlink_ext_ack *extack)156{157 const struct dpll_device_ops *ops = dpll_device_ops(dpll);158 s32 temp;159 int ret;160 161 if (!ops->temp_get)162 return 0;163 ret = ops->temp_get(dpll, dpll_priv(dpll), &temp, extack);164 if (ret)165 return ret;166 if (nla_put_s32(msg, DPLL_A_TEMP, temp))167 return -EMSGSIZE;168 169 return 0;170}171 172static int173dpll_msg_add_pin_prio(struct sk_buff *msg, struct dpll_pin *pin,174 struct dpll_pin_ref *ref,175 struct netlink_ext_ack *extack)176{177 const struct dpll_pin_ops *ops = dpll_pin_ops(ref);178 struct dpll_device *dpll = ref->dpll;179 u32 prio;180 int ret;181 182 if (!ops->prio_get)183 return 0;184 ret = ops->prio_get(pin, dpll_pin_on_dpll_priv(dpll, pin), dpll,185 dpll_priv(dpll), &prio, extack);186 if (ret)187 return ret;188 if (nla_put_u32(msg, DPLL_A_PIN_PRIO, prio))189 return -EMSGSIZE;190 191 return 0;192}193 194static int195dpll_msg_add_pin_on_dpll_state(struct sk_buff *msg, struct dpll_pin *pin,196 struct dpll_pin_ref *ref,197 struct netlink_ext_ack *extack)198{199 const struct dpll_pin_ops *ops = dpll_pin_ops(ref);200 struct dpll_device *dpll = ref->dpll;201 enum dpll_pin_state state;202 int ret;203 204 if (!ops->state_on_dpll_get)205 return 0;206 ret = ops->state_on_dpll_get(pin, dpll_pin_on_dpll_priv(dpll, pin),207 dpll, dpll_priv(dpll), &state, extack);208 if (ret)209 return ret;210 if (nla_put_u32(msg, DPLL_A_PIN_STATE, state))211 return -EMSGSIZE;212 213 return 0;214}215 216static int217dpll_msg_add_pin_direction(struct sk_buff *msg, struct dpll_pin *pin,218 struct dpll_pin_ref *ref,219 struct netlink_ext_ack *extack)220{221 const struct dpll_pin_ops *ops = dpll_pin_ops(ref);222 struct dpll_device *dpll = ref->dpll;223 enum dpll_pin_direction direction;224 int ret;225 226 ret = ops->direction_get(pin, dpll_pin_on_dpll_priv(dpll, pin), dpll,227 dpll_priv(dpll), &direction, extack);228 if (ret)229 return ret;230 if (nla_put_u32(msg, DPLL_A_PIN_DIRECTION, direction))231 return -EMSGSIZE;232 233 return 0;234}235 236static int237dpll_msg_add_pin_phase_adjust(struct sk_buff *msg, struct dpll_pin *pin,238 struct dpll_pin_ref *ref,239 struct netlink_ext_ack *extack)240{241 const struct dpll_pin_ops *ops = dpll_pin_ops(ref);242 struct dpll_device *dpll = ref->dpll;243 s32 phase_adjust;244 int ret;245 246 if (!ops->phase_adjust_get)247 return 0;248 ret = ops->phase_adjust_get(pin, dpll_pin_on_dpll_priv(dpll, pin),249 dpll, dpll_priv(dpll),250 &phase_adjust, extack);251 if (ret)252 return ret;253 if (nla_put_s32(msg, DPLL_A_PIN_PHASE_ADJUST, phase_adjust))254 return -EMSGSIZE;255 256 return 0;257}258 259static int260dpll_msg_add_phase_offset(struct sk_buff *msg, struct dpll_pin *pin,261 struct dpll_pin_ref *ref,262 struct netlink_ext_ack *extack)263{264 const struct dpll_pin_ops *ops = dpll_pin_ops(ref);265 struct dpll_device *dpll = ref->dpll;266 s64 phase_offset;267 int ret;268 269 if (!ops->phase_offset_get)270 return 0;271 ret = ops->phase_offset_get(pin, dpll_pin_on_dpll_priv(dpll, pin),272 dpll, dpll_priv(dpll), &phase_offset,273 extack);274 if (ret)275 return ret;276 if (nla_put_64bit(msg, DPLL_A_PIN_PHASE_OFFSET, sizeof(phase_offset),277 &phase_offset, DPLL_A_PIN_PAD))278 return -EMSGSIZE;279 280 return 0;281}282 283static int dpll_msg_add_ffo(struct sk_buff *msg, struct dpll_pin *pin,284 struct dpll_pin_ref *ref,285 struct netlink_ext_ack *extack)286{287 const struct dpll_pin_ops *ops = dpll_pin_ops(ref);288 struct dpll_device *dpll = ref->dpll;289 s64 ffo;290 int ret;291 292 if (!ops->ffo_get)293 return 0;294 ret = ops->ffo_get(pin, dpll_pin_on_dpll_priv(dpll, pin),295 dpll, dpll_priv(dpll), &ffo, extack);296 if (ret) {297 if (ret == -ENODATA)298 return 0;299 return ret;300 }301 return nla_put_sint(msg, DPLL_A_PIN_FRACTIONAL_FREQUENCY_OFFSET, ffo);302}303 304static int305dpll_msg_add_pin_freq(struct sk_buff *msg, struct dpll_pin *pin,306 struct dpll_pin_ref *ref, struct netlink_ext_ack *extack)307{308 const struct dpll_pin_ops *ops = dpll_pin_ops(ref);309 struct dpll_device *dpll = ref->dpll;310 struct nlattr *nest;311 int fs, ret;312 u64 freq;313 314 if (!ops->frequency_get)315 return 0;316 ret = ops->frequency_get(pin, dpll_pin_on_dpll_priv(dpll, pin), dpll,317 dpll_priv(dpll), &freq, extack);318 if (ret)319 return ret;320 if (nla_put_64bit(msg, DPLL_A_PIN_FREQUENCY, sizeof(freq), &freq,321 DPLL_A_PIN_PAD))322 return -EMSGSIZE;323 for (fs = 0; fs < pin->prop.freq_supported_num; fs++) {324 nest = nla_nest_start(msg, DPLL_A_PIN_FREQUENCY_SUPPORTED);325 if (!nest)326 return -EMSGSIZE;327 freq = pin->prop.freq_supported[fs].min;328 if (nla_put_64bit(msg, DPLL_A_PIN_FREQUENCY_MIN, sizeof(freq),329 &freq, DPLL_A_PIN_PAD)) {330 nla_nest_cancel(msg, nest);331 return -EMSGSIZE;332 }333 freq = pin->prop.freq_supported[fs].max;334 if (nla_put_64bit(msg, DPLL_A_PIN_FREQUENCY_MAX, sizeof(freq),335 &freq, DPLL_A_PIN_PAD)) {336 nla_nest_cancel(msg, nest);337 return -EMSGSIZE;338 }339 nla_nest_end(msg, nest);340 }341 342 return 0;343}344 345static int346dpll_msg_add_pin_esync(struct sk_buff *msg, struct dpll_pin *pin,347 struct dpll_pin_ref *ref, struct netlink_ext_ack *extack)348{349 const struct dpll_pin_ops *ops = dpll_pin_ops(ref);350 struct dpll_device *dpll = ref->dpll;351 struct dpll_pin_esync esync;352 struct nlattr *nest;353 int ret, i;354 355 if (!ops->esync_get)356 return 0;357 ret = ops->esync_get(pin, dpll_pin_on_dpll_priv(dpll, pin), dpll,358 dpll_priv(dpll), &esync, extack);359 if (ret == -EOPNOTSUPP)360 return 0;361 else if (ret)362 return ret;363 if (nla_put_64bit(msg, DPLL_A_PIN_ESYNC_FREQUENCY, sizeof(esync.freq),364 &esync.freq, DPLL_A_PIN_PAD))365 return -EMSGSIZE;366 if (nla_put_u32(msg, DPLL_A_PIN_ESYNC_PULSE, esync.pulse))367 return -EMSGSIZE;368 for (i = 0; i < esync.range_num; i++) {369 nest = nla_nest_start(msg,370 DPLL_A_PIN_ESYNC_FREQUENCY_SUPPORTED);371 if (!nest)372 return -EMSGSIZE;373 if (nla_put_64bit(msg, DPLL_A_PIN_FREQUENCY_MIN,374 sizeof(esync.range[i].min),375 &esync.range[i].min, DPLL_A_PIN_PAD))376 goto nest_cancel;377 if (nla_put_64bit(msg, DPLL_A_PIN_FREQUENCY_MAX,378 sizeof(esync.range[i].max),379 &esync.range[i].max, DPLL_A_PIN_PAD))380 goto nest_cancel;381 nla_nest_end(msg, nest);382 }383 return 0;384 385nest_cancel:386 nla_nest_cancel(msg, nest);387 return -EMSGSIZE;388}389 390static bool dpll_pin_is_freq_supported(struct dpll_pin *pin, u32 freq)391{392 int fs;393 394 for (fs = 0; fs < pin->prop.freq_supported_num; fs++)395 if (freq >= pin->prop.freq_supported[fs].min &&396 freq <= pin->prop.freq_supported[fs].max)397 return true;398 return false;399}400 401static int402dpll_msg_add_pin_parents(struct sk_buff *msg, struct dpll_pin *pin,403 struct dpll_pin_ref *dpll_ref,404 struct netlink_ext_ack *extack)405{406 enum dpll_pin_state state;407 struct dpll_pin_ref *ref;408 struct dpll_pin *ppin;409 struct nlattr *nest;410 unsigned long index;411 int ret;412 413 xa_for_each(&pin->parent_refs, index, ref) {414 const struct dpll_pin_ops *ops = dpll_pin_ops(ref);415 void *parent_priv;416 417 ppin = ref->pin;418 parent_priv = dpll_pin_on_dpll_priv(dpll_ref->dpll, ppin);419 ret = ops->state_on_pin_get(pin,420 dpll_pin_on_pin_priv(ppin, pin),421 ppin, parent_priv, &state, extack);422 if (ret)423 return ret;424 nest = nla_nest_start(msg, DPLL_A_PIN_PARENT_PIN);425 if (!nest)426 return -EMSGSIZE;427 ret = dpll_msg_add_dev_parent_handle(msg, ppin->id);428 if (ret)429 goto nest_cancel;430 if (nla_put_u32(msg, DPLL_A_PIN_STATE, state)) {431 ret = -EMSGSIZE;432 goto nest_cancel;433 }434 nla_nest_end(msg, nest);435 }436 437 return 0;438 439nest_cancel:440 nla_nest_cancel(msg, nest);441 return ret;442}443 444static int445dpll_msg_add_pin_dplls(struct sk_buff *msg, struct dpll_pin *pin,446 struct netlink_ext_ack *extack)447{448 struct dpll_pin_ref *ref;449 struct nlattr *attr;450 unsigned long index;451 int ret;452 453 xa_for_each(&pin->dpll_refs, index, ref) {454 attr = nla_nest_start(msg, DPLL_A_PIN_PARENT_DEVICE);455 if (!attr)456 return -EMSGSIZE;457 ret = dpll_msg_add_dev_parent_handle(msg, ref->dpll->id);458 if (ret)459 goto nest_cancel;460 ret = dpll_msg_add_pin_on_dpll_state(msg, pin, ref, extack);461 if (ret)462 goto nest_cancel;463 ret = dpll_msg_add_pin_prio(msg, pin, ref, extack);464 if (ret)465 goto nest_cancel;466 ret = dpll_msg_add_pin_direction(msg, pin, ref, extack);467 if (ret)468 goto nest_cancel;469 ret = dpll_msg_add_phase_offset(msg, pin, ref, extack);470 if (ret)471 goto nest_cancel;472 nla_nest_end(msg, attr);473 }474 475 return 0;476 477nest_cancel:478 nla_nest_end(msg, attr);479 return ret;480}481 482static int483dpll_cmd_pin_get_one(struct sk_buff *msg, struct dpll_pin *pin,484 struct netlink_ext_ack *extack)485{486 const struct dpll_pin_properties *prop = &pin->prop;487 struct dpll_pin_ref *ref;488 int ret;489 490 ref = dpll_xa_ref_dpll_first(&pin->dpll_refs);491 ASSERT_NOT_NULL(ref);492 493 ret = dpll_msg_add_pin_handle(msg, pin);494 if (ret)495 return ret;496 if (nla_put_string(msg, DPLL_A_PIN_MODULE_NAME,497 module_name(pin->module)))498 return -EMSGSIZE;499 if (nla_put_64bit(msg, DPLL_A_PIN_CLOCK_ID, sizeof(pin->clock_id),500 &pin->clock_id, DPLL_A_PIN_PAD))501 return -EMSGSIZE;502 if (prop->board_label &&503 nla_put_string(msg, DPLL_A_PIN_BOARD_LABEL, prop->board_label))504 return -EMSGSIZE;505 if (prop->panel_label &&506 nla_put_string(msg, DPLL_A_PIN_PANEL_LABEL, prop->panel_label))507 return -EMSGSIZE;508 if (prop->package_label &&509 nla_put_string(msg, DPLL_A_PIN_PACKAGE_LABEL,510 prop->package_label))511 return -EMSGSIZE;512 if (nla_put_u32(msg, DPLL_A_PIN_TYPE, prop->type))513 return -EMSGSIZE;514 if (nla_put_u32(msg, DPLL_A_PIN_CAPABILITIES, prop->capabilities))515 return -EMSGSIZE;516 ret = dpll_msg_add_pin_freq(msg, pin, ref, extack);517 if (ret)518 return ret;519 if (nla_put_s32(msg, DPLL_A_PIN_PHASE_ADJUST_MIN,520 prop->phase_range.min))521 return -EMSGSIZE;522 if (nla_put_s32(msg, DPLL_A_PIN_PHASE_ADJUST_MAX,523 prop->phase_range.max))524 return -EMSGSIZE;525 ret = dpll_msg_add_pin_phase_adjust(msg, pin, ref, extack);526 if (ret)527 return ret;528 ret = dpll_msg_add_ffo(msg, pin, ref, extack);529 if (ret)530 return ret;531 ret = dpll_msg_add_pin_esync(msg, pin, ref, extack);532 if (ret)533 return ret;534 if (xa_empty(&pin->parent_refs))535 ret = dpll_msg_add_pin_dplls(msg, pin, extack);536 else537 ret = dpll_msg_add_pin_parents(msg, pin, ref, extack);538 539 return ret;540}541 542static int543dpll_device_get_one(struct dpll_device *dpll, struct sk_buff *msg,544 struct netlink_ext_ack *extack)545{546 int ret;547 548 ret = dpll_msg_add_dev_handle(msg, dpll);549 if (ret)550 return ret;551 if (nla_put_string(msg, DPLL_A_MODULE_NAME, module_name(dpll->module)))552 return -EMSGSIZE;553 if (nla_put_64bit(msg, DPLL_A_CLOCK_ID, sizeof(dpll->clock_id),554 &dpll->clock_id, DPLL_A_PAD))555 return -EMSGSIZE;556 ret = dpll_msg_add_temp(msg, dpll, extack);557 if (ret)558 return ret;559 ret = dpll_msg_add_lock_status(msg, dpll, extack);560 if (ret)561 return ret;562 ret = dpll_msg_add_mode(msg, dpll, extack);563 if (ret)564 return ret;565 ret = dpll_msg_add_mode_supported(msg, dpll, extack);566 if (ret)567 return ret;568 if (nla_put_u32(msg, DPLL_A_TYPE, dpll->type))569 return -EMSGSIZE;570 571 return 0;572}573 574static int575dpll_device_event_send(enum dpll_cmd event, struct dpll_device *dpll)576{577 struct sk_buff *msg;578 int ret = -ENOMEM;579 void *hdr;580 581 if (WARN_ON(!xa_get_mark(&dpll_device_xa, dpll->id, DPLL_REGISTERED)))582 return -ENODEV;583 msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);584 if (!msg)585 return -ENOMEM;586 hdr = genlmsg_put(msg, 0, 0, &dpll_nl_family, 0, event);587 if (!hdr)588 goto err_free_msg;589 ret = dpll_device_get_one(dpll, msg, NULL);590 if (ret)591 goto err_cancel_msg;592 genlmsg_end(msg, hdr);593 genlmsg_multicast(&dpll_nl_family, msg, 0, 0, GFP_KERNEL);594 595 return 0;596 597err_cancel_msg:598 genlmsg_cancel(msg, hdr);599err_free_msg:600 nlmsg_free(msg);601 602 return ret;603}604 605int dpll_device_create_ntf(struct dpll_device *dpll)606{607 return dpll_device_event_send(DPLL_CMD_DEVICE_CREATE_NTF, dpll);608}609 610int dpll_device_delete_ntf(struct dpll_device *dpll)611{612 return dpll_device_event_send(DPLL_CMD_DEVICE_DELETE_NTF, dpll);613}614 615static int616__dpll_device_change_ntf(struct dpll_device *dpll)617{618 return dpll_device_event_send(DPLL_CMD_DEVICE_CHANGE_NTF, dpll);619}620 621static bool dpll_pin_available(struct dpll_pin *pin)622{623 struct dpll_pin_ref *par_ref;624 unsigned long i;625 626 if (!xa_get_mark(&dpll_pin_xa, pin->id, DPLL_REGISTERED))627 return false;628 xa_for_each(&pin->parent_refs, i, par_ref)629 if (xa_get_mark(&dpll_pin_xa, par_ref->pin->id,630 DPLL_REGISTERED))631 return true;632 xa_for_each(&pin->dpll_refs, i, par_ref)633 if (xa_get_mark(&dpll_device_xa, par_ref->dpll->id,634 DPLL_REGISTERED))635 return true;636 return false;637}638 639/**640 * dpll_device_change_ntf - notify that the dpll device has been changed641 * @dpll: registered dpll pointer642 *643 * Context: acquires and holds a dpll_lock.644 * Return: 0 if succeeds, error code otherwise.645 */646int dpll_device_change_ntf(struct dpll_device *dpll)647{648 int ret;649 650 mutex_lock(&dpll_lock);651 ret = __dpll_device_change_ntf(dpll);652 mutex_unlock(&dpll_lock);653 654 return ret;655}656EXPORT_SYMBOL_GPL(dpll_device_change_ntf);657 658static int659dpll_pin_event_send(enum dpll_cmd event, struct dpll_pin *pin)660{661 struct sk_buff *msg;662 int ret = -ENOMEM;663 void *hdr;664 665 if (!dpll_pin_available(pin))666 return -ENODEV;667 668 msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);669 if (!msg)670 return -ENOMEM;671 672 hdr = genlmsg_put(msg, 0, 0, &dpll_nl_family, 0, event);673 if (!hdr)674 goto err_free_msg;675 ret = dpll_cmd_pin_get_one(msg, pin, NULL);676 if (ret)677 goto err_cancel_msg;678 genlmsg_end(msg, hdr);679 genlmsg_multicast(&dpll_nl_family, msg, 0, 0, GFP_KERNEL);680 681 return 0;682 683err_cancel_msg:684 genlmsg_cancel(msg, hdr);685err_free_msg:686 nlmsg_free(msg);687 688 return ret;689}690 691int dpll_pin_create_ntf(struct dpll_pin *pin)692{693 return dpll_pin_event_send(DPLL_CMD_PIN_CREATE_NTF, pin);694}695 696int dpll_pin_delete_ntf(struct dpll_pin *pin)697{698 return dpll_pin_event_send(DPLL_CMD_PIN_DELETE_NTF, pin);699}700 701static int __dpll_pin_change_ntf(struct dpll_pin *pin)702{703 return dpll_pin_event_send(DPLL_CMD_PIN_CHANGE_NTF, pin);704}705 706/**707 * dpll_pin_change_ntf - notify that the pin has been changed708 * @pin: registered pin pointer709 *710 * Context: acquires and holds a dpll_lock.711 * Return: 0 if succeeds, error code otherwise.712 */713int dpll_pin_change_ntf(struct dpll_pin *pin)714{715 int ret;716 717 mutex_lock(&dpll_lock);718 ret = __dpll_pin_change_ntf(pin);719 mutex_unlock(&dpll_lock);720 721 return ret;722}723EXPORT_SYMBOL_GPL(dpll_pin_change_ntf);724 725static int726dpll_pin_freq_set(struct dpll_pin *pin, struct nlattr *a,727 struct netlink_ext_ack *extack)728{729 u64 freq = nla_get_u64(a), old_freq;730 struct dpll_pin_ref *ref, *failed;731 const struct dpll_pin_ops *ops;732 struct dpll_device *dpll;733 unsigned long i;734 int ret;735 736 if (!dpll_pin_is_freq_supported(pin, freq)) {737 NL_SET_ERR_MSG_ATTR(extack, a, "frequency is not supported by the device");738 return -EINVAL;739 }740 741 xa_for_each(&pin->dpll_refs, i, ref) {742 ops = dpll_pin_ops(ref);743 if (!ops->frequency_set || !ops->frequency_get) {744 NL_SET_ERR_MSG(extack, "frequency set not supported by the device");745 return -EOPNOTSUPP;746 }747 }748 ref = dpll_xa_ref_dpll_first(&pin->dpll_refs);749 ops = dpll_pin_ops(ref);750 dpll = ref->dpll;751 ret = ops->frequency_get(pin, dpll_pin_on_dpll_priv(dpll, pin), dpll,752 dpll_priv(dpll), &old_freq, extack);753 if (ret) {754 NL_SET_ERR_MSG(extack, "unable to get old frequency value");755 return ret;756 }757 if (freq == old_freq)758 return 0;759 760 xa_for_each(&pin->dpll_refs, i, ref) {761 ops = dpll_pin_ops(ref);762 dpll = ref->dpll;763 ret = ops->frequency_set(pin, dpll_pin_on_dpll_priv(dpll, pin),764 dpll, dpll_priv(dpll), freq, extack);765 if (ret) {766 failed = ref;767 NL_SET_ERR_MSG_FMT(extack, "frequency set failed for dpll_id:%u",768 dpll->id);769 goto rollback;770 }771 }772 __dpll_pin_change_ntf(pin);773 774 return 0;775 776rollback:777 xa_for_each(&pin->dpll_refs, i, ref) {778 if (ref == failed)779 break;780 ops = dpll_pin_ops(ref);781 dpll = ref->dpll;782 if (ops->frequency_set(pin, dpll_pin_on_dpll_priv(dpll, pin),783 dpll, dpll_priv(dpll), old_freq, extack))784 NL_SET_ERR_MSG(extack, "set frequency rollback failed");785 }786 return ret;787}788 789static int790dpll_pin_esync_set(struct dpll_pin *pin, struct nlattr *a,791 struct netlink_ext_ack *extack)792{793 struct dpll_pin_ref *ref, *failed;794 const struct dpll_pin_ops *ops;795 struct dpll_pin_esync esync;796 u64 freq = nla_get_u64(a);797 struct dpll_device *dpll;798 bool supported = false;799 unsigned long i;800 int ret;801 802 xa_for_each(&pin->dpll_refs, i, ref) {803 ops = dpll_pin_ops(ref);804 if (!ops->esync_set || !ops->esync_get) {805 NL_SET_ERR_MSG(extack,806 "embedded sync feature is not supported by this device");807 return -EOPNOTSUPP;808 }809 }810 ref = dpll_xa_ref_dpll_first(&pin->dpll_refs);811 ops = dpll_pin_ops(ref);812 dpll = ref->dpll;813 ret = ops->esync_get(pin, dpll_pin_on_dpll_priv(dpll, pin), dpll,814 dpll_priv(dpll), &esync, extack);815 if (ret) {816 NL_SET_ERR_MSG(extack, "unable to get current embedded sync frequency value");817 return ret;818 }819 if (freq == esync.freq)820 return 0;821 for (i = 0; i < esync.range_num; i++)822 if (freq <= esync.range[i].max && freq >= esync.range[i].min)823 supported = true;824 if (!supported) {825 NL_SET_ERR_MSG_ATTR(extack, a,826 "requested embedded sync frequency value is not supported by this device");827 return -EINVAL;828 }829 830 xa_for_each(&pin->dpll_refs, i, ref) {831 void *pin_dpll_priv;832 833 ops = dpll_pin_ops(ref);834 dpll = ref->dpll;835 pin_dpll_priv = dpll_pin_on_dpll_priv(dpll, pin);836 ret = ops->esync_set(pin, pin_dpll_priv, dpll, dpll_priv(dpll),837 freq, extack);838 if (ret) {839 failed = ref;840 NL_SET_ERR_MSG_FMT(extack,841 "embedded sync frequency set failed for dpll_id: %u",842 dpll->id);843 goto rollback;844 }845 }846 __dpll_pin_change_ntf(pin);847 848 return 0;849 850rollback:851 xa_for_each(&pin->dpll_refs, i, ref) {852 void *pin_dpll_priv;853 854 if (ref == failed)855 break;856 ops = dpll_pin_ops(ref);857 dpll = ref->dpll;858 pin_dpll_priv = dpll_pin_on_dpll_priv(dpll, pin);859 if (ops->esync_set(pin, pin_dpll_priv, dpll, dpll_priv(dpll),860 esync.freq, extack))861 NL_SET_ERR_MSG(extack, "set embedded sync frequency rollback failed");862 }863 return ret;864}865 866static int867dpll_pin_on_pin_state_set(struct dpll_pin *pin, u32 parent_idx,868 enum dpll_pin_state state,869 struct netlink_ext_ack *extack)870{871 struct dpll_pin_ref *parent_ref;872 const struct dpll_pin_ops *ops;873 struct dpll_pin_ref *dpll_ref;874 void *pin_priv, *parent_priv;875 struct dpll_pin *parent;876 unsigned long i;877 int ret;878 879 if (!(DPLL_PIN_CAPABILITIES_STATE_CAN_CHANGE &880 pin->prop.capabilities)) {881 NL_SET_ERR_MSG(extack, "state changing is not allowed");882 return -EOPNOTSUPP;883 }884 parent = xa_load(&dpll_pin_xa, parent_idx);885 if (!parent)886 return -EINVAL;887 parent_ref = xa_load(&pin->parent_refs, parent->pin_idx);888 if (!parent_ref)889 return -EINVAL;890 xa_for_each(&parent->dpll_refs, i, dpll_ref) {891 ops = dpll_pin_ops(parent_ref);892 if (!ops->state_on_pin_set)893 return -EOPNOTSUPP;894 pin_priv = dpll_pin_on_pin_priv(parent, pin);895 parent_priv = dpll_pin_on_dpll_priv(dpll_ref->dpll, parent);896 ret = ops->state_on_pin_set(pin, pin_priv, parent, parent_priv,897 state, extack);898 if (ret)899 return ret;900 }901 __dpll_pin_change_ntf(pin);902 903 return 0;904}905 906static int907dpll_pin_state_set(struct dpll_device *dpll, struct dpll_pin *pin,908 enum dpll_pin_state state,909 struct netlink_ext_ack *extack)910{911 const struct dpll_pin_ops *ops;912 struct dpll_pin_ref *ref;913 int ret;914 915 if (!(DPLL_PIN_CAPABILITIES_STATE_CAN_CHANGE &916 pin->prop.capabilities)) {917 NL_SET_ERR_MSG(extack, "state changing is not allowed");918 return -EOPNOTSUPP;919 }920 ref = xa_load(&pin->dpll_refs, dpll->id);921 ASSERT_NOT_NULL(ref);922 ops = dpll_pin_ops(ref);923 if (!ops->state_on_dpll_set)924 return -EOPNOTSUPP;925 ret = ops->state_on_dpll_set(pin, dpll_pin_on_dpll_priv(dpll, pin),926 dpll, dpll_priv(dpll), state, extack);927 if (ret)928 return ret;929 __dpll_pin_change_ntf(pin);930 931 return 0;932}933 934static int935dpll_pin_prio_set(struct dpll_device *dpll, struct dpll_pin *pin,936 u32 prio, struct netlink_ext_ack *extack)937{938 const struct dpll_pin_ops *ops;939 struct dpll_pin_ref *ref;940 int ret;941 942 if (!(DPLL_PIN_CAPABILITIES_PRIORITY_CAN_CHANGE &943 pin->prop.capabilities)) {944 NL_SET_ERR_MSG(extack, "prio changing is not allowed");945 return -EOPNOTSUPP;946 }947 ref = xa_load(&pin->dpll_refs, dpll->id);948 ASSERT_NOT_NULL(ref);949 ops = dpll_pin_ops(ref);950 if (!ops->prio_set)951 return -EOPNOTSUPP;952 ret = ops->prio_set(pin, dpll_pin_on_dpll_priv(dpll, pin), dpll,953 dpll_priv(dpll), prio, extack);954 if (ret)955 return ret;956 __dpll_pin_change_ntf(pin);957 958 return 0;959}960 961static int962dpll_pin_direction_set(struct dpll_pin *pin, struct dpll_device *dpll,963 enum dpll_pin_direction direction,964 struct netlink_ext_ack *extack)965{966 const struct dpll_pin_ops *ops;967 struct dpll_pin_ref *ref;968 int ret;969 970 if (!(DPLL_PIN_CAPABILITIES_DIRECTION_CAN_CHANGE &971 pin->prop.capabilities)) {972 NL_SET_ERR_MSG(extack, "direction changing is not allowed");973 return -EOPNOTSUPP;974 }975 ref = xa_load(&pin->dpll_refs, dpll->id);976 ASSERT_NOT_NULL(ref);977 ops = dpll_pin_ops(ref);978 if (!ops->direction_set)979 return -EOPNOTSUPP;980 ret = ops->direction_set(pin, dpll_pin_on_dpll_priv(dpll, pin),981 dpll, dpll_priv(dpll), direction, extack);982 if (ret)983 return ret;984 __dpll_pin_change_ntf(pin);985 986 return 0;987}988 989static int990dpll_pin_phase_adj_set(struct dpll_pin *pin, struct nlattr *phase_adj_attr,991 struct netlink_ext_ack *extack)992{993 struct dpll_pin_ref *ref, *failed;994 const struct dpll_pin_ops *ops;995 s32 phase_adj, old_phase_adj;996 struct dpll_device *dpll;997 unsigned long i;998 int ret;999 1000 phase_adj = nla_get_s32(phase_adj_attr);1001 if (phase_adj > pin->prop.phase_range.max ||1002 phase_adj < pin->prop.phase_range.min) {1003 NL_SET_ERR_MSG_ATTR(extack, phase_adj_attr,1004 "phase adjust value not supported");1005 return -EINVAL;1006 }1007 1008 xa_for_each(&pin->dpll_refs, i, ref) {1009 ops = dpll_pin_ops(ref);1010 if (!ops->phase_adjust_set || !ops->phase_adjust_get) {1011 NL_SET_ERR_MSG(extack, "phase adjust not supported");1012 return -EOPNOTSUPP;1013 }1014 }1015 ref = dpll_xa_ref_dpll_first(&pin->dpll_refs);1016 ops = dpll_pin_ops(ref);1017 dpll = ref->dpll;1018 ret = ops->phase_adjust_get(pin, dpll_pin_on_dpll_priv(dpll, pin),1019 dpll, dpll_priv(dpll), &old_phase_adj,1020 extack);1021 if (ret) {1022 NL_SET_ERR_MSG(extack, "unable to get old phase adjust value");1023 return ret;1024 }1025 if (phase_adj == old_phase_adj)1026 return 0;1027 1028 xa_for_each(&pin->dpll_refs, i, ref) {1029 ops = dpll_pin_ops(ref);1030 dpll = ref->dpll;1031 ret = ops->phase_adjust_set(pin,1032 dpll_pin_on_dpll_priv(dpll, pin),1033 dpll, dpll_priv(dpll), phase_adj,1034 extack);1035 if (ret) {1036 failed = ref;1037 NL_SET_ERR_MSG_FMT(extack,1038 "phase adjust set failed for dpll_id:%u",1039 dpll->id);1040 goto rollback;1041 }1042 }1043 __dpll_pin_change_ntf(pin);1044 1045 return 0;1046 1047rollback:1048 xa_for_each(&pin->dpll_refs, i, ref) {1049 if (ref == failed)1050 break;1051 ops = dpll_pin_ops(ref);1052 dpll = ref->dpll;1053 if (ops->phase_adjust_set(pin, dpll_pin_on_dpll_priv(dpll, pin),1054 dpll, dpll_priv(dpll), old_phase_adj,1055 extack))1056 NL_SET_ERR_MSG(extack, "set phase adjust rollback failed");1057 }1058 return ret;1059}1060 1061static int1062dpll_pin_parent_device_set(struct dpll_pin *pin, struct nlattr *parent_nest,1063 struct netlink_ext_ack *extack)1064{1065 struct nlattr *tb[DPLL_A_PIN_MAX + 1];1066 enum dpll_pin_direction direction;1067 enum dpll_pin_state state;1068 struct dpll_pin_ref *ref;1069 struct dpll_device *dpll;1070 u32 pdpll_idx, prio;1071 int ret;1072 1073 nla_parse_nested(tb, DPLL_A_PIN_MAX, parent_nest,1074 dpll_pin_parent_device_nl_policy, extack);1075 if (!tb[DPLL_A_PIN_PARENT_ID]) {1076 NL_SET_ERR_MSG(extack, "device parent id expected");1077 return -EINVAL;1078 }1079 pdpll_idx = nla_get_u32(tb[DPLL_A_PIN_PARENT_ID]);1080 dpll = xa_load(&dpll_device_xa, pdpll_idx);1081 if (!dpll) {1082 NL_SET_ERR_MSG(extack, "parent device not found");1083 return -EINVAL;1084 }1085 ref = xa_load(&pin->dpll_refs, dpll->id);1086 if (!ref) {1087 NL_SET_ERR_MSG(extack, "pin not connected to given parent device");1088 return -EINVAL;1089 }1090 if (tb[DPLL_A_PIN_STATE]) {1091 state = nla_get_u32(tb[DPLL_A_PIN_STATE]);1092 ret = dpll_pin_state_set(dpll, pin, state, extack);1093 if (ret)1094 return ret;1095 }1096 if (tb[DPLL_A_PIN_PRIO]) {1097 prio = nla_get_u32(tb[DPLL_A_PIN_PRIO]);1098 ret = dpll_pin_prio_set(dpll, pin, prio, extack);1099 if (ret)1100 return ret;1101 }1102 if (tb[DPLL_A_PIN_DIRECTION]) {1103 direction = nla_get_u32(tb[DPLL_A_PIN_DIRECTION]);1104 ret = dpll_pin_direction_set(pin, dpll, direction, extack);1105 if (ret)1106 return ret;1107 }1108 return 0;1109}1110 1111static int1112dpll_pin_parent_pin_set(struct dpll_pin *pin, struct nlattr *parent_nest,1113 struct netlink_ext_ack *extack)1114{1115 struct nlattr *tb[DPLL_A_PIN_MAX + 1];1116 u32 ppin_idx;1117 int ret;1118 1119 nla_parse_nested(tb, DPLL_A_PIN_MAX, parent_nest,1120 dpll_pin_parent_pin_nl_policy, extack);1121 if (!tb[DPLL_A_PIN_PARENT_ID]) {1122 NL_SET_ERR_MSG(extack, "device parent id expected");1123 return -EINVAL;1124 }1125 ppin_idx = nla_get_u32(tb[DPLL_A_PIN_PARENT_ID]);1126 1127 if (tb[DPLL_A_PIN_STATE]) {1128 enum dpll_pin_state state = nla_get_u32(tb[DPLL_A_PIN_STATE]);1129 1130 ret = dpll_pin_on_pin_state_set(pin, ppin_idx, state, extack);1131 if (ret)1132 return ret;1133 }1134 1135 return 0;1136}1137 1138static int1139dpll_pin_set_from_nlattr(struct dpll_pin *pin, struct genl_info *info)1140{1141 struct nlattr *a;1142 int rem, ret;1143 1144 nla_for_each_attr(a, genlmsg_data(info->genlhdr),1145 genlmsg_len(info->genlhdr), rem) {1146 switch (nla_type(a)) {1147 case DPLL_A_PIN_FREQUENCY:1148 ret = dpll_pin_freq_set(pin, a, info->extack);1149 if (ret)1150 return ret;1151 break;1152 case DPLL_A_PIN_PHASE_ADJUST:1153 ret = dpll_pin_phase_adj_set(pin, a, info->extack);1154 if (ret)1155 return ret;1156 break;1157 case DPLL_A_PIN_PARENT_DEVICE:1158 ret = dpll_pin_parent_device_set(pin, a, info->extack);1159 if (ret)1160 return ret;1161 break;1162 case DPLL_A_PIN_PARENT_PIN:1163 ret = dpll_pin_parent_pin_set(pin, a, info->extack);1164 if (ret)1165 return ret;1166 break;1167 case DPLL_A_PIN_ESYNC_FREQUENCY:1168 ret = dpll_pin_esync_set(pin, a, info->extack);1169 if (ret)1170 return ret;1171 break;1172 }1173 }1174 1175 return 0;1176}1177 1178static struct dpll_pin *1179dpll_pin_find(u64 clock_id, struct nlattr *mod_name_attr,1180 enum dpll_pin_type type, struct nlattr *board_label,1181 struct nlattr *panel_label, struct nlattr *package_label,1182 struct netlink_ext_ack *extack)1183{1184 bool board_match, panel_match, package_match;1185 struct dpll_pin *pin_match = NULL, *pin;1186 const struct dpll_pin_properties *prop;1187 bool cid_match, mod_match, type_match;1188 unsigned long i;1189 1190 xa_for_each_marked(&dpll_pin_xa, i, pin, DPLL_REGISTERED) {1191 prop = &pin->prop;1192 cid_match = clock_id ? pin->clock_id == clock_id : true;1193 mod_match = mod_name_attr && module_name(pin->module) ?1194 !nla_strcmp(mod_name_attr,1195 module_name(pin->module)) : true;1196 type_match = type ? prop->type == type : true;1197 board_match = board_label ? (prop->board_label ?1198 !nla_strcmp(board_label, prop->board_label) : false) :1199 true;1200 panel_match = panel_label ? (prop->panel_label ?1201 !nla_strcmp(panel_label, prop->panel_label) : false) :1202 true;1203 package_match = package_label ? (prop->package_label ?1204 !nla_strcmp(package_label, prop->package_label) :1205 false) : true;1206 if (cid_match && mod_match && type_match && board_match &&1207 panel_match && package_match) {1208 if (pin_match) {1209 NL_SET_ERR_MSG(extack, "multiple matches");1210 return ERR_PTR(-EINVAL);1211 }1212 pin_match = pin;1213 }1214 }1215 if (!pin_match) {1216 NL_SET_ERR_MSG(extack, "not found");1217 return ERR_PTR(-ENODEV);1218 }1219 return pin_match;1220}1221 1222static struct dpll_pin *dpll_pin_find_from_nlattr(struct genl_info *info)1223{1224 struct nlattr *attr, *mod_name_attr = NULL, *board_label_attr = NULL,1225 *panel_label_attr = NULL, *package_label_attr = NULL;1226 enum dpll_pin_type type = 0;1227 u64 clock_id = 0;1228 int rem = 0;1229 1230 nla_for_each_attr(attr, genlmsg_data(info->genlhdr),1231 genlmsg_len(info->genlhdr), rem) {1232 switch (nla_type(attr)) {1233 case DPLL_A_PIN_CLOCK_ID:1234 if (clock_id)1235 goto duplicated_attr;1236 clock_id = nla_get_u64(attr);1237 break;1238 case DPLL_A_PIN_MODULE_NAME:1239 if (mod_name_attr)1240 goto duplicated_attr;1241 mod_name_attr = attr;1242 break;1243 case DPLL_A_PIN_TYPE:1244 if (type)1245 goto duplicated_attr;1246 type = nla_get_u32(attr);1247 break;1248 case DPLL_A_PIN_BOARD_LABEL:1249 if (board_label_attr)1250 goto duplicated_attr;1251 board_label_attr = attr;1252 break;1253 case DPLL_A_PIN_PANEL_LABEL:1254 if (panel_label_attr)1255 goto duplicated_attr;1256 panel_label_attr = attr;1257 break;1258 case DPLL_A_PIN_PACKAGE_LABEL:1259 if (package_label_attr)1260 goto duplicated_attr;1261 package_label_attr = attr;1262 break;1263 default:1264 break;1265 }1266 }1267 if (!(clock_id || mod_name_attr || board_label_attr ||1268 panel_label_attr || package_label_attr)) {1269 NL_SET_ERR_MSG(info->extack, "missing attributes");1270 return ERR_PTR(-EINVAL);1271 }1272 return dpll_pin_find(clock_id, mod_name_attr, type, board_label_attr,1273 panel_label_attr, package_label_attr,1274 info->extack);1275duplicated_attr:1276 NL_SET_ERR_MSG(info->extack, "duplicated attribute");1277 return ERR_PTR(-EINVAL);1278}1279 1280int dpll_nl_pin_id_get_doit(struct sk_buff *skb, struct genl_info *info)1281{1282 struct dpll_pin *pin;1283 struct sk_buff *msg;1284 struct nlattr *hdr;1285 int ret;1286 1287 msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);1288 if (!msg)1289 return -ENOMEM;1290 hdr = genlmsg_put_reply(msg, info, &dpll_nl_family, 0,1291 DPLL_CMD_PIN_ID_GET);1292 if (!hdr) {1293 nlmsg_free(msg);1294 return -EMSGSIZE;1295 }1296 pin = dpll_pin_find_from_nlattr(info);1297 if (!IS_ERR(pin)) {1298 if (!dpll_pin_available(pin)) {1299 nlmsg_free(msg);1300 return -ENODEV;1301 }1302 ret = dpll_msg_add_pin_handle(msg, pin);1303 if (ret) {1304 nlmsg_free(msg);1305 return ret;1306 }1307 }1308 genlmsg_end(msg, hdr);1309 1310 return genlmsg_reply(msg, info);1311}1312 1313int dpll_nl_pin_get_doit(struct sk_buff *skb, struct genl_info *info)1314{1315 struct dpll_pin *pin = info->user_ptr[0];1316 struct sk_buff *msg;1317 struct nlattr *hdr;1318 int ret;1319 1320 if (!pin)1321 return -ENODEV;1322 msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);1323 if (!msg)1324 return -ENOMEM;1325 hdr = genlmsg_put_reply(msg, info, &dpll_nl_family, 0,1326 DPLL_CMD_PIN_GET);1327 if (!hdr) {1328 nlmsg_free(msg);1329 return -EMSGSIZE;1330 }1331 ret = dpll_cmd_pin_get_one(msg, pin, info->extack);1332 if (ret) {1333 nlmsg_free(msg);1334 return ret;1335 }1336 genlmsg_end(msg, hdr);1337 1338 return genlmsg_reply(msg, info);1339}1340 1341int dpll_nl_pin_get_dumpit(struct sk_buff *skb, struct netlink_callback *cb)1342{1343 struct dpll_dump_ctx *ctx = dpll_dump_context(cb);1344 struct dpll_pin *pin;1345 struct nlattr *hdr;1346 unsigned long i;1347 int ret = 0;1348 1349 mutex_lock(&dpll_lock);1350 xa_for_each_marked_start(&dpll_pin_xa, i, pin, DPLL_REGISTERED,1351 ctx->idx) {1352 if (!dpll_pin_available(pin))1353 continue;1354 hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid,1355 cb->nlh->nlmsg_seq,1356 &dpll_nl_family, NLM_F_MULTI,1357 DPLL_CMD_PIN_GET);1358 if (!hdr) {1359 ret = -EMSGSIZE;1360 break;1361 }1362 ret = dpll_cmd_pin_get_one(skb, pin, cb->extack);1363 if (ret) {1364 genlmsg_cancel(skb, hdr);1365 break;1366 }1367 genlmsg_end(skb, hdr);1368 }1369 mutex_unlock(&dpll_lock);1370 1371 if (ret == -EMSGSIZE) {1372 ctx->idx = i;1373 return skb->len;1374 }1375 return ret;1376}1377 1378int dpll_nl_pin_set_doit(struct sk_buff *skb, struct genl_info *info)1379{1380 struct dpll_pin *pin = info->user_ptr[0];1381 1382 return dpll_pin_set_from_nlattr(pin, info);1383}1384 1385static struct dpll_device *1386dpll_device_find(u64 clock_id, struct nlattr *mod_name_attr,1387 enum dpll_type type, struct netlink_ext_ack *extack)1388{1389 struct dpll_device *dpll_match = NULL, *dpll;1390 bool cid_match, mod_match, type_match;1391 unsigned long i;1392 1393 xa_for_each_marked(&dpll_device_xa, i, dpll, DPLL_REGISTERED) {1394 cid_match = clock_id ? dpll->clock_id == clock_id : true;1395 mod_match = mod_name_attr ? (module_name(dpll->module) ?1396 !nla_strcmp(mod_name_attr,1397 module_name(dpll->module)) : false) : true;1398 type_match = type ? dpll->type == type : true;1399 if (cid_match && mod_match && type_match) {1400 if (dpll_match) {1401 NL_SET_ERR_MSG(extack, "multiple matches");1402 return ERR_PTR(-EINVAL);1403 }1404 dpll_match = dpll;1405 }1406 }1407 if (!dpll_match) {1408 NL_SET_ERR_MSG(extack, "not found");1409 return ERR_PTR(-ENODEV);1410 }1411 1412 return dpll_match;1413}1414 1415static struct dpll_device *1416dpll_device_find_from_nlattr(struct genl_info *info)1417{1418 struct nlattr *attr, *mod_name_attr = NULL;1419 enum dpll_type type = 0;1420 u64 clock_id = 0;1421 int rem = 0;1422 1423 nla_for_each_attr(attr, genlmsg_data(info->genlhdr),1424 genlmsg_len(info->genlhdr), rem) {1425 switch (nla_type(attr)) {1426 case DPLL_A_CLOCK_ID:1427 if (clock_id)1428 goto duplicated_attr;1429 clock_id = nla_get_u64(attr);1430 break;1431 case DPLL_A_MODULE_NAME:1432 if (mod_name_attr)1433 goto duplicated_attr;1434 mod_name_attr = attr;1435 break;1436 case DPLL_A_TYPE:1437 if (type)1438 goto duplicated_attr;1439 type = nla_get_u32(attr);1440 break;1441 default:1442 break;1443 }1444 }1445 if (!clock_id && !mod_name_attr && !type) {1446 NL_SET_ERR_MSG(info->extack, "missing attributes");1447 return ERR_PTR(-EINVAL);1448 }1449 return dpll_device_find(clock_id, mod_name_attr, type, info->extack);1450duplicated_attr:1451 NL_SET_ERR_MSG(info->extack, "duplicated attribute");1452 return ERR_PTR(-EINVAL);1453}1454 1455int dpll_nl_device_id_get_doit(struct sk_buff *skb, struct genl_info *info)1456{1457 struct dpll_device *dpll;1458 struct sk_buff *msg;1459 struct nlattr *hdr;1460 int ret;1461 1462 msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);1463 if (!msg)1464 return -ENOMEM;1465 hdr = genlmsg_put_reply(msg, info, &dpll_nl_family, 0,1466 DPLL_CMD_DEVICE_ID_GET);1467 if (!hdr) {1468 nlmsg_free(msg);1469 return -EMSGSIZE;1470 }1471 1472 dpll = dpll_device_find_from_nlattr(info);1473 if (!IS_ERR(dpll)) {1474 ret = dpll_msg_add_dev_handle(msg, dpll);1475 if (ret) {1476 nlmsg_free(msg);1477 return ret;1478 }1479 }1480 genlmsg_end(msg, hdr);1481 1482 return genlmsg_reply(msg, info);1483}1484 1485int dpll_nl_device_get_doit(struct sk_buff *skb, struct genl_info *info)1486{1487 struct dpll_device *dpll = info->user_ptr[0];1488 struct sk_buff *msg;1489 struct nlattr *hdr;1490 int ret;1491 1492 msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);1493 if (!msg)1494 return -ENOMEM;1495 hdr = genlmsg_put_reply(msg, info, &dpll_nl_family, 0,1496 DPLL_CMD_DEVICE_GET);1497 if (!hdr) {1498 nlmsg_free(msg);1499 return -EMSGSIZE;1500 }1501 1502 ret = dpll_device_get_one(dpll, msg, info->extack);1503 if (ret) {1504 nlmsg_free(msg);1505 return ret;1506 }1507 genlmsg_end(msg, hdr);1508 1509 return genlmsg_reply(msg, info);1510}1511 1512int dpll_nl_device_set_doit(struct sk_buff *skb, struct genl_info *info)1513{1514 /* placeholder for set command */1515 return 0;1516}1517 1518int dpll_nl_device_get_dumpit(struct sk_buff *skb, struct netlink_callback *cb)1519{1520 struct dpll_dump_ctx *ctx = dpll_dump_context(cb);1521 struct dpll_device *dpll;1522 struct nlattr *hdr;1523 unsigned long i;1524 int ret = 0;1525 1526 mutex_lock(&dpll_lock);1527 xa_for_each_marked_start(&dpll_device_xa, i, dpll, DPLL_REGISTERED,1528 ctx->idx) {1529 hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid,1530 cb->nlh->nlmsg_seq, &dpll_nl_family,1531 NLM_F_MULTI, DPLL_CMD_DEVICE_GET);1532 if (!hdr) {1533 ret = -EMSGSIZE;1534 break;1535 }1536 ret = dpll_device_get_one(dpll, skb, cb->extack);1537 if (ret) {1538 genlmsg_cancel(skb, hdr);1539 break;1540 }1541 genlmsg_end(skb, hdr);1542 }1543 mutex_unlock(&dpll_lock);1544 1545 if (ret == -EMSGSIZE) {1546 ctx->idx = i;1547 return skb->len;1548 }1549 return ret;1550}1551 1552int dpll_pre_doit(const struct genl_split_ops *ops, struct sk_buff *skb,1553 struct genl_info *info)1554{1555 u32 id;1556 1557 if (GENL_REQ_ATTR_CHECK(info, DPLL_A_ID))1558 return -EINVAL;1559 1560 mutex_lock(&dpll_lock);1561 id = nla_get_u32(info->attrs[DPLL_A_ID]);1562 info->user_ptr[0] = dpll_device_get_by_id(id);1563 if (!info->user_ptr[0]) {1564 NL_SET_ERR_MSG(info->extack, "device not found");1565 goto unlock;1566 }1567 return 0;1568unlock:1569 mutex_unlock(&dpll_lock);1570 return -ENODEV;1571}1572 1573void dpll_post_doit(const struct genl_split_ops *ops, struct sk_buff *skb,1574 struct genl_info *info)1575{1576 mutex_unlock(&dpll_lock);1577}1578 1579int1580dpll_lock_doit(const struct genl_split_ops *ops, struct sk_buff *skb,1581 struct genl_info *info)1582{1583 mutex_lock(&dpll_lock);1584 1585 return 0;1586}1587 1588void1589dpll_unlock_doit(const struct genl_split_ops *ops, struct sk_buff *skb,1590 struct genl_info *info)1591{1592 mutex_unlock(&dpll_lock);1593}1594 1595int dpll_pin_pre_doit(const struct genl_split_ops *ops, struct sk_buff *skb,1596 struct genl_info *info)1597{1598 int ret;1599 1600 mutex_lock(&dpll_lock);1601 if (GENL_REQ_ATTR_CHECK(info, DPLL_A_PIN_ID)) {1602 ret = -EINVAL;1603 goto unlock_dev;1604 }1605 info->user_ptr[0] = xa_load(&dpll_pin_xa,1606 nla_get_u32(info->attrs[DPLL_A_PIN_ID]));1607 if (!info->user_ptr[0] ||1608 !dpll_pin_available(info->user_ptr[0])) {1609 NL_SET_ERR_MSG(info->extack, "pin not found");1610 ret = -ENODEV;1611 goto unlock_dev;1612 }1613 1614 return 0;1615 1616unlock_dev:1617 mutex_unlock(&dpll_lock);1618 return ret;1619}1620 1621void dpll_pin_post_doit(const struct genl_split_ops *ops, struct sk_buff *skb,1622 struct genl_info *info)1623{1624 mutex_unlock(&dpll_lock);1625}1626