aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStefan 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:44 -0400
commit1ec3c0269d7196118cc7c403654ca5f19ef4d584 (patch)
tree087bdf8db274f29f92762adf0ed6cef1bae2ce5d
parentb1bda4cdc2037447bd66753bf5ccab66d91b0b59 (diff)
firewire: cdev: add ioctls for manual iso resource management
This adds ioctls for allocation and deallocation of a channel or/and bandwidth without auto-reallocation and without auto-deallocation. The benefit of these ioctls is that libraw1394-style isochronous resource management can be implemented without write access to the IRM's character device file. Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de>
-rw-r--r--drivers/firewire/fw-cdev.c67
-rw-r--r--include/linux/firewire-cdev.h42
2 files changed, 86 insertions, 23 deletions
diff --git a/drivers/firewire/fw-cdev.c b/drivers/firewire/fw-cdev.c
index a227853aa1e..08fe68d34f3 100644
--- a/drivers/firewire/fw-cdev.c
+++ b/drivers/firewire/fw-cdev.c
@@ -121,14 +121,15 @@ struct iso_resource {
121 struct client *client; 121 struct client *client;
122 /* Schedule work and access todo only with client->lock held. */ 122 /* Schedule work and access todo only with client->lock held. */
123 struct delayed_work work; 123 struct delayed_work work;
124 enum {ISO_RES_ALLOC, ISO_RES_REALLOC, ISO_RES_DEALLOC,} todo; 124 enum {ISO_RES_ALLOC, ISO_RES_REALLOC, ISO_RES_DEALLOC,
125 ISO_RES_ALLOC_ONCE, ISO_RES_DEALLOC_ONCE,} todo;
125 int generation; 126 int generation;
126 u64 channels; 127 u64 channels;
127 s32 bandwidth; 128 s32 bandwidth;
128 struct iso_resource_event *e_alloc, *e_dealloc; 129 struct iso_resource_event *e_alloc, *e_dealloc;
129}; 130};
130 131
131static void schedule_iso_resource(struct iso_resource *); 132static int schedule_iso_resource(struct iso_resource *);
132static void release_iso_resource(struct client *, struct client_resource *); 133static void release_iso_resource(struct client *, struct client_resource *);
133 134
134/* 135/*
@@ -1033,7 +1034,9 @@ static void iso_resource_work(struct work_struct *work)
1033 skip = todo == ISO_RES_REALLOC && 1034 skip = todo == ISO_RES_REALLOC &&
1034 r->generation == generation; 1035 r->generation == generation;
1035 } 1036 }
1036 free = todo == ISO_RES_DEALLOC; 1037 free = todo == ISO_RES_DEALLOC ||
1038 todo == ISO_RES_ALLOC_ONCE ||
1039 todo == ISO_RES_DEALLOC_ONCE;
1037 r->generation = generation; 1040 r->generation = generation;
1038 spin_unlock_irq(&client->lock); 1041 spin_unlock_irq(&client->lock);
1039 1042
@@ -1044,7 +1047,9 @@ static void iso_resource_work(struct work_struct *work)
1044 1047
1045 fw_iso_resource_manage(client->device->card, generation, 1048 fw_iso_resource_manage(client->device->card, generation,
1046 r->channels, &channel, &bandwidth, 1049 r->channels, &channel, &bandwidth,
1047 todo == ISO_RES_ALLOC || todo == ISO_RES_REALLOC); 1050 todo == ISO_RES_ALLOC ||
1051 todo == ISO_RES_REALLOC ||
1052 todo == ISO_RES_ALLOC_ONCE);
1048 /* 1053 /*
1049 * Is this generation outdated already? As long as this resource sticks 1054 * 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 1055 * in the idr, it will be scheduled again for a newer generation or at
@@ -1082,7 +1087,7 @@ static void iso_resource_work(struct work_struct *work)
1082 if (todo == ISO_RES_REALLOC && success) 1087 if (todo == ISO_RES_REALLOC && success)
1083 goto out; 1088 goto out;
1084 1089
1085 if (todo == ISO_RES_ALLOC) { 1090 if (todo == ISO_RES_ALLOC || todo == ISO_RES_ALLOC_ONCE) {
1086 e = r->e_alloc; 1091 e = r->e_alloc;
1087 r->e_alloc = NULL; 1092 r->e_alloc = NULL;
1088 } else { 1093 } else {
@@ -1106,10 +1111,17 @@ static void iso_resource_work(struct work_struct *work)
1106 client_put(client); 1111 client_put(client);
1107} 1112}
1108 1113
1109static void schedule_iso_resource(struct iso_resource *r) 1114static int schedule_iso_resource(struct iso_resource *r)
1110{ 1115{
1111 if (schedule_delayed_work(&r->work, 0)) 1116 int scheduled;
1112 client_get(r->client); 1117
1118 client_get(r->client);
1119
1120 scheduled = schedule_delayed_work(&r->work, 0);
1121 if (!scheduled)
1122 client_put(r->client);
1123
1124 return scheduled;
1113} 1125}
1114 1126
1115static void release_iso_resource(struct client *client, 1127static void release_iso_resource(struct client *client,
@@ -1124,9 +1136,9 @@ static void release_iso_resource(struct client *client,
1124 spin_unlock_irq(&client->lock); 1136 spin_unlock_irq(&client->lock);
1125} 1137}
1126 1138
1127static int ioctl_allocate_iso_resource(struct client *client, void *buffer) 1139static int init_iso_resource(struct client *client,
1140 struct fw_cdev_allocate_iso_resource *request, int todo)
1128{ 1141{
1129 struct fw_cdev_allocate_iso_resource *request = buffer;
1130 struct iso_resource_event *e1, *e2; 1142 struct iso_resource_event *e1, *e2;
1131 struct iso_resource *r; 1143 struct iso_resource *r;
1132 int ret; 1144 int ret;
@@ -1146,7 +1158,7 @@ static int ioctl_allocate_iso_resource(struct client *client, void *buffer)
1146 1158
1147 INIT_DELAYED_WORK(&r->work, iso_resource_work); 1159 INIT_DELAYED_WORK(&r->work, iso_resource_work);
1148 r->client = client; 1160 r->client = client;
1149 r->todo = ISO_RES_ALLOC; 1161 r->todo = todo;
1150 r->generation = -1; 1162 r->generation = -1;
1151 r->channels = request->channels; 1163 r->channels = request->channels;
1152 r->bandwidth = request->bandwidth; 1164 r->bandwidth = request->bandwidth;
@@ -1158,8 +1170,14 @@ static int ioctl_allocate_iso_resource(struct client *client, void *buffer)
1158 e2->resource.closure = request->closure; 1170 e2->resource.closure = request->closure;
1159 e2->resource.type = FW_CDEV_EVENT_ISO_RESOURCE_DEALLOCATED; 1171 e2->resource.type = FW_CDEV_EVENT_ISO_RESOURCE_DEALLOCATED;
1160 1172
1161 r->resource.release = release_iso_resource; 1173 if (todo == ISO_RES_ALLOC) {
1162 ret = add_client_resource(client, &r->resource, GFP_KERNEL); 1174 r->resource.release = release_iso_resource;
1175 ret = add_client_resource(client, &r->resource, GFP_KERNEL);
1176 } else {
1177 r->resource.release = NULL;
1178 r->resource.handle = -1;
1179 ret = schedule_iso_resource(r) ? 0 : -ENOMEM;
1180 }
1163 if (ret < 0) 1181 if (ret < 0)
1164 goto fail; 1182 goto fail;
1165 request->handle = r->resource.handle; 1183 request->handle = r->resource.handle;
@@ -1173,6 +1191,13 @@ static int ioctl_allocate_iso_resource(struct client *client, void *buffer)
1173 return ret; 1191 return ret;
1174} 1192}
1175 1193
1194static int ioctl_allocate_iso_resource(struct client *client, void *buffer)
1195{
1196 struct fw_cdev_allocate_iso_resource *request = buffer;
1197
1198 return init_iso_resource(client, request, ISO_RES_ALLOC);
1199}
1200
1176static int ioctl_deallocate_iso_resource(struct client *client, void *buffer) 1201static int ioctl_deallocate_iso_resource(struct client *client, void *buffer)
1177{ 1202{
1178 struct fw_cdev_deallocate *request = buffer; 1203 struct fw_cdev_deallocate *request = buffer;
@@ -1181,6 +1206,20 @@ static int ioctl_deallocate_iso_resource(struct client *client, void *buffer)
1181 release_iso_resource, NULL); 1206 release_iso_resource, NULL);
1182} 1207}
1183 1208
1209static int ioctl_allocate_iso_resource_once(struct client *client, void *buffer)
1210{
1211 struct fw_cdev_allocate_iso_resource *request = buffer;
1212
1213 return init_iso_resource(client, request, ISO_RES_ALLOC_ONCE);
1214}
1215
1216static int ioctl_deallocate_iso_resource_once(struct client *client, void *buffer)
1217{
1218 struct fw_cdev_allocate_iso_resource *request = buffer;
1219
1220 return init_iso_resource(client, request, ISO_RES_DEALLOC_ONCE);
1221}
1222
1184static int (* const ioctl_handlers[])(struct client *client, void *buffer) = { 1223static int (* const ioctl_handlers[])(struct client *client, void *buffer) = {
1185 ioctl_get_info, 1224 ioctl_get_info,
1186 ioctl_send_request, 1225 ioctl_send_request,
@@ -1197,6 +1236,8 @@ static int (* const ioctl_handlers[])(struct client *client, void *buffer) = {
1197 ioctl_get_cycle_timer, 1236 ioctl_get_cycle_timer,
1198 ioctl_allocate_iso_resource, 1237 ioctl_allocate_iso_resource,
1199 ioctl_deallocate_iso_resource, 1238 ioctl_deallocate_iso_resource,
1239 ioctl_allocate_iso_resource_once,
1240 ioctl_deallocate_iso_resource_once,
1200}; 1241};
1201 1242
1202static int dispatch_ioctl(struct client *client, 1243static int dispatch_ioctl(struct client *client,
diff --git a/include/linux/firewire-cdev.h b/include/linux/firewire-cdev.h
index 25b96dd0574..08ca838a727 100644
--- a/include/linux/firewire-cdev.h
+++ b/include/linux/firewire-cdev.h
@@ -151,7 +151,7 @@ struct fw_cdev_event_iso_interrupt {
151/** 151/**
152 * struct fw_cdev_event_iso_resource - Iso resources were allocated or freed 152 * struct fw_cdev_event_iso_resource - Iso resources were allocated or freed
153 * @closure: See &fw_cdev_event_common; 153 * @closure: See &fw_cdev_event_common;
154 * set by %FW_CDEV_IOC_ALLOCATE_ISO_RESOURCE ioctl 154 * set by %FW_CDEV_IOC_(DE)ALLOCATE_ISO_RESOURCE(_ONCE) ioctl
155 * @type: %FW_CDEV_EVENT_ISO_RESOURCE_ALLOCATED or 155 * @type: %FW_CDEV_EVENT_ISO_RESOURCE_ALLOCATED or
156 * %FW_CDEV_EVENT_ISO_RESOURCE_DEALLOCATED 156 * %FW_CDEV_EVENT_ISO_RESOURCE_DEALLOCATED
157 * @handle: Reference by which an allocated resource can be deallocated 157 * @handle: Reference by which an allocated resource can be deallocated
@@ -164,12 +164,12 @@ struct fw_cdev_event_iso_interrupt {
164 * resource was allocated at the IRM. The client has to check @channel and 164 * resource was allocated at the IRM. The client has to check @channel and
165 * @bandwidth for whether the allocation actually succeeded. 165 * @bandwidth for whether the allocation actually succeeded.
166 * 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 167 * 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 168 * resource was deallocated at the IRM. It is also sent when automatic
172 * reallocation after a bus reset failed. 169 * reallocation after a bus reset failed.
170 *
171 * @channel is <0 if no channel was (de)allocated or if reallocation failed.
172 * @bandwidth is 0 if no bandwidth was (de)allocated or if reallocation failed.
173 */ 173 */
174struct fw_cdev_event_iso_resource { 174struct fw_cdev_event_iso_resource {
175 __u64 closure; 175 __u64 closure;
@@ -225,8 +225,10 @@ union fw_cdev_event {
225#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)
226 226
227/* available since kernel version 2.6.30 */ 227/* available since kernel version 2.6.30 */
228#define FW_CDEV_IOC_ALLOCATE_ISO_RESOURCE _IOWR('#', 0x0d, struct fw_cdev_allocate_iso_resource) 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) 229#define FW_CDEV_IOC_DEALLOCATE_ISO_RESOURCE _IOW('#', 0x0e, struct fw_cdev_deallocate)
230#define FW_CDEV_IOC_ALLOCATE_ISO_RESOURCE_ONCE _IOW('#', 0x0f, struct fw_cdev_allocate_iso_resource)
231#define FW_CDEV_IOC_DEALLOCATE_ISO_RESOURCE_ONCE _IOW('#', 0x10, struct fw_cdev_allocate_iso_resource)
230 232
231/* FW_CDEV_VERSION History 233/* FW_CDEV_VERSION History
232 * 234 *
@@ -523,11 +525,12 @@ struct fw_cdev_get_cycle_timer {
523}; 525};
524 526
525/** 527/**
526 * struct fw_cdev_allocate_iso_resource - Allocate a channel or bandwidth 528 * struct fw_cdev_allocate_iso_resource - (De)allocate a channel or bandwidth
527 * @closure: Passed back to userspace in correponding iso resource events 529 * @closure: Passed back to userspace in correponding iso resource events
528 * @channels: Isochronous channels of which one is to be allocated 530 * @channels: Isochronous channels of which one is to be (de)allocated
529 * @bandwidth: Isochronous bandwidth units to be allocated 531 * @bandwidth: Isochronous bandwidth units to be (de)allocated
530 * @handle: Handle to the allocation, written by the kernel 532 * @handle: Handle to the allocation, written by the kernel (only valid in
533 * case of %FW_CDEV_IOC_ALLOCATE_ISO_RESOURCE ioctls)
531 * 534 *
532 * The %FW_CDEV_IOC_ALLOCATE_ISO_RESOURCE ioctl initiates allocation of an 535 * The %FW_CDEV_IOC_ALLOCATE_ISO_RESOURCE ioctl initiates allocation of an
533 * isochronous channel and/or of isochronous bandwidth at the isochronous 536 * isochronous channel and/or of isochronous bandwidth at the isochronous
@@ -539,6 +542,25 @@ struct fw_cdev_get_cycle_timer {
539 * will be sent. The kernel will also automatically deallocate the resources 542 * will be sent. The kernel will also automatically deallocate the resources
540 * when the file descriptor is closed. 543 * when the file descriptor is closed.
541 * 544 *
545 * The %FW_CDEV_IOC_DEALLOCATE_ISO_RESOURCE ioctl can be used to initiate
546 * deallocation of resources which were allocated as described above.
547 * An %FW_CDEV_EVENT_ISO_RESOURCE_DEALLOCATED event concludes this operation.
548 *
549 * The %FW_CDEV_IOC_ALLOCATE_ISO_RESOURCE_ONCE ioctl is a variant of allocation
550 * without automatic re- or deallocation.
551 * An %FW_CDEV_EVENT_ISO_RESOURCE_ALLOCATED event concludes this operation,
552 * indicating success or failure in its data.
553 *
554 * The %FW_CDEV_IOC_DEALLOCATE_ISO_RESOURCE_ONCE ioctl works like
555 * %FW_CDEV_IOC_ALLOCATE_ISO_RESOURCE_ONCE except that resources are freed
556 * instead of allocated. At most one channel may be specified in this ioctl.
557 * An %FW_CDEV_EVENT_ISO_RESOURCE_DEALLOCATED event concludes this operation.
558 *
559 * To summarize, %FW_CDEV_IOC_DEALLOCATE_ISO_RESOURCE allocates iso resources
560 * for the lifetime of the fd or handle.
561 * In contrast, %FW_CDEV_IOC_ALLOCATE_ISO_RESOURCE_ONCE allocates iso resources
562 * for the duration of a bus generation.
563 *
542 * @channels is a host-endian bitfield with the most significant bit 564 * @channels is a host-endian bitfield with the most significant bit
543 * representing channel 0 and the least significant bit representing channel 63: 565 * representing channel 0 and the least significant bit representing channel 63:
544 * 1ULL << (63 - c) 566 * 1ULL << (63 - c)