diff options
Diffstat (limited to 'drivers/dma/pxp/pxp_device.c')
-rw-r--r-- | drivers/dma/pxp/pxp_device.c | 878 |
1 files changed, 878 insertions, 0 deletions
diff --git a/drivers/dma/pxp/pxp_device.c b/drivers/dma/pxp/pxp_device.c new file mode 100644 index 000000000000..a4a65a45d30c --- /dev/null +++ b/drivers/dma/pxp/pxp_device.c @@ -0,0 +1,878 @@ +/* + * Copyright (C) 2010-2015 Freescale Semiconductor, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#include <linux/interrupt.h> +#include <linux/miscdevice.h> +#include <linux/platform_device.h> +#include <linux/fs.h> +#include <linux/slab.h> +#include <linux/uaccess.h> +#include <linux/delay.h> +#include <linux/dmaengine.h> +#include <linux/dma-mapping.h> +#include <linux/sched.h> +#include <linux/module.h> +#include <linux/pxp_device.h> +#include <linux/atomic.h> +#include <linux/platform_data/dma-imx.h> + +#define BUFFER_HASH_ORDER 4 + +static struct pxp_buffer_hash bufhash; +static struct pxp_irq_info irq_info[NR_PXP_VIRT_CHANNEL]; + +static int pxp_ht_create(struct pxp_buffer_hash *hash, int order) +{ + unsigned long i; + unsigned long table_size; + + table_size = 1U << order; + + hash->order = order; + hash->hash_table = kmalloc(sizeof(*hash->hash_table) * table_size, GFP_KERNEL); + + if (!hash->hash_table) { + pr_err("%s: Out of memory for hash table\n", __func__); + return -ENOMEM; + } + + for (i = 0; i < table_size; i++) + INIT_HLIST_HEAD(&hash->hash_table[i]); + + return 0; +} + +static int pxp_ht_insert_item(struct pxp_buffer_hash *hash, + struct pxp_buf_obj *new) +{ + unsigned long hashkey; + struct hlist_head *h_list; + + hashkey = hash_long(new->offset >> PAGE_SHIFT, hash->order); + h_list = &hash->hash_table[hashkey]; + + spin_lock(&hash->hash_lock); + hlist_add_head_rcu(&new->item, h_list); + spin_unlock(&hash->hash_lock); + + return 0; +} + +static int pxp_ht_remove_item(struct pxp_buffer_hash *hash, + struct pxp_buf_obj *obj) +{ + spin_lock(&hash->hash_lock); + hlist_del_init_rcu(&obj->item); + spin_unlock(&hash->hash_lock); + return 0; +} + +static struct hlist_node *pxp_ht_find_key(struct pxp_buffer_hash *hash, + unsigned long key) +{ + struct pxp_buf_obj *entry; + struct hlist_head *h_list; + unsigned long hashkey; + + hashkey = hash_long(key, hash->order); + h_list = &hash->hash_table[hashkey]; + + hlist_for_each_entry_rcu(entry, h_list, item) { + if (entry->offset >> PAGE_SHIFT == key) + return &entry->item; + } + + return NULL; +} + +static void pxp_ht_destroy(struct pxp_buffer_hash *hash) +{ + kfree(hash->hash_table); + hash->hash_table = NULL; +} + +static int pxp_buffer_handle_create(struct pxp_file *file_priv, + struct pxp_buf_obj *obj, + uint32_t *handlep) +{ + int ret; + + idr_preload(GFP_KERNEL); + spin_lock(&file_priv->buffer_lock); + + ret = idr_alloc(&file_priv->buffer_idr, obj, 1, 0, GFP_NOWAIT); + + spin_unlock(&file_priv->buffer_lock); + idr_preload_end(); + + if (ret < 0) + return ret; + + *handlep = ret; + + return 0; +} + +static struct pxp_buf_obj * +pxp_buffer_object_lookup(struct pxp_file *file_priv, + uint32_t handle) +{ + struct pxp_buf_obj *obj; + + spin_lock(&file_priv->buffer_lock); + + obj = idr_find(&file_priv->buffer_idr, handle); + if (!obj) { + spin_unlock(&file_priv->buffer_lock); + return NULL; + } + + spin_unlock(&file_priv->buffer_lock); + + return obj; +} + +static int pxp_buffer_handle_delete(struct pxp_file *file_priv, + uint32_t handle) +{ + struct pxp_buf_obj *obj; + + spin_lock(&file_priv->buffer_lock); + + obj = idr_find(&file_priv->buffer_idr, handle); + if (!obj) { + spin_unlock(&file_priv->buffer_lock); + return -EINVAL; + } + + idr_remove(&file_priv->buffer_idr, handle); + spin_unlock(&file_priv->buffer_lock); + + return 0; +} + +static int pxp_channel_handle_create(struct pxp_file *file_priv, + struct pxp_chan_obj *obj, + uint32_t *handlep) +{ + int ret; + + idr_preload(GFP_KERNEL); + spin_lock(&file_priv->channel_lock); + + ret = idr_alloc(&file_priv->channel_idr, obj, 0, 0, GFP_NOWAIT); + + spin_unlock(&file_priv->channel_lock); + idr_preload_end(); + + if (ret < 0) + return ret; + + *handlep = ret; + + return 0; +} + +static struct pxp_chan_obj * +pxp_channel_object_lookup(struct pxp_file *file_priv, + uint32_t handle) +{ + struct pxp_chan_obj *obj; + + spin_lock(&file_priv->channel_lock); + + obj = idr_find(&file_priv->channel_idr, handle); + if (!obj) { + spin_unlock(&file_priv->channel_lock); + return NULL; + } + + spin_unlock(&file_priv->channel_lock); + + return obj; +} + +static int pxp_channel_handle_delete(struct pxp_file *file_priv, + uint32_t handle) +{ + struct pxp_chan_obj *obj; + + spin_lock(&file_priv->channel_lock); + + obj = idr_find(&file_priv->channel_idr, handle); + if (!obj) { + spin_unlock(&file_priv->channel_lock); + return -EINVAL; + } + + idr_remove(&file_priv->channel_idr, handle); + spin_unlock(&file_priv->channel_lock); + + return 0; +} + +static int pxp_alloc_dma_buffer(struct pxp_buf_obj *obj) +{ + obj->virtual = dma_alloc_coherent(NULL, PAGE_ALIGN(obj->size), + (dma_addr_t *) (&obj->offset), + GFP_DMA | GFP_KERNEL); + pr_debug("[ALLOC] mem alloc phys_addr = 0x%lx\n", obj->offset); + + if (obj->virtual == NULL) { + printk(KERN_ERR "Physical memory allocation error!\n"); + return -1; + } + + return 0; +} + +static void pxp_free_dma_buffer(struct pxp_buf_obj *obj) +{ + if (obj->virtual != NULL) { + dma_free_coherent(0, PAGE_ALIGN(obj->size), + obj->virtual, (dma_addr_t)obj->offset); + } +} + +static int +pxp_buffer_object_free(int id, void *ptr, void *data) +{ + struct pxp_file *file_priv = data; + struct pxp_buf_obj *obj = ptr; + int ret; + + ret = pxp_buffer_handle_delete(file_priv, obj->handle); + if (ret < 0) + return ret; + + pxp_ht_remove_item(&bufhash, obj); + pxp_free_dma_buffer(obj); + kfree(obj); + + return 0; +} + +static int +pxp_channel_object_free(int id, void *ptr, void *data) +{ + struct pxp_file *file_priv = data; + struct pxp_chan_obj *obj = ptr; + int chan_id; + + chan_id = obj->chan->chan_id; + wait_event(irq_info[chan_id].waitq, + atomic_read(&irq_info[chan_id].irq_pending) == 0); + + pxp_channel_handle_delete(file_priv, obj->handle); + dma_release_channel(obj->chan); + kfree(obj); + + return 0; +} + +static void pxp_free_buffers(struct pxp_file *file_priv) +{ + idr_for_each(&file_priv->buffer_idr, + &pxp_buffer_object_free, file_priv); + idr_destroy(&file_priv->buffer_idr); +} + +static void pxp_free_channels(struct pxp_file *file_priv) +{ + idr_for_each(&file_priv->channel_idr, + &pxp_channel_object_free, file_priv); + idr_destroy(&file_priv->channel_idr); +} + +/* Callback function triggered after PxP receives an EOF interrupt */ +static void pxp_dma_done(void *arg) +{ + struct pxp_tx_desc *tx_desc = to_tx_desc(arg); + struct dma_chan *chan = tx_desc->txd.chan; + struct pxp_channel *pxp_chan = to_pxp_channel(chan); + int chan_id = pxp_chan->dma_chan.chan_id; + + pr_debug("DMA Done ISR, chan_id %d\n", chan_id); + + atomic_dec(&irq_info[chan_id].irq_pending); + irq_info[chan_id].hist_status = tx_desc->hist_status; + + wake_up(&(irq_info[chan_id].waitq)); +} + +static int pxp_ioc_config_chan(struct pxp_file *priv, unsigned long arg) +{ + struct scatterlist *sg; + struct pxp_tx_desc *desc; + struct dma_async_tx_descriptor *txd; + struct pxp_config_data *pxp_conf; + dma_cookie_t cookie; + int handle, chan_id; + struct dma_chan *chan; + struct pxp_chan_obj *obj; + int i = 0, j = 0, k = 0, m = 0, length, ret, sg_len; + + pxp_conf = kzalloc(sizeof(*pxp_conf), GFP_KERNEL); + if (!pxp_conf) + return -ENOMEM; + + ret = copy_from_user(pxp_conf, + (struct pxp_config_data *)arg, + sizeof(struct pxp_config_data)); + if (ret) { + kfree(pxp_conf); + return -EFAULT; + } + + handle = pxp_conf->handle; + obj = pxp_channel_object_lookup(priv, handle); + if (!obj) { + kfree(pxp_conf); + return -EINVAL; + } + chan = obj->chan; + chan_id = chan->chan_id; + + sg_len = 3; + if (pxp_conf->proc_data.engine_enable & PXP_ENABLE_WFE_A) + sg_len += 4; + if (pxp_conf->proc_data.engine_enable & PXP_ENABLE_WFE_B) + sg_len += 4; + if (pxp_conf->proc_data.engine_enable & PXP_ENABLE_DITHER) + sg_len += 4; + + sg = kmalloc(sizeof(*sg) * sg_len, GFP_KERNEL); + if (!sg) { + kfree(pxp_conf); + return -ENOMEM; + } + + sg_init_table(sg, sg_len); + + txd = chan->device->device_prep_slave_sg(chan, + sg, sg_len, + DMA_TO_DEVICE, + DMA_PREP_INTERRUPT, + NULL); + if (!txd) { + pr_err("Error preparing a DMA transaction descriptor.\n"); + kfree(pxp_conf); + kfree(sg); + return -EIO; + } + + txd->callback_param = txd; + txd->callback = pxp_dma_done; + + desc = to_tx_desc(txd); + + length = desc->len; + for (i = 0; i < length; i++) { + if (i == 0) { /* S0 */ + memcpy(&desc->proc_data, + &pxp_conf->proc_data, + sizeof(struct pxp_proc_data)); + memcpy(&desc->layer_param.s0_param, + &pxp_conf->s0_param, + sizeof(struct pxp_layer_param)); + desc = desc->next; + } else if (i == 1) { /* Output */ + memcpy(&desc->layer_param.out_param, + &pxp_conf->out_param, + sizeof(struct pxp_layer_param)); + desc = desc->next; + } else if (i == 2) { + /* OverLay */ + memcpy(&desc->layer_param.ol_param, + &pxp_conf->ol_param, + sizeof(struct pxp_layer_param)); + desc = desc->next; + } else if ((pxp_conf->proc_data.engine_enable & PXP_ENABLE_WFE_A) && (j < 4)) { + for (j = 0; j < 4; j++) { + if (j == 0) { + memcpy(&desc->layer_param.processing_param, + &pxp_conf->wfe_a_fetch_param[0], + sizeof(struct pxp_layer_param)); + desc->layer_param.processing_param.flag = PXP_BUF_FLAG_WFE_A_FETCH0; + } else if (j == 1) { + memcpy(&desc->layer_param.processing_param, + &pxp_conf->wfe_a_fetch_param[1], + sizeof(struct pxp_layer_param)); + desc->layer_param.processing_param.flag = PXP_BUF_FLAG_WFE_A_FETCH1; + } else if (j == 2) { + memcpy(&desc->layer_param.processing_param, + &pxp_conf->wfe_a_store_param[0], + sizeof(struct pxp_layer_param)); + desc->layer_param.processing_param.flag = PXP_BUF_FLAG_WFE_A_STORE0; + } else if (j == 3) { + memcpy(&desc->layer_param.processing_param, + &pxp_conf->wfe_a_store_param[1], + sizeof(struct pxp_layer_param)); + desc->layer_param.processing_param.flag = PXP_BUF_FLAG_WFE_A_STORE1; + } + + desc = desc->next; + } + + i += 4; + + } else if ((pxp_conf->proc_data.engine_enable & PXP_ENABLE_WFE_B) && (m < 4)) { + for (m = 0; m < 4; m++) { + if (m == 0) { + memcpy(&desc->layer_param.processing_param, + &pxp_conf->wfe_b_fetch_param[0], + sizeof(struct pxp_layer_param)); + desc->layer_param.processing_param.flag = PXP_BUF_FLAG_WFE_B_FETCH0; + } else if (m == 1) { + memcpy(&desc->layer_param.processing_param, + &pxp_conf->wfe_b_fetch_param[1], + sizeof(struct pxp_layer_param)); + desc->layer_param.processing_param.flag = PXP_BUF_FLAG_WFE_B_FETCH1; + } else if (m == 2) { + memcpy(&desc->layer_param.processing_param, + &pxp_conf->wfe_b_store_param[0], + sizeof(struct pxp_layer_param)); + desc->layer_param.processing_param.flag = PXP_BUF_FLAG_WFE_B_STORE0; + } else if (m == 3) { + memcpy(&desc->layer_param.processing_param, + &pxp_conf->wfe_b_store_param[1], + sizeof(struct pxp_layer_param)); + desc->layer_param.processing_param.flag = PXP_BUF_FLAG_WFE_B_STORE1; + } + + desc = desc->next; + } + + i += 4; + + } else if ((pxp_conf->proc_data.engine_enable & PXP_ENABLE_DITHER) && (k < 4)) { + for (k = 0; k < 4; k++) { + if (k == 0) { + memcpy(&desc->layer_param.processing_param, + &pxp_conf->dither_fetch_param[0], + sizeof(struct pxp_layer_param)); + desc->layer_param.processing_param.flag = PXP_BUF_FLAG_DITHER_FETCH0; + } else if (k == 1) { + memcpy(&desc->layer_param.processing_param, + &pxp_conf->dither_fetch_param[1], + sizeof(struct pxp_layer_param)); + desc->layer_param.processing_param.flag = PXP_BUF_FLAG_DITHER_FETCH1; + } else if (k == 2) { + memcpy(&desc->layer_param.processing_param, + &pxp_conf->dither_store_param[0], + sizeof(struct pxp_layer_param)); + desc->layer_param.processing_param.flag = PXP_BUF_FLAG_DITHER_STORE0; + } else if (k == 3) { + memcpy(&desc->layer_param.processing_param, + &pxp_conf->dither_store_param[1], + sizeof(struct pxp_layer_param)); + desc->layer_param.processing_param.flag = PXP_BUF_FLAG_DITHER_STORE1; + } + + desc = desc->next; + } + + i += 4; + } + } + + cookie = txd->tx_submit(txd); + if (cookie < 0) { + pr_err("Error tx_submit\n"); + kfree(pxp_conf); + kfree(sg); + return -EIO; + } + + atomic_inc(&irq_info[chan_id].irq_pending); + + kfree(pxp_conf); + kfree(sg); + + return 0; +} + +static int pxp_device_open(struct inode *inode, struct file *filp) +{ + struct pxp_file *priv; + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + + if (!priv) + return -ENOMEM; + + filp->private_data = priv; + priv->filp = filp; + + idr_init(&priv->buffer_idr); + spin_lock_init(&priv->buffer_lock); + + idr_init(&priv->channel_idr); + spin_lock_init(&priv->channel_lock); + + return 0; +} + +static int pxp_device_release(struct inode *inode, struct file *filp) +{ + struct pxp_file *priv = filp->private_data; + + if (priv) { + pxp_free_channels(priv); + pxp_free_buffers(priv); + kfree(priv); + filp->private_data = NULL; + } + + return 0; +} + +static int pxp_device_mmap(struct file *file, struct vm_area_struct *vma) +{ + int request_size; + struct hlist_node *node; + struct pxp_buf_obj *obj; + + request_size = vma->vm_end - vma->vm_start; + + pr_debug("start=0x%x, pgoff=0x%x, size=0x%x\n", + (unsigned int)(vma->vm_start), (unsigned int)(vma->vm_pgoff), + request_size); + + node = pxp_ht_find_key(&bufhash, vma->vm_pgoff); + if (!node) + return -EINVAL; + + obj = list_entry(node, struct pxp_buf_obj, item); + if (obj->offset + (obj->size >> PAGE_SHIFT) < + (vma->vm_pgoff + vma_pages(vma))) + return -ENOMEM; + + switch (obj->mem_type) { + case MEMORY_TYPE_UNCACHED: + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + break; + case MEMORY_TYPE_WC: + vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); + break; + case MEMORY_TYPE_CACHED: + break; + default: + pr_err("%s: invalid memory type!\n", __func__); + return -EINVAL; + } + + return remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff, + request_size, vma->vm_page_prot) ? -EAGAIN : 0; +} + +static bool chan_filter(struct dma_chan *chan, void *arg) +{ + if (imx_dma_is_pxp(chan)) + return true; + else + return false; +} + +static long pxp_device_ioctl(struct file *filp, + unsigned int cmd, unsigned long arg) +{ + int ret = 0; + struct pxp_file *file_priv = filp->private_data; + + switch (cmd) { + case PXP_IOC_GET_CHAN: + { + int ret; + struct dma_chan *chan = NULL; + dma_cap_mask_t mask; + struct pxp_chan_obj *obj = NULL; + + pr_debug("drv: PXP_IOC_GET_CHAN Line %d\n", __LINE__); + + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + dma_cap_set(DMA_PRIVATE, mask); + + chan = dma_request_channel(mask, chan_filter, NULL); + if (!chan) { + pr_err("Unsccessfully received channel!\n"); + return -EBUSY; + } + + pr_debug("Successfully received channel." + "chan_id %d\n", chan->chan_id); + + obj = kzalloc(sizeof(*obj), GFP_KERNEL); + if (!obj) { + dma_release_channel(chan); + return -ENOMEM; + } + obj->chan = chan; + + ret = pxp_channel_handle_create(file_priv, obj, + &obj->handle); + if (ret) { + dma_release_channel(chan); + kfree(obj); + return ret; + } + + init_waitqueue_head(&(irq_info[chan->chan_id].waitq)); + if (put_user(obj->handle, (u32 __user *) arg)) { + pxp_channel_handle_delete(file_priv, obj->handle); + dma_release_channel(chan); + kfree(obj); + return -EFAULT; + } + + break; + } + case PXP_IOC_PUT_CHAN: + { + int handle; + struct pxp_chan_obj *obj; + + if (get_user(handle, (u32 __user *) arg)) + return -EFAULT; + + pr_debug("%d release handle %d\n", __LINE__, handle); + + obj = pxp_channel_object_lookup(file_priv, handle); + if (!obj) + return -EINVAL; + + pxp_channel_handle_delete(file_priv, obj->handle); + dma_release_channel(obj->chan); + kfree(obj); + + break; + } + case PXP_IOC_CONFIG_CHAN: + { + int ret; + + ret = pxp_ioc_config_chan(file_priv, arg); + if (ret) + return ret; + + break; + } + case PXP_IOC_START_CHAN: + { + int handle; + struct pxp_chan_obj *obj = NULL; + + if (get_user(handle, (u32 __user *) arg)) + return -EFAULT; + + obj = pxp_channel_object_lookup(file_priv, handle); + if (!obj) + return -EINVAL; + + dma_async_issue_pending(obj->chan); + + break; + } + case PXP_IOC_GET_PHYMEM: + { + struct pxp_mem_desc buffer; + struct pxp_buf_obj *obj; + + ret = copy_from_user(&buffer, + (struct pxp_mem_desc *)arg, + sizeof(struct pxp_mem_desc)); + if (ret) + return -EFAULT; + + pr_debug("[ALLOC] mem alloc size = 0x%x\n", + buffer.size); + + obj = kzalloc(sizeof(*obj), GFP_KERNEL); + if (!obj) + return -ENOMEM; + obj->size = buffer.size; + obj->mem_type = buffer.mtype; + + ret = pxp_alloc_dma_buffer(obj); + if (ret == -1) { + printk(KERN_ERR + "Physical memory allocation error!\n"); + kfree(obj); + return ret; + } + + ret = pxp_buffer_handle_create(file_priv, obj, &obj->handle); + if (ret) { + pxp_free_dma_buffer(obj); + kfree(obj); + return ret; + } + buffer.handle = obj->handle; + buffer.phys_addr = obj->offset; + + ret = copy_to_user((void __user *)arg, &buffer, + sizeof(struct pxp_mem_desc)); + if (ret) { + pxp_buffer_handle_delete(file_priv, buffer.handle); + pxp_free_dma_buffer(obj); + kfree(obj); + return -EFAULT; + } + + pxp_ht_insert_item(&bufhash, obj); + + break; + } + case PXP_IOC_PUT_PHYMEM: + { + struct pxp_mem_desc pxp_mem; + struct pxp_buf_obj *obj; + + ret = copy_from_user(&pxp_mem, + (struct pxp_mem_desc *)arg, + sizeof(struct pxp_mem_desc)); + if (ret) + return -EACCES; + + obj = pxp_buffer_object_lookup(file_priv, pxp_mem.handle); + if (!obj) + return -EINVAL; + + ret = pxp_buffer_handle_delete(file_priv, obj->handle); + if (ret) + return ret; + + pxp_ht_remove_item(&bufhash, obj); + pxp_free_dma_buffer(obj); + kfree(obj); + + break; + } + case PXP_IOC_FLUSH_PHYMEM: + { + int ret; + struct pxp_mem_flush flush; + struct pxp_buf_obj *obj; + + ret = copy_from_user(&flush, + (struct pxp_mem_flush *)arg, + sizeof(struct pxp_mem_flush)); + if (ret) + return -EACCES; + + obj = pxp_buffer_object_lookup(file_priv, flush.handle); + if (!obj) + return -EINVAL; + + switch (flush.type) { + case CACHE_CLEAN: + dma_sync_single_for_device(NULL, obj->offset, + obj->size, DMA_TO_DEVICE); + break; + case CACHE_INVALIDATE: + dma_sync_single_for_device(NULL, obj->offset, + obj->size, DMA_FROM_DEVICE); + break; + case CACHE_FLUSH: + dma_sync_single_for_device(NULL, obj->offset, + obj->size, DMA_TO_DEVICE); + dma_sync_single_for_device(NULL, obj->offset, + obj->size, DMA_FROM_DEVICE); + break; + default: + pr_err("%s: invalid cache flush type\n", __func__); + return -EINVAL; + } + + break; + } + case PXP_IOC_WAIT4CMPLT: + { + struct pxp_chan_handle chan_handle; + int ret, chan_id, handle; + struct pxp_chan_obj *obj = NULL; + + ret = copy_from_user(&chan_handle, + (struct pxp_chan_handle *)arg, + sizeof(struct pxp_chan_handle)); + if (ret) + return -EFAULT; + + handle = chan_handle.handle; + obj = pxp_channel_object_lookup(file_priv, handle); + if (!obj) + return -EINVAL; + chan_id = obj->chan->chan_id; + + ret = wait_event_interruptible + (irq_info[chan_id].waitq, + (atomic_read(&irq_info[chan_id].irq_pending) == 0)); + if (ret < 0) + return -ERESTARTSYS; + + chan_handle.hist_status = irq_info[chan_id].hist_status; + ret = copy_to_user((struct pxp_chan_handle *)arg, + &chan_handle, + sizeof(struct pxp_chan_handle)); + if (ret) + return -EFAULT; + break; + } + default: + break; + } + + return 0; +} + +static const struct file_operations pxp_device_fops = { + .open = pxp_device_open, + .release = pxp_device_release, + .unlocked_ioctl = pxp_device_ioctl, + .mmap = pxp_device_mmap, +}; + +static struct miscdevice pxp_device_miscdev = { + .minor = MISC_DYNAMIC_MINOR, + .name = "pxp_device", + .fops = &pxp_device_fops, +}; + +int register_pxp_device(void) +{ + int ret; + + ret = misc_register(&pxp_device_miscdev); + if (ret) + return ret; + + ret = pxp_ht_create(&bufhash, BUFFER_HASH_ORDER); + if (ret) + return ret; + spin_lock_init(&(bufhash.hash_lock)); + + pr_debug("PxP_Device registered Successfully\n"); + return 0; +} + +void unregister_pxp_device(void) +{ + pxp_ht_destroy(&bufhash); + misc_deregister(&pxp_device_miscdev); +} |