Commit | Line | Data |
---|---|---|
87918334 SR |
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> | |
87918334 SR |
9 | #include <linux/kernel.h> |
10 | #include <linux/list.h> | |
a8aeb783 | 11 | #include <linux/mm.h> |
92374e88 SR |
12 | #include <linux/mod_devicetable.h> |
13 | #include <linux/module.h> | |
14 | #include <linux/mutex.h> | |
87918334 SR |
15 | #include <linux/slab.h> |
16 | #include <linux/spinlock.h> | |
92374e88 | 17 | #include <linux/string.h> |
87918334 | 18 | #include <linux/types.h> |
92374e88 SR |
19 | #include <linux/wait.h> |
20 | #include <linux/workqueue.h> | |
87918334 SR |
21 | |
22 | #include <asm/page.h> | |
23 | ||
24 | #include <dvb_demux.h> | |
25 | ||
26 | #include "firedtv.h" | |
27 | ||
28 | static LIST_HEAD(node_list); | |
29 | static DEFINE_SPINLOCK(node_list_lock); | |
30 | ||
31 | static inline struct fw_device *device_of(struct firedtv *fdtv) | |
32 | { | |
33 | return fw_device(fdtv->device->parent); | |
34 | } | |
35 | ||
36 | static int node_req(struct firedtv *fdtv, u64 addr, void *data, size_t len, | |
37 | int tcode) | |
38 | { | |
39 | struct fw_device *device = device_of(fdtv); | |
40 | int rcode, generation = device->generation; | |
41 | ||
42 | smp_rmb(); /* node_id vs. generation */ | |
43 | ||
44 | rcode = fw_run_transaction(device->card, tcode, device->node_id, | |
45 | generation, device->max_speed, addr, data, len); | |
46 | ||
47 | return rcode != RCODE_COMPLETE ? -EIO : 0; | |
48 | } | |
49 | ||
92374e88 | 50 | int fdtv_lock(struct firedtv *fdtv, u64 addr, void *data) |
87918334 SR |
51 | { |
52 | return node_req(fdtv, addr, data, 8, TCODE_LOCK_COMPARE_SWAP); | |
53 | } | |
54 | ||
92374e88 | 55 | int fdtv_read(struct firedtv *fdtv, u64 addr, void *data) |
87918334 | 56 | { |
5375659a | 57 | return node_req(fdtv, addr, data, 4, TCODE_READ_QUADLET_REQUEST); |
87918334 SR |
58 | } |
59 | ||
92374e88 | 60 | int fdtv_write(struct firedtv *fdtv, u64 addr, void *data, size_t len) |
87918334 SR |
61 | { |
62 | return node_req(fdtv, addr, data, len, TCODE_WRITE_BLOCK_REQUEST); | |
63 | } | |
64 | ||
65 | #define ISO_HEADER_SIZE 4 | |
66 | #define CIP_HEADER_SIZE 8 | |
67 | #define MPEG2_TS_HEADER_SIZE 4 | |
68 | #define MPEG2_TS_SOURCE_PACKET_SIZE (4 + 188) | |
69 | ||
70 | #define MAX_PACKET_SIZE 1024 /* 776, rounded up to 2^n */ | |
71 | #define PACKETS_PER_PAGE (PAGE_SIZE / MAX_PACKET_SIZE) | |
72 | #define N_PACKETS 64 /* buffer size */ | |
73 | #define N_PAGES DIV_ROUND_UP(N_PACKETS, PACKETS_PER_PAGE) | |
74 | #define IRQ_INTERVAL 16 | |
75 | ||
92374e88 | 76 | struct fdtv_ir_context { |
87918334 SR |
77 | struct fw_iso_context *context; |
78 | struct fw_iso_buffer buffer; | |
79 | int interrupt_packet; | |
80 | int current_packet; | |
a8aeb783 | 81 | char *pages[N_PAGES]; |
87918334 SR |
82 | }; |
83 | ||
92374e88 | 84 | static int queue_iso(struct fdtv_ir_context *ctx, int index) |
87918334 SR |
85 | { |
86 | struct fw_iso_packet p; | |
87918334 SR |
87 | |
88 | p.payload_length = MAX_PACKET_SIZE; | |
b1d33f4b | 89 | p.interrupt = !(++ctx->interrupt_packet & (IRQ_INTERVAL - 1)); |
87918334 SR |
90 | p.skip = 0; |
91 | p.header_length = ISO_HEADER_SIZE; | |
92 | ||
b1d33f4b SR |
93 | return fw_iso_context_queue(ctx->context, &p, &ctx->buffer, |
94 | index * MAX_PACKET_SIZE); | |
87918334 SR |
95 | } |
96 | ||
97 | static 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; | |
92374e88 | 101 | struct fdtv_ir_context *ctx = fdtv->ir_context; |
87918334 | 102 | __be32 *h, *h_end; |
a8aeb783 | 103 | int length, err, i = ctx->current_packet; |
87918334 SR |
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 | ||
a8aeb783 SR |
113 | p = ctx->pages[i / PACKETS_PER_PAGE] |
114 | + (i % PACKETS_PER_PAGE) * MAX_PACKET_SIZE; | |
87918334 SR |
115 | p_end = p + length; |
116 | ||
117 | for (p += CIP_HEADER_SIZE + MPEG2_TS_HEADER_SIZE; p < p_end; | |
118 | p += MPEG2_TS_SOURCE_PACKET_SIZE) | |
119 | dvb_dmx_swfilter_packets(&fdtv->demux, p, 1); | |
120 | ||
121 | err = queue_iso(ctx, i); | |
122 | if (unlikely(err)) | |
123 | dev_err(fdtv->device, "requeue failed\n"); | |
124 | ||
125 | i = (i + 1) & (N_PACKETS - 1); | |
126 | } | |
13882a82 | 127 | fw_iso_context_queue_flush(ctx->context); |
87918334 SR |
128 | ctx->current_packet = i; |
129 | } | |
130 | ||
92374e88 | 131 | int fdtv_start_iso(struct firedtv *fdtv) |
87918334 | 132 | { |
92374e88 | 133 | struct fdtv_ir_context *ctx; |
87918334 | 134 | struct fw_device *device = device_of(fdtv); |
a8aeb783 | 135 | int i, err; |
87918334 SR |
136 | |
137 | ctx = kmalloc(sizeof(*ctx), GFP_KERNEL); | |
138 | if (!ctx) | |
139 | return -ENOMEM; | |
140 | ||
141 | ctx->context = fw_iso_context_create(device->card, | |
142 | FW_ISO_CONTEXT_RECEIVE, fdtv->isochannel, | |
143 | device->max_speed, ISO_HEADER_SIZE, handle_iso, fdtv); | |
144 | if (IS_ERR(ctx->context)) { | |
145 | err = PTR_ERR(ctx->context); | |
146 | goto fail_free; | |
147 | } | |
148 | ||
149 | err = fw_iso_buffer_init(&ctx->buffer, device->card, | |
150 | N_PAGES, DMA_FROM_DEVICE); | |
151 | if (err) | |
152 | goto fail_context_destroy; | |
153 | ||
b1d33f4b | 154 | ctx->interrupt_packet = 0; |
87918334 SR |
155 | ctx->current_packet = 0; |
156 | ||
a8aeb783 SR |
157 | for (i = 0; i < N_PAGES; i++) |
158 | ctx->pages[i] = page_address(ctx->buffer.pages[i]); | |
87918334 SR |
159 | |
160 | for (i = 0; i < N_PACKETS; i++) { | |
161 | err = queue_iso(ctx, i); | |
162 | if (err) | |
163 | goto fail; | |
164 | } | |
165 | ||
166 | err = fw_iso_context_start(ctx->context, -1, 0, | |
167 | FW_ISO_CONTEXT_MATCH_ALL_TAGS); | |
168 | if (err) | |
169 | goto fail; | |
170 | ||
92374e88 | 171 | fdtv->ir_context = ctx; |
87918334 SR |
172 | |
173 | return 0; | |
174 | fail: | |
175 | fw_iso_buffer_destroy(&ctx->buffer, device->card); | |
176 | fail_context_destroy: | |
177 | fw_iso_context_destroy(ctx->context); | |
178 | fail_free: | |
179 | kfree(ctx); | |
180 | ||
181 | return err; | |
182 | } | |
183 | ||
92374e88 | 184 | void fdtv_stop_iso(struct firedtv *fdtv) |
87918334 | 185 | { |
92374e88 | 186 | struct fdtv_ir_context *ctx = fdtv->ir_context; |
87918334 SR |
187 | |
188 | fw_iso_context_stop(ctx->context); | |
189 | fw_iso_buffer_destroy(&ctx->buffer, device_of(fdtv)->card); | |
190 | fw_iso_context_destroy(ctx->context); | |
191 | kfree(ctx); | |
192 | } | |
193 | ||
87918334 SR |
194 | static void handle_fcp(struct fw_card *card, struct fw_request *request, |
195 | int tcode, int destination, int source, int generation, | |
33e553fe SR |
196 | unsigned long long offset, void *payload, size_t length, |
197 | void *callback_data) | |
87918334 SR |
198 | { |
199 | struct firedtv *f, *fdtv = NULL; | |
200 | struct fw_device *device; | |
201 | unsigned long flags; | |
202 | int su; | |
203 | ||
db5d247a | 204 | if (length < 2 || (((u8 *)payload)[0] & 0xf0) != 0) |
87918334 | 205 | return; |
87918334 SR |
206 | |
207 | su = ((u8 *)payload)[1] & 0x7; | |
208 | ||
209 | spin_lock_irqsave(&node_list_lock, flags); | |
210 | list_for_each_entry(f, &node_list, list) { | |
211 | device = device_of(f); | |
212 | if (device->generation != generation) | |
213 | continue; | |
214 | ||
215 | smp_rmb(); /* node_id vs. generation */ | |
216 | ||
217 | if (device->card == card && | |
218 | device->node_id == source && | |
219 | (f->subunit == su || (f->subunit == 0 && su == 0x7))) { | |
220 | fdtv = f; | |
221 | break; | |
222 | } | |
223 | } | |
224 | spin_unlock_irqrestore(&node_list_lock, flags); | |
225 | ||
db5d247a | 226 | if (fdtv) |
87918334 | 227 | avc_recv(fdtv, payload, length); |
87918334 SR |
228 | } |
229 | ||
230 | static struct fw_address_handler fcp_handler = { | |
231 | .length = CSR_FCP_END - CSR_FCP_RESPONSE, | |
232 | .address_callback = handle_fcp, | |
233 | }; | |
234 | ||
235 | static const struct fw_address_region fcp_region = { | |
236 | .start = CSR_REGISTER_BASE + CSR_FCP_RESPONSE, | |
237 | .end = CSR_REGISTER_BASE + CSR_FCP_END, | |
238 | }; | |
239 | ||
92374e88 SR |
240 | static const char * const model_names[] = { |
241 | [FIREDTV_UNKNOWN] = "unknown type", | |
242 | [FIREDTV_DVB_S] = "FireDTV S/CI", | |
243 | [FIREDTV_DVB_C] = "FireDTV C/CI", | |
244 | [FIREDTV_DVB_T] = "FireDTV T/CI", | |
245 | [FIREDTV_DVB_S2] = "FireDTV S2 ", | |
246 | }; | |
247 | ||
87918334 | 248 | /* Adjust the template string if models with longer names appear. */ |
1f8fef7b | 249 | #define MAX_MODEL_NAME_LEN sizeof("FireDTV ????") |
87918334 | 250 | |
94a87157 | 251 | static int node_probe(struct fw_unit *unit, const struct ieee1394_device_id *id) |
87918334 SR |
252 | { |
253 | struct firedtv *fdtv; | |
1f8fef7b | 254 | char name[MAX_MODEL_NAME_LEN]; |
92374e88 | 255 | int name_len, i, err; |
87918334 | 256 | |
92374e88 | 257 | fdtv = kzalloc(sizeof(*fdtv), GFP_KERNEL); |
87918334 SR |
258 | if (!fdtv) |
259 | return -ENOMEM; | |
260 | ||
94a87157 SR |
261 | dev_set_drvdata(&unit->device, fdtv); |
262 | fdtv->device = &unit->device; | |
92374e88 SR |
263 | fdtv->isochannel = -1; |
264 | fdtv->voltage = 0xff; | |
265 | fdtv->tone = 0xff; | |
266 | ||
267 | mutex_init(&fdtv->avc_mutex); | |
268 | init_waitqueue_head(&fdtv->avc_wait); | |
269 | mutex_init(&fdtv->demux_mutex); | |
270 | INIT_WORK(&fdtv->remote_ctrl_work, avc_remote_ctrl_work); | |
271 | ||
94a87157 | 272 | name_len = fw_csr_string(unit->directory, CSR_MODEL, |
92374e88 SR |
273 | name, sizeof(name)); |
274 | for (i = ARRAY_SIZE(model_names); --i; ) | |
275 | if (strlen(model_names[i]) <= name_len && | |
276 | strncmp(name, model_names[i], name_len) == 0) | |
277 | break; | |
278 | fdtv->type = i; | |
279 | ||
94a87157 | 280 | err = fdtv_register_rc(fdtv, &unit->device); |
87918334 SR |
281 | if (err) |
282 | goto fail_free; | |
283 | ||
284 | spin_lock_irq(&node_list_lock); | |
285 | list_add_tail(&fdtv->list, &node_list); | |
286 | spin_unlock_irq(&node_list_lock); | |
287 | ||
288 | err = avc_identify_subunit(fdtv); | |
289 | if (err) | |
290 | goto fail; | |
291 | ||
92374e88 | 292 | err = fdtv_dvb_register(fdtv, model_names[fdtv->type]); |
87918334 SR |
293 | if (err) |
294 | goto fail; | |
295 | ||
296 | avc_register_remote_control(fdtv); | |
297 | ||
298 | return 0; | |
299 | fail: | |
300 | spin_lock_irq(&node_list_lock); | |
301 | list_del(&fdtv->list); | |
302 | spin_unlock_irq(&node_list_lock); | |
303 | fdtv_unregister_rc(fdtv); | |
304 | fail_free: | |
305 | kfree(fdtv); | |
306 | ||
307 | return err; | |
308 | } | |
309 | ||
94a87157 | 310 | static void node_remove(struct fw_unit *unit) |
87918334 | 311 | { |
94a87157 | 312 | struct firedtv *fdtv = dev_get_drvdata(&unit->device); |
87918334 SR |
313 | |
314 | fdtv_dvb_unregister(fdtv); | |
315 | ||
316 | spin_lock_irq(&node_list_lock); | |
317 | list_del(&fdtv->list); | |
318 | spin_unlock_irq(&node_list_lock); | |
319 | ||
320 | fdtv_unregister_rc(fdtv); | |
321 | ||
322 | kfree(fdtv); | |
87918334 SR |
323 | } |
324 | ||
325 | static void node_update(struct fw_unit *unit) | |
326 | { | |
327 | struct firedtv *fdtv = dev_get_drvdata(&unit->device); | |
328 | ||
329 | if (fdtv->isochannel >= 0) | |
330 | cmp_establish_pp_connection(fdtv, fdtv->subunit, | |
331 | fdtv->isochannel); | |
332 | } | |
333 | ||
92374e88 SR |
334 | #define MATCH_FLAGS (IEEE1394_MATCH_VENDOR_ID | IEEE1394_MATCH_MODEL_ID | \ |
335 | IEEE1394_MATCH_SPECIFIER_ID | IEEE1394_MATCH_VERSION) | |
336 | ||
337 | #define DIGITAL_EVERYWHERE_OUI 0x001287 | |
338 | #define AVC_UNIT_SPEC_ID_ENTRY 0x00a02d | |
339 | #define AVC_SW_VERSION_ENTRY 0x010001 | |
340 | ||
341 | static const struct ieee1394_device_id fdtv_id_table[] = { | |
342 | { | |
343 | /* FloppyDTV S/CI and FloppyDTV S2 */ | |
344 | .match_flags = MATCH_FLAGS, | |
345 | .vendor_id = DIGITAL_EVERYWHERE_OUI, | |
346 | .model_id = 0x000024, | |
347 | .specifier_id = AVC_UNIT_SPEC_ID_ENTRY, | |
348 | .version = AVC_SW_VERSION_ENTRY, | |
349 | }, { | |
350 | /* FloppyDTV T/CI */ | |
351 | .match_flags = MATCH_FLAGS, | |
352 | .vendor_id = DIGITAL_EVERYWHERE_OUI, | |
353 | .model_id = 0x000025, | |
354 | .specifier_id = AVC_UNIT_SPEC_ID_ENTRY, | |
355 | .version = AVC_SW_VERSION_ENTRY, | |
356 | }, { | |
357 | /* FloppyDTV C/CI */ | |
358 | .match_flags = MATCH_FLAGS, | |
359 | .vendor_id = DIGITAL_EVERYWHERE_OUI, | |
360 | .model_id = 0x000026, | |
361 | .specifier_id = AVC_UNIT_SPEC_ID_ENTRY, | |
362 | .version = AVC_SW_VERSION_ENTRY, | |
363 | }, { | |
364 | /* FireDTV S/CI and FloppyDTV S2 */ | |
365 | .match_flags = MATCH_FLAGS, | |
366 | .vendor_id = DIGITAL_EVERYWHERE_OUI, | |
367 | .model_id = 0x000034, | |
368 | .specifier_id = AVC_UNIT_SPEC_ID_ENTRY, | |
369 | .version = AVC_SW_VERSION_ENTRY, | |
370 | }, { | |
371 | /* FireDTV T/CI */ | |
372 | .match_flags = MATCH_FLAGS, | |
373 | .vendor_id = DIGITAL_EVERYWHERE_OUI, | |
374 | .model_id = 0x000035, | |
375 | .specifier_id = AVC_UNIT_SPEC_ID_ENTRY, | |
376 | .version = AVC_SW_VERSION_ENTRY, | |
377 | }, { | |
378 | /* FireDTV C/CI */ | |
379 | .match_flags = MATCH_FLAGS, | |
380 | .vendor_id = DIGITAL_EVERYWHERE_OUI, | |
381 | .model_id = 0x000036, | |
382 | .specifier_id = AVC_UNIT_SPEC_ID_ENTRY, | |
383 | .version = AVC_SW_VERSION_ENTRY, | |
384 | }, {} | |
385 | }; | |
386 | MODULE_DEVICE_TABLE(ieee1394, fdtv_id_table); | |
387 | ||
87918334 SR |
388 | static struct fw_driver fdtv_driver = { |
389 | .driver = { | |
390 | .owner = THIS_MODULE, | |
391 | .name = "firedtv", | |
392 | .bus = &fw_bus_type, | |
87918334 | 393 | }, |
94a87157 | 394 | .probe = node_probe, |
87918334 | 395 | .update = node_update, |
94a87157 | 396 | .remove = node_remove, |
87918334 SR |
397 | .id_table = fdtv_id_table, |
398 | }; | |
399 | ||
92374e88 | 400 | static int __init fdtv_init(void) |
87918334 SR |
401 | { |
402 | int ret; | |
403 | ||
404 | ret = fw_core_add_address_handler(&fcp_handler, &fcp_region); | |
405 | if (ret < 0) | |
406 | return ret; | |
407 | ||
92374e88 SR |
408 | ret = driver_register(&fdtv_driver.driver); |
409 | if (ret < 0) | |
410 | fw_core_remove_address_handler(&fcp_handler); | |
411 | ||
412 | return ret; | |
87918334 SR |
413 | } |
414 | ||
92374e88 | 415 | static void __exit fdtv_exit(void) |
87918334 SR |
416 | { |
417 | driver_unregister(&fdtv_driver.driver); | |
418 | fw_core_remove_address_handler(&fcp_handler); | |
419 | } | |
92374e88 SR |
420 | |
421 | module_init(fdtv_init); | |
422 | module_exit(fdtv_exit); | |
423 | ||
424 | MODULE_AUTHOR("Andreas Monitzer <andy@monitzer.com>"); | |
425 | MODULE_AUTHOR("Ben Backx <ben@bbackx.com>"); | |
426 | MODULE_DESCRIPTION("FireDTV DVB Driver"); | |
427 | MODULE_LICENSE("GPL"); | |
428 | MODULE_SUPPORTED_DEVICE("FireDTV DVB"); |