162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Char device for device raw access 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2005-2007 Kristian Hoegsberg <krh@bitplanet.net> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/bug.h> 962306a36Sopenharmony_ci#include <linux/compat.h> 1062306a36Sopenharmony_ci#include <linux/delay.h> 1162306a36Sopenharmony_ci#include <linux/device.h> 1262306a36Sopenharmony_ci#include <linux/dma-mapping.h> 1362306a36Sopenharmony_ci#include <linux/err.h> 1462306a36Sopenharmony_ci#include <linux/errno.h> 1562306a36Sopenharmony_ci#include <linux/firewire.h> 1662306a36Sopenharmony_ci#include <linux/firewire-cdev.h> 1762306a36Sopenharmony_ci#include <linux/idr.h> 1862306a36Sopenharmony_ci#include <linux/irqflags.h> 1962306a36Sopenharmony_ci#include <linux/jiffies.h> 2062306a36Sopenharmony_ci#include <linux/kernel.h> 2162306a36Sopenharmony_ci#include <linux/kref.h> 2262306a36Sopenharmony_ci#include <linux/mm.h> 2362306a36Sopenharmony_ci#include <linux/module.h> 2462306a36Sopenharmony_ci#include <linux/mutex.h> 2562306a36Sopenharmony_ci#include <linux/poll.h> 2662306a36Sopenharmony_ci#include <linux/sched.h> /* required for linux/wait.h */ 2762306a36Sopenharmony_ci#include <linux/slab.h> 2862306a36Sopenharmony_ci#include <linux/spinlock.h> 2962306a36Sopenharmony_ci#include <linux/string.h> 3062306a36Sopenharmony_ci#include <linux/time.h> 3162306a36Sopenharmony_ci#include <linux/uaccess.h> 3262306a36Sopenharmony_ci#include <linux/vmalloc.h> 3362306a36Sopenharmony_ci#include <linux/wait.h> 3462306a36Sopenharmony_ci#include <linux/workqueue.h> 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci#include "core.h" 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci/* 4062306a36Sopenharmony_ci * ABI version history is documented in linux/firewire-cdev.h. 4162306a36Sopenharmony_ci */ 4262306a36Sopenharmony_ci#define FW_CDEV_KERNEL_VERSION 5 4362306a36Sopenharmony_ci#define FW_CDEV_VERSION_EVENT_REQUEST2 4 4462306a36Sopenharmony_ci#define FW_CDEV_VERSION_ALLOCATE_REGION_END 4 4562306a36Sopenharmony_ci#define FW_CDEV_VERSION_AUTO_FLUSH_ISO_OVERFLOW 5 4662306a36Sopenharmony_ci#define FW_CDEV_VERSION_EVENT_ASYNC_TSTAMP 6 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cistruct client { 4962306a36Sopenharmony_ci u32 version; 5062306a36Sopenharmony_ci struct fw_device *device; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci spinlock_t lock; 5362306a36Sopenharmony_ci bool in_shutdown; 5462306a36Sopenharmony_ci struct idr resource_idr; 5562306a36Sopenharmony_ci struct list_head event_list; 5662306a36Sopenharmony_ci wait_queue_head_t wait; 5762306a36Sopenharmony_ci wait_queue_head_t tx_flush_wait; 5862306a36Sopenharmony_ci u64 bus_reset_closure; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci struct fw_iso_context *iso_context; 6162306a36Sopenharmony_ci u64 iso_closure; 6262306a36Sopenharmony_ci struct fw_iso_buffer buffer; 6362306a36Sopenharmony_ci unsigned long vm_start; 6462306a36Sopenharmony_ci bool buffer_is_mapped; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci struct list_head phy_receiver_link; 6762306a36Sopenharmony_ci u64 phy_receiver_closure; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci struct list_head link; 7062306a36Sopenharmony_ci struct kref kref; 7162306a36Sopenharmony_ci}; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_cistatic inline void client_get(struct client *client) 7462306a36Sopenharmony_ci{ 7562306a36Sopenharmony_ci kref_get(&client->kref); 7662306a36Sopenharmony_ci} 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_cistatic void client_release(struct kref *kref) 7962306a36Sopenharmony_ci{ 8062306a36Sopenharmony_ci struct client *client = container_of(kref, struct client, kref); 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci fw_device_put(client->device); 8362306a36Sopenharmony_ci kfree(client); 8462306a36Sopenharmony_ci} 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_cistatic void client_put(struct client *client) 8762306a36Sopenharmony_ci{ 8862306a36Sopenharmony_ci kref_put(&client->kref, client_release); 8962306a36Sopenharmony_ci} 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_cistruct client_resource; 9262306a36Sopenharmony_citypedef void (*client_resource_release_fn_t)(struct client *, 9362306a36Sopenharmony_ci struct client_resource *); 9462306a36Sopenharmony_cistruct client_resource { 9562306a36Sopenharmony_ci client_resource_release_fn_t release; 9662306a36Sopenharmony_ci int handle; 9762306a36Sopenharmony_ci}; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_cistruct address_handler_resource { 10062306a36Sopenharmony_ci struct client_resource resource; 10162306a36Sopenharmony_ci struct fw_address_handler handler; 10262306a36Sopenharmony_ci __u64 closure; 10362306a36Sopenharmony_ci struct client *client; 10462306a36Sopenharmony_ci}; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_cistruct outbound_transaction_resource { 10762306a36Sopenharmony_ci struct client_resource resource; 10862306a36Sopenharmony_ci struct fw_transaction transaction; 10962306a36Sopenharmony_ci}; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_cistruct inbound_transaction_resource { 11262306a36Sopenharmony_ci struct client_resource resource; 11362306a36Sopenharmony_ci struct fw_card *card; 11462306a36Sopenharmony_ci struct fw_request *request; 11562306a36Sopenharmony_ci bool is_fcp; 11662306a36Sopenharmony_ci void *data; 11762306a36Sopenharmony_ci size_t length; 11862306a36Sopenharmony_ci}; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_cistruct descriptor_resource { 12162306a36Sopenharmony_ci struct client_resource resource; 12262306a36Sopenharmony_ci struct fw_descriptor descriptor; 12362306a36Sopenharmony_ci u32 data[]; 12462306a36Sopenharmony_ci}; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_cistruct iso_resource { 12762306a36Sopenharmony_ci struct client_resource resource; 12862306a36Sopenharmony_ci struct client *client; 12962306a36Sopenharmony_ci /* Schedule work and access todo only with client->lock held. */ 13062306a36Sopenharmony_ci struct delayed_work work; 13162306a36Sopenharmony_ci enum {ISO_RES_ALLOC, ISO_RES_REALLOC, ISO_RES_DEALLOC, 13262306a36Sopenharmony_ci ISO_RES_ALLOC_ONCE, ISO_RES_DEALLOC_ONCE,} todo; 13362306a36Sopenharmony_ci int generation; 13462306a36Sopenharmony_ci u64 channels; 13562306a36Sopenharmony_ci s32 bandwidth; 13662306a36Sopenharmony_ci struct iso_resource_event *e_alloc, *e_dealloc; 13762306a36Sopenharmony_ci}; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_cistatic void release_iso_resource(struct client *, struct client_resource *); 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_cistatic void schedule_iso_resource(struct iso_resource *r, unsigned long delay) 14262306a36Sopenharmony_ci{ 14362306a36Sopenharmony_ci client_get(r->client); 14462306a36Sopenharmony_ci if (!queue_delayed_work(fw_workqueue, &r->work, delay)) 14562306a36Sopenharmony_ci client_put(r->client); 14662306a36Sopenharmony_ci} 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_cistatic void schedule_if_iso_resource(struct client_resource *resource) 14962306a36Sopenharmony_ci{ 15062306a36Sopenharmony_ci if (resource->release == release_iso_resource) 15162306a36Sopenharmony_ci schedule_iso_resource(container_of(resource, 15262306a36Sopenharmony_ci struct iso_resource, resource), 0); 15362306a36Sopenharmony_ci} 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci/* 15662306a36Sopenharmony_ci * dequeue_event() just kfree()'s the event, so the event has to be 15762306a36Sopenharmony_ci * the first field in a struct XYZ_event. 15862306a36Sopenharmony_ci */ 15962306a36Sopenharmony_cistruct event { 16062306a36Sopenharmony_ci struct { void *data; size_t size; } v[2]; 16162306a36Sopenharmony_ci struct list_head link; 16262306a36Sopenharmony_ci}; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_cistruct bus_reset_event { 16562306a36Sopenharmony_ci struct event event; 16662306a36Sopenharmony_ci struct fw_cdev_event_bus_reset reset; 16762306a36Sopenharmony_ci}; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_cistruct outbound_transaction_event { 17062306a36Sopenharmony_ci struct event event; 17162306a36Sopenharmony_ci struct client *client; 17262306a36Sopenharmony_ci struct outbound_transaction_resource r; 17362306a36Sopenharmony_ci union { 17462306a36Sopenharmony_ci struct fw_cdev_event_response without_tstamp; 17562306a36Sopenharmony_ci struct fw_cdev_event_response2 with_tstamp; 17662306a36Sopenharmony_ci } rsp; 17762306a36Sopenharmony_ci}; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_cistruct inbound_transaction_event { 18062306a36Sopenharmony_ci struct event event; 18162306a36Sopenharmony_ci union { 18262306a36Sopenharmony_ci struct fw_cdev_event_request request; 18362306a36Sopenharmony_ci struct fw_cdev_event_request2 request2; 18462306a36Sopenharmony_ci struct fw_cdev_event_request3 with_tstamp; 18562306a36Sopenharmony_ci } req; 18662306a36Sopenharmony_ci}; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_cistruct iso_interrupt_event { 18962306a36Sopenharmony_ci struct event event; 19062306a36Sopenharmony_ci struct fw_cdev_event_iso_interrupt interrupt; 19162306a36Sopenharmony_ci}; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_cistruct iso_interrupt_mc_event { 19462306a36Sopenharmony_ci struct event event; 19562306a36Sopenharmony_ci struct fw_cdev_event_iso_interrupt_mc interrupt; 19662306a36Sopenharmony_ci}; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_cistruct iso_resource_event { 19962306a36Sopenharmony_ci struct event event; 20062306a36Sopenharmony_ci struct fw_cdev_event_iso_resource iso_resource; 20162306a36Sopenharmony_ci}; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_cistruct outbound_phy_packet_event { 20462306a36Sopenharmony_ci struct event event; 20562306a36Sopenharmony_ci struct client *client; 20662306a36Sopenharmony_ci struct fw_packet p; 20762306a36Sopenharmony_ci union { 20862306a36Sopenharmony_ci struct fw_cdev_event_phy_packet without_tstamp; 20962306a36Sopenharmony_ci struct fw_cdev_event_phy_packet2 with_tstamp; 21062306a36Sopenharmony_ci } phy_packet; 21162306a36Sopenharmony_ci}; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_cistruct inbound_phy_packet_event { 21462306a36Sopenharmony_ci struct event event; 21562306a36Sopenharmony_ci union { 21662306a36Sopenharmony_ci struct fw_cdev_event_phy_packet without_tstamp; 21762306a36Sopenharmony_ci struct fw_cdev_event_phy_packet2 with_tstamp; 21862306a36Sopenharmony_ci } phy_packet; 21962306a36Sopenharmony_ci}; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci#ifdef CONFIG_COMPAT 22262306a36Sopenharmony_cistatic void __user *u64_to_uptr(u64 value) 22362306a36Sopenharmony_ci{ 22462306a36Sopenharmony_ci if (in_compat_syscall()) 22562306a36Sopenharmony_ci return compat_ptr(value); 22662306a36Sopenharmony_ci else 22762306a36Sopenharmony_ci return (void __user *)(unsigned long)value; 22862306a36Sopenharmony_ci} 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_cistatic u64 uptr_to_u64(void __user *ptr) 23162306a36Sopenharmony_ci{ 23262306a36Sopenharmony_ci if (in_compat_syscall()) 23362306a36Sopenharmony_ci return ptr_to_compat(ptr); 23462306a36Sopenharmony_ci else 23562306a36Sopenharmony_ci return (u64)(unsigned long)ptr; 23662306a36Sopenharmony_ci} 23762306a36Sopenharmony_ci#else 23862306a36Sopenharmony_cistatic inline void __user *u64_to_uptr(u64 value) 23962306a36Sopenharmony_ci{ 24062306a36Sopenharmony_ci return (void __user *)(unsigned long)value; 24162306a36Sopenharmony_ci} 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_cistatic inline u64 uptr_to_u64(void __user *ptr) 24462306a36Sopenharmony_ci{ 24562306a36Sopenharmony_ci return (u64)(unsigned long)ptr; 24662306a36Sopenharmony_ci} 24762306a36Sopenharmony_ci#endif /* CONFIG_COMPAT */ 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_cistatic int fw_device_op_open(struct inode *inode, struct file *file) 25062306a36Sopenharmony_ci{ 25162306a36Sopenharmony_ci struct fw_device *device; 25262306a36Sopenharmony_ci struct client *client; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci device = fw_device_get_by_devt(inode->i_rdev); 25562306a36Sopenharmony_ci if (device == NULL) 25662306a36Sopenharmony_ci return -ENODEV; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci if (fw_device_is_shutdown(device)) { 25962306a36Sopenharmony_ci fw_device_put(device); 26062306a36Sopenharmony_ci return -ENODEV; 26162306a36Sopenharmony_ci } 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci client = kzalloc(sizeof(*client), GFP_KERNEL); 26462306a36Sopenharmony_ci if (client == NULL) { 26562306a36Sopenharmony_ci fw_device_put(device); 26662306a36Sopenharmony_ci return -ENOMEM; 26762306a36Sopenharmony_ci } 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci client->device = device; 27062306a36Sopenharmony_ci spin_lock_init(&client->lock); 27162306a36Sopenharmony_ci idr_init(&client->resource_idr); 27262306a36Sopenharmony_ci INIT_LIST_HEAD(&client->event_list); 27362306a36Sopenharmony_ci init_waitqueue_head(&client->wait); 27462306a36Sopenharmony_ci init_waitqueue_head(&client->tx_flush_wait); 27562306a36Sopenharmony_ci INIT_LIST_HEAD(&client->phy_receiver_link); 27662306a36Sopenharmony_ci INIT_LIST_HEAD(&client->link); 27762306a36Sopenharmony_ci kref_init(&client->kref); 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci file->private_data = client; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci return nonseekable_open(inode, file); 28262306a36Sopenharmony_ci} 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_cistatic void queue_event(struct client *client, struct event *event, 28562306a36Sopenharmony_ci void *data0, size_t size0, void *data1, size_t size1) 28662306a36Sopenharmony_ci{ 28762306a36Sopenharmony_ci unsigned long flags; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci event->v[0].data = data0; 29062306a36Sopenharmony_ci event->v[0].size = size0; 29162306a36Sopenharmony_ci event->v[1].data = data1; 29262306a36Sopenharmony_ci event->v[1].size = size1; 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci spin_lock_irqsave(&client->lock, flags); 29562306a36Sopenharmony_ci if (client->in_shutdown) 29662306a36Sopenharmony_ci kfree(event); 29762306a36Sopenharmony_ci else 29862306a36Sopenharmony_ci list_add_tail(&event->link, &client->event_list); 29962306a36Sopenharmony_ci spin_unlock_irqrestore(&client->lock, flags); 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci wake_up_interruptible(&client->wait); 30262306a36Sopenharmony_ci} 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_cistatic int dequeue_event(struct client *client, 30562306a36Sopenharmony_ci char __user *buffer, size_t count) 30662306a36Sopenharmony_ci{ 30762306a36Sopenharmony_ci struct event *event; 30862306a36Sopenharmony_ci size_t size, total; 30962306a36Sopenharmony_ci int i, ret; 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci ret = wait_event_interruptible(client->wait, 31262306a36Sopenharmony_ci !list_empty(&client->event_list) || 31362306a36Sopenharmony_ci fw_device_is_shutdown(client->device)); 31462306a36Sopenharmony_ci if (ret < 0) 31562306a36Sopenharmony_ci return ret; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci if (list_empty(&client->event_list) && 31862306a36Sopenharmony_ci fw_device_is_shutdown(client->device)) 31962306a36Sopenharmony_ci return -ENODEV; 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci spin_lock_irq(&client->lock); 32262306a36Sopenharmony_ci event = list_first_entry(&client->event_list, struct event, link); 32362306a36Sopenharmony_ci list_del(&event->link); 32462306a36Sopenharmony_ci spin_unlock_irq(&client->lock); 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci total = 0; 32762306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(event->v) && total < count; i++) { 32862306a36Sopenharmony_ci size = min(event->v[i].size, count - total); 32962306a36Sopenharmony_ci if (copy_to_user(buffer + total, event->v[i].data, size)) { 33062306a36Sopenharmony_ci ret = -EFAULT; 33162306a36Sopenharmony_ci goto out; 33262306a36Sopenharmony_ci } 33362306a36Sopenharmony_ci total += size; 33462306a36Sopenharmony_ci } 33562306a36Sopenharmony_ci ret = total; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci out: 33862306a36Sopenharmony_ci kfree(event); 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci return ret; 34162306a36Sopenharmony_ci} 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_cistatic ssize_t fw_device_op_read(struct file *file, char __user *buffer, 34462306a36Sopenharmony_ci size_t count, loff_t *offset) 34562306a36Sopenharmony_ci{ 34662306a36Sopenharmony_ci struct client *client = file->private_data; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci return dequeue_event(client, buffer, count); 34962306a36Sopenharmony_ci} 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_cistatic void fill_bus_reset_event(struct fw_cdev_event_bus_reset *event, 35262306a36Sopenharmony_ci struct client *client) 35362306a36Sopenharmony_ci{ 35462306a36Sopenharmony_ci struct fw_card *card = client->device->card; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci spin_lock_irq(&card->lock); 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci event->closure = client->bus_reset_closure; 35962306a36Sopenharmony_ci event->type = FW_CDEV_EVENT_BUS_RESET; 36062306a36Sopenharmony_ci event->generation = client->device->generation; 36162306a36Sopenharmony_ci event->node_id = client->device->node_id; 36262306a36Sopenharmony_ci event->local_node_id = card->local_node->node_id; 36362306a36Sopenharmony_ci event->bm_node_id = card->bm_node_id; 36462306a36Sopenharmony_ci event->irm_node_id = card->irm_node->node_id; 36562306a36Sopenharmony_ci event->root_node_id = card->root_node->node_id; 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci spin_unlock_irq(&card->lock); 36862306a36Sopenharmony_ci} 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_cistatic void for_each_client(struct fw_device *device, 37162306a36Sopenharmony_ci void (*callback)(struct client *client)) 37262306a36Sopenharmony_ci{ 37362306a36Sopenharmony_ci struct client *c; 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci mutex_lock(&device->client_list_mutex); 37662306a36Sopenharmony_ci list_for_each_entry(c, &device->client_list, link) 37762306a36Sopenharmony_ci callback(c); 37862306a36Sopenharmony_ci mutex_unlock(&device->client_list_mutex); 37962306a36Sopenharmony_ci} 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_cistatic int schedule_reallocations(int id, void *p, void *data) 38262306a36Sopenharmony_ci{ 38362306a36Sopenharmony_ci schedule_if_iso_resource(p); 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci return 0; 38662306a36Sopenharmony_ci} 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_cistatic void queue_bus_reset_event(struct client *client) 38962306a36Sopenharmony_ci{ 39062306a36Sopenharmony_ci struct bus_reset_event *e; 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci e = kzalloc(sizeof(*e), GFP_KERNEL); 39362306a36Sopenharmony_ci if (e == NULL) 39462306a36Sopenharmony_ci return; 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci fill_bus_reset_event(&e->reset, client); 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci queue_event(client, &e->event, 39962306a36Sopenharmony_ci &e->reset, sizeof(e->reset), NULL, 0); 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci spin_lock_irq(&client->lock); 40262306a36Sopenharmony_ci idr_for_each(&client->resource_idr, schedule_reallocations, client); 40362306a36Sopenharmony_ci spin_unlock_irq(&client->lock); 40462306a36Sopenharmony_ci} 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_civoid fw_device_cdev_update(struct fw_device *device) 40762306a36Sopenharmony_ci{ 40862306a36Sopenharmony_ci for_each_client(device, queue_bus_reset_event); 40962306a36Sopenharmony_ci} 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_cistatic void wake_up_client(struct client *client) 41262306a36Sopenharmony_ci{ 41362306a36Sopenharmony_ci wake_up_interruptible(&client->wait); 41462306a36Sopenharmony_ci} 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_civoid fw_device_cdev_remove(struct fw_device *device) 41762306a36Sopenharmony_ci{ 41862306a36Sopenharmony_ci for_each_client(device, wake_up_client); 41962306a36Sopenharmony_ci} 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ciunion ioctl_arg { 42262306a36Sopenharmony_ci struct fw_cdev_get_info get_info; 42362306a36Sopenharmony_ci struct fw_cdev_send_request send_request; 42462306a36Sopenharmony_ci struct fw_cdev_allocate allocate; 42562306a36Sopenharmony_ci struct fw_cdev_deallocate deallocate; 42662306a36Sopenharmony_ci struct fw_cdev_send_response send_response; 42762306a36Sopenharmony_ci struct fw_cdev_initiate_bus_reset initiate_bus_reset; 42862306a36Sopenharmony_ci struct fw_cdev_add_descriptor add_descriptor; 42962306a36Sopenharmony_ci struct fw_cdev_remove_descriptor remove_descriptor; 43062306a36Sopenharmony_ci struct fw_cdev_create_iso_context create_iso_context; 43162306a36Sopenharmony_ci struct fw_cdev_queue_iso queue_iso; 43262306a36Sopenharmony_ci struct fw_cdev_start_iso start_iso; 43362306a36Sopenharmony_ci struct fw_cdev_stop_iso stop_iso; 43462306a36Sopenharmony_ci struct fw_cdev_get_cycle_timer get_cycle_timer; 43562306a36Sopenharmony_ci struct fw_cdev_allocate_iso_resource allocate_iso_resource; 43662306a36Sopenharmony_ci struct fw_cdev_send_stream_packet send_stream_packet; 43762306a36Sopenharmony_ci struct fw_cdev_get_cycle_timer2 get_cycle_timer2; 43862306a36Sopenharmony_ci struct fw_cdev_send_phy_packet send_phy_packet; 43962306a36Sopenharmony_ci struct fw_cdev_receive_phy_packets receive_phy_packets; 44062306a36Sopenharmony_ci struct fw_cdev_set_iso_channels set_iso_channels; 44162306a36Sopenharmony_ci struct fw_cdev_flush_iso flush_iso; 44262306a36Sopenharmony_ci}; 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_cistatic int ioctl_get_info(struct client *client, union ioctl_arg *arg) 44562306a36Sopenharmony_ci{ 44662306a36Sopenharmony_ci struct fw_cdev_get_info *a = &arg->get_info; 44762306a36Sopenharmony_ci struct fw_cdev_event_bus_reset bus_reset; 44862306a36Sopenharmony_ci unsigned long ret = 0; 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci client->version = a->version; 45162306a36Sopenharmony_ci a->version = FW_CDEV_KERNEL_VERSION; 45262306a36Sopenharmony_ci a->card = client->device->card->index; 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci down_read(&fw_device_rwsem); 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci if (a->rom != 0) { 45762306a36Sopenharmony_ci size_t want = a->rom_length; 45862306a36Sopenharmony_ci size_t have = client->device->config_rom_length * 4; 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci ret = copy_to_user(u64_to_uptr(a->rom), 46162306a36Sopenharmony_ci client->device->config_rom, min(want, have)); 46262306a36Sopenharmony_ci } 46362306a36Sopenharmony_ci a->rom_length = client->device->config_rom_length * 4; 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci up_read(&fw_device_rwsem); 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci if (ret != 0) 46862306a36Sopenharmony_ci return -EFAULT; 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci mutex_lock(&client->device->client_list_mutex); 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci client->bus_reset_closure = a->bus_reset_closure; 47362306a36Sopenharmony_ci if (a->bus_reset != 0) { 47462306a36Sopenharmony_ci fill_bus_reset_event(&bus_reset, client); 47562306a36Sopenharmony_ci /* unaligned size of bus_reset is 36 bytes */ 47662306a36Sopenharmony_ci ret = copy_to_user(u64_to_uptr(a->bus_reset), &bus_reset, 36); 47762306a36Sopenharmony_ci } 47862306a36Sopenharmony_ci if (ret == 0 && list_empty(&client->link)) 47962306a36Sopenharmony_ci list_add_tail(&client->link, &client->device->client_list); 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci mutex_unlock(&client->device->client_list_mutex); 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci return ret ? -EFAULT : 0; 48462306a36Sopenharmony_ci} 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_cistatic int add_client_resource(struct client *client, 48762306a36Sopenharmony_ci struct client_resource *resource, gfp_t gfp_mask) 48862306a36Sopenharmony_ci{ 48962306a36Sopenharmony_ci bool preload = gfpflags_allow_blocking(gfp_mask); 49062306a36Sopenharmony_ci unsigned long flags; 49162306a36Sopenharmony_ci int ret; 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci if (preload) 49462306a36Sopenharmony_ci idr_preload(gfp_mask); 49562306a36Sopenharmony_ci spin_lock_irqsave(&client->lock, flags); 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci if (client->in_shutdown) 49862306a36Sopenharmony_ci ret = -ECANCELED; 49962306a36Sopenharmony_ci else 50062306a36Sopenharmony_ci ret = idr_alloc(&client->resource_idr, resource, 0, 0, 50162306a36Sopenharmony_ci GFP_NOWAIT); 50262306a36Sopenharmony_ci if (ret >= 0) { 50362306a36Sopenharmony_ci resource->handle = ret; 50462306a36Sopenharmony_ci client_get(client); 50562306a36Sopenharmony_ci schedule_if_iso_resource(resource); 50662306a36Sopenharmony_ci } 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci spin_unlock_irqrestore(&client->lock, flags); 50962306a36Sopenharmony_ci if (preload) 51062306a36Sopenharmony_ci idr_preload_end(); 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci return ret < 0 ? ret : 0; 51362306a36Sopenharmony_ci} 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_cistatic int release_client_resource(struct client *client, u32 handle, 51662306a36Sopenharmony_ci client_resource_release_fn_t release, 51762306a36Sopenharmony_ci struct client_resource **return_resource) 51862306a36Sopenharmony_ci{ 51962306a36Sopenharmony_ci struct client_resource *resource; 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci spin_lock_irq(&client->lock); 52262306a36Sopenharmony_ci if (client->in_shutdown) 52362306a36Sopenharmony_ci resource = NULL; 52462306a36Sopenharmony_ci else 52562306a36Sopenharmony_ci resource = idr_find(&client->resource_idr, handle); 52662306a36Sopenharmony_ci if (resource && resource->release == release) 52762306a36Sopenharmony_ci idr_remove(&client->resource_idr, handle); 52862306a36Sopenharmony_ci spin_unlock_irq(&client->lock); 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci if (!(resource && resource->release == release)) 53162306a36Sopenharmony_ci return -EINVAL; 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci if (return_resource) 53462306a36Sopenharmony_ci *return_resource = resource; 53562306a36Sopenharmony_ci else 53662306a36Sopenharmony_ci resource->release(client, resource); 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci client_put(client); 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci return 0; 54162306a36Sopenharmony_ci} 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_cistatic void release_transaction(struct client *client, 54462306a36Sopenharmony_ci struct client_resource *resource) 54562306a36Sopenharmony_ci{ 54662306a36Sopenharmony_ci} 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_cistatic void complete_transaction(struct fw_card *card, int rcode, u32 request_tstamp, 54962306a36Sopenharmony_ci u32 response_tstamp, void *payload, size_t length, void *data) 55062306a36Sopenharmony_ci{ 55162306a36Sopenharmony_ci struct outbound_transaction_event *e = data; 55262306a36Sopenharmony_ci struct client *client = e->client; 55362306a36Sopenharmony_ci unsigned long flags; 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci spin_lock_irqsave(&client->lock, flags); 55662306a36Sopenharmony_ci idr_remove(&client->resource_idr, e->r.resource.handle); 55762306a36Sopenharmony_ci if (client->in_shutdown) 55862306a36Sopenharmony_ci wake_up(&client->tx_flush_wait); 55962306a36Sopenharmony_ci spin_unlock_irqrestore(&client->lock, flags); 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci switch (e->rsp.without_tstamp.type) { 56262306a36Sopenharmony_ci case FW_CDEV_EVENT_RESPONSE: 56362306a36Sopenharmony_ci { 56462306a36Sopenharmony_ci struct fw_cdev_event_response *rsp = &e->rsp.without_tstamp; 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci if (length < rsp->length) 56762306a36Sopenharmony_ci rsp->length = length; 56862306a36Sopenharmony_ci if (rcode == RCODE_COMPLETE) 56962306a36Sopenharmony_ci memcpy(rsp->data, payload, rsp->length); 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci rsp->rcode = rcode; 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci // In the case that sizeof(*rsp) doesn't align with the position of the 57462306a36Sopenharmony_ci // data, and the read is short, preserve an extra copy of the data 57562306a36Sopenharmony_ci // to stay compatible with a pre-2.6.27 bug. Since the bug is harmless 57662306a36Sopenharmony_ci // for short reads and some apps depended on it, this is both safe 57762306a36Sopenharmony_ci // and prudent for compatibility. 57862306a36Sopenharmony_ci if (rsp->length <= sizeof(*rsp) - offsetof(typeof(*rsp), data)) 57962306a36Sopenharmony_ci queue_event(client, &e->event, rsp, sizeof(*rsp), rsp->data, rsp->length); 58062306a36Sopenharmony_ci else 58162306a36Sopenharmony_ci queue_event(client, &e->event, rsp, sizeof(*rsp) + rsp->length, NULL, 0); 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci break; 58462306a36Sopenharmony_ci } 58562306a36Sopenharmony_ci case FW_CDEV_EVENT_RESPONSE2: 58662306a36Sopenharmony_ci { 58762306a36Sopenharmony_ci struct fw_cdev_event_response2 *rsp = &e->rsp.with_tstamp; 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci if (length < rsp->length) 59062306a36Sopenharmony_ci rsp->length = length; 59162306a36Sopenharmony_ci if (rcode == RCODE_COMPLETE) 59262306a36Sopenharmony_ci memcpy(rsp->data, payload, rsp->length); 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci rsp->rcode = rcode; 59562306a36Sopenharmony_ci rsp->request_tstamp = request_tstamp; 59662306a36Sopenharmony_ci rsp->response_tstamp = response_tstamp; 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci queue_event(client, &e->event, rsp, sizeof(*rsp) + rsp->length, NULL, 0); 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci break; 60162306a36Sopenharmony_ci default: 60262306a36Sopenharmony_ci WARN_ON(1); 60362306a36Sopenharmony_ci break; 60462306a36Sopenharmony_ci } 60562306a36Sopenharmony_ci } 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci /* Drop the idr's reference */ 60862306a36Sopenharmony_ci client_put(client); 60962306a36Sopenharmony_ci} 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_cistatic int init_request(struct client *client, 61262306a36Sopenharmony_ci struct fw_cdev_send_request *request, 61362306a36Sopenharmony_ci int destination_id, int speed) 61462306a36Sopenharmony_ci{ 61562306a36Sopenharmony_ci struct outbound_transaction_event *e; 61662306a36Sopenharmony_ci void *payload; 61762306a36Sopenharmony_ci int ret; 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci if (request->tcode != TCODE_STREAM_DATA && 62062306a36Sopenharmony_ci (request->length > 4096 || request->length > 512 << speed)) 62162306a36Sopenharmony_ci return -EIO; 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci if (request->tcode == TCODE_WRITE_QUADLET_REQUEST && 62462306a36Sopenharmony_ci request->length < 4) 62562306a36Sopenharmony_ci return -EINVAL; 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci e = kmalloc(sizeof(*e) + request->length, GFP_KERNEL); 62862306a36Sopenharmony_ci if (e == NULL) 62962306a36Sopenharmony_ci return -ENOMEM; 63062306a36Sopenharmony_ci e->client = client; 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci if (client->version < FW_CDEV_VERSION_EVENT_ASYNC_TSTAMP) { 63362306a36Sopenharmony_ci struct fw_cdev_event_response *rsp = &e->rsp.without_tstamp; 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci rsp->type = FW_CDEV_EVENT_RESPONSE; 63662306a36Sopenharmony_ci rsp->length = request->length; 63762306a36Sopenharmony_ci rsp->closure = request->closure; 63862306a36Sopenharmony_ci payload = rsp->data; 63962306a36Sopenharmony_ci } else { 64062306a36Sopenharmony_ci struct fw_cdev_event_response2 *rsp = &e->rsp.with_tstamp; 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci rsp->type = FW_CDEV_EVENT_RESPONSE2; 64362306a36Sopenharmony_ci rsp->length = request->length; 64462306a36Sopenharmony_ci rsp->closure = request->closure; 64562306a36Sopenharmony_ci payload = rsp->data; 64662306a36Sopenharmony_ci } 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci if (request->data && copy_from_user(payload, u64_to_uptr(request->data), request->length)) { 64962306a36Sopenharmony_ci ret = -EFAULT; 65062306a36Sopenharmony_ci goto failed; 65162306a36Sopenharmony_ci } 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci e->r.resource.release = release_transaction; 65462306a36Sopenharmony_ci ret = add_client_resource(client, &e->r.resource, GFP_KERNEL); 65562306a36Sopenharmony_ci if (ret < 0) 65662306a36Sopenharmony_ci goto failed; 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci fw_send_request_with_tstamp(client->device->card, &e->r.transaction, request->tcode, 65962306a36Sopenharmony_ci destination_id, request->generation, speed, request->offset, 66062306a36Sopenharmony_ci payload, request->length, complete_transaction, e); 66162306a36Sopenharmony_ci return 0; 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci failed: 66462306a36Sopenharmony_ci kfree(e); 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci return ret; 66762306a36Sopenharmony_ci} 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_cistatic int ioctl_send_request(struct client *client, union ioctl_arg *arg) 67062306a36Sopenharmony_ci{ 67162306a36Sopenharmony_ci switch (arg->send_request.tcode) { 67262306a36Sopenharmony_ci case TCODE_WRITE_QUADLET_REQUEST: 67362306a36Sopenharmony_ci case TCODE_WRITE_BLOCK_REQUEST: 67462306a36Sopenharmony_ci case TCODE_READ_QUADLET_REQUEST: 67562306a36Sopenharmony_ci case TCODE_READ_BLOCK_REQUEST: 67662306a36Sopenharmony_ci case TCODE_LOCK_MASK_SWAP: 67762306a36Sopenharmony_ci case TCODE_LOCK_COMPARE_SWAP: 67862306a36Sopenharmony_ci case TCODE_LOCK_FETCH_ADD: 67962306a36Sopenharmony_ci case TCODE_LOCK_LITTLE_ADD: 68062306a36Sopenharmony_ci case TCODE_LOCK_BOUNDED_ADD: 68162306a36Sopenharmony_ci case TCODE_LOCK_WRAP_ADD: 68262306a36Sopenharmony_ci case TCODE_LOCK_VENDOR_DEPENDENT: 68362306a36Sopenharmony_ci break; 68462306a36Sopenharmony_ci default: 68562306a36Sopenharmony_ci return -EINVAL; 68662306a36Sopenharmony_ci } 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci return init_request(client, &arg->send_request, client->device->node_id, 68962306a36Sopenharmony_ci client->device->max_speed); 69062306a36Sopenharmony_ci} 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_cistatic void release_request(struct client *client, 69362306a36Sopenharmony_ci struct client_resource *resource) 69462306a36Sopenharmony_ci{ 69562306a36Sopenharmony_ci struct inbound_transaction_resource *r = container_of(resource, 69662306a36Sopenharmony_ci struct inbound_transaction_resource, resource); 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci if (r->is_fcp) 69962306a36Sopenharmony_ci fw_request_put(r->request); 70062306a36Sopenharmony_ci else 70162306a36Sopenharmony_ci fw_send_response(r->card, r->request, RCODE_CONFLICT_ERROR); 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci fw_card_put(r->card); 70462306a36Sopenharmony_ci kfree(r); 70562306a36Sopenharmony_ci} 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_cistatic void handle_request(struct fw_card *card, struct fw_request *request, 70862306a36Sopenharmony_ci int tcode, int destination, int source, 70962306a36Sopenharmony_ci int generation, unsigned long long offset, 71062306a36Sopenharmony_ci void *payload, size_t length, void *callback_data) 71162306a36Sopenharmony_ci{ 71262306a36Sopenharmony_ci struct address_handler_resource *handler = callback_data; 71362306a36Sopenharmony_ci bool is_fcp = is_in_fcp_region(offset, length); 71462306a36Sopenharmony_ci struct inbound_transaction_resource *r; 71562306a36Sopenharmony_ci struct inbound_transaction_event *e; 71662306a36Sopenharmony_ci size_t event_size0; 71762306a36Sopenharmony_ci int ret; 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci /* card may be different from handler->client->device->card */ 72062306a36Sopenharmony_ci fw_card_get(card); 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci // Extend the lifetime of data for request so that its payload is safely accessible in 72362306a36Sopenharmony_ci // the process context for the client. 72462306a36Sopenharmony_ci if (is_fcp) 72562306a36Sopenharmony_ci fw_request_get(request); 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci r = kmalloc(sizeof(*r), GFP_ATOMIC); 72862306a36Sopenharmony_ci e = kmalloc(sizeof(*e), GFP_ATOMIC); 72962306a36Sopenharmony_ci if (r == NULL || e == NULL) 73062306a36Sopenharmony_ci goto failed; 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci r->card = card; 73362306a36Sopenharmony_ci r->request = request; 73462306a36Sopenharmony_ci r->is_fcp = is_fcp; 73562306a36Sopenharmony_ci r->data = payload; 73662306a36Sopenharmony_ci r->length = length; 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci r->resource.release = release_request; 73962306a36Sopenharmony_ci ret = add_client_resource(handler->client, &r->resource, GFP_ATOMIC); 74062306a36Sopenharmony_ci if (ret < 0) 74162306a36Sopenharmony_ci goto failed; 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci if (handler->client->version < FW_CDEV_VERSION_EVENT_REQUEST2) { 74462306a36Sopenharmony_ci struct fw_cdev_event_request *req = &e->req.request; 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci if (tcode & 0x10) 74762306a36Sopenharmony_ci tcode = TCODE_LOCK_REQUEST; 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci req->type = FW_CDEV_EVENT_REQUEST; 75062306a36Sopenharmony_ci req->tcode = tcode; 75162306a36Sopenharmony_ci req->offset = offset; 75262306a36Sopenharmony_ci req->length = length; 75362306a36Sopenharmony_ci req->handle = r->resource.handle; 75462306a36Sopenharmony_ci req->closure = handler->closure; 75562306a36Sopenharmony_ci event_size0 = sizeof(*req); 75662306a36Sopenharmony_ci } else if (handler->client->version < FW_CDEV_VERSION_EVENT_ASYNC_TSTAMP) { 75762306a36Sopenharmony_ci struct fw_cdev_event_request2 *req = &e->req.request2; 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci req->type = FW_CDEV_EVENT_REQUEST2; 76062306a36Sopenharmony_ci req->tcode = tcode; 76162306a36Sopenharmony_ci req->offset = offset; 76262306a36Sopenharmony_ci req->source_node_id = source; 76362306a36Sopenharmony_ci req->destination_node_id = destination; 76462306a36Sopenharmony_ci req->card = card->index; 76562306a36Sopenharmony_ci req->generation = generation; 76662306a36Sopenharmony_ci req->length = length; 76762306a36Sopenharmony_ci req->handle = r->resource.handle; 76862306a36Sopenharmony_ci req->closure = handler->closure; 76962306a36Sopenharmony_ci event_size0 = sizeof(*req); 77062306a36Sopenharmony_ci } else { 77162306a36Sopenharmony_ci struct fw_cdev_event_request3 *req = &e->req.with_tstamp; 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_ci req->type = FW_CDEV_EVENT_REQUEST3; 77462306a36Sopenharmony_ci req->tcode = tcode; 77562306a36Sopenharmony_ci req->offset = offset; 77662306a36Sopenharmony_ci req->source_node_id = source; 77762306a36Sopenharmony_ci req->destination_node_id = destination; 77862306a36Sopenharmony_ci req->card = card->index; 77962306a36Sopenharmony_ci req->generation = generation; 78062306a36Sopenharmony_ci req->length = length; 78162306a36Sopenharmony_ci req->handle = r->resource.handle; 78262306a36Sopenharmony_ci req->closure = handler->closure; 78362306a36Sopenharmony_ci req->tstamp = fw_request_get_timestamp(request); 78462306a36Sopenharmony_ci event_size0 = sizeof(*req); 78562306a36Sopenharmony_ci } 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_ci queue_event(handler->client, &e->event, 78862306a36Sopenharmony_ci &e->req, event_size0, r->data, length); 78962306a36Sopenharmony_ci return; 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci failed: 79262306a36Sopenharmony_ci kfree(r); 79362306a36Sopenharmony_ci kfree(e); 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci if (!is_fcp) 79662306a36Sopenharmony_ci fw_send_response(card, request, RCODE_CONFLICT_ERROR); 79762306a36Sopenharmony_ci else 79862306a36Sopenharmony_ci fw_request_put(request); 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_ci fw_card_put(card); 80162306a36Sopenharmony_ci} 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_cistatic void release_address_handler(struct client *client, 80462306a36Sopenharmony_ci struct client_resource *resource) 80562306a36Sopenharmony_ci{ 80662306a36Sopenharmony_ci struct address_handler_resource *r = 80762306a36Sopenharmony_ci container_of(resource, struct address_handler_resource, resource); 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_ci fw_core_remove_address_handler(&r->handler); 81062306a36Sopenharmony_ci kfree(r); 81162306a36Sopenharmony_ci} 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_cistatic int ioctl_allocate(struct client *client, union ioctl_arg *arg) 81462306a36Sopenharmony_ci{ 81562306a36Sopenharmony_ci struct fw_cdev_allocate *a = &arg->allocate; 81662306a36Sopenharmony_ci struct address_handler_resource *r; 81762306a36Sopenharmony_ci struct fw_address_region region; 81862306a36Sopenharmony_ci int ret; 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_ci r = kmalloc(sizeof(*r), GFP_KERNEL); 82162306a36Sopenharmony_ci if (r == NULL) 82262306a36Sopenharmony_ci return -ENOMEM; 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_ci region.start = a->offset; 82562306a36Sopenharmony_ci if (client->version < FW_CDEV_VERSION_ALLOCATE_REGION_END) 82662306a36Sopenharmony_ci region.end = a->offset + a->length; 82762306a36Sopenharmony_ci else 82862306a36Sopenharmony_ci region.end = a->region_end; 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_ci r->handler.length = a->length; 83162306a36Sopenharmony_ci r->handler.address_callback = handle_request; 83262306a36Sopenharmony_ci r->handler.callback_data = r; 83362306a36Sopenharmony_ci r->closure = a->closure; 83462306a36Sopenharmony_ci r->client = client; 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci ret = fw_core_add_address_handler(&r->handler, ®ion); 83762306a36Sopenharmony_ci if (ret < 0) { 83862306a36Sopenharmony_ci kfree(r); 83962306a36Sopenharmony_ci return ret; 84062306a36Sopenharmony_ci } 84162306a36Sopenharmony_ci a->offset = r->handler.offset; 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_ci r->resource.release = release_address_handler; 84462306a36Sopenharmony_ci ret = add_client_resource(client, &r->resource, GFP_KERNEL); 84562306a36Sopenharmony_ci if (ret < 0) { 84662306a36Sopenharmony_ci release_address_handler(client, &r->resource); 84762306a36Sopenharmony_ci return ret; 84862306a36Sopenharmony_ci } 84962306a36Sopenharmony_ci a->handle = r->resource.handle; 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ci return 0; 85262306a36Sopenharmony_ci} 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_cistatic int ioctl_deallocate(struct client *client, union ioctl_arg *arg) 85562306a36Sopenharmony_ci{ 85662306a36Sopenharmony_ci return release_client_resource(client, arg->deallocate.handle, 85762306a36Sopenharmony_ci release_address_handler, NULL); 85862306a36Sopenharmony_ci} 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_cistatic int ioctl_send_response(struct client *client, union ioctl_arg *arg) 86162306a36Sopenharmony_ci{ 86262306a36Sopenharmony_ci struct fw_cdev_send_response *a = &arg->send_response; 86362306a36Sopenharmony_ci struct client_resource *resource; 86462306a36Sopenharmony_ci struct inbound_transaction_resource *r; 86562306a36Sopenharmony_ci int ret = 0; 86662306a36Sopenharmony_ci 86762306a36Sopenharmony_ci if (release_client_resource(client, a->handle, 86862306a36Sopenharmony_ci release_request, &resource) < 0) 86962306a36Sopenharmony_ci return -EINVAL; 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_ci r = container_of(resource, struct inbound_transaction_resource, 87262306a36Sopenharmony_ci resource); 87362306a36Sopenharmony_ci if (r->is_fcp) { 87462306a36Sopenharmony_ci fw_request_put(r->request); 87562306a36Sopenharmony_ci goto out; 87662306a36Sopenharmony_ci } 87762306a36Sopenharmony_ci 87862306a36Sopenharmony_ci if (a->length != fw_get_response_length(r->request)) { 87962306a36Sopenharmony_ci ret = -EINVAL; 88062306a36Sopenharmony_ci fw_request_put(r->request); 88162306a36Sopenharmony_ci goto out; 88262306a36Sopenharmony_ci } 88362306a36Sopenharmony_ci if (copy_from_user(r->data, u64_to_uptr(a->data), a->length)) { 88462306a36Sopenharmony_ci ret = -EFAULT; 88562306a36Sopenharmony_ci fw_request_put(r->request); 88662306a36Sopenharmony_ci goto out; 88762306a36Sopenharmony_ci } 88862306a36Sopenharmony_ci fw_send_response(r->card, r->request, a->rcode); 88962306a36Sopenharmony_ci out: 89062306a36Sopenharmony_ci fw_card_put(r->card); 89162306a36Sopenharmony_ci kfree(r); 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_ci return ret; 89462306a36Sopenharmony_ci} 89562306a36Sopenharmony_ci 89662306a36Sopenharmony_cistatic int ioctl_initiate_bus_reset(struct client *client, union ioctl_arg *arg) 89762306a36Sopenharmony_ci{ 89862306a36Sopenharmony_ci fw_schedule_bus_reset(client->device->card, true, 89962306a36Sopenharmony_ci arg->initiate_bus_reset.type == FW_CDEV_SHORT_RESET); 90062306a36Sopenharmony_ci return 0; 90162306a36Sopenharmony_ci} 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_cistatic void release_descriptor(struct client *client, 90462306a36Sopenharmony_ci struct client_resource *resource) 90562306a36Sopenharmony_ci{ 90662306a36Sopenharmony_ci struct descriptor_resource *r = 90762306a36Sopenharmony_ci container_of(resource, struct descriptor_resource, resource); 90862306a36Sopenharmony_ci 90962306a36Sopenharmony_ci fw_core_remove_descriptor(&r->descriptor); 91062306a36Sopenharmony_ci kfree(r); 91162306a36Sopenharmony_ci} 91262306a36Sopenharmony_ci 91362306a36Sopenharmony_cistatic int ioctl_add_descriptor(struct client *client, union ioctl_arg *arg) 91462306a36Sopenharmony_ci{ 91562306a36Sopenharmony_ci struct fw_cdev_add_descriptor *a = &arg->add_descriptor; 91662306a36Sopenharmony_ci struct descriptor_resource *r; 91762306a36Sopenharmony_ci int ret; 91862306a36Sopenharmony_ci 91962306a36Sopenharmony_ci /* Access policy: Allow this ioctl only on local nodes' device files. */ 92062306a36Sopenharmony_ci if (!client->device->is_local) 92162306a36Sopenharmony_ci return -ENOSYS; 92262306a36Sopenharmony_ci 92362306a36Sopenharmony_ci if (a->length > 256) 92462306a36Sopenharmony_ci return -EINVAL; 92562306a36Sopenharmony_ci 92662306a36Sopenharmony_ci r = kmalloc(sizeof(*r) + a->length * 4, GFP_KERNEL); 92762306a36Sopenharmony_ci if (r == NULL) 92862306a36Sopenharmony_ci return -ENOMEM; 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_ci if (copy_from_user(r->data, u64_to_uptr(a->data), a->length * 4)) { 93162306a36Sopenharmony_ci ret = -EFAULT; 93262306a36Sopenharmony_ci goto failed; 93362306a36Sopenharmony_ci } 93462306a36Sopenharmony_ci 93562306a36Sopenharmony_ci r->descriptor.length = a->length; 93662306a36Sopenharmony_ci r->descriptor.immediate = a->immediate; 93762306a36Sopenharmony_ci r->descriptor.key = a->key; 93862306a36Sopenharmony_ci r->descriptor.data = r->data; 93962306a36Sopenharmony_ci 94062306a36Sopenharmony_ci ret = fw_core_add_descriptor(&r->descriptor); 94162306a36Sopenharmony_ci if (ret < 0) 94262306a36Sopenharmony_ci goto failed; 94362306a36Sopenharmony_ci 94462306a36Sopenharmony_ci r->resource.release = release_descriptor; 94562306a36Sopenharmony_ci ret = add_client_resource(client, &r->resource, GFP_KERNEL); 94662306a36Sopenharmony_ci if (ret < 0) { 94762306a36Sopenharmony_ci fw_core_remove_descriptor(&r->descriptor); 94862306a36Sopenharmony_ci goto failed; 94962306a36Sopenharmony_ci } 95062306a36Sopenharmony_ci a->handle = r->resource.handle; 95162306a36Sopenharmony_ci 95262306a36Sopenharmony_ci return 0; 95362306a36Sopenharmony_ci failed: 95462306a36Sopenharmony_ci kfree(r); 95562306a36Sopenharmony_ci 95662306a36Sopenharmony_ci return ret; 95762306a36Sopenharmony_ci} 95862306a36Sopenharmony_ci 95962306a36Sopenharmony_cistatic int ioctl_remove_descriptor(struct client *client, union ioctl_arg *arg) 96062306a36Sopenharmony_ci{ 96162306a36Sopenharmony_ci return release_client_resource(client, arg->remove_descriptor.handle, 96262306a36Sopenharmony_ci release_descriptor, NULL); 96362306a36Sopenharmony_ci} 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_cistatic void iso_callback(struct fw_iso_context *context, u32 cycle, 96662306a36Sopenharmony_ci size_t header_length, void *header, void *data) 96762306a36Sopenharmony_ci{ 96862306a36Sopenharmony_ci struct client *client = data; 96962306a36Sopenharmony_ci struct iso_interrupt_event *e; 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_ci e = kmalloc(sizeof(*e) + header_length, GFP_ATOMIC); 97262306a36Sopenharmony_ci if (e == NULL) 97362306a36Sopenharmony_ci return; 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_ci e->interrupt.type = FW_CDEV_EVENT_ISO_INTERRUPT; 97662306a36Sopenharmony_ci e->interrupt.closure = client->iso_closure; 97762306a36Sopenharmony_ci e->interrupt.cycle = cycle; 97862306a36Sopenharmony_ci e->interrupt.header_length = header_length; 97962306a36Sopenharmony_ci memcpy(e->interrupt.header, header, header_length); 98062306a36Sopenharmony_ci queue_event(client, &e->event, &e->interrupt, 98162306a36Sopenharmony_ci sizeof(e->interrupt) + header_length, NULL, 0); 98262306a36Sopenharmony_ci} 98362306a36Sopenharmony_ci 98462306a36Sopenharmony_cistatic void iso_mc_callback(struct fw_iso_context *context, 98562306a36Sopenharmony_ci dma_addr_t completed, void *data) 98662306a36Sopenharmony_ci{ 98762306a36Sopenharmony_ci struct client *client = data; 98862306a36Sopenharmony_ci struct iso_interrupt_mc_event *e; 98962306a36Sopenharmony_ci 99062306a36Sopenharmony_ci e = kmalloc(sizeof(*e), GFP_ATOMIC); 99162306a36Sopenharmony_ci if (e == NULL) 99262306a36Sopenharmony_ci return; 99362306a36Sopenharmony_ci 99462306a36Sopenharmony_ci e->interrupt.type = FW_CDEV_EVENT_ISO_INTERRUPT_MULTICHANNEL; 99562306a36Sopenharmony_ci e->interrupt.closure = client->iso_closure; 99662306a36Sopenharmony_ci e->interrupt.completed = fw_iso_buffer_lookup(&client->buffer, 99762306a36Sopenharmony_ci completed); 99862306a36Sopenharmony_ci queue_event(client, &e->event, &e->interrupt, 99962306a36Sopenharmony_ci sizeof(e->interrupt), NULL, 0); 100062306a36Sopenharmony_ci} 100162306a36Sopenharmony_ci 100262306a36Sopenharmony_cistatic enum dma_data_direction iso_dma_direction(struct fw_iso_context *context) 100362306a36Sopenharmony_ci{ 100462306a36Sopenharmony_ci if (context->type == FW_ISO_CONTEXT_TRANSMIT) 100562306a36Sopenharmony_ci return DMA_TO_DEVICE; 100662306a36Sopenharmony_ci else 100762306a36Sopenharmony_ci return DMA_FROM_DEVICE; 100862306a36Sopenharmony_ci} 100962306a36Sopenharmony_ci 101062306a36Sopenharmony_cistatic struct fw_iso_context *fw_iso_mc_context_create(struct fw_card *card, 101162306a36Sopenharmony_ci fw_iso_mc_callback_t callback, 101262306a36Sopenharmony_ci void *callback_data) 101362306a36Sopenharmony_ci{ 101462306a36Sopenharmony_ci struct fw_iso_context *ctx; 101562306a36Sopenharmony_ci 101662306a36Sopenharmony_ci ctx = fw_iso_context_create(card, FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL, 101762306a36Sopenharmony_ci 0, 0, 0, NULL, callback_data); 101862306a36Sopenharmony_ci if (!IS_ERR(ctx)) 101962306a36Sopenharmony_ci ctx->callback.mc = callback; 102062306a36Sopenharmony_ci 102162306a36Sopenharmony_ci return ctx; 102262306a36Sopenharmony_ci} 102362306a36Sopenharmony_ci 102462306a36Sopenharmony_cistatic int ioctl_create_iso_context(struct client *client, union ioctl_arg *arg) 102562306a36Sopenharmony_ci{ 102662306a36Sopenharmony_ci struct fw_cdev_create_iso_context *a = &arg->create_iso_context; 102762306a36Sopenharmony_ci struct fw_iso_context *context; 102862306a36Sopenharmony_ci union fw_iso_callback cb; 102962306a36Sopenharmony_ci int ret; 103062306a36Sopenharmony_ci 103162306a36Sopenharmony_ci BUILD_BUG_ON(FW_CDEV_ISO_CONTEXT_TRANSMIT != FW_ISO_CONTEXT_TRANSMIT || 103262306a36Sopenharmony_ci FW_CDEV_ISO_CONTEXT_RECEIVE != FW_ISO_CONTEXT_RECEIVE || 103362306a36Sopenharmony_ci FW_CDEV_ISO_CONTEXT_RECEIVE_MULTICHANNEL != 103462306a36Sopenharmony_ci FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL); 103562306a36Sopenharmony_ci 103662306a36Sopenharmony_ci switch (a->type) { 103762306a36Sopenharmony_ci case FW_ISO_CONTEXT_TRANSMIT: 103862306a36Sopenharmony_ci if (a->speed > SCODE_3200 || a->channel > 63) 103962306a36Sopenharmony_ci return -EINVAL; 104062306a36Sopenharmony_ci 104162306a36Sopenharmony_ci cb.sc = iso_callback; 104262306a36Sopenharmony_ci break; 104362306a36Sopenharmony_ci 104462306a36Sopenharmony_ci case FW_ISO_CONTEXT_RECEIVE: 104562306a36Sopenharmony_ci if (a->header_size < 4 || (a->header_size & 3) || 104662306a36Sopenharmony_ci a->channel > 63) 104762306a36Sopenharmony_ci return -EINVAL; 104862306a36Sopenharmony_ci 104962306a36Sopenharmony_ci cb.sc = iso_callback; 105062306a36Sopenharmony_ci break; 105162306a36Sopenharmony_ci 105262306a36Sopenharmony_ci case FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL: 105362306a36Sopenharmony_ci cb.mc = iso_mc_callback; 105462306a36Sopenharmony_ci break; 105562306a36Sopenharmony_ci 105662306a36Sopenharmony_ci default: 105762306a36Sopenharmony_ci return -EINVAL; 105862306a36Sopenharmony_ci } 105962306a36Sopenharmony_ci 106062306a36Sopenharmony_ci if (a->type == FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL) 106162306a36Sopenharmony_ci context = fw_iso_mc_context_create(client->device->card, cb.mc, 106262306a36Sopenharmony_ci client); 106362306a36Sopenharmony_ci else 106462306a36Sopenharmony_ci context = fw_iso_context_create(client->device->card, a->type, 106562306a36Sopenharmony_ci a->channel, a->speed, 106662306a36Sopenharmony_ci a->header_size, cb.sc, client); 106762306a36Sopenharmony_ci if (IS_ERR(context)) 106862306a36Sopenharmony_ci return PTR_ERR(context); 106962306a36Sopenharmony_ci if (client->version < FW_CDEV_VERSION_AUTO_FLUSH_ISO_OVERFLOW) 107062306a36Sopenharmony_ci context->drop_overflow_headers = true; 107162306a36Sopenharmony_ci 107262306a36Sopenharmony_ci /* We only support one context at this time. */ 107362306a36Sopenharmony_ci spin_lock_irq(&client->lock); 107462306a36Sopenharmony_ci if (client->iso_context != NULL) { 107562306a36Sopenharmony_ci spin_unlock_irq(&client->lock); 107662306a36Sopenharmony_ci fw_iso_context_destroy(context); 107762306a36Sopenharmony_ci 107862306a36Sopenharmony_ci return -EBUSY; 107962306a36Sopenharmony_ci } 108062306a36Sopenharmony_ci if (!client->buffer_is_mapped) { 108162306a36Sopenharmony_ci ret = fw_iso_buffer_map_dma(&client->buffer, 108262306a36Sopenharmony_ci client->device->card, 108362306a36Sopenharmony_ci iso_dma_direction(context)); 108462306a36Sopenharmony_ci if (ret < 0) { 108562306a36Sopenharmony_ci spin_unlock_irq(&client->lock); 108662306a36Sopenharmony_ci fw_iso_context_destroy(context); 108762306a36Sopenharmony_ci 108862306a36Sopenharmony_ci return ret; 108962306a36Sopenharmony_ci } 109062306a36Sopenharmony_ci client->buffer_is_mapped = true; 109162306a36Sopenharmony_ci } 109262306a36Sopenharmony_ci client->iso_closure = a->closure; 109362306a36Sopenharmony_ci client->iso_context = context; 109462306a36Sopenharmony_ci spin_unlock_irq(&client->lock); 109562306a36Sopenharmony_ci 109662306a36Sopenharmony_ci a->handle = 0; 109762306a36Sopenharmony_ci 109862306a36Sopenharmony_ci return 0; 109962306a36Sopenharmony_ci} 110062306a36Sopenharmony_ci 110162306a36Sopenharmony_cistatic int ioctl_set_iso_channels(struct client *client, union ioctl_arg *arg) 110262306a36Sopenharmony_ci{ 110362306a36Sopenharmony_ci struct fw_cdev_set_iso_channels *a = &arg->set_iso_channels; 110462306a36Sopenharmony_ci struct fw_iso_context *ctx = client->iso_context; 110562306a36Sopenharmony_ci 110662306a36Sopenharmony_ci if (ctx == NULL || a->handle != 0) 110762306a36Sopenharmony_ci return -EINVAL; 110862306a36Sopenharmony_ci 110962306a36Sopenharmony_ci return fw_iso_context_set_channels(ctx, &a->channels); 111062306a36Sopenharmony_ci} 111162306a36Sopenharmony_ci 111262306a36Sopenharmony_ci/* Macros for decoding the iso packet control header. */ 111362306a36Sopenharmony_ci#define GET_PAYLOAD_LENGTH(v) ((v) & 0xffff) 111462306a36Sopenharmony_ci#define GET_INTERRUPT(v) (((v) >> 16) & 0x01) 111562306a36Sopenharmony_ci#define GET_SKIP(v) (((v) >> 17) & 0x01) 111662306a36Sopenharmony_ci#define GET_TAG(v) (((v) >> 18) & 0x03) 111762306a36Sopenharmony_ci#define GET_SY(v) (((v) >> 20) & 0x0f) 111862306a36Sopenharmony_ci#define GET_HEADER_LENGTH(v) (((v) >> 24) & 0xff) 111962306a36Sopenharmony_ci 112062306a36Sopenharmony_cistatic int ioctl_queue_iso(struct client *client, union ioctl_arg *arg) 112162306a36Sopenharmony_ci{ 112262306a36Sopenharmony_ci struct fw_cdev_queue_iso *a = &arg->queue_iso; 112362306a36Sopenharmony_ci struct fw_cdev_iso_packet __user *p, *end, *next; 112462306a36Sopenharmony_ci struct fw_iso_context *ctx = client->iso_context; 112562306a36Sopenharmony_ci unsigned long payload, buffer_end, transmit_header_bytes = 0; 112662306a36Sopenharmony_ci u32 control; 112762306a36Sopenharmony_ci int count; 112862306a36Sopenharmony_ci struct { 112962306a36Sopenharmony_ci struct fw_iso_packet packet; 113062306a36Sopenharmony_ci u8 header[256]; 113162306a36Sopenharmony_ci } u; 113262306a36Sopenharmony_ci 113362306a36Sopenharmony_ci if (ctx == NULL || a->handle != 0) 113462306a36Sopenharmony_ci return -EINVAL; 113562306a36Sopenharmony_ci 113662306a36Sopenharmony_ci /* 113762306a36Sopenharmony_ci * If the user passes a non-NULL data pointer, has mmap()'ed 113862306a36Sopenharmony_ci * the iso buffer, and the pointer points inside the buffer, 113962306a36Sopenharmony_ci * we setup the payload pointers accordingly. Otherwise we 114062306a36Sopenharmony_ci * set them both to 0, which will still let packets with 114162306a36Sopenharmony_ci * payload_length == 0 through. In other words, if no packets 114262306a36Sopenharmony_ci * use the indirect payload, the iso buffer need not be mapped 114362306a36Sopenharmony_ci * and the a->data pointer is ignored. 114462306a36Sopenharmony_ci */ 114562306a36Sopenharmony_ci payload = (unsigned long)a->data - client->vm_start; 114662306a36Sopenharmony_ci buffer_end = client->buffer.page_count << PAGE_SHIFT; 114762306a36Sopenharmony_ci if (a->data == 0 || client->buffer.pages == NULL || 114862306a36Sopenharmony_ci payload >= buffer_end) { 114962306a36Sopenharmony_ci payload = 0; 115062306a36Sopenharmony_ci buffer_end = 0; 115162306a36Sopenharmony_ci } 115262306a36Sopenharmony_ci 115362306a36Sopenharmony_ci if (ctx->type == FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL && payload & 3) 115462306a36Sopenharmony_ci return -EINVAL; 115562306a36Sopenharmony_ci 115662306a36Sopenharmony_ci p = (struct fw_cdev_iso_packet __user *)u64_to_uptr(a->packets); 115762306a36Sopenharmony_ci 115862306a36Sopenharmony_ci end = (void __user *)p + a->size; 115962306a36Sopenharmony_ci count = 0; 116062306a36Sopenharmony_ci while (p < end) { 116162306a36Sopenharmony_ci if (get_user(control, &p->control)) 116262306a36Sopenharmony_ci return -EFAULT; 116362306a36Sopenharmony_ci u.packet.payload_length = GET_PAYLOAD_LENGTH(control); 116462306a36Sopenharmony_ci u.packet.interrupt = GET_INTERRUPT(control); 116562306a36Sopenharmony_ci u.packet.skip = GET_SKIP(control); 116662306a36Sopenharmony_ci u.packet.tag = GET_TAG(control); 116762306a36Sopenharmony_ci u.packet.sy = GET_SY(control); 116862306a36Sopenharmony_ci u.packet.header_length = GET_HEADER_LENGTH(control); 116962306a36Sopenharmony_ci 117062306a36Sopenharmony_ci switch (ctx->type) { 117162306a36Sopenharmony_ci case FW_ISO_CONTEXT_TRANSMIT: 117262306a36Sopenharmony_ci if (u.packet.header_length & 3) 117362306a36Sopenharmony_ci return -EINVAL; 117462306a36Sopenharmony_ci transmit_header_bytes = u.packet.header_length; 117562306a36Sopenharmony_ci break; 117662306a36Sopenharmony_ci 117762306a36Sopenharmony_ci case FW_ISO_CONTEXT_RECEIVE: 117862306a36Sopenharmony_ci if (u.packet.header_length == 0 || 117962306a36Sopenharmony_ci u.packet.header_length % ctx->header_size != 0) 118062306a36Sopenharmony_ci return -EINVAL; 118162306a36Sopenharmony_ci break; 118262306a36Sopenharmony_ci 118362306a36Sopenharmony_ci case FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL: 118462306a36Sopenharmony_ci if (u.packet.payload_length == 0 || 118562306a36Sopenharmony_ci u.packet.payload_length & 3) 118662306a36Sopenharmony_ci return -EINVAL; 118762306a36Sopenharmony_ci break; 118862306a36Sopenharmony_ci } 118962306a36Sopenharmony_ci 119062306a36Sopenharmony_ci next = (struct fw_cdev_iso_packet __user *) 119162306a36Sopenharmony_ci &p->header[transmit_header_bytes / 4]; 119262306a36Sopenharmony_ci if (next > end) 119362306a36Sopenharmony_ci return -EINVAL; 119462306a36Sopenharmony_ci if (copy_from_user 119562306a36Sopenharmony_ci (u.packet.header, p->header, transmit_header_bytes)) 119662306a36Sopenharmony_ci return -EFAULT; 119762306a36Sopenharmony_ci if (u.packet.skip && ctx->type == FW_ISO_CONTEXT_TRANSMIT && 119862306a36Sopenharmony_ci u.packet.header_length + u.packet.payload_length > 0) 119962306a36Sopenharmony_ci return -EINVAL; 120062306a36Sopenharmony_ci if (payload + u.packet.payload_length > buffer_end) 120162306a36Sopenharmony_ci return -EINVAL; 120262306a36Sopenharmony_ci 120362306a36Sopenharmony_ci if (fw_iso_context_queue(ctx, &u.packet, 120462306a36Sopenharmony_ci &client->buffer, payload)) 120562306a36Sopenharmony_ci break; 120662306a36Sopenharmony_ci 120762306a36Sopenharmony_ci p = next; 120862306a36Sopenharmony_ci payload += u.packet.payload_length; 120962306a36Sopenharmony_ci count++; 121062306a36Sopenharmony_ci } 121162306a36Sopenharmony_ci fw_iso_context_queue_flush(ctx); 121262306a36Sopenharmony_ci 121362306a36Sopenharmony_ci a->size -= uptr_to_u64(p) - a->packets; 121462306a36Sopenharmony_ci a->packets = uptr_to_u64(p); 121562306a36Sopenharmony_ci a->data = client->vm_start + payload; 121662306a36Sopenharmony_ci 121762306a36Sopenharmony_ci return count; 121862306a36Sopenharmony_ci} 121962306a36Sopenharmony_ci 122062306a36Sopenharmony_cistatic int ioctl_start_iso(struct client *client, union ioctl_arg *arg) 122162306a36Sopenharmony_ci{ 122262306a36Sopenharmony_ci struct fw_cdev_start_iso *a = &arg->start_iso; 122362306a36Sopenharmony_ci 122462306a36Sopenharmony_ci BUILD_BUG_ON( 122562306a36Sopenharmony_ci FW_CDEV_ISO_CONTEXT_MATCH_TAG0 != FW_ISO_CONTEXT_MATCH_TAG0 || 122662306a36Sopenharmony_ci FW_CDEV_ISO_CONTEXT_MATCH_TAG1 != FW_ISO_CONTEXT_MATCH_TAG1 || 122762306a36Sopenharmony_ci FW_CDEV_ISO_CONTEXT_MATCH_TAG2 != FW_ISO_CONTEXT_MATCH_TAG2 || 122862306a36Sopenharmony_ci FW_CDEV_ISO_CONTEXT_MATCH_TAG3 != FW_ISO_CONTEXT_MATCH_TAG3 || 122962306a36Sopenharmony_ci FW_CDEV_ISO_CONTEXT_MATCH_ALL_TAGS != FW_ISO_CONTEXT_MATCH_ALL_TAGS); 123062306a36Sopenharmony_ci 123162306a36Sopenharmony_ci if (client->iso_context == NULL || a->handle != 0) 123262306a36Sopenharmony_ci return -EINVAL; 123362306a36Sopenharmony_ci 123462306a36Sopenharmony_ci if (client->iso_context->type == FW_ISO_CONTEXT_RECEIVE && 123562306a36Sopenharmony_ci (a->tags == 0 || a->tags > 15 || a->sync > 15)) 123662306a36Sopenharmony_ci return -EINVAL; 123762306a36Sopenharmony_ci 123862306a36Sopenharmony_ci return fw_iso_context_start(client->iso_context, 123962306a36Sopenharmony_ci a->cycle, a->sync, a->tags); 124062306a36Sopenharmony_ci} 124162306a36Sopenharmony_ci 124262306a36Sopenharmony_cistatic int ioctl_stop_iso(struct client *client, union ioctl_arg *arg) 124362306a36Sopenharmony_ci{ 124462306a36Sopenharmony_ci struct fw_cdev_stop_iso *a = &arg->stop_iso; 124562306a36Sopenharmony_ci 124662306a36Sopenharmony_ci if (client->iso_context == NULL || a->handle != 0) 124762306a36Sopenharmony_ci return -EINVAL; 124862306a36Sopenharmony_ci 124962306a36Sopenharmony_ci return fw_iso_context_stop(client->iso_context); 125062306a36Sopenharmony_ci} 125162306a36Sopenharmony_ci 125262306a36Sopenharmony_cistatic int ioctl_flush_iso(struct client *client, union ioctl_arg *arg) 125362306a36Sopenharmony_ci{ 125462306a36Sopenharmony_ci struct fw_cdev_flush_iso *a = &arg->flush_iso; 125562306a36Sopenharmony_ci 125662306a36Sopenharmony_ci if (client->iso_context == NULL || a->handle != 0) 125762306a36Sopenharmony_ci return -EINVAL; 125862306a36Sopenharmony_ci 125962306a36Sopenharmony_ci return fw_iso_context_flush_completions(client->iso_context); 126062306a36Sopenharmony_ci} 126162306a36Sopenharmony_ci 126262306a36Sopenharmony_cistatic int ioctl_get_cycle_timer2(struct client *client, union ioctl_arg *arg) 126362306a36Sopenharmony_ci{ 126462306a36Sopenharmony_ci struct fw_cdev_get_cycle_timer2 *a = &arg->get_cycle_timer2; 126562306a36Sopenharmony_ci struct fw_card *card = client->device->card; 126662306a36Sopenharmony_ci struct timespec64 ts = {0, 0}; 126762306a36Sopenharmony_ci u32 cycle_time = 0; 126862306a36Sopenharmony_ci int ret = 0; 126962306a36Sopenharmony_ci 127062306a36Sopenharmony_ci local_irq_disable(); 127162306a36Sopenharmony_ci 127262306a36Sopenharmony_ci ret = fw_card_read_cycle_time(card, &cycle_time); 127362306a36Sopenharmony_ci if (ret < 0) 127462306a36Sopenharmony_ci goto end; 127562306a36Sopenharmony_ci 127662306a36Sopenharmony_ci switch (a->clk_id) { 127762306a36Sopenharmony_ci case CLOCK_REALTIME: ktime_get_real_ts64(&ts); break; 127862306a36Sopenharmony_ci case CLOCK_MONOTONIC: ktime_get_ts64(&ts); break; 127962306a36Sopenharmony_ci case CLOCK_MONOTONIC_RAW: ktime_get_raw_ts64(&ts); break; 128062306a36Sopenharmony_ci default: 128162306a36Sopenharmony_ci ret = -EINVAL; 128262306a36Sopenharmony_ci } 128362306a36Sopenharmony_ciend: 128462306a36Sopenharmony_ci local_irq_enable(); 128562306a36Sopenharmony_ci 128662306a36Sopenharmony_ci a->tv_sec = ts.tv_sec; 128762306a36Sopenharmony_ci a->tv_nsec = ts.tv_nsec; 128862306a36Sopenharmony_ci a->cycle_timer = cycle_time; 128962306a36Sopenharmony_ci 129062306a36Sopenharmony_ci return ret; 129162306a36Sopenharmony_ci} 129262306a36Sopenharmony_ci 129362306a36Sopenharmony_cistatic int ioctl_get_cycle_timer(struct client *client, union ioctl_arg *arg) 129462306a36Sopenharmony_ci{ 129562306a36Sopenharmony_ci struct fw_cdev_get_cycle_timer *a = &arg->get_cycle_timer; 129662306a36Sopenharmony_ci struct fw_cdev_get_cycle_timer2 ct2; 129762306a36Sopenharmony_ci 129862306a36Sopenharmony_ci ct2.clk_id = CLOCK_REALTIME; 129962306a36Sopenharmony_ci ioctl_get_cycle_timer2(client, (union ioctl_arg *)&ct2); 130062306a36Sopenharmony_ci 130162306a36Sopenharmony_ci a->local_time = ct2.tv_sec * USEC_PER_SEC + ct2.tv_nsec / NSEC_PER_USEC; 130262306a36Sopenharmony_ci a->cycle_timer = ct2.cycle_timer; 130362306a36Sopenharmony_ci 130462306a36Sopenharmony_ci return 0; 130562306a36Sopenharmony_ci} 130662306a36Sopenharmony_ci 130762306a36Sopenharmony_cistatic void iso_resource_work(struct work_struct *work) 130862306a36Sopenharmony_ci{ 130962306a36Sopenharmony_ci struct iso_resource_event *e; 131062306a36Sopenharmony_ci struct iso_resource *r = 131162306a36Sopenharmony_ci container_of(work, struct iso_resource, work.work); 131262306a36Sopenharmony_ci struct client *client = r->client; 131362306a36Sopenharmony_ci int generation, channel, bandwidth, todo; 131462306a36Sopenharmony_ci bool skip, free, success; 131562306a36Sopenharmony_ci 131662306a36Sopenharmony_ci spin_lock_irq(&client->lock); 131762306a36Sopenharmony_ci generation = client->device->generation; 131862306a36Sopenharmony_ci todo = r->todo; 131962306a36Sopenharmony_ci /* Allow 1000ms grace period for other reallocations. */ 132062306a36Sopenharmony_ci if (todo == ISO_RES_ALLOC && 132162306a36Sopenharmony_ci time_before64(get_jiffies_64(), 132262306a36Sopenharmony_ci client->device->card->reset_jiffies + HZ)) { 132362306a36Sopenharmony_ci schedule_iso_resource(r, DIV_ROUND_UP(HZ, 3)); 132462306a36Sopenharmony_ci skip = true; 132562306a36Sopenharmony_ci } else { 132662306a36Sopenharmony_ci /* We could be called twice within the same generation. */ 132762306a36Sopenharmony_ci skip = todo == ISO_RES_REALLOC && 132862306a36Sopenharmony_ci r->generation == generation; 132962306a36Sopenharmony_ci } 133062306a36Sopenharmony_ci free = todo == ISO_RES_DEALLOC || 133162306a36Sopenharmony_ci todo == ISO_RES_ALLOC_ONCE || 133262306a36Sopenharmony_ci todo == ISO_RES_DEALLOC_ONCE; 133362306a36Sopenharmony_ci r->generation = generation; 133462306a36Sopenharmony_ci spin_unlock_irq(&client->lock); 133562306a36Sopenharmony_ci 133662306a36Sopenharmony_ci if (skip) 133762306a36Sopenharmony_ci goto out; 133862306a36Sopenharmony_ci 133962306a36Sopenharmony_ci bandwidth = r->bandwidth; 134062306a36Sopenharmony_ci 134162306a36Sopenharmony_ci fw_iso_resource_manage(client->device->card, generation, 134262306a36Sopenharmony_ci r->channels, &channel, &bandwidth, 134362306a36Sopenharmony_ci todo == ISO_RES_ALLOC || 134462306a36Sopenharmony_ci todo == ISO_RES_REALLOC || 134562306a36Sopenharmony_ci todo == ISO_RES_ALLOC_ONCE); 134662306a36Sopenharmony_ci /* 134762306a36Sopenharmony_ci * Is this generation outdated already? As long as this resource sticks 134862306a36Sopenharmony_ci * in the idr, it will be scheduled again for a newer generation or at 134962306a36Sopenharmony_ci * shutdown. 135062306a36Sopenharmony_ci */ 135162306a36Sopenharmony_ci if (channel == -EAGAIN && 135262306a36Sopenharmony_ci (todo == ISO_RES_ALLOC || todo == ISO_RES_REALLOC)) 135362306a36Sopenharmony_ci goto out; 135462306a36Sopenharmony_ci 135562306a36Sopenharmony_ci success = channel >= 0 || bandwidth > 0; 135662306a36Sopenharmony_ci 135762306a36Sopenharmony_ci spin_lock_irq(&client->lock); 135862306a36Sopenharmony_ci /* 135962306a36Sopenharmony_ci * Transit from allocation to reallocation, except if the client 136062306a36Sopenharmony_ci * requested deallocation in the meantime. 136162306a36Sopenharmony_ci */ 136262306a36Sopenharmony_ci if (r->todo == ISO_RES_ALLOC) 136362306a36Sopenharmony_ci r->todo = ISO_RES_REALLOC; 136462306a36Sopenharmony_ci /* 136562306a36Sopenharmony_ci * Allocation or reallocation failure? Pull this resource out of the 136662306a36Sopenharmony_ci * idr and prepare for deletion, unless the client is shutting down. 136762306a36Sopenharmony_ci */ 136862306a36Sopenharmony_ci if (r->todo == ISO_RES_REALLOC && !success && 136962306a36Sopenharmony_ci !client->in_shutdown && 137062306a36Sopenharmony_ci idr_remove(&client->resource_idr, r->resource.handle)) { 137162306a36Sopenharmony_ci client_put(client); 137262306a36Sopenharmony_ci free = true; 137362306a36Sopenharmony_ci } 137462306a36Sopenharmony_ci spin_unlock_irq(&client->lock); 137562306a36Sopenharmony_ci 137662306a36Sopenharmony_ci if (todo == ISO_RES_ALLOC && channel >= 0) 137762306a36Sopenharmony_ci r->channels = 1ULL << channel; 137862306a36Sopenharmony_ci 137962306a36Sopenharmony_ci if (todo == ISO_RES_REALLOC && success) 138062306a36Sopenharmony_ci goto out; 138162306a36Sopenharmony_ci 138262306a36Sopenharmony_ci if (todo == ISO_RES_ALLOC || todo == ISO_RES_ALLOC_ONCE) { 138362306a36Sopenharmony_ci e = r->e_alloc; 138462306a36Sopenharmony_ci r->e_alloc = NULL; 138562306a36Sopenharmony_ci } else { 138662306a36Sopenharmony_ci e = r->e_dealloc; 138762306a36Sopenharmony_ci r->e_dealloc = NULL; 138862306a36Sopenharmony_ci } 138962306a36Sopenharmony_ci e->iso_resource.handle = r->resource.handle; 139062306a36Sopenharmony_ci e->iso_resource.channel = channel; 139162306a36Sopenharmony_ci e->iso_resource.bandwidth = bandwidth; 139262306a36Sopenharmony_ci 139362306a36Sopenharmony_ci queue_event(client, &e->event, 139462306a36Sopenharmony_ci &e->iso_resource, sizeof(e->iso_resource), NULL, 0); 139562306a36Sopenharmony_ci 139662306a36Sopenharmony_ci if (free) { 139762306a36Sopenharmony_ci cancel_delayed_work(&r->work); 139862306a36Sopenharmony_ci kfree(r->e_alloc); 139962306a36Sopenharmony_ci kfree(r->e_dealloc); 140062306a36Sopenharmony_ci kfree(r); 140162306a36Sopenharmony_ci } 140262306a36Sopenharmony_ci out: 140362306a36Sopenharmony_ci client_put(client); 140462306a36Sopenharmony_ci} 140562306a36Sopenharmony_ci 140662306a36Sopenharmony_cistatic void release_iso_resource(struct client *client, 140762306a36Sopenharmony_ci struct client_resource *resource) 140862306a36Sopenharmony_ci{ 140962306a36Sopenharmony_ci struct iso_resource *r = 141062306a36Sopenharmony_ci container_of(resource, struct iso_resource, resource); 141162306a36Sopenharmony_ci 141262306a36Sopenharmony_ci spin_lock_irq(&client->lock); 141362306a36Sopenharmony_ci r->todo = ISO_RES_DEALLOC; 141462306a36Sopenharmony_ci schedule_iso_resource(r, 0); 141562306a36Sopenharmony_ci spin_unlock_irq(&client->lock); 141662306a36Sopenharmony_ci} 141762306a36Sopenharmony_ci 141862306a36Sopenharmony_cistatic int init_iso_resource(struct client *client, 141962306a36Sopenharmony_ci struct fw_cdev_allocate_iso_resource *request, int todo) 142062306a36Sopenharmony_ci{ 142162306a36Sopenharmony_ci struct iso_resource_event *e1, *e2; 142262306a36Sopenharmony_ci struct iso_resource *r; 142362306a36Sopenharmony_ci int ret; 142462306a36Sopenharmony_ci 142562306a36Sopenharmony_ci if ((request->channels == 0 && request->bandwidth == 0) || 142662306a36Sopenharmony_ci request->bandwidth > BANDWIDTH_AVAILABLE_INITIAL) 142762306a36Sopenharmony_ci return -EINVAL; 142862306a36Sopenharmony_ci 142962306a36Sopenharmony_ci r = kmalloc(sizeof(*r), GFP_KERNEL); 143062306a36Sopenharmony_ci e1 = kmalloc(sizeof(*e1), GFP_KERNEL); 143162306a36Sopenharmony_ci e2 = kmalloc(sizeof(*e2), GFP_KERNEL); 143262306a36Sopenharmony_ci if (r == NULL || e1 == NULL || e2 == NULL) { 143362306a36Sopenharmony_ci ret = -ENOMEM; 143462306a36Sopenharmony_ci goto fail; 143562306a36Sopenharmony_ci } 143662306a36Sopenharmony_ci 143762306a36Sopenharmony_ci INIT_DELAYED_WORK(&r->work, iso_resource_work); 143862306a36Sopenharmony_ci r->client = client; 143962306a36Sopenharmony_ci r->todo = todo; 144062306a36Sopenharmony_ci r->generation = -1; 144162306a36Sopenharmony_ci r->channels = request->channels; 144262306a36Sopenharmony_ci r->bandwidth = request->bandwidth; 144362306a36Sopenharmony_ci r->e_alloc = e1; 144462306a36Sopenharmony_ci r->e_dealloc = e2; 144562306a36Sopenharmony_ci 144662306a36Sopenharmony_ci e1->iso_resource.closure = request->closure; 144762306a36Sopenharmony_ci e1->iso_resource.type = FW_CDEV_EVENT_ISO_RESOURCE_ALLOCATED; 144862306a36Sopenharmony_ci e2->iso_resource.closure = request->closure; 144962306a36Sopenharmony_ci e2->iso_resource.type = FW_CDEV_EVENT_ISO_RESOURCE_DEALLOCATED; 145062306a36Sopenharmony_ci 145162306a36Sopenharmony_ci if (todo == ISO_RES_ALLOC) { 145262306a36Sopenharmony_ci r->resource.release = release_iso_resource; 145362306a36Sopenharmony_ci ret = add_client_resource(client, &r->resource, GFP_KERNEL); 145462306a36Sopenharmony_ci if (ret < 0) 145562306a36Sopenharmony_ci goto fail; 145662306a36Sopenharmony_ci } else { 145762306a36Sopenharmony_ci r->resource.release = NULL; 145862306a36Sopenharmony_ci r->resource.handle = -1; 145962306a36Sopenharmony_ci schedule_iso_resource(r, 0); 146062306a36Sopenharmony_ci } 146162306a36Sopenharmony_ci request->handle = r->resource.handle; 146262306a36Sopenharmony_ci 146362306a36Sopenharmony_ci return 0; 146462306a36Sopenharmony_ci fail: 146562306a36Sopenharmony_ci kfree(r); 146662306a36Sopenharmony_ci kfree(e1); 146762306a36Sopenharmony_ci kfree(e2); 146862306a36Sopenharmony_ci 146962306a36Sopenharmony_ci return ret; 147062306a36Sopenharmony_ci} 147162306a36Sopenharmony_ci 147262306a36Sopenharmony_cistatic int ioctl_allocate_iso_resource(struct client *client, 147362306a36Sopenharmony_ci union ioctl_arg *arg) 147462306a36Sopenharmony_ci{ 147562306a36Sopenharmony_ci return init_iso_resource(client, 147662306a36Sopenharmony_ci &arg->allocate_iso_resource, ISO_RES_ALLOC); 147762306a36Sopenharmony_ci} 147862306a36Sopenharmony_ci 147962306a36Sopenharmony_cistatic int ioctl_deallocate_iso_resource(struct client *client, 148062306a36Sopenharmony_ci union ioctl_arg *arg) 148162306a36Sopenharmony_ci{ 148262306a36Sopenharmony_ci return release_client_resource(client, 148362306a36Sopenharmony_ci arg->deallocate.handle, release_iso_resource, NULL); 148462306a36Sopenharmony_ci} 148562306a36Sopenharmony_ci 148662306a36Sopenharmony_cistatic int ioctl_allocate_iso_resource_once(struct client *client, 148762306a36Sopenharmony_ci union ioctl_arg *arg) 148862306a36Sopenharmony_ci{ 148962306a36Sopenharmony_ci return init_iso_resource(client, 149062306a36Sopenharmony_ci &arg->allocate_iso_resource, ISO_RES_ALLOC_ONCE); 149162306a36Sopenharmony_ci} 149262306a36Sopenharmony_ci 149362306a36Sopenharmony_cistatic int ioctl_deallocate_iso_resource_once(struct client *client, 149462306a36Sopenharmony_ci union ioctl_arg *arg) 149562306a36Sopenharmony_ci{ 149662306a36Sopenharmony_ci return init_iso_resource(client, 149762306a36Sopenharmony_ci &arg->allocate_iso_resource, ISO_RES_DEALLOC_ONCE); 149862306a36Sopenharmony_ci} 149962306a36Sopenharmony_ci 150062306a36Sopenharmony_ci/* 150162306a36Sopenharmony_ci * Returns a speed code: Maximum speed to or from this device, 150262306a36Sopenharmony_ci * limited by the device's link speed, the local node's link speed, 150362306a36Sopenharmony_ci * and all PHY port speeds between the two links. 150462306a36Sopenharmony_ci */ 150562306a36Sopenharmony_cistatic int ioctl_get_speed(struct client *client, union ioctl_arg *arg) 150662306a36Sopenharmony_ci{ 150762306a36Sopenharmony_ci return client->device->max_speed; 150862306a36Sopenharmony_ci} 150962306a36Sopenharmony_ci 151062306a36Sopenharmony_cistatic int ioctl_send_broadcast_request(struct client *client, 151162306a36Sopenharmony_ci union ioctl_arg *arg) 151262306a36Sopenharmony_ci{ 151362306a36Sopenharmony_ci struct fw_cdev_send_request *a = &arg->send_request; 151462306a36Sopenharmony_ci 151562306a36Sopenharmony_ci switch (a->tcode) { 151662306a36Sopenharmony_ci case TCODE_WRITE_QUADLET_REQUEST: 151762306a36Sopenharmony_ci case TCODE_WRITE_BLOCK_REQUEST: 151862306a36Sopenharmony_ci break; 151962306a36Sopenharmony_ci default: 152062306a36Sopenharmony_ci return -EINVAL; 152162306a36Sopenharmony_ci } 152262306a36Sopenharmony_ci 152362306a36Sopenharmony_ci /* Security policy: Only allow accesses to Units Space. */ 152462306a36Sopenharmony_ci if (a->offset < CSR_REGISTER_BASE + CSR_CONFIG_ROM_END) 152562306a36Sopenharmony_ci return -EACCES; 152662306a36Sopenharmony_ci 152762306a36Sopenharmony_ci return init_request(client, a, LOCAL_BUS | 0x3f, SCODE_100); 152862306a36Sopenharmony_ci} 152962306a36Sopenharmony_ci 153062306a36Sopenharmony_cistatic int ioctl_send_stream_packet(struct client *client, union ioctl_arg *arg) 153162306a36Sopenharmony_ci{ 153262306a36Sopenharmony_ci struct fw_cdev_send_stream_packet *a = &arg->send_stream_packet; 153362306a36Sopenharmony_ci struct fw_cdev_send_request request; 153462306a36Sopenharmony_ci int dest; 153562306a36Sopenharmony_ci 153662306a36Sopenharmony_ci if (a->speed > client->device->card->link_speed || 153762306a36Sopenharmony_ci a->length > 1024 << a->speed) 153862306a36Sopenharmony_ci return -EIO; 153962306a36Sopenharmony_ci 154062306a36Sopenharmony_ci if (a->tag > 3 || a->channel > 63 || a->sy > 15) 154162306a36Sopenharmony_ci return -EINVAL; 154262306a36Sopenharmony_ci 154362306a36Sopenharmony_ci dest = fw_stream_packet_destination_id(a->tag, a->channel, a->sy); 154462306a36Sopenharmony_ci request.tcode = TCODE_STREAM_DATA; 154562306a36Sopenharmony_ci request.length = a->length; 154662306a36Sopenharmony_ci request.closure = a->closure; 154762306a36Sopenharmony_ci request.data = a->data; 154862306a36Sopenharmony_ci request.generation = a->generation; 154962306a36Sopenharmony_ci 155062306a36Sopenharmony_ci return init_request(client, &request, dest, a->speed); 155162306a36Sopenharmony_ci} 155262306a36Sopenharmony_ci 155362306a36Sopenharmony_cistatic void outbound_phy_packet_callback(struct fw_packet *packet, 155462306a36Sopenharmony_ci struct fw_card *card, int status) 155562306a36Sopenharmony_ci{ 155662306a36Sopenharmony_ci struct outbound_phy_packet_event *e = 155762306a36Sopenharmony_ci container_of(packet, struct outbound_phy_packet_event, p); 155862306a36Sopenharmony_ci struct client *e_client = e->client; 155962306a36Sopenharmony_ci u32 rcode; 156062306a36Sopenharmony_ci 156162306a36Sopenharmony_ci switch (status) { 156262306a36Sopenharmony_ci // expected: 156362306a36Sopenharmony_ci case ACK_COMPLETE: 156462306a36Sopenharmony_ci rcode = RCODE_COMPLETE; 156562306a36Sopenharmony_ci break; 156662306a36Sopenharmony_ci // should never happen with PHY packets: 156762306a36Sopenharmony_ci case ACK_PENDING: 156862306a36Sopenharmony_ci rcode = RCODE_COMPLETE; 156962306a36Sopenharmony_ci break; 157062306a36Sopenharmony_ci case ACK_BUSY_X: 157162306a36Sopenharmony_ci case ACK_BUSY_A: 157262306a36Sopenharmony_ci case ACK_BUSY_B: 157362306a36Sopenharmony_ci rcode = RCODE_BUSY; 157462306a36Sopenharmony_ci break; 157562306a36Sopenharmony_ci case ACK_DATA_ERROR: 157662306a36Sopenharmony_ci rcode = RCODE_DATA_ERROR; 157762306a36Sopenharmony_ci break; 157862306a36Sopenharmony_ci case ACK_TYPE_ERROR: 157962306a36Sopenharmony_ci rcode = RCODE_TYPE_ERROR; 158062306a36Sopenharmony_ci break; 158162306a36Sopenharmony_ci // stale generation; cancelled; on certain controllers: no ack 158262306a36Sopenharmony_ci default: 158362306a36Sopenharmony_ci rcode = status; 158462306a36Sopenharmony_ci break; 158562306a36Sopenharmony_ci } 158662306a36Sopenharmony_ci 158762306a36Sopenharmony_ci switch (e->phy_packet.without_tstamp.type) { 158862306a36Sopenharmony_ci case FW_CDEV_EVENT_PHY_PACKET_SENT: 158962306a36Sopenharmony_ci { 159062306a36Sopenharmony_ci struct fw_cdev_event_phy_packet *pp = &e->phy_packet.without_tstamp; 159162306a36Sopenharmony_ci 159262306a36Sopenharmony_ci pp->rcode = rcode; 159362306a36Sopenharmony_ci pp->data[0] = packet->timestamp; 159462306a36Sopenharmony_ci queue_event(e->client, &e->event, &e->phy_packet, sizeof(*pp) + pp->length, 159562306a36Sopenharmony_ci NULL, 0); 159662306a36Sopenharmony_ci break; 159762306a36Sopenharmony_ci } 159862306a36Sopenharmony_ci case FW_CDEV_EVENT_PHY_PACKET_SENT2: 159962306a36Sopenharmony_ci { 160062306a36Sopenharmony_ci struct fw_cdev_event_phy_packet2 *pp = &e->phy_packet.with_tstamp; 160162306a36Sopenharmony_ci 160262306a36Sopenharmony_ci pp->rcode = rcode; 160362306a36Sopenharmony_ci pp->tstamp = packet->timestamp; 160462306a36Sopenharmony_ci queue_event(e->client, &e->event, &e->phy_packet, sizeof(*pp) + pp->length, 160562306a36Sopenharmony_ci NULL, 0); 160662306a36Sopenharmony_ci break; 160762306a36Sopenharmony_ci } 160862306a36Sopenharmony_ci default: 160962306a36Sopenharmony_ci WARN_ON(1); 161062306a36Sopenharmony_ci break; 161162306a36Sopenharmony_ci } 161262306a36Sopenharmony_ci 161362306a36Sopenharmony_ci client_put(e_client); 161462306a36Sopenharmony_ci} 161562306a36Sopenharmony_ci 161662306a36Sopenharmony_cistatic int ioctl_send_phy_packet(struct client *client, union ioctl_arg *arg) 161762306a36Sopenharmony_ci{ 161862306a36Sopenharmony_ci struct fw_cdev_send_phy_packet *a = &arg->send_phy_packet; 161962306a36Sopenharmony_ci struct fw_card *card = client->device->card; 162062306a36Sopenharmony_ci struct outbound_phy_packet_event *e; 162162306a36Sopenharmony_ci 162262306a36Sopenharmony_ci /* Access policy: Allow this ioctl only on local nodes' device files. */ 162362306a36Sopenharmony_ci if (!client->device->is_local) 162462306a36Sopenharmony_ci return -ENOSYS; 162562306a36Sopenharmony_ci 162662306a36Sopenharmony_ci e = kzalloc(sizeof(*e) + sizeof(a->data), GFP_KERNEL); 162762306a36Sopenharmony_ci if (e == NULL) 162862306a36Sopenharmony_ci return -ENOMEM; 162962306a36Sopenharmony_ci 163062306a36Sopenharmony_ci client_get(client); 163162306a36Sopenharmony_ci e->client = client; 163262306a36Sopenharmony_ci e->p.speed = SCODE_100; 163362306a36Sopenharmony_ci e->p.generation = a->generation; 163462306a36Sopenharmony_ci e->p.header[0] = TCODE_LINK_INTERNAL << 4; 163562306a36Sopenharmony_ci e->p.header[1] = a->data[0]; 163662306a36Sopenharmony_ci e->p.header[2] = a->data[1]; 163762306a36Sopenharmony_ci e->p.header_length = 12; 163862306a36Sopenharmony_ci e->p.callback = outbound_phy_packet_callback; 163962306a36Sopenharmony_ci 164062306a36Sopenharmony_ci if (client->version < FW_CDEV_VERSION_EVENT_ASYNC_TSTAMP) { 164162306a36Sopenharmony_ci struct fw_cdev_event_phy_packet *pp = &e->phy_packet.without_tstamp; 164262306a36Sopenharmony_ci 164362306a36Sopenharmony_ci pp->closure = a->closure; 164462306a36Sopenharmony_ci pp->type = FW_CDEV_EVENT_PHY_PACKET_SENT; 164562306a36Sopenharmony_ci if (is_ping_packet(a->data)) 164662306a36Sopenharmony_ci pp->length = 4; 164762306a36Sopenharmony_ci } else { 164862306a36Sopenharmony_ci struct fw_cdev_event_phy_packet2 *pp = &e->phy_packet.with_tstamp; 164962306a36Sopenharmony_ci 165062306a36Sopenharmony_ci pp->closure = a->closure; 165162306a36Sopenharmony_ci pp->type = FW_CDEV_EVENT_PHY_PACKET_SENT2; 165262306a36Sopenharmony_ci // Keep the data field so that application can match the response event to the 165362306a36Sopenharmony_ci // request. 165462306a36Sopenharmony_ci pp->length = sizeof(a->data); 165562306a36Sopenharmony_ci memcpy(pp->data, a->data, sizeof(a->data)); 165662306a36Sopenharmony_ci } 165762306a36Sopenharmony_ci 165862306a36Sopenharmony_ci card->driver->send_request(card, &e->p); 165962306a36Sopenharmony_ci 166062306a36Sopenharmony_ci return 0; 166162306a36Sopenharmony_ci} 166262306a36Sopenharmony_ci 166362306a36Sopenharmony_cistatic int ioctl_receive_phy_packets(struct client *client, union ioctl_arg *arg) 166462306a36Sopenharmony_ci{ 166562306a36Sopenharmony_ci struct fw_cdev_receive_phy_packets *a = &arg->receive_phy_packets; 166662306a36Sopenharmony_ci struct fw_card *card = client->device->card; 166762306a36Sopenharmony_ci 166862306a36Sopenharmony_ci /* Access policy: Allow this ioctl only on local nodes' device files. */ 166962306a36Sopenharmony_ci if (!client->device->is_local) 167062306a36Sopenharmony_ci return -ENOSYS; 167162306a36Sopenharmony_ci 167262306a36Sopenharmony_ci spin_lock_irq(&card->lock); 167362306a36Sopenharmony_ci 167462306a36Sopenharmony_ci list_move_tail(&client->phy_receiver_link, &card->phy_receiver_list); 167562306a36Sopenharmony_ci client->phy_receiver_closure = a->closure; 167662306a36Sopenharmony_ci 167762306a36Sopenharmony_ci spin_unlock_irq(&card->lock); 167862306a36Sopenharmony_ci 167962306a36Sopenharmony_ci return 0; 168062306a36Sopenharmony_ci} 168162306a36Sopenharmony_ci 168262306a36Sopenharmony_civoid fw_cdev_handle_phy_packet(struct fw_card *card, struct fw_packet *p) 168362306a36Sopenharmony_ci{ 168462306a36Sopenharmony_ci struct client *client; 168562306a36Sopenharmony_ci struct inbound_phy_packet_event *e; 168662306a36Sopenharmony_ci unsigned long flags; 168762306a36Sopenharmony_ci 168862306a36Sopenharmony_ci spin_lock_irqsave(&card->lock, flags); 168962306a36Sopenharmony_ci 169062306a36Sopenharmony_ci list_for_each_entry(client, &card->phy_receiver_list, phy_receiver_link) { 169162306a36Sopenharmony_ci e = kmalloc(sizeof(*e) + 8, GFP_ATOMIC); 169262306a36Sopenharmony_ci if (e == NULL) 169362306a36Sopenharmony_ci break; 169462306a36Sopenharmony_ci 169562306a36Sopenharmony_ci if (client->version < FW_CDEV_VERSION_EVENT_ASYNC_TSTAMP) { 169662306a36Sopenharmony_ci struct fw_cdev_event_phy_packet *pp = &e->phy_packet.without_tstamp; 169762306a36Sopenharmony_ci 169862306a36Sopenharmony_ci pp->closure = client->phy_receiver_closure; 169962306a36Sopenharmony_ci pp->type = FW_CDEV_EVENT_PHY_PACKET_RECEIVED; 170062306a36Sopenharmony_ci pp->rcode = RCODE_COMPLETE; 170162306a36Sopenharmony_ci pp->length = 8; 170262306a36Sopenharmony_ci pp->data[0] = p->header[1]; 170362306a36Sopenharmony_ci pp->data[1] = p->header[2]; 170462306a36Sopenharmony_ci queue_event(client, &e->event, &e->phy_packet, sizeof(*pp) + 8, NULL, 0); 170562306a36Sopenharmony_ci } else { 170662306a36Sopenharmony_ci struct fw_cdev_event_phy_packet2 *pp = &e->phy_packet.with_tstamp; 170762306a36Sopenharmony_ci 170862306a36Sopenharmony_ci pp = &e->phy_packet.with_tstamp; 170962306a36Sopenharmony_ci pp->closure = client->phy_receiver_closure; 171062306a36Sopenharmony_ci pp->type = FW_CDEV_EVENT_PHY_PACKET_RECEIVED2; 171162306a36Sopenharmony_ci pp->rcode = RCODE_COMPLETE; 171262306a36Sopenharmony_ci pp->length = 8; 171362306a36Sopenharmony_ci pp->tstamp = p->timestamp; 171462306a36Sopenharmony_ci pp->data[0] = p->header[1]; 171562306a36Sopenharmony_ci pp->data[1] = p->header[2]; 171662306a36Sopenharmony_ci queue_event(client, &e->event, &e->phy_packet, sizeof(*pp) + 8, NULL, 0); 171762306a36Sopenharmony_ci } 171862306a36Sopenharmony_ci } 171962306a36Sopenharmony_ci 172062306a36Sopenharmony_ci spin_unlock_irqrestore(&card->lock, flags); 172162306a36Sopenharmony_ci} 172262306a36Sopenharmony_ci 172362306a36Sopenharmony_cistatic int (* const ioctl_handlers[])(struct client *, union ioctl_arg *) = { 172462306a36Sopenharmony_ci [0x00] = ioctl_get_info, 172562306a36Sopenharmony_ci [0x01] = ioctl_send_request, 172662306a36Sopenharmony_ci [0x02] = ioctl_allocate, 172762306a36Sopenharmony_ci [0x03] = ioctl_deallocate, 172862306a36Sopenharmony_ci [0x04] = ioctl_send_response, 172962306a36Sopenharmony_ci [0x05] = ioctl_initiate_bus_reset, 173062306a36Sopenharmony_ci [0x06] = ioctl_add_descriptor, 173162306a36Sopenharmony_ci [0x07] = ioctl_remove_descriptor, 173262306a36Sopenharmony_ci [0x08] = ioctl_create_iso_context, 173362306a36Sopenharmony_ci [0x09] = ioctl_queue_iso, 173462306a36Sopenharmony_ci [0x0a] = ioctl_start_iso, 173562306a36Sopenharmony_ci [0x0b] = ioctl_stop_iso, 173662306a36Sopenharmony_ci [0x0c] = ioctl_get_cycle_timer, 173762306a36Sopenharmony_ci [0x0d] = ioctl_allocate_iso_resource, 173862306a36Sopenharmony_ci [0x0e] = ioctl_deallocate_iso_resource, 173962306a36Sopenharmony_ci [0x0f] = ioctl_allocate_iso_resource_once, 174062306a36Sopenharmony_ci [0x10] = ioctl_deallocate_iso_resource_once, 174162306a36Sopenharmony_ci [0x11] = ioctl_get_speed, 174262306a36Sopenharmony_ci [0x12] = ioctl_send_broadcast_request, 174362306a36Sopenharmony_ci [0x13] = ioctl_send_stream_packet, 174462306a36Sopenharmony_ci [0x14] = ioctl_get_cycle_timer2, 174562306a36Sopenharmony_ci [0x15] = ioctl_send_phy_packet, 174662306a36Sopenharmony_ci [0x16] = ioctl_receive_phy_packets, 174762306a36Sopenharmony_ci [0x17] = ioctl_set_iso_channels, 174862306a36Sopenharmony_ci [0x18] = ioctl_flush_iso, 174962306a36Sopenharmony_ci}; 175062306a36Sopenharmony_ci 175162306a36Sopenharmony_cistatic int dispatch_ioctl(struct client *client, 175262306a36Sopenharmony_ci unsigned int cmd, void __user *arg) 175362306a36Sopenharmony_ci{ 175462306a36Sopenharmony_ci union ioctl_arg buffer; 175562306a36Sopenharmony_ci int ret; 175662306a36Sopenharmony_ci 175762306a36Sopenharmony_ci if (fw_device_is_shutdown(client->device)) 175862306a36Sopenharmony_ci return -ENODEV; 175962306a36Sopenharmony_ci 176062306a36Sopenharmony_ci if (_IOC_TYPE(cmd) != '#' || 176162306a36Sopenharmony_ci _IOC_NR(cmd) >= ARRAY_SIZE(ioctl_handlers) || 176262306a36Sopenharmony_ci _IOC_SIZE(cmd) > sizeof(buffer)) 176362306a36Sopenharmony_ci return -ENOTTY; 176462306a36Sopenharmony_ci 176562306a36Sopenharmony_ci memset(&buffer, 0, sizeof(buffer)); 176662306a36Sopenharmony_ci 176762306a36Sopenharmony_ci if (_IOC_DIR(cmd) & _IOC_WRITE) 176862306a36Sopenharmony_ci if (copy_from_user(&buffer, arg, _IOC_SIZE(cmd))) 176962306a36Sopenharmony_ci return -EFAULT; 177062306a36Sopenharmony_ci 177162306a36Sopenharmony_ci ret = ioctl_handlers[_IOC_NR(cmd)](client, &buffer); 177262306a36Sopenharmony_ci if (ret < 0) 177362306a36Sopenharmony_ci return ret; 177462306a36Sopenharmony_ci 177562306a36Sopenharmony_ci if (_IOC_DIR(cmd) & _IOC_READ) 177662306a36Sopenharmony_ci if (copy_to_user(arg, &buffer, _IOC_SIZE(cmd))) 177762306a36Sopenharmony_ci return -EFAULT; 177862306a36Sopenharmony_ci 177962306a36Sopenharmony_ci return ret; 178062306a36Sopenharmony_ci} 178162306a36Sopenharmony_ci 178262306a36Sopenharmony_cistatic long fw_device_op_ioctl(struct file *file, 178362306a36Sopenharmony_ci unsigned int cmd, unsigned long arg) 178462306a36Sopenharmony_ci{ 178562306a36Sopenharmony_ci return dispatch_ioctl(file->private_data, cmd, (void __user *)arg); 178662306a36Sopenharmony_ci} 178762306a36Sopenharmony_ci 178862306a36Sopenharmony_cistatic int fw_device_op_mmap(struct file *file, struct vm_area_struct *vma) 178962306a36Sopenharmony_ci{ 179062306a36Sopenharmony_ci struct client *client = file->private_data; 179162306a36Sopenharmony_ci unsigned long size; 179262306a36Sopenharmony_ci int page_count, ret; 179362306a36Sopenharmony_ci 179462306a36Sopenharmony_ci if (fw_device_is_shutdown(client->device)) 179562306a36Sopenharmony_ci return -ENODEV; 179662306a36Sopenharmony_ci 179762306a36Sopenharmony_ci /* FIXME: We could support multiple buffers, but we don't. */ 179862306a36Sopenharmony_ci if (client->buffer.pages != NULL) 179962306a36Sopenharmony_ci return -EBUSY; 180062306a36Sopenharmony_ci 180162306a36Sopenharmony_ci if (!(vma->vm_flags & VM_SHARED)) 180262306a36Sopenharmony_ci return -EINVAL; 180362306a36Sopenharmony_ci 180462306a36Sopenharmony_ci if (vma->vm_start & ~PAGE_MASK) 180562306a36Sopenharmony_ci return -EINVAL; 180662306a36Sopenharmony_ci 180762306a36Sopenharmony_ci client->vm_start = vma->vm_start; 180862306a36Sopenharmony_ci size = vma->vm_end - vma->vm_start; 180962306a36Sopenharmony_ci page_count = size >> PAGE_SHIFT; 181062306a36Sopenharmony_ci if (size & ~PAGE_MASK) 181162306a36Sopenharmony_ci return -EINVAL; 181262306a36Sopenharmony_ci 181362306a36Sopenharmony_ci ret = fw_iso_buffer_alloc(&client->buffer, page_count); 181462306a36Sopenharmony_ci if (ret < 0) 181562306a36Sopenharmony_ci return ret; 181662306a36Sopenharmony_ci 181762306a36Sopenharmony_ci spin_lock_irq(&client->lock); 181862306a36Sopenharmony_ci if (client->iso_context) { 181962306a36Sopenharmony_ci ret = fw_iso_buffer_map_dma(&client->buffer, 182062306a36Sopenharmony_ci client->device->card, 182162306a36Sopenharmony_ci iso_dma_direction(client->iso_context)); 182262306a36Sopenharmony_ci client->buffer_is_mapped = (ret == 0); 182362306a36Sopenharmony_ci } 182462306a36Sopenharmony_ci spin_unlock_irq(&client->lock); 182562306a36Sopenharmony_ci if (ret < 0) 182662306a36Sopenharmony_ci goto fail; 182762306a36Sopenharmony_ci 182862306a36Sopenharmony_ci ret = vm_map_pages_zero(vma, client->buffer.pages, 182962306a36Sopenharmony_ci client->buffer.page_count); 183062306a36Sopenharmony_ci if (ret < 0) 183162306a36Sopenharmony_ci goto fail; 183262306a36Sopenharmony_ci 183362306a36Sopenharmony_ci return 0; 183462306a36Sopenharmony_ci fail: 183562306a36Sopenharmony_ci fw_iso_buffer_destroy(&client->buffer, client->device->card); 183662306a36Sopenharmony_ci return ret; 183762306a36Sopenharmony_ci} 183862306a36Sopenharmony_ci 183962306a36Sopenharmony_cistatic int is_outbound_transaction_resource(int id, void *p, void *data) 184062306a36Sopenharmony_ci{ 184162306a36Sopenharmony_ci struct client_resource *resource = p; 184262306a36Sopenharmony_ci 184362306a36Sopenharmony_ci return resource->release == release_transaction; 184462306a36Sopenharmony_ci} 184562306a36Sopenharmony_ci 184662306a36Sopenharmony_cistatic int has_outbound_transactions(struct client *client) 184762306a36Sopenharmony_ci{ 184862306a36Sopenharmony_ci int ret; 184962306a36Sopenharmony_ci 185062306a36Sopenharmony_ci spin_lock_irq(&client->lock); 185162306a36Sopenharmony_ci ret = idr_for_each(&client->resource_idr, 185262306a36Sopenharmony_ci is_outbound_transaction_resource, NULL); 185362306a36Sopenharmony_ci spin_unlock_irq(&client->lock); 185462306a36Sopenharmony_ci 185562306a36Sopenharmony_ci return ret; 185662306a36Sopenharmony_ci} 185762306a36Sopenharmony_ci 185862306a36Sopenharmony_cistatic int shutdown_resource(int id, void *p, void *data) 185962306a36Sopenharmony_ci{ 186062306a36Sopenharmony_ci struct client_resource *resource = p; 186162306a36Sopenharmony_ci struct client *client = data; 186262306a36Sopenharmony_ci 186362306a36Sopenharmony_ci resource->release(client, resource); 186462306a36Sopenharmony_ci client_put(client); 186562306a36Sopenharmony_ci 186662306a36Sopenharmony_ci return 0; 186762306a36Sopenharmony_ci} 186862306a36Sopenharmony_ci 186962306a36Sopenharmony_cistatic int fw_device_op_release(struct inode *inode, struct file *file) 187062306a36Sopenharmony_ci{ 187162306a36Sopenharmony_ci struct client *client = file->private_data; 187262306a36Sopenharmony_ci struct event *event, *next_event; 187362306a36Sopenharmony_ci 187462306a36Sopenharmony_ci spin_lock_irq(&client->device->card->lock); 187562306a36Sopenharmony_ci list_del(&client->phy_receiver_link); 187662306a36Sopenharmony_ci spin_unlock_irq(&client->device->card->lock); 187762306a36Sopenharmony_ci 187862306a36Sopenharmony_ci mutex_lock(&client->device->client_list_mutex); 187962306a36Sopenharmony_ci list_del(&client->link); 188062306a36Sopenharmony_ci mutex_unlock(&client->device->client_list_mutex); 188162306a36Sopenharmony_ci 188262306a36Sopenharmony_ci if (client->iso_context) 188362306a36Sopenharmony_ci fw_iso_context_destroy(client->iso_context); 188462306a36Sopenharmony_ci 188562306a36Sopenharmony_ci if (client->buffer.pages) 188662306a36Sopenharmony_ci fw_iso_buffer_destroy(&client->buffer, client->device->card); 188762306a36Sopenharmony_ci 188862306a36Sopenharmony_ci /* Freeze client->resource_idr and client->event_list */ 188962306a36Sopenharmony_ci spin_lock_irq(&client->lock); 189062306a36Sopenharmony_ci client->in_shutdown = true; 189162306a36Sopenharmony_ci spin_unlock_irq(&client->lock); 189262306a36Sopenharmony_ci 189362306a36Sopenharmony_ci wait_event(client->tx_flush_wait, !has_outbound_transactions(client)); 189462306a36Sopenharmony_ci 189562306a36Sopenharmony_ci idr_for_each(&client->resource_idr, shutdown_resource, client); 189662306a36Sopenharmony_ci idr_destroy(&client->resource_idr); 189762306a36Sopenharmony_ci 189862306a36Sopenharmony_ci list_for_each_entry_safe(event, next_event, &client->event_list, link) 189962306a36Sopenharmony_ci kfree(event); 190062306a36Sopenharmony_ci 190162306a36Sopenharmony_ci client_put(client); 190262306a36Sopenharmony_ci 190362306a36Sopenharmony_ci return 0; 190462306a36Sopenharmony_ci} 190562306a36Sopenharmony_ci 190662306a36Sopenharmony_cistatic __poll_t fw_device_op_poll(struct file *file, poll_table * pt) 190762306a36Sopenharmony_ci{ 190862306a36Sopenharmony_ci struct client *client = file->private_data; 190962306a36Sopenharmony_ci __poll_t mask = 0; 191062306a36Sopenharmony_ci 191162306a36Sopenharmony_ci poll_wait(file, &client->wait, pt); 191262306a36Sopenharmony_ci 191362306a36Sopenharmony_ci if (fw_device_is_shutdown(client->device)) 191462306a36Sopenharmony_ci mask |= EPOLLHUP | EPOLLERR; 191562306a36Sopenharmony_ci if (!list_empty(&client->event_list)) 191662306a36Sopenharmony_ci mask |= EPOLLIN | EPOLLRDNORM; 191762306a36Sopenharmony_ci 191862306a36Sopenharmony_ci return mask; 191962306a36Sopenharmony_ci} 192062306a36Sopenharmony_ci 192162306a36Sopenharmony_ciconst struct file_operations fw_device_ops = { 192262306a36Sopenharmony_ci .owner = THIS_MODULE, 192362306a36Sopenharmony_ci .llseek = no_llseek, 192462306a36Sopenharmony_ci .open = fw_device_op_open, 192562306a36Sopenharmony_ci .read = fw_device_op_read, 192662306a36Sopenharmony_ci .unlocked_ioctl = fw_device_op_ioctl, 192762306a36Sopenharmony_ci .mmap = fw_device_op_mmap, 192862306a36Sopenharmony_ci .release = fw_device_op_release, 192962306a36Sopenharmony_ci .poll = fw_device_op_poll, 193062306a36Sopenharmony_ci .compat_ioctl = compat_ptr_ioctl, 193162306a36Sopenharmony_ci}; 1932