smschar.c 13 KB

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