nuttx/drivers/pci/pci_epc.c
yangshuyong a537ecdd0f Support nuttx pci endpoint driver framework
Signed-off-by: yangshuyong <yangshuyong@xiaomi.com>
Signed-off-by: lipengfei28 <lipengfei28@xiaomi.com>
2024-09-23 10:03:54 +02:00

1012 lines
29 KiB
C

/****************************************************************************
* drivers/pci/pci_epc.c
*
* 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 <assert.h>
#include <errno.h>
#include <string.h>
#include <debug.h>
#include <nuttx/bits.h>
#include <nuttx/kmalloc.h>
#include <nuttx/lib/math32.h>
#include <nuttx/pci/pci_epc.h>
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/****************************************************************************
* Private Data
****************************************************************************/
static mutex_t g_pci_epc_lock = NXMUTEX_INITIALIZER;
static struct list_node g_pci_epc_device_list =
LIST_INITIAL_VALUE(g_pci_epc_device_list);
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: pci_get_epc
*
* Description:
* This function is used to get a PCI endpoint controller.
*
* Invoke to get struct pci_epc_ctrl_s * corresponding to the device name
* of the endpoint controller.
*
* Input Parameters:
* epc_name - Device name of the endpoint controller
*
* Returned Value:
* Return epc created if success, NULL if failed
****************************************************************************/
FAR struct pci_epc_ctrl_s *pci_get_epc(FAR const char *epc_name)
{
FAR struct pci_epc_ctrl_s *res = NULL;
FAR struct pci_epc_ctrl_s *epc;
int ret;
DEBUGASSERT(epc_name != NULL);
ret = nxmutex_lock(&g_pci_epc_lock);
if (ret < 0)
{
return NULL;
}
list_for_every_entry(&g_pci_epc_device_list, epc, struct pci_epc_ctrl_s,
node)
{
if (strcmp(epc_name, epc->name) == 0)
{
res = epc;
break;
}
}
nxmutex_unlock(&g_pci_epc_lock);
return res;
}
/****************************************************************************
* Name: pci_epc_get_next_free_bar
*
* Description:
* Helper to get unreserved BAR starting from bar.
*
* Invoke to get the next unreserved BAR starting from barno that can be
* used for endpoint function. For any incorrect value in bar_reserved return
* '0'.
*
* Input Parameters:
* epc_features - pci_epc_features_s structure that holds the reserved bar
* bitmap
* bar - The starting BAR number from where unreserved BAR should
* be searched
*
* Returned Value:
* Return the member if success, negative if failed
****************************************************************************/
int pci_epc_get_next_free_bar(
FAR const struct pci_epc_features_s *epc_features, int barno)
{
unsigned long free_bar;
if (epc_features == NULL)
{
return -EINVAL;
}
/* If 'bar - 1' is a 64-bit BAR, move to the next BAR */
if ((epc_features->bar_fixed_64bit << 1) & (1 << barno))
{
barno++;
}
/* Find if the reserved BAR is also a 64-bit BAR */
free_bar = epc_features->bar_reserved & epc_features->bar_fixed_64bit;
/* Set the adjacent bit if the reserved BAR is also a 64-bit BAR */
free_bar <<= 1;
free_bar |= epc_features->bar_reserved;
free_bar = find_next_zero_bit(&free_bar, PCI_STD_NUM_BARS, barno);
if (free_bar >= PCI_STD_NUM_BARS)
{
return -ENOENT;
}
return free_bar;
}
/****************************************************************************
* Name: pci_epc_get_features
*
* Description:
* This function is used to get the features supported by EPC.
*
* Invoke to get the features provided by the EPC which may be
* specific to an endpoint function. Returns pci_epc_features_s on success
* and NULL for any failures.
*
* Input Parameters:
* epc - The features supported by *this* EPC device will be returned
* funcno - The features supported by the EPC device specific to the
* endpoint function with funcno will be returned
*
* Returned Value:
* Epc features if success, NULL if failed
****************************************************************************/
FAR const struct pci_epc_features_s *
pci_epc_get_features(FAR struct pci_epc_ctrl_s *epc, uint8_t funcno)
{
FAR const struct pci_epc_features_s *epc_features;
if (epc == NULL || epc->ops->get_features == NULL ||
funcno >= epc->max_functions)
{
return NULL;
}
nxmutex_lock(&epc->lock);
epc_features = epc->ops->get_features(epc, funcno);
nxmutex_unlock(&epc->lock);
return epc_features;
}
/****************************************************************************
* Name: pci_epc_stop
*
* Description:
* This function is used to stop the PCI link.
*
* Invoke to stop the PCI link.
*
* Input Parameters:
* epc - The link of the EPC device that has to be stopped
*
* Returned Value:
* None
****************************************************************************/
void pci_epc_stop(FAR struct pci_epc_ctrl_s *epc)
{
if (epc == NULL || !epc->ops->stop)
{
return;
}
nxmutex_lock(&epc->lock);
epc->ops->stop(epc);
nxmutex_unlock(&epc->lock);
}
/****************************************************************************
* Name: pci_epc_start
*
* Description:
* This function is used to start the PCI link.
*
* Invoke to start the PCI link.
*
* Input Parameters:
* epc - The link of *this* EPC device has to be started
*
* Returned Value:
* Return 0 if success, negative if failed
****************************************************************************/
int pci_epc_start(FAR struct pci_epc_ctrl_s *epc)
{
int ret;
if (epc == NULL || !epc->ops->start)
{
return -EINVAL;
}
nxmutex_lock(&epc->lock);
ret = epc->ops->start(epc);
nxmutex_unlock(&epc->lock);
return ret;
}
/****************************************************************************
* Name: pci_epc_raise_irq
*
* Description:
* This function is used to interrupt the host system.
*
* Invoke to raise an legacy, MSI or MSI-X interrupt.
*
* Input Parameters:
* epc - The EPC device which has to interrupt the host
* funcno - The physical endpoint function number in the EPC device
* type - Specify the type of interrupt; legacy, MSI or MSI-X
* interrupt_num - The MSI or MSI-X interrupt number with range (1-N)
*
* Returned Value:
* Return 0 if success, negative if failed
****************************************************************************/
int pci_epc_raise_irq(FAR struct pci_epc_ctrl_s *epc, uint8_t funcno,
enum pci_epc_irq_type_e type, uint16_t interrupt_num)
{
int ret;
if (epc == NULL || epc->ops->raise_irq == NULL ||
funcno >= epc->max_functions)
{
return -EINVAL;
}
nxmutex_lock(&epc->lock);
ret = epc->ops->raise_irq(epc, funcno, type, interrupt_num);
nxmutex_unlock(&epc->lock);
return ret;
}
/****************************************************************************
* Name: pci_epc_map_msi_irq
*
* Description:
* Map physical address to MSI address and return MSI data.
*
* Invoke to map physical address to MSI address and return MSI data. The
* physical address should be an address in the outbound region. This is
* required to implement doorbell functionality of NTB wherein EPC on either
* side of the interface (primary and secondary) can directly write to the
* physical address (in outbound region) of the other interface to ring
* doorbell.
*
* Input Parameters:
* epc - The EPC device which has to interrupt the host
* funcno - The physical endpoint function number in the EPC
* device
* phys_addr - The physical address of the outbound region
* interrupt_num - The MSI or MSI-X interrupt number with range (1-N)
* entry_size - Size of Outbound address region for each interrupt
* msi_data - The data that should be written in order to raise MSI
* interrupt with interrupt number as 'interrupt num'
* msi_addr_offset - Offset of MSI address from the aligned outbound
* address to which the MSI address is mapped
*
* Returned Value:
* Return 0 if success, negative if failed
****************************************************************************/
int pci_epc_map_msi_irq(FAR struct pci_epc_ctrl_s *epc, uint8_t funcno,
uintptr_t phys_addr, uint8_t interrupt_num,
uint32_t entry_size, FAR uint32_t *msi_data,
FAR uint32_t *msi_addr_offset)
{
int ret;
if (epc == NULL && epc->ops->map_msi_irq == NULL)
{
return -EINVAL;
}
nxmutex_lock(&epc->lock);
ret = epc->ops->map_msi_irq(epc, funcno, phys_addr, interrupt_num,
entry_size, msi_data, msi_addr_offset);
nxmutex_unlock(&epc->lock);
return ret;
}
/****************************************************************************
* Name: pci_epc_get_msi
*
* Description:
* Get the number of MSI interrupt numbers allocated.
*
* Message Control Register for MSI:bit6-bit4:Multiple Message Enable
* Software writes to this field to indicate the number of allocate vectors
* Equal to or less than the number of requested vectors. The number of
* allocated vectors is aligned to a power of two. If a Function requests
* four vectors (indicated by a Multiple Message Capable encoding of
* 010b), system software can allocate either four, two, or one vector
* by writing a 010b, 001b, or 000b to this field, respectively. When
* MSI Enable is Set, a Function will be allocated at least 1 vector
*
* Invoke to get the number of MSI interrupts allocated by the RC.
*
* Input Parameters:
* epc - The EPC device to which MSI interrupts was requested
* funcno - The physical endpoint function number in the EPC device
*
* Returned Value:
* Return interrupt number if success, negative if failed
****************************************************************************/
int pci_epc_get_msi(FAR struct pci_epc_ctrl_s *epc, uint8_t funcno)
{
int interrupt;
if (epc == NULL || funcno >= epc->max_functions ||
epc->ops->get_msi == NULL)
{
return -EINVAL;
}
nxmutex_lock(&epc->lock);
interrupt = epc->ops->get_msi(epc, funcno);
nxmutex_unlock(&epc->lock);
if (interrupt >= 0)
{
interrupt = 1 << interrupt;
}
return interrupt;
}
/****************************************************************************
* Name: pci_epc_set_msi
*
* Description:
* Set the number of MSI interrupt numbers required.
*
* Message Control Register for MSI:bit6-bit4:Multiple Message Enable
* Software writes to this field to indicate the number of allocate vectors
* Equal to or less than the number of requested vectors. The number of
* allocated vectors is aligned to a power of two. If a Function requests
* four vectors (indicated by a Multiple Message Capable encoding of
* 010b), system software can allocate either four, two, or one vector
* by writing a 010b, 001b, or 000b to this field, respectively. When
* MSI Enable is Set, a Function will be allocated at least 1 vector
*
* Invoke to set the required number of MSI interrupts.
*
* Input Parameters:
* epc - The EPC device on which MSI has to be configured
* funcno - The physical endpoint function number in the EPC device
* interrupts - Number of MSI interrupts required by the EPF
*
* Returned Value:
* Return 0 if success, negative if failed
****************************************************************************/
int pci_epc_set_msi(FAR struct pci_epc_ctrl_s *epc, uint8_t funcno,
uint8_t interrupts)
{
int ret;
if (epc == NULL || funcno >= epc->max_functions ||
interrupts < 1 || interrupts > 32 || epc->ops->set_msi == NULL)
{
return -EINVAL;
}
interrupts = order_base_2(interrupts);
nxmutex_lock(&epc->lock);
ret = epc->ops->set_msi(epc, funcno, interrupts);
nxmutex_unlock(&epc->lock);
return ret;
}
/****************************************************************************
* Name: pci_epc_get_msix
*
* Description:
* Get the number of MSI-X interrupt numbers allocated.
*
* Message Control Register for MSI-X:bit10-bit0:Table Size
* System software reads this field to determine the MSI-X Table Size N,
* which is encoded as N-1. For example, a returned value of 000 0000 0011b
* indicates a table size of 4.
*
* Invoke to get the number of MSI-X interrupts allocated by the RC.
*
* Input Parameters:
* epc - The EPC device to which MSI-X interrupts was requested
* funcno - The physical endpoint function number in the EPC device
*
* Returned Value:
* Return interrupt + 1 number if success, negative if failed
****************************************************************************/
int pci_epc_get_msix(FAR struct pci_epc_ctrl_s *epc, uint8_t funcno)
{
int interrupt;
if (epc == NULL || funcno >= epc->max_functions ||
epc->ops->get_msix == NULL)
{
return -EINVAL;
}
nxmutex_lock(&epc->lock);
interrupt = epc->ops->get_msix(epc, funcno);
nxmutex_unlock(&epc->lock);
if (interrupt >= 0)
{
interrupt += 1;
}
return interrupt;
}
/****************************************************************************
* Name: pci_epc_set_msix
*
* Description:
* Set the number of MSI-X interrupt numbers required.
*
* Message Control Register for MSI-X:bit10-bit0:Table Size
* System software reads this field to determine the MSI-X Table Size N,
* which is encoded as N-1. For example, a returned value of 000 0000 0011b
* indicates a table size of 4.
*
* Invoke to set the required number of MSI-X interrupts.
*
* Input Parameters:
* epc - The EPC device on which MSI-X has to be configured
* funcno - The physical endpoint function number in the EPC device
* interrupts - Number of MSI-X interrupts required by the EPF
* barno - BAR where the MSI-X table resides
* offset - Offset pointing to the start of MSI-X table
*
* Returned Value:
* Return 0 if success, negative if failed
****************************************************************************/
int pci_epc_set_msix(FAR struct pci_epc_ctrl_s *epc, uint8_t funcno,
uint16_t interrupts, int barno, uint32_t offset)
{
int ret;
if (epc == NULL || funcno >= epc->max_functions || interrupts < 1 ||
interrupts > 2048 || epc->ops->set_msix == NULL)
{
return -EINVAL;
}
nxmutex_lock(&epc->lock);
ret = epc->ops->set_msix(epc, funcno, interrupts - 1, barno, offset);
nxmutex_unlock(&epc->lock);
return ret;
}
/****************************************************************************
* Name: pci_epc_unmap_addr
*
* Description:
* Unmap CPU address from PCI address.
*
* Invoke to unmap the CPU address from PCI address.
*
* Input Parameters:
* epc - The EPC device on which address is allocated
* funcno - The physical endpoint function number in the EPC device
* phys_addr - Physical address of the local systeme
*
* Returned Value:
* None
****************************************************************************/
void pci_epc_unmap_addr(FAR struct pci_epc_ctrl_s *epc, uint8_t funcno,
uintptr_t phys_addr)
{
if (epc == NULL || funcno >= epc->max_functions ||
epc->ops->unmap_addr == NULL)
{
return;
}
nxmutex_lock(&epc->lock);
epc->ops->unmap_addr(epc, funcno, phys_addr);
nxmutex_unlock(&epc->lock);
}
/****************************************************************************
* Name: pci_epc_map_addr
*
* Description:
* Map CPU address to PCI address.
*
* Invoke to map CPU address with PCI address.
*
* Input Parameters:
* epc - The EPC device on which address is allocated
* funcno - The physical endpoint function number in the EPC device
* phys_addr - Physical address of the local system
* pci_addr - PCI address to which the physical address should be mapped
* size - The size of the allocation
*
* Returned Value:
* Return 0 if success, negative if failed
****************************************************************************/
int pci_epc_map_addr(FAR struct pci_epc_ctrl_s *epc, uint8_t funcno,
uintptr_t phys_addr, uint64_t pci_addr, size_t size)
{
int ret;
if (epc == NULL || funcno >= epc->max_functions ||
epc->ops->map_addr == NULL)
{
return -EINVAL;
}
nxmutex_lock(&epc->lock);
ret = epc->ops->map_addr(epc, funcno, phys_addr, pci_addr, size);
nxmutex_unlock(&epc->lock);
return ret;
}
/****************************************************************************
* Name: pci_epc_clear_bar
*
* Description:
* Reset the BAR.
*
* Invoke to reset the BAR of the endpoint device.
*
* Input Parameters:
* epc - The EPC device for which the BAR has to be cleared
* funcno - The physical endpoint function number in the EPC device
* bar - The struct bar that contains the BAR information
*
* Returned Value:
* None
****************************************************************************/
void pci_epc_clear_bar(FAR struct pci_epc_ctrl_s *epc, uint8_t funcno,
FAR struct pci_epf_bar_s *bar)
{
if (epc == NULL || funcno >= epc->max_functions ||
epc->ops->clear_bar == NULL ||
(bar->barno == PCI_STD_NUM_BARS - 1 &&
(bar->flags & PCI_BASE_ADDRESS_MEM_TYPE_64)))
{
return;
}
nxmutex_lock(&epc->lock);
epc->ops->clear_bar(epc, funcno, bar);
nxmutex_unlock(&epc->lock);
}
/****************************************************************************
* Name: pci_epc_set_bar
*
* Description:
* Configure BAR in order for host to assign PCI addr space.
*
* Invoke to configure the BAR of the endpoint device.
*
* Input Parameters:
* epc - The EPC device on which BAR has to be configured
* funcno - The physical endpoint function number in the EPC device
* bar - The struct bar that contains the BAR information
*
* Returned Value:
* Return 0 if success, negative if failed
****************************************************************************/
int pci_epc_set_bar(FAR struct pci_epc_ctrl_s *epc, uint8_t funcno,
FAR struct pci_epf_bar_s *bar)
{
int flags = bar->flags;
int ret;
if (epc == NULL || funcno >= epc->max_functions ||
(bar->barno == PCI_STD_NUM_BARS - 1 &&
flags & PCI_BASE_ADDRESS_MEM_TYPE_64) ||
(bar->size > UINT32_MAX &&
!(flags & PCI_BASE_ADDRESS_MEM_TYPE_64)) ||
epc->ops->set_bar == NULL)
{
return -EINVAL;
}
nxmutex_lock(&epc->lock);
ret = epc->ops->set_bar(epc, funcno, bar);
nxmutex_unlock(&epc->lock);
return ret;
}
/****************************************************************************
* Name: pci_epc_write_header
*
* Description:
* Write standard configuration header.
*
* Invoke to write the configuration header to the endpoint controller.
* Every endpoint controller will have a dedicated location to which the
* standard configuration header would be written. The callback function
* should write the header fields to this dedicated location.
*
* Input Parameters:
* epc - The EPC device to which the configuration header should be
* written
* funcno - The physical endpoint function number in the EPC device
* header - Standard configuration header fields
*
* Returned Value:
* Return 0 if success, negative if failed
****************************************************************************/
int pci_epc_write_header(FAR struct pci_epc_ctrl_s *epc, uint8_t funcno,
FAR struct pci_epf_header_s *header)
{
int ret;
if (epc == NULL || epc->ops->write_header == NULL ||
funcno >= epc->max_functions)
{
return -EINVAL;
}
nxmutex_lock(&epc->lock);
ret = epc->ops->write_header(epc, funcno, header);
nxmutex_unlock(&epc->lock);
return ret;
}
/****************************************************************************
* Name: pci_epc_add_epf
*
* Description:
* This function is used to bind PCI endpoint function to an endpoint
* controller.
*
* A PCI endpoint device can have one or more functions In the case of
* PCIe,the specification allows up to 8 PCIe endpoint functions Invoke
* pci_epc_add_epf() to add a PCI endpoint function to an endpoint
* controller.
*
* Input Parameters:
* epc - The EPC device to which the endpoint function should be added
* epf - The endpoint function to be added
*
* Returned Value:
* Return 0 if success, negative if failed
****************************************************************************/
int pci_epc_add_epf(FAR struct pci_epc_ctrl_s *epc,
FAR struct pci_epf_device_s *epf)
{
uint32_t funcno;
int ret = 0;
DEBUGASSERT(epc != NULL && epf != NULL);
if (epf->epc)
{
return -EBUSY;
}
nxmutex_lock(&epc->lock);
funcno = find_first_zero_bit(&epc->funcno_map, epc->max_functions);
if (funcno >= epc->max_functions)
{
pcierr("Exceeding max supported Function Number\n");
ret = -ENOENT;
goto out;
}
set_bit(funcno, &epc->funcno_map);
epf->funcno = funcno;
epf->epc = epc;
list_add_tail(&epc->epf, &epf->node);
out:
nxmutex_unlock(&epc->lock);
return ret;
}
/****************************************************************************
* Name: pci_epc_remove_epf
*
* Description:
* This function is used to remove PCI endpoint function from endpoint
* controller.
*
* Invoke to remove PCI endpoint function from the endpoint controller.
*
* Input Parameters:
* epc - The EPC device from which the endpoint function should be removed
* epf - The endpoint function to be removed
*
* Returned Value:
* None
****************************************************************************/
void pci_epc_remove_epf(FAR struct pci_epc_ctrl_s *epc,
FAR struct pci_epf_device_s *epf)
{
if (epc == NULL || epf == NULL)
{
return;
}
nxmutex_lock(&epc->lock);
clear_bit(epf->funcno, &epc->funcno_map);
list_delete(&epf->node);
epf->epc = NULL;
nxmutex_unlock(&epc->lock);
}
/****************************************************************************
* Name: pci_epc_linkup
*
* Description:
* Notify the EPF device that EPC device has established a connection with
* the Root Complex.
*
* Invoke to Notify the EPF device that the EPC device has established a
* connection with the Root Complex.
*
* Input Parameters:
* epc - The EPC device which has established link with the host
*
* Returned Value:
* None
****************************************************************************/
void pci_epc_linkup(FAR struct pci_epc_ctrl_s *epc)
{
FAR struct pci_epf_device_s *epf;
if (epc == NULL)
{
return;
}
nxmutex_lock(&epc->lock);
list_for_every_entry(&epc->epf, epf, struct pci_epf_device_s, node)
{
nxmutex_lock(&epf->lock);
if (epf->event_ops && epf->event_ops->link_up)
{
epf->event_ops->link_up(epf);
}
nxmutex_unlock(&epf->lock);
}
nxmutex_unlock(&epc->lock);
}
/****************************************************************************
* Name: pci_epc_linkdown
*
* Description:
* Notify the EPF device that EPC device has dropped the connection with
* the Root Complex.
*
* Invoke to Notify the EPF device that the EPC device has dropped the
* connection with the Root Complex.
*
* Input Parameters:
* epc - The EPC device which has dropped the link with the host
*
* Returned Value:
* None
****************************************************************************/
void pci_epc_linkdown(FAR struct pci_epc_ctrl_s *epc)
{
struct pci_epf_device_s *epf;
if (epc == NULL)
{
return;
}
nxmutex_lock(&epc->lock);
list_for_every_entry(&epc->epf, epf, struct pci_epf_device_s, node)
{
nxmutex_lock(&epf->lock);
if (epf->event_ops && epf->event_ops->link_down)
{
epf->event_ops->link_down(epf);
}
nxmutex_unlock(&epf->lock);
}
nxmutex_unlock(&epc->lock);
}
/****************************************************************************
* Name: pci_epc_init_notify
*
* Description:
* Notify the EPF device that EPC device's core initialization is
* completed.
*
* Invoke to Notify the EPF device that the EPC device's initialization
* is completed.
*
* Input Parameters:
* epc - The EPC device whose core initialization is completed
*
* Returned Value:
* None
****************************************************************************/
void pci_epc_init_notify(FAR struct pci_epc_ctrl_s *epc)
{
FAR struct pci_epf_device_s *epf;
if (epc == NULL)
{
return;
}
nxmutex_lock(&epc->lock);
list_for_every_entry(&epc->epf, epf, struct pci_epf_device_s, node)
{
nxmutex_lock(&epf->lock);
if (epf->event_ops && epf->event_ops->core_init)
{
epf->event_ops->core_init(epf);
}
nxmutex_unlock(&epf->lock);
}
nxmutex_unlock(&epc->lock);
}
/****************************************************************************
* Name: pci_epc_bme_notify
*
* Description:
* Notify the EPF device that the EPC device has received the BME event
* from the Root complex.
*
* Invoke to Notify the EPF device that the EPC device has received the Bus
* Master Enable (BME) event from the Root complex.
*
* Input Parameters:
* epc - The EPC device that received the BME event
*
* Returned Value:
* None
****************************************************************************/
void pci_epc_bme_notify(FAR struct pci_epc_ctrl_s *epc)
{
FAR struct pci_epf_device_s *epf;
if (epc == NULL)
{
return;
}
nxmutex_lock(&epc->lock);
list_for_every_entry(&epc->epf, epf, struct pci_epf_device_s, node)
{
nxmutex_lock(&epf->lock);
if (epf->event_ops && epf->event_ops->bme)
{
epf->event_ops->bme(epf);
}
nxmutex_unlock(&epf->lock);
}
nxmutex_unlock(&epc->lock);
}
/****************************************************************************
* Name: pci_epc_create
*
* Description:
* This function is used to destroy the EPC device.
*
* Invoke to create a new EPC device and add it to pci_epc class.
*
* Input Parameters:
* name - EPC name strings
* ops - Function pointers for performing EPC operations
* Returned Value:
* Return struct pci_epc_ctrl_s * if success, NULL if failed.
****************************************************************************/
FAR struct pci_epc_ctrl_s *
pci_epc_create(FAR const char *name, FAR const struct pci_epc_ops_s *ops)
{
FAR struct pci_epc_ctrl_s *epc;
size_t len;
if (name == NULL || ops == NULL)
{
return NULL;
}
len = strlen(name) + 1;
epc = kmm_zalloc(sizeof(*epc) + len);
if (epc == NULL)
{
return NULL;
}
memcpy(epc->name, name, len);
nxmutex_init(&epc->lock);
list_initialize(&epc->epf);
epc->ops = ops;
nxmutex_lock(&g_pci_epc_lock);
list_add_tail(&g_pci_epc_device_list, &epc->node);
nxmutex_unlock(&g_pci_epc_lock);
return epc;
}
/****************************************************************************
* Name: pci_epc_destroy
*
* Description:
* This function is used to create a new endpoint controller (EPC) device.
*
* Invoke to destroy the PCI EPC device.
*
* Input Parameters:
* epc - The EPC device that has to be destroyed
*
* Returned Value:
* None
****************************************************************************/
void pci_epc_destroy(FAR struct pci_epc_ctrl_s *epc)
{
if (epc == NULL)
{
return;
}
nxmutex_lock(&g_pci_epc_lock);
list_delete(&epc->node);
nxmutex_unlock(&g_pci_epc_lock);
nxmutex_destroy(&epc->lock);
kmm_free(epc);
}