diff options
Diffstat (limited to 'drivers/firewire/core-cdev.c')
| -rw-r--r-- | drivers/firewire/core-cdev.c | 252 | 
1 files changed, 188 insertions, 64 deletions
diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c index 2c16ee8fd842..6274b86eb943 100644 --- a/drivers/firewire/core-cdev.c +++ b/drivers/firewire/core-cdev.c @@ -43,6 +43,7 @@  #define FW_CDEV_VERSION_EVENT_REQUEST2		4  #define FW_CDEV_VERSION_ALLOCATE_REGION_END	4  #define FW_CDEV_VERSION_AUTO_FLUSH_ISO_OVERFLOW	5 +#define FW_CDEV_VERSION_EVENT_ASYNC_TSTAMP	6  struct client {  	u32 version; @@ -169,7 +170,10 @@ struct outbound_transaction_event {  	struct event event;  	struct client *client;  	struct outbound_transaction_resource r; -	struct fw_cdev_event_response response; +	union { +		struct fw_cdev_event_response without_tstamp; +		struct fw_cdev_event_response2 with_tstamp; +	} rsp;  };  struct inbound_transaction_event { @@ -177,6 +181,7 @@ struct inbound_transaction_event {  	union {  		struct fw_cdev_event_request request;  		struct fw_cdev_event_request2 request2; +		struct fw_cdev_event_request3 with_tstamp;  	} req;  }; @@ -199,12 +204,18 @@ struct outbound_phy_packet_event {  	struct event event;  	struct client *client;  	struct fw_packet p; -	struct fw_cdev_event_phy_packet phy_packet; +	union { +		struct fw_cdev_event_phy_packet without_tstamp; +		struct fw_cdev_event_phy_packet2 with_tstamp; +	} phy_packet;  };  struct inbound_phy_packet_event {  	struct event event; -	struct fw_cdev_event_phy_packet phy_packet; +	union { +		struct fw_cdev_event_phy_packet without_tstamp; +		struct fw_cdev_event_phy_packet2 with_tstamp; +	} phy_packet;  };  #ifdef CONFIG_COMPAT @@ -534,41 +545,64 @@ static void release_transaction(struct client *client,  {  } -static void complete_transaction(struct fw_card *card, int rcode, -				 void *payload, size_t length, void *data) +static void complete_transaction(struct fw_card *card, int rcode, u32 request_tstamp, +				 u32 response_tstamp, void *payload, size_t length, void *data)  {  	struct outbound_transaction_event *e = data; -	struct fw_cdev_event_response *rsp = &e->response;  	struct client *client = e->client;  	unsigned long flags; -	if (length < rsp->length) -		rsp->length = length; -	if (rcode == RCODE_COMPLETE) -		memcpy(rsp->data, payload, rsp->length); -  	spin_lock_irqsave(&client->lock, flags);  	idr_remove(&client->resource_idr, e->r.resource.handle);  	if (client->in_shutdown)  		wake_up(&client->tx_flush_wait);  	spin_unlock_irqrestore(&client->lock, flags); -	rsp->type = FW_CDEV_EVENT_RESPONSE; -	rsp->rcode = rcode; +	switch (e->rsp.without_tstamp.type) { +	case FW_CDEV_EVENT_RESPONSE: +	{ +		struct fw_cdev_event_response *rsp = &e->rsp.without_tstamp; -	/* -	 * In the case that sizeof(*rsp) doesn't align with the position of the -	 * data, and the read is short, preserve an extra copy of the data -	 * to stay compatible with a pre-2.6.27 bug.  Since the bug is harmless -	 * for short reads and some apps depended on it, this is both safe -	 * and prudent for compatibility. -	 */ -	if (rsp->length <= sizeof(*rsp) - offsetof(typeof(*rsp), data)) -		queue_event(client, &e->event, rsp, sizeof(*rsp), -			    rsp->data, rsp->length); -	else -		queue_event(client, &e->event, rsp, sizeof(*rsp) + rsp->length, -			    NULL, 0); +		if (length < rsp->length) +			rsp->length = length; +		if (rcode == RCODE_COMPLETE) +			memcpy(rsp->data, payload, rsp->length); + +		rsp->rcode = rcode; + +		// In the case that sizeof(*rsp) doesn't align with the position of the +		// data, and the read is short, preserve an extra copy of the data +		// to stay compatible with a pre-2.6.27 bug.  Since the bug is harmless +		// for short reads and some apps depended on it, this is both safe +		// and prudent for compatibility. +		if (rsp->length <= sizeof(*rsp) - offsetof(typeof(*rsp), data)) +			queue_event(client, &e->event, rsp, sizeof(*rsp), rsp->data, rsp->length); +		else +			queue_event(client, &e->event, rsp, sizeof(*rsp) + rsp->length, NULL, 0); + +		break; +	} +	case FW_CDEV_EVENT_RESPONSE2: +	{ +		struct fw_cdev_event_response2 *rsp = &e->rsp.with_tstamp; + +		if (length < rsp->length) +			rsp->length = length; +		if (rcode == RCODE_COMPLETE) +			memcpy(rsp->data, payload, rsp->length); + +		rsp->rcode = rcode; +		rsp->request_tstamp = request_tstamp; +		rsp->response_tstamp = response_tstamp; + +		queue_event(client, &e->event, rsp, sizeof(*rsp) + rsp->length, NULL, 0); + +		break; +	default: +		WARN_ON(1); +		break; +	} +	}  	/* Drop the idr's reference */  	client_put(client); @@ -579,6 +613,7 @@ static int init_request(struct client *client,  			int destination_id, int speed)  {  	struct outbound_transaction_event *e; +	void *payload;  	int ret;  	if (request->tcode != TCODE_STREAM_DATA && @@ -592,14 +627,25 @@ static int init_request(struct client *client,  	e = kmalloc(sizeof(*e) + request->length, GFP_KERNEL);  	if (e == NULL)  		return -ENOMEM; -  	e->client = client; -	e->response.length = request->length; -	e->response.closure = request->closure; -	if (request->data && -	    copy_from_user(e->response.data, -			   u64_to_uptr(request->data), request->length)) { +	if (client->version < FW_CDEV_VERSION_EVENT_ASYNC_TSTAMP) { +		struct fw_cdev_event_response *rsp = &e->rsp.without_tstamp; + +		rsp->type = FW_CDEV_EVENT_RESPONSE; +		rsp->length = request->length; +		rsp->closure = request->closure; +		payload = rsp->data; +	} else { +		struct fw_cdev_event_response2 *rsp = &e->rsp.with_tstamp; + +		rsp->type = FW_CDEV_EVENT_RESPONSE2; +		rsp->length = request->length; +		rsp->closure = request->closure; +		payload = rsp->data; +	} + +	if (request->data && copy_from_user(payload, u64_to_uptr(request->data), request->length)) {  		ret = -EFAULT;  		goto failed;  	} @@ -609,10 +655,9 @@ static int init_request(struct client *client,  	if (ret < 0)  		goto failed; -	fw_send_request(client->device->card, &e->r.transaction, -			request->tcode, destination_id, request->generation, -			speed, request->offset, e->response.data, -			request->length, complete_transaction, e); +	fw_send_request_with_tstamp(client->device->card, &e->r.transaction, request->tcode, +				    destination_id, request->generation, speed, request->offset, +				    payload, request->length, complete_transaction, e);  	return 0;   failed: @@ -708,7 +753,7 @@ static void handle_request(struct fw_card *card, struct fw_request *request,  		req->handle	= r->resource.handle;  		req->closure	= handler->closure;  		event_size0	= sizeof(*req); -	} else { +	} else if (handler->client->version < FW_CDEV_VERSION_EVENT_ASYNC_TSTAMP) {  		struct fw_cdev_event_request2 *req = &e->req.request2;  		req->type	= FW_CDEV_EVENT_REQUEST2; @@ -722,6 +767,21 @@ static void handle_request(struct fw_card *card, struct fw_request *request,  		req->handle	= r->resource.handle;  		req->closure	= handler->closure;  		event_size0	= sizeof(*req); +	} else { +		struct fw_cdev_event_request3 *req = &e->req.with_tstamp; + +		req->type	= FW_CDEV_EVENT_REQUEST3; +		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; +		req->tstamp	= fw_request_get_timestamp(request); +		event_size0	= sizeof(*req);  	}  	queue_event(handler->client, &e->event, @@ -1495,26 +1555,61 @@ static void outbound_phy_packet_callback(struct fw_packet *packet,  {  	struct outbound_phy_packet_event *e =  		container_of(packet, struct outbound_phy_packet_event, p); -	struct client *e_client; +	struct client *e_client = e->client; +	u32 rcode;  	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; +	// expected: +	case ACK_COMPLETE: +		rcode = RCODE_COMPLETE; +		break; +	// should never happen with PHY packets: +	case ACK_PENDING: +		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; +	case ACK_BUSY_B: +		rcode = RCODE_BUSY; +		break; +	case ACK_DATA_ERROR: +		rcode = RCODE_DATA_ERROR; +		break; +	case ACK_TYPE_ERROR: +		rcode = RCODE_TYPE_ERROR; +		break; +	// stale generation; cancelled; on certain controllers: no ack +	default: +		rcode = status; +		break; +	} + +	switch (e->phy_packet.without_tstamp.type) { +	case FW_CDEV_EVENT_PHY_PACKET_SENT: +	{ +		struct fw_cdev_event_phy_packet *pp = &e->phy_packet.without_tstamp; + +		pp->rcode = rcode; +		pp->data[0] = packet->timestamp; +		queue_event(e->client, &e->event, &e->phy_packet, sizeof(*pp) + pp->length, +			    NULL, 0); +		break; +	} +	case FW_CDEV_EVENT_PHY_PACKET_SENT2: +	{ +		struct fw_cdev_event_phy_packet2 *pp = &e->phy_packet.with_tstamp; + +		pp->rcode = rcode; +		pp->tstamp = packet->timestamp; +		queue_event(e->client, &e->event, &e->phy_packet, sizeof(*pp) + pp->length, +			    NULL, 0); +		break; +	} +	default: +		WARN_ON(1); +		break;  	} -	e->phy_packet.data[0] = packet->timestamp; -	e_client = e->client; -	queue_event(e->client, &e->event, &e->phy_packet, -		    sizeof(e->phy_packet) + e->phy_packet.length, NULL, 0);  	client_put(e_client);  } @@ -1528,7 +1623,7 @@ static int ioctl_send_phy_packet(struct client *client, union ioctl_arg *arg)  	if (!client->device->is_local)  		return -ENOSYS; -	e = kzalloc(sizeof(*e) + 4, GFP_KERNEL); +	e = kzalloc(sizeof(*e) + sizeof(a->data), GFP_KERNEL);  	if (e == NULL)  		return -ENOMEM; @@ -1541,10 +1636,24 @@ static int ioctl_send_phy_packet(struct client *client, union ioctl_arg *arg)  	e->p.header[2]		= a->data[1];  	e->p.header_length	= 12;  	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; + +	if (client->version < FW_CDEV_VERSION_EVENT_ASYNC_TSTAMP) { +		struct fw_cdev_event_phy_packet *pp = &e->phy_packet.without_tstamp; + +		pp->closure = a->closure; +		pp->type = FW_CDEV_EVENT_PHY_PACKET_SENT; +		if (is_ping_packet(a->data)) +			pp->length = 4; +	} else { +		struct fw_cdev_event_phy_packet2 *pp = &e->phy_packet.with_tstamp; + +		pp->closure = a->closure; +		pp->type = FW_CDEV_EVENT_PHY_PACKET_SENT2; +		// Keep the data field so that application can match the response event to the +		// request. +		pp->length = sizeof(a->data); +		memcpy(pp->data, a->data, sizeof(a->data)); +	}  	card->driver->send_request(card, &e->p); @@ -1583,14 +1692,29 @@ void fw_cdev_handle_phy_packet(struct fw_card *card, struct fw_packet *p)  		if (e == NULL)  			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); +		if (client->version < FW_CDEV_VERSION_EVENT_ASYNC_TSTAMP) { +			struct fw_cdev_event_phy_packet *pp = &e->phy_packet.without_tstamp; + +			pp->closure = client->phy_receiver_closure; +			pp->type = FW_CDEV_EVENT_PHY_PACKET_RECEIVED; +			pp->rcode = RCODE_COMPLETE; +			pp->length = 8; +			pp->data[0] = p->header[1]; +			pp->data[1] = p->header[2]; +			queue_event(client, &e->event, &e->phy_packet, sizeof(*pp) + 8, NULL, 0); +		} else { +			struct fw_cdev_event_phy_packet2 *pp = &e->phy_packet.with_tstamp; + +			pp = &e->phy_packet.with_tstamp; +			pp->closure = client->phy_receiver_closure; +			pp->type = FW_CDEV_EVENT_PHY_PACKET_RECEIVED2; +			pp->rcode = RCODE_COMPLETE; +			pp->length = 8; +			pp->tstamp = p->timestamp; +			pp->data[0] = p->header[1]; +			pp->data[1] = p->header[2]; +			queue_event(client, &e->event, &e->phy_packet, sizeof(*pp) + 8, NULL, 0); +		}  	}  	spin_unlock_irqrestore(&card->lock, flags);  | 
