| /**************************************************************************** |
| * drivers/i3c/device.c |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. The |
| * ASF licenses this file to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance with the |
| * License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| * License for the specific language governing permissions and limitations |
| * under the License. |
| * |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Included Files |
| ****************************************************************************/ |
| |
| #include "internals.h" |
| |
| /**************************************************************************** |
| * Public Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: i3c_device_do_priv_xfers |
| * |
| * Description: |
| * Do I3C SDR private transfers directed to a specific device. |
| * |
| * Initiate one or several private SDR transfers with @dev. |
| * |
| * This function can sleep and thus cannot be called in atomic context. |
| * |
| * Input Parameters: |
| * dev - Device with which the transfers should be done |
| * xfers - Array of transfers |
| * nxfers - Number of transfers |
| * |
| * Returned Value: |
| * 0 in case of success, a negative error core otherwise. |
| * |
| ****************************************************************************/ |
| |
| int i3c_device_do_priv_xfers(FAR const struct i3c_device *dev, |
| FAR struct i3c_priv_xfer *xfers, |
| int nxfers) |
| { |
| int ret; |
| int i; |
| |
| if (nxfers < 1) |
| { |
| return 0; |
| } |
| |
| for (i = 0; i < nxfers; i++) |
| { |
| if (!xfers[i].len || !xfers[i].data.in) |
| { |
| return -EINVAL; |
| } |
| } |
| |
| i3c_bus_normaluse_lock(dev->bus); |
| ret = i3c_dev_do_priv_xfers_locked(dev->desc, xfers, nxfers); |
| i3c_bus_normaluse_unlock(dev->bus); |
| |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * Name: i3c_device_get_info |
| * Get I3C device information, Retrieve I3C dev info. |
| * |
| * Input Parameters: |
| * dev - Device we want information on. |
| * info - The information object to fill in. |
| * |
| ****************************************************************************/ |
| |
| void i3c_device_get_info(FAR const struct i3c_device *dev, |
| FAR struct i3c_device_info *info) |
| { |
| if (info == NULL) |
| { |
| return; |
| } |
| |
| i3c_bus_normaluse_lock(dev->bus); |
| if (dev->desc) |
| { |
| *info = dev->desc->info; |
| } |
| |
| i3c_bus_normaluse_unlock(dev->bus); |
| } |
| |
| /**************************************************************************** |
| * Name: i3c_device_disable_ibi |
| * |
| * Description: |
| * Disable IBIs coming from a specific device. |
| * |
| * This function disable IBIs coming from a specific device and wait for |
| * all pending IBIs to be processed. |
| * |
| * Input Parameters: |
| * dev - Device on which IBIs should be disabled |
| * |
| * Returned Value: |
| * 0 in case of success, a negative error core otherwise. |
| * |
| ****************************************************************************/ |
| |
| int i3c_device_disable_ibi(FAR const struct i3c_device *dev) |
| { |
| int ret = -ENOENT; |
| |
| i3c_bus_normaluse_lock(dev->bus); |
| if (dev->desc) |
| { |
| nxmutex_lock(&dev->desc->ibi_lock); |
| ret = i3c_dev_disable_ibi_locked(dev->desc); |
| nxmutex_unlock(&dev->desc->ibi_lock); |
| } |
| |
| i3c_bus_normaluse_unlock(dev->bus); |
| |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * Name: i3c_device_enable_ibi |
| * |
| * Description: |
| * Enable IBIs coming from a specific device. |
| * |
| * This function enable IBIs coming from a specific device and wait for |
| * all pending IBIs to be processed. This should be called on a device |
| * where i3c_device_request_ibi() has succeeded. |
| * |
| * Note that IBIs from this device might be received before this function |
| * returns to its caller. |
| * |
| * Input Parameters: |
| * dev - Device on which IBIs should be enabled |
| * |
| * Returned Value: |
| * 0 in case of success, a negative error core otherwise. |
| * |
| ****************************************************************************/ |
| |
| int i3c_device_enable_ibi(FAR const struct i3c_device *dev) |
| { |
| int ret = -ENOENT; |
| |
| i3c_bus_normaluse_lock(dev->bus); |
| if (dev->desc) |
| { |
| nxmutex_lock(&dev->desc->ibi_lock); |
| ret = i3c_dev_enable_ibi_locked(dev->desc); |
| nxmutex_unlock(&dev->desc->ibi_lock); |
| } |
| |
| i3c_bus_normaluse_unlock(dev->bus); |
| |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * Name: i3c_device_request_ibi |
| * |
| * Description: |
| * Request an IBI. |
| * |
| * This function is responsible for pre-allocating all resources needed to |
| * process IBIs coming from @dev. When this function returns, the IBI is |
| * not enabled until i3c_device_enable_ibi() is called. |
| * |
| * Input Parameters: |
| * dev - Device for which we should enable IBIs |
| * req - Setup requested for this IBI |
| * |
| * Returned Value: |
| * 0 in case of success, a negative error core otherwise. |
| * |
| ****************************************************************************/ |
| |
| int i3c_device_request_ibi(FAR const struct i3c_device *dev, |
| FAR const struct i3c_ibi_setup *req) |
| { |
| int ret = -ENOENT; |
| |
| if (req->handler == NULL || !req->num_slots) |
| { |
| return -EINVAL; |
| } |
| |
| i3c_bus_normaluse_lock(dev->bus); |
| if (dev->desc) |
| { |
| nxmutex_lock(&dev->desc->ibi_lock); |
| ret = i3c_dev_request_ibi_locked(dev->desc, req); |
| nxmutex_unlock(&dev->desc->ibi_lock); |
| } |
| |
| i3c_bus_normaluse_unlock(dev->bus); |
| |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * Name: i3c_device_free_ibi |
| * |
| * Description: |
| * Free all resources needed for IBI handling. |
| * |
| * This function is responsible for de-allocating resources previously |
| * allocated by i3c_device_request_ibi(). It should be called after |
| * disabling IBIs with i3c_device_disable_ibi(). |
| * |
| * Input Parameters: |
| * dev - Device on which you want to release IBI resources |
| * |
| ****************************************************************************/ |
| |
| void i3c_device_free_ibi(FAR const struct i3c_device *dev) |
| { |
| i3c_bus_normaluse_lock(dev->bus); |
| if (dev->desc) |
| { |
| nxmutex_lock(&dev->desc->ibi_lock); |
| i3c_dev_free_ibi_locked(dev->desc); |
| nxmutex_unlock(&dev->desc->ibi_lock); |
| } |
| |
| i3c_bus_normaluse_unlock(dev->bus); |
| } |
| |
| /**************************************************************************** |
| * Name: i3c_device_send_ccc_cmd |
| * |
| * Description: |
| * This function is used to send a common ccc command. |
| * |
| * Input Parameters: |
| * dev - An I3C device descriptor will be used for |
| * cmd - The buf of ccc commands to transfer, only one frame at a time |
| * |
| * Returned Value: |
| * 0 or positive if Success, negative otherwise. |
| ****************************************************************************/ |
| |
| int i3c_device_send_ccc_cmd(FAR const struct i3c_device *dev, |
| FAR struct i3c_ccc_cmd *cmd) |
| { |
| int ret; |
| FAR struct i3c_master_controller *master; |
| |
| if (dev == NULL || cmd == NULL) |
| { |
| return -EINVAL; |
| } |
| |
| master = i3c_dev_get_master(dev->desc); |
| |
| i3c_bus_normaluse_lock(dev->bus); |
| |
| ret = i3c_master_send_ccc_cmd_locked(master, cmd); |
| |
| i3c_bus_normaluse_unlock(dev->bus); |
| |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * Name: i3c_master_find_i3c_dev |
| * |
| * Description: |
| * This function is used to be find a i3c_device address by master handle |
| * and provisional ID. |
| * |
| * Input Parameters: |
| * master - The master used to get i3c_device on the bus |
| * id - An instance of i3c_device_id, include manufid,partid and so on. |
| * |
| * Returned Value: |
| * Struct i3c_device var in case of success, NULL otherwise. |
| ****************************************************************************/ |
| |
| FAR const struct i3c_device *i3c_master_find_i3c_dev( |
| FAR struct i3c_master_controller *master, |
| FAR const struct i3c_device_id *id) |
| { |
| FAR struct i3c_dev_desc *desc; |
| uint16_t manuf; |
| uint16_t part; |
| uint16_t ext_info; |
| bool rndpid; |
| |
| i3c_bus_normaluse_lock(&master->bus); |
| i3c_bus_for_each_i3cdev(&master->bus, desc) |
| { |
| manuf = I3C_PID_MANUF_ID(desc->info.pid); |
| part = I3C_PID_PART_ID(desc->info.pid); |
| ext_info = I3C_PID_EXTRA_INFO(desc->info.pid); |
| rndpid = I3C_PID_RND_LOWER_32BITS(desc->info.pid); |
| |
| if ((id->match_flags & I3C_MATCH_DCR) && |
| id->dcr != desc->info.dcr) |
| { |
| continue; |
| } |
| |
| if ((id->match_flags & I3C_MATCH_MANUF) && |
| id->manuf_id != manuf) |
| { |
| continue; |
| } |
| |
| if ((id->match_flags & I3C_MATCH_PART) && |
| (rndpid || id->part_id != part)) |
| { |
| continue; |
| } |
| |
| if ((id->match_flags & I3C_MATCH_EXTRA_INFO) && |
| (rndpid || id->extra_info != ext_info)) |
| { |
| continue; |
| } |
| |
| i3c_bus_normaluse_unlock(&master->bus); |
| return desc->dev; |
| } |
| |
| i3c_bus_normaluse_unlock(&master->bus); |
| return NULL; |
| } |