/alps/pcitool

To get this branch, use:
bzr branch http://suren.me/webbzr/alps/pcitool
1 by Suren A. Chilingaryan
Initial import
1
/**
2
 *
3
 * @file sysfs.c
4
 * @brief This file contains the functions providing the SysFS-interface.
5
 * @author Guillermo Marcus
6
 * @date 2010-03-01
7
 *
8
 */
9
#include <linux/version.h>
10
#include <linux/string.h>
11
#include <linux/types.h>
12
#include <linux/list.h>
13
#include <linux/interrupt.h>
14
#include <linux/pci.h>
15
#include <linux/cdev.h>
16
#include <linux/wait.h>
17
#include <linux/mm.h>
18
#include <linux/pagemap.h>
19
#include <linux/kernel.h>
20
365 by Suren A. Chilingaryan
Restructure driver headers
21
#include "base.h"
22
23
#define SYSFS_GET_PRIVDATA dev_get_drvdata(dev)
24
#define SYSFS_GET_FUNCTION(name) ssize_t name(struct device *dev, struct device_attribute *attr, char *buf)
25
#define SYSFS_SET_FUNCTION(name) ssize_t name(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
26
27
#define SYSFS_ATTR_NAME(name) (dev_attr_##name)
28
29
#define SYSFS_ATTR_CREATE(name) do { \
30
	int err = device_create_file(privdata->class_dev, &SYSFS_ATTR_NAME(name)); \
31
	if (err != 0) return err; \
32
    } while (0)
33
34
#define SYSFS_ATTR_REMOVE(name) do { \
35
	device_remove_file(privdata->class_dev, &SYSFS_ATTR_NAME(name)); \
36
    } while (0)
