Merge firewire branches to be released post v2.6.35
Conflicts: drivers/firewire/core-card.c drivers/firewire/core-cdev.c and forgotten #include <linux/time.h> in drivers/firewire/ohci.c Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de>
This commit is contained in:
@@ -18,6 +18,7 @@
|
||||
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include <linux/bug.h>
|
||||
#include <linux/compat.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
@@ -33,7 +34,7 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/poll.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/sched.h> /* required for linux/wait.h */
|
||||
#include <linux/slab.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/string.h>
|
||||
@@ -47,6 +48,13 @@
|
||||
|
||||
#include "core.h"
|
||||
|
||||
/*
|
||||
* ABI version history is documented in linux/firewire-cdev.h.
|
||||
*/
|
||||
#define FW_CDEV_KERNEL_VERSION 4
|
||||
#define FW_CDEV_VERSION_EVENT_REQUEST2 4
|
||||
#define FW_CDEV_VERSION_ALLOCATE_REGION_END 4
|
||||
|
||||
struct client {
|
||||
u32 version;
|
||||
struct fw_device *device;
|
||||
@@ -63,6 +71,9 @@ struct client {
|
||||
struct fw_iso_buffer buffer;
|
||||
unsigned long vm_start;
|
||||
|
||||
struct list_head phy_receiver_link;
|
||||
u64 phy_receiver_closure;
|
||||
|
||||
struct list_head link;
|
||||
struct kref kref;
|
||||
};
|
||||
@@ -107,6 +118,7 @@ struct outbound_transaction_resource {
|
||||
|
||||
struct inbound_transaction_resource {
|
||||
struct client_resource resource;
|
||||
struct fw_card *card;
|
||||
struct fw_request *request;
|
||||
void *data;
|
||||
size_t length;
|
||||
@@ -171,7 +183,10 @@ struct outbound_transaction_event {
|
||||
|
||||
struct inbound_transaction_event {
|
||||
struct event event;
|
||||
struct fw_cdev_event_request request;
|
||||
union {
|
||||
struct fw_cdev_event_request request;
|
||||
struct fw_cdev_event_request2 request2;
|
||||
} req;
|
||||
};
|
||||
|
||||
struct iso_interrupt_event {
|
||||
@@ -179,11 +194,28 @@ struct iso_interrupt_event {
|
||||
struct fw_cdev_event_iso_interrupt interrupt;
|
||||
};
|
||||
|
||||
struct iso_interrupt_mc_event {
|
||||
struct event event;
|
||||
struct fw_cdev_event_iso_interrupt_mc interrupt;
|
||||
};
|
||||
|
||||
struct iso_resource_event {
|
||||
struct event event;
|
||||
struct fw_cdev_event_iso_resource iso_resource;
|
||||
};
|
||||
|
||||
struct outbound_phy_packet_event {
|
||||
struct event event;
|
||||
struct client *client;
|
||||
struct fw_packet p;
|
||||
struct fw_cdev_event_phy_packet phy_packet;
|
||||
};
|
||||
|
||||
struct inbound_phy_packet_event {
|
||||
struct event event;
|
||||
struct fw_cdev_event_phy_packet phy_packet;
|
||||
};
|
||||
|
||||
static inline void __user *u64_to_uptr(__u64 value)
|
||||
{
|
||||
return (void __user *)(unsigned long)value;
|
||||
@@ -219,6 +251,7 @@ static int fw_device_op_open(struct inode *inode, struct file *file)
|
||||
idr_init(&client->resource_idr);
|
||||
INIT_LIST_HEAD(&client->event_list);
|
||||
init_waitqueue_head(&client->wait);
|
||||
INIT_LIST_HEAD(&client->phy_receiver_link);
|
||||
kref_init(&client->kref);
|
||||
|
||||
file->private_data = client;
|
||||
@@ -309,7 +342,7 @@ static void fill_bus_reset_event(struct fw_cdev_event_bus_reset *event,
|
||||
event->generation = client->device->generation;
|
||||
event->node_id = client->device->node_id;
|
||||
event->local_node_id = card->local_node->node_id;
|
||||
event->bm_node_id = 0; /* FIXME: We don't track the BM. */
|
||||
event->bm_node_id = card->bm_node_id;
|
||||
event->irm_node_id = card->irm_node->node_id;
|
||||
event->root_node_id = card->root_node->node_id;
|
||||
|
||||
@@ -340,7 +373,7 @@ static void queue_bus_reset_event(struct client *client)
|
||||
|
||||
e = kzalloc(sizeof(*e), GFP_KERNEL);
|
||||
if (e == NULL) {
|
||||
fw_notify("Out of memory when allocating bus reset event\n");
|
||||
fw_notify("Out of memory when allocating event\n");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -386,6 +419,9 @@ union ioctl_arg {
|
||||
struct fw_cdev_allocate_iso_resource allocate_iso_resource;
|
||||
struct fw_cdev_send_stream_packet send_stream_packet;
|
||||
struct fw_cdev_get_cycle_timer2 get_cycle_timer2;
|
||||
struct fw_cdev_send_phy_packet send_phy_packet;
|
||||
struct fw_cdev_receive_phy_packets receive_phy_packets;
|
||||
struct fw_cdev_set_iso_channels set_iso_channels;
|
||||
};
|
||||
|
||||
static int ioctl_get_info(struct client *client, union ioctl_arg *arg)
|
||||
@@ -395,7 +431,7 @@ static int ioctl_get_info(struct client *client, union ioctl_arg *arg)
|
||||
unsigned long ret = 0;
|
||||
|
||||
client->version = a->version;
|
||||
a->version = FW_CDEV_VERSION;
|
||||
a->version = FW_CDEV_KERNEL_VERSION;
|
||||
a->card = client->device->card->index;
|
||||
|
||||
down_read(&fw_device_rwsem);
|
||||
@@ -554,6 +590,10 @@ static int init_request(struct client *client,
|
||||
(request->length > 4096 || request->length > 512 << speed))
|
||||
return -EIO;
|
||||
|
||||
if (request->tcode == TCODE_WRITE_QUADLET_REQUEST &&
|
||||
request->length < 4)
|
||||
return -EINVAL;
|
||||
|
||||
e = kmalloc(sizeof(*e) + request->length, GFP_KERNEL);
|
||||
if (e == NULL)
|
||||
return -ENOMEM;
|
||||
@@ -626,28 +666,34 @@ static void release_request(struct client *client,
|
||||
if (is_fcp_request(r->request))
|
||||
kfree(r->data);
|
||||
else
|
||||
fw_send_response(client->device->card, r->request,
|
||||
RCODE_CONFLICT_ERROR);
|
||||
fw_send_response(r->card, r->request, RCODE_CONFLICT_ERROR);
|
||||
|
||||
fw_card_put(r->card);
|
||||
kfree(r);
|
||||
}
|
||||
|
||||
static void handle_request(struct fw_card *card, struct fw_request *request,
|
||||
int tcode, int destination, int source,
|
||||
int generation, int speed,
|
||||
unsigned long long offset,
|
||||
int generation, unsigned long long offset,
|
||||
void *payload, size_t length, void *callback_data)
|
||||
{
|
||||
struct address_handler_resource *handler = callback_data;
|
||||
struct inbound_transaction_resource *r;
|
||||
struct inbound_transaction_event *e;
|
||||
size_t event_size0;
|
||||
void *fcp_frame = NULL;
|
||||
int ret;
|
||||
|
||||
/* card may be different from handler->client->device->card */
|
||||
fw_card_get(card);
|
||||
|
||||
r = kmalloc(sizeof(*r), GFP_ATOMIC);
|
||||
e = kmalloc(sizeof(*e), GFP_ATOMIC);
|
||||
if (r == NULL || e == NULL)
|
||||
if (r == NULL || e == NULL) {
|
||||
fw_notify("Out of memory when allocating event\n");
|
||||
goto failed;
|
||||
|
||||
}
|
||||
r->card = card;
|
||||
r->request = request;
|
||||
r->data = payload;
|
||||
r->length = length;
|
||||
@@ -669,15 +715,37 @@ static void handle_request(struct fw_card *card, struct fw_request *request,
|
||||
if (ret < 0)
|
||||
goto failed;
|
||||
|
||||
e->request.type = FW_CDEV_EVENT_REQUEST;
|
||||
e->request.tcode = tcode;
|
||||
e->request.offset = offset;
|
||||
e->request.length = length;
|
||||
e->request.handle = r->resource.handle;
|
||||
e->request.closure = handler->closure;
|
||||
if (handler->client->version < FW_CDEV_VERSION_EVENT_REQUEST2) {
|
||||
struct fw_cdev_event_request *req = &e->req.request;
|
||||
|
||||
if (tcode & 0x10)
|
||||
tcode = TCODE_LOCK_REQUEST;
|
||||
|
||||
req->type = FW_CDEV_EVENT_REQUEST;
|
||||
req->tcode = tcode;
|
||||
req->offset = offset;
|
||||
req->length = length;
|
||||
req->handle = r->resource.handle;
|
||||
req->closure = handler->closure;
|
||||
event_size0 = sizeof(*req);
|
||||
} else {
|
||||
struct fw_cdev_event_request2 *req = &e->req.request2;
|
||||
|
||||
req->type = FW_CDEV_EVENT_REQUEST2;
|
||||
req->tcode = tcode;
|
||||
req->offset = offset;
|
||||
req->source_node_id = source;
|
||||
req->destination_node_id = destination;
|
||||
req->card = card->index;
|
||||
req->generation = generation;
|
||||
req->length = length;
|
||||
req->handle = r->resource.handle;
|
||||
req->closure = handler->closure;
|
||||
event_size0 = sizeof(*req);
|
||||
}
|
||||
|
||||
queue_event(handler->client, &e->event,
|
||||
&e->request, sizeof(e->request), r->data, length);
|
||||
&e->req, event_size0, r->data, length);
|
||||
return;
|
||||
|
||||
failed:
|
||||
@@ -687,6 +755,8 @@ static void handle_request(struct fw_card *card, struct fw_request *request,
|
||||
|
||||
if (!is_fcp_request(request))
|
||||
fw_send_response(card, request, RCODE_CONFLICT_ERROR);
|
||||
|
||||
fw_card_put(card);
|
||||
}
|
||||
|
||||
static void release_address_handler(struct client *client,
|
||||
@@ -711,7 +781,11 @@ static int ioctl_allocate(struct client *client, union ioctl_arg *arg)
|
||||
return -ENOMEM;
|
||||
|
||||
region.start = a->offset;
|
||||
region.end = a->offset + a->length;
|
||||
if (client->version < FW_CDEV_VERSION_ALLOCATE_REGION_END)
|
||||
region.end = a->offset + a->length;
|
||||
else
|
||||
region.end = a->region_end;
|
||||
|
||||
r->handler.length = a->length;
|
||||
r->handler.address_callback = handle_request;
|
||||
r->handler.callback_data = r;
|
||||
@@ -723,6 +797,7 @@ static int ioctl_allocate(struct client *client, union ioctl_arg *arg)
|
||||
kfree(r);
|
||||
return ret;
|
||||
}
|
||||
a->offset = r->handler.offset;
|
||||
|
||||
r->resource.release = release_address_handler;
|
||||
ret = add_client_resource(client, &r->resource, GFP_KERNEL);
|
||||
@@ -757,15 +832,19 @@ static int ioctl_send_response(struct client *client, union ioctl_arg *arg)
|
||||
if (is_fcp_request(r->request))
|
||||
goto out;
|
||||
|
||||
if (a->length < r->length)
|
||||
r->length = a->length;
|
||||
if (copy_from_user(r->data, u64_to_uptr(a->data), r->length)) {
|
||||
if (a->length != fw_get_response_length(r->request)) {
|
||||
ret = -EINVAL;
|
||||
kfree(r->request);
|
||||
goto out;
|
||||
}
|
||||
if (copy_from_user(r->data, u64_to_uptr(a->data), a->length)) {
|
||||
ret = -EFAULT;
|
||||
kfree(r->request);
|
||||
goto out;
|
||||
}
|
||||
fw_send_response(client->device->card, r->request, a->rcode);
|
||||
fw_send_response(r->card, r->request, a->rcode);
|
||||
out:
|
||||
fw_card_put(r->card);
|
||||
kfree(r);
|
||||
|
||||
return ret;
|
||||
@@ -773,8 +852,9 @@ static int ioctl_send_response(struct client *client, union ioctl_arg *arg)
|
||||
|
||||
static int ioctl_initiate_bus_reset(struct client *client, union ioctl_arg *arg)
|
||||
{
|
||||
return fw_core_initiate_bus_reset(client->device->card,
|
||||
fw_schedule_bus_reset(client->device->card, true,
|
||||
arg->initiate_bus_reset.type == FW_CDEV_SHORT_RESET);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void release_descriptor(struct client *client,
|
||||
@@ -845,10 +925,11 @@ static void iso_callback(struct fw_iso_context *context, u32 cycle,
|
||||
struct client *client = data;
|
||||
struct iso_interrupt_event *e;
|
||||
|
||||
e = kzalloc(sizeof(*e) + header_length, GFP_ATOMIC);
|
||||
if (e == NULL)
|
||||
e = kmalloc(sizeof(*e) + header_length, GFP_ATOMIC);
|
||||
if (e == NULL) {
|
||||
fw_notify("Out of memory when allocating event\n");
|
||||
return;
|
||||
|
||||
}
|
||||
e->interrupt.type = FW_CDEV_EVENT_ISO_INTERRUPT;
|
||||
e->interrupt.closure = client->iso_closure;
|
||||
e->interrupt.cycle = cycle;
|
||||
@@ -858,27 +939,54 @@ static void iso_callback(struct fw_iso_context *context, u32 cycle,
|
||||
sizeof(e->interrupt) + header_length, NULL, 0);
|
||||
}
|
||||
|
||||
static void iso_mc_callback(struct fw_iso_context *context,
|
||||
dma_addr_t completed, void *data)
|
||||
{
|
||||
struct client *client = data;
|
||||
struct iso_interrupt_mc_event *e;
|
||||
|
||||
e = kmalloc(sizeof(*e), GFP_ATOMIC);
|
||||
if (e == NULL) {
|
||||
fw_notify("Out of memory when allocating event\n");
|
||||
return;
|
||||
}
|
||||
e->interrupt.type = FW_CDEV_EVENT_ISO_INTERRUPT_MULTICHANNEL;
|
||||
e->interrupt.closure = client->iso_closure;
|
||||
e->interrupt.completed = fw_iso_buffer_lookup(&client->buffer,
|
||||
completed);
|
||||
queue_event(client, &e->event, &e->interrupt,
|
||||
sizeof(e->interrupt), NULL, 0);
|
||||
}
|
||||
|
||||
static int ioctl_create_iso_context(struct client *client, union ioctl_arg *arg)
|
||||
{
|
||||
struct fw_cdev_create_iso_context *a = &arg->create_iso_context;
|
||||
struct fw_iso_context *context;
|
||||
fw_iso_callback_t cb;
|
||||
|
||||
/* We only support one context at this time. */
|
||||
if (client->iso_context != NULL)
|
||||
return -EBUSY;
|
||||
|
||||
if (a->channel > 63)
|
||||
return -EINVAL;
|
||||
BUILD_BUG_ON(FW_CDEV_ISO_CONTEXT_TRANSMIT != FW_ISO_CONTEXT_TRANSMIT ||
|
||||
FW_CDEV_ISO_CONTEXT_RECEIVE != FW_ISO_CONTEXT_RECEIVE ||
|
||||
FW_CDEV_ISO_CONTEXT_RECEIVE_MULTICHANNEL !=
|
||||
FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL);
|
||||
|
||||
switch (a->type) {
|
||||
case FW_ISO_CONTEXT_RECEIVE:
|
||||
if (a->header_size < 4 || (a->header_size & 3))
|
||||
case FW_ISO_CONTEXT_TRANSMIT:
|
||||
if (a->speed > SCODE_3200 || a->channel > 63)
|
||||
return -EINVAL;
|
||||
|
||||
cb = iso_callback;
|
||||
break;
|
||||
|
||||
case FW_ISO_CONTEXT_TRANSMIT:
|
||||
if (a->speed > SCODE_3200)
|
||||
case FW_ISO_CONTEXT_RECEIVE:
|
||||
if (a->header_size < 4 || (a->header_size & 3) ||
|
||||
a->channel > 63)
|
||||
return -EINVAL;
|
||||
|
||||
cb = iso_callback;
|
||||
break;
|
||||
|
||||
case FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL:
|
||||
cb = (fw_iso_callback_t)iso_mc_callback;
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -886,20 +994,37 @@ static int ioctl_create_iso_context(struct client *client, union ioctl_arg *arg)
|
||||
}
|
||||
|
||||
context = fw_iso_context_create(client->device->card, a->type,
|
||||
a->channel, a->speed, a->header_size,
|
||||
iso_callback, client);
|
||||
a->channel, a->speed, a->header_size, cb, client);
|
||||
if (IS_ERR(context))
|
||||
return PTR_ERR(context);
|
||||
|
||||
/* We only support one context at this time. */
|
||||
spin_lock_irq(&client->lock);
|
||||
if (client->iso_context != NULL) {
|
||||
spin_unlock_irq(&client->lock);
|
||||
fw_iso_context_destroy(context);
|
||||
return -EBUSY;
|
||||
}
|
||||
client->iso_closure = a->closure;
|
||||
client->iso_context = context;
|
||||
spin_unlock_irq(&client->lock);
|
||||
|
||||
/* We only support one context at this time. */
|
||||
a->handle = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ioctl_set_iso_channels(struct client *client, union ioctl_arg *arg)
|
||||
{
|
||||
struct fw_cdev_set_iso_channels *a = &arg->set_iso_channels;
|
||||
struct fw_iso_context *ctx = client->iso_context;
|
||||
|
||||
if (ctx == NULL || a->handle != 0)
|
||||
return -EINVAL;
|
||||
|
||||
return fw_iso_context_set_channels(ctx, &a->channels);
|
||||
}
|
||||
|
||||
/* Macros for decoding the iso packet control header. */
|
||||
#define GET_PAYLOAD_LENGTH(v) ((v) & 0xffff)
|
||||
#define GET_INTERRUPT(v) (((v) >> 16) & 0x01)
|
||||
@@ -913,7 +1038,7 @@ static int ioctl_queue_iso(struct client *client, union ioctl_arg *arg)
|
||||
struct fw_cdev_queue_iso *a = &arg->queue_iso;
|
||||
struct fw_cdev_iso_packet __user *p, *end, *next;
|
||||
struct fw_iso_context *ctx = client->iso_context;
|
||||
unsigned long payload, buffer_end, header_length;
|
||||
unsigned long payload, buffer_end, transmit_header_bytes = 0;
|
||||
u32 control;
|
||||
int count;
|
||||
struct {
|
||||
@@ -933,7 +1058,6 @@ static int ioctl_queue_iso(struct client *client, union ioctl_arg *arg)
|
||||
* use the indirect payload, the iso buffer need not be mapped
|
||||
* and the a->data pointer is ignored.
|
||||
*/
|
||||
|
||||
payload = (unsigned long)a->data - client->vm_start;
|
||||
buffer_end = client->buffer.page_count << PAGE_SHIFT;
|
||||
if (a->data == 0 || client->buffer.pages == NULL ||
|
||||
@@ -942,8 +1066,10 @@ static int ioctl_queue_iso(struct client *client, union ioctl_arg *arg)
|
||||
buffer_end = 0;
|
||||
}
|
||||
|
||||
p = (struct fw_cdev_iso_packet __user *)u64_to_uptr(a->packets);
|
||||
if (ctx->type == FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL && payload & 3)
|
||||
return -EINVAL;
|
||||
|
||||
p = (struct fw_cdev_iso_packet __user *)u64_to_uptr(a->packets);
|
||||
if (!access_ok(VERIFY_READ, p, a->size))
|
||||
return -EFAULT;
|
||||
|
||||
@@ -959,31 +1085,32 @@ static int ioctl_queue_iso(struct client *client, union ioctl_arg *arg)
|
||||
u.packet.sy = GET_SY(control);
|
||||
u.packet.header_length = GET_HEADER_LENGTH(control);
|
||||
|
||||
if (ctx->type == FW_ISO_CONTEXT_TRANSMIT) {
|
||||
if (u.packet.header_length % 4 != 0)
|
||||
switch (ctx->type) {
|
||||
case FW_ISO_CONTEXT_TRANSMIT:
|
||||
if (u.packet.header_length & 3)
|
||||
return -EINVAL;
|
||||
header_length = u.packet.header_length;
|
||||
} else {
|
||||
/*
|
||||
* We require that header_length is a multiple of
|
||||
* the fixed header size, ctx->header_size.
|
||||
*/
|
||||
if (ctx->header_size == 0) {
|
||||
if (u.packet.header_length > 0)
|
||||
return -EINVAL;
|
||||
} else if (u.packet.header_length == 0 ||
|
||||
u.packet.header_length % ctx->header_size != 0) {
|
||||
transmit_header_bytes = u.packet.header_length;
|
||||
break;
|
||||
|
||||
case FW_ISO_CONTEXT_RECEIVE:
|
||||
if (u.packet.header_length == 0 ||
|
||||
u.packet.header_length % ctx->header_size != 0)
|
||||
return -EINVAL;
|
||||
}
|
||||
header_length = 0;
|
||||
break;
|
||||
|
||||
case FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL:
|
||||
if (u.packet.payload_length == 0 ||
|
||||
u.packet.payload_length & 3)
|
||||
return -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
next = (struct fw_cdev_iso_packet __user *)
|
||||
&p->header[header_length / 4];
|
||||
&p->header[transmit_header_bytes / 4];
|
||||
if (next > end)
|
||||
return -EINVAL;
|
||||
if (__copy_from_user
|
||||
(u.packet.header, p->header, header_length))
|
||||
(u.packet.header, p->header, transmit_header_bytes))
|
||||
return -EFAULT;
|
||||
if (u.packet.skip && ctx->type == FW_ISO_CONTEXT_TRANSMIT &&
|
||||
u.packet.header_length + u.packet.payload_length > 0)
|
||||
@@ -1011,6 +1138,13 @@ static int ioctl_start_iso(struct client *client, union ioctl_arg *arg)
|
||||
{
|
||||
struct fw_cdev_start_iso *a = &arg->start_iso;
|
||||
|
||||
BUILD_BUG_ON(
|
||||
FW_CDEV_ISO_CONTEXT_MATCH_TAG0 != FW_ISO_CONTEXT_MATCH_TAG0 ||
|
||||
FW_CDEV_ISO_CONTEXT_MATCH_TAG1 != FW_ISO_CONTEXT_MATCH_TAG1 ||
|
||||
FW_CDEV_ISO_CONTEXT_MATCH_TAG2 != FW_ISO_CONTEXT_MATCH_TAG2 ||
|
||||
FW_CDEV_ISO_CONTEXT_MATCH_TAG3 != FW_ISO_CONTEXT_MATCH_TAG3 ||
|
||||
FW_CDEV_ISO_CONTEXT_MATCH_ALL_TAGS != FW_ISO_CONTEXT_MATCH_ALL_TAGS);
|
||||
|
||||
if (client->iso_context == NULL || a->handle != 0)
|
||||
return -EINVAL;
|
||||
|
||||
@@ -1042,7 +1176,7 @@ static int ioctl_get_cycle_timer2(struct client *client, union ioctl_arg *arg)
|
||||
|
||||
local_irq_disable();
|
||||
|
||||
cycle_time = card->driver->get_cycle_time(card);
|
||||
cycle_time = card->driver->read_csr(card, CSR_CYCLE_TIME);
|
||||
|
||||
switch (a->clk_id) {
|
||||
case CLOCK_REALTIME: getnstimeofday(&ts); break;
|
||||
@@ -1323,28 +1457,135 @@ static int ioctl_send_stream_packet(struct client *client, union ioctl_arg *arg)
|
||||
return init_request(client, &request, dest, a->speed);
|
||||
}
|
||||
|
||||
static void outbound_phy_packet_callback(struct fw_packet *packet,
|
||||
struct fw_card *card, int status)
|
||||
{
|
||||
struct outbound_phy_packet_event *e =
|
||||
container_of(packet, struct outbound_phy_packet_event, p);
|
||||
|
||||
switch (status) {
|
||||
/* expected: */
|
||||
case ACK_COMPLETE: e->phy_packet.rcode = RCODE_COMPLETE; break;
|
||||
/* should never happen with PHY packets: */
|
||||
case ACK_PENDING: e->phy_packet.rcode = RCODE_COMPLETE; break;
|
||||
case ACK_BUSY_X:
|
||||
case ACK_BUSY_A:
|
||||
case ACK_BUSY_B: e->phy_packet.rcode = RCODE_BUSY; break;
|
||||
case ACK_DATA_ERROR: e->phy_packet.rcode = RCODE_DATA_ERROR; break;
|
||||
case ACK_TYPE_ERROR: e->phy_packet.rcode = RCODE_TYPE_ERROR; break;
|
||||
/* stale generation; cancelled; on certain controllers: no ack */
|
||||
default: e->phy_packet.rcode = status; break;
|
||||
}
|
||||
e->phy_packet.data[0] = packet->timestamp;
|
||||
|
||||
queue_event(e->client, &e->event, &e->phy_packet,
|
||||
sizeof(e->phy_packet) + e->phy_packet.length, NULL, 0);
|
||||
client_put(e->client);
|
||||
}
|
||||
|
||||
static int ioctl_send_phy_packet(struct client *client, union ioctl_arg *arg)
|
||||
{
|
||||
struct fw_cdev_send_phy_packet *a = &arg->send_phy_packet;
|
||||
struct fw_card *card = client->device->card;
|
||||
struct outbound_phy_packet_event *e;
|
||||
|
||||
/* Access policy: Allow this ioctl only on local nodes' device files. */
|
||||
if (!client->device->is_local)
|
||||
return -ENOSYS;
|
||||
|
||||
e = kzalloc(sizeof(*e) + 4, GFP_KERNEL);
|
||||
if (e == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
client_get(client);
|
||||
e->client = client;
|
||||
e->p.speed = SCODE_100;
|
||||
e->p.generation = a->generation;
|
||||
e->p.header[0] = a->data[0];
|
||||
e->p.header[1] = a->data[1];
|
||||
e->p.header_length = 8;
|
||||
e->p.callback = outbound_phy_packet_callback;
|
||||
e->phy_packet.closure = a->closure;
|
||||
e->phy_packet.type = FW_CDEV_EVENT_PHY_PACKET_SENT;
|
||||
if (is_ping_packet(a->data))
|
||||
e->phy_packet.length = 4;
|
||||
|
||||
card->driver->send_request(card, &e->p);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ioctl_receive_phy_packets(struct client *client, union ioctl_arg *arg)
|
||||
{
|
||||
struct fw_cdev_receive_phy_packets *a = &arg->receive_phy_packets;
|
||||
struct fw_card *card = client->device->card;
|
||||
|
||||
/* Access policy: Allow this ioctl only on local nodes' device files. */
|
||||
if (!client->device->is_local)
|
||||
return -ENOSYS;
|
||||
|
||||
spin_lock_irq(&card->lock);
|
||||
|
||||
list_move_tail(&client->phy_receiver_link, &card->phy_receiver_list);
|
||||
client->phy_receiver_closure = a->closure;
|
||||
|
||||
spin_unlock_irq(&card->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void fw_cdev_handle_phy_packet(struct fw_card *card, struct fw_packet *p)
|
||||
{
|
||||
struct client *client;
|
||||
struct inbound_phy_packet_event *e;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&card->lock, flags);
|
||||
|
||||
list_for_each_entry(client, &card->phy_receiver_list, phy_receiver_link) {
|
||||
e = kmalloc(sizeof(*e) + 8, GFP_ATOMIC);
|
||||
if (e == NULL) {
|
||||
fw_notify("Out of memory when allocating event\n");
|
||||
break;
|
||||
}
|
||||
e->phy_packet.closure = client->phy_receiver_closure;
|
||||
e->phy_packet.type = FW_CDEV_EVENT_PHY_PACKET_RECEIVED;
|
||||
e->phy_packet.rcode = RCODE_COMPLETE;
|
||||
e->phy_packet.length = 8;
|
||||
e->phy_packet.data[0] = p->header[1];
|
||||
e->phy_packet.data[1] = p->header[2];
|
||||
queue_event(client, &e->event,
|
||||
&e->phy_packet, sizeof(e->phy_packet) + 8, NULL, 0);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&card->lock, flags);
|
||||
}
|
||||
|
||||
static int (* const ioctl_handlers[])(struct client *, union ioctl_arg *) = {
|
||||
ioctl_get_info,
|
||||
ioctl_send_request,
|
||||
ioctl_allocate,
|
||||
ioctl_deallocate,
|
||||
ioctl_send_response,
|
||||
ioctl_initiate_bus_reset,
|
||||
ioctl_add_descriptor,
|
||||
ioctl_remove_descriptor,
|
||||
ioctl_create_iso_context,
|
||||
ioctl_queue_iso,
|
||||
ioctl_start_iso,
|
||||
ioctl_stop_iso,
|
||||
ioctl_get_cycle_timer,
|
||||
ioctl_allocate_iso_resource,
|
||||
ioctl_deallocate_iso_resource,
|
||||
ioctl_allocate_iso_resource_once,
|
||||
ioctl_deallocate_iso_resource_once,
|
||||
ioctl_get_speed,
|
||||
ioctl_send_broadcast_request,
|
||||
ioctl_send_stream_packet,
|
||||
ioctl_get_cycle_timer2,
|
||||
[0x00] = ioctl_get_info,
|
||||
[0x01] = ioctl_send_request,
|
||||
[0x02] = ioctl_allocate,
|
||||
[0x03] = ioctl_deallocate,
|
||||
[0x04] = ioctl_send_response,
|
||||
[0x05] = ioctl_initiate_bus_reset,
|
||||
[0x06] = ioctl_add_descriptor,
|
||||
[0x07] = ioctl_remove_descriptor,
|
||||
[0x08] = ioctl_create_iso_context,
|
||||
[0x09] = ioctl_queue_iso,
|
||||
[0x0a] = ioctl_start_iso,
|
||||
[0x0b] = ioctl_stop_iso,
|
||||
[0x0c] = ioctl_get_cycle_timer,
|
||||
[0x0d] = ioctl_allocate_iso_resource,
|
||||
[0x0e] = ioctl_deallocate_iso_resource,
|
||||
[0x0f] = ioctl_allocate_iso_resource_once,
|
||||
[0x10] = ioctl_deallocate_iso_resource_once,
|
||||
[0x11] = ioctl_get_speed,
|
||||
[0x12] = ioctl_send_broadcast_request,
|
||||
[0x13] = ioctl_send_stream_packet,
|
||||
[0x14] = ioctl_get_cycle_timer2,
|
||||
[0x15] = ioctl_send_phy_packet,
|
||||
[0x16] = ioctl_receive_phy_packets,
|
||||
[0x17] = ioctl_set_iso_channels,
|
||||
};
|
||||
|
||||
static int dispatch_ioctl(struct client *client,
|
||||
@@ -1452,6 +1693,10 @@ static int fw_device_op_release(struct inode *inode, struct file *file)
|
||||
struct client *client = file->private_data;
|
||||
struct event *event, *next_event;
|
||||
|
||||
spin_lock_irq(&client->device->card->lock);
|
||||
list_del(&client->phy_receiver_link);
|
||||
spin_unlock_irq(&client->device->card->lock);
|
||||
|
||||
mutex_lock(&client->device->client_list_mutex);
|
||||
list_del(&client->link);
|
||||
mutex_unlock(&client->device->client_list_mutex);
|
||||
|
Reference in New Issue
Block a user