Matrix keyboard driver under Linux and Qtopia

Publisher:平和思绪Latest update time:2024-06-06 Source: elecfansKeywords:Linux Reading articles on mobile phones Scan QR code
Read articles on your mobile phone anytime, anywhere

Based on s3c2440 and linux, a 3*4 matrix keyboard driver was implemented.

Function: Delay debounce, repeat key press, multi-key press (??)

More detailed description document: "Matrix keyboard design based on S3C24440 and embedded Linux", Electronic Technology, 2008, 45(5): 21-23

/****************************************************** *********
* s3c2440-keyboard.c
*
* keyboard driver for S3C2440 based PDA
*
*
* History:2007/04/30
*
*
*************** ************************************************/

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

#define DEVICE_NAME "s3c2440-kb" //Keyboard device name

static int kbMajor = 0; //Default major device number

#define MAX_KB_BUF 10 //Circular queue size

typedef struct {
unsigned int keyStatus;
int irq;
// int timeCount;
u_short buf[MAX_KB_BUF]; /* Circular queue*/
unsigned int head, tail; /* Read and write pointers of circular queue*/
spinlock_t lock; /* Lock*/
} KB_DEV ;

static KB_DEV kbdev;

#define KEY_UP 0 //Key up
#define KEY_DOWN 1 //Key pressed
#define NO_KEY_DOWN 2 //No key pressed

#define EINT1_DOWN 0
#define EINT3_DOWN 1
#define EINT8_DOWN 2
#define NO_EINT_DOWN 3

/*Circular queue operation*/
#define BUF_HEAD (kbdev.buf[kbdev.head])
#define BUF_TAIL (kbdev.buf[kbdev.tail])
#define INCBUF(x) if((++ x)==MAX_KB_BUF) x=0

/*Timer settings*/
#define KB_TIMER_DELAY (HZ/50) /*HZ represents the number of clock ticks generated per second, and the timer timeout is 20ms*/
#define REPEAT_START_DELAY (HZ) / * Automatic repeat start delay: 1 second*/
#define REPEAT_DELAY (HZ/2) /*Automatic repeat delay: 0.5 seconds*/

static struct timer_list kb_timer;
static struct timer_list repeat_timer;
spinlock_t repeat_lock;
static int timeCount =1;
static int TIME_OUT =5;

/* keyboard matrix*/

static u_short keyboard_code_map[4][3]={
{1 , 2 , 3 },
{4 , 5 , 6 },
{7 , 8 , 9 } ,
{10, 11, 12}
}; //Keyboard code corresponding to each key
static u_short pre_keyboard_code = 0; //Key value obtained from the last scan
static u_short curr_keyboard_code = 0; //Key value obtained from the current scan
static u_short snap_keyboard_code[4][3]={
{0 , 0 , 0 },
{0 , 0 , 0 },
{0 , 0 , 0 },
{0, 0, 0}
GPJCON |= 0xFC03FFFF; GPJCON |= 0x01540000 ; }; //temporary key value
#define DETECTION_THROLD 3


static int requestIrq();
static int s3c2440_kb_release(struct inode *inode, struct file *filp);



/*----------------------------------------------------
* func: Initialize GPJCON register and configure GPJ9,GPJ10,GPJ11 ,GPJ12
* as output legs
*
------------------------------------------------------*/
static void init_gpjcon()
{

//GPJ9,GPJ10,GPJ11,GPJ12------>output
GPJCON &= 0xFC03FFFF;
GPJCON |= 0x01540000;
}

/*----------------------------------------------------
* func: Output value to GPJ9,GPJ10,GPJ11,GPJ12
* param:
* value: output value
*
------------------------------------------------------*/
//
static inline void output_giop(int value ) //Output to all lines
{
value &= 0x0000000F;
value <<= 9;
GPJDAT &= 0xE1FF;
GPJDAT |= value;
udelay(2);
}

