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/spinlock.h>
19 #include <linux/types.h>
23 #include <highlevel.h>
29 #include <dvb_demux.h>
33 static LIST_HEAD(node_list
);
34 static DEFINE_SPINLOCK(node_list_lock
);
36 #define CIP_HEADER_SIZE 8
37 #define MPEG2_TS_HEADER_SIZE 4
38 #define MPEG2_TS_SOURCE_PACKET_SIZE (4 + 188)
40 static void rawiso_activity_cb(struct hpsb_iso
*iso
)
42 struct firedtv
*f
, *fdtv
= NULL
;
43 unsigned int i
, num
, packet
;
48 spin_lock_irqsave(&node_list_lock
, flags
);
49 list_for_each_entry(f
, &node_list
, list
)
50 if (f
->backend_data
== iso
) {
54 spin_unlock_irqrestore(&node_list_lock
, flags
);
56 packet
= iso
->first_packet
;
57 num
= hpsb_iso_n_ready(iso
);
60 dev_err(fdtv
->device
, "received at unknown iso channel\n");
64 for (i
= 0; i
< num
; i
++, packet
= (packet
+ 1) % iso
->buf_packets
) {
65 buf
= dma_region_i(&iso
->data_buf
, unsigned char,
66 iso
->infos
[packet
].offset
+ CIP_HEADER_SIZE
);
67 count
= (iso
->infos
[packet
].len
- CIP_HEADER_SIZE
) /
68 MPEG2_TS_SOURCE_PACKET_SIZE
;
70 /* ignore empty packet */
71 if (iso
->infos
[packet
].len
<= CIP_HEADER_SIZE
)
75 if (buf
[MPEG2_TS_HEADER_SIZE
] == 0x47)
76 dvb_dmx_swfilter_packets(&fdtv
->demux
,
77 &buf
[MPEG2_TS_HEADER_SIZE
], 1);
80 "skipping invalid packet\n");
81 buf
+= MPEG2_TS_SOURCE_PACKET_SIZE
;
85 hpsb_iso_recv_release_packets(iso
, num
);
88 static inline struct node_entry
*node_of(struct firedtv
*fdtv
)
90 return container_of(fdtv
->device
, struct unit_directory
, device
)->ne
;
93 static int node_lock(struct firedtv
*fdtv
, u64 addr
, void *data
)
98 ret
= hpsb_node_lock(node_of(fdtv
), addr
,
99 EXTCODE_COMPARE_SWAP
, &d
[1], d
[0]);
105 static int node_read(struct firedtv
*fdtv
, u64 addr
, void *data
)
107 return hpsb_node_read(node_of(fdtv
), addr
, data
, 4);
110 static int node_write(struct firedtv
*fdtv
, u64 addr
, void *data
, size_t len
)
112 return hpsb_node_write(node_of(fdtv
), addr
, data
, len
);
115 #define FDTV_ISO_BUFFER_PACKETS 256
116 #define FDTV_ISO_BUFFER_SIZE (FDTV_ISO_BUFFER_PACKETS * 200)
118 static int start_iso(struct firedtv
*fdtv
)
120 struct hpsb_iso
*iso_handle
;
123 iso_handle
= hpsb_iso_recv_init(node_of(fdtv
)->host
,
124 FDTV_ISO_BUFFER_SIZE
, FDTV_ISO_BUFFER_PACKETS
,
125 fdtv
->isochannel
, HPSB_ISO_DMA_DEFAULT
,
126 -1, /* stat.config.irq_interval */
128 if (iso_handle
== NULL
) {
129 dev_err(fdtv
->device
, "cannot initialize iso receive\n");
132 fdtv
->backend_data
= iso_handle
;
134 ret
= hpsb_iso_recv_start(iso_handle
, -1, -1, 0);
136 dev_err(fdtv
->device
, "cannot start iso receive\n");
137 hpsb_iso_shutdown(iso_handle
);
138 fdtv
->backend_data
= NULL
;
143 static void stop_iso(struct firedtv
*fdtv
)
145 struct hpsb_iso
*iso_handle
= fdtv
->backend_data
;
147 if (iso_handle
!= NULL
) {
148 hpsb_iso_stop(iso_handle
);
149 hpsb_iso_shutdown(iso_handle
);
151 fdtv
->backend_data
= NULL
;
154 static const struct firedtv_backend fdtv_1394_backend
= {
158 .start_iso
= start_iso
,
159 .stop_iso
= stop_iso
,
162 static void fcp_request(struct hpsb_host
*host
, int nodeid
, int direction
,
163 int cts
, u8
*data
, size_t length
)
165 struct firedtv
*f
, *fdtv
= NULL
;
169 if (length
== 0 || (data
[0] & 0xf0) != 0)
174 spin_lock_irqsave(&node_list_lock
, flags
);
175 list_for_each_entry(f
, &node_list
, list
)
176 if (node_of(f
)->host
== host
&&
177 node_of(f
)->nodeid
== nodeid
&&
178 (f
->subunit
== su
|| (f
->subunit
== 0 && su
== 0x7))) {
182 spin_unlock_irqrestore(&node_list_lock
, flags
);
185 avc_recv(fdtv
, data
, length
);
188 static int node_probe(struct device
*dev
)
190 struct unit_directory
*ud
=
191 container_of(dev
, struct unit_directory
, device
);
192 struct firedtv
*fdtv
;
196 if (ud
->model_name_kv
) {
197 kv_len
= (ud
->model_name_kv
->value
.leaf
.len
- 2) * 4;
198 kv_str
= CSR1212_TEXTUAL_DESCRIPTOR_LEAF_DATA(ud
->model_name_kv
);
203 fdtv
= fdtv_alloc(dev
, &fdtv_1394_backend
, kv_str
, kv_len
);
208 * Work around a bug in udev's path_id script: Use the fw-host's dev
209 * instead of the unit directory's dev as parent of the input device.
211 err
= fdtv_register_rc(fdtv
, dev
->parent
->parent
);
215 spin_lock_irq(&node_list_lock
);
216 list_add_tail(&fdtv
->list
, &node_list
);
217 spin_unlock_irq(&node_list_lock
);
219 err
= avc_identify_subunit(fdtv
);
223 err
= fdtv_dvb_register(fdtv
);
227 avc_register_remote_control(fdtv
);
231 spin_lock_irq(&node_list_lock
);
232 list_del(&fdtv
->list
);
233 spin_unlock_irq(&node_list_lock
);
234 fdtv_unregister_rc(fdtv
);
241 static int node_remove(struct device
*dev
)
243 struct firedtv
*fdtv
= dev_get_drvdata(dev
);
245 fdtv_dvb_unregister(fdtv
);
247 spin_lock_irq(&node_list_lock
);
248 list_del(&fdtv
->list
);
249 spin_unlock_irq(&node_list_lock
);
251 fdtv_unregister_rc(fdtv
);
257 static int node_update(struct unit_directory
*ud
)
259 struct firedtv
*fdtv
= dev_get_drvdata(&ud
->device
);
261 if (fdtv
->isochannel
>= 0)
262 cmp_establish_pp_connection(fdtv
, fdtv
->subunit
,
267 static struct hpsb_protocol_driver fdtv_driver
= {
269 .id_table
= fdtv_id_table
,
270 .update
= node_update
,
273 .remove
= node_remove
,
277 static struct hpsb_highlevel fdtv_highlevel
= {
279 .fcp_request
= fcp_request
,
282 int __init
fdtv_1394_init(void)
286 hpsb_register_highlevel(&fdtv_highlevel
);
287 ret
= hpsb_register_protocol(&fdtv_driver
);
289 printk(KERN_ERR
"firedtv: failed to register protocol\n");
290 hpsb_unregister_highlevel(&fdtv_highlevel
);
295 void __exit
fdtv_1394_exit(void)
297 hpsb_unregister_protocol(&fdtv_driver
);
298 hpsb_unregister_highlevel(&fdtv_highlevel
);