1 by Suren A. Chilingaryan
Initial import
37
38
#ifdef ENABLE_IRQ
365 by Suren A. Chilingaryan
Restructure driver headers
39
static SYSFS_GET_FUNCTION(pcidriver_show_irq_count)
1 by Suren A. Chilingaryan
Initial import
40
{
364 by Suren A. Chilingaryan
Drop support of kernels prior to 3.2 (Debian 7, Ubuntu 12.04)
41
    pcidriver_privdata_t *privdata = SYSFS_GET_PRIVDATA;
1 by Suren A. Chilingaryan
Initial import
42
364 by Suren A. Chilingaryan
Drop support of kernels prior to 3.2 (Debian 7, Ubuntu 12.04)
43
    return snprintf(buf, PAGE_SIZE, "%d\n", privdata->irq_count);
1 by Suren A. Chilingaryan
Initial import
44
}
45
365 by Suren A. Chilingaryan
Restructure driver headers
46
static SYSFS_GET_FUNCTION(pcidriver_show_irq_queues)
1 by Suren A. Chilingaryan
Initial import
47
{
364 by Suren A. Chilingaryan
Drop support of kernels prior to 3.2 (Debian 7, Ubuntu 12.04)
48
    pcidriver_privdata_t *privdata = SYSFS_GET_PRIVDATA;
49
    int i, offset;
50
51
    /* output will be truncated to PAGE_SIZE */
52
    offset = snprintf(buf, PAGE_SIZE, "Queue\tOutstanding IRQs\n");
53
    for (i = 0; i < PCIDRIVER_INT_MAXSOURCES; i++)
54
        offset += snprintf(buf+offset, PAGE_SIZE-offset, "%d\t%d\n", i, atomic_read(&(privdata->irq_outstanding[i])) );
55
56
    return (offset > PAGE_SIZE ? PAGE_SIZE : offset+1);
1 by Suren A. Chilingaryan
Initial import
57
}
58
#endif
59
365 by Suren A. Chilingaryan
Restructure driver headers
60
static SYSFS_GET_FUNCTION(pcidriver_show_mmap_mode)
1 by Suren A. Chilingaryan
Initial import
61
{
364 by Suren A. Chilingaryan
Drop support of kernels prior to 3.2 (Debian 7, Ubuntu 12.04)
62
    pcidriver_privdata_t *privdata = SYSFS_GET_PRIVDATA;
1 by Suren A. Chilingaryan
Initial import
63
364 by Suren A. Chilingaryan
Drop support of kernels prior to 3.2 (Debian 7, Ubuntu 12.04)
64
    return snprintf(buf, PAGE_SIZE, "%d\n", privdata->mmap_mode);
1 by Suren A. Chilingaryan
Initial import
65
}
66
365 by Suren A. Chilingaryan
Restructure driver headers
67
static SYSFS_SET_FUNCTION(pcidriver_store_mmap_mode)
1 by Suren A. Chilingaryan
Initial import
68
{
364 by Suren A. Chilingaryan
Drop support of kernels prior to 3.2 (Debian 7, Ubuntu 12.04)
69
    pcidriver_privdata_t *privdata = SYSFS_GET_PRIVDATA;
70
    int mode = -1;
71
72
    /* Set the mmap-mode if it is either PCIDRIVER_MMAP_PCI or PCIDRIVER_MMAP_KMEM */
73
    if (sscanf(buf, "%d", &mode) == 1 &&
74
            (mode == PCIDRIVER_MMAP_PCI || mode == PCIDRIVER_MMAP_KMEM))
75
        privdata->mmap_mode = mode;
76
77
    return strlen(buf);
1 by Suren A. Chilingaryan
Initial import
78
}
79
365 by Suren A. Chilingaryan
Restructure driver headers
80
static SYSFS_GET_FUNCTION(pcidriver_show_mmap_area)
1 by Suren A. Chilingaryan
Initial import
81
{
364 by Suren A. Chilingaryan
Drop support of kernels prior to 3.2 (Debian 7, Ubuntu 12.04)
82
    pcidriver_privdata_t *privdata = SYSFS_GET_PRIVDATA;
1 by Suren A. Chilingaryan
Initial import
83
364 by Suren A. Chilingaryan
Drop support of kernels prior to 3.2 (Debian 7, Ubuntu 12.04)
84
    return snprintf(buf, PAGE_SIZE, "%d\n", privdata->mmap_area);
1 by Suren A. Chilingaryan
Initial import
85
}
86
365 by Suren A. Chilingaryan
Restructure driver headers
87
static SYSFS_SET_FUNCTION(pcidriver_store_mmap_area)
1 by Suren A. Chilingaryan
Initial import
88
{
364 by Suren A. Chilingaryan
Drop support of kernels prior to 3.2 (Debian 7, Ubuntu 12.04)
89
    pcidriver_privdata_t *privdata = SYSFS_GET_PRIVDATA;
90
    int temp = -1;
91
92
    sscanf(buf, "%d", &temp);
93
94
    if ((temp >= PCIDRIVER_BAR0) && (temp <= PCIDRIVER_BAR5))
95
        privdata->mmap_area = temp;
96
97
    return strlen(buf);
1 by Suren A. Chilingaryan
Initial import
98
}
99
365 by Suren A. Chilingaryan
Restructure driver headers
100
static SYSFS_GET_FUNCTION(pcidriver_show_kmem_count)
1 by Suren A. Chilingaryan
Initial import
101
{
364 by Suren A. Chilingaryan
Drop support of kernels prior to 3.2 (Debian 7, Ubuntu 12.04)
102
    pcidriver_privdata_t *privdata = SYSFS_GET_PRIVDATA;
1 by Suren A. Chilingaryan
Initial import
103
364 by Suren A. Chilingaryan
Drop support of kernels prior to 3.2 (Debian 7, Ubuntu 12.04)
104
    return snprintf(buf, PAGE_SIZE, "%d\n", atomic_read(&(privdata->kmem_count)));
1 by Suren A. Chilingaryan
Initial import
105
}
106
365 by Suren A. Chilingaryan
Restructure driver headers
107
static SYSFS_SET_FUNCTION(pcidriver_store_kmem_alloc)
1 by Suren A. Chilingaryan
Initial import
108
{
364 by Suren A. Chilingaryan
Drop support of kernels prior to 3.2 (Debian 7, Ubuntu 12.04)
109
    pcidriver_privdata_t *privdata = SYSFS_GET_PRIVDATA;
110
    kmem_handle_t kmem_handle;
111
112
    /* FIXME: guillermo: is validation of parsing an unsigned int enough? */
113
    if (sscanf(buf, "%lu", &kmem_handle.size) == 1)
114
        pcidriver_kmem_alloc(privdata, &kmem_handle);
115
116
    return strlen(buf);
1 by Suren A. Chilingaryan
Initial import
117
}
118
365 by Suren A. Chilingaryan
Restructure driver headers
119
static SYSFS_SET_FUNCTION(pcidriver_store_kmem_free)
1 by Suren A. Chilingaryan
Initial import
120
{
364 by Suren A. Chilingaryan
Drop support of kernels prior to 3.2 (Debian 7, Ubuntu 12.04)
121
    pcidriver_privdata_t *privdata = SYSFS_GET_PRIVDATA;
122
    unsigned int id;
123
    pcidriver_kmem_entry_t *kmem_entry;
124
125
    /* Parse the ID of the kernel memory to be freed, check bounds */
126
    if (sscanf(buf, "%u", &id) != 1 ||
127
            (id >= atomic_read(&(privdata->kmem_count))))
128
        goto err;
129
130
    if ((kmem_entry = pcidriver_kmem_find_entry_id(privdata,id)) == NULL)
131
        goto err;
132
133
    pcidriver_kmem_free_entry(privdata, kmem_entry );
1 by Suren A. Chilingaryan
Initial import
134
err:
364 by Suren A. Chilingaryan
Drop support of kernels prior to 3.2 (Debian 7, Ubuntu 12.04)
135
    return strlen(buf);
1 by Suren A. Chilingaryan
Initial import
136
}
137
365 by Suren A. Chilingaryan
Restructure driver headers
138
static SYSFS_GET_FUNCTION(pcidriver_show_kbuffers)
1 by Suren A. Chilingaryan
Initial import
139
{
364 by Suren A. Chilingaryan
Drop support of kernels prior to 3.2 (Debian 7, Ubuntu 12.04)
140
    pcidriver_privdata_t *privdata = SYSFS_GET_PRIVDATA;
141
    int offset = 0;
142
    struct list_head *ptr;
143
    pcidriver_kmem_entry_t *entry;
144
145
    /* print the header */
146
    offset += snprintf(buf, PAGE_SIZE, "kbuf#\tcpu addr\tsize\n");
147
148
    spin_lock(&(privdata->kmemlist_lock));
149
    list_for_each(ptr, &(privdata->kmem_list)) {
150
        entry = list_entry(ptr, pcidriver_kmem_entry_t, list);
151
152
        /* print entry info */
153
        if (offset > PAGE_SIZE) {
154
            spin_unlock( &(privdata->kmemlist_lock) );
155
            return PAGE_SIZE;
156
        }
157
158
        offset += snprintf(buf+offset, PAGE_SIZE-offset, "%3d\t%08lx\t%lu\n", entry->id, (unsigned long)(entry->dma_handle), entry->size );
159
    }
160
161
    spin_unlock(&(privdata->kmemlist_lock));
162
163
    /* output will be truncated to PAGE_SIZE */
164
    return (offset > PAGE_SIZE ? PAGE_SIZE : offset+1);
1 by Suren A. Chilingaryan
Initial import
165
}
166
365 by Suren A. Chilingaryan
Restructure driver headers
167
static SYSFS_GET_FUNCTION(pcidriver_show_umappings)
1 by Suren A. Chilingaryan
Initial import
168
{
364 by Suren A. Chilingaryan
Drop support of kernels prior to 3.2 (Debian 7, Ubuntu 12.04)
169
    int offset = 0;
170
    pcidriver_privdata_t *privdata = SYSFS_GET_PRIVDATA;
171
    struct list_head *ptr;
172
    pcidriver_umem_entry_t *entry;
173
174
    /* print the header */
175
    offset += snprintf(buf, PAGE_SIZE, "umap#\tn_pages\tsg_ents\n");
176
177
    spin_lock( &(privdata->umemlist_lock) );
178
    list_for_each( ptr, &(privdata->umem_list) ) {
179
        entry = list_entry(ptr, pcidriver_umem_entry_t, list );
180
181
        /* print entry info */
182
        if (offset > PAGE_SIZE) {
183
            spin_unlock( &(privdata->umemlist_lock) );
184
            return PAGE_SIZE;
185
        }
186
187
        offset += snprintf(buf+offset, PAGE_SIZE-offset, "%3d\t%lu\t%lu\n", entry->id,
188
                           (unsigned long)(entry->nr_pages), (unsigned long)(entry->nents));
189
    }
190
191
    spin_unlock( &(privdata->umemlist_lock) );
192
193
    /* output will be truncated to PAGE_SIZE */
194
    return (offset > PAGE_SIZE ? PAGE_SIZE : offset+1);
1 by Suren A. Chilingaryan
Initial import
195
}
196
365 by Suren A. Chilingaryan
Restructure driver headers
197
static SYSFS_SET_FUNCTION(pcidriver_store_umem_unmap)
1 by Suren A. Chilingaryan
Initial import
198
{
364 by Suren A. Chilingaryan
Drop support of kernels prior to 3.2 (Debian 7, Ubuntu 12.04)
199
    pcidriver_privdata_t *privdata = SYSFS_GET_PRIVDATA;
200
    pcidriver_umem_entry_t *umem_entry;
201
    unsigned int id;
202
203
    if (sscanf(buf, "%u", &id) != 1 ||
204
            (id >= atomic_read(&(privdata->umem_count))))
205
        goto err;
206
207
    if ((umem_entry = pcidriver_umem_find_entry_id(privdata, id)) == NULL)
208
        goto err;
209
210
    pcidriver_umem_sgunmap(privdata, umem_entry);
1 by Suren A. Chilingaryan
Initial import
211
err:
364 by Suren A. Chilingaryan
Drop support of kernels prior to 3.2 (Debian 7, Ubuntu 12.04)
212
    return strlen(buf);
1 by Suren A. Chilingaryan
Initial import
213
}
365 by Suren A. Chilingaryan
Restructure driver headers
214
215
static SYSFS_GET_FUNCTION(pcidriver_show_kmem_entry)
216
{
217
    pcidriver_privdata_t *privdata = SYSFS_GET_PRIVDATA;
218
219
    /* As we can be sure that attr.name contains a filename which we
220
     * created (see _pcidriver_sysfs_initialize), we do not need to have
221
     * sanity checks but can directly call simple_strtol() */
222
    int id = simple_strtol(attr->attr.name + strlen("kbuf"), NULL, 10);
223
    pcidriver_kmem_entry_t *entry = pcidriver_kmem_find_entry_id(privdata, id);
224
    if (entry) {
225
        unsigned long addr = entry->cpua;
226
        unsigned long dma_addr = entry->dma_handle;
227
228
        if (entry->size >= 16) {
229
            pcidriver_kmem_sync_entry(privdata, entry, PCILIB_KMEM_SYNC_FROMDEVICE);
230
            return snprintf(buf, PAGE_SIZE, "buffer: %d\naddr: %lx\nhw addr: %llx\nbus addr: %lx\ntype: %lx\nuse: 0x%lx\nitem: %lu\nsize: %lu\nrefs: %lu\nhw ref: %i\nmode: 0x%lx\ndata: %8x %8x %8x %8x\n", id, addr, virt_to_phys((void*)addr), dma_addr, entry->type, entry->use, entry->item, entry->size, entry->refs&KMEM_REF_COUNT, (entry->refs&KMEM_REF_HW)?1:0, entry->mode, *(u32*)(entry->cpua), *(u32*)(entry->cpua + 4),  *(u32*)(entry->cpua + 8), *(u32*)(entry->cpua + 12));
231
        } else
232
            return snprintf(buf, PAGE_SIZE, "buffer: %d\naddr: %lx\nhw addr: %llx\nbus addr: %lx\ntype: %lx\nuse: 0x%lx\nitem: %lu\nsize: %lu\nrefs: %lu\nhw ref: %i\nmode: 0x%lx\n", id, addr, virt_to_phys((void*)addr), dma_addr, entry->type, entry->use, entry->item, entry->size, entry->refs&KMEM_REF_COUNT, (entry->refs&KMEM_REF_HW)?1:0, entry->mode);
233
    } else
234
        return snprintf(buf, PAGE_SIZE, "I am in the kmem_entry show function for buffer %d\n", id);
235
}
236
237
static SYSFS_GET_FUNCTION(pcidriver_show_umem_entry)
238
{
239
    return 0;
240
}
241
242
243
#ifdef ENABLE_IRQ
244
static DEVICE_ATTR(irq_count, S_IRUGO, pcidriver_show_irq_count, NULL);
245
static DEVICE_ATTR(irq_queues, S_IRUGO, pcidriver_show_irq_queues, NULL);
246
#endif
247
248
static DEVICE_ATTR(mmap_mode, 0664, pcidriver_show_mmap_mode, pcidriver_store_mmap_mode);
249
static DEVICE_ATTR(mmap_area, 0664, pcidriver_show_mmap_area, pcidriver_store_mmap_area);
250
static DEVICE_ATTR(kmem_count, 0444, pcidriver_show_kmem_count, NULL);
251
static DEVICE_ATTR(kbuffers, 0444, pcidriver_show_kbuffers, NULL);
252
static DEVICE_ATTR(kmem_alloc, 0220, NULL, pcidriver_store_kmem_alloc);
253
static DEVICE_ATTR(kmem_free, 0220, NULL, pcidriver_store_kmem_free);
254
static DEVICE_ATTR(umappings, 0444, pcidriver_show_umappings, NULL);
255
static DEVICE_ATTR(umem_unmap, 0220, NULL, pcidriver_store_umem_unmap);
256
257
int pcidriver_create_sysfs_attributes(pcidriver_privdata_t *privdata) {
258
#ifdef ENABLE_IRQ
259
    SYSFS_ATTR_CREATE(irq_count);
260
    SYSFS_ATTR_CREATE(irq_queues);
261
#endif
262
263
    SYSFS_ATTR_CREATE(mmap_mode);
264
    SYSFS_ATTR_CREATE(mmap_area);
265
    SYSFS_ATTR_CREATE(kmem_count);
266
    SYSFS_ATTR_CREATE(kmem_alloc);
267
    SYSFS_ATTR_CREATE(kmem_free);
268
    SYSFS_ATTR_CREATE(kbuffers);
269
    SYSFS_ATTR_CREATE(umappings);
270
    SYSFS_ATTR_CREATE(umem_unmap);
271
272
    return 0;
273
}
274
275
void pcidriver_remove_sysfs_attributes(pcidriver_privdata_t *privdata) {
276
#ifdef ENABLE_IRQ
277
    SYSFS_ATTR_REMOVE(irq_count);
278
    SYSFS_ATTR_REMOVE(irq_queues);
279
#endif
280
281
    SYSFS_ATTR_REMOVE(mmap_mode);
282
    SYSFS_ATTR_REMOVE(mmap_area);
283
    SYSFS_ATTR_REMOVE(kmem_count);
284
    SYSFS_ATTR_REMOVE(kmem_alloc);
285
    SYSFS_ATTR_REMOVE(kmem_free);
286
    SYSFS_ATTR_REMOVE(kbuffers);
287
    SYSFS_ATTR_REMOVE(umappings);
288
    SYSFS_ATTR_REMOVE(umem_unmap);
289
}
290
291
/**
292
 *
293
 * Removes the file from sysfs and frees the allocated (kstrdup()) memory.
294
 *
295
 */
