18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Char device for device raw access 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2005-2007 Kristian Hoegsberg <krh@bitplanet.net> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/bug.h> 98c2ecf20Sopenharmony_ci#include <linux/compat.h> 108c2ecf20Sopenharmony_ci#include <linux/delay.h> 118c2ecf20Sopenharmony_ci#include <linux/device.h> 128c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 138c2ecf20Sopenharmony_ci#include <linux/errno.h> 148c2ecf20Sopenharmony_ci#include <linux/firewire.h> 158c2ecf20Sopenharmony_ci#include <linux/firewire-cdev.h> 168c2ecf20Sopenharmony_ci#include <linux/idr.h> 178c2ecf20Sopenharmony_ci#include <linux/irqflags.h> 188c2ecf20Sopenharmony_ci#include <linux/jiffies.h> 198c2ecf20Sopenharmony_ci#include <linux/kernel.h> 208c2ecf20Sopenharmony_ci#include <linux/kref.h> 218c2ecf20Sopenharmony_ci#include <linux/mm.h> 228c2ecf20Sopenharmony_ci#include <linux/module.h> 238c2ecf20Sopenharmony_ci#include <linux/mutex.h> 248c2ecf20Sopenharmony_ci#include <linux/poll.h> 258c2ecf20Sopenharmony_ci#include <linux/sched.h> /* required for linux/wait.h */ 268c2ecf20Sopenharmony_ci#include <linux/slab.h> 278c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 288c2ecf20Sopenharmony_ci#include <linux/string.h> 298c2ecf20Sopenharmony_ci#include <linux/time.h> 308c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 318c2ecf20Sopenharmony_ci#include <linux/vmalloc.h> 328c2ecf20Sopenharmony_ci#include <linux/wait.h> 338c2ecf20Sopenharmony_ci#include <linux/workqueue.h> 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci#include "core.h" 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci/* 398c2ecf20Sopenharmony_ci * ABI version history is documented in linux/firewire-cdev.h. 408c2ecf20Sopenharmony_ci */ 418c2ecf20Sopenharmony_ci#define FW_CDEV_KERNEL_VERSION 5 428c2ecf20Sopenharmony_ci#define FW_CDEV_VERSION_EVENT_REQUEST2 4 438c2ecf20Sopenharmony_ci#define FW_CDEV_VERSION_ALLOCATE_REGION_END 4 448c2ecf20Sopenharmony_ci#define FW_CDEV_VERSION_AUTO_FLUSH_ISO_OVERFLOW 5 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_cistruct client { 478c2ecf20Sopenharmony_ci u32 version; 488c2ecf20Sopenharmony_ci struct fw_device *device; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci spinlock_t lock; 518c2ecf20Sopenharmony_ci bool in_shutdown; 528c2ecf20Sopenharmony_ci struct idr resource_idr; 538c2ecf20Sopenharmony_ci struct list_head event_list; 548c2ecf20Sopenharmony_ci wait_queue_head_t wait; 558c2ecf20Sopenharmony_ci wait_queue_head_t tx_flush_wait; 568c2ecf20Sopenharmony_ci u64 bus_reset_closure; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci struct fw_iso_context *iso_context; 598c2ecf20Sopenharmony_ci u64 iso_closure; 608c2ecf20Sopenharmony_ci struct fw_iso_buffer buffer; 618c2ecf20Sopenharmony_ci unsigned long vm_start; 628c2ecf20Sopenharmony_ci bool buffer_is_mapped; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci struct list_head phy_receiver_link; 658c2ecf20Sopenharmony_ci u64 phy_receiver_closure; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci struct list_head link; 688c2ecf20Sopenharmony_ci struct kref kref; 698c2ecf20Sopenharmony_ci}; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_cistatic inline void client_get(struct client *client) 728c2ecf20Sopenharmony_ci{ 738c2ecf20Sopenharmony_ci kref_get(&client->kref); 748c2ecf20Sopenharmony_ci} 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_cistatic void client_release(struct kref *kref) 778c2ecf20Sopenharmony_ci{ 788c2ecf20Sopenharmony_ci struct client *client = container_of(kref, struct client, kref); 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci fw_device_put(client->device); 818c2ecf20Sopenharmony_ci kfree(client); 828c2ecf20Sopenharmony_ci} 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_cistatic void client_put(struct client *client) 858c2ecf20Sopenharmony_ci{ 868c2ecf20Sopenharmony_ci kref_put(&client->kref, client_release); 878c2ecf20Sopenharmony_ci} 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_cistruct client_resource; 908c2ecf20Sopenharmony_citypedef void (*client_resource_release_fn_t)(struct client *, 918c2ecf20Sopenharmony_ci struct client_resource *); 928c2ecf20Sopenharmony_cistruct client_resource { 938c2ecf20Sopenharmony_ci client_resource_release_fn_t release; 948c2ecf20Sopenharmony_ci int handle; 958c2ecf20Sopenharmony_ci}; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_cistruct address_handler_resource { 988c2ecf20Sopenharmony_ci struct client_resource resource; 998c2ecf20Sopenharmony_ci struct fw_address_handler handler; 1008c2ecf20Sopenharmony_ci __u64 closure; 1018c2ecf20Sopenharmony_ci struct client *client; 1028c2ecf20Sopenharmony_ci}; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_cistruct outbound_transaction_resource { 1058c2ecf20Sopenharmony_ci struct client_resource resource; 1068c2ecf20Sopenharmony_ci struct fw_transaction transaction; 1078c2ecf20Sopenharmony_ci}; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_cistruct inbound_transaction_resource { 1108c2ecf20Sopenharmony_ci struct client_resource resource; 1118c2ecf20Sopenharmony_ci struct fw_card *card; 1128c2ecf20Sopenharmony_ci struct fw_request *request; 1138c2ecf20Sopenharmony_ci void *data; 1148c2ecf20Sopenharmony_ci size_t length; 1158c2ecf20Sopenharmony_ci}; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_cistruct descriptor_resource { 1188c2ecf20Sopenharmony_ci struct client_resource resource; 1198c2ecf20Sopenharmony_ci struct fw_descriptor descriptor; 1208c2ecf20Sopenharmony_ci u32 data[]; 1218c2ecf20Sopenharmony_ci}; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_cistruct iso_resource { 1248c2ecf20Sopenharmony_ci struct client_resource resource; 1258c2ecf20Sopenharmony_ci struct client *client; 1268c2ecf20Sopenharmony_ci /* Schedule work and access todo only with client->lock held. */ 1278c2ecf20Sopenharmony_ci struct delayed_work work; 1288c2ecf20Sopenharmony_ci enum {ISO_RES_ALLOC, ISO_RES_REALLOC, ISO_RES_DEALLOC, 1298c2ecf20Sopenharmony_ci ISO_RES_ALLOC_ONCE, ISO_RES_DEALLOC_ONCE,} todo; 1308c2ecf20Sopenharmony_ci int generation; 1318c2ecf20Sopenharmony_ci u64 channels; 1328c2ecf20Sopenharmony_ci s32 bandwidth; 1338c2ecf20Sopenharmony_ci struct iso_resource_event *e_alloc, *e_dealloc; 1348c2ecf20Sopenharmony_ci}; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_cistatic void release_iso_resource(struct client *, struct client_resource *); 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_cistatic void schedule_iso_resource(struct iso_resource *r, unsigned long delay) 1398c2ecf20Sopenharmony_ci{ 1408c2ecf20Sopenharmony_ci client_get(r->client); 1418c2ecf20Sopenharmony_ci if (!queue_delayed_work(fw_workqueue, &r->work, delay)) 1428c2ecf20Sopenharmony_ci client_put(r->client); 1438c2ecf20Sopenharmony_ci} 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_cistatic void schedule_if_iso_resource(struct client_resource *resource) 1468c2ecf20Sopenharmony_ci{ 1478c2ecf20Sopenharmony_ci if (resource->release == release_iso_resource) 1488c2ecf20Sopenharmony_ci schedule_iso_resource(container_of(resource, 1498c2ecf20Sopenharmony_ci struct iso_resource, resource), 0); 1508c2ecf20Sopenharmony_ci} 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci/* 1538c2ecf20Sopenharmony_ci * dequeue_event() just kfree()'s the event, so the event has to be 1548c2ecf20Sopenharmony_ci * the first field in a struct XYZ_event. 1558c2ecf20Sopenharmony_ci */ 1568c2ecf20Sopenharmony_cistruct event { 1578c2ecf20Sopenharmony_ci struct { void *data; size_t size; } v[2]; 1588c2ecf20Sopenharmony_ci struct list_head link; 1598c2ecf20Sopenharmony_ci}; 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_cistruct bus_reset_event { 1628c2ecf20Sopenharmony_ci struct event event; 1638c2ecf20Sopenharmony_ci struct fw_cdev_event_bus_reset reset; 1648c2ecf20Sopenharmony_ci}; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_cistruct outbound_transaction_event { 1678c2ecf20Sopenharmony_ci struct event event; 1688c2ecf20Sopenharmony_ci struct client *client; 1698c2ecf20Sopenharmony_ci struct outbound_transaction_resource r; 1708c2ecf20Sopenharmony_ci struct fw_cdev_event_response response; 1718c2ecf20Sopenharmony_ci}; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_cistruct inbound_transaction_event { 1748c2ecf20Sopenharmony_ci struct event event; 1758c2ecf20Sopenharmony_ci union { 1768c2ecf20Sopenharmony_ci struct fw_cdev_event_request request; 1778c2ecf20Sopenharmony_ci struct fw_cdev_event_request2 request2; 1788c2ecf20Sopenharmony_ci } req; 1798c2ecf20Sopenharmony_ci}; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_cistruct iso_interrupt_event { 1828c2ecf20Sopenharmony_ci struct event event; 1838c2ecf20Sopenharmony_ci struct fw_cdev_event_iso_interrupt interrupt; 1848c2ecf20Sopenharmony_ci}; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_cistruct iso_interrupt_mc_event { 1878c2ecf20Sopenharmony_ci struct event event; 1888c2ecf20Sopenharmony_ci struct fw_cdev_event_iso_interrupt_mc interrupt; 1898c2ecf20Sopenharmony_ci}; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_cistruct iso_resource_event { 1928c2ecf20Sopenharmony_ci struct event event; 1938c2ecf20Sopenharmony_ci struct fw_cdev_event_iso_resource iso_resource; 1948c2ecf20Sopenharmony_ci}; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_cistruct outbound_phy_packet_event { 1978c2ecf20Sopenharmony_ci struct event event; 1988c2ecf20Sopenharmony_ci struct client *client; 1998c2ecf20Sopenharmony_ci struct fw_packet p; 2008c2ecf20Sopenharmony_ci struct fw_cdev_event_phy_packet phy_packet; 2018c2ecf20Sopenharmony_ci}; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_cistruct inbound_phy_packet_event { 2048c2ecf20Sopenharmony_ci struct event event; 2058c2ecf20Sopenharmony_ci struct fw_cdev_event_phy_packet phy_packet; 2068c2ecf20Sopenharmony_ci}; 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci#ifdef CONFIG_COMPAT 2098c2ecf20Sopenharmony_cistatic void __user *u64_to_uptr(u64 value) 2108c2ecf20Sopenharmony_ci{ 2118c2ecf20Sopenharmony_ci if (in_compat_syscall()) 2128c2ecf20Sopenharmony_ci return compat_ptr(value); 2138c2ecf20Sopenharmony_ci else 2148c2ecf20Sopenharmony_ci return (void __user *)(unsigned long)value; 2158c2ecf20Sopenharmony_ci} 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_cistatic u64 uptr_to_u64(void __user *ptr) 2188c2ecf20Sopenharmony_ci{ 2198c2ecf20Sopenharmony_ci if (in_compat_syscall()) 2208c2ecf20Sopenharmony_ci return ptr_to_compat(ptr); 2218c2ecf20Sopenharmony_ci else 2228c2ecf20Sopenharmony_ci return (u64)(unsigned long)ptr; 2238c2ecf20Sopenharmony_ci} 2248c2ecf20Sopenharmony_ci#else 2258c2ecf20Sopenharmony_cistatic inline void __user *u64_to_uptr(u64 value) 2268c2ecf20Sopenharmony_ci{ 2278c2ecf20Sopenharmony_ci return (void __user *)(unsigned long)value; 2288c2ecf20Sopenharmony_ci} 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_cistatic inline u64 uptr_to_u64(void __user *ptr) 2318c2ecf20Sopenharmony_ci{ 2328c2ecf20Sopenharmony_ci return (u64)(unsigned long)ptr; 2338c2ecf20Sopenharmony_ci} 2348c2ecf20Sopenharmony_ci#endif /* CONFIG_COMPAT */ 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_cistatic int fw_device_op_open(struct inode *inode, struct file *file) 2378c2ecf20Sopenharmony_ci{ 2388c2ecf20Sopenharmony_ci struct fw_device *device; 2398c2ecf20Sopenharmony_ci struct client *client; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci device = fw_device_get_by_devt(inode->i_rdev); 2428c2ecf20Sopenharmony_ci if (device == NULL) 2438c2ecf20Sopenharmony_ci return -ENODEV; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci if (fw_device_is_shutdown(device)) { 2468c2ecf20Sopenharmony_ci fw_device_put(device); 2478c2ecf20Sopenharmony_ci return -ENODEV; 2488c2ecf20Sopenharmony_ci } 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci client = kzalloc(sizeof(*client), GFP_KERNEL); 2518c2ecf20Sopenharmony_ci if (client == NULL) { 2528c2ecf20Sopenharmony_ci fw_device_put(device); 2538c2ecf20Sopenharmony_ci return -ENOMEM; 2548c2ecf20Sopenharmony_ci } 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci client->device = device; 2578c2ecf20Sopenharmony_ci spin_lock_init(&client->lock); 2588c2ecf20Sopenharmony_ci idr_init(&client->resource_idr); 2598c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&client->event_list); 2608c2ecf20Sopenharmony_ci init_waitqueue_head(&client->wait); 2618c2ecf20Sopenharmony_ci init_waitqueue_head(&client->tx_flush_wait); 2628c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&client->phy_receiver_link); 2638c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&client->link); 2648c2ecf20Sopenharmony_ci kref_init(&client->kref); 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci file->private_data = client; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci return nonseekable_open(inode, file); 2698c2ecf20Sopenharmony_ci} 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_cistatic void queue_event(struct client *client, struct event *event, 2728c2ecf20Sopenharmony_ci void *data0, size_t size0, void *data1, size_t size1) 2738c2ecf20Sopenharmony_ci{ 2748c2ecf20Sopenharmony_ci unsigned long flags; 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci event->v[0].data = data0; 2778c2ecf20Sopenharmony_ci event->v[0].size = size0; 2788c2ecf20Sopenharmony_ci event->v[1].data = data1; 2798c2ecf20Sopenharmony_ci event->v[1].size = size1; 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci spin_lock_irqsave(&client->lock, flags); 2828c2ecf20Sopenharmony_ci if (client->in_shutdown) 2838c2ecf20Sopenharmony_ci kfree(event); 2848c2ecf20Sopenharmony_ci else 2858c2ecf20Sopenharmony_ci list_add_tail(&event->link, &client->event_list); 2868c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&client->lock, flags); 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci wake_up_interruptible(&client->wait); 2898c2ecf20Sopenharmony_ci} 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_cistatic int dequeue_event(struct client *client, 2928c2ecf20Sopenharmony_ci char __user *buffer, size_t count) 2938c2ecf20Sopenharmony_ci{ 2948c2ecf20Sopenharmony_ci struct event *event; 2958c2ecf20Sopenharmony_ci size_t size, total; 2968c2ecf20Sopenharmony_ci int i, ret; 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci ret = wait_event_interruptible(client->wait, 2998c2ecf20Sopenharmony_ci !list_empty(&client->event_list) || 3008c2ecf20Sopenharmony_ci fw_device_is_shutdown(client->device)); 3018c2ecf20Sopenharmony_ci if (ret < 0) 3028c2ecf20Sopenharmony_ci return ret; 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci if (list_empty(&client->event_list) && 3058c2ecf20Sopenharmony_ci fw_device_is_shutdown(client->device)) 3068c2ecf20Sopenharmony_ci return -ENODEV; 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci spin_lock_irq(&client->lock); 3098c2ecf20Sopenharmony_ci event = list_first_entry(&client->event_list, struct event, link); 3108c2ecf20Sopenharmony_ci list_del(&event->link); 3118c2ecf20Sopenharmony_ci spin_unlock_irq(&client->lock); 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci total = 0; 3148c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(event->v) && total < count; i++) { 3158c2ecf20Sopenharmony_ci size = min(event->v[i].size, count - total); 3168c2ecf20Sopenharmony_ci if (copy_to_user(buffer + total, event->v[i].data, size)) { 3178c2ecf20Sopenharmony_ci ret = -EFAULT; 3188c2ecf20Sopenharmony_ci goto out; 3198c2ecf20Sopenharmony_ci } 3208c2ecf20Sopenharmony_ci total += size; 3218c2ecf20Sopenharmony_ci } 3228c2ecf20Sopenharmony_ci ret = total; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci out: 3258c2ecf20Sopenharmony_ci kfree(event); 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci return ret; 3288c2ecf20Sopenharmony_ci} 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_cistatic ssize_t fw_device_op_read(struct file *file, char __user *buffer, 3318c2ecf20Sopenharmony_ci size_t count, loff_t *offset) 3328c2ecf20Sopenharmony_ci{ 3338c2ecf20Sopenharmony_ci struct client *client = file->private_data; 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci return dequeue_event(client, buffer, count); 3368c2ecf20Sopenharmony_ci} 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_cistatic void fill_bus_reset_event(struct fw_cdev_event_bus_reset *event, 3398c2ecf20Sopenharmony_ci struct client *client) 3408c2ecf20Sopenharmony_ci{ 3418c2ecf20Sopenharmony_ci struct fw_card *card = client->device->card; 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci spin_lock_irq(&card->lock); 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci event->closure = client->bus_reset_closure; 3468c2ecf20Sopenharmony_ci event->type = FW_CDEV_EVENT_BUS_RESET; 3478c2ecf20Sopenharmony_ci event->generation = client->device->generation; 3488c2ecf20Sopenharmony_ci event->node_id = client->device->node_id; 3498c2ecf20Sopenharmony_ci event->local_node_id = card->local_node->node_id; 3508c2ecf20Sopenharmony_ci event->bm_node_id = card->bm_node_id; 3518c2ecf20Sopenharmony_ci event->irm_node_id = card->irm_node->node_id; 3528c2ecf20Sopenharmony_ci event->root_node_id = card->root_node->node_id; 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci spin_unlock_irq(&card->lock); 3558c2ecf20Sopenharmony_ci} 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_cistatic void for_each_client(struct fw_device *device, 3588c2ecf20Sopenharmony_ci void (*callback)(struct client *client)) 3598c2ecf20Sopenharmony_ci{ 3608c2ecf20Sopenharmony_ci struct client *c; 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci mutex_lock(&device->client_list_mutex); 3638c2ecf20Sopenharmony_ci list_for_each_entry(c, &device->client_list, link) 3648c2ecf20Sopenharmony_ci callback(c); 3658c2ecf20Sopenharmony_ci mutex_unlock(&device->client_list_mutex); 3668c2ecf20Sopenharmony_ci} 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_cistatic int schedule_reallocations(int id, void *p, void *data) 3698c2ecf20Sopenharmony_ci{ 3708c2ecf20Sopenharmony_ci schedule_if_iso_resource(p); 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci return 0; 3738c2ecf20Sopenharmony_ci} 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_cistatic void queue_bus_reset_event(struct client *client) 3768c2ecf20Sopenharmony_ci{ 3778c2ecf20Sopenharmony_ci struct bus_reset_event *e; 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci e = kzalloc(sizeof(*e), GFP_KERNEL); 3808c2ecf20Sopenharmony_ci if (e == NULL) 3818c2ecf20Sopenharmony_ci return; 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci fill_bus_reset_event(&e->reset, client); 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci queue_event(client, &e->event, 3868c2ecf20Sopenharmony_ci &e->reset, sizeof(e->reset), NULL, 0); 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci spin_lock_irq(&client->lock); 3898c2ecf20Sopenharmony_ci idr_for_each(&client->resource_idr, schedule_reallocations, client); 3908c2ecf20Sopenharmony_ci spin_unlock_irq(&client->lock); 3918c2ecf20Sopenharmony_ci} 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_civoid fw_device_cdev_update(struct fw_device *device) 3948c2ecf20Sopenharmony_ci{ 3958c2ecf20Sopenharmony_ci for_each_client(device, queue_bus_reset_event); 3968c2ecf20Sopenharmony_ci} 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_cistatic void wake_up_client(struct client *client) 3998c2ecf20Sopenharmony_ci{ 4008c2ecf20Sopenharmony_ci wake_up_interruptible(&client->wait); 4018c2ecf20Sopenharmony_ci} 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_civoid fw_device_cdev_remove(struct fw_device *device) 4048c2ecf20Sopenharmony_ci{ 4058c2ecf20Sopenharmony_ci for_each_client(device, wake_up_client); 4068c2ecf20Sopenharmony_ci} 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ciunion ioctl_arg { 4098c2ecf20Sopenharmony_ci struct fw_cdev_get_info get_info; 4108c2ecf20Sopenharmony_ci struct fw_cdev_send_request send_request; 4118c2ecf20Sopenharmony_ci struct fw_cdev_allocate allocate; 4128c2ecf20Sopenharmony_ci struct fw_cdev_deallocate deallocate; 4138c2ecf20Sopenharmony_ci struct fw_cdev_send_response send_response; 4148c2ecf20Sopenharmony_ci struct fw_cdev_initiate_bus_reset initiate_bus_reset; 4158c2ecf20Sopenharmony_ci struct fw_cdev_add_descriptor add_descriptor; 4168c2ecf20Sopenharmony_ci struct fw_cdev_remove_descriptor remove_descriptor; 4178c2ecf20Sopenharmony_ci struct fw_cdev_create_iso_context create_iso_context; 4188c2ecf20Sopenharmony_ci struct fw_cdev_queue_iso queue_iso; 4198c2ecf20Sopenharmony_ci struct fw_cdev_start_iso start_iso; 4208c2ecf20Sopenharmony_ci struct fw_cdev_stop_iso stop_iso; 4218c2ecf20Sopenharmony_ci struct fw_cdev_get_cycle_timer get_cycle_timer; 4228c2ecf20Sopenharmony_ci struct fw_cdev_allocate_iso_resource allocate_iso_resource; 4238c2ecf20Sopenharmony_ci struct fw_cdev_send_stream_packet send_stream_packet; 4248c2ecf20Sopenharmony_ci struct fw_cdev_get_cycle_timer2 get_cycle_timer2; 4258c2ecf20Sopenharmony_ci struct fw_cdev_send_phy_packet send_phy_packet; 4268c2ecf20Sopenharmony_ci struct fw_cdev_receive_phy_packets receive_phy_packets; 4278c2ecf20Sopenharmony_ci struct fw_cdev_set_iso_channels set_iso_channels; 4288c2ecf20Sopenharmony_ci struct fw_cdev_flush_iso flush_iso; 4298c2ecf20Sopenharmony_ci}; 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_cistatic int ioctl_get_info(struct client *client, union ioctl_arg *arg) 4328c2ecf20Sopenharmony_ci{ 4338c2ecf20Sopenharmony_ci struct fw_cdev_get_info *a = &arg->get_info; 4348c2ecf20Sopenharmony_ci struct fw_cdev_event_bus_reset bus_reset; 4358c2ecf20Sopenharmony_ci unsigned long ret = 0; 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci client->version = a->version; 4388c2ecf20Sopenharmony_ci a->version = FW_CDEV_KERNEL_VERSION; 4398c2ecf20Sopenharmony_ci a->card = client->device->card->index; 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci down_read(&fw_device_rwsem); 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci if (a->rom != 0) { 4448c2ecf20Sopenharmony_ci size_t want = a->rom_length; 4458c2ecf20Sopenharmony_ci size_t have = client->device->config_rom_length * 4; 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci ret = copy_to_user(u64_to_uptr(a->rom), 4488c2ecf20Sopenharmony_ci client->device->config_rom, min(want, have)); 4498c2ecf20Sopenharmony_ci } 4508c2ecf20Sopenharmony_ci a->rom_length = client->device->config_rom_length * 4; 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci up_read(&fw_device_rwsem); 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci if (ret != 0) 4558c2ecf20Sopenharmony_ci return -EFAULT; 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci mutex_lock(&client->device->client_list_mutex); 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci client->bus_reset_closure = a->bus_reset_closure; 4608c2ecf20Sopenharmony_ci if (a->bus_reset != 0) { 4618c2ecf20Sopenharmony_ci fill_bus_reset_event(&bus_reset, client); 4628c2ecf20Sopenharmony_ci /* unaligned size of bus_reset is 36 bytes */ 4638c2ecf20Sopenharmony_ci ret = copy_to_user(u64_to_uptr(a->bus_reset), &bus_reset, 36); 4648c2ecf20Sopenharmony_ci } 4658c2ecf20Sopenharmony_ci if (ret == 0 && list_empty(&client->link)) 4668c2ecf20Sopenharmony_ci list_add_tail(&client->link, &client->device->client_list); 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci mutex_unlock(&client->device->client_list_mutex); 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci return ret ? -EFAULT : 0; 4718c2ecf20Sopenharmony_ci} 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_cistatic int add_client_resource(struct client *client, 4748c2ecf20Sopenharmony_ci struct client_resource *resource, gfp_t gfp_mask) 4758c2ecf20Sopenharmony_ci{ 4768c2ecf20Sopenharmony_ci bool preload = gfpflags_allow_blocking(gfp_mask); 4778c2ecf20Sopenharmony_ci unsigned long flags; 4788c2ecf20Sopenharmony_ci int ret; 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci if (preload) 4818c2ecf20Sopenharmony_ci idr_preload(gfp_mask); 4828c2ecf20Sopenharmony_ci spin_lock_irqsave(&client->lock, flags); 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci if (client->in_shutdown) 4858c2ecf20Sopenharmony_ci ret = -ECANCELED; 4868c2ecf20Sopenharmony_ci else 4878c2ecf20Sopenharmony_ci ret = idr_alloc(&client->resource_idr, resource, 0, 0, 4888c2ecf20Sopenharmony_ci GFP_NOWAIT); 4898c2ecf20Sopenharmony_ci if (ret >= 0) { 4908c2ecf20Sopenharmony_ci resource->handle = ret; 4918c2ecf20Sopenharmony_ci client_get(client); 4928c2ecf20Sopenharmony_ci schedule_if_iso_resource(resource); 4938c2ecf20Sopenharmony_ci } 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&client->lock, flags); 4968c2ecf20Sopenharmony_ci if (preload) 4978c2ecf20Sopenharmony_ci idr_preload_end(); 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci return ret < 0 ? ret : 0; 5008c2ecf20Sopenharmony_ci} 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_cistatic int release_client_resource(struct client *client, u32 handle, 5038c2ecf20Sopenharmony_ci client_resource_release_fn_t release, 5048c2ecf20Sopenharmony_ci struct client_resource **return_resource) 5058c2ecf20Sopenharmony_ci{ 5068c2ecf20Sopenharmony_ci struct client_resource *resource; 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci spin_lock_irq(&client->lock); 5098c2ecf20Sopenharmony_ci if (client->in_shutdown) 5108c2ecf20Sopenharmony_ci resource = NULL; 5118c2ecf20Sopenharmony_ci else 5128c2ecf20Sopenharmony_ci resource = idr_find(&client->resource_idr, handle); 5138c2ecf20Sopenharmony_ci if (resource && resource->release == release) 5148c2ecf20Sopenharmony_ci idr_remove(&client->resource_idr, handle); 5158c2ecf20Sopenharmony_ci spin_unlock_irq(&client->lock); 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci if (!(resource && resource->release == release)) 5188c2ecf20Sopenharmony_ci return -EINVAL; 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci if (return_resource) 5218c2ecf20Sopenharmony_ci *return_resource = resource; 5228c2ecf20Sopenharmony_ci else 5238c2ecf20Sopenharmony_ci resource->release(client, resource); 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci client_put(client); 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci return 0; 5288c2ecf20Sopenharmony_ci} 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_cistatic void release_transaction(struct client *client, 5318c2ecf20Sopenharmony_ci struct client_resource *resource) 5328c2ecf20Sopenharmony_ci{ 5338c2ecf20Sopenharmony_ci} 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_cistatic void complete_transaction(struct fw_card *card, int rcode, 5368c2ecf20Sopenharmony_ci void *payload, size_t length, void *data) 5378c2ecf20Sopenharmony_ci{ 5388c2ecf20Sopenharmony_ci struct outbound_transaction_event *e = data; 5398c2ecf20Sopenharmony_ci struct fw_cdev_event_response *rsp = &e->response; 5408c2ecf20Sopenharmony_ci struct client *client = e->client; 5418c2ecf20Sopenharmony_ci unsigned long flags; 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci if (length < rsp->length) 5448c2ecf20Sopenharmony_ci rsp->length = length; 5458c2ecf20Sopenharmony_ci if (rcode == RCODE_COMPLETE) 5468c2ecf20Sopenharmony_ci memcpy(rsp->data, payload, rsp->length); 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci spin_lock_irqsave(&client->lock, flags); 5498c2ecf20Sopenharmony_ci idr_remove(&client->resource_idr, e->r.resource.handle); 5508c2ecf20Sopenharmony_ci if (client->in_shutdown) 5518c2ecf20Sopenharmony_ci wake_up(&client->tx_flush_wait); 5528c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&client->lock, flags); 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci rsp->type = FW_CDEV_EVENT_RESPONSE; 5558c2ecf20Sopenharmony_ci rsp->rcode = rcode; 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci /* 5588c2ecf20Sopenharmony_ci * In the case that sizeof(*rsp) doesn't align with the position of the 5598c2ecf20Sopenharmony_ci * data, and the read is short, preserve an extra copy of the data 5608c2ecf20Sopenharmony_ci * to stay compatible with a pre-2.6.27 bug. Since the bug is harmless 5618c2ecf20Sopenharmony_ci * for short reads and some apps depended on it, this is both safe 5628c2ecf20Sopenharmony_ci * and prudent for compatibility. 5638c2ecf20Sopenharmony_ci */ 5648c2ecf20Sopenharmony_ci if (rsp->length <= sizeof(*rsp) - offsetof(typeof(*rsp), data)) 5658c2ecf20Sopenharmony_ci queue_event(client, &e->event, rsp, sizeof(*rsp), 5668c2ecf20Sopenharmony_ci rsp->data, rsp->length); 5678c2ecf20Sopenharmony_ci else 5688c2ecf20Sopenharmony_ci queue_event(client, &e->event, rsp, sizeof(*rsp) + rsp->length, 5698c2ecf20Sopenharmony_ci NULL, 0); 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci /* Drop the idr's reference */ 5728c2ecf20Sopenharmony_ci client_put(client); 5738c2ecf20Sopenharmony_ci} 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_cistatic int init_request(struct client *client, 5768c2ecf20Sopenharmony_ci struct fw_cdev_send_request *request, 5778c2ecf20Sopenharmony_ci int destination_id, int speed) 5788c2ecf20Sopenharmony_ci{ 5798c2ecf20Sopenharmony_ci struct outbound_transaction_event *e; 5808c2ecf20Sopenharmony_ci int ret; 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci if (request->tcode != TCODE_STREAM_DATA && 5838c2ecf20Sopenharmony_ci (request->length > 4096 || request->length > 512 << speed)) 5848c2ecf20Sopenharmony_ci return -EIO; 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci if (request->tcode == TCODE_WRITE_QUADLET_REQUEST && 5878c2ecf20Sopenharmony_ci request->length < 4) 5888c2ecf20Sopenharmony_ci return -EINVAL; 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci e = kmalloc(sizeof(*e) + request->length, GFP_KERNEL); 5918c2ecf20Sopenharmony_ci if (e == NULL) 5928c2ecf20Sopenharmony_ci return -ENOMEM; 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci e->client = client; 5958c2ecf20Sopenharmony_ci e->response.length = request->length; 5968c2ecf20Sopenharmony_ci e->response.closure = request->closure; 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ci if (request->data && 5998c2ecf20Sopenharmony_ci copy_from_user(e->response.data, 6008c2ecf20Sopenharmony_ci u64_to_uptr(request->data), request->length)) { 6018c2ecf20Sopenharmony_ci ret = -EFAULT; 6028c2ecf20Sopenharmony_ci goto failed; 6038c2ecf20Sopenharmony_ci } 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci e->r.resource.release = release_transaction; 6068c2ecf20Sopenharmony_ci ret = add_client_resource(client, &e->r.resource, GFP_KERNEL); 6078c2ecf20Sopenharmony_ci if (ret < 0) 6088c2ecf20Sopenharmony_ci goto failed; 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci fw_send_request(client->device->card, &e->r.transaction, 6118c2ecf20Sopenharmony_ci request->tcode, destination_id, request->generation, 6128c2ecf20Sopenharmony_ci speed, request->offset, e->response.data, 6138c2ecf20Sopenharmony_ci request->length, complete_transaction, e); 6148c2ecf20Sopenharmony_ci return 0; 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci failed: 6178c2ecf20Sopenharmony_ci kfree(e); 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci return ret; 6208c2ecf20Sopenharmony_ci} 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_cistatic int ioctl_send_request(struct client *client, union ioctl_arg *arg) 6238c2ecf20Sopenharmony_ci{ 6248c2ecf20Sopenharmony_ci switch (arg->send_request.tcode) { 6258c2ecf20Sopenharmony_ci case TCODE_WRITE_QUADLET_REQUEST: 6268c2ecf20Sopenharmony_ci case TCODE_WRITE_BLOCK_REQUEST: 6278c2ecf20Sopenharmony_ci case TCODE_READ_QUADLET_REQUEST: 6288c2ecf20Sopenharmony_ci case TCODE_READ_BLOCK_REQUEST: 6298c2ecf20Sopenharmony_ci case TCODE_LOCK_MASK_SWAP: 6308c2ecf20Sopenharmony_ci case TCODE_LOCK_COMPARE_SWAP: 6318c2ecf20Sopenharmony_ci case TCODE_LOCK_FETCH_ADD: 6328c2ecf20Sopenharmony_ci case TCODE_LOCK_LITTLE_ADD: 6338c2ecf20Sopenharmony_ci case TCODE_LOCK_BOUNDED_ADD: 6348c2ecf20Sopenharmony_ci case TCODE_LOCK_WRAP_ADD: 6358c2ecf20Sopenharmony_ci case TCODE_LOCK_VENDOR_DEPENDENT: 6368c2ecf20Sopenharmony_ci break; 6378c2ecf20Sopenharmony_ci default: 6388c2ecf20Sopenharmony_ci return -EINVAL; 6398c2ecf20Sopenharmony_ci } 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ci return init_request(client, &arg->send_request, client->device->node_id, 6428c2ecf20Sopenharmony_ci client->device->max_speed); 6438c2ecf20Sopenharmony_ci} 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_cistatic inline bool is_fcp_request(struct fw_request *request) 6468c2ecf20Sopenharmony_ci{ 6478c2ecf20Sopenharmony_ci return request == NULL; 6488c2ecf20Sopenharmony_ci} 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_cistatic void release_request(struct client *client, 6518c2ecf20Sopenharmony_ci struct client_resource *resource) 6528c2ecf20Sopenharmony_ci{ 6538c2ecf20Sopenharmony_ci struct inbound_transaction_resource *r = container_of(resource, 6548c2ecf20Sopenharmony_ci struct inbound_transaction_resource, resource); 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci if (is_fcp_request(r->request)) 6578c2ecf20Sopenharmony_ci kfree(r->data); 6588c2ecf20Sopenharmony_ci else 6598c2ecf20Sopenharmony_ci fw_send_response(r->card, r->request, RCODE_CONFLICT_ERROR); 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci fw_card_put(r->card); 6628c2ecf20Sopenharmony_ci kfree(r); 6638c2ecf20Sopenharmony_ci} 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_cistatic void handle_request(struct fw_card *card, struct fw_request *request, 6668c2ecf20Sopenharmony_ci int tcode, int destination, int source, 6678c2ecf20Sopenharmony_ci int generation, unsigned long long offset, 6688c2ecf20Sopenharmony_ci void *payload, size_t length, void *callback_data) 6698c2ecf20Sopenharmony_ci{ 6708c2ecf20Sopenharmony_ci struct address_handler_resource *handler = callback_data; 6718c2ecf20Sopenharmony_ci struct inbound_transaction_resource *r; 6728c2ecf20Sopenharmony_ci struct inbound_transaction_event *e; 6738c2ecf20Sopenharmony_ci size_t event_size0; 6748c2ecf20Sopenharmony_ci void *fcp_frame = NULL; 6758c2ecf20Sopenharmony_ci int ret; 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci /* card may be different from handler->client->device->card */ 6788c2ecf20Sopenharmony_ci fw_card_get(card); 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci r = kmalloc(sizeof(*r), GFP_ATOMIC); 6818c2ecf20Sopenharmony_ci e = kmalloc(sizeof(*e), GFP_ATOMIC); 6828c2ecf20Sopenharmony_ci if (r == NULL || e == NULL) 6838c2ecf20Sopenharmony_ci goto failed; 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci r->card = card; 6868c2ecf20Sopenharmony_ci r->request = request; 6878c2ecf20Sopenharmony_ci r->data = payload; 6888c2ecf20Sopenharmony_ci r->length = length; 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_ci if (is_fcp_request(request)) { 6918c2ecf20Sopenharmony_ci /* 6928c2ecf20Sopenharmony_ci * FIXME: Let core-transaction.c manage a 6938c2ecf20Sopenharmony_ci * single reference-counted copy? 6948c2ecf20Sopenharmony_ci */ 6958c2ecf20Sopenharmony_ci fcp_frame = kmemdup(payload, length, GFP_ATOMIC); 6968c2ecf20Sopenharmony_ci if (fcp_frame == NULL) 6978c2ecf20Sopenharmony_ci goto failed; 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_ci r->data = fcp_frame; 7008c2ecf20Sopenharmony_ci } 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_ci r->resource.release = release_request; 7038c2ecf20Sopenharmony_ci ret = add_client_resource(handler->client, &r->resource, GFP_ATOMIC); 7048c2ecf20Sopenharmony_ci if (ret < 0) 7058c2ecf20Sopenharmony_ci goto failed; 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_ci if (handler->client->version < FW_CDEV_VERSION_EVENT_REQUEST2) { 7088c2ecf20Sopenharmony_ci struct fw_cdev_event_request *req = &e->req.request; 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_ci if (tcode & 0x10) 7118c2ecf20Sopenharmony_ci tcode = TCODE_LOCK_REQUEST; 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ci req->type = FW_CDEV_EVENT_REQUEST; 7148c2ecf20Sopenharmony_ci req->tcode = tcode; 7158c2ecf20Sopenharmony_ci req->offset = offset; 7168c2ecf20Sopenharmony_ci req->length = length; 7178c2ecf20Sopenharmony_ci req->handle = r->resource.handle; 7188c2ecf20Sopenharmony_ci req->closure = handler->closure; 7198c2ecf20Sopenharmony_ci event_size0 = sizeof(*req); 7208c2ecf20Sopenharmony_ci } else { 7218c2ecf20Sopenharmony_ci struct fw_cdev_event_request2 *req = &e->req.request2; 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_ci req->type = FW_CDEV_EVENT_REQUEST2; 7248c2ecf20Sopenharmony_ci req->tcode = tcode; 7258c2ecf20Sopenharmony_ci req->offset = offset; 7268c2ecf20Sopenharmony_ci req->source_node_id = source; 7278c2ecf20Sopenharmony_ci req->destination_node_id = destination; 7288c2ecf20Sopenharmony_ci req->card = card->index; 7298c2ecf20Sopenharmony_ci req->generation = generation; 7308c2ecf20Sopenharmony_ci req->length = length; 7318c2ecf20Sopenharmony_ci req->handle = r->resource.handle; 7328c2ecf20Sopenharmony_ci req->closure = handler->closure; 7338c2ecf20Sopenharmony_ci event_size0 = sizeof(*req); 7348c2ecf20Sopenharmony_ci } 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_ci queue_event(handler->client, &e->event, 7378c2ecf20Sopenharmony_ci &e->req, event_size0, r->data, length); 7388c2ecf20Sopenharmony_ci return; 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_ci failed: 7418c2ecf20Sopenharmony_ci kfree(r); 7428c2ecf20Sopenharmony_ci kfree(e); 7438c2ecf20Sopenharmony_ci kfree(fcp_frame); 7448c2ecf20Sopenharmony_ci 7458c2ecf20Sopenharmony_ci if (!is_fcp_request(request)) 7468c2ecf20Sopenharmony_ci fw_send_response(card, request, RCODE_CONFLICT_ERROR); 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_ci fw_card_put(card); 7498c2ecf20Sopenharmony_ci} 7508c2ecf20Sopenharmony_ci 7518c2ecf20Sopenharmony_cistatic void release_address_handler(struct client *client, 7528c2ecf20Sopenharmony_ci struct client_resource *resource) 7538c2ecf20Sopenharmony_ci{ 7548c2ecf20Sopenharmony_ci struct address_handler_resource *r = 7558c2ecf20Sopenharmony_ci container_of(resource, struct address_handler_resource, resource); 7568c2ecf20Sopenharmony_ci 7578c2ecf20Sopenharmony_ci fw_core_remove_address_handler(&r->handler); 7588c2ecf20Sopenharmony_ci kfree(r); 7598c2ecf20Sopenharmony_ci} 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_cistatic int ioctl_allocate(struct client *client, union ioctl_arg *arg) 7628c2ecf20Sopenharmony_ci{ 7638c2ecf20Sopenharmony_ci struct fw_cdev_allocate *a = &arg->allocate; 7648c2ecf20Sopenharmony_ci struct address_handler_resource *r; 7658c2ecf20Sopenharmony_ci struct fw_address_region region; 7668c2ecf20Sopenharmony_ci int ret; 7678c2ecf20Sopenharmony_ci 7688c2ecf20Sopenharmony_ci r = kmalloc(sizeof(*r), GFP_KERNEL); 7698c2ecf20Sopenharmony_ci if (r == NULL) 7708c2ecf20Sopenharmony_ci return -ENOMEM; 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_ci region.start = a->offset; 7738c2ecf20Sopenharmony_ci if (client->version < FW_CDEV_VERSION_ALLOCATE_REGION_END) 7748c2ecf20Sopenharmony_ci region.end = a->offset + a->length; 7758c2ecf20Sopenharmony_ci else 7768c2ecf20Sopenharmony_ci region.end = a->region_end; 7778c2ecf20Sopenharmony_ci 7788c2ecf20Sopenharmony_ci r->handler.length = a->length; 7798c2ecf20Sopenharmony_ci r->handler.address_callback = handle_request; 7808c2ecf20Sopenharmony_ci r->handler.callback_data = r; 7818c2ecf20Sopenharmony_ci r->closure = a->closure; 7828c2ecf20Sopenharmony_ci r->client = client; 7838c2ecf20Sopenharmony_ci 7848c2ecf20Sopenharmony_ci ret = fw_core_add_address_handler(&r->handler, ®ion); 7858c2ecf20Sopenharmony_ci if (ret < 0) { 7868c2ecf20Sopenharmony_ci kfree(r); 7878c2ecf20Sopenharmony_ci return ret; 7888c2ecf20Sopenharmony_ci } 7898c2ecf20Sopenharmony_ci a->offset = r->handler.offset; 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_ci r->resource.release = release_address_handler; 7928c2ecf20Sopenharmony_ci ret = add_client_resource(client, &r->resource, GFP_KERNEL); 7938c2ecf20Sopenharmony_ci if (ret < 0) { 7948c2ecf20Sopenharmony_ci release_address_handler(client, &r->resource); 7958c2ecf20Sopenharmony_ci return ret; 7968c2ecf20Sopenharmony_ci } 7978c2ecf20Sopenharmony_ci a->handle = r->resource.handle; 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_ci return 0; 8008c2ecf20Sopenharmony_ci} 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_cistatic int ioctl_deallocate(struct client *client, union ioctl_arg *arg) 8038c2ecf20Sopenharmony_ci{ 8048c2ecf20Sopenharmony_ci return release_client_resource(client, arg->deallocate.handle, 8058c2ecf20Sopenharmony_ci release_address_handler, NULL); 8068c2ecf20Sopenharmony_ci} 8078c2ecf20Sopenharmony_ci 8088c2ecf20Sopenharmony_cistatic int ioctl_send_response(struct client *client, union ioctl_arg *arg) 8098c2ecf20Sopenharmony_ci{ 8108c2ecf20Sopenharmony_ci struct fw_cdev_send_response *a = &arg->send_response; 8118c2ecf20Sopenharmony_ci struct client_resource *resource; 8128c2ecf20Sopenharmony_ci struct inbound_transaction_resource *r; 8138c2ecf20Sopenharmony_ci int ret = 0; 8148c2ecf20Sopenharmony_ci 8158c2ecf20Sopenharmony_ci if (release_client_resource(client, a->handle, 8168c2ecf20Sopenharmony_ci release_request, &resource) < 0) 8178c2ecf20Sopenharmony_ci return -EINVAL; 8188c2ecf20Sopenharmony_ci 8198c2ecf20Sopenharmony_ci r = container_of(resource, struct inbound_transaction_resource, 8208c2ecf20Sopenharmony_ci resource); 8218c2ecf20Sopenharmony_ci if (is_fcp_request(r->request)) { 8228c2ecf20Sopenharmony_ci kfree(r->data); 8238c2ecf20Sopenharmony_ci goto out; 8248c2ecf20Sopenharmony_ci } 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_ci if (a->length != fw_get_response_length(r->request)) { 8278c2ecf20Sopenharmony_ci ret = -EINVAL; 8288c2ecf20Sopenharmony_ci kfree(r->request); 8298c2ecf20Sopenharmony_ci goto out; 8308c2ecf20Sopenharmony_ci } 8318c2ecf20Sopenharmony_ci if (copy_from_user(r->data, u64_to_uptr(a->data), a->length)) { 8328c2ecf20Sopenharmony_ci ret = -EFAULT; 8338c2ecf20Sopenharmony_ci kfree(r->request); 8348c2ecf20Sopenharmony_ci goto out; 8358c2ecf20Sopenharmony_ci } 8368c2ecf20Sopenharmony_ci fw_send_response(r->card, r->request, a->rcode); 8378c2ecf20Sopenharmony_ci out: 8388c2ecf20Sopenharmony_ci fw_card_put(r->card); 8398c2ecf20Sopenharmony_ci kfree(r); 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_ci return ret; 8428c2ecf20Sopenharmony_ci} 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_cistatic int ioctl_initiate_bus_reset(struct client *client, union ioctl_arg *arg) 8458c2ecf20Sopenharmony_ci{ 8468c2ecf20Sopenharmony_ci fw_schedule_bus_reset(client->device->card, true, 8478c2ecf20Sopenharmony_ci arg->initiate_bus_reset.type == FW_CDEV_SHORT_RESET); 8488c2ecf20Sopenharmony_ci return 0; 8498c2ecf20Sopenharmony_ci} 8508c2ecf20Sopenharmony_ci 8518c2ecf20Sopenharmony_cistatic void release_descriptor(struct client *client, 8528c2ecf20Sopenharmony_ci struct client_resource *resource) 8538c2ecf20Sopenharmony_ci{ 8548c2ecf20Sopenharmony_ci struct descriptor_resource *r = 8558c2ecf20Sopenharmony_ci container_of(resource, struct descriptor_resource, resource); 8568c2ecf20Sopenharmony_ci 8578c2ecf20Sopenharmony_ci fw_core_remove_descriptor(&r->descriptor); 8588c2ecf20Sopenharmony_ci kfree(r); 8598c2ecf20Sopenharmony_ci} 8608c2ecf20Sopenharmony_ci 8618c2ecf20Sopenharmony_cistatic int ioctl_add_descriptor(struct client *client, union ioctl_arg *arg) 8628c2ecf20Sopenharmony_ci{ 8638c2ecf20Sopenharmony_ci struct fw_cdev_add_descriptor *a = &arg->add_descriptor; 8648c2ecf20Sopenharmony_ci struct descriptor_resource *r; 8658c2ecf20Sopenharmony_ci int ret; 8668c2ecf20Sopenharmony_ci 8678c2ecf20Sopenharmony_ci /* Access policy: Allow this ioctl only on local nodes' device files. */ 8688c2ecf20Sopenharmony_ci if (!client->device->is_local) 8698c2ecf20Sopenharmony_ci return -ENOSYS; 8708c2ecf20Sopenharmony_ci 8718c2ecf20Sopenharmony_ci if (a->length > 256) 8728c2ecf20Sopenharmony_ci return -EINVAL; 8738c2ecf20Sopenharmony_ci 8748c2ecf20Sopenharmony_ci r = kmalloc(sizeof(*r) + a->length * 4, GFP_KERNEL); 8758c2ecf20Sopenharmony_ci if (r == NULL) 8768c2ecf20Sopenharmony_ci return -ENOMEM; 8778c2ecf20Sopenharmony_ci 8788c2ecf20Sopenharmony_ci if (copy_from_user(r->data, u64_to_uptr(a->data), a->length * 4)) { 8798c2ecf20Sopenharmony_ci ret = -EFAULT; 8808c2ecf20Sopenharmony_ci goto failed; 8818c2ecf20Sopenharmony_ci } 8828c2ecf20Sopenharmony_ci 8838c2ecf20Sopenharmony_ci r->descriptor.length = a->length; 8848c2ecf20Sopenharmony_ci r->descriptor.immediate = a->immediate; 8858c2ecf20Sopenharmony_ci r->descriptor.key = a->key; 8868c2ecf20Sopenharmony_ci r->descriptor.data = r->data; 8878c2ecf20Sopenharmony_ci 8888c2ecf20Sopenharmony_ci ret = fw_core_add_descriptor(&r->descriptor); 8898c2ecf20Sopenharmony_ci if (ret < 0) 8908c2ecf20Sopenharmony_ci goto failed; 8918c2ecf20Sopenharmony_ci 8928c2ecf20Sopenharmony_ci r->resource.release = release_descriptor; 8938c2ecf20Sopenharmony_ci ret = add_client_resource(client, &r->resource, GFP_KERNEL); 8948c2ecf20Sopenharmony_ci if (ret < 0) { 8958c2ecf20Sopenharmony_ci fw_core_remove_descriptor(&r->descriptor); 8968c2ecf20Sopenharmony_ci goto failed; 8978c2ecf20Sopenharmony_ci } 8988c2ecf20Sopenharmony_ci a->handle = r->resource.handle; 8998c2ecf20Sopenharmony_ci 9008c2ecf20Sopenharmony_ci return 0; 9018c2ecf20Sopenharmony_ci failed: 9028c2ecf20Sopenharmony_ci kfree(r); 9038c2ecf20Sopenharmony_ci 9048c2ecf20Sopenharmony_ci return ret; 9058c2ecf20Sopenharmony_ci} 9068c2ecf20Sopenharmony_ci 9078c2ecf20Sopenharmony_cistatic int ioctl_remove_descriptor(struct client *client, union ioctl_arg *arg) 9088c2ecf20Sopenharmony_ci{ 9098c2ecf20Sopenharmony_ci return release_client_resource(client, arg->remove_descriptor.handle, 9108c2ecf20Sopenharmony_ci release_descriptor, NULL); 9118c2ecf20Sopenharmony_ci} 9128c2ecf20Sopenharmony_ci 9138c2ecf20Sopenharmony_cistatic void iso_callback(struct fw_iso_context *context, u32 cycle, 9148c2ecf20Sopenharmony_ci size_t header_length, void *header, void *data) 9158c2ecf20Sopenharmony_ci{ 9168c2ecf20Sopenharmony_ci struct client *client = data; 9178c2ecf20Sopenharmony_ci struct iso_interrupt_event *e; 9188c2ecf20Sopenharmony_ci 9198c2ecf20Sopenharmony_ci e = kmalloc(sizeof(*e) + header_length, GFP_ATOMIC); 9208c2ecf20Sopenharmony_ci if (e == NULL) 9218c2ecf20Sopenharmony_ci return; 9228c2ecf20Sopenharmony_ci 9238c2ecf20Sopenharmony_ci e->interrupt.type = FW_CDEV_EVENT_ISO_INTERRUPT; 9248c2ecf20Sopenharmony_ci e->interrupt.closure = client->iso_closure; 9258c2ecf20Sopenharmony_ci e->interrupt.cycle = cycle; 9268c2ecf20Sopenharmony_ci e->interrupt.header_length = header_length; 9278c2ecf20Sopenharmony_ci memcpy(e->interrupt.header, header, header_length); 9288c2ecf20Sopenharmony_ci queue_event(client, &e->event, &e->interrupt, 9298c2ecf20Sopenharmony_ci sizeof(e->interrupt) + header_length, NULL, 0); 9308c2ecf20Sopenharmony_ci} 9318c2ecf20Sopenharmony_ci 9328c2ecf20Sopenharmony_cistatic void iso_mc_callback(struct fw_iso_context *context, 9338c2ecf20Sopenharmony_ci dma_addr_t completed, void *data) 9348c2ecf20Sopenharmony_ci{ 9358c2ecf20Sopenharmony_ci struct client *client = data; 9368c2ecf20Sopenharmony_ci struct iso_interrupt_mc_event *e; 9378c2ecf20Sopenharmony_ci 9388c2ecf20Sopenharmony_ci e = kmalloc(sizeof(*e), GFP_ATOMIC); 9398c2ecf20Sopenharmony_ci if (e == NULL) 9408c2ecf20Sopenharmony_ci return; 9418c2ecf20Sopenharmony_ci 9428c2ecf20Sopenharmony_ci e->interrupt.type = FW_CDEV_EVENT_ISO_INTERRUPT_MULTICHANNEL; 9438c2ecf20Sopenharmony_ci e->interrupt.closure = client->iso_closure; 9448c2ecf20Sopenharmony_ci e->interrupt.completed = fw_iso_buffer_lookup(&client->buffer, 9458c2ecf20Sopenharmony_ci completed); 9468c2ecf20Sopenharmony_ci queue_event(client, &e->event, &e->interrupt, 9478c2ecf20Sopenharmony_ci sizeof(e->interrupt), NULL, 0); 9488c2ecf20Sopenharmony_ci} 9498c2ecf20Sopenharmony_ci 9508c2ecf20Sopenharmony_cistatic enum dma_data_direction iso_dma_direction(struct fw_iso_context *context) 9518c2ecf20Sopenharmony_ci{ 9528c2ecf20Sopenharmony_ci if (context->type == FW_ISO_CONTEXT_TRANSMIT) 9538c2ecf20Sopenharmony_ci return DMA_TO_DEVICE; 9548c2ecf20Sopenharmony_ci else 9558c2ecf20Sopenharmony_ci return DMA_FROM_DEVICE; 9568c2ecf20Sopenharmony_ci} 9578c2ecf20Sopenharmony_ci 9588c2ecf20Sopenharmony_cistatic int ioctl_create_iso_context(struct client *client, union ioctl_arg *arg) 9598c2ecf20Sopenharmony_ci{ 9608c2ecf20Sopenharmony_ci struct fw_cdev_create_iso_context *a = &arg->create_iso_context; 9618c2ecf20Sopenharmony_ci struct fw_iso_context *context; 9628c2ecf20Sopenharmony_ci fw_iso_callback_t cb; 9638c2ecf20Sopenharmony_ci int ret; 9648c2ecf20Sopenharmony_ci 9658c2ecf20Sopenharmony_ci BUILD_BUG_ON(FW_CDEV_ISO_CONTEXT_TRANSMIT != FW_ISO_CONTEXT_TRANSMIT || 9668c2ecf20Sopenharmony_ci FW_CDEV_ISO_CONTEXT_RECEIVE != FW_ISO_CONTEXT_RECEIVE || 9678c2ecf20Sopenharmony_ci FW_CDEV_ISO_CONTEXT_RECEIVE_MULTICHANNEL != 9688c2ecf20Sopenharmony_ci FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL); 9698c2ecf20Sopenharmony_ci 9708c2ecf20Sopenharmony_ci switch (a->type) { 9718c2ecf20Sopenharmony_ci case FW_ISO_CONTEXT_TRANSMIT: 9728c2ecf20Sopenharmony_ci if (a->speed > SCODE_3200 || a->channel > 63) 9738c2ecf20Sopenharmony_ci return -EINVAL; 9748c2ecf20Sopenharmony_ci 9758c2ecf20Sopenharmony_ci cb = iso_callback; 9768c2ecf20Sopenharmony_ci break; 9778c2ecf20Sopenharmony_ci 9788c2ecf20Sopenharmony_ci case FW_ISO_CONTEXT_RECEIVE: 9798c2ecf20Sopenharmony_ci if (a->header_size < 4 || (a->header_size & 3) || 9808c2ecf20Sopenharmony_ci a->channel > 63) 9818c2ecf20Sopenharmony_ci return -EINVAL; 9828c2ecf20Sopenharmony_ci 9838c2ecf20Sopenharmony_ci cb = iso_callback; 9848c2ecf20Sopenharmony_ci break; 9858c2ecf20Sopenharmony_ci 9868c2ecf20Sopenharmony_ci case FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL: 9878c2ecf20Sopenharmony_ci cb = (fw_iso_callback_t)iso_mc_callback; 9888c2ecf20Sopenharmony_ci break; 9898c2ecf20Sopenharmony_ci 9908c2ecf20Sopenharmony_ci default: 9918c2ecf20Sopenharmony_ci return -EINVAL; 9928c2ecf20Sopenharmony_ci } 9938c2ecf20Sopenharmony_ci 9948c2ecf20Sopenharmony_ci context = fw_iso_context_create(client->device->card, a->type, 9958c2ecf20Sopenharmony_ci a->channel, a->speed, a->header_size, cb, client); 9968c2ecf20Sopenharmony_ci if (IS_ERR(context)) 9978c2ecf20Sopenharmony_ci return PTR_ERR(context); 9988c2ecf20Sopenharmony_ci if (client->version < FW_CDEV_VERSION_AUTO_FLUSH_ISO_OVERFLOW) 9998c2ecf20Sopenharmony_ci context->drop_overflow_headers = true; 10008c2ecf20Sopenharmony_ci 10018c2ecf20Sopenharmony_ci /* We only support one context at this time. */ 10028c2ecf20Sopenharmony_ci spin_lock_irq(&client->lock); 10038c2ecf20Sopenharmony_ci if (client->iso_context != NULL) { 10048c2ecf20Sopenharmony_ci spin_unlock_irq(&client->lock); 10058c2ecf20Sopenharmony_ci fw_iso_context_destroy(context); 10068c2ecf20Sopenharmony_ci 10078c2ecf20Sopenharmony_ci return -EBUSY; 10088c2ecf20Sopenharmony_ci } 10098c2ecf20Sopenharmony_ci if (!client->buffer_is_mapped) { 10108c2ecf20Sopenharmony_ci ret = fw_iso_buffer_map_dma(&client->buffer, 10118c2ecf20Sopenharmony_ci client->device->card, 10128c2ecf20Sopenharmony_ci iso_dma_direction(context)); 10138c2ecf20Sopenharmony_ci if (ret < 0) { 10148c2ecf20Sopenharmony_ci spin_unlock_irq(&client->lock); 10158c2ecf20Sopenharmony_ci fw_iso_context_destroy(context); 10168c2ecf20Sopenharmony_ci 10178c2ecf20Sopenharmony_ci return ret; 10188c2ecf20Sopenharmony_ci } 10198c2ecf20Sopenharmony_ci client->buffer_is_mapped = true; 10208c2ecf20Sopenharmony_ci } 10218c2ecf20Sopenharmony_ci client->iso_closure = a->closure; 10228c2ecf20Sopenharmony_ci client->iso_context = context; 10238c2ecf20Sopenharmony_ci spin_unlock_irq(&client->lock); 10248c2ecf20Sopenharmony_ci 10258c2ecf20Sopenharmony_ci a->handle = 0; 10268c2ecf20Sopenharmony_ci 10278c2ecf20Sopenharmony_ci return 0; 10288c2ecf20Sopenharmony_ci} 10298c2ecf20Sopenharmony_ci 10308c2ecf20Sopenharmony_cistatic int ioctl_set_iso_channels(struct client *client, union ioctl_arg *arg) 10318c2ecf20Sopenharmony_ci{ 10328c2ecf20Sopenharmony_ci struct fw_cdev_set_iso_channels *a = &arg->set_iso_channels; 10338c2ecf20Sopenharmony_ci struct fw_iso_context *ctx = client->iso_context; 10348c2ecf20Sopenharmony_ci 10358c2ecf20Sopenharmony_ci if (ctx == NULL || a->handle != 0) 10368c2ecf20Sopenharmony_ci return -EINVAL; 10378c2ecf20Sopenharmony_ci 10388c2ecf20Sopenharmony_ci return fw_iso_context_set_channels(ctx, &a->channels); 10398c2ecf20Sopenharmony_ci} 10408c2ecf20Sopenharmony_ci 10418c2ecf20Sopenharmony_ci/* Macros for decoding the iso packet control header. */ 10428c2ecf20Sopenharmony_ci#define GET_PAYLOAD_LENGTH(v) ((v) & 0xffff) 10438c2ecf20Sopenharmony_ci#define GET_INTERRUPT(v) (((v) >> 16) & 0x01) 10448c2ecf20Sopenharmony_ci#define GET_SKIP(v) (((v) >> 17) & 0x01) 10458c2ecf20Sopenharmony_ci#define GET_TAG(v) (((v) >> 18) & 0x03) 10468c2ecf20Sopenharmony_ci#define GET_SY(v) (((v) >> 20) & 0x0f) 10478c2ecf20Sopenharmony_ci#define GET_HEADER_LENGTH(v) (((v) >> 24) & 0xff) 10488c2ecf20Sopenharmony_ci 10498c2ecf20Sopenharmony_cistatic int ioctl_queue_iso(struct client *client, union ioctl_arg *arg) 10508c2ecf20Sopenharmony_ci{ 10518c2ecf20Sopenharmony_ci struct fw_cdev_queue_iso *a = &arg->queue_iso; 10528c2ecf20Sopenharmony_ci struct fw_cdev_iso_packet __user *p, *end, *next; 10538c2ecf20Sopenharmony_ci struct fw_iso_context *ctx = client->iso_context; 10548c2ecf20Sopenharmony_ci unsigned long payload, buffer_end, transmit_header_bytes = 0; 10558c2ecf20Sopenharmony_ci u32 control; 10568c2ecf20Sopenharmony_ci int count; 10578c2ecf20Sopenharmony_ci struct { 10588c2ecf20Sopenharmony_ci struct fw_iso_packet packet; 10598c2ecf20Sopenharmony_ci u8 header[256]; 10608c2ecf20Sopenharmony_ci } u; 10618c2ecf20Sopenharmony_ci 10628c2ecf20Sopenharmony_ci if (ctx == NULL || a->handle != 0) 10638c2ecf20Sopenharmony_ci return -EINVAL; 10648c2ecf20Sopenharmony_ci 10658c2ecf20Sopenharmony_ci /* 10668c2ecf20Sopenharmony_ci * If the user passes a non-NULL data pointer, has mmap()'ed 10678c2ecf20Sopenharmony_ci * the iso buffer, and the pointer points inside the buffer, 10688c2ecf20Sopenharmony_ci * we setup the payload pointers accordingly. Otherwise we 10698c2ecf20Sopenharmony_ci * set them both to 0, which will still let packets with 10708c2ecf20Sopenharmony_ci * payload_length == 0 through. In other words, if no packets 10718c2ecf20Sopenharmony_ci * use the indirect payload, the iso buffer need not be mapped 10728c2ecf20Sopenharmony_ci * and the a->data pointer is ignored. 10738c2ecf20Sopenharmony_ci */ 10748c2ecf20Sopenharmony_ci payload = (unsigned long)a->data - client->vm_start; 10758c2ecf20Sopenharmony_ci buffer_end = client->buffer.page_count << PAGE_SHIFT; 10768c2ecf20Sopenharmony_ci if (a->data == 0 || client->buffer.pages == NULL || 10778c2ecf20Sopenharmony_ci payload >= buffer_end) { 10788c2ecf20Sopenharmony_ci payload = 0; 10798c2ecf20Sopenharmony_ci buffer_end = 0; 10808c2ecf20Sopenharmony_ci } 10818c2ecf20Sopenharmony_ci 10828c2ecf20Sopenharmony_ci if (ctx->type == FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL && payload & 3) 10838c2ecf20Sopenharmony_ci return -EINVAL; 10848c2ecf20Sopenharmony_ci 10858c2ecf20Sopenharmony_ci p = (struct fw_cdev_iso_packet __user *)u64_to_uptr(a->packets); 10868c2ecf20Sopenharmony_ci 10878c2ecf20Sopenharmony_ci end = (void __user *)p + a->size; 10888c2ecf20Sopenharmony_ci count = 0; 10898c2ecf20Sopenharmony_ci while (p < end) { 10908c2ecf20Sopenharmony_ci if (get_user(control, &p->control)) 10918c2ecf20Sopenharmony_ci return -EFAULT; 10928c2ecf20Sopenharmony_ci u.packet.payload_length = GET_PAYLOAD_LENGTH(control); 10938c2ecf20Sopenharmony_ci u.packet.interrupt = GET_INTERRUPT(control); 10948c2ecf20Sopenharmony_ci u.packet.skip = GET_SKIP(control); 10958c2ecf20Sopenharmony_ci u.packet.tag = GET_TAG(control); 10968c2ecf20Sopenharmony_ci u.packet.sy = GET_SY(control); 10978c2ecf20Sopenharmony_ci u.packet.header_length = GET_HEADER_LENGTH(control); 10988c2ecf20Sopenharmony_ci 10998c2ecf20Sopenharmony_ci switch (ctx->type) { 11008c2ecf20Sopenharmony_ci case FW_ISO_CONTEXT_TRANSMIT: 11018c2ecf20Sopenharmony_ci if (u.packet.header_length & 3) 11028c2ecf20Sopenharmony_ci return -EINVAL; 11038c2ecf20Sopenharmony_ci transmit_header_bytes = u.packet.header_length; 11048c2ecf20Sopenharmony_ci break; 11058c2ecf20Sopenharmony_ci 11068c2ecf20Sopenharmony_ci case FW_ISO_CONTEXT_RECEIVE: 11078c2ecf20Sopenharmony_ci if (u.packet.header_length == 0 || 11088c2ecf20Sopenharmony_ci u.packet.header_length % ctx->header_size != 0) 11098c2ecf20Sopenharmony_ci return -EINVAL; 11108c2ecf20Sopenharmony_ci break; 11118c2ecf20Sopenharmony_ci 11128c2ecf20Sopenharmony_ci case FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL: 11138c2ecf20Sopenharmony_ci if (u.packet.payload_length == 0 || 11148c2ecf20Sopenharmony_ci u.packet.payload_length & 3) 11158c2ecf20Sopenharmony_ci return -EINVAL; 11168c2ecf20Sopenharmony_ci break; 11178c2ecf20Sopenharmony_ci } 11188c2ecf20Sopenharmony_ci 11198c2ecf20Sopenharmony_ci next = (struct fw_cdev_iso_packet __user *) 11208c2ecf20Sopenharmony_ci &p->header[transmit_header_bytes / 4]; 11218c2ecf20Sopenharmony_ci if (next > end) 11228c2ecf20Sopenharmony_ci return -EINVAL; 11238c2ecf20Sopenharmony_ci if (copy_from_user 11248c2ecf20Sopenharmony_ci (u.packet.header, p->header, transmit_header_bytes)) 11258c2ecf20Sopenharmony_ci return -EFAULT; 11268c2ecf20Sopenharmony_ci if (u.packet.skip && ctx->type == FW_ISO_CONTEXT_TRANSMIT && 11278c2ecf20Sopenharmony_ci u.packet.header_length + u.packet.payload_length > 0) 11288c2ecf20Sopenharmony_ci return -EINVAL; 11298c2ecf20Sopenharmony_ci if (payload + u.packet.payload_length > buffer_end) 11308c2ecf20Sopenharmony_ci return -EINVAL; 11318c2ecf20Sopenharmony_ci 11328c2ecf20Sopenharmony_ci if (fw_iso_context_queue(ctx, &u.packet, 11338c2ecf20Sopenharmony_ci &client->buffer, payload)) 11348c2ecf20Sopenharmony_ci break; 11358c2ecf20Sopenharmony_ci 11368c2ecf20Sopenharmony_ci p = next; 11378c2ecf20Sopenharmony_ci payload += u.packet.payload_length; 11388c2ecf20Sopenharmony_ci count++; 11398c2ecf20Sopenharmony_ci } 11408c2ecf20Sopenharmony_ci fw_iso_context_queue_flush(ctx); 11418c2ecf20Sopenharmony_ci 11428c2ecf20Sopenharmony_ci a->size -= uptr_to_u64(p) - a->packets; 11438c2ecf20Sopenharmony_ci a->packets = uptr_to_u64(p); 11448c2ecf20Sopenharmony_ci a->data = client->vm_start + payload; 11458c2ecf20Sopenharmony_ci 11468c2ecf20Sopenharmony_ci return count; 11478c2ecf20Sopenharmony_ci} 11488c2ecf20Sopenharmony_ci 11498c2ecf20Sopenharmony_cistatic int ioctl_start_iso(struct client *client, union ioctl_arg *arg) 11508c2ecf20Sopenharmony_ci{ 11518c2ecf20Sopenharmony_ci struct fw_cdev_start_iso *a = &arg->start_iso; 11528c2ecf20Sopenharmony_ci 11538c2ecf20Sopenharmony_ci BUILD_BUG_ON( 11548c2ecf20Sopenharmony_ci FW_CDEV_ISO_CONTEXT_MATCH_TAG0 != FW_ISO_CONTEXT_MATCH_TAG0 || 11558c2ecf20Sopenharmony_ci FW_CDEV_ISO_CONTEXT_MATCH_TAG1 != FW_ISO_CONTEXT_MATCH_TAG1 || 11568c2ecf20Sopenharmony_ci FW_CDEV_ISO_CONTEXT_MATCH_TAG2 != FW_ISO_CONTEXT_MATCH_TAG2 || 11578c2ecf20Sopenharmony_ci FW_CDEV_ISO_CONTEXT_MATCH_TAG3 != FW_ISO_CONTEXT_MATCH_TAG3 || 11588c2ecf20Sopenharmony_ci FW_CDEV_ISO_CONTEXT_MATCH_ALL_TAGS != FW_ISO_CONTEXT_MATCH_ALL_TAGS); 11598c2ecf20Sopenharmony_ci 11608c2ecf20Sopenharmony_ci if (client->iso_context == NULL || a->handle != 0) 11618c2ecf20Sopenharmony_ci return -EINVAL; 11628c2ecf20Sopenharmony_ci 11638c2ecf20Sopenharmony_ci if (client->iso_context->type == FW_ISO_CONTEXT_RECEIVE && 11648c2ecf20Sopenharmony_ci (a->tags == 0 || a->tags > 15 || a->sync > 15)) 11658c2ecf20Sopenharmony_ci return -EINVAL; 11668c2ecf20Sopenharmony_ci 11678c2ecf20Sopenharmony_ci return fw_iso_context_start(client->iso_context, 11688c2ecf20Sopenharmony_ci a->cycle, a->sync, a->tags); 11698c2ecf20Sopenharmony_ci} 11708c2ecf20Sopenharmony_ci 11718c2ecf20Sopenharmony_cistatic int ioctl_stop_iso(struct client *client, union ioctl_arg *arg) 11728c2ecf20Sopenharmony_ci{ 11738c2ecf20Sopenharmony_ci struct fw_cdev_stop_iso *a = &arg->stop_iso; 11748c2ecf20Sopenharmony_ci 11758c2ecf20Sopenharmony_ci if (client->iso_context == NULL || a->handle != 0) 11768c2ecf20Sopenharmony_ci return -EINVAL; 11778c2ecf20Sopenharmony_ci 11788c2ecf20Sopenharmony_ci return fw_iso_context_stop(client->iso_context); 11798c2ecf20Sopenharmony_ci} 11808c2ecf20Sopenharmony_ci 11818c2ecf20Sopenharmony_cistatic int ioctl_flush_iso(struct client *client, union ioctl_arg *arg) 11828c2ecf20Sopenharmony_ci{ 11838c2ecf20Sopenharmony_ci struct fw_cdev_flush_iso *a = &arg->flush_iso; 11848c2ecf20Sopenharmony_ci 11858c2ecf20Sopenharmony_ci if (client->iso_context == NULL || a->handle != 0) 11868c2ecf20Sopenharmony_ci return -EINVAL; 11878c2ecf20Sopenharmony_ci 11888c2ecf20Sopenharmony_ci return fw_iso_context_flush_completions(client->iso_context); 11898c2ecf20Sopenharmony_ci} 11908c2ecf20Sopenharmony_ci 11918c2ecf20Sopenharmony_cistatic int ioctl_get_cycle_timer2(struct client *client, union ioctl_arg *arg) 11928c2ecf20Sopenharmony_ci{ 11938c2ecf20Sopenharmony_ci struct fw_cdev_get_cycle_timer2 *a = &arg->get_cycle_timer2; 11948c2ecf20Sopenharmony_ci struct fw_card *card = client->device->card; 11958c2ecf20Sopenharmony_ci struct timespec64 ts = {0, 0}; 11968c2ecf20Sopenharmony_ci u32 cycle_time; 11978c2ecf20Sopenharmony_ci int ret = 0; 11988c2ecf20Sopenharmony_ci 11998c2ecf20Sopenharmony_ci local_irq_disable(); 12008c2ecf20Sopenharmony_ci 12018c2ecf20Sopenharmony_ci cycle_time = card->driver->read_csr(card, CSR_CYCLE_TIME); 12028c2ecf20Sopenharmony_ci 12038c2ecf20Sopenharmony_ci switch (a->clk_id) { 12048c2ecf20Sopenharmony_ci case CLOCK_REALTIME: ktime_get_real_ts64(&ts); break; 12058c2ecf20Sopenharmony_ci case CLOCK_MONOTONIC: ktime_get_ts64(&ts); break; 12068c2ecf20Sopenharmony_ci case CLOCK_MONOTONIC_RAW: ktime_get_raw_ts64(&ts); break; 12078c2ecf20Sopenharmony_ci default: 12088c2ecf20Sopenharmony_ci ret = -EINVAL; 12098c2ecf20Sopenharmony_ci } 12108c2ecf20Sopenharmony_ci 12118c2ecf20Sopenharmony_ci local_irq_enable(); 12128c2ecf20Sopenharmony_ci 12138c2ecf20Sopenharmony_ci a->tv_sec = ts.tv_sec; 12148c2ecf20Sopenharmony_ci a->tv_nsec = ts.tv_nsec; 12158c2ecf20Sopenharmony_ci a->cycle_timer = cycle_time; 12168c2ecf20Sopenharmony_ci 12178c2ecf20Sopenharmony_ci return ret; 12188c2ecf20Sopenharmony_ci} 12198c2ecf20Sopenharmony_ci 12208c2ecf20Sopenharmony_cistatic int ioctl_get_cycle_timer(struct client *client, union ioctl_arg *arg) 12218c2ecf20Sopenharmony_ci{ 12228c2ecf20Sopenharmony_ci struct fw_cdev_get_cycle_timer *a = &arg->get_cycle_timer; 12238c2ecf20Sopenharmony_ci struct fw_cdev_get_cycle_timer2 ct2; 12248c2ecf20Sopenharmony_ci 12258c2ecf20Sopenharmony_ci ct2.clk_id = CLOCK_REALTIME; 12268c2ecf20Sopenharmony_ci ioctl_get_cycle_timer2(client, (union ioctl_arg *)&ct2); 12278c2ecf20Sopenharmony_ci 12288c2ecf20Sopenharmony_ci a->local_time = ct2.tv_sec * USEC_PER_SEC + ct2.tv_nsec / NSEC_PER_USEC; 12298c2ecf20Sopenharmony_ci a->cycle_timer = ct2.cycle_timer; 12308c2ecf20Sopenharmony_ci 12318c2ecf20Sopenharmony_ci return 0; 12328c2ecf20Sopenharmony_ci} 12338c2ecf20Sopenharmony_ci 12348c2ecf20Sopenharmony_cistatic void iso_resource_work(struct work_struct *work) 12358c2ecf20Sopenharmony_ci{ 12368c2ecf20Sopenharmony_ci struct iso_resource_event *e; 12378c2ecf20Sopenharmony_ci struct iso_resource *r = 12388c2ecf20Sopenharmony_ci container_of(work, struct iso_resource, work.work); 12398c2ecf20Sopenharmony_ci struct client *client = r->client; 12408c2ecf20Sopenharmony_ci int generation, channel, bandwidth, todo; 12418c2ecf20Sopenharmony_ci bool skip, free, success; 12428c2ecf20Sopenharmony_ci 12438c2ecf20Sopenharmony_ci spin_lock_irq(&client->lock); 12448c2ecf20Sopenharmony_ci generation = client->device->generation; 12458c2ecf20Sopenharmony_ci todo = r->todo; 12468c2ecf20Sopenharmony_ci /* Allow 1000ms grace period for other reallocations. */ 12478c2ecf20Sopenharmony_ci if (todo == ISO_RES_ALLOC && 12488c2ecf20Sopenharmony_ci time_before64(get_jiffies_64(), 12498c2ecf20Sopenharmony_ci client->device->card->reset_jiffies + HZ)) { 12508c2ecf20Sopenharmony_ci schedule_iso_resource(r, DIV_ROUND_UP(HZ, 3)); 12518c2ecf20Sopenharmony_ci skip = true; 12528c2ecf20Sopenharmony_ci } else { 12538c2ecf20Sopenharmony_ci /* We could be called twice within the same generation. */ 12548c2ecf20Sopenharmony_ci skip = todo == ISO_RES_REALLOC && 12558c2ecf20Sopenharmony_ci r->generation == generation; 12568c2ecf20Sopenharmony_ci } 12578c2ecf20Sopenharmony_ci free = todo == ISO_RES_DEALLOC || 12588c2ecf20Sopenharmony_ci todo == ISO_RES_ALLOC_ONCE || 12598c2ecf20Sopenharmony_ci todo == ISO_RES_DEALLOC_ONCE; 12608c2ecf20Sopenharmony_ci r->generation = generation; 12618c2ecf20Sopenharmony_ci spin_unlock_irq(&client->lock); 12628c2ecf20Sopenharmony_ci 12638c2ecf20Sopenharmony_ci if (skip) 12648c2ecf20Sopenharmony_ci goto out; 12658c2ecf20Sopenharmony_ci 12668c2ecf20Sopenharmony_ci bandwidth = r->bandwidth; 12678c2ecf20Sopenharmony_ci 12688c2ecf20Sopenharmony_ci fw_iso_resource_manage(client->device->card, generation, 12698c2ecf20Sopenharmony_ci r->channels, &channel, &bandwidth, 12708c2ecf20Sopenharmony_ci todo == ISO_RES_ALLOC || 12718c2ecf20Sopenharmony_ci todo == ISO_RES_REALLOC || 12728c2ecf20Sopenharmony_ci todo == ISO_RES_ALLOC_ONCE); 12738c2ecf20Sopenharmony_ci /* 12748c2ecf20Sopenharmony_ci * Is this generation outdated already? As long as this resource sticks 12758c2ecf20Sopenharmony_ci * in the idr, it will be scheduled again for a newer generation or at 12768c2ecf20Sopenharmony_ci * shutdown. 12778c2ecf20Sopenharmony_ci */ 12788c2ecf20Sopenharmony_ci if (channel == -EAGAIN && 12798c2ecf20Sopenharmony_ci (todo == ISO_RES_ALLOC || todo == ISO_RES_REALLOC)) 12808c2ecf20Sopenharmony_ci goto out; 12818c2ecf20Sopenharmony_ci 12828c2ecf20Sopenharmony_ci success = channel >= 0 || bandwidth > 0; 12838c2ecf20Sopenharmony_ci 12848c2ecf20Sopenharmony_ci spin_lock_irq(&client->lock); 12858c2ecf20Sopenharmony_ci /* 12868c2ecf20Sopenharmony_ci * Transit from allocation to reallocation, except if the client 12878c2ecf20Sopenharmony_ci * requested deallocation in the meantime. 12888c2ecf20Sopenharmony_ci */ 12898c2ecf20Sopenharmony_ci if (r->todo == ISO_RES_ALLOC) 12908c2ecf20Sopenharmony_ci r->todo = ISO_RES_REALLOC; 12918c2ecf20Sopenharmony_ci /* 12928c2ecf20Sopenharmony_ci * Allocation or reallocation failure? Pull this resource out of the 12938c2ecf20Sopenharmony_ci * idr and prepare for deletion, unless the client is shutting down. 12948c2ecf20Sopenharmony_ci */ 12958c2ecf20Sopenharmony_ci if (r->todo == ISO_RES_REALLOC && !success && 12968c2ecf20Sopenharmony_ci !client->in_shutdown && 12978c2ecf20Sopenharmony_ci idr_remove(&client->resource_idr, r->resource.handle)) { 12988c2ecf20Sopenharmony_ci client_put(client); 12998c2ecf20Sopenharmony_ci free = true; 13008c2ecf20Sopenharmony_ci } 13018c2ecf20Sopenharmony_ci spin_unlock_irq(&client->lock); 13028c2ecf20Sopenharmony_ci 13038c2ecf20Sopenharmony_ci if (todo == ISO_RES_ALLOC && channel >= 0) 13048c2ecf20Sopenharmony_ci r->channels = 1ULL << channel; 13058c2ecf20Sopenharmony_ci 13068c2ecf20Sopenharmony_ci if (todo == ISO_RES_REALLOC && success) 13078c2ecf20Sopenharmony_ci goto out; 13088c2ecf20Sopenharmony_ci 13098c2ecf20Sopenharmony_ci if (todo == ISO_RES_ALLOC || todo == ISO_RES_ALLOC_ONCE) { 13108c2ecf20Sopenharmony_ci e = r->e_alloc; 13118c2ecf20Sopenharmony_ci r->e_alloc = NULL; 13128c2ecf20Sopenharmony_ci } else { 13138c2ecf20Sopenharmony_ci e = r->e_dealloc; 13148c2ecf20Sopenharmony_ci r->e_dealloc = NULL; 13158c2ecf20Sopenharmony_ci } 13168c2ecf20Sopenharmony_ci e->iso_resource.handle = r->resource.handle; 13178c2ecf20Sopenharmony_ci e->iso_resource.channel = channel; 13188c2ecf20Sopenharmony_ci e->iso_resource.bandwidth = bandwidth; 13198c2ecf20Sopenharmony_ci 13208c2ecf20Sopenharmony_ci queue_event(client, &e->event, 13218c2ecf20Sopenharmony_ci &e->iso_resource, sizeof(e->iso_resource), NULL, 0); 13228c2ecf20Sopenharmony_ci 13238c2ecf20Sopenharmony_ci if (free) { 13248c2ecf20Sopenharmony_ci cancel_delayed_work(&r->work); 13258c2ecf20Sopenharmony_ci kfree(r->e_alloc); 13268c2ecf20Sopenharmony_ci kfree(r->e_dealloc); 13278c2ecf20Sopenharmony_ci kfree(r); 13288c2ecf20Sopenharmony_ci } 13298c2ecf20Sopenharmony_ci out: 13308c2ecf20Sopenharmony_ci client_put(client); 13318c2ecf20Sopenharmony_ci} 13328c2ecf20Sopenharmony_ci 13338c2ecf20Sopenharmony_cistatic void release_iso_resource(struct client *client, 13348c2ecf20Sopenharmony_ci struct client_resource *resource) 13358c2ecf20Sopenharmony_ci{ 13368c2ecf20Sopenharmony_ci struct iso_resource *r = 13378c2ecf20Sopenharmony_ci container_of(resource, struct iso_resource, resource); 13388c2ecf20Sopenharmony_ci 13398c2ecf20Sopenharmony_ci spin_lock_irq(&client->lock); 13408c2ecf20Sopenharmony_ci r->todo = ISO_RES_DEALLOC; 13418c2ecf20Sopenharmony_ci schedule_iso_resource(r, 0); 13428c2ecf20Sopenharmony_ci spin_unlock_irq(&client->lock); 13438c2ecf20Sopenharmony_ci} 13448c2ecf20Sopenharmony_ci 13458c2ecf20Sopenharmony_cistatic int init_iso_resource(struct client *client, 13468c2ecf20Sopenharmony_ci struct fw_cdev_allocate_iso_resource *request, int todo) 13478c2ecf20Sopenharmony_ci{ 13488c2ecf20Sopenharmony_ci struct iso_resource_event *e1, *e2; 13498c2ecf20Sopenharmony_ci struct iso_resource *r; 13508c2ecf20Sopenharmony_ci int ret; 13518c2ecf20Sopenharmony_ci 13528c2ecf20Sopenharmony_ci if ((request->channels == 0 && request->bandwidth == 0) || 13538c2ecf20Sopenharmony_ci request->bandwidth > BANDWIDTH_AVAILABLE_INITIAL) 13548c2ecf20Sopenharmony_ci return -EINVAL; 13558c2ecf20Sopenharmony_ci 13568c2ecf20Sopenharmony_ci r = kmalloc(sizeof(*r), GFP_KERNEL); 13578c2ecf20Sopenharmony_ci e1 = kmalloc(sizeof(*e1), GFP_KERNEL); 13588c2ecf20Sopenharmony_ci e2 = kmalloc(sizeof(*e2), GFP_KERNEL); 13598c2ecf20Sopenharmony_ci if (r == NULL || e1 == NULL || e2 == NULL) { 13608c2ecf20Sopenharmony_ci ret = -ENOMEM; 13618c2ecf20Sopenharmony_ci goto fail; 13628c2ecf20Sopenharmony_ci } 13638c2ecf20Sopenharmony_ci 13648c2ecf20Sopenharmony_ci INIT_DELAYED_WORK(&r->work, iso_resource_work); 13658c2ecf20Sopenharmony_ci r->client = client; 13668c2ecf20Sopenharmony_ci r->todo = todo; 13678c2ecf20Sopenharmony_ci r->generation = -1; 13688c2ecf20Sopenharmony_ci r->channels = request->channels; 13698c2ecf20Sopenharmony_ci r->bandwidth = request->bandwidth; 13708c2ecf20Sopenharmony_ci r->e_alloc = e1; 13718c2ecf20Sopenharmony_ci r->e_dealloc = e2; 13728c2ecf20Sopenharmony_ci 13738c2ecf20Sopenharmony_ci e1->iso_resource.closure = request->closure; 13748c2ecf20Sopenharmony_ci e1->iso_resource.type = FW_CDEV_EVENT_ISO_RESOURCE_ALLOCATED; 13758c2ecf20Sopenharmony_ci e2->iso_resource.closure = request->closure; 13768c2ecf20Sopenharmony_ci e2->iso_resource.type = FW_CDEV_EVENT_ISO_RESOURCE_DEALLOCATED; 13778c2ecf20Sopenharmony_ci 13788c2ecf20Sopenharmony_ci if (todo == ISO_RES_ALLOC) { 13798c2ecf20Sopenharmony_ci r->resource.release = release_iso_resource; 13808c2ecf20Sopenharmony_ci ret = add_client_resource(client, &r->resource, GFP_KERNEL); 13818c2ecf20Sopenharmony_ci if (ret < 0) 13828c2ecf20Sopenharmony_ci goto fail; 13838c2ecf20Sopenharmony_ci } else { 13848c2ecf20Sopenharmony_ci r->resource.release = NULL; 13858c2ecf20Sopenharmony_ci r->resource.handle = -1; 13868c2ecf20Sopenharmony_ci schedule_iso_resource(r, 0); 13878c2ecf20Sopenharmony_ci } 13888c2ecf20Sopenharmony_ci request->handle = r->resource.handle; 13898c2ecf20Sopenharmony_ci 13908c2ecf20Sopenharmony_ci return 0; 13918c2ecf20Sopenharmony_ci fail: 13928c2ecf20Sopenharmony_ci kfree(r); 13938c2ecf20Sopenharmony_ci kfree(e1); 13948c2ecf20Sopenharmony_ci kfree(e2); 13958c2ecf20Sopenharmony_ci 13968c2ecf20Sopenharmony_ci return ret; 13978c2ecf20Sopenharmony_ci} 13988c2ecf20Sopenharmony_ci 13998c2ecf20Sopenharmony_cistatic int ioctl_allocate_iso_resource(struct client *client, 14008c2ecf20Sopenharmony_ci union ioctl_arg *arg) 14018c2ecf20Sopenharmony_ci{ 14028c2ecf20Sopenharmony_ci return init_iso_resource(client, 14038c2ecf20Sopenharmony_ci &arg->allocate_iso_resource, ISO_RES_ALLOC); 14048c2ecf20Sopenharmony_ci} 14058c2ecf20Sopenharmony_ci 14068c2ecf20Sopenharmony_cistatic int ioctl_deallocate_iso_resource(struct client *client, 14078c2ecf20Sopenharmony_ci union ioctl_arg *arg) 14088c2ecf20Sopenharmony_ci{ 14098c2ecf20Sopenharmony_ci return release_client_resource(client, 14108c2ecf20Sopenharmony_ci arg->deallocate.handle, release_iso_resource, NULL); 14118c2ecf20Sopenharmony_ci} 14128c2ecf20Sopenharmony_ci 14138c2ecf20Sopenharmony_cistatic int ioctl_allocate_iso_resource_once(struct client *client, 14148c2ecf20Sopenharmony_ci union ioctl_arg *arg) 14158c2ecf20Sopenharmony_ci{ 14168c2ecf20Sopenharmony_ci return init_iso_resource(client, 14178c2ecf20Sopenharmony_ci &arg->allocate_iso_resource, ISO_RES_ALLOC_ONCE); 14188c2ecf20Sopenharmony_ci} 14198c2ecf20Sopenharmony_ci 14208c2ecf20Sopenharmony_cistatic int ioctl_deallocate_iso_resource_once(struct client *client, 14218c2ecf20Sopenharmony_ci union ioctl_arg *arg) 14228c2ecf20Sopenharmony_ci{ 14238c2ecf20Sopenharmony_ci return init_iso_resource(client, 14248c2ecf20Sopenharmony_ci &arg->allocate_iso_resource, ISO_RES_DEALLOC_ONCE); 14258c2ecf20Sopenharmony_ci} 14268c2ecf20Sopenharmony_ci 14278c2ecf20Sopenharmony_ci/* 14288c2ecf20Sopenharmony_ci * Returns a speed code: Maximum speed to or from this device, 14298c2ecf20Sopenharmony_ci * limited by the device's link speed, the local node's link speed, 14308c2ecf20Sopenharmony_ci * and all PHY port speeds between the two links. 14318c2ecf20Sopenharmony_ci */ 14328c2ecf20Sopenharmony_cistatic int ioctl_get_speed(struct client *client, union ioctl_arg *arg) 14338c2ecf20Sopenharmony_ci{ 14348c2ecf20Sopenharmony_ci return client->device->max_speed; 14358c2ecf20Sopenharmony_ci} 14368c2ecf20Sopenharmony_ci 14378c2ecf20Sopenharmony_cistatic int ioctl_send_broadcast_request(struct client *client, 14388c2ecf20Sopenharmony_ci union ioctl_arg *arg) 14398c2ecf20Sopenharmony_ci{ 14408c2ecf20Sopenharmony_ci struct fw_cdev_send_request *a = &arg->send_request; 14418c2ecf20Sopenharmony_ci 14428c2ecf20Sopenharmony_ci switch (a->tcode) { 14438c2ecf20Sopenharmony_ci case TCODE_WRITE_QUADLET_REQUEST: 14448c2ecf20Sopenharmony_ci case TCODE_WRITE_BLOCK_REQUEST: 14458c2ecf20Sopenharmony_ci break; 14468c2ecf20Sopenharmony_ci default: 14478c2ecf20Sopenharmony_ci return -EINVAL; 14488c2ecf20Sopenharmony_ci } 14498c2ecf20Sopenharmony_ci 14508c2ecf20Sopenharmony_ci /* Security policy: Only allow accesses to Units Space. */ 14518c2ecf20Sopenharmony_ci if (a->offset < CSR_REGISTER_BASE + CSR_CONFIG_ROM_END) 14528c2ecf20Sopenharmony_ci return -EACCES; 14538c2ecf20Sopenharmony_ci 14548c2ecf20Sopenharmony_ci return init_request(client, a, LOCAL_BUS | 0x3f, SCODE_100); 14558c2ecf20Sopenharmony_ci} 14568c2ecf20Sopenharmony_ci 14578c2ecf20Sopenharmony_cistatic int ioctl_send_stream_packet(struct client *client, union ioctl_arg *arg) 14588c2ecf20Sopenharmony_ci{ 14598c2ecf20Sopenharmony_ci struct fw_cdev_send_stream_packet *a = &arg->send_stream_packet; 14608c2ecf20Sopenharmony_ci struct fw_cdev_send_request request; 14618c2ecf20Sopenharmony_ci int dest; 14628c2ecf20Sopenharmony_ci 14638c2ecf20Sopenharmony_ci if (a->speed > client->device->card->link_speed || 14648c2ecf20Sopenharmony_ci a->length > 1024 << a->speed) 14658c2ecf20Sopenharmony_ci return -EIO; 14668c2ecf20Sopenharmony_ci 14678c2ecf20Sopenharmony_ci if (a->tag > 3 || a->channel > 63 || a->sy > 15) 14688c2ecf20Sopenharmony_ci return -EINVAL; 14698c2ecf20Sopenharmony_ci 14708c2ecf20Sopenharmony_ci dest = fw_stream_packet_destination_id(a->tag, a->channel, a->sy); 14718c2ecf20Sopenharmony_ci request.tcode = TCODE_STREAM_DATA; 14728c2ecf20Sopenharmony_ci request.length = a->length; 14738c2ecf20Sopenharmony_ci request.closure = a->closure; 14748c2ecf20Sopenharmony_ci request.data = a->data; 14758c2ecf20Sopenharmony_ci request.generation = a->generation; 14768c2ecf20Sopenharmony_ci 14778c2ecf20Sopenharmony_ci return init_request(client, &request, dest, a->speed); 14788c2ecf20Sopenharmony_ci} 14798c2ecf20Sopenharmony_ci 14808c2ecf20Sopenharmony_cistatic void outbound_phy_packet_callback(struct fw_packet *packet, 14818c2ecf20Sopenharmony_ci struct fw_card *card, int status) 14828c2ecf20Sopenharmony_ci{ 14838c2ecf20Sopenharmony_ci struct outbound_phy_packet_event *e = 14848c2ecf20Sopenharmony_ci container_of(packet, struct outbound_phy_packet_event, p); 14858c2ecf20Sopenharmony_ci struct client *e_client; 14868c2ecf20Sopenharmony_ci 14878c2ecf20Sopenharmony_ci switch (status) { 14888c2ecf20Sopenharmony_ci /* expected: */ 14898c2ecf20Sopenharmony_ci case ACK_COMPLETE: e->phy_packet.rcode = RCODE_COMPLETE; break; 14908c2ecf20Sopenharmony_ci /* should never happen with PHY packets: */ 14918c2ecf20Sopenharmony_ci case ACK_PENDING: e->phy_packet.rcode = RCODE_COMPLETE; break; 14928c2ecf20Sopenharmony_ci case ACK_BUSY_X: 14938c2ecf20Sopenharmony_ci case ACK_BUSY_A: 14948c2ecf20Sopenharmony_ci case ACK_BUSY_B: e->phy_packet.rcode = RCODE_BUSY; break; 14958c2ecf20Sopenharmony_ci case ACK_DATA_ERROR: e->phy_packet.rcode = RCODE_DATA_ERROR; break; 14968c2ecf20Sopenharmony_ci case ACK_TYPE_ERROR: e->phy_packet.rcode = RCODE_TYPE_ERROR; break; 14978c2ecf20Sopenharmony_ci /* stale generation; cancelled; on certain controllers: no ack */ 14988c2ecf20Sopenharmony_ci default: e->phy_packet.rcode = status; break; 14998c2ecf20Sopenharmony_ci } 15008c2ecf20Sopenharmony_ci e->phy_packet.data[0] = packet->timestamp; 15018c2ecf20Sopenharmony_ci 15028c2ecf20Sopenharmony_ci e_client = e->client; 15038c2ecf20Sopenharmony_ci queue_event(e->client, &e->event, &e->phy_packet, 15048c2ecf20Sopenharmony_ci sizeof(e->phy_packet) + e->phy_packet.length, NULL, 0); 15058c2ecf20Sopenharmony_ci client_put(e_client); 15068c2ecf20Sopenharmony_ci} 15078c2ecf20Sopenharmony_ci 15088c2ecf20Sopenharmony_cistatic int ioctl_send_phy_packet(struct client *client, union ioctl_arg *arg) 15098c2ecf20Sopenharmony_ci{ 15108c2ecf20Sopenharmony_ci struct fw_cdev_send_phy_packet *a = &arg->send_phy_packet; 15118c2ecf20Sopenharmony_ci struct fw_card *card = client->device->card; 15128c2ecf20Sopenharmony_ci struct outbound_phy_packet_event *e; 15138c2ecf20Sopenharmony_ci 15148c2ecf20Sopenharmony_ci /* Access policy: Allow this ioctl only on local nodes' device files. */ 15158c2ecf20Sopenharmony_ci if (!client->device->is_local) 15168c2ecf20Sopenharmony_ci return -ENOSYS; 15178c2ecf20Sopenharmony_ci 15188c2ecf20Sopenharmony_ci e = kzalloc(sizeof(*e) + 4, GFP_KERNEL); 15198c2ecf20Sopenharmony_ci if (e == NULL) 15208c2ecf20Sopenharmony_ci return -ENOMEM; 15218c2ecf20Sopenharmony_ci 15228c2ecf20Sopenharmony_ci client_get(client); 15238c2ecf20Sopenharmony_ci e->client = client; 15248c2ecf20Sopenharmony_ci e->p.speed = SCODE_100; 15258c2ecf20Sopenharmony_ci e->p.generation = a->generation; 15268c2ecf20Sopenharmony_ci e->p.header[0] = TCODE_LINK_INTERNAL << 4; 15278c2ecf20Sopenharmony_ci e->p.header[1] = a->data[0]; 15288c2ecf20Sopenharmony_ci e->p.header[2] = a->data[1]; 15298c2ecf20Sopenharmony_ci e->p.header_length = 12; 15308c2ecf20Sopenharmony_ci e->p.callback = outbound_phy_packet_callback; 15318c2ecf20Sopenharmony_ci e->phy_packet.closure = a->closure; 15328c2ecf20Sopenharmony_ci e->phy_packet.type = FW_CDEV_EVENT_PHY_PACKET_SENT; 15338c2ecf20Sopenharmony_ci if (is_ping_packet(a->data)) 15348c2ecf20Sopenharmony_ci e->phy_packet.length = 4; 15358c2ecf20Sopenharmony_ci 15368c2ecf20Sopenharmony_ci card->driver->send_request(card, &e->p); 15378c2ecf20Sopenharmony_ci 15388c2ecf20Sopenharmony_ci return 0; 15398c2ecf20Sopenharmony_ci} 15408c2ecf20Sopenharmony_ci 15418c2ecf20Sopenharmony_cistatic int ioctl_receive_phy_packets(struct client *client, union ioctl_arg *arg) 15428c2ecf20Sopenharmony_ci{ 15438c2ecf20Sopenharmony_ci struct fw_cdev_receive_phy_packets *a = &arg->receive_phy_packets; 15448c2ecf20Sopenharmony_ci struct fw_card *card = client->device->card; 15458c2ecf20Sopenharmony_ci 15468c2ecf20Sopenharmony_ci /* Access policy: Allow this ioctl only on local nodes' device files. */ 15478c2ecf20Sopenharmony_ci if (!client->device->is_local) 15488c2ecf20Sopenharmony_ci return -ENOSYS; 15498c2ecf20Sopenharmony_ci 15508c2ecf20Sopenharmony_ci spin_lock_irq(&card->lock); 15518c2ecf20Sopenharmony_ci 15528c2ecf20Sopenharmony_ci list_move_tail(&client->phy_receiver_link, &card->phy_receiver_list); 15538c2ecf20Sopenharmony_ci client->phy_receiver_closure = a->closure; 15548c2ecf20Sopenharmony_ci 15558c2ecf20Sopenharmony_ci spin_unlock_irq(&card->lock); 15568c2ecf20Sopenharmony_ci 15578c2ecf20Sopenharmony_ci return 0; 15588c2ecf20Sopenharmony_ci} 15598c2ecf20Sopenharmony_ci 15608c2ecf20Sopenharmony_civoid fw_cdev_handle_phy_packet(struct fw_card *card, struct fw_packet *p) 15618c2ecf20Sopenharmony_ci{ 15628c2ecf20Sopenharmony_ci struct client *client; 15638c2ecf20Sopenharmony_ci struct inbound_phy_packet_event *e; 15648c2ecf20Sopenharmony_ci unsigned long flags; 15658c2ecf20Sopenharmony_ci 15668c2ecf20Sopenharmony_ci spin_lock_irqsave(&card->lock, flags); 15678c2ecf20Sopenharmony_ci 15688c2ecf20Sopenharmony_ci list_for_each_entry(client, &card->phy_receiver_list, phy_receiver_link) { 15698c2ecf20Sopenharmony_ci e = kmalloc(sizeof(*e) + 8, GFP_ATOMIC); 15708c2ecf20Sopenharmony_ci if (e == NULL) 15718c2ecf20Sopenharmony_ci break; 15728c2ecf20Sopenharmony_ci 15738c2ecf20Sopenharmony_ci e->phy_packet.closure = client->phy_receiver_closure; 15748c2ecf20Sopenharmony_ci e->phy_packet.type = FW_CDEV_EVENT_PHY_PACKET_RECEIVED; 15758c2ecf20Sopenharmony_ci e->phy_packet.rcode = RCODE_COMPLETE; 15768c2ecf20Sopenharmony_ci e->phy_packet.length = 8; 15778c2ecf20Sopenharmony_ci e->phy_packet.data[0] = p->header[1]; 15788c2ecf20Sopenharmony_ci e->phy_packet.data[1] = p->header[2]; 15798c2ecf20Sopenharmony_ci queue_event(client, &e->event, 15808c2ecf20Sopenharmony_ci &e->phy_packet, sizeof(e->phy_packet) + 8, NULL, 0); 15818c2ecf20Sopenharmony_ci } 15828c2ecf20Sopenharmony_ci 15838c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&card->lock, flags); 15848c2ecf20Sopenharmony_ci} 15858c2ecf20Sopenharmony_ci 15868c2ecf20Sopenharmony_cistatic int (* const ioctl_handlers[])(struct client *, union ioctl_arg *) = { 15878c2ecf20Sopenharmony_ci [0x00] = ioctl_get_info, 15888c2ecf20Sopenharmony_ci [0x01] = ioctl_send_request, 15898c2ecf20Sopenharmony_ci [0x02] = ioctl_allocate, 15908c2ecf20Sopenharmony_ci [0x03] = ioctl_deallocate, 15918c2ecf20Sopenharmony_ci [0x04] = ioctl_send_response, 15928c2ecf20Sopenharmony_ci [0x05] = ioctl_initiate_bus_reset, 15938c2ecf20Sopenharmony_ci [0x06] = ioctl_add_descriptor, 15948c2ecf20Sopenharmony_ci [0x07] = ioctl_remove_descriptor, 15958c2ecf20Sopenharmony_ci [0x08] = ioctl_create_iso_context, 15968c2ecf20Sopenharmony_ci [0x09] = ioctl_queue_iso, 15978c2ecf20Sopenharmony_ci [0x0a] = ioctl_start_iso, 15988c2ecf20Sopenharmony_ci [0x0b] = ioctl_stop_iso, 15998c2ecf20Sopenharmony_ci [0x0c] = ioctl_get_cycle_timer, 16008c2ecf20Sopenharmony_ci [0x0d] = ioctl_allocate_iso_resource, 16018c2ecf20Sopenharmony_ci [0x0e] = ioctl_deallocate_iso_resource, 16028c2ecf20Sopenharmony_ci [0x0f] = ioctl_allocate_iso_resource_once, 16038c2ecf20Sopenharmony_ci [0x10] = ioctl_deallocate_iso_resource_once, 16048c2ecf20Sopenharmony_ci [0x11] = ioctl_get_speed, 16058c2ecf20Sopenharmony_ci [0x12] = ioctl_send_broadcast_request, 16068c2ecf20Sopenharmony_ci [0x13] = ioctl_send_stream_packet, 16078c2ecf20Sopenharmony_ci [0x14] = ioctl_get_cycle_timer2, 16088c2ecf20Sopenharmony_ci [0x15] = ioctl_send_phy_packet, 16098c2ecf20Sopenharmony_ci [0x16] = ioctl_receive_phy_packets, 16108c2ecf20Sopenharmony_ci [0x17] = ioctl_set_iso_channels, 16118c2ecf20Sopenharmony_ci [0x18] = ioctl_flush_iso, 16128c2ecf20Sopenharmony_ci}; 16138c2ecf20Sopenharmony_ci 16148c2ecf20Sopenharmony_cistatic int dispatch_ioctl(struct client *client, 16158c2ecf20Sopenharmony_ci unsigned int cmd, void __user *arg) 16168c2ecf20Sopenharmony_ci{ 16178c2ecf20Sopenharmony_ci union ioctl_arg buffer; 16188c2ecf20Sopenharmony_ci int ret; 16198c2ecf20Sopenharmony_ci 16208c2ecf20Sopenharmony_ci if (fw_device_is_shutdown(client->device)) 16218c2ecf20Sopenharmony_ci return -ENODEV; 16228c2ecf20Sopenharmony_ci 16238c2ecf20Sopenharmony_ci if (_IOC_TYPE(cmd) != '#' || 16248c2ecf20Sopenharmony_ci _IOC_NR(cmd) >= ARRAY_SIZE(ioctl_handlers) || 16258c2ecf20Sopenharmony_ci _IOC_SIZE(cmd) > sizeof(buffer)) 16268c2ecf20Sopenharmony_ci return -ENOTTY; 16278c2ecf20Sopenharmony_ci 16288c2ecf20Sopenharmony_ci memset(&buffer, 0, sizeof(buffer)); 16298c2ecf20Sopenharmony_ci 16308c2ecf20Sopenharmony_ci if (_IOC_DIR(cmd) & _IOC_WRITE) 16318c2ecf20Sopenharmony_ci if (copy_from_user(&buffer, arg, _IOC_SIZE(cmd))) 16328c2ecf20Sopenharmony_ci return -EFAULT; 16338c2ecf20Sopenharmony_ci 16348c2ecf20Sopenharmony_ci ret = ioctl_handlers[_IOC_NR(cmd)](client, &buffer); 16358c2ecf20Sopenharmony_ci if (ret < 0) 16368c2ecf20Sopenharmony_ci return ret; 16378c2ecf20Sopenharmony_ci 16388c2ecf20Sopenharmony_ci if (_IOC_DIR(cmd) & _IOC_READ) 16398c2ecf20Sopenharmony_ci if (copy_to_user(arg, &buffer, _IOC_SIZE(cmd))) 16408c2ecf20Sopenharmony_ci return -EFAULT; 16418c2ecf20Sopenharmony_ci 16428c2ecf20Sopenharmony_ci return ret; 16438c2ecf20Sopenharmony_ci} 16448c2ecf20Sopenharmony_ci 16458c2ecf20Sopenharmony_cistatic long fw_device_op_ioctl(struct file *file, 16468c2ecf20Sopenharmony_ci unsigned int cmd, unsigned long arg) 16478c2ecf20Sopenharmony_ci{ 16488c2ecf20Sopenharmony_ci return dispatch_ioctl(file->private_data, cmd, (void __user *)arg); 16498c2ecf20Sopenharmony_ci} 16508c2ecf20Sopenharmony_ci 16518c2ecf20Sopenharmony_cistatic int fw_device_op_mmap(struct file *file, struct vm_area_struct *vma) 16528c2ecf20Sopenharmony_ci{ 16538c2ecf20Sopenharmony_ci struct client *client = file->private_data; 16548c2ecf20Sopenharmony_ci unsigned long size; 16558c2ecf20Sopenharmony_ci int page_count, ret; 16568c2ecf20Sopenharmony_ci 16578c2ecf20Sopenharmony_ci if (fw_device_is_shutdown(client->device)) 16588c2ecf20Sopenharmony_ci return -ENODEV; 16598c2ecf20Sopenharmony_ci 16608c2ecf20Sopenharmony_ci /* FIXME: We could support multiple buffers, but we don't. */ 16618c2ecf20Sopenharmony_ci if (client->buffer.pages != NULL) 16628c2ecf20Sopenharmony_ci return -EBUSY; 16638c2ecf20Sopenharmony_ci 16648c2ecf20Sopenharmony_ci if (!(vma->vm_flags & VM_SHARED)) 16658c2ecf20Sopenharmony_ci return -EINVAL; 16668c2ecf20Sopenharmony_ci 16678c2ecf20Sopenharmony_ci if (vma->vm_start & ~PAGE_MASK) 16688c2ecf20Sopenharmony_ci return -EINVAL; 16698c2ecf20Sopenharmony_ci 16708c2ecf20Sopenharmony_ci client->vm_start = vma->vm_start; 16718c2ecf20Sopenharmony_ci size = vma->vm_end - vma->vm_start; 16728c2ecf20Sopenharmony_ci page_count = size >> PAGE_SHIFT; 16738c2ecf20Sopenharmony_ci if (size & ~PAGE_MASK) 16748c2ecf20Sopenharmony_ci return -EINVAL; 16758c2ecf20Sopenharmony_ci 16768c2ecf20Sopenharmony_ci ret = fw_iso_buffer_alloc(&client->buffer, page_count); 16778c2ecf20Sopenharmony_ci if (ret < 0) 16788c2ecf20Sopenharmony_ci return ret; 16798c2ecf20Sopenharmony_ci 16808c2ecf20Sopenharmony_ci spin_lock_irq(&client->lock); 16818c2ecf20Sopenharmony_ci if (client->iso_context) { 16828c2ecf20Sopenharmony_ci ret = fw_iso_buffer_map_dma(&client->buffer, 16838c2ecf20Sopenharmony_ci client->device->card, 16848c2ecf20Sopenharmony_ci iso_dma_direction(client->iso_context)); 16858c2ecf20Sopenharmony_ci client->buffer_is_mapped = (ret == 0); 16868c2ecf20Sopenharmony_ci } 16878c2ecf20Sopenharmony_ci spin_unlock_irq(&client->lock); 16888c2ecf20Sopenharmony_ci if (ret < 0) 16898c2ecf20Sopenharmony_ci goto fail; 16908c2ecf20Sopenharmony_ci 16918c2ecf20Sopenharmony_ci ret = vm_map_pages_zero(vma, client->buffer.pages, 16928c2ecf20Sopenharmony_ci client->buffer.page_count); 16938c2ecf20Sopenharmony_ci if (ret < 0) 16948c2ecf20Sopenharmony_ci goto fail; 16958c2ecf20Sopenharmony_ci 16968c2ecf20Sopenharmony_ci return 0; 16978c2ecf20Sopenharmony_ci fail: 16988c2ecf20Sopenharmony_ci fw_iso_buffer_destroy(&client->buffer, client->device->card); 16998c2ecf20Sopenharmony_ci return ret; 17008c2ecf20Sopenharmony_ci} 17018c2ecf20Sopenharmony_ci 17028c2ecf20Sopenharmony_cistatic int is_outbound_transaction_resource(int id, void *p, void *data) 17038c2ecf20Sopenharmony_ci{ 17048c2ecf20Sopenharmony_ci struct client_resource *resource = p; 17058c2ecf20Sopenharmony_ci 17068c2ecf20Sopenharmony_ci return resource->release == release_transaction; 17078c2ecf20Sopenharmony_ci} 17088c2ecf20Sopenharmony_ci 17098c2ecf20Sopenharmony_cistatic int has_outbound_transactions(struct client *client) 17108c2ecf20Sopenharmony_ci{ 17118c2ecf20Sopenharmony_ci int ret; 17128c2ecf20Sopenharmony_ci 17138c2ecf20Sopenharmony_ci spin_lock_irq(&client->lock); 17148c2ecf20Sopenharmony_ci ret = idr_for_each(&client->resource_idr, 17158c2ecf20Sopenharmony_ci is_outbound_transaction_resource, NULL); 17168c2ecf20Sopenharmony_ci spin_unlock_irq(&client->lock); 17178c2ecf20Sopenharmony_ci 17188c2ecf20Sopenharmony_ci return ret; 17198c2ecf20Sopenharmony_ci} 17208c2ecf20Sopenharmony_ci 17218c2ecf20Sopenharmony_cistatic int shutdown_resource(int id, void *p, void *data) 17228c2ecf20Sopenharmony_ci{ 17238c2ecf20Sopenharmony_ci struct client_resource *resource = p; 17248c2ecf20Sopenharmony_ci struct client *client = data; 17258c2ecf20Sopenharmony_ci 17268c2ecf20Sopenharmony_ci resource->release(client, resource); 17278c2ecf20Sopenharmony_ci client_put(client); 17288c2ecf20Sopenharmony_ci 17298c2ecf20Sopenharmony_ci return 0; 17308c2ecf20Sopenharmony_ci} 17318c2ecf20Sopenharmony_ci 17328c2ecf20Sopenharmony_cistatic int fw_device_op_release(struct inode *inode, struct file *file) 17338c2ecf20Sopenharmony_ci{ 17348c2ecf20Sopenharmony_ci struct client *client = file->private_data; 17358c2ecf20Sopenharmony_ci struct event *event, *next_event; 17368c2ecf20Sopenharmony_ci 17378c2ecf20Sopenharmony_ci spin_lock_irq(&client->device->card->lock); 17388c2ecf20Sopenharmony_ci list_del(&client->phy_receiver_link); 17398c2ecf20Sopenharmony_ci spin_unlock_irq(&client->device->card->lock); 17408c2ecf20Sopenharmony_ci 17418c2ecf20Sopenharmony_ci mutex_lock(&client->device->client_list_mutex); 17428c2ecf20Sopenharmony_ci list_del(&client->link); 17438c2ecf20Sopenharmony_ci mutex_unlock(&client->device->client_list_mutex); 17448c2ecf20Sopenharmony_ci 17458c2ecf20Sopenharmony_ci if (client->iso_context) 17468c2ecf20Sopenharmony_ci fw_iso_context_destroy(client->iso_context); 17478c2ecf20Sopenharmony_ci 17488c2ecf20Sopenharmony_ci if (client->buffer.pages) 17498c2ecf20Sopenharmony_ci fw_iso_buffer_destroy(&client->buffer, client->device->card); 17508c2ecf20Sopenharmony_ci 17518c2ecf20Sopenharmony_ci /* Freeze client->resource_idr and client->event_list */ 17528c2ecf20Sopenharmony_ci spin_lock_irq(&client->lock); 17538c2ecf20Sopenharmony_ci client->in_shutdown = true; 17548c2ecf20Sopenharmony_ci spin_unlock_irq(&client->lock); 17558c2ecf20Sopenharmony_ci 17568c2ecf20Sopenharmony_ci wait_event(client->tx_flush_wait, !has_outbound_transactions(client)); 17578c2ecf20Sopenharmony_ci 17588c2ecf20Sopenharmony_ci idr_for_each(&client->resource_idr, shutdown_resource, client); 17598c2ecf20Sopenharmony_ci idr_destroy(&client->resource_idr); 17608c2ecf20Sopenharmony_ci 17618c2ecf20Sopenharmony_ci list_for_each_entry_safe(event, next_event, &client->event_list, link) 17628c2ecf20Sopenharmony_ci kfree(event); 17638c2ecf20Sopenharmony_ci 17648c2ecf20Sopenharmony_ci client_put(client); 17658c2ecf20Sopenharmony_ci 17668c2ecf20Sopenharmony_ci return 0; 17678c2ecf20Sopenharmony_ci} 17688c2ecf20Sopenharmony_ci 17698c2ecf20Sopenharmony_cistatic __poll_t fw_device_op_poll(struct file *file, poll_table * pt) 17708c2ecf20Sopenharmony_ci{ 17718c2ecf20Sopenharmony_ci struct client *client = file->private_data; 17728c2ecf20Sopenharmony_ci __poll_t mask = 0; 17738c2ecf20Sopenharmony_ci 17748c2ecf20Sopenharmony_ci poll_wait(file, &client->wait, pt); 17758c2ecf20Sopenharmony_ci 17768c2ecf20Sopenharmony_ci if (fw_device_is_shutdown(client->device)) 17778c2ecf20Sopenharmony_ci mask |= EPOLLHUP | EPOLLERR; 17788c2ecf20Sopenharmony_ci if (!list_empty(&client->event_list)) 17798c2ecf20Sopenharmony_ci mask |= EPOLLIN | EPOLLRDNORM; 17808c2ecf20Sopenharmony_ci 17818c2ecf20Sopenharmony_ci return mask; 17828c2ecf20Sopenharmony_ci} 17838c2ecf20Sopenharmony_ci 17848c2ecf20Sopenharmony_ciconst struct file_operations fw_device_ops = { 17858c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 17868c2ecf20Sopenharmony_ci .llseek = no_llseek, 17878c2ecf20Sopenharmony_ci .open = fw_device_op_open, 17888c2ecf20Sopenharmony_ci .read = fw_device_op_read, 17898c2ecf20Sopenharmony_ci .unlocked_ioctl = fw_device_op_ioctl, 17908c2ecf20Sopenharmony_ci .mmap = fw_device_op_mmap, 17918c2ecf20Sopenharmony_ci .release = fw_device_op_release, 17928c2ecf20Sopenharmony_ci .poll = fw_device_op_poll, 17938c2ecf20Sopenharmony_ci .compat_ioctl = compat_ptr_ioctl, 17948c2ecf20Sopenharmony_ci}; 1795