Commit bf54e146 authored by Stefan Richter's avatar Stefan Richter

firewire: cdev: add PHY packet reception

Add an FW_CDEV_IOC_RECEIVE_PHY_PACKETS ioctl() and
FW_CDEV_EVENT_PHY_PACKET_RECEIVED poll()/read() event for /dev/fw*.
This can be used to get information from remote PHYs by remote access
PHY packets.

This is also the 2nd half of the functionality (the receive part) to
support a userspace implementation of a VersaPHY transaction layer.

Safety considerations:

  - PHY packets are generally broadcasts, hence some kind of elevated
    privileges should be required of a process to be able to listen in
    on PHY packets.  This implementation assumes that a process that is
    allowed to open the /dev/fw* of a local node does have this
    privilege.

    There was an inconclusive discussion about introducing POSIX
    capabilities as a means to check for user privileges for these
    kinds of operations.

Other limitations:

  - PHY packet reception may be switched on by ioctl() but cannot be
    switched off again.  It would be trivial to provide an off switch,
    but this is not worth the code.  The client should simply close()
    the fd then, or just ignore further events.

  - For sake of simplicity of API and kernel-side implementation, no
    filter per packet content is provided.
Signed-off-by: default avatarStefan Richter <stefanr@s5r6.in-berlin.de>
parent 850bb6f2
...@@ -500,6 +500,7 @@ void fw_card_initialize(struct fw_card *card, ...@@ -500,6 +500,7 @@ void fw_card_initialize(struct fw_card *card,
kref_init(&card->kref); kref_init(&card->kref);
init_completion(&card->done); init_completion(&card->done);
INIT_LIST_HEAD(&card->transaction_list); INIT_LIST_HEAD(&card->transaction_list);
INIT_LIST_HEAD(&card->phy_receiver_list);
spin_lock_init(&card->lock); spin_lock_init(&card->lock);
card->local_node = NULL; card->local_node = NULL;
......
...@@ -69,6 +69,9 @@ struct client { ...@@ -69,6 +69,9 @@ struct client {
struct fw_iso_buffer buffer; struct fw_iso_buffer buffer;
unsigned long vm_start; unsigned long vm_start;
struct list_head phy_receiver_link;
u64 phy_receiver_closure;
struct list_head link; struct list_head link;
struct kref kref; struct kref kref;
}; };
...@@ -201,6 +204,11 @@ struct outbound_phy_packet_event { ...@@ -201,6 +204,11 @@ struct outbound_phy_packet_event {
struct fw_cdev_event_phy_packet phy_packet; 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) static inline void __user *u64_to_uptr(__u64 value)
{ {
return (void __user *)(unsigned long)value; return (void __user *)(unsigned long)value;
...@@ -236,6 +244,7 @@ static int fw_device_op_open(struct inode *inode, struct file *file) ...@@ -236,6 +244,7 @@ static int fw_device_op_open(struct inode *inode, struct file *file)
idr_init(&client->resource_idr); idr_init(&client->resource_idr);
INIT_LIST_HEAD(&client->event_list); INIT_LIST_HEAD(&client->event_list);
init_waitqueue_head(&client->wait); init_waitqueue_head(&client->wait);
INIT_LIST_HEAD(&client->phy_receiver_link);
kref_init(&client->kref); kref_init(&client->kref);
file->private_data = client; file->private_data = client;
...@@ -357,7 +366,7 @@ static void queue_bus_reset_event(struct client *client) ...@@ -357,7 +366,7 @@ static void queue_bus_reset_event(struct client *client)
e = kzalloc(sizeof(*e), GFP_KERNEL); e = kzalloc(sizeof(*e), GFP_KERNEL);
if (e == NULL) { if (e == NULL) {
fw_notify("Out of memory when allocating bus reset event\n"); fw_notify("Out of memory when allocating event\n");
return; return;
} }
...@@ -404,6 +413,7 @@ union ioctl_arg { ...@@ -404,6 +413,7 @@ union ioctl_arg {
struct fw_cdev_send_stream_packet send_stream_packet; struct fw_cdev_send_stream_packet send_stream_packet;
struct fw_cdev_get_cycle_timer2 get_cycle_timer2; struct fw_cdev_get_cycle_timer2 get_cycle_timer2;
struct fw_cdev_send_phy_packet send_phy_packet; struct fw_cdev_send_phy_packet send_phy_packet;
struct fw_cdev_receive_phy_packets receive_phy_packets;
}; };
static int ioctl_get_info(struct client *client, union ioctl_arg *arg) static int ioctl_get_info(struct client *client, union ioctl_arg *arg)
...@@ -671,9 +681,10 @@ static void handle_request(struct fw_card *card, struct fw_request *request, ...@@ -671,9 +681,10 @@ static void handle_request(struct fw_card *card, struct fw_request *request,
r = kmalloc(sizeof(*r), GFP_ATOMIC); r = kmalloc(sizeof(*r), GFP_ATOMIC);
e = kmalloc(sizeof(*e), 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; goto failed;
}
r->card = card; r->card = card;
r->request = request; r->request = request;
r->data = payload; r->data = payload;
...@@ -902,9 +913,10 @@ static void iso_callback(struct fw_iso_context *context, u32 cycle, ...@@ -902,9 +913,10 @@ static void iso_callback(struct fw_iso_context *context, u32 cycle,
struct iso_interrupt_event *e; struct iso_interrupt_event *e;
e = kmalloc(sizeof(*e) + header_length, GFP_ATOMIC); e = kmalloc(sizeof(*e) + header_length, GFP_ATOMIC);
if (e == NULL) if (e == NULL) {
fw_notify("Out of memory when allocating event\n");
return; return;
}
e->interrupt.type = FW_CDEV_EVENT_ISO_INTERRUPT; e->interrupt.type = FW_CDEV_EVENT_ISO_INTERRUPT;
e->interrupt.closure = client->iso_closure; e->interrupt.closure = client->iso_closure;
e->interrupt.cycle = cycle; e->interrupt.cycle = cycle;
...@@ -1447,6 +1459,52 @@ static int ioctl_send_phy_packet(struct client *client, union ioctl_arg *arg) ...@@ -1447,6 +1459,52 @@ static int ioctl_send_phy_packet(struct client *client, union ioctl_arg *arg)
return 0; 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 *) = { static int (* const ioctl_handlers[])(struct client *, union ioctl_arg *) = {
[0x00] = ioctl_get_info, [0x00] = ioctl_get_info,
[0x01] = ioctl_send_request, [0x01] = ioctl_send_request,
...@@ -1470,6 +1528,7 @@ static int (* const ioctl_handlers[])(struct client *, union ioctl_arg *) = { ...@@ -1470,6 +1528,7 @@ static int (* const ioctl_handlers[])(struct client *, union ioctl_arg *) = {
[0x13] = ioctl_send_stream_packet, [0x13] = ioctl_send_stream_packet,
[0x14] = ioctl_get_cycle_timer2, [0x14] = ioctl_get_cycle_timer2,
[0x15] = ioctl_send_phy_packet, [0x15] = ioctl_send_phy_packet,
[0x16] = ioctl_receive_phy_packets,
}; };
static int dispatch_ioctl(struct client *client, static int dispatch_ioctl(struct client *client,
...@@ -1577,6 +1636,10 @@ static int fw_device_op_release(struct inode *inode, struct file *file) ...@@ -1577,6 +1636,10 @@ static int fw_device_op_release(struct inode *inode, struct file *file)
struct client *client = file->private_data; struct client *client = file->private_data;
struct event *event, *next_event; 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); mutex_lock(&client->device->client_list_mutex);
list_del(&client->link); list_del(&client->link);
mutex_unlock(&client->device->client_list_mutex); mutex_unlock(&client->device->client_list_mutex);
......
...@@ -883,6 +883,11 @@ void fw_core_handle_request(struct fw_card *card, struct fw_packet *p) ...@@ -883,6 +883,11 @@ void fw_core_handle_request(struct fw_card *card, struct fw_packet *p)
if (p->ack != ACK_PENDING && p->ack != ACK_COMPLETE) if (p->ack != ACK_PENDING && p->ack != ACK_COMPLETE)
return; return;
if (TCODE_IS_LINK_INTERNAL(HEADER_GET_TCODE(p->header[0]))) {
fw_cdev_handle_phy_packet(card, p);
return;
}
request = allocate_request(card, p); request = allocate_request(card, p);
if (request == NULL) { if (request == NULL) {
/* FIXME: send statically allocated busy packet. */ /* FIXME: send statically allocated busy packet. */
......
...@@ -128,6 +128,7 @@ extern const struct file_operations fw_device_ops; ...@@ -128,6 +128,7 @@ extern const struct file_operations fw_device_ops;
void fw_device_cdev_update(struct fw_device *device); void fw_device_cdev_update(struct fw_device *device);
void fw_device_cdev_remove(struct fw_device *device); void fw_device_cdev_remove(struct fw_device *device);
void fw_cdev_handle_phy_packet(struct fw_card *card, struct fw_packet *p);
/* -device */ /* -device */
...@@ -214,6 +215,7 @@ static inline bool is_next_generation(int new_generation, int old_generation) ...@@ -214,6 +215,7 @@ static inline bool is_next_generation(int new_generation, int old_generation)
#define TCODE_IS_READ_REQUEST(tcode) (((tcode) & ~1) == 4) #define TCODE_IS_READ_REQUEST(tcode) (((tcode) & ~1) == 4)
#define TCODE_IS_BLOCK_PACKET(tcode) (((tcode) & 1) != 0) #define TCODE_IS_BLOCK_PACKET(tcode) (((tcode) & 1) != 0)
#define TCODE_IS_LINK_INTERNAL(tcode) ((tcode) == 0xe)
#define TCODE_IS_REQUEST(tcode) (((tcode) & 2) == 0) #define TCODE_IS_REQUEST(tcode) (((tcode) & 2) == 0)
#define TCODE_IS_RESPONSE(tcode) (((tcode) & 2) != 0) #define TCODE_IS_RESPONSE(tcode) (((tcode) & 2) != 0)
#define TCODE_HAS_REQUEST_DATA(tcode) (((tcode) & 12) != 4) #define TCODE_HAS_REQUEST_DATA(tcode) (((tcode) & 12) != 4)
......
...@@ -1759,10 +1759,9 @@ static int ohci_enable(struct fw_card *card, ...@@ -1759,10 +1759,9 @@ static int ohci_enable(struct fw_card *card,
OHCI1394_HCControl_noByteSwapData); OHCI1394_HCControl_noByteSwapData);
reg_write(ohci, OHCI1394_SelfIDBuffer, ohci->self_id_bus); reg_write(ohci, OHCI1394_SelfIDBuffer, ohci->self_id_bus);
reg_write(ohci, OHCI1394_LinkControlClear,
OHCI1394_LinkControl_rcvPhyPkt);
reg_write(ohci, OHCI1394_LinkControlSet, reg_write(ohci, OHCI1394_LinkControlSet,
OHCI1394_LinkControl_rcvSelfID | OHCI1394_LinkControl_rcvSelfID |
OHCI1394_LinkControl_rcvPhyPkt |
OHCI1394_LinkControl_cycleTimerEnable | OHCI1394_LinkControl_cycleTimerEnable |
OHCI1394_LinkControl_cycleMaster); OHCI1394_LinkControl_cycleMaster);
......
...@@ -35,6 +35,7 @@ ...@@ -35,6 +35,7 @@
/* available since kernel version 2.6.36 */ /* available since kernel version 2.6.36 */
#define FW_CDEV_EVENT_REQUEST2 0x06 #define FW_CDEV_EVENT_REQUEST2 0x06
#define FW_CDEV_EVENT_PHY_PACKET_SENT 0x07 #define FW_CDEV_EVENT_PHY_PACKET_SENT 0x07
#define FW_CDEV_EVENT_PHY_PACKET_RECEIVED 0x08
/** /**
* struct fw_cdev_event_common - Common part of all fw_cdev_event_ types * struct fw_cdev_event_common - Common part of all fw_cdev_event_ types
...@@ -285,16 +286,24 @@ struct fw_cdev_event_iso_resource { ...@@ -285,16 +286,24 @@ struct fw_cdev_event_iso_resource {
}; };
/** /**
* struct fw_cdev_event_phy_packet - A PHY packet was transmitted * struct fw_cdev_event_phy_packet - A PHY packet was transmitted or received
* @closure: See &fw_cdev_event_common; * @closure: See &fw_cdev_event_common; set by %FW_CDEV_IOC_SEND_PHY_PACKET
* set by %FW_CDEV_IOC_SEND_PHY_PACKET ioctl * or %FW_CDEV_IOC_RECEIVE_PHY_PACKETS ioctl
* @type: %FW_CDEV_EVENT_PHY_PACKET_SENT * @type: %FW_CDEV_EVENT_PHY_PACKET_SENT or %..._RECEIVED
* @rcode: %RCODE_..., indicates success or failure of transmission * @rcode: %RCODE_..., indicates success or failure of transmission
* @length: Data length in bytes
* @data: Incoming data
*
* If @type is %FW_CDEV_EVENT_PHY_PACKET_SENT, @length is 0 and @data empty.
* If @type is %FW_CDEV_EVENT_PHY_PACKET_RECEIVED, @length is 8 and @data
* consists of the two PHY packet quadlets, in host byte order.
*/ */
struct fw_cdev_event_phy_packet { struct fw_cdev_event_phy_packet {
__u64 closure; __u64 closure;
__u32 type; __u32 type;
__u32 rcode; __u32 rcode;
__u32 length;
__u32 data[0];
}; };
/** /**
...@@ -308,7 +317,9 @@ struct fw_cdev_event_phy_packet { ...@@ -308,7 +317,9 @@ struct fw_cdev_event_phy_packet {
* @iso_resource: Valid if @common.type == * @iso_resource: Valid if @common.type ==
* %FW_CDEV_EVENT_ISO_RESOURCE_ALLOCATED or * %FW_CDEV_EVENT_ISO_RESOURCE_ALLOCATED or
* %FW_CDEV_EVENT_ISO_RESOURCE_DEALLOCATED * %FW_CDEV_EVENT_ISO_RESOURCE_DEALLOCATED
* @phy_packet: Valid if @common.type == %FW_CDEV_EVENT_PHY_PACKET_SENT * @phy_packet: Valid if @common.type ==
* %FW_CDEV_EVENT_PHY_PACKET_SENT or
* %FW_CDEV_EVENT_PHY_PACKET_RECEIVED
* *
* Convenience union for userspace use. Events could be read(2) into an * Convenience union for userspace use. Events could be read(2) into an
* appropriately aligned char buffer and then cast to this union for further * appropriately aligned char buffer and then cast to this union for further
...@@ -360,6 +371,7 @@ union fw_cdev_event { ...@@ -360,6 +371,7 @@ union fw_cdev_event {
/* available since kernel version 2.6.36 */ /* available since kernel version 2.6.36 */
#define FW_CDEV_IOC_SEND_PHY_PACKET _IOWR('#', 0x15, struct fw_cdev_send_phy_packet) #define FW_CDEV_IOC_SEND_PHY_PACKET _IOWR('#', 0x15, struct fw_cdev_send_phy_packet)
#define FW_CDEV_IOC_RECEIVE_PHY_PACKETS _IOW('#', 0x16, struct fw_cdev_receive_phy_packets)
/* /*
* ABI version history * ABI version history
...@@ -376,9 +388,9 @@ union fw_cdev_event { ...@@ -376,9 +388,9 @@ union fw_cdev_event {
* - shared use and auto-response for FCP registers * - shared use and auto-response for FCP registers
* 3 (2.6.34) - made &fw_cdev_get_cycle_timer reliable * 3 (2.6.34) - made &fw_cdev_get_cycle_timer reliable
* - added %FW_CDEV_IOC_GET_CYCLE_TIMER2 * - added %FW_CDEV_IOC_GET_CYCLE_TIMER2
* 4 (2.6.36) - added %FW_CDEV_EVENT_REQUEST2, %FW_CDEV_EVENT_PHY_PACKET_SENT * 4 (2.6.36) - added %FW_CDEV_EVENT_REQUEST2, %FW_CDEV_EVENT_PHY_PACKET_*
* - implemented &fw_cdev_event_bus_reset.bm_node_id * - implemented &fw_cdev_event_bus_reset.bm_node_id
* - added %FW_CDEV_IOC_SEND_PHY_PACKET * - added %FW_CDEV_IOC_SEND_PHY_PACKET, _RECEIVE_PHY_PACKETS
*/ */
#define FW_CDEV_VERSION 3 /* Meaningless; don't use this macro. */ #define FW_CDEV_VERSION 3 /* Meaningless; don't use this macro. */
...@@ -850,4 +862,17 @@ struct fw_cdev_send_phy_packet { ...@@ -850,4 +862,17 @@ struct fw_cdev_send_phy_packet {
__u32 generation; __u32 generation;
}; };
/**
* struct fw_cdev_receive_phy_packets - start reception of PHY packets
* @closure: Passed back to userspace in phy packet events
*
* This ioctl activates issuing of %FW_CDEV_EVENT_PHY_PACKET_RECEIVED due to
* incoming PHY packets from any node on the same bus as the device.
*
* The ioctl is only permitted on device files which represent a local node.
*/
struct fw_cdev_receive_phy_packets {
__u64 closure;
};
#endif /* _LINUX_FIREWIRE_CDEV_H */ #endif /* _LINUX_FIREWIRE_CDEV_H */
...@@ -111,9 +111,10 @@ struct fw_card { ...@@ -111,9 +111,10 @@ struct fw_card {
bool beta_repeaters_present; bool beta_repeaters_present;
int index; int index;
struct list_head link; struct list_head link;
struct list_head phy_receiver_list;
struct delayed_work br_work; /* bus reset job */ struct delayed_work br_work; /* bus reset job */
bool br_short; bool br_short;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment