linux kernel programming - · pdf filelinux kernel programming by flyduck ˘ ˇˆ ˙˝ ˛...
TRANSCRIPT
�����
Linux Kernel Programmingby flyduck
����������������
�����������������
�������������� ����������������������������� ���!"#$
%&'� #�� ()*�� +,�� -.)�� #�� (/���� �� ��#$� 01 23� 4��56� 78�
9:� ���� ;��� �� ���� �� �� ��� <=:� >?@A� BCD'� �E#��� FG� ��
�'�H�I�4�#��JK��BJ#3�KL��M�N�����������!O�����PQ���R��
S6��TU�PQ���R��SVW��X��()*��YZ[�\]7�� �����\]�� ����\]^��_`�
\]���XW�a/�����b�cJd�<e�����fg� ;3��hi#;>�Qj����������c
Jd� ��� �'� >k)�� #�� ()*�� +,�� ����� �6�� l�Z�� #m/���� cJ-� no
ip�� (q7� rst)3� uv��� wx#;�� ip�� (�3� y� ��������������6$� z{'� c
|;3� �}v/���� ~�� BCd� z{�� cW� �;�� � � 78�� ���W6� (�� ���� $;�
'��JE|;>�Qj��������6�(��+,�� ���� !!��"��#$"#����!������ �������W
6���H���(/����������y��d� �������6$��E�()*��{i7�G�����@)���J
H����)*��G�������������$���H���(/���
����������� ��http://linuxkernel.to/������������ � ��http://flyduck.com/mailman/listinfo/linux-kernel
�����
��������
��� �
�������������� ���� ����������������������������� �!"�"!"��ftp://ftp.oreilly.com
#���$��������� ��������
�����%!�&!�'�(���)
��� �
*����+�����,��-��..��-�/���������,�.���01http://linuxdoc.org/LDP/lkmpg/mpg.html
#���$�*����+�����,��-��..��-�/���http://kldp.org/Translations/Kernel_Module_Programming_Guide
2��������/����0��3��4��-�05�������*�����,������ 0���� ��http://netfilter.kernelnotes.org/unreliable-guides/
�����
��
�6�������������
"6�������� � ��3�����7����
86�!������ � ��+�����9���"#���$����� ��
'6�%������ � ��+�����9���&�'�()���$����� ��
�6�*������ � ��:����+,-�.�/0
6�12������ � �����0��/034
&6��5�678
%6�-"9�:�;��<�7�=��>?�
!6�@2������ � ��-�.�A�B�C4
�(6�DEFG�H�
��6�3�I��J�
�"6�,K������ � ��+��������"#��LM����� ��
NO� �6�PQRJ� ��,;��S������ ��
NO� �6�TU� <�=
�����
�������������
! ��������VW� �
��X0��� � ��Y0$�?�#� ��.��$Z��[\3]����
��-�.�A�DE^��� ��X0��� Z_�����`�a�Jb3]�DE^��
����� ����%��c���>?��c����,;��+,-�.��*Gde������ ���f
��3�I�� ��;,2����+���7��*Gg��@h�ijklm
! ���� ��� �
��kla�no�p��1q]�r�GI�6
��s�3�I��D�klB�tu v�r�GI��D�kla�wm��1x�yv�z16
�����?�Z��{|}~��>?�Z��i3�����}���]16
������ ��a��3@�kla����� ��kla�+,H��1x�y�z16
��kla��B��P�����~�P�Z�� �����DE^��a��3@�kla�J��16
! kl��P�� �
���$�kl#�5����0�����$�����a��3���~�����~���y�z]�kl6
0�.������ ������:��������4��������.�� ��,;� :�4���>�� 0��4�� �������666
��LM�kl#����4����$�������-a���� �����3���kl�� ��� o��� B�31
#�����.���� $6�LM#��������3���+,-�.��V���y�z16
9��::���� 4��5������ 4����+��� 4��0�:��;����+��666
��*Gg�kl#�0?��4����$���*Gg����� y¡��y�z]�kl6
05��0�� ��:��:::
�����
! ���?¢#4����.����$��
������-�.�N£¤Z�[�p������¥�����y�z]�����V¦�r6
������1-�§+,3¨��-�.���N£3���~v�����,N©��ª«��y�z16
������ ����+,-�.��*Gg���¬A�f��?¢��wm�16
! ?¢���� 9�,®�X0��� ��¯�� �
��main() °yB�±~���²�¥��-Z�³�]� 0��0�:�@������:�°yB�´µ�16
0��0�:���int init_module(void)¦b3¶� (��{�3¶���·��¸��¹º»16
�����:���void cleanup_module(void)������.������4��-��
�.������jy��°y��¼
�.�����.:��0���1½�¾Z�´µ3]� �.�����Y03]�¿
�.�����:��0���1½�¾Z��Y0��y�zvO� �.�����Jb3]�¿
�.����0������ �.���m��ÀO��B�~�z]� 0���
0�0��� ���4��-� ��§+,��3¶�� ���4�\Á��Â���16�?��ÃÄm�� � ��}��z4
Å�Z���$«��{|�B�316
����.��� ���4��-� �� �.���m�� ���4]�?¢������Å��16� �.:��0}]� �.���m��1
½�¾Z��Jb���i�Æ��16
?¢U�§+,w��3~�Ç7a�3���U� ��>�0�+,�È<�16
����9�ɵ�����?¢Z�� �:��03~�z]� �.���w��Y0��y�z16
�����
! ?¢������Z���i�YÊ� �
��9���0�5������-���?¢U���?�Z��[\3Ë��>?��o�Z�Ì���Í��ÎÏv�3���
p���?¢Z�� ÐÑ�� Zh]� -�.Z� lÒ��Ë�� >?�a�1x� Å� i�3~�� ��
°y�Ï�-Z]�®�-�ZhÓ�a�ÔY3~�Zha�H��Æ��16
��{y�ÕÖ��� ++)�Ò×U�Y0��y�±16�{y�ÕÖU��y�ÕÖp��Ì«3@�H�3
vO��16
�� ���� Y03]� �Ø� 74]� J�}�� z~� #"� :�-$�� DEFG� Ùmhv� [,�� �Ø�
Y03Ë���Ø��Ú��Y03¶�Û�16��ØZ�Ü�ÝÞ��)p¶�Û}~� #[�p���
ßàvO��1$������ �����Ú��,�����vO�i��16
�� 1½� áâã�� ~º�Æ� �16� ���]� @h� äåZ�� Y0æ� y� zpË�� 8"`G²� '
`G�äå9� ��0� �����f��?!�~ºa��Æ�3��� ;,2Z�ç���Ó�]�}vO�è�
vO��16
! ?¢�cé���� � �
��lsmod�������?¢��ÀO��Î@»16�#/proc/modules$��insmod������?¢�����16
��rmmod������?¢��¥���16
��depmod������?¢"���´¦��ÔY�16�#/lib/modules/*/modules.dep$��modprobe���?¢��Î1�êU�y»Z��1q]���� 6
�´¦Z�� ��ë���åì�1½�?¢������¥��a��16
��kerneld������?¢��'6�X0��� ��tí����îï���,P
��Z��?¢��ë�����Å�$[p��?¢����3~��ë�±��Å�¥���16
modprobe���� ���03@�����¥��a��16
�����
! ?¢�������cé���� � �
��ksyms����:��0}��z]�ɵ����� �.����ÀO��Î@»16�#/proc/ksyms$��gcc���/�2�;���.:���
��make�����ðG�c�Z�ñ0�� /�2�.�4��0���0�
��ld���/�2����4��#�������������0������>�0�9��$��nm�����>�0�9��Z�z]� �.���m��ÀO��Î@»16
��mknod������� :�����9����w�16
����
��� �������� ����������
! À�� ��?¢����3~�¥����Å�"#��>-�a���3]�?¢���� ��J\�16
! VÉ�òó� ��?¢������Å�³�]�°yD� init_module()Z�����>-�a���3]�°
yD� printk()a� �0� A3����� 7����A� �$Þ�� ���16� ?¢�� ¥���� Å� ³�]� °yD
cleanup_module()Z�]�ÂU�òóp�� A/�����A��$Þ�����16
! 6�G�òó� ��?¢����3~�¥����Å�>-�B�JÌ����}]��ôD�16
! 9�� �
��r����
��§+,� #gcc$��?¢����3~�õ9a�ö�¤�¥��34� #insmod��lsmod��rmmod$��+�49���Y034� #make$��r�a�!�������§+,34� #gcc��ld��make$���:��0��ÃÄ�ÀO��J�34
�����
! Filename : hello.c
/*Hello, World Module Program
*/
#include <linux/kernel.h>#include <linux/module.h>
#ifdef CONFIG_MODVERSIONS#define MODVERSIONS#include <linux/modversions.h>#endif
int init_module(void){
printk("<1>Hello, World\n");
return 0;}
void cleanup_module(void){
printk("<1>Goodbye\n");}
������
! Filename : hello_init.c
#include <linux/kernel.h>#include <linux/module.h>
#ifdef CONFIG_MODVERSIONS#define MODVERSIONS#include <linux/modversions.h>#endif
int init_module(void){
printk("<1>Hello, World\n");
return 0;}
! Filename : hello_cleanup.c
#define __NO_VERSION__#include <linux/kernel.h>#include <linux/module.h>
#ifdef CONFIG_MODVERSIONS#define MODVERSIONS#include <linux/modversions.h>#endif
void cleanup_module(void){
printk("<1>Goodbye\n");}
������
! Filename : Makefile (hello.c)
CC = gccCFLAGS = -D__KERNEL__ -DMODULE -O -Wall
OBJS = hello.o
all : $(OBJS)
clean :rm -f *.o *~
! Filename : Makefile2 (hello_init.c, hello_cleanup.c)
CC = gccLD = ldCFLAGS = -D__KERNEL__ -DMODULE -O -Wall
TARGET = hello.oOBJS = hello_init.o hello_cleanup.o
all : $(TARGET)
$(TARGET) : $(OBJS)$(LD) -r $^ -o $@
clean :rm -f *.o *~
������
! ?¢�§+,34� �
gcc -c -D__KERNEL__ -DMODULE -O hello.c
! make���� ���0��§+,� �
make� %&�>?� �'�#���#A� Makefile'��J#����{
make clean %&���{��":�'�,q#>
make -f Makefile2� %&���� �'�#���#'�BJH�I�BJH� �'�#���#����W�
! ?¢�÷l� �
insmod hello.o
! ����?¢�ÀO�Î4� �
lsmod
! ?¢�J¨� �
rmmod hello
! ?¢Z������>-��Î4� �
tail /var/log/messages� %&����z;W� >
tail -f /var/log/messages� %&����z;W����� �E��rs¡��z;W� >
������
! ?¢��ø��J� �
��?�� ?¢Z]� §+,����� �ø� �ÎB� m�BÆ� 3��� �]�ɵ� {|}~� z]� ��
�ø9�,l�Æ��16
��?¢Z��ø��Î�ù°-ú4� �
#include <linux/module.h>Z��$[p��?¢��ø��Îa��B�16
?¢��øU� char kernel_version[]�jy�����16
?¢� �ø� �Î]� ø«� ?¢Z�� 3�w� z�Æ� �16� r�B� @h�D� åì� 3��
r�Z�w�?¢��ø����3~��;��r�Z�]� B�����@.����65C�+,��ù°
34�ø� #define __NO_VERSION__�����?¢��ø����3���vO��16
��?¢��ø��û�¨��?¢��ø��ÎB�±1~���Å��J��õ� �
�$�ɵ�����?¢��ø��ù°-ü�§+,��1-��1� ��Bk�ýU�òó
"$��ø��û�' v�þJ��?¢���34� ��insmod -fÌ«�� .������ �����ÂU�åì#"6"6�$��ø��û�' v�Ïä}]�åìB�Ú�
w��.������ ������û��åìZ]�Ïä}�����B�¦��ê16
D�N£��Å�Y0������ø9�?¢��§+,��Å�Y03]��ø��û��åì�!�
���ø�����i�Æ��16
! ��� ���� �.����Zh��J� �
�$�Y0��y�±]�ÃÄ��Y0��åì� �
?¢Z�]� ��Z� ��}�� z]� ?�� ÃÄ�� Y0�� y� ±p��� ��Z�� �:��0� �
i]�ÃÄw�Y0��y�z16
ÃÄ��®�-�ë���åì�����no�y�3@��ß�ÃÄ�� �:��0�-üi�Æ��16
#kernel/ksyms.c��net/netsyms.c �] � r�Z�$
������
"$� �.���mZ��ø��ÎB�ù°}��z]�åì� �
ÂU� ÃÄ� v� �ø� �Îa� ù°-�]�� @NZ� � �#;��<�/E+��FG�����$
�:��0a���Å�1�_� �16� printk()�°ya�R��m¶������ø�ÔYa�3
���p¶���� printk�� �:��0}�w���ø�ÔYa�3¶� printk��Z�?¢��
ø��ÎB��B}�� printk_xxxxxx�È<�� �:��0��16
Ó�Z��1��N©���a����i]������16#ifdef CONFIG_MODVERSIONS#define MODVERSIONS#include <linux/modversions.h>#endif
8$�������9���0�����:0�.�1��-��
������ 9���0���p����}��z]��B��°ym#R� �� get_user�� put_user�� strcpy�strcmp$U� �:0�.�1��-�� 3�� �¨�� ��� �:0�.�1��-�� 3¶� §+,-Z� �����
9���0�����tí�,®� 9���0���p��@�yv�z16��åì� ������9���0����� �0����
�.�����@4~� ���4a�-v34�Å�Z�ZhB�ÐÑ�16
§+,���Z�� ��a�®�-����Æ��16
�� � �:0�.�1��-U� ��"
��8� �:0�.�1��-U� ������ 9���0����� 9���0���p�� �� y� z4� �Z� Y0�� y
±16�#�94:�������9���0��� $
��?¢Z��Y0��y�z]�jy²�°y� �
��Z�� �:��0� �.����0������3@� �:��0��i]�ÃÄ
ɵ�����?¢Z�� �:��0��i]�ÃÄ
������9���0���p������°y
������
! �.�����:��0��J� �
���v��ÃÄ�� �:��0�@Na���3���p¶�?¢Z�� �:��0�B���?��ÃÄm# 0�0��p
����3���U�?�� -������ÃÄm$��$[p������ �.���� 0���Z��BB��1� HC
��. :���:���� �����B��16
��?¢Z��t���ÃÄv� �:��03���pº¶�r�Z�1����B�16EXPORT_NO_SYMBOLS;
���3]�ÃÄw� �:��0a�3º¶��v��ÃÄm��ÀO�����16
#define EXPORT_SYMTAB (#include <linux/module.h> �G6 ��)
EXPORT_SYMBOL(symbol1); (exportH .¢D' £¤� ��)EXPORT_SYMBOL(symbol2);
���� .��������EXPORT_SYMBOL()���v����3���U�åì�?�� �0����� �.���mU�$[
p�� �.����0���Z��B�}]���a� �.����0���Z��B3���_��16
���� .���������0����� �.�����tu' v� 4 �.��: a����?¢Z�Ì���Îa�ù°3~�z
]� ÃÄ�� $[p�� fO3]�� �a� fO3�� �]16� �� ÃÄmU� EE�� .��EI?¢�
¼JE�����È<��}��z16
������
������������ ����������������������������
! À�� ������4B�B���"#���$����� ��a�J\�16���kl]��4�4�w
��B�~�zp��� klZ�� �4a�3¶��N���Z�m��z]��4¸��¹º»16�!���
�4� î4B� B��� ��� � ��a� J\�16� @4�]� klZ� �� �0�� �N� ��Z
���!�1B��4a�3¶��a�¹º»16�%����î4a���Å��N�����0���N�3
¶� ����4��-��ÐÑ3]����� ��a�J\�16
! VÉ�òó� ���$�kl�VÉZ�ë��� 9��� �:��0���m��VÉ�16�@hB�� �:��0�����Z��@
4��ë���¿U� �:������ �������?��0�16��:������ Z�]�?¢��Y0� ya�c��16
��àU� ��Ea� �k34� ��� ~��� 74�� �È��#��������� ��99�$a� 3�� !16� ��]
ø��jy��!��[-Z��¨��[-Z�î4a���y]�±16����Z�]��N���Z�z]��
0��¹ºi~��?��0Z�]���àU��0���N���Z��k���16�?��0a���Å�����0
���N�3¶��a��"#�H��16
! 6�G� òó� �� ?¢�� J\3~� ���� ¤� mknod� Ò×p�� �ß3]� kl� çy� +,#���
:����� 9��$�� w�16� @4�]� kl�� .�>��� ��.��a� ��3�� �$p�� ��Z�� ��
»� .�>�����.��Z�� ��kl�+,��w�16�����?¢Z�]� cat�Ò×���03@�kla
����Å���Z������!��0���%]��ôD�16�!���?¢Z�]�î4a���¤��
¤Z� �4a� 3&�� Å� �øZ� �� �0�� JÌ�� �%]�� ôD�16� %��� ?¢Z�]� ��
�� 74a� '��� î4a� 3&�� Å� ����4��-�� }]�� @N²�� kla� ��m�¶� ?�4�:�
}]��ôD�16
������
! 9�� �
���4w���3]��$����� ���J\
���4@î4a�?!���3]��$����� ���J\
��î4a���Å���B�N�3¶� ����4��-��}]����� ���J\
! 6�G�9�� �
�$��4w���3]��$����� ��� �insmod minibuf.ocat /proc/devices� (%�;�¥6��]¦�PQ���R��S��§¨'�©A
mknod /dev/minibuf c major 0� (%��')�$�"��*#$��z;W�ª«�":6��©A
cat /dev/minibufrmmod minibuf
"$��4@î4a�?!���3]��$����� ��� �insmod minibuf2.ocat > /dev/minibufcat /dev/minibufrmmod minibuf2
8$�î4a���Å���B�N�3¶� ����4��-��}]����� ��insmod minibuf3.ocat minibuf3.c > /dev/minibuf� (%�*�����"+
cat /dev/minibuf (%�,'�#��
rmmod minibuf3
�����
! Filename : minibuf.c
/* Mini Buffer Device Driver*/
#include <linux/kernel.h>#include <linux/module.h>
#ifdef CONFIG_MODVERSIONS#define MODVERSIONS#include <linux/modversions.h>#endif
#include <linux/fs.h>#include <linux/kdev_t.h>#include <asm/uaccess.h>
/* * Device Definitions */
#define DEVICE_NAME "MiniBuf"#define BUFFER_LEN 1024
/* * Global Variables */
static int s_nMajor = 0;
�����
static int s_bDeviceOpen = 0;static char s_strBuf[BUFFER_LEN];static int s_nBufPos = 0, s_nBufEnd = 0;
/* * Function Prototypes */
static int device_open(struct inode *inode, struct file *filp);static int device_release(struct inode *inode, struct file *filp);static ssize_t device_read(struct file *filp, char *buffer, size_t length,
loff_t *offset);static ssize_t device_write(struct file *filp, const char *buffer, size_t length,
loff_t *offset);
static int is_buffer_empty(void);static int is_buffer_full(void);static int read_buffer_char(char *buffer);static int write_buffer_char(char *buffer);
/* * Device Operations */
struct file_operations device_fops = { NULL, /* seek */ device_read, /* read */ device_write, /* write */ NULL, /* readdir */
������
NULL, /* poll */ NULL, /* ioctl */ NULL, /* mmap */ device_open, /* open */ NULL, /* flush */ device_release /* release */};
/* * Module startup/cleanup */
int init_module(void){ printk("Loading Mini Buffer Module\n");
if ((s_nMajor = register_chrdev(0, DEVICE_NAME, &device_fops)) < 0) { printk(DEVICE_NAME " : Device registration failed (%d)\n", s_nMajor); return s_nMajor; }
printk(DEVICE_NAME " : Device registered with Major Number = %d\n", s_nMajor);
strcpy(s_strBuf, "Hello, World\n"); s_nBufEnd = strlen(s_strBuf) + 1;
return 0;}
void cleanup_module(void)
������
{ int nRetCode;
printk("Unloading Mini Buffer Module\n");
if ((nRetCode = unregister_chrdev(s_nMajor, DEVICE_NAME)) < 0) printk(DEVICE_NAME " : Device unregistration failed (%d)\n", nRetCode);}
/* * Device Operations */
int device_open(struct inode *inode, struct file *filp){ printk(DEVICE_NAME " : Device open (%d, %d)\n", MAJOR(inode->i_rdev),
MINOR(inode->i_rdev));
if (s_bDeviceOpen) { printk(DEVICE_NAME " : Device already open\n"); return -EBUSY; }
++s_bDeviceOpen;
MOD_INC_USE_COUNT;
return 0;}
������
int device_release(struct inode *inode, struct file *filp){ printk(DEVICE_NAME " : Device release (%d, %d)\n", MAJOR(inode->i_rdev),
MINOR(inode->i_rdev));
if (!s_bDeviceOpen) { printk(DEVICE_NAME " : Device has not opened\n"); return -EINVAL; }
--s_bDeviceOpen;
MOD_DEC_USE_COUNT;
return 0;}
ssize_t device_read(struct file *filp, char *buffer, size_t length, loff_t *offset){ int count = 0;
if (is_buffer_empty()) { /* end of file */ printk(DEVICE_NAME " : Read return EOF\n"); return 0; }
while (!is_buffer_empty() && length > 1) { read_buffer_char(buffer); ++buffer; --length;
������
++count; }
put_user(0, buffer); ++count;
printk(DEVICE_NAME " : Read %d bytes\n", count);
return count;}
ssize_t device_write(struct file *filp, const char *buffer, size_t length, loff_t *offset){ return -ENOSYS; /* not implemented */}
/* * Buffer Management */
int is_buffer_empty(void){ return (s_nBufPos == s_nBufEnd) ? 1 : 0;}
int is_buffer_full(void){ int pos = s_nBufEnd + 1;
if (pos == BUFFER_LEN)
������
pos = 0;
return (pos == s_nBufPos) ? 1 : 0;}
int read_buffer_char(char *buffer){ if (is_buffer_empty()) return -1;
put_user(s_strBuf[s_nBufPos], buffer);
if (++s_nBufPos == BUFFER_LEN) s_nBufPos = 0;
return 0;}
int write_buffer_char(char *buffer){ if (is_buffer_full()) return -1;
get_user(s_strBuf[s_nBufEnd], buffer);
if (++s_nBufEnd == BUFFER_LEN) s_nBufEnd = 0;
return 0;}
������
! Filename : minibuf2.c
ssize_t device_write(struct file *filp, const char *buffer, size_t length,loff_t *offset)
{ int count = 0;
if (is_buffer_full()) { /* out of buffer */ printk(DEVICE_NAME " : Write return Out of Buffer\n"); return -ENOMEM; }
while (!is_buffer_full() && length > 0) { write_buffer_char((char *) buffer); ++buffer; --length; ++count; }
filp->f_pos += count;
printk(DEVICE_NAME " : Write %d bytes\n", count);
return count;}
������
! Filename : minibuf3.c
static int s_bDeviceReadOpen = 0;static int s_bDeviceWriteOpen = 0;static struct wait_queue *s_wq = NULL;
�"��#-��#.��#" ��
if ((filp->f_flags & O_ACCMODE) & (O_WRONLY | O_RDWR)) { if (s_bDeviceWriteOpen) { printk(DEVICE_NAME " : Device already open for writing\n"); return -EBUSY; } else ++s_bDeviceWriteOpen; } else { if (s_bDeviceReadOpen) { printk(DEVICE_NAME " : Device already open for reading\n"); return -EBUSY; } else ++s_bDeviceReadOpen; }
�"��#-��#.$#�#'/# ��
if ((filp->f_flags & O_ACCMODE) & (O_WRONLY | O_RDWR)) --s_bDeviceWriteOpen; else --s_bDeviceWriteOpen;
������
�"��#-��#.$#'� ��
while (!is_buffer_empty() && length > 1) { read_buffer_char(buffer); ++buffer; --length; ++count; wake_up_interruptible(&s_wq); }
�"��#-��#.,$��# ��
if (is_buffer_full()) { /* out of buffer */ printk(DEVICE_NAME " : Write go to sleep\n"); interruptible_sleep_on(&s_wq); printk(DEVICE_NAME " : Write wake up"); }
�����
! �$����� ����J\ó
��·N²����� ��]�+,�DE^��a�����Õõ�16
������ ��]� file_operationsa�Jb°p�(�VÉ�16
������ ��]�$¡��V�34����~ñ�� .�>�����.��a�Y0�16
! kl��fO9��J
��fO� �int register_chrdev(unsigned int major, const char *name, struct file_operations *fops)major� �¬¨H� �')�$�"��*#$��0�3�BJ#W�a��®-6��¯@)��H°¦��
name� �\]����
fops� �\]6�±���{�²³�´�D
���J� �int unregister_chrdev(unsigned int major, const char *name)
! +�>�����.��²� +�������.��
��kla�V©3]�òóp��)���03@�ç��kla�V��16
��.�>�����.�������Z������ ��a�V©3]��Y0�16
��.�������.��������� ����Z��ë���åì�kla�V©34�����Y0�16
��*�+����� ��]�*�+� .�>�����.��a�B���16
��Documentation/devices.txtZ�?��kl�� .�>�����.��B���}��z16
��register_chrdev()��kla�fO��� .�>�����.��a����16
,ÂU� .�>�����.��B����fO}��zp¶�fO��{��16
����.���������0������major���-�.�0Z� (�ø-
�����
�Ð�9�Z�]�.��� .�>�����.��a�Y0��y�z�w�� Ýù-Z]� .�>�����.��a
�ßàtÆ��16
��mknod�Ò×p������ ��Z�o���y�z]�kl�+,�Ѧ
.4����I����9�����.J�I0�:J�I.�>��J�I.����J
��kdev_t���kl�� .�>����.�������.��a�/É3]�$0VW
MAJOR()���kdev_tZ�� .�>�����.��a�1��]�27�
MINOR()���kdev_tZ�� .�������.��a�1��]�27�
MKDEV(ma, mi)���.�>�����.����.�������.��a�B�~� kdev_ta�w�16
��cat /proc/devices Ò×p��ɵ��������� ���ÀO��Ä�y�z16
! struct file���#������@�����@9 65$���:���9����/É3]�$0VW
���:�#$�°yZ�����wm��~���;��°yZ�ø-�16
struct dentry *f_dentry;� HC�����0�����0��
struct file_operations *f_op; HC�9����:��0��� �#9���0����:���0� $
mode_t f_mode; HC�<+��GE���
loff_t f_pos; HC�ɵ�+,Z���~�î]��l
unsigned int f_flags; HC���� �.�����������4�����0���::��ÂU� 9��-
unsigned int f_uid, f_gid; HC�9����?��������-��
void *private_data; HC��:��9��31��B�D���EB�ë���Å
������
! struct file_operations���#������@�����@9 65$loff_t (*llseek) (struct file *, loff_t, int);
+,Z��ɵ��~�î]��la��[�16ssize_t (*read) (struct file *, char *, size_t, loff_t *);
klZ����Ea���mD16ssize_t (*write) (struct file *, const char *, size_t, loff_t *);
klZ���Ea�4O�16int (*readdir) (struct file *, void *, filldir_t);
klZ�]�Y03���~�4¬�Z�w�Y03]�°y�16unsigned int (*poll) (struct file *, struct poll_table_struct *);
klZ��~���y�z]����]��5�R·�6��ÐÑ7]��ÔY�16
poll()��select()�°ya�VÉ3]��Y0�16int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
�4@î4B�tí�kl31�ë���Ò×����]��Y0�16int (*mmap) (struct file *, struct vm_area_struct *);
kl��>?�a���%���>?�²� .�::��-���16int (*open) (struct inode *, struct file *);
kla�Õ16int (*flush) (struct file *);int (*release) (struct inode *, struct file *);
kla�8]16int (*fsync) (struct file *, struct dentry *);
kla� 9�� 5��16int (*fasync) (int, struct file *, int);
������
� ���5����� ���0�9���0������:�]� 9��-D� <��K�;�9��-B�9�¿��:º»16int (*check_media_change) (kdev_t dev);
á�SÂ��Y0�v�Z� .���B�;�y�z]�LM����� ��Z��Y0�16int (*revalidate) (kdev_t dev);
J¨�B���LM�klZ�� ��99�����5a�c�34�����Y0�16int (*lock) (struct file *, int, struct file_lock *);
! �:�Z����,� �
��H�p��kla�Õ�åì�kla��4��16
��.������Ïa�ôD3~�ë���åì� f_op�ùDEa�y��16
��ë���åì�>?�a��ßàt� filp->private_dataZ��]1
��<W� y#� �-�����0$a�=B->16
! ��� Z����,� �
��<W� ya�?r->16
��filp->private_dataZ��ßàU���EB�zp¶��J�16
��3�@p�� ��� a�3]�åì�kla�P0�16
! ����� �0�������
���V�wA��U�åì� count²�ÂU�¸��¹º»16
���V��74Î1��_��U�åì� countÎ1�\U�Bya�¹º»16
��G�<#����9�9��$D�åì� (��¹º»16
��ZhB�ÐÑ3¶��ya�¹º»16
������
! ?��0���V�wA�4O��åì� count²�ÂU�¸��¹º»16
���V��74Î1��_���åì� countÎ1�\U�Bya�¹º»1
��3�v�î��C��åì� (��¹º»16���åì� write()�°y]�µ-va��16
��ZhB�ÐÑ��åì��ya�¹º»16
! ���>?�²�Y0$�>?�����E�ªä� ��#������@� .@���� 65$
�� ��� >?�²� Y0$� >?�]� ��� 1½� irb"Z� ´µ34� Å�Z� ��Z�� Y0$
>?�a���o���y�±16�� �� �����?��0�� � 0.�����H����9�X0���
Y�Z���E�ªä��ë���åì�����ir�b"p��(Ya��Æ��16
��Y0$�>?�� HC����>?�� �void get_user(to, from);void copy_from_user(to, from, n);
�����>?�� HC�Y0$�>?�� �void put_user(from, to);void copy_to_user(to, from, n);
��get_user()��put_user()���'D»�D$��74Z�� � ���"��'���0���E�(Y
��copy_from_user()��copy_to_user()���74�J��±������74wA�(Y
! ?¢��<W� y� ��?¢�Y0��¥��}]�E�ò�� #������@�����@.����65$
��?¢��1½�?¢������ Z����������Y0}~�z]�a��:F16
����� � ��a�Y03~� z�� Å� rmmodÂU� Ò×p�� ?¢��J¨}]� E�� @4
���� �:��� �� Å� <W� ya� =B-ú4~�� ��� a� 3¶� <W� ya� ?r-ü
¥���æ�y�z_��16
��?¢�<W� y�=B� ��MOD_INC_USE_COUNT
������
��?¢�<W� y�?r� ��MOD_DEC_USE_COUNT��?¢�<W�@N� ��MOD_IN_USE
! ����4��-��@��� $��� ë��� 3]��� ɵ� $��� Y0�� y� ±�� åì�� $��� Y0�� y� z�� ÅG�
41��� �:��3~��$���Y0��y�z_}¶� ?�4�:��}��\Á��HI�16
���������pº~�3]�������EB�±��Å� ����4��-6���EB�vJ3¶� ?�4�:
��?��0���îº~�3]����b"��±��Å� ����4��-��b"��Ñ4¶� ?�4�:
�� �:�°y� �void interruptible_sleep_on(struct wait_queue **q);void sleep_on(struct wait_queue **q);
��?�4�:�°y� �void wake_up_interruptible(struct wait_queue **q);void wake_up (struct wait_queue **q);
����0���:0��������4��-���,®�D� �@�Z�����ÐÑ�� ����4��-�{6��µ>!¶>������;�¥� 1��2���·¸�¬
������0���:0��������4��-������� ���0����� �0���Z��ÐÑ�� ����4��-�'+#��'���s�¹w#��P�º6����W��µ»D���¼½
����0������J� �
����4��-��ÐÑ��Ó�]�µ���,�K�y�z16
��0�����J��õ� �
klZ�o���y�z]� y�J�34� #�:�$
-������jyB�tí� ������jy�Y034� # 0��4$
0���0�9���� :���0���0��Y0� ��Þ��+,31���E��ß
ë���åì� 4.�����#$�f��Y03@� 5�:�L���0
������
��� ���� ����� ��� � ������ ������ �����������
! À�� ������� +�����9���� ���M3@���a���p��)���~�[�p���ß��à
]� ��� � ��a� J\�16� @4�� [�p�� �ßà]� ���� 74]� 4ö�p�� 4ö
¸p��÷���74��w�� insmod���� p��?¢������Å�����74a�D$����
��yv�z16�!���� .������ÏZ�� ���4�1½�[\��3]����� ��a�J\
�16��øZ�J\��Ó�a��03@� .������ÏB� (,�ÅZ]� ����4��-��3���~�� �,�Å
Z]�î4Z�� ����4��-��3���",�ÅZ]��4Z�v� ����4��-���16
! VÉòó� �� 4´�� .�����9"6�� Ó�a�y�3@���a���p��)���~�� init_module()Z�� kmalloc()� °ya� �03@� [�p�� >?�a� �ßà]16� �� Å� insmod� ��� Z� ø
-�� D$a� ÔY3@� ���� 74a� ��3~� zp¶�� �ß3]� 74�� ��a� �ßà]16
cleanup_module()Z�]�H�Z��ßàU���a� kfree()�°ya��03@��J�16�!�
��?¢U���Z�J\�� .�����9'6��Ó�a�y�3@���4�1½�[\��3]� ���@?��0�°ya
VÉ3~���:�-Z� .������ÏZ�� � file�VW«�� 9����:��0������4�1½�°y�����
3@� .������ÏZ�� �1½�[\��3vO��16
! 6�G� òó� �� ���� ?¢U� ?¢�� ���� Å� ���� 74a� ���16� ��~� �ß�� 74
������Ea� 4O3&��Å�'����4O��}���~�>?��N��ZhB�ÐÑ3]��ö
16�!���?¢U� .������ÏB�1½�kl�+,��%�a�wm������[\��ôD�16
������
! 9�� �
����a�[�p���ßà]��$����� ���J\
��.������ÏZ�� �1½�[\��3]��$����� ���J\
! 6�G�9�� �
�$���a�[�p���ßà]��$����� ��� �insmod minibuf4.o minibuf_bufsize=10cat > /dev/minibuf� (%�S¾�º>�����¿�À��«
cat > /dev/minibuf� (%�rs���«#m'�I�6Ás�¹w#�W�©A
cat /dev/minibuf� (%�S¾�º>ÂÃÂ�y\¡Ä�W�©A
rmmod minibuf
"$�.������ÏZ�� �1½�[\��3]��$����� ��� �insmod minbuf5.omknod /dev/minibuf0 c major 0mknod /dev/minibuf1 c major 1mknod /dev/minibuf2 c major 2cat minibuf5.c > /dev/minibuf0� (%�S¾�º>ÂÃÂ��«
cat /dev/minibuf0� (%�S¾�º>ÂÃÂ�ª«
cat minibuf5.c > /dev/minibuf1� (%�*�����"+
cat /dev/minibuf1� (%�,'�#��
cat /dev/minibuf2 (%�*�����"+
cat > /dev/minibuf2� (%�,'�#��
rmmod minibuf5
������
! Filename : minibuf4.c
/* Mini Buffer Device Driver : Dynamic allocation*/
#include <linux/kernel.h>#include <linux/module.h>
#ifdef CONFIG_MODVERSIONS#define MODVERSIONS#include <linux/modversions.h>#endif
#include <linux/fs.h>#include <linux/kdev_t.h>#include <linux/slab.h>#include <asm/uaccess.h>
/* * Device Definitions */
#define DEVICE_NAME "MiniBuf"#define DEFAULT_BUFFER_LEN 1024
/* * Global Variables */
������
static int s_nMajor = 0;
static int s_bDeviceOpen = 0;static char *s_strBuf = NULL;static int s_nBufPos = 0, s_nBufEnd = 0;static int s_nBufSize = 0;
static int minibuf_bufsize = DEFAULT_BUFFER_LEN;
MODULE_PARM(minibuf_bufsize, "i");MODULE_PARM_DESC(minibuf_bufsize, "Size of buffer");
/* * Function Prototypes */
static int device_open(struct inode *inode, struct file *filp);static int device_release(struct inode *inode, struct file *filp);static ssize_t device_read(struct file *filp, char *buffer, size_t length,
loff_t *offset);static ssize_t device_write(struct file *filp, const char *buffer, size_t length,
loff_t *offset);
static int is_buffer_empty(void);static int is_buffer_full(void);static int read_buffer_char(char *buffer);static int write_buffer_char(char *buffer);
/* * Device Operations
�����
*/
struct file_operations device_fops = { NULL, /* seek */ device_read, /* read */ device_write, /* write */ NULL, /* readdir */ NULL, /* poll */ NULL, /* ioctl */ NULL, /* mmap */ device_open, /* open */ NULL, /* flush */ device_release /* release */};
/* * Module startup/cleanup */
int init_module(void){ printk("Loading Mini Buffer Module\n");
if ((s_nMajor = register_chrdev(0, DEVICE_NAME, &device_fops)) < 0) { printk(DEVICE_NAME " : Device registration failed (%d)\n", s_nMajor); return s_nMajor; }
printk(DEVICE_NAME " : Device registered with Major Number = %d\n", s_nMajor);
�����
printk(DEVICE_NAME " : Buffer allocation size = %d\n", minibuf_bufsize);
if (minibuf_bufsize <= 0) { printk(DEVICE_NAME " : invalid minibuf_bufsize argument\n"); return -EINVAL; }
if ((s_strBuf = (char *) kmalloc(minibuf_bufsize, GFP_KERNEL)) == NULL) { printk(DEVICE_NAME " : Memory allocation error\n"); return -ENOMEM; }
s_nBufSize = minibuf_bufsize;
return 0;}
void cleanup_module(void){ int nRetCode;
printk("Unloading Mini Buffer Module\n");
if ((nRetCode = unregister_chrdev(s_nMajor, DEVICE_NAME)) < 0) printk(DEVICE_NAME " : Device unregistration failed (%d)\n", nRetCode);
if (s_strBuf) { kfree(s_strBuf); s_strBuf = NULL; s_nBufSize = 0;
������
}}
/* * Device Operations */
int device_open(struct inode *inode, struct file *filp){ printk(DEVICE_NAME " : Device open (%d, %d)\n", MAJOR(inode->i_rdev), MINOR(inode->i_rdev));
if (s_bDeviceOpen) { printk(DEVICE_NAME " : Device already open\n"); return -EBUSY; }
++s_bDeviceOpen;
MOD_INC_USE_COUNT;
return 0;}
int device_release(struct inode *inode, struct file *filp){ printk(DEVICE_NAME " : Device release (%d, %d)\n", MAJOR(inode->i_rdev), MINOR(inode->i_rdev));
if (!s_bDeviceOpen) {
������
printk(DEVICE_NAME " : Device has not opened\n"); return -EINVAL; }
--s_bDeviceOpen;
MOD_DEC_USE_COUNT;
return 0;}
ssize_t device_read(struct file *filp, char *buffer, size_t length, loff_t *offset){ int count = 0;
if (is_buffer_empty()) { /* end of file */ printk(DEVICE_NAME " : Read return EOF\n"); return 0; }
while (!is_buffer_empty() && length > 1) { read_buffer_char(buffer); ++buffer; --length; ++count; }
put_user(0, buffer); ++count;
������
printk(DEVICE_NAME " : Read %d bytes\n", count);
return count;}
ssize_t device_write(struct file *filp, const char *buffer, size_t length,loff_t *offset)
{ int count = 0;
if (is_buffer_full()) { /* out of buffer */ printk(DEVICE_NAME " : Write out of buffer\n"); return -ENOMEM; }
while (!is_buffer_full() && length > 0) { write_buffer_char((char *) buffer); ++buffer; --length; ++count; }
filp->f_pos += count;
printk(DEVICE_NAME " : Write %d bytes\n", count);
return count;}
/*
������
* Buffer Management */
int is_buffer_empty(void){ return (s_nBufPos == s_nBufEnd) ? 1 : 0;}
int is_buffer_full(void){ int pos = s_nBufEnd + 1;
if (pos == s_nBufSize) pos = 0;
return (pos == s_nBufPos) ? 1 : 0;}
int read_buffer_char(char *buffer){ if (is_buffer_empty()) return -1;
put_user(s_strBuf[s_nBufPos], buffer);
if (++s_nBufPos == s_nBufSize) s_nBufPos = 0;
return 0;}
������
int write_buffer_char(char *buffer){ if (is_buffer_full()) return -1;
get_user(s_strBuf[s_nBufEnd], buffer);
if (++s_nBufEnd == s_nBufSize) s_nBufEnd = 0;
return 0;}
������
! Filename : minibuf5.c
/* Mini Buffer Device Driver : Minor devices*/
#include <linux/kernel.h>#include <linux/module.h>
#ifdef CONFIG_MODVERSIONS#define MODVERSIONS#include <linux/modversions.h>#endif
#include <linux/fs.h>#include <linux/kdev_t.h>#include <linux/slab.h>#include <asm/uaccess.h>
/* * Device Definitions */
#define DEVICE_NAME "MiniBuf"#define DEFAULT_BUFFER_LEN 1024
#define DEVICE_NOBLOCK 0#define DEVICE_WRITEBLOCK 1#define DEVICE_BLOCK 2#define DEVICE_NUM 3
������
/* * Global Variables */
static int s_nMajor = 0;
static struct devicedata_t { int m_bDeviceReadOpen; int m_bDeviceWriteOpen; char *m_strBuf; int m_nBufPos, m_nBufEnd; int m_nBufSize; struct wait_queue *m_writequeue, *m_readqueue;} s_devicedata[DEVICE_NUM];
int minibuf_bufsize = DEFAULT_BUFFER_LEN;
MODULE_PARM(minibuf_bufsize, "i");MODULE_PARM_DESC(minibuf_bufsize, "Size of buffer");
/* * Function Prototypes */
static int device_open(struct inode *inode, struct file *filp);static int device_release(struct inode *inode, struct file *filp);static ssize_t device_noblock_read(struct file *filp, char *buffer, size_t length,
loff_t *offset);static ssize_t device_noblock_write(struct file *filp, const char *buffer,
������
size_t length, loff_t *offset);static ssize_t device_writeblock_read(struct file *filp, char *buffer, size_t length,
loff_t *offset);static ssize_t device_writeblock_write(struct file *filp, const char *buffer,
size_t length, loff_t *offset);static ssize_t device_block_read(struct file *filp, char *buffer, size_t length,
loff_t *offset);static ssize_t device_block_write(struct file *filp, const char *buffer,
size_t length, loff_t *offset);
static int is_buffer_empty(struct devicedata_t *pdata);static int is_buffer_full(struct devicedata_t *pdata);static int read_buffer_char(struct devicedata_t *pdata, char *buffer);static int write_buffer_char(struct devicedata_t *pdata, char *buffer);
/* * Device Operations */
struct file_operations device_noblock_fops = { open: device_open, release: device_release, read: device_noblock_read, write: device_noblock_write,};
struct file_operations device_writeblock_fops = { open: device_open, release: device_release, read: device_writeblock_read,
�����
write: device_writeblock_write,};
struct file_operations device_block_fops = { open: device_open, release: device_release, read: device_block_read, write: device_block_write,};
struct file_operations *device_fops_array[] = { &device_noblock_fops, &device_writeblock_fops, &device_block_fops};
/* * Module startup/cleanup */
int init_module(void){ int i; struct devicedata_t *pdata;
printk("Loading Mini Buffer Module\n");
if ((s_nMajor = register_chrdev(0, DEVICE_NAME, device_fops_array[0])) < 0) { printk(DEVICE_NAME " : Device registration failed (%d)\n", s_nMajor); return s_nMajor;
�����
}
printk(DEVICE_NAME " : Device registered with Major Number = %d\n", s_nMajor);
printk(DEVICE_NAME " : Buffer allocation size = %d\n", minibuf_bufsize);
if (minibuf_bufsize <= 0) { printk(DEVICE_NAME " : invalid minibuf_bufsize argument = %d\n", minibuf_bufsize); return -EINVAL; }
for (i = 0, pdata = s_devicedata; i < DEVICE_NUM; ++i, ++pdata) { if ((pdata->m_strBuf = (char *) kmalloc(minibuf_bufsize, GFP_KERNEL)) == NULL) { printk(DEVICE_NAME " : Memory allocation error\n");
while (--i >= 0) kfree(pdata->m_strBuf);
return -ENOMEM; }
pdata->m_nBufSize = minibuf_bufsize; }
return 0;}
void cleanup_module(void){
������
int i, nRetCode; struct devicedata_t *pdata;
printk("Unloading Mini Buffer Module\n");
if ((nRetCode = unregister_chrdev(s_nMajor, DEVICE_NAME)) < 0) printk(DEVICE_NAME " : Device unregistration failed (%d)\n", nRetCode);
for (i = 0, pdata = s_devicedata; i < DEVICE_NUM; ++i, ++pdata) { if (pdata->m_strBuf) { kfree(pdata->m_strBuf); pdata->m_strBuf = NULL; pdata->m_nBufSize = 0; } }}
/* * Device Operations */
int device_open(struct inode *inode, struct file *filp){ int minor = MINOR(inode->i_rdev); struct devicedata_t *pdata = &s_devicedata[minor];
printk(DEVICE_NAME " : Device open (%d, %d)\n", MAJOR(inode->i_rdev), MINOR(inode->i_rdev));
if ((filp->f_flags & O_ACCMODE) & (O_WRONLY | O_RDWR)) {
������
if (pdata->m_bDeviceWriteOpen) { printk(DEVICE_NAME " : Device already open for writing\n"); return -EBUSY; } else ++pdata->m_bDeviceWriteOpen; } else { if (pdata->m_bDeviceReadOpen) { printk(DEVICE_NAME " : Device already open for reading\n"); return -EBUSY; } else ++pdata->m_bDeviceReadOpen; }
filp->f_op = device_fops_array[minor];
MOD_INC_USE_COUNT;
return 0;}
int device_release(struct inode *inode, struct file *filp){ int minor = MINOR(inode->i_rdev); struct devicedata_t *pdata = &s_devicedata[minor];
printk(DEVICE_NAME " : Device release (%d, %d)\n", MAJOR(inode->i_rdev), MINOR(inode->i_rdev));
if ((filp->f_flags & O_ACCMODE) & (O_WRONLY | O_RDWR)) --pdata->m_bDeviceWriteOpen;
������
else --pdata->m_bDeviceReadOpen;
MOD_DEC_USE_COUNT;
return 0;}
/* nonblocking read/write*/
ssize_t device_noblock_read(struct file *filp, char *buffer, size_t length,loff_t *offset)
{ int count = 0; struct devicedata_t *pdata = &s_devicedata[DEVICE_NOBLOCK];
if (is_buffer_empty(pdata)) { /* end of file */ printk(DEVICE_NAME " (NOBLOCK) : Read return EOF\n"); return 0; }
while (!is_buffer_empty(pdata) && length > 1) { read_buffer_char(pdata, buffer); ++buffer; --length; ++count; }
������
put_user(0, buffer); ++count;
printk(DEVICE_NAME " (NOBLOCK) : Read %d bytes\n", count);
return count;}
ssize_t device_noblock_write(struct file *filp, const char *buffer, size_t length,loff_t *offset)
{ int count = 0; struct devicedata_t *pdata = &s_devicedata[DEVICE_NOBLOCK];
if (is_buffer_full(pdata)) { /* out of buffer */ printk(DEVICE_NAME " (NOBLOCK) : Write out of buffer\n"); return -ENOMEM; }
while (!is_buffer_full(pdata) && length > 0) { write_buffer_char(pdata, (char *) buffer); ++buffer; --length; ++count; }
filp->f_pos += count;
printk(DEVICE_NAME " (NOBLOCK) : Write %d bytes\n", count);
������
return count;}
/* blocking on write read/write*/
ssize_t device_writeblock_read(struct file *filp, char *buffer, size_t length,loff_t *offset)
{ int count = 0; struct devicedata_t *pdata = &s_devicedata[DEVICE_WRITEBLOCK];
if (is_buffer_empty(pdata)) /* end of file */ return 0;
while (!is_buffer_empty(pdata) && length > 1) { read_buffer_char(pdata, buffer); ++buffer; --length; ++count; wake_up_interruptible(&pdata->m_writequeue); }
put_user(0, buffer); ++count;
printk(DEVICE_NAME " (WRITEBLOCK) : Read %d bytes\n", count);
return count;
������
}
ssize_t device_writeblock_write(struct file *filp, const char *buffer, size_t length,loff_t *offset)
{ int count = 0; struct devicedata_t *pdata = &s_devicedata[DEVICE_WRITEBLOCK];
if (is_buffer_full(pdata)) { printk(DEVICE_NAME " (WRITEBLOCK) : Write go to sleep\n"); interruptible_sleep_on(&pdata->m_writequeue); printk(DEVICE_NAME " (WRITEBLOCK) : Write wake up\n"); }
while (!is_buffer_full(pdata) && length > 0) { write_buffer_char(pdata, (char *) buffer); ++buffer; --length; ++count; }
filp->f_pos += count;
printk(DEVICE_NAME " (WRITEBLOCK) : Write %d bytes\n", count);
return count;}
/* blocking on read read/write
������
*/
ssize_t device_block_read(struct file *filp, char *buffer, size_t length,loff_t *offset)
{ int count = 0; struct devicedata_t *pdata = &s_devicedata[DEVICE_BLOCK];
if (is_buffer_empty(pdata)) { /* end of file */ printk(DEVICE_NAME " (BLOCK) : Read go to sleep\n"); interruptible_sleep_on(&pdata->m_readqueue); printk(DEVICE_NAME " (BLOCK) : Read wake up\n"); }
while (!is_buffer_empty(pdata) && length > 1) { read_buffer_char(pdata, buffer); ++buffer; --length; ++count; wake_up_interruptible(&pdata->m_writequeue); }
put_user(0, buffer); ++count;
printk(DEVICE_NAME " : Read %d bytes\n", count);
return count;}
������
ssize_t device_block_write(struct file *filp, const char *buffer, size_t length,loff_t *offset)
{ int count = 0; struct devicedata_t *pdata = &s_devicedata[DEVICE_BLOCK];
if (is_buffer_full(pdata)) { printk(DEVICE_NAME " (BLOCK) : Write go to sleep\n"); interruptible_sleep_on(&pdata->m_writequeue); printk(DEVICE_NAME " (BLOCK) : Write wake up\n"); }
while (!is_buffer_full(pdata) && length > 0) { write_buffer_char(pdata, (char *) buffer); ++buffer; --length; ++count; wake_up_interruptible(&pdata->m_readqueue); }
filp->f_pos += count;
printk(DEVICE_NAME " (BLOCK) : Write %d bytes\n", count);
return count;}
/* * Buffer Management */
�����
int is_buffer_empty(struct devicedata_t *pdata){ return (pdata->m_nBufPos == pdata->m_nBufEnd) ? 1 : 0;}
int is_buffer_full(struct devicedata_t *pdata){ int pos = pdata->m_nBufEnd + 1;
if (pos == pdata->m_nBufSize) pos = 0;
return (pos == pdata->m_nBufPos) ? 1 : 0;}
int read_buffer_char(struct devicedata_t *pdata, char *buffer){ if (is_buffer_empty(pdata)) return -1;
put_user(pdata->m_strBuf[pdata->m_nBufPos], buffer);
if (++pdata->m_nBufPos == pdata->m_nBufSize) pdata->m_nBufPos = 0;
return 0;}
int write_buffer_char(struct devicedata_t *pdata, char *buffer)
�����
{ if (is_buffer_full(pdata)) return -1;
get_user(pdata->m_strBuf[pdata->m_nBufEnd], buffer);
if (++pdata->m_nBufEnd == pdata->m_nBufSize) pdata->m_nBufEnd = 0;
return 0;}
������
! >?���[�D��ß9��J� ��#������@�����@ ���65$void *kmalloc(size_t, int);
����74wA��>?�a��ß3@�¹º»16void kfree(const void *);
kmalloc()p���ßàU�>?�a��J�16
! ?¢Z�� :���.0��ø-34� �
��MODULE_PARM(var, type)�27��� :���.0���Y0��jya���
�����jyÒ6�-������jyB�tu�v��c��±16
0�:����$Þ�� I.��I�.��JJL��5����� M���ÈN��O]16
������0��5��� 5��0��������0���������-�� ��� 0���-
��MODULE_PARM_DESC(var, description)�27��� :���.0�Z�Ì��÷Ò���1
��:���.0����34� �
insmoda���Å� ��H����ÈNp��¸�����16
insmod���� U�?¢�������jya��ß3]�¸p��lä�16
��"6(6���øZ�]� �:��0��?��jya� :���.0�a����¸����y�z�16
! .������ÏZ�� �1½�[\��3]����� ��� �
�� �:�9� ��� � -Z� ø-}]� inode� $0VW�� i_rdev� ÊÀ�� ���� �:��� kl�
.������Ïa�:�y�z16
���:�����Å� file�$0VW�� 9����:��0����� .������ÏZ� � �� 1�_� ��°p�(�
�]� 9����:��0������,N�°ym��Pp�(�Q_�1½�[\��3vO��16
������
������������ ������� !�"�#$
! À�� �� ��� :���� +,-�.Z� ɵ� ��%��� �Îa� Î@i]� +,�� �B�16� 1�p�
.�����9Z�H��+,Z���Ea�4O3~����y�z]�+,�� :����+,-�.Z��B�16
! VÉ�òó� �� :����+,-�.�DE^��a��03@� /proc�RZ� curproc� ]�+,���B
3~�� �� +,Z�� �4a� -v3¶� �a� {|�� ��%��� ɵ� �Îa� [�p�� wm��
Î@»16� @4�]� proc_dir_entry�� read_proc� °y� ùDEa� �03@� "#3_� VÉ
�16� �� òóU� VÉ34]� Q�w� ªä�� y� z]� ��E�� 74B� PAGE_SIZEa� '�� y
±1]�#S��z16���74a�']���E�ªäU�Tø��+,�ÕÖ��VÉ3@Æ��16�!�
�]� /proc� RZ� bufproc� ]� +,�� �B3~� �� +,Z� 4O�� 3¶� 4O�� �0�� �
N� ��Z� �k�� 3~�� �� +,Z�� ��m�¶� 3�@Z� 4O�� �0�� Î@»16� @4�]
������:��0���9� 9����:��0������03@�VÉ�16�@4�]�UZ�²� ÂU� PAGE_SIZE�J���±16
! 6�G� òó� �� ���� ?¢�� ���� ¤� cat� Ò×�� �º�� ɵ� ��%��� �ÎB� �%]�
VWö16�!���?¢������¤� cat�Ò×���03@�+,Z���Ea�4O��¤��a���
m&��Å�øZ������0����}]��VWö16
! 9�� �
��:����+,-�.Z� ���:������B3~�6�G34
��:����+,-�.��4���Tø#�/0�� ���@?��0�B��� ��9:����+,�wm4
������
! Filename : curproc.c
/* Current Process Information*/
#include <linux/kernel.h>#include <linux/module.h>
#ifdef CONFIG_MODVERSIONS#define MODVERSIONS#include <linux/modversions.h>#endif
#include <linux/proc_fs.h>
/* * Function Prototypes */
static int curproc_read_proc(char *buf, char **start, off_t offset, int count,int *eof, void *data);
/* * Proc Entry */
struct proc_dir_entry curproc_proc_entry = {0, /* low inode */7, "curproc", /* name length and name" */
������
S_IFREG | S_IRUGO, /* mode */1, 0, 0, /* nlink, uid, gid */0, /* size */NULL, /* inode operation : NULL = use default */NULL, /* get_info */NULL, /* fill_inode */NULL, NULL, NULL, /* next, parent, subdir */NULL, /* data */curproc_read_proc, /* read_proc */NULL, /* write_proc */NULL, /* readlink_proc *//* nothing more */
};
/* * Module Startup/Cleanup */
int init_module(void){
int retcode;
printk("Loading CurProc Module\n");
if ((retcode = proc_register(&proc_root, &curproc_proc_entry)) < 0)return retcode;
return 0;}
������
void cleanup_module(void){
printk("Unloading CurProc Module\n");
proc_unregister(&proc_root, curproc_proc_entry.low_ino);}
int curproc_read_proc(char *buf, char **start, off_t offset, int count, int *eof,void *data)
{static char *s_strState[] = {
"Running", "Sleep", "Disk Sleep", "Zombie", "Stop", "Swap", "Exclusive" };
int state, stateid;
count = 0;
for (stateid = 0, state = current->state; state > 0; state >>= 1, ++stateid);
count += sprintf(buf + count, "pid : %d\n", current->pid);count += sprintf(buf + count, "command : %s\n", current->comm);count += sprintf(buf + count, "state : %s\n", s_strState[stateid]);count += sprintf(buf + count, "priority : %ld\n", current->priority);count += sprintf(buf + count, "counter : %ld\n", current->counter);count += sprintf(buf + count, "processor : %d\n", current->processor);
return count;}
������
! Filename : bufproc.c
/* Read/Write Proc Filesystem*/
#include <linux/kernel.h>#include <linux/module.h>
#ifdef CONFIG_MODVERSIONS#define MODVERSIONS#include <linux/modversions.h>#endif
#include <linux/proc_fs.h>#include <asm/uaccess.h>
/* * Global Variables */
#define BUFFER_LEN 1024
static char s_strBuf[BUFFER_LEN];
/* * Function Prototypes */
static int bufproc_open(struct inode *inodep, struct file *filp);
������
static int bufproc_release(struct inode *inodep, struct file *filp);static ssize_t bufproc_read(struct file *filp, char *buf, size_t count, loff_t *ppos);static ssize_t bufproc_write(struct file *filp, const char *buf, size_t count, loff_t *ppos);
static int bufproc_permission(struct inode * inodep, int op);
/* * Proc Entry */
static struct file_operations bufproc_file_operations = { open: bufproc_open, release: bufproc_release, read: bufproc_read, write: bufproc_write,};
static struct inode_operations bufproc_inode_operations = { default_file_ops: &bufproc_file_operations, permission: bufproc_permission,};
struct proc_dir_entry bufproc_proc_entry = { 0, /* low inode */ 7, "bufproc", /* name length and name" */ S_IFREG | S_IRUGO | S_IWUSR, /* mode */ 1, 0, 0, /* nlink, uid, gid */ 0, /* size */ &bufproc_inode_operations, /* inode operation */ /* nothing more */};
�����
/* * Module Startup/Cleanup */
int init_module(void){ int retcode;
printk("Loading BufProc Module\n");
if ((retcode = proc_register(&proc_root, &bufproc_proc_entry)) < 0) return retcode;
return 0;}
void cleanup_module(void){ printk("Unloading BufProc Module\n");
proc_unregister(&proc_root, bufproc_proc_entry.low_ino);}
/* * File Operations */
int bufproc_open(struct inode *inodep, struct file *filp){ MOD_INC_USE_COUNT;
�����
return 0;}
int bufproc_release(struct inode *inodep, struct file *filp){ MOD_DEC_USE_COUNT;
return 0;}
ssize_t bufproc_read(struct file *filp, char *buf, size_t count, loff_t *ppos){ static int s_bRead = 0;
int len = strlen(s_strBuf);
if (len == 0 || s_bRead) { s_bRead = 0; return 0; }
if (len > count - 1) len = count - 1;
copy_to_user(buf, s_strBuf, len); put_user(0, &buf[len]); ++len;
s_bRead = 1;
return len;
������
}
ssize_t bufproc_write(struct file *filp, const char *buf, size_t count, loff_t *ppos){ int len = (count > BUFFER_LEN - 1) ? BUFFER_LEN - 1 : count;
if (count == 0) return 0;
copy_from_user(s_strBuf, buf, len); s_strBuf[len] = '\0';
return len;}
/* * Inode Operations */
/* op : 0 = execute, 2 = write, 4 = read return : 0 = permission ok, -EACCES = permission error*/
int bufproc_permission(struct inode * inodep, int op){ if (op == 4 || (op == 2 && current->euid == 0)) return 0;
return -EACCES;}
������
! :����+,-�.�X� �
��{J��7B�tu �>?��Z�´µ3]�+,-�.
����%�Z_��Î�ø-��Y0$Z_�-�.��$%���<�ø-34����wm�Y6
����E]�{J��4a�3]�-SZ�wm�16
! :����+,-�.��R� �
/proc/cpuinfo���-�.Z�÷l�� ;,2Z�Ì���Îa�Î@»16
/proc/devices����������� ����ÀO��Î@»16
/proc/interrupts���ɵ�Y0�D�DEFG��ÀO��Î@»16
/proc/meminfo���>?��Y0��6��Î@»16
/proc/sys/kernel/hostname���5� 0��.��Î@i~���y�z_��16
/proc/sys/vm/freepages���.���.�.����?��5�-5�9��:�-��¸���~���y�z_��16
! :����+,-�.�fO9��Jint proc_register(struct proc_dir_entry *, struct proc_dir_entry *);int proc_unregister(struct proc_dir_entry *, int);
/proc�PÅÆ��� proc_dir_entry� �proc_root/proc/sys�PÅÆ��� proc_dir_entry� �proc_sys
������
! struct proc_dir_entryunsigned short low_ino;unsigned short namelen;const char *name;mode_t mode;nlink_t nlink;uid_t uid;gid_t gid;unsigned long size;struct inode_operations * ops;int (*get_info)(char *, char **, off_t, int, int);void (*fill_inode)(struct inode *, int);struct proc_dir_entry *next, *parent, *subdir;void *data;int (*read_proc)(char *page, char **start, off_t off, int count,
int *eof, void *data);int (*write_proc)(struct file *file, const char *buffer,
unsigned long count, void *data);int (*readlink_proc)(struct proc_dir_entry *de, char *page);unsigned int count; /* use count */int deleted; /* delete flag */
������
! struct inode_operationstruct file_operations * default_file_ops;
�� ����Z��Y0��4ö� 9��E�:��0��� int (*create) (struct inode *,struct dentry *,int);int (*readpage) (struct file *, struct page *);int (*writepage) (struct file *, struct page *);int (*permission) (struct inode *, int);
��%�B�+,Z�Ì���5�,��3º���Å�³����o��ZB�@Na�õ�
ɵ� ���²�+,��<��4:�@hB�a�B�~�õ�
! proc_dir_entryZ�fO3]� .��
��+,��I¦���:F16
��+,��P�� �
S_IFREG����-�����9��S_IFLNK������4S_IFBLK�������4����� :�����9��S_IFCHR����5����0������ :�����9��S_IFDIR�������0���S_IFFIFO�����.��:�:�#<�<�$
��� ��:�.� ������S_IRUSR��S_IWUSR��S_IXUSR�HC�S_IRWXU��-���:�:�. ������S_IRGRP��S_IWGRP��S_IXGRP�HC�S_IRWXG���05��:�.� ������S_IROTH��S_IWOTH��S_IXOTH�HC�S_RWXO
������
���%&�������� �������#$'(
! À�� ������ ��Z�� ���@?��0·��òóp��kla�J���y�z]� ���0���VÉ�16
.�����9Z�� ����� ��E²�� ��E� �a� 1~�� ��a� `ì]� ���0�� Ò×�� �B3~��
���� ��a��03]���� Z�]� ���0��Ò×���º��kla�J��16
! VÉòó� �� .�����986��r�a�y�3@�\¦�16�ìM� ���0��Ò×����3@��a�['�+,Z
�~������ ���� 9��� �:��0���Z� ���0��Ò×���B�16� ���0��Ò×��H�3]�°y]
$¡Z_�i��Ò×Z�� ��ß�\Á��H��16��¤����� ��a��03]�,®
;���� ��\¦3@�klZ���Ea���3~�����0��Ò×����16
! 6�G�òó� ��6�G���� �����klZ���Ea���3~� ���0��Ò×p����Ea�ôD
3~����E���a�1���¤���a�`ì~��1-���E���a�1��16�@4��õ9
B��3]����%]��VWö16
! 9�� �
�����0���H�3]����� ���J\
������ ���� ���0����03]�6�G���� �J\
! 6�G�9�� �insmod minibuf6.o./minibuf_testrmmod minibuf6
������
! Filename : minibuf.h
/* MiniBuf.h*/
#ifndef __MINIBUF_H#define __MINIBUF_H
#include <linux/ioctl.h>
#define MINIBUF_MAGICNUM 254
struct minibuf_peek_t { char *m_buf; int m_maxlen;};
#define IOCTL_MINIBUF_MAKEEMPTY _IO(MINIBUF_MAGICNUM, 0)#define IOCTL_MINIBUF_GETLENGTH _IOR(MINIBUF_MAGICNUM, 1, int *)#define IOCTL_MINIBUF_PEEK _IOR(MINIBUF_MAGICNUM, 2, struct minibuf_peek_t *)
#endif
������
! Filename : minibuf6.c
/* Mini Buffer Device Driver : ioctl*/
#include <linux/kernel.h>#include <linux/module.h>
#ifdef CONFIG_MODVERSIONS#define MODVERSIONS#include <linux/modversions.h>#endif
#include <linux/fs.h>#include <linux/kdev_t.h>#include <asm/uaccess.h>
#include "minibuf.h"
/* * Device Definitions */
#define DEVICE_NAME "MiniBuf"#define BUFFER_LEN 1024
/* * Global Variables */
������
static int s_nMajor = 0;
static int s_bDeviceReadOpen = 0;static int s_bDeviceWriteOpen = 0;static char s_strBuf[BUFFER_LEN];static int s_nBufPos = 0, s_nBufEnd = 0;
static struct wait_queue *s_wq = NULL;
/* * Function Prototypes */
static int device_open(struct inode *inode, struct file *filp);static int device_release(struct inode *inode, struct file *filp);static ssize_t device_read(struct file *filp, char *buffer, size_t length,
loff_t *offset);static ssize_t device_write(struct file *filp, const char *buffer, size_t length,
loff_t *offset);static int device_ioctl(struct inode *inodep, struct file *filp, unsigned int cmd,
unsigned long arg);
static int peek_buffer(struct minibuf_peek_t *pdata);
static int is_buffer_empty(void);static int is_buffer_full(void);static int read_buffer_char(char *buffer);static int write_buffer_char(char *buffer);static void make_buffer_empty(void);
�����
static int get_buffer_length(void);
/* * Device Operations */
struct file_operations device_fops = { NULL, /* seek */ device_read, /* read */ device_write, /* write */ NULL, /* readdir */ NULL, /* select */ device_ioctl, /* ioctl */ NULL, /* mmap */ device_open, /* open */ NULL, /* flush */ device_release /* release */};
/* * Module startup/cleanup */
int init_module(void){ printk("Loading Mini Buffer Module\n");
if ((s_nMajor = register_chrdev(0, DEVICE_NAME, &device_fops)) < 0) { printk(DEVICE_NAME " : Device registration failed (%d)\n", s_nMajor); return s_nMajor;
�����
}
printk(DEVICE_NAME " : Device registered with Major Number = %d\n", s_nMajor);
return 0;}
void cleanup_module(void){ int nRetCode;
printk("Unloading Mini Buf Module\n");
if ((nRetCode = unregister_chrdev(s_nMajor, DEVICE_NAME)) < 0) printk(DEVICE_NAME " : Device unregistration failed (%d)\n", nRetCode);}
/* * Device Operations */
int device_open(struct inode *inode, struct file *filp){ printk(DEVICE_NAME " : Device open (%d, %d)\n", MAJOR(inode->i_rdev), MINOR(inode->i_rdev));
if ((filp->f_flags & O_ACCMODE) & (O_WRONLY | O_RDWR)) { if (s_bDeviceWriteOpen) { printk(DEVICE_NAME " : Device already open for writing\n"); return -EBUSY;
������
} else ++s_bDeviceWriteOpen; } else { if (s_bDeviceReadOpen) { printk(DEVICE_NAME " : Device already open for reading\n"); return -EBUSY; } else ++s_bDeviceReadOpen; }
MOD_INC_USE_COUNT;
return 0;}
int device_release(struct inode *inode, struct file *filp){ printk(DEVICE_NAME " : Device release (%d, %d)\n", MAJOR(inode->i_rdev), MINOR(inode->i_rdev));
if ((filp->f_flags & O_ACCMODE) & (O_WRONLY | O_RDWR)) --s_bDeviceWriteOpen; else --s_bDeviceReadOpen;
MOD_DEC_USE_COUNT;
return 0;}
������
ssize_t device_read(struct file *filp, char *buffer, size_t length, loff_t *offset){ int count = 0;
if (is_buffer_empty()) /* end of file */ return 0;
while (!is_buffer_empty() && length > 1) { read_buffer_char(buffer); ++buffer; --length; ++count; wake_up_interruptible(&s_wq); }
put_user(0, buffer); ++count;
printk(DEVICE_NAME " : Read %d bytes\n", count);
return count;}
ssize_t device_write(struct file *filp, const char *buffer, size_t length,loff_t *offset)
{ int count = 0;
if (is_buffer_full()) { printk(DEVICE_NAME " : Write go to sleep\n");
������
interruptible_sleep_on(&s_wq); printk(DEVICE_NAME " : Write wake up\n"); }
while (!is_buffer_full() && length > 0) { write_buffer_char((char *) buffer); ++buffer; --length; ++count; }
filp->f_pos += count;
printk(DEVICE_NAME " : Write %d bytes\n", count);
return count;}
int device_ioctl(struct inode *inodep, struct file *filp, unsigned int cmd,unsigned long arg)
{ int length;
printk(DEVICE_NAME " : ioctl, cmd = %x, arg = %x\n", cmd, arg);
switch (cmd) { case IOCTL_MINIBUF_MAKEEMPTY : printk(DEVICE_NAME " : ioctl : make empty\n"); make_buffer_empty(); break;
������
case IOCTL_MINIBUF_GETLENGTH : printk(DEVICE_NAME " : ioctl : get length\n"); length = get_buffer_length(); put_user(length, (int *) arg); break; case IOCTL_MINIBUF_PEEK : printk(DEVICE_NAME " : ioctl : peek\n"); return peek_buffer((struct minibuf_peek_t *) arg); default : printk(DEVICE_NAME " : ioctl : unknown\n"); return -EINVAL; }
return 0;}
/* * Higher level buffer management */
int peek_buffer(struct minibuf_peek_t *pdata){ int count = 0, nBufPos; int maxlen; char *buffer;
if (is_buffer_empty()) /* end of file */ return 0;
get_user(buffer, &pdata->m_buf);
������
get_user(maxlen, &pdata->m_maxlen);
nBufPos = s_nBufPos;
while (!is_buffer_empty() && maxlen > 1) { read_buffer_char(buffer); ++buffer; --maxlen; ++count; }
put_user(0, buffer); ++count;
s_nBufPos = nBufPos;
printk(DEVICE_NAME " : Peek %d bytes\n", count);
return count;}
/* * Buffer Management */
int is_buffer_empty(void){ return (s_nBufPos == s_nBufEnd) ? 1 : 0;}
������
int is_buffer_full(void){ int pos = s_nBufEnd + 1;
if (pos == BUFFER_LEN) pos = 0;
return (pos == s_nBufPos) ? 1 : 0;}
int read_buffer_char(char *buffer){ if (is_buffer_empty()) return -1;
put_user(s_strBuf[s_nBufPos], buffer);
if (++s_nBufPos == BUFFER_LEN) s_nBufPos = 0;
return 0;}
int write_buffer_char(char *buffer){ if (is_buffer_full()) return -1;
get_user(s_strBuf[s_nBufEnd], buffer);
������
if (++s_nBufEnd == BUFFER_LEN) s_nBufEnd = 0;
return 0;}
void make_buffer_empty(void){ s_nBufPos = s_nBufEnd = 0;}
int get_buffer_length(void){ int pos = s_nBufEnd;
if (pos < s_nBufPos) pos += BUFFER_LEN;
return pos - s_nBufPos;}
������
! Filename : minibuf_test.c
/* Mini Buffer Test : ioctl*/
#include <stdio.h>#include <fcntl.h>
#include "minibuf.h"
#define BUFFER_LEN 1024
static void minibuf_read(int fd);static void minibuf_write(int fd, char *str);static void minibuf_peek(int fd);static void minibuf_ioctl_test(int fd);
static int minibuf_ioctl_get_length(int fd);static int minibuf_ioctl_make_empty(int fd);static int minibuf_ioctl_peek(int fd, char *buffer, int length);
int main(void){ int fd;
if ((fd = open("/dev/minibuf", O_RDWR)) < 0) { printf("Error opening device file\n"); return 1; }
�����
minibuf_read(fd); minibuf_write(fd, "Hello, World"); minibuf_peek(fd); minibuf_ioctl_test(fd); minibuf_read(fd);
close(fd);
return 0;}
void minibuf_read(int fd){ int len; char str[BUFFER_LEN];
while ((len = read(fd, str, BUFFER_LEN)) > 0) printf("READ : %s (%d)\n", str, len);}
void minibuf_write(int fd, char *str){ int len;
if ((len = write(fd, str, strlen(str))) > 0) printf("WRITE : %s (%d)\n", str, len);}
void minibuf_peek(int fd)
����
{ int len; char str[BUFFER_LEN];
if ((len = minibuf_ioctl_peek(fd, str, BUFFER_LEN)) >= 0) printf("PEEK : %s (%d)\n", str, len); else printf("PEEK : Error\n");}
void minibuf_ioctl_test(int fd){ int length;
length = minibuf_ioctl_get_length(fd); printf("Buffer Length = %d\n", length); minibuf_ioctl_make_empty(fd); printf("Make buffer empty\n"); length = minibuf_ioctl_get_length(fd); printf("Buffer Length = %d\n", length);}
int minibuf_ioctl_get_length(int fd){ int length;
return (ioctl(fd, IOCTL_MINIBUF_GETLENGTH, &length) >= 0) ? length : -1;}
int minibuf_ioctl_make_empty(int fd)
�����
{ return ioctl(fd, IOCTL_MINIBUF_MAKEEMPTY, 0) >= 0 ? 0 : -1;}
int minibuf_ioctl_peek(int fd, char *buffer, int length){ struct minibuf_peek_t peek_data = { buffer, length };
return ioctl(fd, IOCTL_MINIBUF_PEEK, &peek_data);}
�����
! ���0��#��:�0���0:�0����0���$�����@?��0�·Z�kla�J���y�z]�òó
��ÌN©��kl]� ���@?��0wp��J�a�1���y]�±16
��klZ_�@hB��1B��Ò×���\�y�z]�kl�~ñ�� �0���:���0�Jb
��� ��:��-��..��-�int ioctl(int fd, int cmd, ...);
��9����:��0������0�9���int (*ioctl) (struct inode *inodep, struct file *filp, unsigned int cmd, unsigned long arg);
���.����#������@� .@���0�65$
���0���{|��Ò×�a�V©3]�¸
{y���ß�Ò×��1½����� ��Z_��ºv��JB�ÐÑ3���vO�()
��òóp��Ѧ3@��-�.�ø«Z���(}]�åìa�B���S�16
�����0���.��]$�wm4� ��_IOC(direction, type, number, size)direction�����E��ø ò^
_IOC_NONE��_IOC_READ��_IOC_WRITEtype���.�-�����.��6�B������� ��31�~��
number����� ���0��Ò×��V©��y�z]�]$
size���ø ����E��74
#define _IO(type,nr) _IOC(_IOC_NONE,(type),(nr),0)#define _IOR(type,nr,size) _IOC(_IOC_READ,(type),(nr),sizeof(size))#define _IOW(type,nr,size) _IOC(_IOC_WRITE,(type),(nr),sizeof(size))#define _IOWR(type,nr,size) _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),sizeof(size))
�����
��:��9������..�����
FIOCLEX��� 0���� �������9��-FIONCLEX����������� �������9��-FIOASYNC��� 0@� 0�� ���5����� �?��0�9���05�9��FIONBIO����������4��-��@�
filp->f_flagsZ�� �E������;*�9��-a�_16fcntl(fd, F_SETFTL, fcntl(fd, F_GETTFL) | O_NONBLOCK));
�����0��H�� �����0��5������°yZ��'��� �.��Ò×Z�� �� ?�0�5�������H�switch (cmd) {case MYDRV_IORESET :
......case MYDRV_IOCLEAR :
......default :
return -EINVAL;}
�����
�����)�*+,
! printk()a��0���5
��Bk�`a+�òóp��ë���Å�>-�a���3@�[\��ôD�16
����-����
>-���fb��÷�3@��ë���fb��>-�w���3]�¿��B�3_��16
>-��-\Z� B�C�ÂU�òóp����6
��-������3���p¶� DEFAULT_MESSAGE_LOGLEVELZ��ß�16
console_loglevelÎ1�cU� ��-�����kw����16
sys_syslog� � 0.�������� klogd -c�Ò×p�� console_loglevel��jå6
��>-�����9�� �
printk()]��N�����Z�>-�a�4O�16�#��9��?�B�$
klogd���%�]��4�p�����>-�a�àt²�� syslogdZ_�ø-�16
syslogd]� /etc/syslog.conf�+,��÷�Z�� ����>-���H��òó�õ��
16�4ö�÷�U�>-�a� /var/log/messagesZ��k3]�¿�16
klogd -f <filename>�Ò×p�����>-�a��v��+,Z��k��y�z16
���5�?�Z�w�>-�a���3_�34� �
DEBUG���B�}��z��Åw�>-����� #���G�2/��§+,$
òó� �$#ifdef DEBUGprintk(..................)#endif
�����
òó� "$#ifdef DEBUG#define dprintk(fmt, args...) printk(KERN_DEBUG " : " fmt, ##args)#else#define dprintk(fmt, args...)#endif
! ë���ÅZw��Î���34
��printk()]�ë�3���U�>-�a�Ú����3_�3���Ú��Y03¶�-�.��dº16
��:����+,-�.�/034� �
Y0$B��V��-SZ��Îa�wm���Y0$Z_�Î@»16
ɵ�?¢���<���N���Ea�Î@è�y�z���5��0�3_��16
�����0��/034� �
���0�Z��5�����Ò×�m���B�16
�5��ÎB��ß� ���0��Ò×��t]�Ye��tu¶�f�}���]16
:����+,-�.Î1�IvB�g���Y0òóZ�Jh��±16
! Y0$���� ��Y03]�9�Z���J�ôD34� �
���¨��Y0$���� �����16
��-�.�A���� ��a�Y03]�N©Z� printf()�p���Îa����16
��strace���� ���03@�-�.�°yB�³�]�¿��ÔY�16
-�.�AZ�ø-��D$9���õ9a�Î@»16strace cat /dev/minibuf
�����
! ��: ����Z��JB�ÐÑ3@�����i��Å� #R�������:���0����� $���: �>-�a����16
����: �>-�]�ZhB�ÐÑ��ir²�ï��E��<�� 0��4��<�f�w�����16
��ksymoops���� ���03@� ��: �>-�a��j����JB�ÐÑ��¾��:yz16ksymoops /boot/System.map < oops
��#��Zh���lw�Ìþ�k��y�z��l���ô��Ó����la�:º¶��mL��Ó�a
���Æ�3~��ß-��>?���<�f��Î@i���p���-�.���p���JB�ÐÑ
7��Å��¶Z� ��: �>-�a���3��C3¨���n��ÚU� ��: �>-�a���3@�>
-�B���B�\�yv�z16
��System.map���:��0��ÃÄlw�tu ���Z�m��z]�?��ÃÄmZ�Ì���Îa�B�~�z16
����§+,��Å�wm�16
����9�?¢��ù°��ÃÄ�ÀO�wm4� �
System.mapZ]�����?¢��ÃÄ��m�z���p��� /proc/ksymsZ]�?¢�
ÃÄ��m�z�w���Z�� �:��0}��ÛU�ÃÄmZ�Ì���ÎB�±16
��0�@:���@4 �. �@���0@�� 0.6.�:�N� ��� @�6�@�@��N��?4��L:���0�O���APA��O"M��N� ��0����C� �.��� 6.�:
! *������.:�
��4. -��.:�������i��Å� ������>-�²� ��: �>-�a�á�SZ��k�16
�� .���� ������i�������#���$a�o�3@� ��+Z��k��3~�����>?�Z��
�}�� zp� *�+� ��� ����� ����0a� �16� ����0¤Z� �øZ� ��+Z� �k�
���a��B�� � 0.��������03@�o���q~��7Z��k��y�z16
��kS� �
�����
��0���:0����0�0Z�v�Y0��y�z16
��+Z��k��34��Z�g�16
��#S� �
÷lB��r16
����0-Z�>?���0���d��]��16
���*;�� #������*����;�����.:�$� ���;����7��4��� /03@����� i�� � ���a
?�:� :��0�0���Z��k���16� �;��� �������J�Å�Z� ��0���:0�sZ��ZhB�ÐÑ�
åì�Y0��y�±16
! 4������Z� ���4�� 4�������--�
��;,2Z�� 9���0B�ÐÑ3¨���,�2�G�úa���¨��� �������� ��������Ï��16
��4���������0���:0�� �������0���:0�ÛZ��H��16
�� ��-�� 0:�����4:���0a��0���5��B�
������ �.����lw�tu � .������ �.���v�:~�z��?¢��5v�B�316
�� 0��4Z�� 9��.�:���0�a��03@� 0��4�0���a���yv�z16
! 4-�����.�0�-������ ����������|[��J��16
�� !� Ì�� 4Ha� ������ Õõ3~� �s� 4HZ� �5�� ���� 1½� 4HZ�� -��a� ¹
�16
�� ���� ���--��-� �.����� ù°3@� §+,3~� 9��.� :���0�a� �03vO� §+,3@
G�<������w�16
�����
�� ��-�� 0:�����4:���0a��0���5�B�6
���5����Å�1½�4HB�ë�3~��÷lB��ºì���?¢��ira�ôD3��C�16
! +�-����� �Q�*�
��ɵ��<a�Î@i]�ú� �
�5�90R��������� 5�?�..���;0��R��������� 5�?� 0�0��0�R���������� 5�?��-� 0�
��çy��ú�WQ������çy��[\��3vO���y�z16
�����÷�����Å� +�-����� �Q�����ü��§+,�Æ��16
Y0ó� ����0R�� �QRB��..���C
��..������#����.�0�0���@ � �Q60�0$� ���� ��������.���0��9�� � 0.������ 5�0��99�.������.:������0�..���
����
���!�-�.�/�0�+�1�234
! ��Z���-"� �
���P;�#����P�.�;���4$���-�.Z��ɵ�-"��c�3]�kl
�� ��U� 0�.�� ��0���:0B� ÐÑ�� Å31� �N�� -"�� c�3]� ��Ea� =B-ü� ɵ
-"��c��16
��HZ�����Z� 0�.����0���:0B�ÐÑ3]� y6�#������@�����@:���.65$
��jiffies���+L«JB�N£��¤�ÐÑ��tM� 0��4�� y6���>�99� �H��@3S��
��0�.����0���:0��ÐÑ�"u�W�� �
HZ�¸�=B� ��Iv]�dº��w�®X�Iv]�v 16
HZ�¸�?r� ��Iv]�v ��w�®X�Iv]�dº16
! ɵ�-"� �
��do_gettimeofday()�°ya����� ������.���� �����#���-"��1��y�z16
���N�p��ɵ�-"��B�~�z]� struct timeval xtime�X�jyB�z�w����
���N�$0VWa�no�o�3]�¿U�en3���16
��ɵ�-"�14� �struct timeval {
time_t tv_sec;suseconds_t tv_usec;
}
#include <linux/time.h>void do_gettimeofday(struct timeval *tv);
�����
! ���-�������òó� ������ ��?��0��-
41�]�[ÛZ����?�Z�HI�z�Æ�3���1½�\Á��{|��y�±�6
unsigned long j = jiffies + delay * HZ;
while (jiffies < j);
��òó� "��
1½�{|����%�B�±p¶�HI�$¡��MØ}��{|w6
while (jiffies < j)schedule();
��òó� 8��
0�.��0���03@��3]�-"[� ����4��-��}��Bk�xy��16
@4�� 0�.��0U��Õ��-"� #>�99� �R�αB�tu $
struct wait_queue *wq = NULL;
interruptible_sleep_on_timeout(&wait, timeout);
! �5��0�������������#$�#������@� .@����65$
��:��0�0�:���void udelay(unsigned long usecs);��>�99� �Î1�'�TU�-"�� ����a�����Y0�16
�� N£�� Å� HÖ�� ��-�+�: a� zp�� loops_per_secondsa� HÖ3~�� �a� zp
������
���3]�-"[�� ����a����r�GI��p�� ���:a�{16
���� ��?��0��-���� �����H��(((�.��� ��������.��� �����H��(((�.���� ����
! ���:�;
���5�ç��\Á��{|��-"����3@�,��-"��å9��¤Z�{|3_��16
��:�;�=Z�m�"�\ÁU���w�{|�16
��|K�òN��:�;� ��~���}y��:�;a�Jb�16struct timer_struct {
unsigned long expires;void (*fn)(void);
};
unsigned long timer_active;struct timer_struct timer_table[32];
��*�+�òN��:�;� �����4���� 0�òNp��}yZ�J���±16struct timer_list {
struct timer_list *next; /* MUST be first element */struct timer_list *prev;unsigned long expires;unsigned long data;void (*function)(unsigned long);
};
void init_timer(struct timer_list * timer)void add_timer(struct timer_list * timer);int del_timer(struct timer_list * timer);
������
! Filename : mytimer.c
/* Timer Handling*/
#include <linux/kernel.h>#include <linux/module.h>
#ifdef CONFIG_MODVERSIONS#define MODVERSIONS#include <linux/modversions.h>#endif
#include <linux/sched.h>#include <linux/timer.h>
/* * type definitions */
struct mytimer_data { int m_times; int m_endtimes;};
/* * global variables */
������
static struct timer_list s_mytimer;static struct mytimer_data s_mytimer_data;
/* * function prototypes */
static void mytimer_proc(unsigned long ptr);static void add_mytimer(void);static void del_mytimer(void);
/* * Module startup/cleanup */
int init_module(void){ printk("Loading Timer\n");
s_mytimer_data.m_times = 0; s_mytimer_data.m_endtimes = 5;
add_mytimer();
return 0;}
void cleanup_module(void){ del_mytimer();
������
printk("Unloading Timer\n");}
void mytimer_proc(unsigned long ptr){ struct mytimer_data *pdata = (struct mytimer_data *) ptr;
++pdata->m_times;
printk("Timer called %d/%d\n", pdata->m_times, pdata->m_endtimes);
if (pdata->m_times < pdata->m_endtimes) add_mytimer();}
void add_mytimer(void){ init_timer(&s_mytimer);
s_mytimer.function = mytimer_proc; s_mytimer.data = (unsigned long) &s_mytimer_data; s_mytimer.expires = jiffies + HZ * 5; /* 5 seconds */
add_timer(&s_mytimer);}
void del_mytimer(void){ del_timer(&s_mytimer);}
������
! ;��0�0��� �����0�0��� � 0.�������0��:�H��ɵ�����[\��ɵ���%�3~�cé��z��Å6
����4��-��B�316
schedule()�°ya�³h��1½���%�B�{|}_���y�z16
����0���:0����0�0���DEFG�ÙmhZ��DEFGa�H�3~�z]�����]�ɵ���%�²
�c3_�.���-SZ�{|}]�DEFG²�ñY���66
��5���?�����0���:0����0�0���DEFG�ÙmhB�H�}~�z�� Å���ß� DEFGB�~
����<Z��{|}���åìZ�� �1½�DEFGB�~�}4v��16
�� �90?�����0���:0����0�0���DEFGB�Z0���<Z��{|}�����00�.�5��9��0�.���ç�
0� 4�Q��H��.���-SZ�{|�16
����0���:0����0�0Z����y�±]�,� �
�����0�:��� 3~�cé}]�,� �������0�:��� B���B�±16
�����0�:��� B�±pË�� � �� :��²���E�ªä����y�±16
�5�����-��� �5���#$�°ya�N�¨���"o�p��Na�y�z]�°ym6
?�:��ÐÑ��y�z]�,� ��>?���ßÂ�� �0�.����:��0�����tí�¿m6
� ��..���Z�� :�-�9���0B�ÐÑ��y�z]�,� ����:�E0�E� �#$Â��Y0$�>?
�a�o�3]�°ym6
! P� 4�=����ç��\Á���d�v�-"����¤Z�{|3]�òó����3��16
��:�����-��H�3¨���3�I�a���3_�J���Å�Y0�16
�� ��00�.� 5��9H�� ��0���:0a�H���Å� ��0���:0� 5�����a�H���¤Z��U�\Á��H��
Åv�Y0�16
������
��0� 4�Q���$0VW� �����4���� 0�È<struct tq_struct {
struct tq_struct *next; /* linked list of active bh's */unsigned long sync; /* must be initialized to zero */void (*routine)(void *); /* function to call */void *data; /* argument to function */
};
��0� 4�Q���°y#define DECLARE_TASK_QUEUE(q)
*�+�<�7�=a����16void queue_task(struct tq_struct *bh_pointer, task_queue *bh_list);
<�7�=Z�<�7a��B�16void run_task_queue(task_queue *list);
=Z�z]�?��\Á��{|�16
��:��9����0� 4�Q�� ���-�.�[\�������������U� 0� 4�Q��m6
tq_timer���:�;�DEFGB�ÐÑ3&��Å�H�}]�=� #��0���:0�0�.$
tq_immediate���B����g½�-"�Z�H�}]�=� #��0���:0�0�.$
tq_scheduler������hB�{|æ�Å�H�}]�=� #��0���0���:0�0�.$
! >?���� ����4.�������49��#������@�����@ ���65$void *kmalloc(size_t size, int priority);
malloc()H� ����74wA��>?�a���16void kfree(const void *ptr);
free()H� malloc()p� �ßàU >?�a��J�16
��:�����0����>?���ß�òN�����16
������
GFP_KERNEL : (__GFP_MED | __GFP_WAIT | __GFP_IO),®�p��Y03]� :�����0��� ����4��-��,�K�y�z16
>?�B�N�3¶� ?�:�f������>?��b"��w�16GFP_USER : (__GFP_LOW | __GFP_WAIT | __GFP_IO)GFP_NFS : (__GFP_HIGH | __GFP_WAIT | __GFP_IO)
�<��+,-�.Z��}vO�v��>?�a��ßà��Å�Y0�16GFP_ATOMIC : (__GFP_HIGH)
��0���:0�0�.Z��Y0� # �5�����-��,���]�Û}]�åì$
__min_free_pages²��c3_�B���?��>?��Y0�16GFP_DMA : (__GFP_DMA)
�+��>?����16
���p�� � +���3��L�Z�ÕI��>?�a��ß�16
�� �1��
��Z�]� :�-�:������03@�^��a�c��16
:�-�����0��������0��������+��Bk�ñÕ3_�Y0��y�z]�òó
������������0������������Â�� :�-�����0��òNZ�]�c�34��r16
PAGE_SIZE�D�"T�Î1�W~�\U�74��>?���ß��Bk�xy��16
74J���z1� ���"%*�
! >?���� ��:�-�#������@�����@..65$unsigned long get_free_page(int gfp_mask);
��^��a��ß��¤� :�-a��+16unsigned long __get_free_page(int gfp_mask);
��^��a���16
������
unsigned long __get_dma_pages(int gfp_mask, order)�+�0�>?��^��a���16
unsigned long __get_free_pages(int gfp_mask, unsigned long gfp_order);����74wA��^��a���16
void free_page(unsigned long addr);��^��a��J�16
void free_pages(unsigned long addr, unsigned long order);@h�^��a��J�16
! >?���� ��.�������9��#������@�����@.�����65$void * vmalloc(unsigned long size);
B��ir�b"Z��>?�a���16void vfree(void * addr);
vmalloc()p���ßàU�>?�a��J�16
��B��ir�b"#��0�������� � :��$Z��ÕI��>?��L����ß�16
kmalloc()U�����ir�b"Z��>?�a���16
���p��ÕI}���U�>?�v�B��ir�b"Z�]�ÕI�p��Ä�y�z16
:�-�0������03@�����ir��jä�16
�����>?��b"Z�w��]�L��16
�����D�b"Z�w�ÕI}�����l���ßàU�>?�]�,Â��Y0��y�zp���ÌN
©��åì��ß�U�>?�B����p��ÕI}�Æ���ë�]�±16
�� r�GI��p�w� Y03]� Ü� ��a� �ßà�� Å� Y0�1� #74� J��� kmalloc()Î1�71$
�����
���5&�������� ��!�"�6�7�8(
! À�� �� �5� ç��� -�.� A�� ³��� Å� �a� B�C�� 1½� ,�� 3vO� �16� @4�]
sys_open()9� sys_close()� -�.� A�� B�C��� -�.�� Û�¦�� ���� -�.� A�
³�]�w�ôD3~������-�.�A�H��°ya��Ì��³h»16
! VÉòó� ��ø«�-�.�A�Ùmh��ÀO��B�~�z]� sys_call_table��E���0��n
o�y�3@�� �:��� ��� �-�.�A��Ùmha��k3~����� ��Z�*��J\��Ùmh�
Ìl�16�Ìl��ÙmhZ�]�'���D$��¸����3~������-�.�A�Ùmha�³h
��\Á��H���¤�õ9¸��Î@»16
! 6�G�òó� ��?¢������¤� �:��� ��� �-�.�A��³\�Å31�>-�B���}]��ô
D�16
! 9�� �
���:������ �-�.�A��B�C4
��1½�-�.�A��B�C4
! 6�G�9�� �insmod syscall.olsrmmod syscall
������
! Filename : syscall.c
/* System Call Spy*/
#include <linux/kernel.h>#include <linux/module.h>
#ifdef CONFIG_MODVERSIONS#define MODVERSIONS#include <linux/modversions.h>#endif
#include <sys/syscall.h>#include <asm/uaccess.h>
/* * Global Variable */
extern void *sys_call_table[];
static asmlinkage int (*orig_sys_open)(const char *filename, int flags, int mode);static asmlinkage int (*orig_sys_close)(unsigned int fd);
/* * Function Prototypes */
�������
static asmlinkage int my_sys_open(const char *filename, int flags, int mode);static asmlinkage int my_sys_close(unsigned int fd);
/* * Module startup/cleanup */
int init_module(void){ printk("Loading System Call Spy Module\n");
orig_sys_open = sys_call_table[__NR_open]; sys_call_table[__NR_open] = my_sys_open;
orig_sys_close = sys_call_table[__NR_close]; sys_call_table[__NR_close] = my_sys_close;
return 0;}
void cleanup_module(void){ printk("Unloading System Call Spy Module\n");
if (sys_call_table[__NR_open] != my_sys_open || sys_call_table[__NR_close] != my_sys_close) { printk("Error : other program changed the system call handler\n"); }
sys_call_table[__NR_open] = orig_sys_open;
�������
sys_call_table[__NR_close] = orig_sys_close;}
/* * System Call handler */
int my_sys_open(const char *filename, int flags, int mode){ int retcode; char fname[1024], *cpsrc = (char *) filename, *cpdes = fname;
for (;;) { get_user(*cpdes, cpsrc); if (*cpdes == 0) break; ++cpdes; ++cpsrc; }
printk("sys_open : pid = %d, filename = %s, flags = %x, mode = %x\n", current->pid, fname, flags, mode); retcode = orig_sys_open(filename, flags, mode); printk("sys_open : result = %d\n", retcode);
return retcode;}
int my_sys_close(unsigned int fd){
�������
int retcode;
printk("sys_close : pid = %d, fd = %d\n", current->pid, fd); retcode = orig_sys_close(fd); printk("sys_close : result = %d\n", retcode);
return retcode;}
�������
! -�.�A� �
��Y0$�?�Z�z]�X0��� ������4���Y0��y�zvO�3]�¿
��-�.�A��N�¶�Y0$�?�Z�����?���916
����Z��-�.�A��H�3¶����?�Z��Y0$�?������\Á��HI�16
��-�.�A��P�� �
include/asm/unistd.hZ���}��z16
! -�.�A��H�9�� �
�����8"Z�� � 0.�����U� (�%(�DEFGa��3@�H��16
��Y0$���� Z�� � 0.�������N�¶�DEFGB�ÐÑ�16
��(�%(�DEFG�Ùmh]� system_call�#���5@�8% @4���@�0��6�$�16
�� system_call� °y]� 'D»� � 0.� ����� �Ï#U��$Z� � � sys_call_table[]Z� z]
°ya�Ï��16
����-�.�A�H��°y]��ß�Ò×��{|3~�õ9a�¹º»16
��ret_from_sys_call�°ya���� � 0.�������3l~�Y0$�?���(��16
�������
����9:;<�=4
! DEFG��klB� ;,2Z_� �0B�ÐÑ7���:ºi]�òó
��� ���5����� ��0��:�����-@��0���:0
:�����-���kl��j�a�i4�p��?-����<��j�a�:t�16
��0���:0���klZ�¡åî���~�\Á��31B��<�j�B�z��Å�klB�:º»16
���+�]�DEFG²�ÕH���Y0�16
! DEFG�Ùmh��fO9���� ��#������@�����@ �5�65$int request_irq(unsigned int irq,
void (*handler)(int, void *, struct pt_regs *),unsigned long flags,const char *device,void *dev_id);
irq���DEFG��Ï6
handler���DEFGa�H���Ùmh�°y
flags���DEFG�c�²�cé�� 9��-
SA_INTERRUPT���9� 0���0���:0@ ��?���0���:0a�V�34����Y06
SA_SHIRQ���DEFGa�bñ��y�z1]�¿���:F16
SA_SAMPLE_RANDOM� �� �����.�¸��w�]��4@�16�kl��DEFGB�`�
4�p��ÐÑ3]�åì� �����.�¸��w�]��v����16
device���DEFG�rñ$a�/-��Å��:���$Þ
�������
dev_id���DEFGa�bñ��Å�$¡��V�34�����Y03]�ñ,��¸6
ÙmhZ_���Ea�ø-3]�À�p�v�Y0��y�z16void free_irq(unsigned int irq, void *dev_id);
DEFGa�®��16�fO��Å����� ��Q�²� �E��a��Ì�����16
��DEFGa�fO@����-S� �
���0E.�������?¢��Y03���' v�DEFGa�Sñ3]��JB�ÐÑ
�:����DEFGa�1½���� ��Y03~�zp¶�Sñ3��C��yv�z16
fO� ��H�p��kla� �:���ÅZ�DEFGa�fO
��� ��3�@p�� ��� a����DEFGa���
��@:���@��0���:0
ɵ�Sñ3~�z]�DEFG��ÀO��Î@»16
! ��=���:t�4
��÷�B��� ��=���,;�Z�]� ��=B�-�.Z������ß�16
kl��ÎZ��k��DEFG�÷�����²��Y0�16
��~��� ��=������Z�]� ��=B�3�I��p��~�}��z16
/»�D�òóp�� ��=a�:t��yB�±16
�$�?¢������Å� ��=���@��ira�D$��ø-�16
"$�kl�� �@��L���¸��Z� ��=��ÎB�z]�åì��a����16
8$�kl�ç¦���03@� ��=��Ïa� :�����-�16
����=�:�����-���#������@�����@��0���:065$unsigned long probe_irq_on(void);
ɵ��ß}���U�DEFG�� ��0.� 4a�¹º»16
{�3¶� (��¹º»16
�������
int probe_irq_off(unsigned long);:���E���¤Z�ÐÑ��DEFG��Ïa�¹º»16
DEFGB�ÐÑ3���p¶� (��¹º»16
@h���DEFGB�ÐÑ3¶� #�.��-��� ��0�0���$��ya�¹º»16
:����34ø� ��probe_irq_on()��N½16
kl��ç¦���03@�3�I��p��DEFGa�ÐÑ->16
:���a�3l~� ��probe_irq_off()a�N½16
! DEFG�Ùmh�VÉ� �void handler(int irq, void *dev_id, struct pt_regs *regs);��DEFG�Ùmh]� 5���?�����0���:0����0�0Z��{|�16
��DEFG�H��-"�� r����16
DEFG�ÙmhZ�]��ß�DEFGB�~����<Z��{|�16
9� 0���0���:0��åì�?��DEFGB�~����<Z��{|�16
-�.���/��\Á�����]�DEFG�H��-"��T��yO�ý16
DEFG�H�Z�-"��Ú��ë���åì�DEFG�H�a�DEFG�ÙmhZ��H�
�Æ�3]�N©9������tv�}]� N©p������� ��N©U� ��00�.�5��9�� 0� 4
Q��a��03@�H��16
! DEFG�Z09�~�� ��#������@� .@��Q65$void disable_irq(unsigned int);
��DEFGa�~��16void enable_irq(unsigned int);
DEFGa�Z0�16
�������
��?��DEFG�Ùmh]�ɵ�H��D�DEFGB�~����<Z��H��16
��DEFG�Z0@~�]�DEFG�ÙmhZ��Y0��y�±16
��9� 0���0���:0@ ��?���0���:0
9� 0� ��0���:0� �� ?�� DEFGB� ~��� �<Z�� DEFGa� H��16� DEFG� H�
v�Z� 1½� DEFG]� ÐÑ3�� �]16� v�� H��Æ3]� DEFG� H�-Z
Y0�16
��?���0���:0���1½�DEFGB�Z0���<Z��DEFGa�H��16
! ��00�.�3��9
����0���:0�5�����Z��H��Æ�3]�\Á��ÚU�åì��®�-� ��0���:0�5�����Z��H�3�
�tv�}]�,�� ��0���:0�5�����a�g����¤�{|��y�zvO��16
��0�:�5��9�@���00�.�5��9
0�:�5��9Z�]�kl����Ea��v����Z��k3~� ��00�.�5��9a�/-�16
��00�.�5��9]�vJ����Ea���%�Z_�ø-�16
����00�.�5��9�H��v�Z]�?��DEFGB�Z0�16
����00�.�5��9�V� ��#������@�����@��0���:065$
9���0����:���0���ÝÞ9�Y0@N²�H�@Na��:�]� ��0.� 4��wm�16volatile unsigned char bh_running;atomic_t bh_mask_count[32];unsigned long bh_active;unsigned long bh_mask;void (*bh_base[32])(void);
����00�.�5��9�c�y� ��#������@� .@ �90��Q65$void init_bh(int nr, void (*routine)(void))
��00�.�5��9a�fO�16
������
void remove_bh(int nr)��00�.�5��9a����16
void mark_bh(int nr)��00�.�5��9a�H��Æ�3]�åì�³h»16
void disable_bh(int nr)� ��00�.�5��9�H�a�~��16
void enable_bh(int nr)� ��00�.�5��9�H�a�Z0�16
void do_bottom_half(void)��00�.�5��9�H��°y6��0���E9��.E � E����#$Z��³�16
����00�.�5��9��� �
��00�.�5��9��}y]���}��z16
ÌN©�ç������ ��Z_��ß}��z16
����Z��Y03]� ��00�.�5��9��
IMMEDIATE_BH���0QE�..���0a�H��16
TQUEUE_BH���0QE0�.�a�H��16
NET_BH����0?��4�����Z��Y0�16
CONSOLE_BH���00�� ?�0�5��-TIMER_BH���0�.����0���:0�H��¤�Ï�}���4����0�.�a�H��16
! DEFG�bñ� �
��DEFGa�Sñ��Å�DEFG�bñ�@Na�÷��16
�� ���� ��DEFG�bñZ�Ì�������¿U�±�w��3�I��p��DEFG�bñB�³B
�3�]� �16� 3�I��p�� klZ� DEFGB� ÐÑ7]�� @Na� ôD�� y� z]
������
òó��z1¶�r�GI��D�òóp���õ��B�316
��,;�����hZ��4ö�p��DEFG�bñB�B�3vO�÷H}��z16
����0���:0�5�����
DEFGB�ÐÑ3¶�DEFGa�bñ3]�?��ÙmhB�³�16
DEFG�ÙmhB�³���Å�®�-�$¡��DEFGB�ÐÑ��¿U�tu16
3�I�a�ÔY3@�DEFGB�$¡��klZ��ÐÑ��åìZw�DEFGa�H�
! ���������0�������Ó�B�DN3��C��Y�Z�$¡��1q]���EB� ��0���:0�0�.Z��~���ß�Ó�]
��Ea�j�a�DN3��C3~�H�a�HI3]�åì6
������������0�.����:��0���9�1½�Ó��Y�Z���B����H�3��åi6
�����������0�����ÐÑ3]�åì� �
°y��Z�� �5�����-��ÐÑ� ��n@"o�p�� �5���#$�°yB�³��åì6
����4��-��:��0���
DEFG�Ùmh²�-�.�A��1½�Ó�²���Ea�bñ3]�åì
�����������0�����õ� �
b�Ea�Y03���~�����a�Y0�16
b�Ea�y������[�DEFGa�~�->16
��Ea�o���� ���4��Y0��� �0�.����:��0���p��w�16
�������
����'�>?�@?
! 3�I��J�Z�Y03]�$�� �
���@��,��0�����@��+.���������0���:0���+�
! �@��,��0�J�� ��#������@� .@��65��������@�����@��:��065$
���@��:��0�L���� �
�@��L���Y034�øZ��ß�L���1½�� ��B�Y03]��ÔY�16
{J�� �ß�� à�� �$1~� ��� �ß� L��� o��� y� ±]� ¿U� tu�w�� ��
v� kl� Y��� �¹�� @4� ���� L��� ÔY3~�� Y0�D� åì� Zha� ÐÑ
3]�¿��ý16int check_region(unsigned long from, unsigned long extent);
�ß� �@��:��0�L���`�z]��ÔY�16void request_region(unsigned long from, unsigned long extent, const char *name);
�@��:��0�L����ßà]16void release_region(unsigned long from, unsigned long extent);
�@��:��0�L���Y0��3l~�$���®��16
��3�����E� �@�unsigned int inb(unsigned int port);void outb(unsigned char data, unsigned int port);
�������
%���0���E� �@�unsigned int inw(unsigned int port);void outw(unsigned short data, unsigned int port);
� ���0���E� �@�unsigned int inl(unsigned int port);void outl(unsigned int data, unsigned int port);
8"���0���E� �@�
��3��� :��0��ÕI����E� �@�unsigned int insb(unsigned int port, void *addr, unsigned long count);void outsb(unsigned int port, void *addr, unsigned long count);
%���0���E� �@�unsigned int insw(unsigned int port, void *addr, unsigned long count);void outsw(unsigned int port, void *addr, unsigned long count);
� ���0���E� �@�unsigned int insl(unsigned int port, void *addr, unsigned long count);void outsl(unsigned int port, void *addr, unsigned long count);
8"���0���E� �@�
��:�� ��-��@�
;,2��Iv]� �@���� ��IvÎ1�g�16
|K�3�I���Z]�Iv¯��Å�Z� :��0��@�Z����Ea��l]�åìB�z16
inb_p()��outb_p()�ÂU�°ya��03@� �@��Y�Z� ����a�èyz16
! �@��+.����J�� ��#������@� .@��65$
���@��..����L���� �
��0���:0�� �@��:��0Z�²�ÂU�L���ß�>�u�U�±16
���@��..�����@�
�������
unsigned int readb(void *addr);void writeb(unsigned int data, void *addr);
%���0���E� �@�unsigned int readw(void *addr);void writew(unsigned int data, void *addr);
� ���0���E� �@�unsigned int readl(void *addr);void writel(unsigned int data, void *addr);
8"���0���E� �@�void memset_io(void *addr, unsigned int data, int count);
����74wA�������E��÷�void memcpy_fromio(void *dest, void *src, int count);
�@��>?�Z�� ��+p����E�ø void memcpy_toio(void *dst, void *src, int count);
��+Z�� �@��>?�����E�ø
���@��..����.�::��-
,;��klÂU�¾Z�]� ..����.�:������ �@��>?�a�o��16void * ioremap (unsigned long offset, unsigned long size)
�@��>?�a�B��>?��ir�� .�::��-���16void iounmap(void *addr);
.�::��-��>?�a��J�16
! �+��J�� ��#������@� .@�.�65$
���+��L���� �int request_dma(unsigned int dmanr, const char * device_id);
�+���5�������V�16��5����U� (�&G��B�316
�������
void free_dma(unsigned int dmanr);
�+���5������®��16
���+������� �kmalloc(size, GFP_DMA);get_dma_pages(mask, pages);
���+��Y034� �void enable_dma(unsigned int dmanr);
�+���5����Z�ñx����EB�z1~�/-�16void disable_dma(unsigned int dmanr);
�+���5������ �� ����->16void set_dma_mode(unsigned int dmanr, char mode);
�+�a��3@���Ea��������a����16void set_dma_addr(unsigned int dmanr, unsigned int a);
�+������ira����16void set_dma_page(unsigned int dmanr, char pagenr);
ø ��ir�� :�-��-� 0�w��÷��16void set_dma_count(unsigned int dmanr, unsigned int count);
ø ����E��74a����16int get_dma_residue(unsigned int dmanr);
�+�a�����ø ����E��Z���U�]$a�:º»16
�+��Ò×��¦b�@Na�:º»16void clear_dma_ff(unsigned int dmanr);
�������
���� A�������� ���������������BC����������
! À�� ��"#��LM� ��a�w�16�@4�]�{J�LM�kla�Y03���~�� ��+��Z
,N�>?�a��7H��/03]�"#�� ��+��7a�w�16
! VÉòó� �� �$� ��� � ��²� 3�B��� +,� ÕÖ�� ��3~� �a� fO�16� �Å
+,�ÕÖ��Z�� ���@?��0]�,®�D�LM� ���@?��0�°ya��0�16���~�LM���
� ���VÉZ�ë��� �Q� 0�°y²�LM�74���E�74��kl�74�f�����16�?¢�
����Å� ��+��7a����>?�a��ß�16� ��+��7��74]�?¢Z�~�}��z
p��� ?¢�� �������� Å� �ßàU� >?�a� �J�16� �Q� 0� °yZ�]� ���-�� ��²
�ßàU�>?��Y�Z� ���@?��0a�y|�16
! 6�G�òó� ��?¢������¤�LM����� ��a�B�ú]�kl�+,��w�16�@4
�]� �GF�;GE�2+� }ywA�� .����� ��.��a� Z034� �Z� @h��� kl� +,�� wm
y� z16� .4"9 � Ò×p�� kl� +,�Z� +,-�.�� w�� ¤� .���0a� ��� +,-�.H�
Y0���16
! 9�� �
��"#�� ��+��7�LM����� ���J\
����+��7��Z�+,-�.�V����6�G
�������
! 6�G�9�� �insmod minird.o minird_size=1024mknod /dev/minird0 b [major] 0dd if=/dev/zero of=/dev/minird0 bs=1024 count=1024mke2fs /dev/minird0 1024mkdir /mnt/othermount /dev/minird0 /mnt/othercp /bin/ls /mnt/otherls /mnt/otherumount /mnt/othermount /dev/minird0 /mnt/otherls /mnt/otherumount /mnt/otherrmmod minird
�������
! Filename : minird.c
/* Mini RAM Disk Driver*/
#include <linux/kernel.h>#include <linux/module.h>
#include <linux/vmalloc.h>#include <asm/uaccess.h>
#define MINIRD_MAJOR 0 /* major number : dynamic allocation */#define DEVICE_NAME "MiniRD" /* device name */#define DEVICE_NUM 2 /* minor device number */
#define MINIRD_DEF_SIZE 1024 /* default device size in KB */#define MINIRD_BLOCKSIZE 1024 /* sizeof block */#define MINIRD_SECTSIZE 512 /* sizeof sector */
extern int s_nMajor;
#define MAJOR_NR (s_nMajor)#define DEVICE_REQUEST minird_request#define DEVICE_NR(device) (MINOR(device))#define DEVICE_ON(device) /* do nothing */#define DEVICE_OFF(device) /* do nothing */#define DEVICE_NO_RANDOM
#include <linux/blk.h>
�������
/* * Globak Variables */
static int s_nMajor = 0;static int minird_size = MINIRD_DEF_SIZE;
static int s_kbsize[DEVICE_NUM]; /* size in blocks of 1024 bytes */static int s_blocksize[DEVICE_NUM]; /* size of 1024 byte block */static int s_hardsect[DEVICE_NUM]; /* sizeof real block in bytes */static int s_length[DEVICE_NUM]; /* size of disks in bytes */static char *s_data[DEVICE_NUM];
MODULE_PARM(minird_size, "i");MODULE_PARM_DESC(minird_size, "RAM Disk size in KB");
/* * Function Prototypes */
static int minird_open(struct inode *inodep, struct file *filp);static int minird_release(struct inode *inodep, struct file *filp);static int minird_ioctl(struct inode *inodep, struct file *filp, unsigned int cmd,
unsigned long arg);
static void minird_request(void);
/* * Device Operations
������
*/
static struct file_operations minird_fops = { open : minird_open, release : minird_release, read : block_read, write : block_write, ioctl : minird_ioctl, fsync : block_fsync,};
/* * Module startup/cleanup */
int init_module(void){ int i, j;
printk("Loading Mini RAM Disk Module\n");
for (i = 0; i < DEVICE_NUM; ++i) { s_kbsize[i] = minird_size; s_blocksize[i] = MINIRD_BLOCKSIZE; s_hardsect[i] = MINIRD_SECTSIZE; s_length[i] = (minird_size << BLOCK_SIZE_BITS);
if ((s_data[i] = vmalloc(s_length[i])) == NULL) { for (j = 0; j < i; ++j) vfree(s_data[j]);
������
return -ENOMEM; } }
if ((s_nMajor = register_blkdev(MINIRD_MAJOR, DEVICE_NAME, &minird_fops)) < 0) { printk(DEVICE_NAME " : Device registration failed (%d)\n", s_nMajor); return s_nMajor; }
printk(DEVICE_NAME " : Device registered with Major Number = %d\n", MAJOR_NR);
blk_dev[MAJOR_NR].request_fn = &minird_request;
blk_size[MAJOR_NR] = s_kbsize; blksize_size[MAJOR_NR] = s_blocksize; hardsect_size[MAJOR_NR] = s_hardsect;
return 0;}
void cleanup_module(void){ int i;
printk("Unloading Mini RAM Disk Module\n");
for (i = 0; i < DEVICE_NUM; ++i) destroy_buffers(MKDEV(MAJOR_NR, i)); /* flush the devices */
for (i = 0; i < DEVICE_NUM; ++i)
�������
vfree(s_data[i]);
unregister_blkdev(MAJOR_NR, DEVICE_NAME);
blk_dev[MAJOR_NR].request_fn = NULL;
blk_size[MAJOR_NR] = NULL; blksize_size[MAJOR_NR] = NULL; hardsect_size[MAJOR_NR] = NULL;}
/* * Device Operations */
int minird_open(struct inode *inodep, struct file *filp){ if (DEVICE_NR(inodep->i_rdev) >= DEVICE_NUM) return -ENXIO;
MOD_INC_USE_COUNT;
return 0;}
int minird_release(struct inode *inodep, struct file *filp){ MOD_DEC_USE_COUNT;
return 0;
�������
}
int minird_ioctl(struct inode *inodep, struct file *filp, unsigned int cmd,unsigned long arg)
{ int minor = DEVICE_NR(inodep->i_rdev); long devsize;
switch (cmd) { case BLKGETSIZE : /* return device size */ devsize = s_length[minor] / s_hardsect[minor]; return put_user(devsize, (long *) arg);
case BLKSSZGET : /* block size of media */ return put_user(s_blocksize[minor], (int *) arg);
case BLKFLSBUF : /* flush */ if (!capable(CAP_SYS_ADMIN)) return -EACCES; destroy_buffers(inodep->i_rdev); break;
RO_IOCTLS(inodep->i_rdev, arg); /* default ioctl for BLKROSET, BLKROGET */
default : return -EINVAL; }
return 0;}
�������
/* * Request Processing */
void minird_request(void){ char *ptr; int size, minor;
while (1) { INIT_REQUEST;
if ((minor = DEVICE_NR(CURRENT_DEV)) > DEVICE_NUM) { printk(DEVICE_NAME " : Unknown Minor Device\n"); end_request(0); continue; }
ptr = s_data[minor] + CURRENT->sector * s_hardsect[minor]; size = CURRENT->current_nr_sectors * s_hardsect[minor];
if (ptr + size > s_data[minor] + s_length[minor]) { printk(DEVICE_NAME " : Request past end of device\n"); end_request(0); continue; }
switch (CURRENT->cmd) { case READ :
�������
memcpy(CURRENT->buffer, ptr, size); break; case WRITE : memcpy(ptr, CURRENT->buffer, size); break; default : end_request(0); continue; }
end_request(1); }}
�������
! LM����� ����J\ó
���$����� ��²�3�B��� ·N²]� +,�DE^���� Õõ}��+,�ÕÖm
#9��E�:��0��� $��Jb�16
������ ��]� .�>�����.����V�}���$����� ��²]�����c�}
Ë���$����� ��²� .�>�����.��B���v��c±16
��+,�-�.U� ��99�� ���5a�����LM���a�o��16�����-a����\ÁU
,®� ���@?��0²]� û�4� Å�Z� +,� ÕÖ�� ���@?��0²� �v�� ��� �-a� ��
���@?��0a�34�����v��DE^��B�´µ�1� ���Q� 0
����� �Q� 0�°ya�VÉ�Æ�3~����°yZ��{��D� ���@?��0�\Á���16����L
M��74���E��74��ø«�74�f�����LM�kla�c�3]�ø��jyZ�÷��
��16
! kl��fO9��J
��fO� �int register_blkdev(unsigned int major, const char *name, struct file_operations *fops);
�$klZ�²�3�B��� .�>����ÏZ� (����3@�[�p���ßà��y�z16
���J� �int unregister_blkdev(unsigned int major, const char * name);
! +,�ÕÖ#9��E�:��0��� $
��LM����� ����+,�ÕÖU��$����� ��²�W~�¯�B�z16
�� ���@?��0@9 ���� °y]� ��� VÉ�� ë�B� ±p��� ,®�p�� block_read()�block_write()��block_fsync()�°ya��Ì��Y0�16
�������
���:������ ����$����� ��Z�²�3�B���VÉ�16
�� ���0�� ��LM����� ��Z]�b��p��Y03]�@hB�� ���0��Ò×��zp����
m��ÌN©U�®�-������i�Æ��16
BLKGETSIZE���kl��74a��E��}y��¹º»16
BLKFLSBUF�����a� 9�� 5�16
BLKRAGET��BLKRASET����3�����4#�����5��$a���¿D��1�%~�÷��16
BLKROSET��BLKROGET���kl�� ���������9��-a�1�%~�jå�16����VÉ3���~
,®�p�� RO_IOCTLS(dev, where)�27�a����VÉ�16
BLKRRPART���+���6�L��1-����16�#+����z]�klZ�$
HDIO_GETGEO���3��7��VWa����16�#3���7$
���5�4E.���E�5��-���.���a���y�z]�kl��åì� .���B���]��:t�4���
��Y0�16�.���B�9�åì� ��������U�åì� (��¹ºi¶��16
�� ������0� �� .���B�9�åìZ�³�]�°y�16�*�+� .���a�H�34�����N��
Îa�y�3]�\Á��3¶��16
! V�������B�����@��465C��øZ�����y�z]�¿@���Æ���¿
��MAJOR_NR���kl�� .�>����Ï
��DEVICE_NAME���kl���¼
��DEVICE_NR(kdev_t device)���{J�kl��Ïa�14����27�6�Î�U�MINOR(device)��DEVICE_ON(kdev_t device)����DEVICE_OFF(kdev_t device)���end_request()B�\Á��3l~�N�]�°y
��DEVICE_NO_RANDOM��������.���.��a�w�]��4@3����
�������
! LM����� ���cé�$0VWstruct blk_dev_struct {
request_fn_proc *request_fn; => request Ç� ́ �
queue_proc *queue;void *data;struct request *current_request; => ÈÉ Ç�H requeststruct request plug;struct tq_struct plug_tq;
};
extern struct blk_dev_struct blk_dev[MAX_BLKDEV];
?��LM����� �Q� 0Z�cé���Îa�B�~�z]�ÝÞextern int * blk_size[MAX_BLKDEV];
.�>�����.����.�������.��Z��ß3]�kl��74a� *��#����:F16extern int * blksize_size[MAX_BLKDEV];
.�>�����.����.�������.��Z��ß3]�kl��LM��74a� ��0�#����:F16extern int * hardsect_size[MAX_BLKDEV];
.�>��� ��.���� .����� ��.��Z� �ß3]� kl�� �E�� 74a� ��0#��� �:F16
#�9���0H��"$extern struct sec_size * blk_sec[MAX_BLKDEV];extern int * max_readahead[MAX_BLKDEV];extern int * max_sectors[MAX_BLKDEV];extern int * max_segments[MAX_BLKDEV];
��LM����� ��]�LM���a�fO��¤� ��4E�I.�>��J6�Q� 0E9���`�3@
klZ�Ì��@hB���Îa�÷��Æ��16
��{��D���E�ø U� ��4E�I.�>��J6�Q� 0E9��°ya������q�16
�������
��ɵ�àU�� U� ��4E�I.�>��J6�����0E�Q� 0��"##� ;2��G�P
struct request {volatile int rq_status;kdev_t rq_dev; => Ç�H \] (major/minor)int cmd; /* READ or WRITE */ => Ç�H �Ê : READ/WRITEint errors;unsigned long sector; => %Ë� I/O� ÌÍ ÎÀunsigned long nr_sectors; => clustered request� BJH I Ag� ÎÀ� Ï�unsigned long nr_segments;unsigned long current_nr_sectors; => %Ë� I/O� ÎÀ� Ï�char * buffer; => ¿�À� µq7 Ð S¾ (S¾ Ñ;6 (� S¾)struct semaphore * sem;struct buffer_head * bh; => ÌÍ S¾6 ±� � struct buffer_head * bhtail; => �WÒ S¾6 ±� � struct request * next; => �Ó request6 ±� ²" ��Z
};
�� �Q� 0E9�#$��VÉ� �� ;2��G�PZ�� � �@�a�y|�16� �.�B� �G���¶������k3
~��7��PG�¶�kl���k�16��@�a�-\���l]� �0����74]� �����0E��E �0�� Z
m��zp�� �Q� 0a�H���Å31� ��E�Q� 0#$�°ya�N½16
� �
������
DE� ��FGH@� ��!"��I�����������
! À�� �� �$� ��� � ��a� {J�� \¦�16� @4�]� ,;� �S�� ��� � ��a
\¦3@��a��03@�Y+�a����16
! VÉòó� �� ,;� �S��� Y+�a� ��3]� ¿U� %��� �@�� ùGa� J�°p�(� B�316
(�'"��(�'8��(� ��ùGZ��"��¸����i¶����� i+y��r�B� ÐÑ3_� ��y�zp��
r�B�����_v���y�z16�@4Z�¡¢@����:�;a��03@�����-"[Û�r�
B� �vO� �16� @h��� ��� ��àU� åì� �a� �N�� =Z� £t!~� :�;a� ®(�p
���03@�r�B��vO��16
! �¤� �
�������������-"[� ,;��S�������y�z16
��klZ�,®�D� write()�Ò×���º������y�z16���Å�ø-}]���E��ÈN
U�r���ê�²��a�Â��B�~�z]� struct playnote_t��ÝÞ�16
�� ioctl()�Ò×������Y+�a�����y�z16���Å�ø-}]���E��ÈNU�ø«
��� }y²� .ù²� struct playnote_t�� ÝÞ�� B�ú]� ùDEa� B�~� z]
struct playdata_t�16
��1B�� ioctl()�Ò×������?¢��J���y�z16
��2���������i+y��������16
��2����PG�������������16
����2�����r�B�����_��16
,��K����¥��Õi�16�ɵ���}~�z]���EB�zp¶�=��¦Z��B�16
������
�P�,� �� ?�� Õia� �#�16� =Z� z]� ?�� �0U� §J�16� @4�� ¨��� z]
��%�w��§JB�B�316
�GPPG+,����write()����3]���E��4ö�.ùa����16
;3��/GPG+,����ɵ�Õi}]��¥��.ùa�_16
/GP;�2�P���ɵ��U����}ya�:º»16
��klZ� Ì����U� `[4�p�� ��y� z16�©� ,®�D� åìZ]���� � �� ��E
a�?!�����¤Z������%���¹tB�w��`[4�D�åìZ]�=Zw��U�¤
��¹t"16
��kl]�[-Z�3������ w����y�z16
! 6�G�òó� ������4���JÌ��[\3]��ôD�16����0���VÉ�� �����@��� �����@�:����@
0�:�f��[\��\[��ôD3~� ?��0#$��4O��¥ÎÌ��Õi}]��ôD�16� � ���5�����
?��0a�÷���¤�JÌ��[\3]��ôD�16
! 9�� �
������ ���J\
��6�G���� �J\
! 6�G�9�� �insmod pcplay.omknod /dev/pcplay c [major] 0./pcplay_testrmmod pcplay
�������
! Filename : pcplay.h
/* * PC Speaker Play */
#ifndef __PCPLAY_H#define __PCPLAY_H
#include <linux/ioctl.h>
#define PCPLAY_MAGICNUM 0x37
#define PCPLAY_DEFAULTTEMPO 120
enum OCTAVE_NONE, OCTAVE_1, OCTAVE_2, OCTAVE_3, OCTAVE_4, OCTAVE_5, OCTAVE_6, OCTAVE_7, OCTAVE_8, OCTAVE_NUM};
enum NOTE_C,
�������
NOTE_CSHARP, NOTE_DFLAT = NOTE_CSHARP, NOTE_D, NOTE_DSHARP, NOTE_EFLAT = NOTE_DSHARP, NOTE_E, NOTE_F, NOTE_FSHARP, NOTE_GFLAT = NOTE_FSHARP, NOTE_G, NOTE_GSHARP, NOTE_AFLAT = NOTE_GSHARP, NOTE_A, NOTE_ASHARP, NOTE_BFLAT = NOTE_ASHARP, NOTE_B, NOTE_NUM};
#define NOTE_NOSOUND 0#define NOTE_REST 0 #define PCPLAY_MAKEINOTE(octave, note) ((octave) * NOTE_NUM + note)#define PCPLAY_GETOCTAVE(inote) ((inote) / NOTE_NUM)#define PCPLAY_GETNOTE(inote) ((inote) % NOTE_NUM)
enum DURATION_64, DURATION_32, DURATION_16, DURATION_8, DURATION_4, DURATION_2, DURATION_1, DURATION_2X,
�������
DURATION_4X, DURATION_8X, DURATION_16X, DURATION_32X, DURATION_64X, DURATION_START = DURATION_64, DURATION_END = DURATION_64X, DURATION_0 = 0xff};
enum DURATION_POINT = 0x10, DURATION_POINT2X = 0x20, DURATION_POINT3X = 0x30,};
#define PCPLAY_GETDURATIONLENGTH(duration) ((duration) & 0x0f)#define PCPLAY_GETDURATIONPOINT(duration) ((duration) & 0xf0)#define PCPLAY_MAKEDURATION(duration, point) ((duration) | (point))
struct playnote_t { unsigned char m_note; unsigned char m_duration;};
struct playdata_t { int m_length; int m_tempo; struct playnote_t *m_pdata;};
�������
#define IOCTL_PCPLAY_SOUND _IOW(PCPLAY_MAGICNUM, 0, int)#define IOCTL_PCPLAY_SOUNDNOTE _IOW(PCPLAY_MAGICNUM, 1, int)#define IOCTL_PCPLAY_NOSOUND _IO(PCPLAY_MAGICNUM, 2)#define IOCTL_PCPLAY_PLAY _IOW(PCPLAY_MAGICNUM, 10, struct playdata_t *)#define IOCTL_PCPLAY_STOP _IO(PCPLAY_MAGICNUM, 11)#define IOCTL_PCPLAY_SETTEMPO _IOW(PCPLAY_MAGICNUM, 20, int)#define IOCTL_PCPLAY_CHANGETEMPO _IOW(PCPLAY_MAGICNUM, 21, int)#define IOCTL_PCPLAY_GETCOUNT _IOR(PCPLAY_MAGICNUM, 22, int *)
#endif
�������
! Filename : pcplay.c
/* PC Speaker Play Module*/
#include <linux/kernel.h>#include <linux/module.h>
#ifdef CONFIG_MODVERSIONS#define MODVERSIONS#include <linux/modversions.h>#endif
#include <linux/sched.h>#include <linux/slab.h>#include <linux/timer.h>#include <asm/uaccess.h>#include <asm/io.h>
#include "pcplay.h"
/* * Device Definitions */
#define DEVICE_NAME "PCPlay"
/* * type definitions
�������
*/
struct playqueue_t { struct playqueue_t *m_next; pid_t m_pid; uid_t m_uid, m_euid; gid_t m_gid, m_egid; int m_curpos; struct playdata_t *m_pdata; struct wait_queue *m_wq;};
/* * sound data definitions */
static int s_notefreq[OCTAVE_NUM][NOTE_NUM] = { { 16, 17, 18, 19, 21, 22, 23, 24, 26, 27, 29, 31 }, { 33, 35, 37, 39, 41, 44, 46, 49, 52, 55, 58, 62 }, { 65, 69, 73, 78, 82, 87, 92, 98, 104, 110, 116, 123 }, { 131, 139, 147, 155, 165, 175, 185, 196, 208, 220, 233, 245 }, { 262, 277, 294, 311, 330, 349, 370, 392, 415, 440, 466, 494 }, { 523, 554, 587, 622, 659, 698, 740, 784, 831, 880, 932, 988 }, { 1046, 1109, 1175, 1244, 1328, 1397, 1480, 1568, 1661, 1760, 1865, 1975 }, { 2093, 2217, 2349, 2489, 2637, 2794, 2960, 3136, 3322, 3520, 3729, 3951 }};
/* * function prototypes */
�������
static int device_open(struct inode *inode, struct file *filp);static int device_release(struct inode *inode, struct file *filp);static ssize_t device_read(struct file *filp, char *buffer, size_t length,
loff_t *offset);static ssize_t device_write(struct file *filp, const char *buffer, size_t length,
loff_t *offset);static int device_ioctl(struct inode *inodep, struct file *filp, unsigned int cmd,
unsigned long arg);
static void mytimer_proc(unsigned long ptr);static void add_mytimer(int clocks);static void del_mytimer(void);
static int play_curnote(void);
static int add_playdata_notes(char *pdata, int length);static int add_playdata(struct playdata_t *pdata);static void process_wait(struct file *filp);
static int enqueue_playdata(struct playdata_t *pdata);static int dequeue_playdata(void);static void dequeueall_playdata(void);static int check_delperm(void);static int settempo(int tempo);static int changetempo_playdata(int tempo);static int get_playqueue_count(void);
static void soundnote(int inote);static void sound(int freq);
�������
static void nosound(void);
/* * Device Operations */
struct file_operations device_fops = { open: device_open, release: device_release, read: device_read, write: device_write, ioctl: device_ioctl,};
/* * global variables */
static int s_nMajor = 0;static int s_bDeviceReadOpen = 0, s_bDeviceWriteOpen = 0;
static struct playqueue_t *s_queue = NULL;static int s_bInPlay = 0;static int s_nTempo = 120;
static struct timer_list s_mytimer;
/* * Module startup/cleanup */
������
int init_module(void){ printk("Loading PC Speaker Play Module\n");
if ((s_nMajor = register_chrdev(0, DEVICE_NAME, &device_fops)) < 0) { printk(DEVICE_NAME " : Device registration failed (%d)\n", s_nMajor); return s_nMajor; }
printk(DEVICE_NAME " : Device registered with Major Number = %d\n", s_nMajor);
return 0;}
void cleanup_module(void){ int nRetCode;
printk("Unloading PC Speaker Play Module\n"); nosound(); dequeueall_playdata();
if ((nRetCode = unregister_chrdev(s_nMajor, DEVICE_NAME)) < 0) printk(DEVICE_NAME " : Device unregistration failed (%d)\n", nRetCode);}
/* * Device Operations
������
*/
int device_open(struct inode *inode, struct file *filp){ printk(DEVICE_NAME " : Device open (%d, %d)\n", MAJOR(inode->i_rdev), MINOR(inode->i_rdev));
if ((filp->f_flags & O_ACCMODE) & (O_WRONLY | O_RDWR)) { if (s_bDeviceWriteOpen) { printk(DEVICE_NAME " : Device already open for writing\n"); return -EBUSY; } else ++s_bDeviceWriteOpen; } else ++s_bDeviceReadOpen;
MOD_INC_USE_COUNT;
return 0;}
int device_release(struct inode *inode, struct file *filp){ printk(DEVICE_NAME " : Device release (%d, %d)\n", MAJOR(inode->i_rdev), MINOR(inode->i_rdev));
if ((filp->f_flags & O_ACCMODE) & (O_WRONLY | O_RDWR)) --s_bDeviceWriteOpen; else --s_bDeviceReadOpen;
�������
MOD_DEC_USE_COUNT;
return 0;}
ssize_t device_read(struct file *filp, char *buffer, size_t length, loff_t *offset){ return 0;}
ssize_t device_write(struct file *filp, const char *buffer, size_t length,loff_t *offset)
{ int count = add_playdata_notes((char *) buffer, length); if (count > 0) filp->f_pos += count; process_wait(filp); return count;}
int device_ioctl(struct inode *inodep, struct file *filp, unsigned int cmd,unsigned long arg)
{ int length, retcode = 0;
printk(DEVICE_NAME " : ioctl, cmd = %x, arg = %x\n", cmd, (int) arg);
�������
switch (cmd) { case IOCTL_PCPLAY_SOUND : sound((int) arg); break; case IOCTL_PCPLAY_SOUNDNOTE : soundnote((int) arg); break; case IOCTL_PCPLAY_NOSOUND : nosound(); break;
case IOCTL_PCPLAY_PLAY : retcode = add_playdata((struct playdata_t *) arg); process_wait(filp); break; case IOCTL_PCPLAY_STOP : nosound(); dequeueall_playdata(); break;
case IOCTL_PCPLAY_SETTEMPO : settempo((int) arg); break; case IOCTL_PCPLAY_CHANGETEMPO : changetempo_playdata((int) arg);
�������
break; case IOCTL_PCPLAY_GETCOUNT : length = get_playqueue_count(); put_user(length, (int *) arg); break;
default : printk(DEVICE_NAME " : ioctl : unknown\n"); retcode = -EINVAL; }
return retcode;}
/* Timer Management*/
void mytimer_proc(unsigned long ptr){ play_curnote();}
void add_mytimer(int clock){ init_timer(&s_mytimer);
s_mytimer.function = mytimer_proc; s_mytimer.expires = jiffies + clock;
�������
add_timer(&s_mytimer);}
void del_mytimer(void){ del_timer(&s_mytimer);}
#define DURATION_FACTOR 1000
int play_curnote(void){ int duration, point, clocks; struct playnote_t *pnote;
if (s_queue) { if (s_queue->m_curpos == s_queue->m_pdata->m_length) { nosound(); dequeue_playdata(); s_bInPlay = 0; play_curnote(); } else { pnote = s_queue->m_pdata->m_pdata + s_queue->m_curpos; duration = PCPLAY_GETDURATIONLENGTH(pnote->m_duration); point = PCPLAY_GETDURATIONPOINT(pnote->m_duration);
clocks = HZ * DURATION_FACTOR; if (duration >= DURATION_4 && duration <= DURATION_END)
�������
clocks <<= (duration - DURATION_4); else if (duration >= DURATION_START && duration < DURATION_4) clocks >>= (DURATION_4 - duration); else clocks = 0;
while (point-- > 0) clocks = clocks * 3 / 2; clocks = clocks * 60 / s_queue->m_pdata->m_tempo; clocks /= DURATION_FACTOR; soundnote(pnote->m_note); add_mytimer(clocks); ++s_queue->m_curpos; s_bInPlay = 1; } return 0; } else return -ENODATA;}
/* User space / Queue*/
int add_playdata_notes(char *pdata, int length){
�������
struct playdata_t *newdata; int itemnum, buflen, retcode; itemnum = length / sizeof(struct playnote_t); buflen = sizeof(struct playdata_t) + (itemnum - 1) * sizeof(struct playnote_t); if ((newdata = kmalloc(buflen, GFP_KERNEL)) == NULL) return -ENOMEM; newdata->m_length = itemnum; newdata->m_tempo = s_nTempo; copy_from_user(newdata->m_pdata, pdata, itemnum * sizeof(struct playnote_t)); if ((retcode = enqueue_playdata(newdata)) < 0) { kfree(newdata); return retcode; } if (!s_bInPlay) play_curnote();
return length;}
int add_playdata(struct playdata_t *pdata){ int length, buflen, retcode; struct playdata_t *newdata; struct playnote_t *pnotes;
�������
get_user(length, &pdata->m_length); get_user(pnotes, &pdata->m_pdata);
buflen = sizeof(struct playdata_t) + sizeof(struct playnote_t) * length - sizeof pdata->m_pdata; if ((newdata = kmalloc(buflen, GFP_KERNEL)) == NULL) return -ENOMEM; copy_from_user(newdata, pdata, sizeof(struct playdata_t)); copy_from_user(newdata->m_pdata, pnotes, sizeof(struct playnote_t) * length); if ((retcode = enqueue_playdata(newdata)) < 0) { kfree(newdata); return retcode; } if (!s_bInPlay) play_curnote(); return 0;}
void process_wait(struct file *filp){ if ((filp->f_flags & FASYNC) == 0) interruptible_sleep_on(&s_queue->m_wq);}
/*
�������
Queue Management*/
int enqueue_playdata(struct playdata_t *pdata){ struct playqueue_t *endqueue, *newqueue; if ((newqueue = (struct playqueue_t *) kmalloc(sizeof(struct playqueue_t), GFP_KERNEL)) != NULL) { newqueue->m_next = NULL; newqueue->m_pid = current->pid; newqueue->m_uid = current->uid; newqueue->m_euid = current->euid; newqueue->m_gid = current->gid; newqueue->m_egid = current->egid; newqueue->m_curpos = 0; newqueue->m_pdata = pdata; newqueue->m_wq = NULL; if (s_queue == NULL) s_queue = newqueue; else { for (endqueue = s_queue; endqueue->m_next != NULL; endqueue = endqueue->m_next) ; endqueue->m_next = newqueue; } return 0; } else return -ENOMEM;
������
}
int dequeue_playdata(void){ int retcode; struct playqueue_t *curqueue; if ((retcode = check_delperm()) < 0) return retcode; if (s_queue) { curqueue = s_queue; s_queue = s_queue->m_next; wake_up_interruptible(&curqueue->m_wq); kfree(curqueue->m_pdata); kfree(curqueue); return 0; } else return -ENODATA;}
void dequeueall_playdata(void){ while (dequeue_playdata() == 0) ;}
int check_delperm(void)
������
{ if (s_queue) { if (current->uid == s_queue->m_uid || current->euid == s_queue->m_uid || current->euid == s_queue->m_euid) return 0; else return -EPERM; } else return -ENODATA;}
int settempo(int tempo){ s_nTempo = tempo; return 0;}
int changetempo_playdata(int tempo){ if (s_queue) { s_queue->m_pdata->m_tempo = tempo; return 0; } else return -ENODATA;}
int get_playqueue_count(void){
�������
int count; struct playqueue_t *queue; for (count = 0, queue = s_queue; queue != NULL; queue = queue->m_next) count += queue->m_pdata->m_length - queue->m_curpos; return count;}
/* PC Speaker Sound Primitive*/
void soundnote(int inote){ if (inote == NOTE_REST) nosound(); else sound(s_notefreq[PCPLAY_GETOCTAVE(inote) - 1][PCPLAY_GETNOTE(inote)]);}
void sound(int freq){ unsigned int value = inb(0x61); freq = 1193181 / freq; if ((value & 3) == 0) { outb(value | 3, 0x61); outb(0xb6, 0x43);
�������
} outb(freq & 0xff, 0x42); outb((freq >> 8) & 0xff, 0x42); printk("freq = %x\n", freq);}
void nosound(void){ unsigned int value = inb(0x61); value &= 0xfc; outb(value, 0x61);}
�������
! Filename : pcplay_test.c
/* PC Speaker Play Module Test*/
#include <stdio.h>#include <fcntl.h>
#include "pcplay.h"
/* * global data */
static struct playdata_t s_playdata;
static struct playnote_t s_notes[] = { { PCPLAY_MAKEINOTE(OCTAVE_4, NOTE_C), DURATION_4 }, { PCPLAY_MAKEINOTE(OCTAVE_4, NOTE_D), DURATION_4 }, { PCPLAY_MAKEINOTE(OCTAVE_4, NOTE_E), DURATION_4 }, { PCPLAY_MAKEINOTE(OCTAVE_4, NOTE_F), DURATION_4 }, { PCPLAY_MAKEINOTE(OCTAVE_4, NOTE_G), DURATION_4 }, { PCPLAY_MAKEINOTE(OCTAVE_4, NOTE_A), DURATION_4 }, { PCPLAY_MAKEINOTE(OCTAVE_4, NOTE_B), DURATION_4 }, { PCPLAY_MAKEINOTE(OCTAVE_5, NOTE_C), DURATION_4 },};
#define s_notesnum (sizeof s_notes / sizeof s_notes[0])
�������
/* * function prototypes */
static int pcplay_ioctl_sound(int fd, int freq);static int pcplay_ioctl_soundnote(int fd, int octave, int note);static int pcplay_ioctl_nosound(int fd);static int pcplay_ioctl_play(int fd, struct playdata_t *pdata);static int pcplay_ioctl_stop(int fd);static int pcplay_ioctl_settempo(int fd, int tempo);static int pcplay_ioctl_changetempo(int fd, int tempo);static int pcplay_ioctl_getcount(int fd);
/* * main of the program */
int main(void){ int i, fd;
if ((fd = open("/dev/pcplay", O_RDWR)) < 0) { printf("Error opening device file\n"); return 1; } printf("SOUND : octave 4, C for 1 second\n"); pcplay_ioctl_soundnote(fd, OCTAVE_4, NOTE_C); sleep(1); printf("NOSOUND : for 1 second\n");
�������
pcplay_ioctl_nosound(fd); sleep(1); s_playdata.m_length = s_notesnum; s_playdata.m_tempo = PCPLAY_DEFAULTTEMPO; printf("PLAY 1 : using IOCTL\n"); pcplay_ioctl_play(fd, &s_playdata); printf("PLAY 2 : using WRITE\n"); write(fd, s_notes, sizeof s_notes); printf("PLAY 3 : asynchronous play & stop\n"); fcntl(fd, F_SETFL, fcntl(0, F_GETFL) | FASYNC); write(fd, s_notes, sizeof s_notes); sleep(3); pcplay_ioctl_stop(fd); printf("PLAY end\n");
close(fd);
return 0;}
/* * ioctls */
int pcplay_ioctl_sound(int fd, int freq){ return ioctl(fd, IOCTL_PCPLAY_SOUND, freq);}
�������
int pcplay_ioctl_soundnote(int fd, int octave, int note){ return ioctl(fd, IOCTL_PCPLAY_SOUNDNOTE, PCPLAY_MAKEINOTE(octave, note));}
int pcplay_ioctl_nosound(int fd){ return ioctl(fd, IOCTL_PCPLAY_NOSOUND, 0);}
int pcplay_ioctl_play(int fd, struct playdata_t *pdata){ return ioctl(fd, IOCTL_PCPLAY_PLAY, pdata);}
int pcplay_ioctl_stop(int fd){ return ioctl(fd, IOCTL_PCPLAY_STOP, 0);}
int pcplay_ioctl_settempo(int fd, int tempo){ return ioctl(fd, IOCTL_PCPLAY_SETTEMPO, tempo);}
int pcplay_ioctl_changetempo(int fd, int tempo){ return ioctl(fd, IOCTL_PCPLAY_CHANGETEMPO, tempo);}
�������
int pcplay_ioctl_getcount(int fd){ int count, retcode; return ((retcode = ioctl(fd, IOCTL_PCPLAY_GETCOUNT, &count)) < 0) ? retcode : count;}
�������
DE� ���JK� # $
=������>-�a�κ¶W
�� ��ɵ�Î~�z]��¶�� ��� ��� ¶�@4Z��� ���>-�a� ���16�)�7����?���
¶� E��� ª�� 3�� '� «ì~� tail -f /var/log/messages ]� Ò×�� {|��� ��}]
>-�a�HI�ôD3¶��������3]�¿��ý16
=����� .�����..���f����� ��{|��}���]�W
�� ��?¢������¥��a�3]���� U� �:�� �w����y�z16� �:�� ����Ò×��{|
��3~��:�05B�)¬z���1¶� @ ���@�� .���Np��4¬�a����16
=���?¢����3º]���ø������]1]�ZhB�ÐÑ�16
�� ��ɵ�Y03]��ø9�?¢��§+,3]��Y0������ø��,l->16�$%��òóU� "
k�� B?¢��ø��JCa�<~�16
=���?¢����3º]��<W3]�ÃÄ��±1]�ZhB�ÐÑ�16
����"k�� B��� ���� �.����Zh��JCa�<~3@��D��kt����õ�16
=���?¢9�Y0$�?�����%�²���Ea�ªä��Å��(Ya��Æ3�W
������?�Z��Y03]���E�%��G�b"9�Y0$?�Z��Y03]���E�%��G�b
"��û�4�Å�Z���Z�]�Y0$�>?�a�no�o���y�±16�� ����Ea�i~à�
�(Ya����16
������
=���?¢Z�� sys_��-\3]�-�.�A��H�3]�°ya�Na�y�z]BW
�� ��-�.�A��H�3]�°y]�D$��'���ùDE]�Y0$?���irb"Z�z]���E
~� B��� �16� � �� D$�� ��?��� irb"Z� z]� ��E�� ùDEa� 'Di�Æ� 3
]�åì�-�.�A��H�3]�°ya�no�Na�y�±16
=���1½��ø����Z��§+,��3]��ZhB�ÐÑ3¶W
����?¢������3]�òóU�Ì«��`®3�w��%%�� Ó�Z�]��ø31�¯�B� z16��
øZ�� �°y��$0VWB�;�yv�z~���B��DE^����j�B�z��y�z16����ø
����VW��r�a�~���16