| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625 |
- // 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;
- }
- /**
- * 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);
- enum dpll_mode mode;
- int ret;
- /* No mode change is supported now, so 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;
- if (nla_put_u32(msg, DPLL_A_MODE_SUPPORTED, mode))
- 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_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;
- }
- return nla_put_sint(msg, DPLL_A_PIN_FRACTIONAL_FREQUENCY_OFFSET, 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 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 (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->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_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;
- 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)
- {
- return dpll_device_event_send(DPLL_CMD_DEVICE_CREATE_NTF, dpll);
- }
- int dpll_device_delete_ntf(struct dpll_device *dpll)
- {
- return dpll_device_event_send(DPLL_CMD_DEVICE_DELETE_NTF, dpll);
- }
- static int
- __dpll_device_change_ntf(struct dpll_device *dpll)
- {
- return dpll_device_event_send(DPLL_CMD_DEVICE_CHANGE_NTF, dpll);
- }
- 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_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)
- {
- return dpll_pin_event_send(DPLL_CMD_PIN_CREATE_NTF, pin);
- }
- int dpll_pin_delete_ntf(struct dpll_pin *pin)
- {
- return dpll_pin_event_send(DPLL_CMD_PIN_DELETE_NTF, pin);
- }
- static int __dpll_pin_change_ntf(struct dpll_pin *pin)
- {
- 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_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_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 not supported");
- 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;
- }
- }
- 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)) {
- 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)) {
- 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);
- }
- int dpll_nl_device_set_doit(struct sk_buff *skb, struct genl_info *info)
- {
- /* placeholder for set command */
- return 0;
- }
- 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);
- }
|