📄 video1394.c
字号:
}
cycleTimer = reg_read(ohci, OHCI1394_IsochronousCycleTimer);
timeStamp = ((cycleTimer & 0x0fff) + d->syt_offset); /* 11059 = 450 us */
timeStamp = (timeStamp % 3072 + ((timeStamp / 3072) << 12)
+ (cycleTimer & 0xf000)) & 0xffff;
buf[6] = timeStamp >> 8;
buf[7] = timeStamp & 0xff;
/* if first packet is empty packet, then put timestamp into the next full one too */
if ( (le32_to_cpu(d->it_prg[n][0].data[1]) >>16) == 0x008) {
buf += d->packet_size;
buf[6] = timeStamp >> 8;
buf[7] = timeStamp & 0xff;
}
/* do the next buffer frame too in case of irq latency */
n = d->next_buffer[n];
if (n == -1) {
return;
}
buf = d->dma.kvirt + n * d->buf_size;
timeStamp += (d->last_used_cmd[n] << 12) & 0xffff;
buf[6] = timeStamp >> 8;
buf[7] = timeStamp & 0xff;
/* if first packet is empty packet, then put timestamp into the next full one too */
if ( (le32_to_cpu(d->it_prg[n][0].data[1]) >>16) == 0x008) {
buf += d->packet_size;
buf[6] = timeStamp >> 8;
buf[7] = timeStamp & 0xff;
}
#if 0
printk("curr: %d, next: %d, cycleTimer: %08x timeStamp: %08x\n",
curr, n, cycleTimer, timeStamp);
#endif
}
static void wakeup_dma_it_ctx(unsigned long l)
{
struct dma_iso_ctx *d = (struct dma_iso_ctx *) l;
struct ti_ohci *ohci = d->ohci;
int i;
spin_lock(&d->lock);
for (i = 0; i < d->num_desc; i++) {
if (d->it_prg[i][d->last_used_cmd[i]].end.status &
cpu_to_le32(0xFFFF0000)) {
int next = d->next_buffer[i];
put_timestamp(ohci, d, next);
d->it_prg[i][d->last_used_cmd[i]].end.status = 0;
d->buffer_status[d->buffer_prg_assignment[i]] = VIDEO1394_BUFFER_READY;
}
}
spin_unlock(&d->lock);
if (waitqueue_active(&d->waitq))
wake_up_interruptible(&d->waitq);
}
static void reprogram_dma_it_prg(struct dma_iso_ctx *d, int n, int buffer)
{
struct it_dma_prg *it_prg = d->it_prg[n];
unsigned long buf = (unsigned long)d->dma.kvirt + buffer * d->buf_size;
int i;
d->buffer_prg_assignment[n] = buffer;
for (i=0;i<d->nb_cmd;i++) {
it_prg[i].end.address =
cpu_to_le32(dma_region_offset_to_bus(&d->dma,
(buf+i*d->packet_size) - (unsigned long)d->dma.kvirt));
}
}
static void initialize_dma_it_prg(struct dma_iso_ctx *d, int n, int sync_tag)
{
struct it_dma_prg *it_prg = d->it_prg[n];
struct dma_prog_region *it_reg = &d->prg_reg[n];
unsigned long buf = (unsigned long)d->dma.kvirt;
int i;
d->last_used_cmd[n] = d->nb_cmd - 1;
for (i=0;i<d->nb_cmd;i++) {
it_prg[i].begin.control = cpu_to_le32(DMA_CTL_OUTPUT_MORE |
DMA_CTL_IMMEDIATE | 8) ;
it_prg[i].begin.address = 0;
it_prg[i].begin.status = 0;
it_prg[i].data[0] = cpu_to_le32(
(IEEE1394_SPEED_100 << 16)
| (/* tag */ 1 << 14)
| (d->channel << 8)
| (TCODE_ISO_DATA << 4));
if (i==0) it_prg[i].data[0] |= cpu_to_le32(sync_tag);
it_prg[i].data[1] = cpu_to_le32(d->packet_size << 16);
it_prg[i].data[2] = 0;
it_prg[i].data[3] = 0;
it_prg[i].end.control = cpu_to_le32(DMA_CTL_OUTPUT_LAST |
DMA_CTL_BRANCH);
it_prg[i].end.address =
cpu_to_le32(dma_region_offset_to_bus(&d->dma, (buf+i*d->packet_size) -
(unsigned long)d->dma.kvirt));
if (i<d->nb_cmd-1) {
it_prg[i].end.control |= cpu_to_le32(d->packet_size);
it_prg[i].begin.branchAddress =
cpu_to_le32((dma_prog_region_offset_to_bus(it_reg, (i + 1) *
sizeof(struct it_dma_prg)) & 0xfffffff0) | 0x3);
it_prg[i].end.branchAddress =
cpu_to_le32((dma_prog_region_offset_to_bus(it_reg, (i + 1) *
sizeof(struct it_dma_prg)) & 0xfffffff0) | 0x3);
} else {
/* the last prg generates an interrupt */
it_prg[i].end.control |= cpu_to_le32(DMA_CTL_UPDATE |
DMA_CTL_IRQ | d->left_size);
/* the last prg doesn't branch */
it_prg[i].begin.branchAddress = 0;
it_prg[i].end.branchAddress = 0;
}
it_prg[i].end.status = 0;
}
}
static void initialize_dma_it_prg_var_packet_queue(
struct dma_iso_ctx *d, int n, unsigned int * packet_sizes,
struct ti_ohci *ohci)
{
struct it_dma_prg *it_prg = d->it_prg[n];
struct dma_prog_region *it_reg = &d->prg_reg[n];
int i;
#if 0
if (n != -1) {
put_timestamp(ohci, d, n);
}
#endif
d->last_used_cmd[n] = d->nb_cmd - 1;
for (i = 0; i < d->nb_cmd; i++) {
unsigned int size;
if (packet_sizes[i] > d->packet_size) {
size = d->packet_size;
} else {
size = packet_sizes[i];
}
it_prg[i].data[1] = cpu_to_le32(size << 16);
it_prg[i].end.control = cpu_to_le32(DMA_CTL_OUTPUT_LAST | DMA_CTL_BRANCH);
if (i < d->nb_cmd-1 && packet_sizes[i+1] != 0) {
it_prg[i].end.control |= cpu_to_le32(size);
it_prg[i].begin.branchAddress =
cpu_to_le32((dma_prog_region_offset_to_bus(it_reg, (i + 1) *
sizeof(struct it_dma_prg)) & 0xfffffff0) | 0x3);
it_prg[i].end.branchAddress =
cpu_to_le32((dma_prog_region_offset_to_bus(it_reg, (i + 1) *
sizeof(struct it_dma_prg)) & 0xfffffff0) | 0x3);
} else {
/* the last prg generates an interrupt */
it_prg[i].end.control |= cpu_to_le32(DMA_CTL_UPDATE |
DMA_CTL_IRQ | size);
/* the last prg doesn't branch */
it_prg[i].begin.branchAddress = 0;
it_prg[i].end.branchAddress = 0;
d->last_used_cmd[n] = i;
break;
}
}
}
static void initialize_dma_it_ctx(struct dma_iso_ctx *d, int sync_tag,
unsigned int syt_offset, int flags)
{
struct ti_ohci *ohci = (struct ti_ohci *)d->ohci;
int i;
d->flags = flags;
d->syt_offset = (syt_offset == 0 ? 11000 : syt_offset);
ohci1394_stop_context(ohci, d->ctrlClear, NULL);
for (i=0;i<d->num_desc;i++)
initialize_dma_it_prg(d, i, sync_tag);
/* Set up isoRecvIntMask to generate interrupts */
reg_write(ohci, OHCI1394_IsoXmitIntMaskSet, 1<<d->ctx);
}
static inline unsigned video1394_buffer_state(struct dma_iso_ctx *d,
unsigned int buffer)
{
unsigned long flags;
unsigned int ret;
spin_lock_irqsave(&d->lock, flags);
ret = d->buffer_status[buffer];
spin_unlock_irqrestore(&d->lock, flags);
return ret;
}
static long video1394_ioctl(struct file *file,
unsigned int cmd, unsigned long arg)
{
struct file_ctx *ctx = (struct file_ctx *)file->private_data;
struct ti_ohci *ohci = ctx->ohci;
unsigned long flags;
void __user *argp = (void __user *)arg;
switch(cmd)
{
case VIDEO1394_IOC_LISTEN_CHANNEL:
case VIDEO1394_IOC_TALK_CHANNEL:
{
struct video1394_mmap v;
u64 mask;
struct dma_iso_ctx *d;
int i;
if (copy_from_user(&v, argp, sizeof(v)))
return -EFAULT;
/* if channel < 0, find lowest available one */
if (v.channel < 0) {
mask = (u64)0x1;
for (i=0; ; i++) {
if (i == ISO_CHANNELS) {
PRINT(KERN_ERR, ohci->host->id,
"No free channel found");
return -EAGAIN;
}
if (!(ohci->ISO_channel_usage & mask)) {
v.channel = i;
PRINT(KERN_INFO, ohci->host->id, "Found free channel %d", i);
break;
}
mask = mask << 1;
}
} else if (v.channel >= ISO_CHANNELS) {
PRINT(KERN_ERR, ohci->host->id,
"Iso channel %d out of bounds", v.channel);
return -EINVAL;
} else {
mask = (u64)0x1<<v.channel;
}
DBGMSG(ohci->host->id, "mask: %08X%08X usage: %08X%08X\n",
(u32)(mask>>32),(u32)(mask&0xffffffff),
(u32)(ohci->ISO_channel_usage>>32),
(u32)(ohci->ISO_channel_usage&0xffffffff));
if (ohci->ISO_channel_usage & mask) {
PRINT(KERN_ERR, ohci->host->id,
"Channel %d is already taken", v.channel);
return -EBUSY;
}
if (v.buf_size == 0 || v.buf_size > VIDEO1394_MAX_SIZE) {
PRINT(KERN_ERR, ohci->host->id,
"Invalid %d length buffer requested",v.buf_size);
return -EINVAL;
}
if (v.nb_buffers == 0 || v.nb_buffers > VIDEO1394_MAX_SIZE) {
PRINT(KERN_ERR, ohci->host->id,
"Invalid %d buffers requested",v.nb_buffers);
return -EINVAL;
}
if (v.nb_buffers * v.buf_size > VIDEO1394_MAX_SIZE) {
PRINT(KERN_ERR, ohci->host->id,
"%d buffers of size %d bytes is too big",
v.nb_buffers, v.buf_size);
return -EINVAL;
}
if (cmd == VIDEO1394_IOC_LISTEN_CHANNEL) {
d = alloc_dma_iso_ctx(ohci, OHCI_ISO_RECEIVE,
v.nb_buffers + 1, v.buf_size,
v.channel, 0);
if (d == NULL) {
PRINT(KERN_ERR, ohci->host->id,
"Couldn't allocate ir context");
return -EAGAIN;
}
initialize_dma_ir_ctx(d, v.sync_tag, v.flags);
ctx->current_ctx = d;
v.buf_size = d->buf_size;
list_add_tail(&d->link, &ctx->context_list);
DBGMSG(ohci->host->id,
"iso context %d listen on channel %d",
d->ctx, v.channel);
}
else {
d = alloc_dma_iso_ctx(ohci, OHCI_ISO_TRANSMIT,
v.nb_buffers + 1, v.buf_size,
v.channel, v.packet_size);
if (d == NULL) {
PRINT(KERN_ERR, ohci->host->id,
"Couldn't allocate it context");
return -EAGAIN;
}
initialize_dma_it_ctx(d, v.sync_tag,
v.syt_offset, v.flags);
ctx->current_ctx = d;
v.buf_size = d->buf_size;
list_add_tail(&d->link, &ctx->context_list);
DBGMSG(ohci->host->id,
"Iso context %d talk on channel %d", d->ctx,
v.channel);
}
if (copy_to_user(argp, &v, sizeof(v))) {
/* FIXME : free allocated dma resources */
return -EFAULT;
}
ohci->ISO_channel_usage |= mask;
return 0;
}
case VIDEO1394_IOC_UNLISTEN_CHANNEL:
case VIDEO1394_IOC_UNTALK_CHANNEL:
{
int channel;
u64 mask;
struct dma_iso_ctx *d;
if (copy_from_user(&channel, argp, sizeof(int)))
return -EFAULT;
if (channel < 0 || channel >= ISO_CHANNELS) {
PRINT(KERN_ERR, ohci->host->id,
"Iso channel %d out of bound", channel);
return -EINVAL;
}
mask = (u64)0x1<<channel;
if (!(ohci->ISO_channel_usage & mask)) {
PRINT(KERN_ERR, ohci->host->id,
"Channel %d is not being used", channel);
return -ESRCH;
}
/* Mark this channel as unused */
ohci->ISO_channel_usage &= ~mask;
if (cmd == VIDEO1394_IOC_UNLISTEN_CHANNEL)
d = find_ctx(&ctx->context_list, OHCI_ISO_RECEIVE, channel);
else
d = find_ctx(&ctx->context_list, OHCI_ISO_TRANSMIT, channel);
if (d == NULL) return -ESRCH;
DBGMSG(ohci->host->id, "Iso context %d "
"stop talking on channel %d", d->ctx, channel);
free_dma_iso_ctx(d);
return 0;
}
case VIDEO1394_IOC_LISTEN_QUEUE_BUFFER:
{
struct video1394_wait v;
struct dma_iso_ctx *d;
int next_prg;
if (unlikely(copy_from_user(&v, argp, sizeof(v))))
return -EFAULT;
d = find_ctx(&ctx->context_list, OHCI_ISO_RECEIVE, v.channel);
if (unlikely(d == NULL))
return -EFAULT;
if (unlikely((v.buffer<0) || (v.buffer>=d->num_desc - 1))) {
PRINT(KERN_ERR, ohci->host->id,
"Buffer %d out of range",v.buffer);
return -EINVAL;
}
spin_lock_irqsave(&d->lock,flags);
if (unlikely(d->buffer_status[v.buffer]==VIDEO1394_BUFFER_QUEUED)) {
PRINT(KERN_ERR, ohci->host->id,
"Buffer %d is already used",v.buffer);
spin_unlock_irqrestore(&d->lock,flags);
return -EBUSY;
}
d->buffer_status[v.buffer]=VIDEO1394_BUFFER_QUEUED;
next_prg = (d->last_buffer + 1) % d->num_desc;
if (d->last_buffer>=0)
d->ir_prg[d->last_buffer][d->nb_cmd-1].branchAddress =
cpu_to_le32((dma_prog_region_offset_to_bus(&d->prg_reg[next_prg], 0)
& 0xfffffff0) | 0x1);
d->last_buffer = next_prg;
reprogram_dma_ir_prg(d, d->last_buffer, v.buffer, d->flags);
d->ir_prg[d->last_buffer][d->nb_cmd-1].branchAddress = 0;
spin_unlock_irqrestore(&d->lock,flags);
if (!(reg_read(ohci, d->ctrlSet) & 0x8000))
{
DBGMSG(ohci->host->id, "Starting iso DMA ctx=%d",d->ctx);
/* Tell the controller where the first program is */
reg_write(ohci, d->cmdPtr,
dma_prog_region_offset_to_bus(&d->prg_reg[d->last_buffer], 0) | 0x1);
/* Run IR context */
reg_write(ohci, d->ctrlSet, 0x8000);
}
else {
/* Wake up dma context if necessary */
if (!(reg_read(ohci, d->ctrlSet) & 0x400)) {
DBGMSG(ohci->host->id,
"Waking up iso dma ctx=%d", d->ctx);
reg_write(ohci, d->ctrlSet, 0x1000);
}
}
return 0;
}
case VIDEO1394_IOC_LISTEN_WAIT_BUFFER:
case VIDEO1394_IOC_LISTEN_POLL_BUFFER:
{
struct video1394_wait v;
struct dma_iso_ctx *d;
int i = 0;
if (unlikely(copy_from_user(&v, argp, sizeof(v))))
return -EFAULT;
d = find_ctx(&ctx->context_list, OHCI_ISO_RECEIVE, v.channel);
if (unlikely(d == NULL))
return -EFAULT;
if (unlikely((v.buffer<0) || (v.buffer>d->num_desc - 1))) {
PRINT(KERN_ERR, ohci->host->id,
"Buffer %d out of range",v.buffer);
return -EINVAL;
}
/*
* I change the way it works so that it returns
* the last received frame.
*/
spin_lock_irqsave(&d->lock, flags);
switch(d->buffer_status[v.buffer]) {
case VIDEO1394_BUFFER_READY:
d->buffer_status[v.buffer]=VIDEO1394_BUFFER_FREE;
break;
case VIDEO1394_BUFFER_QUEUED:
if (cmd == VIDEO1394_IOC_LISTEN_POLL_BUFFER) {
/* for polling, return error code EINTR */
spin_unlock_irqrestore(&d->lock, flags);
return -EINTR;
}
spin_unlock_irqrestore(&d->lock, flags);
wait_event_interruptible(d->waitq,
video1394_buffer_state(d, v.buffer) ==
VIDEO1394_BUFFER_READY);
if (signal_pending(current))
return -EINTR;
spin_lock_irqsave(&d->lock, flags);
d->buffer_status[v.buffer]=VIDEO1394_BUFFER_FREE;
break;
default:
PRINT(KERN_ERR, ohci->host->id,
"Buffer %d is not queued",v.buffer);
spin_unlock_irqrestore(&d->lock, flags);
return -ESRCH;
}
/* set time of buffer */
v.filltime = d->buffer_time[v.buffer];
/*
* Look ahead to see how many more buffers have been received
*/
i=0;
while (d->buffer_status[(v.buffer+1)%(d->num_desc - 1)]==
VIDEO1394_BUFFER_READY) {
v.buffer=(v.buffer+1)%(d->num_desc - 1);
i++;
}
spin_unlock_irqrestore(&d->lock, flags);
v.buffer=i;
if (unlikely(copy_to_user(argp, &v, sizeof(v))))
return -EFAULT;
return 0;
}
case VIDEO1394_IOC_TALK_QUEUE_BUFFER:
{
struct video1394_wait v;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -