core-cdev.c revision 168cf9af699e87d5a6f44b684583714ecabb8e71
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> 38be5bbd6756b44602a3f281af05c2f416fa9bd1c6Stefan Richter#include <linux/time.h> 39e034d242593f12533c11742ce38c245a33e57dc7Stefan Richter#include <linux/uaccess.h> 40be5bbd6756b44602a3f281af05c2f416fa9bd1c6Stefan Richter#include <linux/vmalloc.h> 41be5bbd6756b44602a3f281af05c2f416fa9bd1c6Stefan Richter#include <linux/wait.h> 42b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter#include <linux/workqueue.h> 43be5bbd6756b44602a3f281af05c2f416fa9bd1c6Stefan Richter 44a64408b96b5f67c2778958a230b5cfa3408a4a81Stefan Richter#include <asm/system.h> 45be5bbd6756b44602a3f281af05c2f416fa9bd1c6Stefan Richter 4677c9a5daa9c4d9b37812c9c69c7bcbb3f9399c3cStefan Richter#include "core.h" 4719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 4819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsbergstruct client { 49344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg u32 version; 5019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg struct fw_device *device; 5145ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason 5219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg spinlock_t lock; 5345ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason bool in_shutdown; 5445ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason struct idr resource_idr; 5519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg struct list_head event_list; 5619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg wait_queue_head_t wait; 57da8ecffaed434a12930f652898f9e86d1c2abc3eKristian Høgsberg u64 bus_reset_closure; 589aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg 5919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg struct fw_iso_context *iso_context; 60abaa5743e340c23922d92c9a5a6753ea3ae71e58Kristian Høgsberg u64 iso_closure; 619aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg struct fw_iso_buffer buffer; 629aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg unsigned long vm_start; 6397bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg 6497bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg struct list_head link; 65fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter struct kref kref; 6619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}; 6719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 68fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richterstatic inline void client_get(struct client *client) 69fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter{ 70fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter kref_get(&client->kref); 71fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter} 72fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter 73fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richterstatic void client_release(struct kref *kref) 74fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter{ 75fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter struct client *client = container_of(kref, struct client, kref); 76fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter 77fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter fw_device_put(client->device); 78fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter kfree(client); 79fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter} 80fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter 81fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richterstatic void client_put(struct client *client) 82fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter{ 83fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter kref_put(&client->kref, client_release); 84fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter} 85fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter 8697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richterstruct client_resource; 8797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richtertypedef void (*client_resource_release_fn_t)(struct client *, 8897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct client_resource *); 8997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richterstruct client_resource { 9097c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter client_resource_release_fn_t release; 9197c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter int handle; 9297c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter}; 9397c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter 9497c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richterstruct address_handler_resource { 9597c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct client_resource resource; 9697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct fw_address_handler handler; 9797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter __u64 closure; 9897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct client *client; 9997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter}; 10097c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter 10197c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richterstruct outbound_transaction_resource { 10297c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct client_resource resource; 10397c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct fw_transaction transaction; 10497c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter}; 10597c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter 10697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richterstruct inbound_transaction_resource { 10797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct client_resource resource; 10897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct fw_request *request; 10997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter void *data; 11097c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter size_t length; 11197c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter}; 11297c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter 11397c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richterstruct descriptor_resource { 11497c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct client_resource resource; 11597c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct fw_descriptor descriptor; 11697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter u32 data[0]; 11797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter}; 11897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter 119b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richterstruct iso_resource { 120b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter struct client_resource resource; 121b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter struct client *client; 122b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter /* Schedule work and access todo only with client->lock held. */ 123b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter struct delayed_work work; 1241ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter enum {ISO_RES_ALLOC, ISO_RES_REALLOC, ISO_RES_DEALLOC, 1251ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter ISO_RES_ALLOC_ONCE, ISO_RES_DEALLOC_ONCE,} todo; 126b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter int generation; 127b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter u64 channels; 128b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter s32 bandwidth; 1296fdc03709433ccc2005f0f593ae9d9dd04f7b485Stefan Richter __be32 transaction_data[2]; 130b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter struct iso_resource_event *e_alloc, *e_dealloc; 131b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter}; 132b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 133b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richterstatic void release_iso_resource(struct client *, struct client_resource *); 134b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 1359fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richterstatic void schedule_iso_resource(struct iso_resource *r, unsigned long delay) 1369fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter{ 1379fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter client_get(r->client); 1389fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter if (!schedule_delayed_work(&r->work, delay)) 1399fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter client_put(r->client); 1409fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter} 1419fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter 1429fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richterstatic void schedule_if_iso_resource(struct client_resource *resource) 1439fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter{ 1449fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter if (resource->release == release_iso_resource) 1459fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter schedule_iso_resource(container_of(resource, 1469fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter struct iso_resource, resource), 0); 1479fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter} 1489fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter 14997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter/* 15097c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter * dequeue_event() just kfree()'s the event, so the event has to be 15197c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter * the first field in a struct XYZ_event. 15297c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter */ 15397c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richterstruct event { 15497c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct { void *data; size_t size; } v[2]; 15597c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct list_head link; 15697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter}; 15797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter 15897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richterstruct bus_reset_event { 15997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct event event; 16097c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct fw_cdev_event_bus_reset reset; 16197c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter}; 16297c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter 16397c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richterstruct outbound_transaction_event { 16497c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct event event; 16597c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct client *client; 16697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct outbound_transaction_resource r; 16797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct fw_cdev_event_response response; 16897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter}; 16997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter 17097c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richterstruct inbound_transaction_event { 17197c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct event event; 17297c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct fw_cdev_event_request request; 17397c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter}; 17497c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter 17597c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richterstruct iso_interrupt_event { 17697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct event event; 17797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct fw_cdev_event_iso_interrupt interrupt; 17897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter}; 17997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter 180b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richterstruct iso_resource_event { 181b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter struct event event; 182e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter struct fw_cdev_event_iso_resource iso_resource; 183b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter}; 184b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 18553dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic inline void __user *u64_to_uptr(__u64 value) 18619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{ 18719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg return (void __user *)(unsigned long)value; 18819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg} 18919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 19053dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic inline __u64 uptr_to_u64(void __user *ptr) 19119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{ 19219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg return (__u64)(unsigned long)ptr; 19319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg} 19419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 19519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsbergstatic int fw_device_op_open(struct inode *inode, struct file *file) 19619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{ 19719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg struct fw_device *device; 19819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg struct client *client; 19919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 20096b19062e741b715cf399312c30e0672d8889569Stefan Richter device = fw_device_get_by_devt(inode->i_rdev); 201a3aca3dabbcf00f2088d472f27755c29acaa992eKristian Høgsberg if (device == NULL) 202a3aca3dabbcf00f2088d472f27755c29acaa992eKristian Høgsberg return -ENODEV; 20319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 204551f4cb9de716ffcdaf968c99a450c22ff12e8c3Jay Fenlason if (fw_device_is_shutdown(device)) { 205551f4cb9de716ffcdaf968c99a450c22ff12e8c3Jay Fenlason fw_device_put(device); 206551f4cb9de716ffcdaf968c99a450c22ff12e8c3Jay Fenlason return -ENODEV; 207551f4cb9de716ffcdaf968c99a450c22ff12e8c3Jay Fenlason } 208551f4cb9de716ffcdaf968c99a450c22ff12e8c3Jay Fenlason 2092d826cc5c791bdc5f5651324c485746be9492be0Kristian Høgsberg client = kzalloc(sizeof(*client), GFP_KERNEL); 21096b19062e741b715cf399312c30e0672d8889569Stefan Richter if (client == NULL) { 21196b19062e741b715cf399312c30e0672d8889569Stefan Richter fw_device_put(device); 21219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg return -ENOMEM; 21396b19062e741b715cf399312c30e0672d8889569Stefan Richter } 21419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 21596b19062e741b715cf399312c30e0672d8889569Stefan Richter client->device = device; 21619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg spin_lock_init(&client->lock); 21745ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason idr_init(&client->resource_idr); 21845ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason INIT_LIST_HEAD(&client->event_list); 21919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg init_waitqueue_head(&client->wait); 220fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter kref_init(&client->kref); 22119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 22219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg file->private_data = client; 22319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 224d67cfb9613f373d76daa2c8d209629601424ca12Stefan Richter mutex_lock(&device->client_list_mutex); 22597bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg list_add_tail(&client->link, &device->client_list); 226d67cfb9613f373d76daa2c8d209629601424ca12Stefan Richter mutex_unlock(&device->client_list_mutex); 22797bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg 22819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg return 0; 22919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg} 23019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 23119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsbergstatic void queue_event(struct client *client, struct event *event, 23219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg void *data0, size_t size0, void *data1, size_t size1) 23319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{ 23419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg unsigned long flags; 23519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 23619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg event->v[0].data = data0; 23719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg event->v[0].size = size0; 23819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg event->v[1].data = data1; 23919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg event->v[1].size = size1; 24019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 24119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg spin_lock_irqsave(&client->lock, flags); 24245ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason if (client->in_shutdown) 24345ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason kfree(event); 24445ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason else 24545ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason list_add_tail(&event->link, &client->event_list); 24619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg spin_unlock_irqrestore(&client->lock, flags); 24783431cba3d847fc2296d3f38ce7feb623a1cfc45Jay Fenlason 24883431cba3d847fc2296d3f38ce7feb623a1cfc45Jay Fenlason wake_up_interruptible(&client->wait); 24919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg} 25019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 25153dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic int dequeue_event(struct client *client, 25253dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter char __user *buffer, size_t count) 25319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{ 25419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg struct event *event; 25519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg size_t size, total; 2562dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter int i, ret; 25719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 2582dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter ret = wait_event_interruptible(client->wait, 2592dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter !list_empty(&client->event_list) || 2602dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter fw_device_is_shutdown(client->device)); 2612dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter if (ret < 0) 2622dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter return ret; 26319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 2642603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg if (list_empty(&client->event_list) && 2652603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg fw_device_is_shutdown(client->device)) 2662603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg return -ENODEV; 26719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 2683ba949868a6dc082b24cba5c3bf3f50de7391433Stefan Richter spin_lock_irq(&client->lock); 269a459b8ab9c176143fecef8ace4b70d6dbd7a8113Stefan Richter event = list_first_entry(&client->event_list, struct event, link); 27019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg list_del(&event->link); 2713ba949868a6dc082b24cba5c3bf3f50de7391433Stefan Richter spin_unlock_irq(&client->lock); 27219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 27319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg total = 0; 27419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg for (i = 0; i < ARRAY_SIZE(event->v) && total < count; i++) { 27519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg size = min(event->v[i].size, count - total); 2762603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg if (copy_to_user(buffer + total, event->v[i].data, size)) { 2772dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter ret = -EFAULT; 27819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg goto out; 2792603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg } 28019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg total += size; 28119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg } 2822dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter ret = total; 28319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 28419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg out: 28519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg kfree(event); 28619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 2872dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter return ret; 28819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg} 28919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 29053dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic ssize_t fw_device_op_read(struct file *file, char __user *buffer, 29153dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter size_t count, loff_t *offset) 29219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{ 29319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg struct client *client = file->private_data; 29419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 29519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg return dequeue_event(client, buffer, count); 29619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg} 29719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 29853dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic void fill_bus_reset_event(struct fw_cdev_event_bus_reset *event, 29953dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter struct client *client) 300344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg{ 301da8ecffaed434a12930f652898f9e86d1c2abc3eKristian Høgsberg struct fw_card *card = client->device->card; 302cf417e5494582453c033d8cac9e1352e74215435Jay Fenlason 3033ba949868a6dc082b24cba5c3bf3f50de7391433Stefan Richter spin_lock_irq(&card->lock); 304344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg 305da8ecffaed434a12930f652898f9e86d1c2abc3eKristian Høgsberg event->closure = client->bus_reset_closure; 306344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg event->type = FW_CDEV_EVENT_BUS_RESET; 307cf5a56ac8083dd04ffe8b9b2ec7895e9bcff44bcStefan Richter event->generation = client->device->generation; 308da8ecffaed434a12930f652898f9e86d1c2abc3eKristian Høgsberg event->node_id = client->device->node_id; 309344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg event->local_node_id = card->local_node->node_id; 310344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg event->bm_node_id = 0; /* FIXME: We don't track the BM. */ 311344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg event->irm_node_id = card->irm_node->node_id; 312344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg event->root_node_id = card->root_node->node_id; 313cf417e5494582453c033d8cac9e1352e74215435Jay Fenlason 3143ba949868a6dc082b24cba5c3bf3f50de7391433Stefan Richter spin_unlock_irq(&card->lock); 315344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg} 316344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg 31753dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic void for_each_client(struct fw_device *device, 31853dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter void (*callback)(struct client *client)) 3192603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg{ 3202603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg struct client *c; 3212603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg 322d67cfb9613f373d76daa2c8d209629601424ca12Stefan Richter mutex_lock(&device->client_list_mutex); 3232603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg list_for_each_entry(c, &device->client_list, link) 3242603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg callback(c); 325d67cfb9613f373d76daa2c8d209629601424ca12Stefan Richter mutex_unlock(&device->client_list_mutex); 3262603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg} 3272603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg 328b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richterstatic int schedule_reallocations(int id, void *p, void *data) 329b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter{ 3309fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter schedule_if_iso_resource(p); 331b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 332b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter return 0; 333b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter} 334b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 33553dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic void queue_bus_reset_event(struct client *client) 33697bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg{ 33797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct bus_reset_event *e; 33897bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg 33997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter e = kzalloc(sizeof(*e), GFP_KERNEL); 34097c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter if (e == NULL) { 34197bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg fw_notify("Out of memory when allocating bus reset event\n"); 34297bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg return; 34397bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg } 34497bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg 34597c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter fill_bus_reset_event(&e->reset, client); 34697bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg 34797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter queue_event(client, &e->event, 34897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter &e->reset, sizeof(e->reset), NULL, 0); 349b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 350b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter spin_lock_irq(&client->lock); 351b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter idr_for_each(&client->resource_idr, schedule_reallocations, client); 352b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter spin_unlock_irq(&client->lock); 35397bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg} 35497bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg 35597bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsbergvoid fw_device_cdev_update(struct fw_device *device) 35697bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg{ 3572603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg for_each_client(device, queue_bus_reset_event); 3582603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg} 35997bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg 3602603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsbergstatic void wake_up_client(struct client *client) 3612603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg{ 3622603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg wake_up_interruptible(&client->wait); 3632603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg} 36497bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg 3652603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsbergvoid fw_device_cdev_remove(struct fw_device *device) 3662603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg{ 3672603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg for_each_client(device, wake_up_client); 36897bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg} 36997bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg 3704f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsbergstatic int ioctl_get_info(struct client *client, void *buffer) 37119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{ 3724f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg struct fw_cdev_get_info *get_info = buffer; 373344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg struct fw_cdev_event_bus_reset bus_reset; 374c9755e14a01987ada4063e8b4c50c2b6738d879eStefan Richter unsigned long ret = 0; 375344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg 3764f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg client->version = get_info->version; 3774f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg get_info->version = FW_CDEV_VERSION; 378cf417e5494582453c033d8cac9e1352e74215435Jay Fenlason get_info->card = client->device->card->index; 379344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg 380c9755e14a01987ada4063e8b4c50c2b6738d879eStefan Richter down_read(&fw_device_rwsem); 381c9755e14a01987ada4063e8b4c50c2b6738d879eStefan Richter 3824f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg if (get_info->rom != 0) { 3834f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg void __user *uptr = u64_to_uptr(get_info->rom); 3844f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg size_t want = get_info->rom_length; 385d84702a5d7b500ead8db129ddea789c88764f357Stefan Richter size_t have = client->device->config_rom_length * 4; 386344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg 387c9755e14a01987ada4063e8b4c50c2b6738d879eStefan Richter ret = copy_to_user(uptr, client->device->config_rom, 388c9755e14a01987ada4063e8b4c50c2b6738d879eStefan Richter min(want, have)); 389344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg } 3904f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg get_info->rom_length = client->device->config_rom_length * 4; 391344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg 392c9755e14a01987ada4063e8b4c50c2b6738d879eStefan Richter up_read(&fw_device_rwsem); 393c9755e14a01987ada4063e8b4c50c2b6738d879eStefan Richter 394c9755e14a01987ada4063e8b4c50c2b6738d879eStefan Richter if (ret != 0) 395c9755e14a01987ada4063e8b4c50c2b6738d879eStefan Richter return -EFAULT; 396c9755e14a01987ada4063e8b4c50c2b6738d879eStefan Richter 3974f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg client->bus_reset_closure = get_info->bus_reset_closure; 3984f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg if (get_info->bus_reset != 0) { 3994f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg void __user *uptr = u64_to_uptr(get_info->bus_reset); 400344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg 401da8ecffaed434a12930f652898f9e86d1c2abc3eKristian Høgsberg fill_bus_reset_event(&bus_reset, client); 4022d826cc5c791bdc5f5651324c485746be9492be0Kristian Høgsberg if (copy_to_user(uptr, &bus_reset, sizeof(bus_reset))) 403344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg return -EFAULT; 404344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg } 40519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 40619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg return 0; 40719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg} 40819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 40953dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic int add_client_resource(struct client *client, 41053dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter struct client_resource *resource, gfp_t gfp_mask) 4113964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg{ 4123964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg unsigned long flags; 41345ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason int ret; 41445ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason 41545ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason retry: 41645ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason if (idr_pre_get(&client->resource_idr, gfp_mask) == 0) 41745ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason return -ENOMEM; 4183964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg 4193964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg spin_lock_irqsave(&client->lock, flags); 42045ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason if (client->in_shutdown) 42145ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason ret = -ECANCELED; 42245ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason else 42345ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason ret = idr_get_new(&client->resource_idr, resource, 42445ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason &resource->handle); 425b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter if (ret >= 0) { 426fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter client_get(client); 4279fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter schedule_if_iso_resource(resource); 428b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter } 4293964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg spin_unlock_irqrestore(&client->lock, flags); 43045ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason 43145ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason if (ret == -EAGAIN) 43245ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason goto retry; 43345ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason 43445ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason return ret < 0 ? ret : 0; 4353964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg} 4363964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg 43753dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic int release_client_resource(struct client *client, u32 handle, 43853dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter client_resource_release_fn_t release, 439e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter struct client_resource **return_resource) 4403964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg{ 441e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter struct client_resource *resource; 4423964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg 4433ba949868a6dc082b24cba5c3bf3f50de7391433Stefan Richter spin_lock_irq(&client->lock); 44445ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason if (client->in_shutdown) 445e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter resource = NULL; 44645ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason else 447e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter resource = idr_find(&client->resource_idr, handle); 448e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter if (resource && resource->release == release) 44945ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason idr_remove(&client->resource_idr, handle); 4503ba949868a6dc082b24cba5c3bf3f50de7391433Stefan Richter spin_unlock_irq(&client->lock); 4513964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg 452e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter if (!(resource && resource->release == release)) 4533964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg return -EINVAL; 4543964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg 455e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter if (return_resource) 456e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter *return_resource = resource; 4573964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg else 458e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter resource->release(client, resource); 4593964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg 460fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter client_put(client); 461fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter 4623964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg return 0; 4633964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg} 4643964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg 46553dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic void release_transaction(struct client *client, 46653dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter struct client_resource *resource) 4673964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg{ 46897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct outbound_transaction_resource *r = container_of(resource, 46997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct outbound_transaction_resource, resource); 4703964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg 47197c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter fw_cancel_transaction(client->device->card, &r->transaction); 4723964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg} 4733964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg 47453dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic void complete_transaction(struct fw_card *card, int rcode, 47553dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter void *payload, size_t length, void *data) 47619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{ 47797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct outbound_transaction_event *e = data; 47897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct fw_cdev_event_response *rsp = &e->response; 47997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct client *client = e->client; 48028cf6a04c82857d562968dc3a8a89726e6ac3dcbKristian Høgsberg unsigned long flags; 48119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 48297c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter if (length < rsp->length) 48397c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter rsp->length = length; 48419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg if (rcode == RCODE_COMPLETE) 48597c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter memcpy(rsp->data, payload, rsp->length); 48619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 48728cf6a04c82857d562968dc3a8a89726e6ac3dcbKristian Høgsberg spin_lock_irqsave(&client->lock, flags); 48845ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason /* 489fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter * 1. If called while in shutdown, the idr tree must be left untouched. 490fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter * The idr handle will be removed and the client reference will be 491fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter * dropped later. 492fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter * 2. If the call chain was release_client_resource -> 493fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter * release_transaction -> complete_transaction (instead of a normal 494fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter * conclusion of the transaction), i.e. if this resource was already 495fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter * unregistered from the idr, the client reference will be dropped 496fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter * by release_client_resource and we must not drop it here. 49745ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason */ 498fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter if (!client->in_shutdown && 49997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter idr_find(&client->resource_idr, e->r.resource.handle)) { 50097c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter idr_remove(&client->resource_idr, e->r.resource.handle); 501fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter /* Drop the idr's reference */ 502fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter client_put(client); 503fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter } 50428cf6a04c82857d562968dc3a8a89726e6ac3dcbKristian Høgsberg spin_unlock_irqrestore(&client->lock, flags); 50528cf6a04c82857d562968dc3a8a89726e6ac3dcbKristian Høgsberg 50697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter rsp->type = FW_CDEV_EVENT_RESPONSE; 50797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter rsp->rcode = rcode; 5088401d92ba46a1e859464cbd9c9ee304f6e361da3David Moore 5098401d92ba46a1e859464cbd9c9ee304f6e361da3David Moore /* 51097c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter * In the case that sizeof(*rsp) doesn't align with the position of the 5118401d92ba46a1e859464cbd9c9ee304f6e361da3David Moore * data, and the read is short, preserve an extra copy of the data 5128401d92ba46a1e859464cbd9c9ee304f6e361da3David Moore * to stay compatible with a pre-2.6.27 bug. Since the bug is harmless 5138401d92ba46a1e859464cbd9c9ee304f6e361da3David Moore * for short reads and some apps depended on it, this is both safe 5148401d92ba46a1e859464cbd9c9ee304f6e361da3David Moore * and prudent for compatibility. 5158401d92ba46a1e859464cbd9c9ee304f6e361da3David Moore */ 51697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter if (rsp->length <= sizeof(*rsp) - offsetof(typeof(*rsp), data)) 51797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter queue_event(client, &e->event, rsp, sizeof(*rsp), 51897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter rsp->data, rsp->length); 5198401d92ba46a1e859464cbd9c9ee304f6e361da3David Moore else 52097c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter queue_event(client, &e->event, rsp, sizeof(*rsp) + rsp->length, 5218401d92ba46a1e859464cbd9c9ee304f6e361da3David Moore NULL, 0); 522fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter 523fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter /* Drop the transaction callback's reference */ 524fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter client_put(client); 52519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg} 52619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 527acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richterstatic int init_request(struct client *client, 528acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter struct fw_cdev_send_request *request, 529acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter int destination_id, int speed) 53019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{ 53197c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct outbound_transaction_event *e; 5321f3125af8ed7410cc0ebcc0acd59bbfc1ae0057aStefan Richter int ret; 53319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 53418e9b10fcdc090d3a38606958167d5923c7099b7Stefan Richter if (request->tcode != TCODE_STREAM_DATA && 53518e9b10fcdc090d3a38606958167d5923c7099b7Stefan Richter (request->length > 4096 || request->length > 512 << speed)) 5365d3fd692a7196a9045fb606f891f5987959b65a0Stefan Richter return -EIO; 53719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 53897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter e = kmalloc(sizeof(*e) + request->length, GFP_KERNEL); 53997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter if (e == NULL) 54019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg return -ENOMEM; 54119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 54297c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter e->client = client; 54397c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter e->response.length = request->length; 54497c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter e->response.closure = request->closure; 54519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 5464f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg if (request->data && 54797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter copy_from_user(e->response.data, 5484f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg u64_to_uptr(request->data), request->length)) { 5491f3125af8ed7410cc0ebcc0acd59bbfc1ae0057aStefan Richter ret = -EFAULT; 55045ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason goto failed; 5511f3125af8ed7410cc0ebcc0acd59bbfc1ae0057aStefan Richter } 5521f3125af8ed7410cc0ebcc0acd59bbfc1ae0057aStefan Richter 55397c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter e->r.resource.release = release_transaction; 55497c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter ret = add_client_resource(client, &e->r.resource, GFP_KERNEL); 55545ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason if (ret < 0) 55645ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason goto failed; 55728cf6a04c82857d562968dc3a8a89726e6ac3dcbKristian Høgsberg 558fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter /* Get a reference for the transaction callback */ 559fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter client_get(client); 560fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter 561acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter fw_send_request(client->device->card, &e->r.transaction, 562664d8010b170ae8b3ce9268b4f4da934d27b0491Stefan Richter request->tcode, destination_id, request->generation, 563664d8010b170ae8b3ce9268b4f4da934d27b0491Stefan Richter speed, request->offset, e->response.data, 564664d8010b170ae8b3ce9268b4f4da934d27b0491Stefan Richter request->length, complete_transaction, e); 565664d8010b170ae8b3ce9268b4f4da934d27b0491Stefan Richter return 0; 56619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 56745ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason failed: 56897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter kfree(e); 5691f3125af8ed7410cc0ebcc0acd59bbfc1ae0057aStefan Richter 5701f3125af8ed7410cc0ebcc0acd59bbfc1ae0057aStefan Richter return ret; 57119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg} 57219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 573acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richterstatic int ioctl_send_request(struct client *client, void *buffer) 574acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter{ 575acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter struct fw_cdev_send_request *request = buffer; 576acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter 577acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter switch (request->tcode) { 578acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter case TCODE_WRITE_QUADLET_REQUEST: 579acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter case TCODE_WRITE_BLOCK_REQUEST: 580acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter case TCODE_READ_QUADLET_REQUEST: 581acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter case TCODE_READ_BLOCK_REQUEST: 582acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter case TCODE_LOCK_MASK_SWAP: 583acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter case TCODE_LOCK_COMPARE_SWAP: 584acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter case TCODE_LOCK_FETCH_ADD: 585acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter case TCODE_LOCK_LITTLE_ADD: 586acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter case TCODE_LOCK_BOUNDED_ADD: 587acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter case TCODE_LOCK_WRAP_ADD: 588acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter case TCODE_LOCK_VENDOR_DEPENDENT: 589acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter break; 590acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter default: 591acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter return -EINVAL; 592acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter } 593acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter 594207fbefb18de9bc6f871e4008da29879c90cb67eStefan Richter return init_request(client, request, client->device->node_id, 595acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter client->device->max_speed); 596acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter} 597acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter 59853dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic void release_request(struct client *client, 59953dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter struct client_resource *resource) 6003964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg{ 60197c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct inbound_transaction_resource *r = container_of(resource, 60297c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct inbound_transaction_resource, resource); 6033964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg 604db5d247ae811f49185a71e703b65acad845e4b18Clemens Ladisch if (r->request) 605db5d247ae811f49185a71e703b65acad845e4b18Clemens Ladisch fw_send_response(client->device->card, r->request, 606db5d247ae811f49185a71e703b65acad845e4b18Clemens Ladisch RCODE_CONFLICT_ERROR); 60797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter kfree(r); 6083964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg} 6093964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg 61097c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richterstatic void handle_request(struct fw_card *card, struct fw_request *request, 61153dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter int tcode, int destination, int source, 61253dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter int generation, int speed, 61353dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter unsigned long long offset, 61453dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter void *payload, size_t length, void *callback_data) 61519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{ 61697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct address_handler_resource *handler = callback_data; 61797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct inbound_transaction_resource *r; 61897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct inbound_transaction_event *e; 61945ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason int ret; 62019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 62197c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter r = kmalloc(sizeof(*r), GFP_ATOMIC); 6222d826cc5c791bdc5f5651324c485746be9492be0Kristian Høgsberg e = kmalloc(sizeof(*e), GFP_ATOMIC); 62397c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter if (r == NULL || e == NULL) 62445ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason goto failed; 62519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 62697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter r->request = request; 62797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter r->data = payload; 62897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter r->length = length; 62919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 63097c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter r->resource.release = release_request; 63197c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter ret = add_client_resource(handler->client, &r->resource, GFP_ATOMIC); 63245ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason if (ret < 0) 63345ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason goto failed; 63419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 63519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg e->request.type = FW_CDEV_EVENT_REQUEST; 63619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg e->request.tcode = tcode; 63719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg e->request.offset = offset; 63819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg e->request.length = length; 63997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter e->request.handle = r->resource.handle; 64019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg e->request.closure = handler->closure; 64119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 64297c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter queue_event(handler->client, &e->event, 6432d826cc5c791bdc5f5651324c485746be9492be0Kristian Høgsberg &e->request, sizeof(e->request), payload, length); 64445ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason return; 64545ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason 64645ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason failed: 64797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter kfree(r); 64845ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason kfree(e); 649db5d247ae811f49185a71e703b65acad845e4b18Clemens Ladisch if (request) 650db5d247ae811f49185a71e703b65acad845e4b18Clemens Ladisch fw_send_response(card, request, RCODE_CONFLICT_ERROR); 65119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg} 65219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 65353dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic void release_address_handler(struct client *client, 65453dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter struct client_resource *resource) 6553964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg{ 65697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct address_handler_resource *r = 65797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter container_of(resource, struct address_handler_resource, resource); 6583964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg 65997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter fw_core_remove_address_handler(&r->handler); 66097c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter kfree(r); 6613964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg} 6623964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg 6634f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsbergstatic int ioctl_allocate(struct client *client, void *buffer) 66419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{ 6654f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg struct fw_cdev_allocate *request = buffer; 66697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct address_handler_resource *r; 66719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg struct fw_address_region region; 66845ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason int ret; 66919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 67097c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter r = kmalloc(sizeof(*r), GFP_KERNEL); 67197c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter if (r == NULL) 67219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg return -ENOMEM; 67319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 6744f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg region.start = request->offset; 6754f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg region.end = request->offset + request->length; 67697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter r->handler.length = request->length; 67797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter r->handler.address_callback = handle_request; 67897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter r->handler.callback_data = r; 67997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter r->closure = request->closure; 68097c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter r->client = client; 68119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 68297c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter ret = fw_core_add_address_handler(&r->handler, ®ion); 6833e0b5f0d7cb5fef402517e41eebff5a0f0e65a13Stefan Richter if (ret < 0) { 68497c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter kfree(r); 6853e0b5f0d7cb5fef402517e41eebff5a0f0e65a13Stefan Richter return ret; 68619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg } 68719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 68897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter r->resource.release = release_address_handler; 68997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter ret = add_client_resource(client, &r->resource, GFP_KERNEL); 69045ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason if (ret < 0) { 69197c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter release_address_handler(client, &r->resource); 69245ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason return ret; 69345ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason } 69497c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter request->handle = r->resource.handle; 69519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 69619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg return 0; 69719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg} 69819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 6994f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsbergstatic int ioctl_deallocate(struct client *client, void *buffer) 7009472316b6eab3500ded544f6e86700c33541ef4eKristian Høgsberg{ 7014f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg struct fw_cdev_deallocate *request = buffer; 7029472316b6eab3500ded544f6e86700c33541ef4eKristian Høgsberg 70345ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason return release_client_resource(client, request->handle, 70445ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason release_address_handler, NULL); 7059472316b6eab3500ded544f6e86700c33541ef4eKristian Høgsberg} 7069472316b6eab3500ded544f6e86700c33541ef4eKristian Høgsberg 7074f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsbergstatic int ioctl_send_response(struct client *client, void *buffer) 70819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{ 7094f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg struct fw_cdev_send_response *request = buffer; 7103964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg struct client_resource *resource; 71197c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct inbound_transaction_resource *r; 7127e44c0b56b07a5e34de9943cfb2fee72e71a9f0eStefan Richter int ret = 0; 71319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 71445ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason if (release_client_resource(client, request->handle, 71545ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason release_request, &resource) < 0) 71619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg return -EINVAL; 71745ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason 71897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter r = container_of(resource, struct inbound_transaction_resource, 71997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter resource); 720db5d247ae811f49185a71e703b65acad845e4b18Clemens Ladisch if (r->request) { 721db5d247ae811f49185a71e703b65acad845e4b18Clemens Ladisch if (request->length < r->length) 722db5d247ae811f49185a71e703b65acad845e4b18Clemens Ladisch r->length = request->length; 723db5d247ae811f49185a71e703b65acad845e4b18Clemens Ladisch if (copy_from_user(r->data, u64_to_uptr(request->data), 724db5d247ae811f49185a71e703b65acad845e4b18Clemens Ladisch r->length)) { 725db5d247ae811f49185a71e703b65acad845e4b18Clemens Ladisch ret = -EFAULT; 726cf0e575dcc4cab9fd955e9bec49df7e8ee30a7cfStefan Richter kfree(r->request); 727db5d247ae811f49185a71e703b65acad845e4b18Clemens Ladisch goto out; 728db5d247ae811f49185a71e703b65acad845e4b18Clemens Ladisch } 729db5d247ae811f49185a71e703b65acad845e4b18Clemens Ladisch fw_send_response(client->device->card, r->request, 730db5d247ae811f49185a71e703b65acad845e4b18Clemens Ladisch request->rcode); 7317e44c0b56b07a5e34de9943cfb2fee72e71a9f0eStefan Richter } 7327e44c0b56b07a5e34de9943cfb2fee72e71a9f0eStefan Richter out: 73319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg kfree(r); 73419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 7357e44c0b56b07a5e34de9943cfb2fee72e71a9f0eStefan Richter return ret; 73619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg} 73719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 7384f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsbergstatic int ioctl_initiate_bus_reset(struct client *client, void *buffer) 7395371842b723dd04df57171f2c74660966901380cKristian Høgsberg{ 7404f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg struct fw_cdev_initiate_bus_reset *request = buffer; 7415371842b723dd04df57171f2c74660966901380cKristian Høgsberg int short_reset; 7425371842b723dd04df57171f2c74660966901380cKristian Høgsberg 7434f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg short_reset = (request->type == FW_CDEV_SHORT_RESET); 7445371842b723dd04df57171f2c74660966901380cKristian Høgsberg 7455371842b723dd04df57171f2c74660966901380cKristian Høgsberg return fw_core_initiate_bus_reset(client->device->card, short_reset); 7465371842b723dd04df57171f2c74660966901380cKristian Høgsberg} 7475371842b723dd04df57171f2c74660966901380cKristian Høgsberg 7483964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsbergstatic void release_descriptor(struct client *client, 7493964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg struct client_resource *resource) 7503964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg{ 75197c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct descriptor_resource *r = 75297c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter container_of(resource, struct descriptor_resource, resource); 7533964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg 75497c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter fw_core_remove_descriptor(&r->descriptor); 75597c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter kfree(r); 7563964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg} 7573964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg 7584f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsbergstatic int ioctl_add_descriptor(struct client *client, void *buffer) 75966dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg{ 7604f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg struct fw_cdev_add_descriptor *request = buffer; 76197c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct descriptor_resource *r; 76245ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason int ret; 76366dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg 764de487da8ca5839d057e1f4b57ee3f387e180b800Stefan Richter /* Access policy: Allow this ioctl only on local nodes' device files. */ 76592368890d551794ee8d7e90477d8498bb7f82a9bStefan Richter if (!client->device->is_local) 766de487da8ca5839d057e1f4b57ee3f387e180b800Stefan Richter return -ENOSYS; 767de487da8ca5839d057e1f4b57ee3f387e180b800Stefan Richter 7684f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg if (request->length > 256) 76966dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg return -EINVAL; 77066dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg 77197c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter r = kmalloc(sizeof(*r) + request->length * 4, GFP_KERNEL); 77297c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter if (r == NULL) 77366dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg return -ENOMEM; 77466dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg 77597c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter if (copy_from_user(r->data, 7764f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg u64_to_uptr(request->data), request->length * 4)) { 77745ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason ret = -EFAULT; 77845ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason goto failed; 77966dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg } 78066dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg 78197c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter r->descriptor.length = request->length; 78297c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter r->descriptor.immediate = request->immediate; 78397c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter r->descriptor.key = request->key; 78497c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter r->descriptor.data = r->data; 78566dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg 78697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter ret = fw_core_add_descriptor(&r->descriptor); 78745ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason if (ret < 0) 78845ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason goto failed; 78966dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg 79097c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter r->resource.release = release_descriptor; 79197c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter ret = add_client_resource(client, &r->resource, GFP_KERNEL); 79245ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason if (ret < 0) { 79397c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter fw_core_remove_descriptor(&r->descriptor); 79445ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason goto failed; 79545ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason } 79697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter request->handle = r->resource.handle; 79766dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg 79866dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg return 0; 79945ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason failed: 80097c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter kfree(r); 80145ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason 80245ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason return ret; 80366dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg} 80466dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg 8054f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsbergstatic int ioctl_remove_descriptor(struct client *client, void *buffer) 80666dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg{ 8074f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg struct fw_cdev_remove_descriptor *request = buffer; 80866dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg 80945ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason return release_client_resource(client, request->handle, 81045ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason release_descriptor, NULL); 81166dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg} 81266dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg 81353dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic void iso_callback(struct fw_iso_context *context, u32 cycle, 81453dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter size_t header_length, void *header, void *data) 81519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{ 81619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg struct client *client = data; 81797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter struct iso_interrupt_event *e; 81819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 81997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter e = kzalloc(sizeof(*e) + header_length, GFP_ATOMIC); 82097c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter if (e == NULL) 82119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg return; 82219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 82397c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter e->interrupt.type = FW_CDEV_EVENT_ISO_INTERRUPT; 82497c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter e->interrupt.closure = client->iso_closure; 82597c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter e->interrupt.cycle = cycle; 82697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter e->interrupt.header_length = header_length; 82797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter memcpy(e->interrupt.header, header, header_length); 82897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter queue_event(client, &e->event, &e->interrupt, 82997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter sizeof(e->interrupt) + header_length, NULL, 0); 83019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg} 83119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 8324f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsbergstatic int ioctl_create_iso_context(struct client *client, void *buffer) 83319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{ 8344f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg struct fw_cdev_create_iso_context *request = buffer; 83524315c5e6f508edd84e996d67daef3d1bcc72f8bKristian Høgsberg struct fw_iso_context *context; 83619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 837fae603121428ba83b7343c88e68a7144525ab3ebStefan Richter /* We only support one context at this time. */ 838fae603121428ba83b7343c88e68a7144525ab3ebStefan Richter if (client->iso_context != NULL) 839fae603121428ba83b7343c88e68a7144525ab3ebStefan Richter return -EBUSY; 840fae603121428ba83b7343c88e68a7144525ab3ebStefan Richter 8414f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg if (request->channel > 63) 84221efb3cfc6ed49991638000f58bb23b838c76e25Kristian Høgsberg return -EINVAL; 84321efb3cfc6ed49991638000f58bb23b838c76e25Kristian Høgsberg 8444f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg switch (request->type) { 845c70dc788fd8d3870b41231b6a53a64afb98cfd13Kristian Høgsberg case FW_ISO_CONTEXT_RECEIVE: 8464f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg if (request->header_size < 4 || (request->header_size & 3)) 847c70dc788fd8d3870b41231b6a53a64afb98cfd13Kristian Høgsberg return -EINVAL; 84898b6cbe83b6e8db54638746c9040c7962d96b322Kristian Høgsberg 849c70dc788fd8d3870b41231b6a53a64afb98cfd13Kristian Høgsberg break; 850c70dc788fd8d3870b41231b6a53a64afb98cfd13Kristian Høgsberg 851c70dc788fd8d3870b41231b6a53a64afb98cfd13Kristian Høgsberg case FW_ISO_CONTEXT_TRANSMIT: 8524f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg if (request->speed > SCODE_3200) 853c70dc788fd8d3870b41231b6a53a64afb98cfd13Kristian Høgsberg return -EINVAL; 854c70dc788fd8d3870b41231b6a53a64afb98cfd13Kristian Høgsberg 855c70dc788fd8d3870b41231b6a53a64afb98cfd13Kristian Høgsberg break; 856c70dc788fd8d3870b41231b6a53a64afb98cfd13Kristian Høgsberg 857c70dc788fd8d3870b41231b6a53a64afb98cfd13Kristian Høgsberg default: 85821efb3cfc6ed49991638000f58bb23b838c76e25Kristian Høgsberg return -EINVAL; 859c70dc788fd8d3870b41231b6a53a64afb98cfd13Kristian Høgsberg } 860c70dc788fd8d3870b41231b6a53a64afb98cfd13Kristian Høgsberg 86124315c5e6f508edd84e996d67daef3d1bcc72f8bKristian Høgsberg context = fw_iso_context_create(client->device->card, 86224315c5e6f508edd84e996d67daef3d1bcc72f8bKristian Høgsberg request->type, 86324315c5e6f508edd84e996d67daef3d1bcc72f8bKristian Høgsberg request->channel, 86424315c5e6f508edd84e996d67daef3d1bcc72f8bKristian Høgsberg request->speed, 86524315c5e6f508edd84e996d67daef3d1bcc72f8bKristian Høgsberg request->header_size, 86624315c5e6f508edd84e996d67daef3d1bcc72f8bKristian Høgsberg iso_callback, client); 86724315c5e6f508edd84e996d67daef3d1bcc72f8bKristian Høgsberg if (IS_ERR(context)) 86824315c5e6f508edd84e996d67daef3d1bcc72f8bKristian Høgsberg return PTR_ERR(context); 86924315c5e6f508edd84e996d67daef3d1bcc72f8bKristian Høgsberg 870abaa5743e340c23922d92c9a5a6753ea3ae71e58Kristian Høgsberg client->iso_closure = request->closure; 87124315c5e6f508edd84e996d67daef3d1bcc72f8bKristian Høgsberg client->iso_context = context; 87219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 873abaa5743e340c23922d92c9a5a6753ea3ae71e58Kristian Høgsberg /* We only support one context at this time. */ 874abaa5743e340c23922d92c9a5a6753ea3ae71e58Kristian Høgsberg request->handle = 0; 875abaa5743e340c23922d92c9a5a6753ea3ae71e58Kristian Høgsberg 87619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg return 0; 87719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg} 87819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 8791ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg/* Macros for decoding the iso packet control header. */ 8801ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg#define GET_PAYLOAD_LENGTH(v) ((v) & 0xffff) 8811ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg#define GET_INTERRUPT(v) (((v) >> 16) & 0x01) 8821ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg#define GET_SKIP(v) (((v) >> 17) & 0x01) 8837a1003449c693f0d57443c8786bbf19717921ae0Stefan Richter#define GET_TAG(v) (((v) >> 18) & 0x03) 8847a1003449c693f0d57443c8786bbf19717921ae0Stefan Richter#define GET_SY(v) (((v) >> 20) & 0x0f) 8851ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg#define GET_HEADER_LENGTH(v) (((v) >> 24) & 0xff) 8861ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg 8874f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsbergstatic int ioctl_queue_iso(struct client *client, void *buffer) 88819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{ 8894f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg struct fw_cdev_queue_iso *request = buffer; 89019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg struct fw_cdev_iso_packet __user *p, *end, *next; 8919b32d5f3074e9b1afaa39a360a59fd77a2214783Kristian Høgsberg struct fw_iso_context *ctx = client->iso_context; 892ef370ee74b7a9cb769d50bfb73b4023ee3e37719Kristian Høgsberg unsigned long payload, buffer_end, header_length; 8931ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg u32 control; 89419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg int count; 89519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg struct { 89619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg struct fw_iso_packet packet; 89719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg u8 header[256]; 89819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg } u; 89919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 900abaa5743e340c23922d92c9a5a6753ea3ae71e58Kristian Høgsberg if (ctx == NULL || request->handle != 0) 90119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg return -EINVAL; 90219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 903c781c06d119d04601727f2fbc30151e6760d536dKristian Høgsberg /* 904c781c06d119d04601727f2fbc30151e6760d536dKristian Høgsberg * If the user passes a non-NULL data pointer, has mmap()'ed 90519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg * the iso buffer, and the pointer points inside the buffer, 90619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg * we setup the payload pointers accordingly. Otherwise we 9079aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg * set them both to 0, which will still let packets with 90819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg * payload_length == 0 through. In other words, if no packets 90919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg * use the indirect payload, the iso buffer need not be mapped 910c781c06d119d04601727f2fbc30151e6760d536dKristian Høgsberg * and the request->data pointer is ignored. 911c781c06d119d04601727f2fbc30151e6760d536dKristian Høgsberg */ 91219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 9134f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg payload = (unsigned long)request->data - client->vm_start; 914ef370ee74b7a9cb769d50bfb73b4023ee3e37719Kristian Høgsberg buffer_end = client->buffer.page_count << PAGE_SHIFT; 9154f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg if (request->data == 0 || client->buffer.pages == NULL || 916ef370ee74b7a9cb769d50bfb73b4023ee3e37719Kristian Høgsberg payload >= buffer_end) { 9179aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg payload = 0; 918ef370ee74b7a9cb769d50bfb73b4023ee3e37719Kristian Høgsberg buffer_end = 0; 91919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg } 92019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 9211ccc9147f6a063c42fef67acff34de18435a4a6bAl Viro p = (struct fw_cdev_iso_packet __user *)u64_to_uptr(request->packets); 9221ccc9147f6a063c42fef67acff34de18435a4a6bAl Viro 9231ccc9147f6a063c42fef67acff34de18435a4a6bAl Viro if (!access_ok(VERIFY_READ, p, request->size)) 92419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg return -EFAULT; 92519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 9264f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg end = (void __user *)p + request->size; 92719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg count = 0; 92819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg while (p < end) { 9291ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg if (get_user(control, &p->control)) 93019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg return -EFAULT; 9311ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg u.packet.payload_length = GET_PAYLOAD_LENGTH(control); 9321ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg u.packet.interrupt = GET_INTERRUPT(control); 9331ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg u.packet.skip = GET_SKIP(control); 9341ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg u.packet.tag = GET_TAG(control); 9351ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg u.packet.sy = GET_SY(control); 9361ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg u.packet.header_length = GET_HEADER_LENGTH(control); 937295e3feb92e5073ec32a3c626302d4b92c4c8a95Kristian Høgsberg 9389b32d5f3074e9b1afaa39a360a59fd77a2214783Kristian Høgsberg if (ctx->type == FW_ISO_CONTEXT_TRANSMIT) { 939295e3feb92e5073ec32a3c626302d4b92c4c8a95Kristian Høgsberg header_length = u.packet.header_length; 940295e3feb92e5073ec32a3c626302d4b92c4c8a95Kristian Høgsberg } else { 941c781c06d119d04601727f2fbc30151e6760d536dKristian Høgsberg /* 942c781c06d119d04601727f2fbc30151e6760d536dKristian Høgsberg * We require that header_length is a multiple of 943c781c06d119d04601727f2fbc30151e6760d536dKristian Høgsberg * the fixed header size, ctx->header_size. 944c781c06d119d04601727f2fbc30151e6760d536dKristian Høgsberg */ 9459b32d5f3074e9b1afaa39a360a59fd77a2214783Kristian Høgsberg if (ctx->header_size == 0) { 9469b32d5f3074e9b1afaa39a360a59fd77a2214783Kristian Høgsberg if (u.packet.header_length > 0) 9479b32d5f3074e9b1afaa39a360a59fd77a2214783Kristian Høgsberg return -EINVAL; 9489b32d5f3074e9b1afaa39a360a59fd77a2214783Kristian Høgsberg } else if (u.packet.header_length % ctx->header_size != 0) { 949295e3feb92e5073ec32a3c626302d4b92c4c8a95Kristian Høgsberg return -EINVAL; 9509b32d5f3074e9b1afaa39a360a59fd77a2214783Kristian Høgsberg } 951295e3feb92e5073ec32a3c626302d4b92c4c8a95Kristian Høgsberg header_length = 0; 952295e3feb92e5073ec32a3c626302d4b92c4c8a95Kristian Høgsberg } 953295e3feb92e5073ec32a3c626302d4b92c4c8a95Kristian Høgsberg 95419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg next = (struct fw_cdev_iso_packet __user *) 955295e3feb92e5073ec32a3c626302d4b92c4c8a95Kristian Høgsberg &p->header[header_length / 4]; 95619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg if (next > end) 95719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg return -EINVAL; 95819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg if (__copy_from_user 959295e3feb92e5073ec32a3c626302d4b92c4c8a95Kristian Høgsberg (u.packet.header, p->header, header_length)) 96019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg return -EFAULT; 96198b6cbe83b6e8db54638746c9040c7962d96b322Kristian Høgsberg if (u.packet.skip && ctx->type == FW_ISO_CONTEXT_TRANSMIT && 96219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg u.packet.header_length + u.packet.payload_length > 0) 96319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg return -EINVAL; 964ef370ee74b7a9cb769d50bfb73b4023ee3e37719Kristian Høgsberg if (payload + u.packet.payload_length > buffer_end) 96519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg return -EINVAL; 96619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 9679b32d5f3074e9b1afaa39a360a59fd77a2214783Kristian Høgsberg if (fw_iso_context_queue(ctx, &u.packet, 9689b32d5f3074e9b1afaa39a360a59fd77a2214783Kristian Høgsberg &client->buffer, payload)) 96919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg break; 97019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 97119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg p = next; 97219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg payload += u.packet.payload_length; 97319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg count++; 97419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg } 97519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 9764f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg request->size -= uptr_to_u64(p) - request->packets; 9774f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg request->packets = uptr_to_u64(p); 9784f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg request->data = client->vm_start + payload; 97919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 98019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg return count; 98119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg} 98219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 9834f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsbergstatic int ioctl_start_iso(struct client *client, void *buffer) 98419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{ 9854f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg struct fw_cdev_start_iso *request = buffer; 98619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 987fae603121428ba83b7343c88e68a7144525ab3ebStefan Richter if (client->iso_context == NULL || request->handle != 0) 988abaa5743e340c23922d92c9a5a6753ea3ae71e58Kristian Høgsberg return -EINVAL; 989fae603121428ba83b7343c88e68a7144525ab3ebStefan Richter 990eb0306eac0aad0b7da18d8fbfb777f155b2c010dKristian Høgsberg if (client->iso_context->type == FW_ISO_CONTEXT_RECEIVE) { 9914f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg if (request->tags == 0 || request->tags > 15) 992eb0306eac0aad0b7da18d8fbfb777f155b2c010dKristian Høgsberg return -EINVAL; 993eb0306eac0aad0b7da18d8fbfb777f155b2c010dKristian Høgsberg 9944f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg if (request->sync > 15) 995eb0306eac0aad0b7da18d8fbfb777f155b2c010dKristian Høgsberg return -EINVAL; 996eb0306eac0aad0b7da18d8fbfb777f155b2c010dKristian Høgsberg } 997eb0306eac0aad0b7da18d8fbfb777f155b2c010dKristian Høgsberg 9984f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg return fw_iso_context_start(client->iso_context, request->cycle, 9994f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg request->sync, request->tags); 100019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg} 100119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 10024f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsbergstatic int ioctl_stop_iso(struct client *client, void *buffer) 1003b82956685aab4a9d333714300eb8a86fed6c9ab3Kristian Høgsberg{ 1004abaa5743e340c23922d92c9a5a6753ea3ae71e58Kristian Høgsberg struct fw_cdev_stop_iso *request = buffer; 1005abaa5743e340c23922d92c9a5a6753ea3ae71e58Kristian Høgsberg 1006fae603121428ba83b7343c88e68a7144525ab3ebStefan Richter if (client->iso_context == NULL || request->handle != 0) 1007abaa5743e340c23922d92c9a5a6753ea3ae71e58Kristian Høgsberg return -EINVAL; 1008abaa5743e340c23922d92c9a5a6753ea3ae71e58Kristian Høgsberg 1009b82956685aab4a9d333714300eb8a86fed6c9ab3Kristian Høgsberg return fw_iso_context_stop(client->iso_context); 1010b82956685aab4a9d333714300eb8a86fed6c9ab3Kristian Høgsberg} 1011b82956685aab4a9d333714300eb8a86fed6c9ab3Kristian Høgsberg 1012a64408b96b5f67c2778958a230b5cfa3408a4a81Stefan Richterstatic int ioctl_get_cycle_timer(struct client *client, void *buffer) 1013a64408b96b5f67c2778958a230b5cfa3408a4a81Stefan Richter{ 1014a64408b96b5f67c2778958a230b5cfa3408a4a81Stefan Richter struct fw_cdev_get_cycle_timer *request = buffer; 1015a64408b96b5f67c2778958a230b5cfa3408a4a81Stefan Richter struct fw_card *card = client->device->card; 1016a64408b96b5f67c2778958a230b5cfa3408a4a81Stefan Richter struct timeval tv; 10174a9bde9b8ab55a2bb51b57cad215a97bcf80bae2Stefan Richter u32 cycle_time; 1018a64408b96b5f67c2778958a230b5cfa3408a4a81Stefan Richter 10194a9bde9b8ab55a2bb51b57cad215a97bcf80bae2Stefan Richter local_irq_disable(); 1020a64408b96b5f67c2778958a230b5cfa3408a4a81Stefan Richter 1021168cf9af699e87d5a6f44b684583714ecabb8e71Stefan Richter cycle_time = card->driver->get_cycle_time(card); 1022a64408b96b5f67c2778958a230b5cfa3408a4a81Stefan Richter do_gettimeofday(&tv); 1023a64408b96b5f67c2778958a230b5cfa3408a4a81Stefan Richter 10244a9bde9b8ab55a2bb51b57cad215a97bcf80bae2Stefan Richter local_irq_enable(); 1025a64408b96b5f67c2778958a230b5cfa3408a4a81Stefan Richter 1026a64408b96b5f67c2778958a230b5cfa3408a4a81Stefan Richter request->local_time = tv.tv_sec * 1000000ULL + tv.tv_usec; 10274a9bde9b8ab55a2bb51b57cad215a97bcf80bae2Stefan Richter request->cycle_timer = cycle_time; 10284a9bde9b8ab55a2bb51b57cad215a97bcf80bae2Stefan Richter 1029a64408b96b5f67c2778958a230b5cfa3408a4a81Stefan Richter return 0; 1030a64408b96b5f67c2778958a230b5cfa3408a4a81Stefan Richter} 1031a64408b96b5f67c2778958a230b5cfa3408a4a81Stefan Richter 1032b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richterstatic void iso_resource_work(struct work_struct *work) 1033b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter{ 1034b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter struct iso_resource_event *e; 1035b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter struct iso_resource *r = 1036b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter container_of(work, struct iso_resource, work.work); 1037b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter struct client *client = r->client; 1038b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter int generation, channel, bandwidth, todo; 1039b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter bool skip, free, success; 1040b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 1041b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter spin_lock_irq(&client->lock); 1042b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter generation = client->device->generation; 1043b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter todo = r->todo; 1044b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter /* Allow 1000ms grace period for other reallocations. */ 1045b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter if (todo == ISO_RES_ALLOC && 1046b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter time_is_after_jiffies(client->device->card->reset_jiffies + HZ)) { 10479fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter schedule_iso_resource(r, DIV_ROUND_UP(HZ, 3)); 1048b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter skip = true; 1049b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter } else { 1050b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter /* We could be called twice within the same generation. */ 1051b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter skip = todo == ISO_RES_REALLOC && 1052b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter r->generation == generation; 1053b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter } 10541ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter free = todo == ISO_RES_DEALLOC || 10551ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter todo == ISO_RES_ALLOC_ONCE || 10561ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter todo == ISO_RES_DEALLOC_ONCE; 1057b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter r->generation = generation; 1058b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter spin_unlock_irq(&client->lock); 1059b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 1060b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter if (skip) 1061b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter goto out; 1062b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 1063b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter bandwidth = r->bandwidth; 1064b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 1065b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter fw_iso_resource_manage(client->device->card, generation, 1066b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter r->channels, &channel, &bandwidth, 10671ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter todo == ISO_RES_ALLOC || 10681ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter todo == ISO_RES_REALLOC || 10696fdc03709433ccc2005f0f593ae9d9dd04f7b485Stefan Richter todo == ISO_RES_ALLOC_ONCE, 10706fdc03709433ccc2005f0f593ae9d9dd04f7b485Stefan Richter r->transaction_data); 1071b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter /* 1072b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter * Is this generation outdated already? As long as this resource sticks 1073b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter * in the idr, it will be scheduled again for a newer generation or at 1074b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter * shutdown. 1075b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter */ 1076b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter if (channel == -EAGAIN && 1077b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter (todo == ISO_RES_ALLOC || todo == ISO_RES_REALLOC)) 1078b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter goto out; 1079b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 1080b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter success = channel >= 0 || bandwidth > 0; 1081b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 1082b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter spin_lock_irq(&client->lock); 1083b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter /* 1084b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter * Transit from allocation to reallocation, except if the client 1085b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter * requested deallocation in the meantime. 1086b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter */ 1087b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter if (r->todo == ISO_RES_ALLOC) 1088b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter r->todo = ISO_RES_REALLOC; 1089b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter /* 1090b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter * Allocation or reallocation failure? Pull this resource out of the 1091b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter * idr and prepare for deletion, unless the client is shutting down. 1092b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter */ 1093b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter if (r->todo == ISO_RES_REALLOC && !success && 1094b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter !client->in_shutdown && 1095b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter idr_find(&client->resource_idr, r->resource.handle)) { 1096b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter idr_remove(&client->resource_idr, r->resource.handle); 1097b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter client_put(client); 1098b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter free = true; 1099b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter } 1100b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter spin_unlock_irq(&client->lock); 1101b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 1102b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter if (todo == ISO_RES_ALLOC && channel >= 0) 11035d9cb7d276a9c465fef5a771792eac2cf1929f2bStefan Richter r->channels = 1ULL << channel; 1104b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 1105b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter if (todo == ISO_RES_REALLOC && success) 1106b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter goto out; 1107b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 11081ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter if (todo == ISO_RES_ALLOC || todo == ISO_RES_ALLOC_ONCE) { 1109b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter e = r->e_alloc; 1110b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter r->e_alloc = NULL; 1111b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter } else { 1112b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter e = r->e_dealloc; 1113b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter r->e_dealloc = NULL; 1114b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter } 1115e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter e->iso_resource.handle = r->resource.handle; 1116e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter e->iso_resource.channel = channel; 1117e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter e->iso_resource.bandwidth = bandwidth; 1118b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 1119b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter queue_event(client, &e->event, 1120e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter &e->iso_resource, sizeof(e->iso_resource), NULL, 0); 1121b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 1122b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter if (free) { 1123b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter cancel_delayed_work(&r->work); 1124b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter kfree(r->e_alloc); 1125b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter kfree(r->e_dealloc); 1126b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter kfree(r); 1127b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter } 1128b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter out: 1129b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter client_put(client); 1130b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter} 1131b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 1132b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richterstatic void release_iso_resource(struct client *client, 1133b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter struct client_resource *resource) 1134b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter{ 1135b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter struct iso_resource *r = 1136b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter container_of(resource, struct iso_resource, resource); 1137b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 1138b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter spin_lock_irq(&client->lock); 1139b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter r->todo = ISO_RES_DEALLOC; 11409fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter schedule_iso_resource(r, 0); 1141b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter spin_unlock_irq(&client->lock); 1142b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter} 1143b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 11441ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richterstatic int init_iso_resource(struct client *client, 11451ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter struct fw_cdev_allocate_iso_resource *request, int todo) 1146b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter{ 1147b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter struct iso_resource_event *e1, *e2; 1148b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter struct iso_resource *r; 1149b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter int ret; 1150b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 1151b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter if ((request->channels == 0 && request->bandwidth == 0) || 1152b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter request->bandwidth > BANDWIDTH_AVAILABLE_INITIAL || 1153b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter request->bandwidth < 0) 1154b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter return -EINVAL; 1155b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 1156b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter r = kmalloc(sizeof(*r), GFP_KERNEL); 1157b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter e1 = kmalloc(sizeof(*e1), GFP_KERNEL); 1158b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter e2 = kmalloc(sizeof(*e2), GFP_KERNEL); 1159b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter if (r == NULL || e1 == NULL || e2 == NULL) { 1160b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter ret = -ENOMEM; 1161b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter goto fail; 1162b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter } 1163b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 1164b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter INIT_DELAYED_WORK(&r->work, iso_resource_work); 1165b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter r->client = client; 11661ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter r->todo = todo; 1167b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter r->generation = -1; 1168b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter r->channels = request->channels; 1169b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter r->bandwidth = request->bandwidth; 1170b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter r->e_alloc = e1; 1171b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter r->e_dealloc = e2; 1172b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 1173e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter e1->iso_resource.closure = request->closure; 1174e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter e1->iso_resource.type = FW_CDEV_EVENT_ISO_RESOURCE_ALLOCATED; 1175e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter e2->iso_resource.closure = request->closure; 1176e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter e2->iso_resource.type = FW_CDEV_EVENT_ISO_RESOURCE_DEALLOCATED; 1177b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 11781ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter if (todo == ISO_RES_ALLOC) { 11791ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter r->resource.release = release_iso_resource; 11801ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter ret = add_client_resource(client, &r->resource, GFP_KERNEL); 118181610b8fbfc027a67707ff567d490819a3d55844Stefan Richter if (ret < 0) 118281610b8fbfc027a67707ff567d490819a3d55844Stefan Richter goto fail; 11831ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter } else { 11841ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter r->resource.release = NULL; 11851ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter r->resource.handle = -1; 11869fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter schedule_iso_resource(r, 0); 11871ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter } 1188b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter request->handle = r->resource.handle; 1189b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 1190b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter return 0; 1191b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter fail: 1192b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter kfree(r); 1193b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter kfree(e1); 1194b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter kfree(e2); 1195b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 1196b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter return ret; 1197b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter} 1198b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 11991ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richterstatic int ioctl_allocate_iso_resource(struct client *client, void *buffer) 12001ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter{ 12011ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter struct fw_cdev_allocate_iso_resource *request = buffer; 12021ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter 12031ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter return init_iso_resource(client, request, ISO_RES_ALLOC); 12041ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter} 12051ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter 1206b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richterstatic int ioctl_deallocate_iso_resource(struct client *client, void *buffer) 1207b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter{ 1208b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter struct fw_cdev_deallocate *request = buffer; 1209b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 1210b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter return release_client_resource(client, request->handle, 1211b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter release_iso_resource, NULL); 1212b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter} 1213b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter 12141ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richterstatic int ioctl_allocate_iso_resource_once(struct client *client, void *buffer) 12151ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter{ 12161ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter struct fw_cdev_allocate_iso_resource *request = buffer; 12171ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter 12181ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter return init_iso_resource(client, request, ISO_RES_ALLOC_ONCE); 12191ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter} 12201ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter 12211ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richterstatic int ioctl_deallocate_iso_resource_once(struct client *client, void *buffer) 12221ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter{ 12231ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter struct fw_cdev_allocate_iso_resource *request = buffer; 12241ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter 12251ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter return init_iso_resource(client, request, ISO_RES_DEALLOC_ONCE); 12261ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter} 12271ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter 1228c8a25900f35e575938c791507894c036c0f2ca7dStefan Richter/* 1229c8a25900f35e575938c791507894c036c0f2ca7dStefan Richter * Returns a speed code: Maximum speed to or from this device, 1230c8a25900f35e575938c791507894c036c0f2ca7dStefan Richter * limited by the device's link speed, the local node's link speed, 1231c8a25900f35e575938c791507894c036c0f2ca7dStefan Richter * and all PHY port speeds between the two links. 1232c8a25900f35e575938c791507894c036c0f2ca7dStefan Richter */ 123333580a3ef5ba3bc0ee1b520df82a24bb37ce28f0Stefan Richterstatic int ioctl_get_speed(struct client *client, void *buffer) 123433580a3ef5ba3bc0ee1b520df82a24bb37ce28f0Stefan Richter{ 1235c8a25900f35e575938c791507894c036c0f2ca7dStefan Richter return client->device->max_speed; 123633580a3ef5ba3bc0ee1b520df82a24bb37ce28f0Stefan Richter} 123733580a3ef5ba3bc0ee1b520df82a24bb37ce28f0Stefan Richter 1238acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richterstatic int ioctl_send_broadcast_request(struct client *client, void *buffer) 1239acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter{ 1240acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter struct fw_cdev_send_request *request = buffer; 1241acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter 1242acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter switch (request->tcode) { 1243acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter case TCODE_WRITE_QUADLET_REQUEST: 1244acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter case TCODE_WRITE_BLOCK_REQUEST: 1245acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter break; 1246acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter default: 1247acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter return -EINVAL; 1248acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter } 1249acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter 12501566f3dc3e5986a16c7bbb3bb95bb691251a8d25Stefan Richter /* Security policy: Only allow accesses to Units Space. */ 12511566f3dc3e5986a16c7bbb3bb95bb691251a8d25Stefan Richter if (request->offset < CSR_REGISTER_BASE + CSR_CONFIG_ROM_END) 12521566f3dc3e5986a16c7bbb3bb95bb691251a8d25Stefan Richter return -EACCES; 12531566f3dc3e5986a16c7bbb3bb95bb691251a8d25Stefan Richter 1254acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter return init_request(client, request, LOCAL_BUS | 0x3f, SCODE_100); 1255acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter} 1256acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter 1257f8c2287c65f8f72000102fc058232669e4540bc4Jay Fenlasonstatic int ioctl_send_stream_packet(struct client *client, void *buffer) 1258f8c2287c65f8f72000102fc058232669e4540bc4Jay Fenlason{ 125918e9b10fcdc090d3a38606958167d5923c7099b7Stefan Richter struct fw_cdev_send_stream_packet *p = buffer; 126018e9b10fcdc090d3a38606958167d5923c7099b7Stefan Richter struct fw_cdev_send_request request; 126118e9b10fcdc090d3a38606958167d5923c7099b7Stefan Richter int dest; 1262f8c2287c65f8f72000102fc058232669e4540bc4Jay Fenlason 126318e9b10fcdc090d3a38606958167d5923c7099b7Stefan Richter if (p->speed > client->device->card->link_speed || 126418e9b10fcdc090d3a38606958167d5923c7099b7Stefan Richter p->length > 1024 << p->speed) 126518e9b10fcdc090d3a38606958167d5923c7099b7Stefan Richter return -EIO; 1266f8c2287c65f8f72000102fc058232669e4540bc4Jay Fenlason 126718e9b10fcdc090d3a38606958167d5923c7099b7Stefan Richter if (p->tag > 3 || p->channel > 63 || p->sy > 15) 126818e9b10fcdc090d3a38606958167d5923c7099b7Stefan Richter return -EINVAL; 126918e9b10fcdc090d3a38606958167d5923c7099b7Stefan Richter 127018e9b10fcdc090d3a38606958167d5923c7099b7Stefan Richter dest = fw_stream_packet_destination_id(p->tag, p->channel, p->sy); 127118e9b10fcdc090d3a38606958167d5923c7099b7Stefan Richter request.tcode = TCODE_STREAM_DATA; 127218e9b10fcdc090d3a38606958167d5923c7099b7Stefan Richter request.length = p->length; 127318e9b10fcdc090d3a38606958167d5923c7099b7Stefan Richter request.closure = p->closure; 127418e9b10fcdc090d3a38606958167d5923c7099b7Stefan Richter request.data = p->data; 127518e9b10fcdc090d3a38606958167d5923c7099b7Stefan Richter request.generation = p->generation; 127618e9b10fcdc090d3a38606958167d5923c7099b7Stefan Richter 127718e9b10fcdc090d3a38606958167d5923c7099b7Stefan Richter return init_request(client, &request, dest, p->speed); 1278f8c2287c65f8f72000102fc058232669e4540bc4Jay Fenlason} 1279f8c2287c65f8f72000102fc058232669e4540bc4Jay Fenlason 12804f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsbergstatic int (* const ioctl_handlers[])(struct client *client, void *buffer) = { 12814f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg ioctl_get_info, 12824f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg ioctl_send_request, 12834f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg ioctl_allocate, 12844f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg ioctl_deallocate, 12854f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg ioctl_send_response, 12864f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg ioctl_initiate_bus_reset, 12874f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg ioctl_add_descriptor, 12884f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg ioctl_remove_descriptor, 12894f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg ioctl_create_iso_context, 12904f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg ioctl_queue_iso, 12914f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg ioctl_start_iso, 12924f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg ioctl_stop_iso, 1293a64408b96b5f67c2778958a230b5cfa3408a4a81Stefan Richter ioctl_get_cycle_timer, 1294b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter ioctl_allocate_iso_resource, 1295b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter ioctl_deallocate_iso_resource, 12961ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter ioctl_allocate_iso_resource_once, 12971ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter ioctl_deallocate_iso_resource_once, 129833580a3ef5ba3bc0ee1b520df82a24bb37ce28f0Stefan Richter ioctl_get_speed, 1299acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter ioctl_send_broadcast_request, 1300f8c2287c65f8f72000102fc058232669e4540bc4Jay Fenlason ioctl_send_stream_packet, 13014f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg}; 13024f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg 130353dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic int dispatch_ioctl(struct client *client, 130453dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter unsigned int cmd, void __user *arg) 130519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{ 1306b2c0a2ac3e59f4764c59c23bd90b571e44256030Stefan Richter char buffer[sizeof(union { 1307b2c0a2ac3e59f4764c59c23bd90b571e44256030Stefan Richter struct fw_cdev_get_info _00; 1308b2c0a2ac3e59f4764c59c23bd90b571e44256030Stefan Richter struct fw_cdev_send_request _01; 1309b2c0a2ac3e59f4764c59c23bd90b571e44256030Stefan Richter struct fw_cdev_allocate _02; 1310b2c0a2ac3e59f4764c59c23bd90b571e44256030Stefan Richter struct fw_cdev_deallocate _03; 1311b2c0a2ac3e59f4764c59c23bd90b571e44256030Stefan Richter struct fw_cdev_send_response _04; 1312b2c0a2ac3e59f4764c59c23bd90b571e44256030Stefan Richter struct fw_cdev_initiate_bus_reset _05; 1313b2c0a2ac3e59f4764c59c23bd90b571e44256030Stefan Richter struct fw_cdev_add_descriptor _06; 1314b2c0a2ac3e59f4764c59c23bd90b571e44256030Stefan Richter struct fw_cdev_remove_descriptor _07; 1315b2c0a2ac3e59f4764c59c23bd90b571e44256030Stefan Richter struct fw_cdev_create_iso_context _08; 1316b2c0a2ac3e59f4764c59c23bd90b571e44256030Stefan Richter struct fw_cdev_queue_iso _09; 1317b2c0a2ac3e59f4764c59c23bd90b571e44256030Stefan Richter struct fw_cdev_start_iso _0a; 1318b2c0a2ac3e59f4764c59c23bd90b571e44256030Stefan Richter struct fw_cdev_stop_iso _0b; 1319b2c0a2ac3e59f4764c59c23bd90b571e44256030Stefan Richter struct fw_cdev_get_cycle_timer _0c; 1320b2c0a2ac3e59f4764c59c23bd90b571e44256030Stefan Richter struct fw_cdev_allocate_iso_resource _0d; 1321b2c0a2ac3e59f4764c59c23bd90b571e44256030Stefan Richter struct fw_cdev_send_stream_packet _13; 1322b2c0a2ac3e59f4764c59c23bd90b571e44256030Stefan Richter })]; 13232dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter int ret; 13244f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg 13254f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg if (_IOC_TYPE(cmd) != '#' || 13264f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg _IOC_NR(cmd) >= ARRAY_SIZE(ioctl_handlers)) 132719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg return -EINVAL; 13284f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg 13294f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg if (_IOC_DIR(cmd) & _IOC_WRITE) { 13302d826cc5c791bdc5f5651324c485746be9492be0Kristian Høgsberg if (_IOC_SIZE(cmd) > sizeof(buffer) || 13314f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg copy_from_user(buffer, arg, _IOC_SIZE(cmd))) 13324f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg return -EFAULT; 13334f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg } 13344f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg 13352dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter ret = ioctl_handlers[_IOC_NR(cmd)](client, buffer); 13362dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter if (ret < 0) 13372dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter return ret; 13384f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg 13394f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg if (_IOC_DIR(cmd) & _IOC_READ) { 13402d826cc5c791bdc5f5651324c485746be9492be0Kristian Høgsberg if (_IOC_SIZE(cmd) > sizeof(buffer) || 13414f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg copy_to_user(arg, buffer, _IOC_SIZE(cmd))) 13424f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg return -EFAULT; 134319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg } 13444f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg 13452dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter return ret; 134619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg} 134719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 134853dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic long fw_device_op_ioctl(struct file *file, 134953dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter unsigned int cmd, unsigned long arg) 135019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{ 135119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg struct client *client = file->private_data; 135219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 1353551f4cb9de716ffcdaf968c99a450c22ff12e8c3Jay Fenlason if (fw_device_is_shutdown(client->device)) 1354551f4cb9de716ffcdaf968c99a450c22ff12e8c3Jay Fenlason return -ENODEV; 1355551f4cb9de716ffcdaf968c99a450c22ff12e8c3Jay Fenlason 135619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg return dispatch_ioctl(client, cmd, (void __user *) arg); 135719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg} 135819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 135919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg#ifdef CONFIG_COMPAT 136053dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic long fw_device_op_compat_ioctl(struct file *file, 136153dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter unsigned int cmd, unsigned long arg) 136219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{ 136319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg struct client *client = file->private_data; 136419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 1365551f4cb9de716ffcdaf968c99a450c22ff12e8c3Jay Fenlason if (fw_device_is_shutdown(client->device)) 1366551f4cb9de716ffcdaf968c99a450c22ff12e8c3Jay Fenlason return -ENODEV; 1367551f4cb9de716ffcdaf968c99a450c22ff12e8c3Jay Fenlason 136819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg return dispatch_ioctl(client, cmd, compat_ptr(arg)); 136919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg} 137019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg#endif 137119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 137219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsbergstatic int fw_device_op_mmap(struct file *file, struct vm_area_struct *vma) 137319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{ 137419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg struct client *client = file->private_data; 13759aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg enum dma_data_direction direction; 13769aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg unsigned long size; 13772dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter int page_count, ret; 13789aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg 1379551f4cb9de716ffcdaf968c99a450c22ff12e8c3Jay Fenlason if (fw_device_is_shutdown(client->device)) 1380551f4cb9de716ffcdaf968c99a450c22ff12e8c3Jay Fenlason return -ENODEV; 1381551f4cb9de716ffcdaf968c99a450c22ff12e8c3Jay Fenlason 13829aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg /* FIXME: We could support multiple buffers, but we don't. */ 13839aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg if (client->buffer.pages != NULL) 13849aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg return -EBUSY; 13859aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg 13869aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg if (!(vma->vm_flags & VM_SHARED)) 13879aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg return -EINVAL; 138819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 13899aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg if (vma->vm_start & ~PAGE_MASK) 139019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg return -EINVAL; 139119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 139219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg client->vm_start = vma->vm_start; 13939aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg size = vma->vm_end - vma->vm_start; 13949aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg page_count = size >> PAGE_SHIFT; 13959aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg if (size & ~PAGE_MASK) 13969aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg return -EINVAL; 13979aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg 13989aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg if (vma->vm_flags & VM_WRITE) 13999aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg direction = DMA_TO_DEVICE; 14009aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg else 14019aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg direction = DMA_FROM_DEVICE; 14029aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg 14032dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter ret = fw_iso_buffer_init(&client->buffer, client->device->card, 14042dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter page_count, direction); 14052dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter if (ret < 0) 14062dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter return ret; 140719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 14082dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter ret = fw_iso_buffer_map(&client->buffer, vma); 14092dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter if (ret < 0) 14109aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg fw_iso_buffer_destroy(&client->buffer, client->device->card); 14119aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg 14122dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter return ret; 141319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg} 141419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 141545ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlasonstatic int shutdown_resource(int id, void *p, void *data) 141645ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason{ 1417e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter struct client_resource *resource = p; 141845ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason struct client *client = data; 141945ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason 1420e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter resource->release(client, resource); 1421fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter client_put(client); 142245ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason 142345ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason return 0; 142445ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason} 142545ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason 142619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsbergstatic int fw_device_op_release(struct inode *inode, struct file *file) 142719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{ 142819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg struct client *client = file->private_data; 1429e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter struct event *event, *next_event; 143019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 143197811e347310766030a648fdf0e407b2c91a39c1Stefan Richter mutex_lock(&client->device->client_list_mutex); 143297811e347310766030a648fdf0e407b2c91a39c1Stefan Richter list_del(&client->link); 143397811e347310766030a648fdf0e407b2c91a39c1Stefan Richter mutex_unlock(&client->device->client_list_mutex); 143497811e347310766030a648fdf0e407b2c91a39c1Stefan Richter 143519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg if (client->iso_context) 143619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg fw_iso_context_destroy(client->iso_context); 143719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 143836a755cfc398fc50abc74055d4478c1b067dac55Stefan Richter if (client->buffer.pages) 143936a755cfc398fc50abc74055d4478c1b067dac55Stefan Richter fw_iso_buffer_destroy(&client->buffer, client->device->card); 144036a755cfc398fc50abc74055d4478c1b067dac55Stefan Richter 144145ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason /* Freeze client->resource_idr and client->event_list */ 14423ba949868a6dc082b24cba5c3bf3f50de7391433Stefan Richter spin_lock_irq(&client->lock); 144345ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason client->in_shutdown = true; 14443ba949868a6dc082b24cba5c3bf3f50de7391433Stefan Richter spin_unlock_irq(&client->lock); 144566dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg 144645ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason idr_for_each(&client->resource_idr, shutdown_resource, client); 144745ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason idr_remove_all(&client->resource_idr); 144845ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason idr_destroy(&client->resource_idr); 144928cf6a04c82857d562968dc3a8a89726e6ac3dcbKristian Høgsberg 1450e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter list_for_each_entry_safe(event, next_event, &client->event_list, link) 1451e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter kfree(event); 145219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 1453fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter client_put(client); 145419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 145519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg return 0; 145619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg} 145719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 145819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsbergstatic unsigned int fw_device_op_poll(struct file *file, poll_table * pt) 145919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{ 146019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg struct client *client = file->private_data; 14612603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg unsigned int mask = 0; 146219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 146319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg poll_wait(file, &client->wait, pt); 146419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 14652603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg if (fw_device_is_shutdown(client->device)) 14662603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg mask |= POLLHUP | POLLERR; 146719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg if (!list_empty(&client->event_list)) 14682603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg mask |= POLLIN | POLLRDNORM; 14692603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg 14702603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg return mask; 147119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg} 147219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 147321ebcd1224d05c8673053e1e93ab9ec7ef3e0b84Stefan Richterconst struct file_operations fw_device_ops = { 147419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg .owner = THIS_MODULE, 147519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg .open = fw_device_op_open, 147619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg .read = fw_device_op_read, 147719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg .unlocked_ioctl = fw_device_op_ioctl, 147819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg .poll = fw_device_op_poll, 147919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg .release = fw_device_op_release, 148019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg .mmap = fw_device_op_mmap, 148119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg 148219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg#ifdef CONFIG_COMPAT 14835af4e5eab30d481f76b89a2167c873dfad960acbStefan Richter .compat_ioctl = fw_device_op_compat_ioctl, 148419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg#endif 148519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}; 1486