diff options
-rw-r--r-- | drivers/staging/poch/poch.c | 58 | ||||
-rw-r--r-- | drivers/staging/poch/poch.h | 9 |
2 files changed, 65 insertions, 2 deletions
diff --git a/drivers/staging/poch/poch.c b/drivers/staging/poch/poch.c index 1308c7bada8a..6b4aa5f1d51d 100644 --- a/drivers/staging/poch/poch.c +++ b/drivers/staging/poch/poch.c @@ -201,6 +201,8 @@ struct channel_info { 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; @@ -589,6 +591,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. */ @@ -1033,6 +1036,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; @@ -1108,12 +1156,18 @@ static void poch_irq_dma(struct channel_info *channel) 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..ba1490b4252b 100644 --- a/drivers/staging/poch/poch.h +++ b/drivers/staging/poch/poch.h @@ -19,6 +19,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 +33,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) |