V4L/DVB (8258): add support for SMS1010 and SMS1150 based digital television devices
[pandora-kernel.git] / drivers / media / mdtv / smschar.c
1 /*!
2
3         \file           smschar.c
4
5         \brief          Implementation of smscore client for cdev based access
6
7     \par                Copyright (c), 2005-2008 Siano Mobile Silicon, Inc.
8
9     \par                This program is free software; you can redistribute it and/or modify
10                         it under the terms of the GNU General Public License version 3 as
11                         published by the Free Software Foundation;
12
13                         Software distributed under the License is distributed on an "AS
14                         IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
15                         implied.
16
17         \author         Anatoly Greenblat
18
19 */
20
21 #include <linux/module.h>
22 #include <linux/moduleparam.h>
23 #include <linux/init.h>
24
25 #include <linux/kernel.h>               /* printk() */
26 #include <linux/fs.h>                   /* everything... */
27 #include <linux/types.h>                /* size_t */
28 #include <linux/cdev.h>
29 #include <linux/sched.h>
30 #include <asm/system.h>                 /* cli(), *_flags */
31 #include <asm/uaccess.h>                /* copy_*_user */
32
33 #include "smskdefs.h" // page, scatterlist, kmutex
34 #include "smscoreapi.h"
35 #include "smstypes.h"
36
37 #include "smscharioctl.h"
38
39 #define SMS_CHR_MAX_Q_LEN       10 // max number of packets allowed to be pending on queue
40 #define SMSCHAR_NR_DEVS         7
41
42 typedef struct _smschar_device
43 {
44         struct cdev                     cdev;                           //!<  Char device structure - kernel's device model representation
45
46         wait_queue_head_t       waitq;                                  /* Processes waiting */
47         spinlock_t                      lock;                           //!< critical section
48         int                                     pending_count;
49         struct list_head        pending_data;           //!< list of pending data
50
51         smscore_buffer_t        *currentcb;
52
53         int                                     device_index;
54
55         smscore_device_t        *coredev;
56         smscore_client_t        *smsclient;
57 } smschar_device_t;
58
59 //!  Holds the major number of the device node. may be changed at load time.
60 int smschar_major = 251;
61
62 //!  Holds the first minor number of the device node. may be changed at load time.
63 int smschar_minor = 0;
64
65 // macros that allow the load time parameters change
66 module_param ( smschar_major, int, S_IRUGO );
67 module_param ( smschar_minor, int, S_IRUGO );
68
69 #ifdef SMSCHAR_DEBUG
70
71         #undef PERROR
72 #  define PERROR(fmt, args...) printk( KERN_INFO "smschar error: line %d- %s(): " fmt,__LINE__,  __FUNCTION__, ## args)
73         #undef PWARNING
74 #  define PWARNING(fmt, args...) printk( KERN_INFO "smschar warning: line %d- %s(): " fmt,__LINE__,  __FUNCTION__, ## args)
75         #undef PDEBUG                                   /* undef it, just in case */
76 #  define PDEBUG(fmt, args...)  printk( KERN_INFO "smschar - %s(): " fmt, __FUNCTION__, ## args)
77
78 #else /* not debugging: nothing */
79
80         #define PDEBUG(fmt, args...)
81         #define PERROR(fmt, args...)
82         #define PWARNING(fmt, args...)
83
84 #endif
85
86 smschar_device_t smschar_devices[SMSCHAR_NR_DEVS];
87 static int g_smschar_inuse = 0;
88
89 /**
90  * unregisters sms client and returns all queued buffers
91  *
92  * @param dev pointer to the client context (smschar parameters block)
93  *
94  */
95 void smschar_unregister_client(smschar_device_t* dev)
96 {
97         unsigned long flags;
98
99         if (dev->coredev && dev->smsclient)
100         {
101                 wake_up_interruptible(&dev->waitq);
102
103                 spin_lock_irqsave(&dev->lock, flags);
104
105                 while (!list_empty(&dev->pending_data))
106                 {
107                         smscore_buffer_t *cb = (smscore_buffer_t *) dev->pending_data.next;
108                         list_del(&cb->entry);
109
110                         smscore_putbuffer(dev->coredev, cb);
111
112                         dev->pending_count --;
113                 }
114
115                 if (dev->currentcb)
116                 {
117                         smscore_putbuffer(dev->coredev, dev->currentcb);
118                         dev->currentcb = NULL;
119                         dev->pending_count --;
120                 }
121
122                 smscore_unregister_client(dev->smsclient);
123                 dev->smsclient = NULL;
124
125                 spin_unlock_irqrestore(&dev->lock, flags);
126         }
127 }
128
129 /**
130  * queues incoming buffers into buffers queue
131  *
132  * @param context pointer to the client context (smschar parameters block)
133  * @param cb pointer to incoming buffer descriptor
134  *
135  * @return 0 on success, <0 on queue overflow.
136  */
137 int smschar_onresponse(void *context, smscore_buffer_t *cb)
138 {
139         smschar_device_t *dev = context;
140         unsigned long flags;
141
142         spin_lock_irqsave(&dev->lock, flags);
143
144         if (dev->pending_count > SMS_CHR_MAX_Q_LEN)
145         {
146                 spin_unlock_irqrestore(&dev->lock, flags);
147                 return -EBUSY;
148         }
149
150         dev->pending_count ++;
151
152         // if data channel, remove header
153         if (dev->device_index)
154         {
155                 cb->size -= sizeof(SmsMsgHdr_ST);
156                 cb->offset += sizeof(SmsMsgHdr_ST);
157         }
158
159         list_add_tail(&cb->entry, &dev->pending_data);
160         spin_unlock_irqrestore(&dev->lock, flags);
161
162         if (waitqueue_active(&dev->waitq))
163                 wake_up_interruptible(&dev->waitq);
164
165         return 0;
166 }
167
168 /**
169  * handles device removal event
170  *
171  * @param context pointer to the client context (smschar parameters block)
172  *
173  */
174 void smschar_onremove(void *context)
175 {
176         smschar_device_t *dev = (smschar_device_t *) context;
177
178         smschar_unregister_client(dev);
179         dev->coredev = NULL;
180 }
181
182 /**
183  * registers client associated with the node
184  *
185  * @param inode Inode concerned.
186  * @param file File concerned.
187  *
188  * @return 0 on success, <0 on error.
189  */
190 int smschar_open (struct inode *inode, struct file *file)
191 {
192         smschar_device_t *dev = container_of(inode->i_cdev, smschar_device_t, cdev);
193         int rc = -ENODEV;
194
195         PDEBUG("entering index %d\n", dev->device_index);
196
197         if (dev->coredev)
198         {
199                 smsclient_params_t params;
200
201                 params.initial_id = dev->device_index ? dev->device_index : SMS_HOST_LIB;
202                 params.data_type = dev->device_index ? MSG_SMS_DAB_CHANNEL : 0;
203                 params.onresponse_handler = smschar_onresponse;
204                 params.onremove_handler = smschar_onremove;
205                 params.context = dev;
206
207                 rc = smscore_register_client(dev->coredev, &params, &dev->smsclient);
208                 if (!rc)
209                 {
210                         file->private_data = dev;
211                 }
212         }
213
214         PDEBUG("exiting, rc %d\n", rc);
215
216         return rc;
217 }
218
219 /**
220  * unregisters client associated with the node
221  *
222  * @param inode Inode concerned.
223  * @param file File concerned.
224  *
225  */
226 int smschar_release(struct inode *inode, struct file *file)
227 {
228         smschar_unregister_client(file->private_data);
229
230         PDEBUG("exiting\n");
231
232         return 0;
233 }
234
235 /**
236  * copies data from buffers in incoming queue into a user buffer
237  *
238  * @param file File structure.
239  * @param buf Source buffer.
240  * @param count Size of source buffer.
241  * @param f_pos Position in file (ignored).
242  *
243  * @return Number of bytes read, or <0 on error.
244  */
245 ssize_t smschar_read ( struct file * file, char __user * buf, size_t count, loff_t * f_pos )
246 {
247         smschar_device_t *dev = file->private_data;
248         unsigned long flags;
249         int copied = 0;
250
251         if (!dev->coredev || !dev->smsclient)
252         {
253                 PERROR("no client\n");
254                 return -ENODEV;
255         }
256
257         while (copied != count)
258         {
259                 if (0 > wait_event_interruptible(dev->waitq, !list_empty(&dev->pending_data)))
260                 {
261                         PERROR("wait_event_interruptible error\n");
262                         return -ENODEV;
263                 }
264
265                 if (!dev->smsclient)
266                 {
267                         PERROR("no client\n");
268                         return -ENODEV;
269                 }
270
271                 spin_lock_irqsave(&dev->lock, flags);
272
273                 while (!list_empty(&dev->pending_data) && (copied != count))
274                 {
275                         smscore_buffer_t *cb = (smscore_buffer_t *) dev->pending_data.next;
276                         int actual_size = min(((int) count - copied), cb->size);
277
278                         copy_to_user(&buf[copied], &((char*)cb->p)[cb->offset], actual_size);
279
280                         copied += actual_size;
281                         cb->offset += actual_size;
282                         cb->size -= actual_size;
283
284                         if (!cb->size)
285                         {
286                                 list_del(&cb->entry);
287                                 smscore_putbuffer(dev->coredev, cb);
288
289                                 dev->pending_count --;
290                         }
291                 }
292
293                 spin_unlock_irqrestore(&dev->lock, flags);
294         }
295
296         return copied;
297 }
298
299 /**
300  * sends the buffer to the associated device
301  *
302  * @param file File structure.
303  * @param buf Source buffer.
304  * @param count Size of source buffer.
305  * @param f_pos Position in file (ignored).
306  *
307  * @return Number of bytes read, or <0 on error.
308  */
309 ssize_t smschar_write(struct file *file, const char __user *buf, size_t count, loff_t *f_pos)
310 {
311         smschar_device_t *dev = file->private_data;
312         void *buffer;
313
314         if (!dev->smsclient)
315         {
316                 PERROR("no client\n");
317                 return -ENODEV;
318         }
319
320         buffer = kmalloc(ALIGN(count, SMS_ALLOC_ALIGNMENT) + SMS_DMA_ALIGNMENT, GFP_KERNEL | GFP_DMA);
321         if (buffer)
322         {
323                 void *msg_buffer = (void*) SMS_ALIGN_ADDRESS(buffer);
324
325                 if (!copy_from_user(msg_buffer, buf, count))
326                         smsclient_sendrequest(dev->smsclient, msg_buffer, count);
327                 else
328                         count = 0;
329
330                 kfree(buffer);
331         }
332
333         return count;
334 }
335
336 int smschar_mmap(struct file *file, struct vm_area_struct *vma)
337 {
338         smschar_device_t *dev = file->private_data;
339         return smscore_map_common_buffer(dev->coredev, vma);
340 }
341
342 /**
343  * waits until buffer inserted into a queue. when inserted buffer offset are reported
344  * to the calling process. previously reported buffer is returned to smscore pool
345  *
346  * @param dev pointer to smschar parameters block
347  * @param touser pointer to a structure that receives incoming buffer offsets
348  *
349  * @return 0 on success, <0 on error.
350  */
351 int smschar_wait_get_buffer(smschar_device_t* dev, smschar_buffer_t* touser)
352 {
353         unsigned long flags;
354         int rc;
355
356         spin_lock_irqsave(&dev->lock, flags);
357
358         if (dev->currentcb)
359         {
360                 smscore_putbuffer(dev->coredev, dev->currentcb);
361                 dev->currentcb = NULL;
362                 dev->pending_count --;
363         }
364
365         spin_unlock_irqrestore(&dev->lock, flags);
366
367         rc = wait_event_interruptible(dev->waitq, !list_empty(&dev->pending_data));
368         if (rc < 0)
369         {
370                 PERROR("wait_event_interruptible error\n");
371                 return rc;
372         }
373
374         if (!dev->smsclient)
375         {
376                 PERROR("no client\n");
377                 return -ENODEV;
378         }
379
380         spin_lock_irqsave(&dev->lock, flags);
381
382         if (!list_empty(&dev->pending_data))
383         {
384                 smscore_buffer_t *cb = (smscore_buffer_t *) dev->pending_data.next;
385
386                 touser->offset = cb->offset_in_common + cb->offset;
387                 touser->size = cb->size;
388
389                 list_del(&cb->entry);
390
391                 dev->currentcb = cb;
392         }
393         else
394         {
395                 touser->offset = 0;
396                 touser->size = 0;
397         }
398
399         spin_unlock_irqrestore(&dev->lock, flags);
400
401         return 0;
402 }
403
404 int smschar_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
405 {
406         smschar_device_t *dev = file->private_data;
407         void __user *up = (void __user *) arg;
408
409         if (!dev->coredev || !dev->smsclient)
410         {
411                 PERROR("no client\n");
412                 return -ENODEV;
413         }
414
415         switch(cmd)
416         {
417                 case SMSCHAR_SET_DEVICE_MODE:
418                         return smscore_set_device_mode(dev->coredev, (int) arg);
419
420                 case SMSCHAR_GET_DEVICE_MODE:
421                 {
422                         if (put_user(smscore_get_device_mode(dev->coredev), (int*) up))
423                                 return -EFAULT;
424
425                         break;
426                 }
427
428                 case SMSCHAR_GET_BUFFER_SIZE:
429                 {
430                         if (put_user(smscore_get_common_buffer_size(dev->coredev), (int*) up))
431                                 return -EFAULT;
432
433                         break;
434                 }
435
436                 case SMSCHAR_WAIT_GET_BUFFER:
437                 {
438                         smschar_buffer_t touser;
439                         int rc;
440
441                         rc = smschar_wait_get_buffer(dev, &touser);
442                         if (rc < 0)
443                                 return rc;
444
445                         if (copy_to_user(up, &touser, sizeof(smschar_buffer_t)))
446                                 return -EFAULT;
447
448                         break;
449                 }
450
451                 default:
452                         return -ENOIOCTLCMD;
453         }
454
455         return 0;
456 }
457
458 struct file_operations smschar_fops =
459 {
460         .owner = THIS_MODULE,
461         .read = smschar_read,
462         .write = smschar_write,
463         .open = smschar_open,
464         .release = smschar_release,
465         .mmap = smschar_mmap,
466         .ioctl = smschar_ioctl,
467 };
468
469 static int smschar_setup_cdev ( smschar_device_t *dev, int index )
470 {
471         int rc, devno = MKDEV ( smschar_major, smschar_minor + index );
472
473         cdev_init ( &dev->cdev, &smschar_fops );
474
475         dev->cdev.owner = THIS_MODULE;
476         dev->cdev.ops = &smschar_fops;
477
478         kobject_set_name(&dev->cdev.kobj, "Siano_sms%d", index);
479
480         rc = cdev_add ( &dev->cdev, devno, 1 );
481
482         PDEBUG("exiting %p %d, rc %d\n", dev, index, rc);
483
484         return rc;
485 }
486
487 /**
488  * smschar callback that called when device plugged in/out. the function
489  * register or unregisters char device interface according to plug in/out
490  *
491  * @param coredev pointer to device that is being plugged in/out
492  * @param device pointer to system device object
493  * @param arrival 1 on plug-on, 0 othewise
494  *
495  * @return 0 on success, <0 on error.
496  */
497 int smschar_hotplug(smscore_device_t* coredev, struct device* device, int arrival)
498 {
499         int rc = 0, i;
500
501         PDEBUG("entering %d\n", arrival);
502
503         if (arrival)
504         {
505                 // currently only 1 instance supported
506                 if (!g_smschar_inuse)
507                 {
508                         /* data notification callbacks assignment */
509                         memset ( smschar_devices, 0, SMSCHAR_NR_DEVS * sizeof ( smschar_device_t ) );
510
511                         /* Initialize each device. */
512                         for (i = 0; i < SMSCHAR_NR_DEVS; i++)
513                         {
514                                 smschar_setup_cdev ( &smschar_devices[i], i );
515
516                                 INIT_LIST_HEAD(&smschar_devices[i].pending_data);
517                                 spin_lock_init(&smschar_devices[i].lock);
518                                 init_waitqueue_head(&smschar_devices[i].waitq);
519
520                                 smschar_devices[i].coredev = coredev;
521                                 smschar_devices[i].device_index = i;
522                         }
523
524                         g_smschar_inuse = 1;
525                 }
526         }
527         else
528         {
529                 // currently only 1 instance supported
530                 if (g_smschar_inuse)
531                 {
532                         /* Get rid of our char dev entries */
533                         for(i = 0; i < SMSCHAR_NR_DEVS; i++)
534                                 cdev_del(&smschar_devices[i].cdev);
535
536                         g_smschar_inuse = 0;
537                 }
538         }
539
540         PDEBUG("exiting, rc %d\n", rc);
541
542         return rc;                                      /* succeed */
543 }
544
545 int smschar_initialize(void)
546 {
547         dev_t devno = MKDEV ( smschar_major, smschar_minor );
548         int rc;
549
550         if(smschar_major)
551         {
552                 rc = register_chrdev_region ( devno, SMSCHAR_NR_DEVS, "smschar" );
553         }
554         else
555         {
556                 rc = alloc_chrdev_region ( &devno, smschar_minor, SMSCHAR_NR_DEVS, "smschar" );
557                 smschar_major = MAJOR ( devno );
558         }
559
560         if (rc < 0)
561         {
562                 PWARNING (  "smschar: can't get major %d\n", smschar_major );
563                 return rc;
564         }
565
566         return smscore_register_hotplug(smschar_hotplug);
567 }
568
569 void smschar_terminate(void)
570 {
571         dev_t devno = MKDEV ( smschar_major, smschar_minor );
572
573         unregister_chrdev_region(devno, SMSCHAR_NR_DEVS);
574         smscore_unregister_hotplug(smschar_hotplug);
575 }