struct iso_resource_event *e_alloc, *e_dealloc;
};
-static int schedule_iso_resource(struct iso_resource *);
+static void schedule_iso_resource(struct iso_resource *);
static void release_iso_resource(struct client *, struct client_resource *);
/*
struct outbound_transaction_event *e;
int ret;
- if (request->length > 4096 || request->length > 512 << speed)
+ if (request->tcode != TCODE_STREAM_DATA &&
+ (request->length > 4096 || request->length > 512 << speed))
return -EIO;
e = kmalloc(sizeof(*e) + request->length, GFP_KERNEL);
client_get(client);
fw_send_request(client->device->card, &e->r.transaction,
- request->tcode & 0x1f, destination_id,
- request->generation, speed, request->offset,
- e->response.data, request->length,
- complete_transaction, e);
+ request->tcode, destination_id, request->generation,
+ speed, request->offset, e->response.data,
+ request->length, complete_transaction, e);
+ return 0;
- if (request->data)
- return sizeof(request) + request->length;
- else
- return sizeof(request);
failed:
kfree(e);
return -EINVAL;
}
- return init_request(client, request, client->device->node->node_id,
+ return init_request(client, request, client->device->node_id,
client->device->max_speed);
}
static int ioctl_add_descriptor(struct client *client, void *buffer)
{
struct fw_cdev_add_descriptor *request = buffer;
+ struct fw_card *card = client->device->card;
struct descriptor_resource *r;
int ret;
+ /* Access policy: Allow this ioctl only on local nodes' device files. */
+ spin_lock_irq(&card->lock);
+ ret = client->device->node_id != card->local_node->node_id;
+ spin_unlock_irq(&card->lock);
+ if (ret)
+ return -ENOSYS;
+
if (request->length > 256)
return -EINVAL;
client_put(client);
}
-static int schedule_iso_resource(struct iso_resource *r)
+static void schedule_iso_resource(struct iso_resource *r)
{
- int scheduled;
-
client_get(r->client);
-
- scheduled = schedule_delayed_work(&r->work, 0);
- if (!scheduled)
+ if (!schedule_delayed_work(&r->work, 0))
client_put(r->client);
-
- return scheduled;
}
static void release_iso_resource(struct client *client,
if (todo == ISO_RES_ALLOC) {
r->resource.release = release_iso_resource;
ret = add_client_resource(client, &r->resource, GFP_KERNEL);
+ if (ret < 0)
+ goto fail;
} else {
r->resource.release = NULL;
r->resource.handle = -1;
- ret = schedule_iso_resource(r) ? 0 : -ENOMEM;
+ schedule_iso_resource(r);
}
- if (ret < 0)
- goto fail;
request->handle = r->resource.handle;
return 0;
return init_iso_resource(client, request, ISO_RES_DEALLOC_ONCE);
}
+/*
+ * Returns a speed code: Maximum speed to or from this device,
+ * limited by the device's link speed, the local node's link speed,
+ * and all PHY port speeds between the two links.
+ */
static int ioctl_get_speed(struct client *client, void *buffer)
{
- struct fw_cdev_get_speed *request = buffer;
-
- request->max_speed = client->device->max_speed;
-
- return 0;
+ return client->device->max_speed;
}
static int ioctl_send_broadcast_request(struct client *client, void *buffer)
return init_request(client, request, LOCAL_BUS | 0x3f, SCODE_100);
}
+static int ioctl_send_stream_packet(struct client *client, void *buffer)
+{
+ struct fw_cdev_send_stream_packet *p = buffer;
+ struct fw_cdev_send_request request;
+ int dest;
+
+ if (p->speed > client->device->card->link_speed ||
+ p->length > 1024 << p->speed)
+ return -EIO;
+
+ if (p->tag > 3 || p->channel > 63 || p->sy > 15)
+ return -EINVAL;
+
+ dest = fw_stream_packet_destination_id(p->tag, p->channel, p->sy);
+ request.tcode = TCODE_STREAM_DATA;
+ request.length = p->length;
+ request.closure = p->closure;
+ request.data = p->data;
+ request.generation = p->generation;
+
+ return init_request(client, &request, dest, p->speed);
+}
+
static int (* const ioctl_handlers[])(struct client *client, void *buffer) = {
ioctl_get_info,
ioctl_send_request,
ioctl_deallocate_iso_resource_once,
ioctl_get_speed,
ioctl_send_broadcast_request,
+ ioctl_send_stream_packet,
};
static int dispatch_ioctl(struct client *client,