/*----------------------------------------------------
* func: Determine whether eint is currently at a low level
* param:
* irq: the irq number of the eint that currently triggers the interrupt
* return:
* EINT1_DOWN: eint1 is at a low level
* EINT3_DOWN: eint3 is at a low level
* EINT8_DOWN: eint8 is at a low level
* NO_EINT_DOWN: eint is not at a low level
------------------------------------------------------*/
int get_eint_value(int irq)
{
u_int IOValue;

IOValue = GPFDAT ;

if( (irq == 1) && (( IOValue & 0x00000002)==0) )
{
return 0x00000008 )==0) ) { return EINT3_DOWN;
} IOValue = GPGDAT ; if((irq ==36) && (( IOValue & 0x0000001)==0) ) { return EINT8_DOWN; } return NO_EINT_DOWN; } /*---------------------------------------------------- * func: Scan the keyboard to determine which key is pressed * param: * x: get the row number of the key * y: get the column number of the key * return: * KEY_DOWN: A key is pressed on the keyboard * NO_KEY_DOWN: No key is pressed on the keyboard ------------------------------------------------------*/ static inline int scan_keyboard(int* x,int* y) { int matrix_row,matrix_col,matrix_col_status; output_giop(0xF); //Output 1 to all rows //Determine which row the key is in matrix_row=matrix_col=-1;



































output_giop(0xE);//output 1 on line 1 and 0 on the rest of the lines
matrix_col_status = get_eint_value(kbdev.irq);
if(matrix_col_status != NO_EINT_DOWN)
{
matrix_row = 0;
matrix_col = matrix_col_status;
goto scanend;

}

output_giop(0xD);//output 1 on line 2 and 0 on the rest of the lines

matrix_col_status = get_eint_value(kbdev.irq);
if(matrix_col_status != NO_EINT_DOWN)
{
matrix_row=1;
matrix_col = matrix_col_status;
goto scanend;


}

output_giop(0xB);//output 1 on line 3 and 0 on the rest of the lines
matrix_col_status = get_eint_value(kbdev.irq);
if(matrix_col_status != NO_EINT_DOWN)
{
matrix_row=2;
matrix_col = matrix_col_status;
goto scanend;


}

output_giop(0x7);//Output 1 on line 4, and output 0 on the remaining lines
matrix_col_status =get_eint_value(kbdev.irq);
if(matrix_col_status != NO_EINT_DOWN)
{
matrix_row=3;
matrix_col = matrix_col_status;
goto scanend;


}
scanend:
output_giop(0);
if(matrix_row >=0 )
{
snap_keyboard_code[matrix_row][matrix_col_status]= snap_keyboard_code[matrix_row][matrix_col_status] + 1;
if(snap_keyboard_code[matrix_row][matrix_col]>=DETECTION_THROLD)
{
*x=matrix_row;
*y=matrix_col;
curr_keyboard_code = keyboard_code_map[matrix_row][matrix_col];
return KEY_DOWN;
}


}
return NO_KEY_DOWN;

}

/*----------------------------------------------------
* func: Determine whether the current key is the same as the previous key
* param:
*
* return:
* 0: Same
* 1: Different
------------------------------------------------------*/
static inline int key_changed()
{

return (pre_keyboard_code == curr_keyboard_code)? 0 : 1;
}

/*----------------------------------------------------
* func: Save the keyboard code corresponding to the key into the circular queue
* param:
* keyValue: The corresponding keyboard code of the key

* return:
*
------------------------------------------------------*/

static inline void save_key_to_queue(u_short keyValue)
{
if (kbdev.keyStatus == KEY_DOWN)
{
BUF_HEAD = keyValue;
INCBUF(kbdev.head);
//wake_up_interruptible(&(kbdev.wq));
}

}

/*----------------------------------------------------
* func: Repeat key timer handler. If a key is pressed all the time,
the keyboard code of the key will be stored in the circular queue at a fixed time.
* param:
* data: no parameters

* return:
*
------------------------------------------------------*/
static inline void repeat_timer_handler(unsigned long data)
{
spin_lock_irq(&(repeat_lock));

if(kbdev.keyStatus ==KEY_DOWN)
{
repeat_timer.expires = jiffies + REPEAT_DELAY;//Set automatic repeat delay
add_timer(&repeat_timer);//Add timer to queue
if(pre_keyboard_code != 0)
{
//printk("repeat save keyvaluen %d",pre_keyboard_code);
save_key_to_queue(pre_keyboard_code);//Store the key value in the circular queue
}
}
else//If the key pops up
{
//del_timer(&repeat_timer);
// printk("del repeat timern");
}

spin_unlock_irq(&(repeat_lock));


}

