summaryrefslogtreecommitdiff
path: root/drivers/staging/poch
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/staging/poch')
-rw-r--r--drivers/staging/poch/README124
-rw-r--r--drivers/staging/poch/poch.c198
-rw-r--r--drivers/staging/poch/poch.h16
3 files changed, 222 insertions, 116 deletions
diff --git a/drivers/staging/poch/README b/drivers/staging/poch/README
index 842afd464993..ac76ff969a2f 100644
--- a/drivers/staging/poch/README
+++ b/drivers/staging/poch/README
@@ -1,7 +1,6 @@
TODO:
- Rx block size is limited to < 2048, hardware bug?
- Group size is limited to < page size, kernel alloc/mmap API issues
- - fix/workaround cache issues in circular buffer header
- test whether Tx is transmitting data from provided buffers
- handle device unplug case
- handle temperature above threshold
@@ -10,5 +9,128 @@ TODO:
- audit userspace interfaces
- get reserved major/minor if needed
+Sample Code:
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <sys/ioctl.h>
+#include <poll.h>
+#include <stdio.h>
+#include <error.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdint.h>
+
+#include <sysfs/libsysfs.h>
+
+#include <poch.h>
+
+struct pconsume {
+ uint32_t * offsets;
+ uint32_t nfetch;
+ uint32_t nflush;
+};
+
+uint32_t offsets[10];
+
+void process_group(unsigned char *buf, uint32_t size)
+{
+ uint16_t *buf16 = (uint16_t *)buf;
+
+ printf("RX: %p %u %04x %04x %04x %04x %04x %04x\n", buf, size,
+ buf16[0], buf16[1], buf16[2], buf16[3], buf16[4], buf16[5]);
+}
+
+int main()
+{
+ struct sysfs_attribute *attr;
+ char *path;
+ int ret;
+ unsigned long mmap_size;
+ int fd;
+ unsigned char *cbuf;
+
+ uint32_t nflush;
+ struct pollfd poll_fds;
+ int count = 0;
+ int i;
+
+ path = "/sys/class/pocketchange/poch0/ch0/block_size";
+ attr = sysfs_open_attribute(path);
+ ret = sysfs_write_attribute(attr, "256", strlen("256"));
+ if (ret == -1)
+ error(1, errno, "error writing attribute %s", path);
+ sysfs_close_attribute(attr);
+
+ path = "/sys/class/pocketchange/poch0/ch0/group_size";
+ attr = sysfs_open_attribute(path);
+ ret = sysfs_write_attribute(attr, "4096", strlen("4096"));
+ if (ret == -1)
+ error(1, errno, "error writing attribute %s", path);
+ sysfs_close_attribute(attr);
+
+ path = "/sys/class/pocketchange/poch0/ch0/group_count";
+ attr = sysfs_open_attribute(path);
+ ret = sysfs_write_attribute(attr, "64", strlen("64"));
+ if (ret == -1)
+ error(1, errno, "error writing attribute %s", path);
+ sysfs_close_attribute(attr);
+
+ fd = open("/dev/ch0", O_RDWR);
+ if (fd == -1)
+ error(1, errno, "error opening device node");
+
+ path = "/sys/class/pocketchange/poch0/ch0/mmap_size";
+ attr = sysfs_open_attribute(path);
+ ret = sysfs_read_attribute(attr);
+ if (ret == -1)
+ error(1, errno, "error reading attribute %s", path);
+ printf("%s", attr->value);
+ sscanf(attr->value, "%lu", &mmap_size);
+ sysfs_close_attribute(attr);
+
+ cbuf = mmap(NULL, mmap_size, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE, fd, 0);
+ if (cbuf == MAP_FAILED)
+ error(1, errno, "error mapping DMA buffers");
+
+ ret = ioctl(fd, POCH_IOC_TRANSFER_START, 0);
+ if (ret == -1)
+ error(1, errno, "error starting transfer");
+
+ nflush = 0;
+ while (1) {
+ struct pconsume consume;
+
+ consume.offsets = offsets;
+ consume.nfetch = 10;
+ consume.nflush = nflush;
+
+ ret = ioctl(fd, POCH_IOC_CONSUME, &consume);
+ if (ret == -1)
+ error(1, errno, "error consuming groups");
+
+ nflush = consume.nfetch;
+
+ for (i = 0; i < nflush; i++) {
+ process_group(cbuf + consume.offsets[i], 4096);
+
+ count++;
+ if (count == 1000)
+ break;
+ }
+
+ if (count == 1000)
+ break;
+ }
+
+ ret = ioctl(fd, POCH_IOC_TRANSFER_STOP, 0);
+ if (ret == -1)
+ error(1, errno, "error starting transfer");
+
+ return 0;
+}
+
Please send patches to Greg Kroah-Hartman <greg@kroah.com> and
Vijay Kumar <vijaykumar@bravegnu.org> and Jaya Kumar <jayakumar.lkml@gmail.com>
diff --git a/drivers/staging/poch/poch.c b/drivers/staging/poch/poch.c
index 2eb8e3d43c4d..9095158fb1b3 100644
--- a/drivers/staging/poch/poch.c
+++ b/drivers/staging/poch/poch.c
@@ -197,10 +197,9 @@ struct channel_info {
/* Contains the header and circular buffer exported to userspace. */
spinlock_t group_offsets_lock;
- struct poch_cbuf_header *header;
- struct page *header_pg;
- unsigned long header_size;
+ /* Last group consumed by user space. */
+ unsigned int consumed;
/* Last group indicated as 'complete' to user space. */
unsigned int transfer;
@@ -245,6 +244,16 @@ struct poch_dev {
struct device *dev;
};
+static int synth_rx;
+module_param(synth_rx, bool, 0600);
+MODULE_PARM_DESC(synth_rx,
+ "Synthesize received values using a counter. Default: No");
+
+static int loopback;
+module_param(loopback, bool, 0600);
+MODULE_PARM_DESC(loopback,
+ "Enable hardware loopback of trasnmitted data. Default: No");
+
static dev_t poch_first_dev;
static struct class *poch_cls;
static DEFINE_IDR(poch_ids);
@@ -317,14 +326,12 @@ static ssize_t show_mmap_size(struct device *dev,
int len;
unsigned long mmap_size;
unsigned long group_pages;
- unsigned long header_pages;
unsigned long total_group_pages;
group_pages = npages(channel->group_size);
- header_pages = npages(channel->header_size);
total_group_pages = group_pages * channel->group_count;
- mmap_size = (header_pages + total_group_pages) * PAGE_SIZE;
+ mmap_size = total_group_pages * PAGE_SIZE;
len = sprintf(buf, "%lu\n", mmap_size);
return len;
}
@@ -357,10 +364,8 @@ static int poch_channel_alloc_groups(struct channel_info *channel)
{
unsigned long i;
unsigned long group_pages;
- unsigned long header_pages;
group_pages = npages(channel->group_size);
- header_pages = npages(channel->header_size);
for (i = 0; i < channel->group_count; i++) {
struct poch_group_info *group;
@@ -390,8 +395,7 @@ static int poch_channel_alloc_groups(struct channel_info *channel)
* this?
*/
group->dma_addr = page_to_pfn(group->pg) * PAGE_SIZE;
- group->user_offset =
- (header_pages + (i * group_pages)) * PAGE_SIZE;
+ group->user_offset = (i * group_pages) * PAGE_SIZE;
printk(KERN_INFO PFX "%ld: user_offset: 0x%lx\n", i,
group->user_offset);
@@ -476,7 +480,7 @@ static void channel_dma_init(struct channel_info *channel)
fpga + block_count_reg);
iowrite32(channel->group_count, fpga + group_count_reg);
/* FIXME: Hardcoded groups per int. Get it from sysfs? */
- iowrite32(1, fpga + groups_per_int_reg);
+ iowrite32(16, fpga + groups_per_int_reg);
/* Unlock PCI address? Not defined in the data sheet, but used
* in the reference code by Redrapids.
@@ -513,56 +517,6 @@ static void channel_dma_init(struct channel_info *channel)
}
-static int poch_channel_alloc_header(struct channel_info *channel)
-{
- struct poch_cbuf_header *header = channel->header;
- unsigned long group_offset_size;
- unsigned long tot_group_offsets_size;
-
- /* Allocate memory to hold header exported userspace */
- group_offset_size = sizeof(header->group_offsets[0]);
- tot_group_offsets_size = group_offset_size * channel->group_count;
- channel->header_size = sizeof(*header) + tot_group_offsets_size;
- channel->header_pg = alloc_pages(GFP_KERNEL | __GFP_ZERO,
- get_order(channel->header_size));
- if (!channel->header_pg)
- return -ENOMEM;
-
- channel->header = page_address(channel->header_pg);
-
- return 0;
-}
-
-static void poch_channel_free_header(struct channel_info *channel)
-{
- unsigned int order;
-
- order = get_order(channel->header_size);
- __free_pages(channel->header_pg, order);
-}
-
-static void poch_channel_init_header(struct channel_info *channel)
-{
- int i;
- struct poch_group_info *groups;
- s32 *group_offsets;
-
- channel->header->group_size_bytes = channel->group_size;
- channel->header->group_count = channel->group_count;
-
- spin_lock_init(&channel->group_offsets_lock);
-
- group_offsets = channel->header->group_offsets;
- groups = channel->groups;
-
- for (i = 0; i < channel->group_count; i++) {
- if (channel->dir == CHANNEL_DIR_RX)
- group_offsets[i] = -1;
- else
- group_offsets[i] = groups[i].user_offset;
- }
-}
-
static void __poch_channel_clear_counters(struct channel_info *channel)
{
channel->counters.pll_unlock = 0;
@@ -584,6 +538,7 @@ static int poch_channel_init(struct channel_info *channel,
if (ret != 0)
goto out;
+ channel->consumed = 0;
channel->transfer = 0;
/* Allocate memory to hold group information. */
@@ -604,12 +559,6 @@ static int poch_channel_init(struct channel_info *channel,
goto out_free_group_info;
}
- ret = poch_channel_alloc_header(channel);
- if (ret) {
- dev_err(dev, "error allocating user space header\n");
- goto out_free_groups;
- }
-
channel->fpga_iomem = poch_dev->fpga_iomem;
channel->bridge_iomem = poch_dev->bridge_iomem;
channel->iomem_lock = &poch_dev->iomem_lock;
@@ -617,14 +566,8 @@ static int poch_channel_init(struct channel_info *channel,
__poch_channel_clear_counters(channel);
- printk(KERN_WARNING "poch_channel_init_header\n");
-
- poch_channel_init_header(channel);
-
return 0;
- out_free_groups:
- poch_channel_free_groups(channel);
out_free_group_info:
kfree(channel->groups);
out:
@@ -822,14 +765,21 @@ static int poch_open(struct inode *inode, struct file *filp)
if (channel->dir == CHANNEL_DIR_TX) {
/* Flush TX FIFO and output data from cardbus. */
- iowrite32(FPGA_TX_CTL_FIFO_FLUSH
- | FPGA_TX_CTL_OUTPUT_CARDBUS,
- fpga + FPGA_TX_CTL_REG);
+ u32 ctl_val = 0;
+
+ ctl_val |= FPGA_TX_CTL_FIFO_FLUSH;
+ ctl_val |= FPGA_TX_CTL_OUTPUT_CARDBUS;
+ if (loopback)
+ ctl_val |= FPGA_TX_CTL_LOOPBACK;
+
+ iowrite32(ctl_val, fpga + FPGA_TX_CTL_REG);
} else {
/* Flush RX FIFO and output data to cardbus. */
- iowrite32(FPGA_RX_CTL_CONT_CAP
- | FPGA_RX_CTL_FIFO_FLUSH,
- fpga + FPGA_RX_CTL_REG);
+ u32 ctl_val = FPGA_RX_CTL_CONT_CAP | FPGA_RX_CTL_FIFO_FLUSH;
+ if (synth_rx)
+ ctl_val |= FPGA_RX_CTL_SYNTH_DATA;
+
+ iowrite32(ctl_val, fpga + FPGA_RX_CTL_REG);
}
atomic_inc(&channel->inited);
@@ -861,7 +811,6 @@ static int poch_release(struct inode *inode, struct file *filp)
}
atomic_dec(&channel->inited);
- poch_channel_free_header(channel);
poch_channel_free_groups(channel);
kfree(channel->groups);
atomic_inc(&channel->free);
@@ -870,7 +819,7 @@ static int poch_release(struct inode *inode, struct file *filp)
}
/*
- * Map the header and the group buffers, to user space.
+ * Map the the group buffers, to user space.
*/
static int poch_mmap(struct file *filp, struct vm_area_struct *vma)
{
@@ -880,7 +829,6 @@ static int poch_mmap(struct file *filp, struct vm_area_struct *vma)
unsigned long size;
unsigned long group_pages;
- unsigned long header_pages;
unsigned long total_group_pages;
int pg_num;
@@ -897,30 +845,16 @@ static int poch_mmap(struct file *filp, struct vm_area_struct *vma)
}
group_pages = npages(channel->group_size);
- header_pages = npages(channel->header_size);
total_group_pages = group_pages * channel->group_count;
size = vma->vm_end - vma->vm_start;
- if (size != (header_pages + total_group_pages) * PAGE_SIZE) {
+ if (size != total_group_pages * PAGE_SIZE) {
printk(KERN_WARNING PFX "required %lu bytes\n", size);
return -EINVAL;
}
start = vma->vm_start;
- /* FIXME: Cleanup required on failure? */
- pg = channel->header_pg;
- for (pg_num = 0; pg_num < header_pages; pg_num++, pg++) {
- printk(KERN_DEBUG PFX "page_count: %d\n", page_count(pg));
- printk(KERN_DEBUG PFX "%d: header: 0x%lx\n", pg_num, start);
- ret = vm_insert_page(vma, start, pg);
- if (ret) {
- printk(KERN_DEBUG "vm_insert 1 failed at %lx\n", start);
- return ret;
- }
- start += PAGE_SIZE;
- }
-
for (i = 0; i < channel->group_count; i++) {
pg = channel->groups[i].pg;
for (pg_num = 0; pg_num < group_pages; pg_num++, pg++) {
@@ -947,20 +881,16 @@ static int poch_mmap(struct file *filp, struct vm_area_struct *vma)
*/
static int poch_channel_available(struct channel_info *channel)
{
- int i;
+ int available = 0;
spin_lock_irq(&channel->group_offsets_lock);
- for (i = 0; i < channel->group_count; i++) {
- if (channel->header->group_offsets[i] != -1) {
- spin_unlock_irq(&channel->group_offsets_lock);
- return 1;
- }
- }
+ if (channel->consumed != channel->transfer)
+ available = 1;
spin_unlock_irq(&channel->group_offsets_lock);
- return 0;
+ return available;
}
static unsigned int poch_poll(struct file *filp, poll_table *pt)
@@ -1026,6 +956,51 @@ static int poch_ioctl(struct inode *inode, struct file *filp,
break;
}
break;
+ case POCH_IOC_CONSUME:
+ {
+ int available;
+ int nfetch;
+ unsigned int from;
+ unsigned int count;
+ unsigned int i, j;
+ struct poch_consume consume;
+ struct poch_consume *uconsume;
+
+ uconsume = argp;
+ ret = copy_from_user(&consume, uconsume, sizeof(consume));
+ if (ret)
+ return ret;
+
+ spin_lock_irq(&channel->group_offsets_lock);
+
+ channel->consumed += consume.nflush;
+ channel->consumed %= channel->group_count;
+
+ available = channel->transfer - channel->consumed;
+ if (available < 0)
+ available += channel->group_count;
+
+ from = channel->consumed;
+
+ spin_unlock_irq(&channel->group_offsets_lock);
+
+ nfetch = consume.nfetch;
+ count = min(available, nfetch);
+
+ for (i = 0; i < count; i++) {
+ j = (from + i) % channel->group_count;
+ ret = put_user(channel->groups[j].user_offset,
+ &consume.offsets[i]);
+ if (ret)
+ return -EFAULT;
+ }
+
+ ret = put_user(count, &uconsume->nfetch);
+ if (ret)
+ return -EFAULT;
+
+ break;
+ }
case POCH_IOC_GET_COUNTERS:
if (!access_ok(VERIFY_WRITE, argp, sizeof(struct poch_counters)))
return -EFAULT;
@@ -1073,7 +1048,6 @@ static void poch_irq_dma(struct channel_info *channel)
long groups_done;
unsigned long i, j;
struct poch_group_info *groups;
- s32 *group_offsets;
u32 curr_group_reg;
if (!atomic_read(&channel->inited))
@@ -1093,20 +1067,24 @@ static void poch_irq_dma(struct channel_info *channel)
if (groups_done <= 0)
groups_done += channel->group_count;
- group_offsets = channel->header->group_offsets;
groups = channel->groups;
spin_lock(&channel->group_offsets_lock);
for (i = 0; i < groups_done; i++) {
j = (prev_transfer + i) % channel->group_count;
- group_offsets[j] = groups[j].user_offset;
+
+ channel->transfer += 1;
+ channel->transfer %= channel->group_count;
+
+ if (channel->transfer == channel->consumed) {
+ channel->consumed += 1;
+ channel->consumed %= channel->group_count;
+ }
}
spin_unlock(&channel->group_offsets_lock);
- channel->transfer = curr_transfer;
-
wake_up_interruptible(&channel->wq);
}
diff --git a/drivers/staging/poch/poch.h b/drivers/staging/poch/poch.h
index 51a2d145798e..8b08385861fd 100644
--- a/drivers/staging/poch/poch.h
+++ b/drivers/staging/poch/poch.h
@@ -7,11 +7,8 @@
* include/linux for final version.
*
*/
-struct poch_cbuf_header {
- __s32 group_size_bytes;
- __s32 group_count;
- __s32 group_offsets[0];
-};
+
+#include <linux/types.h>
struct poch_counters {
__u32 fifo_empty;
@@ -19,6 +16,12 @@ struct poch_counters {
__u32 pll_unlock;
};
+struct poch_consume {
+ __u32 __user *offsets;
+ __u32 nfetch;
+ __u32 nflush;
+};
+
#define POCH_IOC_NUM '9'
#define POCH_IOC_TRANSFER_START _IO(POCH_IOC_NUM, 0)
@@ -27,3 +30,6 @@ struct poch_counters {
struct poch_counters)
#define POCH_IOC_SYNC_GROUP_FOR_USER _IO(POCH_IOC_NUM, 3)
#define POCH_IOC_SYNC_GROUP_FOR_DEVICE _IO(POCH_IOC_NUM, 4)
+
+#define POCH_IOC_CONSUME _IOWR(POCH_IOC_NUM, 5, \
+ struct poch_consume)