296
void pcidriver_sysfs_remove(pcidriver_privdata_t *privdata, struct device_attribute *sysfs_attr)
297
{
298
    device_remove_file(privdata->class_dev, sysfs_attr);
299
    kfree(sysfs_attr->attr.name);
300
}
301
302
/**
303
 *
304
 * Initializes the sysfs attributes for an kmem/umem-entry
305
 *
306
 */
307
static int _pcidriver_sysfs_initialize(pcidriver_privdata_t *privdata, int id, struct device_attribute *sysfs_attr, const char *fmtstring, SYSFS_GET_FUNCTION((*callback)))
308
{
309
    /* sysfs attributes for kmem buffers don’t make sense before 2.6.13, as
310
       we have no mmap support before */
311
    char namebuffer[16];
312
313
    /* allocate space for the name of the attribute */
314
    snprintf(namebuffer, sizeof(namebuffer), fmtstring, id);
315
316
    if ((sysfs_attr->attr.name = kstrdup(namebuffer, GFP_KERNEL)) == NULL)
317
        return -ENOMEM;
318
319
    sysfs_attr->attr.mode = S_IRUGO;
320
    sysfs_attr->show = callback;
321
    sysfs_attr->store = NULL;
322
323
    /* name and add attribute */
324
    if (device_create_file(privdata->class_dev, sysfs_attr) != 0)
325
        return -ENXIO; /* Device not configured. Not the really best choice, but hm. */
326
327
    return 0;
328
}
329
330
int pcidriver_sysfs_initialize_kmem(pcidriver_privdata_t *privdata, int id, struct device_attribute *sysfs_attr)
331
{
332
    return _pcidriver_sysfs_initialize(privdata, id, sysfs_attr, "kbuf%d", pcidriver_show_kmem_entry);
333
}
334
335
int pcidriver_sysfs_initialize_umem(pcidriver_privdata_t *privdata, int id, struct device_attribute *sysfs_attr)
336
{
337
    return _pcidriver_sysfs_initialize(privdata, id, sysfs_attr, "umem%d", pcidriver_show_umem_entry);
338
}