diff options
-rw-r--r-- | drivers/firewire/fw-cdev.c | 215 | ||||
-rw-r--r-- | drivers/firewire/fw-iso.c | 176 | ||||
-rw-r--r-- | drivers/firewire/fw-transaction.h | 4 | ||||
-rw-r--r-- | include/linux/firewire-cdev.h | 100 |
4 files changed, 475 insertions, 20 deletions
diff --git a/drivers/firewire/fw-cdev.c b/drivers/firewire/fw-cdev.c index 4c33b51b735a..a227853aa1e2 100644 --- a/drivers/firewire/fw-cdev.c +++ b/drivers/firewire/fw-cdev.c | |||
@@ -24,6 +24,7 @@ | |||
24 | #include <linux/errno.h> | 24 | #include <linux/errno.h> |
25 | #include <linux/firewire-cdev.h> | 25 | #include <linux/firewire-cdev.h> |
26 | #include <linux/idr.h> | 26 | #include <linux/idr.h> |
27 | #include <linux/jiffies.h> | ||
27 | #include <linux/kernel.h> | 28 | #include <linux/kernel.h> |
28 | #include <linux/kref.h> | 29 | #include <linux/kref.h> |
29 | #include <linux/mm.h> | 30 | #include <linux/mm.h> |
@@ -35,6 +36,7 @@ | |||
35 | #include <linux/time.h> | 36 | #include <linux/time.h> |
36 | #include <linux/vmalloc.h> | 37 | #include <linux/vmalloc.h> |
37 | #include <linux/wait.h> | 38 | #include <linux/wait.h> |
39 | #include <linux/workqueue.h> | ||
38 | 40 | ||
39 | #include <asm/system.h> | 41 | #include <asm/system.h> |
40 | #include <asm/uaccess.h> | 42 | #include <asm/uaccess.h> |
@@ -114,6 +116,21 @@ struct descriptor_resource { | |||
114 | u32 data[0]; | 116 | u32 data[0]; |
115 | }; | 117 | }; |
116 | 118 | ||
119 | struct iso_resource { | ||
120 | struct client_resource resource; | ||
121 | struct client *client; | ||
122 | /* Schedule work and access todo only with client->lock held. */ | ||
123 | struct delayed_work work; | ||
124 | enum {ISO_RES_ALLOC, ISO_RES_REALLOC, ISO_RES_DEALLOC,} todo; | ||
125 | int generation; | ||
126 | u64 channels; | ||
127 | s32 bandwidth; | ||
128 | struct iso_resource_event *e_alloc, *e_dealloc; | ||
129 | }; | ||
130 | |||
131 | static void schedule_iso_resource(struct iso_resource *); | ||
132 | static void release_iso_resource(struct client *, struct client_resource *); | ||
133 | |||
117 | /* | 134 | /* |
118 | * dequeue_event() just kfree()'s the event, so the event has to be | 135 | * dequeue_event() just kfree()'s the event, so the event has to be |
119 | * the first field in a struct XYZ_event. | 136 | * the first field in a struct XYZ_event. |
@@ -145,6 +162,11 @@ struct iso_interrupt_event { | |||
145 | struct fw_cdev_event_iso_interrupt interrupt; | 162 | struct fw_cdev_event_iso_interrupt interrupt; |
146 | }; | 163 | }; |
147 | 164 | ||
165 | struct iso_resource_event { | ||
166 | struct event event; | ||
167 | struct fw_cdev_event_iso_resource resource; | ||
168 | }; | ||
169 | |||
148 | static inline void __user *u64_to_uptr(__u64 value) | 170 | static inline void __user *u64_to_uptr(__u64 value) |
149 | { | 171 | { |
150 | return (void __user *)(unsigned long)value; | 172 | return (void __user *)(unsigned long)value; |
@@ -290,6 +312,16 @@ static void for_each_client(struct fw_device *device, | |||
290 | mutex_unlock(&device->client_list_mutex); | 312 | mutex_unlock(&device->client_list_mutex); |
291 | } | 313 | } |
292 | 314 | ||
315 | static int schedule_reallocations(int id, void *p, void *data) | ||
316 | { | ||
317 | struct client_resource *r = p; | ||
318 | |||
319 | if (r->release == release_iso_resource) | ||
320 | schedule_iso_resource(container_of(r, | ||
321 | struct iso_resource, resource)); | ||
322 | return 0; | ||
323 | } | ||
324 | |||
293 | static void queue_bus_reset_event(struct client *client) | 325 | static void queue_bus_reset_event(struct client *client) |
294 | { | 326 | { |
295 | struct bus_reset_event *e; | 327 | struct bus_reset_event *e; |
@@ -304,6 +336,10 @@ static void queue_bus_reset_event(struct client *client) | |||
304 | 336 | ||
305 | queue_event(client, &e->event, | 337 | queue_event(client, &e->event, |
306 | &e->reset, sizeof(e->reset), NULL, 0); | 338 | &e->reset, sizeof(e->reset), NULL, 0); |
339 | |||
340 | spin_lock_irq(&client->lock); | ||
341 | idr_for_each(&client->resource_idr, schedule_reallocations, client); | ||
342 | spin_unlock_irq(&client->lock); | ||
307 | } | 343 | } |
308 | 344 | ||
309 | void fw_device_cdev_update(struct fw_device *device) | 345 | void fw_device_cdev_update(struct fw_device *device) |
@@ -376,8 +412,12 @@ static int add_client_resource(struct client *client, | |||
376 | else | 412 | else |
377 | ret = idr_get_new(&client->resource_idr, resource, | 413 | ret = idr_get_new(&client->resource_idr, resource, |
378 | &resource->handle); | 414 | &resource->handle); |
379 | if (ret >= 0) | 415 | if (ret >= 0) { |
380 | client_get(client); | 416 | client_get(client); |
417 | if (resource->release == release_iso_resource) | ||
418 | schedule_iso_resource(container_of(resource, | ||
419 | struct iso_resource, resource)); | ||
420 | } | ||
381 | spin_unlock_irqrestore(&client->lock, flags); | 421 | spin_unlock_irqrestore(&client->lock, flags); |
382 | 422 | ||
383 | if (ret == -EAGAIN) | 423 | if (ret == -EAGAIN) |
@@ -970,6 +1010,177 @@ static int ioctl_get_cycle_timer(struct client *client, void *buffer) | |||
970 | return 0; | 1010 | return 0; |
971 | } | 1011 | } |
972 | 1012 | ||
1013 | static void iso_resource_work(struct work_struct *work) | ||
1014 | { | ||
1015 | struct iso_resource_event *e; | ||
1016 | struct iso_resource *r = | ||
1017 | container_of(work, struct iso_resource, work.work); | ||
1018 | struct client *client = r->client; | ||
1019 | int generation, channel, bandwidth, todo; | ||
1020 | bool skip, free, success; | ||
1021 | |||
1022 | spin_lock_irq(&client->lock); | ||
1023 | generation = client->device->generation; | ||
1024 | todo = r->todo; | ||
1025 | /* Allow 1000ms grace period for other reallocations. */ | ||
1026 | if (todo == ISO_RES_ALLOC && | ||
1027 | time_is_after_jiffies(client->device->card->reset_jiffies + HZ)) { | ||
1028 | if (schedule_delayed_work(&r->work, DIV_ROUND_UP(HZ, 3))) | ||
1029 | client_get(client); | ||
1030 | skip = true; | ||
1031 | } else { | ||
1032 | /* We could be called twice within the same generation. */ | ||
1033 | skip = todo == ISO_RES_REALLOC && | ||
1034 | r->generation == generation; | ||
1035 | } | ||
1036 | free = todo == ISO_RES_DEALLOC; | ||
1037 | r->generation = generation; | ||
1038 | spin_unlock_irq(&client->lock); | ||
1039 | |||
1040 | if (skip) | ||
1041 | goto out; | ||
1042 | |||
1043 | bandwidth = r->bandwidth; | ||
1044 | |||
1045 | fw_iso_resource_manage(client->device->card, generation, | ||
1046 | r->channels, &channel, &bandwidth, | ||
1047 | todo == ISO_RES_ALLOC || todo == ISO_RES_REALLOC); | ||
1048 | /* | ||
1049 | * Is this generation outdated already? As long as this resource sticks | ||
1050 | * in the idr, it will be scheduled again for a newer generation or at | ||
1051 | * shutdown. | ||
1052 | */ | ||
1053 | if (channel == -EAGAIN && | ||
1054 | (todo == ISO_RES_ALLOC || todo == ISO_RES_REALLOC)) | ||
1055 | goto out; | ||
1056 | |||
1057 | success = channel >= 0 || bandwidth > 0; | ||
1058 | |||
1059 | spin_lock_irq(&client->lock); | ||
1060 | /* | ||
1061 | * Transit from allocation to reallocation, except if the client | ||
1062 | * requested deallocation in the meantime. | ||
1063 | */ | ||
1064 | if (r->todo == ISO_RES_ALLOC) | ||
1065 | r->todo = ISO_RES_REALLOC; | ||
1066 | /* | ||
1067 | * Allocation or reallocation failure? Pull this resource out of the | ||
1068 | * idr and prepare for deletion, unless the client is shutting down. | ||
1069 | */ | ||
1070 | if (r->todo == ISO_RES_REALLOC && !success && | ||
1071 | !client->in_shutdown && | ||
1072 | idr_find(&client->resource_idr, r->resource.handle)) { | ||
1073 | idr_remove(&client->resource_idr, r->resource.handle); | ||
1074 | client_put(client); | ||
1075 | free = true; | ||
1076 | } | ||
1077 | spin_unlock_irq(&client->lock); | ||
1078 | |||
1079 | if (todo == ISO_RES_ALLOC && channel >= 0) | ||
1080 | r->channels = 1ULL << (63 - channel); | ||
1081 | |||
1082 | if (todo == ISO_RES_REALLOC && success) | ||
1083 | goto out; | ||
1084 | |||
1085 | if (todo == ISO_RES_ALLOC) { | ||
1086 | e = r->e_alloc; | ||
1087 | r->e_alloc = NULL; | ||
1088 | } else { | ||
1089 | e = r->e_dealloc; | ||
1090 | r->e_dealloc = NULL; | ||
1091 | } | ||
1092 | e->resource.handle = r->resource.handle; | ||
1093 | e->resource.channel = channel; | ||
1094 | e->resource.bandwidth = bandwidth; | ||
1095 | |||
1096 | queue_event(client, &e->event, | ||
1097 | &e->resource, sizeof(e->resource), NULL, 0); | ||
1098 | |||
1099 | if (free) { | ||
1100 | cancel_delayed_work(&r->work); | ||
1101 | kfree(r->e_alloc); | ||
1102 | kfree(r->e_dealloc); | ||
1103 | kfree(r); | ||
1104 | } | ||
1105 | out: | ||
1106 | client_put(client); | ||
1107 | } | ||
1108 | |||
1109 | static void schedule_iso_resource(struct iso_resource *r) | ||
1110 | { | ||
1111 | if (schedule_delayed_work(&r->work, 0)) | ||
1112 | client_get(r->client); | ||
1113 | } | ||
1114 | |||
1115 | static void release_iso_resource(struct client *client, | ||
1116 | struct client_resource *resource) | ||
1117 | { | ||
1118 | struct iso_resource *r = | ||
1119 | container_of(resource, struct iso_resource, resource); | ||
1120 | |||
1121 | spin_lock_irq(&client->lock); | ||
1122 | r->todo = ISO_RES_DEALLOC; | ||
1123 | schedule_iso_resource(r); | ||
1124 | spin_unlock_irq(&client->lock); | ||
1125 | } | ||
1126 | |||
1127 | static int ioctl_allocate_iso_resource(struct client *client, void *buffer) | ||
1128 | { | ||
1129 | struct fw_cdev_allocate_iso_resource *request = buffer; | ||
1130 | struct iso_resource_event *e1, *e2; | ||
1131 | struct iso_resource *r; | ||
1132 | int ret; | ||
1133 | |||
1134 | if ((request->channels == 0 && request->bandwidth == 0) || | ||
1135 | request->bandwidth > BANDWIDTH_AVAILABLE_INITIAL || | ||
1136 | request->bandwidth < 0) | ||
1137 | return -EINVAL; | ||
1138 | |||
1139 | r = kmalloc(sizeof(*r), GFP_KERNEL); | ||
1140 | e1 = kmalloc(sizeof(*e1), GFP_KERNEL); | ||
1141 | e2 = kmalloc(sizeof(*e2), GFP_KERNEL); | ||
1142 | if (r == NULL || e1 == NULL || e2 == NULL) { | ||
1143 | ret = -ENOMEM; | ||
1144 | goto fail; | ||
1145 | } | ||
1146 | |||
1147 | INIT_DELAYED_WORK(&r->work, iso_resource_work); | ||
1148 | r->client = client; | ||
1149 | r->todo = ISO_RES_ALLOC; | ||
1150 | r->generation = -1; | ||
1151 | r->channels = request->channels; | ||
1152 | r->bandwidth = request->bandwidth; | ||
1153 | r->e_alloc = e1; | ||
1154 | r->e_dealloc = e2; | ||
1155 | |||
1156 | e1->resource.closure = request->closure; | ||
1157 | e1->resource.type = FW_CDEV_EVENT_ISO_RESOURCE_ALLOCATED; | ||
1158 | e2->resource.closure = request->closure; | ||
1159 | e2->resource.type = FW_CDEV_EVENT_ISO_RESOURCE_DEALLOCATED; | ||
1160 | |||
1161 | r->resource.release = release_iso_resource; | ||
1162 | ret = add_client_resource(client, &r->resource, GFP_KERNEL); | ||
1163 | if (ret < 0) | ||
1164 | goto fail; | ||
1165 | request->handle = r->resource.handle; | ||
1166 | |||
1167 | return 0; | ||
1168 | fail: | ||
1169 | kfree(r); | ||
1170 | kfree(e1); | ||
1171 | kfree(e2); | ||
1172 | |||
1173 | return ret; | ||
1174 | } | ||
1175 | |||
1176 | static int ioctl_deallocate_iso_resource(struct client *client, void *buffer) | ||
1177 | { | ||
1178 | struct fw_cdev_deallocate *request = buffer; | ||
1179 | |||
1180 | return release_client_resource(client, request->handle, | ||
1181 | release_iso_resource, NULL); | ||
1182 | } | ||
1183 | |||
973 | static int (* const ioctl_handlers[])(struct client *client, void *buffer) = { | 1184 | static int (* const ioctl_handlers[])(struct client *client, void *buffer) = { |
974 | ioctl_get_info, | 1185 | ioctl_get_info, |
975 | ioctl_send_request, | 1186 | ioctl_send_request, |
@@ -984,6 +1195,8 @@ static int (* const ioctl_handlers[])(struct client *client, void *buffer) = { | |||
984 | ioctl_start_iso, | 1195 | ioctl_start_iso, |
985 | ioctl_stop_iso, | 1196 | ioctl_stop_iso, |
986 | ioctl_get_cycle_timer, | 1197 | ioctl_get_cycle_timer, |
1198 | ioctl_allocate_iso_resource, | ||
1199 | ioctl_deallocate_iso_resource, | ||
987 | }; | 1200 | }; |
988 | 1201 | ||
989 | static int dispatch_ioctl(struct client *client, | 1202 | static int dispatch_ioctl(struct client *client, |
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 | ||
31 | int fw_iso_buffer_init(struct fw_iso_buffer *buffer, struct fw_card *card, | 38 | int 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 | |||
168 | static 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 | |||
206 | static 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 | |||
249 | static 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 | */ | ||
282 | void 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 | } | ||
diff --git a/drivers/firewire/fw-transaction.h b/drivers/firewire/fw-transaction.h index 48e88d53998b..212a10293828 100644 --- a/drivers/firewire/fw-transaction.h +++ b/drivers/firewire/fw-transaction.h | |||
@@ -82,6 +82,7 @@ | |||
82 | #define CSR_SPEED_MAP 0x2000 | 82 | #define CSR_SPEED_MAP 0x2000 |
83 | #define CSR_SPEED_MAP_END 0x3000 | 83 | #define CSR_SPEED_MAP_END 0x3000 |
84 | 84 | ||
85 | #define BANDWIDTH_AVAILABLE_INITIAL 4915 | ||
85 | #define BROADCAST_CHANNEL_INITIAL (1 << 31 | 31) | 86 | #define BROADCAST_CHANNEL_INITIAL (1 << 31 | 31) |
86 | #define BROADCAST_CHANNEL_VALID (1 << 30) | 87 | #define BROADCAST_CHANNEL_VALID (1 << 30) |
87 | 88 | ||
@@ -343,6 +344,9 @@ int fw_iso_context_start(struct fw_iso_context *ctx, | |||
343 | int fw_iso_context_stop(struct fw_iso_context *ctx); | 344 | int fw_iso_context_stop(struct fw_iso_context *ctx); |
344 | void fw_iso_context_destroy(struct fw_iso_context *ctx); | 345 | void fw_iso_context_destroy(struct fw_iso_context *ctx); |
345 | 346 | ||
347 | void fw_iso_resource_manage(struct fw_card *card, int generation, | ||
348 | u64 channels_mask, int *channel, int *bandwidth, bool allocate); | ||
349 | |||
346 | struct fw_card_driver { | 350 | struct fw_card_driver { |
347 | /* | 351 | /* |
348 | * Enable the given card with the given initial config rom. | 352 | * Enable the given card with the given initial config rom. |
diff --git a/include/linux/firewire-cdev.h b/include/linux/firewire-cdev.h index 86c8ff5326f9..25b96dd0574f 100644 --- a/include/linux/firewire-cdev.h +++ b/include/linux/firewire-cdev.h | |||
@@ -25,10 +25,12 @@ | |||
25 | #include <linux/types.h> | 25 | #include <linux/types.h> |
26 | #include <linux/firewire-constants.h> | 26 | #include <linux/firewire-constants.h> |
27 | 27 | ||
28 | #define FW_CDEV_EVENT_BUS_RESET 0x00 | 28 | #define FW_CDEV_EVENT_BUS_RESET 0x00 |
29 | #define FW_CDEV_EVENT_RESPONSE 0x01 | 29 | #define FW_CDEV_EVENT_RESPONSE 0x01 |
30 | #define FW_CDEV_EVENT_REQUEST 0x02 | 30 | #define FW_CDEV_EVENT_REQUEST 0x02 |
31 | #define FW_CDEV_EVENT_ISO_INTERRUPT 0x03 | 31 | #define FW_CDEV_EVENT_ISO_INTERRUPT 0x03 |
32 | #define FW_CDEV_EVENT_ISO_RESOURCE_ALLOCATED 0x04 | ||
33 | #define FW_CDEV_EVENT_ISO_RESOURCE_DEALLOCATED 0x05 | ||
32 | 34 | ||
33 | /** | 35 | /** |
34 | * struct fw_cdev_event_common - Common part of all fw_cdev_event_ types | 36 | * struct fw_cdev_event_common - Common part of all fw_cdev_event_ types |
@@ -147,12 +149,46 @@ struct fw_cdev_event_iso_interrupt { | |||
147 | }; | 149 | }; |
148 | 150 | ||
149 | /** | 151 | /** |
152 | * struct fw_cdev_event_iso_resource - Iso resources were allocated or freed | ||
153 | * @closure: See &fw_cdev_event_common; | ||
154 | * set by %FW_CDEV_IOC_ALLOCATE_ISO_RESOURCE ioctl | ||
155 | * @type: %FW_CDEV_EVENT_ISO_RESOURCE_ALLOCATED or | ||
156 | * %FW_CDEV_EVENT_ISO_RESOURCE_DEALLOCATED | ||
157 | * @handle: Reference by which an allocated resource can be deallocated | ||
158 | * @channel: Isochronous channel which was (de)allocated, if any | ||
159 | * @bandwidth: Bandwidth allocation units which were (de)allocated, if any | ||
160 | * @channels_available: Last known availability of channels | ||
161 | * @bandwidth_available: Last known availability of bandwidth | ||
162 | * | ||
163 | * An %FW_CDEV_EVENT_ISO_RESOURCE_ALLOCATED event is sent after an isochronous | ||
164 | * resource was allocated at the IRM. The client has to check @channel and | ||
165 | * @bandwidth for whether the allocation actually succeeded. | ||
166 | * | ||
167 | * @channel is <0 if no channel was allocated. | ||
168 | * @bandwidth is 0 if no bandwidth was allocated. | ||
169 | * | ||
170 | * An %FW_CDEV_EVENT_ISO_RESOURCE_DEALLOCATED event is sent after an isochronous | ||
171 | * resource was deallocated at the IRM. It is also sent when automatic | ||
172 | * reallocation after a bus reset failed. | ||
173 | */ | ||
174 | struct fw_cdev_event_iso_resource { | ||
175 | __u64 closure; | ||
176 | __u32 type; | ||
177 | __u32 handle; | ||
178 | __s32 channel; | ||
179 | __s32 bandwidth; | ||
180 | }; | ||
181 | |||
182 | /** | ||
150 | * union fw_cdev_event - Convenience union of fw_cdev_event_ types | 183 | * union fw_cdev_event - Convenience union of fw_cdev_event_ types |
151 | * @common: Valid for all types | 184 | * @common: Valid for all types |
152 | * @bus_reset: Valid if @common.type == %FW_CDEV_EVENT_BUS_RESET | 185 | * @bus_reset: Valid if @common.type == %FW_CDEV_EVENT_BUS_RESET |
153 | * @response: Valid if @common.type == %FW_CDEV_EVENT_RESPONSE | 186 | * @response: Valid if @common.type == %FW_CDEV_EVENT_RESPONSE |
154 | * @request: Valid if @common.type == %FW_CDEV_EVENT_REQUEST | 187 | * @request: Valid if @common.type == %FW_CDEV_EVENT_REQUEST |
155 | * @iso_interrupt: Valid if @common.type == %FW_CDEV_EVENT_ISO_INTERRUPT | 188 | * @iso_interrupt: Valid if @common.type == %FW_CDEV_EVENT_ISO_INTERRUPT |
189 | * @iso_resource: Valid if @common.type == | ||
190 | * %FW_CDEV_EVENT_ISO_RESOURCE_ALLOCATED or | ||
191 | * %FW_CDEV_EVENT_ISO_RESOURCE_DEALLOCATED | ||
156 | * | 192 | * |
157 | * Convenience union for userspace use. Events could be read(2) into an | 193 | * Convenience union for userspace use. Events could be read(2) into an |
158 | * appropriately aligned char buffer and then cast to this union for further | 194 | * appropriately aligned char buffer and then cast to this union for further |
@@ -163,13 +199,15 @@ struct fw_cdev_event_iso_interrupt { | |||
163 | * not fit will be discarded so that the next read(2) will return a new event. | 199 | * not fit will be discarded so that the next read(2) will return a new event. |
164 | */ | 200 | */ |
165 | union fw_cdev_event { | 201 | union fw_cdev_event { |
166 | struct fw_cdev_event_common common; | 202 | struct fw_cdev_event_common common; |
167 | struct fw_cdev_event_bus_reset bus_reset; | 203 | struct fw_cdev_event_bus_reset bus_reset; |
168 | struct fw_cdev_event_response response; | 204 | struct fw_cdev_event_response response; |
169 | struct fw_cdev_event_request request; | 205 | struct fw_cdev_event_request request; |
170 | struct fw_cdev_event_iso_interrupt iso_interrupt; | 206 | struct fw_cdev_event_iso_interrupt iso_interrupt; |
207 | struct fw_cdev_event_iso_resource iso_resource; | ||
171 | }; | 208 | }; |
172 | 209 | ||
210 | /* available since kernel version 2.6.22 */ | ||
173 | #define FW_CDEV_IOC_GET_INFO _IOWR('#', 0x00, struct fw_cdev_get_info) | 211 | #define FW_CDEV_IOC_GET_INFO _IOWR('#', 0x00, struct fw_cdev_get_info) |
174 | #define FW_CDEV_IOC_SEND_REQUEST _IOW('#', 0x01, struct fw_cdev_send_request) | 212 | #define FW_CDEV_IOC_SEND_REQUEST _IOW('#', 0x01, struct fw_cdev_send_request) |
175 | #define FW_CDEV_IOC_ALLOCATE _IOWR('#', 0x02, struct fw_cdev_allocate) | 213 | #define FW_CDEV_IOC_ALLOCATE _IOWR('#', 0x02, struct fw_cdev_allocate) |
@@ -178,13 +216,18 @@ union fw_cdev_event { | |||
178 | #define FW_CDEV_IOC_INITIATE_BUS_RESET _IOW('#', 0x05, struct fw_cdev_initiate_bus_reset) | 216 | #define FW_CDEV_IOC_INITIATE_BUS_RESET _IOW('#', 0x05, struct fw_cdev_initiate_bus_reset) |
179 | #define FW_CDEV_IOC_ADD_DESCRIPTOR _IOWR('#', 0x06, struct fw_cdev_add_descriptor) | 217 | #define FW_CDEV_IOC_ADD_DESCRIPTOR _IOWR('#', 0x06, struct fw_cdev_add_descriptor) |
180 | #define FW_CDEV_IOC_REMOVE_DESCRIPTOR _IOW('#', 0x07, struct fw_cdev_remove_descriptor) | 218 | #define FW_CDEV_IOC_REMOVE_DESCRIPTOR _IOW('#', 0x07, struct fw_cdev_remove_descriptor) |
181 | |||
182 | #define FW_CDEV_IOC_CREATE_ISO_CONTEXT _IOWR('#', 0x08, struct fw_cdev_create_iso_context) | 219 | #define FW_CDEV_IOC_CREATE_ISO_CONTEXT _IOWR('#', 0x08, struct fw_cdev_create_iso_context) |
183 | #define FW_CDEV_IOC_QUEUE_ISO _IOWR('#', 0x09, struct fw_cdev_queue_iso) | 220 | #define FW_CDEV_IOC_QUEUE_ISO _IOWR('#', 0x09, struct fw_cdev_queue_iso) |
184 | #define FW_CDEV_IOC_START_ISO _IOW('#', 0x0a, struct fw_cdev_start_iso) | 221 | #define FW_CDEV_IOC_START_ISO _IOW('#', 0x0a, struct fw_cdev_start_iso) |
185 | #define FW_CDEV_IOC_STOP_ISO _IOW('#', 0x0b, struct fw_cdev_stop_iso) | 222 | #define FW_CDEV_IOC_STOP_ISO _IOW('#', 0x0b, struct fw_cdev_stop_iso) |
223 | |||
224 | /* available since kernel version 2.6.24 */ | ||
186 | #define FW_CDEV_IOC_GET_CYCLE_TIMER _IOR('#', 0x0c, struct fw_cdev_get_cycle_timer) | 225 | #define FW_CDEV_IOC_GET_CYCLE_TIMER _IOR('#', 0x0c, struct fw_cdev_get_cycle_timer) |
187 | 226 | ||
227 | /* available since kernel version 2.6.30 */ | ||
228 | #define FW_CDEV_IOC_ALLOCATE_ISO_RESOURCE _IOWR('#', 0x0d, struct fw_cdev_allocate_iso_resource) | ||
229 | #define FW_CDEV_IOC_DEALLOCATE_ISO_RESOURCE _IOW('#', 0x0e, struct fw_cdev_deallocate) | ||
230 | |||
188 | /* FW_CDEV_VERSION History | 231 | /* FW_CDEV_VERSION History |
189 | * | 232 | * |
190 | * 1 Feb 18, 2007: Initial version. | 233 | * 1 Feb 18, 2007: Initial version. |
@@ -284,9 +327,9 @@ struct fw_cdev_allocate { | |||
284 | }; | 327 | }; |
285 | 328 | ||
286 | /** | 329 | /** |
287 | * struct fw_cdev_deallocate - Free an address range allocation | 330 | * struct fw_cdev_deallocate - Free a CSR address range or isochronous resource |
288 | * @handle: Handle to the address range, as returned by the kernel when the | 331 | * @handle: Handle to the address range or iso resource, as returned by the |
289 | * range was allocated | 332 | * kernel when the range or resource was allocated |
290 | */ | 333 | */ |
291 | struct fw_cdev_deallocate { | 334 | struct fw_cdev_deallocate { |
292 | __u32 handle; | 335 | __u32 handle; |
@@ -479,4 +522,35 @@ struct fw_cdev_get_cycle_timer { | |||
479 | __u32 cycle_timer; | 522 | __u32 cycle_timer; |
480 | }; | 523 | }; |
481 | 524 | ||
525 | /** | ||
526 | * struct fw_cdev_allocate_iso_resource - Allocate a channel or bandwidth | ||
527 | * @closure: Passed back to userspace in correponding iso resource events | ||
528 | * @channels: Isochronous channels of which one is to be allocated | ||
529 | * @bandwidth: Isochronous bandwidth units to be allocated | ||
530 | * @handle: Handle to the allocation, written by the kernel | ||
531 | * | ||
532 | * The %FW_CDEV_IOC_ALLOCATE_ISO_RESOURCE ioctl initiates allocation of an | ||
533 | * isochronous channel and/or of isochronous bandwidth at the isochronous | ||
534 | * resource manager (IRM). Only one of the channels specified in @channels is | ||
535 | * allocated. An %FW_CDEV_EVENT_ISO_RESOURCE_ALLOCATED is sent after | ||
536 | * communication with the IRM, indicating success or failure in the event data. | ||
537 | * The kernel will automatically reallocate the resources after bus resets. | ||
538 | * Should a reallocation fail, an %FW_CDEV_EVENT_ISO_RESOURCE_DEALLOCATED event | ||
539 | * will be sent. The kernel will also automatically deallocate the resources | ||
540 | * when the file descriptor is closed. | ||
541 | * | ||
542 | * @channels is a host-endian bitfield with the most significant bit | ||
543 | * representing channel 0 and the least significant bit representing channel 63: | ||
544 | * 1ULL << (63 - c) | ||
545 | * | ||
546 | * @bandwidth is expressed in bandwidth allocation units, i.e. the time to send | ||
547 | * one quadlet of data (payload or header data) at speed S1600. | ||
548 | */ | ||
549 | struct fw_cdev_allocate_iso_resource { | ||
550 | __u64 closure; | ||
551 | __u64 channels; | ||
552 | __u32 bandwidth; | ||
553 | __u32 handle; | ||
554 | }; | ||
555 | |||
482 | #endif /* _LINUX_FIREWIRE_CDEV_H */ | 556 | #endif /* _LINUX_FIREWIRE_CDEV_H */ |