aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media/dvb/firewire/firedtv-fw.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/dvb/firewire/firedtv-fw.c')
-rw-r--r--drivers/media/dvb/firewire/firedtv-fw.c385
1 files changed, 385 insertions, 0 deletions
diff --git a/drivers/media/dvb/firewire/firedtv-fw.c b/drivers/media/dvb/firewire/firedtv-fw.c
new file mode 100644
index 000000000000..208e5b59e830
--- /dev/null
+++ b/drivers/media/dvb/firewire/firedtv-fw.c
@@ -0,0 +1,385 @@
1/*
2 * FireDTV driver -- firewire I/O backend
3 */
4
5#include <linux/device.h>
6#include <linux/errno.h>
7#include <linux/firewire.h>
8#include <linux/firewire-constants.h>
9#include <linux/highmem.h>
10#include <linux/kernel.h>
11#include <linux/list.h>
12#include <linux/slab.h>
13#include <linux/spinlock.h>
14#include <linux/types.h>
15
16#include <asm/page.h>
17
18#include <dvb_demux.h>
19
20#include "firedtv.h"
21
22static LIST_HEAD(node_list);
23static DEFINE_SPINLOCK(node_list_lock);
24
25static inline struct fw_device *device_of(struct firedtv *fdtv)
26{
27 return fw_device(fdtv->device->parent);
28}
29
30static int node_req(struct firedtv *fdtv, u64 addr, void *data, size_t len,
31 int tcode)
32{
33 struct fw_device *device = device_of(fdtv);
34 int rcode, generation = device->generation;
35
36 smp_rmb(); /* node_id vs. generation */
37
38 rcode = fw_run_transaction(device->card, tcode, device->node_id,
39 generation, device->max_speed, addr, data, len);
40
41 return rcode != RCODE_COMPLETE ? -EIO : 0;
42}
43
44static int node_lock(struct firedtv *fdtv, u64 addr, __be32 data[])
45{
46 return node_req(fdtv, addr, data, 8, TCODE_LOCK_COMPARE_SWAP);
47}
48
49static int node_read(struct firedtv *fdtv, u64 addr, void *data, size_t len)
50{
51 return node_req(fdtv, addr, data, len, len == 4 ?
52 TCODE_READ_QUADLET_REQUEST : TCODE_READ_BLOCK_REQUEST);
53}
54
55static int node_write(struct firedtv *fdtv, u64 addr, void *data, size_t len)
56{
57 return node_req(fdtv, addr, data, len, TCODE_WRITE_BLOCK_REQUEST);
58}
59
60#define ISO_HEADER_SIZE 4
61#define CIP_HEADER_SIZE 8
62#define MPEG2_TS_HEADER_SIZE 4
63#define MPEG2_TS_SOURCE_PACKET_SIZE (4 + 188)
64
65#define MAX_PACKET_SIZE 1024 /* 776, rounded up to 2^n */
66#define PACKETS_PER_PAGE (PAGE_SIZE / MAX_PACKET_SIZE)
67#define N_PACKETS 64 /* buffer size */
68#define N_PAGES DIV_ROUND_UP(N_PACKETS, PACKETS_PER_PAGE)
69#define IRQ_INTERVAL 16
70
71struct firedtv_receive_context {
72 struct fw_iso_context *context;
73 struct fw_iso_buffer buffer;
74 int interrupt_packet;
75 int current_packet;
76 char *packets[N_PACKETS];
77};
78
79static int queue_iso(struct firedtv_receive_context *ctx, int index)
80{
81 struct fw_iso_packet p;
82 int err;
83
84 p.payload_length = MAX_PACKET_SIZE;
85 p.interrupt = !(ctx->interrupt_packet & (IRQ_INTERVAL - 1));
86 p.skip = 0;
87 p.header_length = ISO_HEADER_SIZE;
88
89 err = fw_iso_context_queue(ctx->context, &p, &ctx->buffer,
90 index * MAX_PACKET_SIZE);
91 if (!err)
92 ctx->interrupt_packet++;
93
94 return err;
95}
96
97static void handle_iso(struct fw_iso_context *context, u32 cycle,
98 size_t header_length, void *header, void *data)
99{
100 struct firedtv *fdtv = data;
101 struct firedtv_receive_context *ctx = fdtv->backend_data;
102 __be32 *h, *h_end;
103 int i = ctx->current_packet, length, err;
104 char *p, *p_end;
105
106 for (h = header, h_end = h + header_length / 4; h < h_end; h++) {
107 length = be32_to_cpup(h) >> 16;
108 if (unlikely(length > MAX_PACKET_SIZE)) {
109 dev_err(fdtv->device, "length = %d\n", length);
110 length = MAX_PACKET_SIZE;
111 }
112
113 p = ctx->packets[i];
114 p_end = p + length;
115
116 for (p += CIP_HEADER_SIZE + MPEG2_TS_HEADER_SIZE; p < p_end;
117 p += MPEG2_TS_SOURCE_PACKET_SIZE)
118 dvb_dmx_swfilter_packets(&fdtv->demux, p, 1);
119
120 err = queue_iso(ctx, i);
121 if (unlikely(err))
122 dev_err(fdtv->device, "requeue failed\n");
123
124 i = (i + 1) & (N_PACKETS - 1);
125 }
126 ctx->current_packet = i;
127}
128
129static int start_iso(struct firedtv *fdtv)
130{
131 struct firedtv_receive_context *ctx;
132 struct fw_device *device = device_of(fdtv);
133 char *p;
134 int i, j, k, err;
135
136 ctx = kmalloc(sizeof(*ctx), GFP_KERNEL);
137 if (!ctx)
138 return -ENOMEM;
139
140 ctx->context = fw_iso_context_create(device->card,
141 FW_ISO_CONTEXT_RECEIVE, fdtv->isochannel,
142 device->max_speed, ISO_HEADER_SIZE, handle_iso, fdtv);
143 if (IS_ERR(ctx->context)) {
144 err = PTR_ERR(ctx->context);
145 goto fail_free;
146 }
147
148 err = fw_iso_buffer_init(&ctx->buffer, device->card,
149 N_PAGES, DMA_FROM_DEVICE);
150 if (err)
151 goto fail_context_destroy;
152
153 ctx->interrupt_packet = 1;
154 ctx->current_packet = 0;
155
156 for (i = 0, k = 0; k < N_PAGES; k++) {
157 p = kmap(ctx->buffer.pages[k]);
158 for (j = 0; j < PACKETS_PER_PAGE && i < N_PACKETS; j++, i++)
159 ctx->packets[i] = p + j * MAX_PACKET_SIZE;
160 }
161
162 for (i = 0; i < N_PACKETS; i++) {
163 err = queue_iso(ctx, i);
164 if (err)
165 goto fail;
166 }
167
168 err = fw_iso_context_start(ctx->context, -1, 0,
169 FW_ISO_CONTEXT_MATCH_ALL_TAGS);
170 if (err)
171 goto fail;
172
173 fdtv->backend_data = ctx;
174
175 return 0;
176fail:
177 fw_iso_buffer_destroy(&ctx->buffer, device->card);
178fail_context_destroy:
179 fw_iso_context_destroy(ctx->context);
180fail_free:
181 kfree(ctx);
182
183 return err;
184}
185
186static void stop_iso(struct firedtv *fdtv)
187{
188 struct firedtv_receive_context *ctx = fdtv->backend_data;
189
190 fw_iso_context_stop(ctx->context);
191 fw_iso_buffer_destroy(&ctx->buffer, device_of(fdtv)->card);
192 fw_iso_context_destroy(ctx->context);
193 kfree(ctx);
194}
195
196static const struct firedtv_backend backend = {
197 .lock = node_lock,
198 .read = node_read,
199 .write = node_write,
200 .start_iso = start_iso,
201 .stop_iso = stop_iso,
202};
203
204static void handle_fcp(struct fw_card *card, struct fw_request *request,
205 int tcode, int destination, int source, int generation,
206 int speed, unsigned long long offset,
207 void *payload, size_t length, void *callback_data)
208{
209 struct firedtv *f, *fdtv = NULL;
210 struct fw_device *device;
211 unsigned long flags;
212 int su;
213
214 if ((tcode != TCODE_WRITE_QUADLET_REQUEST &&
215 tcode != TCODE_WRITE_BLOCK_REQUEST) ||
216 offset != CSR_REGISTER_BASE + CSR_FCP_RESPONSE ||
217 length == 0 ||
218 (((u8 *)payload)[0] & 0xf0) != 0) {
219 fw_send_response(card, request, RCODE_TYPE_ERROR);
220 return;
221 }
222
223 su = ((u8 *)payload)[1] & 0x7;
224
225 spin_lock_irqsave(&node_list_lock, flags);
226 list_for_each_entry(f, &node_list, list) {
227 device = device_of(f);
228 if (device->generation != generation)
229 continue;
230
231 smp_rmb(); /* node_id vs. generation */
232
233 if (device->card == card &&
234 device->node_id == source &&
235 (f->subunit == su || (f->subunit == 0 && su == 0x7))) {
236 fdtv = f;
237 break;
238 }
239 }
240 spin_unlock_irqrestore(&node_list_lock, flags);
241
242 if (fdtv) {
243 avc_recv(fdtv, payload, length);
244 fw_send_response(card, request, RCODE_COMPLETE);
245 }
246}
247
248static struct fw_address_handler fcp_handler = {
249 .length = CSR_FCP_END - CSR_FCP_RESPONSE,
250 .address_callback = handle_fcp,
251};
252
253static const struct fw_address_region fcp_region = {
254 .start = CSR_REGISTER_BASE + CSR_FCP_RESPONSE,
255 .end = CSR_REGISTER_BASE + CSR_FCP_END,
256};
257
258/* Adjust the template string if models with longer names appear. */
259#define MAX_MODEL_NAME_LEN ((int)DIV_ROUND_UP(sizeof("FireDTV ????"), 4))
260
261static size_t model_name(u32 *directory, __be32 *buffer)
262{
263 struct fw_csr_iterator ci;
264 int i, length, key, value, last_key = 0;
265 u32 *block = NULL;
266
267 fw_csr_iterator_init(&ci, directory);
268 while (fw_csr_iterator_next(&ci, &key, &value)) {
269 if (last_key == CSR_MODEL &&
270 key == (CSR_DESCRIPTOR | CSR_LEAF))
271 block = ci.p - 1 + value;
272 last_key = key;
273 }
274
275 if (block == NULL)
276 return 0;
277
278 length = min((int)(block[0] >> 16) - 2, MAX_MODEL_NAME_LEN);
279 if (length <= 0)
280 return 0;
281
282 /* fast-forward to text string */
283 block += 3;
284
285 for (i = 0; i < length; i++)
286 buffer[i] = cpu_to_be32(block[i]);
287
288 return length * 4;
289}
290
291static int node_probe(struct device *dev)
292{
293 struct firedtv *fdtv;
294 __be32 name[MAX_MODEL_NAME_LEN];
295 int name_len, err;
296
297 name_len = model_name(fw_unit(dev)->directory, name);
298
299 fdtv = fdtv_alloc(dev, &backend, (char *)name, name_len);
300 if (!fdtv)
301 return -ENOMEM;
302
303 err = fdtv_register_rc(fdtv, dev);
304 if (err)
305 goto fail_free;
306
307 spin_lock_irq(&node_list_lock);
308 list_add_tail(&fdtv->list, &node_list);
309 spin_unlock_irq(&node_list_lock);
310
311 err = avc_identify_subunit(fdtv);
312 if (err)
313 goto fail;
314
315 err = fdtv_dvb_register(fdtv);
316 if (err)
317 goto fail;
318
319 avc_register_remote_control(fdtv);
320
321 return 0;
322fail:
323 spin_lock_irq(&node_list_lock);
324 list_del(&fdtv->list);
325 spin_unlock_irq(&node_list_lock);
326 fdtv_unregister_rc(fdtv);
327fail_free:
328 kfree(fdtv);
329
330 return err;
331}
332
333static int node_remove(struct device *dev)
334{
335 struct firedtv *fdtv = dev_get_drvdata(dev);
336
337 fdtv_dvb_unregister(fdtv);
338
339 spin_lock_irq(&node_list_lock);
340 list_del(&fdtv->list);
341 spin_unlock_irq(&node_list_lock);
342
343 fdtv_unregister_rc(fdtv);
344
345 kfree(fdtv);
346 return 0;
347}
348
349static void node_update(struct fw_unit *unit)
350{
351 struct firedtv *fdtv = dev_get_drvdata(&unit->device);
352
353 if (fdtv->isochannel >= 0)
354 cmp_establish_pp_connection(fdtv, fdtv->subunit,
355 fdtv->isochannel);
356}
357
358static struct fw_driver fdtv_driver = {
359 .driver = {
360 .owner = THIS_MODULE,
361 .name = "firedtv",
362 .bus = &fw_bus_type,
363 .probe = node_probe,
364 .remove = node_remove,
365 },
366 .update = node_update,
367 .id_table = fdtv_id_table,
368};
369
370int __init fdtv_fw_init(void)
371{
372 int ret;
373
374 ret = fw_core_add_address_handler(&fcp_handler, &fcp_region);
375 if (ret < 0)
376 return ret;
377
378 return driver_register(&fdtv_driver.driver);
379}
380
381void fdtv_fw_exit(void)
382{
383 driver_unregister(&fdtv_driver.driver);
384 fw_core_remove_address_handler(&fcp_handler);
385}