vc_screen.c
上传用户:lgb322
上传日期:2013-02-24
资源大小:30529k
文件大小:12k
源码类别:

嵌入式Linux

开发平台:

Unix_Linux

  1. /*
  2.  * linux/drivers/char/vc_screen.c
  3.  *
  4.  * Provide access to virtual console memory.
  5.  * /dev/vcs0: the screen as it is being viewed right now (possibly scrolled)
  6.  * /dev/vcsN: the screen of /dev/ttyN (1 <= N <= 63)
  7.  *            [minor: N]
  8.  *
  9.  * /dev/vcsaN: idem, but including attributes, and prefixed with
  10.  * the 4 bytes lines,columns,x,y (as screendump used to give).
  11.  * Attribute/character pair is in native endianity.
  12.  *            [minor: N+128]
  13.  *
  14.  * This replaces screendump and part of selection, so that the system
  15.  * administrator can control access using file system permissions.
  16.  *
  17.  * aeb@cwi.nl - efter Friedas begravelse - 950211
  18.  *
  19.  * machek@k332.feld.cvut.cz - modified not to send characters to wrong console
  20.  *  - fixed some fatal off-by-one bugs (0-- no longer == -1 -> looping and looping and looping...)
  21.  *  - making it shorter - scr_readw are macros which expand in PRETTY long code
  22.  */
  23. #include <linux/config.h>
  24. #include <linux/kernel.h>
  25. #include <linux/major.h>
  26. #include <linux/errno.h>
  27. #include <linux/tty.h>
  28. #include <linux/devfs_fs_kernel.h>
  29. #include <linux/sched.h>
  30. #include <linux/interrupt.h>
  31. #include <linux/mm.h>
  32. #include <linux/init.h>
  33. #include <linux/vt_kern.h>
  34. #include <linux/console_struct.h>
  35. #include <linux/selection.h>
  36. #include <linux/kbd_kern.h>
  37. #include <linux/console.h>
  38. #include <asm/uaccess.h>
  39. #include <asm/byteorder.h>
  40. #include <asm/unaligned.h>
  41. #undef attr
  42. #undef org
  43. #undef addr
  44. #define HEADER_SIZE 4
  45. static int
  46. vcs_size(struct inode *inode)
  47. {
  48. int size;
  49. int currcons = MINOR(inode->i_rdev) & 127;
  50. if (currcons == 0)
  51. currcons = fg_console;
  52. else
  53. currcons--;
  54. if (!vc_cons_allocated(currcons))
  55. return -ENXIO;
  56. size = video_num_lines * video_num_columns;
  57. if (MINOR(inode->i_rdev) & 128)
  58. size = 2*size + HEADER_SIZE;
  59. return size;
  60. }
  61. static loff_t vcs_lseek(struct file *file, loff_t offset, int orig)
  62. {
  63. int size = vcs_size(file->f_dentry->d_inode);
  64. switch (orig) {
  65. default:
  66. return -EINVAL;
  67. case 2:
  68. offset += size;
  69. break;
  70. case 1:
  71. offset += file->f_pos;
  72. case 0:
  73. break;
  74. }
  75. if (offset < 0 || offset > size)
  76. return -EINVAL;
  77. file->f_pos = offset;
  78. return file->f_pos;
  79. }
  80. /* We share this temporary buffer with the console write code
  81.  * so that we can easily avoid touching user space while holding the
  82.  * console spinlock.
  83.  */
  84. extern char con_buf[PAGE_SIZE];
  85. #define CON_BUF_SIZE PAGE_SIZE
  86. extern struct semaphore con_buf_sem;
  87. static ssize_t
  88. vcs_read(struct file *file, char *buf, size_t count, loff_t *ppos)
  89. {
  90. struct inode *inode = file->f_dentry->d_inode;
  91. unsigned int currcons = MINOR(inode->i_rdev);
  92. long pos = *ppos;
  93. long viewed, attr, read;
  94. int col, maxcol;
  95. unsigned short *org = NULL;
  96. ssize_t ret;
  97. down(&con_buf_sem);
  98. /* Select the proper current console and verify
  99.  * sanity of the situation under the console lock.
  100.  */
  101. acquire_console_sem();
  102. attr = (currcons & 128);
  103. currcons = (currcons & 127);
  104. if (currcons == 0) {
  105. currcons = fg_console;
  106. viewed = 1;
  107. } else {
  108. currcons--;
  109. viewed = 0;
  110. }
  111. ret = -ENXIO;
  112. if (!vc_cons_allocated(currcons))
  113. goto unlock_out;
  114. ret = -EINVAL;
  115. if (pos < 0)
  116. goto unlock_out;
  117. read = 0;
  118. ret = 0;
  119. while (count) {
  120. char *con_buf0, *con_buf_start;
  121. long this_round, size;
  122. ssize_t orig_count;
  123. long p = pos;
  124. /* Check whether we are above size each round,
  125.  * as copy_to_user at the end of this loop
  126.  * could sleep.
  127.  */
  128. size = vcs_size(inode);
  129. if (pos >= size)
  130. break;
  131. if (count > size - pos)
  132. count = size - pos;
  133. this_round = count;
  134. if (this_round > CON_BUF_SIZE)
  135. this_round = CON_BUF_SIZE;
  136. /* Perform the whole read into the local con_buf.
  137.  * Then we can drop the console spinlock and safely
  138.  * attempt to move it to userspace.
  139.  */
  140. con_buf_start = con_buf0 = con_buf;
  141. orig_count = this_round;
  142. maxcol = video_num_columns;
  143. if (!attr) {
  144. org = screen_pos(currcons, p, viewed);
  145. col = p % maxcol;
  146. p += maxcol - col;
  147. while (this_round-- > 0) {
  148. *con_buf0++ = (vcs_scr_readw(currcons, org++) & 0xff);
  149. if (++col == maxcol) {
  150. org = screen_pos(currcons, p, viewed);
  151. col = 0;
  152. p += maxcol;
  153. }
  154. }
  155. } else {
  156. if (p < HEADER_SIZE) {
  157. size_t tmp_count;
  158. con_buf0[0] = (char) video_num_lines;
  159. con_buf0[1] = (char) video_num_columns;
  160. getconsxy(currcons, con_buf0 + 2);
  161. con_buf_start += p;
  162. this_round += p;
  163. if (this_round > CON_BUF_SIZE) {
  164. this_round = CON_BUF_SIZE;
  165. orig_count = this_round - p;
  166. }
  167. tmp_count = HEADER_SIZE;
  168. if (tmp_count > this_round)
  169. tmp_count = this_round;
  170. /* Advance state pointers and move on. */
  171. this_round -= tmp_count;
  172. p = HEADER_SIZE;
  173. con_buf0 = con_buf + HEADER_SIZE;
  174. /* If this_round >= 0, then p is even... */
  175. } else if (p & 1) {
  176. /* Skip first byte for output if start address is odd
  177.  * Update region sizes up/down depending on free
  178.  * space in buffer.
  179.  */
  180. con_buf_start++;
  181. if (this_round < CON_BUF_SIZE)
  182. this_round++;
  183. else
  184. orig_count--;
  185. }
  186. if (this_round > 0) {
  187. unsigned short *tmp_buf = (unsigned short *)con_buf0;
  188. p -= HEADER_SIZE;
  189. p /= 2;
  190. col = p % maxcol;
  191. org = screen_pos(currcons, p, viewed);
  192. p += maxcol - col;
  193. /* Buffer has even length, so we can always copy
  194.  * character + attribute. We do not copy last byte
  195.  * to userspace if this_round is odd.
  196.  */
  197. this_round = (this_round + 1) >> 1;
  198. while (this_round) {
  199. *tmp_buf++ = vcs_scr_readw(currcons, org++);
  200. this_round --;
  201. if (++col == maxcol) {
  202. org = screen_pos(currcons, p, viewed);
  203. col = 0;
  204. p += maxcol;
  205. }
  206. }
  207. }
  208. }
  209. /* Finally, release the console semaphore while we push
  210.  * all the data to userspace from our temporary buffer.
  211.  *
  212.  * AKPM: Even though it's a semaphore, we should drop it because
  213.  * the pagefault handling code may want to call printk().
  214.  */
  215. release_console_sem();
  216. ret = copy_to_user(buf, con_buf_start, orig_count);
  217. acquire_console_sem();
  218. if (ret) {
  219. read += (orig_count - ret);
  220. ret = -EFAULT;
  221. break;
  222. }
  223. buf += orig_count;
  224. pos += orig_count;
  225. read += orig_count;
  226. count -= orig_count;
  227. }
  228. *ppos += read;
  229. if (read)
  230. ret = read;
  231. unlock_out:
  232. release_console_sem();
  233. up(&con_buf_sem);
  234. return ret;
  235. }
  236. static ssize_t
  237. vcs_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
  238. {
  239. struct inode *inode = file->f_dentry->d_inode;
  240. unsigned int currcons = MINOR(inode->i_rdev);
  241. long pos = *ppos;
  242. long viewed, attr, size, written;
  243. char *con_buf0;
  244. int col, maxcol;
  245. u16 *org0 = NULL, *org = NULL;
  246. size_t ret;
  247. down(&con_buf_sem);
  248. /* Select the proper current console and verify
  249.  * sanity of the situation under the console lock.
  250.  */
  251. acquire_console_sem();
  252. attr = (currcons & 128);
  253. currcons = (currcons & 127);
  254. if (currcons == 0) {
  255. currcons = fg_console;
  256. viewed = 1;
  257. } else {
  258. currcons--;
  259. viewed = 0;
  260. }
  261. ret = -ENXIO;
  262. if (!vc_cons_allocated(currcons))
  263. goto unlock_out;
  264. size = vcs_size(inode);
  265. ret = -EINVAL;
  266. if (pos < 0 || pos > size)
  267. goto unlock_out;
  268. if (count > size - pos)
  269. count = size - pos;
  270. written = 0;
  271. while (count) {
  272. long this_round = count;
  273. size_t orig_count;
  274. long p;
  275. if (this_round > CON_BUF_SIZE)
  276. this_round = CON_BUF_SIZE;
  277. /* Temporarily drop the console lock so that we can read
  278.  * in the write data from userspace safely.
  279.  */
  280. release_console_sem();
  281. ret = copy_from_user(con_buf, buf, this_round);
  282. acquire_console_sem();
  283. if (ret) {
  284. this_round -= ret;
  285. if (!this_round) {
  286. /* Abort loop if no data were copied. Otherwise
  287.  * fail with -EFAULT.
  288.  */
  289. if (written)
  290. break;
  291. ret = -EFAULT;
  292. goto unlock_out;
  293. }
  294. }
  295. /* The vcs_size might have changed while we slept to grab
  296.  * the user buffer, so recheck.
  297.  * Return data written up to now on failure.
  298.  */
  299. size = vcs_size(inode);
  300. if (pos >= size)
  301. break;
  302. if (this_round > size - pos)
  303. this_round = size - pos;
  304. /* OK, now actually push the write to the console
  305.  * under the lock using the local kernel buffer.
  306.  */
  307. con_buf0 = con_buf;
  308. orig_count = this_round;
  309. maxcol = video_num_columns;
  310. p = pos;
  311. if (!attr) {
  312. org0 = org = screen_pos(currcons, p, viewed);
  313. col = p % maxcol;
  314. p += maxcol - col;
  315. while (this_round > 0) {
  316. unsigned char c = *con_buf0++;
  317. this_round--;
  318. vcs_scr_writew(currcons,
  319.        (vcs_scr_readw(currcons, org) & 0xff00) | c, org);
  320. org++;
  321. if (++col == maxcol) {
  322. org = screen_pos(currcons, p, viewed);
  323. col = 0;
  324. p += maxcol;
  325. }
  326. }
  327. } else {
  328. if (p < HEADER_SIZE) {
  329. char header[HEADER_SIZE];
  330. getconsxy(currcons, header + 2);
  331. while (p < HEADER_SIZE && this_round > 0) {
  332. this_round--;
  333. header[p++] = *con_buf0++;
  334. }
  335. if (!viewed)
  336. putconsxy(currcons, header + 2);
  337. }
  338. p -= HEADER_SIZE;
  339. col = (p/2) % maxcol;
  340. if (this_round > 0) {
  341. org0 = org = screen_pos(currcons, p/2, viewed);
  342. if ((p & 1) && this_round > 0) {
  343. char c;
  344. this_round--;
  345. c = *con_buf0++;
  346. #ifdef __BIG_ENDIAN
  347. vcs_scr_writew(currcons, c |
  348.      (vcs_scr_readw(currcons, org) & 0xff00), org);
  349. #else
  350. vcs_scr_writew(currcons, (c << 8) |
  351.      (vcs_scr_readw(currcons, org) & 0xff), org);
  352. #endif
  353. org++;
  354. p++;
  355. if (++col == maxcol) {
  356. org = screen_pos(currcons, p/2, viewed);
  357. col = 0;
  358. }
  359. }
  360. p /= 2;
  361. p += maxcol - col;
  362. }
  363. while (this_round > 1) {
  364. unsigned short w;
  365. w = get_unaligned(((const unsigned short *)con_buf0));
  366. vcs_scr_writew(currcons, w, org++);
  367. con_buf0 += 2;
  368. this_round -= 2;
  369. if (++col == maxcol) {
  370. org = screen_pos(currcons, p, viewed);
  371. col = 0;
  372. p += maxcol;
  373. }
  374. }
  375. if (this_round > 0) {
  376. unsigned char c;
  377. c = *con_buf0++;
  378. #ifdef __BIG_ENDIAN
  379. vcs_scr_writew(currcons, (vcs_scr_readw(currcons, org) & 0xff) | (c << 8), org);
  380. #else
  381. vcs_scr_writew(currcons, (vcs_scr_readw(currcons, org) & 0xff00) | c, org);
  382. #endif
  383. }
  384. }
  385. count -= orig_count;
  386. written += orig_count;
  387. buf += orig_count;
  388. pos += orig_count;
  389. if (org0)
  390. update_region(currcons, (unsigned long)(org0), org-org0);
  391. }
  392. *ppos += written;
  393. ret = written;
  394. unlock_out:
  395. release_console_sem();
  396. up(&con_buf_sem);
  397. return ret;
  398. }
  399. static int
  400. vcs_open(struct inode *inode, struct file *filp)
  401. {
  402. unsigned int currcons = (MINOR(inode->i_rdev) & 127);
  403. if(currcons && !vc_cons_allocated(currcons-1))
  404. return -ENXIO;
  405. return 0;
  406. }
  407. static struct file_operations vcs_fops = {
  408. llseek: vcs_lseek,
  409. read: vcs_read,
  410. write: vcs_write,
  411. open: vcs_open,
  412. };
  413. static devfs_handle_t devfs_handle;
  414. void vcs_make_devfs (unsigned int index, int unregister)
  415. {
  416. #ifdef CONFIG_DEVFS_FS
  417.     char name[8];
  418.     sprintf (name, "a%u", index + 1);
  419.     if (unregister)
  420.     {
  421. devfs_unregister ( devfs_find_handle (devfs_handle, name + 1, 0, 0,
  422.       DEVFS_SPECIAL_CHR, 0) );
  423. devfs_unregister ( devfs_find_handle (devfs_handle, name, 0, 0,
  424.       DEVFS_SPECIAL_CHR, 0) );
  425.     }
  426.     else
  427.     {
  428. devfs_register (devfs_handle, name + 1, DEVFS_FL_DEFAULT,
  429. VCS_MAJOR, index + 1,
  430. S_IFCHR | S_IRUSR | S_IWUSR, &vcs_fops, NULL);
  431. devfs_register (devfs_handle, name, DEVFS_FL_DEFAULT,
  432. VCS_MAJOR, index + 129,
  433. S_IFCHR | S_IRUSR | S_IWUSR, &vcs_fops, NULL);
  434.     }
  435. #endif /* CONFIG_DEVFS_FS */
  436. }
  437. int __init vcs_init(void)
  438. {
  439. int error;
  440. error = devfs_register_chrdev(VCS_MAJOR, "vcs", &vcs_fops);
  441. if (error)
  442. printk("unable to get major %d for vcs device", VCS_MAJOR);
  443. devfs_handle = devfs_mk_dir (NULL, "vcc", NULL);
  444. devfs_register (devfs_handle, "0", DEVFS_FL_DEFAULT,
  445. VCS_MAJOR, 0,
  446. S_IFCHR | S_IRUSR | S_IWUSR, &vcs_fops, NULL);
  447. devfs_register (devfs_handle, "a", DEVFS_FL_DEFAULT,
  448. VCS_MAJOR, 128,
  449. S_IFCHR | S_IRUSR | S_IWUSR, &vcs_fops, NULL);
  450. return error;
  451. }