| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001 |
- // SPDX-License-Identifier: GPL-2.0
- /*
- * Generic netlink for DPLL management framework
- *
- * Copyright (c) 2023 Meta Platforms, Inc. and affiliates
- * Copyright (c) 2023 Intel and affiliates
- *
- */
- #include <linux/module.h>
- #include <linux/kernel.h>
- #include <linux/netdevice.h>
- #include <net/genetlink.h>
- #include "dpll_core.h"
- #include "dpll_netlink.h"
- #include "dpll_nl.h"
- #include <uapi/linux/dpll.h>
- #define ASSERT_NOT_NULL(ptr) (WARN_ON(!ptr))
- #define xa_for_each_marked_start(xa, index, entry, filter, start) \
- for (index = start, entry = xa_find(xa, &index, ULONG_MAX, filter); \
- entry; entry = xa_find_after(xa, &index, ULONG_MAX, filter))
- struct dpll_dump_ctx {
- unsigned long idx;
- };
- static struct dpll_dump_ctx *dpll_dump_context(struct netlink_callback *cb)
- {
- return (struct dpll_dump_ctx *)cb->ctx;
- }
- static int
- dpll_msg_add_dev_handle(struct sk_buff *msg, struct dpll_device *dpll)
- {
- if (nla_put_u32(msg, DPLL_A_ID, dpll->id))
- return -EMSGSIZE;
- return 0;
- }
- static int
- dpll_msg_add_dev_parent_handle(struct sk_buff *msg, u32 id)
- {
- if (nla_put_u32(msg, DPLL_A_PIN_PARENT_ID, id))
- return -EMSGSIZE;
- return 0;
- }
- static bool dpll_pin_available(struct dpll_pin *pin)
- {
- struct dpll_pin_ref *par_ref;
- unsigned long i;
- if (!xa_get_mark(&dpll_pin_xa, pin->id, DPLL_REGISTERED))
- return false;
- xa_for_each(&pin->parent_refs, i, par_ref)
- if (xa_get_mark(&dpll_pin_xa, par_ref->pin->id,
- DPLL_REGISTERED))
- return true;
- xa_for_each(&pin->dpll_refs, i, par_ref)
- if (xa_get_mark(&dpll_device_xa, par_ref->dpll->id,
- DPLL_REGISTERED))
- return true;
- return false;
- }
- /**
- * dpll_msg_add_pin_handle - attach pin handle attribute to a given message
- * @msg: pointer to sk_buff message to attach a pin handle
- * @pin: pin pointer
- *
- * Return:
- * * 0 - success
- * * -EMSGSIZE - no space in message to attach pin handle
- */
- static int dpll_msg_add_pin_handle(struct sk_buff *msg, struct dpll_pin *pin)
- {
- if (!pin)
- return 0;
- if (nla_put_u32(msg, DPLL_A_PIN_ID, pin->id))
- return -EMSGSIZE;
- return 0;
- }
- static struct dpll_pin *dpll_netdev_pin(const struct net_device *dev)
- {
- return rcu_dereference_rtnl(dev->dpll_pin);
- }
- /**
- * dpll_netdev_pin_handle_size - get size of pin handle attribute of a netdev
- * @dev: netdev from which to get the pin
- *
- * Return: byte size of pin handle attribute, or 0 if @dev has no pin.
- */
- size_t dpll_netdev_pin_handle_size(const struct net_device *dev)
- {
- return dpll_netdev_pin(dev) ? nla_total_size(4) : 0; /* DPLL_A_PIN_ID */
- }
- int dpll_netdev_add_pin_handle(struct sk_buff *msg,
- const struct net_device *dev)
- {
- return dpll_msg_add_pin_handle(msg, dpll_netdev_pin(dev));
- }
- static int
- dpll_msg_add_mode(struct sk_buff *msg, struct dpll_device *dpll,
- struct netlink_ext_ack *extack)
- {
- const struct dpll_device_ops *ops = dpll_device_ops(dpll);
- enum dpll_mode mode;
- int ret;
- ret = ops->mode_get(dpll, dpll_priv(dpll), &mode, extack);
- if (ret)
- return ret;
- if (nla_put_u32(msg, DPLL_A_MODE, mode))
- return -EMSGSIZE;
- return 0;
- }
- static int
- dpll_msg_add_mode_supported(struct sk_buff *msg, struct dpll_device *dpll,
- struct netlink_ext_ack *extack)
- {
- const struct dpll_device_ops *ops = dpll_device_ops(dpll);
- DECLARE_BITMAP(modes, DPLL_MODE_MAX + 1) = { 0 };
- enum dpll_mode mode;
- int ret;
- if (ops->supported_modes_get) {
- ret = ops->supported_modes_get(dpll, dpll_priv(dpll), modes,
- extack);
- if (ret)
- return ret;
- } else {
- /* If the supported modes are not reported by the driver, the
- * only supported mode is the one obtained by mode_get().
- */
- ret = ops->mode_get(dpll, dpll_priv(dpll), &mode, extack);
- if (ret)
- return ret;
- __set_bit(mode, modes);
- }
- for_each_set_bit(mode, modes, DPLL_MODE_MAX + 1)
- if (nla_put_u32(msg, DPLL_A_MODE_SUPPORTED, mode))
- return -EMSGSIZE;
- return 0;
- }
- static int
- dpll_msg_add_phase_offset_monitor(struct sk_buff *msg, struct dpll_device *dpll,
- struct netlink_ext_ack *extack)
- {
- const struct dpll_device_ops *ops = dpll_device_ops(dpll);
- enum dpll_feature_state state;
- int ret;
- if (ops->phase_offset_monitor_set && ops->phase_offset_monitor_get) {
- ret = ops->phase_offset_monitor_get(dpll, dpll_priv(dpll),
- &state, extack);
- if (ret)
- return ret;
- if (nla_put_u32(msg, DPLL_A_PHASE_OFFSET_MONITOR, state))
- return -EMSGSIZE;
- }
- return 0;
- }
- static int
- dpll_msg_add_phase_offset_avg_factor(struct sk_buff *msg,
- struct dpll_device *dpll,
- struct netlink_ext_ack *extack)
- {
- const struct dpll_device_ops *ops = dpll_device_ops(dpll);
- u32 factor;
- int ret;
- if (ops->phase_offset_avg_factor_get) {
- ret = ops->phase_offset_avg_factor_get(dpll, dpll_priv(dpll),
- &factor, extack);
- if (ret)
- return ret;
- if (nla_put_u32(msg, DPLL_A_PHASE_OFFSET_AVG_FACTOR, factor))
- return -EMSGSIZE;
- }
- return 0;
- }
- static int
- dpll_msg_add_lock_status(struct sk_buff *msg, struct dpll_device *dpll,
- struct netlink_ext_ack *extack)
- {
- const struct dpll_device_ops *ops = dpll_device_ops(dpll);
- enum dpll_lock_status_error status_error = 0;
- enum dpll_lock_status status;
- int ret;
- ret = ops->lock_status_get(dpll, dpll_priv(dpll), &status,
- &status_error, extack);
- if (ret)
- return ret;
- if (nla_put_u32(msg, DPLL_A_LOCK_STATUS, status))
- return -EMSGSIZE;
- if (status_error &&
- (status == DPLL_LOCK_STATUS_UNLOCKED ||
- status == DPLL_LOCK_STATUS_HOLDOVER) &&
- nla_put_u32(msg, DPLL_A_LOCK_STATUS_ERROR, status_error))
- return -EMSGSIZE;
- return 0;
- }
- static int
- dpll_msg_add_temp(struct sk_buff *msg, struct dpll_device *dpll,
- struct netlink_ext_ack *extack)
- {
- const struct dpll_device_ops *ops = dpll_device_ops(dpll);
- s32 temp;
- int ret;
- if (!ops->temp_get)
- return 0;
- ret = ops->temp_get(dpll, dpll_priv(dpll), &temp, extack);
- if (ret)
- return ret;
- if (nla_put_s32(msg, DPLL_A_TEMP, temp))
- return -EMSGSIZE;
- return 0;
- }
- static int
- dpll_msg_add_clock_quality_level(struct sk_buff *msg, struct dpll_device *dpll,
- struct netlink_ext_ack *extack)
- {
- DECLARE_BITMAP(qls, DPLL_CLOCK_QUALITY_LEVEL_MAX + 1) = { 0 };
- const struct dpll_device_ops *ops = dpll_device_ops(dpll);
- enum dpll_clock_quality_level ql;
- int ret;
- if (!ops->clock_quality_level_get)
- return 0;
- ret = ops->clock_quality_level_get(dpll, dpll_priv(dpll), qls, extack);
- if (ret)
- return ret;
- for_each_set_bit(ql, qls, DPLL_CLOCK_QUALITY_LEVEL_MAX + 1)
- if (nla_put_u32(msg, DPLL_A_CLOCK_QUALITY_LEVEL, ql))
- return -EMSGSIZE;
- return 0;
- }
- static int
- dpll_msg_add_pin_prio(struct sk_buff *msg, struct dpll_pin *pin,
- struct dpll_pin_ref *ref,
- struct netlink_ext_ack *extack)
- {
- const struct dpll_pin_ops *ops = dpll_pin_ops(ref);
- struct dpll_device *dpll = ref->dpll;
- u32 prio;
- int ret;
- if (!ops->prio_get)
- return 0;
- ret = ops->prio_get(pin, dpll_pin_on_dpll_priv(dpll, pin), dpll,
- dpll_priv(dpll), &prio, extack);
- if (ret)
- return ret;
- if (nla_put_u32(msg, DPLL_A_PIN_PRIO, prio))
- return -EMSGSIZE;
- return 0;
- }
- static int
- dpll_msg_add_pin_on_dpll_state(struct sk_buff *msg, struct dpll_pin *pin,
- struct dpll_pin_ref *ref,
- struct netlink_ext_ack *extack)
- {
- const struct dpll_pin_ops *ops = dpll_pin_ops(ref);
- struct dpll_device *dpll = ref->dpll;
- enum dpll_pin_state state;
- int ret;
- if (!ops->state_on_dpll_get)
- return 0;
- ret = ops->state_on_dpll_get(pin, dpll_pin_on_dpll_priv(dpll, pin),
- dpll, dpll_priv(dpll), &state, extack);
- if (ret)
- return ret;
- if (nla_put_u32(msg, DPLL_A_PIN_STATE, state))
- return -EMSGSIZE;
- return 0;
- }
- static int
- dpll_msg_add_pin_direction(struct sk_buff *msg, struct dpll_pin *pin,
- struct dpll_pin_ref *ref,
- struct netlink_ext_ack *extack)
- {
- const struct dpll_pin_ops *ops = dpll_pin_ops(ref);
- struct dpll_device *dpll = ref->dpll;
- enum dpll_pin_direction direction;
- int ret;
- ret = ops->direction_get(pin, dpll_pin_on_dpll_priv(dpll, pin), dpll,
- dpll_priv(dpll), &direction, extack);
- if (ret)
- return ret;
- if (nla_put_u32(msg, DPLL_A_PIN_DIRECTION, direction))
- return -EMSGSIZE;
- return 0;
- }
- static int
- dpll_msg_add_pin_phase_adjust(struct sk_buff *msg, struct dpll_pin *pin,
- struct dpll_pin_ref *ref,
- struct netlink_ext_ack *extack)
- {
- const struct dpll_pin_ops *ops = dpll_pin_ops(ref);
- struct dpll_device *dpll = ref->dpll;
- s32 phase_adjust;
- int ret;
- if (!ops->phase_adjust_get)
- return 0;
- ret = ops->phase_adjust_get(pin, dpll_pin_on_dpll_priv(dpll, pin),
- dpll, dpll_priv(dpll),
- &phase_adjust, extack);
- if (ret)
- return ret;
- if (nla_put_s32(msg, DPLL_A_PIN_PHASE_ADJUST, phase_adjust))
- return -EMSGSIZE;
- return 0;
- }
- static int
- dpll_msg_add_phase_offset(struct sk_buff *msg, struct dpll_pin *pin,
- struct dpll_pin_ref *ref,
- struct netlink_ext_ack *extack)
- {
- const struct dpll_pin_ops *ops = dpll_pin_ops(ref);
- struct dpll_device *dpll = ref->dpll;
- s64 phase_offset;
- int ret;
- if (!ops->phase_offset_get)
- return 0;
- ret = ops->phase_offset_get(pin, dpll_pin_on_dpll_priv(dpll, pin),
- dpll, dpll_priv(dpll), &phase_offset,
- extack);
- if (ret)
- return ret;
- if (nla_put_64bit(msg, DPLL_A_PIN_PHASE_OFFSET, sizeof(phase_offset),
- &phase_offset, DPLL_A_PIN_PAD))
- return -EMSGSIZE;
- return 0;
- }
- static int dpll_msg_add_ffo(struct sk_buff *msg, struct dpll_pin *pin,
- struct dpll_pin_ref *ref,
- struct netlink_ext_ack *extack)
- {
- const struct dpll_pin_ops *ops = dpll_pin_ops(ref);
- struct dpll_device *dpll = ref->dpll;
- s64 ffo;
- int ret;
- if (!ops->ffo_get)
- return 0;
- ret = ops->ffo_get(pin, dpll_pin_on_dpll_priv(dpll, pin),
- dpll, dpll_priv(dpll), &ffo, extack);
- if (ret) {
- if (ret == -ENODATA)
- return 0;
- return ret;
- }
- /* Put the FFO value in PPM to preserve compatibility with older
- * programs.
- */
- ret = nla_put_sint(msg, DPLL_A_PIN_FRACTIONAL_FREQUENCY_OFFSET,
- div_s64(ffo, 1000000));
- if (ret)
- return -EMSGSIZE;
- return nla_put_sint(msg, DPLL_A_PIN_FRACTIONAL_FREQUENCY_OFFSET_PPT,
- ffo);
- }
- static int
- dpll_msg_add_pin_freq(struct sk_buff *msg, struct dpll_pin *pin,
- struct dpll_pin_ref *ref, struct netlink_ext_ack *extack)
- {
- const struct dpll_pin_ops *ops = dpll_pin_ops(ref);
- struct dpll_device *dpll = ref->dpll;
- struct nlattr *nest;
- int fs, ret;
- u64 freq;
- if (!ops->frequency_get)
- return 0;
- ret = ops->frequency_get(pin, dpll_pin_on_dpll_priv(dpll, pin), dpll,
- dpll_priv(dpll), &freq, extack);
- if (ret)
- return ret;
- if (nla_put_64bit(msg, DPLL_A_PIN_FREQUENCY, sizeof(freq), &freq,
- DPLL_A_PIN_PAD))
- return -EMSGSIZE;
- for (fs = 0; fs < pin->prop.freq_supported_num; fs++) {
- nest = nla_nest_start(msg, DPLL_A_PIN_FREQUENCY_SUPPORTED);
- if (!nest)
- return -EMSGSIZE;
- freq = pin->prop.freq_supported[fs].min;
- if (nla_put_64bit(msg, DPLL_A_PIN_FREQUENCY_MIN, sizeof(freq),
- &freq, DPLL_A_PIN_PAD)) {
- nla_nest_cancel(msg, nest);
- return -EMSGSIZE;
- }
- freq = pin->prop.freq_supported[fs].max;
- if (nla_put_64bit(msg, DPLL_A_PIN_FREQUENCY_MAX, sizeof(freq),
- &freq, DPLL_A_PIN_PAD)) {
- nla_nest_cancel(msg, nest);
- return -EMSGSIZE;
- }
- nla_nest_end(msg, nest);
- }
- return 0;
- }
- static int
- dpll_msg_add_pin_esync(struct sk_buff *msg, struct dpll_pin *pin,
- struct dpll_pin_ref *ref, struct netlink_ext_ack *extack)
- {
- const struct dpll_pin_ops *ops = dpll_pin_ops(ref);
- struct dpll_device *dpll = ref->dpll;
- struct dpll_pin_esync esync;
- struct nlattr *nest;
- int ret, i;
- if (!ops->esync_get)
- return 0;
- ret = ops->esync_get(pin, dpll_pin_on_dpll_priv(dpll, pin), dpll,
- dpll_priv(dpll), &esync, extack);
- if (ret == -EOPNOTSUPP)
- return 0;
- else if (ret)
- return ret;
- if (nla_put_64bit(msg, DPLL_A_PIN_ESYNC_FREQUENCY, sizeof(esync.freq),
- &esync.freq, DPLL_A_PIN_PAD))
- return -EMSGSIZE;
- if (nla_put_u32(msg, DPLL_A_PIN_ESYNC_PULSE, esync.pulse))
- return -EMSGSIZE;
- for (i = 0; i < esync.range_num; i++) {
- nest = nla_nest_start(msg,
- DPLL_A_PIN_ESYNC_FREQUENCY_SUPPORTED);
- if (!nest)
- return -EMSGSIZE;
- if (nla_put_64bit(msg, DPLL_A_PIN_FREQUENCY_MIN,
- sizeof(esync.range[i].min),
- &esync.range[i].min, DPLL_A_PIN_PAD))
- goto nest_cancel;
- if (nla_put_64bit(msg, DPLL_A_PIN_FREQUENCY_MAX,
- sizeof(esync.range[i].max),
- &esync.range[i].max, DPLL_A_PIN_PAD))
- goto nest_cancel;
- nla_nest_end(msg, nest);
- }
- return 0;
- nest_cancel:
- nla_nest_cancel(msg, nest);
- return -EMSGSIZE;
- }
- static int
- dpll_msg_add_pin_ref_sync(struct sk_buff *msg, struct dpll_pin *pin,
- struct dpll_pin_ref *ref,
- struct netlink_ext_ack *extack)
- {
- const struct dpll_pin_ops *ops = dpll_pin_ops(ref);
- struct dpll_device *dpll = ref->dpll;
- void *pin_priv, *ref_sync_pin_priv;
- struct dpll_pin *ref_sync_pin;
- enum dpll_pin_state state;
- struct nlattr *nest;
- unsigned long index;
- int ret;
- pin_priv = dpll_pin_on_dpll_priv(dpll, pin);
- xa_for_each(&pin->ref_sync_pins, index, ref_sync_pin) {
- if (!dpll_pin_available(ref_sync_pin))
- continue;
- ref_sync_pin_priv = dpll_pin_on_dpll_priv(dpll, ref_sync_pin);
- if (WARN_ON(!ops->ref_sync_get))
- return -EOPNOTSUPP;
- ret = ops->ref_sync_get(pin, pin_priv, ref_sync_pin,
- ref_sync_pin_priv, &state, extack);
- if (ret)
- return ret;
- nest = nla_nest_start(msg, DPLL_A_PIN_REFERENCE_SYNC);
- if (!nest)
- return -EMSGSIZE;
- if (nla_put_s32(msg, DPLL_A_PIN_ID, ref_sync_pin->id))
- goto nest_cancel;
- if (nla_put_s32(msg, DPLL_A_PIN_STATE, state))
- goto nest_cancel;
- nla_nest_end(msg, nest);
- }
- return 0;
- nest_cancel:
- nla_nest_cancel(msg, nest);
- return -EMSGSIZE;
- }
- static bool dpll_pin_is_freq_supported(struct dpll_pin *pin, u32 freq)
- {
- int fs;
- for (fs = 0; fs < pin->prop.freq_supported_num; fs++)
- if (freq >= pin->prop.freq_supported[fs].min &&
- freq <= pin->prop.freq_supported[fs].max)
- return true;
- return false;
- }
- static int
- dpll_msg_add_pin_parents(struct sk_buff *msg, struct dpll_pin *pin,
- struct dpll_pin_ref *dpll_ref,
- struct netlink_ext_ack *extack)
- {
- enum dpll_pin_state state;
- struct dpll_pin_ref *ref;
- struct dpll_pin *ppin;
- struct nlattr *nest;
- unsigned long index;
- int ret;
- xa_for_each(&pin->parent_refs, index, ref) {
- const struct dpll_pin_ops *ops = dpll_pin_ops(ref);
- void *parent_priv;
- ppin = ref->pin;
- parent_priv = dpll_pin_on_dpll_priv(dpll_ref->dpll, ppin);
- ret = ops->state_on_pin_get(pin,
- dpll_pin_on_pin_priv(ppin, pin),
- ppin, parent_priv, &state, extack);
- if (ret)
- return ret;
- nest = nla_nest_start(msg, DPLL_A_PIN_PARENT_PIN);
- if (!nest)
- return -EMSGSIZE;
- ret = dpll_msg_add_dev_parent_handle(msg, ppin->id);
- if (ret)
- goto nest_cancel;
- if (nla_put_u32(msg, DPLL_A_PIN_STATE, state)) {
- ret = -EMSGSIZE;
- goto nest_cancel;
- }
- nla_nest_end(msg, nest);
- }
- return 0;
- nest_cancel:
- nla_nest_cancel(msg, nest);
- return ret;
- }
- static int
- dpll_msg_add_pin_dplls(struct sk_buff *msg, struct dpll_pin *pin,
- struct netlink_ext_ack *extack)
- {
- struct dpll_pin_ref *ref;
- struct nlattr *attr;
- unsigned long index;
- int ret;
- xa_for_each(&pin->dpll_refs, index, ref) {
- attr = nla_nest_start(msg, DPLL_A_PIN_PARENT_DEVICE);
- if (!attr)
- return -EMSGSIZE;
- ret = dpll_msg_add_dev_parent_handle(msg, ref->dpll->id);
- if (ret)
- goto nest_cancel;
- ret = dpll_msg_add_pin_on_dpll_state(msg, pin, ref, extack);
- if (ret)
- goto nest_cancel;
- ret = dpll_msg_add_pin_prio(msg, pin, ref, extack);
- if (ret)
- goto nest_cancel;
- ret = dpll_msg_add_pin_direction(msg, pin, ref, extack);
- if (ret)
- goto nest_cancel;
- ret = dpll_msg_add_phase_offset(msg, pin, ref, extack);
- if (ret)
- goto nest_cancel;
- nla_nest_end(msg, attr);
- }
- return 0;
- nest_cancel:
- nla_nest_end(msg, attr);
- return ret;
- }
- static int
- dpll_cmd_pin_get_one(struct sk_buff *msg, struct dpll_pin *pin,
- struct netlink_ext_ack *extack)
- {
- const struct dpll_pin_properties *prop = &pin->prop;
- struct dpll_pin_ref *ref;
- int ret;
- ref = dpll_xa_ref_dpll_first(&pin->dpll_refs);
- ASSERT_NOT_NULL(ref);
- ret = dpll_msg_add_pin_handle(msg, pin);
- if (ret)
- return ret;
- if (nla_put_string(msg, DPLL_A_PIN_MODULE_NAME,
- module_name(pin->module)))
- return -EMSGSIZE;
- if (nla_put_64bit(msg, DPLL_A_PIN_CLOCK_ID, sizeof(pin->clock_id),
- &pin->clock_id, DPLL_A_PIN_PAD))
- return -EMSGSIZE;
- if (prop->board_label &&
- nla_put_string(msg, DPLL_A_PIN_BOARD_LABEL, prop->board_label))
- return -EMSGSIZE;
- if (prop->panel_label &&
- nla_put_string(msg, DPLL_A_PIN_PANEL_LABEL, prop->panel_label))
- return -EMSGSIZE;
- if (prop->package_label &&
- nla_put_string(msg, DPLL_A_PIN_PACKAGE_LABEL,
- prop->package_label))
- return -EMSGSIZE;
- if (nla_put_u32(msg, DPLL_A_PIN_TYPE, prop->type))
- return -EMSGSIZE;
- if (nla_put_u32(msg, DPLL_A_PIN_CAPABILITIES, prop->capabilities))
- return -EMSGSIZE;
- ret = dpll_msg_add_pin_freq(msg, pin, ref, extack);
- if (ret)
- return ret;
- if (prop->phase_gran &&
- nla_put_u32(msg, DPLL_A_PIN_PHASE_ADJUST_GRAN,
- prop->phase_gran))
- return -EMSGSIZE;
- if (nla_put_s32(msg, DPLL_A_PIN_PHASE_ADJUST_MIN,
- prop->phase_range.min))
- return -EMSGSIZE;
- if (nla_put_s32(msg, DPLL_A_PIN_PHASE_ADJUST_MAX,
- prop->phase_range.max))
- return -EMSGSIZE;
- ret = dpll_msg_add_pin_phase_adjust(msg, pin, ref, extack);
- if (ret)
- return ret;
- ret = dpll_msg_add_ffo(msg, pin, ref, extack);
- if (ret)
- return ret;
- ret = dpll_msg_add_pin_esync(msg, pin, ref, extack);
- if (ret)
- return ret;
- if (!xa_empty(&pin->ref_sync_pins))
- ret = dpll_msg_add_pin_ref_sync(msg, pin, ref, extack);
- if (ret)
- return ret;
- if (xa_empty(&pin->parent_refs))
- ret = dpll_msg_add_pin_dplls(msg, pin, extack);
- else
- ret = dpll_msg_add_pin_parents(msg, pin, ref, extack);
- return ret;
- }
- static int
- dpll_device_get_one(struct dpll_device *dpll, struct sk_buff *msg,
- struct netlink_ext_ack *extack)
- {
- int ret;
- ret = dpll_msg_add_dev_handle(msg, dpll);
- if (ret)
- return ret;
- if (nla_put_string(msg, DPLL_A_MODULE_NAME, module_name(dpll->module)))
- return -EMSGSIZE;
- if (nla_put_64bit(msg, DPLL_A_CLOCK_ID, sizeof(dpll->clock_id),
- &dpll->clock_id, DPLL_A_PAD))
- return -EMSGSIZE;
- ret = dpll_msg_add_temp(msg, dpll, extack);
- if (ret)
- return ret;
- ret = dpll_msg_add_lock_status(msg, dpll, extack);
- if (ret)
- return ret;
- ret = dpll_msg_add_clock_quality_level(msg, dpll, extack);
- if (ret)
- return ret;
- ret = dpll_msg_add_mode(msg, dpll, extack);
- if (ret)
- return ret;
- ret = dpll_msg_add_mode_supported(msg, dpll, extack);
- if (ret)
- return ret;
- if (nla_put_u32(msg, DPLL_A_TYPE, dpll->type))
- return -EMSGSIZE;
- ret = dpll_msg_add_phase_offset_monitor(msg, dpll, extack);
- if (ret)
- return ret;
- ret = dpll_msg_add_phase_offset_avg_factor(msg, dpll, extack);
- if (ret)
- return ret;
- return 0;
- }
- static int
- dpll_device_event_send(enum dpll_cmd event, struct dpll_device *dpll)
- {
- struct sk_buff *msg;
- int ret = -ENOMEM;
- void *hdr;
- if (WARN_ON(!xa_get_mark(&dpll_device_xa, dpll->id, DPLL_REGISTERED)))
- return -ENODEV;
- msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
- if (!msg)
- return -ENOMEM;
- hdr = genlmsg_put(msg, 0, 0, &dpll_nl_family, 0, event);
- if (!hdr)
- goto err_free_msg;
- ret = dpll_device_get_one(dpll, msg, NULL);
- if (ret)
- goto err_cancel_msg;
- genlmsg_end(msg, hdr);
- genlmsg_multicast(&dpll_nl_family, msg, 0, 0, GFP_KERNEL);
- return 0;
- err_cancel_msg:
- genlmsg_cancel(msg, hdr);
- err_free_msg:
- nlmsg_free(msg);
- return ret;
- }
- int dpll_device_create_ntf(struct dpll_device *dpll)
- {
- dpll_device_notify(dpll, DPLL_DEVICE_CREATED);
- return dpll_device_event_send(DPLL_CMD_DEVICE_CREATE_NTF, dpll);
- }
- int dpll_device_delete_ntf(struct dpll_device *dpll)
- {
- dpll_device_notify(dpll, DPLL_DEVICE_DELETED);
- return dpll_device_event_send(DPLL_CMD_DEVICE_DELETE_NTF, dpll);
- }
- static int
- __dpll_device_change_ntf(struct dpll_device *dpll)
- {
- dpll_device_notify(dpll, DPLL_DEVICE_CHANGED);
- return dpll_device_event_send(DPLL_CMD_DEVICE_CHANGE_NTF, dpll);
- }
- /**
- * dpll_device_change_ntf - notify that the dpll device has been changed
- * @dpll: registered dpll pointer
- *
- * Context: acquires and holds a dpll_lock.
- * Return: 0 if succeeds, error code otherwise.
- */
- int dpll_device_change_ntf(struct dpll_device *dpll)
- {
- int ret;
- mutex_lock(&dpll_lock);
- ret = __dpll_device_change_ntf(dpll);
- mutex_unlock(&dpll_lock);
- return ret;
- }
- EXPORT_SYMBOL_GPL(dpll_device_change_ntf);
- static int
- dpll_pin_event_send(enum dpll_cmd event, struct dpll_pin *pin)
- {
- struct sk_buff *msg;
- int ret = -ENOMEM;
- void *hdr;
- if (!dpll_pin_available(pin))
- return -ENODEV;
- msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
- if (!msg)
- return -ENOMEM;
- hdr = genlmsg_put(msg, 0, 0, &dpll_nl_family, 0, event);
- if (!hdr)
- goto err_free_msg;
- ret = dpll_cmd_pin_get_one(msg, pin, NULL);
- if (ret)
- goto err_cancel_msg;
- genlmsg_end(msg, hdr);
- genlmsg_multicast(&dpll_nl_family, msg, 0, 0, GFP_KERNEL);
- return 0;
- err_cancel_msg:
- genlmsg_cancel(msg, hdr);
- err_free_msg:
- nlmsg_free(msg);
- return ret;
- }
- int dpll_pin_create_ntf(struct dpll_pin *pin)
- {
- dpll_pin_notify(pin, DPLL_PIN_CREATED);
- return dpll_pin_event_send(DPLL_CMD_PIN_CREATE_NTF, pin);
- }
- int dpll_pin_delete_ntf(struct dpll_pin *pin)
- {
- dpll_pin_notify(pin, DPLL_PIN_DELETED);
- return dpll_pin_event_send(DPLL_CMD_PIN_DELETE_NTF, pin);
- }
- int __dpll_pin_change_ntf(struct dpll_pin *pin)
- {
- dpll_pin_notify(pin, DPLL_PIN_CHANGED);
- return dpll_pin_event_send(DPLL_CMD_PIN_CHANGE_NTF, pin);
- }
- /**
- * dpll_pin_change_ntf - notify that the pin has been changed
- * @pin: registered pin pointer
- *
- * Context: acquires and holds a dpll_lock.
- * Return: 0 if succeeds, error code otherwise.
- */
- int dpll_pin_change_ntf(struct dpll_pin *pin)
- {
- int ret;
- mutex_lock(&dpll_lock);
- ret = __dpll_pin_change_ntf(pin);
- mutex_unlock(&dpll_lock);
- return ret;
- }
- EXPORT_SYMBOL_GPL(dpll_pin_change_ntf);
- static int
- dpll_mode_set(struct dpll_device *dpll, struct nlattr *a,
- struct netlink_ext_ack *extack)
- {
- const struct dpll_device_ops *ops = dpll_device_ops(dpll);
- DECLARE_BITMAP(modes, DPLL_MODE_MAX + 1) = { 0 };
- enum dpll_mode mode = nla_get_u32(a), old_mode;
- int ret;
- if (!(ops->mode_set && ops->supported_modes_get)) {
- NL_SET_ERR_MSG_ATTR(extack, a,
- "dpll device does not support mode switch");
- return -EOPNOTSUPP;
- }
- ret = ops->mode_get(dpll, dpll_priv(dpll), &old_mode, extack);
- if (ret) {
- NL_SET_ERR_MSG(extack, "unable to get current mode");
- return ret;
- }
- if (mode == old_mode)
- return 0;
- ret = ops->supported_modes_get(dpll, dpll_priv(dpll), modes, extack);
- if (ret) {
- NL_SET_ERR_MSG(extack, "unable to get supported modes");
- return ret;
- }
- if (!test_bit(mode, modes)) {
- NL_SET_ERR_MSG(extack,
- "dpll device does not support requested mode");
- return -EINVAL;
- }
- return ops->mode_set(dpll, dpll_priv(dpll), mode, extack);
- }
- static int
- dpll_phase_offset_monitor_set(struct dpll_device *dpll, struct nlattr *a,
- struct netlink_ext_ack *extack)
- {
- const struct dpll_device_ops *ops = dpll_device_ops(dpll);
- enum dpll_feature_state state = nla_get_u32(a), old_state;
- int ret;
- if (!(ops->phase_offset_monitor_set && ops->phase_offset_monitor_get)) {
- NL_SET_ERR_MSG_ATTR(extack, a, "dpll device not capable of phase offset monitor");
- return -EOPNOTSUPP;
- }
- ret = ops->phase_offset_monitor_get(dpll, dpll_priv(dpll), &old_state,
- extack);
- if (ret) {
- NL_SET_ERR_MSG(extack, "unable to get current state of phase offset monitor");
- return ret;
- }
- if (state == old_state)
- return 0;
- return ops->phase_offset_monitor_set(dpll, dpll_priv(dpll), state,
- extack);
- }
- static int
- dpll_phase_offset_avg_factor_set(struct dpll_device *dpll, struct nlattr *a,
- struct netlink_ext_ack *extack)
- {
- const struct dpll_device_ops *ops = dpll_device_ops(dpll);
- u32 factor = nla_get_u32(a);
- if (!ops->phase_offset_avg_factor_set) {
- NL_SET_ERR_MSG_ATTR(extack, a,
- "device not capable of changing phase offset average factor");
- return -EOPNOTSUPP;
- }
- return ops->phase_offset_avg_factor_set(dpll, dpll_priv(dpll), factor,
- extack);
- }
- static int
- dpll_pin_freq_set(struct dpll_pin *pin, struct nlattr *a,
- struct netlink_ext_ack *extack)
- {
- u64 freq = nla_get_u64(a), old_freq;
- struct dpll_pin_ref *ref, *failed;
- const struct dpll_pin_ops *ops;
- struct dpll_device *dpll;
- unsigned long i;
- int ret;
- if (!dpll_pin_is_freq_supported(pin, freq)) {
- NL_SET_ERR_MSG_ATTR(extack, a, "frequency is not supported by the device");
- return -EINVAL;
- }
- xa_for_each(&pin->dpll_refs, i, ref) {
- ops = dpll_pin_ops(ref);
- if (!ops->frequency_set || !ops->frequency_get) {
- NL_SET_ERR_MSG(extack, "frequency set not supported by the device");
- return -EOPNOTSUPP;
- }
- }
- ref = dpll_xa_ref_dpll_first(&pin->dpll_refs);
- ops = dpll_pin_ops(ref);
- dpll = ref->dpll;
- ret = ops->frequency_get(pin, dpll_pin_on_dpll_priv(dpll, pin), dpll,
- dpll_priv(dpll), &old_freq, extack);
- if (ret) {
- NL_SET_ERR_MSG(extack, "unable to get old frequency value");
- return ret;
- }
- if (freq == old_freq)
- return 0;
- xa_for_each(&pin->dpll_refs, i, ref) {
- ops = dpll_pin_ops(ref);
- dpll = ref->dpll;
- ret = ops->frequency_set(pin, dpll_pin_on_dpll_priv(dpll, pin),
- dpll, dpll_priv(dpll), freq, extack);
- if (ret) {
- failed = ref;
- NL_SET_ERR_MSG_FMT(extack, "frequency set failed for dpll_id:%u",
- dpll->id);
- goto rollback;
- }
- }
- __dpll_pin_change_ntf(pin);
- return 0;
- rollback:
- xa_for_each(&pin->dpll_refs, i, ref) {
- if (ref == failed)
- break;
- ops = dpll_pin_ops(ref);
- dpll = ref->dpll;
- if (ops->frequency_set(pin, dpll_pin_on_dpll_priv(dpll, pin),
- dpll, dpll_priv(dpll), old_freq, extack))
- NL_SET_ERR_MSG(extack, "set frequency rollback failed");
- }
- return ret;
- }
- static int
- dpll_pin_esync_set(struct dpll_pin *pin, struct nlattr *a,
- struct netlink_ext_ack *extack)
- {
- struct dpll_pin_ref *ref, *failed;
- const struct dpll_pin_ops *ops;
- struct dpll_pin_esync esync;
- u64 freq = nla_get_u64(a);
- struct dpll_device *dpll;
- bool supported = false;
- unsigned long i;
- int ret;
- xa_for_each(&pin->dpll_refs, i, ref) {
- ops = dpll_pin_ops(ref);
- if (!ops->esync_set || !ops->esync_get) {
- NL_SET_ERR_MSG(extack,
- "embedded sync feature is not supported by this device");
- return -EOPNOTSUPP;
- }
- }
- ref = dpll_xa_ref_dpll_first(&pin->dpll_refs);
- ops = dpll_pin_ops(ref);
- dpll = ref->dpll;
- ret = ops->esync_get(pin, dpll_pin_on_dpll_priv(dpll, pin), dpll,
- dpll_priv(dpll), &esync, extack);
- if (ret) {
- NL_SET_ERR_MSG(extack, "unable to get current embedded sync frequency value");
- return ret;
- }
- if (freq == esync.freq)
- return 0;
- for (i = 0; i < esync.range_num; i++)
- if (freq <= esync.range[i].max && freq >= esync.range[i].min)
- supported = true;
- if (!supported) {
- NL_SET_ERR_MSG_ATTR(extack, a,
- "requested embedded sync frequency value is not supported by this device");
- return -EINVAL;
- }
- xa_for_each(&pin->dpll_refs, i, ref) {
- void *pin_dpll_priv;
- ops = dpll_pin_ops(ref);
- dpll = ref->dpll;
- pin_dpll_priv = dpll_pin_on_dpll_priv(dpll, pin);
- ret = ops->esync_set(pin, pin_dpll_priv, dpll, dpll_priv(dpll),
- freq, extack);
- if (ret) {
- failed = ref;
- NL_SET_ERR_MSG_FMT(extack,
- "embedded sync frequency set failed for dpll_id: %u",
- dpll->id);
- goto rollback;
- }
- }
- __dpll_pin_change_ntf(pin);
- return 0;
- rollback:
- xa_for_each(&pin->dpll_refs, i, ref) {
- void *pin_dpll_priv;
- if (ref == failed)
- break;
- ops = dpll_pin_ops(ref);
- dpll = ref->dpll;
- pin_dpll_priv = dpll_pin_on_dpll_priv(dpll, pin);
- if (ops->esync_set(pin, pin_dpll_priv, dpll, dpll_priv(dpll),
- esync.freq, extack))
- NL_SET_ERR_MSG(extack, "set embedded sync frequency rollback failed");
- }
- return ret;
- }
- static int
- dpll_pin_ref_sync_state_set(struct dpll_pin *pin,
- unsigned long ref_sync_pin_idx,
- const enum dpll_pin_state state,
- struct netlink_ext_ack *extack)
- {
- struct dpll_pin_ref *ref, *failed;
- const struct dpll_pin_ops *ops;
- enum dpll_pin_state old_state;
- struct dpll_pin *ref_sync_pin;
- struct dpll_device *dpll;
- unsigned long i;
- int ret;
- ref_sync_pin = xa_find(&pin->ref_sync_pins, &ref_sync_pin_idx,
- ULONG_MAX, XA_PRESENT);
- if (!ref_sync_pin) {
- NL_SET_ERR_MSG(extack, "reference sync pin not found");
- return -EINVAL;
- }
- if (!dpll_pin_available(ref_sync_pin)) {
- NL_SET_ERR_MSG(extack, "reference sync pin not available");
- return -EINVAL;
- }
- ref = dpll_xa_ref_dpll_first(&pin->dpll_refs);
- ASSERT_NOT_NULL(ref);
- ops = dpll_pin_ops(ref);
- if (!ops->ref_sync_set || !ops->ref_sync_get) {
- NL_SET_ERR_MSG(extack, "reference sync not supported by this pin");
- return -EOPNOTSUPP;
- }
- dpll = ref->dpll;
- ret = ops->ref_sync_get(pin, dpll_pin_on_dpll_priv(dpll, pin),
- ref_sync_pin,
- dpll_pin_on_dpll_priv(dpll, ref_sync_pin),
- &old_state, extack);
- if (ret) {
- NL_SET_ERR_MSG(extack, "unable to get old reference sync state");
- return ret;
- }
- if (state == old_state)
- return 0;
- xa_for_each(&pin->dpll_refs, i, ref) {
- ops = dpll_pin_ops(ref);
- dpll = ref->dpll;
- ret = ops->ref_sync_set(pin, dpll_pin_on_dpll_priv(dpll, pin),
- ref_sync_pin,
- dpll_pin_on_dpll_priv(dpll,
- ref_sync_pin),
- state, extack);
- if (ret) {
- failed = ref;
- NL_SET_ERR_MSG_FMT(extack, "reference sync set failed for dpll_id:%u",
- dpll->id);
- goto rollback;
- }
- }
- __dpll_pin_change_ntf(pin);
- return 0;
- rollback:
- xa_for_each(&pin->dpll_refs, i, ref) {
- if (ref == failed)
- break;
- ops = dpll_pin_ops(ref);
- dpll = ref->dpll;
- if (ops->ref_sync_set(pin, dpll_pin_on_dpll_priv(dpll, pin),
- ref_sync_pin,
- dpll_pin_on_dpll_priv(dpll, ref_sync_pin),
- old_state, extack))
- NL_SET_ERR_MSG(extack, "set reference sync rollback failed");
- }
- return ret;
- }
- static int
- dpll_pin_ref_sync_set(struct dpll_pin *pin, struct nlattr *nest,
- struct netlink_ext_ack *extack)
- {
- struct nlattr *tb[DPLL_A_PIN_MAX + 1];
- enum dpll_pin_state state;
- u32 sync_pin_id;
- nla_parse_nested(tb, DPLL_A_PIN_MAX, nest,
- dpll_reference_sync_nl_policy, extack);
- if (!tb[DPLL_A_PIN_ID]) {
- NL_SET_ERR_MSG(extack, "sync pin id expected");
- return -EINVAL;
- }
- sync_pin_id = nla_get_u32(tb[DPLL_A_PIN_ID]);
- if (!tb[DPLL_A_PIN_STATE]) {
- NL_SET_ERR_MSG(extack, "sync pin state expected");
- return -EINVAL;
- }
- state = nla_get_u32(tb[DPLL_A_PIN_STATE]);
- return dpll_pin_ref_sync_state_set(pin, sync_pin_id, state, extack);
- }
- static int
- dpll_pin_on_pin_state_set(struct dpll_pin *pin, u32 parent_idx,
- enum dpll_pin_state state,
- struct netlink_ext_ack *extack)
- {
- struct dpll_pin_ref *parent_ref;
- const struct dpll_pin_ops *ops;
- struct dpll_pin_ref *dpll_ref;
- void *pin_priv, *parent_priv;
- struct dpll_pin *parent;
- unsigned long i;
- int ret;
- if (!(DPLL_PIN_CAPABILITIES_STATE_CAN_CHANGE &
- pin->prop.capabilities)) {
- NL_SET_ERR_MSG(extack, "state changing is not allowed");
- return -EOPNOTSUPP;
- }
- parent = xa_load(&dpll_pin_xa, parent_idx);
- if (!parent)
- return -EINVAL;
- parent_ref = xa_load(&pin->parent_refs, parent->pin_idx);
- if (!parent_ref)
- return -EINVAL;
- xa_for_each(&parent->dpll_refs, i, dpll_ref) {
- ops = dpll_pin_ops(parent_ref);
- if (!ops->state_on_pin_set)
- return -EOPNOTSUPP;
- pin_priv = dpll_pin_on_pin_priv(parent, pin);
- parent_priv = dpll_pin_on_dpll_priv(dpll_ref->dpll, parent);
- ret = ops->state_on_pin_set(pin, pin_priv, parent, parent_priv,
- state, extack);
- if (ret)
- return ret;
- }
- __dpll_pin_change_ntf(pin);
- return 0;
- }
- static int
- dpll_pin_state_set(struct dpll_device *dpll, struct dpll_pin *pin,
- enum dpll_pin_state state,
- struct netlink_ext_ack *extack)
- {
- const struct dpll_pin_ops *ops;
- struct dpll_pin_ref *ref;
- int ret;
- if (!(DPLL_PIN_CAPABILITIES_STATE_CAN_CHANGE &
- pin->prop.capabilities)) {
- NL_SET_ERR_MSG(extack, "state changing is not allowed");
- return -EOPNOTSUPP;
- }
- ref = xa_load(&pin->dpll_refs, dpll->id);
- ASSERT_NOT_NULL(ref);
- ops = dpll_pin_ops(ref);
- if (!ops->state_on_dpll_set)
- return -EOPNOTSUPP;
- ret = ops->state_on_dpll_set(pin, dpll_pin_on_dpll_priv(dpll, pin),
- dpll, dpll_priv(dpll), state, extack);
- if (ret)
- return ret;
- __dpll_pin_change_ntf(pin);
- return 0;
- }
- static int
- dpll_pin_prio_set(struct dpll_device *dpll, struct dpll_pin *pin,
- u32 prio, struct netlink_ext_ack *extack)
- {
- const struct dpll_pin_ops *ops;
- struct dpll_pin_ref *ref;
- int ret;
- if (!(DPLL_PIN_CAPABILITIES_PRIORITY_CAN_CHANGE &
- pin->prop.capabilities)) {
- NL_SET_ERR_MSG(extack, "prio changing is not allowed");
- return -EOPNOTSUPP;
- }
- ref = xa_load(&pin->dpll_refs, dpll->id);
- ASSERT_NOT_NULL(ref);
- ops = dpll_pin_ops(ref);
- if (!ops->prio_set)
- return -EOPNOTSUPP;
- ret = ops->prio_set(pin, dpll_pin_on_dpll_priv(dpll, pin), dpll,
- dpll_priv(dpll), prio, extack);
- if (ret)
- return ret;
- __dpll_pin_change_ntf(pin);
- return 0;
- }
- static int
- dpll_pin_direction_set(struct dpll_pin *pin, struct dpll_device *dpll,
- enum dpll_pin_direction direction,
- struct netlink_ext_ack *extack)
- {
- const struct dpll_pin_ops *ops;
- struct dpll_pin_ref *ref;
- int ret;
- if (!(DPLL_PIN_CAPABILITIES_DIRECTION_CAN_CHANGE &
- pin->prop.capabilities)) {
- NL_SET_ERR_MSG(extack, "direction changing is not allowed");
- return -EOPNOTSUPP;
- }
- ref = xa_load(&pin->dpll_refs, dpll->id);
- ASSERT_NOT_NULL(ref);
- ops = dpll_pin_ops(ref);
- if (!ops->direction_set)
- return -EOPNOTSUPP;
- ret = ops->direction_set(pin, dpll_pin_on_dpll_priv(dpll, pin),
- dpll, dpll_priv(dpll), direction, extack);
- if (ret)
- return ret;
- __dpll_pin_change_ntf(pin);
- return 0;
- }
- static int
- dpll_pin_phase_adj_set(struct dpll_pin *pin, struct nlattr *phase_adj_attr,
- struct netlink_ext_ack *extack)
- {
- struct dpll_pin_ref *ref, *failed;
- const struct dpll_pin_ops *ops;
- s32 phase_adj, old_phase_adj;
- struct dpll_device *dpll;
- unsigned long i;
- int ret;
- phase_adj = nla_get_s32(phase_adj_attr);
- if (phase_adj > pin->prop.phase_range.max ||
- phase_adj < pin->prop.phase_range.min) {
- NL_SET_ERR_MSG_ATTR(extack, phase_adj_attr,
- "phase adjust value of out range");
- return -EINVAL;
- }
- if (pin->prop.phase_gran && phase_adj % (s32)pin->prop.phase_gran) {
- NL_SET_ERR_MSG_ATTR_FMT(extack, phase_adj_attr,
- "phase adjust value not multiple of %u",
- pin->prop.phase_gran);
- return -EINVAL;
- }
- xa_for_each(&pin->dpll_refs, i, ref) {
- ops = dpll_pin_ops(ref);
- if (!ops->phase_adjust_set || !ops->phase_adjust_get) {
- NL_SET_ERR_MSG(extack, "phase adjust not supported");
- return -EOPNOTSUPP;
- }
- }
- ref = dpll_xa_ref_dpll_first(&pin->dpll_refs);
- ops = dpll_pin_ops(ref);
- dpll = ref->dpll;
- ret = ops->phase_adjust_get(pin, dpll_pin_on_dpll_priv(dpll, pin),
- dpll, dpll_priv(dpll), &old_phase_adj,
- extack);
- if (ret) {
- NL_SET_ERR_MSG(extack, "unable to get old phase adjust value");
- return ret;
- }
- if (phase_adj == old_phase_adj)
- return 0;
- xa_for_each(&pin->dpll_refs, i, ref) {
- ops = dpll_pin_ops(ref);
- dpll = ref->dpll;
- ret = ops->phase_adjust_set(pin,
- dpll_pin_on_dpll_priv(dpll, pin),
- dpll, dpll_priv(dpll), phase_adj,
- extack);
- if (ret) {
- failed = ref;
- NL_SET_ERR_MSG_FMT(extack,
- "phase adjust set failed for dpll_id:%u",
- dpll->id);
- goto rollback;
- }
- }
- __dpll_pin_change_ntf(pin);
- return 0;
- rollback:
- xa_for_each(&pin->dpll_refs, i, ref) {
- if (ref == failed)
- break;
- ops = dpll_pin_ops(ref);
- dpll = ref->dpll;
- if (ops->phase_adjust_set(pin, dpll_pin_on_dpll_priv(dpll, pin),
- dpll, dpll_priv(dpll), old_phase_adj,
- extack))
- NL_SET_ERR_MSG(extack, "set phase adjust rollback failed");
- }
- return ret;
- }
- static int
- dpll_pin_parent_device_set(struct dpll_pin *pin, struct nlattr *parent_nest,
- struct netlink_ext_ack *extack)
- {
- struct nlattr *tb[DPLL_A_PIN_MAX + 1];
- enum dpll_pin_direction direction;
- enum dpll_pin_state state;
- struct dpll_pin_ref *ref;
- struct dpll_device *dpll;
- u32 pdpll_idx, prio;
- int ret;
- nla_parse_nested(tb, DPLL_A_PIN_MAX, parent_nest,
- dpll_pin_parent_device_nl_policy, extack);
- if (!tb[DPLL_A_PIN_PARENT_ID]) {
- NL_SET_ERR_MSG(extack, "device parent id expected");
- return -EINVAL;
- }
- pdpll_idx = nla_get_u32(tb[DPLL_A_PIN_PARENT_ID]);
- dpll = xa_load(&dpll_device_xa, pdpll_idx);
- if (!dpll) {
- NL_SET_ERR_MSG(extack, "parent device not found");
- return -EINVAL;
- }
- ref = xa_load(&pin->dpll_refs, dpll->id);
- if (!ref) {
- NL_SET_ERR_MSG(extack, "pin not connected to given parent device");
- return -EINVAL;
- }
- if (tb[DPLL_A_PIN_STATE]) {
- state = nla_get_u32(tb[DPLL_A_PIN_STATE]);
- ret = dpll_pin_state_set(dpll, pin, state, extack);
- if (ret)
- return ret;
- }
- if (tb[DPLL_A_PIN_PRIO]) {
- prio = nla_get_u32(tb[DPLL_A_PIN_PRIO]);
- ret = dpll_pin_prio_set(dpll, pin, prio, extack);
- if (ret)
- return ret;
- }
- if (tb[DPLL_A_PIN_DIRECTION]) {
- direction = nla_get_u32(tb[DPLL_A_PIN_DIRECTION]);
- ret = dpll_pin_direction_set(pin, dpll, direction, extack);
- if (ret)
- return ret;
- }
- return 0;
- }
- static int
- dpll_pin_parent_pin_set(struct dpll_pin *pin, struct nlattr *parent_nest,
- struct netlink_ext_ack *extack)
- {
- struct nlattr *tb[DPLL_A_PIN_MAX + 1];
- u32 ppin_idx;
- int ret;
- nla_parse_nested(tb, DPLL_A_PIN_MAX, parent_nest,
- dpll_pin_parent_pin_nl_policy, extack);
- if (!tb[DPLL_A_PIN_PARENT_ID]) {
- NL_SET_ERR_MSG(extack, "device parent id expected");
- return -EINVAL;
- }
- ppin_idx = nla_get_u32(tb[DPLL_A_PIN_PARENT_ID]);
- if (tb[DPLL_A_PIN_STATE]) {
- enum dpll_pin_state state = nla_get_u32(tb[DPLL_A_PIN_STATE]);
- ret = dpll_pin_on_pin_state_set(pin, ppin_idx, state, extack);
- if (ret)
- return ret;
- }
- return 0;
- }
- static int
- dpll_pin_set_from_nlattr(struct dpll_pin *pin, struct genl_info *info)
- {
- struct nlattr *a;
- int rem, ret;
- nla_for_each_attr(a, genlmsg_data(info->genlhdr),
- genlmsg_len(info->genlhdr), rem) {
- switch (nla_type(a)) {
- case DPLL_A_PIN_FREQUENCY:
- ret = dpll_pin_freq_set(pin, a, info->extack);
- if (ret)
- return ret;
- break;
- case DPLL_A_PIN_PHASE_ADJUST:
- ret = dpll_pin_phase_adj_set(pin, a, info->extack);
- if (ret)
- return ret;
- break;
- case DPLL_A_PIN_PARENT_DEVICE:
- ret = dpll_pin_parent_device_set(pin, a, info->extack);
- if (ret)
- return ret;
- break;
- case DPLL_A_PIN_PARENT_PIN:
- ret = dpll_pin_parent_pin_set(pin, a, info->extack);
- if (ret)
- return ret;
- break;
- case DPLL_A_PIN_ESYNC_FREQUENCY:
- ret = dpll_pin_esync_set(pin, a, info->extack);
- if (ret)
- return ret;
- break;
- case DPLL_A_PIN_REFERENCE_SYNC:
- ret = dpll_pin_ref_sync_set(pin, a, info->extack);
- if (ret)
- return ret;
- break;
- }
- }
- return 0;
- }
- static struct dpll_pin *
- dpll_pin_find(u64 clock_id, struct nlattr *mod_name_attr,
- enum dpll_pin_type type, struct nlattr *board_label,
- struct nlattr *panel_label, struct nlattr *package_label,
- struct netlink_ext_ack *extack)
- {
- bool board_match, panel_match, package_match;
- struct dpll_pin *pin_match = NULL, *pin;
- const struct dpll_pin_properties *prop;
- bool cid_match, mod_match, type_match;
- unsigned long i;
- xa_for_each_marked(&dpll_pin_xa, i, pin, DPLL_REGISTERED) {
- prop = &pin->prop;
- cid_match = clock_id ? pin->clock_id == clock_id : true;
- mod_match = mod_name_attr && module_name(pin->module) ?
- !nla_strcmp(mod_name_attr,
- module_name(pin->module)) : true;
- type_match = type ? prop->type == type : true;
- board_match = board_label ? (prop->board_label ?
- !nla_strcmp(board_label, prop->board_label) : false) :
- true;
- panel_match = panel_label ? (prop->panel_label ?
- !nla_strcmp(panel_label, prop->panel_label) : false) :
- true;
- package_match = package_label ? (prop->package_label ?
- !nla_strcmp(package_label, prop->package_label) :
- false) : true;
- if (cid_match && mod_match && type_match && board_match &&
- panel_match && package_match) {
- if (pin_match) {
- NL_SET_ERR_MSG(extack, "multiple matches");
- return ERR_PTR(-EINVAL);
- }
- pin_match = pin;
- }
- }
- if (!pin_match) {
- NL_SET_ERR_MSG(extack, "not found");
- return ERR_PTR(-ENODEV);
- }
- return pin_match;
- }
- static struct dpll_pin *dpll_pin_find_from_nlattr(struct genl_info *info)
- {
- struct nlattr *attr, *mod_name_attr = NULL, *board_label_attr = NULL,
- *panel_label_attr = NULL, *package_label_attr = NULL;
- enum dpll_pin_type type = 0;
- u64 clock_id = 0;
- int rem = 0;
- nla_for_each_attr(attr, genlmsg_data(info->genlhdr),
- genlmsg_len(info->genlhdr), rem) {
- switch (nla_type(attr)) {
- case DPLL_A_PIN_CLOCK_ID:
- if (clock_id)
- goto duplicated_attr;
- clock_id = nla_get_u64(attr);
- break;
- case DPLL_A_PIN_MODULE_NAME:
- if (mod_name_attr)
- goto duplicated_attr;
- mod_name_attr = attr;
- break;
- case DPLL_A_PIN_TYPE:
- if (type)
- goto duplicated_attr;
- type = nla_get_u32(attr);
- break;
- case DPLL_A_PIN_BOARD_LABEL:
- if (board_label_attr)
- goto duplicated_attr;
- board_label_attr = attr;
- break;
- case DPLL_A_PIN_PANEL_LABEL:
- if (panel_label_attr)
- goto duplicated_attr;
- panel_label_attr = attr;
- break;
- case DPLL_A_PIN_PACKAGE_LABEL:
- if (package_label_attr)
- goto duplicated_attr;
- package_label_attr = attr;
- break;
- default:
- break;
- }
- }
- if (!(clock_id || mod_name_attr || board_label_attr ||
- panel_label_attr || package_label_attr)) {
- NL_SET_ERR_MSG(info->extack, "missing attributes");
- return ERR_PTR(-EINVAL);
- }
- return dpll_pin_find(clock_id, mod_name_attr, type, board_label_attr,
- panel_label_attr, package_label_attr,
- info->extack);
- duplicated_attr:
- NL_SET_ERR_MSG(info->extack, "duplicated attribute");
- return ERR_PTR(-EINVAL);
- }
- int dpll_nl_pin_id_get_doit(struct sk_buff *skb, struct genl_info *info)
- {
- struct dpll_pin *pin;
- struct sk_buff *msg;
- struct nlattr *hdr;
- int ret;
- msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
- if (!msg)
- return -ENOMEM;
- hdr = genlmsg_put_reply(msg, info, &dpll_nl_family, 0,
- DPLL_CMD_PIN_ID_GET);
- if (!hdr) {
- nlmsg_free(msg);
- return -EMSGSIZE;
- }
- pin = dpll_pin_find_from_nlattr(info);
- if (IS_ERR(pin)) {
- nlmsg_free(msg);
- return PTR_ERR(pin);
- }
- if (!dpll_pin_available(pin)) {
- nlmsg_free(msg);
- return -ENODEV;
- }
- ret = dpll_msg_add_pin_handle(msg, pin);
- if (ret) {
- nlmsg_free(msg);
- return ret;
- }
- genlmsg_end(msg, hdr);
- return genlmsg_reply(msg, info);
- }
- int dpll_nl_pin_get_doit(struct sk_buff *skb, struct genl_info *info)
- {
- struct dpll_pin *pin = info->user_ptr[0];
- struct sk_buff *msg;
- struct nlattr *hdr;
- int ret;
- if (!pin)
- return -ENODEV;
- msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
- if (!msg)
- return -ENOMEM;
- hdr = genlmsg_put_reply(msg, info, &dpll_nl_family, 0,
- DPLL_CMD_PIN_GET);
- if (!hdr) {
- nlmsg_free(msg);
- return -EMSGSIZE;
- }
- ret = dpll_cmd_pin_get_one(msg, pin, info->extack);
- if (ret) {
- nlmsg_free(msg);
- return ret;
- }
- genlmsg_end(msg, hdr);
- return genlmsg_reply(msg, info);
- }
- int dpll_nl_pin_get_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
- {
- struct dpll_dump_ctx *ctx = dpll_dump_context(cb);
- struct dpll_pin *pin;
- struct nlattr *hdr;
- unsigned long i;
- int ret = 0;
- mutex_lock(&dpll_lock);
- xa_for_each_marked_start(&dpll_pin_xa, i, pin, DPLL_REGISTERED,
- ctx->idx) {
- if (!dpll_pin_available(pin))
- continue;
- hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid,
- cb->nlh->nlmsg_seq,
- &dpll_nl_family, NLM_F_MULTI,
- DPLL_CMD_PIN_GET);
- if (!hdr) {
- ret = -EMSGSIZE;
- break;
- }
- ret = dpll_cmd_pin_get_one(skb, pin, cb->extack);
- if (ret) {
- genlmsg_cancel(skb, hdr);
- break;
- }
- genlmsg_end(skb, hdr);
- }
- mutex_unlock(&dpll_lock);
- if (ret == -EMSGSIZE) {
- ctx->idx = i;
- return skb->len;
- }
- return ret;
- }
- int dpll_nl_pin_set_doit(struct sk_buff *skb, struct genl_info *info)
- {
- struct dpll_pin *pin = info->user_ptr[0];
- return dpll_pin_set_from_nlattr(pin, info);
- }
- static struct dpll_device *
- dpll_device_find(u64 clock_id, struct nlattr *mod_name_attr,
- enum dpll_type type, struct netlink_ext_ack *extack)
- {
- struct dpll_device *dpll_match = NULL, *dpll;
- bool cid_match, mod_match, type_match;
- unsigned long i;
- xa_for_each_marked(&dpll_device_xa, i, dpll, DPLL_REGISTERED) {
- cid_match = clock_id ? dpll->clock_id == clock_id : true;
- mod_match = mod_name_attr ? (module_name(dpll->module) ?
- !nla_strcmp(mod_name_attr,
- module_name(dpll->module)) : false) : true;
- type_match = type ? dpll->type == type : true;
- if (cid_match && mod_match && type_match) {
- if (dpll_match) {
- NL_SET_ERR_MSG(extack, "multiple matches");
- return ERR_PTR(-EINVAL);
- }
- dpll_match = dpll;
- }
- }
- if (!dpll_match) {
- NL_SET_ERR_MSG(extack, "not found");
- return ERR_PTR(-ENODEV);
- }
- return dpll_match;
- }
- static struct dpll_device *
- dpll_device_find_from_nlattr(struct genl_info *info)
- {
- struct nlattr *attr, *mod_name_attr = NULL;
- enum dpll_type type = 0;
- u64 clock_id = 0;
- int rem = 0;
- nla_for_each_attr(attr, genlmsg_data(info->genlhdr),
- genlmsg_len(info->genlhdr), rem) {
- switch (nla_type(attr)) {
- case DPLL_A_CLOCK_ID:
- if (clock_id)
- goto duplicated_attr;
- clock_id = nla_get_u64(attr);
- break;
- case DPLL_A_MODULE_NAME:
- if (mod_name_attr)
- goto duplicated_attr;
- mod_name_attr = attr;
- break;
- case DPLL_A_TYPE:
- if (type)
- goto duplicated_attr;
- type = nla_get_u32(attr);
- break;
- default:
- break;
- }
- }
- if (!clock_id && !mod_name_attr && !type) {
- NL_SET_ERR_MSG(info->extack, "missing attributes");
- return ERR_PTR(-EINVAL);
- }
- return dpll_device_find(clock_id, mod_name_attr, type, info->extack);
- duplicated_attr:
- NL_SET_ERR_MSG(info->extack, "duplicated attribute");
- return ERR_PTR(-EINVAL);
- }
- int dpll_nl_device_id_get_doit(struct sk_buff *skb, struct genl_info *info)
- {
- struct dpll_device *dpll;
- struct sk_buff *msg;
- struct nlattr *hdr;
- int ret;
- msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
- if (!msg)
- return -ENOMEM;
- hdr = genlmsg_put_reply(msg, info, &dpll_nl_family, 0,
- DPLL_CMD_DEVICE_ID_GET);
- if (!hdr) {
- nlmsg_free(msg);
- return -EMSGSIZE;
- }
- dpll = dpll_device_find_from_nlattr(info);
- if (IS_ERR(dpll)) {
- nlmsg_free(msg);
- return PTR_ERR(dpll);
- }
- ret = dpll_msg_add_dev_handle(msg, dpll);
- if (ret) {
- nlmsg_free(msg);
- return ret;
- }
- genlmsg_end(msg, hdr);
- return genlmsg_reply(msg, info);
- }
- int dpll_nl_device_get_doit(struct sk_buff *skb, struct genl_info *info)
- {
- struct dpll_device *dpll = info->user_ptr[0];
- struct sk_buff *msg;
- struct nlattr *hdr;
- int ret;
- msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
- if (!msg)
- return -ENOMEM;
- hdr = genlmsg_put_reply(msg, info, &dpll_nl_family, 0,
- DPLL_CMD_DEVICE_GET);
- if (!hdr) {
- nlmsg_free(msg);
- return -EMSGSIZE;
- }
- ret = dpll_device_get_one(dpll, msg, info->extack);
- if (ret) {
- nlmsg_free(msg);
- return ret;
- }
- genlmsg_end(msg, hdr);
- return genlmsg_reply(msg, info);
- }
- static int
- dpll_set_from_nlattr(struct dpll_device *dpll, struct genl_info *info)
- {
- struct nlattr *a;
- int rem, ret;
- nla_for_each_attr(a, genlmsg_data(info->genlhdr),
- genlmsg_len(info->genlhdr), rem) {
- switch (nla_type(a)) {
- case DPLL_A_MODE:
- ret = dpll_mode_set(dpll, a, info->extack);
- if (ret)
- return ret;
- break;
- case DPLL_A_PHASE_OFFSET_MONITOR:
- ret = dpll_phase_offset_monitor_set(dpll, a,
- info->extack);
- if (ret)
- return ret;
- break;
- case DPLL_A_PHASE_OFFSET_AVG_FACTOR:
- ret = dpll_phase_offset_avg_factor_set(dpll, a,
- info->extack);
- if (ret)
- return ret;
- break;
- }
- }
- return 0;
- }
- int dpll_nl_device_set_doit(struct sk_buff *skb, struct genl_info *info)
- {
- struct dpll_device *dpll = info->user_ptr[0];
- return dpll_set_from_nlattr(dpll, info);
- }
- int dpll_nl_device_get_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
- {
- struct dpll_dump_ctx *ctx = dpll_dump_context(cb);
- struct dpll_device *dpll;
- struct nlattr *hdr;
- unsigned long i;
- int ret = 0;
- mutex_lock(&dpll_lock);
- xa_for_each_marked_start(&dpll_device_xa, i, dpll, DPLL_REGISTERED,
- ctx->idx) {
- hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid,
- cb->nlh->nlmsg_seq, &dpll_nl_family,
- NLM_F_MULTI, DPLL_CMD_DEVICE_GET);
- if (!hdr) {
- ret = -EMSGSIZE;
- break;
- }
- ret = dpll_device_get_one(dpll, skb, cb->extack);
- if (ret) {
- genlmsg_cancel(skb, hdr);
- break;
- }
- genlmsg_end(skb, hdr);
- }
- mutex_unlock(&dpll_lock);
- if (ret == -EMSGSIZE) {
- ctx->idx = i;
- return skb->len;
- }
- return ret;
- }
- int dpll_pre_doit(const struct genl_split_ops *ops, struct sk_buff *skb,
- struct genl_info *info)
- {
- u32 id;
- if (GENL_REQ_ATTR_CHECK(info, DPLL_A_ID))
- return -EINVAL;
- mutex_lock(&dpll_lock);
- id = nla_get_u32(info->attrs[DPLL_A_ID]);
- info->user_ptr[0] = dpll_device_get_by_id(id);
- if (!info->user_ptr[0]) {
- NL_SET_ERR_MSG(info->extack, "device not found");
- goto unlock;
- }
- return 0;
- unlock:
- mutex_unlock(&dpll_lock);
- return -ENODEV;
- }
- void dpll_post_doit(const struct genl_split_ops *ops, struct sk_buff *skb,
- struct genl_info *info)
- {
- mutex_unlock(&dpll_lock);
- }
- int
- dpll_lock_doit(const struct genl_split_ops *ops, struct sk_buff *skb,
- struct genl_info *info)
- {
- mutex_lock(&dpll_lock);
- return 0;
- }
- void
- dpll_unlock_doit(const struct genl_split_ops *ops, struct sk_buff *skb,
- struct genl_info *info)
- {
- mutex_unlock(&dpll_lock);
- }
- int dpll_pin_pre_doit(const struct genl_split_ops *ops, struct sk_buff *skb,
- struct genl_info *info)
- {
- int ret;
- mutex_lock(&dpll_lock);
- if (GENL_REQ_ATTR_CHECK(info, DPLL_A_PIN_ID)) {
- ret = -EINVAL;
- goto unlock_dev;
- }
- info->user_ptr[0] = xa_load(&dpll_pin_xa,
- nla_get_u32(info->attrs[DPLL_A_PIN_ID]));
- if (!info->user_ptr[0] ||
- !dpll_pin_available(info->user_ptr[0])) {
- NL_SET_ERR_MSG(info->extack, "pin not found");
- ret = -ENODEV;
- goto unlock_dev;
- }
- return 0;
- unlock_dev:
- mutex_unlock(&dpll_lock);
- return ret;
- }
- void dpll_pin_post_doit(const struct genl_split_ops *ops, struct sk_buff *skb,
- struct genl_info *info)
- {
- mutex_unlock(&dpll_lock);
- }
|