2 * FireDTV driver -- ieee1394 I/O backend
4 * Copyright (C) 2004 Andreas Monitzer <andy@monitzer.com>
5 * Copyright (C) 2007-2008 Ben Backx <ben@bbackx.com>
6 * Copyright (C) 2008 Henrik Kurelid <henrik@kurelid.se>
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License as
10 * published by the Free Software Foundation; either version 2 of
11 * the License, or (at your option) any later version.
14 #include <linux/device.h>
15 #include <linux/errno.h>
16 #include <linux/kernel.h>
17 #include <linux/list.h>
18 #include <linux/slab.h>
19 #include <linux/spinlock.h>
20 #include <linux/types.h>
24 #include <highlevel.h>
30 #include <dvb_demux.h>
34 static LIST_HEAD(node_list
);
35 static DEFINE_SPINLOCK(node_list_lock
);
37 #define CIP_HEADER_SIZE 8
38 #define MPEG2_TS_HEADER_SIZE 4
39 #define MPEG2_TS_SOURCE_PACKET_SIZE (4 + 188)
41 static void rawiso_activity_cb(struct hpsb_iso
*iso
)
43 struct firedtv
*f
, *fdtv
= NULL
;
44 unsigned int i
, num
, packet
;
49 spin_lock_irqsave(&node_list_lock
, flags
);
50 list_for_each_entry(f
, &node_list
, list
)
51 if (f
->backend_data
== iso
) {
55 spin_unlock_irqrestore(&node_list_lock
, flags
);
57 packet
= iso
->first_packet
;
58 num
= hpsb_iso_n_ready(iso
);
61 pr_err("received at unknown iso channel\n");
65 for (i
= 0; i
< num
; i
++, packet
= (packet
+ 1) % iso
->buf_packets
) {
66 buf
= dma_region_i(&iso
->data_buf
, unsigned char,
67 iso
->infos
[packet
].offset
+ CIP_HEADER_SIZE
);
68 count
= (iso
->infos
[packet
].len
- CIP_HEADER_SIZE
) /
69 MPEG2_TS_SOURCE_PACKET_SIZE
;
71 /* ignore empty packet */
72 if (iso
->infos
[packet
].len
<= CIP_HEADER_SIZE
)
76 if (buf
[MPEG2_TS_HEADER_SIZE
] == 0x47)
77 dvb_dmx_swfilter_packets(&fdtv
->demux
,
78 &buf
[MPEG2_TS_HEADER_SIZE
], 1);
81 "skipping invalid packet\n");
82 buf
+= MPEG2_TS_SOURCE_PACKET_SIZE
;
86 hpsb_iso_recv_release_packets(iso
, num
);
89 static inline struct node_entry
*node_of(struct firedtv
*fdtv
)
91 return container_of(fdtv
->device
, struct unit_directory
, device
)->ne
;
94 static int node_lock(struct firedtv
*fdtv
, u64 addr
, void *data
)
99 ret
= hpsb_node_lock(node_of(fdtv
), addr
,
100 EXTCODE_COMPARE_SWAP
, &d
[1], d
[0]);
106 static int node_read(struct firedtv
*fdtv
, u64 addr
, void *data
)
108 return hpsb_node_read(node_of(fdtv
), addr
, data
, 4);
111 static int node_write(struct firedtv
*fdtv
, u64 addr
, void *data
, size_t len
)
113 return hpsb_node_write(node_of(fdtv
), addr
, data
, len
);
116 #define FDTV_ISO_BUFFER_PACKETS 256
117 #define FDTV_ISO_BUFFER_SIZE (FDTV_ISO_BUFFER_PACKETS * 200)
119 static int start_iso(struct firedtv
*fdtv
)
121 struct hpsb_iso
*iso_handle
;
124 iso_handle
= hpsb_iso_recv_init(node_of(fdtv
)->host
,
125 FDTV_ISO_BUFFER_SIZE
, FDTV_ISO_BUFFER_PACKETS
,
126 fdtv
->isochannel
, HPSB_ISO_DMA_DEFAULT
,
127 -1, /* stat.config.irq_interval */
129 if (iso_handle
== NULL
) {
130 dev_err(fdtv
->device
, "cannot initialize iso receive\n");
133 fdtv
->backend_data
= iso_handle
;
135 ret
= hpsb_iso_recv_start(iso_handle
, -1, -1, 0);
137 dev_err(fdtv
->device
, "cannot start iso receive\n");
138 hpsb_iso_shutdown(iso_handle
);
139 fdtv
->backend_data
= NULL
;
144 static void stop_iso(struct firedtv
*fdtv
)
146 struct hpsb_iso
*iso_handle
= fdtv
->backend_data
;
148 if (iso_handle
!= NULL
) {
149 hpsb_iso_stop(iso_handle
);
150 hpsb_iso_shutdown(iso_handle
);
152 fdtv
->backend_data
= NULL
;
155 static const struct firedtv_backend fdtv_1394_backend
= {
159 .start_iso
= start_iso
,
160 .stop_iso
= stop_iso
,
163 static void fcp_request(struct hpsb_host
*host
, int nodeid
, int direction
,
164 int cts
, u8
*data
, size_t length
)
166 struct firedtv
*f
, *fdtv
= NULL
;
170 if (length
== 0 || (data
[0] & 0xf0) != 0)
175 spin_lock_irqsave(&node_list_lock
, flags
);
176 list_for_each_entry(f
, &node_list
, list
)
177 if (node_of(f
)->host
== host
&&
178 node_of(f
)->nodeid
== nodeid
&&
179 (f
->subunit
== su
|| (f
->subunit
== 0 && su
== 0x7))) {
183 spin_unlock_irqrestore(&node_list_lock
, flags
);
186 avc_recv(fdtv
, data
, length
);
189 static int node_probe(struct device
*dev
)
191 struct unit_directory
*ud
=
192 container_of(dev
, struct unit_directory
, device
);
193 struct firedtv
*fdtv
;
197 if (ud
->model_name_kv
) {
198 kv_len
= (ud
->model_name_kv
->value
.leaf
.len
- 2) * 4;
199 kv_str
= CSR1212_TEXTUAL_DESCRIPTOR_LEAF_DATA(ud
->model_name_kv
);
204 fdtv
= fdtv_alloc(dev
, &fdtv_1394_backend
, kv_str
, kv_len
);
209 * Work around a bug in udev's path_id script: Use the fw-host's dev
210 * instead of the unit directory's dev as parent of the input device.
212 err
= fdtv_register_rc(fdtv
, dev
->parent
->parent
);
216 spin_lock_irq(&node_list_lock
);
217 list_add_tail(&fdtv
->list
, &node_list
);
218 spin_unlock_irq(&node_list_lock
);
220 err
= avc_identify_subunit(fdtv
);
224 err
= fdtv_dvb_register(fdtv
);
228 avc_register_remote_control(fdtv
);
232 spin_lock_irq(&node_list_lock
);
233 list_del(&fdtv
->list
);
234 spin_unlock_irq(&node_list_lock
);
235 fdtv_unregister_rc(fdtv
);
242 static int node_remove(struct device
*dev
)
244 struct firedtv
*fdtv
= dev_get_drvdata(dev
);
246 fdtv_dvb_unregister(fdtv
);
248 spin_lock_irq(&node_list_lock
);
249 list_del(&fdtv
->list
);
250 spin_unlock_irq(&node_list_lock
);
252 fdtv_unregister_rc(fdtv
);
258 static int node_update(struct unit_directory
*ud
)
260 struct firedtv
*fdtv
= dev_get_drvdata(&ud
->device
);
262 if (fdtv
->isochannel
>= 0)
263 cmp_establish_pp_connection(fdtv
, fdtv
->subunit
,
268 static struct hpsb_protocol_driver fdtv_driver
= {
270 .id_table
= fdtv_id_table
,
271 .update
= node_update
,
274 .remove
= node_remove
,
278 static struct hpsb_highlevel fdtv_highlevel
= {
280 .fcp_request
= fcp_request
,
283 int __init
fdtv_1394_init(void)
287 hpsb_register_highlevel(&fdtv_highlevel
);
288 ret
= hpsb_register_protocol(&fdtv_driver
);
290 printk(KERN_ERR
"firedtv: failed to register protocol\n");
291 hpsb_unregister_highlevel(&fdtv_highlevel
);
296 void __exit
fdtv_1394_exit(void)
298 hpsb_unregister_protocol(&fdtv_driver
);
299 hpsb_unregister_highlevel(&fdtv_highlevel
);