aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/firewire/fw-iso.c
diff options
context:
space:
mode:
authorJay Fenlason, Stefan Richter <stefanr@s5r6.in-berlin.de>2009-01-04 10:23:29 -0500
committerStefan Richter <stefanr@s5r6.in-berlin.de>2009-03-24 15:56:43 -0400
commitb1bda4cdc2037447bd66753bf5ccab66d91b0b59 (patch)
tree6aae47fb85125c15150d6d306354de5deb1e316f /drivers/firewire/fw-iso.c
parentb769bd17656f991c5588c676376e5ec77d25997a (diff)
firewire: cdev: add ioctls for isochronous resource management
Based on Date: Tue, 18 Nov 2008 11:41:27 -0500 From: Jay Fenlason <fenlason@redhat.com> Subject: [Patch V4] Add ISO resource management support with several changes to the ABI and implementation. Only the part of the ABI which enables auto-reallocation and auto-deallocation is included here. This implements ioctls for kernel-assisted allocation of isochronous channels and isochronous bandwidth. The benefits are: - The client does not have to have write access to the /dev/fw* device corresponding to the IRM. - The client does not have to perform reallocation after bus resets. - Channel and bandwidth are deallocated by the kernel if the file is closed before the client deallocated the resources. Thus resources are released even if the client crashes. It is anticipated that future in-kernel code (firewire-core IRM code; the firewire port of firedtv), will use the fw-iso.c portions of this code too. Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de> Tested-by: David Moore <dcm@acm.org>
Diffstat (limited to 'drivers/firewire/fw-iso.c')
-rw-r--r--drivers/firewire/fw-iso.c176
1 files changed, 170 insertions, 6 deletions
diff --git a/drivers/firewire/fw-iso.c b/drivers/firewire/fw-iso.c
index 39f3bacee404..a7b57b253b06 100644
--- a/drivers/firewire/fw-iso.c
+++ b/drivers/firewire/fw-iso.c
@@ -1,5 +1,7 @@
1/* 1/*
2 * Isochronous IO functionality 2 * Isochronous I/O functionality:
3 * - Isochronous DMA context management
4 * - Isochronous bus resource management (channels, bandwidth), client side
3 * 5 *
4 * Copyright (C) 2006 Kristian Hoegsberg <krh@bitplanet.net> 6 * Copyright (C) 2006 Kristian Hoegsberg <krh@bitplanet.net>
5 * 7 *
@@ -18,15 +20,20 @@
18 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 20 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19 */ 21 */
20 22
21#include <linux/kernel.h>
22#include <linux/module.h>
23#include <linux/dma-mapping.h> 23#include <linux/dma-mapping.h>
24#include <linux/vmalloc.h> 24#include <linux/errno.h>
25#include <linux/firewire-constants.h>
26#include <linux/kernel.h>
25#include <linux/mm.h> 27#include <linux/mm.h>
28#include <linux/spinlock.h>
29#include <linux/vmalloc.h>
26 30
27#include "fw-transaction.h"
28#include "fw-topology.h" 31#include "fw-topology.h"
29#include "fw-device.h" 32#include "fw-transaction.h"
33
34/*
35 * Isochronous DMA context management
36 */
30 37
31int fw_iso_buffer_init(struct fw_iso_buffer *buffer, struct fw_card *card, 38int fw_iso_buffer_init(struct fw_iso_buffer *buffer, struct fw_card *card,
32 int page_count, enum dma_data_direction direction) 39 int page_count, enum dma_data_direction direction)
@@ -153,3 +160,160 @@ int fw_iso_context_stop(struct fw_iso_context *ctx)
153{ 160{
154 return ctx->card->driver->stop_iso(ctx); 161 return ctx->card->driver->stop_iso(ctx);
155} 162}
163
164/*
165 * Isochronous bus resource management (channels, bandwidth), client side
166 */
167
168static int manage_bandwidth(struct fw_card *card, int irm_id, int generation,
169 int bandwidth, bool allocate)
170{
171 __be32 data[2];
172 int try, new, old = allocate ? BANDWIDTH_AVAILABLE_INITIAL : 0;
173
174 /*
175 * On a 1394a IRM with low contention, try < 1 is enough.
176 * On a 1394-1995 IRM, we need at least try < 2.
177 * Let's just do try < 5.
178 */
179 for (try = 0; try < 5; try++) {
180 new = allocate ? old - bandwidth : old + bandwidth;
181 if (new < 0 || new > BANDWIDTH_AVAILABLE_INITIAL)
182 break;
183
184 data[0] = cpu_to_be32(old);
185 data[1] = cpu_to_be32(new);
186 switch (fw_run_transaction(card, TCODE_LOCK_COMPARE_SWAP,
187 irm_id, generation, SCODE_100,
188 CSR_REGISTER_BASE + CSR_BANDWIDTH_AVAILABLE,
189 data, sizeof(data))) {
190 case RCODE_GENERATION:
191 /* A generation change frees all bandwidth. */
192 return allocate ? -EAGAIN : bandwidth;
193
194 case RCODE_COMPLETE:
195 if (be32_to_cpup(data) == old)
196 return bandwidth;
197
198 old = be32_to_cpup(data);
199 /* Fall through. */
200 }
201 }
202
203 return -EIO;
204}
205
206static int manage_channel(struct fw_card *card, int irm_id, int generation,
207 __be32 channels_mask, u64 offset, bool allocate)
208{
209 __be32 data[2], c, old = allocate ? cpu_to_be32(~0) : 0;
210 int i, retry = 5;
211
212 for (i = 0; i < 32; i++) {
213 c = cpu_to_be32(1 << (31 - i));
214 if (!(channels_mask & c))
215 continue;
216
217 if (allocate == !(old & c))
218 continue;
219
220 data[0] = old;
221 data[1] = old ^ c;
222 switch (fw_run_transaction(card, TCODE_LOCK_COMPARE_SWAP,
223 irm_id, generation, SCODE_100,
224 offset, data, sizeof(data))) {
225 case RCODE_GENERATION:
226 /* A generation change frees all channels. */
227 return allocate ? -EAGAIN : i;
228
229 case RCODE_COMPLETE:
230 if (data[0] == old)
231 return i;
232
233 old = data[0];
234
235 /* Is the IRM 1394a-2000 compliant? */
236 if ((data[0] & c) != (data[1] & c))
237 continue;
238
239 /* 1394-1995 IRM, fall through to retry. */
240 default:
241 if (retry--)
242 i--;
243 }
244 }
245
246 return -EIO;
247}
248
249static void deallocate_channel(struct fw_card *card, int irm_id,
250 int generation, int channel)
251{
252 __be32 mask;
253 u64 offset;
254
255 mask = channel < 32 ? cpu_to_be32(1 << (31 - channel)) :
256 cpu_to_be32(1 << (63 - channel));
257 offset = channel < 32 ? CSR_REGISTER_BASE + CSR_CHANNELS_AVAILABLE_HI :
258 CSR_REGISTER_BASE + CSR_CHANNELS_AVAILABLE_LO;
259
260 manage_channel(card, irm_id, generation, mask, offset, false);
261}
262
263/**
264 * fw_iso_resource_manage - Allocate or deallocate a channel and/or bandwidth
265 *
266 * In parameters: card, generation, channels_mask, bandwidth, allocate
267 * Out parameters: channel, bandwidth
268 * This function blocks (sleeps) during communication with the IRM.
269 * Allocates or deallocates at most one channel out of channels_mask.
270 *
271 * Returns channel < 0 if no channel was allocated or deallocated.
272 * Returns bandwidth = 0 if no bandwidth was allocated or deallocated.
273 *
274 * If generation is stale, deallocations succeed but allocations fail with
275 * channel = -EAGAIN.
276 *
277 * If channel (de)allocation fails, bandwidth (de)allocation fails too.
278 * If bandwidth allocation fails, no channel will be allocated either.
279 * If bandwidth deallocation fails, channel deallocation may still have been
280 * successful.
281 */
282void fw_iso_resource_manage(struct fw_card *card, int generation,
283 u64 channels_mask, int *channel, int *bandwidth,
284 bool allocate)
285{
286 __be32 channels_hi = cpu_to_be32(channels_mask >> 32);
287 __be32 channels_lo = cpu_to_be32(channels_mask);
288 int irm_id, ret, c = -EINVAL;
289
290 spin_lock_irq(&card->lock);
291 irm_id = card->irm_node->node_id;
292 spin_unlock_irq(&card->lock);
293
294 if (channels_hi)
295 c = manage_channel(card, irm_id, generation, channels_hi,
296 CSR_REGISTER_BASE + CSR_CHANNELS_AVAILABLE_HI, allocate);
297 if (channels_lo && c < 0) {
298 c = manage_channel(card, irm_id, generation, channels_lo,
299 CSR_REGISTER_BASE + CSR_CHANNELS_AVAILABLE_LO, allocate);
300 if (c >= 0)
301 c += 32;
302 }
303 *channel = c;
304
305 if (channels_mask != 0 && c < 0)
306 *bandwidth = 0;
307
308 if (*bandwidth == 0)
309 return;
310
311 ret = manage_bandwidth(card, irm_id, generation, *bandwidth, allocate);
312 if (ret < 0)
313 *bandwidth = 0;
314
315 if (ret < 0 && c >= 0 && allocate) {
316 deallocate_channel(card, irm_id, generation, c);
317 *channel = ret;
318 }
319}