core-cdev.c revision e205597d188a9ea69ce43f740a14f07b3f5b996a
1c781c06d119d04601727f2fbc30151e6760d536dKristian Høgsberg/* 2c781c06d119d04601727f2fbc30151e6760d536dKristian Høgsberg * Char device for device raw access 319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg * 4c781c06d119d04601727f2fbc30151e6760d536dKristian Høgsberg * Copyright (C) 2005-2007 Kristian Hoegsberg <krh@bitplanet.net> 519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg * 619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg * This program is free software; you can redistribute it and/or modify 719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg * it under the terms of the GNU General Public License as published by 819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg * the Free Software Foundation; either version 2 of the License, or 919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg * (at your option) any later version. 1019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg * 1119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg * This program is distributed in the hope that it will be useful, 1219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg * but WITHOUT ANY WARRANTY; without even the implied warranty of 1319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg * GNU General Public License for more details. 1519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg * 1619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg * You should have received a copy of the GNU General Public License 1719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg * along with this program; if not, write to the Free Software Foundation, 1819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 1919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg */ 2019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 21be5bbd6756b44602a3f281af05c2f416fa9bd1c6Stefan Richter#include <linux/compat.h> 22be5bbd6756b44602a3f281af05c2f416fa9bd1c6Stefan Richter#include <linux/delay.h> 23be5bbd6756b44602a3f281af05c2f416fa9bd1c6Stefan Richter#include <linux/device.h> 24be5bbd6756b44602a3f281af05c2f416fa9bd1c6Stefan Richter#include <linux/errno.h> 2577c9a5daa9c4d9b37812c9c69c7bcbb3f9399c3cStefan Richter#include <linux/firewire.h> 26be5bbd6756b44602a3f281af05c2f416fa9bd1c6Stefan Richter#include <linux/firewire-cdev.h> 27be5bbd6756b44602a3f281af05c2f416fa9bd1c6Stefan Richter#include <linux/idr.h> 284a9bde9b8ab55a2bb51b57cad215a97bcf80bae2Stefan Richter#include <linux/irqflags.h> 29b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter#include <linux/jiffies.h> 3019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg#include <linux/kernel.h> 31fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter#include <linux/kref.h> 32be5bbd6756b44602a3f281af05c2f416fa9bd1c6Stefan Richter#include <linux/mm.h> 33be5bbd6756b44602a3f281af05c2f416fa9bd1c6Stefan Richter#include <linux/module.h> 34d67cfb9613f373d76daa2c8d209629601424ca12Stefan Richter#include <linux/mutex.h> 3519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg#include <linux/poll.h> 36a99bbaf5ee6bad1aca0c88ea65ec6e5373e86184Alexey Dobriyan#include <linux/sched.h> 37cf417e5494582453c033d8cac9e1352e74215435Jay Fenlason#include <linux/spinlock.h> 38281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter#include <linux/string.h> 39be5bbd6756b44602a3f281af05c2f416fa9bd1c6Stefan Richter#include <linux/time.h> 40e034d242593f12533c11742ce38c245a33e57dc7Stefan Richter#include <linux/uaccess.h> 41be5bbd6756b44602a3f281af05c2f416fa9bd1c6Stefan Richter#include <linux/vmalloc.h> 42be5bbd6756b44602a3f281af05c2f416fa9bd1c6Stefan Richter#include <linux/wait.h> 43b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter#include <linux/workqueue.h> 44be5bbd6756b44602a3f281af05c2f416fa9bd1c6Stefan Richter 45a64408b96b5f67c2778958a230b5cfa3408a4a81Stefan Richter#include <asm/system.h> 46be5bbd6756b44602a3f281af05c2f416fa9bd1c6Stefan Richter 4777c9a5daa9c4d9b37812c9c69c7bcbb3f9399c3cStefan Richter#include "core.h" 4819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 49604f45167824e18ad5766e51ecf1d4d65f15118dStefan Richter/* 50604f45167824e18ad5766e51ecf1d4d65f15118dStefan Richter * ABI version history is documented in linux/firewire-cdev.h. 51604f45167824e18ad5766e51ecf1d4d65f15118dStefan Richter */ 52e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter#define FW_CDEV_KERNEL_VERSION 4 53e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter#define FW_CDEV_VERSION_EVENT_REQUEST2 4 54604f45167824e18ad5766e51ecf1d4d65f15118dStefan Richter 5519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsbergstruct client { 56344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg u32 version; 5719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg struct fw_device *device; 5845ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason 5919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg spinlock_t lock; 6045ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason bool in_shutdown; 6145ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason struct idr resource_idr; 6219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg struct list_head event_list; 6319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg wait_queue_head_t wait; 64da8ecffaed434a12930f652898f9e86d1c2abc3eKristian Høgsberg u64 bus_reset_closure; 659aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg 6619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg struct fw_iso_context *iso_context; 67abaa5743e340c23922d92c9a5a6753ea3ae71e58Kristian Høgsberg u64 iso_closure; 689aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg struct fw_iso_buffer buffer; 699aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg unsigned long vm_start; 7097bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg 7197bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg struct list_head link; 72fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter struct kref kref; 7319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}; 7419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 75fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richterstatic inline void client_get(struct client *client) 76fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter{ 77fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter kref_get(&client->kref); 78fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter} 79fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter 80fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richterstatic void client_release(struct kref *kref) 81fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter{ 82fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter struct client *client = container_of(kref, struct client, kref); 83fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter 84fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter fw_device_put(client->device); 85fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter kfree(client); 86fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter} 87fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter 88fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richterstatic void client_put(struct client *client) 89fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter{ 90fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter kref_put(&client->kref, client_release); 91fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter} 92fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter 9397c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richterstruct client_resource; 9497c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richtertypedef void (*client_resource_release_fn_t)(struct client *, 9597c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct client_resource *); 9697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richterstruct client_resource { 9797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter client_resource_release_fn_t release; 9897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter int handle; 9997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter}; 10097c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter 10197c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richterstruct address_handler_resource { 10297c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct client_resource resource; 10397c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct fw_address_handler handler; 10497c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter __u64 closure; 10597c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct client *client; 10697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter}; 10797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter 10897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richterstruct outbound_transaction_resource { 10997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct client_resource resource; 11097c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct fw_transaction transaction; 11197c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter}; 11297c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter 11397c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richterstruct inbound_transaction_resource { 11497c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct client_resource resource; 11508bd34c98d631fe85744d4c920c80f48a1d95f54Jay Fenlason struct fw_card *card; 11697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct fw_request *request; 11797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter void *data; 11897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter size_t length; 11997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter}; 12097c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter 12197c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richterstruct descriptor_resource { 12297c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct client_resource resource; 12397c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct fw_descriptor descriptor; 12497c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter u32 data[0]; 12597c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter}; 12697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter 127b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richterstruct iso_resource { 128b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter struct client_resource resource; 129b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter struct client *client; 130b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter /* Schedule work and access todo only with client->lock held. */ 131b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter struct delayed_work work; 1321ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter enum {ISO_RES_ALLOC, ISO_RES_REALLOC, ISO_RES_DEALLOC, 1331ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter ISO_RES_ALLOC_ONCE, ISO_RES_DEALLOC_ONCE,} todo; 134b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter int generation; 135b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter u64 channels; 136b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter s32 bandwidth; 1376fdc03709433ccc2005f0f593ae9d9dd04f7b485Stefan Richter __be32 transaction_data[2]; 138b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter struct iso_resource_event *e_alloc, *e_dealloc; 139b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter}; 140b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 141b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richterstatic void release_iso_resource(struct client *, struct client_resource *); 142b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 1439fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richterstatic void schedule_iso_resource(struct iso_resource *r, unsigned long delay) 1449fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter{ 1459fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter client_get(r->client); 1469fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter if (!schedule_delayed_work(&r->work, delay)) 1479fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter client_put(r->client); 1489fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter} 1499fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter 1509fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richterstatic void schedule_if_iso_resource(struct client_resource *resource) 1519fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter{ 1529fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter if (resource->release == release_iso_resource) 1539fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter schedule_iso_resource(container_of(resource, 1549fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter struct iso_resource, resource), 0); 1559fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter} 1569fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter 15797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter/* 15897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter * dequeue_event() just kfree()'s the event, so the event has to be 15997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter * the first field in a struct XYZ_event. 16097c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter */ 16197c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richterstruct event { 16297c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct { void *data; size_t size; } v[2]; 16397c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct list_head link; 16497c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter}; 16597c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter 16697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richterstruct bus_reset_event { 16797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct event event; 16897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct fw_cdev_event_bus_reset reset; 16997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter}; 17097c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter 17197c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richterstruct outbound_transaction_event { 17297c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct event event; 17397c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct client *client; 17497c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct outbound_transaction_resource r; 17597c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct fw_cdev_event_response response; 17697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter}; 17797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter 17897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richterstruct inbound_transaction_event { 17997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct event event; 180e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter union { 181e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter struct fw_cdev_event_request request; 182e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter struct fw_cdev_event_request2 request2; 183e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter } req; 18497c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter}; 18597c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter 18697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richterstruct iso_interrupt_event { 18797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct event event; 18897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct fw_cdev_event_iso_interrupt interrupt; 18997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter}; 19097c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter 191b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richterstruct iso_resource_event { 192b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter struct event event; 193e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter struct fw_cdev_event_iso_resource iso_resource; 194b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter}; 195b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 19653dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic inline void __user *u64_to_uptr(__u64 value) 19719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{ 19819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg return (void __user *)(unsigned long)value; 19919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg} 20019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 20153dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic inline __u64 uptr_to_u64(void __user *ptr) 20219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{ 20319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg return (__u64)(unsigned long)ptr; 20419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg} 20519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 20619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsbergstatic int fw_device_op_open(struct inode *inode, struct file *file) 20719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{ 20819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg struct fw_device *device; 20919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg struct client *client; 21019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 21196b19062e741b715cf399312c30e0672d8889569Stefan Richter device = fw_device_get_by_devt(inode->i_rdev); 212a3aca3dabbcf00f2088d472f27755c29acaa992eKristian Høgsberg if (device == NULL) 213a3aca3dabbcf00f2088d472f27755c29acaa992eKristian Høgsberg return -ENODEV; 21419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 215551f4cb9de716ffcdaf968c99a450c22ff12e8c3Jay Fenlason if (fw_device_is_shutdown(device)) { 216551f4cb9de716ffcdaf968c99a450c22ff12e8c3Jay Fenlason fw_device_put(device); 217551f4cb9de716ffcdaf968c99a450c22ff12e8c3Jay Fenlason return -ENODEV; 218551f4cb9de716ffcdaf968c99a450c22ff12e8c3Jay Fenlason } 219551f4cb9de716ffcdaf968c99a450c22ff12e8c3Jay Fenlason 2202d826cc5c791bdc5f5651324c485746be9492be0Kristian Høgsberg client = kzalloc(sizeof(*client), GFP_KERNEL); 22196b19062e741b715cf399312c30e0672d8889569Stefan Richter if (client == NULL) { 22296b19062e741b715cf399312c30e0672d8889569Stefan Richter fw_device_put(device); 22319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg return -ENOMEM; 22496b19062e741b715cf399312c30e0672d8889569Stefan Richter } 22519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 22696b19062e741b715cf399312c30e0672d8889569Stefan Richter client->device = device; 22719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg spin_lock_init(&client->lock); 22845ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason idr_init(&client->resource_idr); 22945ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason INIT_LIST_HEAD(&client->event_list); 23019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg init_waitqueue_head(&client->wait); 231fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter kref_init(&client->kref); 23219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 23319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg file->private_data = client; 23419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 235d67cfb9613f373d76daa2c8d209629601424ca12Stefan Richter mutex_lock(&device->client_list_mutex); 23697bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg list_add_tail(&client->link, &device->client_list); 237d67cfb9613f373d76daa2c8d209629601424ca12Stefan Richter mutex_unlock(&device->client_list_mutex); 23897bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg 2393ac26b2ee30005930117fe6a180c139c5f300fafStefan Richter return nonseekable_open(inode, file); 24019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg} 24119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 24219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsbergstatic void queue_event(struct client *client, struct event *event, 24319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg void *data0, size_t size0, void *data1, size_t size1) 24419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{ 24519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg unsigned long flags; 24619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 24719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg event->v[0].data = data0; 24819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg event->v[0].size = size0; 24919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg event->v[1].data = data1; 25019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg event->v[1].size = size1; 25119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 25219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg spin_lock_irqsave(&client->lock, flags); 25345ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason if (client->in_shutdown) 25445ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason kfree(event); 25545ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason else 25645ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason list_add_tail(&event->link, &client->event_list); 25719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg spin_unlock_irqrestore(&client->lock, flags); 25883431cba3d847fc2296d3f38ce7feb623a1cfc45Jay Fenlason 25983431cba3d847fc2296d3f38ce7feb623a1cfc45Jay Fenlason wake_up_interruptible(&client->wait); 26019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg} 26119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 26253dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic int dequeue_event(struct client *client, 26353dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter char __user *buffer, size_t count) 26419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{ 26519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg struct event *event; 26619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg size_t size, total; 2672dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter int i, ret; 26819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 2692dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter ret = wait_event_interruptible(client->wait, 2702dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter !list_empty(&client->event_list) || 2712dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter fw_device_is_shutdown(client->device)); 2722dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter if (ret < 0) 2732dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter return ret; 27419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 2752603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg if (list_empty(&client->event_list) && 2762603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg fw_device_is_shutdown(client->device)) 2772603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg return -ENODEV; 27819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 2793ba949868a6dc082b24cba5c3bf3f50de7391433Stefan Richter spin_lock_irq(&client->lock); 280a459b8ab9c176143fecef8ace4b70d6dbd7a8113Stefan Richter event = list_first_entry(&client->event_list, struct event, link); 28119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg list_del(&event->link); 2823ba949868a6dc082b24cba5c3bf3f50de7391433Stefan Richter spin_unlock_irq(&client->lock); 28319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 28419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg total = 0; 28519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg for (i = 0; i < ARRAY_SIZE(event->v) && total < count; i++) { 28619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg size = min(event->v[i].size, count - total); 2872603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg if (copy_to_user(buffer + total, event->v[i].data, size)) { 2882dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter ret = -EFAULT; 28919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg goto out; 2902603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg } 29119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg total += size; 29219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg } 2932dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter ret = total; 29419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 29519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg out: 29619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg kfree(event); 29719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 2982dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter return ret; 29919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg} 30019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 30153dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic ssize_t fw_device_op_read(struct file *file, char __user *buffer, 30253dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter size_t count, loff_t *offset) 30319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{ 30419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg struct client *client = file->private_data; 30519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 30619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg return dequeue_event(client, buffer, count); 30719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg} 30819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 30953dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic void fill_bus_reset_event(struct fw_cdev_event_bus_reset *event, 31053dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter struct client *client) 311344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg{ 312da8ecffaed434a12930f652898f9e86d1c2abc3eKristian Høgsberg struct fw_card *card = client->device->card; 313cf417e5494582453c033d8cac9e1352e74215435Jay Fenlason 3143ba949868a6dc082b24cba5c3bf3f50de7391433Stefan Richter spin_lock_irq(&card->lock); 315344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg 316da8ecffaed434a12930f652898f9e86d1c2abc3eKristian Høgsberg event->closure = client->bus_reset_closure; 317344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg event->type = FW_CDEV_EVENT_BUS_RESET; 318cf5a56ac8083dd04ffe8b9b2ec7895e9bcff44bcStefan Richter event->generation = client->device->generation; 319da8ecffaed434a12930f652898f9e86d1c2abc3eKristian Høgsberg event->node_id = client->device->node_id; 320344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg event->local_node_id = card->local_node->node_id; 321344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg event->bm_node_id = 0; /* FIXME: We don't track the BM. */ 322344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg event->irm_node_id = card->irm_node->node_id; 323344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg event->root_node_id = card->root_node->node_id; 324cf417e5494582453c033d8cac9e1352e74215435Jay Fenlason 3253ba949868a6dc082b24cba5c3bf3f50de7391433Stefan Richter spin_unlock_irq(&card->lock); 326344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg} 327344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg 32853dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic void for_each_client(struct fw_device *device, 32953dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter void (*callback)(struct client *client)) 3302603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg{ 3312603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg struct client *c; 3322603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg 333d67cfb9613f373d76daa2c8d209629601424ca12Stefan Richter mutex_lock(&device->client_list_mutex); 3342603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg list_for_each_entry(c, &device->client_list, link) 3352603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg callback(c); 336d67cfb9613f373d76daa2c8d209629601424ca12Stefan Richter mutex_unlock(&device->client_list_mutex); 3372603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg} 3382603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg 339b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richterstatic int schedule_reallocations(int id, void *p, void *data) 340b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter{ 3419fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter schedule_if_iso_resource(p); 342b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 343b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter return 0; 344b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter} 345b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 34653dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic void queue_bus_reset_event(struct client *client) 34797bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg{ 34897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct bus_reset_event *e; 34997bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg 35097c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter e = kzalloc(sizeof(*e), GFP_KERNEL); 35197c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter if (e == NULL) { 35297bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg fw_notify("Out of memory when allocating bus reset event\n"); 35397bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg return; 35497bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg } 35597bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg 35697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter fill_bus_reset_event(&e->reset, client); 35797bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg 35897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter queue_event(client, &e->event, 35997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter &e->reset, sizeof(e->reset), NULL, 0); 360b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 361b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter spin_lock_irq(&client->lock); 362b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter idr_for_each(&client->resource_idr, schedule_reallocations, client); 363b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter spin_unlock_irq(&client->lock); 36497bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg} 36597bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg 36697bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsbergvoid fw_device_cdev_update(struct fw_device *device) 36797bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg{ 3682603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg for_each_client(device, queue_bus_reset_event); 3692603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg} 37097bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg 3712603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsbergstatic void wake_up_client(struct client *client) 3722603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg{ 3732603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg wake_up_interruptible(&client->wait); 3742603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg} 37597bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg 3762603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsbergvoid fw_device_cdev_remove(struct fw_device *device) 3772603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg{ 3782603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg for_each_client(device, wake_up_client); 37997bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg} 38097bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg 3816e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterunion ioctl_arg { 3826e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter struct fw_cdev_get_info get_info; 3836e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter struct fw_cdev_send_request send_request; 3846e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter struct fw_cdev_allocate allocate; 3856e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter struct fw_cdev_deallocate deallocate; 3866e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter struct fw_cdev_send_response send_response; 3876e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter struct fw_cdev_initiate_bus_reset initiate_bus_reset; 3886e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter struct fw_cdev_add_descriptor add_descriptor; 3896e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter struct fw_cdev_remove_descriptor remove_descriptor; 3906e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter struct fw_cdev_create_iso_context create_iso_context; 3916e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter struct fw_cdev_queue_iso queue_iso; 3926e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter struct fw_cdev_start_iso start_iso; 3936e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter struct fw_cdev_stop_iso stop_iso; 3946e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter struct fw_cdev_get_cycle_timer get_cycle_timer; 3956e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter struct fw_cdev_allocate_iso_resource allocate_iso_resource; 3966e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter struct fw_cdev_send_stream_packet send_stream_packet; 3976e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter struct fw_cdev_get_cycle_timer2 get_cycle_timer2; 3986e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter}; 3996e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter 4006e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int ioctl_get_info(struct client *client, union ioctl_arg *arg) 40119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{ 4026e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter struct fw_cdev_get_info *a = &arg->get_info; 403344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg struct fw_cdev_event_bus_reset bus_reset; 404c9755e14a01987ada4063e8b4c50c2b6738d879eStefan Richter unsigned long ret = 0; 405344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg 4066e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter client->version = a->version; 407604f45167824e18ad5766e51ecf1d4d65f15118dStefan Richter a->version = FW_CDEV_KERNEL_VERSION; 4086e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter a->card = client->device->card->index; 409344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg 410c9755e14a01987ada4063e8b4c50c2b6738d879eStefan Richter down_read(&fw_device_rwsem); 411c9755e14a01987ada4063e8b4c50c2b6738d879eStefan Richter 4126e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter if (a->rom != 0) { 4136e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter size_t want = a->rom_length; 414d84702a5d7b500ead8db129ddea789c88764f357Stefan Richter size_t have = client->device->config_rom_length * 4; 415344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg 4166e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter ret = copy_to_user(u64_to_uptr(a->rom), 4176e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter client->device->config_rom, min(want, have)); 418344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg } 4196e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter a->rom_length = client->device->config_rom_length * 4; 420344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg 421c9755e14a01987ada4063e8b4c50c2b6738d879eStefan Richter up_read(&fw_device_rwsem); 422c9755e14a01987ada4063e8b4c50c2b6738d879eStefan Richter 423c9755e14a01987ada4063e8b4c50c2b6738d879eStefan Richter if (ret != 0) 424c9755e14a01987ada4063e8b4c50c2b6738d879eStefan Richter return -EFAULT; 425c9755e14a01987ada4063e8b4c50c2b6738d879eStefan Richter 4266e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter client->bus_reset_closure = a->bus_reset_closure; 4276e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter if (a->bus_reset != 0) { 428da8ecffaed434a12930f652898f9e86d1c2abc3eKristian Høgsberg fill_bus_reset_event(&bus_reset, client); 4296e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter if (copy_to_user(u64_to_uptr(a->bus_reset), 4306e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter &bus_reset, sizeof(bus_reset))) 431344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg return -EFAULT; 432344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg } 43319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 43419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg return 0; 43519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg} 43619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 43753dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic int add_client_resource(struct client *client, 43853dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter struct client_resource *resource, gfp_t gfp_mask) 4393964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg{ 4403964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg unsigned long flags; 44145ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason int ret; 44245ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason 44345ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason retry: 44445ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason if (idr_pre_get(&client->resource_idr, gfp_mask) == 0) 44545ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason return -ENOMEM; 4463964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg 4473964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg spin_lock_irqsave(&client->lock, flags); 44845ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason if (client->in_shutdown) 44945ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason ret = -ECANCELED; 45045ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason else 45145ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason ret = idr_get_new(&client->resource_idr, resource, 45245ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason &resource->handle); 453b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter if (ret >= 0) { 454fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter client_get(client); 4559fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter schedule_if_iso_resource(resource); 456b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter } 4573964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg spin_unlock_irqrestore(&client->lock, flags); 45845ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason 45945ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason if (ret == -EAGAIN) 46045ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason goto retry; 46145ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason 46245ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason return ret < 0 ? ret : 0; 4633964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg} 4643964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg 46553dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic int release_client_resource(struct client *client, u32 handle, 46653dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter client_resource_release_fn_t release, 467e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter struct client_resource **return_resource) 4683964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg{ 469e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter struct client_resource *resource; 4703964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg 4713ba949868a6dc082b24cba5c3bf3f50de7391433Stefan Richter spin_lock_irq(&client->lock); 47245ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason if (client->in_shutdown) 473e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter resource = NULL; 47445ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason else 475e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter resource = idr_find(&client->resource_idr, handle); 476e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter if (resource && resource->release == release) 47745ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason idr_remove(&client->resource_idr, handle); 4783ba949868a6dc082b24cba5c3bf3f50de7391433Stefan Richter spin_unlock_irq(&client->lock); 4793964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg 480e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter if (!(resource && resource->release == release)) 4813964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg return -EINVAL; 4823964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg 483e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter if (return_resource) 484e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter *return_resource = resource; 4853964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg else 486e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter resource->release(client, resource); 4873964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg 488fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter client_put(client); 489fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter 4903964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg return 0; 4913964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg} 4923964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg 49353dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic void release_transaction(struct client *client, 49453dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter struct client_resource *resource) 4953964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg{ 49697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct outbound_transaction_resource *r = container_of(resource, 49797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct outbound_transaction_resource, resource); 4983964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg 49997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter fw_cancel_transaction(client->device->card, &r->transaction); 5003964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg} 5013964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg 50253dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic void complete_transaction(struct fw_card *card, int rcode, 50353dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter void *payload, size_t length, void *data) 50419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{ 50597c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct outbound_transaction_event *e = data; 50697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct fw_cdev_event_response *rsp = &e->response; 50797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct client *client = e->client; 50828cf6a04c82857d562968dc3a8a89726e6ac3dcbKristian Høgsberg unsigned long flags; 50919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 51097c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter if (length < rsp->length) 51197c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter rsp->length = length; 51219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg if (rcode == RCODE_COMPLETE) 51397c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter memcpy(rsp->data, payload, rsp->length); 51419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 51528cf6a04c82857d562968dc3a8a89726e6ac3dcbKristian Høgsberg spin_lock_irqsave(&client->lock, flags); 51645ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason /* 517fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter * 1. If called while in shutdown, the idr tree must be left untouched. 518fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter * The idr handle will be removed and the client reference will be 519fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter * dropped later. 520fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter * 2. If the call chain was release_client_resource -> 521fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter * release_transaction -> complete_transaction (instead of a normal 522fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter * conclusion of the transaction), i.e. if this resource was already 523fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter * unregistered from the idr, the client reference will be dropped 524fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter * by release_client_resource and we must not drop it here. 52545ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason */ 526fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter if (!client->in_shutdown && 52797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter idr_find(&client->resource_idr, e->r.resource.handle)) { 52897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter idr_remove(&client->resource_idr, e->r.resource.handle); 529fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter /* Drop the idr's reference */ 530fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter client_put(client); 531fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter } 53228cf6a04c82857d562968dc3a8a89726e6ac3dcbKristian Høgsberg spin_unlock_irqrestore(&client->lock, flags); 53328cf6a04c82857d562968dc3a8a89726e6ac3dcbKristian Høgsberg 53497c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter rsp->type = FW_CDEV_EVENT_RESPONSE; 53597c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter rsp->rcode = rcode; 5368401d92ba46a1e859464cbd9c9ee304f6e361da3David Moore 5378401d92ba46a1e859464cbd9c9ee304f6e361da3David Moore /* 53897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter * In the case that sizeof(*rsp) doesn't align with the position of the 5398401d92ba46a1e859464cbd9c9ee304f6e361da3David Moore * data, and the read is short, preserve an extra copy of the data 5408401d92ba46a1e859464cbd9c9ee304f6e361da3David Moore * to stay compatible with a pre-2.6.27 bug. Since the bug is harmless 5418401d92ba46a1e859464cbd9c9ee304f6e361da3David Moore * for short reads and some apps depended on it, this is both safe 5428401d92ba46a1e859464cbd9c9ee304f6e361da3David Moore * and prudent for compatibility. 5438401d92ba46a1e859464cbd9c9ee304f6e361da3David Moore */ 54497c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter if (rsp->length <= sizeof(*rsp) - offsetof(typeof(*rsp), data)) 54597c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter queue_event(client, &e->event, rsp, sizeof(*rsp), 54697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter rsp->data, rsp->length); 5478401d92ba46a1e859464cbd9c9ee304f6e361da3David Moore else 54897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter queue_event(client, &e->event, rsp, sizeof(*rsp) + rsp->length, 5498401d92ba46a1e859464cbd9c9ee304f6e361da3David Moore NULL, 0); 550fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter 551fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter /* Drop the transaction callback's reference */ 552fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter client_put(client); 55319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg} 55419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 555acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richterstatic int init_request(struct client *client, 556acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter struct fw_cdev_send_request *request, 557acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter int destination_id, int speed) 55819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{ 55997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct outbound_transaction_event *e; 5601f3125af8ed7410cc0ebcc0acd59bbfc1ae0057aStefan Richter int ret; 56119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 56218e9b10fcdc090d3a38606958167d5923c7099b7Stefan Richter if (request->tcode != TCODE_STREAM_DATA && 56318e9b10fcdc090d3a38606958167d5923c7099b7Stefan Richter (request->length > 4096 || request->length > 512 << speed)) 5645d3fd692a7196a9045fb606f891f5987959b65a0Stefan Richter return -EIO; 56519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 56697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter e = kmalloc(sizeof(*e) + request->length, GFP_KERNEL); 56797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter if (e == NULL) 56819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg return -ENOMEM; 56919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 57097c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter e->client = client; 57197c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter e->response.length = request->length; 57297c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter e->response.closure = request->closure; 57319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 5744f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg if (request->data && 57597c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter copy_from_user(e->response.data, 5764f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg u64_to_uptr(request->data), request->length)) { 5771f3125af8ed7410cc0ebcc0acd59bbfc1ae0057aStefan Richter ret = -EFAULT; 57845ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason goto failed; 5791f3125af8ed7410cc0ebcc0acd59bbfc1ae0057aStefan Richter } 5801f3125af8ed7410cc0ebcc0acd59bbfc1ae0057aStefan Richter 58197c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter e->r.resource.release = release_transaction; 58297c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter ret = add_client_resource(client, &e->r.resource, GFP_KERNEL); 58345ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason if (ret < 0) 58445ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason goto failed; 58528cf6a04c82857d562968dc3a8a89726e6ac3dcbKristian Høgsberg 586fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter /* Get a reference for the transaction callback */ 587fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter client_get(client); 588fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter 589acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter fw_send_request(client->device->card, &e->r.transaction, 590664d8010b170ae8b3ce9268b4f4da934d27b0491Stefan Richter request->tcode, destination_id, request->generation, 591664d8010b170ae8b3ce9268b4f4da934d27b0491Stefan Richter speed, request->offset, e->response.data, 592664d8010b170ae8b3ce9268b4f4da934d27b0491Stefan Richter request->length, complete_transaction, e); 593664d8010b170ae8b3ce9268b4f4da934d27b0491Stefan Richter return 0; 59419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 59545ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason failed: 59697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter kfree(e); 5971f3125af8ed7410cc0ebcc0acd59bbfc1ae0057aStefan Richter 5981f3125af8ed7410cc0ebcc0acd59bbfc1ae0057aStefan Richter return ret; 59919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg} 60019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 6016e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int ioctl_send_request(struct client *client, union ioctl_arg *arg) 602acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter{ 6036e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter switch (arg->send_request.tcode) { 604acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter case TCODE_WRITE_QUADLET_REQUEST: 605acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter case TCODE_WRITE_BLOCK_REQUEST: 606acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter case TCODE_READ_QUADLET_REQUEST: 607acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter case TCODE_READ_BLOCK_REQUEST: 608acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter case TCODE_LOCK_MASK_SWAP: 609acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter case TCODE_LOCK_COMPARE_SWAP: 610acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter case TCODE_LOCK_FETCH_ADD: 611acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter case TCODE_LOCK_LITTLE_ADD: 612acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter case TCODE_LOCK_BOUNDED_ADD: 613acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter case TCODE_LOCK_WRAP_ADD: 614acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter case TCODE_LOCK_VENDOR_DEPENDENT: 615acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter break; 616acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter default: 617acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter return -EINVAL; 618acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter } 619acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter 6206e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter return init_request(client, &arg->send_request, client->device->node_id, 621acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter client->device->max_speed); 622acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter} 623acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter 624281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richterstatic inline bool is_fcp_request(struct fw_request *request) 625281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter{ 626281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter return request == NULL; 627281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter} 628281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter 62953dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic void release_request(struct client *client, 63053dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter struct client_resource *resource) 6313964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg{ 63297c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct inbound_transaction_resource *r = container_of(resource, 63397c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct inbound_transaction_resource, resource); 6343964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg 635281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter if (is_fcp_request(r->request)) 636281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter kfree(r->data); 637281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter else 63808bd34c98d631fe85744d4c920c80f48a1d95f54Jay Fenlason fw_send_response(r->card, r->request, RCODE_CONFLICT_ERROR); 6390244f57302f7e8bebd2f1ab58767eac2e9f678a6Stefan Richter 6400244f57302f7e8bebd2f1ab58767eac2e9f678a6Stefan Richter fw_card_put(r->card); 64197c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter kfree(r); 6423964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg} 6433964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg 64497c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richterstatic void handle_request(struct fw_card *card, struct fw_request *request, 64553dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter int tcode, int destination, int source, 64633e553fe2b4a983ef34a57ab1440d8d33397bb12Stefan Richter int generation, unsigned long long offset, 64753dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter void *payload, size_t length, void *callback_data) 64819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{ 64997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct address_handler_resource *handler = callback_data; 65097c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct inbound_transaction_resource *r; 65197c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct inbound_transaction_event *e; 652e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter size_t event_size0; 653281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter void *fcp_frame = NULL; 65445ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason int ret; 65519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 6560244f57302f7e8bebd2f1ab58767eac2e9f678a6Stefan Richter /* card may be different from handler->client->device->card */ 6570244f57302f7e8bebd2f1ab58767eac2e9f678a6Stefan Richter fw_card_get(card); 6580244f57302f7e8bebd2f1ab58767eac2e9f678a6Stefan Richter 65997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter r = kmalloc(sizeof(*r), GFP_ATOMIC); 6602d826cc5c791bdc5f5651324c485746be9492be0Kristian Høgsberg e = kmalloc(sizeof(*e), GFP_ATOMIC); 66197c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter if (r == NULL || e == NULL) 66245ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason goto failed; 66319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 66408bd34c98d631fe85744d4c920c80f48a1d95f54Jay Fenlason r->card = card; 66597c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter r->request = request; 66697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter r->data = payload; 66797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter r->length = length; 66819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 669281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter if (is_fcp_request(request)) { 670281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter /* 671281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter * FIXME: Let core-transaction.c manage a 672281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter * single reference-counted copy? 673281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter */ 674281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter fcp_frame = kmemdup(payload, length, GFP_ATOMIC); 675281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter if (fcp_frame == NULL) 676281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter goto failed; 677281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter 678281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter r->data = fcp_frame; 679281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter } 680281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter 68197c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter r->resource.release = release_request; 68297c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter ret = add_client_resource(handler->client, &r->resource, GFP_ATOMIC); 68345ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason if (ret < 0) 68445ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason goto failed; 68519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 686e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter if (handler->client->version < FW_CDEV_VERSION_EVENT_REQUEST2) { 687e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter struct fw_cdev_event_request *req = &e->req.request; 688e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter 689e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter if (tcode & 0x10) 690e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter tcode = TCODE_LOCK_REQUEST; 691e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter 692e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter req->type = FW_CDEV_EVENT_REQUEST; 693e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter req->tcode = tcode; 694e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter req->offset = offset; 695e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter req->length = length; 696e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter req->handle = r->resource.handle; 697e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter req->closure = handler->closure; 698e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter event_size0 = sizeof(*req); 699e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter } else { 700e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter struct fw_cdev_event_request2 *req = &e->req.request2; 701e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter 702e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter req->type = FW_CDEV_EVENT_REQUEST2; 703e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter req->tcode = tcode; 704e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter req->offset = offset; 705e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter req->source_node_id = source; 706e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter req->destination_node_id = destination; 707e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter req->card = card->index; 708e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter req->generation = generation; 709e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter req->length = length; 710e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter req->handle = r->resource.handle; 711e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter req->closure = handler->closure; 712e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter event_size0 = sizeof(*req); 713e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter } 71419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 71597c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter queue_event(handler->client, &e->event, 716e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter &e->req, event_size0, r->data, length); 71745ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason return; 71845ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason 71945ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason failed: 72097c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter kfree(r); 72145ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason kfree(e); 722281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter kfree(fcp_frame); 723281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter 724281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter if (!is_fcp_request(request)) 725db5d247ae811f49185a71e703b65acad845e4b18Clemens Ladisch fw_send_response(card, request, RCODE_CONFLICT_ERROR); 7260244f57302f7e8bebd2f1ab58767eac2e9f678a6Stefan Richter 7270244f57302f7e8bebd2f1ab58767eac2e9f678a6Stefan Richter fw_card_put(card); 72819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg} 72919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 73053dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic void release_address_handler(struct client *client, 73153dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter struct client_resource *resource) 7323964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg{ 73397c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct address_handler_resource *r = 73497c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter container_of(resource, struct address_handler_resource, resource); 7353964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg 73697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter fw_core_remove_address_handler(&r->handler); 73797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter kfree(r); 7383964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg} 7393964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg 7406e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int ioctl_allocate(struct client *client, union ioctl_arg *arg) 74119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{ 7426e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter struct fw_cdev_allocate *a = &arg->allocate; 74397c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct address_handler_resource *r; 74419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg struct fw_address_region region; 74545ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason int ret; 74619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 74797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter r = kmalloc(sizeof(*r), GFP_KERNEL); 74897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter if (r == NULL) 74919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg return -ENOMEM; 75019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 7516e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter region.start = a->offset; 7526e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter region.end = a->offset + a->length; 7536e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter r->handler.length = a->length; 75497c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter r->handler.address_callback = handle_request; 7556e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter r->handler.callback_data = r; 7566e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter r->closure = a->closure; 7576e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter r->client = client; 75819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 75997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter ret = fw_core_add_address_handler(&r->handler, ®ion); 7603e0b5f0d7cb5fef402517e41eebff5a0f0e65a13Stefan Richter if (ret < 0) { 76197c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter kfree(r); 7623e0b5f0d7cb5fef402517e41eebff5a0f0e65a13Stefan Richter return ret; 76319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg } 76419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 76597c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter r->resource.release = release_address_handler; 76697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter ret = add_client_resource(client, &r->resource, GFP_KERNEL); 76745ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason if (ret < 0) { 76897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter release_address_handler(client, &r->resource); 76945ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason return ret; 77045ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason } 7716e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter a->handle = r->resource.handle; 77219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 77319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg return 0; 77419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg} 77519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 7766e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int ioctl_deallocate(struct client *client, union ioctl_arg *arg) 7779472316b6eab3500ded544f6e86700c33541ef4eKristian Høgsberg{ 7786e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter return release_client_resource(client, arg->deallocate.handle, 77945ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason release_address_handler, NULL); 7809472316b6eab3500ded544f6e86700c33541ef4eKristian Høgsberg} 7819472316b6eab3500ded544f6e86700c33541ef4eKristian Høgsberg 7826e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int ioctl_send_response(struct client *client, union ioctl_arg *arg) 78319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{ 7846e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter struct fw_cdev_send_response *a = &arg->send_response; 7853964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg struct client_resource *resource; 78697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct inbound_transaction_resource *r; 7877e44c0b56b07a5e34de9943cfb2fee72e71a9f0eStefan Richter int ret = 0; 78819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 7896e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter if (release_client_resource(client, a->handle, 79045ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason release_request, &resource) < 0) 79119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg return -EINVAL; 79245ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason 79397c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter r = container_of(resource, struct inbound_transaction_resource, 79497c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter resource); 795281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter if (is_fcp_request(r->request)) 796281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter goto out; 797281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter 798a10c0ce76098857b899505d05de9f2e13ddf7a7aClemens Ladisch if (a->length != fw_get_response_length(r->request)) { 799a10c0ce76098857b899505d05de9f2e13ddf7a7aClemens Ladisch ret = -EINVAL; 800a10c0ce76098857b899505d05de9f2e13ddf7a7aClemens Ladisch kfree(r->request); 801a10c0ce76098857b899505d05de9f2e13ddf7a7aClemens Ladisch goto out; 802a10c0ce76098857b899505d05de9f2e13ddf7a7aClemens Ladisch } 803a10c0ce76098857b899505d05de9f2e13ddf7a7aClemens Ladisch if (copy_from_user(r->data, u64_to_uptr(a->data), a->length)) { 804281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter ret = -EFAULT; 805281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter kfree(r->request); 806281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter goto out; 8077e44c0b56b07a5e34de9943cfb2fee72e71a9f0eStefan Richter } 80808bd34c98d631fe85744d4c920c80f48a1d95f54Jay Fenlason fw_send_response(r->card, r->request, a->rcode); 8097e44c0b56b07a5e34de9943cfb2fee72e71a9f0eStefan Richter out: 8100244f57302f7e8bebd2f1ab58767eac2e9f678a6Stefan Richter fw_card_put(r->card); 81119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg kfree(r); 81219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 8137e44c0b56b07a5e34de9943cfb2fee72e71a9f0eStefan Richter return ret; 81419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg} 81519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 8166e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int ioctl_initiate_bus_reset(struct client *client, union ioctl_arg *arg) 8175371842b723dd04df57171f2c74660966901380cKristian Høgsberg{ 8186e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter return fw_core_initiate_bus_reset(client->device->card, 8196e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter arg->initiate_bus_reset.type == FW_CDEV_SHORT_RESET); 8205371842b723dd04df57171f2c74660966901380cKristian Høgsberg} 8215371842b723dd04df57171f2c74660966901380cKristian Høgsberg 8223964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsbergstatic void release_descriptor(struct client *client, 8233964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg struct client_resource *resource) 8243964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg{ 82597c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct descriptor_resource *r = 82697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter container_of(resource, struct descriptor_resource, resource); 8273964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg 82897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter fw_core_remove_descriptor(&r->descriptor); 82997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter kfree(r); 8303964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg} 8313964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg 8326e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int ioctl_add_descriptor(struct client *client, union ioctl_arg *arg) 83366dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg{ 8346e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter struct fw_cdev_add_descriptor *a = &arg->add_descriptor; 83597c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct descriptor_resource *r; 83645ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason int ret; 83766dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg 838de487da8ca5839d057e1f4b57ee3f387e180b800Stefan Richter /* Access policy: Allow this ioctl only on local nodes' device files. */ 83992368890d551794ee8d7e90477d8498bb7f82a9bStefan Richter if (!client->device->is_local) 840de487da8ca5839d057e1f4b57ee3f387e180b800Stefan Richter return -ENOSYS; 841de487da8ca5839d057e1f4b57ee3f387e180b800Stefan Richter 8426e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter if (a->length > 256) 84366dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg return -EINVAL; 84466dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg 8456e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter r = kmalloc(sizeof(*r) + a->length * 4, GFP_KERNEL); 84697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter if (r == NULL) 84766dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg return -ENOMEM; 84866dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg 8496e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter if (copy_from_user(r->data, u64_to_uptr(a->data), a->length * 4)) { 85045ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason ret = -EFAULT; 85145ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason goto failed; 85266dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg } 85366dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg 8546e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter r->descriptor.length = a->length; 8556e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter r->descriptor.immediate = a->immediate; 8566e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter r->descriptor.key = a->key; 85797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter r->descriptor.data = r->data; 85866dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg 85997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter ret = fw_core_add_descriptor(&r->descriptor); 86045ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason if (ret < 0) 86145ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason goto failed; 86266dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg 86397c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter r->resource.release = release_descriptor; 86497c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter ret = add_client_resource(client, &r->resource, GFP_KERNEL); 86545ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason if (ret < 0) { 86697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter fw_core_remove_descriptor(&r->descriptor); 86745ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason goto failed; 86845ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason } 8696e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter a->handle = r->resource.handle; 87066dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg 87166dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg return 0; 87245ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason failed: 87397c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter kfree(r); 87445ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason 87545ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason return ret; 87666dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg} 87766dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg 8786e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int ioctl_remove_descriptor(struct client *client, union ioctl_arg *arg) 87966dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg{ 8806e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter return release_client_resource(client, arg->remove_descriptor.handle, 88145ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason release_descriptor, NULL); 88266dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg} 88366dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg 88453dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic void iso_callback(struct fw_iso_context *context, u32 cycle, 88553dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter size_t header_length, void *header, void *data) 88619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{ 88719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg struct client *client = data; 88897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct iso_interrupt_event *e; 88919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 89056d04cb189f955e5167c27944d61aa57ad69b598Stefan Richter e = kmalloc(sizeof(*e) + header_length, GFP_ATOMIC); 89197c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter if (e == NULL) 89219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg return; 89319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 89497c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter e->interrupt.type = FW_CDEV_EVENT_ISO_INTERRUPT; 89597c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter e->interrupt.closure = client->iso_closure; 89697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter e->interrupt.cycle = cycle; 89797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter e->interrupt.header_length = header_length; 89897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter memcpy(e->interrupt.header, header, header_length); 89997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter queue_event(client, &e->event, &e->interrupt, 90097c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter sizeof(e->interrupt) + header_length, NULL, 0); 90119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg} 90219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 9036e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int ioctl_create_iso_context(struct client *client, union ioctl_arg *arg) 90419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{ 9056e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter struct fw_cdev_create_iso_context *a = &arg->create_iso_context; 90624315c5e6f508edd84e996d67daef3d1bcc72f8bKristian Høgsberg struct fw_iso_context *context; 90719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 9086e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter if (a->channel > 63) 90921efb3cfc6ed49991638000f58bb23b838c76e25Kristian Høgsberg return -EINVAL; 91021efb3cfc6ed49991638000f58bb23b838c76e25Kristian Høgsberg 9116e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter switch (a->type) { 912c70dc788fd8d3870b41231b6a53a64afb98cfd13Kristian Høgsberg case FW_ISO_CONTEXT_RECEIVE: 9136e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter if (a->header_size < 4 || (a->header_size & 3)) 914c70dc788fd8d3870b41231b6a53a64afb98cfd13Kristian Høgsberg return -EINVAL; 915c70dc788fd8d3870b41231b6a53a64afb98cfd13Kristian Høgsberg break; 916c70dc788fd8d3870b41231b6a53a64afb98cfd13Kristian Høgsberg 917c70dc788fd8d3870b41231b6a53a64afb98cfd13Kristian Høgsberg case FW_ISO_CONTEXT_TRANSMIT: 9186e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter if (a->speed > SCODE_3200) 919c70dc788fd8d3870b41231b6a53a64afb98cfd13Kristian Høgsberg return -EINVAL; 920c70dc788fd8d3870b41231b6a53a64afb98cfd13Kristian Høgsberg break; 921c70dc788fd8d3870b41231b6a53a64afb98cfd13Kristian Høgsberg 922c70dc788fd8d3870b41231b6a53a64afb98cfd13Kristian Høgsberg default: 92321efb3cfc6ed49991638000f58bb23b838c76e25Kristian Høgsberg return -EINVAL; 924c70dc788fd8d3870b41231b6a53a64afb98cfd13Kristian Høgsberg } 925c70dc788fd8d3870b41231b6a53a64afb98cfd13Kristian Høgsberg 9266e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter context = fw_iso_context_create(client->device->card, a->type, 9276e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter a->channel, a->speed, a->header_size, 9286e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter iso_callback, client); 92924315c5e6f508edd84e996d67daef3d1bcc72f8bKristian Høgsberg if (IS_ERR(context)) 93024315c5e6f508edd84e996d67daef3d1bcc72f8bKristian Høgsberg return PTR_ERR(context); 93124315c5e6f508edd84e996d67daef3d1bcc72f8bKristian Høgsberg 932bdfe273ee54b29498851fc8058516037d284270cClemens Ladisch /* We only support one context at this time. */ 933bdfe273ee54b29498851fc8058516037d284270cClemens Ladisch spin_lock_irq(&client->lock); 934bdfe273ee54b29498851fc8058516037d284270cClemens Ladisch if (client->iso_context != NULL) { 935bdfe273ee54b29498851fc8058516037d284270cClemens Ladisch spin_unlock_irq(&client->lock); 936bdfe273ee54b29498851fc8058516037d284270cClemens Ladisch fw_iso_context_destroy(context); 937bdfe273ee54b29498851fc8058516037d284270cClemens Ladisch return -EBUSY; 938bdfe273ee54b29498851fc8058516037d284270cClemens Ladisch } 9396e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter client->iso_closure = a->closure; 94024315c5e6f508edd84e996d67daef3d1bcc72f8bKristian Høgsberg client->iso_context = context; 941bdfe273ee54b29498851fc8058516037d284270cClemens Ladisch spin_unlock_irq(&client->lock); 94219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 9436e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter a->handle = 0; 944abaa5743e340c23922d92c9a5a6753ea3ae71e58Kristian Høgsberg 94519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg return 0; 94619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg} 94719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 9481ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg/* Macros for decoding the iso packet control header. */ 9491ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg#define GET_PAYLOAD_LENGTH(v) ((v) & 0xffff) 9501ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg#define GET_INTERRUPT(v) (((v) >> 16) & 0x01) 9511ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg#define GET_SKIP(v) (((v) >> 17) & 0x01) 9527a1003449c693f0d57443c8786bbf19717921ae0Stefan Richter#define GET_TAG(v) (((v) >> 18) & 0x03) 9537a1003449c693f0d57443c8786bbf19717921ae0Stefan Richter#define GET_SY(v) (((v) >> 20) & 0x0f) 9541ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg#define GET_HEADER_LENGTH(v) (((v) >> 24) & 0xff) 9551ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg 9566e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int ioctl_queue_iso(struct client *client, union ioctl_arg *arg) 95719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{ 9586e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter struct fw_cdev_queue_iso *a = &arg->queue_iso; 95919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg struct fw_cdev_iso_packet __user *p, *end, *next; 9609b32d5f3074e9b1afaa39a360a59fd77a2214783Kristian Høgsberg struct fw_iso_context *ctx = client->iso_context; 961ef370ee74b7a9cb769d50bfb73b4023ee3e37719Kristian Høgsberg unsigned long payload, buffer_end, header_length; 9621ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg u32 control; 96319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg int count; 96419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg struct { 96519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg struct fw_iso_packet packet; 96619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg u8 header[256]; 96719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg } u; 96819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 9696e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter if (ctx == NULL || a->handle != 0) 97019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg return -EINVAL; 97119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 972c781c06d119d04601727f2fbc30151e6760d536dKristian Høgsberg /* 973c781c06d119d04601727f2fbc30151e6760d536dKristian Høgsberg * If the user passes a non-NULL data pointer, has mmap()'ed 97419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg * the iso buffer, and the pointer points inside the buffer, 97519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg * we setup the payload pointers accordingly. Otherwise we 9769aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg * set them both to 0, which will still let packets with 97719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg * payload_length == 0 through. In other words, if no packets 97819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg * use the indirect payload, the iso buffer need not be mapped 9796e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter * and the a->data pointer is ignored. 980c781c06d119d04601727f2fbc30151e6760d536dKristian Høgsberg */ 98119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 9826e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter payload = (unsigned long)a->data - client->vm_start; 983ef370ee74b7a9cb769d50bfb73b4023ee3e37719Kristian Høgsberg buffer_end = client->buffer.page_count << PAGE_SHIFT; 9846e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter if (a->data == 0 || client->buffer.pages == NULL || 985ef370ee74b7a9cb769d50bfb73b4023ee3e37719Kristian Høgsberg payload >= buffer_end) { 9869aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg payload = 0; 987ef370ee74b7a9cb769d50bfb73b4023ee3e37719Kristian Høgsberg buffer_end = 0; 98819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg } 98919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 9906e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter p = (struct fw_cdev_iso_packet __user *)u64_to_uptr(a->packets); 9911ccc9147f6a063c42fef67acff34de18435a4a6bAl Viro 9926e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter if (!access_ok(VERIFY_READ, p, a->size)) 99319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg return -EFAULT; 99419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 9956e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter end = (void __user *)p + a->size; 99619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg count = 0; 99719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg while (p < end) { 9981ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg if (get_user(control, &p->control)) 99919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg return -EFAULT; 10001ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg u.packet.payload_length = GET_PAYLOAD_LENGTH(control); 10011ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg u.packet.interrupt = GET_INTERRUPT(control); 10021ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg u.packet.skip = GET_SKIP(control); 10031ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg u.packet.tag = GET_TAG(control); 10041ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg u.packet.sy = GET_SY(control); 10051ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg u.packet.header_length = GET_HEADER_LENGTH(control); 1006295e3feb92e5073ec32a3c626302d4b92c4c8a95Kristian Høgsberg 10079b32d5f3074e9b1afaa39a360a59fd77a2214783Kristian Høgsberg if (ctx->type == FW_ISO_CONTEXT_TRANSMIT) { 1008385ab5bcd4be586dffdba550b310308d89eade71Clemens Ladisch if (u.packet.header_length % 4 != 0) 1009385ab5bcd4be586dffdba550b310308d89eade71Clemens Ladisch return -EINVAL; 1010295e3feb92e5073ec32a3c626302d4b92c4c8a95Kristian Høgsberg header_length = u.packet.header_length; 1011295e3feb92e5073ec32a3c626302d4b92c4c8a95Kristian Høgsberg } else { 1012c781c06d119d04601727f2fbc30151e6760d536dKristian Høgsberg /* 1013c781c06d119d04601727f2fbc30151e6760d536dKristian Høgsberg * We require that header_length is a multiple of 1014c781c06d119d04601727f2fbc30151e6760d536dKristian Høgsberg * the fixed header size, ctx->header_size. 1015c781c06d119d04601727f2fbc30151e6760d536dKristian Høgsberg */ 10169b32d5f3074e9b1afaa39a360a59fd77a2214783Kristian Høgsberg if (ctx->header_size == 0) { 10179b32d5f3074e9b1afaa39a360a59fd77a2214783Kristian Høgsberg if (u.packet.header_length > 0) 10189b32d5f3074e9b1afaa39a360a59fd77a2214783Kristian Høgsberg return -EINVAL; 10194ba1d9c0c22947a9207029e7184733252e6135f1Clemens Ladisch } else if (u.packet.header_length == 0 || 10204ba1d9c0c22947a9207029e7184733252e6135f1Clemens Ladisch u.packet.header_length % ctx->header_size != 0) { 1021295e3feb92e5073ec32a3c626302d4b92c4c8a95Kristian Høgsberg return -EINVAL; 10229b32d5f3074e9b1afaa39a360a59fd77a2214783Kristian Høgsberg } 1023295e3feb92e5073ec32a3c626302d4b92c4c8a95Kristian Høgsberg header_length = 0; 1024295e3feb92e5073ec32a3c626302d4b92c4c8a95Kristian Høgsberg } 1025295e3feb92e5073ec32a3c626302d4b92c4c8a95Kristian Høgsberg 102619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg next = (struct fw_cdev_iso_packet __user *) 1027295e3feb92e5073ec32a3c626302d4b92c4c8a95Kristian Høgsberg &p->header[header_length / 4]; 102819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg if (next > end) 102919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg return -EINVAL; 103019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg if (__copy_from_user 1031295e3feb92e5073ec32a3c626302d4b92c4c8a95Kristian Høgsberg (u.packet.header, p->header, header_length)) 103219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg return -EFAULT; 103398b6cbe83b6e8db54638746c9040c7962d96b322Kristian Høgsberg if (u.packet.skip && ctx->type == FW_ISO_CONTEXT_TRANSMIT && 103419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg u.packet.header_length + u.packet.payload_length > 0) 103519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg return -EINVAL; 1036ef370ee74b7a9cb769d50bfb73b4023ee3e37719Kristian Høgsberg if (payload + u.packet.payload_length > buffer_end) 103719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg return -EINVAL; 103819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 10399b32d5f3074e9b1afaa39a360a59fd77a2214783Kristian Høgsberg if (fw_iso_context_queue(ctx, &u.packet, 10409b32d5f3074e9b1afaa39a360a59fd77a2214783Kristian Høgsberg &client->buffer, payload)) 104119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg break; 104219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 104319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg p = next; 104419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg payload += u.packet.payload_length; 104519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg count++; 104619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg } 104719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 10486e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter a->size -= uptr_to_u64(p) - a->packets; 10496e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter a->packets = uptr_to_u64(p); 10506e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter a->data = client->vm_start + payload; 105119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 105219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg return count; 105319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg} 105419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 10556e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int ioctl_start_iso(struct client *client, union ioctl_arg *arg) 105619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{ 10576e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter struct fw_cdev_start_iso *a = &arg->start_iso; 105819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 10596e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter if (client->iso_context == NULL || a->handle != 0) 1060abaa5743e340c23922d92c9a5a6753ea3ae71e58Kristian Høgsberg return -EINVAL; 1061fae603121428ba83b7343c88e68a7144525ab3ebStefan Richter 10626e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter if (client->iso_context->type == FW_ISO_CONTEXT_RECEIVE && 10636e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter (a->tags == 0 || a->tags > 15 || a->sync > 15)) 10646e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter return -EINVAL; 1065eb0306eac0aad0b7da18d8fbfb777f155b2c010dKristian Høgsberg 10666e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter return fw_iso_context_start(client->iso_context, 10676e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter a->cycle, a->sync, a->tags); 106819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg} 106919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 10706e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int ioctl_stop_iso(struct client *client, union ioctl_arg *arg) 1071b82956685aab4a9d333714300eb8a86fed6c9ab3Kristian Høgsberg{ 10726e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter struct fw_cdev_stop_iso *a = &arg->stop_iso; 1073abaa5743e340c23922d92c9a5a6753ea3ae71e58Kristian Høgsberg 10746e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter if (client->iso_context == NULL || a->handle != 0) 1075abaa5743e340c23922d92c9a5a6753ea3ae71e58Kristian Høgsberg return -EINVAL; 1076abaa5743e340c23922d92c9a5a6753ea3ae71e58Kristian Høgsberg 1077b82956685aab4a9d333714300eb8a86fed6c9ab3Kristian Høgsberg return fw_iso_context_stop(client->iso_context); 1078b82956685aab4a9d333714300eb8a86fed6c9ab3Kristian Høgsberg} 1079b82956685aab4a9d333714300eb8a86fed6c9ab3Kristian Høgsberg 10806e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int ioctl_get_cycle_timer2(struct client *client, union ioctl_arg *arg) 1081a64408b96b5f67c2778958a230b5cfa3408a4a81Stefan Richter{ 10826e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter struct fw_cdev_get_cycle_timer2 *a = &arg->get_cycle_timer2; 1083a64408b96b5f67c2778958a230b5cfa3408a4a81Stefan Richter struct fw_card *card = client->device->card; 1084abfe5a01ef1e463cbafdae461b693db34e308c02Stefan Richter struct timespec ts = {0, 0}; 10854a9bde9b8ab55a2bb51b57cad215a97bcf80bae2Stefan Richter u32 cycle_time; 1086abfe5a01ef1e463cbafdae461b693db34e308c02Stefan Richter int ret = 0; 1087a64408b96b5f67c2778958a230b5cfa3408a4a81Stefan Richter 10884a9bde9b8ab55a2bb51b57cad215a97bcf80bae2Stefan Richter local_irq_disable(); 1089a64408b96b5f67c2778958a230b5cfa3408a4a81Stefan Richter 10900fcff4e39323f466a47684d7c8ffa77e1be86c8aStefan Richter cycle_time = card->driver->read_csr(card, CSR_CYCLE_TIME); 1091abfe5a01ef1e463cbafdae461b693db34e308c02Stefan Richter 10926e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter switch (a->clk_id) { 1093abfe5a01ef1e463cbafdae461b693db34e308c02Stefan Richter case CLOCK_REALTIME: getnstimeofday(&ts); break; 1094abfe5a01ef1e463cbafdae461b693db34e308c02Stefan Richter case CLOCK_MONOTONIC: do_posix_clock_monotonic_gettime(&ts); break; 1095abfe5a01ef1e463cbafdae461b693db34e308c02Stefan Richter case CLOCK_MONOTONIC_RAW: getrawmonotonic(&ts); break; 1096abfe5a01ef1e463cbafdae461b693db34e308c02Stefan Richter default: 1097abfe5a01ef1e463cbafdae461b693db34e308c02Stefan Richter ret = -EINVAL; 1098abfe5a01ef1e463cbafdae461b693db34e308c02Stefan Richter } 1099a64408b96b5f67c2778958a230b5cfa3408a4a81Stefan Richter 11004a9bde9b8ab55a2bb51b57cad215a97bcf80bae2Stefan Richter local_irq_enable(); 1101a64408b96b5f67c2778958a230b5cfa3408a4a81Stefan Richter 11026e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter a->tv_sec = ts.tv_sec; 11036e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter a->tv_nsec = ts.tv_nsec; 11046e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter a->cycle_timer = cycle_time; 1105abfe5a01ef1e463cbafdae461b693db34e308c02Stefan Richter 1106abfe5a01ef1e463cbafdae461b693db34e308c02Stefan Richter return ret; 1107abfe5a01ef1e463cbafdae461b693db34e308c02Stefan Richter} 1108abfe5a01ef1e463cbafdae461b693db34e308c02Stefan Richter 11096e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int ioctl_get_cycle_timer(struct client *client, union ioctl_arg *arg) 1110abfe5a01ef1e463cbafdae461b693db34e308c02Stefan Richter{ 11116e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter struct fw_cdev_get_cycle_timer *a = &arg->get_cycle_timer; 1112abfe5a01ef1e463cbafdae461b693db34e308c02Stefan Richter struct fw_cdev_get_cycle_timer2 ct2; 1113abfe5a01ef1e463cbafdae461b693db34e308c02Stefan Richter 1114abfe5a01ef1e463cbafdae461b693db34e308c02Stefan Richter ct2.clk_id = CLOCK_REALTIME; 11156e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter ioctl_get_cycle_timer2(client, (union ioctl_arg *)&ct2); 1116abfe5a01ef1e463cbafdae461b693db34e308c02Stefan Richter 11176e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter a->local_time = ct2.tv_sec * USEC_PER_SEC + ct2.tv_nsec / NSEC_PER_USEC; 11186e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter a->cycle_timer = ct2.cycle_timer; 11194a9bde9b8ab55a2bb51b57cad215a97bcf80bae2Stefan Richter 1120a64408b96b5f67c2778958a230b5cfa3408a4a81Stefan Richter return 0; 1121a64408b96b5f67c2778958a230b5cfa3408a4a81Stefan Richter} 1122a64408b96b5f67c2778958a230b5cfa3408a4a81Stefan Richter 1123b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richterstatic void iso_resource_work(struct work_struct *work) 1124b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter{ 1125b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter struct iso_resource_event *e; 1126b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter struct iso_resource *r = 1127b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter container_of(work, struct iso_resource, work.work); 1128b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter struct client *client = r->client; 1129b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter int generation, channel, bandwidth, todo; 1130b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter bool skip, free, success; 1131b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 1132b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter spin_lock_irq(&client->lock); 1133b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter generation = client->device->generation; 1134b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter todo = r->todo; 1135b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter /* Allow 1000ms grace period for other reallocations. */ 1136b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter if (todo == ISO_RES_ALLOC && 1137b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter time_is_after_jiffies(client->device->card->reset_jiffies + HZ)) { 11389fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter schedule_iso_resource(r, DIV_ROUND_UP(HZ, 3)); 1139b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter skip = true; 1140b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter } else { 1141b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter /* We could be called twice within the same generation. */ 1142b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter skip = todo == ISO_RES_REALLOC && 1143b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter r->generation == generation; 1144b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter } 11451ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter free = todo == ISO_RES_DEALLOC || 11461ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter todo == ISO_RES_ALLOC_ONCE || 11471ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter todo == ISO_RES_DEALLOC_ONCE; 1148b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter r->generation = generation; 1149b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter spin_unlock_irq(&client->lock); 1150b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 1151b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter if (skip) 1152b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter goto out; 1153b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 1154b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter bandwidth = r->bandwidth; 1155b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 1156b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter fw_iso_resource_manage(client->device->card, generation, 1157b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter r->channels, &channel, &bandwidth, 11581ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter todo == ISO_RES_ALLOC || 11591ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter todo == ISO_RES_REALLOC || 11606fdc03709433ccc2005f0f593ae9d9dd04f7b485Stefan Richter todo == ISO_RES_ALLOC_ONCE, 11616fdc03709433ccc2005f0f593ae9d9dd04f7b485Stefan Richter r->transaction_data); 1162b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter /* 1163b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter * Is this generation outdated already? As long as this resource sticks 1164b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter * in the idr, it will be scheduled again for a newer generation or at 1165b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter * shutdown. 1166b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter */ 1167b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter if (channel == -EAGAIN && 1168b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter (todo == ISO_RES_ALLOC || todo == ISO_RES_REALLOC)) 1169b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter goto out; 1170b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 1171b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter success = channel >= 0 || bandwidth > 0; 1172b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 1173b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter spin_lock_irq(&client->lock); 1174b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter /* 1175b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter * Transit from allocation to reallocation, except if the client 1176b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter * requested deallocation in the meantime. 1177b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter */ 1178b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter if (r->todo == ISO_RES_ALLOC) 1179b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter r->todo = ISO_RES_REALLOC; 1180b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter /* 1181b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter * Allocation or reallocation failure? Pull this resource out of the 1182b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter * idr and prepare for deletion, unless the client is shutting down. 1183b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter */ 1184b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter if (r->todo == ISO_RES_REALLOC && !success && 1185b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter !client->in_shutdown && 1186b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter idr_find(&client->resource_idr, r->resource.handle)) { 1187b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter idr_remove(&client->resource_idr, r->resource.handle); 1188b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter client_put(client); 1189b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter free = true; 1190b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter } 1191b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter spin_unlock_irq(&client->lock); 1192b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 1193b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter if (todo == ISO_RES_ALLOC && channel >= 0) 11945d9cb7d276a9c465fef5a771792eac2cf1929f2bStefan Richter r->channels = 1ULL << channel; 1195b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 1196b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter if (todo == ISO_RES_REALLOC && success) 1197b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter goto out; 1198b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 11991ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter if (todo == ISO_RES_ALLOC || todo == ISO_RES_ALLOC_ONCE) { 1200b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter e = r->e_alloc; 1201b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter r->e_alloc = NULL; 1202b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter } else { 1203b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter e = r->e_dealloc; 1204b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter r->e_dealloc = NULL; 1205b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter } 1206e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter e->iso_resource.handle = r->resource.handle; 1207e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter e->iso_resource.channel = channel; 1208e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter e->iso_resource.bandwidth = bandwidth; 1209b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 1210b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter queue_event(client, &e->event, 1211e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter &e->iso_resource, sizeof(e->iso_resource), NULL, 0); 1212b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 1213b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter if (free) { 1214b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter cancel_delayed_work(&r->work); 1215b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter kfree(r->e_alloc); 1216b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter kfree(r->e_dealloc); 1217b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter kfree(r); 1218b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter } 1219b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter out: 1220b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter client_put(client); 1221b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter} 1222b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 1223b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richterstatic void release_iso_resource(struct client *client, 1224b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter struct client_resource *resource) 1225b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter{ 1226b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter struct iso_resource *r = 1227b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter container_of(resource, struct iso_resource, resource); 1228b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 1229b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter spin_lock_irq(&client->lock); 1230b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter r->todo = ISO_RES_DEALLOC; 12319fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter schedule_iso_resource(r, 0); 1232b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter spin_unlock_irq(&client->lock); 1233b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter} 1234b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 12351ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richterstatic int init_iso_resource(struct client *client, 12361ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter struct fw_cdev_allocate_iso_resource *request, int todo) 1237b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter{ 1238b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter struct iso_resource_event *e1, *e2; 1239b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter struct iso_resource *r; 1240b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter int ret; 1241b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 1242b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter if ((request->channels == 0 && request->bandwidth == 0) || 1243b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter request->bandwidth > BANDWIDTH_AVAILABLE_INITIAL || 1244b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter request->bandwidth < 0) 1245b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter return -EINVAL; 1246b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 1247b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter r = kmalloc(sizeof(*r), GFP_KERNEL); 1248b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter e1 = kmalloc(sizeof(*e1), GFP_KERNEL); 1249b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter e2 = kmalloc(sizeof(*e2), GFP_KERNEL); 1250b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter if (r == NULL || e1 == NULL || e2 == NULL) { 1251b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter ret = -ENOMEM; 1252b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter goto fail; 1253b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter } 1254b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 1255b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter INIT_DELAYED_WORK(&r->work, iso_resource_work); 1256b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter r->client = client; 12571ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter r->todo = todo; 1258b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter r->generation = -1; 1259b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter r->channels = request->channels; 1260b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter r->bandwidth = request->bandwidth; 1261b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter r->e_alloc = e1; 1262b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter r->e_dealloc = e2; 1263b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 1264e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter e1->iso_resource.closure = request->closure; 1265e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter e1->iso_resource.type = FW_CDEV_EVENT_ISO_RESOURCE_ALLOCATED; 1266e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter e2->iso_resource.closure = request->closure; 1267e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter e2->iso_resource.type = FW_CDEV_EVENT_ISO_RESOURCE_DEALLOCATED; 1268b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 12691ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter if (todo == ISO_RES_ALLOC) { 12701ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter r->resource.release = release_iso_resource; 12711ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter ret = add_client_resource(client, &r->resource, GFP_KERNEL); 127281610b8fbfc027a67707ff567d490819a3d55844Stefan Richter if (ret < 0) 127381610b8fbfc027a67707ff567d490819a3d55844Stefan Richter goto fail; 12741ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter } else { 12751ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter r->resource.release = NULL; 12761ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter r->resource.handle = -1; 12779fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter schedule_iso_resource(r, 0); 12781ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter } 1279b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter request->handle = r->resource.handle; 1280b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 1281b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter return 0; 1282b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter fail: 1283b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter kfree(r); 1284b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter kfree(e1); 1285b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter kfree(e2); 1286b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 1287b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter return ret; 1288b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter} 1289b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 12906e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int ioctl_allocate_iso_resource(struct client *client, 12916e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter union ioctl_arg *arg) 12921ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter{ 12936e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter return init_iso_resource(client, 12946e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter &arg->allocate_iso_resource, ISO_RES_ALLOC); 12951ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter} 12961ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter 12976e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int ioctl_deallocate_iso_resource(struct client *client, 12986e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter union ioctl_arg *arg) 1299b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter{ 13006e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter return release_client_resource(client, 13016e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter arg->deallocate.handle, release_iso_resource, NULL); 1302b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter} 1303b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 13046e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int ioctl_allocate_iso_resource_once(struct client *client, 13056e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter union ioctl_arg *arg) 13061ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter{ 13076e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter return init_iso_resource(client, 13086e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter &arg->allocate_iso_resource, ISO_RES_ALLOC_ONCE); 13091ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter} 13101ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter 13116e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int ioctl_deallocate_iso_resource_once(struct client *client, 13126e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter union ioctl_arg *arg) 13131ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter{ 13146e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter return init_iso_resource(client, 13156e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter &arg->allocate_iso_resource, ISO_RES_DEALLOC_ONCE); 13161ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter} 13171ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter 1318c8a25900f35e575938c791507894c036c0f2ca7dStefan Richter/* 1319c8a25900f35e575938c791507894c036c0f2ca7dStefan Richter * Returns a speed code: Maximum speed to or from this device, 1320c8a25900f35e575938c791507894c036c0f2ca7dStefan Richter * limited by the device's link speed, the local node's link speed, 1321c8a25900f35e575938c791507894c036c0f2ca7dStefan Richter * and all PHY port speeds between the two links. 1322c8a25900f35e575938c791507894c036c0f2ca7dStefan Richter */ 13236e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int ioctl_get_speed(struct client *client, union ioctl_arg *arg) 132433580a3ef5ba3bc0ee1b520df82a24bb37ce28f0Stefan Richter{ 1325c8a25900f35e575938c791507894c036c0f2ca7dStefan Richter return client->device->max_speed; 132633580a3ef5ba3bc0ee1b520df82a24bb37ce28f0Stefan Richter} 132733580a3ef5ba3bc0ee1b520df82a24bb37ce28f0Stefan Richter 13286e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int ioctl_send_broadcast_request(struct client *client, 13296e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter union ioctl_arg *arg) 1330acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter{ 13316e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter struct fw_cdev_send_request *a = &arg->send_request; 1332acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter 13336e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter switch (a->tcode) { 1334acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter case TCODE_WRITE_QUADLET_REQUEST: 1335acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter case TCODE_WRITE_BLOCK_REQUEST: 1336acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter break; 1337acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter default: 1338acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter return -EINVAL; 1339acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter } 1340acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter 13411566f3dc3e5986a16c7bbb3bb95bb691251a8d25Stefan Richter /* Security policy: Only allow accesses to Units Space. */ 13426e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter if (a->offset < CSR_REGISTER_BASE + CSR_CONFIG_ROM_END) 13431566f3dc3e5986a16c7bbb3bb95bb691251a8d25Stefan Richter return -EACCES; 13441566f3dc3e5986a16c7bbb3bb95bb691251a8d25Stefan Richter 13456e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter return init_request(client, a, LOCAL_BUS | 0x3f, SCODE_100); 1346acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter} 1347acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter 13486e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int ioctl_send_stream_packet(struct client *client, union ioctl_arg *arg) 1349f8c2287c65f8f72000102fc058232669e4540bc4Jay Fenlason{ 13506e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter struct fw_cdev_send_stream_packet *a = &arg->send_stream_packet; 135118e9b10fcdc090d3a38606958167d5923c7099b7Stefan Richter struct fw_cdev_send_request request; 135218e9b10fcdc090d3a38606958167d5923c7099b7Stefan Richter int dest; 1353f8c2287c65f8f72000102fc058232669e4540bc4Jay Fenlason 13546e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter if (a->speed > client->device->card->link_speed || 13556e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter a->length > 1024 << a->speed) 135618e9b10fcdc090d3a38606958167d5923c7099b7Stefan Richter return -EIO; 1357f8c2287c65f8f72000102fc058232669e4540bc4Jay Fenlason 13586e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter if (a->tag > 3 || a->channel > 63 || a->sy > 15) 135918e9b10fcdc090d3a38606958167d5923c7099b7Stefan Richter return -EINVAL; 136018e9b10fcdc090d3a38606958167d5923c7099b7Stefan Richter 13616e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter dest = fw_stream_packet_destination_id(a->tag, a->channel, a->sy); 136218e9b10fcdc090d3a38606958167d5923c7099b7Stefan Richter request.tcode = TCODE_STREAM_DATA; 13636e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter request.length = a->length; 13646e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter request.closure = a->closure; 13656e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter request.data = a->data; 13666e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter request.generation = a->generation; 136718e9b10fcdc090d3a38606958167d5923c7099b7Stefan Richter 13686e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter return init_request(client, &request, dest, a->speed); 1369f8c2287c65f8f72000102fc058232669e4540bc4Jay Fenlason} 1370f8c2287c65f8f72000102fc058232669e4540bc4Jay Fenlason 13716e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int (* const ioctl_handlers[])(struct client *, union ioctl_arg *) = { 13724f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg ioctl_get_info, 13734f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg ioctl_send_request, 13744f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg ioctl_allocate, 13754f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg ioctl_deallocate, 13764f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg ioctl_send_response, 13774f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg ioctl_initiate_bus_reset, 13784f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg ioctl_add_descriptor, 13794f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg ioctl_remove_descriptor, 13804f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg ioctl_create_iso_context, 13814f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg ioctl_queue_iso, 13824f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg ioctl_start_iso, 13834f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg ioctl_stop_iso, 1384a64408b96b5f67c2778958a230b5cfa3408a4a81Stefan Richter ioctl_get_cycle_timer, 1385b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter ioctl_allocate_iso_resource, 1386b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter ioctl_deallocate_iso_resource, 13871ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter ioctl_allocate_iso_resource_once, 13881ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter ioctl_deallocate_iso_resource_once, 138933580a3ef5ba3bc0ee1b520df82a24bb37ce28f0Stefan Richter ioctl_get_speed, 1390acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter ioctl_send_broadcast_request, 1391f8c2287c65f8f72000102fc058232669e4540bc4Jay Fenlason ioctl_send_stream_packet, 1392abfe5a01ef1e463cbafdae461b693db34e308c02Stefan Richter ioctl_get_cycle_timer2, 13934f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg}; 13944f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg 139553dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic int dispatch_ioctl(struct client *client, 139653dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter unsigned int cmd, void __user *arg) 139719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{ 13986e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter union ioctl_arg buffer; 13992dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter int ret; 14004f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg 140164582298b9c29535188380f488873e7d2196a2ebStefan Richter if (fw_device_is_shutdown(client->device)) 140264582298b9c29535188380f488873e7d2196a2ebStefan Richter return -ENODEV; 140364582298b9c29535188380f488873e7d2196a2ebStefan Richter 14044f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg if (_IOC_TYPE(cmd) != '#' || 14059cac00b8f0079d5d3d54ec4dae453d58dec30e7cStefan Richter _IOC_NR(cmd) >= ARRAY_SIZE(ioctl_handlers) || 14069cac00b8f0079d5d3d54ec4dae453d58dec30e7cStefan Richter _IOC_SIZE(cmd) > sizeof(buffer)) 140719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg return -EINVAL; 14084f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg 14099cac00b8f0079d5d3d54ec4dae453d58dec30e7cStefan Richter if (_IOC_DIR(cmd) == _IOC_READ) 14109cac00b8f0079d5d3d54ec4dae453d58dec30e7cStefan Richter memset(&buffer, 0, _IOC_SIZE(cmd)); 14119cac00b8f0079d5d3d54ec4dae453d58dec30e7cStefan Richter 14129cac00b8f0079d5d3d54ec4dae453d58dec30e7cStefan Richter if (_IOC_DIR(cmd) & _IOC_WRITE) 14139cac00b8f0079d5d3d54ec4dae453d58dec30e7cStefan Richter if (copy_from_user(&buffer, arg, _IOC_SIZE(cmd))) 14144f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg return -EFAULT; 14154f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg 14166e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter ret = ioctl_handlers[_IOC_NR(cmd)](client, &buffer); 14172dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter if (ret < 0) 14182dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter return ret; 14194f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg 14209cac00b8f0079d5d3d54ec4dae453d58dec30e7cStefan Richter if (_IOC_DIR(cmd) & _IOC_READ) 14219cac00b8f0079d5d3d54ec4dae453d58dec30e7cStefan Richter if (copy_to_user(arg, &buffer, _IOC_SIZE(cmd))) 14224f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg return -EFAULT; 14234f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg 14242dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter return ret; 142519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg} 142619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 142753dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic long fw_device_op_ioctl(struct file *file, 142853dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter unsigned int cmd, unsigned long arg) 142919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{ 143064582298b9c29535188380f488873e7d2196a2ebStefan Richter return dispatch_ioctl(file->private_data, cmd, (void __user *)arg); 143119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg} 143219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 143319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg#ifdef CONFIG_COMPAT 143453dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic long fw_device_op_compat_ioctl(struct file *file, 143553dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter unsigned int cmd, unsigned long arg) 143619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{ 143764582298b9c29535188380f488873e7d2196a2ebStefan Richter return dispatch_ioctl(file->private_data, cmd, compat_ptr(arg)); 143819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg} 143919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg#endif 144019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 144119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsbergstatic int fw_device_op_mmap(struct file *file, struct vm_area_struct *vma) 144219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{ 144319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg struct client *client = file->private_data; 14449aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg enum dma_data_direction direction; 14459aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg unsigned long size; 14462dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter int page_count, ret; 14479aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg 1448551f4cb9de716ffcdaf968c99a450c22ff12e8c3Jay Fenlason if (fw_device_is_shutdown(client->device)) 1449551f4cb9de716ffcdaf968c99a450c22ff12e8c3Jay Fenlason return -ENODEV; 1450551f4cb9de716ffcdaf968c99a450c22ff12e8c3Jay Fenlason 14519aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg /* FIXME: We could support multiple buffers, but we don't. */ 14529aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg if (client->buffer.pages != NULL) 14539aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg return -EBUSY; 14549aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg 14559aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg if (!(vma->vm_flags & VM_SHARED)) 14569aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg return -EINVAL; 145719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 14589aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg if (vma->vm_start & ~PAGE_MASK) 145919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg return -EINVAL; 146019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 146119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg client->vm_start = vma->vm_start; 14629aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg size = vma->vm_end - vma->vm_start; 14639aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg page_count = size >> PAGE_SHIFT; 14649aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg if (size & ~PAGE_MASK) 14659aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg return -EINVAL; 14669aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg 14679aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg if (vma->vm_flags & VM_WRITE) 14689aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg direction = DMA_TO_DEVICE; 14699aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg else 14709aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg direction = DMA_FROM_DEVICE; 14719aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg 14722dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter ret = fw_iso_buffer_init(&client->buffer, client->device->card, 14732dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter page_count, direction); 14742dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter if (ret < 0) 14752dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter return ret; 147619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 14772dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter ret = fw_iso_buffer_map(&client->buffer, vma); 14782dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter if (ret < 0) 14799aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg fw_iso_buffer_destroy(&client->buffer, client->device->card); 14809aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg 14812dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter return ret; 148219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg} 148319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 148445ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlasonstatic int shutdown_resource(int id, void *p, void *data) 148545ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason{ 1486e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter struct client_resource *resource = p; 148745ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason struct client *client = data; 148845ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason 1489e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter resource->release(client, resource); 1490fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter client_put(client); 149145ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason 149245ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason return 0; 149345ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason} 149445ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason 149519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsbergstatic int fw_device_op_release(struct inode *inode, struct file *file) 149619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{ 149719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg struct client *client = file->private_data; 1498e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter struct event *event, *next_event; 149919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 150097811e347310766030a648fdf0e407b2c91a39c1Stefan Richter mutex_lock(&client->device->client_list_mutex); 150197811e347310766030a648fdf0e407b2c91a39c1Stefan Richter list_del(&client->link); 150297811e347310766030a648fdf0e407b2c91a39c1Stefan Richter mutex_unlock(&client->device->client_list_mutex); 150397811e347310766030a648fdf0e407b2c91a39c1Stefan Richter 150419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg if (client->iso_context) 150519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg fw_iso_context_destroy(client->iso_context); 150619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 150736a755cfc398fc50abc74055d4478c1b067dac55Stefan Richter if (client->buffer.pages) 150836a755cfc398fc50abc74055d4478c1b067dac55Stefan Richter fw_iso_buffer_destroy(&client->buffer, client->device->card); 150936a755cfc398fc50abc74055d4478c1b067dac55Stefan Richter 151045ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason /* Freeze client->resource_idr and client->event_list */ 15113ba949868a6dc082b24cba5c3bf3f50de7391433Stefan Richter spin_lock_irq(&client->lock); 151245ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason client->in_shutdown = true; 15133ba949868a6dc082b24cba5c3bf3f50de7391433Stefan Richter spin_unlock_irq(&client->lock); 151466dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg 151545ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason idr_for_each(&client->resource_idr, shutdown_resource, client); 151645ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason idr_remove_all(&client->resource_idr); 151745ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason idr_destroy(&client->resource_idr); 151828cf6a04c82857d562968dc3a8a89726e6ac3dcbKristian Høgsberg 1519e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter list_for_each_entry_safe(event, next_event, &client->event_list, link) 1520e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter kfree(event); 152119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 1522fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter client_put(client); 152319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 152419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg return 0; 152519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg} 152619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 152719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsbergstatic unsigned int fw_device_op_poll(struct file *file, poll_table * pt) 152819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{ 152919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg struct client *client = file->private_data; 15302603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg unsigned int mask = 0; 153119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 153219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg poll_wait(file, &client->wait, pt); 153319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 15342603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg if (fw_device_is_shutdown(client->device)) 15352603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg mask |= POLLHUP | POLLERR; 153619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg if (!list_empty(&client->event_list)) 15372603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg mask |= POLLIN | POLLRDNORM; 15382603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg 15392603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg return mask; 154019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg} 154119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 154221ebcd1224d05c8673053e1e93ab9ec7ef3e0b84Stefan Richterconst struct file_operations fw_device_ops = { 154319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg .owner = THIS_MODULE, 15443ac26b2ee30005930117fe6a180c139c5f300fafStefan Richter .llseek = no_llseek, 154519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg .open = fw_device_op_open, 154619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg .read = fw_device_op_read, 154719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg .unlocked_ioctl = fw_device_op_ioctl, 154819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg .mmap = fw_device_op_mmap, 15493ac26b2ee30005930117fe6a180c139c5f300fafStefan Richter .release = fw_device_op_release, 15503ac26b2ee30005930117fe6a180c139c5f300fafStefan Richter .poll = fw_device_op_poll, 155119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg#ifdef CONFIG_COMPAT 15525af4e5eab30d481f76b89a2167c873dfad960acbStefan Richter .compat_ioctl = fw_device_op_compat_ioctl, 155319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg#endif 155419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}; 1555