/*----------------------------------------------------
* func: Enable interrupt
* param:
* return:
*
------------------------------------------------------*/
//Enable interrupt
static inline void enableIrq()
{
//Clear the corresponding bits of eint1 eint2 eint8 in the SRCPND register
SRCPND = 0x0000002A;
//Enable interrupt
enable_irq(IRQ_EINT1);
enable_irq(IRQ_EINT3);
enable_irq(IRQ_EINT8);

}

/*----------------------------------------------------
* func: Keyboard timing scanning program, if a stable key code is obtained, the key code will be stored
in the circular queue; if not, the scan will continue after a delay of 20ms
* param:
* data: no parameters

* return:
*
------------------------------------------------------*/
static inline void kb_timer_handler(unsigned long data)
{
int x,y;
spin_lock_irq(&(kbdev.lock));
x = y = 0;


if(scan_keyboard(&x,&y) == KEY_DOWN)
{
// printk("snap_keyboard_code=%d, %d, %d, %dn", snap_keyboard_code[0][1],snap_keyboard_code[1][1],snap_keyboard_code[2][1],snap_keyboard_code[3][1]);
kbdev.keyStatus =KEY_DOWN;
if(key_changed())
{
pre_keyboard_code = curr_keyboard_code;
save_key_to_queue(pre_keyboard_code);
//printk("KEY_DOWN:%d x=%d y =%dn",timeCount,x,y);
//Set the automatic repeat start delay timer
/*repeat_timer.expires = jiffies + REPEAT_START_DELAY;
add_timer(&repeat_timer);*/

}

timeCount=1;
memset(snap_keyboard_code,0,12*sizeof(u_short));
//curr_keyboard_code =0;

kb_timer.expires = jiffies + KB_TIMER_DELAY;
add_timer(&kb_timer);



}
else
{
//printk("snap_keyboard_code=%d, %d, %d, %dn", snap_keyboard_code[3][0],snap_keyboard_code[3][1],snap_keyboard_code[3][2],snap_keyboard_code[3][3]);
kb_timer.expires = jiffies + KB_TIMER_DELAY;
add_timer(&kb_timer);

//printk("timeCount:%dn",timeCount);

if (timeCount==TIME_OUT) //No stable key value is obtained after scanning 5 times
{
//Reset counter
timeCount=1;
kbdev.keyStatus =KEY_UP;
//Enable interrupt
enableIrq();
//Turn off timer
del_timer(&kb_timer);

del_timer(&repeat_timer);
//printk("enable irq nnn");
curr_keyboard_code = 0;
pre_keyboard_code= 0 ;
memset(snap_keyboard_code,0,12*sizeof(u_short));
}
else
timeCount++;
}

spin_unlock_irq(&(kbdev.lock));

}

/*----------------------------------------------------
* func: Read the key code of the key from the circular queue

* param:


* return: the key code of the key
*
------------------------------------------------------*/
static inline int kbRead()
{
u_short keyvalue;

spin_lock_irq(&(kbdev.lock));

keyvalue = BUF_TAIL;

INCBUF(kbdev.tail );

spin_unlock_irq(&(tsdev.lock));

return keyvalue;
}

/*----------------------------------------------------
* func: The function corresponding to file reading, if there is a key code in the circular queue,
the key code will be copied to the buffer in the user space.
* param:
*

* return:
* Returns the number of bytes of the key code read from the circular queue
*
*
------------------------------------------------------*/

static ssize_t
S3C2440_kb_read(struct file *filp, char *buffer, size_t count, loff_t * ppos)
{
u_short keyvalue;
if(kbdev.head == kbdev.tail)
{

return 0;
}
else
{

keyvalue = kbRead();
count = sizeof(keyvalue);
/*Copy data to user space*/
copy_to_user(buffer,&(keyvalue),count);
return count;
}


}

/*----------------------------------------------------
* func: The open function corresponding to opening a file, initializes global variables and timers
and requests
interrupts param:
*
*
* return:
*
------------------------------------------------------*/

