1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
|
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/file.h>
#include "model.h"
#include "error.h"
#include "kmem.h"
#include "pcilib.h"
#include "pci.h"
typedef struct pcilib_software_register_bank_context_s pcilib_software_register_bank_context_t;
/**
* structure defining the context of software registers
*/
struct pcilib_software_register_bank_context_s {
pcilib_register_bank_context_t bank_ctx; /**< the bank context associated with the software registers */
pcilib_kmem_handle_t *kmem; /**< the kernel memory for software registers */
void *addr; /**< the virtual adress of the allocated kernel memory*/
};
void pcilib_software_registers_close(pcilib_t *ctx, pcilib_register_bank_context_t *bank_ctx) {
if (((pcilib_software_register_bank_context_t*)bank_ctx)->kmem)
pcilib_free_kernel_memory(ctx, ((pcilib_software_register_bank_context_t*)bank_ctx)->kmem, PCILIB_KMEM_FLAG_REUSE);
free(bank_ctx);
}
pcilib_register_bank_context_t* pcilib_software_registers_open(pcilib_t *ctx, pcilib_register_bank_t bank, const char* model, const void *args) {
int err;
pcilib_software_register_bank_context_t *bank_ctx;
pcilib_kmem_handle_t *handle;
pcilib_lock_t *lock;
pcilib_kmem_reuse_state_t reused;
const pcilib_register_bank_description_t *bank_desc = ctx->banks + bank;
if (bank_desc->size > PCILIB_KMEM_PAGE_SIZE) {
pcilib_error("Currently software register banks are limited to %lu bytes, but %lu requested", PCILIB_KMEM_PAGE_SIZE, bank_desc->size);
return NULL;
}
bank_ctx = calloc(1, sizeof(pcilib_software_register_bank_context_t));
if (!bank_ctx) {
pcilib_error("Memory allocation for bank context has failed");
return NULL;
}
/*we get a lock to protect the creation of the kernel space for software registers against multiple creations*/
lock = pcilib_get_lock(ctx, PCILIB_LOCK_FLAGS_DEFAULT, "softreg/%s", bank_desc->name);
if (!lock) {
pcilib_software_registers_close(ctx, (pcilib_register_bank_context_t*)bank_ctx);
pcilib_error("Failed to initialize a lock to protect bank %s with software registers", bank_desc->name);
return NULL;
}
err = pcilib_lock(lock);
if (err) {
pcilib_return_lock(ctx, PCILIB_LOCK_FLAGS_DEFAULT, lock);
pcilib_software_registers_close(ctx, (pcilib_register_bank_context_t*)bank_ctx);
pcilib_error("Error (%i) obtaining lock on bank %s with software registers", err, bank_desc->name);
return NULL;
}
/*creation of the kernel space*/
handle = pcilib_alloc_kernel_memory(ctx, PCILIB_KMEM_TYPE_PAGE, 1, PCILIB_KMEM_PAGE_SIZE, 0, PCILIB_KMEM_USE(PCILIB_KMEM_USE_SOFTWARE_REGISTERS, bank_desc->addr), PCILIB_KMEM_FLAG_REUSE|PCILIB_KMEM_FLAG_PERSISTENT);
if (!handle) {
pcilib_unlock(lock);
pcilib_return_lock(ctx, PCILIB_LOCK_FLAGS_DEFAULT, lock);
pcilib_software_registers_close(ctx, (pcilib_register_bank_context_t*)bank_ctx);
pcilib_error("Allocation of kernel memory for software registers has failed");
return NULL;
}
bank_ctx->kmem = handle;
bank_ctx->addr = pcilib_kmem_get_block_ua(ctx, handle, 0);
reused = pcilib_kmem_is_reused(ctx, handle);
if ((reused & PCILIB_KMEM_REUSE_REUSED) == 0) {
pcilib_register_t i;
if (reused & PCILIB_KMEM_REUSE_PARTIAL) {
pcilib_unlock(lock);
pcilib_return_lock(ctx, PCILIB_LOCK_FLAGS_DEFAULT, lock);
pcilib_software_registers_close(ctx, (pcilib_register_bank_context_t*)bank_ctx);
pcilib_error("Inconsistent software registers are found (only part of required buffers is available)");
return NULL;
}
/* here we fill the software registers with their default value*/
for (i = 0; ctx->model_info.registers[i].name != NULL; i++) {
if ((ctx->model_info.registers[i].bank == ctx->banks[bank].addr)&&(ctx->model_info.registers[i].type == PCILIB_REGISTER_STANDARD)) {
*(pcilib_register_value_t*)(bank_ctx->addr + ctx->model_info.registers[i].addr) = ctx->model_info.registers[i].defvalue;
}
}
}
pcilib_unlock(lock);
pcilib_return_lock(ctx, PCILIB_LOCK_FLAGS_DEFAULT, lock);
return (pcilib_register_bank_context_t*)bank_ctx;
}
int pcilib_software_registers_read(pcilib_t *ctx, pcilib_register_bank_context_t *bank_ctx, pcilib_register_addr_t addr, pcilib_register_value_t *value){
if ((addr + sizeof(pcilib_register_value_t)) > bank_ctx->bank->size) {
pcilib_error("Trying to access space outside of the define register bank (bank: %s, addr: 0x%lx)", bank_ctx->bank->name, addr);
return PCILIB_ERROR_INVALID_ADDRESS;
}
// we consider this atomic operation and, therefore, do no locking
*value = *(pcilib_register_value_t*)(((pcilib_software_register_bank_context_t*)bank_ctx)->addr + addr);
return 0;
}
int pcilib_software_registers_write(pcilib_t *ctx, pcilib_register_bank_context_t *bank_ctx, pcilib_register_addr_t addr, pcilib_register_value_t value) {
if ((addr + sizeof(pcilib_register_value_t)) > bank_ctx->bank->size) {
pcilib_error("Trying to access space outside of the define register bank (bank: %s, addr: 0x%lx)", bank_ctx->bank->name, addr);
return PCILIB_ERROR_INVALID_ADDRESS;
}
// we consider this atomic operation and, therefore, do no locking
*(pcilib_register_value_t*)(((pcilib_software_register_bank_context_t*)bank_ctx)->addr + addr) = value;
return 0;
}
|