diff options
| -rw-r--r-- | drivers/firewire/core-cdev.c | 64 | ||||
| -rw-r--r-- | include/linux/firewire-cdev.h | 44 |
2 files changed, 107 insertions, 1 deletions
diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c index acf4fa1f3f8c..f95719926487 100644 --- a/drivers/firewire/core-cdev.c +++ b/drivers/firewire/core-cdev.c | |||
| @@ -194,6 +194,13 @@ struct iso_resource_event { | |||
| 194 | struct fw_cdev_event_iso_resource iso_resource; | 194 | struct fw_cdev_event_iso_resource iso_resource; |
| 195 | }; | 195 | }; |
| 196 | 196 | ||
| 197 | struct outbound_phy_packet_event { | ||
| 198 | struct event event; | ||
| 199 | struct client *client; | ||
| 200 | struct fw_packet p; | ||
| 201 | struct fw_cdev_event_phy_packet phy_packet; | ||
| 202 | }; | ||
| 203 | |||
| 197 | static inline void __user *u64_to_uptr(__u64 value) | 204 | static inline void __user *u64_to_uptr(__u64 value) |
| 198 | { | 205 | { |
| 199 | return (void __user *)(unsigned long)value; | 206 | return (void __user *)(unsigned long)value; |
| @@ -396,6 +403,7 @@ union ioctl_arg { | |||
| 396 | struct fw_cdev_allocate_iso_resource allocate_iso_resource; | 403 | struct fw_cdev_allocate_iso_resource allocate_iso_resource; |
| 397 | struct fw_cdev_send_stream_packet send_stream_packet; | 404 | struct fw_cdev_send_stream_packet send_stream_packet; |
| 398 | struct fw_cdev_get_cycle_timer2 get_cycle_timer2; | 405 | struct fw_cdev_get_cycle_timer2 get_cycle_timer2; |
| 406 | struct fw_cdev_send_phy_packet send_phy_packet; | ||
| 399 | }; | 407 | }; |
| 400 | 408 | ||
| 401 | static int ioctl_get_info(struct client *client, union ioctl_arg *arg) | 409 | static int ioctl_get_info(struct client *client, union ioctl_arg *arg) |
| @@ -1384,6 +1392,61 @@ static int ioctl_send_stream_packet(struct client *client, union ioctl_arg *arg) | |||
| 1384 | return init_request(client, &request, dest, a->speed); | 1392 | return init_request(client, &request, dest, a->speed); |
| 1385 | } | 1393 | } |
| 1386 | 1394 | ||
| 1395 | static void outbound_phy_packet_callback(struct fw_packet *packet, | ||
| 1396 | struct fw_card *card, int status) | ||
| 1397 | { | ||
| 1398 | struct outbound_phy_packet_event *e = | ||
| 1399 | container_of(packet, struct outbound_phy_packet_event, p); | ||
| 1400 | |||
| 1401 | switch (status) { | ||
| 1402 | /* expected: */ | ||
| 1403 | case ACK_COMPLETE: e->phy_packet.rcode = RCODE_COMPLETE; break; | ||
| 1404 | /* should never happen with PHY packets: */ | ||
| 1405 | case ACK_PENDING: e->phy_packet.rcode = RCODE_COMPLETE; break; | ||
| 1406 | case ACK_BUSY_X: | ||
| 1407 | case ACK_BUSY_A: | ||
| 1408 | case ACK_BUSY_B: e->phy_packet.rcode = RCODE_BUSY; break; | ||
| 1409 | case ACK_DATA_ERROR: e->phy_packet.rcode = RCODE_DATA_ERROR; break; | ||
| 1410 | case ACK_TYPE_ERROR: e->phy_packet.rcode = RCODE_TYPE_ERROR; break; | ||
| 1411 | /* stale generation; cancelled; on certain controllers: no ack */ | ||
| 1412 | default: e->phy_packet.rcode = status; break; | ||
| 1413 | } | ||
| 1414 | |||
| 1415 | queue_event(e->client, &e->event, | ||
| 1416 | &e->phy_packet, sizeof(e->phy_packet), NULL, 0); | ||
| 1417 | client_put(e->client); | ||
| 1418 | } | ||
| 1419 | |||
| 1420 | static int ioctl_send_phy_packet(struct client *client, union ioctl_arg *arg) | ||
| 1421 | { | ||
| 1422 | struct fw_cdev_send_phy_packet *a = &arg->send_phy_packet; | ||
| 1423 | struct fw_card *card = client->device->card; | ||
| 1424 | struct outbound_phy_packet_event *e; | ||
| 1425 | |||
| 1426 | /* Access policy: Allow this ioctl only on local nodes' device files. */ | ||
| 1427 | if (!client->device->is_local) | ||
| 1428 | return -ENOSYS; | ||
| 1429 | |||
| 1430 | e = kzalloc(sizeof(*e), GFP_KERNEL); | ||
| 1431 | if (e == NULL) | ||
| 1432 | return -ENOMEM; | ||
| 1433 | |||
| 1434 | client_get(client); | ||
| 1435 | e->client = client; | ||
| 1436 | e->p.speed = SCODE_100; | ||
| 1437 | e->p.generation = a->generation; | ||
| 1438 | e->p.header[0] = a->data[0]; | ||
| 1439 | e->p.header[1] = a->data[1]; | ||
| 1440 | e->p.header_length = 8; | ||
| 1441 | e->p.callback = outbound_phy_packet_callback; | ||
| 1442 | e->phy_packet.closure = a->closure; | ||
| 1443 | e->phy_packet.type = FW_CDEV_EVENT_PHY_PACKET_SENT; | ||
| 1444 | |||
| 1445 | card->driver->send_request(card, &e->p); | ||
| 1446 | |||
| 1447 | return 0; | ||
| 1448 | } | ||
| 1449 | |||
| 1387 | static int (* const ioctl_handlers[])(struct client *, union ioctl_arg *) = { | 1450 | static int (* const ioctl_handlers[])(struct client *, union ioctl_arg *) = { |
| 1388 | [0x00] = ioctl_get_info, | 1451 | [0x00] = ioctl_get_info, |
| 1389 | [0x01] = ioctl_send_request, | 1452 | [0x01] = ioctl_send_request, |
| @@ -1406,6 +1469,7 @@ static int (* const ioctl_handlers[])(struct client *, union ioctl_arg *) = { | |||
| 1406 | [0x12] = ioctl_send_broadcast_request, | 1469 | [0x12] = ioctl_send_broadcast_request, |
| 1407 | [0x13] = ioctl_send_stream_packet, | 1470 | [0x13] = ioctl_send_stream_packet, |
| 1408 | [0x14] = ioctl_get_cycle_timer2, | 1471 | [0x14] = ioctl_get_cycle_timer2, |
| 1472 | [0x15] = ioctl_send_phy_packet, | ||
| 1409 | }; | 1473 | }; |
| 1410 | 1474 | ||
| 1411 | static int dispatch_ioctl(struct client *client, | 1475 | static int dispatch_ioctl(struct client *client, |
diff --git a/include/linux/firewire-cdev.h b/include/linux/firewire-cdev.h index fde9568151d5..5bc051b9a013 100644 --- a/include/linux/firewire-cdev.h +++ b/include/linux/firewire-cdev.h | |||
| @@ -34,6 +34,7 @@ | |||
| 34 | 34 | ||
| 35 | /* available since kernel version 2.6.36 */ | 35 | /* available since kernel version 2.6.36 */ |
| 36 | #define FW_CDEV_EVENT_REQUEST2 0x06 | 36 | #define FW_CDEV_EVENT_REQUEST2 0x06 |
| 37 | #define FW_CDEV_EVENT_PHY_PACKET_SENT 0x07 | ||
| 37 | 38 | ||
| 38 | /** | 39 | /** |
| 39 | * struct fw_cdev_event_common - Common part of all fw_cdev_event_ types | 40 | * struct fw_cdev_event_common - Common part of all fw_cdev_event_ types |
| @@ -284,6 +285,19 @@ struct fw_cdev_event_iso_resource { | |||
| 284 | }; | 285 | }; |
| 285 | 286 | ||
| 286 | /** | 287 | /** |
| 288 | * struct fw_cdev_event_phy_packet - A PHY packet was transmitted | ||
| 289 | * @closure: See &fw_cdev_event_common; | ||
| 290 | * set by %FW_CDEV_IOC_SEND_PHY_PACKET ioctl | ||
| 291 | * @type: %FW_CDEV_EVENT_PHY_PACKET_SENT | ||
| 292 | * @rcode: %RCODE_..., indicates success or failure of transmission | ||
| 293 | */ | ||
| 294 | struct fw_cdev_event_phy_packet { | ||
| 295 | __u64 closure; | ||
| 296 | __u32 type; | ||
| 297 | __u32 rcode; | ||
| 298 | }; | ||
| 299 | |||
| 300 | /** | ||
| 287 | * union fw_cdev_event - Convenience union of fw_cdev_event_ types | 301 | * union fw_cdev_event - Convenience union of fw_cdev_event_ types |
| 288 | * @common: Valid for all types | 302 | * @common: Valid for all types |
| 289 | * @bus_reset: Valid if @common.type == %FW_CDEV_EVENT_BUS_RESET | 303 | * @bus_reset: Valid if @common.type == %FW_CDEV_EVENT_BUS_RESET |
| @@ -294,6 +308,7 @@ struct fw_cdev_event_iso_resource { | |||
| 294 | * @iso_resource: Valid if @common.type == | 308 | * @iso_resource: Valid if @common.type == |
| 295 | * %FW_CDEV_EVENT_ISO_RESOURCE_ALLOCATED or | 309 | * %FW_CDEV_EVENT_ISO_RESOURCE_ALLOCATED or |
| 296 | * %FW_CDEV_EVENT_ISO_RESOURCE_DEALLOCATED | 310 | * %FW_CDEV_EVENT_ISO_RESOURCE_DEALLOCATED |
| 311 | * @phy_packet: Valid if @common.type == %FW_CDEV_EVENT_PHY_PACKET_SENT | ||
| 297 | * | 312 | * |
| 298 | * Convenience union for userspace use. Events could be read(2) into an | 313 | * Convenience union for userspace use. Events could be read(2) into an |
| 299 | * appropriately aligned char buffer and then cast to this union for further | 314 | * appropriately aligned char buffer and then cast to this union for further |
| @@ -311,6 +326,7 @@ union fw_cdev_event { | |||
| 311 | struct fw_cdev_event_request2 request2; /* added in 2.6.36 */ | 326 | struct fw_cdev_event_request2 request2; /* added in 2.6.36 */ |
| 312 | struct fw_cdev_event_iso_interrupt iso_interrupt; | 327 | struct fw_cdev_event_iso_interrupt iso_interrupt; |
| 313 | struct fw_cdev_event_iso_resource iso_resource; /* added in 2.6.30 */ | 328 | struct fw_cdev_event_iso_resource iso_resource; /* added in 2.6.30 */ |
| 329 | struct fw_cdev_event_phy_packet phy_packet; /* added in 2.6.36 */ | ||
| 314 | }; | 330 | }; |
| 315 | 331 | ||
| 316 | /* available since kernel version 2.6.22 */ | 332 | /* available since kernel version 2.6.22 */ |
| @@ -342,6 +358,9 @@ union fw_cdev_event { | |||
| 342 | /* available since kernel version 2.6.34 */ | 358 | /* available since kernel version 2.6.34 */ |
| 343 | #define FW_CDEV_IOC_GET_CYCLE_TIMER2 _IOWR('#', 0x14, struct fw_cdev_get_cycle_timer2) | 359 | #define FW_CDEV_IOC_GET_CYCLE_TIMER2 _IOWR('#', 0x14, struct fw_cdev_get_cycle_timer2) |
| 344 | 360 | ||
| 361 | /* available since kernel version 2.6.36 */ | ||
| 362 | #define FW_CDEV_IOC_SEND_PHY_PACKET _IOWR('#', 0x15, struct fw_cdev_send_phy_packet) | ||
| 363 | |||
| 345 | /* | 364 | /* |
| 346 | * ABI version history | 365 | * ABI version history |
| 347 | * 1 (2.6.22) - initial version | 366 | * 1 (2.6.22) - initial version |
| @@ -357,8 +376,9 @@ union fw_cdev_event { | |||
| 357 | * - shared use and auto-response for FCP registers | 376 | * - shared use and auto-response for FCP registers |
| 358 | * 3 (2.6.34) - made &fw_cdev_get_cycle_timer reliable | 377 | * 3 (2.6.34) - made &fw_cdev_get_cycle_timer reliable |
| 359 | * - added %FW_CDEV_IOC_GET_CYCLE_TIMER2 | 378 | * - added %FW_CDEV_IOC_GET_CYCLE_TIMER2 |
| 360 | * 4 (2.6.36) - added %FW_CDEV_EVENT_REQUEST2 | 379 | * 4 (2.6.36) - added %FW_CDEV_EVENT_REQUEST2, %FW_CDEV_EVENT_PHY_PACKET_SENT |
| 361 | * - implemented &fw_cdev_event_bus_reset.bm_node_id | 380 | * - implemented &fw_cdev_event_bus_reset.bm_node_id |
| 381 | * - added %FW_CDEV_IOC_SEND_PHY_PACKET | ||
| 362 | */ | 382 | */ |
| 363 | #define FW_CDEV_VERSION 3 /* Meaningless; don't use this macro. */ | 383 | #define FW_CDEV_VERSION 3 /* Meaningless; don't use this macro. */ |
| 364 | 384 | ||
| @@ -808,4 +828,26 @@ struct fw_cdev_send_stream_packet { | |||
| 808 | __u32 speed; | 828 | __u32 speed; |
| 809 | }; | 829 | }; |
| 810 | 830 | ||
| 831 | /** | ||
| 832 | * struct fw_cdev_send_phy_packet - send a PHY packet | ||
| 833 | * @closure: Passed back to userspace in the PHY-packet-sent event | ||
| 834 | * @data: First and second quadlet of the PHY packet | ||
| 835 | * @generation: The bus generation where packet is valid | ||
| 836 | * | ||
| 837 | * The %FW_CDEV_IOC_SEND_PHY_PACKET ioctl sends a PHY packet to all nodes | ||
| 838 | * on the same card as this device. After transmission, an | ||
| 839 | * %FW_CDEV_EVENT_PHY_PACKET_SENT event is generated. | ||
| 840 | * | ||
| 841 | * The payload @data[] shall be specified in host byte order. Usually, | ||
| 842 | * @data[1] needs to be the bitwise inverse of @data[0]. VersaPHY packets | ||
| 843 | * are an exception to this rule. | ||
| 844 | * | ||
| 845 | * The ioctl is only permitted on device files which represent a local node. | ||
| 846 | */ | ||
| 847 | struct fw_cdev_send_phy_packet { | ||
| 848 | __u64 closure; | ||
| 849 | __u32 data[2]; | ||
| 850 | __u32 generation; | ||
| 851 | }; | ||
| 852 | |||
| 811 | #endif /* _LINUX_FIREWIRE_CDEV_H */ | 853 | #endif /* _LINUX_FIREWIRE_CDEV_H */ |
