yPQ{tS*t /*
\pzvoj7{ * Driver for the PN544 NFC chip.
\3LD^[qi *
>8JvnBFx= * Copyright (C) Nokia Corporation
epG;=\f}m` *
aSF&^/j * Author: Jari Vanhala <
ext-jari.vanhala@nokia.com>
=~0XdS/1 * Contact: Matti Aaltonen <
matti.j.aaltonen@nokia.com>
I^ >zr.zA *
|Q I3H]T7 * This program is free software; you can redistribute it and/or
hPk+vvXtK * modify it under the terms of the GNU General Public License
I9Sh~vTm=u * version 2 as published by the Free Software Foundation.
{VNeh *
RsOK5XnQn * This program is distributed in the hope that it will be useful,
wlpbfO e/ * but WITHOUT ANY WARRANTY; without even the implied warranty of
C,<TAm * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
>{??/fBd- * GNU General Public License for more details.
<Ihn1? *
o>*{5>#k' * You should have received a copy of the GNU General Public License
'PYl%2 * along with this program; if not, write to the Free Software
_x'StD * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
)xK!i. */
z::2O/ho 4dok/ +Ec #include <linux/completion.h>
.XE]vo #include <linux/crc-ccitt.h>
@'rO=(-b #include <linux/delay.h>
'9[_w$~( #include <linux/interrupt.h>
(\I =v". #include <linux/kernel.h>
y@Or2bO# #include <linux/miscdevice.h>
5 O6MI4: #include <linux/module.h>
LtU+w*Gj #include <linux/mutex.h>
lxz %bC@ #include <linux/nfc/pn544.h>
[T^6Kzz #include <linux/poll.h>
lz*2wGI9 #include <linux/regulator/consumer.h>
A+l" #include <linux/serial_core.h> /* for TCGETS */
o{hKt? #include <linux/slab.h>
37[C^R!1c a;zcAeX #define DRIVER_CARD "PN544 NFC"
V&nN/CF #define DRIVER_DESC "NFC driver for PN544"
>@%!r ;'Q{ ywr static struct i2c_device_id pn544_id_table[] = {
GkC88l9z { PN544_DRIVER_NAME, 0 },
<>:kAT,sP { }
@gj5' };
cs5Xd MODULE_DEVICE_TABLE(i2c, pn544_id_table);
hf-S6PEsM /PCQv_Y&,/ #define HCI_MODE 0
[y:LA~q #define FW_MODE 1
{h=Ai[|l4Q p[eRK .$! enum pn544_state {
"osYw\unI PN544_ST_COLD,
=3:ltI.'*I PN544_ST_FW_READY,
H:_`]X" PN544_ST_READY,
8B+uNN~%] };
EGt)tI& 72 6y/o enum pn544_irq {
;4bu=<% PN544_NONE,
#? *jdN: PN544_INT,
;:4puv+] };
>xjy
P!bca 6uyf struct pn544_info {
U&XoT-p$L struct miscdevice miscdev;
KOQTvJ_# struct i2c_client *i2c_dev;
S@#L!sT`u struct regulator_bulk_data regs[3];
|(<L!6 e'Pa@]VaC enum pn544_state state;
i&$uG[&P wait_queue_head_t read_wait;
7,zARWB!? loff_t read_offset;
5ZVTI,4K enum pn544_irq read_irq;
kYAvzuGRb struct mutex read_mutex; /* Serialize read_irq access */
rBf?kDt6l struct mutex mutex; /* Serialize info struct access */
#L)rz u u8 *buf;
Z7^}G=* size_t buflen;
1#(1Bs6X };
?zEF?LJoK SXEiyy[7v static const char reg_vdd_io[] = "Vdd_IO";
EHZSM5hu static const char reg_vbat[] = "VBat";
%8Z,t+' static const char reg_vsim[] = "VSim";
/HRaX!|E# qAS^5|(b[ /* sysfs interface */
1N+#(<x@, static ssize_t pn544_test(struct device *dev,
m
C Ge*V} struct device_attribute *attr, char *buf)
Nz;;X\GI {
YYHm0pc struct pn544_info *info = dev_get_drvdata(dev);
Jy_'(hG struct i2c_client *client = info->i2c_dev;
hbeC|_+ struct pn544_nfc_platform_data *pdata = client->dev.platform_data;
* 5n:+Tw( ^U}0D^jDeE return snprintf(buf, PAGE_SIZE, "%d\n", pdata->test());
pQNFH)=nw }
U\ued=H zTLn*? static int pn544_enable(struct pn544_info *info, int mode)
+$t%L {
F0Hbklr struct pn544_nfc_platform_data *pdata;
~|rkt`8p struct i2c_client *client = info->i2c_dev;
7;NV
1RV j,XKu5w)Oi int r;
%jkPrI e`r;`a& r = regulator_bulk_enable(ARRAY_SIZE(info->regs), info->regs);
,X^_w
g if (r < 0)
TpgBS4q return r;
37xxVbik =f!M=D pdata = client->dev.platform_data;
p/h&_^EXU info->read_irq = PN544_NONE;
i7V~LO:gq if (pdata->enable)
0K ?(xB pdata->enable(mode);
B! V{.p cqx1NWlY if (mode) {
1 1CJT info->state = PN544_ST_FW_READY;
$8k_M dev_dbg(&client->dev, "now in FW-mode\n");
T;@>O^ } else {
Wi^rnr'Ss info->state = PN544_ST_READY;
s~
A8/YoU} dev_dbg(&client->dev, "now in HCI-mode\n");
}eM<A$J }
N_D+d4@ p'~5[JR: usleep_range(10000, 15000);
=joXP$n^ K"7;Y#1g return 0;
G4' U; }
t[hocl/6 CDnz
&? static void pn544_disable(struct pn544_info *info)
1 ],,
Ar5 {
tr8Cx~< struct pn544_nfc_platform_data *pdata;
~aAJn IO struct i2c_client *client = info->i2c_dev;
CqK#O'\ |SKG4_wGe pdata = client->dev.platform_data;
AijTT% if (pdata->disable)
Aq%^>YAp pdata->disable();
bpa
O`[* xc.D!Iav info->state = PN544_ST_COLD;
ROS"VV< -WvgK"k dev_dbg(&client->dev, "Now in OFF-mode\n");
Y>Hl0$:= f\|?_k] msleep(PN544_RESETVEN_TIME);
FK# E7
K m\M+pjz info->read_irq = PN544_NONE;
FLI8r: regulator_bulk_disable(ARRAY_SIZE(info->regs), info->regs);
PMhhPw] }
KTjlWxD D"4&9"C U static int check_crc(u8 *buf, int buflen)
2BX GVo {
bDBO+qA u8 len;
W#I:j: p u16 crc;
-MU.Hu 6F.7Ws< len = buf[0] + 1;
X|E+K if (len < 4 || len != buflen || len > PN544_MSG_MAX_SIZE) {
cO+Xzd;838 pr_err(PN544_DRIVER_NAME
U
qw}4C/0 ": CRC; corrupt packet len %u (%d)\n", len, buflen);
dyiEK)$h print_hex_dump(KERN_DEBUG, "crc: ", DUMP_PREFIX_NONE,
s%[GQQ-N 16, 2, buf, buflen, false);
exO#>th1 return -EPERM;
r>~d[,^$m4 }
jS3(> crc = crc_ccitt(0xffff, buf, len - 2);
s^YTI\L
\ crc = ~crc;
_T|H69 J 4bev*[k if (buf[len-2] != (crc & 0xff) || buf[len-1] != (crc >> 8)) {
E? eWv)// pr_err(PN544_DRIVER_NAME ": CRC error 0x%x != 0x%x 0x%x\n",
C]yQ "b crc, buf[len-1], buf[len-2]);
76A>^Bs\/ :q64K?X print_hex_dump(KERN_DEBUG, "crc: ", DUMP_PREFIX_NONE,
7k=F6k0) 16, 2, buf, buflen, false);
Ldhk^/+ return -EPERM;
2FIR]@MQd }
E<Dh_K return 0;
'nWs0iH. }
'K`Rbhy 51G=RYay9 static int pn544_i2c_write(struct i2c_client *client, u8 *buf, int len)
fA_%8CjI {
gZl w int r;
N^
s!!Sbpq 0vuKGjK if (len < 4 || len != (buf[0] + 1)) {
g:DTVq dev_err(&client->dev, "%s: Illegal message length: %d\n",
,{{#a*nd __func__, len);
mvq7G return -EINVAL;
7ec0Xh1 }
AwXt @!( 6L,lq; if (check_crc(buf, len))
9Ue7
~"= return -EINVAL;
a WeBav}_ S6T!qH{6 usleep_range(3000, 6000);
qfGtUkSSb 2Za,4' r = i2c_master_send(client, buf, len);
^0~c7`k`V dev_dbg(&client->dev, "send: %d\n", r);
>bA$SN B3dA%\' if (r == -EREMOTEIO) { /* Retry, chip was in standby */
o)#q9Vk%b usleep_range(6000, 10000);
& &" 'dL r = i2c_master_send(client, buf, len);
q11QAx4p dev_dbg(&client->dev, "send2: %d\n", r);
Q^lQi\[ }
cK[R1 ReH Wm)-zvNY; if (r != len)
m0|Ae@g~3 return -EREMOTEIO;
,2,SG/BB *Mwfod return r;
)WVItqQKV }
\5Vp6^ BbrT f"` static int pn544_i2c_read(struct i2c_client *client, u8 *buf, int buflen)
fW.GNX8 {
2y+70(E1 int r;
+ze}0lrEL u8 len;
=a)iVXSB] (_w
% /*
{_zV5V * You could read a packet in one go, but then you'd need to read
=[P%_v`` * max size and rest would be 0xff fill, so we do split reads.
Kc%n(,+%" */
/M^V2= r = i2c_master_recv(client, &len, 1);
,!6M*| dev_dbg(&client->dev, "recv1: %d\n", r);
_%wK}eH+sy .!JMPf"QEI if (r != 1)
#\pP2
return -EREMOTEIO;
d7"U WY^ xH<'GB) if (len < PN544_LLC_HCI_OVERHEAD)
wJ+U[a len = PN544_LLC_HCI_OVERHEAD;
vpm ]9>1[ else if (len > (PN544_MSG_MAX_SIZE - 1))
dD/t_ {h len = PN544_MSG_MAX_SIZE - 1;
uxa=KM1H g7xbyBo7 if (1 + len > buflen) /* len+(data+crc16) */
+_qh)HX return -EMSGSIZE;
A\".t=+7
(R_CUH buf[0] = len;
3 ppuQQ :E>&s9Yj? r = i2c_master_recv(client, buf + 1, len);
~ HK1X dev_dbg(&client->dev, "recv2: %d\n", r);
of8mwnZR I*D<J$ 9N if (r != len)
XzT78 return -EREMOTEIO;
/M3y)K`^ umQi usleep_range(3000, 6000);
>P<z |8 [ULwzjss#L return r + 1;
&/s~? Iq }
pC*BA<?Rg 9i0M/vx static int pn544_fw_write(struct i2c_client *client, u8 *buf, int len)
9>N\sOh {
[ njx7d int r;
br0u@G =G(*gx dev_dbg(&client->dev, "%s\n", __func__);
4|nQ=bIau }0QN[$H! if (len < PN544_FW_HEADER_SIZE ||
_yj1:TtCNT (PN544_FW_HEADER_SIZE + (buf[1] << 8) + buf[2]) != len)
FZiZg; return -EINVAL;
^:qD .h>& bH*@,EE r = i2c_master_send(client, buf, len);
XVw-G
}5 dev_dbg(&client->dev, "fw send: %d\n", r);
5 U%MoH I> {!U$ if (r == -EREMOTEIO) { /* Retry, chip was in standby */
Sv7 i! j usleep_range(6000, 10000);
"YJ[$TG r = i2c_master_send(client, buf, len);
s=MT, dev_dbg(&client->dev, "fw send2: %d\n", r);
vQUZVq5M }
%N)e91wC t)9]<pN% if (r != len)
cTM$ZNin return -EREMOTEIO;
=HVfJ"vK 2B-.}OJ return r;
*B1x`=
}
V1<ow'^i ejVdxVr \7 static int pn544_fw_read(struct i2c_client *client, u8 *buf, int buflen)
{>@QJlE0 {
%y>+1hakkX int r, len;
@g4Shlx| :cy>c2 if (buflen < PN544_FW_HEADER_SIZE)
|xh&p( return -EINVAL;
:G -1YA D9.`hs0 r = i2c_master_recv(client, buf, PN544_FW_HEADER_SIZE);
f')c/Yw dev_dbg(&client->dev, "FW recv1: %d\n", r);
Q"%QQo}} `mzb(bE if (r < 0)
4qt+uNe! return r;
9RQU? U/Wrh($ #4 if (r < PN544_FW_HEADER_SIZE)
eIg+PuQD] return -EINVAL;
D*\v0=P'? bDDqaO ,8 len = (buf[1] << 8) + buf[2];
Q"%S~' if (len == 0) /* just header, no additional data */
jv =EheD return r;
y;nvR6) Yt+h2ft! if (len > buflen - PN544_FW_HEADER_SIZE)
+3;Ody"59 return -EMSGSIZE;
EUy(T1Cl&& |jT2W
r = i2c_master_recv(client, buf + PN544_FW_HEADER_SIZE, len);
_1
pDA dev_dbg(&client->dev, "fw recv2: %d\n", r);
l&L,7BX w9f
_b3 if (r != len)
O2.'- return -EINVAL;
3pSj kS|?> ]]TqP{H return r + PN544_FW_HEADER_SIZE;
*YtB )6j }
;L~p|sF URA0ey` static irqreturn_t pn544_irq_thread_fn(int irq, void *dev_id)
U]hF
{
zBY~lNB struct pn544_info *info = dev_id;
6YmP[% struct i2c_client *client = info->i2c_dev;
)JhT1j Qc 4(=kE>n} BUG_ON(!info);
5qbq,#Pf BUG_ON(irq != info->i2c_dev->irq);
;~+]! U *0y{ ~@ dev_dbg(&client->dev, "IRQ\n");
Kb&V!#o) <sX VW mutex_lock(&info->read_mutex);
j13DJ.xu info->read_irq = PN544_INT;
!`&\Lx_ mutex_unlock(&info->read_mutex);
\\:|Odd +-BwQ{92[: wake_up_interruptible(&info->read_wait);
R,t$"bOd =gjDCx$| return IRQ_HANDLED;
:et#0! }
$wV1*$1NM nPFwPk8=M static enum pn544_irq pn544_irq_state(struct pn544_info *info)
PD6_)PXn {
7 [d? enum pn544_irq irq;
*fnvZw? rrqQCn9 mutex_lock(&info->read_mutex);
;3"@g]e irq = info->read_irq;
sw;|'N$:< mutex_unlock(&info->read_mutex);
WO X}Sw" /*
m#e*c[*G * XXX: should we check GPIO-line status directly?
<
Ek/8x * return pdata->irq_status() ? PN544_INT : PN544_NONE;
W:`#% :C */
tfYB _N ; s|w{.<: return irq;
PFrfd_s{>\ }
'yY>as ""% A'TZ static ssize_t pn544_read(struct file *file, char __user *buf,
8'#/LA[uPe size_t count, loff_t *offset)
C <B<o[:H {
T2FE+ A]n9 struct pn544_info *info = container_of(file->private_data,
$t}<85YCQ struct pn544_info, miscdev);
pOT7;-#n struct i2c_client *client = info->i2c_dev;
hyg8wI enum pn544_irq irq;
=*Z5!W'd size_t len;
2`?!+") int r = 0;
y_p.Gzy(^} -e*ZCwQ dev_dbg(&client->dev, "%s: info: %p, count: %zu\n", __func__,
Hfym30 info, count);
z>m=h)9d~
4x;_AN mutex_lock(&info->mutex);
|[$~\MU SaOYu &> if (info->state == PN544_ST_COLD) {
:A1: r = -ENODEV;
?D`T7KSe~D goto out;
U_B((Z(g }
rl08R
2]cRXJ7h irq = pn544_irq_state(info);
_S}A=hK' if (irq == PN544_NONE) {
4_/?:$KO if (file->f_flags & O_NONBLOCK) {
/Ncm^b4 r = -EAGAIN;
c; 2#,m^ goto out;
P{Lf5V9# < }
Ztr Cv? tDg}Ys=4K> if (wait_event_interruptible(info->read_wait,
u #w29Pm (info->read_irq == PN544_INT))) {
d5`3wd]]'v r = -ERESTARTSYS;
V)(R]BK{ goto out;
^T::-pN* }
<h-vjz }
#_93f
| **3 z;58i if (info->state == PN544_ST_FW_READY) {
QX4ai3v len = min(count, info->buflen);
6( CDNMzj 1KM`i mutex_lock(&info->read_mutex);
Uu 8,@W+ r = pn544_fw_read(info->i2c_dev, info->buf, len);
`-h8vj5uG info->read_irq = PN544_NONE;
hrG M|_BE mutex_unlock(&info->read_mutex);
phnV7D(E .mLK`c6 if (r < 0) {
#X 52/8G dev_err(&info->i2c_dev->dev, "FW read failed: %d\n", r);
a`[uNgDO goto out;
%w7u]-tR }
?']5dD {!t7[Ctb print_hex_dump(KERN_DEBUG, "FW read: ", DUMP_PREFIX_NONE,
x^4xq#Bb7 16, 2, info->buf, r, false);
9RN-suE[ Od4E x;F *offset += r;
?T9(Vw if (copy_to_user(buf, info->buf, r)) {
VXIP0p@ r = -EFAULT;
CHrFM@CM goto out;
?=m?jNa;nC }
m< _S_c } else {
ojyIQk+ len = min(count, info->buflen);
{M-YHX>*;g ?qCK7$j mutex_lock(&info->read_mutex);
;r_F[E2z r = pn544_i2c_read(info->i2c_dev, info->buf, len);
.ZvM ^GJb info->read_irq = PN544_NONE;
S4=~`$eP mutex_unlock(&info->read_mutex);
-gSUjP C{gyj}5 if (r < 0) {
I!e} )Y dev_err(&info->i2c_dev->dev, "read failed (%d)\n", r);
91=OF*w goto out;
N(I& }
^6_e=jIN
print_hex_dump(KERN_DEBUG, "read: ", DUMP_PREFIX_NONE,
QFYWA1<pDh 16, 2, info->buf, r, false);
}:X*7 n(& J5^'HU3 *offset += r;
Ut2y;2)a if (copy_to_user(buf, info->buf, r)) {
q#<^ ^4U r = -EFAULT;
o.0ci+z@ goto out;
.C 8PitS }
)+:EJH~ }
tjupJ*Rt uc;8 K,[t out:
UK<Nj<-'t mutex_unlock(&info->mutex);
"jG}B.l=, bbrXgQ`s+w return r;
-$\+'
\ }
NR`C(^} o4|M0 static unsigned int pn544_poll(struct file *file, poll_table *wait)
G1 vNt7 {
{phNds% struct pn544_info *info = container_of(file->private_data,
28 ?\ struct pn544_info, miscdev);
bD/~eIcWL struct i2c_client *client = info->i2c_dev;
dBz/7&Q int r = 0;
O8h%3& xai*CY@cQ dev_dbg(&client->dev, "%s: info: %p\n", __func__, info);
a(l29> Vh_P/C+ mutex_lock(&info->mutex);
z6*X%6,8 r"P|dlV- if (info->state == PN544_ST_COLD) {
Wk)OkIFR r = -ENODEV;
,yiX# ;j goto out;
$<}$DH_Y }
\WxukYH vEJWFoeEFm poll_wait(file, &info->read_wait, wait);
ZrsBm_Rx a{L
d if (pn544_irq_state(info) == PN544_INT) {
I}1NB3>^ r = POLLIN | POLLRDNORM;
#qK:J;Sn3 goto out;
G3Z)Z)N }
k?+?v?I
= out:
<g"{Wv: h mutex_unlock(&info->mutex);
e )d`pQ6 sS*3=Yh return r;
#d6)#:uss }
PGqQ@6B aDU<wxnSvO static ssize_t pn544_write(struct file *file, const char __user *buf,
=vX/{C size_t count, loff_t *ppos)
~"nxE {
4y|BOVl struct pn544_info *info = container_of(file->private_data,
A1O'|7X struct pn544_info, miscdev);
YtmrRDQs struct i2c_client *client = info->i2c_dev;
3}}38A|4 ssize_t len;
t'n pG}`tE int r;
nLXlU*ES LRL,m_gt dev_dbg(&client->dev, "%s: info: %p, count %zu\n", __func__,
hgPa6Kd info, count);
;ub;lh 3 HiZ*+T.B mutex_lock(&info->mutex);
h`^jyoF"( b,7k)ND1F if (info->state == PN544_ST_COLD) {
b3=rG(0f r = -ENODEV;
F3On?x) goto out;
l9{hq/V }
-|$@-fY; v[1aWv: /*
"~sW"n(F_ * XXX: should we detect rset-writes and clean possible
KcWN,!G * read_irq state
Va"0>KX */
d;boIP`M; if (info->state == PN544_ST_FW_READY) {
TM%|'^) size_t fw_len;
"\:`/k3 =$'6(aDH if (count < PN544_FW_HEADER_SIZE) {
>mwlsL~X r = -EINVAL;
0"<H;7K#W goto out;
&."iFe }
P3x8UR=fS _kef0K6 len = min(count, info->buflen);
oH97=> if (copy_from_user(info->buf, buf, len)) {
3lrT3a3vV r = -EFAULT;
Ag-(5: goto out;
$*^7iT4q_t }
f\|w' o_izl\ print_hex_dump(KERN_DEBUG, "FW write: ", DUMP_PREFIX_NONE,
03$mYS_? 16, 2, info->buf, len, false);
)1?y 8_B ?+))}J5N\ fw_len = PN544_FW_HEADER_SIZE + (info->buf[1] << 8) +
ZF!h<h&, info->buf[2];
0"jY.*_EW ^9v4O UG if (len > fw_len) /* 1 msg at a time */
$0W|26; len = fw_len;
u|\1hLXX g|o,uD r = pn544_fw_write(info->i2c_dev, info->buf, len);
yb<fpM } else {
Vr3Zu{&2 if (count < PN544_LLC_MIN_SIZE) {
p*XANGA r = -EINVAL;
(p" %O goto out;
\"7*{L: }
=Qy<GeY j`{?OYD len = min(count, info->buflen);
Hus)c3Ty7 if (copy_from_user(info->buf, buf, len)) {
T^zXt? r = -EFAULT;
=*oJEy" goto out;
/:cd\A} }
A#e%^{q$ wW Lj?;bx print_hex_dump(KERN_DEBUG, "write: ", DUMP_PREFIX_NONE,
hZm"t/aKc 16, 2, info->buf, len, false);
yl'u'-Zb6 5?f ^Rz if (len > (info->buf[0] + 1)) /* 1 msg at a time */
M[NV)q/) len = info->buf[0] + 1;
)*u8/U 1.}d.t
r = pn544_i2c_write(info->i2c_dev, info->buf, len);
{a =#B)6 }
mVj9 ,q0 out:
KYB`D.O mutex_unlock(&info->mutex);
'+@=ILj> wo3d#= return r;
D(~U6SR 4S7v:1~xe }
p/ ,=OaVU x`mG<Yt static long pn544_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
'6DBs8>1 {
6,pnw struct pn544_info *info = container_of(file->private_data,
FUiRTRIYe struct pn544_info, miscdev);
0j^Kgx struct i2c_client *client = info->i2c_dev;
4j-Xi struct pn544_nfc_platform_data *pdata;
?al'F q unsigned int val;
ko!)s int r = 0;
1a/++4O.| QFA8N dev_dbg(&client->dev, "%s: info: %p, cmd: 0x%x\n", __func__, info, cmd);
:a!^
t$` r4Lb9/ mutex_lock(&info->mutex);
>>,e4s, |44Ploz2b if (info->state == PN544_ST_COLD) {
%:i7s-0w r = -ENODEV;
91/Q9xY goto out;
)7hqJa-V }
)j6~Wy@4 sWhZby7 pdata = info->i2c_dev->dev.platform_data;
[:dY0r+ switch (cmd) {
9p]QM)M case PN544_GET_FW_MODE:
59LG{R2 dev_dbg(&client->dev, "%s: PN544_GET_FW_MODE\n", __func__);
[DuttFX^x -oGdk|Yn val = (info->state == PN544_ST_FW_READY);
vz&|J
if (copy_to_user((void __user *)arg, &val, sizeof(val))) {
5%"V[lDx@ r = -EFAULT;
@@f"%2ZR[ goto out;
,CJWO bn3 }
=F|{#F /WcG{Wdp break;
6bg
;q(*7 hW<%R]^| case PN544_SET_FW_MODE:
PrqlTT}Px dev_dbg(&client->dev, "%s: PN544_SET_FW_MODE\n", __func__);
Lj({[H7D! q])K,) if (copy_from_user(&val, (void __user *)arg, sizeof(val))) {
Xg6Jh`` r = -EFAULT;
1er
TldX goto out;
1C+13LE$U }
{p2!|A&a hE{K=Tz$ if (val) {
`bq<$e if (info->state == PN544_ST_FW_READY)
J0WxR&%a) break;
)$2QZ
qX -_g0C^:<, pn544_disable(info);
\doUTr R r = pn544_enable(info, FW_MODE);
'@v\{ l if (r < 0)
b/K PaNv goto out;
'ms-*c&
} else {
vO^m;[' if (info->state == PN544_ST_READY)
.^`{1% break;
T=DbBy0- pn544_disable(info);
yZY \MB/ r = pn544_enable(info, HCI_MODE);
:U|1 xgB if (r < 0)
P\tB~SZ* goto out;
bIDj[-CDG }
Q-oktRK file->f_pos = info->read_offset;
),%%$G\ break;
fUWG*o9 FjHv case TCGETS:
P8:dU(nlW dev_dbg(&client->dev, "%s: TCGETS\n", __func__);
~7w"nIs<c RMV/&85?y r = -ENOIOCTLCMD;
r8?gD&