diff options
author | Stefan Richter <stefanr@s5r6.in-berlin.de> | 2009-01-04 10:23:29 -0500 |
---|---|---|
committer | Stefan Richter <stefanr@s5r6.in-berlin.de> | 2009-03-24 15:56:44 -0400 |
commit | 1ec3c0269d7196118cc7c403654ca5f19ef4d584 (patch) | |
tree | 087bdf8db274f29f92762adf0ed6cef1bae2ce5d | |
parent | b1bda4cdc2037447bd66753bf5ccab66d91b0b59 (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.c | 67 | ||||
-rw-r--r-- | include/linux/firewire-cdev.h | 42 |
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 | ||
131 | static void schedule_iso_resource(struct iso_resource *); | 132 | static int schedule_iso_resource(struct iso_resource *); |
132 | static void release_iso_resource(struct client *, struct client_resource *); | 133 | static 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 | ||
1109 | static void schedule_iso_resource(struct iso_resource *r) | 1114 | static 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 | ||
1115 | static void release_iso_resource(struct client *client, | 1127 | static 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 | ||
1127 | static int ioctl_allocate_iso_resource(struct client *client, void *buffer) | 1139 | static 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 | ||
1194 | static 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 | |||
1176 | static int ioctl_deallocate_iso_resource(struct client *client, void *buffer) | 1201 | static 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 | ||
1209 | static 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 | |||
1216 | static 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 | |||
1184 | static int (* const ioctl_handlers[])(struct client *client, void *buffer) = { | 1223 | static 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 | ||
1202 | static int dispatch_ioctl(struct client *client, | 1243 | static 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 | */ |
174 | struct fw_cdev_event_iso_resource { | 174 | struct 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) |