static int S3C2440_kb_open(struct inode *inode, struct file *filp)
{

kbdev.keyStatus = KEY_UP;
kbdev.head=kbdev.tail = 0;
kbdev.lock = SPIN_LOCK_UNLOCKED;
repeat_lock = SPIN_LOCK_UNLOCKED;

output_giop(0);

//Initialize timer
init_timer(&kb_timer);
kb_timer.function = kb_timer_handler;

//Initialize repeat key timer
init_timer(&repeat_timer);
repeat_timer.function = repeat_timer_handler;


/*if(requestIrq() != 0)
return -1;*/
enableIrq();

MOD_INC_USE_COUNT;

return 0;
}



static struct file_operations kb_fops = {
owner: THIS_MODULE,
open: S3C2440_kb_open,
read: S3C2440_kb_read,
release: s3c2440_kb_release,
};

/*----------------------------------------------------
* func: interrupt handler, turn off interrupt and turn on keyboard scan timer
* param:
*
*
* return:
*
------------------------------------------------------*/
static void keyboard_interrupt(int irq, void *dev_id, struct pt_regs *regs)

{

spin_lock_irq(&kbdev.lock);

//Disable all interrupts
disable_irq(IRQ_EINT1);
disable_irq(IRQ_EINT3);
disable_irq(IRQ_EINT8);

kbdev.irq = irq;
//printk("irq=%dn",kbdev.irq);

//Start timer
kb_timer.expires = jiffies + KB_TIMER_DELAY;
add_timer(&kb_timer);

repeat_timer.expires = jiffies + REPEAT_START_DELAY;
add_timer(&repeat_timer);



spin_unlock_irq(&kbdev.lock);
}
/*----------------------------------------------------
* func: Initialize eint interrupt related registers and install interrupt handler
* param:
*
*
* return:
*
------------------------------------------------------*/
static int requestIrq()
{
int ret;
/* Enable interrupt */
//=====================================================
// irq: Linux interrupt number, different from hardware interrupt number
// handle: interrupt handler
// flag: SA_INTERRUPT indicates that this is a fast interrupt handler
// dev_id: interrupt signal line used for sharing, usually set to NULL
//====================================================

ret = set_external_irq(IRQ_EINT1,EXT_FALLING_EDGE,GPIO_PULLUP_DIS);
if(ret)
goto eint_failed;

ret = request_irq(IRQ_EINT1, keyboard_interrupt, SA_INTERRUPT,
DEVICE_NAME, NULL);

if(ret)
goto eint1_failed;
ret = set_external_irq(IRQ_EINT8,EXT_FALLING_EDGE,GPIO_PULLUP_DIS); //EXT_LOWLEVEL
if(ret)
goto eint_failed;

ret = request_irq(IRQ _EINT8, keyboard_interrupt , SA_INTERRUPT,
DEVICE_NAME, NULL); if(ret) goto eint8_failed ; ret = set_external_irq(IRQ_EINT3,EXT_FALLING_EDGE,GPIO_PULLUP_DIS); if(ret
) goto eint_failed; ret = request_irq(IRQ_EINT3, keyboard_interrupt, SA_INTERRUPT, DEVICE_NAME, NULL ); if(ret) goto eint3_failed; return 0; eint3_failed: free_irq(IRQ_EINT3, keyboard_interrupt); eint8_failed: free_irq(IRQ_EINT8, keyboard_interrupt); eint1_failed: free_irq(IRQ_EINT1, keyboard_interrupt); eint_failed: printk(DEVICE_NAME ": IRQ Requeset Error n"); return ret; } static int s3c2440_kb_release(struct inode *inode, struct file *filp) { /*Unregister device*/ // unregister_chrdev(kbMajor, DEVICE_NAME); /*Release interrupt*/ /* free_irq(IRQ_EINT1,NULL); free_irq (IRQ_EINT8,NULL); free_irq(IRQ_EINT3,NULL); MOD_DEC_USE_COUNT; */ return 0; } /*---------------------------------------- ------------ * func: Initialize keyboard driver, register character device * param: * * * return: >=0: Initialize keyboard driver successfully <0: Failed * ------- -----------------------------------------------*/ static int __init s3c2440_kb_init(void) { int ret; /*Initialize the configuration of the tube leg*/ init_gpjcon(); output_giop(0); /*Register the device*/ ret = register_chrdev(99, DEVICE_NAME, &kb_fops); if(ret < 0) { printk(DEVICE_NAME " can't get major numbern"); return ret; } kbMajor = ret; printk("%s: major number=99n",DEVICE_NAME); requestIrq(); // Temporarily disable all interrupts and wait until open Then turn on disable_irq(IRQ_EINT1); disable_irq(IRQ_EINT3); disable_irq(IRQ_EINT8); return 0; } /*---------------------------------- ------------------ * func: Unregister character device, release interrupt * param: * * * return:
































































































*
------------------------------------------------------*/
static void __exit s3c2440_kb_exit(void)
{

/*Deregister device*/
unregister_chrdev(kbMajor, DEVICE_NAME);
printk("exitn");

/*Release interrupt*/
free_irq(IRQ_EINT1,NULL);

free_irq(IRQ_EINT8,NULL);

free_irq(IRQ_EINT3,NULL);
}

module_init(s3c2440_kb_init);
module_exit(s3c2440_kb_exit);

//EXPORT_SYMBOL(s3c2440_kb_init) ;
//EXPORT_SYMBOL(s3c2440_kb_exit);



If you combine this driver with the qtopia program, you need to modify the qkeyboard_qws.cpp file of the qt source code to add support for the matrix keyboard

class QWSHPCButtonsHandler : public QWSKeyboardHandler
{
Q_OBJECT
public:
QWSHPCButtonsHandler();
virtual ~QWSHPCButtonsHandler();

bool isOpen() { return buttonFD > 0; }

private slots:
void readKeyboardData();

private:
QString terminalName;
int buttonFD;
struct termios newT, oldT;
QSocketNotifier *notifier;
};



QWSHPCButtonsHandler::QWSHPCButtons Handler() : QWSKeyboardHandler()
{
#ifdef QT_QWS_HPC
terminalName = "/dev/keyboard";
buttonFD = -1;
notifier = 0;

if ((buttonFD = open(terminalName, O_RDONLY | O_NDELAY, 0)) < 0) {
qWarning("Cannot open %sn", terminalName.latin1());
return;
}

notifier = new QSocketNotifier( buttonFD , QSocketNotifier::Read, this );
connect( notifier, SIGNAL(activated(int)),this,
SLOT(readKeyboardData()) );

#endif
}

QWSHPCButtonsHandler::~QWSHPCButtonsHandler()
{
#ifdef QT_QWS_HPC
if ( buttonFD > 0 ) {
::close( buttonFD );
buttonFD = -1;
}
#endif
}

void QWSHPCButtonsHan dler::readKeyboardData()
{
#ifdef QT_QWS_HPC
//-----------port form ButtonDetect-begin-----------
int tempint,i;
unsigned short buffer[1];
tempint=0;
int current_press=-1;

do{
tempint=read(buttonFD,buffer,1);
if(tempint > 0 )
{
// printf("nCurrent Press Buttom %d n",buffer[0]);
current_ press = (int)buffer[1];
goto set_hpckey;
}

}while(tempint >0);



//-----------port form ButtonDetect-end-------------

set_hpckey:

int k=(-1);
switch(current_press) {
case 3: k=Qt::Key_Up; break; //
case 11: k=Qt::Key_Down; break; //
case 8: k=Qt::Key_Left; break; //
case 6: k=Qt::Key_Right; break; //
case 7: k=Qt::Key_Enter; break; // Enter
case 4: k=Qt::Key_Escape; break; // Enter
default: k=(-1); break;
}

if ( k >= 0 )
{
qwsServer->processKeyEvent( 0, k, 0, 1, false );
}

#endif
}

[1] [2]
Keywords:Linux Reference address:Matrix keyboard driver under Linux and Qtopia

Previous article:S3C2440 timer configuration
Next article:Compilation and configuration of u-boot-2011.06 based on s3c2440 development board

Recommended ReadingLatest update time:2024-11-16 09:46

Linux system installation under SmartArm3250
I am currently researching the Linux driver for SmartArm3250, so first I have to install (also known as download) the Linux system on the development board. I followed the steps in the book and encountered some minor problems. After many experiments and summaries, I can now install the Linux system skillfully. Here is
[Microcontroller]
Detailed steps to install arm-linux-gcc on Ubuntu
gcc compiled can only be used in Linux. To use it in ARM, you also need to use a cross-compilation tool: arm-linux-gcc 1. Get the compressed package First try to install it using the command line, the result is as follows: However, this method requires climbing over a firewall to obtain it (I heard from others...)
[Microcontroller]
Detailed steps to install arm-linux-gcc on Ubuntu
Linux ARM (IMX6U) bare metal assembly LED driver experiment-burn bin file to SD card to run
Code burning Although I.MX6U has 96K ROM inside, this 96K ROM is used by NXP itself and is not open to users. So it is equivalent to saying that I.MX6U has no internal flash, but our code must be stored somewhere. For this reason, I.MX6U supports booting from external storage media such as NOR Flash, NAND Flash, SD/EM
[Microcontroller]
Linux ARM (IMX6U) bare metal assembly LED driver experiment-burn bin file to SD card to run
The battle for the smart car entrance of in-vehicle OS
Automotive OS (Operating System) is also known as automotive operating system. In the future, cars will evolve into mobile smart terminals, and software will define cars. OS is the key to traditional car companies' digital transformation and has become a strategic location that all companies must compete for. In the a
[Automotive Electronics]
The battle for the smart car entrance of in-vehicle OS
ARM-LINUX-GCC simple universal makefile
#Created by JamieChu 2019-06-19 ; #Almost fully automatic, just change the value of TARGET_BIN, then throw it into the project directory and run the make command; #You can also modify VPATH to search other directories; #This makefile will automatically generate dependencies and automatically determine whether the dep
[Microcontroller]
Configure arm-linux virtual machine development environment
Basic tool configuration The system is Ubuntu 12.01, which feels quite easy to use and stable. After entering the system, if a user is created during installation, root has no password:   #sudo passwd root Configure the root password. Then change VI, because the vim used by the Ubuntu series is the common version,
[Microcontroller]
Configure arm-linux virtual machine development environment
2440 serial port linux programming, s3c2440 serial port control
After configuring the system clock of the s3c2440, we control the serial port. The reason why the serial port is placed so high is because the serial port will bring us more information. When there is no serial port, the uboot startup phase can only rely on LED To display some information. Then with the serial port, d
[Microcontroller]
2440 serial port linux programming, s3c2440 serial port control
Implementation of Serial Peripheral Interface SPI Based on S3C2410 and Embedded Driver under Linux
    Serial peripheral interface SPI (serial peripheral interface) bus technology is a synchronous serial interface introduced by Motorola. It allows the CPU to communicate with peripheral interface devices such as TTL shift registers, A/D or D/A converters, real-time clocks (RTO), memories, and LCD and LED display dri
[Microcontroller]
Implementation of Serial Peripheral Interface SPI Based on S3C2410 and Embedded Driver under Linux
Latest Microcontroller Articles
  • Download from the Internet--ARM Getting Started Notes
    A brief introduction: From today on, the ARM notebook of the rookie is open, and it can be regarded as a place to store these notes. Why publish it? Maybe you are interested in it. In fact, the reason for these notes is ...
  • Learn ARM development(22)
    Turning off and on interrupts Interrupts are an efficient dialogue mechanism, but sometimes you don't want to interrupt the program while it is running. For example, when you are printing something, the program suddenly interrupts and another ...
  • Learn ARM development(21)
    First, declare the task pointer, because it will be used later. Task pointer volatile TASK_TCB* volatile g_pCurrentTask = NULL;volatile TASK_TCB* vol ...
  • Learn ARM development(20)
    With the previous Tick interrupt, the basic task switching conditions are ready. However, this "easterly" is also difficult to understand. Only through continuous practice can we understand it. ...
  • Learn ARM development(19)
    After many days of hard work, I finally got the interrupt working. But in order to allow RTOS to use timer interrupts, what kind of interrupts can be implemented in S3C44B0? There are two methods in S3C44B0. ...
  • Learn ARM development(14)
  • Learn ARM development(15)
  • Learn ARM development(16)
  • Learn ARM development(17)
Change More Related Popular Components

EEWorld
subscription
account

EEWorld
service
account

Automotive
development
circle

About Us Customer Service Contact Information Datasheet Sitemap LatestNews


Room 1530, 15th Floor, Building B, No.18 Zhongguancun Street, Haidian District, Beijing, Postal Code: 100190 China Telephone: 008610 8235 0740

Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号