Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
L
linux
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
linux
Commits
cd535057
Commit
cd535057
authored
Jan 16, 2006
by
Linus Torvalds
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'for-linus' of master.kernel.org:/pub/scm/linux/kernel/git/scjody/ieee1394
parents
f74e6670
506eecde
Changes
4
Show whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
0 additions
and
1723 deletions
+0
-1723
drivers/ieee1394/amdtp.c
drivers/ieee1394/amdtp.c
+0
-1297
drivers/ieee1394/amdtp.h
drivers/ieee1394/amdtp.h
+0
-84
drivers/ieee1394/cmp.c
drivers/ieee1394/cmp.c
+0
-311
drivers/ieee1394/cmp.h
drivers/ieee1394/cmp.h
+0
-31
No files found.
drivers/ieee1394/amdtp.c
deleted
100644 → 0
View file @
f74e6670
/* -*- c-basic-offset: 8 -*-
*
* amdtp.c - Audio and Music Data Transmission Protocol Driver
* Copyright (C) 2001 Kristian Hgsberg
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
/* OVERVIEW
* --------
*
* The AMDTP driver is designed to expose the IEEE1394 bus as a
* regular OSS soundcard, i.e. you can link /dev/dsp to /dev/amdtp and
* then your favourite MP3 player, game or whatever sound program will
* output to an IEEE1394 isochronous channel. The signal destination
* could be a set of IEEE1394 loudspeakers (if and when such things
* become available) or an amplifier with IEEE1394 input (like the
* Sony STR-LSA1). The driver only handles the actual streaming, some
* connection management is also required for this to actually work.
* That is outside the scope of this driver, and furthermore it is not
* really standardized yet.
*
* The Audio and Music Data Tranmission Protocol is available at
*
* http://www.1394ta.org/Download/Technology/Specifications/2001/AM20Final-jf2.pdf
*
*
* TODO
* ----
*
* - We should be able to change input sample format between LE/BE, as
* we already shift the bytes around when we construct the iso
* packets.
*
* - Fix DMA stop after bus reset!
*
* - Clean up iso context handling in ohci1394.
*
*
* MAYBE TODO
* ----------
*
* - Receive data for local playback or recording. Playback requires
* soft syncing with the sound card.
*
* - Signal processing, i.e. receive packets, do some processing, and
* transmit them again using the same packet structure and timestamps
* offset by processing time.
*
* - Maybe make an ALSA interface, that is, create a file_ops
* implementation that recognizes ALSA ioctls and uses defaults for
* things that can't be controlled through ALSA (iso channel).
*
* Changes:
*
* - Audit copy_from_user in amdtp_write.
* Daniele Bellucci <bellucda@tiscali.it>
*
*/
#include <linux/module.h>
#include <linux/list.h>
#include <linux/sched.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/ioctl.h>
#include <linux/wait.h>
#include <linux/pci.h>
#include <linux/interrupt.h>
#include <linux/poll.h>
#include <linux/compat.h>
#include <linux/cdev.h>
#include <asm/uaccess.h>
#include <asm/atomic.h>
#include "hosts.h"
#include "highlevel.h"
#include "ieee1394.h"
#include "ieee1394_core.h"
#include "ohci1394.h"
#include "amdtp.h"
#include "cmp.h"
#define FMT_AMDTP 0x10
#define FDF_AM824 0x00
#define FDF_SFC_32KHZ 0x00
#define FDF_SFC_44K1HZ 0x01
#define FDF_SFC_48KHZ 0x02
#define FDF_SFC_88K2HZ 0x03
#define FDF_SFC_96KHZ 0x04
#define FDF_SFC_176K4HZ 0x05
#define FDF_SFC_192KHZ 0x06
struct
descriptor_block
{
struct
output_more_immediate
{
u32
control
;
u32
pad0
;
u32
skip
;
u32
pad1
;
u32
header
[
4
];
}
header_desc
;
struct
output_last
{
u32
control
;
u32
data_address
;
u32
branch
;
u32
status
;
}
payload_desc
;
};
struct
packet
{
struct
descriptor_block
*
db
;
dma_addr_t
db_bus
;
struct
iso_packet
*
payload
;
dma_addr_t
payload_bus
;
};
#include <asm/byteorder.h>
#if defined __BIG_ENDIAN_BITFIELD
struct
iso_packet
{
/* First quadlet */
unsigned
int
dbs
:
8
;
unsigned
int
eoh0
:
2
;
unsigned
int
sid
:
6
;
unsigned
int
dbc
:
8
;
unsigned
int
fn
:
2
;
unsigned
int
qpc
:
3
;
unsigned
int
sph
:
1
;
unsigned
int
reserved
:
2
;
/* Second quadlet */
unsigned
int
fdf
:
8
;
unsigned
int
eoh1
:
2
;
unsigned
int
fmt
:
6
;
unsigned
int
syt
:
16
;
quadlet_t
data
[
0
];
};
#elif defined __LITTLE_ENDIAN_BITFIELD
struct
iso_packet
{
/* First quadlet */
unsigned
int
sid
:
6
;
unsigned
int
eoh0
:
2
;
unsigned
int
dbs
:
8
;
unsigned
int
reserved
:
2
;
unsigned
int
sph
:
1
;
unsigned
int
qpc
:
3
;
unsigned
int
fn
:
2
;
unsigned
int
dbc
:
8
;
/* Second quadlet */
unsigned
int
fmt
:
6
;
unsigned
int
eoh1
:
2
;
unsigned
int
fdf
:
8
;
unsigned
int
syt
:
16
;
quadlet_t
data
[
0
];
};
#else
#error Unknown bitfield type
#endif
struct
fraction
{
int
integer
;
int
numerator
;
int
denominator
;
};
#define PACKET_LIST_SIZE 256
#define MAX_PACKET_LISTS 4
struct
packet_list
{
struct
list_head
link
;
int
last_cycle_count
;
struct
packet
packets
[
PACKET_LIST_SIZE
];
};
#define BUFFER_SIZE 128
/* This implements a circular buffer for incoming samples. */
struct
buffer
{
size_t
head
,
tail
,
length
,
size
;
unsigned
char
data
[
0
];
};
struct
stream
{
int
iso_channel
;
int
format
;
int
rate
;
int
dimension
;
int
fdf
;
int
mode
;
int
sample_format
;
struct
cmp_pcr
*
opcr
;
/* Input samples are copied here. */
struct
buffer
*
input
;
/* ISO Packer state */
unsigned
char
dbc
;
struct
packet_list
*
current_packet_list
;
int
current_packet
;
struct
fraction
ready_samples
,
samples_per_cycle
;
/* We use these to generate control bits when we are packing
* iec958 data.
*/
int
iec958_frame_count
;
int
iec958_rate_code
;
/* The cycle_count and cycle_offset fields are used for the
* synchronization timestamps (syt) in the cip header. They
* are incremented by at least a cycle every time we put a
* time stamp in a packet. As we don't time stamp all
* packages, cycle_count isn't updated in every cycle, and
* sometimes it's incremented by 2. Thus, we have
* cycle_count2, which is simply incremented by one with each
* packet, so we can compare it to the transmission time
* written back in the dma programs.
*/
atomic_t
cycle_count
,
cycle_count2
;
struct
fraction
cycle_offset
,
ticks_per_syt_offset
;
int
syt_interval
;
int
stale_count
;
/* Theses fields control the sample output to the DMA engine.
* The dma_packet_lists list holds packet lists currently
* queued for dma; the head of the list is currently being
* processed. The last program in a packet list generates an
* interrupt, which removes the head from dma_packet_lists and
* puts it back on the free list.
*/
struct
list_head
dma_packet_lists
;
struct
list_head
free_packet_lists
;
wait_queue_head_t
packet_list_wait
;
spinlock_t
packet_list_lock
;
struct
ohci1394_iso_tasklet
iso_tasklet
;
struct
pci_pool
*
descriptor_pool
,
*
packet_pool
;
/* Streams at a host controller are chained through this field. */
struct
list_head
link
;
struct
amdtp_host
*
host
;
};
struct
amdtp_host
{
struct
hpsb_host
*
host
;
struct
ti_ohci
*
ohci
;
struct
list_head
stream_list
;
spinlock_t
stream_list_lock
;
};
static
struct
hpsb_highlevel
amdtp_highlevel
;
/* FIXME: This doesn't belong here... */
#define OHCI1394_CONTEXT_CYCLE_MATCH 0x80000000
#define OHCI1394_CONTEXT_RUN 0x00008000
#define OHCI1394_CONTEXT_WAKE 0x00001000
#define OHCI1394_CONTEXT_DEAD 0x00000800
#define OHCI1394_CONTEXT_ACTIVE 0x00000400
static
void
ohci1394_start_it_ctx
(
struct
ti_ohci
*
ohci
,
int
ctx
,
dma_addr_t
first_cmd
,
int
z
,
int
cycle_match
)
{
reg_write
(
ohci
,
OHCI1394_IsoXmitIntMaskSet
,
1
<<
ctx
);
reg_write
(
ohci
,
OHCI1394_IsoXmitCommandPtr
+
ctx
*
16
,
first_cmd
|
z
);
reg_write
(
ohci
,
OHCI1394_IsoXmitContextControlClear
+
ctx
*
16
,
~
0
);
wmb
();
reg_write
(
ohci
,
OHCI1394_IsoXmitContextControlSet
+
ctx
*
16
,
OHCI1394_CONTEXT_CYCLE_MATCH
|
(
cycle_match
<<
16
)
|
OHCI1394_CONTEXT_RUN
);
}
static
void
ohci1394_wake_it_ctx
(
struct
ti_ohci
*
ohci
,
int
ctx
)
{
reg_write
(
ohci
,
OHCI1394_IsoXmitContextControlSet
+
ctx
*
16
,
OHCI1394_CONTEXT_WAKE
);
}
static
void
ohci1394_stop_it_ctx
(
struct
ti_ohci
*
ohci
,
int
ctx
,
int
synchronous
)
{
u32
control
;
int
wait
;
reg_write
(
ohci
,
OHCI1394_IsoXmitIntMaskClear
,
1
<<
ctx
);
reg_write
(
ohci
,
OHCI1394_IsoXmitContextControlClear
+
ctx
*
16
,
OHCI1394_CONTEXT_RUN
);
wmb
();
if
(
synchronous
)
{
for
(
wait
=
0
;
wait
<
5
;
wait
++
)
{
control
=
reg_read
(
ohci
,
OHCI1394_IsoXmitContextControlSet
+
ctx
*
16
);
if
((
control
&
OHCI1394_CONTEXT_ACTIVE
)
==
0
)
break
;
schedule_timeout_interruptible
(
1
);
}
}
}
/* Note: we can test if free_packet_lists is empty without aquiring
* the packet_list_lock. The interrupt handler only adds to the free
* list, there is no race condition between testing the list non-empty
* and acquiring the lock.
*/
static
struct
packet_list
*
stream_get_free_packet_list
(
struct
stream
*
s
)
{
struct
packet_list
*
pl
;
unsigned
long
flags
;
if
(
list_empty
(
&
s
->
free_packet_lists
))
return
NULL
;
spin_lock_irqsave
(
&
s
->
packet_list_lock
,
flags
);
pl
=
list_entry
(
s
->
free_packet_lists
.
next
,
struct
packet_list
,
link
);
list_del
(
&
pl
->
link
);
spin_unlock_irqrestore
(
&
s
->
packet_list_lock
,
flags
);
return
pl
;
}
static
void
stream_start_dma
(
struct
stream
*
s
,
struct
packet_list
*
pl
)
{
u32
syt_cycle
,
cycle_count
,
start_cycle
;
cycle_count
=
reg_read
(
s
->
host
->
ohci
,
OHCI1394_IsochronousCycleTimer
)
>>
12
;
syt_cycle
=
(
pl
->
last_cycle_count
-
PACKET_LIST_SIZE
+
1
)
&
0x0f
;
/* We program the DMA controller to start transmission at
* least 17 cycles from now - this happens when the lower four
* bits of cycle_count is 0x0f and syt_cycle is 0, in this
* case the start cycle is cycle_count - 15 + 32. */
start_cycle
=
(
cycle_count
&
~
0x0f
)
+
32
+
syt_cycle
;
if
((
start_cycle
&
0x1fff
)
>=
8000
)
start_cycle
=
start_cycle
-
8000
+
0x2000
;
ohci1394_start_it_ctx
(
s
->
host
->
ohci
,
s
->
iso_tasklet
.
context
,
pl
->
packets
[
0
].
db_bus
,
3
,
start_cycle
&
0x7fff
);
}
static
void
stream_put_dma_packet_list
(
struct
stream
*
s
,
struct
packet_list
*
pl
)
{
unsigned
long
flags
;
struct
packet_list
*
prev
;
/* Remember the cycle_count used for timestamping the last packet. */
pl
->
last_cycle_count
=
atomic_read
(
&
s
->
cycle_count2
)
-
1
;
pl
->
packets
[
PACKET_LIST_SIZE
-
1
].
db
->
payload_desc
.
branch
=
0
;
spin_lock_irqsave
(
&
s
->
packet_list_lock
,
flags
);
list_add_tail
(
&
pl
->
link
,
&
s
->
dma_packet_lists
);
spin_unlock_irqrestore
(
&
s
->
packet_list_lock
,
flags
);
prev
=
list_entry
(
pl
->
link
.
prev
,
struct
packet_list
,
link
);
if
(
pl
->
link
.
prev
!=
&
s
->
dma_packet_lists
)
{
struct
packet
*
last
=
&
prev
->
packets
[
PACKET_LIST_SIZE
-
1
];
last
->
db
->
payload_desc
.
branch
=
pl
->
packets
[
0
].
db_bus
|
3
;
last
->
db
->
header_desc
.
skip
=
pl
->
packets
[
0
].
db_bus
|
3
;
ohci1394_wake_it_ctx
(
s
->
host
->
ohci
,
s
->
iso_tasklet
.
context
);
}
else
stream_start_dma
(
s
,
pl
);
}
static
void
stream_shift_packet_lists
(
unsigned
long
l
)
{
struct
stream
*
s
=
(
struct
stream
*
)
l
;
struct
packet_list
*
pl
;
struct
packet
*
last
;
int
diff
;
if
(
list_empty
(
&
s
->
dma_packet_lists
))
{
HPSB_ERR
(
"empty dma_packet_lists in %s"
,
__FUNCTION__
);
return
;
}
/* Now that we know the list is non-empty, we can get the head
* of the list without locking, because the process context
* only adds to the tail.
*/
pl
=
list_entry
(
s
->
dma_packet_lists
.
next
,
struct
packet_list
,
link
);
last
=
&
pl
->
packets
[
PACKET_LIST_SIZE
-
1
];
/* This is weird... if we stop dma processing in the middle of
* a packet list, the dma context immediately generates an
* interrupt if we enable it again later. This only happens
* when amdtp_release is interrupted while waiting for dma to
* complete, though. Anyway, we detect this by seeing that
* the status of the dma descriptor that we expected an
* interrupt from is still 0.
*/
if
(
last
->
db
->
payload_desc
.
status
==
0
)
{
HPSB_INFO
(
"weird interrupt..."
);
return
;
}
/* If the last descriptor block does not specify a branch
* address, we have a sample underflow.
*/
if
(
last
->
db
->
payload_desc
.
branch
==
0
)
HPSB_INFO
(
"FIXME: sample underflow..."
);
/* Here we check when (which cycle) the last packet was sent
* and compare it to what the iso packer was using at the
* time. If there is a mismatch, we adjust the cycle count in
* the iso packer. However, there are still up to
* MAX_PACKET_LISTS packet lists queued with bad time stamps,
* so we disable time stamp monitoring for the next
* MAX_PACKET_LISTS packet lists.
*/
diff
=
(
last
->
db
->
payload_desc
.
status
-
pl
->
last_cycle_count
)
&
0xf
;
if
(
diff
>
0
&&
s
->
stale_count
==
0
)
{
atomic_add
(
diff
,
&
s
->
cycle_count
);
atomic_add
(
diff
,
&
s
->
cycle_count2
);
s
->
stale_count
=
MAX_PACKET_LISTS
;
}
if
(
s
->
stale_count
>
0
)
s
->
stale_count
--
;
/* Finally, we move the packet list that was just processed
* back to the free list, and notify any waiters.
*/
spin_lock
(
&
s
->
packet_list_lock
);
list_del
(
&
pl
->
link
);
list_add_tail
(
&
pl
->
link
,
&
s
->
free_packet_lists
);
spin_unlock
(
&
s
->
packet_list_lock
);
wake_up_interruptible
(
&
s
->
packet_list_wait
);
}
static
struct
packet
*
stream_current_packet
(
struct
stream
*
s
)
{
if
(
s
->
current_packet_list
==
NULL
&&
(
s
->
current_packet_list
=
stream_get_free_packet_list
(
s
))
==
NULL
)
return
NULL
;
return
&
s
->
current_packet_list
->
packets
[
s
->
current_packet
];
}
static
void
stream_queue_packet
(
struct
stream
*
s
)
{
s
->
current_packet
++
;
if
(
s
->
current_packet
==
PACKET_LIST_SIZE
)
{
stream_put_dma_packet_list
(
s
,
s
->
current_packet_list
);
s
->
current_packet_list
=
NULL
;
s
->
current_packet
=
0
;
}
}
/* Integer fractional math. When we transmit a 44k1Hz signal we must
* send 5 41/80 samples per isochronous cycle, as these occur 8000
* times a second. Of course, we must send an integral number of
* samples in a packet, so we use the integer math to alternate
* between sending 5 and 6 samples per packet.
*/
static
void
fraction_init
(
struct
fraction
*
f
,
int
numerator
,
int
denominator
)
{
f
->
integer
=
numerator
/
denominator
;
f
->
numerator
=
numerator
%
denominator
;
f
->
denominator
=
denominator
;
}
static
__inline__
void
fraction_add
(
struct
fraction
*
dst
,
struct
fraction
*
src1
,
struct
fraction
*
src2
)
{
/* assert: src1->denominator == src2->denominator */
int
sum
,
denom
;
/* We use these two local variables to allow gcc to optimize
* the division and the modulo into only one division. */
sum
=
src1
->
numerator
+
src2
->
numerator
;
denom
=
src1
->
denominator
;
dst
->
integer
=
src1
->
integer
+
src2
->
integer
+
sum
/
denom
;
dst
->
numerator
=
sum
%
denom
;
dst
->
denominator
=
denom
;
}
static
__inline__
void
fraction_sub_int
(
struct
fraction
*
dst
,
struct
fraction
*
src
,
int
integer
)
{
dst
->
integer
=
src
->
integer
-
integer
;
dst
->
numerator
=
src
->
numerator
;
dst
->
denominator
=
src
->
denominator
;
}
static
__inline__
int
fraction_floor
(
struct
fraction
*
frac
)
{
return
frac
->
integer
;
}
static
__inline__
int
fraction_ceil
(
struct
fraction
*
frac
)
{
return
frac
->
integer
+
(
frac
->
numerator
>
0
?
1
:
0
);
}
static
void
packet_initialize
(
struct
packet
*
p
,
struct
packet
*
next
)
{
/* Here we initialize the dma descriptor block for
* transferring one iso packet. We use two descriptors per
* packet: an OUTPUT_MORE_IMMMEDIATE descriptor for the
* IEEE1394 iso packet header and an OUTPUT_LAST descriptor
* for the payload.
*/
p
->
db
->
header_desc
.
control
=
DMA_CTL_OUTPUT_MORE
|
DMA_CTL_IMMEDIATE
|
8
;
if
(
next
)
{
p
->
db
->
payload_desc
.
control
=
DMA_CTL_OUTPUT_LAST
|
DMA_CTL_BRANCH
;
p
->
db
->
payload_desc
.
branch
=
next
->
db_bus
|
3
;
p
->
db
->
header_desc
.
skip
=
next
->
db_bus
|
3
;
}
else
{
p
->
db
->
payload_desc
.
control
=
DMA_CTL_OUTPUT_LAST
|
DMA_CTL_BRANCH
|
DMA_CTL_UPDATE
|
DMA_CTL_IRQ
;
p
->
db
->
payload_desc
.
branch
=
0
;
p
->
db
->
header_desc
.
skip
=
0
;
}
p
->
db
->
payload_desc
.
data_address
=
p
->
payload_bus
;
p
->
db
->
payload_desc
.
status
=
0
;
}
static
struct
packet_list
*
packet_list_alloc
(
struct
stream
*
s
)
{
int
i
;
struct
packet_list
*
pl
;
struct
packet
*
next
;
pl
=
kmalloc
(
sizeof
*
pl
,
SLAB_KERNEL
);
if
(
pl
==
NULL
)
return
NULL
;
for
(
i
=
0
;
i
<
PACKET_LIST_SIZE
;
i
++
)
{
struct
packet
*
p
=
&
pl
->
packets
[
i
];
p
->
db
=
pci_pool_alloc
(
s
->
descriptor_pool
,
SLAB_KERNEL
,
&
p
->
db_bus
);
p
->
payload
=
pci_pool_alloc
(
s
->
packet_pool
,
SLAB_KERNEL
,
&
p
->
payload_bus
);
}
for
(
i
=
0
;
i
<
PACKET_LIST_SIZE
;
i
++
)
{
if
(
i
<
PACKET_LIST_SIZE
-
1
)
next
=
&
pl
->
packets
[
i
+
1
];
else
next
=
NULL
;
packet_initialize
(
&
pl
->
packets
[
i
],
next
);
}
return
pl
;
}
static
void
packet_list_free
(
struct
packet_list
*
pl
,
struct
stream
*
s
)
{
int
i
;
for
(
i
=
0
;
i
<
PACKET_LIST_SIZE
;
i
++
)
{
struct
packet
*
p
=
&
pl
->
packets
[
i
];
pci_pool_free
(
s
->
descriptor_pool
,
p
->
db
,
p
->
db_bus
);
pci_pool_free
(
s
->
packet_pool
,
p
->
payload
,
p
->
payload_bus
);
}
kfree
(
pl
);
}
static
struct
buffer
*
buffer_alloc
(
int
size
)
{
struct
buffer
*
b
;
b
=
kmalloc
(
sizeof
*
b
+
size
,
SLAB_KERNEL
);
if
(
b
==
NULL
)
return
NULL
;
b
->
head
=
0
;
b
->
tail
=
0
;
b
->
length
=
0
;
b
->
size
=
size
;
return
b
;
}
static
unsigned
char
*
buffer_get_bytes
(
struct
buffer
*
buffer
,
int
size
)
{
unsigned
char
*
p
;
if
(
buffer
->
head
+
size
>
buffer
->
size
)
BUG
();
p
=
&
buffer
->
data
[
buffer
->
head
];
buffer
->
head
+=
size
;
if
(
buffer
->
head
==
buffer
->
size
)
buffer
->
head
=
0
;
buffer
->
length
-=
size
;
return
p
;
}
static
unsigned
char
*
buffer_put_bytes
(
struct
buffer
*
buffer
,
size_t
max
,
size_t
*
actual
)
{
size_t
length
;
unsigned
char
*
p
;
p
=
&
buffer
->
data
[
buffer
->
tail
];
length
=
min
(
buffer
->
size
-
buffer
->
length
,
max
);
if
(
buffer
->
tail
+
length
<
buffer
->
size
)
{
*
actual
=
length
;
buffer
->
tail
+=
length
;
}
else
{
*
actual
=
buffer
->
size
-
buffer
->
tail
;
buffer
->
tail
=
0
;
}
buffer
->
length
+=
*
actual
;
return
p
;
}
static
u32
get_iec958_header_bits
(
struct
stream
*
s
,
int
sub_frame
,
u32
sample
)
{
int
csi
,
parity
,
shift
;
int
block_start
;
u32
bits
;
switch
(
s
->
iec958_frame_count
)
{
case
1
:
csi
=
s
->
format
==
AMDTP_FORMAT_IEC958_AC3
;
break
;
case
2
:
case
9
:
csi
=
1
;
break
;
case
24
...
27
:
csi
=
(
s
->
iec958_rate_code
>>
(
27
-
s
->
iec958_frame_count
))
&
0x01
;
break
;
default:
csi
=
0
;
break
;
}
block_start
=
(
s
->
iec958_frame_count
==
0
&&
sub_frame
==
0
);
/* The parity bit is the xor of the sample bits and the
* channel status info bit. */
for
(
shift
=
16
,
parity
=
sample
^
csi
;
shift
>
0
;
shift
>>=
1
)
parity
^=
(
parity
>>
shift
);
bits
=
(
block_start
<<
5
)
|
/* Block start bit */
((
sub_frame
==
0
)
<<
4
)
|
/* Subframe bit */
((
parity
&
1
)
<<
3
)
|
/* Parity bit */
(
csi
<<
2
);
/* Channel status info bit */
return
bits
;
}
static
u32
get_header_bits
(
struct
stream
*
s
,
int
sub_frame
,
u32
sample
)
{
switch
(
s
->
format
)
{
case
AMDTP_FORMAT_IEC958_PCM
:
case
AMDTP_FORMAT_IEC958_AC3
:
return
get_iec958_header_bits
(
s
,
sub_frame
,
sample
);
case
AMDTP_FORMAT_RAW
:
return
0x40
;
default:
return
0
;
}
}
static
void
fill_payload_le16
(
struct
stream
*
s
,
quadlet_t
*
data
,
int
nevents
)
{
quadlet_t
*
event
,
sample
,
bits
;
unsigned
char
*
p
;
int
i
,
j
;
for
(
i
=
0
,
event
=
data
;
i
<
nevents
;
i
++
)
{
for
(
j
=
0
;
j
<
s
->
dimension
;
j
++
)
{
p
=
buffer_get_bytes
(
s
->
input
,
2
);
sample
=
(
p
[
1
]
<<
16
)
|
(
p
[
0
]
<<
8
);
bits
=
get_header_bits
(
s
,
j
,
sample
);
event
[
j
]
=
cpu_to_be32
((
bits
<<
24
)
|
sample
);
}
event
+=
s
->
dimension
;
if
(
++
s
->
iec958_frame_count
==
192
)
s
->
iec958_frame_count
=
0
;
}
}
static
void
fill_packet
(
struct
stream
*
s
,
struct
packet
*
packet
,
int
nevents
)
{
int
syt_index
,
syt
,
size
;
u32
control
;
size
=
(
nevents
*
s
->
dimension
+
2
)
*
sizeof
(
quadlet_t
);
/* Update DMA descriptors */
packet
->
db
->
payload_desc
.
status
=
0
;
control
=
packet
->
db
->
payload_desc
.
control
&
0xffff0000
;
packet
->
db
->
payload_desc
.
control
=
control
|
size
;
/* Fill IEEE1394 headers */
packet
->
db
->
header_desc
.
header
[
0
]
=
(
IEEE1394_SPEED_100
<<
16
)
|
(
0x01
<<
14
)
|
(
s
->
iso_channel
<<
8
)
|
(
TCODE_ISO_DATA
<<
4
);
packet
->
db
->
header_desc
.
header
[
1
]
=
size
<<
16
;
/* Calculate synchronization timestamp (syt). First we
* determine syt_index, that is, the index in the packet of
* the sample for which the timestamp is valid. */
syt_index
=
(
s
->
syt_interval
-
s
->
dbc
)
&
(
s
->
syt_interval
-
1
);
if
(
syt_index
<
nevents
)
{
syt
=
((
atomic_read
(
&
s
->
cycle_count
)
<<
12
)
|
s
->
cycle_offset
.
integer
)
&
0xffff
;
fraction_add
(
&
s
->
cycle_offset
,
&
s
->
cycle_offset
,
&
s
->
ticks_per_syt_offset
);
/* This next addition should be modulo 8000 (0x1f40),
* but we only use the lower 4 bits of cycle_count, so
* we don't need the modulo. */
atomic_add
(
s
->
cycle_offset
.
integer
/
3072
,
&
s
->
cycle_count
);
s
->
cycle_offset
.
integer
%=
3072
;
}
else
syt
=
0xffff
;
atomic_inc
(
&
s
->
cycle_count2
);
/* Fill cip header */
packet
->
payload
->
eoh0
=
0
;
packet
->
payload
->
sid
=
s
->
host
->
host
->
node_id
&
0x3f
;
packet
->
payload
->
dbs
=
s
->
dimension
;
packet
->
payload
->
fn
=
0
;
packet
->
payload
->
qpc
=
0
;
packet
->
payload
->
sph
=
0
;
packet
->
payload
->
reserved
=
0
;
packet
->
payload
->
dbc
=
s
->
dbc
;
packet
->
payload
->
eoh1
=
2
;
packet
->
payload
->
fmt
=
FMT_AMDTP
;
packet
->
payload
->
fdf
=
s
->
fdf
;
packet
->
payload
->
syt
=
cpu_to_be16
(
syt
);
switch
(
s
->
sample_format
)
{
case
AMDTP_INPUT_LE16
:
fill_payload_le16
(
s
,
packet
->
payload
->
data
,
nevents
);
break
;
}
s
->
dbc
+=
nevents
;
}
static
void
stream_flush
(
struct
stream
*
s
)
{
struct
packet
*
p
;
int
nevents
;
struct
fraction
next
;
/* The AMDTP specifies two transmission modes: blocking and
* non-blocking. In blocking mode you always transfer
* syt_interval or zero samples, whereas in non-blocking mode
* you send as many samples as you have available at transfer
* time.
*
* The fraction samples_per_cycle specifies the number of
* samples that become available per cycle. We add this to
* the fraction ready_samples, which specifies the number of
* leftover samples from the previous transmission. The sum,
* stored in the fraction next, specifies the number of
* samples available for transmission, and from this we
* determine the number of samples to actually transmit.
*/
while
(
1
)
{
fraction_add
(
&
next
,
&
s
->
ready_samples
,
&
s
->
samples_per_cycle
);
if
(
s
->
mode
==
AMDTP_MODE_BLOCKING
)
{
if
(
fraction_floor
(
&
next
)
>=
s
->
syt_interval
)
nevents
=
s
->
syt_interval
;
else
nevents
=
0
;
}
else
nevents
=
fraction_floor
(
&
next
);
p
=
stream_current_packet
(
s
);
if
(
s
->
input
->
length
<
nevents
*
s
->
dimension
*
2
||
p
==
NULL
)
break
;
fill_packet
(
s
,
p
,
nevents
);
stream_queue_packet
(
s
);
/* Now that we have successfully queued the packet for
* transmission, we update the fraction ready_samples. */
fraction_sub_int
(
&
s
->
ready_samples
,
&
next
,
nevents
);
}
}
static
int
stream_alloc_packet_lists
(
struct
stream
*
s
)
{
int
max_nevents
,
max_packet_size
,
i
;
if
(
s
->
mode
==
AMDTP_MODE_BLOCKING
)
max_nevents
=
s
->
syt_interval
;
else
max_nevents
=
fraction_ceil
(
&
s
->
samples_per_cycle
);
max_packet_size
=
max_nevents
*
s
->
dimension
*
4
+
8
;
s
->
packet_pool
=
pci_pool_create
(
"packet pool"
,
s
->
host
->
ohci
->
dev
,
max_packet_size
,
0
,
0
);
if
(
s
->
packet_pool
==
NULL
)
return
-
1
;
INIT_LIST_HEAD
(
&
s
->
free_packet_lists
);
INIT_LIST_HEAD
(
&
s
->
dma_packet_lists
);
for
(
i
=
0
;
i
<
MAX_PACKET_LISTS
;
i
++
)
{
struct
packet_list
*
pl
=
packet_list_alloc
(
s
);
if
(
pl
==
NULL
)
break
;
list_add_tail
(
&
pl
->
link
,
&
s
->
free_packet_lists
);
}
return
i
<
MAX_PACKET_LISTS
?
-
1
:
0
;
}
static
void
stream_free_packet_lists
(
struct
stream
*
s
)
{
struct
packet_list
*
packet_l
,
*
packet_l_next
;
if
(
s
->
current_packet_list
!=
NULL
)
packet_list_free
(
s
->
current_packet_list
,
s
);
list_for_each_entry_safe
(
packet_l
,
packet_l_next
,
&
s
->
dma_packet_lists
,
link
)
packet_list_free
(
packet_l
,
s
);
list_for_each_entry_safe
(
packet_l
,
packet_l_next
,
&
s
->
free_packet_lists
,
link
)
packet_list_free
(
packet_l
,
s
);
if
(
s
->
packet_pool
!=
NULL
)
pci_pool_destroy
(
s
->
packet_pool
);
s
->
current_packet_list
=
NULL
;
INIT_LIST_HEAD
(
&
s
->
free_packet_lists
);
INIT_LIST_HEAD
(
&
s
->
dma_packet_lists
);
s
->
packet_pool
=
NULL
;
}
static
void
plug_update
(
struct
cmp_pcr
*
plug
,
void
*
data
)
{
struct
stream
*
s
=
data
;
HPSB_INFO
(
"plug update: p2p_count=%d, channel=%d"
,
plug
->
p2p_count
,
plug
->
channel
);
s
->
iso_channel
=
plug
->
channel
;
if
(
plug
->
p2p_count
>
0
)
{
struct
packet_list
*
pl
;
pl
=
list_entry
(
s
->
dma_packet_lists
.
next
,
struct
packet_list
,
link
);
stream_start_dma
(
s
,
pl
);
}
else
{
ohci1394_stop_it_ctx
(
s
->
host
->
ohci
,
s
->
iso_tasklet
.
context
,
0
);
}
}
static
int
stream_configure
(
struct
stream
*
s
,
int
cmd
,
struct
amdtp_ioctl
*
cfg
)
{
const
int
transfer_delay
=
9000
;
if
(
cfg
->
format
<=
AMDTP_FORMAT_IEC958_AC3
)
s
->
format
=
cfg
->
format
;
else
return
-
EINVAL
;
switch
(
cfg
->
rate
)
{
case
32000
:
s
->
syt_interval
=
8
;
s
->
fdf
=
FDF_SFC_32KHZ
;
s
->
iec958_rate_code
=
0x0c
;
break
;
case
44100
:
s
->
syt_interval
=
8
;
s
->
fdf
=
FDF_SFC_44K1HZ
;
s
->
iec958_rate_code
=
0x00
;
break
;
case
48000
:
s
->
syt_interval
=
8
;
s
->
fdf
=
FDF_SFC_48KHZ
;
s
->
iec958_rate_code
=
0x04
;
break
;
case
88200
:
s
->
syt_interval
=
16
;
s
->
fdf
=
FDF_SFC_88K2HZ
;
s
->
iec958_rate_code
=
0x00
;
break
;
case
96000
:
s
->
syt_interval
=
16
;
s
->
fdf
=
FDF_SFC_96KHZ
;
s
->
iec958_rate_code
=
0x00
;
break
;
case
176400
:
s
->
syt_interval
=
32
;
s
->
fdf
=
FDF_SFC_176K4HZ
;
s
->
iec958_rate_code
=
0x00
;
break
;
case
192000
:
s
->
syt_interval
=
32
;
s
->
fdf
=
FDF_SFC_192KHZ
;
s
->
iec958_rate_code
=
0x00
;
break
;
default:
return
-
EINVAL
;
}
s
->
rate
=
cfg
->
rate
;
fraction_init
(
&
s
->
samples_per_cycle
,
s
->
rate
,
8000
);
fraction_init
(
&
s
->
ready_samples
,
0
,
8000
);
/* The ticks_per_syt_offset is initialized to the number of
* ticks between syt_interval events. The number of ticks per
* second is 24.576e6, so the number of ticks between
* syt_interval events is 24.576e6 * syt_interval / rate.
*/
fraction_init
(
&
s
->
ticks_per_syt_offset
,
24576000
*
s
->
syt_interval
,
s
->
rate
);
fraction_init
(
&
s
->
cycle_offset
,
(
transfer_delay
%
3072
)
*
s
->
rate
,
s
->
rate
);
atomic_set
(
&
s
->
cycle_count
,
transfer_delay
/
3072
);
atomic_set
(
&
s
->
cycle_count2
,
0
);
s
->
mode
=
cfg
->
mode
;
s
->
sample_format
=
AMDTP_INPUT_LE16
;
/* When using the AM824 raw subformat we can stream signals of
* any dimension. The IEC958 subformat, however, only
* supports 2 channels.
*/
if
(
s
->
format
==
AMDTP_FORMAT_RAW
||
cfg
->
dimension
==
2
)
s
->
dimension
=
cfg
->
dimension
;
else
return
-
EINVAL
;
if
(
s
->
opcr
!=
NULL
)
{
cmp_unregister_opcr
(
s
->
host
->
host
,
s
->
opcr
);
s
->
opcr
=
NULL
;
}
switch
(
cmd
)
{
case
AMDTP_IOC_PLUG
:
s
->
opcr
=
cmp_register_opcr
(
s
->
host
->
host
,
cfg
->
u
.
plug
,
/*payload*/
12
,
plug_update
,
s
);
if
(
s
->
opcr
==
NULL
)
return
-
EINVAL
;
s
->
iso_channel
=
s
->
opcr
->
channel
;
break
;
case
AMDTP_IOC_CHANNEL
:
if
(
cfg
->
u
.
channel
>=
0
&&
cfg
->
u
.
channel
<
64
)
s
->
iso_channel
=
cfg
->
u
.
channel
;
else
return
-
EINVAL
;
break
;
}
/* The ioctl settings were all valid, so we realloc the packet
* lists to make sure the packet size is big enough.
*/
if
(
s
->
packet_pool
!=
NULL
)
stream_free_packet_lists
(
s
);
if
(
stream_alloc_packet_lists
(
s
)
<
0
)
{
stream_free_packet_lists
(
s
);
return
-
ENOMEM
;
}
return
0
;
}
static
struct
stream
*
stream_alloc
(
struct
amdtp_host
*
host
)
{
struct
stream
*
s
;
unsigned
long
flags
;
s
=
kmalloc
(
sizeof
(
struct
stream
),
SLAB_KERNEL
);
if
(
s
==
NULL
)
return
NULL
;
memset
(
s
,
0
,
sizeof
(
struct
stream
));
s
->
host
=
host
;
s
->
input
=
buffer_alloc
(
BUFFER_SIZE
);
if
(
s
->
input
==
NULL
)
{
kfree
(
s
);
return
NULL
;
}
s
->
descriptor_pool
=
pci_pool_create
(
"descriptor pool"
,
host
->
ohci
->
dev
,
sizeof
(
struct
descriptor_block
),
16
,
0
);
if
(
s
->
descriptor_pool
==
NULL
)
{
kfree
(
s
->
input
);
kfree
(
s
);
return
NULL
;
}
INIT_LIST_HEAD
(
&
s
->
free_packet_lists
);
INIT_LIST_HEAD
(
&
s
->
dma_packet_lists
);
init_waitqueue_head
(
&
s
->
packet_list_wait
);
spin_lock_init
(
&
s
->
packet_list_lock
);
ohci1394_init_iso_tasklet
(
&
s
->
iso_tasklet
,
OHCI_ISO_TRANSMIT
,
stream_shift_packet_lists
,
(
unsigned
long
)
s
);
if
(
ohci1394_register_iso_tasklet
(
host
->
ohci
,
&
s
->
iso_tasklet
)
<
0
)
{
pci_pool_destroy
(
s
->
descriptor_pool
);
kfree
(
s
->
input
);
kfree
(
s
);
return
NULL
;
}
spin_lock_irqsave
(
&
host
->
stream_list_lock
,
flags
);
list_add_tail
(
&
s
->
link
,
&
host
->
stream_list
);
spin_unlock_irqrestore
(
&
host
->
stream_list_lock
,
flags
);
return
s
;
}
static
void
stream_free
(
struct
stream
*
s
)
{
unsigned
long
flags
;
/* Stop the DMA. We wait for the dma packet list to become
* empty and let the dma controller run out of programs. This
* seems to be more reliable than stopping it directly, since
* that sometimes generates an it transmit interrupt if we
* later re-enable the context.
*/
wait_event_interruptible
(
s
->
packet_list_wait
,
list_empty
(
&
s
->
dma_packet_lists
));
ohci1394_stop_it_ctx
(
s
->
host
->
ohci
,
s
->
iso_tasklet
.
context
,
1
);
ohci1394_unregister_iso_tasklet
(
s
->
host
->
ohci
,
&
s
->
iso_tasklet
);
if
(
s
->
opcr
!=
NULL
)
cmp_unregister_opcr
(
s
->
host
->
host
,
s
->
opcr
);
spin_lock_irqsave
(
&
s
->
host
->
stream_list_lock
,
flags
);
list_del
(
&
s
->
link
);
spin_unlock_irqrestore
(
&
s
->
host
->
stream_list_lock
,
flags
);
kfree
(
s
->
input
);
stream_free_packet_lists
(
s
);
pci_pool_destroy
(
s
->
descriptor_pool
);
kfree
(
s
);
}
/* File operations */
static
ssize_t
amdtp_write
(
struct
file
*
file
,
const
char
__user
*
buffer
,
size_t
count
,
loff_t
*
offset_is_ignored
)
{
struct
stream
*
s
=
file
->
private_data
;
unsigned
char
*
p
;
int
i
;
size_t
length
;
if
(
s
->
packet_pool
==
NULL
)
return
-
EBADFD
;
/* Fill the circular buffer from the input buffer and call the
* iso packer when the buffer is full. The iso packer may
* leave bytes in the buffer for two reasons: either the
* remaining bytes wasn't enough to build a new packet, or
* there were no free packet lists. In the first case we
* re-fill the buffer and call the iso packer again or return
* if we used all the data from userspace. In the second
* case, the wait_event_interruptible will block until the irq
* handler frees a packet list.
*/
for
(
i
=
0
;
i
<
count
;
i
+=
length
)
{
p
=
buffer_put_bytes
(
s
->
input
,
count
-
i
,
&
length
);
if
(
copy_from_user
(
p
,
buffer
+
i
,
length
))
return
-
EFAULT
;
if
(
s
->
input
->
length
<
s
->
input
->
size
)
continue
;
stream_flush
(
s
);
if
(
s
->
current_packet_list
!=
NULL
)
continue
;
if
(
file
->
f_flags
&
O_NONBLOCK
)
return
i
+
length
>
0
?
i
+
length
:
-
EAGAIN
;
if
(
wait_event_interruptible
(
s
->
packet_list_wait
,
!
list_empty
(
&
s
->
free_packet_lists
)))
return
-
EINTR
;
}
return
count
;
}
static
long
amdtp_ioctl
(
struct
file
*
file
,
unsigned
int
cmd
,
unsigned
long
arg
)
{
struct
stream
*
s
=
file
->
private_data
;
struct
amdtp_ioctl
cfg
;
int
err
;
lock_kernel
();
switch
(
cmd
)
{
case
AMDTP_IOC_PLUG
:
case
AMDTP_IOC_CHANNEL
:
if
(
copy_from_user
(
&
cfg
,
(
struct
amdtp_ioctl
__user
*
)
arg
,
sizeof
cfg
))
err
=
-
EFAULT
;
else
err
=
stream_configure
(
s
,
cmd
,
&
cfg
);
break
;
default:
err
=
-
EINVAL
;
break
;
}
unlock_kernel
();
return
err
;
}
static
unsigned
int
amdtp_poll
(
struct
file
*
file
,
poll_table
*
pt
)
{
struct
stream
*
s
=
file
->
private_data
;
poll_wait
(
file
,
&
s
->
packet_list_wait
,
pt
);
if
(
!
list_empty
(
&
s
->
free_packet_lists
))
return
POLLOUT
|
POLLWRNORM
;
else
return
0
;
}
static
int
amdtp_open
(
struct
inode
*
inode
,
struct
file
*
file
)
{
struct
amdtp_host
*
host
;
int
i
=
ieee1394_file_to_instance
(
file
);
host
=
hpsb_get_hostinfo_bykey
(
&
amdtp_highlevel
,
i
);
if
(
host
==
NULL
)
return
-
ENODEV
;
file
->
private_data
=
stream_alloc
(
host
);
if
(
file
->
private_data
==
NULL
)
return
-
ENOMEM
;
return
0
;
}
static
int
amdtp_release
(
struct
inode
*
inode
,
struct
file
*
file
)
{
struct
stream
*
s
=
file
->
private_data
;
stream_free
(
s
);
return
0
;
}
static
struct
cdev
amdtp_cdev
;
static
struct
file_operations
amdtp_fops
=
{
.
owner
=
THIS_MODULE
,
.
write
=
amdtp_write
,
.
poll
=
amdtp_poll
,
.
unlocked_ioctl
=
amdtp_ioctl
,
.
compat_ioctl
=
amdtp_ioctl
,
/* All amdtp ioctls are compatible */
.
open
=
amdtp_open
,
.
release
=
amdtp_release
};
/* IEEE1394 Subsystem functions */
static
void
amdtp_add_host
(
struct
hpsb_host
*
host
)
{
struct
amdtp_host
*
ah
;
int
minor
;
if
(
strcmp
(
host
->
driver
->
name
,
OHCI1394_DRIVER_NAME
)
!=
0
)
return
;
ah
=
hpsb_create_hostinfo
(
&
amdtp_highlevel
,
host
,
sizeof
(
*
ah
));
if
(
!
ah
)
{
HPSB_ERR
(
"amdtp: Unable able to alloc hostinfo"
);
return
;
}
ah
->
host
=
host
;
ah
->
ohci
=
host
->
hostdata
;
hpsb_set_hostinfo_key
(
&
amdtp_highlevel
,
host
,
ah
->
host
->
id
);
minor
=
IEEE1394_MINOR_BLOCK_AMDTP
*
16
+
ah
->
host
->
id
;
INIT_LIST_HEAD
(
&
ah
->
stream_list
);
spin_lock_init
(
&
ah
->
stream_list_lock
);
devfs_mk_cdev
(
MKDEV
(
IEEE1394_MAJOR
,
minor
),
S_IFCHR
|
S_IRUSR
|
S_IWUSR
,
"amdtp/%d"
,
ah
->
host
->
id
);
}
static
void
amdtp_remove_host
(
struct
hpsb_host
*
host
)
{
struct
amdtp_host
*
ah
=
hpsb_get_hostinfo
(
&
amdtp_highlevel
,
host
);
if
(
ah
)
devfs_remove
(
"amdtp/%d"
,
ah
->
host
->
id
);
return
;
}
static
struct
hpsb_highlevel
amdtp_highlevel
=
{
.
name
=
"amdtp"
,
.
add_host
=
amdtp_add_host
,
.
remove_host
=
amdtp_remove_host
,
};
/* Module interface */
MODULE_AUTHOR
(
"Kristian Hogsberg <hogsberg@users.sf.net>"
);
MODULE_DESCRIPTION
(
"Driver for Audio & Music Data Transmission Protocol "
"on OHCI boards."
);
MODULE_SUPPORTED_DEVICE
(
"amdtp"
);
MODULE_LICENSE
(
"GPL"
);
static
int
__init
amdtp_init_module
(
void
)
{
cdev_init
(
&
amdtp_cdev
,
&
amdtp_fops
);
amdtp_cdev
.
owner
=
THIS_MODULE
;
kobject_set_name
(
&
amdtp_cdev
.
kobj
,
"amdtp"
);
if
(
cdev_add
(
&
amdtp_cdev
,
IEEE1394_AMDTP_DEV
,
16
))
{
HPSB_ERR
(
"amdtp: unable to add char device"
);
return
-
EIO
;
}
devfs_mk_dir
(
"amdtp"
);
hpsb_register_highlevel
(
&
amdtp_highlevel
);
HPSB_INFO
(
"Loaded AMDTP driver"
);
return
0
;
}
static
void
__exit
amdtp_exit_module
(
void
)
{
hpsb_unregister_highlevel
(
&
amdtp_highlevel
);
devfs_remove
(
"amdtp"
);
cdev_del
(
&
amdtp_cdev
);
HPSB_INFO
(
"Unloaded AMDTP driver"
);
}
module_init
(
amdtp_init_module
);
module_exit
(
amdtp_exit_module
);
drivers/ieee1394/amdtp.h
deleted
100644 → 0
View file @
f74e6670
/* -*- c-basic-offset: 8 -*- */
#ifndef __AMDTP_H
#define __AMDTP_H
#include <asm/types.h>
#include "ieee1394-ioctl.h"
/* The userspace interface for the Audio & Music Data Transmission
* Protocol driver is really simple. First, open /dev/amdtp, use the
* ioctl to configure format, rate, dimension and either plug or
* channel, then start writing samples.
*
* The formats supported by the driver are listed below.
* AMDTP_FORMAT_RAW corresponds to the AM824 raw format, which can
* carry any number of channels, so use this if you're streaming
* multichannel audio. The AMDTP_FORMAT_IEC958_PCM corresponds to the
* AM824 IEC958 encapsulation without the IEC958 data bit set, using
* AMDTP_FORMAT_IEC958_AC3 will transmit the samples with the data bit
* set, suitable for transmitting compressed AC-3 audio.
*
* The rate field specifies the transmission rate; supported values
* are 32000, 44100, 48000, 88200, 96000, 176400 and 192000.
*
* The dimension field specifies the dimension of the signal, that is,
* the number of audio channels. Only AMDTP_FORMAT_RAW supports
* settings greater than 2.
*
* The mode field specifies which transmission mode to use. The AMDTP
* specifies two different transmission modes: blocking and
* non-blocking. The blocking transmission mode always send a fixed
* number of samples, typically 8, 16 or 32. To exactly match the
* transmission rate, the driver alternates between sending empty and
* non-empty packets. In non-blocking mode, the driver transmits as
* small packets as possible. For example, for a transmission rate of
* 44100Hz, the driver should send 5 41/80 samples in every cycle, but
* this is not possible so instead the driver alternates between
* sending 5 and 6 samples.
*
* The last thing to specify is either the isochronous channel to use
* or the output plug to connect to. If you know what channel the
* destination device will listen on, you can specify the channel
* directly and use the AMDTP_IOC_CHANNEL ioctl. However, if the
* destination device chooses the channel and uses the IEC61883-1 plug
* mechanism, you can specify an output plug to connect to. The
* driver will pick up the channel number from the plug once the
* destination device locks the output plug control register. In this
* case set the plug field and use the AMDTP_IOC_PLUG ioctl.
*
* Having configured the interface, the driver now accepts writes of
* regular 16 bit signed little endian samples, with the channels
* interleaved. For example, 4 channels would look like:
*
* | sample 0 | sample 1 ...
* | ch. 0 | ch. 1 | ch. 2 | ch. 3 | ch. 0 | ...
* | lsb | msb | lsb | msb | lsb | msb | lsb | msb | lsb | msb | ...
*
*/
enum
{
AMDTP_FORMAT_RAW
,
AMDTP_FORMAT_IEC958_PCM
,
AMDTP_FORMAT_IEC958_AC3
};
enum
{
AMDTP_MODE_BLOCKING
,
AMDTP_MODE_NON_BLOCKING
,
};
enum
{
AMDTP_INPUT_LE16
,
AMDTP_INPUT_BE16
,
};
struct
amdtp_ioctl
{
__u32
format
;
__u32
rate
;
__u32
dimension
;
__u32
mode
;
union
{
__u32
channel
;
__u32
plug
;
}
u
;
};
#endif
/* __AMDTP_H */
drivers/ieee1394/cmp.c
deleted
100644 → 0
View file @
f74e6670
/* -*- c-basic-offset: 8 -*-
*
* cmp.c - Connection Management Procedures
* Copyright (C) 2001 Kristian Hgsberg
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
/* TODO
* ----
*
* - Implement IEC61883-1 output plugs and connection management.
* This should probably be part of the general subsystem, as it could
* be shared with dv1394.
*
* - Add IEC61883 unit directory when loading this module. This
* requires a run-time changeable config rom.
*/
#include <linux/module.h>
#include <linux/list.h>
#include <linux/sched.h>
#include <linux/types.h>
#include <linux/wait.h>
#include <linux/interrupt.h>
#include "hosts.h"
#include "highlevel.h"
#include "ieee1394.h"
#include "ieee1394_core.h"
#include "cmp.h"
struct
plug
{
union
{
struct
cmp_pcr
pcr
;
quadlet_t
quadlet
;
}
u
;
void
(
*
update
)(
struct
cmp_pcr
*
plug
,
void
*
data
);
void
*
data
;
};
struct
cmp_host
{
struct
hpsb_host
*
host
;
union
{
struct
cmp_mpr
ompr
;
quadlet_t
ompr_quadlet
;
}
u
;
struct
plug
opcr
[
2
];
union
{
struct
cmp_mpr
impr
;
quadlet_t
impr_quadlet
;
}
v
;
struct
plug
ipcr
[
2
];
};
enum
{
CMP_P2P_CONNECTION
,
CMP_BC_CONNECTION
};
#define CSR_PCR_MAP 0x900
#define CSR_PCR_MAP_END 0x9fc
static
struct
hpsb_highlevel
cmp_highlevel
;
static
void
cmp_add_host
(
struct
hpsb_host
*
host
);
static
void
cmp_host_reset
(
struct
hpsb_host
*
host
);
static
int
pcr_read
(
struct
hpsb_host
*
host
,
int
nodeid
,
quadlet_t
*
buf
,
u64
addr
,
size_t
length
,
u16
flags
);
static
int
pcr_lock
(
struct
hpsb_host
*
host
,
int
nodeid
,
quadlet_t
*
store
,
u64
addr
,
quadlet_t
data
,
quadlet_t
arg
,
int
extcode
,
u16
flags
);
static
struct
hpsb_highlevel
cmp_highlevel
=
{
.
name
=
"cmp"
,
.
add_host
=
cmp_add_host
,
.
host_reset
=
cmp_host_reset
,
};
static
struct
hpsb_address_ops
pcr_ops
=
{
.
read
=
pcr_read
,
.
lock
=
pcr_lock
,
};
struct
cmp_pcr
*
cmp_register_opcr
(
struct
hpsb_host
*
host
,
int
opcr_number
,
int
payload
,
void
(
*
update
)(
struct
cmp_pcr
*
pcr
,
void
*
data
),
void
*
data
)
{
struct
cmp_host
*
ch
;
struct
plug
*
plug
;
ch
=
hpsb_get_hostinfo
(
&
cmp_highlevel
,
host
);
if
(
opcr_number
>=
ch
->
u
.
ompr
.
nplugs
||
ch
->
opcr
[
opcr_number
].
update
!=
NULL
)
return
NULL
;
plug
=
&
ch
->
opcr
[
opcr_number
];
plug
->
u
.
pcr
.
online
=
1
;
plug
->
u
.
pcr
.
bcast_count
=
0
;
plug
->
u
.
pcr
.
p2p_count
=
0
;
plug
->
u
.
pcr
.
overhead
=
0
;
plug
->
u
.
pcr
.
payload
=
payload
;
plug
->
update
=
update
;
plug
->
data
=
data
;
return
&
plug
->
u
.
pcr
;
}
void
cmp_unregister_opcr
(
struct
hpsb_host
*
host
,
struct
cmp_pcr
*
opcr
)
{
struct
cmp_host
*
ch
;
struct
plug
*
plug
;
ch
=
hpsb_get_hostinfo
(
&
cmp_highlevel
,
host
);
plug
=
(
struct
plug
*
)
opcr
;
if
(
plug
-
ch
->
opcr
>=
ch
->
u
.
ompr
.
nplugs
)
BUG
();
plug
->
u
.
pcr
.
online
=
0
;
plug
->
update
=
NULL
;
}
static
void
reset_plugs
(
struct
cmp_host
*
ch
)
{
int
i
;
ch
->
u
.
ompr
.
non_persistent_ext
=
0xff
;
for
(
i
=
0
;
i
<
ch
->
u
.
ompr
.
nplugs
;
i
++
)
{
ch
->
opcr
[
i
].
u
.
pcr
.
bcast_count
=
0
;
ch
->
opcr
[
i
].
u
.
pcr
.
p2p_count
=
0
;
ch
->
opcr
[
i
].
u
.
pcr
.
overhead
=
0
;
}
}
static
void
cmp_add_host
(
struct
hpsb_host
*
host
)
{
struct
cmp_host
*
ch
=
hpsb_create_hostinfo
(
&
cmp_highlevel
,
host
,
sizeof
(
*
ch
));
if
(
ch
==
NULL
)
{
HPSB_ERR
(
"Failed to allocate cmp_host"
);
return
;
}
hpsb_register_addrspace
(
&
cmp_highlevel
,
host
,
&
pcr_ops
,
CSR_REGISTER_BASE
+
CSR_PCR_MAP
,
CSR_REGISTER_BASE
+
CSR_PCR_MAP_END
);
ch
->
host
=
host
;
ch
->
u
.
ompr
.
rate
=
IEEE1394_SPEED_100
;
ch
->
u
.
ompr
.
bcast_channel_base
=
63
;
ch
->
u
.
ompr
.
nplugs
=
2
;
reset_plugs
(
ch
);
}
static
void
cmp_host_reset
(
struct
hpsb_host
*
host
)
{
struct
cmp_host
*
ch
;
ch
=
hpsb_get_hostinfo
(
&
cmp_highlevel
,
host
);
if
(
ch
==
NULL
)
{
HPSB_ERR
(
"cmp: Tried to reset unknown host"
);
return
;
}
reset_plugs
(
ch
);
}
static
int
pcr_read
(
struct
hpsb_host
*
host
,
int
nodeid
,
quadlet_t
*
buf
,
u64
addr
,
size_t
length
,
u16
flags
)
{
int
csraddr
=
addr
-
CSR_REGISTER_BASE
;
int
plug
;
struct
cmp_host
*
ch
;
if
(
length
!=
4
)
return
RCODE_TYPE_ERROR
;
ch
=
hpsb_get_hostinfo
(
&
cmp_highlevel
,
host
);
if
(
csraddr
==
0x900
)
{
*
buf
=
cpu_to_be32
(
ch
->
u
.
ompr_quadlet
);
return
RCODE_COMPLETE
;
}
else
if
(
csraddr
<
0x904
+
ch
->
u
.
ompr
.
nplugs
*
4
)
{
plug
=
(
csraddr
-
0x904
)
/
4
;
*
buf
=
cpu_to_be32
(
ch
->
opcr
[
plug
].
u
.
quadlet
);
return
RCODE_COMPLETE
;
}
else
if
(
csraddr
<
0x980
)
{
return
RCODE_ADDRESS_ERROR
;
}
else
if
(
csraddr
==
0x980
)
{
*
buf
=
cpu_to_be32
(
ch
->
v
.
impr_quadlet
);
return
RCODE_COMPLETE
;
}
else
if
(
csraddr
<
0x984
+
ch
->
v
.
impr
.
nplugs
*
4
)
{
plug
=
(
csraddr
-
0x984
)
/
4
;
*
buf
=
cpu_to_be32
(
ch
->
ipcr
[
plug
].
u
.
quadlet
);
return
RCODE_COMPLETE
;
}
else
return
RCODE_ADDRESS_ERROR
;
}
static
int
pcr_lock
(
struct
hpsb_host
*
host
,
int
nodeid
,
quadlet_t
*
store
,
u64
addr
,
quadlet_t
data
,
quadlet_t
arg
,
int
extcode
,
u16
flags
)
{
int
csraddr
=
addr
-
CSR_REGISTER_BASE
;
int
plug
;
struct
cmp_host
*
ch
;
ch
=
hpsb_get_hostinfo
(
&
cmp_highlevel
,
host
);
if
(
extcode
!=
EXTCODE_COMPARE_SWAP
)
return
RCODE_TYPE_ERROR
;
if
(
csraddr
==
0x900
)
{
/* FIXME: Ignore writes to bits 30-31 and 0-7 */
*
store
=
cpu_to_be32
(
ch
->
u
.
ompr_quadlet
);
if
(
arg
==
cpu_to_be32
(
ch
->
u
.
ompr_quadlet
))
ch
->
u
.
ompr_quadlet
=
be32_to_cpu
(
data
);
return
RCODE_COMPLETE
;
}
if
(
csraddr
<
0x904
+
ch
->
u
.
ompr
.
nplugs
*
4
)
{
plug
=
(
csraddr
-
0x904
)
/
4
;
*
store
=
cpu_to_be32
(
ch
->
opcr
[
plug
].
u
.
quadlet
);
if
(
arg
==
*
store
)
ch
->
opcr
[
plug
].
u
.
quadlet
=
be32_to_cpu
(
data
);
if
(
be32_to_cpu
(
*
store
)
!=
ch
->
opcr
[
plug
].
u
.
quadlet
&&
ch
->
opcr
[
plug
].
update
!=
NULL
)
ch
->
opcr
[
plug
].
update
(
&
ch
->
opcr
[
plug
].
u
.
pcr
,
ch
->
opcr
[
plug
].
data
);
return
RCODE_COMPLETE
;
}
else
if
(
csraddr
<
0x980
)
{
return
RCODE_ADDRESS_ERROR
;
}
else
if
(
csraddr
==
0x980
)
{
/* FIXME: Ignore writes to bits 24-31 and 0-7 */
*
store
=
cpu_to_be32
(
ch
->
u
.
ompr_quadlet
);
if
(
arg
==
cpu_to_be32
(
ch
->
u
.
ompr_quadlet
))
ch
->
u
.
ompr_quadlet
=
be32_to_cpu
(
data
);
return
RCODE_COMPLETE
;
}
else
if
(
csraddr
<
0x984
+
ch
->
v
.
impr
.
nplugs
*
4
)
{
plug
=
(
csraddr
-
0x984
)
/
4
;
*
store
=
cpu_to_be32
(
ch
->
ipcr
[
plug
].
u
.
quadlet
);
if
(
arg
==
*
store
)
ch
->
ipcr
[
plug
].
u
.
quadlet
=
be32_to_cpu
(
data
);
if
(
be32_to_cpu
(
*
store
)
!=
ch
->
ipcr
[
plug
].
u
.
quadlet
&&
ch
->
ipcr
[
plug
].
update
!=
NULL
)
ch
->
ipcr
[
plug
].
update
(
&
ch
->
ipcr
[
plug
].
u
.
pcr
,
ch
->
ipcr
[
plug
].
data
);
return
RCODE_COMPLETE
;
}
else
return
RCODE_ADDRESS_ERROR
;
}
/* Module interface */
MODULE_AUTHOR
(
"Kristian Hogsberg <hogsberg@users.sf.net>"
);
MODULE_DESCRIPTION
(
"Connection Management Procedures (CMP)"
);
MODULE_SUPPORTED_DEVICE
(
"cmp"
);
MODULE_LICENSE
(
"GPL"
);
EXPORT_SYMBOL
(
cmp_register_opcr
);
EXPORT_SYMBOL
(
cmp_unregister_opcr
);
static
int
__init
cmp_init_module
(
void
)
{
hpsb_register_highlevel
(
&
cmp_highlevel
);
HPSB_INFO
(
"Loaded CMP driver"
);
return
0
;
}
static
void
__exit
cmp_exit_module
(
void
)
{
hpsb_unregister_highlevel
(
&
cmp_highlevel
);
HPSB_INFO
(
"Unloaded CMP driver"
);
}
module_init
(
cmp_init_module
);
module_exit
(
cmp_exit_module
);
drivers/ieee1394/cmp.h
deleted
100644 → 0
View file @
f74e6670
#ifndef __CMP_H
#define __CMP_H
struct
cmp_mpr
{
u32
nplugs
:
5
;
u32
reserved
:
3
;
u32
persistent_ext
:
8
;
u32
non_persistent_ext
:
8
;
u32
bcast_channel_base
:
6
;
u32
rate
:
2
;
}
__attribute__
((
packed
));
struct
cmp_pcr
{
u32
payload
:
10
;
u32
overhead
:
4
;
u32
speed
:
2
;
u32
channel
:
6
;
u32
reserved
:
2
;
u32
p2p_count
:
6
;
u32
bcast_count
:
1
;
u32
online
:
1
;
}
__attribute__
((
packed
));
struct
cmp_pcr
*
cmp_register_opcr
(
struct
hpsb_host
*
host
,
int
plug
,
int
payload
,
void
(
*
update
)(
struct
cmp_pcr
*
plug
,
void
*
data
),
void
*
data
);
void
cmp_unregister_opcr
(
struct
hpsb_host
*
host
,
struct
cmp_pcr
*
plug
);
#endif
/* __CMP_H */
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment